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.
4
5from __future__ import absolute_import, division, print_function
6
7import binascii
8
9import pytest
10
11from cryptography.exceptions import (
12    AlreadyFinalized, InvalidKey, _Reasons
13)
14from cryptography.hazmat.backends.interfaces import HMACBackend
15from cryptography.hazmat.backends.interfaces import HashBackend
16from cryptography.hazmat.primitives import hashes
17from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHMAC
18from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash
19
20from ...utils import raises_unsupported_algorithm
21
22
23@pytest.mark.requires_backend_interface(interface=HashBackend)
24class TestConcatKDFHash(object):
25    def test_length_limit(self, backend):
26        big_length = hashes.SHA256().digest_size * (2 ** 32 - 1) + 1
27
28        with pytest.raises(ValueError):
29            ConcatKDFHash(hashes.SHA256(), big_length, None, backend)
30
31    def test_already_finalized(self, backend):
32        ckdf = ConcatKDFHash(hashes.SHA256(), 16, None, backend)
33
34        ckdf.derive(b"\x01" * 16)
35
36        with pytest.raises(AlreadyFinalized):
37            ckdf.derive(b"\x02" * 16)
38
39    def test_derive(self, backend):
40        prk = binascii.unhexlify(
41            b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
42        )
43
44        okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55")
45
46        oinfo = binascii.unhexlify(
47            b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2"
48            b"46f72971f292badaa2fe4124612cba"
49        )
50
51        ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend)
52
53        assert ckdf.derive(prk) == okm
54
55    def test_buffer_protocol(self, backend):
56        prk = binascii.unhexlify(
57            b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
58        )
59
60        okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55")
61
62        oinfo = binascii.unhexlify(
63            b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2"
64            b"46f72971f292badaa2fe4124612cba"
65        )
66
67        ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend)
68
69        assert ckdf.derive(bytearray(prk)) == okm
70
71    def test_verify(self, backend):
72        prk = binascii.unhexlify(
73            b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
74        )
75
76        okm = binascii.unhexlify(b"1c3bc9e7c4547c5191c0d478cccaed55")
77
78        oinfo = binascii.unhexlify(
79            b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2"
80            b"46f72971f292badaa2fe4124612cba"
81        )
82
83        ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend)
84
85        assert ckdf.verify(prk, okm) is None
86
87    def test_invalid_verify(self, backend):
88        prk = binascii.unhexlify(
89            b"52169af5c485dcc2321eb8d26d5efa21fb9b93c98e38412ee2484cf14f0d0d23"
90        )
91
92        oinfo = binascii.unhexlify(
93            b"a1b2c3d4e53728157e634612c12d6d5223e204aeea4341565369647bd184bcd2"
94            b"46f72971f292badaa2fe4124612cba"
95        )
96
97        ckdf = ConcatKDFHash(hashes.SHA256(), 16, oinfo, backend)
98
99        with pytest.raises(InvalidKey):
100            ckdf.verify(prk, b"wrong key")
101
102    def test_unicode_typeerror(self, backend):
103        with pytest.raises(TypeError):
104            ConcatKDFHash(
105                hashes.SHA256(),
106                16,
107                otherinfo=u"foo",
108                backend=backend
109            )
110
111        with pytest.raises(TypeError):
112            ckdf = ConcatKDFHash(
113                hashes.SHA256(),
114                16,
115                otherinfo=None,
116                backend=backend
117            )
118
119            ckdf.derive(u"foo")
120
121        with pytest.raises(TypeError):
122            ckdf = ConcatKDFHash(
123                hashes.SHA256(),
124                16,
125                otherinfo=None,
126                backend=backend
127            )
128
129            ckdf.verify(u"foo", b"bar")
130
131        with pytest.raises(TypeError):
132            ckdf = ConcatKDFHash(
133                hashes.SHA256(),
134                16,
135                otherinfo=None,
136                backend=backend
137            )
138
139            ckdf.verify(b"foo", u"bar")
140
141
142@pytest.mark.requires_backend_interface(interface=HMACBackend)
143class TestConcatKDFHMAC(object):
144    def test_length_limit(self, backend):
145        big_length = hashes.SHA256().digest_size * (2 ** 32 - 1) + 1
146
147        with pytest.raises(ValueError):
148            ConcatKDFHMAC(hashes.SHA256(), big_length, None, None, backend)
149
150    def test_already_finalized(self, backend):
151        ckdf = ConcatKDFHMAC(hashes.SHA256(), 16, None, None, backend)
152
153        ckdf.derive(b"\x01" * 16)
154
155        with pytest.raises(AlreadyFinalized):
156            ckdf.derive(b"\x02" * 16)
157
158    def test_derive(self, backend):
159        prk = binascii.unhexlify(
160            b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
161            b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
162            b"8f0af7fce1d045edbc5790931e8d5ca79c73"
163        )
164
165        okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
166                                 b"605323ce2f39bf27eaaac8b34cf89f2f")
167
168        oinfo = binascii.unhexlify(
169            b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
170            b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
171        )
172
173        ckdf = ConcatKDFHMAC(hashes.SHA512(), 32, None, oinfo, backend)
174
175        assert ckdf.derive(prk) == okm
176
177    def test_buffer_protocol(self, backend):
178        prk = binascii.unhexlify(
179            b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
180            b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
181            b"8f0af7fce1d045edbc5790931e8d5ca79c73"
182        )
183
184        okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
185                                 b"605323ce2f39bf27eaaac8b34cf89f2f")
186
187        oinfo = binascii.unhexlify(
188            b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
189            b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
190        )
191
192        ckdf = ConcatKDFHMAC(hashes.SHA512(), 32, None, oinfo, backend)
193
194        assert ckdf.derive(bytearray(prk)) == okm
195
196    def test_derive_explicit_salt(self, backend):
197        prk = binascii.unhexlify(
198            b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
199            b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
200            b"8f0af7fce1d045edbc5790931e8d5ca79c73"
201        )
202
203        okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
204                                 b"605323ce2f39bf27eaaac8b34cf89f2f")
205
206        oinfo = binascii.unhexlify(
207            b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
208            b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
209        )
210
211        ckdf = ConcatKDFHMAC(
212            hashes.SHA512(), 32, b"\x00" * 128, oinfo, backend
213        )
214
215        assert ckdf.derive(prk) == okm
216
217    def test_verify(self, backend):
218        prk = binascii.unhexlify(
219            b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
220            b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
221            b"8f0af7fce1d045edbc5790931e8d5ca79c73"
222        )
223
224        okm = binascii.unhexlify(b"64ce901db10d558661f10b6836a122a7"
225                                 b"605323ce2f39bf27eaaac8b34cf89f2f")
226
227        oinfo = binascii.unhexlify(
228            b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
229            b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
230        )
231
232        ckdf = ConcatKDFHMAC(hashes.SHA512(), 32, None, oinfo, backend)
233
234        assert ckdf.verify(prk, okm) is None
235
236    def test_invalid_verify(self, backend):
237        prk = binascii.unhexlify(
238            b"013951627c1dea63ea2d7702dd24e963eef5faac6b4af7e4"
239            b"b831cde499dff1ce45f6179f741c728aa733583b02409208"
240            b"8f0af7fce1d045edbc5790931e8d5ca79c73"
241        )
242
243        oinfo = binascii.unhexlify(
244            b"a1b2c3d4e55e600be5f367e0e8a465f4bf2704db00c9325c"
245            b"9fbd216d12b49160b2ae5157650f43415653696421e68e"
246        )
247
248        ckdf = ConcatKDFHMAC(hashes.SHA512(), 32, None, oinfo, backend)
249
250        with pytest.raises(InvalidKey):
251            ckdf.verify(prk, b"wrong key")
252
253    def test_unicode_typeerror(self, backend):
254        with pytest.raises(TypeError):
255            ConcatKDFHMAC(
256                hashes.SHA256(),
257                16, salt=u"foo",
258                otherinfo=None,
259                backend=backend
260            )
261
262        with pytest.raises(TypeError):
263            ConcatKDFHMAC(
264                hashes.SHA256(),
265                16, salt=None,
266                otherinfo=u"foo",
267                backend=backend
268            )
269
270        with pytest.raises(TypeError):
271            ckdf = ConcatKDFHMAC(
272                hashes.SHA256(),
273                16, salt=None,
274                otherinfo=None,
275                backend=backend
276            )
277
278            ckdf.derive(u"foo")
279
280        with pytest.raises(TypeError):
281            ckdf = ConcatKDFHMAC(
282                hashes.SHA256(),
283                16, salt=None,
284                otherinfo=None,
285                backend=backend
286            )
287
288            ckdf.verify(u"foo", b"bar")
289
290        with pytest.raises(TypeError):
291            ckdf = ConcatKDFHMAC(
292                hashes.SHA256(),
293                16, salt=None,
294                otherinfo=None,
295                backend=backend
296            )
297
298            ckdf.verify(b"foo", u"bar")
299
300
301def test_invalid_backend():
302    pretend_backend = object()
303
304    with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
305        ConcatKDFHash(hashes.SHA256(), 16, None, pretend_backend)
306    with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE):
307        ConcatKDFHMAC(hashes.SHA256(), 16, None, None, pretend_backend)
308