Coverage Report

Created: 2026-04-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/smime/cmssiginfo.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 signerInfo 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 "prtime.h"
18
#include "secerr.h"
19
#include "secder.h"
20
#include "cryptohi.h"
21
22
#include "smime.h"
23
24
/* =============================================================================
25
 * SIGNERINFO
26
 */
27
NSSCMSSignerInfo *
28
nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
29
                         CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
30
                         SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
31
32
NSSCMSSignerInfo *
33
NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID,
34
                                      SECKEYPublicKey *pubKey,
35
                                      SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
36
0
{
37
0
    return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL,
38
0
                                    subjKeyID, pubKey, signingKey, digestalgtag);
39
0
}
40
41
NSSCMSSignerInfo *
42
NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
43
0
{
44
0
    return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL,
45
0
                                    NULL, NULL, digestalgtag);
46
0
}
47
48
NSSCMSSignerInfo *
49
nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
50
                         CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
51
                         SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
52
0
{
53
0
    void *mark;
54
0
    NSSCMSSignerInfo *signerinfo;
55
0
    int version;
56
0
    PLArenaPool *poolp;
57
0
    SECStatus rv;
58
59
0
    poolp = cmsg->poolp;
60
61
0
    mark = PORT_ArenaMark(poolp);
62
63
0
    signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
64
0
    if (signerinfo == NULL) {
65
0
        PORT_ArenaRelease(poolp, mark);
66
0
        return NULL;
67
0
    }
68
69
0
    signerinfo->cmsg = cmsg;
70
71
0
    switch (type) {
72
0
        case NSSCMSSignerID_IssuerSN:
73
0
            signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
74
0
            if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
75
0
                goto loser;
76
0
            if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
77
0
                goto loser;
78
0
            break;
79
0
        case NSSCMSSignerID_SubjectKeyID:
80
0
            signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
81
0
            PORT_Assert(subjKeyID);
82
0
            if (!subjKeyID)
83
0
                goto loser;
84
85
0
            signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
86
0
            rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
87
0
                                  subjKeyID);
88
0
            if (rv != SECSuccess) {
89
0
                goto loser;
90
0
            }
91
0
            signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
92
0
            if (!signerinfo->signingKey)
93
0
                goto loser;
94
0
            signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
95
0
            if (!signerinfo->pubKey)
96
0
                goto loser;
97
0
            break;
98
0
        default:
99
0
            goto loser;
100
0
    }
101
102
    /* set version right now */
103
0
    version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
104
    /* RFC2630 5.3 "version is the syntax version number. If the .... " */
105
0
    if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
106
0
        version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
107
0
    (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
108
109
0
    if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
110
0
        goto loser;
111
112
0
    PORT_ArenaUnmark(poolp, mark);
113
0
    return signerinfo;
114
115
0
loser:
116
0
    PORT_ArenaRelease(poolp, mark);
117
0
    return NULL;
118
0
}
119
120
/*
121
 * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
122
 */
123
void
124
NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
125
590
{
126
590
    if (si->cert != NULL)
127
0
        CERT_DestroyCertificate(si->cert);
128
129
590
    if (si->certList != NULL)
130
0
        CERT_DestroyCertificateList(si->certList);
131
132
    /* XXX storage ??? */
133
590
}
134
static SECOidTag
135
NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,
136
                                              SECOidTag pubkAlgTag,
137
                                              SECOidTag signAlgTag)
138
0
{
139
0
    switch (keyType) {
140
0
        case rsaKey:
141
0
            return pubkAlgTag;
142
0
        case rsaPssKey:
143
0
        case dsaKey:
144
0
        case ecKey:
145
0
            return signAlgTag;
146
0
        default:
147
0
            return SEC_OID_UNKNOWN;
148
0
    }
149
0
}
150
151
/*
152
 * NSS_CMSSignerInfo_Sign - sign something
153
 *
154
 */
155
SECStatus
156
NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest,
157
                       SECItem *contentType)
