A Python port of the Invisible Internet Project (I2P)
1"""HMAC generators — HMAC-SHA256 computation.
2
3Ported from net.i2p.crypto.HMACGenerator and HMAC256Generator.
4Wraps Python's hmac module.
5"""
6
7import hmac as _hmac
8import hashlib
9
10from i2p_crypto.hash_data import Hash
11
12
13class HMACGenerator:
14 """Abstract base for HMAC computation."""
15
16 def calculate(self, key: bytes, data: bytes, offset: int, length: int,
17 target: bytearray, target_offset: int) -> None:
18 raise NotImplementedError
19
20 def verify(self, key: bytes, data: bytes, offset: int, length: int,
21 orig_mac: bytes, orig_mac_offset: int, orig_mac_length: int) -> bool:
22 raise NotImplementedError
23
24
25class HMAC256Generator(HMACGenerator):
26 """HMAC-SHA256 generator. Thread-safe.
27
28 Ported from net.i2p.crypto.HMAC256Generator.
29 """
30
31 _instance: "HMAC256Generator | None" = None
32
33 def __init__(self) -> None:
34 super().__init__()
35
36 @classmethod
37 def get_instance(cls) -> "HMAC256Generator":
38 if cls._instance is None:
39 cls._instance = HMAC256Generator()
40 return cls._instance
41
42 def calculate(self, key: bytes, data: bytes, offset: int = 0, length: int = -1, # type: ignore[override]
43 target: bytearray | None = None, target_offset: int = 0) -> bytes:
44 """Calculate HMAC-SHA256.
45
46 If target is provided, writes 32 bytes into target at target_offset.
47 Always returns the 32-byte MAC.
48 """
49 if length < 0:
50 length = len(data) - offset
51 # Use first 32 bytes of key
52 k = key[:32]
53 mac = _hmac.new(k, data[offset : offset + length], hashlib.sha256).digest()
54 if target is not None:
55 target[target_offset : target_offset + 32] = mac
56 return mac
57
58 def verify(self, key: bytes, data: bytes, offset: int, length: int,
59 orig_mac: bytes, orig_mac_offset: int = 0,
60 orig_mac_length: int = 32) -> bool:
61 """Verify HMAC-SHA256 in constant time."""
62 calc = self.calculate(key, data, offset, length)
63 return _hmac.compare_digest(
64 calc[:orig_mac_length],
65 orig_mac[orig_mac_offset : orig_mac_offset + orig_mac_length],
66 )