Key Derivation
What It Is
Key derivation is the process of turning one secret into one or more cryptographic keys that are safe to use. Instead of using a secret directly as an encryption key, you run it through a Key Derivation Function (KDF) that produces output with the right properties for cryptographic use.
FortrOS uses HKDF (HMAC-based Key Derivation Function, RFC 5869) for all key derivation.
Why It Matters
You might ask: "If I have a secret, why can't I just use it as the encryption key?" Three reasons:
1. The secret might have structure. A Diffie-Hellman shared secret (from a TLS key exchange) isn't uniformly random -- it has mathematical structure from the key agreement algorithm. Using it directly as an AES key could leak information. A KDF removes that structure, producing output that's indistinguishable from random.
2. You need multiple keys from one secret. A single connection might need
an encryption key, a MAC key, and an initialization vector. Using the same
key for both encryption and authentication is a crypto sin -- it can break the
security of both. A KDF lets you derive separate keys from one secret:
encrypt_key = KDF(secret, "encryption"), mac_key = KDF(secret, "mac").
3. You need deterministic re-derivation. If a machine stores a preboot_secret in the TPM and needs to derive the same LUKS encryption key on every boot, the derivation must be deterministic -- same input always produces the same output. A KDF provides this while still being cryptographically secure.
How It Works
HKDF (RFC 5869)
HKDF has two phases:
Extract: Takes raw input key material (IKM) and an optional salt, produces a fixed-length pseudorandom key (PRK).
PRK = HMAC-SHA256(salt, input_key_material)
This "concentrates" the entropy from potentially structured input into a uniformly distributed key. If the input is already uniformly random (like a random 32-byte secret), you can skip Extract.
Expand: Takes the PRK and a context string ("info"), produces output keys of any desired length.
output_key = HMAC-SHA256(PRK, info || 0x01)
The info string is critical -- it provides key separation. Different info strings produce completely unrelated keys from the same PRK:
luks_key = HKDF-Expand(PRK, info="luks-persist") # 32 bytes
hmac_key = HKDF-Expand(PRK, info="auth-hmac") # 32 bytes
Even though both derive from the same secret, the luks_key and hmac_key are cryptographically independent. Compromising one reveals nothing about the other.
FortrOS Key Derivation
FortrOS derives the LUKS encryption key for /persist using:
luks_key = HKDF-SHA256(
input_key_material = preboot_secret, # 32 bytes from TPM
salt = ca_pubkey, # org CA public key
info = generation_id # current generation
)
This design has specific properties:
- preboot_secret ties the key to this specific machine's TPM
- ca_pubkey ties it to this specific org (a machine can't accidentally unlock a /persist from a different org)
- generation_id ties it to a specific generation, enabling revocation: delete the generation_secret from the generation authority, and no machine can derive the key for that generation
Key Derivation Chains
FortrOS also uses HKDF in the key service for per-service secret derivation:
master_key (sealed to TPM or YubiKey HMAC)
|
+-- HKDF(master, info="key-service") -> key service master
|
+-- HKDF(ks_master, info="svc-A") -> service A encryption key
+-- HKDF(ks_master, info="svc-B") -> service B encryption key
+-- HKDF(ks_master, info="svc-C") -> service C encryption key
Each service gets its own key derived from the chain. Compromising service A's key reveals nothing about service B's key.
KDF vs Password Hashing
These are often confused but serve different purposes:
| KDF (HKDF) | Password Hash (bcrypt, Argon2) | |
|---|---|---|
| Input | High-entropy secret (crypto key, DH output) | Low-entropy password (human-chosen) |
| Speed | Fast (microseconds) | Deliberately slow (100ms+) |
| Memory | Minimal | Deliberately memory-hard |
| Purpose | Derive safe keys from good secrets | Resist brute-force on weak passwords |
Using HKDF on a password would be insecure -- an attacker could try billions of guesses per second. Using bcrypt on a DH shared secret would be pointlessly slow. Match the tool to the input entropy.
How FortrOS Uses It
- LUKS key derivation: HKDF-SHA256 from preboot_secret + ca_pubkey + generation_id. See 04 Disk Encryption.
- Key service: HKDF chain from YubiKey/TPM-sealed master to per-service keys. Each org service gets a unique derived key.
- Generation secrets: Per-generation keys derived by the generation authority. Revocation = destruction of the generation_secret (irreversible).
Alternatives
Direct key use: Skip derivation, use the secret directly. Only safe if the secret is uniformly random AND you only need one key from it. Rare in practice.
PBKDF2 / bcrypt / scrypt / Argon2: Password-based KDFs. Use these when the input is a human password. FortrOS uses these nowhere -- all input secrets are high-entropy cryptographic material.
X9.63 KDF / NIST SP 800-56C: Alternative KDFs specified by NIST. Used in some government contexts. HKDF is the most widely adopted in the open-source ecosystem.
Links
- RFC 5869 -- HKDF specification
- RFC 2104 -- HMAC (the building block of HKDF)
- Hugo Krawczyk: Cryptographic Extraction and Key Derivation -- The paper behind HKDF