FreeHSM C

An open-source PKCS#11 v3.2 cryptographic module under Simorgh Labs

View the Project on GitHub afchine1337/freehsm

FreeHSM C — Non-Proprietary Security Policy (FIPS 140-3 Level 1 method, uncertified)

Not certified. FreeHSM is built to the requirements of FIPS 140-3 Level 1 and documented with its methodology. A certificate costs more than this project will ever have, and that cost is exactly the barrier that keeps public bodies, universities and developing countries away from auditable cryptography.

This document is written, as the methodology requires, in the register of a submission. The two lines below name the standard it is written to and the authority that would receive it — not an engagement anyone has entered. docs/FIPS_140_3_SECURITY_TARGET.md says the same in its own status note, and docs/AGD_PRE.md and docs/AGD_OPE.md carry this banner; this file lacked it, which left “Target validation” and “Validation authority” reading as a live intent. See Certification in README.md.

Module name: FreeHSM Cryptographic Module (libfreehsm.so) Version: 1.0.0-FIPS Module type: Software, Level 1 (multi-chip standalone, OS bound) Written to: FIPS 140-3 (ISO/IEC 19790:2012 + 24759:2017) Would be received by: NIST CMVP (US) / CCCS (Canada) — no submission made


1. Module overview

FreeHSM is a software cryptographic module that exposes a PKCS#11 v3.2 interface to applications. It is intended for use as a Soft HSM in production environments where a hardware HSM is unavailable but FIPS 140-3 Level 1 conformance is required (e.g. internal PKIs, code-signing servers, regulated SaaS workloads).

The module’s cryptographic boundary is the single shared object libfreehsm.so plus the OpenSSL FIPS provider (fips.so) that it dynamically loads at C_Initialize. All other software — including the calling application, the host OS, and the OpenSSL base/default providers — is outside the boundary.

2. Cryptographic boundary

                  ┌─────────────────────────────────────────────┐
                  │     Application                             │
                  └──────────────────┬──────────────────────────┘
                                     │  PKCS#11 v3.2 ABI (C_*)
   ┌─── boundary ───────────────────┼─────────────────────────────┐
   │                                ▼                              │
   │         libfreehsm.so (this module)                       │
   │            fhsm_pkcs11.c    --- entry points (audit + dispatch)│
   │            fhsm_crypto.c    --- KAT, AES-GCM, HMAC, PBKDF2     │
   │            fhsm_token.c     --- token store, PIN, throttle     │
   │            fhsm_session.c   --- session table                  │
   │            fhsm_audit.c     --- HMAC-chained audit log         │
   │            fhsm_memory.c    --- secure heap, zeroize           │
   │            fhsm_state.c     --- state machine                  │
   │                                ▼                              │
   │         OpenSSL FIPS provider (fips.so)                        │
   └────────────────────────────────────────────────────────────────┘

3. Roles, services, authentication

The module supports two operator roles:

Role Identifier in PKCS#11 Authentication
Crypto Officer (CO) CKU_SO PBKDF2-HMAC-SHA-256 200_000 iter
User CKU_USER PBKDF2-HMAC-SHA-256 200_000 iter

Authentication is per slot. PINs must be at least 8 characters. After FHSM_PIN_MAX_FAILED = 5 consecutive failures, the role is locked (FHSM_RV_PIN_LOCKED); only C_InitPIN (for USER, called by SO) or C_InitToken (for both) clears the lock.

Between failed attempts, the module enforces exponential backoff: delay_n = min(500 ms * 2^(n-1), 60 s). The throttle is persisted in the token file so a process restart does not bypass it.

3.1 Services map

