]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecImportExportUtils.cpp
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecImportExportUtils.cpp
1 /*
2 * Copyright (c) 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 * 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 <security_utilities/casts.h>
33 #include <Security/SecBase.h>
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 errSecSuccess;
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 errSecSuccess;
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
346 if(keyData == NULL || CFDataGetLength(keyData) == 0){
347 MacOSError::throwMe(errSecUnsupportedKeySize);
348 }
349
350 switch(*externForm) {
351 case kSecFormatUnknown:
352 break; // run through all formats
353 case kSecFormatOpenSSL:
354 case kSecFormatSSH:
355 case kSecFormatSSHv2:
356 case kSecFormatBSAFE:
357 minForm = maxForm = *externForm; // just test this one
358 break;
359 default:
360 return false;
361 }
362 switch(*itemType) {
363 case kSecItemTypeUnknown:
364 break;
365 case kSecItemTypePrivateKey:
366 case kSecItemTypePublicKey:
367 minType = maxType = *itemType;
368 break;
369 default:
370 return false;
371 }
372 switch(*keyAlg) {
373 case CSSM_ALGID_NONE:
374 break;
375 case CSSM_ALGID_RSA:
376 case CSSM_ALGID_DSA:
377 case CSSM_ALGID_DH:
378 case CSSM_ALGID_ECDSA:
379 minAlg = maxAlg = *keyAlg;
380 break;
381 default:
382 return false;
383 }
384
385 CSSM_ALGORITHMS theAlg;
386 SecExternalFormat theForm;
387 SecExternalItemType theType;
388 CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE);
389 if(cspHand == 0) {
390 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
391 }
392
393 /*
394 * Iterate through all set of enabled {alg, type, format}.
395 * We do not assume that any of the enums are sequential hence this
396 * odd iteration algorithm....
397 */
398 bool ourRtn = false;
399 for(theAlg=minAlg; ; ) {
400 for(theForm=minForm; ; ) {
401 for(theType=minType; ; ) {
402
403 /* do super lightweight null unwrap to parse */
404 OSStatus ortn = impExpImportRawKey(keyData,
405 theForm, theType, theAlg,
406 NULL, // no keychain
407 cspHand,
408 0, // no flags
409 NULL, // no key params
410 NULL, // no printName
411 NULL); // no returned items
412 if(ortn == errSecSuccess) {
413 *externForm = theForm;
414 *itemType = theType;
415 *keyAlg = theAlg;
416 ourRtn = true;
417 goto done;
418 }
419
420 /* next type or break if we're done */
421 if(theType == maxType) {
422 break;
423 }
424 else switch(theType) {
425 case kSecItemTypePrivateKey:
426 theType = kSecItemTypePublicKey;
427 break;
428 default:
429 assert(0);
430 ourRtn = false;
431 goto done;
432 }
433 } /* for each class/type */
434
435 /* next format or break if we're done */
436 if(theForm == maxForm) {
437 break;
438 }
439 else switch(theForm) {
440 case kSecFormatOpenSSL:
441 theForm = kSecFormatSSH;
442 break;
443 case kSecFormatSSH:
444 theForm = kSecFormatBSAFE;
445 break;
446 case kSecFormatBSAFE:
447 theForm = kSecFormatSSHv2;
448 break;
449 default:
450 assert(0);
451 ourRtn = false;
452 goto done;
453 }
454 } /* for each format */
455
456 /* next alg or break if we're done */
457 if(theAlg == maxAlg) {
458 break;
459 }
460 else switch(theAlg) {
461 case CSSM_ALGID_RSA:
462 theAlg = CSSM_ALGID_DSA;
463 break;
464 case CSSM_ALGID_DSA:
465 theAlg = CSSM_ALGID_DH;
466 break;
467 case CSSM_ALGID_DH:
468 theAlg = CSSM_ALGID_ECDSA;
469 break;
470 default:
471 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
472 assert(0);
473 ourRtn = false;
474 goto done;
475 }
476 } /* for each alg */
477 done:
478 cuCspDetachUnload(cspHand, CSSM_TRUE);
479 return ourRtn;
480 }
481
482 /*
483 * Guess an incoming blob's type, format and (for keys only) algorithm
484 * by examining its contents. Returns true on success, in which case
485 * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally
486 * passes in valid values any number of these as a clue.
487 */
488 bool impExpImportGuessByExamination(
489 CFDataRef inData,
490 SecExternalFormat *inputFormat, // may be kSecFormatUnknown on entry
491 SecExternalItemType *itemType, // may be kSecItemTypeUnknown on entry
492 CSSM_ALGORITHMS *keyAlg) // CSSM_ALGID_NONE - unknown
493 {
494 if( ( (*inputFormat == kSecFormatUnknown) ||
495 (*inputFormat == kSecFormatX509Cert)
496 ) &&
497 ( (*itemType == kSecItemTypeUnknown) ||
498 (*itemType == kSecItemTypeCertificate) ) ) {
499 /*
500 * See if it parses as a cert
501 */
502 CSSM_CL_HANDLE clHand = cuClStartup();
503 if(clHand == 0) {
504 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
505 }
506 CSSM_HANDLE cacheHand;
507 CSSM_RETURN crtn;
508 CSSM_DATA cdata = { int_cast<CFIndex,CSSM_SIZE>(CFDataGetLength(inData)),
509 (uint8 *)CFDataGetBytePtr(inData) };
510 crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand);
511 bool brtn = false;
512 if(crtn == CSSM_OK) {
513 *inputFormat = kSecFormatX509Cert;
514 *itemType = kSecItemTypeCertificate;
515 SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
516 CSSM_CL_CertAbortCache(clHand, cacheHand);
517 brtn = true;
518 }
519 cuClDetachUnload(clHand);
520 if(brtn) {
521 return true;
522 }
523 }
524 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
525
526 if( ( (*inputFormat == kSecFormatUnknown) ||
527 (*inputFormat == kSecFormatNetscapeCertSequence)
528 ) &&
529 ( (*itemType == kSecItemTypeUnknown) ||
530 (*itemType == kSecItemTypeAggregate) ) ) {
531 /* See if it's a netscape cert sequence */
532 CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL);
533 if(crtn == CSSM_OK) {
534 *inputFormat = kSecFormatNetscapeCertSequence;
535 *itemType = kSecItemTypeAggregate;
536 SecImpInferDbg("Inferred netscape-cert-sequence by decoding");
537 return true;
538 }
539 }
540
541 /* See if it's a key */
542 return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg);
543 }
544
545 #pragma mark --- Key Import support ---
546
547 /*
548 * Given a context specified via a CSSM_CC_HANDLE, add a new
549 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
550 * AttributeLength, and an untyped pointer.
551 */
552 CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
553 uint32 AttributeType,
554 uint32 AttributeLength,
555 const void *AttributePtr)
556 {
557 CSSM_CONTEXT_ATTRIBUTE newAttr;
558
559 newAttr.AttributeType = AttributeType;
560 newAttr.AttributeLength = AttributeLength;
561 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
562 return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
563 }
564
565 /*
566 * Free memory via specified plugin's app-level allocator
567 */
568 void impExpFreeCssmMemory(
569 CSSM_HANDLE hand,
570 void *p)
571 {
572 CSSM_API_MEMORY_FUNCS memFuncs;
573 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
574 if(crtn) {
575 return;
576 }
577 memFuncs.free_func(p, memFuncs.AllocRef);
578 }
579
580 /*
581 * Calculate digest of any CSSM_KEY. Unlike older implementations
582 * of this logic, you can actually calculate the public key hash
583 * on any class of key, any format, raw CSP or CSPDL (though if
584 * you're using the CSPDL, the key has to be a reference key
585 * in that CSPDL session).
586 *
587 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
588 * this is allocated by the CSP's app-specified allocator.
589 */
590 CSSM_RETURN impExpKeyDigest(
591 CSSM_CSP_HANDLE cspHand,
592 CSSM_KEY_PTR key,
593 CSSM_DATA_PTR keyDigest) // contents allocd and RETURNED
594 {
595 CSSM_DATA_PTR localDigest;
596 CSSM_CC_HANDLE ccHand;
597
598 CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand,
599 key,
600 &ccHand);
601 if(crtn) {
602 return crtn;
603 }
604 crtn = CSSM_CSP_PassThrough(ccHand,
605 CSSM_APPLECSP_KEYDIGEST,
606 NULL,
607 (void **)&localDigest);
608 if(crtn) {
609 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
610 }
611 else {
612 /*
613 * Give caller the Data referent and we'll free the
614 * CSSM_DATA struct itswelf.
615 */
616 *keyDigest = *localDigest;
617 impExpFreeCssmMemory(cspHand, localDigest);
618 }
619 CSSM_DeleteContext(ccHand);
620 return crtn;
621 }
622
623
624 /*
625 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
626 * return a refcounted CFStringRef suitable for use with the PKCS12 library.
627 * PKCS12 passphrases in CFData format must be UTF8 encoded.
628 */
629 OSStatus impExpPassphraseToCFString(
630 CFTypeRef passin,
631 CFStringRef *passout) // may be the same as passin, but refcounted
632 {
633 if(CFGetTypeID(passin) == CFStringGetTypeID()) {
634 CFStringRef passInStr = (CFStringRef)passin;
635 CFRetain(passInStr);
636 *passout = passInStr;
637 return errSecSuccess;
638 }
639 else if(CFGetTypeID(passin) == CFDataGetTypeID()) {
640 CFDataRef cfData = (CFDataRef)passin;
641 CFIndex len = CFDataGetLength(cfData);
642 CFStringRef cfStr = CFStringCreateWithBytes(NULL,
643 CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true);
644 if(cfStr == NULL) {
645 SecImpExpDbg("Passphrase not in UTF8 format");
646 return errSecParam;
647 }
648 *passout = cfStr;
649 return errSecSuccess;
650 }
651 else {
652 SecImpExpDbg("Passphrase not CFData or CFString");
653 return errSecParam;
654 }
655 }
656
657 /*
658 * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef,
659 * return a refcounted CFDataRef whose bytes are suitable for use with
660 * PKCS5 (v1.5 and v2.0) key derivation.
661 */
662 OSStatus impExpPassphraseToCFData(
663 CFTypeRef passin,
664 CFDataRef *passout) // may be the same as passin, but refcounted
665 {
666 if(CFGetTypeID(passin) == CFDataGetTypeID()) {
667 CFDataRef passInData = (CFDataRef)passin;
668 CFRetain(passInData);
669 *passout = passInData;
670 return errSecSuccess;
671 }
672 else if(CFGetTypeID(passin) == CFStringGetTypeID()) {
673 CFStringRef passInStr = (CFStringRef)passin;
674 CFDataRef outData;
675 outData = CFStringCreateExternalRepresentation(NULL,
676 passInStr,
677 kCFStringEncodingUTF8,
678 0); // lossByte 0 ==> no loss allowed
679 if(outData == NULL) {
680 /* Well try with lossy conversion */
681 SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8");
682 outData = CFStringCreateExternalRepresentation(NULL,
683 passInStr,
684 kCFStringEncodingUTF8,
685 1);
686 if(outData == NULL) {
687 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
688 /* what do we do now, Batman? */
689 return errSecParam;
690 }
691 }
692 *passout = outData;
693 return errSecSuccess;
694 }
695 else {
696 SecImpExpDbg("Passphrase not CFData or CFString");
697 return errSecParam;
698 }
699 }
700
701 /*
702 * Add a CFString to a crypto context handle.
703 */
704 static CSSM_RETURN impExpAddStringAttr(
705 CSSM_CC_HANDLE ccHand,
706 CFStringRef str,
707 CSSM_ATTRIBUTE_TYPE attrType)
708 {
709 /* CFStrings are passed as external rep in UTF8 encoding by convention */
710 CFDataRef outData;
711 outData = CFStringCreateExternalRepresentation(NULL,
712 str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed
713 if(outData == NULL) {
714 SecImpExpDbg("impExpAddStringAttr: bad string format");
715 return errSecParam;
716 }
717
718 CSSM_DATA attrData;
719 attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
720 attrData.Length = CFDataGetLength(outData);
721 CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
722 &attrData);
723 CFRelease(outData);
724 if(crtn) {
725 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
726 }
727 return crtn;
728 }
729
730 /*
731 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
732 */
733 static CSSM_RETURN impExpCreatePassKey(
734 const SecKeyImportExportParameters *keyParams, // required
735 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL
736 impExpVerifyPhrase verifyPhrase, // for secure passphrase
737 CSSM_KEY_PTR *passKey) // mallocd and RETURNED
738 {
739 CSSM_RETURN crtn;
740 CSSM_CC_HANDLE ccHand;
741 uint32 verifyAttr;
742 CSSM_DATA dummyLabel;
743 CSSM_KEY_PTR ourKey = NULL;
744
745 SecImpExpDbg("Generating secure passphrase key");
746 ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
747 if(ourKey == NULL) {
748 return errSecAllocate;
749 }
750 memset(ourKey, 0, sizeof(CSSM_KEY));
751
752 crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
753 CSSM_ALGID_SECURE_PASSPHRASE,
754 4, // keySizeInBits must be non zero
755 NULL, // Seed
756 NULL, // Salt
757 NULL, // StartDate
758 NULL, // EndDate
759 NULL, // Params
760 &ccHand);
761 if(crtn) {
762 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
763 return crtn;
764 }
765 /* subsequent errors to errOut: */
766
767 /* additional context attributes specific to this type of key gen */
768 assert(keyParams != NULL); // or we wouldn't be here
769 if(keyParams->alertTitle != NULL) {
770 crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
771 CSSM_ATTRIBUTE_ALERT_TITLE);
772 if(crtn) {
773 goto errOut;
774 }
775 }
776 if(keyParams->alertPrompt != NULL) {
777 crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
778 CSSM_ATTRIBUTE_PROMPT);
779 if(crtn) {
780 goto errOut;
781 }
782 }
783 verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0;
784 crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
785 sizeof(uint32), (const void *)((size_t) verifyAttr));
786 if(crtn) {
787 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
788 goto errOut;
789 }
790
791 dummyLabel.Data = (uint8 *)"Secure Passphrase";
792 dummyLabel.Length = strlen((char *)dummyLabel.Data);
793
794 crtn = CSSM_GenerateKey(ccHand,
795 CSSM_KEYUSE_ANY,
796 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
797 &dummyLabel,
798 NULL, // ACL
799 ourKey);
800 if(crtn) {
801 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
802 }
803 errOut:
804 CSSM_DeleteContext(ccHand);
805 if(crtn == CSSM_OK) {
806 *passKey = ourKey;
807 }
808 else if(ourKey != NULL) {
809 free(ourKey);
810 }
811 return crtn;
812 }
813
814 /*
815 * Obtain passphrase, given a SecKeyImportExportParameters.
816 *
817 * Passphrase comes from one of two places: app-specified, in
818 * SecKeyImportExportParameters.passphrase (either as CFStringRef
819 * or CFDataRef); or via the secure passphrase mechanism.
820 *
821 * Passphrase is returned in AT MOST one of two forms:
822 *
823 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
824 * caller must CSSM_FreeKey later as well as free()ing the actual
825 * CSSM_KEY_PTR.
826 * -- CFTypeRef for app-supplied passphrases. This can be one of
827 * two types:
828 *
829 * -- CFStringRef, for use with P12
830 * -- CFDataRef, for more general use (e.g. for PKCS5).
831 *
832 * In either case the caller must CFRelease the result.
833 */
834 OSStatus impExpPassphraseCommon(
835 const SecKeyImportExportParameters *keyParams,
836 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL, for passKey generation
837 impExpPassphraseForm phraseForm,
838 impExpVerifyPhrase verifyPhrase, // for secure passphrase
839 CFTypeRef *phrase, // RETURNED, or
840 CSSM_KEY_PTR *passKey) // mallocd and RETURNED
841 {
842 assert(keyParams != NULL);
843
844 /* Give precedence to secure passphrase */
845 if(keyParams->flags & kSecKeySecurePassphrase) {
846 assert(passKey != NULL);
847 return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey);
848 }
849 else if(keyParams->passphrase != NULL) {
850 CFTypeRef phraseOut;
851 OSStatus ortn;
852 assert(phrase != NULL);
853 switch(phraseForm) {
854 case SPF_String:
855 ortn = impExpPassphraseToCFString(keyParams->passphrase,
856 (CFStringRef *)&phraseOut);
857 break;
858 case SPF_Data:
859 ortn = impExpPassphraseToCFData(keyParams->passphrase,
860 (CFDataRef *)&phraseOut);
861 break;
862 default:
863 /* only called internally */
864 assert(0);
865 ortn = errSecParam;
866 }
867 if(ortn == errSecSuccess) {
868 *phrase = phraseOut;
869 }
870 return ortn;
871 }
872 else {
873 return errSecPassphraseRequired;
874 }
875 }
876
877 static void ToggleKeyAttribute(
878 CFArrayRef keyAttrs,
879 CFTypeRef attr,
880 CSSM_KEYATTR_FLAGS mask,
881 CSSM_KEYATTR_FLAGS &result)
882 {
883 // If the keyAttrs array contains the given attribute,
884 // set the corresponding keyattr flags, otherwise clear them.
885 // (Note: caller verifies that keyAttrs is not NULL.)
886 CFIndex numItems = CFArrayGetCount(keyAttrs);
887 result &= ~(mask);
888 if (numItems > 0) {
889 CFRange range = CFRangeMake(0, numItems);
890 if (CFArrayContainsValue(keyAttrs, range, attr))
891 result |= mask;
892 }
893 }
894
895 CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs)
896 {
897 CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT;
898
899 if (aKey) {
900 const CSSM_KEY* cssmKey = NULL;
901 if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey))
902 result = cssmKey->KeyHeader.KeyAttr;
903 }
904
905 if (!keyAttrs)
906 return result;
907
908 CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
909 CFIndex idx, count = CFArrayGetCount(keyAttrs);
910 for (idx=0; idx<count; idx++) {
911 CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx);
912 if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) {
913 // Convert numeric CSSM keyattr values to equivalent attribute constants
914 uint32 value;
915 if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) {
916 switch (value) {
917 case CSSM_KEYATTR_SENSITIVE:
918 attr = kSecAttrIsSensitive;
919 break;
920 case CSSM_KEYATTR_EXTRACTABLE:
921 attr = kSecAttrIsExtractable;
922 break;
923 case CSSM_KEYATTR_PERMANENT:
924 attr = kSecAttrIsPermanent;
925 break;
926 default:
927 attr = NULL;
928 break;
929 }
930 }
931 }
932 if (attr)
933 CFArrayAppendValue(attrs, attr);
934 }
935
936 // Set key attribute flag in result if present in the array, otherwise clear
937 ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result);
938 ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result);
939 ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result);
940
941 // if caller specified an attributes array which omitted kSecAttrIsExtractable,
942 // this implies the sensitive attribute; force it on so that at least one bit
943 // is set. If our result is 0, this is indistinguishable from the case where
944 // no key attributes were specified, causing a default bitmask to be used
945 // in subsequent import code.
946
947 if (0==(result & CSSM_KEYATTR_EXTRACTABLE))
948 result |= CSSM_KEYATTR_SENSITIVE;
949
950 CFRelease(attrs);
951 return result;
952 }
953
954 Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey,
955 const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr)
956 {
957 Boolean result = false;
958
959 if (NULL != oldPtr && NULL != newPtr)
960 {
961 oldPtr->version = newPtr->version;
962 oldPtr->flags = newPtr->flags;
963 oldPtr->passphrase = newPtr->passphrase;
964 oldPtr->alertTitle = newPtr->alertTitle;
965 oldPtr->alertPrompt = newPtr->alertPrompt;
966 oldPtr->accessRef = newPtr->accessRef;
967 oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage);
968 oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes);
969 result = true;
970 }
971 return result;
972 }
973