]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2003-2004,2011-2014 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 | /* | |
25 | * pkcs12Utils.cpp | |
26 | */ | |
27 | ||
28 | #include "pkcs12Utils.h" | |
29 | #include <string.h> | |
30 | #include "pkcs7Templates.h" | |
31 | #include "pkcs12Templates.h" | |
32 | #include "pkcs12Crypto.h" | |
33 | #include "pkcs12Debug.h" | |
34 | #include <security_asn1/nssUtils.h> | |
35 | #include <Security/secasn1t.h> | |
36 | #include <security_utilities/errors.h> | |
37 | #include <security_cdsa_utils/cuCdsaUtils.h> | |
38 | #include <Security/oidsattr.h> | |
39 | #include <Security/oidsalg.h> | |
40 | #include <Security/cssmapple.h> | |
41 | ||
42 | /* malloc a NULL-ed array of pointers of size num+1 */ | |
43 | void **p12NssNullArray( | |
44 | uint32 num, | |
45 | SecNssCoder &coder) | |
46 | { | |
47 | unsigned len = (num + 1) * sizeof(void *); | |
48 | void **p = (void **)coder.malloc(len); | |
49 | memset(p, 0, len); | |
50 | return p; | |
51 | } | |
52 | ||
53 | /* CSSM_DATA --> uint32. Returns true if OK. */ | |
54 | bool p12DataToInt( | |
55 | const CSSM_DATA &cdata, | |
56 | uint32 &u) | |
57 | { | |
58 | if((cdata.Length == 0) || (cdata.Data == NULL)) { | |
59 | /* default/not present */ | |
60 | u = 0; | |
61 | return true; | |
62 | } | |
63 | CSSM_SIZE len = cdata.Length; | |
64 | if(len > sizeof(uint32)) { | |
65 | return false; | |
66 | } | |
67 | ||
68 | uint32 rtn = 0; | |
69 | uint8 *cp = cdata.Data; | |
70 | for(uint32 i=0; i<len; i++) { | |
71 | rtn = (rtn << 8) | *cp++; | |
72 | } | |
73 | u = rtn; | |
74 | return true; | |
75 | } | |
76 | ||
77 | /* uint32 --> CSSM_DATA */ | |
78 | void p12IntToData( | |
79 | uint32 num, | |
80 | CSSM_DATA &cdata, | |
81 | SecNssCoder &coder) | |
82 | { | |
83 | uint32 len = 0; | |
84 | ||
85 | if(num < 0x100) { | |
86 | len = 1; | |
87 | } | |
88 | else if(num < 0x10000) { | |
89 | len = 2; | |
90 | } | |
91 | else if(num < 0x1000000) { | |
92 | len = 3; | |
93 | } | |
94 | else { | |
95 | len = 4; | |
96 | } | |
97 | coder.allocItem(cdata, len); | |
98 | uint8 *cp = &cdata.Data[len - 1]; | |
99 | for(unsigned i=0; i<len; i++) { | |
100 | *cp-- = num & 0xff; | |
101 | num >>= 8; | |
102 | } | |
103 | } | |
104 | ||
105 | /* CFDataRef <--> CSSM_DATA */ | |
106 | CFDataRef CF_RETURNS_RETAINED p12CssmDataToCf( | |
107 | const CSSM_DATA &c) | |
108 | { | |
109 | return CFDataCreate(NULL, c.Data, c.Length); | |
110 | } | |
111 | ||
112 | void p12CfDataToCssm( | |
113 | CFDataRef cf, | |
114 | CSSM_DATA &c, | |
115 | SecNssCoder &coder) | |
116 | { | |
117 | coder.allocCopyItem(CFDataGetBytePtr(cf), | |
118 | CFDataGetLength(cf), c); | |
119 | } | |
120 | ||
121 | /* | |
122 | * Attempt to convert a CFStringRef, which represents a SafeBag's | |
123 | * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its | |
124 | * referent are allocated in the specified SecNssCoder's memory. | |
125 | * No guarantee that this conversion works. If it doesn't we return | |
126 | * NULL and caller must be prepared to deal with that. | |
127 | */ | |
128 | CSSM_DATA_PTR p12StringToUtf8( | |
129 | CFStringRef cfStr, | |
130 | SecNssCoder &coder) | |
131 | { | |
132 | if(cfStr == NULL) { | |
133 | return NULL; | |
134 | } | |
135 | ||
136 | CFIndex strLen = 0; | |
137 | CFRange range = { 0, CFStringGetLength(cfStr) }; | |
138 | CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, &strLen); | |
139 | if(strLen == 0) { | |
140 | return NULL; | |
141 | } | |
142 | ||
143 | CSSM_DATA_PTR rtn = coder.mallocn<CSSM_DATA>(); | |
144 | coder.allocItem(*rtn, strLen); | |
145 | ||
146 | if(!CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8*)rtn->Data, strLen, &strLen)) { | |
147 | /* not convertible from native Unicode to UTF8 */ | |
148 | return NULL; | |
149 | } | |
150 | return rtn; | |
151 | } | |
152 | ||
153 | /* | |
154 | * Enum to string mappper. | |
155 | * Maybe DEBUG only. | |
156 | */ | |
157 | /* | |
158 | * Each type of attribute has a name/value pair in a table of these: | |
159 | */ | |
160 | typedef struct { | |
161 | unsigned value; | |
162 | const char *name; | |
163 | } p12NameValuePair; | |
164 | ||
165 | /* declare one entry in a table of p12NameValuePair */ | |
166 | #define NVP(attr) {attr, #attr} | |
167 | ||
168 | /* the NULL entry which terminates all p12NameValuePair tables */ | |
169 | #define NVP_END {0, NULL} | |
170 | ||
171 | static const p12NameValuePair p7CITypeNames[] = | |
172 | { | |
173 | NVP(CT_None), | |
174 | NVP(CT_Data), | |
175 | NVP(CT_SignedData), | |
176 | NVP(CT_EnvData), | |
177 | NVP(CT_SignedEnvData), | |
178 | NVP(CT_DigestData), | |
179 | NVP(CT_EncryptedData), | |
180 | NVP_END | |
181 | }; | |
182 | ||
183 | static const p12NameValuePair p12BagTypeNames[] = | |
184 | { | |
185 | NVP(BT_None), | |
186 | NVP(BT_KeyBag), | |
187 | NVP(BT_ShroudedKeyBag), | |
188 | NVP(BT_CertBag), | |
189 | NVP(BT_CrlBag), | |
190 | NVP(BT_SecretBag), | |
191 | NVP(BT_SafeContentsBag), | |
192 | NVP_END | |
193 | }; | |
194 | ||
195 | static const char *typeToStr( | |
196 | unsigned type, | |
197 | const p12NameValuePair *table) | |
198 | { | |
199 | while(table->name) { | |
200 | if(table->value == type) { | |
201 | return table->name; | |
202 | } | |
203 | table++; | |
204 | } | |
205 | return "Unknown"; | |
206 | } | |
207 | ||
208 | const char *p12BagTypeStr( | |
209 | NSS_P12_SB_Type type) | |
210 | { | |
211 | return typeToStr(type, p12BagTypeNames); | |
212 | } | |
213 | ||
214 | const char *p7ContentInfoTypeStr( | |
215 | NSS_P7_CI_Type type) | |
216 | { | |
217 | return typeToStr(type, p7CITypeNames); | |
218 | } | |
219 | ||
220 | /* | |
221 | * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following | |
222 | * attributes. | |
223 | */ | |
224 | typedef struct { | |
225 | const CSSM_OID *oid; | |
226 | CSSM_ALGORITHMS keyAlg; // e.g., CSSM_ALGID_DES | |
227 | CSSM_ALGORITHMS encrAlg; // e.g., CSSM_ALGID_3DES_3KEY_EDE | |
228 | CSSM_ALGORITHMS pbeHashAlg; // SHA1 or MD5 | |
229 | uint32 keySizeInBits; | |
230 | uint32 blockSizeInBytes; // for IV, optional | |
231 | CSSM_PADDING padding; // CSSM_PADDING_PKCS7, etc. | |
232 | CSSM_ENCRYPT_MODE mode; // CSSM_ALGMODE_CBCPadIV8, etc. | |
233 | PKCS_Which pkcs; // PW_PKCS12 (for this module) or PW_PKCS5_v1_5 | |
234 | } PKCSOidInfo; | |
235 | ||
236 | static const PKCSOidInfo pkcsOidInfos[] = { | |
237 | /* PKCS12 first, the ones this module uses */ | |
238 | { | |
239 | &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4, | |
240 | CSSM_ALGID_RC4, | |
241 | CSSM_ALGID_RC4, | |
242 | CSSM_ALGID_SHA1, | |
243 | 128, | |
244 | 0, // RC4 is a stream cipher | |
245 | CSSM_PADDING_NONE, | |
246 | CSSM_ALGMODE_NONE, | |
247 | PW_PKCS12 | |
248 | }, | |
249 | { | |
250 | &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4, | |
251 | CSSM_ALGID_RC4, | |
252 | CSSM_ALGID_RC4, | |
253 | CSSM_ALGID_SHA1, | |
254 | 40, | |
255 | 0, // RC4 is a stream cipher | |
256 | CSSM_PADDING_NONE, | |
257 | CSSM_ALGMODE_NONE, | |
258 | PW_PKCS12 | |
259 | }, | |
260 | { | |
261 | &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC, | |
262 | CSSM_ALGID_3DES_3KEY, | |
263 | CSSM_ALGID_3DES_3KEY_EDE, | |
264 | CSSM_ALGID_SHA1, | |
265 | 64 * 3, | |
266 | 8, | |
267 | CSSM_PADDING_PKCS7, | |
268 | CSSM_ALGMODE_CBCPadIV8, | |
269 | PW_PKCS12 | |
270 | }, | |
271 | { | |
272 | &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC, | |
273 | CSSM_ALGID_3DES_2KEY, | |
274 | CSSM_ALGID_3DES_2KEY_EDE, | |
275 | CSSM_ALGID_SHA1, | |
276 | 64 * 2, | |
277 | 8, | |
278 | CSSM_PADDING_PKCS7, | |
279 | CSSM_ALGMODE_CBCPadIV8, | |
280 | PW_PKCS12 | |
281 | }, | |
282 | { | |
283 | &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC, | |
284 | CSSM_ALGID_RC2, | |
285 | CSSM_ALGID_RC2, | |
286 | CSSM_ALGID_SHA1, | |
287 | 128, | |
288 | 8, | |
289 | CSSM_PADDING_PKCS7, | |
290 | CSSM_ALGMODE_CBCPadIV8, | |
291 | PW_PKCS12 | |
292 | }, | |
293 | { | |
294 | &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC, | |
295 | CSSM_ALGID_RC2, | |
296 | CSSM_ALGID_RC2, | |
297 | CSSM_ALGID_SHA1, | |
298 | 40, | |
299 | 8, | |
300 | CSSM_PADDING_PKCS7, | |
301 | CSSM_ALGMODE_CBCPadIV8, | |
302 | PW_PKCS12 | |
303 | }, | |
304 | ||
305 | /* PKCS5 v1.5, used for SecImportExport module */ | |
306 | { | |
307 | &CSSMOID_PKCS5_pbeWithMD2AndDES, | |
308 | CSSM_ALGID_DES, | |
309 | CSSM_ALGID_DES, | |
310 | CSSM_ALGID_MD2, | |
311 | 64, | |
312 | 8, | |
313 | CSSM_PADDING_PKCS7, | |
314 | CSSM_ALGMODE_CBCPadIV8, | |
315 | PW_PKCS5_v1_5 | |
316 | }, | |
317 | { | |
318 | &CSSMOID_PKCS5_pbeWithMD2AndRC2, | |
319 | CSSM_ALGID_RC2, | |
320 | CSSM_ALGID_RC2, | |
321 | CSSM_ALGID_MD2, | |
322 | 64, | |
323 | 8, | |
324 | CSSM_PADDING_PKCS7, | |
325 | CSSM_ALGMODE_CBCPadIV8, | |
326 | PW_PKCS5_v1_5 | |
327 | }, | |
328 | { | |
329 | &CSSMOID_PKCS5_pbeWithMD5AndDES, | |
330 | CSSM_ALGID_DES, | |
331 | CSSM_ALGID_DES, | |
332 | CSSM_ALGID_MD5, | |
333 | 64, | |
334 | 8, | |
335 | CSSM_PADDING_PKCS7, | |
336 | CSSM_ALGMODE_CBCPadIV8, | |
337 | PW_PKCS5_v1_5 | |
338 | }, | |
339 | { | |
340 | &CSSMOID_PKCS5_pbeWithMD5AndRC2, | |
341 | CSSM_ALGID_RC2, | |
342 | CSSM_ALGID_RC2, | |
343 | CSSM_ALGID_MD5, | |
344 | 64, | |
345 | 8, | |
346 | CSSM_PADDING_PKCS7, | |
347 | CSSM_ALGMODE_CBCPadIV8, | |
348 | PW_PKCS5_v1_5 | |
349 | }, | |
350 | { | |
351 | &CSSMOID_PKCS5_pbeWithSHA1AndDES, | |
352 | CSSM_ALGID_DES, | |
353 | CSSM_ALGID_DES, | |
354 | CSSM_ALGID_SHA1, | |
355 | 64, | |
356 | 8, | |
357 | CSSM_PADDING_PKCS7, | |
358 | CSSM_ALGMODE_CBCPadIV8, | |
359 | PW_PKCS5_v1_5 | |
360 | }, | |
361 | { | |
362 | &CSSMOID_PKCS5_pbeWithSHA1AndRC2, | |
363 | CSSM_ALGID_RC2, | |
364 | CSSM_ALGID_RC2, | |
365 | CSSM_ALGID_SHA1, | |
366 | 64, | |
367 | 8, | |
368 | CSSM_PADDING_PKCS7, | |
369 | CSSM_ALGMODE_CBCPadIV8, | |
370 | PW_PKCS5_v1_5 | |
371 | }, | |
372 | ||
373 | /* finally one for PKCS5 v2.0, which has its own means of | |
374 | * cooking up all the parameters */ | |
375 | { | |
376 | &CSSMOID_PKCS5_PBES2, | |
377 | CSSM_ALGID_NONE, | |
378 | CSSM_ALGID_NONE, | |
379 | CSSM_ALGID_NONE, | |
380 | 0, 0, 0, 0, | |
381 | PW_PKCS5_v2 | |
382 | } | |
383 | }; | |
384 | ||
385 | #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1])) | |
386 | ||
387 | /* map an OID to the components */ | |
388 | /* returns false if OID not found */ | |
389 | ||
390 | /* | |
391 | * NOTE: as of March 8 2004 this is also used by the SecImportExport | |
392 | * module...not just PKCS12! | |
393 | */ | |
394 | bool pkcsOidToParams( | |
395 | const CSSM_OID *oid, | |
396 | CSSM_ALGORITHMS &keyAlg, // e.g., CSSM_ALGID_DES | |
397 | CSSM_ALGORITHMS &encrAlg, // e.g., CSSM_ALGID_3DES_3KEY_EDE | |
398 | CSSM_ALGORITHMS &pbeHashAlg, // SHA1 or MD5 | |
399 | uint32 &keySizeInBits, | |
400 | uint32 &blockSizeInBytes, // for IV, optional | |
401 | CSSM_PADDING &padding, // CSSM_PADDING_PKCS7, etc. | |
402 | CSSM_ENCRYPT_MODE &mode, // CSSM_ALGMODE_CBCPadIV8, etc. | |
403 | PKCS_Which &pkcs) // PW_PKCS5_v1_5 or PW_PKCS12 | |
404 | { | |
405 | const PKCSOidInfo *info = pkcsOidInfos; | |
406 | pkcs = PW_None; | |
407 | ||
408 | for(unsigned dex=0; dex<NUM_PKCS_OID_INFOS; dex++) { | |
409 | if(nssCompareCssmData(oid, info->oid)) { | |
410 | keyAlg = info->keyAlg; | |
411 | encrAlg = info->encrAlg; | |
412 | pbeHashAlg = info->pbeHashAlg; | |
413 | keySizeInBits = info->keySizeInBits; | |
414 | blockSizeInBytes = info->blockSizeInBytes; | |
415 | padding = info->padding; | |
416 | mode = info->mode; | |
417 | pkcs = info->pkcs; | |
418 | return true; | |
419 | } | |
420 | info++; | |
421 | } | |
422 | return false; | |
423 | } | |
424 | ||
425 | /* | |
426 | * Verify MAC on an existing PFX. | |
427 | */ | |
428 | CSSM_RETURN p12VerifyMac( | |
429 | const NSS_P12_DecodedPFX &pfx, | |
430 | CSSM_CSP_HANDLE cspHand, | |
431 | const CSSM_DATA *pwd, // unicode, double null terminated | |
432 | const CSSM_KEY *passKey, | |
433 | SecNssCoder &coder) // for temp mallocs | |
434 | { | |
435 | if(pfx.macData == NULL) { | |
436 | return CSSMERR_CSP_INVALID_SIGNATURE; | |
437 | } | |
438 | NSS_P12_MacData &macData = *pfx.macData; | |
439 | NSS_P7_DigestInfo &digestInfo = macData.mac; | |
440 | CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm; | |
441 | CSSM_ALGORITHMS macAlg; | |
442 | if(!cssmOidToAlg(&algOid, &macAlg)) { | |
443 | return CSSMERR_CSP_INVALID_ALGORITHM; | |
444 | } | |
445 | uint32 iterCount = 0; | |
446 | CSSM_DATA &citer = macData.iterations; | |
447 | if(!p12DataToInt(citer, iterCount)) { | |
448 | return CSSMERR_CSP_INVALID_ATTR_ROUNDS; | |
449 | } | |
450 | if(iterCount == 0) { | |
451 | /* optional, default 1 */ | |
452 | iterCount = 1; | |
453 | } | |
454 | ||
455 | /* | |
456 | * In classic fashion, the PKCS12 spec now says: | |
457 | * | |
458 | * When password integrity mode is used to secure a PFX PDU, | |
459 | * an SHA-1 HMAC is computed on the BER-encoding of the contents | |
460 | * of the content field of the authSafe field in the PFX PDU. | |
461 | * | |
462 | * So here we go. | |
463 | */ | |
464 | CSSM_DATA genMac; | |
465 | CSSM_RETURN crtn = p12GenMac(cspHand, *pfx.authSafe.content.data, | |
466 | macAlg, iterCount, macData.macSalt, pwd, passKey, coder, genMac); | |
467 | if(crtn) { | |
468 | return crtn; | |
469 | } | |
470 | if(nssCompareCssmData(&genMac, &digestInfo.digest)) { | |
471 | return CSSM_OK; | |
472 | } | |
473 | else { | |
474 | return CSSMERR_CSP_VERIFY_FAILED; | |
475 | } | |
476 | } | |
477 | ||
478 | /* we generate 8 random bytes of salt */ | |
479 | #define P12_SALT_LEN 8 | |
480 | ||
481 | void p12GenSalt( | |
482 | CSSM_DATA &salt, | |
483 | SecNssCoder &coder) | |
484 | { | |
485 | coder.allocItem(salt, P12_SALT_LEN); | |
486 | MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, P12_SALT_LEN, salt.Data)); | |
487 | } | |
488 | ||
489 | /* | |
490 | * Generate random label string to allow associating an imported private | |
491 | * key with a cert. | |
492 | */ | |
493 | void p12GenLabel( | |
494 | CSSM_DATA &label, | |
495 | SecNssCoder &coder) | |
496 | { | |
497 | /* first a random uint32 */ | |
498 | uint8 d[4]; | |
499 | MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, 4, d)); | |
500 | CSSM_DATA cd = {4, d}; | |
501 | uint32 i; | |
502 | p12DataToInt(cd, i); | |
503 | ||
504 | /* sprintf that into a real string */ | |
505 | coder.allocItem(label, 9); | |
506 | memset(label.Data, 0, 9); | |
507 | sprintf((char *)label.Data, "%08X", (unsigned)i); | |
508 | } | |
509 | ||
510 | /* NULL algorithm parameters */ | |
511 | ||
512 | static const uint8 nullAlg[2] = {SEC_ASN1_NULL, 0}; | |
513 | ||
514 | void p12NullAlgParams( | |
515 | CSSM_X509_ALGORITHM_IDENTIFIER &algId) | |
516 | { | |
517 | CSSM_DATA &p = algId.parameters; | |
518 | p.Data = (uint8 *)nullAlg; | |
519 | p.Length = 2; | |
520 | } | |
521 | ||
522 | /* | |
523 | * Free memory via specified plugin's app-level allocator | |
524 | */ | |
525 | void freeCssmMemory( | |
526 | CSSM_HANDLE hand, | |
527 | void *p) | |
528 | { | |
529 | CSSM_API_MEMORY_FUNCS memFuncs; | |
530 | CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); | |
531 | if(crtn) { | |
532 | p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn); | |
533 | /* oh well, leak and continue */ | |
534 | return; | |
535 | } | |
536 | memFuncs.free_func(p, memFuncs.AllocRef); | |
537 | } | |
538 | ||
539 | /* | |
540 | * Find private key by label, modify its Label attr to be the | |
541 | * hash of the associated public key. | |
542 | * Also optionally re-sets the key's PrintName attribute; used to reset | |
543 | * this attr from the random label we create when first unwrap it | |
544 | * to the friendly name we find later after parsing attributes. | |
545 | * Detection of a duplicate key when updating the key's attributes | |
546 | * results in a lookup of the original key and returning it in | |
547 | * foundKey. | |
548 | */ | |
549 | CSSM_RETURN p12SetPubKeyHash( | |
550 | CSSM_CSP_HANDLE cspHand, // where the key lives | |
551 | CSSM_DL_DB_HANDLE dlDbHand, // ditto | |
552 | CSSM_DATA &keyLabel, // for DB lookup | |
553 | CSSM_DATA_PTR newPrintName, // optional | |
554 | SecNssCoder &coder, // for mallocing newLabel | |
555 | CSSM_DATA &newLabel, // RETURNED with label as hash | |
556 | CSSM_KEY_PTR &foundKey) // RETURNED | |
557 | { | |
558 | CSSM_QUERY query; | |
559 | CSSM_SELECTION_PREDICATE predicate; | |
560 | CSSM_DB_UNIQUE_RECORD_PTR record = NULL; | |
561 | CSSM_RETURN crtn; | |
562 | CSSM_HANDLE resultHand = 0; | |
563 | CSSM_DATA keyData = {0, NULL}; | |
564 | CSSM_CC_HANDLE ccHand = 0; | |
565 | CSSM_KEY_PTR privKey = NULL; | |
566 | CSSM_DATA_PTR keyDigest = NULL; | |
567 | ||
568 | assert(cspHand != 0); | |
569 | query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; | |
570 | query.Conjunctive = CSSM_DB_NONE; | |
571 | query.NumSelectionPredicates = 1; | |
572 | predicate.DbOperator = CSSM_DB_EQUAL; | |
573 | ||
574 | predicate.Attribute.Info.AttributeNameFormat = | |
575 | CSSM_DB_ATTRIBUTE_NAME_AS_STRING; | |
576 | predicate.Attribute.Info.Label.AttributeName = | |
577 | (char*) P12_KEY_ATTR_LABEL_AND_HASH; | |
578 | predicate.Attribute.Info.AttributeFormat = | |
579 | CSSM_DB_ATTRIBUTE_FORMAT_BLOB; | |
580 | /* hope this cast is OK */ | |
581 | predicate.Attribute.Value = &keyLabel; | |
582 | query.SelectionPredicate = &predicate; | |
583 | ||
584 | query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? | |
585 | query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? | |
586 | query.QueryFlags = CSSM_QUERY_RETURN_DATA; | |
587 | ||
588 | /* build Record attribute with one or two attrs */ | |
589 | CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; | |
590 | CSSM_DB_ATTRIBUTE_DATA attr[2]; | |
591 | attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; | |
592 | attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH; | |
593 | attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; | |
594 | if(newPrintName) { | |
595 | attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; | |
596 | attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME; | |
597 | attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; | |
598 | } | |
599 | recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; | |
600 | recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1; | |
601 | recordAttrs.AttributeData = attr; | |
602 | ||
603 | crtn = CSSM_DL_DataGetFirst(dlDbHand, | |
604 | &query, | |
605 | &resultHand, | |
606 | &recordAttrs, | |
607 | &keyData, // theData | |
608 | &record); | |
609 | /* abort only on success */ | |
610 | if(crtn != CSSM_OK) { | |
611 | p12LogCssmError("CSSM_DL_DataGetFirst", crtn); | |
612 | p12ErrorLog("***p12SetPubKeyHash: can't find private key\n"); | |
613 | return crtn; | |
614 | } | |
615 | /* subsequent errors to errOut: */ | |
616 | if(keyData.Data == NULL) { | |
617 | p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n"); | |
618 | crtn = CSSMERR_CSSM_INTERNAL_ERROR; | |
619 | goto errOut; | |
620 | } | |
621 | privKey = (CSSM_KEY_PTR)keyData.Data; | |
622 | ||
623 | /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */ | |
624 | /* | |
625 | * Warning! This relies on the current default ACL meaning "allow this | |
626 | * current app to access this private key" since we created the key. | |
627 | */ | |
628 | crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand); | |
629 | if(crtn) { | |
630 | p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn); | |
631 | goto errOut; | |
632 | } | |
633 | crtn = CSSM_CSP_PassThrough(ccHand, | |
634 | CSSM_APPLECSP_KEYDIGEST, | |
635 | NULL, | |
636 | (void **)&keyDigest); | |
637 | if(crtn) { | |
638 | p12LogCssmError("CSSM_CSP_PassThrough", crtn); | |
639 | goto errOut; | |
640 | } | |
641 | ||
642 | /* | |
643 | * Replace Label attr data with hash. | |
644 | * NOTE: the module which allocated this attribute data - a DL - | |
645 | * was loaded and attached by out client layer, not by us. Thus | |
646 | * we can't use the memory allocator functions *we* used when | |
647 | * attaching to the CSP - we have to use the ones | |
648 | * which the client registered with the DL. | |
649 | */ | |
650 | freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data); | |
651 | freeCssmMemory(dlDbHand.DLHandle, attr[0].Value); | |
652 | if(newPrintName) { | |
653 | freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data); | |
654 | freeCssmMemory(dlDbHand.DLHandle, attr[1].Value); | |
655 | } | |
656 | /* modify key attributes */ | |
657 | attr[0].Value = keyDigest; | |
658 | if(newPrintName) { | |
659 | attr[1].Value = newPrintName; | |
660 | } | |
661 | crtn = CSSM_DL_DataModify(dlDbHand, | |
662 | CSSM_DL_DB_RECORD_PRIVATE_KEY, | |
663 | record, | |
664 | &recordAttrs, | |
665 | NULL, // DataToBeModified | |
666 | CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); | |
667 | switch(crtn) { | |
668 | case CSSM_OK: | |
669 | /* give caller the key's new label */ | |
670 | coder.allocCopyItem(*keyDigest, newLabel); | |
671 | break; | |
672 | default: | |
673 | p12LogCssmError("CSSM_DL_DataModify", crtn); | |
674 | break; | |
675 | case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: | |
676 | { | |
677 | /* | |
678 | * Special case: dup private key. The label we just tried to modify is | |
679 | * the public key hash so we can be confident that this really is a dup. | |
680 | * Delete it, look up the original, and return the original to caller. | |
681 | */ | |
682 | CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record); | |
683 | if(drtn) { | |
684 | p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn); | |
685 | crtn = drtn; | |
686 | break; | |
687 | } | |
688 | ||
689 | /* Free items created in last search */ | |
690 | CSSM_DL_DataAbortQuery(dlDbHand, resultHand); | |
691 | resultHand = 0; | |
692 | CSSM_DL_FreeUniqueRecord(dlDbHand, record); | |
693 | record = NULL; | |
694 | ||
695 | /* lookup by label as public key hash this time */ | |
696 | predicate.Attribute.Value = keyDigest; | |
697 | drtn = CSSM_DL_DataGetFirst(dlDbHand, | |
698 | &query, | |
699 | &resultHand, | |
700 | NULL, // no attrs this time | |
701 | &keyData, | |
702 | &record); | |
703 | if(drtn) { | |
704 | p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn); | |
705 | crtn = drtn; | |
706 | break; | |
707 | } | |
708 | foundKey = (CSSM_KEY_PTR)keyData.Data; | |
709 | /* give caller the key's actual label */ | |
710 | coder.allocCopyItem(*keyDigest, newLabel); | |
711 | break; | |
712 | } | |
713 | } | |
714 | ||
715 | errOut: | |
716 | /* free resources */ | |
717 | if(resultHand) { | |
718 | CSSM_DL_DataAbortQuery(dlDbHand, resultHand); | |
719 | } | |
720 | if(record) { | |
721 | CSSM_DL_FreeUniqueRecord(dlDbHand, record); | |
722 | } | |
723 | if(ccHand) { | |
724 | CSSM_DeleteContext(ccHand); | |
725 | } | |
726 | if(privKey) { | |
727 | /* key created by the CSPDL */ | |
728 | CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE); | |
729 | freeCssmMemory(dlDbHand.DLHandle, privKey); | |
730 | } | |
731 | if(keyDigest) { | |
732 | /* mallocd by someone else's CSP */ | |
733 | freeCssmMemory(cspHand, keyDigest->Data); | |
734 | freeCssmMemory(cspHand, keyDigest); | |
735 | } | |
736 | return crtn; | |
737 | } | |
738 | ||
739 | /* | |
740 | * Given a context specified via a CSSM_CC_HANDLE, add a new | |
741 | * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, | |
742 | * AttributeLength, and an untyped pointer. | |
743 | */ | |
744 | CSSM_RETURN p12AddContextAttribute(CSSM_CC_HANDLE CCHandle, | |
745 | uint32 AttributeType, | |
746 | uint32 AttributeLength, | |
747 | const void *AttributePtr) | |
748 | { | |
749 | CSSM_CONTEXT_ATTRIBUTE newAttr; | |
750 | CSSM_RETURN crtn; | |
751 | ||
752 | newAttr.AttributeType = AttributeType; | |
753 | newAttr.AttributeLength = AttributeLength; | |
754 | newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; | |
755 | crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); | |
756 | if(crtn) { | |
757 | p12LogCssmError("CSSM_UpdateContextAttributes", crtn); | |
758 | } | |
759 | return crtn; | |
760 | } | |
761 | ||
762 | /* | |
763 | * Find private key by specified label, delete it. | |
764 | */ | |
765 | CSSM_RETURN p12DeleteKey( | |
766 | CSSM_DL_DB_HANDLE dlDbHand, | |
767 | const CSSM_DATA &keyLabel) | |
768 | { | |
769 | CSSM_QUERY query; | |
770 | CSSM_SELECTION_PREDICATE predicate; | |
771 | CSSM_DB_UNIQUE_RECORD_PTR record = NULL; | |
772 | CSSM_RETURN crtn; | |
773 | CSSM_HANDLE resultHand = 0; | |
774 | ||
775 | query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; | |
776 | query.Conjunctive = CSSM_DB_NONE; | |
777 | query.NumSelectionPredicates = 1; | |
778 | predicate.DbOperator = CSSM_DB_EQUAL; | |
779 | ||
780 | predicate.Attribute.Info.AttributeNameFormat = | |
781 | CSSM_DB_ATTRIBUTE_NAME_AS_STRING; | |
782 | predicate.Attribute.Info.Label.AttributeName = | |
783 | (char*) P12_KEY_ATTR_LABEL_AND_HASH; | |
784 | predicate.Attribute.Info.AttributeFormat = | |
785 | CSSM_DB_ATTRIBUTE_FORMAT_BLOB; | |
786 | predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel); | |
787 | ||
788 | query.SelectionPredicate = &predicate; | |
789 | query.QueryLimits.TimeLimit = 0; | |
790 | query.QueryLimits.SizeLimit = 1; | |
791 | query.QueryFlags = 0; | |
792 | ||
793 | crtn = CSSM_DL_DataGetFirst(dlDbHand, | |
794 | &query, | |
795 | &resultHand, | |
796 | NULL, // attrs - don't need 'em | |
797 | NULL, // theData - don't need it | |
798 | &record); | |
799 | /* abort only on success */ | |
800 | if(crtn) { | |
801 | p12LogCssmError("CSSM_DL_DataGetFirst", crtn); | |
802 | p12ErrorLog("***p12DeleteKey: can't find private key\n"); | |
803 | return crtn; | |
804 | } | |
805 | ||
806 | crtn = CSSM_DL_DataDelete(dlDbHand, record); | |
807 | if(crtn) { | |
808 | p12LogCssmError("CSSM_DL_DataDelete", crtn); | |
809 | p12ErrorLog("***p12DeleteKey: can't delete private key\n"); | |
810 | } | |
811 | ||
812 | CSSM_DL_DataAbortQuery(dlDbHand, resultHand); | |
813 | CSSM_DL_FreeUniqueRecord(dlDbHand, record); | |
814 | return crtn; | |
815 | } | |
816 | ||
817 | /* convert App passphrase to array of chars used in P12 PBE */ | |
818 | void p12ImportPassPhrase( | |
819 | CFStringRef inPhrase, | |
820 | SecNssCoder &coder, | |
821 | CSSM_DATA &outPhrase) | |
822 | { | |
823 | CFDataRef cfData = CFStringCreateExternalRepresentation(NULL, | |
824 | inPhrase, kCFStringEncodingUTF8, 0); | |
825 | if(cfData == NULL) { | |
826 | p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n"); | |
827 | MacOSError::throwMe(errSecParam); | |
828 | } | |
829 | CFIndex keyLen = CFDataGetLength(cfData); | |
830 | coder.allocItem(outPhrase, keyLen); | |
831 | memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen); | |
832 | CFRelease(cfData); | |
833 | } |