158
0
{
159
0
    CERTCertificate *cert;
160
0
    SECKEYPrivateKey *privkey = NULL;
161
0
    SECOidTag digestalgtag;
162
0
    SECOidTag pubkAlgTag;
163
0
    SECOidTag signAlgTag;
164
0
    SECOidTag cmsSignAlgTag;
165
0
    SECItem signature = { 0 };
166
0
    SECStatus rv;
167
0
    PLArenaPool *poolp, *tmppoolp = NULL;
168
0
    SECAlgorithmID *algID, freeAlgID;
169
0
    CERTSubjectPublicKeyInfo *spki;
170
171
0
    PORT_Assert(digest != NULL);
172
173
0
    poolp = signerinfo->cmsg->poolp;
174
175
0
    switch (signerinfo->signerIdentifier.identifierType) {
176
0
        case NSSCMSSignerID_IssuerSN:
177
0
            cert = signerinfo->cert;
178
179
0
            privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
180
0
            if (privkey == NULL)
181
0
                goto loser;
182
0
            algID = &cert->subjectPublicKeyInfo.algorithm;
183
0
            break;
184
0
        case NSSCMSSignerID_SubjectKeyID:
185
0
            privkey = signerinfo->signingKey;
186
0
            signerinfo->signingKey = NULL;
187
0
            spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
188
0
            SECKEY_DestroyPublicKey(signerinfo->pubKey);
189
0
            signerinfo->pubKey = NULL;
190
0
            SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
191
0
            SECKEY_DestroySubjectPublicKeyInfo(spki);
192
0
            algID = &freeAlgID;
193
0
            break;
194
0
        default:
195
0
            goto loser;
196
0
    }
197
0
    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
198
    /*
199
     * XXX I think there should be a cert-level interface for this,
200
     * so that I do not have to know about subjectPublicKeyInfo...
201
     */
202
0
    pubkAlgTag = SECOID_GetAlgorithmTag(algID);
203
0
    if (algID == &freeAlgID) {
204
0
        SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
205
0
    }
206
207
0
    signAlgTag = SEC_GetSignatureAlgorithmOidTagByKey(privkey, NULL,
208
0
                                                      digestalgtag);
209
0
    if (signAlgTag == SEC_OID_UNKNOWN) {
210
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
211
0
        goto loser;
212
0
    }
213
214
0
    cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(
215
0
        SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag);
216
0
    if (cmsSignAlgTag == SEC_OID_UNKNOWN) {
217
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
218
0
        goto loser;
219
0
    }
220
221
0
    if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
222
0
                              cmsSignAlgTag, NULL) != SECSuccess)
223
0
        goto loser;
224
225
0
    if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) {
226
0
        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
227
0
        goto loser;
228
0
    }
229
230
0
    if (signerinfo->authAttr != NULL) {
231
0
        SECItem encoded_attrs;
232
233
        /* find and fill in the message digest attribute. */
234
0
        rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
235
0
                                           SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
236
0
        if (rv != SECSuccess)
237
0
            goto loser;
238
239
0
        if (contentType != NULL) {
240
            /* if the caller wants us to, find and fill in the content type attribute. */
241
0
            rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
242
0
                                               SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
243
0
            if (rv != SECSuccess)
244
0
                goto loser;
245
0
        }
246
247
0
        if ((tmppoolp = PORT_NewArena(1024)) == NULL) {
248
0
            PORT_SetError(SEC_ERROR_NO_MEMORY);
249
0
            goto loser;
250
0
        }
251
252
        /*
253
         * Before encoding, reorder the attributes so that when they
254
         * are encoded, they will be conforming DER, which is required
255
         * to have a specific order and that is what must be used for
256
         * the hash/signature.  We do this here, rather than building
257
         * it into EncodeAttributes, because we do not want to do
258
         * such reordering on incoming messages (which also uses
259
         * EncodeAttributes) or our old signatures (and other "broken"
260
         * implementations) will not verify.  So, we want to guarantee
261
         * that we send out good DER encodings of attributes, but not
262
         * to expect to receive them.
263
         */
264
0
        if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
265
0
            goto loser;
266
267
0
        encoded_attrs.data = NULL;
268
0
        encoded_attrs.len = 0;
269
0
        if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr),
270
0
                                         &encoded_attrs) == NULL)
271
0
            goto loser;
272
273
0
        rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len,
274
0
                          privkey, signAlgTag);
275
0
        PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
276
0
        tmppoolp = 0;
277
0
    } else {
278
0
        rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
279
0
    }
