A Python port of the Invisible Internet Project (I2P)
at main 78 lines 2.4 kB view raw
1"""HKDF — HKDF-SHA256 key derivation. 2 3Ported from net.i2p.crypto.HKDF. 4Implements RFC 5869 with HMAC-SHA256. 5One or two 32-byte outputs, with or without info. 6""" 7 8import hmac as _hmac 9import hashlib 10 11 12class HKDF: 13 """HKDF-SHA256 key derivation. Thread-safe, no state.""" 14 15 def __init__(self) -> None: 16 pass 17 18 def calculate(self, key: bytes, data: bytes, 19 info: str = "", 20 out: bytearray | None = None, 21 out2: bytearray | None = None, 22 off2: int = 0) -> bytes: 23 """HKDF-SHA256 extract-then-expand. 24 25 Args: 26 key: first 32 bytes used as the salt for extract 27 data: input keying material 28 info: optional context string (ASCII) 29 out: if provided, 32-byte output buffer (output 1) 30 out2: if provided, 32-byte output buffer (output 2) 31 off2: offset into out2 32 33 Returns: 34 First 32-byte output (T1). If out2 is provided, also writes T2. 35 """ 36 k = key[:32] 37 38 # Extract: PRK = HMAC-SHA256(salt=key, data) 39 prk = _hmac.new(k, data, hashlib.sha256).digest() 40 41 # Expand T1: HMAC-SHA256(PRK, info || 0x01) 42 info_bytes = info.encode("ascii") if info else b"" 43 t1 = _hmac.new(prk, info_bytes + b"\x01", hashlib.sha256).digest() 44 45 if out is not None: 46 out[0:32] = t1 47 48 if out2 is not None: 49 # Expand T2: HMAC-SHA256(PRK, T1 || info || 0x02) 50 t2 = _hmac.new(prk, t1 + info_bytes + b"\x02", hashlib.sha256).digest() 51 out2[off2 : off2 + 32] = t2 52 53 return t1 54 55 def extract_and_expand(self, salt: bytes, ikm: bytes, info: bytes, length: int) -> bytes: 56 """General-purpose HKDF-SHA256 per RFC 5869. 57 58 Args: 59 salt: salt bytes 60 ikm: input keying material 61 info: context/application-specific info 62 length: output length in bytes (max 255 * 32 = 8160) 63 64 Returns: 65 Derived key material of requested length. 66 """ 67 # Extract 68 prk = _hmac.new(salt, ikm, hashlib.sha256).digest() 69 70 # Expand 71 n = (length + 31) // 32 72 okm = b"" 73 t = b"" 74 for i in range(1, n + 1): 75 t = _hmac.new(prk, t + info + bytes([i]), hashlib.sha256).digest() 76 okm += t 77 78 return okm[:length]