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

Quelle  certdb.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/. */


/*
 * Certificate handling code
 */


#include "nssilock.h"
#include "prmon.h"
#include "prtime.h"
#include "cert.h"
#include "certi.h"
#include "secder.h"
#include "secoid.h"
#include "secasn1.h"
#include "genname.h"
#include "keyhi.h"
#include "secitem.h"
#include "certdb.h"
#include "prprf.h"
#include "sechash.h"
#include "prlong.h"
#include "certxutl.h"
#include "portreg.h"
#include "secerr.h"
#include "sslerr.h"
#include "pk11func.h"
#include "xconst.h" /* for  CERT_DecodeAltNameExtension */

#include "pki.h"
#include "pki3hack.h"

SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
SEC_ASN1_MKSUB(SEC_BitStringTemplate)
SEC_ASN1_MKSUB(SEC_IntegerTemplate)
SEC_ASN1_MKSUB(SEC_SkipTemplate)

/*
 * Certificate database handling code
 */


const SEC_ASN1Template CERT_CertExtensionTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertExtension) },
    { SEC_ASN1_OBJECT_ID, offsetof(CERTCertExtension, id) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */
      offsetof(CERTCertExtension, critical) },
    { SEC_ASN1_OCTET_STRING, offsetof(CERTCertExtension, value) },
    { 0 }
};

const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, CERT_CertExtensionTemplate }
};

const SEC_ASN1Template CERT_TimeChoiceTemplate[] = {
    { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) },
    { SEC_ASN1_UTC_TIME, 0, 0, siUTCTime },
    { SEC_ASN1_GENERALIZED_TIME, 0, 0, siGeneralizedTime },
    { 0 }
};

const SEC_ASN1Template CERT_ValidityTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTValidity) },
    { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notBefore),
      SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
    { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTValidity, notAfter),
      SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
    { 0 }
};

const SEC_ASN1Template CERT_CertificateTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificate) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, /* XXX DER_DEFAULT */
      offsetof(CERTCertificate, version),
      SEC_ASN1_SUB(SEC_IntegerTemplate) },
    { SEC_ASN1_INTEGER, offsetof(CERTCertificate, serialNumber) },
    { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(CERTCertificate, signature),
      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    { SEC_ASN1_SAVE, offsetof(CERTCertificate, derIssuer) },
    { SEC_ASN1_INLINE, offsetof(CERTCertificate, issuer), CERT_NameTemplate },
    { SEC_ASN1_INLINE, offsetof(CERTCertificate, validity),
      CERT_ValidityTemplate },
    { SEC_ASN1_SAVE, offsetof(CERTCertificate, derSubject) },
    { SEC_ASN1_INLINE, offsetof(CERTCertificate, subject), CERT_NameTemplate },
    { SEC_ASN1_SAVE, offsetof(CERTCertificate, derPublicKey) },
    { SEC_ASN1_INLINE, offsetof(CERTCertificate, subjectPublicKeyInfo),
      CERT_SubjectPublicKeyInfoTemplate },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
      offsetof(CERTCertificate, issuerID),
      SEC_ASN1_SUB(SEC_BitStringTemplate) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
      offsetof(CERTCertificate, subjectID),
      SEC_ASN1_SUB(SEC_BitStringTemplate) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | 3,
      offsetof(CERTCertificate, extensions),
      CERT_SequenceOfCertExtensionTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_SignedCertificateTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificate) },
    { SEC_ASN1_SAVE, offsetof(CERTCertificate, signatureWrap.data) },
    { SEC_ASN1_INLINE, 0, CERT_CertificateTemplate },
    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
      offsetof(CERTCertificate, signatureWrap.signatureAlgorithm),
      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    { SEC_ASN1_BIT_STRING, offsetof(CERTCertificate, signatureWrap.signature) },
    { 0 }
};

/*
 * Find the subjectName in a DER encoded certificate
 */

const SEC_ASN1Template SEC_CertSubjectTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
      0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
    { SEC_ASN1_SKIP },                     /* serial number */
    { SEC_ASN1_SKIP },                     /* signature algorithm */
    { SEC_ASN1_SKIP },                     /* issuer */
    { SEC_ASN1_SKIP },                     /* validity */
    { SEC_ASN1_ANY, 0, NULL },             /* subject */
    { SEC_ASN1_SKIP_REST },
    { 0 }
};

/*
 * Find the issuerName in a DER encoded certificate
 */

const SEC_ASN1Template SEC_CertIssuerTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
      0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
    { SEC_ASN1_SKIP },                     /* serial number */
    { SEC_ASN1_SKIP },                     /* signature algorithm */
    { SEC_ASN1_ANY, 0, NULL },             /* issuer */
    { SEC_ASN1_SKIP_REST },
    { 0 }
};
/*
 * Find the subjectName in a DER encoded certificate
 */

const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECItem) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
      0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
    { SEC_ASN1_ANY, 0, NULL },             /* serial number */
    { SEC_ASN1_SKIP_REST },
    { 0 }
};

/*
 * Find the issuer and serialNumber in a DER encoded certificate.
 * This data is used as the database lookup key since its the unique
 * identifier of a certificate.
 */

const SEC_ASN1Template CERT_CertKeyTemplate[] = {
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertKey) },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
      0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
    { SEC_ASN1_INTEGER, offsetof(CERTCertKey, serialNumber) },
    { SEC_ASN1_SKIP }, /* signature algorithm */
    { SEC_ASN1_ANY, offsetof(CERTCertKey, derIssuer) },
    { SEC_ASN1_SKIP_REST },
    { 0 }
};

SEC_ASN1_CHOOSER_IMPLEMENT(CERT_TimeChoiceTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate)
SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SequenceOfCertExtensionTemplate)

SECStatus
CERT_KeyFromIssuerAndSN(PLArenaPool *arena, SECItem *issuer, SECItem *sn,
                        SECItem *key)
{
    key->len = sn->len + issuer->len;

    if ((sn->data == NULL) || (issuer->data == NULL)) {
        goto loser;
    }

    key->data = (unsigned char *)PORT_ArenaAlloc(arena, key->len);
    if (!key->data) {
        goto loser;
    }

    /* copy the serialNumber */
    PORT_Memcpy(key->data, sn->data, sn->len);

    /* copy the issuer */
    PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * Extract the subject name from a DER certificate
 */

SECStatus
CERT_NameFromDERCert(SECItem *derCert, SECItem *derName)
{
    int rv;
    PLArenaPool *arena;
    CERTSignedData sd;
    void *tmpptr;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        return (SECFailure);
    }

    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);

    if (rv) {
        goto loser;
    }

    PORT_Memset(derName, 0, sizeof(SECItem));
    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSubjectTemplate,
                                &sd.data);

    if (rv) {
        goto loser;
    }

    tmpptr = derName->data;
    derName->data = (unsigned char *)PORT_Alloc(derName->len);
    if (derName->data == NULL) {
        goto loser;
    }

    PORT_Memcpy(derName->data, tmpptr, derName->len);

    PORT_FreeArena(arena, PR_FALSE);
    return (SECSuccess);

loser:
    PORT_FreeArena(arena, PR_FALSE);
    return (SECFailure);
}