280
0
    SECKEY_DestroyPrivateKey(privkey);
281
0
    privkey = NULL;
282
283
0
    if (rv != SECSuccess)
284
0
        goto loser;
285
286
0
    if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
287
0
        goto loser;
288
289
0
    SECITEM_FreeItem(&signature, PR_FALSE);
290
291
0
    return SECSuccess;
292
293
0
loser:
294
0
    if (signature.len != 0)
295
0
        SECITEM_FreeItem(&signature, PR_FALSE);
296
0
    if (privkey)
297
0
        SECKEY_DestroyPrivateKey(privkey);
298
0
    if (tmppoolp)
299
0
        PORT_FreeArena(tmppoolp, PR_FALSE);
300
0
    return SECFailure;
301
0
}
302
303
SECStatus
304
NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
305
                                    SECCertUsage certusage)
306
0
{
307
0
    CERTCertificate *cert;
308
0
    PRTime stime;
309
310
0
    if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
311
0
        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
312
0
        return SECFailure;
313
0
    }
314
315
    /*
316
     * Get and convert the signing time; if available, it will be used
317
     * both on the cert verification and for importing the sender
318
     * email profile.
319
     */
320
0
    if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess)
321
0
        stime = PR_Now(); /* not found or conversion failed, so check against now */
322
323
    /*
324
     * XXX  This uses the signing time, if available.  Additionally, we
325
     * might want to, if there is no signing time, get the message time
326
     * from the mail header itself, and use that.  That would require
327
     * a change to our interface though, and for S/MIME callers to pass
328
     * in a time (and for non-S/MIME callers to pass in nothing, or
329
     * maybe make them pass in the current time, always?).
330
     */
331
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime,
332
0
                        signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
333
0
        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
334
0
        return SECFailure;
335
0
    }
336
0
    return SECSuccess;
337
0
}
338
339
/*
340
 * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
341
 *
342
 * Just verifies the signature. The assumption is that verification of
343
 * the certificate is done already.
344
 */
345
SECStatus
346
NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo,
347
                         SECItem *digest,      /* may be NULL */
348
                         SECItem *contentType) /* may be NULL */
349
0
{
350
0
    SECKEYPublicKey *publickey = NULL;
351
0
    NSSCMSAttribute *attr;
352
0
    SECItem encoded_attrs;
353
0
    CERTCertificate *cert;
354
0
    NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
355
0
    PLArenaPool *poolp;
356
0
    SECOidTag digestalgtag;
357
0
    SECOidTag pubkAlgTag;
358
0
    SECOidTag digestalgtagCmp;
359
0
    SECOidTag sigAlgTag;
360
361
0
    if (signerinfo == NULL)
362
0
        return SECFailure;
363
364
    /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL
365
    ** and cert has not been verified
366
    */
367
0
    cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
368
0
    if (cert == NULL) {
369
0
        vs = NSSCMSVS_SigningCertNotFound;
370
0
        goto loser;
371
0
    }
372
373
0
    if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
374
0
        vs = NSSCMSVS_ProcessingError;
375
0
        goto loser;
376
0
    }
377
378
0
    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
379
0
    pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
380
0
    sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
381
0
    if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) ||
382
0
        (sigAlgTag == SEC_OID_UNKNOWN)) {
383
0
        vs = NSSCMSVS_SignatureAlgorithmUnknown;
384
0
        goto loser;
385
0
    }
386
0
    if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) {
387
0
        vs = NSSCMSVS_SignatureAlgorithmUnsupported;
388
0
        goto loser;
389
0
    }
390
391
0
    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
392
0
        if (contentType) {
393
            /*
394
             * Check content type
395
             *
396
             * RFC2630 sez that if there are any authenticated attributes,
397
             * then there must be one for content type which matches the
398
             * content type of the content being signed, and there must
399
             * be one for message digest which matches our message digest.
400
             * So check these things first.
401
             */
402
0
            attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
403
0
                                                          SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
404
0
            if (attr == NULL) {
405
0
                vs = NSSCMSVS_MalformedSignature;
406
0
                goto loser;
407
0
            }
408
409
0
            if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
410
0
                vs = NSSCMSVS_MalformedSignature;
411
0
                goto loser;
412
0
            }
