Coverage Report

Created: 2026-10-13 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/pkcs7/p7common.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
 * PKCS7 implementation -- the exported parts that are used whether
7
 * creating or decoding.
8
 */
9
10
#include "p7local.h"
11
12
#include "cert.h"
13
#include "secitem.h"
14
#include "secoid.h"
15
#include "pk11func.h"
16
17
/*
18
 * Find out (saving pointer to lookup result for future reference)
19
 * and return the inner content type.
20
 */
21
SECOidTag
22
SEC_PKCS7ContentType(SEC_PKCS7ContentInfo *cinfo)
23
273k
{
24
273k
    if (cinfo->contentTypeTag == NULL)
25
125k
        cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
26
27
273k
    if (cinfo->contentTypeTag == NULL)
28
125k
        return SEC_OID_UNKNOWN;
29
30
147k
    return cinfo->contentTypeTag->offset;
31
273k
}
32
33
/*
34
 * Destroy a PKCS7 contentInfo and all of its sub-pieces.
35
 */
36
void
37
SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo)
38
133k
{
39
133k
    SECOidTag kind;
40
133k
    CERTCertificate **certs;
41
133k
    CERTCertificateList **certlists;
42
133k
    SEC_PKCS7SignerInfo **signerinfos;
43
133k
    SEC_PKCS7RecipientInfo **recipientinfos;
44
45
133k
    PORT_Assert(cinfo->refCount > 0);
46
133k
    if (cinfo->refCount <= 0)
47
0
        return;
48
49
133k
    cinfo->refCount--;
50
133k
    if (cinfo->refCount > 0)
51
0
        return;
52
53
133k
    certs = NULL;
54
133k
    certlists = NULL;
55
133k
    recipientinfos = NULL;
56
133k
    signerinfos = NULL;
57
58
133k
    kind = SEC_PKCS7ContentType(cinfo);
59
133k
    switch (kind) {
60
993
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
61
993
            SEC_PKCS7EnvelopedData *edp;
62
63
993
            edp = cinfo->content.envelopedData;
64
993
            if (edp != NULL) {
65
177
                recipientinfos = edp->recipientInfos;
66
177
            }
67
993
        } break;
68
3.86k
        case SEC_OID_PKCS7_SIGNED_DATA: {
69
3.86k
            SEC_PKCS7SignedData *sdp;
70
71
3.86k
            sdp = cinfo->content.signedData;
72
3.86k
            if (sdp != NULL) {
73
3.51k
                certs = sdp->certs;
74
3.51k
                certlists = sdp->certLists;
75
3.51k
                signerinfos = sdp->signerInfos;
76
3.51k
            }
77
3.86k
        } break;
78
638
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
79
638
            SEC_PKCS7SignedAndEnvelopedData *saedp;
80
81
638
            saedp = cinfo->content.signedAndEnvelopedData;
82
638
            if (saedp != NULL) {
83
118
                certs = saedp->certs;
84
118
                certlists = saedp->certLists;
85
118
                recipientinfos = saedp->recipientInfos;
86
118
                signerinfos = saedp->signerInfos;
87
118
                if (saedp->sigKey != NULL)
88
0
                    PK11_FreeSymKey(saedp->sigKey);
89
118
            }
90
638
        } break;
91
128k
        default:
92
            /* XXX Anything else that needs to be "manually" freed/destroyed? */
93
128k
            break;
94
133k
    }
95
96
133k
    if (certs != NULL) {
97
0
        CERTCertificate *cert;
98
99
0
        while ((cert = *certs++) != NULL) {
100
0
            CERT_DestroyCertificate(cert);
101
0
        }
102
0
    }
103
104
133k
    if (certlists != NULL) {
105
0
        CERTCertificateList *certlist;
106
107
0
        while ((certlist = *certlists++) != NULL) {
108
0
            CERT_DestroyCertificateList(certlist);
109
0
        }
110
0
    }
111
112
133k
    if (recipientinfos != NULL) {
113
182
        SEC_PKCS7RecipientInfo *ri;
114
115
1.57k
        while ((ri = *recipientinfos++) != NULL) {
116
1.39k
            if (ri->cert != NULL)
117
0
                CERT_DestroyCertificate(ri->cert);
118
1.39k
        }
119
182
    }
120
121
133k
    if (signerinfos != NULL) {
122
52
        SEC_PKCS7SignerInfo *si;
123
124
728
        while ((si = *signerinfos++) != NULL) {
125
676
            if (si->cert != NULL)
126
0
                CERT_DestroyCertificate(si->cert);
127
676
            if (si->certList != NULL)
128
0
                CERT_DestroyCertificateList(si->certList);
129
676
        }
130
52
    }
