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