Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py: 85%
55 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-14 06:36 +0000
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-14 06:36 +0000
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
6import typing
8from cryptography import utils
9from cryptography.exceptions import AlreadyFinalized, InvalidKey
10from cryptography.hazmat.primitives import constant_time, hashes, hmac
11from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
14class HKDF(KeyDerivationFunction):
15 def __init__(
16 self,
17 algorithm: hashes.HashAlgorithm,
18 length: int,
19 salt: typing.Optional[bytes],
20 info: typing.Optional[bytes],
21 backend: typing.Any = None,
22 ):
23 self._algorithm = algorithm
25 if salt is None:
26 salt = b"\x00" * self._algorithm.digest_size
27 else:
28 utils._check_bytes("salt", salt)
30 self._salt = salt
32 self._hkdf_expand = HKDFExpand(self._algorithm, length, info)
34 def _extract(self, key_material: bytes) -> bytes:
35 h = hmac.HMAC(self._salt, self._algorithm)
36 h.update(key_material)
37 return h.finalize()
39 def derive(self, key_material: bytes) -> bytes:
40 utils._check_byteslike("key_material", key_material)
41 return self._hkdf_expand.derive(self._extract(key_material))
43 def verify(self, key_material: bytes, expected_key: bytes) -> None:
44 if not constant_time.bytes_eq(self.derive(key_material), expected_key):
45 raise InvalidKey
48class HKDFExpand(KeyDerivationFunction):
49 def __init__(
50 self,
51 algorithm: hashes.HashAlgorithm,
52 length: int,
53 info: typing.Optional[bytes],
54 backend: typing.Any = None,
55 ):
56 self._algorithm = algorithm
58 max_length = 255 * algorithm.digest_size
60 if length > max_length:
61 raise ValueError(
62 f"Cannot derive keys larger than {max_length} octets."
63 )
65 self._length = length
67 if info is None:
68 info = b""
69 else:
70 utils._check_bytes("info", info)
72 self._info = info
74 self._used = False
76 def _expand(self, key_material: bytes) -> bytes:
77 output = [b""]
78 counter = 1
80 while self._algorithm.digest_size * (len(output) - 1) < self._length:
81 h = hmac.HMAC(key_material, self._algorithm)
82 h.update(output[-1])
83 h.update(self._info)
84 h.update(bytes([counter]))
85 output.append(h.finalize())
86 counter += 1
88 return b"".join(output)[: self._length]
90 def derive(self, key_material: bytes) -> bytes:
91 utils._check_byteslike("key_material", key_material)
92 if self._used:
93 raise AlreadyFinalized
95 self._used = True
96 return self._expand(key_material)
98 def verify(self, key_material: bytes, expected_key: bytes) -> None:
99 if not constant_time.bytes_eq(self.derive(key_material), expected_key):
100 raise InvalidKey