SECStatus
CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName)
{
    int rv;
    PORTCheapArenaPool tmpArena;
    CERTSignedData sd;
    void *tmpptr;

    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);

    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
                                derCert);
    if (rv) {
        goto loser;
    }

    PORT_Memset(derName, 0, sizeof(SECItem));
    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName,
                                SEC_CertIssuerTemplate, &sd.data);
    if (rv) {
        goto loser;
    }

    tmpptr = derName->data;
    derName->data = (unsigned char *)PORT_Alloc(derName->len);
    if (derName->data == NULL) {
        goto loser;
    }

    PORT_Memcpy(derName->data, tmpptr, derName->len);

    PORT_DestroyCheapArena(&tmpArena);
    return (SECSuccess);

loser:
    PORT_DestroyCheapArena(&tmpArena);
    return (SECFailure);
}

SECStatus
CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName)
{
    int rv;
    PORTCheapArenaPool tmpArena;
    CERTSignedData sd;
    void *tmpptr;

    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);

    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
                                derCert);
    if (rv) {
        goto loser;
    }

    PORT_Memset(derName, 0, sizeof(SECItem));
    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName,
                                SEC_CertSerialNumberTemplate, &sd.data);
    if (rv) {
        goto loser;
    }

    tmpptr = derName->data;
    derName->data = (unsigned char *)PORT_Alloc(derName->len);
    if (derName->data == NULL) {
        goto loser;
    }

    PORT_Memcpy(derName->data, tmpptr, derName->len);

    PORT_DestroyCheapArena(&tmpArena);
    return (SECSuccess);

loser:
    PORT_DestroyCheapArena(&tmpArena);
    return (SECFailure);
}

/*
 * Generate a database key, based on serial number and issuer, from a
 * DER certificate.
 */

SECStatus
CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key)
{
    int rv;
    CERTSignedData sd;
    CERTCertKey certkey;

    if (!reqArena) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
    rv =
        SEC_QuickDERDecodeItem(reqArena, &sd, CERT_SignedDataTemplate, derCert);

    if (rv) {
        goto loser;
    }

    PORT_Memset(&certkey, 0, sizeof(CERTCertKey));
    rv = SEC_QuickDERDecodeItem(reqArena, &certkey, CERT_CertKeyTemplate,
                                &sd.data);

    if (rv) {
        goto loser;
    }

    return (CERT_KeyFromIssuerAndSN(reqArena, &certkey.derIssuer,
                                    &certkey.serialNumber, key));
loser:
    return (SECFailure);
}

/*
 * fill in keyUsage field of the cert based on the cert extension
 * if the extension is not critical, then we allow all uses
 */

static SECStatus
GetKeyUsage(CERTCertificate *cert)
{
    SECStatus rv;
    SECItem tmpitem;

    rv = CERT_FindKeyUsageExtension(cert, &tmpitem);
    if (rv == SECSuccess) {
        /* remember the actual value of the extension */
        cert->rawKeyUsage = tmpitem.len ? tmpitem.data[0] : 0;
        cert->keyUsagePresent = PR_TRUE;
        cert->keyUsage = cert->rawKeyUsage;

        PORT_Free(tmpitem.data);
        tmpitem.data = NULL;
    } else {
        /* if the extension is not present, then we allow all uses */
        cert->keyUsage = KU_ALL;
        cert->rawKeyUsage = KU_ALL;
        cert->keyUsagePresent = PR_FALSE;
    }

    if (CERT_GovtApprovedBitSet(cert)) {
        cert->keyUsage |= KU_NS_GOVT_APPROVED;
        cert->rawKeyUsage |= KU_NS_GOVT_APPROVED;
    }

    return (SECSuccess);
}

static SECStatus
findOIDinOIDSeqByTagNum(CERTOidSequence *seq, SECOidTag tagnum)
{
    SECItem **oids;
    SECItem *oid;
    SECStatus rv = SECFailure;

    if (seq != NULL) {
        oids = seq->oids;
        while (oids != NULL && *oids != NULL) {
            oid = *oids;
            if (SECOID_FindOIDTag(oid) == tagnum) {
                rv = SECSuccess;
                break;
            }
            oids++;
        }
    }
    return rv;
}

/*
 * fill in nsCertType field of the cert based on the cert extension
 */

SECStatus
cert_GetCertType(CERTCertificate *cert)
{
    PRUint32 nsCertType;

    if (cert->nsCertType) {
        /* once set, no need to recalculate */
        return SECSuccess;
    }
    nsCertType = cert_ComputeCertType(cert);

    /* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */
    PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32));
    PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType);
    return SECSuccess;
}

PRBool
cert_IsIPsecOID(CERTOidSequence *extKeyUsage)
{
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_IKE) == SECSuccess) {
        return PR_TRUE;
    }
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_IPSEC_IKE_END) == SECSuccess) {
        return PR_TRUE;
    }
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_IPSEC_IKE_INTERMEDIATE) == SECSuccess) {
        return PR_TRUE;
    }
    /* these are now deprecated, but may show up. Treat them the same as IKE */
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_END) == SECSuccess) {
        return PR_TRUE;
    }
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL) == SECSuccess) {
        return PR_TRUE;
    }
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_EXT_KEY_USAGE_IPSEC_USER) == SECSuccess) {
        return PR_TRUE;
    }
    /* this one should probably be in cert_ComputeCertType and set all usages? */
    if (findOIDinOIDSeqByTagNum(
            extKeyUsage, SEC_OID_X509_ANY_EXT_KEY_USAGE) == SECSuccess) {
        return PR_TRUE;
    }
    return PR_FALSE;
}

