Craton HSM
OpenSSL Backend
OpenSSL Backend
The craton-hsm-openssl crate implements the CryptoBackend trait over the
system's OpenSSL 3 library via the openssl
Rust bindings. If the linked OpenSSL build has the FIPS provider installed
and active, every operation dispatched through this backend runs inside
that provider's validation boundary.
- Crate:
craton-hsm-openssl - Backend type:
OpenSslBackend - License: BSL 1.1
- MSRV: Rust 1.75
- Status: production-ready
Choose this backend when you already have an OpenSSL 3 FIPS provider in production and need Craton HSM to reuse it, or when you want a non-FIPS OpenSSL build for development symmetry with an existing OpenSSL-based stack.
Build
cargo build -p craton-hsm-openssl
Requirements:
- OpenSSL 3.x development headers and libraries at build time
(
libssl-dev/openssl-devel/ vcpkg). OpenSSL 1.1.1 builds are best-effort only — the library is EOL upstream. - For FIPS operation, an OpenSSL 3 installation with the FIPS provider
loaded and activated in
openssl.cnf. This crate does not itself enable FIPS mode; that is a property of the linked OpenSSL build.
On macOS, point the build at Homebrew's OpenSSL:
export OPENSSL_DIR=$(brew --prefix openssl)
This crate exposes no Cargo features. Hardware acceleration (AES-NI, AVX) comes from whatever the linked OpenSSL build provides.
Algorithms
- AES in GCM, CBC, CTR, and key-wrap modes (128 / 192 / 256-bit).
- RSA PKCS#1 v1.5, RSA-PSS, and RSA-OAEP. Modulus size is clamped at a 2048-bit minimum and 8192-bit ceiling.
- ECDSA on P-256 and P-384.
- Ed25519 sign and verify.
- ECDH on P-256 and P-384 with HKDF-SHA256 derivation (fixed salt matching the core crate's ECDH-HKDF contract).
- SHA-2 (SHA-256, SHA-384, SHA-512) via the streaming
DigestAccumulator.
Verification paths use subtle::ConstantTimeEq for constant-time
comparisons, and private-key material is wrapped in Zeroizing buffers.
Usage
use craton_hsm_openssl::OpenSslBackend;
use craton_hsm::crypto::backend::CryptoBackend;
let backend = OpenSslBackend;
let key = backend.generate_aes_key(32, true)?;
let ct = backend.aes_256_gcm_encrypt(key.as_ref(), b"plaintext")?;
# Ok::<(), craton_hsm::error::HsmError>(())
FIPS Provider Notes
FIPS compliance is a property of the underlying OpenSSL build, not of this
crate. The crate does not call OSSL_PROVIDER_load on your behalf and
does not force the FIPS property query string. Deployments targeting
FIPS must:
- Install an OpenSSL 3 distribution with the FIPS provider present
(
fips.so/fips.dll). - Enable FIPS in
openssl.cnfso the default library context uses the FIPS provider. - Ensure the process that loads
craton-hsm-opensslinherits that configuration.
If those preconditions are not met, operations still succeed but the
deployment is not FIPS-compliant regardless of what this crate does. For a
self-contained FIPS boundary, use craton-hsm-awslc instead.
AES-GCM Nonce Safety
The backend generates random 96-bit nonces and enforces the NIST SP 800-38D birthday bound of 2^32 encryptions per key:
- A per-process counter map keyed by
SHA-256(key)tracks the encryption count for each key. - On exhaustion, the key is moved into a sticky poison set that
cannot be cleared by
reset_gcm_counterorevict_gcm_counters. Re-keying is the only way to continue. - The counter map has a soft cap of 1,000,000 entries. Poisoned entries are never evicted; if the map cannot be shrunk below the cap, encryption fails fast rather than silently re-arming a retired key.
For keys that outlive the process, use the file-backed journal:
use craton_hsm_openssl::OpenSslBackend;
let backend = OpenSslBackend::new_with_persistent_gcm_counter(
"/var/lib/craton-hsm/gcm-counter.journal",
)?;
# Ok::<(), craton_hsm::error::HsmError>(())
Behaviour mirrors the craton-hsm-awslc
journal: HMAC-SHA256 footer, fsync-batched 1024-count flushes, fail-
closed on integrity mismatch, warn-and-accept on a torn write with no
footer.
Limitations
- No AAD for AES-GCM. The core
CryptoBackendtrait does not thread additional authenticated data through its API, so AAD cannot be supplied via this backend today. - RSA-OAEP uses MGF1 with the OAEP hash; label and MGF1 hash are not independently configurable.
- RSA-PSS salt length is fixed at digest length (the RFC 8017 SHOULD value); configurable salt length is not exposed.
- Not implemented: ChaCha20-Poly1305, AES-GCM-SIV, AES-CCM, AES-CMAC, HMAC, X25519, X448. These gaps live in the core trait and affect every backend.
- The nonce counter defaults to per-process memory. Long-lived keys used across restarts need either rotation, a higher-level counter, or the persistent journal above.
Related Documents
- AWS-LC backend — self-contained FIPS boundary.
- Compatibility matrix — tested OpenSSL versions.