File: | s/lib/smime/cmsencode.c |
Warning: | line 663, column 9 Value stored to 'rv' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | * CMS encoding. |
7 | */ |
8 | |
9 | #include "cmslocal.h" |
10 | |
11 | #include "cert.h" |
12 | #include "keyhi.h" |
13 | #include "secasn1.h" |
14 | #include "secoid.h" |
15 | #include "secitem.h" |
16 | #include "pk11func.h" |
17 | #include "secerr.h" |
18 | |
19 | struct nss_cms_encoder_output { |
20 | NSSCMSContentCallback outputfn; |
21 | void *outputarg; |
22 | PLArenaPool *destpoolp; |
23 | SECItem *dest; |
24 | }; |
25 | |
26 | struct NSSCMSEncoderContextStr { |
27 | SEC_ASN1EncoderContext *ecx; /* ASN.1 encoder context */ |
28 | PRBool ecxupdated; /* true if data was handed in */ |
29 | NSSCMSMessage *cmsg; /* pointer to the root message */ |
30 | SECOidTag type; /* type tag of the current content */ |
31 | NSSCMSContent content; /* pointer to current content */ |
32 | struct nss_cms_encoder_output output; /* output function */ |
33 | int error; /* error code */ |
34 | NSSCMSEncoderContext *childp7ecx; /* link to child encoder context */ |
35 | }; |
36 | |
37 | static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx); |
38 | static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx); |
39 | static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, |
40 | const char *data, unsigned long len); |
41 | static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, |
42 | const unsigned char *data, unsigned long len, |
43 | PRBool final, PRBool innermost); |
44 | |
45 | extern const SEC_ASN1Template NSSCMSMessageTemplate[]; |
46 | |
47 | /* |
48 | * The little output function that the ASN.1 encoder calls to hand |
49 | * us bytes which we in turn hand back to our caller (via the callback |
50 | * they gave us). |
51 | */ |
52 | static void |
53 | nss_cms_encoder_out(void *arg, const char *buf, unsigned long len, |
54 | int depth, SEC_ASN1EncodingPart data_kind) |
55 | { |
56 | struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; |
57 | unsigned char *dest; |
58 | unsigned long offset; |
59 | |
60 | #ifdef CMSDEBUG |
61 | int i; |
62 | const char *data_name = "unknown"; |
63 | |
64 | switch (data_kind) { |
65 | case SEC_ASN1_Identifier: |
66 | data_name = "identifier"; |
67 | break; |
68 | case SEC_ASN1_Length: |
69 | data_name = "length"; |
70 | break; |
71 | case SEC_ASN1_Contents: |
72 | data_name = "contents"; |
73 | break; |
74 | case SEC_ASN1_EndOfContents: |
75 | data_name = "end-of-contents"; |
76 | break; |
77 | } |
78 | fprintf(stderrstderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len); |
79 | for (i = 0; i < len; i++) { |
80 | fprintf(stderrstderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); |
81 | } |
82 | if ((i % 16) != 0) |
83 | fprintf(stderrstderr, "\n"); |
84 | #endif |
85 | |
86 | if (output->outputfn != NULL((void*)0)) |
87 | /* call output callback with DER data */ |
88 | output->outputfn(output->outputarg, buf, len); |
89 | |
90 | if (output->dest != NULL((void*)0)) { |
91 | /* store DER data in SECItem */ |
92 | offset = output->dest->len; |
93 | if (offset == 0) { |
94 | dest = (unsigned char *)PORT_ArenaAllocPORT_ArenaAlloc_Util(output->destpoolp, len); |
95 | } else { |
96 | dest = (unsigned char *)PORT_ArenaGrowPORT_ArenaGrow_Util(output->destpoolp, |
97 | output->dest->data, |
98 | output->dest->len, |
99 | output->dest->len + len); |
100 | } |
101 | if (dest == NULL((void*)0)) |
102 | /* oops */ |
103 | return; |
104 | |
105 | output->dest->data = dest; |
106 | output->dest->len += len; |
107 | |
108 | /* copy it in */ |
109 | if (len) { |
110 | PORT_Memcpymemcpy(output->dest->data + offset, buf, len); |
111 | } |
112 | } |
113 | } |
114 | |
115 | /* |
116 | * nss_cms_encoder_notify - ASN.1 encoder callback |
117 | * |
118 | * this function is called by the ASN.1 encoder before and after the encoding of |
119 | * every object. here, it is used to keep track of data structures, set up |
120 | * encryption and/or digesting and possibly set up child encoders. |
121 | */ |
122 | static void |
123 | nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth) |
124 | { |
125 | NSSCMSEncoderContext *p7ecx; |
126 | NSSCMSContentInfo *rootcinfo, *cinfo; |
127 | PRBool after = !before; |
128 | SECOidTag childtype; |
129 | SECItem *item; |
130 | |
131 | p7ecx = (NSSCMSEncoderContext *)arg; |
132 | PORT_Assert(p7ecx != NULL)((p7ecx != ((void*)0))?((void)0):PR_Assert("p7ecx != NULL","cmsencode.c" ,132)); |
133 | |
134 | rootcinfo = &(p7ecx->cmsg->contentInfo); |
135 | |
136 | #ifdef CMSDEBUG |
137 | fprintf(stderrstderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", |
138 | dest, depth); |
139 | #endif |
140 | |
141 | /* |
142 | * Watch for the content field, at which point we want to instruct |
143 | * the ASN.1 encoder to start taking bytes from the buffer. |
144 | */ |
145 | if (NSS_CMSType_IsData(p7ecx->type)) { |
146 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
147 | if (before && dest == &(cinfo->rawContent)) { |
148 | /* just set up encoder to grab from user - no encryption or digesting */ |
149 | if ((item = cinfo->content.data) != NULL((void*)0)) |
150 | (void)nss_cms_encoder_work_data(p7ecx, NULL((void*)0), item->data, |
151 | item->len, PR_TRUE1, PR_TRUE1); |
152 | else |
153 | SEC_ASN1EncoderSetTakeFromBufSEC_ASN1EncoderSetTakeFromBuf_Util(p7ecx->ecx); |
154 | SEC_ASN1EncoderClearNotifyProcSEC_ASN1EncoderClearNotifyProc_Util(p7ecx->ecx); /* no need to get notified anymore */ |
155 | } |
156 | } else if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
157 | /* when we know what the content is, we encode happily until we reach the inner content */ |
158 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
159 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
160 | |
161 | if (after && dest == &(cinfo->contentType)) { |
162 | /* we're right before encoding the data (if we have some or not) */ |
163 | /* (for encrypted data, we're right before the contentEncAlg which may change */ |
164 | /* in nss_cms_before_data because of IV calculation when setting up encryption) */ |
165 | if (nss_cms_before_data(p7ecx) != SECSuccess) |
166 | p7ecx->error = PORT_GetErrorPORT_GetError_Util(); |
167 | } |
168 | if (before && dest == &(cinfo->rawContent)) { |
169 | if (p7ecx->childp7ecx == NULL((void*)0)) { |
170 | if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL((void*)0))) { |
171 | /* we are the innermost non-data and we have data - feed it in */ |
172 | (void)nss_cms_encoder_work_data(p7ecx, NULL((void*)0), item->data, |
173 | item->len, PR_TRUE1, PR_TRUE1); |
174 | } else { |
175 | /* else we'll have to get data from user */ |
176 | SEC_ASN1EncoderSetTakeFromBufSEC_ASN1EncoderSetTakeFromBuf_Util(p7ecx->ecx); |
177 | } |
178 | } else { |
179 | /* if we have a nested encoder, wait for its data */ |
180 | SEC_ASN1EncoderSetTakeFromBufSEC_ASN1EncoderSetTakeFromBuf_Util(p7ecx->ecx); |
181 | } |
182 | } |
183 | if (after && dest == &(cinfo->rawContent)) { |
184 | if (nss_cms_after_data(p7ecx) != SECSuccess) |
185 | p7ecx->error = PORT_GetErrorPORT_GetError_Util(); |
186 | SEC_ASN1EncoderClearNotifyProcSEC_ASN1EncoderClearNotifyProc_Util(p7ecx->ecx); /* no need to get notified anymore */ |
187 | } |
188 | } else { |
189 | /* we're still in the root message */ |
190 | if (after && dest == &(rootcinfo->contentType)) { |
191 | /* got the content type OID now - so find out the type tag */ |
192 | p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); |
193 | /* set up a pointer to our current content */ |
194 | p7ecx->content = rootcinfo->content; |
195 | } |
196 | } |
197 | } |
198 | |
199 | /* |
200 | * nss_cms_before_data - setup the current encoder to receive data |
201 | */ |
202 | static SECStatus |
203 | nss_cms_before_data(NSSCMSEncoderContext *p7ecx) |
204 | { |
205 | SECStatus rv; |
206 | SECOidTag childtype; |
207 | NSSCMSContentInfo *cinfo; |
208 | NSSCMSEncoderContext *childp7ecx; |
209 | const SEC_ASN1Template *template; |
210 | |
211 | /* call _Encode_BeforeData handlers */ |
212 | switch (p7ecx->type) { |
213 | case SEC_OID_PKCS7_SIGNED_DATA: |
214 | /* we're encoding a signedData, so set up the digests */ |
215 | rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData); |
216 | break; |
217 | case SEC_OID_PKCS7_DIGESTED_DATA: |
218 | /* we're encoding a digestedData, so set up the digest */ |
219 | rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData); |
220 | break; |
221 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
222 | rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData); |
223 | break; |
224 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
225 | rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData); |
226 | break; |
227 | default: |
228 | if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
229 | rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, |
230 | p7ecx->content.genericData); |
231 | } else { |
232 | rv = SECFailure; |
233 | } |
234 | } |
235 | if (rv != SECSuccess) |
236 | return SECFailure; |
237 | |
238 | /* ok, now we have a pointer to cinfo */ |
239 | /* find out what kind of data is encapsulated */ |
240 | |
241 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
242 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
243 | |
244 | if (NSS_CMSType_IsWrapper(childtype)) { |
245 | /* in these cases, we need to set up a child encoder! */ |
246 | /* create new encoder context */ |
247 | childp7ecx = PORT_ZAllocPORT_ZAlloc_Util(sizeof(NSSCMSEncoderContext)); |
248 | if (childp7ecx == NULL((void*)0)) |
249 | return SECFailure; |
250 | |
251 | /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder |
252 | * (which will encrypt and/or digest it) |
253 | * this needs to route back into our update function |
254 | * which finds the lowest encoding context & encrypts and computes digests */ |
255 | childp7ecx->type = childtype; |
256 | childp7ecx->content = cinfo->content; |
257 | /* use the non-recursive update function here, of course */ |
258 | childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update; |
259 | childp7ecx->output.outputarg = p7ecx; |
260 | childp7ecx->output.destpoolp = NULL((void*)0); |
261 | childp7ecx->output.dest = NULL((void*)0); |
262 | childp7ecx->cmsg = p7ecx->cmsg; |
263 | childp7ecx->ecxupdated = PR_FALSE0; |
264 | childp7ecx->childp7ecx = NULL((void*)0); |
265 | |
266 | template = NSS_CMSUtil_GetTemplateByTypeTag(childtype); |
267 | if (template == NULL((void*)0)) |
268 | goto loser; /* cannot happen */ |
269 | |
270 | /* now initialize the data for encoding the first third */ |
271 | switch (childp7ecx->type) { |
272 | case SEC_OID_PKCS7_SIGNED_DATA: |
273 | rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); |
274 | break; |
275 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
276 | rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); |
277 | break; |
278 | case SEC_OID_PKCS7_DIGESTED_DATA: |
279 | rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); |
280 | break; |
281 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
282 | rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); |
283 | break; |
284 | default: |
285 | rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, |
286 | cinfo->content.genericData); |
287 | break; |
288 | } |
289 | if (rv != SECSuccess) |
290 | goto loser; |
291 | |
292 | /* |
293 | * Initialize the BER encoder. |
294 | */ |
295 | childp7ecx->ecx = SEC_ASN1EncoderStartSEC_ASN1EncoderStart_Util(cinfo->content.pointer, template, |
296 | nss_cms_encoder_out, &(childp7ecx->output)); |
297 | if (childp7ecx->ecx == NULL((void*)0)) |
298 | goto loser; |
299 | |
300 | /* |
301 | * Indicate that we are streaming. We will be streaming until we |
302 | * get past the contents bytes. |
303 | */ |
304 | if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) |
305 | SEC_ASN1EncoderSetStreamingSEC_ASN1EncoderSetStreaming_Util(childp7ecx->ecx); |
306 | |
307 | /* |
308 | * The notify function will watch for the contents field. |
309 | */ |
310 | p7ecx->childp7ecx = childp7ecx; |
311 | SEC_ASN1EncoderSetNotifyProcSEC_ASN1EncoderSetNotifyProc_Util(childp7ecx->ecx, nss_cms_encoder_notify, |
312 | childp7ecx); |
313 | |
314 | /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ |
315 | /* encoding process - we'll do that from the update function instead */ |
316 | /* otherwise we'd be encoding data from a call of the notify function of the */ |
317 | /* parent encoder (which would not work) */ |
318 | |
319 | } else if (NSS_CMSType_IsData(childtype)) { |
320 | p7ecx->childp7ecx = NULL((void*)0); |
321 | } else { |
322 | /* we do not know this type */ |
323 | p7ecx->error = SEC_ERROR_BAD_DER; |
324 | } |
325 | |
326 | return SECSuccess; |
327 | |
328 | loser: |
329 | if (childp7ecx) { |
330 | if (childp7ecx->ecx) |
331 | SEC_ASN1EncoderFinishSEC_ASN1EncoderFinish_Util(childp7ecx->ecx); |
332 | PORT_FreePORT_Free_Util(childp7ecx); |
333 | p7ecx->childp7ecx = NULL((void*)0); |
334 | } |
335 | return SECFailure; |
336 | } |
337 | |
338 | static SECStatus |
339 | nss_cms_after_data(NSSCMSEncoderContext *p7ecx) |
340 | { |
341 | SECStatus rv = SECFailure; |
342 | |
343 | switch (p7ecx->type) { |
344 | case SEC_OID_PKCS7_SIGNED_DATA: |
345 | /* this will finish the digests and sign */ |
346 | rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData); |
347 | break; |
348 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
349 | rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData); |
350 | break; |
351 | case SEC_OID_PKCS7_DIGESTED_DATA: |
352 | rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData); |
353 | break; |
354 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
355 | rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData); |
356 | break; |
357 | default: |
358 | if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
359 | rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, |
360 | p7ecx->content.genericData); |
361 | } else { |
362 | rv = SECFailure; |
363 | } |
364 | break; |
365 | } |
366 | return rv; |
367 | } |
368 | |
369 | /* |
370 | * nss_cms_encoder_work_data - process incoming data |
371 | * |
372 | * (from the user or the next encoding layer) |
373 | * Here, we need to digest and/or encrypt, then pass it on |
374 | */ |
375 | static SECStatus |
376 | nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest, |
377 | const unsigned char *data, unsigned long len, |
378 | PRBool final, PRBool innermost) |
379 | { |
380 | unsigned char *buf = NULL((void*)0); |
381 | SECStatus rv; |
382 | NSSCMSContentInfo *cinfo; |
383 | |
384 | rv = SECSuccess; /* may as well be optimistic */ |
385 | |
386 | /* |
387 | * We should really have data to process, or we should be trying |
388 | * to finish/flush the last block. (This is an overly paranoid |
389 | * check since all callers are in this file and simple inspection |
390 | * proves they do it right. But it could find a bug in future |
391 | * modifications/development, that is why it is here.) |
392 | */ |
393 | PORT_Assert((data != NULL && len) || final)(((data != ((void*)0) && len) || final)?((void)0):PR_Assert ("(data != NULL && len) || final","cmsencode.c",393)); |
394 | |
395 | /* we got data (either from the caller, or from a lower level encoder) */ |
396 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
397 | if (!cinfo) { |
398 | /* The original programmer didn't expect this to happen */ |
399 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
400 | return SECFailure; |
401 | } |
402 | |
403 | /* Update the running digest. */ |
404 | if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL((void*)0)) |
405 | NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); |
406 | |
407 | /* Encrypt this chunk. */ |
408 | if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL((void*)0)) { |
409 | unsigned int inlen; /* length of data being encrypted */ |
410 | unsigned int outlen; /* length of encrypted data */ |
411 | unsigned int buflen; /* length available for encrypted data */ |
412 | |
413 | inlen = len; |
414 | buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, |
415 | inlen, final); |
416 | if (buflen == 0) { |
417 | /* |
418 | * No output is expected, but the input data may be buffered |
419 | * so we still have to call Encrypt. |
420 | */ |
421 | rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL((void*)0), NULL((void*)0), 0, |
422 | data, inlen, final); |
423 | if (final) { |
424 | len = 0; |
425 | goto done; |
426 | } |
427 | return rv; |
428 | } |
429 | |
430 | if (dest != NULL((void*)0)) |
431 | buf = (unsigned char *)PORT_ArenaAllocPORT_ArenaAlloc_Util(p7ecx->cmsg->poolp, buflen); |
432 | else |
433 | buf = (unsigned char *)PORT_AllocPORT_Alloc_Util(buflen); |
434 | |
435 | if (buf == NULL((void*)0)) { |
436 | rv = SECFailure; |
437 | } else { |
438 | rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, |
439 | &outlen, buflen, |
440 | data, inlen, final); |
441 | data = buf; |
442 | len = outlen; |
443 | } |
444 | if (rv != SECSuccess) |
445 | /* encryption or malloc failed? */ |
446 | return rv; |
447 | } |
448 | |
449 | /* |
450 | * at this point (data,len) has everything we'd like to give to the CURRENT encoder |
451 | * (which will encode it, then hand it back to the user or the parent encoder) |
452 | * We don't encode the data if we're innermost and we're told not to include the data |
453 | */ |
454 | if (p7ecx->ecx != NULL((void*)0) && len && |
455 | (!innermost || cinfo->rawContent != cinfo->content.pointer)) |
456 | rv = SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->ecx, (const char *)data, len); |
457 | |
458 | done: |
459 | |
460 | if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL((void*)0)) { |
461 | if (dest != NULL((void*)0)) { |
462 | dest->data = buf; |
463 | dest->len = len; |
464 | } else if (buf != NULL((void*)0)) { |
465 | PORT_FreePORT_Free_Util(buf); |
466 | } |
467 | } |
468 | return rv; |
469 | } |
470 | |
471 | /* |
472 | * nss_cms_encoder_update - deliver encoded data to the next higher level |
473 | * |
474 | * no recursion here because we REALLY want to end up at the next higher encoder! |
475 | */ |
476 | static SECStatus |
477 | nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) |
478 | { |
479 | /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ |
480 | return nss_cms_encoder_work_data(p7ecx, NULL((void*)0), (const unsigned char *)data, |
481 | len, PR_FALSE0, PR_FALSE0); |
482 | } |
483 | |
484 | /* |
485 | * NSS_CMSEncoder_Start - set up encoding of a CMS message |
486 | * |
487 | * "cmsg" - message to encode |
488 | * "outputfn", "outputarg" - callback function for delivery of DER-encoded output |
489 | * will not be called if NULL. |
490 | * "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output |
491 | * "destpoolp" - pool to allocate DER-encoded output in |
492 | * "pwfn", pwfn_arg" - callback function for getting token password |
493 | * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData |
494 | * "detached_digestalgs", "detached_digests" - digests from detached content |
495 | */ |
496 | NSSCMSEncoderContext * |
497 | NSS_CMSEncoder_Start(NSSCMSMessage *cmsg, |
498 | NSSCMSContentCallback outputfn, void *outputarg, |
499 | SECItem *dest, PLArenaPool *destpoolp, |
500 | PK11PasswordFunc pwfn, void *pwfn_arg, |
501 | NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, |
502 | SECAlgorithmID **detached_digestalgs, SECItem **detached_digests) |
503 | { |
504 | NSSCMSEncoderContext *p7ecx; |
505 | SECStatus rv; |
506 | NSSCMSContentInfo *cinfo; |
507 | SECOidTag tag; |
508 | |
509 | NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, |
510 | detached_digestalgs, detached_digests); |
511 | |
512 | p7ecx = (NSSCMSEncoderContext *)PORT_ZAllocPORT_ZAlloc_Util(sizeof(NSSCMSEncoderContext)); |
513 | if (p7ecx == NULL((void*)0)) { |
514 | PORT_SetErrorPORT_SetError_Util(SEC_ERROR_NO_MEMORY); |
515 | return NULL((void*)0); |
516 | } |
517 | |
518 | p7ecx->cmsg = cmsg; |
519 | p7ecx->output.outputfn = outputfn; |
520 | p7ecx->output.outputarg = outputarg; |
521 | p7ecx->output.dest = dest; |
522 | p7ecx->output.destpoolp = destpoolp; |
523 | p7ecx->type = SEC_OID_UNKNOWN; |
524 | |
525 | cinfo = NSS_CMSMessage_GetContentInfo(cmsg); |
526 | |
527 | tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
528 | switch (tag) { |
529 | case SEC_OID_PKCS7_SIGNED_DATA: |
530 | rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); |
531 | break; |
532 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
533 | rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); |
534 | break; |
535 | case SEC_OID_PKCS7_DIGESTED_DATA: |
536 | rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); |
537 | break; |
538 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
539 | rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); |
540 | break; |
541 | default: |
542 | if (NSS_CMSType_IsWrapper(tag)) { |
543 | rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, |
544 | p7ecx->content.genericData); |
545 | } else { |
546 | rv = SECFailure; |
547 | } |
548 | break; |
549 | } |
550 | if (rv != SECSuccess) { |
551 | PORT_FreePORT_Free_Util(p7ecx); |
552 | return NULL((void*)0); |
553 | } |
554 | |
555 | /* Initialize the BER encoder. |
556 | * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ |
557 | p7ecx->ecx = SEC_ASN1EncoderStartSEC_ASN1EncoderStart_Util(cmsg, NSSCMSMessageTemplate, |
558 | nss_cms_encoder_out, &(p7ecx->output)); |
559 | if (p7ecx->ecx == NULL((void*)0)) { |
560 | PORT_FreePORT_Free_Util(p7ecx); |
561 | return NULL((void*)0); |
562 | } |
563 | p7ecx->ecxupdated = PR_FALSE0; |
564 | |
565 | /* |
566 | * Indicate that we are streaming. We will be streaming until we |
567 | * get past the contents bytes. |
568 | */ |
569 | if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) |
570 | SEC_ASN1EncoderSetStreamingSEC_ASN1EncoderSetStreaming_Util(p7ecx->ecx); |
571 | |
572 | /* |
573 | * The notify function will watch for the contents field. |
574 | */ |
575 | SEC_ASN1EncoderSetNotifyProcSEC_ASN1EncoderSetNotifyProc_Util(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); |
576 | |
577 | /* this will kick off the encoding process & encode everything up to the content bytes, |
578 | * at which point the notify function sets streaming mode (and possibly creates |
579 | * a child encoder). */ |
580 | p7ecx->ecxupdated = PR_TRUE1; |
581 | if (SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->ecx, NULL((void*)0), 0) != SECSuccess) { |
582 | PORT_FreePORT_Free_Util(p7ecx); |
583 | return NULL((void*)0); |
584 | } |
585 | |
586 | return p7ecx; |
587 | } |
588 | |
589 | /* |
590 | * NSS_CMSEncoder_Update - take content data delivery from the user |
591 | * |
592 | * "p7ecx" - encoder context |
593 | * "data" - content data |
594 | * "len" - length of content data |
595 | * |
596 | * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), |
597 | * then hand the data to the work_data fn |
598 | */ |
599 | SECStatus |
600 | NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len) |
601 | { |
602 | SECStatus rv; |
603 | NSSCMSContentInfo *cinfo; |
604 | SECOidTag childtype; |
605 | |
606 | if (p7ecx->error) |
607 | return SECFailure; |
608 | |
609 | /* hand data to the innermost decoder */ |
610 | if (p7ecx->childp7ecx) { |
611 | /* tell the child to start encoding, up to its first data byte, if it |
612 | * hasn't started yet */ |
613 | if (!p7ecx->childp7ecx->ecxupdated) { |
614 | p7ecx->childp7ecx->ecxupdated = PR_TRUE1; |
615 | if (SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->childp7ecx->ecx, NULL((void*)0), 0) != SECSuccess) |
616 | return SECFailure; |
617 | } |
618 | /* recursion here */ |
619 | rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len); |
620 | } else { |
621 | /* we are at innermost decoder */ |
622 | /* find out about our inner content type - must be data */ |
623 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
624 | if (!cinfo) { |
625 | /* The original programmer didn't expect this to happen */ |
626 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
627 | return SECFailure; |
628 | } |
629 | |
630 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
631 | if (!NSS_CMSType_IsData(childtype)) |
632 | return SECFailure; |
633 | /* and we must not have preset data */ |
634 | if (cinfo->content.data != NULL((void*)0)) |
635 | return SECFailure; |
636 | |
637 | /* hand it the data so it can encode it (let DER trickle up the chain) */ |
638 | rv = nss_cms_encoder_work_data(p7ecx, NULL((void*)0), (const unsigned char *)data, |
639 | len, PR_FALSE0, PR_TRUE1); |
640 | } |
641 | return rv; |
642 | } |
643 | |
644 | /* |
645 | * NSS_CMSEncoder_Cancel - stop all encoding |
646 | * |
647 | * we need to walk down the chain of encoders and the finish them from the innermost out |
648 | */ |
649 | SECStatus |
650 | NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx) |
651 | { |
652 | SECStatus rv = SECFailure; |
653 | |
654 | /* XXX do this right! */ |
655 | |
656 | /* |
657 | * Finish any inner decoders before us so that all the encoded data is flushed |
658 | * This basically finishes all the decoders from the innermost to the outermost. |
659 | * Finishing an inner decoder may result in data being updated to the outer decoder |
660 | * while we are already in NSS_CMSEncoder_Finish, but that's allright. |
661 | */ |
662 | if (p7ecx->childp7ecx) { |
663 | rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
Value stored to 'rv' is never read | |
664 | /* remember rv for now */ |
665 | #ifdef CMSDEBUG |
666 | if (rv != SECSuccess) { |
667 | fprintf(stderrstderr, "Fail to cancel inner encoder\n"); |
668 | } |
669 | #endif |
670 | } |
671 | |
672 | /* |
673 | * On the way back up, there will be no more data (if we had an |
674 | * inner encoder, it is done now!) |
675 | * Flush out any remaining data and/or finish digests. |
676 | */ |
677 | rv = nss_cms_encoder_work_data(p7ecx, NULL((void*)0), NULL((void*)0), 0, PR_TRUE1, (p7ecx->childp7ecx == NULL((void*)0))); |
678 | if (rv != SECSuccess) |
679 | goto loser; |
680 | |
681 | p7ecx->childp7ecx = NULL((void*)0); |
682 | |
683 | /* kick the encoder back into working mode again. |
684 | * We turn off streaming stuff (which will cause the encoder to continue |
685 | * encoding happily, now that we have all the data (like digests) ready for it). |
686 | */ |
687 | SEC_ASN1EncoderClearTakeFromBufSEC_ASN1EncoderClearTakeFromBuf_Util(p7ecx->ecx); |
688 | SEC_ASN1EncoderClearStreamingSEC_ASN1EncoderClearStreaming_Util(p7ecx->ecx); |
689 | |
690 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ |
691 | rv = SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->ecx, NULL((void*)0), 0); |
692 | |
693 | loser: |
694 | SEC_ASN1EncoderFinishSEC_ASN1EncoderFinish_Util(p7ecx->ecx); |
695 | PORT_FreePORT_Free_Util(p7ecx); |
696 | return rv; |
697 | } |
698 | |
699 | /* |
700 | * NSS_CMSEncoder_Finish - signal the end of data |
701 | * |
702 | * we need to walk down the chain of encoders and the finish them from the innermost out |
703 | */ |
704 | SECStatus |
705 | NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx) |
706 | { |
707 | SECStatus rv = SECFailure; |
708 | NSSCMSContentInfo *cinfo; |
709 | |
710 | /* |
711 | * Finish any inner decoders before us so that all the encoded data is flushed |
712 | * This basically finishes all the decoders from the innermost to the outermost. |
713 | * Finishing an inner decoder may result in data being updated to the outer decoder |
714 | * while we are already in NSS_CMSEncoder_Finish, but that's allright. |
715 | */ |
716 | if (p7ecx->childp7ecx) { |
717 | /* tell the child to start encoding, up to its first data byte, if it |
718 | * hasn't yet */ |
719 | if (!p7ecx->childp7ecx->ecxupdated) { |
720 | p7ecx->childp7ecx->ecxupdated = PR_TRUE1; |
721 | rv = SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->childp7ecx->ecx, NULL((void*)0), 0); |
722 | if (rv != SECSuccess) { |
723 | NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
724 | goto loser; |
725 | } |
726 | } |
727 | rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
728 | if (rv != SECSuccess) |
729 | goto loser; |
730 | } |
731 | |
732 | /* |
733 | * On the way back up, there will be no more data (if we had an |
734 | * inner encoder, it is done now!) |
735 | * Flush out any remaining data and/or finish digests. |
736 | */ |
737 | rv = nss_cms_encoder_work_data(p7ecx, NULL((void*)0), NULL((void*)0), 0, PR_TRUE1, (p7ecx->childp7ecx == NULL((void*)0))); |
738 | if (rv != SECSuccess) |
739 | goto loser; |
740 | |
741 | p7ecx->childp7ecx = NULL((void*)0); |
742 | |
743 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
744 | if (!cinfo) { |
745 | /* The original programmer didn't expect this to happen */ |
746 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
747 | rv = SECFailure; |
748 | goto loser; |
749 | } |
750 | SEC_ASN1EncoderClearTakeFromBufSEC_ASN1EncoderClearTakeFromBuf_Util(p7ecx->ecx); |
751 | SEC_ASN1EncoderClearStreamingSEC_ASN1EncoderClearStreaming_Util(p7ecx->ecx); |
752 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ |
753 | rv = SEC_ASN1EncoderUpdateSEC_ASN1EncoderUpdate_Util(p7ecx->ecx, NULL((void*)0), 0); |
754 | |
755 | if (p7ecx->error) |
756 | rv = SECFailure; |
757 | |
758 | loser: |
759 | SEC_ASN1EncoderFinishSEC_ASN1EncoderFinish_Util(p7ecx->ecx); |
760 | PORT_FreePORT_Free_Util(p7ecx); |
761 | return rv; |
762 | } |