/**
 * XML Security Library (http://www.aleksey.com/xmlsec).
 *
 * This is free software; see the Copyright file in the source distribution for precise wording.
 *
 * Copyright (C) 2003-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
 * Copyright (c) 2003 America Online, Inc.  All rights reserved.
 */
/**
 * @addtogroup xmlsec_nss_x509
 * @brief X509 certificates verification support functions for NSS.
 */
#include "globals.h"

#ifndef XMLSEC_NO_X509

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <cert.h>
#include <secerr.h>
#include <secder.h>
#include <sechash.h>
#include <keyhi.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/keys.h>
#include <xmlsec/keyinfo.h>
#include <xmlsec/keysmngr.h>
#include <xmlsec/base64.h>
#include <xmlsec/errors.h>
#include <xmlsec/private.h>
#include <xmlsec/xmltree.h>

#include <xmlsec/nss/crypto.h>
#include <xmlsec/nss/x509.h>

#include "../cast_helpers.h"
#include "../x509_helpers.h"
#include "private.h"

/******************************************************************************
 *
 * Internal NSS X509 store CTX
 *
  *****************************************************************************/
typedef struct _xmlSecNssX509StoreCtx           xmlSecNssX509StoreCtx,
                                                *xmlSecNssX509StoreCtxPtr;
struct _xmlSecNssX509StoreCtx {
    /* Two uses:
     *
     * 1) Just keeping a reference to destroy later.
     *
     * 2) NSS doesn't update it's cache correctly when new certs are added
     *          https://bugzilla.mozilla.org/show_bug.cgi?id=211051
     *    we use this list to perform search ourselves.
     */

    CERTCertList* certsList; /* just keeping a reference to destroy later */

    xmlSecNssX509CrlNodePtr crlsList;
    unsigned int     numCrls;
};

/******************************************************************************
 *
 * xmlSecNssKeyDataStoreX509Id:
 *
  *****************************************************************************/
XMLSEC_KEY_DATA_STORE_DECLARE(NssX509Store, xmlSecNssX509StoreCtx)
#define xmlSecNssX509StoreSize XMLSEC_KEY_DATA_STORE_SIZE(NssX509Store)

static int              xmlSecNssX509StoreInitialize    (xmlSecKeyDataStorePtr store);
static void             xmlSecNssX509StoreFinalize      (xmlSecKeyDataStorePtr store);


static xmlSecKeyDataStoreKlass xmlSecNssX509StoreKlass = {
    sizeof(xmlSecKeyDataStoreKlass),
    xmlSecNssX509StoreSize,

    /* data */
    xmlSecNameX509Store,                        /* const xmlChar* name; */

    /* constructors/destructor */
    xmlSecNssX509StoreInitialize,               /* xmlSecKeyDataStoreInitializeMethod initialize; */
    xmlSecNssX509StoreFinalize,                 /* xmlSecKeyDataStoreFinalizeMethod finalize; */

    /* reserved for the future */
    NULL,                                       /* void* reserved0; */
    NULL,                                       /* void* reserved1; */
};

static CERTCertificate*         xmlSecNssX509FindCert(CERTCertList* certsList, xmlSecNssX509FindCertCtxPtr findCertCtx);
static int                      xmlSecNssX509VerifyCRLTimeValidity(CERTSignedCrl* crl, xmlSecKeyInfoCtxPtr keyInfoCtx);


/**
 * @brief The NSS X509 certificates key data store klass.
 * @return pointer to NSS X509 certificates key data store klass.
 */
xmlSecKeyDataStoreId
xmlSecNssX509StoreGetKlass(void) {
    return(&xmlSecNssX509StoreKlass);
}

/**
 * @brief Searches @p store for a certificate that matches given criteria.
 * @param store the pointer to X509 key data store klass.
 * @param subjectName the desired certificate name.
 * @param issuerName the desired certificate issuer name.
 * @param issuerSerial the desired certificate issuer serial number.
 * @param ski the desired certificate SKI.
 * @param keyInfoCtx the pointer to &lt;dsig:KeyInfo/&gt; element processing context.
 *
 *
 * @return pointer to found certificate or NULL if certificate is not found
 * or an error occurs.
 */
CERTCertificate *
xmlSecNssX509StoreFindCert(xmlSecKeyDataStorePtr store, xmlChar *subjectName,
                                xmlChar *issuerName, xmlChar *issuerSerial,
                                xmlChar *ski, xmlSecKeyInfoCtx* keyInfoCtx) {
    if(ski != NULL) {
        xmlSecSize skiDecodedSize = 0;
        int ret;

        /* our usual trick with base64 decode */
        ret = xmlSecBase64DecodeInPlace(ski, &skiDecodedSize);
        if(ret < 0) {
            xmlSecInternalError2("xmlSecBase64DecodeInPlace", NULL,
                "ski=%s", xmlSecErrorsSafeString(ski));
            return(NULL);
        }

        return(xmlSecNssX509StoreFindCert_ex(store, subjectName, issuerName, issuerSerial,
            (xmlSecByte*)ski, skiDecodedSize, keyInfoCtx));
    } else {
        return(xmlSecNssX509StoreFindCert_ex(store, subjectName, issuerName, issuerSerial,
            NULL, 0, keyInfoCtx));

    }
}


/**
 * @brief Deprecated. Searches @p store for a certificate that matches given criteria.
 * @param store the pointer to X509 key data store klass.
 * @param subjectName the desired certificate name.
 * @param issuerName the desired certificate issuer name.
 * @param issuerSerial the desired certificate issuer serial number.
 * @param ski the desired certificate SKI.
 * @param skiSize the desired certificate SKI size.
 * @param keyInfoCtx the pointer to &lt;dsig:KeyInfo/&gt; element processing context.
 *
 *
 * @return pointer to found certificate or NULL if certificate is not found
 * or an error occurs.
 */
