]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * The contents of this file are subject to the Mozilla Public | |
3 | * License Version 1.1 (the "License"); you may not use this file | |
4 | * except in compliance with the License. You may obtain a copy of | |
5 | * the License at http://www.mozilla.org/MPL/ | |
6 | * | |
7 | * Software distributed under the License is distributed on an "AS | |
8 | * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or | |
9 | * implied. See the License for the specific language governing | |
10 | * rights and limitations under the License. | |
11 | * | |
12 | * The Original Code is the Netscape security libraries. | |
13 | * | |
14 | * The Initial Developer of the Original Code is Netscape | |
15 | * Communications Corporation. Portions created by Netscape are | |
16 | * Copyright (C) 1994-2000 Netscape Communications Corporation. All | |
17 | * Rights Reserved. | |
18 | * | |
19 | * Contributor(s): | |
20 | * | |
21 | * Alternatively, the contents of this file may be used under the | |
22 | * terms of the GNU General Public License Version 2 or later (the | |
23 | * "GPL"), in which case the provisions of the GPL are applicable | |
24 | * instead of those above. If you wish to allow use of your | |
25 | * version of this file only under the terms of the GPL and not to | |
26 | * allow others to use your version of this file under the MPL, | |
27 | * indicate your decision by deleting the provisions above and | |
28 | * replace them with the notice and other provisions required by | |
29 | * the GPL. If you do not delete the provisions above, a recipient | |
30 | * may use your version of this file under either the MPL or the | |
31 | * GPL. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * CMS encoding. | |
36 | */ | |
37 | ||
38 | #include <Security/SecCmsEncoder.h> | |
39 | #include <Security/SecCmsContentInfo.h> | |
40 | #include <Security/SecCmsDigestContext.h> | |
41 | #include <Security/SecCmsMessage.h> | |
42 | ||
43 | #include "cmslocal.h" | |
44 | ||
45 | #include "secoid.h" | |
d8f41ccd | 46 | #include "SecAsn1Item.h" |
b1ab9ed8 A |
47 | |
48 | #include <security_asn1/secasn1.h> | |
49 | #include <security_asn1/secerr.h> | |
d8f41ccd A |
50 | #include <security_asn1/secport.h> |
51 | ||
52 | #include <Security/SecBase.h> | |
53 | ||
54 | #include <limits.h> | |
b1ab9ed8 A |
55 | |
56 | struct nss_cms_encoder_output { | |
57 | SecCmsContentCallback outputfn; | |
58 | void *outputarg; | |
d8f41ccd | 59 | CFMutableDataRef berData; |
b1ab9ed8 A |
60 | }; |
61 | ||
62 | struct SecCmsEncoderStr { | |
63 | SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */ | |
64 | Boolean ecxupdated; /* true if data was handed in */ | |
65 | SecCmsMessageRef cmsg; /* pointer to the root message */ | |
66 | SECOidTag type; /* type tag of the current content */ | |
67 | SecCmsContent content; /* pointer to current content */ | |
68 | struct nss_cms_encoder_output output; /* output function */ | |
69 | int error; /* error code */ | |
70 | SecCmsEncoderRef childp7ecx; /* link to child encoder context */ | |
71 | }; | |
72 | ||
73 | static OSStatus nss_cms_before_data(SecCmsEncoderRef p7ecx); | |
74 | static OSStatus nss_cms_after_data(SecCmsEncoderRef p7ecx); | |
d8f41ccd A |
75 | static void nss_cms_encoder_update(void *arg, const char *data, size_t len); |
76 | static OSStatus nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, SecAsn1Item * dest, | |
b1ab9ed8 A |
77 | const unsigned char *data, size_t len, |
78 | Boolean final, Boolean innermost); | |
79 | ||
80 | extern const SecAsn1Template SecCmsMessageTemplate[]; | |
81 | ||
82 | /* | |
83 | * The little output function that the ASN.1 encoder calls to hand | |
84 | * us bytes which we in turn hand back to our caller (via the callback | |
85 | * they gave us). | |
86 | */ | |
87 | static void | |
88 | nss_cms_encoder_out(void *arg, const char *buf, size_t len, | |
89 | int depth, SEC_ASN1EncodingPart data_kind) | |
90 | { | |
91 | struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg; | |
b1ab9ed8 A |
92 | |
93 | #ifdef CMSDEBUG | |
d8f41ccd | 94 | size_t i; |
b1ab9ed8 | 95 | |
d8f41ccd | 96 | fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len); |
b1ab9ed8 A |
97 | for (i=0; i < len; i++) { |
98 | fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : ""); | |
99 | } | |
100 | if ((i % 16) != 0) | |
101 | fprintf(stderr, "\n"); | |
102 | #endif | |
103 | ||
104 | if (output->outputfn != NULL) | |
105 | /* call output callback with DER data */ | |
106 | output->outputfn(output->outputarg, buf, len); | |
107 | ||
d8f41ccd A |
108 | if (output->berData != NULL) { |
109 | /* store DER data in output->dest */ | |
110 | CFDataAppendBytes(output->berData, (const UInt8 *)buf, len); | |
b1ab9ed8 A |
111 | } |
112 | } | |
113 | ||
114 | /* | |
115 | * nss_cms_encoder_notify - ASN.1 encoder callback | |
116 | * | |
117 | * this function is called by the ASN.1 encoder before and after the encoding of | |
118 | * every object. here, it is used to keep track of data structures, set up | |
119 | * encryption and/or digesting and possibly set up child encoders. | |
120 | */ | |
121 | static void | |
122 | nss_cms_encoder_notify(void *arg, Boolean before, void *dest, int depth) | |
123 | { | |
124 | SecCmsEncoderRef p7ecx; | |
125 | SecCmsContentInfoRef rootcinfo, cinfo; | |
126 | Boolean after = !before; | |
b1ab9ed8 | 127 | SECOidTag childtype; |
d8f41ccd | 128 | SecAsn1Item * item; |
b1ab9ed8 A |
129 | |
130 | p7ecx = (SecCmsEncoderRef)arg; | |
131 | PORT_Assert(p7ecx != NULL); | |
132 | ||
133 | rootcinfo = &(p7ecx->cmsg->contentInfo); | |
b1ab9ed8 A |
134 | |
135 | #ifdef CMSDEBUG | |
d8f41ccd | 136 | fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth); |
b1ab9ed8 A |
137 | #endif |
138 | ||
139 | /* | |
140 | * Watch for the content field, at which point we want to instruct | |
141 | * the ASN.1 encoder to start taking bytes from the buffer. | |
142 | */ | |
143 | switch (p7ecx->type) { | |
144 | default: | |
145 | case SEC_OID_UNKNOWN: | |
146 | /* we're still in the root message */ | |
147 | if (after && dest == &(rootcinfo->contentType)) { | |
148 | /* got the content type OID now - so find out the type tag */ | |
149 | p7ecx->type = SecCmsContentInfoGetContentTypeTag(rootcinfo); | |
150 | /* set up a pointer to our current content */ | |
151 | p7ecx->content = rootcinfo->content; | |
152 | } | |
153 | break; | |
154 | ||
155 | case SEC_OID_PKCS7_DATA: | |
b1ab9ed8 A |
156 | if (before && dest == &(rootcinfo->rawContent)) { |
157 | /* just set up encoder to grab from user - no encryption or digesting */ | |
158 | if ((item = rootcinfo->content.data) != NULL) | |
159 | (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE); | |
160 | else | |
161 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); | |
162 | SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ | |
163 | } | |
164 | break; | |
165 | ||
166 | case SEC_OID_PKCS7_SIGNED_DATA: | |
167 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
168 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
169 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
170 | ||
171 | /* when we know what the content is, we encode happily until we reach the inner content */ | |
172 | cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); | |
173 | childtype = SecCmsContentInfoGetContentTypeTag(cinfo); | |
174 | ||
175 | if (after && dest == &(cinfo->contentType)) { | |
176 | /* we're right before encoding the data (if we have some or not) */ | |
177 | /* (for encrypted data, we're right before the contentEncAlg which may change */ | |
178 | /* in nss_cms_before_data because of IV calculation when setting up encryption) */ | |
ecaf5866 | 179 | if (nss_cms_before_data(p7ecx) != SECSuccess) { |
b1ab9ed8 | 180 | p7ecx->error = PORT_GetError(); |
ecaf5866 A |
181 | PORT_SetError(0); // Clean the thread error since we've returned the error |
182 | } | |
b1ab9ed8 A |
183 | } |
184 | if (before && dest == &(cinfo->rawContent)) { | |
d8f41ccd | 185 | if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL) |
b1ab9ed8 A |
186 | /* we have data - feed it in */ |
187 | (void)nss_cms_encoder_work_data(p7ecx, NULL, item->Data, item->Length, PR_TRUE, PR_TRUE); | |
188 | else | |
189 | /* else try to get it from user */ | |
190 | SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx); | |
191 | } | |
192 | if (after && dest == &(cinfo->rawContent)) { | |
ecaf5866 | 193 | if (nss_cms_after_data(p7ecx) != SECSuccess) { |
b1ab9ed8 | 194 | p7ecx->error = PORT_GetError(); |
ecaf5866 A |
195 | PORT_SetError(0); // Clean the thread error since we've returned the error |
196 | } | |
b1ab9ed8 A |
197 | SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */ |
198 | } | |
199 | break; | |
200 | } | |
201 | } | |
202 | ||
203 | /* | |
204 | * nss_cms_before_data - setup the current encoder to receive data | |
205 | */ | |
206 | static OSStatus | |
207 | nss_cms_before_data(SecCmsEncoderRef p7ecx) | |
208 | { | |
209 | OSStatus rv; | |
210 | SECOidTag childtype; | |
211 | SecCmsContentInfoRef cinfo; | |
b1ab9ed8 A |
212 | SecCmsEncoderRef childp7ecx; |
213 | const SecAsn1Template *template; | |
214 | ||
b1ab9ed8 A |
215 | /* call _Encode_BeforeData handlers */ |
216 | switch (p7ecx->type) { | |
217 | case SEC_OID_PKCS7_SIGNED_DATA: | |
218 | /* we're encoding a signedData, so set up the digests */ | |
219 | rv = SecCmsSignedDataEncodeBeforeData(p7ecx->content.signedData); | |
220 | break; | |
221 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
222 | /* we're encoding a digestedData, so set up the digest */ | |
223 | rv = SecCmsDigestedDataEncodeBeforeData(p7ecx->content.digestedData); | |
224 | break; | |
225 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
226 | rv = SecCmsEnvelopedDataEncodeBeforeData(p7ecx->content.envelopedData); | |
227 | break; | |
228 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
229 | rv = SecCmsEncryptedDataEncodeBeforeData(p7ecx->content.encryptedData); | |
230 | break; | |
231 | default: | |
232 | rv = SECFailure; | |
233 | } | |
234 | if (rv != SECSuccess) | |
235 | return SECFailure; | |
236 | ||
237 | /* ok, now we have a pointer to cinfo */ | |
238 | /* find out what kind of data is encapsulated */ | |
239 | ||
240 | cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); | |
241 | childtype = SecCmsContentInfoGetContentTypeTag(cinfo); | |
242 | ||
243 | switch (childtype) { | |
244 | case SEC_OID_PKCS7_SIGNED_DATA: | |
245 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
246 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
247 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
248 | #if 0 | |
249 | case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */ | |
250 | #endif | |
251 | /* in these cases, we need to set up a child encoder! */ | |
252 | /* create new encoder context */ | |
253 | childp7ecx = PORT_ZAlloc(sizeof(struct SecCmsEncoderStr)); | |
254 | if (childp7ecx == NULL) | |
255 | return SECFailure; | |
256 | ||
257 | /* the CHILD encoder needs to hand its encoded data to the CURRENT encoder | |
258 | * (which will encrypt and/or digest it) | |
259 | * this needs to route back into our update function | |
260 | * which finds the lowest encoding context & encrypts and computes digests */ | |
261 | childp7ecx->type = childtype; | |
262 | childp7ecx->content = cinfo->content; | |
263 | /* use the non-recursive update function here, of course */ | |
d8f41ccd | 264 | childp7ecx->output.outputfn = nss_cms_encoder_update; |
b1ab9ed8 | 265 | childp7ecx->output.outputarg = p7ecx; |
d8f41ccd | 266 | childp7ecx->output.berData = NULL; |
b1ab9ed8 A |
267 | childp7ecx->cmsg = p7ecx->cmsg; |
268 | ||
269 | template = SecCmsUtilGetTemplateByTypeTag(childtype); | |
270 | if (template == NULL) | |
271 | goto loser; /* cannot happen */ | |
272 | ||
273 | /* now initialize the data for encoding the first third */ | |
274 | switch (childp7ecx->type) { | |
275 | case SEC_OID_PKCS7_SIGNED_DATA: | |
276 | rv = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData); | |
277 | break; | |
278 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
279 | rv = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData); | |
280 | break; | |
281 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
282 | rv = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData); | |
283 | break; | |
284 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
285 | rv = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData); | |
286 | break; | |
287 | case SEC_OID_PKCS7_DATA: | |
b1ab9ed8 A |
288 | rv = SECSuccess; |
289 | break; | |
290 | default: | |
291 | PORT_Assert(0); | |
292 | break; | |
293 | } | |
294 | if (rv != SECSuccess) | |
295 | goto loser; | |
296 | ||
297 | /* | |
298 | * Initialize the BER encoder. | |
299 | */ | |
300 | childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template, | |
301 | nss_cms_encoder_out, &(childp7ecx->output)); | |
302 | if (childp7ecx->ecx == NULL) | |
303 | goto loser; | |
304 | ||
305 | childp7ecx->ecxupdated = PR_FALSE; | |
306 | ||
307 | /* | |
308 | * Indicate that we are streaming. We will be streaming until we | |
309 | * get past the contents bytes. | |
310 | */ | |
311 | SEC_ASN1EncoderSetStreaming(childp7ecx->ecx); | |
312 | ||
313 | /* | |
314 | * The notify function will watch for the contents field. | |
315 | */ | |
316 | SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx); | |
317 | ||
318 | /* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */ | |
319 | /* encoding process - we'll do that from the update function instead */ | |
320 | /* otherwise we'd be encoding data from a call of the notify function of the */ | |
321 | /* parent encoder (which would not work) */ | |
322 | ||
323 | /* this will kick off the encoding process & encode everything up to the content bytes, | |
324 | * at which point the notify function sets streaming mode (and possibly creates | |
325 | * another child encoder). */ | |
326 | if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess) | |
327 | goto loser; | |
328 | ||
329 | p7ecx->childp7ecx = childp7ecx; | |
330 | break; | |
331 | ||
332 | case SEC_OID_PKCS7_DATA: | |
b1ab9ed8 A |
333 | p7ecx->childp7ecx = NULL; |
334 | break; | |
335 | default: | |
336 | /* we do not know this type */ | |
337 | p7ecx->error = SEC_ERROR_BAD_DER; | |
338 | break; | |
339 | } | |
340 | ||
341 | return SECSuccess; | |
342 | ||
343 | loser: | |
344 | if (childp7ecx) { | |
345 | if (childp7ecx->ecx) | |
346 | SEC_ASN1EncoderFinish(childp7ecx->ecx); | |
347 | PORT_Free(childp7ecx); | |
348 | } | |
349 | return SECFailure; | |
350 | } | |
351 | ||
352 | static OSStatus | |
353 | nss_cms_after_data(SecCmsEncoderRef p7ecx) | |
354 | { | |
355 | OSStatus rv = SECFailure; | |
356 | ||
357 | switch (p7ecx->type) { | |
358 | case SEC_OID_PKCS7_SIGNED_DATA: | |
359 | /* this will finish the digests and sign */ | |
360 | rv = SecCmsSignedDataEncodeAfterData(p7ecx->content.signedData); | |
361 | break; | |
362 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
363 | rv = SecCmsEnvelopedDataEncodeAfterData(p7ecx->content.envelopedData); | |
364 | break; | |
365 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
366 | rv = SecCmsDigestedDataEncodeAfterData(p7ecx->content.digestedData); | |
367 | break; | |
368 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
369 | rv = SecCmsEncryptedDataEncodeAfterData(p7ecx->content.encryptedData); | |
370 | break; | |
371 | case SEC_OID_PKCS7_DATA: | |
b1ab9ed8 A |
372 | /* do nothing */ |
373 | break; | |
374 | default: | |
375 | rv = SECFailure; | |
376 | break; | |
377 | } | |
378 | return rv; | |
379 | } | |
380 | ||
381 | /* | |
382 | * nss_cms_encoder_work_data - process incoming data | |
383 | * | |
384 | * (from the user or the next encoding layer) | |
385 | * Here, we need to digest and/or encrypt, then pass it on | |
d8f41ccd | 386 | * |
b1ab9ed8 A |
387 | */ |
388 | static OSStatus | |
d8f41ccd | 389 | nss_cms_encoder_work_data(SecCmsEncoderRef p7ecx, SecAsn1Item * dest, |
b1ab9ed8 A |
390 | const unsigned char *data, size_t len, |
391 | Boolean final, Boolean innermost) | |
392 | { | |
393 | unsigned char *buf = NULL; | |
394 | OSStatus rv; | |
395 | SecCmsContentInfoRef cinfo; | |
396 | ||
397 | rv = SECSuccess; /* may as well be optimistic */ | |
398 | ||
399 | /* | |
400 | * We should really have data to process, or we should be trying | |
401 | * to finish/flush the last block. (This is an overly paranoid | |
402 | * check since all callers are in this file and simple inspection | |
403 | * proves they do it right. But it could find a bug in future | |
404 | * modifications/development, that is why it is here.) | |
405 | */ | |
406 | PORT_Assert ((data != NULL && len) || final); | |
d8f41ccd | 407 | PORT_Assert (len < UINT_MAX); /* overflow check for later cast */ |
b1ab9ed8 A |
408 | |
409 | /* we got data (either from the caller, or from a lower level encoder) */ | |
410 | cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); | |
411 | ||
412 | /* Update the running digest. */ | |
413 | if (len && cinfo->digcx != NULL) | |
414 | SecCmsDigestContextUpdate(cinfo->digcx, data, len); | |
415 | ||
416 | /* Encrypt this chunk. */ | |
417 | if (cinfo->ciphcx != NULL) { | |
d8f41ccd | 418 | unsigned int inlen; /* length of data being encrypted */ |
60c433a9 | 419 | unsigned int outlen = 0; /* length of encrypted data */ |
d8f41ccd A |
420 | unsigned int buflen; /* length available for encrypted data */ |
421 | ||
422 | /* 64 bits cast: only an issue if unsigned int is smaller than size_t. | |
423 | Worst case is you will truncate a CMS blob bigger than 4GB when | |
424 | encrypting */ | |
425 | inlen = (unsigned int)len; | |
b1ab9ed8 | 426 | |
b1ab9ed8 A |
427 | buflen = SecCmsCipherContextEncryptLength(cinfo->ciphcx, inlen, final); |
428 | if (buflen == 0) { | |
429 | /* | |
430 | * No output is expected, but the input data may be buffered | |
431 | * so we still have to call Encrypt. | |
432 | */ | |
433 | rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, NULL, NULL, 0, | |
434 | data, inlen, final); | |
435 | if (final) { | |
436 | len = 0; | |
437 | goto done; | |
438 | } | |
439 | return rv; | |
440 | } | |
441 | ||
442 | if (dest != NULL) | |
443 | buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen); | |
444 | else | |
445 | buf = (unsigned char*)PORT_Alloc(buflen); | |
446 | ||
447 | if (buf == NULL) { | |
448 | rv = SECFailure; | |
449 | } else { | |
450 | rv = SecCmsCipherContextEncrypt(cinfo->ciphcx, buf, &outlen, buflen, | |
451 | data, inlen, final); | |
452 | data = buf; | |
453 | len = outlen; | |
454 | } | |
455 | if (rv != SECSuccess) | |
456 | /* encryption or malloc failed? */ | |
457 | return rv; | |
458 | } | |
459 | ||
460 | ||
461 | /* | |
462 | * at this point (data,len) has everything we'd like to give to the CURRENT encoder | |
463 | * (which will encode it, then hand it back to the user or the parent encoder) | |
464 | * We don't encode the data if we're innermost and we're told not to include the data | |
465 | */ | |
466 | if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL)) | |
467 | rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len); | |
468 | ||
469 | done: | |
470 | ||
471 | if (cinfo->ciphcx != NULL) { | |
472 | if (dest != NULL) { | |
473 | dest->Data = buf; | |
474 | dest->Length = len; | |
475 | } else if (buf != NULL) { | |
476 | PORT_Free (buf); | |
477 | } | |
478 | } | |
479 | return rv; | |
480 | } | |
481 | ||
482 | /* | |
483 | * nss_cms_encoder_update - deliver encoded data to the next higher level | |
484 | * | |
485 | * no recursion here because we REALLY want to end up at the next higher encoder! | |
486 | */ | |
d8f41ccd A |
487 | static void |
488 | nss_cms_encoder_update(void *arg, const char *data, size_t len) | |
b1ab9ed8 A |
489 | { |
490 | /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ | |
d8f41ccd A |
491 | SecCmsEncoderRef p7ecx = (SecCmsEncoderRef)arg; |
492 | ||
493 | (void)nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE); | |
b1ab9ed8 A |
494 | } |
495 | ||
496 | /* | |
497 | * SecCmsEncoderCreate - set up encoding of a CMS message | |
498 | * | |
499 | * "cmsg" - message to encode | |
500 | * "outputfn", "outputarg" - callback function for delivery of DER-encoded output | |
501 | * will not be called if NULL. | |
d8f41ccd | 502 | * "dest" - if non-NULL, pointer to SecAsn1Item that will hold the DER-encoded output |
b1ab9ed8 A |
503 | * "destpoolp" - pool to allocate DER-encoded output in |
504 | * "pwfn", pwfn_arg" - callback function for getting token password | |
505 | * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData | |
506 | * "detached_digestalgs", "detached_digests" - digests from detached content | |
507 | */ | |
508 | OSStatus | |
509 | SecCmsEncoderCreate(SecCmsMessageRef cmsg, | |
510 | SecCmsContentCallback outputfn, void *outputarg, | |
d8f41ccd | 511 | CFMutableDataRef outBer, |
b1ab9ed8 A |
512 | PK11PasswordFunc pwfn, void *pwfn_arg, |
513 | SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, | |
b1ab9ed8 A |
514 | SecCmsEncoderRef *outEncoder) |
515 | { | |
516 | SecCmsEncoderRef p7ecx; | |
517 | OSStatus result; | |
518 | SecCmsContentInfoRef cinfo; | |
519 | ||
ecaf5866 A |
520 | /* Clear the thread error to clean up dirty threads */ |
521 | PORT_SetError(0); | |
522 | ||
d8f41ccd | 523 | SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg); |
b1ab9ed8 A |
524 | |
525 | p7ecx = (SecCmsEncoderRef)PORT_ZAlloc(sizeof(struct SecCmsEncoderStr)); | |
526 | if (p7ecx == NULL) { | |
d8f41ccd | 527 | result = errSecAllocate; |
b1ab9ed8 A |
528 | goto loser; |
529 | } | |
530 | ||
531 | p7ecx->cmsg = cmsg; | |
532 | p7ecx->output.outputfn = outputfn; | |
533 | p7ecx->output.outputarg = outputarg; | |
d8f41ccd A |
534 | p7ecx->output.berData = outBer; |
535 | ||
b1ab9ed8 A |
536 | p7ecx->type = SEC_OID_UNKNOWN; |
537 | ||
538 | cinfo = SecCmsMessageGetContentInfo(cmsg); | |
539 | ||
540 | switch (SecCmsContentInfoGetContentTypeTag(cinfo)) { | |
541 | case SEC_OID_PKCS7_SIGNED_DATA: | |
542 | result = SecCmsSignedDataEncodeBeforeStart(cinfo->content.signedData); | |
543 | break; | |
544 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
545 | result = SecCmsEnvelopedDataEncodeBeforeStart(cinfo->content.envelopedData); | |
546 | break; | |
547 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
548 | result = SecCmsDigestedDataEncodeBeforeStart(cinfo->content.digestedData); | |
549 | break; | |
550 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
551 | result = SecCmsEncryptedDataEncodeBeforeStart(cinfo->content.encryptedData); | |
552 | break; | |
553 | default: | |
554 | /* @@@ We need a better error for unsupported message types. */ | |
d8f41ccd | 555 | result = errSecParam; |
b1ab9ed8 A |
556 | break; |
557 | } | |
866f8763 A |
558 | |
559 | if (result) { | |
560 | PORT_Free(p7ecx); | |
b1ab9ed8 | 561 | goto loser; |
866f8763 | 562 | } |
b1ab9ed8 A |
563 | |
564 | /* Initialize the BER encoder. | |
565 | * Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */ | |
566 | p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, SecCmsMessageTemplate, | |
567 | nss_cms_encoder_out, &(p7ecx->output)); | |
568 | if (p7ecx->ecx == NULL) { | |
569 | result = PORT_GetError(); | |
866f8763 | 570 | PORT_Free(p7ecx); |
ecaf5866 | 571 | PORT_SetError(0); // Clean the thread error since we've returned the error |
b1ab9ed8 A |
572 | goto loser; |
573 | } | |
574 | p7ecx->ecxupdated = PR_FALSE; | |
575 | ||
576 | /* | |
577 | * Indicate that we are streaming. We will be streaming until we | |
578 | * get past the contents bytes. | |
579 | */ | |
580 | SEC_ASN1EncoderSetStreaming(p7ecx->ecx); | |
581 | ||
582 | /* | |
583 | * The notify function will watch for the contents field. | |
584 | */ | |
585 | SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx); | |
586 | ||
587 | /* this will kick off the encoding process & encode everything up to the content bytes, | |
588 | * at which point the notify function sets streaming mode (and possibly creates | |
589 | * a child encoder). */ | |
590 | if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) { | |
591 | result = PORT_GetError(); | |
866f8763 | 592 | PORT_Free(p7ecx); |
ecaf5866 | 593 | PORT_SetError(0); // Clean the thread error since we've returned the error |
b1ab9ed8 A |
594 | goto loser; |
595 | } | |
596 | ||
597 | *outEncoder = p7ecx; | |
598 | loser: | |
599 | return result; | |
600 | } | |
601 | ||
602 | /* | |
603 | * SecCmsEncoderUpdate - take content data delivery from the user | |
604 | * | |
605 | * "p7ecx" - encoder context | |
606 | * "data" - content data | |
607 | * "len" - length of content data | |
608 | * | |
609 | * need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down), | |
610 | * then hand the data to the work_data fn | |
611 | */ | |
612 | OSStatus | |
613 | SecCmsEncoderUpdate(SecCmsEncoderRef p7ecx, const void *data, CFIndex len) | |
614 | { | |
615 | OSStatus result; | |
616 | SecCmsContentInfoRef cinfo; | |
617 | SECOidTag childtype; | |
618 | ||
6b200bc3 A |
619 | if (!p7ecx) { |
620 | return errSecParam; | |
621 | } | |
622 | ||
b1ab9ed8 A |
623 | if (p7ecx->error) |
624 | return p7ecx->error; | |
625 | ||
626 | /* hand data to the innermost decoder */ | |
627 | if (p7ecx->childp7ecx) { | |
628 | /* recursion here */ | |
629 | result = SecCmsEncoderUpdate(p7ecx->childp7ecx, data, len); | |
630 | } else { | |
631 | /* we are at innermost decoder */ | |
632 | /* find out about our inner content type - must be data */ | |
633 | cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); | |
634 | childtype = SecCmsContentInfoGetContentTypeTag(cinfo); | |
d8f41ccd A |
635 | if (childtype != SEC_OID_PKCS7_DATA) |
636 | return errSecParam; /* @@@ Maybe come up with a better error? */ | |
b1ab9ed8 A |
637 | /* and we must not have preset data */ |
638 | if (cinfo->content.data != NULL) | |
d8f41ccd | 639 | return errSecParam; /* @@@ Maybe come up with a better error? */ |
b1ab9ed8 A |
640 | |
641 | /* hand it the data so it can encode it (let DER trickle up the chain) */ | |
642 | result = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE); | |
ecaf5866 | 643 | if (result) { |
b1ab9ed8 | 644 | result = PORT_GetError(); |
ecaf5866 A |
645 | PORT_SetError(0); // Clean the thread error since we've returned the error |
646 | } | |
b1ab9ed8 A |
647 | } |
648 | return result; | |
649 | } | |
650 | ||
651 | /* | |
652 | * SecCmsEncoderDestroy - stop all encoding | |
653 | * | |
654 | * we need to walk down the chain of encoders and the finish them from the innermost out | |
655 | */ | |
656 | void | |
657 | SecCmsEncoderDestroy(SecCmsEncoderRef p7ecx) | |
658 | { | |
659 | /* XXX do this right! */ | |
660 | ||
661 | /* | |
662 | * Finish any inner decoders before us so that all the encoded data is flushed | |
663 | * This basically finishes all the decoders from the innermost to the outermost. | |
664 | * Finishing an inner decoder may result in data being updated to the outer decoder | |
665 | * while we are already in SecCmsEncoderFinish, but that's allright. | |
666 | */ | |
667 | if (p7ecx->childp7ecx) | |
668 | SecCmsEncoderDestroy(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ | |
669 | ||
670 | /* | |
671 | * On the way back up, there will be no more data (if we had an | |
672 | * inner encoder, it is done now!) | |
673 | * Flush out any remaining data and/or finish digests. | |
674 | */ | |
675 | if (nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL))) | |
676 | goto loser; | |
677 | ||
678 | p7ecx->childp7ecx = NULL; | |
679 | ||
680 | /* kick the encoder back into working mode again. | |
681 | * We turn off streaming stuff (which will cause the encoder to continue | |
682 | * encoding happily, now that we have all the data (like digests) ready for it). | |
683 | */ | |
684 | SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); | |
685 | SEC_ASN1EncoderClearStreaming(p7ecx->ecx); | |
686 | ||
687 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ | |
688 | SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); | |
689 | ||
690 | loser: | |
691 | SEC_ASN1EncoderFinish(p7ecx->ecx); | |
692 | PORT_Free (p7ecx); | |
693 | } | |
694 | ||
695 | /* | |
696 | * SecCmsEncoderFinish - signal the end of data | |
697 | * | |
698 | * we need to walk down the chain of encoders and the finish them from the innermost out | |
699 | */ | |
700 | OSStatus | |
701 | SecCmsEncoderFinish(SecCmsEncoderRef p7ecx) | |
702 | { | |
703 | OSStatus result; | |
704 | SecCmsContentInfoRef cinfo; | |
705 | SECOidTag childtype; | |
706 | ||
707 | /* | |
708 | * Finish any inner decoders before us so that all the encoded data is flushed | |
709 | * This basically finishes all the decoders from the innermost to the outermost. | |
710 | * Finishing an inner decoder may result in data being updated to the outer decoder | |
711 | * while we are already in SecCmsEncoderFinish, but that's allright. | |
712 | */ | |
713 | if (p7ecx->childp7ecx) { | |
714 | result = SecCmsEncoderFinish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */ | |
715 | if (result) | |
716 | goto loser; | |
717 | } | |
718 | ||
719 | /* | |
720 | * On the way back up, there will be no more data (if we had an | |
721 | * inner encoder, it is done now!) | |
722 | * Flush out any remaining data and/or finish digests. | |
723 | */ | |
724 | result = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL)); | |
725 | if (result) { | |
726 | result = PORT_GetError(); | |
727 | goto loser; | |
728 | } | |
729 | ||
730 | p7ecx->childp7ecx = NULL; | |
731 | ||
732 | /* find out about our inner content type - must be data */ | |
733 | cinfo = SecCmsContentGetContentInfo(p7ecx->content.pointer, p7ecx->type); | |
734 | childtype = SecCmsContentInfoGetContentTypeTag(cinfo); | |
d8f41ccd | 735 | if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) { |
b1ab9ed8 A |
736 | SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx); |
737 | /* now that TakeFromBuf is off, this will kick this encoder to finish encoding */ | |
738 | result = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0); | |
739 | if (result) | |
740 | result = PORT_GetError(); | |
741 | } | |
742 | ||
743 | SEC_ASN1EncoderClearStreaming(p7ecx->ecx); | |
744 | ||
745 | if (p7ecx->error && !result) | |
746 | result = p7ecx->error; | |
747 | ||
748 | loser: | |
749 | SEC_ASN1EncoderFinish(p7ecx->ecx); | |
750 | PORT_Free (p7ecx); | |
ecaf5866 | 751 | PORT_SetError(0); // Clean the thread error since we've returned the error |
b1ab9ed8 A |
752 | return result; |
753 | } | |
754 | ||
755 | OSStatus | |
d8f41ccd A |
756 | SecCmsMessageEncode(SecCmsMessageRef cmsg, const SecAsn1Item *input, |
757 | CFMutableDataRef outBer) | |
b1ab9ed8 | 758 | { |
d8f41ccd | 759 | SecCmsEncoderRef encoder = NULL; |
b1ab9ed8 A |
760 | OSStatus result; |
761 | ||
d8f41ccd A |
762 | if (!cmsg || !outBer) { |
763 | result = errSecParam; | |
b1ab9ed8 A |
764 | goto loser; |
765 | } | |
766 | ||
d8f41ccd | 767 | result = SecCmsEncoderCreate(cmsg, 0, 0, outBer, 0, 0, 0, 0, &encoder); |
b1ab9ed8 A |
768 | if (result) |
769 | goto loser; | |
770 | ||
771 | if (input) { | |
772 | result = SecCmsEncoderUpdate(encoder, input->Data, input->Length); | |
773 | if (result) { | |
774 | SecCmsEncoderDestroy(encoder); | |
775 | goto loser; | |
776 | } | |
777 | } | |
778 | result = SecCmsEncoderFinish(encoder); | |
779 | ||
780 | loser: | |
781 | return result; | |
782 | } |