Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/security/nss/lib/certhigh/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 74 kB image not shown  

Quelle  certvfy.c   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nspr.h"
#include "secerr.h"
#include "secport.h"
#include "seccomon.h"
#include "secoid.h"
#include "genname.h"
#include "keyhi.h"
#include "cert.h"
#include "certdb.h"
#include "certi.h"
#include "cryptohi.h"

#ifndef NSS_DISABLE_LIBPKIX
#include "pkix.h"
#include "pkix_pl_cert.h"
#else
#include "nss.h"
#endif /* NSS_DISABLE_LIBPKIX */

#include "nsspki.h"
#include "pkitm.h"
#include "pkim.h"
#include "pki3hack.h"
#include "base.h"
#include "keyi.h"

/*
 * Check the validity times of a certificate
 */

SECStatus
CERT_CertTimesValid(CERTCertificate *c)
{
    SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE);
    return (valid == secCertTimeValid) ? SECSuccess : SECFailure;
}

static SECStatus
checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key)
{
    SECStatus rv;
    SECOidTag sigAlg;
    SECOidTag curve;
    PRUint32 policyFlags = 0;
    PRInt32 minLen, len, optFlags;

    sigAlg = SECOID_GetAlgorithmTag(sigAlgorithm);

    switch (sigAlg) {
        case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
        case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
        case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
        case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
        case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
            if (key->keyType != ecKey) {
                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
                return SECFailure;
            }

            curve = SECKEY_GetECCOid(&key->u.ec.DEREncodedParams);
            if (curve != 0) {
                if (NSS_GetAlgorithmPolicy(curve, &policyFlags) == SECFailure ||
                    !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
                    PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
                    return SECFailure;
                }
                return SECSuccess;
            }
            PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
            return SECFailure;

        case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: {
            PORTCheapArenaPool tmpArena;
            SECOidTag hashAlg;
            SECOidTag maskHashAlg;

            PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
            rv = sec_DecodeRSAPSSParams(&tmpArena.arena,
                                        &sigAlgorithm->parameters,
                                        &hashAlg, &maskHashAlg, NULL);
            PORT_DestroyCheapArena(&tmpArena);
            if (rv != SECSuccess) {
                return SECFailure;
            }

            if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess &&
                !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
                PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
                return SECFailure;
            }
            if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess &&
                !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
                PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
                return SECFailure;
            }
        }
        /* fall through to RSA key checking */
        case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
        case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
        case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
        case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
        case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
        case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
        case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
            if (key->keyType != rsaKey && key->keyType != rsaPssKey) {
                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
                return SECFailure;
            }

            if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) == SECFailure) {
                return SECSuccess;
            }
            if ((optFlags & NSS_KEY_SIZE_POLICY_VERIFY_FLAG) == 0) {
                return SECSuccess;
            }

            len = 8 * key->u.rsa.modulus.len;

            rv = NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minLen);
            if (rv != SECSuccess) {
                return SECFailure;
            }

            if (len < minLen) {
                return SECFailure;
            }

            return SECSuccess;
        case SEC_OID_ANSIX9_DSA_SIGNATURE:
        case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
        case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
        case SEC_OID_SDN702_DSA_SIGNATURE:
        case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
        case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST:
            if (key->keyType != dsaKey) {
                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
                return SECFailure;
            }
            if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) == SECFailure) {
                return SECSuccess;
            }
            if ((optFlags & NSS_KEY_SIZE_POLICY_VERIFY_FLAG) == 0) {
                return SECSuccess;
            }

            len = 8 * key->u.dsa.params.prime.len;

            rv = NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minLen);
            if (rv != SECSuccess) {
                return SECFailure;
            }

            if (len < minLen) {
                return SECFailure;
            }

            return SECSuccess;
        default:
            return SECSuccess;
    }
}

/*
 * verify the signature of a signed data object with the given DER publickey
 */

SECStatus
CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd,
                                   SECKEYPublicKey *pubKey,
                                   void *wincx)
{
    SECStatus rv;
    SECItem sig;
    SECOidTag sigAlg;
    SECOidTag encAlg;
    SECOidTag hashAlg;
    CK_MECHANISM_TYPE mech;
    PRUint32 policyFlags;

    if (!pubKey || !sd) {
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
        return SECFailure;
    }

    /* Can we use this algorithm for signature verification?  */
    sigAlg = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm);
    rv = sec_DecodeSigAlg(pubKey, sigAlg,
                          &sd->signatureAlgorithm.parameters,
                          &encAlg, &hashAlg, &mech, NULL);
    if (rv != SECSuccess) {
        return SECFailure; /* error is set */
    }
    rv = NSS_GetAlgorithmPolicy(encAlg, &policyFlags);
    if (rv == SECSuccess &&
        !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
        PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
        return SECFailure;
    }
    rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
    if (rv == SECSuccess &&
        !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
        PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
        return SECFailure;
    }
    rv = checkKeyParams(&sd->signatureAlgorithm, pubKey);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
        return SECFailure;
    }

    /* check the signature */
    sig = sd->signature;
    /* convert sig->len from bit counts to byte count. */
    DER_ConvertBitString(&sig);

    rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey,
                                       &sig, &sd->signatureAlgorithm,
                                       &hashAlg, wincx);
    if (rv != SECSuccess) {
        return SECFailure; /* error is set */
    }

    /* for some algorithms, hash algorithm is only known after verification */
    rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
    if (rv == SECSuccess &&
        !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
        PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
        return SECFailure;
    }
    return SECSuccess;
}

/*
 * verify the signature of a signed data object with the given DER publickey
 */

SECStatus
CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd,
                                       CERTSubjectPublicKeyInfo *pubKeyInfo,
                                       void *wincx)
{
    SECKEYPublicKey *pubKey;
    SECStatus rv = SECFailure;

    /* get cert's public key */
    pubKey = SECKEY_ExtractPublicKey(pubKeyInfo);
    if (pubKey) {
        rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
        SECKEY_DestroyPublicKey(pubKey);
    }
    return rv;
}