CERTCertificate *
xmlSecNssX509StoreFindCert_ex(xmlSecKeyDataStorePtr store, xmlChar *subjectName,
                                xmlChar *issuerName, xmlChar *issuerSerial,
                                 xmlSecByte * ski, xmlSecSize skiSize,
                                 xmlSecKeyInfoCtx* keyInfoCtx XMLSEC_ATTRIBUTE_UNUSED) {
    xmlSecNssX509StoreCtxPtr ctx;
    xmlSecNssX509FindCertCtx findCertCtx;
    CERTCertificate * cert;
    int ret;

    xmlSecAssert2(store != NULL, NULL);
    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), NULL);
    UNREFERENCED_PARAMETER(keyInfoCtx);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, NULL);

    /* ctx->certsList CAN be NULL since we are searching NSSDB as well */

    ret = xmlSecNssX509FindCertCtxInitialize(&findCertCtx,
            subjectName,
            issuerName, issuerSerial,
            ski, skiSize);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509FindCertCtxInitialize", NULL);
        xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
        return(NULL);
    }

    cert = xmlSecNssX509FindCert(ctx->certsList, &findCertCtx);

    /* done */
    xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
    return(cert);
}

CERTCertificate *
xmlSecNssX509StoreFindCertByValue(xmlSecKeyDataStorePtr store, xmlSecKeyX509DataValuePtr x509Value) {
    xmlSecNssX509StoreCtxPtr ctx;
    xmlSecNssX509FindCertCtx findCertCtx;
    CERTCertificate * cert;
    int ret;

    xmlSecAssert2(store != NULL, NULL);
    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), NULL);
    xmlSecAssert2(x509Value != NULL, NULL);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, NULL);

    /* ctx->certsList CAN be NULL since we are searching NSSDB as well */

    ret = xmlSecNssX509FindCertCtxInitializeFromValue(&findCertCtx, x509Value);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509FindCertCtxInitializeFromValue", NULL);
        xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
        return(NULL);
    }

    cert = xmlSecNssX509FindCert(ctx->certsList, &findCertCtx);

    /* done */
    xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
    return(cert);
}

