]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2007-2008,2010 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <stdlib.h> | |
25 | #include <string.h> // memcpy | |
26 | ||
27 | #include <CommonCrypto/CommonDigest.h> | |
427c49bc | 28 | #include <CommonCrypto/CommonDigestSPI.h> |
b1ab9ed8 A |
29 | |
30 | #include <corecrypto/ccn.h> | |
31 | ||
32 | #include "p12pbegen.h" | |
33 | ||
34 | static uint8_t *concatenate_to_blocksize(const uint8_t *data, size_t data_length, | |
35 | size_t blocksize, size_t *blocklength) | |
36 | { | |
37 | size_t block_length = blocksize * ((data_length + blocksize - 1) / blocksize); | |
38 | uint8_t *block_ptr, *block; | |
39 | block_ptr = block = malloc(block_length); | |
40 | if (!block_ptr) | |
41 | return NULL; | |
42 | while (block_ptr < block + block_length) { | |
43 | size_t bytes_to_move = block + block_length - block_ptr; | |
44 | memcpy(block_ptr, data, bytes_to_move > data_length ? data_length : bytes_to_move); | |
45 | block_ptr += data_length; | |
46 | } | |
47 | *blocklength = block_length; | |
48 | return block; | |
49 | } | |
50 | ||
51 | int p12_pbe_gen(CFStringRef passphrase, uint8_t *salt_ptr, size_t salt_length, | |
52 | unsigned iter_count, P12_PBE_ID pbe_id, uint8_t *data, size_t length) | |
53 | { | |
54 | unsigned int hash_blocksize = CC_SHA1_BLOCK_BYTES; | |
55 | unsigned int hash_outputsize = CC_SHA1_DIGEST_LENGTH; | |
56 | ||
57 | if (!passphrase) | |
58 | return -1; | |
59 | ||
60 | /* generate diversifier block */ | |
61 | unsigned char diversifier[hash_blocksize]; | |
62 | memset(diversifier, pbe_id, sizeof(diversifier)); | |
63 | ||
64 | /* convert passphrase to BE UTF16 and append double null */ | |
65 | CFDataRef passphrase_be_unicode = CFStringCreateExternalRepresentation(kCFAllocatorDefault, passphrase, kCFStringEncodingUTF16BE, '\0'); | |
66 | if (!passphrase_be_unicode) | |
67 | return -1; | |
68 | uint8_t null_termination[2] = { 0, 0 }; | |
69 | CFMutableDataRef passphrase_be_unicode_null_term = CFDataCreateMutableCopy(NULL, 0, passphrase_be_unicode); | |
70 | CFRelease(passphrase_be_unicode); | |
71 | if (!passphrase_be_unicode_null_term) | |
72 | return -1; | |
73 | CFDataAppendBytes(passphrase_be_unicode_null_term, null_termination, sizeof(null_termination)); | |
74 | ||
75 | /* generate passphrase block */ | |
76 | uint8_t *passphrase_data = NULL; | |
77 | size_t passphrase_data_len = 0; | |
78 | size_t passphrase_length = CFDataGetLength(passphrase_be_unicode_null_term); | |
79 | const unsigned char *passphrase_ptr = CFDataGetBytePtr(passphrase_be_unicode_null_term); | |
80 | passphrase_data = concatenate_to_blocksize(passphrase_ptr, passphrase_length, hash_blocksize, &passphrase_data_len); | |
81 | CFRelease(passphrase_be_unicode_null_term); | |
82 | if (!passphrase_data) | |
83 | return -1; | |
84 | ||
85 | /* generate salt block */ | |
86 | uint8_t *salt_data = NULL; | |
87 | size_t salt_data_len = 0; | |
88 | if (salt_length) | |
89 | salt_data = concatenate_to_blocksize(salt_ptr, salt_length, hash_blocksize, &salt_data_len); | |
427c49bc A |
90 | if (!salt_data){ |
91 | free(passphrase_data); | |
b1ab9ed8 | 92 | return -1; |
427c49bc | 93 | } |
b1ab9ed8 A |
94 | /* generate S||P block */ |
95 | size_t I_length = salt_data_len + passphrase_data_len; | |
96 | uint8_t *I_data = malloc(I_length); | |
427c49bc A |
97 | if (!I_data){ |
98 | free(salt_data); | |
99 | free(passphrase_data); | |
b1ab9ed8 | 100 | return -1; |
427c49bc | 101 | } |
b1ab9ed8 A |
102 | memcpy(I_data + 0, salt_data, salt_data_len); |
103 | memcpy(I_data + salt_data_len, passphrase_data, passphrase_data_len); | |
104 | free(salt_data); | |
105 | free(passphrase_data); | |
106 | ||
107 | /* round up output buffer to multiple of hash block size and allocate */ | |
108 | size_t hash_output_blocks = (length + hash_outputsize - 1) / hash_outputsize; | |
109 | size_t temp_buf_size = hash_output_blocks * hash_outputsize; | |
110 | uint8_t *temp_buf = malloc(temp_buf_size); | |
111 | uint8_t *cursor = temp_buf; | |
427c49bc A |
112 | if (!temp_buf){ |
113 | free(I_data); | |
b1ab9ed8 | 114 | return -1; |
427c49bc | 115 | } |
b1ab9ed8 A |
116 | /* 64 bits cast(s): worst case here is we dont hash all the data and incorectly derive the wrong key, |
117 | when the passphrase + salt are over 2^32 bytes long */ | |
118 | /* loop over output in hash_output_size increments */ | |
119 | while (cursor < temp_buf + temp_buf_size) { | |
120 | CC_SHA1_CTX ctx; | |
121 | CC_SHA1_Init(&ctx); | |
122 | CC_SHA1_Update(&ctx, diversifier, (CC_LONG)sizeof(diversifier)); | |
123 | assert(I_length<=UINT32_MAX); /* debug check. Correct as long as CC_LONG is uint32_t */ | |
124 | CC_SHA1_Update(&ctx, I_data, (CC_LONG)I_length); | |
125 | CC_SHA1_Final(cursor, &ctx); | |
126 | ||
127 | /* run block through SHA-1 for iteration count */ | |
128 | unsigned int i; | |
129 | for (i = 1; /*first round done above*/ i < iter_count; i++) | |
427c49bc | 130 | CCDigest(kCCDigestSHA1, cursor, hash_outputsize, cursor); |
b1ab9ed8 A |
131 | |
132 | /* | |
133 | * b) Concatenate copies of A[i] to create a string B of | |
134 | * length v bits (the final copy of A[i]i may be truncated | |
135 | * to create B). | |
136 | */ | |
137 | size_t A_i_len = 0; | |
138 | uint8_t *A_i = concatenate_to_blocksize(cursor, | |
139 | hash_outputsize, hash_blocksize, &A_i_len); | |
427c49bc A |
140 | if (!A_i){ |
141 | free(I_data); | |
142 | free(temp_buf); | |
b1ab9ed8 | 143 | return -1; |
427c49bc | 144 | } |
b1ab9ed8 A |
145 | /* |
146 | * c) Treating I as a concatenation I[0], I[1], ..., | |
147 | * I[k-1] of v-bit blocks, where k = ceil(s/v) + ceil(p/v), | |
148 | * modify I by setting I[j]=(I[j]+B+1) mod (2 ** v) | |
149 | * for each j. | |
150 | */ | |
151 | ||
152 | /* tmp1 = B+1 */ | |
153 | ||
154 | const cc_size tmp_n = ccn_nof_size(A_i_len + 1) > ccn_nof_size(hash_blocksize) ? ccn_nof_size(A_i_len + 1) : ccn_nof_size(hash_blocksize); | |
155 | cc_unit tmp1[tmp_n]; | |
156 | ccn_read_uint(tmp_n, tmp1, A_i_len, A_i); | |
157 | ccn_add1(tmp_n, tmp1, tmp1, 1); | |
158 | ||
159 | free(A_i); | |
160 | ||
161 | cc_unit tmp2[tmp_n]; | |
162 | unsigned int j; | |
163 | for (j = 0; j < I_length; j+=hash_blocksize) { | |
164 | /* tempg = I[j]; */ | |
165 | ccn_read_uint(tmp_n, tmp2, hash_blocksize, I_data + j); | |
166 | /* tempg += tmp1 */ | |
167 | ccn_add(tmp_n, tmp2, tmp2, tmp1); | |
168 | ||
169 | /* I[j] = tempg mod 2**v | |
170 | Just clear all the high bits above 2**v | |
171 | In practice at most it rolled over by 1 bit, since all we did was add so | |
172 | we should only clear one bit at most. | |
173 | */ | |
174 | size_t bitSize; | |
175 | const size_t hash_blocksize_bits = hash_blocksize * 8; | |
176 | while ((bitSize = ccn_bitlen(tmp_n, tmp2)) > hash_blocksize_bits) | |
177 | { | |
178 | ccn_set_bit(tmp2, bitSize - 1, 0); | |
179 | } | |
180 | ||
181 | ccn_write_uint_padded(tmp_n, tmp2, hash_blocksize, I_data + j); | |
182 | } | |
183 | ||
184 | cursor += hash_outputsize; | |
185 | } | |
186 | ||
187 | /* | |
188 | * 7. Concatenate A[1], A[2], ..., A[c] together to form a | |
189 | * pseudo-random bit string, A. | |
190 | * | |
191 | * 8. Use the first n bits of A as the output of this entire | |
192 | * process. | |
193 | */ | |
194 | memmove(data, temp_buf, length); | |
195 | free(temp_buf); | |
196 | free(I_data); | |
197 | return 0; | |
198 | } | |
199 | ||
200 | #if 0 | |
201 | bool test() | |
202 | { | |
203 | //smeg => 0073006D006500670000 | |
204 | CFStringRef password = CFSTR("smeg"); | |
205 | //Salt (length 8): | |
206 | unsigned char salt_bytes[] = { 0x0A, 0x58, 0xCF, 0x64, 0x53, 0x0D, 0x82, 0x3F }; | |
207 | CFDataRef salt = CFDataCreate(NULL, salt_bytes, sizeof(salt_bytes)); | |
208 | // ID 1, ITER 1 | |
209 | // Output KEY (length 24) | |
210 | unsigned char correct_result[] = { 0x8A, 0xAA, 0xE6, 0x29, 0x7B, 0x6C, 0xB0, 0x46, 0x42, 0xAB, 0x5B, 0x07, 0x78, 0x51, 0x28, 0x4E, 0xB7, 0x12, 0x8F, 0x1A, 0x2A, 0x7F, 0xBC, 0xA3 }; | |
211 | unsigned char result[24]; | |
212 | p12PbeGen(password, salt, 1, PBE_ID_Key, result, sizeof(result)); | |
213 | if (memcmp(correct_result, result, sizeof(correct_result))) { | |
214 | printf("test failure\n"); | |
215 | return false; | |
216 | } | |
217 | return true; | |
218 | } | |
219 | ||
220 | bool test2() | |
221 | { | |
222 | CFStringRef password = CFSTR("queeg"); | |
223 | unsigned char salt_bytes[] = { 0x05,0xDE,0xC9,0x59,0xAC,0xFF,0x72,0xF7 }; | |
224 | CFDataRef salt = CFDataCreate(NULL, salt_bytes, sizeof(salt_bytes)); | |
225 | unsigned char correct_result[] = { 0xED,0x20,0x34,0xE3,0x63,0x28,0x83,0x0F,0xF0,0x9D,0xF1,0xE1,0xA0,0x7D,0xD3,0x57,0x18,0x5D,0xAC,0x0D,0x4F,0x9E,0xB3,0xD4 }; | |
226 | unsigned char result[24]; | |
227 | p12PbeGen(password, salt, 1000, PBE_ID_Key, result, sizeof(result)); | |
228 | if (memcmp(correct_result, result, sizeof(correct_result))) { | |
229 | printf("test failure\n"); | |
230 | return false; | |
231 | } | |
232 | return true; | |
233 | } | |
234 | ||
235 | int main(int argc, char *argv[]) | |
236 | { | |
237 | test(); | |
238 | test2(); | |
239 | } | |
240 | ||
241 | #endif | |
242 | ||
243 | /* http://www.drh-consultancy.demon.co.uk/test.txt | |
244 | ||
245 | Test Vectors set 1. | |
246 | ||
247 | Password: smeg | |
248 | ||
249 | KEYGEN DEBUG | |
250 | ID 1, ITER 1 | |
251 | Password (length 10): | |
252 | 0073006D006500670000 | |
253 | Salt (length 8): | |
254 | 0A58CF64530D823F | |
255 | ID 1, ITER 1 | |
256 | Output KEY (length 24) | |
257 | 8AAAE6297B6CB04642AB5B077851284EB7128F1A2A7FBCA3 | |
258 | ||
259 | KEYGEN DEBUG | |
260 | ID 2, ITER 1 | |
261 | Password (length 10): | |
262 | 0073006D006500670000 | |
263 | Salt (length 8): | |
264 | 0A58CF64530D823F | |
265 | ID 2, ITER 1 | |
266 | Output KEY (length 8) | |
267 | 79993DFE048D3B76 | |
268 | ||
269 | KEYGEN DEBUG | |
270 | ID 1, ITER 1 | |
271 | Password (length 10): | |
272 | 0073006D006500670000 | |
273 | Salt (length 8): | |
274 | 642B99AB44FB4B1F | |
275 | ID 1, ITER 1 | |
276 | Output KEY (length 24) | |
277 | F3A95FEC48D7711E985CFE67908C5AB79FA3D7C5CAA5D966 | |
278 | ||
279 | KEYGEN DEBUG | |
280 | ID 2, ITER 1 | |
281 | Password (length 10): | |
282 | 0073006D006500670000 | |
283 | Salt (length 8): | |
284 | 642B99AB44FB4B1F | |
285 | ID 2, ITER 1 | |
286 | Output KEY (length 8) | |
287 | C0A38D64A79BEA1D | |
288 | ||
289 | KEYGEN DEBUG | |
290 | ID 3, ITER 1 | |
291 | Password (length 10): | |
292 | 0073006D006500670000 | |
293 | Salt (length 8): | |
294 | 3D83C0E4546AC140 | |
295 | ID 3, ITER 1 | |
296 | Output KEY (length 20) | |
297 | 8D967D88F6CAA9D714800AB3D48051D63F73A312 | |
298 | ||
299 | Test Vectors set 2. | |
300 | Password: queeg | |
301 | ||
302 | KEYGEN DEBUG | |
303 | ID 1, ITER 1000 | |
304 | Password (length 12): | |
305 | 007100750065006500670000 | |
306 | Salt (length 8): | |
307 | 05DEC959ACFF72F7 | |
308 | ID 1, ITER 1000 | |
309 | Output KEY (length 24) | |
310 | ED2034E36328830FF09DF1E1A07DD357185DAC0D4F9EB3D4 | |
311 | ||
312 | KEYGEN DEBUG | |
313 | ID 2, ITER 1000 | |
314 | Password (length 12): | |
315 | 007100750065006500670000 | |
316 | Salt (length 8): | |
317 | 05DEC959ACFF72F7 | |
318 | ID 2, ITER 1000 | |
319 | Output KEY (length 8) | |
320 | 11DEDAD7758D4860 | |
321 | ||
322 | KEYGEN DEBUG | |
323 | ID 1, ITER 1000 | |
324 | Password (length 12): | |
325 | 007100750065006500670000 | |
326 | Salt (length 8): | |
327 | 1682C0FC5B3F7EC5 | |
328 | ID 1, ITER 1000 | |
329 | Output KEY (length 24) | |
330 | 483DD6E919D7DE2E8E648BA8F862F3FBFBDC2BCB2C02957F | |
331 | ||
332 | KEYGEN DEBUG | |
333 | ID 2, ITER 1000 | |
334 | Password (length 12): | |
335 | 007100750065006500670000 | |
336 | Salt (length 8): | |
337 | 1682C0FC5B3F7EC5 | |
338 | ID 2, ITER 1000 | |
339 | Output KEY (length 8) | |
340 | 9D461D1B00355C50 | |
341 | ||
342 | KEYGEN DEBUG | |
343 | ID 3, ITER 1000 | |
344 | Password (length 12): | |
345 | 007100750065006500670000 | |
346 | Salt (length 8): | |
347 | 263216FCC2FAB31C | |
348 | ID 3, ITER 1000 | |
349 | Output KEY (length 20) | |
350 | 5EC4C7A80DF652294C3925B6489A7AB857C83476 | |
351 | */ | |
352 |