/*
 * verify the signature of a signed data object with the given certificate
 */

SECStatus
CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert,
                      PRTime t, void *wincx)
{
    SECKEYPublicKey *pubKey = 0;
    SECStatus rv = SECFailure;
    SECCertTimeValidity validity;

    /* check the certificate's validity */
    validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE);
    if (validity != secCertTimeValid) {
        return rv;
    }

    /* get cert's public key */
    pubKey = CERT_ExtractPublicKey(cert);
    if (pubKey) {
        rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
        SECKEY_DestroyPublicKey(pubKey);
    }
    return rv;
}

SECStatus
SEC_CheckCRL(CERTCertDBHandle *handle, CERTCertificate *cert,
             CERTCertificate *caCert, PRTime t, void *wincx)
{
    return CERT_CheckCRL(cert, caCert, NULL, t, wincx);
}

/*
 * Find the issuer of a cert.  Use the authorityKeyID if it exists.
 */

CERTCertificate *
CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage)
{
    NSSCertificate *me;
    NSSTime *nssTime;
    NSSTrustDomain *td;
    NSSCryptoContext *cc;
    NSSCertificate *chain[3];
    NSSUsage nssUsage;
    PRStatus status;

    me = STAN_GetNSSCertificate(cert);
    if (!me) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return NULL;
    }
    nssTime = NSSTime_SetPRTime(NULL, validTime);
    nssUsage.anyUsage = PR_FALSE;
    nssUsage.nss3usage = usage;
    nssUsage.nss3lookingForCA = PR_TRUE;
    memset(chain, 0, 3 * sizeof(NSSCertificate *));
    td = STAN_GetDefaultTrustDomain();
    cc = STAN_GetDefaultCryptoContext();
    (void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL,
                                    chain, 2, NULL, &status, td, cc);
    nss_ZFreeIf(nssTime);
    if (status == PR_SUCCESS) {
        PORT_Assert(me == chain[0]);
        /* if it's a root, the chain will only have one cert */
        if (!chain[1]) {
            /* already has a reference from the call to BuildChain */
            return cert;
        }
        NSSCertificate_Destroy(chain[0]);         /* the first cert in the chain */
        return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */
    }
    if (chain[0]) {
        PORT_Assert(me == chain[0]);
        NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */
    }
    PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
    return NULL;
}

/*
 * return required trust flags for various cert usages for CAs
 */

SECStatus
CERT_TrustFlagsForCACertUsage(SECCertUsage usage,
                              unsigned int *retFlags,
                              SECTrustType *retTrustType)
{
    unsigned int requiredFlags;
    SECTrustType trustType;

    switch (usage) {
        case certUsageSSLClient:
            requiredFlags = CERTDB_TRUSTED_CLIENT_CA;
            trustType = trustSSL;
            break;
        case certUsageSSLServer:
        case certUsageSSLCA:
            requiredFlags = CERTDB_TRUSTED_CA;
            trustType = trustSSL;
            break;
        case certUsageIPsec:
            requiredFlags = CERTDB_TRUSTED_CA;
            trustType = trustSSL;
            break;
        case certUsageSSLServerWithStepUp:
            requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA;
            trustType = trustSSL;
            break;
        case certUsageEmailSigner:
        case certUsageEmailRecipient:
            requiredFlags = CERTDB_TRUSTED_CA;
            trustType = trustEmail;
            break;
        case certUsageObjectSigner:
            requiredFlags = CERTDB_TRUSTED_CA;
            trustType = trustObjectSigning;
            break;
        case certUsageVerifyCA:
        case certUsageAnyCA:
        case certUsageStatusResponder:
            requiredFlags = CERTDB_TRUSTED_CA;
            trustType = trustTypeNone;
            break;
        default:
            PORT_Assert(0);
            goto loser;
    }
    if (retFlags != NULL) {
        *retFlags = requiredFlags;
    }
    if (retTrustType != NULL) {
        *retTrustType = trustType;
    }

    return (SECSuccess);
loser:
    return (SECFailure);
}

void
cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error,
                    unsigned int depth, void *arg)
{
    CERTVerifyLogNode *node, *tnode;

    PORT_Assert(log != NULL);

    node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena,
                                                sizeof(CERTVerifyLogNode));
    if (node != NULL) {
        node->cert = CERT_DupCertificate(cert);
        node->error = error;
        node->depth = depth;
        node->arg = arg;

        if (log->tail == NULL) {
            /* empty list */
            log->head = log->tail = node;
            node->prev = NULL;
            node->next = NULL;
        } else if (depth >= log->tail->depth) {
            /* add to tail */
            node->prev = log->tail;
            log->tail->next = node;
            log->tail = node;
            node->next = NULL;
        } else if (depth < log->head->depth) {
            /* add at head */
            node->prev = NULL;
            node->next = log->head;
            log->head->prev = node;
            log->head = node;
        } else {
            /* add in middle */
            tnode = log->tail;
            while (tnode != NULL) {
                if (depth >= tnode->depth) {
                    /* insert after tnode */
                    node->prev = tnode;
                    node->next = tnode->next;
                    tnode->next->prev = node;
                    tnode->next = node;
                    break;
                }

                tnode = tnode->prev;
            }
        }

        log->count++;
    }
    return;
}

#define EXIT_IF_NOT_LOGGING(log) \
    if (log == NULL) {           \
        goto loser;              \
    }

#define LOG_ERROR_OR_EXIT(log, cert, depth, arg)               \
    if (log != NULL) {                                         \
        cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
                            (void *)(PRWord)arg);              \
    } else {                                                   \
        goto loser;                                            \
    }

#define LOG_ERROR(log, cert, depth, arg)                       \
    if (log != NULL) {                                         \
        cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
                            (void *)(PRWord)arg);              \
    }

/* /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6
 * Using a consistent naming convention, this would actually be called
 * 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8
 * identifiers, this will have to do.
 */

static const unsigned char CAWoSignRootDN[72] = {
    0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
    0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
    0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
    0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0,
    0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6
};