413
0
        }
414
415
        /*
416
         * Check digest
417
         */
418
0
        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
419
0
                                                      SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
420
0
        if (attr == NULL) {
421
0
            vs = NSSCMSVS_MalformedSignature;
422
0
            goto loser;
423
0
        }
424
0
        if (!digest ||
425
0
            NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
426
0
            vs = NSSCMSVS_DigestMismatch;
427
0
            goto loser;
428
0
        }
429
430
0
        if ((poolp = PORT_NewArena(1024)) == NULL) {
431
0
            vs = NSSCMSVS_ProcessingError;
432
0
            goto loser;
433
0
        }
434
435
        /*
436
         * Check signature
437
         *
438
         * The signature is based on a digest of the DER-encoded authenticated
439
         * attributes.  So, first we encode and then we digest/verify.
440
         * we trust the decoder to have the attributes in the right (sorted)
441
         * order
442
         */
443
0
        encoded_attrs.data = NULL;
444
0
        encoded_attrs.len = 0;
445
446
0
        if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr),
447
0
                                         &encoded_attrs) == NULL ||
448
0
            encoded_attrs.data == NULL || encoded_attrs.len == 0) {
449
0
            PORT_FreeArena(poolp, PR_FALSE);
450
0
            vs = NSSCMSVS_ProcessingError;
451
0
            goto loser;
452
0
        }
453
454
0
        if (sigAlgTag == pubkAlgTag) {
455
            /* This is to handle cases in which signatureAlgorithm field
456
             * specifies the public key algorithm rather than a signature
457
             * algorithm. */
458
0
            vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
459
0
                                       publickey, &(signerinfo->encDigest), pubkAlgTag,
460
0
                                       digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess)
461
0
                     ? NSSCMSVS_BadSignature
462
0
                     : NSSCMSVS_GoodSignature;
463
0
        } else {
464
0
            if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data,
465
0
                                              encoded_attrs.len, publickey, &(signerinfo->encDigest),
466
0
                                              &(signerinfo->digestEncAlg), &digestalgtagCmp,
467
0
                                              signerinfo->cmsg->pwfn_arg) != SECSuccess) {
468
0
                vs = NSSCMSVS_BadSignature;
469
0
            } else if (digestalgtagCmp != digestalgtag) {
470
0
                PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
471
0
                vs = NSSCMSVS_BadSignature;
472
0
            } else {
473
0
                vs = NSSCMSVS_GoodSignature;
474
0
            }
475
0
        }
476
477
0
        PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
478
479
0
    } else {
480
0
        SECItem *sig;
481
482
        /* No authenticated attributes.
483
         ** The signature is based on the plain message digest.
484
         */
485
0
        sig = &(signerinfo->encDigest);
486
0
        if (sig->len == 0)
487
0
            goto loser;
488
489
0
        if (sigAlgTag == pubkAlgTag) {
490
            /* This is to handle cases in which signatureAlgorithm field
491
             * specifies the public key algorithm rather than a signature
492
             * algorithm. */
493
0
            vs = (!digest ||
494
0
                  VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
495
0
                                         digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess)
496
0
                     ? NSSCMSVS_BadSignature
497
0
                     : NSSCMSVS_GoodSignature;
498
0
        } else {
499
0
            vs = (!digest ||
500
0
                  VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig,
501
0
                                                  &(signerinfo->digestEncAlg), digestalgtag,
502
0
                                                  signerinfo->cmsg->pwfn_arg) != SECSuccess)
503
0
                     ? NSSCMSVS_BadSignature
504
0
                     : NSSCMSVS_GoodSignature;
505
0
        }
506
0
    }