PRUint32
cert_ComputeCertType(CERTCertificate *cert)
{
    SECStatus rv;
    SECItem tmpitem;
    SECItem encodedExtKeyUsage;
    CERTOidSequence *extKeyUsage = NULL;
    CERTBasicConstraints basicConstraint;
    PRUint32 nsCertType = 0;
    PRBool isCA = PR_FALSE;

    tmpitem.data = NULL;
    CERT_FindNSCertTypeExtension(cert, &tmpitem);
    encodedExtKeyUsage.data = NULL;
    rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE,
                                &encodedExtKeyUsage);
    if (rv == SECSuccess) {
        extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage);
    }
    rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
    if (rv == SECSuccess) {
        isCA = basicConstraint.isCA;
    }
    if (tmpitem.data != NULL || extKeyUsage != NULL) {
        if (tmpitem.data == NULL || tmpitem.len == 0) {
            nsCertType = 0;
        } else {
            nsCertType = tmpitem.data[0];
        }

        /* free tmpitem data pointer to avoid memory leak */
        PORT_Free(tmpitem.data);
        tmpitem.data = NULL;

        /*
         * for this release, we will allow SSL certs with an email address
         * to be used for email
         */

        if ((nsCertType & NS_CERT_TYPE_SSL_CLIENT) && cert->emailAddr &&
            cert->emailAddr[0]) {
            nsCertType |= NS_CERT_TYPE_EMAIL;
        }
        /*
         * for this release, we will allow SSL intermediate CAs to be
         * email intermediate CAs too.
         */

        if (nsCertType & NS_CERT_TYPE_SSL_CA) {
            nsCertType |= NS_CERT_TYPE_EMAIL_CA;
        }
        /*
         * allow a cert with the extended key usage of EMail Protect
         * to be used for email or as an email CA, if basic constraints
         * indicates that it is a CA.
         */

        if (findOIDinOIDSeqByTagNum(extKeyUsage,
                                    SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) ==
            SECSuccess) {
            nsCertType |= isCA ? NS_CERT_TYPE_EMAIL_CA : NS_CERT_TYPE_EMAIL;
        }
        if (findOIDinOIDSeqByTagNum(
                extKeyUsage, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == SECSuccess) {
            nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
        }
        /*
         * Treat certs with step-up OID as also having SSL server type.
         * COMODO needs this behaviour until June 2020.  See Bug 737802.
         */

        if (findOIDinOIDSeqByTagNum(extKeyUsage,
                                    SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
            SECSuccess) {
            nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_SERVER;
        }
        if (findOIDinOIDSeqByTagNum(
                extKeyUsage, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == SECSuccess) {
            nsCertType |= isCA ? NS_CERT_TYPE_SSL_CA : NS_CERT_TYPE_SSL_CLIENT;
        }
        if (cert_IsIPsecOID(extKeyUsage)) {
            nsCertType |= isCA ? NS_CERT_TYPE_IPSEC_CA : NS_CERT_TYPE_IPSEC;
        }
        if (findOIDinOIDSeqByTagNum(
                extKeyUsage, SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == SECSuccess) {
            nsCertType |= isCA ? NS_CERT_TYPE_OBJECT_SIGNING_CA : NS_CERT_TYPE_OBJECT_SIGNING;
        }
        if (findOIDinOIDSeqByTagNum(
                extKeyUsage, SEC_OID_EXT_KEY_USAGE_TIME_STAMP) == SECSuccess) {
            nsCertType |= EXT_KEY_USAGE_TIME_STAMP;
        }
        if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_OCSP_RESPONDER) ==
            SECSuccess) {
            nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
        }
    } else {
        /* If no NS Cert Type extension and no EKU extension, then */
        nsCertType = 0;
        if (CERT_IsCACert(cert, &nsCertType))
            nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
        /* if the basic constraint extension says the cert is a CA, then
           allow SSL CA and EMAIL CA and Status Responder */

        if (isCA) {
            nsCertType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
                           EXT_KEY_USAGE_STATUS_RESPONDER);
        }
        /* allow any ssl or email (no ca or object signing. */
        nsCertType |= NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER |
                      NS_CERT_TYPE_EMAIL;
    }

    /* IPSEC is allowed to use SSL client and server certs as well as email certs */
    if (nsCertType & (NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL)) {
        nsCertType |= NS_CERT_TYPE_IPSEC;
    }
    if (nsCertType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA)) {
        nsCertType |= NS_CERT_TYPE_IPSEC_CA;
    }

    if (encodedExtKeyUsage.data != NULL) {
        PORT_Free(encodedExtKeyUsage.data);
    }
    if (extKeyUsage != NULL) {
        CERT_DestroyOidSequence(extKeyUsage);
    }
    return nsCertType;
}

/*
 * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate
 */

SECStatus
cert_GetKeyID(CERTCertificate *cert)
{
    SECItem tmpitem;
    SECStatus rv;

    cert->subjectKeyID.len = 0;

    /* see of the cert has a key identifier extension */
    rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
    if (rv == SECSuccess) {
        cert->subjectKeyID.data =
            (unsigned char *)PORT_ArenaAlloc(cert->arena, tmpitem.len);
        if (cert->subjectKeyID.data != NULL) {
            PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len);
            cert->subjectKeyID.len = tmpitem.len;
            cert->keyIDGenerated = PR_FALSE;
        }

        PORT_Free(tmpitem.data);
    }

    /* if the cert doesn't have a key identifier extension, then generate one*/
    if (cert->subjectKeyID.len == 0) {
        /*
         * pkix says that if the subjectKeyID is not present, then we should
         * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert
         */

        cert->subjectKeyID.data =
            (unsigned char *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH);
        if (cert->subjectKeyID.data != NULL) {
            rv = PK11_HashBuf(SEC_OID_SHA1, cert->subjectKeyID.data,
                              cert->derPublicKey.data, cert->derPublicKey.len);
            if (rv == SECSuccess) {
                cert->subjectKeyID.len = SHA1_LENGTH;
            }
        }
    }

    if (cert->subjectKeyID.len == 0) {
        return (SECFailure);
    }
    return (SECSuccess);
}

static PRBool
cert_IsRootCert(CERTCertificate *cert)
{
    SECStatus rv;
    SECItem tmpitem;

    /* cache the authKeyID extension, if present */
    cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert);

    /* it MUST be self-issued to be a root */
    if (cert->derIssuer.len == 0 ||
        !SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject)) {
        return PR_FALSE;
    }

    /* check the authKeyID extension */
    if (cert->authKeyID) {
        /* authority key identifier is present */
        if (cert->authKeyID->keyID.len > 0) {
            /* the keyIdentifier field is set, look for subjectKeyID */
            rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
            if (rv == SECSuccess) {
                PRBool match;
                /* also present, they MUST match for it to be a root */
                match =
                    SECITEM_ItemsAreEqual(&cert->authKeyID->keyID, &tmpitem);
                PORT_Free(tmpitem.data);
                if (!match)
                    return PR_FALSE; /* else fall through */
            } else {
                /* the subject key ID is required when AKI is present */
                return PR_FALSE;
            }
        }
        if (cert->authKeyID->authCertIssuer) {
            SECItem *caName;
            caName = (SECItem *)CERT_GetGeneralNameByType(
                cert->authKeyID->authCertIssuer, certDirectoryName, PR_TRUE);
            if (caName) {
                if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) {
                    return PR_FALSE;
                } /* else fall through */
            }     /* else ??? could not get general name as directory name? */
        }
        if (cert->authKeyID->authCertSerialNumber.len > 0) {
            if (!SECITEM_ItemsAreEqual(
                    &cert->serialNumber,
                    &cert->authKeyID->authCertSerialNumber)) {
                return PR_FALSE;
            } /* else fall through */
        }
        /* all of the AKI fields that were present passed the test */
        return PR_TRUE;
    }
    /* else the AKI was not present, so this is a root */
    return PR_TRUE;
}

/*
 * take a DER certificate and decode it into a certificate structure
 */

CERTCertificate *
CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
                          char *nickname)
{
    CERTCertificate *cert;
    PLArenaPool *arena;
    void *data;
    int rv;
    int len;
    char *tmpname;

    /* make a new arena */
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        return 0;
    }

    /* allocate the certificate structure */
    cert = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));

    if (!cert) {
        goto loser;
    }

    cert->arena = arena;

    if (copyDER) {
        /* copy the DER data for the cert into this arena */
        data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len);
        if (!data) {
            goto loser;
        }
        cert->derCert.data = (unsigned char *)data;
        cert->derCert.len = derSignedCert->len;
        PORT_Memcpy(data, derSignedCert->data, derSignedCert->len);
    } else {
        /* point to passed in DER data */
        cert->derCert = *derSignedCert;
    }

    /* decode the certificate info */
    rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate,
                                &cert->derCert);

    if (rv) {
        goto loser;
    }

    if (cert_HasUnknownCriticalExten(cert->extensions) == PR_TRUE) {
        cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE;
    }

    /* generate and save the database key for the cert */
    rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber,
                                 &cert->certKey);
    if (rv) {
        goto loser;
    }

    /* set the nickname */
    if (nickname == NULL) {
        cert->nickname = NULL;
    } else {
        /* copy and install the nickname */
        len = PORT_Strlen(nickname) + 1;
        cert->nickname = (char *)PORT_ArenaAlloc(arena, len);
        if (cert->nickname == NULL) {
            goto loser;
        }

        PORT_Memcpy(cert->nickname, nickname, len);
    }

    /* set the email address */
    cert->emailAddr = cert_GetCertificateEmailAddresses(cert);

    /* initialize the subjectKeyID */
    rv = cert_GetKeyID(cert);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* initialize keyUsage */
    rv = GetKeyUsage(cert);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* determine if this is a root cert */
    cert->isRoot = cert_IsRootCert(cert);

    /* initialize the certType */
    rv = cert_GetCertType(cert);
    if (rv != SECSuccess) {
        goto loser;
    }

    tmpname = CERT_NameToAscii(&cert->subject);
    if (tmpname != NULL) {
        cert->subjectName = PORT_ArenaStrdup(cert->arena, tmpname);
        PORT_Free(tmpname);
    }

    tmpname = CERT_NameToAscii(&cert->issuer);
    if (tmpname != NULL) {
        cert->issuerName = PORT_ArenaStrdup(cert->arena, tmpname);
        PORT_Free(tmpname);
    }

    cert->referenceCount = 1;
    cert->slot = NULL;
    cert->pkcs11ID = CK_INVALID_HANDLE;
    cert->dbnickname = NULL;

    return (cert);

