# FreeHSM C --- Non-Proprietary Security Policy (FIPS 140-3 candidate)

> **Not certified, and not seeking certification.** FreeHSM is built to the
> requirements of FIPS 140-3 Level 1 and documented with its methodology. It
> holds no certificate and will not pursue one: 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        | FIPS 198-1    | 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 SHA-256/384/512            | SP 800-56C    | 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      |

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-256 conditioner → `RAND_add`). Source unavailability → ERROR. See [`RNG.md`](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):

- OS: Linux ≥ 5.4 with `mlock` permission and `RLIMIT_MEMLOCK` ≥ 1 MiB
- libc: glibc ≥ 2.31 or musl ≥ 1.2
- OpenSSL: 3.x with the FIPS provider installed and listed in `openssl.cnf`
- Filesystem: ext4 or xfs for the audit log (atomic `append` semantics)

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 |