/* returns 1 if cert was revoked, 0 if not, and a negative value if an error occurs */
static int
xmlSecNssX509StoreCheckIfCertIsRevoked(CERTCertificate* cert, CERTSignedCrl* crl, xmlSecKeyInfoCtx* keyInfoCtx) {
    CERTCrlEntry *entry;
    SECStatus rv;
    int ret;
    int ii;

    xmlSecAssert2(cert != NULL, -1);
    xmlSecAssert2(crl != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* do we have any revocation entries? */
    if (crl->crl.entries == NULL) {
        return(0);
    }
    for(ii = 0; ((entry = crl->crl.entries[ii]) != NULL); ++ii) {
        if (SECITEM_CompareItem(&(cert->serialNumber), &(entry->serialNumber)) != SECEqual) {
            continue;
        }
        /* check revocation date: if we are checking against current time, we assume
         * that CRL and revocation do NOT come from the future and we don't need to check
         * the timestamps */
        if(keyInfoCtx->certsVerificationTime > 0) {
            PRTime revocationDate = 0;
            time_t revocationTs = 0;

            rv = DER_DecodeTimeChoice(&revocationDate, &(entry->revocationDate));
            if((rv != SECSuccess) || (revocationDate == 0)) {
                xmlSecNssError("DER_DecodeTimeChoice(revocationDate)", NULL);
                return(-1);
            }
            ret = xmlSecNssX509CertGetTime(&revocationDate, &revocationTs);
            if((ret < 0) || (revocationTs == 0)) {
                xmlSecInternalError("xmlSecNssX509CertGetTime(revocationDate)", NULL);
                return(-1);
            }
            if(keyInfoCtx->certsVerificationTime < revocationTs) {
                /* verification time before revocation ts, this doesn't apply */
                continue;
            }
        }

        /* we found a valid revocation entry for this cert */
        return(1);
    }

    /* not found */
    return(0);
}

static int
xmlSecNssX509StoreFindBestCrl(xmlSecNssX509StoreCtxPtr x509StoreCtx, CERTCertificate* cert, CERTSignedCrl ** res, xmlSecKeyInfoCtx* keyInfoCtx) {
    xmlSecNssX509CrlNodePtr cur;
    PRTime lastUpdate = 0;
    PRTime resLastUpdate = 0;
    int timeRet;
    SECStatus rv;

    xmlSecAssert2(x509StoreCtx != NULL, -1);
    xmlSecAssert2(cert != NULL, -1);
    xmlSecAssert2(res != NULL, -1);
    xmlSecAssert2((*res) == NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* find best matching CRL */
    for(cur = x509StoreCtx->crlsList; cur != NULL; cur = cur->next) {
        if(cur->crl == NULL) {
            continue;
        }
        if (SECITEM_CompareItem(&(cert->derIssuer), &(cur->crl->crl.derName)) != SECEqual) {
            continue;
        }

        /* skip CRLs that are not yet valid or have expired */
        timeRet = xmlSecNssX509VerifyCRLTimeValidity(cur->crl, keyInfoCtx);
        if(timeRet < 0) {
            xmlSecInternalError("xmlSecNssX509VerifyCRLTimeValidity", NULL);
            return(-1);
        } else if(timeRet != 1) {
            continue;
        }

        /* get lastUpdate time */
        rv = DER_DecodeTimeChoice(&lastUpdate, &(cur->crl->crl.lastUpdate));
        if((rv != SECSuccess) || (lastUpdate == 0)) {
            xmlSecNssError("DER_DecodeTimeChoice(lastUpdate)", NULL);
            return(-1);
        }

        /* Use latest CRL by the last update time */
        if(((*res) == NULL) || (resLastUpdate < lastUpdate)) {
            (*res) = cur->crl;
            resLastUpdate = lastUpdate;
        }
    }

    /* done! */
    return(0);
}

static int
xmlSecNssX509StoreRemoveRevokedCerts(xmlSecNssX509StoreCtxPtr x509StoreCtx, CERTCertList* certs,
    CERTCertList** res, xmlSecKeyInfoCtx* keyInfoCtx
) {
    CERTCertListNode* cur;
    CERTCertificate* cert;
    SECStatus rv;
    int ret;

    xmlSecAssert2(x509StoreCtx != NULL, -1);
    xmlSecAssert2(certs != NULL, -1);
    xmlSecAssert2(res != NULL, -1);
    xmlSecAssert2((*res) == NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    (*res) = CERT_NewCertList();
    if((*res) == NULL) {
        xmlSecNssError("CERT_NewCertList", NULL);
        return(-1);
    }

    for (cur = CERT_LIST_HEAD(certs); !CERT_LIST_END(cur, certs); cur = CERT_LIST_NEXT(cur)) {
        CERTSignedCrl* crl = NULL;

        if(cur->cert == NULL) {
            continue;
        }

        ret = xmlSecNssX509StoreFindBestCrl(x509StoreCtx, cur->cert, &crl, keyInfoCtx);
        if(ret < 0) {
            xmlSecInternalError("xmlSecNssX509StoreFindBestCrl",  NULL);
            return(-1);
        }
        if(crl != NULL) {
            ret = xmlSecNssX509StoreCheckIfCertIsRevoked(cur->cert, crl, keyInfoCtx);
            if(ret < 0) {
                xmlSecInternalError("xmlSecNssX509StoreCheckIfCertIsRevoked",  NULL);
                return(-1);
            } else if(ret != 0) {
                /* cert was revoked */
                continue;
            }
        }

        /* add to the output */
        cert = CERT_DupCertificate(cur->cert);
        if(cert == NULL) {
            xmlSecNssError("CERT_DupCertificate", NULL);
            return(-1);
        }
        rv = CERT_AddCertToListTail((*res),  cert);
        if(rv != SECSuccess) {
            xmlSecNssError("CERT_AddCertToListTail", NULL);
            return(-1);
        }
    }

    /* done */
    return(0);
}

static CERTCertificate *
xmlSecNssX509StoreFindChildCert(CERTCertificate* cert, CERTCertList* certs) {
    CERTCertListNode* cur;

    xmlSecAssert2(cert != NULL, NULL);
    xmlSecAssert2(certs != NULL, NULL);

     for (cur = CERT_LIST_HEAD(certs); !CERT_LIST_END(cur, certs); cur = CERT_LIST_NEXT(cur)) {
        /* allow self signed certs */
        if(cur->cert == cert) {
            continue;
        }
        if (SECITEM_CompareItem(&(cur->cert->derIssuer), &(cert->derSubject)) == SECEqual) {
            return(cur->cert);
        }
     }
     return(NULL);
}

static int64
xmlSecNssX509SGetVerificationTime(xmlSecKeyInfoCtx* keyInfoCtx) {
    xmlSecAssert2(keyInfoCtx != NULL, 0);

    if(keyInfoCtx->certsVerificationTime > 0) {
        /* convert the time since epoch in seconds to microseconds */
        return (int64)keyInfoCtx->certsVerificationTime * PR_USEC_PER_SEC;
    } else {
        return PR_Now();
    }
}

/* returns 1 if verified, 0 if not, an < 0 if an error occurs */
static int
xmlSecNssX509StoreVerifyCert(CERTCertDBHandle *handle, CERTCertificate* cert, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    int64 verificationTime;
    SECStatus status;
    PRErrorCode err;

    xmlSecAssert2(handle != NULL, -1);
    xmlSecAssert2(cert != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* do we need to verify anything at all? */
    if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) {
        return(1);
    }

    /* get verification time */
    verificationTime = xmlSecNssX509SGetVerificationTime(keyInfoCtx);

    /* it's important to set the usage here, otherwise no real verification
     * is performed. */
    status = CERT_VerifyCertificate(handle, cert, PR_FALSE,
                certificateUsageEmailSigner,
                verificationTime , NULL, NULL, NULL);
    if(status == SECSuccess) {
        return(1);
    }

    /* not verified, print an error and bail out */
    err = PORT_GetError();
    switch(err) {
        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
        case SEC_ERROR_CA_CERT_INVALID:
        case SEC_ERROR_UNKNOWN_SIGNER:
            xmlSecOtherError2(XMLSEC_ERRORS_R_CERT_ISSUER_FAILED, NULL,
                "subject=\"%s\"; reason=the issuer's cert is expired/invalid or not found",
                xmlSecErrorsSafeString(cert != NULL ? cert->subjectName : NULL));
            break;
        case SEC_ERROR_EXPIRED_CERTIFICATE:
            xmlSecOtherError2(XMLSEC_ERRORS_R_CERT_HAS_EXPIRED, NULL,
                "subject=\"%s\"; reason=expired",
                xmlSecErrorsSafeString(cert != NULL ? cert->subjectName : NULL));
            break;
        case SEC_ERROR_REVOKED_CERTIFICATE:
            xmlSecOtherError2(XMLSEC_ERRORS_R_CERT_REVOKED, NULL,
                "subject=\"%s\"; reason=revoked",
                xmlSecErrorsSafeString(cert != NULL ? cert->subjectName : NULL));
            break;
        default:
            xmlSecOtherError3(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL,
                "subject=\"%s\"; reason=%d",
                xmlSecErrorsSafeString(cert != NULL ? cert->subjectName : NULL),
                err);
            break;
    }
    return(0);
}

/**
 * @brief Verifies @p key with the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit
 * @param store the pointer to X509 key data store klass.
 * @param key the pointer to key.
 * @param keyInfoCtx the key info context for verification.
 *
 * function:
 * - Checks that key certificate is present
 * - Checks that key certificate is valid
 *
 * Adds @p key to the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit
 * function.
 *
 * @return 1 if key is verified, 0 otherwise, or a negative value if an error occurs.
 */
int
xmlSecNssX509StoreVerifyKey(xmlSecKeyDataStorePtr store, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecNssX509StoreCtxPtr ctx;
    xmlSecKeyDataPtr x509Data;
    CERTCertificate* key_cert;
    int ret;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, -1);

    /* retrieve X509 data and get key cert, other certs and crls */
    x509Data = xmlSecKeyGetData(key, xmlSecNssKeyDataX509Id);
    if(x509Data == NULL) {
        xmlSecInternalError("xmlSecKeyGetData(xmlSecNssKeyDataX509Id)", xmlSecKeyDataStoreGetName(store));
        return(0); /* key cannot be verified w/o key cert */
    }
    key_cert = xmlSecNssKeyDataX509GetKeyCert(x509Data);
    if(key_cert == NULL) {
        xmlSecInternalError("xmlSecNssKeyDataX509GetKeyCert", xmlSecKeyDataStoreGetName(store));
        return(0); /* key cannot be verified w/o key cert */
    }

    ret = xmlSecNssX509StoreVerifyCert(CERT_GetDefaultCertDB(), key_cert, keyInfoCtx);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509StoreVerifyCert", xmlSecKeyDataStoreGetName(store));
        return(-1);
    } else if(ret != 1) {
        return(0); /* cert verification failed*/
    }

    /* success */
    return(1);
}

/**
 * @brief Verifies @p certs list.
 * @param store the pointer to X509 key data store klass.
 * @param certs the untrusted certificates stack.
 * @param keyInfoCtx the pointer to &lt;dsig:KeyInfo/&gt; element processing context.
 * @return pointer to the first verified certificate from @p certs.
 */
CERTCertificate *
xmlSecNssX509StoreVerify(xmlSecKeyDataStorePtr store, CERTCertList* certs, xmlSecKeyInfoCtx* keyInfoCtx) {
    xmlSecNssX509StoreCtxPtr ctx;
    CERTCertListNode* cur;
    CERTCertList* good_certs = NULL;

    CERTCertificate* res = NULL;
    int ret;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), NULL);
    xmlSecAssert2(certs != NULL, NULL);
    xmlSecAssert2(keyInfoCtx != NULL, NULL);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, NULL);


    /* do we need to verify anything at all? */
    if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) {
        good_certs = certs;
    } else {
        /* look through the certs and remove all revoked certs */
        ret = xmlSecNssX509StoreRemoveRevokedCerts(ctx, certs, &good_certs, keyInfoCtx);
        if((ret < 0) || (good_certs == NULL)) {
            xmlSecInternalError("xmlSecNssX509StoreRemoveRevokedCerts",  xmlSecKeyDataStoreGetName(store));
            goto done;
        }
    }

    /* now go through all good certs we have and try to verify them */
    for (cur = CERT_LIST_HEAD(good_certs); (!CERT_LIST_END(cur, good_certs)) && (res == NULL); cur = CERT_LIST_NEXT(cur)) {
        CERTCertificate* cert = cur->cert;

        /* if cert is the issuer of any other cert in the list, then it is
         * to be skipped (note that we are using the bigger "certs" list instead of good_certs!) */
        if(xmlSecNssX509StoreFindChildCert(cert, certs) != NULL) {
            /* found a child, skip */
            continue;
        }

        ret = xmlSecNssX509StoreVerifyCert(CERT_GetDefaultCertDB(), cert, keyInfoCtx);
        if(ret < 0) {
            xmlSecInternalError("xmlSecNssX509StoreVerifyCert", xmlSecKeyDataStoreGetName(store));
            continue; /* ignore all errors and try other certs */
        } else if(ret != 1) {
            continue; /* ignore all errors and try other certs */
        }

        /* DONE! */
        res = cert;
    }


