1# coding: utf-8
2from __future__ import unicode_literals, division, absolute_import, print_function
3
4from .._errors import pretty_message
5from .._ffi import buffer_from_bytes, bytes_from_buffer
6from ._libcrypto import libcrypto, libcrypto_version_info, handle_openssl_error
7from .._rand import rand_bytes
8from .._types import type_name, byte_cls, int_types
9
10
11__all__ = [
12 'pbkdf2',
13 'pkcs12_kdf',
14 'rand_bytes',
15]
16
17
18# OpenSSL 0.9.8 does not include PBKDF2
19if libcrypto_version_info < (1,):
20 from .._pkcs5 import pbkdf2
21
22else:
23 def pbkdf2(hash_algorithm, password, salt, iterations, key_length):
24 """
25 PBKDF2 from PKCS#5
26
27 :param hash_algorithm:
28 The string name of the hash algorithm to use: "sha1", "sha224", "sha256", "sha384", "sha512"
29
30 :param password:
31 A byte string of the password to use an input to the KDF
32
33 :param salt:
34 A cryptographic random byte string
35
36 :param iterations:
37 The numbers of iterations to use when deriving the key
38
39 :param key_length:
40 The length of the desired key in bytes
41
42 :raises:
43 ValueError - when any of the parameters contain an invalid value
44 TypeError - when any of the parameters are of the wrong type
45
46 :return:
47 The derived key as a byte string
48 """
49
50 if not isinstance(password, byte_cls):
51 raise TypeError(pretty_message(
52 '''
53 password must be a byte string, not %s
54 ''',
55 type_name(password)
56 ))
57
58 if not isinstance(salt, byte_cls):
59 raise TypeError(pretty_message(
60 '''
61 salt must be a byte string, not %s
62 ''',
63 type_name(salt)
64 ))
65
66 if not isinstance(iterations, int_types):
67 raise TypeError(pretty_message(
68 '''
69 iterations must be an integer, not %s
70 ''',
71 type_name(iterations)
72 ))
73
74 if iterations < 1:
75 raise ValueError('iterations must be greater than 0')
76
77 if not isinstance(key_length, int_types):
78 raise TypeError(pretty_message(
79 '''
80 key_length must be an integer, not %s
81 ''',
82 type_name(key_length)
83 ))
84
85 if key_length < 1:
86 raise ValueError('key_length must be greater than 0')
87
88 if hash_algorithm not in set(['sha1', 'sha224', 'sha256', 'sha384', 'sha512']):
89 raise ValueError(pretty_message(
90 '''
91 hash_algorithm must be one of "sha1", "sha224", "sha256", "sha384",
92 "sha512", not %s
93 ''',
94 repr(hash_algorithm)
95 ))
96
97 evp_md = {
98 'sha1': libcrypto.EVP_sha1,
99 'sha224': libcrypto.EVP_sha224,
100 'sha256': libcrypto.EVP_sha256,
101 'sha384': libcrypto.EVP_sha384,
102 'sha512': libcrypto.EVP_sha512
103 }[hash_algorithm]()
104
105 output_buffer = buffer_from_bytes(key_length)
106 result = libcrypto.PKCS5_PBKDF2_HMAC(
107 password,
108 len(password),
109 salt,
110 len(salt),
111 iterations,
112 evp_md,
113 key_length,
114 output_buffer
115 )
116 handle_openssl_error(result)
117
118 return bytes_from_buffer(output_buffer)
119
120 pbkdf2.pure_python = False
121
122
123def pkcs12_kdf(hash_algorithm, password, salt, iterations, key_length, id_):
124 """
125 KDF from RFC7292 appendix B.2 - https://tools.ietf.org/html/rfc7292#page-19
126
127 :param hash_algorithm:
128 The string name of the hash algorithm to use: "md5", "sha1", "sha224", "sha256", "sha384", "sha512"
129
130 :param password:
131 A byte string of the password to use an input to the KDF
132
133 :param salt:
134 A cryptographic random byte string
135
136 :param iterations:
137 The numbers of iterations to use when deriving the key
138
139 :param key_length:
140 The length of the desired key in bytes
141
142 :param id_:
143 The ID of the usage - 1 for key, 2 for iv, 3 for mac
144
145 :raises:
146 ValueError - when any of the parameters contain an invalid value
147 TypeError - when any of the parameters are of the wrong type
148
149 :return:
150 The derived key as a byte string
151 """
152
153 if not isinstance(password, byte_cls):
154 raise TypeError(pretty_message(
155 '''
156 password must be a byte string, not %s
157 ''',
158 type_name(password)
159 ))
160
161 if not isinstance(salt, byte_cls):
162 raise TypeError(pretty_message(
163 '''
164 salt must be a byte string, not %s
165 ''',
166 type_name(salt)
167 ))
168
169 if not isinstance(iterations, int_types):
170 raise TypeError(pretty_message(
171 '''
172 iterations must be an integer, not %s
173 ''',
174 type_name(iterations)
175 ))
176
177 if iterations < 1:
178 raise ValueError(pretty_message(
179 '''
180 iterations must be greater than 0 - is %s
181 ''',
182 repr(iterations)
183 ))
184
185 if not isinstance(key_length, int_types):
186 raise TypeError(pretty_message(
187 '''
188 key_length must be an integer, not %s
189 ''',
190 type_name(key_length)
191 ))
192
193 if key_length < 1:
194 raise ValueError(pretty_message(
195 '''
196 key_length must be greater than 0 - is %s
197 ''',
198 repr(key_length)
199 ))
200
201 if hash_algorithm not in set(['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512']):
202 raise ValueError(pretty_message(
203 '''
204 hash_algorithm must be one of "md5", "sha1", "sha224", "sha256",
205 "sha384", "sha512", not %s
206 ''',
207 repr(hash_algorithm)
208 ))
209
210 if id_ not in set([1, 2, 3]):
211 raise ValueError(pretty_message(
212 '''
213 id_ must be one of 1, 2, 3, not %s
214 ''',
215 repr(id_)
216 ))
217
218 utf16_password = password.decode('utf-8').encode('utf-16be') + b'\x00\x00'
219
220 digest_type = {
221 'md5': libcrypto.EVP_md5,
222 'sha1': libcrypto.EVP_sha1,
223 'sha224': libcrypto.EVP_sha224,
224 'sha256': libcrypto.EVP_sha256,
225 'sha384': libcrypto.EVP_sha384,
226 'sha512': libcrypto.EVP_sha512,
227 }[hash_algorithm]()
228
229 output_buffer = buffer_from_bytes(key_length)
230 result = libcrypto.PKCS12_key_gen_uni(
231 utf16_password,
232 len(utf16_password),
233 salt,
234 len(salt),
235 id_,
236 iterations,
237 key_length,
238 output_buffer,
239 digest_type
240 )
241 handle_openssl_error(result)
242
243 return bytes_from_buffer(output_buffer)