Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/nss/lib/smime/cmssigdata.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 signedData methods.
7
 */
8
9
#include "cmslocal.h"
10
11
#include "cert.h"
12
/*#include "cdbhdl.h"*/
13
#include "secasn1.h"
14
#include "secitem.h"
15
#include "secoid.h"
16
#include "pk11func.h"
17
#include "secerr.h"
18
19
NSSCMSSignedData *
20
NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
21
0
{
22
0
    void *mark;
23
0
    NSSCMSSignedData *sigd;
24
0
    PLArenaPool *poolp;
25
0
26
0
    if (!cmsg) {
27
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
28
0
        return NULL;
29
0
    }
30
0
31
0
    poolp = cmsg->poolp;
32
0
33
0
    mark = PORT_ArenaMark(poolp);
34
0
35
0
    sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignedData));
36
0
    if (sigd == NULL)
37
0
        goto loser;
38
0
39
0
    sigd->cmsg = cmsg;
40
0
41
0
    /* signerInfos, certs, certlists, crls are all empty */
42
0
    /* version is set in NSS_CMSSignedData_Finalize() */
43
0
44
0
    PORT_ArenaUnmark(poolp, mark);
45
0
    return sigd;
46
0
47
0
loser:
48
0
    PORT_ArenaRelease(poolp, mark);
49
0
    return NULL;
50
0
}
51
52
void
53
NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
54
0
{
55
0
    CERTCertificate **certs, **tempCerts, *cert;
56
0
    CERTCertificateList **certlists, *certlist;
57
0
    NSSCMSSignerInfo **signerinfos, *si;
58
0
59
0
    if (sigd == NULL)
60
0
        return;
61
0
62
0
    certs = sigd->certs;
63
0
    tempCerts = sigd->tempCerts;
64
0
    certlists = sigd->certLists;
65
0
    signerinfos = sigd->signerInfos;
66
0
67
0
    if (certs != NULL) {
68
0
        while ((cert = *certs++) != NULL)
69
0
            CERT_DestroyCertificate(cert);
70
0
    }
71
0
72
0
    if (tempCerts != NULL) {
73
0
        while ((cert = *tempCerts++) != NULL)
74
0
            CERT_DestroyCertificate(cert);
75
0
    }
76
0
77
0
    if (certlists != NULL) {
78
0
        while ((certlist = *certlists++) != NULL)
79
0
            CERT_DestroyCertificateList(certlist);
80
0
    }
81
0
82
0
    if (signerinfos != NULL) {
83
0
        while ((si = *signerinfos++) != NULL)
84
0
            NSS_CMSSignerInfo_Destroy(si);
85
0
    }
86
0
87
0
    /* everything's in a pool, so don't worry about the storage */
88
0
    NSS_CMSContentInfo_Destroy(&(sigd->contentInfo));
89
0
}
90
91
/*
92
 * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
93
 *     before start of encoding.
94
 *
95
 * In detail:
96
 *  - find out about the right value to put into sigd->version
97
 *  - come up with a list of digestAlgorithms (which should be the union of the algorithms
98
 *         in the signerinfos).
99
 *         If we happen to have a pre-set list of algorithms (and digest values!), we
100
 *         check if we have all the signerinfos' algorithms. If not, this is an error.
101
 */
102
SECStatus
103
NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
104
0
{
105
0
    NSSCMSSignerInfo *signerinfo;
106
0
    SECOidTag digestalgtag;
107
0
    SECItem *dummy;
108
0
    int version;
109
0
    SECStatus rv;
110
0
    PRBool haveDigests = PR_FALSE;
111
0
    int n, i;
112
0
    PLArenaPool *poolp;
113
0
114
0
    if (!sigd) {
115
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
116
0
        return SECFailure;
117
0
    }
118
0
119
0
    poolp = sigd->cmsg->poolp;
120
0
121
0
    /* we assume that we have precomputed digests if there is a list of algorithms, and */
122
0
    /* a chunk of data for each of those algorithms */
123
0
    if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
124
0
        for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
125
0
            if (sigd->digests[i] == NULL)
126
0
                break;
127
0
        }
128
0
        if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
129
0
            haveDigests = PR_TRUE;             /* yes: we must have all the digests */
130
0
    }
131
0
132
0
    version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
133
0
134
0
    /* RFC2630 5.1 "version is the syntax version number..." */
135
0
    if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
136
0
        version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
137
0
138
0
    /* prepare all the SignerInfos (there may be none) */
139
0
    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
140
0
        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
141
0
142
0
        /* RFC2630 5.1 "version is the syntax version number..." */