done:
    /* SMALL HACK: we are using the fact that NSS implements certs as ref counted objects
     * and CERT_DupCertificate() simply bumps the counter. Otherwise, the "cert"
     * might belong to good_certs and will be destroyed here. But exactly the
     * same pointer is in certs as well so we are good. Otherwise we will need to find
     * a certificates in "certs" that matches "cert" and return that pointer instead.
     */
    if((good_certs != certs) && (good_certs != NULL)) {
        CERT_DestroyCertList(good_certs);
    }
    return(res);
}

/**
 * @brief Adds cert to the trusted or untrusted store.
 * @details Adds trusted (root) or untrusted certificate to the store.
 * @param store the pointer to X509 key data store klass.
 * @param cert the pointer to NSS X509 certificate.
 * @param type the certificate type (trusted/untrusted).
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecNssX509StoreAdoptCert(xmlSecKeyDataStorePtr store, CERTCertificate* cert, xmlSecKeyDataType type) {
    xmlSecNssX509StoreCtxPtr ctx;
    int ret;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), -1);
    xmlSecAssert2(cert != NULL, -1);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, -1);

    if(ctx->certsList == NULL) {
        ctx->certsList = CERT_NewCertList();
        if(ctx->certsList == NULL) {
            xmlSecNssError("CERT_NewCertList", xmlSecKeyDataStoreGetName(store));
            return(-1);
        }
    }

    ret = CERT_AddCertToListTail(ctx->certsList, cert);
    if(ret != SECSuccess) {
        xmlSecNssError("CERT_AddCertToListTail", xmlSecKeyDataStoreGetName(store));
        return(-1);
    }

    if(type == xmlSecKeyDataTypeTrusted) {
        SECStatus status;

        /* if requested, mark the certificate as trusted */
        CERTCertTrust trust;
        status = CERT_DecodeTrustString(&trust, "TCu,Cu,Tu");
        if(status != SECSuccess) {
            xmlSecNssError("CERT_DecodeTrustString", xmlSecKeyDataStoreGetName(store));
            return(-1);
        }
        status = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust);
        if(status != SECSuccess) {
            xmlSecNssError("CERT_ChangeCertTrust", xmlSecKeyDataStoreGetName(store));
            return(-1);
        }
    }

    return(0);
}


/**
 * @brief Adds CRL to the store.
 * @param store the pointer to X509 key data store klass.
 * @param crl the pointer to NSS X509 CRL.
 * @return 0 on success or a negative value if an error occurs.
 */
int
xmlSecNssX509StoreAdoptCrl(xmlSecKeyDataStorePtr store, CERTSignedCrl * crl) {
    xmlSecNssX509StoreCtxPtr ctx;
    int ret;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), -1);
    xmlSecAssert2(crl != NULL, -1);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, -1);

    ret = xmlSecNssX509CrlListAdoptCrl(&(ctx->crlsList), crl);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509CrlListAdoptCrl", xmlSecKeyDataStoreGetName(store));
        return(-1);
    }
    ++ctx->numCrls;
    return(0);
}