loser:

    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (0);
}

CERTCertificate *
__CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
                            char *nickname)
{
    return CERT_DecodeDERCertificate(derSignedCert, copyDER, nickname);
}

CERTValidity *
CERT_CreateValidity(PRTime notBefore, PRTime notAfter)
{
    CERTValidity *v;
    int rv;
    PLArenaPool *arena;

    if (notBefore > notAfter) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return NULL;
    }
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        return (0);
    }

    v = (CERTValidity *)PORT_ArenaZAlloc(arena, sizeof(CERTValidity));
    if (v) {
        v->arena = arena;
        rv = DER_EncodeTimeChoice(arena, &v->notBefore, notBefore);
        if (rv)
            goto loser;
        rv = DER_EncodeTimeChoice(arena, &v->notAfter, notAfter);
        if (rv)
            goto loser;
    }
    return v;

loser:
    CERT_DestroyValidity(v);
    return 0;
}

SECStatus
CERT_CopyValidity(PLArenaPool *arena, CERTValidity *to, CERTValidity *from)
{
    SECStatus rv;

    CERT_DestroyValidity(to);
    to->arena = arena;

    rv = SECITEM_CopyItem(arena, &to->notBefore, &from->notBefore);
    if (rv)
        return rv;
    rv = SECITEM_CopyItem(arena, &to->notAfter, &from->notAfter);
    return rv;
}

void
CERT_DestroyValidity(CERTValidity *v)
{
    if (v && v->arena) {
        PORT_FreeArena(v->arena, PR_FALSE);
    }
    return;
}

/*
** Amount of time that a certifiate is allowed good before it is actually
** good. This is used for pending certificates, ones that are about to be
** valid. The slop is designed to allow for some variance in the clocks
** of the machine checking the certificate.
*/

#define PENDING_SLOP (24L * 60L * 60L)     /* seconds per day */
static PRInt32 pendingSlop = PENDING_SLOP; /* seconds */

PRInt32
CERT_GetSlopTime(void)
{
    return pendingSlop; /* seconds */
}

SECStatus
CERT_SetSlopTime(PRInt32 slop) /* seconds */
{
    if (slop < 0)
        return SECFailure;
    pendingSlop = slop;
    return SECSuccess;
}

SECStatus
CERT_GetCertTimes(const CERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
{
    SECStatus rv;

    if (!c || !notBefore || !notAfter) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    /* convert DER not-before time */
    rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore);
    if (rv) {
        return (SECFailure);
    }

    /* convert DER not-after time */
    rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter);
    if (rv) {
        return (SECFailure);
    }

    return (SECSuccess);
}

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

SECCertTimeValidity
CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t,
                         PRBool allowOverride)
{
    PRTime notBefore, notAfter, llPendingSlop, tmp1;
    SECStatus rv;

    if (!c) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return (secCertTimeUndetermined);
    }
    /* if cert is already marked OK, then don't bother to check */
    if (allowOverride && c->timeOK) {
        return (secCertTimeValid);
    }

    rv = CERT_GetCertTimes(c, ¬Before, ¬After);

    if (rv) {
        return (secCertTimeExpired); /*XXX is this the right thing to do here?*/
    }

    LL_I2L(llPendingSlop, pendingSlop);
    /* convert to micro seconds */
    LL_UI2L(tmp1, PR_USEC_PER_SEC);
    LL_MUL(llPendingSlop, llPendingSlop, tmp1);
    LL_SUB(notBefore, notBefore, llPendingSlop);
    if (LL_CMP(t, <, notBefore)) {
        PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
        return (secCertTimeNotValidYet);
    }
    if (LL_CMP(t, >, notAfter)) {
        PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
        return (secCertTimeExpired);
    }

    return (secCertTimeValid);
}

SECStatus
SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter)
{
    int rv;

    /* convert DER not-before time */
    rv = DER_DecodeTimeChoice(notBefore, &date->lastUpdate);
    if (rv) {
        return (SECFailure);
    }

    /* convert DER not-after time */
    if (date->nextUpdate.data) {
        rv = DER_DecodeTimeChoice(notAfter, &date->nextUpdate);
        if (rv) {
            return (SECFailure);
        }
    } else {
        LL_I2L(*notAfter, 0L);
    }
    return (SECSuccess);
}

/* These routines should probably be combined with the cert
 * routines using an common extraction routine.
 */

SECCertTimeValidity
SEC_CheckCrlTimes(CERTCrl *crl, PRTime t)
{
    PRTime notBefore, notAfter, llPendingSlop, tmp1;
    SECStatus rv;

    if (!crl) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return (secCertTimeUndetermined);
    }

    rv = SEC_GetCrlTimes(crl, ¬Before, ¬After);

    if (rv) {
        return (secCertTimeExpired);
    }

    LL_I2L(llPendingSlop, pendingSlop);
    /* convert to micro seconds */
    LL_I2L(tmp1, PR_USEC_PER_SEC);
    LL_MUL(llPendingSlop, llPendingSlop, tmp1);
    LL_SUB(notBefore, notBefore, llPendingSlop);
    if (LL_CMP(t, <, notBefore)) {
        PORT_SetError(SEC_ERROR_CRL_EXPIRED);
        return (secCertTimeNotValidYet);
    }

    /* If next update is omitted and the test for notBefore passes, then
       we assume that the crl is up to date.
     */

    if (LL_IS_ZERO(notAfter)) {
        return (secCertTimeValid);
    }

    if (LL_CMP(t, >, notAfter)) {
        PORT_SetError(SEC_ERROR_CRL_EXPIRED);
        return (secCertTimeExpired);
    }

    return (secCertTimeValid);
}

PRBool
SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old)
{
    PRTime newNotBefore, newNotAfter;
    PRTime oldNotBefore, oldNotAfter;
    SECStatus rv;

    /* problems with the new CRL? reject it */
    rv = SEC_GetCrlTimes(inNew, &newNotBefore, &newNotAfter);
    if (rv)
        return PR_FALSE;

    /* problems with the old CRL? replace it */
    rv = SEC_GetCrlTimes(old, &oldNotBefore, &oldNotAfter);
    if (rv)
        return PR_TRUE;

    /* Question: what about the notAfter's? */
    return ((PRBool)LL_CMP(oldNotBefore, <, newNotBefore));
}