143
0
        if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
144
0
            version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
145
0
146
0
        /* collect digestAlgorithms from SignerInfos */
147
0
        /* (we need to know which algorithms we have when the content comes in) */
148
0
        /* do not overwrite any existing digestAlgorithms (and digest) */
149
0
        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
150
0
        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
151
0
        if (n < 0 && haveDigests) {
152
0
            /* oops, there is a digestalg we do not have a digest for */
153
0
            /* but we were supposed to have all the digests already... */
154
0
            goto loser;
155
0
        } else if (n < 0) {
156
0
            /* add the digestAlgorithm & a NULL digest */
157
0
            rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
158
0
            if (rv != SECSuccess)
159
0
                goto loser;
160
0
        } else {
161
0
            /* found it, nothing to do */
162
0
        }
163
0
    }
164
0
165
0
    dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
166
0
    if (dummy == NULL)
167
0
        return SECFailure;
168
0
169
0
    /* this is a SET OF, so we need to sort them guys */
170
0
    rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms,
171
0
                                SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
172
0
                                (void **)sigd->digests);
173
0
    if (rv != SECSuccess)
174
0
        return SECFailure;
175
0
176
0
    return SECSuccess;
177
0
178
0
loser:
179
0
    return SECFailure;
180
0
}
181
182
SECStatus
183
NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
184
0
{
185
0
    SECStatus rv;
186
0
    if (!sigd) {
187
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
188
0
        return SECFailure;
189
0
    }
190
0
    rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
191
0
    if (rv != SECSuccess) {
192
0
        return SECFailure;
193
0
    }
194
0
    /* set up the digests */
195
0
    if (sigd->digests && sigd->digests[0]) {
196
0
        sigd->contentInfo.privateInfo->digcx = NULL; /* don't attempt to make new ones. */
197
0
    } else if (sigd->digestAlgorithms != NULL) {
198
0
        sigd->contentInfo.privateInfo->digcx =
199
0
            NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
200
0
        if (sigd->contentInfo.privateInfo->digcx == NULL)
201
0
            return SECFailure;
202
0
    }
203
0
    return SECSuccess;
204
0
}
205
206
/*
207
 * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
208
 *     after all the encapsulated data was passed through the encoder.
209
 *
210
 * In detail:
211
 *  - create the signatures in all the SignerInfos
212
 *
213
 * Please note that nothing is done to the Certificates and CRLs in the message - this
214
 * is entirely the responsibility of our callers.
215
 */
216
SECStatus
217
NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
218
0
{
219
0
    NSSCMSSignerInfo **signerinfos, *signerinfo;
220
0
    NSSCMSContentInfo *cinfo;
221
0
    SECOidTag digestalgtag;
222
0
    SECStatus ret = SECFailure;
223
0
    SECStatus rv;
224
0
    SECItem *contentType;
225
0
    int certcount;
226
0
    int i, ci, cli, n, rci, si;
227
0
    PLArenaPool *poolp;
228
0
    CERTCertificateList *certlist;
229
0
    extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
230
0
231
0
    if (!sigd) {
232
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
233
0
        return SECFailure;
234
0
    }
235
0
236
0
    poolp = sigd->cmsg->poolp;
237
0
    cinfo = &(sigd->contentInfo);
238
0
239
0
    /* did we have digest calculation going on? */
240
0
    if (cinfo->privateInfo && cinfo->privateInfo->digcx) {
241
0
        rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp,
242
0
                                                 &(sigd->digests));
243
0
        /* error has been set by NSS_CMSDigestContext_FinishMultiple */
244
0
        cinfo->privateInfo->digcx = NULL;
245
0
        if (rv != SECSuccess)
246
0
            goto loser;
247
0
    }
248
0
249
0
    signerinfos = sigd->signerInfos;
250
0
    certcount = 0;
251
0
252
0
    /* prepare all the SignerInfos (there may be none) */
253
0
    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
254
0
        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);
255
0
256
0
        /* find correct digest for this signerinfo */
257
0
        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
258
0
        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
259
0
        if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
260
0
            /* oops - digest not found */
261
0
            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
262
0
            goto loser;
263
0
        }
264
0
265
0
        /* XXX if our content is anything else but data, we need to force the
266
0
         * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
267
0
         * collection...") */
268
0
269
0
        /* pass contentType here as we want a contentType attribute */
270
0
        if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
271
0
            goto loser;
272
0
273
0
        /* sign the thing */
274
0
        rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
275
0
        if (rv != SECSuccess)
276
0
            goto loser;
277
0
278
0
        /* while we're at it, count number of certs in certLists */