/* Helper function to verify CRL time validity */
static int
xmlSecNssX509VerifyCRLTimeValidity(CERTSignedCrl* crl, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    PRTime verification_time;
    PRTime thisUpdate = 0;
    PRTime nextUpdate = 0;
    time_t verification_ts;

    xmlSecAssert2(crl != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* Get verification time */
    if(keyInfoCtx->certsVerificationTime > 0) {
        verification_ts = keyInfoCtx->certsVerificationTime;
    } else {
        verification_ts = time(NULL);
    }

    /* Convert time_t to PRTime (microseconds since epoch) */
    verification_time = ((PRTime)verification_ts) * PR_USEC_PER_SEC;

    /* Get thisUpdate */
    if(crl->crl.lastUpdate.data != NULL) {
        SECStatus rv = DER_DecodeTimeChoice(&thisUpdate, &(crl->crl.lastUpdate));
        if(rv != SECSuccess) {
            xmlSecNssError("DER_DecodeTimeChoice(thisUpdate)", NULL);
            return(-1);
        }

        /* Verify thisUpdate <= verification_time */
        if(thisUpdate > verification_time) {
            /* CRL not yet valid */
            xmlSecOtherError(XMLSEC_ERRORS_R_CRL_NOT_YET_VALID, NULL, NULL);
            return(0);
        }
    }

    /* Get nextUpdate */
    if(crl->crl.nextUpdate.data != NULL) {
        SECStatus rv = DER_DecodeTimeChoice(&nextUpdate, &(crl->crl.nextUpdate));
        if(rv != SECSuccess) {
            xmlSecNssError("DER_DecodeTimeChoice(nextUpdate)", NULL);
            return(-1);
        }

        /* Verify verification_time <= nextUpdate */
        if(verification_time > nextUpdate) {
            /* CRL expired */
            xmlSecOtherError(XMLSEC_ERRORS_R_CRL_HAS_EXPIRED, NULL, NULL);
            return(0);
        }
    }

    /* Success */
    return(1);
}

/* Helper function to verify CRL signature */
static int
xmlSecNssX509VerifyCRLSignature(xmlSecNssX509StoreCtxPtr ctx, CERTSignedCrl* crl, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    CERTCertificate* issuer_cert = NULL;
    SECKEYPublicKey* pubkey = NULL;
    SECStatus rv;
    int64 verificationTime;
    int res = -1;

    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(crl != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* Find the CRL issuer certificate in the store */
    if((crl->crl.derName.data != NULL) && (crl->crl.derName.len > 0)) {
        /* Search in the certs list */
        if(ctx->certsList != NULL) {
            CERTCertListNode* node;
            for(node = CERT_LIST_HEAD(ctx->certsList); !CERT_LIST_END(node, ctx->certsList); node = CERT_LIST_NEXT(node)) {
                if(node->cert != NULL) {
                    /* Compare CRL issuer (derName) with certificate subject (derSubject) */
                    if(SECITEM_CompareItem(&(crl->crl.derName), &(node->cert->derSubject)) == SECEqual) {
                        issuer_cert = CERT_DupCertificate(node->cert);
                        break;
                    }
                }
            }
        }

        /* If not found in store, try to find in NSS database */
        if(issuer_cert == NULL) {
            issuer_cert = CERT_FindCertByName(CERT_GetDefaultCertDB(), &(crl->crl.derName));
        }
    }

    if(issuer_cert == NULL) {
        xmlSecOtherError(XMLSEC_ERRORS_R_CERT_NOT_FOUND, NULL, "CRL issuer certificate not found");
        goto done;
    }

    /* Get the public key from the issuer certificate */
    pubkey = CERT_ExtractPublicKey(issuer_cert);
    if(pubkey == NULL) {
        xmlSecNssError("CERT_ExtractPublicKey", NULL);
        goto done;
    }

    /* get verification time */
    verificationTime = xmlSecNssX509SGetVerificationTime(keyInfoCtx);

    /* Verify the CRL signature */
    rv = CERT_VerifySignedData(&(crl->signatureWrap), issuer_cert, verificationTime, NULL);
    if(rv != SECSuccess) {
        xmlSecNssError("CERT_VerifySignedData", NULL);
        /* Verification failed */
        res = 0;
        goto done;
    }

    /* Success: verified */
    res = 1;

done:
    if(pubkey != NULL) {
        SECKEY_DestroyPublicKey(pubkey);
    }
    if(issuer_cert != NULL) {
        CERT_DestroyCertificate(issuer_cert);
    }
    return(res);
}

/**
 * @brief Verifies @p crl by checking:
 * @param store the pointer to X509 key data store klass.
 * @param crl the CRL to verify.
 * @param keyInfoCtx the key info context for verification parameters.
 *
 * 1. Signature is valid (signed by issuer cert in store)
 * 2. thisUpdate <= verification_time <= nextUpdate
 *
 * @return 1 if verified, 0 if not verified, or a negative value on error.
 */
int
xmlSecNssX509StoreVerifyCrl(xmlSecKeyDataStorePtr store, CERTSignedCrl* crl,
    xmlSecKeyInfoCtxPtr keyInfoCtx
) {
    xmlSecNssX509StoreCtxPtr ctx;
    int ret;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), -1);
    xmlSecAssert2(crl != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    /* do we even need to verify the CRL? */
    if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) {
        return(1);
    }

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, -1);

    /* Verify time validity first (fast check) */
    ret = xmlSecNssX509VerifyCRLTimeValidity(crl, keyInfoCtx);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509VerifyCRLTimeValidity", xmlSecKeyDataStoreGetName(store));
        return(-1);
    } else if(ret != 1) {
        /* Time validity check failed */
        return(0);
    }

    /* Verify CRL signature (slower check) */
    ret = xmlSecNssX509VerifyCRLSignature(ctx, crl, keyInfoCtx);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509VerifyCRLSignature", xmlSecKeyDataStoreGetName(store));
        return(-1);
    } else if(ret != 1) {
        /* Signature verification failed */
        return(0);
    }

    /* Success */
    return(1);
}

