hkdf-pure
Zero-dependency HKDF-SHA256 (RFC 5869) — pure stdlib, <10 KB
"I need HKDF for my TLS 1.3 client / Noise Protocol implementation — without dragging in
cffi+ OpenSSL."
hkdf-pure is a single-file, zero-dependency HKDF-SHA256 implementation built entirely on Python's stdlib hmac + hashlib. No C extensions. No cryptography. No cffi. Safe for MicroPython, Pyodide, AWS Lambda, and any serverless environment.
Quick Start
pip install -e .
from hkdf_pure import hkdf_sha256, hkdf_extract, hkdf_expand, hkdf_sha256_hex
# Full HKDF-SHA256 in one call
ikm = bytes.fromhex("0b" * 22)
salt = bytes.fromhex("000102030405060708090a0b0c")
info = bytes.fromhex("f0f1f2f3f4f5f6f7f8f9")
okm = hkdf_sha256(ikm, salt, info, 42)
print(okm.hex()) # 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865
# Uppercase hex output (convenience API)
hex_str = hkdf_sha256_hex(ikm, salt, info, 42)
print(hex_str) # CB95D056D6BA6F084DF0A03A3317BCCA7F83773204B76F527F4F06736168A52BBCD88869A3A4E7972DCD
# Standalone extract and expand (two-phase per RFC 5869)
prk = hkdf_extract(salt, ikm)
okm = hkdf_expand(prk, info, length=32)
⚡ Performance
Pure-Python HKDF is intended for use in environments that cannot install C extensions. For comparison, cryptography (which uses C/OpenSSL) is ~10-50× faster for bulk operations. hkdf-pure is a reference implementation optimized for portability, not throughput.
# Benchmarks: generate 32-byte OKM (single expand block)
# Pure-python implementation — for serverless/bootstrap use cases
Install and run locally:
python3 benchmarks/run_benchmark.py
Why hkdf-pure?
| hkdf-pure | cryptography | hkdf (PyPI) | |
|---|---|---|---|
| Dependencies | 0 | cffi + OpenSSL (~50 MB) | setuptools |
| Pure stdlib | Yes | No | No |
| Serverless-ready | Yes | No | Maybe |
| MicroPython | Yes | No | No |
| LOC (core) | ~45 | — | — |
cryptography— full-featured crypto library, but requires C FFI + OpenSSL. Too heavy for Lambda/MicroPython.hkdf(PyPI) — unmaintained, requiressetuptools, not a proper installable package.hkdf-pure— zero deps, single__init__.py, installs in milliseconds, works anywhere Python runs.
Key Features
- HKDF-SHA256 — RFC 5869 compliant, extract + expand phases
- Pure stdlib —
hmac+hashlibonly, no C extensions - Standalone APIs —
hkdf_extract(),hkdf_expand()for two-phase use - Convenience API —
hkdf_sha256(),hkdf_sha256_hex()for one-liners - Streaming-ready —
hkdf_expand()can be called incrementally - 100+ tests — RFC 5869 test vectors, edge cases, type safety
API Reference
hkdf_extract(salt: bytes, ikm: bytes) -> bytes
HKDF-Extract (RFC 5869 §2.2). Derives a pseudorandom key (PRK) from input keying material and salt. If salt is empty, uses a zero-filled 32-byte string.
prk = hkdf_extract(salt, ikm) # always returns 32 bytes
hkdf_expand(prk: bytes, info: bytes, length: int) -> bytes
HKDF-Expand (RFC 5869 §2.3). Derives length bytes of output key material from a PRK. length must be ≤ 8160 (255 × 32). Raises ValueError if exceeded.
okm = hkdf_expand(prk, info, length=42)
hkdf_sha256(ikm: bytes, salt: bytes, info: bytes, length: int) -> bytes
Full HKDF-SHA256: extract then expand in one call.
okm = hkdf_sha256(ikm, salt, info, length=32)
hkdf_sha256_hex(ikm: bytes, salt: bytes, info: bytes, length: int) -> str
Same as hkdf_sha256() but returns an uppercase hex string.
hex_str = hkdf_sha256_hex(ikm, salt, info, length=16) # "1A2B3C..."
RFC 5869 Test Vectors
Test Vector 1 — Full HKDF
| Field | Value |
|---|---|
| IKM | 0b0b0b... (22 bytes) |
| salt | 000102030405060708090a0b0c (13 bytes) |
| info | f0f1f2f3f4f5f6f7f8f9 (10 bytes) |
| L | 42 bytes |
| PRK (extract) | 077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5 |
| OKM (expand) | 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865 |
Test Vector 2 — Empty Salt
| Field | Value |
|---|---|
| IKM | 0b0b... (11 bytes) |
| salt | (empty, treated as zero-filled SHA-256 string) |
| info | f0f1f2f3f4f5f6f7f8f9 (10 bytes) |
| L | 42 bytes |
| PRK (extract) | 8917c8f10cb7a97cafae49f35ba02a06cde0b45ed0b7bb0c03d47d22e3f0470b |
| OKM (expand) | bfec4bb1674dea26f2a7528c7bb10079142fd4212f19b8e6525aa9c22689853467b304b55e927756e056 |
Acceptance Criteria Checklist
- AC1 —
hkdf_sha256(bytes.fromhex('0b'*22), ...)produces exact RFC 5869 A.2 TV1 OKM - AC2 —
hkdf_extract(...)returns correct PRK for TV1 - AC3 —
hkdf_extract(b'', bytes.fromhex('0b'*11))returns correct PRK for TV2 - AC4 —
hkdf_sha256_hex(...)returns uppercase hex string - AC5 —
hkdf_expand(prk, b'', 8160)succeeds;8161raisesValueError - AC6 — Streaming: two-step expand equivalence verified
- AC7 — Handles empty
info, emptyIKM, emptysalt(zero-filled per RFC 5869 §2.2) - AC8 — Pure Python stdlib only (
hmac,hashlib), zero pip dependencies - AC9 — Core LOC ≤ 150 (actual: ~45 non-comment lines)
- AC10 — 102 unit tests covering RFC vectors, ValueError limits, hex output, streaming, empty inputs, determinism, type errors
Limitations
- Format validation only —
hkdf-purevalidates thatlength ≤ 8160and that inputs are bytes. It does not enforce cryptographic key-length sanity for specific protocols (e.g., TLS 1.3 requires specific derived key lengths). You are responsible for using the correct derived key lengths for your protocol. - HKDF-SHA256 only — This package implements HKDF with SHA-256. HKDF-SHA384 and HKDF-SHA512 are not implemented.
- Not a TLS library — This is a key-derivation primitive. It does not handle session management, protocol negotiation, or any other TLS concern.
Non-Goals
- HKDF-SHA384 / HKDF-SHA512
- PBKDF2 / Argon2 / scrypt
- CLI / argparse interface
cryptography/cffiintegration- Authenticated encryption (AEAD)
License
MIT — see LICENSE.
Metadata
Release files for hkdf-pure 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hkdf_pure-0.1.0.tar.gz | 10.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hkdf_pure-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.2 kB
Release files / hkdf_pure-0.1.0.tar.gz
| Download URL | hkdf_pure-0.1.0.tar.gz |
|---|---|
| Size | 10.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e56673f2d23715364e234fac76262f983de4a036c754b7d2dbba630f6712aba
|
|
BLAKE2b-256 checksum How to use checksums |
a84546895c99c71fbe723fc77feac05f21e867cbd9fffc9046d91c5c4f5eab18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / hkdf_pure-0.1.0-py3-none-any.whl
| Download URL | hkdf_pure-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6c64643c2bacacefa30519da56059bcdc35dd8a793c7ce6d074331812160fa03
|
|
BLAKE2b-256 checksum How to use checksums |
e9840f988240c00f3e38bb8f6192478da664e719af34d505d22e51da2241597b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|