279
0
        certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
280
0
        if (certlist)
281
0
            certcount += certlist->len;
282
0
    }
283
0
284
0
    /* this is a SET OF, so we need to sort them guys */
285
0
    rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
286
0
    if (rv != SECSuccess)
287
0
        goto loser;
288
0
289
0
    /*
290
0
     * now prepare certs & crls
291
0
     */
292
0
293
0
    /* count the rest of the certs */
294
0
    if (sigd->certs != NULL) {
295
0
        for (ci = 0; sigd->certs[ci] != NULL; ci++)
296
0
            certcount++;
297
0
    }
298
0
299
0
    if (sigd->certLists != NULL) {
300
0
        for (cli = 0; sigd->certLists[cli] != NULL; cli++)
301
0
            certcount += sigd->certLists[cli]->len;
302
0
    }
303
0
304
0
    if (certcount == 0) {
305
0
        sigd->rawCerts = NULL;
306
0
    } else {
307
0
        /*
308
0
         * Combine all of the certs and cert chains into rawcerts.
309
0
         * Note: certcount is an upper bound; we may not need that many slots
310
0
         * but we will allocate anyway to avoid having to do another pass.
311
0
         * (The temporary space saving is not worth it.)
312
0
         *
313
0
         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
314
0
         *  SetOfDERcertficates implementation
315
0
         */
316
0
        sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
317
0
        if (sigd->rawCerts == NULL)
318
0
            return SECFailure;
319
0
320
0
        /*
321
0
         * XXX Want to check for duplicates and not add *any* cert that is
322
0
         * already in the set.  This will be more important when we start
323
0
         * dealing with larger sets of certs, dual-key certs (signing and
324
0
         * encryption), etc.  For the time being we can slide by...
325
0
         *
326
0
         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
327
0
         *  SetOfDERcertficates implementation
328
0
         */
329
0
        rci = 0;
330
0
        if (signerinfos != NULL) {
331
0
            for (si = 0; signerinfos[si] != NULL; si++) {
332
0
                signerinfo = signerinfos[si];
333
0
                for (ci = 0; ci < signerinfo->certList->len; ci++)
334
0
                    sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
335
0
            }
336
0
        }
337
0
338
0
        if (sigd->certs != NULL) {
339
0
            for (ci = 0; sigd->certs[ci] != NULL; ci++)
340
0
                sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
341
0
        }
342
0
343
0
        if (sigd->certLists != NULL) {
344
0
            for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
345
0
                for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
346
0
                    sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
347
0
            }
348
0
        }
349
0
350
0
        sigd->rawCerts[rci] = NULL;
351
0
352
0
        /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
353
0
        NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
354
0
    }
355
0
356
0
    ret = SECSuccess;
357
0
358
0
loser:
359
0
    return ret;
360
0
}
361
362
SECStatus
363
NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
364
0
{
365
0
    SECStatus rv;
366
0
    if (!sigd) {
367
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
368
0
        return SECFailure;
369
0
    }
370
0
    rv = NSS_CMSContentInfo_Private_Init(&sigd->contentInfo);
371
0
    if (rv != SECSuccess) {
372
0
        return SECFailure;
373
0
    }
374
0
    /* handle issue with Windows 2003 servers and kerberos */
375
0
    if (sigd->digestAlgorithms != NULL) {
376
0
        int i;
377
0
        for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
378
0
            SECAlgorithmID *algid = sigd->digestAlgorithms[i];
379
0
            SECOidTag senttag = SECOID_FindOIDTag(&algid->algorithm);
380
0
            SECOidTag maptag = NSS_CMSUtil_MapSignAlgs(senttag);
381
0
382
0
            if (maptag != senttag) {
383
0
                SECOidData *hashoid = SECOID_FindOIDByTag(maptag);
384
0
                rv = SECITEM_CopyItem(sigd->cmsg->poolp, &algid->algorithm, &hashoid->oid);
385
0
                if (rv != SECSuccess) {
386
0
                    return rv;
387
0
                }
388
0
            }
389
0
        }
390
0
    }
391
0
392
0
    /* set up the digests */
393
0
    if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
394
0
        /* if digests are already there, do nothing */
395
0
        sigd->contentInfo.privateInfo->digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
396
0
        if (sigd->contentInfo.privateInfo->digcx == NULL)
397
0
            return SECFailure;
398
0
    }
399
0
    return SECSuccess;
400
0
}
401
402
/*
403
 * NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a
404
 *   SignedData after all the encapsulated data was passed through the decoder.
405
 */
