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