static int
xmlSecNssX509StoreInitialize(xmlSecKeyDataStorePtr store) {
    xmlSecNssX509StoreCtxPtr ctx;
    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId), -1);

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert2(ctx != NULL, -1);

    memset(ctx, 0, sizeof(xmlSecNssX509StoreCtx));

    return(0);
}

static void
xmlSecNssX509StoreFinalize(xmlSecKeyDataStorePtr store) {
    xmlSecNssX509StoreCtxPtr ctx;
    xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecNssX509StoreId));

    ctx = xmlSecNssX509StoreGetCtx(store);
    xmlSecAssert(ctx != NULL);

    if (ctx->certsList) {
        CERT_DestroyCertList(ctx->certsList);
        ctx->certsList = NULL;
    }
    if (ctx->crlsList != NULL) {
        xmlSecNssX509CrlListDestroy(ctx->crlsList);
        ctx->crlsList = NULL;
    }

    memset(ctx, 0, sizeof(xmlSecNssX509StoreCtx));
}


/******************************************************************************
 *
 * Low-level x509 functions
 *
  *****************************************************************************/
static CERTName *
xmlSecNssGetCertName(const xmlChar * name) {
    xmlChar *name2;
    xmlChar *p;
    CERTName *res;

    xmlSecAssert2(name != NULL, NULL);

    /* nss doesn't support emailAddress (see https://bugzilla.mozilla.org/show_bug.cgi?id=561689)
     * This code is not bullet proof and may produce incorrect results if someone has
     * "emailAddress=" string in one of the fields, but it is best I can suggest to fix
     * this problem.
     */
    name2 = xmlStrdup(name);
    if(name2 == NULL) {
        xmlSecStrdupError(name, NULL);
        return(NULL);
    }
    while( (p = (xmlChar*)xmlStrstr(name2, BAD_CAST "emailAddress=")) != NULL) {
        memcpy(p, "           E=", 13);
    }

    res = CERT_AsciiToName((char*)name2);
    if (res == NULL) {
        xmlSecNssError2("CERT_AsciiToName", NULL, "name2=\"%s\"", xmlSecErrorsSafeString((char*)name2));
        xmlFree(name2);
        return(NULL);
    }

    xmlFree(name2);
    return(res);
}

static CERTCertificate*
xmlSecNssX509FindCert(CERTCertList* certsList, xmlSecNssX509FindCertCtxPtr findCertCtx) {
    CERTCertDBHandle * certDb;
    CERTCertificate * cert = NULL;
    int ret;

    /* certsList can be NULL */
    xmlSecAssert2(findCertCtx != NULL, NULL);

    /* try to search in our list - NSS doesn't update it's cache correctly
     * when new certs are added https://bugzilla.mozilla.org/show_bug.cgi?id=211051
     */
    if(certsList != NULL) {
        CERTCertListNode* curCertNode;

        for(curCertNode = CERT_LIST_HEAD(certsList);
            (cert == NULL) && !CERT_LIST_END(curCertNode, certsList) &&
            (curCertNode != NULL) && (curCertNode->cert != NULL);
            curCertNode = CERT_LIST_NEXT(curCertNode)
        ) {
            ret = xmlSecNssX509FindCertCtxMatch(findCertCtx, curCertNode->cert);
            if(ret < 0) {
                xmlSecInternalError("xmlSecNssX509FindCertCtxMatch", NULL);
                return(NULL);
            } else if(ret == 1) {
                cert = CERT_DupCertificate(curCertNode->cert);
                if(cert == NULL) {
                    xmlSecNssError("CERT_DupCertificate", NULL);
                    return(NULL);
                }
                return(cert);
            }
        }
    }

    /* search in the NSS DB */
    certDb = CERT_GetDefaultCertDB();
    if(certDb == NULL) {
        xmlSecNssError("CERT_GetDefaultCertDB(ski)", NULL);
        return(NULL);
    }

    /* search by subject name if available */
    if ((cert == NULL) && (findCertCtx->subjectNameItem != NULL)) {
        cert = CERT_FindCertByName(certDb, findCertCtx->subjectNameItem);
    }

    /* search by issuer name+serial if available */
    if((cert == NULL) && (findCertCtx->issuerAndSNInitialized == 1)) {
        cert = CERT_FindCertByIssuerAndSN(certDb, &(findCertCtx->issuerAndSN));
    }

    /* search by SKI if available */
    if((cert == NULL) && (findCertCtx->skiItem.data != NULL) && (findCertCtx->skiItem.len > 0)) {
        cert = CERT_FindCertBySubjectKeyID(certDb, &(findCertCtx->skiItem));
    }

    /* done */
    return(cert);
}

xmlSecKeyPtr
xmlSecNssX509FindKeyByValue(xmlSecPtrListPtr keysList, xmlSecKeyX509DataValuePtr x509Value) {
    xmlSecNssX509FindCertCtx findCertCtx;
    xmlSecSize keysListSize, ii;
    xmlSecKeyPtr res = NULL;
    int ret;

    xmlSecAssert2(keysList != NULL, NULL);
    xmlSecAssert2(x509Value != NULL, NULL);

    ret = xmlSecNssX509FindCertCtxInitializeFromValue(&findCertCtx, x509Value);
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509FindCertCtxInitializeFromValue", NULL);
        xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
        return(NULL);
    }

    keysListSize = xmlSecPtrListGetSize(keysList);
    for(ii = 0; ii < keysListSize; ++ii) {
        xmlSecKeyPtr key;
        xmlSecKeyDataPtr keyData;
        CERTCertificate* keyCert;

        /* get key's cert from x509 key data */
        key = (xmlSecKeyPtr)xmlSecPtrListGetItem(keysList, ii);
        if(key == NULL) {
            continue;
        }
        keyData = xmlSecKeyGetData(key, xmlSecNssKeyDataX509Id);
        if(keyData == NULL) {
            continue;
        }
        keyCert = xmlSecNssKeyDataX509GetKeyCert(keyData);
        if(keyCert == NULL) {
            continue;
        }

        /* does it match? */
        ret = xmlSecNssX509FindCertCtxMatch(&findCertCtx, keyCert);
        if(ret < 0) {
            xmlSecInternalError("xmlSecNssX509FindCertCtxMatch", NULL);
            xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
            return(NULL);
        } else if(ret == 1) {
            res = key;
            break;
        }
    }

    /* done */
    xmlSecNssX509FindCertCtxFinalize(&findCertCtx);
    return(res);
}

