GUIDE · PREVIEW
GUIDE / CON.23
source: docs/guide/concepts/Key Derivation.md
Concepts

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