Skip to content

Commit ec2ca6c

Browse files
committed
implement derive_into for kbkdfhmac
1 parent 870bf35 commit ec2ca6c

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Changelog
5555
:class:`~cryptography.hazmat.primitives.kdf.concatkdf.ConcatKDFHMAC`,
5656
:class:`~cryptography.hazmat.primitives.kdf.argon2.Argon2id`,
5757
:class:`~cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC`,
58+
:class:`~cryptography.hazmat.primitives.kdf.kbkdf.KBKDFHMAC`,
5859
:class:`~cryptography.hazmat.primitives.kdf.scrypt.Scrypt`, and
5960
:class:`~cryptography.hazmat.primitives.kdf.x963kdf.X963KDF` to allow
6061
deriving keys directly into pre-allocated buffers.

docs/hazmat/primitives/key-derivation-functions.rst

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,13 +1090,40 @@ KBKDF
10901090
:raises TypeError: This exception is raised if ``key_material`` is
10911091
not ``bytes``.
10921092
:raises cryptography.exceptions.AlreadyFinalized: This is raised when
1093-
:meth:`derive` or
1093+
:meth:`derive`,
1094+
:meth:`derive_into`, or
10941095
:meth:`verify` is
10951096
called more than
10961097
once.
10971098

10981099
Derives a new key from the input key material.
10991100

1101+
.. method:: derive_into(key_material, buffer)
1102+
1103+
.. versionadded:: 47.0.0
1104+
1105+
:param key_material: The input key material.
1106+
:type key_material: :term:`bytes-like`
1107+
:param buffer: A writable buffer to write the derived key into. The
1108+
buffer must be equal to the length supplied in the
1109+
constructor.
1110+
:type buffer: :term:`bytes-like`
1111+
:return int: the number of bytes written to the buffer.
1112+
:raises ValueError: This exception is raised if the buffer length does
1113+
not match the specified ``length``.
1114+
:raises TypeError: This exception is raised if ``key_material`` or
1115+
``buffer`` is not ``bytes``.
1116+
:raises cryptography.exceptions.AlreadyFinalized: This is raised when
1117+
:meth:`derive`,
1118+
:meth:`derive_into`, or
1119+
:meth:`verify` is
1120+
called more than
1121+
once.
1122+
1123+
Derives a new key from the input key material and writes it into
1124+
the provided buffer. This is useful when you want to avoid allocating
1125+
new memory for the derived key.
1126+
11001127
.. method:: verify(key_material, expected_key)
11011128

11021129
:param bytes key_material: The input key material. This is the same as
@@ -1108,7 +1135,8 @@ KBKDF
11081135
derived key does not match
11091136
the expected key.
11101137
:raises cryptography.exceptions.AlreadyFinalized: This is raised when
1111-
:meth:`derive` or
1138+
:meth:`derive`,
1139+
:meth:`derive_into`, or
11121140
:meth:`verify` is
11131141
called more than
11141142
once.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,5 @@ class KBKDFHMAC:
181181
break_location: int | None = None,
182182
) -> None: ...
183183
def derive(self, key_material: Buffer) -> bytes: ...
184+
def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ...
184185
def verify(self, key_material: bytes, expected_key: bytes) -> None: ...

src/rust/src/backend/kdf.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,6 +1971,15 @@ impl KbkdfHmac {
19711971
})?)
19721972
}
19731973

1974+
fn derive_into(
1975+
&mut self,
1976+
py: pyo3::Python<'_>,
1977+
key_material: CffiBuf<'_>,
1978+
mut buf: CffiMutBuf<'_>,
1979+
) -> CryptographyResult<usize> {
1980+
self.derive_into_buffer(py, key_material.as_bytes(), buf.as_mut_bytes())
1981+
}
1982+
19741983
fn verify(
19751984
&mut self,
19761985
py: pyo3::Python<'_>,

tests/hazmat/primitives/test_kbkdf.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,74 @@ def test_already_finalized(self, backend):
113113
with pytest.raises(AlreadyFinalized):
114114
kdf.verify(b"material", key)
115115

116+
def test_derive_into(self, backend):
117+
kdf = KBKDFHMAC(
118+
hashes.SHA256(),
119+
Mode.CounterMode,
120+
32,
121+
4,
122+
4,
123+
CounterLocation.BeforeFixed,
124+
b"label",
125+
b"context",
126+
None,
127+
backend=backend,
128+
)
129+
buf = bytearray(32)
130+
n = kdf.derive_into(b"material", buf)
131+
assert n == 32
132+
# Verify the output matches what derive would produce
133+
kdf2 = KBKDFHMAC(
134+
hashes.SHA256(),
135+
Mode.CounterMode,
136+
32,
137+
4,
138+
4,
139+
CounterLocation.BeforeFixed,
140+
b"label",
141+
b"context",
142+
None,
143+
backend=backend,
144+
)
145+
expected = kdf2.derive(b"material")
146+
assert buf == expected
147+
148+
@pytest.mark.parametrize(("buflen", "outlen"), [(31, 32), (33, 32)])
149+
def test_derive_into_buffer_incorrect_size(self, buflen, outlen, backend):
150+
kdf = KBKDFHMAC(
151+
hashes.SHA256(),
152+
Mode.CounterMode,
153+
outlen,
154+
4,
155+
4,
156+
CounterLocation.BeforeFixed,
157+
b"label",
158+
b"context",
159+
None,
160+
backend=backend,
161+
)
162+
buf = bytearray(buflen)
163+
with pytest.raises(ValueError, match="buffer must be"):
164+
kdf.derive_into(b"material", buf)
165+
166+
def test_derive_into_already_finalized(self, backend):
167+
kdf = KBKDFHMAC(
168+
hashes.SHA256(),
169+
Mode.CounterMode,
170+
32,
171+
4,
172+
4,
173+
CounterLocation.BeforeFixed,
174+
b"label",
175+
b"context",
176+
None,
177+
backend=backend,
178+
)
179+
buf = bytearray(32)
180+
kdf.derive_into(b"material", buf)
181+
with pytest.raises(AlreadyFinalized):
182+
kdf.derive_into(b"material2", buf)
183+
116184
def test_key_length(self, backend):
117185
error = OverflowError if sys.maxsize <= 2**31 else ValueError
118186
with pytest.raises(error):

0 commit comments

Comments
 (0)