Skip to content

Commit 8f0052d

Browse files
committed
migrate concatkdf{hash,hmac} to rust
1 parent 6ac1384 commit 8f0052d

File tree

4 files changed

+278
-118
lines changed

4 files changed

+278
-118
lines changed

src/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,26 @@ class X963KDF:
9191
) -> None: ...
9292
def derive(self, key_material: Buffer) -> bytes: ...
9393
def verify(self, key_material: bytes, expected_key: bytes) -> None: ...
94+
95+
class ConcatKDFHash:
96+
def __init__(
97+
self,
98+
algorithm: HashAlgorithm,
99+
length: int,
100+
otherinfo: bytes | None,
101+
backend: typing.Any = None,
102+
) -> None: ...
103+
def derive(self, key_material: Buffer) -> bytes: ...
104+
def verify(self, key_material: bytes, expected_key: bytes) -> None: ...
105+
106+
class ConcatKDFHMAC:
107+
def __init__(
108+
self,
109+
algorithm: HashAlgorithm,
110+
length: int,
111+
salt: bytes | None,
112+
otherinfo: bytes | None,
113+
backend: typing.Any = None,
114+
) -> None: ...
115+
def derive(self, key_material: Buffer) -> bytes: ...
116+
def verify(self, key_material: bytes, expected_key: bytes) -> None: ...

src/cryptography/hazmat/primitives/kdf/concatkdf.py

Lines changed: 6 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,122 +4,13 @@
44

55
from __future__ import annotations
66

7-
import typing
8-
from collections.abc import Callable
9-
10-
from cryptography import utils
11-
from cryptography.exceptions import AlreadyFinalized, InvalidKey
12-
from cryptography.hazmat.primitives import constant_time, hashes, hmac
7+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
138
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
149

10+
ConcatKDFHash = rust_openssl.kdf.ConcatKDFHash
11+
ConcatKDFHMAC = rust_openssl.kdf.ConcatKDFHMAC
1512

16-
def _int_to_u32be(n: int) -> bytes:
17-
return n.to_bytes(length=4, byteorder="big")
18-
19-
20-
def _common_args_checks(
21-
algorithm: hashes.HashAlgorithm,
22-
length: int,
23-
otherinfo: bytes | None,
24-
) -> None:
25-
max_length = algorithm.digest_size * (2**32 - 1)
26-
if length > max_length:
27-
raise ValueError(f"Cannot derive keys larger than {max_length} bits.")
28-
if otherinfo is not None:
29-
utils._check_bytes("otherinfo", otherinfo)
30-
31-
32-
def _concatkdf_derive(
33-
key_material: utils.Buffer,
34-
length: int,
35-
auxfn: Callable[[], hashes.HashContext],
36-
otherinfo: bytes,
37-
) -> bytes:
38-
utils._check_byteslike("key_material", key_material)
39-
output = [b""]
40-
outlen = 0
41-
counter = 1
42-
43-
while length > outlen:
44-
h = auxfn()
45-
h.update(_int_to_u32be(counter))
46-
h.update(key_material)
47-
h.update(otherinfo)
48-
output.append(h.finalize())
49-
outlen += len(output[-1])
50-
counter += 1
51-
52-
return b"".join(output)[:length]
53-
54-
55-
class ConcatKDFHash(KeyDerivationFunction):
56-
def __init__(
57-
self,
58-
algorithm: hashes.HashAlgorithm,
59-
length: int,
60-
otherinfo: bytes | None,
61-
backend: typing.Any = None,
62-
):
63-
_common_args_checks(algorithm, length, otherinfo)
64-
self._algorithm = algorithm
65-
self._length = length
66-
self._otherinfo: bytes = otherinfo if otherinfo is not None else b""
67-
68-
self._used = False
69-
70-
def _hash(self) -> hashes.Hash:
71-
return hashes.Hash(self._algorithm)
72-
73-
def derive(self, key_material: utils.Buffer) -> bytes:
74-
if self._used:
75-
raise AlreadyFinalized
76-
self._used = True
77-
return _concatkdf_derive(
78-
key_material, self._length, self._hash, self._otherinfo
79-
)
80-
81-
def verify(self, key_material: bytes, expected_key: bytes) -> None:
82-
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
83-
raise InvalidKey
84-
85-
86-
class ConcatKDFHMAC(KeyDerivationFunction):
87-
def __init__(
88-
self,
89-
algorithm: hashes.HashAlgorithm,
90-
length: int,
91-
salt: bytes | None,
92-
otherinfo: bytes | None,
93-
backend: typing.Any = None,
94-
):
95-
_common_args_checks(algorithm, length, otherinfo)
96-
self._algorithm = algorithm
97-
self._length = length
98-
self._otherinfo: bytes = otherinfo if otherinfo is not None else b""
99-
100-
if algorithm.block_size is None:
101-
raise TypeError(f"{algorithm.name} is unsupported for ConcatKDF")
102-
103-
if salt is None:
104-
salt = b"\x00" * algorithm.block_size
105-
else:
106-
utils._check_bytes("salt", salt)
107-
108-
self._salt = salt
109-
110-
self._used = False
111-
112-
def _hmac(self) -> hmac.HMAC:
113-
return hmac.HMAC(self._salt, self._algorithm)
114-
115-
def derive(self, key_material: utils.Buffer) -> bytes:
116-
if self._used:
117-
raise AlreadyFinalized
118-
self._used = True
119-
return _concatkdf_derive(
120-
key_material, self._length, self._hmac, self._otherinfo
121-
)
13+
KeyDerivationFunction.register(ConcatKDFHash)
14+
KeyDerivationFunction.register(ConcatKDFHMAC)
12215

123-
def verify(self, key_material: bytes, expected_key: bytes) -> None:
124-
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
125-
raise InvalidKey
16+
__all__ = ["ConcatKDFHMAC", "ConcatKDFHash"]

0 commit comments

Comments
 (0)