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

1from hashlib import sha256 

2 

3class HKDF: 

4 

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) 

9 

10 

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) 

21 

22 

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] 

39 

40 

41 @classmethod 

42 def _hmac_sha256(cls, key, message): 

43 blocksize = 64 

44 

45 if len(key) > blocksize: 

46 key = sha256(key).digest() 

47 

48 # Pad key with zeros if it's shorter than blocksize 

49 if len(key) < blocksize: 

50 key = key + b'\x00' * (blocksize - len(key)) 

51 

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()