tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cmssiginfo.c (36643B)


      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 {
     37    return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL,
     38                                    subjKeyID, pubKey, signingKey, digestalgtag);
     39 }
     40 
     41 NSSCMSSignerInfo *
     42 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
     43 {
     44    return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL,
     45                                    NULL, NULL, digestalgtag);
     46 }
     47 
     48 NSSCMSSignerInfo *
     49 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
     50                         CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
     51                         SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
     52 {
     53    void *mark;
     54    NSSCMSSignerInfo *signerinfo;
     55    int version;
     56    PLArenaPool *poolp;
     57    SECStatus rv;
     58 
     59    poolp = cmsg->poolp;
     60 
     61    mark = PORT_ArenaMark(poolp);
     62 
     63    signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
     64    if (signerinfo == NULL) {
     65        PORT_ArenaRelease(poolp, mark);
     66        return NULL;
     67    }
     68 
     69    signerinfo->cmsg = cmsg;
     70 
     71    switch (type) {
     72        case NSSCMSSignerID_IssuerSN:
     73            signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
     74            if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
     75                goto loser;
     76            if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
     77                goto loser;
     78            break;
     79        case NSSCMSSignerID_SubjectKeyID:
     80            signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
     81            PORT_Assert(subjKeyID);
     82            if (!subjKeyID)
     83                goto loser;
     84 
     85            signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
     86            rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
     87                                  subjKeyID);
     88            if (rv != SECSuccess) {
     89                goto loser;
     90            }
     91            signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
     92            if (!signerinfo->signingKey)
     93                goto loser;
     94            signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
     95            if (!signerinfo->pubKey)
     96                goto loser;
     97            break;
     98        default:
     99            goto loser;
    100    }
    101 
    102    /* set version right now */
    103    version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
    104    /* RFC2630 5.3 "version is the syntax version number. If the .... " */
    105    if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
    106        version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
    107    (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
    108 
    109    if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
    110        goto loser;
    111 
    112    PORT_ArenaUnmark(poolp, mark);
    113    return signerinfo;
    114 
    115 loser:
    116    PORT_ArenaRelease(poolp, mark);
    117    return NULL;
    118 }
    119 
    120 /*
    121 * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
    122 */
    123 void
    124 NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
    125 {
    126    if (si->cert != NULL)
    127        CERT_DestroyCertificate(si->cert);
    128 
    129    if (si->certList != NULL)
    130        CERT_DestroyCertificateList(si->certList);
    131 
    132    /* XXX storage ??? */
    133 }
    134 static SECOidTag
    135 NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,
    136                                              SECOidTag pubkAlgTag,
    137                                              SECOidTag signAlgTag)
    138 {
    139    switch (keyType) {
    140        case rsaKey:
    141            return pubkAlgTag;
    142        case rsaPssKey:
    143        case dsaKey:
    144        case ecKey:
    145            return signAlgTag;
    146        default:
    147            return SEC_OID_UNKNOWN;
    148    }
    149 }
    150 
    151 /*
    152 * NSS_CMSSignerInfo_Sign - sign something
    153 *
    154 */
    155 SECStatus
    156 NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest,
    157                       SECItem *contentType)
    158 {
    159    CERTCertificate *cert;
    160    SECKEYPrivateKey *privkey = NULL;
    161    SECOidTag digestalgtag;
    162    SECOidTag pubkAlgTag;
    163    SECOidTag signAlgTag;
    164    SECOidTag cmsSignAlgTag;
    165    SECItem signature = { 0 };
    166    SECStatus rv;
    167    PLArenaPool *poolp, *tmppoolp = NULL;
    168    SECAlgorithmID *algID, freeAlgID;
    169    CERTSubjectPublicKeyInfo *spki;
    170 
    171    PORT_Assert(digest != NULL);
    172 
    173    poolp = signerinfo->cmsg->poolp;
    174 
    175    switch (signerinfo->signerIdentifier.identifierType) {
    176        case NSSCMSSignerID_IssuerSN:
    177            cert = signerinfo->cert;
    178 
    179            privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
    180            if (privkey == NULL)
    181                goto loser;
    182            algID = &cert->subjectPublicKeyInfo.algorithm;
    183            break;
    184        case NSSCMSSignerID_SubjectKeyID:
    185            privkey = signerinfo->signingKey;
    186            signerinfo->signingKey = NULL;
    187            spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
    188            SECKEY_DestroyPublicKey(signerinfo->pubKey);
    189            signerinfo->pubKey = NULL;
    190            SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
    191            SECKEY_DestroySubjectPublicKeyInfo(spki);
    192            algID = &freeAlgID;
    193            break;
    194        default:
    195            goto loser;
    196    }
    197    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    pubkAlgTag = SECOID_GetAlgorithmTag(algID);
    203    if (algID == &freeAlgID) {
    204        SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
    205    }
    206 
    207    signAlgTag = SEC_GetSignatureAlgorithmOidTagByKey(privkey, NULL,
    208                                                      digestalgtag);
    209    if (signAlgTag == SEC_OID_UNKNOWN) {
    210        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    211        goto loser;
    212    }
    213 
    214    cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(
    215        SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag);
    216    if (cmsSignAlgTag == SEC_OID_UNKNOWN) {
    217        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    218        goto loser;
    219    }
    220 
    221    if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
    222                              cmsSignAlgTag, NULL) != SECSuccess)
    223        goto loser;
    224 
    225    if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) {
    226        PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    227        goto loser;
    228    }
    229 
    230    if (signerinfo->authAttr != NULL) {
    231        SECItem encoded_attrs;
    232 
    233        /* find and fill in the message digest attribute. */
    234        rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
    235                                           SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
    236        if (rv != SECSuccess)
    237            goto loser;
    238 
    239        if (contentType != NULL) {
    240            /* if the caller wants us to, find and fill in the content type attribute. */
    241            rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
    242                                               SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
    243            if (rv != SECSuccess)
    244                goto loser;
    245        }
    246 
    247        if ((tmppoolp = PORT_NewArena(1024)) == NULL) {
    248            PORT_SetError(SEC_ERROR_NO_MEMORY);
    249            goto loser;
    250        }
    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        if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
    265            goto loser;
    266 
    267        encoded_attrs.data = NULL;
    268        encoded_attrs.len = 0;
    269        if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr),
    270                                         &encoded_attrs) == NULL)
    271            goto loser;
    272 
    273        rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len,
    274                          privkey, signAlgTag);
    275        PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
    276        tmppoolp = 0;
    277    } else {
    278        rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
    279    }
    280    SECKEY_DestroyPrivateKey(privkey);
    281    privkey = NULL;
    282 
    283    if (rv != SECSuccess)
    284        goto loser;
    285 
    286    if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
    287        goto loser;
    288 
    289    SECITEM_FreeItem(&signature, PR_FALSE);
    290 
    291    return SECSuccess;
    292 
    293 loser:
    294    if (signature.len != 0)
    295        SECITEM_FreeItem(&signature, PR_FALSE);
    296    if (privkey)
    297        SECKEY_DestroyPrivateKey(privkey);
    298    if (tmppoolp)
    299        PORT_FreeArena(tmppoolp, PR_FALSE);
    300    return SECFailure;
    301 }
    302 
    303 SECStatus
    304 NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
    305                                    SECCertUsage certusage)
    306 {
    307    CERTCertificate *cert;
    308    PRTime stime;
    309 
    310    if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
    311        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
    312        return SECFailure;
    313    }
    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    if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess)
    321        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    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime,
    332                        signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
    333        signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
    334        return SECFailure;
    335    }
    336    return SECSuccess;
    337 }
    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 {
    350    SECKEYPublicKey *publickey = NULL;
    351    NSSCMSAttribute *attr;
    352    SECItem encoded_attrs;
    353    CERTCertificate *cert;
    354    NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
    355    PLArenaPool *poolp;
    356    SECOidTag digestalgtag;
    357    SECOidTag pubkAlgTag;
    358    SECOidTag digestalgtagCmp;
    359    SECOidTag sigAlgTag;
    360 
    361    if (signerinfo == NULL)
    362        return SECFailure;
    363 
    364    /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL
    365    ** and cert has not been verified
    366    */
    367    cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
    368    if (cert == NULL) {
    369        vs = NSSCMSVS_SigningCertNotFound;
    370        goto loser;
    371    }
    372 
    373    if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
    374        vs = NSSCMSVS_ProcessingError;
    375        goto loser;
    376    }
    377 
    378    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    379    pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
    380    sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
    381    if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) ||
    382        (sigAlgTag == SEC_OID_UNKNOWN)) {
    383        vs = NSSCMSVS_SignatureAlgorithmUnknown;
    384        goto loser;
    385    }
    386    if (!NSS_SMIMEUtil_SigningAllowed(&signerinfo->digestEncAlg)) {
    387        vs = NSSCMSVS_SignatureAlgorithmUnsupported;
    388        goto loser;
    389    }
    390 
    391    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
    392        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            attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
    403                                                          SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
    404            if (attr == NULL) {
    405                vs = NSSCMSVS_MalformedSignature;
    406                goto loser;
    407            }
    408 
    409            if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
    410                vs = NSSCMSVS_MalformedSignature;
    411                goto loser;
    412            }
    413        }
    414 
    415        /*
    416         * Check digest
    417         */
    418        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
    419                                                      SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
    420        if (attr == NULL) {
    421            vs = NSSCMSVS_MalformedSignature;
    422            goto loser;
    423        }
    424        if (!digest ||
    425            NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
    426            vs = NSSCMSVS_DigestMismatch;
    427            goto loser;
    428        }
    429 
    430        if ((poolp = PORT_NewArena(1024)) == NULL) {
    431            vs = NSSCMSVS_ProcessingError;
    432            goto loser;
    433        }
    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        encoded_attrs.data = NULL;
    444        encoded_attrs.len = 0;
    445 
    446        if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr),
    447                                         &encoded_attrs) == NULL ||
    448            encoded_attrs.data == NULL || encoded_attrs.len == 0) {
    449            PORT_FreeArena(poolp, PR_FALSE);
    450            vs = NSSCMSVS_ProcessingError;
    451            goto loser;
    452        }
    453 
    454        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            vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
    459                                       publickey, &(signerinfo->encDigest), pubkAlgTag,
    460                                       digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess)
    461                     ? NSSCMSVS_BadSignature
    462                     : NSSCMSVS_GoodSignature;
    463        } else {
    464            if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data,
    465                                              encoded_attrs.len, publickey, &(signerinfo->encDigest),
    466                                              &(signerinfo->digestEncAlg), &digestalgtagCmp,
    467                                              signerinfo->cmsg->pwfn_arg) != SECSuccess) {
    468                vs = NSSCMSVS_BadSignature;
    469            } else if (digestalgtagCmp != digestalgtag) {
    470                PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
    471                vs = NSSCMSVS_BadSignature;
    472            } else {
    473                vs = NSSCMSVS_GoodSignature;
    474            }
    475        }
    476 
    477        PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
    478 
    479    } else {
    480        SECItem *sig;
    481 
    482        /* No authenticated attributes.
    483         ** The signature is based on the plain message digest.
    484         */
    485        sig = &(signerinfo->encDigest);
    486        if (sig->len == 0)
    487            goto loser;
    488 
    489        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            vs = (!digest ||
    494                  VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
    495                                         digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess)
    496                     ? NSSCMSVS_BadSignature
    497                     : NSSCMSVS_GoodSignature;
    498        } else {
    499            vs = (!digest ||
    500                  VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig,
    501                                                  &(signerinfo->digestEncAlg), digestalgtag,
    502                                                  signerinfo->cmsg->pwfn_arg) != SECSuccess)
    503                     ? NSSCMSVS_BadSignature
    504                     : NSSCMSVS_GoodSignature;
    505        }
    506    }
    507 
    508    if (vs == NSSCMSVS_BadSignature) {
    509        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        if (error == SEC_ERROR_BAD_SIGNATURE)
    527            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
    528        /*
    529         * map algorithm failures to NSSCMSVS values
    530         */
    531        if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
    532            (error == SEC_ERROR_INVALID_ALGORITHM)) {
    533            /* keep the same error code as 3.11 and before */
    534            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
    535            vs = NSSCMSVS_SignatureAlgorithmUnsupported;
    536        }
    537    }
    538 
    539    if (publickey != NULL)
    540        SECKEY_DestroyPublicKey(publickey);
    541 
    542    signerinfo->verificationStatus = vs;
    543 
    544    return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
    545 
    546 loser:
    547    if (publickey != NULL)
    548        SECKEY_DestroyPublicKey(publickey);
    549 
    550    signerinfo->verificationStatus = vs;
    551 
    552    PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
    553    return SECFailure;
    554 }
    555 
    556 NSSCMSVerificationStatus
    557 NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
    558 {
    559    return signerinfo->verificationStatus;
    560 }
    561 
    562 SECOidData *
    563 NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
    564 {
    565    SECOidData *algdata;
    566    SECOidTag algtag;
    567 
    568    algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm));
    569    if (algdata == NULL) {
    570        return algdata;
    571    }
    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    algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
    578    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        algdata = SECOID_FindOIDByTag(algtag);
    583    }
    584 
    585    return algdata;
    586 }
    587 
    588 SECOidTag
    589 NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
    590 {
    591    SECOidData *algdata;
    592 
    593    if (!signerinfo) {
    594        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    595        return SEC_OID_UNKNOWN;
    596    }
    597 
    598    algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
    599    if (algdata != NULL)
    600        return algdata->offset;
    601    else
    602        return SEC_OID_UNKNOWN;
    603 }
    604 
    605 CERTCertificateList *
    606 NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
    607 {
    608    return signerinfo->certList;
    609 }
    610 
    611 int
    612 NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
    613 {
    614    unsigned long version;
    615 
    616    /* always take apart the SECItem */
    617    if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
    618        return 0;
    619    else
    620        return (int)version;
    621 }
    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 {
    636    NSSCMSAttribute *attr;
    637    SECItem *value;
    638 
    639    if (sinfo == NULL)
    640        return SECFailure;
    641 
    642    if (sinfo->signingTime != 0) {
    643        *stime = sinfo->signingTime; /* cached copy */
    644        return SECSuccess;
    645    }
    646 
    647    attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr,
    648                                                  SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
    649    /* XXXX multi-valued attributes NIH */
    650    if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
    651        return SECFailure;
    652    if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
    653        return SECFailure;
    654    sinfo->signingTime = *stime; /* make cached copy */
    655    return SECSuccess;
    656 }
    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 {
    666    CERTCertificate *cert;
    667    NSSCMSSignerIdentifier *sid;
    668 
    669    if (signerinfo->cert != NULL)
    670        return signerinfo->cert;
    671 
    672    /* no certdb, and cert hasn't been set yet? */
    673    if (certdb == NULL)
    674        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    sid = &signerinfo->signerIdentifier;
    683    switch (sid->identifierType) {
    684        case NSSCMSSignerID_IssuerSN:
    685            cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
    686            break;
    687        case NSSCMSSignerID_SubjectKeyID:
    688            cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
    689            break;
    690        default:
    691            cert = NULL;
    692            break;
    693    }
    694 
    695    /* cert can be NULL at that point */
    696    signerinfo->cert = cert; /* earmark it */
    697 
    698    return cert;
    699 }
    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 {
    712    CERTCertificate *signercert;
    713 
    714    /* will fail if cert is not verified */
    715    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
    716        return NULL;
    717 
    718    return (CERT_GetCommonName(&signercert->subject));
    719 }
    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 {
    732    CERTCertificate *signercert;
    733 
    734    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
    735        return NULL;
    736 
    737    if (!signercert->emailAddr || !signercert->emailAddr[0])
    738        return NULL;
    739 
    740    return (PORT_Strdup(signercert->emailAddr));
    741 }
    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 {
    750    return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
    751 }
    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 {
    760    return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
    761 }
    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 {
    779    NSSCMSAttribute *attr;
    780    SECItem stime;
    781    void *mark;
    782    PLArenaPool *poolp;
    783 
    784    poolp = signerinfo->cmsg->poolp;
    785 
    786    mark = PORT_ArenaMark(poolp);
    787 
    788    /* create new signing time attribute */
    789    if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
    790        goto loser;
    791 
    792    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
    793        SECITEM_FreeItem(&stime, PR_FALSE);
    794        goto loser;
    795    }
    796 
    797    SECITEM_FreeItem(&stime, PR_FALSE);
    798 
    799    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
    800        goto loser;
    801 
    802    PORT_ArenaUnmark(poolp, mark);
    803 
    804    return SECSuccess;
    805 
    806 loser:
    807    PORT_ArenaRelease(poolp, mark);
    808    return SECFailure;
    809 }
    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 {
    821    NSSCMSAttribute *attr;
    822    SECItem *smimecaps = NULL;
    823    void *mark;
    824    PLArenaPool *poolp;
    825 
    826    poolp = signerinfo->cmsg->poolp;
    827 
    828    mark = PORT_ArenaMark(poolp);
    829 
    830    smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
    831    if (smimecaps == NULL)
    832        goto loser;
    833 
    834    /* create new signing time attribute */
    835    if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
    836        goto loser;
    837 
    838    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
    839        goto loser;
    840 
    841    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
    842        goto loser;
    843 
    844    PORT_ArenaUnmark(poolp, mark);
    845    return SECSuccess;
    846 
    847 loser:
    848    PORT_ArenaRelease(poolp, mark);
    849    return SECFailure;
    850 }
    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 {
    861    NSSCMSAttribute *attr;
    862    SECItem *smimeekp = NULL;
    863    void *mark;
    864    PLArenaPool *poolp;
    865 
    866    /* verify this cert for encryption */
    867    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
    868        return SECFailure;
    869    }
    870 
    871    poolp = signerinfo->cmsg->poolp;
    872    mark = PORT_ArenaMark(poolp);
    873 
    874    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
    875    if (smimeekp == NULL)
    876        goto loser;
    877 
    878    /* create new signing time attribute */
    879    if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
    880        goto loser;
    881 
    882    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
    883        goto loser;
    884 
    885    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
    886        goto loser;
    887 
    888    PORT_ArenaUnmark(poolp, mark);
    889    return SECSuccess;
    890 
    891 loser:
    892    PORT_ArenaRelease(poolp, mark);
    893    return SECFailure;
    894 }
    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 {
    906    NSSCMSAttribute *attr;
    907    SECItem *smimeekp = NULL;
    908    void *mark;
    909    PLArenaPool *poolp;
    910 
    911    /* verify this cert for encryption */
    912    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
    913        return SECFailure;
    914    }
    915 
    916    poolp = signerinfo->cmsg->poolp;
    917    mark = PORT_ArenaMark(poolp);
    918 
    919    smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
    920    if (smimeekp == NULL)
    921        goto loser;
    922 
    923    /* create new signing time attribute */
    924    if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
    925        goto loser;
    926 
    927    if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
    928        goto loser;
    929 
    930    if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
    931        goto loser;
    932 
    933    PORT_ArenaUnmark(poolp, mark);
    934    return SECSuccess;
    935 
    936 loser:
    937    PORT_ArenaRelease(poolp, mark);
    938    return SECFailure;
    939 }
    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 {
    958    /* XXXX TBD XXXX */
    959    return SECFailure;
    960 }
    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 {
    969    CERTCertificate *cert = NULL;
    970    SECItem *profile = NULL;
    971    NSSCMSAttribute *attr;
    972    SECItem *stime = NULL;
    973    SECItem *ekp;
    974    CERTCertDBHandle *certdb;
    975    int save_error;
    976    SECStatus rv;
    977    PRBool must_free_cert = PR_FALSE;
    978 
    979    certdb = CERT_GetDefaultCertDB();
    980 
    981    /* sanity check - see if verification status is ok (unverified does not count...) */
    982    if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
    983        return SECFailure;
    984 
    985    /* find preferred encryption cert */
    986    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
    987        (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
    988                                                       SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
    989        ekp = NSS_CMSAttribute_GetValue(attr);
    990        if (ekp == NULL)
    991            return SECFailure;
    992 
    993        /* we assume that all certs coming with the message have been imported to the */
    994        /* temporary database */
    995        cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
    996        if (cert == NULL)
    997            return SECFailure;
    998        must_free_cert = PR_TRUE;
    999    }
   1000 
   1001    if (cert == NULL) {
   1002        /* no preferred cert found?
   1003         * find the cert the signerinfo is signed with instead */
   1004        cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
   1005        if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
   1006            return SECFailure;
   1007    }
   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    save_error = PORT_GetError();
   1028 
   1029    if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
   1030        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   1031                                                      SEC_OID_PKCS9_SMIME_CAPABILITIES,
   1032                                                      PR_TRUE);
   1033        profile = NSS_CMSAttribute_GetValue(attr);
   1034        attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
   1035                                                      SEC_OID_PKCS9_SIGNING_TIME,
   1036                                                      PR_TRUE);
   1037        stime = NSS_CMSAttribute_GetValue(attr);
   1038    }
   1039 
   1040    rv = CERT_SaveSMimeProfile(cert, profile, stime);
   1041    if (must_free_cert)
   1042        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    PORT_SetError(save_error);
   1049 
   1050    return rv;
   1051 }
   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 {
   1060    if (signerinfo->cert == NULL)
   1061        return SECFailure;
   1062 
   1063    /* don't leak if we get called twice */
   1064    if (signerinfo->certList != NULL) {
   1065        CERT_DestroyCertificateList(signerinfo->certList);
   1066        signerinfo->certList = NULL;
   1067    }
   1068 
   1069    switch (cm) {
   1070        case NSSCMSCM_None:
   1071            signerinfo->certList = NULL;
   1072            break;
   1073        case NSSCMSCM_CertOnly:
   1074            signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
   1075            break;
   1076        case NSSCMSCM_CertChain:
   1077            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
   1078                                                          usage, PR_FALSE);
   1079            break;
   1080        case NSSCMSCM_CertChainWithRoot:
   1081            signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
   1082                                                          usage, PR_TRUE);
   1083            break;
   1084    }
   1085 
   1086    if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
   1087        return SECFailure;
   1088 
   1089    return SECSuccess;
   1090 }