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