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