Skip to main content

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, requires setuptools, 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 + hashlib only, 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; 8161 raises ValueError
  • AC6 — Streaming: two-step expand equivalence verified
  • AC7 — Handles empty info, empty IKM, empty salt (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-pure validates that length ≤ 8160 and 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 / cffi integration
  • 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)

Source distribution for hkdf-pure 0.1.0
File Size Uploaded
hkdf_pure-0.1.0.tar.gz 10.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for hkdf-pure 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page