/*
 * return required key usage and cert type based on cert usage
 */

SECStatus
CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, PRBool ca,
                                 unsigned int *retKeyUsage,
                                 unsigned int *retCertType)
{
    unsigned int requiredKeyUsage = 0;
    unsigned int requiredCertType = 0;

    if (ca) {
        switch (usage) {
            case certUsageSSLServerWithStepUp:
                requiredKeyUsage = KU_NS_GOVT_APPROVED | KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_SSL_CA;
                break;
            case certUsageSSLClient:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_SSL_CA;
                break;
            case certUsageSSLServer:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_SSL_CA;
                break;
            case certUsageIPsec:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_IPSEC_CA;
                break;
            case certUsageSSLCA:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_SSL_CA;
                break;
            case certUsageEmailSigner:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_EMAIL_CA;
                break;
            case certUsageEmailRecipient:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_EMAIL_CA;
                break;
            case certUsageObjectSigner:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA;
                break;
            case certUsageAnyCA:
            case certUsageVerifyCA:
            case certUsageStatusResponder:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA |
                                   NS_CERT_TYPE_EMAIL_CA | NS_CERT_TYPE_SSL_CA;
                break;
            default:
                PORT_Assert(0);
                goto loser;
        }
    } else {
        switch (usage) {
            case certUsageSSLClient:
                /*
                 * RFC 5280 lists digitalSignature and keyAgreement for
                 * id-kp-clientAuth.  NSS does not support the *_fixed_dh and
                 * *_fixed_ecdh client certificate types.
                 */

                requiredKeyUsage = KU_DIGITAL_SIGNATURE;
                requiredCertType = NS_CERT_TYPE_SSL_CLIENT;
                break;
            case certUsageSSLServer:
                requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
                requiredCertType = NS_CERT_TYPE_SSL_SERVER;
                break;
            case certUsageIPsec:
                /* RFC 4945 Section 5.1.3.2 */
                requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
                requiredCertType = NS_CERT_TYPE_IPSEC;
                break;
            case certUsageSSLServerWithStepUp:
                requiredKeyUsage =
                    KU_KEY_AGREEMENT_OR_ENCIPHERMENT | KU_NS_GOVT_APPROVED;
                requiredCertType = NS_CERT_TYPE_SSL_SERVER;
                break;
            case certUsageSSLCA:
                requiredKeyUsage = KU_KEY_CERT_SIGN;
                requiredCertType = NS_CERT_TYPE_SSL_CA;
                break;
            case certUsageEmailSigner:
                requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
                requiredCertType = NS_CERT_TYPE_EMAIL;
                break;
            case certUsageEmailRecipient:
                requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
                requiredCertType = NS_CERT_TYPE_EMAIL;
                break;
            case certUsageObjectSigner:
                /* RFC 5280 lists only digitalSignature for id-kp-codeSigning.
                 */

                requiredKeyUsage = KU_DIGITAL_SIGNATURE;
                requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING;
                break;
            case certUsageStatusResponder:
                requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
                requiredCertType = EXT_KEY_USAGE_STATUS_RESPONDER;
                break;
            default:
                PORT_Assert(0);
                goto loser;
        }
    }

    if (retKeyUsage != NULL) {
        *retKeyUsage = requiredKeyUsage;
    }
    if (retCertType != NULL) {
        *retCertType = requiredCertType;
    }

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

/*
 * check the key usage of a cert against a set of required values
 */

SECStatus
CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage)
{
    if (!cert) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    /* choose between key agreement or key encipherment based on key
     * type in cert
     */

    if (requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT) {
        KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
        /* turn off the special bit */
        requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);

        switch (keyType) {
            case rsaKey:
                requiredUsage |= KU_KEY_ENCIPHERMENT;
                break;
            case rsaPssKey:
            case dsaKey:
                requiredUsage |= KU_DIGITAL_SIGNATURE;
                break;
            case dhKey:
                requiredUsage |= KU_KEY_AGREEMENT;
                break;
            case ecKey:
                /* Accept either signature or agreement. */
                if (!(cert->keyUsage &
                      (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)))
                    goto loser;
                break;
            default:
                goto loser;
        }
    }

    /* Allow either digital signature or non-repudiation */
    if (requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION) {
        /* turn off the special bit */
        requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION);

        if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)))
            goto loser;
    }

    if ((cert->keyUsage & requiredUsage) == requiredUsage)
        return SECSuccess;

loser:
    PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
    return SECFailure;
}

CERTCertificate *
CERT_DupCertificate(CERTCertificate *c)
{
    if (c) {
        NSSCertificate *tmp = STAN_GetNSSCertificate(c);
        nssCertificate_AddRef(tmp);
    }
    return c;
}

SECStatus
CERT_GetCertificateDer(const CERTCertificate *c, SECItem *der)
{
    if (!c || !der) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    *der = c->derCert;
    return SECSuccess;
}

/*
 * Allow use of default cert database, so that apps(such as mozilla) don't
 * have to pass the handle all over the place.
 */

static CERTCertDBHandle *default_cert_db_handle = 0;

void
CERT_SetDefaultCertDB(CERTCertDBHandle *handle)
{
    default_cert_db_handle = handle;

    return;
}

CERTCertDBHandle *
CERT_GetDefaultCertDB(void)
{
    return (default_cert_db_handle);
}

/* XXX this would probably be okay/better as an xp routine? */
static void
sec_lower_string(char *s)
{
    if (s == NULL) {
        return;
    }

    while (*s) {
        *s = PORT_Tolower((unsigned char)*s);
        s++;
    }

    return;
}

static PRBool
cert_IsIPAddr(const char *hn)
{
    PRBool isIPaddr = PR_FALSE;
    PRNetAddr netAddr;
    isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
    return isIPaddr;
}

/*
** Add a domain name to the list of names that the user has explicitly
** allowed (despite cert name mismatches) for use with a server cert.
*/

SECStatus
CERT_AddOKDomainName(CERTCertificate *cert, const char *hn)
{
    CERTOKDomainName *domainOK;
    int newNameLen;

    if (!hn || !(newNameLen = strlen(hn))) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena, sizeof(*domainOK));
    if (!domainOK) {
        return SECFailure; /* error code is already set. */
    }
    domainOK->name = (char *)PORT_ArenaZAlloc(cert->arena, newNameLen + 1);
    if (!domainOK->name) {
        return SECFailure; /* error code is already set. */
    }

    PORT_Strncpy(domainOK->name, hn, newNameLen + 1);
    sec_lower_string(domainOK->name);

    /* put at head of list. */
    domainOK->next = cert->domainOK;
    cert->domainOK = domainOK;
    return SECSuccess;
}

/* returns SECSuccess if hn matches pattern cn,
** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match,
** returns SECFailure with some other error code if another error occurs.
**
** This function may modify string cn, so caller must pass a modifiable copy.
*/

