/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the PKIX-C library.
 *
 * The Initial Developer of the Original Code is
 * Sun Microsystems, Inc.
 * Portions created by the Initial Developer are
 * Copyright 2004-2007 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Contributor(s):
 *   Sun Microsystems, Inc.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/*
 * pkix_pl_cert.c
 *
 * Certificate Object Functions
 *
 */

#include "pkix_pl_cert.h"

extern PKIX_PL_HashTable *cachedCertSigTable;

/* --Private-Cert-Functions------------------------------------- */

/*
 * FUNCTION: pkix_pl_Cert_IsExtensionCritical
 * DESCRIPTION:
 *
 *  Checks the Cert specified by "cert" to determine whether the extension
 *  whose tag is the UInt32 value given by "tag" is marked as a critical
 *  extension, and stores the result in "pCritical".
 *
 *  Tags are the index into the table "oids" of SECOidData defined in the
 *  file secoid.c. Constants, such as SEC_OID_X509_CERTIFICATE_POLICIES, are
 *  are defined in secoidt.h for most of the table entries.
 *
 *  If the specified tag is invalid (not in the list of tags) or if the
 *  extension is not found in the certificate, PKIX_FALSE is stored.
 *
 * PARAMETERS
 *  "cert"
 *      Address of Cert whose extensions are to be examined. Must be non-NULL.
 *  "tag"
 *      The UInt32 value of the tag for the extension whose criticality is
 *      to be determined
 *  "pCritical"
 *      Address where the Boolean value will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_pl_Cert_IsExtensionCritical(
        PKIX_PL_Cert *cert,
        PKIX_UInt32 tag,
        PKIX_Boolean *pCritical,
        void *plContext)
{
        PKIX_Boolean criticality = PKIX_FALSE;
        CERTCertExtension **extensions = NULL;
        SECStatus rv;

        PKIX_ENTER(CERT, "pkix_pl_Cert_IsExtensionCritical");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pCritical);

        extensions = cert->nssCert->extensions;
        PKIX_NULLCHECK_ONE(extensions);

        PKIX_CERT_DEBUG("\t\tCalling CERT_GetExtenCriticality).\n");
        rv = CERT_GetExtenCriticality(extensions, tag, &criticality);
        if (SECSuccess == rv) {
                *pCritical = criticality;
        } else {
                *pCritical = PKIX_FALSE;
        }

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_DecodePolicyInfo
 * DESCRIPTION:
 *
 *  Decodes the contents of the CertificatePolicy extension in the
 *  CERTCertificate pointed to by "nssCert", to create a List of
 *  CertPolicyInfos, which is stored at the address "pCertPolicyInfos".
 *  A CERTCertificate contains the DER representation of the Cert.
 *  If this certificate does not have a CertificatePolicy extension,
 *  NULL will be stored. If a List is returned, it will be immutable.
 *
 * PARAMETERS
 *  "nssCert"
 *      Address of the Cert data whose extension is to be examined. Must be
 *      non-NULL.
 *  "pCertPolicyInfos"
 *      Address where the List of CertPolicyInfos will be stored. Must be
 *      non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_pl_Cert_DecodePolicyInfo(
        CERTCertificate *nssCert,
        PKIX_List **pCertPolicyInfos,
        void *plContext)
{

        SECStatus rv;
        SECItem encodedCertPolicyInfo;

        /* Allocated in the arena; freed in CERT_Destroy... */
        CERTCertificatePolicies *certPol = NULL;
        CERTPolicyInfo **policyInfos = NULL;
        CERTPolicyInfo *policyInfo = NULL;
        CERTPolicyQualifier **policyQualifiers = NULL;
        CERTPolicyQualifier *policyQualifier = NULL;

        /* Holder for the return value */
        PKIX_List *infos = NULL;

        char *oidAscii = NULL;
        PKIX_PL_OID *pkixOID = NULL;
        PKIX_List *qualifiers = NULL;
        PKIX_PL_CertPolicyInfo *certPolicyInfo = NULL;
        PKIX_PL_CertPolicyQualifier *certPolicyQualifier = NULL;
        PKIX_PL_ByteArray *qualifierArray = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_DecodePolicyInfo");
        PKIX_NULLCHECK_TWO(nssCert, pCertPolicyInfos);

        /* get PolicyInfo as a SECItem */
        PKIX_CERT_DEBUG("\t\tCERT_FindCertExtension).\n");
        rv = CERT_FindCertExtension
                (nssCert,
                SEC_OID_X509_CERTIFICATE_POLICIES,
                &encodedCertPolicyInfo);
        if (SECSuccess != rv) {
                *pCertPolicyInfos = NULL;
                goto cleanup;
        }

        /* translate PolicyInfo to CERTCertificatePolicies */
        PKIX_CERT_DEBUG("\t\tCERT_DecodeCertificatePoliciesExtension).\n");
        certPol = CERT_DecodeCertificatePoliciesExtension
                (&encodedCertPolicyInfo);

        PORT_Free(encodedCertPolicyInfo.data);

        if (NULL == certPol) {
                PKIX_ERROR(PKIX_CERTDECODECERTIFICATEPOLICIESEXTENSIONFAILED);
        }

        /*
         * Check whether there are any policyInfos, so we can
         * avoid creating an unnecessary List
         */
        policyInfos = certPol->policyInfos;
        if (!policyInfos) {
                *pCertPolicyInfos = NULL;
                goto cleanup;
        }

        /* create a List of CertPolicyInfo Objects */
        PKIX_CHECK(PKIX_List_Create(&infos, plContext),
                PKIX_LISTCREATEFAILED);

        /*
         * Traverse the CERTCertificatePolicies structure,
         * building each PKIX_PL_CertPolicyInfo object in turn
         */
        while (*policyInfos != NULL) {
                policyInfo = *policyInfos;
                policyQualifiers = policyInfo->policyQualifiers;
                if (policyQualifiers) {
                        /* create a PKIX_List of PKIX_PL_CertPolicyQualifiers */
                        PKIX_CHECK(PKIX_List_Create(&qualifiers, plContext),
                                PKIX_LISTCREATEFAILED);

                        while (*policyQualifiers != NULL) {
                            policyQualifier = *policyQualifiers;

                            /* create the qualifier's OID object */

                            PKIX_CHECK(pkix_pl_oidBytes2Ascii
                                (&(policyQualifier->qualifierID),
                                &oidAscii,
                                plContext),
                                PKIX_OIDBYTES2ASCIIFAILED);

                            PKIX_CHECK(PKIX_PL_OID_Create
                                (oidAscii, &pkixOID, plContext),
                                PKIX_OIDCREATEFAILED);

                            /* create qualifier's ByteArray object */

                            PKIX_CHECK(PKIX_PL_ByteArray_Create
                                (policyQualifier->qualifierValue.data,
                                policyQualifier->qualifierValue.len,
                                &qualifierArray,
                                plContext),
                                PKIX_BYTEARRAYCREATEFAILED);

                            /* create a CertPolicyQualifier object */

                            PKIX_CHECK(pkix_pl_CertPolicyQualifier_Create
                                (pkixOID,
                                qualifierArray,
                                &certPolicyQualifier,
                                plContext),
                                PKIX_CERTPOLICYQUALIFIERCREATEFAILED);

                            PKIX_CHECK(PKIX_List_AppendItem
                                (qualifiers,
                                (PKIX_PL_Object *)certPolicyQualifier,
                                plContext),
                                PKIX_LISTAPPENDITEMFAILED);

                            PKIX_FREE(oidAscii);
                            PKIX_DECREF(pkixOID);
                            PKIX_DECREF(qualifierArray);
                            PKIX_DECREF(certPolicyQualifier);

                            policyQualifiers++;
                        }

                        PKIX_CHECK(PKIX_List_SetImmutable
                                (qualifiers, plContext),
                                PKIX_LISTSETIMMUTABLEFAILED);
                }


                /*
                 * Create an OID object pkixOID from policyInfo->policyID.
                 * (The CERTPolicyInfo structure has an oid field, but it
                 * is of type SECOidTag. This function wants a SECItem.)
                 */

                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                        (&(policyInfo->policyID), &oidAscii, plContext),
                        PKIX_OIDBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_OID_Create
                        (oidAscii, &pkixOID, plContext),
                        PKIX_OIDCREATEFAILED);

                /* Create a CertPolicyInfo object */
                PKIX_CHECK(pkix_pl_CertPolicyInfo_Create
                        (pkixOID, qualifiers, &certPolicyInfo, plContext),
                        PKIX_CERTPOLICYINFOCREATEFAILED);

                /* Append the new CertPolicyInfo object to the list */
                PKIX_CHECK(PKIX_List_AppendItem
                        (infos, (PKIX_PL_Object *)certPolicyInfo, plContext),
                        PKIX_LISTAPPENDITEMFAILED);

                PKIX_FREE(oidAscii);
                PKIX_DECREF(pkixOID);
                PKIX_DECREF(qualifiers);
                PKIX_DECREF(certPolicyInfo);

                policyInfos++;
        }

        /*
         * If there were no policies, we went straight to
         * cleanup, so we don't have to NULLCHECK infos.
         */
        PKIX_CHECK(PKIX_List_SetImmutable(infos, plContext),
                PKIX_LISTSETIMMUTABLEFAILED);

        *pCertPolicyInfos = infos;

cleanup:
        if (certPol) {
            PKIX_CERT_DEBUG
                ("\t\tCalling CERT_DestroyCertificatePoliciesExtension).\n");
            CERT_DestroyCertificatePoliciesExtension(certPol);
        }

        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(infos);
        }

        PKIX_FREE(oidAscii);
        PKIX_DECREF(pkixOID);
        PKIX_DECREF(qualifiers);
        PKIX_DECREF(certPolicyInfo);
        PKIX_DECREF(certPolicyQualifier);
        PKIX_DECREF(qualifierArray);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_DecodePolicyMapping
 * DESCRIPTION:
 *
 *  Decodes the contents of the PolicyMapping extension of the CERTCertificate
 *  pointed to by "nssCert", storing the resulting List of CertPolicyMaps at
 *  the address pointed to by "pCertPolicyMaps". If this certificate does not
 *  have a PolicyMapping extension, NULL will be stored. If a List is returned,
 *  it will be immutable.
 *
 * PARAMETERS
 *  "nssCert"
 *      Address of the Cert data whose extension is to be examined. Must be
 *      non-NULL.
 *  "pCertPolicyMaps"
 *      Address where the List of CertPolicyMaps will be stored. Must be
 *      non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_pl_Cert_DecodePolicyMapping(
        CERTCertificate *nssCert,
        PKIX_List **pCertPolicyMaps,
        void *plContext)
{
        SECStatus rv;
        SECItem encodedCertPolicyMaps;

        /* Allocated in the arena; freed in CERT_Destroy... */
        CERTCertificatePolicyMappings *certPolMaps = NULL;
        CERTPolicyMap **policyMaps = NULL;
        CERTPolicyMap *policyMap = NULL;

        /* Holder for the return value */
        PKIX_List *maps = NULL;

        char *issuerPolicyOIDAscii = NULL;
        char *subjectPolicyOIDAscii = NULL;
        PKIX_PL_OID *issuerDomainOID = NULL;
        PKIX_PL_OID *subjectDomainOID = NULL;
        PKIX_PL_CertPolicyMap *certPolicyMap = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_DecodePolicyMapping");
        PKIX_NULLCHECK_TWO(nssCert, pCertPolicyMaps);

        /* get PolicyMappings as a SECItem */
        PKIX_CERT_DEBUG("\t\tCERT_FindCertExtension).\n");
        rv = CERT_FindCertExtension
                (nssCert, SEC_OID_X509_POLICY_MAPPINGS, &encodedCertPolicyMaps);
        if (SECSuccess != rv) {
                *pCertPolicyMaps = NULL;
                goto cleanup;
        }

        /* translate PolicyMaps to CERTCertificatePolicyMappings */
        certPolMaps = CERT_DecodePolicyMappingsExtension
                (&encodedCertPolicyMaps);

        PORT_Free(encodedCertPolicyMaps.data);

        if (!certPolMaps) {
                PKIX_ERROR(PKIX_CERTDECODEPOLICYMAPPINGSEXTENSIONFAILED);
        }

        PKIX_NULLCHECK_ONE(certPolMaps->policyMaps);

        policyMaps = certPolMaps->policyMaps;

        /* create a List of CertPolicyMap Objects */
        PKIX_CHECK(PKIX_List_Create(&maps, plContext),
                PKIX_LISTCREATEFAILED);

        /*
         * Traverse the CERTCertificatePolicyMappings structure,
         * building each CertPolicyMap object in turn
         */
        do {
                policyMap = *policyMaps;

                /* create the OID for the issuer Domain Policy */

                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                        (&(policyMap->issuerDomainPolicy),
                        &issuerPolicyOIDAscii,
                        plContext),
                        PKIX_OIDBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_OID_Create
                        (issuerPolicyOIDAscii, &issuerDomainOID, plContext),
                        PKIX_OIDCREATEFAILED);

                /* create the OID for the subject Domain Policy */

                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                        (&(policyMap->subjectDomainPolicy),
                        &subjectPolicyOIDAscii,
                        plContext),
                        PKIX_OIDBYTES2ASCIIFAILED);

                PKIX_CHECK(PKIX_PL_OID_Create
                        (subjectPolicyOIDAscii, &subjectDomainOID, plContext),
                        PKIX_OIDCREATEFAILED);

                /* create the CertPolicyMap */

                PKIX_CHECK(pkix_pl_CertPolicyMap_Create
                        (issuerDomainOID,
                        subjectDomainOID,
                        &certPolicyMap,
                        plContext),
                        PKIX_CERTPOLICYMAPCREATEFAILED);

                PKIX_CHECK(PKIX_List_AppendItem
                        (maps, (PKIX_PL_Object *)certPolicyMap, plContext),
                        PKIX_LISTAPPENDITEMFAILED);

                PKIX_FREE(issuerPolicyOIDAscii);
                PKIX_FREE(subjectPolicyOIDAscii);
                PKIX_DECREF(issuerDomainOID);
                PKIX_DECREF(subjectDomainOID);
                PKIX_DECREF(certPolicyMap);

                policyMaps++;
        } while (*policyMaps != NULL);

        PKIX_CHECK(PKIX_List_SetImmutable(maps, plContext),
                PKIX_LISTSETIMMUTABLEFAILED);

        *pCertPolicyMaps = maps;