/******************************************************************************
 *
 * xmlSecNssX509FindCertCtx
 *
  *****************************************************************************/
SECOidTag
xmlSecNssX509GetDigestFromAlgorithm(const xmlChar* href) {
    /* use SHA256 by default */
    if(href == NULL) {
#ifndef XMLSEC_NO_SHA256
        return(SEC_OID_SHA256);
#else  /* XMLSEC_NO_SHA256 */
        xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL,
            "SHA256 is disabled; href=%s", xmlSecErrorsSafeString(href));
        return(SEC_OID_UNKNOWN);
#endif /* XMLSEC_NO_SHA256 */
    } else

#ifndef XMLSEC_NO_SHA1
    if(xmlStrcmp(href, xmlSecHrefSha1) == 0) {
        return(SEC_OID_SHA1);
    } else
#endif /* XMLSEC_NO_SHA1 */

#ifndef XMLSEC_NO_SHA224
    if(xmlStrcmp(href, xmlSecHrefSha224) == 0) {
        return(SEC_OID_SHA224);
    } else
#endif /* XMLSEC_NO_SHA224 */

#ifndef XMLSEC_NO_SHA256
    if(xmlStrcmp(href, xmlSecHrefSha256) == 0) {
        return(SEC_OID_SHA256);
    } else
#endif /* XMLSEC_NO_SHA256 */

#ifndef XMLSEC_NO_SHA384
    if(xmlStrcmp(href, xmlSecHrefSha384) == 0) {
        return(SEC_OID_SHA384);
    } else
#endif /* XMLSEC_NO_SHA384 */

#ifndef XMLSEC_NO_SHA512
    if(xmlStrcmp(href, xmlSecHrefSha512) == 0) {
        return(SEC_OID_SHA512);
    } else
#endif /* XMLSEC_NO_SHA512 */

    {
        xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL,
            "href=%s", xmlSecErrorsSafeString(href));
        return(SEC_OID_UNKNOWN);
    }
}


static int
xmlSecNssX509SerialNumberRead(const xmlChar *str, SECItem *res, PLArenaPool *arena) {
    xmlSecByte buf[XMLSEC_X509_MAX_SERIAL_NUMBER_BYTES];
    xmlSecSize written = 0;
    unsigned int uwritten;
    int ret;

    xmlSecAssert2(str != NULL, -1);
    xmlSecAssert2(res != NULL, -1);
    xmlSecAssert2(arena != NULL, -1);

    ret = xmlSecX509SerialNumberRead(str, buf, sizeof(buf), &written);
    if(ret < 0) {
        xmlSecInternalError("xmlSecX509SerialNumberRead", NULL);
        return(-1);
    }
    xmlSecAssert2(written > 0, -1);
    XMLSEC_SAFE_CAST_SIZE_TO_UINT(written, uwritten, return(-1), NULL);

    res->data = (unsigned char *)PORT_ArenaAlloc(arena, uwritten);
    if(res->data == NULL) {
        xmlSecNssError("PORT_ArenaAlloc", NULL);
        return(-1);
    }

    memcpy(res->data, buf, written);
    res->len = uwritten;
    res->type = siBuffer;
    return(0);
}