/* /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root */
static const unsigned char CAWoSignECCRootDN[72] = {
    0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
    0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
    0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
    0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45,
    0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74
};

/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign */
static const unsigned char CertificationAuthorityofWoSignDN[87] = {
    0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
    0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
    0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
    0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
    0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
    0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E
};

/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2 */
static const unsigned char CertificationAuthorityofWoSignG2DN[90] = {
    0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
    0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
    0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03,
    0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
    0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
    0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32
};

/* /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */
static const unsigned char StartComCertificationAuthorityDN[127] = {
    0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
    0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
    0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65,
    0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20,
    0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
    0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
    0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20,
    0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
    0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79
};

/* /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */
static const unsigned char StartComCertificationAuthorityG2DN[85] = {
    0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
    0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
    0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
    0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74,
    0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
    0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F,
    0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32
};

struct DataAndLength {
    const unsigned char *data;
    PRUint32 len;
};

static const struct DataAndLength StartComAndWoSignDNs[] = {
    { CAWoSignRootDN,
      sizeof(CAWoSignRootDN) },
    { CAWoSignECCRootDN,
      sizeof(CAWoSignECCRootDN) },
    { CertificationAuthorityofWoSignDN,
      sizeof(CertificationAuthorityofWoSignDN) },
    { CertificationAuthorityofWoSignG2DN,
      sizeof(CertificationAuthorityofWoSignG2DN) },
    { StartComCertificationAuthorityDN,
      sizeof(StartComCertificationAuthorityDN) },
    { StartComCertificationAuthorityG2DN,
      sizeof(StartComCertificationAuthorityG2DN) },
};

static PRBool
CertIsStartComOrWoSign(const CERTCertificate *cert)
{
    int i;
    const struct DataAndLength *dn = StartComAndWoSignDNs;

    for (i = 0; i < sizeof(StartComAndWoSignDNs) / sizeof(struct DataAndLength); ++i, dn++) {
        if (cert->derSubject.len == dn->len &&
            memcmp(cert->derSubject.data, dn->data, dn->len) == 0) {
            return PR_TRUE;
        }
    }
    return PR_FALSE;
}

SECStatus
isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert,
                                      CERTCertificate *referenceCert)
{
    if (!issuerCert || !referenceCert) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (CertIsStartComOrWoSign(issuerCert)) {
        /* PRTime is microseconds since the epoch, whereas JS time is milliseconds.
         * (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
         */

        static const PRTime OCTOBER_21_2016 = 1477008000000000;

        PRTime notBefore, notAfter;
        SECStatus rv;

        rv = CERT_GetCertTimes(referenceCert, ¬Before, ¬After);
        if (rv != SECSuccess)
            return rv;

        if (notBefore > OCTOBER_21_2016) {
            return SECFailure;
        }
    }

    return SECSuccess;
}