131
132
133k
    if (cinfo->poolp != NULL) {
133
133k
        PORT_FreeArena(cinfo->poolp, PR_FALSE); /* XXX clear it? */
134
133k
    }
135
133k
}
136
137
/*
138
 * Return a copy of the given contentInfo.  The copy may be virtual
139
 * or may be real -- either way, the result needs to be passed to
140
 * SEC_PKCS7DestroyContentInfo later (as does the original).
141
 */
142
SEC_PKCS7ContentInfo *
143
SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo)
144
0
{
145
0
    if (cinfo == NULL)
146
0
        return NULL;
147
148
0
    PORT_Assert(cinfo->refCount > 0);
149
150
0
    if (cinfo->created) {
151
        /*
152
         * Want to do a real copy of these; otherwise subsequent
153
         * changes made to either copy are likely to be a surprise.
154
         * XXX I suspect that this will not actually be called for yet,
155
         * which is why the assert, so to notice if it is...
156
         */
157
0
        PORT_Assert(0);
158
        /*
159
         * XXX Create a new pool here, and copy everything from
160
         * within.  For cert stuff, need to call the appropriate
161
         * copy functions, etc.
162
         */
163
0
    }
164
165
0
    cinfo->refCount++;
166
0
    return cinfo;
167
0
}
168
169
/*
170
 * Return a pointer to the actual content.  In the case of those types
171
 * which are encrypted, this returns the *plain* content.
172
 * XXX Needs revisiting if/when we handle nested encrypted types.
173
 */
174
SECItem *
175
SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo)
176
0
{
177
0
    SECOidTag kind;
178
179
0
    kind = SEC_PKCS7ContentType(cinfo);
180
0
    switch (kind) {
181
0
        case SEC_OID_PKCS7_DATA:
182
0
            return cinfo->content.data;
183
0
        case SEC_OID_PKCS7_DIGESTED_DATA: {
184
0
            SEC_PKCS7DigestedData *digd;
185
186
0
            digd = cinfo->content.digestedData;
187
0
            if (digd == NULL)
188
0
                break;
189
0
            return SEC_PKCS7GetContent(&(digd->contentInfo));
190
0
        }
191
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA: {
192
0
            SEC_PKCS7EncryptedData *encd;
193
194
0
            encd = cinfo->content.encryptedData;
195
0
            if (encd == NULL)
196
0
                break;
197
0
            return &(encd->encContentInfo.plainContent);
198
0
        }
199
0
        case SEC_OID_PKCS7_ENVELOPED_DATA: {
200
0
            SEC_PKCS7EnvelopedData *envd;
201
202
0
            envd = cinfo->content.envelopedData;
203
0
            if (envd == NULL)
204
0
                break;
205
0
            return &(envd->encContentInfo.plainContent);
206
0
        }
207
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
208
0
            SEC_PKCS7SignedData *sigd;
209
210
0
            sigd = cinfo->content.signedData;
211
0
            if (sigd == NULL)
212
0
                break;
213
0
            return SEC_PKCS7GetContent(&(sigd->contentInfo));
214
0
        }
215
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
216
0
            SEC_PKCS7SignedAndEnvelopedData *saed;
217
218
0
            saed = cinfo->content.signedAndEnvelopedData;
219
0
            if (saed == NULL)
220
0
                break;
221
0
            return &(saed->encContentInfo.plainContent);
222
0
        }
223
0
        default:
224
0
            PORT_Assert(0);
225
0
            break;
226
0
    }
227
228
0
    return NULL;
229
0
}
230
231
/*
232
 * XXX Fix the placement and formatting of the
233
 * following routines (i.e. make them consistent with the rest of
234
 * the pkcs7 code -- I think some/many belong in other files and
235
 * they all need a formatting/style rehaul)
236
 */
237
238
/* retrieve the algorithm identifier for encrypted data.
239
 * the identifier returned is a copy of the algorithm identifier
240
 * in the content info and needs to be freed after being used.
241
 *
242
 *   cinfo is the content info for which to retrieve the
243
 *     encryption algorithm.
244
 *
245
 * if the content info is not encrypted data or an error
246
 * occurs NULL is returned.
247
 */