static SECStatus
cert_TestHostName(char *cn, const char *hn)
{
    static int useShellExp = -1;

    if (useShellExp < 0) {
        useShellExp = (NULL != PR_GetEnvSecure("NSS_USE_SHEXP_IN_CERT_NAME"));
    }
    if (useShellExp) {
        /* Backward compatible code, uses Shell Expressions (SHEXP). */
        int regvalid = PORT_RegExpValid(cn);
        if (regvalid != NON_SXP) {
            SECStatus rv;
            /* cn is a regular expression, try to match the shexp */
            int match = PORT_RegExpCaseSearch(hn, cn);

            if (match == 0) {
                rv = SECSuccess;
            } else {
                PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
                rv = SECFailure;
            }
            return rv;
        }
    } else {
        /* New approach conforms to RFC 6125. */
        char *wildcard = PORT_Strchr(cn, '*');
        char *firstcndot = PORT_Strchr(cn, '.');
        char *secondcndot =
            firstcndot ? PORT_Strchr(firstcndot + 1, '.') : NULL;
        char *firsthndot = PORT_Strchr(hn, '.');

        /* For a cn pattern to be considered valid, the wildcard character...
         * - may occur only in a DNS name with at least 3 components, and
         * - may occur only as last character in the first component, and
         * - may be preceded by additional characters, and
         * - must not be preceded by an IDNA ACE prefix (xn--)
         */

        if (wildcard && secondcndot && secondcndot[1] && firsthndot &&
            firstcndot - wildcard == 1           /* wildcard is last char in first component */
            && secondcndot - firstcndot > 1      /* second component is non-empty */
            && PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */
            && !PORT_Strncasecmp(cn, hn, wildcard - cn) &&
            !PORT_Strcasecmp(firstcndot, firsthndot)
            /* If hn starts with xn--, then cn must start with wildcard */
            && (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) {
            /* valid wildcard pattern match */
            return SECSuccess;
        }
    }
    /* String cn has no wildcard or shell expression.
     * Compare entire string hn with cert name.
     */

    if (PORT_Strcasecmp(hn, cn) == 0) {
        return SECSuccess;
    }

    PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
    return SECFailure;
}

SECStatus
cert_VerifySubjectAltName(const CERTCertificate *cert, const char *hn)
{
    PLArenaPool *arena = NULL;
    CERTGeneralName *nameList = NULL;
    CERTGeneralName *current;
    char *cn;
    int cnBufLen;
    int DNSextCount = 0;
    int IPextCount = 0;
    PRBool isIPaddr = PR_FALSE;
    SECStatus rv = SECFailure;
    SECItem subAltName;
    PRNetAddr netAddr;
    char cnbuf[128];

    subAltName.data = NULL;
    cn = cnbuf;
    cnBufLen = sizeof cnbuf;

    rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
                                &subAltName);
    if (rv != SECSuccess) {
        goto fail;
    }
    isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena)
        goto fail;

    nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
    if (!current)
        goto fail;

    do {
        switch (current->type) {
            case certDNSName:
                if (!isIPaddr) {
                    /* DNS name current->name.other.data is not null terminated.
                    ** so must copy it.
                    */

                    int cnLen = current->name.other.len;
                    rv = CERT_RFC1485_EscapeAndQuote(
                        cn, cnBufLen, (char *)current->name.other.data, cnLen);
                    if (rv != SECSuccess &&
                        PORT_GetError() == SEC_ERROR_OUTPUT_LEN) {
                        cnBufLen =
                            cnLen * 3 + 3; /* big enough for worst case */
                        cn = (char *)PORT_ArenaAlloc(arena, cnBufLen);
                        if (!cn)
                            goto fail;
                        rv = CERT_RFC1485_EscapeAndQuote(
                            cn, cnBufLen, (char *)current->name.other.data,
                            cnLen);
                    }
                    if (rv == SECSuccess)
                        rv = cert_TestHostName(cn, hn);
                    if (rv == SECSuccess)
                        goto finish;
                }
                DNSextCount++;
                break;
            case certIPAddress:
                if (isIPaddr) {
                    int match = 0;
                    PRIPv6Addr v6Addr;
                    if (current->name.other.len == 4 && /* IP v4 address */
                        netAddr.inet.family == PR_AF_INET) {
                        match = !memcmp(&netAddr.inet.ip,
                                        current->name.other.data, 4);
                    } else if (current->name.other.len ==
                                   16 && /* IP v6 address */
                               netAddr.ipv6.family == PR_AF_INET6) {
                        match = !memcmp(&netAddr.ipv6.ip,
                                        current->name.other.data, 16);
                    } else if (current->name.other.len ==
                                   16 && /* IP v6 address */
                               netAddr.inet.family == PR_AF_INET) {
                        /* convert netAddr to ipv6, then compare. */
                        /* ipv4 must be in Network Byte Order on input. */
                        PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr);
                        match = !memcmp(&v6Addr, current->name.other.data, 16);
                    } else if (current->name.other.len == 4 && /* IP v4 address */
                               netAddr.inet.family == PR_AF_INET6) {
                        /* convert netAddr to ipv6, then compare. */
                        PRUint32 ipv4 = (current->name.other.data[0] << 24) |
                                        (current->name.other.data[1] << 16) |
                                        (current->name.other.data[2] << 8) |
                                        current->name.other.data[3];
                        /* ipv4 must be in Network Byte Order on input. */
                        PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr);
                        match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16);
                    }
                    if (match) {
                        rv = SECSuccess;
                        goto finish;
                    }
                }
                IPextCount++;
                break;
            default:
                break;
        }
        current = CERT_GetNextGeneralName(current);
    } while (current != nameList);

fail:

    if (!(isIPaddr ? IPextCount : DNSextCount)) {
        /* no relevant value in the extension was found. */
        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
    } else {
        PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
    }
    rv = SECFailure;

finish:

    /* Don't free nameList, it's part of the arena. */
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    if (subAltName.data) {
        SECITEM_FreeItem(&subAltName, PR_FALSE);
    }

    return rv;
}

/*
 * If found:
 *   - subAltName contains the extension (caller must free)
 *   - return value is the decoded namelist (allocated off arena)
 * if not found, or if failure to decode:
 *   - return value is NULL
 */

CERTGeneralName *
cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena)
{
    CERTGeneralName *nameList = NULL;
    SECStatus rv = SECFailure;
    SECItem subAltName;

    if (!cert || !arena)
        return NULL;

    subAltName.data = NULL;

    rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
                                &subAltName);
    if (rv != SECSuccess)
        return NULL;

    nameList = CERT_DecodeAltNameExtension(arena, &subAltName);
    SECITEM_FreeItem(&subAltName, PR_FALSE);
    return nameList;
}

PRUint32
cert_CountDNSPatterns(CERTGeneralName *firstName)
{
    CERTGeneralName *current;
    PRUint32 count = 0;

    if (!firstName)
        return 0;

    current = firstName;
    do {
        switch (current->type) {
            case certDNSName:
            case certIPAddress:
                ++count;
                break;
            default:
                break;
        }
        current = CERT_GetNextGeneralName(current);
    } while (current != firstName);

    return count;
}

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif

/* will fill nickNames,
 * will allocate all data from nickNames->arena,
 * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns,
 * will ensure the numberOfGeneralNames matches the number of output entries.
 */