406
SECStatus
407
NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
408
0
{
409
0
    SECStatus rv = SECSuccess;
410
0
411
0
    if (!sigd) {
412
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
413
0
        return SECFailure;
414
0
    }
415
0
416
0
    /* did we have digest calculation going on? */
417
0
    if (sigd->contentInfo.privateInfo && sigd->contentInfo.privateInfo->digcx) {
418
0
        rv = NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.privateInfo->digcx,
419
0
                                                 sigd->cmsg->poolp, &(sigd->digests));
420
0
        /* error set by NSS_CMSDigestContext_FinishMultiple */
421
0
        sigd->contentInfo.privateInfo->digcx = NULL;
422
0
    }
423
0
    return rv;
424
0
}
425
426
/*
427
 * NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
428
 *     after all decoding is finished.
429
 */
430
SECStatus
431
NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
432
0
{
433
0
    NSSCMSSignerInfo **signerinfos = NULL;
434
0
    int i;
435
0
436
0
    if (!sigd) {
437
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
438
0
        return SECFailure;
439
0
    }
440
0
441
0
    /* set cmsg for all the signerinfos */
442
0
    signerinfos = sigd->signerInfos;
443
0
444
0
    /* set cmsg for all the signerinfos */
445
0
    if (signerinfos) {
446
0
        for (i = 0; signerinfos[i] != NULL; i++)
447
0
            signerinfos[i]->cmsg = sigd->cmsg;
448
0
    }
449
0
450
0
    return SECSuccess;
451
0
}
452
453
/*
454
 * NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
455
 */
456
NSSCMSSignerInfo **
457
NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
458
0
{
459
0
    if (!sigd) {
460
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
461
0
        return NULL;
462
0
    }
463
0
    return sigd->signerInfos;
464
0
}
465
466
int
467
NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
468
0
{
469
0
    if (!sigd) {
470
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
471
0
        return 0;
472
0
    }
473
0
    return NSS_CMSArray_Count((void **)sigd->signerInfos);
474
0
}
475
476
NSSCMSSignerInfo *
477
NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
478
0
{
479
0
    if (!sigd) {
480
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
481
0
        return NULL;
482
0
    }
483
0
    return sigd->signerInfos[i];
484
0
}
485
486
/*
487
 * NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
488
 */
489
SECAlgorithmID **
490
NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
491
0
{
492
0
    if (!sigd) {
493
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
494
0
        return NULL;
495
0
    }
496
0
    return sigd->digestAlgorithms;
497
0
}
498
499
/*
500
 * NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
501
 */
502
NSSCMSContentInfo *
503
NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
504
0
{
505
0
    if (!sigd) {
506
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
507
0
        return NULL;
508
0
    }
509
0
    return &(sigd->contentInfo);
510
0
}
511
512
/*
513
 * NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
514
 */
515
SECItem **
516
NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
517
0
{
518
0
    if (!sigd) {
519
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
520
0
        return NULL;
521
0
    }
522
0
    return sigd->rawCerts;
523
0
}
524
525
SECStatus
526
NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
527
                              SECCertUsage certusage, PRBool keepcerts)
528
0
{
529
0
    int certcount;
530
0
    CERTCertificate **certArray = NULL;
531
0
    CERTCertList *certList = NULL;
532
0
    CERTCertListNode *node;
533
0
    SECStatus rv;
534
0
    SECItem **rawArray;
535
0
    int i;
536
0
    PRTime now;
537
0
538
0
    if (!sigd) {
539
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
540
0
        return SECFailure;
541
0
    }
542
0
543
0
    certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
544
0
545
0
    /* get the certs in the temp DB */
546
0
    rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
547
0
                          &certArray, PR_FALSE, PR_FALSE, NULL);
548
0
    if (rv != SECSuccess) {
549
0
        goto loser;
550
0
    }
551
0
552
0
    /* save the certs so they don't get destroyed */
553
0
    for (i = 0; i < certcount; i++) {
554
0
        CERTCertificate *cert = certArray[i];
555
0
        if (cert)
556
0
            NSS_CMSSignedData_AddTempCertificate(sigd, cert);
557
0
    }
558
0
559
0
    if (!keepcerts) {
560
0
        goto done;
561
0
    }
562
0
563
0
    /* build a CertList for filtering */
564
0
    certList = CERT_NewCertList();
565
0
    if (certList == NULL) {
566
0
        rv = SECFailure;
567
0
        goto loser;
568
0
    }
