All posts
Security10 min read

KEK and FEK explained: why per-file encryption matters

KEK (Key Encryption Key) and FEK (File Encryption Key) form the two-layer key hierarchy used by serious encrypted apps — Signal, BitLocker, Apple's File Protection, Filarr. Here's what each one does, why one global key is dangerous, and what a correctly designed encryption layout looks like.

MB

Mathis Belouar-Pruvot

Quick Answer. KEK (Key Encryption Key) and FEK (File Encryption Key) form a two-layer key hierarchy used by serious encrypted apps. Each file gets its own random FEK; the FEK is then encrypted ("wrapped") by a single KEK derived from your password. This pattern, called envelope encryption, limits the blast radius of any single key compromise to one file. It's used by Signal, BitLocker, AWS KMS, Apple's File Protection, and Filarr.

When an encrypted notes app says "your data is encrypted with AES-256," there's a hidden architectural choice that matters more than the cipher: how many keys are there, and how are they organized?

The naive design uses one key for everything — your password, stretched into a master key, encrypts every file. The correct design uses two layers: a unique key per file, wrapped by a master key. This is called envelope encryption, and the two layers have specific names: KEK (Key Encryption Key) and FEK (File Encryption Key).

This article explains why the two-layer design exists, how it works step by step, what attacks it defends against, and how to recognize a correctly designed encrypted app.

What are KEK and FEK?

The terms come from key management systems and cloud KMS literature. The definitions:

  • FEK (File Encryption Key): a random symmetric key (typically 256 bits) that encrypts a single file. Each file in the system has its own unique FEK.
  • KEK (Key Encryption Key): a master key, typically derived from the user's password, that encrypts FEKs. The KEK itself never directly encrypts file content.

The arrangement is hierarchical: one KEK, many FEKs.

                    User password
                         │
                         ▼
                  KDF (PBKDF2 / Argon2id)
                         │
                         ▼
                  ╔══════════╗
                  ║   KEK    ║   ← lives in memory only
                  ╚══════════╝
                         │
                         │ encrypts each FEK
            ┌────────────┼────────────┐
            ▼            ▼            ▼
        ┌──────┐    ┌──────┐    ┌──────┐
        │ FEK1 │    │ FEK2 │    │ FEK3 │
        └──────┘    └──────┘    └──────┘
            │            │            │
            ▼            ▼            ▼
        ┌──────┐    ┌──────┐    ┌──────┐
        │file 1│    │file 2│    │file 3│
        │encrypt│   │encrypt│   │encrypt│
        └──────┘    └──────┘    └──────┘

When you save a file:

  1. Generate a fresh random FEK
  2. Encrypt the file with its FEK using AES-256-GCM
  3. Encrypt the FEK with the KEK using AES-256-GCM
  4. Store the encrypted file alongside the encrypted FEK on disk (or upload to cloud)

When you read a file:

  1. Use your password to re-derive the KEK
  2. Decrypt the FEK with the KEK
  3. Decrypt the file with the FEK

The KEK lives only in process memory and is wiped when you lock the app or close it. The FEKs live encrypted on disk. The original password is never stored anywhere.

Why not use one global key for everything?

The single-key design seems simpler. You stretch your password into a master key, encrypt every file with that master key, done. Why add a layer?

Three concrete problems with the single-key design.

1. Blast radius. If one ciphertext leaks the key (through a bug, a side-channel, or a nonce reuse), every file encrypted with that key is exposed. With per-file FEKs, leaking one file's encryption key only exposes that one file.

2. Key rotation is impossible. With a single global key, rotating the key (because of a suspected compromise) means re-encrypting every file. That's slow and risky. With KEK/FEK, you can rotate the KEK and re-wrap the FEKs without ever decrypting and re-encrypting the file content.

3. Selective sharing is impossible. If you want to share one file with another user, you'd have to share the global master key — which gives them access to all your files, forever. With per-file FEKs, you share a single FEK encrypted with the recipient's public key. They get exactly one file.

These problems aren't theoretical. The history of cryptographic disasters is largely a history of "one key for everything" designs failing badly.

What does envelope encryption mean?

"Envelope encryption" is the name of the KEK/FEK pattern in the cloud security industry. The metaphor: each file is sealed in an envelope with its own data key (FEK), and that envelope is then placed in another envelope sealed with a key encryption key (KEK).

AWS KMS, Google Cloud KMS, Azure Key Vault, and HashiCorp Vault all implement envelope encryption. The cloud-managed KEK never leaves a hardware security module; only the FEKs (encrypted under that KEK) are sent back to the application.

Filarr uses the same pattern locally: the user's password derives the KEK, the KEK lives only in memory, and the FEKs are stored encrypted alongside the files.

How are the KEK and FEK actually generated?

The two halves use different generation methods because they have different requirements.

FEK generation: cryptographically secure random. A FEK is a 256-bit random value pulled from the operating system's secure random number generator (crypto.randomBytes in Node, getRandomValues in browsers, /dev/urandom on Linux, BCryptGenRandom on Windows). Each new file gets a fresh FEK. The FEK is never derived from anything; it's pure randomness.

