security/nss/lib/smime/cmsenvdata.c

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a6c221b379c6
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

mercurial