Coverage Report

Created: 2018-09-25 14:53

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