Algorithm Documentation¶
adk-secure-sessions delegates all cryptographic operations to the pyca/cryptography library. No custom cryptographic primitives are implemented. This page documents the algorithms, parameters, and standards compliance for each registered encryption backend.
For how encrypted data is framed and identified, see the Envelope Protocol.
Algorithm Overview¶
The current release ships with two backends:
- Fernet (backend ID
0x01) — AES-128-CBC + HMAC-SHA256 with two-phase key derivation (PBKDF2 + HKDF) for passphrase-based keys. - AES-256-GCM (backend ID
0x02) — AES-256-GCM authenticated encryption with 96-bit random nonces.
Fernet¶
Fernet provides authenticated encryption by combining AES-128-CBC for confidentiality with HMAC-SHA256 for integrity. Key material can be a pre-generated Fernet key or an arbitrary passphrase derived via a two-phase scheme: PBKDF2-HMAC-SHA256 at init time, followed by HKDF-SHA256 per operation.
Fernet keys are 256 bits (32 bytes), composed of two 128-bit halves:
- Bytes 0 -- 15: HMAC-SHA256 signing key (message authentication)
- Bytes 16 -- 31: AES-128-CBC encryption key (confidentiality)
This explains why the algorithm is called "AES-128-CBC" despite the overall key being 32 bytes — the 32 bytes are split, with only the second half used for AES encryption.
NIST/FIPS Compliance Mapping¶
Every cryptographic component maps to a published NIST or FIPS standard:
| Component | Algorithm | Standard | Reference |
|---|---|---|---|
| Block cipher | AES-128-CBC | FIPS 197 (AES) + NIST SP 800-38A (CBC mode) | 128-bit key, CBC mode of operation |
| Message authentication | HMAC-SHA256 | FIPS 198-1 | Keyed-hash message authentication code |
| Key derivation (extract) | PBKDF2-HMAC-SHA256 | NIST SP 800-132 | Password-based key derivation |
| Key expansion (per-op) | HKDF-SHA256 | RFC 5869 / NIST SP 800-56C Rev. 2 | HMAC-based extract-and-expand KDF |
| Initialization vector | 128-bit random IV | NIST SP 800-38A | Per-message, generated by Fernet |
FIPS 140-2 Certification
This library uses NIST-approved algorithms via pyca/cryptography, which builds on OpenSSL. FIPS 140-2 certification applies to the underlying cryptographic module (OpenSSL), not to libraries built on top of it. Organizations requiring FIPS 140-2 compliance should verify their deployment's OpenSSL build is FIPS-validated.
Component Details¶
AES-128-CBC (Confidentiality)¶
Advanced Encryption Standard with 128-bit keys in Cipher Block Chaining mode. Each message is encrypted with a fresh 128-bit random IV (initialization vector), generated by the Fernet implementation. Plaintext is PKCS7-padded to 16-byte block boundaries before encryption.
HMAC-SHA256 (Integrity)¶
Hash-based Message Authentication Code using SHA-256. Applied over the Fernet token's version, timestamp, IV, and ciphertext to detect tampering. The signing key (first 128 bits of the Fernet key) is independent of the encryption key.
PBKDF2-HMAC-SHA256 (Key Derivation — Extract Phase)¶
Password-Based Key Derivation Function 2 using HMAC-SHA256 as the pseudorandom function. Used at backend construction time to stretch an arbitrary passphrase into a 32-byte master key.
Current parameters:
| Parameter | Value | Source |
|---|---|---|
| Hash function | SHA-256 | hashlib.pbkdf2_hmac("sha256", ...) |
| Iterations | 600,000 | _PBKDF2_ITERATIONS in backends/fernet.py |
| Salt | b"adk-secure-sessions-fernet-v1" | _PBKDF2_SALT in backends/fernet.py |
| Output length | 32 bytes (256 bits) | Master key for HKDF expansion |
The iteration count meets the OWASP 2023 Password Storage Cheat Sheet recommendation of 600,000 for PBKDF2-HMAC-SHA256.
A legacy iteration count of 480,000 (_PBKDF2_ITERATIONS_LEGACY) is retained for backward-compatible decryption of data encrypted prior to version 3.2.
HKDF-SHA256 (Key Expansion — Per-Operation)¶
HMAC-based Extract-and-Expand Key Derivation Function (RFC 5869) using SHA-256. Used per encrypt/decrypt operation to derive a unique Fernet key from the PBKDF2-derived master key and a fresh random salt.
Parameters:
| Parameter | Value | Source |
|---|---|---|
| Algorithm | SHA-256 | hashes.SHA256() |
| Salt | 16 random bytes | os.urandom(16) per operation |
| Info | b"adk-fernet-v2" | _HKDF_INFO in backends/fernet.py |
| Output length | 32 bytes (256 bits) | Base64url-encoded → Fernet key |
HKDF completes in microseconds, making per-operation key derivation practical without the latency of repeated PBKDF2 calls.
Key Resolution¶
FernetBackend accepts keys as str or bytes and resolves them as follows:
- Valid Fernet key — if the input is a valid base64url-encoded 32-byte key, it is used directly (passthrough). No PBKDF2 derivation, no salt marker, no HKDF expansion.
- Arbitrary passphrase — the input is stretched via PBKDF2-HMAC-SHA256 (600,000 iterations) into a master key at init time. Each encrypt call generates a fresh 16-byte random salt and expands the master key via HKDF-SHA256 into a unique per-operation Fernet key.
Fernet Ciphertext Format¶
The ciphertext portion of the envelope (bytes 2+) depends on the key mode:
Passphrase mode (salted, v3.2+)¶
- Byte 0:
0x01salt marker, distinguishes from legacy format. - Bytes 1--16: Random salt used for HKDF expansion.
- Bytes 17+: Standard Fernet token.
Direct-key mode / legacy passphrase mode (pre-3.2)¶
No marker byte. The entire blob is a standard Fernet token. Legacy passphrase data (encrypted with 480,000-iteration fixed-salt derivation) is decrypted transparently using a retained legacy Fernet instance.
Fernet Token Structure¶
A Fernet token contains: version byte (0x80), 8-byte big-endian timestamp, 16-byte random IV, AES-128-CBC ciphertext padded to 16-byte blocks, and a 32-byte HMAC-SHA256 authentication tag. See the Fernet Specification for the canonical format definition.
Async Implementation¶
All FernetBackend encrypt and decrypt operations are wrapped in asyncio.to_thread() because Fernet's underlying AES and HMAC computations are CPU-bound. This prevents blocking the async event loop in ADK agent pipelines.
Known Limitations¶
No FIPS 140-2 certification. The library uses NIST-approved algorithms but FIPS 140-2 certification belongs to the underlying cryptographic module (OpenSSL), not the application library. A FIPS deployment guide is planned for Phase 4.
Related¶
- Envelope Protocol — binary envelope format wrapping the ciphertext
- Roadmap — Phase 3 improvements (AES-256-GCM, per-key salt, iteration increase)
- FernetBackend API Reference — full API documentation