569
0
    for (i = 0; i < certcount; i++) {
570
0
        CERTCertificate *cert = certArray[i];
571
0
        if (cert)
572
0
            cert = CERT_DupCertificate(cert);
573
0
        if (cert)
574
0
            CERT_AddCertToListTail(certList, cert);
575
0
    }
576
0
577
0
    /* filter out the certs we don't want */
578
0
    rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
579
0
    if (rv != SECSuccess) {
580
0
        goto loser;
581
0
    }
582
0
583
0
    /* go down the remaining list of certs and verify that they have
584
0
     * valid chains, then import them.
585
0
     */
586
0
    now = PR_Now();
587
0
    for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
588
0
         node = CERT_LIST_NEXT(node)) {
589
0
        CERTCertificateList *certChain;
590
0
591
0
        if (CERT_VerifyCert(certdb, node->cert,
592
0
                            PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
593
0
            continue;
594
0
        }
595
0
596
0
        certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
597
0
        if (!certChain) {
598
0
            continue;
599
0
        }
600
0
601
0
        /*
602
0
         * CertChain returns an array of SECItems, import expects an array of
603
0
         * SECItem pointers. Create the SECItem Pointers from the array of
604
0
         * SECItems.
605
0
         */
606
0
        rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *));
607
0
        if (!rawArray) {
608
0
            CERT_DestroyCertificateList(certChain);
609
0
            continue;
610
0
        }
611
0
        for (i = 0; i < certChain->len; i++) {
612
0
            rawArray[i] = &certChain->certs[i];
613
0
        }
614
0
        (void)CERT_ImportCerts(certdb, certusage, certChain->len,
615
0
                               rawArray, NULL, keepcerts, PR_FALSE, NULL);
616
0
        PORT_Free(rawArray);
617
0
        CERT_DestroyCertificateList(certChain);
618
0
    }
619
0
620
0
    rv = SECSuccess;
621
0
622
0
/* XXX CRL handling */
623
0
624
0
done:
625
0
    if (sigd->signerInfos != NULL) {
626
0
        /* fill in all signerinfo's certs */
627
0
        for (i = 0; sigd->signerInfos[i] != NULL; i++)
628
0
            (void)NSS_CMSSignerInfo_GetSigningCertificate(
629
0
                sigd->signerInfos[i], certdb);
630
0
    }
631
0
632
0
loser:
633
0
    /* now free everything */
634
0
    if (certArray) {
635
0
        CERT_DestroyCertArray(certArray, certcount);
636
0
    }
637
0
    if (certList) {
638
0
        CERT_DestroyCertList(certList);
639
0
    }
640
0
641
0
    return rv;
642
0
}
643
644
/*
645
 * XXX the digests need to be passed in BETWEEN the decoding and the verification in case
646
 *     of external signatures!
647
 */
648
649
/*
650
 * NSS_CMSSignedData_VerifySignerInfo - check the signatures.
651
 *
652
 * The digests were either calculated during decoding (and are stored in the
653
 * signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
654
 *
655
 * The verification checks if the signing cert is valid and has a trusted chain
656
 * for the purpose specified by "certusage".
657
 */
658
SECStatus
659
NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i,
660
                                   CERTCertDBHandle *certdb, SECCertUsage certusage)
661
0
{
662
0
    NSSCMSSignerInfo *signerinfo;
663
0
    NSSCMSContentInfo *cinfo;
664
0
    SECOidData *algiddata;
665
0
    SECItem *contentType, *digest;
666
0
    SECOidTag oidTag;
667
0
    SECStatus rv;
668
0
669
0
    if (!sigd) {
670
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
671
0
        return SECFailure;
672
0
    }
673
0
674
0
    cinfo = &(sigd->contentInfo);
675
0
676
0
    signerinfo = sigd->signerInfos[i];
677
0
678
0
    /* verify certificate */
679
0
    rv = NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage);
680
0
    if (rv != SECSuccess)
681
0
        return rv; /* error is set */
682
0
683
0
    /* find digest and contentType for signerinfo */
684
0
    algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
685
0
    oidTag = algiddata ? algiddata->offset : SEC_OID_UNKNOWN;
686
0
    digest = NSS_CMSSignedData_GetDigestValue(sigd, oidTag);
687
0
    /* NULL digest is acceptable. */
688
0
    contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
689
0
    /* NULL contentType is acceptable. */
690
0
691
0
    /* now verify signature */
692
0
    rv = NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
693
0
    return rv;
694
0
}
695
696
/*
697
 * NSS_CMSSignedData_VerifyCertsOnly - verify the certs in a certs-only message
698
 */
