2 * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * SecImportExportUtils.cpp - misc. utilities for import/export module
26 #include "SecImportExportUtils.h"
27 #include "SecImportExportAgg.h"
28 #include "SecImportExportCrypto.h"
29 #include <Security/SecIdentityPriv.h>
30 #include <Security/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 ---
38 const char *impExpExtFormatStr(
39 SecExternalFormat 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";
60 const char *impExpExtItemTypeStr(
61 SecExternalItemType 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";
76 * Parse file extension and attempt to map it to format and type. Returns true
79 bool impExpImportParseFileExten(
81 SecExternalFormat
*inputFormat
, // RETURNED
82 SecExternalItemType
*itemType
) // RETURNED
85 /* nothing to work with */
88 if(CFStringHasSuffix(fstr
, CFSTR(".cer")) ||
89 CFStringHasSuffix(fstr
, CFSTR(".crt"))) {
90 *inputFormat
= kSecFormatX509Cert
;
91 *itemType
= kSecItemTypeCertificate
;
92 SecImpInferDbg("Inferring kSecFormatX509Cert from file name");
95 if(CFStringHasSuffix(fstr
, CFSTR(".p12")) ||
96 CFStringHasSuffix(fstr
, CFSTR(".pfx"))) {
97 *inputFormat
= kSecFormatPKCS12
;
98 *itemType
= kSecItemTypeAggregate
;
99 SecImpInferDbg("Inferring kSecFormatPKCS12 from file name");
103 /* Get extension, look for key indicators as substrings */
104 CFURLRef url
= CFURLCreateWithString(NULL
, fstr
, NULL
);
106 SecImpInferDbg("impExpImportParseFileExten: error creating URL");
109 CFStringRef exten
= CFURLCopyPathExtension(url
);
112 /* no extension, app probably passed in only an extension */
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");
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");
138 /* do a [NSString stringByDeletingPathExtension] equivalent */
139 CFStringRef
impExpImportDeleteExtension(
142 CFDataRef fileStrData
= CFStringCreateExternalRepresentation(NULL
, fileStr
,
143 kCFStringEncodingUTF8
, 0);
144 if(fileStrData
== NULL
) {
148 CFURLRef urlRef
= CFURLCreateFromFileSystemRepresentation(NULL
,
149 CFDataGetBytePtr(fileStrData
), CFDataGetLength(fileStrData
), false);
151 CFRelease(fileStrData
);
154 CFURLRef rtnUrl
= CFURLCreateCopyDeletingPathExtension(NULL
, urlRef
);
155 CFStringRef rtnStr
= NULL
;
158 rtnStr
= CFURLGetString(rtnUrl
);
162 CFRelease(fileStrData
);
166 #pragma mark --- mapping of external format to CDSA formats ---
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.
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
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.
191 /* Arrays expressing the above table. */
193 /* l.s. dimension is pub/priv for one alg */
195 CSSM_KEYBLOB_FORMAT priv
;
196 CSSM_KEYBLOB_FORMAT pub
;
200 * indices into array of algForms defining all algs' formats for a given
203 #define SIE_ALG_RSA 0
204 #define SIE_ALG_DSA 1
206 #define SIE_ALG_ECDSA 3
207 #define SIE_ALG_LAST SIE_ALG_ECDSA
208 #define SIE_NUM_ALGS (SIE_ALG_LAST + 1)
210 /* kSecFormatOpenSSL */
211 static algForms opensslAlgForms
[SIE_NUM_ALGS
] =
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
219 /* kSecFormatBSAFE */
220 static algForms bsafeAlgForms
[SIE_NUM_ALGS
] =
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
228 /* kSecFormatSSH (v1) */
229 static algForms ssh1AlgForms
[SIE_NUM_ALGS
] =
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
237 /* kSecFormatSSHv2 */
238 static algForms ssh2AlgForms
[SIE_NUM_ALGS
] =
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
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.
251 OSStatus
impExpKeyForm(
252 SecExternalFormat externForm
,
253 SecExternalItemType itemType
,
255 CSSM_KEYBLOB_FORMAT
*cssmForm
, // RETURNED
256 CSSM_KEYCLASS
*cssmClass
) // RETRUNED
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
;
265 if(externForm
== kSecFormatUnknown
) {
266 /* default is openssl */
267 externForm
= kSecFormatOpenSSL
;
273 algDex
= SIE_ALG_RSA
;
276 algDex
= SIE_ALG_DSA
;
281 case CSSM_ALGID_ECDSA
:
282 algDex
= SIE_ALG_ECDSA
;
285 return CSSMERR_CSP_INVALID_ALGORITHM
;
287 const algForms
*forms
;
289 case kSecFormatOpenSSL
:
290 forms
= opensslAlgForms
;
292 case kSecFormatBSAFE
:
293 forms
= bsafeAlgForms
;
296 forms
= ssh1AlgForms
;
298 case kSecFormatSSHv2
:
299 forms
= ssh2AlgForms
;
302 return errSecUnsupportedFormat
;
304 CSSM_KEYBLOB_FORMAT form
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
306 case kSecItemTypePrivateKey
:
307 form
= forms
[algDex
].priv
;
308 *cssmClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
310 case kSecItemTypePublicKey
:
311 form
= forms
[algDex
].pub
;
312 *cssmClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
315 return errSecUnsupportedFormat
;
317 if(form
== CSSM_KEYBLOB_RAW_FORMAT_NONE
) {
318 /* not in the tables - abort */
319 return errSecUnsupportedFormat
;
323 return errSecSuccess
;
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.
331 static bool impExpGuessKeyParams(
333 SecExternalFormat
*externForm
, // IN/OUT
334 SecExternalItemType
*itemType
, // IN/OUT
335 CSSM_ALGORITHMS
*keyAlg
) // IN/OUT
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
;
346 if(keyData
== NULL
|| CFDataGetLength(keyData
) == 0){
347 MacOSError::throwMe(errSecUnsupportedKeySize
);
350 switch(*externForm
) {
351 case kSecFormatUnknown
:
352 break; // run through all formats
353 case kSecFormatOpenSSL
:
355 case kSecFormatSSHv2
:
356 case kSecFormatBSAFE
:
357 minForm
= maxForm
= *externForm
; // just test this one
363 case kSecItemTypeUnknown
:
365 case kSecItemTypePrivateKey
:
366 case kSecItemTypePublicKey
:
367 minType
= maxType
= *itemType
;
373 case CSSM_ALGID_NONE
:
378 case CSSM_ALGID_ECDSA
:
379 minAlg
= maxAlg
= *keyAlg
;
385 CSSM_ALGORITHMS theAlg
;
386 SecExternalFormat theForm
;
387 SecExternalItemType theType
;
388 CSSM_CSP_HANDLE cspHand
= cuCspStartup(CSSM_TRUE
);
390 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
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....
399 for(theAlg
=minAlg
; ; ) {
400 for(theForm
=minForm
; ; ) {
401 for(theType
=minType
; ; ) {
403 /* do super lightweight null unwrap to parse */
404 OSStatus ortn
= impExpImportRawKey(keyData
,
405 theForm
, theType
, theAlg
,
409 NULL
, // no key params
410 NULL
, // no printName
411 NULL
); // no returned items
412 if(ortn
== errSecSuccess
) {
413 *externForm
= theForm
;
420 /* next type or break if we're done */
421 if(theType
== maxType
) {
424 else switch(theType
) {
425 case kSecItemTypePrivateKey
:
426 theType
= kSecItemTypePublicKey
;
433 } /* for each class/type */
435 /* next format or break if we're done */
436 if(theForm
== maxForm
) {
439 else switch(theForm
) {
440 case kSecFormatOpenSSL
:
441 theForm
= kSecFormatSSH
;
444 theForm
= kSecFormatBSAFE
;
446 case kSecFormatBSAFE
:
447 theForm
= kSecFormatSSHv2
;
454 } /* for each format */
456 /* next alg or break if we're done */
457 if(theAlg
== maxAlg
) {
460 else switch(theAlg
) {
462 theAlg
= CSSM_ALGID_DSA
;
465 theAlg
= CSSM_ALGID_DH
;
468 theAlg
= CSSM_ALGID_ECDSA
;
471 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
478 cuCspDetachUnload(cspHand
, CSSM_TRUE
);
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.
488 bool impExpImportGuessByExamination(
490 SecExternalFormat
*inputFormat
, // may be kSecFormatUnknown on entry
491 SecExternalItemType
*itemType
, // may be kSecItemTypeUnknown on entry
492 CSSM_ALGORITHMS
*keyAlg
) // CSSM_ALGID_NONE - unknown
494 if( ( (*inputFormat
== kSecFormatUnknown
) ||
495 (*inputFormat
== kSecFormatX509Cert
)
497 ( (*itemType
== kSecItemTypeUnknown
) ||
498 (*itemType
== kSecItemTypeCertificate
) ) ) {
500 * See if it parses as a cert
502 CSSM_CL_HANDLE clHand
= cuClStartup();
504 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
506 CSSM_HANDLE cacheHand
;
508 CSSM_DATA cdata
= { int_cast
<CFIndex
,CSSM_SIZE
>(CFDataGetLength(inData
)),
509 (uint8
*)CFDataGetBytePtr(inData
) };
510 crtn
= CSSM_CL_CertCache(clHand
, &cdata
, &cacheHand
);
512 if(crtn
== CSSM_OK
) {
513 *inputFormat
= kSecFormatX509Cert
;
514 *itemType
= kSecItemTypeCertificate
;
515 SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
516 CSSM_CL_CertAbortCache(clHand
, cacheHand
);
519 cuClDetachUnload(clHand
);
524 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
526 if( ( (*inputFormat
== kSecFormatUnknown
) ||
527 (*inputFormat
== kSecFormatNetscapeCertSequence
)
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");
541 /* See if it's a key */
542 return impExpGuessKeyParams(inData
, inputFormat
, itemType
, keyAlg
);
545 #pragma mark --- Key Import support ---
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.
552 CSSM_RETURN
impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle
,
553 uint32 AttributeType
,
554 uint32 AttributeLength
,
555 const void *AttributePtr
)
557 CSSM_CONTEXT_ATTRIBUTE newAttr
;
559 newAttr
.AttributeType
= AttributeType
;
560 newAttr
.AttributeLength
= AttributeLength
;
561 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)AttributePtr
;
562 return CSSM_UpdateContextAttributes(CCHandle
, 1, &newAttr
);
566 * Free memory via specified plugin's app-level allocator
568 void impExpFreeCssmMemory(
572 CSSM_API_MEMORY_FUNCS memFuncs
;
573 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
577 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
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).
587 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
588 * this is allocated by the CSP's app-specified allocator.
590 CSSM_RETURN
impExpKeyDigest(
591 CSSM_CSP_HANDLE cspHand
,
593 CSSM_DATA_PTR keyDigest
) // contents allocd and RETURNED
595 CSSM_DATA_PTR localDigest
;
596 CSSM_CC_HANDLE ccHand
;
598 CSSM_RETURN crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
,
604 crtn
= CSSM_CSP_PassThrough(ccHand
,
605 CSSM_APPLECSP_KEYDIGEST
,
607 (void **)&localDigest
);
609 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
613 * Give caller the Data referent and we'll free the
614 * CSSM_DATA struct itswelf.
616 *keyDigest
= *localDigest
;
617 impExpFreeCssmMemory(cspHand
, localDigest
);
619 CSSM_DeleteContext(ccHand
);
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.
629 OSStatus
impExpPassphraseToCFString(
631 CFStringRef
*passout
) // may be the same as passin, but refcounted
633 if(CFGetTypeID(passin
) == CFStringGetTypeID()) {
634 CFStringRef passInStr
= (CFStringRef
)passin
;
636 *passout
= passInStr
;
637 return errSecSuccess
;
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);
645 SecImpExpDbg("Passphrase not in UTF8 format");
649 return errSecSuccess
;
652 SecImpExpDbg("Passphrase not CFData or CFString");
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.
662 OSStatus
impExpPassphraseToCFData(
664 CFDataRef
*passout
) // may be the same as passin, but refcounted
666 if(CFGetTypeID(passin
) == CFDataGetTypeID()) {
667 CFDataRef passInData
= (CFDataRef
)passin
;
668 CFRetain(passInData
);
669 *passout
= passInData
;
670 return errSecSuccess
;
672 else if(CFGetTypeID(passin
) == CFStringGetTypeID()) {
673 CFStringRef passInStr
= (CFStringRef
)passin
;
675 outData
= CFStringCreateExternalRepresentation(NULL
,
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
,
684 kCFStringEncodingUTF8
,
686 if(outData
== NULL
) {
687 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
688 /* what do we do now, Batman? */
693 return errSecSuccess
;
696 SecImpExpDbg("Passphrase not CFData or CFString");
702 * Add a CFString to a crypto context handle.
704 static CSSM_RETURN
impExpAddStringAttr(
705 CSSM_CC_HANDLE ccHand
,
707 CSSM_ATTRIBUTE_TYPE attrType
)
709 /* CFStrings are passed as external rep in UTF8 encoding by convention */
711 outData
= CFStringCreateExternalRepresentation(NULL
,
712 str
, kCFStringEncodingUTF8
, 0); // lossByte 0 ==> no loss allowed
713 if(outData
== NULL
) {
714 SecImpExpDbg("impExpAddStringAttr: bad string format");
719 attrData
.Data
= (uint8
*)CFDataGetBytePtr(outData
);
720 attrData
.Length
= CFDataGetLength(outData
);
721 CSSM_RETURN crtn
= impExpAddContextAttribute(ccHand
, attrType
, sizeof(CSSM_DATA
),
725 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
731 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
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
740 CSSM_CC_HANDLE ccHand
;
742 CSSM_DATA dummyLabel
;
743 CSSM_KEY_PTR ourKey
= NULL
;
745 SecImpExpDbg("Generating secure passphrase key");
746 ourKey
= (CSSM_KEY_PTR
)malloc(sizeof(CSSM_KEY
));
748 return errSecAllocate
;
750 memset(ourKey
, 0, sizeof(CSSM_KEY
));
752 crtn
= CSSM_CSP_CreateKeyGenContext(cspHand
,
753 CSSM_ALGID_SECURE_PASSPHRASE
,
754 4, // keySizeInBits must be non zero
762 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
766 /* subsequent errors to errOut: */
768 /* additional context attributes specific to this type of key gen */
769 assert(keyParams
!= NULL
); // or we wouldn't be here
770 if(keyParams
->alertTitle
!= NULL
) {
771 crtn
= impExpAddStringAttr(ccHand
, keyParams
->alertTitle
,
772 CSSM_ATTRIBUTE_ALERT_TITLE
);
777 if(keyParams
->alertPrompt
!= NULL
) {
778 crtn
= impExpAddStringAttr(ccHand
, keyParams
->alertPrompt
,
779 CSSM_ATTRIBUTE_PROMPT
);
784 verifyAttr
= (verifyPhrase
== VP_Export
) ? 1 : 0;
785 crtn
= impExpAddContextAttribute(ccHand
, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE
,
786 sizeof(uint32
), (const void *)((size_t) verifyAttr
));
788 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
792 dummyLabel
.Data
= (uint8
*)"Secure Passphrase";
793 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
795 crtn
= CSSM_GenerateKey(ccHand
,
797 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
,
802 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
805 CSSM_DeleteContext(ccHand
);
806 if(crtn
== CSSM_OK
) {
809 else if(ourKey
!= NULL
) {
816 * Obtain passphrase, given a SecKeyImportExportParameters.
818 * Passphrase comes from one of two places: app-specified, in
819 * SecKeyImportExportParameters.passphrase (either as CFStringRef
820 * or CFDataRef); or via the secure passphrase mechanism.
822 * Passphrase is returned in AT MOST one of two forms:
824 * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the
825 * caller must CSSM_FreeKey later as well as free()ing the actual
827 * -- CFTypeRef for app-supplied passphrases. This can be one of
830 * -- CFStringRef, for use with P12
831 * -- CFDataRef, for more general use (e.g. for PKCS5).
833 * In either case the caller must CFRelease the result.
835 OSStatus
impExpPassphraseCommon(
836 const SecKeyImportExportParameters
*keyParams
,
837 CSSM_CSP_HANDLE cspHand
, // MUST be CSPDL, for passKey generation
838 impExpPassphraseForm phraseForm
,
839 impExpVerifyPhrase verifyPhrase
, // for secure passphrase
840 CFTypeRef
*phrase
, // RETURNED, or
841 CSSM_KEY_PTR
*passKey
) // mallocd and RETURNED
843 assert(keyParams
!= NULL
);
845 /* Give precedence to secure passphrase */
846 if(keyParams
->flags
& kSecKeySecurePassphrase
) {
847 assert(passKey
!= NULL
);
848 return impExpCreatePassKey(keyParams
, cspHand
, verifyPhrase
, passKey
);
850 else if(keyParams
->passphrase
!= NULL
) {
853 assert(phrase
!= NULL
);
856 ortn
= impExpPassphraseToCFString(keyParams
->passphrase
,
857 (CFStringRef
*)&phraseOut
);
860 ortn
= impExpPassphraseToCFData(keyParams
->passphrase
,
861 (CFDataRef
*)&phraseOut
);
864 /* only called internally */
868 if(ortn
== errSecSuccess
) {
874 return errSecPassphraseRequired
;
878 static void ToggleKeyAttribute(
881 CSSM_KEYATTR_FLAGS mask
,
882 CSSM_KEYATTR_FLAGS
&result
)
884 // If the keyAttrs array contains the given attribute,
885 // set the corresponding keyattr flags, otherwise clear them.
886 // (Note: caller verifies that keyAttrs is not NULL.)
887 CFIndex numItems
= CFArrayGetCount(keyAttrs
);
890 CFRange range
= CFRangeMake(0, numItems
);
891 if (CFArrayContainsValue(keyAttrs
, range
, attr
))
896 CSSM_KEYATTR_FLAGS
ConvertArrayToKeyAttributes(SecKeyRef aKey
, CFArrayRef keyAttrs
)
898 CSSM_KEYATTR_FLAGS result
= CSSM_KEYATTR_RETURN_DEFAULT
;
901 const CSSM_KEY
* cssmKey
= NULL
;
902 if (errSecSuccess
== SecKeyGetCSSMKey(aKey
, &cssmKey
))
903 result
= cssmKey
->KeyHeader
.KeyAttr
;
909 CFMutableArrayRef attrs
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
910 CFIndex idx
, count
= CFArrayGetCount(keyAttrs
);
911 for (idx
=0; idx
<count
; idx
++) {
912 CFTypeRef attr
= (CFTypeRef
) CFArrayGetValueAtIndex(keyAttrs
, idx
);
913 if (attr
&& (CFNumberGetTypeID() == CFGetTypeID(attr
))) {
914 // Convert numeric CSSM keyattr values to equivalent attribute constants
916 if (CFNumberGetValue((CFNumberRef
)attr
, kCFNumberSInt32Type
, &value
)) {
918 case CSSM_KEYATTR_SENSITIVE
:
919 attr
= kSecAttrIsSensitive
;
921 case CSSM_KEYATTR_EXTRACTABLE
:
922 attr
= kSecAttrIsExtractable
;
924 case CSSM_KEYATTR_PERMANENT
:
925 attr
= kSecAttrIsPermanent
;
934 CFArrayAppendValue(attrs
, attr
);
937 // Set key attribute flag in result if present in the array, otherwise clear
938 ToggleKeyAttribute(attrs
, kSecAttrIsSensitive
, CSSM_KEYATTR_SENSITIVE
, result
);
939 ToggleKeyAttribute(attrs
, kSecAttrIsExtractable
, CSSM_KEYATTR_EXTRACTABLE
, result
);
940 ToggleKeyAttribute(attrs
, kSecAttrIsPermanent
, CSSM_KEYATTR_PERMANENT
, result
);
942 // if caller specified an attributes array which omitted kSecAttrIsExtractable,
943 // this implies the sensitive attribute; force it on so that at least one bit
944 // is set. If our result is 0, this is indistinguishable from the case where
945 // no key attributes were specified, causing a default bitmask to be used
946 // in subsequent import code.
948 if (0==(result
& CSSM_KEYATTR_EXTRACTABLE
))
949 result
|= CSSM_KEYATTR_SENSITIVE
;
955 Boolean
ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey
,
956 const SecItemImportExportKeyParameters
* newPtr
, SecKeyImportExportParameters
* oldPtr
)
958 Boolean result
= false;
960 if (NULL
!= oldPtr
&& NULL
!= newPtr
)
962 oldPtr
->version
= newPtr
->version
;
963 oldPtr
->flags
= newPtr
->flags
;
964 oldPtr
->passphrase
= newPtr
->passphrase
;
965 oldPtr
->alertTitle
= newPtr
->alertTitle
;
966 oldPtr
->alertPrompt
= newPtr
->alertPrompt
;
967 oldPtr
->accessRef
= newPtr
->accessRef
;
968 oldPtr
->keyUsage
= ConvertArrayToKeyUsage(newPtr
->keyUsage
);
969 oldPtr
->keyAttributes
= ConvertArrayToKeyAttributes(aKey
, newPtr
->keyAttributes
);