248
SECAlgorithmID *
249
SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo)
250
0
{
251
0
    SECAlgorithmID *alg = 0;
252
0
    switch (SEC_PKCS7ContentType(cinfo)) {
253
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
254
0
            alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg;
255
0
            break;
256
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
257
0
            alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg;
258
0
            break;
259
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
260
0
            alg = &cinfo->content.signedAndEnvelopedData
261
0
                       ->encContentInfo.contentEncAlg;
262
0
            break;
263
0
        default:
264
0
            alg = 0;
265
0
            break;
266
0
    }
267
268
0
    return alg;
269
0
}
270
271
/* set the content of the content info.  For data content infos,
272
 * the data is set.  For encrytped content infos, the plainContent
273
 * is set, and is expected to be encrypted later.
274
 *
275
 * cinfo is the content info where the data will be set
276
 *
277
 * buf is a buffer of the data to set
278
 *
279
 * len is the length of the data being set.
280
 *
281
 * in the event of an error, SECFailure is returned.  SECSuccess
282
 * indicates the content was successfully set.
283
 */
284
SECStatus
285
SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo,
286
                    const char *buf,
287
                    unsigned long len)
288
0
{
289
0
    SECOidTag cinfo_type;
290
0
    SECStatus rv;
291
0
    SECItem content;
292
0
    SECOidData *contentTypeTag = NULL;
293
294
0
    content.type = siBuffer;
295
0
    content.data = (unsigned char *)buf;
296
0
    content.len = len;
297
298
0
    cinfo_type = SEC_PKCS7ContentType(cinfo);
299
300
    /* set inner content */
301
0
    switch (cinfo_type) {
302
0
        case SEC_OID_PKCS7_SIGNED_DATA:
303
0
            if (content.len > 0) {
304
                /* we "leak" the old content here, but as it's all in the pool */
305
                /* it does not really matter */
306
307
                /* create content item if necessary */
308
0
                if (cinfo->content.signedData->contentInfo.content.data == NULL)
309
0
                    cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0);
310
0
                rv = SECITEM_CopyItem(cinfo->poolp,
311
0
                                      cinfo->content.signedData->contentInfo.content.data,
312
0
                                      &content);
313
0
            } else {
314
0
                cinfo->content.signedData->contentInfo.content.data->data = NULL;
315
0
                cinfo->content.signedData->contentInfo.content.data->len = 0;
316
0
                rv = SECSuccess;
317
0
            }
318
0
            if (rv == SECFailure)
319
0
                goto loser;
320
321
0
            break;
322
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
323
            /* XXX this forces the inner content type to be "data" */
324
            /* do we really want to override without asking or reason? */
325
0
            contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA);
326
0
            if (contentTypeTag == NULL)
327
0
                goto loser;
328
0
            rv = SECITEM_CopyItem(cinfo->poolp,
329
0
                                  &(cinfo->content.encryptedData->encContentInfo.contentType),
330
0
                                  &(contentTypeTag->oid));
331
0
            if (rv == SECFailure)
332
0
                goto loser;
333
0
            if (content.len > 0) {
334
0
                rv = SECITEM_CopyItem(cinfo->poolp,
335
0
                                      &(cinfo->content.encryptedData->encContentInfo.plainContent),
336
0
                                      &content);
337
0
            } else {
338
0
                cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL;
339
0
                cinfo->content.encryptedData->encContentInfo.encContent.data = NULL;
340
0
                cinfo->content.encryptedData->encContentInfo.plainContent.len = 0;
341
0
                cinfo->content.encryptedData->encContentInfo.encContent.len = 0;
342
0
                rv = SECSuccess;
343
0
            }
344
0
            if (rv == SECFailure)
345
0
                goto loser;
346
0
            break;
347
0
        case SEC_OID_PKCS7_DATA:
348
0
            cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp,
349
0
                                                              sizeof(SECItem));
350
0
            if (cinfo->content.data == NULL)
351
0
                goto loser;
352
0
            if (content.len > 0) {
353
0
                rv = SECITEM_CopyItem(cinfo->poolp,
354
0
                                      cinfo->content.data, &content);
355
0
            } else {
356
                /* handle case with NULL content */
357
0
                rv = SECSuccess;
358
0
            }
359
0
            if (rv == SECFailure)
360
0
                goto loser;
361
0
            break;
362
0
        default:
363
0
            goto loser;
364
0
    }
365
366
0
    return SECSuccess;
367
368
0
loser:
369
370
0
    return SECFailure;
371
0
}
372
373
/* the content of an encrypted data content info is decrypted.
374
 * it is assumed that for encrypted data, that the data has already
375
 * been set and is in the "encContent" field of the content info.
376
 *
377
 * cinfo is the content info to decrypt
378
 *
379
 * key is the key with which to perform the decryption.  if the
380
 *     algorithm is a password based encryption algorithm, the
381
 *     key is actually a password which will be processed per
382
 *     PKCS #5.
383
 *
384
 * in the event of an error, SECFailure is returned.  SECSuccess
385
 * indicates a success.
386
 */