699
SECStatus
700
NSS_CMSSignedData_VerifyCertsOnly(NSSCMSSignedData *sigd,
701
                                  CERTCertDBHandle *certdb,
702
                                  SECCertUsage usage)
703
0
{
704
0
    CERTCertificate *cert;
705
0
    SECStatus rv = SECSuccess;
706
0
    int i;
707
0
    int count;
708
0
    PRTime now;
709
0
    void *pwarg = NULL;
710
0
711
0
    if (!sigd || !certdb || !sigd->rawCerts) {
712
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
713
0
        return SECFailure;
714
0
    }
715
0
716
0
    count = NSS_CMSArray_Count((void **)sigd->rawCerts);
717
0
    now = PR_Now();
718
0
    for (i = 0; i < count; i++) {
719
0
        if (sigd->certs && sigd->certs[i]) {
720
0
            cert = CERT_DupCertificate(sigd->certs[i]);
721
0
        } else {
722
0
            cert = CERT_FindCertByDERCert(certdb, sigd->rawCerts[i]);
723
0
            if (!cert) {
724
0
                rv = SECFailure;
725
0
                break;
726
0
            }
727
0
        }
728
0
        if (sigd->cmsg) {
729
0
            pwarg = sigd->cmsg->pwfn_arg;
730
0
        }
731
0
        rv |= CERT_VerifyCert(certdb, cert, PR_TRUE, usage, now,
732
0
                              pwarg, NULL);
733
0
        CERT_DestroyCertificate(cert);
734
0
    }
735
0
736
0
    return rv;
737
0
}
738
739
/*
740
 * NSS_CMSSignedData_HasDigests - see if we have digests in place
741
 */
742
PRBool
743
NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
744
0
{
745
0
    if (!sigd) {
746
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
747
0
        return PR_FALSE;
748
0
    }
749
0
    return (sigd->digests != NULL);
750
0
}
751
752
SECStatus
753
NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
754
0
{
755
0
    SECStatus rv;
756
0
757
0
    if (!sigd || !certlist) {
758
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
759
0
        return SECFailure;
760
0
    }
761
0
762
0
    /* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
763
0
    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
764
0
765
0
    return rv;
766
0
}
767
768
/*
769
 * NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
770
 */
771
SECStatus
772
NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
773
0
{
774
0
    CERTCertificateList *certlist;
775
0
    SECCertUsage usage;
776
0
    SECStatus rv;
777
0
778
0
    usage = certUsageEmailSigner;
779
0
780
0
    if (!sigd || !cert) {
781
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
782
0
        return SECFailure;
783
0
    }
784
0
785
0
    /* do not include root */
786
0
    certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
787
0
    if (certlist == NULL)
788
0
        return SECFailure;
789
0
790
0
    rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
791
0
792
0
    return rv;
793
0
}
794
795
extern SECStatus
796
NSS_CMSSignedData_AddTempCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
797
0
{
798
0
    CERTCertificate *c;
799
0
    SECStatus rv;
800
0
801
0
    if (!sigd || !cert) {
802
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
803
0
        return SECFailure;
804
0
    }
805
0
806
0
    c = CERT_DupCertificate(cert);
807
0
    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->tempCerts), (void *)c);
808
0
    return rv;
809
0
}
810
811
SECStatus
812
NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
813
0
{
814
0
    CERTCertificate *c;
815
0
    SECStatus rv;
816
0
817
0
    if (!sigd || !cert) {
818
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
819
0
        return SECFailure;
820
0
    }
821
0
822
0
    c = CERT_DupCertificate(cert);
823
0
    rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
824
0
    return rv;
825
0
}
826
827
PRBool
828
NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
829
0
{
830
0
    if (!sigd) {
831
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
832
0
        return PR_FALSE;
833
0
    }
834
0
    if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
835
0
        return PR_TRUE;
836
0
    else if (sigd->crls != NULL && sigd->crls[0] != NULL)
837
0
        return PR_TRUE;
838
0
    else
839
0
        return PR_FALSE;
840
0
}
841
842
SECStatus
843
NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
844
                                NSSCMSSignerInfo *signerinfo)