SECStatus
cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName,
                                    PRUint32 numberOfGeneralNames,
                                    CERTCertNicknames *nickNames)
{
    CERTGeneralName *currentInput;
    char **currentOutput;

    if (!firstName || !nickNames || !numberOfGeneralNames)
        return SECFailure;

    nickNames->numnicknames = numberOfGeneralNames;
    nickNames->nicknames = PORT_ArenaAlloc(
        nickNames->arena, sizeof(char *) * numberOfGeneralNames);
    if (!nickNames->nicknames)
        return SECFailure;

    currentInput = firstName;
    currentOutput = nickNames->nicknames;
    do {
        char *cn = NULL;
        char ipbuf[INET6_ADDRSTRLEN];
        PRNetAddr addr;

        if (numberOfGeneralNames < 1) {
            /* internal consistency error */
            return SECFailure;
        }

        switch (currentInput->type) {
            case certDNSName:
                /* DNS name currentInput->name.other.data is not null
                 *terminated.
                 ** so must copy it.
                 */

                cn = (char *)PORT_ArenaAlloc(nickNames->arena,
                                             currentInput->name.other.len + 1);
                if (!cn)
                    return SECFailure;
                PORT_Memcpy(cn, currentInput->name.other.data,
                            currentInput->name.other.len);
                cn[currentInput->name.other.len] = 0;
                break;
            case certIPAddress:
                if (currentInput->name.other.len == 4) {
                    addr.inet.family = PR_AF_INET;
                    memcpy(&addr.inet.ip, currentInput->name.other.data,
                           currentInput->name.other.len);
                } else if (currentInput->name.other.len == 16) {
                    addr.ipv6.family = PR_AF_INET6;
                    memcpy(&addr.ipv6.ip, currentInput->name.other.data,
                           currentInput->name.other.len);
                }
                if (PR_NetAddrToString(&addr, ipbuf, sizeof(ipbuf)) ==
                    PR_FAILURE)
                    return SECFailure;
                cn = PORT_ArenaStrdup(nickNames->arena, ipbuf);
                if (!cn)
                    return SECFailure;
                break;
            default:
                break;
        }
        if (cn) {
            *currentOutput = cn;
            nickNames->totallen += PORT_Strlen(cn);
            ++currentOutput;
            --numberOfGeneralNames;
        }
        currentInput = CERT_GetNextGeneralName(currentInput);
    } while (currentInput != firstName);

    return (numberOfGeneralNames == 0) ? SECSuccess : SECFailure;
}

/*
 * Collect all valid DNS names from the given cert.
 * The output arena will reference some temporaray data,
 * but this saves us from dealing with two arenas.
 * The caller may free all data by freeing CERTCertNicknames->arena.
 */

CERTCertNicknames *
CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert)
{
    CERTGeneralName *generalNames;
    CERTCertNicknames *nickNames;
    PLArenaPool *arena;
    char *singleName;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena) {
        return NULL;
    }

    nickNames = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
    if (!nickNames) {
        PORT_FreeArena(arena, PR_FALSE);
        return NULL;
    }

    /* init the structure */
    nickNames->arena = arena;
    nickNames->head = NULL;
    nickNames->numnicknames = 0;
    nickNames->nicknames = NULL;
    nickNames->totallen = 0;

    generalNames = cert_GetSubjectAltNameList(cert, arena);
    if (generalNames) {
        SECStatus rv_getnames = SECFailure;
        PRUint32 numNames = cert_CountDNSPatterns(generalNames);

        if (numNames) {
            rv_getnames = cert_GetDNSPatternsFromGeneralNames(
                generalNames, numNames, nickNames);
        }

        /* if there were names, we'll exit now, either with success or failure
         */

        if (numNames) {
            if (rv_getnames == SECSuccess) {
                return nickNames;
            }

            /* failure to produce output */
            PORT_FreeArena(arena, PR_FALSE);
            return NULL;
        }
    }

    /* no SAN extension or no names found in extension */
    singleName = CERT_GetCommonName(&cert->subject);
    if (singleName) {
        nickNames->numnicknames = 1;
        nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *));
        if (nickNames->nicknames) {
            *nickNames->nicknames = PORT_ArenaStrdup(arena, singleName);
        }
        PORT_Free(singleName);

        /* Did we allocate both the buffer of pointers and the string? */
        if (nickNames->nicknames && *nickNames->nicknames) {
            return nickNames;
        }
    }

    PORT_FreeArena(arena, PR_FALSE);
    return NULL;
}

/* Make sure that the name of the host we are connecting to matches the
 * name that is incoded in the common-name component of the certificate
 * that they are using.
 */

SECStatus
CERT_VerifyCertName(const CERTCertificate *cert, const char *hn)
{
    char *cn;
    SECStatus rv;
    CERTOKDomainName *domainOK;

    if (!hn || !strlen(hn)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    /* if the name is one that the user has already approved, it's OK. */
    for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) {
        if (0 == PORT_Strcasecmp(hn, domainOK->name)) {
            return SECSuccess;
        }
    }

    /* Per RFC 2818, if the SubjectAltName extension is present, it must
    ** be used as the cert's identity.
    */

    rv = cert_VerifySubjectAltName(cert, hn);
    if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND)
        return rv;

    cn = CERT_GetCommonName(&cert->subject);
    if (cn) {
        PRBool isIPaddr = cert_IsIPAddr(hn);
        if (isIPaddr) {
            if (PORT_Strcasecmp(hn, cn) == 0) {
                rv = SECSuccess;
            } else {
                PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
                rv = SECFailure;
            }
        } else {
            rv = cert_TestHostName(cn, hn);
        }
        PORT_Free(cn);
    } else
        PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
    return rv;
}

PRBool
CERT_CompareCerts(const CERTCertificate *c1, const CERTCertificate *c2)
{
    SECComparison comp;

    comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
    if (comp == SECEqual) { /* certs are the same */
        return (PR_TRUE);
    } else {
        return (PR_FALSE);
    }
}

static SECStatus
StringsEqual(char *s1, char *s2)
{
    if ((s1 == NULL) || (s2 == NULL)) {
        if (s1 != s2) { /* only one is null */
            return (SECFailure);
        }
        return (SECSuccess); /* both are null */
    }

    if (PORT_Strcmp(s1, s2) != 0) {
        return (SECFailure); /* not equal */
    }

    return (SECSuccess); /* strings are equal */
}

PRBool
CERT_CompareCertsForRedirection(CERTCertificate *c1, CERTCertificate *c2)
{
    SECComparison comp;
    char *c1str, *c2str;
    SECStatus eq;

    comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
    if (comp == SECEqual) { /* certs are the same */
        return (PR_TRUE);
    }

    /* check if they are issued by the same CA */
    comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer);
    if (comp != SECEqual) { /* different issuer */
        return (PR_FALSE);
    }

    /* check country name */
    c1str = CERT_GetCountryName(&c1->subject);
    c2str = CERT_GetCountryName(&c2->subject);
    eq = StringsEqual(c1str, c2str);
    PORT_Free(c1str);
    PORT_Free(c2str);
    if (eq != SECSuccess) {
        return (PR_FALSE);
    }

    /* check locality name */
    c1str = CERT_GetLocalityName(&c1->subject);
    c2str = CERT_GetLocalityName(&c2->subject);
    eq = StringsEqual(c1str, c2str);
    PORT_Free(c1str);
    PORT_Free(c2str);
    if (eq != SECSuccess) {
        return (PR_FALSE);
    }

    /* check state name */
    c1str = CERT_GetStateName(&c1->subject);
    c2str = CERT_GetStateName(&c2->subject);
    eq = StringsEqual(c1str, c2str);
    PORT_Free(c1str);
    PORT_Free(c2str);
    if (eq != SECSuccess) {
        return (PR_FALSE);
    }

    /* check org name */
    c1str = CERT_GetOrgName(&c1->subject);
    c2str = CERT_GetOrgName(&c2->subject);
    eq = StringsEqual(c1str, c2str);
    PORT_Free(c1str);
    PORT_Free(c2str);
    if (eq != SECSuccess) {
        return (PR_FALSE);
    }

