]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/SecImportExportUtils.cpp
Security-55179.13.tar.gz
[apple/security.git] / libsecurity_keychain / lib / SecImportExportUtils.cpp
1 /*
2 * Copyright (c) 2004 Apple Computer, 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 * SecImportExportUtils.cpp - misc. utilities for import/export module
24 */
25
26 #include "SecImportExportUtils.h"
27 #include "SecImportExportAgg.h"
28 #include "SecImportExportCrypto.h"
29 #include "SecIdentityPriv.h"
30 #include "SecItem.h"
31 #include <security_cdsa_utils/cuCdsaUtils.h>
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
33
34 #pragma mark --- Debug support ---
35
36 #ifndef NDEBUG
37
38 const char *impExpExtFormatStr(
39 SecExternalFormat format)
40 {
41 switch(format) {
42 case kSecFormatUnknown: return "kSecFormatUnknown";
43 case kSecFormatOpenSSL: return "kSecFormatOpenSSL";
44 case kSecFormatSSH: return "kSecFormatSSH";
45 case kSecFormatBSAFE: return "kSecFormatBSAFE";
46 case kSecFormatRawKey: return "kSecFormatRawKey";
47 case kSecFormatWrappedPKCS8: return "kSecFormatWrappedPKCS8";
48 case kSecFormatWrappedOpenSSL: return "kSecFormatWrappedOpenSSL";
49 case kSecFormatWrappedSSH: return "kSecFormatWrappedSSH";
50 case kSecFormatWrappedLSH: return "kSecFormatWrappedLSH";
51 case kSecFormatX509Cert: return "kSecFormatX509Cert";
52 case kSecFormatPEMSequence: return "kSecFormatPEMSequence";
53 case kSecFormatPKCS7: return "kSecFormatPKCS7";
54 case kSecFormatPKCS12: return "kSecFormatPKCS12";
55 case kSecFormatNetscapeCertSequence: return "kSecFormatNetscapeCertSequence";
56 default: return "UNKNOWN FORMAT ENUM";
57 }
58 }
59
60 const char *impExpExtItemTypeStr(
61 SecExternalItemType itemType)
62 {
63 switch(itemType) {
64 case kSecItemTypeUnknown: return "kSecItemTypeUnknown";
65 case kSecItemTypePrivateKey: return "kSecItemTypePrivateKey";
66 case kSecItemTypePublicKey: return "kSecItemTypePublicKey";
67 case kSecItemTypeSessionKey: return "kSecItemTypeSessionKey";
68 case kSecItemTypeCertificate: return "kSecItemTypeCertificate";
69 case kSecItemTypeAggregate: return "kSecItemTypeAggregate";
70 default: return "UNKNOWN ITEM TYPE ENUM";
71 }
72 }
73 #endif /* NDEBUG */
74
75 /*
76 * Parse file extension and attempt to map it to format and type. Returns true
77 * on success.
78 */
79 bool impExpImportParseFileExten(
80 CFStringRef fstr,
81 SecExternalFormat *inputFormat, // RETURNED
82 SecExternalItemType *itemType) // RETURNED
83 {
84 if(fstr == NULL) {
85 /* nothing to work with */
86 return false;
87 }
88 if(CFStringHasSuffix(fstr, CFSTR(".cer")) ||
89 CFStringHasSuffix(fstr, CFSTR(".crt"))) {
90 *inputFormat = kSecFormatX509Cert;
91 *itemType = kSecItemTypeCertificate;
92 SecImpInferDbg("Inferring kSecFormatX509Cert from file name");
93 return true;
94 }
95 if(CFStringHasSuffix(fstr, CFSTR(".p12")) ||
96 CFStringHasSuffix(fstr, CFSTR(".pfx"))) {
97 *inputFormat = kSecFormatPKCS12;
98 *itemType = kSecItemTypeAggregate;
99 SecImpInferDbg("Inferring kSecFormatPKCS12 from file name");
100 return true;
101 }
102
103 /* Get extension, look for key indicators as substrings */
104 CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL);
105 if(url == NULL) {
106 SecImpInferDbg("impExpImportParseFileExten: error creating URL");
107 return false;
108 }
109 CFStringRef exten = CFURLCopyPathExtension(url);
110 CFRelease(url);
111 if(exten == NULL) {
112 /* no extension, app probably passed in only an extension */
113 exten = fstr;
114 CFRetain(exten);
115 }
116 bool ortn = false;
117 CFRange cfr;
118 cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive);
119 if(cfr.length != 0) {
120 *inputFormat = kSecFormatPKCS7;
121 *itemType = kSecItemTypeAggregate;
122 SecImpInferDbg("Inferring kSecFormatPKCS7 from file name");
123 ortn = true;
124 }
125 if(!ortn) {
126 cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive);
127 if(cfr.length != 0) {
128 *inputFormat = kSecFormatWrappedPKCS8;
129 *itemType = kSecItemTypePrivateKey;
130 SecImpInferDbg("Inferring kSecFormatPKCS8 from file name");
131 ortn = true;
132 }
133 }
134 CFRelease(exten);
135 return ortn;
136 }
137
138 /* do a [NSString stringByDeletingPathExtension] equivalent */
139 CFStringRef impExpImportDeleteExtension(
140 CFStringRef fileStr)
141 {
142 CFDataRef fileStrData = CFStringCreateExternalRepresentation(NULL, fileStr,
143 kCFStringEncodingUTF8, 0);
144 if(fileStrData == NULL) {
145 return NULL;
146 }
147
148 CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL,
149 CFDataGetBytePtr(fileStrData), CFDataGetLength(fileStrData), false);
150 if(urlRef == NULL) {
151 CFRelease(fileStrData);
152 return NULL;
153 }
154 CFURLRef rtnUrl = CFURLCreateCopyDeletingPathExtension(NULL, urlRef);
155 CFStringRef rtnStr = NULL;
156 CFRelease(urlRef);
157 if(rtnUrl) {
158 rtnStr = CFURLGetString(rtnUrl);
159 CFRetain(rtnStr);
160 CFRelease(rtnUrl);
161 }
162 CFRelease(fileStrData);
163 return rtnStr;
164 }
165
166 #pragma mark --- mapping of external format to CDSA formats ---
167
168 /*
169 * For the record, here is the mapping of SecExternalFormat, algorithm, and key
170 * class to CSSM-style key format (CSSM_KEYBLOB_FORMAT -
171 * CSSM_KEYBLOB_RAW_FORMAT_X509, etc). The entries in the table are the
172 * last component of a CSSM_KEYBLOB_FORMAT. Format kSecFormatUnknown means
173 * "default for specified class and algorithm", which is currently the
174 * same as kSecFormatOpenSSL.
175 *
176 * algorithm/class
177 * RSA DSA DH
178 * ---------------- ---------------- ----------------
179 * SecExternalFormat priv pub priv pub priv pub
180 * ----------------- ------- ------- ------- ------- ------- -------
181 * kSecFormatOpenSSL PKCS1 X509 OPENSSL X509 PKCS3 X509
182 * kSecFormatBSAFE PKCS8 PKCS1 FIPS186 FIPS186 PKCS8 not supported
183 * kSecFormatUnknown PKCS1 X509 OPENSSL X509 PKCS3 X509
184 * kSecFormatSSH SSH SSH n/s n/s n/s n/s
185 * kSecFormatSSHv2 n/s SSH2 n/s SSH2 n/s n/s
186 *
187 * The only external format supported for ECDSA and ECDH keys is kSecFormatOpenSSL,
188 * which translates to OPENSSL for private keys and X509 for public keys.
189 */
190
191 /* Arrays expressing the above table. */
192
193 /* l.s. dimension is pub/priv for one alg */
194 typedef struct {
195 CSSM_KEYBLOB_FORMAT priv;
196 CSSM_KEYBLOB_FORMAT pub;
197 } algForms;
198
199 /*
200 * indices into array of algForms defining all algs' formats for a given
201 * SecExternalFormat
202 */
203 #define SIE_ALG_RSA 0
204 #define SIE_ALG_DSA 1
205 #define SIE_ALG_DH 2
206 #define SIE_ALG_ECDSA 3
207 #define SIE_ALG_LAST SIE_ALG_ECDSA
208 #define SIE_NUM_ALGS (SIE_ALG_LAST + 1)
209
210 /* kSecFormatOpenSSL */
211 static algForms opensslAlgForms[SIE_NUM_ALGS] =
212 {
213 { CSSM_KEYBLOB_RAW_FORMAT_PKCS1, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // RSA
214 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // DSA
215 { CSSM_KEYBLOB_RAW_FORMAT_PKCS3, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // D-H
216 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // ECDSA
217 };
218
219 /* kSecFormatBSAFE */
220 static algForms bsafeAlgForms[SIE_NUM_ALGS] =
221 {
222 { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_PKCS1 }, // RSA
223 { CSSM_KEYBLOB_RAW_FORMAT_FIPS186, CSSM_KEYBLOB_RAW_FORMAT_FIPS186 }, // DSA
224 { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H
225 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported
226 };
227
228 /* kSecFormatSSH (v1) */
229 static algForms ssh1AlgForms[SIE_NUM_ALGS] =
230 {
231 { CSSM_KEYBLOB_RAW_FORMAT_OPENSSH, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH }, // RSA only
232 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // DSA not supported
233 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported
234 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported
235 };
236
237 /* kSecFormatSSHv2 */
238 static algForms ssh2AlgForms[SIE_NUM_ALGS] =
239 {
240 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // RSA - public only
241 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // DSA - public only
242 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported
243 { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported
244 };
245
246 /*
247 * This routine performs a lookup into the above 3-dimensional array to
248 * map {algorithm, class, SecExternalFormat} to a CSSM_KEYBLOB_FORMAT.
249 * Returns errSecUnsupportedFormat in the rare appropriate case.
250 */
251 OSStatus impExpKeyForm(
252 SecExternalFormat externForm,
253 SecExternalItemType itemType,
254 CSSM_ALGORITHMS alg,
255 CSSM_KEYBLOB_FORMAT *cssmForm, // RETURNED
256 CSSM_KEYCLASS *cssmClass) // RETRUNED
257 {
258 if(itemType == kSecItemTypeSessionKey) {
259 /* special trivial case */
260 /* FIXME ensure caller hasn't specified bogus format */
261 *cssmForm = CSSM_KEYBLOB_RAW_FORMAT_NONE;
262 *cssmClass = CSSM_KEYCLASS_SESSION_KEY;
263 return noErr;
264 }
265 if(externForm == kSecFormatUnknown) {
266 /* default is openssl */
267 externForm = kSecFormatOpenSSL;
268 }
269
270 unsigned algDex;
271 switch(alg) {
272 case CSSM_ALGID_RSA:
273 algDex = SIE_ALG_RSA;
274 break;
275 case CSSM_ALGID_DSA:
276 algDex = SIE_ALG_DSA;
277 break;
278 case CSSM_ALGID_DH:
279 algDex = SIE_ALG_DH;
280 break;
281 case CSSM_ALGID_ECDSA:
282 algDex = SIE_ALG_ECDSA;
283 break;
284 default:
285 return CSSMERR_CSP_INVALID_ALGORITHM;
286 }
287 const algForms *forms;
288 switch(externForm) {
289 case kSecFormatOpenSSL:
290 forms = opensslAlgForms;
291 break;
292 case kSecFormatBSAFE:
293 forms = bsafeAlgForms;
294 break;
295 case kSecFormatSSH:
296 forms = ssh1AlgForms;
297 break;
298 case kSecFormatSSHv2:
299 forms = ssh2AlgForms;
300 break;
301 default:
302 return errSecUnsupportedFormat;
303 }
304 CSSM_KEYBLOB_FORMAT form = CSSM_KEYBLOB_RAW_FORMAT_NONE;
305 switch(itemType) {
306 case kSecItemTypePrivateKey:
307 form = forms[algDex].priv;
308 *cssmClass = CSSM_KEYCLASS_PRIVATE_KEY;
309 break;
310 case kSecItemTypePublicKey:
311 form = forms[algDex].pub;
312 *cssmClass = CSSM_KEYCLASS_PUBLIC_KEY;
313 break;
314 default:
315 return errSecUnsupportedFormat;
316 }
317 if(form == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
318 /* not in the tables - abort */
319 return errSecUnsupportedFormat;
320 }
321 else {
322 *cssmForm = form;
323 return noErr;
324 }
325 }
326
327 /*
328 * Given a raw key blob and zero to three known parameters (type, format,
329 * algorithm), figure out all parameters. Used for private and public keys.
330 */
331 static bool impExpGuessKeyParams(
332 CFDataRef keyData,
333 SecExternalFormat *externForm, // IN/OUT
334 SecExternalItemType *itemType, // IN/OUT
335 CSSM_ALGORITHMS *keyAlg) // IN/OUT
336 {
337 /* CSSM alg list: RSA, DSA, DH, ECDSA */
338 CSSM_ALGORITHMS minAlg = CSSM_ALGID_RSA;
339 CSSM_ALGORITHMS maxAlg = CSSM_ALGID_ECDSA;
340 SecExternalFormat minForm = kSecFormatOpenSSL; // then SSH, BSAFE, then...
341 SecExternalFormat maxForm = kSecFormatSSHv2;
342 SecExternalItemType minType = kSecItemTypePrivateKey; // just two
343 SecExternalItemType maxType = kSecItemTypePublicKey;
344
345 switch(*externForm) {
346 case kSecFormatUnknown:
347 break; // run through all formats
348 case kSecFormatOpenSSL:
349 case kSecFormatSSH:
350 case kSecFormatSSHv2:
351 case kSecFormatBSAFE:
352 minForm = maxForm = *externForm; // just test this one
353 break;
354 default:
355 return false;
356 }
357 switch(*itemType) {
358 case kSecItemTypeUnknown:
359 break;
360 case kSecItemTypePrivateKey:
361 case kSecItemTypePublicKey:
362 minType = maxType = *itemType;
363 break;
364 default:
365 return false;
366 }
367 switch(*keyAlg) {
368 case CSSM_ALGID_NONE:
369 break;
370 case CSSM_ALGID_RSA:
371 case CSSM_ALGID_DSA:
372 case CSSM_ALGID_DH:
373 case CSSM_ALGID_ECDSA:
374 minAlg = maxAlg = *keyAlg;
375 break;
376 default:
377 return false;
378 }
379
380 CSSM_ALGORITHMS theAlg;
381 SecExternalFormat theForm;
382 SecExternalItemType theType;
383 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
384 if(cspHand == 0) {
385 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
386 }
387
388 /*
389 * Iterate through all set of enabled {alg, type, format}.
390 * We do not assume that any of the enums are sequential hence this
391 * odd iteration algorithm....
392 */
393 bool ourRtn = false;
394 for(theAlg=minAlg; ; ) {
395 for(theForm=minForm; ; ) {
396 for(theType=minType; ; ) {
397
398 /* do super lightweight null unwrap to parse */
399 OSStatus ortn = impExpImportRawKey(keyData,
400 theForm, theType, theAlg,
401 NULL, // no keychain
402 cspHand,
403 0, // no flags
404 NULL, // no key params
405 NULL, // no printName
406 NULL); // no returned items
407 if(ortn == noErr) {
408 *externForm = theForm;
409 *itemType = theType;
410 *keyAlg = theAlg;
411 ourRtn = true;
412 goto done;
413 }
414
415 /* next type or break if we're done */
416 if(theType == maxType) {
417 break;
418 }
419 else switch(theType) {
420 case kSecItemTypePrivateKey:
421 theType = kSecItemTypePublicKey;
422 break;
423 default:
424 assert(0);
425 ourRtn = false;
426 goto done;
427 }
428 } /* for each class/type */
429
430 /* next format or break if we're done */
431 if(theForm == maxForm) {
432 break;
433 }
434 else switch(theForm) {
435 case kSecFormatOpenSSL:
436 theForm = kSecFormatSSH;
437 break;
438 case kSecFormatSSH:
439 theForm = kSecFormatBSAFE;
440 break;
441 case kSecFormatBSAFE:
442 theForm = kSecFormatSSHv2;
443 break;
444 default:
445 assert(0);
446 ourRtn = false;
447 goto done;
448 }
449 } /* for each format */
450
451 /* next alg or break if we're done */
452 if(theAlg == maxAlg) {
453 break;
454 }
455 else switch(theAlg) {
456 case CSSM_ALGID_RSA:
457 theAlg = CSSM_ALGID_DSA;
458 break;
459 case CSSM_ALGID_DSA:
460 theAlg = CSSM_ALGID_DH;
461 break;
462 case CSSM_ALGID_DH:
463 theAlg = CSSM_ALGID_ECDSA;
464 break;
465 default:
466 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
467 assert(0);
468 ourRtn = false;
469 goto done;
470 }
471 } /* for each alg */
472 done:
473 cuCspDetachUnload(cspHand, CSSM_TRUE);
474 return ourRtn;
475 }
476
477 /*
478 * Guess an incoming blob's type, format and (for keys only) algorithm
479 * by examining its contents. Returns true on success, in which case
480 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally
481 * passes in valid values any number of these as a clue.
482 */
483 bool impExpImportGuessByExamination(
484 CFDataRef inData,
485 SecExternalFormat *inputFormat, // may be kSecFormatUnknown on entry
486 SecExternalItemType *itemType, // may be kSecItemTypeUnknown on entry
487 CSSM_ALGORITHMS *keyAlg) // CSSM_ALGID_NONE - unknown
488 {
489 if( ( (*inputFormat == kSecFormatUnknown) ||
490 (*inputFormat == kSecFormatX509Cert)
491 ) &&
492 ( (*itemType == kSecItemTypeUnknown) ||
493 (*itemType == kSecItemTypeCertificate) ) ) {
494 /*
495 * See if it parses as a cert
496 */
497 CSSM_CL_HANDLE clHand = cuClStartup();
498 if(clHand == 0) {
499 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
500 }
501 CSSM_HANDLE cacheHand;
502 CSSM_RETURN crtn;
503 CSSM_DATA cdata = { CFDataGetLength(inData),
504 (uint8 *)CFDataGetBytePtr(inData) };
505 crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand);
506 bool brtn = false;
507 if(crtn == CSSM_OK) {
508 *inputFormat = kSecFormatX509Cert;
509 *itemType = kSecItemTypeCertificate;
510 SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
511 CSSM_CL_CertAbortCache(clHand, cacheHand);
512 brtn = true;
513 }
514 cuClDetachUnload(clHand);
515 if(brtn) {
516 return true;
517 }
518 }
519 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
520
521 if( ( (*inputFormat == kSecFormatUnknown) ||
522 (*inputFormat == kSecFormatNetscapeCertSequence)
523 ) &&
524 ( (*itemType == kSecItemTypeUnknown) ||
525 (*itemType == kSecItemTypeAggregate) ) ) {
526 /* See if it's a netscape cert sequence */
527 CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL);
528 if(crtn == CSSM_OK) {
529 *inputFormat = kSecFormatNetscapeCertSequence;
530 *itemType = kSecItemTypeAggregate;
531 SecImpInferDbg("Inferred netscape-cert-sequence by decoding");
532 return true;
533 }
534 }
535
536 /* See if it's a key */
537 return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg);
538 }
539
540 #pragma mark --- Key Import support ---
541
542 /*
543 * Given a context specified via a CSSM_CC_HANDLE, add a new
544 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
545 * AttributeLength, and an untyped pointer.
546 */
547 CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
548 uint32 AttributeType,
549 uint32 AttributeLength,
550 const void *AttributePtr)
551 {
552 CSSM_CONTEXT_ATTRIBUTE newAttr;
553
554 newAttr.AttributeType = AttributeType;
555 newAttr.AttributeLength = AttributeLength;
556 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
557 return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
558 }
559
560 /*
561 * Free memory via specified plugin's app-level allocator
562 */
563 void impExpFreeCssmMemory(
564 CSSM_HANDLE hand,
565 void *p)
566 {
567 CSSM_API_MEMORY_FUNCS memFuncs;
568 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
569 if(crtn) {
570 return;
571 }
572 memFuncs.free_func(p, memFuncs.AllocRef);
573 }
574
575 /*
576 * Calculate digest of any CSSM_KEY. Unlike older implementations
577 * of this logic, you can actually calculate the public key hash
578 * on any class of key, any format, raw CSP or CSPDL (though if
579 * you're using the CSPDL, the key has to be a reference key
580 * in that CSPDL session).
581 *
582 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
583 * this is allocated by the CSP's app-specified allocator.
584 */
585 CSSM_RETURN impExpKeyDigest(
586 CSSM_CSP_HANDLE cspHand,
587 CSSM_KEY_PTR key,
588 CSSM_DATA_PTR keyDigest) // contents allocd and RETURNED
589 {
590 CSSM_DATA_PTR localDigest;
591 CSSM_CC_HANDLE ccHand;
592
593 CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
594 key,
595 &ccHand);
596 if(crtn) {
597 return crtn;
598 }
599 crtn = CSSM_CSP_PassThrough(ccHand,
600 CSSM_APPLECSP_KEYDIGEST,
601 NULL,
602 (void **)&localDigest);
603 if(crtn) {
604 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
605 }
606 else {
607 /*
608 * Give caller the Data referent and we'll free the
609 * CSSM_DATA struct itswelf.
610 */
611 *keyDigest = *localDigest;
612 impExpFreeCssmMemory(cspHand, localDigest);
613 }
614 CSSM_DeleteContext(ccHand);
615 return crtn;
616 }
617
618
619 /*
620 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
621 * return a refcounted CFStringRef suitable for use with the PKCS12 library.
622 * PKCS12 passphrases in CFData format must be UTF8 encoded.
623 */
624 OSStatus impExpPassphraseToCFString(
625 CFTypeRef passin,
626 CFStringRef *passout) // may be the same as passin, but refcounted
627 {
628 if(CFGetTypeID(passin) == CFStringGetTypeID()) {
629 CFStringRef passInStr = (CFStringRef)passin;
630 CFRetain(passInStr);
631 *passout = passInStr;
632 return noErr;
633 }
634 else if(CFGetTypeID(passin) == CFDataGetTypeID()) {
635 CFDataRef cfData = (CFDataRef)passin;
636 CFIndex len = CFDataGetLength(cfData);
637 CFStringRef cfStr = CFStringCreateWithBytes(NULL,
638 CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true);
639 if(cfStr == NULL) {
640 SecImpExpDbg("Passphrase not in UTF8 format");
641 return paramErr;
642 }
643 *passout = cfStr;
644 return noErr;
645 }
646 else {
647 SecImpExpDbg("Passphrase not CFData or CFString");
648 return paramErr;
649 }
650 }
651
652 /*
653 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
654 * return a refcounted CFDataRef whose bytes are suitable for use with
655 * PKCS5 (v1.5 and v2.0) key derivation.
656 */
657 OSStatus impExpPassphraseToCFData(
658 CFTypeRef passin,
659 CFDataRef *passout) // may be the same as passin, but refcounted
660 {
661 if(CFGetTypeID(passin) == CFDataGetTypeID()) {
662 CFDataRef passInData = (CFDataRef)passin;
663 CFRetain(passInData);
664 *passout = passInData;
665 return noErr;
666 }
667 else if(CFGetTypeID(passin) == CFStringGetTypeID()) {
668 CFStringRef passInStr = (CFStringRef)passin;
669 CFDataRef outData;
670 outData = CFStringCreateExternalRepresentation(NULL,
671 passInStr,
672 kCFStringEncodingUTF8,
673 0); // lossByte 0 ==> no loss allowed
674 if(outData == NULL) {
675 /* Well try with lossy conversion */
676 SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8");
677 outData = CFStringCreateExternalRepresentation(NULL,
678 passInStr,
679 kCFStringEncodingUTF8,
680 1);
681 if(outData == NULL) {
682 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
683 /* what do we do now, Batman? */
684 return paramErr;
685 }
686 }
687 *passout = outData;
688 return noErr;
689 }
690 else {
691 SecImpExpDbg("Passphrase not CFData or CFString");
692 return paramErr;
693 }
694 }
695
696 /*
697 * Add a CFString to a crypto context handle.
698 */
699 static CSSM_RETURN impExpAddStringAttr(
700 CSSM_CC_HANDLE ccHand,
701 CFStringRef str,
702 CSSM_ATTRIBUTE_TYPE attrType)
703 {
704 /* CFStrings are passed as external rep in UTF8 encoding by convention */
705 CFDataRef outData;
706 outData = CFStringCreateExternalRepresentation(NULL,
707 str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed
708 if(outData == NULL) {
709 SecImpExpDbg("impExpAddStringAttr: bad string format");
710 return paramErr;
711 }
712
713 CSSM_DATA attrData;
714 attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
715 attrData.Length = CFDataGetLength(outData);
716 CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
717 &attrData);
718 CFRelease(outData);
719 if(crtn) {
720 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
721 }
722 return crtn;
723 }
724
725 /*
726 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
727 */
728 static CSSM_RETURN impExpCreatePassKey(
729 const SecKeyImportExportParameters *keyParams, // required
730 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL
731 impExpVerifyPhrase verifyPhrase, // for secure passphrase
732 CSSM_KEY_PTR *passKey) // mallocd and RETURNED
733 {
734 CSSM_RETURN crtn;
735 CSSM_CC_HANDLE ccHand;
736 uint32 verifyAttr;
737 CSSM_DATA dummyLabel;
738 CSSM_KEY_PTR ourKey = NULL;
739
740 SecImpExpDbg("Generating secure passphrase key");
741 ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
742 if(ourKey == NULL) {
743 return memFullErr;
744 }
745 memset(ourKey, 0, sizeof(CSSM_KEY));
746
747 crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
748 CSSM_ALGID_SECURE_PASSPHRASE,
749 4, // keySizeInBits must be non zero
750 NULL, // Seed
751 NULL, // Salt
752 NULL, // StartDate
753 NULL, // EndDate
754 NULL, // Params
755 &ccHand);
756 if(crtn) {
757 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
758 return crtn;
759 }
760 /* subsequent errors to errOut: */
761
762 /* additional context attributes specific to this type of key gen */
763 assert(keyParams != NULL); // or we wouldn't be here
764 if(keyParams->alertTitle != NULL) {
765 crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
766 CSSM_ATTRIBUTE_ALERT_TITLE);
767 if(crtn) {
768 goto errOut;
769 }
770 }
771 if(keyParams->alertPrompt != NULL) {
772 crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
773 CSSM_ATTRIBUTE_PROMPT);
774 if(crtn) {
775 goto errOut;
776 }
777 }
778 verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0;
779 crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
780 sizeof(uint32), (const void *)verifyAttr);
781 if(crtn) {
782 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
783 goto errOut;
784 }
785
786 dummyLabel.Data = (uint8 *)"Secure Passphrase";
787 dummyLabel.Length = strlen((char *)dummyLabel.Data);
788
789 crtn = CSSM_GenerateKey(ccHand,
790 CSSM_KEYUSE_ANY,
791 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
792 &dummyLabel,
793 NULL, // ACL
794 ourKey);
795 if(crtn) {
796 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
797 }
798 errOut:
799 CSSM_DeleteContext(ccHand);
800 if(crtn == CSSM_OK) {
801 *passKey = ourKey;
802 }
803 else if(ourKey != NULL) {
804 free(ourKey);
805 }
806 return crtn;
807 }
808
809 /*
810 * Obtain passphrase, given a SecKeyImportExportParameters.
811 *
812 * Passphrase comes from one of two places: app-specified, in
813 * SecKeyImportExportParameters.passphrase (either as CFStringRef
814 * or CFDataRef); or via the secure passphrase mechanism.
815 *
816 * Passphrase is returned in AT MOST one of two forms:
817 *
818 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
819 * caller must CSSM_FreeKey later as well as free()ing the actual
820 * CSSM_KEY_PTR.
821 * -- CFTypeRef for app-supplied passphrases. This can be one of
822 * two types:
823 *
824 * -- CFStringRef, for use with P12
825 * -- CFDataRef, for more general use (e.g. for PKCS5).
826 *
827 * In either case the caller must CFRelease the result.
828 */
829 OSStatus impExpPassphraseCommon(
830 const SecKeyImportExportParameters *keyParams,
831 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL, for passKey generation
832 impExpPassphraseForm phraseForm,
833 impExpVerifyPhrase verifyPhrase, // for secure passphrase
834 CFTypeRef *phrase, // RETURNED, or
835 CSSM_KEY_PTR *passKey) // mallocd and RETURNED
836 {
837 assert(keyParams != NULL);
838
839 /* Give precedence to secure passphrase */
840 if(keyParams->flags & kSecKeySecurePassphrase) {
841 assert(passKey != NULL);
842 return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey);
843 }
844 else if(keyParams->passphrase != NULL) {
845 CFTypeRef phraseOut;
846 OSStatus ortn;
847 assert(phrase != NULL);
848 switch(phraseForm) {
849 case SPF_String:
850 ortn = impExpPassphraseToCFString(keyParams->passphrase,
851 (CFStringRef *)&phraseOut);
852 break;
853 case SPF_Data:
854 ortn = impExpPassphraseToCFData(keyParams->passphrase,
855 (CFDataRef *)&phraseOut);
856 break;
857 default:
858 /* only called internally */
859 assert(0);
860 ortn = paramErr;
861 }
862 if(ortn == noErr) {
863 *phrase = phraseOut;
864 }
865 return ortn;
866 }
867 else {
868 return errSecPassphraseRequired;
869 }
870 }
871
872 CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef usage)
873 {
874 CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT;
875 if (NULL == aKey)
876 {
877 return result;
878 }
879 OSStatus err = noErr;
880
881 const CSSM_KEY* cssmKey = NULL;
882 err = SecKeyGetCSSMKey(aKey, &cssmKey);
883 if (noErr != err)
884 {
885 return result;
886 }
887
888 result = cssmKey->KeyHeader.KeyAttr;
889
890 if (NULL != usage)
891 {
892 CFTypeRef item = NULL;
893 CFIndex numItems = CFArrayGetCount(usage);
894 for (CFIndex iCnt = 0L; iCnt < numItems; iCnt++)
895 {
896 item = (CFTypeRef)CFArrayGetValueAtIndex(usage, iCnt);
897 if (CFEqual(item, kSecAttrIsPermanent))
898 {
899 result |= CSSM_KEYATTR_PERMANENT;
900 }
901 /*
902 Currently the kSecAttrIsModifiable is private. Does this need to be
903 made public?
904 else if (CFEqual(item, kSecAttrIsModifiable))
905 {
906 result |= CSSM_KEYATTR_MODIFIABLE;
907 }
908 */
909 }
910 }
911
912 return result;
913
914 }
915
916 Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey,
917 const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr)
918 {
919 Boolean result = false;
920
921 if (NULL != oldPtr && NULL != newPtr)
922 {
923 oldPtr->version = newPtr->version;
924 oldPtr->flags = newPtr->flags;
925 oldPtr->passphrase = newPtr->passphrase;
926 oldPtr->alertTitle = newPtr->alertTitle;
927 oldPtr->alertPrompt = newPtr->alertPrompt;
928 oldPtr->accessRef = newPtr->accessRef;
929 oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage);
930 oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes);
931 result = true;
932 }
933 return result;
934 }
935