Coverage Report

Created: 2018-09-25 14:53

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