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 @@ +``` +██╗ ██╗███████╗███╗ ███╗ ███████╗███╗ ███╗██╗ ██╗██╗ █████╗ ████████╗ ██████╗ ██████╗ +██║ ██║██╔════╝████╗ ████║ ██╔════╝████╗ ████║██║ ██║██║ ██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗ +███████║███████╗██╔████╔██║ █████╗ ██╔████╔██║██║ ██║██║ ███████║ ██║ ██║ ██║██████╔╝ +██╔══██║╚════██║██║╚██╔╝██║ ██╔══╝ ██║╚██╔╝██║██║ ██║██║ ██╔══██║ ██║ ██║ ██║██╔══██╗ +██║ ██║███████║██║ ╚═╝ ██║ ███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ██║ ██║ ╚██████╔╝██║ ██║ +╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ +``` + +[![Cybersecurity Projects](https://img.shields.io/badge/Cybersecurity--Projects-Project%20%2333-red?style=flat&logo=github)](https://github.com/CarterPerez-dev/Cybersecurity-Projects/tree/main/PROJECTS/advanced/hsm-emulator) +[![Zig](https://img.shields.io/badge/Zig-0.16.0-F7A41D?style=flat&logo=zig&logoColor=white)](https://ziglang.org) +[![PKCS#11](https://img.shields.io/badge/PKCS%2311-v2.40-4B7BEC?style=flat)](https://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/os/pkcs11-base-v2.40-errata01-os.html) +[![Verified with](https://img.shields.io/badge/verified-pkcs11--tool-green?style=flat)](https://github.com/OpenSC/OpenSC) +[![License: AGPLv3](https://img.shields.io/badge/License-AGPL_v3-purple.svg)](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