2 * Copyright (c) 2003-2004 Apple Computer, 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@
28 #include "pkcs12Utils.h"
30 #include "pkcs7Templates.h"
31 #include "pkcs12Templates.h"
32 #include "pkcs12Crypto.h"
33 #include "pkcs12Debug.h"
34 #include <security_asn1/nssUtils.h>
35 #include <Security/secasn1t.h>
36 #include <security_utilities/devrandom.h>
37 #include <security_utilities/errors.h>
38 #include <security_cdsa_utils/cuCdsaUtils.h>
39 #include <Security/oidsattr.h>
40 #include <Security/oidsalg.h>
41 #include <Security/cssmapple.h>
42 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
44 /* malloc a NULL-ed array of pointers of size num+1 */
45 void **p12NssNullArray(
49 unsigned len
= (num
+ 1) * sizeof(void *);
50 void **p
= (void **)coder
.malloc(len
);
55 /* CSSM_DATA --> uint32. Returns true if OK. */
57 const CSSM_DATA
&cdata
,
60 if((cdata
.Length
== 0) || (cdata
.Data
== NULL
)) {
61 /* default/not present */
65 uint32 len
= cdata
.Length
;
66 if(len
> sizeof(uint32
)) {
71 uint8
*cp
= cdata
.Data
;
72 for(uint32 i
=0; i
<len
; i
++) {
73 rtn
= (rtn
<< 8) | *cp
++;
79 /* uint32 --> CSSM_DATA */
90 else if(num
< 0x10000) {
93 else if(num
< 0x1000000) {
99 coder
.allocItem(cdata
, len
);
100 uint8
*cp
= &cdata
.Data
[len
- 1];
101 for(unsigned i
=0; i
<len
; i
++) {
107 /* CFDataRef <--> CSSM_DATA */
108 CFDataRef
p12CssmDataToCf(
111 return CFDataCreate(NULL
, c
.Data
, c
.Length
);
114 void p12CfDataToCssm(
119 coder
.allocCopyItem(CFDataGetBytePtr(cf
),
120 CFDataGetLength(cf
), c
);
124 * Attempt to convert a CFStringRef, which represents a SafeBag's
125 * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its
126 * referent are allocated in the specified SecNssCoder's memory.
127 * No guarantee that this conversion works. If it doesn't we return
128 * NULL and caller must be prepared to deal with that.
130 CSSM_DATA_PTR
p12StringToUtf8(
137 CFIndex strLen
= CFStringGetLength(cfStr
);
141 CSSM_DATA_PTR rtn
= coder
.mallocn
<CSSM_DATA
>();
142 coder
.allocItem(*rtn
, strLen
+ 1);
143 if(!CFStringGetCString(cfStr
, (char *)rtn
->Data
,strLen
+ 1,
144 kCFStringEncodingUTF8
)) {
145 /* not convertible from native Unicode to UTF8 */
152 * Enum to string mappper.
156 * Each type of attribute has a name/value pair in a table of these:
163 /* declare one entry in a table of p12NameValuePair */
164 #define NVP(attr) {attr, #attr}
166 /* the NULL entry which terminates all p12NameValuePair tables */
167 #define NVP_END {0, NULL}
169 static const p12NameValuePair p7CITypeNames
[] =
175 NVP(CT_SignedEnvData
),
177 NVP(CT_EncryptedData
),
181 static const p12NameValuePair p12BagTypeNames
[] =
185 NVP(BT_ShroudedKeyBag
),
189 NVP(BT_SafeContentsBag
),
193 static const char *typeToStr(
195 const p12NameValuePair
*table
)
198 if(table
->value
== type
) {
206 const char *p12BagTypeStr(
207 NSS_P12_SB_Type type
)
209 return typeToStr(type
, p12BagTypeNames
);
212 const char *p7ContentInfoTypeStr(
215 return typeToStr(type
, p7CITypeNames
);
219 * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following
224 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
225 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
226 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
227 uint32 keySizeInBits
;
228 uint32 blockSizeInBytes
; // for IV, optional
229 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
230 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
231 PKCS_Which pkcs
; // PW_PKCS12 (for this module) or PW_PKCS5_v1_5
234 static const PKCSOidInfo pkcsOidInfos
[] = {
235 /* PKCS12 first, the ones this module uses */
237 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4
,
242 0, // RC4 is a stream cipher
248 &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4
,
253 0, // RC4 is a stream cipher
259 &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC
,
260 CSSM_ALGID_3DES_3KEY
,
261 CSSM_ALGID_3DES_3KEY_EDE
,
266 CSSM_ALGMODE_CBCPadIV8
,
270 &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC
,
271 CSSM_ALGID_3DES_2KEY
,
272 CSSM_ALGID_3DES_2KEY_EDE
,
277 CSSM_ALGMODE_CBCPadIV8
,
281 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC
,
288 CSSM_ALGMODE_CBCPadIV8
,
292 &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC
,
299 CSSM_ALGMODE_CBCPadIV8
,
303 /* PKCS5 v1.5, used for SecImportExport module */
305 &CSSMOID_PKCS5_pbeWithMD2AndDES
,
312 CSSM_ALGMODE_CBCPadIV8
,
316 &CSSMOID_PKCS5_pbeWithMD2AndRC2
,
323 CSSM_ALGMODE_CBCPadIV8
,
327 &CSSMOID_PKCS5_pbeWithMD5AndDES
,
334 CSSM_ALGMODE_CBCPadIV8
,
338 &CSSMOID_PKCS5_pbeWithMD5AndRC2
,
345 CSSM_ALGMODE_CBCPadIV8
,
349 &CSSMOID_PKCS5_pbeWithSHA1AndDES
,
356 CSSM_ALGMODE_CBCPadIV8
,
360 &CSSMOID_PKCS5_pbeWithSHA1AndRC2
,
367 CSSM_ALGMODE_CBCPadIV8
,
371 /* finally one for PKCS5 v2.0, which has its own means of
372 * cooking up all the parameters */
374 &CSSMOID_PKCS5_PBES2
,
383 #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
385 /* map an OID to the components */
386 /* returns false if OID not found */
389 * NOTE: as of March 8 2004 this is also used by the SecImportExport
390 * module...not just PKCS12!
392 bool pkcsOidToParams(
394 CSSM_ALGORITHMS
&keyAlg
, // e.g., CSSM_ALGID_DES
395 CSSM_ALGORITHMS
&encrAlg
, // e.g., CSSM_ALGID_3DES_3KEY_EDE
396 CSSM_ALGORITHMS
&pbeHashAlg
, // SHA1 or MD5
397 uint32
&keySizeInBits
,
398 uint32
&blockSizeInBytes
, // for IV, optional
399 CSSM_PADDING
&padding
, // CSSM_PADDING_PKCS7, etc.
400 CSSM_ENCRYPT_MODE
&mode
, // CSSM_ALGMODE_CBCPadIV8, etc.
401 PKCS_Which
&pkcs
) // PW_PKCS5_v1_5 or PW_PKCS12
403 const PKCSOidInfo
*info
= pkcsOidInfos
;
406 for(unsigned dex
=0; dex
<NUM_PKCS_OID_INFOS
; dex
++) {
407 if(nssCompareCssmData(oid
, info
->oid
)) {
408 keyAlg
= info
->keyAlg
;
409 encrAlg
= info
->encrAlg
;
410 pbeHashAlg
= info
->pbeHashAlg
;
411 keySizeInBits
= info
->keySizeInBits
;
412 blockSizeInBytes
= info
->blockSizeInBytes
;
413 padding
= info
->padding
;
424 * Verify MAC on an existing PFX.
426 CSSM_RETURN
p12VerifyMac(
427 const NSS_P12_DecodedPFX
&pfx
,
428 CSSM_CSP_HANDLE cspHand
,
429 const CSSM_DATA
*pwd
, // unicode, double null terminated
430 const CSSM_KEY
*passKey
,
431 SecNssCoder
&coder
) // for temp mallocs
433 if(pfx
.macData
== NULL
) {
434 return CSSMERR_CSP_INVALID_SIGNATURE
;
436 NSS_P12_MacData
&macData
= *pfx
.macData
;
437 NSS_P7_DigestInfo
&digestInfo
= macData
.mac
;
438 CSSM_OID
&algOid
= digestInfo
.digestAlgorithm
.algorithm
;
439 CSSM_ALGORITHMS macAlg
;
440 if(!cssmOidToAlg(&algOid
, &macAlg
)) {
441 return CSSMERR_CSP_INVALID_ALGORITHM
;
443 uint32 iterCount
= 0;
444 CSSM_DATA
&citer
= macData
.iterations
;
445 if(!p12DataToInt(citer
, iterCount
)) {
446 return CSSMERR_CSP_INVALID_ATTR_ROUNDS
;
449 /* optional, default 1 */
454 * In classic fashion, the PKCS12 spec now says:
456 * When password integrity mode is used to secure a PFX PDU,
457 * an SHA-1 HMAC is computed on the BER-encoding of the contents
458 * of the content field of the authSafe field in the PFX PDU.
463 CSSM_RETURN crtn
= p12GenMac(cspHand
, *pfx
.authSafe
.content
.data
,
464 macAlg
, iterCount
, macData
.macSalt
, pwd
, passKey
, coder
, genMac
);
468 if(nssCompareCssmData(&genMac
, &digestInfo
.digest
)) {
472 return CSSMERR_CSP_VERIFY_FAILED
;
476 /* we generate 8 random bytes of salt */
477 #define P12_SALT_LEN 8
483 DevRandomGenerator rng
;
484 coder
.allocItem(salt
, P12_SALT_LEN
);
485 rng
.random(salt
.Data
, P12_SALT_LEN
);
489 * Generate random label string to allow associating an imported private
496 /* first a random uint32 */
498 DevRandomGenerator rng
;
500 CSSM_DATA cd
= {4, d
};
504 /* sprintf that into a real string */
505 coder
.allocItem(label
, 9);
506 memset(label
.Data
, 0, 9);
507 sprintf((char *)label
.Data
, "%08X", (unsigned)i
);
510 /* NULL algorithm parameters */
512 static const uint8 nullAlg
[2] = {SEC_ASN1_NULL
, 0};
514 void p12NullAlgParams(
515 CSSM_X509_ALGORITHM_IDENTIFIER
&algId
)
517 CSSM_DATA
&p
= algId
.parameters
;
518 p
.Data
= (uint8
*)nullAlg
;
523 * Free memory via specified plugin's app-level allocator
529 CSSM_API_MEMORY_FUNCS memFuncs
;
530 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
532 p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn
);
533 /* oh well, leak and continue */
536 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
540 * Find private key by label, modify its Label attr to be the
541 * hash of the associated public key.
542 * Also optionally re-sets the key's PrintName attribute; used to reset
543 * this attr from the random label we create when first unwrap it
544 * to the friendly name we find later after parsing attributes.
545 * Detection of a duplicate key when updating the key's attributes
546 * results in a lookup of the original key and returning it in
549 CSSM_RETURN
p12SetPubKeyHash(
550 CSSM_CSP_HANDLE cspHand
, // where the key lives
551 CSSM_DL_DB_HANDLE dlDbHand
, // ditto
552 CSSM_DATA
&keyLabel
, // for DB lookup
553 CSSM_DATA_PTR newPrintName
, // optional
554 SecNssCoder
&coder
, // for mallocing newLabel
555 CSSM_DATA
&newLabel
, // RETURNED with label as hash
556 CSSM_KEY_PTR
&foundKey
) // RETURNED
559 CSSM_SELECTION_PREDICATE predicate
;
560 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
562 CSSM_HANDLE resultHand
= 0;
563 CSSM_DATA keyData
= {0, NULL
};
564 CSSM_CC_HANDLE ccHand
= 0;
565 CSSM_KEY_PTR privKey
= NULL
;
566 CSSM_DATA_PTR keyDigest
= NULL
;
568 assert(cspHand
!= 0);
569 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
570 query
.Conjunctive
= CSSM_DB_NONE
;
571 query
.NumSelectionPredicates
= 1;
572 predicate
.DbOperator
= CSSM_DB_EQUAL
;
574 predicate
.Attribute
.Info
.AttributeNameFormat
=
575 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
576 predicate
.Attribute
.Info
.Label
.AttributeName
=
577 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
578 predicate
.Attribute
.Info
.AttributeFormat
=
579 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
580 /* hope this cast is OK */
581 predicate
.Attribute
.Value
= &keyLabel
;
582 query
.SelectionPredicate
= &predicate
;
584 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
585 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
586 query
.QueryFlags
= CSSM_QUERY_RETURN_DATA
;
588 /* build Record attribute with one or two attrs */
589 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
590 CSSM_DB_ATTRIBUTE_DATA attr
[2];
591 attr
[0].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
592 attr
[0].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
593 attr
[0].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
595 attr
[1].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
596 attr
[1].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_PRINT_NAME
;
597 attr
[1].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
599 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
600 recordAttrs
.NumberOfAttributes
= newPrintName
? 2 : 1;
601 recordAttrs
.AttributeData
= attr
;
603 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
609 /* abort only on success */
610 if(crtn
!= CSSM_OK
) {
611 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
612 p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
615 /* subsequent errors to errOut: */
616 if(keyData
.Data
== NULL
) {
617 p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
618 crtn
= CSSMERR_CSSM_INTERNAL_ERROR
;
621 privKey
= (CSSM_KEY_PTR
)keyData
.Data
;
623 /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
625 * Warning! This relies on the current default ACL meaning "allow this
626 * current app to access this private key" since we created the key.
628 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
, privKey
, &ccHand
);
630 p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn
);
633 crtn
= CSSM_CSP_PassThrough(ccHand
,
634 CSSM_APPLECSP_KEYDIGEST
,
636 (void **)&keyDigest
);
638 p12LogCssmError("CSSM_CSP_PassThrough", crtn
);
643 * Replace Label attr data with hash.
644 * NOTE: the module which allocated this attribute data - a DL -
645 * was loaded and attached by out client layer, not by us. Thus
646 * we can't use the memory allocator functions *we* used when
647 * attaching to the CSP - we have to use the ones
648 * which the client registered with the DL.
650 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
->Data
);
651 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
);
653 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
->Data
);
654 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
);
656 /* modify key attributes */
657 attr
[0].Value
= keyDigest
;
659 attr
[1].Value
= newPrintName
;
661 crtn
= CSSM_DL_DataModify(dlDbHand
,
662 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
665 NULL
, // DataToBeModified
666 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
669 /* give caller the key's new label */
670 coder
.allocCopyItem(*keyDigest
, newLabel
);
673 p12LogCssmError("CSSM_DL_DataModify", crtn
);
675 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
678 * Special case: dup private key. The label we just tried to modify is
679 * the public key hash so we can be confident that this really is a dup.
680 * Delete it, look up the original, and return the original to caller.
682 CSSM_RETURN drtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
684 p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn
);
689 /* Free items created in last search */
690 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
692 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
695 /* lookup by label as public key hash this time */
696 predicate
.Attribute
.Value
= keyDigest
;
697 drtn
= CSSM_DL_DataGetFirst(dlDbHand
,
700 NULL
, // no attrs this time
704 p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn
);
708 foundKey
= (CSSM_KEY_PTR
)keyData
.Data
;
709 /* give caller the key's actual label */
710 coder
.allocCopyItem(*keyDigest
, newLabel
);
718 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
721 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
724 CSSM_DeleteContext(ccHand
);
727 /* key created by the CSPDL */
728 CSSM_FreeKey(cspHand
, NULL
, privKey
, CSSM_FALSE
);
729 freeCssmMemory(dlDbHand
.DLHandle
, privKey
);
732 /* mallocd by someone else's CSP */
733 freeCssmMemory(cspHand
, keyDigest
->Data
);
734 freeCssmMemory(cspHand
, keyDigest
);
740 * Given a context specified via a CSSM_CC_HANDLE, add a new
741 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
742 * AttributeLength, and an untyped pointer.
744 CSSM_RETURN
p12AddContextAttribute(CSSM_CC_HANDLE CCHandle
,
745 uint32 AttributeType
,
746 uint32 AttributeLength
,
747 const void *AttributePtr
)
749 CSSM_CONTEXT_ATTRIBUTE newAttr
;
752 newAttr
.AttributeType
= AttributeType
;
753 newAttr
.AttributeLength
= AttributeLength
;
754 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)AttributePtr
;
755 crtn
= CSSM_UpdateContextAttributes(CCHandle
, 1, &newAttr
);
757 p12LogCssmError("CSSM_UpdateContextAttributes", crtn
);
763 * Find private key by specified label, delete it.
765 CSSM_RETURN
p12DeleteKey(
766 CSSM_DL_DB_HANDLE dlDbHand
,
767 const CSSM_DATA
&keyLabel
)
770 CSSM_SELECTION_PREDICATE predicate
;
771 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
773 CSSM_HANDLE resultHand
= 0;
775 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
776 query
.Conjunctive
= CSSM_DB_NONE
;
777 query
.NumSelectionPredicates
= 1;
778 predicate
.DbOperator
= CSSM_DB_EQUAL
;
780 predicate
.Attribute
.Info
.AttributeNameFormat
=
781 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
782 predicate
.Attribute
.Info
.Label
.AttributeName
=
783 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
784 predicate
.Attribute
.Info
.AttributeFormat
=
785 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
786 predicate
.Attribute
.Value
= const_cast<CSSM_DATA_PTR
>(&keyLabel
);
788 query
.SelectionPredicate
= &predicate
;
789 query
.QueryLimits
.TimeLimit
= 0;
790 query
.QueryLimits
.SizeLimit
= 1;
791 query
.QueryFlags
= 0;
793 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
796 NULL
, // attrs - don't need 'em
797 NULL
, // theData - don't need it
799 /* abort only on success */
801 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
802 p12ErrorLog("***p12DeleteKey: can't find private key\n");
806 crtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
808 p12LogCssmError("CSSM_DL_DataDelete", crtn
);
809 p12ErrorLog("***p12DeleteKey: can't delete private key\n");
812 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
813 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
817 /* convert App passphrase to array of chars used in P12 PBE */
818 void p12ImportPassPhrase(
819 CFStringRef inPhrase
,
821 CSSM_DATA
&outPhrase
)
823 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
824 inPhrase
, kCFStringEncodingUTF8
, 0);
826 p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
827 MacOSError::throwMe(paramErr
);
829 unsigned keyLen
= CFDataGetLength(cfData
);
830 coder
.allocItem(outPhrase
, keyLen
);
831 memmove(outPhrase
.Data
, CFDataGetBytePtr(cfData
), keyLen
);