static SECStatus
cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert,
                        PRBool checkSig, PRBool *sigerror,
                        SECCertUsage certUsage, PRTime t, void *wincx,
                        CERTVerifyLog *log, PRBool *revoked)
{
    SECTrustType trustType;
    CERTBasicConstraints basicConstraint;
    CERTCertificate *issuerCert = NULL;
    CERTCertificate *subjectCert = NULL;
    CERTCertificate *badCert = NULL;
    PRBool isca;
    SECStatus rv;
    SECStatus rvFinal = SECSuccess;
    int count;
    int currentPathLen = 0;
    int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
    unsigned int caCertType;
    unsigned int requiredCAKeyUsage;
    unsigned int requiredFlags;
    PLArenaPool *arena = NULL;
    CERTGeneralName *namesList = NULL;
    CERTCertificate **certsList = NULL;
    int certsListLen = 16;
    int namesCount = 0;
    PRBool subjectCertIsSelfIssued;
    CERTCertTrust issuerTrust;

    if (revoked) {
        *revoked = PR_FALSE;
    }

    if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
                                         &requiredCAKeyUsage,
                                         &caCertType) !=
        SECSuccess) {
        PORT_Assert(0);
        EXIT_IF_NOT_LOGGING(log);
        requiredCAKeyUsage = 0;
        caCertType = 0;
    }

    switch (certUsage) {
        case certUsageSSLClient:
        case certUsageSSLServer:
        case certUsageIPsec:
        case certUsageSSLCA:
        case certUsageSSLServerWithStepUp:
        case certUsageEmailSigner:
        case certUsageEmailRecipient:
        case certUsageObjectSigner:
        case certUsageVerifyCA:
        case certUsageAnyCA:
        case certUsageStatusResponder:
            if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
                                              &trustType) != SECSuccess) {
                PORT_Assert(0);
                EXIT_IF_NOT_LOGGING(log);
                /* XXX continuing with requiredFlags = 0 seems wrong.  It'll
                 * cause the following test to be true incorrectly:
                 *   flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType);
                 *   if (( flags & requiredFlags ) == requiredFlags) {
                 *       rv = rvFinal;
                 *       goto done;
                 *   }
                 * There are three other instances of this problem.
                 */

                requiredFlags = 0;
                trustType = trustSSL;
            }
            break;
        default:
            PORT_Assert(0);
            EXIT_IF_NOT_LOGGING(log);
            requiredFlags = 0;
            trustType = trustSSL; /* This used to be 0, but we need something
                                   * that matches the enumeration type.
                                   */

            caCertType = 0;
    }

    subjectCert = CERT_DupCertificate(cert);
    if (subjectCert == NULL) {
        goto loser;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        goto loser;
    }

    certsList = PORT_ZNewArray(CERTCertificate *, certsListLen);
    if (certsList == NULL)
        goto loser;

    /* RFC 3280 says that the name constraints will apply to the names
    ** in the leaf (EE) cert, whether it is self issued or not, so
    ** we pretend that it is not.
    */

    subjectCertIsSelfIssued = PR_FALSE;
    for (count = 0; count < CERT_MAX_CERT_CHAIN; count++) {
        PRBool validCAOverride = PR_FALSE;

        /* Construct a list of names for the current and all previous
         * certifcates (except leaf (EE) certs, root CAs, and self-issued
         * intermediate CAs) to be verified against the name constraints
         * extension of the issuer certificate.
         */

        if (subjectCertIsSelfIssued == PR_FALSE) {
            CERTGeneralName *subjectNameList;
            int subjectNameListLen;
            int i;
            PRBool getSubjectCN = (!count &&
                                   (certUsage == certUsageSSLServer || certUsage == certUsageIPsec));
            subjectNameList =
                CERT_GetConstrainedCertificateNames(subjectCert, arena,
                                                    getSubjectCN);
            if (!subjectNameList)
                goto loser;
            subjectNameListLen = CERT_GetNamesLength(subjectNameList);
            if (!subjectNameListLen)
                goto loser;
            if (certsListLen <= namesCount + subjectNameListLen) {
                CERTCertificate **tmpCertsList;
                certsListLen = (namesCount + subjectNameListLen) * 2;
                tmpCertsList =
                    (CERTCertificate **)PORT_Realloc(certsList,
                                                     certsListLen *
                                                         sizeof(CERTCertificate *));
                if (tmpCertsList == NULL) {
                    goto loser;
                }
                certsList = tmpCertsList;
            }
            for (i = 0; i < subjectNameListLen; i++) {
                certsList[namesCount + i] = subjectCert;
            }
            namesCount += subjectNameListLen;
            namesList = cert_CombineNamesLists(namesList, subjectNameList);
        }

        /* check if the cert has an unsupported critical extension */
        if (subjectCert->options.bits.hasUnsupportedCriticalExt) {
            PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
            LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
        }

        /* check that the signatureAlgorithm field of the certificate
         * matches the signature field of the tbsCertificate */

        if (SECOID_CompareAlgorithmID(
                &subjectCert->signatureWrap.signatureAlgorithm,
                &subjectCert->signature)) {
            PORT_SetError(SEC_ERROR_ALGORITHM_MISMATCH);
            LOG_ERROR(log, subjectCert, count, 0);
            goto loser;
        }

        /* find the certificate of the issuer */
        issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage);
        if (!issuerCert) {
            PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
            LOG_ERROR(log, subjectCert, count, 0);
            goto loser;
        }

        /* verify the signature on the cert */
        if (checkSig) {
            rv = CERT_VerifySignedData(&subjectCert->signatureWrap,
                                       issuerCert, t, wincx);

            if (rv != SECSuccess) {
                if (sigerror) {
                    *sigerror = PR_TRUE;
                }
                if (PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE) {
                    PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
                    LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
                } else {
                    if (PORT_GetError() !=
                        SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) {
                        PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
                    }
                    LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
                }
            }
        }

        /* If the basicConstraint extension is included in an immediate CA
         * certificate, make sure that the isCA flag is on.  If the
         * pathLenConstraint component exists, it must be greater than the
         * number of CA certificates we have seen so far.  If the extension
         * is omitted, we will assume that this is a CA certificate with
         * an unlimited pathLenConstraint (since it already passes the
         * netscape-cert-type extension checking).
         */


        rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint);
        if (rv != SECSuccess) {
            if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
                LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
            }
            pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
            /* no basic constraints found, we aren't (yet) a CA. */
            isca = PR_FALSE;
        } else {
            if (basicConstraint.isCA == PR_FALSE) {
                PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
                LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
            }
            pathLengthLimit = basicConstraint.pathLenConstraint;
            isca = PR_TRUE;
        }
        /* make sure that the path len constraint is properly set.*/
        if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) {
            PORT_SetError(SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
            LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, pathLengthLimit);
        }

        /* make sure that the entire chain is within the name space of the
         * current issuer certificate.
         */

        rv = CERT_CompareNameSpace(issuerCert, namesList, certsList,
                                   arena, &badCert);
        if (rv != SECSuccess || badCert != NULL) {
            PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE);
            LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0);
            goto loser;
        }

        rv = isIssuerCertAllowedAtCertIssuanceTime(issuerCert, cert);
        if (rv != SECSuccess) {
            PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
            LOG_ERROR(log, issuerCert, count + 1, 0);
            goto loser;
        }

        /* XXX - the error logging may need to go down into CRL stuff at some
         * point
         */

        /* check revoked list (issuer) */
        rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx);
        if (rv == SECFailure) {
            if (revoked) {
                *revoked = PR_TRUE;
            }
            LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
        } else if (rv == SECWouldBlock) {
            /* We found something fishy, so we intend to issue an
             * error to the user, but the user may wish to continue
             * processing, in which case we better make sure nothing
             * worse has happened... so keep cranking the loop */

            rvFinal = SECFailure;
            if (revoked) {
                *revoked = PR_TRUE;
            }
            LOG_ERROR(log, subjectCert, count, 0);
        }

        if (CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) {
            /* we have some trust info, but this does NOT imply that this
             * cert is actually trusted for any purpose.  The cert may be
             * explicitly UNtrusted.  We won't know until we examine the
             * trust bits.
             */

            unsigned int flags;

            if (certUsage != certUsageAnyCA &&
                certUsage != certUsageStatusResponder) {

                /*
                 * XXX This choice of trustType seems arbitrary.
                 */

                if (certUsage == certUsageVerifyCA) {
                    if (subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA) {
                        trustType = trustEmail;
                    } else if (subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA) {
                        trustType = trustSSL;
                    } else {
                        trustType = trustObjectSigning;
                    }
                }

                flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
                if ((flags & requiredFlags) == requiredFlags) {
                    /* we found a trusted one, so return */
                    rv = rvFinal;
                    goto done;
                }
                if (flags & CERTDB_VALID_CA) {
                    validCAOverride = PR_TRUE;
                }
                /* is it explicitly distrusted? */
                if ((flags & CERTDB_TERMINAL_RECORD) &&
                    ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
                    /* untrusted -- the cert is explicitly untrusted, not
                     * just that it doesn't chain to a trusted cert */

                    PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
                    LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
                }
            } else {
                /* Check if we have any valid trust when cheching for
                 * certUsageAnyCA or certUsageStatusResponder. */

                for (trustType = trustSSL; trustType < trustTypeNone;
                     trustType++) {
                    flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
                    if ((flags & requiredFlags) == requiredFlags) {
                        rv = rvFinal;
                        goto done;
                    }
                    if (flags & CERTDB_VALID_CA)
                        validCAOverride = PR_TRUE;
                }
                /* We have 2 separate loops because we want any single trust
                 * bit to allow this usage to return trusted. Only if none of
                 * the trust bits are on do we check to see if the cert is
                 * untrusted */

                for (trustType = trustSSL; trustType < trustTypeNone;
                     trustType++) {
                    flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
                    /* is it explicitly distrusted? */
                    if ((flags & CERTDB_TERMINAL_RECORD) &&
                        ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
                        /* untrusted -- the cert is explicitly untrusted, not
                         * just that it doesn't chain to a trusted cert */

                        PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
                        LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
                    }
                }
            }
        }

        if (!validCAOverride) {
            /*
             * Make sure that if this is an intermediate CA in the chain that
             * it was given permission by its signer to be a CA.
             */

            /*
             * if basicConstraints says it is a ca, then we check the
             * nsCertType.  If the nsCertType has any CA bits set, then
             * it must have the right one.
             */

            if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) {
                isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
            }

            if (!isca) {
                PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
                LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
            }

            /* make sure key usage allows cert signing */
            if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) {
                PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
                LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, requiredCAKeyUsage);
            }
        }

        /* make sure that the issuer is not self signed.  If it is, then
         * stop here to prevent looping.
         */

        if (issuerCert->isRoot) {
            PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
            LOG_ERROR(log, issuerCert, count + 1, 0);
            goto loser;
        }
        /* The issuer cert will be the subject cert in the next loop.
         * A cert is self-issued if its subject and issuer are equal and
         * both are of non-zero length.
         */

        subjectCertIsSelfIssued = (PRBool)
                                      SECITEM_ItemsAreEqual(&issuerCert->derIssuer,
                                                            &issuerCert->derSubject) &&
                                  issuerCert->derSubject.len >
                                      0;
        if (subjectCertIsSelfIssued == PR_FALSE) {
            /* RFC 3280 says only non-self-issued intermediate CA certs
             * count in path length.
             */

            ++currentPathLen;
        }

        CERT_DestroyCertificate(subjectCert);
        subjectCert = issuerCert;
        issuerCert = NULL;
    }

    PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
    LOG_ERROR(log, subjectCert, count, 0);