507
508
0
    if (vs == NSSCMSVS_BadSignature) {
509
0
        int error = PORT_GetError();
510
        /*
511
         * XXX Change the generic error into our specific one, because
512
         * in that case we get a better explanation out of the Security
513
         * Advisor.  This is really a bug in the PSM error strings (the
514
         * "generic" error has a lousy/wrong message associated with it
515
         * which assumes the signature verification was done for the
516
         * purposes of checking the issuer signature on a certificate)
517
         * but this is at least an easy workaround and/or in the
518
         * Security Advisor, which specifically checks for the error
519
         * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
520
         * in that case but does not similarly check for
521
         * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
522
         * probably say the wrong thing in the case that it *was* the
523
         * certificate signature check that failed during the cert
524
         * verification done above.  Our error handling is really a mess.
525
         */
526
0
        if (error == SEC_ERROR_BAD_SIGNATURE)
527
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
528
        /*
529
         * map algorithm failures to NSSCMSVS values
530
         */
531
0
        if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
532
0
            (error == SEC_ERROR_INVALID_ALGORITHM)) {
533
            /* keep the same error code as 3.11 and before */
534
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
535
0
            vs = NSSCMSVS_SignatureAlgorithmUnsupported;
536
0
        }
537
0
    }
538
539
0
    if (publickey != NULL)
540
0
        SECKEY_DestroyPublicKey(publickey);
541
542
0
    signerinfo->verificationStatus = vs;
543
544
0
    return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
545
546
0
loser:
547
0
    if (publickey != NULL)
548
0
        SECKEY_DestroyPublicKey(publickey);
549
550
0
    signerinfo->verificationStatus = vs;
551
552
0
    PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
553
0
    return SECFailure;
554
0
}
555
556
NSSCMSVerificationStatus
557
NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
558
0
{
559
0
    return signerinfo->verificationStatus;
560
0
}
561
562
SECOidData *
563
NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
564
0
{
565
0
    SECOidData *algdata;
566
0
    SECOidTag algtag;
567
568
0
    algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm));
569
0
    if (algdata == NULL) {
570
0
        return algdata;
571
0
    }
572
    /* Windows may have given us a signer algorithm oid instead of a digest
573
     * algorithm oid. This call will map to a signer oid to a digest one,
574
     * otherwise it leaves the oid alone and let the chips fall as they may
575
     * if it's not a digest oid.
576
     */
577
0
    algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
578
0
    if (algtag != algdata->offset) {
579
        /* if the tags don't match, then we must have received a signer
580
         * algorithID. Now we need to get the oid data for the digest
581
         * oid, which the rest of the code is expecting */
582
0
        algdata = SECOID_FindOIDByTag(algtag);
583
0
    }
584
585
0
    return algdata;
586
0
}
587
588
SECOidTag
589
NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
590
0
{
591
0
    SECOidData *algdata;
592
593
0
    if (!signerinfo) {
594
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
595
0
        return SEC_OID_UNKNOWN;
596
0
    }
597
598
0
    algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
599
0
    if (algdata != NULL)
600
0
        return algdata->offset;
601
0
    else
602
0
        return SEC_OID_UNKNOWN;
603
0
}
604
605
CERTCertificateList *
606
NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
607
0
{
608
0
    return signerinfo->certList;
609
0
}
610
611
int
612
NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
613
0
{
614
0
    unsigned long version;
615
616
    /* always take apart the SECItem */
617
0
    if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
618
0
        return 0;
619
0
    else
620
0
        return (int)version;
621
0
}
622
623
/*
624
 * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
625
 *                    in UTCTime or GeneralizedTime format,
626
 *                                    of a CMS signerInfo.
627
 *
628
 * sinfo - signerInfo data for this signer
629
 *
630
 * Returns a pointer to XXXX (what?)
631
 * A return value of NULL is an error.
632
 */
633
SECStatus
634
NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
635
0
{
636
0
    NSSCMSAttribute *attr;
637
0
    SECItem *value;
638
639
0
    if (sinfo == NULL)
640
0
        return SECFailure;
641
642
0
    if (sinfo->signingTime != 0) {
643
0
        *stime = sinfo->signingTime; /* cached copy */
644
0
        return SECSuccess;
645
0
    }
646
647
0
    attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr,
648
0
                                                  SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
649
    /* XXXX multi-valued attributes NIH */
650
0
    if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
651
0
        return SECFailure;
652
0
    if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
653
0
        return SECFailure;
654
0
    sinfo->signingTime = *stime; /* make cached copy */
655
0
    return SECSuccess;
656
0
}
657
658
/*
659
 * Return the signing cert of a CMS signerInfo.
660
 *
661
 * the certs in the enclosing SignedData must have been imported already
662
 */
