• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 struct
8
9from cryptography import utils
10from cryptography.exceptions import (
11    AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
12)
13from cryptography.hazmat.backends.interfaces import HMACBackend
14from cryptography.hazmat.backends.interfaces import HashBackend
15from cryptography.hazmat.primitives import constant_time, hashes, hmac
16from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
17
18
19def _int_to_u32be(n):
20    return struct.pack('>I', n)
21
22
23def _common_args_checks(algorithm, length, otherinfo):
24    max_length = algorithm.digest_size * (2 ** 32 - 1)
25    if length > max_length:
26        raise ValueError(
27            "Can not derive keys larger than {0} bits.".format(
28                max_length
29            ))
30    if otherinfo is not None:
31        utils._check_bytes("otherinfo", otherinfo)
32
33
34def _concatkdf_derive(key_material, length, auxfn, otherinfo):
35    utils._check_byteslike("key_material", key_material)
36    output = [b""]
37    outlen = 0
38    counter = 1
39
40    while (length > outlen):
41        h = auxfn()
42        h.update(_int_to_u32be(counter))
43        h.update(key_material)
44        h.update(otherinfo)
45        output.append(h.finalize())
46        outlen += len(output[-1])
47        counter += 1
48
49    return b"".join(output)[:length]
50
51
52@utils.register_interface(KeyDerivationFunction)
53class ConcatKDFHash(object):
54    def __init__(self, algorithm, length, otherinfo, backend):
55
56        _common_args_checks(algorithm, length, otherinfo)
57        self._algorithm = algorithm
58        self._length = length
59        self._otherinfo = otherinfo
60        if self._otherinfo is None:
61            self._otherinfo = b""
62
63        if not isinstance(backend, HashBackend):
64            raise UnsupportedAlgorithm(
65                "Backend object does not implement HashBackend.",
66                _Reasons.BACKEND_MISSING_INTERFACE
67            )
68        self._backend = backend
69        self._used = False
70
71    def _hash(self):
72        return hashes.Hash(self._algorithm, self._backend)
73
74    def derive(self, key_material):
75        if self._used:
76            raise AlreadyFinalized
77        self._used = True
78        return _concatkdf_derive(key_material, self._length,
79                                 self._hash, self._otherinfo)
80
81    def verify(self, key_material, expected_key):
82        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
83            raise InvalidKey
84
85
86@utils.register_interface(KeyDerivationFunction)
87class ConcatKDFHMAC(object):
88    def __init__(self, algorithm, length, salt, otherinfo, backend):
89
90        _common_args_checks(algorithm, length, otherinfo)
91        self._algorithm = algorithm
92        self._length = length
93        self._otherinfo = otherinfo
94        if self._otherinfo is None:
95            self._otherinfo = b""
96
97        if salt is None:
98            salt = b"\x00" * algorithm.block_size
99        else:
100            utils._check_bytes("salt", salt)
101
102        self._salt = salt
103
104        if not isinstance(backend, HMACBackend):
105            raise UnsupportedAlgorithm(
106                "Backend object does not implement HMACBackend.",
107                _Reasons.BACKEND_MISSING_INTERFACE
108            )
109        self._backend = backend
110        self._used = False
111
112    def _hmac(self):
113        return hmac.HMAC(self._salt, self._algorithm, self._backend)
114
115    def derive(self, key_material):
116        if self._used:
117            raise AlreadyFinalized
118        self._used = True
119        return _concatkdf_derive(key_material, self._length,
120                                 self._hmac, self._otherinfo)
121
122    def verify(self, key_material, expected_key):
123        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
124            raise InvalidKey
125