FIPS 140-3

Government deployments need every cryptographic primitive to flow through a FIPS-validated module. The tetrapus-fips facade wraps the four primitives Tetrapus actually uses (SHA-256, HMAC-SHA-256, AES-256-GCM, RNG) so the validated provider can be swapped in via a single cargo feature.

Status

We ship the FIPS-validated module integration — the build matrix, the lint, the startup self-test, and the API surface. Formal certification (the customer's NIST-recognised lab walking the boundary) is an ongoing process and is part of a customer engagement, not a shrink-wrap claim.

Backend swap

The cargo feature fips swaps the default RustCrypto stack (sha2, hmac, aes-gcm, rand) for aws-lc-rs backed by aws-lc-fips-sys. The public Rust API is identical in both modes — calling code never branches on is_fips_mode() to choose primitives, only to surface posture in /health output.

Bash
cargo build --release -p tetrapus-server --features fips
./target/release/tetrapus-server --version
# tetrapus-server 0.3.0 (fips)

forbid-ring lint

A cargo-deny rule blocks any direct dependency on ring in the FIPS build profile. Transitive pulls are quarantined to the FIPS-validated provider crate. CI fails the FIPS matrix job if a pull request reintroduces a non-validated path.

TOML deny.toml (excerpt)
[bans]
deny = [
  { name = "ring", reason = "use aws-lc-rs in fips builds" },
  { name = "rustls", wrappers = ["aws-lc-rs"] },
]

Startup self-test

tetrapus_fips::fips_self_test() is called once at process boot. It runs a known-answer test against each primitive and, in FIPS mode, surfaces the underlying provider's power-on integrity self-test (POST). Failure returns CryptoError::IntegritySelfTest and the process exits non-zero — there is no graceful continuation.

Primitive Default backend FIPS backend KAT
SHA-256sha2aws-lc-rs"abc" → ba7816bf…
HMAC-SHA-256hmac + sha2aws-lc-rsempty key, empty msg
AES-256-GCMaes-gcmaws-lc-rsround-trip "tetrapus-kat"
RNGrandaws-lc-rs CTR_DRBGsmoke (provider POST)

Build matrix

Every PR runs both backends in CI so a FIPS regression in shared code is caught before merge.

YAML .github/workflows/ci.yml (excerpt)
strategy:
  matrix:
    profile: [default, fips]
    include:
      - profile: default
        features: ""
      - profile: fips
        features: "--features fips"
        deny: "deny.toml"
steps:
  - run: cargo build --release ${{ matrix.features }}
  - run: cargo test --workspace ${{ matrix.features }}
  - if: matrix.profile == 'fips'
    run: cargo deny --config ${{ matrix.deny }} check bans

Crates that use the facade

Anything in Tetrapus that needs SHA-256, HMAC, AES-GCM, or random bytes must route through tetrapus-fips. The current consumers:

  • tetrapus-audit — hash-chained audit row digests
  • tetrapus-attestation — Merkle leaf and inner-node hashing
  • tetrapus-cmek — AES-256-GCM at-rest envelope encryption
  • tetrapus-auth — refresh-token and session-id randomness
  • tetrapus-server — boot-time fips_self_test()

Note that the KMS clients (tetrapus-kms) do their cryptographic work inside the remote provider — AWS KMS, GCP KMS, and Vault Transit are themselves FIPS-validated services, and the local code path only does signature transport.

Related

Questions?

Reach out for help with integration, deployment, or custom domain codecs.