/ Documentation / Security / Cryptography

Cryptography

The primitives LUKSbox uses and where each one comes from.

Primitives reference

Purpose Primitive Source crate
File / metadata AEAD AES-256-GCM-SIV (default; RFC 8452, nonce-misuse-resistant) RustCrypto aes-gcm-siv
Alternate AEAD AES-256-GCM (legacy compat) RustCrypto aes-gcm
Alternate AEAD ChaCha20-Poly1305 (constant-time on every CPU) RustCrypto chacha20poly1305
Header MAC HMAC-SHA256 RustCrypto hmac + sha2
Subkey derivation HKDF-SHA256 with per-purpose info strings RustCrypto hkdf
Passphrase stretching Argon2id (interactive: 256 MiB / t=3 / p=4; sensitive: 1 GiB / t=4 / p=8) RustCrypto argon2
FIDO2 hmac-secret transport ECDH-P256 + AES-256-CBC + HMAC-SHA256 (CTAP2 section 6.5) hand-rolled in luksbox-fido2/src/protocol.rs (verified against OpenSSL test vectors); libfido2 / webauthn.dll for the device side
Post-quantum KEM (hybrid mode) ML-KEM-768 or ML-KEM-1024 (FIPS 203) RustCrypto ml-kem
RNG OS RNG (getrandom / BCryptGenRandom / SecRandomCopyBytes) rand_core::OsRng

Why these specific choices

AES-256-GCM-SIV as default

Random 96-bit nonces under vanilla AES-GCM have a NIST-recommended bound of 2^32 messages per key. AES-GCM-SIV (RFC 8452) is nonce-misuse-resistant: a nonce collision under the same key reveals only that two messages had identical (key, nonce, AAD, plaintext) tuples - never the GHASH key. Same 12-byte nonce + 16-byte tag wire shape as vanilla GCM, so on-disk format is byte-identical regardless of which AES variant a vault was created with.

Argon2id for passphrase stretching

Memory-hard, side-channel-resistant, NIST-recommended (SP 800-63B allows it). The "interactive" preset is calibrated for 540 ms on a modern CPU, the "sensitive" preset for 3.2 sec. See Passphrase keyslots for the math behind these choices.

HKDF-SHA256 with explicit info strings

Every per-purpose subkey derivation uses a unique info string (lbx:header-mac/v1, lbx:metadata-key/v1, lbx:file/v1: || file_id, etc.) so subkeys for different purposes are domain-separated even when derived from the same MVK. The full list of info strings is in docs/CRYPTO_SPEC.md Section 3.3.

ML-KEM (FIPS 203) for the post-quantum layer

NIST-standardised in August 2024. ML-KEM-768 is security category 3 (approximately AES-192 strength); ML-KEM-1024 is category 5 (approximately AES-256). The hybrid construction combines the classical Argon2id-derived KEK with the ML-KEM shared secret via HKDF-SHA256, so a future cryptanalytic break in EITHER family doesn't break the vault - only a break in BOTH does.

Key derivation tree

Everything in the vault traces back to the Master Volume Key (MVK) by HKDF-SHA256 with a unique label per purpose:


flowchart TD
    RNG["OS RNG (getrandom / getentropy / BCryptGenRandom)"] --> MVK
    MVK["Master Volume Key (MVK)
32 random bytes
in-memory only
(Linux: memfd_secret)"] MVK -->|HKDF info = lbx:header-mac/v1| MAC["mac_key
HMAC over the 8 KiB header"] MVK -->|HKDF info = lbx:metadata-key/v1| META["meta_key
AEAD over the metadata blob"] MVK -->|HKDF info = lbx:file/v1: + file_id| FK["file_key (one per file)
AEAD over each chunk"] MVK -->|HKDF info = lbx:anchor-mac/v1| ANC["anchor_key
HMAC over the rollback anchor"] classDef root fill:#fff4e6,stroke:#d97706,stroke-width:2px; classDef leaf fill:#ecfdf5,stroke:#059669; class MVK root; class MAC,META,FK,ANC leaf;

The four arrows out of the MVK use four distinct info strings, so the four subkeys are independent under the HKDF security argument: a leak of any one subkey does not let an attacker recover the MVK or any sibling subkey. The per-file file_key also mixes the file's file_id into its info string, so two files in the same vault have unrelated AEAD keys even if their plaintext is identical.

For the full per-operation crypto walkthrough (15 scenarios from vault creation through tampering detection), see docs/CRYPTO_SPEC.md in the source repository.

What we DON'T use (and why)

Not used Why
SHA-1, MD5 Cryptographically broken
AES-XTS (LUKS-style) XTS leaks more than AEAD on tamper - we want authenticated encryption
RSA Quantum-broken; ML-KEM is the modern KEM
PBKDF2 Not memory-hard; Argon2id is strictly better
scrypt Memory-hard but Argon2id won the PHC competition specifically for this use case
BLAKE3 Excellent hash, but SHA-256 has wider hardware acceleration + standardisation