2 * Copyright (c) 2007-2010,2012-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@
24 #include <libDER/oidsPriv.h>
25 #include <security_asn1/nssUtils.h>
26 #include <security_asn1/SecAsn1Templates.h>
27 #include <security_asn1/pkcs12Templates.h>
29 #include <CommonCrypto/CommonCryptor.h>
30 #include <CommonCrypto/CommonDigest.h>
31 #include <CommonCrypto/CommonHMAC.h>
33 #include <CoreFoundation/CoreFoundation.h>
35 #include <AssertMacros.h>
36 #include <Security/SecInternal.h>
37 #include <utilities/debugging.h>
39 #include "p12pbegen.h"
40 #include "p12import.h"
41 #include "SecImportExport.h"
44 #define p12DecodeLog(args...)
46 #define p12DecodeLog(args...) secdebug("pkcs12", "%s\n", args)
49 int decode_item(pkcs12_context
* context
, const SecAsn1Item
*item
,
50 const SecAsn1Template
*tmpl
, void *dest
);
51 inline int decode_item(pkcs12_context
* context
, const SecAsn1Item
*item
,
52 const SecAsn1Template
*tmpl
, void *dest
)
54 return SecAsn1Decode(context
->coder
, (const char *)item
->Data
, item
->Length
, tmpl
, dest
);
57 void alloc_item(pkcs12_context
* context
, SecAsn1Item
*item
, size_t len
);
58 inline void alloc_item(pkcs12_context
* context
, SecAsn1Item
*item
, size_t len
)
60 SecAsn1AllocItem(context
->coder
, item
, len
);
64 * OIDS for P12 map to the following attributes.
68 uint32_t keySizeInBits
; // XXX/cs make keysize in bytes
69 uint32_t blockSizeInBytes
; // for IV, optional, make iv size in bytes
70 CCOptions options
; // padding and mode.
73 /* PKCS12 algorithms OID_ISO_MEMBER, OID_US, OID_RSA, OID_PKCS, OID_PKCS_12 */
74 static const uint8_t PKCS12_pbep
[] = { 42, 134, 72, 134, 247, 13, 1, 12, 1 };
75 static const DERItem OID_PKCS12_pbep
= { (uint8_t*)PKCS12_pbep
, sizeof(PKCS12_pbep
) };
76 static const PKCSOidInfo pkcsOidInfos
[] = {
77 { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,*/
78 kCCAlgorithmRC4
, 128, 0/* stream cipher */, 0 },
79 { /*CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,*/
80 kCCAlgorithmRC4
, 40, 0/* stream cipher */, 0 },
81 { /*CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,*/
82 kCCAlgorithm3DES
, 64 * 3, 8, kCCOptionPKCS7Padding
},
83 { /*CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,*/
84 -1 /*CSSM_ALGID_3DES_2KEY unsupported*/, 64 * 2, 8, kCCOptionPKCS7Padding
},
85 { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,*/
86 kCCAlgorithmRC2
, 128, 8, kCCOptionPKCS7Padding
},
87 { /*CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,*/
88 kCCAlgorithmRC2
, 40, 8, kCCOptionPKCS7Padding
}
91 #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
93 static int pkcsOidToParams(const SecAsn1Item
*oid
, CCAlgorithm
*alg
,
94 uint32_t *keySizeInBits
, uint32_t *blockSizeInBytes
, CCOptions
*options
)
96 DERItem prefix
= { oid
->Data
, oid
->Length
};
98 if (DEROidCompare(&OID_PKCS12_pbep
, &prefix
)) {
99 uint8_t postfix
= oid
->Data
[oid
->Length
-1];
100 if (postfix
> NUM_PKCS_OID_INFOS
|| postfix
== 4)
102 *alg
= pkcsOidInfos
[postfix
-1].alg
;
103 *keySizeInBits
= pkcsOidInfos
[postfix
-1].keySizeInBits
;
104 *blockSizeInBytes
= pkcsOidInfos
[postfix
-1].blockSizeInBytes
;
105 *options
= pkcsOidInfos
[postfix
-1].options
;
111 static int p12DataToInt(const SecAsn1Item
*cdata
, uint32_t *u
)
113 /* default/not present */
114 if((cdata
->Length
== 0) || (cdata
->Data
== NULL
)) {
118 size_t len
= cdata
->Length
;
119 if(len
> sizeof(uint32_t)) {
124 uint8_t *cp
= cdata
->Data
;
126 for(i
= 0; i
< len
; i
++) {
127 rtn
= (rtn
<< 8) | *cp
++;
134 * Parse an SecAsn1AlgId specific to P12.
135 * Decode the alg params as a NSS_P12_PBE_Params and parse and
136 * return the result if the pbeParams is non-NULL.
138 static int algIdParse(pkcs12_context
* context
,
139 const SecAsn1AlgId
*algId
, NSS_P12_PBE_Params
*pbeParams
/*optional*/)
141 p12DecodeLog("algIdParse");
142 const SecAsn1Item
*param
= &algId
->parameters
;
143 require(pbeParams
, out
);
144 require(param
&& param
->Length
, out
);
145 memset(pbeParams
, 0, sizeof(*pbeParams
));
146 require_noerr(decode_item(context
, param
, NSS_P12_PBE_ParamsTemplate
, pbeParams
), out
);
153 static int p12Decrypt(pkcs12_context
* context
, const SecAsn1AlgId
*algId
,
154 const SecAsn1Item
*cipherText
, SecAsn1Item
*plainText
)
156 NSS_P12_PBE_Params pbep
= {};
157 // XXX/cs not requiring decoding, but if pbep is uninit this will fail later
158 algIdParse(context
, algId
, &pbep
);
161 uint32_t keySizeInBits
= 0;
162 uint32_t blockSizeInBytes
= 0; // for IV, optional
163 CCOptions options
= 0;
164 require_noerr_quiet(pkcsOidToParams(&algId
->algorithm
, &alg
, &keySizeInBits
,
165 &blockSizeInBytes
, &options
), out
);
167 uint32_t iterCount
= 0;
168 require_noerr(p12DataToInt(&pbep
.iterations
, &iterCount
), out
);
170 /* P12 style key derivation */
171 SecAsn1Item key
= {0, NULL
};
173 alloc_item(context
, &key
, (keySizeInBits
+7)/8);
174 require_noerr(p12_pbe_gen(context
->passphrase
, pbep
.salt
.Data
, pbep
.salt
.Length
,
175 iterCount
, PBE_ID_Key
, key
.Data
, key
.Length
), out
);
177 /* P12 style IV derivation, optional */
178 SecAsn1Item iv
= {0, NULL
};
179 if(blockSizeInBytes
) {
180 alloc_item(context
, &iv
, blockSizeInBytes
);
181 require_noerr(p12_pbe_gen(context
->passphrase
, pbep
.salt
.Data
, pbep
.salt
.Length
,
182 iterCount
, PBE_ID_IV
, iv
.Data
, iv
.Length
), out
);
185 SecAsn1Item ourPtext
= {0, NULL
};
186 alloc_item(context
, &ourPtext
, cipherText
->Length
);
187 require_noerr(CCCrypt(kCCDecrypt
, alg
, options
/*kCCOptionPKCS7Padding*/,
188 key
.Data
, key
.Length
, iv
.Data
, cipherText
->Data
, cipherText
->Length
,
189 ourPtext
.Data
, ourPtext
.Length
, &ourPtext
.Length
), out
);
190 *plainText
= ourPtext
;
197 static int emit_item(pkcs12_context
* context
, NSS_Attribute
**attrs
,
198 CFStringRef item_key
, CFTypeRef item_value
)
201 /* parse attrs into friendlyName, localKeyId; ignoring generic attrs */
202 CFMutableDictionaryRef attr_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
203 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
204 require(attr_dict
, out
);
205 unsigned numAttrs
= nssArraySize((const void **)attrs
);
207 for(dex
= 0; dex
< numAttrs
; dex
++) {
208 NSS_Attribute
*attr
= attrs
[dex
];
209 unsigned numValues
= nssArraySize((const void**)attr
->attrValue
);
210 DERItem type
= { attr
->attrType
.Data
, attr
->attrType
.Length
};
211 if(DEROidCompare(&type
, &oidFriendlyName
)) {
213 * BMP string (UniCode). Spec says only one legal value.
215 require(numValues
== 1, out
);
216 SecAsn1Item friendly_name_asn1
;
217 require_noerr(decode_item(context
, attr
->attrValue
[0],
218 kSecAsn1BMPStringTemplate
, &friendly_name_asn1
), out
);
219 CFStringRef friendly_name
= CFStringCreateWithBytes(kCFAllocatorDefault
,
220 friendly_name_asn1
.Data
, friendly_name_asn1
.Length
,
221 kCFStringEncodingUnicode
, true);
223 CFDictionarySetValue(attr_dict
, kSecImportItemLabel
, friendly_name
);
224 CFRelease(friendly_name
);
227 else if(DEROidCompare(&type
, &oidLocalKeyId
)) {
229 * Octet string. Spec says only one legal value.
231 require(numValues
== 1, out
);
232 SecAsn1Item local_key_id
;
233 require_noerr(decode_item(context
, attr
->attrValue
[0],
234 kSecAsn1OctetStringTemplate
, &local_key_id
), out
);
235 CFDataRef keyid
= CFDataCreate(kCFAllocatorDefault
, local_key_id
.Data
, local_key_id
.Length
);
237 CFDictionarySetValue(attr_dict
, kSecImportItemKeyID
, keyid
);
243 CFTypeRef key
= CFDictionaryGetValue(attr_dict
, kSecImportItemKeyID
);
245 key
= CFDictionaryGetValue(attr_dict
, kSecImportItemLabel
);
249 CFMutableDictionaryRef item
= (CFMutableDictionaryRef
)CFDictionaryGetValue(context
->items
, key
);
251 CFDictionarySetValue(item
, item_key
, item_value
);
253 CFDictionarySetValue(attr_dict
, item_key
, item_value
);
254 CFDictionarySetValue(context
->items
, key
, attr_dict
);
258 CFReleaseSafe(attr_dict
);
264 * ShroudedKeyBag parser w/decrypt
266 static int shroudedKeyBagParse(pkcs12_context
* context
, const NSS_P12_SafeBag
*safeBag
)
268 CFDataRef algoidData
= NULL
;
269 CFDataRef keyData
= NULL
;
271 p12DecodeLog("Found shrouded key bag");
273 const NSS_P12_ShroudedKeyBag
*keyBag
= safeBag
->bagValue
.shroudedKeyBag
;
274 SecAsn1Item ptext
= {0, NULL
};
275 require_noerr_quiet(p12Decrypt(context
, &keyBag
->algorithm
,
276 &keyBag
->encryptedData
, &ptext
), out
);
278 /* Decode PKCS#8 formatted private key */
279 NSS_PrivateKeyInfo pki
;
280 memset(&pki
, 0, sizeof(pki
));
281 require_noerr(decode_item(context
, &ptext
, kSecAsn1PrivateKeyInfoTemplate
,
284 DERItem algorithm
= { pki
.algorithm
.algorithm
.Data
, pki
.algorithm
.algorithm
.Length
};
286 if (DEROidCompare(&oidEcPubKey
, &algorithm
)) {
287 algoidData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, oidEcPubKey
.data
, oidEcPubKey
.length
, kCFAllocatorNull
);
288 } else if (DEROidCompare(&oidRsa
, &algorithm
)) {
289 algoidData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, oidRsa
.data
, oidRsa
.length
, kCFAllocatorNull
);
293 require_noerr(emit_item(context
, safeBag
->bagAttrs
, CFSTR("algid"), algoidData
), out
);
294 CFReleaseNull(algoidData
);
296 keyData
= CFDataCreate(kCFAllocatorDefault
, pki
.privateKey
.Data
, pki
.privateKey
.Length
);
297 require_noerr(emit_item(context
, safeBag
->bagAttrs
, CFSTR("key"), keyData
), out
);
298 CFReleaseNull(keyData
);
302 CFReleaseSafe(algoidData
);
303 CFReleaseSafe(keyData
);
311 static int certBagParse(pkcs12_context
* context
, const NSS_P12_SafeBag
*safeBag
)
313 CFDataRef certData
= NULL
;
314 p12DecodeLog("found certBag");
315 NSS_P12_CertBag
*certBag
= safeBag
->bagValue
.certBag
;
317 switch(certBag
->type
) {
320 /* certType = CSSM_CERT_X_509v3;
321 certEncoding = CSSM_CERT_ENCODING_DER; */
322 certData
= CFDataCreate(kCFAllocatorDefault
, certBag
->certValue
.Data
,
323 certBag
->certValue
.Length
);
324 require_noerr(emit_item(context
, safeBag
->bagAttrs
, CFSTR("cert"), certData
), out
);
329 /* certType = CSSM_CERT_SDSIv1; */
330 /* it's base64 encoded - no value for that in this enum */
337 CFReleaseSafe(certData
);
343 * Parse an encoded NSS_P12_SafeContents. This could be either
344 * present as plaintext in an AuthSafe or decrypted.
346 static int safeContentsParse(pkcs12_context
* context
, const SecAsn1Item
*contentsBlob
)
348 p12DecodeLog("safeContentsParse");
350 NSS_P12_SafeContents sc
;
351 memset(&sc
, 0, sizeof(sc
));
352 require_noerr(decode_item(context
, contentsBlob
, NSS_P12_SafeContentsTemplate
,
355 unsigned numBags
= nssArraySize((const void **)sc
.bags
);
357 for(dex
=0; dex
<numBags
; dex
++) {
358 NSS_P12_SafeBag
*bag
= sc
.bags
[dex
];
361 /* ensure that *something* is there */
362 require(bag
->bagValue
.keyBag
!= NULL
, out
);
365 * Break out to individual bag type
368 case BT_ShroudedKeyBag
:
369 require_noerr(shroudedKeyBagParse(context
, bag
), out
);
372 require_noerr(certBagParse(context
, bag
), out
);
376 /* keyBagParse(bag); */
377 p12DecodeLog("Unhandled BT_KeyBag");
380 /* crlBagParse(bag); */
381 p12DecodeLog("Unhandled BT_CrlBag");
384 /* secretBagParse(bag); */
385 p12DecodeLog("Unhandled BT_SecretBag");
387 case BT_SafeContentsBag
:
388 /* safeContentsBagParse(bag); */
389 p12DecodeLog("Unhandled BT_SafeContentsBag");
392 p12DecodeLog("Unknown bag type");
403 * Parse a ContentInfo in the context of (i.e., as an element of)
404 * an AuthenticatedSafe.
406 static int authSafeElementParse(pkcs12_context
* context
, const NSS_P7_DecodedContentInfo
*info
)
408 p12DecodeLog("authSafeElementParse");
411 /* unencrypted SafeContents */
412 require_noerr(safeContentsParse(context
, info
->content
.data
), out
);
415 case CT_EncryptedData
:
418 * Decrypt contents to get a SafeContents and
421 SecAsn1Item ptext
= {0, NULL
};
422 NSS_P7_EncryptedData
*edata
= info
->content
.encryptData
;
423 require_noerr_quiet(p12Decrypt(context
, &edata
->contentInfo
.encrAlg
,
424 &edata
->contentInfo
.encrContent
, &ptext
), out
);
425 require_noerr(safeContentsParse(context
, &ptext
), out
);
437 * Parse an encoded NSS_P12_AuthenticatedSafe
439 static int authSafeParse(pkcs12_context
* context
, const SecAsn1Item
*authSafeBlob
)
441 p12DecodeLog("authSafeParse");
442 NSS_P12_AuthenticatedSafe authSafe
;
443 memset(&authSafe
, 0, sizeof(authSafe
));
444 require_noerr(decode_item(context
, authSafeBlob
,
445 NSS_P12_AuthenticatedSafeTemplate
, &authSafe
), out
);
447 unsigned numInfos
= nssArraySize((const void **)authSafe
.info
);
449 for (dex
=0; dex
<numInfos
; dex
++) {
450 NSS_P7_DecodedContentInfo
*info
= authSafe
.info
[dex
];
451 require_noerr_quiet(authSafeElementParse(context
, info
), out
);
458 static int p12VerifyMac(pkcs12_context
* context
, const NSS_P12_DecodedPFX
*pfx
)
460 NSS_P12_MacData
*macData
= pfx
->macData
;
461 require(macData
, out
);
462 NSS_P7_DigestInfo
*digestInfo
= &macData
->mac
;
463 require(digestInfo
, out
);
464 SecAsn1Item
*algOid
= &digestInfo
->digestAlgorithm
.algorithm
;
465 require(algOid
, out
);
467 /* has to be OID_OIW_SHA1 */
468 DERItem algOidItem
= { algOid
->Data
, algOid
->Length
};
469 require(algOidItem
.length
&& DEROidCompare(&oidSha1
, &algOidItem
), out
);
471 uint32_t iterCount
= 0;
472 require_noerr_quiet(p12DataToInt(&macData
->iterations
, &iterCount
), out
);
473 if (iterCount
== 0) { /* optional, default 1 */
478 * In classic fashion, the PKCS12 spec now says:
480 * When password integrity mode is used to secure a PFX PDU,
481 * an SHA-1 HMAC is computed on the BER-encoding of the contents
482 * of the content field of the authSafe field in the PFX PDU.
486 uint8_t hmac_key
[CC_SHA1_DIGEST_LENGTH
];
487 require_noerr_quiet(p12_pbe_gen(context
->passphrase
,
488 macData
->macSalt
.Data
, macData
->macSalt
.Length
,
489 iterCount
, PBE_ID_MAC
, hmac_key
, sizeof(hmac_key
)), out
);
491 /* prealloc the mac data */
492 SecAsn1Item verifyMac
;
493 alloc_item(context
, &verifyMac
, CC_SHA1_DIGEST_LENGTH
);
494 SecAsn1Item
*ptext
= pfx
->authSafe
.content
.data
;
495 CCHmac(kCCHmacAlgSHA1
, hmac_key
, CC_SHA1_DIGEST_LENGTH
,
496 ptext
->Data
, ptext
->Length
, verifyMac
.Data
);
497 require_quiet(nssCompareSecAsn1Items(&verifyMac
, &digestInfo
->digest
), out
);
504 p12_error
p12decode(pkcs12_context
* context
, CFDataRef cdpfx
)
506 int err
= p12_decodeErr
;
507 NSS_P12_DecodedPFX pfx
;
508 memset(&pfx
, 0, sizeof(pfx
));
509 SecAsn1Item raw_blob
= { CFDataGetLength(cdpfx
), (void*)CFDataGetBytePtr(cdpfx
) };
511 require_noerr_quiet(decode_item(context
, &raw_blob
, NSS_P12_DecodedPFXTemplate
, &pfx
), out
);
512 NSS_P7_DecodedContentInfo
*dci
= &pfx
.authSafe
;
514 /* only support CT_Data at top level (password based integrity mode) */
515 require(dci
->type
== CT_Data
, out
);
516 require(pfx
.macData
, out
);
518 require_noerr_action_quiet(p12VerifyMac(context
, &pfx
), out
, err
= p12_passwordErr
);
519 require_noerr_quiet(authSafeParse(context
, dci
->content
.data
), out
);
521 return errSecSuccess
;