663
CERTCertificate *
664
NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
665
0
{
666
0
    CERTCertificate *cert;
667
0
    NSSCMSSignerIdentifier *sid;
668
669
0
    if (signerinfo->cert != NULL)
670
0
        return signerinfo->cert;
671
672
    /* no certdb, and cert hasn't been set yet? */
673
0
    if (certdb == NULL)
674
0
        return NULL;
675
676
    /*
677
     * This cert will also need to be freed, but since we save it
678
     * in signerinfo for later, we do not want to destroy it when
679
     * we leave this function -- we let the clean-up of the entire
680
     * cinfo structure later do the destroy of this cert.
681
     */
682
0
    sid = &signerinfo->signerIdentifier;
683
0
    switch (sid->identifierType) {
684
0
        case NSSCMSSignerID_IssuerSN:
685
0
            cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
686
0
            break;
687
0
        case NSSCMSSignerID_SubjectKeyID:
688
0
            cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
689
0
            break;
690
0
        default:
691
0
            cert = NULL;
692
0
            break;
693
0
    }
694
695
    /* cert can be NULL at that point */
696
0
    signerinfo->cert = cert; /* earmark it */
697
698
0
    return cert;
699
0
}
700
701
/*
702
 * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
703
 *
704
 * sinfo - signerInfo data for this signer
705
 *
706
 * Returns a pointer to allocated memory, which must be freed with PORT_Free.
707
 * A return value of NULL is an error.
708
 */
709
char *
710
NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
711
0
{
712
0
    CERTCertificate *signercert;
713
714
    /* will fail if cert is not verified */
715
0
    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
716
0
        return NULL;
717
718
0
    return (CERT_GetCommonName(&signercert->subject));
719
0
}
720
721
/*
722
 * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
723
 *
724
 * sinfo - signerInfo data for this signer
725
 *
726
 * Returns a pointer to allocated memory, which must be freed.
727
 * A return value of NULL is an error.
728
 */
729
char *
730
NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
731
0
{
732
0
    CERTCertificate *signercert;
733
734
0
    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
735
0
        return NULL;
736
737
0
    if (!signercert->emailAddr || !signercert->emailAddr[0])
738
0
        return NULL;
739
740
0
    return (PORT_Strdup(signercert->emailAddr));
741
0
}
742
743
/*
744
 * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
745
 * authenticated (i.e. signed) attributes of "signerinfo".
746
 */
747
SECStatus
748
NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
749
0
{
750
0
    return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
751
0
}
752
753
/*
754
 * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
755
 * unauthenticated attributes of "signerinfo".
756
 */
757
SECStatus
758
NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
759
0
{
760
0
    return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
761
0
}
762
763
/*
764
 * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
765
 * authenticated (i.e. signed) attributes of "signerinfo".
766
 *
767
 * This is expected to be included in outgoing signed
768
 * messages for email (S/MIME) but is likely useful in other situations.
769
 *
770
 * This should only be added once; a second call will do nothing.
771
 *
772
 * XXX This will probably just shove the current time into "signerinfo"
773
 * but it will not actually get signed until the entire item is
774
 * processed for encoding.  Is this (expected to be small) delay okay?
775
 */
776
SECStatus
777
NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
778
0
{
779
0
    NSSCMSAttribute *attr;
780
0
    SECItem stime;
781
0
    void *mark;
782
0
    PLArenaPool *poolp;
783
784
0
    poolp = signerinfo->cmsg->poolp;
785
786
0
    mark = PORT_ArenaMark(poolp);
787
788
    /* create new signing time attribute */
789
0
    if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
790
0
        goto loser;
791
792
0
    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
793
0
        SECITEM_FreeItem(&stime, PR_FALSE);
794
0
        goto loser;
795
0
    }
796
797
0
    SECITEM_FreeItem(&stime, PR_FALSE);
798
799
0
    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
800
0
        goto loser;
801
802
0
    PORT_ArenaUnmark(poolp, mark);
803
804
0
    return SECSuccess;
805
806
0
loser:
807
0
    PORT_ArenaRelease(poolp, mark);
808
0
    return SECFailure;
809
0
}
810
811
/*
812
 * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
813
 * authenticated (i.e. signed) attributes of "signerinfo".
814
 *
815
 * This is expected to be included in outgoing signed
816
 * messages for email (S/MIME).
817
 */
