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 "SecIdentityPriv.h"
31 #include <security_cdsa_utils/cuCdsaUtils.h>
32 #include <Security/SecBase.h>
33 #pragma mark --- Debug support ---
37 const char *impExpExtFormatStr(
38 SecExternalFormat 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";
59 const char *impExpExtItemTypeStr(
60 SecExternalItemType 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";
75 * Parse file extension and attempt to map it to format and type. Returns true
78 bool impExpImportParseFileExten(
80 SecExternalFormat
*inputFormat
, // RETURNED
81 SecExternalItemType
*itemType
) // RETURNED
84 /* nothing to work with */
87 if(CFStringHasSuffix(fstr
, CFSTR(".cer")) ||
88 CFStringHasSuffix(fstr
, CFSTR(".crt"))) {
89 *inputFormat
= kSecFormatX509Cert
;
90 *itemType
= kSecItemTypeCertificate
;
91 SecImpInferDbg("Inferring kSecFormatX509Cert from file name");
94 if(CFStringHasSuffix(fstr
, CFSTR(".p12")) ||
95 CFStringHasSuffix(fstr
, CFSTR(".pfx"))) {
96 *inputFormat
= kSecFormatPKCS12
;
97 *itemType
= kSecItemTypeAggregate
;
98 SecImpInferDbg("Inferring kSecFormatPKCS12 from file name");
102 /* Get extension, look for key indicators as substrings */
103 CFURLRef url
= CFURLCreateWithString(NULL
, fstr
, NULL
);
105 SecImpInferDbg("impExpImportParseFileExten: error creating URL");
108 CFStringRef exten
= CFURLCopyPathExtension(url
);
111 /* no extension, app probably passed in only an extension */
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");
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");
137 /* do a [NSString stringByDeletingPathExtension] equivalent */
138 CFStringRef
impExpImportDeleteExtension(
141 CFDataRef fileStrData
= CFStringCreateExternalRepresentation(NULL
, fileStr
,
142 kCFStringEncodingUTF8
, 0);
143 if(fileStrData
== NULL
) {
147 CFURLRef urlRef
= CFURLCreateFromFileSystemRepresentation(NULL
,
148 CFDataGetBytePtr(fileStrData
), CFDataGetLength(fileStrData
), false);
150 CFRelease(fileStrData
);
153 CFURLRef rtnUrl
= CFURLCreateCopyDeletingPathExtension(NULL
, urlRef
);
154 CFStringRef rtnStr
= NULL
;
157 rtnStr
= CFURLGetString(rtnUrl
);
161 CFRelease(fileStrData
);
165 #pragma mark --- mapping of external format to CDSA formats ---
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.
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
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.
190 /* Arrays expressing the above table. */
192 /* l.s. dimension is pub/priv for one alg */
194 CSSM_KEYBLOB_FORMAT priv
;
195 CSSM_KEYBLOB_FORMAT pub
;
199 * indices into array of algForms defining all algs' formats for a given
202 #define SIE_ALG_RSA 0
203 #define SIE_ALG_DSA 1
205 #define SIE_ALG_ECDSA 3
206 #define SIE_ALG_LAST SIE_ALG_ECDSA
207 #define SIE_NUM_ALGS (SIE_ALG_LAST + 1)
209 /* kSecFormatOpenSSL */
210 static algForms opensslAlgForms
[SIE_NUM_ALGS
] =
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
218 /* kSecFormatBSAFE */
219 static algForms bsafeAlgForms
[SIE_NUM_ALGS
] =
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
227 /* kSecFormatSSH (v1) */
228 static algForms ssh1AlgForms
[SIE_NUM_ALGS
] =
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
236 /* kSecFormatSSHv2 */
237 static algForms ssh2AlgForms
[SIE_NUM_ALGS
] =
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
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.
250 OSStatus
impExpKeyForm(
251 SecExternalFormat externForm
,
252 SecExternalItemType itemType
,
254 CSSM_KEYBLOB_FORMAT
*cssmForm
, // RETURNED
255 CSSM_KEYCLASS
*cssmClass
) // RETRUNED
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
;
264 if(externForm
== kSecFormatUnknown
) {
265 /* default is openssl */
266 externForm
= kSecFormatOpenSSL
;
272 algDex
= SIE_ALG_RSA
;
275 algDex
= SIE_ALG_DSA
;
280 case CSSM_ALGID_ECDSA
:
281 algDex
= SIE_ALG_ECDSA
;
284 return CSSMERR_CSP_INVALID_ALGORITHM
;
286 const algForms
*forms
;
288 case kSecFormatOpenSSL
:
289 forms
= opensslAlgForms
;
291 case kSecFormatBSAFE
:
292 forms
= bsafeAlgForms
;
295 forms
= ssh1AlgForms
;
297 case kSecFormatSSHv2
:
298 forms
= ssh2AlgForms
;
301 return errSecUnsupportedFormat
;
303 CSSM_KEYBLOB_FORMAT form
= CSSM_KEYBLOB_RAW_FORMAT_NONE
;
305 case kSecItemTypePrivateKey
:
306 form
= forms
[algDex
].priv
;
307 *cssmClass
= CSSM_KEYCLASS_PRIVATE_KEY
;
309 case kSecItemTypePublicKey
:
310 form
= forms
[algDex
].pub
;
311 *cssmClass
= CSSM_KEYCLASS_PUBLIC_KEY
;
314 return errSecUnsupportedFormat
;
316 if(form
== CSSM_KEYBLOB_RAW_FORMAT_NONE
) {
317 /* not in the tables - abort */
318 return errSecUnsupportedFormat
;
322 return errSecSuccess
;
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.
330 static bool impExpGuessKeyParams(
332 SecExternalFormat
*externForm
, // IN/OUT
333 SecExternalItemType
*itemType
, // IN/OUT
334 CSSM_ALGORITHMS
*keyAlg
) // IN/OUT
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
;
345 if(keyData
== NULL
|| CFDataGetLength(keyData
) == 0){
346 MacOSError::throwMe(errSecUnsupportedKeySize
);
349 switch(*externForm
) {
350 case kSecFormatUnknown
:
351 break; // run through all formats
352 case kSecFormatOpenSSL
:
354 case kSecFormatSSHv2
:
355 case kSecFormatBSAFE
:
356 minForm
= maxForm
= *externForm
; // just test this one
362 case kSecItemTypeUnknown
:
364 case kSecItemTypePrivateKey
:
365 case kSecItemTypePublicKey
:
366 minType
= maxType
= *itemType
;
372 case CSSM_ALGID_NONE
:
377 case CSSM_ALGID_ECDSA
:
378 minAlg
= maxAlg
= *keyAlg
;
384 CSSM_ALGORITHMS theAlg
;
385 SecExternalFormat theForm
;
386 SecExternalItemType theType
;
387 CSSM_CSP_HANDLE cspHand
= cuCspStartup(CSSM_TRUE
);
389 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
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....
398 for(theAlg
=minAlg
; ; ) {
399 for(theForm
=minForm
; ; ) {
400 for(theType
=minType
; ; ) {
402 /* do super lightweight null unwrap to parse */
403 OSStatus ortn
= impExpImportRawKey(keyData
,
404 theForm
, theType
, theAlg
,
408 NULL
, // no key params
409 NULL
, // no printName
410 NULL
); // no returned items
411 if(ortn
== errSecSuccess
) {
412 *externForm
= theForm
;
419 /* next type or break if we're done */
420 if(theType
== maxType
) {
423 else switch(theType
) {
424 case kSecItemTypePrivateKey
:
425 theType
= kSecItemTypePublicKey
;
432 } /* for each class/type */
434 /* next format or break if we're done */
435 if(theForm
== maxForm
) {
438 else switch(theForm
) {
439 case kSecFormatOpenSSL
:
440 theForm
= kSecFormatSSH
;
443 theForm
= kSecFormatBSAFE
;
445 case kSecFormatBSAFE
:
446 theForm
= kSecFormatSSHv2
;
453 } /* for each format */
455 /* next alg or break if we're done */
456 if(theAlg
== maxAlg
) {
459 else switch(theAlg
) {
461 theAlg
= CSSM_ALGID_DSA
;
464 theAlg
= CSSM_ALGID_DH
;
467 theAlg
= CSSM_ALGID_ECDSA
;
470 /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */
477 cuCspDetachUnload(cspHand
, CSSM_TRUE
);
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.
487 bool impExpImportGuessByExamination(
489 SecExternalFormat
*inputFormat
, // may be kSecFormatUnknown on entry
490 SecExternalItemType
*itemType
, // may be kSecItemTypeUnknown on entry
491 CSSM_ALGORITHMS
*keyAlg
) // CSSM_ALGID_NONE - unknown
493 if( ( (*inputFormat
== kSecFormatUnknown
) ||
494 (*inputFormat
== kSecFormatX509Cert
)
496 ( (*itemType
== kSecItemTypeUnknown
) ||
497 (*itemType
== kSecItemTypeCertificate
) ) ) {
499 * See if it parses as a cert
501 CSSM_CL_HANDLE clHand
= cuClStartup();
503 return CSSMERR_CSSM_ADDIN_LOAD_FAILED
;
505 CSSM_HANDLE cacheHand
;
507 CSSM_DATA cdata
= { CFDataGetLength(inData
),
508 (uint8
*)CFDataGetBytePtr(inData
) };
509 crtn
= CSSM_CL_CertCache(clHand
, &cdata
, &cacheHand
);
511 if(crtn
== CSSM_OK
) {
512 *inputFormat
= kSecFormatX509Cert
;
513 *itemType
= kSecItemTypeCertificate
;
514 SecImpInferDbg("Inferred kSecFormatX509Cert via CL");
515 CSSM_CL_CertAbortCache(clHand
, cacheHand
);
518 cuClDetachUnload(clHand
);
523 /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */
525 if( ( (*inputFormat
== kSecFormatUnknown
) ||
526 (*inputFormat
== kSecFormatNetscapeCertSequence
)
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");
540 /* See if it's a key */
541 return impExpGuessKeyParams(inData
, inputFormat
, itemType
, keyAlg
);
544 #pragma mark --- Key Import support ---
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.
551 CSSM_RETURN
impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle
,
552 uint32 AttributeType
,
553 uint32 AttributeLength
,
554 const void *AttributePtr
)
556 CSSM_CONTEXT_ATTRIBUTE newAttr
;
558 newAttr
.AttributeType
= AttributeType
;
559 newAttr
.AttributeLength
= AttributeLength
;
560 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)AttributePtr
;
561 return CSSM_UpdateContextAttributes(CCHandle
, 1, &newAttr
);
565 * Free memory via specified plugin's app-level allocator
567 void impExpFreeCssmMemory(
571 CSSM_API_MEMORY_FUNCS memFuncs
;
572 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
576 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
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).
586 * Caller must free keyDigest->Data using impExpFreeCssmMemory() since
587 * this is allocated by the CSP's app-specified allocator.
589 CSSM_RETURN
impExpKeyDigest(
590 CSSM_CSP_HANDLE cspHand
,
592 CSSM_DATA_PTR keyDigest
) // contents allocd and RETURNED
594 CSSM_DATA_PTR localDigest
;
595 CSSM_CC_HANDLE ccHand
;
597 CSSM_RETURN crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
,
603 crtn
= CSSM_CSP_PassThrough(ccHand
,
604 CSSM_APPLECSP_KEYDIGEST
,
606 (void **)&localDigest
);
608 SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure");
612 * Give caller the Data referent and we'll free the
613 * CSSM_DATA struct itswelf.
615 *keyDigest
= *localDigest
;
616 impExpFreeCssmMemory(cspHand
, localDigest
);
618 CSSM_DeleteContext(ccHand
);
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.
628 OSStatus
impExpPassphraseToCFString(
630 CFStringRef
*passout
) // may be the same as passin, but refcounted
632 if(CFGetTypeID(passin
) == CFStringGetTypeID()) {
633 CFStringRef passInStr
= (CFStringRef
)passin
;
635 *passout
= passInStr
;
636 return errSecSuccess
;
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);
644 SecImpExpDbg("Passphrase not in UTF8 format");
648 return errSecSuccess
;
651 SecImpExpDbg("Passphrase not CFData or CFString");
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.
661 OSStatus
impExpPassphraseToCFData(
663 CFDataRef
*passout
) // may be the same as passin, but refcounted
665 if(CFGetTypeID(passin
) == CFDataGetTypeID()) {
666 CFDataRef passInData
= (CFDataRef
)passin
;
667 CFRetain(passInData
);
668 *passout
= passInData
;
669 return errSecSuccess
;
671 else if(CFGetTypeID(passin
) == CFStringGetTypeID()) {
672 CFStringRef passInStr
= (CFStringRef
)passin
;
674 outData
= CFStringCreateExternalRepresentation(NULL
,
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
,
683 kCFStringEncodingUTF8
,
685 if(outData
== NULL
) {
686 SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8");
687 /* what do we do now, Batman? */
692 return errSecSuccess
;
695 SecImpExpDbg("Passphrase not CFData or CFString");
701 * Add a CFString to a crypto context handle.
703 static CSSM_RETURN
impExpAddStringAttr(
704 CSSM_CC_HANDLE ccHand
,
706 CSSM_ATTRIBUTE_TYPE attrType
)
708 /* CFStrings are passed as external rep in UTF8 encoding by convention */
710 outData
= CFStringCreateExternalRepresentation(NULL
,
711 str
, kCFStringEncodingUTF8
, 0); // lossByte 0 ==> no loss allowed
712 if(outData
== NULL
) {
713 SecImpExpDbg("impExpAddStringAttr: bad string format");
718 attrData
.Data
= (uint8
*)CFDataGetBytePtr(outData
);
719 attrData
.Length
= CFDataGetLength(outData
);
720 CSSM_RETURN crtn
= impExpAddContextAttribute(ccHand
, attrType
, sizeof(CSSM_DATA
),
724 SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error");
730 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
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
739 CSSM_CC_HANDLE ccHand
;
741 CSSM_DATA dummyLabel
;
742 CSSM_KEY_PTR ourKey
= NULL
;
744 SecImpExpDbg("Generating secure passphrase key");
745 ourKey
= (CSSM_KEY_PTR
)malloc(sizeof(CSSM_KEY
));
747 return errSecAllocate
;
749 memset(ourKey
, 0, sizeof(CSSM_KEY
));
751 crtn
= CSSM_CSP_CreateKeyGenContext(cspHand
,
752 CSSM_ALGID_SECURE_PASSPHRASE
,
753 4, // keySizeInBits must be non zero
761 SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error");
764 /* subsequent errors to errOut: */
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
);
775 if(keyParams
->alertPrompt
!= NULL
) {
776 crtn
= impExpAddStringAttr(ccHand
, keyParams
->alertPrompt
,
777 CSSM_ATTRIBUTE_PROMPT
);
782 verifyAttr
= (verifyPhrase
== VP_Export
) ? 1 : 0;
783 crtn
= impExpAddContextAttribute(ccHand
, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE
,
784 sizeof(uint32
), (const void *)((size_t) verifyAttr
));
786 SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error");
790 dummyLabel
.Data
= (uint8
*)"Secure Passphrase";
791 dummyLabel
.Length
= strlen((char *)dummyLabel
.Data
);
793 crtn
= CSSM_GenerateKey(ccHand
,
795 CSSM_KEYATTR_RETURN_REF
| CSSM_KEYATTR_SENSITIVE
,
800 SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error");
803 CSSM_DeleteContext(ccHand
);
804 if(crtn
== CSSM_OK
) {
807 else if(ourKey
!= NULL
) {
814 * Obtain passphrase, given a SecKeyImportExportParameters.
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.
820 * Passphrase is returned in AT MOST one of two forms:
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
825 * -- CFTypeRef for app-supplied passphrases. This can be one of
828 * -- CFStringRef, for use with P12
829 * -- CFDataRef, for more general use (e.g. for PKCS5).
831 * In either case the caller must CFRelease the result.
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
841 assert(keyParams
!= NULL
);
843 /* Give precedence to secure passphrase */
844 if(keyParams
->flags
& kSecKeySecurePassphrase
) {
845 assert(passKey
!= NULL
);
846 return impExpCreatePassKey(keyParams
, cspHand
, verifyPhrase
, passKey
);
848 else if(keyParams
->passphrase
!= NULL
) {
851 assert(phrase
!= NULL
);
854 ortn
= impExpPassphraseToCFString(keyParams
->passphrase
,
855 (CFStringRef
*)&phraseOut
);
858 ortn
= impExpPassphraseToCFData(keyParams
->passphrase
,
859 (CFDataRef
*)&phraseOut
);
862 /* only called internally */
866 if(ortn
== errSecSuccess
) {
872 return errSecPassphraseRequired
;
876 static void ToggleKeyAttribute(
879 CSSM_KEYATTR_FLAGS mask
,
880 CSSM_KEYATTR_FLAGS
&result
)
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
);
888 CFRange range
= CFRangeMake(0, numItems
);
889 if (CFArrayContainsValue(keyAttrs
, range
, attr
))
894 CSSM_KEYATTR_FLAGS
ConvertArrayToKeyAttributes(SecKeyRef aKey
, CFArrayRef keyAttrs
)
896 CSSM_KEYATTR_FLAGS result
= CSSM_KEYATTR_RETURN_DEFAULT
;
899 const CSSM_KEY
* cssmKey
= NULL
;
900 if (errSecSuccess
== SecKeyGetCSSMKey(aKey
, &cssmKey
))
901 result
= cssmKey
->KeyHeader
.KeyAttr
;
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
914 if (CFNumberGetValue((CFNumberRef
)attr
, kCFNumberSInt32Type
, &value
)) {
916 case CSSM_KEYATTR_SENSITIVE
:
917 attr
= kSecAttrIsSensitive
;
919 case CSSM_KEYATTR_EXTRACTABLE
:
920 attr
= kSecAttrIsExtractable
;
922 case CSSM_KEYATTR_PERMANENT
:
923 attr
= kSecAttrIsPermanent
;
932 CFArrayAppendValue(attrs
, attr
);
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
);
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.
946 if (0==(result
& CSSM_KEYATTR_EXTRACTABLE
))
947 result
|= CSSM_KEYATTR_SENSITIVE
;
953 Boolean
ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey
,
954 const SecItemImportExportKeyParameters
* newPtr
, SecKeyImportExportParameters
* oldPtr
)
956 Boolean result
= false;
958 if (NULL
!= oldPtr
&& NULL
!= newPtr
)
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
);