loser:
    rv = SECFailure;
done:
    if (certsList != NULL) {
        PORT_Free(certsList);
    }
    if (issuerCert) {
        CERT_DestroyCertificate(issuerCert);
    }

    if (subjectCert) {
        CERT_DestroyCertificate(subjectCert);
    }

    if (arena != NULL) {
        PORT_FreeArena(arena, PR_FALSE);
    }
    return rv;
}

SECStatus
cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
                     PRBool checkSig, PRBool *sigerror,
                     SECCertUsage certUsage, PRTime t, void *wincx,
                     CERTVerifyLog *log, PRBool *revoked)
{
    if (CERT_GetUsePKIXForValidation()) {
        return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t,
                                        wincx, log, sigerror, revoked);
    }
    return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror,
                                   certUsage, t, wincx, log, revoked);
}

SECStatus
CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
                     PRBool checkSig, SECCertUsage certUsage, PRTime t,
                     void *wincx, CERTVerifyLog *log)
{
    return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t,
                                wincx, log, NULL);
}

/*
 * verify that a CA can sign a certificate with the requested usage.
 */

SECStatus
CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert,
                          PRBool checkSig, SECCertUsage certUsage, PRTime t,
                          void *wincx, CERTVerifyLog *log)
{
    SECTrustType trustType;
    CERTBasicConstraints basicConstraint;
    PRBool isca;
    PRBool validCAOverride = PR_FALSE;
    SECStatus rv;
    SECStatus rvFinal = SECSuccess;
    unsigned int flags;
    unsigned int caCertType;
    unsigned int requiredCAKeyUsage;
    unsigned int requiredFlags;
    CERTCertificate *issuerCert;
    CERTCertTrust certTrust;

    if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
                                         &requiredCAKeyUsage,
                                         &caCertType) != SECSuccess) {
        PORT_Assert(0);
        EXIT_IF_NOT_LOGGING(log);
        requiredCAKeyUsage = 0;
        caCertType = 0;
    }

    switch (certUsage) {
        case certUsageSSLClient:
        case certUsageSSLServer:
        case certUsageIPsec:
        case certUsageSSLCA:
        case certUsageSSLServerWithStepUp:
        case certUsageEmailSigner:
        case certUsageEmailRecipient:
        case certUsageObjectSigner:
        case certUsageVerifyCA:
        case certUsageStatusResponder:
            if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
                                              &trustType) != SECSuccess) {
                PORT_Assert(0);
                EXIT_IF_NOT_LOGGING(log);
                requiredFlags = 0;
                trustType = trustSSL;
            }
            break;
        default:
            PORT_Assert(0);
            EXIT_IF_NOT_LOGGING(log);
            requiredFlags = 0;
            trustType = trustSSL; /* This used to be 0, but we need something
                                   * that matches the enumeration type.
                                   */

            caCertType = 0;
    }

    /* If the basicConstraint extension is included in an intermmediate CA
     * certificate, make sure that the isCA flag is on.  If the
     * pathLenConstraint component exists, it must be greater than the
     * number of CA certificates we have seen so far.  If the extension
     * is omitted, we will assume that this is a CA certificate with
     * an unlimited pathLenConstraint (since it already passes the
     * netscape-cert-type extension checking).
     */


    rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
    if (rv != SECSuccess) {
        if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
            LOG_ERROR_OR_EXIT(log, cert, 0, 0);
        }
        /* no basic constraints found, we aren't (yet) a CA. */
        isca = PR_FALSE;
    } else {
        if (basicConstraint.isCA == PR_FALSE) {
            PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
            LOG_ERROR_OR_EXIT(log, cert, 0, 0);
        }

        /* can't check path length if we don't know the previous path */
        isca = PR_TRUE;
    }

    if (CERT_GetCertTrust(cert, &certTrust) == SECSuccess) {
        /* we have some trust info, but this does NOT imply that this
         * cert is actually trusted for any purpose.  The cert may be
         * explicitly UNtrusted.  We won't know until we examine the
         * trust bits.
         */

        if (certUsage == certUsageStatusResponder) {
            /* Check the special case of certUsageStatusResponder */
            issuerCert = CERT_FindCertIssuer(cert, t, certUsage);
            if (issuerCert) {
                if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) !=
                    SECSuccess) {
                    PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
                    CERT_DestroyCertificate(issuerCert);
                    goto loser;
                }
                CERT_DestroyCertificate(issuerCert);
            }
            /* XXX We have NOT determined that this cert is trusted.
             * For years, NSS has treated this as trusted,
             * but it seems incorrect.
             */

            rv = rvFinal;
            goto done;
        }

        /*
         * check the trust params of the issuer
         */

        flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType);
        if ((flags & requiredFlags) == requiredFlags) {
            /* we found a trusted one, so return */
            rv = rvFinal;
            goto done;
        }
        if (flags & CERTDB_VALID_CA) {
            validCAOverride = PR_TRUE;
        }
        /* is it explicitly distrusted? */
        if ((flags & CERTDB_TERMINAL_RECORD) &&
            ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
            /* untrusted -- the cert is explicitly untrusted, not
             * just that it doesn't chain to a trusted cert */

            PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
            LOG_ERROR_OR_EXIT(log, cert, 0, flags);
        }
    }
    if (!validCAOverride) {
        /*
         * Make sure that if this is an intermediate CA in the chain that
         * it was given permission by its signer to be a CA.
         */

        /*
         * if basicConstraints says it is a ca, then we check the
         * nsCertType.  If the nsCertType has any CA bits set, then
         * it must have the right one.
         */

        if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) {
            isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
        }

        if (!isca) {
            PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
            LOG_ERROR_OR_EXIT(log, cert, 0, 0);
        }

        /* make sure key usage allows cert signing */
        if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) {
            PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
            LOG_ERROR_OR_EXIT(log, cert, 0, requiredCAKeyUsage);
        }
    }
    /* make sure that the issuer is not self signed.  If it is, then
     * stop here to prevent looping.
     */

    if (cert->isRoot) {
        PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
        LOG_ERROR(log, cert, 0, 0);
        goto loser;
    }

    return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t,
                                wincx, log);