KEK generation: key derivation function. The KEK is derived from the user's password through a slow function designed to resist brute-force attacks. Two functions are commonly used:

  • PBKDF2-SHA-512 with 600,000+ iterations, salted per user. The minimum acceptable in 2026 (OWASP recommendation).
  • Argon2id with at least 64 MB of memory cost, 3 iterations, 4 parallelism lanes. The current best practice (RFC 9106).

Argon2id is preferred for new designs because it's memory-hard, which makes GPU and ASIC brute-forcing dramatically more expensive than PBKDF2. Many encrypted apps still use PBKDF2 for compatibility with existing libraries, sometimes alongside Argon2id for defense in depth.

What attacks does the KEK/FEK design defend against?

A good way to evaluate any cryptographic architecture is to walk through specific attack scenarios.

Attack 1: One file's key is exposed. A bug in the application briefly logs a FEK to disk. An attacker with disk access reads the log. What they get: the contents of one file. What they don't get: the master key, any other file. Single-key design fails this test catastrophically.

Attack 2: A user shares a file. With per-file FEKs, sharing means encrypting one FEK to the recipient's public key. The recipient gets access to one file, nothing more. With a single global key, you can't share anything without sharing everything.

Attack 3: User changes their password. With KEK/FEK, you re-derive a new KEK from the new password and re-wrap each FEK with the new KEK. No file content is touched. The operation is fast (milliseconds) and atomic. With a single key, you must re-encrypt every file from scratch — a destructive, slow, error-prone process.

Attack 4: Cloud server is compromised. The attacker reads all the encrypted blobs on disk. What they get: ciphertext + encrypted FEKs. What they don't get: the KEK (it's only in your device's memory) — so they cannot decrypt the FEKs, and therefore cannot decrypt anything.

Attack 5: Forensic recovery of a deleted file. With per-file FEKs, you can "shred" a file by deleting just its FEK. The encrypted file content becomes permanently unreadable, even if the bytes remain on disk. With a single key, deleting just one file's metadata still leaves it decryptable — secure deletion requires overwriting the bytes.

In every scenario, the two-layer design behaves better than the single-key design.

Are there other names for this pattern?

Yes. The same architectural pattern shows up in many places under different names:

  • Envelope encryption — AWS, Google Cloud, Azure
  • Key wrapping — NIST terminology
  • DEK + KEK — "Data Encryption Key" instead of "File Encryption Key"
  • Master key + file keys — Apple's File Protection in iOS / macOS
  • Vault key + record keys — 1Password
  • VMK + FVEK — Microsoft BitLocker (Volume Master Key + Full Volume Encryption Key)

These are all the same pattern: a master key that encrypts data keys, where the data keys actually touch the data. The naming differs but the structure is constant.

How can I tell if a notes app uses per-file encryption?

When evaluating an encrypted notes or file storage app, look for these signals:

  1. Architecture documentation that mentions key wrapping, envelope encryption, or per-file keys. A serious app explains its encryption layout publicly. If the docs only say "AES-256," that's a marketing claim, not an architecture.

  2. Independent audit reports. Auditors will explicitly note whether per-file keys are used. The presence of "KEK and FEK" in an audit report is a strong positive signal.

  3. Selective sharing capability. If the app supports sharing individual files without giving the recipient access to your whole vault, it's almost certainly using per-file keys. Single-key designs cannot do selective sharing.

  4. Fast password change. If changing your master password takes seconds and doesn't require re-encrypting your whole library, the design is using key wrapping (KEK is updated; FEKs and files are untouched).

  5. Open source. With public source code, you can verify the architecture directly. Search for terms like wrapKey, wrappedFek, unwrapKey, kekId, keyEncryptionKey.

How does Filarr implement KEK/FEK?

Filarr's architecture is textbook envelope encryption with current best-practice parameters.

  • KEK derivation. Your password is run through Argon2id (memory cost 64 MiB, 3 iterations, parallelism 4) and PBKDF2-SHA-512 (600,000 iterations) to produce a 256-bit KEK. The KEK lives in process memory only; it is never written to disk in plaintext.
  • FEK generation. Each file in your workspace gets a 256-bit random FEK from the OS's secure RNG. FEKs are never derived from anything else and never reused across files.
  • File encryption. The file is encrypted with its FEK using AES-256-GCM with a fresh random nonce per encryption. See what is AES-256-GCM for why this mode matters.
  • FEK wrapping. The FEK is encrypted with the KEK using AES-256-GCM and stored alongside the encrypted file (locally) or uploaded with the encrypted blob (cloud sync).
  • Multi-profile isolation. Each Filarr profile has its own KEK derived from a different password. Profile A's KEK cannot decrypt Profile B's FEKs — even though both belong to the same user.
  • Cloud upload. When cloud sync is enabled, Filarr uploads the encrypted file blob and the encrypted FEK. The server sees both; without your KEK (which never leaves your device), it can decrypt neither.

The result: a single key compromise (a logged FEK, a side-channel leak) exposes one file. The KEK never leaves your machine in plaintext. Password changes are instantaneous because they only re-wrap FEKs, not file content.

Read Filarr's full security architecture for the exact parameters, threat model, and audit reports.

Further reading

#kek#fek#key-encryption-key#file-encryption-key#encryption-architecture#envelope-encryption