818
SECStatus
819
NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
820
0
{
821
0
    NSSCMSAttribute *attr;
822
0
    SECItem *smimecaps = NULL;
823
0
    void *mark;
824
0
    PLArenaPool *poolp;
825
826
0
    poolp = signerinfo->cmsg->poolp;
827
828
0
    mark = PORT_ArenaMark(poolp);
829
830
0
    smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
831
0
    if (smimecaps == NULL)
832
0
        goto loser;
833
834
    /* create new signing time attribute */
835
0
    if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
836
0
        goto loser;
837
838
0
    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
839
0
        goto loser;
840
841
0
    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
842
0
        goto loser;
843
844
0
    PORT_ArenaUnmark(poolp, mark);
845
0
    return SECSuccess;
846
847
0
loser:
848
0
    PORT_ArenaRelease(poolp, mark);
849
0
    return SECFailure;
850
0
}
851
852
/*
853
 * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
854
 * authenticated (i.e. signed) attributes of "signerinfo".
855
 *
856
 * This is expected to be included in outgoing signed messages for email (S/MIME).
857
 */
858
SECStatus
859
NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
860
0
{
861
0
    NSSCMSAttribute *attr;
862
0
    SECItem *smimeekp = NULL;
863
0
    void *mark;
864
0
    PLArenaPool *poolp;
865
866
    /* verify this cert for encryption */
867
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
868
0
        return SECFailure;
869
0
    }
870
871
0
    poolp = signerinfo->cmsg->poolp;
872
0
    mark = PORT_ArenaMark(poolp);
873
874
0
    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
875
0
    if (smimeekp == NULL)
876
0
        goto loser;
877
878
    /* create new signing time attribute */
879
0
    if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
880
0
        goto loser;
881
882
0
    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
883
0
        goto loser;
884
885
0
    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
886
0
        goto loser;
887
888
0
    PORT_ArenaUnmark(poolp, mark);
889
0
    return SECSuccess;
890
891
0
loser:
892
0
    PORT_ArenaRelease(poolp, mark);
893
0
    return SECFailure;
894
0
}
895
896
/*
897
 * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
898
 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
899
 *
900
 * This is expected to be included in outgoing signed messages for email (S/MIME),
901
 * if compatibility with Microsoft mail clients is wanted.
902
 */
903
SECStatus
904
NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
905
0
{
906
0
    NSSCMSAttribute *attr;
907
0
    SECItem *smimeekp = NULL;
908
0
    void *mark;
909
0
    PLArenaPool *poolp;
910
911
    /* verify this cert for encryption */
912
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
913
0
        return SECFailure;
914
0
    }
915
916
0
    poolp = signerinfo->cmsg->poolp;
917
0
    mark = PORT_ArenaMark(poolp);
918
919
0
    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
920
0
    if (smimeekp == NULL)
921
0
        goto loser;
922
923
    /* create new signing time attribute */
924
0
    if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
925
0
        goto loser;
926
927
0
    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
928
0
        goto loser;
929
930
0
    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
931
0
        goto loser;
932
933
0
    PORT_ArenaUnmark(poolp, mark);
934
0
    return SECSuccess;
935
936
0
loser:
937
0
    PORT_ArenaRelease(poolp, mark);
938
0
    return SECFailure;
939
0
}
940
941
/*
942
 * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
943
 *
944
 * 1. digest the DER-encoded signature value of the original signerinfo
945
 * 2. create new signerinfo with correct version, sid, digestAlg
946
 * 3. add message-digest authAttr, but NO content-type
947
 * 4. sign the authAttrs
948
 * 5. DER-encode the new signerInfo
949
 * 6. add the whole thing to original signerInfo's unAuthAttrs
950
 *    as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
951
 *
952
 * XXXX give back the new signerinfo?
953
 */
954
SECStatus
955
NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
956
                                      SECOidTag digestalg, CERTCertificate signingcert)
957
0
{
958
    /* XXXX TBD XXXX */
959
0
    return SECFailure;
960
0
}
961
962
/*
963
 * XXXX the following needs to be done in the S/MIME layer code
964
 * after signature of a signerinfo is verified
965
 */