loser:
    rv = SECFailure;
done:
    return rv;
}

#define NEXT_USAGE() \
    {                \
        i *= 2;      \
        certUsage++; \
        continue;    \
    }

#define VALID_USAGE() \
    {                 \
        NEXT_USAGE(); \
    }

#define INVALID_USAGE()                 \
    {                                   \
        if (returnedUsages) {           \
            *returnedUsages &= (~i);    \
        }                               \
        if (PR_TRUE == requiredUsage) { \
            valid = SECFailure;         \
        }                               \
        NEXT_USAGE();                   \
    }

/*
 * check the leaf cert against trust and usage.
 *   returns success if the cert is not distrusted. If the cert is
 *       trusted, then the trusted bool will be true.
 *   returns failure if the cert is distrusted. If failure, flags
 *       will return the flag bits that indicated distrust.
 */

SECStatus
cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage,
                    unsigned int *failedFlags, PRBool *trusted)
{
    unsigned int flags;
    CERTCertTrust trust;

    *failedFlags = 0;
    *trusted = PR_FALSE;

    /* check trust flags to see if this cert is directly trusted */
    if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
        switch (certUsage) {
            case certUsageSSLClient:
            case certUsageSSLServer:
            case certUsageIPsec:
                flags = trust.sslFlags;

                /* is the cert directly trusted or not trusted ? */
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if (flags & CERTDB_TRUSTED) {     /* trust this cert */
                        *trusted = PR_TRUE;
                        return SECSuccess;
                    } else { /* don't trust this cert */
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                break;
            case certUsageSSLServerWithStepUp:
                /* XXX - step up certs can't be directly trusted, only distrust */
                flags = trust.sslFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if ((flags & CERTDB_TRUSTED) == 0) {
                        /* don't trust this cert */
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                break;
            case certUsageSSLCA:
                flags = trust.sslFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
                        /* don't trust this cert */
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                break;
            case certUsageEmailSigner:
            case certUsageEmailRecipient:
                flags = trust.emailFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if (flags & CERTDB_TRUSTED) {     /* trust this cert */
                        *trusted = PR_TRUE;
                        return SECSuccess;
                    } else { /* don't trust this cert */
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }

                break;
            case certUsageObjectSigner:
                flags = trust.objectSigningFlags;

                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if (flags & CERTDB_TRUSTED) {     /* trust this cert */
                        *trusted = PR_TRUE;
                        return SECSuccess;
                    } else { /* don't trust this cert */
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                break;
            case certUsageVerifyCA:
            case certUsageStatusResponder:
                flags = trust.sslFlags;
                /* is the cert directly trusted or not trusted ? */
                if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
                    (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
                    *trusted = PR_TRUE;
                    return SECSuccess;
                }
                flags = trust.emailFlags;
                /* is the cert directly trusted or not trusted ? */
                if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
                    (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
                    *trusted = PR_TRUE;
                    return SECSuccess;
                }
                flags = trust.objectSigningFlags;
                /* is the cert directly trusted or not trusted ? */
                if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
                    (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
                    *trusted = PR_TRUE;
                    return SECSuccess;
                }
            /* fall through to test distrust */
            case certUsageAnyCA:
            case certUsageUserCertImport:
                /* do we distrust these certs explicitly */
                flags = trust.sslFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                flags = trust.emailFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
            /* fall through */
            case certUsageProtectedObjectSigner:
                flags = trust.objectSigningFlags;
                if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
                                                       * authoritative */

                    if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
                        *failedFlags = flags;
                        return SECFailure;
                    }
                }
                break;
        }
    }
    return SECSuccess;
}