cleanup:
        if (certPolMaps) {
            PKIX_CERT_DEBUG
                ("\t\tCalling CERT_DestroyPolicyMappingsExtension).\n");
            CERT_DestroyPolicyMappingsExtension(certPolMaps);
        }

        PKIX_FREE(issuerPolicyOIDAscii);
        PKIX_FREE(subjectPolicyOIDAscii);
        PKIX_DECREF(issuerDomainOID);
        PKIX_DECREF(subjectDomainOID);
        PKIX_DECREF(certPolicyMap);

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_DecodePolicyConstraints
 * DESCRIPTION:
 *
 *  Decodes the contents of the PolicyConstraints extension in the
 *  CERTCertificate pointed to by "nssCert", to obtain SkipCerts values
 *  which are stored at the addresses "pExplicitPolicySkipCerts" and
 *  "pInhibitMappingSkipCerts", respectively. If this certificate does
 *  not have an PolicyConstraints extension, or if either of the optional
 *  components is not supplied, this function stores a value of -1 for any
 *  missing component.
 *
 * PARAMETERS
 *  "nssCert"
 *      Address of the Cert data whose extension is to be examined. Must be
 *      non-NULL.
 *  "pExplicitPolicySkipCerts"
 *      Address where the SkipCert value for the requireExplicitPolicy
 *      component will be stored. Must be non-NULL.
 *  "pInhibitMappingSkipCerts"
 *      Address where the SkipCert value for the inhibitPolicyMapping
 *      component will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_pl_Cert_DecodePolicyConstraints(
        CERTCertificate *nssCert,
        PKIX_Int32 *pExplicitPolicySkipCerts,
        PKIX_Int32 *pInhibitMappingSkipCerts,
        void *plContext)
{
        CERTCertificatePolicyConstraints policyConstraints;
        SECStatus rv;
        SECItem encodedCertPolicyConstraints;
        PKIX_Int32 explicitPolicySkipCerts = -1;
        PKIX_Int32 inhibitMappingSkipCerts = -1;

        PKIX_ENTER(CERT, "pkix_pl_Cert_DecodePolicyConstraints");
        PKIX_NULLCHECK_THREE
                (nssCert, pExplicitPolicySkipCerts, pInhibitMappingSkipCerts);

        /* get the two skipCert values as SECItems */
        PKIX_CERT_DEBUG("\t\tCalling CERT_FindCertExtension).\n");
        rv = CERT_FindCertExtension
                (nssCert,
                SEC_OID_X509_POLICY_CONSTRAINTS,
                &encodedCertPolicyConstraints);

        if (rv == SECSuccess) {

                policyConstraints.explicitPolicySkipCerts.data =
                        (unsigned char *)&explicitPolicySkipCerts;
                policyConstraints.inhibitMappingSkipCerts.data =
                        (unsigned char *)&inhibitMappingSkipCerts;

                /* translate DER to CERTCertificatePolicyConstraints */
                rv = CERT_DecodePolicyConstraintsExtension
                        (&policyConstraints, &encodedCertPolicyConstraints);

                PORT_Free(encodedCertPolicyConstraints.data);

                if (rv != SECSuccess) {
                    PKIX_ERROR
                        (PKIX_CERTDECODEPOLICYCONSTRAINTSEXTENSIONFAILED);
                }
        }

        *pExplicitPolicySkipCerts = explicitPolicySkipCerts;
        *pInhibitMappingSkipCerts = inhibitMappingSkipCerts;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_DecodeInhibitAnyPolicy
 * DESCRIPTION:
 *
 *  Decodes the contents of the InhibitAnyPolicy extension in the
 *  CERTCertificate pointed to by "nssCert", to obtain a SkipCerts value,
 *  which is stored at the address "pSkipCerts". If this certificate does
 *  not have an InhibitAnyPolicy extension, -1 will be stored.
 *
 * PARAMETERS
 *  "nssCert"
 *      Address of the Cert data whose InhibitAnyPolicy extension is to be
 *      processed. Must be non-NULL.
 *  "pSkipCerts"
 *      Address where the SkipCert value from the InhibitAnyPolicy extension
 *      will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_Cert_DecodeInhibitAnyPolicy(
        CERTCertificate *nssCert,
        PKIX_Int32 *pSkipCerts,
        void *plContext)
{
        CERTCertificateInhibitAny inhibitAny;
        SECStatus rv;
        SECItem encodedCertInhibitAny;
        PKIX_Int32 skipCerts = -1;

        PKIX_ENTER(CERT, "pkix_pl_Cert_DecodeInhibitAnyPolicy");
        PKIX_NULLCHECK_TWO(nssCert, pSkipCerts);

        /* get InhibitAny as a SECItem */
        PKIX_CERT_DEBUG("\t\tCalling CERT_FindCertExtension).\n");
        rv = CERT_FindCertExtension
                (nssCert, SEC_OID_X509_INHIBIT_ANY_POLICY, &encodedCertInhibitAny);

        if (rv == SECSuccess) {
                inhibitAny.inhibitAnySkipCerts.data =
                        (unsigned char *)&skipCerts;

                /* translate DER to CERTCertificateInhibitAny */
                rv = CERT_DecodeInhibitAnyExtension
                        (&inhibitAny, &encodedCertInhibitAny);

                PORT_Free(encodedCertInhibitAny.data);

                if (rv != SECSuccess) {
                        PKIX_ERROR(PKIX_CERTDECODEINHIBITANYEXTENSIONFAILED);
                }
        }

        *pSkipCerts = skipCerts;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_GetNssSubjectAltNames
 * DESCRIPTION:
 *
 *  Retrieves the Subject Alternative Names of the certificate specified by
 *  "cert" and stores it at "pNssSubjAltNames". If the Subject Alternative
 *  Name extension is not present, NULL is returned at "pNssSubjAltNames".
 *  If the Subject Alternative Names has not been previously decoded, it is
 *  decoded here with lock on the "cert" unless the flag "hasLock" indicates
 *  the lock had been obtained at a higher call level.
 *
 * PARAMETERS
 *  "cert"
 *      Address of the certificate whose Subject Alternative Names extensions
 *      is retrieved. Must be non-NULL.
 *  "hasLock"
 *      Boolean indicates caller has acquired a lock.
 *      Must be non-NULL.
 *  "pNssSubjAltNames"
 *      Address where the returned Subject Alternative Names will be stored.
 *      Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
pkix_pl_Cert_GetNssSubjectAltNames(
        PKIX_PL_Cert *cert,
        PKIX_Boolean hasLock,
        CERTGeneralName **pNssSubjAltNames,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        CERTGeneralName *nssOriginalAltName = NULL;
        PLArenaPool *arena = NULL;
        SECItem altNameExtension = {siBuffer, NULL, 0};
        SECStatus rv = SECFailure;

        PKIX_ENTER(CERT, "pkix_pl_Cert_GetNssSubjectAltNames");
        PKIX_NULLCHECK_THREE(cert, pNssSubjAltNames, cert->nssCert);

        nssCert = cert->nssCert;

        if ((cert->nssSubjAltNames == NULL) && (!cert->subjAltNamesAbsent)){

                if (!hasLock) {
                    PKIX_OBJECT_LOCK(cert);
                }

                if ((cert->nssSubjAltNames == NULL) &&
                    (!cert->subjAltNamesAbsent)){

                    PKIX_PL_NSSCALLRV(CERT, rv, CERT_FindCertExtension,
                        (nssCert,
                        SEC_OID_X509_SUBJECT_ALT_NAME,
                        &altNameExtension));

                    if (rv != SECSuccess) {
                        *pNssSubjAltNames = NULL;
                        cert->subjAltNamesAbsent = PKIX_TRUE;
                        goto cleanup;
                    }

                    if (cert->arenaNameConstraints == NULL) {
                        PKIX_PL_NSSCALLRV(CERT, arena, PORT_NewArena,
                                (DER_DEFAULT_CHUNKSIZE));

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

                    PKIX_PL_NSSCALLRV
                        (CERT,
                        nssOriginalAltName,
                        (CERTGeneralName *) CERT_DecodeAltNameExtension,
                        (cert->arenaNameConstraints, &altNameExtension));

                    PKIX_PL_NSSCALL(CERT, PORT_Free, (altNameExtension.data));

                    if (nssOriginalAltName == NULL) {
                        PKIX_ERROR(PKIX_CERTDECODEALTNAMEEXTENSIONFAILED);
                    }
                    cert->nssSubjAltNames = nssOriginalAltName;

                }

                if (!hasLock) {
                    PKIX_OBJECT_UNLOCK(cert);
                }
        }

        *pNssSubjAltNames = cert->nssSubjAltNames;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_CheckExtendKeyUsage
 * DESCRIPTION:
 *
 *  For each of the ON bit in "requiredExtendedKeyUsages" that represents its
 *  SECCertUsageEnum type, this function checks "cert"'s certType (extended
 *  key usage) and key usage with what is required for SECCertUsageEnum type.
 *
 * PARAMETERS
 *  "cert"
 *      Address of the certificate whose Extended Key Usage extensions
 *      is retrieved. Must be non-NULL.
 *  "requiredExtendedKeyUsages"
 *      An unsigned integer, its bit location is ON based on the required key
 *      usage value representing in SECCertUsageEnum.
 *  "pPass"
 *      Address where the return value, indicating key usage check passed, is
 *       stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_Cert_CheckExtendedKeyUsage(
        PKIX_PL_Cert *cert,
        PKIX_UInt32 requiredExtendedKeyUsages,
        PKIX_Boolean *pPass,
        void *plContext)
{
        PKIX_PL_CertBasicConstraints *basicConstraints = NULL;
        PKIX_UInt32 certType = 0;
        PKIX_UInt32 requiredKeyUsage = 0;
        PKIX_UInt32 requiredCertType = 0;
        PKIX_UInt32 requiredExtendedKeyUsage = 0;
        PKIX_UInt32 i;
        PKIX_Boolean isCA = PKIX_FALSE;
        SECStatus rv = SECFailure;

        PKIX_ENTER(CERT, "pkix_pl_Cert_CheckExtendKeyUsage");
        PKIX_NULLCHECK_THREE(cert, pPass, cert->nssCert);

        *pPass = PKIX_FALSE;

        PKIX_CERT_DEBUG("\t\tCalling cert_GetCertType).\n");
        cert_GetCertType(cert->nssCert);
        certType = cert->nssCert->nsCertType;

        PKIX_CHECK(PKIX_PL_Cert_GetBasicConstraints
                    (cert,
                    &basicConstraints,
                    plContext),
                    PKIX_CERTGETBASICCONSTRAINTFAILED);

        if (basicConstraints != NULL) {
                PKIX_CHECK(PKIX_PL_BasicConstraints_GetCAFlag
                    (basicConstraints, &isCA, plContext),
                    PKIX_BASICCONSTRAINTSGETCAFLAGFAILED);
        }

        i = 0;
        while (requiredExtendedKeyUsages != 0) {

                /* Find the bit location of the right-most non-zero bit */
                while (requiredExtendedKeyUsages != 0) {
                        if (((1 << i) & requiredExtendedKeyUsages) != 0) {
                                requiredExtendedKeyUsage = 1 << i;
                                break;
                        }
                        i++;
                }
                requiredExtendedKeyUsages ^= requiredExtendedKeyUsage;

                requiredExtendedKeyUsage = i;

                PKIX_PL_NSSCALLRV(CERT, rv, CERT_KeyUsageAndTypeForCertUsage,
                        (requiredExtendedKeyUsage,
                        isCA,
                        &requiredKeyUsage,
                        &requiredCertType));

                if (!(certType & requiredCertType)) {
                        goto cleanup;
                }

                PKIX_PL_NSSCALLRV(CERT, rv, CERT_CheckKeyUsage,
                        (cert->nssCert, requiredKeyUsage));
                if (rv != SECSuccess) {
                        goto cleanup;
                }
                i++;

        }

        *pPass = PKIX_TRUE;

cleanup:
        PKIX_DECREF(basicConstraints);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_ToString_Helper
 * DESCRIPTION:
 *
 *  Helper function that creates a string representation of the Cert pointed
 *  to by "cert" and stores it at "pString", where the value of
 *  "partialString" determines whether a full or partial representation of
 *  the Cert is stored.
 *
 * PARAMETERS
 *  "cert"
 *      Address of Cert whose string representation is desired.
 *      Must be non-NULL.
 *  "partialString"
 *      Boolean indicating whether a partial Cert representation is desired.
 *  "pString"
 *      Address where object pointer will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_Cert_ToString_Helper(
        PKIX_PL_Cert *cert,
        PKIX_Boolean partialString,
        PKIX_PL_String **pString,
        void *plContext)
{
        PKIX_PL_String *certString = NULL;
        char *asciiFormat = NULL;
        PKIX_PL_String *formatString = NULL;
        PKIX_UInt32 certVersion;
        PKIX_PL_BigInt *certSN = NULL;
        PKIX_PL_String *certSNString = NULL;
        PKIX_PL_X500Name *certIssuer = NULL;
        PKIX_PL_String *certIssuerString = NULL;
        PKIX_PL_X500Name *certSubject = NULL;
        PKIX_PL_String *certSubjectString = NULL;
        PKIX_PL_String *notBeforeString = NULL;
        PKIX_PL_String *notAfterString = NULL;
        PKIX_List *subjAltNames = NULL;
        PKIX_PL_String *subjAltNamesString = NULL;
        PKIX_PL_ByteArray *authKeyId = NULL;
        PKIX_PL_String *authKeyIdString = NULL;
        PKIX_PL_ByteArray *subjKeyId = NULL;
        PKIX_PL_String *subjKeyIdString = NULL;
        PKIX_PL_PublicKey *nssPubKey = NULL;
        PKIX_PL_String *nssPubKeyString = NULL;
        PKIX_List *critExtOIDs = NULL;
        PKIX_PL_String *critExtOIDsString = NULL;
        PKIX_List *extKeyUsages = NULL;
        PKIX_PL_String *extKeyUsagesString = NULL;
        PKIX_PL_CertBasicConstraints *basicConstraint = NULL;
        PKIX_PL_String *certBasicConstraintsString = NULL;
        PKIX_List *policyInfo = NULL;
        PKIX_PL_String *certPolicyInfoString = NULL;
        PKIX_List *certPolicyMappings = NULL;
        PKIX_PL_String *certPolicyMappingsString = NULL;
        PKIX_Int32 certExplicitPolicy = 0;
        PKIX_Int32 certInhibitMapping = 0;
        PKIX_Int32 certInhibitAnyPolicy = 0;
        PKIX_PL_CertNameConstraints *nameConstraints = NULL;
        PKIX_PL_String *nameConstraintsString = NULL;
        PKIX_List *authorityInfoAccess = NULL;
        PKIX_PL_String *authorityInfoAccessString = NULL;
        PKIX_List *subjectInfoAccess = NULL;
        PKIX_PL_String *subjectInfoAccessString = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_ToString_Helper");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pString);

        /*
         * XXX Add to this format as certificate components are developed.
         */

        if (partialString){
                asciiFormat =
                        "\t[Issuer:          %s\n"
                        "\t Subject:         %s]";
        } else {
                asciiFormat =
                        "[\n"
                        "\tVersion:         v%d\n"
                        "\tSerialNumber:    %s\n"
                        "\tIssuer:          %s\n"
                        "\tSubject:         %s\n"
                        "\tValidity: [From: %s\n"
                        "\t           To:   %s]\n"
                        "\tSubjectAltNames: %s\n"
                        "\tAuthorityKeyId:  %s\n"
                        "\tSubjectKeyId:    %s\n"
                        "\tSubjPubKeyAlgId: %s\n"
                        "\tCritExtOIDs:     %s\n"
                        "\tExtKeyUsages:    %s\n"
                        "\tBasicConstraint: %s\n"
                        "\tCertPolicyInfo:  %s\n"
                        "\tPolicyMappings:  %s\n"
                        "\tExplicitPolicy:  %d\n"
                        "\tInhibitMapping:  %d\n"
                        "\tInhibitAnyPolicy:%d\n"
                        "\tNameConstraints: %s\n"
                        "\tAuthorityInfoAccess: %s\n"
                        "\tSubjectInfoAccess: %s\n"
                        "\tCacheFlag:       %d\n"
                        "]\n";
        }



        PKIX_CHECK(PKIX_PL_String_Create
                (PKIX_ESCASCII, asciiFormat, 0, &formatString, plContext),
                PKIX_STRINGCREATEFAILED);

        /* Issuer */
        PKIX_CHECK(PKIX_PL_Cert_GetIssuer
                (cert, &certIssuer, plContext),
                PKIX_CERTGETISSUERFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                ((PKIX_PL_Object *)certIssuer, &certIssuerString, plContext),
                PKIX_X500NAMETOSTRINGFAILED);

        /* Subject */
        PKIX_CHECK(PKIX_PL_Cert_GetSubject(cert, &certSubject, plContext),
                PKIX_CERTGETSUBJECTFAILED);

        PKIX_TOSTRING(certSubject, &certSubjectString, plContext,
                PKIX_X500NAMETOSTRINGFAILED);

        if (partialString){
                PKIX_CHECK(PKIX_PL_Sprintf
                            (&certString,
                            plContext,
                            formatString,
                            certIssuerString,
                            certSubjectString),
                            PKIX_SPRINTFFAILED);

                *pString = certString;
                goto cleanup;
        }

        /* Version */
        PKIX_CHECK(PKIX_PL_Cert_GetVersion(cert, &certVersion, plContext),
                PKIX_CERTGETVERSIONFAILED);

        /* SerialNumber */
        PKIX_CHECK(PKIX_PL_Cert_GetSerialNumber(cert, &certSN, plContext),
                PKIX_CERTGETSERIALNUMBERFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                ((PKIX_PL_Object *)certSN, &certSNString, plContext),
                PKIX_BIGINTTOSTRINGFAILED);

        /* Validity: NotBefore */
        PKIX_CHECK(pkix_pl_Date_ToString_Helper
                (&(cert->nssCert->validity.notBefore),
                &notBeforeString,
                plContext),
                PKIX_DATETOSTRINGHELPERFAILED);

        /* Validity: NotAfter */
        PKIX_CHECK(pkix_pl_Date_ToString_Helper
                (&(cert->nssCert->validity.notAfter),
                &notAfterString,
                plContext),
                PKIX_DATETOSTRINGHELPERFAILED);

        /* SubjectAltNames */
        PKIX_CHECK(PKIX_PL_Cert_GetSubjectAltNames
                (cert, &subjAltNames, plContext),
                PKIX_CERTGETSUBJECTALTNAMESFAILED);

        PKIX_TOSTRING(subjAltNames, &subjAltNamesString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* AuthorityKeyIdentifier */
        PKIX_CHECK(PKIX_PL_Cert_GetAuthorityKeyIdentifier
                (cert, &authKeyId, plContext),
                PKIX_CERTGETAUTHORITYKEYIDENTIFIERFAILED);

        PKIX_TOSTRING(authKeyId, &authKeyIdString, plContext,
                PKIX_BYTEARRAYTOSTRINGFAILED);

        /* SubjectKeyIdentifier */
        PKIX_CHECK(PKIX_PL_Cert_GetSubjectKeyIdentifier
                (cert, &subjKeyId, plContext),
                PKIX_CERTGETSUBJECTKEYIDENTIFIERFAILED);

        PKIX_TOSTRING(subjKeyId, &subjKeyIdString, plContext,
                PKIX_BYTEARRAYTOSTRINGFAILED);

        /* SubjectPublicKey */
        PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey
                    (cert, &nssPubKey, plContext),
                    PKIX_CERTGETSUBJECTPUBLICKEYFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                ((PKIX_PL_Object *)nssPubKey, &nssPubKeyString, plContext),
                PKIX_PUBLICKEYTOSTRINGFAILED);

        /* CriticalExtensionOIDs */
        PKIX_CHECK(PKIX_PL_Cert_GetCriticalExtensionOIDs
                (cert, &critExtOIDs, plContext),
                PKIX_CERTGETCRITICALEXTENSIONOIDSFAILED);

        PKIX_TOSTRING(critExtOIDs, &critExtOIDsString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* ExtendedKeyUsages */
        PKIX_CHECK(PKIX_PL_Cert_GetExtendedKeyUsage
                (cert, &extKeyUsages, plContext),
                PKIX_CERTGETEXTENDEDKEYUSAGEFAILED);

        PKIX_TOSTRING(extKeyUsages, &extKeyUsagesString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* CertBasicConstraints */
        PKIX_CHECK(PKIX_PL_Cert_GetBasicConstraints
                (cert, &basicConstraint, plContext),
                PKIX_CERTGETBASICCONSTRAINTSFAILED);

        PKIX_TOSTRING(basicConstraint, &certBasicConstraintsString, plContext,
                PKIX_CERTBASICCONSTRAINTSTOSTRINGFAILED);

        /* CertPolicyInfo */
        PKIX_CHECK(PKIX_PL_Cert_GetPolicyInformation
                (cert, &policyInfo, plContext),
                PKIX_CERTGETPOLICYINFORMATIONFAILED);

        PKIX_TOSTRING(policyInfo, &certPolicyInfoString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* Advanced Policies */
        PKIX_CHECK(PKIX_PL_Cert_GetPolicyMappings
                (cert, &certPolicyMappings, plContext),
                PKIX_CERTGETPOLICYMAPPINGSFAILED);

        PKIX_TOSTRING(certPolicyMappings, &certPolicyMappingsString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        PKIX_CHECK(PKIX_PL_Cert_GetRequireExplicitPolicy
                (cert, &certExplicitPolicy, plContext),
                PKIX_CERTGETREQUIREEXPLICITPOLICYFAILED);

        PKIX_CHECK(PKIX_PL_Cert_GetPolicyMappingInhibited
                (cert, &certInhibitMapping, plContext),
                PKIX_CERTGETPOLICYMAPPINGINHIBITEDFAILED);

        PKIX_CHECK(PKIX_PL_Cert_GetInhibitAnyPolicy
                (cert, &certInhibitAnyPolicy, plContext),
                PKIX_CERTGETINHIBITANYPOLICYFAILED);

        /* Name Constraints */
        PKIX_CHECK(PKIX_PL_Cert_GetNameConstraints
                (cert, &nameConstraints, plContext),
                PKIX_CERTGETNAMECONSTRAINTSFAILED);

        PKIX_TOSTRING(nameConstraints, &nameConstraintsString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* Authority Information Access */
        PKIX_CHECK(PKIX_PL_Cert_GetAuthorityInfoAccess
                (cert, &authorityInfoAccess, plContext),
                PKIX_CERTGETAUTHORITYINFOACCESSFAILED);

        PKIX_TOSTRING(authorityInfoAccess, &authorityInfoAccessString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        /* Subject Information Access */
        PKIX_CHECK(PKIX_PL_Cert_GetSubjectInfoAccess
                (cert, &subjectInfoAccess, plContext),
                PKIX_CERTGETSUBJECTINFOACCESSFAILED);

        PKIX_TOSTRING(subjectInfoAccess, &subjectInfoAccessString, plContext,
                PKIX_LISTTOSTRINGFAILED);

        PKIX_CHECK(PKIX_PL_Sprintf
                    (&certString,
                    plContext,
                    formatString,
                    certVersion + 1,
                    certSNString,
                    certIssuerString,
                    certSubjectString,
                    notBeforeString,
                    notAfterString,
                    subjAltNamesString,
                    authKeyIdString,
                    subjKeyIdString,
                    nssPubKeyString,
                    critExtOIDsString,
                    extKeyUsagesString,
                    certBasicConstraintsString,
                    certPolicyInfoString,
                    certPolicyMappingsString,
                    certExplicitPolicy,         /* an Int32, not a String */
                    certInhibitMapping,         /* an Int32, not a String */
                    certInhibitAnyPolicy,       /* an Int32, not a String */
                    nameConstraintsString,
                    authorityInfoAccessString,
                    subjectInfoAccessString,
                    cert->cacheFlag),           /* a boolean */
                    PKIX_SPRINTFFAILED);

        *pString = certString;

cleanup:
        PKIX_DECREF(certSN);
        PKIX_DECREF(certSNString);
        PKIX_DECREF(certIssuer);
        PKIX_DECREF(certIssuerString);
        PKIX_DECREF(certSubject);
        PKIX_DECREF(certSubjectString);
        PKIX_DECREF(notBeforeString);
        PKIX_DECREF(notAfterString);
        PKIX_DECREF(subjAltNames);
        PKIX_DECREF(subjAltNamesString);
        PKIX_DECREF(authKeyId);
        PKIX_DECREF(authKeyIdString);
        PKIX_DECREF(subjKeyId);
        PKIX_DECREF(subjKeyIdString);
        PKIX_DECREF(nssPubKey);
        PKIX_DECREF(nssPubKeyString);
        PKIX_DECREF(critExtOIDs);
        PKIX_DECREF(critExtOIDsString);
        PKIX_DECREF(extKeyUsages);
        PKIX_DECREF(extKeyUsagesString);
        PKIX_DECREF(basicConstraint);
        PKIX_DECREF(certBasicConstraintsString);
        PKIX_DECREF(policyInfo);
        PKIX_DECREF(certPolicyInfoString);
        PKIX_DECREF(certPolicyMappings);
        PKIX_DECREF(certPolicyMappingsString);
        PKIX_DECREF(nameConstraints);
        PKIX_DECREF(nameConstraintsString);
        PKIX_DECREF(authorityInfoAccess);
        PKIX_DECREF(authorityInfoAccessString);
        PKIX_DECREF(subjectInfoAccess);
        PKIX_DECREF(subjectInfoAccessString);
        PKIX_DECREF(formatString);

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_Destroy
 * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_Cert_Destroy(
        PKIX_PL_Object *object,
        void *plContext)
{
        PKIX_PL_Cert *cert = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_Destroy");
        PKIX_NULLCHECK_ONE(object);

        PKIX_CHECK(pkix_CheckType(object, PKIX_CERT_TYPE, plContext),
                    PKIX_OBJECTNOTCERT);

        cert = (PKIX_PL_Cert*)object;

        PKIX_DECREF(cert->subject);
        PKIX_DECREF(cert->issuer);
        PKIX_DECREF(cert->subjAltNames);
        PKIX_DECREF(cert->publicKeyAlgId);
        PKIX_DECREF(cert->publicKey);
        PKIX_DECREF(cert->serialNumber);
        PKIX_DECREF(cert->critExtOids);
        PKIX_DECREF(cert->authKeyId);
        PKIX_DECREF(cert->subjKeyId);
        PKIX_DECREF(cert->extKeyUsages);
        PKIX_DECREF(cert->certBasicConstraints);
        PKIX_DECREF(cert->certPolicyInfos);
        PKIX_DECREF(cert->certPolicyMappings);
        PKIX_DECREF(cert->nameConstraints);
        PKIX_DECREF(cert->store);
        PKIX_DECREF(cert->authorityInfoAccess);
        PKIX_DECREF(cert->subjectInfoAccess);

        if (cert->arenaNameConstraints){
                /* This arena was allocated for SubjectAltNames */
                PKIX_PL_NSSCALL(CERT, PORT_FreeArena,
                        (cert->arenaNameConstraints, PR_FALSE));

                cert->arenaNameConstraints = NULL;
                cert->nssSubjAltNames = NULL;
        }

        CERT_DestroyCertificate(cert->nssCert);
        cert->nssCert = NULL;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_ToString
 * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_Cert_ToString(
        PKIX_PL_Object *object,
        PKIX_PL_String **pString,
        void *plContext)
{
        PKIX_PL_String *certString = NULL;
        PKIX_PL_Cert *pkixCert = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_toString");
        PKIX_NULLCHECK_TWO(object, pString);

        PKIX_CHECK(pkix_CheckType(object, PKIX_CERT_TYPE, plContext),
                    PKIX_OBJECTNOTCERT);

        pkixCert = (PKIX_PL_Cert *)object;

        PKIX_CHECK(pkix_pl_Cert_ToString_Helper
                    (pkixCert, PKIX_FALSE, &certString, plContext),
                    PKIX_CERTTOSTRINGHELPERFAILED);

        *pString = certString;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_Hashcode
 * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_Cert_Hashcode(
        PKIX_PL_Object *object,
        PKIX_UInt32 *pHashcode,
        void *plContext)
{
        PKIX_PL_Cert *pkixCert = NULL;
        CERTCertificate *nssCert = NULL;
        unsigned char *derBytes = NULL;
        PKIX_UInt32 derLength;
        PKIX_UInt32 certHash;

        PKIX_ENTER(CERT, "pkix_pl_Cert_Hashcode");
        PKIX_NULLCHECK_TWO(object, pHashcode);

        PKIX_CHECK(pkix_CheckType(object, PKIX_CERT_TYPE, plContext),
                    PKIX_OBJECTNOTCERT);

        pkixCert = (PKIX_PL_Cert *)object;

        nssCert = pkixCert->nssCert;
        derBytes = (nssCert->derCert).data;
        derLength = (nssCert->derCert).len;

        PKIX_CHECK(pkix_hash(derBytes, derLength, &certHash, plContext),
                    PKIX_HASHFAILED);

        *pHashcode = certHash;

cleanup:
        PKIX_RETURN(CERT);
}


/*
 * FUNCTION: pkix_pl_Cert_Equals
 * (see comments for PKIX_PL_Equals_Callback in pkix_pl_system.h)
 */
static PKIX_Error *
pkix_pl_Cert_Equals(
        PKIX_PL_Object *firstObject,
        PKIX_PL_Object *secondObject,
        PKIX_Boolean *pResult,
        void *plContext)
{
        CERTCertificate *firstCert = NULL;
        CERTCertificate *secondCert = NULL;
        PKIX_UInt32 secondType;
        PKIX_Boolean cmpResult;

        PKIX_ENTER(CERT, "pkix_pl_Cert_Equals");
        PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult);

        /* test that firstObject is a Cert */
        PKIX_CHECK(pkix_CheckType(firstObject, PKIX_CERT_TYPE, plContext),
                    PKIX_FIRSTOBJECTNOTCERT);

        /*
         * Since we know firstObject is a Cert, if both references are
         * identical, they must be equal
         */
        if (firstObject == secondObject){
                *pResult = PKIX_TRUE;
                goto cleanup;
        }

        /*
         * If secondObject isn't a Cert, we don't throw an error.
         * We simply return a Boolean result of FALSE
         */
        *pResult = PKIX_FALSE;
        PKIX_CHECK(PKIX_PL_Object_GetType
                    (secondObject, &secondType, plContext),
                    PKIX_COULDNOTGETTYPEOFSECONDARGUMENT);
        if (secondType != PKIX_CERT_TYPE) goto cleanup;

        firstCert = ((PKIX_PL_Cert *)firstObject)->nssCert;
        secondCert = ((PKIX_PL_Cert *)secondObject)->nssCert;

        PKIX_NULLCHECK_TWO(firstCert, secondCert);

        /* CERT_CompareCerts does byte comparison on DER encodings of certs */
        PKIX_CERT_DEBUG("\t\tCalling CERT_CompareCerts).\n");
        cmpResult = CERT_CompareCerts(firstCert, secondCert);

        *pResult = cmpResult;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_RegisterSelf
 * DESCRIPTION:
 *  Registers PKIX_CERT_TYPE and its related functions with systemClasses[]
 * THREAD SAFETY:
 *  Not Thread Safe - for performance and complexity reasons
 *
 *  Since this function is only called by PKIX_PL_Initialize, which should
 *  only be called once, it is acceptable that this function is not
 *  thread-safe.
 */
PKIX_Error *
pkix_pl_Cert_RegisterSelf(void *plContext)
{

        extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
        pkix_ClassTable_Entry entry;

        PKIX_ENTER(CERT, "pkix_pl_Cert_RegisterSelf");

        entry.description = "Cert";
        entry.objCounter = 0;
        entry.typeObjectSize = sizeof(PKIX_PL_Cert);
        entry.destructor = pkix_pl_Cert_Destroy;
        entry.equalsFunction = pkix_pl_Cert_Equals;
        entry.hashcodeFunction = pkix_pl_Cert_Hashcode;
        entry.toStringFunction = pkix_pl_Cert_ToString;
        entry.comparator = NULL;
        entry.duplicateFunction = pkix_duplicateImmutable;

        systemClasses[PKIX_CERT_TYPE] = entry;

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_CreateWithNSSCert
 * DESCRIPTION:
 *
 *  Creates a new certificate using the CERTCertificate pointed to by "nssCert"
 *  and stores it at "pCert". Once created, a Cert is immutable.
 *
 *  This function is primarily used as a convenience function for the
 *  performance tests that have easy access to a CERTCertificate.
 *
 * PARAMETERS:
 *  "nssCert"
 *      Address of CERTCertificate representing the NSS certificate.
 *      Must be non-NULL.
 *  "pCert"
 *      Address where object pointer will be stored. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_Cert_CreateWithNSSCert(
        CERTCertificate *nssCert,
        PKIX_PL_Cert **pCert,
        void *plContext)
{
        PKIX_PL_Cert *cert = NULL;

        PKIX_ENTER(CERT, "pkix_pl_Cert_CreateWithNSSCert");
        PKIX_NULLCHECK_TWO(pCert, nssCert);

        /* create a PKIX_PL_Cert object */
        PKIX_CHECK(PKIX_PL_Object_Alloc
                    (PKIX_CERT_TYPE,
                    sizeof (PKIX_PL_Cert),
                    (PKIX_PL_Object **)&cert,
                    plContext),
                    PKIX_COULDNOTCREATEOBJECT);

        /* populate the nssCert field */
        cert->nssCert = nssCert;

        /* initialize remaining fields */
        /*
         * Fields ending with Absent are initialized to PKIX_FALSE so that the
         * first time we need the value we will look for it. If we find it is
         * actually absent, the flag will at that time be set to PKIX_TRUE to
         * prevent searching for it later.
         * Fields ending with Processed are those where a value is defined
         * for the Absent case, and a value of zero is possible. When the
         * flag is still true we have to look for the field, set the default
         * value if necessary, and set the Processed flag to PKIX_TRUE.
         */
        cert->subject = NULL;
        cert->issuer = NULL;
        cert->subjAltNames = NULL;
        cert->subjAltNamesAbsent = PKIX_FALSE;
        cert->publicKeyAlgId = NULL;
        cert->publicKey = NULL;
        cert->serialNumber = NULL;
        cert->critExtOids = NULL;
        cert->subjKeyId = NULL;
        cert->subjKeyIdAbsent = PKIX_FALSE;
        cert->authKeyId = NULL;
        cert->authKeyIdAbsent = PKIX_FALSE;
        cert->extKeyUsages = NULL;
        cert->extKeyUsagesAbsent = PKIX_FALSE;
        cert->certBasicConstraints = NULL;
        cert->basicConstraintsAbsent = PKIX_FALSE;
        cert->certPolicyInfos = NULL;
        cert->policyInfoAbsent = PKIX_FALSE;
        cert->policyMappingsAbsent = PKIX_FALSE;
        cert->certPolicyMappings = NULL;
        cert->policyConstraintsProcessed = PKIX_FALSE;
        cert->policyConstraintsExplicitPolicySkipCerts = 0;
        cert->policyConstraintsInhibitMappingSkipCerts = 0;
        cert->inhibitAnyPolicyProcessed = PKIX_FALSE;
        cert->inhibitAnySkipCerts = 0;
        cert->nameConstraints = NULL;
        cert->nameConstraintsAbsent = PKIX_FALSE;
        cert->arenaNameConstraints = NULL;
        cert->nssSubjAltNames = NULL;
        cert->cacheFlag = PKIX_FALSE;
        cert->store = NULL;
        cert->authorityInfoAccess = NULL;
        cert->subjectInfoAccess = NULL;

        *pCert = cert;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: pkix_pl_Cert_CreateToList
 * DESCRIPTION:
 *
 *  Creates a new certificate using the DER-encoding pointed to by "derCertItem"
 *  and appends it to the list pointed to by "certList". If Cert creation fails,
 *  the function returns with certList unchanged, but any decoding Error is
 *  discarded.
 *
 * PARAMETERS:
 *  "derCertItem"
 *      Address of SECItem containing the DER representation of a certificate.
 *      Must be non-NULL.
 *  "certList"
 *      Address of List to which the Cert will be appended, if successfully
 *      created. May be empty, but must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns NULL if the function succeeds.
 *  Returns a Cert Error if the function fails in a non-fatal way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
PKIX_Error *
pkix_pl_Cert_CreateToList(
        SECItem *derCertItem,
        PKIX_List *certList,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        PKIX_PL_Cert *cert = NULL;
        CERTCertDBHandle *handle;

        PKIX_ENTER(CERT, "pkix_pl_Cert_CreateToList");
        PKIX_NULLCHECK_TWO(derCertItem, certList);

        handle  = CERT_GetDefaultCertDB();
        nssCert = CERT_NewTempCertificate(handle, derCertItem,
					  /* nickname */ NULL, 
					  /* isPerm   */ PR_FALSE, 
					  /* copyDer  */ PR_TRUE);
        if (!nssCert) {
            goto cleanup;
        }

        PKIX_CHECK(pkix_pl_Cert_CreateWithNSSCert
                   (nssCert, &cert, plContext),
                   PKIX_CERTCREATEWITHNSSCERTFAILED);

        nssCert = NULL;

        PKIX_CHECK(PKIX_List_AppendItem
                   (certList, (PKIX_PL_Object *) cert, plContext),
                   PKIX_LISTAPPENDITEMFAILED);

cleanup:
        if (nssCert) {
            CERT_DestroyCertificate(nssCert);
        }

        PKIX_DECREF(cert);
        PKIX_RETURN(CERT);
}

/* --Public-Functions------------------------------------------------------- */

/*
 * FUNCTION: PKIX_PL_Cert_Create (see comments in pkix_pl_pki.h)
 * XXX We may want to cache the cert after parsing it, so it can be reused
 * XXX Are the NSS/NSPR functions thread safe
 */
PKIX_Error *
PKIX_PL_Cert_Create(
        PKIX_PL_ByteArray *byteArray,
        PKIX_PL_Cert **pCert,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        SECItem *derCertItem = NULL;
        void *derBytes = NULL;
        PKIX_UInt32 derLength;
        PKIX_Boolean copyDER;
        PKIX_PL_Cert *cert = NULL;
        CERTCertDBHandle *handle;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_Create");
        PKIX_NULLCHECK_TWO(pCert, byteArray);

        PKIX_CHECK(PKIX_PL_ByteArray_GetPointer
                    (byteArray, &derBytes, plContext),
                    PKIX_BYTEARRAYGETPOINTERFAILED);

        PKIX_CHECK(PKIX_PL_ByteArray_GetLength
                    (byteArray, &derLength, plContext),
                    PKIX_BYTEARRAYGETLENGTHFAILED);

        derCertItem = SECITEM_AllocItem(NULL, NULL, derLength);
        if (derCertItem == NULL){
                PKIX_ERROR(PKIX_OUTOFMEMORY);
        }

        (void) PORT_Memcpy(derCertItem->data, derBytes, derLength);

        /*
         * setting copyDER to true forces NSS to make its own copy of the DER,
         * allowing us to free our copy without worrying about whether NSS
         * is still using it
         */
        copyDER = PKIX_TRUE;
        handle  = CERT_GetDefaultCertDB();
        nssCert = CERT_NewTempCertificate(handle, derCertItem,
					  /* nickname */ NULL, 
					  /* isPerm   */ PR_FALSE, 
					  /* copyDer  */ PR_TRUE);
        if (!nssCert){
                PKIX_ERROR(PKIX_CERTDECODEDERCERTIFICATEFAILED);
        }

        PKIX_CHECK(pkix_pl_Cert_CreateWithNSSCert
                (nssCert, &cert, plContext),
                PKIX_CERTCREATEWITHNSSCERTFAILED);

        *pCert = cert;

cleanup:
        if (derCertItem){
                SECITEM_FreeItem(derCertItem, PKIX_TRUE);
        }

        if (nssCert && PKIX_ERROR_RECEIVED){
                PKIX_CERT_DEBUG("\t\tCalling CERT_DestroyCertificate).\n");
                CERT_DestroyCertificate(nssCert);
                nssCert = NULL;
        }

        PKIX_FREE(derBytes);
        PKIX_RETURN(CERT);
}


/*
 * FUNCTION: PKIX_PL_Cert_CreateFromCERTCertificate
 *  (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_CreateFromCERTCertificate(
        const CERTCertificate *nssCert,
        PKIX_PL_Cert **pCert,
        void *plContext)
{
        void *buf = NULL;
        PKIX_UInt32 len;
        PKIX_PL_ByteArray *byteArray = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_CreateWithNssCert");
        PKIX_NULLCHECK_TWO(pCert, nssCert);

        buf = (void*)nssCert->derCert.data;
        len = nssCert->derCert.len;

        PKIX_CHECK(
            PKIX_PL_ByteArray_Create(buf, len, &byteArray, plContext),
            PKIX_BYTEARRAYCREATEFAILED);

        PKIX_CHECK(
            PKIX_PL_Cert_Create(byteArray, pCert, plContext),
            PKIX_CERTCREATEWITHNSSCERTFAILED);

#ifdef PKIX_UNDEF
        /* will be tested and used as a patch for bug 391612 */
        nssCert = CERT_DupCertificate(nssInCert);

        PKIX_CHECK(pkix_pl_Cert_CreateWithNSSCert
                (nssCert, &cert, plContext),
                PKIX_CERTCREATEWITHNSSCERTFAILED);
#endif /* PKIX_UNDEF */

cleanup:

#ifdef PKIX_UNDEF
        if (nssCert && PKIX_ERROR_RECEIVED){
                PKIX_CERT_DEBUG("\t\tCalling CERT_DestroyCertificate).\n");
                CERT_DestroyCertificate(nssCert);
                nssCert = NULL;
        }
#endif /* PKIX_UNDEF */

        PKIX_DECREF(byteArray);
        PKIX_RETURN(CERT);
}


/*
 * FUNCTION: PKIX_PL_Cert_GetVersion (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetVersion(
        PKIX_PL_Cert *cert,
        PKIX_UInt32 *pVersion,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        PKIX_UInt32 myVersion = 1;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetVersion");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pVersion);

        nssCert = cert->nssCert;
        if (nssCert->version.data) {
                myVersion = *(nssCert->version.data);
        }

        if (myVersion > 2){
                PKIX_ERROR(PKIX_VERSIONVALUEMUSTBEV1V2ORV3);
        }

        *pVersion = myVersion;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSerialNumber (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSerialNumber(
        PKIX_PL_Cert *cert,
        PKIX_PL_BigInt **pSerialNumber,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        SECItem serialNumItem;
        PKIX_PL_BigInt *serialNumber = NULL;
        char *bytes = NULL;
        PKIX_UInt32 length;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSerialNumber");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSerialNumber);

        if (cert->serialNumber == NULL){

                PKIX_OBJECT_LOCK(cert);

                if (cert->serialNumber == NULL){

                        nssCert = cert->nssCert;
                        serialNumItem = nssCert->serialNumber;

                        length = serialNumItem.len;
                        bytes = (char *)serialNumItem.data;

                        PKIX_CHECK(pkix_pl_BigInt_CreateWithBytes
                                    (bytes, length, &serialNumber, plContext),
                                    PKIX_BIGINTCREATEWITHBYTESFAILED);

                        /* save a cached copy in case it is asked for again */
                        cert->serialNumber = serialNumber;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->serialNumber);
        *pSerialNumber = cert->serialNumber;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSubject (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubject(
        PKIX_PL_Cert *cert,
        PKIX_PL_X500Name **pCertSubject,
        void *plContext)
{
        PKIX_PL_X500Name *pkixSubject = NULL;
        CERTName *subjName = NULL;
        SECItem  *derSubjName = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubject");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pCertSubject);

        /* if we don't have a cached copy from before, we create one */
        if (cert->subject == NULL){

                PKIX_OBJECT_LOCK(cert);

                if (cert->subject == NULL){

                        subjName = &cert->nssCert->subject;
                        derSubjName = &cert->nssCert->derSubject;

                        /* if there is no subject name */
                        if (derSubjName->data == NULL) {

                                pkixSubject = NULL;

                        } else {
                                PKIX_CHECK(PKIX_PL_X500Name_CreateFromCERTName
                                    (derSubjName, subjName, &pkixSubject,
                                     plContext),
                                    PKIX_X500NAMECREATEFROMCERTNAMEFAILED);

                        }
                        /* save a cached copy in case it is asked for again */
                        cert->subject = pkixSubject;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->subject);
        *pCertSubject = cert->subject;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetIssuer (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetIssuer(
        PKIX_PL_Cert *cert,
        PKIX_PL_X500Name **pCertIssuer,
        void *plContext)
{
        PKIX_PL_X500Name *pkixIssuer = NULL;
        SECItem  *derIssuerName = NULL;
        CERTName *issuerName = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetIssuer");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pCertIssuer);

        /* if we don't have a cached copy from before, we create one */
        if (cert->issuer == NULL){

                PKIX_OBJECT_LOCK(cert);

                if (cert->issuer == NULL){

                        issuerName = &cert->nssCert->issuer;
                        derIssuerName = &cert->nssCert->derIssuer;

                        /* if there is no subject name */
                        PKIX_CHECK(PKIX_PL_X500Name_CreateFromCERTName
                                    (derIssuerName, issuerName,
                                     &pkixIssuer, plContext),
                                    PKIX_X500NAMECREATEFROMCERTNAMEFAILED);

                        /* save a cached copy in case it is asked for again */
                        cert->issuer = pkixIssuer;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->issuer);
        *pCertIssuer = cert->issuer;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSubjectAltNames (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubjectAltNames(
        PKIX_PL_Cert *cert,
        PKIX_List **pSubjectAltNames,  /* list of PKIX_PL_GeneralName */
        void *plContext)
{
        PKIX_PL_GeneralName *pkixAltName = NULL;
        PKIX_List *altNamesList = NULL;

        CERTGeneralName *nssOriginalAltName = NULL;
        CERTGeneralName *nssTempAltName = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubjectAltNames");
        PKIX_NULLCHECK_TWO(cert, pSubjectAltNames);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->subjAltNames == NULL) && (!cert->subjAltNamesAbsent)){

                PKIX_OBJECT_LOCK(cert);

                if ((cert->subjAltNames == NULL) &&
                    (!cert->subjAltNamesAbsent)){

                        PKIX_CHECK(pkix_pl_Cert_GetNssSubjectAltNames
                                (cert,
                                PKIX_TRUE,
                                &nssOriginalAltName,
                                plContext),
                                PKIX_CERTGETNSSSUBJECTALTNAMESFAILED);

                        if (nssOriginalAltName == NULL) {
                                cert->subjAltNamesAbsent = PKIX_TRUE;
                                pSubjectAltNames = NULL;
                                goto cleanup;
                        }

                        nssTempAltName = nssOriginalAltName;

                        PKIX_CHECK(PKIX_List_Create(&altNamesList, plContext),
                                PKIX_LISTCREATEFAILED);

                        do {
                            PKIX_CHECK(pkix_pl_GeneralName_Create
                                (nssTempAltName, &pkixAltName, plContext),
                                PKIX_GENERALNAMECREATEFAILED);

                            PKIX_CHECK(PKIX_List_AppendItem
                                (altNamesList,
                                (PKIX_PL_Object *)pkixAltName,
                                plContext),
                                PKIX_LISTAPPENDITEMFAILED);

                            PKIX_DECREF(pkixAltName);

                            PKIX_CERT_DEBUG
                                ("\t\tCalling CERT_GetNextGeneralName).\n");
                            nssTempAltName = CERT_GetNextGeneralName
                                (nssTempAltName);

                        } while (nssTempAltName != nssOriginalAltName);

                        /* save a cached copy in case it is asked for again */
                        cert->subjAltNames = altNamesList;
                        PKIX_CHECK(PKIX_List_SetImmutable
                                (cert->subjAltNames, plContext),
                                PKIX_LISTSETIMMUTABLEFAILED);

                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->subjAltNames);

        *pSubjectAltNames = cert->subjAltNames;

cleanup:
        PKIX_DECREF(pkixAltName);
        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(altNamesList);
        }
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetAllSubjectNames (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetAllSubjectNames(
        PKIX_PL_Cert *cert,
        PKIX_List **pAllSubjectNames,  /* list of PKIX_PL_GeneralName */
        void *plContext)
{
        CERTGeneralName *nssOriginalSubjectName = NULL;
        CERTGeneralName *nssTempSubjectName = NULL;
        PKIX_List *allSubjectNames = NULL;
        PKIX_PL_GeneralName *pkixSubjectName = NULL;
        PLArenaPool *arena = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetAllSubjectNames");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pAllSubjectNames);


        if (cert->nssCert->subjectName == NULL){
                /* if there is no subject DN, just get altnames */

                PKIX_CHECK(pkix_pl_Cert_GetNssSubjectAltNames
                            (cert,
                            PKIX_FALSE, /* hasLock */
                            &nssOriginalSubjectName,
                            plContext),
                            PKIX_CERTGETNSSSUBJECTALTNAMESFAILED);

        } else { /* get subject DN and altnames */

                arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                if (arena == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                }

                /* This NSS call returns both Subject and  Subject Alt Names */
                PKIX_CERT_DEBUG("\t\tCalling CERT_GetCertificateNames\n");
                nssOriginalSubjectName =
                        CERT_GetCertificateNames(cert->nssCert, arena);
        }

        if (nssOriginalSubjectName == NULL) {
                pAllSubjectNames = NULL;
                goto cleanup;
        }

        nssTempSubjectName = nssOriginalSubjectName;

        PKIX_CHECK(PKIX_List_Create(&allSubjectNames, plContext),
                    PKIX_LISTCREATEFAILED);

        do {
                PKIX_CHECK(pkix_pl_GeneralName_Create
                            (nssTempSubjectName, &pkixSubjectName, plContext),
                            PKIX_GENERALNAMECREATEFAILED);

                PKIX_CHECK(PKIX_List_AppendItem
                            (allSubjectNames,
                            (PKIX_PL_Object *)pkixSubjectName,
                            plContext),
                            PKIX_LISTAPPENDITEMFAILED);

                PKIX_DECREF(pkixSubjectName);

                PKIX_CERT_DEBUG
                        ("\t\tCalling CERT_GetNextGeneralName).\n");
                nssTempSubjectName = CERT_GetNextGeneralName
                        (nssTempSubjectName);
        } while (nssTempSubjectName != nssOriginalSubjectName);

        *pAllSubjectNames = allSubjectNames;

cleanup:
        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(allSubjectNames);
        }

        if (arena){
                PORT_FreeArena(arena, PR_FALSE);
        }
        PKIX_DECREF(pkixSubjectName);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSubjectPublicKeyAlgId
 *      (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubjectPublicKeyAlgId(
        PKIX_PL_Cert *cert,
        PKIX_PL_OID **pSubjKeyAlgId,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        PKIX_PL_OID *pubKeyAlgId = NULL;
        SECAlgorithmID algorithm;
        SECItem algBytes;
        char *asciiOID = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubjectPublicKeyAlgId");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSubjKeyAlgId);

        /* if we don't have a cached copy from before, we create one */
        if (cert->publicKeyAlgId == NULL){

                PKIX_OBJECT_LOCK(cert);

                if (cert->publicKeyAlgId == NULL){

                        nssCert = cert->nssCert;
                        algorithm = nssCert->subjectPublicKeyInfo.algorithm;
                        algBytes = algorithm.algorithm;

                        PKIX_NULLCHECK_ONE(algBytes.data);
                        if (algBytes.len == 0) {
                                PKIX_ERROR_FATAL(PKIX_ALGORITHMBYTESLENGTH0);
                        }

                        PKIX_CHECK(pkix_pl_oidBytes2Ascii
                                    (&algBytes, &asciiOID, plContext),
                                    PKIX_OIDBYTES2ASCIIFAILED);

                        PKIX_CHECK(PKIX_PL_OID_Create
                                    (asciiOID, &pubKeyAlgId, plContext),
                                    PKIX_OIDCREATEFAILED);

                        /* save a cached copy in case it is asked for again */
                        cert->publicKeyAlgId = pubKeyAlgId;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->publicKeyAlgId);
        *pSubjKeyAlgId = cert->publicKeyAlgId;

cleanup:
        PKIX_FREE(asciiOID);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSubjectPublicKey (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubjectPublicKey(
        PKIX_PL_Cert *cert,
        PKIX_PL_PublicKey **pPublicKey,
        void *plContext)
{
        PKIX_PL_PublicKey *pkixPubKey = NULL;
        SECStatus rv;

        CERTSubjectPublicKeyInfo *from = NULL;
        CERTSubjectPublicKeyInfo *to = NULL;
        SECItem *fromItem = NULL;
        SECItem *toItem = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubjectPublicKey");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pPublicKey);

        /* if we don't have a cached copy from before, we create one */
        if (cert->publicKey == NULL){

                PKIX_OBJECT_LOCK(cert);

                if (cert->publicKey == NULL){

                        /* create a PKIX_PL_PublicKey object */
                        PKIX_CHECK(PKIX_PL_Object_Alloc
                                    (PKIX_PUBLICKEY_TYPE,
                                    sizeof (PKIX_PL_PublicKey),
                                    (PKIX_PL_Object **)&pkixPubKey,
                                    plContext),
                                    PKIX_COULDNOTCREATEOBJECT);

                        /* initialize fields */
                        pkixPubKey->nssSPKI = NULL;

                        /* populate the SPKI field */
                        PKIX_CHECK(PKIX_PL_Malloc
                                    (sizeof (CERTSubjectPublicKeyInfo),
                                    (void **)&pkixPubKey->nssSPKI,
                                    plContext),
                                    PKIX_MALLOCFAILED);

                        to = pkixPubKey->nssSPKI;
                        from  = &cert->nssCert->subjectPublicKeyInfo;

                        PKIX_NULLCHECK_TWO(to, from);

                        PKIX_CERT_DEBUG
                                ("\t\tCalling SECOID_CopyAlgorithmID).\n");
                        rv = SECOID_CopyAlgorithmID
                                (NULL, &to->algorithm, &from->algorithm);
                        if (rv != SECSuccess) {
                                PKIX_ERROR(PKIX_SECOIDCOPYALGORITHMIDFAILED);
                        }

                        /*
                         * NSS stores the length of subjectPublicKey in bits.
                         * Therefore, we use that length converted to bytes
                         * using ((length+7)>>3) before calling PORT_Memcpy
                         * in order to avoid "read from uninitialized memory"
                         * errors.
                         */

                        toItem = &to->subjectPublicKey;
                        fromItem = &from->subjectPublicKey;

                        PKIX_NULLCHECK_TWO(toItem, fromItem);

                        toItem->type = fromItem->type;

                        toItem->data =
                                (unsigned char*) PORT_ZAlloc(fromItem->len);
                        if (!toItem->data){
                                PKIX_ERROR(PKIX_OUTOFMEMORY);
                        }

                        (void) PORT_Memcpy(toItem->data,
                                    fromItem->data,
                                    (fromItem->len + 7)>>3);
                        toItem->len = fromItem->len;

                        /* save a cached copy in case it is asked for again */
                        cert->publicKey = pkixPubKey;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->publicKey);
        *pPublicKey = cert->publicKey;

cleanup:

        if (PKIX_ERROR_RECEIVED && pkixPubKey){
                PKIX_DECREF(pkixPubKey);
                cert->publicKey = NULL;
        }
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetCriticalExtensionOIDs
 *      (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetCriticalExtensionOIDs(
        PKIX_PL_Cert *cert,
        PKIX_List **pList,  /* list of PKIX_PL_OID */
        void *plContext)
{
        PKIX_List *oidsList = NULL;
        CERTCertExtension **extensions = NULL;
        CERTCertificate *nssCert = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetCriticalExtensionOIDs");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pList);

        /* if we don't have a cached copy from before, we create one */
        if (cert->critExtOids == NULL) {

            PKIX_OBJECT_LOCK(cert);

            if (cert->critExtOids == NULL) {

                nssCert = cert->nssCert;

                /*
                 * ASN.1 for Extension
                 *
                 * Extension  ::=  SEQUENCE  {
                 *      extnID          OBJECT IDENTIFIER,
                 *      critical        BOOLEAN DEFAULT FALSE,
                 *      extnValue       OCTET STRING  }
                 *
                 */

                extensions = nssCert->extensions;

                PKIX_CHECK(pkix_pl_OID_GetCriticalExtensionOIDs
                            (extensions, &oidsList, plContext),
                            PKIX_GETCRITICALEXTENSIONOIDSFAILED);

                /* save a cached copy in case it is asked for again */
                cert->critExtOids = oidsList;
            }

            PKIX_OBJECT_UNLOCK(cert);
        }

        /* We should return a copy of the List since this list changes */
        PKIX_DUPLICATE(cert->critExtOids, pList, plContext,
                PKIX_OBJECTDUPLICATELISTFAILED);

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetAuthorityKeyIdentifier
 *      (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetAuthorityKeyIdentifier(
        PKIX_PL_Cert *cert,
        PKIX_PL_ByteArray **pAuthKeyId,
        void *plContext)
{
        PKIX_PL_ByteArray *authKeyId = NULL;
        CERTCertificate *nssCert = NULL;
        CERTAuthKeyID *authKeyIdExtension = NULL;
        PLArenaPool *arena = NULL;
        SECItem retItem;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetAuthorityKeyIdentifier");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pAuthKeyId);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->authKeyId == NULL) && (!cert->authKeyIdAbsent)){

                PKIX_OBJECT_LOCK(cert);

                if ((cert->authKeyId == NULL) && (!cert->authKeyIdAbsent)){

                        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                        if (arena == NULL) {
                                PKIX_ERROR(PKIX_OUTOFMEMORY);
                        }

                        nssCert = cert->nssCert;

                        authKeyIdExtension =
                                CERT_FindAuthKeyIDExten(arena, nssCert);
                        if (authKeyIdExtension == NULL){
                                cert->authKeyIdAbsent = PKIX_TRUE;
                                *pAuthKeyId = NULL;
                                goto cleanup;
                        }

                        retItem = authKeyIdExtension->keyID;

                        if (retItem.len == 0){
                                cert->authKeyIdAbsent = PKIX_TRUE;
                                *pAuthKeyId = NULL;
                                goto cleanup;
                        }

                        PKIX_CHECK(PKIX_PL_ByteArray_Create
                                    (retItem.data,
                                    retItem.len,
                                    &authKeyId,
                                    plContext),
                                    PKIX_BYTEARRAYCREATEFAILED);

                        /* save a cached copy in case it is asked for again */
                        cert->authKeyId = authKeyId;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->authKeyId);
        *pAuthKeyId = cert->authKeyId;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        if (arena){
                PORT_FreeArena(arena, PR_FALSE);
        }
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetSubjectKeyIdentifier
 *      (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubjectKeyIdentifier(
        PKIX_PL_Cert *cert,
        PKIX_PL_ByteArray **pSubjKeyId,
        void *plContext)
{
        PKIX_PL_ByteArray *subjKeyId = NULL;
        CERTCertificate *nssCert = NULL;
        SECItem *retItem = NULL;
        SECStatus status;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubjectKeyIdentifier");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSubjKeyId);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->subjKeyId == NULL) && (!cert->subjKeyIdAbsent)){

                PKIX_OBJECT_LOCK(cert);

                if ((cert->subjKeyId == NULL) && (!cert->subjKeyIdAbsent)){

                        retItem = SECITEM_AllocItem(NULL, NULL, 0);
                        if (retItem == NULL){
                                PKIX_ERROR(PKIX_OUTOFMEMORY);
                        }

                        nssCert = cert->nssCert;

                        status = CERT_FindSubjectKeyIDExtension
                                (nssCert, retItem);
                        if (status != SECSuccess) {
                                cert->subjKeyIdAbsent = PKIX_TRUE;
                                *pSubjKeyId = NULL;
                                goto cleanup;
                        }

                        PKIX_CHECK(PKIX_PL_ByteArray_Create
                                    (retItem->data,
                                    retItem->len,
                                    &subjKeyId,
                                    plContext),
                                    PKIX_BYTEARRAYCREATEFAILED);

                        /* save a cached copy in case it is asked for again */
                        cert->subjKeyId = subjKeyId;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->subjKeyId);
        *pSubjKeyId = cert->subjKeyId;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        if (retItem){
                SECITEM_FreeItem(retItem, PKIX_TRUE);
        }
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetExtendedKeyUsage (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetExtendedKeyUsage(
        PKIX_PL_Cert *cert,
        PKIX_List **pKeyUsage,  /* list of PKIX_PL_OID */
        void *plContext)
{
        CERTOidSequence *extKeyUsage = NULL;
        CERTCertificate *nssCert = NULL;
        PKIX_PL_OID *pkixOID = NULL;
        PKIX_List *oidsList = NULL;
        char *oidAscii = NULL;
        SECItem **oids = NULL;
        SECItem *oid = NULL;
        SECItem encodedExtKeyUsage;
        SECStatus rv;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetExtendedKeyUsage");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pKeyUsage);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->extKeyUsages == NULL) && (!cert->extKeyUsagesAbsent)){

                PKIX_OBJECT_LOCK(cert);

                if ((cert->extKeyUsages == NULL) &&
                    (!cert->extKeyUsagesAbsent)){

                        nssCert = cert->nssCert;

                        rv = CERT_FindCertExtension
                                (nssCert, SEC_OID_X509_EXT_KEY_USAGE,
                                &encodedExtKeyUsage);
                        if (rv != SECSuccess){
                                cert->extKeyUsagesAbsent = PKIX_TRUE;
                                *pKeyUsage = NULL;
                                goto cleanup;
                        }

                        extKeyUsage =
                                CERT_DecodeOidSequence(&encodedExtKeyUsage);
                        if (extKeyUsage == NULL){
                                PKIX_ERROR(PKIX_CERTDECODEOIDSEQUENCEFAILED);
                        }

                        PORT_Free(encodedExtKeyUsage.data);

                        oids = extKeyUsage->oids;

                        if (!oids){
                                /* no extended key usage extensions found */
                                cert->extKeyUsagesAbsent = PKIX_TRUE;
                                *pKeyUsage = NULL;
                                goto cleanup;
                        }

                        PKIX_CHECK(PKIX_List_Create(&oidsList, plContext),
                                    PKIX_LISTCREATEFAILED);

                        while (*oids){
                                oid = *oids++;

                                PKIX_CHECK(pkix_pl_oidBytes2Ascii
                                            (oid, &oidAscii, plContext),
                                            PKIX_OIDBYTES2ASCIIFAILED);

                                PKIX_CHECK(PKIX_PL_OID_Create
                                            (oidAscii, &pkixOID, plContext),
                                            PKIX_OIDCREATEFAILED);

                                PKIX_CHECK(PKIX_List_AppendItem
                                            (oidsList,
                                            (PKIX_PL_Object *)pkixOID,
                                            plContext),
                                            PKIX_LISTAPPENDITEMFAILED);

                                PKIX_FREE(oidAscii);

                                PKIX_DECREF(pkixOID);
                        }

                        /* save a cached copy in case it is asked for again */
                        cert->extKeyUsages = oidsList;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        if (cert->extKeyUsages){

                PKIX_INCREF(cert->extKeyUsages);

                PKIX_CHECK(PKIX_List_SetImmutable
                            (cert->extKeyUsages, plContext),
                            PKIX_LISTSETIMMUTABLEFAILED);
        }

        *pKeyUsage = cert->extKeyUsages;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);

        PKIX_FREE(oidAscii);
        PKIX_DECREF(pkixOID);

        CERT_DestroyOidSequence(extKeyUsage);

        if (PKIX_ERROR_RECEIVED){
                PKIX_DECREF(oidsList);
        }

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetBasicConstraints
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetBasicConstraints(
        PKIX_PL_Cert *cert,
        PKIX_PL_CertBasicConstraints **pBasicConstraints,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        CERTBasicConstraints nssBasicConstraint;
        SECStatus rv;
        PKIX_PL_CertBasicConstraints *basic;
        PKIX_Int32 pathLen = 0;
        PKIX_Boolean isCA = PKIX_FALSE;
        enum {
          realBC, synthBC, absentBC
        } constraintSource = absentBC;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetBasicConstraints");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pBasicConstraints);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->certBasicConstraints == NULL) &&
                (!cert->basicConstraintsAbsent)) {

                PKIX_OBJECT_LOCK(cert);

                if ((cert->certBasicConstraints == NULL) &&
                    (!cert->basicConstraintsAbsent)) {

                        nssCert = cert->nssCert;

                        PKIX_CERT_DEBUG(
                            "\t\tCalling Cert_FindBasicConstraintExten\n");
                        rv = CERT_FindBasicConstraintExten
                                (nssCert, &nssBasicConstraint);
                        if (rv == SECSuccess) {
                            constraintSource = realBC;
                        }

                        if (constraintSource == absentBC) {
                            /* can we deduce it's a CA and create a 
                               synthetic constraint?
                            */
                            CERTCertTrust trust;
                            rv = CERT_GetCertTrust(nssCert, &trust);
                            if (rv == SECSuccess) {
                                int anyWantedFlag = CERTDB_TRUSTED_CA | CERTDB_VALID_CA;
                                if ((trust.sslFlags & anyWantedFlag) 
                                    || (trust.emailFlags & anyWantedFlag) 
                                    || (trust.objectSigningFlags & anyWantedFlag)) {

                                    constraintSource = synthBC;
                                }
                            }
                        }

                        if (constraintSource == absentBC) {
                            cert->basicConstraintsAbsent = PKIX_TRUE;
                            *pBasicConstraints = NULL;
                            goto cleanup;
                        }
                }

                if (constraintSource == synthBC) {
                    isCA = PKIX_TRUE;
                    pathLen = PKIX_UNLIMITED_PATH_CONSTRAINT;
                } else {
                    isCA = (nssBasicConstraint.isCA)?PKIX_TRUE:PKIX_FALSE;
    
                    /* The pathLen has meaning only for CAs */
                    if (isCA) {
                        if (CERT_UNLIMITED_PATH_CONSTRAINT ==
                            nssBasicConstraint.pathLenConstraint) {
                            pathLen = PKIX_UNLIMITED_PATH_CONSTRAINT;
                        } else {
                            pathLen = nssBasicConstraint.pathLenConstraint;
                        }
                    }
                }

                PKIX_CHECK(pkix_pl_CertBasicConstraints_Create
                            (isCA, pathLen, &basic, plContext),
                            PKIX_CERTBASICCONSTRAINTSCREATEFAILED);

                /* save a cached copy in case it is asked for again */
                cert->certBasicConstraints = basic;
        }

        PKIX_INCREF(cert->certBasicConstraints);
        *pBasicConstraints = cert->certBasicConstraints;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetPolicyInformation
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetPolicyInformation(
        PKIX_PL_Cert *cert,
        PKIX_List **pPolicyInfo,
        void *plContext)
{
        PKIX_List *policyList = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetPolicyInformation");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pPolicyInfo);

        /* if we don't have a cached copy from before, we create one */
        if ((cert->certPolicyInfos == NULL) &&
                (!cert->policyInfoAbsent)) {

                PKIX_OBJECT_LOCK(cert);

                if ((cert->certPolicyInfos == NULL) &&
                    (!cert->policyInfoAbsent)) {

                        PKIX_CHECK(pkix_pl_Cert_DecodePolicyInfo
                                (cert->nssCert, &policyList, plContext),
                                PKIX_CERTDECODEPOLICYINFOFAILED);

                        if (!policyList) {
                                cert->policyInfoAbsent = PKIX_TRUE;
                                *pPolicyInfo = NULL;
                                goto cleanup;
                        }
                }

                PKIX_OBJECT_UNLOCK(cert);

                /* save a cached copy in case it is asked for again */
                cert->certPolicyInfos = policyList;
        }

        PKIX_INCREF(cert->certPolicyInfos);

        *pPolicyInfo = cert->certPolicyInfos;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetPolicyMappings (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetPolicyMappings(
        PKIX_PL_Cert *cert,
        PKIX_List **pPolicyMappings, /* list of PKIX_PL_CertPolicyMap */
        void *plContext)
{
        PKIX_List *policyMappings = NULL; /* list of PKIX_PL_CertPolicyMap */

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetPolicyMappings");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pPolicyMappings);

        /* if we don't have a cached copy from before, we create one */
        if (!(cert->certPolicyMappings) && !(cert->policyMappingsAbsent)) {

                PKIX_OBJECT_LOCK(cert);

                if (!(cert->certPolicyMappings) &&
                    !(cert->policyMappingsAbsent)) {

                        PKIX_CHECK(pkix_pl_Cert_DecodePolicyMapping
                                (cert->nssCert, &policyMappings, plContext),
                                PKIX_CERTDECODEPOLICYMAPPINGFAILED);

                        if (!policyMappings) {
                                cert->policyMappingsAbsent = PKIX_TRUE;
                                *pPolicyMappings = NULL;
                                goto cleanup;
                        }
                }

                PKIX_OBJECT_UNLOCK(cert);

                /* save a cached copy in case it is asked for again */
                cert->certPolicyMappings = policyMappings;
        }

        PKIX_INCREF(cert->certPolicyMappings);
        *pPolicyMappings = cert->certPolicyMappings;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetRequireExplicitPolicy
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetRequireExplicitPolicy(
        PKIX_PL_Cert *cert,
        PKIX_Int32 *pSkipCerts,
        void *plContext)
{
        PKIX_Int32 explicitPolicySkipCerts = 0;
        PKIX_Int32 inhibitMappingSkipCerts = 0;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetRequireExplicitPolicy");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSkipCerts);

        if (!(cert->policyConstraintsProcessed)) {
                PKIX_OBJECT_LOCK(cert);

                if (!(cert->policyConstraintsProcessed)) {

                        /*
                         * If we can't process it now, we probably will be
                         * unable to process it later. Set the default value.
                         */
                        cert->policyConstraintsProcessed = PKIX_TRUE;
                        cert->policyConstraintsExplicitPolicySkipCerts = -1;
                        cert->policyConstraintsInhibitMappingSkipCerts = -1;

                        PKIX_CHECK(pkix_pl_Cert_DecodePolicyConstraints
                                (cert->nssCert,
                                &explicitPolicySkipCerts,
                                &inhibitMappingSkipCerts,
                                plContext),
                                PKIX_CERTDECODEPOLICYCONSTRAINTSFAILED);

                        cert->policyConstraintsExplicitPolicySkipCerts =
                                explicitPolicySkipCerts;
                        cert->policyConstraintsInhibitMappingSkipCerts =
                                inhibitMappingSkipCerts;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        *pSkipCerts = cert->policyConstraintsExplicitPolicySkipCerts;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetPolicyMappingInhibited
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetPolicyMappingInhibited(
        PKIX_PL_Cert *cert,
        PKIX_Int32 *pSkipCerts,
        void *plContext)
{
        PKIX_Int32 explicitPolicySkipCerts = 0;
        PKIX_Int32 inhibitMappingSkipCerts = 0;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetPolicyMappingInhibited");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSkipCerts);

        if (!(cert->policyConstraintsProcessed)) {
                PKIX_OBJECT_LOCK(cert);

                if (!(cert->policyConstraintsProcessed)) {

                        /*
                         * If we can't process it now, we probably will be
                         * unable to process it later. Set the default value.
                         */
                        cert->policyConstraintsProcessed = PKIX_TRUE;
                        cert->policyConstraintsExplicitPolicySkipCerts = -1;
                        cert->policyConstraintsInhibitMappingSkipCerts = -1;

                        PKIX_CHECK(pkix_pl_Cert_DecodePolicyConstraints
                                (cert->nssCert,
                                &explicitPolicySkipCerts,
                                &inhibitMappingSkipCerts,
                                plContext),
                                PKIX_CERTDECODEPOLICYCONSTRAINTSFAILED);

                        cert->policyConstraintsExplicitPolicySkipCerts =
                                explicitPolicySkipCerts;
                        cert->policyConstraintsInhibitMappingSkipCerts =
                                inhibitMappingSkipCerts;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        *pSkipCerts = cert->policyConstraintsInhibitMappingSkipCerts;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetInhibitAnyPolicy (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetInhibitAnyPolicy(
        PKIX_PL_Cert *cert,
        PKIX_Int32 *pSkipCerts,
        void *plContext)
{
        PKIX_Int32 skipCerts = 0;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetInhibitAnyPolicy");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSkipCerts);

        if (!(cert->inhibitAnyPolicyProcessed)) {

                PKIX_OBJECT_LOCK(cert);

                if (!(cert->inhibitAnyPolicyProcessed)) {

                        /*
                         * If we can't process it now, we probably will be
                         * unable to process it later. Set the default value.
                         */
                        cert->inhibitAnyPolicyProcessed = PKIX_TRUE;
                        cert->inhibitAnySkipCerts = -1;

                        PKIX_CHECK(pkix_pl_Cert_DecodeInhibitAnyPolicy
                                (cert->nssCert, &skipCerts, plContext),
                                PKIX_CERTDECODEINHIBITANYPOLICYFAILED);

                        cert->inhibitAnySkipCerts = skipCerts;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        *pSkipCerts = cert->inhibitAnySkipCerts;
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_AreCertPoliciesCritical
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_AreCertPoliciesCritical(
        PKIX_PL_Cert *cert,
        PKIX_Boolean *pCritical,
        void *plContext)
{
        PKIX_Boolean criticality = PKIX_FALSE;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_AreCertPoliciesCritical");
        PKIX_NULLCHECK_TWO(cert, pCritical);

        PKIX_CHECK(pkix_pl_Cert_IsExtensionCritical(
                cert,
                SEC_OID_X509_CERTIFICATE_POLICIES,
                &criticality,
                plContext),
                PKIX_CERTISEXTENSIONCRITICALFAILED);

        *pCritical = criticality;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_VerifySignature (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_VerifySignature(
        PKIX_PL_Cert *cert,
        PKIX_PL_PublicKey *pubKey,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        SECKEYPublicKey *nssPubKey = NULL;
        CERTSignedData *tbsCert = NULL;
        PKIX_PL_Cert *cachedCert = NULL;
        PKIX_Error *verifySig = NULL;
        PKIX_Error *cachedSig = NULL;
        SECStatus status;
        PKIX_Boolean certEqual = PKIX_FALSE;
        PKIX_Boolean certInHash = PKIX_FALSE;
        void* wincx = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_VerifySignature");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pubKey);

        verifySig = PKIX_PL_HashTable_Lookup
                        (cachedCertSigTable,
                        (PKIX_PL_Object *) pubKey,
                        (PKIX_PL_Object **) &cachedCert,
                        plContext);

        if (cachedCert != NULL && verifySig == NULL) {
                /* Cached Signature Table lookup succeed */
                PKIX_EQUALS(cert, cachedCert, &certEqual, plContext,
                            PKIX_OBJECTEQUALSFAILED);
                if (certEqual == PKIX_TRUE) {
                        goto cleanup;
                }
                /* Different PubKey may hash to same value, skip add */
                certInHash = PKIX_TRUE;
        }

        nssCert = cert->nssCert;
        tbsCert = &nssCert->signatureWrap;

        PKIX_CERT_DEBUG("\t\tCalling SECKEY_ExtractPublicKey).\n");
        nssPubKey = SECKEY_ExtractPublicKey(pubKey->nssSPKI);
        if (!nssPubKey){
                PKIX_ERROR(PKIX_SECKEYEXTRACTPUBLICKEYFAILED);
        }

        PKIX_CERT_DEBUG("\t\tCalling CERT_VerifySignedDataWithPublicKey).\n");

        PKIX_CHECK(pkix_pl_NssContext_GetWincx
                   ((PKIX_PL_NssContext *)plContext, &wincx),
                   PKIX_NSSCONTEXTGETWINCXFAILED);

        status = CERT_VerifySignedDataWithPublicKey(tbsCert, nssPubKey, wincx);

        if (status != SECSuccess) {
                PKIX_ERROR(PKIX_SIGNATUREDIDNOTVERIFYWITHTHEPUBLICKEY);
        }

        if (certInHash == PKIX_FALSE) {
                cachedSig = PKIX_PL_HashTable_Add
                        (cachedCertSigTable,
                        (PKIX_PL_Object *) pubKey,
                        (PKIX_PL_Object *) cert,
                        plContext);

                if (cachedSig != NULL) {
                        PKIX_DEBUG("PKIX_PL_HashTable_Add skipped: entry existed\n");
                }
        }

cleanup:
        if (nssPubKey){
                PKIX_CERT_DEBUG("\t\tCalling SECKEY_DestroyPublicKey).\n");
                SECKEY_DestroyPublicKey(nssPubKey);
        }

        PKIX_DECREF(cachedCert);
        PKIX_DECREF(verifySig);
        PKIX_DECREF(cachedSig);

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_CheckValidity (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_CheckValidity(
        PKIX_PL_Cert *cert,
        PKIX_PL_Date *date,
        void *plContext)
{
        SECCertTimeValidity val;
        PRTime timeToCheck;
        PKIX_Boolean allowOverride;
        SECCertificateUsage requiredUsages;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_CheckValidity");
        PKIX_NULLCHECK_ONE(cert);

        /* if the caller supplies a date, we use it; else, use current time */
        if (date != NULL){
                PKIX_CHECK(pkix_pl_Date_GetPRTime
                        (date, &timeToCheck, plContext),
                        PKIX_DATEGETPRTIMEFAILED);
        } else {
                timeToCheck = PR_Now();
        }

        requiredUsages = ((PKIX_PL_NssContext*)plContext)->certificateUsage;
        allowOverride =
            (PRBool)((requiredUsages & certificateUsageSSLServer) ||
                     (requiredUsages & certificateUsageSSLServerWithStepUp));
        val = CERT_CheckCertValidTimes(cert->nssCert, timeToCheck, allowOverride);
        if (val != secCertTimeValid){
                PKIX_ERROR(PKIX_CERTCHECKCERTVALIDTIMESFAILED);
        }

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetValidityNotAfter (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetValidityNotAfter(
        PKIX_PL_Cert *cert,
        PKIX_PL_Date **pDate,
        void *plContext)
{
        PRTime prtime;
        SECStatus rv = SECFailure;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetValidityNotAfter");
        PKIX_NULLCHECK_TWO(cert, pDate);

        PKIX_DATE_DEBUG("\t\tCalling DER_DecodeTimeChoice).\n");
        rv = DER_DecodeTimeChoice(&prtime, &(cert->nssCert->validity.notAfter));
        if (rv != SECSuccess){
                PKIX_ERROR(PKIX_DERDECODETIMECHOICEFAILED);
        }

        PKIX_CHECK(pkix_pl_Date_CreateFromPRTime
                    (prtime, pDate, plContext),
                    PKIX_DATECREATEFROMPRTIMEFAILED);

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_VerifyKeyUsage (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_VerifyKeyUsage(
        PKIX_PL_Cert *cert,
        PKIX_UInt32 keyUsage,
        void *plContext)
{
        CERTCertificate *nssCert = NULL;
        PKIX_UInt32 nssKeyUsage = 0;
        SECStatus status;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_VerifyKeyUsage");
        PKIX_NULLCHECK_TWO(cert, cert->nssCert);

        nssCert = cert->nssCert;

        /* if cert doesn't have keyUsage extension, all keyUsages are valid */
        if (!nssCert->keyUsagePresent){
                goto cleanup;
        }

        if (keyUsage & PKIX_DIGITAL_SIGNATURE){
                nssKeyUsage = nssKeyUsage | KU_DIGITAL_SIGNATURE;
        }

        if (keyUsage & PKIX_NON_REPUDIATION){
                nssKeyUsage = nssKeyUsage | KU_NON_REPUDIATION;
        }

        if (keyUsage & PKIX_KEY_ENCIPHERMENT){
                nssKeyUsage = nssKeyUsage | KU_KEY_ENCIPHERMENT;
        }

        if (keyUsage & PKIX_DATA_ENCIPHERMENT){
                nssKeyUsage = nssKeyUsage | KU_DATA_ENCIPHERMENT;
        }

        if (keyUsage & PKIX_KEY_AGREEMENT){
                nssKeyUsage = nssKeyUsage | KU_KEY_AGREEMENT;
        }

        if (keyUsage & PKIX_KEY_CERT_SIGN){
                nssKeyUsage = nssKeyUsage | KU_KEY_CERT_SIGN;
        }

        if (keyUsage & PKIX_CRL_SIGN){
                nssKeyUsage = nssKeyUsage | KU_CRL_SIGN;
        }

        if (keyUsage & PKIX_ENCIPHER_ONLY){
                nssKeyUsage = nssKeyUsage | 0x01;
        }

        if (keyUsage & PKIX_DECIPHER_ONLY){
                /* XXX we should support this once it is fixed in NSS */
                PKIX_ERROR(PKIX_DECIPHERONLYKEYUSAGENOTSUPPORTED);
        }

        status = CERT_CheckKeyUsage(nssCert, nssKeyUsage);
        if (status != SECSuccess) {
                PKIX_ERROR(PKIX_CERTCHECKKEYUSAGEFAILED);
        }

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetNameConstraints
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetNameConstraints(
        PKIX_PL_Cert *cert,
        PKIX_PL_CertNameConstraints **pNameConstraints,
        void *plContext)
{
        PKIX_PL_CertNameConstraints *nameConstraints = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetNameConstraints");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pNameConstraints);

        /* if we don't have a cached copy from before, we create one */
        if (cert->nameConstraints == NULL && !cert->nameConstraintsAbsent) {

                PKIX_OBJECT_LOCK(cert);

                if (cert->nameConstraints == NULL &&
                    !cert->nameConstraintsAbsent) {

                        PKIX_CHECK(pkix_pl_CertNameConstraints_Create
                                (cert->nssCert, &nameConstraints, plContext),
                                PKIX_CERTNAMECONSTRAINTSCREATEFAILED);

                        if (nameConstraints == NULL) {
                                cert->nameConstraintsAbsent = PKIX_TRUE;
                        }

                        cert->nameConstraints = nameConstraints;
                }

                PKIX_OBJECT_UNLOCK(cert);

        }

        PKIX_INCREF(cert->nameConstraints);

        *pNameConstraints = cert->nameConstraints;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_CheckNameConstraints
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_CheckNameConstraints(
        PKIX_PL_Cert *cert,
        PKIX_PL_CertNameConstraints *nameConstraints,
        void *plContext)
{
        PKIX_Boolean checkPass = PKIX_TRUE;
        CERTGeneralName *nssSubjectNames = NULL;
        PLArenaPool *arena = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_CheckNameConstraints");
        PKIX_NULLCHECK_ONE(cert);

        if (nameConstraints != NULL) {

                arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                if (arena == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                }

                /* This NSS call returns both Subject and  Subject Alt Names */
                PKIX_CERT_DEBUG("\t\tCalling CERT_GetCertificateNames\n");
                nssSubjectNames = CERT_GetCertificateNames
                        (cert->nssCert, arena);

                PKIX_CHECK(pkix_pl_CertNameConstraints_CheckNameSpaceNssNames
                        (nssSubjectNames,
                        nameConstraints,
                        &checkPass,
                        plContext),
                        PKIX_CERTNAMECONSTRAINTSCHECKNAMESPACENSSNAMESFAILED);

                if (checkPass != PKIX_TRUE) {
                        PKIX_ERROR(PKIX_CERTFAILEDNAMECONSTRAINTSCHECKING);
                }
        }

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

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_MergeNameConstraints
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_MergeNameConstraints(
        PKIX_PL_CertNameConstraints *firstNC,
        PKIX_PL_CertNameConstraints *secondNC,
        PKIX_PL_CertNameConstraints **pResultNC,
        void *plContext)
{
        PKIX_PL_CertNameConstraints *mergedNC = NULL;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_MergeNameConstraints");
        PKIX_NULLCHECK_TWO(firstNC, pResultNC);

        if (secondNC == NULL) {

                PKIX_INCREF(firstNC);
                *pResultNC = firstNC;

                goto cleanup;
        }

        PKIX_CHECK(pkix_pl_CertNameConstraints_Merge
                (firstNC, secondNC, &mergedNC, plContext),
                PKIX_CERTNAMECONSTRAINTSMERGEFAILED);

        *pResultNC = mergedNC;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_IsCertTrusted
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_IsCertTrusted(
        PKIX_PL_Cert *cert,
        PKIX_Boolean *pTrusted,
        void *plContext)
{
        PKIX_CertStore_CheckTrustCallback trustCallback = NULL;
        SECCertUsage certUsage = 0;
        PKIX_Boolean trusted = PKIX_FALSE;
        SECStatus rv = SECFailure;
        unsigned int requiredFlags;
        SECTrustType trustType;
        CERTCertTrust trust;
        CERTCertificate *nssCert = NULL;
        SECCertificateUsage certificateUsage;

        PKIX_ENTER(CERT, "pkix_pl_Cert_IsCertTrusted");
        PKIX_NULLCHECK_TWO(cert, pTrusted);

        /* no key usage information and store is not trusted */
        if (plContext == NULL || cert->store == NULL) {
                *pTrusted = PKIX_FALSE;
                goto cleanup;
        }

        if (cert->store) {
                PKIX_CHECK(PKIX_CertStore_GetTrustCallback
                        (cert->store, &trustCallback, plContext),
                        PKIX_CERTSTOREGETTRUSTCALLBACKFAILED);

                PKIX_CHECK_ONLY_FATAL(trustCallback
                        (cert->store, cert, &trusted, plContext),
                        PKIX_CHECKTRUSTCALLBACKFAILED);

                if (PKIX_ERROR_RECEIVED || (trusted == PKIX_FALSE)) {

                        *pTrusted = PKIX_FALSE;
                        goto cleanup;
                }

        }

        certificateUsage = ((PKIX_PL_NssContext*)plContext)->certificateUsage;

        /* ensure we obtained a single usage bit only */
        PORT_Assert(!(certificateUsage & (certificateUsage - 1)));

        /* convert SECertificateUsage (bit mask) to SECCertUsage (enum) */
        while (0 != (certificateUsage = certificateUsage >> 1)) { certUsage++; }

        rv = CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, &trustType);
        if (rv != SECSuccess) {
                *pTrusted = PKIX_FALSE;
                goto cleanup;
        }

        nssCert = cert->nssCert;

        rv = CERT_GetCertTrust(nssCert, &trust);
        if (rv == SECSuccess) {
                unsigned int certFlags;
                certFlags = SEC_GET_TRUST_FLAGS((&trust), trustType);
                if ((certFlags & requiredFlags) == requiredFlags) {
                        trusted = PKIX_TRUE;
                }
        }

        *pTrusted = trusted;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetCacheFlag (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetCacheFlag(
        PKIX_PL_Cert *cert,
        PKIX_Boolean *pCacheFlag,
        void *plContext)
{
        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetCacheFlag");
        PKIX_NULLCHECK_TWO(cert, pCacheFlag);

        *pCacheFlag = cert->cacheFlag;

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_SetCacheFlag (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_SetCacheFlag(
        PKIX_PL_Cert *cert,
        PKIX_Boolean cacheFlag,
        void *plContext)
{
        PKIX_ENTER(CERT, "PKIX_PL_Cert_SetCacheFlag");
        PKIX_NULLCHECK_ONE(cert);

        cert->cacheFlag = cacheFlag;

        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetTrustCertStore (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetTrustCertStore(
        PKIX_PL_Cert *cert,
        PKIX_CertStore **pTrustCertStore,
        void *plContext)
{
        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetTrustCertStore");
        PKIX_NULLCHECK_TWO(cert, pTrustCertStore);

        PKIX_INCREF(cert->store);
        *pTrustCertStore = cert->store;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_SetTrustCertStore (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_SetTrustCertStore(
        PKIX_PL_Cert *cert,
        PKIX_CertStore *trustCertStore,
        void *plContext)
{
        PKIX_ENTER(CERT, "PKIX_PL_Cert_SetTrustCertStore");
        PKIX_NULLCHECK_TWO(cert, trustCertStore);

        PKIX_INCREF(trustCertStore);
        cert->store = trustCertStore;

cleanup:
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetAuthorityInfoAccess
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetAuthorityInfoAccess(
        PKIX_PL_Cert *cert,
        PKIX_List **pAiaList, /* of PKIX_PL_InfoAccess */
        void *plContext)
{
        PKIX_List *aiaList = NULL; /* of PKIX_PL_InfoAccess */
        SECItem *encodedAIA = NULL;
        CERTAuthInfoAccess **aia = NULL;
        PLArenaPool *arena = NULL;
        SECStatus rv;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetAuthorityInfoAccess");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pAiaList);

        /* if we don't have a cached copy from before, we create one */
        if (cert->authorityInfoAccess == NULL) {

                PKIX_OBJECT_LOCK(cert);

                if (cert->authorityInfoAccess == NULL) {

                    PKIX_PL_NSSCALLRV(CERT, encodedAIA, SECITEM_AllocItem,
                        (NULL, NULL, 0));

                    if (encodedAIA == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                    }

                    PKIX_PL_NSSCALLRV(CERT, rv, CERT_FindCertExtension,
                        (cert->nssCert,
                        SEC_OID_X509_AUTH_INFO_ACCESS,
                        encodedAIA));

                    if (rv == SECFailure) {
                        goto cleanup;
                    }

                    PKIX_PL_NSSCALLRV(CERT, arena, PORT_NewArena,
                        (DER_DEFAULT_CHUNKSIZE));

                    if (arena == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                    }

                    PKIX_PL_NSSCALLRV
                        (CERT, aia, CERT_DecodeAuthInfoAccessExtension,
                        (arena, encodedAIA));

                    PKIX_CHECK(pkix_pl_InfoAccess_CreateList
                        (aia, &aiaList, plContext),
                        PKIX_INFOACCESSCREATELISTFAILED);

                    cert->authorityInfoAccess = aiaList;
                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->authorityInfoAccess);

        *pAiaList = cert->authorityInfoAccess;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        if (arena != NULL) {
                PORT_FreeArena(arena, PR_FALSE);
        }

        if (encodedAIA != NULL) {
                SECITEM_FreeItem(encodedAIA, PR_TRUE);
        }

        PKIX_RETURN(CERT);
}

/* XXX Following defines belongs to NSS */
static const unsigned char siaOIDString[] = {0x2b, 0x06, 0x01, 0x05, 0x05,
                                0x07, 0x01, 0x0b};
#define OI(x) { siDEROID, (unsigned char *)x, sizeof x }

/*
 * FUNCTION: PKIX_PL_Cert_GetSubjectInfoAccess
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetSubjectInfoAccess(
        PKIX_PL_Cert *cert,
        PKIX_List **pSiaList, /* of PKIX_PL_InfoAccess */
        void *plContext)
{
        PKIX_List *siaList; /* of PKIX_PL_InfoAccess */
        SECItem siaOID = OI(siaOIDString);
        SECItem *encodedSubjInfoAccess = NULL;
        CERTAuthInfoAccess **subjInfoAccess = NULL;
        PLArenaPool *arena = NULL;
        SECStatus rv;

        PKIX_ENTER(CERT, "PKIX_PL_Cert_GetSubjectInfoAccess");
        PKIX_NULLCHECK_THREE(cert, cert->nssCert, pSiaList);

        /* XXX
         * Codes to deal with SubjectInfoAccess OID should be moved to
         * NSS soon. I implemented them here so we don't touch NSS
         * source tree, from JP's suggestion.
         */

        /* if we don't have a cached copy from before, we create one */
        if (cert->subjectInfoAccess == NULL) {

                PKIX_OBJECT_LOCK(cert);

                if (cert->subjectInfoAccess == NULL) {

                    encodedSubjInfoAccess = SECITEM_AllocItem(NULL, NULL, 0);
                    if (encodedSubjInfoAccess == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                    }

                    PKIX_CERT_DEBUG
                        ("\t\tCalling CERT_FindCertExtensionByOID).\n");
                    rv = CERT_FindCertExtensionByOID
                                (cert->nssCert, &siaOID, encodedSubjInfoAccess);

                    if (rv == SECFailure) {
                        goto cleanup;
                    }

                    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
                    if (arena == NULL) {
                        PKIX_ERROR(PKIX_OUTOFMEMORY);
                    }

                    /* XXX
                     * Decode Subject Information Access -
                     * since its type is the same as Authority Information
                     * Access, reuse the call. NSS- change name to avoid
                     * confusion.
                     */
                    PKIX_CERT_DEBUG
                        ("\t\tCalling CERT_DecodeAuthInfoAccessExtension).\n");
                    subjInfoAccess = CERT_DecodeAuthInfoAccessExtension
                        (arena, encodedSubjInfoAccess);

                    PKIX_CHECK(pkix_pl_InfoAccess_CreateList
                            (subjInfoAccess, &siaList, plContext),
                            PKIX_INFOACCESSCREATELISTFAILED);

                    cert->subjectInfoAccess = siaList;

                }

                PKIX_OBJECT_UNLOCK(cert);
        }

        PKIX_INCREF(cert->subjectInfoAccess);
        *pSiaList = cert->subjectInfoAccess;

cleanup:
	PKIX_OBJECT_UNLOCK(lockedObject);
        if (arena != NULL) {
                PORT_FreeArena(arena, PR_FALSE);
        }

        if (encodedSubjInfoAccess != NULL) {
                SECITEM_FreeItem(encodedSubjInfoAccess, PR_TRUE);
        }
        PKIX_RETURN(CERT);
}

/*
 * FUNCTION: PKIX_PL_Cert_GetCERTCertificate
 * (see comments in pkix_pl_pki.h)
 */
PKIX_Error *
PKIX_PL_Cert_GetCERTCertificate(
        PKIX_PL_Cert *cert,
        CERTCertificate **pnssCert, 
        void *plContext)
{
    PKIX_ENTER(CERT, "PKIX_PL_Cert_GetNssCert");
    PKIX_NULLCHECK_TWO(cert, pnssCert);

    *pnssCert = CERT_DupCertificate(cert->nssCert);

    PKIX_RETURN(CERT);
}
