pax_global_header00006660000000000000000000000064150737161330014520gustar00rootroot0000000000000052 comment=f67e9a3e2fbf11e89b4dfdc839982fca4f267093 nethsm-pkcs11-2.0.0/000077500000000000000000000000001507371613300141155ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/.cargo/000077500000000000000000000000001507371613300152665ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/.cargo/audit.toml000066400000000000000000000001201507371613300172620ustar00rootroot00000000000000[advisories] ignore = [ "RUSTSEC-2022-0034", # pkcs11 is only used in tests ] nethsm-pkcs11-2.0.0/.dockerignore000066400000000000000000000000201507371613300165610ustar00rootroot00000000000000target profilingnethsm-pkcs11-2.0.0/.github/000077500000000000000000000000001507371613300154555ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/.github/workflows/000077500000000000000000000000001507371613300175125ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/.github/workflows/release.yml000066400000000000000000000241141507371613300216570ustar00rootroot00000000000000permissions: contents: write name: Build Rust project on multiple platforms when a new release is created on: release: types: [created] push: branches: [main] workflow_dispatch: jobs: build-windows: runs-on: windows-latest env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-x86_64-windows.dll steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable-x86_64-pc-windows-msvc - uses: Swatinem/rust-cache@v2 - name: Build project run: cargo build --release - name: rename file run: mv -Verbose target/release/nethsm_pkcs11.dll ${{ env.FILE_NAME }} - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: windows path: ${{ env.FILE_NAME }} - name: Upload to the release if: ${{ github.event_name == 'release' }} uses: softprops/action-gh-release@v1 with: files: ${{ env.FILE_NAME }} build-macos: runs-on: macos-latest env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-x86_64-macos.dylib FILE_NAME_ARM: nethsm-pkcs11-${{ github.ref_name }}-aarch64-macos.dylib steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust run: rustup install stable && rustup target install aarch64-apple-darwin && rustup target install x86_64-apple-darwin - uses: Swatinem/rust-cache@v2 - name: Build project for MacOs aaarch64 run: cargo build --release --target aarch64-apple-darwin - name: Build project run: cargo build --release --target x86_64-apple-darwin - name: rename file run: mv -v target/x86_64-apple-darwin/release/libnethsm_pkcs11.dylib ${{ env.FILE_NAME }} - name: rename file arm run: mv -v target/aarch64-apple-darwin/release/libnethsm_pkcs11.dylib ${{ env.FILE_NAME_ARM }} - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: macos path: | ${{ env.FILE_NAME }} ${{ env.FILE_NAME_ARM }} - name: Upload to the release if: ${{ github.event_name == 'release' }} uses: softprops/action-gh-release@v1 with: files: | ${{ env.FILE_NAME }} ${{ env.FILE_NAME_ARM }} build-linux: runs-on: ubuntu-latest container: image: ${{ matrix.os }} strategy: matrix: include: - os: "alpine:3.18" name: musl - os: redhat/ubi8 name: glibc env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-x86_64-linux-${{ matrix.name }}.so steps: - name: Checkout code uses: actions/checkout@v2 - name: install dependencies run: | case ${{matrix.os}} in redhat/ubi8*) dnf install -y curl gcc ;; alpine*) apk add curl musl-dev gcc echo RUSTFLAGS="-C target-feature=-crt-static" >> $GITHUB_ENV echo HOME=/root >> $GITHUB_ENV ;; esac - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable - uses: Swatinem/rust-cache@v2 - name: Build project run: cargo build --release - name: rename file run: mv --verbose target/release/libnethsm_pkcs11.so ${{ env.FILE_NAME }} - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: linux-x86_64-${{ matrix.name }} path: ${{ env.FILE_NAME }} - name: Upload to the release if: ${{ github.event_name == 'release' }} uses: softprops/action-gh-release@v1 with: files: ${{ env.FILE_NAME }} test-windows: runs-on: windows-latest needs: build-windows env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-x86_64-windows.dll steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts uses: actions/download-artifact@v4 with: name: windows - name: Install dependencies run: choco install opensc - name: Test pkcs11 module run: | & "C:\Program Files\Git\usr\bin\sed.exe" --in-place s/localhost:8443/nethsmdemo.nitrokey.com/g p11nethsm.conf $env:P11NETHSM_CONFIG_FILE = "p11nethsm.conf" & "C:\Program Files\OpenSC Project\OpenSC\tools\pkcs11-tool.exe" --module ./${{ env.FILE_NAME }} --list-slots & "C:\Program Files\OpenSC Project\OpenSC\tools\pkcs11-tool.exe" --module ./${{ env.FILE_NAME }} --list-slots | Should -Contain "Slot 0 (0x0): NetHSM" test-macos: runs-on: macos-latest needs: build-macos env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-aarch64-macos.dylib steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts uses: actions/download-artifact@v4 with: name: macos - name: Install dependencies run: | brew update brew install opensc - name: Test pkcs11 module run: | sed -i "" s/localhost:8443/nethsmdemo.nitrokey.com/g p11nethsm.conf export P11NETHSM_CONFIG_FILE=p11nethsm.conf pkcs11-tool --module ./${{ env.FILE_NAME }} --list-slots pkcs11-tool --module ./${{ env.FILE_NAME }} --list-slots | grep NetHSM test-linux: runs-on: ubuntu-latest needs: build-linux container: image: ${{ matrix.os }} strategy: matrix: include: - os: "alpine:3.18" name: musl - os: "debian:12" name: glibc - os: "fedora:39" name: glibc - os: "fedora:40" name: glibc - os: "ubuntu:22.04" name: glibc - os: "ubuntu:24.04" name: glibc env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-x86_64-linux-${{ matrix.name }}.so services: nethsm: image: nitrokey/nethsm:testing steps: - name: Checkout code uses: actions/checkout@v2 - name: Download artifacts uses: actions/download-artifact@v4 with: name: linux-x86_64-${{ matrix.name }} - name: Install dependencies run: | case ${{ matrix.os }} in ubuntu* | debian* ) apt-get update apt-get install -y opensc ;; alpine* ) apk add opensc ;; fedora* ) dnf install -y opensc ;; esac - name: Test pkcs11 module run: | sed --in-place s/localhost/nethsm/g p11nethsm.conf export P11NETHSM_CONFIG_FILE=p11nethsm.conf pkcs11-tool --module ./${{ env.FILE_NAME }} --list-slots pkcs11-tool --module ./${{ env.FILE_NAME }} --list-slots | grep NetHSM cross-build-linux: runs-on: ubuntu-latest name: Build on ${{ matrix.distro }} ${{ matrix.arch }} strategy: matrix: include: - arch: aarch64 distro: ubuntu22.04 target: aarch64-unknown-linux-gnu name: glibc - arch: aarch64 distro: alpine_latest target: aarch64-unknown-linux-musl name: musl # Not supported by ring # - arch: riscv64 # distro: ubuntu22.04 # target: riscv64gc-unknown-linux-gnu env: FILE_NAME: nethsm-pkcs11-${{ github.ref_name }}-${{ matrix.arch }}-linux-${{ matrix.name }}.so steps: - uses: actions/checkout@v3 - uses: uraimo/run-on-arch-action@v3 name: Build artifact id: build with: githubToken: ${{ github.token }} arch: ${{ matrix.arch }} distro: ${{ matrix.distro }} setup: | mkdir -p "${PWD}/artifacts" dockerRunArgs: | --volume "${PWD}/artifacts:/artifacts" env: | FILE_NAME: "${{env.FILE_NAME}}" TARGET: "${{ matrix.target }}" RUSTFLAGS: "-C target-feature=-crt-static" shell: /bin/sh install: | case ${{matrix.distro}} in ubuntu*) apt-get update apt-get install -y curl gcc ;; alpine*) apk add curl musl-dev gcc ;; esac curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y . $HOME/.cargo/env rustup target add ${{ matrix.target }} run: | . $HOME/.cargo/env cargo build --release --target ${{ matrix.target }} mv --verbose target/${{ matrix.target }}/release/libnethsm_pkcs11.so /artifacts/${{env.FILE_NAME}} - name: Upload artifacts if: ${{ github.event_name != 'release' }} uses: actions/upload-artifact@v4 with: name: linux-${{ matrix.arch }}-${{ matrix.name }} path: artifacts/${{ env.FILE_NAME }} - uses: softprops/action-gh-release@v1 if: ${{ github.event_name == 'release' }} with: files: artifacts/${{ env.FILE_NAME }} upload-licenses: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable - name: install cargo-license run: cargo install cargo-license - name: Generate licenses run: ./tools/collect_licenses.sh - name: rename file run: mv --verbose _LICENSE LICENSE - name: Upload artifacts if: ${{ github.event_name != 'release' }} uses: actions/upload-artifact@v4 with: name: licenses path: LICENSE - name: Upload to the release if: ${{ github.event_name == 'release' }} uses: softprops/action-gh-release@v1 with: files: LICENSE nethsm-pkcs11-2.0.0/.github/workflows/rust-tests.yml000066400000000000000000000153641507371613300224030ustar00rootroot00000000000000name: Rust CI on: pull_request: types: [opened, synchronize, reopened] branches: - main push: branches: - main jobs: check-formatting-linting: permissions: contents: read checks: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 - name: Check formatting run: cargo fmt -- --check - name: Fetch dependencies run: cargo fetch --locked - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --frozen --all-targets -p nethsm_pkcs11 -- -D warnings --no-deps check-windows: runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable-x86_64-pc-windows-msvc - uses: Swatinem/rust-cache@v2 - name: Build project run: cargo build --release - name: Archive Artifact uses: actions/upload-artifact@v4 with: name: nethsm-pkcs11-x86_64-windows.dll path: target/release/nethsm_pkcs11.dll if-no-files-found: error build-macos: runs-on: macos-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Install Rust run: rustup install stable && rustup target install aarch64-apple-darwin && rustup target install x86_64-apple-darwin - uses: Swatinem/rust-cache@v2 - name: Build project for MacOs x86_64 run: cargo build --release --target x86_64-apple-darwin - name: Archive Artifact x86_64 uses: actions/upload-artifact@v4 with: name: nethsm-pkcs11-x86_64-apple.dylib path: target/x86_64-apple-darwin/release/libnethsm_pkcs11.dylib if-no-files-found: error - name: Build project MacOs aaarch64 run: cargo build --release --target aarch64-apple-darwin - name: Archive Artifact uses: actions/upload-artifact@v4 with: name: nethsm-pkcs11-aarch64-apple.dylib path: target/aarch64-apple-darwin/release/libnethsm_pkcs11.dylib if-no-files-found: error build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Install Rust run: rustup toolchain install stable --profile minimal - uses: Swatinem/rust-cache@v2 - name: build release run: cargo build --release - name: Archive Artifact uses: actions/upload-artifact@v4 with: name: nethsm-pkcs11-x86_64-linux-gnu.so path: target/release/libnethsm_pkcs11.so if-no-files-found: error tests: runs-on: ubuntu-latest container: debian:12 env: RUST_BACKTRACE: full RUST_LOG: trace services: nethsm: image: nitrokey/nethsm:testing ports: - 8443:8443 steps: - uses: actions/checkout@v2 - name: install opensc and dependencies run: apt-get update && apt-get install -y curl opensc openssl gcc xxd jq gnutls-bin make - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable components: rustfmt, clippy, llvm-tools-preview - uses: Swatinem/rust-cache@v2 - name: install demangler run: cargo install rustfilt - name: Build with profiling run: ./tools/build_profiling.sh - name: repalce localhost with nethsm run: sed -i 's/localhost/nethsm/g' ./tools/*.sh ./tools/tests/*.sh ./p11nethsm.conf ./tools/*.conf - name: setup NetHSM run: ./tools/ci_setup.sh - name: cargo test run: ./tools/test_profiling.sh - name: Run integration tests run: ./tools/ci_integration_tests.sh - name: get coverage run: ./tools/get_coverage.sh - name: upload coverage uses: codecov/codecov-action@v3 with: files: ./coverage.txt verbose: true fork-tests: runs-on: ubuntu-latest container: debian:12 needs: build env: RUST_BACKTRACE: full RUST_LOG: trace services: nethsm: image: nitrokey/nethsm:testing ports: - 8443:8443 steps: - uses: actions/checkout@v2 - name: install dependencies run: apt-get update && apt-get install -y gcc make - name: Download Artifact uses: actions/download-artifact@v5 with: name: nethsm-pkcs11-x86_64-linux-gnu.so path: target/release - name: run fork tests run: cd fork-tests && make compatibility-tests: runs-on: ubuntu-latest container: debian:12 needs: build strategy: matrix: nethsm: [v1.0, v2.0, v2.1, v2.2, v3.0, v3.1] services: nethsm: image: nitrokey/nethsm:testing-${{ matrix.nethsm }} ports: - 8443:8443 env: RUST_BACKTRACE: full RUST_LOG: info,nethsm_pkcs11=trace,ureq::run=trace,ureq::util=trace NETHSM_VERSION: ${{ matrix.nethsm }} steps: - uses: actions/checkout@v2 - name: install dependencies run: apt-get update && apt-get install -y curl opensc openssl xxd jq gnutls-bin - name: Download Artifact uses: actions/download-artifact@v5 with: name: nethsm-pkcs11-x86_64-linux-gnu.so path: target/debug - name: replace localhost with nethsm run: sed -i 's/localhost/nethsm/g' ./tools/*.sh ./tools/tests/*.sh ./p11nethsm.conf ./tools/*.conf - name: setup NetHSM run: ./tools/ci_setup.sh - name: Run integration tests run: ./tools/ci_integration_tests.sh test_network: runs-on: ubuntu-latest env: RUST_BACKTRACE: full RUST_LOG: trace USE_SUDO_IPTABLES: true services: nethsm: image: nitrokey/nethsm:testing ports: - 8443:8443 steps: - uses: actions/checkout@v2 - name: install opensc and dependencies run: sudo apt-get update && sudo apt-get install -y curl opensc openssl gcc xxd jq gnutls-bin make - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable components: rustfmt, clippy, llvm-tools-preview - name: pull docker image run: docker pull nitrokey/nethsm:testing - uses: Swatinem/rust-cache@v2 - name: build release run: cargo build --release - name: run network tests run: cargo t --features pkcs11-full-tests -p nethsm_pkcs11 --test basic -- --nocapture nethsm-pkcs11-2.0.0/.gitignore000066400000000000000000000005621507371613300161100ustar00rootroot00000000000000p11nethsm p11nethsm.so db.sqlite3 p11nethsm.h /tests/pkcs11-test/config* /tests/pkcs11-test/dtc-config.yaml /tests/dns-tools/dtc/config/ /tests/dns-tools/dtcnode/config/ *~ node_modules # test file _* # Added by cargo /target profile *.profraw coverage coverage.txt fork-tests/fork_test pkcs11-proxy-raw pkcs11-proxy-slow pkcs11-thread-bench stress-test certutil-db nethsm-pkcs11-2.0.0/.vscode/000077500000000000000000000000001507371613300154565ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/.vscode/launch.json000066400000000000000000000012171507371613300176240ustar00rootroot00000000000000{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Debug", "type": "gdb", "gdbpath": "/usr/bin/rust-gdb", "request": "launch", "target": "/usr/bin/pkcs11-tool", "arguments": "--module target/debug/libnethsm_pkcs11.so -O", "env": { "P11NETHSM_CONFIG_FILE": "./p11nethsm.conf", "RUST_LOG": "trace" }, "cwd": "${workspaceRoot}", "valuesFormatting": "prettyPrinters" } ] }nethsm-pkcs11-2.0.0/CHANGELOG.md000066400000000000000000000176131507371613300157360ustar00rootroot00000000000000# Changelog ## Unreleased - ## [2.0.0][] (2025-10-15) This release adds support for the new features introduced in [NetHSM v3.0](https://github.com/Nitrokey/nethsm/releases/tag/v3.0) and improves the key ID handling. ### Breaking Changes - Remove support for EC_P224 keys - Remove `enable_set_attribute_value` config option - Reject invalid IDs when creating or changing objects ### Features - Add support for EC_P256K1, BrainpoolP256, BrainpoolP384 and BrainpoolP512 keys (requires NetHSM v3.0 or later) - Implement `C_SetAttributeValue` for `CKA_ID` to support renaming keys (requires NetHSM v3.0 or later) - Add `CKF_ENCRYPT` flag for `CKM_RSA_PKCS` ### Bugfixes - Fix ID validation. The new requirements are: - The ID must not be empty and not be longer than 128 characters. - The first character must be in the range `a`-`z`, `A-Z` or `0`-`9`. - The remaining characters must be in the range `a`-`z`, `A-Z` or `0`-`9` or one of the characters `.`, `-`, `_`. - The characters `.`, `-` and `_` can only be used with NetHSM v3.0 or later. ### Bugfixes - Remove corresponding certificate and public key objects from the cache if a private key is deleted ([#260](https://github.com/Nitrokey/nethsm-pkcs11/issues/260)) ### Compatibility - This release is fully compatible with NetHSM v3.1. - This release is generally compatible with NetHSM v1.0, v2.0, v2.1, v2.2 and v3.0 but not all features are available on these versions (as indicated in the changelog entries). - RSA signatures using the PKCS1 mechanisms do not work with NetHSM v3.0. [2.0.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v2.0.0 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.7.2...v2.0.0) ## [1.7.2][] (2025-07-25) - Build Linux x86_64 binary against glibc v2.28 [1.7.2]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.7.2 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.7.1...v1.7.2) ## [1.7.1][] (2025-07-04) - Fix PKCS#1v1.5 RSA signature prefix ([#246][]) [#246]: https://github.com/Nitrokey/nethsm-pkcs11/pull/246 [1.7.1]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.7.1 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.7.0...v1.7.1) ## [1.7.0][] (2025-03-17) - Support storing the certificates as DER on the Nethsm ([#234][]) - Update dependencies, fixing possible crash when compiled in debug mode ([#235][]) [#234]: https://github.com/Nitrokey/nethsm-pkcs11/pull/234 [#235]: https://github.com/Nitrokey/nethsm-pkcs11/pull/235 [1.7.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.7.0 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.6.0...v1.7.0) ## [1.6.0][] (2025-02-26) - Expose all PKCS11 functions as part of the C API, not just `C_GetFunctionList` ([#231][]) - Upgrade to upstream ureq 3.0 ([#229][]) [#231]: https://github.com/Nitrokey/nethsm-pkcs11/pull/231 [#229]: https://github.com/Nitrokey/nethsm-pkcs11/pull/229 [1.6.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.6.0 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.5.0...v1.6.0) ## [1.5.0][] (2024-09-18) - Make instance use within a slot really round-robin ([#218][]) - Mark instances as bad on network failure ([#218][]) - Automatically clear all connection pools on network failure ([#218][]) - Retry bad instances in a background thread ([#218][]) - Retry bad instance "inline" with the requests if background threads are not allowd ([#223][]) - Add tests for retry mechanisms and timeouts with dead or stalled connections ([#214][]) [#214]: https://github.com/Nitrokey/nethsm-pkcs11/pull/214 [#218]: https://github.com/Nitrokey/nethsm-pkcs11/pull/218 [#223]: https://github.com/Nitrokey/nethsm-pkcs11/pull/223 [1.5.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.5.0 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.4.1...v1.5.0) ## [1.4.1][] (2024-09-09) - Fix compilation on windows ([#219][]) [#219]: https://github.com/Nitrokey/nethsm-pkcs11/pull/219 [1.4.1]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.4.1 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.4.0...v1.4.1) ## [1.4.0][] (2024-09-05) - Change instance on each retry after a network failure ([#206][]) - Enable configuring tcp keepalive ([#207][]) - Enable configuring a time after which an idle connection is considered "stale" and not reused ([#207][]) [#206]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#206 [#207]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#207 [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/v1.3.1...v1.4.0) [1.4.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.4.0 ## [1.3.1][] (2024-04-19) ### Fixed - Update `rustls` dependency to 0.21.11 to fix [RUSTSEC-2024-0336][] [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/1.3.0...v1.3.1) [1.3.1]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/v1.3.1 [RUSTSEC-2024-0336]: https://rustsec.org/advisories/RUSTSEC-2024-0336.html ## [1.3.0][] (2024-01-12) ### Fixed - Log level selection by [@sosthene-nitrokey][] in [#184][] - Version in user agent by [@sosthene-nitrokey][] in [#186][] - Certificate listing with `certutil` by [@sosthene-nitrokey][] in [#185][] - Parrallel reading of all keys by [@sosthene-nitrokey][] in [#177][] [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/1.2.0...1.3.0) [1.3.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/1.3.0 [#184]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#184 [#186]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#186 [#185]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#185 [#177]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/#177 ## [1.2.0][] (2024-01-24) ### Added - Add configuration example with all available configuration by [@sosthene-nitrokey][] in [#172][] - Support syslog logging by [@sosthene-nitrokey][] in [#176][] ### Changed - Improve logging: by [@sosthene-nitrokey][] in ,[#180][] and [#181][] - Log the number of retries and the timeouts for each slot - Log the number of attempts on each network failure - Log the paths of the source configuration files [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/1.1.0...1.2.0) [#180]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/180 [#181]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/181 [#176]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/176 [#172]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/172 [1.2.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/1.2.0 ## [1.1.0][] (2024-01-12) ## Examples - Added Webserver PKCS11 Examples by [@q-nk][] in [#149][] - Releases: add fedora 39 build by [@sosthene-nitrokey][] in [#159][] ## Additions - Nginx test by [@sosthene-nitrokey][] in [#171][] ## Changes - Dependency updates by [@sosthene-nitrokey][] in [#162][] - Better handle network errors by [@sosthene-nitrokey][] in [#164][] - Improve CF_INFO by [@sosthene-nitrokey][] in [#167][] - Improve multithreaded performance by [@sosthene-nitrokey][] in [#173][] ## Fixes - Fix panics on failed initialization by [@sosthene-nitrokey][] in [#165][] [Full Changelog](https://github.com/Nitrokey/nethsm-pkcs11/compare/1.0.0...1.1.0) [#149]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/149 [#159]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/159 [#162]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/162 [#164]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/164 [#167]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/167 [#171]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/171 [#165]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/165 [#173]: https://github.com/Nitrokey/nethsm-pkcs11/pulls/173 [1.1.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/1.1.0 [@q-nk]: https://github.com/q-nk [@sosthene-nitrokey]: https://github.com/sosthene-nitrokey ## [1.0.0][] (2023-11-27) Initial release [1.0.0]: https://github.com/Nitrokey/nethsm-pkcs11/releases/tag/1.0.0 nethsm-pkcs11-2.0.0/Cargo.lock000066400000000000000000001470411507371613300160310ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys 0.60.2", ] [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" version = "1.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "config_file" version = "0.1.0" dependencies = [ "hex", "hex-literal", "log", "merge", "serde", "serde_yaml", "thiserror", ] [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cookie" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "percent-encoding", "time", "version_check", ] [[package]] name = "cookie_store" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" dependencies = [ "cookie", "document-features", "idna", "indexmap", "log", "serde", "serde_derive", "serde_json", "time", "url", ] [[package]] name = "core-foundation" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "cryptoki-sys" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "753e27d860277930ae9f394c119c8c70303236aab0ffab1d51f3d207dbb2bc4b" dependencies = [ "libloading 0.8.8", ] [[package]] name = "der" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "der_derive", "flagset", "pem-rfc7468", "zeroize", ] [[package]] name = "der_derive" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "deranged" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "document-features" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", ] [[package]] name = "env_logger" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", "jiff", "log", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", "windows-sys 0.60.2", ] [[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "version_check", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" [[package]] name = "flagset" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", "wasi 0.14.3+wasi-0.2.4", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcaaec4551594c969335c98c903c1397853d4198408ea609190f420500f6be71" [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", "winapi", ] [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "icu_collections" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "io-uring" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags", "cfg-if", "libc", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", "serde", ] [[package]] name = "jiff-static" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ "cc", "winapi", ] [[package]] name = "libloading" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", "windows-targets 0.53.3", ] [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchers" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ "regex-automata", ] [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", "digest", ] [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "merge" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e520ba58faea3487f75df198b1d079644ec226ea3b0507d002c6fa4b8cf93a" dependencies = [ "merge_derive", ] [[package]] name = "merge_derive" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8f8ce6efff81cbc83caf4af0905c46e58cb46892f63ad3835e81b47eaf7968" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", "syn", ] [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", ] [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "multipart" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" dependencies = [ "log", "mime", "mime_guess", "rand", "tempfile", ] [[package]] name = "nethsm-sdk-rs" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75db903339fbb85d05b3d370d820f2c5cec44e1e6cae5b91bc6a20cdc8353c50" dependencies = [ "base64 0.21.7", "http", "mime", "multipart", "serde", "serde_derive", "serde_json", "ureq", "url", ] [[package]] name = "nethsm_pkcs11" version = "2.0.0" dependencies = [ "arc-swap", "base64ct", "config_file", "cryptoki-sys", "der", "digest", "env_logger", "hex", "hex-literal", "lazy_static", "log", "md-5", "nethsm-sdk-rs", "once_cell", "pem-rfc7468", "pkcs11", "rayon", "rustls", "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", "serde_yaml", "sha1", "sha2", "socket2", "syslog", "tempfile", "test-log", "thiserror", "time", "tokio", "ureq", "x509-cert", ] [[package]] name = "nu-ansi-term" version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "num-bigint" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_threads" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pkcs11" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3aca6d67e4c8613bfe455599d0233d00735f85df2001f6bfd9bb7ac0496b10af" dependencies = [ "libloading 0.5.2", "num-bigint", ] [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "proc-macro-error2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", "syn", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.16", ] [[package]] name = "rayon" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex-automata" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rustc-demangle" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustix" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.60.2", ] [[package]] name = "rustls" version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "security-framework" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", "ryu", "serde", "unsafe-libyaml", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha2" version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "spki" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syslog" version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc7e95b5b795122fafe6519e27629b5ab4232c73ebb2428f568e82b1a457ad3" dependencies = [ "error-chain", "hostname", "libc", "log", "time", ] [[package]] name = "tempfile" version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.60.2", ] [[package]] name = "test-log" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b" dependencies = [ "env_logger", "test-log-macros", "tracing-subscriber", ] [[package]] name = "test-log-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thiserror" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thread_local" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", ] [[package]] name = "time" version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", "libc", "num-conv", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinystr" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tokio" version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", "io-uring", "libc", "mio", "pin-project-lite", "slab", "socket2", "windows-sys 0.59.0", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex-automata", "sharded-slab", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537" dependencies = [ "base64 0.22.1", "cookie_store", "log", "percent-encoding", "rustls", "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "ureq-proto", "utf-8", "webpki-roots", ] [[package]] name = "ureq-proto" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" dependencies = [ "base64 0.22.1", "http", "httparse", "log", ] [[package]] name = "url" version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ "wit-bindgen", ] [[package]] name = "webpki-roots" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.3", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "wit-bindgen" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "x509-cert" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", "der", "spki", ] [[package]] name = "yoke" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerocopy" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zerotrie" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", "syn", ] nethsm-pkcs11-2.0.0/Cargo.toml000066400000000000000000000005401507371613300160440ustar00rootroot00000000000000[workspace] resolver = "2" members = [ "pkcs11", "pkcs11/config_file", ] [profile.release] opt-level = 'z' # Optimize for size lto = true # Enable link-time optimization codegen-units = 1 # Reduce number of codegen units to increase optimizations panic = 'abort' # Abort on panic strip = true # Strip symbols from binary nethsm-pkcs11-2.0.0/LICENSE000066400000000000000000000513541507371613300151320ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ====== Dependencies: webpki: Except as otherwise noted, this project is licensed under the following (ISC-style) terms: Copyright 2015 Brian Smith. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The files under third-party/chromium are licensed as described in third-party/chromium/LICENSE. --- ring: Note that it is easy for this file to get out of sync with the licenses in the source code files. It's recommended to compare the licenses in the source code with what's mentioned here. *ring* is derived from BoringSSL, so the licensing situation in *ring* is similar to BoringSSL. *ring* uses an ISC-style license like BoringSSL for code in new files, including in particular all the Rust code: Copyright 2015-2016 Brian Smith. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL licensing. Files that are completely new have a Google copyright and an ISC license. This license is reproduced at the bottom of this file. Contributors to BoringSSL are required to follow the CLA rules for Chromium: https://cla.developers.google.com/clas Files in third_party/ have their own licenses, as described therein. The MIT license, for third_party/fiat, which, unlike other third_party directories, is compiled into non-test libraries, is included below. The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org. The following are Google-internal bug numbers where explicit permission from some authors is recorded for use of their work: 27287199 27287880 27287883 OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ ISC license used for completely new code in BoringSSL: /* Copyright (c) 2015, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ The code in third_party/fiat carries the MIT license: Copyright (c) 2015-2016 the fiat-crypto authors (see https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nethsm-pkcs11-2.0.0/README.md000066400000000000000000000072061507371613300154010ustar00rootroot00000000000000# PKCS#11 Module for the Nitrokey NetHSM [![codecov.io][codecov-badge]][codecov-url] [codecov-badge]: https://codecov.io/gh/nitrokey/nethsm-pkcs11/branch/main/graph/badge.svg [codecov-url]: https://app.codecov.io/gh/nitrokey/nethsm-pkcs11/tree/main This module allows to use a [Nitrokey NetHSM](https://www.nitrokey.com/products/nethsm) as a backend for PKCS#11 operations. See the [list of supported features](./features.md) for more details. ## Download Download the latest binary from the [release page](https://github.com/Nitrokey/nethsm-pkcs11/releases). ## Documentation Follow the [documentation](https://docs.nitrokey.com/nethsm/pkcs11-setup.html) for usage instructions. ## Compatibility nethsm-pkcs11 is compatible with these NetHSM versions: | NetHSM Version | Compatibility | Notes | | :------------: | :-----------: | ----- | | [v1.0][nethsm-v1.0] | limited | | | [v2.0][nethsm-v2.0] | limited | | | [v2.1][nethsm-v2.1] | limited | | | [v2.2][nethsm-v2.2] | limited | | | [v3.0][nethsm-v3.0] | limited | RSA signatures using PKCS1 mechanisms do not work. | | [v3.1][nethsm-v3.1] | full | | [nethsm-v1.0]: https://github.com/Nitrokey/nethsm/releases/tag/v1.0 [nethsm-v2.0]: https://github.com/Nitrokey/nethsm/releases/tag/v2.0 [nethsm-v2.1]: https://github.com/Nitrokey/nethsm/releases/tag/v2.1 [nethsm-v2.2]: https://github.com/Nitrokey/nethsm/releases/tag/v2.2 [nethsm-v3.0]: https://github.com/Nitrokey/nethsm/releases/tag/v3.0 [nethsm-v3.1]: https://github.com/Nitrokey/nethsm/releases/tag/v3.1 Full compatibility means that all features of the NetHSM PKCS#11 module can be used with this version. Limited compatibility means that only some features are available for this version. See the [changelog](./CHANGELOG.md) for more detailed information on the version requirements for new features. ## Debug Options Set the `RUST_LOG` env variable to `trace`, `debug`, `info`, `warn` or `err` to change the logging level. ## Docker Examples For testing and development purposes there are two examples using the PKCS11 driver with Nginx and Apache. They require each a certificate built with the `container//generate.sh`. They can be built with: ```bash # Building the images docker build -t nginx-testing -f container/nginx/Dockerfile . docker build -t apache-testing -f container/apache/Dockerfile . ``` Assuming that a NetHSM is runnig on localhost:8443, they can then be run with : ```bash docker run --net=host nginx-testing:latest docker run --net=host apache-testing:latest ``` The NetHSM is expected to have be provisionned with the following configuration: ```bash nitropy nethsm --host localhost:8443 --no-verify-tls provision -u 0123456789 -a Administrator nitropy nethsm --host localhost:8443 --no-verify-tls add-user -n Operator -u operator -p opPassphrase -r Operator ``` ## Testing retries There is a set of tests that run with multiple instances and test the retry and timeout mechanisms. They require: access to `sudo` (or being run as root) and `podman`. You can run the command: ```bash USE_SUDO=true cargo t -p nethsm_pkcs11 --test basic -- multi_instance_retries # Or remove the use of sudo if running as root cargo t -p nethsm_pkcs11 --test basic -- multi_instance_retries ``` ## Building Required are `gcc` and a working Rust toolchain of at least version (MSRV) 1.70. ``` cargo build --release ``` The dynamic library will be in `${CARGO_TARGET_DIR:-target}/release/libnethsm_pkcs11.so`. ### Alpine Linux You need to install `musl-dev` and `gcc`: ``` apk add musl-dev gcc ``` To build on Alpine Linux you will need to add the C argument `target-feature=-crt-static`: ``` RUSTFLAGS="-C target-feature=-crt-static" cargo build --release ``` nethsm-pkcs11-2.0.0/codecov.yml000066400000000000000000000000271507371613300162610ustar00rootroot00000000000000codecov: branch: mainnethsm-pkcs11-2.0.0/container/000077500000000000000000000000001507371613300160775ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/container/apache/000077500000000000000000000000001507371613300173205ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/container/apache/Dockerfile000066400000000000000000000011701507371613300213110ustar00rootroot00000000000000FROM docker.io/rust:1.72-bookworm AS builder WORKDIR /rust ADD '.' /rust/build RUN cd build && cargo build --release FROM docker.io/httpd:2.4 RUN apt-get update && apt-get install -y --no-install-recommends \ libengine-pkcs11-openssl \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /rust/build/target/release/libnethsm_pkcs11.so /usr/lib/x86_64-linux-gnu/pkcs11/libnethsm_pkcs11.so COPY _certificate.pem /certs/certificate.pem ADD container/apache/openssl.cnf /etc/ssl/openssl.cnf ADD container/apache/p11nethsm.conf /etc/nitrokey/p11nethsm.conf ADD container/apache/httpd.conf /usr/local/apache2/conf/httpd.conf nethsm-pkcs11-2.0.0/container/apache/generate.sh000077500000000000000000000033501507371613300214520ustar00rootroot00000000000000#!/bin/bash set -e HOST='localhost:8443' ADMIN_ACCOUNT='admin' ADMIN_ACCOUNT_PWD='Administrator' NETHSM_PKCS11_LIBRARY_PATH="target/release/libnethsm_pkcs11.so" CREDENTIALS="${ADMIN_ACCOUNT}:${ADMIN_ACCOUNT_PWD}" #Use here-documents to temporarily store the OpenSSL configuration. After this command the temporary file will be available at /dev/fd/3. exec 3<<< " openssl_conf = openssl_init [openssl_init] engines = engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 MODULE_PATH = ${NETHSM_PKCS11_LIBRARY_PATH} init = 0 " curl --include --insecure --user $CREDENTIALS --request DELETE \ "https://${HOST}/api/v1/keys/webserver" curl --include --insecure --user $CREDENTIALS --request POST \ "https://${HOST}/api/v1/keys/generate" \ --header 'Content-Type: application/json' \ --data ' { "mechanisms": ["RSA_Decryption_RAW", "RSA_Decryption_PKCS1", "RSA_Decryption_OAEP_MD5", "RSA_Decryption_OAEP_SHA1", "RSA_Decryption_OAEP_SHA224", "RSA_Decryption_OAEP_SHA256", "RSA_Decryption_OAEP_SHA384", "RSA_Decryption_OAEP_SHA512", "RSA_Signature_PKCS1", "RSA_Signature_PSS_MD5", "RSA_Signature_PSS_SHA1", "RSA_Signature_PSS_SHA224", "RSA_Signature_PSS_SHA256", "RSA_Signature_PSS_SHA384", "RSA_Signature_PSS_SHA512"], "type": "RSA", "length": 2048, "id": "webserver" } ' export OPENSSL_CONF="/dev/fd/3" P11NETHSM_CONFIG_FILE="p11nethsm.conf" openssl req -new -x509 -out ./_certificate.pem -days 365 -subj "/CN=yourdomain.com" -engine pkcs11 -keyform engine -key "pkcs11:object=webserver;type=public" curl -k -i -w '\n' -u $CREDENTIALS -X PUT \ "https://${HOST}/api/v1/keys/webserver/cert" \ -H 'Content-Type: application/octet-stream' \ --data-binary '@_certificate.pem' nethsm-pkcs11-2.0.0/container/apache/httpd.conf000066400000000000000000000054711507371613300213210ustar00rootroot00000000000000ServerRoot "/usr/local/apache2" # Listen 80 # Listen 443 Listen 8081 LoadModule socache_shmcb_module modules/mod_socache_shmcb.so LoadModule mpm_event_module modules/mod_mpm_event.so LoadModule authn_file_module modules/mod_authn_file.so LoadModule authn_core_module modules/mod_authn_core.so LoadModule authz_host_module modules/mod_authz_host.so LoadModule authz_groupfile_module modules/mod_authz_groupfile.so LoadModule authz_user_module modules/mod_authz_user.so LoadModule authz_core_module modules/mod_authz_core.so LoadModule access_compat_module modules/mod_access_compat.so LoadModule auth_basic_module modules/mod_auth_basic.so LoadModule reqtimeout_module modules/mod_reqtimeout.so LoadModule filter_module modules/mod_filter.so LoadModule mime_module modules/mod_mime.so LoadModule log_config_module modules/mod_log_config.so LoadModule env_module modules/mod_env.so LoadModule headers_module modules/mod_headers.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule version_module modules/mod_version.so LoadModule ssl_module modules/mod_ssl.so LoadModule unixd_module modules/mod_unixd.so LoadModule status_module modules/mod_status.so LoadModule autoindex_module modules/mod_autoindex.so LoadModule dir_module modules/mod_dir.so User www-data Group www-data AllowOverride none Require all denied DocumentRoot "/usr/local/apache2/htdocs" Options Indexes FollowSymLinks AllowOverride None Require all granted DirectoryIndex index.html Require all denied ErrorLog /tmp/error.log LogLevel debug LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" common LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio CustomLog /tmp/apache.log common AllowOverride None Options None Require all granted RequestHeader unset Proxy early TypesConfig conf/mime.types AddType application/x-compress .Z AddType application/x-gzip .gz .tgz Include conf/extra/proxy-html.conf SSLRandomSeed startup builtin SSLRandomSeed connect builtin DocumentRoot /usr/local/apache2/htdocs SSLEngine on SSLCertificateFile /certs/certificate.pem SSLCertificateKeyFile "pkcs11:object=webserver" ErrorLog /tmp/a-error.log CustomLog /tmp/a-access.log combined nethsm-pkcs11-2.0.0/container/apache/openssl.cnf000066400000000000000000000004331507371613300214730ustar00rootroot00000000000000openssl_conf = openssl_init [openssl_init] engines=engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 dynamic_path = /usr/lib/x86_64-linux-gnu/engines-3/pkcs11.so MODULE_PATH = /usr/lib/x86_64-linux-gnu/pkcs11/libnethsm_pkcs11.so init = 0 nethsm-pkcs11-2.0.0/container/apache/p11nethsm.conf000066400000000000000000000004161507371613300220100ustar00rootroot00000000000000log_file: /tmp/hsm.log log_level: Trace slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" instances: - url: "https://localhost:8443/api/v1" danger_insecure_cert: true nethsm-pkcs11-2.0.0/container/ejbca/000077500000000000000000000000001507371613300171435ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/container/ejbca/Dockerfile000066400000000000000000000005211507371613300211330ustar00rootroot00000000000000FROM rust AS builder WORKDIR /rust ADD . build RUN cd build && cargo build --release FROM almalinux AS alma RUN dnf install -y opensc FROM keyfactor/ejbca-ce COPY --from=builder /rust/build/target/release/libnethsm_pkcs11.so /usr/lib/nitrokey/libnethsm_pkcs11.so COPY --from=alma /usr/lib64/pkcs11-spy.so /usr/lib64/pkcs11-spy.so nethsm-pkcs11-2.0.0/container/ejbca/Dockerfile.dockerignore000066400000000000000000000000171507371613300236050ustar00rootroot00000000000000.git container nethsm-pkcs11-2.0.0/container/ejbca/README.md000066400000000000000000000016701507371613300204260ustar00rootroot00000000000000EJBCA Example ------------- ### Startup 1. `docker compose up --build -d` 2. `docker compose logs -f` 3. After previous command settled, connect to `https://localhost:9443` with a browser 4. "CA Functions" -> "Crypto Tokens" -> "Create new..." 1. Type: `PKCS#11`, PKCS#11 Library: `NetHSM`, set "Authentication Code" to any value 2. `Save` 3. `Generate new key pair` 5. `docker compose down -v` ### Configuration Details * If `logs/` is used via `LOG_STORAGE_LOCATION` (it is by default), make sure it is world-writable: `chmod a+w logs` * The attributes file is currently not needed/used * `pkcs11spy` is configured to trace the pkcs11 calls, to only use `libnethsm_pkcs11.so` make sure to remove the `PKCS11SPY*` environment variables and update `web.properties` like that: ```diff - cryptotoken.p11.lib.245.file=/usr/lib64/pkcs11-spy.so + cryptotoken.p11.lib.245.file=/usr/lib/nitrokey/libnethsm_pkcs11.so ``` nethsm-pkcs11-2.0.0/container/ejbca/compose.yml000066400000000000000000000026511507371613300213370ustar00rootroot00000000000000services: nethsm: image: nitrokey/nethsm:4241c247 platform: linux/amd64 ports: - "8443:8443" networks: - nethsm-network nethsm-provisioner: image: curlimages/curl:latest depends_on: - nethsm volumes: - ./provision.sh:/provision.sh:ro command: ["/bin/sh", "/provision.sh"] networks: - nethsm-network environment: NETHSM_URL: https://nethsm:8443 ejbca: hostname: ejbca image: ejbca_test platform: linux/amd64 build: context: ../.. dockerfile: container/ejbca/Dockerfile depends_on: nethsm-provisioner: condition: service_completed_successfully volumes: - ./logs:/logs - ./p11nethsm.conf:/etc/nitrokey/p11nethsm.conf:ro - ./web.properties:/etc/ejbca/conf/web.properties:ro # - ./nethsm_attributes.conf:/etc/nitrokey/nethsm_attributes.conf:ro ports: - "9080:8080" - "9443:8443" networks: - nethsm-network environment: TLS_SETUP_ENABLED: simple # LOG_LEVEL_APP: DEBUG # LOG_LEVEL_SERVER: DEBUG # LOG_LEVEL_SERVER_SUBSYSTEMS: DEBUG JAVA_OPTS_CUSTOM: "-Xms128m -Xmx13904m -Xss256k -XX:MetaspaceSize=160m -XX:MaxMetaspaceSize=2048m -Djava.security.debug=sunpkcs11,pkcs11keystore" LOG_STORAGE_LOCATION: /logs/ PKCS11SPY: /usr/lib/nitrokey/libnethsm_pkcs11.so PKCS11SPY_OUTPUT: /logs/pkcs11spy.log networks: nethsm-network: nethsm-pkcs11-2.0.0/container/ejbca/logs/000077500000000000000000000000001507371613300201075ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/container/ejbca/logs/rotate.sh000077500000000000000000000002371507371613300217460ustar00rootroot00000000000000#!/bin/bash TIMESTAMP=$(date "+%y%m%d%H%M%S") mkdir -p saved for i in *.log ; do cp $i saved/${TIMESTAMP}_$i cat /dev/null >$i cat /dev/null >$i done nethsm-pkcs11-2.0.0/container/ejbca/nethsm_attributes.conf000066400000000000000000000014511507371613300235570ustar00rootroot00000000000000allowLegacy = true attributes(*, CKO_PUBLIC_KEY, *) = { CKA_TOKEN = false CKA_ENCRYPT = true CKA_VERIFY = true CKA_WRAP = true } attributes(*, CKO_PRIVATE_KEY, *) = { CKA_DERIVE = false CKA_TOKEN = true CKA_PRIVATE = true CKA_SENSITIVE = true CKA_EXTRACTABLE = false CKA_DECRYPT = true CKA_SIGN = true CKA_UNWRAP = true } disabledMechanisms = { CKM_SHA1_RSA_PKCS CKM_SHA256_RSA_PKCS CKM_SHA384_RSA_PKCS CKM_SHA512_RSA_PKCS CKM_MD2_RSA_PKCS CKM_MD5_RSA_PKCS CKM_DSA_SHA1 CKM_ECDSA_SHA1 CKM_ECDSA_SHA224 CKM_ECDSA_SHA256 CKM_ECDSA_SHA384 CKM_ECDSA_SHA512 } attributes(*, CKO_SECRET_KEY, *) = { CKA_SENSITIVE = true CKA_EXTRACTABLE = false CKA_ENCRYPT = true CKA_DECRYPT = true CKA_SIGN = true CKA_VERIFY = true CKA_WRAP = true CKA_UNWRAP = true } nethsm-pkcs11-2.0.0/container/ejbca/p11nethsm.conf000066400000000000000000000005451507371613300216360ustar00rootroot00000000000000log_level: Trace log_file: /logs/p11nethsm.log slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "OperatorOperator" administrator: username: "admin" password: "AdminPassphrase" instances: - url: "https://nethsm:8443/api/v1" danger_insecure_cert: true nethsm-pkcs11-2.0.0/container/ejbca/provision.sh000077500000000000000000000033371507371613300215400ustar00rootroot00000000000000#!/bin/bash HOST=nethsm:8443 while ! curl -f -k -s https://${HOST}/api/v1/health/alive ; do sleep 1 ; done curl -k -v "https://${HOST}/api/v1/provision" -X POST -d '{"unlockPassphrase":"UnlockPassphrase","adminPassphrase":"AdminPassphrase","systemTime":"'$(date -u "+%Y-%m-%dT%H:%M:%SZ")'"}' curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/users/operator" -X PUT -d '{"role":"Operator", "passphrase":"OperatorOperator","realName":"Operator"}' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/keys/generate" -X POST -d '{ "mechanisms": ["ECDSA_Signature"], "type": "EC_P256", "id": "p256"}' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/keys/generate" -X POST -d '{ "mechanisms": ["ECDSA_Signature"], "type": "EC_P521", "id": "p521"}' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/keys/generate" -X POST -d '{ "mechanisms": ["RSA_Signature_PKCS1", "RSA_Decryption_PKCS1"], "type": "RSA", "length": 2048, "id":"rsa2048" }' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/keys/generate" -X POST -d '{ "mechanisms": ["RSA_Signature_PKCS1", "RSA_Decryption_PKCS1"], "type": "RSA", "length": 4096, "id":"rsa4096" }' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/keys/generate" -X POST -d '{ "mechanisms": ["EdDSA_Signature"], "type": "Curve25519", "id": "ed25519"}' -H "Content-Type: application/json" #curl -k -v "https://admin:AdminPassphrase@${HOST}/api/v1/config/logging" -X PUT -d '{"ipAddress":"0.0.0.0","port":0,"logLevel":"warning"}' -H "content-type: application/json" nethsm-pkcs11-2.0.0/container/ejbca/web.properties000066400000000000000000000023021507371613300220330ustar00rootroot00000000000000httpserver.pubhttp=80 httpserver.pubhttps=443 httpserver.privhttps=443 httpserver.external.privhttps=443 web.reqcertindb=false # The CA servers DNS host name, must exist on client using the admin GUI. httpsserver.hostname=ejbca #cryptotoken.p11.lib.114.file=/opt/keyfactor/cloudhsm/lib/libliquidsec_pkcs11.so #cryptotoken.p11.lib.255.name=P11 Proxy #cryptotoken.p11.lib.255.file=/opt/keyfactor/p11proxy-client/p11proxy-client.so #cryptotoken.p11.lib.255.canGenerateKeyMsg=ClientToolBox must be used to generate keys for this HSM provider. # Normally key generation will be allowed via the UI #cryptotoken.p11.lib.255.canGenerateKey=true # Enable usage of Azure Key Vault Crypto Token in the Admin UI #keyvault.cryptotoken.enabled=true # Enable usage of AWS KMS Crypto Token in the Admin UI #awskms.cryptotoken.enabled=true # Enable P11NG for EJBCA to use for CloudHSM and other PKCS11 integrations #p11ng.cryptotoken.enabled=true web.docbaseuri=disabled web.reqcert=false cryptotoken.p11.lib.245.name=NetHSM cryptotoken.p11.lib.245.file=/usr/lib64/pkcs11-spy.so cryptotoken.p11.lib.245.canGenerateKey=true #cryptotoken.p11.attr.245.name=NetHSM #cryptotoken.p11.attr.245.file=/etc/nitrokey/nethsm_attributes.conf nethsm-pkcs11-2.0.0/container/nginx/000077500000000000000000000000001507371613300172225ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/container/nginx/Dockerfile000066400000000000000000000012501507371613300212120ustar00rootroot00000000000000FROM docker.io/rust:1.70-buster AS builder WORKDIR /rust ADD '.' /rust/build RUN cd build && cargo build --release FROM docker.io/nginx:stable RUN apt-get update && apt-get install -y --no-install-recommends \ libengine-pkcs11-openssl \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /rust/build/target/release/libnethsm_pkcs11.so /usr/lib/x86_64-linux-gnu/pkcs11/libnethsm_pkcs11.so COPY _certificate.pem /certs/certificate.pem ADD container/nginx/openssl.cnf /etc/ssl/openssl.cnf ADD container/nginx/nginx.conf /etc/nginx/nginx.conf ADD container/nginx/p11nethsm.conf /etc/nitrokey/p11nethsm.conf ADD container/nginx/default.conf /etc/nginx/conf.d/default.conf nethsm-pkcs11-2.0.0/container/nginx/default.conf000066400000000000000000000034521507371613300215210ustar00rootroot00000000000000server { # listen 433 ssl; listen 8081 ssl; server_name localhost; ssl_certificate /certs/certificate.pem; # Syntax using label engine:pkcs11:slot_-label_; # Syntax using ID engine:pkcs11:slot_-id_; ssl_certificate_key "engine:pkcs11:pkcs11:object=webserver;pin-value=opPassphrase"; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10s; ssl_session_tickets off; #ssl_protocols TLSv1.2; TLSv1.3; ssl_protocols TLSv1.3; # ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers off; # HSTS (ngx_http_headers_module is required) (63072000 seconds) add_header Strict-Transport-Security "max-age=63072000" always; # OCSP stapling ssl_stapling on; ssl_stapling_verify on; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } nethsm-pkcs11-2.0.0/container/nginx/generate.sh000077500000000000000000000033501507371613300213540ustar00rootroot00000000000000#!/bin/bash set -e HOST='localhost:8443' ADMIN_ACCOUNT='admin' ADMIN_ACCOUNT_PWD='Administrator' NETHSM_PKCS11_LIBRARY_PATH="target/release/libnethsm_pkcs11.so" CREDENTIALS="${ADMIN_ACCOUNT}:${ADMIN_ACCOUNT_PWD}" #Use here-documents to temporarily store the OpenSSL configuration. After this command the temporary file will be available at /dev/fd/3. exec 3<<< " openssl_conf = openssl_init [openssl_init] engines = engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 MODULE_PATH = ${NETHSM_PKCS11_LIBRARY_PATH} init = 0 " curl --include --insecure --user $CREDENTIALS --request DELETE \ "https://${HOST}/api/v1/keys/webserver" curl --include --insecure --user $CREDENTIALS --request POST \ "https://${HOST}/api/v1/keys/generate" \ --header 'Content-Type: application/json' \ --data ' { "mechanisms": ["RSA_Decryption_RAW", "RSA_Decryption_PKCS1", "RSA_Decryption_OAEP_MD5", "RSA_Decryption_OAEP_SHA1", "RSA_Decryption_OAEP_SHA224", "RSA_Decryption_OAEP_SHA256", "RSA_Decryption_OAEP_SHA384", "RSA_Decryption_OAEP_SHA512", "RSA_Signature_PKCS1", "RSA_Signature_PSS_MD5", "RSA_Signature_PSS_SHA1", "RSA_Signature_PSS_SHA224", "RSA_Signature_PSS_SHA256", "RSA_Signature_PSS_SHA384", "RSA_Signature_PSS_SHA512"], "type": "RSA", "length": 2048, "id": "webserver" } ' export OPENSSL_CONF="/dev/fd/3" P11NETHSM_CONFIG_FILE="p11nethsm.conf" openssl req -new -x509 -out ./_certificate.pem -days 365 -subj "/CN=yourdomain.com" -engine pkcs11 -keyform engine -key "pkcs11:object=webserver;type=public" curl -k -i -w '\n' -u $CREDENTIALS -X PUT \ "https://${HOST}/api/v1/keys/webserver/cert" \ -H 'Content-Type: application/octet-stream' \ --data-binary '@_certificate.pem' nethsm-pkcs11-2.0.0/container/nginx/nginx.conf000066400000000000000000000013721507371613300212170ustar00rootroot00000000000000 user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; #Add engine in the main section ssl_engine pkcs11; #Without those two options, issues master_process off; #daemon off; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } nethsm-pkcs11-2.0.0/container/nginx/openssl.cnf000066400000000000000000000004401507371613300213730ustar00rootroot00000000000000openssl_conf = openssl_init [openssl_init] engines=engine_section [engine_section] pkcs11 = pkcs11_section [pkcs11_section] engine_id = pkcs11 dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so MODULE_PATH = /usr/lib/x86_64-linux-gnu/pkcs11/libnethsm_pkcs11.so init = 0 nethsm-pkcs11-2.0.0/container/nginx/p11nethsm.conf000066400000000000000000000003461507371613300217140ustar00rootroot00000000000000slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" instances: - url: "https://localhost:8443/api/v1" danger_insecure_cert: true nethsm-pkcs11-2.0.0/features.md000066400000000000000000000235501507371613300162620ustar00rootroot00000000000000# Features - :white_check_mark: : Fully functional - :warning: : Functional but with limitations - :x: : Not implemented ## Base features | Feature | Status | Notes | | ----------------- | ------------------ | -------------------------------- | | C_GetFunctionList | :white_check_mark: | | | C_Initialize | :white_check_mark: | Custom mutexes are not supported | | C_Finalize | :white_check_mark: | | | C_GetInfo | :white_check_mark: | | ## Session | Feature | Status | Notes | | ------------------- | ------------------ | --------------------------------- | | C_OpenSession | :white_check_mark: | Notify not supported | | C_CloseSession | :white_check_mark: | | | C_CloseAllSessions | :white_check_mark: | | | C_GetSessionInfo | :white_check_mark: | | | C_GetOperationState | :x: | No demand | | C_SetOperationState | :x: | No demand | | C_GetFunctionStatus | :white_check_mark: | Returns CKR_FUNCTION_NOT_PARALLEL | | C_CancelFunction | :white_check_mark: | Returns CKR_FUNCTION_NOT_PARALLEL | ## Token | Feature | Status | Notes | | ------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | C_GetSlotList | :white_check_mark: | | | C_GetSlotInfo | :white_check_mark: | | | C_GetTokenInfo | :white_check_mark: | | | C_InitToken | :x: | | | C_GetMechanismList | :white_check_mark: | | | C_GetMechanismInfo | :white_check_mark: | | | C_Login | :white_check_mark: | The PIN is used as the password, login as SO means logging in with an Administrator account ("admin" username set by default) | | C_Logout | :white_check_mark: | | | C_WaitForSlotEvent | :white_check_mark: | CKF_DONT_BLOCK set: checks if a slot has changed state since last check. CKF_DONT_BLOCK clear: waits for a slot to change state | ## Decrypt Mechanisms: - AES-CBC - RSA-X-509 (Raw RSA) - RSA-PKCS - RSA-PKCS-OAEP: data hashed with MD5/SHA1/SHA224/SHA256/SHA384/SHA512 | Feature | Status | Notes | | --------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- | | C_DecryptInit | :white_check_mark: | | | C_Decrypt | :white_check_mark: | | | C_DecryptUpdate | :white_check_mark: | The length of the output buffer will always be 0. The decrypted data will be all sent in the C_DecryptFinal call | | C_DecryptFinal | :white_check_mark: | | | C_DecryptVerifyUpdate | :x: | Verify is not supported by NetHSM | ## Encrypt Mechanisms: - AES-CBC | Feature | Status | Notes | | --------------- | ------------------ | ----------------------------------------------------- | | C_EncryptInit | :white_check_mark: | | | C_Encrypt | :white_check_mark: | | | C_EncryptUpdate | :white_check_mark: | | | C_EncryptFinal | :white_check_mark: | AES-CBC expects messages with a length multiple of 16 | ## Sign Mechanisms: - RSA-PKCS - SHA1-RSA-PKCS (Hash is computed by the PKCS#11 module) - SHA224-RSA-PKCS (Hash is computed by the PKCS#11 module) - SHA256-RSA-PKCS (Hash is computed by the PKCS#11 module) - SHA384-RSA-PKCS (Hash is computed by the PKCS#11 module) - SHA512-RSA-PKCS (Hash is computed by the PKCS#11 module) - RSA-PKCS-PSS: expects already hashed value with MD5/SHA1/SHA224/SHA256/SHA384/SHA512 (set the correct one in CK_RSA_PKCS_PSS_PARAMS) - SHA1-RSA-PKCS-PSS (Hash is computed by the PKCS#11 module) - SHA224-RSA-PKCS-PSS (Hash is computed by the PKCS#11 module) - SHA256-RSA-PKCS-PSS (Hash is computed by the PKCS#11 module) - SHA384-RSA-PKCS-PSS (Hash is computed by the PKCS#11 module) - SHA512-RSA-PKCS-PSS (Hash is computed by the PKCS#11 module) - EDDSA - ECDSA - ECDSA-SHA1 (Hash is computed by the PKCS#11 module) - ECDSA-SHA224 (Hash is computed by the PKCS#11 module) - ECDSA-SHA256 (Hash is computed by the PKCS#11 module) - ECDSA-SHA384 (Hash is computed by the PKCS#11 module) - ECDSA-SHA512 (Hash is computed by the PKCS#11 module) | Feature | Status | Notes | | ------------------- | ------------------ | ----------------------- | | C_SignInit | :white_check_mark: | | | C_Sign | :white_check_mark: | | | C_SignUpdate | :white_check_mark: | | | C_SignFinal | :white_check_mark: | | | C_SignRecoverInit | :x: | Not supported by NetHSM | | C_SignRecover | :x: | Not supported by NetHSM | | C_SignEncryptUpdate | :x: | Not supported by NetHSM | ## Digest :x: Digest is not supported by NetHSM ## Verify :x: Verify is not supported by NetHSM ## Generation | Feature | Status | Notes | | ----------------- | ------------------ | ---------------------------------------- | | C_GenerateKey | :white_check_mark: | Needs Administrator | | C_GenerateKeyPair | :white_check_mark: | Needs Administrator | | C_GenerateRandom | :white_check_mark: | | | C_SeedRandom | :warning: | Returns OK but the arguments are ignored | | C_WrapKey | :x: | Not supported by NetHSM | | C_UnwrapKey | :x: | Not supported by NetHSM | | C_DeriveKey | :x: | Not supported by NetHSM | ## Objects | Feature | Status | Notes | | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | C_FindObjectsInit | :warning: | Only lists the available keys | | C_FindObjects | :warning: | Only lists the available keys | | C_FindObjectsFinal | :white_check_mark: | | | C_GetAttributeValue | :white_check_mark: | | | C_GetObjectSize | :white_check_mark: | | | C_CreateObject | :warning: | Needs to be logged as Administrator (SO). Only private keys can be added. | | C_CopyObject | :white_check_mark: | Always returns CKR_ACTION_PROHIBITED | | C_DestroyObject | :warning: | Needs to be logged as Administrator (SO). Only private keys can be deleted. | | C_SetAttributeValue | :white_check_mark: | Needs to be logged as Administrator (SO). Only supports changing CKA_ID. | ## Pin management | Feature | Status | Notes | | --------- | ------------------ | -------------------------------- | | C_InitPIN | :x: | | | C_SetPIN | :white_check_mark: | Changes the password of the user | nethsm-pkcs11-2.0.0/fork-tests/000077500000000000000000000000001507371613300162165ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/fork-tests/Makefile000066400000000000000000000002111507371613300176500ustar00rootroot00000000000000.PHONY: test test: fork_test P11NETHSM_CONFIG_FILE=../p11nethsm.conf ./fork_test fork_test: fork_test.c gcc fork_test.c -o fork_test nethsm-pkcs11-2.0.0/fork-tests/fork_test.c000066400000000000000000000022601507371613300203620ustar00rootroot00000000000000#include "dlfcn.h" #include "pkcs11.h" #include "stdio.h" #include "unistd.h" #include "sys/wait.h" int main() { void *handle = dlopen("../target/release/libnethsm_pkcs11.so", RTLD_LAZY) ; if (!handle) { fprintf(stderr, "%s\n", dlerror()); return 1; } dlerror(); CK_C_GetFunctionList c_get_function_list = dlsym(handle, "C_GetFunctionList"); char * error = dlerror(); if (error != NULL) { fprintf(stderr, "%s\n", error); return 1; } struct _CK_FUNCTION_LIST *flist; c_get_function_list(&flist); flist->C_Initialize(NULL); pid_t p = fork(); if (p < 0) { perror("Fork failed"); return 1; } int wstatus = 0; if (p == 0 ) { flist->C_Initialize(NULL); CK_SLOT_ID slotId = CK_UNAVAILABLE_INFORMATION; CK_SESSION_HANDLE session; CK_RV rv = flist->C_OpenSession(0,CKF_SERIAL_SESSION, NULL, NULL, &session); if (rv != CKR_OK) { printf("Failed open session: %lu", rv); } unsigned char random[8]; rv = flist->C_GenerateRandom(session, random, 8); if (rv != CKR_OK) { printf("Failed get random: %lu", rv); } return 0; } else { wait(&wstatus); return WEXITSTATUS(wstatus); } } nethsm-pkcs11-2.0.0/fork-tests/pkcs11.h000066400000000000000000001556701507371613300175070ustar00rootroot00000000000000/* pkcs11.h Copyright 2006, 2007 g10 Code GmbH Copyright 2006 Andreas Jellinghaus Copyright 2017 Red Hat, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* Please submit any changes back to the p11-kit project at https://github.com/p11-glue/p11-kit/, so that they can be picked up by other projects from there as well. */ /* This file is a modified implementation of the PKCS #11 standard by OASIS group. It is mostly a drop-in replacement, with the following change: This header file does not require any macro definitions by the user (like CK_DEFINE_FUNCTION etc). In fact, it defines those macros for you (if useful, some are missing, let me know if you need more). There is an additional API available that does comply better to the GNU coding standard. It can be switched on by defining CRYPTOKI_GNU before including this header file. For this, the following changes are made to the specification: All structure types are changed to a "struct ck_foo" where CK_FOO is the type name in PKCS #11. All non-structure types are changed to ck_foo_t where CK_FOO is the lowercase version of the type name in PKCS #11. The basic types (CK_ULONG et al.) are removed without substitute. All members of structures are modified in the following way: Type indication prefixes are removed, and underscore characters are inserted before words. Then the result is lowercased. Note that function names are still in the original case, as they need for ABI compatibility. CK_FALSE, CK_TRUE and NULL_PTR are removed without substitute. Use . If CRYPTOKI_COMPAT is defined before including this header file, then none of the API changes above take place, and the API is the one defined by the PKCS #11 standard. */ #ifndef PKCS11_H #define PKCS11_H 1 #if defined(__cplusplus) extern "C" { #endif /* The version of cryptoki we implement. The revision is changed with each modification of this file. */ #define CRYPTOKI_VERSION_MAJOR 2 #define CRYPTOKI_VERSION_MINOR 40 #define P11_KIT_CRYPTOKI_VERSION_REVISION 0 /* Compatibility interface is default, unless CRYPTOKI_GNU is given. */ #ifndef CRYPTOKI_GNU #ifndef CRYPTOKI_COMPAT #define CRYPTOKI_COMPAT 1 #endif #endif /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) /* There is a matching pop below. */ #pragma pack(push, cryptoki, 1) #ifdef CRYPTOKI_EXPORTS #define CK_SPEC __declspec(dllexport) #else #define CK_SPEC __declspec(dllimport) #endif #else #define CK_SPEC #endif #ifdef CRYPTOKI_COMPAT /* If we are in compatibility mode, switch all exposed names to the PKCS #11 variant. There are corresponding #undefs below. */ #define ck_flags_t CK_FLAGS #define ck_version _CK_VERSION #define ck_info _CK_INFO #define cryptoki_version cryptokiVersion #define manufacturer_id manufacturerID #define library_description libraryDescription #define library_version libraryVersion #define ck_notification_t CK_NOTIFICATION #define ck_slot_id_t CK_SLOT_ID #define ck_slot_info _CK_SLOT_INFO #define slot_description slotDescription #define hardware_version hardwareVersion #define firmware_version firmwareVersion #define ck_token_info _CK_TOKEN_INFO #define serial_number serialNumber #define max_session_count ulMaxSessionCount #define session_count ulSessionCount #define max_rw_session_count ulMaxRwSessionCount #define rw_session_count ulRwSessionCount #define max_pin_len ulMaxPinLen #define min_pin_len ulMinPinLen #define total_public_memory ulTotalPublicMemory #define free_public_memory ulFreePublicMemory #define total_private_memory ulTotalPrivateMemory #define free_private_memory ulFreePrivateMemory #define utc_time utcTime #define ck_session_handle_t CK_SESSION_HANDLE #define ck_user_type_t CK_USER_TYPE #define ck_state_t CK_STATE #define ck_session_info _CK_SESSION_INFO #define slot_id slotID #define device_error ulDeviceError #define ck_object_handle_t CK_OBJECT_HANDLE #define ck_object_class_t CK_OBJECT_CLASS #define ck_hw_feature_type_t CK_HW_FEATURE_TYPE #define ck_key_type_t CK_KEY_TYPE #define ck_certificate_type_t CK_CERTIFICATE_TYPE #define ck_attribute_type_t CK_ATTRIBUTE_TYPE #define ck_attribute _CK_ATTRIBUTE #define value pValue #define value_len ulValueLen #define count ulCount #define ck_date _CK_DATE #define ck_mechanism_type_t CK_MECHANISM_TYPE #define ck_mechanism _CK_MECHANISM #define parameter pParameter #define parameter_len ulParameterLen #define params pParams #define ck_mechanism_info _CK_MECHANISM_INFO #define min_key_size ulMinKeySize #define max_key_size ulMaxKeySize #define ck_param_type CK_PARAM_TYPE #define ck_otp_param CK_OTP_PARAM #define ck_otp_params CK_OTP_PARAMS #define ck_otp_signature_info CK_OTP_SIGNATURE_INFO #define ck_rv_t CK_RV #define ck_notify_t CK_NOTIFY #define ck_function_list _CK_FUNCTION_LIST #define ck_createmutex_t CK_CREATEMUTEX #define ck_destroymutex_t CK_DESTROYMUTEX #define ck_lockmutex_t CK_LOCKMUTEX #define ck_unlockmutex_t CK_UNLOCKMUTEX #define ck_c_initialize_args _CK_C_INITIALIZE_ARGS #define create_mutex CreateMutex #define destroy_mutex DestroyMutex #define lock_mutex LockMutex #define unlock_mutex UnlockMutex #define reserved pReserved #define ck_rsa_pkcs_mgf_type_t CK_RSA_PKCS_MGF_TYPE #define ck_rsa_pkcs_oaep_source_type_t CK_RSA_PKCS_OAEP_SOURCE_TYPE #define hash_alg hashAlg #define s_len sLen #define source_data pSourceData #define source_data_len ulSourceDataLen #define counter_bits ulCounterBits #define iv_ptr pIv #define iv_len ulIvLen #define iv_bits ulIvBits #define aad_ptr pAAD #define aad_len ulAADLen #define tag_bits ulTagBits #define shared_data_len ulSharedDataLen #define shared_data pSharedData #define public_data_len ulPublicDataLen #define public_data pPublicData #define string_data pData #define string_data_len ulLen #define data_params pData #endif /* CRYPTOKI_COMPAT */ typedef unsigned long ck_flags_t; struct ck_version { unsigned char major; unsigned char minor; }; struct ck_info { struct ck_version cryptoki_version; unsigned char manufacturer_id[32]; ck_flags_t flags; unsigned char library_description[32]; struct ck_version library_version; }; typedef unsigned long ck_notification_t; const unsigned long CKN_SURRENDER = 0UL; typedef unsigned long ck_slot_id_t; struct ck_slot_info { unsigned char slot_description[64]; unsigned char manufacturer_id[32]; ck_flags_t flags; struct ck_version hardware_version; struct ck_version firmware_version; }; const unsigned long CKF_TOKEN_PRESENT = 1UL << 0; const unsigned long CKF_REMOVABLE_DEVICE = 1UL << 1; const unsigned long CKF_HW_SLOT = 1UL << 2; const unsigned long CKF_ARRAY_ATTRIBUTE = 1UL << 30; struct ck_token_info { unsigned char label[32]; unsigned char manufacturer_id[32]; unsigned char model[16]; unsigned char serial_number[16]; ck_flags_t flags; unsigned long max_session_count; unsigned long session_count; unsigned long max_rw_session_count; unsigned long rw_session_count; unsigned long max_pin_len; unsigned long min_pin_len; unsigned long total_public_memory; unsigned long free_public_memory; unsigned long total_private_memory; unsigned long free_private_memory; struct ck_version hardware_version; struct ck_version firmware_version; unsigned char utc_time[16]; }; #define CKF_RNG (1UL << 0) #define CKF_WRITE_PROTECTED (1UL << 1) #define CKF_LOGIN_REQUIRED (1UL << 2) #define CKF_USER_PIN_INITIALIZED (1UL << 3) #define CKF_RESTORE_KEY_NOT_NEEDED (1UL << 5) #define CKF_CLOCK_ON_TOKEN (1UL << 6) #define CKF_PROTECTED_AUTHENTICATION_PATH (1UL << 8) #define CKF_DUAL_CRYPTO_OPERATIONS (1UL << 9) #define CKF_TOKEN_INITIALIZED (1UL << 10) #define CKF_SECONDARY_AUTHENTICATION (1UL << 11) #define CKF_USER_PIN_COUNT_LOW (1UL << 16) #define CKF_USER_PIN_FINAL_TRY (1UL << 17) #define CKF_USER_PIN_LOCKED (1UL << 18) #define CKF_USER_PIN_TO_BE_CHANGED (1UL << 19) #define CKF_SO_PIN_COUNT_LOW (1UL << 20) #define CKF_SO_PIN_FINAL_TRY (1UL << 21) #define CKF_SO_PIN_LOCKED (1UL << 22) #define CKF_SO_PIN_TO_BE_CHANGED (1UL << 23) #define CKF_ERROR_STATE (1UL << 24) #define CK_UNAVAILABLE_INFORMATION ((unsigned long)-1L) #define CK_EFFECTIVELY_INFINITE (0UL) typedef unsigned long ck_session_handle_t; #define CK_INVALID_HANDLE (0UL) typedef unsigned long ck_user_type_t; #define CKU_SO (0UL) #define CKU_USER (1UL) #define CKU_CONTEXT_SPECIFIC (2UL) typedef unsigned long ck_state_t; #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) struct ck_session_info { ck_slot_id_t slot_id; ck_state_t state; ck_flags_t flags; unsigned long device_error; }; #define CKF_RW_SESSION (1UL << 1) #define CKF_SERIAL_SESSION (1UL << 2) typedef unsigned long ck_object_handle_t; typedef unsigned long ck_object_class_t; #define CKO_DATA (0UL) #define CKO_CERTIFICATE (1UL) #define CKO_PUBLIC_KEY (2UL) #define CKO_PRIVATE_KEY (3UL) #define CKO_SECRET_KEY (4UL) #define CKO_HW_FEATURE (5UL) #define CKO_DOMAIN_PARAMETERS (6UL) #define CKO_MECHANISM (7UL) #define CKO_OTP_KEY (8UL) #define CKO_VENDOR_DEFINED ((unsigned long) (1UL << 31)) typedef unsigned long ck_hw_feature_type_t; #define CKH_MONOTONIC_COUNTER (1UL) #define CKH_CLOCK (2UL) #define CKH_USER_INTERFACE (3UL) #define CKH_VENDOR_DEFINED ((unsigned long) (1UL << 31)) typedef unsigned long ck_key_type_t; #define CKK_RSA (0UL) #define CKK_DSA (1UL) #define CKK_DH (2UL) #define CKK_ECDSA (3UL) #define CKK_EC (3UL) #define CKK_X9_42_DH (4UL) #define CKK_KEA (5UL) #define CKK_GENERIC_SECRET (0x10UL) #define CKK_RC2 (0x11UL) #define CKK_RC4 (0x12UL) #define CKK_DES (0x13UL) #define CKK_DES2 (0x14UL) #define CKK_DES3 (0x15UL) #define CKK_CAST (0x16UL) #define CKK_CAST3 (0x17UL) #define CKK_CAST128 (0x18UL) #define CKK_RC5 (0x19UL) #define CKK_IDEA (0x1aUL) #define CKK_SKIPJACK (0x1bUL) #define CKK_BATON (0x1cUL) #define CKK_JUNIPER (0x1dUL) #define CKK_CDMF (0x1eUL) #define CKK_AES (0x1fUL) #define CKK_BLOWFISH (0x20UL) #define CKK_TWOFISH (0x21UL) #define CKK_SECURID (0x22UL) #define CKK_HOTP (0x23UL) #define CKK_ACTI (0x24UL) #define CKK_CAMELLIA (0x25UL) #define CKK_ARIA (0x26UL) #define CKK_MD5_HMAC (0x27UL) #define CKK_SHA_1_HMAC (0x28UL) #define CKK_RIPEMD128_HMAC (0x29UL) #define CKK_RIPEMD160_HMAC (0x2aUL) #define CKK_SHA256_HMAC (0x2bUL) #define CKK_SHA384_HMAC (0x2cUL) #define CKK_SHA512_HMAC (0x2dUL) #define CKK_SHA224_HMAC (0x2eUL) #define CKK_SEED (0x2fUL) #define CKK_GOSTR3410 (0x30UL) #define CKK_GOSTR3411 (0x31UL) #define CKK_GOST28147 (0x32UL) #define CKK_EC_EDWARDS (0x40UL) #define CKK_VENDOR_DEFINED ((unsigned long) (1UL << 31)) typedef unsigned long ck_certificate_type_t; #define CKC_X_509 (0UL) #define CKC_X_509_ATTR_CERT (1UL) #define CKC_WTLS (2UL) #define CKC_VENDOR_DEFINED ((unsigned long) (1UL << 31)) #define CKC_OPENPGP (CKC_VENDOR_DEFINED|0x504750UL) typedef unsigned long ck_attribute_type_t; #define CKA_CLASS (0UL) #define CKA_TOKEN (1UL) #define CKA_PRIVATE (2UL) #define CKA_LABEL (3UL) #define CKA_APPLICATION (0x10UL) #define CKA_VALUE (0x11UL) #define CKA_OBJECT_ID (0x12UL) #define CKA_CERTIFICATE_TYPE (0x80UL) #define CKA_ISSUER (0x81UL) #define CKA_SERIAL_NUMBER (0x82UL) #define CKA_AC_ISSUER (0x83UL) #define CKA_OWNER (0x84UL) #define CKA_ATTR_TYPES (0x85UL) #define CKA_TRUSTED (0x86UL) #define CKA_CERTIFICATE_CATEGORY (0x87UL) #define CKA_JAVA_MIDP_SECURITY_DOMAIN (0x88UL) #define CKA_URL (0x89UL) #define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8aUL) #define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8bUL) #define CKA_NAME_HASH_ALGORITHM (0x8cUL) #define CKA_CHECK_VALUE (0x90UL) #define CKA_KEY_TYPE (0x100UL) #define CKA_SUBJECT (0x101UL) #define CKA_ID (0x102UL) #define CKA_SENSITIVE (0x103UL) #define CKA_ENCRYPT (0x104UL) #define CKA_DECRYPT (0x105UL) #define CKA_WRAP (0x106UL) #define CKA_UNWRAP (0x107UL) #define CKA_SIGN (0x108UL) #define CKA_SIGN_RECOVER (0x109UL) #define CKA_VERIFY (0x10aUL) #define CKA_VERIFY_RECOVER (0x10bUL) #define CKA_DERIVE (0x10cUL) #define CKA_START_DATE (0x110UL) #define CKA_END_DATE (0x111UL) #define CKA_MODULUS (0x120UL) #define CKA_MODULUS_BITS (0x121UL) #define CKA_PUBLIC_EXPONENT (0x122UL) #define CKA_PRIVATE_EXPONENT (0x123UL) #define CKA_PRIME_1 (0x124UL) #define CKA_PRIME_2 (0x125UL) #define CKA_EXPONENT_1 (0x126UL) #define CKA_EXPONENT_2 (0x127UL) #define CKA_COEFFICIENT (0x128UL) #define CKA_PUBLIC_KEY_INFO (0x129UL) #define CKA_PRIME (0x130UL) #define CKA_SUBPRIME (0x131UL) #define CKA_BASE (0x132UL) #define CKA_PRIME_BITS (0x133UL) #define CKA_SUB_PRIME_BITS (0x134UL) #define CKA_VALUE_BITS (0x160UL) #define CKA_VALUE_LEN (0x161UL) #define CKA_EXTRACTABLE (0x162UL) #define CKA_LOCAL (0x163UL) #define CKA_NEVER_EXTRACTABLE (0x164UL) #define CKA_ALWAYS_SENSITIVE (0x165UL) #define CKA_KEY_GEN_MECHANISM (0x166UL) #define CKA_MODIFIABLE (0x170UL) #define CKA_COPYABLE (0x171UL) #define CKA_DESTROYABLE (0x172UL) #define CKA_ECDSA_PARAMS (0x180UL) #define CKA_EC_PARAMS (0x180UL) #define CKA_EC_POINT (0x181UL) #define CKA_SECONDARY_AUTH (0x200UL) #define CKA_AUTH_PIN_FLAGS (0x201UL) #define CKA_ALWAYS_AUTHENTICATE (0x202UL) #define CKA_WRAP_WITH_TRUSTED (0x210UL) #define CKA_OTP_FORMAT (0x220UL) #define CKA_OTP_LENGTH (0x221UL) #define CKA_OTP_TIME_INTERVAL (0x222UL) #define CKA_OTP_USER_FRIENDLY_MODE (0x223UL) #define CKA_OTP_CHALLENGE_REQUIREMENT (0x224UL) #define CKA_OTP_TIME_REQUIREMENT (0x225UL) #define CKA_OTP_COUNTER_REQUIREMENT (0x226UL) #define CKA_OTP_PIN_REQUIREMENT (0x227UL) #define CKA_OTP_USER_IDENTIFIER (0x22AUL) #define CKA_OTP_SERVICE_IDENTIFIER (0x22BUL) #define CKA_OTP_SERVICE_LOGO (0x22CUL) #define CKA_OTP_SERVICE_LOGO_TYPE (0x22DUL) #define CKA_OTP_COUNTER (0x22EUL) #define CKA_OTP_TIME (0x22FUL) #define CKA_GOSTR3410_PARAMS (0x250UL) #define CKA_GOSTR3411_PARAMS (0x251UL) #define CKA_GOST28147_PARAMS (0x252UL) #define CKA_HW_FEATURE_TYPE (0x300UL) #define CKA_RESET_ON_INIT (0x301UL) #define CKA_HAS_RESET (0x302UL) #define CKA_PIXEL_X (0x400UL) #define CKA_PIXEL_Y (0x401UL) #define CKA_RESOLUTION (0x402UL) #define CKA_CHAR_ROWS (0x403UL) #define CKA_CHAR_COLUMNS (0x404UL) #define CKA_COLOR (0x405UL) #define CKA_BITS_PER_PIXEL (0x406UL) #define CKA_CHAR_SETS (0x480UL) #define CKA_ENCODING_METHODS (0x481UL) #define CKA_MIME_TYPES (0x482UL) #define CKA_MECHANISM_TYPE (0x500UL) #define CKA_REQUIRED_CMS_ATTRIBUTES (0x501UL) #define CKA_DEFAULT_CMS_ATTRIBUTES (0x502UL) #define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503UL) #define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211UL) #define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212UL) #define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x213UL) #define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL) #define CKA_VENDOR_DEFINED ((unsigned long) (1UL << 31)) struct ck_attribute { ck_attribute_type_t type; void *value; unsigned long value_len; }; struct ck_date { unsigned char year[4]; unsigned char month[2]; unsigned char day[2]; }; typedef unsigned long ck_mechanism_type_t; #define CKM_RSA_PKCS_KEY_PAIR_GEN (0UL) #define CKM_RSA_PKCS (1UL) #define CKM_RSA_9796 (2UL) #define CKM_RSA_X_509 (3UL) #define CKM_MD2_RSA_PKCS (4UL) #define CKM_MD5_RSA_PKCS (5UL) #define CKM_SHA1_RSA_PKCS (6UL) #define CKM_RIPEMD128_RSA_PKCS (7UL) #define CKM_RIPEMD160_RSA_PKCS (8UL) #define CKM_RSA_PKCS_OAEP (9UL) #define CKM_RSA_X9_31_KEY_PAIR_GEN (0xaUL) #define CKM_RSA_X9_31 (0xbUL) #define CKM_SHA1_RSA_X9_31 (0xcUL) #define CKM_RSA_PKCS_PSS (0xdUL) #define CKM_SHA1_RSA_PKCS_PSS (0xeUL) #define CKM_DSA_KEY_PAIR_GEN (0x10UL) #define CKM_DSA (0x11UL) #define CKM_DSA_SHA1 (0x12UL) #define CKM_DSA_SHA224 (0x13UL) #define CKM_DSA_SHA256 (0x14UL) #define CKM_DSA_SHA384 (0x15UL) #define CKM_DSA_SHA512 (0x16UL) #define CKM_DH_PKCS_KEY_PAIR_GEN (0x20UL) #define CKM_DH_PKCS_DERIVE (0x21UL) #define CKM_X9_42_DH_KEY_PAIR_GEN (0x30UL) #define CKM_X9_42_DH_DERIVE (0x31UL) #define CKM_X9_42_DH_HYBRID_DERIVE (0x32UL) #define CKM_X9_42_MQV_DERIVE (0x33UL) #define CKM_SHA256_RSA_PKCS (0x40UL) #define CKM_SHA384_RSA_PKCS (0x41UL) #define CKM_SHA512_RSA_PKCS (0x42UL) #define CKM_SHA256_RSA_PKCS_PSS (0x43UL) #define CKM_SHA384_RSA_PKCS_PSS (0x44UL) #define CKM_SHA512_RSA_PKCS_PSS (0x45UL) #define CKM_SHA512_224 (0x48UL) #define CKM_SHA512_224_HMAC (0x49UL) #define CKM_SHA512_224_HMAC_GENERAL (0x4aUL) #define CKM_SHA512_224_KEY_DERIVATION (0x4bUL) #define CKM_SHA512_256 (0x4cUL) #define CKM_SHA512_256_HMAC (0x4dUL) #define CKM_SHA512_256_HMAC_GENERAL (0x4eUL) #define CKM_SHA512_256_KEY_DERIVATION (0x4fUL) #define CKM_SHA512_T (0x50UL) #define CKM_SHA512_T_HMAC (0x51UL) #define CKM_SHA512_T_HMAC_GENERAL (0x52UL) #define CKM_SHA512_T_KEY_DERIVATION (0x53UL) #define CKM_RC2_KEY_GEN (0x100UL) #define CKM_RC2_ECB (0x101UL) #define CKM_RC2_CBC (0x102UL) #define CKM_RC2_MAC (0x103UL) #define CKM_RC2_MAC_GENERAL (0x104UL) #define CKM_RC2_CBC_PAD (0x105UL) #define CKM_RC4_KEY_GEN (0x110UL) #define CKM_RC4 (0x111UL) #define CKM_DES_KEY_GEN (0x120UL) #define CKM_DES_ECB (0x121UL) #define CKM_DES_CBC (0x122UL) #define CKM_DES_MAC (0x123UL) #define CKM_DES_MAC_GENERAL (0x124UL) #define CKM_DES_CBC_PAD (0x125UL) #define CKM_DES2_KEY_GEN (0x130UL) #define CKM_DES3_KEY_GEN (0x131UL) #define CKM_DES3_ECB (0x132UL) #define CKM_DES3_CBC (0x133UL) #define CKM_DES3_MAC (0x134UL) #define CKM_DES3_MAC_GENERAL (0x135UL) #define CKM_DES3_CBC_PAD (0x136UL) #define CKM_DES3_CMAC_GENERAL (0x137UL) #define CKM_DES3_CMAC (0x138UL) #define CKM_CDMF_KEY_GEN (0x140UL) #define CKM_CDMF_ECB (0x141UL) #define CKM_CDMF_CBC (0x142UL) #define CKM_CDMF_MAC (0x143UL) #define CKM_CDMF_MAC_GENERAL (0x144UL) #define CKM_CDMF_CBC_PAD (0x145UL) #define CKM_DES_OFB64 (0x150UL) #define CKM_DES_OFB8 (0x151UL) #define CKM_DES_CFB64 (0x152UL) #define CKM_DES_CFB8 (0x153UL) #define CKM_MD2 (0x200UL) #define CKM_MD2_HMAC (0x201UL) #define CKM_MD2_HMAC_GENERAL (0x202UL) #define CKM_MD5 (0x210UL) #define CKM_MD5_HMAC (0x211UL) #define CKM_MD5_HMAC_GENERAL (0x212UL) #define CKM_SHA_1 (0x220UL) #define CKM_SHA_1_HMAC (0x221UL) #define CKM_SHA_1_HMAC_GENERAL (0x222UL) #define CKM_RIPEMD128 (0x230UL) #define CKM_RIPEMD128_HMAC (0x231UL) #define CKM_RIPEMD128_HMAC_GENERAL (0x232UL) #define CKM_RIPEMD160 (0x240UL) #define CKM_RIPEMD160_HMAC (0x241UL) #define CKM_RIPEMD160_HMAC_GENERAL (0x242UL) #define CKM_SHA256 (0x250UL) #define CKM_SHA256_HMAC (0x251UL) #define CKM_SHA256_HMAC_GENERAL (0x252UL) #define CKM_SHA384 (0x260UL) #define CKM_SHA384_HMAC (0x261UL) #define CKM_SHA384_HMAC_GENERAL (0x262UL) #define CKM_SHA512 (0x270UL) #define CKM_SHA512_HMAC (0x271UL) #define CKM_SHA512_HMAC_GENERAL (0x272UL) #define CKM_SECURID_KEY_GEN (0x280UL) #define CKM_SECURID (0x282UL) #define CKM_HOTP_KEY_GEN (0x290UL) #define CKM_HOTP (0x291UL) #define CKM_ACTI (0x2a0UL) #define CKM_ACTI_KEY_GEN (0x2a1UL) #define CKM_CAST_KEY_GEN (0x300UL) #define CKM_CAST_ECB (0x301UL) #define CKM_CAST_CBC (0x302UL) #define CKM_CAST_MAC (0x303UL) #define CKM_CAST_MAC_GENERAL (0x304UL) #define CKM_CAST_CBC_PAD (0x305UL) #define CKM_CAST3_KEY_GEN (0x310UL) #define CKM_CAST3_ECB (0x311UL) #define CKM_CAST3_CBC (0x312UL) #define CKM_CAST3_MAC (0x313UL) #define CKM_CAST3_MAC_GENERAL (0x314UL) #define CKM_CAST3_CBC_PAD (0x315UL) #define CKM_CAST5_KEY_GEN (0x320UL) #define CKM_CAST128_KEY_GEN (0x320UL) #define CKM_CAST5_ECB (0x321UL) #define CKM_CAST128_ECB (0x321UL) #define CKM_CAST5_CBC (0x322UL) #define CKM_CAST128_CBC (0x322UL) #define CKM_CAST5_MAC (0x323UL) #define CKM_CAST128_MAC (0x323UL) #define CKM_CAST5_MAC_GENERAL (0x324UL) #define CKM_CAST128_MAC_GENERAL (0x324UL) #define CKM_CAST5_CBC_PAD (0x325UL) #define CKM_CAST128_CBC_PAD (0x325UL) #define CKM_RC5_KEY_GEN (0x330UL) #define CKM_RC5_ECB (0x331UL) #define CKM_RC5_CBC (0x332UL) #define CKM_RC5_MAC (0x333UL) #define CKM_RC5_MAC_GENERAL (0x334UL) #define CKM_RC5_CBC_PAD (0x335UL) #define CKM_IDEA_KEY_GEN (0x340UL) #define CKM_IDEA_ECB (0x341UL) #define CKM_IDEA_CBC (0x342UL) #define CKM_IDEA_MAC (0x343UL) #define CKM_IDEA_MAC_GENERAL (0x344UL) #define CKM_IDEA_CBC_PAD (0x345UL) #define CKM_GENERIC_SECRET_KEY_GEN (0x350UL) #define CKM_CONCATENATE_BASE_AND_KEY (0x360UL) #define CKM_CONCATENATE_BASE_AND_DATA (0x362UL) #define CKM_CONCATENATE_DATA_AND_BASE (0x363UL) #define CKM_XOR_BASE_AND_DATA (0x364UL) #define CKM_EXTRACT_KEY_FROM_KEY (0x365UL) #define CKM_SSL3_PRE_MASTER_KEY_GEN (0x370UL) #define CKM_SSL3_MASTER_KEY_DERIVE (0x371UL) #define CKM_SSL3_KEY_AND_MAC_DERIVE (0x372UL) #define CKM_SSL3_MASTER_KEY_DERIVE_DH (0x373UL) #define CKM_TLS_PRE_MASTER_KEY_GEN (0x374UL) #define CKM_TLS_MASTER_KEY_DERIVE (0x375UL) #define CKM_TLS_KEY_AND_MAC_DERIVE (0x376UL) #define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377UL) #define CKM_TLS_PRF (0x378UL) #define CKM_SSL3_MD5_MAC (0x380UL) #define CKM_SSL3_SHA1_MAC (0x381UL) #define CKM_MD5_KEY_DERIVATION (0x390UL) #define CKM_MD2_KEY_DERIVATION (0x391UL) #define CKM_SHA1_KEY_DERIVATION (0x392UL) #define CKM_SHA256_KEY_DERIVATION (0x393UL) #define CKM_SHA384_KEY_DERIVATION (0x394UL) #define CKM_SHA512_KEY_DERIVATION (0x395UL) #define CKM_PBE_MD2_DES_CBC (0x3a0UL) #define CKM_PBE_MD5_DES_CBC (0x3a1UL) #define CKM_PBE_MD5_CAST_CBC (0x3a2UL) #define CKM_PBE_MD5_CAST3_CBC (0x3a3UL) #define CKM_PBE_MD5_CAST5_CBC (0x3a4UL) #define CKM_PBE_MD5_CAST128_CBC (0x3a4UL) #define CKM_PBE_SHA1_CAST5_CBC (0x3a5UL) #define CKM_PBE_SHA1_CAST128_CBC (0x3a5UL) #define CKM_PBE_SHA1_RC4_128 (0x3a6UL) #define CKM_PBE_SHA1_RC4_40 (0x3a7UL) #define CKM_PBE_SHA1_DES3_EDE_CBC (0x3a8UL) #define CKM_PBE_SHA1_DES2_EDE_CBC (0x3a9UL) #define CKM_PBE_SHA1_RC2_128_CBC (0x3aaUL) #define CKM_PBE_SHA1_RC2_40_CBC (0x3abUL) #define CKM_PKCS5_PBKD2 (0x3b0UL) #define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0UL) #define CKM_WTLS_PRE_MASTER_KEY_GEN (0x3d0UL) #define CKM_WTLS_MASTER_KEY_DERIVE (0x3d1UL) #define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC (0x3d2UL) #define CKM_WTLS_PRF (0x3d3UL) #define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE (0x3d4UL) #define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE (0x3d5UL) #define CKM_TLS10_MAC_SERVER (0x3d6UL) #define CKM_TLS10_MAC_CLIENT (0x3d7UL) #define CKM_TLS12_MAC (0x3d8UL) #define CKM_TLS12_KDF (0x3d9UL) #define CKM_TLS12_MASTER_KEY_DERIVE (0x3e0UL) #define CKM_TLS12_KEY_AND_MAC_DERIVE (0x3e1UL) #define CKM_TLS12_MASTER_KEY_DERIVE_DH (0x3e2UL) #define CKM_TLS12_KEY_SAFE_DERIVE (0x3e3UL) #define CKM_TLS_MAC (0x3e4UL) #define CKM_TLS_KDF (0x3e5UL) #define CKM_KEY_WRAP_LYNKS (0x400UL) #define CKM_KEY_WRAP_SET_OAEP (0x401UL) #define CKM_CMS_SIG (0x500UL) #define CKM_KIP_DERIVE (0x510UL) #define CKM_KIP_WRAP (0x511UL) #define CKM_KIP_MAC (0x512UL) #define CKM_CAMELLIA_KEY_GEN (0x550UL) #define CKM_CAMELLIA_CTR (0x558UL) #define CKM_ARIA_KEY_GEN (0x560UL) #define CKM_ARIA_ECB (0x561UL) #define CKM_ARIA_CBC (0x562UL) #define CKM_ARIA_MAC (0x563UL) #define CKM_ARIA_MAC_GENERAL (0x564UL) #define CKM_ARIA_CBC_PAD (0x565UL) #define CKM_ARIA_ECB_ENCRYPT_DATA (0x566UL) #define CKM_ARIA_CBC_ENCRYPT_DATA (0x567UL) #define CKM_SEED_KEY_GEN (0x650UL) #define CKM_SEED_ECB (0x651UL) #define CKM_SEED_CBC (0x652UL) #define CKM_SEED_MAC (0x653UL) #define CKM_SEED_MAC_GENERAL (0x654UL) #define CKM_SEED_CBC_PAD (0x655UL) #define CKM_SEED_ECB_ENCRYPT_DATA (0x656UL) #define CKM_SEED_CBC_ENCRYPT_DATA (0x657UL) #define CKM_SKIPJACK_KEY_GEN (0x1000UL) #define CKM_SKIPJACK_ECB64 (0x1001UL) #define CKM_SKIPJACK_CBC64 (0x1002UL) #define CKM_SKIPJACK_OFB64 (0x1003UL) #define CKM_SKIPJACK_CFB64 (0x1004UL) #define CKM_SKIPJACK_CFB32 (0x1005UL) #define CKM_SKIPJACK_CFB16 (0x1006UL) #define CKM_SKIPJACK_CFB8 (0x1007UL) #define CKM_SKIPJACK_WRAP (0x1008UL) #define CKM_SKIPJACK_PRIVATE_WRAP (0x1009UL) #define CKM_SKIPJACK_RELAYX (0x100aUL) #define CKM_KEA_KEY_PAIR_GEN (0x1010UL) #define CKM_KEA_KEY_DERIVE (0x1011UL) #define CKM_FORTEZZA_TIMESTAMP (0x1020UL) #define CKM_BATON_KEY_GEN (0x1030UL) #define CKM_BATON_ECB128 (0x1031UL) #define CKM_BATON_ECB96 (0x1032UL) #define CKM_BATON_CBC128 (0x1033UL) #define CKM_BATON_COUNTER (0x1034UL) #define CKM_BATON_SHUFFLE (0x1035UL) #define CKM_BATON_WRAP (0x1036UL) #define CKM_ECDSA_KEY_PAIR_GEN (0x1040UL) #define CKM_EC_KEY_PAIR_GEN (0x1040UL) #define CKM_ECDSA (0x1041UL) #define CKM_ECDSA_SHA1 (0x1042UL) #define CKM_ECDSA_SHA224 (0x1043UL) #define CKM_ECDSA_SHA256 (0x1044UL) #define CKM_ECDSA_SHA384 (0x1045UL) #define CKM_ECDSA_SHA512 (0x1046UL) #define CKM_ECDH1_DERIVE (0x1050UL) #define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) #define CKM_ECMQV_DERIVE (0x1052UL) #define CKM_ECDH_AES_KEY_WRAP (0x1053UL) #define CKM_RSA_AES_KEY_WRAP (0x1054UL) #define CKM_JUNIPER_KEY_GEN (0x1060UL) #define CKM_JUNIPER_ECB128 (0x1061UL) #define CKM_JUNIPER_CBC128 (0x1062UL) #define CKM_JUNIPER_COUNTER (0x1063UL) #define CKM_JUNIPER_SHUFFLE (0x1064UL) #define CKM_JUNIPER_WRAP (0x1065UL) #define CKM_FASTHASH (0x1070UL) #define CKM_AES_KEY_GEN (0x1080UL) #define CKM_AES_ECB (0x1081UL) #define CKM_AES_CBC (0x1082UL) #define CKM_AES_MAC (0x1083UL) #define CKM_AES_MAC_GENERAL (0x1084UL) #define CKM_AES_CBC_PAD (0x1085UL) #define CKM_AES_CTR (0x1086UL) #define CKM_AES_GCM (0x1087UL) #define CKM_AES_CCM (0x1088UL) #define CKM_AES_CTS (0x1089UL) #define CKM_AES_CMAC (0x108aUL) #define CKM_AES_CMAC_GENERAL (0x108bUL) #define CKM_AES_XCBC_MAC (0x108cUL) #define CKM_AES_XCBC_MAC_96 (0x108dUL) #define CKM_AES_GMAC (0x108eUL) #define CKM_BLOWFISH_KEY_GEN (0x1090UL) #define CKM_BLOWFISH_CBC (0x1091UL) #define CKM_TWOFISH_KEY_GEN (0x1092UL) #define CKM_TWOFISH_CBC (0x1093UL) #define CKM_BLOWFISH_CBC_PAD (0x1094UL) #define CKM_TWOFISH_CBC_PAD (0x1095UL) #define CKM_DES_ECB_ENCRYPT_DATA (0x1100UL) #define CKM_DES_CBC_ENCRYPT_DATA (0x1101UL) #define CKM_DES3_ECB_ENCRYPT_DATA (0x1102UL) #define CKM_DES3_CBC_ENCRYPT_DATA (0x1103UL) #define CKM_AES_ECB_ENCRYPT_DATA (0x1104UL) #define CKM_AES_CBC_ENCRYPT_DATA (0x1105UL) #define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL) #define CKM_GOSTR3410 (0x1201UL) #define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL) #define CKM_GOSTR3410_KEY_WRAP (0x1203UL) #define CKM_GOSTR3410_DERIVE (0x1204UL) #define CKM_GOSTR3411 (0x1210UL) #define CKM_GOSTR3411_HMAC (0x1211UL) #define CKM_GOST28147_KEY_GEN (0x1220UL) #define CKM_GOST28147_ECB (0x1221UL) #define CKM_GOST28147 (0x1222UL) #define CKM_GOST28147_MAC (0x1223UL) #define CKM_GOST28147_KEY_WRAP (0x1224UL) #define CKM_DSA_PARAMETER_GEN (0x2000UL) #define CKM_DH_PKCS_PARAMETER_GEN (0x2001UL) #define CKM_X9_42_DH_PARAMETER_GEN (0x2002UL) #define CKM_DSA_PROBABLISTIC_PARAMETER_GEN (0x2003UL) #define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN (0x2004UL) #define CKM_AES_OFB (0x2104UL) #define CKM_AES_CFB64 (0x2105UL) #define CKM_AES_CFB8 (0x2106UL) #define CKM_AES_CFB128 (0x2107UL) #define CKM_AES_CFB1 (0x2108UL) #define CKM_VENDOR_DEFINED ((unsigned long) (1UL << 31)) /* Amendments */ #define CKM_SHA224 (0x255UL) #define CKM_SHA224_HMAC (0x256UL) #define CKM_SHA224_HMAC_GENERAL (0x257UL) #define CKM_SHA224_RSA_PKCS (0x46UL) #define CKM_SHA224_RSA_PKCS_PSS (0x47UL) #define CKM_SHA224_KEY_DERIVATION (0x396UL) #define CKM_CAMELLIA_KEY_GEN (0x550UL) #define CKM_CAMELLIA_ECB (0x551UL) #define CKM_CAMELLIA_CBC (0x552UL) #define CKM_CAMELLIA_MAC (0x553UL) #define CKM_CAMELLIA_MAC_GENERAL (0x554UL) #define CKM_CAMELLIA_CBC_PAD (0x555UL) #define CKM_CAMELLIA_ECB_ENCRYPT_DATA (0x556UL) #define CKM_CAMELLIA_CBC_ENCRYPT_DATA (0x557UL) #define CKM_AES_KEY_WRAP (0x2109UL) #define CKM_AES_KEY_WRAP_PAD (0x210aUL) #define CKM_RSA_PKCS_TPM_1_1 (0x4001UL) #define CKM_RSA_PKCS_OAEP_TPM_1_1 (0x4002UL) /* From version 3.0 */ #define CKM_EC_EDWARDS_KEY_PAIR_GEN (0x1055UL) #define CKM_EC_MONTGOMERY_KEY_PAIR_GEN (0x1056UL) #define CKM_EDDSA (0x1057UL) /* Attribute and other constants related to OTP */ #define CK_OTP_FORMAT_DECIMAL (0UL) #define CK_OTP_FORMAT_HEXADECIMAL (1UL) #define CK_OTP_FORMAT_ALPHANUMERIC (2UL) #define CK_OTP_FORMAT_BINARY (3UL) #define CK_OTP_PARAM_IGNORED (0UL) #define CK_OTP_PARAM_OPTIONAL (1UL) #define CK_OTP_PARAM_MANDATORY (2UL) #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_FORMAT (7UL) /* OTP mechanism flags */ #define CKF_NEXT_OTP (0x01UL) #define CKF_EXCLUDE_TIME (0x02UL) #define CKF_EXCLUDE_COUNTER (0x04UL) #define CKF_EXCLUDE_CHALLENGE (0x08UL) #define CKF_EXCLUDE_PIN (0x10UL) #define CKF_USER_FRIENDLY_OTP (0x20UL) #define CKN_OTP_CHANGED (0x01UL) struct ck_mechanism { ck_mechanism_type_t mechanism; void *parameter; unsigned long parameter_len; }; struct ck_mechanism_info { unsigned long min_key_size; unsigned long max_key_size; ck_flags_t flags; }; typedef unsigned long ck_param_type; typedef struct ck_otp_param { ck_param_type type; void *value; unsigned long value_len; } ck_otp_param; typedef struct ck_otp_params { struct ck_otp_param *params; unsigned long count; } ck_otp_params; typedef struct ck_otp_signature_info { struct ck_otp_param *params; unsigned long count; } ck_otp_signature_info; #define CKG_MGF1_SHA1 0x00000001UL #define CKG_MGF1_SHA224 0x00000005UL #define CKG_MGF1_SHA256 0x00000002UL #define CKG_MGF1_SHA384 0x00000003UL #define CKG_MGF1_SHA512 0x00000004UL typedef unsigned long ck_rsa_pkcs_mgf_type_t; struct ck_rsa_pkcs_pss_params { ck_mechanism_type_t hash_alg; ck_rsa_pkcs_mgf_type_t mgf; unsigned long s_len; }; typedef unsigned long ck_rsa_pkcs_oaep_source_type_t; struct ck_rsa_pkcs_oaep_params { ck_mechanism_type_t hash_alg; ck_rsa_pkcs_mgf_type_t mgf; ck_rsa_pkcs_oaep_source_type_t source; void *source_data; unsigned long source_data_len; }; struct ck_aes_ctr_params { unsigned long counter_bits; unsigned char cb[16]; }; struct ck_gcm_params { unsigned char *iv_ptr; unsigned long iv_len; unsigned long iv_bits; unsigned char *aad_ptr; unsigned long aad_len; unsigned long tag_bits; }; /* The following EC Key Derivation Functions are defined */ #define CKD_NULL (0x01UL) #define CKD_SHA1_KDF (0x02UL) /* The following X9.42 DH key derivation functions are defined */ #define CKD_SHA1_KDF_ASN1 (0x03UL) #define CKD_SHA1_KDF_CONCATENATE (0x04UL) #define CKD_SHA224_KDF (0x05UL) #define CKD_SHA256_KDF (0x06UL) #define CKD_SHA384_KDF (0x07UL) #define CKD_SHA512_KDF (0x08UL) #define CKD_CPDIVERSIFY_KDF (0x09UL) typedef unsigned long ck_ec_kdf_t; struct ck_ecdh1_derive_params { ck_ec_kdf_t kdf; unsigned long shared_data_len; unsigned char *shared_data; unsigned long public_data_len; unsigned char *public_data; }; struct ck_key_derivation_string_data { unsigned char *string_data; unsigned long string_data_len; }; struct ck_des_cbc_encrypt_data_params { unsigned char iv[8]; unsigned char *data_params; unsigned long length; }; struct ck_aes_cbc_encrypt_data_params { unsigned char iv[16]; unsigned char *data_params; unsigned long length; }; #define CKF_HW (1UL << 0) #define CKF_ENCRYPT (1UL << 8) #define CKF_DECRYPT (1UL << 9) #define CKF_DIGEST (1UL << 10) #define CKF_SIGN (1UL << 11) #define CKF_SIGN_RECOVER (1UL << 12) #define CKF_VERIFY (1UL << 13) #define CKF_VERIFY_RECOVER (1UL << 14) #define CKF_GENERATE (1UL << 15) #define CKF_GENERATE_KEY_PAIR (1UL << 16) #define CKF_WRAP (1UL << 17) #define CKF_UNWRAP (1UL << 18) #define CKF_DERIVE (1UL << 19) #define CKF_EXTENSION ((unsigned long) (1UL << 31)) #define CKF_EC_F_P (1UL << 20) #define CKF_EC_F_2M (1UL << 21) #define CKF_EC_ECPARAMETERS (1UL << 22) #define CKF_EC_NAMEDCURVE (1UL << 23) #define CKF_EC_UNCOMPRESS (1UL << 24) #define CKF_EC_COMPRESS (1UL << 25) /* Flags for C_WaitForSlotEvent. */ #define CKF_DONT_BLOCK (1UL) typedef unsigned long ck_rv_t; typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, ck_notification_t event, void *application); /* Forward reference. */ struct ck_function_list; #define _CK_DECLARE_FUNCTION(name, args) \ typedef ck_rv_t (*CK_ ## name) args; \ ck_rv_t CK_SPEC name args _CK_DECLARE_FUNCTION (C_Initialize, (void *init_args)); _CK_DECLARE_FUNCTION (C_Finalize, (void *reserved)); _CK_DECLARE_FUNCTION (C_GetInfo, (struct ck_info *info)); _CK_DECLARE_FUNCTION (C_GetFunctionList, (struct ck_function_list **function_list)); _CK_DECLARE_FUNCTION (C_GetSlotList, (unsigned char token_present, ck_slot_id_t *slot_list, unsigned long *count)); _CK_DECLARE_FUNCTION (C_GetSlotInfo, (ck_slot_id_t slot_id, struct ck_slot_info *info)); _CK_DECLARE_FUNCTION (C_GetTokenInfo, (ck_slot_id_t slot_id, struct ck_token_info *info)); _CK_DECLARE_FUNCTION (C_WaitForSlotEvent, (ck_flags_t flags, ck_slot_id_t *slot, void *reserved)); _CK_DECLARE_FUNCTION (C_GetMechanismList, (ck_slot_id_t slot_id, ck_mechanism_type_t *mechanism_list, unsigned long *count)); _CK_DECLARE_FUNCTION (C_GetMechanismInfo, (ck_slot_id_t slot_id, ck_mechanism_type_t type, struct ck_mechanism_info *info)); _CK_DECLARE_FUNCTION (C_InitToken, (ck_slot_id_t slot_id, unsigned char *pin, unsigned long pin_len, unsigned char *label)); _CK_DECLARE_FUNCTION (C_InitPIN, (ck_session_handle_t session, unsigned char *pin, unsigned long pin_len)); _CK_DECLARE_FUNCTION (C_SetPIN, (ck_session_handle_t session, unsigned char *old_pin, unsigned long old_len, unsigned char *new_pin, unsigned long new_len)); _CK_DECLARE_FUNCTION (C_OpenSession, (ck_slot_id_t slot_id, ck_flags_t flags, void *application, ck_notify_t notify, ck_session_handle_t *session)); _CK_DECLARE_FUNCTION (C_CloseSession, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CloseAllSessions, (ck_slot_id_t slot_id)); _CK_DECLARE_FUNCTION (C_GetSessionInfo, (ck_session_handle_t session, struct ck_session_info *info)); _CK_DECLARE_FUNCTION (C_GetOperationState, (ck_session_handle_t session, unsigned char *operation_state, unsigned long *operation_state_len)); _CK_DECLARE_FUNCTION (C_SetOperationState, (ck_session_handle_t session, unsigned char *operation_state, unsigned long operation_state_len, ck_object_handle_t encryption_key, ck_object_handle_t authentiation_key)); _CK_DECLARE_FUNCTION (C_Login, (ck_session_handle_t session, ck_user_type_t user_type, unsigned char *pin, unsigned long pin_len)); _CK_DECLARE_FUNCTION (C_Logout, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CreateObject, (ck_session_handle_t session, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *object)); _CK_DECLARE_FUNCTION (C_CopyObject, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *new_object)); _CK_DECLARE_FUNCTION (C_DestroyObject, (ck_session_handle_t session, ck_object_handle_t object)); _CK_DECLARE_FUNCTION (C_GetObjectSize, (ck_session_handle_t session, ck_object_handle_t object, unsigned long *size)); _CK_DECLARE_FUNCTION (C_GetAttributeValue, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_SetAttributeValue, (ck_session_handle_t session, ck_object_handle_t object, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_FindObjectsInit, (ck_session_handle_t session, struct ck_attribute *templ, unsigned long count)); _CK_DECLARE_FUNCTION (C_FindObjects, (ck_session_handle_t session, ck_object_handle_t *object, unsigned long max_object_count, unsigned long *object_count)); _CK_DECLARE_FUNCTION (C_FindObjectsFinal, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_EncryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Encrypt, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *encrypted_data, unsigned long *encrypted_data_len)); _CK_DECLARE_FUNCTION (C_EncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_EncryptFinal, (ck_session_handle_t session, unsigned char *last_encrypted_part, unsigned long *last_encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Decrypt, (ck_session_handle_t session, unsigned char *encrypted_data, unsigned long encrypted_data_len, unsigned char *data, unsigned long *data_len)); _CK_DECLARE_FUNCTION (C_DecryptUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_DecryptFinal, (ck_session_handle_t session, unsigned char *last_part, unsigned long *last_part_len)); _CK_DECLARE_FUNCTION (C_DigestInit, (ck_session_handle_t session, struct ck_mechanism *mechanism)); _CK_DECLARE_FUNCTION (C_Digest, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *digest, unsigned long *digest_len)); _CK_DECLARE_FUNCTION (C_DigestUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_DigestKey, (ck_session_handle_t session, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_DigestFinal, (ck_session_handle_t session, unsigned char *digest, unsigned long *digest_len)); _CK_DECLARE_FUNCTION (C_SignInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Sign, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_SignUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_SignFinal, (ck_session_handle_t session, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_SignRecoverInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_SignRecover, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_VerifyInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_Verify, (ck_session_handle_t session, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_VerifyUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len)); _CK_DECLARE_FUNCTION (C_VerifyFinal, (ck_session_handle_t session, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_VerifyRecoverInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_VerifyRecover, (ck_session_handle_t session, unsigned char *signature, unsigned long signature_len, unsigned char *data, unsigned long *data_len)); _CK_DECLARE_FUNCTION (C_DigestEncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptDigestUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_SignEncryptUpdate, (ck_session_handle_t session, unsigned char *part, unsigned long part_len, unsigned char *encrypted_part, unsigned long *encrypted_part_len)); _CK_DECLARE_FUNCTION (C_DecryptVerifyUpdate, (ck_session_handle_t session, unsigned char *encrypted_part, unsigned long encrypted_part_len, unsigned char *part, unsigned long *part_len)); _CK_DECLARE_FUNCTION (C_GenerateKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, struct ck_attribute *templ, unsigned long count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_GenerateKeyPair, (ck_session_handle_t session, struct ck_mechanism *mechanism, struct ck_attribute *public_key_template, unsigned long public_key_attribute_count, struct ck_attribute *private_key_template, unsigned long private_key_attribute_count, ck_object_handle_t *public_key, ck_object_handle_t *private_key)); _CK_DECLARE_FUNCTION (C_WrapKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t wrapping_key, ck_object_handle_t key, unsigned char *wrapped_key, unsigned long *wrapped_key_len)); _CK_DECLARE_FUNCTION (C_UnwrapKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t unwrapping_key, unsigned char *wrapped_key, unsigned long wrapped_key_len, struct ck_attribute *templ, unsigned long attribute_count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_DeriveKey, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t base_key, struct ck_attribute *templ, unsigned long attribute_count, ck_object_handle_t *key)); _CK_DECLARE_FUNCTION (C_SeedRandom, (ck_session_handle_t session, unsigned char *seed, unsigned long seed_len)); _CK_DECLARE_FUNCTION (C_GenerateRandom, (ck_session_handle_t session, unsigned char *random_data, unsigned long random_len)); _CK_DECLARE_FUNCTION (C_GetFunctionStatus, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_CancelFunction, (ck_session_handle_t session)); struct ck_function_list { struct ck_version version; CK_C_Initialize C_Initialize; CK_C_Finalize C_Finalize; CK_C_GetInfo C_GetInfo; CK_C_GetFunctionList C_GetFunctionList; CK_C_GetSlotList C_GetSlotList; CK_C_GetSlotInfo C_GetSlotInfo; CK_C_GetTokenInfo C_GetTokenInfo; CK_C_GetMechanismList C_GetMechanismList; CK_C_GetMechanismInfo C_GetMechanismInfo; CK_C_InitToken C_InitToken; CK_C_InitPIN C_InitPIN; CK_C_SetPIN C_SetPIN; CK_C_OpenSession C_OpenSession; CK_C_CloseSession C_CloseSession; CK_C_CloseAllSessions C_CloseAllSessions; CK_C_GetSessionInfo C_GetSessionInfo; CK_C_GetOperationState C_GetOperationState; CK_C_SetOperationState C_SetOperationState; CK_C_Login C_Login; CK_C_Logout C_Logout; CK_C_CreateObject C_CreateObject; CK_C_CopyObject C_CopyObject; CK_C_DestroyObject C_DestroyObject; CK_C_GetObjectSize C_GetObjectSize; CK_C_GetAttributeValue C_GetAttributeValue; CK_C_SetAttributeValue C_SetAttributeValue; CK_C_FindObjectsInit C_FindObjectsInit; CK_C_FindObjects C_FindObjects; CK_C_FindObjectsFinal C_FindObjectsFinal; CK_C_EncryptInit C_EncryptInit; CK_C_Encrypt C_Encrypt; CK_C_EncryptUpdate C_EncryptUpdate; CK_C_EncryptFinal C_EncryptFinal; CK_C_DecryptInit C_DecryptInit; CK_C_Decrypt C_Decrypt; CK_C_DecryptUpdate C_DecryptUpdate; CK_C_DecryptFinal C_DecryptFinal; CK_C_DigestInit C_DigestInit; CK_C_Digest C_Digest; CK_C_DigestUpdate C_DigestUpdate; CK_C_DigestKey C_DigestKey; CK_C_DigestFinal C_DigestFinal; CK_C_SignInit C_SignInit; CK_C_Sign C_Sign; CK_C_SignUpdate C_SignUpdate; CK_C_SignFinal C_SignFinal; CK_C_SignRecoverInit C_SignRecoverInit; CK_C_SignRecover C_SignRecover; CK_C_VerifyInit C_VerifyInit; CK_C_Verify C_Verify; CK_C_VerifyUpdate C_VerifyUpdate; CK_C_VerifyFinal C_VerifyFinal; CK_C_VerifyRecoverInit C_VerifyRecoverInit; CK_C_VerifyRecover C_VerifyRecover; CK_C_DigestEncryptUpdate C_DigestEncryptUpdate; CK_C_DecryptDigestUpdate C_DecryptDigestUpdate; CK_C_SignEncryptUpdate C_SignEncryptUpdate; CK_C_DecryptVerifyUpdate C_DecryptVerifyUpdate; CK_C_GenerateKey C_GenerateKey; CK_C_GenerateKeyPair C_GenerateKeyPair; CK_C_WrapKey C_WrapKey; CK_C_UnwrapKey C_UnwrapKey; CK_C_DeriveKey C_DeriveKey; CK_C_SeedRandom C_SeedRandom; CK_C_GenerateRandom C_GenerateRandom; CK_C_GetFunctionStatus C_GetFunctionStatus; CK_C_CancelFunction C_CancelFunction; CK_C_WaitForSlotEvent C_WaitForSlotEvent; }; typedef ck_rv_t (*ck_createmutex_t) (void **mutex); typedef ck_rv_t (*ck_destroymutex_t) (void *mutex); typedef ck_rv_t (*ck_lockmutex_t) (void *mutex); typedef ck_rv_t (*ck_unlockmutex_t) (void *mutex); struct ck_c_initialize_args { ck_createmutex_t create_mutex; ck_destroymutex_t destroy_mutex; ck_lockmutex_t lock_mutex; ck_unlockmutex_t unlock_mutex; ck_flags_t flags; void *reserved; }; #define CKF_LIBRARY_CANT_CREATE_OS_THREADS (1UL << 0) #define CKF_OS_LOCKING_OK (1UL << 1) #define CKR_OK (0UL) #define CKR_CANCEL (1UL) #define CKR_HOST_MEMORY (2UL) #define CKR_SLOT_ID_INVALID (3UL) #define CKR_GENERAL_ERROR (5UL) #define CKR_FUNCTION_FAILED (6UL) #define CKR_ARGUMENTS_BAD (7UL) #define CKR_NO_EVENT (8UL) #define CKR_NEED_TO_CREATE_THREADS (9UL) #define CKR_CANT_LOCK (0xaUL) #define CKR_ATTRIBUTE_READ_ONLY (0x10UL) #define CKR_ATTRIBUTE_SENSITIVE (0x11UL) #define CKR_ATTRIBUTE_TYPE_INVALID (0x12UL) #define CKR_ATTRIBUTE_VALUE_INVALID (0x13UL) #define CKR_ACTION_PROHIBITED (0x1BUL) #define CKR_DATA_INVALID (0x20UL) #define CKR_DATA_LEN_RANGE (0x21UL) #define CKR_DEVICE_ERROR (0x30UL) #define CKR_DEVICE_MEMORY (0x31UL) #define CKR_DEVICE_REMOVED (0x32UL) #define CKR_ENCRYPTED_DATA_INVALID (0x40UL) #define CKR_ENCRYPTED_DATA_LEN_RANGE (0x41UL) #define CKR_FUNCTION_CANCELED (0x50UL) #define CKR_FUNCTION_NOT_PARALLEL (0x51UL) #define CKR_FUNCTION_NOT_SUPPORTED (0x54UL) #define CKR_KEY_HANDLE_INVALID (0x60UL) #define CKR_KEY_SIZE_RANGE (0x62UL) #define CKR_KEY_TYPE_INCONSISTENT (0x63UL) #define CKR_KEY_NOT_NEEDED (0x64UL) #define CKR_KEY_CHANGED (0x65UL) #define CKR_KEY_NEEDED (0x66UL) #define CKR_KEY_INDIGESTIBLE (0x67UL) #define CKR_KEY_FUNCTION_NOT_PERMITTED (0x68UL) #define CKR_KEY_NOT_WRAPPABLE (0x69UL) #define CKR_KEY_UNEXTRACTABLE (0x6aUL) #define CKR_MECHANISM_INVALID (0x70UL) #define CKR_MECHANISM_PARAM_INVALID (0x71UL) #define CKR_OBJECT_HANDLE_INVALID (0x82UL) #define CKR_OPERATION_ACTIVE (0x90UL) #define CKR_OPERATION_NOT_INITIALIZED (0x91UL) #define CKR_PIN_INCORRECT (0xa0UL) #define CKR_PIN_INVALID (0xa1UL) #define CKR_PIN_LEN_RANGE (0xa2UL) #define CKR_PIN_EXPIRED (0xa3UL) #define CKR_PIN_LOCKED (0xa4UL) #define CKR_SESSION_CLOSED (0xb0UL) #define CKR_SESSION_COUNT (0xb1UL) #define CKR_SESSION_HANDLE_INVALID (0xb3UL) #define CKR_SESSION_PARALLEL_NOT_SUPPORTED (0xb4UL) #define CKR_SESSION_READ_ONLY (0xb5UL) #define CKR_SESSION_EXISTS (0xb6UL) #define CKR_SESSION_READ_ONLY_EXISTS (0xb7UL) #define CKR_SESSION_READ_WRITE_SO_EXISTS (0xb8UL) #define CKR_SIGNATURE_INVALID (0xc0UL) #define CKR_SIGNATURE_LEN_RANGE (0xc1UL) #define CKR_TEMPLATE_INCOMPLETE (0xd0UL) #define CKR_TEMPLATE_INCONSISTENT (0xd1UL) #define CKR_TOKEN_NOT_PRESENT (0xe0UL) #define CKR_TOKEN_NOT_RECOGNIZED (0xe1UL) #define CKR_TOKEN_WRITE_PROTECTED (0xe2UL) #define CKR_UNWRAPPING_KEY_HANDLE_INVALID (0xf0UL) #define CKR_UNWRAPPING_KEY_SIZE_RANGE (0xf1UL) #define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT (0xf2UL) #define CKR_USER_ALREADY_LOGGED_IN (0x100UL) #define CKR_USER_NOT_LOGGED_IN (0x101UL) #define CKR_USER_PIN_NOT_INITIALIZED (0x102UL) #define CKR_USER_TYPE_INVALID (0x103UL) #define CKR_USER_ANOTHER_ALREADY_LOGGED_IN (0x104UL) #define CKR_USER_TOO_MANY_TYPES (0x105UL) #define CKR_WRAPPED_KEY_INVALID (0x110UL) #define CKR_WRAPPED_KEY_LEN_RANGE (0x112UL) #define CKR_WRAPPING_KEY_HANDLE_INVALID (0x113UL) #define CKR_WRAPPING_KEY_SIZE_RANGE (0x114UL) #define CKR_WRAPPING_KEY_TYPE_INCONSISTENT (0x115UL) #define CKR_RANDOM_SEED_NOT_SUPPORTED (0x120UL) #define CKR_RANDOM_NO_RNG (0x121UL) #define CKR_DOMAIN_PARAMS_INVALID (0x130UL) #define CKR_BUFFER_TOO_SMALL (0x150UL) #define CKR_SAVED_STATE_INVALID (0x160UL) #define CKR_INFORMATION_SENSITIVE (0x170UL) #define CKR_STATE_UNSAVEABLE (0x180UL) #define CKR_CRYPTOKI_NOT_INITIALIZED (0x190UL) #define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191UL) #define CKR_MUTEX_BAD (0x1a0UL) #define CKR_MUTEX_NOT_LOCKED (0x1a1UL) #define CKR_NEW_PIN_MODE (0x1b0UL) #define CKR_NEXT_OTP (0x1b1UL) #define CKR_EXCEEDED_MAX_ITERATIONS (0x1c0UL) #define CKR_FIPS_SELF_TEST_FAILED (0x1c1UL) #define CKR_LIBRARY_LOAD_FAILED (0x1c2UL) #define CKR_PIN_TOO_WEAK (0x1c3UL) #define CKR_PUBLIC_KEY_INVALID (0x1c4UL) #define CKR_FUNCTION_REJECTED (0x200UL) #define CKR_VENDOR_DEFINED ((unsigned long) (1UL << 31)) #define CKZ_DATA_SPECIFIED (0x01UL) /* Compatibility layer. */ #ifdef CRYPTOKI_COMPAT #undef CK_DEFINE_FUNCTION #define CK_DEFINE_FUNCTION(retval, name) retval CK_SPEC name /* For NULL. */ #include typedef unsigned char CK_BYTE; typedef unsigned char CK_CHAR; typedef unsigned char CK_UTF8CHAR; typedef unsigned char CK_BBOOL; typedef unsigned long int CK_ULONG; typedef long int CK_LONG; typedef CK_BYTE *CK_BYTE_PTR; typedef CK_CHAR *CK_CHAR_PTR; typedef CK_UTF8CHAR *CK_UTF8CHAR_PTR; typedef CK_ULONG *CK_ULONG_PTR; typedef void *CK_VOID_PTR; typedef void **CK_VOID_PTR_PTR; #define CK_FALSE 0 #define CK_TRUE 1 #ifndef CK_DISABLE_TRUE_FALSE #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif typedef struct ck_version CK_VERSION; typedef struct ck_version *CK_VERSION_PTR; typedef struct ck_info CK_INFO; typedef struct ck_info *CK_INFO_PTR; typedef ck_slot_id_t *CK_SLOT_ID_PTR; typedef struct ck_slot_info CK_SLOT_INFO; typedef struct ck_slot_info *CK_SLOT_INFO_PTR; typedef struct ck_token_info CK_TOKEN_INFO; typedef struct ck_token_info *CK_TOKEN_INFO_PTR; typedef ck_session_handle_t *CK_SESSION_HANDLE_PTR; typedef struct ck_session_info CK_SESSION_INFO; typedef struct ck_session_info *CK_SESSION_INFO_PTR; typedef ck_object_handle_t *CK_OBJECT_HANDLE_PTR; typedef ck_object_class_t *CK_OBJECT_CLASS_PTR; typedef struct ck_attribute CK_ATTRIBUTE; typedef struct ck_attribute *CK_ATTRIBUTE_PTR; typedef struct ck_date CK_DATE; typedef struct ck_date *CK_DATE_PTR; typedef ck_mechanism_type_t *CK_MECHANISM_TYPE_PTR; typedef struct ck_mechanism CK_MECHANISM; typedef struct ck_mechanism *CK_MECHANISM_PTR; typedef struct ck_mechanism_info CK_MECHANISM_INFO; typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR; typedef struct ck_otp_mechanism_info CK_OTP_MECHANISM_INFO; typedef struct ck_otp_mechanism_info *CK_OTP_MECHANISM_INFO_PTR; typedef struct ck_function_list CK_FUNCTION_LIST; typedef struct ck_function_list *CK_FUNCTION_LIST_PTR; typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR; typedef struct ck_c_initialize_args CK_C_INITIALIZE_ARGS; typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR; typedef struct ck_rsa_pkcs_pss_params CK_RSA_PKCS_PSS_PARAMS; typedef struct ck_rsa_pkcs_pss_params *CK_RSA_PKCS_PSS_PARAMS_PTR; typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS; typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR; typedef struct ck_aes_ctr_params CK_AES_CTR_PARAMS; typedef struct ck_aes_ctr_params *CK_AES_CTR_PARAMS_PTR; typedef struct ck_gcm_params CK_GCM_PARAMS; typedef struct ck_gcm_params *CK_GCM_PARAMS_PTR; typedef struct ck_ecdh1_derive_params CK_ECDH1_DERIVE_PARAMS; typedef struct ck_ecdh1_derive_params *CK_ECDH1_DERIVE_PARAMS_PTR; typedef struct ck_key_derivation_string_data CK_KEY_DERIVATION_STRING_DATA; typedef struct ck_key_derivation_string_data *CK_KEY_DERIVATION_STRING_DATA_PTR; typedef struct ck_des_cbc_encrypt_data_params CK_DES_CBC_ENCRYPT_DATA_PARAMS; typedef struct ck_des_cbc_encrypt_data_params *CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR; typedef struct ck_aes_cbc_encrypt_data_params CK_AES_CBC_ENCRYPT_DATA_PARAMS; typedef struct ck_aes_cbc_encrypt_data_params *CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR; #ifndef NULL_PTR #define NULL_PTR NULL #endif /* Delete the helper macros defined at the top of the file. */ #undef ck_flags_t #undef ck_version #undef ck_info #undef cryptoki_version #undef manufacturer_id #undef library_description #undef library_version #undef ck_notification_t #undef ck_slot_id_t #undef ck_slot_info #undef slot_description #undef hardware_version #undef firmware_version #undef ck_token_info #undef serial_number #undef max_session_count #undef session_count #undef max_rw_session_count #undef rw_session_count #undef max_pin_len #undef min_pin_len #undef total_public_memory #undef free_public_memory #undef total_private_memory #undef free_private_memory #undef utc_time #undef ck_session_handle_t #undef ck_user_type_t #undef ck_state_t #undef ck_session_info #undef slot_id #undef device_error #undef ck_object_handle_t #undef ck_object_class_t #undef ck_hw_feature_type_t #undef ck_key_type_t #undef ck_certificate_type_t #undef ck_attribute_type_t #undef ck_attribute #undef value #undef value_len #undef params #undef count #undef ck_date #undef ck_mechanism_type_t #undef ck_mechanism #undef parameter #undef parameter_len #undef ck_mechanism_info #undef ck_param_type #undef ck_otp_param #undef ck_otp_params #undef ck_otp_signature_info #undef min_key_size #undef max_key_size #undef ck_rv_t #undef ck_notify_t #undef ck_function_list #undef ck_createmutex_t #undef ck_destroymutex_t #undef ck_lockmutex_t #undef ck_unlockmutex_t #undef ck_c_initialize_args #undef create_mutex #undef destroy_mutex #undef lock_mutex #undef unlock_mutex #undef reserved #endif /* CRYPTOKI_COMPAT */ /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) #pragma pack(pop, cryptoki) #endif #if defined(__cplusplus) } #endif #endif /* PKCS11_H */ nethsm-pkcs11-2.0.0/p11nethsm.conf000066400000000000000000000011431507371613300166030ustar00rootroot00000000000000# log_file: /tmp/p11nethsm.log # log_level: Trace slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" administrator: username: "admin" password: "Administrator" instances: - url: "https://localhost:8443/api/v1" max_idle_connections: 16 danger_insecure_cert: true # sha256_fingerprints: # - "31:92:8E:A4:5E:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" retries: count: 10 delay_seconds: 1 timeout_seconds: 10 nethsm-pkcs11-2.0.0/p11nethsm.example.conf000066400000000000000000000111111507371613300202310ustar00rootroot00000000000000# Optional log level, acceptable values are Trace, Debug, Info, Warn and Error log_level: Debug # By default, the module logs to both syslog and stderr, trying the sockets /dev/log, /var/run/syslog and finally /var/run/log # A custom socket can be configured: syslog_socket: /var/nethsm/log # Instead of a socket, a custom UDP or TCP syslog can be configured: # syslog_udp: # to_addr: 127.0.0:1:514 # from_addr: 127.0.0:1:4789 # syslog_tcp: 127.0.0.1:601 # Only one option among "syslog_socket", "syslog_udp", "syslog_tcp" can be configured at the same time # You can configure the syslog facility ( "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp", "local0", "local1", "local2", "local3", "local4", "local5", "local6" or "local7"): syslog_facility: "user" # You can set the hostname (for use only with syslog_udp or syslog_tcp) # syslog_hostname: "localhsm-pkcs11" # You can set the process name (defaults to the process name obtained from the OS) # syslog_process: "NetHSM Pkcs11" # You can set the pid used in logs (defaults to the process id obtained from the OS) # syslog_pid: 0 # You can also configure a custom file, or "-" for stderr. # log_file: /tmp/p11nethsm.log # Each "slot" represents a HSM cluster of server that share the same user and keys. slots: - label: LocalHSM # Name your NetHSM however you want description: Local HSM (docker) # Optional description # Users connecting to the NetHSM server operator: username: "operator" # If the password starts with `env:`, it will obtain the password from an environment variable: # password: "env:LOCALHSMPASS" password: "localpass" administrator: username: "admin" # List the NetHSM instances instances: - url: "https://keyfender:8443/api/v1" # URL to reach the server # To avoid having to re-open connections on each requests, the module keeps a connection pool to each instance. If the module is used by a multithreaded application, multiple connections can be opened at the same time. # This configures the maximum number of connections in the pool at the same time. # Note that this does not limit the total number of open connections. # Having a degree of parrallelism that is higher than the max number of idle connection can lead overhead as those connections will be closed an re-opened frenquently max_idle_connections: 10 # By default, the certificate of the HSM will be validated using the system's root certificate authority. # When the NetHSM uses a self-signed certificate, it can be verified against an allowed list of sha256 fingerprint of the NetHSM's certificate: sha256_fingerprints: - "31:92:8E:A4:5E:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" # Alternatively certificate checks can be skipped entirely with danger_insecure_cert option. # This should be avoided if possible and certainly not used with a productive NetHSM. # danger_insecure_cert: true # Configure whether the certificates stored in the nethsm are stored in PEM or DER # The nethsm itself supports both, but some tooling may only support one of the encodings. # Valid values are PEM or DER. Defaults to PEM # # Values exchanged over the PKCS#11 interface will always be DER encoded. # This config only changes the format they are stored in on the Nethsm itself for compatibility with other tooling. certificate_format: PEM # Configure the network retry mechanism. If absent, no retries are attempted on a network error retries: # The number of retries after a network error count: 3 # The delay between retries, in integer seconds delay_seconds: 1 # it is possible to configure idle connections to make use of TCP keepalives, preventing the closing of connections by a firewall or detecting such cases tcp_keepalive: # the number of seconds before keepalives packets start being sent time_seconds: 600 # the number of seconds between each keepalive packet interval_seconds: 60 # the number of keepalive packets being sent without a response before the connection # is considered closed retries: 3 # Time a connection can spend idle before being closed connections_max_idle_duration: 1800 # Configurable timeout for network operations. If a network operation takes more than, `timeout_seconds`, consider it failed. If `retries` is configured, it will be retried. # Defaults to infinite timeout_seconds: 10 nethsm-pkcs11-2.0.0/pkcs11/000077500000000000000000000000001507371613300152175ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/Cargo.toml000066400000000000000000000030231507371613300171450ustar00rootroot00000000000000[package] name = "nethsm_pkcs11" version = "2.0.0" edition = "2021" [lib] name = "nethsm_pkcs11" crate-type = ["cdylib"] [dependencies] env_logger = { default-features = false, version = "0.11.7", features = ["auto-color", "humantime"] } cryptoki-sys = "0.4.0" log = "0.4.19" serde = { version = "1", features = ["derive"], default-features = false } serde_yaml = "0.9.22" serde_json = { default-features = false, version = "1.0.64" } lazy_static = "1.4.0" nethsm-sdk-rs = "3" rustls = { version = "0.23", default-features = false } rustls-native-certs = "0.8" base64ct = { version = "1.6", default-features = false } hex = "0.4" der = { version = "0.7", default-features = false } pem-rfc7468 = "0.7" x509-cert = { features = ["pem"], default-features = false, version = "0.2" } sha2 = { default-features = false, version = "0.10" } sha1 = { default-features = false, version = "0.10" } digest = { default-features = false, version = "0.10" } rayon = "1.8.0" syslog = "6.1.1" thiserror = "2.0.12" arc-swap = "1.7.1" hex-literal = "1.0.0" md-5 = "0.10.6" config_file = { path = "config_file" } # Needed to prevent breaking change with rustls updates ureq = { version = "=3.1.2", default-features = false } rustls-pki-types = "1.11.0" socket2 = { version = "0.6", features = ["all"] } [dev-dependencies] once_cell = "1.19.0" pkcs11 = "0.5.0" tempfile = "3.12.0" test-log = "0.2.16" time = "0.3.36" tokio = {version = "1", default-features = false, features = ["net", "sync", "rt", "io-util", "time"] } [features] pkcs11-full-tests = [] default = [] nethsm-pkcs11-2.0.0/pkcs11/config_file/000077500000000000000000000000001507371613300174635ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/config_file/Cargo.toml000066400000000000000000000005221507371613300214120ustar00rootroot00000000000000[package] name = "config_file" version = "0.1.0" edition = "2021" [dependencies] serde = { version = "1", features = ["derive"] } log = "0.4.22" serde_yaml = "0.9.34" merge = { features = [ "derive", "std", ], default-features = false, version = "0.2.0" } hex = "0.4.3" thiserror = "2.0.12" [dev-dependencies] hex-literal = "1.0.0" nethsm-pkcs11-2.0.0/pkcs11/config_file/src/000077500000000000000000000000001507371613300202525ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/config_file/src/lib.rs000066400000000000000000000321731507371613300213740ustar00rootroot00000000000000use std::{fmt::Display, io::Read, mem, net::SocketAddr, path::PathBuf}; use merge::Merge; use serde::{Deserialize, Serialize}; #[derive(Debug, thiserror::Error)] pub enum ConfigError { #[error("Failed to load configuration file")] Io(std::io::Error), #[error("Failed to parse configuration file {0}")] Yaml(serde_yaml::Error), #[error("Config file not found")] NoConfigFile, } const CONFIG_FILE_NAME: &str = "p11nethsm.conf"; pub const ENV_VAR_CONFIG_FILE: &str = "P11NETHSM_CONFIG_FILE"; pub fn config_files() -> Result, PathBuf)>, ConfigError> { if let Ok(file_path) = std::env::var(ENV_VAR_CONFIG_FILE) { let file = std::fs::read(&file_path).map_err(ConfigError::Io)?; return Ok(vec![(file, file_path.into())]); } let mut config_folders = vec![ "/etc/nitrokey".to_string(), "/usr/local/etc/nitrokey".to_string(), ]; if let Ok(home) = std::env::var("HOME") { config_folders.push(format!("{home}/.config/nitrokey")); } let mut res = Vec::new(); let mut buffer = Vec::new(); for folder in config_folders { let file_path = format!("{folder}/{CONFIG_FILE_NAME}"); if let Ok(mut file) = std::fs::File::open(&file_path) { file.read_to_end(&mut buffer).map_err(ConfigError::Io)?; res.push((mem::take(&mut buffer), file_path.into())); } } Ok(res) } pub fn merge_configurations<'a>( configs: impl IntoIterator, ) -> Result { let mut config = P11Config::default(); let mut no_config = true; for file in configs { let parsed = serde_yaml::from_slice(file).map_err(ConfigError::Yaml)?; no_config = false; config.merge(parsed); } if no_config { return Err(ConfigError::NoConfigFile); } Ok(config) } #[cfg(test)] pub fn read_configuration() -> Result { let configs = config_files()?; merge_configurations(configs.iter().map(|(data, _)| &**data)) } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub enum LogLevel { Trace, Debug, Info, Warn, Error, } impl From for log::LevelFilter { fn from(level: LogLevel) -> Self { match level { LogLevel::Trace => log::LevelFilter::Trace, LogLevel::Debug => log::LevelFilter::Debug, LogLevel::Info => log::LevelFilter::Info, LogLevel::Warn => log::LevelFilter::Warn, LogLevel::Error => log::LevelFilter::Error, } } } // representation of the config file to parse #[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)] pub struct SyslogUdp { pub to_addr: SocketAddr, pub from_addr: SocketAddr, } // representation of the config file to parse #[derive(Debug, Clone, Serialize, Deserialize, Merge, Default, PartialEq)] pub struct P11Config { #[merge(strategy = merge::option::overwrite_none)] pub syslog_socket: Option, #[merge(strategy = merge::option::overwrite_none)] pub syslog_udp: Option, #[merge(strategy = merge::option::overwrite_none)] pub syslog_tcp: Option, #[merge(strategy = merge::option::overwrite_none)] #[serde(default)] pub syslog_facility: Option, #[merge(strategy = merge::option::overwrite_none)] #[serde(default)] pub syslog_hostname: Option, #[merge(strategy = merge::option::overwrite_none)] #[serde(default)] pub syslog_process: Option, #[merge(strategy = merge::option::overwrite_none)] #[serde(default)] pub syslog_pid: Option, #[merge(strategy = merge::option::overwrite_none)] pub log_file: Option, #[merge(strategy = merge::option::overwrite_none)] pub log_level: Option, #[merge(strategy = merge::vec::append)] pub slots: Vec, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct RetryConfig { pub count: u32, pub delay_seconds: u64, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct TcpKeepaliveConfig { pub time_seconds: u64, pub interval_seconds: u64, pub retries: u32, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct HexFingerprint { pub value: Vec, } impl Serialize for HexFingerprint { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(&hex::encode(&self.value)) } } impl<'de> Deserialize<'de> for HexFingerprint { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct HexFingerprintVisitor; impl serde::de::Visitor<'_> for HexFingerprintVisitor { type Value = HexFingerprint; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("An hexadecimal value, possibly separated with ':'") } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { Ok(HexFingerprint { value: hex::decode(v.replace(':', "")).map_err(|err| { E::custom(format_args!( "Failed to parse hexadecimal fingerprint: {err}" )) })?, }) } } deserializer.deserialize_str(HexFingerprintVisitor) } } #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Copy)] #[serde(rename_all = "UPPERCASE")] pub enum CertificateFormat { #[default] Pem, Der, } impl Display for CertificateFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Pem => f.write_str("PEM"), Self::Der => f.write_str("DER"), } } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct InstanceConfig { pub url: String, #[serde(default)] pub danger_insecure_cert: bool, #[serde(default)] pub sha256_fingerprints: Vec, #[serde(default)] pub max_idle_connections: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct SlotConfig { pub label: String, pub operator: Option, pub administrator: Option, pub description: Option, pub instances: Vec, #[serde(default)] pub retries: Option, #[serde(default)] pub tcp_keepalive: Option, #[serde(default)] pub timeout_seconds: Option, #[serde(default)] pub connections_max_idle_duration: Option, #[serde(default)] pub certificate_format: CertificateFormat, } // An user #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct UserConfig { pub username: String, #[serde(deserialize_with = "deserialize_password", default)] pub password: Option, } const PASSWORD_ENV_PREFIX: &str = "env:"; // Deserialize a string, but if it starts with "env:" then read the environment variable corresponding to the rest of the string fn deserialize_password<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { match Option::::deserialize(deserializer)? { Some(s) => { if s.starts_with(PASSWORD_ENV_PREFIX) { let var = s.trim_start_matches(PASSWORD_ENV_PREFIX); let val = std::env::var(var).map_err(serde::de::Error::custom)?; return Ok(Some(val)); } if s.is_empty() { return Ok(None); } Ok(Some(s)) } None => Ok(None), } } #[cfg(test)] mod tests { use hex_literal::hex; use std::fs; use super::*; // Ignored by default due ENV variable being changed for the duration of the tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_read_home_config() { let config = r#" log_file: /tmp/p11nethsm.log log_level: Trace slots: - label: test operator: username: test password: test_password instances: - url: https://localhost:23443 danger_insecure_cert: true "#; let home = "/tmp/home/"; // create a temporary "fake" home folder fs::create_dir_all(format!("{home}.config/nitrokey")).unwrap(); fs::write( format!("{home}/.config/nitrokey/{CONFIG_FILE_NAME}"), config, ) .unwrap(); std::env::remove_var(ENV_VAR_CONFIG_FILE); std::env::set_var("HOME", home); let config = read_configuration().unwrap(); assert_eq!(config.log_file, Some("/tmp/p11nethsm.log".into())); assert!(matches!(config.log_level, Some(LogLevel::Trace))); assert_eq!(config.slots.len(), 1); assert_eq!(config.slots[0].label, "test"); assert_eq!(config.slots[0].operator.as_ref().unwrap().username, "test"); assert_eq!( config.slots[0].operator.as_ref().unwrap().password, Some("test_password".to_string()) ); // clean up fs::remove_dir_all(home).unwrap(); } // Ignored by default due ENV variable being changed for the duration of the tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_read_config_no_file() { std::env::remove_var(ENV_VAR_CONFIG_FILE); let config = read_configuration(); assert!(config.is_err()); assert!(matches!( config.unwrap_err(), super::ConfigError::NoConfigFile )); } #[test] fn test_deserialize_password_env() { let config = r#" username: test password: env:TEST_PASSWORD "#; std::env::set_var("TEST_PASSWORD", "test_password"); let config: super::UserConfig = serde_yaml::from_str(config).unwrap(); assert_eq!(config.username, "test"); assert_eq!(config.password, Some("test_password".to_string())); } #[test] fn test_deserialize_password() { let config = r#" username: test password: test_password "#; let config: super::UserConfig = serde_yaml::from_str(config).unwrap(); assert_eq!(config.username, "test"); assert_eq!(config.password, Some("test_password".to_string())); } #[test] fn test_deserialize_password_none() { let config = r#" username: test "#; let config: super::UserConfig = serde_yaml::from_str(config).unwrap(); assert_eq!(config.username, "test"); assert_eq!(config.password, None); } #[test] fn test_deserialize_password_empty() { let config = r#" username: test password: "" "#; let config: super::UserConfig = serde_yaml::from_str(config).unwrap(); assert_eq!(config.username, "test"); assert_eq!(config.password, None); } #[test] fn test_deserialize_full_example_config() { let config = include_str!("../../../p11nethsm.example.conf"); assert_eq!( P11Config { syslog_socket: Some("/var/nethsm/log".into()), syslog_facility: Some("user".into()), syslog_hostname: None, syslog_pid: None, syslog_udp: None, syslog_tcp: None, syslog_process: None, log_file: None, log_level: Some(LogLevel::Debug), slots: vec![SlotConfig { label: "LocalHSM".into(), description: Some("Local HSM (docker)".into()), operator: Some(UserConfig { username: "operator".into(), password: Some("localpass".into()) }), administrator: Some(UserConfig { username: "admin".into(), password: None }), instances: vec![InstanceConfig { url: "https://keyfender:8443/api/v1".into(), danger_insecure_cert: false, sha256_fingerprints: vec![HexFingerprint { value: hex!( "31928EA45E165CA73344E8E98E64C4AE7B2A57E5774349F369C98FC42F3A3B6E" ) .into() }], max_idle_connections: Some(10), }], retries: Some(RetryConfig { count: 3, delay_seconds: 1 }), timeout_seconds: Some(10), tcp_keepalive: Some(TcpKeepaliveConfig { time_seconds: 600, interval_seconds: 60, retries: 3 }), connections_max_idle_duration: Some(60 * 30), certificate_format: CertificateFormat::Pem, }] }, serde_yaml::from_str(config).unwrap() ); } } nethsm-pkcs11-2.0.0/pkcs11/src/000077500000000000000000000000001507371613300160065ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/api/000077500000000000000000000000001507371613300165575ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/api/decrypt.rs000066400000000000000000000352371507371613300206110ustar00rootroot00000000000000use cryptoki_sys::CK_ULONG; use log::error; use crate::{ api::api_function, backend::{ mechanism::{CkRawMechanism, Mechanism}, session::Session, Pkcs11Error, }, data, }; api_function!( C_DecryptInit = decrypt_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn decrypt_init( session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { let raw_mech = unsafe { CkRawMechanism::from_raw_ptr(mechanism_ptr) }.ok_or(Pkcs11Error::ArgumentsBad)?; let mech = Mechanism::from_ckraw_mech(&raw_mech).map_err(|err| { error!("C_DecryptInit() failed to convert mechanism: {err}"); Pkcs11Error::MechanismInvalid })?; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.decrypt_init(&mech, key).map_err(From::from) } api_function!( C_Decrypt = decrypt; hSession: cryptoki_sys::CK_SESSION_HANDLE, pEncryptedData: cryptoki_sys::CK_BYTE_PTR, ulEncryptedDataLen: cryptoki_sys::CK_ULONG, pData: cryptoki_sys::CK_BYTE_PTR, pulDataLen: cryptoki_sys::CK_ULONG_PTR, ); fn decrypt( session: cryptoki_sys::CK_SESSION_HANDLE, encrypted_data_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_data_len: cryptoki_sys::CK_ULONG, data_ptr: cryptoki_sys::CK_BYTE_PTR, data_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = decrypt_impl( &mut session, encrypted_data_ptr, encrypted_data_len, data_ptr, data_len_ptr, ); // A call to C_Decrypt always terminates the active decryption operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the plaintext. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && data_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.decrypt_clear(); } result } fn decrypt_impl( session: &mut Session, encrypted_data_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_data_len: cryptoki_sys::CK_ULONG, data_ptr: cryptoki_sys::CK_BYTE_PTR, data_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if data_len_ptr.is_null() || encrypted_data_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let buffer_size = unsafe { *data_len_ptr } as usize; let theoretical_size = session.decrypt_theoretical_size(encrypted_data_len as usize); unsafe { std::ptr::write(data_len_ptr, theoretical_size as CK_ULONG); } if data_ptr.is_null() { return Ok(()); } if theoretical_size > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } let data = unsafe { std::slice::from_raw_parts(encrypted_data_ptr, encrypted_data_len as usize) }; let decrypted_data = session.decrypt(data)?; unsafe { std::ptr::write(data_len_ptr, decrypted_data.len() as CK_ULONG); } // we double-check the buffer size here, in case the theoretical size was wrong if decrypted_data.len() > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping(decrypted_data.as_ptr(), data_ptr, decrypted_data.len()); } Ok(()) } api_function!( C_DecryptUpdate = decrypt_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, ulEncryptedPartLen: cryptoki_sys::CK_ULONG, pPart: cryptoki_sys::CK_BYTE_PTR, pulPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn decrypt_update( session: cryptoki_sys::CK_SESSION_HANDLE, encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_part_len: cryptoki_sys::CK_ULONG, part_ptr: cryptoki_sys::CK_BYTE_PTR, part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = decrypt_update_impl( &mut session, encrypted_part_ptr, encrypted_part_len, part_ptr, part_len_ptr, ); // A call to C_DecryptUpdate which results in an error other than CKR_BUFFER_TOO_SMALL // terminates the current decryption operation. if result.is_err() && result != Err(Pkcs11Error::BufferTooSmall) { session.decrypt_clear(); } result } fn decrypt_update_impl( session: &mut Session, encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_part_len: cryptoki_sys::CK_ULONG, _part_ptr: cryptoki_sys::CK_BYTE_PTR, part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if part_len_ptr.is_null() || encrypted_part_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let data = unsafe { std::slice::from_raw_parts(encrypted_part_ptr, encrypted_part_len as usize) }; // we only add to the buffer, so we don't need to check the size unsafe { std::ptr::write(part_len_ptr, 0 as CK_ULONG); } session.decrypt_update(data).map_err(From::from) } api_function!( C_DecryptFinal = decrypt_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, pLastPart: cryptoki_sys::CK_BYTE_PTR, pulLastPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn decrypt_final( session: cryptoki_sys::CK_SESSION_HANDLE, last_part_ptr: cryptoki_sys::CK_BYTE_PTR, last_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = decrypt_final_impl(&mut session, last_part_ptr, last_part_len_ptr); // A call to C_DecryptFinal always terminates the active decryption operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the plaintext. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && last_part_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.decrypt_clear(); } result } fn decrypt_final_impl( session: &mut Session, last_part_ptr: cryptoki_sys::CK_BYTE_PTR, last_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if last_part_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let buffer_size = unsafe { *last_part_len_ptr } as usize; let theoretical_size = session.decrypt_theoretical_final_size()?; unsafe { std::ptr::write(last_part_len_ptr, theoretical_size as CK_ULONG); } if last_part_ptr.is_null() { return Ok(()); } if theoretical_size > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } let decrypted_data = session.decrypt_final()?; unsafe { std::ptr::write(last_part_len_ptr, decrypted_data.len() as CK_ULONG); } // we double-check the buffer size here, in case the theoretical size was wrong if decrypted_data.len() > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping(decrypted_data.as_ptr(), last_part_ptr, decrypted_data.len()); } Ok(()) } api_function!( C_DecryptVerifyUpdate = decrypt_verify_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, ulEncryptedPartLen: cryptoki_sys::CK_ULONG, pPart: cryptoki_sys::CK_BYTE_PTR, pulPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn decrypt_verify_update( _session: cryptoki_sys::CK_SESSION_HANDLE, _encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, _encrypted_part_len: cryptoki_sys::CK_ULONG, _part_ptr: cryptoki_sys::CK_BYTE_PTR, _part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } #[cfg(test)] mod tests { use super::*; use crate::{backend::slot::init_for_tests, data::SESSION_MANAGER}; fn setup_session() -> cryptoki_sys::CK_SESSION_HANDLE { SESSION_MANAGER.lock().unwrap().setup_dummy_session() } #[test] fn test_decrypt_init_null_mech() { let _guard = init_for_tests(); let rv = C_DecryptInit(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_init_unknown_mech() { let _guard = init_for_tests(); let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: 15000, // doesn't exist pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_DecryptInit(0, &mut mech, 0); assert_eq!(rv, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_decrypt_init_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: 0, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_DecryptInit(0, &mut mech, 0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_decrypt_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let rv = C_Decrypt( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_decrypt_null_data_len() { let _guard = init_for_tests(); let mut encrypted_data = [0u8; 32]; let session_handle = setup_session(); let rv = C_Decrypt( session_handle, encrypted_data.as_mut_ptr(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_null_encrypted_data() { let _guard = init_for_tests(); let mut data_len = 0; let session_handle = setup_session(); let rv = C_Decrypt( session_handle, std::ptr::null_mut(), 32, std::ptr::null_mut(), &mut data_len, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_null_data() { let _guard = init_for_tests(); let mut data_len = 0; let session_handle = setup_session(); let mut encrypted_data = [0u8; 32]; let rv = C_Decrypt( session_handle, encrypted_data.as_mut_ptr(), 32, std::ptr::null_mut(), &mut data_len, ); assert_eq!(rv, cryptoki_sys::CKR_OK); } #[test] fn test_decrypt_update_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let rv = C_DecryptUpdate( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_decrypt_update_null_encrypted_part() { let _guard = init_for_tests(); let session_handle = setup_session(); let mut part_len = 0; let mut part = [0u8; 32]; let rv = C_DecryptUpdate( session_handle, std::ptr::null_mut(), 0, part.as_mut_ptr(), &mut part_len, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_update_null_part_len() { let _guard = init_for_tests(); let session_handle = setup_session(); let mut encrypted_part = [0u8; 32]; let mut part = [0u8; 32]; let rv = C_DecryptUpdate( session_handle, encrypted_part.as_mut_ptr(), 0, part.as_mut_ptr(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_update_operation_not_initialized() { let _guard = init_for_tests(); let session_handle = setup_session(); let mut encrypted_part = [0u8; 32]; let mut part = [0u8; 32]; let mut part_len = 0; let rv = C_DecryptUpdate( session_handle, encrypted_part.as_mut_ptr(), 0, part.as_mut_ptr(), &mut part_len, ); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } #[test] fn test_decrypt_final_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut last_part_len = 0; let rv = C_DecryptFinal(0, std::ptr::null_mut(), &mut last_part_len); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_decrypt_final_null_last_part_len() { let _guard = init_for_tests(); let session_handle = setup_session(); let mut last_part = [0u8; 32]; let rv = C_DecryptFinal(session_handle, last_part.as_mut_ptr(), std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_decrypt_final_operation_not_initialized() { let _guard = init_for_tests(); let session_handle = setup_session(); let mut last_part = [0u8; 32]; let mut last_part_len = 0; let rv = C_DecryptFinal(session_handle, last_part.as_mut_ptr(), &mut last_part_len); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } // #[test] // fn test_decrypt_final_null_last_part() { // init_for_tests(); // let session_handle = setup_session(); // let mut pulLastPartLen = 0; // let rv = C_DecryptFinal(session_handle, std::ptr::null_mut(), &mut pulLastPartLen); // assert_eq!(rv, cryptoki_sys::CKR_OK); // } // unsupported function #[test] fn test_decrypt_verify_update() { let _guard = init_for_tests(); let rv = C_DecryptVerifyUpdate( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/digest.rs000066400000000000000000000161461507371613300204140ustar00rootroot00000000000000/* We won't implement these function as it is not a feature of NetHSM. */ use crate::{api::api_function, backend::Pkcs11Error}; api_function!( C_DigestInit = digest_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: *mut cryptoki_sys::CK_MECHANISM, ); fn digest_init( _session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: *mut cryptoki_sys::CK_MECHANISM, ) -> Result<(), Pkcs11Error> { if mechanism_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_Digest = digest; hSession: cryptoki_sys::CK_SESSION_HANDLE, pData: *mut cryptoki_sys::CK_BYTE, ulDataLen: cryptoki_sys::CK_ULONG, pDigest: *mut cryptoki_sys::CK_BYTE, pulDigestLen: *mut cryptoki_sys::CK_ULONG, ); fn digest( _session: cryptoki_sys::CK_SESSION_HANDLE, data_ptr: *mut cryptoki_sys::CK_BYTE, _data_len: cryptoki_sys::CK_ULONG, digest_ptr: *mut cryptoki_sys::CK_BYTE, digest_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if data_ptr.is_null() || digest_ptr.is_null() || digest_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DigestUpdate = digest_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: *mut cryptoki_sys::CK_BYTE, ulPartLen: cryptoki_sys::CK_ULONG, ); fn digest_update( _session: cryptoki_sys::CK_SESSION_HANDLE, part_ptr: *mut cryptoki_sys::CK_BYTE, _part_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if part_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DigestFinal = digest_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, pDigest: *mut cryptoki_sys::CK_BYTE, pulDigestLen: *mut cryptoki_sys::CK_ULONG, ); fn digest_final( _session: cryptoki_sys::CK_SESSION_HANDLE, digest_ptr: *mut cryptoki_sys::CK_BYTE, digest_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if digest_ptr.is_null() || digest_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DigestKey = digest_key; hSession: cryptoki_sys::CK_SESSION_HANDLE, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn digest_key( _session: cryptoki_sys::CK_SESSION_HANDLE, _key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DigestEncryptUpdate = digest_encrypt_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: cryptoki_sys::CK_BYTE_PTR, ulPartLen: cryptoki_sys::CK_ULONG, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, pulEncryptedPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn digest_encrypt_update( _session: cryptoki_sys::CK_SESSION_HANDLE, _part_ptr: cryptoki_sys::CK_BYTE_PTR, _part_len: cryptoki_sys::CK_ULONG, _encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, _encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DecryptDigestUpdate = decrypt_digest_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, ulEncryptedPartLen: cryptoki_sys::CK_ULONG, pPart: cryptoki_sys::CK_BYTE_PTR, pulPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn decrypt_digest_update( _session: cryptoki_sys::CK_SESSION_HANDLE, _encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, _encrypted_part_len: cryptoki_sys::CK_ULONG, _part_ptr: cryptoki_sys::CK_BYTE_PTR, _part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } #[cfg(test)] mod tests { use cryptoki_sys::CK_ULONG; use crate::backend::slot::init_for_tests; use super::*; #[test] fn test_digest_init() { let _guard = init_for_tests(); let rv = C_DigestInit(0, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: 0, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_DigestInit(0, &mut mech); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_digest() { let _guard = init_for_tests(); let rv = C_Digest( 0, std::ptr::null_mut(), 0 as CK_ULONG, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); let mut digest_len: CK_ULONG = 0; let mut digest: Vec = Vec::new(); let mut data: Vec = Vec::new(); let rv = C_Digest( 0, data.as_mut_ptr(), data.len() as CK_ULONG, digest.as_mut_ptr(), &mut digest_len, ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_digest_update() { let _guard = init_for_tests(); let rv = C_DigestUpdate(0, std::ptr::null_mut(), 0 as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); let mut data: Vec = Vec::new(); let rv = C_DigestUpdate(0, data.as_mut_ptr(), data.len() as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_digest_final() { let _guard = init_for_tests(); let rv = C_DigestFinal(0, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); let mut digest_len: CK_ULONG = 0; let mut digest: Vec = Vec::new(); let rv = C_DigestFinal(0, digest.as_mut_ptr(), &mut digest_len); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_digest_key() { let _guard = init_for_tests(); let rv = C_DigestKey(0, 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_digest_encrypt_update() { let _guard = init_for_tests(); let mut encrypted_part_len: CK_ULONG = 0; let mut encrypted_part: Vec = Vec::new(); let mut part: Vec = Vec::new(); let rv = C_DigestEncryptUpdate( 0, part.as_mut_ptr(), part.len() as CK_ULONG, encrypted_part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_decrypt_digest_update() { let _guard = init_for_tests(); let mut encrypted_part_len: CK_ULONG = 0; let mut encrypted_part: Vec = Vec::new(); let mut part: Vec = Vec::new(); let rv = C_DecryptDigestUpdate( 0, encrypted_part.as_mut_ptr(), encrypted_part.len() as CK_ULONG, part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/encrypt.rs000066400000000000000000000451011507371613300206120ustar00rootroot00000000000000use cryptoki_sys::CK_ULONG; use log::{error, trace}; use crate::{ api::api_function, backend::{ encrypt::ENCRYPT_BLOCK_SIZE, mechanism::{CkRawMechanism, Mechanism}, session::Session, Pkcs11Error, }, data, }; api_function!( C_EncryptInit = encrypt_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn encrypt_init( session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { let raw_mech = unsafe { CkRawMechanism::from_raw_ptr(mechanism_ptr) }.ok_or(Pkcs11Error::ArgumentsBad)?; let mech = Mechanism::from_ckraw_mech(&raw_mech).map_err(|err| { error!("C_EncryptInit() failed to convert mechanism: {err}"); Pkcs11Error::MechanismInvalid })?; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.encrypt_init(&mech, key).map_err(From::from) } api_function!( C_Encrypt = encrypt; hSession: cryptoki_sys::CK_SESSION_HANDLE, pData: cryptoki_sys::CK_BYTE_PTR, ulDataLen: cryptoki_sys::CK_ULONG, pEncryptedData: cryptoki_sys::CK_BYTE_PTR, pulEncryptedDataLen: cryptoki_sys::CK_ULONG_PTR, ); fn encrypt( session: cryptoki_sys::CK_SESSION_HANDLE, data_ptr: cryptoki_sys::CK_BYTE_PTR, data_len: cryptoki_sys::CK_ULONG, encrypted_data_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_data_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = encrypt_impl( &mut session, data_ptr, data_len, encrypted_data_ptr, encrypted_data_len_ptr, ); // A call to C_Encrypt always terminates the active encryption operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the ciphertext. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && encrypted_data_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.encrypt_clear(); } result } fn encrypt_impl( session: &mut Session, data_ptr: cryptoki_sys::CK_BYTE_PTR, data_len: cryptoki_sys::CK_ULONG, encrypted_data_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_data_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if data_ptr.is_null() || encrypted_data_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let data = unsafe { std::slice::from_raw_parts(data_ptr, data_len as usize) }; // We only support AES-CBC for now the size of the encrypted data is the same as the size of the input if encrypted_data_ptr.is_null() { unsafe { std::ptr::write(encrypted_data_len_ptr, data.len() as CK_ULONG); } return Ok(()); } let buffer_len = unsafe { *encrypted_data_len_ptr } as usize; unsafe { std::ptr::write(encrypted_data_len_ptr, data.len() as CK_ULONG); } if data.len() > buffer_len { return Err(Pkcs11Error::BufferTooSmall); } let encrypted_data = session.encrypt(data)?; unsafe { std::ptr::write(encrypted_data_len_ptr, encrypted_data.len() as CK_ULONG); } // this shouldn't happen as it's checked above, but it's safe to keep it if encrypted_data.len() != data.len() if encrypted_data.len() > buffer_len { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping( encrypted_data.as_ptr(), encrypted_data_ptr, encrypted_data.len(), ); } Ok(()) } api_function!( C_EncryptUpdate = encrypt_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: cryptoki_sys::CK_BYTE_PTR, ulPartLen: cryptoki_sys::CK_ULONG, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, pulEncryptedPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn encrypt_update( session: cryptoki_sys::CK_SESSION_HANDLE, part_ptr: cryptoki_sys::CK_BYTE_PTR, part_len: cryptoki_sys::CK_ULONG, encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = encrypt_update_impl( &mut session, part_ptr, part_len, encrypted_part_ptr, encrypted_part_len_ptr, ); // A call to C_EncryptUpdate which results in an error other than CKR_BUFFER_TOO_SMALL // terminates the current encryption operation. if result.is_err() && result != Err(Pkcs11Error::BufferTooSmall) { session.encrypt_clear(); } result } fn encrypt_update_impl( session: &mut Session, part_ptr: cryptoki_sys::CK_BYTE_PTR, part_len: cryptoki_sys::CK_ULONG, encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { trace!("C_EncryptUpdate() called with {part_len} bytes"); if part_ptr.is_null() || encrypted_part_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let data = unsafe { std::slice::from_raw_parts(part_ptr, part_len as usize) }; let buffer_len = unsafe { std::ptr::read(encrypted_part_len_ptr) as usize }; // We only support AES-CBC for now the size of the encrypted data is the same as the size of the input let theoretical_size = ENCRYPT_BLOCK_SIZE * (data.len() / ENCRYPT_BLOCK_SIZE + 1); unsafe { std::ptr::write(encrypted_part_len_ptr, theoretical_size as CK_ULONG); } if encrypted_part_ptr.is_null() { return Ok(()); } if buffer_len < theoretical_size { return Err(Pkcs11Error::BufferTooSmall); } let encrypted_data = session.encrypt_update(data)?; unsafe { std::ptr::write(encrypted_part_len_ptr, encrypted_data.len() as CK_ULONG); } // shouldn't happen if encrypted_data.len() > buffer_len { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping( encrypted_data.as_ptr(), encrypted_part_ptr, encrypted_data.len(), ); } Ok(()) } api_function!( C_EncryptFinal = encrypt_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, pLastEncryptedPart: cryptoki_sys::CK_BYTE_PTR, pulLastEncryptedPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn encrypt_final( session: cryptoki_sys::CK_SESSION_HANDLE, last_encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, last_encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = encrypt_final_impl( &mut session, last_encrypted_part_ptr, last_encrypted_part_len_ptr, ); // A call to C_EncryptFinal always terminates the active encryption operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the ciphertext. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && last_encrypted_part_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.encrypt_clear(); } result } fn encrypt_final_impl( session: &mut Session, last_encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, last_encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if last_encrypted_part_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } // enverything should be encrypted at this point, so we just need to return the last block let buffer_len = unsafe { std::ptr::read(last_encrypted_part_len_ptr) as usize }; unsafe { std::ptr::write(last_encrypted_part_len_ptr, ENCRYPT_BLOCK_SIZE as CK_ULONG); } if last_encrypted_part_ptr.is_null() { return Ok(()); } let size = session.encrypt_get_theoretical_final_size()?; if buffer_len < size { return Err(Pkcs11Error::BufferTooSmall); } let encrypted_data = session.encrypt_final()?; unsafe { std::ptr::write( last_encrypted_part_len_ptr, encrypted_data.len() as CK_ULONG, ); } // shouldn't happen if encrypted_data.len() > buffer_len { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping( encrypted_data.as_ptr(), last_encrypted_part_ptr, encrypted_data.len(), ); } Ok(()) } #[cfg(test)] mod tests { use crate::{backend::slot::init_for_tests, data::SESSION_MANAGER}; use super::*; #[test] fn test_encrypt_init_null_mechanism() { let _guard = init_for_tests(); let rv = C_EncryptInit(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_encrypt_init_invalid_mechanism() { let _guard = init_for_tests(); let mut mechanism = cryptoki_sys::CK_MECHANISM { mechanism: 15000, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_EncryptInit(0, &mut mechanism, 0); assert_eq!(rv, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_encrypt_init_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(1); let mut mechanism = cryptoki_sys::CK_MECHANISM { mechanism: 0, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_EncryptInit(1, &mut mechanism, 0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_encrypt_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(1); let mut data: Vec = Vec::new(); let mut encrypted_data: Vec = Vec::new(); let mut encrypted_data_len: CK_ULONG = 0; let rv = C_Encrypt( 1, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_data.as_mut_ptr(), &mut encrypted_data_len, ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_encrypt_update_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(1); let mut data: Vec = Vec::new(); let mut encrypted_data: Vec = Vec::new(); let mut encrypted_data_len: CK_ULONG = 0; let rv = C_EncryptUpdate( 1, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_data.as_mut_ptr(), &mut encrypted_data_len, ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_encrypt_final_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(1); let mut encrypted_data: Vec = Vec::new(); let mut encrypted_data_len: CK_ULONG = 0; let rv = C_EncryptFinal(1, encrypted_data.as_mut_ptr(), &mut encrypted_data_len); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_encrypt_null_data() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut encrypted_data_len: CK_ULONG = 0; let mut encrypted_data: Vec = Vec::new(); let rv = C_Encrypt( session_handle, std::ptr::null_mut(), 0 as CK_ULONG, encrypted_data.as_mut_ptr(), &mut encrypted_data_len, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_encrypt_null_encrypted_data_len() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = Vec::new(); let mut encrypted_data: Vec = Vec::new(); let rv = C_Encrypt( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_data.as_mut_ptr(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_encrypt_null_encrypted_data() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = Vec::new(); let mut encrypted_data_len: CK_ULONG = 0; let rv = C_Encrypt( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, std::ptr::null_mut(), &mut encrypted_data_len, ); assert_eq!(rv, cryptoki_sys::CKR_OK); } #[test] fn test_encrypt_operation_not_initialized() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = Vec::new(); let mut encrypted_data: Vec = Vec::new(); let mut encrypted_data_len: CK_ULONG = 0; let rv = C_Encrypt( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_data.as_mut_ptr(), &mut encrypted_data_len, ); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } #[test] fn test_encrypt_update_null_part() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut encrypted_part_len: CK_ULONG = 0; let mut encrypted_part: Vec = Vec::new(); let rv = C_EncryptUpdate( session_handle, std::ptr::null_mut(), 0 as CK_ULONG, encrypted_part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_encrypt_update_null_encrypted_part_len() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = Vec::new(); let mut encrypted_part: Vec = Vec::new(); let rv = C_EncryptUpdate( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_part.as_mut_ptr(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_encrypt_update_null_encrypted_part() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = Vec::new(); let mut encrypted_part_len: CK_ULONG = 0; let rv = C_EncryptUpdate( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, std::ptr::null_mut(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_OK); } #[test] fn test_encrypt_update_buffer_too_small() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = vec![0; 100]; let mut encrypted_part: Vec = Vec::new(); let mut encrypted_part_len: CK_ULONG = 0; let rv = C_EncryptUpdate( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_BUFFER_TOO_SMALL); } #[test] fn test_encrypt_update_operation_not_initialized() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data: Vec = vec![0; 100]; let mut encrypted_part: Vec = Vec::new(); let mut encrypted_part_len: CK_ULONG = 512; let rv = C_EncryptUpdate( session_handle, data.as_mut_ptr(), data.len() as CK_ULONG, encrypted_part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } #[test] fn test_encrypt_final_null_encrypted_part() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut encrypted_part_len: CK_ULONG = 0; let rv = C_EncryptFinal( session_handle, std::ptr::null_mut(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_OK); } #[test] fn test_encrypt_final_null_encrypted_part_len() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut encrypted_part: Vec = Vec::new(); let rv = C_EncryptFinal( session_handle, encrypted_part.as_mut_ptr(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } // #[test] // fn test_encrypt_final_buffer_too_small() { // init_for_tests(); // let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); // let mut pEncryptedPart: Vec = Vec::new(); // let mut pEncryptedPartLen: CK_ULONG = 0; // let rv = C_EncryptFinal( // session_handle, // pEncryptedPart.as_mut_ptr(), // &mut pEncryptedPartLen, // ); // assert_eq!(rv, cryptoki_sys::CKR_BUFFER_TOO_SMALL); // } #[test] fn test_encrypt_final_operation_not_initialized() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut encrypted_part: Vec = Vec::new(); let mut encrypted_part_len: CK_ULONG = 512; let rv = C_EncryptFinal( session_handle, encrypted_part.as_mut_ptr(), &mut encrypted_part_len, ); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/generation.rs000066400000000000000000000456351507371613300212750ustar00rootroot00000000000000use base64ct::{Base64, Encoding}; use log::{error, trace}; use nethsm_sdk_rs::apis::default_api; use crate::{ api::api_function, backend::{ db::attr::CkRawAttrTemplate, mechanism::{CkRawMechanism, Mechanism}, Pkcs11Error, }, data, }; api_function!( C_GenerateKey = generate_key; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, phKey: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); fn generate_key( session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: cryptoki_sys::CK_ULONG, key_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { // template_ptr and mechanism_ptr are checked for null with `from_raw_ptr` if key_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let mech = unsafe { CkRawMechanism::from_raw_ptr(mechanism_ptr) }.ok_or(Pkcs11Error::ArgumentsBad)?; trace!("C_GenerateKey() mech: {:?}", mech.type_()); trace!("C_GenerateKey() mech param len: {:?}", mech.len()); let mech = Mechanism::from_ckraw_mech(&mech).map_err(|err| { error!("C_GenerateKey() failed to convert mechanism: {err}"); Pkcs11Error::MechanismInvalid })?; let template = unsafe { CkRawAttrTemplate::from_raw_ptr(template_ptr, count as usize) } .ok_or(Pkcs11Error::ArgumentsBad)?; let session = data::get_session(session)?; let session = data::lock_session(&session)?; let key = session .generate_key(&template, None, &mech) .map_err(|err| { error!("C_GenerateKey() failed to generate key: {err:?}"); Pkcs11Error::FunctionFailed })?; if key.is_empty() { error!("C_GenerateKey() failed to generate key,invalid length: {key:?}"); return Err(Pkcs11Error::FunctionFailed); } unsafe { std::ptr::write(key_ptr, key[0].0); } Ok(()) } api_function!( C_GenerateKeyPair = generate_key_pair; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, pPublicKeyTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulPublicKeyAttributeCount: cryptoki_sys::CK_ULONG, pPrivateKeyTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulPrivateKeyAttributeCount: cryptoki_sys::CK_ULONG, phPublicKey: cryptoki_sys::CK_OBJECT_HANDLE_PTR, phPrivateKey: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); #[allow(clippy::too_many_arguments)] fn generate_key_pair( session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, public_key_template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, public_key_attribute_count: cryptoki_sys::CK_ULONG, private_key_template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, private_key_attribute_count: cryptoki_sys::CK_ULONG, public_key_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, private_key_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { // mechamism_ptr, private_key_template_ptr, public_key_template_ptr checked for null with `from_raw_ptr` if public_key_ptr.is_null() || private_key_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let mech = unsafe { CkRawMechanism::from_raw_ptr(mechanism_ptr) }.ok_or(Pkcs11Error::ArgumentsBad)?; trace!("C_GenerateKeyPair() mech: {:?}", mech.type_()); trace!("C_GenerateKeyPair() mech param len: {:?}", mech.len()); trace!("Private count: {private_key_attribute_count:?}"); trace!("Public count: {public_key_attribute_count:?}"); let mech = Mechanism::from_ckraw_mech(&mech).map_err(|err| { error!("C_GenerateKeyPair() failed to convert mechanism: {err}"); Pkcs11Error::MechanismInvalid })?; let public_template = unsafe { CkRawAttrTemplate::from_raw_ptr( public_key_template_ptr, public_key_attribute_count as usize, ) } .ok_or(Pkcs11Error::ArgumentsBad)?; let private_template = unsafe { CkRawAttrTemplate::from_raw_ptr( private_key_template_ptr, private_key_attribute_count as usize, ) } .ok_or(Pkcs11Error::ArgumentsBad)?; let session = data::get_session(session)?; let session = data::lock_session(&session)?; let keys = session .generate_key(&private_template, Some(&public_template), &mech) .map_err(|err| { error!("C_GenerateKeyPair() failed to generate key: {err:?}"); Pkcs11Error::FunctionFailed })?; if keys.len() < 2 { error!("C_GenerateKeyPair() failed to generate key,invalid length: {keys:?}"); return Err(Pkcs11Error::FunctionFailed); } unsafe { std::ptr::write(public_key_ptr, keys[0].0); std::ptr::write(private_key_ptr, keys[1].0); } Ok(()) } api_function!( C_WrapKey = wrap_key; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hWrappingKey: cryptoki_sys::CK_OBJECT_HANDLE, hKey: cryptoki_sys::CK_OBJECT_HANDLE, pWrappedKey: cryptoki_sys::CK_BYTE_PTR, pulWrappedKeyLen: cryptoki_sys::CK_ULONG_PTR, ); fn wrap_key( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _wrapping_key: cryptoki_sys::CK_OBJECT_HANDLE, _key: cryptoki_sys::CK_OBJECT_HANDLE, _wrapped_key_ptr: cryptoki_sys::CK_BYTE_PTR, _wrapped_key_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_UnwrapKey = unwrap_key; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hUnwrappingKey: cryptoki_sys::CK_OBJECT_HANDLE, pWrappedKey: cryptoki_sys::CK_BYTE_PTR, ulWrappedKeyLen: cryptoki_sys::CK_ULONG, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulAttributeCount: cryptoki_sys::CK_ULONG, phKey: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); #[allow(clippy::too_many_arguments)] fn unwrap_key( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _unwrapping_key: cryptoki_sys::CK_OBJECT_HANDLE, _wrapped_key_ptr: cryptoki_sys::CK_BYTE_PTR, _wrapped_key_len: cryptoki_sys::CK_ULONG, _template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, _attribute_count: cryptoki_sys::CK_ULONG, _key_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_DeriveKey = derive_key; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hBaseKey: cryptoki_sys::CK_OBJECT_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulAttributeCount: cryptoki_sys::CK_ULONG, phKey: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); fn derive_key( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _base_key: cryptoki_sys::CK_OBJECT_HANDLE, _template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, _attribute_count: cryptoki_sys::CK_ULONG, _key_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_SeedRandom = seed_random; hSession: cryptoki_sys::CK_SESSION_HANDLE, pSeed: cryptoki_sys::CK_BYTE_PTR, ulSeedLen: cryptoki_sys::CK_ULONG, ); // we silently ignore this function as NetHSM handles the random number generation fn seed_random( _session: cryptoki_sys::CK_SESSION_HANDLE, _seed_ptr: cryptoki_sys::CK_BYTE_PTR, _seed_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { Ok(()) } api_function!( C_GenerateRandom = generate_random; hSession: cryptoki_sys::CK_SESSION_HANDLE, pRandomData: cryptoki_sys::CK_BYTE_PTR, ulRandomLen: cryptoki_sys::CK_ULONG, ); fn generate_random( session: cryptoki_sys::CK_SESSION_HANDLE, random_data_ptr: cryptoki_sys::CK_BYTE_PTR, random_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if random_data_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } if random_len == 0 { return Ok(()); } if random_len > 1024 { error!( "C_GenerateRandom() called with invalid length {random_len}, NetHSM supports up to 1024 bytes" ); return Err(Pkcs11Error::ArgumentsBad); } let session = data::get_session(session)?; let session = data::lock_session(&session)?; if !session .login_ctx .can_run_mode(crate::backend::login::UserMode::Operator) { error!("C_GenerateRandom() called with session not connected as operator."); return Err(Pkcs11Error::UserNotLoggedIn); } let data = session .login_ctx .try_( |api_config| { default_api::random_post( api_config, nethsm_sdk_rs::models::RandomRequestData::new(random_len as i32), ) }, crate::backend::login::UserMode::Operator, ) .map_err(|err| { error!("C_GenerateRandom() failed to generate random data: {err:?}"); Pkcs11Error::FunctionFailed })?; // parse base64 string to bytes let raw_data = Base64::decode_vec(&data.entity.random).map_err(|err| { error!("C_GenerateRandom() failed to decode random data: {err:?}"); Pkcs11Error::FunctionFailed })?; unsafe { std::ptr::copy_nonoverlapping(raw_data.as_ptr(), random_data_ptr, random_len as usize); } Ok(()) } #[cfg(test)] mod tests { use crate::backend::slot::init_for_tests; use super::*; #[test] fn test_generate_key_null_mech() { let _guard = init_for_tests(); let mut template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut key = 0; let rv = C_GenerateKey(0, std::ptr::null_mut(), template.as_mut_ptr(), 0, &mut key); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_null_template() { let _guard = init_for_tests(); let mut key = 0; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_GenerateKey(0, &mut mech, std::ptr::null_mut(), 0, &mut key); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_null_phkey() { let _guard = init_for_tests(); let mut template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_GenerateKey(0, &mut mech, template.as_mut_ptr(), 0, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_unknown_mech() { let _guard = init_for_tests(); let mut template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut key = 0; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: 15000, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_GenerateKey(0, &mut mech, template.as_mut_ptr(), 0, &mut key); assert_eq!(rv, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_generate_key_pair_null_mech() { let _guard = init_for_tests(); let mut template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut public_key = 0; let mut private_key = 0; let rv = C_GenerateKeyPair( 0, std::ptr::null_mut(), template.as_mut_ptr(), 0, template.as_mut_ptr(), 0, &mut public_key, &mut private_key, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_pair_null_public_template() { let _guard = init_for_tests(); let mut public_key = 0; let mut private_key = 0; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let mut private_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let rv = C_GenerateKeyPair( 0, &mut mech, std::ptr::null_mut(), 0, private_template.as_mut_ptr(), 0, &mut public_key, &mut private_key, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_pair_null_private_template() { let _guard = init_for_tests(); let mut public_key = 0; let mut private_key = 0; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let mut public_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let rv = C_GenerateKeyPair( 0, &mut mech, public_template.as_mut_ptr(), 0, std::ptr::null_mut(), 0, &mut public_key, &mut private_key, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_pair_null_ph_public_key() { let _guard = init_for_tests(); let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let mut public_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut private_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let rv = C_GenerateKeyPair( 0, &mut mech, public_template.as_mut_ptr(), 0, private_template.as_mut_ptr(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_pair_null_ph_private_key() { let _guard = init_for_tests(); let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let mut public_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut private_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let rv = C_GenerateKeyPair( 0, &mut mech, public_template.as_mut_ptr(), 0, private_template.as_mut_ptr(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_key_pair_unknown_mech() { let _guard = init_for_tests(); let mut public_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut private_template = vec![cryptoki_sys::CK_ATTRIBUTE { type_: 0, pValue: std::ptr::null_mut(), ulValueLen: 0, }]; let mut public_key = 0; let mut private_key = 0; let mut mech = cryptoki_sys::CK_MECHANISM { mechanism: 15000, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_GenerateKeyPair( 0, &mut mech, public_template.as_mut_ptr(), 0, private_template.as_mut_ptr(), 0, &mut public_key, &mut private_key, ); assert_eq!(rv, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_generate_random_null_data() { let _guard = init_for_tests(); let rv = C_GenerateRandom(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_random_invalid_length() { let _guard = init_for_tests(); let mut random_data = vec![0; 1500]; let rv = C_GenerateRandom(0, random_data.as_mut_ptr(), 1500); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_generate_random_zero_length() { let _guard = init_for_tests(); let mut random_data = vec![0; 1500]; let rv = C_GenerateRandom(0, random_data.as_mut_ptr(), 0); assert_eq!(rv, cryptoki_sys::CKR_OK); } #[test] fn test_wrap_key() { let _guard = init_for_tests(); let rv = C_WrapKey( 0, std::ptr::null_mut(), 0, 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_unwrap_key() { let _guard = init_for_tests(); let rv = C_UnwrapKey( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_derive_key() { let _guard = init_for_tests(); let rv = C_DeriveKey( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_seed_random() { let _guard = init_for_tests(); let rv = C_SeedRandom(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_OK); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/mod.rs000066400000000000000000000124371507371613300177130ustar00rootroot00000000000000pub mod decrypt; pub mod digest; pub mod encrypt; pub mod generation; pub mod object; pub mod pin; pub mod session; pub mod sign; pub mod token; pub mod verify; use std::sync::atomic::Ordering; use std::{ptr::addr_of_mut, sync::Arc}; use crate::config::device::{start_background_timer, stop_background_timer}; use crate::{ backend::{ events::{fetch_slots_state, EventsManager}, Pkcs11Error, }, data::{self, DEVICE, EVENTS_MANAGER, THREADS_ALLOWED, TOKENS_STATE}, defs, utils::padded_str, }; use cryptoki_sys::{CK_INFO, CK_INFO_PTR, CK_VOID_PTR}; use log::{debug, error, trace}; macro_rules! api_function { ( $c_fn:ident = $rust_fn:ident; $( $arg:ident: $ty:ty, )+ $(,)? ) => { #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn $c_fn($($arg: $ty,)*) -> ::cryptoki_sys::CK_RV { ::log::trace!("{} called", stringify!($c_fn)); let result = $rust_fn($($arg,)*); match result { Ok(()) => { ::log::trace!("{} was successful", stringify!($c_fn)); ::cryptoki_sys::CKR_OK } Err(err) => { ::log::warn!("{} failed with error {err:?}", stringify!($c_fn)); ::std::convert::From::from(err) } } } } } use api_function; api_function!( C_GetFunctionList = get_function_list; ppFunctionList: *mut *mut cryptoki_sys::CK_FUNCTION_LIST, ); fn get_function_list( fn_list_ptr: *mut *mut cryptoki_sys::CK_FUNCTION_LIST, ) -> Result<(), Pkcs11Error> { if fn_list_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } unsafe { std::ptr::write(fn_list_ptr, addr_of_mut!(data::FN_LIST)); } Ok(()) } api_function!( C_Initialize = initialize; pInitArgs: CK_VOID_PTR, ); fn initialize(init_args_ptr: CK_VOID_PTR) -> Result<(), Pkcs11Error> { let device = crate::config::initialization::initialize().map_err(|err| { error!("NetHSM PKCS#11: Failed to initialize configuration: {err}"); Pkcs11Error::FunctionFailed })?; let device = Arc::new(device); DEVICE.store(Some(device.clone())); // we force the initialization of the lazy static here if device.slots.is_empty() { debug!("No slots configured"); } if defs::CRYPTOKI_VERSION.major == 2 && defs::CRYPTOKI_VERSION.minor == 40 && !init_args_ptr.is_null() { let args = init_args_ptr as cryptoki_sys::CK_C_INITIALIZE_ARGS_PTR; let args = unsafe { std::ptr::read(args) }; // for cryptoki 2.40 this should always be null if !(args).pReserved.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let flags = args.flags; let create_mutex = args.CreateMutex; trace!("C_Initialize() called with flags: {flags:?}"); trace!("C_Initialize() called with CreateMutex: {create_mutex:?}"); // currently we don't support custom locking // if the flag is not set and the mutex functions are not null, the program asks us to use only the mutex functions, we can't do that if flags & cryptoki_sys::CKF_OS_LOCKING_OK == 0 && create_mutex.is_some() { return Err(Pkcs11Error::CantLock); } if flags & cryptoki_sys::CKF_LIBRARY_CANT_CREATE_OS_THREADS != 0 { THREADS_ALLOWED.store(false, Ordering::Relaxed); } else { THREADS_ALLOWED.store(true, Ordering::Relaxed); start_background_timer(); } } // Initialize the events manager *EVENTS_MANAGER.write().unwrap() = EventsManager::new(); *TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new(); fetch_slots_state() } api_function!( C_Finalize = finalize; pReserved: CK_VOID_PTR, ); fn finalize(reserved_ptr: CK_VOID_PTR) -> Result<(), Pkcs11Error> { if !reserved_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } DEVICE.store(None); stop_background_timer(); EVENTS_MANAGER.write().unwrap().finalized = true; Ok(()) } api_function!( C_GetInfo = get_info; pInfo: CK_INFO_PTR, ); fn get_info(info_ptr: CK_INFO_PTR) -> Result<(), Pkcs11Error> { if info_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let infos = CK_INFO { cryptokiVersion: defs::CRYPTOKI_VERSION, manufacturerID: padded_str(defs::LIB_MANUFACTURER), flags: 0, libraryDescription: padded_str(defs::LIB_DESCRIPTION), libraryVersion: defs::LIB_VERSION, }; unsafe { std::ptr::write(info_ptr, infos); } Ok(()) } #[cfg(test)] mod test { use super::*; #[test] fn test_get_function_list() { let mut fn_list: *mut cryptoki_sys::CK_FUNCTION_LIST = std::ptr::null_mut(); let rv = C_GetFunctionList(&mut fn_list); assert_eq!(rv, cryptoki_sys::CKR_OK); assert!(!fn_list.is_null()); } #[test] fn test_get_function_list_null_ptr() { let rv = C_GetFunctionList(std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_info_null_ptr() { let rv = C_GetInfo(std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/object.rs000066400000000000000000000404201507371613300203730ustar00rootroot00000000000000use std::slice; use cryptoki_sys::CK_ULONG; use log::{error, info, trace}; use crate::{ api::api_function, backend::{db::attr::CkRawAttrTemplate, key::Id, Pkcs11Error}, data, }; api_function!( C_FindObjectsInit = find_objects_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, ); fn find_objects_init( session: cryptoki_sys::CK_SESSION_HANDLE, template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if count > 0 && template_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let template = unsafe { CkRawAttrTemplate::from_raw_ptr(template_ptr, count as usize) }; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; trace!("C_FindObjectsInit() template: {template:?}"); session.enum_init(template).map_err(From::from) } api_function!( C_FindObjects = find_objects; hSession: cryptoki_sys::CK_SESSION_HANDLE, phObject: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ulMaxObjectCount: cryptoki_sys::CK_ULONG, pulObjectCount: cryptoki_sys::CK_ULONG_PTR, ); fn find_objects( session: cryptoki_sys::CK_SESSION_HANDLE, object_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, max_object_count: cryptoki_sys::CK_ULONG, object_count_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if object_ptr.is_null() || object_count_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; trace!("C_FindObjects() ulMaxObjectCount: {max_object_count}"); let objects = session.enum_next_chunk(max_object_count as usize)?; trace!("C_FindObjects() objects: {objects:?}"); let returned_count = objects.len(); unsafe { std::ptr::copy_nonoverlapping(objects.as_ptr(), object_ptr, returned_count); std::ptr::write(object_count_ptr, returned_count as CK_ULONG); } Ok(()) } api_function!( C_FindObjectsFinal = find_objects_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, ); fn find_objects_final(session: cryptoki_sys::CK_SESSION_HANDLE) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.enum_final(); Ok(()) } api_function!( C_GetAttributeValue = get_attribute_value; hSession: cryptoki_sys::CK_SESSION_HANDLE, hObject: cryptoki_sys::CK_OBJECT_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, ); fn get_attribute_value( session: cryptoki_sys::CK_SESSION_HANDLE, object: cryptoki_sys::CK_OBJECT_HANDLE, template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if template_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let session = data::get_session(session)?; let session = data::lock_session(&session)?; let object = session.get_object(object).ok_or_else(|| { error!("C_GetAttributeValue() called with invalid object handle {object}."); Pkcs11Error::ObjectHandleInvalid })?; trace!( "C_GetAttributeValue() object id : {} {:?}", object.id, object.kind ); let mut template = unsafe { CkRawAttrTemplate::from_raw_ptr(template_ptr, count as usize) } .ok_or(Pkcs11Error::ArgumentsBad)?; object.fill_attr_template(&mut template) } api_function!( C_GetObjectSize = get_object_size; hSession: cryptoki_sys::CK_SESSION_HANDLE, hObject: cryptoki_sys::CK_OBJECT_HANDLE, pulSize: cryptoki_sys::CK_ULONG_PTR, ); fn get_object_size( session: cryptoki_sys::CK_SESSION_HANDLE, object: cryptoki_sys::CK_OBJECT_HANDLE, size_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if size_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let session = data::get_session(session)?; let session = data::lock_session(&session)?; let object = session.get_object(object).ok_or_else(|| { error!("function called with invalid object handle {object}."); Pkcs11Error::ObjectHandleInvalid })?; unsafe { std::ptr::write(size_ptr, object.size.unwrap_or(0) as CK_ULONG); } Ok(()) } api_function!( C_CreateObject = create_object; hSession: cryptoki_sys::CK_SESSION_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, phObject: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); fn create_object( session: cryptoki_sys::CK_SESSION_HANDLE, template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: cryptoki_sys::CK_ULONG, object_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { // template_ptr checked with from_raw_ptr if object_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let template = unsafe { CkRawAttrTemplate::from_raw_ptr(template_ptr, count as usize) } .ok_or(Pkcs11Error::ArgumentsBad)?; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let objects = session.create_object(template)?; if objects.is_empty() { error!("C_CreateObject() failed: no object created"); return Err(Pkcs11Error::GeneralError); } unsafe { std::ptr::write(object_ptr, objects[0].0); } Ok(()) } api_function!( C_CopyObject = copy_object; hSession: cryptoki_sys::CK_SESSION_HANDLE, hObject: cryptoki_sys::CK_OBJECT_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, phNewObject: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ); fn copy_object( _session: cryptoki_sys::CK_SESSION_HANDLE, _object: cryptoki_sys::CK_OBJECT_HANDLE, _template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, _count: cryptoki_sys::CK_ULONG, _new_object_ptr: cryptoki_sys::CK_OBJECT_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::ActionProhibited) } api_function!( C_DestroyObject = destroy_object; hSession: cryptoki_sys::CK_SESSION_HANDLE, hObject: cryptoki_sys::CK_OBJECT_HANDLE, ); fn destroy_object( session: cryptoki_sys::CK_SESSION_HANDLE, object: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.delete_object(object).map_err(From::from) } api_function!( C_SetAttributeValue = set_attribute_value; hSession: cryptoki_sys::CK_SESSION_HANDLE, hObject: cryptoki_sys::CK_OBJECT_HANDLE, pTemplate: cryptoki_sys::CK_ATTRIBUTE_PTR, ulCount: cryptoki_sys::CK_ULONG, ); fn set_attribute_value( session: cryptoki_sys::CK_SESSION_HANDLE, object: cryptoki_sys::CK_OBJECT_HANDLE, template_ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let session = data::lock_session(&session)?; if template_ptr.is_null() || count == 0 { error!("C_SetAttributeValue called without attributes in the template"); return Err(Pkcs11Error::ArgumentsBad); } let object = session.get_object(object).ok_or_else(|| { error!("C_SetAttributeValue() called with invalid object handle {object}."); Pkcs11Error::ObjectHandleInvalid })?; let n = usize::try_from(count).map_err(|_| { error!("C_SetAttributeValue called with too many attributes in the template"); Pkcs11Error::ArgumentsBad })?; // SAFETY: The caller must ensure that pTemplate points to an array of ulCount attributes. // We already checked for null pointers and length zero above. let attrs = unsafe { slice::from_raw_parts(template_ptr, n) }; let mut id = None; for attr in attrs { if attr.type_ != cryptoki_sys::CKA_ID { error!("C_SetAttributeValue() is supported only on CKA_ID"); return Err(Pkcs11Error::AttributeReadOnly); } if id.is_some() { error!("CKA_ID cannot be set twice in C_SetAttributeValue"); return Err(Pkcs11Error::TemplateInconsistent); } if attr.ulValueLen == 0 || attr.ulValueLen == cryptoki_sys::CK_UNAVAILABLE_INFORMATION || attr.pValue.is_null() { error!("CKA_ID value may not be empty in C_SetAttributeValue"); return Err(Pkcs11Error::AttributeValueInvalid); } let Ok(n) = usize::try_from(attr.ulValueLen) else { error!("CKA_ID value is too long in C_SetAttributeValue"); return Err(Pkcs11Error::AttributeValueInvalid); }; // SAFETY: The caller must provide a byte slice of the correct size for CKA_ID attributes. // We already checked for null pointers and length zero above. id = Some(unsafe { slice::from_raw_parts(attr.pValue as *const u8, n) }); } // We already checked before that there is at least one attribute. As CKA_ID is the only // attribute we support, id cannot be None. let id = id.ok_or(Pkcs11Error::ArgumentsBad)?; let id = Id::try_from(id.to_owned()).map_err(|_| { error!("CKA_ID value is not a valid NetHSM ID: {id:?}"); Pkcs11Error::AttributeValueInvalid })?; info!("Changing ID to: {}", id.as_ref()); session .rename_objects(&object.id, id.as_ref()) .map_err(From::from) } #[cfg(test)] mod tests { use std::sync::{Arc, Condvar, Mutex}; use crate::{ backend::{ db::{Db, Object}, login::LoginCtx, session::Session, slot::{get_slot, init_for_tests}, }, data::SESSION_MANAGER, }; use super::*; #[test] fn test_find_objects_init_bad_arguments() { let _guard = init_for_tests(); let rv = C_FindObjectsInit(0, std::ptr::null_mut(), 1); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_find_objects_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut object: cryptoki_sys::CK_OBJECT_HANDLE = 0; let mut object_count: cryptoki_sys::CK_ULONG = 0; let rv = C_FindObjects(0, &mut object, 1, &mut object_count); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_find_objects_null_object() { let _guard = init_for_tests(); let mut object_count: cryptoki_sys::CK_ULONG = 0; let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_FindObjects(session, std::ptr::null_mut(), 1, &mut object_count); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_find_objects_null_object_count() { let _guard = init_for_tests(); let mut object: cryptoki_sys::CK_OBJECT_HANDLE = 0; let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_FindObjects(session, &mut object, 1, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_find_objects_final_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let rv = C_FindObjectsFinal(0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_get_attribute_value_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut template = vec![]; let rv = C_GetAttributeValue(0, 0, template.as_mut_ptr(), 0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_get_attribute_value_null_template() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_GetAttributeValue(session, 0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_attribute_value_invalid_object() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut template = vec![]; let rv = C_GetAttributeValue(session, 0, template.as_mut_ptr(), 0); assert_eq!(rv, cryptoki_sys::CKR_OBJECT_HANDLE_INVALID); } #[test] fn test_get_object_size_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut size: cryptoki_sys::CK_ULONG = 0; let rv = C_GetObjectSize(0, 0, &mut size); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_get_object_size_null_size() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_GetObjectSize(session, 0, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_object_size_invalid_object() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut size: cryptoki_sys::CK_ULONG = 0; let rv = C_GetObjectSize(session, 0, &mut size); assert_eq!(rv, cryptoki_sys::CKR_OBJECT_HANDLE_INVALID); } #[test] fn test_get_object_size() { let _guard = init_for_tests(); let slot = get_slot(0).unwrap(); let size = 32; let mut db = Db::new(); let mut object = Object::default(); object.size = Some(size); let (object_handle, _) = db.add_object(object); let session_handle = 1; let session = Session { db: Arc::new((Mutex::new(db), Condvar::new())), decrypt_ctx: None, encrypt_ctx: None, sign_ctx: None, device_error: None, enum_ctx: None, flags: 0, login_ctx: LoginCtx::new(slot, false, false), slot_id: 0, }; SESSION_MANAGER .lock() .unwrap() .set_session(session_handle, session); let mut object_size: cryptoki_sys::CK_ULONG = 0; let rv = C_GetObjectSize(session_handle, object_handle, &mut object_size); assert_eq!(rv, cryptoki_sys::CKR_OK); assert_eq!(object_size, size as CK_ULONG); } #[test] fn test_create_object_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut template = vec![]; let mut object: cryptoki_sys::CK_OBJECT_HANDLE = 0; let rv = C_CreateObject(0, template.as_mut_ptr(), 0, &mut object); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_create_object_null_object() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut template = vec![]; let rv = C_CreateObject(session, template.as_mut_ptr(), 0, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_create_object_null_template() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut object: cryptoki_sys::CK_OBJECT_HANDLE = 0; let rv = C_CreateObject(session, std::ptr::null_mut(), 0, &mut object); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_destroy_object_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let rv = C_DestroyObject(0, 0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_set_attribute_null_template() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_SetAttributeValue(session, 0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_copy_object() { let _guard = init_for_tests(); let rv = C_CopyObject(0, 0, std::ptr::null_mut(), 0, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ACTION_PROHIBITED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/pin.rs000066400000000000000000000127141507371613300177200ustar00rootroot00000000000000use log::error; use crate::{api::api_function, backend::Pkcs11Error, data}; api_function!( C_InitPIN = init_pin; hSession: cryptoki_sys::CK_SESSION_HANDLE, uPin: cryptoki_sys::CK_UTF8CHAR_PTR, ulPinLen: cryptoki_sys::CK_ULONG, ); fn init_pin( _session: cryptoki_sys::CK_SESSION_HANDLE, _pin_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, _pin_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_SetPIN = set_pin; hSession: cryptoki_sys::CK_SESSION_HANDLE, pOldPin: cryptoki_sys::CK_UTF8CHAR_PTR, ulOldLen: cryptoki_sys::CK_ULONG, pNewPin: cryptoki_sys::CK_UTF8CHAR_PTR, ulNewLen: cryptoki_sys::CK_ULONG, ); fn set_pin( session: cryptoki_sys::CK_SESSION_HANDLE, old_pin_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, old_len: cryptoki_sys::CK_ULONG, new_pin_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, new_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; if old_pin_ptr.is_null() || new_pin_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let old_pin = unsafe { std::slice::from_raw_parts(old_pin_ptr, old_len as usize) }; let new_pin = unsafe { std::slice::from_raw_parts(new_pin_ptr, new_len as usize) }; // parse string to utf8 let old_pin = std::str::from_utf8(old_pin).map_err(|_| Pkcs11Error::ArgumentsBad)?; let new_pin = std::str::from_utf8(new_pin).map_err(|_| Pkcs11Error::ArgumentsBad)?; session.login(cryptoki_sys::CKU_USER, old_pin.to_string())?; if !session .login_ctx .can_run_mode(crate::backend::login::UserMode::OperatorOrAdministrator) { error!("C_SetPIN() called with session not connected as operator."); return Err(Pkcs11Error::UserNotLoggedIn); } session.login_ctx.change_pin(new_pin.to_string()) } #[cfg(test)] mod tests { use cryptoki_sys::CK_ULONG; use crate::{backend::slot::init_for_tests, data::SESSION_MANAGER}; use super::*; #[test] fn test_init_pin() { let _guard = init_for_tests(); let rv = C_InitPIN(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_set_pin_null_old_pin() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let new_pin = "12345678"; let rv = C_SetPIN( session_handle, std::ptr::null_mut(), 0, new_pin.as_ptr() as *mut u8, new_pin.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_set_pin_null_new_pin() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let old_pin = "12345678"; let rv = C_SetPIN( session_handle, old_pin.as_ptr() as *mut u8, old_pin.len() as CK_ULONG, std::ptr::null_mut(), 0, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_set_pin_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let old_pin = "12345678"; let new_pin = "12345678"; let rv = C_SetPIN( 0, old_pin.as_ptr() as *mut u8, old_pin.len() as CK_ULONG, new_pin.as_ptr() as *mut u8, new_pin.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_set_pin_no_utf8_old_pin() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); // random bytes let old_pin = [ 0xC0, 0xC1, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, ]; let new_pin = "12345678"; let rv = C_SetPIN( session_handle, old_pin.as_ptr() as *mut u8, old_pin.len() as CK_ULONG, new_pin.as_ptr() as *mut u8, new_pin.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_set_pin_no_utf8_new_pin() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let old_pin = "12345678"; // random bytes let new_pin = [ 0xC0, 0xC1, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, ]; let rv = C_SetPIN( session_handle, old_pin.as_ptr() as *mut u8, old_pin.len() as CK_ULONG, new_pin.as_ptr() as *mut u8, new_pin.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_set_pin_no_user() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let old_pin = "12345678"; let new_pin = "12345678"; let rv = C_SetPIN( session_handle, old_pin.as_ptr() as *mut u8, old_pin.len() as CK_ULONG, new_pin.as_ptr() as *mut u8, new_pin.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_USER_TYPE_INVALID); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/session.rs000066400000000000000000000173041507371613300206150ustar00rootroot00000000000000use log::{error, trace}; use crate::{ api::api_function, backend::{slot::get_slot, Pkcs11Error}, data::{self, SESSION_MANAGER}, }; api_function!( C_OpenSession = open_session; slotID: cryptoki_sys::CK_SLOT_ID, flags: cryptoki_sys::CK_FLAGS, pApplication: cryptoki_sys::CK_VOID_PTR, Notify: cryptoki_sys::CK_NOTIFY, phSession: cryptoki_sys::CK_SESSION_HANDLE_PTR, ); fn open_session( slot_id: cryptoki_sys::CK_SLOT_ID, flags: cryptoki_sys::CK_FLAGS, _application_ptr: cryptoki_sys::CK_VOID_PTR, _notify: cryptoki_sys::CK_NOTIFY, session_ptr: cryptoki_sys::CK_SESSION_HANDLE_PTR, ) -> Result<(), Pkcs11Error> { trace!("C_OpenSession() called with slotID {slot_id}, flags {flags}"); if session_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } // Serial should always be set if flags & cryptoki_sys::CKF_SERIAL_SESSION == 0 { return Err(Pkcs11Error::SessionParallelNotSupported); } let slot = get_slot(slot_id).map_err(|_| { error!("C_OpenSession() called with invalid slotID {slot_id}."); Pkcs11Error::SlotIdInvalid })?; // create the session in memory let mut manager = SESSION_MANAGER.lock().unwrap(); let session = manager.create_session(slot_id, slot, flags); trace!("C_OpenSession() created session: {session:?}"); unsafe { std::ptr::write(session_ptr, session); } Ok(()) } api_function!( C_CloseSession = close_session; hSession: cryptoki_sys::CK_SESSION_HANDLE, ); fn close_session(session: cryptoki_sys::CK_SESSION_HANDLE) -> Result<(), Pkcs11Error> { trace!("C_CloseSession() called with session handle {session}."); let mut manager = SESSION_MANAGER.lock().unwrap(); let result = manager.delete_session(session); if result.is_none() { error!("C_CloseSession() called with invalid session handle {session}."); return Err(Pkcs11Error::SessionHandleInvalid); } Ok(()) } api_function!( C_CloseAllSessions = close_all_sessions; slotID: cryptoki_sys::CK_SLOT_ID, ); fn close_all_sessions(slot_id: cryptoki_sys::CK_SLOT_ID) -> Result<(), Pkcs11Error> { if get_slot(slot_id).is_err() { error!("C_CloseAllSessions() called with invalid slotID {slot_id}."); return Err(Pkcs11Error::SlotIdInvalid); } let mut manager = SESSION_MANAGER.lock().unwrap(); manager.delete_all_slot_sessions(slot_id); Ok(()) } api_function!( C_GetSessionInfo = get_session_info; hSession: cryptoki_sys::CK_SESSION_HANDLE, pInfo: cryptoki_sys::CK_SESSION_INFO_PTR, ); fn get_session_info( session: cryptoki_sys::CK_SESSION_HANDLE, info_ptr: cryptoki_sys::CK_SESSION_INFO_PTR, ) -> Result<(), Pkcs11Error> { if info_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let session = data::get_session(session)?; let session = data::lock_session(&session)?; unsafe { std::ptr::write(info_ptr, session.get_ck_info()); } Ok(()) } api_function!( C_GetOperationState = get_operation_state; hSession: cryptoki_sys::CK_SESSION_HANDLE, pOperationState: cryptoki_sys::CK_BYTE_PTR, pulOperationStateLen: cryptoki_sys::CK_ULONG_PTR, ); fn get_operation_state( _session: cryptoki_sys::CK_SESSION_HANDLE, _operation_state_ptr: cryptoki_sys::CK_BYTE_PTR, _operation_state_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_SetOperationState = set_operation_state; hSession: cryptoki_sys::CK_SESSION_HANDLE, pOperationState: cryptoki_sys::CK_BYTE_PTR, ulOperationStateLen: cryptoki_sys::CK_ULONG, hEncryptionKey: cryptoki_sys::CK_OBJECT_HANDLE, hAuthenticationKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn set_operation_state( _session: cryptoki_sys::CK_SESSION_HANDLE, _operation_state_ptr: cryptoki_sys::CK_BYTE_PTR, _operation_state_len: cryptoki_sys::CK_ULONG, _encryption_key: cryptoki_sys::CK_OBJECT_HANDLE, _authentication_key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_GetFunctionStatus = get_function_status; hSession: cryptoki_sys::CK_SESSION_HANDLE, ); fn get_function_status(_session: cryptoki_sys::CK_SESSION_HANDLE) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotParallel) } api_function!( C_CancelFunction = cancel_function; hSession: cryptoki_sys::CK_SESSION_HANDLE, ); fn cancel_function(_session: cryptoki_sys::CK_SESSION_HANDLE) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotParallel) } #[cfg(test)] mod tests { use crate::backend::slot::init_for_tests; use super::*; #[test] fn test_open_session_null_session() { let _guard = init_for_tests(); let rv = C_OpenSession( 0, cryptoki_sys::CKF_SERIAL_SESSION | cryptoki_sys::CKF_RW_SESSION, std::ptr::null_mut(), None, std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_open_session_parallel() { let _guard = init_for_tests(); let mut session = 0; let rv = C_OpenSession(0, 0, std::ptr::null_mut(), None, &mut session); assert_eq!(rv, cryptoki_sys::CKR_SESSION_PARALLEL_NOT_SUPPORTED); } #[test] fn test_delete_session_invalid() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let rv = C_CloseSession(0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_close_all_sessions_invalid_slot() { let _guard = init_for_tests(); let rv = C_CloseAllSessions(99); assert_eq!(rv, cryptoki_sys::CKR_SLOT_ID_INVALID); } #[test] fn test_close_all_sessions() { let _guard = init_for_tests(); let slot = get_slot(0).unwrap(); let handle = SESSION_MANAGER.lock().unwrap().create_session(0, slot, 0); let rv = C_CloseAllSessions(0); assert_eq!(rv, cryptoki_sys::CKR_OK); assert!(SESSION_MANAGER .lock() .unwrap() .get_session(handle) .is_none()); } #[test] fn test_get_session_info_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut info = cryptoki_sys::CK_SESSION_INFO::default(); let rv = C_GetSessionInfo(0, &mut info); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_get_session_info_null_info() { let _guard = init_for_tests(); let session_handle = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_GetSessionInfo(session_handle, std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_operation_state() { let _guard = init_for_tests(); let rv = C_GetOperationState(0, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_set_operation_state() { let _guard = init_for_tests(); let rv = C_SetOperationState(0, std::ptr::null_mut(), 0, 0, 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_get_function_status() { let _guard = init_for_tests(); let rv = C_GetFunctionStatus(0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_PARALLEL); } #[test] fn test_cancel_function() { let _guard = init_for_tests(); let rv = C_CancelFunction(0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_PARALLEL); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/sign.rs000066400000000000000000000365571507371613300201050ustar00rootroot00000000000000use cryptoki_sys::CK_ULONG; use log::{error, trace}; use crate::{ api::api_function, backend::{ mechanism::{CkRawMechanism, Mechanism}, session::Session, Pkcs11Error, }, data, }; api_function!( C_SignInit = sign_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: *mut cryptoki_sys::CK_MECHANISM, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn sign_init( session: cryptoki_sys::CK_SESSION_HANDLE, mechanism_ptr: *mut cryptoki_sys::CK_MECHANISM, key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { trace!("C_SignInit() called with hKey {key} and session {session}"); let raw_mech = unsafe { CkRawMechanism::from_raw_ptr(mechanism_ptr) }.ok_or(Pkcs11Error::ArgumentsBad)?; let mech = Mechanism::from_ckraw_mech(&raw_mech).map_err(|e| { error!("C_SignInit() failed to convert mechanism: {e}"); Pkcs11Error::MechanismInvalid })?; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.sign_init(&mech, key).map_err(From::from) } api_function!( C_Sign = sign; hSession: cryptoki_sys::CK_SESSION_HANDLE, pData: *mut cryptoki_sys::CK_BYTE, ulDataLen: cryptoki_sys::CK_ULONG, pSignature: *mut cryptoki_sys::CK_BYTE, pulSignatureLen: *mut cryptoki_sys::CK_ULONG, ); fn sign( session: cryptoki_sys::CK_SESSION_HANDLE, data_ptr: *mut cryptoki_sys::CK_BYTE, data_len: cryptoki_sys::CK_ULONG, signature_ptr: *mut cryptoki_sys::CK_BYTE, signature_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = sign_impl( &mut session, data_ptr, data_len, signature_ptr, signature_len_ptr, ); // A call to C_Sign always terminates the active signing operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the signature. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && signature_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.sign_clear(); } result } fn sign_impl( session: &mut Session, data_ptr: *mut cryptoki_sys::CK_BYTE, data_len: cryptoki_sys::CK_ULONG, signature_ptr: *mut cryptoki_sys::CK_BYTE, signature_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { trace!("pData null {}", data_ptr.is_null()); trace!("pulSignatureLen null {}", signature_len_ptr.is_null()); if data_ptr.is_null() || signature_len_ptr.is_null() { trace!("aborting sign due to null"); return Err(Pkcs11Error::ArgumentsBad); } let data = unsafe { std::slice::from_raw_parts(data_ptr, data_len as usize) }; let buffer_size = unsafe { *signature_len_ptr } as usize; let theoretical_size = session.sign_theoretical_size()?; unsafe { std::ptr::write(signature_len_ptr, theoretical_size as CK_ULONG); } if signature_ptr.is_null() { trace!("sending only the size"); // only the size was requested return Ok(()); } if buffer_size < theoretical_size { trace!("buffer too small"); return Err(Pkcs11Error::BufferTooSmall); } let signature = session.sign(data)?; unsafe { std::ptr::write(signature_len_ptr, signature.len() as CK_ULONG); } // double check the buffer size if signature.len() > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping(signature.as_ptr(), signature_ptr, signature.len()); } Ok(()) } api_function!( C_SignUpdate = sign_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: *mut cryptoki_sys::CK_BYTE, ulPartLen: cryptoki_sys::CK_ULONG, ); fn sign_update( session: cryptoki_sys::CK_SESSION_HANDLE, part_ptr: *mut cryptoki_sys::CK_BYTE, part_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = sign_update_impl(&mut session, part_ptr, part_len); // A call to C_SignUpdate which results in an error terminates the current signature operation. if result.is_err() { session.sign_clear(); } result } fn sign_update_impl( session: &mut Session, part_ptr: *mut cryptoki_sys::CK_BYTE, part_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if part_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let part = unsafe { std::slice::from_raw_parts(part_ptr, part_len as usize) }; session.sign_update(part).map_err(From::from) } api_function!( C_SignFinal = sign_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, pSignature: *mut cryptoki_sys::CK_BYTE, pulSignatureLen: *mut cryptoki_sys::CK_ULONG, ); fn sign_final( session: cryptoki_sys::CK_SESSION_HANDLE, signature_ptr: *mut cryptoki_sys::CK_BYTE, signature_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; let result = sign_final_impl(&mut session, signature_ptr, signature_len_ptr); // A call to C_SignFinal always terminates the active signing operation unless it returns // CKR_BUFFER_TOO_SMALL or is a successful call (i.e., one which returns CKR_OK) to determine // the length of the buffer needed to hold the signature. let is_buffer_too_small = result == Err(Pkcs11Error::BufferTooSmall); let is_buffer_size_query = result.is_ok() && signature_ptr.is_null(); if !(is_buffer_too_small || is_buffer_size_query) { session.sign_clear(); } result } fn sign_final_impl( session: &mut Session, signature_ptr: *mut cryptoki_sys::CK_BYTE, signature_len_ptr: *mut cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if signature_len_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let buffer_size = unsafe { *signature_len_ptr } as usize; let theoretical_size = session.sign_theoretical_size()?; unsafe { std::ptr::write(signature_len_ptr, theoretical_size as CK_ULONG); } if signature_ptr.is_null() { // only the size was requested return Ok(()); } if buffer_size < theoretical_size { return Err(Pkcs11Error::BufferTooSmall); } let signature = session.sign_final()?; unsafe { std::ptr::write(signature_len_ptr, signature.len() as CK_ULONG); } // double check the buffer size if signature.len() > buffer_size { return Err(Pkcs11Error::BufferTooSmall); } unsafe { std::ptr::copy_nonoverlapping(signature.as_ptr(), signature_ptr, signature.len()); } Ok(()) } api_function!( C_SignRecoverInit = sign_recover_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn sign_recover_init( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_SignRecover = sign_recover; hSession: cryptoki_sys::CK_SESSION_HANDLE, pData: cryptoki_sys::CK_BYTE_PTR, ulDataLen: cryptoki_sys::CK_ULONG, pSignature: cryptoki_sys::CK_BYTE_PTR, pulSignatureLen: cryptoki_sys::CK_ULONG_PTR, ); fn sign_recover( _session: cryptoki_sys::CK_SESSION_HANDLE, _data_ptr: cryptoki_sys::CK_BYTE_PTR, _data_len: cryptoki_sys::CK_ULONG, _signature_ptr: cryptoki_sys::CK_BYTE_PTR, _signature_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_SignEncryptUpdate = sign_encrypt_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: cryptoki_sys::CK_BYTE_PTR, ulPartLen: cryptoki_sys::CK_ULONG, pEncryptedPart: cryptoki_sys::CK_BYTE_PTR, pulEncryptedPartLen: cryptoki_sys::CK_ULONG_PTR, ); fn sign_encrypt_update( _session: cryptoki_sys::CK_SESSION_HANDLE, _part_ptr: cryptoki_sys::CK_BYTE_PTR, _part_len: cryptoki_sys::CK_ULONG, _encrypted_part_ptr: cryptoki_sys::CK_BYTE_PTR, _encrypted_part_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } #[cfg(test)] mod tests { use crate::{backend::slot::init_for_tests, data::SESSION_MANAGER}; use super::*; #[test] fn test_sign_init_null_mechanism() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_SignInit(session, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_sign_init_invalid_mechanism() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut mechanism = cryptoki_sys::CK_MECHANISM { mechanism: 15000, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_SignInit(session, &mut mechanism, 0); assert_eq!(rv, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_sign_init_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut mechanism = cryptoki_sys::CK_MECHANISM { mechanism: 0, pParameter: std::ptr::null_mut(), ulParameterLen: 0, }; let rv = C_SignInit(0, &mut mechanism, 0); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_sign_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut data = [0u8; 32]; let mut signature = [0u8; 32]; let mut signature_len = 32; let rv = C_Sign( 0, data.as_mut_ptr(), data.len() as CK_ULONG, signature.as_mut_ptr(), &mut signature_len, ); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_sign_null_data() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut signature = [0u8; 32]; let mut signature_len = 32; let rv = C_Sign( session, std::ptr::null_mut(), 0, signature.as_mut_ptr(), &mut signature_len, ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_sign_null_signature_len() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data = [0u8; 32]; let mut signature = [0u8; 32]; let rv = C_Sign( session, data.as_mut_ptr(), data.len() as CK_ULONG, signature.as_mut_ptr(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_sign_operation_not_initialized() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data = [0u8; 32]; let mut signature = [0u8; 32]; let mut signature_len = 32; let rv = C_Sign( session, data.as_mut_ptr(), data.len() as CK_ULONG, signature.as_mut_ptr(), &mut signature_len, ); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } // #[test] // fn test_sign_null_signature() { // init_for_tests(); // let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); // let mut data = [0u8; 32]; // let mut signature_len = 32; // let rv = C_Sign( // session, // data.as_mut_ptr(), // data.len() as CK_ULONG, // std::ptr::null_mut(), // &mut signature_len, // ); // assert_eq!(rv, cryptoki_sys::CKR_OK); // } #[test] fn test_sign_update_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut data = [0u8; 32]; let rv = C_SignUpdate(0, data.as_mut_ptr(), data.len() as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_sign_update_null_data() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let rv = C_SignUpdate(session, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_sign_update_operation_not_initialized() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut data = [0u8; 32]; let rv = C_SignUpdate(session, data.as_mut_ptr(), data.len() as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } #[test] fn test_sign_final_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut signature = [0u8; 32]; let mut signature_len = 32; let rv = C_SignFinal(0, signature.as_mut_ptr(), &mut signature_len); assert_eq!(rv, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_sign_final_null_signature_len() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut signature = [0u8; 32]; let rv = C_SignFinal(session, signature.as_mut_ptr(), std::ptr::null_mut()); assert_eq!(rv, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_sign_final_operation_not_initialized() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut signature = [0u8; 32]; let mut signature_len = 32; let rv = C_SignFinal(session, signature.as_mut_ptr(), &mut signature_len); assert_eq!(rv, cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED); } #[test] fn test_sign_recover_init() { let _guard = init_for_tests(); let rv = C_SignRecoverInit(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_sign_recover() { let _guard = init_for_tests(); let rv = C_SignRecover( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_sign_encrypt_update() { let _guard = init_for_tests(); let rv = C_SignEncryptUpdate( 0, std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/token.rs000066400000000000000000000500721507371613300202510ustar00rootroot00000000000000use cryptoki_sys::{ CKF_RNG, CKF_TOKEN_INITIALIZED, CKF_USER_PIN_INITIALIZED, CK_SLOT_ID, CK_SLOT_INFO, CK_TOKEN_INFO, CK_ULONG, }; use log::{debug, error, trace, warn}; use nethsm_sdk_rs::{ apis::default_api, models::{HealthStateData, InfoData, SystemState}, }; use crate::{ api::api_function, backend::{ events::fetch_slots_state, login::{LoginCtx, UserMode}, slot::get_slot, Pkcs11Error, }, data::{self, EVENTS_MANAGER}, defs::{DEFAULT_FIRMWARE_VERSION, DEFAULT_HARDWARE_VERSION, MECHANISM_LIST}, utils::{padded_str, version_struct_from_str}, }; api_function!( C_GetSlotList = get_slot_list; tokenPresent: cryptoki_sys::CK_BBOOL, pSlotList: cryptoki_sys::CK_SLOT_ID_PTR, pulCount: cryptoki_sys::CK_ULONG_PTR, ); fn get_slot_list( _token_present: cryptoki_sys::CK_BBOOL, slot_list_ptr: cryptoki_sys::CK_SLOT_ID_PTR, count_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if count_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let device = data::load_device()?; let count = device.slots.len() as CK_ULONG; // only the count is requested if slot_list_ptr.is_null() { unsafe { std::ptr::write(count_ptr, count); } return Ok(()); } // check if the buffer is large enough if unsafe { *count_ptr } < count { unsafe { std::ptr::write(count_ptr, count); } return Err(Pkcs11Error::BufferTooSmall); } // list the ids let id_list: Vec = device .slots .iter() .enumerate() .map(|(i, _)| i as CK_SLOT_ID) .collect(); unsafe { std::ptr::copy_nonoverlapping(id_list.as_ptr(), slot_list_ptr, id_list.len()); std::ptr::write(count_ptr, count as CK_ULONG); } Ok(()) } api_function!( C_GetSlotInfo = get_slot_info; slotID: cryptoki_sys::CK_SLOT_ID, pInfo: cryptoki_sys::CK_SLOT_INFO_PTR, ); fn get_slot_info( slot_id: cryptoki_sys::CK_SLOT_ID, info_ptr: cryptoki_sys::CK_SLOT_INFO_PTR, ) -> Result<(), Pkcs11Error> { trace!("C_GetSlotInfo() called with slotID: {slot_id}"); if info_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } // get the slot let slot = get_slot(slot_id)?; let mut flags = 0; let login_ctx = LoginCtx::new(slot.clone(), false, false); let result = login_ctx.try_( default_api::info_get, crate::backend::login::UserMode::Guest, ); // fetch info from the device let info = match result { Ok(info) => info.entity, Err(e) => { trace!("Error getting info: {e:?}"); InfoData::new("unknown".to_owned(), "unknown".to_owned()) } }; let result = login_ctx.try_( default_api::health_state_get, crate::backend::login::UserMode::Guest, ); // fetch the sysem state let system_state = match result { Ok(info) => info.entity, Err(e) => { trace!("Error getting system state: {e:?}"); HealthStateData::new(SystemState::Unprovisioned) } }; if system_state.state == SystemState::Operational { flags |= cryptoki_sys::CKF_TOKEN_PRESENT; } let info: CK_SLOT_INFO = CK_SLOT_INFO { slotDescription: padded_str(&info.product), manufacturerID: padded_str(&info.vendor), flags, hardwareVersion: DEFAULT_HARDWARE_VERSION, firmwareVersion: DEFAULT_FIRMWARE_VERSION, }; unsafe { std::ptr::write(info_ptr, info); } Ok(()) } api_function!( C_GetTokenInfo = get_token_info; slotID: cryptoki_sys::CK_SLOT_ID, pInfo: cryptoki_sys::CK_TOKEN_INFO_PTR, ); fn get_token_info( slot_id: cryptoki_sys::CK_SLOT_ID, info_ptr: cryptoki_sys::CK_TOKEN_INFO_PTR, ) -> Result<(), Pkcs11Error> { trace!("C_GetTokenInfo() called with slotID: {slot_id}"); // get the slot let slot = get_slot(slot_id)?; if info_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let login_ctx = LoginCtx::new(slot.clone(), true, false); // fetch info from the device let info = login_ctx .try_( default_api::info_get, crate::backend::login::UserMode::Guest, ) .map_err(|err| { error!("Error getting info: {err:?}"); Pkcs11Error::FunctionFailed })?; let mut serial_number = "unknown".to_string(); let mut hardware_version = DEFAULT_HARDWARE_VERSION; let mut firmware_version = DEFAULT_FIRMWARE_VERSION; // Try to fech system info if login_ctx.can_run_mode(crate::backend::login::UserMode::Administrator) { match login_ctx.try_(default_api::system_info_get, UserMode::Administrator) { Err(e) => { warn!("Error getting system info: {e:?}"); } Ok(system_info) => { serial_number = system_info.entity.device_id; hardware_version = version_struct_from_str(system_info.entity.hardware_version); // The PKCS11 firmware version actually corresponds to the NetHSM software version firmware_version = version_struct_from_str(system_info.entity.software_version); } } } let mut flags = CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED | CKF_RNG; // if the slot has no password, set the login required flag if !slot.is_connected() { flags |= cryptoki_sys::CKF_LOGIN_REQUIRED; debug!("Login required"); } let token_info = CK_TOKEN_INFO { label: padded_str(&slot.label), manufacturerID: padded_str(&info.entity.vendor), model: padded_str(&info.entity.product), serialNumber: padded_str(&serial_number), flags, hardwareVersion: hardware_version, firmwareVersion: firmware_version, ..Default::default() }; unsafe { std::ptr::write(info_ptr, token_info); } Ok(()) } api_function!( C_InitToken = init_token; slotID: cryptoki_sys::CK_SLOT_ID, pPin: cryptoki_sys::CK_UTF8CHAR_PTR, ulPinLen: cryptoki_sys::CK_ULONG, pLabel: cryptoki_sys::CK_UTF8CHAR_PTR, ); fn init_token( _slot_id: cryptoki_sys::CK_SLOT_ID, _pin_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, _pin_len: cryptoki_sys::CK_ULONG, _label_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_GetMechanismList = get_mechanism_list; slotID: cryptoki_sys::CK_SLOT_ID, pMechanismList: cryptoki_sys::CK_MECHANISM_TYPE_PTR, pulCount: cryptoki_sys::CK_ULONG_PTR, ); fn get_mechanism_list( slot_id: cryptoki_sys::CK_SLOT_ID, mechanism_list_ptr: cryptoki_sys::CK_MECHANISM_TYPE_PTR, count_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { if count_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } get_slot(slot_id)?; let count = MECHANISM_LIST.len() as CK_ULONG; // only the count is requested if mechanism_list_ptr.is_null() { unsafe { std::ptr::write(count_ptr, count); } return Ok(()); } let buffer_size = unsafe { std::ptr::read(count_ptr) }; // set the buffer size unsafe { std::ptr::write(count_ptr, count); } // check if the buffer is large enough if buffer_size < count { return Err(Pkcs11Error::BufferTooSmall); } // list the ids let id_list: Vec = MECHANISM_LIST .iter() .map(|mechanism| mechanism.ck_type()) .collect(); unsafe { std::ptr::copy_nonoverlapping(id_list.as_ptr(), mechanism_list_ptr, id_list.len()); } Ok(()) } api_function!( C_GetMechanismInfo = get_mechanism_info; slotID: cryptoki_sys::CK_SLOT_ID, type_: cryptoki_sys::CK_MECHANISM_TYPE, pInfo: cryptoki_sys::CK_MECHANISM_INFO_PTR, ); fn get_mechanism_info( slot_id: cryptoki_sys::CK_SLOT_ID, type_: cryptoki_sys::CK_MECHANISM_TYPE, info_ptr: cryptoki_sys::CK_MECHANISM_INFO_PTR, ) -> Result<(), Pkcs11Error> { get_slot(slot_id)?; if info_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } // find the mechanism in the list let mechanism = MECHANISM_LIST .iter() .find(|m| m.ck_type() == type_) .ok_or(Pkcs11Error::MechanismInvalid)?; unsafe { std::ptr::write(info_ptr, mechanism.ck_info()); } Ok(()) } api_function!( C_Login = login; hSession: cryptoki_sys::CK_SESSION_HANDLE, userType: cryptoki_sys::CK_USER_TYPE, pPin: cryptoki_sys::CK_UTF8CHAR_PTR, ulPinLen: cryptoki_sys::CK_ULONG, ); fn login( session: cryptoki_sys::CK_SESSION_HANDLE, user_type: cryptoki_sys::CK_USER_TYPE, pin_ptr: cryptoki_sys::CK_UTF8CHAR_PTR, pin_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { if pin_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } let pin = unsafe { std::slice::from_raw_parts(pin_ptr, pin_len as usize) }; // parse string to utf8 let pin = std::str::from_utf8(pin).map_err(|_| Pkcs11Error::ArgumentsBad)?; let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session .login(user_type, pin.to_string()) .map_err(From::from) } api_function!( C_Logout = logout; hSession: cryptoki_sys::CK_SESSION_HANDLE, ); fn logout(session: cryptoki_sys::CK_SESSION_HANDLE) -> Result<(), Pkcs11Error> { let session = data::get_session(session)?; let mut session = data::lock_session(&session)?; session.logout().map_err(From::from) } api_function!( C_WaitForSlotEvent = wait_for_slot_event; flags: cryptoki_sys::CK_FLAGS, pSlot: cryptoki_sys::CK_SLOT_ID_PTR, pReserved: cryptoki_sys::CK_VOID_PTR, ); fn wait_for_slot_event( flags: cryptoki_sys::CK_FLAGS, slot_ptr: cryptoki_sys::CK_SLOT_ID_PTR, _reserved_ptr: cryptoki_sys::CK_VOID_PTR, ) -> Result<(), Pkcs11Error> { if slot_ptr.is_null() { return Err(Pkcs11Error::ArgumentsBad); } fetch_slots_state()?; loop { // check if there is an event in the queue let slot = EVENTS_MANAGER.write().unwrap().events.pop(); if let Some(slot) = slot { unsafe { std::ptr::write(slot_ptr, slot); } return Ok(()); } // if the dont block flag is set, return no event if flags & cryptoki_sys::CKF_DONT_BLOCK == 1 { return Err(Pkcs11Error::NoEvent); } else { // Otherwise, wait for an event // If C_Finalize() has been called, return an error if EVENTS_MANAGER.read().unwrap().finalized { return Err(Pkcs11Error::CryptokiNotInitialized); } // sleep for 1 second std::thread::sleep(std::time::Duration::from_secs(1)); // fetch the slots state so we get the latest events in the next iteration fetch_slots_state()?; } } } #[cfg(test)] mod tests { use cryptoki_sys::{CKF_DONT_BLOCK, CKU_USER, CK_MECHANISM_INFO}; use crate::{ api::C_Finalize, backend::{ events::{update_slot_state, EventsManager}, slot::init_for_tests, }, data::{SESSION_MANAGER, TOKENS_STATE}, }; use super::*; // Ignored by default because it would race with the other #[ignore] tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_wait_for_slot_event_no_event() { let _guard = init_for_tests(); *EVENTS_MANAGER.write().unwrap() = EventsManager::new(); *TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new(); let mut slot = 0; let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, &mut slot, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_NO_EVENT); } // Ignored by default because it would race with the other #[ignore] tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_wait_for_slot_event_one_event() { let _guard = init_for_tests(); *EVENTS_MANAGER.write().unwrap() = EventsManager::new(); *TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new(); update_slot_state(0, false); update_slot_state(0, true); println!("Events: {:?}", EVENTS_MANAGER.read().unwrap().events); let mut slot = 15; let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, &mut slot, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_OK); assert_eq!(slot, 0); } // Ignored by default because it would race with the other #[ignore] tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_wait_for_slot_event_blocking_one_event() { let _guard = init_for_tests(); *EVENTS_MANAGER.write().unwrap() = EventsManager::new(); *TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new(); // update the slot state in a separate thread let handle = std::thread::spawn(|| { std::thread::sleep(std::time::Duration::from_millis(100)); update_slot_state(0, false); update_slot_state(0, true); }); let mut slot = 15; let result = C_WaitForSlotEvent(0, &mut slot, std::ptr::null_mut()); handle.join().unwrap(); assert_eq!(result, cryptoki_sys::CKR_OK); assert_eq!(slot, 0); } // Ignored by default because it would race with the other #[ignore] tests // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn test_wait_for_slot_event_blocking_finalize() { let _guard = init_for_tests(); *EVENTS_MANAGER.write().unwrap() = EventsManager::new(); *TOKENS_STATE.lock().unwrap() = std::collections::HashMap::new(); // update the slot state in a separate thread let handle = std::thread::spawn(|| { std::thread::sleep(std::time::Duration::from_millis(100)); C_Finalize(std::ptr::null_mut()); }); let mut slot = 15; let result = C_WaitForSlotEvent(0, &mut slot, std::ptr::null_mut()); handle.join().unwrap(); println!("slot: {slot}"); assert_eq!(result, cryptoki_sys::CKR_CRYPTOKI_NOT_INITIALIZED); } #[test] fn test_wait_for_slot_event_null_slot_ptr() { let _guard = init_for_tests(); let result = C_WaitForSlotEvent(CKF_DONT_BLOCK, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_slot_list_null_count() { let _guard = init_for_tests(); let result = C_GetSlotList(0, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_slot_list_null_list() { let _guard = init_for_tests(); let mut count = 0; let result = C_GetSlotList(0, std::ptr::null_mut(), &mut count); assert_eq!(result, cryptoki_sys::CKR_OK); assert_eq!(count, 1); } #[test] fn test_get_slot_list_small_buffer() { let _guard = init_for_tests(); let mut count = 0; let mut list = [0; 1]; let result = C_GetSlotList(0, list.as_mut_ptr(), &mut count); assert_eq!(result, cryptoki_sys::CKR_BUFFER_TOO_SMALL); assert_eq!(count, 1); } #[test] fn test_get_slot_info_invalid_slot() { let _guard = init_for_tests(); let mut info = CK_SLOT_INFO::default(); let result = C_GetSlotInfo(99, &mut info); assert_eq!(result, cryptoki_sys::CKR_SLOT_ID_INVALID); } #[test] fn test_get_slot_info_null_info() { let _guard = init_for_tests(); let result = C_GetSlotInfo(0, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_mechanism_list_null_count() { let _guard = init_for_tests(); let result = C_GetMechanismList(0, std::ptr::null_mut(), std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_mechanism_list_null_list() { let _guard = init_for_tests(); let mut count = 0; let result = C_GetMechanismList(0, std::ptr::null_mut(), &mut count); assert_eq!(result, cryptoki_sys::CKR_OK); assert_eq!(count, MECHANISM_LIST.len() as CK_ULONG); } #[test] fn test_get_mechanism_list_small_buffer() { let _guard = init_for_tests(); let mut count = 0; let mut list = [0; 1]; let result = C_GetMechanismList(0, list.as_mut_ptr(), &mut count); assert_eq!(result, cryptoki_sys::CKR_BUFFER_TOO_SMALL); assert_eq!(count, MECHANISM_LIST.len() as CK_ULONG); } #[test] fn test_get_mechanism_list_invalid_slot() { let _guard = init_for_tests(); let mut count = 0; let mut list = [0; 1]; let result = C_GetMechanismList(99, list.as_mut_ptr(), &mut count); assert_eq!(result, cryptoki_sys::CKR_SLOT_ID_INVALID); } #[test] fn test_get_mechanism_info_invalid_mechanism() { let _guard = init_for_tests(); let mut info = CK_MECHANISM_INFO::default(); let result = C_GetMechanismInfo(0, 15000, &mut info); assert_eq!(result, cryptoki_sys::CKR_MECHANISM_INVALID); } #[test] fn test_get_mechanism_info_invalid_slot() { let _guard = init_for_tests(); let mut info = CK_MECHANISM_INFO::default(); let result = C_GetMechanismInfo(99, 0, &mut info); assert_eq!(result, cryptoki_sys::CKR_SLOT_ID_INVALID); } #[test] fn test_get_mechanism_info_null_info() { let _guard = init_for_tests(); let result = C_GetMechanismInfo(0, 0, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_get_token_info_invalid_slot() { let _guard = init_for_tests(); let mut info = CK_TOKEN_INFO::default(); let result = C_GetTokenInfo(99, &mut info); assert_eq!(result, cryptoki_sys::CKR_SLOT_ID_INVALID); } #[test] fn test_get_token_info_null_info() { let _guard = init_for_tests(); let result = C_GetTokenInfo(0, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_login_null_pin() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let result = C_Login(session, CKU_USER, std::ptr::null_mut(), 0); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_login_non_utf8_pin() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let mut pin = [0xFF, 0xFF, 0xFF, 0xFF]; let result = C_Login(session, CKU_USER, pin.as_mut_ptr(), 4); assert_eq!(result, cryptoki_sys::CKR_ARGUMENTS_BAD); } #[test] fn test_login_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let mut pin = "1234".to_string(); let result = C_Login(0, CKU_USER, pin.as_mut_ptr(), 4); assert_eq!(result, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_logout_invalid_session() { let _guard = init_for_tests(); SESSION_MANAGER.lock().unwrap().delete_session(0); let result = C_Logout(0); assert_eq!(result, cryptoki_sys::CKR_SESSION_HANDLE_INVALID); } #[test] fn test_logout() { let _guard = init_for_tests(); let session = SESSION_MANAGER.lock().unwrap().setup_dummy_session(); let result = C_Logout(session); assert_eq!(result, cryptoki_sys::CKR_OK); } #[test] fn test_init_token() { let _guard = init_for_tests(); let result = C_InitToken(0, std::ptr::null_mut(), 0, std::ptr::null_mut()); assert_eq!(result, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } } nethsm-pkcs11-2.0.0/pkcs11/src/api/verify.rs000066400000000000000000000115431507371613300204350ustar00rootroot00000000000000/* We won't implement these function as it is not a feature of NetHSM. */ use crate::{api::api_function, backend::Pkcs11Error}; api_function!( C_VerifyInit = verify_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn verify_init( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_Verify = verify; hSession: cryptoki_sys::CK_SESSION_HANDLE, pData: cryptoki_sys::CK_BYTE_PTR, ulDataLen: cryptoki_sys::CK_ULONG, pSignature: cryptoki_sys::CK_BYTE_PTR, ulSignatureLen: cryptoki_sys::CK_ULONG, ); fn verify( _session: cryptoki_sys::CK_SESSION_HANDLE, _data_ptr: cryptoki_sys::CK_BYTE_PTR, _data_len: cryptoki_sys::CK_ULONG, _signature_ptr: cryptoki_sys::CK_BYTE_PTR, _signature_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_VerifyUpdate = verify_update; hSession: cryptoki_sys::CK_SESSION_HANDLE, pPart: cryptoki_sys::CK_BYTE_PTR, ulPartLen: cryptoki_sys::CK_ULONG, ); fn verify_update( _session: cryptoki_sys::CK_SESSION_HANDLE, _part_ptr: cryptoki_sys::CK_BYTE_PTR, _part_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_VerifyFinal = verify_final; hSession: cryptoki_sys::CK_SESSION_HANDLE, pSignature: cryptoki_sys::CK_BYTE_PTR, ulSignatureLen: cryptoki_sys::CK_ULONG, ); fn verify_final( _session: cryptoki_sys::CK_SESSION_HANDLE, _signature_ptr: cryptoki_sys::CK_BYTE_PTR, _signature_len: cryptoki_sys::CK_ULONG, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_VerifyRecoverInit = verify_recover_init; hSession: cryptoki_sys::CK_SESSION_HANDLE, pMechanism: cryptoki_sys::CK_MECHANISM_PTR, hKey: cryptoki_sys::CK_OBJECT_HANDLE, ); fn verify_recover_init( _session: cryptoki_sys::CK_SESSION_HANDLE, _mechanism_ptr: cryptoki_sys::CK_MECHANISM_PTR, _key: cryptoki_sys::CK_OBJECT_HANDLE, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } api_function!( C_VerifyRecover = verify_recover; hSession: cryptoki_sys::CK_SESSION_HANDLE, pSignature: cryptoki_sys::CK_BYTE_PTR, ulSignatureLen: cryptoki_sys::CK_ULONG, pData: cryptoki_sys::CK_BYTE_PTR, pulDataLen: cryptoki_sys::CK_ULONG_PTR, ); fn verify_recover( _session: cryptoki_sys::CK_SESSION_HANDLE, _signature_ptr: cryptoki_sys::CK_BYTE_PTR, _signature_len: cryptoki_sys::CK_ULONG, _data_ptr: cryptoki_sys::CK_BYTE_PTR, _data_len_ptr: cryptoki_sys::CK_ULONG_PTR, ) -> Result<(), Pkcs11Error> { Err(Pkcs11Error::FunctionNotSupported) } // just test that the functions return CKR_FUNCTION_NOT_SUPPORTED #[cfg(test)] mod tests { use cryptoki_sys::CK_ULONG; use crate::backend::slot::init_for_tests; use super::*; #[test] fn test_verify_init() { let _guard = init_for_tests(); let rv = C_VerifyInit(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_verify() { let _guard = init_for_tests(); let mut data = [0u8; 1]; let mut sig = [0u8; 1]; let rv = C_Verify( 0, data.as_mut_ptr(), data.len() as CK_ULONG, sig.as_mut_ptr(), sig.len() as CK_ULONG, ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_verify_update() { let _guard = init_for_tests(); let mut data = [0u8; 1]; let rv = C_VerifyUpdate(0, data.as_mut_ptr(), data.len() as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_verify_final() { let _guard = init_for_tests(); let mut sig = [0u8; 1]; let rv = C_VerifyFinal(0, sig.as_mut_ptr(), sig.len() as CK_ULONG); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_verify_recover_init() { let _guard = init_for_tests(); let rv = C_VerifyRecoverInit(0, std::ptr::null_mut(), 0); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } #[test] fn test_verify_recover() { let _guard = init_for_tests(); let mut sig = [0u8; 1]; let mut data = [0u8; 1]; let mut data_len = 0; let rv = C_VerifyRecover( 0, sig.as_mut_ptr(), sig.len() as CK_ULONG, data.as_mut_ptr(), &mut data_len, ); assert_eq!(rv, cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED); } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/000077500000000000000000000000001507371613300173755ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/backend/db/000077500000000000000000000000001507371613300177625ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/backend/db/attr.rs000066400000000000000000000072261507371613300213110ustar00rootroot00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2023 Nitrokey // SPDX-License-Identifier: Apache-2.0 use std::iter::Iterator; use cryptoki_sys::{CK_ULONG, CK_UNAVAILABLE_INFORMATION}; pub enum Error { BufTooSmall, NullPtrDeref, } pub struct CkRawAttr(cryptoki_sys::CK_ATTRIBUTE_PTR); impl CkRawAttr { pub unsafe fn from_raw_ptr(ptr: cryptoki_sys::CK_ATTRIBUTE_PTR) -> Option { if ptr.is_null() { return None; } Some(Self(ptr)) } pub fn type_(&self) -> cryptoki_sys::CK_ATTRIBUTE_TYPE { unsafe { (*self.0).type_ } } pub fn val_bytes(&self) -> Option<&[u8]> { // check the size of the value if self.len() == CK_UNAVAILABLE_INFORMATION || self.len() == 0 { return None; } let val_ptr = unsafe { (*self.0).pValue }; if val_ptr.is_null() { return None; } unsafe { Some(std::slice::from_raw_parts( val_ptr as *const u8, self.len() as usize, )) } } pub unsafe fn read_value(&self) -> Option { // check if the size of the value is correct if self.len() == CK_UNAVAILABLE_INFORMATION || self.len() != std::mem::size_of::() as CK_ULONG { return None; } // read the value let val_ptr = unsafe { (*self.0).pValue }; if val_ptr.is_null() { return None; } unsafe { Some(std::ptr::read(val_ptr as *const T)) } } pub fn len(&self) -> cryptoki_sys::CK_ULONG { unsafe { (*self.0).ulValueLen } } pub fn set_unavailable(&mut self) { self.set_len(CK_UNAVAILABLE_INFORMATION) } fn set_len(&mut self, len: cryptoki_sys::CK_ULONG) { unsafe { (*self.0).ulValueLen = len; } } pub fn set_val_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> { unsafe { if (*self.0).pValue.is_null() { // Null ptr means that the application wants to know the size of the value self.set_len(bytes.len() as CK_ULONG); return Err(Error::NullPtrDeref); } if bytes.len() > (*self.0).ulValueLen as usize { return Err(Error::BufTooSmall); } std::ptr::copy_nonoverlapping(bytes.as_ptr(), (*self.0).pValue as *mut u8, bytes.len()); } self.set_len(bytes.len() as CK_ULONG); Ok(()) } } #[derive(Debug)] pub struct CkRawAttrTemplate { ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: usize, } impl CkRawAttrTemplate { pub unsafe fn from_raw_ptr(ptr: cryptoki_sys::CK_ATTRIBUTE_PTR, count: usize) -> Option { if ptr.is_null() { return None; } Some(Self { ptr, count }) } pub fn attr_wrapper(&self, index: usize) -> Option { if index >= self.count { return None; } unsafe { CkRawAttr::from_raw_ptr(self.ptr.add(index)) } } pub fn len(&self) -> usize { self.count } pub fn iter(&self) -> CkRawAttrTemplateIter<'_> { CkRawAttrTemplateIter { tpl: self, index: 0, } } } pub struct CkRawAttrTemplateIter<'a> { tpl: &'a CkRawAttrTemplate, index: usize, } impl Iterator for CkRawAttrTemplateIter<'_> { type Item = CkRawAttr; fn next(&mut self) -> Option { if self.index < self.tpl.len() { let ret = self.tpl.attr_wrapper(self.index); self.index += 1; ret } else { None } } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/db/mod.rs000066400000000000000000000072151507371613300211140ustar00rootroot00000000000000// Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2023 Nitrokey // SPDX-License-Identifier: Apache-2.0 // modified from https://github.com/aws/aws-nitro-enclaves-acm pub mod attr; pub mod object; use cryptoki_sys::CK_OBJECT_HANDLE; use log::info; use std::{collections::HashMap, time::SystemTime}; pub use object::Object; #[derive(Debug)] pub struct Db { objects: HashMap, next_handle: CK_OBJECT_HANDLE, last_fetchall_timestamp: Option, is_being_fetched: bool, } impl Db { pub fn new() -> Self { Self { objects: HashMap::new(), // 0 means invalid handle, we need to start from 1 next_handle: 1, last_fetchall_timestamp: None, is_being_fetched: false, } } pub fn fetched_all_keys(&self) -> bool { self.last_fetchall_timestamp .map(|last| { last.elapsed() // cache for 1 hour .map(|elapsed| elapsed.as_secs() < 3600) .unwrap_or(false) }) .unwrap_or(false) } pub fn is_being_fetched(&self) -> bool { self.is_being_fetched } pub fn set_is_being_fetched(&mut self, value: bool) { self.is_being_fetched = value; } pub fn set_fetched_all_keys(&mut self, fetched_all_keys: bool) { if fetched_all_keys { self.last_fetchall_timestamp = Some(SystemTime::now()); self.is_being_fetched = false; } else { self.last_fetchall_timestamp = None; } } #[allow(dead_code)] pub fn clear(&mut self) { self.set_fetched_all_keys(false); self.objects.clear(); } pub fn iter(&self) -> impl Iterator { self.objects .iter() .map(|(handle, object)| (*handle, object)) } pub fn add_object(&mut self, object: Object) -> (CK_OBJECT_HANDLE, Object) { // check if the object already exists let found = self .objects .iter_mut() .find(|(_, obj)| obj.id == object.id && obj.kind == object.kind); if let Some((handle, obj)) = found { *obj = object; return (*handle, obj.clone()); } // increment the handle let handle = self.next_handle; self.next_handle += 1; self.objects.insert(handle, object); (handle, self.objects.get(&handle).unwrap().clone()) } pub fn object(&self, handle: CK_OBJECT_HANDLE) -> Option<&Object> { self.objects.get(&handle) } pub fn remove(&mut self, handle: CK_OBJECT_HANDLE) -> Option { self.objects.remove(&handle) } pub fn rename(&mut self, old_id: &str, new_id: &str) { for object in self.objects.values_mut() { if object.id == old_id { info!( "Renaming object {:?}:{} to {}", object.kind, object.id, new_id ); object.rename(new_id); } } } pub fn remove_objects_by_id(&mut self, id: &str) { self.objects.retain(|_, object| object.id != id) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_adding_same_object() { let mut db = Db::new(); let mut object = Object::default(); object.id = "id".to_string(); let (handle1, object1) = db.add_object(object.clone()); let (handle2, object2) = db.add_object(object.clone()); assert_eq!(handle1, handle2); assert_eq!(object1.id, object2.id); } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/db/object.rs000066400000000000000000000430051507371613300216000ustar00rootroot00000000000000use base64ct::{Base64, Encoding}; use config_file::CertificateFormat; // Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2023 Nitrokey // SPDX-License-Identifier: Apache-2.0 use cryptoki_sys::{ CKA_ALLOWED_MECHANISMS, CKA_ALWAYS_AUTHENTICATE, CKA_ALWAYS_SENSITIVE, CKA_CERTIFICATE_CATEGORY, CKA_CERTIFICATE_TYPE, CKA_CLASS, CKA_DECRYPT, CKA_DERIVE, CKA_EC_PARAMS, CKA_EC_POINT, CKA_ENCRYPT, CKA_EXTRACTABLE, CKA_ID, CKA_ISSUER, CKA_KEY_GEN_MECHANISM, CKA_KEY_TYPE, CKA_LABEL, CKA_LOCAL, CKA_MODIFIABLE, CKA_MODULUS, CKA_MODULUS_BITS, CKA_NEVER_EXTRACTABLE, CKA_PRIVATE, CKA_PUBLIC_EXPONENT, CKA_SENSITIVE, CKA_SERIAL_NUMBER, CKA_SIGN, CKA_SIGN_RECOVER, CKA_SUBJECT, CKA_TOKEN, CKA_TRUSTED, CKA_UNWRAP, CKA_VALUE, CKA_VALUE_LEN, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_WRAP_WITH_TRUSTED, CKC_X_509, CK_ATTRIBUTE_TYPE, CK_KEY_TYPE, CK_MECHANISM_TYPE, CK_OBJECT_CLASS, CK_ULONG, CK_UNAVAILABLE_INFORMATION, }; use der::{asn1::OctetString, Decode, DecodePem, Encode}; use log::{debug, error, info, trace}; use nethsm_sdk_rs::models::PublicKey; use std::collections::HashMap; use std::mem::size_of; use crate::backend::{ key::{EcKeyType, KeyType}, mechanism::{KeyMechanism, Mechanism}, Error, Pkcs11Error, }; use super::attr::{self, CkRawAttrTemplate}; /// Object and object attribute handling logic. See the PKCS#11 /// Section 4 on objects for more details on how these attributes /// are handled. Each object has a unique handle and /// a well defined class (i.e. private key, certificate etc.) and /// based on this class a well defined set of valid attributes. /// Since there is no R/W session support these objects are created /// from the user provisioned database. #[derive(Debug, Clone)] pub enum Attr { Bytes(Vec), CkBbool([u8; size_of::()]), CkByte([u8; size_of::()]), CkKeyType([u8; size_of::()]), CkCertType([u8; size_of::()]), CkCertCategory([u8; size_of::()]), CkMechanismType([u8; size_of::()]), CkObjectClass([u8; size_of::()]), CkUlong([u8; size_of::()]), #[allow(dead_code)] Sensitive, } impl Attr { const CK_TRUE: Self = Self::CkBbool([cryptoki_sys::CK_TRUE; 1]); const CK_FALSE: Self = Self::CkBbool([cryptoki_sys::CK_FALSE; 1]); pub fn as_bytes(&self) -> &[u8] { match self { Self::CkBbool(v) => v, Self::CkByte(v) => v, Self::CkKeyType(v) => v, Self::CkCertType(v) => v, Self::CkCertCategory(v) => v, Self::CkMechanismType(v) => v, Self::CkObjectClass(v) => v, Self::CkUlong(v) => v, Self::Bytes(v) => v, Self::Sensitive => &[0u8; 0], } } #[allow(dead_code)] fn from_ck_byte(src: cryptoki_sys::CK_BYTE) -> Self { #[cfg(target_endian = "little")] Self::CkByte(src.to_le_bytes()) } fn from_ck_key_type(src: cryptoki_sys::CK_KEY_TYPE) -> Self { #[cfg(target_endian = "little")] Self::CkKeyType(src.to_le_bytes()) } #[allow(dead_code)] fn from_ck_cert_type(src: cryptoki_sys::CK_CERTIFICATE_TYPE) -> Self { #[cfg(target_endian = "little")] Self::CkCertType(src.to_le_bytes()) } #[allow(dead_code)] fn from_ck_cert_category(src: cryptoki_sys::CK_ULONG) -> Self { #[cfg(target_endian = "little")] Self::CkCertCategory(src.to_le_bytes()) } fn from_ck_mechanism_type(src: cryptoki_sys::CK_MECHANISM_TYPE) -> Self { #[cfg(target_endian = "little")] Self::CkMechanismType(src.to_le_bytes()) } #[allow(dead_code)] fn from_ck_mechanism_type_vec(src: Vec) -> Self { #[cfg(target_endian = "little")] Self::Bytes(src.iter().flat_map(|x| x.to_le_bytes()).collect()) } fn from_ck_object_class(src: cryptoki_sys::CK_OBJECT_CLASS) -> Self { #[cfg(target_endian = "little")] Self::CkObjectClass(src.to_le_bytes()) } fn from_ck_ulong(src: cryptoki_sys::CK_ULONG) -> Self { #[cfg(target_endian = "little")] Self::CkUlong(src.to_le_bytes()) } } impl PartialEq for Attr { fn eq(&self, other: &Attr) -> bool { self.as_bytes() == other.as_bytes() } } #[derive(Clone, Copy, Debug, PartialEq)] pub enum ObjectKind { PrivateKey, PublicKey, SecretKey, Certificate, Other, } impl Default for ObjectKind { fn default() -> Self { Self::Other } } impl From for ObjectKind { fn from(src: CK_OBJECT_CLASS) -> Self { match src { cryptoki_sys::CKO_PRIVATE_KEY => Self::PrivateKey, cryptoki_sys::CKO_PUBLIC_KEY => Self::PublicKey, cryptoki_sys::CKO_SECRET_KEY => Self::SecretKey, cryptoki_sys::CKO_CERTIFICATE => Self::Certificate, _ => Self::Other, } } } #[derive(Debug, Clone, Default)] pub struct Object { attrs: HashMap, pub kind: ObjectKind, pub id: String, pub size: Option, // the size of the object in bytes pub mechanisms: Vec, } impl Object { pub fn rename(&mut self, id: &str) { let attr_id = self.attrs.get(&CKA_ID); let attr_label = self.attrs.get(&CKA_LABEL); info!("renaming {}/{attr_id:?}/{attr_label:?} to {}", self.id, id); let id_bytes = Attr::Bytes(id.as_bytes().to_vec()); self.attrs.insert(CKA_LABEL, id_bytes.clone()); self.attrs.insert(CKA_ID, id_bytes); self.id = id.to_owned(); } } struct KeyData { key_type: CK_KEY_TYPE, key_size: Option, attrs: HashMap, } fn configure_rsa(key_data: &PublicKey) -> Result { let key_data = key_data .public .as_ref() .ok_or(Error::KeyField("public".to_string()))?; let modulus = key_data .modulus .as_ref() .ok_or(Error::KeyField("modulus".to_string()))?; let public_exponent = key_data .public_exponent .as_ref() .ok_or(Error::KeyField("public_exponent".to_string()))?; let modulus = Base64::decode_vec(modulus)?; let public_exponent = Base64::decode_vec(public_exponent)?; let mut attrs = HashMap::new(); let size = modulus.len(); attrs.insert(CKA_KEY_TYPE, Attr::from_ck_key_type(cryptoki_sys::CKK_RSA)); attrs.insert(CKA_DERIVE, Attr::CK_FALSE); attrs.insert(CKA_DECRYPT, Attr::CK_TRUE); attrs.insert(CKA_SIGN, Attr::CK_TRUE); attrs.insert(CKA_SIGN_RECOVER, Attr::CK_FALSE); attrs.insert(CKA_UNWRAP, Attr::CK_FALSE); attrs.insert(CKA_WRAP_WITH_TRUSTED, Attr::CK_FALSE); attrs.insert(CKA_MODULUS, Attr::Bytes(modulus)); attrs.insert(CKA_PUBLIC_EXPONENT, Attr::Bytes(public_exponent)); attrs.insert( CKA_MODULUS_BITS, Attr::from_ck_ulong((size * 8) as CK_ULONG), ); Ok(KeyData { key_type: cryptoki_sys::CKK_RSA, key_size: Some(size), attrs, }) } fn configure_ec(key_data: &PublicKey, key_type: EcKeyType) -> Result { let ec_points = key_data .public .as_ref() .ok_or(Error::KeyField("public".to_string()))? .data .as_ref() .ok_or(Error::KeyField("data".to_string()))?; let size = key_type.key_size(); trace!("EC key data: {ec_points:?}"); let mut ec_point_bytes = Base64::decode_vec(ec_points)?; trace!("EC key data bytes length : {}", ec_point_bytes.len()); // add padding while ec_point_bytes.len() < size / 8 { ec_point_bytes.insert(0, 0); } trace!("EC key data bytes: {ec_point_bytes:?}"); let encoded_points = OctetString::new(ec_point_bytes).unwrap().to_der().unwrap(); trace!("EC key data encoded len : {}", encoded_points.len()); let key_params = key_type.to_asn1(); let ec_params = key_params.to_der().map_err(Error::Der)?; let key_type = match key_type { EcKeyType::Curve25519 => cryptoki_sys::CKK_EC_EDWARDS, _ => cryptoki_sys::CKK_EC, }; let mut attrs = HashMap::new(); attrs.insert(CKA_KEY_TYPE, Attr::from_ck_key_type(key_type)); attrs.insert(CKA_DERIVE, Attr::CK_TRUE); attrs.insert(CKA_DECRYPT, Attr::CK_FALSE); attrs.insert(CKA_SIGN, Attr::CK_TRUE); attrs.insert(CKA_SIGN_RECOVER, Attr::CK_FALSE); attrs.insert(CKA_UNWRAP, Attr::CK_FALSE); attrs.insert(CKA_WRAP_WITH_TRUSTED, Attr::CK_FALSE); attrs.insert(CKA_EC_PARAMS, Attr::Bytes(ec_params)); attrs.insert(CKA_EC_POINT, Attr::Bytes(encoded_points)); Ok(KeyData { key_type, key_size: Some(size), attrs, }) } // should be an aes key ?? fn configure_generic() -> Result { let mut attrs = HashMap::new(); attrs.insert( CKA_CLASS, Attr::from_ck_object_class(cryptoki_sys::CKO_SECRET_KEY), ); attrs.insert( CKA_KEY_TYPE, Attr::from_ck_key_type(cryptoki_sys::CKK_GENERIC_SECRET), ); attrs.insert(CKA_DERIVE, Attr::CK_FALSE); attrs.insert(CKA_DECRYPT, Attr::CK_TRUE); attrs.insert(CKA_ENCRYPT, Attr::CK_TRUE); attrs.insert(CKA_SIGN, Attr::CK_FALSE); attrs.insert(CKA_SIGN_RECOVER, Attr::CK_FALSE); attrs.insert(CKA_UNWRAP, Attr::CK_FALSE); attrs.insert(CKA_WRAP_WITH_TRUSTED, Attr::CK_FALSE); attrs.insert(CKA_VALUE_LEN, Attr::from_ck_ulong(0)); attrs.insert(CKA_ALWAYS_AUTHENTICATE, Attr::CK_FALSE); attrs.insert(CKA_VERIFY, Attr::CK_FALSE); Ok(KeyData { key_type: cryptoki_sys::CKK_GENERIC_SECRET, key_size: None, attrs, }) } pub fn from_key_data(key_data: PublicKey, id: &str) -> Result, Error> { let key_type = KeyType::try_from(key_data.r#type).map_err(|_| { error!( "Failed to create key with unsupported type {}", key_data.r#type ); Error::KeyField(format!("Unsupported key type: {:?}", key_data.r#type)) })?; let mut attrs = HashMap::new(); attrs.insert( CKA_CLASS, Attr::from_ck_object_class(cryptoki_sys::CKO_PRIVATE_KEY), ); attrs.insert(CKA_ID, Attr::Bytes(id.as_bytes().to_vec())); attrs.insert(CKA_LABEL, Attr::Bytes(id.as_bytes().to_vec())); attrs.insert( CKA_KEY_GEN_MECHANISM, Attr::from_ck_mechanism_type(CK_UNAVAILABLE_INFORMATION), ); attrs.insert(CKA_LOCAL, Attr::CK_FALSE); attrs.insert(CKA_MODIFIABLE, Attr::CK_FALSE); attrs.insert(CKA_TOKEN, Attr::CK_TRUE); attrs.insert(CKA_ALWAYS_AUTHENTICATE, Attr::CK_FALSE); attrs.insert(CKA_SENSITIVE, Attr::CK_TRUE); attrs.insert(CKA_ALWAYS_SENSITIVE, Attr::CK_TRUE); attrs.insert(CKA_EXTRACTABLE, Attr::CK_FALSE); attrs.insert(CKA_NEVER_EXTRACTABLE, Attr::CK_TRUE); attrs.insert(CKA_PRIVATE, Attr::CK_TRUE); attrs.insert(CKA_VERIFY_RECOVER, Attr::CK_FALSE); attrs.insert(CKA_VALUE, Attr::Bytes(vec![])); attrs.insert(CKA_TRUSTED, Attr::CK_FALSE); attrs.insert(CKA_WRAP, Attr::CK_FALSE); let key_attrs = match key_type { KeyType::Rsa => configure_rsa(&key_data)?, KeyType::Ec(ty) => configure_ec(&key_data, ty)?, KeyType::Generic => configure_generic()?, }; attrs.extend(key_attrs.attrs); let mechanisms: Vec<_> = key_data .mechanisms .iter() .copied() .flat_map(TryFrom::try_from) .collect(); let ck_mech_list: Vec = mechanisms .iter() .flat_map(|mech| Mechanism::try_from(*mech).ok()) .map(|m: Mechanism| m.ck_type()) .collect(); attrs.insert( CKA_ALLOWED_MECHANISMS, Attr::from_ck_mechanism_type_vec(ck_mech_list), ); let private_key = Object { attrs: attrs.clone(), kind: ObjectKind::PrivateKey, id: id.to_string(), size: key_attrs.key_size, mechanisms, }; if key_type == KeyType::Generic { let secret = Object { kind: ObjectKind::SecretKey, ..private_key }; return Ok(vec![secret]); } let mut public_key = Object { attrs: attrs.clone(), kind: ObjectKind::PublicKey, id: id.to_string(), size: key_attrs.key_size, mechanisms: vec![], }; public_key .attrs .insert(CKA_ALLOWED_MECHANISMS, Attr::Bytes(vec![])); public_key.attrs.insert( CKA_CLASS, Attr::from_ck_object_class(cryptoki_sys::CKO_PUBLIC_KEY), ); public_key .attrs .insert(CKA_KEY_TYPE, Attr::from_ck_key_type(key_attrs.key_type)); public_key.attrs.insert(CKA_PRIVATE, Attr::CK_FALSE); public_key.attrs.insert(CKA_SENSITIVE, Attr::CK_FALSE); public_key .attrs .insert(CKA_ALWAYS_SENSITIVE, Attr::CK_FALSE); public_key.attrs.insert(CKA_EXTRACTABLE, Attr::CK_FALSE); public_key .attrs .insert(CKA_NEVER_EXTRACTABLE, Attr::CK_FALSE); public_key.attrs.insert(CKA_DECRYPT, Attr::CK_FALSE); public_key.attrs.insert(CKA_ENCRYPT, Attr::CK_FALSE); public_key.attrs.insert(CKA_SIGN, Attr::CK_FALSE); public_key.attrs.insert(CKA_VERIFY, Attr::CK_FALSE); public_key.attrs.insert(CKA_DERIVE, Attr::CK_FALSE); public_key.attrs.insert(CKA_SIGN_RECOVER, Attr::CK_FALSE); public_key.attrs.insert(CKA_UNWRAP, Attr::CK_FALSE); public_key.attrs.insert(CKA_WRAP, Attr::CK_FALSE); public_key .attrs .insert(CKA_WRAP_WITH_TRUSTED, Attr::CK_FALSE); Ok(vec![public_key, private_key]) } pub fn from_cert_data( cert: Vec, key_id: &str, certificate_format: CertificateFormat, ) -> Result { debug!("Loading certificate, expecting {certificate_format} encoding as per configuration"); let (cert, cert_der) = match certificate_format { CertificateFormat::Der => ( x509_cert::Certificate::from_der(&cert).map_err(Error::Der)?, cert, ), CertificateFormat::Pem => { let mut buf = Vec::new(); let parsed_cert = x509_cert::Certificate::from_pem(&cert).map_err(Error::Der)?; parsed_cert.encode_to_vec(&mut buf).map_err(Error::Der)?; (parsed_cert, buf) } }; let length = cert_der.len(); let mut attrs = HashMap::new(); attrs.insert( CKA_CLASS, Attr::from_ck_object_class(cryptoki_sys::CKO_CERTIFICATE), ); attrs.insert(CKA_ID, Attr::Bytes(key_id.as_bytes().to_vec())); attrs.insert(CKA_LABEL, Attr::Bytes(key_id.as_bytes().to_vec())); attrs.insert( CKA_KEY_GEN_MECHANISM, Attr::from_ck_mechanism_type(CK_UNAVAILABLE_INFORMATION), ); attrs.insert(CKA_LOCAL, Attr::CK_TRUE); attrs.insert(CKA_MODIFIABLE, Attr::CK_FALSE); attrs.insert(CKA_TOKEN, Attr::CK_TRUE); attrs.insert(CKA_ALWAYS_AUTHENTICATE, Attr::CK_FALSE); attrs.insert(CKA_SENSITIVE, Attr::CK_FALSE); attrs.insert(CKA_ALWAYS_SENSITIVE, Attr::CK_FALSE); attrs.insert(CKA_EXTRACTABLE, Attr::CK_FALSE); attrs.insert(CKA_NEVER_EXTRACTABLE, Attr::CK_TRUE); attrs.insert(CKA_PRIVATE, Attr::CK_FALSE); attrs.insert( CKA_CERTIFICATE_TYPE, Attr::from_ck_cert_type(cryptoki_sys::CKC_X_509), ); attrs.insert(CKA_VALUE, Attr::Bytes(cert_der)); attrs.insert(CKA_VALUE_LEN, Attr::from_ck_ulong(length as CK_ULONG)); attrs.insert( CKA_SUBJECT, Attr::Bytes(cert.tbs_certificate.subject.to_der().map_err(Error::Der)?), ); attrs.insert( CKA_ISSUER, Attr::Bytes(cert.tbs_certificate.issuer.to_der().map_err(Error::Der)?), ); attrs.insert( CKA_SERIAL_NUMBER, Attr::Bytes( cert.tbs_certificate .serial_number .to_der() .map_err(Error::Der)?, ), ); attrs.insert(CKA_TRUSTED, Attr::CK_TRUE); attrs.insert(CKA_CERTIFICATE_TYPE, Attr::from_ck_cert_type(CKC_X_509)); attrs.insert(CKA_CERTIFICATE_CATEGORY, Attr::from_ck_cert_category(0)); Ok(Object { attrs, kind: ObjectKind::Certificate, id: key_id.to_owned(), size: Some(length), mechanisms: vec![], }) } impl Object { pub fn attr(&self, attr_type: cryptoki_sys::CK_ATTRIBUTE_TYPE) -> Option<&Attr> { self.attrs.get(&attr_type) } pub fn fill_attr_template(&self, tpl: &mut CkRawAttrTemplate) -> Result<(), Pkcs11Error> { let mut result = Ok(()); for mut raw_attr in tpl.iter() { match self.attr(raw_attr.type_()) { Some(attr) => { let sres = match attr { Attr::Sensitive => { result = Err(Pkcs11Error::AttributeSensitive); raw_attr.set_unavailable(); continue; } a => raw_attr.set_val_bytes(a.as_bytes()), }; if matches!(sres, Err(attr::Error::BufTooSmall)) { result = Err(Pkcs11Error::BufferTooSmall); raw_attr.set_unavailable(); } } None => { // rcode = cryptoki_sys::CKR_CRYPTOKI_ALREADY_INITIALIZED; raw_attr.set_unavailable(); } }; debug!( "fill_attr_template: {:?} | result: {:?}", raw_attr.type_(), result ); } result } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/decrypt.rs000066400000000000000000000044251507371613300214220ustar00rootroot00000000000000use base64ct::{Base64, Encoding}; use log::trace; use nethsm_sdk_rs::apis::default_api; use super::{ db::Object, login::{self, LoginCtx}, mechanism::{MechMode, Mechanism}, Error, }; #[derive(Clone, Debug)] pub struct DecryptCtx { pub mechanism: Mechanism, pub key_id: String, pub data: Vec, } impl DecryptCtx { pub fn init(mechanism: Mechanism, key: &Object, login_ctx: &LoginCtx) -> Result { if !login_ctx.can_run_mode(crate::backend::login::UserMode::Operator) { return Err(Error::NotLoggedIn(login::UserMode::Operator)); } let api_mech = mechanism .to_api_mech(MechMode::Decrypt) .ok_or(Error::InvalidMechanismMode( MechMode::Decrypt, mechanism.clone(), ))?; if !key.mechanisms.contains(&api_mech) { return Err(Error::InvalidMechanism( (key.id.clone(), key.kind), mechanism, )); } Ok(Self { mechanism, key_id: key.id.clone(), data: Vec::new(), }) } pub fn update(&mut self, data: &[u8]) { self.data.extend_from_slice(data); } pub fn decrypt_final(&mut self, login_ctx: &LoginCtx) -> Result, Error> { if self.data.is_empty() { return Err(Error::InvalidEncryptedDataLength); } let b64_message = Base64::encode_string(self.data.as_slice()); let mode = self .mechanism .decrypt_name() .ok_or(Error::InvalidMechanismMode( MechMode::Decrypt, self.mechanism.clone(), ))?; trace!("Decrypt with mode: {mode:?}"); let iv = self .mechanism .iv() .map(|iv| Base64::encode_string(iv.as_slice())); let key_id = self.key_id.as_str(); let mut request = nethsm_sdk_rs::models::DecryptRequestData::new(mode, b64_message); request.iv = iv; let output = login_ctx.try_( |api_config| default_api::keys_key_id_decrypt_post(api_config, key_id, request), login::UserMode::Operator, )?; Ok(Base64::decode_vec(&output.entity.decrypted)?) } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/encrypt.rs000066400000000000000000000072501507371613300214330ustar00rootroot00000000000000use base64ct::{Base64, Encoding}; use log::{debug, trace}; use nethsm_sdk_rs::apis::default_api; use crate::backend::mechanism::MechMode; use crate::backend::ApiError; use super::Error; use super::{ db::Object, login::{self, LoginCtx}, mechanism::Mechanism, }; // we only handle AES-CBC for now that has a block size of 16 pub const ENCRYPT_BLOCK_SIZE: usize = 16; #[derive(Clone, Debug)] pub struct EncryptCtx { pub mechanism: Mechanism, pub key_id: String, pub data: Vec, } impl EncryptCtx { pub fn init(mechanism: Mechanism, key: &Object, login_ctx: &LoginCtx) -> Result { if !login_ctx.can_run_mode(crate::backend::login::UserMode::Operator) { return Err(Error::NotLoggedIn(login::UserMode::Operator)); } let api_mech = match mechanism.to_api_mech(MechMode::Encrypt) { Some(mech) => mech, None => { debug!("Tried to encrypt with an invalid mechanism: {mechanism:?}"); return Err(Error::InvalidMechanismMode(MechMode::Encrypt, mechanism)); } }; if !key.mechanisms.contains(&api_mech) { debug!("Tried to encrypt with an invalid mechanism: {mechanism:?}"); return Err(Error::InvalidMechanism( (key.id.clone(), key.kind), mechanism, )); } Ok(Self { mechanism, key_id: key.id.clone(), data: Vec::new(), }) } pub fn add_data(&mut self, data: &[u8]) { self.data.extend_from_slice(data); } pub fn get_biggest_chunk_len(&self) -> usize { let full_blocks = self.data.len() / ENCRYPT_BLOCK_SIZE; full_blocks * ENCRYPT_BLOCK_SIZE } pub fn encrypt_available_data(&mut self, login_ctx: &LoginCtx) -> Result, Error> { let chunk_size = self.get_biggest_chunk_len(); // if there is no data to encrypt, return an empty vector if chunk_size == 0 { return Ok(Vec::new()); } // drain the data to encrypt from the data vector let input_data = self.data.drain(..chunk_size).collect::>(); encrypt_data(&self.key_id, login_ctx, &input_data, &self.mechanism) } pub fn encrypt_final(&self, login_ctx: &LoginCtx) -> Result, Error> { encrypt_data( &self.key_id, login_ctx, self.data.as_slice(), &self.mechanism, ) } } fn encrypt_data( key_id: &str, login_ctx: &LoginCtx, data: &[u8], mechanism: &Mechanism, ) -> Result, Error> { let b64_message = Base64::encode_string(data); let mode = mechanism.encrypt_name().ok_or(Error::InvalidMechanismMode( MechMode::Encrypt, mechanism.clone(), ))?; trace!("Signing with mode: {mode:?}"); let iv = mechanism .iv() .map(|iv| Base64::encode_string(iv.as_slice())); trace!("iv: {iv:?}"); let mut request = nethsm_sdk_rs::models::EncryptRequestData::new(mode, b64_message); request.iv = iv; let output = login_ctx .try_( |api_config| default_api::keys_key_id_encrypt_post(api_config, key_id, request), login::UserMode::Operator, ) .map_err(|err| { if let Error::Api(ApiError::ResponseError(ref resp)) = err { if resp.status == 400 { if resp.content.contains("argument length") { return Error::InvalidDataLength; } return Error::InvalidData; } } err })?; Ok(Base64::decode_vec(&output.entity.encrypted)?) } nethsm-pkcs11-2.0.0/pkcs11/src/backend/events.rs000066400000000000000000000027741507371613300212610ustar00rootroot00000000000000use cryptoki_sys::CK_SLOT_ID; use nethsm_sdk_rs::{apis::default_api, models::SystemState}; use crate::data::{self, EVENTS_MANAGER, TOKENS_STATE}; use super::{login::LoginCtx, Pkcs11Error}; pub struct EventsManager { pub events: Vec, // list of slots that changed // Used when CKF_DONT_BLOCK is clear and C_Finalize is called, then every blocking call to C_WaitForSlotEvent should return CKR_CRYPTOKI_NOT_INITIALIZED pub finalized: bool, } impl EventsManager { pub const fn new() -> Self { EventsManager { events: Vec::new(), finalized: false, } } } pub fn update_slot_state(slot_id: CK_SLOT_ID, present: bool) { let mut tokens_state = TOKENS_STATE.lock().unwrap(); if let Some(prev) = tokens_state.get(&slot_id) { if *prev == present { return; } else { // new event EVENTS_MANAGER.write().unwrap().events.push(slot_id); } } tokens_state.insert(slot_id, present); } pub fn fetch_slots_state() -> Result<(), Pkcs11Error> { let device = data::load_device()?; for (index, slot) in device.slots.iter().enumerate() { let login_ctx = LoginCtx::new(slot.clone(), false, false); let status = login_ctx .try_(default_api::health_state_get, super::login::UserMode::Guest) .map(|state| state.entity.state == SystemState::Operational) .unwrap_or(false); update_slot_state(index as CK_SLOT_ID, status); } Ok(()) } nethsm-pkcs11-2.0.0/pkcs11/src/backend/key.rs000066400000000000000000000552271507371613300205460ustar00rootroot00000000000000use std::{collections::HashMap, sync::Mutex}; use super::{ db::{self, attr::CkRawAttrTemplate, Object}, login::{self, LoginCtx}, Error, }; use crate::backend::{self, db::object::ObjectKind, mechanism::Mechanism, ApiError}; use base64ct::{Base64, Encoding}; use config_file::CertificateFormat; use cryptoki_sys::{ CKA_CLASS, CKA_DECRYPT, CKA_EC_PARAMS, CKA_ENCRYPT, CKA_ID, CKA_KEY_TYPE, CKA_LABEL, CKA_MODULUS_BITS, CKA_PRIME_1, CKA_PRIME_2, CKA_PUBLIC_EXPONENT, CKA_SIGN, CKA_VALUE, CKA_VALUE_LEN, CKK_EC, CKK_EC_EDWARDS, CKK_GENERIC_SECRET, CKK_RSA, CK_KEY_TYPE, CK_OBJECT_CLASS, CK_OBJECT_HANDLE, CK_ULONG, }; use der::{oid::ObjectIdentifier, Decode}; use log::{debug, error, trace, warn}; use nethsm_sdk_rs::{ apis::default_api, models::{KeyGenerateRequestData, KeyItem, KeyPrivateData, KeyType as RawKeyType, PrivateKey}, }; // Exhaustive version of nethsm_sdk_rs::models::KeyType #[derive(Clone, Copy, PartialEq)] pub enum KeyType { Rsa, Ec(EcKeyType), Generic, } impl From for RawKeyType { fn from(ty: KeyType) -> Self { match ty { KeyType::Rsa => Self::Rsa, KeyType::Ec(ty) => ty.into(), KeyType::Generic => Self::Generic, } } } pub struct UnsupportedKeyTypeError; impl TryFrom for KeyType { type Error = UnsupportedKeyTypeError; fn try_from(ty: RawKeyType) -> Result { let ty = match ty { RawKeyType::Rsa => Self::Rsa, RawKeyType::Curve25519 => Self::Ec(EcKeyType::Curve25519), RawKeyType::EcP256 => Self::Ec(EcKeyType::EcP256), RawKeyType::EcP384 => Self::Ec(EcKeyType::EcP384), RawKeyType::EcP521 => Self::Ec(EcKeyType::EcP521), RawKeyType::EcP256K1 => Self::Ec(EcKeyType::EcP256K1), RawKeyType::BrainpoolP256 => Self::Ec(EcKeyType::BrainpoolP256), RawKeyType::BrainpoolP384 => Self::Ec(EcKeyType::BrainpoolP384), RawKeyType::BrainpoolP512 => Self::Ec(EcKeyType::BrainpoolP512), RawKeyType::Generic => Self::Generic, _ => { warn!("Unsupported key type: {ty:?}"); return Err(UnsupportedKeyTypeError); } }; Ok(ty) } } #[derive(Clone, Copy, PartialEq)] pub enum EcKeyType { Curve25519, EcP256, EcP384, EcP521, EcP256K1, BrainpoolP256, BrainpoolP384, BrainpoolP512, } impl EcKeyType { pub fn to_asn1(self) -> ObjectIdentifier { match self { Self::EcP256 => KEYTYPE_EC_P256, Self::EcP384 => KEYTYPE_EC_P384, Self::EcP521 => KEYTYPE_EC_P521, Self::Curve25519 => KEYTYPE_CURVE25519, Self::EcP256K1 => KEYTYPE_EC_P256_K1, Self::BrainpoolP256 => KEYTYPE_BRAINPOOL_P256, Self::BrainpoolP384 => KEYTYPE_BRAINPOOL_P384, Self::BrainpoolP512 => KEYTYPE_BRAINPOOL_P512, } } pub fn key_size(&self) -> usize { let size = match self { Self::EcP256 => 256, Self::EcP384 => 384, Self::EcP521 => 521, Self::Curve25519 => 255, Self::EcP256K1 => 256, Self::BrainpoolP256 => 256, Self::BrainpoolP384 => 384, Self::BrainpoolP512 => 512, }; size / 8 } } impl From for RawKeyType { fn from(ty: EcKeyType) -> Self { match ty { EcKeyType::Curve25519 => Self::Curve25519, EcKeyType::EcP256 => Self::EcP256, EcKeyType::EcP384 => Self::EcP384, EcKeyType::EcP521 => Self::EcP521, EcKeyType::EcP256K1 => Self::EcP256K1, EcKeyType::BrainpoolP256 => Self::BrainpoolP256, EcKeyType::BrainpoolP384 => Self::BrainpoolP384, EcKeyType::BrainpoolP512 => Self::BrainpoolP512, } } } impl TryFrom for EcKeyType { type Error = UnsupportedKeyTypeError; fn try_from(oid: ObjectIdentifier) -> Result { let ty = match oid { KEYTYPE_CURVE25519 => Self::Curve25519, KEYTYPE_EC_P256 => Self::EcP256, KEYTYPE_EC_P384 => Self::EcP384, KEYTYPE_EC_P521 => Self::EcP521, KEYTYPE_EC_P256_K1 => Self::EcP256K1, KEYTYPE_BRAINPOOL_P256 => Self::BrainpoolP256, KEYTYPE_BRAINPOOL_P384 => Self::BrainpoolP384, KEYTYPE_BRAINPOOL_P512 => Self::BrainpoolP512, _ => { warn!("Unsupported EC key type OID {oid}"); return Err(UnsupportedKeyTypeError); } }; Ok(ty) } } #[derive(Debug, PartialEq)] pub struct Id(String); impl Id { pub fn new(s: String) -> Result { // See https://github.com/Nitrokey/nethsm/blob/60b9b2c0caa609f53e50870451731c5803c4b724/src/keyfender/json.ml#L459-L472 const ALLOWED_CHARS: &[u8] = b"-_."; const MAX_LEN: usize = 128; if s.len() > MAX_LEN { warn!( "ID '{s}' is invalid: length is {} bytes (maximum: {MAX_LEN} bytes)", s.len(), ); return Err(InvalidIdError); } let (first, rest) = s.as_bytes().split_first().ok_or_else(|| { warn!("Empty IDs are invalid"); InvalidIdError })?; if !first.is_ascii_alphanumeric() { warn!("ID '{s}' is invalid: first character must be alphanumeric"); return Err(InvalidIdError); } if let Some(c) = rest .iter() .find(|c| !c.is_ascii_alphanumeric() && !ALLOWED_CHARS.contains(c)) { warn!("ID '{s}' is invalid: invalid character '{c}'"); return Err(InvalidIdError); } Ok(Self(s)) } } impl AsRef for Id { fn as_ref(&self) -> &str { &self.0 } } impl From for String { fn from(id: Id) -> String { id.0 } } impl TryFrom for Id { type Error = InvalidIdError; fn try_from(s: String) -> Result { Self::new(s) } } impl TryFrom> for Id { type Error = InvalidIdError; fn try_from(bytes: Vec) -> Result { String::from_utf8(bytes) .map_err(|err| { warn!("ID {:?} is invalid: not a UTF-8 string", err.into_bytes()); InvalidIdError }) .and_then(Id::try_from) } } #[derive(Debug, PartialEq)] pub struct InvalidIdError; #[derive(Debug, Default)] pub struct ParsedAttributes { pub id: Option, pub key_type: Option, pub sign: bool, pub encrypt: bool, pub decrypt: bool, pub key_class: Option, pub ec_params: Option>, pub value: Option>, pub public_exponent: Option>, pub prime_p: Option>, pub prime_q: Option>, pub value_len: Option, pub modulus_bits: Option, } pub fn parse_attributes(template: &CkRawAttrTemplate) -> Result { let mut parsed = ParsedAttributes::default(); for attr in template.iter() { let t = attr.type_(); match t { CKA_CLASS => match unsafe { attr.read_value::() } { Some(val) => { parsed.key_class = match ObjectKind::from(val) { ObjectKind::Other => { debug!("Class not supported: {val:?}"); None } k => Some(k), } } None => return Err(Error::InvalidAttribute(CKA_CLASS)), }, CKA_ID => { if let Some(bytes) = attr.val_bytes() { let id = Id::try_from(bytes.to_owned()) .map_err(|_| Error::InvalidAttribute(CKA_ID))?; parsed.id = Some(id.0); } } CKA_LABEL => { if let Some(bytes) = attr.val_bytes() { let id = Id::try_from(bytes.to_owned()) .map_err(|_| Error::InvalidAttribute(CKA_LABEL))?; trace!("label: {id:?}"); if parsed.id.is_none() { parsed.id = Some(id.0); } } } CKA_KEY_TYPE => { let ktype = match unsafe { attr.read_value::() } { Some(val) => val, None => return Err(Error::InvalidAttribute(CKA_KEY_TYPE)), }; parsed.key_type = Some(ktype); } CKA_EC_PARAMS => { parsed.ec_params = attr.val_bytes().map(|val| val.to_vec()); } CKA_VALUE => { parsed.value = attr.val_bytes().map(|val| val.to_vec()); } CKA_SIGN => { if let Some(val) = attr.val_bytes() { if val[0] == 1 { parsed.sign = true; } } } CKA_ENCRYPT => { if let Some(val) = attr.val_bytes() { if val[0] == 1 { parsed.encrypt = true; } } } CKA_DECRYPT => { if let Some(val) = attr.val_bytes() { if val[0] == 1 { parsed.decrypt = true; } } } CKA_PUBLIC_EXPONENT => { parsed.public_exponent = attr.val_bytes().map(|val| val.to_vec()); } CKA_PRIME_1 => { parsed.prime_p = attr.val_bytes().map(|val| val.to_vec()); } CKA_PRIME_2 => { parsed.prime_q = attr.val_bytes().map(|val| val.to_vec()); } CKA_VALUE_LEN => { parsed.value_len = unsafe { attr.read_value::() }; } CKA_MODULUS_BITS => { parsed.modulus_bits = unsafe { attr.read_value::() }; } _ => { debug!("Attribute not supported: {:?}", attr.type_()); } } } Ok(parsed) } fn upload_certificate( parsed_template: &ParsedAttributes, login_ctx: &LoginCtx, ) -> Result<(String, ObjectKind), Error> { let cert = parsed_template .value .as_ref() .ok_or(Error::MissingAttribute(CKA_VALUE))?; let id = parsed_template.id.clone().ok_or_else(|| { error!("A key ID is required"); Error::MissingAttribute(CKA_ID) })?; let certificate_format = login_ctx.slot().certificate_format; debug!("Uploading certificate, sending {certificate_format} encoding to the nethsm as per configuration"); let body = match certificate_format { CertificateFormat::Pem => { pem_rfc7468::encode_string("CERTIFICATE", pem_rfc7468::LineEnding::default(), cert) .map_err(Error::Pem)? .into_bytes() } CertificateFormat::Der => cert.clone(), }; let key_id = id.as_str(); login_ctx.try_( |api_config| default_api::keys_key_id_cert_put(api_config, key_id, body), login::UserMode::Administrator, )?; Ok((id, ObjectKind::Certificate)) } pub fn create_key_from_template( template: CkRawAttrTemplate, login_ctx: &LoginCtx, ) -> Result<(String, ObjectKind), Error> { let parsed = parse_attributes(&template)?; debug!("key_class: {:?}", parsed.key_class); debug!("key_type: {:?}", parsed.key_type); let key_class = if let Some(ref key_class) = parsed.key_class { let key_class = *key_class; if key_class == ObjectKind::Other || key_class == ObjectKind::PublicKey { // Supported object types are Certificates, Private keys and keypairs warn!("Creating object of class {key_class:?} is not supported by the nethsm"); return Err(Error::ObjectClassNotSupported); } key_class } else { return Err(Error::ObjectClassNotSupported); }; if key_class == ObjectKind::Certificate { return upload_certificate(&parsed, login_ctx); } let (r#type, key) = match parsed .key_type .ok_or(Error::InvalidAttribute(CKA_KEY_TYPE))? { CKK_RSA => { trace!("Creating RSA key"); let prime_p = Base64::encode_string(&parsed.prime_p.ok_or(Error::MissingAttribute(CKA_PRIME_1))?); let prime_q = Base64::encode_string(&parsed.prime_q.ok_or(Error::MissingAttribute(CKA_PRIME_2))?); let public_exponent = Base64::encode_string( &parsed .public_exponent .ok_or(Error::MissingAttribute(CKA_PUBLIC_EXPONENT))?, ); let mut key = KeyPrivateData::new(); key.prime_p = Some(prime_p); key.prime_q = Some(prime_q); key.public_exponent = Some(public_exponent); (KeyType::Rsa, key) } CKK_EC | CKK_EC_EDWARDS => { let ec_params = parsed .ec_params .ok_or(Error::MissingAttribute(CKA_EC_PARAMS))?; let oid: der::oid::ObjectIdentifier = der::oid::ObjectIdentifier::from_der(&ec_params) .map_err(|_| Error::InvalidAttribute(CKA_EC_PARAMS))?; let ec_type = EcKeyType::try_from(oid).map_err(|_| Error::InvalidAttribute(CKA_EC_PARAMS))?; let size = ec_type.key_size(); let mut value = parsed.value.ok_or(Error::MissingAttribute(CKA_VALUE))?; // add padding while value.len() < size { value.insert(0, 0); } let b64_private = Base64::encode_string(value.as_slice()); let mut key = KeyPrivateData::new(); key.data = Some(b64_private); (KeyType::Ec(ec_type), key) } CKK_GENERIC_SECRET => { let b64_private = Base64::encode_string( parsed .value .as_ref() .ok_or(Error::MissingAttribute(CKA_VALUE))? .as_slice(), ); let mut key = KeyPrivateData::new(); key.data = Some(b64_private); (KeyType::Generic, key) } _ => return Err(Error::InvalidAttribute(CKA_KEY_TYPE)), }; let mechs = Mechanism::from_key_type(r#type); let mut mechanisms = vec![]; for mech in mechs { if parsed.sign { if let Some(m) = mech.to_api_mech(super::mechanism::MechMode::Sign) { mechanisms.push(m.into()); } } if parsed.encrypt { if let Some(m) = mech.to_api_mech(super::mechanism::MechMode::Encrypt) { mechanisms.push(m.into()); } } if parsed.decrypt { if let Some(m) = mech.to_api_mech(super::mechanism::MechMode::Decrypt) { mechanisms.push(m.into()); } } } let private_key = PrivateKey::new(mechanisms, r#type.into(), key); let id = if let Some(id) = parsed.id { let key_id = id.as_str(); if let Err(err) = login_ctx.try_( |api_config| { default_api::keys_key_id_put( api_config, key_id, default_api::KeysKeyIdPutBody::ApplicationJson(private_key), ) }, login::UserMode::Administrator, ) { Err(err) } else { Ok(id) } } else { let resp = login_ctx.try_( |api_config| { default_api::keys_post( api_config, default_api::KeysPostBody::ApplicationJson(private_key), ) }, login::UserMode::Administrator, ); match resp { Ok(resp) => { let id = extract_key_id_location_header(resp.headers)?; Ok(id) } Err(err) => Err(err), } }?; Ok((id, key_class)) } const KEYTYPE_EC_P256: ObjectIdentifier = der::oid::db::rfc5912::SECP_256_R_1; const KEYTYPE_EC_P384: ObjectIdentifier = der::oid::db::rfc5912::SECP_384_R_1; const KEYTYPE_EC_P521: ObjectIdentifier = der::oid::db::rfc5912::SECP_521_R_1; const KEYTYPE_CURVE25519: ObjectIdentifier = der::oid::db::rfc8410::ID_ED_25519; const KEYTYPE_EC_P256_K1: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.132.0.10"); const KEYTYPE_BRAINPOOL_P256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.36.3.3.2.8.1.1.7"); const KEYTYPE_BRAINPOOL_P384: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.36.3.3.2.8.1.1.11"); const KEYTYPE_BRAINPOOL_P512: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.36.3.3.2.8.1.1.13"); fn key_type_from_params(params: &[u8]) -> Option { // decode der to ObjectIdentifier let oid: der::oid::ObjectIdentifier = der::oid::ObjectIdentifier::from_der(params).ok()?; EcKeyType::try_from(oid).ok().map(KeyType::Ec) } pub fn generate_key_from_template( template: &CkRawAttrTemplate, public_template: Option<&CkRawAttrTemplate>, mechanism: &Mechanism, login_ctx: &LoginCtx, db: &Mutex, ) -> Result, Error> { let parsed = parse_attributes(template)?; let parsed_public = public_template.map(parse_attributes).transpose()?; let api_mechs = mechanism.get_all_possible_api_mechs(); let length = parsed.value_len.or(parsed.modulus_bits).or(parsed_public .as_ref() .and_then(|p| p.value_len.or(p.modulus_bits))); trace!("length: {length:?}"); let mut key_type = mechanism.to_key_type(); if let Some(public) = parsed_public { if let Some(ec_params) = public.ec_params { key_type = key_type_from_params(&ec_params).ok_or(Error::InvalidAttribute(CKA_EC_PARAMS))?; } } let api_mechs = api_mechs.into_iter().map(From::from).collect(); let mut request = KeyGenerateRequestData::new(api_mechs, key_type.into()); request.id = parsed.id; request.length = length.map(|length| length as i32); let id = login_ctx.try_( |api_config| default_api::keys_generate_post(api_config, request), login::UserMode::Administrator, )?; let id = extract_key_id_location_header(id.headers)?; fetch_key(&id, login_ctx, db) } fn fetch_one_key(key_id: &str, login_ctx: &LoginCtx) -> Result, Error> { if !login_ctx.can_run_mode(super::login::UserMode::OperatorOrAdministrator) { return Err(Error::NotLoggedIn( super::login::UserMode::OperatorOrAdministrator, )); } let key_data = match login_ctx.try_( |api_config| default_api::keys_key_id_get(api_config, key_id), super::login::UserMode::OperatorOrAdministrator, ) { Ok(key_data) => key_data.entity, Err(err) => { debug!("Failed to fetch key {key_id}: {err:?}"); if matches!( err, Error::Api(ApiError::ResponseError(backend::ResponseContent { status: 404, .. })) ) { return Ok(vec![]); } return Err(err); } }; let objects = db::object::from_key_data(key_data, key_id)?; Ok(objects) } pub fn fetch_key( key_id: &str, login_ctx: &LoginCtx, db: &Mutex, ) -> Result, Error> { let objects = fetch_one_key(key_id, login_ctx)?; let mut db = db.lock()?; Ok(objects.into_iter().map(|o| db.add_object(o)).collect()) } fn fetch_one_certificate(key_id: &str, login_ctx: &LoginCtx) -> Result { if !login_ctx.can_run_mode(super::login::UserMode::OperatorOrAdministrator) { return Err(Error::NotLoggedIn( super::login::UserMode::OperatorOrAdministrator, )); } let cert_data = login_ctx.try_( |api_config| default_api::keys_key_id_cert_get(api_config, key_id), super::login::UserMode::OperatorOrAdministrator, )?; let object = db::object::from_cert_data( cert_data.entity, key_id, login_ctx.slot().certificate_format, )?; Ok(object) } pub fn fetch_certificate( key_id: &str, login_ctx: &LoginCtx, db: &Mutex, ) -> Result<(CK_OBJECT_HANDLE, Object), Error> { let object = fetch_one_certificate(key_id, login_ctx)?; let r = db.lock()?.add_object(object); Ok(r) } // get the id from the logation header value : // location: /api/v1/keys/?mechanisms=ECDSA_Signature fn extract_key_id_location_header(headers: HashMap) -> Result { let location_header = headers.get("location").ok_or(Error::InvalidData)?; let key_id = location_header .split('/') .next_back() .ok_or(Error::InvalidData)? .split('?') .next() .ok_or(Error::InvalidData)? .to_string(); Ok(key_id) } pub fn fetch_one( key: &KeyItem, login_ctx: &LoginCtx, kind: Option, ) -> Result, Error> { let mut acc = Vec::new(); if matches!( kind, None | Some(ObjectKind::Other) | Some(ObjectKind::PrivateKey) | Some(ObjectKind::PublicKey) | Some(ObjectKind::SecretKey) ) { acc = fetch_one_key(&key.id, login_ctx)?; } if matches!(kind, None | Some(ObjectKind::Certificate)) { match fetch_one_certificate(&key.id, login_ctx) { Ok(cert) => acc.push(cert), Err(err) => { debug!("Failed to fetch certificate: {err:?}"); } } } Ok(acc) } #[cfg(test)] mod tests { use super::{Id, InvalidIdError}; #[test] fn test_id_valid() { let valid_ids = ["keyID", "mykeyid", "test-key", "test_key", "test.key"]; for id in valid_ids { assert_eq!(Id::new(id.to_owned()), Ok(Id(id.to_owned()))); } } #[test] fn test_id_invalid() { let invalid_ids = [ "", "&*&*&*", "-key", ".key", "_key", "--", "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "schlüssel", "¾藏", ]; for id in invalid_ids { assert_eq!( Id::new(id.to_owned()), Err(InvalidIdError), "'{id}' should be rejected" ); } } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/login.rs000066400000000000000000000463101507371613300210570ustar00rootroot00000000000000use cryptoki_sys::{ CKS_RO_PUBLIC_SESSION, CKS_RW_SO_FUNCTIONS, CKS_RW_USER_FUNCTIONS, CKU_CONTEXT_SPECIFIC, CKU_SO, CKU_USER, CK_STATE, CK_USER_TYPE, }; use log::{debug, error, trace, warn}; use nethsm_sdk_rs::{ apis::{self, configuration::Configuration, default_api, ResponseContent}, models::UserRole, ureq, }; use std::{ sync::{atomic::Ordering::Relaxed, Arc}, thread, time::Duration, }; use crate::{ config::{ config_file::{RetryConfig, UserConfig}, device::{InstanceAttempt, InstanceData, Slot}, }, data::THREADS_ALLOWED, }; use super::{ApiError, Error, Pkcs11Error}; #[derive(Debug)] enum ShouldHealthCheck { /// The instance is ready to be used RunDirectly, /// The instance needs to first be health checked HealthCheckFirst, } impl ShouldHealthCheck { fn should_check(&self) -> bool { matches!(self, ShouldHealthCheck::HealthCheckFirst) } } #[derive(Debug, Clone, Copy)] enum HealthCheck { Possible, Avoid, } #[derive(Debug)] pub struct LoginCtx { slot: Arc, /// If set to `Some`, this will be used to replace the slot's default value when performing requests /// /// Set to `Some` by `C_Login` operator_login_override: Option, admin_login_override: Option, admin_allowed: bool, operator_allowed: bool, ck_state: CK_STATE, } #[derive(Debug, Clone)] pub enum LoginError { InvalidUser, UserNotPresent, BadArgument, IncorrectPin, } impl From for Pkcs11Error { fn from(val: LoginError) -> Self { match val { LoginError::InvalidUser => Self::UserTypeInvalid, LoginError::UserNotPresent => Self::UserTypeInvalid, LoginError::BadArgument => Self::ArgumentsBad, LoginError::IncorrectPin => Self::PinIncorrect, } } } impl From for Error { fn from(val: LoginError) -> Self { Error::Login(val) } } impl std::fmt::Display for LoginError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LoginError::InvalidUser => write!(f, "User type not supported"), LoginError::UserNotPresent => write!(f, "Username not cofigured for this user"), LoginError::BadArgument => write!(f, "Bad argument"), LoginError::IncorrectPin => write!(f, "Incorrect pin"), } } } /// Perform a health check with a timeout of 1 second fn health_check_get_timeout(instance: &InstanceData) -> bool { instance.clear_pool(); let config = &instance.config(); let uri_str = format!("{}/health/ready", config.base_path); let mut req = config .client .get(&uri_str) .config() .timeout_global(Some(Duration::from_secs(1))) .build(); if let Some(ref user_agent) = config.user_agent { req = req.header("user-agent", user_agent); } match req.call() { Ok(r) => { if r.status() == 200 { instance.clear_failed(); return true; } log::warn!("Failed retry {}", r.status()); instance.bump_failed(); false } Err(err) => { log::warn!("Failed retry {err:?}"); instance.bump_failed(); false } } } impl LoginCtx { pub fn new(slot: Arc, admin_allowed: bool, operator_allowed: bool) -> Self { let mut ck_state = CKS_RO_PUBLIC_SESSION; // CKS_RW_USER_FUNCTIONS has the priority, OpenDNSSEC checks for it if operator_allowed && slot.operator.is_some() { ck_state = CKS_RW_USER_FUNCTIONS; } else if admin_allowed && slot.administrator.is_some() { ck_state = CKS_RW_SO_FUNCTIONS } Self { slot, operator_login_override: None, admin_login_override: None, operator_allowed, admin_allowed, ck_state, } } pub fn slot(&self) -> &Arc { &self.slot } fn operator_config(&self) -> Option<&UserConfig> { if !self.operator_allowed { return None; } self.operator_login_override .as_ref() .or(self.slot.operator.as_ref()) } fn admin_config(&self) -> Option<&UserConfig> { if !self.admin_allowed { return None; } self.admin_login_override .as_ref() .or(self.slot.administrator.as_ref()) } pub fn login(&mut self, user_type: CK_USER_TYPE, pin: String) -> Result<(), LoginError> { trace!("Login as {user_type:?} with pin"); let (user_status, user_mode) = match user_type { CKU_CONTEXT_SPECIFIC => return Err(LoginError::InvalidUser), CKU_SO => { trace!("administrator: {:?}", self.slot.administrator); self.admin_login_override = match self.admin_config() { None => return Err(LoginError::UserNotPresent), Some(user) => Some(UserConfig { password: Some(pin), ..user.clone() }), }; self.admin_allowed = true; (UserStatus::Administrator, UserMode::Administrator) } CKU_USER => { self.operator_login_override = match self.operator_config() { None => return Err(LoginError::UserNotPresent), Some(user) => Some(UserConfig { password: Some(pin), ..user.clone() }), }; self.operator_allowed = true; (UserStatus::Operator, UserMode::Operator) } _ => return Err(LoginError::BadArgument), }; let got_user = self .try_(get_current_user_status, user_mode) .map_err(|err| { error!("Login check failed: {err:?}"); LoginError::UserNotPresent })?; if got_user == user_status { self.ck_state = match user_status { UserStatus::Operator => CKS_RW_USER_FUNCTIONS, UserStatus::Administrator => CKS_RW_SO_FUNCTIONS, UserStatus::LoggedOut => CKS_RO_PUBLIC_SESSION, }; Ok(()) } else { error!("Failed to login as {user_mode:?} with pin, got user {got_user:?}"); Err(LoginError::IncorrectPin) } } fn next_instance( &self, accept_health_check: HealthCheck, ) -> (&InstanceData, ShouldHealthCheck) { let threads_allowed = THREADS_ALLOWED.load(Relaxed); let index = self.slot.instance_balancer.fetch_add(1, Relaxed); let index = index % self.slot.instances.len(); let instance = &self.slot.instances[index]; match (instance.should_try(), threads_allowed, accept_health_check) { (InstanceAttempt::Failed, _, _) | (InstanceAttempt::Retry, true, _) | (InstanceAttempt::Retry, false, HealthCheck::Avoid) => {} (InstanceAttempt::Working, _, _) => return (instance, ShouldHealthCheck::RunDirectly), (InstanceAttempt::Retry, false, HealthCheck::Possible) => { return (instance, ShouldHealthCheck::HealthCheckFirst) } } for i in 0..self.slot.instances.len() - 1 { let instance = &self.slot.instances[index + i]; match (instance.should_try(), threads_allowed, accept_health_check) { (InstanceAttempt::Failed, _, _) | (InstanceAttempt::Retry, true, _) | (InstanceAttempt::Retry, false, HealthCheck::Avoid) => continue, (InstanceAttempt::Working, _, _) => { // This not true round-robin in case of multithreaded acces // This is degraded mode so best-effort is attempted at best self.slot.instance_balancer.fetch_add(i, Relaxed); return (instance, ShouldHealthCheck::RunDirectly); } (InstanceAttempt::Retry, false, HealthCheck::Possible) => { // This not true round-robin in case of multithreaded acces // This is degraded mode so best-effort is attempted at best self.slot.instance_balancer.fetch_add(i, Relaxed); return (instance, ShouldHealthCheck::HealthCheckFirst); } } } // No instance is valid, return a failed instance for an attempt let index = self.slot.instance_balancer.fetch_add(1, Relaxed); let index = index % self.slot.instances.len(); // Instance is not valid, don't try health check, it would only slow things down (&self.slot.instances[index], ShouldHealthCheck::RunDirectly) } fn operator( &self, accept_health_check: HealthCheck, ) -> Option<(InstanceData, ShouldHealthCheck)> { let (instance, should_health_check) = self.next_instance(accept_health_check); get_user_api_config(self.operator_config(), instance).map(|c| (c, should_health_check)) } fn administrator( &self, accept_health_check: HealthCheck, ) -> Option<(InstanceData, ShouldHealthCheck)> { let (instance, should_health_check) = self.next_instance(accept_health_check); get_user_api_config(self.admin_config(), instance).map(|c| (c, should_health_check)) } fn operator_or_administrator( &self, accept_health_check: HealthCheck, ) -> Option<(InstanceData, ShouldHealthCheck)> { self.operator(accept_health_check) .or_else(|| self.administrator(accept_health_check)) } fn guest(&self, accept_health_check: HealthCheck) -> (&InstanceData, ShouldHealthCheck) { self.next_instance(accept_health_check) } pub fn can_run_mode(&self, mode: UserMode) -> bool { if self.slot.instances.is_empty() { debug!("No instance configured"); return false; } // trace!("Checking if user can run mode: {:?}", mode); match mode { UserMode::Operator => user_is_valid(self.operator_config()), UserMode::Administrator => user_is_valid(self.admin_config()), UserMode::Guest => true, UserMode::OperatorOrAdministrator => { user_is_valid(self.operator_config()) || user_is_valid(self.admin_config()) } } } pub fn logout(&mut self) { self.ck_state = CKS_RO_PUBLIC_SESSION; } fn get_config_user_mode( &self, user_mode: &UserMode, accept_health_check: HealthCheck, ) -> Option<(InstanceData, ShouldHealthCheck)> { match user_mode { UserMode::Operator => self.operator(accept_health_check), UserMode::Administrator => self.administrator(accept_health_check), UserMode::Guest => { let (instance, should_health_check) = self.guest(accept_health_check); Some((instance.clone(), should_health_check)) } UserMode::OperatorOrAdministrator => { self.operator_or_administrator(accept_health_check) } } } // Try to run the api call on each instance until one succeeds pub fn try_(&self, api_call: F, user_mode: UserMode) -> Result where F: FnOnce(&Configuration) -> Result> + Clone, { let mut health_check_count = 0; // we loop for a maximum of instances.len() times let Some((mut instance, mut should_health_check)) = self.get_config_user_mode(&user_mode, HealthCheck::Possible) else { return Err(Error::Login(LoginError::UserNotPresent)); }; let mut retry_count = 0; let RetryConfig { count: retry_limit, delay_seconds, } = self.slot.retries.unwrap_or(RetryConfig { count: 0, delay_seconds: 0, }); let delay = Duration::from_secs(delay_seconds); loop { let accept_health_check = if health_check_count < 3 { HealthCheck::Possible } else { HealthCheck::Avoid }; if retry_count > retry_limit { error!( "Retry count exceeded after {retry_limit} attempts, instance is unreachable" ); return Err(ApiError::InstanceRemoved.into()); } if should_health_check.should_check() && !health_check_get_timeout(&instance) { health_check_count += 1; // Instance is not valid, we try the next one if let Some((new_instance, new_should_health_check)) = self.get_config_user_mode(&user_mode, accept_health_check) { instance = new_instance; should_health_check = new_should_health_check; } continue; } retry_count += 1; let api_call_clone = api_call.clone(); match api_call_clone(&instance.config()) { Ok(result) => { instance.clear_failed(); return Ok(result); } // If the server is in an unusable state, skip retries and try the next one Err(apis::Error::ResponseError(err @ ResponseContent { status: 500, .. })) | Err(apis::Error::ResponseError(err @ ResponseContent { status: 501, .. })) | Err(apis::Error::ResponseError(err @ ResponseContent { status: 502, .. })) | Err(apis::Error::ResponseError(err @ ResponseContent { status: 503, .. })) | Err(apis::Error::ResponseError(err @ ResponseContent { status: 412, .. })) => { instance.bump_failed(); warn!("Connection attempt {retry_count} failed: Status error connecting to the instance, {:?}, retrying in {delay_seconds}s", err.status); thread::sleep(delay); if let Some((new_instance, new_should_health_check)) = self.get_config_user_mode(&user_mode, accept_health_check) { instance = new_instance; should_health_check = new_should_health_check; } } // If the connection to the server failed with a network error, reconnecting might solve the issue // Err(apis::Error::Ureq(ureq::Error::Transport(err))) // if matches!( // err.kind(), // ureq::ErrorKind::Io | ureq::ErrorKind::ConnectionFailed // ) => // { Err(apis::Error::Ureq( err @ (ureq::Error::Io(_) | ureq::Error::ConnectionFailed | ureq::Error::Timeout(_) | ureq::Error::ConnectProxyFailed(_)), )) => { self.slot.clear_all_pools(); instance.bump_failed(); warn!("Connection attempt {retry_count} failed: IO error connecting to the instance, {err}, retrying in {delay_seconds}s"); thread::sleep(delay); if let Some((new_instance, new_should_health_check)) = self.get_config_user_mode(&user_mode, accept_health_check) { instance = new_instance; should_health_check = new_should_health_check; } } // Otherwise, return the error Err(err) => return Err(err.into()), } } } pub fn ck_state(&self) -> CK_STATE { self.ck_state } pub fn change_pin(&mut self, pin: String) -> Result<(), Pkcs11Error> { let options = match self.ck_state { CKS_RW_SO_FUNCTIONS => { let username = match self.admin_config() { Some(user) => user.username.clone(), None => return Err(Pkcs11Error::UserNotLoggedIn), }; (username, UserMode::Administrator) } CKS_RW_USER_FUNCTIONS => { let username = match self.operator_config() { Some(user) => user.username.clone(), None => return Err(Pkcs11Error::UserNotLoggedIn), }; (username, UserMode::Operator) } _ => return Err(Pkcs11Error::UserNotLoggedIn), }; self.try_( |config| { default_api::users_user_id_passphrase_post( config, &options.0, nethsm_sdk_rs::models::UserPassphrasePostData::new(pin), ) }, options.1, ) .map_err(|err| { error!("Failed to change pin: {err:?}"); Pkcs11Error::DeviceError })?; Ok(()) } } #[derive(Clone, Copy, Debug, PartialEq)] pub enum UserMode { Operator, Administrator, Guest, OperatorOrAdministrator, } #[derive(Clone, Debug, PartialEq)] pub enum UserStatus { Operator, Administrator, LoggedOut, } pub fn get_current_user_status( api_config: &nethsm_sdk_rs::apis::configuration::Configuration, ) -> Result> { let auth = match api_config.basic_auth.as_ref() { Some(auth) => auth, None => return Ok(UserStatus::LoggedOut), }; if auth.1.is_none() { return Ok(UserStatus::LoggedOut); } let user = default_api::users_user_id_get(api_config, auth.0.as_str())?; Ok(match user.entity.role { UserRole::Operator => UserStatus::Operator, UserRole::Administrator => UserStatus::Administrator, _ => UserStatus::LoggedOut, }) } // Check if the user is logged in and then return the configuration to connect as this user fn get_user_api_config( user: Option<&UserConfig>, api_config: &InstanceData, ) -> Option { let user = user?; #[allow(clippy::question_mark)] if user.password.is_none() { return None; } Some(api_config.with_custom_config(|config| Configuration { basic_auth: Some((user.username.clone(), user.password.clone())), ..config })) } fn user_is_valid(user: Option<&UserConfig>) -> bool { let Some(user) = user else { return false }; let Some(ref password) = user.password else { return false; }; !user.username.is_empty() && !password.is_empty() } #[cfg(test)] mod test { use super::*; #[test] fn test_user_is_valid() { let user = UserConfig { username: "test".to_string(), password: Some("password".to_string()), }; let empty_password_user = UserConfig { username: "test".to_string(), password: None, }; assert!(user_is_valid(Some(&user))); assert!(!user_is_valid(None)); assert!(!user_is_valid(Some(&empty_password_user))); } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/mechanism.rs000066400000000000000000000737171507371613300217260ustar00rootroot00000000000000// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright 2023 Nitrokey // SPDX-License-Identifier: Apache-2.0 use cryptoki_sys::{CKM_RSA_PKCS_OAEP, CK_MECHANISM_TYPE, CK_ULONG}; use hex_literal::hex; use log::trace; use nethsm_sdk_rs::models::{DecryptMode, EncryptMode, KeyMechanism as RawKeyMechanism, SignMode}; use super::key::{EcKeyType, KeyType}; // Exhaustive version of nethsm_sdk_rs::models::KeyMechanism #[derive(Clone, Copy, Debug, PartialEq)] pub enum KeyMechanism { RsaDecryptionRaw, RsaDecryptionPkcs1, RsaDecryptionOaepMd5, RsaDecryptionOaepSha1, RsaDecryptionOaepSha224, RsaDecryptionOaepSha256, RsaDecryptionOaepSha384, RsaDecryptionOaepSha512, RsaSignaturePkcs1, RsaSignaturePssMd5, RsaSignaturePssSha1, RsaSignaturePssSha224, RsaSignaturePssSha256, RsaSignaturePssSha384, RsaSignaturePssSha512, EdDsaSignature, EcdsaSignature, Bip340Signature, AesEncryptionCbc, AesDecryptionCbc, } impl From for RawKeyMechanism { fn from(mechanism: KeyMechanism) -> Self { match mechanism { KeyMechanism::RsaDecryptionRaw => Self::RsaDecryptionRaw, KeyMechanism::RsaDecryptionPkcs1 => Self::RsaDecryptionPkcs1, KeyMechanism::RsaDecryptionOaepMd5 => Self::RsaDecryptionOaepMd5, KeyMechanism::RsaDecryptionOaepSha1 => Self::RsaDecryptionOaepSha1, KeyMechanism::RsaDecryptionOaepSha224 => Self::RsaDecryptionOaepSha224, KeyMechanism::RsaDecryptionOaepSha256 => Self::RsaDecryptionOaepSha256, KeyMechanism::RsaDecryptionOaepSha384 => Self::RsaDecryptionOaepSha384, KeyMechanism::RsaDecryptionOaepSha512 => Self::RsaDecryptionOaepSha512, KeyMechanism::RsaSignaturePkcs1 => Self::RsaSignaturePkcs1, KeyMechanism::RsaSignaturePssMd5 => Self::RsaSignaturePssMd5, KeyMechanism::RsaSignaturePssSha1 => Self::RsaSignaturePssSha1, KeyMechanism::RsaSignaturePssSha224 => Self::RsaSignaturePssSha224, KeyMechanism::RsaSignaturePssSha256 => Self::RsaSignaturePssSha256, KeyMechanism::RsaSignaturePssSha384 => Self::RsaSignaturePssSha384, KeyMechanism::RsaSignaturePssSha512 => Self::RsaSignaturePssSha512, KeyMechanism::EdDsaSignature => Self::EdDsaSignature, KeyMechanism::EcdsaSignature => Self::EcdsaSignature, KeyMechanism::Bip340Signature => Self::Bip340Signature, KeyMechanism::AesEncryptionCbc => Self::AesEncryptionCbc, KeyMechanism::AesDecryptionCbc => Self::AesDecryptionCbc, } } } impl TryFrom for KeyMechanism { type Error = UnsupportedMechanismError; fn try_from(mechanism: RawKeyMechanism) -> Result { let mechanism = match mechanism { RawKeyMechanism::RsaDecryptionRaw => Self::RsaDecryptionRaw, RawKeyMechanism::RsaDecryptionPkcs1 => Self::RsaDecryptionPkcs1, RawKeyMechanism::RsaDecryptionOaepMd5 => Self::RsaDecryptionOaepMd5, RawKeyMechanism::RsaDecryptionOaepSha1 => Self::RsaDecryptionOaepSha1, RawKeyMechanism::RsaDecryptionOaepSha224 => Self::RsaDecryptionOaepSha224, RawKeyMechanism::RsaDecryptionOaepSha256 => Self::RsaDecryptionOaepSha256, RawKeyMechanism::RsaDecryptionOaepSha384 => Self::RsaDecryptionOaepSha384, RawKeyMechanism::RsaDecryptionOaepSha512 => Self::RsaDecryptionOaepSha512, RawKeyMechanism::RsaSignaturePkcs1 => Self::RsaSignaturePkcs1, RawKeyMechanism::RsaSignaturePssMd5 => Self::RsaSignaturePssMd5, RawKeyMechanism::RsaSignaturePssSha1 => Self::RsaSignaturePssSha1, RawKeyMechanism::RsaSignaturePssSha224 => Self::RsaSignaturePssSha224, RawKeyMechanism::RsaSignaturePssSha256 => Self::RsaSignaturePssSha256, RawKeyMechanism::RsaSignaturePssSha384 => Self::RsaSignaturePssSha384, RawKeyMechanism::RsaSignaturePssSha512 => Self::RsaSignaturePssSha512, RawKeyMechanism::EdDsaSignature => Self::EdDsaSignature, RawKeyMechanism::EcdsaSignature => Self::EcdsaSignature, RawKeyMechanism::Bip340Signature => Self::Bip340Signature, RawKeyMechanism::AesEncryptionCbc => Self::AesEncryptionCbc, RawKeyMechanism::AesDecryptionCbc => Self::AesDecryptionCbc, _ => { return Err(UnsupportedMechanismError); } }; Ok(mechanism) } } // from https://github.com/aws/aws-nitro-enclaves-acm/blob/main/src/vtok_p11/src/backend/mech.rs #[derive(Debug)] pub enum CkRawError { MechParamTypeMismatch, NullPtrDeref, } #[derive(Debug)] pub struct CkRawMechanism { ptr: *mut cryptoki_sys::CK_MECHANISM, } pub trait MechParams {} impl MechParams for cryptoki_sys::CK_RSA_PKCS_PSS_PARAMS {} impl MechParams for cryptoki_sys::CK_RSA_PKCS_OAEP_PARAMS {} impl MechParams for [cryptoki_sys::CK_BYTE; 16] {} impl CkRawMechanism { pub unsafe fn from_raw_ptr(ptr: *mut cryptoki_sys::CK_MECHANISM) -> Option { if ptr.is_null() { return None; } Some(Self { ptr }) } pub fn type_(&self) -> cryptoki_sys::CK_MECHANISM_TYPE { unsafe { (*self.ptr).mechanism } } // Note: marking this unsafe, even if it breaks our pattern of using object constructors // to cover unsafe FFI code. // Reading the wrong data type is bad, mkay? pub unsafe fn params(&self) -> Result, CkRawError> { let param_ptr = (*self.ptr).pParameter; let param_len = (*self.ptr).ulParameterLen; if param_ptr.is_null() || param_len == 0 { return Ok(None); } if std::mem::size_of::() != param_len as usize { return Err(CkRawError::MechParamTypeMismatch); } Ok(Some(std::ptr::read(param_ptr as *const T))) } pub fn len(&self) -> CK_ULONG { unsafe { (*self.ptr).ulParameterLen } } } #[derive(Debug)] pub enum Error { CkRaw(CkRawError), UnknownMech(CK_MECHANISM_TYPE), UnknownDigest(CK_MECHANISM_TYPE), } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match &self { Error::CkRaw(e) => write!(f, "CkRaw error: {e:?}"), Error::UnknownMech(t) => write!(f, "Unknown mechanism {t}"), Error::UnknownDigest(t) => write!(f, "Unknown digest {t}"), } } } #[derive(Clone, Copy, Debug, PartialEq)] pub enum MechDigest { Md5, Sha1, Sha224, Sha256, Sha384, Sha512, } impl MechDigest { pub fn from_ck_mech(mech: CK_MECHANISM_TYPE) -> Option { match mech { cryptoki_sys::CKM_MD5 => Some(Self::Md5), cryptoki_sys::CKM_SHA_1 => Some(Self::Sha1), cryptoki_sys::CKM_SHA224 => Some(Self::Sha224), cryptoki_sys::CKM_SHA256 => Some(Self::Sha256), cryptoki_sys::CKM_SHA384 => Some(Self::Sha384), cryptoki_sys::CKM_SHA512 => Some(Self::Sha512), _ => None, } } } pub type InitializationVector = Option<[u8; 16]>; #[derive(Clone, Debug, PartialEq)] pub enum Mechanism { // Digest(MechDigest), AesCbc(InitializationVector), RsaPkcs(Option), RsaPkcsOaep(MechDigest), RsaPkcsPss(MechDigest, bool), // (Hashing algorithm, pre-hashing needed) RsaX509, EdDsa, Ecdsa(Option), GenerateGeneric, GenerateAes, GenerateRsa, GenerateEc, GenerateEd, } pub struct UnsupportedMechanismError; impl TryFrom for Mechanism { type Error = UnsupportedMechanismError; fn try_from(value: KeyMechanism) -> Result { Ok(match value { KeyMechanism::AesDecryptionCbc => Self::AesCbc(None), KeyMechanism::AesEncryptionCbc => Self::AesCbc(None), KeyMechanism::EcdsaSignature => Self::Ecdsa(None), KeyMechanism::EdDsaSignature => Self::EdDsa, KeyMechanism::RsaDecryptionOaepMd5 => Self::RsaPkcsOaep(MechDigest::Md5), KeyMechanism::RsaDecryptionOaepSha1 => Self::RsaPkcsOaep(MechDigest::Sha1), KeyMechanism::RsaDecryptionOaepSha224 => Self::RsaPkcsOaep(MechDigest::Sha224), KeyMechanism::RsaDecryptionOaepSha256 => Self::RsaPkcsOaep(MechDigest::Sha256), KeyMechanism::RsaDecryptionOaepSha384 => Self::RsaPkcsOaep(MechDigest::Sha384), KeyMechanism::RsaDecryptionOaepSha512 => Self::RsaPkcsOaep(MechDigest::Sha512), KeyMechanism::RsaSignaturePssMd5 => Self::RsaPkcsPss(MechDigest::Md5, false), KeyMechanism::RsaSignaturePssSha1 => Self::RsaPkcsPss(MechDigest::Sha1, false), KeyMechanism::RsaSignaturePssSha224 => Self::RsaPkcsPss(MechDigest::Sha224, false), KeyMechanism::RsaSignaturePssSha256 => Self::RsaPkcsPss(MechDigest::Sha256, false), KeyMechanism::RsaSignaturePssSha384 => Self::RsaPkcsPss(MechDigest::Sha384, false), KeyMechanism::RsaSignaturePssSha512 => Self::RsaPkcsPss(MechDigest::Sha512, false), KeyMechanism::RsaDecryptionPkcs1 => Self::RsaPkcs(None), KeyMechanism::RsaDecryptionRaw => Self::RsaX509, KeyMechanism::RsaSignaturePkcs1 => Self::RsaPkcs(None), KeyMechanism::Bip340Signature => { return Err(UnsupportedMechanismError); } }) } } #[derive(Clone, Debug)] pub enum MechMode { Sign, Encrypt, Decrypt, } /// The token supported mechanisms and their capabilities. /// See PKCS#11 Mechanisms Specification Version 2.40 for details on how these /// mechanisms should behave. /// /// Mechanisms are split into single-part (i.e. sign()) and/or multi-part /// operations (i.e. sign_update() + sign_final()) depending on type and capabilities. impl Mechanism { const RSA_MIN_KEY_BITS: cryptoki_sys::CK_ULONG = 1024; const RSA_MAX_KEY_BITS: cryptoki_sys::CK_ULONG = 8192; const EC_MIN_KEY_BITS: cryptoki_sys::CK_ULONG = 256; const EC_MAX_KEY_BITS: cryptoki_sys::CK_ULONG = 521; const ED_MIN_KEY_BITS: cryptoki_sys::CK_ULONG = 256; const ED_MAX_KEY_BITS: cryptoki_sys::CK_ULONG = 256; pub fn from_key_type(key_type: KeyType) -> Vec { match key_type { KeyType::Generic => vec![Self::AesCbc(None)], KeyType::Rsa => vec![ Self::RsaPkcs(None), Self::RsaPkcsOaep(MechDigest::Md5), Self::RsaPkcsOaep(MechDigest::Sha1), Self::RsaPkcsOaep(MechDigest::Sha224), Self::RsaPkcsOaep(MechDigest::Sha256), Self::RsaPkcsOaep(MechDigest::Sha384), Self::RsaPkcsOaep(MechDigest::Sha512), Self::RsaPkcsPss(MechDigest::Md5, false), Self::RsaPkcsPss(MechDigest::Sha1, false), Self::RsaPkcsPss(MechDigest::Sha224, false), Self::RsaPkcsPss(MechDigest::Sha256, false), Self::RsaPkcsPss(MechDigest::Sha384, false), Self::RsaPkcsPss(MechDigest::Sha512, false), Self::RsaX509, ], KeyType::Ec(ty) => Self::from_ec_key_type(ty), } } pub fn from_ec_key_type(key_type: EcKeyType) -> Vec { match key_type { EcKeyType::EcP256 | EcKeyType::EcP384 | EcKeyType::EcP521 | EcKeyType::EcP256K1 | EcKeyType::BrainpoolP256 | EcKeyType::BrainpoolP384 | EcKeyType::BrainpoolP512 => { vec![Self::Ecdsa(None)] } EcKeyType::Curve25519 => vec![Self::EdDsa], } } pub fn to_key_type(&self) -> KeyType { match self { Self::AesCbc(_) | Self::GenerateAes | Self::GenerateGeneric => KeyType::Generic, Self::RsaPkcs(_) | Self::RsaPkcsOaep(_) | Self::RsaPkcsPss(_, _) | Self::RsaX509 | Self::GenerateRsa => KeyType::Rsa, // return a default one, the right size will be obtained from the OID in the template Self::Ecdsa(_) | Self::GenerateEc => KeyType::Ec(EcKeyType::EcP256), Self::EdDsa | Self::GenerateEd => KeyType::Ec(EcKeyType::Curve25519), } } pub fn get_all_possible_api_mechs(&self) -> Vec { match self { Self::AesCbc(_) | Self::GenerateAes | Self::GenerateGeneric => vec![ KeyMechanism::AesDecryptionCbc, KeyMechanism::AesEncryptionCbc, ], Self::RsaPkcs(_) | Self::RsaPkcsOaep(_) | Self::RsaPkcsPss(_, _) | Self::RsaX509 | Self::GenerateRsa => vec![ KeyMechanism::RsaDecryptionOaepMd5, KeyMechanism::RsaDecryptionOaepSha1, KeyMechanism::RsaDecryptionOaepSha224, KeyMechanism::RsaDecryptionOaepSha256, KeyMechanism::RsaDecryptionOaepSha384, KeyMechanism::RsaDecryptionOaepSha512, KeyMechanism::RsaSignaturePssMd5, KeyMechanism::RsaSignaturePssSha1, KeyMechanism::RsaSignaturePssSha224, KeyMechanism::RsaSignaturePssSha256, KeyMechanism::RsaSignaturePssSha384, KeyMechanism::RsaSignaturePssSha512, KeyMechanism::RsaDecryptionPkcs1, KeyMechanism::RsaDecryptionRaw, KeyMechanism::RsaSignaturePkcs1, ], Self::Ecdsa(_) | Self::GenerateEc => vec![KeyMechanism::EcdsaSignature], Self::EdDsa | Self::GenerateEd => vec![KeyMechanism::EdDsaSignature], } } pub fn to_api_mech(&self, mode: MechMode) -> Option { match mode { MechMode::Sign => match self { Self::AesCbc(_) => None, Self::RsaPkcs(_) => Some(KeyMechanism::RsaSignaturePkcs1), Self::RsaPkcsPss(digest, _) => match digest { MechDigest::Md5 => Some(KeyMechanism::RsaSignaturePssMd5), MechDigest::Sha1 => Some(KeyMechanism::RsaSignaturePssSha1), MechDigest::Sha224 => Some(KeyMechanism::RsaSignaturePssSha224), MechDigest::Sha256 => Some(KeyMechanism::RsaSignaturePssSha256), MechDigest::Sha384 => Some(KeyMechanism::RsaSignaturePssSha384), MechDigest::Sha512 => Some(KeyMechanism::RsaSignaturePssSha512), }, Self::RsaX509 => None, Self::Ecdsa(_) => Some(KeyMechanism::EcdsaSignature), Self::EdDsa => Some(KeyMechanism::EdDsaSignature), Self::RsaPkcsOaep(_) => None, _ => None, }, MechMode::Encrypt => match self { Self::AesCbc(_) => Some(KeyMechanism::AesEncryptionCbc), _ => None, }, MechMode::Decrypt => match self { Self::AesCbc(_) => Some(KeyMechanism::AesDecryptionCbc), Self::RsaX509 => Some(KeyMechanism::RsaDecryptionRaw), Self::RsaPkcs(_) => Some(KeyMechanism::RsaDecryptionPkcs1), Self::RsaPkcsOaep(digest) => match digest { MechDigest::Md5 => Some(KeyMechanism::RsaDecryptionOaepMd5), MechDigest::Sha1 => Some(KeyMechanism::RsaDecryptionOaepSha1), MechDigest::Sha224 => Some(KeyMechanism::RsaDecryptionOaepSha224), MechDigest::Sha256 => Some(KeyMechanism::RsaDecryptionOaepSha256), MechDigest::Sha384 => Some(KeyMechanism::RsaDecryptionOaepSha384), MechDigest::Sha512 => Some(KeyMechanism::RsaDecryptionOaepSha512), }, _ => None, }, } } pub fn from_ckraw_mech(raw_mech: &CkRawMechanism) -> Result { let mech = match raw_mech.type_() { cryptoki_sys::CKM_AES_KEY_GEN => Self::GenerateAes, cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN => Self::GenerateRsa, cryptoki_sys::CKM_EC_KEY_PAIR_GEN => Self::GenerateEc, cryptoki_sys::CKM_EC_EDWARDS_KEY_PAIR_GEN => Self::GenerateEd, cryptoki_sys::CKM_GENERIC_SECRET_KEY_GEN => Self::GenerateGeneric, cryptoki_sys::CKM_AES_CBC => { let params = unsafe { raw_mech.params::<[cryptoki_sys::CK_BYTE; 16]>() } .map_err(Error::CkRaw)?; let params = params.ok_or(Error::CkRaw(CkRawError::NullPtrDeref))?; Self::AesCbc(Some(params)) } cryptoki_sys::CKM_RSA_PKCS => Self::RsaPkcs(None), cryptoki_sys::CKM_SHA1_RSA_PKCS => Self::RsaPkcs(Some(MechDigest::Sha1)), cryptoki_sys::CKM_SHA224_RSA_PKCS => Self::RsaPkcs(Some(MechDigest::Sha224)), cryptoki_sys::CKM_SHA256_RSA_PKCS => Self::RsaPkcs(Some(MechDigest::Sha256)), cryptoki_sys::CKM_SHA384_RSA_PKCS => Self::RsaPkcs(Some(MechDigest::Sha384)), cryptoki_sys::CKM_SHA512_RSA_PKCS => Self::RsaPkcs(Some(MechDigest::Sha512)), cryptoki_sys::CKM_RSA_PKCS_PSS => { let params = unsafe { raw_mech.params::() } .map_err(Error::CkRaw)?; let params = params.ok_or(Error::CkRaw(CkRawError::NullPtrDeref))?; let hash_alg = params.hashAlg; trace!("params.hashAlg: {hash_alg:?}"); Self::RsaPkcsPss( MechDigest::from_ck_mech(params.hashAlg) .ok_or(Error::UnknownDigest(hash_alg))?, false, ) } cryptoki_sys::CKM_SHA1_RSA_PKCS_PSS => Self::RsaPkcsPss(MechDigest::Sha1, true), cryptoki_sys::CKM_SHA224_RSA_PKCS_PSS => Self::RsaPkcsPss(MechDigest::Sha224, true), cryptoki_sys::CKM_SHA256_RSA_PKCS_PSS => Self::RsaPkcsPss(MechDigest::Sha256, true), cryptoki_sys::CKM_SHA384_RSA_PKCS_PSS => Self::RsaPkcsPss(MechDigest::Sha384, true), cryptoki_sys::CKM_SHA512_RSA_PKCS_PSS => Self::RsaPkcsPss(MechDigest::Sha512, true), cryptoki_sys::CKM_RSA_PKCS_OAEP => { let params = unsafe { raw_mech.params::() } .map_err(Error::CkRaw)?; let params = params.ok_or(Error::CkRaw(CkRawError::NullPtrDeref))?; Self::RsaPkcsOaep( MechDigest::from_ck_mech(params.hashAlg) .ok_or(Error::UnknownDigest(params.hashAlg))?, ) } cryptoki_sys::CKM_RSA_X_509 => Self::RsaX509, cryptoki_sys::CKM_ECDSA => Self::Ecdsa(None), cryptoki_sys::CKM_ECDSA_SHA1 => Self::Ecdsa(Some(MechDigest::Sha1)), cryptoki_sys::CKM_ECDSA_SHA224 => Self::Ecdsa(Some(MechDigest::Sha224)), cryptoki_sys::CKM_ECDSA_SHA256 => Self::Ecdsa(Some(MechDigest::Sha256)), cryptoki_sys::CKM_ECDSA_SHA384 => Self::Ecdsa(Some(MechDigest::Sha384)), cryptoki_sys::CKM_ECDSA_SHA512 => Self::Ecdsa(Some(MechDigest::Sha512)), cryptoki_sys::CKM_EDDSA => Self::EdDsa, _ => return Err(Error::UnknownMech(raw_mech.type_())), }; Ok(mech) } pub fn ck_type(&self) -> cryptoki_sys::CK_MECHANISM_TYPE { match self { Self::AesCbc(_) => cryptoki_sys::CKM_AES_CBC, Self::RsaPkcs(digest) => match digest { Some(MechDigest::Sha1) => cryptoki_sys::CKM_SHA1_RSA_PKCS, Some(MechDigest::Sha224) => cryptoki_sys::CKM_SHA224_RSA_PKCS, Some(MechDigest::Sha256) => cryptoki_sys::CKM_SHA256_RSA_PKCS, Some(MechDigest::Sha384) => cryptoki_sys::CKM_SHA384_RSA_PKCS, Some(MechDigest::Sha512) => cryptoki_sys::CKM_SHA512_RSA_PKCS, _ => cryptoki_sys::CKM_RSA_PKCS, }, Self::RsaPkcsPss(digest, pre_hash) => { if *pre_hash { match digest { MechDigest::Sha1 => cryptoki_sys::CKM_SHA1_RSA_PKCS_PSS, MechDigest::Sha224 => cryptoki_sys::CKM_SHA224_RSA_PKCS_PSS, MechDigest::Sha256 => cryptoki_sys::CKM_SHA256_RSA_PKCS_PSS, MechDigest::Sha384 => cryptoki_sys::CKM_SHA384_RSA_PKCS_PSS, MechDigest::Sha512 => cryptoki_sys::CKM_SHA512_RSA_PKCS_PSS, _ => cryptoki_sys::CKM_RSA_PKCS_PSS, } } else { cryptoki_sys::CKM_RSA_PKCS_PSS } } Self::RsaPkcsOaep(_) => CKM_RSA_PKCS_OAEP, Self::RsaX509 => cryptoki_sys::CKM_RSA_X_509, Self::Ecdsa(None) => cryptoki_sys::CKM_ECDSA, Self::Ecdsa(Some(MechDigest::Sha1)) => cryptoki_sys::CKM_ECDSA_SHA1, Self::Ecdsa(Some(MechDigest::Sha224)) => cryptoki_sys::CKM_ECDSA_SHA224, Self::Ecdsa(Some(MechDigest::Sha256)) => cryptoki_sys::CKM_ECDSA_SHA256, Self::Ecdsa(Some(MechDigest::Sha384)) => cryptoki_sys::CKM_ECDSA_SHA384, Self::Ecdsa(Some(MechDigest::Sha512)) => cryptoki_sys::CKM_ECDSA_SHA512, // should not exist Self::Ecdsa(Some(MechDigest::Md5)) => cryptoki_sys::CKM_ECDSA, Self::EdDsa => cryptoki_sys::CKM_EDDSA, Self::GenerateAes => cryptoki_sys::CKM_AES_KEY_GEN, Self::GenerateRsa => cryptoki_sys::CKM_RSA_PKCS_KEY_PAIR_GEN, Self::GenerateEc => cryptoki_sys::CKM_EC_KEY_PAIR_GEN, Self::GenerateEd => cryptoki_sys::CKM_EC_EDWARDS_KEY_PAIR_GEN, Self::GenerateGeneric => cryptoki_sys::CKM_GENERIC_SECRET_KEY_GEN, } } pub fn ck_info(&self) -> cryptoki_sys::CK_MECHANISM_INFO { let (min_bits, max_bits) = match self { // Self::Digest(_) => (0, 0), Self::AesCbc(_) | Self::GenerateAes => (128, 256), Self::RsaPkcs(_) | Self::RsaPkcsPss(_, _) | Self::RsaX509 | Self::GenerateRsa => { (Self::RSA_MIN_KEY_BITS, Self::RSA_MAX_KEY_BITS) } Self::Ecdsa(_) | Self::GenerateEc => (Self::EC_MIN_KEY_BITS, Self::EC_MAX_KEY_BITS), Self::RsaPkcsOaep(_) => (Self::RSA_MIN_KEY_BITS, Self::RSA_MAX_KEY_BITS), Self::EdDsa | Self::GenerateEd => (Self::ED_MIN_KEY_BITS, Self::ED_MAX_KEY_BITS), Self::GenerateGeneric => (128, 256), }; cryptoki_sys::CK_MECHANISM_INFO { ulMinKeySize: min_bits, ulMaxKeySize: max_bits, flags: self.ck_flags(), } } // get the initialization vector for AES CBC pub fn iv(&self) -> Option<[u8; 16]> { match self { Self::AesCbc(Some(iv)) => Some(*iv), _ => None, } } pub fn ck_flags(&self) -> cryptoki_sys::CK_FLAGS { // NOTE: Though we have a soft-token, we stamp the cryptoki_sys::CKF_HW flag since most unit // tests out there seem to check for it cryptoki_sys::CKF_HW | match self { Self::GenerateGeneric => { cryptoki_sys::CKF_GENERATE | cryptoki_sys::CKF_GENERATE_KEY_PAIR | cryptoki_sys::CKF_DERIVE } Self::AesCbc(_) | Self::GenerateAes => { cryptoki_sys::CKF_ENCRYPT | cryptoki_sys::CKF_DECRYPT | cryptoki_sys::CKF_GENERATE } // Self::Digest(_) => cryptoki_sys::CKF_DIGEST, // Single-part CKM_RSA_PKCS also has encrypt/decrypt Self::RsaPkcs(_) | Self::GenerateRsa => { cryptoki_sys::CKF_SIGN | cryptoki_sys::CKF_DECRYPT | cryptoki_sys::CKF_ENCRYPT | cryptoki_sys::CKF_GENERATE_KEY_PAIR } // Multi-part CKM_RSA_PKCS has sign only Self::RsaPkcsPss(_, _) => cryptoki_sys::CKF_SIGN, // "RAW" RSA has decrypt only Self::RsaX509 => cryptoki_sys::CKF_DECRYPT, Self::Ecdsa(_) | Self::GenerateEc => { cryptoki_sys::CKF_SIGN | cryptoki_sys::CKF_EC_F_P | cryptoki_sys::CKF_EC_NAMEDCURVE | cryptoki_sys::CKF_EC_UNCOMPRESS | cryptoki_sys::CKF_GENERATE_KEY_PAIR } Self::RsaPkcsOaep(_) => cryptoki_sys::CKF_SIGN, Self::EdDsa | Self::GenerateEd => { cryptoki_sys::CKF_SIGN | cryptoki_sys::CKF_GENERATE_KEY_PAIR } } } /// return the digest to apply to the data before signing if needed /// and the PKCS#1v1.5 DigestInfo prefix if relevant /// /// Perfixes can be found in RFC 8017: https://datatracker.ietf.org/doc/html/rfc8017#page-47 /// pub fn internal_digest_and_prefix(&self) -> Option<(MechDigest, &'static [u8])> { match self { Self::RsaPkcs(None) => None, Self::RsaPkcs(Some(MechDigest::Md5)) => Some(( MechDigest::Md5, &hex!("3020300c06082a864886f70d020505000410"), )), Self::RsaPkcs(Some(MechDigest::Sha1)) => { Some((MechDigest::Sha1, &hex!("3021300906052b0e03021a05000414"))) } Self::RsaPkcs(Some(MechDigest::Sha224)) => Some(( MechDigest::Sha224, &hex!("302d300d06096086480165030402040500041c"), )), Self::RsaPkcs(Some(MechDigest::Sha256)) => Some(( MechDigest::Sha256, &hex!("3031300d060960864801650304020105000420"), )), Self::RsaPkcs(Some(MechDigest::Sha384)) => Some(( MechDigest::Sha384, &hex!("3041300d060960864801650304020205000430"), )), Self::RsaPkcs(Some(MechDigest::Sha512)) => Some(( MechDigest::Sha512, &hex!("3051300d060960864801650304020305000440"), )), Self::RsaPkcsPss(digest, pre_hash) => { if *pre_hash { Some((*digest, &[])) } else { None } } Self::Ecdsa(Some(digest)) => Some((*digest, &[])), Self::Ecdsa(None) => None, _ => None, } } /// returns the name to use in the api, None if not supported pub fn sign_name(&self) -> Option { match self { Self::RsaPkcs(_) => Some(SignMode::Pkcs1), Self::RsaPkcsPss(digest, _) => match digest { MechDigest::Md5 => Some(SignMode::PssSha1), MechDigest::Sha1 => Some(SignMode::PssSha1), MechDigest::Sha224 => Some(SignMode::PssSha224), MechDigest::Sha256 => Some(SignMode::PssSha256), MechDigest::Sha384 => Some(SignMode::PssSha384), MechDigest::Sha512 => Some(SignMode::PssSha512), }, Self::Ecdsa(_) => Some(SignMode::Ecdsa), Self::EdDsa => Some(SignMode::EdDsa), _ => None, } } pub fn encrypt_name(&self) -> Option { match self { Self::AesCbc(_) => Some(EncryptMode::AesCbc), _ => None, } } /// Returns the name to use in the api, None if not supported pub fn decrypt_name(&self) -> Option { match self { Self::AesCbc(_) => Some(DecryptMode::AesCbc), Self::RsaX509 => Some(DecryptMode::Raw), Self::RsaPkcs(_) => Some(DecryptMode::Pkcs1), Self::RsaPkcsOaep(digest) => match digest { MechDigest::Md5 => Some(DecryptMode::OaepMd5), MechDigest::Sha1 => Some(DecryptMode::OaepSha1), MechDigest::Sha224 => Some(DecryptMode::OaepSha224), MechDigest::Sha256 => Some(DecryptMode::OaepSha256), MechDigest::Sha384 => Some(DecryptMode::OaepSha384), MechDigest::Sha512 => Some(DecryptMode::OaepSha512), }, _ => None, } } // get the theoretical size of the key in bytes pub fn get_key_size(&self, key_size: Option) -> usize { match self { Self::RsaPkcs(_) => key_size.unwrap_or((Self::RSA_MAX_KEY_BITS / 8) as usize), Self::RsaPkcsPss(_, _) => key_size.unwrap_or((Self::RSA_MAX_KEY_BITS / 8) as usize), Self::Ecdsa(_) => { let s = key_size.unwrap_or((Self::EC_MAX_KEY_BITS / 8) as usize); if s == 65 { 66 // correct the division error for 521 } else { s } } Self::EdDsa => key_size.unwrap_or((Self::ED_MAX_KEY_BITS / 8) as usize), _ => (Self::RSA_MAX_KEY_BITS / 8) as usize, } } pub fn get_input_size(&self, key_size: Option) -> usize { match self { Self::RsaPkcs(_) => key_size.unwrap_or((Self::RSA_MAX_KEY_BITS / 8) as usize), Self::RsaPkcsPss(_, _) => key_size.unwrap_or((Self::RSA_MAX_KEY_BITS / 8) as usize), Self::Ecdsa(_) => { let s = key_size.unwrap_or((Self::EC_MAX_KEY_BITS / 8) as usize); if s == 65 { // p512 uses 64 bytes 64 } else { s } } Self::EdDsa => key_size.unwrap_or((Self::ED_MAX_KEY_BITS / 8) as usize), _ => (Self::RSA_MAX_KEY_BITS / 8) as usize, } } // get the theoretical size of the signature in bytes pub fn get_signature_size(&self, key_size: Option) -> usize { let key_size = self.get_key_size(key_size); match self { Self::RsaPkcs(_) => key_size, Self::RsaPkcsPss(_, _) => key_size, Self::Ecdsa(_) => key_size * 2, Self::EdDsa => key_size * 2, _ => key_size, } } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/mod.rs000066400000000000000000000251511507371613300205260ustar00rootroot00000000000000use std::sync::PoisonError; use self::{ db::object::ObjectKind, login::{LoginError, UserMode}, mechanism::{MechMode, Mechanism}, }; use cryptoki_sys::{CK_ATTRIBUTE_TYPE, CK_OBJECT_HANDLE, CK_RV}; use log::error; use nethsm_sdk_rs::apis; pub mod db; pub mod decrypt; pub mod encrypt; pub mod events; pub mod key; pub mod login; pub mod mechanism; pub mod object; pub mod session; pub mod sign; pub mod slot; macro_rules! pkcs11_error { ( $(#[$outer:meta])* $vis:vis enum $name:ident { $($var:ident = $val:expr),+ $(,)? } ) => { $(#[$outer])* $vis enum $name { $( $var, )* } impl From<$name> for ::cryptoki_sys::CK_RV { fn from(error: $name) -> Self { match error { $( $name::$var => $val, )* } } } } } pkcs11_error! { #[derive(Clone, Copy, Debug, PartialEq)] pub enum Pkcs11Error { ActionProhibited = cryptoki_sys::CKR_ACTION_PROHIBITED, ArgumentsBad = cryptoki_sys::CKR_ARGUMENTS_BAD, AttributeReadOnly = cryptoki_sys::CKR_ATTRIBUTE_READ_ONLY, AttributeSensitive = cryptoki_sys::CKR_ATTRIBUTE_SENSITIVE, AttributeValueInvalid = cryptoki_sys::CKR_ATTRIBUTE_VALUE_INVALID, BufferTooSmall = cryptoki_sys::CKR_BUFFER_TOO_SMALL, CantLock = cryptoki_sys::CKR_CANT_LOCK, CryptokiNotInitialized = cryptoki_sys::CKR_CRYPTOKI_NOT_INITIALIZED, DataInvalid = cryptoki_sys::CKR_DATA_INVALID, DataLenRange = cryptoki_sys::CKR_DATA_LEN_RANGE, DeviceError = cryptoki_sys::CKR_DEVICE_ERROR, DeviceRemoved = cryptoki_sys::CKR_DEVICE_REMOVED, EncryptedDataLenRange = cryptoki_sys::CKR_ENCRYPTED_DATA_LEN_RANGE, FunctionFailed = cryptoki_sys::CKR_FUNCTION_FAILED, FunctionNotParallel = cryptoki_sys::CKR_FUNCTION_NOT_PARALLEL, FunctionNotSupported = cryptoki_sys::CKR_FUNCTION_NOT_SUPPORTED, GeneralError = cryptoki_sys::CKR_GENERAL_ERROR, KeyHandleInvalid = cryptoki_sys::CKR_KEY_HANDLE_INVALID, MechanismInvalid = cryptoki_sys::CKR_MECHANISM_INVALID, NoEvent = cryptoki_sys::CKR_NO_EVENT, ObjectHandleInvalid = cryptoki_sys::CKR_OBJECT_HANDLE_INVALID, OperationActive = cryptoki_sys::CKR_OPERATION_ACTIVE, OperationNotInitialized = cryptoki_sys::CKR_OPERATION_NOT_INITIALIZED, PinIncorrect = cryptoki_sys::CKR_PIN_INCORRECT, SessionHandleInvalid = cryptoki_sys::CKR_SESSION_HANDLE_INVALID, SessionParallelNotSupported = cryptoki_sys::CKR_SESSION_PARALLEL_NOT_SUPPORTED, SlotIdInvalid = cryptoki_sys::CKR_SLOT_ID_INVALID, TemplateInconsistent = cryptoki_sys::CKR_TEMPLATE_INCONSISTENT, TokenNotPresent = cryptoki_sys::CKR_TOKEN_NOT_PRESENT, UserNotLoggedIn = cryptoki_sys::CKR_USER_NOT_LOGGED_IN, UserTypeInvalid = cryptoki_sys::CKR_USER_TYPE_INVALID, } } #[derive(Debug, Clone)] pub struct ResponseContent { pub status: u16, pub content: String, } #[derive(Debug)] pub enum ApiError { Ureq(String), Serde(serde_json::Error), Io(std::io::Error), ResponseError(ResponseContent), InstanceRemoved, NoInstance, StringParse(std::string::FromUtf8Error), } impl From> for ApiError { fn from(err: apis::Error) -> Self { match err { apis::Error::Ureq(e) => ApiError::Ureq(e.to_string()), apis::Error::Serde(e) => ApiError::Serde(e), apis::Error::Io(e) => ApiError::Io(e), apis::Error::ResponseError(resp) => ApiError::ResponseError(ResponseContent { status: resp.status, content: String::from_utf8(resp.content).unwrap_or_else(|e| { error!( "Unable to parse response content into string: {:?}", e.as_bytes() ); String::default() }), }), apis::Error::StringParse(e) => ApiError::StringParse(e), apis::Error::Multipart { field: _, error } => ApiError::Io(error), } } } #[derive(Debug)] pub enum Error { Der(der::Error), Pem(pem_rfc7468::Error), NotLoggedIn(UserMode), InvalidObjectHandle(CK_OBJECT_HANDLE), InvalidMechanism((String, ObjectKind), Mechanism), InvalidAttribute(CK_ATTRIBUTE_TYPE), MissingAttribute(CK_ATTRIBUTE_TYPE), ObjectClassNotSupported, InvalidMechanismMode(MechMode, Mechanism), Api(ApiError), Base64(base64ct::Error), StringParse(std::string::FromUtf8Error), Login(LoginError), OperationNotInitialized, LibraryNotInitialized, OperationActive, // a field recieved from the API is not valid KeyField(String), DbLock, InvalidDataLength, InvalidData, InvalidEncryptedDataLength, } impl From for Error { fn from(err: ApiError) -> Self { Error::Api(err) } } impl From> for Error { fn from(_: PoisonError) -> Self { Error::DbLock } } impl From> for Error { fn from(err: apis::Error) -> Self { Error::Api(err.into()) } } impl From for Error { fn from(err: base64ct::Error) -> Self { Error::Base64(err) } } impl From for Error { fn from(err: std::string::FromUtf8Error) -> Self { Error::StringParse(err) } } impl From for CK_RV { fn from(err: Error) -> Self { Pkcs11Error::from(err).into() } } impl From for Pkcs11Error { fn from(err: Error) -> Self { // diplay the error when converting to CK_RV error!("{err}"); match err { Error::Der(_) => Self::DeviceError, Error::Pem(_) => Self::DeviceError, Error::InvalidEncryptedDataLength => Self::EncryptedDataLenRange, Error::InvalidData => Self::DataInvalid, Error::InvalidDataLength => Self::DataLenRange, Error::InvalidObjectHandle(_) => Self::KeyHandleInvalid, Error::OperationNotInitialized => Self::OperationNotInitialized, Error::LibraryNotInitialized => Self::CryptokiNotInitialized, Error::DbLock => Self::DeviceError, Error::KeyField(_) => Self::DeviceError, Error::OperationActive => Self::OperationActive, Error::Login(e) => e.into(), Error::InvalidAttribute(_) => Self::AttributeValueInvalid, Error::ObjectClassNotSupported => Self::AttributeValueInvalid, Error::MissingAttribute(_) => Self::ArgumentsBad, Error::NotLoggedIn(_) => Self::UserNotLoggedIn, Error::InvalidMechanism(_, _) => Self::MechanismInvalid, Error::InvalidMechanismMode(_, _) => Self::MechanismInvalid, Error::Base64(_) | Error::StringParse(_) => Self::DeviceError, Error::Api(err) => match err { ApiError::NoInstance => Self::TokenNotPresent, ApiError::Ureq(_) => Self::DeviceError, ApiError::Io(_) => Self::DeviceError, ApiError::Serde(_) => Self::DeviceError, ApiError::ResponseError(resp) => match resp.status { 404 => Self::KeyHandleInvalid, 401 | 403 => Self::UserNotLoggedIn, 412 => Self::TokenNotPresent, _ => Self::DeviceError, }, ApiError::StringParse(_) => Self::DeviceError, ApiError::InstanceRemoved => Self::DeviceRemoved, }, } } } // display error impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let msg = match self { Error::Der(err) => format!("DER error: {err:?}"), Error::Pem(err) => format!("PEM error: {err:?}"), Error::InvalidEncryptedDataLength => "Invalid encrypted data length".to_string(), Error::InvalidData => "Invalid input data".to_string(), Error::InvalidDataLength => "Invalid input data length".to_string(), Error::InvalidObjectHandle(handle) => { format!("Object handle does not exist: {handle}") } Error::OperationNotInitialized => "Operation not initialized".to_string(), Error::LibraryNotInitialized => "Library not initialized".to_string(), Error::DbLock => "Internal mutex lock error".to_string(), Error::KeyField(field) => { format!("Key field {field} received from the NetHSM is not valid") } Error::OperationActive => "An operation is already active for this session".to_string(), Error::Login(err) => err.to_string(), Error::NotLoggedIn(mode) => { format!("The module needs to be logged in as {mode:?}, check the configuration") } Error::InvalidMechanism(obj, mech) => { format!( "The mechanism {:?} not supported for {:?} {}", mech, obj.1, obj.0 ) } Error::InvalidAttribute(attr) => format!("Invalid attribute: {attr:?}"), Error::MissingAttribute(attr) => format!("Missing attribute: {attr:?}"), Error::ObjectClassNotSupported => "Object class not supported".to_string(), Error::InvalidMechanismMode(mode, mechanism) => { format!("Unable to use mechanim {mechanism:?} for {mode:?}") } Error::Api(err) => match err { ApiError::NoInstance => "No valid instance in the slot".to_string(), ApiError::Ureq(err) => format!("Request error : {err}"), ApiError::Serde(err) => format!("Serde error: {err:?}"), ApiError::Io(err) => format!("IO error: {err:?}"), ApiError::ResponseError(resp) => match resp.status { 404 => "Key not found".to_string(), 401 | 403 => "Invalid credentials".to_string(), 412 => "The NetHSM is not set up properly".to_string(), _ => format!("Api error: {resp:?}"), }, ApiError::StringParse(err) => format!("String parse error: {err:?}"), ApiError::InstanceRemoved => "Failed to connect to instance".to_string(), }, Error::Base64(err) => format!("Base64 Decode error: {err:?}"), Error::StringParse(err) => format!("String parse error: {err:?}"), }; write!(f, "{msg}") } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/object.rs000066400000000000000000000114741507371613300212200ustar00rootroot00000000000000use cryptoki_sys::{CKA_CLASS, CKA_ID, CKA_LABEL, CK_OBJECT_CLASS, CK_SESSION_HANDLE}; use log::{debug, trace, warn}; use super::{ db::{ attr::{CkRawAttr, CkRawAttrTemplate}, object::ObjectKind, }, key::Id, session::Session, Error, }; // context to find objects #[derive(Clone, Debug)] pub struct EnumCtx { pub handles: Vec, index: usize, } #[derive(Clone, Debug)] pub struct KeyRequirements { pub kind: Option, pub id: Option, pub invalid_id: bool, } fn parse_key_requirements(template: Option) -> Result { match template { Some(template) => { let mut key_id = None; let mut kind = None; let mut invalid_id = false; for attr in template.iter() { debug!("attr {:?}: {:?}", attr.type_(), attr.val_bytes()); if attr.type_() == CKA_CLASS { kind = unsafe { attr.read_value::() }.map(ObjectKind::from) } if attr.type_() == CKA_ID { if let Some(bytes) = attr.val_bytes() { if let Ok(id) = Id::try_from(bytes.to_owned()) { key_id = Some(id.into()); } else { warn!("Invalid ID in key requirements"); invalid_id = true; } } } if attr.type_() == CKA_LABEL && key_id.is_none() { key_id = Some(parse_str_from_attr(&attr)?); } } Ok(KeyRequirements { kind, id: key_id, invalid_id, }) } None => Ok(KeyRequirements { kind: None, id: None, invalid_id: false, }), } } fn parse_str_from_attr(attr: &CkRawAttr) -> Result { let bytes = attr .val_bytes() .ok_or(Error::InvalidAttribute(attr.type_()))?; Ok(String::from_utf8(bytes.to_vec())?) } impl EnumCtx { pub fn enum_init( session: &mut Session, template: Option, ) -> Result { let key_req = parse_key_requirements(template)?; // Elements with a non-compliant ID are no longer supported let handles = if key_req.invalid_id { vec![] } else { session.find_key(key_req.id.as_deref(), key_req.kind)? }; Ok(EnumCtx::new(handles)) } pub fn new(handles: Vec) -> Self { Self { handles, index: 0 } } pub fn next_chunck(&mut self, chunk_size: usize) -> Vec { let mut result = Vec::new(); for _ in 0..chunk_size { trace!("index: {}", self.index); if let Some(handle) = self.handles.get(self.index) { result.push(*handle); self.index += 1; } else { break; } } result } } #[cfg(test)] mod tests { use cryptoki_sys::CK_ATTRIBUTE; use super::*; #[test] fn test_parse_key_requirements_none_template() -> Result<(), Error> { let template = None; let res = parse_key_requirements(template)?; assert_eq!(res.kind, None); assert_eq!(res.id, None); assert!(!res.invalid_id); Ok(()) } #[test] fn test_parse_key_requirements_non_utf8_id() -> Result<(), Error> { let mut bytes: Vec = vec![0x00, 0xFF, 0x00, 0xFF]; let mut attributes = vec![CK_ATTRIBUTE { type_: CKA_ID, pValue: bytes.as_mut_ptr() as *mut _, ulValueLen: 4, }]; let template = Some( unsafe { CkRawAttrTemplate::from_raw_ptr(attributes.as_mut_ptr(), 1) } .ok_or(Error::InvalidAttribute(CKA_ID))?, ); let res = parse_key_requirements(template)?; assert_eq!(res.kind, None); assert_eq!(res.id, None); assert!(res.invalid_id); Ok(()) } #[test] fn test_parse_key_requirements_id_from_label() -> Result<(), Error> { let mut bytes = "test".to_string().into_bytes(); let mut attributes = vec![CK_ATTRIBUTE { type_: CKA_LABEL, pValue: bytes.as_mut_ptr() as *mut _, ulValueLen: 4, }]; let template = Some( unsafe { CkRawAttrTemplate::from_raw_ptr(attributes.as_mut_ptr(), 1) } .ok_or(Error::InvalidAttribute(CKA_ID))?, ); let res = parse_key_requirements(template)?; assert_eq!(res.kind, None); assert_eq!(res.id, Some("test".to_string())); assert!(!res.invalid_id); Ok(()) } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/session.rs000066400000000000000000000564621507371613300214430ustar00rootroot00000000000000use std::{ collections::HashMap, sync::{atomic::Ordering, Arc, Condvar, Mutex, MutexGuard}, }; use cryptoki_sys::{ CKR_OK, CK_FLAGS, CK_OBJECT_HANDLE, CK_SESSION_HANDLE, CK_SESSION_INFO, CK_SLOT_ID, CK_USER_TYPE, }; use log::{debug, error, trace}; use nethsm_sdk_rs::{apis::default_api, models::MoveKeyRequest}; use crate::{ backend::{login::UserMode, Error, Pkcs11Error}, config::device::Slot, data::THREADS_ALLOWED, }; use super::{ db::{attr::CkRawAttrTemplate, object::ObjectKind, Db, Object}, decrypt::DecryptCtx, encrypt::EncryptCtx, key::{create_key_from_template, fetch_certificate, fetch_key, generate_key_from_template}, login::LoginCtx, mechanism::Mechanism, object::EnumCtx, sign::SignCtx, }; #[derive(Debug)] pub struct SessionManager { pub sessions: HashMap>>, pub next_session_handle: CK_SESSION_HANDLE, } impl SessionManager { pub fn new() -> Self { Self { sessions: HashMap::new(), next_session_handle: 1, } } pub fn create_session( &mut self, slot_id: CK_SLOT_ID, slot: Arc, flags: CK_FLAGS, ) -> CK_SESSION_HANDLE { let session = Session::new(slot_id, slot, flags); let handle = self.next_session_handle; self.sessions.insert(handle, Arc::new(Mutex::new(session))); self.next_session_handle += 1; handle } pub fn get_session(&self, handle: CK_SESSION_HANDLE) -> Option>> { trace!("Opening session {handle}"); self.sessions.get(&handle).cloned() } pub fn delete_session( &mut self, handle: CK_SESSION_HANDLE, ) -> Option<(CK_SESSION_HANDLE, Arc>)> { self.sessions.remove_entry(&handle) } pub fn delete_all_slot_sessions(&mut self, slot_id: CK_SLOT_ID) { let mut deleted_sessions = Vec::new(); self.sessions.iter().for_each(|(handle, session)| { if session.lock().unwrap().slot_id == slot_id { deleted_sessions.push(*handle); } }); for handle in deleted_sessions.iter() { self.sessions.remove(handle); } } // test only function to setup a session how we want it #[allow(dead_code)] #[cfg(test)] pub fn set_session(&mut self, handle: CK_SESSION_HANDLE, session: Session) { self.sessions.insert(handle, Arc::new(Mutex::new(session))); } // test only function to setup a blank session #[cfg(test)] pub fn setup_dummy_session(&mut self) -> cryptoki_sys::CK_SESSION_HANDLE { self.create_session( 0, Arc::new(Slot { administrator: None, retries: None, db: Arc::new((Mutex::new(Db::new()), Condvar::new())), _description: None, instances: Default::default(), label: "test".to_string(), operator: None, instance_balancer: Default::default(), certificate_format: config_file::CertificateFormat::Der, }), 0, ) } } #[derive(Debug)] pub struct Session { pub slot_id: CK_SLOT_ID, pub login_ctx: LoginCtx, pub flags: CK_FLAGS, pub device_error: Option, pub db: Arc<(Mutex, Condvar)>, pub sign_ctx: Option, pub encrypt_ctx: Option, pub decrypt_ctx: Option, pub enum_ctx: Option, } impl Session { pub fn new(slot_id: CK_SLOT_ID, slot: Arc, flags: CK_FLAGS) -> Self { let db = slot.db.clone(); let login_ctx = LoginCtx::new(slot, true, true); Self { login_ctx, slot_id, flags, db, device_error: None, sign_ctx: None, encrypt_ctx: None, decrypt_ctx: None, enum_ctx: None, } } pub fn get_ck_info(&self) -> CK_SESSION_INFO { let state = self.login_ctx.ck_state(); CK_SESSION_INFO { slotID: self.slot_id, state, flags: self.flags, ulDeviceError: self.device_error.map(From::from).unwrap_or(CKR_OK), } } pub fn login(&mut self, user_type: CK_USER_TYPE, pin: String) -> Result<(), Error> { Ok(self.login_ctx.login(user_type, pin)?) } // ignore logout for now pub fn logout(&mut self) -> Result<(), Error> { self.login_ctx.logout(); Ok(()) } pub fn enum_init(&mut self, template: Option) -> Result<(), Error> { if self.enum_ctx.is_some() { return Err(Error::OperationActive); } self.enum_ctx = Some(EnumCtx::enum_init(self, template)?); Ok(()) } pub fn enum_next_chunk(&mut self, count: usize) -> Result, Error> { match self.enum_ctx { Some(ref mut enum_ctx) => Ok(enum_ctx.next_chunck(count)), None => Err(Error::OperationNotInitialized), } } pub fn enum_final(&mut self) { self.enum_ctx = None; } pub fn sign_init( &mut self, mechanism: &Mechanism, key_handle: CK_OBJECT_HANDLE, ) -> Result<(), Error> { if self.sign_ctx.is_some() { return Err(Error::OperationActive); } trace!("sign_init() called with key handle {key_handle}"); trace!("sign_init() called with mechanism {mechanism:?}"); // get key id from the handle let key = { let db = self.db.0.lock()?; match db.object(key_handle) { Some(object) => Ok(object.clone()), None => { error!("Failed to get key: invalid handle"); Err(Error::InvalidObjectHandle(key_handle)) } } }?; self.sign_ctx = Some(SignCtx::init(mechanism.clone(), key, &self.login_ctx)?); Ok(()) } pub fn sign_theoretical_size(&self) -> Result { let sign_ctx = self .sign_ctx .as_ref() .ok_or(Error::OperationNotInitialized)?; Ok(sign_ctx.get_theoretical_size()) } pub fn sign_update(&mut self, data: &[u8]) -> Result<(), Error> { let sign_ctx = self .sign_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; sign_ctx.update(data); Ok(()) } pub fn sign_final(&mut self) -> Result, Error> { let sign_ctx = self .sign_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; sign_ctx.sign_final(&self.login_ctx) } pub fn sign(&mut self, data: &[u8]) -> Result, Error> { self.sign_update(data)?; self.sign_final() } pub fn sign_clear(&mut self) { self.sign_ctx = None; } pub fn encrypt_init( &mut self, mechanism: &Mechanism, key_handle: CK_OBJECT_HANDLE, ) -> Result<(), Error> { if self.encrypt_ctx.is_some() { return Err(Error::OperationActive); } // get key id from the handle let key = { let db = self.db.0.lock()?; match db.object(key_handle) { Some(object) => Ok(object.clone()), None => { error!("Failed to get key: invalid handle"); Err(Error::InvalidObjectHandle(key_handle)) } } }?; self.encrypt_ctx = Some(EncryptCtx::init(mechanism.clone(), &key, &self.login_ctx)?); Ok(()) } pub fn encrypt_add_data(&mut self, data: &[u8]) -> Result<(), Error> { let encrypt_ctx = self .encrypt_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; encrypt_ctx.add_data(data); Ok(()) } pub fn encrypt_available_data(&mut self) -> Result, Error> { let encrypt_ctx = self .encrypt_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; encrypt_ctx.encrypt_available_data(&self.login_ctx) } pub fn encrypt_update(&mut self, data: &[u8]) -> Result, Error> { self.encrypt_add_data(data)?; self.encrypt_available_data() } pub fn encrypt_get_theoretical_final_size(&self) -> Result { let encrypt_ctx = self .encrypt_ctx .as_ref() .ok_or(Error::OperationNotInitialized)?; Ok(encrypt_ctx.get_biggest_chunk_len()) } pub fn encrypt_final(&mut self) -> Result, Error> { let encrypt_ctx = self .encrypt_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; encrypt_ctx.encrypt_final(&self.login_ctx) } pub fn encrypt(&mut self, data: &[u8]) -> Result, Error> { self.encrypt_add_data(data)?; self.encrypt_final() } pub fn encrypt_clear(&mut self) { self.encrypt_ctx = None; } pub fn decrypt_init( &mut self, mechanism: &Mechanism, key_handle: CK_OBJECT_HANDLE, ) -> Result<(), Error> { if self.decrypt_ctx.is_some() { return Err(Error::OperationActive); } // get key id from the handle let key = { let db = self.db.0.lock()?; match db.object(key_handle) { Some(object) => Ok(object.clone()), None => { error!("Failed to get key: invalid handle"); Err(Error::InvalidObjectHandle(key_handle)) } } }?; self.decrypt_ctx = Some(DecryptCtx::init(mechanism.clone(), &key, &self.login_ctx)?); Ok(()) } // only adds data to the decrypt context, does not decrypt anything pub fn decrypt_update(&mut self, data: &[u8]) -> Result<(), Error> { let decrypt_ctx = self .decrypt_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; decrypt_ctx.update(data); Ok(()) } // For now we go safe and lazy and just return the same size as the input pub fn decrypt_theoretical_size(&self, input_size: usize) -> usize { input_size } pub fn decrypt_theoretical_final_size(&self) -> Result { let decrypt_ctx = self .decrypt_ctx .as_ref() .ok_or(Error::OperationNotInitialized)?; Ok(decrypt_ctx.data.len()) } pub fn decrypt_final(&mut self) -> Result, Error> { let decrypt_ctx = self .decrypt_ctx .as_mut() .ok_or(Error::OperationNotInitialized)?; decrypt_ctx.decrypt_final(&self.login_ctx) } pub fn decrypt(&mut self, data: &[u8]) -> Result, Error> { self.decrypt_update(data)?; self.decrypt_final() } pub fn decrypt_clear(&mut self) { self.decrypt_ctx = None; } pub fn rename_objects(&self, old_id: &str, new_id: &str) -> Result<(), Error> { self.login_ctx.try_( |api_config| { default_api::keys_key_id_move_post( api_config, old_id, MoveKeyRequest::new(new_id.to_owned()), ) }, crate::backend::login::UserMode::Administrator, )?; let mut db = self.db.0.lock().unwrap(); db.rename(old_id, new_id); Ok(()) } pub fn get_object(&self, handle: CK_OBJECT_HANDLE) -> Option { trace!("Accessing object {handle}"); let db = self.db.0.lock().unwrap(); db.object(handle).cloned() } pub(super) fn find_key( &mut self, id: Option<&str>, kind: Option, ) -> Result, Error> { let result = match id { Some(key_id) => { // try to search in the db first let mut results: Vec<(CK_OBJECT_HANDLE, Object)> = { let db = self.db.0.lock()?; db.iter() .filter(|(_, obj)| { obj.id == key_id && kind.map(|k| k == obj.kind).unwrap_or(true) }) .map(|(handle, obj)| (handle, obj.clone())) .collect() }; // then try to fetch from the server if results.is_empty() { if matches!( kind, None | Some(ObjectKind::Other) | Some(ObjectKind::PrivateKey) | Some(ObjectKind::PublicKey) | Some(ObjectKind::SecretKey) ) { results = fetch_key(key_id, &self.login_ctx, &self.db.0)?; } if (kind.is_none() && !results.is_empty()) || matches!(kind, Some(ObjectKind::Certificate)) { match fetch_certificate(key_id, &self.login_ctx, &self.db.0) { Ok(cert) => { trace!("Fetched certificate: {cert:?}"); results.push(cert); } Err(err) => { debug!("Failed to fetch certificate: {err:?}"); } } } } Ok(results) } None => self.fetch_all_keys(), }?; Ok(result .into_iter() .filter(|(_, obj)| { if let Some(kind) = kind { kind == obj.kind } else { true } }) .map(|(handle, _)| handle) .collect()) } fn fetch_all_keys(&mut self) -> Result, Error> { let condvar = &self.db.1; { let mut db = self.db.0.lock()?; if db.fetched_all_keys() { debug!("All keys already in cache. Returning"); return Ok(db .iter() .map(|(handle, obj)| (handle, obj.clone())) .collect()); } else if db.is_being_fetched() { debug!("Fetch in progress, waiting"); let mut db_inner = db; db_inner = condvar .wait_while(db_inner, |db| { debug!("Woken up"); db.is_being_fetched() }) .unwrap(); debug!("Waited for fetch"); // If for some reason the waiting did not lead to keys being fetched, refetch everything. if db_inner.fetched_all_keys() { return Ok(db_inner .iter() .map(|(handle, obj)| (handle, obj.clone())) .collect()); } db = db_inner; } // The cache is empty or the fetching failed, we are now the one fetching. db.set_is_being_fetched(true); debug!("Preparing to fetch"); } /// Drop the Condvar to notify on close struct NotifyAllGuard<'a>(Option<&'a (Mutex, Condvar)>); impl Drop for NotifyAllGuard<'_> { fn drop(&mut self) { if let Some(cv) = self.0 { cv.0.lock().unwrap().set_is_being_fetched(false); cv.1.notify_all(); } } } impl<'a> NotifyAllGuard<'a> { fn success(&mut self, mut lock: MutexGuard<'a, Db>) { let cv = self.0.take().unwrap(); lock.set_is_being_fetched(false); lock.set_fetched_all_keys(true); drop(lock); cv.1.notify_all(); } } let mut guard = NotifyAllGuard(Some(&self.db)); if !self .login_ctx .can_run_mode(super::login::UserMode::OperatorOrAdministrator) { return Err(Error::NotLoggedIn( super::login::UserMode::OperatorOrAdministrator, )); } let keys = self .login_ctx .try_( |api_config| default_api::keys_get(api_config, None), super::login::UserMode::OperatorOrAdministrator, )? .entity; let results: Result>, _> = if THREADS_ALLOWED.load(Ordering::Relaxed) { use rayon::prelude::*; keys.par_iter() .map(|k| super::key::fetch_one(k, &self.login_ctx, None)) .collect() } else { keys.iter() .map(|k| super::key::fetch_one(k, &self.login_ctx, None)) .collect() }; let results = results?; let mut db = self.db.0.lock()?; let handles = results .into_iter() .flatten() .map(|o| db.add_object(o)) .collect(); guard.success(db); Ok(handles) } pub fn create_object( &mut self, template: CkRawAttrTemplate, ) -> Result, Error> { if !self .login_ctx .can_run_mode(super::login::UserMode::Administrator) { return Err(Error::NotLoggedIn(super::login::UserMode::Administrator)); } let key_info = create_key_from_template(template, &self.login_ctx)?; let db = self.db.clone(); match key_info.1 { ObjectKind::Certificate => Ok(vec![fetch_certificate( &key_info.0, &self.login_ctx, &db.0, )?]), _ => fetch_key(&key_info.0, &self.login_ctx, &db.0), } } pub fn delete_object(&mut self, handle: CK_OBJECT_HANDLE) -> Result<(), Error> { trace!("Deleting object {handle}"); if !self.login_ctx.can_run_mode(UserMode::Administrator) { return Err(Error::NotLoggedIn(UserMode::Administrator)); } // get key id from the handle let key = { let db = self.db.0.lock()?; match db.object(handle) { Some(object) => Ok(object.clone()), None => Err(Error::InvalidObjectHandle(handle)), } }?; debug!("Deleting key {} {:?}", key.id, key.kind); let mut delete_related_objects = false; match key.kind { ObjectKind::Certificate => { self.login_ctx.try_( |api_config| default_api::keys_key_id_cert_delete(api_config, &key.id), crate::backend::login::UserMode::Administrator, )?; } ObjectKind::SecretKey | ObjectKind::PrivateKey => { self.login_ctx.try_( |api_config| default_api::keys_key_id_delete(api_config, &key.id), crate::backend::login::UserMode::Administrator, )?; delete_related_objects = true; } _ => { // we don't support deleting other objects } } let mut db = self.db.0.lock()?; let object = db .remove(handle) .ok_or(Error::InvalidObjectHandle(handle))?; if delete_related_objects { // certificate and public key objects for this key are no longer valid so we need to // manually remove them db.remove_objects_by_id(&object.id); } Ok(()) } pub fn generate_key( &self, template: &CkRawAttrTemplate, public_template: Option<&CkRawAttrTemplate>, mechanism: &Mechanism, ) -> Result, Error> { if !self.login_ctx.can_run_mode(UserMode::Administrator) { return Err(Error::NotLoggedIn(UserMode::Administrator)); } generate_key_from_template( template, public_template, mechanism, &self.login_ctx, &self.db.0, ) } } #[cfg(test)] mod test { use crate::{ backend::{ slot::{get_slot, init_for_tests}, ApiError, }, config::config_file::RetryConfig, }; use std::thread; use super::*; // Ignored by default due to network access // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn parrallel_fetch_all_keys() { let _guard = init_for_tests(); let slot = get_slot(0).unwrap(); let db = Arc::new((Mutex::new(Db::new()), Condvar::new())); let mut sessions = Vec::new(); for _ in 0..10 { let session = Session { db: db.clone(), decrypt_ctx: None, encrypt_ctx: None, sign_ctx: None, device_error: None, enum_ctx: None, flags: 0, login_ctx: LoginCtx::new(slot.clone(), false, true), slot_id: 0, }; sessions.push(session); } thread::scope(|s| { for session in &mut sessions { s.spawn(|| session.fetch_all_keys().unwrap()); } }) } // Ignored by default due to network access // Run with cargo test -- --test-threads=1 --ignored #[test] #[ignore] fn parrallel_fetch_all_keys_fail() { THREADS_ALLOWED.store(false, Ordering::Relaxed); let _guard = init_for_tests(); let mut slot = get_slot(0).unwrap(); let mut sessions = Vec::new(); for _ in 0..10 { let slot_mut = Arc::make_mut(&mut slot); slot_mut.db = Arc::new((Mutex::new(Db::new()), Condvar::new())); slot_mut.retries = Some(RetryConfig { count: 2, delay_seconds: 0, }); let bad_instance = &mut slot_mut.instances[0]; bad_instance .config_mut() .base_path .push_str("/corrupted_url"); let session = Session { db: slot_mut.db.clone(), decrypt_ctx: None, encrypt_ctx: None, sign_ctx: None, device_error: None, enum_ctx: None, flags: 0, login_ctx: LoginCtx::new(slot.clone(), false, true), slot_id: 0, }; sessions.push(session); } thread::scope(|s| { for session in &mut sessions { s.spawn(|| { match session.fetch_all_keys() { Err(Error::Api(ApiError::Ureq(r))) => { assert!( r.ends_with(": status code 404"), "expected 404 error, got {r}" ); } // FIXME: check for error 404 here Err(Error::Api(ApiError::Serde(_))) => {} res => panic!("{res:?}"), }; }); } }) } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/sign.rs000066400000000000000000000117021507371613300207040ustar00rootroot00000000000000use crate::backend::mechanism::MechDigest; use super::{ db::Object, login::{self, LoginCtx}, mechanism::{MechMode, Mechanism}, Error, }; use base64ct::{Base64, Encoding}; use der::Decode; use log::{debug, trace}; use nethsm_sdk_rs::{apis::default_api, models::SignMode}; use sha2::Digest; #[derive(Clone, Debug)] pub struct SignCtx { pub mechanism: Mechanism, pub sign_name: SignMode, pub key: Object, pub data: Vec, } impl SignCtx { pub fn init(mechanism: Mechanism, key: Object, login_ctx: &LoginCtx) -> Result { trace!("key_type: {:?}", key.kind); if !login_ctx.can_run_mode(crate::backend::login::UserMode::Operator) { return Err(Error::NotLoggedIn(login::UserMode::Operator)); } let sign_name = mechanism.sign_name().ok_or_else(|| { debug!("Tried to sign with an invalid mechanism: {mechanism:?}"); Error::InvalidMechanismMode(MechMode::Sign, mechanism.clone()) })?; let api_mech = match mechanism.to_api_mech(MechMode::Sign) { Some(mech) => mech, None => { debug!("Tried to sign with an invalid mechanism: {mechanism:?}"); return Err(Error::InvalidMechanismMode(MechMode::Sign, mechanism)); } }; trace!("Signing with mechanism: {mechanism:?}"); trace!("key mechanisms: {:?}", key.mechanisms); if !key.mechanisms.contains(&api_mech) { debug!("Tried to sign with an invalid mechanism for this key: {mechanism:?}"); return Err(Error::InvalidMechanism((key.id, key.kind), mechanism)); } Ok(Self { mechanism, key, sign_name, data: Vec::new(), }) } pub fn update(&mut self, data: &[u8]) { self.data.extend_from_slice(data); } pub fn sign_final(&self, login_ctx: &LoginCtx) -> Result, Error> { // helper function to hash the data with the correct algorithm fn hasher(data: &[u8], prefix: &'static [u8]) -> Vec { let mut hasher = D::new(); hasher.update(data); let mut res = Vec::from(prefix); res.extend_from_slice(&hasher.finalize()); res } let mut data = if let Some((digest, prefix)) = self.mechanism.internal_digest_and_prefix() { match digest { MechDigest::Md5 => hasher::(&self.data, prefix), MechDigest::Sha1 => hasher::(&self.data, prefix), MechDigest::Sha224 => hasher::(&self.data, prefix), MechDigest::Sha256 => hasher::(&self.data, prefix), MechDigest::Sha384 => hasher::(&self.data, prefix), MechDigest::Sha512 => hasher::(&self.data, prefix), } } else { self.data.clone() }; // with ecdsa we need to send the correct size, so we truncate/pad the data to the correct size if matches!(self.mechanism, Mechanism::Ecdsa(_)) { let size = self.mechanism.get_input_size(self.key.size); let mut out = vec![0; size]; let len = data.len().min(size); out[(size - len)..size].copy_from_slice(&data[..len]); data = out; } let b64_message = Base64::encode_string(data.as_slice()); let mode = self.sign_name; trace!("Signing with mode: {mode:?}"); let signature = login_ctx.try_( |conf| { default_api::keys_key_id_sign_post( conf, &self.key.id.clone(), nethsm_sdk_rs::models::SignRequestData::new(mode, b64_message), ) }, login::UserMode::Operator, )?; let mut output = Base64::decode_vec(&signature.entity.signature)?; // ECDSA signatures returned by the API are DER encoded, we need to remove the DER encoding if matches!(self.mechanism, Mechanism::Ecdsa(_)) { let size = self.mechanism.get_key_size(self.key.size); let sig: der::asn1::SequenceOf = der::asn1::SequenceOf::from_der(&output).map_err(Error::Der)?; let r = sig.get(0).ok_or(Error::InvalidData)?.as_bytes(); let s = sig.get(1).ok_or(Error::InvalidData)?.as_bytes(); let mut o = Vec::new(); if r.len() > size || s.len() > size { return Err(Error::InvalidData); } // copy with padding o.extend_from_slice(&vec![0; size - r.len()]); o.extend_from_slice(r); o.extend_from_slice(&vec![0; size - s.len()]); o.extend_from_slice(s); output = o; } Ok(output) } pub fn get_theoretical_size(&self) -> usize { self.mechanism.get_signature_size(self.key.size) } } nethsm-pkcs11-2.0.0/pkcs11/src/backend/slot.rs000066400000000000000000000016261507371613300207310ustar00rootroot00000000000000use std::sync::Arc; use cryptoki_sys::CK_SLOT_ID; use crate::{backend::Pkcs11Error, config::device::Slot, data}; pub fn get_slot(slot_id: CK_SLOT_ID) -> Result, Pkcs11Error> { let device = data::load_device()?; let slot_id = usize::try_from(slot_id).map_err(|_| Pkcs11Error::SlotIdInvalid)?; let slot = device .slots .get(slot_id) .ok_or(Pkcs11Error::SlotIdInvalid)?; Ok(slot.clone()) } #[cfg(test)] pub fn init_for_tests() -> std::sync::MutexGuard<'static, ()> { use std::{ptr, sync::Mutex}; use crate::{api::C_Initialize, data::DEVICE}; static MUTEX: Mutex<()> = Mutex::new(()); let guard = MUTEX.lock().unwrap_or_else(|err| err.into_inner()); if DEVICE.load().is_none() { std::env::set_var("P11NETHSM_CONFIG_FILE", "../p11nethsm.conf"); assert_eq!(C_Initialize(ptr::null_mut()), cryptoki_sys::CKR_OK); } guard } nethsm-pkcs11-2.0.0/pkcs11/src/config/000077500000000000000000000000001507371613300172535ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/config/config_file.rs000066400000000000000000000000301507371613300220560ustar00rootroot00000000000000pub use config_file::*; nethsm-pkcs11-2.0.0/pkcs11/src/config/device.rs000066400000000000000000000262211507371613300210630ustar00rootroot00000000000000use std::{ collections::BTreeMap, sync::{ atomic::AtomicUsize, mpsc::{self, RecvError, RecvTimeoutError}, Arc, Condvar, Mutex, RwLock, Weak, }, thread::{self, JoinHandle}, time::{Duration, Instant}, }; use arc_swap::ArcSwap; use config_file::CertificateFormat; use nethsm_sdk_rs::apis::{configuration::Configuration, default_api::health_ready_get}; use ureq::unversioned::{ resolver::{DefaultResolver, Resolver}, transport::{ConnectProxyConnector, Connector}, }; use crate::{ backend::db::Db, ureq::{rustls_connector::RustlsConnector, tcp_connector::TcpConnector}, }; use super::config_file::{RetryConfig, UserConfig}; #[allow(clippy::large_enum_variant)] pub enum RetryThreadMessage { FailedInstnace { retry_in: Duration, instance: InstanceData, }, } pub struct RetryChannel { tx: mpsc::Sender, background_thread: JoinHandle<()>, background_timer: JoinHandle<()>, } pub static RETRY_THREAD: RwLock> = RwLock::new(None); pub fn start_background_timer() { let (tx, rx) = mpsc::channel(); let (tx_instance, rx_instance) = mpsc::channel(); let background_thread = thread::spawn(background_thread(rx_instance)); let background_timer = thread::spawn(background_timer(rx, tx_instance)); *RETRY_THREAD.write().unwrap() = Some(RetryChannel { tx, background_thread, background_timer, }); } pub fn stop_background_timer() { let res = RETRY_THREAD.write().unwrap().take(); let Some(RetryChannel { tx, background_thread, background_timer, }) = res else { return; }; drop(tx); background_thread .join() .inspect_err(|err| { if let Some(err) = err.downcast_ref::<&'static str>() { log::error!("Background thread panicked: {err}"); } else if let Some(err) = err.downcast_ref::() { log::error!("Background thread panicked: {err}"); } else { log::error!("Background thread panicked: {err:?}"); } }) .ok(); background_timer .join() .inspect_err(|err| { if let Some(err) = err.downcast_ref::<&'static str>() { log::error!("Background timer panicked: {err}"); } else if let Some(err) = err.downcast_ref::() { log::error!("Background timer panicked: {err}"); } else { log::error!("Background timer panicked: {err:?}"); } }) .ok(); } fn background_timer( rx: mpsc::Receiver, tx_instance: mpsc::Sender, ) -> impl FnOnce() { let mut jobs: BTreeMap = BTreeMap::new(); move || loop { let next_job = jobs.pop_first(); let Some((next_job_deadline, next_job_instance)) = next_job else { // No jobs in the queue, we can just run the next match rx.recv() { Err(RecvError) => break, Ok(RetryThreadMessage::FailedInstnace { retry_in, instance }) => { jobs.insert(Instant::now() + retry_in, instance.into()); continue; } } }; let now = Instant::now(); if now >= next_job_deadline { if let Some(instance) = next_job_instance.upgrade() { tx_instance.send(instance).unwrap(); continue; } } else { jobs.insert(next_job_deadline, next_job_instance); } let timeout = next_job_deadline.duration_since(now); match rx.recv_timeout(timeout) { Ok(RetryThreadMessage::FailedInstnace { retry_in, instance }) => { jobs.insert(now + retry_in, instance.into()); continue; } Err(RecvTimeoutError::Timeout) => continue, Err(RecvTimeoutError::Disconnected) => break, } } } fn background_thread(rx: mpsc::Receiver) -> impl FnOnce() { move || { while let Ok(instance) = rx.recv() { instance.clear_pool(); match health_ready_get(&instance.config) { Ok(_) => instance.clear_failed(), Err(_) => instance.bump_failed(), } } } } // stores the global configuration of the module #[derive(Debug, Clone)] pub struct Device { pub slots: Vec>, } #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum InstanceState { #[default] Working, Failed { retry_count: u8, last_retry_at: Instant, }, } pub fn create_ureq_connector(tcp: TcpConnector, rustls: RustlsConnector) -> impl Connector { tcp.chain(rustls).chain(ConnectProxyConnector::default()) } pub fn create_ureq_resolver() -> impl Resolver { DefaultResolver::default() } impl InstanceState { pub fn new_failed() -> InstanceState { InstanceState::Failed { retry_count: 0, last_retry_at: Instant::now(), } } } #[derive(Debug, Clone)] pub struct InstanceData { pub agent: Arc>, pub agent_config: ureq::config::Config, pub tcp_connector: TcpConnector, pub rustls_connector: RustlsConnector, config: Configuration, pub state: Arc>, } impl InstanceData { pub fn new( agent: Arc>, agent_config: ureq::config::Config, tcp_connector: TcpConnector, rustls_connector: RustlsConnector, config: Configuration, state: Arc>, ) -> Self { Self { agent, agent_config, tcp_connector, rustls_connector, config, state, } } pub fn with_custom_config(&self, f: impl FnOnce(Configuration) -> Configuration) -> Self { Self { config: f(self.config()), ..self.clone() } } pub fn clear_pool(&self) { let agent = ureq::Agent::with_parts( self.agent_config.clone(), self.tcp_connector .clone() .chain(self.rustls_connector.clone()) .chain(ConnectProxyConnector::default()), DefaultResolver::default(), ); self.agent.swap(Arc::new(agent.clone())); } pub fn config(&self) -> Configuration { Configuration { client: (**self.agent.load()).clone(), ..self.config.clone() } } #[cfg(test)] pub fn config_mut(&mut self) -> &mut Configuration { &mut self.config } } #[derive(Debug, Clone)] pub struct WeakInstanceData { pub agent: Arc>, pub agent_config: ureq::config::Config, pub tcp_connector: TcpConnector, pub rustls_connector: RustlsConnector, config: Configuration, pub state: Weak>, } impl From for WeakInstanceData { fn from(value: InstanceData) -> Self { Self { agent: value.agent, agent_config: value.agent_config, tcp_connector: value.tcp_connector, rustls_connector: value.rustls_connector, config: value.config, state: Arc::downgrade(&value.state), } } } impl WeakInstanceData { fn upgrade(self) -> Option { let state = self.state.upgrade()?; Some(InstanceData { agent: self.agent, agent_config: self.agent_config, tcp_connector: self.tcp_connector, rustls_connector: self.rustls_connector, config: self.config, state, }) } } pub enum InstanceAttempt { /// The instance is in the failed state and should not be used Failed, /// The instance is in the failed state but a connection should be attempted Retry, /// The instance is in the working state Working, } impl InstanceData { pub fn should_try(&self) -> InstanceAttempt { let this = self.state.read().unwrap(); match *this { InstanceState::Working => InstanceAttempt::Working, InstanceState::Failed { retry_count, last_retry_at, } => { if last_retry_at.elapsed() < retry_duration_from_count(retry_count) { InstanceAttempt::Failed } else { InstanceAttempt::Retry } } } } pub fn clear_failed(&self) { *self.state.write().unwrap() = InstanceState::Working; } pub fn bump_failed(&self) { let mut write = self.state.write().unwrap(); let retry_count = match *write { InstanceState::Working => { *write = InstanceState::new_failed(); 0 } InstanceState::Failed { retry_count: prev_retry_count, last_retry_at, } => { // We only bump if it's a "real" retry. This is to avoid race conditions where // the same instance stops working when multiple threads are simultaneously connecting // to it if last_retry_at.elapsed() >= retry_duration_from_count(prev_retry_count) { let retry_count = prev_retry_count.saturating_add(1); *write = InstanceState::Failed { retry_count, last_retry_at: Instant::now(), }; retry_count } else { prev_retry_count } } }; drop(write); if let Some(t) = &*RETRY_THREAD.read().unwrap() { t.tx.send(RetryThreadMessage::FailedInstnace { retry_in: retry_duration_from_count(retry_count), instance: self.clone(), }) .ok(); } } } fn retry_duration_from_count(retry_count: u8) -> Duration { let secs = match retry_count { 0 | 1 => 1, 2 => 2, 3 => 5, 4 => 10, 5 => 60, 6.. => 60 * 5, }; Duration::from_secs(secs) } #[derive(Debug, Clone)] pub struct Slot { pub label: String, pub retries: Option, pub _description: Option, pub instances: Vec, pub operator: Option, pub administrator: Option, pub db: Arc<(Mutex, Condvar)>, pub instance_balancer: Arc, pub certificate_format: CertificateFormat, } impl Slot { // the user is connected if the basic auth is filled with an username and a password, otherwise the user will have to login pub fn is_connected(&self) -> bool { let Some(instance_data) = self.instances.first() else { return false; }; let Some(auth) = &instance_data.config.basic_auth else { return false; }; let Some(pwd) = &auth.1 else { return false }; !pwd.is_empty() } pub fn clear_all_pools(&self) { for instance in &self.instances { instance.clear_pool(); } } } nethsm-pkcs11-2.0.0/pkcs11/src/config/initialization.rs000066400000000000000000000332341507371613300226550ustar00rootroot00000000000000use std::{ path::PathBuf, sync::{Arc, Condvar, Mutex}, thread::available_parallelism, time::Duration, }; use crate::{ config::device::{create_ureq_connector, create_ureq_resolver, InstanceData}, ureq::{rustls_connector::RustlsConnector, tcp_connector::TcpConnector}, }; use super::{ config_file::{config_files, ConfigError, SlotConfig}, device::{Device, Slot}, }; use arc_swap::ArcSwap; use log::{debug, error, info, trace}; use nethsm_sdk_rs::ureq; use rustls::{ client::danger::ServerCertVerifier, crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}, }; use sha2::Digest; use ureq::tls::{TlsConfig, TlsProvider::Rustls}; const DEFAULT_USER_AGENT: &str = concat!("pkcs11-rs/", env!("CARGO_PKG_VERSION")); #[derive(Debug, thiserror::Error)] pub enum InitializationError { #[error("Failed to load config")] Config(crate::config::config_file::ConfigError), #[error("Failed to load certificates")] NoCerts, #[error("No operator or administrator for slot: {0}")] NoUser(String), #[error("No instance given for a slot")] NoInstance, } pub fn initialize_with_configs( configs: Result, PathBuf)>, ConfigError>, ) -> Result { // Use a closure called immediately so that `?` can be used let config_res = (|| { let configs_files = configs.map_err(InitializationError::Config)?; let config = crate::config::config_file::merge_configurations( configs_files.iter().map(|(data, _)| &**data), ) .map_err(InitializationError::Config)?; let file_paths: Vec = configs_files.into_iter().map(|(_, path)| path).collect(); Ok((config, file_paths)) })(); crate::config::logging::configure_logger(&config_res); let (config, _) = config_res?; info!("Loaded configuration with {} slots", config.slots.len()); // initialize the clients let mut slots = vec![]; for slot in config.slots.iter() { slots.push(Arc::new(slot_from_config(slot)?)); } Ok(Device { slots }) } pub fn initialize() -> Result { rustls::crypto::ring::default_provider() .install_default() .ok(); initialize_with_configs(config_files()) } #[derive(Debug)] struct DangerIgnoreVerifier; impl ServerCertVerifier for DangerIgnoreVerifier { fn verify_server_cert( &self, _end_entity: &rustls::pki_types::CertificateDer<'_>, _intermediates: &[rustls::pki_types::CertificateDer<'_>], _server_name: &rustls::pki_types::ServerName<'_>, _ocsp_response: &[u8], _now: rustls::pki_types::UnixTime, ) -> Result { Ok(rustls::client::danger::ServerCertVerified::assertion()) } fn verify_tls12_signature( &self, message: &[u8], cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { let default_provider = CryptoProvider::get_default().unwrap(); verify_tls12_signature( message, cert, dss, &default_provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, message: &[u8], cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { let default_provider = CryptoProvider::get_default().unwrap(); verify_tls13_signature( message, cert, dss, &default_provider.signature_verification_algorithms, ) } fn supported_verify_schemes(&self) -> Vec { let default_provider = CryptoProvider::get_default().unwrap(); default_provider .signature_verification_algorithms .supported_schemes() } } #[derive(Debug)] struct FingerprintVerifier { fingerprints: Vec>, } impl ServerCertVerifier for FingerprintVerifier { fn verify_server_cert( &self, end_entity: &rustls::pki_types::CertificateDer<'_>, _intermediates: &[rustls::pki_types::CertificateDer<'_>], _server_name: &rustls::pki_types::ServerName<'_>, _ocsp_response: &[u8], _now: rustls::pki_types::UnixTime, ) -> Result { let mut hasher = sha2::Sha256::new(); hasher.update(end_entity.as_ref()); let result = hasher.finalize(); for fingerprint in &self.fingerprints { if fingerprint == &*result { trace!("Certificate fingerprint matches"); return Ok(rustls::client::danger::ServerCertVerified::assertion()); } } Err(rustls::Error::General( "Could not verify certificate fingerprint".to_string(), )) } fn verify_tls12_signature( &self, message: &[u8], cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { let default_provider = CryptoProvider::get_default().unwrap(); verify_tls12_signature( message, cert, dss, &default_provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, message: &[u8], cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { let default_provider = CryptoProvider::get_default().unwrap(); verify_tls13_signature( message, cert, dss, &default_provider.signature_verification_algorithms, ) } fn supported_verify_schemes(&self) -> Vec { let default_provider = CryptoProvider::get_default().unwrap(); default_provider .signature_verification_algorithms .supported_schemes() } } fn slot_from_config(slot: &SlotConfig) -> Result { let default_user = slot .operator .as_ref() .or(slot.administrator.as_ref()) .ok_or(InitializationError::NoUser(slot.label.clone()))?; info!( "Slot with {} instances, timeout: {:?}, retries: {:?}", slot.instances.len(), slot.timeout_seconds, slot.retries ); let mut instances = Vec::new(); for instance in &slot.instances { let tls_conf = rustls::ClientConfig::builder(); let tls_conf = if instance.danger_insecure_cert { tls_conf .dangerous() .with_custom_certificate_verifier(Arc::new(DangerIgnoreVerifier)) .with_no_client_auth() } else if !instance.sha256_fingerprints.is_empty() { let fingerprints = instance .sha256_fingerprints .iter() .map(|f| f.value.clone()) .collect(); tls_conf .dangerous() .with_custom_certificate_verifier(Arc::new(FingerprintVerifier { fingerprints })) .with_no_client_auth() } else { let mut roots = rustls::RootCertStore::empty(); let native_certs = rustls_native_certs::load_native_certs(); if !native_certs.errors.is_empty() { error!( "Failed to load certificates: {}", native_certs .errors .iter() .map(|e| e.to_string()) .collect::() ); return Err(InitializationError::NoCerts); } let (added, failed) = roots.add_parsable_certificates(native_certs.certs); // panic!("{:?}", (added, failed)); debug!("Added {added} certifcates and failed to parse {failed} certificates"); if added == 0 { error!("Added no native certificates"); return Err(InitializationError::NoCerts); } tls_conf.with_root_certificates(roots).with_no_client_auth() }; info!( "Instance configured with: max_idle_connection: {:?}", instance.max_idle_connections ); let max_idle_connections = instance .max_idle_connections .or_else(|| available_parallelism().ok().map(Into::into)) .unwrap_or(100); // 100 idle connections is the default // By default there is 1 idle connection per host, but we are only connecting to 1 host. // So we need to allow the connection pool to scale to match the number of threads let mut builder = ureq::Agent::config_builder() .tls_config(TlsConfig::builder().provider(Rustls).build()) .max_idle_connections(max_idle_connections) .max_idle_connections_per_host(max_idle_connections); if let Some(t) = slot.timeout_seconds { builder = builder.timeout_global(Some(Duration::from_secs(t))); } let mut tcp_keepalive_time = None; let mut tcp_keepalive_retries = None; let mut tcp_keepalive_interval = None; if let Some(keepalive) = slot.tcp_keepalive { tcp_keepalive_time = Some(Duration::from_secs(keepalive.time_seconds)); tcp_keepalive_interval = Some(Duration::from_secs(keepalive.interval_seconds)); tcp_keepalive_retries = Some(keepalive.retries); } if let Some(max_idle_duration) = slot.connections_max_idle_duration { builder = builder.max_idle_age(Duration::from_secs(max_idle_duration)); } else { builder = builder.max_idle_age(Duration::MAX) } let tcp_connector = TcpConnector { tcp_keepalive_time, tcp_keepalive_retries, tcp_keepalive_interval, }; let rustls_connector = RustlsConnector { config: tls_conf.into(), }; let agent_config = builder.build(); let agent = ureq::Agent::with_parts( agent_config.clone(), create_ureq_connector(tcp_connector.clone(), rustls_connector.clone()), create_ureq_resolver(), ); let api_config = nethsm_sdk_rs::apis::configuration::Configuration { client: agent.clone(), base_path: instance.url.clone(), basic_auth: Some((default_user.username.clone(), default_user.password.clone())), user_agent: Some(DEFAULT_USER_AGENT.to_string()), ..Default::default() }; instances.push(InstanceData::new( Arc::new(ArcSwap::new(Arc::new(agent))), agent_config, tcp_connector, rustls_connector, api_config, Default::default(), )); } if instances.is_empty() { error!("Slot without any instance configured"); return Err(InitializationError::NoInstance); } Ok(Slot { _description: slot.description.clone(), label: slot.label.clone(), instances, administrator: slot.administrator.clone(), operator: slot.operator.clone(), retries: slot.retries, db: Arc::new((Mutex::new(crate::backend::db::Db::new()), Condvar::new())), instance_balancer: Default::default(), certificate_format: slot.certificate_format, }) } #[cfg(test)] mod tests { use super::*; use config_file::CertificateFormat; /// Test various good and bad configs for panics #[test] fn test_config_loading() { let config_content = r#" slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" administrator: username: "admin" password: "Administrator" instances: - url: "https://localhost:8443/api/v1" danger_insecure_cert: true sha256_fingerprints: - "31:92:8E:A4:5E:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" certificate_format: DER retries: count: 10 delay_seconds: 1 timeout_seconds: 10 "#; let config_path = "/path/to/config.conf"; let configs = vec![(config_content.into(), config_path.into())]; let device = initialize_with_configs(Ok(configs)).unwrap(); assert_eq!(device.slots[0].certificate_format, CertificateFormat::Der); let config_bad_fingerprint_content = r#" slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" administrator: username: "admin" password: "Administrator" instances: - url: "https://localhost:8443/api/v1" danger_insecure_cert: true sha256_fingerprints: - "31:92:8E:A4:5Eeeeee:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" retries: count: 10 delay_seconds: 1 timeout_seconds: 10 "#; let configs_bad_fingerprint = vec![(config_bad_fingerprint_content.into(), config_path.into())]; assert!(initialize_with_configs(Ok(configs_bad_fingerprint)).is_err()); let config_bad_yml_content = r#" dict: bad_yml "#; let configs_bad_yml = vec![(config_bad_yml_content.into(), config_path.into())]; assert!(initialize_with_configs(Ok(configs_bad_yml)).is_err()); } } nethsm-pkcs11-2.0.0/pkcs11/src/config/logging.rs000066400000000000000000000171441507371613300212560ustar00rootroot00000000000000use std::env; use std::path::PathBuf; use log::{info, warn, LevelFilter}; use syslog::{BasicLogger, Formatter3164}; use super::{config_file::P11Config, initialization::InitializationError}; pub struct MultiLog { syslog_logger: Option, env_logger: Option, } impl log::Log for MultiLog { fn enabled(&self, metadata: &log::Metadata) -> bool { self.syslog_logger .as_ref() .is_some_and(|logger| log::Log::enabled(logger, metadata)) || self .env_logger .as_ref() .is_some_and(|logger| log::Log::enabled(logger, metadata)) } fn log(&self, record: &log::Record) { if let Some(ref logger) = self.syslog_logger { log::Log::log(logger, record) }; if let Some(ref logger) = self.env_logger { log::Log::log(logger, record) }; } fn flush(&self) { if let Some(ref logger) = self.syslog_logger { log::Log::flush(logger) }; if let Some(ref logger) = self.env_logger { log::Log::flush(logger) }; } } // output to stdout, a file or syslog pub fn configure_logger(config: &Result<(P11Config, Vec), InitializationError>) { let Ok((config, file_paths)) = config else { let formatter = Formatter3164 { facility: syslog::Facility::LOG_USER, hostname: None, process: std::env::current_exe() .map(|p| p.into_os_string().to_string_lossy().into_owned()) .unwrap_or_else(|_| "NetHSM PKCS11 module".into()), pid: std::process::id(), }; let unix_logger = syslog::unix(formatter).map(BasicLogger::new).ok(); let env_logger = env_logger::Builder::from_default_env().build(); log::set_boxed_logger(Box::new(MultiLog { syslog_logger: unix_logger, env_logger: Some(env_logger), })) .ok(); log::set_max_level(log::LevelFilter::Info); return; }; // Warning messages for invalid configuration let mut messages = Vec::new(); // Info messages to log after logger is configured let mut info_messages = Vec::new(); for path in file_paths { info_messages.push(format!("Loaded config file at: {}", path.to_string_lossy())); } if config.syslog_socket.is_some() as u32 + config.syslog_tcp.is_some() as u32 + config.syslog_udp.is_some() as u32 > 1 { messages.push("Multiple syslog target selected".to_string()); } // The user asked for syslog let use_syslog_explicit = config.syslog_socket.is_some() || config.syslog_tcp.is_some() || config.syslog_udp.is_some() || config.syslog_facility.is_some() || config.syslog_hostname.is_some() || config.syslog_process.is_some() || config.syslog_pid.is_some(); // The user asked for env logging let use_file_explicit = config.log_file.is_some(); // Automatically enable a logger if the other is not enabled let use_syslog = use_syslog_explicit || !use_file_explicit; let use_file = use_file_explicit || !use_syslog_explicit; let mut syslog_logger = None; let mut env_logger = None; // syslog is used if neither syslog nor file is configured if use_syslog { let facility = config .syslog_facility .as_ref() .map(|f| match f.parse() { Ok(facility) => facility, Err(_) => { messages.push(format!( "Failed to parse {f} as facility. Defaulting to LOG_USER" )); Default::default() } }) .unwrap_or_default(); let process_name = || { let Ok(current_exe) = std::env::current_exe() else { return "Unknown".into(); }; current_exe.as_os_str().to_string_lossy().into_owned() }; let formatter = Formatter3164 { facility, hostname: config.syslog_hostname.clone(), process: config.syslog_process.clone().unwrap_or_else(process_name), pid: config.syslog_pid.unwrap_or_else(std::process::id), }; if let Some(path) = &config.syslog_socket { if let Ok(logger) = syslog::unix_custom(formatter, path) { syslog_logger = Some(BasicLogger::new(logger)); info_messages.push("Logging to Unix socket".into()); } else { messages.push("Could not open SYSLOG socket".into()); } } else if let Some(addr) = config.syslog_tcp { if let Ok(logger) = syslog::tcp(formatter, addr) { syslog_logger = Some(BasicLogger::new(logger)); info_messages.push("Logging to TCP".into()); } else { messages.push("Failed to create TCP logger".to_string()); } } else if let Some(udp_conf) = config.syslog_udp { if let Ok(logger) = syslog::udp(formatter, udp_conf.from_addr, udp_conf.to_addr) { syslog_logger = Some(BasicLogger::new(logger)); info_messages.push("Logging to UDP".into()); } else { messages.push("Failed to create UDP logger".to_string()); } } else { #[allow(clippy::collapsible_else_if)] if let Ok(logger) = syslog::unix(formatter) { syslog_logger = Some(BasicLogger::new(logger)); info_messages.push("Logging to standard Unix socket".into()); } else { messages.push("Failed to create standard unix logger".to_string()); } } } let mut log_level: LevelFilter = config .log_level .map(Into::into) .unwrap_or_else(log::max_level); if use_file { let mut builder = env_logger::Builder::new(); let path = &config.log_file.as_deref().unwrap_or("-".as_ref()); if path.as_os_str() == "-" { builder.target(env_logger::Target::Stderr); info_messages.push("Logging to STDERR".into()); } else { match std::fs::OpenOptions::new() .create(true) .append(true) .open(path) { Ok(file) => { // open the file for appending builder.target(env_logger::Target::Pipe(Box::new(file))); info_messages.push(format!( "Logging to File {}", path.as_os_str().to_string_lossy() )); } Err(err) => { messages.push(format!("Failed to open file for logging: {err}")); } }; } let mut override_filter = false; if let Ok(filters) = env::var("RUST_LOG") { builder.parse_filters(&filters); override_filter = true; } else if let Some(filter) = config.log_level { builder.filter_level(filter.into()); } if let Ok(format) = env::var("RUST_LOG_STYLE") { builder.parse_write_style(&format); } let tmp = builder.build(); if override_filter { log_level = tmp.filter(); } env_logger = Some(tmp); } log::set_max_level(log_level); log::set_boxed_logger(Box::new(MultiLog { syslog_logger, env_logger, })) .ok(); for m in info_messages { info!("{m}"); } for m in messages { warn!("{m}"); } } nethsm-pkcs11-2.0.0/pkcs11/src/config/mod.rs000066400000000000000000000001161507371613300203760ustar00rootroot00000000000000pub mod config_file; pub mod device; pub mod initialization; pub mod logging; nethsm-pkcs11-2.0.0/pkcs11/src/data.rs000066400000000000000000000131571507371613300172740ustar00rootroot00000000000000use std::collections::HashMap; use std::sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard, RwLock}; use crate::backend::{events::EventsManager, Pkcs11Error}; use crate::{ api, backend::session::{Session, SessionManager}, config::device::Device, }; use arc_swap::ArcSwapOption; use cryptoki_sys::{CK_FUNCTION_LIST, CK_SESSION_HANDLE, CK_SLOT_ID, CK_VERSION}; use lazy_static::lazy_static; use log::error; pub const DEVICE_VERSION: CK_VERSION = CK_VERSION { major: 2, minor: 40, }; pub static DEVICE: ArcSwapOption = ArcSwapOption::const_empty(); lazy_static! { pub static ref SESSION_MANAGER : Mutex = Mutex::new(SessionManager::new()); // Token present or not (true = present) pub static ref TOKENS_STATE : Mutex> = Mutex::new(HashMap::new()); } // Storage of events pub static EVENTS_MANAGER: RwLock = RwLock::new(EventsManager::new()); // If the calling application allows threads to be used pub static THREADS_ALLOWED: AtomicBool = AtomicBool::new(true); pub static mut FN_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST { version: DEVICE_VERSION, C_Initialize: Some(api::C_Initialize), C_Finalize: Some(api::C_Finalize), C_GetInfo: Some(api::C_GetInfo), C_GetFunctionList: Some(api::C_GetFunctionList), C_GetSlotList: Some(api::token::C_GetSlotList), C_GetSlotInfo: Some(api::token::C_GetSlotInfo), C_GetTokenInfo: Some(api::token::C_GetTokenInfo), C_GetMechanismList: Some(api::token::C_GetMechanismList), C_GetMechanismInfo: Some(api::token::C_GetMechanismInfo), C_InitToken: Some(api::token::C_InitToken), C_InitPIN: Some(api::pin::C_InitPIN), C_SetPIN: Some(api::pin::C_SetPIN), C_OpenSession: Some(api::session::C_OpenSession), C_CloseSession: Some(api::session::C_CloseSession), C_CloseAllSessions: Some(api::session::C_CloseAllSessions), C_GetSessionInfo: Some(api::session::C_GetSessionInfo), C_GetOperationState: Some(api::session::C_GetOperationState), C_SetOperationState: Some(api::session::C_SetOperationState), C_Login: Some(api::token::C_Login), C_Logout: Some(api::token::C_Logout), C_CreateObject: Some(api::object::C_CreateObject), C_CopyObject: Some(api::object::C_CopyObject), C_DestroyObject: Some(api::object::C_DestroyObject), C_GetObjectSize: Some(api::object::C_GetObjectSize), C_GetAttributeValue: Some(api::object::C_GetAttributeValue), C_SetAttributeValue: Some(api::object::C_SetAttributeValue), C_FindObjectsInit: Some(api::object::C_FindObjectsInit), C_FindObjects: Some(api::object::C_FindObjects), C_FindObjectsFinal: Some(api::object::C_FindObjectsFinal), C_EncryptInit: Some(api::encrypt::C_EncryptInit), C_Encrypt: Some(api::encrypt::C_Encrypt), C_EncryptUpdate: Some(api::encrypt::C_EncryptUpdate), C_EncryptFinal: Some(api::encrypt::C_EncryptFinal), C_DecryptInit: Some(api::decrypt::C_DecryptInit), C_Decrypt: Some(api::decrypt::C_Decrypt), C_DecryptUpdate: Some(api::decrypt::C_DecryptUpdate), C_DecryptFinal: Some(api::decrypt::C_DecryptFinal), C_DigestInit: Some(api::digest::C_DigestInit), C_Digest: Some(api::digest::C_Digest), C_DigestUpdate: Some(api::digest::C_DigestUpdate), C_DigestKey: Some(api::digest::C_DigestKey), C_DigestFinal: Some(api::digest::C_DigestFinal), C_SignInit: Some(api::sign::C_SignInit), C_Sign: Some(api::sign::C_Sign), C_SignUpdate: Some(api::sign::C_SignUpdate), C_SignFinal: Some(api::sign::C_SignFinal), C_SignRecoverInit: Some(api::sign::C_SignRecoverInit), C_SignRecover: Some(api::sign::C_SignRecover), C_VerifyInit: Some(api::verify::C_VerifyInit), C_Verify: Some(api::verify::C_Verify), C_VerifyUpdate: Some(api::verify::C_VerifyUpdate), C_VerifyFinal: Some(api::verify::C_VerifyFinal), C_VerifyRecoverInit: Some(api::verify::C_VerifyRecoverInit), C_VerifyRecover: Some(api::verify::C_VerifyRecover), C_DigestEncryptUpdate: Some(api::digest::C_DigestEncryptUpdate), C_DecryptDigestUpdate: Some(api::digest::C_DecryptDigestUpdate), C_SignEncryptUpdate: Some(api::sign::C_SignEncryptUpdate), C_DecryptVerifyUpdate: Some(api::decrypt::C_DecryptVerifyUpdate), C_GenerateKey: Some(api::generation::C_GenerateKey), C_GenerateKeyPair: Some(api::generation::C_GenerateKeyPair), C_WrapKey: Some(api::generation::C_WrapKey), C_UnwrapKey: Some(api::generation::C_UnwrapKey), C_DeriveKey: Some(api::generation::C_DeriveKey), C_SeedRandom: Some(api::generation::C_SeedRandom), C_GenerateRandom: Some(api::generation::C_GenerateRandom), C_GetFunctionStatus: Some(api::session::C_GetFunctionStatus), C_CancelFunction: Some(api::session::C_CancelFunction), C_WaitForSlotEvent: Some(api::token::C_WaitForSlotEvent), }; pub fn get_session(handle: CK_SESSION_HANDLE) -> Result>, Pkcs11Error> { let session_manager = SESSION_MANAGER.lock().map_err(|e| { error!("Failed to lock session manager: {e:?}"); Pkcs11Error::FunctionFailed })?; session_manager.get_session(handle).ok_or_else(|| { error!("Function called with invalid session handle {handle}"); Pkcs11Error::SessionHandleInvalid }) } pub fn lock_session(session: &Arc>) -> Result, Pkcs11Error> { session.lock().map_err(|e| { error!("Failed to lock session: {e:?}"); Pkcs11Error::FunctionFailed }) } pub fn load_device() -> Result, Pkcs11Error> { DEVICE.load_full().ok_or_else(|| { error!("Initialization was not performed or failed"); Pkcs11Error::CryptokiNotInitialized }) } nethsm-pkcs11-2.0.0/pkcs11/src/defs.rs000066400000000000000000000071371507371613300173050ustar00rootroot00000000000000use cryptoki_sys::{CK_VERSION, CRYPTOKI_VERSION_MAJOR}; use crate::backend::mechanism::Mechanism; pub const CRYPTOKI_VERSION: cryptoki_sys::CK_VERSION = cryptoki_sys::CK_VERSION { major: CRYPTOKI_VERSION_MAJOR, minor: cryptoki_sys::CRYPTOKI_VERSION_MINOR, }; #[allow(clippy::result_unit_err)] const fn parse_u8(s: &str) -> Result { let mut idx = 0; let mut acc = 0u8; while idx < s.len() { let Some(temp1) = acc.checked_mul(10u8) else { return Err(()); }; let Some(digit) = s.as_bytes()[idx].checked_sub(b'0') else { return Err(()); }; if digit >= 10 { return Err(()); } let Some(temp2) = temp1.checked_add(digit) else { return Err(()); }; acc = temp2; idx += 1; } Ok(acc) } pub const LIB_VERSION_MINOR: u8 = { match parse_u8(env!("CARGO_PKG_VERSION_MINOR")) { Ok(v) => v, Err(()) => panic!("Failed to parse minor version"), } }; pub const LIB_VERSION_MAJOR: u8 = { match parse_u8(env!("CARGO_PKG_VERSION_MAJOR")) { Ok(v) => v, Err(()) => panic!("Failed to parse major version"), } }; pub const LIB_VERSION: CK_VERSION = CK_VERSION { major: LIB_VERSION_MAJOR, minor: LIB_VERSION_MINOR, }; pub const LIB_DESCRIPTION: &str = { let v = "Nitrokey NetHsm PKCS#11 library"; // max length of libraryDescription in CK_INFO assert!(v.len() < 32); v }; pub const LIB_MANUFACTURER: &str = "Nitrokey"; pub const DEFAULT_FIRMWARE_VERSION: CK_VERSION = CK_VERSION { major: 0, minor: 1 }; pub const DEFAULT_HARDWARE_VERSION: CK_VERSION = CK_VERSION { major: 0, minor: 1 }; pub const MECHANISM_LIST: [Mechanism; 27] = [ Mechanism::AesCbc(None), Mechanism::RsaX509, Mechanism::RsaPkcs(None), Mechanism::RsaPkcs(Some(crate::backend::mechanism::MechDigest::Sha1)), Mechanism::RsaPkcs(Some(crate::backend::mechanism::MechDigest::Sha224)), Mechanism::RsaPkcs(Some(crate::backend::mechanism::MechDigest::Sha256)), Mechanism::RsaPkcs(Some(crate::backend::mechanism::MechDigest::Sha384)), Mechanism::RsaPkcs(Some(crate::backend::mechanism::MechDigest::Sha512)), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Md5, false), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Sha1, true), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Sha224, true), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Sha256, true), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Sha384, true), Mechanism::RsaPkcsPss(crate::backend::mechanism::MechDigest::Sha512, true), Mechanism::RsaPkcsOaep(crate::backend::mechanism::MechDigest::Md5), Mechanism::EdDsa, Mechanism::Ecdsa(None), Mechanism::Ecdsa(Some(crate::backend::mechanism::MechDigest::Sha1)), Mechanism::Ecdsa(Some(crate::backend::mechanism::MechDigest::Sha224)), Mechanism::Ecdsa(Some(crate::backend::mechanism::MechDigest::Sha256)), Mechanism::Ecdsa(Some(crate::backend::mechanism::MechDigest::Sha384)), Mechanism::Ecdsa(Some(crate::backend::mechanism::MechDigest::Sha512)), Mechanism::GenerateAes, Mechanism::GenerateRsa, Mechanism::GenerateEc, Mechanism::GenerateEd, Mechanism::GenerateGeneric, ]; #[cfg(test)] mod tests { use super::*; #[test] fn version_parsing() { assert_eq!( LIB_VERSION_MAJOR, env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap() ); assert_eq!( LIB_VERSION_MINOR, env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap() ); } } nethsm-pkcs11-2.0.0/pkcs11/src/lib.rs000066400000000000000000000002351507371613300171220ustar00rootroot00000000000000mod api; mod data; pub mod utils; mod backend; mod config; mod defs; mod ureq; #[cfg(panic = "abort")] mod unwind_stubs; #[macro_use] extern crate std; nethsm-pkcs11-2.0.0/pkcs11/src/unwind_stubs.rs000066400000000000000000000010631507371613300211000ustar00rootroot00000000000000#[no_mangle] pub extern "C" fn _Unwind_Resume() {} #[no_mangle] pub extern "C" fn _Unwind_GetTextRelBase() {} #[no_mangle] pub extern "C" fn _Unwind_GetIPInfo() {} #[no_mangle] pub extern "C" fn _Unwind_GetIP() {} #[no_mangle] pub extern "C" fn _Unwind_SetGR() {} #[no_mangle] pub extern "C" fn _Unwind_GetLanguageSpecificData() {} #[no_mangle] pub extern "C" fn _Unwind_Backtrace() {} #[no_mangle] pub extern "C" fn _Unwind_GetRegionStart() {} #[no_mangle] pub extern "C" fn _Unwind_SetIP() {} #[no_mangle] pub extern "C" fn _Unwind_GetDataRelBase() {} nethsm-pkcs11-2.0.0/pkcs11/src/ureq.rs000066400000000000000000000001001507371613300173170ustar00rootroot00000000000000pub mod rustls_connector; pub mod tcp_connector; pub mod utils; nethsm-pkcs11-2.0.0/pkcs11/src/ureq/000077500000000000000000000000001507371613300167625ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/src/ureq/rustls_connector.rs000066400000000000000000000074131507371613300227430ustar00rootroot00000000000000use std::convert::TryInto; use std::fmt; use std::io::{Read, Write}; use std::sync::Arc; use rustls::{ClientConfig, ClientConnection, StreamOwned}; use rustls_pki_types::ServerName; use ureq::tls::TlsProvider; use ureq::unversioned::transport::{Buffers, ConnectionDetails, Connector, LazyBuffers}; use ureq::unversioned::transport::{Either, NextTimeout, Transport, TransportAdapter}; use ureq::Error; use log::{debug, trace}; #[derive(Clone)] /// Wrapper for TLS using rustls. pub struct RustlsConnector { pub config: Arc, } impl Connector for RustlsConnector { type Out = Either>; fn connect( &self, details: &ConnectionDetails, chained: Option, ) -> Result, Error> { let Some(transport) = chained else { panic!("RustlConnector requires a chained transport"); }; // Only add TLS if we are connecting via HTTPS and the transport isn't TLS // already, otherwise use chained transport as is. if !details.needs_tls() || transport.is_tls() { trace!("Skip"); return Ok(Some(Either::A(transport))); } assert_eq!( details.config.tls_config().provider(), TlsProvider::Rustls, "Config must be set to rustls" ); trace!("Try wrap in TLS"); // Initialize the config on first run. let config = self.config.clone(); // cheap clone due to Arc let name_borrowed: ServerName<'_> = details .uri .authority() .expect("uri authority for tls") .host() .try_into() .map_err(|e| { debug!("rustls invalid dns name: {e}"); Error::Tls("Rustls invalid dns name error") })?; let name = name_borrowed.to_owned(); let conn = ClientConnection::new(config, name)?; let stream = StreamOwned { conn, sock: TransportAdapter::new(transport), }; let buffers = LazyBuffers::new( details.config.input_buffer_size(), details.config.output_buffer_size(), ); let transport = RustlsTransport { buffers, stream }; debug!("Wrapped TLS"); Ok(Some(Either::B(transport))) } } pub struct RustlsTransport { buffers: LazyBuffers, stream: StreamOwned>, } impl Transport for RustlsTransport { fn buffers(&mut self) -> &mut dyn Buffers { &mut self.buffers } fn transmit_output(&mut self, amount: usize, timeout: NextTimeout) -> Result<(), Error> { self.stream.get_mut().set_timeout(timeout); let output = &self.buffers.output()[..amount]; self.stream.write_all(output)?; Ok(()) } fn await_input(&mut self, timeout: NextTimeout) -> Result { if self.buffers.can_use_input() { return Ok(true); } self.stream.get_mut().set_timeout(timeout); let input = self.buffers.input_append_buf(); let amount = self.stream.read(input)?; self.buffers.input_appended(amount); Ok(amount > 0) } fn is_open(&mut self) -> bool { self.stream.get_mut().get_mut().is_open() } fn is_tls(&self) -> bool { true } } impl fmt::Debug for RustlsConnector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RustlsConnector").finish() } } impl fmt::Debug for RustlsTransport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RustlsTransport") .field("chained", &self.stream.sock.inner()) .finish() } } nethsm-pkcs11-2.0.0/pkcs11/src/ureq/tcp_connector.rs000066400000000000000000000171341507371613300221760ustar00rootroot00000000000000use std::io::{Read, Write}; use std::net::{SocketAddr, TcpStream}; use std::time::Duration as StdDuration; use std::{fmt, io}; use ureq::config::Config; use ureq::unversioned::transport::time::Duration; use ureq::Error; use crate::ureq::utils::{timeout_not_zero, IoResultExt}; use ureq::unversioned::resolver::ResolvedSocketAddrs; use ureq::unversioned::transport::Either; use ureq::unversioned::transport::{ Buffers, ConnectionDetails, Connector, LazyBuffers, NextTimeout, Transport, }; use log::{debug, trace}; #[derive(Default, Clone)] /// Connector for regular TCP sockets. pub struct TcpConnector { pub tcp_keepalive_time: Option, pub tcp_keepalive_interval: Option, pub tcp_keepalive_retries: Option, } impl Connector for TcpConnector { type Out = Either; fn connect( &self, details: &ConnectionDetails, chained: Option, ) -> Result, Error> { use socket2::TcpKeepalive; if chained.is_some() { // The chained connection overrides whatever we were to open here. // In the DefaultConnector chain this would be a SOCKS proxy connection. trace!("Skip"); return Ok(chained.map(Either::A)); } let config = &details.config; let stream = try_connect(&details.addrs, details.timeout, config)?; let socket: socket2::Socket = stream.into(); let mut keepalive_config = TcpKeepalive::new(); let mut keepalive_enable = false; if let Some(time) = self.tcp_keepalive_time { keepalive_config = keepalive_config.with_time(time); keepalive_enable = true; } if let Some(interval) = self.tcp_keepalive_interval { keepalive_config = keepalive_config.with_interval(interval); keepalive_enable = true; } #[cfg(not(target_os = "windows"))] if let Some(retries) = self.tcp_keepalive_retries { keepalive_config = keepalive_config.with_retries(retries); keepalive_enable = true; } #[cfg(target_os = "windows")] if self.tcp_keepalive_retries.is_some() { log::warn!("Keepalive retries configured but not supported on windows"); } if keepalive_enable { socket.set_tcp_keepalive(&keepalive_config)?; } let buffers = LazyBuffers::new(config.input_buffer_size(), config.output_buffer_size()); let transport = TcpTransport::new(socket.into(), buffers); Ok(Some(Either::B(transport))) } } fn try_connect( addrs: &ResolvedSocketAddrs, timeout: NextTimeout, config: &Config, ) -> Result { for addr in addrs { match try_connect_single(*addr, timeout, config) { // First that connects Ok(v) => return Ok(v), // Intercept ConnectionRefused to try next addrs Err(Error::Io(e)) if e.kind() == io::ErrorKind::ConnectionRefused => { trace!("{addr} connection refused"); continue; } // Other errors bail Err(e) => return Err(e), } } debug!("Failed to connect to any resolved address"); Err(Error::Io(io::Error::new( io::ErrorKind::ConnectionRefused, "Connection refused", ))) } fn try_connect_single( addr: SocketAddr, timeout: NextTimeout, config: &Config, ) -> Result { trace!("Try connect TcpStream to {addr}"); let maybe_stream = if let Some(when) = timeout_not_zero(&timeout) { TcpStream::connect_timeout(&addr, *when) } else { TcpStream::connect(addr) } .normalize_would_block(); let stream = match maybe_stream { Ok(v) => v, Err(e) if e.kind() == io::ErrorKind::TimedOut => { return Err(Error::Timeout(timeout.reason)) } Err(e) => return Err(e.into()), }; if config.no_delay() { stream.set_nodelay(true)?; } debug!("Connected TcpStream to {addr}"); Ok(stream) } pub struct TcpTransport { stream: TcpStream, buffers: LazyBuffers, timeout_write: Option, timeout_read: Option, } impl TcpTransport { pub fn new(stream: TcpStream, buffers: LazyBuffers) -> TcpTransport { TcpTransport { stream, buffers, timeout_read: None, timeout_write: None, } } } // The goal here is to only cause a syscall to set the timeout if it's necessary. fn maybe_update_timeout( timeout: NextTimeout, previous: &mut Option, stream: &TcpStream, f: impl Fn(&TcpStream, Option) -> io::Result<()>, ) -> io::Result<()> { let maybe_timeout = timeout_not_zero(&timeout); if maybe_timeout != *previous { (f)(stream, maybe_timeout.map(|t| *t))?; *previous = maybe_timeout; } Ok(()) } impl Transport for TcpTransport { fn buffers(&mut self) -> &mut dyn Buffers { &mut self.buffers } fn transmit_output(&mut self, amount: usize, timeout: NextTimeout) -> Result<(), Error> { maybe_update_timeout( timeout, &mut self.timeout_write, &self.stream, TcpStream::set_write_timeout, )?; let output = &self.buffers.output()[..amount]; match self.stream.write_all(output).normalize_would_block() { Ok(v) => Ok(v), Err(e) if e.kind() == io::ErrorKind::TimedOut => Err(Error::Timeout(timeout.reason)), Err(e) => Err(e.into()), }?; Ok(()) } fn await_input(&mut self, timeout: NextTimeout) -> Result { if self.buffers.can_use_input() { return Ok(true); } // Proceed to fill the buffers from the TcpStream maybe_update_timeout( timeout, &mut self.timeout_read, &self.stream, TcpStream::set_read_timeout, )?; let input = self.buffers.input_append_buf(); let amount = match self.stream.read(input).normalize_would_block() { Ok(v) => Ok(v), Err(e) if e.kind() == io::ErrorKind::TimedOut => Err(Error::Timeout(timeout.reason)), Err(e) => Err(e.into()), }?; self.buffers.input_appended(amount); Ok(amount > 0) } fn is_open(&mut self) -> bool { probe_tcp_stream(&mut self.stream).unwrap_or(false) } } fn probe_tcp_stream(stream: &mut TcpStream) -> Result { // Temporary do non-blocking IO stream.set_nonblocking(true)?; let mut buf = [0]; match stream.read(&mut buf) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { // This is the correct condition. There should be no waiting // bytes, and therefore reading would block } // Any bytes read means the server sent some garbage we didn't ask for Ok(_) => { debug!("Unexpected bytes from server. Closing connection"); return Ok(false); } // Errors such as closed connection Err(_) => return Ok(false), }; // Reset back to blocking stream.set_nonblocking(false)?; Ok(true) } impl fmt::Debug for TcpConnector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TcpConnector").finish() } } impl fmt::Debug for TcpTransport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TcpTransport") .field("addr", &self.stream.peer_addr().ok()) .finish() } } nethsm-pkcs11-2.0.0/pkcs11/src/ureq/utils.rs000066400000000000000000000017041507371613300204720ustar00rootroot00000000000000use std::io::{self, ErrorKind}; use ureq::unversioned::transport::{time::Duration, NextTimeout}; /// Windows causes kind `TimedOut` while unix does `WouldBlock`. Since we are not /// using non-blocking streams, we normalize `WouldBlock` -> `TimedOut`. pub(crate) trait IoResultExt { fn normalize_would_block(self) -> Self; } impl IoResultExt for io::Result { fn normalize_would_block(self) -> Self { match self { Ok(v) => Ok(v), Err(e) if e.kind() == ErrorKind::WouldBlock => { Err(io::Error::new(ErrorKind::TimedOut, e)) } Err(e) => Err(e), } } } // Remove when https://github.com/algesten/ureq/pull/974 is released pub(crate) fn timeout_not_zero(this: &NextTimeout) -> Option { if this.after.is_not_happening() { None } else if this.after.is_zero() { Some(Duration::from_secs(1)) } else { Some(this.after) } } nethsm-pkcs11-2.0.0/pkcs11/src/utils.rs000066400000000000000000000017771507371613300175300ustar00rootroot00000000000000// makes a CK_VERSION struct from a string like "1.2" pub fn version_struct_from_str(version_str: String) -> cryptoki_sys::CK_VERSION { let parts: Vec<&str> = version_str.split('.').collect(); let (major, minor) = match &parts[..] { [major_str, minor_str] => { let major = major_str.parse().unwrap_or(0); let minor = minor_str.parse().unwrap_or(1); (major, minor) } _ => (0, 1), }; cryptoki_sys::CK_VERSION { major: major as ::std::os::raw::c_uchar, minor: minor as ::std::os::raw::c_uchar, } } // Modified from the ACM project : https://github.com/aws/aws-nitro-enclaves-acm/blob/main/src/vtok_p11/src/util/mod.rs // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 pub fn padded_str(s: &str) -> [u8; N] { let mut ret = [b' '; N]; let count = std::cmp::min(s.len(), N); ret[..count].copy_from_slice(&s.as_bytes()[..count]); ret } nethsm-pkcs11-2.0.0/pkcs11/tests/000077500000000000000000000000001507371613300163615ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/tests/basic.rs000066400000000000000000000560301507371613300200140ustar00rootroot00000000000000#![cfg(feature = "pkcs11-full-tests")] use core::ptr; use std::{ thread, time::{Duration, Instant}, }; use config_file::{ CertificateFormat, InstanceConfig, P11Config, RetryConfig, SlotConfig, UserConfig, }; use pkcs11::{ types::{ CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_SIGN, CKA_TOKEN, CKA_VERIFY, CKM_RSA_PKCS, CK_ATTRIBUTE, CK_BBOOL, CK_FALSE, CK_MECHANISM, CK_OBJECT_HANDLE, CK_SESSION_HANDLE, CK_TRUE, CK_ULONG, }, Ctx, }; mod tools; use tools::NETHSM_DOCKER_HOSTNAME; const RSA_PRIVATE_KEY_ATTRIBUTES: &[CK_ATTRIBUTE] = &[ CK_ATTRIBUTE { attrType: CKA_SIGN, pValue: &CK_TRUE as *const _ as *mut _, ulValueLen: size_of::() as _, }, CK_ATTRIBUTE { attrType: CKA_TOKEN, pValue: &CK_FALSE as *const _ as *mut _, ulValueLen: size_of::() as _, }, ]; const RSA_PUBLIC_KEY_ATTRIBUTES: &[CK_ATTRIBUTE] = &[ CK_ATTRIBUTE { attrType: CKA_VERIFY, pValue: &CK_TRUE as *const _ as *mut _, ulValueLen: size_of::() as _, }, CK_ATTRIBUTE { attrType: CKA_MODULUS_BITS, pValue: &(2048 as CK_ULONG) as *const _ as *mut _, ulValueLen: size_of::() as _, }, CK_ATTRIBUTE { attrType: CKA_TOKEN, pValue: &CK_FALSE as *const _ as *mut _, ulValueLen: size_of::() as _, }, CK_ATTRIBUTE { attrType: CKA_PUBLIC_EXPONENT, pValue: [0x01, 0x00, 0x01].as_ptr() as *mut _, ulValueLen: 3 as _, }, ]; const RSA_MECHANISM: CK_MECHANISM = CK_MECHANISM { mechanism: CKM_RSA_PKCS, pParameter: ptr::null_mut(), ulParameterLen: 0, }; #[test_log::test] fn basic() { tools::run_tests( &[], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![InstanceConfig { url: "https://localhost:8443/api/v1".into(), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |_test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn set_attribute_value() { tools::run_tests( &[], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![InstanceConfig { url: "https://localhost:8443/api/v1".into(), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |_test_ctx, ctx| { fn get_attributes( ctx: &Ctx, session: CK_SESSION_HANDLE, object: CK_OBJECT_HANDLE, ) -> (String, String) { let mut buffer1 = [0; 128]; let mut buffer2 = [0; 128]; let mut attributes = vec![ CK_ATTRIBUTE { attrType: pkcs11::types::CKA_ID, pValue: buffer1.as_mut_ptr() as _, ulValueLen: buffer1.len().try_into().unwrap(), }, CK_ATTRIBUTE { attrType: pkcs11::types::CKA_LABEL, pValue: buffer2.as_mut_ptr() as _, ulValueLen: buffer2.len().try_into().unwrap(), }, ]; ctx.get_attribute_value(session, object, &mut attributes) .unwrap(); let id = String::from_utf8(attributes[0].get_bytes().unwrap()).unwrap(); let label = String::from_utf8(attributes[1].get_bytes().unwrap()).unwrap(); (id, label) } let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let (public_id, public_label) = get_attributes(ctx, session, public_key); println!("public key: id = {public_id}, label = {public_label}"); let (private_id, private_label) = get_attributes(ctx, session, private_key); println!("private key: id = {private_id}, label = {private_label}"); let new_id = "mynewkeyid"; ctx.set_attribute_value( session, private_key, &[CK_ATTRIBUTE::new(pkcs11::types::CKA_ID).with_bytes(new_id.as_bytes())], ) .unwrap(); let (public_id, public_label) = get_attributes(ctx, session, public_key); let (private_id, private_label) = get_attributes(ctx, session, private_key); assert_eq!(&public_id, new_id); assert_eq!(&public_label, new_id); assert_eq!(&private_id, new_id); assert_eq!(&private_label, new_id); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn delete() { tools::run_tests( &[], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![InstanceConfig { url: "https://localhost:8443/api/v1".into(), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |_test_ctx, ctx| { fn assert_objects( ctx: &Ctx, session: CK_SESSION_HANDLE, expected: &[CK_OBJECT_HANDLE], ) { ctx.find_objects_init(session, &[]).unwrap(); let mut objects = ctx.find_objects(session, 10).unwrap(); ctx.find_objects_final(session).unwrap(); let mut expected_objects = expected.to_vec(); objects.sort(); expected_objects.sort(); assert_eq!(objects, expected_objects); } let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key1, private_key1) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let (public_key2, private_key2) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); assert_objects( ctx, session, &[public_key1, public_key2, private_key1, private_key2], ); ctx.destroy_object(session, private_key1).unwrap(); assert_objects(ctx, session, &[public_key2, private_key2]); ctx.destroy_object(session, private_key2).unwrap(); assert_objects(ctx, session, &[]); }, ) } #[test_log::test] fn multiple_instances() { tools::run_tests( &[(8444, 8443)], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![ InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8443/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8444/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, ], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |_test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; for _ in 0..10 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); } ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn timeout() { tools::run_tests( &[], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![InstanceConfig { url: "https://localhost:8443/api/v1".into(), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; for _ in 0..10 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); } test_ctx.add_block(8443); ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); let start = Instant::now(); ctx.sign(session, &data).unwrap_err(); let elapsed = start.elapsed(); assert!(elapsed > Duration::from_secs(10), "Elapsed: {elapsed:?}"); assert!(elapsed < Duration::from_secs(11), "Elapsed: {elapsed:?}"); test_ctx.remove_block(8443); ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn retries() { tools::run_tests( &[], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![InstanceConfig { url: "https://localhost:8443/api/v1".into(), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }], certificate_format: CertificateFormat::Pem, retries: Some(RetryConfig { count: 2, delay_seconds: 2, }), timeout_seconds: Some(10), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; for _ in 0..10 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); } test_ctx.add_block(8443); ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); thread::scope(|s| { s.spawn(|| { // 10s and 500ms to unblock while waiting fort the retry thread::sleep(Duration::new(10, 500_000_000)); test_ctx.remove_block(8443); }); let start = Instant::now(); ctx.sign(session, &data).unwrap(); let elapsed = start.elapsed(); assert!(elapsed > Duration::from_secs(11), "Elapsed: {elapsed:?}"); assert!(elapsed < Duration::from_secs(13), "Elapsed: {elapsed:?}"); }); ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn multi_instance_retries() { tools::run_tests( &[(8444, 8443)], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![ InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8443/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8444/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, ], certificate_format: CertificateFormat::Pem, retries: Some(RetryConfig { count: 3, delay_seconds: 1, }), timeout_seconds: Some(1), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; for _ in 0..2 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); } test_ctx.add_block(8444); for _ in 0..2 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); ctx.sign(session, &data).unwrap(); } test_ctx.remove_block(8444); ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } #[test_log::test] fn pool_not_reused() { tools::run_tests( &[(8444, 8443), (8445, 8443)], P11Config { slots: vec![SlotConfig { label: "Test slot".into(), operator: Some(UserConfig { username: "operator".into(), password: Some("opPassphrase".into()), }), administrator: Some(UserConfig { username: "admin".into(), password: Some("Administrator".into()), }), description: Some("Test slot".into()), instances: vec![ InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8444/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, InstanceConfig { url: format!("https://{NETHSM_DOCKER_HOSTNAME}:8445/api/v1"), danger_insecure_cert: true, sha256_fingerprints: Vec::new(), max_idle_connections: None, }, ], certificate_format: CertificateFormat::Pem, retries: None, timeout_seconds: Some(5), connections_max_idle_duration: None, tcp_keepalive: None, }], ..Default::default() }, |test_ctx, ctx| { let slot = 0; let session = ctx.open_session(slot, 0x04, None, None).unwrap(); let (public_key, private_key) = ctx .generate_key_pair( session, &RSA_MECHANISM, RSA_PUBLIC_KEY_ATTRIBUTES, RSA_PRIVATE_KEY_ATTRIBUTES, ) .unwrap(); let data = [0x42; 32]; for _ in 0..2 { ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); // Verifying signatures is not supported let _signature = ctx.sign(session, &data).unwrap(); } test_ctx.stall_active_connections(); let start_at = Instant::now(); ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); ctx.sign(session, &data).unwrap_err(); assert!(start_at.elapsed() > Duration::from_secs(5)); assert!(start_at.elapsed() < Duration::from_secs(6)); let start_at = Instant::now(); ctx.sign_init(session, &RSA_MECHANISM, private_key).unwrap(); ctx.sign(session, &data).unwrap(); assert!(start_at.elapsed() < Duration::from_secs(1)); ctx.destroy_object(session, public_key).unwrap(); ctx.destroy_object(session, private_key).unwrap(); }, ) } nethsm-pkcs11-2.0.0/pkcs11/tests/tools/000077500000000000000000000000001507371613300175215ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/pkcs11/tests/tools/mod.rs000066400000000000000000000245471507371613300206620ustar00rootroot00000000000000use std::collections::HashSet; use std::io::BufWriter; use std::mem; use std::net::Ipv4Addr; use std::ptr; use std::sync::{LazyLock, Mutex, MutexGuard}; use std::thread::sleep; use std::time::Duration; use std::{env::set_var, process::Command}; pub use config_file::P11Config; use nethsm_sdk_rs::{ apis::{ configuration::Configuration, default_api::{provision_post, users_user_id_put}, }, models::{ProvisionRequestData, UserPostData, UserRole}, }; use pkcs11::{types::CK_C_INITIALIZE_ARGS, Ctx}; use tempfile::NamedTempFile; use time::format_description; use tokio::net::{TcpListener, TcpStream}; use tokio::runtime::{self}; use tokio::sync::broadcast; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio::task::AbortHandle; use ureq::tls::TlsConfig; pub const NETHSM_DOCKER_HOSTNAME: &str = match option_env!("NETHSM_DOCKER_HOSTNAME") { Some(v) => v, None => "localhost", }; pub struct TestContext { blocked_ports: HashSet, stall_connections: broadcast::Sender<()>, } pub struct TestDropper { // treated as dead code even though it shouldn't: https://github.com/rust-lang/rust/issues/122833 #[allow(dead_code)] serialize_test: MutexGuard<'static, bool>, context: TestContext, } fn iptables() -> Command { if option_env!("USE_SUDO_IPTABLES").is_some() { let mut command = Command::new("sudo"); command.arg("iptables"); command } else { Command::new("iptables") } } impl TestContext { fn unblock(port: u16) { let out_in = iptables() .args([ "-D", "INPUT", "-p", "tcp", "--dport", &port.to_string(), "-j", "DROP", ]) .output() .unwrap(); assert!(out_in.status.success()); let out_in = iptables() .args([ "-D", "OUTPUT", "-p", "tcp", "--dport", &port.to_string(), "-j", "DROP", ]) .output() .unwrap(); assert!(out_in.status.success()); } pub fn remove_block(&mut self, port: u16) { assert!(self.blocked_ports.remove(&port)); Self::unblock(port); } pub fn add_block(&mut self, port: u16) { if !self.blocked_ports.insert(port) { return; } let out_in = iptables() .args([ "-A", "INPUT", "-p", "tcp", "--dport", &port.to_string(), "-j", "DROP", ]) .output() .unwrap(); assert!(out_in.status.success()); let out_in = iptables() .args([ "-A", "OUTPUT", "-p", "tcp", "--dport", &port.to_string(), "-j", "DROP", ]) .output() .unwrap(); assert!(out_in.status.success()); } /// Make all active connections wait before killing the connection pub fn stall_active_connections(&self) { self.stall_connections.send(()).unwrap(); } } impl TestDropper { fn clear(&mut self) { for p in self.context.blocked_ports.iter().cloned() { TestContext::unblock(p); } println!("Finished unblocking ports"); } } impl Drop for TestDropper { fn drop(&mut self) { self.clear(); } } enum ProxyMessage { NewProxy(u16, u16, broadcast::Sender<()>), CloseAll, } static PROXY_SENDER: LazyLock> = LazyLock::new(|| { let (tx, mut rx) = unbounded_channel(); std::thread::spawn(move || { runtime::Builder::new_current_thread() .enable_time() .enable_io() .build() .unwrap() .block_on(async move { let mut tasks = Vec::new(); while let Some(msg) = rx.recv().await { match msg { ProxyMessage::NewProxy(from_port, to_port, sender) => { tasks.push(tokio::spawn(proxy(from_port, to_port, sender))) } ProxyMessage::CloseAll => { for task in mem::take(&mut tasks) { task.abort(); } } } } for task in tasks { task.abort(); } }) }); tx }); async fn proxy(from_port: u16, to_port: u16, stall_sender: broadcast::Sender<()>) { let listener = TcpListener::bind(((Ipv4Addr::from([127, 0, 0, 1])), from_port)) .await .unwrap(); struct Droppper(Vec); impl Drop for Droppper { fn drop(&mut self) { assert!(!self.0.is_empty(), "The proxy was not used"); for handle in &self.0 { handle.abort(); } } } let mut dropper = Droppper(Vec::new()); loop { let (socket1, _) = listener.accept().await.unwrap(); let socket2 = TcpStream::connect((Ipv4Addr::from([127, 0, 0, 1]), to_port)) .await .unwrap(); async fn handle_stream( mut rx: tokio::net::tcp::OwnedReadHalf, mut tx: tokio::net::tcp::OwnedWriteHalf, mut stall_receiver: broadcast::Receiver<()>, ) { use tokio::io::{AsyncReadExt, AsyncWriteExt}; let mut buffer = vec![0; 12 * 1024]; let mut should_stall = false; loop { let n = rx.read(&mut buffer).await.unwrap(); match stall_receiver.try_recv() { Ok(()) | Err(broadcast::error::TryRecvError::Lagged(_)) => should_stall = true, Err(broadcast::error::TryRecvError::Empty) => {} Err(broadcast::error::TryRecvError::Closed) => {} } if n == 0 { return; } if should_stall { tokio::time::sleep(Duration::from_secs(50)).await; return; } tx.write_all(&buffer[..n]).await.unwrap(); } } let (rx1, tx1) = socket1.into_split(); let (rx2, tx2) = socket2.into_split(); let stall_rx = stall_sender.subscribe(); let stall_tx = stall_sender.subscribe(); dropper .0 .push(tokio::spawn(handle_stream(rx1, tx2, stall_tx)).abort_handle()); dropper .0 .push(tokio::spawn(handle_stream(rx2, tx1, stall_rx)).abort_handle()); } } /// Contain true if the nethsm has already been provisionned static DOCKER_HELD: Mutex = Mutex::new(false); pub fn run_tests( proxies: &[(u16, u16)], config: P11Config, f: impl FnOnce(&mut TestContext, &mut Ctx) + Clone, ) { let Ok(serialize_test) = DOCKER_HELD.lock() else { eprintln!("Test not run"); return; }; let mut test_dropper = TestDropper { serialize_test, context: TestContext { blocked_ports: HashSet::new(), stall_connections: broadcast::channel(1).0, }, }; let is_provisionned = mem::replace(&mut *test_dropper.serialize_test, true); if !is_provisionned { let client = ureq::Agent::config_builder() .tls_config(TlsConfig::builder().disable_verification(true).build()) .timeout_connect(Some(Duration::from_secs(1))) .timeout_global(Some(Duration::from_secs(10))) .build() .into(); let sdk_config = Configuration { client, base_path: format!("https://{NETHSM_DOCKER_HOSTNAME}:8443/api/v1"), basic_auth: Some(("admin".into(), Some("Administrator".into()))), ..Default::default() }; println!( "Configuration built, waiting for test instance to be up at {}", &sdk_config.base_path ); sleep(Duration::from_secs(2)); println!("Attempting provisionning"); provision_post( &sdk_config, ProvisionRequestData::new( "1234567890".into(), "Administrator".into(), time::OffsetDateTime::now_utc() .format( &format_description::parse( "[year]-[month]-[day]T[hour]:[minute]:[second]Z", ) .unwrap(), ) .unwrap(), ), ) .unwrap(); users_user_id_put( &sdk_config, "operator", UserPostData::new("Operator".into(), UserRole::Operator, "opPassphrase".into()), ) .unwrap(); } else { println!("Already provisionned") } for (in_port, out_port) in proxies { PROXY_SENDER .send(ProxyMessage::NewProxy( *in_port, *out_port, test_dropper.context.stall_connections.clone(), )) .unwrap(); } let mut tmpfile: NamedTempFile = NamedTempFile::new().unwrap(); serde_yaml::to_writer(BufWriter::new(tmpfile.as_file_mut()), &config).unwrap(); let path = tmpfile.path(); set_var(config_file::ENV_VAR_CONFIG_FILE, path); { let mut ctx = Ctx::new_and_initialize("../target/release/libnethsm_pkcs11.so").unwrap(); let f_cl = f.clone(); f_cl(&mut test_dropper.context, &mut ctx); ctx.close_all_sessions(0).unwrap(); } { let mut ctx = Ctx::new("../target/release/libnethsm_pkcs11.so").unwrap(); ctx.initialize(Some(CK_C_INITIALIZE_ARGS { CreateMutex: None, DestroyMutex: None, LockMutex: None, UnlockMutex: None, flags: cryptoki_sys::CKF_LIBRARY_CANT_CREATE_OS_THREADS, pReserved: ptr::null_mut(), })) .unwrap(); f(&mut test_dropper.context, &mut ctx); ctx.close_all_sessions(0).unwrap(); } PROXY_SENDER.send(ProxyMessage::CloseAll).unwrap(); println!("Ending test"); } nethsm-pkcs11-2.0.0/tools/000077500000000000000000000000001507371613300152555ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/tools/build_profiling.sh000077500000000000000000000001641507371613300207650ustar00rootroot00000000000000#!/bin/sh export LLVM_PROFILE_FILE="${PWD}/profile/%p-%m.profraw" RUSTFLAGS="-C instrument-coverage" cargo build nethsm-pkcs11-2.0.0/tools/ci_integration_tests.sh000077500000000000000000000005171507371613300220370ustar00rootroot00000000000000#!/bin/sh -x set -e mkdir -p profile export LLVM_PROFILE_FILE="${PWD}/profile/%p-%m.profraw" echo $LLVM_PROFILE_FILE echo "Starting tests" export P11NETHSM_CONFIG_FILE=./p11nethsm.conf # execute all scripts in the tests directory for f in ./tools/tests/*.sh; do echo "Executing $f" bash $f done echo "All tests passed" nethsm-pkcs11-2.0.0/tools/ci_setup.sh000077500000000000000000000005431507371613300174310ustar00rootroot00000000000000#/bin/sh set -e echo "Provisioning the HSM" ./tools/provision.sh ./tools/create_operator.sh ./tools/create_aes_key.sh ./tools/create_brainpool_keys.sh ./tools/create_ec_p256_key.sh ./tools/create_ec_p384_key.sh ./tools/create_ec_p521_key.sh ./tools/create_ec_p256k1_key.sh ./tools/create_ed_key.sh ./tools/create_rsa_key.sh ./tools/create_web_key.sh nethsm-pkcs11-2.0.0/tools/collect_licenses.sh000077500000000000000000000001351507371613300211250ustar00rootroot00000000000000#!/bin/sh set -e cargo license -d -a > _licenses.txt cat LICENSE _licenses.txt > _LICENSEnethsm-pkcs11-2.0.0/tools/create_aes_key.sh000077500000000000000000000004461507371613300205630ustar00rootroot00000000000000#!/bin/bash curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "AES_Encryption_CBC", "AES_Decryption_CBC" ], "type": "Generic", "id": "aeskey", "length": 256 }' nethsm-pkcs11-2.0.0/tools/create_brainpool_keys.sh000077500000000000000000000006201507371613300221550ustar00rootroot00000000000000#!/bin/bash if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi for type in 256 384 512 do curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "BrainpoolP'${type}'", "id": "bpkey'${type}'" }' done nethsm-pkcs11-2.0.0/tools/create_ec_p256_key.sh000077500000000000000000000004001507371613300211440ustar00rootroot00000000000000#!/bin/bash curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P256", "id": "eckey256" }' nethsm-pkcs11-2.0.0/tools/create_ec_p256k1_key.sh000077500000000000000000000005261507371613300214110ustar00rootroot00000000000000#!/bin/bash if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P256K1", "id": "eckey256k1" }' nethsm-pkcs11-2.0.0/tools/create_ec_p384_key.sh000077500000000000000000000004001507371613300211460ustar00rootroot00000000000000#!/bin/bash curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P384", "id": "eckey384" }' nethsm-pkcs11-2.0.0/tools/create_ec_p521_key.sh000077500000000000000000000004001507371613300211370ustar00rootroot00000000000000#!/bin/bash curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P521", "id": "eckey521" }' nethsm-pkcs11-2.0.0/tools/create_ed_key.sh000077500000000000000000000004001507371613300203710ustar00rootroot00000000000000#!/bin/bash curl -k --fail-with-body -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "EdDSA_Signature" ], "type": "Curve25519", "id": "edkey" }' nethsm-pkcs11-2.0.0/tools/create_operator.sh000077500000000000000000000004131507371613300207700ustar00rootroot00000000000000#!/bin/bash curl -k -i --fail-with-body -w '\n' -u admin:Administrator \ https://localhost:8443/api/v1/users/operator -X PUT \ -H "content-type: application/json" -d "{\"realName\": \"Jane User\", \ \"role\": \"Operator\", \"passphrase\": \"opPassphrase\"}" nethsm-pkcs11-2.0.0/tools/create_rsa_key.sh000077500000000000000000000012201507371613300205670ustar00rootroot00000000000000#!/bin/bash curl -k -i --fail-with-body -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "RSA_Decryption_RAW", "RSA_Decryption_PKCS1", "RSA_Decryption_OAEP_MD5", "RSA_Decryption_OAEP_SHA1", "RSA_Decryption_OAEP_SHA224", "RSA_Decryption_OAEP_SHA256", "RSA_Decryption_OAEP_SHA384", "RSA_Decryption_OAEP_SHA512", "RSA_Signature_PKCS1", "RSA_Signature_PSS_MD5", "RSA_Signature_PSS_SHA1", "RSA_Signature_PSS_SHA224", "RSA_Signature_PSS_SHA256", "RSA_Signature_PSS_SHA384", "RSA_Signature_PSS_SHA512" ], "type": "RSA", "length": 2048, "id": "rsakey" }' nethsm-pkcs11-2.0.0/tools/create_web_key.sh000077500000000000000000000027111507371613300205650ustar00rootroot00000000000000#!/bin/bash curl -k -i --fail-with-body -w '\n' -u admin:Administrator -X PUT \ https://localhost:8443/api/v1/keys/webserver \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "RSA_Decryption_RAW", "RSA_Decryption_PKCS1", "RSA_Decryption_OAEP_MD5", "RSA_Decryption_OAEP_SHA1", "RSA_Decryption_OAEP_SHA224", "RSA_Decryption_OAEP_SHA256", "RSA_Decryption_OAEP_SHA384", "RSA_Decryption_OAEP_SHA512", "RSA_Signature_PKCS1", "RSA_Signature_PSS_MD5", "RSA_Signature_PSS_SHA1", "RSA_Signature_PSS_SHA224", "RSA_Signature_PSS_SHA256", "RSA_Signature_PSS_SHA384", "RSA_Signature_PSS_SHA512" ], "type": "RSA", "private": { "publicExponent": "AQAB", "primeP": "AOedR8mKUVN2jLE60cbESw+o88d2f19oyAjNLUtnLgYnBIKva10JYDRHa/EXqiStx+cDTNvd5xBVPXFrt56sdpHgW1rL9BkcXX5Z75eNQwCEZOxrHp7uSkefr3we7KCTEvFMnA8tp4tnA5y7J+anlgz5oucmS91JS8O8l/UGGk0Sx52N7aRjEVI8Rbm8Mz91jPPuHevvYy0uqkEwI2nxVTlNadmCrJi3DJ/xVm/8bUTCixBcs9LurDfUI70llz9XqHX/AfOOBc8giIAS8PUDa6djKMbKtKR2OurAdHLFMvUWEMEpUwjS+CyFkv+LtXCnl2J0KqKGDW5DYZOMuYSo71s=", "primeQ": "ANAOJHTHgQNr+VWf35WoVYKR6r3fZDy5mtfDlj3i0YRdU7PReanwesNcDiHc1a5nkmVUOpmzG9VmI6vWX2+VEAbW4nukqKsljrla1VZ7RtYsmeoat5vSKwiL1P2fDqjX8xKM1Q94z4wMoXjfuuRbimoOa9uuGpTfKEJolXF0Z6YFUdQWnosOY3GIOQNvVNGYwtczTj2ykVbF3rFepVOhMgvUPKEN0foXAI1yXQECf3nrEHZmNS1IX6m0pqKOdc9xrRZn6Je1E9CLkp52pCkPxWJ0Swep1uk8Lc5MnSo1NmnahVBra8rozvSEEh4p8GVDRsDivzfJYTMEuJS+8pUShCs=" } }' nethsm-pkcs11-2.0.0/tools/der_cert.conf000066400000000000000000000011771507371613300177210ustar00rootroot00000000000000# log_file: /tmp/p11nethsm.log # log_level: Trace slots: - label: LocalHSM description: Local HSM (docker) operator: username: "operator" password: "opPassphrase" administrator: username: "admin" password: "Administrator" instances: - url: "https://localhost:8443/api/v1" max_idle_connections: 16 danger_insecure_cert: true # sha256_fingerprints: # - "31:92:8E:A4:5E:16:5C:A7:33:44:E8:E9:8E:64:C4:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" certificate_format: DER retries: count: 10 delay_seconds: 1 timeout_seconds: 10 nethsm-pkcs11-2.0.0/tools/generate_coverage_all.sh000077500000000000000000000006441507371613300221150ustar00rootroot00000000000000#!/bin/sh -x set -e ./tools/build_profiling.sh ./tools/ci_integration_tests.sh ./tools/test_profiling.sh ./tools/get_coverage.sh SYSROOT=$(rustc --print sysroot) cov=$(find $SYSROOT -name "llvm-cov") objects=$(cat _test_objects) $cov report ${objects} -Xdemangler=rustfilt target/debug/libnethsm_pkcs11.so -instr-profile=profile/libnethsm.profdata --ignore-filename-regex='/.cargo' --ignore-filename-regex='rustc/' nethsm-pkcs11-2.0.0/tools/get_coverage.sh000077500000000000000000000007031507371613300202460ustar00rootroot00000000000000#!/bin/sh SYSROOT=$(rustc --print sysroot) profdata=$(find $SYSROOT -name "llvm-profdata") cov=$(find $SYSROOT -name "llvm-cov") objects=$(cat _test_objects) $profdata merge -sparse profile/*.profraw -o profile/libnethsm.profdata $cov export ${objects} -Xdemangler=rustfilt target/debug/libnethsm_pkcs11.so -instr-profile=profile/libnethsm.profdata --ignore-filename-regex='/.cargo' --ignore-filename-regex='rustc/' --format=lcov > coverage.txt nethsm-pkcs11-2.0.0/tools/invalid_cert.conf000066400000000000000000000005771507371613300206000ustar00rootroot00000000000000log_level: Trace log_file: /tmp/hsm.log slots: - label: LocalHSM description: Local HSM (docker) - invalid cert operator: username: "operator" password: "opPassphrase" instances: - url: "https://localhost:8443/api/v1" sha256_fingerprints: - "31:92:8E:A4:5E:16:5C:A7:33:44:AE:7B:2A:57:E5:77:43:49:F3:69:C9:8F:C4:2F:3A:3B:6E" nethsm-pkcs11-2.0.0/tools/invalid_root_cert.conf000066400000000000000000000003211507371613300216260ustar00rootroot00000000000000slots: - label: LocalHSM description: Local HSM (docker) - invalid cert operator: username: "operator" password: "opPassphrase" instances: - url: "https://localhost:8443/api/v1"nethsm-pkcs11-2.0.0/tools/provision.sh000077500000000000000000000004001507371613300176360ustar00rootroot00000000000000#!/bin/sh curl --fail-with-body -k -i \ https://localhost:8443/api/v1/provision -X POST \ -H "content-type: application/json" -d '{"unlockPassphrase": "UnlockPassphrase", "adminPassphrase": "Administrator", "systemTime": "2018-10-30T11:20:50Z" }'nethsm-pkcs11-2.0.0/tools/test_profiling.sh000077500000000000000000000010751507371613300206470ustar00rootroot00000000000000#!/bin/bash set -e export LLVM_PROFILE_FILE="${PWD}/profile/%p-%m.profraw" rm -rf _test_objects # run normal tests RUSTFLAGS="-C instrument-coverage" cargo test --all-targets # run tests that require to be run in a single thread RUSTFLAGS="-C instrument-coverage" cargo test --all-targets -- --test-threads=1 --ignored files=$(RUSTFLAGS="-C instrument-coverage" cargo test --tests --no-run --message-format=json | jq -r "select(.profile.test == true) | .filenames[]" | grep -v dSYM - ) for file in $files; do printf "%s %s " -object $file >> _test_objects done nethsm-pkcs11-2.0.0/tools/tests/000077500000000000000000000000001507371613300164175ustar00rootroot00000000000000nethsm-pkcs11-2.0.0/tools/tests/change_pin.sh000077500000000000000000000015151507371613300210530ustar00rootroot00000000000000#!/bin/sh -x set -e # create a new test user curl -k -i -w '\n' -u admin:Administrator \ https://localhost:8443/api/v1/users/testuser -X DELETE curl -k -i --fail-with-body -w '\n' -u admin:Administrator \ https://localhost:8443/api/v1/users/testuser -X PUT \ -H "content-type: application/json" -d "{\"realName\": \"Jane User\", \ \"role\": \"Operator\", \"passphrase\": \"testPassphrase\"}" # create a config file for the test user sed -e 's/"operator"/"testuser"/g' -e 's/opPassphrase/testPassphrase/g' \ < p11nethsm.conf > _testuser.conf # change the pin P11NETHSM_CONFIG_FILE=_testuser.conf pkcs11-tool --module target/debug/libnethsm_pkcs11.so -c -p testPassphrase --new-pin testPassphrase2 # try to use the new pin curl -k -i --fail-with-body -w '\n' -u testuser:testPassphrase2 \ https://localhost:8443/api/v1/keysnethsm-pkcs11-2.0.0/tools/tests/decrypt_aes.sh000077500000000000000000000021051507371613300212560ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=aeskeytemp HEXID=$(echo -n ${KEYID} | xxd -ps) # We need to have access to the private key, so we need to generate it locally and then upload it to the HSM openssl rand -out _aes.key 32 B64=$(base64 _aes.key) curl -k -i -w '\n' -u admin:Administrator -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID \ curl -k -i --fail-with-body -w '\n' -u admin:Administrator -X PUT \ https://localhost:8443/api/v1/keys/$KEYID \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "AES_Encryption_CBC", "AES_Decryption_CBC" ], "type": "Generic", "private": { "data": "'$B64'" } }' rm -rf _data.crypt _input _data.decrypt IV=$(openssl rand -hex 16) echo -n "NetHSM rulez! NetHSM rulez! " > _input openssl aes-256-cbc -nopad -in _input -out _data.crypt -K $(cat _aes.key | xxd -c 256 -p) -iv $IV pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism AES-CBC --input-file _data.crypt --id $HEXID \ --iv $IV --output-file _data.decrypt diff _input _data.decrypt nethsm-pkcs11-2.0.0/tools/tests/decrypt_rsa_pkcs.sh000077500000000000000000000011341507371613300223140ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.crypt _public.pem _input _data.decrypt curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' > _input openssl rsautl -encrypt -inkey _public.pem -pubin \ -out _data.crypt -in _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism RSA-PKCS --input-file _data.crypt --id $HEXID --output-file _data.decrypt # the input should be the same as the output diff _input _data.decryptnethsm-pkcs11-2.0.0/tools/tests/decrypt_rsa_pkcs_sha1.sh000077500000000000000000000012111507371613300232240ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.crypt _public.pem _input _data.decrypt curl -k -s --fail-with-body -v -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' > _input openssl rsautl -encrypt -inkey _public.pem -pubin \ -out _data.crypt -oaep -in _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism RSA-PKCS-OAEP --input-file _data.crypt --id $HEXID \ --hash-algorithm SHA-1 --output-file _data.decrypt # the input should be the same as the output diff _input _data.decrypt nethsm-pkcs11-2.0.0/tools/tests/decrypt_rsa_pkcs_sha256.sh000077500000000000000000000013501507371613300234040ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.crypt _public.pem _input _data.decrypt curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!NetHSM rulez!' > _input openssl pkeyutl -encrypt -pubin -inkey _public.pem \ -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 \ -pkeyopt rsa_mgf1_md:sha256 -out _data.crypt -in _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism RSA-PKCS-OAEP --input-file _data.crypt --id $HEXID \ --hash-algorithm SHA256 --output-file _data.decrypt # the input should be the same as the output diff _input _data.decryptnethsm-pkcs11-2.0.0/tools/tests/decrypt_rsa_pkcs_sha512.sh000077500000000000000000000013321507371613300233770ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.crypt _public.pem _input _data.decrypt curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' > _input openssl pkeyutl -encrypt -pubin -inkey _public.pem \ -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha512 \ -pkeyopt rsa_mgf1_md:sha512 -out _data.crypt -in _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism RSA-PKCS-OAEP --input-file _data.crypt --id $HEXID \ --hash-algorithm SHA512 --output-file _data.decrypt # the input should be the same as the output diff _input _data.decryptnethsm-pkcs11-2.0.0/tools/tests/decrypt_rsa_raw.sh000077500000000000000000000016211507371613300221460ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.crypt _public.pem _input _data.decrypt curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem # length of the input should be the same as the key length echo 'NetHSM rulez!aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaastratsaatastrastrastratsartsrasrastartsartastasrstatsarstarstar' > _input openssl rsautl -encrypt -inkey _public.pem -pubin \ -out _data.crypt -raw -in _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --decrypt \ --mechanism RSA-X-509 --input-file _data.crypt --id $HEXID --output-file _data.decrypt # the input should be the same as the output diff _input _data.decrypt nethsm-pkcs11-2.0.0/tools/tests/delete_key.sh000077500000000000000000000014461507371613300210750ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=tempkey HEXID=$(echo -n ${KEYID} | xxd -ps) # create a key curl -k -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P256", "id": "'$KEYID'" }' # delete the key pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --delete-object --type privkey --id $HEXID --login --login-type so --so-pin Administrator ## check that the key is gone RESPONSE=$(curl -s -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o /dev/null -w "%{http_code}") if [ $RESPONSE -eq 404 ]; then echo "Got 404 error, key was deleted" else echo "No 404 error, response code was $RESPONSE" exit 1 fi nethsm-pkcs11-2.0.0/tools/tests/delete_key_certificate.sh000077500000000000000000000032261507371613300234350ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=tempkey HEXID=$(echo -n ${KEYID} | xxd -ps) # create a key openssl req -x509 -newkey rsa:2048 -keyout ./_privatekey.pem -out ./_certificate.pem -days 365 -nodes -subj "/C=US/ST=California/L=San Francisco/O=Your Company/OU=Your Department/CN=yourdomain.com" curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID curl -k -i -w '\n' -u admin:Administrator -X PUT \ "https://localhost:8443/api/v1/keys/${KEYID}" \ -F "arguments={\"mechanisms\": [\"RSA_Decryption_RAW\",\"RSA_Decryption_PKCS1\",\"RSA_Decryption_OAEP_MD5\",\"RSA_Decryption_OAEP_SHA1\",\"RSA_Decryption_OAEP_SHA224\",\"RSA_Decryption_OAEP_SHA256\",\"RSA_Decryption_OAEP_SHA384\",\"RSA_Decryption_OAEP_SHA512\",\"RSA_Signature_PKCS1\",\"RSA_Signature_PSS_MD5\",\"RSA_Signature_PSS_SHA1\",\"RSA_Signature_PSS_SHA224\",\"RSA_Signature_PSS_SHA256\",\"RSA_Signature_PSS_SHA384\",\"RSA_Signature_PSS_SHA512\"]}" \ -F "key_file=@_privatekey.pem" curl -k -i -w '\n' -u admin:Administrator -X PUT \ https://localhost:8443/api/v1/keys/${KEYID}/cert \ -H 'Content-Type: application/octet-stream' \ --data-binary '@_certificate.pem' # delete the key pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --delete-object --type cert --id $HEXID --login --login-type so --so-pin Administrator ## check that the key is gone RESPONSE=$(curl -s -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/cert \ -H 'Accept: application/octet-stream' -o /dev/null -w "%{http_code}") if [ $RESPONSE -eq 404 ]; then echo "Got 404 error, cert was deleted" else echo "No 404 error, response code was $RESPONSE" exit 1 fi nethsm-pkcs11-2.0.0/tools/tests/encrypt_aes.sh000077500000000000000000000021401507371613300212670ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=aeskeytemp HEXID=$(echo -n ${KEYID} | xxd -ps) # We need to have access to the private key, so we need to generate it locally and then upload it to the HSM openssl rand -out _aes.key 32 B64=$(base64 _aes.key) curl -k -i -w '\n' -u admin:Administrator -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID \ curl -k -i --fail-with-body -w '\n' -u admin:Administrator -X PUT \ https://localhost:8443/api/v1/keys/$KEYID \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "AES_Encryption_CBC", "AES_Decryption_CBC" ], "type": "Generic", "private": { "data": "'$B64'" } }' rm -rf _data.crypt _input _data.decrypt IV=$(openssl rand -hex 16) echo -n "NetHSM rulez! NetHSM rulez! " > _input pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v --encrypt \ --mechanism AES-CBC --id $HEXID \ --output-file _data.crypt --iv $IV --input-file _input # decrypt with openssl openssl aes-256-cbc -nopad -d -in _data.crypt -out _data.decrypt -K $(cat _aes.key | xxd -c 256 -p) -iv $IV diff _input _data.decrypt nethsm-pkcs11-2.0.0/tools/tests/general_test.sh000077500000000000000000000002261507371613300214320ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi pkcs11-tool --module target/debug/libnethsm_pkcs11.so -p opPassphrase --test nethsm-pkcs11-2.0.0/tools/tests/generate_aes_id.sh000077500000000000000000000006461507371613300220620ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testAESkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so --keygen --key-type AES:128 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYIDnethsm-pkcs11-2.0.0/tools/tests/generate_brainpool.sh000077500000000000000000000011031507371613300226100ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi KEYID=testBrainpoolKey HEXID=$(echo -n ${KEYID} | xxd -ps) for type in 256 384 512 do curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type EC:brainpoolP${type}r1 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem done nethsm-pkcs11-2.0.0/tools/tests/generate_ec_p256_id.sh000077500000000000000000000006611507371613300224520ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testECkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type EC:prime256v1 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/generate_ec_p256k1_id.sh000077500000000000000000000010051507371613300226770ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi KEYID=testECkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type EC:secp256k1 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/generate_ec_p384_id.sh000077500000000000000000000006611507371613300224540ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testECkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type EC:prime384v1 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/generate_ec_p521_id.sh000077500000000000000000000006611507371613300224450ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testECkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type EC:prime521v1 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/generate_ed_id.sh000077500000000000000000000006171507371613300217000ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testEDkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --generate-privkey=Ed25519 --id $HEXID --label $KEYID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/generate_generic_id.sh000077500000000000000000000006521507371613300227230ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testAeskey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so --keygen --key-type GENERIC:512 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYIDnethsm-pkcs11-2.0.0/tools/tests/generate_rsa_id.sh000077500000000000000000000006551507371613300220770ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=testRSAkey HEXID=$(echo -n ${KEYID} | xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID pkcs11-tool --module target/debug/libnethsm_pkcs11.so -k --key-type RSA:4096 \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem nethsm-pkcs11-2.0.0/tools/tests/invalid_certificate_fingerprint.sh000077500000000000000000000002751507371613300253610ustar00rootroot00000000000000#!/bin/sh -x mkdir /tmp P11NETHSM_CONFIG_FILE=./tools/invalid_cert.conf pkcs11-tool --module target/debug/libnethsm_pkcs11.so -O if [ $? -eq 0 ]; then cat /tmp/hsm.log exit 1 fi nethsm-pkcs11-2.0.0/tools/tests/invalid_root_certificate.sh000077500000000000000000000002421507371613300240070ustar00rootroot00000000000000#!/bin/sh -x P11NETHSM_CONFIG_FILE=./tools/invalid_root_cert.conf pkcs11-tool --module target/debug/libnethsm_pkcs11.so -O if [ $? -eq 0 ]; then exit 1 fi nethsm-pkcs11-2.0.0/tools/tests/list_all.sh000077500000000000000000000001221507371613300205540ustar00rootroot00000000000000#!/bin/sh -x set -e pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -O nethsm-pkcs11-2.0.0/tools/tests/random.sh000077500000000000000000000007541507371613300202440ustar00rootroot00000000000000#!/bin/sh -x rm -rf _data _data2 # generate two random files, and compare them to make sure they are # return 1 if they are the same, 0 if they are different pkcs11-tool --module target/debug/libnethsm_pkcs11.so --generate-random 32 --output-file _data if [ $? -ne 0 ]; then exit $? fi pkcs11-tool --module target/debug/libnethsm_pkcs11.so --generate-random 32 --output-file _data2 if [ $? -ne 0 ]; then exit $? fi # ensure they are different diff _data _data2 && exit 1 exit 0nethsm-pkcs11-2.0.0/tools/tests/rename_key.sh000077500000000000000000000026601507371613300211010ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi KEYID=tempkey KEYID_NEW=tempkey2 HEXID=$(echo -n ${KEYID} | xxd -ps) HEXID_NEW=$(echo -n ${KEYID_NEW} | xxd -ps) # create a key curl -k -i -w '\n' -u admin:Administrator -X POST \ https://localhost:8443/api/v1/keys/generate \ -H "content-type: application/json" \ -d '{ "mechanisms": [ "ECDSA_Signature" ], "type": "EC_P256", "id": "'$KEYID'" }' # rename the key pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --set-id $HEXID_NEW --type privkey --id $HEXID --login --login-type so --so-pin Administrator # check that the old key is gone RESPONSE=$(curl -s -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o /dev/null -w "%{http_code}") if [ $RESPONSE -eq 404 ]; then echo "Got 404 error, key was renamed" else echo "No 404 error, response code was $RESPONSE" exit 1 fi # check that the new key is still there RESPONSE=$(curl -s -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID_NEW/public.pem -o /dev/null -w "%{http_code}") if [ $RESPONSE -eq 200 ]; then echo "Got 200 response, key was renamed" else echo "No 200 response, response code was $RESPONSE" exit 1 fi pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --delete-object --type privkey --id $HEXID_NEW --login --login-type so --so-pin Administrator nethsm-pkcs11-2.0.0/tools/tests/sign_brainpool.sh000077500000000000000000000012501507371613300217610ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi for type in 256 384 512 do KEYID=bpkey${type} HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 -verify _public.pem -signature _data.sig done nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p256.sh000077500000000000000000000012331507371613300211400ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey256 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID #echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | openssl pkeyutl -verify -pubin -inkey _public.pem -sigfile _data.sig echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p256_longer.sh000077500000000000000000000011371507371613300225110ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey256 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | openssl dgst -sha512 -binary > _data pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID --input-file _data openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -keyform PEM -pubin -in _data nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p256_sha256.sh000077500000000000000000000010071507371613300222270ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey256 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA-SHA256 --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p256_shorter.sh000077500000000000000000000011371507371613300227110ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey256 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' | openssl dgst -sha224 -binary > _data pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID --input-file _data openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -keyform PEM -pubin -in _data nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p256k1.sh000077500000000000000000000013611507371613300213760ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v1.* ]] || [[ $NETHSM_VERSION == v2.* ]] then exit fi KEYID=eckey256k1 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID #echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | openssl pkeyutl -verify -pubin -inkey _public.pem -sigfile _data.sig echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p384.sh000077500000000000000000000010371507371613300211440ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey384 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha384 -binary | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha384 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p384_sha384.sh000077500000000000000000000010071507371613300222330ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey384 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA-SHA384 --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha384 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p521.sh000077500000000000000000000010371507371613300211350ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey521 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha512 -binary | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha512 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p521_sha1.sh000077500000000000000000000010571507371613300220530ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey521 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'aaa' > _data pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA-SHA1 --output-file _data.sig --signature-format openssl --id $HEXID --input-file _data cat _data | openssl dgst -sha1 -binary | openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -keyform PEM -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p521_sha512.sh000077500000000000000000000010071507371613300222150ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=eckey521 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA-SHA512 --output-file _data.sig --signature-format openssl --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha512 -verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_ec_p521_shorter.sh000077500000000000000000000011401507371613300226760ustar00rootroot00000000000000#!/bin/sh -x # test with a shorter value, this should work even though it sould be a sha512 hash set -e KEYID=eckey521 HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'aaa' > _data pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism ECDSA --output-file _data.sig --signature-format openssl --id $HEXID --input-file _data openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -keyform PEM -pubin -in _data nethsm-pkcs11-2.0.0/tools/tests/sign_ed.sh000077500000000000000000000007751507371613300203770ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=edkey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' > _data pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism EDDSA --output-file _data.sig --id $HEXID --input-file _data openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -keyform PEM -pubin -rawin -in _data nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs.sh000077500000000000000000000007721507371613300216110ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism RSA-PKCS --output-file _data.sig --id $HEXID openssl rsautl -verify -inkey _public.pem -in _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_pss_sha1.sh000077500000000000000000000010551507371613300234050ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA1-RSA-PKCS-PSS --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha1 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_pss_sha224.sh000077500000000000000000000010611507371613300235510ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA224-RSA-PKCS-PSS --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha224 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_pss_sha256.sh000077500000000000000000000010611507371613300235560ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA256-RSA-PKCS-PSS --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha256 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_pss_sha384.sh000077500000000000000000000010611507371613300235600ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA384-RSA-PKCS-PSS --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha384 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_pss_sha512.sh000077500000000000000000000010611507371613300235510ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA512-RSA-PKCS-PSS --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha512 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_sha1.sh000077500000000000000000000011131507371613300225130ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA1-RSA-PKCS --output-file _data.sig --id $HEXID --signature-format openssl echo 'NetHSM rulez!' | openssl pkeyutl -verify -rawin -digest sha1 -inkey _public.pem -sigfile _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_sha224.sh000077500000000000000000000011171507371613300226660ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA224-RSA-PKCS --output-file _data.sig --id $HEXID --signature-format openssl echo 'NetHSM rulez!' | openssl pkeyutl -verify -rawin -digest sha224 -inkey _public.pem -sigfile _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_sha256.sh000077500000000000000000000011171507371613300226730ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA256-RSA-PKCS --output-file _data.sig --id $HEXID --signature-format openssl echo 'NetHSM rulez!' | openssl pkeyutl -verify -rawin -digest sha256 -inkey _public.pem -sigfile _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_sha384.sh000077500000000000000000000011171507371613300226750ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA384-RSA-PKCS --output-file _data.sig --id $HEXID --signature-format openssl echo 'NetHSM rulez!' | openssl pkeyutl -verify -rawin -digest sha384 -inkey _public.pem -sigfile _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_pkcs_sha512.sh000077500000000000000000000011171507371613300226660ustar00rootroot00000000000000#!/bin/bash -x set -e if [[ $NETHSM_VERSION == v3.0 ]] then exit fi KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -k -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -v \ --sign --mechanism SHA512-RSA-PKCS --output-file _data.sig --id $HEXID --signature-format openssl echo 'NetHSM rulez!' | openssl pkeyutl -verify -rawin -digest sha512 -inkey _public.pem -sigfile _data.sig -pubin nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_sha1.sh000077500000000000000000000011341507371613300214760ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID} | xxd -ps) rm -rf _data.sig _public.pem curl -k -s --fail-with-body -u operator:opPassphrase -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha1 -binary | pkcs11-tool \ --module ./target/debug/libnethsm_pkcs11.so -v --sign --mechanism RSA-PKCS-PSS \ --hash-algorithm SHA-1 --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha1 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/sign_rsa_sha256.sh000077500000000000000000000012061507371613300216520ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=rsakey HEXID=$(echo -n ${KEYID}| xxd -ps) rm -rf _data.sig _public.pem curl -s --fail-with-body -u operator:opPassphrase -k -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | P11NETHSM_CONFIG_FILE=./p11nethsm.conf pkcs11-tool \ --module ./target/debug/libnethsm_pkcs11.so -v --sign --mechanism RSA-PKCS-PSS \ --hash-algorithm SHA256 --output-file _data.sig --id $HEXID echo 'NetHSM rulez!' | openssl dgst -keyform PEM -verify _public.pem -sha256 \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/upload_aes_key.sh000077500000000000000000000017241507371613300217460ustar00rootroot00000000000000#!/bin/sh -x set -e rm -rf _aes.key _crypt.json _data.dec _data.crypt _iv.bin data.txt _decrypted.txt openssl rand -out _aes.key 32 OUTPUT=$(pkcs11-tool --module target/debug/libnethsm_pkcs11.so -y secrkey --write-object _aes.key --login --login-type so --so-pin Administrator) id=$(echo "$OUTPUT" | awk '/label:/{print $2}') echo $id echo $OUTPUT # encrypt with openssl aes-256-cbc IV=$(openssl rand -hex 16) echo "NetHSM rulez! " > _data.txt openssl aes-256-cbc -in _data.txt -out _data.crypt -K $(cat _aes.key | xxd -c 256 -p) -iv $IV # decrypt with api call curl -k --fail-with-body -u operator:opPassphrase -v -X POST \ -H "Content-Type: application/json" \ -d '{"mode":"AES_CBC","iv":"'$(echo -n $IV | xxd -r -p | base64)'","encrypted":"'$(cat _data.crypt | base64)'"}' \ https://localhost:8443/api/v1/keys/$id/decrypt > _data.dec # verify decrypted=$(jq -r '.decrypted' _data.dec | base64 -d| tr -d '' > _decrypted.txt) diff _data.txt _decrypted.txtnethsm-pkcs11-2.0.0/tools/tests/upload_certificate.sh000077500000000000000000000014441507371613300226070ustar00rootroot00000000000000#!/bin/sh -x set -e rm -rf _cert.key _cert.pem _curl_cert.pem KEYID=certtest HEXID=$(echo -n ${KEYID}| xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID openssl req -x509 -newkey rsa:2048 -keyout _cert.key -out _cert.pem -days 365 -nodes -subj "/CN=www.example.com" p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-privkey _cert.key p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-certificate _cert.pem # check if the cert is there curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/cert --header "Accept: application/octet-stream" -o _curl_cert.pem diff _cert.pem _curl_cert.pem nethsm-pkcs11-2.0.0/tools/tests/upload_der_certificate.sh000077500000000000000000000020641507371613300234400ustar00rootroot00000000000000#!/bin/sh -x set -e rm -rf _cert.key _cert.der _curl_cert.der export P11NETHSM_CONFIG_FILE=./tools/der_cert.conf KEYID=certtest HEXID=$(echo -n ${KEYID}| xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID openssl req -x509 -newkey rsa:2048 -keyout _cert.key -out _cert.der -outform DER -days 365 -nodes -subj "/CN=www.example.com" openssl x509 -in _cert.der -out _cert.pem -inform DER -outform PEM p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-privkey _cert.key p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-certificate _cert.pem p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --list-cert |grep 'Type: X.509 Certificate' # check if the cert is there curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/cert --header "Accept: application/octet-stream" -o _curl_cert.der diff _cert.der _curl_cert.der nethsm-pkcs11-2.0.0/tools/tests/upload_ec_key.sh000077500000000000000000000015551507371613300215670ustar00rootroot00000000000000#!/bin/sh -x set -e rm -rf _test_ec_private.pem _test_ec_private.der _public.pem _ec_public.pem openssl ecparam -name prime256v1 -genkey -noout -out _ec_private.pem openssl pkcs8 -topk8 -nocrypt -in _ec_private.pem -outform DER -out _ec_private.der OUTPUT=$(pkcs11-tool --module target/debug/libnethsm_pkcs11.so -y privkey --write-object _ec_private.der --login --login-type so --so-pin Administrator) id=$(echo "$OUTPUT" | awk '/label:/{print $2}') curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$id/public.pem -o _public.pem ## Sign with openssl echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | openssl pkeyutl -sign -inkey _ec_private.pem -out _data.sig ## Verify with openssl and the retrieved public key echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 --verify _public.pem -signature _data.sig nethsm-pkcs11-2.0.0/tools/tests/upload_ec_key_id.sh000077500000000000000000000017211507371613300222360ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=Hello HEXID=$(echo -n ${KEYID}| xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID rm -rf _test_ec_private.pem _test_ec_private.der _public.pem _ec_public.pem openssl ecparam -name prime256v1 -genkey -noout -out _ec_private.pem openssl pkcs8 -topk8 -nocrypt -in _ec_private.pem -outform DER -out _ec_private.der pkcs11-tool --module target/debug/libnethsm_pkcs11.so -y privkey --write-object _ec_private.der \ --login --login-type so --so-pin Administrator --id $HEXID curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem ## Sign with openssl echo 'NetHSM rulez!' | openssl dgst -sha256 -binary | openssl pkeyutl -sign -inkey _ec_private.pem -out _data.sig ## Verify with openssl and the retrieved public key echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 --verify _public.pem -signature _data.signethsm-pkcs11-2.0.0/tools/tests/upload_ed_key_id.sh000077500000000000000000000015701507371613300222410ustar00rootroot00000000000000#!/bin/sh -x set -e KEYID=edtest HEXID=$(echo -n ${KEYID}| xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID rm -rf _ed_private.pem _ed_private.der _public.pem openssl genpkey -algorithm Ed25519 -out _ed_private.pem # openssl pkcs8 -topk8 -nocrypt -in _ec_private.pem -outform DER -out _ec_private.der p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-privkey _ed_private.pem curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/$KEYID/public.pem -o _public.pem ## Sign with openssl echo 'NetHSM rulez!' > _data openssl pkeyutl -sign -inkey _ed_private.pem -out _data.sig -rawin -in _data ## Verify with openssl and the retrieved public key openssl pkeyutl -verify -sigfile _data.sig -inkey _public.pem -pubin -rawin -in _datanethsm-pkcs11-2.0.0/tools/tests/upload_rsa_key_id.sh000077500000000000000000000014271507371613300224370ustar00rootroot00000000000000#!/bin/sh -x set -e rm -rf _rsa_private.pem _rsa_private.der _public.pem _data.sig openssl genrsa -out _rsa_private.pem 2048 KEYID=edtest HEXID=$(echo -n ${KEYID}| xxd -ps) curl -k -u admin:Administrator -v -X DELETE \ https://localhost:8443/api/v1/keys/$KEYID p11tool --provider ${PWD}/target/debug/libnethsm_pkcs11.so --write --id $HEXID --label $KEYID --load-privkey _rsa_private.pem curl -k --fail-with-body -u operator:opPassphrase -v -X GET \ https://localhost:8443/api/v1/keys/${KEYID}/public.pem -o _public.pem ## Sign with openssl echo 'NetHSM rulez!' | openssl dgst -sha256 -sign _rsa_private.pem -out _data.sig ## Verify with openssl and the retrieved public key echo 'NetHSM rulez!' | openssl dgst -keyform PEM -sha256 --verify _public.pem -signature _data.sig