Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "pkcs12.h"
6 #include "plarena.h"
7 #include "secpkcs7.h"
8 #include "p12local.h"
9 #include "secoid.h"
10 #include "secitem.h"
11 #include "secport.h"
12 #include "secasn1.h"
13 #include "secder.h"
14 #include "secerr.h"
15 #include "cert.h"
16 #include "certdb.h"
17 #include "p12plcy.h"
18 #include "p12.h"
19 #include "secpkcs5.h"
21 /* PFX extraction and validation routines */
23 /* decode the DER encoded PFX item. if unable to decode, check to see if it
24 * is an older PFX item. If that fails, assume the file was not a valid
25 * pfx file.
26 * the returned pfx structure should be destroyed using SEC_PKCS12DestroyPFX
27 */
28 static SEC_PKCS12PFXItem *
29 sec_pkcs12_decode_pfx(SECItem *der_pfx)
30 {
31 SEC_PKCS12PFXItem *pfx;
32 SECStatus rv;
34 if(der_pfx == NULL) {
35 return NULL;
36 }
38 /* allocate the space for a new PFX item */
39 pfx = sec_pkcs12_new_pfx();
40 if(pfx == NULL) {
41 return NULL;
42 }
44 rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate,
45 der_pfx);
47 /* if a failure occurred, check for older version...
48 * we also get rid of the old pfx structure, because we don't
49 * know where it failed and what data in may contain
50 */
51 if(rv != SECSuccess) {
52 SEC_PKCS12DestroyPFX(pfx);
53 pfx = sec_pkcs12_new_pfx();
54 if(pfx == NULL) {
55 return NULL;
56 }
57 rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate_OLD,
58 der_pfx);
59 if(rv != SECSuccess) {
60 PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX);
61 PORT_FreeArena(pfx->poolp, PR_TRUE);
62 return NULL;
63 }
64 pfx->old = PR_TRUE;
65 SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac);
66 SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt);
67 } else {
68 pfx->old = PR_FALSE;
69 }
71 /* convert bit string from bits to bytes */
72 pfx->macData.macSalt.len /= 8;
74 return pfx;
75 }
77 /* validate the integrity MAC used in the PFX. The MAC is generated
78 * per the PKCS 12 document. If the MAC is incorrect, it is most likely
79 * due to an invalid password.
80 * pwitem is the integrity password
81 * pfx is the decoded pfx item
82 */
83 static PRBool
84 sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem *pfx,
85 SECItem *pwitem)
86 {
87 SECItem *key = NULL, *mac = NULL, *data = NULL;
88 SECItem *vpwd = NULL;
89 SECOidTag algorithm;
90 PRBool ret = PR_FALSE;
92 if(pfx == NULL) {
93 return PR_FALSE;
94 }
96 algorithm = SECOID_GetAlgorithmTag(&pfx->macData.safeMac.digestAlgorithm);
97 switch(algorithm) {
98 /* only SHA1 hashing supported as a MACing algorithm */
99 case SEC_OID_SHA1:
100 if(pfx->old == PR_FALSE) {
101 pfx->swapUnicode = PR_FALSE;
102 }
104 recheckUnicodePassword:
105 vpwd = sec_pkcs12_create_virtual_password(pwitem,
106 &pfx->macData.macSalt,
107 pfx->swapUnicode);
108 if(vpwd == NULL) {
109 return PR_FALSE;
110 }
112 key = sec_pkcs12_generate_key_from_password(algorithm,
113 &pfx->macData.macSalt,
114 (pfx->old ? pwitem : vpwd));
115 /* free vpwd only for newer PFX */
116 if(vpwd) {
117 SECITEM_ZfreeItem(vpwd, PR_TRUE);
118 }
119 if(key == NULL) {
120 return PR_FALSE;
121 }
123 data = SEC_PKCS7GetContent(&pfx->authSafe);
124 if(data == NULL) {
125 break;
126 }
128 /* check MAC */
129 mac = sec_pkcs12_generate_mac(key, data, pfx->old);
130 ret = PR_TRUE;
131 if(mac) {
132 SECItem *safeMac = &pfx->macData.safeMac.digest;
133 if(SECITEM_CompareItem(mac, safeMac) != SECEqual) {
135 /* if we encounter an invalid mac, lets invert the
136 * password in case of unicode changes
137 */
138 if(((!pfx->old) && pfx->swapUnicode) || (pfx->old)){
139 PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
140 ret = PR_FALSE;
141 } else {
142 SECITEM_ZfreeItem(mac, PR_TRUE);
143 pfx->swapUnicode = PR_TRUE;
144 goto recheckUnicodePassword;
145 }
146 }
147 SECITEM_ZfreeItem(mac, PR_TRUE);
148 } else {
149 ret = PR_FALSE;
150 }
151 break;
152 default:
153 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM);
154 ret = PR_FALSE;
155 break;
156 }
158 /* let success fall through */
159 if(key != NULL)
160 SECITEM_ZfreeItem(key, PR_TRUE);
162 return ret;
163 }
165 /* check the validity of the pfx structure. we currently only support
166 * password integrity mode, so we check the MAC.
167 */
168 static PRBool
169 sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem *pfx,
170 SECItem *pwitem)
171 {
172 SECOidTag contentType;
174 contentType = SEC_PKCS7ContentType(&pfx->authSafe);
175 switch(contentType)
176 {
177 case SEC_OID_PKCS7_DATA:
178 return sec_pkcs12_check_pfx_mac(pfx, pwitem);
179 break;
180 case SEC_OID_PKCS7_SIGNED_DATA:
181 default:
182 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
183 break;
184 }
186 return PR_FALSE;
187 }
189 /* decode and return the valid PFX. if the PFX item is not valid,
190 * NULL is returned.
191 */
192 static SEC_PKCS12PFXItem *
193 sec_pkcs12_get_pfx(SECItem *pfx_data,
194 SECItem *pwitem)
195 {
196 SEC_PKCS12PFXItem *pfx;
197 PRBool valid_pfx;
199 if((pfx_data == NULL) || (pwitem == NULL)) {
200 return NULL;
201 }
203 pfx = sec_pkcs12_decode_pfx(pfx_data);
204 if(pfx == NULL) {
205 return NULL;
206 }
208 valid_pfx = sec_pkcs12_validate_pfx(pfx, pwitem);
209 if(valid_pfx != PR_TRUE) {
210 SEC_PKCS12DestroyPFX(pfx);
211 pfx = NULL;
212 }
214 return pfx;
215 }
217 /* authenticated safe decoding, validation, and access routines
218 */
220 /* convert dogbert beta 3 authenticated safe structure to a post
221 * beta three structure, so that we don't have to change more routines.
222 */
223 static SECStatus
224 sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
225 {
226 SEC_PKCS12Baggage *baggage;
227 SEC_PKCS12BaggageItem *bag;
228 SECStatus rv = SECSuccess;
230 if(asafe->old_baggage.espvks == NULL) {
231 /* XXX should the ASN1 engine produce a single NULL element list
232 * rather than setting the pointer to NULL?
233 * There is no need to return an error -- assume that the list
234 * was empty.
235 */
236 return SECSuccess;
237 }
239 baggage = sec_pkcs12_create_baggage(asafe->poolp);
240 if(!baggage) {
241 return SECFailure;
242 }
243 bag = sec_pkcs12_create_external_bag(baggage);
244 if(!bag) {
245 return SECFailure;
246 }
248 PORT_Memcpy(&asafe->baggage, baggage, sizeof(SEC_PKCS12Baggage));
250 /* if there are shrouded keys, append them to the bag */
251 rv = SECSuccess;
252 if(asafe->old_baggage.espvks[0] != NULL) {
253 int nEspvk = 0;
254 rv = SECSuccess;
255 while((asafe->old_baggage.espvks[nEspvk] != NULL) &&
256 (rv == SECSuccess)) {
257 rv = sec_pkcs12_append_shrouded_key(bag,
258 asafe->old_baggage.espvks[nEspvk]);
259 nEspvk++;
260 }
261 }
263 return rv;
264 }
266 /* decodes the authenticated safe item. a return of NULL indicates
267 * an error. however, the error will have occurred either in memory
268 * allocation or in decoding the authenticated safe.
269 *
270 * if an old PFX item has been found, we want to convert the
271 * old authenticated safe to the new one.
272 */
273 static SEC_PKCS12AuthenticatedSafe *
274 sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem *pfx)
275 {
276 SECItem *der_asafe = NULL;
277 SEC_PKCS12AuthenticatedSafe *asafe = NULL;
278 SECStatus rv;
280 if(pfx == NULL) {
281 return NULL;
282 }
284 der_asafe = SEC_PKCS7GetContent(&pfx->authSafe);
285 if(der_asafe == NULL) {
286 /* XXX set error ? */
287 goto loser;
288 }
290 asafe = sec_pkcs12_new_asafe(pfx->poolp);
291 if(asafe == NULL) {
292 goto loser;
293 }
295 if(pfx->old == PR_FALSE) {
296 rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
297 SEC_PKCS12AuthenticatedSafeTemplate,
298 der_asafe);
299 asafe->old = PR_FALSE;
300 asafe->swapUnicode = pfx->swapUnicode;
301 } else {
302 /* handle beta exported files */
303 rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
304 SEC_PKCS12AuthenticatedSafeTemplate_OLD,
305 der_asafe);
306 asafe->safe = &(asafe->old_safe);
307 rv = sec_pkcs12_convert_old_auth_safe(asafe);
308 asafe->old = PR_TRUE;
309 }
311 if(rv != SECSuccess) {
312 goto loser;
313 }
315 asafe->poolp = pfx->poolp;
317 return asafe;
319 loser:
320 return NULL;
321 }
323 /* validates the safe within the authenticated safe item.
324 * in order to be valid:
325 * 1. the privacy salt must be present
326 * 2. the encryption algorithm must be supported (including
327 * export policy)
328 * PR_FALSE indicates an error, PR_TRUE indicates a valid safe
329 */
330 static PRBool
331 sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe *asafe)
332 {
333 PRBool valid = PR_FALSE;
334 SECAlgorithmID *algid;
336 if(asafe == NULL) {
337 return PR_FALSE;
338 }
340 /* if mode is password privacy, then privacySalt is assumed
341 * to be non-zero.
342 */
343 if(asafe->privacySalt.len != 0) {
344 valid = PR_TRUE;
345 asafe->privacySalt.len /= 8;
346 } else {
347 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
348 return PR_FALSE;
349 }
351 /* until spec changes, content will have between 2 and 8 bytes depending
352 * upon the algorithm used if certs are unencrypted...
353 * also want to support case where content is empty -- which we produce
354 */
355 if(SEC_PKCS7IsContentEmpty(asafe->safe, 8) == PR_TRUE) {
356 asafe->emptySafe = PR_TRUE;
357 return PR_TRUE;
358 }
360 asafe->emptySafe = PR_FALSE;
362 /* make sure that a pbe algorithm is being used */
363 algid = SEC_PKCS7GetEncryptionAlgorithm(asafe->safe);
364 if(algid != NULL) {
365 if(SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
366 valid = SEC_PKCS12DecryptionAllowed(algid);
368 if(valid == PR_FALSE) {
369 PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
370 }
371 } else {
372 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
373 valid = PR_FALSE;
374 }
375 } else {
376 valid = PR_FALSE;
377 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
378 }
380 return valid;
381 }
383 /* validates authenticates safe:
384 * 1. checks that the version is supported
385 * 2. checks that only password privacy mode is used (currently)
386 * 3. further, makes sure safe has appropriate policies per above function
387 * PR_FALSE indicates failure.
388 */
389 static PRBool
390 sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
391 {
392 PRBool valid = PR_TRUE;
393 SECOidTag safe_type;
394 int version;
396 if(asafe == NULL) {
397 return PR_FALSE;
398 }
400 /* check version, since it is default it may not be present.
401 * therefore, assume ok
402 */
403 if((asafe->version.len > 0) && (asafe->old == PR_FALSE)) {
404 version = DER_GetInteger(&asafe->version);
405 if(version > SEC_PKCS12_PFX_VERSION) {
406 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION);
407 return PR_FALSE;
408 }
409 }
411 /* validate password mode is being used */
412 safe_type = SEC_PKCS7ContentType(asafe->safe);
413 switch(safe_type)
414 {
415 case SEC_OID_PKCS7_ENCRYPTED_DATA:
416 valid = sec_pkcs12_validate_encrypted_safe(asafe);
417 break;
418 case SEC_OID_PKCS7_ENVELOPED_DATA:
419 default:
420 PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
421 valid = PR_FALSE;
422 break;
423 }
425 return valid;
426 }
428 /* retrieves the authenticated safe item from the PFX item
429 * before returning the authenticated safe, the validity of the
430 * authenticated safe is checked and if valid, returned.
431 * a return of NULL indicates that an error occurred.
432 */
433 static SEC_PKCS12AuthenticatedSafe *
434 sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem *pfx)
435 {
436 SEC_PKCS12AuthenticatedSafe *asafe;
437 PRBool valid_safe;
439 if(pfx == NULL) {
440 return NULL;
441 }
443 asafe = sec_pkcs12_decode_authenticated_safe(pfx);
444 if(asafe == NULL) {
445 return NULL;
446 }
448 valid_safe = sec_pkcs12_validate_auth_safe(asafe);
449 if(valid_safe != PR_TRUE) {
450 asafe = NULL;
451 } else if(asafe) {
452 asafe->baggage.poolp = asafe->poolp;
453 }
455 return asafe;
456 }
458 /* decrypts the authenticated safe.
459 * a return of anything but SECSuccess indicates an error. the
460 * password is not known to be valid until the call to the
461 * function sec_pkcs12_get_safe_contents. If decoding the safe
462 * fails, it is assumed the password was incorrect and the error
463 * is set then. any failure here is assumed to be due to
464 * internal problems in SEC_PKCS7DecryptContents or below.
465 */
466 static SECStatus
467 sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe,
468 SECItem *pwitem,
469 void *wincx)
470 {
471 SECStatus rv = SECFailure;
472 SECItem *vpwd = NULL;
474 if((asafe == NULL) || (pwitem == NULL)) {
475 return SECFailure;
476 }
478 if(asafe->old == PR_FALSE) {
479 vpwd = sec_pkcs12_create_virtual_password(pwitem, &asafe->privacySalt,
480 asafe->swapUnicode);
481 if(vpwd == NULL) {
482 return SECFailure;
483 }
484 }
486 rv = SEC_PKCS7DecryptContents(asafe->poolp, asafe->safe,
487 (asafe->old ? pwitem : vpwd), wincx);
489 if(asafe->old == PR_FALSE) {
490 SECITEM_ZfreeItem(vpwd, PR_TRUE);
491 }
493 return rv;
494 }
496 /* extract the safe from the authenticated safe.
497 * if we are unable to decode the safe, then it is likely that the
498 * safe has not been decrypted or the password used to decrypt
499 * the safe was invalid. we assume that the password was invalid and
500 * set an error accordingly.
501 * a return of NULL indicates that an error occurred.
502 */
503 static SEC_PKCS12SafeContents *
504 sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe *asafe)
505 {
506 SECItem *src = NULL;
507 SEC_PKCS12SafeContents *safe = NULL;
508 SECStatus rv = SECFailure;
510 if(asafe == NULL) {
511 return NULL;
512 }
514 safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(asafe->poolp,
515 sizeof(SEC_PKCS12SafeContents));
516 if(safe == NULL) {
517 return NULL;
518 }
519 safe->poolp = asafe->poolp;
520 safe->old = asafe->old;
521 safe->swapUnicode = asafe->swapUnicode;
523 src = SEC_PKCS7GetContent(asafe->safe);
524 if(src != NULL) {
525 const SEC_ASN1Template *theTemplate;
526 if(asafe->old != PR_TRUE) {
527 theTemplate = SEC_PKCS12SafeContentsTemplate;
528 } else {
529 theTemplate = SEC_PKCS12SafeContentsTemplate_OLD;
530 }
532 rv = SEC_ASN1DecodeItem(asafe->poolp, safe, theTemplate, src);
534 /* if we could not decode the item, password was probably invalid */
535 if(rv != SECSuccess) {
536 safe = NULL;
537 PORT_SetError(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT);
538 }
539 } else {
540 PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
541 rv = SECFailure;
542 }
544 return safe;
545 }
547 /* import PFX item
548 * der_pfx is the der encoded pfx structure
549 * pbef and pbearg are the integrity/encryption password call back
550 * ncCall is the nickname collision calllback
551 * slot is the destination token
552 * wincx window handler
553 *
554 * on error, error code set and SECFailure returned
555 */
556 SECStatus
557 SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem,
558 SEC_PKCS12NicknameCollisionCallback ncCall,
559 PK11SlotInfo *slot,
560 void *wincx)
561 {
562 SEC_PKCS12PFXItem *pfx;
563 SEC_PKCS12AuthenticatedSafe *asafe;
564 SEC_PKCS12SafeContents *safe_contents = NULL;
565 SECStatus rv;
567 if(!der_pfx || !pwitem || !slot) {
568 return SECFailure;
569 }
571 /* decode and validate each section */
572 rv = SECFailure;
574 pfx = sec_pkcs12_get_pfx(der_pfx, pwitem);
575 if(pfx != NULL) {
576 asafe = sec_pkcs12_get_auth_safe(pfx);
577 if(asafe != NULL) {
579 /* decrypt safe -- only if not empty */
580 if(asafe->emptySafe != PR_TRUE) {
581 rv = sec_pkcs12_decrypt_auth_safe(asafe, pwitem, wincx);
582 if(rv == SECSuccess) {
583 safe_contents = sec_pkcs12_get_safe_contents(asafe);
584 if(safe_contents == NULL) {
585 rv = SECFailure;
586 }
587 }
588 } else {
589 safe_contents = sec_pkcs12_create_safe_contents(asafe->poolp);
590 if(safe_contents == NULL) {
591 rv = SECFailure;
592 } else {
593 safe_contents->swapUnicode = pfx->swapUnicode;
594 rv = SECSuccess;
595 }
596 }
598 /* get safe contents and begin import */
599 if(rv == SECSuccess) {
600 SEC_PKCS12DecoderContext *p12dcx;
602 p12dcx = sec_PKCS12ConvertOldSafeToNew(pfx->poolp, slot,
603 pfx->swapUnicode,
604 pwitem, wincx, safe_contents,
605 &asafe->baggage);
606 if(!p12dcx) {
607 rv = SECFailure;
608 goto loser;
609 }
611 if(SEC_PKCS12DecoderValidateBags(p12dcx, ncCall)
612 != SECSuccess) {
613 rv = SECFailure;
614 goto loser;
615 }
617 rv = SEC_PKCS12DecoderImportBags(p12dcx);
618 }
620 }
621 }
623 loser:
625 if(pfx) {
626 SEC_PKCS12DestroyPFX(pfx);
627 }
629 return rv;
630 }
632 PRBool
633 SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength)
634 {
635 int lengthLength;
637 PRBool valid = PR_FALSE;
639 if(buf == NULL) {
640 return PR_FALSE;
641 }
643 /* check for constructed sequence identifier tag */
644 if(*buf == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) {
645 totalLength--; /* header byte taken care of */
646 buf++;
648 lengthLength = (long int)SEC_ASN1LengthLength(totalLength - 1);
649 if(totalLength > 0x7f) {
650 lengthLength--;
651 *buf &= 0x7f; /* remove bit 8 indicator */
652 if((*buf - (char)lengthLength) == 0) {
653 valid = PR_TRUE;
654 }
655 } else {
656 lengthLength--;
657 if((*buf - (char)lengthLength) == 0) {
658 valid = PR_TRUE;
659 }
660 }
661 }
663 return valid;
664 }