/*
 * verify a certificate by checking if it's valid and that we
 * trust the issuer.
 *
 * certificateUsage contains a bitfield of all cert usages that are
 * required for verification to succeed
 *
 * a bitfield of cert usages is returned in *returnedUsages
 * if requiredUsages is non-zero, the returned bitmap is only
 * for those required usages, otherwise it is for all usages
 *
 */

SECStatus
CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert,
                       PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t,
                       void *wincx, CERTVerifyLog *log, SECCertificateUsage *returnedUsages)
{
    SECStatus rv;
    SECStatus valid;
    unsigned int requiredKeyUsage;
    unsigned int requiredCertType;
    unsigned int flags;
    unsigned int certType;
    PRBool allowOverride;
    SECCertTimeValidity validity;
    CERTStatusConfig *statusConfig;
    PRInt32 i;
    SECCertUsage certUsage = 0;
    PRBool checkedOCSP = PR_FALSE;
    PRBool checkAllUsages = PR_FALSE;
    PRBool revoked = PR_FALSE;
    PRBool sigerror = PR_FALSE;
    PRBool trusted = PR_FALSE;

    if (!requiredUsages) {
        /* there are no required usages, so the user probably wants to
           get status for all usages */

        checkAllUsages = PR_TRUE;
    }

    if (returnedUsages) {
        *returnedUsages = 0;
    } else {
        /* we don't have a place to return status for all usages,
           so we can skip checks for usages that aren't required */

        checkAllUsages = PR_FALSE;
    }
    valid = SECSuccess; /* start off assuming cert is valid */

    /* make sure that the cert is valid at time t */
    allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) ||
                             (requiredUsages & certificateUsageSSLServerWithStepUp) ||
                             (requiredUsages & certificateUsageIPsec));
    validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
    if (validity != secCertTimeValid) {
        valid = SECFailure;
        LOG_ERROR_OR_EXIT(log, cert, 0, validity);
    }

    /* check key usage and netscape cert type */
    cert_GetCertType(cert);
    certType = cert->nsCertType;

    for (i = 1; i <= certificateUsageHighest &&
                (SECSuccess == valid || returnedUsages || log);) {
        PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE;
        if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) {
            NEXT_USAGE();
        }
        if (returnedUsages) {
            *returnedUsages |= i; /* start off assuming this usage is valid */
        }
        switch (certUsage) {
            case certUsageSSLClient:
            case certUsageSSLServer:
            case certUsageSSLServerWithStepUp:
            case certUsageSSLCA:
            case certUsageEmailSigner:
            case certUsageEmailRecipient:
            case certUsageObjectSigner:
            case certUsageStatusResponder:
            case certUsageIPsec:
                rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
                                                      &requiredKeyUsage,
                                                      &requiredCertType);
                if (rv != SECSuccess) {
                    PORT_Assert(0);
                    /* EXIT_IF_NOT_LOGGING(log); XXX ??? */
                    requiredKeyUsage = 0;
                    requiredCertType = 0;
                    INVALID_USAGE();
                }
                break;

            case certUsageAnyCA:
            case certUsageProtectedObjectSigner:
            case certUsageUserCertImport:
            case certUsageVerifyCA:
                /* these usages cannot be verified */
                NEXT_USAGE();

            default:
                PORT_Assert(0);
                requiredKeyUsage = 0;
                requiredCertType = 0;
                INVALID_USAGE();
        }
        if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
            if (PR_TRUE == requiredUsage) {
                PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
            }
            LOG_ERROR(log, cert, 0, requiredKeyUsage);
            INVALID_USAGE();
        }
        if (!(certType & requiredCertType)) {
            if (PR_TRUE == requiredUsage) {
                PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
            }
            LOG_ERROR(log, cert, 0, requiredCertType);
            INVALID_USAGE();
        }

        rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted);
        if (rv == SECFailure) {
            if (PR_TRUE == requiredUsage) {
                PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
            }
            LOG_ERROR(log, cert, 0, flags);
            INVALID_USAGE();
        } else if (trusted) {
            VALID_USAGE();
        }

        if (PR_TRUE == revoked || PR_TRUE == sigerror) {
            INVALID_USAGE();
        }

        rv = cert_VerifyCertChain(handle, cert,
                                  checkSig, &sigerror,
                                  certUsage, t, wincx, log,
                                  &revoked);

        if (rv != SECSuccess) {
            /* EXIT_IF_NOT_LOGGING(log); XXX ???? */
            INVALID_USAGE();
        }

        /*
         * Check OCSP revocation status, but only if the cert we are checking
         * is not a status responder itself. We only do this in the case
         * where we checked the cert chain (above); explicit trust "wins"
         * (avoids status checking, just as it avoids CRL checking) by
         * bypassing this code.
         */


        if (PR_FALSE == checkedOCSP) {
            checkedOCSP = PR_TRUE; /* only check OCSP once */
            statusConfig = CERT_GetStatusConfig(handle);
            if (requiredUsages != certificateUsageStatusResponder &&
                statusConfig != NULL) {
                if (statusConfig->statusChecker != NULL) {
                    rv = (*statusConfig->statusChecker)(handle, cert,
                                                        t, wincx);
                    if (rv != SECSuccess) {
                        LOG_ERROR(log, cert, 0, 0);
                        revoked = PR_TRUE;
                        INVALID_USAGE();
                    }
                }
            }
        }

        NEXT_USAGE();
    }

loser:
    return (valid);
}

SECStatus
CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,
                PRBool checkSig, SECCertUsage certUsage, PRTime t,
                void *wincx, CERTVerifyLog *log)
{
    return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t,
                                    CERT_VERIFYCERT_USE_DEFAULTS, wincx, log);
}

