Craton HSM
Self-Tests
Self-Tests
Craton HSM runs a set of self-tests at module load time and further conditional self-tests at runtime. The tests follow FIPS 140-3 §9 (self-tests) and cover software integrity, cryptographic correctness, DRBG health, and per-key-pair consistency. Any failure transitions the module into an error state that cannot be cleared without a process restart.
Power-On Self-Tests
POST runs during C_Initialize, before any cryptographic service becomes
available. The tests execute in a fixed order. Failure of any test sets an
AtomicBool POST_FAILED flag, causes C_Initialize to return
CKR_GENERAL_ERROR, and makes every subsequent PKCS#11 call return
CKR_GENERAL_ERROR until the process restarts.
The full POST suite is 17 tests: one software integrity check and 16 known-answer tests.
Software Integrity Test (§9.4)
| Property | Value |
|---|---|
| Algorithm | HMAC-SHA256 |
| Key | 32-byte compile-time constant embedded in the binary (per FIPS IG 9.7) |
| Input | Full contents of the loaded module file |
| Expected digest | 64 hex characters in a .hmac sidecar alongside the library |
| Order | First test — runs before any KAT |
Behaviour when the sidecar is missing depends on the build: under the
default features the check logs a warning and passes (development mode);
under the FIPS build feature the sidecar is mandatory and its absence fails
POST. A mismatched HMAC always fails POST. The module path is discovered
with dladdr(3) on Unix and GetModuleHandleExW / GetModuleFileNameW on
Windows.
Known-Answer Tests
| # | Algorithm | Vector source | Notes |
|---|---|---|---|
| 1 | SHA-256 | NIST FIPS 180-4, input "abc" | |
| 2 | SHA-384 | NIST FIPS 180-4, input "abc" | Checks prefix of expected digest |
| 3 | SHA-512 | NIST FIPS 180-4, input "abc" | Checks prefix of expected digest |
| 4 | SHA3-256 | NIST FIPS 202, input "abc" | |
| 5 | HMAC-SHA256 | RFC 4231 Test Case 2 | |
| 6 | HMAC-SHA384 | RFC 4231 Test Case 2 | Checks prefix of expected MAC |
| 7 | HMAC-SHA512 | RFC 4231 Test Case 2 | Checks prefix of expected MAC |
| 8 | AES-256-GCM | Fixed key, known-answer decrypt | |
| 9 | AES-256-CBC | Fixed key and IV, hardcoded expected ciphertext | Upgraded from round-trip to genuine KAT in v0.9.1 |
| 10 | AES-256-CTR | Fixed key and IV, hardcoded expected ciphertext | Upgraded from round-trip to genuine KAT in v0.9.1 |
| 11 | RSA-2048 PKCS#1 v1.5 | Generated key pair, sign/verify round-trip | Added in v0.9.1 |
| 12 | ECDSA P-256 | Generated key pair, sign/verify round-trip | |
| 13 | ML-DSA-44 | Generated key pair, sign/verify round-trip | Not approved, runs for correctness in non-FIPS mode |
| 14 | ML-KEM-768 | Generated key pair, encap/decap round-trip | Not approved, runs for correctness in non-FIPS mode |
| 15 | OS RNG health | Generate 256 bytes, check not all zero / not all identical; generate a second 256 bytes and compare against the first | Per SP 800-90B §4.3 |
| 16 | HMAC_DRBG health | Instantiate DRBG, generate two 32-byte outputs, compare | |
| 17 | HMAC_DRBG KAT | NIST CAVP vector | Validates DRBG correctness with a known input/output pair |
Non-approved algorithm KATs (ML-DSA, ML-KEM) run in both modes so that any build-time breakage is caught early. In FIPS mode those mechanisms are still rejected at the enforcement layer; a passing KAT does not make the mechanism approved.
Conditional Self-Tests
Pairwise Consistency Tests (§9.6)
A pairwise consistency test runs immediately after every asymmetric key
pair generation. A signature algorithm signs the fixed test string
"FIPS 140-3 pairwise consistency test" with the generated private key
and verifies with the generated public key; KEM algorithms perform an
encap/decap round-trip.
| Key type | Triggering mechanism | Test |
|---|---|---|
| RSA | CKM_RSA_PKCS_KEY_PAIR_GEN | SHA-256 RSA PKCS#1 v1.5 sign/verify |
| ECDSA P-256 | CKM_EC_KEY_PAIR_GEN with P-256 | ECDSA sign/verify |
| ECDSA P-384 | CKM_EC_KEY_PAIR_GEN with P-384 | ECDSA sign/verify |
| Ed25519 | CKM_EDDSA | Ed25519 sign/verify |
| ML-DSA | CKM_ML_DSA_44 / CKM_ML_DSA_65 / CKM_ML_DSA_87 | ML-DSA sign/verify |
| SLH-DSA | CKM_SLH_DSA_SHA2_128S / CKM_SLH_DSA_SHA2_256S | SLH-DSA sign/verify |
| ML-KEM | CKM_ML_KEM_512 / CKM_ML_KEM_768 / CKM_ML_KEM_1024 | ML-KEM encap/decap |
A failed pairwise test fails the enclosing C_GenerateKeyPair call and
puts the module in error state. The generated key material is zeroized
before the error is returned.
Continuous RNG Tests
Two continuous tests run on every random number generator call:
- OS entropy source —
C_GenerateRandomstores the last 32 bytes of output in aMutex<[u8; 32]>and compares each new block against the previous one. Identical consecutive output returnsCKR_FUNCTION_FAILEDand sets POST_FAILED. - HMAC_DRBG — the DRBG keeps a
last_outputfield and compares consecutivegeneratecalls. A match aborts the operation, zeroizes the DRBG state, and raises the error state.
Pairwise Imports
C_UnwrapKey and C_CreateObject do not run pairwise consistency on
imported key pairs because the caller supplies key material rather than
generating it. Imported public keys are validated for structural
correctness (curve membership, RSA modulus parity) but do not re-run the
KAT suite.
Failure Behaviour
All self-test failures converge on a single error state:
POST_FAILED: AtomicBoolis set totrue.get_hsm()checks the flag on every PKCS#11 call and returnsCKR_GENERAL_ERRORunconditionally.- No cryptographic service can run until the process restarts.
- On the next
C_Initialize, POST_FAILED is cleared and POST runs again before any service becomes available.
The module does not attempt automatic recovery. A POST failure is a signal that either the binary is corrupt, the environment is hostile (stuck RNG), or a primitive has a real defect — none of those are safe to paper over at runtime.
Operational Expectations
- POST runs in under 100 ms on modern hardware, dominated by the RSA key generation for test 11.
- Conditional tests add a small constant overhead per key pair generation. Applications that generate keys in tight loops should prefer a key ceremony tool and persist the result, rather than regenerate per request.
- POST does not depend on token state or user login. It runs even for anonymous callers doing only digest or random operations.
Further Reading
- fips-mode — operational configuration that activates strict approved-mode enforcement
- gap-analysis — which KATs are approved vs. informational
- ../security/model — broader security posture