387
SECStatus
388
SEC_PKCS7DecryptContents(PLArenaPool *poolp,
389
                         SEC_PKCS7ContentInfo *cinfo,
390
                         SECItem *key,
391
                         void *wincx)
392
0
{
393
0
    SECAlgorithmID *algid = NULL;
394
0
    SECStatus rv = SECFailure;
395
0
    SECItem *dest, *src;
396
0
    void *mark;
397
398
0
    PK11SymKey *eKey = NULL;
399
0
    PK11SlotInfo *slot = NULL;
400
0
    CK_MECHANISM_TYPE cryptoMechType;
401
0
    void *cx;
402
0
    SECItem *c_param = NULL;
403
0
    int bs;
404
405
0
    if ((cinfo == NULL) || (key == NULL))
406
0
        return SECFailure;
407
408
0
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
409
0
        return SECFailure;
410
411
0
    algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
412
0
    if (algid == NULL)
413
0
        return SECFailure;
414
415
0
    if (poolp == NULL)
416
0
        poolp = cinfo->poolp;
417
418
0
    mark = PORT_ArenaMark(poolp);
419
420
0
    src = &cinfo->content.encryptedData->encContentInfo.encContent;
421
0
    dest = &cinfo->content.encryptedData->encContentInfo.plainContent;
422
0
    dest->data = (unsigned char *)PORT_ArenaZAlloc(poolp, (src->len + 64));
423
0
    dest->len = (src->len + 64);
424
0
    if (dest->data == NULL) {
425
0
        rv = SECFailure;
426
0
        goto loser;
427
0
    }
428
429
0
    slot = PK11_GetInternalKeySlot();
430
0
    if (slot == NULL) {
431
0
        rv = SECFailure;
432
0
        goto loser;
433
0
    }
434
435
0
    eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
436
0
    if (eKey == NULL) {
437
0
        rv = SECFailure;
438
0
        goto loser;
439
0
    }
440
441
0
    cryptoMechType = PK11_GetPBECryptoMechanism(algid, &c_param, key);
442
0
    if (cryptoMechType == CKM_INVALID_MECHANISM) {
443
0
        rv = SECFailure;
444
0
        goto loser;
445
0
    }
446
447
0
    cx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
448
0
                                    eKey, c_param);
449
0
    if (cx == NULL) {
450
0
        rv = SECFailure;
451
0
        goto loser;
452
0
    }
453
454
0
    rv = PK11_CipherOp((PK11Context *)cx, dest->data, (int *)(&dest->len),
455
0
                       (int)(src->len + 64), src->data, (int)src->len);
456
0
    PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
457
    /* PK11_CipherOp may leave dest->len at 0; bail out before the padding
458
     * check so dest->len - 1 does not underflow. */
459
0
    if (rv != SECSuccess) {
460
0
        goto loser;
461
0
    }
462
463
0
    bs = PK11_GetBlockSize(cryptoMechType, c_param);
464
0
    if (bs) {
465
        /* Check for proper PKCS#7 padding: the last byte gives the number of
466
         * pad bytes, which must be in [1, bs]. */
467
0
        unsigned int pad = dest->data[dest->len - 1];
468
0
        if ((dest->len >= (unsigned int)bs) &&
469
0
            pad >= 1 && pad <= (unsigned int)bs) {
470
0
            dest->len -= pad;
471
0
        } else {
472
0
            rv = SECFailure;
473
            /* set an error ? */
474
0
        }
475
0
    }
476
477
0
loser:
478
    /* let success fall through */
479
0
    if (rv == SECFailure)
480
0
        PORT_ArenaRelease(poolp, mark);
481
0
    else
482
0
        PORT_ArenaUnmark(poolp, mark);
483
484
0
    if (eKey != NULL)
485
0
        PK11_FreeSymKey(eKey);
486
487
0
    if (slot != NULL)
488
0
        PK11_FreeSlot(slot);
489
490
0
    if (c_param != NULL)
491
0
        SECITEM_ZfreeItem(c_param, PR_TRUE);
492
493
0
    return rv;
494
0
}
495
496
SECItem **
497
SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo)
498
0
{
499
0
    switch (SEC_PKCS7ContentType(cinfo)) {
500
0
        case SEC_OID_PKCS7_SIGNED_DATA:
501
0
            return cinfo->content.signedData->rawCerts;
502
0
            break;
503
0
        default:
504
0
            return NULL;
505
0
            break;
506
0
    }
507
0
}