SECStatus
cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert,
                         PRBool checkSig, SECCertUsage certUsage, PRTime t,
                         PRUint32 flags, void *wincx, CERTVerifyLog *log)
{
    SECStatus rv;
    unsigned int requiredKeyUsage;
    unsigned int requiredCertType;
    unsigned int failedFlags;
    unsigned int certType;
    PRBool trusted;
    PRBool allowOverride;
    SECCertTimeValidity validity;
    CERTStatusConfig *statusConfig;

#ifdef notdef
    /* check if this cert is in the Evil list */
    rv = CERT_CheckForEvilCert(cert);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
        LOG_ERROR_OR_EXIT(log, cert, 0, 0);
    }
#endif

    /* make sure that the cert is valid at time t */
    allowOverride = (PRBool)((certUsage == certUsageSSLServer) ||
                             (certUsage == certUsageSSLServerWithStepUp) ||
                             (certUsage == certUsageIPsec));
    validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
    if (validity != secCertTimeValid) {
        LOG_ERROR_OR_EXIT(log, cert, 0, validity);
    }

    /* check key usage and netscape cert type */
    cert_GetCertType(cert);
    certType = cert->nsCertType;
    switch (certUsage) {
        case certUsageSSLClient:
        case certUsageSSLServer:
        case certUsageSSLServerWithStepUp:
        case certUsageIPsec:
        case certUsageSSLCA:
        case certUsageEmailSigner:
        case certUsageEmailRecipient:
        case certUsageObjectSigner:
        case certUsageStatusResponder:
            rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
                                                  &requiredKeyUsage,
                                                  &requiredCertType);
            if (rv != SECSuccess) {
                PORT_Assert(0);
                EXIT_IF_NOT_LOGGING(log);
                requiredKeyUsage = 0;
                requiredCertType = 0;
            }
            break;
        case certUsageVerifyCA:
        case certUsageAnyCA:
            requiredKeyUsage = KU_KEY_CERT_SIGN;
            requiredCertType = NS_CERT_TYPE_CA;
            if (!(certType & NS_CERT_TYPE_CA)) {
                certType |= NS_CERT_TYPE_CA;
            }
            break;
        default:
            PORT_Assert(0);
            EXIT_IF_NOT_LOGGING(log);
            requiredKeyUsage = 0;
            requiredCertType = 0;
    }
    if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
        PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
        LOG_ERROR_OR_EXIT(log, cert, 0, requiredKeyUsage);
    }
    if (!(certType & requiredCertType)) {
        PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
        LOG_ERROR_OR_EXIT(log, cert, 0, requiredCertType);
    }

    rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted);
    if (rv == SECFailure) {
        PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
        LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags);
    } else if (trusted) {
        goto done;
    }

    rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage,
                              t, wincx, log);
    if (rv != SECSuccess) {
        EXIT_IF_NOT_LOGGING(log);
    }

    /*
     * Check revocation status, but only if the cert we are checking is not a
     * status responder itself and the caller did not ask us to skip the check.
     * We only do this in the case where we checked the cert chain (above);
     * explicit trust "wins" (avoids status checking, just as it avoids CRL
     * checking, which is all done inside VerifyCertChain) by bypassing this
     * code.
     */

    if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) &&
        certUsage != certUsageStatusResponder) {
        statusConfig = CERT_GetStatusConfig(handle);
        if (statusConfig && statusConfig->statusChecker) {
            rv = (*statusConfig->statusChecker)(handle, cert,
                                                t, wincx);
            if (rv != SECSuccess) {
                LOG_ERROR_OR_EXIT(log, cert, 0, 0);
            }
        }
    }

done:
    if (log && log->head) {
        return SECFailure;
    }
    return (SECSuccess);

loser:
    rv = SECFailure;

    return (rv);
}

/*
 * verify a certificate by checking if its valid and that we
 * trust the issuer.  Verify time against now.
 */

SECStatus
CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert,
                          PRBool checkSig, SECCertificateUsage requiredUsages,
                          void *wincx, SECCertificateUsage *returnedUsages)
{
    return (CERT_VerifyCertificate(handle, cert, checkSig,
                                   requiredUsages, PR_Now(), wincx, NULL, returnedUsages));
}

/* obsolete, do not use for new code */
SECStatus
CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert,
                   PRBool checkSig, SECCertUsage certUsage, void *wincx)
{
    return (CERT_VerifyCert(handle, cert, checkSig,
                            certUsage, PR_Now(), wincx, NULL));
}

/* [ FROM pcertdb.c ] */
/*
 * Supported usage values and types:
 *  certUsageSSLClient
 *  certUsageSSLServer
 *  certUsageSSLServerWithStepUp
 *  certUsageIPsec
 *  certUsageEmailSigner
 *  certUsageEmailRecipient
 *  certUsageObjectSigner
 */


CERTCertificate *
CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName,
                      CERTCertOwner owner, SECCertUsage usage,
                      PRBool preferTrusted, PRTime validTime, PRBool validOnly)
{
    CERTCertList *certList = NULL;
    CERTCertificate *cert = NULL;
    CERTCertTrust certTrust;
    unsigned int requiredTrustFlags;
    SECTrustType requiredTrustType;
    unsigned int flags;

    PRBool lookingForCA = PR_FALSE;
    SECStatus rv;
    CERTCertListNode *node;
    CERTCertificate *saveUntrustedCA = NULL;

    /* if preferTrusted is set, must be a CA cert */
    PORT_Assert(!(preferTrusted && (owner != certOwnerCA)));

    if (owner == certOwnerCA) {
        lookingForCA = PR_TRUE;
        if (preferTrusted) {
            rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags,
                                               &requiredTrustType);
            if (rv != SECSuccess) {
                goto loser;
            }
            requiredTrustFlags |= CERTDB_VALID_CA;
        }
    }

    certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime,
                                          validOnly);
    if (certList != NULL) {
        rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA);
        if (rv != SECSuccess) {
            goto loser;
        }

        node = CERT_LIST_HEAD(certList);

        while (!CERT_LIST_END(node, certList)) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=89 G=90

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.