Coverage for tropicsquare / ports / micropython / hkdf.py: 0%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-27 21:24 +0000
1from hashlib import sha256
3class HKDF:
5 @classmethod
6 def derive(cls, salt, key_material, length = 32):
7 prk = cls._hkdf_extract(salt, key_material)
8 return cls._hkdf_expand(prk, b'', length)
11 @classmethod
12 def _hkdf_extract(cls, salt, ikm):
13 """
14 HKDF-Extract step.
15 If salt is empty, use a string of HashLen zeros.
16 """
17 hash_len = 32 # Hard coded for SHA-256
18 if salt is None or len(salt) == 0:
19 salt = b'\x00' * hash_len
20 return cls._hmac_sha256(salt, ikm)
23 @classmethod
24 def _hkdf_expand(cls, prk, info, length):
25 """
26 HKDF-Expand step.
27 'info' is optional context and application specific information (can be empty).
28 """
29 hash_len = 32 # Hard coded for SHA-256
30 n = (length + hash_len - 1) // hash_len
31 if n > 255:
32 raise ValueError("Cannot expand to more than 255 * hash length bytes")
33 t = b""
34 okm = b""
35 for i in range(1, n + 1):
36 t = cls._hmac_sha256(prk, t + info + bytes([i]))
37 okm += t
38 return okm[:length]
41 @classmethod
42 def _hmac_sha256(cls, key, message):
43 blocksize = 64
45 if len(key) > blocksize:
46 key = sha256(key).digest()
48 # Pad key with zeros if it's shorter than blocksize
49 if len(key) < blocksize:
50 key = key + b'\x00' * (blocksize - len(key))
52 # Create inner and outer padded keys
53 o_key_pad = bytes([b ^ 0x5c for b in key])
54 i_key_pad = bytes([b ^ 0x36 for b in key])
55 inner_hash = sha256(i_key_pad + message).digest()
56 return sha256(o_key_pad + inner_hash).digest()