Coverage Report

Created: 2026-05-19 06:33

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