]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2007-2010,2012-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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 | |
11 | * file. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
ecaf5866 | 24 | #include <libDER/oids.h> |
b1ab9ed8 A |
25 | #include <security_asn1/nssUtils.h> |
26 | #include <security_asn1/SecAsn1Templates.h> | |
27 | #include <security_asn1/pkcs12Templates.h> | |
28 | ||
29 | #include <CommonCrypto/CommonCryptor.h> | |
30 | #include <CommonCrypto/CommonDigest.h> | |
31 | #include <CommonCrypto/CommonHMAC.h> | |
32 | ||
33 | #include <CoreFoundation/CoreFoundation.h> | |
34 | ||
35 | #include <AssertMacros.h> | |
36 | #include <Security/SecInternal.h> | |
427c49bc | 37 | #include <utilities/debugging.h> |
b1ab9ed8 A |
38 | |
39 | #include "p12pbegen.h" | |
40 | #include "p12import.h" | |
b54c578e | 41 | #include <Security/SecImportExport.h> |
b1ab9ed8 A |
42 | |
43 | #ifdef NDEBUG | |
44 | #define p12DecodeLog(args...) | |
45 | #else | |
46 | #define p12DecodeLog(args...) secdebug("pkcs12", "%s\n", args) | |
47 | #endif | |
48 | ||
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) | |
53 | { | |
54 | return SecAsn1Decode(context->coder, (const char *)item->Data, item->Length, tmpl, dest); | |
55 | } | |
56 | ||
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) | |
59 | { | |
60 | SecAsn1AllocItem(context->coder, item, len); | |
61 | } | |
62 | ||
63 | /* | |
64 | * OIDS for P12 map to the following attributes. | |
65 | */ | |
66 | typedef struct { | |
67 | CCAlgorithm alg; | |
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. | |
71 | } PKCSOidInfo; | |
72 | ||
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 } | |
89 | }; | |
90 | ||
91 | #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1])) | |
92 | ||
93 | static int pkcsOidToParams(const SecAsn1Item *oid, CCAlgorithm *alg, | |
94 | uint32_t *keySizeInBits, uint32_t *blockSizeInBytes, CCOptions *options) | |
95 | { | |
96 | DERItem prefix = { oid->Data, oid->Length }; | |
97 | prefix.length -= 1; | |
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) | |
101 | return -1; | |
102 | *alg = pkcsOidInfos[postfix-1].alg; | |
103 | *keySizeInBits = pkcsOidInfos[postfix-1].keySizeInBits; | |
104 | *blockSizeInBytes = pkcsOidInfos[postfix-1].blockSizeInBytes; | |
105 | *options = pkcsOidInfos[postfix-1].options; | |
106 | return 0; | |
107 | } | |
108 | return -1; | |
109 | } | |
110 | ||
111 | static int p12DataToInt(const SecAsn1Item *cdata, uint32_t *u) | |
112 | { | |
113 | /* default/not present */ | |
114 | if((cdata->Length == 0) || (cdata->Data == NULL)) { | |
115 | *u = 0; | |
116 | return 0; | |
117 | } | |
118 | size_t len = cdata->Length; | |
119 | if(len > sizeof(uint32_t)) { | |
120 | return -1; | |
121 | } | |
122 | ||
123 | uint32_t rtn = 0; | |
124 | uint8_t *cp = cdata->Data; | |
125 | size_t i; | |
126 | for(i = 0; i < len; i++) { | |
127 | rtn = (rtn << 8) | *cp++; | |
128 | } | |
129 | *u = rtn; | |
130 | return 0; | |
131 | } | |
132 | ||
133 | /* | |
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. | |
137 | */ | |
138 | static int algIdParse(pkcs12_context * context, | |
139 | const SecAsn1AlgId *algId, NSS_P12_PBE_Params *pbeParams/*optional*/) | |
140 | { | |
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); | |
147 | ||
148 | return 0; | |
149 | out: | |
150 | return -1; | |
151 | } | |
152 | ||
153 | static int p12Decrypt(pkcs12_context * context, const SecAsn1AlgId *algId, | |
154 | const SecAsn1Item *cipherText, SecAsn1Item *plainText) | |
155 | { | |
d8f41ccd | 156 | NSS_P12_PBE_Params pbep = {}; |
b1ab9ed8 A |
157 | // XXX/cs not requiring decoding, but if pbep is uninit this will fail later |
158 | algIdParse(context, algId, &pbep); | |
159 | ||
160 | CCAlgorithm alg = 0; | |
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); | |
166 | ||
167 | uint32_t iterCount = 0; | |
168 | require_noerr(p12DataToInt(&pbep.iterations, &iterCount), out); | |
169 | ||
170 | /* P12 style key derivation */ | |
171 | SecAsn1Item key = {0, NULL}; | |
172 | if(keySizeInBits) | |
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); | |
176 | ||
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); | |
183 | } | |
184 | ||
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; | |
191 | ||
192 | return 0; | |
193 | out: | |
194 | return -1; | |
195 | } | |
196 | ||
197 | static int emit_item(pkcs12_context * context, NSS_Attribute **attrs, | |
198 | CFStringRef item_key, CFTypeRef item_value) | |
199 | { | |
200 | int result = -1; | |
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); | |
206 | unsigned int dex; | |
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)) { | |
212 | /* | |
213 | * BMP string (UniCode). Spec says only one legal value. | |
214 | */ | |
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); | |
222 | if (friendly_name) { | |
223 | CFDictionarySetValue(attr_dict, kSecImportItemLabel, friendly_name); | |
224 | CFRelease(friendly_name); | |
225 | } | |
226 | } | |
227 | else if(DEROidCompare(&type, &oidLocalKeyId)) { | |
228 | /* | |
229 | * Octet string. Spec says only one legal value. | |
230 | */ | |
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); | |
236 | if (keyid) { | |
237 | CFDictionarySetValue(attr_dict, kSecImportItemKeyID, keyid); | |
238 | CFRelease(keyid); | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | CFTypeRef key = CFDictionaryGetValue(attr_dict, kSecImportItemKeyID); | |
244 | if (!key) | |
245 | key = CFDictionaryGetValue(attr_dict, kSecImportItemLabel); | |
246 | if (!key) | |
247 | key = item_value; | |
248 | ||
249 | CFMutableDictionaryRef item = (CFMutableDictionaryRef)CFDictionaryGetValue(context->items, key); | |
250 | if (item) { | |
251 | CFDictionarySetValue(item, item_key, item_value); | |
252 | } else { | |
253 | CFDictionarySetValue(attr_dict, item_key, item_value); | |
254 | CFDictionarySetValue(context->items, key, attr_dict); | |
255 | } | |
256 | result = 0; | |
257 | out: | |
258 | CFReleaseSafe(attr_dict); | |
259 | return result; | |
260 | } | |
261 | ||
262 | ||
263 | /* | |
264 | * ShroudedKeyBag parser w/decrypt | |
265 | */ | |
266 | static int shroudedKeyBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) | |
267 | { | |
d8f41ccd A |
268 | CFDataRef algoidData = NULL; |
269 | CFDataRef keyData = NULL; | |
270 | ||
b1ab9ed8 A |
271 | p12DecodeLog("Found shrouded key bag"); |
272 | ||
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); | |
277 | ||
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, | |
282 | &pki), out); | |
427c49bc | 283 | |
b1ab9ed8 | 284 | DERItem algorithm = { pki.algorithm.algorithm.Data, pki.algorithm.algorithm.Length }; |
d8f41ccd | 285 | algoidData = NULL; |
427c49bc A |
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); | |
290 | } else { | |
291 | goto out; | |
292 | } | |
293 | require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("algid"), algoidData), out); | |
d8f41ccd | 294 | CFReleaseNull(algoidData); |
b1ab9ed8 | 295 | |
d8f41ccd | 296 | keyData = CFDataCreate(kCFAllocatorDefault, pki.privateKey.Data, pki.privateKey.Length); |
b1ab9ed8 | 297 | require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("key"), keyData), out); |
d8f41ccd | 298 | CFReleaseNull(keyData); |
427c49bc | 299 | |
b1ab9ed8 A |
300 | return 0; |
301 | out: | |
d8f41ccd A |
302 | CFReleaseSafe(algoidData); |
303 | CFReleaseSafe(keyData); | |
b1ab9ed8 A |
304 | return -1; |
305 | } | |
306 | ||
307 | ||
308 | /* | |
309 | * CertBag parser | |
310 | */ | |
311 | static int certBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) | |
312 | { | |
d8f41ccd | 313 | CFDataRef certData = NULL; |
b1ab9ed8 A |
314 | p12DecodeLog("found certBag"); |
315 | NSS_P12_CertBag *certBag = safeBag->bagValue.certBag; | |
316 | ||
317 | switch(certBag->type) { | |
318 | case CT_X509: | |
319 | { | |
320 | /* certType = CSSM_CERT_X_509v3; | |
321 | certEncoding = CSSM_CERT_ENCODING_DER; */ | |
d8f41ccd | 322 | certData = CFDataCreate(kCFAllocatorDefault, certBag->certValue.Data, |
b1ab9ed8 A |
323 | certBag->certValue.Length); |
324 | require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("cert"), certData), out); | |
325 | CFRelease(certData); | |
326 | break; | |
327 | } | |
328 | case CT_SDSI: | |
329 | /* certType = CSSM_CERT_SDSIv1; */ | |
330 | /* it's base64 encoded - no value for that in this enum */ | |
331 | break; | |
332 | default: | |
333 | return -1; | |
334 | } | |
335 | return 0; | |
336 | out: | |
d8f41ccd | 337 | CFReleaseSafe(certData); |
b1ab9ed8 A |
338 | return -1; |
339 | } | |
340 | ||
341 | ||
342 | /* | |
343 | * Parse an encoded NSS_P12_SafeContents. This could be either | |
344 | * present as plaintext in an AuthSafe or decrypted. | |
345 | */ | |
346 | static int safeContentsParse(pkcs12_context * context, const SecAsn1Item *contentsBlob) | |
347 | { | |
348 | p12DecodeLog("safeContentsParse"); | |
349 | ||
350 | NSS_P12_SafeContents sc; | |
351 | memset(&sc, 0, sizeof(sc)); | |
352 | require_noerr(decode_item(context, contentsBlob, NSS_P12_SafeContentsTemplate, | |
353 | &sc), out); | |
354 | ||
355 | unsigned numBags = nssArraySize((const void **)sc.bags); | |
356 | unsigned int dex; | |
357 | for(dex=0; dex<numBags; dex++) { | |
358 | NSS_P12_SafeBag *bag = sc.bags[dex]; | |
359 | assert(bag != NULL); | |
360 | ||
361 | /* ensure that *something* is there */ | |
362 | require(bag->bagValue.keyBag != NULL, out); | |
363 | ||
364 | /* | |
365 | * Break out to individual bag type | |
366 | */ | |
367 | switch(bag->type) { | |
368 | case BT_ShroudedKeyBag: | |
369 | require_noerr(shroudedKeyBagParse(context, bag), out); | |
370 | break; | |
371 | case BT_CertBag: | |
372 | require_noerr(certBagParse(context, bag), out); | |
373 | break; | |
374 | ||
375 | case BT_KeyBag: | |
376 | /* keyBagParse(bag); */ | |
377 | p12DecodeLog("Unhandled BT_KeyBag"); | |
378 | break; | |
379 | case BT_CrlBag: | |
380 | /* crlBagParse(bag); */ | |
381 | p12DecodeLog("Unhandled BT_CrlBag"); | |
382 | break; | |
383 | case BT_SecretBag: | |
384 | /* secretBagParse(bag); */ | |
385 | p12DecodeLog("Unhandled BT_SecretBag"); | |
386 | break; | |
387 | case BT_SafeContentsBag: | |
388 | /* safeContentsBagParse(bag); */ | |
389 | p12DecodeLog("Unhandled BT_SafeContentsBag"); | |
390 | break; | |
391 | default: | |
392 | p12DecodeLog("Unknown bag type"); | |
393 | goto out; | |
394 | break; | |
395 | } | |
396 | } | |
397 | return 0; | |
398 | out: | |
399 | return -1; | |
400 | } | |
401 | ||
402 | /* | |
403 | * Parse a ContentInfo in the context of (i.e., as an element of) | |
404 | * an AuthenticatedSafe. | |
405 | */ | |
406 | static int authSafeElementParse(pkcs12_context * context, const NSS_P7_DecodedContentInfo *info) | |
407 | { | |
408 | p12DecodeLog("authSafeElementParse"); | |
409 | switch(info->type) { | |
410 | case CT_Data: | |
411 | /* unencrypted SafeContents */ | |
412 | require_noerr(safeContentsParse(context, info->content.data), out); | |
413 | break; | |
414 | ||
415 | case CT_EncryptedData: | |
416 | { | |
417 | /* | |
418 | * Decrypt contents to get a SafeContents and | |
419 | * then parse that. | |
420 | */ | |
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); | |
426 | break; | |
427 | } | |
428 | default: | |
429 | break; | |
430 | } | |
431 | return 0; | |
432 | out: | |
433 | return -1; | |
434 | } | |
435 | ||
436 | /* | |
437 | * Parse an encoded NSS_P12_AuthenticatedSafe | |
438 | */ | |
439 | static int authSafeParse(pkcs12_context * context, const SecAsn1Item *authSafeBlob) | |
440 | { | |
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); | |
446 | ||
447 | unsigned numInfos = nssArraySize((const void **)authSafe.info); | |
448 | unsigned int dex; | |
449 | for (dex=0; dex<numInfos; dex++) { | |
450 | NSS_P7_DecodedContentInfo *info = authSafe.info[dex]; | |
451 | require_noerr_quiet(authSafeElementParse(context, info), out); | |
452 | } | |
453 | return 0; | |
454 | out: | |
455 | return -1; | |
456 | } | |
457 | ||
458 | static int p12VerifyMac(pkcs12_context * context, const NSS_P12_DecodedPFX *pfx) | |
459 | { | |
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); | |
466 | ||
467 | /* has to be OID_OIW_SHA1 */ | |
468 | DERItem algOidItem = { algOid->Data, algOid->Length }; | |
469 | require(algOidItem.length && DEROidCompare(&oidSha1, &algOidItem), out); | |
470 | ||
471 | uint32_t iterCount = 0; | |
472 | require_noerr_quiet(p12DataToInt(&macData->iterations, &iterCount), out); | |
473 | if (iterCount == 0) { /* optional, default 1 */ | |
474 | iterCount = 1; | |
475 | } | |
476 | ||
477 | /* | |
478 | * In classic fashion, the PKCS12 spec now says: | |
479 | * | |
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. | |
483 | * | |
484 | * So here we go. | |
485 | */ | |
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); | |
490 | ||
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); | |
498 | ||
499 | return 0; | |
500 | out: | |
501 | return -1; | |
502 | } | |
503 | ||
504 | p12_error p12decode(pkcs12_context * context, CFDataRef cdpfx) | |
505 | { | |
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) }; | |
510 | ||
511 | require_noerr_quiet(decode_item(context, &raw_blob, NSS_P12_DecodedPFXTemplate, &pfx), out); | |
512 | NSS_P7_DecodedContentInfo *dci = &pfx.authSafe; | |
513 | ||
514 | /* only support CT_Data at top level (password based integrity mode) */ | |
515 | require(dci->type == CT_Data, out); | |
516 | require(pfx.macData, out); | |
517 | ||
518 | require_noerr_action_quiet(p12VerifyMac(context, &pfx), out, err = p12_passwordErr); | |
519 | require_noerr_quiet(authSafeParse(context, dci->content.data), out); | |
520 | ||
521 | return errSecSuccess; | |
522 | out: | |
523 | return err; | |
524 | } |