diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ec12e31e..f0ec85af 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -406,9 +406,9 @@ repos:
- id: check-symlinks
exclude: (\.pnpm-store|node_modules)/
- id: end-of-file-fixer
- exclude: (\.pnpm-store|node_modules)/
+ exclude: (\.pnpm-store|node_modules)/|^PROJECTS/advanced/hsm-emulator/vendor/
- id: trailing-whitespace
- exclude: (\.pnpm-store|node_modules)/
+ exclude: (\.pnpm-store|node_modules)/|^PROJECTS/advanced/hsm-emulator/vendor/
- id: check-illegal-windows-names
exclude: (\.pnpm-store|node_modules)/
- id: check-executables-have-shebangs
diff --git a/PROJECTS/advanced/hsm-emulator/.dockerignore b/PROJECTS/advanced/hsm-emulator/.dockerignore
new file mode 100644
index 00000000..762e4a8b
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/.dockerignore
@@ -0,0 +1,7 @@
+# ©AngelaMos | 2026
+# .dockerignore
+.zig-cache
+zig-out
+docs
+.git
+*.tmp
diff --git a/PROJECTS/advanced/hsm-emulator/.gitignore b/PROJECTS/advanced/hsm-emulator/.gitignore
new file mode 100644
index 00000000..4ec2d165
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/.gitignore
@@ -0,0 +1,13 @@
+# ©AngelaMos | 2026
+# .gitignore
+
+# Zig build artifacts and compilation cache
+.zig-cache/
+zig-out/
+
+# Local-only development docs (handoffs, archives, Zig reference notes)
+docs/
+
+# HSM state files if ever written into the project dir
+.angelamos-hsm-*
+*.tmp
diff --git a/PROJECTS/advanced/hsm-emulator/Dockerfile b/PROJECTS/advanced/hsm-emulator/Dockerfile
new file mode 100644
index 00000000..67e1ed10
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/Dockerfile
@@ -0,0 +1,40 @@
+# ©AngelaMos | 2026
+# Dockerfile
+
+FROM debian:bookworm-slim AS builder
+
+ARG ZIG_VERSION=0.16.0
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ ca-certificates curl xz-utils libssl-dev libc6-dev \
+ && rm -rf /var/lib/apt/lists/*
+RUN curl -fSL "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" \
+ -o /tmp/zig.tar.xz \
+ && mkdir -p /opt/zig \
+ && tar -xJf /tmp/zig.tar.xz -C /opt/zig --strip-components=1 \
+ && rm /tmp/zig.tar.xz
+ENV PATH="/opt/zig:${PATH}"
+
+WORKDIR /src
+COPY build.zig build.zig.zon pkcs11.map ./
+COPY src ./src
+COPY vendor ./vendor
+COPY examples ./examples
+COPY tests ./tests
+RUN zig build --release=safe
+
+FROM debian:bookworm-slim AS runtime
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ opensc libssl3 \
+ && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /hsm
+COPY --from=builder /src/zig-out/lib/ /hsm/lib/
+COPY docker/demo.sh /hsm/demo.sh
+RUN chmod +x /hsm/demo.sh
+
+ENV HSM_MODULE=/hsm/lib/libhsm.so.0.1.0 \
+ ANGELAMOS_HSM_TOKEN=/hsm/state/token \
+ ANGELAMOS_HSM_OBJECTS=/hsm/state/objects
+
+ENTRYPOINT ["/hsm/demo.sh"]
diff --git a/PROJECTS/advanced/hsm-emulator/LICENSE b/PROJECTS/advanced/hsm-emulator/LICENSE
new file mode 100644
index 00000000..0ad25db4
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/PROJECTS/advanced/hsm-emulator/README.md b/PROJECTS/advanced/hsm-emulator/README.md
new file mode 100644
index 00000000..e1ae34dd
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/README.md
@@ -0,0 +1,160 @@
+```
+██╗ ██╗███████╗███╗ ███╗ ███████╗███╗ ███╗██╗ ██╗██╗ █████╗ ████████╗ ██████╗ ██████╗
+██║ ██║██╔════╝████╗ ████║ ██╔════╝████╗ ████║██║ ██║██║ ██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗
+███████║███████╗██╔████╔██║ █████╗ ██╔████╔██║██║ ██║██║ ███████║ ██║ ██║ ██║██████╔╝
+██╔══██║╚════██║██║╚██╔╝██║ ██╔══╝ ██║╚██╔╝██║██║ ██║██║ ██╔══██║ ██║ ██║ ██║██╔══██╗
+██║ ██║███████║██║ ╚═╝ ██║ ███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ██║ ██║ ╚██████╔╝██║ ██║
+╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
+```
+
+[](https://github.com/CarterPerez-dev/Cybersecurity-Projects/tree/main/PROJECTS/advanced/hsm-emulator)
+[](https://ziglang.org)
+[](https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/os/pkcs11-base-v2.40-errata01-os.html)
+[](https://github.com/OpenSC/OpenSC)
+[](https://www.gnu.org/licenses/agpl-3.0)
+
+> A software **Hardware Security Module** that compiles to a real Cryptoki (PKCS#11) shared object. Load it with `pkcs11-tool`, OpenSSL, or any PKCS#11 host the same way you would a real smartcard or HSM — it speaks the C ABI byte-for-byte.
+
+## Why PKCS#11 in Zig
+
+PKCS#11 (Cryptoki) is the C-ABI standard that smartcards, YubiKeys, and cloud HSMs all speak. A conforming module is a `.so` that exports one function — `C_GetFunctionList` — returning a 68-entry table of function pointers in a *fixed canonical order*. Get one struct offset or one pointer slot wrong and the host loads garbage.
+
+That makes it a perfect showcase for Zig's C interop: `extern struct` with natural alignment, `callconv(.c)`, a version script that exports exactly one symbol, and a hand-written ABI that is **machine-checked against the official OASIS headers at build time**.
+
+## What Works Today (M0)
+
+- Loads cleanly under OpenSC `pkcs11-tool` 0.26.1 — enumerates the slot and token (`-L`) and advertises 19 mechanisms (`-M`)
+- Exports **only** `C_GetFunctionList` (verified with `objdump -T`)
+- The full v2.40 ABI hand-written in `src/ck.zig`: every type, 200+ constants, every struct, and the 68-entry `CK_FUNCTION_LIST` in canonical order
+- A build-time cross-check (`zig build test`) that translates the vendored OASIS headers and asserts `@sizeOf` / `@offsetOf` / constant equality **and per-function C-ABI signatures** against `ck.zig` — the spec compliance is a compile-time invariant, not a hope
+- General + slot/token entry points implemented for real; session, object, crypto, key-management, and RNG entry points are typed stubs returning `CKR_FUNCTION_NOT_SUPPORTED` until their milestone lands
+
+## Quick Start
+
+```bash
+git clone https://github.com/CarterPerez-dev/Cybersecurity-Projects.git
+cd Cybersecurity-Projects/PROJECTS/advanced/hsm-emulator
+./install.sh
+```
+
+`install.sh` checks for Zig 0.16, OpenSC, and OpenSSL, builds the module in ReleaseSafe, runs the ABI cross-check + smoke test, and confirms `pkcs11-tool` can load it. Then drive it like any real token:
+
+```bash
+pkcs11-tool --module zig-out/lib/libhsm.so -L # list slots and token
+pkcs11-tool --module zig-out/lib/libhsm.so -M # list mechanisms
+```
+
+```
+Available slots:
+Slot 0 (0x0): AngelaMos HSM Emulator Slot 0
+ token state: uninitialized
+```
+
+> [!TIP]
+> This project uses [`just`](https://github.com/casey/just) as a command runner. Type `just` to see everything. `just spy -L` wraps the module in `pkcs11-spy.so` and logs every Cryptoki call — the fastest way to watch the ABI work.
+>
+> Install: `curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin`
+
+## Architecture
+
+The same three-layer split SoftHSM2 uses: a thin C-ABI façade over typed core state over the store and crypto backends.
+
+```
+ PKCS#11 host (pkcs11-tool, OpenSSL, p11-kit)
+ │ C ABI
+ ▼
+ ┌───────────────────────────────────────────┐
+ │ C_GetFunctionList (src/main.zig) │ one exported symbol,
+ │ 68-entry CK_FUNCTION_LIST │ one version script
+ └───────────────────────┬─────────────────────┘
+ │
+ ┌───────────────────────┴─────────────────────┐
+ │ ABI façade src/ck.zig + src/api/*.zig │ hand-written Cryptoki ABI
+ │ general · slot_token · session · object · │ + per-call entry points
+ │ crypto_ops · keymgmt · random │
+ └───────────────────────┬─────────────────────┘
+ │
+ ┌───────────────────────┴─────────────────────┐
+ │ core state src/core/{state,lock}.zig │ global instance, init args,
+ │ │ C-boundary-safe locking
+ └───────────────────────┬─────────────────────┘
+ │
+ ┌───────────────────────┴─────────────────────┐
+ │ store + crypto (built milestone by │ in-memory → encrypted file
+ │ milestone: sessions, objects, AES/EC/RSA) │ backend at rest
+ └───────────────────────────────────────────────┘
+```
+
+**Design decisions:** non-RSA crypto is pure-Zig `std.crypto`; RSA links libcrypto (OpenSSL EVP) since `std.crypto` has no public RSA. RNG is sourced from `getrandom(2)` directly (there is no `std.Io` at the C boundary, and `std.crypto.random` was removed in Zig 0.16). The ABI is structured for v2.40 with room to add the v3.0 `C_GetInterface` surface later.
+
+## Build and Test
+
+```bash
+zig build # build the module → zig-out/lib/libhsm.so
+zig build test # ABI cross-check vs OASIS headers + unit tests
+zig build smoke # dlopen the built .so and exercise the ABI as a host would
+just ci # fmt-check + test + smoke
+```
+
+The smoke harness in `examples/smoke.zig` is not a unit test — it `dlopen`s the *actual built shared object* and calls through the function list exactly like an external host, so it catches export and ABI-shape bugs that in-process tests cannot.
+
+## Run in Docker
+
+No Zig or OpenSC on the host? The container builds the module and drives it end-to-end through `pkcs11-tool` — token init, RSA + EC keygen and signing, AES-CBC round-trip — all inside the image.
+
+```bash
+just docker-demo # build the image, then run the full pkcs11-tool demo
+```
+
+Or with Docker directly:
+
+```bash
+docker build -t angelamos-hsm:latest .
+docker run --rm angelamos-hsm:latest
+```
+
+A multi-stage build compiles the module in ReleaseSafe in a `debian-slim` builder, then ships only the `.so` plus `opensc` and `libssl3` in a ~96 MB runtime image. The demo exits non-zero if any signature fails to verify.
+
+## Project Structure
+
+```
+hsm-emulator/
+├── build.zig # addLibrary(.dynamic), version script, test + smoke steps, translate-c
+├── build.zig.zon # package manifest
+├── pkcs11.map # version script — exports only C_GetFunctionList
+├── src/
+│ ├── ck.zig # the hand-written Cryptoki v2.40 ABI (types, constants, structs, list)
+│ ├── config.zig # identity strings, key-size bounds, mechanism list (no magic numbers)
+│ ├── util.zig # comptime helpers (space-padded fixed fields)
+│ ├── main.zig # exported C_GetFunctionList + the wired 68-slot table
+│ ├── core/
+│ │ ├── state.zig # global instance, init-args parsing, atomic init flag
+│ │ └── lock.zig # spinlock wrapper (std.Thread.Mutex is gone in 0.16)
+│ └── api/
+│ ├── general.zig # C_Initialize / Finalize / GetInfo (locking template)
+│ ├── slot_token.zig # slot + token + mechanism queries
+│ └── session.zig, object.zig, crypto_ops.zig, keymgmt.zig, random.zig
+├── tests/abi_test.zig # @sizeOf/@offsetOf/constant asserts, incl. cross-check vs OASIS
+├── examples/smoke.zig # loads the built .so via dlopen and drives it
+└── vendor/pkcs11/ # unmodified OASIS v2.40 headers (build-time cross-check only)
+```
+
+## Roadmap
+
+Each milestone ends with a proof from a real external tool — no feature is "done" until `pkcs11-tool` or OpenSSL exercises it.
+
+| Milestone | Scope | Proof |
+|-----------|-------|-------|
+| **M0** ✅ | Scaffold + hand-written ABI + loadable `.so` | `pkcs11-tool -L/-M`, `objdump -T` |
+| **M1** | Sessions + login + PIN (Argon2id, lockout) | `pkcs11-tool --init-token --init-pin --login --change-pin` |
+| **M2** | Objects + find (in-memory), `CKA_PRIVATE` gating | `pkcs11-tool -O --read-object` |
+| **M3** | RNG + SHA + HMAC + AES-GCM/CBC | `--hash --encrypt --decrypt --generate-random` |
+| **M4** | ECDSA P-256/384 + keygen | `--keypairgen EC --sign`, cross-verify with OpenSSL |
+| **M5** | RSA via libcrypto (v1.5 / PSS / OAEP) | OpenSSL pkcs11 provider signs through the module |
+| **M6** | Encrypted file backend at rest (AES-256-GCM under Argon2id KEK) | persist across restart; tamper → fails closed |
+| **M7** | Hardening (secret zeroization, fail-closed) + Docker | — |
+| **M8** | Learn modules + mechanism reference + final docs | — |
+
+## License
+
+[AGPL 3.0](LICENSE)
diff --git a/PROJECTS/advanced/hsm-emulator/build.zig b/PROJECTS/advanced/hsm-emulator/build.zig
new file mode 100644
index 00000000..ca99fe7d
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/build.zig
@@ -0,0 +1,84 @@
+// ©AngelaMos | 2026
+// build.zig
+
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe });
+
+ const lib = b.addLibrary(.{
+ .name = "hsm",
+ .linkage = .dynamic,
+ .version = .{ .major = 0, .minor = 1, .patch = 0 },
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ .sanitize_c = .trap,
+ }),
+ });
+ lib.root_module.linkSystemLibrary("crypto", .{});
+ lib.setVersionScript(b.path("pkcs11.map"));
+ b.installArtifact(lib);
+
+ const ck_module = b.createModule(.{
+ .root_source_file = b.path("src/ck.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const translate_c = b.addTranslateC(.{
+ .root_source_file = b.path("vendor/pkcs11/shim.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+ translate_c.addIncludePath(b.path("vendor/pkcs11"));
+ const p11c_module = translate_c.createModule();
+
+ const tests = b.addTest(.{
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("tests/abi_test.zig"),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ .{ .name = "ck", .module = ck_module },
+ .{ .name = "p11c", .module = p11c_module },
+ },
+ }),
+ });
+ const run_tests = b.addRunArtifact(tests);
+ const test_step = b.step("test", "Run ABI and unit tests");
+ test_step.dependOn(&run_tests.step);
+
+ const unit_tests = b.addTest(.{
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/test_all.zig"),
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ }),
+ });
+ unit_tests.root_module.linkSystemLibrary("crypto", .{});
+ const run_unit_tests = b.addRunArtifact(unit_tests);
+ test_step.dependOn(&run_unit_tests.step);
+
+ const smoke = b.addExecutable(.{
+ .name = "smoke",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("examples/smoke.zig"),
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ .imports = &.{
+ .{ .name = "ck", .module = ck_module },
+ },
+ }),
+ });
+ const run_smoke = b.addRunArtifact(smoke);
+ run_smoke.step.dependOn(b.getInstallStep());
+ if (b.args) |args| run_smoke.addArgs(args);
+ const smoke_step = b.step("smoke", "Load the built .so via dlopen and exercise the Cryptoki ABI");
+ smoke_step.dependOn(&run_smoke.step);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/build.zig.zon b/PROJECTS/advanced/hsm-emulator/build.zig.zon
new file mode 100644
index 00000000..daaae246
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/build.zig.zon
@@ -0,0 +1,16 @@
+// ©AngelaMos | 2026
+// build.zig.zon
+.{
+ .name = .hsm_emulator,
+ .version = "0.1.0",
+ .fingerprint = 0x42f25b5af31e3d64,
+ .minimum_zig_version = "0.16.0",
+ .dependencies = .{},
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "pkcs11.map",
+ "src",
+ "vendor",
+ },
+}
diff --git a/PROJECTS/advanced/hsm-emulator/docker/demo.sh b/PROJECTS/advanced/hsm-emulator/docker/demo.sh
new file mode 100755
index 00000000..f6a66634
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/docker/demo.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# ©AngelaMos | 2026
+# demo.sh
+
+set -euo pipefail
+
+MOD="${HSM_MODULE:-/hsm/lib/libhsm.so.0.1.0}"
+SO_PIN=12345678
+PIN=1234
+WORK="$(mktemp -d)"
+mkdir -p "$(dirname "$ANGELAMOS_HSM_TOKEN")" "$(dirname "$ANGELAMOS_HSM_OBJECTS")"
+
+line() { printf '\n\033[1m== %s ==\033[0m\n' "$1"; }
+
+line "AngelaMos HSM — PKCS#11 module loaded by OpenSC pkcs11-tool"
+pkcs11-tool --module "$MOD" --init-token --label angelamos --so-pin "$SO_PIN" >/dev/null
+pkcs11-tool --module "$MOD" --init-pin --so-pin "$SO_PIN" --pin "$PIN" >/dev/null
+pkcs11-tool --module "$MOD" -I
+
+line "Supported mechanisms"
+pkcs11-tool --module "$MOD" -M
+
+line "RSA-2048 — generate, sign (SHA256-RSA-PKCS), verify"
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --keypairgen --key-type RSA:2048 --label rsa --id 01 2>/dev/null
+printf 'invoice #42 total $1000' > "$WORK/msg"
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --sign --mechanism SHA256-RSA-PKCS --id 01 -i "$WORK/msg" -o "$WORK/rsa.sig" 2>/dev/null
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --verify --mechanism SHA256-RSA-PKCS --id 01 -i "$WORK/msg" --signature-file "$WORK/rsa.sig"
+
+line "EC P-256 — generate, sign (ECDSA-SHA256), verify"
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --keypairgen --key-type EC:prime256v1 --label ec --id 02 2>/dev/null
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --sign --mechanism ECDSA-SHA256 --id 02 -i "$WORK/msg" -o "$WORK/ec.sig" 2>/dev/null
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --verify --mechanism ECDSA-SHA256 --id 02 -i "$WORK/msg" --signature-file "$WORK/ec.sig"
+
+line "AES-256 — generate, encrypt (AES-CBC), decrypt, compare"
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --keygen --key-type AES:32 --label aes --id 03 2>/dev/null
+printf '0123456789ABCDEF' > "$WORK/pt"
+IV=000102030405060708090a0b0c0d0e0f
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --encrypt --mechanism AES-CBC --id 03 --iv "$IV" -i "$WORK/pt" -o "$WORK/ct" 2>/dev/null
+pkcs11-tool --module "$MOD" --login --pin "$PIN" --decrypt --mechanism AES-CBC --id 03 --iv "$IV" -i "$WORK/ct" -o "$WORK/dec" 2>/dev/null
+if cmp -s "$WORK/pt" "$WORK/dec"; then
+ echo "AES-CBC round-trip: plaintext recovered OK"
+else
+ echo "AES-CBC round-trip: MISMATCH" && exit 1
+fi
+
+line "Token objects (private material sealed at rest under Argon2id(User-PIN))"
+pkcs11-tool --module "$MOD" --login --pin "$PIN" -O
+
+line "Demo complete — RSA + ECDSA signatures verified, AES-CBC round-trip OK"
diff --git a/PROJECTS/advanced/hsm-emulator/examples/smoke.zig b/PROJECTS/advanced/hsm-emulator/examples/smoke.zig
new file mode 100644
index 00000000..97bfd7db
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/examples/smoke.zig
@@ -0,0 +1,382 @@
+// ©AngelaMos | 2026
+// smoke.zig
+
+const std = @import("std");
+const ck = @import("ck");
+
+const GetFunctionList = *const fn (*?*ck.CK_FUNCTION_LIST) callconv(.c) ck.CK_RV;
+
+const default_module = "zig-out/lib/libhsm.so";
+const smoke_token = "/tmp/angelamos-hsm-smoke-token.bin";
+const smoke_objects = "/tmp/angelamos-hsm-smoke-objects.bin";
+
+extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
+
+pub fn main() !void {
+ _ = setenv("ANGELAMOS_HSM_TOKEN", smoke_token, 1);
+ _ = setenv("ANGELAMOS_HSM_OBJECTS", smoke_objects, 1);
+ _ = std.c.unlink(smoke_token);
+ _ = std.c.unlink(smoke_objects);
+ defer _ = std.c.unlink(smoke_token);
+ defer _ = std.c.unlink(smoke_objects);
+
+ var so_pin = "12345678".*;
+ var user_pin = "1234".*;
+ var new_user_pin = "5678".*;
+ var wrong_pin = "0000".*;
+ var label: [32]u8 = @splat(' ');
+ @memcpy(label[0..11], "smoke-token");
+
+ var lib = try std.DynLib.open(default_module);
+ defer lib.close();
+
+ const getFunctionList = lib.lookup(GetFunctionList, "C_GetFunctionList") orelse {
+ std.debug.print("smoke: C_GetFunctionList not exported\n", .{});
+ return error.SymbolNotFound;
+ };
+
+ var list_ptr: ?*ck.CK_FUNCTION_LIST = null;
+ try check("C_GetFunctionList", getFunctionList(&list_ptr));
+ const f = list_ptr orelse return error.NullFunctionList;
+
+ if (f.version.major != 2 or f.version.minor != 40) return error.UnexpectedVersion;
+
+ try check("C_Initialize", f.C_Initialize.?(null));
+ if (f.C_Initialize.?(null) != ck.CKR_CRYPTOKI_ALREADY_INITIALIZED) return error.DoubleInitNotRejected;
+
+ var info: ck.CK_INFO = undefined;
+ try check("C_GetInfo", f.C_GetInfo.?(&info));
+
+ var count: ck.CK_ULONG = 0;
+ try check("C_GetSlotList(size)", f.C_GetSlotList.?(ck.CK_FALSE, null, &count));
+ if (count != 1) return error.UnexpectedSlotCount;
+ var slots: [4]ck.CK_SLOT_ID = undefined;
+ try check("C_GetSlotList(fill)", f.C_GetSlotList.?(ck.CK_FALSE, &slots, &count));
+ const slot = slots[0];
+
+ var slot_info: ck.CK_SLOT_INFO = undefined;
+ try check("C_GetSlotInfo", f.C_GetSlotInfo.?(slot, &slot_info));
+
+ var token_info: ck.CK_TOKEN_INFO = undefined;
+ try check("C_GetTokenInfo", f.C_GetTokenInfo.?(slot, &token_info));
+ if (token_info.flags & ck.CKF_TOKEN_INITIALIZED != 0) return error.TokenShouldStartUninitialized;
+
+ var mech_count: ck.CK_ULONG = 0;
+ try check("C_GetMechanismList(size)", f.C_GetMechanismList.?(slot, null, &mech_count));
+ if (mech_count == 0) return error.NoMechanisms;
+
+ try check("C_InitToken", f.C_InitToken.?(slot, &so_pin, so_pin.len, &label));
+ try check("C_GetTokenInfo(post-init)", f.C_GetTokenInfo.?(slot, &token_info));
+ if (token_info.flags & ck.CKF_TOKEN_INITIALIZED == 0) return error.InitTokenDidNotInitialize;
+
+ var h: ck.CK_SESSION_HANDLE = 0;
+ try check("C_OpenSession", f.C_OpenSession.?(slot, ck.CKF_SERIAL_SESSION | ck.CKF_RW_SESSION, null, null, &h));
+
+ var si: ck.CK_SESSION_INFO = undefined;
+ try check("C_GetSessionInfo", f.C_GetSessionInfo.?(h, &si));
+ if (si.state != ck.CKS_RW_PUBLIC_SESSION) return error.UnexpectedPublicState;
+
+ try check("C_Login(SO)", f.C_Login.?(h, ck.CKU_SO, &so_pin, so_pin.len));
+ try check("C_GetSessionInfo(SO)", f.C_GetSessionInfo.?(h, &si));
+ if (si.state != ck.CKS_RW_SO_FUNCTIONS) return error.UnexpectedSoState;
+
+ try check("C_InitPIN", f.C_InitPIN.?(h, &user_pin, user_pin.len));
+ try check("C_Logout(SO)", f.C_Logout.?(h));
+
+ try check("C_GetTokenInfo(post-initpin)", f.C_GetTokenInfo.?(slot, &token_info));
+ if (token_info.flags & ck.CKF_USER_PIN_INITIALIZED == 0) return error.UserPinNotInitialized;
+
+ try check("C_Login(USER)", f.C_Login.?(h, ck.CKU_USER, &user_pin, user_pin.len));
+ try check("C_GetSessionInfo(USER)", f.C_GetSessionInfo.?(h, &si));
+ if (si.state != ck.CKS_RW_USER_FUNCTIONS) return error.UnexpectedUserState;
+
+ try check("C_SetPIN", f.C_SetPIN.?(h, &user_pin, user_pin.len, &new_user_pin, new_user_pin.len));
+ try check("C_Logout(USER)", f.C_Logout.?(h));
+
+ try check("C_Login(USER,new)", f.C_Login.?(h, ck.CKU_USER, &new_user_pin, new_user_pin.len));
+
+ var class_data: ck.CK_OBJECT_CLASS = ck.CKO_DATA;
+ var ck_true: ck.CK_BBOOL = ck.CK_TRUE;
+ var data_label = "smoke-data".*;
+ var data_value = "hello-hsm".*;
+ var create_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_data, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_LABEL, .pValue = &data_label, .ulValueLen = data_label.len },
+ .{ .type = ck.CKA_VALUE, .pValue = &data_value, .ulValueLen = data_value.len },
+ };
+ var h_data: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(data)", f.C_CreateObject.?(h, &create_tmpl, create_tmpl.len, &h_data));
+ if (h_data == ck.CK_INVALID_HANDLE) return error.BadObjectHandle;
+
+ var priv_label = "smoke-secret".*;
+ var priv_value = "top-secret".*;
+ var priv_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_data, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_PRIVATE, .pValue = &ck_true, .ulValueLen = 1 },
+ .{ .type = ck.CKA_LABEL, .pValue = &priv_label, .ulValueLen = priv_label.len },
+ .{ .type = ck.CKA_VALUE, .pValue = &priv_value, .ulValueLen = priv_value.len },
+ };
+ var h_priv: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(private)", f.C_CreateObject.?(h, &priv_tmpl, priv_tmpl.len, &h_priv));
+
+ var find_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_data, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ };
+ var found: [8]ck.CK_OBJECT_HANDLE = undefined;
+ var nfound: ck.CK_ULONG = 0;
+ try check("C_FindObjectsInit", f.C_FindObjectsInit.?(h, &find_tmpl, find_tmpl.len));
+ try check("C_FindObjects", f.C_FindObjects.?(h, &found, found.len, &nfound));
+ try check("C_FindObjectsFinal", f.C_FindObjectsFinal.?(h));
+ if (nfound != 2) return error.FindCountWrong;
+
+ var probe = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_VALUE, .pValue = null, .ulValueLen = 0 },
+ };
+ try check("C_GetAttributeValue(len)", f.C_GetAttributeValue.?(h, h_data, &probe, probe.len));
+ if (probe[0].ulValueLen != data_value.len) return error.LenProbeWrong;
+ var valbuf: [64]u8 = undefined;
+ probe[0].pValue = &valbuf;
+ try check("C_GetAttributeValue(fetch)", f.C_GetAttributeValue.?(h, h_data, &probe, probe.len));
+ if (!std.mem.eql(u8, valbuf[0..probe[0].ulValueLen], &data_value)) return error.ValueMismatch;
+
+ var new_label = "relabeled!!".*;
+ var set_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_LABEL, .pValue = &new_label, .ulValueLen = new_label.len },
+ };
+ try check("C_SetAttributeValue", f.C_SetAttributeValue.?(h, h_data, &set_tmpl, set_tmpl.len));
+ var lblbuf: [32]u8 = undefined;
+ var lblq = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_LABEL, .pValue = &lblbuf, .ulValueLen = lblbuf.len },
+ };
+ try check("C_GetAttributeValue(label)", f.C_GetAttributeValue.?(h, h_data, &lblq, lblq.len));
+ if (!std.mem.eql(u8, lblbuf[0..lblq[0].ulValueLen], &new_label)) return error.RelabelFailed;
+
+ var osize: ck.CK_ULONG = 0;
+ try check("C_GetObjectSize", f.C_GetObjectSize.?(h, h_data, &osize));
+ if (osize == 0) return error.ZeroObjectSize;
+
+ try check("C_DestroyObject", f.C_DestroyObject.?(h, h_data));
+ if (f.C_FindObjects.?(h, &found, found.len, &nfound) != ck.CKR_OPERATION_NOT_INITIALIZED) return error.FsmNotEnforced;
+
+ try check("C_Logout(after-objects)", f.C_Logout.?(h));
+ try check("C_FindObjectsInit(public)", f.C_FindObjectsInit.?(h, null, 0));
+ try check("C_FindObjects(public)", f.C_FindObjects.?(h, &found, found.len, &nfound));
+ try check("C_FindObjectsFinal(public)", f.C_FindObjectsFinal.?(h));
+ if (nfound != 0) return error.PrivateObjectLeaked;
+ if (f.C_GetAttributeValue.?(h, h_priv, &lblq, lblq.len) != ck.CKR_OBJECT_HANDLE_INVALID) return error.PrivateNotGated;
+
+ var ck_false: ck.CK_BBOOL = ck.CK_FALSE;
+ var undead_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_data, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_DESTROYABLE, .pValue = &ck_false, .ulValueLen = 1 },
+ };
+ var h_undead: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(undestroyable)", f.C_CreateObject.?(h, &undead_tmpl, undead_tmpl.len, &h_undead));
+ if (f.C_DestroyObject.?(h, h_undead) != ck.CKR_ACTION_PROHIBITED) return error.DestroyableGateBroken;
+
+ var immut_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_data, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_MODIFIABLE, .pValue = &ck_false, .ulValueLen = 1 },
+ };
+ var h_immut: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(immutable)", f.C_CreateObject.?(h, &immut_tmpl, immut_tmpl.len, &h_immut));
+ var nope = "nope".*;
+ var set_immut = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_LABEL, .pValue = &nope, .ulValueLen = nope.len },
+ };
+ if (f.C_SetAttributeValue.?(h, h_immut, &set_immut, set_immut.len) != ck.CKR_ACTION_PROHIBITED) return error.ModifiableGateBroken;
+
+ if (f.C_FindObjectsInit.?(h, null, 3) != ck.CKR_ARGUMENTS_BAD) return error.ArgsBadNotEnforced;
+
+ var attempt: u8 = 0;
+ while (attempt < 3) : (attempt += 1) {
+ if (f.C_Login.?(h, ck.CKU_USER, &wrong_pin, wrong_pin.len) != ck.CKR_PIN_INCORRECT) return error.WrongPinNotRejected;
+ }
+ if (f.C_Login.?(h, ck.CKU_USER, &new_user_pin, new_user_pin.len) != ck.CKR_PIN_LOCKED) return error.LockoutNotEnforced;
+ try check("C_GetTokenInfo(locked)", f.C_GetTokenInfo.?(slot, &token_info));
+ if (token_info.flags & ck.CKF_USER_PIN_LOCKED == 0) return error.LockFlagNotSet;
+
+ var sha_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_SHA256, .pParameter = null, .ulParameterLen = 0 };
+ try check("C_DigestInit", f.C_DigestInit.?(h, &sha_mech));
+ var abc = "abc".*;
+ var dg: [64]u8 = undefined;
+ var dglen: ck.CK_ULONG = dg.len;
+ try check("C_Digest", f.C_Digest.?(h, &abc, abc.len, &dg, &dglen));
+ const sha_abc = [_]u8{
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
+ };
+ if (dglen != 32 or !std.mem.eql(u8, dg[0..32], &sha_abc)) return error.DigestVectorMismatch;
+
+ var class_secret: ck.CK_OBJECT_CLASS = ck.CKO_SECRET_KEY;
+ var ck_yes: ck.CK_BBOOL = ck.CK_TRUE;
+ var kt_generic: ck.CK_KEY_TYPE = ck.CKK_GENERIC_SECRET;
+ var hkey_val = "secret-hmac-key".*;
+ var hmac_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_secret, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_KEY_TYPE, .pValue = &kt_generic, .ulValueLen = @sizeOf(ck.CK_KEY_TYPE) },
+ .{ .type = ck.CKA_VALUE, .pValue = &hkey_val, .ulValueLen = hkey_val.len },
+ .{ .type = ck.CKA_SIGN, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_VERIFY, .pValue = &ck_yes, .ulValueLen = 1 },
+ };
+ var h_hmac: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(hmac key)", f.C_CreateObject.?(h, &hmac_tmpl, hmac_tmpl.len, &h_hmac));
+
+ var hmac_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_SHA256_HMAC, .pParameter = null, .ulParameterLen = 0 };
+ var hmsg = "authenticate me".*;
+ var sig: [64]u8 = undefined;
+ var siglen: ck.CK_ULONG = sig.len;
+ try check("C_SignInit", f.C_SignInit.?(h, &hmac_mech, h_hmac));
+ try check("C_Sign", f.C_Sign.?(h, &hmsg, hmsg.len, &sig, &siglen));
+ if (siglen != 32) return error.HmacLenWrong;
+ try check("C_VerifyInit", f.C_VerifyInit.?(h, &hmac_mech, h_hmac));
+ try check("C_Verify", f.C_Verify.?(h, &hmsg, hmsg.len, &sig, siglen));
+ try check("C_VerifyInit(tamper)", f.C_VerifyInit.?(h, &hmac_mech, h_hmac));
+ sig[0] ^= 0xff;
+ if (f.C_Verify.?(h, &hmsg, hmsg.len, &sig, siglen) != ck.CKR_SIGNATURE_INVALID) return error.HmacTamperNotDetected;
+
+ var kt_aes: ck.CK_KEY_TYPE = ck.CKK_AES;
+ var aes_val = [_]u8{0} ** 32;
+ for (0..32) |j| aes_val[j] = @intCast(j);
+ var aes_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_CLASS, .pValue = &class_secret, .ulValueLen = @sizeOf(ck.CK_OBJECT_CLASS) },
+ .{ .type = ck.CKA_KEY_TYPE, .pValue = &kt_aes, .ulValueLen = @sizeOf(ck.CK_KEY_TYPE) },
+ .{ .type = ck.CKA_VALUE, .pValue = &aes_val, .ulValueLen = aes_val.len },
+ .{ .type = ck.CKA_ENCRYPT, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_DECRYPT, .pValue = &ck_yes, .ulValueLen = 1 },
+ };
+ var h_aes: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_CreateObject(aes key)", f.C_CreateObject.?(h, &aes_tmpl, aes_tmpl.len, &h_aes));
+
+ var iv = [_]u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ var cbc_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_CBC_PAD, .pParameter = &iv, .ulParameterLen = iv.len };
+ var aes_pt = "AES round-trip through the Cryptoki ABI".*;
+ var aes_ct: [64]u8 = undefined;
+ var ctlen: ck.CK_ULONG = aes_ct.len;
+ try check("C_EncryptInit", f.C_EncryptInit.?(h, &cbc_mech, h_aes));
+ try check("C_Encrypt", f.C_Encrypt.?(h, &aes_pt, aes_pt.len, &aes_ct, &ctlen));
+ var aes_back: [64]u8 = undefined;
+ var backlen: ck.CK_ULONG = aes_back.len;
+ try check("C_DecryptInit", f.C_DecryptInit.?(h, &cbc_mech, h_aes));
+ try check("C_Decrypt", f.C_Decrypt.?(h, &aes_ct, ctlen, &aes_back, &backlen));
+ if (backlen != aes_pt.len or !std.mem.eql(u8, aes_back[0..backlen], &aes_pt)) return error.AesRoundTripFailed;
+
+ var gen_keylen: ck.CK_ULONG = 32;
+ var gen_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_VALUE_LEN, .pValue = &gen_keylen, .ulValueLen = @sizeOf(ck.CK_ULONG) },
+ };
+ var gen_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_AES_KEY_GEN, .pParameter = null, .ulParameterLen = 0 };
+ var h_gen: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_GenerateKey", f.C_GenerateKey.?(h, &gen_mech, &gen_tmpl, gen_tmpl.len, &h_gen));
+ var genval_q = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_VALUE, .pValue = null, .ulValueLen = 0 },
+ };
+ if (f.C_GetAttributeValue.?(h, h_gen, &genval_q, genval_q.len) != ck.CKR_ATTRIBUTE_SENSITIVE) return error.GeneratedKeyNotSensitive;
+
+ var ec_params = [_]u8{ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 };
+ var ec_kpgen = ck.CK_MECHANISM{ .mechanism = ck.CKM_EC_KEY_PAIR_GEN, .pParameter = null, .ulParameterLen = 0 };
+ var ecpub_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_EC_PARAMS, .pValue = &ec_params, .ulValueLen = ec_params.len },
+ .{ .type = ck.CKA_VERIFY, .pValue = &ck_yes, .ulValueLen = 1 },
+ };
+ var ecpriv_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_SIGN, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_PRIVATE, .pValue = &ck_false, .ulValueLen = 1 },
+ };
+ var h_ecpub: ck.CK_OBJECT_HANDLE = 0;
+ var h_ecpriv: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_GenerateKeyPair(EC)", f.C_GenerateKeyPair.?(h, &ec_kpgen, &ecpub_tmpl, ecpub_tmpl.len, &ecpriv_tmpl, ecpriv_tmpl.len, &h_ecpub, &h_ecpriv));
+
+ var ecdsa_mech = ck.CK_MECHANISM{ .mechanism = ck.CKM_ECDSA_SHA256, .pParameter = null, .ulParameterLen = 0 };
+ var ecmsg = "sign me over ECDSA P-256".*;
+ var ecsig: [128]u8 = undefined;
+ var ecsiglen: ck.CK_ULONG = ecsig.len;
+ try check("C_SignInit(ECDSA)", f.C_SignInit.?(h, &ecdsa_mech, h_ecpriv));
+ try check("C_Sign(ECDSA)", f.C_Sign.?(h, &ecmsg, ecmsg.len, &ecsig, &ecsiglen));
+ if (ecsiglen != 64) return error.EcdsaSigLenWrong;
+ try check("C_VerifyInit(ECDSA)", f.C_VerifyInit.?(h, &ecdsa_mech, h_ecpub));
+ try check("C_Verify(ECDSA)", f.C_Verify.?(h, &ecmsg, ecmsg.len, &ecsig, ecsiglen));
+ try check("C_VerifyInit(ECDSA tamper)", f.C_VerifyInit.?(h, &ecdsa_mech, h_ecpub));
+ ecsig[0] ^= 0xff;
+ if (f.C_Verify.?(h, &ecmsg, ecmsg.len, &ecsig, ecsiglen) != ck.CKR_SIGNATURE_INVALID) return error.EcdsaTamperNotDetected;
+
+ var ecval_q = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_VALUE, .pValue = null, .ulValueLen = 0 },
+ };
+ if (f.C_GetAttributeValue.?(h, h_ecpriv, &ecval_q, ecval_q.len) != ck.CKR_ATTRIBUTE_SENSITIVE) return error.EcPrivNotSensitive;
+ var ecpt_q = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_EC_POINT, .pValue = null, .ulValueLen = 0 },
+ };
+ try check("C_GetAttributeValue(EC_POINT)", f.C_GetAttributeValue.?(h, h_ecpub, &ecpt_q, ecpt_q.len));
+ if (ecpt_q[0].ulValueLen != 67) return error.EcPointLenWrong;
+
+ var rsa_bits: ck.CK_ULONG = 2048;
+ var rsa_kpgen = ck.CK_MECHANISM{ .mechanism = ck.CKM_RSA_PKCS_KEY_PAIR_GEN, .pParameter = null, .ulParameterLen = 0 };
+ var rsapub_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_MODULUS_BITS, .pValue = &rsa_bits, .ulValueLen = @sizeOf(ck.CK_ULONG) },
+ .{ .type = ck.CKA_VERIFY, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_ENCRYPT, .pValue = &ck_yes, .ulValueLen = 1 },
+ };
+ var rsapriv_tmpl = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_SIGN, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_DECRYPT, .pValue = &ck_yes, .ulValueLen = 1 },
+ .{ .type = ck.CKA_PRIVATE, .pValue = &ck_false, .ulValueLen = 1 },
+ };
+ var h_rsapub: ck.CK_OBJECT_HANDLE = 0;
+ var h_rsapriv: ck.CK_OBJECT_HANDLE = 0;
+ try check("C_GenerateKeyPair(RSA)", f.C_GenerateKeyPair.?(h, &rsa_kpgen, &rsapub_tmpl, rsapub_tmpl.len, &rsapriv_tmpl, rsapriv_tmpl.len, &h_rsapub, &h_rsapriv));
+
+ var rsa_sha_pkcs = ck.CK_MECHANISM{ .mechanism = ck.CKM_SHA256_RSA_PKCS, .pParameter = null, .ulParameterLen = 0 };
+ var rsamsg = "sign me over RSA PKCS#1 v1.5".*;
+ var rsasig: [256]u8 = undefined;
+ var rsasiglen: ck.CK_ULONG = rsasig.len;
+ try check("C_SignInit(RSA)", f.C_SignInit.?(h, &rsa_sha_pkcs, h_rsapriv));
+ try check("C_Sign(RSA)", f.C_Sign.?(h, &rsamsg, rsamsg.len, &rsasig, &rsasiglen));
+ if (rsasiglen != 256) return error.RsaSigLenWrong;
+ try check("C_VerifyInit(RSA)", f.C_VerifyInit.?(h, &rsa_sha_pkcs, h_rsapub));
+ try check("C_Verify(RSA)", f.C_Verify.?(h, &rsamsg, rsamsg.len, &rsasig, rsasiglen));
+ try check("C_VerifyInit(RSA tamper)", f.C_VerifyInit.?(h, &rsa_sha_pkcs, h_rsapub));
+ rsasig[10] ^= 0xff;
+ if (f.C_Verify.?(h, &rsamsg, rsamsg.len, &rsasig, rsasiglen) != ck.CKR_SIGNATURE_INVALID) return error.RsaTamperNotDetected;
+
+ var rsa_pkcs = ck.CK_MECHANISM{ .mechanism = ck.CKM_RSA_PKCS, .pParameter = null, .ulParameterLen = 0 };
+ var rsapt = "rsa secret".*;
+ var rsact: [256]u8 = undefined;
+ var rsactlen: ck.CK_ULONG = rsact.len;
+ try check("C_EncryptInit(RSA)", f.C_EncryptInit.?(h, &rsa_pkcs, h_rsapub));
+ try check("C_Encrypt(RSA)", f.C_Encrypt.?(h, &rsapt, rsapt.len, &rsact, &rsactlen));
+ if (rsactlen != 256) return error.RsaCtLenWrong;
+ var rsaback: [256]u8 = undefined;
+ var rsabacklen: ck.CK_ULONG = rsaback.len;
+ try check("C_DecryptInit(RSA)", f.C_DecryptInit.?(h, &rsa_pkcs, h_rsapriv));
+ try check("C_Decrypt(RSA)", f.C_Decrypt.?(h, &rsact, rsactlen, &rsaback, &rsabacklen));
+ if (rsabacklen != rsapt.len or !std.mem.eql(u8, rsaback[0..rsabacklen], &rsapt)) return error.RsaRoundTripFailed;
+
+ var rsaval_q = [_]ck.CK_ATTRIBUTE{
+ .{ .type = ck.CKA_PRIVATE_EXPONENT, .pValue = null, .ulValueLen = 0 },
+ };
+ if (f.C_GetAttributeValue.?(h, h_rsapriv, &rsaval_q, rsaval_q.len) != ck.CKR_ATTRIBUTE_SENSITIVE) return error.RsaPrivNotSensitive;
+
+ try check("C_CloseSession", f.C_CloseSession.?(h));
+ try check("C_Finalize", f.C_Finalize.?(null));
+
+ std.debug.print("smoke: OK\n", .{});
+ std.debug.print(" cryptokiVersion = {d}.{d}\n", .{ info.cryptokiVersion.major, info.cryptokiVersion.minor });
+ std.debug.print(" slots = {d}\n", .{count});
+ std.debug.print(" token label = {s}\n", .{token_info.label});
+ std.debug.print(" mechanisms = {d}\n", .{mech_count});
+ std.debug.print(" login + PIN = init/login/initpin/setpin OK; lockout trips after 3 wrong\n", .{});
+ std.debug.print(" objects = create/find/get(2-call)/set/size/destroy OK; CKA_PRIVATE hidden after logout\n", .{});
+ std.debug.print(" object gates = CKA_DESTROYABLE/CKA_MODIFIABLE=false enforced; FindObjectsInit arg-check OK\n", .{});
+ std.debug.print(" crypto = SHA-256 vector OK; HMAC sign/verify (+tamper) OK; AES-CBC-PAD round-trip OK\n", .{});
+ std.debug.print(" keygen = C_GenerateKey AES OK; generated key CKA_VALUE is sensitive (unextractable)\n", .{});
+ std.debug.print(" ecdsa = C_GenerateKeyPair EC P-256 OK; ECDSA-SHA256 sign/verify (+tamper) OK; priv scalar sensitive\n", .{});
+ std.debug.print(" rsa = C_GenerateKeyPair RSA-2048 OK; SHA256-RSA-PKCS sign/verify (+tamper) + RSA-PKCS enc/dec OK; priv sensitive\n", .{});
+}
+
+fn check(name: []const u8, rv: ck.CK_RV) !void {
+ if (rv != ck.CKR_OK) {
+ std.debug.print("smoke: {s} -> 0x{X}\n", .{ name, rv });
+ return error.CryptokiError;
+ }
+}
diff --git a/PROJECTS/advanced/hsm-emulator/install.sh b/PROJECTS/advanced/hsm-emulator/install.sh
new file mode 100755
index 00000000..cfe742d7
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/install.sh
@@ -0,0 +1,141 @@
+#!/usr/bin/env bash
+# ©AngelaMos | 2026
+# install.sh
+
+set -euo pipefail
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+CYAN='\033[0;36m'
+DIM='\033[2m'
+NC='\033[0m'
+
+info() { printf "${CYAN}▸${NC} %s\n" "$1"; }
+ok() { printf "${GREEN}✓${NC} %s\n" "$1"; }
+warn() { printf "${YELLOW}!${NC} %s\n" "$1"; }
+fail() { printf "${RED}✗${NC} %s\n" "$1"; exit 1; }
+
+MIN_ZIG="0.16.0"
+
+banner() {
+ printf "\n"
+ printf "${CYAN}"
+ cat <<'EOF'
+ ██╗ ██╗███████╗███╗ ███╗
+ ██║ ██║██╔════╝████╗ ████║
+ ███████║███████╗██╔████╔██║
+ ██╔══██║╚════██║██║╚██╔╝██║
+ ██║ ██║███████║██║ ╚═╝ ██║
+ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
+EOF
+ printf "${NC}"
+ printf " ${DIM}hsm emulator installer — pkcs#11 v2.40 module in zig${NC}\n"
+ printf "\n"
+}
+
+check_zig() {
+ if ! command -v zig &>/dev/null; then
+ fail "Zig is not installed. Get 0.16.0 at https://ziglang.org/download/"
+ fi
+
+ local ver
+ ver=$(zig version)
+
+ if ! printf '%s\n%s\n' "$MIN_ZIG" "$ver" \
+ | sort -V | head -n1 | grep -qx "$MIN_ZIG"; then
+ fail "Zig $MIN_ZIG+ required (found $ver). This project tracks 0.16 idioms."
+ fi
+
+ ok "Zig $ver"
+}
+
+check_opensc() {
+ if ! command -v pkcs11-tool &>/dev/null; then
+ fail "OpenSC (pkcs11-tool) not found. Install: sudo apt install opensc"
+ fi
+ ok "OpenSC pkcs11-tool$(dpkg -l opensc 2>/dev/null | awk '/^ii/{print " "$3}')"
+
+ if [ -e /usr/lib/x86_64-linux-gnu/pkcs11-spy.so ]; then
+ ok "pkcs11-spy.so present (call tracing via 'just spy')"
+ else
+ warn "pkcs11-spy.so not found (optional; ships with opensc-pkcs11)"
+ fi
+}
+
+check_openssl() {
+ if command -v openssl &>/dev/null; then
+ ok "$(openssl version | cut -d' ' -f1-2) (libcrypto for RSA, M5)"
+ else
+ warn "OpenSSL not found (needed later for the RSA milestone). Install: sudo apt install libssl-dev"
+ fi
+}
+
+check_just() {
+ if command -v just &>/dev/null; then
+ ok "just $(just --version 2>/dev/null | cut -d' ' -f2)"
+ else
+ info "just not found (optional). Install: curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin"
+ fi
+}
+
+build_module() {
+ info "Building the Cryptoki module (ReleaseSafe)..."
+ zig build
+ local so
+ so=$(find zig-out/lib -name 'libhsm.so.*' -type f | head -1)
+ [ -n "$so" ] || fail "Build produced no libhsm.so"
+ ok "Built $so ($(du -h "$so" | cut -f1))"
+}
+
+run_tests() {
+ info "Running ABI cross-check and unit tests..."
+ if zig build test >/dev/null 2>&1; then
+ ok "All tests passed (ck.zig matches OASIS v2.40 headers)"
+ else
+ fail "Tests failed. Run 'zig build test --summary all' for details."
+ fi
+
+ info "Running the dlopen smoke test..."
+ if zig build smoke 2>&1 | grep -q "smoke: OK"; then
+ ok "Smoke test passed (module loads and drives the ABI)"
+ else
+ fail "Smoke test failed. Run 'zig build smoke' for details."
+ fi
+}
+
+verify_load() {
+ info "Verifying the module loads under OpenSC..."
+ if pkcs11-tool --module zig-out/lib/libhsm.so -L >/dev/null 2>&1; then
+ ok "pkcs11-tool loaded the module and enumerated the slot"
+ else
+ fail "pkcs11-tool could not load the module."
+ fi
+}
+
+main() {
+ banner
+
+ info "Checking dependencies..."
+ check_zig
+ check_opensc
+ check_openssl
+ check_just
+
+ printf "\n"
+
+ build_module
+ run_tests
+ verify_load
+
+ printf "\n"
+ ok "Setup complete"
+ printf "\n"
+ printf " ${DIM}List the slot:${NC} pkcs11-tool --module zig-out/lib/libhsm.so -L\n"
+ printf " ${DIM}List mechanisms:${NC} pkcs11-tool --module zig-out/lib/libhsm.so -M\n"
+ printf " ${DIM}Trace every call:${NC} just spy -L\n"
+ printf " ${DIM}All commands:${NC} just\n"
+ printf "\n"
+}
+
+main "$@"
diff --git a/PROJECTS/advanced/hsm-emulator/justfile b/PROJECTS/advanced/hsm-emulator/justfile
new file mode 100644
index 00000000..327de1aa
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/justfile
@@ -0,0 +1,122 @@
+# =============================================================================
+# ©AngelaMos | 2026
+# justfile
+# =============================================================================
+
+set dotenv-load
+set export
+set shell := ["bash", "-uc"]
+
+project := file_name(justfile_directory())
+version := `git describe --tags --always 2>/dev/null || echo "dev"`
+module := justfile_directory() / "zig-out/lib/libhsm.so"
+spy := "/usr/lib/x86_64-linux-gnu/pkcs11-spy.so"
+image := "angelamos-hsm:latest"
+
+# =============================================================================
+# Default
+# =============================================================================
+
+default:
+ @just --list --unsorted
+
+# =============================================================================
+# Build
+# =============================================================================
+
+[group('build')]
+build:
+ zig build
+
+[group('build')]
+debug:
+ zig build -Doptimize=Debug
+
+[group('build')]
+release:
+ zig build -Doptimize=ReleaseSafe
+
+# =============================================================================
+# Test
+# =============================================================================
+
+[group('test')]
+test:
+ zig build test
+
+[group('test')]
+test-verbose:
+ zig build test --summary all
+
+[group('test')]
+smoke:
+ zig build smoke
+
+# =============================================================================
+# Lint and Format
+# =============================================================================
+
+[group('lint')]
+fmt:
+ zig fmt build.zig build.zig.zon src tests examples
+
+[group('lint')]
+fmt-check:
+ zig fmt --check build.zig build.zig.zon src tests examples
+
+# =============================================================================
+# PKCS#11 (drive the built module with OpenSC pkcs11-tool)
+# =============================================================================
+
+[group('pkcs11')]
+inspect: build
+ pkcs11-tool --module {{module}} -I
+
+[group('pkcs11')]
+slots: build
+ pkcs11-tool --module {{module}} -L
+
+[group('pkcs11')]
+mechs: build
+ pkcs11-tool --module {{module}} -M
+
+[group('pkcs11')]
+spy *ARGS: build
+ PKCS11SPY={{module}} pkcs11-tool --module {{spy}} {{ARGS}}
+
+# =============================================================================
+# Docker (build the image, then run the end-to-end pkcs11-tool demo)
+# =============================================================================
+
+[group('docker')]
+docker-build:
+ docker build -t {{image}} .
+
+[group('docker')]
+docker-demo: docker-build
+ docker run --rm {{image}}
+
+# =============================================================================
+# CI / Quality
+# =============================================================================
+
+[group('ci')]
+ci: fmt-check test smoke
+
+# =============================================================================
+# Utilities
+# =============================================================================
+
+[group('util')]
+info:
+ @echo "Project: {{project}}"
+ @echo "Version: {{version}}"
+ @echo "Module: {{module}}"
+ @echo "OS: {{os()}} ({{arch()}})"
+ @zig version | xargs -I{} echo "Zig: {}"
+
+[group('util')]
+clean:
+ -rm -rf zig-out
+ -rm -rf .zig-cache
+ @echo "Build artifacts cleaned"
diff --git a/PROJECTS/advanced/hsm-emulator/pkcs11.map b/PROJECTS/advanced/hsm-emulator/pkcs11.map
new file mode 100644
index 00000000..a2b8bda3
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/pkcs11.map
@@ -0,0 +1,8 @@
+/* ©AngelaMos | 2026 */
+/* pkcs11.map */
+PKCS11_2_40 {
+ global:
+ C_GetFunctionList;
+ local:
+ *;
+};
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/crypto_ops.zig b/PROJECTS/advanced/hsm-emulator/src/api/crypto_ops.zig
new file mode 100644
index 00000000..06dd90a0
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/crypto_ops.zig
@@ -0,0 +1,943 @@
+// ©AngelaMos | 2026
+// crypto_ops.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const state = @import("../core/state.zig");
+const session = @import("../core/session.zig");
+const object_store = @import("../core/object_store.zig");
+const digest = @import("../crypto/digest.zig");
+const mac = @import("../crypto/mac.zig");
+const cipher = @import("../crypto/cipher.zig");
+const ecdsa = @import("../crypto/ecdsa.zig");
+const rsa = @import("../crypto/rsa.zig");
+
+fn part(p: [*]ck.CK_BYTE, len: ck.CK_ULONG) []const u8 {
+ return p[0..@intCast(len)];
+}
+
+fn ctEql(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+ var diff: u8 = 0;
+ for (a, b) |x, y| diff |= x ^ y;
+ return diff == 0;
+}
+
+fn objectClass(obj: *const object_store.Object) ?ck.CK_OBJECT_CLASS {
+ const v = obj.get(ck.CKA_CLASS) orelse return null;
+ if (v.len != @sizeOf(ck.CK_OBJECT_CLASS)) return null;
+ return std.mem.bytesToValue(ck.CK_OBJECT_CLASS, v[0..@sizeOf(ck.CK_OBJECT_CLASS)]);
+}
+
+fn keyType(obj: *const object_store.Object) ?ck.CK_KEY_TYPE {
+ const v = obj.get(ck.CKA_KEY_TYPE) orelse return null;
+ if (v.len != @sizeOf(ck.CK_KEY_TYPE)) return null;
+ return std.mem.bytesToValue(ck.CK_KEY_TYPE, v[0..@sizeOf(ck.CK_KEY_TYPE)]);
+}
+
+const KeyVal = union(enum) {
+ ok: []const u8,
+ err: ck.CK_RV,
+};
+
+fn secretKeyValue(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) KeyVal {
+ const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (objectClass(obj) != ck.CKO_SECRET_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
+ const a = obj.findPtr(ck.CKA_VALUE) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (a.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
+ return .{ .ok = a.value };
+}
+
+const EcKey = union(enum) {
+ ok: struct { curve: ecdsa.Curve, material: []const u8 },
+ err: ck.CK_RV,
+};
+
+fn ecPrivateKey(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE) EcKey {
+ const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (objectClass(obj) != ck.CKO_PRIVATE_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (keyType(obj) != ck.CKK_EC) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (obj.has(ck.CKA_SIGN) and !obj.getBool(ck.CKA_SIGN)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
+ const params = obj.get(ck.CKA_EC_PARAMS) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ const curve = ecdsa.curveFromParams(params) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ const sa = obj.findPtr(ck.CKA_VALUE) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (sa.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
+ if (sa.value.len != curve.scalarLen()) return .{ .err = ck.CKR_FUNCTION_FAILED };
+ return .{ .ok = .{ .curve = curve, .material = sa.value } };
+}
+
+fn ecPublicKey(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE) EcKey {
+ const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (objectClass(obj) != ck.CKO_PUBLIC_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (keyType(obj) != ck.CKK_EC) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (obj.has(ck.CKA_VERIFY) and !obj.getBool(ck.CKA_VERIFY)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
+ const params = obj.get(ck.CKA_EC_PARAMS) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ const curve = ecdsa.curveFromParams(params) orelse return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ const der = obj.get(ck.CKA_EC_POINT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ const point = ecdsa.unwrapEcPoint(der) orelse return .{ .err = ck.CKR_FUNCTION_FAILED };
+ if (point.len != curve.pointLen()) return .{ .err = ck.CKR_FUNCTION_FAILED };
+ return .{ .ok = .{ .curve = curve, .material = point } };
+}
+
+const RsaPriv = union(enum) {
+ ok: rsa.PrivateComponents,
+ err: ck.CK_RV,
+};
+
+const RsaPub = union(enum) {
+ ok: rsa.PublicComponents,
+ err: ck.CK_RV,
+};
+
+fn rsaPrivateComponents(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) RsaPriv {
+ const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (objectClass(obj) != ck.CKO_PRIVATE_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (keyType(obj) != ck.CKK_RSA) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
+ if (obj.findPtr(ck.CKA_PRIVATE_EXPONENT)) |da| {
+ if (da.sealed) return .{ .err = ck.CKR_USER_NOT_LOGGED_IN };
+ }
+ return .{ .ok = .{
+ .n = obj.get(ck.CKA_MODULUS) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .e = obj.get(ck.CKA_PUBLIC_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .d = obj.get(ck.CKA_PRIVATE_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .p = obj.get(ck.CKA_PRIME_1) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .q = obj.get(ck.CKA_PRIME_2) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .dmp1 = obj.get(ck.CKA_EXPONENT_1) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .dmq1 = obj.get(ck.CKA_EXPONENT_2) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .iqmp = obj.get(ck.CKA_COEFFICIENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ } };
+}
+
+fn rsaPublicComponents(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, usage: ck.CK_ATTRIBUTE_TYPE) RsaPub {
+ const obj = inst.objects.getPtr(hKey) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (!object_store.visible(obj, inst.logged_in)) return .{ .err = ck.CKR_KEY_HANDLE_INVALID };
+ if (objectClass(obj) != ck.CKO_PUBLIC_KEY) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (keyType(obj) != ck.CKK_RSA) return .{ .err = ck.CKR_KEY_TYPE_INCONSISTENT };
+ if (obj.has(usage) and !obj.getBool(usage)) return .{ .err = ck.CKR_KEY_FUNCTION_NOT_PERMITTED };
+ return .{ .ok = .{
+ .n = obj.get(ck.CKA_MODULUS) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ .e = obj.get(ck.CKA_PUBLIC_EXPONENT) orelse return .{ .err = ck.CKR_KEY_HANDLE_INVALID },
+ } };
+}
+
+fn isRsaSignMech(mech: ck.CK_MECHANISM_TYPE) bool {
+ return switch (mech) {
+ ck.CKM_RSA_PKCS, ck.CKM_SHA256_RSA_PKCS, ck.CKM_RSA_PKCS_PSS, ck.CKM_SHA256_RSA_PKCS_PSS => true,
+ else => false,
+ };
+}
+
+fn mgfHash(mgf: ck.CK_RSA_PKCS_MGF_TYPE) ?rsa.Hash {
+ return switch (mgf) {
+ ck.CKG_MGF1_SHA256 => .sha256,
+ ck.CKG_MGF1_SHA384 => .sha384,
+ ck.CKG_MGF1_SHA512 => .sha512,
+ else => null,
+ };
+}
+
+const SignParamsResult = union(enum) {
+ ok: rsa.SignParams,
+ err: ck.CK_RV,
+};
+
+fn parsePss(pMechanism: *ck.CK_MECHANISM, digest_hash: rsa.Hash) SignParamsResult {
+ var params: rsa.SignParams = .{
+ .scheme = .pss,
+ .digest = digest_hash,
+ .pss_hash = if (digest_hash == .none) .sha256 else digest_hash,
+ };
+ const p = pMechanism.pParameter orelse {
+ if (digest_hash == .none) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ return .{ .ok = params };
+ };
+ if (pMechanism.ulParameterLen != @sizeOf(ck.CK_RSA_PKCS_PSS_PARAMS)) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ const pp: *const ck.CK_RSA_PKCS_PSS_PARAMS = @ptrCast(@alignCast(p));
+ const h = rsa.Hash.fromMech(pp.hashAlg) orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (digest_hash != .none and h != digest_hash) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (mgfHash(pp.mgf) != h) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (pp.sLen > rsa.max_modulus_bytes) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ params.pss_hash = h;
+ params.salt_len = @intCast(pp.sLen);
+ return .{ .ok = params };
+}
+
+fn rsaSignParams(pMechanism: *ck.CK_MECHANISM) SignParamsResult {
+ return switch (pMechanism.mechanism) {
+ ck.CKM_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1, .digest = .none } },
+ ck.CKM_SHA256_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1, .digest = .sha256 } },
+ ck.CKM_RSA_PKCS_PSS => parsePss(pMechanism, .none),
+ ck.CKM_SHA256_RSA_PKCS_PSS => parsePss(pMechanism, .sha256),
+ else => .{ .err = ck.CKR_MECHANISM_INVALID },
+ };
+}
+
+const CryptParamsResult = union(enum) {
+ ok: rsa.CryptParams,
+ err: ck.CK_RV,
+};
+
+fn rsaCryptParams(pMechanism: *ck.CK_MECHANISM) CryptParamsResult {
+ return switch (pMechanism.mechanism) {
+ ck.CKM_RSA_PKCS => .{ .ok = .{ .scheme = .pkcs1 } },
+ ck.CKM_RSA_PKCS_OAEP => blk: {
+ const p = pMechanism.pParameter orelse break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (pMechanism.ulParameterLen != @sizeOf(ck.CK_RSA_PKCS_OAEP_PARAMS)) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ const op: *const ck.CK_RSA_PKCS_OAEP_PARAMS = @ptrCast(@alignCast(p));
+ const h = rsa.Hash.fromMech(op.hashAlg) orelse break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (mgfHash(op.mgf) != h) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (op.ulSourceDataLen != 0) break :blk .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ break :blk .{ .ok = .{ .scheme = .oaep, .oaep_hash = h } };
+ },
+ else => .{ .err = ck.CKR_MECHANISM_INVALID },
+ };
+}
+
+fn signLen(op: *const session.SignOp) ck.CK_ULONG {
+ return switch (op.*) {
+ .mac => |*m| @intCast(m.macLen()),
+ .ec => |*e| @intCast(e.sigLen()),
+ .rsa => |*r| @intCast(r.sig_len),
+ };
+}
+
+fn emitSign(inst: *state.Instance, sess: *session.Session, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) ck.CK_RV {
+ const op = &sess.sign_op.?;
+ const slen = signLen(op);
+ if (pSignature == null) {
+ pulSignatureLen.* = slen;
+ return ck.CKR_OK;
+ }
+ if (pulSignatureLen.* < slen) {
+ pulSignatureLen.* = slen;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const out = pSignature.?[0..@intCast(slen)];
+ switch (op.*) {
+ .mac => |*m| m.finalInto(out),
+ .ec => |*e| _ = e.finalInto(inst.io(), out) catch {
+ sess.endSign();
+ return ck.CKR_FUNCTION_FAILED;
+ },
+ .rsa => return ck.CKR_FUNCTION_FAILED,
+ }
+ pulSignatureLen.* = slen;
+ sess.endSign();
+ return ck.CKR_OK;
+}
+
+fn finalizeVerify(sess: *session.Session, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) ck.CK_RV {
+ const sig = pSignature[0..@intCast(ulSignatureLen)];
+ const rv = switch (sess.verify_op.?) {
+ .mac => |*m| blk: {
+ const mlen: ck.CK_ULONG = @intCast(m.macLen());
+ var computed: [mac.max_mac_len]u8 = undefined;
+ m.finalInto(computed[0..@intCast(mlen)]);
+ if (ulSignatureLen != mlen) break :blk ck.CKR_SIGNATURE_LEN_RANGE;
+ if (!ctEql(computed[0..@intCast(mlen)], sig)) break :blk ck.CKR_SIGNATURE_INVALID;
+ break :blk ck.CKR_OK;
+ },
+ .ec => |*e| switch (e.finalVerify(sig)) {
+ .ok => ck.CKR_OK,
+ .invalid => ck.CKR_SIGNATURE_INVALID,
+ .len_range => ck.CKR_SIGNATURE_LEN_RANGE,
+ },
+ .rsa => ck.CKR_FUNCTION_FAILED,
+ };
+ sess.endVerify();
+ return rv;
+}
+
+fn signInitOp(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, pMechanism: *ck.CK_MECHANISM) union(enum) { ok: session.SignOp, err: ck.CK_RV } {
+ const mech = pMechanism.mechanism;
+ if (mac.macLenOf(mech) != null) {
+ const val = switch (secretKeyValue(inst, hKey, ck.CKA_SIGN)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |v| v,
+ };
+ return .{ .ok = .{ .mac = mac.Mac.init(mech, val) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
+ }
+ if (ecdsa.hashModeOf(mech) != null) {
+ const k = switch (ecPrivateKey(inst, hKey)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |v| v,
+ };
+ return .{ .ok = .{ .ec = ecdsa.SignState.init(k.curve, mech, k.material) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
+ }
+ if (isRsaSignMech(mech)) {
+ const params = switch (rsaSignParams(pMechanism)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |p| p,
+ };
+ const pc = switch (rsaPrivateComponents(inst, hKey, ck.CKA_SIGN)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |c| c,
+ };
+ return .{ .ok = .{ .rsa = .{ .key = hKey, .params = params, .sig_len = pc.n.len } } };
+ }
+ return .{ .err = ck.CKR_MECHANISM_INVALID };
+}
+
+fn verifyInitOp(inst: *state.Instance, hKey: ck.CK_OBJECT_HANDLE, pMechanism: *ck.CK_MECHANISM) union(enum) { ok: session.VerifyOp, err: ck.CK_RV } {
+ const mech = pMechanism.mechanism;
+ if (mac.macLenOf(mech) != null) {
+ const val = switch (secretKeyValue(inst, hKey, ck.CKA_VERIFY)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |v| v,
+ };
+ return .{ .ok = .{ .mac = mac.Mac.init(mech, val) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
+ }
+ if (ecdsa.hashModeOf(mech) != null) {
+ const k = switch (ecPublicKey(inst, hKey)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |v| v,
+ };
+ return .{ .ok = .{ .ec = ecdsa.VerifyState.init(k.curve, mech, k.material) orelse return .{ .err = ck.CKR_MECHANISM_INVALID } } };
+ }
+ if (isRsaSignMech(mech)) {
+ const params = switch (rsaSignParams(pMechanism)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |p| p,
+ };
+ const pc = switch (rsaPublicComponents(inst, hKey, ck.CKA_VERIFY)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |c| c,
+ };
+ return .{ .ok = .{ .rsa = .{ .key = hKey, .params = params, .sig_len = pc.n.len } } };
+ }
+ return .{ .err = ck.CKR_MECHANISM_INVALID };
+}
+
+fn mapCipherErr(e: cipher.Error) ck.CK_RV {
+ return switch (e) {
+ cipher.Error.DataLenRange => ck.CKR_DATA_LEN_RANGE,
+ cipher.Error.EncryptedDataLenRange => ck.CKR_ENCRYPTED_DATA_LEN_RANGE,
+ cipher.Error.EncryptedDataInvalid => ck.CKR_ENCRYPTED_DATA_INVALID,
+ cipher.Error.KeySize => ck.CKR_KEY_SIZE_RANGE,
+ cipher.Error.AadTooLarge => ck.CKR_ARGUMENTS_BAD,
+ cipher.Error.IvInvalid => ck.CKR_MECHANISM_PARAM_INVALID,
+ };
+}
+
+const CipherInit = union(enum) {
+ ok: cipher.Cipher,
+ err: ck.CK_RV,
+};
+
+fn buildCipher(inst: *state.Instance, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE, encrypt: bool, usage: ck.CK_ATTRIBUTE_TYPE) CipherInit {
+ const mode = cipher.modeOf(pMechanism.mechanism) orelse return .{ .err = ck.CKR_MECHANISM_INVALID };
+ const val = switch (secretKeyValue(inst, hKey, usage)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |v| v,
+ };
+ if (!cipher.validKeyLen(val.len)) return .{ .err = ck.CKR_KEY_SIZE_RANGE };
+
+ var c: cipher.Cipher = .{ .mode = mode, .encrypt = encrypt, .key_len = @intCast(val.len) };
+ @memcpy(c.key_buf[0..val.len], val);
+
+ switch (mode) {
+ .cbc, .cbc_pad => {
+ const p = pMechanism.pParameter orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (pMechanism.ulParameterLen != config.aes_block_len) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ @memcpy(&c.chain, @as([*]const u8, @ptrCast(p))[0..config.aes_block_len]);
+ },
+ .gcm => {
+ const p = pMechanism.pParameter orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (pMechanism.ulParameterLen != @sizeOf(ck.CK_GCM_PARAMS)) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ const gp: *const ck.CK_GCM_PARAMS = @ptrCast(@alignCast(p));
+ if (gp.ulIvLen != config.gcm_iv_len) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (gp.ulIvBits != 0 and gp.ulIvBits != config.gcm_iv_bits) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ if (gp.ulTagBits != config.gcm_tag_bits) return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ const ivp = gp.pIv orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ @memcpy(&c.iv, ivp[0..config.gcm_iv_len]);
+ const aad_len: usize = @intCast(gp.ulAADLen);
+ if (aad_len > config.max_gcm_aad_len) return .{ .err = ck.CKR_ARGUMENTS_BAD };
+ if (aad_len > 0) {
+ const ap = gp.pAAD orelse return .{ .err = ck.CKR_MECHANISM_PARAM_INVALID };
+ @memcpy(c.aad_buf[0..aad_len], ap[0..aad_len]);
+ }
+ c.aad_len = aad_len;
+ },
+ }
+ return .{ .ok = c };
+}
+
+fn updateOutLen(op: *const cipher.Cipher, in_len: usize) ck.CK_ULONG {
+ return @intCast(((op.partial_len + in_len) / config.aes_block_len) * config.aes_block_len);
+}
+
+fn emitDigest(sess: *session.Session, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) ck.CK_RV {
+ const op = &sess.digest_op.?;
+ const dlen: ck.CK_ULONG = @intCast(op.digestLen());
+ if (pDigest == null) {
+ pulDigestLen.* = dlen;
+ return ck.CKR_OK;
+ }
+ if (pulDigestLen.* < dlen) {
+ pulDigestLen.* = dlen;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ op.finalInto(pDigest.?[0..@intCast(dlen)]);
+ pulDigestLen.* = dlen;
+ sess.endDigest();
+ return ck.CKR_OK;
+}
+
+fn isRsaCryptMech(mech: ck.CK_MECHANISM_TYPE) bool {
+ return mech == ck.CKM_RSA_PKCS or mech == ck.CKM_RSA_PKCS_OAEP;
+}
+
+const RsaCryptResult = union(enum) {
+ ok: session.RsaCrypt,
+ err: ck.CK_RV,
+};
+
+fn rsaCryptInit(inst: *state.Instance, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE, private: bool, usage: ck.CK_ATTRIBUTE_TYPE) RsaCryptResult {
+ const params = switch (rsaCryptParams(pMechanism)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |p| p,
+ };
+ const mod_len = if (private) switch (rsaPrivateComponents(inst, hKey, usage)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |c| c.n.len,
+ } else switch (rsaPublicComponents(inst, hKey, usage)) {
+ .err => |rv| return .{ .err = rv },
+ .ok => |c| c.n.len,
+ };
+ return .{ .ok = .{ .key = hKey, .params = params, .out_len = mod_len } };
+}
+
+pub fn C_EncryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.encrypt_op != null) return ck.CKR_OPERATION_ACTIVE;
+ if (cipher.modeOf(pMechanism.mechanism) != null) {
+ sess.encrypt_op = .{ .aes = switch (buildCipher(inst, pMechanism, hKey, true, ck.CKA_ENCRYPT)) {
+ .err => |rv| return rv,
+ .ok => |c| c,
+ } };
+ return ck.CKR_OK;
+ }
+ if (isRsaCryptMech(pMechanism.mechanism)) {
+ sess.encrypt_op = .{ .rsa = switch (rsaCryptInit(inst, pMechanism, hKey, false, ck.CKA_ENCRYPT)) {
+ .err => |rv| return rv,
+ .ok => |o| o,
+ } };
+ return ck.CKR_OK;
+ }
+ return ck.CKR_MECHANISM_INVALID;
+}
+
+pub fn C_Encrypt(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pEncryptedData: ?[*]ck.CK_BYTE, pulEncryptedDataLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const in = part(pData, ulDataLen);
+
+ switch (op.*) {
+ .aes => |*c| {
+ const need: ck.CK_ULONG = @intCast(cipher.encryptOutLen(c.mode, in.len));
+ if (pEncryptedData == null) {
+ pulEncryptedDataLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulEncryptedDataLen.* < need) {
+ pulEncryptedDataLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const out = pEncryptedData.?[0..@intCast(need)];
+ var n: usize = 0;
+ switch (c.mode) {
+ .gcm => n = c.gcmEncrypt(in, out),
+ .cbc, .cbc_pad => {
+ n = c.encryptUpdate(in, out);
+ n += c.encryptFinal(out[n..]) catch |e| {
+ sess.endEncrypt();
+ return mapCipherErr(e);
+ };
+ },
+ }
+ pulEncryptedDataLen.* = @intCast(n);
+ sess.endEncrypt();
+ return ck.CKR_OK;
+ },
+ .rsa => |*r| {
+ const need: ck.CK_ULONG = @intCast(r.out_len);
+ if (pEncryptedData == null) {
+ pulEncryptedDataLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulEncryptedDataLen.* < need) {
+ pulEncryptedDataLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const pc = switch (rsaPublicComponents(inst, r.key, ck.CKA_ENCRYPT)) {
+ .err => |rv| {
+ sess.endEncrypt();
+ return rv;
+ },
+ .ok => |c| c,
+ };
+ const n = rsa.encrypt(pc, r.params, in, pEncryptedData.?[0..@intCast(need)]) catch {
+ sess.endEncrypt();
+ return ck.CKR_DATA_LEN_RANGE;
+ };
+ pulEncryptedDataLen.* = @intCast(n);
+ sess.endEncrypt();
+ return ck.CKR_OK;
+ },
+ }
+}
+
+pub fn C_EncryptUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG, pEncryptedPart: ?[*]ck.CK_BYTE, pulEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const c = switch (op.*) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ .aes => |*x| x,
+ };
+ if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
+
+ const in = part(pPart, ulPartLen);
+ const need = updateOutLen(c, in.len);
+ if (pEncryptedPart == null) {
+ pulEncryptedPartLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulEncryptedPartLen.* < need) {
+ pulEncryptedPartLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ pulEncryptedPartLen.* = @intCast(c.encryptUpdate(in, pEncryptedPart.?[0..@intCast(need)]));
+ return ck.CKR_OK;
+}
+
+pub fn C_EncryptFinal(hSession: ck.CK_SESSION_HANDLE, pLastEncryptedPart: ?[*]ck.CK_BYTE, pulLastEncryptedPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.encrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const c = switch (op.*) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ .aes => |*x| x,
+ };
+ if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
+
+ const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
+ if (pLastEncryptedPart == null) {
+ pulLastEncryptedPartLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulLastEncryptedPartLen.* < need) {
+ pulLastEncryptedPartLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const n = c.encryptFinal(pLastEncryptedPart.?[0..@intCast(need)]) catch |e| {
+ sess.endEncrypt();
+ return mapCipherErr(e);
+ };
+ pulLastEncryptedPartLen.* = @intCast(n);
+ sess.endEncrypt();
+ return ck.CKR_OK;
+}
+
+pub fn C_DecryptInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.decrypt_op != null) return ck.CKR_OPERATION_ACTIVE;
+ if (cipher.modeOf(pMechanism.mechanism) != null) {
+ sess.decrypt_op = .{ .aes = switch (buildCipher(inst, pMechanism, hKey, false, ck.CKA_DECRYPT)) {
+ .err => |rv| return rv,
+ .ok => |c| c,
+ } };
+ return ck.CKR_OK;
+ }
+ if (isRsaCryptMech(pMechanism.mechanism)) {
+ sess.decrypt_op = .{ .rsa = switch (rsaCryptInit(inst, pMechanism, hKey, true, ck.CKA_DECRYPT)) {
+ .err => |rv| return rv,
+ .ok => |o| o,
+ } };
+ return ck.CKR_OK;
+ }
+ return ck.CKR_MECHANISM_INVALID;
+}
+
+pub fn C_Decrypt(hSession: ck.CK_SESSION_HANDLE, pEncryptedData: [*]ck.CK_BYTE, ulEncryptedDataLen: ck.CK_ULONG, pData: ?[*]ck.CK_BYTE, pulDataLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const in = part(pEncryptedData, ulEncryptedDataLen);
+
+ switch (op.*) {
+ .aes => |*c| {
+ const need: ck.CK_ULONG = @intCast(cipher.decryptOutLen(c.mode, in.len));
+ if (pData == null) {
+ pulDataLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulDataLen.* < need) {
+ pulDataLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const out = pData.?[0..@intCast(need)];
+ var n: usize = 0;
+ switch (c.mode) {
+ .gcm => n = c.gcmDecrypt(in, out) catch |e| {
+ sess.endDecrypt();
+ return mapCipherErr(e);
+ },
+ .cbc, .cbc_pad => {
+ n = c.decryptUpdate(in, out);
+ n += c.decryptFinal(out[n..]) catch |e| {
+ sess.endDecrypt();
+ return mapCipherErr(e);
+ };
+ },
+ }
+ pulDataLen.* = @intCast(n);
+ sess.endDecrypt();
+ return ck.CKR_OK;
+ },
+ .rsa => |*r| {
+ const need: ck.CK_ULONG = @intCast(r.out_len);
+ if (pData == null) {
+ pulDataLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulDataLen.* < need) {
+ pulDataLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const sc = switch (rsaPrivateComponents(inst, r.key, ck.CKA_DECRYPT)) {
+ .err => |rv| {
+ sess.endDecrypt();
+ return rv;
+ },
+ .ok => |c| c,
+ };
+ const n = rsa.decrypt(sc, r.params, in, pData.?[0..@intCast(need)]) catch {
+ sess.endDecrypt();
+ return ck.CKR_ENCRYPTED_DATA_INVALID;
+ };
+ pulDataLen.* = @intCast(n);
+ sess.endDecrypt();
+ return ck.CKR_OK;
+ },
+ }
+}
+
+pub fn C_DecryptUpdate(hSession: ck.CK_SESSION_HANDLE, pEncryptedPart: [*]ck.CK_BYTE, ulEncryptedPartLen: ck.CK_ULONG, pPart: ?[*]ck.CK_BYTE, pulPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const c = switch (op.*) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ .aes => |*x| x,
+ };
+ if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
+
+ const in = part(pEncryptedPart, ulEncryptedPartLen);
+ const need = updateOutLen(c, in.len);
+ if (pPart == null) {
+ pulPartLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulPartLen.* < need) {
+ pulPartLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ pulPartLen.* = @intCast(c.decryptUpdate(in, pPart.?[0..@intCast(need)]));
+ return ck.CKR_OK;
+}
+
+pub fn C_DecryptFinal(hSession: ck.CK_SESSION_HANDLE, pLastPart: ?[*]ck.CK_BYTE, pulLastPartLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const op = if (sess.decrypt_op) |*o| o else return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const c = switch (op.*) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ .aes => |*x| x,
+ };
+ if (c.mode == .gcm) return ck.CKR_FUNCTION_NOT_SUPPORTED;
+
+ const need: ck.CK_ULONG = if (c.mode == .cbc_pad) config.aes_block_len else 0;
+ if (pLastPart == null) {
+ pulLastPartLen.* = need;
+ return ck.CKR_OK;
+ }
+ if (pulLastPartLen.* < need) {
+ pulLastPartLen.* = need;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ const n = c.decryptFinal(pLastPart.?[0..@intCast(need)]) catch |e| {
+ sess.endDecrypt();
+ return mapCipherErr(e);
+ };
+ pulLastPartLen.* = @intCast(n);
+ sess.endDecrypt();
+ return ck.CKR_OK;
+}
+
+pub fn C_DigestInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.digest_op != null) return ck.CKR_OPERATION_ACTIVE;
+ sess.digest_op = digest.Hasher.init(pMechanism.mechanism) orelse return ck.CKR_MECHANISM_INVALID;
+ return ck.CKR_OK;
+}
+
+pub fn C_Digest(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const dlen: ck.CK_ULONG = @intCast(sess.digest_op.?.digestLen());
+ if (pDigest == null) {
+ pulDigestLen.* = dlen;
+ return ck.CKR_OK;
+ }
+ if (pulDigestLen.* < dlen) {
+ pulDigestLen.* = dlen;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ sess.digest_op.?.update(part(pData, ulDataLen));
+ return emitDigest(sess, pDigest, pulDigestLen);
+}
+
+pub fn C_DigestUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ sess.digest_op.?.update(part(pPart, ulPartLen));
+ return ck.CKR_OK;
+}
+
+pub fn C_DigestKey(hSession: ck.CK_SESSION_HANDLE, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const obj = inst.objects.getPtr(hKey) orelse return ck.CKR_KEY_HANDLE_INVALID;
+ if (!object_store.visible(obj, inst.logged_in)) return ck.CKR_KEY_HANDLE_INVALID;
+ const val = obj.get(ck.CKA_VALUE) orelse return ck.CKR_KEY_HANDLE_INVALID;
+ sess.digest_op.?.update(val);
+ return ck.CKR_OK;
+}
+
+pub fn C_DigestFinal(hSession: ck.CK_SESSION_HANDLE, pDigest: ?[*]ck.CK_BYTE, pulDigestLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.digest_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ return emitDigest(sess, pDigest, pulDigestLen);
+}
+
+pub fn C_SignInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.sign_op != null) return ck.CKR_OPERATION_ACTIVE;
+ sess.sign_op = switch (signInitOp(inst, hKey, pMechanism)) {
+ .err => |rv| return rv,
+ .ok => |op| op,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_Sign(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ const slen = signLen(&sess.sign_op.?);
+ if (pSignature == null) {
+ pulSignatureLen.* = slen;
+ return ck.CKR_OK;
+ }
+ if (pulSignatureLen.* < slen) {
+ pulSignatureLen.* = slen;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ switch (sess.sign_op.?) {
+ .rsa => |op| {
+ const out = pSignature.?[0..@intCast(slen)];
+ const sc = switch (rsaPrivateComponents(inst, op.key, ck.CKA_SIGN)) {
+ .err => |rv| {
+ sess.endSign();
+ return rv;
+ },
+ .ok => |c| c,
+ };
+ const n = rsa.sign(sc, op.params, part(pData, ulDataLen), out) catch {
+ sess.endSign();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ pulSignatureLen.* = @intCast(n);
+ sess.endSign();
+ return ck.CKR_OK;
+ },
+ else => {
+ sess.sign_op.?.update(part(pData, ulDataLen));
+ return emitSign(inst, sess, pSignature, pulSignatureLen);
+ },
+ }
+}
+
+pub fn C_SignUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ switch (sess.sign_op.?) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ else => {},
+ }
+ sess.sign_op.?.update(part(pPart, ulPartLen));
+ return ck.CKR_OK;
+}
+
+pub fn C_SignFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: ?[*]ck.CK_BYTE, pulSignatureLen: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.sign_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ switch (sess.sign_op.?) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ else => {},
+ }
+ return emitSign(inst, sess, pSignature, pulSignatureLen);
+}
+
+pub fn C_SignRecoverInit(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_SignRecover(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_VerifyInit(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, hKey: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.verify_op != null) return ck.CKR_OPERATION_ACTIVE;
+ sess.verify_op = switch (verifyInitOp(inst, hKey, pMechanism)) {
+ .err => |rv| return rv,
+ .ok => |op| op,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_Verify(hSession: ck.CK_SESSION_HANDLE, pData: [*]ck.CK_BYTE, ulDataLen: ck.CK_ULONG, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ switch (sess.verify_op.?) {
+ .rsa => |op| {
+ const data = part(pData, ulDataLen);
+ sess.endVerify();
+ if (ulSignatureLen != op.sig_len) return ck.CKR_SIGNATURE_LEN_RANGE;
+ const pc = switch (rsaPublicComponents(inst, op.key, ck.CKA_VERIFY)) {
+ .err => |rv| return rv,
+ .ok => |c| c,
+ };
+ const r = rsa.verify(pc, op.params, data, pSignature[0..@intCast(ulSignatureLen)]) catch return ck.CKR_FUNCTION_FAILED;
+ return switch (r) {
+ .ok => ck.CKR_OK,
+ .invalid => ck.CKR_SIGNATURE_INVALID,
+ };
+ },
+ else => {
+ sess.verify_op.?.update(part(pData, ulDataLen));
+ return finalizeVerify(sess, pSignature, ulSignatureLen);
+ },
+ }
+}
+
+pub fn C_VerifyUpdate(hSession: ck.CK_SESSION_HANDLE, pPart: [*]ck.CK_BYTE, ulPartLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ switch (sess.verify_op.?) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ else => {},
+ }
+ sess.verify_op.?.update(part(pPart, ulPartLen));
+ return ck.CKR_OK;
+}
+
+pub fn C_VerifyFinal(hSession: ck.CK_SESSION_HANDLE, pSignature: [*]ck.CK_BYTE, ulSignatureLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.verify_op == null) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ switch (sess.verify_op.?) {
+ .rsa => return ck.CKR_FUNCTION_NOT_SUPPORTED,
+ else => {},
+ }
+ return finalizeVerify(sess, pSignature, ulSignatureLen);
+}
+
+pub fn C_VerifyRecoverInit(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_VerifyRecover(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_DigestEncryptUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_DecryptDigestUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_SignEncryptUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_DecryptVerifyUpdate(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/general.zig b/PROJECTS/advanced/hsm-emulator/src/api/general.zig
new file mode 100644
index 00000000..a67d6a0d
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/general.zig
@@ -0,0 +1,49 @@
+// ©AngelaMos | 2026
+// general.zig
+
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const util = @import("../util.zig");
+const state = @import("../core/state.zig");
+
+pub fn C_Initialize(pInitArgs: ?*anyopaque) callconv(.c) ck.CK_RV {
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (state.isInitialized()) return ck.CKR_CRYPTOKI_ALREADY_INITIALIZED;
+ switch (state.parseInitArgs(pInitArgs)) {
+ .err => |rv| return rv,
+ .ok => |locking| state.initialize(locking),
+ }
+ return ck.CKR_OK;
+}
+
+pub fn C_Finalize(pReserved: ?*anyopaque) callconv(.c) ck.CK_RV {
+ if (pReserved != null) return ck.CKR_ARGUMENTS_BAD;
+ return state.finalize();
+}
+
+pub fn C_GetInfo(pInfo: *ck.CK_INFO) callconv(.c) ck.CK_RV {
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (!state.isInitialized()) return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ pInfo.* = .{
+ .cryptokiVersion = config.cryptoki_version,
+ .manufacturerID = util.padded(32, config.manufacturer_id),
+ .flags = 0,
+ .libraryDescription = util.padded(32, config.library_description),
+ .libraryVersion = config.library_version,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_GetFunctionStatus(_: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_PARALLEL;
+}
+
+pub fn C_CancelFunction(_: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_PARALLEL;
+}
+
+pub fn C_WaitForSlotEvent(_: ck.CK_FLAGS, _: *ck.CK_SLOT_ID, _: ?*anyopaque) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/keymgmt.zig b/PROJECTS/advanced/hsm-emulator/src/api/keymgmt.zig
new file mode 100644
index 00000000..9f4cbd1d
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/keymgmt.zig
@@ -0,0 +1,230 @@
+// ©AngelaMos | 2026
+// keymgmt.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const state = @import("../core/state.zig");
+const object_store = @import("../core/object_store.zig");
+const object = @import("object.zig");
+const ecdsa = @import("../crypto/ecdsa.zig");
+const rsa = @import("../crypto/rsa.zig");
+
+const Object = object_store.Object;
+
+fn attrBytes(a: ck.CK_ATTRIBUTE) []const u8 {
+ const ptr = a.pValue orelse return &.{};
+ return @as([*]const u8, @ptrCast(ptr))[0..@intCast(a.ulValueLen)];
+}
+
+fn ulongFrom(bytes: []const u8) ?ck.CK_ULONG {
+ if (bytes.len != @sizeOf(ck.CK_ULONG)) return null;
+ return std.mem.bytesToValue(ck.CK_ULONG, bytes[0..@sizeOf(ck.CK_ULONG)]);
+}
+
+pub fn C_GenerateKey(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (pMechanism.mechanism != ck.CKM_AES_KEY_GEN) return ck.CKR_MECHANISM_INVALID;
+
+ const allocator = inst.allocator();
+ const template = if (ulCount == 0) &[_]ck.CK_ATTRIBUTE{} else pTemplate[0..@intCast(ulCount)];
+
+ var key_len: usize = 0;
+ var have_len = false;
+ for (template) |a| {
+ if (a.type == ck.CKA_VALUE_LEN) {
+ const v = ulongFrom(attrBytes(a)) orelse return ck.CKR_ATTRIBUTE_VALUE_INVALID;
+ key_len = @intCast(v);
+ have_len = true;
+ }
+ }
+ if (!have_len) return ck.CKR_TEMPLATE_INCOMPLETE;
+ if (key_len != config.aes_min_key_bytes and key_len != config.aes_max_key_bytes) return ck.CKR_KEY_SIZE_RANGE;
+
+ var obj: Object = .{};
+ var moved = false;
+ defer if (!moved) obj.deinit(allocator);
+
+ for (template) |a| {
+ obj.set(allocator, a.type, attrBytes(a)) catch |e| return object_store.mapSetErr(e);
+ }
+
+ var key_bytes: [32]u8 = undefined;
+ defer std.crypto.secureZero(u8, &key_bytes);
+ inst.io().randomSecure(key_bytes[0..key_len]) catch return ck.CKR_FUNCTION_FAILED;
+
+ var class_val: ck.CK_OBJECT_CLASS = ck.CKO_SECRET_KEY;
+ var type_val: ck.CK_KEY_TYPE = ck.CKK_AES;
+ obj.set(allocator, ck.CKA_CLASS, std.mem.asBytes(&class_val)) catch |e| return object_store.mapSetErr(e);
+ obj.set(allocator, ck.CKA_KEY_TYPE, std.mem.asBytes(&type_val)) catch |e| return object_store.mapSetErr(e);
+ obj.set(allocator, ck.CKA_VALUE, key_bytes[0..key_len]) catch |e| return object_store.mapSetErr(e);
+ obj.set(allocator, ck.CKA_LOCAL, &[_]u8{ck.CK_TRUE}) catch |e| return object_store.mapSetErr(e);
+ const kgm: ck.CK_MECHANISM_TYPE = ck.CKM_AES_KEY_GEN;
+ obj.set(allocator, ck.CKA_KEY_GEN_MECHANISM, std.mem.asBytes(&kgm)) catch |e| return object_store.mapSetErr(e);
+
+ if (!obj.has(ck.CKA_SENSITIVE)) obj.set(allocator, ck.CKA_SENSITIVE, &[_]u8{ck.CK_TRUE}) catch |e| return object_store.mapSetErr(e);
+ if (!obj.has(ck.CKA_EXTRACTABLE)) obj.set(allocator, ck.CKA_EXTRACTABLE, &[_]u8{ck.CK_FALSE}) catch |e| return object_store.mapSetErr(e);
+ const always_sensitive: u8 = if (obj.getBool(ck.CKA_SENSITIVE)) ck.CK_TRUE else ck.CK_FALSE;
+ const never_extractable: u8 = if (!obj.getBool(ck.CKA_EXTRACTABLE)) ck.CK_TRUE else ck.CK_FALSE;
+ obj.set(allocator, ck.CKA_ALWAYS_SENSITIVE, &[_]u8{always_sensitive}) catch |e| return object_store.mapSetErr(e);
+ obj.set(allocator, ck.CKA_NEVER_EXTRACTABLE, &[_]u8{never_extractable}) catch |e| return object_store.mapSetErr(e);
+
+ object.materializeDefaults(&obj, allocator, ck.CKO_SECRET_KEY) catch |e| return object_store.mapSetErr(e);
+
+ moved = true;
+ return object.insertNew(inst, sess, obj, phKey);
+}
+
+fn ecParamsFrom(template: []const ck.CK_ATTRIBUTE) ?[]const u8 {
+ for (template) |a| {
+ if (a.type == ck.CKA_EC_PARAMS) return attrBytes(a);
+ }
+ return null;
+}
+
+fn modulusBitsFrom(template: []const ck.CK_ATTRIBUTE) ?ck.CK_ULONG {
+ for (template) |a| {
+ if (a.type == ck.CKA_MODULUS_BITS) return ulongFrom(attrBytes(a));
+ }
+ return null;
+}
+
+fn applySensitivityDefaults(obj: *Object, allocator: std.mem.Allocator, kgm: ck.CK_MECHANISM_TYPE) !void {
+ try obj.set(allocator, ck.CKA_LOCAL, &[_]u8{ck.CK_TRUE});
+ if (!obj.has(ck.CKA_SENSITIVE)) try obj.set(allocator, ck.CKA_SENSITIVE, &[_]u8{ck.CK_TRUE});
+ if (!obj.has(ck.CKA_EXTRACTABLE)) try obj.set(allocator, ck.CKA_EXTRACTABLE, &[_]u8{ck.CK_FALSE});
+ const always_sensitive: u8 = if (obj.getBool(ck.CKA_SENSITIVE)) ck.CK_TRUE else ck.CK_FALSE;
+ const never_extractable: u8 = if (!obj.getBool(ck.CKA_EXTRACTABLE)) ck.CK_TRUE else ck.CK_FALSE;
+ try obj.set(allocator, ck.CKA_ALWAYS_SENSITIVE, &[_]u8{always_sensitive});
+ try obj.set(allocator, ck.CKA_NEVER_EXTRACTABLE, &[_]u8{never_extractable});
+ const m: ck.CK_MECHANISM_TYPE = kgm;
+ try obj.set(allocator, ck.CKA_KEY_GEN_MECHANISM, std.mem.asBytes(&m));
+}
+
+fn buildEcPublic(obj: *Object, allocator: std.mem.Allocator, template: []const ck.CK_ATTRIBUTE, curve: ecdsa.Curve, point: []const u8) !void {
+ for (template) |a| try obj.set(allocator, a.type, attrBytes(a));
+ const class_val: ck.CK_OBJECT_CLASS = ck.CKO_PUBLIC_KEY;
+ const type_val: ck.CK_KEY_TYPE = ck.CKK_EC;
+ try obj.set(allocator, ck.CKA_CLASS, std.mem.asBytes(&class_val));
+ try obj.set(allocator, ck.CKA_KEY_TYPE, std.mem.asBytes(&type_val));
+ try obj.set(allocator, ck.CKA_EC_PARAMS, curve.oidDer());
+ var der_buf: [ecdsa.max_ec_point_der]u8 = undefined;
+ try obj.set(allocator, ck.CKA_EC_POINT, ecdsa.wrapEcPoint(&der_buf, point));
+ try obj.set(allocator, ck.CKA_LOCAL, &[_]u8{ck.CK_TRUE});
+ const kgm: ck.CK_MECHANISM_TYPE = ck.CKM_EC_KEY_PAIR_GEN;
+ try obj.set(allocator, ck.CKA_KEY_GEN_MECHANISM, std.mem.asBytes(&kgm));
+ try object.materializeDefaults(obj, allocator, ck.CKO_PUBLIC_KEY);
+}
+
+fn buildEcPrivate(obj: *Object, allocator: std.mem.Allocator, template: []const ck.CK_ATTRIBUTE, curve: ecdsa.Curve, scalar: []const u8) !void {
+ for (template) |a| try obj.set(allocator, a.type, attrBytes(a));
+ const class_val: ck.CK_OBJECT_CLASS = ck.CKO_PRIVATE_KEY;
+ const type_val: ck.CK_KEY_TYPE = ck.CKK_EC;
+ try obj.set(allocator, ck.CKA_CLASS, std.mem.asBytes(&class_val));
+ try obj.set(allocator, ck.CKA_KEY_TYPE, std.mem.asBytes(&type_val));
+ try obj.set(allocator, ck.CKA_EC_PARAMS, curve.oidDer());
+ try obj.set(allocator, ck.CKA_VALUE, scalar);
+ try applySensitivityDefaults(obj, allocator, ck.CKM_EC_KEY_PAIR_GEN);
+ try object.materializeDefaults(obj, allocator, ck.CKO_PRIVATE_KEY);
+}
+
+fn buildRsaPublic(obj: *Object, allocator: std.mem.Allocator, template: []const ck.CK_ATTRIBUTE, g: *const rsa.Generated) !void {
+ for (template) |a| try obj.set(allocator, a.type, attrBytes(a));
+ const class_val: ck.CK_OBJECT_CLASS = ck.CKO_PUBLIC_KEY;
+ const type_val: ck.CK_KEY_TYPE = ck.CKK_RSA;
+ const bits: ck.CK_ULONG = g.bits;
+ try obj.set(allocator, ck.CKA_CLASS, std.mem.asBytes(&class_val));
+ try obj.set(allocator, ck.CKA_KEY_TYPE, std.mem.asBytes(&type_val));
+ try obj.set(allocator, ck.CKA_MODULUS, g.n.slice());
+ try obj.set(allocator, ck.CKA_PUBLIC_EXPONENT, g.e.slice());
+ try obj.set(allocator, ck.CKA_MODULUS_BITS, std.mem.asBytes(&bits));
+ try obj.set(allocator, ck.CKA_LOCAL, &[_]u8{ck.CK_TRUE});
+ const kgm: ck.CK_MECHANISM_TYPE = ck.CKM_RSA_PKCS_KEY_PAIR_GEN;
+ try obj.set(allocator, ck.CKA_KEY_GEN_MECHANISM, std.mem.asBytes(&kgm));
+ try object.materializeDefaults(obj, allocator, ck.CKO_PUBLIC_KEY);
+}
+
+fn buildRsaPrivate(obj: *Object, allocator: std.mem.Allocator, template: []const ck.CK_ATTRIBUTE, g: *const rsa.Generated) !void {
+ for (template) |a| try obj.set(allocator, a.type, attrBytes(a));
+ const class_val: ck.CK_OBJECT_CLASS = ck.CKO_PRIVATE_KEY;
+ const type_val: ck.CK_KEY_TYPE = ck.CKK_RSA;
+ try obj.set(allocator, ck.CKA_CLASS, std.mem.asBytes(&class_val));
+ try obj.set(allocator, ck.CKA_KEY_TYPE, std.mem.asBytes(&type_val));
+ try obj.set(allocator, ck.CKA_MODULUS, g.n.slice());
+ try obj.set(allocator, ck.CKA_PUBLIC_EXPONENT, g.e.slice());
+ try obj.set(allocator, ck.CKA_PRIVATE_EXPONENT, g.d.slice());
+ try obj.set(allocator, ck.CKA_PRIME_1, g.p.slice());
+ try obj.set(allocator, ck.CKA_PRIME_2, g.q.slice());
+ try obj.set(allocator, ck.CKA_EXPONENT_1, g.dmp1.slice());
+ try obj.set(allocator, ck.CKA_EXPONENT_2, g.dmq1.slice());
+ try obj.set(allocator, ck.CKA_COEFFICIENT, g.iqmp.slice());
+ try applySensitivityDefaults(obj, allocator, ck.CKM_RSA_PKCS_KEY_PAIR_GEN);
+ try object.materializeDefaults(obj, allocator, ck.CKO_PRIVATE_KEY);
+}
+
+pub fn C_GenerateKeyPair(hSession: ck.CK_SESSION_HANDLE, pMechanism: *ck.CK_MECHANISM, pPublicKeyTemplate: [*]ck.CK_ATTRIBUTE, ulPublicKeyAttributeCount: ck.CK_ULONG, pPrivateKeyTemplate: [*]ck.CK_ATTRIBUTE, ulPrivateKeyAttributeCount: ck.CK_ULONG, phPublicKey: *ck.CK_OBJECT_HANDLE, phPrivateKey: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+
+ const allocator = inst.allocator();
+ const pub_template = if (ulPublicKeyAttributeCount == 0) &[_]ck.CK_ATTRIBUTE{} else pPublicKeyTemplate[0..@intCast(ulPublicKeyAttributeCount)];
+ const priv_template = if (ulPrivateKeyAttributeCount == 0) &[_]ck.CK_ATTRIBUTE{} else pPrivateKeyTemplate[0..@intCast(ulPrivateKeyAttributeCount)];
+
+ var pub_obj: Object = .{};
+ var pub_moved = false;
+ defer if (!pub_moved) pub_obj.deinit(allocator);
+ var priv_obj: Object = .{};
+ var priv_moved = false;
+ defer if (!priv_moved) priv_obj.deinit(allocator);
+
+ switch (pMechanism.mechanism) {
+ ck.CKM_EC_KEY_PAIR_GEN => {
+ const params = ecParamsFrom(pub_template) orelse return ck.CKR_TEMPLATE_INCOMPLETE;
+ const curve = ecdsa.curveFromParams(params) orelse return ck.CKR_DOMAIN_PARAMS_INVALID;
+ var km = ecdsa.generate(inst.io(), curve) catch return ck.CKR_FUNCTION_FAILED;
+ defer std.crypto.secureZero(u8, &km.scalar);
+ buildEcPublic(&pub_obj, allocator, pub_template, curve, km.pointBytes()) catch |e| return object_store.mapSetErr(e);
+ buildEcPrivate(&priv_obj, allocator, priv_template, curve, km.scalarBytes()) catch |e| return object_store.mapSetErr(e);
+ },
+ ck.CKM_RSA_PKCS_KEY_PAIR_GEN => {
+ const bits = modulusBitsFrom(pub_template) orelse return ck.CKR_TEMPLATE_INCOMPLETE;
+ if (bits < config.rsa_min_key_bits or bits > config.rsa_max_key_bits) return ck.CKR_KEY_SIZE_RANGE;
+ var g = rsa.generate(@intCast(bits)) catch return ck.CKR_FUNCTION_FAILED;
+ defer g.zeroize();
+ buildRsaPublic(&pub_obj, allocator, pub_template, &g) catch |e| return object_store.mapSetErr(e);
+ buildRsaPrivate(&priv_obj, allocator, priv_template, &g) catch |e| return object_store.mapSetErr(e);
+ },
+ else => return ck.CKR_MECHANISM_INVALID,
+ }
+
+ const pub_is_token = pub_obj.isToken();
+ pub_moved = true;
+ const pub_rv = object.insertNew(inst, sess, pub_obj, phPublicKey);
+ if (pub_rv != ck.CKR_OK) return pub_rv;
+
+ priv_moved = true;
+ const priv_rv = object.insertNew(inst, sess, priv_obj, phPrivateKey);
+ if (priv_rv != ck.CKR_OK) {
+ _ = inst.objects.destroy(allocator, phPublicKey.*);
+ if (pub_is_token) object_store.save(inst.io(), allocator, &inst.objects, inst.mk) catch {};
+ return priv_rv;
+ }
+ return ck.CKR_OK;
+}
+
+pub fn C_WrapKey(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE, _: ck.CK_OBJECT_HANDLE, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_UnwrapKey(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: [*]ck.CK_ATTRIBUTE, _: ck.CK_ULONG, _: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_DeriveKey(_: ck.CK_SESSION_HANDLE, _: *ck.CK_MECHANISM, _: ck.CK_OBJECT_HANDLE, _: ?[*]ck.CK_ATTRIBUTE, _: ck.CK_ULONG, _: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/object.zig b/PROJECTS/advanced/hsm-emulator/src/api/object.zig
new file mode 100644
index 00000000..71516987
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/object.zig
@@ -0,0 +1,337 @@
+// ©AngelaMos | 2026
+// object.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const state = @import("../core/state.zig");
+const session = @import("../core/session.zig");
+const object_store = @import("../core/object_store.zig");
+
+const Object = object_store.Object;
+const mapSetErr = object_store.mapSetErr;
+
+fn inputTemplate(p: ?[*]ck.CK_ATTRIBUTE, count: ck.CK_ULONG) []ck.CK_ATTRIBUTE {
+ if (count == 0) return &.{};
+ return (p orelse return &.{})[0..@intCast(count)];
+}
+
+fn attrBytes(a: ck.CK_ATTRIBUTE) []const u8 {
+ const ptr = a.pValue orelse return &.{};
+ return @as([*]const u8, @ptrCast(ptr))[0..@intCast(a.ulValueLen)];
+}
+
+fn ulongAttr(obj: *const Object, t: ck.CK_ATTRIBUTE_TYPE) ?ck.CK_ULONG {
+ const v = obj.get(t) orelse return null;
+ if (v.len != @sizeOf(ck.CK_ULONG)) return null;
+ return std.mem.bytesToValue(ck.CK_ULONG, v[0..@sizeOf(ck.CK_ULONG)]);
+}
+
+const visible = object_store.visible;
+
+fn matches(obj: *Object, template: []ck.CK_ATTRIBUTE) bool {
+ for (template) |a| {
+ const have = obj.get(a.type) orelse return false;
+ if (!std.mem.eql(u8, have, attrBytes(a))) return false;
+ }
+ return true;
+}
+
+fn readOnlyAttr(t: ck.CK_ATTRIBUTE_TYPE) bool {
+ return switch (t) {
+ ck.CKA_CLASS,
+ ck.CKA_TOKEN,
+ ck.CKA_PRIVATE,
+ ck.CKA_KEY_TYPE,
+ ck.CKA_LOCAL,
+ ck.CKA_KEY_GEN_MECHANISM,
+ ck.CKA_ALWAYS_SENSITIVE,
+ ck.CKA_NEVER_EXTRACTABLE,
+ ck.CKA_MODULUS,
+ ck.CKA_PUBLIC_EXPONENT,
+ ck.CKA_PRIVATE_EXPONENT,
+ ck.CKA_PRIME_1,
+ ck.CKA_PRIME_2,
+ ck.CKA_EXPONENT_1,
+ ck.CKA_EXPONENT_2,
+ ck.CKA_COEFFICIENT,
+ ck.CKA_EC_PARAMS,
+ ck.CKA_EC_POINT,
+ => true,
+ else => false,
+ };
+}
+
+fn sensitiveProtected(obj: *const Object, t: ck.CK_ATTRIBUTE_TYPE) bool {
+ const secret_material = switch (t) {
+ ck.CKA_VALUE,
+ ck.CKA_PRIVATE_EXPONENT,
+ ck.CKA_PRIME_1,
+ ck.CKA_PRIME_2,
+ ck.CKA_EXPONENT_1,
+ ck.CKA_EXPONENT_2,
+ ck.CKA_COEFFICIENT,
+ => true,
+ else => false,
+ };
+ if (!secret_material) return false;
+ if (!obj.has(t)) return false;
+ if (obj.getBool(ck.CKA_SENSITIVE)) return true;
+ return obj.has(ck.CKA_EXTRACTABLE) and !obj.getBool(ck.CKA_EXTRACTABLE);
+}
+
+pub fn materializeDefaults(obj: *Object, allocator: std.mem.Allocator, class: ck.CK_OBJECT_CLASS) !void {
+ if (!obj.has(ck.CKA_TOKEN)) try obj.set(allocator, ck.CKA_TOKEN, &[_]u8{ck.CK_FALSE});
+ if (!obj.has(ck.CKA_PRIVATE)) {
+ const def: u8 = if (class == ck.CKO_PRIVATE_KEY) ck.CK_TRUE else ck.CK_FALSE;
+ try obj.set(allocator, ck.CKA_PRIVATE, &[_]u8{def});
+ }
+}
+
+pub fn insertNew(inst: *state.Instance, sess: *session.Session, obj_in: Object, phObject: *ck.CK_OBJECT_HANDLE) ck.CK_RV {
+ var obj = obj_in;
+ const allocator = inst.allocator();
+ const is_token = obj.isToken();
+ if (is_token and (sess.flags & ck.CKF_RW_SESSION) == 0) {
+ obj.deinit(allocator);
+ return ck.CKR_SESSION_READ_ONLY;
+ }
+ if (obj.isPrivate() and inst.logged_in != ck.CKU_USER) {
+ obj.deinit(allocator);
+ return ck.CKR_USER_NOT_LOGGED_IN;
+ }
+ if (is_token and inst.mk == null and obj.hasSealable()) {
+ obj.deinit(allocator);
+ return ck.CKR_USER_NOT_LOGGED_IN;
+ }
+ const h = inst.objects.insert(obj) orelse {
+ obj.deinit(allocator);
+ return ck.CKR_DEVICE_MEMORY;
+ };
+ phObject.* = h;
+ persistIfToken(inst, is_token);
+ return ck.CKR_OK;
+}
+
+fn worse(cur: ck.CK_RV, new: ck.CK_RV) ck.CK_RV {
+ if (cur == ck.CKR_OK) return new;
+ if (cur == ck.CKR_ATTRIBUTE_SENSITIVE or new == ck.CKR_ATTRIBUTE_SENSITIVE) return ck.CKR_ATTRIBUTE_SENSITIVE;
+ if (cur == ck.CKR_ATTRIBUTE_TYPE_INVALID or new == ck.CKR_ATTRIBUTE_TYPE_INVALID) return ck.CKR_ATTRIBUTE_TYPE_INVALID;
+ return new;
+}
+
+fn persistIfToken(inst: *state.Instance, is_token: bool) void {
+ if (is_token) object_store.save(inst.io(), inst.allocator(), &inst.objects, inst.mk) catch {};
+}
+
+pub fn C_CreateObject(hSession: ck.CK_SESSION_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phObject: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const allocator = inst.allocator();
+ const template = inputTemplate(pTemplate, ulCount);
+
+ var obj: Object = .{};
+ var moved = false;
+ defer if (!moved) obj.deinit(allocator);
+
+ for (template) |a| {
+ obj.set(allocator, a.type, attrBytes(a)) catch |e| return mapSetErr(e);
+ }
+
+ if (!obj.has(ck.CKA_CLASS)) return ck.CKR_TEMPLATE_INCOMPLETE;
+ const class = ulongAttr(&obj, ck.CKA_CLASS) orelse return ck.CKR_ATTRIBUTE_VALUE_INVALID;
+ materializeDefaults(&obj, allocator, class) catch |e| return mapSetErr(e);
+
+ moved = true;
+ return insertNew(inst, sess, obj, phObject);
+}
+
+pub fn C_CopyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG, phNewObject: *ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const src = inst.objects.getPtr(hObject) orelse return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (!visible(src, inst.logged_in)) return ck.CKR_OBJECT_HANDLE_INVALID;
+
+ const allocator = inst.allocator();
+ var obj = src.clone(allocator) catch |e| return mapSetErr(e);
+ var inserted = false;
+ defer if (!inserted) obj.deinit(allocator);
+
+ const template = inputTemplate(pTemplate, ulCount);
+ for (template) |a| {
+ if (readOnlyAttr(a.type) and a.type != ck.CKA_TOKEN and a.type != ck.CKA_PRIVATE) return ck.CKR_ATTRIBUTE_READ_ONLY;
+ }
+ for (template) |a| {
+ obj.set(allocator, a.type, attrBytes(a)) catch |e| return mapSetErr(e);
+ }
+
+ const is_token = obj.isToken();
+ if (is_token and (sess.flags & ck.CKF_RW_SESSION) == 0) return ck.CKR_SESSION_READ_ONLY;
+ if (obj.isPrivate() and inst.logged_in != ck.CKU_USER) return ck.CKR_USER_NOT_LOGGED_IN;
+
+ const h = inst.objects.insert(obj) orelse return ck.CKR_DEVICE_MEMORY;
+ inserted = true;
+ phNewObject.* = h;
+ persistIfToken(inst, is_token);
+ return ck.CKR_OK;
+}
+
+pub fn C_DestroyObject(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const obj = inst.objects.getPtr(hObject) orelse return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (!visible(obj, inst.logged_in)) return ck.CKR_OBJECT_HANDLE_INVALID;
+
+ const was_token = obj.isToken();
+ if (was_token and (sess.flags & ck.CKF_RW_SESSION) == 0) return ck.CKR_SESSION_READ_ONLY;
+ if (obj.has(ck.CKA_DESTROYABLE) and !obj.getBool(ck.CKA_DESTROYABLE)) return ck.CKR_ACTION_PROHIBITED;
+
+ _ = inst.objects.destroy(inst.allocator(), hObject);
+ persistIfToken(inst, was_token);
+ return ck.CKR_OK;
+}
+
+pub fn C_GetObjectSize(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pulSize: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ _ = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const obj = inst.objects.getPtr(hObject) orelse return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (!visible(obj, inst.logged_in)) return ck.CKR_OBJECT_HANDLE_INVALID;
+
+ pulSize.* = obj.sizeBytes();
+ return ck.CKR_OK;
+}
+
+pub fn C_GetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ _ = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const obj = inst.objects.getPtr(hObject) orelse return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (!visible(obj, inst.logged_in)) return ck.CKR_OBJECT_HANDLE_INVALID;
+
+ const template = inputTemplate(pTemplate, ulCount);
+ var rv: ck.CK_RV = ck.CKR_OK;
+ for (template) |*a| {
+ if (sensitiveProtected(obj, a.type)) {
+ a.ulValueLen = ck.CK_UNAVAILABLE_INFORMATION;
+ rv = worse(rv, ck.CKR_ATTRIBUTE_SENSITIVE);
+ continue;
+ }
+ const val = obj.get(a.type) orelse {
+ a.ulValueLen = ck.CK_UNAVAILABLE_INFORMATION;
+ rv = worse(rv, ck.CKR_ATTRIBUTE_TYPE_INVALID);
+ continue;
+ };
+ if (a.pValue) |ptr| {
+ if (a.ulValueLen < val.len) {
+ a.ulValueLen = ck.CK_UNAVAILABLE_INFORMATION;
+ rv = worse(rv, ck.CKR_BUFFER_TOO_SMALL);
+ } else {
+ @memcpy(@as([*]u8, @ptrCast(ptr))[0..val.len], val);
+ a.ulValueLen = @intCast(val.len);
+ }
+ } else {
+ a.ulValueLen = @intCast(val.len);
+ }
+ }
+ return rv;
+}
+
+pub fn C_SetAttributeValue(hSession: ck.CK_SESSION_HANDLE, hObject: ck.CK_OBJECT_HANDLE, pTemplate: [*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ const obj = inst.objects.getPtr(hObject) orelse return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (!visible(obj, inst.logged_in)) return ck.CKR_OBJECT_HANDLE_INVALID;
+ if (obj.isToken() and (sess.flags & ck.CKF_RW_SESSION) == 0) return ck.CKR_SESSION_READ_ONLY;
+ if (obj.has(ck.CKA_MODIFIABLE) and !obj.getBool(ck.CKA_MODIFIABLE)) return ck.CKR_ACTION_PROHIBITED;
+
+ const template = inputTemplate(pTemplate, ulCount);
+ for (template) |a| {
+ if (readOnlyAttr(a.type)) return ck.CKR_ATTRIBUTE_READ_ONLY;
+ }
+
+ const allocator = inst.allocator();
+ var staged = obj.clone(allocator) catch |e| return mapSetErr(e);
+ var swapped = false;
+ defer if (!swapped) staged.deinit(allocator);
+ for (template) |a| {
+ staged.set(allocator, a.type, attrBytes(a)) catch |e| return mapSetErr(e);
+ }
+ obj.deinit(allocator);
+ obj.* = staged;
+ swapped = true;
+ persistIfToken(inst, obj.isToken());
+ return ck.CKR_OK;
+}
+
+pub fn C_FindObjectsInit(hSession: ck.CK_SESSION_HANDLE, pTemplate: ?[*]ck.CK_ATTRIBUTE, ulCount: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (sess.find.active) return ck.CKR_OPERATION_ACTIVE;
+ if (pTemplate == null and ulCount != 0) return ck.CKR_ARGUMENTS_BAD;
+
+ const template = inputTemplate(pTemplate, ulCount);
+ sess.find.count = 0;
+ sess.find.cursor = 0;
+ for (&inst.objects.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (!visible(&e.obj, inst.logged_in)) continue;
+ if (matches(&e.obj, template)) {
+ sess.find.matches[sess.find.count] = e.handle;
+ sess.find.count += 1;
+ }
+ }
+ }
+ sess.find.active = true;
+ return ck.CKR_OK;
+}
+
+pub fn C_FindObjects(hSession: ck.CK_SESSION_HANDLE, phObject: [*]ck.CK_OBJECT_HANDLE, ulMaxObjectCount: ck.CK_ULONG, pulObjectCount: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (!sess.find.active) return ck.CKR_OPERATION_NOT_INITIALIZED;
+
+ const max: usize = @intCast(ulMaxObjectCount);
+ var n: usize = 0;
+ while (n < max and sess.find.cursor < sess.find.count) : (n += 1) {
+ phObject[n] = sess.find.matches[sess.find.cursor];
+ sess.find.cursor += 1;
+ }
+ pulObjectCount.* = @intCast(n);
+ return ck.CKR_OK;
+}
+
+pub fn C_FindObjectsFinal(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ const sess = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ if (!sess.find.active) return ck.CKR_OPERATION_NOT_INITIALIZED;
+ sess.find.active = false;
+ sess.find.count = 0;
+ sess.find.cursor = 0;
+ return ck.CKR_OK;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/random.zig b/PROJECTS/advanced/hsm-emulator/src/api/random.zig
new file mode 100644
index 00000000..686838ec
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/random.zig
@@ -0,0 +1,23 @@
+// ©AngelaMos | 2026
+// random.zig
+
+const ck = @import("../ck.zig");
+const state = @import("../core/state.zig");
+
+pub fn C_SeedRandom(hSession: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
+ return ck.CKR_RANDOM_SEED_NOT_SUPPORTED;
+}
+
+pub fn C_GenerateRandom(hSession: ck.CK_SESSION_HANDLE, pRandomData: [*]ck.CK_BYTE, ulRandomLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
+ if (ulRandomLen == 0) return ck.CKR_OK;
+ inst.io().randomSecure(pRandomData[0..@intCast(ulRandomLen)]) catch return ck.CKR_FUNCTION_FAILED;
+ return ck.CKR_OK;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/session.zig b/PROJECTS/advanced/hsm-emulator/src/api/session.zig
new file mode 100644
index 00000000..cd08fb23
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/session.zig
@@ -0,0 +1,193 @@
+// ©AngelaMos | 2026
+// session.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const state = @import("../core/state.zig");
+const pin = @import("../crypto/pin.zig");
+const keystore = @import("../crypto/keystore.zig");
+const token = @import("../core/token.zig");
+const object_store = @import("../core/object_store.zig");
+
+fn pinSlice(p: ?[*]ck.CK_UTF8CHAR, len: ck.CK_ULONG) []const u8 {
+ return if (p) |ptr| ptr[0..@intCast(len)] else &.{};
+}
+
+fn sessionState(flags: ck.CK_FLAGS, logged_in: ?ck.CK_USER_TYPE) ck.CK_STATE {
+ const rw = (flags & ck.CKF_RW_SESSION) != 0;
+ if (logged_in) |u| {
+ if (u == ck.CKU_SO) return ck.CKS_RW_SO_FUNCTIONS;
+ return if (rw) ck.CKS_RW_USER_FUNCTIONS else ck.CKS_RO_USER_FUNCTIONS;
+ }
+ return if (rw) ck.CKS_RW_PUBLIC_SESSION else ck.CKS_RO_PUBLIC_SESSION;
+}
+
+pub fn C_OpenSession(slotID: ck.CK_SLOT_ID, flags: ck.CK_FLAGS, pApplication: ?*anyopaque, notify: ck.CK_NOTIFY, phSession: *ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ _ = pApplication;
+ _ = notify;
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+ if ((flags & ck.CKF_SERIAL_SESSION) == 0) return ck.CKR_SESSION_PARALLEL_NOT_SUPPORTED;
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if ((flags & ck.CKF_RW_SESSION) == 0 and inst.logged_in == ck.CKU_SO) {
+ return ck.CKR_SESSION_READ_WRITE_SO_EXISTS;
+ }
+ const h = inst.sessions.open(slotID, flags) orelse return ck.CKR_SESSION_COUNT;
+ phSession.* = h;
+ return ck.CKR_OK;
+}
+
+pub fn C_CloseSession(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (!inst.sessions.close(hSession)) return ck.CKR_SESSION_HANDLE_INVALID;
+ if (!inst.sessions.anyOpen()) {
+ inst.relock();
+ inst.logged_in = null;
+ }
+ return ck.CKR_OK;
+}
+
+pub fn C_CloseAllSessions(slotID: ck.CK_SLOT_ID) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ inst.sessions.closeAll(slotID);
+ inst.relock();
+ inst.logged_in = null;
+ return ck.CKR_OK;
+}
+
+pub fn C_GetSessionInfo(hSession: ck.CK_SESSION_HANDLE, pInfo: *ck.CK_SESSION_INFO) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ const s = inst.sessions.get(hSession) orelse return ck.CKR_SESSION_HANDLE_INVALID;
+ pInfo.* = .{
+ .slotID = s.slot,
+ .state = sessionState(s.flags, inst.logged_in),
+ .flags = s.flags,
+ .ulDeviceError = 0,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_GetOperationState(_: ck.CK_SESSION_HANDLE, _: ?[*]ck.CK_BYTE, _: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_SetOperationState(_: ck.CK_SESSION_HANDLE, _: [*]ck.CK_BYTE, _: ck.CK_ULONG, _: ck.CK_OBJECT_HANDLE, _: ck.CK_OBJECT_HANDLE) callconv(.c) ck.CK_RV {
+ return ck.CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+pub fn C_Login(hSession: ck.CK_SESSION_HANDLE, userType: ck.CK_USER_TYPE, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ if (userType != ck.CKU_SO and userType != ck.CKU_USER) return ck.CKR_USER_TYPE_INVALID;
+
+ state.mutex.lock();
+ if (inst.sessions.get(hSession) == null) {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_HANDLE_INVALID;
+ }
+ if (inst.logged_in != null) {
+ state.mutex.unlock();
+ return ck.CKR_USER_ALREADY_LOGGED_IN;
+ }
+ var salt: pin.Salt = undefined;
+ var hash: pin.Hash = undefined;
+ var wrapped_mk: ?keystore.Wrapped = null;
+ if (userType == ck.CKU_SO) {
+ if (inst.sessions.count() > inst.sessions.countRw()) {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_READ_ONLY_EXISTS;
+ }
+ if (!inst.token.initialized) {
+ state.mutex.unlock();
+ return ck.CKR_USER_PIN_NOT_INITIALIZED;
+ }
+ if (inst.token.so_fail >= config.login_max_attempts) {
+ state.mutex.unlock();
+ return ck.CKR_PIN_LOCKED;
+ }
+ salt = inst.token.so.salt;
+ hash = inst.token.so.hash;
+ } else {
+ const u = inst.token.user orelse {
+ state.mutex.unlock();
+ return ck.CKR_USER_PIN_NOT_INITIALIZED;
+ };
+ if (inst.token.user_fail >= config.login_max_attempts) {
+ state.mutex.unlock();
+ return ck.CKR_PIN_LOCKED;
+ }
+ salt = u.salt;
+ hash = u.hash;
+ wrapped_mk = inst.token.user_mk;
+ }
+ const gen = state.cryptoBegin();
+ const io = inst.io();
+ const allocator = inst.allocator();
+ state.mutex.unlock();
+ defer std.crypto.secureZero(u8, &hash);
+
+ const ok = pin.verify(io, allocator, pinSlice(pPin, ulPinLen), &salt, &hash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+
+ var mk: keystore.MasterKey = undefined;
+ defer std.crypto.secureZero(u8, &mk);
+ var have_mk = false;
+ if (ok and userType == ck.CKU_USER) {
+ if (wrapped_mk) |w| {
+ have_mk = keystore.unwrap(io, allocator, pinSlice(pPin, ulPinLen), &w, &mk) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ if (!have_mk) {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ }
+ }
+ }
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ state.cryptoEnd();
+ if (state.currentGeneration() != gen) return ck.CKR_FUNCTION_FAILED;
+ if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
+ if (inst.logged_in != null) return ck.CKR_USER_ALREADY_LOGGED_IN;
+ if (ok) {
+ if (userType == ck.CKU_SO) inst.token.so_fail = 0 else inst.token.user_fail = 0;
+ inst.logged_in = userType;
+ if (have_mk) {
+ inst.mk = mk;
+ object_store.unlock(allocator, &inst.objects, mk) catch {
+ inst.relock();
+ inst.logged_in = null;
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ }
+ token.save(inst.io(), inst.token) catch {};
+ return ck.CKR_OK;
+ }
+ if (userType == ck.CKU_SO) inst.token.so_fail += 1 else inst.token.user_fail += 1;
+ token.save(inst.io(), inst.token) catch {};
+ return ck.CKR_PIN_INCORRECT;
+}
+
+pub fn C_Logout(hSession: ck.CK_SESSION_HANDLE) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ if (inst.sessions.get(hSession) == null) return ck.CKR_SESSION_HANDLE_INVALID;
+ if (inst.logged_in == null) return ck.CKR_USER_NOT_LOGGED_IN;
+ inst.relock();
+ inst.logged_in = null;
+ return ck.CKR_OK;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/api/slot_token.zig b/PROJECTS/advanced/hsm-emulator/src/api/slot_token.zig
new file mode 100644
index 00000000..e2c4545b
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/api/slot_token.zig
@@ -0,0 +1,404 @@
+// ©AngelaMos | 2026
+// slot_token.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const util = @import("../util.zig");
+const state = @import("../core/state.zig");
+const pin = @import("../crypto/pin.zig");
+const keystore = @import("../crypto/keystore.zig");
+const token = @import("../core/token.zig");
+const object_store = @import("../core/object_store.zig");
+
+fn requireInit() ?ck.CK_RV {
+ return if (state.isInitialized()) null else ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+}
+
+fn pinSlice(p: ?[*]ck.CK_UTF8CHAR, len: ck.CK_ULONG) []const u8 {
+ return if (p) |ptr| ptr[0..@intCast(len)] else &.{};
+}
+
+fn labelFrom(p: ?[*]ck.CK_UTF8CHAR) [config.label_len]u8 {
+ var out: [config.label_len]u8 = @splat(' ');
+ if (p) |lp| @memcpy(&out, lp[0..config.label_len]);
+ return out;
+}
+
+fn pinStateFlags(fail: u32, low: ck.CK_FLAGS, final_try: ck.CK_FLAGS, locked: ck.CK_FLAGS) ck.CK_FLAGS {
+ const f: ck.CK_ULONG = fail;
+ const max = config.login_max_attempts;
+ if (f >= max) return locked;
+ if (f == max - 1) return final_try;
+ if (f > 0) return low;
+ return 0;
+}
+
+pub fn C_GetSlotList(_: ck.CK_BBOOL, pSlotList: ?[*]ck.CK_SLOT_ID, pulCount: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ if (requireInit()) |rv| return rv;
+ if (pSlotList == null) {
+ pulCount.* = config.slot_count;
+ return ck.CKR_OK;
+ }
+ if (pulCount.* < config.slot_count) {
+ pulCount.* = config.slot_count;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ pSlotList.?[0] = config.slot_id;
+ pulCount.* = config.slot_count;
+ return ck.CKR_OK;
+}
+
+pub fn C_GetSlotInfo(slotID: ck.CK_SLOT_ID, pInfo: *ck.CK_SLOT_INFO) callconv(.c) ck.CK_RV {
+ if (requireInit()) |rv| return rv;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+ pInfo.* = .{
+ .slotDescription = util.padded(64, config.slot_description),
+ .manufacturerID = util.padded(32, config.manufacturer_id),
+ .flags = ck.CKF_TOKEN_PRESENT | ck.CKF_HW_SLOT,
+ .hardwareVersion = config.hardware_version,
+ .firmwareVersion = config.firmware_version,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_GetTokenInfo(slotID: ck.CK_SLOT_ID, pInfo: *ck.CK_TOKEN_INFO) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+
+ var flags: ck.CK_FLAGS = ck.CKF_RNG | ck.CKF_LOGIN_REQUIRED;
+ if (inst.token.initialized) flags |= ck.CKF_TOKEN_INITIALIZED;
+ if (inst.token.user != null) flags |= ck.CKF_USER_PIN_INITIALIZED;
+ flags |= pinStateFlags(inst.token.user_fail, ck.CKF_USER_PIN_COUNT_LOW, ck.CKF_USER_PIN_FINAL_TRY, ck.CKF_USER_PIN_LOCKED);
+ flags |= pinStateFlags(inst.token.so_fail, ck.CKF_SO_PIN_COUNT_LOW, ck.CKF_SO_PIN_FINAL_TRY, ck.CKF_SO_PIN_LOCKED);
+
+ pInfo.* = .{
+ .label = if (inst.token.initialized) inst.token.label else util.padded(32, config.token_label),
+ .manufacturerID = util.padded(32, config.manufacturer_id),
+ .model = util.padded(16, config.token_model),
+ .serialNumber = util.padded(16, config.token_serial),
+ .flags = flags,
+ .ulMaxSessionCount = config.max_sessions,
+ .ulSessionCount = inst.sessions.count(),
+ .ulMaxRwSessionCount = config.max_sessions,
+ .ulRwSessionCount = inst.sessions.countRw(),
+ .ulMaxPinLen = config.max_pin_len,
+ .ulMinPinLen = config.min_pin_len,
+ .ulTotalPublicMemory = ck.CK_UNAVAILABLE_INFORMATION,
+ .ulFreePublicMemory = ck.CK_UNAVAILABLE_INFORMATION,
+ .ulTotalPrivateMemory = ck.CK_UNAVAILABLE_INFORMATION,
+ .ulFreePrivateMemory = ck.CK_UNAVAILABLE_INFORMATION,
+ .hardwareVersion = config.hardware_version,
+ .firmwareVersion = config.firmware_version,
+ .utcTime = util.padded(16, ""),
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_GetMechanismList(slotID: ck.CK_SLOT_ID, pMechanismList: ?[*]ck.CK_MECHANISM_TYPE, pulCount: *ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ if (requireInit()) |rv| return rv;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+ const n: ck.CK_ULONG = config.supported_mechanisms.len;
+ if (pMechanismList == null) {
+ pulCount.* = n;
+ return ck.CKR_OK;
+ }
+ if (pulCount.* < n) {
+ pulCount.* = n;
+ return ck.CKR_BUFFER_TOO_SMALL;
+ }
+ for (config.supported_mechanisms, 0..) |m, i| pMechanismList.?[i] = m;
+ pulCount.* = n;
+ return ck.CKR_OK;
+}
+
+pub fn C_GetMechanismInfo(slotID: ck.CK_SLOT_ID, mechType: ck.CK_MECHANISM_TYPE, pInfo: *ck.CK_MECHANISM_INFO) callconv(.c) ck.CK_RV {
+ if (requireInit()) |rv| return rv;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+ pInfo.* = switch (mechType) {
+ ck.CKM_SHA256, ck.CKM_SHA384, ck.CKM_SHA512 => .{
+ .ulMinKeySize = 0,
+ .ulMaxKeySize = 0,
+ .flags = ck.CKF_DIGEST,
+ },
+ ck.CKM_SHA256_HMAC, ck.CKM_SHA384_HMAC, ck.CKM_SHA512_HMAC => .{
+ .ulMinKeySize = config.hmac_min_key_bytes,
+ .ulMaxKeySize = config.hmac_max_key_bytes,
+ .flags = ck.CKF_SIGN | ck.CKF_VERIFY,
+ },
+ ck.CKM_AES_KEY_GEN => .{
+ .ulMinKeySize = config.aes_min_key_bytes,
+ .ulMaxKeySize = config.aes_max_key_bytes,
+ .flags = ck.CKF_GENERATE,
+ },
+ ck.CKM_AES_CBC, ck.CKM_AES_CBC_PAD, ck.CKM_AES_GCM => .{
+ .ulMinKeySize = config.aes_min_key_bytes,
+ .ulMaxKeySize = config.aes_max_key_bytes,
+ .flags = ck.CKF_ENCRYPT | ck.CKF_DECRYPT,
+ },
+ ck.CKM_EC_KEY_PAIR_GEN => .{
+ .ulMinKeySize = config.ec_min_key_bits,
+ .ulMaxKeySize = config.ec_max_key_bits,
+ .flags = ck.CKF_GENERATE_KEY_PAIR | ck.CKF_EC_NAMEDCURVE,
+ },
+ ck.CKM_ECDSA, ck.CKM_ECDSA_SHA256 => .{
+ .ulMinKeySize = config.ec_min_key_bits,
+ .ulMaxKeySize = config.ec_max_key_bits,
+ .flags = ck.CKF_SIGN | ck.CKF_VERIFY | ck.CKF_EC_NAMEDCURVE,
+ },
+ ck.CKM_RSA_PKCS_KEY_PAIR_GEN => .{
+ .ulMinKeySize = config.rsa_min_key_bits,
+ .ulMaxKeySize = config.rsa_max_key_bits,
+ .flags = ck.CKF_GENERATE_KEY_PAIR,
+ },
+ ck.CKM_RSA_PKCS => .{
+ .ulMinKeySize = config.rsa_min_key_bits,
+ .ulMaxKeySize = config.rsa_max_key_bits,
+ .flags = ck.CKF_SIGN | ck.CKF_VERIFY | ck.CKF_ENCRYPT | ck.CKF_DECRYPT,
+ },
+ ck.CKM_SHA256_RSA_PKCS, ck.CKM_RSA_PKCS_PSS, ck.CKM_SHA256_RSA_PKCS_PSS => .{
+ .ulMinKeySize = config.rsa_min_key_bits,
+ .ulMaxKeySize = config.rsa_max_key_bits,
+ .flags = ck.CKF_SIGN | ck.CKF_VERIFY,
+ },
+ ck.CKM_RSA_PKCS_OAEP => .{
+ .ulMinKeySize = config.rsa_min_key_bits,
+ .ulMaxKeySize = config.rsa_max_key_bits,
+ .flags = ck.CKF_ENCRYPT | ck.CKF_DECRYPT,
+ },
+ else => return ck.CKR_MECHANISM_INVALID,
+ };
+ return ck.CKR_OK;
+}
+
+pub fn C_InitToken(slotID: ck.CK_SLOT_ID, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG, pLabel: ?[*]ck.CK_UTF8CHAR) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ if (slotID != config.slot_id) return ck.CKR_SLOT_ID_INVALID;
+
+ state.mutex.lock();
+ if (inst.sessions.anyOpen()) {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_EXISTS;
+ }
+ const was_init = inst.token.initialized;
+ if (was_init and inst.token.so_fail >= config.login_max_attempts) {
+ state.mutex.unlock();
+ return ck.CKR_PIN_LOCKED;
+ }
+ var snap_so = inst.token.so;
+ const gen = state.cryptoBegin();
+ const io = inst.io();
+ const allocator = inst.allocator();
+ state.mutex.unlock();
+ defer std.crypto.secureZero(u8, &snap_so.hash);
+
+ const new_label = labelFrom(pLabel);
+
+ if (was_init) {
+ const ok = pin.verify(io, allocator, pinSlice(pPin, ulPinLen), &snap_so.salt, &snap_so.hash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ state.cryptoEnd();
+ if (state.currentGeneration() != gen) return ck.CKR_FUNCTION_FAILED;
+ if (!ok) {
+ inst.token.so_fail += 1;
+ token.save(inst.io(), inst.token) catch {};
+ return ck.CKR_PIN_INCORRECT;
+ }
+ inst.token.user = null;
+ inst.token.user_mk = null;
+ inst.wipeMasterKey();
+ inst.token.user_fail = 0;
+ inst.token.so_fail = 0;
+ inst.token.label = new_label;
+ inst.logged_in = null;
+ state.bumpGeneration();
+ inst.objects.clear(inst.allocator());
+ token.save(inst.io(), inst.token) catch {};
+ object_store.save(inst.io(), inst.allocator(), &inst.objects, inst.mk) catch {};
+ return ck.CKR_OK;
+ }
+
+ var salt: pin.Salt = undefined;
+ pin.genSalt(io, &salt) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ var hash: pin.Hash = undefined;
+ defer std.crypto.secureZero(u8, &hash);
+ pin.derive(io, allocator, pinSlice(pPin, ulPinLen), &salt, &hash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ state.cryptoEnd();
+ inst.token.initialized = true;
+ inst.token.so = .{ .salt = salt, .hash = hash };
+ inst.token.user = null;
+ inst.token.user_mk = null;
+ inst.wipeMasterKey();
+ inst.token.so_fail = 0;
+ inst.token.user_fail = 0;
+ inst.token.label = new_label;
+ inst.logged_in = null;
+ state.bumpGeneration();
+ inst.objects.clear(inst.allocator());
+ token.save(inst.io(), inst.token) catch {};
+ object_store.save(inst.io(), inst.allocator(), &inst.objects, inst.mk) catch {};
+ return ck.CKR_OK;
+}
+
+pub fn C_InitPIN(hSession: ck.CK_SESSION_HANDLE, pPin: ?[*]ck.CK_UTF8CHAR, ulPinLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+
+ state.mutex.lock();
+ if (inst.sessions.get(hSession) == null) {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_HANDLE_INVALID;
+ }
+ if (inst.logged_in != ck.CKU_SO) {
+ state.mutex.unlock();
+ return ck.CKR_USER_NOT_LOGGED_IN;
+ }
+ _ = state.cryptoBegin();
+ const io = inst.io();
+ const allocator = inst.allocator();
+ state.mutex.unlock();
+
+ var salt: pin.Salt = undefined;
+ pin.genSalt(io, &salt) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ var hash: pin.Hash = undefined;
+ defer std.crypto.secureZero(u8, &hash);
+ pin.derive(io, allocator, pinSlice(pPin, ulPinLen), &salt, &hash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+
+ var mk: keystore.MasterKey = undefined;
+ defer std.crypto.secureZero(u8, &mk);
+ keystore.generateMasterKey(io, &mk) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ const wrapped = keystore.wrap(io, allocator, pinSlice(pPin, ulPinLen), &mk) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ state.cryptoEnd();
+ inst.token.user = .{ .salt = salt, .hash = hash };
+ inst.token.user_mk = wrapped;
+ inst.token.user_fail = 0;
+ state.bumpGeneration();
+ token.save(inst.io(), inst.token) catch {};
+ return ck.CKR_OK;
+}
+
+pub fn C_SetPIN(hSession: ck.CK_SESSION_HANDLE, pOldPin: ?[*]ck.CK_UTF8CHAR, ulOldLen: ck.CK_ULONG, pNewPin: ?[*]ck.CK_UTF8CHAR, ulNewLen: ck.CK_ULONG) callconv(.c) ck.CK_RV {
+ const inst = state.current() orelse return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+
+ state.mutex.lock();
+ const sess = inst.sessions.get(hSession) orelse {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_HANDLE_INVALID;
+ };
+ if ((sess.flags & ck.CKF_RW_SESSION) == 0) {
+ state.mutex.unlock();
+ return ck.CKR_SESSION_READ_ONLY;
+ }
+ const as_so = inst.logged_in == ck.CKU_SO;
+ var salt: pin.Salt = undefined;
+ var hash: pin.Hash = undefined;
+ var wrapped_mk: ?keystore.Wrapped = null;
+ if (as_so) {
+ if (!inst.token.initialized) {
+ state.mutex.unlock();
+ return ck.CKR_USER_PIN_NOT_INITIALIZED;
+ }
+ salt = inst.token.so.salt;
+ hash = inst.token.so.hash;
+ } else {
+ const u = inst.token.user orelse {
+ state.mutex.unlock();
+ return ck.CKR_USER_PIN_NOT_INITIALIZED;
+ };
+ salt = u.salt;
+ hash = u.hash;
+ wrapped_mk = inst.token.user_mk;
+ }
+ const gen = state.cryptoBegin();
+ const io = inst.io();
+ const allocator = inst.allocator();
+ state.mutex.unlock();
+ defer std.crypto.secureZero(u8, &hash);
+
+ const old_ok = pin.verify(io, allocator, pinSlice(pOldPin, ulOldLen), &salt, &hash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ if (!old_ok) {
+ state.cryptoAbort();
+ return ck.CKR_PIN_INCORRECT;
+ }
+
+ var nsalt: pin.Salt = undefined;
+ pin.genSalt(io, &nsalt) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ var nhash: pin.Hash = undefined;
+ defer std.crypto.secureZero(u8, &nhash);
+ pin.derive(io, allocator, pinSlice(pNewPin, ulNewLen), &nsalt, &nhash) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+
+ var new_mk_wrap: ?keystore.Wrapped = null;
+ if (!as_so) {
+ if (wrapped_mk) |w| {
+ var mk: keystore.MasterKey = undefined;
+ defer std.crypto.secureZero(u8, &mk);
+ const unwrapped = keystore.unwrap(io, allocator, pinSlice(pOldPin, ulOldLen), &w, &mk) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ if (!unwrapped) {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ }
+ new_mk_wrap = keystore.rewrap(io, allocator, pinSlice(pNewPin, ulNewLen), &mk) catch {
+ state.cryptoAbort();
+ return ck.CKR_FUNCTION_FAILED;
+ };
+ }
+ }
+
+ state.mutex.lock();
+ defer state.mutex.unlock();
+ state.cryptoEnd();
+ if (state.currentGeneration() != gen) return ck.CKR_FUNCTION_FAILED;
+ if (as_so) {
+ inst.token.so = .{ .salt = nsalt, .hash = nhash };
+ inst.token.so_fail = 0;
+ } else {
+ inst.token.user = .{ .salt = nsalt, .hash = nhash };
+ if (new_mk_wrap) |w| inst.token.user_mk = w;
+ inst.token.user_fail = 0;
+ }
+ state.bumpGeneration();
+ token.save(inst.io(), inst.token) catch {};
+ return ck.CKR_OK;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/ck.zig b/PROJECTS/advanced/hsm-emulator/src/ck.zig
new file mode 100644
index 00000000..c13141a0
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/ck.zig
@@ -0,0 +1,503 @@
+// ©AngelaMos | 2026
+// ck.zig
+
+pub const CK_BYTE = u8;
+pub const CK_CHAR = u8;
+pub const CK_UTF8CHAR = u8;
+pub const CK_BBOOL = u8;
+pub const CK_ULONG = c_ulong;
+pub const CK_LONG = c_long;
+pub const CK_FLAGS = CK_ULONG;
+pub const CK_RV = CK_ULONG;
+pub const CK_SLOT_ID = CK_ULONG;
+pub const CK_SESSION_HANDLE = CK_ULONG;
+pub const CK_OBJECT_HANDLE = CK_ULONG;
+pub const CK_OBJECT_CLASS = CK_ULONG;
+pub const CK_KEY_TYPE = CK_ULONG;
+pub const CK_MECHANISM_TYPE = CK_ULONG;
+pub const CK_ATTRIBUTE_TYPE = CK_ULONG;
+pub const CK_RSA_PKCS_MGF_TYPE = CK_ULONG;
+pub const CK_RSA_PKCS_OAEP_SOURCE_TYPE = CK_ULONG;
+pub const CK_USER_TYPE = CK_ULONG;
+pub const CK_STATE = CK_ULONG;
+pub const CK_NOTIFICATION = CK_ULONG;
+
+pub const CK_TRUE: CK_BBOOL = 1;
+pub const CK_FALSE: CK_BBOOL = 0;
+pub const CK_INVALID_HANDLE: CK_ULONG = 0;
+pub const CK_UNAVAILABLE_INFORMATION: CK_ULONG = ~@as(CK_ULONG, 0);
+pub const CK_EFFECTIVELY_INFINITE: CK_ULONG = 0;
+
+pub const CKR_OK: CK_RV = 0x00000000;
+pub const CKR_CANCEL: CK_RV = 0x00000001;
+pub const CKR_HOST_MEMORY: CK_RV = 0x00000002;
+pub const CKR_SLOT_ID_INVALID: CK_RV = 0x00000003;
+pub const CKR_GENERAL_ERROR: CK_RV = 0x00000005;
+pub const CKR_FUNCTION_FAILED: CK_RV = 0x00000006;
+pub const CKR_ARGUMENTS_BAD: CK_RV = 0x00000007;
+pub const CKR_NO_EVENT: CK_RV = 0x00000008;
+pub const CKR_NEED_TO_CREATE_THREADS: CK_RV = 0x00000009;
+pub const CKR_CANT_LOCK: CK_RV = 0x0000000A;
+pub const CKR_ATTRIBUTE_READ_ONLY: CK_RV = 0x00000010;
+pub const CKR_ATTRIBUTE_SENSITIVE: CK_RV = 0x00000011;
+pub const CKR_ATTRIBUTE_TYPE_INVALID: CK_RV = 0x00000012;
+pub const CKR_ATTRIBUTE_VALUE_INVALID: CK_RV = 0x00000013;
+pub const CKR_ACTION_PROHIBITED: CK_RV = 0x0000001B;
+pub const CKR_DATA_INVALID: CK_RV = 0x00000020;
+pub const CKR_DATA_LEN_RANGE: CK_RV = 0x00000021;
+pub const CKR_DEVICE_ERROR: CK_RV = 0x00000030;
+pub const CKR_DEVICE_MEMORY: CK_RV = 0x00000031;
+pub const CKR_DEVICE_REMOVED: CK_RV = 0x00000032;
+pub const CKR_ENCRYPTED_DATA_INVALID: CK_RV = 0x00000040;
+pub const CKR_ENCRYPTED_DATA_LEN_RANGE: CK_RV = 0x00000041;
+pub const CKR_FUNCTION_CANCELED: CK_RV = 0x00000050;
+pub const CKR_FUNCTION_NOT_PARALLEL: CK_RV = 0x00000051;
+pub const CKR_FUNCTION_NOT_SUPPORTED: CK_RV = 0x00000054;
+pub const CKR_KEY_HANDLE_INVALID: CK_RV = 0x00000060;
+pub const CKR_KEY_SIZE_RANGE: CK_RV = 0x00000062;
+pub const CKR_KEY_TYPE_INCONSISTENT: CK_RV = 0x00000063;
+pub const CKR_KEY_FUNCTION_NOT_PERMITTED: CK_RV = 0x00000068;
+pub const CKR_KEY_NOT_WRAPPABLE: CK_RV = 0x00000069;
+pub const CKR_KEY_UNEXTRACTABLE: CK_RV = 0x0000006A;
+pub const CKR_MECHANISM_INVALID: CK_RV = 0x00000070;
+pub const CKR_MECHANISM_PARAM_INVALID: CK_RV = 0x00000071;
+pub const CKR_OBJECT_HANDLE_INVALID: CK_RV = 0x00000082;
+pub const CKR_OPERATION_ACTIVE: CK_RV = 0x00000090;
+pub const CKR_OPERATION_NOT_INITIALIZED: CK_RV = 0x00000091;
+pub const CKR_PIN_INCORRECT: CK_RV = 0x000000A0;
+pub const CKR_PIN_INVALID: CK_RV = 0x000000A1;
+pub const CKR_PIN_LEN_RANGE: CK_RV = 0x000000A2;
+pub const CKR_PIN_EXPIRED: CK_RV = 0x000000A3;
+pub const CKR_PIN_LOCKED: CK_RV = 0x000000A4;
+pub const CKR_SESSION_CLOSED: CK_RV = 0x000000B0;
+pub const CKR_SESSION_COUNT: CK_RV = 0x000000B1;
+pub const CKR_SESSION_HANDLE_INVALID: CK_RV = 0x000000B3;
+pub const CKR_SESSION_PARALLEL_NOT_SUPPORTED: CK_RV = 0x000000B4;
+pub const CKR_SESSION_READ_ONLY: CK_RV = 0x000000B5;
+pub const CKR_SESSION_EXISTS: CK_RV = 0x000000B6;
+pub const CKR_SESSION_READ_ONLY_EXISTS: CK_RV = 0x000000B7;
+pub const CKR_SESSION_READ_WRITE_SO_EXISTS: CK_RV = 0x000000B8;
+pub const CKR_SIGNATURE_INVALID: CK_RV = 0x000000C0;
+pub const CKR_SIGNATURE_LEN_RANGE: CK_RV = 0x000000C1;
+pub const CKR_TEMPLATE_INCOMPLETE: CK_RV = 0x000000D0;
+pub const CKR_TEMPLATE_INCONSISTENT: CK_RV = 0x000000D1;
+pub const CKR_TOKEN_NOT_PRESENT: CK_RV = 0x000000E0;
+pub const CKR_TOKEN_NOT_RECOGNIZED: CK_RV = 0x000000E1;
+pub const CKR_TOKEN_WRITE_PROTECTED: CK_RV = 0x000000E2;
+pub const CKR_USER_ALREADY_LOGGED_IN: CK_RV = 0x00000100;
+pub const CKR_USER_NOT_LOGGED_IN: CK_RV = 0x00000101;
+pub const CKR_USER_PIN_NOT_INITIALIZED: CK_RV = 0x00000102;
+pub const CKR_USER_TYPE_INVALID: CK_RV = 0x00000103;
+pub const CKR_USER_ANOTHER_ALREADY_LOGGED_IN: CK_RV = 0x00000104;
+pub const CKR_USER_TOO_MANY_TYPES: CK_RV = 0x00000105;
+pub const CKR_RANDOM_SEED_NOT_SUPPORTED: CK_RV = 0x00000120;
+pub const CKR_RANDOM_NO_RNG: CK_RV = 0x00000121;
+pub const CKR_DOMAIN_PARAMS_INVALID: CK_RV = 0x00000130;
+pub const CKR_BUFFER_TOO_SMALL: CK_RV = 0x00000150;
+pub const CKR_SAVED_STATE_INVALID: CK_RV = 0x00000160;
+pub const CKR_INFORMATION_SENSITIVE: CK_RV = 0x00000170;
+pub const CKR_STATE_UNSAVEABLE: CK_RV = 0x00000180;
+pub const CKR_CRYPTOKI_NOT_INITIALIZED: CK_RV = 0x00000190;
+pub const CKR_CRYPTOKI_ALREADY_INITIALIZED: CK_RV = 0x00000191;
+pub const CKR_MUTEX_BAD: CK_RV = 0x000001A0;
+pub const CKR_MUTEX_NOT_LOCKED: CK_RV = 0x000001A1;
+pub const CKR_FUNCTION_REJECTED: CK_RV = 0x00000200;
+
+pub const CKU_SO: CK_USER_TYPE = 0;
+pub const CKU_USER: CK_USER_TYPE = 1;
+pub const CKU_CONTEXT_SPECIFIC: CK_USER_TYPE = 2;
+
+pub const CKS_RO_PUBLIC_SESSION: CK_STATE = 0;
+pub const CKS_RO_USER_FUNCTIONS: CK_STATE = 1;
+pub const CKS_RW_PUBLIC_SESSION: CK_STATE = 2;
+pub const CKS_RW_USER_FUNCTIONS: CK_STATE = 3;
+pub const CKS_RW_SO_FUNCTIONS: CK_STATE = 4;
+
+pub const CKO_DATA: CK_OBJECT_CLASS = 0x00000000;
+pub const CKO_CERTIFICATE: CK_OBJECT_CLASS = 0x00000001;
+pub const CKO_PUBLIC_KEY: CK_OBJECT_CLASS = 0x00000002;
+pub const CKO_PRIVATE_KEY: CK_OBJECT_CLASS = 0x00000003;
+pub const CKO_SECRET_KEY: CK_OBJECT_CLASS = 0x00000004;
+pub const CKO_HW_FEATURE: CK_OBJECT_CLASS = 0x00000005;
+pub const CKO_DOMAIN_PARAMETERS: CK_OBJECT_CLASS = 0x00000006;
+pub const CKO_MECHANISM: CK_OBJECT_CLASS = 0x00000007;
+
+pub const CKK_RSA: CK_KEY_TYPE = 0x00000000;
+pub const CKK_DSA: CK_KEY_TYPE = 0x00000001;
+pub const CKK_DH: CK_KEY_TYPE = 0x00000002;
+pub const CKK_EC: CK_KEY_TYPE = 0x00000003;
+pub const CKK_GENERIC_SECRET: CK_KEY_TYPE = 0x00000010;
+pub const CKK_AES: CK_KEY_TYPE = 0x0000001F;
+pub const CKK_SHA256_HMAC: CK_KEY_TYPE = 0x0000002B;
+pub const CKK_SHA384_HMAC: CK_KEY_TYPE = 0x0000002C;
+pub const CKK_SHA512_HMAC: CK_KEY_TYPE = 0x0000002D;
+
+pub const CKM_RSA_PKCS_KEY_PAIR_GEN: CK_MECHANISM_TYPE = 0x00000000;
+pub const CKM_RSA_PKCS: CK_MECHANISM_TYPE = 0x00000001;
+pub const CKM_RSA_PKCS_OAEP: CK_MECHANISM_TYPE = 0x00000009;
+pub const CKM_RSA_PKCS_PSS: CK_MECHANISM_TYPE = 0x0000000D;
+pub const CKM_SHA256_RSA_PKCS: CK_MECHANISM_TYPE = 0x00000040;
+pub const CKM_SHA384_RSA_PKCS: CK_MECHANISM_TYPE = 0x00000041;
+pub const CKM_SHA512_RSA_PKCS: CK_MECHANISM_TYPE = 0x00000042;
+pub const CKM_SHA256_RSA_PKCS_PSS: CK_MECHANISM_TYPE = 0x00000043;
+pub const CKM_SHA384_RSA_PKCS_PSS: CK_MECHANISM_TYPE = 0x00000044;
+pub const CKM_SHA512_RSA_PKCS_PSS: CK_MECHANISM_TYPE = 0x00000045;
+pub const CKM_SHA256: CK_MECHANISM_TYPE = 0x00000250;
+pub const CKM_SHA256_HMAC: CK_MECHANISM_TYPE = 0x00000251;
+pub const CKM_SHA384: CK_MECHANISM_TYPE = 0x00000260;
+pub const CKM_SHA384_HMAC: CK_MECHANISM_TYPE = 0x00000261;
+pub const CKM_SHA512: CK_MECHANISM_TYPE = 0x00000270;
+pub const CKM_SHA512_HMAC: CK_MECHANISM_TYPE = 0x00000271;
+pub const CKM_EC_KEY_PAIR_GEN: CK_MECHANISM_TYPE = 0x00001040;
+pub const CKM_ECDSA: CK_MECHANISM_TYPE = 0x00001041;
+pub const CKM_ECDSA_SHA256: CK_MECHANISM_TYPE = 0x00001044;
+pub const CKM_ECDSA_SHA384: CK_MECHANISM_TYPE = 0x00001045;
+pub const CKM_ECDSA_SHA512: CK_MECHANISM_TYPE = 0x00001046;
+pub const CKM_AES_KEY_GEN: CK_MECHANISM_TYPE = 0x00001080;
+pub const CKM_AES_CBC: CK_MECHANISM_TYPE = 0x00001082;
+pub const CKM_AES_CBC_PAD: CK_MECHANISM_TYPE = 0x00001085;
+pub const CKM_AES_GCM: CK_MECHANISM_TYPE = 0x00001087;
+
+pub const CKA_CLASS: CK_ATTRIBUTE_TYPE = 0x00000000;
+pub const CKA_TOKEN: CK_ATTRIBUTE_TYPE = 0x00000001;
+pub const CKA_PRIVATE: CK_ATTRIBUTE_TYPE = 0x00000002;
+pub const CKA_LABEL: CK_ATTRIBUTE_TYPE = 0x00000003;
+pub const CKA_VALUE: CK_ATTRIBUTE_TYPE = 0x00000011;
+pub const CKA_CERTIFICATE_TYPE: CK_ATTRIBUTE_TYPE = 0x00000080;
+pub const CKA_KEY_TYPE: CK_ATTRIBUTE_TYPE = 0x00000100;
+pub const CKA_ID: CK_ATTRIBUTE_TYPE = 0x00000102;
+pub const CKA_SENSITIVE: CK_ATTRIBUTE_TYPE = 0x00000103;
+pub const CKA_ENCRYPT: CK_ATTRIBUTE_TYPE = 0x00000104;
+pub const CKA_DECRYPT: CK_ATTRIBUTE_TYPE = 0x00000105;
+pub const CKA_WRAP: CK_ATTRIBUTE_TYPE = 0x00000106;
+pub const CKA_UNWRAP: CK_ATTRIBUTE_TYPE = 0x00000107;
+pub const CKA_SIGN: CK_ATTRIBUTE_TYPE = 0x00000108;
+pub const CKA_SIGN_RECOVER: CK_ATTRIBUTE_TYPE = 0x00000109;
+pub const CKA_VERIFY: CK_ATTRIBUTE_TYPE = 0x0000010A;
+pub const CKA_VERIFY_RECOVER: CK_ATTRIBUTE_TYPE = 0x0000010B;
+pub const CKA_DERIVE: CK_ATTRIBUTE_TYPE = 0x0000010C;
+pub const CKA_MODULUS: CK_ATTRIBUTE_TYPE = 0x00000120;
+pub const CKA_MODULUS_BITS: CK_ATTRIBUTE_TYPE = 0x00000121;
+pub const CKA_PUBLIC_EXPONENT: CK_ATTRIBUTE_TYPE = 0x00000122;
+pub const CKA_PRIVATE_EXPONENT: CK_ATTRIBUTE_TYPE = 0x00000123;
+pub const CKA_PRIME_1: CK_ATTRIBUTE_TYPE = 0x00000124;
+pub const CKA_PRIME_2: CK_ATTRIBUTE_TYPE = 0x00000125;
+pub const CKA_EXPONENT_1: CK_ATTRIBUTE_TYPE = 0x00000126;
+pub const CKA_EXPONENT_2: CK_ATTRIBUTE_TYPE = 0x00000127;
+pub const CKA_COEFFICIENT: CK_ATTRIBUTE_TYPE = 0x00000128;
+pub const CKA_VALUE_LEN: CK_ATTRIBUTE_TYPE = 0x00000161;
+pub const CKA_EXTRACTABLE: CK_ATTRIBUTE_TYPE = 0x00000162;
+pub const CKA_LOCAL: CK_ATTRIBUTE_TYPE = 0x00000163;
+pub const CKA_NEVER_EXTRACTABLE: CK_ATTRIBUTE_TYPE = 0x00000164;
+pub const CKA_ALWAYS_SENSITIVE: CK_ATTRIBUTE_TYPE = 0x00000165;
+pub const CKA_KEY_GEN_MECHANISM: CK_ATTRIBUTE_TYPE = 0x00000166;
+pub const CKA_MODIFIABLE: CK_ATTRIBUTE_TYPE = 0x00000170;
+pub const CKA_COPYABLE: CK_ATTRIBUTE_TYPE = 0x00000171;
+pub const CKA_DESTROYABLE: CK_ATTRIBUTE_TYPE = 0x00000172;
+pub const CKA_EC_PARAMS: CK_ATTRIBUTE_TYPE = 0x00000180;
+pub const CKA_EC_POINT: CK_ATTRIBUTE_TYPE = 0x00000181;
+pub const CKA_ALWAYS_AUTHENTICATE: CK_ATTRIBUTE_TYPE = 0x00000202;
+
+pub const CKF_TOKEN_PRESENT: CK_FLAGS = 0x00000001;
+pub const CKF_REMOVABLE_DEVICE: CK_FLAGS = 0x00000002;
+pub const CKF_HW_SLOT: CK_FLAGS = 0x00000004;
+
+pub const CKF_RNG: CK_FLAGS = 0x00000001;
+pub const CKF_WRITE_PROTECTED: CK_FLAGS = 0x00000002;
+pub const CKF_LOGIN_REQUIRED: CK_FLAGS = 0x00000004;
+pub const CKF_USER_PIN_INITIALIZED: CK_FLAGS = 0x00000008;
+pub const CKF_RESTORE_KEY_NOT_NEEDED: CK_FLAGS = 0x00000020;
+pub const CKF_CLOCK_ON_TOKEN: CK_FLAGS = 0x00000040;
+pub const CKF_PROTECTED_AUTHENTICATION_PATH: CK_FLAGS = 0x00000100;
+pub const CKF_DUAL_CRYPTO_OPERATIONS: CK_FLAGS = 0x00000200;
+pub const CKF_TOKEN_INITIALIZED: CK_FLAGS = 0x00000400;
+pub const CKF_USER_PIN_COUNT_LOW: CK_FLAGS = 0x00010000;
+pub const CKF_USER_PIN_FINAL_TRY: CK_FLAGS = 0x00020000;
+pub const CKF_USER_PIN_LOCKED: CK_FLAGS = 0x00040000;
+pub const CKF_SO_PIN_COUNT_LOW: CK_FLAGS = 0x00100000;
+pub const CKF_SO_PIN_FINAL_TRY: CK_FLAGS = 0x00200000;
+pub const CKF_SO_PIN_LOCKED: CK_FLAGS = 0x00400000;
+
+pub const CKF_RW_SESSION: CK_FLAGS = 0x00000002;
+pub const CKF_SERIAL_SESSION: CK_FLAGS = 0x00000004;
+
+pub const CKF_HW: CK_FLAGS = 0x00000001;
+pub const CKF_ENCRYPT: CK_FLAGS = 0x00000100;
+pub const CKF_DECRYPT: CK_FLAGS = 0x00000200;
+pub const CKF_DIGEST: CK_FLAGS = 0x00000400;
+pub const CKF_SIGN: CK_FLAGS = 0x00000800;
+pub const CKF_SIGN_RECOVER: CK_FLAGS = 0x00001000;
+pub const CKF_VERIFY: CK_FLAGS = 0x00002000;
+pub const CKF_VERIFY_RECOVER: CK_FLAGS = 0x00004000;
+pub const CKF_GENERATE: CK_FLAGS = 0x00008000;
+pub const CKF_GENERATE_KEY_PAIR: CK_FLAGS = 0x00010000;
+pub const CKF_WRAP: CK_FLAGS = 0x00020000;
+pub const CKF_UNWRAP: CK_FLAGS = 0x00040000;
+pub const CKF_DERIVE: CK_FLAGS = 0x00080000;
+pub const CKF_EC_F_P: CK_FLAGS = 0x00100000;
+pub const CKF_EC_NAMEDCURVE: CK_FLAGS = 0x00800000;
+pub const CKF_EC_UNCOMPRESS: CK_FLAGS = 0x01000000;
+pub const CKF_EC_COMPRESS: CK_FLAGS = 0x02000000;
+
+pub const CKF_LIBRARY_CANT_CREATE_OS_THREADS: CK_FLAGS = 0x00000001;
+pub const CKF_OS_LOCKING_OK: CK_FLAGS = 0x00000002;
+
+pub const CKG_MGF1_SHA1: CK_RSA_PKCS_MGF_TYPE = 0x00000001;
+pub const CKG_MGF1_SHA256: CK_RSA_PKCS_MGF_TYPE = 0x00000002;
+pub const CKG_MGF1_SHA384: CK_RSA_PKCS_MGF_TYPE = 0x00000003;
+pub const CKG_MGF1_SHA512: CK_RSA_PKCS_MGF_TYPE = 0x00000004;
+
+pub const CKZ_DATA_SPECIFIED: CK_RSA_PKCS_OAEP_SOURCE_TYPE = 0x00000001;
+
+pub const CK_VERSION = extern struct {
+ major: CK_BYTE,
+ minor: CK_BYTE,
+};
+
+pub const CK_INFO = extern struct {
+ cryptokiVersion: CK_VERSION,
+ manufacturerID: [32]CK_UTF8CHAR,
+ flags: CK_FLAGS,
+ libraryDescription: [32]CK_UTF8CHAR,
+ libraryVersion: CK_VERSION,
+};
+
+pub const CK_SLOT_INFO = extern struct {
+ slotDescription: [64]CK_UTF8CHAR,
+ manufacturerID: [32]CK_UTF8CHAR,
+ flags: CK_FLAGS,
+ hardwareVersion: CK_VERSION,
+ firmwareVersion: CK_VERSION,
+};
+
+pub const CK_TOKEN_INFO = extern struct {
+ label: [32]CK_UTF8CHAR,
+ manufacturerID: [32]CK_UTF8CHAR,
+ model: [16]CK_UTF8CHAR,
+ serialNumber: [16]CK_CHAR,
+ flags: CK_FLAGS,
+ ulMaxSessionCount: CK_ULONG,
+ ulSessionCount: CK_ULONG,
+ ulMaxRwSessionCount: CK_ULONG,
+ ulRwSessionCount: CK_ULONG,
+ ulMaxPinLen: CK_ULONG,
+ ulMinPinLen: CK_ULONG,
+ ulTotalPublicMemory: CK_ULONG,
+ ulFreePublicMemory: CK_ULONG,
+ ulTotalPrivateMemory: CK_ULONG,
+ ulFreePrivateMemory: CK_ULONG,
+ hardwareVersion: CK_VERSION,
+ firmwareVersion: CK_VERSION,
+ utcTime: [16]CK_CHAR,
+};
+
+pub const CK_SESSION_INFO = extern struct {
+ slotID: CK_SLOT_ID,
+ state: CK_STATE,
+ flags: CK_FLAGS,
+ ulDeviceError: CK_ULONG,
+};
+
+pub const CK_MECHANISM_INFO = extern struct {
+ ulMinKeySize: CK_ULONG,
+ ulMaxKeySize: CK_ULONG,
+ flags: CK_FLAGS,
+};
+
+pub const CK_ATTRIBUTE = extern struct {
+ type: CK_ATTRIBUTE_TYPE,
+ pValue: ?*anyopaque,
+ ulValueLen: CK_ULONG,
+};
+
+pub const CK_MECHANISM = extern struct {
+ mechanism: CK_MECHANISM_TYPE,
+ pParameter: ?*anyopaque,
+ ulParameterLen: CK_ULONG,
+};
+
+pub const CK_GCM_PARAMS = extern struct {
+ pIv: ?[*]CK_BYTE,
+ ulIvLen: CK_ULONG,
+ ulIvBits: CK_ULONG,
+ pAAD: ?[*]CK_BYTE,
+ ulAADLen: CK_ULONG,
+ ulTagBits: CK_ULONG,
+};
+
+pub const CK_RSA_PKCS_PSS_PARAMS = extern struct {
+ hashAlg: CK_MECHANISM_TYPE,
+ mgf: CK_RSA_PKCS_MGF_TYPE,
+ sLen: CK_ULONG,
+};
+
+pub const CK_RSA_PKCS_OAEP_PARAMS = extern struct {
+ hashAlg: CK_MECHANISM_TYPE,
+ mgf: CK_RSA_PKCS_MGF_TYPE,
+ source: CK_RSA_PKCS_OAEP_SOURCE_TYPE,
+ pSourceData: ?*anyopaque,
+ ulSourceDataLen: CK_ULONG,
+};
+
+pub const CK_DATE = extern struct {
+ year: [4]CK_CHAR,
+ month: [2]CK_CHAR,
+ day: [2]CK_CHAR,
+};
+
+pub const CK_NOTIFY = ?*const fn (CK_SESSION_HANDLE, CK_NOTIFICATION, ?*anyopaque) callconv(.c) CK_RV;
+
+pub const CK_CREATEMUTEX = ?*const fn (*?*anyopaque) callconv(.c) CK_RV;
+pub const CK_DESTROYMUTEX = ?*const fn (?*anyopaque) callconv(.c) CK_RV;
+pub const CK_LOCKMUTEX = ?*const fn (?*anyopaque) callconv(.c) CK_RV;
+pub const CK_UNLOCKMUTEX = ?*const fn (?*anyopaque) callconv(.c) CK_RV;
+
+pub const CK_C_INITIALIZE_ARGS = extern struct {
+ CreateMutex: CK_CREATEMUTEX,
+ DestroyMutex: CK_DESTROYMUTEX,
+ LockMutex: CK_LOCKMUTEX,
+ UnlockMutex: CK_UNLOCKMUTEX,
+ flags: CK_FLAGS,
+ pReserved: ?*anyopaque,
+};
+
+pub const CK_C_Initialize = ?*const fn (?*anyopaque) callconv(.c) CK_RV;
+pub const CK_C_Finalize = ?*const fn (?*anyopaque) callconv(.c) CK_RV;
+pub const CK_C_GetInfo = ?*const fn (*CK_INFO) callconv(.c) CK_RV;
+pub const CK_C_GetFunctionList = ?*const fn (*?*CK_FUNCTION_LIST) callconv(.c) CK_RV;
+pub const CK_C_GetSlotList = ?*const fn (CK_BBOOL, ?[*]CK_SLOT_ID, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GetSlotInfo = ?*const fn (CK_SLOT_ID, *CK_SLOT_INFO) callconv(.c) CK_RV;
+pub const CK_C_GetTokenInfo = ?*const fn (CK_SLOT_ID, *CK_TOKEN_INFO) callconv(.c) CK_RV;
+pub const CK_C_GetMechanismList = ?*const fn (CK_SLOT_ID, ?[*]CK_MECHANISM_TYPE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GetMechanismInfo = ?*const fn (CK_SLOT_ID, CK_MECHANISM_TYPE, *CK_MECHANISM_INFO) callconv(.c) CK_RV;
+pub const CK_C_InitToken = ?*const fn (CK_SLOT_ID, ?[*]CK_UTF8CHAR, CK_ULONG, ?[*]CK_UTF8CHAR) callconv(.c) CK_RV;
+pub const CK_C_InitPIN = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_UTF8CHAR, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SetPIN = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_UTF8CHAR, CK_ULONG, ?[*]CK_UTF8CHAR, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_OpenSession = ?*const fn (CK_SLOT_ID, CK_FLAGS, ?*anyopaque, CK_NOTIFY, *CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_CloseSession = ?*const fn (CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_CloseAllSessions = ?*const fn (CK_SLOT_ID) callconv(.c) CK_RV;
+pub const CK_C_GetSessionInfo = ?*const fn (CK_SESSION_HANDLE, *CK_SESSION_INFO) callconv(.c) CK_RV;
+pub const CK_C_GetOperationState = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SetOperationState = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_Login = ?*const fn (CK_SESSION_HANDLE, CK_USER_TYPE, ?[*]CK_UTF8CHAR, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_Logout = ?*const fn (CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_CreateObject = ?*const fn (CK_SESSION_HANDLE, [*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_CopyObject = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE, [*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_DestroyObject = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_GetObjectSize = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GetAttributeValue = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE, [*]CK_ATTRIBUTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SetAttributeValue = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE, [*]CK_ATTRIBUTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_FindObjectsInit = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_ATTRIBUTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_FindObjects = ?*const fn (CK_SESSION_HANDLE, [*]CK_OBJECT_HANDLE, CK_ULONG, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_FindObjectsFinal = ?*const fn (CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_EncryptInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_Encrypt = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_EncryptUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_EncryptFinal = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DecryptInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_Decrypt = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DecryptUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DecryptFinal = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DigestInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM) callconv(.c) CK_RV;
+pub const CK_C_Digest = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DigestUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DigestKey = ?*const fn (CK_SESSION_HANDLE, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_DigestFinal = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SignInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_Sign = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SignUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SignFinal = ?*const fn (CK_SESSION_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SignRecoverInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_SignRecover = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_VerifyInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_Verify = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_VerifyUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_VerifyFinal = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_VerifyRecoverInit = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_VerifyRecover = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DigestEncryptUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DecryptDigestUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_SignEncryptUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_DecryptVerifyUpdate = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GenerateKey = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, [*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_GenerateKeyPair = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, [*]CK_ATTRIBUTE, CK_ULONG, [*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_WrapKey = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE, CK_OBJECT_HANDLE, ?[*]CK_BYTE, *CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_UnwrapKey = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE, [*]CK_BYTE, CK_ULONG, [*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_DeriveKey = ?*const fn (CK_SESSION_HANDLE, *CK_MECHANISM, CK_OBJECT_HANDLE, ?[*]CK_ATTRIBUTE, CK_ULONG, *CK_OBJECT_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_SeedRandom = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GenerateRandom = ?*const fn (CK_SESSION_HANDLE, [*]CK_BYTE, CK_ULONG) callconv(.c) CK_RV;
+pub const CK_C_GetFunctionStatus = ?*const fn (CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_CancelFunction = ?*const fn (CK_SESSION_HANDLE) callconv(.c) CK_RV;
+pub const CK_C_WaitForSlotEvent = ?*const fn (CK_FLAGS, *CK_SLOT_ID, ?*anyopaque) callconv(.c) CK_RV;
+
+pub const CK_FUNCTION_LIST = extern struct {
+ version: CK_VERSION,
+ C_Initialize: CK_C_Initialize,
+ C_Finalize: CK_C_Finalize,
+ C_GetInfo: CK_C_GetInfo,
+ C_GetFunctionList: CK_C_GetFunctionList,
+ C_GetSlotList: CK_C_GetSlotList,
+ C_GetSlotInfo: CK_C_GetSlotInfo,
+ C_GetTokenInfo: CK_C_GetTokenInfo,
+ C_GetMechanismList: CK_C_GetMechanismList,
+ C_GetMechanismInfo: CK_C_GetMechanismInfo,
+ C_InitToken: CK_C_InitToken,
+ C_InitPIN: CK_C_InitPIN,
+ C_SetPIN: CK_C_SetPIN,
+ C_OpenSession: CK_C_OpenSession,
+ C_CloseSession: CK_C_CloseSession,
+ C_CloseAllSessions: CK_C_CloseAllSessions,
+ C_GetSessionInfo: CK_C_GetSessionInfo,
+ C_GetOperationState: CK_C_GetOperationState,
+ C_SetOperationState: CK_C_SetOperationState,
+ C_Login: CK_C_Login,
+ C_Logout: CK_C_Logout,
+ C_CreateObject: CK_C_CreateObject,
+ C_CopyObject: CK_C_CopyObject,
+ C_DestroyObject: CK_C_DestroyObject,
+ C_GetObjectSize: CK_C_GetObjectSize,
+ C_GetAttributeValue: CK_C_GetAttributeValue,
+ C_SetAttributeValue: CK_C_SetAttributeValue,
+ C_FindObjectsInit: CK_C_FindObjectsInit,
+ C_FindObjects: CK_C_FindObjects,
+ C_FindObjectsFinal: CK_C_FindObjectsFinal,
+ C_EncryptInit: CK_C_EncryptInit,
+ C_Encrypt: CK_C_Encrypt,
+ C_EncryptUpdate: CK_C_EncryptUpdate,
+ C_EncryptFinal: CK_C_EncryptFinal,
+ C_DecryptInit: CK_C_DecryptInit,
+ C_Decrypt: CK_C_Decrypt,
+ C_DecryptUpdate: CK_C_DecryptUpdate,
+ C_DecryptFinal: CK_C_DecryptFinal,
+ C_DigestInit: CK_C_DigestInit,
+ C_Digest: CK_C_Digest,
+ C_DigestUpdate: CK_C_DigestUpdate,
+ C_DigestKey: CK_C_DigestKey,
+ C_DigestFinal: CK_C_DigestFinal,
+ C_SignInit: CK_C_SignInit,
+ C_Sign: CK_C_Sign,
+ C_SignUpdate: CK_C_SignUpdate,
+ C_SignFinal: CK_C_SignFinal,
+ C_SignRecoverInit: CK_C_SignRecoverInit,
+ C_SignRecover: CK_C_SignRecover,
+ C_VerifyInit: CK_C_VerifyInit,
+ C_Verify: CK_C_Verify,
+ C_VerifyUpdate: CK_C_VerifyUpdate,
+ C_VerifyFinal: CK_C_VerifyFinal,
+ C_VerifyRecoverInit: CK_C_VerifyRecoverInit,
+ C_VerifyRecover: CK_C_VerifyRecover,
+ C_DigestEncryptUpdate: CK_C_DigestEncryptUpdate,
+ C_DecryptDigestUpdate: CK_C_DecryptDigestUpdate,
+ C_SignEncryptUpdate: CK_C_SignEncryptUpdate,
+ C_DecryptVerifyUpdate: CK_C_DecryptVerifyUpdate,
+ C_GenerateKey: CK_C_GenerateKey,
+ C_GenerateKeyPair: CK_C_GenerateKeyPair,
+ C_WrapKey: CK_C_WrapKey,
+ C_UnwrapKey: CK_C_UnwrapKey,
+ C_DeriveKey: CK_C_DeriveKey,
+ C_SeedRandom: CK_C_SeedRandom,
+ C_GenerateRandom: CK_C_GenerateRandom,
+ C_GetFunctionStatus: CK_C_GetFunctionStatus,
+ C_CancelFunction: CK_C_CancelFunction,
+ C_WaitForSlotEvent: CK_C_WaitForSlotEvent,
+};
diff --git a/PROJECTS/advanced/hsm-emulator/src/config.zig b/PROJECTS/advanced/hsm-emulator/src/config.zig
new file mode 100644
index 00000000..7ee44fe9
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/config.zig
@@ -0,0 +1,89 @@
+// ©AngelaMos | 2026
+// config.zig
+
+const ck = @import("ck.zig");
+
+pub const cryptoki_version: ck.CK_VERSION = .{ .major = 2, .minor = 40 };
+pub const library_version: ck.CK_VERSION = .{ .major = 0, .minor = 1 };
+pub const hardware_version: ck.CK_VERSION = .{ .major = 1, .minor = 0 };
+pub const firmware_version: ck.CK_VERSION = .{ .major = 0, .minor = 1 };
+
+pub const manufacturer_id = "Angelamos";
+pub const library_description = "Zig HSM Emulator";
+pub const slot_description = "AngelaMos HSM Emulator Slot 0";
+pub const token_label = "AngelaMos-HSM";
+pub const token_model = "hsm-emu";
+pub const token_serial = "0000000000000001";
+
+pub const slot_id: ck.CK_SLOT_ID = 0;
+pub const slot_count: ck.CK_ULONG = 1;
+pub const max_sessions: ck.CK_ULONG = 64;
+
+pub const min_pin_len: ck.CK_ULONG = 4;
+pub const max_pin_len: ck.CK_ULONG = 255;
+
+pub const aes_min_key_bytes: ck.CK_ULONG = 16;
+pub const aes_max_key_bytes: ck.CK_ULONG = 32;
+pub const rsa_min_key_bits: ck.CK_ULONG = 2048;
+pub const rsa_max_key_bits: ck.CK_ULONG = 4096;
+pub const ec_min_key_bits: ck.CK_ULONG = 256;
+pub const ec_max_key_bits: ck.CK_ULONG = 384;
+pub const ec_keygen_max_attempts: usize = 8;
+pub const hmac_min_key_bytes: ck.CK_ULONG = 32;
+pub const hmac_max_key_bytes: ck.CK_ULONG = 64;
+
+pub const pin_kdf_t: u32 = 3;
+pub const pin_kdf_m_kib: u32 = 65536;
+pub const pin_kdf_p: u24 = 1;
+pub const pin_salt_len: usize = 16;
+pub const pin_hash_len: usize = 32;
+
+pub const login_max_attempts: ck.CK_ULONG = 3;
+
+pub const token_path_env = "ANGELAMOS_HSM_TOKEN";
+pub const token_path_default = ".angelamos-hsm-token";
+pub const token_record_magic: u32 = 0x484D5331;
+pub const token_record_version: u32 = 2;
+pub const path_buf_len: usize = 4096;
+pub const token_read_limit: usize = 512;
+pub const label_len: usize = 32;
+
+pub const object_path_env = "ANGELAMOS_HSM_OBJECTS";
+pub const object_path_default = ".angelamos-hsm-objects";
+pub const object_record_magic: u32 = 0x484D4F31;
+pub const object_record_version: u32 = 2;
+pub const object_read_limit: usize = 4 * 1024 * 1024;
+pub const max_objects: usize = 256;
+pub const max_attributes_per_object: usize = 64;
+pub const max_attr_value_len: usize = 64 * 1024;
+
+pub const aes_block_len: usize = 16;
+pub const gcm_iv_len: usize = 12;
+pub const gcm_iv_bits: ck.CK_ULONG = 96;
+pub const gcm_tag_len: usize = 16;
+pub const gcm_tag_bits: ck.CK_ULONG = 128;
+pub const max_gcm_aad_len: usize = 256;
+
+pub const master_key_len: usize = 32;
+
+pub const supported_mechanisms = [_]ck.CK_MECHANISM_TYPE{
+ ck.CKM_SHA256,
+ ck.CKM_SHA384,
+ ck.CKM_SHA512,
+ ck.CKM_SHA256_HMAC,
+ ck.CKM_SHA384_HMAC,
+ ck.CKM_SHA512_HMAC,
+ ck.CKM_AES_KEY_GEN,
+ ck.CKM_AES_CBC,
+ ck.CKM_AES_CBC_PAD,
+ ck.CKM_AES_GCM,
+ ck.CKM_EC_KEY_PAIR_GEN,
+ ck.CKM_ECDSA,
+ ck.CKM_ECDSA_SHA256,
+ ck.CKM_RSA_PKCS_KEY_PAIR_GEN,
+ ck.CKM_RSA_PKCS,
+ ck.CKM_SHA256_RSA_PKCS,
+ ck.CKM_RSA_PKCS_PSS,
+ ck.CKM_SHA256_RSA_PKCS_PSS,
+ ck.CKM_RSA_PKCS_OAEP,
+};
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/env.zig b/PROJECTS/advanced/hsm-emulator/src/core/env.zig
new file mode 100644
index 00000000..bc20c6b2
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/env.zig
@@ -0,0 +1,23 @@
+// ©AngelaMos | 2026
+// env.zig
+
+const std = @import("std");
+
+pub fn get(key: []const u8) ?[]const u8 {
+ var i: usize = 0;
+ while (std.c.environ[i]) |entry| : (i += 1) {
+ const s = std.mem.sliceTo(entry, 0);
+ if (s.len > key.len and s[key.len] == '=' and std.mem.eql(u8, s[0..key.len], key)) {
+ return s[key.len + 1 ..];
+ }
+ }
+ return null;
+}
+
+pub fn resolvePath(buf: []u8, env_key: []const u8, default_rel: []const u8) ![]const u8 {
+ if (get(env_key)) |p| {
+ if (p.len > 0) return p;
+ }
+ const home = get("HOME") orelse return error.NoHomeDir;
+ return std.fmt.bufPrint(buf, "{s}/{s}", .{ home, default_rel });
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/lock.zig b/PROJECTS/advanced/hsm-emulator/src/core/lock.zig
new file mode 100644
index 00000000..d53ca112
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/lock.zig
@@ -0,0 +1,31 @@
+// ©AngelaMos | 2026
+// lock.zig
+
+const std = @import("std");
+
+const spin_limit: usize = 64;
+
+pub const Lock = struct {
+ state: std.atomic.Mutex = .unlocked,
+
+ pub fn lock(self: *Lock) void {
+ var spins: usize = 0;
+ while (!self.state.tryLock()) {
+ if (spins < spin_limit) {
+ spins += 1;
+ std.atomic.spinLoopHint();
+ } else {
+ spins = 0;
+ std.Thread.yield() catch std.atomic.spinLoopHint();
+ }
+ }
+ }
+
+ pub fn tryLock(self: *Lock) bool {
+ return self.state.tryLock();
+ }
+
+ pub fn unlock(self: *Lock) void {
+ self.state.unlock();
+ }
+};
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/object_store.zig b/PROJECTS/advanced/hsm-emulator/src/core/object_store.zig
new file mode 100644
index 00000000..d9ae4e51
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/object_store.zig
@@ -0,0 +1,582 @@
+// ©AngelaMos | 2026
+// object_store.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const env = @import("env.zig");
+const keystore = @import("../crypto/keystore.zig");
+
+fn secureFree(allocator: std.mem.Allocator, value: []u8) void {
+ std.crypto.secureZero(u8, value);
+ allocator.free(value);
+}
+
+pub const Attribute = struct {
+ type: ck.CK_ATTRIBUTE_TYPE,
+ value: []u8,
+ sealed: bool = false,
+};
+
+fn isSecretMaterial(t: ck.CK_ATTRIBUTE_TYPE) bool {
+ return switch (t) {
+ ck.CKA_VALUE,
+ ck.CKA_PRIVATE_EXPONENT,
+ ck.CKA_PRIME_1,
+ ck.CKA_PRIME_2,
+ ck.CKA_EXPONENT_1,
+ ck.CKA_EXPONENT_2,
+ ck.CKA_COEFFICIENT,
+ => true,
+ else => false,
+ };
+}
+
+pub const Object = struct {
+ attrs: std.ArrayList(Attribute) = .empty,
+
+ pub fn deinit(self: *Object, allocator: std.mem.Allocator) void {
+ for (self.attrs.items) |a| secureFree(allocator, a.value);
+ self.attrs.deinit(allocator);
+ self.* = .{};
+ }
+
+ pub fn findPtr(self: *Object, t: ck.CK_ATTRIBUTE_TYPE) ?*Attribute {
+ for (self.attrs.items) |*a| {
+ if (a.type == t) return a;
+ }
+ return null;
+ }
+
+ pub fn get(self: *const Object, t: ck.CK_ATTRIBUTE_TYPE) ?[]const u8 {
+ for (self.attrs.items) |a| {
+ if (a.type == t) return a.value;
+ }
+ return null;
+ }
+
+ pub fn has(self: *const Object, t: ck.CK_ATTRIBUTE_TYPE) bool {
+ return self.get(t) != null;
+ }
+
+ pub fn getBool(self: *const Object, t: ck.CK_ATTRIBUTE_TYPE) bool {
+ const v = self.get(t) orelse return false;
+ return v.len >= 1 and v[0] != ck.CK_FALSE;
+ }
+
+ pub fn isToken(self: *const Object) bool {
+ return self.getBool(ck.CKA_TOKEN);
+ }
+
+ pub fn isPrivate(self: *const Object) bool {
+ return self.getBool(ck.CKA_PRIVATE);
+ }
+
+ pub fn shouldSeal(self: *const Object, t: ck.CK_ATTRIBUTE_TYPE) bool {
+ if (!isSecretMaterial(t)) return false;
+ if (self.getBool(ck.CKA_SENSITIVE)) return true;
+ return self.has(ck.CKA_EXTRACTABLE) and !self.getBool(ck.CKA_EXTRACTABLE);
+ }
+
+ pub fn hasSealable(self: *const Object) bool {
+ for (self.attrs.items) |a| {
+ if (self.shouldSeal(a.type)) return true;
+ }
+ return false;
+ }
+
+ pub fn set(self: *Object, allocator: std.mem.Allocator, t: ck.CK_ATTRIBUTE_TYPE, bytes: []const u8) !void {
+ if (bytes.len > config.max_attr_value_len) return error.AttrTooLarge;
+ if (self.findPtr(t)) |a| {
+ const dup = try allocator.dupe(u8, bytes);
+ secureFree(allocator, a.value);
+ a.value = dup;
+ return;
+ }
+ if (self.attrs.items.len >= config.max_attributes_per_object) return error.TooManyAttributes;
+ const dup = try allocator.dupe(u8, bytes);
+ errdefer secureFree(allocator, dup);
+ try self.attrs.append(allocator, .{ .type = t, .value = dup });
+ }
+
+ pub fn sizeBytes(self: *const Object) ck.CK_ULONG {
+ var total: ck.CK_ULONG = 0;
+ for (self.attrs.items) |a| total += @intCast(a.value.len);
+ return total;
+ }
+
+ pub fn clone(self: *const Object, allocator: std.mem.Allocator) !Object {
+ var out: Object = .{};
+ errdefer out.deinit(allocator);
+ for (self.attrs.items) |a| {
+ try out.set(allocator, a.type, a.value);
+ out.findPtr(a.type).?.sealed = a.sealed;
+ }
+ return out;
+ }
+};
+
+pub fn visible(obj: *const Object, logged_in: ?ck.CK_USER_TYPE) bool {
+ if (!obj.isPrivate()) return true;
+ return logged_in == ck.CKU_USER;
+}
+
+pub fn mapSetErr(e: anyerror) ck.CK_RV {
+ return switch (e) {
+ error.OutOfMemory => ck.CKR_HOST_MEMORY,
+ error.AttrTooLarge => ck.CKR_ATTRIBUTE_VALUE_INVALID,
+ error.TooManyAttributes => ck.CKR_TEMPLATE_INCONSISTENT,
+ else => ck.CKR_FUNCTION_FAILED,
+ };
+}
+
+const Entry = struct {
+ handle: ck.CK_OBJECT_HANDLE,
+ obj: Object,
+};
+
+pub const Store = struct {
+ slots: [config.max_objects]?Entry = @splat(null),
+ next_handle: ck.CK_OBJECT_HANDLE = 1,
+
+ pub fn insert(self: *Store, obj: Object) ?ck.CK_OBJECT_HANDLE {
+ for (&self.slots) |*slot| {
+ if (slot.* == null) {
+ const h = self.next_handle;
+ slot.* = .{ .handle = h, .obj = obj };
+ self.next_handle += 1;
+ return h;
+ }
+ }
+ return null;
+ }
+
+ pub fn getPtr(self: *Store, h: ck.CK_OBJECT_HANDLE) ?*Object {
+ if (h == ck.CK_INVALID_HANDLE) return null;
+ for (&self.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (e.handle == h) return &e.obj;
+ }
+ }
+ return null;
+ }
+
+ pub fn destroy(self: *Store, allocator: std.mem.Allocator, h: ck.CK_OBJECT_HANDLE) bool {
+ if (h == ck.CK_INVALID_HANDLE) return false;
+ for (&self.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (e.handle == h) {
+ e.obj.deinit(allocator);
+ slot.* = null;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ pub fn count(self: *const Store) usize {
+ var n: usize = 0;
+ for (self.slots) |slot| {
+ if (slot != null) n += 1;
+ }
+ return n;
+ }
+
+ pub fn clear(self: *Store, allocator: std.mem.Allocator) void {
+ for (&self.slots) |*slot| {
+ if (slot.*) |*e| {
+ e.obj.deinit(allocator);
+ slot.* = null;
+ }
+ }
+ self.next_handle = 1;
+ }
+
+ pub fn deinit(self: *Store, allocator: std.mem.Allocator) void {
+ self.clear(allocator);
+ }
+};
+
+fn appendU32(buf: *std.ArrayList(u8), allocator: std.mem.Allocator, v: u32) !void {
+ const x: u32 = v;
+ try buf.appendSlice(allocator, std.mem.asBytes(&x));
+}
+
+fn appendU64(buf: *std.ArrayList(u8), allocator: std.mem.Allocator, v: u64) !void {
+ const x: u64 = v;
+ try buf.appendSlice(allocator, std.mem.asBytes(&x));
+}
+
+fn readU32(bytes: []const u8, cursor: *usize) !u32 {
+ const w = @sizeOf(u32);
+ if (cursor.* + w > bytes.len) return error.Truncated;
+ const v = std.mem.bytesToValue(u32, bytes[cursor.*..][0..w]);
+ cursor.* += w;
+ return v;
+}
+
+fn readU64(bytes: []const u8, cursor: *usize) !u64 {
+ const w = @sizeOf(u64);
+ if (cursor.* + w > bytes.len) return error.Truncated;
+ const v = std.mem.bytesToValue(u64, bytes[cursor.*..][0..w]);
+ cursor.* += w;
+ return v;
+}
+
+pub fn serialize(io: std.Io, allocator: std.mem.Allocator, store: *const Store, mk: ?keystore.MasterKey) ![]u8 {
+ var buf: std.ArrayList(u8) = .empty;
+ errdefer buf.deinit(allocator);
+
+ try appendU32(&buf, allocator, config.object_record_magic);
+ try appendU32(&buf, allocator, config.object_record_version);
+
+ var n: u32 = 0;
+ for (store.slots) |slot| {
+ if (slot) |e| {
+ if (e.obj.isToken()) n += 1;
+ }
+ }
+ try appendU32(&buf, allocator, n);
+
+ for (store.slots) |slot| {
+ if (slot) |e| {
+ if (!e.obj.isToken()) continue;
+ try appendU32(&buf, allocator, @intCast(e.obj.attrs.items.len));
+ for (e.obj.attrs.items) |a| {
+ try appendU64(&buf, allocator, @intCast(a.type));
+ if (!a.sealed and e.obj.shouldSeal(a.type)) {
+ const key = mk orelse return error.NoMasterKey;
+ const scratch = try allocator.alloc(u8, keystore.sealedLen(a.value.len));
+ defer allocator.free(scratch);
+ const wrote = try keystore.seal(io, &key, std.mem.asBytes(&a.type), a.value, scratch);
+ try appendU64(&buf, allocator, @intCast(wrote));
+ try buf.appendSlice(allocator, scratch[0..wrote]);
+ } else {
+ try appendU64(&buf, allocator, @intCast(a.value.len));
+ try buf.appendSlice(allocator, a.value);
+ }
+ }
+ }
+ }
+
+ return buf.toOwnedSlice(allocator);
+}
+
+fn parse(allocator: std.mem.Allocator, store: *Store, bytes: []const u8) !void {
+ var c: usize = 0;
+ const magic = try readU32(bytes, &c);
+ const version = try readU32(bytes, &c);
+ if (magic != config.object_record_magic or version != config.object_record_version) return error.BadHeader;
+
+ const obj_count = try readU32(bytes, &c);
+ var i: u32 = 0;
+ while (i < obj_count) : (i += 1) {
+ var obj: Object = .{};
+ var inserted = false;
+ defer if (!inserted) obj.deinit(allocator);
+
+ const attr_count = try readU32(bytes, &c);
+ if (attr_count > config.max_attributes_per_object) return error.TooManyAttributes;
+
+ var j: u32 = 0;
+ while (j < attr_count) : (j += 1) {
+ const t = try readU64(bytes, &c);
+ const len = try readU64(bytes, &c);
+ if (len > config.max_attr_value_len) return error.AttrTooLarge;
+ const n: usize = @intCast(len);
+ if (c + n > bytes.len) return error.Truncated;
+ try obj.set(allocator, @intCast(t), bytes[c .. c + n]);
+ c += n;
+ }
+
+ for (obj.attrs.items) |*a| {
+ if (obj.shouldSeal(a.type)) a.sealed = true;
+ }
+
+ if (store.insert(obj) == null) return error.StoreFull;
+ inserted = true;
+ }
+}
+
+pub fn save(io: std.Io, allocator: std.mem.Allocator, store: *const Store, mk: ?keystore.MasterKey) !void {
+ const data = try serialize(io, allocator, store, mk);
+ defer allocator.free(data);
+
+ var buf: [config.path_buf_len]u8 = undefined;
+ const path = try env.resolvePath(&buf, config.object_path_env, config.object_path_default);
+
+ var tmp_buf: [config.path_buf_len + 8]u8 = undefined;
+ const tmp = try std.fmt.bufPrint(&tmp_buf, "{s}.tmp", .{path});
+ const dir = std.Io.Dir.cwd();
+ try dir.writeFile(io, .{ .sub_path = tmp, .data = data });
+ try dir.rename(tmp, dir, path, io);
+}
+
+pub fn unlock(allocator: std.mem.Allocator, store: *Store, mk: keystore.MasterKey) !void {
+ for (&store.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (!e.obj.isToken()) continue;
+ for (e.obj.attrs.items) |*a| {
+ if (!a.sealed) continue;
+ if (a.value.len < keystore.seal_overhead) return error.Corrupt;
+ const plain = try allocator.alloc(u8, a.value.len - keystore.seal_overhead);
+ _ = keystore.unseal(&mk, std.mem.asBytes(&a.type), a.value, plain) catch {
+ secureFree(allocator, plain);
+ return error.AuthFailed;
+ };
+ secureFree(allocator, a.value);
+ a.value = plain;
+ a.sealed = false;
+ }
+ }
+ }
+}
+
+pub fn lock(io: std.Io, allocator: std.mem.Allocator, store: *Store, mk: keystore.MasterKey) !void {
+ for (&store.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (!e.obj.isToken()) continue;
+ for (e.obj.attrs.items) |*a| {
+ if (a.sealed or !e.obj.shouldSeal(a.type)) continue;
+ const sealed = try allocator.alloc(u8, keystore.sealedLen(a.value.len));
+ _ = keystore.seal(io, &mk, std.mem.asBytes(&a.type), a.value, sealed) catch {
+ secureFree(allocator, sealed);
+ return error.Seal;
+ };
+ secureFree(allocator, a.value);
+ a.value = sealed;
+ a.sealed = true;
+ }
+ }
+ }
+}
+
+pub fn scrubUnsealed(store: *Store) void {
+ for (&store.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (!e.obj.isToken()) continue;
+ for (e.obj.attrs.items) |*a| {
+ if (a.sealed or !e.obj.shouldSeal(a.type)) continue;
+ std.crypto.secureZero(u8, a.value);
+ a.sealed = true;
+ }
+ }
+ }
+}
+
+pub fn load(io: std.Io, allocator: std.mem.Allocator, store: *Store) void {
+ var buf: [config.path_buf_len]u8 = undefined;
+ const path = env.resolvePath(&buf, config.object_path_env, config.object_path_default) catch return;
+ const bytes = std.Io.Dir.cwd().readFileAlloc(io, path, allocator, .limited(config.object_read_limit)) catch return;
+ defer allocator.free(bytes);
+ parse(allocator, store, bytes) catch store.clear(allocator);
+}
+
+test "set replaces an existing attribute and reports size" {
+ const a = std.testing.allocator;
+ var obj: Object = .{};
+ defer obj.deinit(a);
+
+ try obj.set(a, ck.CKA_LABEL, "first");
+ try obj.set(a, ck.CKA_LABEL, "second-value");
+ try std.testing.expectEqual(@as(usize, 1), obj.attrs.items.len);
+ try std.testing.expectEqualSlices(u8, "second-value", obj.get(ck.CKA_LABEL).?);
+ try std.testing.expectEqual(@as(ck.CK_ULONG, "second-value".len), obj.sizeBytes());
+}
+
+test "bool and class helpers read CK_BBOOL semantics" {
+ const a = std.testing.allocator;
+ var obj: Object = .{};
+ defer obj.deinit(a);
+
+ try std.testing.expect(!obj.isToken());
+ try obj.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_TRUE});
+ try obj.set(a, ck.CKA_PRIVATE, &[_]u8{ck.CK_FALSE});
+ try std.testing.expect(obj.isToken());
+ try std.testing.expect(!obj.isPrivate());
+}
+
+test "store hands out monotonic non-reused handles and destroys" {
+ const a = std.testing.allocator;
+ var store: Store = .{};
+ defer store.deinit(a);
+
+ var o1: Object = .{};
+ try o1.set(a, ck.CKA_CLASS, &[_]u8{0});
+ const h1 = store.insert(o1).?;
+
+ var o2: Object = .{};
+ try o2.set(a, ck.CKA_CLASS, &[_]u8{1});
+ const h2 = store.insert(o2).?;
+
+ try std.testing.expect(h1 != h2);
+ try std.testing.expect(store.getPtr(h1) != null);
+ try std.testing.expect(store.destroy(a, h1));
+ try std.testing.expect(store.getPtr(h1) == null);
+ try std.testing.expect(!store.destroy(a, h1));
+
+ var o3: Object = .{};
+ try o3.set(a, ck.CKA_CLASS, &[_]u8{2});
+ const h3 = store.insert(o3).?;
+ try std.testing.expect(h3 != h1 and h3 != h2);
+ try std.testing.expectEqual(@as(usize, 2), store.count());
+}
+
+test "serialize then parse round-trips only token objects" {
+ const a = std.testing.allocator;
+ var store: Store = .{};
+ defer store.deinit(a);
+
+ var tok: Object = .{};
+ try tok.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_TRUE});
+ try tok.set(a, ck.CKA_CLASS, &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 });
+ try tok.set(a, ck.CKA_LABEL, "persisted");
+ _ = store.insert(tok);
+
+ var sess: Object = .{};
+ try sess.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_FALSE});
+ try sess.set(a, ck.CKA_LABEL, "ephemeral");
+ _ = store.insert(sess);
+
+ const data = try serialize(std.testing.io, a, &store, null);
+ defer a.free(data);
+
+ var restored: Store = .{};
+ defer restored.deinit(a);
+ try parse(a, &restored, data);
+
+ try std.testing.expectEqual(@as(usize, 1), restored.count());
+ var found_label: ?[]const u8 = null;
+ for (&restored.slots) |*slot| {
+ if (slot.*) |*e| found_label = e.obj.get(ck.CKA_LABEL);
+ }
+ try std.testing.expectEqualSlices(u8, "persisted", found_label.?);
+}
+
+test "serialize seals a sensitive value at rest and unlock recovers it" {
+ const a = std.testing.allocator;
+ const io = std.testing.io;
+ const mk: keystore.MasterKey = @splat(0x5a);
+ const wrong: keystore.MasterKey = @splat(0x17);
+ const secret = "SUPER-SECRET-KEY-MATERIAL";
+
+ var store: Store = .{};
+ defer store.deinit(a);
+ var key: Object = .{};
+ try key.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_TRUE});
+ try key.set(a, ck.CKA_CLASS, &[_]u8{ 4, 0, 0, 0, 0, 0, 0, 0 });
+ try key.set(a, ck.CKA_SENSITIVE, &[_]u8{ck.CK_TRUE});
+ try key.set(a, ck.CKA_VALUE, secret);
+ _ = store.insert(key);
+
+ const data = try serialize(io, a, &store, mk);
+ defer a.free(data);
+ try std.testing.expect(std.mem.indexOf(u8, data, secret) == null);
+
+ var restored: Store = .{};
+ defer restored.deinit(a);
+ try parse(a, &restored, data);
+
+ var sealed_value: ?[]const u8 = null;
+ for (&restored.slots) |*slot| {
+ if (slot.*) |*e| {
+ if (e.obj.findPtr(ck.CKA_VALUE)) |attr| {
+ try std.testing.expect(attr.sealed);
+ sealed_value = attr.value;
+ }
+ }
+ }
+ try std.testing.expect(sealed_value != null);
+ try std.testing.expect(std.mem.indexOf(u8, sealed_value.?, secret) == null);
+
+ try std.testing.expectError(error.AuthFailed, unlock(a, &restored, wrong));
+ try unlock(a, &restored, mk);
+ for (&restored.slots) |*slot| {
+ if (slot.*) |*e| {
+ const v = e.obj.get(ck.CKA_VALUE).?;
+ try std.testing.expectEqualSlices(u8, secret, v);
+ try std.testing.expect(!e.obj.findPtr(ck.CKA_VALUE).?.sealed);
+ }
+ }
+}
+
+test "secureFree clears the secret from the value buffer" {
+ var backing: [64]u8 = undefined;
+ var fba = std.heap.FixedBufferAllocator.init(&backing);
+ const a = fba.allocator();
+ const v = try a.dupe(u8, "SUPER-SECRET-KEY");
+ const region = backing[0..v.len];
+ try std.testing.expect(std.mem.indexOf(u8, region, "SECRET") != null);
+ secureFree(a, v);
+ try std.testing.expect(std.mem.indexOf(u8, region, "SECRET") == null);
+}
+
+test "deinit clears a plaintext secret attribute from its buffer" {
+ var backing: [512]u8 = undefined;
+ var fba = std.heap.FixedBufferAllocator.init(&backing);
+ const a = fba.allocator();
+ var obj: Object = .{};
+ try obj.set(a, ck.CKA_VALUE, "AES-256-SECRET-KEY-BYTES");
+ const stored = obj.findPtr(ck.CKA_VALUE).?.value;
+ const region = stored[0..stored.len];
+ try std.testing.expect(std.mem.indexOf(u8, region, "SECRET") != null);
+ obj.deinit(a);
+ try std.testing.expect(std.mem.indexOf(u8, region, "SECRET") == null);
+}
+
+test "parse rejects a bad magic without leaking" {
+ const a = std.testing.allocator;
+ var store: Store = .{};
+ defer store.deinit(a);
+ const junk = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+ try std.testing.expectError(error.BadHeader, parse(a, &store, &junk));
+ try std.testing.expectEqual(@as(usize, 0), store.count());
+}
+
+test "parse fails safe on a truncated record and clear frees partial inserts" {
+ const a = std.testing.allocator;
+ var store: Store = .{};
+ defer store.deinit(a);
+
+ var good: Store = .{};
+ defer good.deinit(a);
+ var tok: Object = .{};
+ try tok.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_TRUE});
+ try tok.set(a, ck.CKA_LABEL, "x");
+ _ = good.insert(tok);
+ const data = try serialize(std.testing.io, a, &good, null);
+ defer a.free(data);
+
+ try std.testing.expectError(error.Truncated, parse(a, &store, data[0 .. data.len - 1]));
+ store.clear(a);
+ try std.testing.expectEqual(@as(usize, 0), store.count());
+}
+
+test "scrubUnsealed fail-closes plaintext secrets after a failed re-seal" {
+ const a = std.testing.allocator;
+ const io = std.testing.io;
+ const secret = "PLAINTEXT-KEY-MUST-NOT-SURVIVE-LOGOUT";
+
+ var store: Store = .{};
+ defer store.deinit(a);
+ var key: Object = .{};
+ try key.set(a, ck.CKA_TOKEN, &[_]u8{ck.CK_TRUE});
+ try key.set(a, ck.CKA_SENSITIVE, &[_]u8{ck.CK_TRUE});
+ try key.set(a, ck.CKA_VALUE, secret);
+ _ = store.insert(key);
+
+ const mk: keystore.MasterKey = @splat(0x42);
+ var failing = std.testing.FailingAllocator.init(a, .{ .fail_index = 0 });
+ try std.testing.expectError(error.OutOfMemory, lock(io, failing.allocator(), &store, mk));
+
+ const attr = store.slots[0].?.obj.findPtr(ck.CKA_VALUE).?;
+ try std.testing.expect(!attr.sealed);
+ try std.testing.expect(std.mem.indexOf(u8, attr.value, "SURVIVE") != null);
+
+ scrubUnsealed(&store);
+
+ try std.testing.expect(attr.sealed);
+ try std.testing.expect(std.mem.indexOf(u8, attr.value, "SURVIVE") == null);
+ for (attr.value) |b| try std.testing.expectEqual(@as(u8, 0), b);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/session.zig b/PROJECTS/advanced/hsm-emulator/src/core/session.zig
new file mode 100644
index 00000000..37c7c86e
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/session.zig
@@ -0,0 +1,281 @@
+// ©AngelaMos | 2026
+// session.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const digest = @import("../crypto/digest.zig");
+const mac = @import("../crypto/mac.zig");
+const cipher = @import("../crypto/cipher.zig");
+const ecdsa = @import("../crypto/ecdsa.zig");
+const rsa = @import("../crypto/rsa.zig");
+
+pub const Find = struct {
+ matches: [config.max_objects]ck.CK_OBJECT_HANDLE = undefined,
+ count: usize = 0,
+ cursor: usize = 0,
+ active: bool = false,
+};
+
+pub const RsaSig = struct {
+ key: ck.CK_OBJECT_HANDLE,
+ params: rsa.SignParams,
+ sig_len: usize,
+};
+
+pub const RsaCrypt = struct {
+ key: ck.CK_OBJECT_HANDLE,
+ params: rsa.CryptParams,
+ out_len: usize,
+};
+
+pub const SignOp = union(enum) {
+ mac: mac.Mac,
+ ec: ecdsa.SignState,
+ rsa: RsaSig,
+
+ pub fn update(self: *SignOp, data: []const u8) void {
+ switch (self.*) {
+ .mac => |*m| m.update(data),
+ .ec => |*e| e.update(data),
+ .rsa => {},
+ }
+ }
+
+ pub fn zeroize(self: *SignOp) void {
+ std.crypto.secureZero(u8, std.mem.asBytes(self));
+ }
+};
+
+pub const VerifyOp = union(enum) {
+ mac: mac.Mac,
+ ec: ecdsa.VerifyState,
+ rsa: RsaSig,
+
+ pub fn update(self: *VerifyOp, data: []const u8) void {
+ switch (self.*) {
+ .mac => |*m| m.update(data),
+ .ec => |*e| e.update(data),
+ .rsa => {},
+ }
+ }
+
+ pub fn zeroize(self: *VerifyOp) void {
+ std.crypto.secureZero(u8, std.mem.asBytes(self));
+ }
+};
+
+pub const EncryptOp = union(enum) {
+ aes: cipher.Cipher,
+ rsa: RsaCrypt,
+
+ pub fn zeroize(self: *EncryptOp) void {
+ std.crypto.secureZero(u8, std.mem.asBytes(self));
+ }
+};
+
+pub const DecryptOp = union(enum) {
+ aes: cipher.Cipher,
+ rsa: RsaCrypt,
+
+ pub fn zeroize(self: *DecryptOp) void {
+ std.crypto.secureZero(u8, std.mem.asBytes(self));
+ }
+};
+
+pub const Session = struct {
+ slot: ck.CK_SLOT_ID,
+ flags: ck.CK_FLAGS,
+ find: Find = .{},
+ digest_op: ?digest.Hasher = null,
+ sign_op: ?SignOp = null,
+ verify_op: ?VerifyOp = null,
+ encrypt_op: ?EncryptOp = null,
+ decrypt_op: ?DecryptOp = null,
+
+ pub fn endDigest(self: *Session) void {
+ if (self.digest_op) |*o| std.crypto.secureZero(u8, std.mem.asBytes(o));
+ self.digest_op = null;
+ }
+
+ pub fn endSign(self: *Session) void {
+ if (self.sign_op) |*o| o.zeroize();
+ self.sign_op = null;
+ }
+
+ pub fn endVerify(self: *Session) void {
+ if (self.verify_op) |*o| o.zeroize();
+ self.verify_op = null;
+ }
+
+ pub fn endEncrypt(self: *Session) void {
+ if (self.encrypt_op) |*o| o.zeroize();
+ self.encrypt_op = null;
+ }
+
+ pub fn endDecrypt(self: *Session) void {
+ if (self.decrypt_op) |*o| o.zeroize();
+ self.decrypt_op = null;
+ }
+};
+
+pub const Table = struct {
+ slots: [config.max_sessions]?Session = @splat(null),
+
+ pub fn open(self: *Table, slot: ck.CK_SLOT_ID, flags: ck.CK_FLAGS) ?ck.CK_SESSION_HANDLE {
+ for (&self.slots, 0..) |*s, i| {
+ if (s.* == null) {
+ std.crypto.secureZero(u8, std.mem.asBytes(s));
+ s.* = .{ .slot = slot, .flags = flags };
+ return @intCast(i + 1);
+ }
+ }
+ return null;
+ }
+
+ pub fn get(self: *Table, h: ck.CK_SESSION_HANDLE) ?*Session {
+ if (h == 0 or h > config.max_sessions) return null;
+ if (self.slots[h - 1]) |*s| return s;
+ return null;
+ }
+
+ pub fn close(self: *Table, h: ck.CK_SESSION_HANDLE) bool {
+ if (h == 0 or h > config.max_sessions) return false;
+ if (self.slots[h - 1] == null) return false;
+ std.crypto.secureZero(u8, std.mem.asBytes(&self.slots[h - 1]));
+ self.slots[h - 1] = null;
+ return true;
+ }
+
+ pub fn closeAll(self: *Table, slot: ck.CK_SLOT_ID) void {
+ for (&self.slots) |*s| {
+ if (s.*) |*sp| {
+ if (sp.slot == slot) {
+ std.crypto.secureZero(u8, std.mem.asBytes(s));
+ s.* = null;
+ }
+ }
+ }
+ }
+
+ pub fn wipeAll(self: *Table) void {
+ std.crypto.secureZero(u8, std.mem.asBytes(&self.slots));
+ }
+
+ pub fn anyOpen(self: *Table) bool {
+ for (&self.slots) |*s| {
+ if (s.* != null) return true;
+ }
+ return false;
+ }
+
+ pub fn count(self: *Table) ck.CK_ULONG {
+ var n: ck.CK_ULONG = 0;
+ for (&self.slots) |*s| {
+ if (s.* != null) n += 1;
+ }
+ return n;
+ }
+
+ pub fn countRw(self: *Table) ck.CK_ULONG {
+ var n: ck.CK_ULONG = 0;
+ for (&self.slots) |*s| {
+ if (s.*) |*sp| {
+ if ((sp.flags & ck.CKF_RW_SESSION) != 0) n += 1;
+ }
+ }
+ return n;
+ }
+};
+
+test "open returns nonzero handles and get resolves them" {
+ var t: Table = .{};
+ const h1 = t.open(0, ck.CKF_SERIAL_SESSION).?;
+ const h2 = t.open(0, ck.CKF_SERIAL_SESSION | ck.CKF_RW_SESSION).?;
+ try std.testing.expect(h1 != 0 and h2 != 0 and h1 != h2);
+ try std.testing.expectEqual(@as(ck.CK_ULONG, 2), t.count());
+ try std.testing.expectEqual(@as(ck.CK_ULONG, 1), t.countRw());
+ try std.testing.expect(t.get(h1) != null);
+ try std.testing.expect(t.get(9999) == null);
+}
+
+test "close frees the slot and closeAll empties the table" {
+ var t: Table = .{};
+ const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
+ try std.testing.expect(t.close(h));
+ try std.testing.expect(!t.close(h));
+ try std.testing.expect(!t.anyOpen());
+ _ = t.open(0, ck.CKF_SERIAL_SESSION);
+ t.closeAll(0);
+ try std.testing.expect(!t.anyOpen());
+}
+
+fn expectAllZero(bytes: []const u8) !void {
+ for (bytes) |b| try std.testing.expectEqual(@as(u8, 0), b);
+}
+
+test "EncryptOp.zeroize zeros the AES key material" {
+ var op: EncryptOp = .{ .aes = .{ .mode = .cbc, .encrypt = true, .key_len = 32 } };
+ const key: []u8 = &op.aes.key_buf;
+ @memset(key, 0xAA);
+ op.zeroize();
+ try expectAllZero(key);
+}
+
+test "DecryptOp.zeroize zeros the AES key material" {
+ var op: DecryptOp = .{ .aes = .{ .mode = .cbc, .encrypt = false, .key_len = 16 } };
+ const key: []u8 = &op.aes.key_buf;
+ @memset(key, 0xAA);
+ op.zeroize();
+ try expectAllZero(key);
+}
+
+test "SignOp.zeroize zeros the EC private scalar" {
+ const scalar = [_]u8{0xAB} ** 32;
+ var op: SignOp = .{ .ec = ecdsa.SignState.init(.p256, ck.CKM_ECDSA, &scalar).? };
+ const sc: []u8 = &op.ec.scalar;
+ op.zeroize();
+ try expectAllZero(sc);
+}
+
+test "VerifyOp.zeroize zeros HMAC key state" {
+ var op: VerifyOp = .{ .mac = undefined };
+ const st: []u8 = std.mem.asBytes(&op.mac);
+ @memset(st, 0xCD);
+ op.zeroize();
+ try expectAllZero(st);
+}
+
+test "endEncrypt clears the op and removes the secret from the slot" {
+ var t: Table = .{};
+ const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
+ const sess = t.get(h).?;
+ sess.encrypt_op = .{ .aes = .{ .mode = .gcm, .encrypt = true, .key_len = 32 } };
+ const key: []u8 = &sess.encrypt_op.?.aes.key_buf;
+ @memset(key, 0x5C);
+ sess.endEncrypt();
+ try std.testing.expect(sess.encrypt_op == null);
+ try std.testing.expect(std.mem.indexOfScalar(u8, key, 0x5C) == null);
+}
+
+test "close removes an active op's secret from the slot" {
+ var t: Table = .{};
+ const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
+ const sess = t.get(h).?;
+ sess.decrypt_op = .{ .aes = .{ .mode = .cbc, .encrypt = false, .key_len = 32 } };
+ const key: []u8 = &sess.decrypt_op.?.aes.key_buf;
+ @memset(key, 0x5C);
+ try std.testing.expect(t.close(h));
+ try std.testing.expect(std.mem.indexOfScalar(u8, key, 0x5C) == null);
+}
+
+test "wipeAll zeros secret material in every slot" {
+ var t: Table = .{};
+ const h = t.open(0, ck.CKF_SERIAL_SESSION).?;
+ const sess = t.get(h).?;
+ sess.sign_op = .{ .mac = undefined };
+ const st: []u8 = std.mem.asBytes(&sess.sign_op.?.mac);
+ @memset(st, 0xEF);
+ t.wipeAll();
+ try expectAllZero(st);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/state.zig b/PROJECTS/advanced/hsm-emulator/src/core/state.zig
new file mode 100644
index 00000000..5a13c0cf
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/state.zig
@@ -0,0 +1,140 @@
+// ©AngelaMos | 2026
+// state.zig
+
+const std = @import("std");
+const builtin = @import("builtin");
+const ck = @import("../ck.zig");
+const lock = @import("lock.zig");
+const token = @import("token.zig");
+const session = @import("session.zig");
+const object_store = @import("object_store.zig");
+const keystore = @import("../crypto/keystore.zig");
+
+pub const Locking = enum { none, os };
+
+pub const Instance = struct {
+ debug_alloc: std.heap.DebugAllocator(.{}) = undefined,
+ threaded: std.Io.Threaded = undefined,
+ locking: Locking = .none,
+ token: token.Token = .{},
+ sessions: session.Table = .{},
+ objects: object_store.Store = .{},
+ logged_in: ?ck.CK_USER_TYPE = null,
+ mk: ?keystore.MasterKey = null,
+
+ pub fn allocator(self: *Instance) std.mem.Allocator {
+ return if (builtin.mode == .Debug) self.debug_alloc.allocator() else std.heap.smp_allocator;
+ }
+
+ pub fn io(self: *Instance) std.Io {
+ return self.threaded.io();
+ }
+
+ pub fn wipeMasterKey(self: *Instance) void {
+ if (self.mk) |*mk| std.crypto.secureZero(u8, mk);
+ self.mk = null;
+ }
+
+ pub fn relock(self: *Instance) void {
+ if (self.mk) |*mk| object_store.lock(self.io(), self.allocator(), &self.objects, mk.*) catch {
+ object_store.scrubUnsealed(&self.objects);
+ };
+ self.wipeMasterKey();
+ }
+};
+
+pub var mutex: lock.Lock = .{};
+var storage: Instance = undefined;
+var present: bool = false;
+var inflight: usize = 0;
+var generation: u64 = 0;
+
+pub fn current() ?*Instance {
+ return if (@atomicLoad(bool, &present, .acquire)) &storage else null;
+}
+
+pub fn isInitialized() bool {
+ return @atomicLoad(bool, &present, .acquire);
+}
+
+pub fn initialize(locking: Locking) void {
+ storage = .{
+ .debug_alloc = .init,
+ .threaded = .init(std.heap.smp_allocator, .{}),
+ .locking = locking,
+ };
+ storage.token = token.load(storage.io(), storage.allocator());
+ object_store.load(storage.io(), storage.allocator(), &storage.objects);
+ @atomicStore(bool, &present, true, .release);
+}
+
+pub fn finalize() ck.CK_RV {
+ mutex.lock();
+ if (!@atomicLoad(bool, &present, .acquire)) {
+ mutex.unlock();
+ return ck.CKR_CRYPTOKI_NOT_INITIALIZED;
+ }
+ @atomicStore(bool, &present, false, .release);
+ mutex.unlock();
+
+ while (true) {
+ mutex.lock();
+ const pending = inflight;
+ mutex.unlock();
+ if (pending == 0) break;
+ std.Thread.yield() catch std.atomic.spinLoopHint();
+ }
+
+ mutex.lock();
+ defer mutex.unlock();
+ storage.sessions.wipeAll();
+ storage.wipeMasterKey();
+ storage.objects.deinit(storage.allocator());
+ storage.threaded.deinit();
+ _ = storage.debug_alloc.deinit();
+ return ck.CKR_OK;
+}
+
+pub fn cryptoBegin() u64 {
+ inflight += 1;
+ return generation;
+}
+
+pub fn cryptoEnd() void {
+ inflight -= 1;
+}
+
+pub fn cryptoAbort() void {
+ mutex.lock();
+ inflight -= 1;
+ mutex.unlock();
+}
+
+pub fn bumpGeneration() void {
+ generation += 1;
+}
+
+pub fn currentGeneration() u64 {
+ return generation;
+}
+
+pub const InitOutcome = union(enum) {
+ ok: Locking,
+ err: ck.CK_RV,
+};
+
+pub fn parseInitArgs(p: ?*anyopaque) InitOutcome {
+ if (p == null) return .{ .ok = .none };
+ const args: *ck.CK_C_INITIALIZE_ARGS = @ptrCast(@alignCast(p.?));
+ if (args.pReserved != null) return .{ .err = ck.CKR_ARGUMENTS_BAD };
+ var cbs: u8 = 0;
+ if (args.CreateMutex != null) cbs += 1;
+ if (args.DestroyMutex != null) cbs += 1;
+ if (args.LockMutex != null) cbs += 1;
+ if (args.UnlockMutex != null) cbs += 1;
+ if (cbs != 0 and cbs != 4) return .{ .err = ck.CKR_ARGUMENTS_BAD };
+ const os_locking_ok = (args.flags & ck.CKF_OS_LOCKING_OK) != 0;
+ if (os_locking_ok) return .{ .ok = .os };
+ if (cbs == 4) return .{ .err = ck.CKR_CANT_LOCK };
+ return .{ .ok = .none };
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/core/token.zig b/PROJECTS/advanced/hsm-emulator/src/core/token.zig
new file mode 100644
index 00000000..641386ca
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/core/token.zig
@@ -0,0 +1,168 @@
+// ©AngelaMos | 2026
+// token.zig
+
+const std = @import("std");
+const config = @import("../config.zig");
+const pin = @import("../crypto/pin.zig");
+const keystore = @import("../crypto/keystore.zig");
+const env = @import("env.zig");
+
+pub const PinSlot = struct {
+ salt: pin.Salt,
+ hash: pin.Hash,
+};
+
+pub const Token = struct {
+ initialized: bool = false,
+ label: [config.label_len]u8 = @splat(' '),
+ so: PinSlot = std.mem.zeroes(PinSlot),
+ user: ?PinSlot = null,
+ so_fail: u32 = 0,
+ user_fail: u32 = 0,
+ user_mk: ?keystore.Wrapped = null,
+};
+
+const flag_initialized: u32 = 1 << 0;
+const flag_user_present: u32 = 1 << 1;
+const flag_user_mk: u32 = 1 << 2;
+
+const Record = extern struct {
+ magic: u32,
+ version: u32,
+ flags: u32,
+ label: [config.label_len]u8,
+ so_salt: [pin.salt_len]u8,
+ so_hash: [pin.hash_len]u8,
+ user_salt: [pin.salt_len]u8,
+ user_hash: [pin.hash_len]u8,
+ so_fail: u32,
+ user_fail: u32,
+ mk_salt: [pin.salt_len]u8,
+ mk_nonce: [keystore.nonce_len]u8,
+ mk_ct: [keystore.mk_len]u8,
+ mk_tag: [keystore.tag_len]u8,
+};
+
+fn serialize(t: Token) Record {
+ var r = std.mem.zeroes(Record);
+ r.magic = config.token_record_magic;
+ r.version = config.token_record_version;
+ r.flags = (if (t.initialized) flag_initialized else 0) | (if (t.user != null) flag_user_present else 0) | (if (t.user_mk != null) flag_user_mk else 0);
+ r.label = t.label;
+ r.so_salt = t.so.salt;
+ r.so_hash = t.so.hash;
+ if (t.user) |u| {
+ r.user_salt = u.salt;
+ r.user_hash = u.hash;
+ }
+ if (t.user_mk) |w| {
+ r.mk_salt = w.salt;
+ r.mk_nonce = w.nonce;
+ r.mk_ct = w.ct;
+ r.mk_tag = w.tag;
+ }
+ r.so_fail = t.so_fail;
+ r.user_fail = t.user_fail;
+ return r;
+}
+
+fn deserialize(r: *const Record) Token {
+ return .{
+ .initialized = (r.flags & flag_initialized) != 0,
+ .label = r.label,
+ .so = .{ .salt = r.so_salt, .hash = r.so_hash },
+ .user = if ((r.flags & flag_user_present) != 0) PinSlot{ .salt = r.user_salt, .hash = r.user_hash } else null,
+ .so_fail = r.so_fail,
+ .user_fail = r.user_fail,
+ .user_mk = if ((r.flags & flag_user_mk) != 0) keystore.Wrapped{ .salt = r.mk_salt, .nonce = r.mk_nonce, .ct = r.mk_ct, .tag = r.mk_tag } else null,
+ };
+}
+
+pub fn resolvePath(buf: []u8) ![]const u8 {
+ return env.resolvePath(buf, config.token_path_env, config.token_path_default);
+}
+
+pub fn saveTo(io: std.Io, path: []const u8, t: Token) !void {
+ const rec = serialize(t);
+ var tmp_buf: [config.path_buf_len + 8]u8 = undefined;
+ const tmp = try std.fmt.bufPrint(&tmp_buf, "{s}.tmp", .{path});
+ const dir = std.Io.Dir.cwd();
+ try dir.writeFile(io, .{ .sub_path = tmp, .data = std.mem.asBytes(&rec) });
+ try dir.rename(tmp, dir, path, io);
+}
+
+pub fn loadFrom(io: std.Io, allocator: std.mem.Allocator, path: []const u8) Token {
+ const bytes = std.Io.Dir.cwd().readFileAlloc(io, path, allocator, .limited(config.token_read_limit)) catch return .{};
+ defer allocator.free(bytes);
+ if (bytes.len != @sizeOf(Record)) return .{};
+ var rec: Record = undefined;
+ @memcpy(std.mem.asBytes(&rec), bytes[0..@sizeOf(Record)]);
+ if (rec.magic != config.token_record_magic or rec.version != config.token_record_version) return .{};
+ return deserialize(&rec);
+}
+
+pub fn save(io: std.Io, t: Token) !void {
+ var buf: [config.path_buf_len]u8 = undefined;
+ const path = try resolvePath(&buf);
+ try saveTo(io, path, t);
+}
+
+pub fn load(io: std.Io, allocator: std.mem.Allocator) Token {
+ var buf: [config.path_buf_len]u8 = undefined;
+ const path = resolvePath(&buf) catch return .{};
+ return loadFrom(io, allocator, path);
+}
+
+test "serialize then deserialize round-trips an initialized token with a user PIN" {
+ var t: Token = .{ .initialized = true, .so_fail = 2, .user_fail = 1 };
+ t.label = @splat('X');
+ t.so = .{ .salt = @splat(3), .hash = @splat(4) };
+ t.user = .{ .salt = @splat(5), .hash = @splat(6) };
+
+ const rec = serialize(t);
+ const back = deserialize(&rec);
+
+ try std.testing.expect(back.initialized);
+ try std.testing.expectEqualSlices(u8, &t.label, &back.label);
+ try std.testing.expectEqual(t.so.salt, back.so.salt);
+ try std.testing.expectEqual(t.so.hash, back.so.hash);
+ try std.testing.expect(back.user != null);
+ try std.testing.expectEqual(t.user.?.hash, back.user.?.hash);
+ try std.testing.expectEqual(@as(u32, 2), back.so_fail);
+ try std.testing.expectEqual(@as(u32, 1), back.user_fail);
+}
+
+test "an uninitialized token has no user PIN after a round-trip" {
+ const rec = serialize(.{});
+ const back = deserialize(&rec);
+ try std.testing.expect(!back.initialized);
+ try std.testing.expect(back.user == null);
+}
+
+test "saveTo then loadFrom persists across a file" {
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ defer threaded.deinit();
+ const io = threaded.io();
+
+ const path = "/tmp/angelamos-hsm-unit-token.bin";
+ std.Io.Dir.cwd().deleteFile(io, path) catch {};
+ defer std.Io.Dir.cwd().deleteFile(io, path) catch {};
+
+ var t: Token = .{ .initialized = true };
+ t.label = @splat('Z');
+ t.so = .{ .salt = @splat(9), .hash = @splat(8) };
+
+ try saveTo(io, path, t);
+ const back = loadFrom(io, std.testing.allocator, path);
+ try std.testing.expect(back.initialized);
+ try std.testing.expectEqual(t.so.hash, back.so.hash);
+ try std.testing.expect(back.user == null);
+}
+
+test "loadFrom a missing file yields a default uninitialized token" {
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ defer threaded.deinit();
+ const io = threaded.io();
+ const back = loadFrom(io, std.testing.allocator, "/tmp/angelamos-hsm-does-not-exist.bin");
+ try std.testing.expect(!back.initialized);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/cipher.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/cipher.zig
new file mode 100644
index 00000000..375ade2e
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/cipher.zig
@@ -0,0 +1,302 @@
+// ©AngelaMos | 2026
+// cipher.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+
+const aes = std.crypto.core.aes;
+const aesgcm = std.crypto.aead.aes_gcm;
+
+const block = config.aes_block_len;
+
+pub const Mode = enum { cbc, cbc_pad, gcm };
+
+pub const Error = error{
+ KeySize,
+ DataLenRange,
+ EncryptedDataLenRange,
+ EncryptedDataInvalid,
+ AadTooLarge,
+ IvInvalid,
+};
+
+pub fn modeOf(mech: ck.CK_MECHANISM_TYPE) ?Mode {
+ return switch (mech) {
+ ck.CKM_AES_CBC => .cbc,
+ ck.CKM_AES_CBC_PAD => .cbc_pad,
+ ck.CKM_AES_GCM => .gcm,
+ else => null,
+ };
+}
+
+pub fn validKeyLen(len: usize) bool {
+ return len == config.aes_min_key_bytes or len == config.aes_max_key_bytes;
+}
+
+fn encBlockRaw(key: []const u8, in: *const [block]u8, out: *[block]u8) void {
+ switch (key.len) {
+ 16 => aes.Aes128.initEnc(key[0..16].*).encrypt(out, in),
+ 32 => aes.Aes256.initEnc(key[0..32].*).encrypt(out, in),
+ else => unreachable,
+ }
+}
+
+fn decBlockRaw(key: []const u8, in: *const [block]u8, out: *[block]u8) void {
+ switch (key.len) {
+ 16 => aes.Aes128.initDec(key[0..16].*).decrypt(out, in),
+ 32 => aes.Aes256.initDec(key[0..32].*).decrypt(out, in),
+ else => unreachable,
+ }
+}
+
+pub const Cipher = struct {
+ mode: Mode,
+ encrypt: bool,
+ key_buf: [32]u8 = @splat(0),
+ key_len: u8 = 0,
+ chain: [block]u8 = @splat(0),
+ partial: [block]u8 = @splat(0),
+ partial_len: u8 = 0,
+ held: [block]u8 = @splat(0),
+ has_held: bool = false,
+ iv: [config.gcm_iv_len]u8 = @splat(0),
+ aad_buf: [config.max_gcm_aad_len]u8 = @splat(0),
+ aad_len: usize = 0,
+
+ fn key(self: *const Cipher) []const u8 {
+ return self.key_buf[0..self.key_len];
+ }
+
+ fn cbcEncStep(self: *Cipher, in16: *const [block]u8, out16: *[block]u8) void {
+ var x: [block]u8 = undefined;
+ for (0..block) |j| x[j] = in16[j] ^ self.chain[j];
+ encBlockRaw(self.key(), &x, out16);
+ self.chain = out16.*;
+ }
+
+ fn cbcDecStep(self: *Cipher, in16: *const [block]u8, out16: *[block]u8) void {
+ var d: [block]u8 = undefined;
+ decBlockRaw(self.key(), in16, &d);
+ for (0..block) |j| out16[j] = d[j] ^ self.chain[j];
+ self.chain = in16.*;
+ }
+
+ pub fn encryptUpdate(self: *Cipher, input: []const u8, out: []u8) usize {
+ var o: usize = 0;
+ var i: usize = 0;
+ while (i < input.len) {
+ const take = @min(block - self.partial_len, input.len - i);
+ @memcpy(self.partial[self.partial_len..][0..take], input[i .. i + take]);
+ self.partial_len += @intCast(take);
+ i += take;
+ if (self.partial_len == block) {
+ self.cbcEncStep(&self.partial, out[o..][0..block]);
+ o += block;
+ self.partial_len = 0;
+ }
+ }
+ return o;
+ }
+
+ pub fn encryptFinal(self: *Cipher, out: []u8) Error!usize {
+ if (self.mode == .cbc) {
+ if (self.partial_len != 0) return Error.DataLenRange;
+ return 0;
+ }
+ const padlen: u8 = @intCast(block - self.partial_len);
+ for (self.partial_len..block) |j| self.partial[j] = padlen;
+ self.cbcEncStep(&self.partial, out[0..block]);
+ self.partial_len = 0;
+ return block;
+ }
+
+ pub fn decryptUpdate(self: *Cipher, input: []const u8, out: []u8) usize {
+ var o: usize = 0;
+ var i: usize = 0;
+ while (i < input.len) {
+ const take = @min(block - self.partial_len, input.len - i);
+ @memcpy(self.partial[self.partial_len..][0..take], input[i .. i + take]);
+ self.partial_len += @intCast(take);
+ i += take;
+ if (self.partial_len == block) {
+ if (self.mode == .cbc_pad) {
+ if (self.has_held) {
+ self.cbcDecStep(&self.held, out[o..][0..block]);
+ o += block;
+ }
+ self.held = self.partial;
+ self.has_held = true;
+ } else {
+ self.cbcDecStep(&self.partial, out[o..][0..block]);
+ o += block;
+ }
+ self.partial_len = 0;
+ }
+ }
+ return o;
+ }
+
+ pub fn decryptFinal(self: *Cipher, out: []u8) Error!usize {
+ if (self.partial_len != 0) return Error.EncryptedDataLenRange;
+ if (self.mode == .cbc) return 0;
+ if (!self.has_held) return Error.EncryptedDataLenRange;
+ var pt: [block]u8 = undefined;
+ self.cbcDecStep(&self.held, &pt);
+ self.has_held = false;
+ const padlen = pt[block - 1];
+ if (padlen == 0 or padlen > block) return Error.EncryptedDataInvalid;
+ var bad: u8 = 0;
+ for (0..block) |j| {
+ const is_pad = j >= block - padlen;
+ if (is_pad) bad |= pt[j] ^ padlen;
+ }
+ if (bad != 0) return Error.EncryptedDataInvalid;
+ const keep = block - padlen;
+ @memcpy(out[0..keep], pt[0..keep]);
+ return keep;
+ }
+
+ pub fn gcmEncrypt(self: *Cipher, input: []const u8, out: []u8) usize {
+ std.debug.assert(out.len >= input.len + config.gcm_tag_len);
+ const tag: *[config.gcm_tag_len]u8 = out[input.len..][0..config.gcm_tag_len];
+ const ad = self.aad_buf[0..self.aad_len];
+ switch (self.key_len) {
+ 16 => aesgcm.Aes128Gcm.encrypt(out[0..input.len], tag, input, ad, self.iv, self.key_buf[0..16].*),
+ 32 => aesgcm.Aes256Gcm.encrypt(out[0..input.len], tag, input, ad, self.iv, self.key_buf[0..32].*),
+ else => unreachable,
+ }
+ return input.len + config.gcm_tag_len;
+ }
+
+ pub fn gcmDecrypt(self: *Cipher, input: []const u8, out: []u8) Error!usize {
+ if (input.len < config.gcm_tag_len) return Error.EncryptedDataLenRange;
+ const ct_len = input.len - config.gcm_tag_len;
+ std.debug.assert(out.len >= ct_len);
+ const tag: [config.gcm_tag_len]u8 = input[ct_len..][0..config.gcm_tag_len].*;
+ const ad = self.aad_buf[0..self.aad_len];
+ switch (self.key_len) {
+ 16 => aesgcm.Aes128Gcm.decrypt(out[0..ct_len], input[0..ct_len], tag, ad, self.iv, self.key_buf[0..16].*) catch return Error.EncryptedDataInvalid,
+ 32 => aesgcm.Aes256Gcm.decrypt(out[0..ct_len], input[0..ct_len], tag, ad, self.iv, self.key_buf[0..32].*) catch return Error.EncryptedDataInvalid,
+ else => unreachable,
+ }
+ return ct_len;
+ }
+};
+
+pub fn encryptOutLen(mode: Mode, in_len: usize) usize {
+ return switch (mode) {
+ .cbc => in_len,
+ .cbc_pad => (in_len / block + 1) * block,
+ .gcm => in_len + config.gcm_tag_len,
+ };
+}
+
+pub fn decryptOutLen(mode: Mode, in_len: usize) usize {
+ return switch (mode) {
+ .cbc, .cbc_pad => in_len,
+ .gcm => if (in_len >= config.gcm_tag_len) in_len - config.gcm_tag_len else 0,
+ };
+}
+
+fn testKey() [32]u8 {
+ var k: [32]u8 = undefined;
+ for (0..32) |j| k[j] = @intCast(j);
+ return k;
+}
+
+test "AES-256-CBC single-block matches a NIST SP800-38A vector" {
+ const key = [_]u8{
+ 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
+ };
+ const iv = [_]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+ const pt = [_]u8{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a };
+ const expect = [_]u8{ 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6 };
+
+ var c: Cipher = .{ .mode = .cbc, .encrypt = true, .key_len = 32 };
+ c.key_buf = key;
+ c.chain = iv;
+ var out: [16]u8 = undefined;
+ const n = c.encryptUpdate(&pt, &out);
+ try std.testing.expectEqual(@as(usize, 16), n);
+ try std.testing.expectEqualSlices(u8, &expect, &out);
+}
+
+test "CBC-PAD round-trips arbitrary lengths" {
+ const key = testKey();
+ const iv = [_]u8{1} ** 16;
+ for ([_]usize{ 0, 1, 15, 16, 17, 100 }) |len| {
+ var pt: [100]u8 = undefined;
+ for (0..len) |j| pt[j] = @intCast((j * 7) & 0xff);
+
+ var enc: Cipher = .{ .mode = .cbc_pad, .encrypt = true, .key_len = 32 };
+ enc.key_buf = key;
+ enc.chain = iv;
+ var ct: [128]u8 = undefined;
+ var cn = enc.encryptUpdate(pt[0..len], &ct);
+ cn += try enc.encryptFinal(ct[cn..]);
+ try std.testing.expectEqual(encryptOutLen(.cbc_pad, len), cn);
+
+ var dec: Cipher = .{ .mode = .cbc_pad, .encrypt = false, .key_len = 32 };
+ dec.key_buf = key;
+ dec.chain = iv;
+ var back: [128]u8 = undefined;
+ var bn = dec.decryptUpdate(ct[0..cn], &back);
+ bn += try dec.decryptFinal(back[bn..]);
+ try std.testing.expectEqual(len, bn);
+ try std.testing.expectEqualSlices(u8, pt[0..len], back[0..bn]);
+ }
+}
+
+test "CBC-PAD streaming in small chunks equals one-shot" {
+ const key = testKey();
+ const iv = [_]u8{2} ** 16;
+ var pt: [70]u8 = undefined;
+ for (0..70) |j| pt[j] = @intCast(j);
+
+ var enc: Cipher = .{ .mode = .cbc_pad, .encrypt = true, .key_len = 16 };
+ enc.key_buf = key;
+ enc.chain = iv;
+ var ct: [96]u8 = undefined;
+ var cn = enc.encryptUpdate(&pt, &ct);
+ cn += try enc.encryptFinal(ct[cn..]);
+
+ var dec: Cipher = .{ .mode = .cbc_pad, .encrypt = false, .key_len = 16 };
+ dec.key_buf = key;
+ dec.chain = iv;
+ var back: [96]u8 = undefined;
+ var bn: usize = 0;
+ var i: usize = 0;
+ while (i < cn) : (i += 7) {
+ const end = @min(i + 7, cn);
+ bn += dec.decryptUpdate(ct[i..end], back[bn..]);
+ }
+ bn += try dec.decryptFinal(back[bn..]);
+ try std.testing.expectEqual(@as(usize, 70), bn);
+ try std.testing.expectEqualSlices(u8, &pt, back[0..bn]);
+}
+
+test "GCM round-trips and rejects a tampered tag" {
+ const key = testKey();
+ var c: Cipher = .{ .mode = .gcm, .encrypt = true, .key_len = 32 };
+ c.key_buf = key;
+ c.iv = [_]u8{7} ** 12;
+ const pt = "authenticated secret";
+ var ct: [64]u8 = undefined;
+ const cn = c.gcmEncrypt(pt, &ct);
+ try std.testing.expectEqual(pt.len + 16, cn);
+
+ var d: Cipher = .{ .mode = .gcm, .encrypt = false, .key_len = 32 };
+ d.key_buf = key;
+ d.iv = [_]u8{7} ** 12;
+ var back: [64]u8 = undefined;
+ const bn = try d.gcmDecrypt(ct[0..cn], &back);
+ try std.testing.expectEqualSlices(u8, pt, back[0..bn]);
+
+ ct[0] ^= 0x01;
+ var d2: Cipher = .{ .mode = .gcm, .encrypt = false, .key_len = 32 };
+ d2.key_buf = key;
+ d2.iv = [_]u8{7} ** 12;
+ try std.testing.expectError(Error.EncryptedDataInvalid, d2.gcmDecrypt(ct[0..cn], &back));
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/digest.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/digest.zig
new file mode 100644
index 00000000..f16a96c1
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/digest.zig
@@ -0,0 +1,89 @@
+// ©AngelaMos | 2026
+// digest.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+
+const sha2 = std.crypto.hash.sha2;
+
+pub const max_digest_len = sha2.Sha512.digest_length;
+
+pub const Hasher = union(enum) {
+ sha256: sha2.Sha256,
+ sha384: sha2.Sha384,
+ sha512: sha2.Sha512,
+
+ pub fn init(mech: ck.CK_MECHANISM_TYPE) ?Hasher {
+ return switch (mech) {
+ ck.CKM_SHA256 => .{ .sha256 = sha2.Sha256.init(.{}) },
+ ck.CKM_SHA384 => .{ .sha384 = sha2.Sha384.init(.{}) },
+ ck.CKM_SHA512 => .{ .sha512 = sha2.Sha512.init(.{}) },
+ else => null,
+ };
+ }
+
+ pub fn update(self: *Hasher, data: []const u8) void {
+ switch (self.*) {
+ inline else => |*h| h.update(data),
+ }
+ }
+
+ pub fn digestLen(self: *const Hasher) usize {
+ return switch (self.*) {
+ inline else => |h| @TypeOf(h).digest_length,
+ };
+ }
+
+ pub fn finalInto(self: *Hasher, out: []u8) void {
+ switch (self.*) {
+ inline else => |*h| {
+ const Hash = @TypeOf(h.*);
+ h.final(out[0..Hash.digest_length]);
+ },
+ }
+ }
+};
+
+pub fn digestLenOf(mech: ck.CK_MECHANISM_TYPE) ?usize {
+ return switch (mech) {
+ ck.CKM_SHA256 => sha2.Sha256.digest_length,
+ ck.CKM_SHA384 => sha2.Sha384.digest_length,
+ ck.CKM_SHA512 => sha2.Sha512.digest_length,
+ else => null,
+ };
+}
+
+test "one-shot digest matches a known SHA-256 vector" {
+ var h = Hasher.init(ck.CKM_SHA256).?;
+ h.update("abc");
+ var out: [max_digest_len]u8 = undefined;
+ h.finalInto(&out);
+ const expect = [_]u8{
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad,
+ };
+ try std.testing.expectEqual(@as(usize, 32), h.digestLen());
+ try std.testing.expectEqualSlices(u8, &expect, out[0..32]);
+}
+
+test "multi-part digest equals single-part" {
+ var a = Hasher.init(ck.CKM_SHA512).?;
+ a.update("hello world");
+ var oa: [max_digest_len]u8 = undefined;
+ a.finalInto(&oa);
+
+ var b = Hasher.init(ck.CKM_SHA512).?;
+ b.update("hello ");
+ b.update("world");
+ var ob: [max_digest_len]u8 = undefined;
+ b.finalInto(&ob);
+
+ try std.testing.expectEqualSlices(u8, oa[0..64], ob[0..64]);
+}
+
+test "unknown mechanism yields null" {
+ try std.testing.expect(Hasher.init(ck.CKM_AES_CBC) == null);
+ try std.testing.expect(digestLenOf(ck.CKM_SHA384).? == 48);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/ecdsa.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/ecdsa.zig
new file mode 100644
index 00000000..882d702c
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/ecdsa.zig
@@ -0,0 +1,420 @@
+// ©AngelaMos | 2026
+// ecdsa.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const digest = @import("digest.zig");
+
+const P256 = std.crypto.sign.ecdsa.EcdsaP256Sha256;
+const P384 = std.crypto.sign.ecdsa.EcdsaP384Sha384;
+
+pub const max_scalar = P384.SecretKey.encoded_length;
+pub const max_point = P384.PublicKey.uncompressed_sec1_encoded_length;
+pub const max_sig = P384.Signature.encoded_length;
+pub const max_prehash = digest.max_digest_len;
+pub const max_ec_point_der = 2 + max_point;
+
+const der_octet_string: u8 = 0x04;
+const der_long_form_bit: u8 = 0x80;
+
+const oid_p256 = [_]u8{ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 };
+const oid_p384 = [_]u8{ 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 };
+
+pub const Error = error{
+ Random,
+ Generate,
+ Crypto,
+};
+
+pub const Curve = enum {
+ p256,
+ p384,
+
+ pub fn scalarLen(self: Curve) usize {
+ return switch (self) {
+ .p256 => P256.SecretKey.encoded_length,
+ .p384 => P384.SecretKey.encoded_length,
+ };
+ }
+
+ pub fn pointLen(self: Curve) usize {
+ return switch (self) {
+ .p256 => P256.PublicKey.uncompressed_sec1_encoded_length,
+ .p384 => P384.PublicKey.uncompressed_sec1_encoded_length,
+ };
+ }
+
+ pub fn sigLen(self: Curve) usize {
+ return switch (self) {
+ .p256 => P256.Signature.encoded_length,
+ .p384 => P384.Signature.encoded_length,
+ };
+ }
+
+ pub fn bits(self: Curve) ck.CK_ULONG {
+ return switch (self) {
+ .p256 => config.ec_min_key_bits,
+ .p384 => config.ec_max_key_bits,
+ };
+ }
+
+ pub fn oidDer(self: Curve) []const u8 {
+ return switch (self) {
+ .p256 => &oid_p256,
+ .p384 => &oid_p384,
+ };
+ }
+};
+
+pub const HashMode = enum { raw, sha256 };
+
+pub fn hashModeOf(mech: ck.CK_MECHANISM_TYPE) ?HashMode {
+ return switch (mech) {
+ ck.CKM_ECDSA => .raw,
+ ck.CKM_ECDSA_SHA256 => .sha256,
+ else => null,
+ };
+}
+
+pub fn curveFromParams(ec_params: []const u8) ?Curve {
+ if (std.mem.eql(u8, ec_params, &oid_p256)) return .p256;
+ if (std.mem.eql(u8, ec_params, &oid_p384)) return .p384;
+ return null;
+}
+
+pub fn wrapEcPoint(out: []u8, sec1: []const u8) []u8 {
+ out[0] = der_octet_string;
+ out[1] = @intCast(sec1.len);
+ @memcpy(out[2..][0..sec1.len], sec1);
+ return out[0 .. 2 + sec1.len];
+}
+
+pub fn unwrapEcPoint(der: []const u8) ?[]const u8 {
+ if (der.len < 2 or der[0] != der_octet_string) return null;
+ if (der[1] & der_long_form_bit == 0) {
+ const len: usize = der[1];
+ if (2 + len != der.len) return null;
+ return der[2 .. 2 + len];
+ }
+ const nlen: usize = der[1] & ~der_long_form_bit;
+ if (nlen == 0 or nlen > 2 or der.len < 2 + nlen) return null;
+ var len: usize = 0;
+ for (der[2 .. 2 + nlen]) |b| len = (len << 8) | b;
+ if (2 + nlen + len != der.len) return null;
+ return der[2 + nlen .. 2 + nlen + len];
+}
+
+pub const KeyMaterial = struct {
+ curve: Curve,
+ scalar: [max_scalar]u8 = @splat(0),
+ point: [max_point]u8 = @splat(0),
+
+ pub fn scalarBytes(self: *const KeyMaterial) []const u8 {
+ return self.scalar[0..self.curve.scalarLen()];
+ }
+
+ pub fn pointBytes(self: *const KeyMaterial) []const u8 {
+ return self.point[0..self.curve.pointLen()];
+ }
+};
+
+pub fn generate(io: std.Io, curve: Curve) Error!KeyMaterial {
+ return switch (curve) {
+ .p256 => generateImpl(P256, io, curve),
+ .p384 => generateImpl(P384, io, curve),
+ };
+}
+
+fn generateImpl(comptime Scheme: type, io: std.Io, curve: Curve) Error!KeyMaterial {
+ var attempt: usize = 0;
+ while (attempt < config.ec_keygen_max_attempts) : (attempt += 1) {
+ var seed: [Scheme.KeyPair.seed_length]u8 = undefined;
+ defer std.crypto.secureZero(u8, &seed);
+ io.randomSecure(&seed) catch return Error.Random;
+ const kp = Scheme.KeyPair.generateDeterministic(seed) catch continue;
+ var km: KeyMaterial = .{ .curve = curve };
+ const sk = kp.secret_key.toBytes();
+ @memcpy(km.scalar[0..sk.len], &sk);
+ const pt = kp.public_key.toUncompressedSec1();
+ @memcpy(km.point[0..pt.len], &pt);
+ return km;
+ }
+ return Error.Generate;
+}
+
+fn reduce(curve: Curve, dgst: []const u8, out: *[max_scalar]u8) []const u8 {
+ const n = curve.scalarLen();
+ @memset(out[0..n], 0);
+ if (dgst.len >= n) {
+ @memcpy(out[0..n], dgst[0..n]);
+ } else {
+ @memcpy(out[n - dgst.len .. n], dgst);
+ }
+ return out[0..n];
+}
+
+const Accum = struct {
+ mode: HashMode,
+ hasher: ?digest.Hasher = null,
+ raw: [max_prehash]u8 = @splat(0),
+ raw_len: usize = 0,
+
+ fn init(mode: HashMode) Accum {
+ return .{
+ .mode = mode,
+ .hasher = if (mode == .sha256) digest.Hasher.init(ck.CKM_SHA256) else null,
+ };
+ }
+
+ fn update(self: *Accum, data: []const u8) void {
+ switch (self.mode) {
+ .sha256 => self.hasher.?.update(data),
+ .raw => {
+ const take = @min(max_prehash - self.raw_len, data.len);
+ @memcpy(self.raw[self.raw_len..][0..take], data[0..take]);
+ self.raw_len += take;
+ },
+ }
+ }
+
+ fn digestBytes(self: *Accum, buf: *[max_prehash]u8) []const u8 {
+ switch (self.mode) {
+ .raw => return self.raw[0..self.raw_len],
+ .sha256 => {
+ const dlen = self.hasher.?.digestLen();
+ self.hasher.?.finalInto(buf[0..dlen]);
+ return buf[0..dlen];
+ },
+ }
+ }
+};
+
+pub const SignState = struct {
+ curve: Curve,
+ scalar: [max_scalar]u8 = @splat(0),
+ acc: Accum,
+
+ pub fn init(curve: Curve, mech: ck.CK_MECHANISM_TYPE, scalar: []const u8) ?SignState {
+ const mode = hashModeOf(mech) orelse return null;
+ if (scalar.len != curve.scalarLen()) return null;
+ var st: SignState = .{ .curve = curve, .acc = Accum.init(mode) };
+ @memcpy(st.scalar[0..scalar.len], scalar);
+ return st;
+ }
+
+ pub fn update(self: *SignState, data: []const u8) void {
+ self.acc.update(data);
+ }
+
+ pub fn sigLen(self: *const SignState) usize {
+ return self.curve.sigLen();
+ }
+
+ pub fn finalInto(self: *SignState, io: std.Io, out: []u8) Error!usize {
+ var dbuf: [max_prehash]u8 = undefined;
+ const dgst = self.acc.digestBytes(&dbuf);
+ var phbuf: [max_scalar]u8 = undefined;
+ const prehash = reduce(self.curve, dgst, &phbuf);
+ return switch (self.curve) {
+ .p256 => signImpl(P256, self.scalar[0..P256.SecretKey.encoded_length], prehash, io, out),
+ .p384 => signImpl(P384, self.scalar[0..P384.SecretKey.encoded_length], prehash, io, out),
+ };
+ }
+};
+
+fn signImpl(comptime Scheme: type, scalar: []const u8, prehash: []const u8, io: std.Io, out: []u8) Error!usize {
+ const slen = Scheme.SecretKey.encoded_length;
+ const siglen = Scheme.Signature.encoded_length;
+
+ var sk: [slen]u8 = undefined;
+ defer std.crypto.secureZero(u8, &sk);
+ @memcpy(&sk, scalar[0..slen]);
+ const kp = Scheme.KeyPair.fromSecretKey(.{ .bytes = sk }) catch return Error.Crypto;
+
+ var ph: [slen]u8 = undefined;
+ @memcpy(&ph, prehash[0..slen]);
+
+ var noise: [slen]u8 = undefined;
+ defer std.crypto.secureZero(u8, &noise);
+ const nz: ?[slen]u8 = if (io.randomSecure(&noise)) |_| noise else |_| null;
+
+ const sig = kp.signPrehashed(ph, nz) catch return Error.Crypto;
+ const raw = sig.toBytes();
+ @memcpy(out[0..siglen], &raw);
+ return siglen;
+}
+
+pub const VerifyResult = enum { ok, invalid, len_range };
+
+pub const VerifyState = struct {
+ curve: Curve,
+ point: [max_point]u8 = @splat(0),
+ acc: Accum,
+
+ pub fn init(curve: Curve, mech: ck.CK_MECHANISM_TYPE, point_sec1: []const u8) ?VerifyState {
+ const mode = hashModeOf(mech) orelse return null;
+ if (point_sec1.len != curve.pointLen()) return null;
+ if (!validPoint(curve, point_sec1)) return null;
+ var st: VerifyState = .{ .curve = curve, .acc = Accum.init(mode) };
+ @memcpy(st.point[0..point_sec1.len], point_sec1);
+ return st;
+ }
+
+ pub fn update(self: *VerifyState, data: []const u8) void {
+ self.acc.update(data);
+ }
+
+ pub fn finalVerify(self: *VerifyState, sig: []const u8) VerifyResult {
+ if (sig.len != self.curve.sigLen()) return .len_range;
+ var dbuf: [max_prehash]u8 = undefined;
+ const dgst = self.acc.digestBytes(&dbuf);
+ var phbuf: [max_scalar]u8 = undefined;
+ const prehash = reduce(self.curve, dgst, &phbuf);
+ const point = self.point[0..self.curve.pointLen()];
+ return switch (self.curve) {
+ .p256 => verifyImpl(P256, point, prehash, sig),
+ .p384 => verifyImpl(P384, point, prehash, sig),
+ };
+ }
+};
+
+fn validPoint(curve: Curve, point_sec1: []const u8) bool {
+ switch (curve) {
+ .p256 => {
+ _ = P256.PublicKey.fromSec1(point_sec1) catch return false;
+ },
+ .p384 => {
+ _ = P384.PublicKey.fromSec1(point_sec1) catch return false;
+ },
+ }
+ return true;
+}
+
+fn verifyImpl(comptime Scheme: type, point: []const u8, prehash: []const u8, sig: []const u8) VerifyResult {
+ const slen = Scheme.SecretKey.encoded_length;
+ const siglen = Scheme.Signature.encoded_length;
+
+ const pk = Scheme.PublicKey.fromSec1(point) catch return .invalid;
+
+ var sb: [siglen]u8 = undefined;
+ @memcpy(&sb, sig[0..siglen]);
+ const signature = Scheme.Signature.fromBytes(sb);
+
+ var ph: [slen]u8 = undefined;
+ @memcpy(&ph, prehash[0..slen]);
+
+ signature.verifyPrehashed(ph, pk) catch return .invalid;
+ return .ok;
+}
+
+fn hexToBytes(comptime hex: []const u8) [hex.len / 2]u8 {
+ var out: [hex.len / 2]u8 = undefined;
+ _ = std.fmt.hexToBytes(&out, hex) catch unreachable;
+ return out;
+}
+
+test "RFC 6979 P-256/SHA-256 sample vector verifies and rejects tampering" {
+ const ux = hexToBytes("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6");
+ const uy = hexToBytes("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299");
+ const point = [_]u8{0x04} ++ ux ++ uy;
+ var sig = hexToBytes("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716") ++
+ hexToBytes("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
+
+ var v = VerifyState.init(.p256, ck.CKM_ECDSA_SHA256, &point).?;
+ v.update("sample");
+ try std.testing.expectEqual(VerifyResult.ok, v.finalVerify(&sig));
+
+ sig[0] ^= 0x01;
+ var v2 = VerifyState.init(.p256, ck.CKM_ECDSA_SHA256, &point).?;
+ v2.update("sample");
+ try std.testing.expectEqual(VerifyResult.invalid, v2.finalVerify(&sig));
+}
+
+test "P-256 generate then hash-then-sign round-trips and detects tamper" {
+ const io = std.testing.io;
+ const km = try generate(io, .p256);
+
+ var s = SignState.init(.p256, ck.CKM_ECDSA_SHA256, km.scalarBytes()).?;
+ s.update("attack at dawn");
+ var sig: [max_sig]u8 = undefined;
+ const n = try s.finalInto(io, &sig);
+ try std.testing.expectEqual(@as(usize, 64), n);
+
+ var v = VerifyState.init(.p256, ck.CKM_ECDSA_SHA256, km.pointBytes()).?;
+ v.update("attack at dawn");
+ try std.testing.expectEqual(VerifyResult.ok, v.finalVerify(sig[0..n]));
+
+ var v2 = VerifyState.init(.p256, ck.CKM_ECDSA_SHA256, km.pointBytes()).?;
+ v2.update("attack at dusk");
+ try std.testing.expectEqual(VerifyResult.invalid, v2.finalVerify(sig[0..n]));
+}
+
+test "P-256 raw prehash signing round-trips" {
+ const io = std.testing.io;
+ const km = try generate(io, .p256);
+ const hash = [_]u8{0xab} ** 32;
+
+ var s = SignState.init(.p256, ck.CKM_ECDSA, km.scalarBytes()).?;
+ s.update(&hash);
+ var sig: [max_sig]u8 = undefined;
+ const n = try s.finalInto(io, &sig);
+
+ var v = VerifyState.init(.p256, ck.CKM_ECDSA, km.pointBytes()).?;
+ v.update(&hash);
+ try std.testing.expectEqual(VerifyResult.ok, v.finalVerify(sig[0..n]));
+}
+
+test "P-384 generate sign verify round-trips with correct sizes" {
+ const io = std.testing.io;
+ const km = try generate(io, .p384);
+ try std.testing.expectEqual(@as(usize, 48), km.scalarBytes().len);
+ try std.testing.expectEqual(@as(usize, 97), km.pointBytes().len);
+
+ var s = SignState.init(.p384, ck.CKM_ECDSA_SHA256, km.scalarBytes()).?;
+ s.update("p384 message");
+ var sig: [max_sig]u8 = undefined;
+ const n = try s.finalInto(io, &sig);
+ try std.testing.expectEqual(@as(usize, 96), n);
+
+ var v = VerifyState.init(.p384, ck.CKM_ECDSA_SHA256, km.pointBytes()).?;
+ v.update("p384 message");
+ try std.testing.expectEqual(VerifyResult.ok, v.finalVerify(sig[0..n]));
+}
+
+test "P-384 raw prehash signing round-trips" {
+ const io = std.testing.io;
+ const km = try generate(io, .p384);
+ const hash = [_]u8{0xcd} ** 48;
+
+ var s = SignState.init(.p384, ck.CKM_ECDSA, km.scalarBytes()).?;
+ s.update(&hash);
+ var sig: [max_sig]u8 = undefined;
+ const n = try s.finalInto(io, &sig);
+ try std.testing.expectEqual(@as(usize, 96), n);
+
+ var v = VerifyState.init(.p384, ck.CKM_ECDSA, km.pointBytes()).?;
+ v.update(&hash);
+ try std.testing.expectEqual(VerifyResult.ok, v.finalVerify(sig[0..n]));
+}
+
+test "wrong-length signature reports len_range" {
+ const io = std.testing.io;
+ const km = try generate(io, .p256);
+ var v = VerifyState.init(.p256, ck.CKM_ECDSA_SHA256, km.pointBytes()).?;
+ v.update("data");
+ try std.testing.expectEqual(VerifyResult.len_range, v.finalVerify(&[_]u8{0} ** 63));
+}
+
+test "curve OID mapping and EC point DER round-trip" {
+ try std.testing.expectEqual(Curve.p256, curveFromParams(&oid_p256).?);
+ try std.testing.expectEqual(Curve.p384, curveFromParams(&oid_p384).?);
+ try std.testing.expect(curveFromParams(&[_]u8{ 0x06, 0x01, 0x00 }) == null);
+
+ const sec1 = [_]u8{0x04} ++ [_]u8{0x11} ** 64;
+ var buf: [max_ec_point_der]u8 = undefined;
+ const der = wrapEcPoint(&buf, &sec1);
+ try std.testing.expectEqual(@as(usize, 67), der.len);
+ try std.testing.expectEqualSlices(u8, &sec1, unwrapEcPoint(der).?);
+ try std.testing.expect(unwrapEcPoint(&[_]u8{0x05}) == null);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/keystore.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/keystore.zig
new file mode 100644
index 00000000..9cccebab
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/keystore.zig
@@ -0,0 +1,145 @@
+// ©AngelaMos | 2026
+// keystore.zig
+
+const std = @import("std");
+const config = @import("../config.zig");
+const pin = @import("pin.zig");
+
+const gcm = std.crypto.aead.aes_gcm.Aes256Gcm;
+
+pub const mk_len = config.master_key_len;
+pub const nonce_len = config.gcm_iv_len;
+pub const tag_len = config.gcm_tag_len;
+pub const seal_overhead = nonce_len + tag_len;
+
+pub const MasterKey = [mk_len]u8;
+pub const Salt = pin.Salt;
+
+pub const Error = error{ Malformed, AuthFailed };
+
+pub const Wrapped = struct {
+ salt: Salt = @splat(0),
+ nonce: [nonce_len]u8 = @splat(0),
+ ct: [mk_len]u8 = @splat(0),
+ tag: [tag_len]u8 = @splat(0),
+};
+
+pub fn deriveKek(io: std.Io, allocator: std.mem.Allocator, pin_bytes: []const u8, salt: *const Salt, out: *MasterKey) !void {
+ try pin.derive(io, allocator, pin_bytes, salt, out);
+}
+
+pub fn generateMasterKey(io: std.Io, out: *MasterKey) !void {
+ try io.randomSecure(out);
+}
+
+pub fn wrap(io: std.Io, allocator: std.mem.Allocator, pin_bytes: []const u8, mk: *const MasterKey) !Wrapped {
+ var w: Wrapped = .{};
+ try pin.genSalt(io, &w.salt);
+ var kek: MasterKey = undefined;
+ defer std.crypto.secureZero(u8, &kek);
+ try deriveKek(io, allocator, pin_bytes, &w.salt, &kek);
+ try io.randomSecure(&w.nonce);
+ gcm.encrypt(&w.ct, &w.tag, mk, "", w.nonce, kek);
+ return w;
+}
+
+pub fn rewrap(io: std.Io, allocator: std.mem.Allocator, pin_bytes: []const u8, mk: *const MasterKey) !Wrapped {
+ return wrap(io, allocator, pin_bytes, mk);
+}
+
+pub fn unwrap(io: std.Io, allocator: std.mem.Allocator, pin_bytes: []const u8, w: *const Wrapped, out: *MasterKey) !bool {
+ var kek: MasterKey = undefined;
+ defer std.crypto.secureZero(u8, &kek);
+ try deriveKek(io, allocator, pin_bytes, &w.salt, &kek);
+ gcm.decrypt(out, &w.ct, w.tag, "", w.nonce, kek) catch {
+ std.crypto.secureZero(u8, out);
+ return false;
+ };
+ return true;
+}
+
+pub fn sealedLen(plain_len: usize) usize {
+ return nonce_len + plain_len + tag_len;
+}
+
+pub fn seal(io: std.Io, mk: *const MasterKey, ad: []const u8, plain: []const u8, out: []u8) !usize {
+ var nonce: [nonce_len]u8 = undefined;
+ try io.randomSecure(&nonce);
+ @memcpy(out[0..nonce_len], &nonce);
+ const ct = out[nonce_len..][0..plain.len];
+ const tag = out[nonce_len + plain.len ..][0..tag_len];
+ gcm.encrypt(ct, tag, plain, ad, nonce, mk.*);
+ return sealedLen(plain.len);
+}
+
+pub fn unseal(mk: *const MasterKey, ad: []const u8, sealed: []const u8, out: []u8) Error!usize {
+ if (sealed.len < seal_overhead) return Error.Malformed;
+ const ct_len = sealed.len - seal_overhead;
+ var nonce: [nonce_len]u8 = undefined;
+ @memcpy(&nonce, sealed[0..nonce_len]);
+ var tag: [tag_len]u8 = undefined;
+ @memcpy(&tag, sealed[nonce_len + ct_len ..][0..tag_len]);
+ const ct = sealed[nonce_len..][0..ct_len];
+ gcm.decrypt(out[0..ct_len], ct, tag, ad, nonce, mk.*) catch return Error.AuthFailed;
+ return ct_len;
+}
+
+test "master key wrap then unwrap round-trips under the right PIN" {
+ const io = std.testing.io;
+ const a = std.testing.allocator;
+ var mk: MasterKey = undefined;
+ try generateMasterKey(io, &mk);
+
+ const w = try wrap(io, a, "1234", &mk);
+ var got: MasterKey = undefined;
+ try std.testing.expect(try unwrap(io, a, "1234", &w, &got));
+ try std.testing.expectEqualSlices(u8, &mk, &got);
+}
+
+test "unwrap with the wrong PIN fails the GCM tag and yields false" {
+ const io = std.testing.io;
+ const a = std.testing.allocator;
+ var mk: MasterKey = undefined;
+ try generateMasterKey(io, &mk);
+
+ const w = try wrap(io, a, "1234", &mk);
+ var got: MasterKey = undefined;
+ try std.testing.expect(!try unwrap(io, a, "9999", &w, &got));
+}
+
+test "rewrap under a new PIN keeps the same master key recoverable" {
+ const io = std.testing.io;
+ const a = std.testing.allocator;
+ var mk: MasterKey = undefined;
+ try generateMasterKey(io, &mk);
+
+ const w1 = try wrap(io, a, "old-pin", &mk);
+ var unwrapped: MasterKey = undefined;
+ try std.testing.expect(try unwrap(io, a, "old-pin", &w1, &unwrapped));
+
+ const w2 = try rewrap(io, a, "new-pin", &unwrapped);
+ var got: MasterKey = undefined;
+ try std.testing.expect(!try unwrap(io, a, "old-pin", &w2, &got));
+ try std.testing.expect(try unwrap(io, a, "new-pin", &w2, &got));
+ try std.testing.expectEqualSlices(u8, &mk, &got);
+}
+
+test "seal then unseal round-trips and binds the AAD" {
+ const io = std.testing.io;
+ var mk: MasterKey = undefined;
+ try generateMasterKey(io, &mk);
+
+ const secret = "private-scalar-bytes";
+ const ad = "\x11\x00\x00\x00\x00\x00\x00\x00";
+ var sealed: [64]u8 = undefined;
+ const sn = try seal(io, &mk, ad, secret, &sealed);
+ try std.testing.expectEqual(sealedLen(secret.len), sn);
+
+ var out: [64]u8 = undefined;
+ const un = try unseal(&mk, ad, sealed[0..sn], &out);
+ try std.testing.expectEqualSlices(u8, secret, out[0..un]);
+
+ try std.testing.expectError(Error.AuthFailed, unseal(&mk, "\x12\x00\x00\x00\x00\x00\x00\x00", sealed[0..sn], &out));
+ sealed[0] ^= 0x01;
+ try std.testing.expectError(Error.AuthFailed, unseal(&mk, ad, sealed[0..sn], &out));
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/mac.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/mac.zig
new file mode 100644
index 00000000..0a7e54b7
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/mac.zig
@@ -0,0 +1,76 @@
+// ©AngelaMos | 2026
+// mac.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+
+const hmac = std.crypto.auth.hmac.sha2;
+
+pub const max_mac_len = hmac.HmacSha512.mac_length;
+
+pub const Mac = union(enum) {
+ sha256: hmac.HmacSha256,
+ sha384: hmac.HmacSha384,
+ sha512: hmac.HmacSha512,
+
+ pub fn init(mech: ck.CK_MECHANISM_TYPE, key: []const u8) ?Mac {
+ return switch (mech) {
+ ck.CKM_SHA256_HMAC => .{ .sha256 = hmac.HmacSha256.init(key) },
+ ck.CKM_SHA384_HMAC => .{ .sha384 = hmac.HmacSha384.init(key) },
+ ck.CKM_SHA512_HMAC => .{ .sha512 = hmac.HmacSha512.init(key) },
+ else => null,
+ };
+ }
+
+ pub fn update(self: *Mac, data: []const u8) void {
+ switch (self.*) {
+ inline else => |*m| m.update(data),
+ }
+ }
+
+ pub fn macLen(self: *const Mac) usize {
+ return switch (self.*) {
+ inline else => |m| @TypeOf(m).mac_length,
+ };
+ }
+
+ pub fn finalInto(self: *Mac, out: []u8) void {
+ switch (self.*) {
+ inline else => |*m| {
+ const M = @TypeOf(m.*);
+ m.final(out[0..M.mac_length]);
+ },
+ }
+ }
+};
+
+pub fn macLenOf(mech: ck.CK_MECHANISM_TYPE) ?usize {
+ return switch (mech) {
+ ck.CKM_SHA256_HMAC => hmac.HmacSha256.mac_length,
+ ck.CKM_SHA384_HMAC => hmac.HmacSha384.mac_length,
+ ck.CKM_SHA512_HMAC => hmac.HmacSha512.mac_length,
+ else => null,
+ };
+}
+
+test "HMAC-SHA256 matches RFC 4231 test case 2" {
+ var m = Mac.init(ck.CKM_SHA256_HMAC, "Jefe").?;
+ m.update("what do ya want ");
+ m.update("for nothing?");
+ var out: [max_mac_len]u8 = undefined;
+ m.finalInto(&out);
+ const expect = [_]u8{
+ 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+ 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+ 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+ 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43,
+ };
+ try std.testing.expectEqual(@as(usize, 32), m.macLen());
+ try std.testing.expectEqualSlices(u8, &expect, out[0..32]);
+}
+
+test "macLenOf maps mechanisms and rejects non-HMAC" {
+ try std.testing.expectEqual(@as(?usize, 48), macLenOf(ck.CKM_SHA384_HMAC));
+ try std.testing.expectEqual(@as(?usize, 64), macLenOf(ck.CKM_SHA512_HMAC));
+ try std.testing.expect(macLenOf(ck.CKM_SHA256) == null);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/openssl.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/openssl.zig
new file mode 100644
index 00000000..62586958
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/openssl.zig
@@ -0,0 +1,85 @@
+// ©AngelaMos | 2026
+// openssl.zig
+
+pub const EVP_PKEY = opaque {};
+pub const EVP_PKEY_CTX = opaque {};
+pub const EVP_MD = opaque {};
+pub const EVP_MD_CTX = opaque {};
+pub const ENGINE = opaque {};
+pub const BIGNUM = opaque {};
+pub const OSSL_LIB_CTX = opaque {};
+pub const OSSL_PARAM = opaque {};
+pub const OSSL_PARAM_BLD = opaque {};
+
+pub const pkey_rsa: c_int = 6;
+pub const selection_public_key: c_int = 134;
+pub const selection_keypair: c_int = 135;
+
+pub const pad_pkcs1: c_int = 1;
+pub const pad_oaep: c_int = 4;
+pub const pad_pss: c_int = 6;
+pub const pss_saltlen_digest: c_int = -1;
+
+pub const param_n = "n";
+pub const param_e = "e";
+pub const param_d = "d";
+pub const param_factor1 = "rsa-factor1";
+pub const param_factor2 = "rsa-factor2";
+pub const param_exponent1 = "rsa-exponent1";
+pub const param_exponent2 = "rsa-exponent2";
+pub const param_coefficient1 = "rsa-coefficient1";
+
+pub extern fn EVP_PKEY_CTX_new_id(id: c_int, e: ?*ENGINE) ?*EVP_PKEY_CTX;
+pub extern fn EVP_PKEY_CTX_new(pkey: ?*EVP_PKEY, e: ?*ENGINE) ?*EVP_PKEY_CTX;
+pub extern fn EVP_PKEY_CTX_new_from_name(libctx: ?*OSSL_LIB_CTX, name: [*:0]const u8, propq: ?[*:0]const u8) ?*EVP_PKEY_CTX;
+pub extern fn EVP_PKEY_CTX_free(ctx: ?*EVP_PKEY_CTX) void;
+pub extern fn EVP_PKEY_free(pkey: ?*EVP_PKEY) void;
+
+pub extern fn EVP_PKEY_keygen_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_CTX_set_rsa_keygen_bits(ctx: ?*EVP_PKEY_CTX, bits: c_int) c_int;
+pub extern fn EVP_PKEY_generate(ctx: ?*EVP_PKEY_CTX, ppkey: *?*EVP_PKEY) c_int;
+
+pub extern fn EVP_PKEY_get_bits(pkey: ?*const EVP_PKEY) c_int;
+pub extern fn EVP_PKEY_get_bn_param(pkey: ?*const EVP_PKEY, key_name: [*:0]const u8, bn: *?*BIGNUM) c_int;
+
+pub extern fn EVP_PKEY_fromdata_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_fromdata(ctx: ?*EVP_PKEY_CTX, ppkey: *?*EVP_PKEY, selection: c_int, params: ?*OSSL_PARAM) c_int;
+
+pub extern fn BN_bin2bn(s: [*]const u8, len: c_int, ret: ?*BIGNUM) ?*BIGNUM;
+pub extern fn BN_bn2binpad(a: ?*const BIGNUM, to: [*]u8, tolen: c_int) c_int;
+pub extern fn BN_num_bits(a: ?*const BIGNUM) c_int;
+pub extern fn BN_free(a: ?*BIGNUM) void;
+pub extern fn BN_clear_free(a: ?*BIGNUM) void;
+
+pub extern fn OSSL_PARAM_BLD_new() ?*OSSL_PARAM_BLD;
+pub extern fn OSSL_PARAM_BLD_push_BN(bld: ?*OSSL_PARAM_BLD, key: [*:0]const u8, bn: ?*const BIGNUM) c_int;
+pub extern fn OSSL_PARAM_BLD_to_param(bld: ?*OSSL_PARAM_BLD) ?*OSSL_PARAM;
+pub extern fn OSSL_PARAM_BLD_free(bld: ?*OSSL_PARAM_BLD) void;
+pub extern fn OSSL_PARAM_free(p: ?*OSSL_PARAM) void;
+
+pub extern fn EVP_MD_CTX_new() ?*EVP_MD_CTX;
+pub extern fn EVP_MD_CTX_free(ctx: ?*EVP_MD_CTX) void;
+pub extern fn EVP_sha256() ?*const EVP_MD;
+pub extern fn EVP_sha384() ?*const EVP_MD;
+pub extern fn EVP_sha512() ?*const EVP_MD;
+
+pub extern fn EVP_DigestSignInit(ctx: ?*EVP_MD_CTX, pctx: ?*?*EVP_PKEY_CTX, mdtype: ?*const EVP_MD, e: ?*ENGINE, pkey: ?*EVP_PKEY) c_int;
+pub extern fn EVP_DigestSign(ctx: ?*EVP_MD_CTX, sigret: ?[*]u8, siglen: *usize, tbs: [*]const u8, tbslen: usize) c_int;
+pub extern fn EVP_DigestVerifyInit(ctx: ?*EVP_MD_CTX, pctx: ?*?*EVP_PKEY_CTX, mdtype: ?*const EVP_MD, e: ?*ENGINE, pkey: ?*EVP_PKEY) c_int;
+pub extern fn EVP_DigestVerify(ctx: ?*EVP_MD_CTX, sig: [*]const u8, siglen: usize, tbs: [*]const u8, tbslen: usize) c_int;
+
+pub extern fn EVP_PKEY_sign_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_sign(ctx: ?*EVP_PKEY_CTX, sig: ?[*]u8, siglen: *usize, tbs: [*]const u8, tbslen: usize) c_int;
+pub extern fn EVP_PKEY_verify_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_verify(ctx: ?*EVP_PKEY_CTX, sig: [*]const u8, siglen: usize, tbs: [*]const u8, tbslen: usize) c_int;
+
+pub extern fn EVP_PKEY_encrypt_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_encrypt(ctx: ?*EVP_PKEY_CTX, out: ?[*]u8, outlen: *usize, in: [*]const u8, inlen: usize) c_int;
+pub extern fn EVP_PKEY_decrypt_init(ctx: ?*EVP_PKEY_CTX) c_int;
+pub extern fn EVP_PKEY_decrypt(ctx: ?*EVP_PKEY_CTX, out: ?[*]u8, outlen: *usize, in: [*]const u8, inlen: usize) c_int;
+
+pub extern fn EVP_PKEY_CTX_set_rsa_padding(ctx: ?*EVP_PKEY_CTX, pad_mode: c_int) c_int;
+pub extern fn EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx: ?*EVP_PKEY_CTX, saltlen: c_int) c_int;
+pub extern fn EVP_PKEY_CTX_set_rsa_mgf1_md(ctx: ?*EVP_PKEY_CTX, md: ?*const EVP_MD) c_int;
+pub extern fn EVP_PKEY_CTX_set_rsa_oaep_md(ctx: ?*EVP_PKEY_CTX, md: ?*const EVP_MD) c_int;
+pub extern fn EVP_PKEY_CTX_set_signature_md(ctx: ?*EVP_PKEY_CTX, md: ?*const EVP_MD) c_int;
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/pin.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/pin.zig
new file mode 100644
index 00000000..a9589598
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/pin.zig
@@ -0,0 +1,77 @@
+// ©AngelaMos | 2026
+// pin.zig
+
+const std = @import("std");
+const config = @import("../config.zig");
+
+const argon2 = std.crypto.pwhash.argon2;
+
+pub const salt_len = config.pin_salt_len;
+pub const hash_len = config.pin_hash_len;
+
+pub const Salt = [salt_len]u8;
+pub const Hash = [hash_len]u8;
+
+const params: argon2.Params = .{
+ .t = config.pin_kdf_t,
+ .m = config.pin_kdf_m_kib,
+ .p = config.pin_kdf_p,
+};
+
+pub fn genSalt(io: std.Io, out: *Salt) !void {
+ try io.randomSecure(out);
+}
+
+pub fn derive(io: std.Io, allocator: std.mem.Allocator, pin: []const u8, salt: *const Salt, out: *Hash) !void {
+ try argon2.kdf(allocator, out, pin, salt, params, .argon2id, io);
+}
+
+pub fn verify(io: std.Io, allocator: std.mem.Allocator, pin: []const u8, salt: *const Salt, expected: *const Hash) !bool {
+ var got: Hash = undefined;
+ defer std.crypto.secureZero(u8, &got);
+ try derive(io, allocator, pin, salt, &got);
+ return std.crypto.timing_safe.eql(Hash, got, expected.*);
+}
+
+test "argon2id derive is deterministic for a fixed salt" {
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ defer threaded.deinit();
+ const io = threaded.io();
+
+ const salt: Salt = @splat(7);
+ var a: Hash = undefined;
+ var b: Hash = undefined;
+ try derive(io, std.testing.allocator, "1234", &salt, &a);
+ try derive(io, std.testing.allocator, "1234", &salt, &b);
+ try std.testing.expectEqual(a, b);
+}
+
+test "verify accepts the right PIN and rejects the wrong one" {
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ defer threaded.deinit();
+ const io = threaded.io();
+
+ var salt: Salt = undefined;
+ try genSalt(io, &salt);
+ var h: Hash = undefined;
+ try derive(io, std.testing.allocator, "secret-pin", &salt, &h);
+
+ try std.testing.expect(try verify(io, std.testing.allocator, "secret-pin", &salt, &h));
+ try std.testing.expect(!try verify(io, std.testing.allocator, "wrong-pin", &salt, &h));
+}
+
+test "a fresh salt changes the derived hash" {
+ var threaded: std.Io.Threaded = .init(std.testing.allocator, .{});
+ defer threaded.deinit();
+ const io = threaded.io();
+
+ var s1: Salt = undefined;
+ var s2: Salt = undefined;
+ try genSalt(io, &s1);
+ try genSalt(io, &s2);
+ var h1: Hash = undefined;
+ var h2: Hash = undefined;
+ try derive(io, std.testing.allocator, "1234", &s1, &h1);
+ try derive(io, std.testing.allocator, "1234", &s2, &h2);
+ try std.testing.expect(!std.mem.eql(u8, &h1, &h2));
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/crypto/rsa.zig b/PROJECTS/advanced/hsm-emulator/src/crypto/rsa.zig
new file mode 100644
index 00000000..c48a706c
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/crypto/rsa.zig
@@ -0,0 +1,354 @@
+// ©AngelaMos | 2026
+// rsa.zig
+
+const std = @import("std");
+const ck = @import("../ck.zig");
+const config = @import("../config.zig");
+const ossl = @import("openssl.zig");
+
+pub const max_modulus_bytes: usize = config.rsa_max_key_bits / 8;
+pub const max_sig_bytes: usize = max_modulus_bytes;
+const component_count = 8;
+
+pub const Error = error{Crypto};
+
+pub const Hash = enum {
+ none,
+ sha256,
+ sha384,
+ sha512,
+
+ pub fn fromMech(mech: ck.CK_MECHANISM_TYPE) ?Hash {
+ return switch (mech) {
+ ck.CKM_SHA256 => .sha256,
+ ck.CKM_SHA384 => .sha384,
+ ck.CKM_SHA512 => .sha512,
+ else => null,
+ };
+ }
+};
+
+fn mdOf(h: Hash) ?*const ossl.EVP_MD {
+ return switch (h) {
+ .none => null,
+ .sha256 => ossl.EVP_sha256(),
+ .sha384 => ossl.EVP_sha384(),
+ .sha512 => ossl.EVP_sha512(),
+ };
+}
+
+pub const SigScheme = enum { pkcs1, pss };
+
+pub const SignParams = struct {
+ scheme: SigScheme,
+ digest: Hash,
+ pss_hash: Hash = .sha256,
+ salt_len: c_int = ossl.pss_saltlen_digest,
+};
+
+pub const CryptScheme = enum { pkcs1, oaep };
+
+pub const CryptParams = struct {
+ scheme: CryptScheme,
+ oaep_hash: Hash = .sha256,
+};
+
+pub const VerifyResult = enum { ok, invalid };
+
+pub const Buf = struct {
+ bytes: [max_modulus_bytes]u8 = @splat(0),
+ len: usize = 0,
+
+ pub fn slice(self: *const Buf) []const u8 {
+ return self.bytes[0..self.len];
+ }
+};
+
+pub const Generated = struct {
+ bits: u32 = 0,
+ n: Buf = .{},
+ e: Buf = .{},
+ d: Buf = .{},
+ p: Buf = .{},
+ q: Buf = .{},
+ dmp1: Buf = .{},
+ dmq1: Buf = .{},
+ iqmp: Buf = .{},
+
+ pub fn zeroize(self: *Generated) void {
+ std.crypto.secureZero(u8, &self.d.bytes);
+ std.crypto.secureZero(u8, &self.p.bytes);
+ std.crypto.secureZero(u8, &self.q.bytes);
+ std.crypto.secureZero(u8, &self.dmp1.bytes);
+ std.crypto.secureZero(u8, &self.dmq1.bytes);
+ std.crypto.secureZero(u8, &self.iqmp.bytes);
+ }
+};
+
+pub const PublicComponents = struct {
+ n: []const u8,
+ e: []const u8,
+};
+
+pub const PrivateComponents = struct {
+ n: []const u8,
+ e: []const u8,
+ d: []const u8,
+ p: []const u8,
+ q: []const u8,
+ dmp1: []const u8,
+ dmq1: []const u8,
+ iqmp: []const u8,
+};
+
+fn extractBn(pkey: *const ossl.EVP_PKEY, name: [*:0]const u8, buf: *Buf) Error!void {
+ var bn: ?*ossl.BIGNUM = null;
+ if (ossl.EVP_PKEY_get_bn_param(pkey, name, &bn) <= 0) return Error.Crypto;
+ defer ossl.BN_clear_free(bn);
+ const nbits = ossl.BN_num_bits(bn);
+ if (nbits < 0) return Error.Crypto;
+ const nbytes: usize = @intCast(@divFloor(nbits + 7, 8));
+ if (nbytes == 0 or nbytes > max_modulus_bytes) return Error.Crypto;
+ if (ossl.BN_bn2binpad(bn, &buf.bytes, @intCast(nbytes)) < 0) return Error.Crypto;
+ buf.len = nbytes;
+}
+
+pub fn generate(bits: u32) Error!Generated {
+ const ctx = ossl.EVP_PKEY_CTX_new_id(ossl.pkey_rsa, null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_keygen_init(ctx) <= 0) return Error.Crypto;
+ if (ossl.EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, @intCast(bits)) <= 0) return Error.Crypto;
+ var pkey: ?*ossl.EVP_PKEY = null;
+ if (ossl.EVP_PKEY_generate(ctx, &pkey) <= 0) return Error.Crypto;
+ defer ossl.EVP_PKEY_free(pkey);
+
+ const key = pkey orelse return Error.Crypto;
+ var g: Generated = .{ .bits = @intCast(ossl.EVP_PKEY_get_bits(key)) };
+ errdefer g.zeroize();
+ try extractBn(key, ossl.param_n, &g.n);
+ try extractBn(key, ossl.param_e, &g.e);
+ try extractBn(key, ossl.param_d, &g.d);
+ try extractBn(key, ossl.param_factor1, &g.p);
+ try extractBn(key, ossl.param_factor2, &g.q);
+ try extractBn(key, ossl.param_exponent1, &g.dmp1);
+ try extractBn(key, ossl.param_exponent2, &g.dmq1);
+ try extractBn(key, ossl.param_coefficient1, &g.iqmp);
+ return g;
+}
+
+fn buildKey(names: []const [*:0]const u8, vals: []const []const u8, selection: c_int) Error!*ossl.EVP_PKEY {
+ const bld = ossl.OSSL_PARAM_BLD_new() orelse return Error.Crypto;
+ defer ossl.OSSL_PARAM_BLD_free(bld);
+
+ var bns: [component_count]?*ossl.BIGNUM = @splat(null);
+ defer for (bns[0..names.len]) |bn| ossl.BN_clear_free(bn);
+
+ for (names, vals, 0..) |nm, v, i| {
+ bns[i] = ossl.BN_bin2bn(v.ptr, @intCast(v.len), null) orelse return Error.Crypto;
+ if (ossl.OSSL_PARAM_BLD_push_BN(bld, nm, bns[i]) <= 0) return Error.Crypto;
+ }
+
+ const params = ossl.OSSL_PARAM_BLD_to_param(bld) orelse return Error.Crypto;
+ defer ossl.OSSL_PARAM_free(params);
+
+ const ctx = ossl.EVP_PKEY_CTX_new_from_name(null, "RSA", null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_fromdata_init(ctx) <= 0) return Error.Crypto;
+ var pkey: ?*ossl.EVP_PKEY = null;
+ if (ossl.EVP_PKEY_fromdata(ctx, &pkey, selection, params) <= 0) return Error.Crypto;
+ return pkey orelse Error.Crypto;
+}
+
+fn buildPublic(pc: PublicComponents) Error!*ossl.EVP_PKEY {
+ return buildKey(
+ &.{ ossl.param_n, ossl.param_e },
+ &.{ pc.n, pc.e },
+ ossl.selection_public_key,
+ );
+}
+
+fn buildPrivate(sc: PrivateComponents) Error!*ossl.EVP_PKEY {
+ return buildKey(
+ &.{ ossl.param_n, ossl.param_e, ossl.param_d, ossl.param_factor1, ossl.param_factor2, ossl.param_exponent1, ossl.param_exponent2, ossl.param_coefficient1 },
+ &.{ sc.n, sc.e, sc.d, sc.p, sc.q, sc.dmp1, sc.dmq1, sc.iqmp },
+ ossl.selection_keypair,
+ );
+}
+
+fn applyPss(pctx: ?*ossl.EVP_PKEY_CTX, p: SignParams, raw: bool) Error!void {
+ if (ossl.EVP_PKEY_CTX_set_rsa_padding(pctx, ossl.pad_pss) <= 0) return Error.Crypto;
+ if (raw) {
+ if (ossl.EVP_PKEY_CTX_set_signature_md(pctx, mdOf(p.pss_hash)) <= 0) return Error.Crypto;
+ }
+ if (ossl.EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, p.salt_len) <= 0) return Error.Crypto;
+ if (ossl.EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mdOf(p.pss_hash)) <= 0) return Error.Crypto;
+}
+
+pub fn sign(sc: PrivateComponents, p: SignParams, data: []const u8, out: []u8) Error!usize {
+ const pkey = try buildPrivate(sc);
+ defer ossl.EVP_PKEY_free(pkey);
+ var siglen: usize = out.len;
+
+ if (p.digest != .none) {
+ const mdctx = ossl.EVP_MD_CTX_new() orelse return Error.Crypto;
+ defer ossl.EVP_MD_CTX_free(mdctx);
+ var pctx: ?*ossl.EVP_PKEY_CTX = null;
+ if (ossl.EVP_DigestSignInit(mdctx, &pctx, mdOf(p.digest), null, pkey) <= 0) return Error.Crypto;
+ if (p.scheme == .pss) try applyPss(pctx, p, false);
+ if (ossl.EVP_DigestSign(mdctx, out.ptr, &siglen, data.ptr, data.len) <= 0) return Error.Crypto;
+ return siglen;
+ }
+
+ const ctx = ossl.EVP_PKEY_CTX_new(pkey, null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_sign_init(ctx) <= 0) return Error.Crypto;
+ if (p.scheme == .pss) {
+ try applyPss(ctx, p, true);
+ } else {
+ if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_pkcs1) <= 0) return Error.Crypto;
+ }
+ if (ossl.EVP_PKEY_sign(ctx, out.ptr, &siglen, data.ptr, data.len) <= 0) return Error.Crypto;
+ return siglen;
+}
+
+pub fn verify(pc: PublicComponents, p: SignParams, data: []const u8, sig: []const u8) Error!VerifyResult {
+ const pkey = try buildPublic(pc);
+ defer ossl.EVP_PKEY_free(pkey);
+
+ if (p.digest != .none) {
+ const mdctx = ossl.EVP_MD_CTX_new() orelse return Error.Crypto;
+ defer ossl.EVP_MD_CTX_free(mdctx);
+ var pctx: ?*ossl.EVP_PKEY_CTX = null;
+ if (ossl.EVP_DigestVerifyInit(mdctx, &pctx, mdOf(p.digest), null, pkey) <= 0) return Error.Crypto;
+ if (p.scheme == .pss) try applyPss(pctx, p, false);
+ return if (ossl.EVP_DigestVerify(mdctx, sig.ptr, sig.len, data.ptr, data.len) == 1) .ok else .invalid;
+ }
+
+ const ctx = ossl.EVP_PKEY_CTX_new(pkey, null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_verify_init(ctx) <= 0) return Error.Crypto;
+ if (p.scheme == .pss) {
+ try applyPss(ctx, p, true);
+ } else {
+ if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_pkcs1) <= 0) return Error.Crypto;
+ }
+ return if (ossl.EVP_PKEY_verify(ctx, sig.ptr, sig.len, data.ptr, data.len) == 1) .ok else .invalid;
+}
+
+fn applyCryptPadding(ctx: ?*ossl.EVP_PKEY_CTX, p: CryptParams) Error!void {
+ if (p.scheme == .oaep) {
+ if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_oaep) <= 0) return Error.Crypto;
+ if (ossl.EVP_PKEY_CTX_set_rsa_oaep_md(ctx, mdOf(p.oaep_hash)) <= 0) return Error.Crypto;
+ if (ossl.EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mdOf(p.oaep_hash)) <= 0) return Error.Crypto;
+ } else {
+ if (ossl.EVP_PKEY_CTX_set_rsa_padding(ctx, ossl.pad_pkcs1) <= 0) return Error.Crypto;
+ }
+}
+
+pub fn encrypt(pc: PublicComponents, p: CryptParams, in: []const u8, out: []u8) Error!usize {
+ const pkey = try buildPublic(pc);
+ defer ossl.EVP_PKEY_free(pkey);
+ const ctx = ossl.EVP_PKEY_CTX_new(pkey, null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_encrypt_init(ctx) <= 0) return Error.Crypto;
+ try applyCryptPadding(ctx, p);
+ var outlen: usize = out.len;
+ if (ossl.EVP_PKEY_encrypt(ctx, out.ptr, &outlen, in.ptr, in.len) <= 0) return Error.Crypto;
+ return outlen;
+}
+
+pub fn decrypt(sc: PrivateComponents, p: CryptParams, in: []const u8, out: []u8) Error!usize {
+ const pkey = try buildPrivate(sc);
+ defer ossl.EVP_PKEY_free(pkey);
+ const ctx = ossl.EVP_PKEY_CTX_new(pkey, null) orelse return Error.Crypto;
+ defer ossl.EVP_PKEY_CTX_free(ctx);
+ if (ossl.EVP_PKEY_decrypt_init(ctx) <= 0) return Error.Crypto;
+ try applyCryptPadding(ctx, p);
+ var outlen: usize = out.len;
+ if (ossl.EVP_PKEY_decrypt(ctx, out.ptr, &outlen, in.ptr, in.len) <= 0) return Error.Crypto;
+ return outlen;
+}
+
+fn testPriv(g: *const Generated) PrivateComponents {
+ return .{
+ .n = g.n.slice(),
+ .e = g.e.slice(),
+ .d = g.d.slice(),
+ .p = g.p.slice(),
+ .q = g.q.slice(),
+ .dmp1 = g.dmp1.slice(),
+ .dmq1 = g.dmq1.slice(),
+ .iqmp = g.iqmp.slice(),
+ };
+}
+
+fn testPub(g: *const Generated) PublicComponents {
+ return .{ .n = g.n.slice(), .e = g.e.slice() };
+}
+
+test "generate yields a 2048-bit key with sane component sizes" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ try std.testing.expectEqual(@as(u32, 2048), g.bits);
+ try std.testing.expectEqual(@as(usize, 256), g.n.len);
+ try std.testing.expect(g.e.len >= 3 and g.e.len <= 4);
+ try std.testing.expect(g.p.len == 128 and g.q.len == 128);
+}
+
+test "PKCS#1 v1.5 hash-then-sign round-trips and detects tamper" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ const params: SignParams = .{ .scheme = .pkcs1, .digest = .sha256 };
+ var sig: [max_sig_bytes]u8 = undefined;
+ const n = try sign(testPriv(&g), params, "enterprise message", &sig);
+ try std.testing.expectEqual(@as(usize, 256), n);
+ try std.testing.expectEqual(VerifyResult.ok, try verify(testPub(&g), params, "enterprise message", sig[0..n]));
+ try std.testing.expectEqual(VerifyResult.invalid, try verify(testPub(&g), params, "enterprise messagX", sig[0..n]));
+ sig[0] ^= 0x01;
+ try std.testing.expectEqual(VerifyResult.invalid, try verify(testPub(&g), params, "enterprise message", sig[0..n]));
+}
+
+test "PSS hash-then-sign round-trips" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ const params: SignParams = .{ .scheme = .pss, .digest = .sha256, .pss_hash = .sha256 };
+ var sig: [max_sig_bytes]u8 = undefined;
+ const n = try sign(testPriv(&g), params, "pss payload", &sig);
+ try std.testing.expectEqual(VerifyResult.ok, try verify(testPub(&g), params, "pss payload", sig[0..n]));
+ try std.testing.expectEqual(VerifyResult.invalid, try verify(testPub(&g), params, "pss payloaX", sig[0..n]));
+}
+
+test "raw PKCS#1 v1.5 sign over a pre-hashed value round-trips" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ const params: SignParams = .{ .scheme = .pkcs1, .digest = .none };
+ const prehash = [_]u8{0xa5} ** 32;
+ var sig: [max_sig_bytes]u8 = undefined;
+ const n = try sign(testPriv(&g), params, &prehash, &sig);
+ try std.testing.expectEqual(VerifyResult.ok, try verify(testPub(&g), params, &prehash, sig[0..n]));
+}
+
+test "PKCS#1 v1.5 encrypt/decrypt round-trips" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ const params: CryptParams = .{ .scheme = .pkcs1 };
+ const msg = "wrap me";
+ var ct: [max_modulus_bytes]u8 = undefined;
+ const cn = try encrypt(testPub(&g), params, msg, &ct);
+ try std.testing.expectEqual(@as(usize, 256), cn);
+ var pt: [max_modulus_bytes]u8 = undefined;
+ const pn = try decrypt(testPriv(&g), params, ct[0..cn], &pt);
+ try std.testing.expectEqualSlices(u8, msg, pt[0..pn]);
+}
+
+test "OAEP-SHA256 encrypt/decrypt round-trips" {
+ var g = try generate(config.rsa_min_key_bits);
+ defer g.zeroize();
+ const params: CryptParams = .{ .scheme = .oaep, .oaep_hash = .sha256 };
+ const msg = "oaep secret payload";
+ var ct: [max_modulus_bytes]u8 = undefined;
+ const cn = try encrypt(testPub(&g), params, msg, &ct);
+ var pt: [max_modulus_bytes]u8 = undefined;
+ const pn = try decrypt(testPriv(&g), params, ct[0..cn], &pt);
+ try std.testing.expectEqualSlices(u8, msg, pt[0..pn]);
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/main.zig b/PROJECTS/advanced/hsm-emulator/src/main.zig
new file mode 100644
index 00000000..3c09a199
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/main.zig
@@ -0,0 +1,94 @@
+// ©AngelaMos | 2026
+// main.zig
+
+const std = @import("std");
+const ck = @import("ck.zig");
+const general = @import("api/general.zig");
+const slot_token = @import("api/slot_token.zig");
+const session = @import("api/session.zig");
+const object = @import("api/object.zig");
+const crypto_ops = @import("api/crypto_ops.zig");
+const keymgmt = @import("api/keymgmt.zig");
+const random = @import("api/random.zig");
+
+comptime {
+ std.debug.assert(@sizeOf(ck.CK_FUNCTION_LIST) == 69 * @sizeOf(usize));
+ std.debug.assert(@sizeOf(ck.CK_ATTRIBUTE) == 24);
+}
+
+export fn C_GetFunctionList(ppFunctionList: *?*ck.CK_FUNCTION_LIST) callconv(.c) ck.CK_RV {
+ ppFunctionList.* = &function_list;
+ return ck.CKR_OK;
+}
+
+var function_list: ck.CK_FUNCTION_LIST = .{
+ .version = ck.CK_VERSION{ .major = 2, .minor = 40 },
+ .C_Initialize = general.C_Initialize,
+ .C_Finalize = general.C_Finalize,
+ .C_GetInfo = general.C_GetInfo,
+ .C_GetFunctionList = C_GetFunctionList,
+ .C_GetSlotList = slot_token.C_GetSlotList,
+ .C_GetSlotInfo = slot_token.C_GetSlotInfo,
+ .C_GetTokenInfo = slot_token.C_GetTokenInfo,
+ .C_GetMechanismList = slot_token.C_GetMechanismList,
+ .C_GetMechanismInfo = slot_token.C_GetMechanismInfo,
+ .C_InitToken = slot_token.C_InitToken,
+ .C_InitPIN = slot_token.C_InitPIN,
+ .C_SetPIN = slot_token.C_SetPIN,
+ .C_OpenSession = session.C_OpenSession,
+ .C_CloseSession = session.C_CloseSession,
+ .C_CloseAllSessions = session.C_CloseAllSessions,
+ .C_GetSessionInfo = session.C_GetSessionInfo,
+ .C_GetOperationState = session.C_GetOperationState,
+ .C_SetOperationState = session.C_SetOperationState,
+ .C_Login = session.C_Login,
+ .C_Logout = session.C_Logout,
+ .C_CreateObject = object.C_CreateObject,
+ .C_CopyObject = object.C_CopyObject,
+ .C_DestroyObject = object.C_DestroyObject,
+ .C_GetObjectSize = object.C_GetObjectSize,
+ .C_GetAttributeValue = object.C_GetAttributeValue,
+ .C_SetAttributeValue = object.C_SetAttributeValue,
+ .C_FindObjectsInit = object.C_FindObjectsInit,
+ .C_FindObjects = object.C_FindObjects,
+ .C_FindObjectsFinal = object.C_FindObjectsFinal,
+ .C_EncryptInit = crypto_ops.C_EncryptInit,
+ .C_Encrypt = crypto_ops.C_Encrypt,
+ .C_EncryptUpdate = crypto_ops.C_EncryptUpdate,
+ .C_EncryptFinal = crypto_ops.C_EncryptFinal,
+ .C_DecryptInit = crypto_ops.C_DecryptInit,
+ .C_Decrypt = crypto_ops.C_Decrypt,
+ .C_DecryptUpdate = crypto_ops.C_DecryptUpdate,
+ .C_DecryptFinal = crypto_ops.C_DecryptFinal,
+ .C_DigestInit = crypto_ops.C_DigestInit,
+ .C_Digest = crypto_ops.C_Digest,
+ .C_DigestUpdate = crypto_ops.C_DigestUpdate,
+ .C_DigestKey = crypto_ops.C_DigestKey,
+ .C_DigestFinal = crypto_ops.C_DigestFinal,
+ .C_SignInit = crypto_ops.C_SignInit,
+ .C_Sign = crypto_ops.C_Sign,
+ .C_SignUpdate = crypto_ops.C_SignUpdate,
+ .C_SignFinal = crypto_ops.C_SignFinal,
+ .C_SignRecoverInit = crypto_ops.C_SignRecoverInit,
+ .C_SignRecover = crypto_ops.C_SignRecover,
+ .C_VerifyInit = crypto_ops.C_VerifyInit,
+ .C_Verify = crypto_ops.C_Verify,
+ .C_VerifyUpdate = crypto_ops.C_VerifyUpdate,
+ .C_VerifyFinal = crypto_ops.C_VerifyFinal,
+ .C_VerifyRecoverInit = crypto_ops.C_VerifyRecoverInit,
+ .C_VerifyRecover = crypto_ops.C_VerifyRecover,
+ .C_DigestEncryptUpdate = crypto_ops.C_DigestEncryptUpdate,
+ .C_DecryptDigestUpdate = crypto_ops.C_DecryptDigestUpdate,
+ .C_SignEncryptUpdate = crypto_ops.C_SignEncryptUpdate,
+ .C_DecryptVerifyUpdate = crypto_ops.C_DecryptVerifyUpdate,
+ .C_GenerateKey = keymgmt.C_GenerateKey,
+ .C_GenerateKeyPair = keymgmt.C_GenerateKeyPair,
+ .C_WrapKey = keymgmt.C_WrapKey,
+ .C_UnwrapKey = keymgmt.C_UnwrapKey,
+ .C_DeriveKey = keymgmt.C_DeriveKey,
+ .C_SeedRandom = random.C_SeedRandom,
+ .C_GenerateRandom = random.C_GenerateRandom,
+ .C_GetFunctionStatus = general.C_GetFunctionStatus,
+ .C_CancelFunction = general.C_CancelFunction,
+ .C_WaitForSlotEvent = general.C_WaitForSlotEvent,
+};
diff --git a/PROJECTS/advanced/hsm-emulator/src/test_all.zig b/PROJECTS/advanced/hsm-emulator/src/test_all.zig
new file mode 100644
index 00000000..8658f43f
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/test_all.zig
@@ -0,0 +1,15 @@
+// ©AngelaMos | 2026
+// test_all.zig
+
+test {
+ _ = @import("crypto/pin.zig");
+ _ = @import("core/token.zig");
+ _ = @import("core/session.zig");
+ _ = @import("core/object_store.zig");
+ _ = @import("crypto/digest.zig");
+ _ = @import("crypto/mac.zig");
+ _ = @import("crypto/cipher.zig");
+ _ = @import("crypto/ecdsa.zig");
+ _ = @import("crypto/rsa.zig");
+ _ = @import("crypto/keystore.zig");
+}
diff --git a/PROJECTS/advanced/hsm-emulator/src/util.zig b/PROJECTS/advanced/hsm-emulator/src/util.zig
new file mode 100644
index 00000000..234b6342
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/src/util.zig
@@ -0,0 +1,12 @@
+// ©AngelaMos | 2026
+// util.zig
+
+pub fn padded(comptime n: usize, comptime s: []const u8) [n]u8 {
+ if (s.len > n) @compileError("padded: source string longer than field width");
+ var out: [n]u8 = undefined;
+ var i: usize = 0;
+ while (i < n) : (i += 1) {
+ out[i] = if (i < s.len) s[i] else ' ';
+ }
+ return out;
+}
diff --git a/PROJECTS/advanced/hsm-emulator/tests/abi_test.zig b/PROJECTS/advanced/hsm-emulator/tests/abi_test.zig
new file mode 100644
index 00000000..d7a1278d
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/tests/abi_test.zig
@@ -0,0 +1,151 @@
+// ©AngelaMos | 2026
+// abi_test.zig
+
+const std = @import("std");
+const ck = @import("ck");
+const p11c = @import("p11c");
+
+const ptr = @sizeOf(usize);
+
+test "scalar ABI widths match Cryptoki LP64" {
+ try std.testing.expectEqual(@as(usize, 1), @sizeOf(ck.CK_BYTE));
+ try std.testing.expectEqual(@as(usize, 1), @sizeOf(ck.CK_BBOOL));
+ try std.testing.expectEqual(@as(usize, 8), @sizeOf(ck.CK_ULONG));
+ try std.testing.expectEqual(@as(usize, 8), @sizeOf(ck.CK_RV));
+ try std.testing.expectEqual(@as(usize, 8), @sizeOf(ck.CK_SESSION_HANDLE));
+ try std.testing.expectEqual(@as(usize, 8), @sizeOf(ck.CK_OBJECT_HANDLE));
+}
+
+test "CK_VERSION is two packed bytes" {
+ try std.testing.expectEqual(@as(usize, 2), @sizeOf(ck.CK_VERSION));
+ try std.testing.expectEqual(@as(usize, 0), @offsetOf(ck.CK_VERSION, "major"));
+ try std.testing.expectEqual(@as(usize, 1), @offsetOf(ck.CK_VERSION, "minor"));
+}
+
+test "CK_ATTRIBUTE layout (type, pValue, ulValueLen)" {
+ try std.testing.expectEqual(@as(usize, 24), @sizeOf(ck.CK_ATTRIBUTE));
+ try std.testing.expectEqual(@as(usize, 0), @offsetOf(ck.CK_ATTRIBUTE, "type"));
+ try std.testing.expectEqual(@as(usize, 8), @offsetOf(ck.CK_ATTRIBUTE, "pValue"));
+ try std.testing.expectEqual(@as(usize, 16), @offsetOf(ck.CK_ATTRIBUTE, "ulValueLen"));
+}
+
+test "CK_MECHANISM layout" {
+ try std.testing.expectEqual(@as(usize, 24), @sizeOf(ck.CK_MECHANISM));
+ try std.testing.expectEqual(@as(usize, 0), @offsetOf(ck.CK_MECHANISM, "mechanism"));
+ try std.testing.expectEqual(@as(usize, 8), @offsetOf(ck.CK_MECHANISM, "pParameter"));
+ try std.testing.expectEqual(@as(usize, 16), @offsetOf(ck.CK_MECHANISM, "ulParameterLen"));
+}
+
+test "CK_INFO natural-alignment layout" {
+ try std.testing.expectEqual(@as(usize, 40), @offsetOf(ck.CK_INFO, "flags"));
+ try std.testing.expectEqual(@as(usize, 88), @sizeOf(ck.CK_INFO));
+}
+
+test "CK_TOKEN_INFO natural-alignment layout" {
+ try std.testing.expectEqual(@as(usize, 96), @offsetOf(ck.CK_TOKEN_INFO, "flags"));
+ try std.testing.expectEqual(@as(usize, 208), @sizeOf(ck.CK_TOKEN_INFO));
+}
+
+test "CK_FUNCTION_LIST is version + 68 pointers in canonical order" {
+ try std.testing.expectEqual(69 * ptr, @sizeOf(ck.CK_FUNCTION_LIST));
+ try std.testing.expectEqual(@as(usize, 0), @offsetOf(ck.CK_FUNCTION_LIST, "version"));
+ try std.testing.expectEqual(ptr, @offsetOf(ck.CK_FUNCTION_LIST, "C_Initialize"));
+ try std.testing.expectEqual(5 * ptr, @offsetOf(ck.CK_FUNCTION_LIST, "C_GetSlotList"));
+ try std.testing.expectEqual(68 * ptr, @offsetOf(ck.CK_FUNCTION_LIST, "C_WaitForSlotEvent"));
+}
+
+test "key return codes have canonical values" {
+ try std.testing.expectEqual(@as(ck.CK_RV, 0x00000000), ck.CKR_OK);
+ try std.testing.expectEqual(@as(ck.CK_RV, 0x00000054), ck.CKR_FUNCTION_NOT_SUPPORTED);
+ try std.testing.expectEqual(@as(ck.CK_RV, 0x00000150), ck.CKR_BUFFER_TOO_SMALL);
+ try std.testing.expectEqual(@as(ck.CK_RV, 0x00000190), ck.CKR_CRYPTOKI_NOT_INITIALIZED);
+}
+
+fn expectSameLayout(comptime A: type, comptime B: type) !void {
+ try std.testing.expectEqual(@sizeOf(A), @sizeOf(B));
+ try std.testing.expectEqual(@alignOf(A), @alignOf(B));
+ const fa = @typeInfo(A).@"struct".fields;
+ const fb = @typeInfo(B).@"struct".fields;
+ try std.testing.expectEqual(fa.len, fb.len);
+ inline for (fa) |f| {
+ try std.testing.expectEqual(@offsetOf(A, f.name), @offsetOf(B, f.name));
+ }
+}
+
+test "hand-coded structs match OASIS-translated layout byte-for-byte" {
+ try expectSameLayout(ck.CK_VERSION, p11c.CK_VERSION);
+ try expectSameLayout(ck.CK_INFO, p11c.CK_INFO);
+ try expectSameLayout(ck.CK_SLOT_INFO, p11c.CK_SLOT_INFO);
+ try expectSameLayout(ck.CK_TOKEN_INFO, p11c.CK_TOKEN_INFO);
+ try expectSameLayout(ck.CK_SESSION_INFO, p11c.CK_SESSION_INFO);
+ try expectSameLayout(ck.CK_MECHANISM_INFO, p11c.CK_MECHANISM_INFO);
+ try expectSameLayout(ck.CK_ATTRIBUTE, p11c.CK_ATTRIBUTE);
+ try expectSameLayout(ck.CK_MECHANISM, p11c.CK_MECHANISM);
+ try expectSameLayout(ck.CK_GCM_PARAMS, p11c.CK_GCM_PARAMS);
+ try expectSameLayout(ck.CK_DATE, p11c.CK_DATE);
+ try expectSameLayout(ck.CK_C_INITIALIZE_ARGS, p11c.CK_C_INITIALIZE_ARGS);
+}
+
+test "hand-coded CK_FUNCTION_LIST matches OASIS 68-entry order and size" {
+ try expectSameLayout(ck.CK_FUNCTION_LIST, p11c.CK_FUNCTION_LIST);
+}
+
+test "every hand-coded constant equals its OASIS value" {
+ @setEvalBranchQuota(20000);
+ comptime var checked: usize = 0;
+ inline for (@typeInfo(ck).@"struct".decls) |d| {
+ if (@hasDecl(p11c, d.name)) {
+ const value = @field(ck, d.name);
+ const T = @TypeOf(value);
+ if (T == type) continue;
+ const info = @typeInfo(T);
+ if (info != .int and info != .comptime_int) continue;
+ const ours: u64 = @intCast(value);
+ const theirs: u64 = @intCast(@field(p11c, d.name));
+ if (ours != theirs) {
+ std.debug.print("constant {s}: ck=0x{X} oasis=0x{X}\n", .{ d.name, ours, theirs });
+ return error.ConstantMismatch;
+ }
+ checked += 1;
+ }
+ }
+ try std.testing.expect(checked >= 100);
+}
+
+fn fnInfo(comptime FnPtr: type) std.builtin.Type.Fn {
+ const fn_ptr = @typeInfo(FnPtr).optional.child;
+ return @typeInfo(@typeInfo(fn_ptr).pointer.child).@"fn";
+}
+
+fn expectSameFnAbi(comptime name: []const u8, comptime A: type, comptime B: type) !void {
+ const fa = fnInfo(A);
+ const fb = fnInfo(B);
+ if (fa.params.len != fb.params.len) {
+ std.debug.print("fn {s}: param count ck={d} oasis={d}\n", .{ name, fa.params.len, fb.params.len });
+ return error.FnAbiMismatch;
+ }
+ if (@sizeOf(fa.return_type.?) != @sizeOf(fb.return_type.?)) {
+ std.debug.print("fn {s}: return-type size ck={d} oasis={d}\n", .{ name, @sizeOf(fa.return_type.?), @sizeOf(fb.return_type.?) });
+ return error.FnAbiMismatch;
+ }
+ inline for (0..@min(fa.params.len, fb.params.len)) |i| {
+ const ours = fa.params[i].type.?;
+ const theirs = fb.params[i].type.?;
+ if (@sizeOf(ours) != @sizeOf(theirs) or @alignOf(ours) != @alignOf(theirs)) {
+ std.debug.print("fn {s}: param {d} ck={d}/{d} oasis={d}/{d}\n", .{ name, i, @sizeOf(ours), @alignOf(ours), @sizeOf(theirs), @alignOf(theirs) });
+ return error.FnAbiMismatch;
+ }
+ }
+}
+
+test "every CK_FUNCTION_LIST entry matches the OASIS C ABI signature" {
+ try std.testing.expectEqual(@as(usize, 3), fnInfo(ck.CK_C_GetSlotList).params.len);
+ const cf = @typeInfo(ck.CK_FUNCTION_LIST).@"struct".fields;
+ const pf = @typeInfo(p11c.CK_FUNCTION_LIST).@"struct".fields;
+ inline for (cf, pf) |a, b| {
+ comptime std.debug.assert(std.mem.eql(u8, a.name, b.name));
+ if (comptime !std.mem.eql(u8, a.name, "version")) {
+ try expectSameFnAbi(a.name, a.type, b.type);
+ }
+ }
+}
diff --git a/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/PROVENANCE.md b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/PROVENANCE.md
new file mode 100644
index 00000000..2debc9c5
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/PROVENANCE.md
@@ -0,0 +1,34 @@
+# Vendored PKCS#11 headers
+
+`pkcs11.h`, `pkcs11t.h`, and `pkcs11f.h` are the **canonical OASIS headers**, fetched
+**unmodified** from the published v2.40 errata-01 OASIS Standard:
+
+```
+https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/os/include/pkcs11-v2.40/
+```
+
+They retain their original OASIS copyright notices and are **not** covered by this
+project's license. They are vendored for one purpose only: a build-time ABI
+cross-check (`zig build test`) that proves the hand-written Cryptoki ABI in
+`src/ck.zig` matches the spec byte-for-byte.
+
+## Integrity
+
+SHA-256 of the vendored headers as fetched (verify with `sha256sum -c` after
+re-fetching from the URL above):
+
+```
+8bb7aa1aeaa328b6a39913070d6f3d2bdeb9f2c92baf27f714fbb4cbefdf4054 pkcs11.h
+5b58736b6d23f12b4d9492cd24b06b9d11056c3153afc4e89b1fe564749e71a2 pkcs11t.h
+a85adad038bfc9dad9c71377f3ed3b049ba2ac9b3f37198a372f211d210c6057 pkcs11f.h
+```
+
+`shim.h` is the only file here authored by this project. It defines the five
+caller-supplied macros the OASIS headers require (`CK_PTR`,
+`CK_DECLARE_FUNCTION`, `CK_DECLARE_FUNCTION_POINTER`, `CK_CALLBACK_FUNCTION`,
+`NULL_PTR`) and then includes `pkcs11.h`. `build.zig` runs `addTranslateC` on
+`shim.h`; `tests/abi_test.zig` then asserts `@sizeOf`/`@offsetOf`/constant
+equality between `src/ck.zig` and the translated headers.
+
+The production `.so` does **not** depend on these headers — it ships the pure
+hand-written `src/ck.zig`. These exist for regression-hardening of that file.
diff --git a/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11.h b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11.h
new file mode 100644
index 00000000..0d78dd71
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11.h
@@ -0,0 +1,265 @@
+/* Copyright (c) OASIS Open 2016. All Rights Reserved./
+ * /Distributed under the terms of the OASIS IPR Policy,
+ * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
+ * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
+ */
+
+/* Latest version of the specification:
+ * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ */
+
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 5 platform-specific macros must be defined. These
+ * macros are described below, and typical definitions for them
+ * are also given. Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a Cryptoki library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 5 macros, the packing convention
+ * for Cryptoki structures should be set. The Cryptoki
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, this might be done by using the following
+ * preprocessor directive before including pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, this might be done by using
+ * the following preprocessor directive before including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own for this. You might
+ * not need to do (or be able to do!) anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object. It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, it might be defined by:
+ *
+ * #define CK_PTR far *
+ *
+ * In a typical UNIX environment, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable Cryptoki library function declaration out of a
+ * return type and a function name. It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * );
+ *
+ * If you're using Microsoft Developer Studio 5.0 to declare a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllimport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to declare a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a Cryptoki API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name. It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a Cryptoki API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // Cryptoki API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to access
+ * functions in a Win32 Cryptoki .dll, in might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __declspec(dllimport) (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to access functions in a Win16 Cryptoki .dll, it might
+ * be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 4. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV. It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to do Win32
+ * Cryptoki development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to do Win16 development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 5. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should best be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various Cryptoki types and #define'd values are in the
+ * file pkcs11t.h.
+ */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y) x##y
+
+
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ extern CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points. That is, for
+ * each Cryptoki function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points. A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's Cryptoki version
+ * and then a whole slew of function pointers to the routines in
+ * the library. This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ __PASTE(CK_,name) name;
+
+struct CK_FUNCTION_LIST {
+
+ CK_VERSION version; /* Cryptoki version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes.
+ */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKCS11_H_ */
+
diff --git a/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11f.h b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11f.h
new file mode 100644
index 00000000..ed90affc
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11f.h
@@ -0,0 +1,939 @@
+/* Copyright (c) OASIS Open 2016. All Rights Reserved./
+ * /Distributed under the terms of the OASIS IPR Policy,
+ * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
+ * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
+ */
+
+/* Latest version of the specification:
+ * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ */
+
+/* This header file contains pretty much everything about all the
+ * Cryptoki function prototypes. Because this information is
+ * used for more than just declaring function prototypes, the
+ * order of the functions appearing herein is important, and
+ * should not be altered.
+ */
+
+/* General-purpose */
+
+/* C_Initialize initializes the Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets
+ * cast to CK_C_INITIALIZE_ARGS_PTR
+ * and dereferenced
+ */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * Cryptoki library.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about Cryptoki. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_INFO_PTR pInfo /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to
+ * function list
+ */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_BBOOL tokenPresent, /* only slots with tokens */
+ CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */
+ CK_ULONG_PTR pulCount /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the ID of the slot */
+ CK_SLOT_INFO_PTR pInfo /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_TOKEN_INFO_PTR pInfo /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of token's slot */
+ CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */
+ CK_ULONG_PTR pulCount /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_MECHANISM_TYPE type, /* type of mechanism */
+ CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */
+ CK_ULONG ulPinLen, /* length in bytes of the PIN */
+ CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */
+ CK_ULONG ulPinLen /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pOldPin, /* the old PIN */
+ CK_ULONG ulOldLen, /* length of the old PIN */
+ CK_UTF8CHAR_PTR pNewPin, /* the new PIN */
+ CK_ULONG ulNewLen /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the slot's ID */
+ CK_FLAGS flags, /* from CK_SESSION_INFO */
+ CK_VOID_PTR pApplication, /* passed to callback */
+ CK_NOTIFY Notify, /* callback function */
+ CK_SESSION_HANDLE_PTR phSession /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_SESSION_INFO_PTR pInfo /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* gets state */
+ CK_ULONG_PTR pulOperationStateLen /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* holds state */
+ CK_ULONG ulOperationStateLen, /* holds state length */
+ CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */
+ CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_USER_TYPE userType, /* the user type */
+ CK_UTF8CHAR_PTR pPin, /* the user's PIN */
+ CK_ULONG ulPinLen /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new object */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ULONG_PTR pulSize /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */
+ CK_ULONG ulCount /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */
+ CK_ULONG ulMaxObjectCount, /* max handles to get */
+ CK_ULONG_PTR pulObjectCount /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pData, /* the plaintext data */
+ CK_ULONG ulDataLen, /* bytes of plaintext */
+ CK_BYTE_PTR pEncryptedData, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext data len */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_BYTE_PTR pLastEncryptedPart, /* last c-text */
+ CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedData, /* ciphertext */
+ CK_ULONG ulEncryptedDataLen, /* ciphertext length */
+ CK_BYTE_PTR pData, /* gets plaintext */
+ CK_ULONG_PTR pulDataLen /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* encrypted data */
+ CK_ULONG ulEncryptedPartLen, /* input length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pLastPart, /* gets plaintext */
+ CK_ULONG_PTR pulLastPartLen /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* data to be digested */
+ CK_ULONG ulDataLen, /* bytes of data to digest */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* data to be digested */
+ CK_ULONG ulPartLen /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hKey /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ * signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* the data to sign */
+ CK_ULONG ulPartLen /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation,
+ * returning the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ * cannot be recovered from the signature (e.g. DSA).
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* signed data */
+ CK_ULONG ulDataLen, /* length of signed data */
+ CK_BYTE_PTR pSignature, /* signature */
+ CK_ULONG ulSignatureLen /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* signed data */
+ CK_ULONG ulPartLen /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature.
+ */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen, /* signature length */
+ CK_BYTE_PTR pData, /* gets signed data */
+ CK_ULONG_PTR pulDataLen /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key generation mech. */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new key */
+ CK_ULONG ulCount, /* # of attrs in template */
+ CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_MECHANISM_PTR pMechanism, /* key-gen mech. */
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template for pub. key */
+ CK_ULONG ulPublicKeyAttributeCount, /* # pub. attrs. */
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template for priv. key */
+ CK_ULONG ulPrivateKeyAttributeCount, /* # priv. attrs. */
+ CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. key handle */
+ CK_OBJECT_HANDLE_PTR phPrivateKey /* gets priv. key handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */
+ CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */
+ CK_OBJECT_HANDLE hKey, /* key to be wrapped */
+ CK_BYTE_PTR pWrappedKey, /* gets wrapped key */
+ CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */
+ CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */
+ CK_BYTE_PTR pWrappedKey, /* the wrapped key */
+ CK_ULONG ulWrappedKeyLen, /* wrapped key len */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object.
+ */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */
+ CK_OBJECT_HANDLE hBaseKey, /* base key */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator.
+ */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSeed, /* the seed material */
+ CK_ULONG ulSeedLen /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR RandomData, /* receives the random data */
+ CK_ULONG ulRandomLen /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application.
+ */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel.
+ */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur.
+ */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FLAGS flags, /* blocking/nonblocking flag */
+ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */
+ CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
diff --git a/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11t.h b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11t.h
new file mode 100644
index 00000000..c13e67cf
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/pkcs11t.h
@@ -0,0 +1,2003 @@
+/* Copyright (c) OASIS Open 2016. All Rights Reserved./
+ * /Distributed under the terms of the OASIS IPR Policy,
+ * [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
+ * IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
+ */
+
+/* Latest version of the specification:
+ * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
+ */
+
+/* See top of pkcs11.h for information about the macros that
+ * must be defined and the structure-packing conventions that
+ * must be set before including this file.
+ */
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CRYPTOKI_VERSION_MAJOR 2
+#define CRYPTOKI_VERSION_MINOR 40
+#define CRYPTOKI_VERSION_AMENDMENT 0
+
+#define CK_TRUE 1
+#define CK_FALSE 0
+
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE CK_FALSE
+#endif
+#ifndef TRUE
+#define TRUE CK_TRUE
+#endif
+#endif
+
+/* an unsigned 8-bit value */
+typedef unsigned char CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+typedef long int CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION (~0UL)
+#define CK_EFFECTIVELY_INFINITE 0UL
+
+
+typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+typedef CK_CHAR CK_PTR CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR;
+typedef CK_ULONG CK_PTR CK_ULONG_PTR;
+typedef void CK_PTR CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session
+ * handle or object handle
+ */
+#define CK_INVALID_HANDLE 0UL
+
+
+typedef struct CK_VERSION {
+ CK_BYTE major; /* integer portion of version number */
+ CK_BYTE minor; /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+ CK_VERSION cryptokiVersion; /* Cryptoki interface ver */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags; /* must be zero */
+ CK_UTF8CHAR libraryDescription[32]; /* blank padded */
+ CK_VERSION libraryVersion; /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * Cryptoki provides to an application
+ */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER 0UL
+#define CKN_OTP_CHANGED 1UL
+
+typedef CK_ULONG CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+ CK_UTF8CHAR slotDescription[64]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags;
+
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_TOKEN_PRESENT 0x00000001UL /* a token is there */
+#define CKF_REMOVABLE_DEVICE 0x00000002UL /* removable devices*/
+#define CKF_HW_SLOT 0x00000004UL /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+ CK_UTF8CHAR label[32]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_UTF8CHAR model[16]; /* blank padded */
+ CK_CHAR serialNumber[16]; /* blank padded */
+ CK_FLAGS flags; /* see below */
+
+ CK_ULONG ulMaxSessionCount; /* max open sessions */
+ CK_ULONG ulSessionCount; /* sess. now open */
+ CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */
+ CK_ULONG ulRwSessionCount; /* R/W sess. now open */
+ CK_ULONG ulMaxPinLen; /* in bytes */
+ CK_ULONG ulMinPinLen; /* in bytes */
+ CK_ULONG ulTotalPublicMemory; /* in bytes */
+ CK_ULONG ulFreePublicMemory; /* in bytes */
+ CK_ULONG ulTotalPrivateMemory; /* in bytes */
+ CK_ULONG ulFreePrivateMemory; /* in bytes */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+ CK_CHAR utcTime[16]; /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RNG 0x00000001UL /* has random # generator */
+#define CKF_WRITE_PROTECTED 0x00000002UL /* token is write-protected */
+#define CKF_LOGIN_REQUIRED 0x00000004UL /* user must login */
+#define CKF_USER_PIN_INITIALIZED 0x00000008UL /* normal user's PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED. If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state
+ */
+#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020UL
+
+/* CKF_CLOCK_ON_TOKEN. If it is set, that means
+ * that the token has some sort of clock. The time on that
+ * clock is returned in the token info structure
+ */
+#define CKF_CLOCK_ON_TOKEN 0x00000040UL
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH. If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the Cryptoki library itself
+ */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL
+
+/* CKF_DUAL_CRYPTO_OPERATIONS. If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign)
+ */
+#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200UL
+
+/* CKF_TOKEN_INITIALIZED. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized.
+ */
+#define CKF_TOKEN_INITIALIZED 0x00000400UL
+
+/* CKF_SECONDARY_AUTHENTICATION. If it is
+ * true, the token supports secondary authentication for
+ * private key objects.
+ */
+#define CKF_SECONDARY_AUTHENTICATION 0x00000800UL
+
+/* CKF_USER_PIN_COUNT_LOW. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication.
+ */
+#define CKF_USER_PIN_COUNT_LOW 0x00010000UL
+
+/* CKF_USER_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect user PIN will it to become locked.
+ */
+#define CKF_USER_PIN_FINAL_TRY 0x00020000UL
+
+/* CKF_USER_PIN_LOCKED. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible.
+ */
+#define CKF_USER_PIN_LOCKED 0x00040000UL
+
+/* CKF_USER_PIN_TO_BE_CHANGED. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000UL
+
+/* CKF_SO_PIN_COUNT_LOW. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication.
+ */
+#define CKF_SO_PIN_COUNT_LOW 0x00100000UL
+
+/* CKF_SO_PIN_FINAL_TRY. If it is true,
+ * supplying an incorrect SO PIN will it to become locked.
+ */
+#define CKF_SO_PIN_FINAL_TRY 0x00200000UL
+
+/* CKF_SO_PIN_LOCKED. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED 0x00400000UL
+
+/* CKF_SO_PIN_TO_BE_CHANGED. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card.
+ */
+#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000UL
+
+#define CKF_ERROR_STATE 0x01000000UL
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a Cryptoki-assigned value that
+ * identifies a session
+ */
+typedef CK_ULONG CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of Cryptoki users */
+typedef CK_ULONG CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO 0UL
+/* Normal user */
+#define CKU_USER 1UL
+/* Context specific */
+#define CKU_CONTEXT_SPECIFIC 2UL
+
+/* CK_STATE enumerates the session states */
+typedef CK_ULONG CK_STATE;
+#define CKS_RO_PUBLIC_SESSION 0UL
+#define CKS_RO_USER_FUNCTIONS 1UL
+#define CKS_RW_PUBLIC_SESSION 2UL
+#define CKS_RW_USER_FUNCTIONS 3UL
+#define CKS_RW_SO_FUNCTIONS 4UL
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+ CK_SLOT_ID slotID;
+ CK_STATE state;
+ CK_FLAGS flags; /* see below */
+ CK_ULONG ulDeviceError; /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RW_SESSION 0x00000002UL /* session is r/w */
+#define CKF_SERIAL_SESSION 0x00000004UL /* no parallel */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object
+ */
+typedef CK_ULONG CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that Cryptoki recognizes. It is defined
+ * as follows:
+ */
+typedef CK_ULONG CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+#define CKO_DATA 0x00000000UL
+#define CKO_CERTIFICATE 0x00000001UL
+#define CKO_PUBLIC_KEY 0x00000002UL
+#define CKO_PRIVATE_KEY 0x00000003UL
+#define CKO_SECRET_KEY 0x00000004UL
+#define CKO_HW_FEATURE 0x00000005UL
+#define CKO_DOMAIN_PARAMETERS 0x00000006UL
+#define CKO_MECHANISM 0x00000007UL
+#define CKO_OTP_KEY 0x00000008UL
+
+#define CKO_VENDOR_DEFINED 0x80000000UL
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type
+ * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE.
+ */
+typedef CK_ULONG CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+#define CKH_MONOTONIC_COUNTER 0x00000001UL
+#define CKH_CLOCK 0x00000002UL
+#define CKH_USER_INTERFACE 0x00000003UL
+#define CKH_VENDOR_DEFINED 0x80000000UL
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+typedef CK_ULONG CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA 0x00000000UL
+#define CKK_DSA 0x00000001UL
+#define CKK_DH 0x00000002UL
+#define CKK_ECDSA 0x00000003UL /* Deprecated */
+#define CKK_EC 0x00000003UL
+#define CKK_X9_42_DH 0x00000004UL
+#define CKK_KEA 0x00000005UL
+#define CKK_GENERIC_SECRET 0x00000010UL
+#define CKK_RC2 0x00000011UL
+#define CKK_RC4 0x00000012UL
+#define CKK_DES 0x00000013UL
+#define CKK_DES2 0x00000014UL
+#define CKK_DES3 0x00000015UL
+#define CKK_CAST 0x00000016UL
+#define CKK_CAST3 0x00000017UL
+#define CKK_CAST5 0x00000018UL /* Deprecated */
+#define CKK_CAST128 0x00000018UL
+#define CKK_RC5 0x00000019UL
+#define CKK_IDEA 0x0000001AUL
+#define CKK_SKIPJACK 0x0000001BUL
+#define CKK_BATON 0x0000001CUL
+#define CKK_JUNIPER 0x0000001DUL
+#define CKK_CDMF 0x0000001EUL
+#define CKK_AES 0x0000001FUL
+#define CKK_BLOWFISH 0x00000020UL
+#define CKK_TWOFISH 0x00000021UL
+#define CKK_SECURID 0x00000022UL
+#define CKK_HOTP 0x00000023UL
+#define CKK_ACTI 0x00000024UL
+#define CKK_CAMELLIA 0x00000025UL
+#define CKK_ARIA 0x00000026UL
+
+#define CKK_MD5_HMAC 0x00000027UL
+#define CKK_SHA_1_HMAC 0x00000028UL
+#define CKK_RIPEMD128_HMAC 0x00000029UL
+#define CKK_RIPEMD160_HMAC 0x0000002AUL
+#define CKK_SHA256_HMAC 0x0000002BUL
+#define CKK_SHA384_HMAC 0x0000002CUL
+#define CKK_SHA512_HMAC 0x0000002DUL
+#define CKK_SHA224_HMAC 0x0000002EUL
+
+#define CKK_SEED 0x0000002FUL
+#define CKK_GOSTR3410 0x00000030UL
+#define CKK_GOSTR3411 0x00000031UL
+#define CKK_GOST28147 0x00000032UL
+
+
+
+#define CKK_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type
+ */
+typedef CK_ULONG CK_CERTIFICATE_TYPE;
+
+#define CK_CERTIFICATE_CATEGORY_UNSPECIFIED 0UL
+#define CK_CERTIFICATE_CATEGORY_TOKEN_USER 1UL
+#define CK_CERTIFICATE_CATEGORY_AUTHORITY 2UL
+#define CK_CERTIFICATE_CATEGORY_OTHER_ENTITY 3UL
+
+#define CK_SECURITY_DOMAIN_UNSPECIFIED 0UL
+#define CK_SECURITY_DOMAIN_MANUFACTURER 1UL
+#define CK_SECURITY_DOMAIN_OPERATOR 2UL
+#define CK_SECURITY_DOMAIN_THIRD_PARTY 3UL
+
+
+/* The following certificate types are defined: */
+#define CKC_X_509 0x00000000UL
+#define CKC_X_509_ATTR_CERT 0x00000001UL
+#define CKC_WTLS 0x00000002UL
+#define CKC_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type
+ */
+typedef CK_ULONG CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+ * consists of an array of values.
+ */
+#define CKF_ARRAY_ATTRIBUTE 0x40000000UL
+
+/* The following OTP-related defines relate to the CKA_OTP_FORMAT attribute */
+#define CK_OTP_FORMAT_DECIMAL 0UL
+#define CK_OTP_FORMAT_HEXADECIMAL 1UL
+#define CK_OTP_FORMAT_ALPHANUMERIC 2UL
+#define CK_OTP_FORMAT_BINARY 3UL
+
+/* The following OTP-related defines relate to the CKA_OTP_..._REQUIREMENT
+ * attributes
+ */
+#define CK_OTP_PARAM_IGNORED 0UL
+#define CK_OTP_PARAM_OPTIONAL 1UL
+#define CK_OTP_PARAM_MANDATORY 2UL
+
+/* The following attribute types are defined: */
+#define CKA_CLASS 0x00000000UL
+#define CKA_TOKEN 0x00000001UL
+#define CKA_PRIVATE 0x00000002UL
+#define CKA_LABEL 0x00000003UL
+#define CKA_APPLICATION 0x00000010UL
+#define CKA_VALUE 0x00000011UL
+#define CKA_OBJECT_ID 0x00000012UL
+#define CKA_CERTIFICATE_TYPE 0x00000080UL
+#define CKA_ISSUER 0x00000081UL
+#define CKA_SERIAL_NUMBER 0x00000082UL
+#define CKA_AC_ISSUER 0x00000083UL
+#define CKA_OWNER 0x00000084UL
+#define CKA_ATTR_TYPES 0x00000085UL
+#define CKA_TRUSTED 0x00000086UL
+#define CKA_CERTIFICATE_CATEGORY 0x00000087UL
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088UL
+#define CKA_URL 0x00000089UL
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008AUL
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008BUL
+#define CKA_NAME_HASH_ALGORITHM 0x0000008CUL
+#define CKA_CHECK_VALUE 0x00000090UL
+
+#define CKA_KEY_TYPE 0x00000100UL
+#define CKA_SUBJECT 0x00000101UL
+#define CKA_ID 0x00000102UL
+#define CKA_SENSITIVE 0x00000103UL
+#define CKA_ENCRYPT 0x00000104UL
+#define CKA_DECRYPT 0x00000105UL
+#define CKA_WRAP 0x00000106UL
+#define CKA_UNWRAP 0x00000107UL
+#define CKA_SIGN 0x00000108UL
+#define CKA_SIGN_RECOVER 0x00000109UL
+#define CKA_VERIFY 0x0000010AUL
+#define CKA_VERIFY_RECOVER 0x0000010BUL
+#define CKA_DERIVE 0x0000010CUL
+#define CKA_START_DATE 0x00000110UL
+#define CKA_END_DATE 0x00000111UL
+#define CKA_MODULUS 0x00000120UL
+#define CKA_MODULUS_BITS 0x00000121UL
+#define CKA_PUBLIC_EXPONENT 0x00000122UL
+#define CKA_PRIVATE_EXPONENT 0x00000123UL
+#define CKA_PRIME_1 0x00000124UL
+#define CKA_PRIME_2 0x00000125UL
+#define CKA_EXPONENT_1 0x00000126UL
+#define CKA_EXPONENT_2 0x00000127UL
+#define CKA_COEFFICIENT 0x00000128UL
+#define CKA_PUBLIC_KEY_INFO 0x00000129UL
+#define CKA_PRIME 0x00000130UL
+#define CKA_SUBPRIME 0x00000131UL
+#define CKA_BASE 0x00000132UL
+
+#define CKA_PRIME_BITS 0x00000133UL
+#define CKA_SUBPRIME_BITS 0x00000134UL
+#define CKA_SUB_PRIME_BITS CKA_SUBPRIME_BITS
+
+#define CKA_VALUE_BITS 0x00000160UL
+#define CKA_VALUE_LEN 0x00000161UL
+#define CKA_EXTRACTABLE 0x00000162UL
+#define CKA_LOCAL 0x00000163UL
+#define CKA_NEVER_EXTRACTABLE 0x00000164UL
+#define CKA_ALWAYS_SENSITIVE 0x00000165UL
+#define CKA_KEY_GEN_MECHANISM 0x00000166UL
+
+#define CKA_MODIFIABLE 0x00000170UL
+#define CKA_COPYABLE 0x00000171UL
+
+#define CKA_DESTROYABLE 0x00000172UL
+
+#define CKA_ECDSA_PARAMS 0x00000180UL /* Deprecated */
+#define CKA_EC_PARAMS 0x00000180UL
+
+#define CKA_EC_POINT 0x00000181UL
+
+#define CKA_SECONDARY_AUTH 0x00000200UL /* Deprecated */
+#define CKA_AUTH_PIN_FLAGS 0x00000201UL /* Deprecated */
+
+#define CKA_ALWAYS_AUTHENTICATE 0x00000202UL
+
+#define CKA_WRAP_WITH_TRUSTED 0x00000210UL
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000211UL)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000212UL)
+#define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000213UL)
+
+#define CKA_OTP_FORMAT 0x00000220UL
+#define CKA_OTP_LENGTH 0x00000221UL
+#define CKA_OTP_TIME_INTERVAL 0x00000222UL
+#define CKA_OTP_USER_FRIENDLY_MODE 0x00000223UL
+#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224UL
+#define CKA_OTP_TIME_REQUIREMENT 0x00000225UL
+#define CKA_OTP_COUNTER_REQUIREMENT 0x00000226UL
+#define CKA_OTP_PIN_REQUIREMENT 0x00000227UL
+#define CKA_OTP_COUNTER 0x0000022EUL
+#define CKA_OTP_TIME 0x0000022FUL
+#define CKA_OTP_USER_IDENTIFIER 0x0000022AUL
+#define CKA_OTP_SERVICE_IDENTIFIER 0x0000022BUL
+#define CKA_OTP_SERVICE_LOGO 0x0000022CUL
+#define CKA_OTP_SERVICE_LOGO_TYPE 0x0000022DUL
+
+#define CKA_GOSTR3410_PARAMS 0x00000250UL
+#define CKA_GOSTR3411_PARAMS 0x00000251UL
+#define CKA_GOST28147_PARAMS 0x00000252UL
+
+#define CKA_HW_FEATURE_TYPE 0x00000300UL
+#define CKA_RESET_ON_INIT 0x00000301UL
+#define CKA_HAS_RESET 0x00000302UL
+
+#define CKA_PIXEL_X 0x00000400UL
+#define CKA_PIXEL_Y 0x00000401UL
+#define CKA_RESOLUTION 0x00000402UL
+#define CKA_CHAR_ROWS 0x00000403UL
+#define CKA_CHAR_COLUMNS 0x00000404UL
+#define CKA_COLOR 0x00000405UL
+#define CKA_BITS_PER_PIXEL 0x00000406UL
+#define CKA_CHAR_SETS 0x00000480UL
+#define CKA_ENCODING_METHODS 0x00000481UL
+#define CKA_MIME_TYPES 0x00000482UL
+#define CKA_MECHANISM_TYPE 0x00000500UL
+#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501UL
+#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502UL
+#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503UL
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE|0x00000600UL)
+
+#define CKA_VENDOR_DEFINED 0x80000000UL
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute
+ */
+typedef struct CK_ATTRIBUTE {
+ CK_ATTRIBUTE_TYPE type;
+ CK_VOID_PTR pValue;
+ CK_ULONG ulValueLen; /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+ CK_CHAR year[4]; /* the year ("1900" - "9999") */
+ CK_CHAR month[2]; /* the month ("01" - "12") */
+ CK_CHAR day[2]; /* the day ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type
+ */
+typedef CK_ULONG CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL
+#define CKM_RSA_PKCS 0x00000001UL
+#define CKM_RSA_9796 0x00000002UL
+#define CKM_RSA_X_509 0x00000003UL
+
+#define CKM_MD2_RSA_PKCS 0x00000004UL
+#define CKM_MD5_RSA_PKCS 0x00000005UL
+#define CKM_SHA1_RSA_PKCS 0x00000006UL
+
+#define CKM_RIPEMD128_RSA_PKCS 0x00000007UL
+#define CKM_RIPEMD160_RSA_PKCS 0x00000008UL
+#define CKM_RSA_PKCS_OAEP 0x00000009UL
+
+#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000AUL
+#define CKM_RSA_X9_31 0x0000000BUL
+#define CKM_SHA1_RSA_X9_31 0x0000000CUL
+#define CKM_RSA_PKCS_PSS 0x0000000DUL
+#define CKM_SHA1_RSA_PKCS_PSS 0x0000000EUL
+
+#define CKM_DSA_KEY_PAIR_GEN 0x00000010UL
+#define CKM_DSA 0x00000011UL
+#define CKM_DSA_SHA1 0x00000012UL
+#define CKM_DSA_SHA224 0x00000013UL
+#define CKM_DSA_SHA256 0x00000014UL
+#define CKM_DSA_SHA384 0x00000015UL
+#define CKM_DSA_SHA512 0x00000016UL
+
+#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL
+#define CKM_DH_PKCS_DERIVE 0x00000021UL
+
+#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030UL
+#define CKM_X9_42_DH_DERIVE 0x00000031UL
+#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032UL
+#define CKM_X9_42_MQV_DERIVE 0x00000033UL
+
+#define CKM_SHA256_RSA_PKCS 0x00000040UL
+#define CKM_SHA384_RSA_PKCS 0x00000041UL
+#define CKM_SHA512_RSA_PKCS 0x00000042UL
+#define CKM_SHA256_RSA_PKCS_PSS 0x00000043UL
+#define CKM_SHA384_RSA_PKCS_PSS 0x00000044UL
+#define CKM_SHA512_RSA_PKCS_PSS 0x00000045UL
+
+#define CKM_SHA224_RSA_PKCS 0x00000046UL
+#define CKM_SHA224_RSA_PKCS_PSS 0x00000047UL
+
+#define CKM_SHA512_224 0x00000048UL
+#define CKM_SHA512_224_HMAC 0x00000049UL
+#define CKM_SHA512_224_HMAC_GENERAL 0x0000004AUL
+#define CKM_SHA512_224_KEY_DERIVATION 0x0000004BUL
+#define CKM_SHA512_256 0x0000004CUL
+#define CKM_SHA512_256_HMAC 0x0000004DUL
+#define CKM_SHA512_256_HMAC_GENERAL 0x0000004EUL
+#define CKM_SHA512_256_KEY_DERIVATION 0x0000004FUL
+
+#define CKM_SHA512_T 0x00000050UL
+#define CKM_SHA512_T_HMAC 0x00000051UL
+#define CKM_SHA512_T_HMAC_GENERAL 0x00000052UL
+#define CKM_SHA512_T_KEY_DERIVATION 0x00000053UL
+
+#define CKM_RC2_KEY_GEN 0x00000100UL
+#define CKM_RC2_ECB 0x00000101UL
+#define CKM_RC2_CBC 0x00000102UL
+#define CKM_RC2_MAC 0x00000103UL
+
+#define CKM_RC2_MAC_GENERAL 0x00000104UL
+#define CKM_RC2_CBC_PAD 0x00000105UL
+
+#define CKM_RC4_KEY_GEN 0x00000110UL
+#define CKM_RC4 0x00000111UL
+#define CKM_DES_KEY_GEN 0x00000120UL
+#define CKM_DES_ECB 0x00000121UL
+#define CKM_DES_CBC 0x00000122UL
+#define CKM_DES_MAC 0x00000123UL
+
+#define CKM_DES_MAC_GENERAL 0x00000124UL
+#define CKM_DES_CBC_PAD 0x00000125UL
+
+#define CKM_DES2_KEY_GEN 0x00000130UL
+#define CKM_DES3_KEY_GEN 0x00000131UL
+#define CKM_DES3_ECB 0x00000132UL
+#define CKM_DES3_CBC 0x00000133UL
+#define CKM_DES3_MAC 0x00000134UL
+
+#define CKM_DES3_MAC_GENERAL 0x00000135UL
+#define CKM_DES3_CBC_PAD 0x00000136UL
+#define CKM_DES3_CMAC_GENERAL 0x00000137UL
+#define CKM_DES3_CMAC 0x00000138UL
+#define CKM_CDMF_KEY_GEN 0x00000140UL
+#define CKM_CDMF_ECB 0x00000141UL
+#define CKM_CDMF_CBC 0x00000142UL
+#define CKM_CDMF_MAC 0x00000143UL
+#define CKM_CDMF_MAC_GENERAL 0x00000144UL
+#define CKM_CDMF_CBC_PAD 0x00000145UL
+
+#define CKM_DES_OFB64 0x00000150UL
+#define CKM_DES_OFB8 0x00000151UL
+#define CKM_DES_CFB64 0x00000152UL
+#define CKM_DES_CFB8 0x00000153UL
+
+#define CKM_MD2 0x00000200UL
+
+#define CKM_MD2_HMAC 0x00000201UL
+#define CKM_MD2_HMAC_GENERAL 0x00000202UL
+
+#define CKM_MD5 0x00000210UL
+
+#define CKM_MD5_HMAC 0x00000211UL
+#define CKM_MD5_HMAC_GENERAL 0x00000212UL
+
+#define CKM_SHA_1 0x00000220UL
+
+#define CKM_SHA_1_HMAC 0x00000221UL
+#define CKM_SHA_1_HMAC_GENERAL 0x00000222UL
+
+#define CKM_RIPEMD128 0x00000230UL
+#define CKM_RIPEMD128_HMAC 0x00000231UL
+#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232UL
+#define CKM_RIPEMD160 0x00000240UL
+#define CKM_RIPEMD160_HMAC 0x00000241UL
+#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242UL
+
+#define CKM_SHA256 0x00000250UL
+#define CKM_SHA256_HMAC 0x00000251UL
+#define CKM_SHA256_HMAC_GENERAL 0x00000252UL
+#define CKM_SHA224 0x00000255UL
+#define CKM_SHA224_HMAC 0x00000256UL
+#define CKM_SHA224_HMAC_GENERAL 0x00000257UL
+#define CKM_SHA384 0x00000260UL
+#define CKM_SHA384_HMAC 0x00000261UL
+#define CKM_SHA384_HMAC_GENERAL 0x00000262UL
+#define CKM_SHA512 0x00000270UL
+#define CKM_SHA512_HMAC 0x00000271UL
+#define CKM_SHA512_HMAC_GENERAL 0x00000272UL
+#define CKM_SECURID_KEY_GEN 0x00000280UL
+#define CKM_SECURID 0x00000282UL
+#define CKM_HOTP_KEY_GEN 0x00000290UL
+#define CKM_HOTP 0x00000291UL
+#define CKM_ACTI 0x000002A0UL
+#define CKM_ACTI_KEY_GEN 0x000002A1UL
+
+#define CKM_CAST_KEY_GEN 0x00000300UL
+#define CKM_CAST_ECB 0x00000301UL
+#define CKM_CAST_CBC 0x00000302UL
+#define CKM_CAST_MAC 0x00000303UL
+#define CKM_CAST_MAC_GENERAL 0x00000304UL
+#define CKM_CAST_CBC_PAD 0x00000305UL
+#define CKM_CAST3_KEY_GEN 0x00000310UL
+#define CKM_CAST3_ECB 0x00000311UL
+#define CKM_CAST3_CBC 0x00000312UL
+#define CKM_CAST3_MAC 0x00000313UL
+#define CKM_CAST3_MAC_GENERAL 0x00000314UL
+#define CKM_CAST3_CBC_PAD 0x00000315UL
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST5_KEY_GEN 0x00000320UL
+#define CKM_CAST128_KEY_GEN 0x00000320UL
+#define CKM_CAST5_ECB 0x00000321UL
+#define CKM_CAST128_ECB 0x00000321UL
+#define CKM_CAST5_CBC 0x00000322UL /* Deprecated */
+#define CKM_CAST128_CBC 0x00000322UL
+#define CKM_CAST5_MAC 0x00000323UL /* Deprecated */
+#define CKM_CAST128_MAC 0x00000323UL
+#define CKM_CAST5_MAC_GENERAL 0x00000324UL /* Deprecated */
+#define CKM_CAST128_MAC_GENERAL 0x00000324UL
+#define CKM_CAST5_CBC_PAD 0x00000325UL /* Deprecated */
+#define CKM_CAST128_CBC_PAD 0x00000325UL
+#define CKM_RC5_KEY_GEN 0x00000330UL
+#define CKM_RC5_ECB 0x00000331UL
+#define CKM_RC5_CBC 0x00000332UL
+#define CKM_RC5_MAC 0x00000333UL
+#define CKM_RC5_MAC_GENERAL 0x00000334UL
+#define CKM_RC5_CBC_PAD 0x00000335UL
+#define CKM_IDEA_KEY_GEN 0x00000340UL
+#define CKM_IDEA_ECB 0x00000341UL
+#define CKM_IDEA_CBC 0x00000342UL
+#define CKM_IDEA_MAC 0x00000343UL
+#define CKM_IDEA_MAC_GENERAL 0x00000344UL
+#define CKM_IDEA_CBC_PAD 0x00000345UL
+#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL
+#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL
+#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL
+#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL
+#define CKM_XOR_BASE_AND_DATA 0x00000364UL
+#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365UL
+#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370UL
+#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL
+#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372UL
+
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373UL
+#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374UL
+#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375UL
+#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376UL
+#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377UL
+
+#define CKM_TLS_PRF 0x00000378UL
+
+#define CKM_SSL3_MD5_MAC 0x00000380UL
+#define CKM_SSL3_SHA1_MAC 0x00000381UL
+#define CKM_MD5_KEY_DERIVATION 0x00000390UL
+#define CKM_MD2_KEY_DERIVATION 0x00000391UL
+#define CKM_SHA1_KEY_DERIVATION 0x00000392UL
+
+#define CKM_SHA256_KEY_DERIVATION 0x00000393UL
+#define CKM_SHA384_KEY_DERIVATION 0x00000394UL
+#define CKM_SHA512_KEY_DERIVATION 0x00000395UL
+#define CKM_SHA224_KEY_DERIVATION 0x00000396UL
+
+#define CKM_PBE_MD2_DES_CBC 0x000003A0UL
+#define CKM_PBE_MD5_DES_CBC 0x000003A1UL
+#define CKM_PBE_MD5_CAST_CBC 0x000003A2UL
+#define CKM_PBE_MD5_CAST3_CBC 0x000003A3UL
+#define CKM_PBE_MD5_CAST5_CBC 0x000003A4UL /* Deprecated */
+#define CKM_PBE_MD5_CAST128_CBC 0x000003A4UL
+#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5UL /* Deprecated */
+#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5UL
+#define CKM_PBE_SHA1_RC4_128 0x000003A6UL
+#define CKM_PBE_SHA1_RC4_40 0x000003A7UL
+#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8UL
+#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9UL
+#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AAUL
+#define CKM_PBE_SHA1_RC2_40_CBC 0x000003ABUL
+
+#define CKM_PKCS5_PBKD2 0x000003B0UL
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0UL
+
+#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0UL
+#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1UL
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2UL
+#define CKM_WTLS_PRF 0x000003D3UL
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4UL
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5UL
+
+#define CKM_TLS10_MAC_SERVER 0x000003D6UL
+#define CKM_TLS10_MAC_CLIENT 0x000003D7UL
+#define CKM_TLS12_MAC 0x000003D8UL
+#define CKM_TLS12_KDF 0x000003D9UL
+#define CKM_TLS12_MASTER_KEY_DERIVE 0x000003E0UL
+#define CKM_TLS12_KEY_AND_MAC_DERIVE 0x000003E1UL
+#define CKM_TLS12_MASTER_KEY_DERIVE_DH 0x000003E2UL
+#define CKM_TLS12_KEY_SAFE_DERIVE 0x000003E3UL
+#define CKM_TLS_MAC 0x000003E4UL
+#define CKM_TLS_KDF 0x000003E5UL
+
+#define CKM_KEY_WRAP_LYNKS 0x00000400UL
+#define CKM_KEY_WRAP_SET_OAEP 0x00000401UL
+
+#define CKM_CMS_SIG 0x00000500UL
+#define CKM_KIP_DERIVE 0x00000510UL
+#define CKM_KIP_WRAP 0x00000511UL
+#define CKM_KIP_MAC 0x00000512UL
+
+#define CKM_CAMELLIA_KEY_GEN 0x00000550UL
+#define CKM_CAMELLIA_ECB 0x00000551UL
+#define CKM_CAMELLIA_CBC 0x00000552UL
+#define CKM_CAMELLIA_MAC 0x00000553UL
+#define CKM_CAMELLIA_MAC_GENERAL 0x00000554UL
+#define CKM_CAMELLIA_CBC_PAD 0x00000555UL
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA 0x00000556UL
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA 0x00000557UL
+#define CKM_CAMELLIA_CTR 0x00000558UL
+
+#define CKM_ARIA_KEY_GEN 0x00000560UL
+#define CKM_ARIA_ECB 0x00000561UL
+#define CKM_ARIA_CBC 0x00000562UL
+#define CKM_ARIA_MAC 0x00000563UL
+#define CKM_ARIA_MAC_GENERAL 0x00000564UL
+#define CKM_ARIA_CBC_PAD 0x00000565UL
+#define CKM_ARIA_ECB_ENCRYPT_DATA 0x00000566UL
+#define CKM_ARIA_CBC_ENCRYPT_DATA 0x00000567UL
+
+#define CKM_SEED_KEY_GEN 0x00000650UL
+#define CKM_SEED_ECB 0x00000651UL
+#define CKM_SEED_CBC 0x00000652UL
+#define CKM_SEED_MAC 0x00000653UL
+#define CKM_SEED_MAC_GENERAL 0x00000654UL
+#define CKM_SEED_CBC_PAD 0x00000655UL
+#define CKM_SEED_ECB_ENCRYPT_DATA 0x00000656UL
+#define CKM_SEED_CBC_ENCRYPT_DATA 0x00000657UL
+
+#define CKM_SKIPJACK_KEY_GEN 0x00001000UL
+#define CKM_SKIPJACK_ECB64 0x00001001UL
+#define CKM_SKIPJACK_CBC64 0x00001002UL
+#define CKM_SKIPJACK_OFB64 0x00001003UL
+#define CKM_SKIPJACK_CFB64 0x00001004UL
+#define CKM_SKIPJACK_CFB32 0x00001005UL
+#define CKM_SKIPJACK_CFB16 0x00001006UL
+#define CKM_SKIPJACK_CFB8 0x00001007UL
+#define CKM_SKIPJACK_WRAP 0x00001008UL
+#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009UL
+#define CKM_SKIPJACK_RELAYX 0x0000100aUL
+#define CKM_KEA_KEY_PAIR_GEN 0x00001010UL
+#define CKM_KEA_KEY_DERIVE 0x00001011UL
+#define CKM_KEA_DERIVE 0x00001012UL
+#define CKM_FORTEZZA_TIMESTAMP 0x00001020UL
+#define CKM_BATON_KEY_GEN 0x00001030UL
+#define CKM_BATON_ECB128 0x00001031UL
+#define CKM_BATON_ECB96 0x00001032UL
+#define CKM_BATON_CBC128 0x00001033UL
+#define CKM_BATON_COUNTER 0x00001034UL
+#define CKM_BATON_SHUFFLE 0x00001035UL
+#define CKM_BATON_WRAP 0x00001036UL
+
+#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040UL /* Deprecated */
+#define CKM_EC_KEY_PAIR_GEN 0x00001040UL
+
+#define CKM_ECDSA 0x00001041UL
+#define CKM_ECDSA_SHA1 0x00001042UL
+#define CKM_ECDSA_SHA224 0x00001043UL
+#define CKM_ECDSA_SHA256 0x00001044UL
+#define CKM_ECDSA_SHA384 0x00001045UL
+#define CKM_ECDSA_SHA512 0x00001046UL
+
+#define CKM_ECDH1_DERIVE 0x00001050UL
+#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051UL
+#define CKM_ECMQV_DERIVE 0x00001052UL
+
+#define CKM_ECDH_AES_KEY_WRAP 0x00001053UL
+#define CKM_RSA_AES_KEY_WRAP 0x00001054UL
+
+#define CKM_JUNIPER_KEY_GEN 0x00001060UL
+#define CKM_JUNIPER_ECB128 0x00001061UL
+#define CKM_JUNIPER_CBC128 0x00001062UL
+#define CKM_JUNIPER_COUNTER 0x00001063UL
+#define CKM_JUNIPER_SHUFFLE 0x00001064UL
+#define CKM_JUNIPER_WRAP 0x00001065UL
+#define CKM_FASTHASH 0x00001070UL
+
+#define CKM_AES_KEY_GEN 0x00001080UL
+#define CKM_AES_ECB 0x00001081UL
+#define CKM_AES_CBC 0x00001082UL
+#define CKM_AES_MAC 0x00001083UL
+#define CKM_AES_MAC_GENERAL 0x00001084UL
+#define CKM_AES_CBC_PAD 0x00001085UL
+#define CKM_AES_CTR 0x00001086UL
+#define CKM_AES_GCM 0x00001087UL
+#define CKM_AES_CCM 0x00001088UL
+#define CKM_AES_CTS 0x00001089UL
+#define CKM_AES_CMAC 0x0000108AUL
+#define CKM_AES_CMAC_GENERAL 0x0000108BUL
+
+#define CKM_AES_XCBC_MAC 0x0000108CUL
+#define CKM_AES_XCBC_MAC_96 0x0000108DUL
+#define CKM_AES_GMAC 0x0000108EUL
+
+#define CKM_BLOWFISH_KEY_GEN 0x00001090UL
+#define CKM_BLOWFISH_CBC 0x00001091UL
+#define CKM_TWOFISH_KEY_GEN 0x00001092UL
+#define CKM_TWOFISH_CBC 0x00001093UL
+#define CKM_BLOWFISH_CBC_PAD 0x00001094UL
+#define CKM_TWOFISH_CBC_PAD 0x00001095UL
+
+#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100UL
+#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101UL
+#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102UL
+#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103UL
+#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104UL
+#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105UL
+
+#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200UL
+#define CKM_GOSTR3410 0x00001201UL
+#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202UL
+#define CKM_GOSTR3410_KEY_WRAP 0x00001203UL
+#define CKM_GOSTR3410_DERIVE 0x00001204UL
+#define CKM_GOSTR3411 0x00001210UL
+#define CKM_GOSTR3411_HMAC 0x00001211UL
+#define CKM_GOST28147_KEY_GEN 0x00001220UL
+#define CKM_GOST28147_ECB 0x00001221UL
+#define CKM_GOST28147 0x00001222UL
+#define CKM_GOST28147_MAC 0x00001223UL
+#define CKM_GOST28147_KEY_WRAP 0x00001224UL
+
+#define CKM_DSA_PARAMETER_GEN 0x00002000UL
+#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001UL
+#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002UL
+#define CKM_DSA_PROBABLISTIC_PARAMETER_GEN 0x00002003UL
+#define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN 0x00002004UL
+
+#define CKM_AES_OFB 0x00002104UL
+#define CKM_AES_CFB64 0x00002105UL
+#define CKM_AES_CFB8 0x00002106UL
+#define CKM_AES_CFB128 0x00002107UL
+
+#define CKM_AES_CFB1 0x00002108UL
+#define CKM_AES_KEY_WRAP 0x00002109UL /* WAS: 0x00001090 */
+#define CKM_AES_KEY_WRAP_PAD 0x0000210AUL /* WAS: 0x00001091 */
+
+#define CKM_RSA_PKCS_TPM_1_1 0x00004001UL
+#define CKM_RSA_PKCS_OAEP_TPM_1_1 0x00004002UL
+
+#define CKM_VENDOR_DEFINED 0x80000000UL
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM {
+ CK_MECHANISM_TYPE mechanism;
+ CK_VOID_PTR pParameter;
+ CK_ULONG ulParameterLen; /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism
+ */
+typedef struct CK_MECHANISM_INFO {
+ CK_ULONG ulMinKeySize;
+ CK_ULONG ulMaxKeySize;
+ CK_FLAGS flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ * Bit Flag Mask Meaning */
+#define CKF_HW 0x00000001UL /* performed by HW */
+
+/* Specify whether or not a mechanism can be used for a particular task */
+#define CKF_ENCRYPT 0x00000100UL
+#define CKF_DECRYPT 0x00000200UL
+#define CKF_DIGEST 0x00000400UL
+#define CKF_SIGN 0x00000800UL
+#define CKF_SIGN_RECOVER 0x00001000UL
+#define CKF_VERIFY 0x00002000UL
+#define CKF_VERIFY_RECOVER 0x00004000UL
+#define CKF_GENERATE 0x00008000UL
+#define CKF_GENERATE_KEY_PAIR 0x00010000UL
+#define CKF_WRAP 0x00020000UL
+#define CKF_UNWRAP 0x00040000UL
+#define CKF_DERIVE 0x00080000UL
+
+/* Describe a token's EC capabilities not available in mechanism
+ * information.
+ */
+#define CKF_EC_F_P 0x00100000UL
+#define CKF_EC_F_2M 0x00200000UL
+#define CKF_EC_ECPARAMETERS 0x00400000UL
+#define CKF_EC_NAMEDCURVE 0x00800000UL
+#define CKF_EC_UNCOMPRESS 0x01000000UL
+#define CKF_EC_COMPRESS 0x02000000UL
+
+#define CKF_EXTENSION 0x80000000UL
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+/* CK_RV is a value that identifies the return value of a
+ * Cryptoki function
+ */
+typedef CK_ULONG CK_RV;
+
+#define CKR_OK 0x00000000UL
+#define CKR_CANCEL 0x00000001UL
+#define CKR_HOST_MEMORY 0x00000002UL
+#define CKR_SLOT_ID_INVALID 0x00000003UL
+
+#define CKR_GENERAL_ERROR 0x00000005UL
+#define CKR_FUNCTION_FAILED 0x00000006UL
+
+#define CKR_ARGUMENTS_BAD 0x00000007UL
+#define CKR_NO_EVENT 0x00000008UL
+#define CKR_NEED_TO_CREATE_THREADS 0x00000009UL
+#define CKR_CANT_LOCK 0x0000000AUL
+
+#define CKR_ATTRIBUTE_READ_ONLY 0x00000010UL
+#define CKR_ATTRIBUTE_SENSITIVE 0x00000011UL
+#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012UL
+#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013UL
+
+#define CKR_ACTION_PROHIBITED 0x0000001BUL
+
+#define CKR_DATA_INVALID 0x00000020UL
+#define CKR_DATA_LEN_RANGE 0x00000021UL
+#define CKR_DEVICE_ERROR 0x00000030UL
+#define CKR_DEVICE_MEMORY 0x00000031UL
+#define CKR_DEVICE_REMOVED 0x00000032UL
+#define CKR_ENCRYPTED_DATA_INVALID 0x00000040UL
+#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041UL
+#define CKR_FUNCTION_CANCELED 0x00000050UL
+#define CKR_FUNCTION_NOT_PARALLEL 0x00000051UL
+
+#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054UL
+
+#define CKR_KEY_HANDLE_INVALID 0x00000060UL
+
+#define CKR_KEY_SIZE_RANGE 0x00000062UL
+#define CKR_KEY_TYPE_INCONSISTENT 0x00000063UL
+
+#define CKR_KEY_NOT_NEEDED 0x00000064UL
+#define CKR_KEY_CHANGED 0x00000065UL
+#define CKR_KEY_NEEDED 0x00000066UL
+#define CKR_KEY_INDIGESTIBLE 0x00000067UL
+#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068UL
+#define CKR_KEY_NOT_WRAPPABLE 0x00000069UL
+#define CKR_KEY_UNEXTRACTABLE 0x0000006AUL
+
+#define CKR_MECHANISM_INVALID 0x00000070UL
+#define CKR_MECHANISM_PARAM_INVALID 0x00000071UL
+
+#define CKR_OBJECT_HANDLE_INVALID 0x00000082UL
+#define CKR_OPERATION_ACTIVE 0x00000090UL
+#define CKR_OPERATION_NOT_INITIALIZED 0x00000091UL
+#define CKR_PIN_INCORRECT 0x000000A0UL
+#define CKR_PIN_INVALID 0x000000A1UL
+#define CKR_PIN_LEN_RANGE 0x000000A2UL
+
+#define CKR_PIN_EXPIRED 0x000000A3UL
+#define CKR_PIN_LOCKED 0x000000A4UL
+
+#define CKR_SESSION_CLOSED 0x000000B0UL
+#define CKR_SESSION_COUNT 0x000000B1UL
+#define CKR_SESSION_HANDLE_INVALID 0x000000B3UL
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4UL
+#define CKR_SESSION_READ_ONLY 0x000000B5UL
+#define CKR_SESSION_EXISTS 0x000000B6UL
+
+#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7UL
+#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8UL
+
+#define CKR_SIGNATURE_INVALID 0x000000C0UL
+#define CKR_SIGNATURE_LEN_RANGE 0x000000C1UL
+#define CKR_TEMPLATE_INCOMPLETE 0x000000D0UL
+#define CKR_TEMPLATE_INCONSISTENT 0x000000D1UL
+#define CKR_TOKEN_NOT_PRESENT 0x000000E0UL
+#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1UL
+#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2UL
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0UL
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1UL
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2UL
+#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL
+#define CKR_USER_NOT_LOGGED_IN 0x00000101UL
+#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102UL
+#define CKR_USER_TYPE_INVALID 0x00000103UL
+
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104UL
+#define CKR_USER_TOO_MANY_TYPES 0x00000105UL
+
+#define CKR_WRAPPED_KEY_INVALID 0x00000110UL
+#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112UL
+#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113UL
+#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114UL
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115UL
+#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120UL
+
+#define CKR_RANDOM_NO_RNG 0x00000121UL
+
+#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL
+
+#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL
+
+#define CKR_BUFFER_TOO_SMALL 0x00000150UL
+#define CKR_SAVED_STATE_INVALID 0x00000160UL
+#define CKR_INFORMATION_SENSITIVE 0x00000170UL
+#define CKR_STATE_UNSAVEABLE 0x00000180UL
+
+#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190UL
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL
+#define CKR_MUTEX_BAD 0x000001A0UL
+#define CKR_MUTEX_NOT_LOCKED 0x000001A1UL
+
+#define CKR_NEW_PIN_MODE 0x000001B0UL
+#define CKR_NEXT_OTP 0x000001B1UL
+
+#define CKR_EXCEEDED_MAX_ITERATIONS 0x000001B5UL
+#define CKR_FIPS_SELF_TEST_FAILED 0x000001B6UL
+#define CKR_LIBRARY_LOAD_FAILED 0x000001B7UL
+#define CKR_PIN_TOO_WEAK 0x000001B8UL
+#define CKR_PUBLIC_KEY_INVALID 0x000001B9UL
+
+#define CKR_FUNCTION_REJECTED 0x00000200UL
+
+#define CKR_VENDOR_DEFINED 0x80000000UL
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_NOTIFICATION event,
+ CK_VOID_PTR pApplication /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec
+ * version and pointers of appropriate types to all the
+ * Cryptoki functions
+ */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+ CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex
+ */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize
+ */
+typedef struct CK_C_INITIALIZE_ARGS {
+ CK_CREATEMUTEX CreateMutex;
+ CK_DESTROYMUTEX DestroyMutex;
+ CK_LOCKMUTEX LockMutex;
+ CK_UNLOCKMUTEX UnlockMutex;
+ CK_FLAGS flags;
+ CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL
+#define CKF_OS_LOCKING_OK 0x00000002UL
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK 1
+
+/* CK_RSA_PKCS_MGF_TYPE is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+#define CKG_MGF1_SHA1 0x00000001UL
+#define CKG_MGF1_SHA256 0x00000002UL
+#define CKG_MGF1_SHA384 0x00000003UL
+#define CKG_MGF1_SHA512 0x00000004UL
+#define CKG_MGF1_SHA224 0x00000005UL
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme.
+ */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED 0x00000001UL
+
+/* CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism.
+ */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+ CK_VOID_PTR pSourceData;
+ CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s).
+ */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_ULONG sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL 0x00000001UL
+#define CKD_SHA1_KDF 0x00000002UL
+
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1 0x00000003UL
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004UL
+#define CKD_SHA224_KDF 0x00000005UL
+#define CKD_SHA256_KDF 0x00000006UL
+#define CKD_SHA384_KDF 0x00000007UL
+#define CKD_SHA512_KDF 0x00000008UL
+#define CKD_CPDIVERSIFY_KDF 0x00000009UL
+
+
+/* CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+/*
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs.
+ */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms
+ */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair
+ */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs
+ */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism
+ */
+typedef struct CK_KEA_DERIVE_PARAMS {
+ CK_BBOOL isSender;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pRandomB;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just
+ * holds the effective keysize
+ */
+typedef CK_ULONG CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism
+ */
+typedef struct CK_RC2_CBC_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_BYTE iv[8]; /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms
+ */
+typedef struct CK_RC5_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism
+ */
+typedef struct CK_RC5_CBC_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_BYTE_PTR pIv; /* pointer to IV */
+ CK_ULONG ulIvLen; /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism
+ */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms. Its value is the length of
+ * the MAC
+ */
+typedef CK_ULONG CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[8];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism
+ */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pPassword;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPAndGLen;
+ CK_ULONG ulQLen;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pPrimeP;
+ CK_BYTE_PTR pBaseG;
+ CK_BYTE_PTR pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+ CK_SKIPJACK_PRIVATE_WRAP_PARAMS_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism
+ */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+ CK_ULONG ulOldWrappedXLen;
+ CK_BYTE_PTR pOldWrappedX;
+ CK_ULONG ulOldPasswordLen;
+ CK_BYTE_PTR pOldPassword;
+ CK_ULONG ulOldPublicDataLen;
+ CK_BYTE_PTR pOldPublicData;
+ CK_ULONG ulOldRandomLen;
+ CK_BYTE_PTR pOldRandomA;
+ CK_ULONG ulNewPasswordLen;
+ CK_BYTE_PTR pNewPassword;
+ CK_ULONG ulNewPublicDataLen;
+ CK_BYTE_PTR pNewPublicData;
+ CK_ULONG ulNewRandomLen;
+ CK_BYTE_PTR pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+ CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+ CK_BYTE_PTR pInitVector;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pSalt;
+ CK_ULONG ulSaltLen;
+ CK_ULONG ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism
+ */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+ CK_BYTE bBC; /* block contents byte */
+ CK_BYTE_PTR pX; /* extra data */
+ CK_ULONG ulXLen; /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+typedef struct CK_SSL3_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hClientMacSecret;
+ CK_OBJECT_HANDLE hServerMacSecret;
+ CK_OBJECT_HANDLE hClientKey;
+ CK_OBJECT_HANDLE hServerKey;
+ CK_BYTE_PTR pIVClient;
+ CK_BYTE_PTR pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_PRF_PARAMS {
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hMacSecret;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_ULONG ulSequenceNumber;
+ CK_BBOOL bIsExport;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_CMS_SIG_PARAMS {
+ CK_OBJECT_HANDLE certificateHandle;
+ CK_MECHANISM_PTR pSigningMechanism;
+ CK_MECHANISM_PTR pDigestMechanism;
+ CK_UTF8CHAR_PTR pContentType;
+ CK_BYTE_PTR pRequestedAttributes;
+ CK_ULONG ulRequestedAttributesLen;
+ CK_BYTE_PTR pRequiredAttributes;
+ CK_ULONG ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+ CK_BYTE_PTR pData;
+ CK_ULONG ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+ CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key
+ */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR \
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001UL
+#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411 0x00000002UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA224 0x00000003UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA256 0x00000004UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA384 0x00000005UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512 0x00000006UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007UL
+#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008UL
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2.
+ */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR \
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED 0x00000001UL
+
+/* CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG_PTR ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PARAMS2 is a corrected version of the CK_PKCS5_PBKD2_PARAMS
+ * structure that provides the parameters to the CKM_PKCS5_PBKD2 mechanism
+ * noting that the ulPasswordLen field is a CK_ULONG and not a CK_ULONG_PTR.
+ */
+typedef struct CK_PKCS5_PBKD2_PARAMS2 {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS2;
+
+typedef CK_PKCS5_PBKD2_PARAMS2 CK_PTR CK_PKCS5_PBKD2_PARAMS2_PTR;
+
+typedef CK_ULONG CK_OTP_PARAM_TYPE;
+typedef CK_OTP_PARAM_TYPE CK_PARAM_TYPE; /* backward compatibility */
+
+typedef struct CK_OTP_PARAM {
+ CK_OTP_PARAM_TYPE type;
+ CK_VOID_PTR pValue;
+ CK_ULONG ulValueLen;
+} CK_OTP_PARAM;
+
+typedef CK_OTP_PARAM CK_PTR CK_OTP_PARAM_PTR;
+
+typedef struct CK_OTP_PARAMS {
+ CK_OTP_PARAM_PTR pParams;
+ CK_ULONG ulCount;
+} CK_OTP_PARAMS;
+
+typedef CK_OTP_PARAMS CK_PTR CK_OTP_PARAMS_PTR;
+
+typedef struct CK_OTP_SIGNATURE_INFO {
+ CK_OTP_PARAM_PTR pParams;
+ CK_ULONG ulCount;
+} CK_OTP_SIGNATURE_INFO;
+
+typedef CK_OTP_SIGNATURE_INFO CK_PTR CK_OTP_SIGNATURE_INFO_PTR;
+
+#define CK_OTP_VALUE 0UL
+#define CK_OTP_PIN 1UL
+#define CK_OTP_CHALLENGE 2UL
+#define CK_OTP_TIME 3UL
+#define CK_OTP_COUNTER 4UL
+#define CK_OTP_FLAGS 5UL
+#define CK_OTP_OUTPUT_LENGTH 6UL
+#define CK_OTP_OUTPUT_FORMAT 7UL
+
+#define CKF_NEXT_OTP 0x00000001UL
+#define CKF_EXCLUDE_TIME 0x00000002UL
+#define CKF_EXCLUDE_COUNTER 0x00000004UL
+#define CKF_EXCLUDE_CHALLENGE 0x00000008UL
+#define CKF_EXCLUDE_PIN 0x00000010UL
+#define CKF_USER_FRIENDLY_OTP 0x00000020UL
+
+typedef struct CK_KIP_PARAMS {
+ CK_MECHANISM_PTR pMechanism;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+} CK_KIP_PARAMS;
+
+typedef CK_KIP_PARAMS CK_PTR CK_KIP_PARAMS_PTR;
+
+typedef struct CK_AES_CTR_PARAMS {
+ CK_ULONG ulCounterBits;
+ CK_BYTE cb[16];
+} CK_AES_CTR_PARAMS;
+
+typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR;
+
+typedef struct CK_GCM_PARAMS {
+ CK_BYTE_PTR pIv;
+ CK_ULONG ulIvLen;
+ CK_ULONG ulIvBits;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulTagBits;
+} CK_GCM_PARAMS;
+
+typedef CK_GCM_PARAMS CK_PTR CK_GCM_PARAMS_PTR;
+
+typedef struct CK_CCM_PARAMS {
+ CK_ULONG ulDataLen;
+ CK_BYTE_PTR pNonce;
+ CK_ULONG ulNonceLen;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulMACLen;
+} CK_CCM_PARAMS;
+
+typedef CK_CCM_PARAMS CK_PTR CK_CCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_GCM_PARAMS */
+typedef struct CK_AES_GCM_PARAMS {
+ CK_BYTE_PTR pIv;
+ CK_ULONG ulIvLen;
+ CK_ULONG ulIvBits;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulTagBits;
+} CK_AES_GCM_PARAMS;
+
+typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR;
+
+/* Deprecated. Use CK_CCM_PARAMS */
+typedef struct CK_AES_CCM_PARAMS {
+ CK_ULONG ulDataLen;
+ CK_BYTE_PTR pNonce;
+ CK_ULONG ulNonceLen;
+ CK_BYTE_PTR pAAD;
+ CK_ULONG ulAADLen;
+ CK_ULONG ulMACLen;
+} CK_AES_CCM_PARAMS;
+
+typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CTR_PARAMS {
+ CK_ULONG ulCounterBits;
+ CK_BYTE cb[16];
+} CK_CAMELLIA_CTR_PARAMS;
+
+typedef CK_CAMELLIA_CTR_PARAMS CK_PTR CK_CAMELLIA_CTR_PARAMS_PTR;
+
+typedef struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_ARIA_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_ARIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_ARIA_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_DSA_PARAMETER_GEN_PARAM {
+ CK_MECHANISM_TYPE hash;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_ULONG ulIndex;
+} CK_DSA_PARAMETER_GEN_PARAM;
+
+typedef CK_DSA_PARAMETER_GEN_PARAM CK_PTR CK_DSA_PARAMETER_GEN_PARAM_PTR;
+
+typedef struct CK_ECDH_AES_KEY_WRAP_PARAMS {
+ CK_ULONG ulAESKeyBits;
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+} CK_ECDH_AES_KEY_WRAP_PARAMS;
+
+typedef CK_ECDH_AES_KEY_WRAP_PARAMS CK_PTR CK_ECDH_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef CK_ULONG CK_JAVA_MIDP_SECURITY_DOMAIN;
+
+typedef CK_ULONG CK_CERTIFICATE_CATEGORY;
+
+typedef struct CK_RSA_AES_KEY_WRAP_PARAMS {
+ CK_ULONG ulAESKeyBits;
+ CK_RSA_PKCS_OAEP_PARAMS_PTR pOAEPParams;
+} CK_RSA_AES_KEY_WRAP_PARAMS;
+
+typedef CK_RSA_AES_KEY_WRAP_PARAMS CK_PTR CK_RSA_AES_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+ CK_MECHANISM_TYPE prfHashMechanism;
+} CK_TLS12_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_TLS12_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_TLS12_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_TLS12_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+ CK_MECHANISM_TYPE prfHashMechanism;
+} CK_TLS12_KEY_MAT_PARAMS;
+
+typedef CK_TLS12_KEY_MAT_PARAMS CK_PTR CK_TLS12_KEY_MAT_PARAMS_PTR;
+
+typedef struct CK_TLS_KDF_PARAMS {
+ CK_MECHANISM_TYPE prfMechanism;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLength;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pContextData;
+ CK_ULONG ulContextDataLength;
+} CK_TLS_KDF_PARAMS;
+
+typedef CK_TLS_KDF_PARAMS CK_PTR CK_TLS_KDF_PARAMS_PTR;
+
+typedef struct CK_TLS_MAC_PARAMS {
+ CK_MECHANISM_TYPE prfHashMechanism;
+ CK_ULONG ulMacLength;
+ CK_ULONG ulServerOrClient;
+} CK_TLS_MAC_PARAMS;
+
+typedef CK_TLS_MAC_PARAMS CK_PTR CK_TLS_MAC_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pUKM;
+ CK_ULONG ulUKMLen;
+} CK_GOSTR3410_DERIVE_PARAMS;
+
+typedef CK_GOSTR3410_DERIVE_PARAMS CK_PTR CK_GOSTR3410_DERIVE_PARAMS_PTR;
+
+typedef struct CK_GOSTR3410_KEY_WRAP_PARAMS {
+ CK_BYTE_PTR pWrapOID;
+ CK_ULONG ulWrapOIDLen;
+ CK_BYTE_PTR pUKM;
+ CK_ULONG ulUKMLen;
+ CK_OBJECT_HANDLE hKey;
+} CK_GOSTR3410_KEY_WRAP_PARAMS;
+
+typedef CK_GOSTR3410_KEY_WRAP_PARAMS CK_PTR CK_GOSTR3410_KEY_WRAP_PARAMS_PTR;
+
+typedef struct CK_SEED_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_SEED_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_SEED_CBC_ENCRYPT_DATA_PARAMS CK_PTR \
+ CK_SEED_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+#endif /* _PKCS11T_H_ */
+
diff --git a/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/shim.h b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/shim.h
new file mode 100644
index 00000000..430af8b9
--- /dev/null
+++ b/PROJECTS/advanced/hsm-emulator/vendor/pkcs11/shim.h
@@ -0,0 +1,18 @@
+// ©AngelaMos | 2026
+// shim.h
+
+#ifndef ANGELAMOS_PKCS11_SHIM_H
+#define ANGELAMOS_PKCS11_SHIM_H
+
+#define CK_PTR *
+#define CK_DECLARE_FUNCTION(returnType, name) returnType name
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (*name)
+#define CK_CALLBACK_FUNCTION(returnType, name) returnType (*name)
+
+#ifndef NULL_PTR
+#define NULL_PTR 0
+#endif
+
+#include "pkcs11.h"
+
+#endif