Service Role(s) allowed Approved? FIPS mode?
C_Initialize (none) n/a yes
C_GetInfo (none) n/a yes
C_Login CO, USER n/a yes
C_GenerateKey CO, USER yes yes
C_Encrypt (AES-GCM) USER yes yes
C_Decrypt (AES-GCM) USER yes yes
C_Sign (RSA-PSS, ECDSA, EdDSA, ML-DSA) USER yes yes
C_Verify (idem) USER yes yes
C_Digest (SHA-2, SHA-3) (none) yes yes
C_DeriveKey (HKDF, PBKDF2) USER yes yes
C_EncapsulateKey (ML-KEM) USER yes yes
C_DecapsulateKey (ML-KEM) USER yes yes
C_Sign (MD5, SHA-1, DSA, RSA-PKCS1-v1.5) — no rejected

Services marked not approved return FHSM_RV_FIPS_NOT_APPROVED when FHSM_FIPS_ONLY = 1. They remain dispatchable in non-approved builds for interoperability with legacy systems; transition into / out of approved mode is signaled in the audit log (state_transition event).

4. Approved algorithms

Family Variant / parameter set Reference CAVP cert #
AES 128 / 192 / 256-GCM, CBC, KW, KWP FIPS 197 / SP 800-38{A,D,F} TBD
SHA-2 SHA-256, SHA-384, SHA-512 FIPS 180-4 TBD
SHA-3 SHA3-256/384/512, SHAKE128/256 FIPS 202 TBD
HMAC with all approved SHA-2/3; SHA-1 in HMAC only FIPS 198-1, SP 800-131A rev. 2 TBD
RSA-PSS 2048, 3072, 4096 FIPS 186-5 TBD
ECDSA P-256, P-384, P-521 FIPS 186-5 TBD
EdDSA Ed25519, Ed448 FIPS 186-5 TBD
ECDH/X25519/X448 P-256, P-384, X25519, X448 SP 800-56A TBD
ML-KEM 512 / 768 / 1024 FIPS 203 TBD
ML-DSA 44 / 65 / 87 FIPS 204 TBD
SLH-DSA SHA2/SHAKE × 128/192/256 × s/f FIPS 205 TBD
HKDF with all approved SHA-2/3; SHA-1 as PRF SP 800-56C rev. 2 TBD
PBKDF2 HMAC-SHA-256, ≥ 200_000 iter SP 800-132 TBD
CTR_DRBG AES-256, derivation function SP 800-90A TBD
Entropy source Linux getrandom(GRND_RANDOM) SP 800-90B ESV TBD

SHA-1, and why it appears in two rows above and in none of the signature rows. SP 800-131A rev. 2 scopes the SHA-1 prohibition to digital signature generation. It remains acceptable inside HMAC, and therefore inside the HMAC-based KDFs of SP 800-56C rev. 2. The module follows that boundary rather than a blanket rule: CKM_SHA_1_HMAC and CKM_HKDF_DERIVE with prfHashMechanism = CKM_SHA_1 are accepted under fips-strict, while C_Digest(CKM_SHA_1) and CKM_SHA1_RSA_PKCS are refused by it.

Until 2026-09-19 the code did not draw that boundary. One flag marked SHA-1 non-approved for every use, so HMAC-SHA-1 was approved when reached through C_SignInit and refused when reached through HKDF’s PRF – one primitive, two paths, two answers. It failed closed, so it cost no safety; it cost the 84 valid vectors of hkdf_sha1_test.json and an explanation on every reading of the report. This table said SHA-2/3 only, and docs/MECHANISMS.md, which is generated from the profile, said CKM_SHA_1_HMAC was approved. The two disagreed about the same primitive. The generated one was right.

The TBD cells will be filled in once the CMVP queue advances and the lab runs ACVP. The full CAVP test vector set is loaded at module init via fhsm_kat_run_all (see §6).

5. Sensitive Security Parameters (SSP)

