OLD | NEW |
| (Empty) |
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 "p12t.h" | |
6 #include "p12.h" | |
7 #include "plarena.h" | |
8 #include "secitem.h" | |
9 #include "secoid.h" | |
10 #include "seccomon.h" | |
11 #include "secport.h" | |
12 #include "cert.h" | |
13 #include "secpkcs7.h" | |
14 #include "secasn1.h" | |
15 #include "secerr.h" | |
16 #include "pk11func.h" | |
17 #include "p12plcy.h" | |
18 #include "p12local.h" | |
19 #include "prcpucfg.h" | |
20 | |
21 extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */ | |
22 | |
23 /* | |
24 ** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder | |
25 ** contexts. It can be difficult to keep straight. Here's a picture: | |
26 ** | |
27 ** "outer" ASN.1 encoder. The output goes to the library caller's CB. | |
28 ** "middle" PKCS7 encoder. Feeds the "outer" ASN.1 encoder. | |
29 ** "middle" ASN1 encoder. Encodes the encrypted aSafes. | |
30 ** Feeds the "middle" P7 encoder above. | |
31 ** "inner" PKCS7 encoder. Encrypts the "authenticated Safes" (aSafes) | |
32 ** Feeds the "middle" ASN.1 encoder above. | |
33 ** "inner" ASN.1 encoder. Encodes the unencrypted aSafes. | |
34 ** Feeds the "inner" P7 enocder above. | |
35 ** | |
36 ** Buffering has been added at each point where the output of an ASN.1 | |
37 ** encoder feeds the input of a PKCS7 encoder. | |
38 */ | |
39 | |
40 /********************************* | |
41 * Output buffer object, used to buffer output from ASN.1 encoder | |
42 * before passing data on down to the next PKCS7 encoder. | |
43 *********************************/ | |
44 | |
45 #define PK12_OUTPUT_BUFFER_SIZE 8192 | |
46 | |
47 struct sec_pkcs12OutputBufferStr { | |
48 SEC_PKCS7EncoderContext * p7eCx; | |
49 PK11Context * hmacCx; | |
50 unsigned int numBytes; | |
51 unsigned int bufBytes; | |
52 char buf[PK12_OUTPUT_BUFFER_SIZE]; | |
53 }; | |
54 typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer; | |
55 | |
56 /********************************* | |
57 * Structures used in exporting the PKCS 12 blob | |
58 *********************************/ | |
59 | |
60 /* A SafeInfo is used for each ContentInfo which makes up the | |
61 * sequence of safes in the AuthenticatedSafe portion of the | |
62 * PFX structure. | |
63 */ | |
64 struct SEC_PKCS12SafeInfoStr { | |
65 PLArenaPool *arena; | |
66 | |
67 /* information for setting up password encryption */ | |
68 SECItem pwitem; | |
69 SECOidTag algorithm; | |
70 PK11SymKey *encryptionKey; | |
71 | |
72 /* how many items have been stored in this safe, | |
73 * we will skip any safe which does not contain any | |
74 * items | |
75 */ | |
76 unsigned int itemCount; | |
77 | |
78 /* the content info for the safe */ | |
79 SEC_PKCS7ContentInfo *cinfo; | |
80 | |
81 sec_PKCS12SafeContents *safe; | |
82 }; | |
83 | |
84 /* An opaque structure which contains information needed for exporting | |
85 * certificates and keys through PKCS 12. | |
86 */ | |
87 struct SEC_PKCS12ExportContextStr { | |
88 PLArenaPool *arena; | |
89 PK11SlotInfo *slot; | |
90 void *wincx; | |
91 | |
92 /* integrity information */ | |
93 PRBool integrityEnabled; | |
94 PRBool pwdIntegrity; | |
95 union { | |
96 struct sec_PKCS12PasswordModeInfo pwdInfo; | |
97 struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; | |
98 } integrityInfo; | |
99 | |
100 /* helper functions */ | |
101 /* retrieve the password call back */ | |
102 SECKEYGetPasswordKey pwfn; | |
103 void *pwfnarg; | |
104 | |
105 /* safe contents bags */ | |
106 SEC_PKCS12SafeInfo **safeInfos; | |
107 unsigned int safeInfoCount; | |
108 | |
109 /* the sequence of safes */ | |
110 sec_PKCS12AuthenticatedSafe authSafe; | |
111 | |
112 /* information needing deletion */ | |
113 CERTCertificate **certList; | |
114 }; | |
115 | |
116 /* structures for passing information to encoder callbacks when processing | |
117 * data through the ASN1 engine. | |
118 */ | |
119 struct sec_pkcs12_encoder_output { | |
120 SEC_PKCS12EncoderOutputCallback outputfn; | |
121 void *outputarg; | |
122 }; | |
123 | |
124 struct sec_pkcs12_hmac_and_output_info { | |
125 void *arg; | |
126 struct sec_pkcs12_encoder_output output; | |
127 }; | |
128 | |
129 /* An encoder context which is used for the actual encoding | |
130 * portion of PKCS 12. | |
131 */ | |
132 typedef struct sec_PKCS12EncoderContextStr { | |
133 PLArenaPool *arena; | |
134 SEC_PKCS12ExportContext *p12exp; | |
135 | |
136 /* encoder information - this is set up based on whether | |
137 * password based or public key pased privacy is being used | |
138 */ | |
139 SEC_ASN1EncoderContext *outerA1ecx; | |
140 union { | |
141 struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; | |
142 struct sec_pkcs12_encoder_output encOutput; | |
143 } output; | |
144 | |
145 /* structures for encoding of PFX and MAC */ | |
146 sec_PKCS12PFXItem pfx; | |
147 sec_PKCS12MacData mac; | |
148 | |
149 /* authenticated safe encoding tracking information */ | |
150 SEC_PKCS7ContentInfo *aSafeCinfo; | |
151 SEC_PKCS7EncoderContext *middleP7ecx; | |
152 SEC_ASN1EncoderContext *middleA1ecx; | |
153 unsigned int currentSafe; | |
154 | |
155 /* hmac context */ | |
156 PK11Context *hmacCx; | |
157 | |
158 /* output buffers */ | |
159 sec_pkcs12OutputBuffer middleBuf; | |
160 sec_pkcs12OutputBuffer innerBuf; | |
161 | |
162 } sec_PKCS12EncoderContext; | |
163 | |
164 | |
165 /********************************* | |
166 * Export setup routines | |
167 *********************************/ | |
168 | |
169 /* SEC_PKCS12CreateExportContext | |
170 * Creates an export context and sets the unicode and password retrieval | |
171 * callbacks. This is the first call which must be made when exporting | |
172 * a PKCS 12 blob. | |
173 * | |
174 * pwfn, pwfnarg - password retrieval callback and argument. these are | |
175 * required for password-authentication mode. | |
176 */ | |
177 SEC_PKCS12ExportContext * | |
178 SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, | |
179 PK11SlotInfo *slot, void *wincx) | |
180 { | |
181 PLArenaPool *arena = NULL; | |
182 SEC_PKCS12ExportContext *p12ctxt = NULL; | |
183 | |
184 /* allocate the arena and create the context */ | |
185 arena = PORT_NewArena(4096); | |
186 if(!arena) { | |
187 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
188 return NULL; | |
189 } | |
190 | |
191 p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, | |
192 sizeof(SEC_PKCS12ExportContext)); | |
193 if(!p12ctxt) { | |
194 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
195 goto loser; | |
196 } | |
197 | |
198 /* password callback for key retrieval */ | |
199 p12ctxt->pwfn = pwfn; | |
200 p12ctxt->pwfnarg = pwfnarg; | |
201 | |
202 p12ctxt->integrityEnabled = PR_FALSE; | |
203 p12ctxt->arena = arena; | |
204 p12ctxt->wincx = wincx; | |
205 p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot(); | |
206 | |
207 return p12ctxt; | |
208 | |
209 loser: | |
210 if(arena) { | |
211 PORT_FreeArena(arena, PR_TRUE); | |
212 } | |
213 | |
214 return NULL; | |
215 } | |
216 | |
217 /* | |
218 * Adding integrity mode | |
219 */ | |
220 | |
221 /* SEC_PKCS12AddPasswordIntegrity | |
222 * Add password integrity to the exported data. If an integrity method | |
223 * has already been set, then return an error. | |
224 * | |
225 * p12ctxt - the export context | |
226 * pwitem - the password for integrity mode | |
227 * integAlg - the integrity algorithm to use for authentication. | |
228 */ | |
229 SECStatus | |
230 SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, | |
231 SECItem *pwitem, SECOidTag integAlg) | |
232 { | |
233 if(!p12ctxt || p12ctxt->integrityEnabled) { | |
234 return SECFailure; | |
235 } | |
236 | |
237 /* set up integrity information */ | |
238 p12ctxt->pwdIntegrity = PR_TRUE; | |
239 p12ctxt->integrityInfo.pwdInfo.password = | |
240 (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); | |
241 if(!p12ctxt->integrityInfo.pwdInfo.password) { | |
242 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
243 return SECFailure; | |
244 } | |
245 if(SECITEM_CopyItem(p12ctxt->arena, | |
246 p12ctxt->integrityInfo.pwdInfo.password, pwitem) | |
247 != SECSuccess) { | |
248 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
249 return SECFailure; | |
250 } | |
251 p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; | |
252 p12ctxt->integrityEnabled = PR_TRUE; | |
253 | |
254 return SECSuccess; | |
255 } | |
256 | |
257 /* SEC_PKCS12AddPublicKeyIntegrity | |
258 * Add public key integrity to the exported data. If an integrity method | |
259 * has already been set, then return an error. The certificate must be | |
260 * allowed to be used as a signing cert. | |
261 * | |
262 * p12ctxt - the export context | |
263 * cert - signer certificate | |
264 * certDb - the certificate database | |
265 * algorithm - signing algorithm | |
266 * keySize - size of the signing key (?) | |
267 */ | |
268 SECStatus | |
269 SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, | |
270 CERTCertificate *cert, CERTCertDBHandle *certDb, | |
271 SECOidTag algorithm, int keySize) | |
272 { | |
273 if(!p12ctxt) { | |
274 return SECFailure; | |
275 } | |
276 | |
277 p12ctxt->integrityInfo.pubkeyInfo.cert = cert; | |
278 p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; | |
279 p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; | |
280 p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; | |
281 p12ctxt->integrityEnabled = PR_TRUE; | |
282 | |
283 return SECSuccess; | |
284 } | |
285 | |
286 | |
287 /* | |
288 * Adding safes - encrypted (password/public key) or unencrypted | |
289 * Each of the safe creation routines return an opaque pointer which | |
290 * are later passed into the routines for exporting certificates and | |
291 * keys. | |
292 */ | |
293 | |
294 /* append the newly created safeInfo to list of safeInfos in the export | |
295 * context. | |
296 */ | |
297 static SECStatus | |
298 sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo
*info) | |
299 { | |
300 void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; | |
301 | |
302 if(!p12ctxt || !info) { | |
303 return SECFailure; | |
304 } | |
305 | |
306 mark = PORT_ArenaMark(p12ctxt->arena); | |
307 | |
308 /* if no safeInfos have been set, create the list, otherwise expand it. */ | |
309 if(!p12ctxt->safeInfoCount) { | |
310 p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->ar
ena, | |
311 2 * sizeof(SEC_PKCS12SafeInfo *)); | |
312 dummy1 = p12ctxt->safeInfos; | |
313 p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->a
rena, | |
314 2 * sizeof(SECItem *)); | |
315 dummy2 = p12ctxt->authSafe.encodedSafes; | |
316 } else { | |
317 dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, | |
318 (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12S
afeInfo *), | |
319 (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12S
afeInfo *)); | |
320 p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; | |
321 dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, | |
322 (p12ctxt->authSafe.safeCount + 1) * sizeof(SECIte
m *), | |
323 (p12ctxt->authSafe.safeCount + 2) * sizeof(SECIte
m *)); | |
324 p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; | |
325 } | |
326 if(!dummy1 || !dummy2) { | |
327 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
328 goto loser; | |
329 } | |
330 | |
331 /* append the new safeInfo and null terminate the list */ | |
332 p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; | |
333 p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; | |
334 p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = | |
335 (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); | |
336 if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { | |
337 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
338 goto loser; | |
339 } | |
340 p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; | |
341 | |
342 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
343 return SECSuccess; | |
344 | |
345 loser: | |
346 PORT_ArenaRelease(p12ctxt->arena, mark); | |
347 return SECFailure; | |
348 } | |
349 | |
350 /* SEC_PKCS12CreatePasswordPrivSafe | |
351 * Create a password privacy safe to store exported information in. | |
352 * | |
353 * p12ctxt - export context | |
354 * pwitem - password for encryption | |
355 * privAlg - pbe algorithm through which encryption is done. | |
356 */ | |
357 SEC_PKCS12SafeInfo * | |
358 SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, | |
359 SECItem *pwitem, SECOidTag privAlg) | |
360 { | |
361 SEC_PKCS12SafeInfo *safeInfo = NULL; | |
362 void *mark = NULL; | |
363 PK11SlotInfo *slot = NULL; | |
364 SECAlgorithmID *algId; | |
365 SECItem uniPwitem = {siBuffer, NULL, 0}; | |
366 | |
367 if(!p12ctxt) { | |
368 return NULL; | |
369 } | |
370 | |
371 /* allocate the safe info */ | |
372 mark = PORT_ArenaMark(p12ctxt->arena); | |
373 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, | |
374 sizeof(SEC_PKCS12SafeInfo)); | |
375 if(!safeInfo) { | |
376 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
377 PORT_ArenaRelease(p12ctxt->arena, mark); | |
378 return NULL; | |
379 } | |
380 | |
381 safeInfo->itemCount = 0; | |
382 | |
383 /* create the encrypted safe */ | |
384 safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, | |
385 p12ctxt->pwfnarg); | |
386 if(!safeInfo->cinfo) { | |
387 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
388 goto loser; | |
389 } | |
390 safeInfo->arena = p12ctxt->arena; | |
391 | |
392 /* convert the password to unicode */ | |
393 if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, | |
394 PR_TRUE, PR_TRUE, PR_TRUE)) { | |
395 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
396 goto loser; | |
397 } | |
398 if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuc
cess) { | |
399 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
400 goto loser; | |
401 } | |
402 | |
403 /* generate the encryption key */ | |
404 slot = PK11_ReferenceSlot(p12ctxt->slot); | |
405 if(!slot) { | |
406 slot = PK11_GetInternalKeySlot(); | |
407 if(!slot) { | |
408 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
409 goto loser; | |
410 } | |
411 } | |
412 | |
413 algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); | |
414 safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, | |
415 PR_FALSE, p12ctxt->wincx); | |
416 if(!safeInfo->encryptionKey) { | |
417 goto loser; | |
418 } | |
419 | |
420 safeInfo->arena = p12ctxt->arena; | |
421 safeInfo->safe = NULL; | |
422 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { | |
423 goto loser; | |
424 } | |
425 | |
426 if(uniPwitem.data) { | |
427 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); | |
428 } | |
429 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
430 | |
431 if (slot) { | |
432 PK11_FreeSlot(slot); | |
433 } | |
434 return safeInfo; | |
435 | |
436 loser: | |
437 if (slot) { | |
438 PK11_FreeSlot(slot); | |
439 } | |
440 if(safeInfo->cinfo) { | |
441 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); | |
442 } | |
443 | |
444 if(uniPwitem.data) { | |
445 SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); | |
446 } | |
447 | |
448 PORT_ArenaRelease(p12ctxt->arena, mark); | |
449 return NULL; | |
450 } | |
451 | |
452 /* SEC_PKCS12CreateUnencryptedSafe | |
453 * Creates an unencrypted safe within the export context. | |
454 * | |
455 * p12ctxt - the export context | |
456 */ | |
457 SEC_PKCS12SafeInfo * | |
458 SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) | |
459 { | |
460 SEC_PKCS12SafeInfo *safeInfo = NULL; | |
461 void *mark = NULL; | |
462 | |
463 if(!p12ctxt) { | |
464 return NULL; | |
465 } | |
466 | |
467 /* create the safe info */ | |
468 mark = PORT_ArenaMark(p12ctxt->arena); | |
469 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, | |
470 sizeof(SEC_PKCS12SafeInfo)); | |
471 if(!safeInfo) { | |
472 PORT_ArenaRelease(p12ctxt->arena, mark); | |
473 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
474 return NULL; | |
475 } | |
476 | |
477 safeInfo->itemCount = 0; | |
478 | |
479 /* create the safe content */ | |
480 safeInfo->cinfo = SEC_PKCS7CreateData(); | |
481 if(!safeInfo->cinfo) { | |
482 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
483 goto loser; | |
484 } | |
485 | |
486 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { | |
487 goto loser; | |
488 } | |
489 | |
490 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
491 return safeInfo; | |
492 | |
493 loser: | |
494 if(safeInfo->cinfo) { | |
495 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); | |
496 } | |
497 | |
498 PORT_ArenaRelease(p12ctxt->arena, mark); | |
499 return NULL; | |
500 } | |
501 | |
502 /* SEC_PKCS12CreatePubKeyEncryptedSafe | |
503 * Creates a safe which is protected by public key encryption. | |
504 * | |
505 * p12ctxt - the export context | |
506 * certDb - the certificate database | |
507 * signer - the signer's certificate | |
508 * recipients - the list of recipient certificates. | |
509 * algorithm - the encryption algorithm to use | |
510 * keysize - the algorithms key size (?) | |
511 */ | |
512 SEC_PKCS12SafeInfo * | |
513 SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, | |
514 CERTCertDBHandle *certDb, | |
515 CERTCertificate *signer, | |
516 CERTCertificate **recipients, | |
517 SECOidTag algorithm, int keysize) | |
518 { | |
519 SEC_PKCS12SafeInfo *safeInfo = NULL; | |
520 void *mark = NULL; | |
521 | |
522 if(!p12ctxt || !signer || !recipients || !(*recipients)) { | |
523 return NULL; | |
524 } | |
525 | |
526 /* allocate the safeInfo */ | |
527 mark = PORT_ArenaMark(p12ctxt->arena); | |
528 safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, | |
529 sizeof(SEC_PKCS12SafeInfo)
); | |
530 if(!safeInfo) { | |
531 PORT_ArenaRelease(p12ctxt->arena, mark); | |
532 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
533 return NULL; | |
534 } | |
535 | |
536 safeInfo->itemCount = 0; | |
537 safeInfo->arena = p12ctxt->arena; | |
538 | |
539 /* create the enveloped content info using certUsageEmailSigner currently. | |
540 * XXX We need to eventually use something other than certUsageEmailSigner | |
541 */ | |
542 safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, | |
543 certDb, algorithm, keysize, | |
544 p12ctxt->pwfn, p12ctxt->pwfnarg); | |
545 if(!safeInfo->cinfo) { | |
546 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
547 goto loser; | |
548 } | |
549 | |
550 /* add recipients */ | |
551 if(recipients) { | |
552 unsigned int i = 0; | |
553 while(recipients[i] != NULL) { | |
554 SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], | |
555 certUsageEmailRecipient, certDb); | |
556 if(rv != SECSuccess) { | |
557 goto loser; | |
558 } | |
559 i++; | |
560 } | |
561 } | |
562 | |
563 if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { | |
564 goto loser; | |
565 } | |
566 | |
567 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
568 return safeInfo; | |
569 | |
570 loser: | |
571 if(safeInfo->cinfo) { | |
572 SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); | |
573 safeInfo->cinfo = NULL; | |
574 } | |
575 | |
576 PORT_ArenaRelease(p12ctxt->arena, mark); | |
577 return NULL; | |
578 } | |
579 | |
580 /********************************* | |
581 * Routines to handle the exporting of the keys and certificates | |
582 *********************************/ | |
583 | |
584 /* creates a safe contents which safeBags will be appended to */ | |
585 sec_PKCS12SafeContents * | |
586 sec_PKCS12CreateSafeContents(PLArenaPool *arena) | |
587 { | |
588 sec_PKCS12SafeContents *safeContents; | |
589 | |
590 if(arena == NULL) { | |
591 return NULL; | |
592 } | |
593 | |
594 /* create the safe contents */ | |
595 safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, | |
596 sizeof(sec_PKCS12SafeContents)); | |
597 if(!safeContents) { | |
598 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
599 goto loser; | |
600 } | |
601 | |
602 /* set up the internal contents info */ | |
603 safeContents->safeBags = NULL; | |
604 safeContents->arena = arena; | |
605 safeContents->bagCount = 0; | |
606 | |
607 return safeContents; | |
608 | |
609 loser: | |
610 return NULL; | |
611 } | |
612 | |
613 /* appends a safe bag to a safeContents using the specified arena. | |
614 */ | |
615 SECStatus | |
616 sec_pkcs12_append_bag_to_safe_contents(PLArenaPool *arena, | |
617 sec_PKCS12SafeContents *safeContents, | |
618 sec_PKCS12SafeBag *safeBag) | |
619 { | |
620 void *mark = NULL, *dummy = NULL; | |
621 | |
622 if(!arena || !safeBag || !safeContents) { | |
623 return SECFailure; | |
624 } | |
625 | |
626 mark = PORT_ArenaMark(arena); | |
627 if(!mark) { | |
628 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
629 return SECFailure; | |
630 } | |
631 | |
632 /* allocate space for the list, or reallocate to increase space */ | |
633 if(!safeContents->safeBags) { | |
634 safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, | |
635 (2 * sizeof(sec_PKCS12SafeBag *)
)); | |
636 dummy = safeContents->safeBags; | |
637 safeContents->bagCount = 0; | |
638 } else { | |
639 dummy = PORT_ArenaGrow(arena, safeContents->safeBags, | |
640 (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag
*), | |
641 (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag
*)); | |
642 safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; | |
643 } | |
644 | |
645 if(!dummy) { | |
646 PORT_ArenaRelease(arena, mark); | |
647 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
648 return SECFailure; | |
649 } | |
650 | |
651 /* append the bag at the end and null terminate the list */ | |
652 safeContents->safeBags[safeContents->bagCount++] = safeBag; | |
653 safeContents->safeBags[safeContents->bagCount] = NULL; | |
654 | |
655 PORT_ArenaUnmark(arena, mark); | |
656 | |
657 return SECSuccess; | |
658 } | |
659 | |
660 /* appends a safeBag to a specific safeInfo. | |
661 */ | |
662 SECStatus | |
663 sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, | |
664 SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) | |
665 { | |
666 sec_PKCS12SafeContents *dest; | |
667 SECStatus rv = SECFailure; | |
668 | |
669 if(!p12ctxt || !safeBag || !safeInfo) { | |
670 return SECFailure; | |
671 } | |
672 | |
673 if(!safeInfo->safe) { | |
674 safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); | |
675 if(!safeInfo->safe) { | |
676 return SECFailure; | |
677 } | |
678 } | |
679 | |
680 dest = safeInfo->safe; | |
681 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); | |
682 if(rv == SECSuccess) { | |
683 safeInfo->itemCount++; | |
684 } | |
685 | |
686 return rv; | |
687 } | |
688 | |
689 /* Creates a safeBag of the specified type, and if bagData is specified, | |
690 * the contents are set. The contents could be set later by the calling | |
691 * routine. | |
692 */ | |
693 sec_PKCS12SafeBag * | |
694 sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, | |
695 void *bagData) | |
696 { | |
697 sec_PKCS12SafeBag *safeBag; | |
698 PRBool setName = PR_TRUE; | |
699 void *mark = NULL; | |
700 SECStatus rv = SECSuccess; | |
701 SECOidData *oidData = NULL; | |
702 | |
703 if(!p12ctxt) { | |
704 return NULL; | |
705 } | |
706 | |
707 mark = PORT_ArenaMark(p12ctxt->arena); | |
708 if(!mark) { | |
709 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
710 return NULL; | |
711 } | |
712 | |
713 safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, | |
714 sizeof(sec_PKCS12SafeBag)); | |
715 if(!safeBag) { | |
716 PORT_ArenaRelease(p12ctxt->arena, mark); | |
717 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
718 return NULL; | |
719 } | |
720 | |
721 /* set the bags content based upon bag type */ | |
722 switch(bagType) { | |
723 case SEC_OID_PKCS12_V1_KEY_BAG_ID: | |
724 safeBag->safeBagContent.pkcs8KeyBag = | |
725 (SECKEYPrivateKeyInfo *)bagData; | |
726 break; | |
727 case SEC_OID_PKCS12_V1_CERT_BAG_ID: | |
728 safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; | |
729 break; | |
730 case SEC_OID_PKCS12_V1_CRL_BAG_ID: | |
731 safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; | |
732 break; | |
733 case SEC_OID_PKCS12_V1_SECRET_BAG_ID: | |
734 safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; | |
735 break; | |
736 case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: | |
737 safeBag->safeBagContent.pkcs8ShroudedKeyBag = | |
738 (SECKEYEncryptedPrivateKeyInfo *)bagData; | |
739 break; | |
740 case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: | |
741 safeBag->safeBagContent.safeContents = | |
742 (sec_PKCS12SafeContents *)bagData; | |
743 setName = PR_FALSE; | |
744 break; | |
745 default: | |
746 goto loser; | |
747 } | |
748 | |
749 oidData = SECOID_FindOIDByTag(bagType); | |
750 if(oidData) { | |
751 rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->o
id); | |
752 if(rv != SECSuccess) { | |
753 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
754 goto loser; | |
755 } | |
756 } else { | |
757 goto loser; | |
758 } | |
759 | |
760 safeBag->arena = p12ctxt->arena; | |
761 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
762 | |
763 return safeBag; | |
764 | |
765 loser: | |
766 if(mark) { | |
767 PORT_ArenaRelease(p12ctxt->arena, mark); | |
768 } | |
769 | |
770 return NULL; | |
771 } | |
772 | |
773 /* Creates a new certificate bag and returns a pointer to it. If an error | |
774 * occurs NULL is returned. | |
775 */ | |
776 sec_PKCS12CertBag * | |
777 sec_PKCS12NewCertBag(PLArenaPool *arena, SECOidTag certType) | |
778 { | |
779 sec_PKCS12CertBag *certBag = NULL; | |
780 SECOidData *bagType = NULL; | |
781 SECStatus rv; | |
782 void *mark = NULL; | |
783 | |
784 if(!arena) { | |
785 return NULL; | |
786 } | |
787 | |
788 mark = PORT_ArenaMark(arena); | |
789 certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, | |
790 sizeof(sec_PKCS12CertBag)); | |
791 if(!certBag) { | |
792 PORT_ArenaRelease(arena, mark); | |
793 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
794 return NULL; | |
795 } | |
796 | |
797 bagType = SECOID_FindOIDByTag(certType); | |
798 if(!bagType) { | |
799 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
800 goto loser; | |
801 } | |
802 | |
803 rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); | |
804 if(rv != SECSuccess) { | |
805 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
806 goto loser; | |
807 } | |
808 | |
809 PORT_ArenaUnmark(arena, mark); | |
810 return certBag; | |
811 | |
812 loser: | |
813 PORT_ArenaRelease(arena, mark); | |
814 return NULL; | |
815 } | |
816 | |
817 /* Creates a new CRL bag and returns a pointer to it. If an error | |
818 * occurs NULL is returned. | |
819 */ | |
820 sec_PKCS12CRLBag * | |
821 sec_PKCS12NewCRLBag(PLArenaPool *arena, SECOidTag crlType) | |
822 { | |
823 sec_PKCS12CRLBag *crlBag = NULL; | |
824 SECOidData *bagType = NULL; | |
825 SECStatus rv; | |
826 void *mark = NULL; | |
827 | |
828 if(!arena) { | |
829 return NULL; | |
830 } | |
831 | |
832 mark = PORT_ArenaMark(arena); | |
833 crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, | |
834 sizeof(sec_PKCS12CRLBag)); | |
835 if(!crlBag) { | |
836 PORT_ArenaRelease(arena, mark); | |
837 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
838 return NULL; | |
839 } | |
840 | |
841 bagType = SECOID_FindOIDByTag(crlType); | |
842 if(!bagType) { | |
843 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
844 goto loser; | |
845 } | |
846 | |
847 rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); | |
848 if(rv != SECSuccess) { | |
849 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
850 goto loser; | |
851 } | |
852 | |
853 PORT_ArenaUnmark(arena, mark); | |
854 return crlBag; | |
855 | |
856 loser: | |
857 PORT_ArenaRelease(arena, mark); | |
858 return NULL; | |
859 } | |
860 | |
861 /* sec_PKCS12AddAttributeToBag | |
862 * adds an attribute to a safeBag. currently, the only attributes supported | |
863 * are those which are specified within PKCS 12. | |
864 * | |
865 * p12ctxt - the export context | |
866 * safeBag - the safeBag to which attributes are appended | |
867 * attrType - the attribute type | |
868 * attrData - the attribute data | |
869 */ | |
870 SECStatus | |
871 sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, | |
872 sec_PKCS12SafeBag *safeBag, SECOidTag attrType, | |
873 SECItem *attrData) | |
874 { | |
875 sec_PKCS12Attribute *attribute; | |
876 void *mark = NULL, *dummy = NULL; | |
877 SECOidData *oiddata = NULL; | |
878 SECItem unicodeName = { siBuffer, NULL, 0}; | |
879 void *src = NULL; | |
880 unsigned int nItems = 0; | |
881 SECStatus rv; | |
882 | |
883 if(!safeBag || !p12ctxt) { | |
884 return SECFailure; | |
885 } | |
886 | |
887 mark = PORT_ArenaMark(safeBag->arena); | |
888 | |
889 /* allocate the attribute */ | |
890 attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, | |
891 sizeof(sec_PKCS12Attribute)); | |
892 if(!attribute) { | |
893 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
894 goto loser; | |
895 } | |
896 | |
897 /* set up the attribute */ | |
898 oiddata = SECOID_FindOIDByTag(attrType); | |
899 if(!oiddata) { | |
900 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
901 goto loser; | |
902 } | |
903 if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != | |
904 SECSuccess) { | |
905 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
906 goto loser; | |
907 } | |
908 | |
909 nItems = 1; | |
910 switch(attrType) { | |
911 case SEC_OID_PKCS9_LOCAL_KEY_ID: | |
912 { | |
913 src = attrData; | |
914 break; | |
915 } | |
916 case SEC_OID_PKCS9_FRIENDLY_NAME: | |
917 { | |
918 if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, | |
919 &unicodeName, attrData, PR_FALSE, | |
920 PR_FALSE, PR_TRUE)) { | |
921 goto loser; | |
922 } | |
923 src = &unicodeName; | |
924 break; | |
925 } | |
926 default: | |
927 goto loser; | |
928 } | |
929 | |
930 /* append the attribute to the attribute value list */ | |
931 attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, | |
932 ((nItems + 1) * sizeof(SECItem *))); | |
933 if(!attribute->attrValue) { | |
934 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
935 goto loser; | |
936 } | |
937 | |
938 /* XXX this will need to be changed if attributes requiring more than | |
939 * one element are ever used. | |
940 */ | |
941 attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, | |
942 sizeof(SECItem)); | |
943 if(!attribute->attrValue[0]) { | |
944 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
945 goto loser; | |
946 } | |
947 attribute->attrValue[1] = NULL; | |
948 | |
949 rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], | |
950 (SECItem*)src); | |
951 if(rv != SECSuccess) { | |
952 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
953 goto loser; | |
954 } | |
955 | |
956 /* append the attribute to the safeBag attributes */ | |
957 if(safeBag->nAttribs) { | |
958 dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, | |
959 ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)
), | |
960 ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *)
)); | |
961 safeBag->attribs = (sec_PKCS12Attribute **)dummy; | |
962 } else { | |
963 safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->are
na, | |
964 2 * sizeof(sec_PKCS12Attribute *
)); | |
965 dummy = safeBag->attribs; | |
966 } | |
967 if(!dummy) { | |
968 goto loser; | |
969 } | |
970 | |
971 safeBag->attribs[safeBag->nAttribs] = attribute; | |
972 safeBag->attribs[++safeBag->nAttribs] = NULL; | |
973 | |
974 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
975 return SECSuccess; | |
976 | |
977 loser: | |
978 if(mark) { | |
979 PORT_ArenaRelease(p12ctxt->arena, mark); | |
980 } | |
981 | |
982 return SECFailure; | |
983 } | |
984 | |
985 /* SEC_PKCS12AddCert | |
986 * Adds a certificate to the data being exported. | |
987 * | |
988 * p12ctxt - the export context | |
989 * safe - the safeInfo to which the certificate is placed | |
990 * nestedDest - if the cert is to be placed within a nested safeContents th
en, | |
991 * this value is to be specified with the destination | |
992 * cert - the cert to export | |
993 * certDb - the certificate database handle | |
994 * keyId - a unique identifier to associate a certificate/key pair | |
995 * includeCertChain - PR_TRUE if the certificate chain is to be included. | |
996 */ | |
997 SECStatus | |
998 SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, | |
999 void *nestedDest, CERTCertificate *cert, | |
1000 CERTCertDBHandle *certDb, SECItem *keyId, | |
1001 PRBool includeCertChain) | |
1002 { | |
1003 sec_PKCS12CertBag *certBag; | |
1004 sec_PKCS12SafeBag *safeBag; | |
1005 void *mark; | |
1006 SECStatus rv; | |
1007 SECItem nick = {siBuffer, NULL,0}; | |
1008 | |
1009 if(!p12ctxt || !cert) { | |
1010 return SECFailure; | |
1011 } | |
1012 mark = PORT_ArenaMark(p12ctxt->arena); | |
1013 | |
1014 /* allocate the cert bag */ | |
1015 certBag = sec_PKCS12NewCertBag(p12ctxt->arena, | |
1016 SEC_OID_PKCS9_X509_CERT); | |
1017 if(!certBag) { | |
1018 goto loser; | |
1019 } | |
1020 | |
1021 if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, | |
1022 &cert->derCert) != SECSuccess) { | |
1023 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1024 goto loser; | |
1025 } | |
1026 | |
1027 /* if the cert chain is to be included, we should only be exporting | |
1028 * the cert from our internal database. | |
1029 */ | |
1030 if(includeCertChain) { | |
1031 CERTCertificateList *certList = CERT_CertChainFromCert(cert, | |
1032 certUsageSSLClien
t, | |
1033 PR_TRUE); | |
1034 unsigned int count = 0; | |
1035 if(!certList) { | |
1036 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1037 goto loser; | |
1038 } | |
1039 | |
1040 /* add cert chain */ | |
1041 for(count = 0; count < (unsigned int)certList->len; count++) { | |
1042 if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) | |
1043 != SECEqual) { | |
1044 CERTCertificate *tempCert; | |
1045 | |
1046 /* decode the certificate */ | |
1047 /* XXX | |
1048 * This was rather silly. The chain is constructed above | |
1049 * by finding all of the CERTCertificate's in the database. | |
1050 * Then the chain is put into a CERTCertificateList, which only | |
1051 * contains the DER. Finally, the DER was decoded, and the | |
1052 * decoded cert was sent recursively back to this function. | |
1053 * Beyond being inefficent, this causes data loss (specifically, | |
1054 * the nickname). Instead, for 3.4, we'll do a lookup by the | |
1055 * DER, which should return the cached entry. | |
1056 */ | |
1057 tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), | |
1058 &certList->certs[count]); | |
1059 if(!tempCert) { | |
1060 CERT_DestroyCertificateList(certList); | |
1061 goto loser; | |
1062 } | |
1063 | |
1064 /* add the certificate */ | |
1065 if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, | |
1066 certDb, NULL, PR_FALSE) != SECSuccess) { | |
1067 CERT_DestroyCertificate(tempCert); | |
1068 CERT_DestroyCertificateList(certList); | |
1069 goto loser; | |
1070 } | |
1071 CERT_DestroyCertificate(tempCert); | |
1072 } | |
1073 } | |
1074 CERT_DestroyCertificateList(certList); | |
1075 } | |
1076 | |
1077 /* if the certificate has a nickname, we will set the friendly name | |
1078 * to that. | |
1079 */ | |
1080 if(cert->nickname) { | |
1081 if (cert->slot && !PK11_IsInternal(cert->slot)) { | |
1082 /* | |
1083 * The cert is coming off of an external token, | |
1084 * let's strip the token name from the nickname | |
1085 * and only add what comes after the colon as the | |
1086 * nickname. -javi | |
1087 */ | |
1088 char *delimit; | |
1089 | |
1090 delimit = PORT_Strchr(cert->nickname,':'); | |
1091 if (delimit == NULL) { | |
1092 nick.data = (unsigned char *)cert->nickname; | |
1093 nick.len = PORT_Strlen(cert->nickname); | |
1094 } else { | |
1095 delimit++; | |
1096 nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, | |
1097 delimit); | |
1098 nick.len = PORT_Strlen(delimit); | |
1099 } | |
1100 } else { | |
1101 nick.data = (unsigned char *)cert->nickname; | |
1102 nick.len = PORT_Strlen(cert->nickname); | |
1103 } | |
1104 } | |
1105 | |
1106 safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, | |
1107 certBag); | |
1108 if(!safeBag) { | |
1109 goto loser; | |
1110 } | |
1111 | |
1112 /* add the friendly name and keyId attributes, if necessary */ | |
1113 if(nick.data) { | |
1114 if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, | |
1115 SEC_OID_PKCS9_FRIENDLY_NAME, &nick) | |
1116 != SECSuccess) { | |
1117 goto loser; | |
1118 } | |
1119 } | |
1120 | |
1121 if(keyId) { | |
1122 if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY
_ID, | |
1123 keyId) != SECSuccess) { | |
1124 goto loser; | |
1125 } | |
1126 } | |
1127 | |
1128 /* append the cert safeBag */ | |
1129 if(nestedDest) { | |
1130 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, | |
1131 (sec_PKCS12SafeContents*)nestedDest, | |
1132 safeBag); | |
1133 } else { | |
1134 rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); | |
1135 } | |
1136 | |
1137 if(rv != SECSuccess) { | |
1138 goto loser; | |
1139 } | |
1140 | |
1141 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
1142 return SECSuccess; | |
1143 | |
1144 loser: | |
1145 if(mark) { | |
1146 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1147 } | |
1148 | |
1149 return SECFailure; | |
1150 } | |
1151 | |
1152 /* SEC_PKCS12AddKeyForCert | |
1153 * Extracts the key associated with a particular certificate and exports | |
1154 * it. | |
1155 * | |
1156 * p12ctxt - the export context | |
1157 * safe - the safeInfo to place the key in | |
1158 * nestedDest - the nested safeContents to place a key | |
1159 * cert - the certificate which the key belongs to | |
1160 * shroudKey - encrypt the private key for export. This value should | |
1161 * always be true. lower level code will not allow the export | |
1162 * of unencrypted private keys. | |
1163 * algorithm - the algorithm with which to encrypt the private key | |
1164 * pwitem - the password to encrypt the private key with | |
1165 * keyId - the keyID attribute | |
1166 * nickName - the nickname attribute | |
1167 */ | |
1168 SECStatus | |
1169 SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *sa
fe, | |
1170 void *nestedDest, CERTCertificate *cert, | |
1171 PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, | |
1172 SECItem *keyId, SECItem *nickName) | |
1173 { | |
1174 void *mark; | |
1175 void *keyItem; | |
1176 SECOidTag keyType; | |
1177 SECStatus rv = SECFailure; | |
1178 SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; | |
1179 sec_PKCS12SafeBag *returnBag; | |
1180 | |
1181 if(!p12ctxt || !cert || !safe) { | |
1182 return SECFailure; | |
1183 } | |
1184 | |
1185 mark = PORT_ArenaMark(p12ctxt->arena); | |
1186 | |
1187 /* retrieve the key based upon the type that it is and | |
1188 * specify the type of safeBag to store the key in | |
1189 */ | |
1190 if(!shroudKey) { | |
1191 | |
1192 /* extract the key unencrypted. this will most likely go away */ | |
1193 SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, | |
1194 p12ctxt->wincx); | |
1195 if(!pki) { | |
1196 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1197 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); | |
1198 return SECFailure; | |
1199 } | |
1200 keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo))
; | |
1201 if(!keyItem) { | |
1202 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1203 goto loser; | |
1204 } | |
1205 rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, | |
1206 (SECKEYPrivateKeyInfo *)keyItem, pki); | |
1207 keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; | |
1208 SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); | |
1209 } else { | |
1210 | |
1211 /* extract the key encrypted */ | |
1212 SECKEYEncryptedPrivateKeyInfo *epki = NULL; | |
1213 PK11SlotInfo *slot = NULL; | |
1214 | |
1215 if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, | |
1216 pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { | |
1217 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1218 goto loser; | |
1219 } | |
1220 | |
1221 /* we want to make sure to take the key out of the key slot */ | |
1222 if(PK11_IsInternal(p12ctxt->slot)) { | |
1223 slot = PK11_GetInternalKeySlot(); | |
1224 } else { | |
1225 slot = PK11_ReferenceSlot(p12ctxt->slot); | |
1226 } | |
1227 | |
1228 epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, | |
1229 &uniPwitem, cert, | |
1230 NSS_PBE_DEFAULT_ITERATION_COUNT, | |
1231 p12ctxt->wincx); | |
1232 PK11_FreeSlot(slot); | |
1233 if(!epki) { | |
1234 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); | |
1235 goto loser; | |
1236 } | |
1237 | |
1238 keyItem = PORT_ArenaZAlloc(p12ctxt->arena, | |
1239 sizeof(SECKEYEncryptedPrivateKeyInfo)); | |
1240 if(!keyItem) { | |
1241 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1242 goto loser; | |
1243 } | |
1244 rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, | |
1245 (SECKEYEncryptedPrivateKeyInfo *)keyItem
, | |
1246 epki); | |
1247 keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; | |
1248 SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); | |
1249 } | |
1250 | |
1251 if(rv != SECSuccess) { | |
1252 goto loser; | |
1253 } | |
1254 | |
1255 /* if no nickname specified, let's see if the certificate has a | |
1256 * nickname. | |
1257 */ | |
1258 if(!nickName) { | |
1259 if(cert->nickname) { | |
1260 nickname.data = (unsigned char *)cert->nickname; | |
1261 nickname.len = PORT_Strlen(cert->nickname); | |
1262 nickName = &nickname; | |
1263 } | |
1264 } | |
1265 | |
1266 /* create the safe bag and set any attributes */ | |
1267 returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); | |
1268 if(!returnBag) { | |
1269 rv = SECFailure; | |
1270 goto loser; | |
1271 } | |
1272 | |
1273 if(nickName) { | |
1274 if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, | |
1275 SEC_OID_PKCS9_FRIENDLY_NAME, nickName) | |
1276 != SECSuccess) { | |
1277 goto loser; | |
1278 } | |
1279 } | |
1280 | |
1281 if(keyId) { | |
1282 if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_K
EY_ID, | |
1283 keyId) != SECSuccess) { | |
1284 goto loser; | |
1285 } | |
1286 } | |
1287 | |
1288 if(nestedDest) { | |
1289 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, | |
1290 (sec_PKCS12SafeContents*)nestedDest, | |
1291 returnBag); | |
1292 } else { | |
1293 rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); | |
1294 } | |
1295 | |
1296 loser: | |
1297 | |
1298 if (rv != SECSuccess) { | |
1299 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1300 } else { | |
1301 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
1302 } | |
1303 | |
1304 return rv; | |
1305 } | |
1306 | |
1307 /* SEC_PKCS12AddCertOrChainAndKey | |
1308 * Add a certificate and key pair to be exported. | |
1309 * | |
1310 * p12ctxt - the export context | |
1311 * certSafe - the safeInfo where the cert is stored | |
1312 * certNestedDest - the nested safeContents to store the cert | |
1313 * keySafe - the safeInfo where the key is stored | |
1314 * keyNestedDest - the nested safeContents to store the key | |
1315 * shroudKey - extract the private key encrypted? | |
1316 * pwitem - the password with which the key is encrypted | |
1317 * algorithm - the algorithm with which the key is encrypted | |
1318 * includeCertChain - also add certs from chain to bag. | |
1319 */ | |
1320 SECStatus | |
1321 SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt, | |
1322 void *certSafe, void *certNestedDest, | |
1323 CERTCertificate *cert, CERTCertDBHandle *certDb, | |
1324 void *keySafe, void *keyNestedDest, | |
1325 PRBool shroudKey, SECItem *pwitem, | |
1326 SECOidTag algorithm, PRBool includeCertChain) | |
1327 { | |
1328 SECStatus rv = SECFailure; | |
1329 SGNDigestInfo *digest = NULL; | |
1330 void *mark = NULL; | |
1331 | |
1332 if(!p12ctxt || !certSafe || !keySafe || !cert) { | |
1333 return SECFailure; | |
1334 } | |
1335 | |
1336 mark = PORT_ArenaMark(p12ctxt->arena); | |
1337 | |
1338 /* generate the thumbprint of the cert to use as a keyId */ | |
1339 digest = sec_pkcs12_compute_thumbprint(&cert->derCert); | |
1340 if(!digest) { | |
1341 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1342 return SECFailure; | |
1343 } | |
1344 | |
1345 /* add the certificate */ | |
1346 rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, | |
1347 (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, | |
1348 &digest->digest, includeCertChain); | |
1349 if(rv != SECSuccess) { | |
1350 goto loser; | |
1351 } | |
1352 | |
1353 /* add the key */ | |
1354 rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, | |
1355 keyNestedDest, cert, | |
1356 shroudKey, algorithm, pwitem, | |
1357 &digest->digest, NULL ); | |
1358 if(rv != SECSuccess) { | |
1359 goto loser; | |
1360 } | |
1361 | |
1362 SGN_DestroyDigestInfo(digest); | |
1363 | |
1364 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
1365 return SECSuccess; | |
1366 | |
1367 loser: | |
1368 SGN_DestroyDigestInfo(digest); | |
1369 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1370 | |
1371 return SECFailure; | |
1372 } | |
1373 | |
1374 /* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */ | |
1375 SECStatus | |
1376 SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, | |
1377 void *certSafe, void *certNestedDest, | |
1378 CERTCertificate *cert, CERTCertDBHandle *certDb, | |
1379 void *keySafe, void *keyNestedDest, | |
1380 PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm) | |
1381 { | |
1382 return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest, | |
1383 cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem, | |
1384 algorithm, PR_TRUE); | |
1385 } | |
1386 | |
1387 | |
1388 /* SEC_PKCS12CreateNestedSafeContents | |
1389 * Allows nesting of safe contents to be implemented. No limit imposed on | |
1390 * depth. | |
1391 * | |
1392 * p12ctxt - the export context | |
1393 * baseSafe - the base safeInfo | |
1394 * nestedDest - a parent safeContents (?) | |
1395 */ | |
1396 void * | |
1397 SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, | |
1398 void *baseSafe, void *nestedDest) | |
1399 { | |
1400 sec_PKCS12SafeContents *newSafe; | |
1401 sec_PKCS12SafeBag *safeContentsBag; | |
1402 void *mark; | |
1403 SECStatus rv; | |
1404 | |
1405 if(!p12ctxt || !baseSafe) { | |
1406 return NULL; | |
1407 } | |
1408 | |
1409 mark = PORT_ArenaMark(p12ctxt->arena); | |
1410 | |
1411 newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); | |
1412 if(!newSafe) { | |
1413 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1414 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1415 return NULL; | |
1416 } | |
1417 | |
1418 /* create the safeContents safeBag */ | |
1419 safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, | |
1420 SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, | |
1421 newSafe); | |
1422 if(!safeContentsBag) { | |
1423 goto loser; | |
1424 } | |
1425 | |
1426 /* append the safeContents to the appropriate area */ | |
1427 if(nestedDest) { | |
1428 rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, | |
1429 (sec_PKCS12SafeContents*)nestedDest, | |
1430 safeContentsBag); | |
1431 } else { | |
1432 rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, | |
1433 safeContentsBag); | |
1434 } | |
1435 if(rv != SECSuccess) { | |
1436 goto loser; | |
1437 } | |
1438 | |
1439 PORT_ArenaUnmark(p12ctxt->arena, mark); | |
1440 return newSafe; | |
1441 | |
1442 loser: | |
1443 PORT_ArenaRelease(p12ctxt->arena, mark); | |
1444 return NULL; | |
1445 } | |
1446 | |
1447 /********************************* | |
1448 * Encoding routines | |
1449 *********************************/ | |
1450 | |
1451 /* Clean up the resources allocated by a sec_PKCS12EncoderContext. */ | |
1452 static void | |
1453 sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc) | |
1454 { | |
1455 if(p12enc) { | |
1456 if(p12enc->outerA1ecx) { | |
1457 SEC_ASN1EncoderFinish(p12enc->outerA1ecx); | |
1458 p12enc->outerA1ecx = NULL; | |
1459 } | |
1460 if(p12enc->aSafeCinfo) { | |
1461 SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); | |
1462 p12enc->aSafeCinfo = NULL; | |
1463 } | |
1464 if(p12enc->middleP7ecx) { | |
1465 SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn, | |
1466 p12enc->p12exp->pwfnarg); | |
1467 p12enc->middleP7ecx = NULL; | |
1468 } | |
1469 if(p12enc->middleA1ecx) { | |
1470 SEC_ASN1EncoderFinish(p12enc->middleA1ecx); | |
1471 p12enc->middleA1ecx = NULL; | |
1472 } | |
1473 if(p12enc->hmacCx) { | |
1474 PK11_DestroyContext(p12enc->hmacCx, PR_TRUE); | |
1475 p12enc->hmacCx = NULL; | |
1476 } | |
1477 } | |
1478 } | |
1479 | |
1480 /* set up the encoder context based on information in the export context | |
1481 * and return the newly allocated enocoder context. A return of NULL | |
1482 * indicates an error occurred. | |
1483 */ | |
1484 static sec_PKCS12EncoderContext * | |
1485 sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) | |
1486 { | |
1487 sec_PKCS12EncoderContext *p12enc = NULL; | |
1488 unsigned int i, nonEmptyCnt; | |
1489 SECStatus rv; | |
1490 SECItem ignore = {0}; | |
1491 void *mark; | |
1492 | |
1493 if(!p12exp || !p12exp->safeInfos) { | |
1494 return NULL; | |
1495 } | |
1496 | |
1497 /* check for any empty safes and skip them */ | |
1498 i = nonEmptyCnt = 0; | |
1499 while(p12exp->safeInfos[i]) { | |
1500 if(p12exp->safeInfos[i]->itemCount) { | |
1501 nonEmptyCnt++; | |
1502 } | |
1503 i++; | |
1504 } | |
1505 if(nonEmptyCnt == 0) { | |
1506 return NULL; | |
1507 } | |
1508 p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; | |
1509 | |
1510 /* allocate the encoder context */ | |
1511 mark = PORT_ArenaMark(p12exp->arena); | |
1512 p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext); | |
1513 if(!p12enc) { | |
1514 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1515 return NULL; | |
1516 } | |
1517 | |
1518 p12enc->arena = p12exp->arena; | |
1519 p12enc->p12exp = p12exp; | |
1520 | |
1521 /* set up the PFX version and information */ | |
1522 PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); | |
1523 if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), | |
1524 SEC_PKCS12_VERSION) ) { | |
1525 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1526 goto loser; | |
1527 } | |
1528 | |
1529 /* set up the authenticated safe content info based on the | |
1530 * type of integrity being used. this should be changed to | |
1531 * enforce integrity mode, but will not be implemented until | |
1532 * it is confirmed that integrity must be in place | |
1533 */ | |
1534 if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { | |
1535 SECStatus rv; | |
1536 | |
1537 /* create public key integrity mode */ | |
1538 p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( | |
1539 p12exp->integrityInfo.pubkeyInfo.cert, | |
1540 certUsageEmailSigner, | |
1541 p12exp->integrityInfo.pubkeyInfo.certDb, | |
1542 p12exp->integrityInfo.pubkeyInfo.algorithm, | |
1543 NULL, | |
1544 p12exp->pwfn, | |
1545 p12exp->pwfnarg); | |
1546 if(!p12enc->aSafeCinfo) { | |
1547 goto loser; | |
1548 } | |
1549 if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { | |
1550 goto loser; | |
1551 } | |
1552 rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); | |
1553 PORT_Assert(rv == SECSuccess); | |
1554 } else { | |
1555 p12enc->aSafeCinfo = SEC_PKCS7CreateData(); | |
1556 | |
1557 /* init password pased integrity mode */ | |
1558 if(p12exp->integrityEnabled) { | |
1559 SECItem pwd = {siBuffer,NULL, 0}; | |
1560 SECItem *salt = sec_pkcs12_generate_salt(); | |
1561 PK11SymKey *symKey; | |
1562 SECItem *params; | |
1563 CK_MECHANISM_TYPE integrityMechType; | |
1564 CK_MECHANISM_TYPE hmacMechType; | |
1565 | |
1566 /* zero out macData and set values */ | |
1567 PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); | |
1568 | |
1569 if(!salt) { | |
1570 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1571 goto loser; | |
1572 } | |
1573 if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) | |
1574 != SECSuccess) { | |
1575 /* XXX salt is leaked */ | |
1576 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1577 goto loser; | |
1578 } | |
1579 if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter), | |
1580 NSS_PBE_DEFAULT_ITERATION_COUNT)) { | |
1581 /* XXX salt is leaked */ | |
1582 goto loser; | |
1583 } | |
1584 | |
1585 /* generate HMAC key */ | |
1586 if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, | |
1587 p12exp->integrityInfo.pwdInfo.password, PR_TRUE, | |
1588 PR_TRUE, PR_TRUE)) { | |
1589 /* XXX salt is leaked */ | |
1590 goto loser; | |
1591 } | |
1592 /* | |
1593 * This code only works with PKCS #12 Mac using PKCS #5 v1 | |
1594 * PBA keygens. PKCS #5 v2 support will require a change to | |
1595 * the PKCS #12 spec. | |
1596 */ | |
1597 params = PK11_CreatePBEParams(salt, &pwd, | |
1598 NSS_PBE_DEFAULT_ITERATION_COUNT); | |
1599 SECITEM_ZfreeItem(salt, PR_TRUE); | |
1600 SECITEM_ZfreeItem(&pwd, PR_FALSE); | |
1601 | |
1602 /* get the PBA Mechanism to generate the key */ | |
1603 switch (p12exp->integrityInfo.pwdInfo.algorithm) { | |
1604 case SEC_OID_SHA1: | |
1605 integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC; break; | |
1606 case SEC_OID_MD5: | |
1607 integrityMechType = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break; | |
1608 case SEC_OID_MD2: | |
1609 integrityMechType = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break; | |
1610 default: | |
1611 /* XXX params is leaked */ | |
1612 goto loser; | |
1613 } | |
1614 | |
1615 /* generate the key */ | |
1616 symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL); | |
1617 PK11_DestroyPBEParams(params); | |
1618 if(!symKey) { | |
1619 goto loser; | |
1620 } | |
1621 | |
1622 /* initialize HMAC */ | |
1623 /* Get the HMAC mechanism from the hash OID */ | |
1624 hmacMechType= sec_pkcs12_algtag_to_mech( | |
1625 p12exp->integrityInfo.pwdInfo.algorithm); | |
1626 | |
1627 p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMechType, | |
1628 CKA_SIGN, symKey, &ignore); | |
1629 | |
1630 PK11_FreeSymKey(symKey); | |
1631 if(!p12enc->hmacCx) { | |
1632 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1633 goto loser; | |
1634 } | |
1635 rv = PK11_DigestBegin(p12enc->hmacCx); | |
1636 if (rv != SECSuccess) | |
1637 goto loser; | |
1638 } | |
1639 } | |
1640 | |
1641 if(!p12enc->aSafeCinfo) { | |
1642 goto loser; | |
1643 } | |
1644 | |
1645 PORT_ArenaUnmark(p12exp->arena, mark); | |
1646 | |
1647 return p12enc; | |
1648 | |
1649 loser: | |
1650 sec_pkcs12_encoder_destroy_context(p12enc); | |
1651 if (p12exp->arena != NULL) | |
1652 PORT_ArenaRelease(p12exp->arena, mark); | |
1653 | |
1654 return NULL; | |
1655 } | |
1656 | |
1657 /* The outermost ASN.1 encoder calls this function for output. | |
1658 ** This function calls back to the library caller's output routine, | |
1659 ** which typically writes to a PKCS12 file. | |
1660 */ | |
1661 static void | |
1662 sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len, | |
1663 int depth, SEC_ASN1EncodingPart data_kind) | |
1664 { | |
1665 struct sec_pkcs12_encoder_output *output; | |
1666 | |
1667 output = (struct sec_pkcs12_encoder_output*)arg; | |
1668 (* output->outputfn)(output->outputarg, buf, len); | |
1669 } | |
1670 | |
1671 /* The "middle" and "inner" ASN.1 encoders call this function to output. | |
1672 ** This function does HMACing, if appropriate, and then buffers the data. | |
1673 ** The buffered data is eventually passed down to the underlying PKCS7 encoder. | |
1674 */ | |
1675 static void | |
1676 sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf, | |
1677 unsigned long len, | |
1678 int depth, | |
1679 SEC_ASN1EncodingPart data_kind) | |
1680 { | |
1681 sec_pkcs12OutputBuffer * bufcx = (sec_pkcs12OutputBuffer *)arg; | |
1682 | |
1683 if(!buf || !len) | |
1684 return; | |
1685 | |
1686 if (bufcx->hmacCx) { | |
1687 PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len); | |
1688 } | |
1689 | |
1690 /* buffer */ | |
1691 if (bufcx->numBytes > 0) { | |
1692 int toCopy; | |
1693 if (len + bufcx->numBytes <= bufcx->bufBytes) { | |
1694 memcpy(bufcx->buf + bufcx->numBytes, buf, len); | |
1695 bufcx->numBytes += len; | |
1696 if (bufcx->numBytes < bufcx->bufBytes) | |
1697 return; | |
1698 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); | |
1699 bufcx->numBytes = 0; | |
1700 return; | |
1701 } | |
1702 toCopy = bufcx->bufBytes - bufcx->numBytes; | |
1703 memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy); | |
1704 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); | |
1705 bufcx->numBytes = 0; | |
1706 len -= toCopy; | |
1707 buf += toCopy; | |
1708 } | |
1709 /* buffer is presently empty */ | |
1710 if (len >= bufcx->bufBytes) { | |
1711 /* Just pass it through */ | |
1712 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len); | |
1713 } else { | |
1714 /* copy it all into the buffer, and return */ | |
1715 memcpy(bufcx->buf, buf, len); | |
1716 bufcx->numBytes = len; | |
1717 } | |
1718 } | |
1719 | |
1720 void | |
1721 sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer * bufcx) | |
1722 { | |
1723 if (bufcx->numBytes > 0) { | |
1724 SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes); | |
1725 bufcx->numBytes = 0; | |
1726 } | |
1727 } | |
1728 | |
1729 /* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder. | |
1730 ** This function is used by both the inner and middle PCS7 encoders. | |
1731 */ | |
1732 static void | |
1733 sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len) | |
1734 { | |
1735 SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg; | |
1736 | |
1737 if (!buf || !len) | |
1738 return; | |
1739 | |
1740 SEC_ASN1EncoderUpdate(cx, buf, len); | |
1741 } | |
1742 | |
1743 | |
1744 /* this function encodes content infos which are part of the | |
1745 * sequence of content infos labeled AuthenticatedSafes | |
1746 */ | |
1747 static SECStatus | |
1748 sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) | |
1749 { | |
1750 SEC_PKCS7EncoderContext *innerP7ecx; | |
1751 SEC_PKCS7ContentInfo *cinfo; | |
1752 PK11SymKey *bulkKey = NULL; | |
1753 SEC_ASN1EncoderContext *innerA1ecx = NULL; | |
1754 SECStatus rv = SECSuccess; | |
1755 | |
1756 if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { | |
1757 SEC_PKCS12SafeInfo *safeInfo; | |
1758 SECOidTag cinfoType; | |
1759 | |
1760 safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; | |
1761 | |
1762 /* skip empty safes */ | |
1763 if(safeInfo->itemCount == 0) { | |
1764 return SECSuccess; | |
1765 } | |
1766 | |
1767 cinfo = safeInfo->cinfo; | |
1768 cinfoType = SEC_PKCS7ContentType(cinfo); | |
1769 | |
1770 /* determine the safe type and set the appropriate argument */ | |
1771 switch(cinfoType) { | |
1772 case SEC_OID_PKCS7_DATA: | |
1773 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
1774 break; | |
1775 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
1776 bulkKey = safeInfo->encryptionKey; | |
1777 PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL); | |
1778 break; | |
1779 default: | |
1780 return SECFailure; | |
1781 | |
1782 } | |
1783 | |
1784 /* start the PKCS7 encoder */ | |
1785 innerP7ecx = SEC_PKCS7EncoderStart(cinfo, | |
1786 sec_P12P7OutputCB_CallA1Update, | |
1787 p12ecx->middleA1ecx, bulkKey); | |
1788 if(!innerP7ecx) { | |
1789 goto loser; | |
1790 } | |
1791 | |
1792 /* encode safe contents */ | |
1793 p12ecx->innerBuf.p7eCx = innerP7ecx; | |
1794 p12ecx->innerBuf.hmacCx = NULL; | |
1795 p12ecx->innerBuf.numBytes = 0; | |
1796 p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf; | |
1797 | |
1798 innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe, | |
1799 sec_PKCS12SafeContentsTemplate, | |
1800 sec_P12A1OutputCB_HmacP7Update, | |
1801 &p12ecx->innerBuf); | |
1802 if(!innerA1ecx) { | |
1803 goto loser; | |
1804 } | |
1805 rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0); | |
1806 SEC_ASN1EncoderFinish(innerA1ecx); | |
1807 sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf); | |
1808 innerA1ecx = NULL; | |
1809 if(rv != SECSuccess) { | |
1810 goto loser; | |
1811 } | |
1812 | |
1813 | |
1814 /* finish up safe content info */ | |
1815 rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, | |
1816 p12ecx->p12exp->pwfnarg); | |
1817 } | |
1818 memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); | |
1819 return SECSuccess; | |
1820 | |
1821 loser: | |
1822 if(innerP7ecx) { | |
1823 SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, | |
1824 p12ecx->p12exp->pwfnarg); | |
1825 } | |
1826 | |
1827 if(innerA1ecx) { | |
1828 SEC_ASN1EncoderFinish(innerA1ecx); | |
1829 } | |
1830 memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); | |
1831 return SECFailure; | |
1832 } | |
1833 | |
1834 /* finish the HMAC and encode the macData so that it can be | |
1835 * encoded. | |
1836 */ | |
1837 static SECStatus | |
1838 sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx) | |
1839 { | |
1840 SECItem hmac = { siBuffer, NULL, 0 }; | |
1841 SECStatus rv; | |
1842 SGNDigestInfo *di = NULL; | |
1843 void *dummy; | |
1844 | |
1845 if(!p12ecx) { | |
1846 return SECFailure; | |
1847 } | |
1848 | |
1849 /* make sure we are using password integrity mode */ | |
1850 if(!p12ecx->p12exp->integrityEnabled) { | |
1851 return SECSuccess; | |
1852 } | |
1853 | |
1854 if(!p12ecx->p12exp->pwdIntegrity) { | |
1855 return SECSuccess; | |
1856 } | |
1857 | |
1858 /* finish the hmac */ | |
1859 hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); | |
1860 if(!hmac.data) { | |
1861 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1862 return SECFailure; | |
1863 } | |
1864 | |
1865 rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH); | |
1866 | |
1867 if(rv != SECSuccess) { | |
1868 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1869 goto loser; | |
1870 } | |
1871 | |
1872 /* create the digest info */ | |
1873 di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, | |
1874 hmac.data, hmac.len); | |
1875 if(!di) { | |
1876 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1877 rv = SECFailure; | |
1878 goto loser; | |
1879 } | |
1880 | |
1881 rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); | |
1882 if(rv != SECSuccess) { | |
1883 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1884 goto loser; | |
1885 } | |
1886 | |
1887 /* encode the mac data */ | |
1888 dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, | |
1889 &p12ecx->mac, sec_PKCS12MacDataTemplate); | |
1890 if(!dummy) { | |
1891 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1892 rv = SECFailure; | |
1893 } | |
1894 | |
1895 loser: | |
1896 if(di) { | |
1897 SGN_DestroyDigestInfo(di); | |
1898 } | |
1899 if(hmac.data) { | |
1900 SECITEM_ZfreeItem(&hmac, PR_FALSE); | |
1901 } | |
1902 PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE); | |
1903 p12ecx->hmacCx = NULL; | |
1904 | |
1905 return rv; | |
1906 } | |
1907 | |
1908 /* pfx notify function for ASN1 encoder. | |
1909 * We want to stop encoding once we reach the authenticated safe. | |
1910 * At that point, the encoder will be updated via streaming | |
1911 * as the authenticated safe is encoded. | |
1912 */ | |
1913 static void | |
1914 sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_dep
th) | |
1915 { | |
1916 sec_PKCS12EncoderContext *p12ecx; | |
1917 | |
1918 if(!before) { | |
1919 return; | |
1920 } | |
1921 | |
1922 /* look for authenticated safe */ | |
1923 p12ecx = (sec_PKCS12EncoderContext*)arg; | |
1924 if(dest != &p12ecx->pfx.encodedAuthSafe) { | |
1925 return; | |
1926 } | |
1927 | |
1928 SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx); | |
1929 SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx); | |
1930 SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx); | |
1931 } | |
1932 | |
1933 /* SEC_PKCS12Encode | |
1934 * Encodes the PFX item and returns it to the output function, via | |
1935 * callback. the output function must be capable of multiple updates. | |
1936 * | |
1937 * p12exp - the export context | |
1938 * output - the output function callback, will be called more than once, | |
1939 * must be able to accept streaming data. | |
1940 * outputarg - argument for the output callback. | |
1941 */ | |
1942 SECStatus | |
1943 SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, | |
1944 SEC_PKCS12EncoderOutputCallback output, void *outputarg) | |
1945 { | |
1946 sec_PKCS12EncoderContext *p12enc; | |
1947 struct sec_pkcs12_encoder_output outInfo; | |
1948 SECStatus rv; | |
1949 | |
1950 if(!p12exp || !output) { | |
1951 return SECFailure; | |
1952 } | |
1953 | |
1954 /* get the encoder context */ | |
1955 p12enc = sec_pkcs12_encoder_start_context(p12exp); | |
1956 if(!p12enc) { | |
1957 return SECFailure; | |
1958 } | |
1959 | |
1960 outInfo.outputfn = output; | |
1961 outInfo.outputarg = outputarg; | |
1962 | |
1963 /* set up PFX encoder, the "outer" encoder. Set it for streaming */ | |
1964 p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx, | |
1965 sec_PKCS12PFXItemTemplate, | |
1966 sec_P12A1OutputCB_Outer, | |
1967 &outInfo); | |
1968 if(!p12enc->outerA1ecx) { | |
1969 PORT_SetError(SEC_ERROR_NO_MEMORY); | |
1970 rv = SECFailure; | |
1971 goto loser; | |
1972 } | |
1973 SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx); | |
1974 SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx, | |
1975 sec_pkcs12_encoder_pfx_notify, p12enc); | |
1976 rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); | |
1977 if(rv != SECSuccess) { | |
1978 rv = SECFailure; | |
1979 goto loser; | |
1980 } | |
1981 | |
1982 /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ | |
1983 p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, | |
1984 sec_P12P7OutputCB_CallA1Update, | |
1985 p12enc->outerA1ecx, NULL); | |
1986 if(!p12enc->middleP7ecx) { | |
1987 rv = SECFailure; | |
1988 goto loser; | |
1989 } | |
1990 | |
1991 /* encode asafe */ | |
1992 p12enc->middleBuf.p7eCx = p12enc->middleP7ecx; | |
1993 p12enc->middleBuf.hmacCx = NULL; | |
1994 p12enc->middleBuf.numBytes = 0; | |
1995 p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf; | |
1996 | |
1997 /* Setup the "inner ASN.1 encoder for Authenticated Safes. */ | |
1998 if(p12enc->p12exp->integrityEnabled && | |
1999 p12enc->p12exp->pwdIntegrity) { | |
2000 p12enc->middleBuf.hmacCx = p12enc->hmacCx; | |
2001 } | |
2002 p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, | |
2003 sec_PKCS12AuthenticatedSafeTemplate, | |
2004 sec_P12A1OutputCB_HmacP7Update, | |
2005 &p12enc->middleBuf); | |
2006 if(!p12enc->middleA1ecx) { | |
2007 rv = SECFailure; | |
2008 goto loser; | |
2009 } | |
2010 SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx); | |
2011 SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx); | |
2012 | |
2013 /* encode each of the safes */ | |
2014 while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { | |
2015 sec_pkcs12_encoder_asafe_process(p12enc); | |
2016 p12enc->currentSafe++; | |
2017 } | |
2018 SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx); | |
2019 SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx); | |
2020 SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0); | |
2021 SEC_ASN1EncoderFinish(p12enc->middleA1ecx); | |
2022 p12enc->middleA1ecx = NULL; | |
2023 | |
2024 sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf); | |
2025 | |
2026 /* finish the encoding of the authenticated safes */ | |
2027 rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn, | |
2028 p12exp->pwfnarg); | |
2029 p12enc->middleP7ecx = NULL; | |
2030 if(rv != SECSuccess) { | |
2031 goto loser; | |
2032 } | |
2033 | |
2034 SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx); | |
2035 SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx); | |
2036 | |
2037 /* update the mac, if necessary */ | |
2038 rv = sec_Pkcs12FinishMac(p12enc); | |
2039 if(rv != SECSuccess) { | |
2040 goto loser; | |
2041 } | |
2042 | |
2043 /* finish encoding the pfx */ | |
2044 rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); | |
2045 | |
2046 SEC_ASN1EncoderFinish(p12enc->outerA1ecx); | |
2047 p12enc->outerA1ecx = NULL; | |
2048 | |
2049 loser: | |
2050 sec_pkcs12_encoder_destroy_context(p12enc); | |
2051 return rv; | |
2052 } | |
2053 | |
2054 void | |
2055 SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) | |
2056 { | |
2057 int i = 0; | |
2058 | |
2059 if(!p12ecx) { | |
2060 return; | |
2061 } | |
2062 | |
2063 if(p12ecx->safeInfos) { | |
2064 i = 0; | |
2065 while(p12ecx->safeInfos[i] != NULL) { | |
2066 if(p12ecx->safeInfos[i]->encryptionKey) { | |
2067 PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); | |
2068 } | |
2069 if(p12ecx->safeInfos[i]->cinfo) { | |
2070 SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); | |
2071 } | |
2072 i++; | |
2073 } | |
2074 } | |
2075 | |
2076 PK11_FreeSlot(p12ecx->slot); | |
2077 | |
2078 PORT_FreeArena(p12ecx->arena, PR_TRUE); | |
2079 } | |
OLD | NEW |