845
0
{
846
0
    void *mark;
847
0
    SECStatus rv;
848
0
    SECOidTag digestalgtag;
849
0
    PLArenaPool *poolp;
850
0
851
0
    if (!sigd || !signerinfo) {
852
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
853
0
        return SECFailure;
854
0
    }
855
0
856
0
    poolp = sigd->cmsg->poolp;
857
0
858
0
    mark = PORT_ArenaMark(poolp);
859
0
860
0
    /* add signerinfo */
861
0
    rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
862
0
    if (rv != SECSuccess)
863
0
        goto loser;
864
0
865
0
    /*
866
0
     * add empty digest
867
0
     * Empty because we don't have it yet. Either it gets created during encoding
868
0
     * (if the data is present) or has to be set externally.
869
0
     * XXX maybe pass it in optionally?
870
0
     */
871
0
    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
872
0
    rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
873
0
    if (rv != SECSuccess)
874
0
        goto loser;
875
0
876
0
    /*
877
0
     * The last thing to get consistency would be adding the digest.
878
0
     */
879
0
880
0
    PORT_ArenaUnmark(poolp, mark);
881
0
    return SECSuccess;
882
0
883
0
loser:
884
0
    PORT_ArenaRelease(poolp, mark);
885
0
    return SECFailure;
886
0
}
887
888
/*
889
 * NSS_CMSSignedData_SetDigests - set a signedData's digests member
890
 *
891
 * "digestalgs" - array of digest algorithm IDs
892
 * "digests"    - array of digests corresponding to the digest algorithms
893
 */
894
SECStatus
895
NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
896
                             SECAlgorithmID **digestalgs,
897
                             SECItem **digests)
898
0
{
899
0
    int cnt, i, idx;
900
0
901
0
    if (!sigd || !digestalgs || !digests) {
902
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
903
0
        return SECFailure;
904
0
    }
905
0
906
0
    if (sigd->digestAlgorithms == NULL) {
907
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
908
0
        return SECFailure;
909
0
    }
910
0
911
0
    /* we assume that the digests array is just not there yet */
912
0
    PORT_Assert(sigd->digests == NULL);
913
0
    if (sigd->digests != NULL) {
914
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
915
0
        return SECFailure;
916
0
    }
917
0
918
0
    /* now allocate one (same size as digestAlgorithms) */
919
0
    cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
920
0
    sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
921
0
    if (sigd->digests == NULL) {
922
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
923
0
        return SECFailure;
924
0
    }
925
0
926
0
    for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
927
0
        /* try to find the sigd's i'th digest algorithm in the array we passed in */
928
0
        idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
929
0
        if (idx < 0) {
930
0
            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
931
0
            return SECFailure;
932
0
        }
933
0
        if (!digests[idx]) {
934
0
            /* We have no digest for this algorithm, probably because it is
935
0
            ** unrecognized or unsupported.  We'll ignore this here.  If this
936
0
            ** digest is needed later, an error will be be generated then.
937
0
            */
938
0
            continue;
939
0
        }
940
0
941
0
        /* found it - now set it */
942
0
        if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
943
0
            SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess) {
944
0
            PORT_SetError(SEC_ERROR_NO_MEMORY);
945
0
            return SECFailure;
946
0
        }
947
0
    }
948
0
    return SECSuccess;
949
0
}
950
951
SECStatus
952
NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
953
                                 SECOidTag digestalgtag,
954
                                 SECItem *digestdata)
955
0
{
956
0
    SECItem *digest = NULL;
957
0
    PLArenaPool *poolp;
958
0
    void *mark;
959
0
    int n, cnt;
960
0
961
0
    if (!sigd) {
962
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
963
0
        return SECFailure;
964
0
    }
965
0
966
0
    poolp = sigd->cmsg->poolp;
967
0
968
0
    mark = PORT_ArenaMark(poolp);
969
0
970
0
    if (digestdata) {
971
0
        digest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
972
0
973
0
        /* copy digestdata item to arena (in case we have it and are not only making room) */
974
0
        if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
975
0
            goto loser;
976
0
    }
977
0
978
0
    /* now allocate one (same size as digestAlgorithms) */
979
0
    if (sigd->digests == NULL) {
980
0
        cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
981
0
        sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
982
0
        if (sigd->digests == NULL) {
983
0
            PORT_SetError(SEC_ERROR_NO_MEMORY);
984
0
            return SECFailure;
985
0
        }
986
0
    }
987
0
988
0
    n = -1;
989
0
    if (sigd->digestAlgorithms != NULL)
990
0
        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
991
0
992
0
    /* if not found, add a digest */
993
0
    if (n < 0) {
994
0
        if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
995
0
            goto loser;
996
0
    } else {
997
0
        /* replace NULL pointer with digest item (and leak previous value) */
998
0
        sigd->digests[n] = digest;
999
0
    }
1000
0
1001
0
    PORT_ArenaUnmark(poolp, mark);
1002
0
    return SECSuccess;
1003
0
1004
0
loser:
1005
0
    PORT_ArenaRelease(poolp, mark);
1006
0
    return SECFailure;
1007
0
}
1008
1009
SECStatus
1010
NSS_CMSSignedData_AddDigest(PLArenaPool *poolp,
1011
                            NSSCMSSignedData *sigd,
1012
                            SECOidTag digestalgtag,
1013
                            SECItem *digest)