All SSPs are stored exclusively in the OpenSSL secure heap (OPENSSL_secure_malloc), which is a single mlock-ed mmap arena. The arena is sized at build time (default 8 MiB, configurable via FHSM_SECURE_HEAP_BYTES or the secure_heap_kb setting) and holds every sensitive object value, not only the DEKs (#127). Allocations are returned to the arena via OPENSSL_secure_clear_free, which zeroizes the block before release.

SSP Algorithm Storage Zeroization trigger
DEK (Data Encryption Key) AES-256 Secure heap (live) C_Logout, C_Finalize, lock
KEK derivés du PIN AES-256 Secure heap (live) After token write
Private keys (RSA, EC, PQ) RSA-2048 to 4096, EC, PQ Secure heap (live) C_DestroyObject, logout
Audit MAC key HMAC-SHA-256 Secure heap (live) C_Finalize
KAT plaintexts (see §6) Secure heap (transient) End of KAT
DRBG state CTR_DRBG OpenSSL FIPS provider Provider unload

6. Self-tests

6.1 Pre-operational self-tests (FIPS 140-3 §7.10.2)

Executed at C_Initialize, before any service becomes available:

  1. Software integrity test (§7.10.2.a): SHA-256 of the entire libfreehsm.so is computed and compared to the value embedded at build time in fhsm_module_integrity_digest[]. Mismatch → ERROR state.
  2. Hardened DRBG initialisation: fhsm_drbg_init runs the multi-source seed (getrandom + RDRAND + /dev/urandom + TSC jitter → SHA-384 conditioner → RAND_add). Source unavailability → ERROR. See RNG.md for the pipeline.
  3. Cryptographic algorithm self-test (KAT): every approved primitive (§4) is exercised against a CAVP-derived test vector. The base set lives in kat/fhsm_kat_vectors.c; v1.1.0 adds an extended CAVP set in kat/cavp_extended.c (2 AES-GCM-256 from NIST SP 800-38D Annex B + 4 HMAC-SHA-256 from RFC 4231). Any mismatch → ERROR.

6.2 Conditional self-tests (§7.10.3)

Trigger Test Implementation
AEAD decrypt Tag verification (constant-time) fhsm_aes_gcm_decrypt rejects on tag mismatch
Signature verify Algorithm-specific verification Inline in C_Verify
Asymmetric key generation (§7.10.2.b) Pair-wise consistency check : encrypt-decrypt round-trip (RSA), sign-verify (EC, ML-DSA, SLH-DSA), encap-decap (ML-KEM). Failure latches ERROR and refuses to persist the keypair. src/fhsm_pairwise.c, invoked inline by C_GenerateKeyPair
RNG output (§7.10.3.b) Continuous test : RCT (cutoff 6 successive identical bytes) + APT (W=512, cutoff 51) + CRNGT (16-byte block compared between calls). src/fhsm_drbg.c health_rct_apt + health_crngt

6.3 KAT report

After init, fhsm_kat_results() returns the structured pass/fail/duration vector. The same data is appended to the audit log as event kat_report.

7. Operational environment

The module is built and tested against the following operational environment (it is not validated, and no validation is planned):

Operators must:

  1. Ensure the OpenSSL FIPS provider is loaded and validated (openssl list -providers shows fips with status active).
  2. Store the token files (*.tok) and audit log files (*.audit.log) on a filesystem that is not network-mounted (NFS/CIFS can lose atomicity).
  3. Run the module under a dedicated UNIX user with CAP_IPC_LOCK to allow secure-heap mlock.

8. Mitigations of other attacks

Attack Mitigation
Timing channels on PIN Throttle is enforced before PBKDF2; constant-time tag/MAC compare
Cold-boot DEK exfiltration Secure heap mlock + zeroize on free
Coredump leak prctl(PR_SET_DUMPABLE, 0) at C_Initialize
Log injection Audit emit refuses any byte outside [0x20, 0x7e] and [",\\]
Tamper of audit HMAC-chained log + token-derived MAC key
Brute-force on PIN Exponential backoff + max-5 lock
DRBG seed exhaustion SP 800-90A reseed thresholds + reseed entry point