2 * Copyright (c) 2003-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@
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>
43 /* malloc a NULL-ed array of pointers of size num+1 */
44 void **p12NssNullArray(
48 unsigned len
= (num
+ 1) * sizeof(void *);
49 void **p
= (void **)coder
.malloc(len
);
54 /* CSSM_DATA --> uint32. Returns true if OK. */
56 const CSSM_DATA
&cdata
,
59 if((cdata
.Length
== 0) || (cdata
.Data
== NULL
)) {
60 /* default/not present */
64 CSSM_SIZE len
= cdata
.Length
;
65 if(len
> sizeof(uint32
)) {
70 uint8
*cp
= cdata
.Data
;
71 for(uint32 i
=0; i
<len
; i
++) {
72 rtn
= (rtn
<< 8) | *cp
++;
78 /* uint32 --> CSSM_DATA */
89 else if(num
< 0x10000) {
92 else if(num
< 0x1000000) {
98 coder
.allocItem(cdata
, len
);
99 uint8
*cp
= &cdata
.Data
[len
- 1];
100 for(unsigned i
=0; i
<len
; i
++) {
106 /* CFDataRef <--> CSSM_DATA */
107 CFDataRef
p12CssmDataToCf(
110 return CFDataCreate(NULL
, c
.Data
, c
.Length
);
113 void p12CfDataToCssm(
118 coder
.allocCopyItem(CFDataGetBytePtr(cf
),
119 CFDataGetLength(cf
), c
);
123 * Attempt to convert a CFStringRef, which represents a SafeBag's
124 * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its
125 * referent are allocated in the specified SecNssCoder's memory.
126 * No guarantee that this conversion works. If it doesn't we return
127 * NULL and caller must be prepared to deal with that.
129 CSSM_DATA_PTR
p12StringToUtf8(
136 CFIndex strLen
= CFStringGetLength(cfStr
);
140 CSSM_DATA_PTR rtn
= coder
.mallocn
<CSSM_DATA
>();
141 coder
.allocItem(*rtn
, strLen
+ 1);
142 if(!CFStringGetCString(cfStr
, (char *)rtn
->Data
,strLen
+ 1,
143 kCFStringEncodingUTF8
)) {
144 /* not convertible from native Unicode to UTF8 */
151 * Enum to string mappper.
155 * Each type of attribute has a name/value pair in a table of these:
162 /* declare one entry in a table of p12NameValuePair */
163 #define NVP(attr) {attr, #attr}
165 /* the NULL entry which terminates all p12NameValuePair tables */
166 #define NVP_END {0, NULL}
168 static const p12NameValuePair p7CITypeNames
[] =
174 NVP(CT_SignedEnvData
),
176 NVP(CT_EncryptedData
),
180 static const p12NameValuePair p12BagTypeNames
[] =
184 NVP(BT_ShroudedKeyBag
),
188 NVP(BT_SafeContentsBag
),
192 static const char *typeToStr(
194 const p12NameValuePair
*table
)
197 if(table
->value
== type
) {
205 const char *p12BagTypeStr(
206 NSS_P12_SB_Type type
)
208 return typeToStr(type
, p12BagTypeNames
);
211 const char *p7ContentInfoTypeStr(
214 return typeToStr(type
, p7CITypeNames
);
218 * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following
223 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
224 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
225 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
226 uint32 keySizeInBits
;
227 uint32 blockSizeInBytes
; // for IV, optional
228 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
229 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
230 PKCS_Which pkcs
; // PW_PKCS12 (for this module) or PW_PKCS5_v1_5
233 static const PKCSOidInfo pkcsOidInfos
[] = {
234 /* PKCS12 first, the ones this module uses */
236 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4
,
241 0, // RC4 is a stream cipher
247 &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4
,
252 0, // RC4 is a stream cipher
258 &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC
,
259 CSSM_ALGID_3DES_3KEY
,
260 CSSM_ALGID_3DES_3KEY_EDE
,
265 CSSM_ALGMODE_CBCPadIV8
,
269 &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC
,
270 CSSM_ALGID_3DES_2KEY
,
271 CSSM_ALGID_3DES_2KEY_EDE
,
276 CSSM_ALGMODE_CBCPadIV8
,
280 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC
,
287 CSSM_ALGMODE_CBCPadIV8
,
291 &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC
,
298 CSSM_ALGMODE_CBCPadIV8
,
302 /* PKCS5 v1.5, used for SecImportExport module */
304 &CSSMOID_PKCS5_pbeWithMD2AndDES
,
311 CSSM_ALGMODE_CBCPadIV8
,
315 &CSSMOID_PKCS5_pbeWithMD2AndRC2
,
322 CSSM_ALGMODE_CBCPadIV8
,
326 &CSSMOID_PKCS5_pbeWithMD5AndDES
,
333 CSSM_ALGMODE_CBCPadIV8
,
337 &CSSMOID_PKCS5_pbeWithMD5AndRC2
,
344 CSSM_ALGMODE_CBCPadIV8
,
348 &CSSMOID_PKCS5_pbeWithSHA1AndDES
,
355 CSSM_ALGMODE_CBCPadIV8
,
359 &CSSMOID_PKCS5_pbeWithSHA1AndRC2
,
366 CSSM_ALGMODE_CBCPadIV8
,
370 /* finally one for PKCS5 v2.0, which has its own means of
371 * cooking up all the parameters */
373 &CSSMOID_PKCS5_PBES2
,
382 #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
384 /* map an OID to the components */
385 /* returns false if OID not found */
388 * NOTE: as of March 8 2004 this is also used by the SecImportExport
389 * module...not just PKCS12!
391 bool pkcsOidToParams(
393 CSSM_ALGORITHMS
&keyAlg
, // e.g., CSSM_ALGID_DES
394 CSSM_ALGORITHMS
&encrAlg
, // e.g., CSSM_ALGID_3DES_3KEY_EDE
395 CSSM_ALGORITHMS
&pbeHashAlg
, // SHA1 or MD5
396 uint32
&keySizeInBits
,
397 uint32
&blockSizeInBytes
, // for IV, optional
398 CSSM_PADDING
&padding
, // CSSM_PADDING_PKCS7, etc.
399 CSSM_ENCRYPT_MODE
&mode
, // CSSM_ALGMODE_CBCPadIV8, etc.
400 PKCS_Which
&pkcs
) // PW_PKCS5_v1_5 or PW_PKCS12
402 const PKCSOidInfo
*info
= pkcsOidInfos
;
405 for(unsigned dex
=0; dex
<NUM_PKCS_OID_INFOS
; dex
++) {
406 if(nssCompareCssmData(oid
, info
->oid
)) {
407 keyAlg
= info
->keyAlg
;
408 encrAlg
= info
->encrAlg
;
409 pbeHashAlg
= info
->pbeHashAlg
;
410 keySizeInBits
= info
->keySizeInBits
;
411 blockSizeInBytes
= info
->blockSizeInBytes
;
412 padding
= info
->padding
;
423 * Verify MAC on an existing PFX.
425 CSSM_RETURN
p12VerifyMac(
426 const NSS_P12_DecodedPFX
&pfx
,
427 CSSM_CSP_HANDLE cspHand
,
428 const CSSM_DATA
*pwd
, // unicode, double null terminated
429 const CSSM_KEY
*passKey
,
430 SecNssCoder
&coder
) // for temp mallocs
432 if(pfx
.macData
== NULL
) {
433 return CSSMERR_CSP_INVALID_SIGNATURE
;
435 NSS_P12_MacData
&macData
= *pfx
.macData
;
436 NSS_P7_DigestInfo
&digestInfo
= macData
.mac
;
437 CSSM_OID
&algOid
= digestInfo
.digestAlgorithm
.algorithm
;
438 CSSM_ALGORITHMS macAlg
;
439 if(!cssmOidToAlg(&algOid
, &macAlg
)) {
440 return CSSMERR_CSP_INVALID_ALGORITHM
;
442 uint32 iterCount
= 0;
443 CSSM_DATA
&citer
= macData
.iterations
;
444 if(!p12DataToInt(citer
, iterCount
)) {
445 return CSSMERR_CSP_INVALID_ATTR_ROUNDS
;
448 /* optional, default 1 */
453 * In classic fashion, the PKCS12 spec now says:
455 * When password integrity mode is used to secure a PFX PDU,
456 * an SHA-1 HMAC is computed on the BER-encoding of the contents
457 * of the content field of the authSafe field in the PFX PDU.
462 CSSM_RETURN crtn
= p12GenMac(cspHand
, *pfx
.authSafe
.content
.data
,
463 macAlg
, iterCount
, macData
.macSalt
, pwd
, passKey
, coder
, genMac
);
467 if(nssCompareCssmData(&genMac
, &digestInfo
.digest
)) {
471 return CSSMERR_CSP_VERIFY_FAILED
;
475 /* we generate 8 random bytes of salt */
476 #define P12_SALT_LEN 8
482 DevRandomGenerator rng
;
483 coder
.allocItem(salt
, P12_SALT_LEN
);
484 rng
.random(salt
.Data
, P12_SALT_LEN
);
488 * Generate random label string to allow associating an imported private
495 /* first a random uint32 */
497 DevRandomGenerator rng
;
499 CSSM_DATA cd
= {4, d
};
503 /* sprintf that into a real string */
504 coder
.allocItem(label
, 9);
505 memset(label
.Data
, 0, 9);
506 sprintf((char *)label
.Data
, "%08X", (unsigned)i
);
509 /* NULL algorithm parameters */
511 static const uint8 nullAlg
[2] = {SEC_ASN1_NULL
, 0};
513 void p12NullAlgParams(
514 CSSM_X509_ALGORITHM_IDENTIFIER
&algId
)
516 CSSM_DATA
&p
= algId
.parameters
;
517 p
.Data
= (uint8
*)nullAlg
;
522 * Free memory via specified plugin's app-level allocator
528 CSSM_API_MEMORY_FUNCS memFuncs
;
529 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
531 p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn
);
532 /* oh well, leak and continue */
535 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
539 * Find private key by label, modify its Label attr to be the
540 * hash of the associated public key.
541 * Also optionally re-sets the key's PrintName attribute; used to reset
542 * this attr from the random label we create when first unwrap it
543 * to the friendly name we find later after parsing attributes.
544 * Detection of a duplicate key when updating the key's attributes
545 * results in a lookup of the original key and returning it in
548 CSSM_RETURN
p12SetPubKeyHash(
549 CSSM_CSP_HANDLE cspHand
, // where the key lives
550 CSSM_DL_DB_HANDLE dlDbHand
, // ditto
551 CSSM_DATA
&keyLabel
, // for DB lookup
552 CSSM_DATA_PTR newPrintName
, // optional
553 SecNssCoder
&coder
, // for mallocing newLabel
554 CSSM_DATA
&newLabel
, // RETURNED with label as hash
555 CSSM_KEY_PTR
&foundKey
) // RETURNED
558 CSSM_SELECTION_PREDICATE predicate
;
559 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
561 CSSM_HANDLE resultHand
= 0;
562 CSSM_DATA keyData
= {0, NULL
};
563 CSSM_CC_HANDLE ccHand
= 0;
564 CSSM_KEY_PTR privKey
= NULL
;
565 CSSM_DATA_PTR keyDigest
= NULL
;
567 assert(cspHand
!= 0);
568 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
569 query
.Conjunctive
= CSSM_DB_NONE
;
570 query
.NumSelectionPredicates
= 1;
571 predicate
.DbOperator
= CSSM_DB_EQUAL
;
573 predicate
.Attribute
.Info
.AttributeNameFormat
=
574 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
575 predicate
.Attribute
.Info
.Label
.AttributeName
=
576 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
577 predicate
.Attribute
.Info
.AttributeFormat
=
578 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
579 /* hope this cast is OK */
580 predicate
.Attribute
.Value
= &keyLabel
;
581 query
.SelectionPredicate
= &predicate
;
583 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
584 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
585 query
.QueryFlags
= CSSM_QUERY_RETURN_DATA
;
587 /* build Record attribute with one or two attrs */
588 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
589 CSSM_DB_ATTRIBUTE_DATA attr
[2];
590 attr
[0].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
591 attr
[0].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
592 attr
[0].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
594 attr
[1].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
595 attr
[1].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_PRINT_NAME
;
596 attr
[1].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
598 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
599 recordAttrs
.NumberOfAttributes
= newPrintName
? 2 : 1;
600 recordAttrs
.AttributeData
= attr
;
602 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
608 /* abort only on success */
609 if(crtn
!= CSSM_OK
) {
610 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
611 p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
614 /* subsequent errors to errOut: */
615 if(keyData
.Data
== NULL
) {
616 p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
617 crtn
= CSSMERR_CSSM_INTERNAL_ERROR
;
620 privKey
= (CSSM_KEY_PTR
)keyData
.Data
;
622 /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
624 * Warning! This relies on the current default ACL meaning "allow this
625 * current app to access this private key" since we created the key.
627 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
, privKey
, &ccHand
);
629 p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn
);
632 crtn
= CSSM_CSP_PassThrough(ccHand
,
633 CSSM_APPLECSP_KEYDIGEST
,
635 (void **)&keyDigest
);
637 p12LogCssmError("CSSM_CSP_PassThrough", crtn
);
642 * Replace Label attr data with hash.
643 * NOTE: the module which allocated this attribute data - a DL -
644 * was loaded and attached by out client layer, not by us. Thus
645 * we can't use the memory allocator functions *we* used when
646 * attaching to the CSP - we have to use the ones
647 * which the client registered with the DL.
649 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
->Data
);
650 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
);
652 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
->Data
);
653 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
);
655 /* modify key attributes */
656 attr
[0].Value
= keyDigest
;
658 attr
[1].Value
= newPrintName
;
660 crtn
= CSSM_DL_DataModify(dlDbHand
,
661 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
664 NULL
, // DataToBeModified
665 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
668 /* give caller the key's new label */
669 coder
.allocCopyItem(*keyDigest
, newLabel
);
672 p12LogCssmError("CSSM_DL_DataModify", crtn
);
674 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
677 * Special case: dup private key. The label we just tried to modify is
678 * the public key hash so we can be confident that this really is a dup.
679 * Delete it, look up the original, and return the original to caller.
681 CSSM_RETURN drtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
683 p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn
);
688 /* Free items created in last search */
689 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
691 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
694 /* lookup by label as public key hash this time */
695 predicate
.Attribute
.Value
= keyDigest
;
696 drtn
= CSSM_DL_DataGetFirst(dlDbHand
,
699 NULL
, // no attrs this time
703 p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn
);
707 foundKey
= (CSSM_KEY_PTR
)keyData
.Data
;
708 /* give caller the key's actual label */
709 coder
.allocCopyItem(*keyDigest
, newLabel
);
717 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
720 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
723 CSSM_DeleteContext(ccHand
);
726 /* key created by the CSPDL */
727 CSSM_FreeKey(cspHand
, NULL
, privKey
, CSSM_FALSE
);
728 freeCssmMemory(dlDbHand
.DLHandle
, privKey
);
731 /* mallocd by someone else's CSP */
732 freeCssmMemory(cspHand
, keyDigest
->Data
);
733 freeCssmMemory(cspHand
, keyDigest
);
739 * Given a context specified via a CSSM_CC_HANDLE, add a new
740 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
741 * AttributeLength, and an untyped pointer.
743 CSSM_RETURN
p12AddContextAttribute(CSSM_CC_HANDLE CCHandle
,
744 uint32 AttributeType
,
745 uint32 AttributeLength
,
746 const void *AttributePtr
)
748 CSSM_CONTEXT_ATTRIBUTE newAttr
;
751 newAttr
.AttributeType
= AttributeType
;
752 newAttr
.AttributeLength
= AttributeLength
;
753 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)AttributePtr
;
754 crtn
= CSSM_UpdateContextAttributes(CCHandle
, 1, &newAttr
);
756 p12LogCssmError("CSSM_UpdateContextAttributes", crtn
);
762 * Find private key by specified label, delete it.
764 CSSM_RETURN
p12DeleteKey(
765 CSSM_DL_DB_HANDLE dlDbHand
,
766 const CSSM_DATA
&keyLabel
)
769 CSSM_SELECTION_PREDICATE predicate
;
770 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
772 CSSM_HANDLE resultHand
= 0;
774 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
775 query
.Conjunctive
= CSSM_DB_NONE
;
776 query
.NumSelectionPredicates
= 1;
777 predicate
.DbOperator
= CSSM_DB_EQUAL
;
779 predicate
.Attribute
.Info
.AttributeNameFormat
=
780 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
781 predicate
.Attribute
.Info
.Label
.AttributeName
=
782 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
783 predicate
.Attribute
.Info
.AttributeFormat
=
784 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
785 predicate
.Attribute
.Value
= const_cast<CSSM_DATA_PTR
>(&keyLabel
);
787 query
.SelectionPredicate
= &predicate
;
788 query
.QueryLimits
.TimeLimit
= 0;
789 query
.QueryLimits
.SizeLimit
= 1;
790 query
.QueryFlags
= 0;
792 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
795 NULL
, // attrs - don't need 'em
796 NULL
, // theData - don't need it
798 /* abort only on success */
800 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
801 p12ErrorLog("***p12DeleteKey: can't find private key\n");
805 crtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
807 p12LogCssmError("CSSM_DL_DataDelete", crtn
);
808 p12ErrorLog("***p12DeleteKey: can't delete private key\n");
811 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
812 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
816 /* convert App passphrase to array of chars used in P12 PBE */
817 void p12ImportPassPhrase(
818 CFStringRef inPhrase
,
820 CSSM_DATA
&outPhrase
)
822 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
823 inPhrase
, kCFStringEncodingUTF8
, 0);
825 p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
826 MacOSError::throwMe(errSecParam
);
828 CFIndex keyLen
= CFDataGetLength(cfData
);
829 coder
.allocItem(outPhrase
, keyLen
);
830 memmove(outPhrase
.Data
, CFDataGetBytePtr(cfData
), keyLen
);