1014
0
{
1015
0
    SECAlgorithmID *digestalg;
1016
0
    void *mark;
1017
0
1018
0
    if (!sigd || !poolp) {
1019
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
1020
0
        return SECFailure;
1021
0
    }
1022
0
1023
0
    mark = PORT_ArenaMark(poolp);
1024
0
1025
0
    digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
1026
0
    if (digestalg == NULL)
1027
0
        goto loser;
1028
0
1029
0
    if (SECOID_SetAlgorithmID(poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
1030
0
        goto loser;
1031
0
1032
0
    if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms),
1033
0
                         (void *)digestalg) != SECSuccess ||
1034
0
        /* even if digest is NULL, add dummy to have same-size array */
1035
0
        NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests),
1036
0
                         (void *)digest) != SECSuccess) {
1037
0
        goto loser;
1038
0
    }
1039
0
1040
0
    PORT_ArenaUnmark(poolp, mark);
1041
0
    return SECSuccess;
1042
0
1043
0
loser:
1044
0
    PORT_ArenaRelease(poolp, mark);
1045
0
    return SECFailure;
1046
0
}
1047
1048
/* XXX This function doesn't set the error code on failure. */
1049
SECItem *
1050
NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
1051
0
{
1052
0
    int n;
1053
0
1054
0
    if (!sigd) {
1055
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
1056
0
        return NULL;
1057
0
    }
1058
0
1059
0
    if (sigd->digestAlgorithms == NULL || sigd->digests == NULL) {
1060
0
        PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
1061
0
        return NULL;
1062
0
    }
1063
0
1064
0
    n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
1065
0
1066
0
    return (n < 0) ? NULL : sigd->digests[n];
1067
0
}
1068
1069
/* =============================================================================
1070
 * Misc. utility functions
1071
 */
1072
1073
/*
1074
 * NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
1075
 *
1076
 * cert          - base certificates that will be included
1077
 * include_chain - if true, include the complete cert chain for cert
1078
 *
1079
 * More certs and chains can be added via AddCertificate and AddCertChain.
1080
 *
1081
 * An error results in a return value of NULL and an error set.
1082
 *
1083
 * XXXX CRLs
1084
 */
1085
NSSCMSSignedData *
1086
NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
1087
0
{
1088
0
    NSSCMSSignedData *sigd;
1089
0
    void *mark;
1090
0
    PLArenaPool *poolp;
1091
0
    SECStatus rv;
1092
0
1093
0
    if (!cmsg || !cert) {
1094
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
1095
0
        return NULL;
1096
0
    }
1097
0
1098
0
    poolp = cmsg->poolp;
1099
0
    mark = PORT_ArenaMark(poolp);
1100
0
1101
0
    sigd = NSS_CMSSignedData_Create(cmsg);
1102
0
    if (sigd == NULL)
1103
0
        goto loser;
1104
0
1105
0
    /* no signerinfos, thus no digestAlgorithms */
1106
0
1107
0
    /* but certs */
1108
0
    if (include_chain) {
1109
0
        rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
1110
0
    } else {
1111
0
        rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
1112
0
    }
1113
0
    if (rv != SECSuccess)
1114
0
        goto loser;
1115
0
1116
0
    /* RFC2630 5.2 sez:
1117
0
     * In the degenerate case where there are no signers, the
1118
0
     * EncapsulatedContentInfo value being "signed" is irrelevant.  In this
1119
0
     * case, the content type within the EncapsulatedContentInfo value being
1120
0
     * "signed" should be id-data (as defined in section 4), and the content
1121
0
     * field of the EncapsulatedContentInfo value should be omitted.
1122
0
     */
1123
0
    rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
1124
0
    if (rv != SECSuccess)
1125
0
        goto loser;
1126
0
1127
0
    PORT_ArenaUnmark(poolp, mark);
1128
0
    return sigd;
1129
0
1130
0
loser:
1131
0
    if (sigd)
1132
0
        NSS_CMSSignedData_Destroy(sigd);
1133
0
    PORT_ArenaRelease(poolp, mark);
1134
0
    return NULL;
1135
0
}
1136
1137
/* TODO:
1138
 * NSS_CMSSignerInfo_GetReceiptRequest()
1139
 * NSS_CMSSignedData_HasReceiptRequest()
1140
 * easy way to iterate over signers
1141
 */