int xmlSecNssX509FindCertCtxInitialize(xmlSecNssX509FindCertCtxPtr ctx,
    const xmlChar *subjectName,
    const xmlChar *issuerName, const xmlChar *issuerSerial,
    xmlSecByte * ski, xmlSecSize skiSize
) {
    int ret;

    xmlSecAssert2(ctx != NULL, -1);

    memset(ctx, 0, sizeof(*ctx));

    /* ski (easy first) */
    if((ski != NULL) && (skiSize > 0)) {
        ctx->skiItem.type = siBuffer;
        ctx->skiItem.data = ski;
        XMLSEC_SAFE_CAST_SIZE_TO_UINT(skiSize, ctx->skiItem.len, return(-1), NULL);
    }

    ctx->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (ctx->arena == NULL) {
        xmlSecNssError("PORT_NewArena", NULL);
        xmlSecNssX509FindCertCtxFinalize(ctx);
        return(-1);
    }

    /* subject name */
    if(subjectName != NULL) {
        ctx->subjectName = xmlSecNssGetCertName(subjectName);
        if(ctx->subjectName == NULL) {
            xmlSecInternalError2("xmlSecNssGetCertName", NULL,
                "subjectName=%s", xmlSecErrorsSafeString(subjectName));
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
        ctx->subjectNameItem = SEC_ASN1EncodeItem(ctx->arena, NULL, (void *)ctx->subjectName , SEC_ASN1_GET(CERT_NameTemplate));
        if (ctx->subjectNameItem == NULL) {
            xmlSecNssError2("SEC_ASN1EncodeItem(subjectName)", NULL,
                "subjectName=%s", xmlSecErrorsSafeString(subjectName));
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
    }

    /* issuer name + serial  */
    if((issuerName != NULL) && (issuerSerial != NULL)) {
        memset(&ctx->issuerAndSN, 0, sizeof(ctx->issuerAndSN));

        ctx->issuerName = xmlSecNssGetCertName(issuerName);
        if(ctx->issuerName == NULL) {
            xmlSecInternalError2("xmlSecNssGetCertName", NULL,
                "issuerName=%s", xmlSecErrorsSafeString(issuerName));
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
        ctx->issuerNameItem = SEC_ASN1EncodeItem(ctx->arena, NULL, (void *)ctx->issuerName , SEC_ASN1_GET(CERT_NameTemplate));
        if (ctx->issuerNameItem == NULL) {
            xmlSecNssError2("SEC_ASN1EncodeItem(issuerName)", NULL,
                "issuerName=%s", xmlSecErrorsSafeString(issuerName));
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
        ctx->issuerAndSN.derIssuer.type = ctx->issuerNameItem->type;
        ctx->issuerAndSN.derIssuer.data = ctx->issuerNameItem->data;
        ctx->issuerAndSN.derIssuer.len  = ctx->issuerNameItem->len;

        ret = xmlSecNssX509SerialNumberRead(issuerSerial, &(ctx->issuerAndSN.serialNumber), ctx->arena);
        if(ret < 0) {
            xmlSecInternalError("xmlSecNssX509SerialNumberRead", NULL);
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
        ctx->issuerAndSNInitialized = 1;
    }

    /* done! */
    return(0);
}

int
xmlSecNssX509FindCertCtxInitializeFromValue(xmlSecNssX509FindCertCtxPtr ctx, xmlSecKeyX509DataValuePtr x509Value) {
    int ret;

    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(x509Value != NULL, -1);

    ret = xmlSecNssX509FindCertCtxInitialize(ctx,
                x509Value->subject,
                x509Value->issuerName, x509Value->issuerSerial,
                xmlSecBufferGetData(&(x509Value->ski)), xmlSecBufferGetSize(&(x509Value->ski))
    );
    if(ret < 0) {
        xmlSecInternalError("xmlSecNssX509FindCertCtxInitialize", NULL);
        xmlSecNssX509FindCertCtxFinalize(ctx);
        return(-1);
    }

    if((!xmlSecBufferIsEmpty(&(x509Value->digest))) && (x509Value->digestAlgorithm != NULL)) {
        xmlSecSize digestSize;

        ctx->digestValue = xmlSecBufferGetData(&(x509Value->digest));
        digestSize = xmlSecBufferGetSize(&(x509Value->digest));
        XMLSEC_SAFE_CAST_SIZE_TO_UINT(digestSize, ctx->digestLen, return(-1), NULL);

        ctx->digestAlg = xmlSecNssX509GetDigestFromAlgorithm(x509Value->digestAlgorithm);
        if(ctx->digestAlg == SEC_OID_UNKNOWN) {
            xmlSecInternalError("xmlSecNssX509GetDigestFromAlgorithm", NULL);
            xmlSecNssX509FindCertCtxFinalize(ctx);
            return(-1);
        }
    }

    return(0);
}

void
xmlSecNssX509FindCertCtxFinalize(xmlSecNssX509FindCertCtxPtr ctx) {
    xmlSecAssert(ctx != NULL);

    if(ctx->subjectName != NULL) {
        CERT_DestroyName(ctx->subjectName);
    }
    if(ctx->issuerName != NULL) {
        CERT_DestroyName(ctx->issuerName);
    }
    if (ctx->arena != NULL) {
        PORT_FreeArena(ctx->arena, PR_FALSE);
    }
    memset(ctx, 0, sizeof(*ctx));
}

/* returns 1 for match, 0 for no match, and a negative value if an error occurs */
int
xmlSecNssX509FindCertCtxMatch(xmlSecNssX509FindCertCtxPtr ctx, CERTCertificate* cert) {
    SECStatus status;

    xmlSecAssert2(ctx != NULL, -1);
    xmlSecAssert2(cert != NULL, -1);


    /* subject name */
    if(ctx->subjectNameItem != NULL) {
        if (SECITEM_ItemsAreEqual(&(cert->derSubject), ctx->subjectNameItem)) {
            /* found a match */
            return(1);
        } else {
            /* no match */
            return(0);
        }
    }

    /* issuer name + serial */
    if(ctx->issuerAndSNInitialized != 0) {
        if (
            SECITEM_ItemsAreEqual(&(cert->derIssuer),  &(ctx->issuerAndSN.derIssuer)) &&
            SECITEM_ItemsAreEqual(&(cert->serialNumber),  &(ctx->issuerAndSN.serialNumber))
        ) {
            /* found a match */
            return(1);
        } else {
            /* no match */
            return(0);
        }
    }

    /* ski */
    if( (ctx->skiItem.data != NULL) && (ctx->skiItem.len > 0)) {
        SECItem tmpitem = { siBuffer, NULL, 0 };

        memset(&tmpitem, 0, sizeof(tmpitem));
        status = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
        if (status != SECSuccess)  {
            xmlSecNssError("CERT_FindSubjectKeyIDExtension(ski)", NULL);
            return(-1);
        }

        if((tmpitem.len != ctx->skiItem.len) || (memcmp(tmpitem.data, ctx->skiItem.data, ctx->skiItem.len) != 0)) {
            /* no match */
            SECITEM_FreeItem(&tmpitem, PR_FALSE);
            return(0);
        }
        SECITEM_FreeItem(&tmpitem, PR_FALSE);

        /* found a match */
        return(1);
    }

    /* cert digest */
    if(
         (ctx->digestAlg != SEC_OID_UNKNOWN) && (ctx->digestValue != NULL) && (ctx->digestLen > 0) &&
        (cert->derCert.type == siBuffer) && (cert->derCert.data != NULL) && (cert->derCert.len > 0)
    ) {
        xmlSecByte digest[XMLSEC_NSS_MAX_DIGEST_SIZE];
        unsigned int digestLen;

        digestLen = HASH_ResultLenByOidTag(ctx->digestAlg);
        if((digestLen == 0) || (digestLen > sizeof(digest))) {
            xmlSecNssError3("HASH_ResultLenByOidTag", NULL,
                "digestAlgOid=%d; len=%u", (int)ctx->digestAlg, digestLen);
            return(-1);
        }
        status = PK11_HashBuf(ctx->digestAlg, digest, cert->derCert.data, (PRInt32)cert->derCert.len);
        if (status != SECSuccess) {
            xmlSecNssError2("PK11_HashBuf(cert->derCert)", NULL,
                "digestAlgOid=%d", (int)ctx->digestAlg);
            return(-1);
        }

        if((digestLen != ctx->digestLen) || (memcmp(digest, ctx->digestValue, ctx->digestLen) != 0)) {
            /* no match */
            return(0);
        }

        /* found a match */
        return(1);
    }


    return(0);
}

#endif /* XMLSEC_NO_X509 */
