/src/mozilla-central/security/nss/lib/pkcs7/p7encode.c
Line | Count | Source (jump to first uncovered line) |
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 | | /* |
6 | | * PKCS7 encoding. |
7 | | */ |
8 | | |
9 | | #include "p7local.h" |
10 | | |
11 | | #include "cert.h" |
12 | | #include "cryptohi.h" |
13 | | #include "keyhi.h" |
14 | | #include "secasn1.h" |
15 | | #include "secoid.h" |
16 | | #include "secitem.h" |
17 | | #include "pk11func.h" |
18 | | #include "secerr.h" |
19 | | #include "sechash.h" /* for HASH_GetHashObject() */ |
20 | | |
21 | | struct sec_pkcs7_encoder_output { |
22 | | SEC_PKCS7EncoderOutputCallback outputfn; |
23 | | void *outputarg; |
24 | | }; |
25 | | |
26 | | struct SEC_PKCS7EncoderContextStr { |
27 | | SEC_ASN1EncoderContext *ecx; |
28 | | SEC_PKCS7ContentInfo *cinfo; |
29 | | struct sec_pkcs7_encoder_output output; |
30 | | sec_PKCS7CipherObject *encryptobj; |
31 | | const SECHashObject *digestobj; |
32 | | void *digestcx; |
33 | | }; |
34 | | |
35 | | /* |
36 | | * The little output function that the ASN.1 encoder calls to hand |
37 | | * us bytes which we in turn hand back to our caller (via the callback |
38 | | * they gave us). |
39 | | */ |
40 | | static void |
41 | | sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len, |
42 | | int depth, SEC_ASN1EncodingPart data_kind) |
43 | 0 | { |
44 | 0 | struct sec_pkcs7_encoder_output *output; |
45 | 0 |
|
46 | 0 | output = (struct sec_pkcs7_encoder_output *)arg; |
47 | 0 | output->outputfn(output->outputarg, buf, len); |
48 | 0 | } |
49 | | |
50 | | static sec_PKCS7CipherObject * |
51 | | sec_pkcs7_encoder_start_encrypt(SEC_PKCS7ContentInfo *cinfo, |
52 | | PK11SymKey *orig_bulkkey) |
53 | 0 | { |
54 | 0 | SECOidTag kind; |
55 | 0 | sec_PKCS7CipherObject *encryptobj; |
56 | 0 | SEC_PKCS7RecipientInfo **recipientinfos, *ri; |
57 | 0 | SEC_PKCS7EncryptedContentInfo *enccinfo; |
58 | 0 | SECKEYPublicKey *publickey = NULL; |
59 | 0 | SECKEYPrivateKey *ourPrivKey = NULL; |
60 | 0 | PK11SymKey *bulkkey; |
61 | 0 | void *mark; |
62 | 0 | int i; |
63 | 0 | PLArenaPool *arena = NULL; |
64 | 0 |
|
65 | 0 | kind = SEC_PKCS7ContentType(cinfo); |
66 | 0 | switch (kind) { |
67 | 0 | default: |
68 | 0 | case SEC_OID_PKCS7_DATA: |
69 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
70 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
71 | 0 | recipientinfos = NULL; |
72 | 0 | enccinfo = NULL; |
73 | 0 | break; |
74 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: { |
75 | 0 | SEC_PKCS7EncryptedData *encdp; |
76 | 0 |
|
77 | 0 | /* To do EncryptedData we *must* be given a bulk key. */ |
78 | 0 | PORT_Assert(orig_bulkkey != NULL); |
79 | 0 | if (orig_bulkkey == NULL) { |
80 | 0 | /* XXX error? */ |
81 | 0 | return NULL; |
82 | 0 | } |
83 | 0 | |
84 | 0 | encdp = cinfo->content.encryptedData; |
85 | 0 | recipientinfos = NULL; |
86 | 0 | enccinfo = &(encdp->encContentInfo); |
87 | 0 | } break; |
88 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: { |
89 | 0 | SEC_PKCS7EnvelopedData *envdp; |
90 | 0 |
|
91 | 0 | envdp = cinfo->content.envelopedData; |
92 | 0 | recipientinfos = envdp->recipientInfos; |
93 | 0 | enccinfo = &(envdp->encContentInfo); |
94 | 0 | } break; |
95 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { |
96 | 0 | SEC_PKCS7SignedAndEnvelopedData *saedp; |
97 | 0 |
|
98 | 0 | saedp = cinfo->content.signedAndEnvelopedData; |
99 | 0 | recipientinfos = saedp->recipientInfos; |
100 | 0 | enccinfo = &(saedp->encContentInfo); |
101 | 0 | } break; |
102 | 0 | } |
103 | 0 | |
104 | 0 | if (enccinfo == NULL) |
105 | 0 | return NULL; |
106 | 0 | |
107 | 0 | bulkkey = orig_bulkkey; |
108 | 0 | if (bulkkey == NULL) { |
109 | 0 | CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg); |
110 | 0 | PK11SlotInfo *slot; |
111 | 0 |
|
112 | 0 | slot = PK11_GetBestSlot(type, cinfo->pwfn_arg); |
113 | 0 | if (slot == NULL) { |
114 | 0 | return NULL; |
115 | 0 | } |
116 | 0 | bulkkey = PK11_KeyGen(slot, type, NULL, enccinfo->keysize / 8, |
117 | 0 | cinfo->pwfn_arg); |
118 | 0 | PK11_FreeSlot(slot); |
119 | 0 | if (bulkkey == NULL) { |
120 | 0 | return NULL; |
121 | 0 | } |
122 | 0 | } |
123 | 0 | |
124 | 0 | encryptobj = NULL; |
125 | 0 | mark = PORT_ArenaMark(cinfo->poolp); |
126 | 0 |
|
127 | 0 | /* |
128 | 0 | * Encrypt the bulk key with the public key of each recipient. |
129 | 0 | */ |
130 | 0 | for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) { |
131 | 0 | CERTCertificate *cert; |
132 | 0 | SECOidTag certalgtag, encalgtag; |
133 | 0 | SECStatus rv; |
134 | 0 | int data_len; |
135 | 0 | SECItem *params = NULL; |
136 | 0 |
|
137 | 0 | cert = ri->cert; |
138 | 0 | PORT_Assert(cert != NULL); |
139 | 0 | if (cert == NULL) |
140 | 0 | continue; |
141 | 0 | |
142 | 0 | /* |
143 | 0 | * XXX Want an interface that takes a cert and some data and |
144 | 0 | * fills in an algorithmID and encrypts the data with the public |
145 | 0 | * key from the cert. Or, give me two interfaces -- one which |
146 | 0 | * gets the algorithm tag from a cert (I should not have to go |
147 | 0 | * down into the subjectPublicKeyInfo myself) and another which |
148 | 0 | * takes a public key and algorithm tag and data and encrypts |
149 | 0 | * the data. Or something like that. The point is that all |
150 | 0 | * of the following hardwired RSA stuff should be done elsewhere. |
151 | 0 | */ |
152 | 0 | |
153 | 0 | certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); |
154 | 0 |
|
155 | 0 | switch (certalgtag) { |
156 | 0 | case SEC_OID_PKCS1_RSA_ENCRYPTION: |
157 | 0 | encalgtag = certalgtag; |
158 | 0 | publickey = CERT_ExtractPublicKey(cert); |
159 | 0 | if (publickey == NULL) |
160 | 0 | goto loser; |
161 | 0 | |
162 | 0 | data_len = SECKEY_PublicKeyStrength(publickey); |
163 | 0 | ri->encKey.data = |
164 | 0 | (unsigned char *)PORT_ArenaAlloc(cinfo->poolp, data_len); |
165 | 0 | ri->encKey.len = data_len; |
166 | 0 | if (ri->encKey.data == NULL) |
167 | 0 | goto loser; |
168 | 0 | |
169 | 0 | rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag), publickey, |
170 | 0 | bulkkey, &ri->encKey); |
171 | 0 |
|
172 | 0 | SECKEY_DestroyPublicKey(publickey); |
173 | 0 | publickey = NULL; |
174 | 0 | if (rv != SECSuccess) |
175 | 0 | goto loser; |
176 | 0 | params = NULL; /* paranoia */ |
177 | 0 | break; |
178 | 0 | default: |
179 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
180 | 0 | goto loser; |
181 | 0 | } |
182 | 0 |
|
183 | 0 | rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag, |
184 | 0 | params); |
185 | 0 | if (rv != SECSuccess) |
186 | 0 | goto loser; |
187 | 0 | if (arena) |
188 | 0 | PORT_FreeArena(arena, PR_FALSE); |
189 | 0 | arena = NULL; |
190 | 0 | } |
191 | 0 |
|
192 | 0 | encryptobj = sec_PKCS7CreateEncryptObject(cinfo->poolp, bulkkey, |
193 | 0 | enccinfo->encalg, |
194 | 0 | &(enccinfo->contentEncAlg)); |
195 | 0 | if (encryptobj != NULL) { |
196 | 0 | PORT_ArenaUnmark(cinfo->poolp, mark); |
197 | 0 | mark = NULL; /* good one; do not want to release */ |
198 | 0 | } |
199 | 0 | /* fallthru */ |
200 | 0 |
|
201 | 0 | loser: |
202 | 0 | if (arena) { |
203 | 0 | PORT_FreeArena(arena, PR_FALSE); |
204 | 0 | } |
205 | 0 | if (publickey) { |
206 | 0 | SECKEY_DestroyPublicKey(publickey); |
207 | 0 | } |
208 | 0 | if (ourPrivKey) { |
209 | 0 | SECKEY_DestroyPrivateKey(ourPrivKey); |
210 | 0 | } |
211 | 0 | if (mark != NULL) { |
212 | 0 | PORT_ArenaRelease(cinfo->poolp, mark); |
213 | 0 | } |
214 | 0 | if (orig_bulkkey == NULL) { |
215 | 0 | if (bulkkey) |
216 | 0 | PK11_FreeSymKey(bulkkey); |
217 | 0 | } |
218 | 0 |
|
219 | 0 | return encryptobj; |
220 | 0 | } |
221 | | |
222 | | static void |
223 | | sec_pkcs7_encoder_notify(void *arg, PRBool before, void *dest, int depth) |
224 | 0 | { |
225 | 0 | SEC_PKCS7EncoderContext *p7ecx; |
226 | 0 | SEC_PKCS7ContentInfo *cinfo; |
227 | 0 | SECOidTag kind; |
228 | 0 | PRBool before_content; |
229 | 0 |
|
230 | 0 | /* |
231 | 0 | * We want to notice just before the content field. After fields are |
232 | 0 | * not interesting to us. |
233 | 0 | */ |
234 | 0 | if (!before) |
235 | 0 | return; |
236 | 0 | |
237 | 0 | p7ecx = (SEC_PKCS7EncoderContext *)arg; |
238 | 0 | cinfo = p7ecx->cinfo; |
239 | 0 |
|
240 | 0 | before_content = PR_FALSE; |
241 | 0 |
|
242 | 0 | /* |
243 | 0 | * Watch for the content field, at which point we want to instruct |
244 | 0 | * the ASN.1 encoder to start taking bytes from the buffer. |
245 | 0 | * |
246 | 0 | * XXX The following assumes the inner content type is data; |
247 | 0 | * if/when we want to handle fully nested types, this will have |
248 | 0 | * to recurse until reaching the innermost data content. |
249 | 0 | */ |
250 | 0 | kind = SEC_PKCS7ContentType(cinfo); |
251 | 0 | switch (kind) { |
252 | 0 | default: |
253 | 0 | case SEC_OID_PKCS7_DATA: |
254 | 0 | if (dest == &(cinfo->content.data)) |
255 | 0 | before_content = PR_TRUE; |
256 | 0 | break; |
257 | 0 |
|
258 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: { |
259 | 0 | SEC_PKCS7DigestedData *digd; |
260 | 0 |
|
261 | 0 | digd = cinfo->content.digestedData; |
262 | 0 | if (digd == NULL) |
263 | 0 | break; |
264 | 0 | |
265 | 0 | if (dest == &(digd->contentInfo.content)) |
266 | 0 | before_content = PR_TRUE; |
267 | 0 | } break; |
268 | 0 |
|
269 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: { |
270 | 0 | SEC_PKCS7EncryptedData *encd; |
271 | 0 |
|
272 | 0 | encd = cinfo->content.encryptedData; |
273 | 0 | if (encd == NULL) |
274 | 0 | break; |
275 | 0 | |
276 | 0 | if (dest == &(encd->encContentInfo.encContent)) |
277 | 0 | before_content = PR_TRUE; |
278 | 0 | } break; |
279 | 0 |
|
280 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: { |
281 | 0 | SEC_PKCS7EnvelopedData *envd; |
282 | 0 |
|
283 | 0 | envd = cinfo->content.envelopedData; |
284 | 0 | if (envd == NULL) |
285 | 0 | break; |
286 | 0 | |
287 | 0 | if (dest == &(envd->encContentInfo.encContent)) |
288 | 0 | before_content = PR_TRUE; |
289 | 0 | } break; |
290 | 0 |
|
291 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: { |
292 | 0 | SEC_PKCS7SignedData *sigd; |
293 | 0 |
|
294 | 0 | sigd = cinfo->content.signedData; |
295 | 0 | if (sigd == NULL) |
296 | 0 | break; |
297 | 0 | |
298 | 0 | if (dest == &(sigd->contentInfo.content)) |
299 | 0 | before_content = PR_TRUE; |
300 | 0 | } break; |
301 | 0 |
|
302 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { |
303 | 0 | SEC_PKCS7SignedAndEnvelopedData *saed; |
304 | 0 |
|
305 | 0 | saed = cinfo->content.signedAndEnvelopedData; |
306 | 0 | if (saed == NULL) |
307 | 0 | break; |
308 | 0 | |
309 | 0 | if (dest == &(saed->encContentInfo.encContent)) |
310 | 0 | before_content = PR_TRUE; |
311 | 0 | } break; |
312 | 0 | } |
313 | 0 |
|
314 | 0 | if (before_content) { |
315 | 0 | /* |
316 | 0 | * This will cause the next SEC_ASN1EncoderUpdate to take the |
317 | 0 | * contents bytes from the passed-in buffer. |
318 | 0 | */ |
319 | 0 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); |
320 | 0 | /* |
321 | 0 | * And that is all we needed this notify function for. |
322 | 0 | */ |
323 | 0 | SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | static SEC_PKCS7EncoderContext * |
328 | | sec_pkcs7_encoder_start_contexts(SEC_PKCS7ContentInfo *cinfo, |
329 | | PK11SymKey *bulkkey) |
330 | 0 | { |
331 | 0 | SEC_PKCS7EncoderContext *p7ecx; |
332 | 0 | SECOidTag kind; |
333 | 0 | PRBool encrypt; |
334 | 0 | SECItem **digests; |
335 | 0 | SECAlgorithmID *digestalg, **digestalgs; |
336 | 0 |
|
337 | 0 | p7ecx = |
338 | 0 | (SEC_PKCS7EncoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7EncoderContext)); |
339 | 0 | if (p7ecx == NULL) |
340 | 0 | return NULL; |
341 | 0 | |
342 | 0 | digests = NULL; |
343 | 0 | digestalg = NULL; |
344 | 0 | digestalgs = NULL; |
345 | 0 | encrypt = PR_FALSE; |
346 | 0 |
|
347 | 0 | kind = SEC_PKCS7ContentType(cinfo); |
348 | 0 | switch (kind) { |
349 | 0 | default: |
350 | 0 | case SEC_OID_PKCS7_DATA: |
351 | 0 | break; |
352 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
353 | 0 | digestalg = &(cinfo->content.digestedData->digestAlg); |
354 | 0 | break; |
355 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
356 | 0 | digests = cinfo->content.signedData->digests; |
357 | 0 | digestalgs = cinfo->content.signedData->digestAlgorithms; |
358 | 0 | break; |
359 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
360 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
361 | 0 | encrypt = PR_TRUE; |
362 | 0 | break; |
363 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
364 | 0 | digests = cinfo->content.signedAndEnvelopedData->digests; |
365 | 0 | digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms; |
366 | 0 | encrypt = PR_TRUE; |
367 | 0 | break; |
368 | 0 | } |
369 | 0 |
|
370 | 0 | if (encrypt) { |
371 | 0 | p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt(cinfo, bulkkey); |
372 | 0 | if (p7ecx->encryptobj == NULL) { |
373 | 0 | PORT_Free(p7ecx); |
374 | 0 | return NULL; |
375 | 0 | } |
376 | 0 | } |
377 | 0 |
|
378 | 0 | if (digestalgs != NULL) { |
379 | 0 | if (digests != NULL) { |
380 | 0 | /* digests already created (probably for detached data) */ |
381 | 0 | digestalg = NULL; |
382 | 0 | } else { |
383 | 0 | /* |
384 | 0 | * XXX Some day we should handle multiple digests; for now, |
385 | 0 | * assume only one will be done. |
386 | 0 | */ |
387 | 0 | PORT_Assert(digestalgs[0] != NULL && digestalgs[1] == NULL); |
388 | 0 | digestalg = digestalgs[0]; |
389 | 0 | } |
390 | 0 | } |
391 | 0 |
|
392 | 0 | if (digestalg != NULL) { |
393 | 0 | SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm)); |
394 | 0 |
|
395 | 0 | p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag); |
396 | 0 | if (p7ecx->digestobj != NULL) { |
397 | 0 | p7ecx->digestcx = (*p7ecx->digestobj->create)(); |
398 | 0 | if (p7ecx->digestcx == NULL) |
399 | 0 | p7ecx->digestobj = NULL; |
400 | 0 | else |
401 | 0 | (*p7ecx->digestobj->begin)(p7ecx->digestcx); |
402 | 0 | } |
403 | 0 | if (p7ecx->digestobj == NULL) { |
404 | 0 | if (p7ecx->encryptobj != NULL) |
405 | 0 | sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj); |
406 | 0 | PORT_Free(p7ecx); |
407 | 0 | return NULL; |
408 | 0 | } |
409 | 0 | } |
410 | 0 |
|
411 | 0 | p7ecx->cinfo = cinfo; |
412 | 0 | return p7ecx; |
413 | 0 | } |
414 | | |
415 | | SEC_PKCS7EncoderContext * |
416 | | SEC_PKCS7EncoderStart(SEC_PKCS7ContentInfo *cinfo, |
417 | | SEC_PKCS7EncoderOutputCallback outputfn, |
418 | | void *outputarg, |
419 | | PK11SymKey *bulkkey) |
420 | 0 | { |
421 | 0 | SEC_PKCS7EncoderContext *p7ecx; |
422 | 0 | SECStatus rv; |
423 | 0 |
|
424 | 0 | p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey); |
425 | 0 | if (p7ecx == NULL) |
426 | 0 | return NULL; |
427 | 0 | |
428 | 0 | p7ecx->output.outputfn = outputfn; |
429 | 0 | p7ecx->output.outputarg = outputarg; |
430 | 0 |
|
431 | 0 | /* |
432 | 0 | * Initialize the BER encoder. |
433 | 0 | */ |
434 | 0 | p7ecx->ecx = SEC_ASN1EncoderStart(cinfo, sec_PKCS7ContentInfoTemplate, |
435 | 0 | sec_pkcs7_encoder_out, &(p7ecx->output)); |
436 | 0 | if (p7ecx->ecx == NULL) { |
437 | 0 | PORT_Free(p7ecx); |
438 | 0 | return NULL; |
439 | 0 | } |
440 | 0 |
|
441 | 0 | /* |
442 | 0 | * Indicate that we are streaming. We will be streaming until we |
443 | 0 | * get past the contents bytes. |
444 | 0 | */ |
445 | 0 | SEC_ASN1EncoderSetStreaming(p7ecx->ecx); |
446 | 0 |
|
447 | 0 | /* |
448 | 0 | * The notify function will watch for the contents field. |
449 | 0 | */ |
450 | 0 | SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx); |
451 | 0 |
|
452 | 0 | /* |
453 | 0 | * This will encode everything up to the content bytes. (The notify |
454 | 0 | * function will then cause the encoding to stop there.) Then our |
455 | 0 | * caller can start passing contents bytes to our Update, which we |
456 | 0 | * will pass along. |
457 | 0 | */ |
458 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); |
459 | 0 | if (rv != SECSuccess) { |
460 | 0 | PORT_Free(p7ecx); |
461 | 0 | return NULL; |
462 | 0 | } |
463 | 0 |
|
464 | 0 | return p7ecx; |
465 | 0 | } |
466 | | |
467 | | /* |
468 | | * XXX If/when we support nested contents, this needs to be revised. |
469 | | */ |
470 | | static SECStatus |
471 | | sec_pkcs7_encoder_work_data(SEC_PKCS7EncoderContext *p7ecx, SECItem *dest, |
472 | | const unsigned char *data, unsigned long len, |
473 | | PRBool final) |
474 | 0 | { |
475 | 0 | unsigned char *buf = NULL; |
476 | 0 | SECStatus rv; |
477 | 0 |
|
478 | 0 | rv = SECSuccess; /* may as well be optimistic */ |
479 | 0 |
|
480 | 0 | /* |
481 | 0 | * We should really have data to process, or we should be trying |
482 | 0 | * to finish/flush the last block. (This is an overly paranoid |
483 | 0 | * check since all callers are in this file and simple inspection |
484 | 0 | * proves they do it right. But it could find a bug in future |
485 | 0 | * modifications/development, that is why it is here.) |
486 | 0 | */ |
487 | 0 | PORT_Assert((data != NULL && len) || final); |
488 | 0 |
|
489 | 0 | /* |
490 | 0 | * Update the running digest. |
491 | 0 | * XXX This needs modification if/when we handle multiple digests. |
492 | 0 | */ |
493 | 0 | if (len && p7ecx->digestobj != NULL) { |
494 | 0 | (*p7ecx->digestobj->update)(p7ecx->digestcx, data, len); |
495 | 0 | } |
496 | 0 |
|
497 | 0 | /* |
498 | 0 | * Encrypt this chunk. |
499 | 0 | */ |
500 | 0 | if (p7ecx->encryptobj != NULL) { |
501 | 0 | /* XXX the following lengths should all be longs? */ |
502 | 0 | unsigned int inlen; /* length of data being encrypted */ |
503 | 0 | unsigned int outlen; /* length of encrypted data */ |
504 | 0 | unsigned int buflen; /* length available for encrypted data */ |
505 | 0 |
|
506 | 0 | inlen = len; |
507 | 0 | buflen = sec_PKCS7EncryptLength(p7ecx->encryptobj, inlen, final); |
508 | 0 | if (buflen == 0) { |
509 | 0 | /* |
510 | 0 | * No output is expected, but the input data may be buffered |
511 | 0 | * so we still have to call Encrypt. |
512 | 0 | */ |
513 | 0 | rv = sec_PKCS7Encrypt(p7ecx->encryptobj, NULL, &outlen, 0, |
514 | 0 | data, inlen, final); |
515 | 0 | if (final) { |
516 | 0 | len = 0; |
517 | 0 | goto done; |
518 | 0 | } |
519 | 0 | return rv; |
520 | 0 | } |
521 | 0 | |
522 | 0 | if (dest != NULL) |
523 | 0 | buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen); |
524 | 0 | else |
525 | 0 | buf = (unsigned char *)PORT_Alloc(buflen); |
526 | 0 |
|
527 | 0 | if (buf == NULL) { |
528 | 0 | rv = SECFailure; |
529 | 0 | } else { |
530 | 0 | rv = sec_PKCS7Encrypt(p7ecx->encryptobj, buf, &outlen, buflen, |
531 | 0 | data, inlen, final); |
532 | 0 | data = buf; |
533 | 0 | len = outlen; |
534 | 0 | } |
535 | 0 | if (rv != SECSuccess) { |
536 | 0 | if (final) |
537 | 0 | goto done; |
538 | 0 | return rv; |
539 | 0 | } |
540 | 0 | } |
541 | 0 |
|
542 | 0 | if (p7ecx->ecx != NULL) { |
543 | 0 | /* |
544 | 0 | * Encode the contents bytes. |
545 | 0 | */ |
546 | 0 | if (len) { |
547 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); |
548 | 0 | } |
549 | 0 | } |
550 | 0 |
|
551 | 0 | done: |
552 | 0 | if (p7ecx->encryptobj != NULL) { |
553 | 0 | if (final) |
554 | 0 | sec_PKCS7DestroyEncryptObject(p7ecx->encryptobj); |
555 | 0 | if (dest != NULL) { |
556 | 0 | dest->data = buf; |
557 | 0 | dest->len = len; |
558 | 0 | } else if (buf != NULL) { |
559 | 0 | PORT_Free(buf); |
560 | 0 | } |
561 | 0 | } |
562 | 0 |
|
563 | 0 | if (final && p7ecx->digestobj != NULL) { |
564 | 0 | SECItem *digest, **digests, ***digestsp; |
565 | 0 | unsigned char *digdata; |
566 | 0 | SECOidTag kind; |
567 | 0 |
|
568 | 0 | kind = SEC_PKCS7ContentType(p7ecx->cinfo); |
569 | 0 | switch (kind) { |
570 | 0 | default: |
571 | 0 | PORT_Assert(0); |
572 | 0 | return SECFailure; |
573 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
574 | 0 | digest = &(p7ecx->cinfo->content.digestedData->digest); |
575 | 0 | digestsp = NULL; |
576 | 0 | break; |
577 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
578 | 0 | digest = NULL; |
579 | 0 | digestsp = &(p7ecx->cinfo->content.signedData->digests); |
580 | 0 | break; |
581 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
582 | 0 | digest = NULL; |
583 | 0 | digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests); |
584 | 0 | break; |
585 | 0 | } |
586 | 0 | |
587 | 0 | digdata = (unsigned char *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, |
588 | 0 | p7ecx->digestobj->length); |
589 | 0 | if (digdata == NULL) |
590 | 0 | return SECFailure; |
591 | 0 | |
592 | 0 | if (digestsp != NULL) { |
593 | 0 | PORT_Assert(digest == NULL); |
594 | 0 |
|
595 | 0 | digest = (SECItem *)PORT_ArenaAlloc(p7ecx->cinfo->poolp, |
596 | 0 | sizeof(SECItem)); |
597 | 0 | digests = (SECItem **)PORT_ArenaAlloc(p7ecx->cinfo->poolp, |
598 | 0 | 2 * sizeof(SECItem *)); |
599 | 0 | if (digests == NULL || digest == NULL) |
600 | 0 | return SECFailure; |
601 | 0 | |
602 | 0 | digests[0] = digest; |
603 | 0 | digests[1] = NULL; |
604 | 0 |
|
605 | 0 | *digestsp = digests; |
606 | 0 | } |
607 | 0 |
|
608 | 0 | PORT_Assert(digest != NULL); |
609 | 0 |
|
610 | 0 | digest->data = digdata; |
611 | 0 | digest->len = p7ecx->digestobj->length; |
612 | 0 |
|
613 | 0 | (*p7ecx->digestobj->end)(p7ecx->digestcx, digest->data, |
614 | 0 | &(digest->len), digest->len); |
615 | 0 | (*p7ecx->digestobj->destroy)(p7ecx->digestcx, PR_TRUE); |
616 | 0 | } |
617 | 0 |
|
618 | 0 | return rv; |
619 | 0 | } |
620 | | |
621 | | SECStatus |
622 | | SEC_PKCS7EncoderUpdate(SEC_PKCS7EncoderContext *p7ecx, |
623 | | const char *data, unsigned long len) |
624 | 0 | { |
625 | 0 | /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ |
626 | 0 | return sec_pkcs7_encoder_work_data(p7ecx, NULL, |
627 | 0 | (const unsigned char *)data, len, |
628 | 0 | PR_FALSE); |
629 | 0 | } |
630 | | |
631 | | static SECStatus |
632 | | sec_pkcs7_encoder_sig_and_certs(SEC_PKCS7ContentInfo *cinfo, |
633 | | SECKEYGetPasswordKey pwfn, void *pwfnarg) |
634 | 0 | { |
635 | 0 | SECOidTag kind; |
636 | 0 | CERTCertificate **certs; |
637 | 0 | CERTCertificateList **certlists; |
638 | 0 | SECAlgorithmID **digestalgs; |
639 | 0 | SECItem **digests; |
640 | 0 | SEC_PKCS7SignerInfo *signerinfo, **signerinfos; |
641 | 0 | SECItem **rawcerts, ***rawcertsp; |
642 | 0 | PLArenaPool *poolp; |
643 | 0 | int certcount; |
644 | 0 | int ci, cli, rci, si; |
645 | 0 |
|
646 | 0 | kind = SEC_PKCS7ContentType(cinfo); |
647 | 0 | switch (kind) { |
648 | 0 | default: |
649 | 0 | case SEC_OID_PKCS7_DATA: |
650 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
651 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
652 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
653 | 0 | certs = NULL; |
654 | 0 | certlists = NULL; |
655 | 0 | digestalgs = NULL; |
656 | 0 | digests = NULL; |
657 | 0 | signerinfos = NULL; |
658 | 0 | rawcertsp = NULL; |
659 | 0 | break; |
660 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: { |
661 | 0 | SEC_PKCS7SignedData *sdp; |
662 | 0 |
|
663 | 0 | sdp = cinfo->content.signedData; |
664 | 0 | certs = sdp->certs; |
665 | 0 | certlists = sdp->certLists; |
666 | 0 | digestalgs = sdp->digestAlgorithms; |
667 | 0 | digests = sdp->digests; |
668 | 0 | signerinfos = sdp->signerInfos; |
669 | 0 | rawcertsp = &(sdp->rawCerts); |
670 | 0 | } break; |
671 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: { |
672 | 0 | SEC_PKCS7SignedAndEnvelopedData *saedp; |
673 | 0 |
|
674 | 0 | saedp = cinfo->content.signedAndEnvelopedData; |
675 | 0 | certs = saedp->certs; |
676 | 0 | certlists = saedp->certLists; |
677 | 0 | digestalgs = saedp->digestAlgorithms; |
678 | 0 | digests = saedp->digests; |
679 | 0 | signerinfos = saedp->signerInfos; |
680 | 0 | rawcertsp = &(saedp->rawCerts); |
681 | 0 | } break; |
682 | 0 | } |
683 | 0 | |
684 | 0 | if (certs == NULL && certlists == NULL && signerinfos == NULL) |
685 | 0 | return SECSuccess; /* nothing for us to do! */ |
686 | 0 | |
687 | 0 | poolp = cinfo->poolp; |
688 | 0 | certcount = 0; |
689 | 0 |
|
690 | 0 | if (signerinfos != NULL) { |
691 | 0 | SECOidTag digestalgtag; |
692 | 0 | int di; |
693 | 0 | SECStatus rv; |
694 | 0 | CERTCertificate *cert; |
695 | 0 | SECKEYPrivateKey *privkey; |
696 | 0 | SECItem signature; |
697 | 0 | SECOidTag signalgtag; |
698 | 0 |
|
699 | 0 | PORT_Assert(digestalgs != NULL && digests != NULL); |
700 | 0 |
|
701 | 0 | /* |
702 | 0 | * If one fails, we bail right then. If we want to continue and |
703 | 0 | * try to do subsequent signatures, this loop, and the departures |
704 | 0 | * from it, will need to be reworked. |
705 | 0 | */ |
706 | 0 | for (si = 0; signerinfos[si] != NULL; si++) { |
707 | 0 |
|
708 | 0 | signerinfo = signerinfos[si]; |
709 | 0 |
|
710 | 0 | /* find right digest */ |
711 | 0 | digestalgtag = SECOID_GetAlgorithmTag(&(signerinfo->digestAlg)); |
712 | 0 | for (di = 0; digestalgs[di] != NULL; di++) { |
713 | 0 | /* XXX Should I be comparing more than the tag? */ |
714 | 0 | if (digestalgtag == SECOID_GetAlgorithmTag(digestalgs[di])) |
715 | 0 | break; |
716 | 0 | } |
717 | 0 | if (digestalgs[di] == NULL) { |
718 | 0 | /* XXX oops; do what? set an error? */ |
719 | 0 | return SECFailure; |
720 | 0 | } |
721 | 0 | PORT_Assert(digests[di] != NULL); |
722 | 0 |
|
723 | 0 | cert = signerinfo->cert; |
724 | 0 | privkey = PK11_FindKeyByAnyCert(cert, pwfnarg); |
725 | 0 | if (privkey == NULL) |
726 | 0 | return SECFailure; |
727 | 0 | |
728 | 0 | /* |
729 | 0 | * XXX I think there should be a cert-level interface for this, |
730 | 0 | * so that I do not have to know about subjectPublicKeyInfo... |
731 | 0 | */ |
732 | 0 | signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); |
733 | 0 |
|
734 | 0 | if (signerinfo->authAttr != NULL) { |
735 | 0 | SEC_PKCS7Attribute *attr; |
736 | 0 | SECItem encoded_attrs; |
737 | 0 | SECItem *dummy; |
738 | 0 | SECOidTag algid; |
739 | 0 |
|
740 | 0 | /* |
741 | 0 | * First, find and fill in the message digest attribute. |
742 | 0 | */ |
743 | 0 | attr = sec_PKCS7FindAttribute(signerinfo->authAttr, |
744 | 0 | SEC_OID_PKCS9_MESSAGE_DIGEST, |
745 | 0 | PR_TRUE); |
746 | 0 | PORT_Assert(attr != NULL); |
747 | 0 | if (attr == NULL) { |
748 | 0 | SECKEY_DestroyPrivateKey(privkey); |
749 | 0 | return SECFailure; |
750 | 0 | } |
751 | 0 | |
752 | 0 | /* |
753 | 0 | * XXX The second half of the following assertion prevents |
754 | 0 | * the encoder from being called twice on the same content. |
755 | 0 | * Either just remove the second half the assertion, or |
756 | 0 | * change the code to check if the value already there is |
757 | 0 | * the same as digests[di], whichever seems more right. |
758 | 0 | */ |
759 | 0 | PORT_Assert(attr->values != NULL && attr->values[0] == NULL); |
760 | 0 | attr->values[0] = digests[di]; |
761 | 0 |
|
762 | 0 | /* |
763 | 0 | * Before encoding, reorder the attributes so that when they |
764 | 0 | * are encoded, they will be conforming DER, which is required |
765 | 0 | * to have a specific order and that is what must be used for |
766 | 0 | * the hash/signature. We do this here, rather than building |
767 | 0 | * it into EncodeAttributes, because we do not want to do |
768 | 0 | * such reordering on incoming messages (which also uses |
769 | 0 | * EncodeAttributes) or our old signatures (and other "broken" |
770 | 0 | * implementations) will not verify. So, we want to guarantee |
771 | 0 | * that we send out good DER encodings of attributes, but not |
772 | 0 | * to expect to receive them. |
773 | 0 | */ |
774 | 0 | rv = sec_PKCS7ReorderAttributes(signerinfo->authAttr); |
775 | 0 | if (rv != SECSuccess) { |
776 | 0 | SECKEY_DestroyPrivateKey(privkey); |
777 | 0 | return SECFailure; |
778 | 0 | } |
779 | 0 | |
780 | 0 | encoded_attrs.data = NULL; |
781 | 0 | encoded_attrs.len = 0; |
782 | 0 | dummy = sec_PKCS7EncodeAttributes(NULL, &encoded_attrs, |
783 | 0 | &(signerinfo->authAttr)); |
784 | 0 | if (dummy == NULL) { |
785 | 0 | SECKEY_DestroyPrivateKey(privkey); |
786 | 0 | return SECFailure; |
787 | 0 | } |
788 | 0 | |
789 | 0 | algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, |
790 | 0 | digestalgtag); |
791 | 0 | if (algid == SEC_OID_UNKNOWN) { |
792 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
793 | 0 | SECKEY_DestroyPrivateKey(privkey); |
794 | 0 | return SECFailure; |
795 | 0 | } |
796 | 0 | rv = SEC_SignData(&signature, |
797 | 0 | encoded_attrs.data, encoded_attrs.len, |
798 | 0 | privkey, |
799 | 0 | algid); |
800 | 0 | SECITEM_FreeItem(&encoded_attrs, PR_FALSE); |
801 | 0 | } else { |
802 | 0 | rv = SGN_Digest(privkey, digestalgtag, &signature, |
803 | 0 | digests[di]); |
804 | 0 | } |
805 | 0 |
|
806 | 0 | SECKEY_DestroyPrivateKey(privkey); |
807 | 0 |
|
808 | 0 | if (rv != SECSuccess) |
809 | 0 | return rv; |
810 | 0 | |
811 | 0 | rv = SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature); |
812 | 0 | if (rv != SECSuccess) |
813 | 0 | return rv; |
814 | 0 | |
815 | 0 | SECITEM_FreeItem(&signature, PR_FALSE); |
816 | 0 |
|
817 | 0 | rv = SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), |
818 | 0 | signalgtag, NULL); |
819 | 0 | if (rv != SECSuccess) |
820 | 0 | return SECFailure; |
821 | 0 | |
822 | 0 | /* |
823 | 0 | * Count the cert chain for this signer. |
824 | 0 | */ |
825 | 0 | if (signerinfo->certList != NULL) |
826 | 0 | certcount += signerinfo->certList->len; |
827 | 0 | } |
828 | 0 | } |
829 | 0 |
|
830 | 0 | if (certs != NULL) { |
831 | 0 | for (ci = 0; certs[ci] != NULL; ci++) |
832 | 0 | certcount++; |
833 | 0 | } |
834 | 0 |
|
835 | 0 | if (certlists != NULL) { |
836 | 0 | for (cli = 0; certlists[cli] != NULL; cli++) |
837 | 0 | certcount += certlists[cli]->len; |
838 | 0 | } |
839 | 0 |
|
840 | 0 | if (certcount == 0) |
841 | 0 | return SECSuccess; /* signing done; no certs */ |
842 | 0 | |
843 | 0 | /* |
844 | 0 | * Combine all of the certs and cert chains into rawcerts. |
845 | 0 | * Note: certcount is an upper bound; we may not need that many slots |
846 | 0 | * but we will allocate anyway to avoid having to do another pass. |
847 | 0 | * (The temporary space saving is not worth it.) |
848 | 0 | */ |
849 | 0 | rawcerts = (SECItem **)PORT_ArenaAlloc(poolp, |
850 | 0 | (certcount + 1) * sizeof(SECItem *)); |
851 | 0 | if (rawcerts == NULL) |
852 | 0 | return SECFailure; |
853 | 0 | |
854 | 0 | /* |
855 | 0 | * XXX Want to check for duplicates and not add *any* cert that is |
856 | 0 | * already in the set. This will be more important when we start |
857 | 0 | * dealing with larger sets of certs, dual-key certs (signing and |
858 | 0 | * encryption), etc. For the time being we can slide by... |
859 | 0 | */ |
860 | 0 | rci = 0; |
861 | 0 | if (signerinfos != NULL) { |
862 | 0 | for (si = 0; signerinfos[si] != NULL; si++) { |
863 | 0 | signerinfo = signerinfos[si]; |
864 | 0 | for (ci = 0; ci < signerinfo->certList->len; ci++) |
865 | 0 | rawcerts[rci++] = &(signerinfo->certList->certs[ci]); |
866 | 0 | } |
867 | 0 | } |
868 | 0 |
|
869 | 0 | if (certs != NULL) { |
870 | 0 | for (ci = 0; certs[ci] != NULL; ci++) |
871 | 0 | rawcerts[rci++] = &(certs[ci]->derCert); |
872 | 0 | } |
873 | 0 |
|
874 | 0 | if (certlists != NULL) { |
875 | 0 | for (cli = 0; certlists[cli] != NULL; cli++) { |
876 | 0 | for (ci = 0; ci < certlists[cli]->len; ci++) |
877 | 0 | rawcerts[rci++] = &(certlists[cli]->certs[ci]); |
878 | 0 | } |
879 | 0 | } |
880 | 0 |
|
881 | 0 | rawcerts[rci] = NULL; |
882 | 0 | *rawcertsp = rawcerts; |
883 | 0 |
|
884 | 0 | return SECSuccess; |
885 | 0 | } |
886 | | |
887 | | SECStatus |
888 | | SEC_PKCS7EncoderFinish(SEC_PKCS7EncoderContext *p7ecx, |
889 | | SECKEYGetPasswordKey pwfn, void *pwfnarg) |
890 | 0 | { |
891 | 0 | SECStatus rv; |
892 | 0 |
|
893 | 0 | /* |
894 | 0 | * Flush out any remaining data. |
895 | 0 | */ |
896 | 0 | rv = sec_pkcs7_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE); |
897 | 0 |
|
898 | 0 | /* |
899 | 0 | * Turn off streaming stuff. |
900 | 0 | */ |
901 | 0 | SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); |
902 | 0 | SEC_ASN1EncoderClearStreaming(p7ecx->ecx); |
903 | 0 |
|
904 | 0 | if (rv != SECSuccess) |
905 | 0 | goto loser; |
906 | 0 | |
907 | 0 | rv = sec_pkcs7_encoder_sig_and_certs(p7ecx->cinfo, pwfn, pwfnarg); |
908 | 0 | if (rv != SECSuccess) |
909 | 0 | goto loser; |
910 | 0 | |
911 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); |
912 | 0 |
|
913 | 0 | loser: |
914 | 0 | SEC_ASN1EncoderFinish(p7ecx->ecx); |
915 | 0 | PORT_Free(p7ecx); |
916 | 0 | return rv; |
917 | 0 | } |
918 | | |
919 | | /* |
920 | | * Abort the ASN.1 stream. Used by pkcs 12 |
921 | | */ |
922 | | void |
923 | | SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error) |
924 | 0 | { |
925 | 0 | PORT_Assert(p7ecx); |
926 | 0 | SEC_ASN1EncoderAbort(p7ecx->ecx, error); |
927 | 0 | } |
928 | | |
929 | | /* |
930 | | * After this routine is called, the entire PKCS7 contentInfo is ready |
931 | | * to be encoded. This is used internally, but can also be called from |
932 | | * elsewhere for those who want to be able to just have pointers to |
933 | | * the ASN1 template for pkcs7 contentInfo built into their own encodings. |
934 | | */ |
935 | | SECStatus |
936 | | SEC_PKCS7PrepareForEncode(SEC_PKCS7ContentInfo *cinfo, |
937 | | PK11SymKey *bulkkey, |
938 | | SECKEYGetPasswordKey pwfn, |
939 | | void *pwfnarg) |
940 | 0 | { |
941 | 0 | SEC_PKCS7EncoderContext *p7ecx; |
942 | 0 | SECItem *content, *enc_content; |
943 | 0 | SECStatus rv; |
944 | 0 |
|
945 | 0 | p7ecx = sec_pkcs7_encoder_start_contexts(cinfo, bulkkey); |
946 | 0 | if (p7ecx == NULL) |
947 | 0 | return SECFailure; |
948 | 0 | |
949 | 0 | content = SEC_PKCS7GetContent(cinfo); |
950 | 0 |
|
951 | 0 | if (p7ecx->encryptobj != NULL) { |
952 | 0 | SECOidTag kind; |
953 | 0 | SEC_PKCS7EncryptedContentInfo *enccinfo; |
954 | 0 |
|
955 | 0 | kind = SEC_PKCS7ContentType(p7ecx->cinfo); |
956 | 0 | switch (kind) { |
957 | 0 | default: |
958 | 0 | PORT_Assert(0); |
959 | 0 | rv = SECFailure; |
960 | 0 | goto loser; |
961 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
962 | 0 | enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo); |
963 | 0 | break; |
964 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
965 | 0 | enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo); |
966 | 0 | break; |
967 | 0 | case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
968 | 0 | enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo); |
969 | 0 | break; |
970 | 0 | } |
971 | 0 | enc_content = &(enccinfo->encContent); |
972 | 0 | } else { |
973 | 0 | enc_content = NULL; |
974 | 0 | } |
975 | 0 |
|
976 | 0 | if (content != NULL && content->data != NULL && content->len) { |
977 | 0 | rv = sec_pkcs7_encoder_work_data(p7ecx, enc_content, |
978 | 0 | content->data, content->len, PR_TRUE); |
979 | 0 | if (rv != SECSuccess) |
980 | 0 | goto loser; |
981 | 0 | } |
982 | 0 | |
983 | 0 | rv = sec_pkcs7_encoder_sig_and_certs(cinfo, pwfn, pwfnarg); |
984 | 0 |
|
985 | 0 | loser: |
986 | 0 | PORT_Free(p7ecx); |
987 | 0 | return rv; |
988 | 0 | } |
989 | | |
990 | | /* |
991 | | * Encode a PKCS7 object, in one shot. All necessary components |
992 | | * of the object must already be specified. Either the data has |
993 | | * already been included (via SetContent), or the data is detached, |
994 | | * or there is no data at all (certs-only). |
995 | | * |
996 | | * "cinfo" specifies the object to be encoded. |
997 | | * |
998 | | * "outputfn" is where the encoded bytes will be passed. |
999 | | * |
1000 | | * "outputarg" is an opaque argument to the above callback. |
1001 | | * |
1002 | | * "bulkkey" specifies the bulk encryption key to use. This argument |
1003 | | * can be NULL if no encryption is being done, or if the bulk key should |
1004 | | * be generated internally (usually the case for EnvelopedData but never |
1005 | | * for EncryptedData, which *must* provide a bulk encryption key). |
1006 | | * |
1007 | | * "pwfn" is a callback for getting the password which protects the |
1008 | | * private key of the signer. This argument can be NULL if it is known |
1009 | | * that no signing is going to be done. |
1010 | | * |
1011 | | * "pwfnarg" is an opaque argument to the above callback. |
1012 | | */ |
1013 | | SECStatus |
1014 | | SEC_PKCS7Encode(SEC_PKCS7ContentInfo *cinfo, |
1015 | | SEC_PKCS7EncoderOutputCallback outputfn, |
1016 | | void *outputarg, |
1017 | | PK11SymKey *bulkkey, |
1018 | | SECKEYGetPasswordKey pwfn, |
1019 | | void *pwfnarg) |
1020 | 0 | { |
1021 | 0 | SECStatus rv; |
1022 | 0 |
|
1023 | 0 | rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg); |
1024 | 0 | if (rv == SECSuccess) { |
1025 | 0 | struct sec_pkcs7_encoder_output outputcx; |
1026 | 0 |
|
1027 | 0 | outputcx.outputfn = outputfn; |
1028 | 0 | outputcx.outputarg = outputarg; |
1029 | 0 |
|
1030 | 0 | rv = SEC_ASN1Encode(cinfo, sec_PKCS7ContentInfoTemplate, |
1031 | 0 | sec_pkcs7_encoder_out, &outputcx); |
1032 | 0 | } |
1033 | 0 |
|
1034 | 0 | return rv; |
1035 | 0 | } |
1036 | | |
1037 | | /* |
1038 | | * Encode a PKCS7 object, in one shot. All necessary components |
1039 | | * of the object must already be specified. Either the data has |
1040 | | * already been included (via SetContent), or the data is detached, |
1041 | | * or there is no data at all (certs-only). The output, rather than |
1042 | | * being passed to an output function as is done above, is all put |
1043 | | * into a SECItem. |
1044 | | * |
1045 | | * "pool" specifies a pool from which to allocate the result. |
1046 | | * It can be NULL, in which case memory is allocated generically. |
1047 | | * |
1048 | | * "dest" specifies a SECItem in which to put the result data. |
1049 | | * It can be NULL, in which case the entire item is allocated, too. |
1050 | | * |
1051 | | * "cinfo" specifies the object to be encoded. |
1052 | | * |
1053 | | * "bulkkey" specifies the bulk encryption key to use. This argument |
1054 | | * can be NULL if no encryption is being done, or if the bulk key should |
1055 | | * be generated internally (usually the case for EnvelopedData but never |
1056 | | * for EncryptedData, which *must* provide a bulk encryption key). |
1057 | | * |
1058 | | * "pwfn" is a callback for getting the password which protects the |
1059 | | * private key of the signer. This argument can be NULL if it is known |
1060 | | * that no signing is going to be done. |
1061 | | * |
1062 | | * "pwfnarg" is an opaque argument to the above callback. |
1063 | | */ |
1064 | | SECItem * |
1065 | | SEC_PKCS7EncodeItem(PLArenaPool *pool, |
1066 | | SECItem *dest, |
1067 | | SEC_PKCS7ContentInfo *cinfo, |
1068 | | PK11SymKey *bulkkey, |
1069 | | SECKEYGetPasswordKey pwfn, |
1070 | | void *pwfnarg) |
1071 | 0 | { |
1072 | 0 | SECStatus rv; |
1073 | 0 |
|
1074 | 0 | rv = SEC_PKCS7PrepareForEncode(cinfo, bulkkey, pwfn, pwfnarg); |
1075 | 0 | if (rv != SECSuccess) |
1076 | 0 | return NULL; |
1077 | 0 | |
1078 | 0 | return SEC_ASN1EncodeItem(pool, dest, cinfo, sec_PKCS7ContentInfoTemplate); |
1079 | 0 | } |