Encryption Design
FlexFS Enterprise supports end-to-end encryption where all block data and sensitive metadata fields are encrypted on the mount client before leaving its memory. The encryption key is derived from a user-provided secret and never leaves the client.
Key derivation with Argon2id
Section titled “Key derivation with Argon2id”When encryption is first configured for a volume, the mount client derives cryptographic keys from the user-provided secret using Argon2id, a memory-hard key derivation function resistant to GPU and ASIC brute-force attacks.
Argon2id parameters
Section titled “Argon2id parameters”| Parameter | Value |
|---|---|
| Algorithm | Argon2id |
| Time cost (iterations) | 3 |
| Memory cost | 64 MiB (65,536 KiB) |
| Parallelism (threads) | 4 |
| Output length | 64 bytes |
| Salt length | 16 bytes (cryptographically random) |
The 64-byte derived output is split into two 32-byte halves:
- Bytes 0-31: Combined with the salt to form the secret ID (stored on the admin server for verification).
- Bytes 32-63: Used as the secret key for AES-256-GCM encryption (never stored anywhere).
Secret ID format
Section titled “Secret ID format”The secret ID is stored as a string in the format:
base64(salt):base64(hash)where salt is the 16-byte random salt and hash is the first 32 bytes of the Argon2id output. This format embeds the salt so it can be recovered on subsequent mounts.
First mount vs. subsequent mounts
Section titled “First mount vs. subsequent mounts”First mount (no secret ID registered for the volume):
- The user provides a secret (minimum 8 characters).
- A 16-byte cryptographically random salt is generated.
- Argon2id derives 64 bytes from the secret and salt.
- The secret ID (salt + first 32 bytes) is registered with the admin server.
- The secret key (last 32 bytes) is used for encryption.
Subsequent mounts (secret ID already registered):
- The user provides the same secret.
- The salt is extracted from the stored secret ID.
- Argon2id re-derives the same 64 bytes from the secret and stored salt.
- The derived secret ID is compared against the stored one. If they do not match, the mount fails with “incorrect volume secret.”
- The secret key (last 32 bytes) is used for encryption.
This design ensures that:
- The admin server can verify that the correct secret was provided (by comparing secret IDs).
- The admin server never sees the actual encryption key.
- The same secret always produces the same key for a given volume (deterministic derivation from stored salt).
Block encryption: AES-256-GCM
Section titled “Block encryption: AES-256-GCM”File data blocks are encrypted using AES-256-GCM (Galois/Counter Mode), an authenticated encryption algorithm that provides both confidentiality and integrity.
Encryption process
Section titled “Encryption process”For each block written:
- A 12-byte random nonce is generated using
crypto/rand. - The block plaintext is encrypted with AES-256-GCM using the secret key, the nonce, and additional authenticated data (AAD).
- The encrypted output is formatted as
nonce || ciphertext || GCM-tagand stored as a single object.
Additional authenticated data (AAD)
Section titled “Additional authenticated data (AAD)”Each block’s AAD binds the ciphertext to the block’s identity, preventing block-swapping attacks:
AAD = BigEndian(inode_number, 8 bytes) || BigEndian(block_index, 8 bytes)This means that even if an attacker could copy an encrypted block from one inode/index to another, decryption would fail because the AAD would not match.
Decryption process
Section titled “Decryption process”For each block read:
- The nonce is extracted from the first 12 bytes.
- The remaining bytes are the ciphertext + GCM tag.
- AES-256-GCM decryption verifies the tag against the AAD and produces the plaintext.
- If verification fails (tampered data, wrong key, or mismatched AAD), decryption returns an error.
Processing order
Section titled “Processing order”The block processing pipeline applies compression before encryption on writes, and decryption before decompression on reads:
| Direction | Step 1 | Step 2 |
|---|---|---|
| Write | Compress (LZ4/Snappy/zstd) | Encrypt (AES-256-GCM) |
| Read | Decrypt (AES-256-GCM) | Decompress (LZ4/Snappy/zstd) |
Compressing before encrypting is important because encrypted data has maximum entropy and is incompressible. This order ensures that compression remains effective.
Metadata encryption: AES-256-GCM
Section titled “Metadata encryption: AES-256-GCM”When encryption is enabled, the mount client also encrypts sensitive metadata fields before sending them to the metadata server. This prevents the metadata server from seeing plaintext file names, symlink targets, and extended attribute names and values.
What is encrypted
Section titled “What is encrypted”| Field | Encryption method |
|---|---|
| File names (in directory entries) | AES-256-GCM with deterministic nonce (SHAKE-256 hash of the name), base64-encoded |
| Symlink targets | AES-256-GCM with deterministic nonce, base64-encoded |
| Extended attribute names | AES-256-GCM with deterministic nonce, base64-encoded |
| Extended attribute values | AES-256-GCM with random nonce |
Deterministic vs. random nonces
Section titled “Deterministic vs. random nonces”File names and xattr names use a deterministic nonce derived from the SHAKE-256 hash of the plaintext. This means the same name always encrypts to the same ciphertext, which is necessary for the metadata server to perform directory lookups and xattr key matching without knowing the plaintext.
Extended attribute values use a random nonce because they do not need to be looked up by value.
Metadata cipher initialization
Section titled “Metadata cipher initialization”The mount client initializes a separate AES-256-GCM cipher for metadata encryption using the same 32-byte secret key used for block encryption. After the cipher is initialized, the raw key material is zeroed from memory.
Key lifecycle
Section titled “Key lifecycle”Important operational notes
Section titled “Important operational notes”- The secret is the only key. If the volume secret is lost, the encrypted data is unrecoverable. There is no key escrow or recovery mechanism.
- All mounts must use the same secret. Every mount client for an encrypted volume must provide the identical secret to derive the same encryption key.
- The secret never leaves the client. Neither the admin server, metadata server, proxy servers, nor object storage ever see the secret or the derived encryption key.
- Encryption is set at volume creation time. It cannot be enabled or disabled after the volume is created.
- Server-side encryption (SSE) is independent. S3 SSE can be used in addition to client-side encryption for defense in depth. With both enabled, blocks are encrypted by the client (AES-256-GCM) and then again by S3 (SSE-S3).