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