docs/operations/kms-cryptographic-compliance.md
This document records where RustFS stands on cryptographic module validation, what may and may not be said about it in external material, and what each possible route to a stronger position would actually cost. It exists so that the question is answered once, from the code, instead of being re-litigated from assumptions about crate names and feature flags.
For where master key material lives per backend and how rotation retention works, see KMS backend security properties.
RustFS is not FIPS 140-3 (or 140-2) validated, and no component it links is running as a validated cryptographic module. There is no CMVP certificate covering RustFS or the libraries it uses in the shipped configuration.
This is a deliberate position, not an oversight. It is also not a statement about algorithm strength: the algorithms in use are standard, well-reviewed AEADs. Validation is a property of a specific module build, its documented boundary, and a certificate — none of which RustFS has or currently pursues.
The table below is the audited inventory as of this document's writing. "Validated module" asks only whether the code performing the operation is a FIPS-validated cryptographic module; the answer is uniformly no.
| Layer | Where | Implementation | Primitives | Validated module |
|---|---|---|---|---|
| TLS (S3 server, internode, outbound clients) | Process-wide default provider installed by install_default_crypto_provider in rustfs/src/startup_runtime_hooks.rs | rustls with the aws-lc-rs provider | TLS 1.2/1.3 suites, prefer-post-quantum hybrid key exchange | No — this is the ordinary aws-lc-rs build, not the aws-lc-fips-sys-backed FIPS variant |
| Object data path AEAD (SSE) | crates/kms/src/encryption/ciphers.rs, crates/rio/src/encrypt_reader.rs, crates/rio-v2/src/encrypt_reader.rs | RustCrypto aes-gcm, chacha20poly1305 | AES-256-GCM, ChaCha20-Poly1305 | No |
| DEK wrapping | crates/kms/src/encryption/dek.rs | RustCrypto aes-gcm | AES-256-GCM | No |
| Local KMS backend master key | crates/kms/src/backends/local.rs | RustCrypto argon2, aes-gcm | Argon2id KDF, AES-256-GCM | No |
| Config and IAM blobs at rest | crates/crypto/src/encdec/ (rustfs-crypto) | RustCrypto pbkdf2/argon2, aes-gcm, chacha20poly1305, sha2 | see the fips feature | No |
| JWT signing and verification | jsonwebtoken with the aws_lc_rs feature (crates/crypto, crates/iam, crates/policy) | AWS-LC through aws-lc-rs | Non-FIPS build | No |
Two consequences follow directly from the table and are worth stating explicitly, because both are commonly assumed the other way:
aws-lc-rs has a FIPS variant; the workspace does not enable it. Every aws-lc-rs dependency in the workspace is the default, non-FIPS build.These rules apply to the README, CHANGELOG, release notes, marketing pages, sales decks, RFP responses, and security questionnaires. Claiming validation RustFS does not have is a false statement of fact with regulatory and contractual consequences, not a marketing overreach.
fips as a product capability. It is a build-time algorithm selector (see below), and surfacing it as a feature name invites exactly the misreading this section exists to prevent.Suggested boilerplate when the topic cannot be avoided:
RustFS encrypts object data with AES-256-GCM and supports ChaCha20-Poly1305. These are FIPS-approved algorithms, but the implementations are not FIPS 140-3 validated cryptographic modules and RustFS makes no FIPS validation claim.
README.md and CHANGELOG.md currently contain no FIPS-related wording; scripts/check_fips_wording.sh is the grep guard for that public baseline. Any future occurrence of the banned strings in either file should be treated as a defect and either removed or brought under the qualifier rule above. This document intentionally contains the terminology needed to define the policy and is not part of that narrow outward-material scan.
rustfs-crypto fips feature: what it actually doescrates/crypto/Cargo.toml declares default = ["crypto", "fips"], so the feature is on in every normal build. Its entire effect is which algorithm the write path selects; the implementation is RustCrypto either way.
fips | Algorithm ID written | KDF | AEAD |
|---|---|---|---|
| enabled (default) | ID::Pbkdf2AESGCM (0x02) | PBKDF2-HMAC-SHA256, 8192 iterations | AES-256-GCM |
| disabled | ID::Argon2idAESGCM (0x00) or ID::Argon2idChaCHa20Poly1305 (0x01), chosen at runtime by CPU AES support | Argon2id (64 MiB, t=1, p=4) | AES-256-GCM or ChaCha20-Poly1305 |
The selection sites are crates/crypto/src/encdec/encrypt.rs and crates/crypto/src/encdec/stream_io.rs; the algorithm identifiers and their KDF parameters live in crates/crypto/src/encdec/id.rs.
Three properties matter for anyone reasoning about this feature:
crates/crypto/src/encdec/id.rs accepts all three identifiers unconditionally, and every ciphertext carries its identifier byte. Toggling the feature therefore never orphans existing data in either direction.The name fips states a compliance property the feature does not provide, and rustfs-crypto is published, so the name is visible to downstream consumers. Recommended direction:
fips-preferred-algs as the real feature name, carrying the current behavior.fips = ["fips-preferred-algs"] so existing consumers keep building, and mark it deprecated in the crate documentation with a pointer to this document.fips alias after one release cycle.This is a naming and documentation change only; no ciphertext format changes, because the identifier bytes stay as they are.
aws-lc-rs FIPS variantSwitch the whole process to aws-lc-rs's FIPS build (backed by aws-lc-fips-sys) so cryptographic operations run inside a validated module boundary.
Scope. The TLS provider swap is the small part — one feature flag plus the provider install sites. The substantial work is the data path: every AEAD call in crates/kms/src/encryption/ciphers.rs, crates/kms/src/encryption/dek.rs, crates/rio/src/encrypt_reader.rs, crates/rio-v2/src/encrypt_reader.rs, crates/kms/src/backends/local.rs, and crates/crypto/src/encdec/ would have to be re-implemented against aws-lc-rs primitives. Anything the validated module does not expose has to be dropped or moved out of the boundary: Argon2id has no FIPS status, so the Local backend's KDF and the non-FIPS branch of rustfs-crypto would need a compatibility story (read-only support for existing records, PBKDF2 for new ones), and ChaCha20-Poly1305 would become non-approved for new writes.
Build and platform cost. aws-lc-fips-sys builds a pinned, validated source release and needs CMake, a C toolchain, and Go at build time; it supports a narrower target set than the ordinary crate. The platform matrix cost of plain AWS-LC is already documented and non-hypothetical: rustfs/backlog#883 records that the static musl release build compiles AWS-LC's getentropy entropy backend, which aborts on Linux kernels older than 3.17 (the Synology class of device), and that upstream considers this by design with no plan to fix it. The FIPS variant constrains the buildable matrix strictly harder than that, and pins upgrades to whatever the certified source revision allows.
What it would and would not buy. Linking the validated module makes the accurate claim "cryptographic operations are performed by a FIPS 140-3 validated module", not "RustFS is FIPS validated". A product-level claim additionally requires a documented module boundary, approved-mode enforcement, power-on self-tests, key zeroization, and entropy-source documentation, plus the operational procedures to keep them true across releases.
Verdict. Heavy, and it re-opens a platform-support question that is already an open problem. Justified only by a concrete customer or regulatory commitment that names FIPS as a requirement.
Keep RustFS as-is and place key management inside someone else's validated boundary: the Vault Transit backend against a Vault deployment whose seal/HSM is validated, or an equivalent managed KMS.
Scope. Mostly already built. The Transit backend (VaultTransit) never lets key-encryption key material leave Vault; RustFS only ever holds Transit ciphertext. What remains is configuration guidance, a supported-deployment statement, and the operational documentation that says which parts of the system are covered.
What it buys. Master key generation, wrapping, unwrapping, and rotation happen inside the external module. That is a real, defensible partial answer to "where do keys live and who validated that": it covers the key operations, which is often the part an auditor actually asks about.
What it does not buy. The object data path is untouched. DEKs are used for bulk AEAD by RustCrypto inside the RustFS process, and TLS still runs the non-FIPS AWS-LC build. The honest formulation is "key management operations are performed by an externally validated module; the object data path is not validated".
Verdict. The nearest partial step, with no code rewrite and no platform-matrix risk. This is the route to point customers at when the requirement is about key custody rather than about a certificate covering the storage layer.
Document the position, hold the terminology line, and revisit only when a requirement with a name attached shows up.
Cost. This document plus the grep guard. Nothing else.
Verdict. The current decision. FIPS 140-3 validation is explicitly not a roadmap target, and adjacent items (PKCS#11, KMIP, BYOK, signing keys) are deferred for lack of demand and because HSM-dependent paths cannot be exercised in CI.
Retiring an algorithm from a storage system is not a code change; it is a data migration with a code change at each end. This section fixes the sequence so that no future deprecation removes a decrypt path while data still depends on it.
The precondition for safe migration already holds: nothing relies on a global "current algorithm" setting to be decodable.
rustfs-crypto blobs carry the ID byte (crates/crypto/src/encdec/id.rs) immediately after the salt.EncryptionAlgorithm (crates/kms/src/types.rs).DataKeyEnvelope::master_key_version (crates/kms/src/encryption/dek.rs).So for any stored object it is decidable, from the object alone, which algorithm and which key version it needs.
Retirement moves an algorithm through these states, never skipping one:
Step 3 is currently unreachable for object data. There is no object rewrap or re-encryption capability, so there is no supported way to migrate already-written objects off an algorithm or off a master key version — the same gap that forces the rotation retention rule in KMS backend security properties. Until a rewrap capability exists, treat every algorithm that has ever been written as permanently read-required, and confine deprecation to step 1.