966
SECStatus
967
NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
968
0
{
969
0
    CERTCertificate *cert = NULL;
970
0
    SECItem *profile = NULL;
971
0
    NSSCMSAttribute *attr;
972
0
    SECItem *stime = NULL;
973
0
    SECItem *ekp;
974
0
    CERTCertDBHandle *certdb;
975
0
    int save_error;
976
0
    SECStatus rv;
977
0
    PRBool must_free_cert = PR_FALSE;
978
979
0
    certdb = CERT_GetDefaultCertDB();
980
981
    /* sanity check - see if verification status is ok (unverified does not count...) */
982
0
    if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
983
0
        return SECFailure;
984
985
    /* find preferred encryption cert */
986
0
    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
987
0
        (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
988
0
                                                       SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
989
0
        ekp = NSS_CMSAttribute_GetValue(attr);
990
0
        if (ekp == NULL)
991
0
            return SECFailure;
992
993
        /* we assume that all certs coming with the message have been imported to the */
994
        /* temporary database */
995
0
        cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
996
0
        if (cert == NULL)
997
0
            return SECFailure;
998
0
        must_free_cert = PR_TRUE;
999
0
    }
1000
1001
0
    if (cert == NULL) {
1002
        /* no preferred cert found?
1003
         * find the cert the signerinfo is signed with instead */
1004
0
        cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
1005
0
        if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
1006
0
            return SECFailure;
1007
0
    }
1008
1009
/* verify this cert for encryption (has been verified for signing so far) */
1010
/* don't verify this cert for encryption. It may just be a signing cert.
1011
 * that's OK, we can still save the S/MIME profile. The encryption cert
1012
 * should have already been saved */
1013
#ifdef notdef
1014
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
1015
        if (must_free_cert)
1016
            CERT_DestroyCertificate(cert);
1017
        return SECFailure;
1018
    }
1019
#endif
1020
1021
    /* XXX store encryption cert permanently? */
1022
1023
    /*
1024
     * Remember the current error set because we do not care about
1025
     * anything set by the functions we are about to call.
1026
     */
1027
0
    save_error = PORT_GetError();
1028
1029
0
    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
1030
0
        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
1031
0
                                                      SEC_OID_PKCS9_SMIME_CAPABILITIES,
1032
0
                                                      PR_TRUE);
1033
0
        profile = NSS_CMSAttribute_GetValue(attr);
1034
0
        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
1035
0
                                                      SEC_OID_PKCS9_SIGNING_TIME,
1036
0
                                                      PR_TRUE);
1037
0
        stime = NSS_CMSAttribute_GetValue(attr);
1038
0
    }
1039
1040
0
    rv = CERT_SaveSMimeProfile(cert, profile, stime);
1041
0
    if (must_free_cert)
1042
0
        CERT_DestroyCertificate(cert);
1043
1044
    /*
1045
     * Restore the saved error in case the calls above set a new
1046
     * one that we do not actually care about.
1047
     */
1048
0
    PORT_SetError(save_error);
1049
1050
0
    return rv;
1051
0
}
1052
1053
/*
1054
 * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
1055
 */
1056
SECStatus
1057
NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo,
1058
                               NSSCMSCertChainMode cm, SECCertUsage usage)
1059
0
{
1060
0
    if (signerinfo->cert == NULL)
1061
0
        return SECFailure;
1062
1063
    /* don't leak if we get called twice */
1064
0
    if (signerinfo->certList != NULL) {
1065
0
        CERT_DestroyCertificateList(signerinfo->certList);
1066
0
        signerinfo->certList = NULL;
1067
0
    }
1068
1069
0
    switch (cm) {
1070
0
        case NSSCMSCM_None:
1071
0
            signerinfo->certList = NULL;
1072
0
            break;
1073
0
        case NSSCMSCM_CertOnly:
1074
0
            signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1075
0
            break;
1076
0
        case NSSCMSCM_CertChain:
1077
0
            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
1078
0
                                                          usage, PR_FALSE);
1079
0
            break;
1080
0
        case NSSCMSCM_CertChainWithRoot:
1081
0
            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
1082
0
                                                          usage, PR_TRUE);
1083
0
            break;
1084
0
    }
1085
1086
0
    if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
1087
0
        return SECFailure;
1088
1089
0
    return SECSuccess;
1090
0
}