Craton HSM

PKCS#11 Passthrough Backend

PKCS#11 Passthrough Backend

The craton-hsm-pkcs11 crate turns Craton HSM into an abstraction layer over any vendor PKCS#11 token. All cryptographic operations are delegated to the vendor library; Craton HSM supplies session management, audit logging, and a uniform CryptoBackend API on top of a hardware root of trust.

  • Crate: craton-hsm-pkcs11
  • Backend type: Pkcs11PassthroughBackend
  • License: BSL 1.1
  • MSRV: Rust 1.75
  • Status: production-ready

Architecture

  +------------------------------------------+
  |       Pkcs11PassthroughBackend           |
  |  +------------------------------------+  |
  |  |            SessionPool             |  |
  |  |                                    |  |
  |  |  Mutex<PooledSession>  --+         |  |
  |  |  Mutex<PooledSession>  --+-- N     |  |
  |  |  Mutex<PooledSession>  --+         |  |
  |  |    (each owns a KeyCache)          |  |
  |  +------------------------------------+  |
  +-------------------+----------------------+
                      |
                      v
              cryptoki::Pkcs11 (Arc)
                      |
                      v
              vendor .so / .dll
  • A shared Arc<cryptoki::Pkcs11> context is wrapped by a SessionPool of PooledSession instances. Each session owns a KeyCache for imported key handles.
  • Default pool size is 8 (raised from 4 in the most recent hardening sweep).
  • Lookup and crypto calls run sequentially under a single mutex, so there is no TOCTOU window on imported-key handles.

Configuration

use craton_hsm_pkcs11::{Pkcs11PassthroughBackend, Pkcs11PassthroughConfig};
use craton_hsm::crypto::backend::CryptoBackend;
use zeroize::Zeroizing;

let config = Pkcs11PassthroughConfig {
    library_path: "/usr/lib/softhsm/libsofthsm2.so".into(),
    slot_id: 0,
    user_pin: Some(Zeroizing::new("1234".to_string())),
    allow_software_keygen_fallback: false,
    // ... other fields
};
let backend = Pkcs11PassthroughBackend::new(config)?;
# Ok::<(), craton_hsm::error::HsmError>(())

Key fields:

  • library_path — absolute path to the vendor PKCS#11 .so or .dll. Load from a write-protected location; the library is not itself verified before loading.
  • slot_id — numeric PKCS#11 slot identifier.
  • user_pin — held in Zeroizing<String>; redacted from Debug output. The SessionPool hands exactly one owned copy to AuthPin per login.
  • allow_software_keygen_fallback — off by default. When off, key generation failures surface directly instead of silently falling back to software.

This crate exposes no Cargo features; the vendor PKCS#11 library is loaded at runtime via cryptoki / libloading.

Tested Hardware

See the compatibility matrix for the authoritative list. In summary:

Vendor / LibraryVersionStatus
SoftHSM2.6.0+Tested — the default CI fixture
SoftHSM2.5.xBest-effort
YubiHSM2 SDK2023.08+Supported; requires yubihsm-connector
Thales Luna Client10.xSupported; firmware 7.x, mTLS to appliance
Utimaco CryptoServerSe-Series (CP5)Supported; SDK 4.40+
nCipher nShieldSecurity World 13.xBest-effort; requires hardserver
AWS CloudHSMClient 5.xBest-effort

Only SoftHSM is exercised by CI. Other vendor libraries are validated pre-release against the listed versions; regression risk between Craton releases is low but not zero.

Security Properties

  • No TOCTOU on imported-key handles. Each PooledSession owns its KeyCache; lookup and the subsequent crypto call are sequential under one mutex.
  • AES-GCM nonce-reuse bound enforced per key. Each cache entry tracks the encryption count and refuses further encryptions past the configured ceiling (default 2^32, per NIST SP 800-38D §8.3). Cache eviction does not reset this counter.
  • Sticky high-water marks. Per-key counter high-water marks live in a separate non-evictable counters map. A key evicted and re-imported resumes counting from its prior mark instead of silently resetting to zero.
  • Fail-closed software fallback. Software key-generation fallback is off by default and must be opted into with allow_software_keygen_fallback = true.
  • Domain-separated fingerprints. Cache keys are derived with a per-key-type domain tag and length-prefixed parts, preventing cross-type or concatenation collisions.
  • Typed verification decoding. Verify results go through error::VerifyOutcome / error::classify_verify_result, never string-matching of error text.

All unsafe FFI lives in the upstream cryptoki crate; this crate adds no unsafe blocks of its own. RAII session handles guarantee C_CloseSession on drop.

Limitations

  • No built-in PIN rotation. Changing a PIN requires re-instantiating the backend with the new credential.
  • No offline PKCS#11 module validation. The vendor .so / .dll is loaded and trusted at its configured path. Deployments should load it from a write-protected directory.
  • Software key-generation fallback is explicit only. By design — silent software fallback would invalidate the hardware-backed trust model.
  • Slot / token management (init, relabel, factory reset) is not exposed; use the vendor's own tooling for those operations.

Error Reporting

Failures surface through craton_hsm::error::HsmError, mapped from cryptoki::error::Error and cryptoki::error::RvError via the crate's error submodule.