#ifdef NOTDEF
    /* check orgUnit name */
    /*
     * We need to revisit this and decide which fields should be allowed to be
     * different
     */

    c1str = CERT_GetOrgUnitName(&c1->subject);
    c2str = CERT_GetOrgUnitName(&c2->subject);
    eq = StringsEqual(c1str, c2str);
    PORT_Free(c1str);
    PORT_Free(c2str);
    if (eq != SECSuccess) {
        return (PR_FALSE);
    }
#endif

    return (PR_TRUE); /* all fields but common name are the same */
}

/* CERT_CertChainFromCert and CERT_DestroyCertificateList moved
   to certhigh.c */


CERTIssuerAndSN *
CERT_GetCertIssuerAndSN(PLArenaPool *arena, CERTCertificate *cert)
{
    CERTIssuerAndSN *result;
    SECStatus rv;

    if (arena == NULL) {
        arena = cert->arena;
    }

    result = (CERTIssuerAndSN *)PORT_ArenaZAlloc(arena, sizeof(*result));
    if (result == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return NULL;
    }

    rv = SECITEM_CopyItem(arena, &result->derIssuer, &cert->derIssuer);
    if (rv != SECSuccess)
        return NULL;

    rv = CERT_CopyName(arena, &result->issuer, &cert->issuer);
    if (rv != SECSuccess)
        return NULL;

    rv = SECITEM_CopyItem(arena, &result->serialNumber, &cert->serialNumber);
    if (rv != SECSuccess)
        return NULL;

    return result;
}

char *
CERT_MakeCANickname(CERTCertificate *cert)
{
    char *firstname = NULL;
    char *org = NULL;
    char *nickname = NULL;
    int count;
    CERTCertificate *dummycert;

    firstname = CERT_GetCommonName(&cert->subject);
    if (firstname == NULL) {
        firstname = CERT_GetOrgUnitName(&cert->subject);
    }

    org = CERT_GetOrgName(&cert->issuer);
    if (org == NULL) {
        org = CERT_GetDomainComponentName(&cert->issuer);
        if (org == NULL) {
            if (firstname) {
                org = firstname;
                firstname = NULL;
            } else {
                org = PORT_Strdup("Unknown CA");
            }
        }
    }

    /* can only fail if PORT_Strdup fails, in which case
     * we're having memory problems. */

    if (org == NULL) {
        goto done;
    }

    count = 1;
    while (1) {

        if (firstname) {
            if (count == 1) {
                nickname = PR_smprintf("%s - %s", firstname, org);
            } else {
                nickname = PR_smprintf("%s - %s #%d", firstname, org, count);
            }
        } else {
            if (count == 1) {
                nickname = PR_smprintf("%s", org);
            } else {
                nickname = PR_smprintf("%s #%d", org, count);
            }
        }
        if (nickname == NULL) {
            goto done;
        }

        /* look up the nickname to make sure it isn't in use already */
        dummycert = CERT_FindCertByNickname(cert->dbhandle, nickname);

        if (dummycert == NULL) {
            goto done;
        }

        /* found a cert, destroy it and loop */
        CERT_DestroyCertificate(dummycert);

        /* free the nickname */
        PORT_Free(nickname);

        count++;
    }

done:
    if (firstname) {
        PORT_Free(firstname);
    }
    if (org) {
        PORT_Free(org);
    }

    return (nickname);
}

/* CERT_Import_CAChain moved to certhigh.c */

void
CERT_DestroyCrl(CERTSignedCrl *crl)
{
    SEC_DestroyCrl(crl);
}

static int
cert_Version(CERTCertificate *cert)
{
    int version = 0;
    if (cert && cert->version.data && cert->version.len) {
        version = DER_GetInteger(&cert->version);
        if (version < 0)
            version = 0;
    }
    return version;
}

static unsigned int
cert_ComputeTrustOverrides(CERTCertificate *cert, unsigned int cType)
{
    CERTCertTrust trust;
    SECStatus rv = SECFailure;

    rv = CERT_GetCertTrust(cert, &trust);

    if (rv == SECSuccess &&
        (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags)) {

        if (trust.sslFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
            cType |= NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT;
        if (trust.sslFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
            cType |= NS_CERT_TYPE_SSL_CA;
#if defined(CERTDB_NOT_TRUSTED)
        if (trust.sslFlags & CERTDB_NOT_TRUSTED)
            cType &= ~(NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_SSL_CLIENT |
                       NS_CERT_TYPE_SSL_CA);
#endif
        if (trust.emailFlags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
            cType |= NS_CERT_TYPE_EMAIL;
        if (trust.emailFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
            cType |= NS_CERT_TYPE_EMAIL_CA;
#if defined(CERTDB_NOT_TRUSTED)
        if (trust.emailFlags & CERTDB_NOT_TRUSTED)
            cType &= ~(NS_CERT_TYPE_EMAIL | NS_CERT_TYPE_EMAIL_CA);
#endif
        if (trust.objectSigningFlags &
            (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED))
            cType |= NS_CERT_TYPE_OBJECT_SIGNING;
        if (trust.objectSigningFlags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA))
            cType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
#if defined(CERTDB_NOT_TRUSTED)
        if (trust.objectSigningFlags & CERTDB_NOT_TRUSTED)
            cType &=
                ~(NS_CERT_TYPE_OBJECT_SIGNING | NS_CERT_TYPE_OBJECT_SIGNING_CA);
#endif
    }
    return cType;
}

/*
 * Does a cert belong to a CA?  We decide based on perm database trust
 * flags, Netscape Cert Type Extension, and KeyUsage Extension.
 */

PRBool
CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype)
{
    unsigned int cType = cert->nsCertType;
    PRBool ret = PR_FALSE;

    /*
     * Check if the constraints are available and it's a CA, OR if it's
     * a X.509 v1 Root CA.
     */

    CERTBasicConstraints constraints;
    if ((CERT_FindBasicConstraintExten(cert, &constraints) == SECSuccess &&
         constraints.isCA) ||
        (cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3))
        cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);

    /*
     * Apply trust overrides, if any.
     */

    cType = cert_ComputeTrustOverrides(cert, cType);
    ret = (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
                    NS_CERT_TYPE_OBJECT_SIGNING_CA))
              ? PR_TRUE
              : PR_FALSE;

    if (rettype) {
        *rettype = cType;
    }

    return ret;
}

PRBool
CERT_IsCADERCert(SECItem *derCert, unsigned int *type)
{
    CERTCertificate *cert;
    PRBool isCA;

    /* This is okay -- only looks at extensions */
    cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
    if (cert == NULL)
        return PR_FALSE;

    isCA = CERT_IsCACert(cert, type);
    CERT_DestroyCertificate(cert);
    return isCA;
}

PRBool
CERT_IsRootDERCert(SECItem *derCert)
{
    CERTCertificate *cert;
    PRBool isRoot;

    /* This is okay -- only looks at extensions */
    cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
    if (cert == NULL)
        return PR_FALSE;

    isRoot = cert->isRoot;
    CERT_DestroyCertificate(cert);
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.26 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.