]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2003,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please | |
7 | * obtain a copy of the License at http://www.apple.com/publicsource and | |
8 | * read it before using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
12 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
13 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
14 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
15 | * Please see the License for the specific language governing rights and | |
16 | * limitations under the License. | |
17 | */ | |
18 | /* | |
19 | * pkcs12Derive.cpp - PKCS12 PBE routine | |
20 | * | |
b1ab9ed8 A |
21 | */ |
22 | ||
23 | #include <Security/cssmapple.h> | |
6b200bc3 A |
24 | #include <openssl/bn_legacy.h> |
25 | #include "pbkdDigest.h" | |
b1ab9ed8 A |
26 | |
27 | #include "pkcs12Derive.h" | |
28 | #include "AppleCSPUtils.h" | |
29 | #include "AppleCSPContext.h" | |
30 | ||
31 | #include <string.h> | |
32 | #include <stdlib.h> | |
33 | #include <stdio.h> | |
d64be36e | 34 | #include <security_utilities/simulatecrash_assert.h> |
b1ab9ed8 A |
35 | #include <security_asn1/SecNssCoder.h> |
36 | ||
37 | #include <CoreFoundation/CoreFoundation.h> | |
38 | ||
39 | /* specify which flavor of bits to generate */ | |
40 | typedef enum { | |
41 | PBE_ID_Key = 1, | |
42 | PBE_ID_IV = 2, | |
43 | PBE_ID_MAC = 3 | |
44 | } P12_PBE_ID; | |
45 | ||
46 | /* | |
47 | * Create a "string" (in the loose p12 notation) of specified length | |
48 | * from the concatention of copies of the specified input string. | |
49 | */ | |
50 | static unsigned char *p12StrCat( | |
51 | const unsigned char *inStr, | |
52 | unsigned inStrLen, | |
53 | SecNssCoder &coder, | |
54 | unsigned outLen, | |
55 | unsigned char *outStr = NULL) // if not present, we malloc | |
56 | { | |
57 | if(outStr == NULL) { | |
58 | outStr = (unsigned char *)coder.malloc(outLen); | |
59 | } | |
60 | unsigned toMove = outLen; | |
61 | unsigned char *outp = outStr; | |
62 | while(toMove) { | |
63 | unsigned thisMove = inStrLen; | |
64 | if(thisMove > toMove) { | |
65 | thisMove = toMove; | |
66 | } | |
67 | memmove(outp, inStr, thisMove); | |
68 | toMove -= thisMove; | |
69 | outp += thisMove; | |
70 | } | |
71 | return outStr; | |
72 | } | |
73 | ||
74 | /* | |
75 | * PBE generator per PKCS12 v.1 section B.2. | |
76 | */ | |
77 | static CSSM_RETURN p12PbeGen( | |
78 | const CSSM_DATA &pwd, // unicode, double null terminated | |
79 | const uint8 *salt, | |
80 | unsigned saltLen, | |
81 | unsigned iterCount, | |
82 | P12_PBE_ID pbeId, | |
83 | CSSM_ALGORITHMS hashAlg, // MS5 or SHA1 only | |
84 | SecNssCoder &coder, // for temp allocs | |
85 | /* result goes here, mallocd by caller */ | |
86 | uint8 *outbuf, | |
87 | unsigned outbufLen) | |
88 | { | |
89 | CSSM_RETURN ourRtn = CSSM_OK; | |
427c49bc | 90 | unsigned unipassLen = (unsigned)pwd.Length; |
b1ab9ed8 A |
91 | unsigned char *unipass = pwd.Data; |
92 | int irtn; | |
93 | ||
94 | /* | |
95 | * all variables of the form p12_<XXX> represent <XXX> from the | |
96 | * PKCS12 spec. E.g., p12_u is u, the length of the digest output. | |
97 | * Only difference here is: all of our sizes are in BYTES, not | |
98 | * bits. | |
99 | */ | |
100 | unsigned p12_r = iterCount; | |
101 | unsigned p12_n = outbufLen; | |
102 | ||
103 | unsigned p12_u; // hash output size | |
104 | unsigned p12_v; // hash block size | |
105 | unsigned char *p12_P = NULL; // catted passwords | |
106 | unsigned char *p12_S = NULL; // catted salts | |
107 | ||
108 | switch(hashAlg) { | |
109 | case CSSM_ALGID_MD5: | |
110 | p12_u = kMD5DigestSize; | |
111 | p12_v = kMD5BlockSize; | |
112 | break; | |
113 | case CSSM_ALGID_SHA1: | |
114 | p12_u = kSHA1DigestSize; | |
115 | p12_v = kSHA1BlockSize; | |
116 | break; | |
117 | default: | |
118 | return CSSMERR_CSP_INVALID_ALGORITHM; | |
119 | } | |
120 | ||
121 | /* | |
122 | * 1. Construct a string, D (the diversifier), by | |
123 | * concatenating v/8 copies of ID. | |
124 | */ | |
125 | unsigned char *p12_D = NULL; // diversifier | |
126 | p12_D = (unsigned char *)coder.malloc(p12_v); | |
127 | for(unsigned dex=0; dex<p12_v; dex++) { | |
128 | p12_D[dex] = (unsigned char)pbeId; | |
129 | } | |
130 | ||
131 | /* | |
132 | * 2. Concatenate copies of the salt together to create | |
133 | * a string S of length v * ceil(s/v) bits (the final copy | |
134 | * of the salt may be truncated to create S). Note that if | |
135 | * the salt is the empty string, then so is S. | |
136 | */ | |
137 | unsigned p12_Slen = p12_v * ((saltLen + p12_v - 1) / p12_v); | |
138 | if(p12_Slen) { | |
139 | p12_S = p12StrCat(salt, saltLen, coder, p12_Slen); | |
140 | } | |
141 | ||
142 | ||
143 | /* | |
144 | * 3. Concatenate copies of the password together to create | |
145 | * a string P of length v * ceil(p/v) bits (the final copy of | |
146 | * the password may be truncated to create P). Note that | |
147 | * if the password is the empty string, then so is P. | |
148 | */ | |
149 | unsigned p12_Plen = p12_v * ((unipassLen + p12_v - 1) / p12_v); | |
150 | if(p12_Plen) { | |
151 | p12_P = p12StrCat(unipass, unipassLen, coder, p12_Plen); | |
152 | } | |
153 | ||
154 | /* | |
155 | * 4. Set I= S||P to be the concatenation of S and P. | |
156 | */ | |
157 | unsigned char *p12_I = | |
158 | (unsigned char *)coder.malloc(p12_Slen + p12_Plen); | |
159 | memmove(p12_I, p12_S, p12_Slen); | |
160 | if(p12_Plen) { | |
161 | memmove(p12_I + p12_Slen, p12_P, p12_Plen); | |
162 | } | |
163 | ||
164 | /* | |
165 | * 5. Set c = ceil(n/u). | |
166 | */ | |
167 | unsigned p12_c = (p12_n + p12_u - 1) / p12_u; | |
168 | ||
169 | /* allocate c hash-output-size bufs */ | |
170 | unsigned char *p12_A = (unsigned char *)coder.malloc(p12_c * p12_u); | |
171 | ||
172 | /* one reusable hash object */ | |
173 | DigestCtx ourDigest; | |
174 | DigestCtx *hashHand = &ourDigest; | |
427c49bc | 175 | |
b1ab9ed8 A |
176 | /* reused inside the loop */ |
177 | unsigned char *p12_B = (unsigned char *)coder.malloc(p12_v + 1); | |
178 | BIGNUM *Ij = BN_new(); | |
179 | BIGNUM *Bpl1 = BN_new(); | |
180 | ||
181 | /* | |
182 | * 6. For i=1, 2, ..., p12_c, do the following: | |
183 | */ | |
184 | for(unsigned p12_i=0; p12_i<p12_c; p12_i++) { | |
185 | unsigned char *p12_AsubI = p12_A + (p12_i * p12_u); | |
186 | ||
187 | /* | |
188 | * a) Set A[i] = H**r(D||I). (i.e. the rth hash of D||I, | |
189 | * H(H(H(...H(D||I)))) | |
190 | */ | |
191 | irtn = DigestCtxInit(hashHand, hashAlg); | |
192 | if(!irtn) { | |
193 | ourRtn = CSSMERR_CSP_INTERNAL_ERROR; | |
194 | break; | |
195 | } | |
196 | DigestCtxUpdate(hashHand, p12_D, p12_v); | |
197 | DigestCtxUpdate(hashHand, p12_I, p12_Slen + p12_Plen); | |
198 | DigestCtxFinal(hashHand, p12_AsubI); | |
199 | ||
200 | for(unsigned iter=1; iter<p12_r; iter++) { | |
201 | irtn = DigestCtxInit(hashHand, hashAlg); | |
202 | if(!irtn) { | |
203 | ourRtn = CSSMERR_CSP_INTERNAL_ERROR; | |
204 | break; | |
205 | } | |
206 | DigestCtxUpdate(hashHand, p12_AsubI, p12_u); | |
207 | DigestCtxFinal(hashHand, p12_AsubI); | |
208 | } | |
209 | ||
210 | /* | |
211 | * b) Concatenate copies of A[i] to create a string B of | |
212 | * length v bits (the final copy of A[i]i may be truncated | |
213 | * to create B). | |
214 | */ | |
215 | p12StrCat(p12_AsubI, p12_u, coder, p12_v, p12_B); | |
216 | ||
217 | /* | |
218 | * c) Treating I as a concatenation I[0], I[1], ..., | |
219 | * I[k-1] of v-bit blocks, where k = ceil(s/v) + ceil(p/v), | |
220 | * modify I by setting I[j]=(I[j]+B+1) mod (2 ** v) | |
221 | * for each j. | |
222 | * | |
223 | * Copied from PKCS12_key_gen_uni() from openssl... | |
224 | */ | |
225 | /* Work out B + 1 first then can use B as tmp space */ | |
226 | BN_bin2bn (p12_B, p12_v, Bpl1); | |
227 | BN_add_word (Bpl1, 1); | |
228 | unsigned Ilen = p12_Slen + p12_Plen; | |
229 | ||
230 | for (unsigned j = 0; j < Ilen; j+=p12_v) { | |
231 | BN_bin2bn (p12_I + j, p12_v, Ij); | |
232 | BN_add (Ij, Ij, Bpl1); | |
233 | BN_bn2bin (Ij, p12_B); | |
234 | unsigned Ijlen = BN_num_bytes (Ij); | |
235 | /* If more than 2^(v*8) - 1 cut off MSB */ | |
236 | if (Ijlen > p12_v) { | |
237 | BN_bn2bin (Ij, p12_B); | |
238 | memcpy (p12_I + j, p12_B + 1, p12_v); | |
239 | /* If less than v bytes pad with zeroes */ | |
240 | } else if (Ijlen < p12_v) { | |
241 | memset(p12_I + j, 0, p12_v - Ijlen); | |
242 | BN_bn2bin(Ij, p12_I + j + p12_v - Ijlen); | |
243 | } else BN_bn2bin (Ij, p12_I + j); | |
244 | } | |
245 | } | |
246 | ||
247 | if(ourRtn == CSSM_OK) { | |
248 | /* | |
249 | * 7. Concatenate A[1], A[2], ..., A[c] together to form a | |
250 | * pseudo-random bit string, A. | |
251 | * | |
252 | * 8. Use the first n bits of A as the output of this entire | |
253 | * process. | |
254 | */ | |
255 | memmove(outbuf, p12_A, outbufLen); | |
256 | } | |
257 | ||
258 | /* clear all these strings */ | |
259 | if(p12_D) { | |
260 | memset(p12_D, 0, p12_v); | |
261 | } | |
262 | if(p12_S) { | |
263 | memset(p12_S, 0, p12_Slen); | |
264 | } | |
265 | if(p12_P) { | |
266 | memset(p12_P, 0, p12_Plen); | |
267 | } | |
268 | if(p12_I) { | |
269 | memset(p12_I, 0, p12_Slen + p12_Plen); | |
270 | } | |
271 | if(p12_A) { | |
272 | memset(p12_A, 0, p12_c * p12_u); | |
273 | } | |
274 | if(p12_B) { | |
275 | memset(p12_B, 0, p12_v); | |
276 | } | |
277 | if(hashHand) { | |
278 | DigestCtxFree(hashHand); | |
279 | } | |
280 | BN_free(Bpl1); | |
281 | BN_free(Ij); | |
282 | return ourRtn; | |
283 | } | |
284 | ||
285 | /* | |
286 | * Public P12 derive key function, called out from | |
287 | * AppleCSPSession::DeriveKey() | |
288 | * | |
289 | * On input: | |
290 | * --------- | |
291 | * Context parameters: | |
292 | * Salt | |
293 | * Iteration Count | |
294 | * CSSM_CRYPTO_DATA.Param - Unicode passphrase, double-NULL terminated | |
295 | * Algorithm - CSSM_ALGID_PKCS12_PBE_{ENCR,MAC} | |
296 | * Passed explicitly from DeriveKey(): | |
297 | * CSSM_DATA Param - IN/OUT - optional IV - caller mallocs space to | |
298 | * tell us to generate an IV. The param itself is not | |
299 | * optional; the presence or absence of allocated data in it | |
300 | * is our IV indicator (present/absent as well as size) | |
301 | * KeyData - mallocd by caller, we fill in keyData->Length bytes | |
302 | */ | |
303 | void DeriveKey_PKCS12 ( | |
304 | const Context &context, | |
305 | AppleCSPSession &session, | |
306 | const CssmData &Param, // other's public key | |
307 | CSSM_DATA *keyData) // mallocd by caller | |
308 | // we fill in keyData->Length bytes | |
309 | { | |
310 | SecNssCoder tmpCoder; | |
311 | ||
312 | /* | |
313 | * According to the spec, both passphrase and salt are optional. | |
314 | * Get them from context if they're present. In practical terms | |
315 | * the user really should supply a passphrase either in the | |
316 | * seed attribute (as a Unicode passphrase) or as the BaseKey | |
317 | * as a CSSM_ALGID_SECURE_PASSPHRASE key). | |
318 | */ | |
319 | CSSM_DATA pwd = {0, NULL}; | |
320 | CSSM_DATA appPwd = {0, NULL}; | |
321 | CssmCryptoData *cryptData = | |
322 | context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED); | |
323 | if((cryptData != NULL) && (cryptData->Param.Length != 0)) { | |
324 | appPwd = cryptData->Param; | |
325 | } | |
326 | else { | |
327 | /* Get pwd from base key */ | |
328 | CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY); | |
329 | if (passKey != NULL) { | |
330 | AppleCSPContext::symmetricKeyBits(context, session, | |
331 | CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE, | |
332 | appPwd.Data, appPwd.Length); | |
333 | } | |
334 | } | |
335 | if(appPwd.Data) { | |
336 | /* | |
337 | * The incoming passphrase is a UTF8 encoded enternal representation | |
338 | * of a CFString. Convert to CFString and obtain the unicode characters | |
339 | * from the string. | |
340 | */ | |
341 | CFDataRef cfData = CFDataCreate(NULL, appPwd.Data, appPwd.Length); | |
342 | CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL, | |
343 | cfData, kCFStringEncodingUTF8); | |
344 | if (cfData) | |
345 | CFRelease(cfData); | |
346 | if(cfStr == NULL) { | |
347 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); | |
348 | } | |
349 | ||
350 | /* convert unicode to chars with an extra double-NULL */ | |
427c49bc | 351 | CFIndex len = CFStringGetLength(cfStr); |
b1ab9ed8 A |
352 | tmpCoder.allocItem(pwd, sizeof(UniChar) * (len + 1)); |
353 | unsigned char *cp = pwd.Data; | |
354 | UniChar uc = 0; | |
427c49bc | 355 | for(CFIndex dex=0; dex<len; dex++) { |
b1ab9ed8 A |
356 | uc = CFStringGetCharacterAtIndex(cfStr, dex); |
357 | *cp++ = uc >> 8; | |
358 | *cp++ = uc & 0xff; | |
359 | } | |
360 | /* CFString tends to include a NULL at the end; add it if it's not there */ | |
361 | if(uc == 0) { | |
362 | if(pwd.Length < 2) { | |
363 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); | |
364 | } | |
365 | pwd.Length -= 2; | |
366 | } | |
367 | else { | |
368 | *cp++ = 0; | |
369 | *cp++ = 0; | |
370 | } | |
371 | if (cfStr) | |
372 | CFRelease(cfStr); | |
373 | } | |
374 | ||
375 | /* salt from context */ | |
376 | uint32 saltLen = 0; | |
377 | uint8 *salt = NULL; | |
378 | CssmData *csalt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT); | |
379 | if(csalt) { | |
380 | salt = csalt->Data; | |
427c49bc | 381 | saltLen = (uint32)csalt->Length; |
b1ab9ed8 A |
382 | } |
383 | ||
384 | /* | |
385 | * Iteration count, from context, required. | |
386 | * The spec's ASN1 definition says this is optional with a default | |
387 | * of one but that's a BER encode/decode issue. Here we require | |
388 | * a nonzero value. | |
389 | */ | |
390 | uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT, | |
391 | CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT); | |
392 | if(iterCount == 0) { | |
393 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT); | |
394 | } | |
395 | ||
396 | /* | |
397 | * Algorithm determines which of {PBE_ID_Key,PBE_ID_MAC} we now | |
398 | * generate. We'll also do an optional PBE_ID_IV later. | |
399 | */ | |
400 | P12_PBE_ID pbeId = PBE_ID_Key; | |
401 | switch(context.algorithm()) { | |
402 | case CSSM_ALGID_PKCS12_PBE_ENCR: | |
403 | pbeId = PBE_ID_Key; | |
404 | break; | |
405 | case CSSM_ALGID_PKCS12_PBE_MAC: | |
406 | pbeId = PBE_ID_MAC; | |
407 | break; | |
408 | default: | |
409 | /* really should not be here */ | |
410 | assert(0); | |
411 | CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); | |
412 | } | |
413 | ||
414 | /* Go */ | |
415 | CSSM_RETURN crtn = p12PbeGen(pwd, | |
416 | salt, saltLen, | |
417 | iterCount, | |
418 | pbeId, | |
419 | CSSM_ALGID_SHA1, // all we support for now | |
420 | tmpCoder, | |
421 | keyData->Data, | |
427c49bc | 422 | (unsigned)keyData->Length); |
b1ab9ed8 A |
423 | if(crtn) { |
424 | CssmError::throwMe(crtn); | |
425 | } | |
426 | ||
427 | /* | |
428 | * Optional IV - makes no sense if we just did PBE_ID_MAC, but why | |
429 | * bother restricting? | |
430 | */ | |
431 | if(Param.Data) { | |
432 | crtn = p12PbeGen(pwd, | |
433 | salt, saltLen, | |
434 | iterCount, | |
435 | PBE_ID_IV, | |
436 | CSSM_ALGID_SHA1, // all we support for now | |
437 | tmpCoder, | |
438 | Param.Data, | |
427c49bc | 439 | (unsigned)Param.Length); |
b1ab9ed8 A |
440 | if(crtn) { |
441 | CssmError::throwMe(crtn); | |
442 | } | |
443 | } | |
444 | } | |
445 |