/src/mozilla-central/security/nss/lib/smime/cmsencode.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 | | * 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 | 0 | { |
56 | 0 | struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; |
57 | 0 | unsigned char *dest; |
58 | 0 | unsigned long offset; |
59 | 0 |
|
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(stderr, "kind = %s, depth = %d, len = %d\n", data_name, depth, len); |
79 | | for (i = 0; i < len; i++) { |
80 | | fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); |
81 | | } |
82 | | if ((i % 16) != 0) |
83 | | fprintf(stderr, "\n"); |
84 | | #endif |
85 | |
|
86 | 0 | if (output->outputfn != NULL) |
87 | 0 | /* call output callback with DER data */ |
88 | 0 | output->outputfn(output->outputarg, buf, len); |
89 | 0 |
|
90 | 0 | if (output->dest != NULL) { |
91 | 0 | /* store DER data in SECItem */ |
92 | 0 | offset = output->dest->len; |
93 | 0 | if (offset == 0) { |
94 | 0 | dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len); |
95 | 0 | } else { |
96 | 0 | dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp, |
97 | 0 | output->dest->data, |
98 | 0 | output->dest->len, |
99 | 0 | output->dest->len + len); |
100 | 0 | } |
101 | 0 | if (dest == NULL) |
102 | 0 | /* oops */ |
103 | 0 | return; |
104 | 0 | |
105 | 0 | output->dest->data = dest; |
106 | 0 | output->dest->len += len; |
107 | 0 |
|
108 | 0 | /* copy it in */ |
109 | 0 | if (len) { |
110 | 0 | PORT_Memcpy(output->dest->data + offset, buf, len); |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
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 | 0 | { |
125 | 0 | NSSCMSEncoderContext *p7ecx; |
126 | 0 | NSSCMSContentInfo *rootcinfo, *cinfo; |
127 | 0 | PRBool after = !before; |
128 | 0 | SECOidTag childtype; |
129 | 0 | SECItem *item; |
130 | 0 |
|
131 | 0 | p7ecx = (NSSCMSEncoderContext *)arg; |
132 | 0 | PORT_Assert(p7ecx != NULL); |
133 | 0 |
|
134 | 0 | rootcinfo = &(p7ecx->cmsg->contentInfo); |
135 | 0 |
|
136 | | #ifdef CMSDEBUG |
137 | | fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", |
138 | | dest, depth); |
139 | | #endif |
140 | |
|
141 | 0 | /* |
142 | 0 | * Watch for the content field, at which point we want to instruct |
143 | 0 | * the ASN.1 encoder to start taking bytes from the buffer. |
144 | 0 | */ |
145 | 0 | if (NSS_CMSType_IsData(p7ecx->type)) { |
146 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
147 | 0 | if (before && dest == &(cinfo->rawContent)) { |
148 | 0 | /* just set up encoder to grab from user - no encryption or digesting */ |
149 | 0 | if ((item = cinfo->content.data) != NULL) |
150 | 0 | (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, |
151 | 0 | item->len, PR_TRUE, PR_TRUE); |
152 | 0 | else |
153 | 0 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); |
154 | 0 | SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ |
155 | 0 | } |
156 | 0 | } else if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
157 | 0 | /* when we know what the content is, we encode happily until we reach the inner content */ |
158 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
159 | 0 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
160 | 0 |
|
161 | 0 | if (after && dest == &(cinfo->contentType)) { |
162 | 0 | /* we're right before encoding the data (if we have some or not) */ |
163 | 0 | /* (for encrypted data, we're right before the contentEncAlg which may change */ |
164 | 0 | /* in nss_cms_before_data because of IV calculation when setting up encryption) */ |
165 | 0 | if (nss_cms_before_data(p7ecx) != SECSuccess) |
166 | 0 | p7ecx->error = PORT_GetError(); |
167 | 0 | } |
168 | 0 | if (before && dest == &(cinfo->rawContent)) { |
169 | 0 | if (p7ecx->childp7ecx == NULL) { |
170 | 0 | if ((NSS_CMSType_IsData(childtype) && (item = cinfo->content.data) != NULL)) { |
171 | 0 | /* we are the innermost non-data and we have data - feed it in */ |
172 | 0 | (void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, |
173 | 0 | item->len, PR_TRUE, PR_TRUE); |
174 | 0 | } else { |
175 | 0 | /* else we'll have to get data from user */ |
176 | 0 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); |
177 | 0 | } |
178 | 0 | } else { |
179 | 0 | /* if we have a nested encoder, wait for its data */ |
180 | 0 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | if (after && dest == &(cinfo->rawContent)) { |
184 | 0 | if (nss_cms_after_data(p7ecx) != SECSuccess) |
185 | 0 | p7ecx->error = PORT_GetError(); |
186 | 0 | SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ |
187 | 0 | } |
188 | 0 | } else { |
189 | 0 | /* we're still in the root message */ |
190 | 0 | if (after && dest == &(rootcinfo->contentType)) { |
191 | 0 | /* got the content type OID now - so find out the type tag */ |
192 | 0 | p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); |
193 | 0 | /* set up a pointer to our current content */ |
194 | 0 | p7ecx->content = rootcinfo->content; |
195 | 0 | } |
196 | 0 | } |
197 | 0 | } |
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 | 0 | { |
205 | 0 | SECStatus rv; |
206 | 0 | SECOidTag childtype; |
207 | 0 | NSSCMSContentInfo *cinfo; |
208 | 0 | NSSCMSEncoderContext *childp7ecx; |
209 | 0 | const SEC_ASN1Template *template; |
210 | 0 |
|
211 | 0 | /* call _Encode_BeforeData handlers */ |
212 | 0 | switch (p7ecx->type) { |
213 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
214 | 0 | /* we're encoding a signedData, so set up the digests */ |
215 | 0 | rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData); |
216 | 0 | break; |
217 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
218 | 0 | /* we're encoding a digestedData, so set up the digest */ |
219 | 0 | rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData); |
220 | 0 | break; |
221 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
222 | 0 | rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData); |
223 | 0 | break; |
224 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
225 | 0 | rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData); |
226 | 0 | break; |
227 | 0 | default: |
228 | 0 | if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
229 | 0 | rv = NSS_CMSGenericWrapperData_Encode_BeforeData(p7ecx->type, |
230 | 0 | p7ecx->content.genericData); |
231 | 0 | } else { |
232 | 0 | rv = SECFailure; |
233 | 0 | } |
234 | 0 | } |
235 | 0 | if (rv != SECSuccess) |
236 | 0 | return SECFailure; |
237 | 0 | |
238 | 0 | /* ok, now we have a pointer to cinfo */ |
239 | 0 | /* find out what kind of data is encapsulated */ |
240 | 0 | |
241 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
242 | 0 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
243 | 0 |
|
244 | 0 | if (NSS_CMSType_IsWrapper(childtype)) { |
245 | 0 | /* in these cases, we need to set up a child encoder! */ |
246 | 0 | /* create new encoder context */ |
247 | 0 | childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); |
248 | 0 | if (childp7ecx == NULL) |
249 | 0 | return SECFailure; |
250 | 0 | |
251 | 0 | /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder |
252 | 0 | * (which will encrypt and/or digest it) |
253 | 0 | * this needs to route back into our update function |
254 | 0 | * which finds the lowest encoding context & encrypts and computes digests */ |
255 | 0 | childp7ecx->type = childtype; |
256 | 0 | childp7ecx->content = cinfo->content; |
257 | 0 | /* use the non-recursive update function here, of course */ |
258 | 0 | childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update; |
259 | 0 | childp7ecx->output.outputarg = p7ecx; |
260 | 0 | childp7ecx->output.destpoolp = NULL; |
261 | 0 | childp7ecx->output.dest = NULL; |
262 | 0 | childp7ecx->cmsg = p7ecx->cmsg; |
263 | 0 | childp7ecx->ecxupdated = PR_FALSE; |
264 | 0 | childp7ecx->childp7ecx = NULL; |
265 | 0 |
|
266 | 0 | template = NSS_CMSUtil_GetTemplateByTypeTag(childtype); |
267 | 0 | if (template == NULL) |
268 | 0 | goto loser; /* cannot happen */ |
269 | 0 | |
270 | 0 | /* now initialize the data for encoding the first third */ |
271 | 0 | switch (childp7ecx->type) { |
272 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
273 | 0 | rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); |
274 | 0 | break; |
275 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
276 | 0 | rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); |
277 | 0 | break; |
278 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
279 | 0 | rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); |
280 | 0 | break; |
281 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
282 | 0 | rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); |
283 | 0 | break; |
284 | 0 | default: |
285 | 0 | rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(childp7ecx->type, |
286 | 0 | cinfo->content.genericData); |
287 | 0 | break; |
288 | 0 | } |
289 | 0 | if (rv != SECSuccess) |
290 | 0 | goto loser; |
291 | 0 | |
292 | 0 | /* |
293 | 0 | * Initialize the BER encoder. |
294 | 0 | */ |
295 | 0 | childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, |
296 | 0 | nss_cms_encoder_out, &(childp7ecx->output)); |
297 | 0 | if (childp7ecx->ecx == NULL) |
298 | 0 | goto loser; |
299 | 0 | |
300 | 0 | /* |
301 | 0 | * Indicate that we are streaming. We will be streaming until we |
302 | 0 | * get past the contents bytes. |
303 | 0 | */ |
304 | 0 | if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) |
305 | 0 | SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); |
306 | 0 |
|
307 | 0 | /* |
308 | 0 | * The notify function will watch for the contents field. |
309 | 0 | */ |
310 | 0 | p7ecx->childp7ecx = childp7ecx; |
311 | 0 | SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, |
312 | 0 | childp7ecx); |
313 | 0 |
|
314 | 0 | /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ |
315 | 0 | /* encoding process - we'll do that from the update function instead */ |
316 | 0 | /* otherwise we'd be encoding data from a call of the notify function of the */ |
317 | 0 | /* parent encoder (which would not work) */ |
318 | 0 |
|
319 | 0 | } else if (NSS_CMSType_IsData(childtype)) { |
320 | 0 | p7ecx->childp7ecx = NULL; |
321 | 0 | } else { |
322 | 0 | /* we do not know this type */ |
323 | 0 | p7ecx->error = SEC_ERROR_BAD_DER; |
324 | 0 | } |
325 | 0 |
|
326 | 0 | return SECSuccess; |
327 | 0 | |
328 | 0 | loser: |
329 | 0 | if (childp7ecx) { |
330 | 0 | if (childp7ecx->ecx) |
331 | 0 | SEC_ASN1EncoderFinish(childp7ecx->ecx); |
332 | 0 | PORT_Free(childp7ecx); |
333 | 0 | p7ecx->childp7ecx = NULL; |
334 | 0 | } |
335 | 0 | return SECFailure; |
336 | 0 | } |
337 | | |
338 | | static SECStatus |
339 | | nss_cms_after_data(NSSCMSEncoderContext *p7ecx) |
340 | 0 | { |
341 | 0 | SECStatus rv = SECFailure; |
342 | 0 |
|
343 | 0 | switch (p7ecx->type) { |
344 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
345 | 0 | /* this will finish the digests and sign */ |
346 | 0 | rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData); |
347 | 0 | break; |
348 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
349 | 0 | rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData); |
350 | 0 | break; |
351 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
352 | 0 | rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData); |
353 | 0 | break; |
354 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
355 | 0 | rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData); |
356 | 0 | break; |
357 | 0 | default: |
358 | 0 | if (NSS_CMSType_IsWrapper(p7ecx->type)) { |
359 | 0 | rv = NSS_CMSGenericWrapperData_Encode_AfterData(p7ecx->type, |
360 | 0 | p7ecx->content.genericData); |
361 | 0 | } else { |
362 | 0 | rv = SECFailure; |
363 | 0 | } |
364 | 0 | break; |
365 | 0 | } |
366 | 0 | return rv; |
367 | 0 | } |
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 | 0 | { |
380 | 0 | unsigned char *buf = NULL; |
381 | 0 | SECStatus rv; |
382 | 0 | NSSCMSContentInfo *cinfo; |
383 | 0 |
|
384 | 0 | rv = SECSuccess; /* may as well be optimistic */ |
385 | 0 |
|
386 | 0 | /* |
387 | 0 | * We should really have data to process, or we should be trying |
388 | 0 | * to finish/flush the last block. (This is an overly paranoid |
389 | 0 | * check since all callers are in this file and simple inspection |
390 | 0 | * proves they do it right. But it could find a bug in future |
391 | 0 | * modifications/development, that is why it is here.) |
392 | 0 | */ |
393 | 0 | PORT_Assert((data != NULL && len) || final); |
394 | 0 |
|
395 | 0 | /* we got data (either from the caller, or from a lower level encoder) */ |
396 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
397 | 0 | if (!cinfo) { |
398 | 0 | /* The original programmer didn't expect this to happen */ |
399 | 0 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
400 | 0 | return SECFailure; |
401 | 0 | } |
402 | 0 | |
403 | 0 | /* Update the running digest. */ |
404 | 0 | if (len && cinfo->privateInfo && cinfo->privateInfo->digcx != NULL) |
405 | 0 | NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); |
406 | 0 |
|
407 | 0 | /* Encrypt this chunk. */ |
408 | 0 | if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { |
409 | 0 | unsigned int inlen; /* length of data being encrypted */ |
410 | 0 | unsigned int outlen; /* length of encrypted data */ |
411 | 0 | unsigned int buflen; /* length available for encrypted data */ |
412 | 0 |
|
413 | 0 | inlen = len; |
414 | 0 | buflen = NSS_CMSCipherContext_EncryptLength(cinfo->privateInfo->ciphcx, |
415 | 0 | inlen, final); |
416 | 0 | if (buflen == 0) { |
417 | 0 | /* |
418 | 0 | * No output is expected, but the input data may be buffered |
419 | 0 | * so we still have to call Encrypt. |
420 | 0 | */ |
421 | 0 | rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, NULL, NULL, 0, |
422 | 0 | data, inlen, final); |
423 | 0 | if (final) { |
424 | 0 | len = 0; |
425 | 0 | goto done; |
426 | 0 | } |
427 | 0 | return rv; |
428 | 0 | } |
429 | 0 | |
430 | 0 | if (dest != NULL) |
431 | 0 | buf = (unsigned char *)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); |
432 | 0 | else |
433 | 0 | buf = (unsigned char *)PORT_Alloc(buflen); |
434 | 0 |
|
435 | 0 | if (buf == NULL) { |
436 | 0 | rv = SECFailure; |
437 | 0 | } else { |
438 | 0 | rv = NSS_CMSCipherContext_Encrypt(cinfo->privateInfo->ciphcx, buf, |
439 | 0 | &outlen, buflen, |
440 | 0 | data, inlen, final); |
441 | 0 | data = buf; |
442 | 0 | len = outlen; |
443 | 0 | } |
444 | 0 | if (rv != SECSuccess) |
445 | 0 | /* encryption or malloc failed? */ |
446 | 0 | return rv; |
447 | 0 | } |
448 | 0 | |
449 | 0 | /* |
450 | 0 | * at this point (data,len) has everything we'd like to give to the CURRENT encoder |
451 | 0 | * (which will encode it, then hand it back to the user or the parent encoder) |
452 | 0 | * We don't encode the data if we're innermost and we're told not to include the data |
453 | 0 | */ |
454 | 0 | if (p7ecx->ecx != NULL && len && |
455 | 0 | (!innermost || cinfo->rawContent != cinfo->content.pointer)) |
456 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); |
457 | 0 |
|
458 | 0 | done: |
459 | 0 |
|
460 | 0 | if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { |
461 | 0 | if (dest != NULL) { |
462 | 0 | dest->data = buf; |
463 | 0 | dest->len = len; |
464 | 0 | } else if (buf != NULL) { |
465 | 0 | PORT_Free(buf); |
466 | 0 | } |
467 | 0 | } |
468 | 0 | return rv; |
469 | 0 | } |
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 | 0 | { |
479 | 0 | /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ |
480 | 0 | return nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, |
481 | 0 | len, PR_FALSE, PR_FALSE); |
482 | 0 | } |
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 | 0 | { |
504 | 0 | NSSCMSEncoderContext *p7ecx; |
505 | 0 | SECStatus rv; |
506 | 0 | NSSCMSContentInfo *cinfo; |
507 | 0 | SECOidTag tag; |
508 | 0 |
|
509 | 0 | NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, |
510 | 0 | detached_digestalgs, detached_digests); |
511 | 0 |
|
512 | 0 | p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext)); |
513 | 0 | if (p7ecx == NULL) { |
514 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
515 | 0 | return NULL; |
516 | 0 | } |
517 | 0 |
|
518 | 0 | p7ecx->cmsg = cmsg; |
519 | 0 | p7ecx->output.outputfn = outputfn; |
520 | 0 | p7ecx->output.outputarg = outputarg; |
521 | 0 | p7ecx->output.dest = dest; |
522 | 0 | p7ecx->output.destpoolp = destpoolp; |
523 | 0 | p7ecx->type = SEC_OID_UNKNOWN; |
524 | 0 |
|
525 | 0 | cinfo = NSS_CMSMessage_GetContentInfo(cmsg); |
526 | 0 |
|
527 | 0 | tag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
528 | 0 | switch (tag) { |
529 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
530 | 0 | rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData); |
531 | 0 | break; |
532 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
533 | 0 | rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData); |
534 | 0 | break; |
535 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
536 | 0 | rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData); |
537 | 0 | break; |
538 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
539 | 0 | rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData); |
540 | 0 | break; |
541 | 0 | default: |
542 | 0 | if (NSS_CMSType_IsWrapper(tag)) { |
543 | 0 | rv = NSS_CMSGenericWrapperData_Encode_BeforeStart(tag, |
544 | 0 | p7ecx->content.genericData); |
545 | 0 | } else { |
546 | 0 | rv = SECFailure; |
547 | 0 | } |
548 | 0 | break; |
549 | 0 | } |
550 | 0 | if (rv != SECSuccess) { |
551 | 0 | PORT_Free(p7ecx); |
552 | 0 | return NULL; |
553 | 0 | } |
554 | 0 |
|
555 | 0 | /* Initialize the BER encoder. |
556 | 0 | * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ |
557 | 0 | p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate, |
558 | 0 | nss_cms_encoder_out, &(p7ecx->output)); |
559 | 0 | if (p7ecx->ecx == NULL) { |
560 | 0 | PORT_Free(p7ecx); |
561 | 0 | return NULL; |
562 | 0 | } |
563 | 0 | p7ecx->ecxupdated = PR_FALSE; |
564 | 0 |
|
565 | 0 | /* |
566 | 0 | * Indicate that we are streaming. We will be streaming until we |
567 | 0 | * get past the contents bytes. |
568 | 0 | */ |
569 | 0 | if (!cinfo->privateInfo || !cinfo->privateInfo->dontStream) |
570 | 0 | SEC_ASN1EncoderSetStreaming(p7ecx->ecx); |
571 | 0 |
|
572 | 0 | /* |
573 | 0 | * The notify function will watch for the contents field. |
574 | 0 | */ |
575 | 0 | SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); |
576 | 0 |
|
577 | 0 | /* this will kick off the encoding process & encode everything up to the content bytes, |
578 | 0 | * at which point the notify function sets streaming mode (and possibly creates |
579 | 0 | * a child encoder). */ |
580 | 0 | p7ecx->ecxupdated = PR_TRUE; |
581 | 0 | if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { |
582 | 0 | PORT_Free(p7ecx); |
583 | 0 | return NULL; |
584 | 0 | } |
585 | 0 |
|
586 | 0 | return p7ecx; |
587 | 0 | } |
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 | 0 | { |
602 | 0 | SECStatus rv; |
603 | 0 | NSSCMSContentInfo *cinfo; |
604 | 0 | SECOidTag childtype; |
605 | 0 |
|
606 | 0 | if (p7ecx->error) |
607 | 0 | return SECFailure; |
608 | 0 | |
609 | 0 | /* hand data to the innermost decoder */ |
610 | 0 | if (p7ecx->childp7ecx) { |
611 | 0 | /* tell the child to start encoding, up to its first data byte, if it |
612 | 0 | * hasn't started yet */ |
613 | 0 | if (!p7ecx->childp7ecx->ecxupdated) { |
614 | 0 | p7ecx->childp7ecx->ecxupdated = PR_TRUE; |
615 | 0 | if (SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0) != SECSuccess) |
616 | 0 | return SECFailure; |
617 | 0 | } |
618 | 0 | /* recursion here */ |
619 | 0 | rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len); |
620 | 0 | } else { |
621 | 0 | /* we are at innermost decoder */ |
622 | 0 | /* find out about our inner content type - must be data */ |
623 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
624 | 0 | if (!cinfo) { |
625 | 0 | /* The original programmer didn't expect this to happen */ |
626 | 0 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
627 | 0 | return SECFailure; |
628 | 0 | } |
629 | 0 | |
630 | 0 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
631 | 0 | if (!NSS_CMSType_IsData(childtype)) |
632 | 0 | return SECFailure; |
633 | 0 | /* and we must not have preset data */ |
634 | 0 | if (cinfo->content.data != NULL) |
635 | 0 | return SECFailure; |
636 | 0 | |
637 | 0 | /* hand it the data so it can encode it (let DER trickle up the chain) */ |
638 | 0 | rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, |
639 | 0 | len, PR_FALSE, PR_TRUE); |
640 | 0 | } |
641 | 0 | return rv; |
642 | 0 | } |
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 | 0 | { |
652 | 0 | SECStatus rv = SECFailure; |
653 | 0 |
|
654 | 0 | /* XXX do this right! */ |
655 | 0 |
|
656 | 0 | /* |
657 | 0 | * Finish any inner decoders before us so that all the encoded data is flushed |
658 | 0 | * This basically finishes all the decoders from the innermost to the outermost. |
659 | 0 | * Finishing an inner decoder may result in data being updated to the outer decoder |
660 | 0 | * while we are already in NSS_CMSEncoder_Finish, but that's allright. |
661 | 0 | */ |
662 | 0 | if (p7ecx->childp7ecx) { |
663 | 0 | rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
664 | 0 | /* remember rv for now */ |
665 | | #ifdef CMSDEBUG |
666 | | if (rv != SECSuccess) { |
667 | | fprintf(stderr, "Fail to cancel inner encoder\n"); |
668 | | } |
669 | | #endif |
670 | | } |
671 | 0 |
|
672 | 0 | /* |
673 | 0 | * On the way back up, there will be no more data (if we had an |
674 | 0 | * inner encoder, it is done now!) |
675 | 0 | * Flush out any remaining data and/or finish digests. |
676 | 0 | */ |
677 | 0 | rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); |
678 | 0 | if (rv != SECSuccess) |
679 | 0 | goto loser; |
680 | 0 | |
681 | 0 | p7ecx->childp7ecx = NULL; |
682 | 0 |
|
683 | 0 | /* kick the encoder back into working mode again. |
684 | 0 | * We turn off streaming stuff (which will cause the encoder to continue |
685 | 0 | * encoding happily, now that we have all the data (like digests) ready for it). |
686 | 0 | */ |
687 | 0 | SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); |
688 | 0 | SEC_ASN1EncoderClearStreaming(p7ecx->ecx); |
689 | 0 |
|
690 | 0 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ |
691 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); |
692 | 0 |
|
693 | 0 | loser: |
694 | 0 | SEC_ASN1EncoderFinish(p7ecx->ecx); |
695 | 0 | PORT_Free(p7ecx); |
696 | 0 | return rv; |
697 | 0 | } |
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 | 0 | { |
707 | 0 | SECStatus rv = SECFailure; |
708 | 0 | NSSCMSContentInfo *cinfo; |
709 | 0 |
|
710 | 0 | /* |
711 | 0 | * Finish any inner decoders before us so that all the encoded data is flushed |
712 | 0 | * This basically finishes all the decoders from the innermost to the outermost. |
713 | 0 | * Finishing an inner decoder may result in data being updated to the outer decoder |
714 | 0 | * while we are already in NSS_CMSEncoder_Finish, but that's allright. |
715 | 0 | */ |
716 | 0 | if (p7ecx->childp7ecx) { |
717 | 0 | /* tell the child to start encoding, up to its first data byte, if it |
718 | 0 | * hasn't yet */ |
719 | 0 | if (!p7ecx->childp7ecx->ecxupdated) { |
720 | 0 | p7ecx->childp7ecx->ecxupdated = PR_TRUE; |
721 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->childp7ecx->ecx, NULL, 0); |
722 | 0 | if (rv != SECSuccess) { |
723 | 0 | NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
724 | 0 | goto loser; |
725 | 0 | } |
726 | 0 | } |
727 | 0 | rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ |
728 | 0 | if (rv != SECSuccess) |
729 | 0 | goto loser; |
730 | 0 | } |
731 | 0 | |
732 | 0 | /* |
733 | 0 | * On the way back up, there will be no more data (if we had an |
734 | 0 | * inner encoder, it is done now!) |
735 | 0 | * Flush out any remaining data and/or finish digests. |
736 | 0 | */ |
737 | 0 | rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); |
738 | 0 | if (rv != SECSuccess) |
739 | 0 | goto loser; |
740 | 0 | |
741 | 0 | p7ecx->childp7ecx = NULL; |
742 | 0 |
|
743 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type); |
744 | 0 | if (!cinfo) { |
745 | 0 | /* The original programmer didn't expect this to happen */ |
746 | 0 | p7ecx->error = SEC_ERROR_LIBRARY_FAILURE; |
747 | 0 | rv = SECFailure; |
748 | 0 | goto loser; |
749 | 0 | } |
750 | 0 | SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); |
751 | 0 | SEC_ASN1EncoderClearStreaming(p7ecx->ecx); |
752 | 0 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ |
753 | 0 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); |
754 | 0 |
|
755 | 0 | if (p7ecx->error) |
756 | 0 | rv = SECFailure; |
757 | 0 |
|
758 | 0 | loser: |
759 | 0 | SEC_ASN1EncoderFinish(p7ecx->ecx); |
760 | 0 | PORT_Free(p7ecx); |
761 | 0 | return rv; |
762 | 0 | } |