|
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 envelopedData methods. |
|
7 */ |
|
8 |
|
9 #include "cmslocal.h" |
|
10 |
|
11 #include "cert.h" |
|
12 #include "key.h" |
|
13 #include "secasn1.h" |
|
14 #include "secitem.h" |
|
15 #include "secoid.h" |
|
16 #include "pk11func.h" |
|
17 #include "secerr.h" |
|
18 #include "secpkcs5.h" |
|
19 |
|
20 /* |
|
21 * NSS_CMSEnvelopedData_Create - create an enveloped data message |
|
22 */ |
|
23 NSSCMSEnvelopedData * |
|
24 NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize) |
|
25 { |
|
26 void *mark; |
|
27 NSSCMSEnvelopedData *envd; |
|
28 PLArenaPool *poolp; |
|
29 SECStatus rv; |
|
30 |
|
31 poolp = cmsg->poolp; |
|
32 |
|
33 mark = PORT_ArenaMark(poolp); |
|
34 |
|
35 envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData)); |
|
36 if (envd == NULL) |
|
37 goto loser; |
|
38 |
|
39 envd->cmsg = cmsg; |
|
40 |
|
41 /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */ |
|
42 |
|
43 rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize); |
|
44 if (rv != SECSuccess) |
|
45 goto loser; |
|
46 |
|
47 PORT_ArenaUnmark(poolp, mark); |
|
48 return envd; |
|
49 |
|
50 loser: |
|
51 PORT_ArenaRelease(poolp, mark); |
|
52 return NULL; |
|
53 } |
|
54 |
|
55 /* |
|
56 * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message |
|
57 */ |
|
58 void |
|
59 NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp) |
|
60 { |
|
61 NSSCMSRecipientInfo **recipientinfos; |
|
62 NSSCMSRecipientInfo *ri; |
|
63 |
|
64 if (edp == NULL) |
|
65 return; |
|
66 |
|
67 recipientinfos = edp->recipientInfos; |
|
68 if (recipientinfos == NULL) |
|
69 return; |
|
70 |
|
71 while ((ri = *recipientinfos++) != NULL) |
|
72 NSS_CMSRecipientInfo_Destroy(ri); |
|
73 |
|
74 NSS_CMSContentInfo_Destroy(&(edp->contentInfo)); |
|
75 |
|
76 } |
|
77 |
|
78 /* |
|
79 * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo |
|
80 */ |
|
81 NSSCMSContentInfo * |
|
82 NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd) |
|
83 { |
|
84 return &(envd->contentInfo); |
|
85 } |
|
86 |
|
87 /* |
|
88 * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg |
|
89 * |
|
90 * rip must be created on the same pool as edp - this is not enforced, though. |
|
91 */ |
|
92 SECStatus |
|
93 NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip) |
|
94 { |
|
95 void *mark; |
|
96 SECStatus rv; |
|
97 |
|
98 /* XXX compare pools, if not same, copy rip into edp's pool */ |
|
99 |
|
100 PR_ASSERT(edp != NULL); |
|
101 PR_ASSERT(rip != NULL); |
|
102 |
|
103 mark = PORT_ArenaMark(edp->cmsg->poolp); |
|
104 |
|
105 rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip); |
|
106 if (rv != SECSuccess) { |
|
107 PORT_ArenaRelease(edp->cmsg->poolp, mark); |
|
108 return SECFailure; |
|
109 } |
|
110 |
|
111 PORT_ArenaUnmark (edp->cmsg->poolp, mark); |
|
112 return SECSuccess; |
|
113 } |
|
114 |
|
115 /* |
|
116 * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding |
|
117 * |
|
118 * at this point, we need |
|
119 * - recipientinfos set up with recipient's certificates |
|
120 * - a content encryption algorithm (if none, 3DES will be used) |
|
121 * |
|
122 * this function will generate a random content encryption key (aka bulk key), |
|
123 * initialize the recipientinfos with certificate identification and wrap the bulk key |
|
124 * using the proper algorithm for every certificiate. |
|
125 * it will finally set the bulk algorithm and key so that the encode step can find it. |
|
126 */ |
|
127 SECStatus |
|
128 NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd) |
|
129 { |
|
130 int version; |
|
131 NSSCMSRecipientInfo **recipientinfos; |
|
132 NSSCMSContentInfo *cinfo; |
|
133 PK11SymKey *bulkkey = NULL; |
|
134 SECOidTag bulkalgtag; |
|
135 CK_MECHANISM_TYPE type; |
|
136 PK11SlotInfo *slot; |
|
137 SECStatus rv; |
|
138 SECItem *dummy; |
|
139 PLArenaPool *poolp; |
|
140 extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; |
|
141 void *mark = NULL; |
|
142 int i; |
|
143 |
|
144 poolp = envd->cmsg->poolp; |
|
145 cinfo = &(envd->contentInfo); |
|
146 |
|
147 recipientinfos = envd->recipientInfos; |
|
148 if (recipientinfos == NULL) { |
|
149 PORT_SetError(SEC_ERROR_BAD_DATA); |
|
150 #if 0 |
|
151 PORT_SetErrorString("Cannot find recipientinfos to encode."); |
|
152 #endif |
|
153 goto loser; |
|
154 } |
|
155 |
|
156 version = NSS_CMS_ENVELOPED_DATA_VERSION_REG; |
|
157 if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) { |
|
158 version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; |
|
159 } else { |
|
160 for (i = 0; recipientinfos[i] != NULL; i++) { |
|
161 if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) { |
|
162 version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV; |
|
163 break; |
|
164 } |
|
165 } |
|
166 } |
|
167 dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version); |
|
168 if (dummy == NULL) |
|
169 goto loser; |
|
170 |
|
171 /* now we need to have a proper content encryption algorithm |
|
172 * on the SMIME level, we would figure one out by looking at SMIME capabilities |
|
173 * we cannot do that on our level, so if none is set already, we'll just go |
|
174 * with one of the mandatory algorithms (3DES) */ |
|
175 if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) { |
|
176 rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168); |
|
177 if (rv != SECSuccess) |
|
178 goto loser; |
|
179 bulkalgtag = SEC_OID_DES_EDE3_CBC; |
|
180 } |
|
181 |
|
182 /* generate a random bulk key suitable for content encryption alg */ |
|
183 type = PK11_AlgtagToMechanism(bulkalgtag); |
|
184 slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg); |
|
185 if (slot == NULL) |
|
186 goto loser; /* error has been set by PK11_GetBestSlot */ |
|
187 |
|
188 /* this is expensive... */ |
|
189 bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg); |
|
190 PK11_FreeSlot(slot); |
|
191 if (bulkkey == NULL) |
|
192 goto loser; /* error has been set by PK11_KeyGen */ |
|
193 |
|
194 mark = PORT_ArenaMark(poolp); |
|
195 |
|
196 /* Encrypt the bulk key with the public key of each recipient. */ |
|
197 for (i = 0; recipientinfos[i] != NULL; i++) { |
|
198 rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag); |
|
199 if (rv != SECSuccess) |
|
200 goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */ |
|
201 /* could be: alg not supported etc. */ |
|
202 } |
|
203 |
|
204 /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */ |
|
205 rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL); |
|
206 if (rv != SECSuccess) |
|
207 goto loser; /* error has been set by NSS_CMSArray_SortByDER */ |
|
208 |
|
209 /* store the bulk key in the contentInfo so that the encoder can find it */ |
|
210 NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
|
211 |
|
212 PORT_ArenaUnmark(poolp, mark); |
|
213 |
|
214 PK11_FreeSymKey(bulkkey); |
|
215 |
|
216 return SECSuccess; |
|
217 |
|
218 loser: |
|
219 if (mark != NULL) |
|
220 PORT_ArenaRelease (poolp, mark); |
|
221 if (bulkkey) |
|
222 PK11_FreeSymKey(bulkkey); |
|
223 |
|
224 return SECFailure; |
|
225 } |
|
226 |
|
227 /* |
|
228 * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption |
|
229 * |
|
230 * it is essential that this is called before the contentEncAlg is encoded, because |
|
231 * setting up the encryption may generate IVs and thus change it! |
|
232 */ |
|
233 SECStatus |
|
234 NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd) |
|
235 { |
|
236 NSSCMSContentInfo *cinfo; |
|
237 PK11SymKey *bulkkey; |
|
238 SECAlgorithmID *algid; |
|
239 SECStatus rv; |
|
240 |
|
241 cinfo = &(envd->contentInfo); |
|
242 |
|
243 /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */ |
|
244 bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); |
|
245 if (bulkkey == NULL) |
|
246 return SECFailure; |
|
247 algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
|
248 if (algid == NULL) |
|
249 return SECFailure; |
|
250 |
|
251 rv = NSS_CMSContentInfo_Private_Init(cinfo); |
|
252 if (rv != SECSuccess) { |
|
253 return SECFailure; |
|
254 } |
|
255 /* this may modify algid (with IVs generated in a token). |
|
256 * it is essential that algid is a pointer to the contentEncAlg data, not a |
|
257 * pointer to a copy! */ |
|
258 cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid); |
|
259 PK11_FreeSymKey(bulkkey); |
|
260 if (cinfo->privateInfo->ciphcx == NULL) |
|
261 return SECFailure; |
|
262 |
|
263 return SECSuccess; |
|
264 } |
|
265 |
|
266 /* |
|
267 * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding |
|
268 */ |
|
269 SECStatus |
|
270 NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd) |
|
271 { |
|
272 if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { |
|
273 NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); |
|
274 envd->contentInfo.privateInfo->ciphcx = NULL; |
|
275 } |
|
276 |
|
277 /* nothing else to do after data */ |
|
278 return SECSuccess; |
|
279 } |
|
280 |
|
281 /* |
|
282 * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, |
|
283 * derive bulk key & set up our contentinfo |
|
284 */ |
|
285 SECStatus |
|
286 NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd) |
|
287 { |
|
288 NSSCMSRecipientInfo *ri; |
|
289 PK11SymKey *bulkkey = NULL; |
|
290 SECOidTag bulkalgtag; |
|
291 SECAlgorithmID *bulkalg; |
|
292 SECStatus rv = SECFailure; |
|
293 NSSCMSContentInfo *cinfo; |
|
294 NSSCMSRecipient **recipient_list = NULL; |
|
295 NSSCMSRecipient *recipient; |
|
296 int rlIndex; |
|
297 |
|
298 if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) { |
|
299 PORT_SetError(SEC_ERROR_BAD_DATA); |
|
300 #if 0 |
|
301 PORT_SetErrorString("No recipient data in envelope."); |
|
302 #endif |
|
303 goto loser; |
|
304 } |
|
305 |
|
306 /* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */ |
|
307 /* get the cert and private key for it right away */ |
|
308 recipient_list = nss_cms_recipient_list_create(envd->recipientInfos); |
|
309 if (recipient_list == NULL) |
|
310 goto loser; |
|
311 |
|
312 /* what about multiple recipientInfos that match? |
|
313 * especially if, for some reason, we could not produce a bulk key with the first match?! |
|
314 * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList... |
|
315 * maybe later... */ |
|
316 rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg); |
|
317 |
|
318 /* if that fails, then we're not an intended recipient and cannot decrypt */ |
|
319 if (rlIndex < 0) { |
|
320 PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT); |
|
321 #if 0 |
|
322 PORT_SetErrorString("Cannot decrypt data because proper key cannot be found."); |
|
323 #endif |
|
324 goto loser; |
|
325 } |
|
326 |
|
327 recipient = recipient_list[rlIndex]; |
|
328 if (!recipient->cert || !recipient->privkey) { |
|
329 /* XXX should set an error code ?!? */ |
|
330 goto loser; |
|
331 } |
|
332 /* get a pointer to "our" recipientinfo */ |
|
333 ri = envd->recipientInfos[recipient->riIndex]; |
|
334 |
|
335 cinfo = &(envd->contentInfo); |
|
336 bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); |
|
337 if (bulkalgtag == SEC_OID_UNKNOWN) { |
|
338 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
|
339 } else |
|
340 bulkkey = |
|
341 NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex, |
|
342 recipient->cert, |
|
343 recipient->privkey, |
|
344 bulkalgtag); |
|
345 if (bulkkey == NULL) { |
|
346 /* no success finding a bulk key */ |
|
347 goto loser; |
|
348 } |
|
349 |
|
350 NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); |
|
351 |
|
352 bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo); |
|
353 |
|
354 rv = NSS_CMSContentInfo_Private_Init(cinfo); |
|
355 if (rv != SECSuccess) { |
|
356 goto loser; |
|
357 } |
|
358 rv = SECFailure; |
|
359 cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg); |
|
360 if (cinfo->privateInfo->ciphcx == NULL) |
|
361 goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */ |
|
362 |
|
363 |
|
364 rv = SECSuccess; |
|
365 |
|
366 loser: |
|
367 if (bulkkey) |
|
368 PK11_FreeSymKey(bulkkey); |
|
369 if (recipient_list != NULL) |
|
370 nss_cms_recipient_list_destroy(recipient_list); |
|
371 return rv; |
|
372 } |
|
373 |
|
374 /* |
|
375 * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content |
|
376 */ |
|
377 SECStatus |
|
378 NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd) |
|
379 { |
|
380 if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) { |
|
381 NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx); |
|
382 envd->contentInfo.privateInfo->ciphcx = NULL; |
|
383 } |
|
384 |
|
385 return SECSuccess; |
|
386 } |
|
387 |
|
388 /* |
|
389 * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData |
|
390 */ |
|
391 SECStatus |
|
392 NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd) |
|
393 { |
|
394 /* apply final touches */ |
|
395 return SECSuccess; |
|
396 } |
|
397 |