/* ssl_certman.c
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

#include <wolfssl/internal.h>

#if !defined(WOLFSSL_SSL_CERTMAN_INCLUDED)
    #ifndef WOLFSSL_IGNORE_FILE_WARN
        #warning ssl_certman.c not to be compiled separately from ssl.c
    #endif
#else

#ifndef NO_CERTS

/* Pick an available TLS method.
 *
 * Used when creating temporary WOLFSSL_CTX.
 *
 * @return  A TLS method on success.
 * @return  NULL when no TLS method built into wolfSSL.
 */
static WC_INLINE WOLFSSL_METHOD* cm_pick_method(void* heap)
{
    (void)heap;
    #ifndef NO_WOLFSSL_CLIENT
        #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_SSLV3)
            return wolfSSLv3_client_method_ex(heap);
        #elif !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10)
            return wolfTLSv1_client_method_ex(heap);
        #elif !defined(NO_OLD_TLS)
            return wolfTLSv1_1_client_method_ex(heap);
        #elif !defined(WOLFSSL_NO_TLS12)
            return wolfTLSv1_2_client_method_ex(heap);
        #elif defined(WOLFSSL_TLS13)
            return wolfTLSv1_3_client_method_ex(heap);
        #else
            return NULL;
        #endif
    #elif !defined(NO_WOLFSSL_SERVER)
        #if !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_SSLV3)
            return wolfSSLv3_server_method_ex(heap);
        #elif !defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLSV10)
            return wolfTLSv1_server_method_ex(heap);
        #elif !defined(NO_OLD_TLS)
            return wolfTLSv1_1_server_method_ex(heap);
        #elif !defined(WOLFSSL_NO_TLS12)
            return wolfTLSv1_2_server_method_ex(heap);
        #elif defined(WOLFSSL_TLS13)
            return wolfTLSv1_3_server_method_ex(heap);
        #else
            return NULL;
        #endif
    #else
        return NULL;
    #endif
}

/* Create a new certificate manager with a heap hint.
 *
 * @param [in] heap  Heap hint.
 * @return  Certificate manager object on success.
 * @return  NULL on failure.
 */
WOLFSSL_CERT_MANAGER* wolfSSL_CertManagerNew_ex(void* heap)
{
    int err = 0;
    WOLFSSL_CERT_MANAGER* cm;

    WOLFSSL_ENTER("wolfSSL_CertManagerNew");
    if (heap == NULL) {
         WOLFSSL_MSG("heap param is null");
    }
    else {
        /* Some systems may have heap in unexpected segments. (IRAM vs DRAM) */
        WOLFSSL_MSG_EX("heap param = %p", heap);
    }
    WOLFSSL_MSG_EX("DYNAMIC_TYPE_CERT_MANAGER Allocating = %d bytes",
                    (word32)sizeof(WOLFSSL_CERT_MANAGER));

    /* Allocate memory for certificate manager. */
    cm = (WOLFSSL_CERT_MANAGER*)XMALLOC(sizeof(WOLFSSL_CERT_MANAGER), heap,
        DYNAMIC_TYPE_CERT_MANAGER);
    if (cm == NULL) {
        WOLFSSL_MSG_EX("XMALLOC failed to allocate WOLFSSL_CERT_MANAGER %d "
                    "bytes.", (int)sizeof(WOLFSSL_CERT_MANAGER));
        err = 1;
    }
    if (!err) {
        /* Reset all fields. */
        XMEMSET(cm, 0, sizeof(WOLFSSL_CERT_MANAGER));

        /* Create a mutex for use when modify table of stored CAs. */
        if (wc_InitMutex(&cm->caLock) != 0) {
            WOLFSSL_MSG("Bad mutex init");
            err = 1;
        }
    }
    if (!err) {
        /* Initialize reference count. */
        wolfSSL_RefInit(&cm->ref, &err);
    #ifdef WOLFSSL_REFCNT_ERROR_RETURN
        if (err != 0) {
            WOLFSSL_MSG("Bad reference count init");
        }
    #endif
    }
#ifdef WOLFSSL_TRUST_PEER_CERT
    /* Create a mutex for use when modify table of trusted peers. */
    if ((!err) && (wc_InitMutex(&cm->tpLock) != 0)) {
        WOLFSSL_MSG("Bad mutex init");
        err = 1;
    }
#endif
    if (!err) {
        /* Set default minimum key sizes allowed. */
    #ifndef NO_RSA
        cm->minRsaKeySz = MIN_RSAKEY_SZ;
    #endif
    #ifdef HAVE_ECC
        cm->minEccKeySz = MIN_ECCKEY_SZ;
    #endif
    #ifdef HAVE_FALCON
        cm->minFalconKeySz = MIN_FALCONKEY_SZ;
    #endif /* HAVE_FALCON */
    #ifdef HAVE_DILITHIUM
        cm->minDilithiumKeySz = MIN_DILITHIUMKEY_SZ;
    #endif /* HAVE_DILITHIUM */

        /* Set heap hint to use in certificate manager operations. */
        cm->heap = heap;
    }

    /* Dispose of certificate manager on error. */
    if (err && (cm != NULL)) {
        wolfSSL_CertManagerFree(cm);
        cm = NULL;
    }
    return cm;
}

/* Create a new certificate manager.
 *
 * @return  Certificate manager object on success.
 * @return  NULL on failure.
 */
WOLFSSL_CERT_MANAGER* wolfSSL_CertManagerNew(void)
{
    /* No heap hint. */
    return wolfSSL_CertManagerNew_ex(NULL);
}

/* Dispose of certificate manager.
 *
 * @param [in, out] cm  Certificate manager.
 */
void wolfSSL_CertManagerFree(WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_ENTER("wolfSSL_CertManagerFree");

    /* Validate parameter. */
    if (cm != NULL) {
        int doFree = 0;
        int ret;

        /* Decrement reference count and check if value is 0. */
        wolfSSL_RefDec(&cm->ref, &doFree, &ret);
    #ifdef WOLFSSL_REFCNT_ERROR_RETURN
        if (ret != 0) {
            WOLFSSL_MSG("Couldn't lock cm mutex");
        }
    #else
        (void)ret;
    #endif
        if (doFree) {
        #ifdef HAVE_CRL
            /* Dispose of CRL handler. */
            if (cm->crl != NULL) {
                /* Dispose of CRL object - indicating dynamically allocated. */
                FreeCRL(cm->crl, 1);
            }
        #endif

    #ifdef HAVE_OCSP
            /* Dispose of OCSP handler. */
            if (cm->ocsp != NULL) {
                FreeOCSP(cm->ocsp, 1);
            }
            /* Dispose of URL. */
            XFREE(cm->ocspOverrideURL, cm->heap, DYNAMIC_TYPE_URL);
        #if !defined(NO_WOLFSSL_SERVER) && \
            (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
             defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2))
            /* Dispose of OCSP stapling handler. */
            if (cm->ocsp_stapling) {
                FreeOCSP(cm->ocsp_stapling, 1);
            }
        #endif
    #endif /* HAVE_OCSP */

            /* Dispose of CA table and mutex. */
            FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap);
            wc_FreeMutex(&cm->caLock);

        #ifdef WOLFSSL_TRUST_PEER_CERT
            /* Dispose of trusted peer table and mutex. */
            FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, cm->heap);
            wc_FreeMutex(&cm->tpLock);
        #endif

            /* Dispose of reference count. */
            wolfSSL_RefFree(&cm->ref);
            /* Dispose of certificate manager memory. */
            XFREE(cm, cm->heap, DYNAMIC_TYPE_CERT_MANAGER);
        }
    }
}

/* Increase reference count on certificate manager.
 *
 * @param [in, out] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  0 when cm is NULL or locking mutex fails.
 */
int wolfSSL_CertManager_up_ref(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    /* Validate parameter. */
    if (cm == NULL) {
        ret = 0;
    }
    if (ret == WOLFSSL_SUCCESS) {
        int err;

        /* Increment reference. */
        wolfSSL_RefInc(&cm->ref, &err);
    #ifdef WOLFSSL_REFCNT_ERROR_RETURN
        if (err) {
            WOLFSSL_MSG("Failed to lock cm mutex");
            ret = 0;
        }
    #else
        (void)err;
    #endif
    }

    return ret;
}

#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM)
#if defined(WOLFSSL_SIGNER_DER_CERT)
static WC_INLINE int wolfssl_cm_get_certs_der(WOLFSSL_CERT_MANAGER* cm,
    DerBuffer*** buffers, int* cnt)
{
    int err = 0;
    Signer* signers = NULL;
    DerBuffer** certBuffers = NULL;
    int i = 0;
    word32 row = 0;
    int numCerts = 0;

    /* Iterate once to get the number of certs, for memory allocation
     * purposes. */
    for (row = 0; row < CA_TABLE_SIZE; row++) {
        /* Get signer information of CAs in a row. */
        signers = cm->caTable[row];
        /* Count each signer in row that has a DER certificate buffer. */
        while ((signers != NULL) && (signers->derCert != NULL) &&
                (signers->derCert->buffer != NULL)) {
            ++numCerts;
            signers = signers->next;
        }
    }
    /* Check we found certificates. */
    if (numCerts == 0) {
        err = 1;
    }

    if (!err) {
        /* Allocate memory for pointers to each DER buffer. */
        certBuffers = (DerBuffer**)XMALLOC(
            sizeof(DerBuffer*) * (size_t)numCerts, cm->heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (certBuffers == NULL) {
            err = 1;
        }
    }
    if (!err) {
        /* Reset pointers. */
        XMEMSET(certBuffers, 0, sizeof(DerBuffer*) * (size_t)numCerts);
    }

    /* Copy the certs locally so that we can release the caLock. If the lock
     * is held when wolfSSL_d2i_X509 is called, GetCA will also try to get
     * the lock, leading to deadlock. */
    for (row = 0; (!err) && (row < CA_TABLE_SIZE); row++) {
        /* Get signer information of CAs in a row. */
        signers = cm->caTable[row];
        /* Copy each DER certificate buffer of signers in a row. */
        while ((signers != NULL) && (signers->derCert != NULL) &&
                (signers->derCert->buffer != NULL)) {
            /* Allocate memory to hold DER certificate buffer. */
            int ret = AllocDer(&certBuffers[i], signers->derCert->length,
                CA_TYPE, cm->heap);
            if (ret < 0) {
                err = 1;
                break;
            }

            /* Copy buffer into array element. */
            XMEMCPY(certBuffers[i]->buffer, signers->derCert->buffer,
                signers->derCert->length);
            certBuffers[i]->length = signers->derCert->length;

            /* Store in next index. */
            ++i;
            /* Move on to next signer in row. */
            signers = signers->next;
        }
    }

    *buffers = certBuffers;
    *cnt = numCerts;
    return err;
}

/* Retrieve stack of X509 certificates in a certificate manager (CM).
 *
 * @param [in] cm  Certificate manager.
 *
 * @return  Stack of X509 certs on success
 * @return  NULL on failure.
 */
WOLFSSL_STACK* wolfSSL_CertManagerGetCerts(WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_STACK* sk = NULL;
    int numCerts = 0;
    DerBuffer** certBuffers = NULL;
    int i = 0;
    int err = 0;

    WOLFSSL_ENTER("wolfSSL_CertManagerGetCerts");

    /* Validate parameter. */
    if (cm == NULL) {
        err = 1;
    }
    if (!err) {
        /* Create an empty certificate stack to return. */
        sk = wolfSSL_sk_X509_new_null();
        if (sk == NULL) {
            err = 1;
        }
    }
    /* Lock CA table. */
    if ((!err) && (wc_LockMutex(&cm->caLock) != 0)) {
        err = 1;
    }
    if (!err) {
        err = wolfssl_cm_get_certs_der(cm, &certBuffers, &numCerts);
        /* Release CA lock. */
        wc_UnLockMutex(&cm->caLock);
    }

    /* Put each DER certificate buffer into a stack of WOLFSSL_X509 */
    for (i = 0; (!err) && (i < numCerts); ++i) {
        const byte* derBuffer = NULL;
        WOLFSSL_X509* x509 = NULL;

        /* Get pointer to DER encoding of certificate. */
        derBuffer = certBuffers[i]->buffer;
        /* Decode certificate. */
        wolfSSL_d2i_X509(&x509, &derBuffer, (int)certBuffers[i]->length);
        if (x509 == NULL) {
            err = 1;
        }

        /* Decode certificate. */
        if ((!err) && (wolfSSL_sk_X509_push(sk, x509) <= 0)) {
            wolfSSL_X509_free(x509);
            x509 = NULL;
            err = 1;
        }
    }

    if (certBuffers != NULL) {
        /* Dispose of temporary cert storage (for access outside of lock). */
        for (i = 0; i < numCerts && certBuffers[i] != NULL; ++i) {
            FreeDer(&certBuffers[i]);
        }
        XFREE(certBuffers, cm->heap, DYNAMIC_TYPE_TMP_BUFFER);
    }

    /* Dispose of stack of certificates on error. */
    if (err && (sk != NULL)) {
        wolfSSL_sk_X509_pop_free(sk, NULL);
        sk = NULL;
    }
    return sk;
}

#endif /* WOLFSSL_SIGNER_DER_CERT */
#endif /* OPENSSL_EXTRA && !NO_FILESYSTEM */

/* Unload the CA signer table.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  BAD_MUTEX_E when locking fails.
 */
int wolfSSL_CertManagerUnloadCAs(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerUnloadCAs");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    /* Lock CA table. */
    if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) {
        ret = BAD_MUTEX_E;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Dispose of CA table. */
        FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap);

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    return ret;
}

int wolfSSL_CertManagerUnloadTypeCerts(
                                WOLFSSL_CERT_MANAGER* cm, byte type)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerUnloadTypeCerts");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    /* Lock CA table. */
    if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) {
        ret = BAD_MUTEX_E;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Dispose of CA table. */
        FreeSignerTableType(cm->caTable, CA_TABLE_SIZE, type,
                cm->heap);

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    return ret;
}

#if defined(OPENSSL_EXTRA)
static int wolfSSL_CertManagerUnloadTempIntermediateCerts(
    WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_ENTER("wolfSSL_CertManagerUnloadTempIntermediateCerts");
    return wolfSSL_CertManagerUnloadTypeCerts(cm, WOLFSSL_TEMP_CA);
}
#endif

int wolfSSL_CertManagerUnloadIntermediateCerts(
    WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_ENTER("wolfSSL_CertManagerUnloadIntermediateCerts");
    return wolfSSL_CertManagerUnloadTypeCerts(cm, WOLFSSL_CHAIN_CA);
}

#ifdef WOLFSSL_TRUST_PEER_CERT
/* Unload the trusted peers table.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  BAD_MUTEX_E when locking fails.
 */
int wolfSSL_CertManagerUnload_trust_peers(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerUnload_trust_peers");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    /* Lock trusted peers table. */
    if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->tpLock) != 0)) {
        ret = BAD_MUTEX_E;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Dispose of trusted peers table. */
        FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, cm->heap);

        /* Unlock trusted peers table. */
        wc_UnLockMutex(&cm->tpLock);
    }

    return ret;
}
#endif /* WOLFSSL_TRUST_PEER_CERT */

/* Load certificate/s from buffer with flags and type.
 *
 * @param [in] cm         Certificate manager.
 * @param [in] buff       Buffer holding encoding of certificate.
 * @param [in] sz         Length in bytes of data in buffer.
 * @param [in] format     Format of encoding. Valid values:
 *                          WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] userChain  Indicates buffer holds chain of certificates.
 * @param [in] flags      Flags to modify behaviour of loading. Valid flags:
 *                          WOLFSSL_LOAD_FLAG_IGNORE_ERR,
 *                          WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY,
 *                          WOLFSSL_LOAD_FLAG_PEM_CA_ONLY,
 *                          WOLFSSL_LOAD_FLAG_IGNORE_BAD_PATH_ERR, and
 *                          WOLFSSL_LOAD_FLAG_IGNORE_ZEROFILE.
 * @param [in] type       The CA cert's type, used in the internal CA
                            table.  Defaults to WOLFSSL_USER_CA, passing
                            in WOLFSSL_USER_CA = noop.  Recommended to
                            set to WOLFSSL_USER_INTER when loading
                            intermediate certs to allow unloading via
                            wolfSSL_CertManagerUnloadTypeCerts.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX.
 * @return  Other values on loading failure.
 */
int wolfSSL_CertManagerLoadCABufferType(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* buff, long sz, int format, int userChain,
    word32 flags, int type)
{
    int ret = WOLFSSL_SUCCESS;
    WOLFSSL_CTX* tmp = NULL;
    DecodedCert* dCert = NULL;
    DerBuffer* der = NULL;

    WOLFSSL_ENTER("wolfSSL_CertManagerLoadCABufferType");

    /* Validate parameters. */
    if (cm == NULL) {
        WOLFSSL_MSG("No CertManager error");
        ret = WOLFSSL_FATAL_ERROR;
    }
    /* Allocate a temporary WOLFSSL_CTX to load with. */
    if ((ret == WOLFSSL_SUCCESS) && ((tmp =
        wolfSSL_CTX_new_ex(cm_pick_method(cm->heap), cm->heap)) == NULL)) {
        WOLFSSL_MSG("CTX new failed");
        ret = WOLFSSL_FATAL_ERROR;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Some configurations like OPENSSL_COMPATIBLE_DEFAULTS may turn off
         * verification by default. Let's restore our desired defaults. */
        wolfSSL_CTX_set_verify(tmp, WOLFSSL_VERIFY_DEFAULT, NULL);

        /* Replace certificate manager with one to load certificate/s into. */
        wolfSSL_CertManagerFree(tmp->cm);
        tmp->cm = cm;

        /* Load certificate buffer. */
        ret = wolfSSL_CTX_load_verify_buffer_ex(tmp, buff, sz, format,
            userChain, flags);

        /* Clear certificate manager in WOLFSSL_CTX so it won't be freed. */
        tmp->cm = NULL;
    }
    if (ret == WOLFSSL_SUCCESS && type != WOLFSSL_USER_CA) {
        dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), cm->heap,
                                                DYNAMIC_TYPE_DCERT);

        if (dCert == NULL) {
            ret = WOLFSSL_FATAL_ERROR;
        } else {
            if (format == WOLFSSL_FILETYPE_PEM) {
            #ifndef WOLFSSL_PEM_TO_DER
                ret = NOT_COMPILED_IN;
            #else
                ret = PemToDer(buff, sz, CERT_TYPE, &der, cm->heap, NULL, NULL);
                if (!ret) {
                    /* Replace buffer pointer and size with DER buffer. */
                    buff = der->buffer;
                    sz = (long)der->length;
                    ret = WOLFSSL_SUCCESS;
                } else {
                    WOLFSSL_ERROR(ret);
                    ret = WOLFSSL_FATAL_ERROR;
                }
            #endif
            }

            if (ret == WOLFSSL_SUCCESS) {
                XMEMSET(dCert, 0, sizeof(DecodedCert));
                wc_InitDecodedCert(dCert, buff,
                                (word32)sz, cm->heap);
                ret = wc_ParseCert(dCert, CERT_TYPE, NO_VERIFY, NULL);
                if (ret) {
                    ret = WOLFSSL_FATAL_ERROR;
                } else {
                    ret = SetCAType(cm, dCert->extSubjKeyId, type);
                }
            }

            if (dCert) {
                wc_FreeDecodedCert(dCert);
                XFREE(dCert, cm->heap, DYNAMIC_TYPE_DCERT);
            }
            if (der) {
                FreeDer(&der);
            }
        }
    }

    /* Dispose of temporary WOLFSSL_CTX. */
    wolfSSL_CTX_free(tmp);
    return ret;

}

/* Load certificate/s from buffer with flags.
 *
 * @param [in] cm         Certificate manager.
 * @param [in] buff       Buffer holding encoding of certificate.
 * @param [in] sz         Length in bytes of data in buffer.
 * @param [in] format     Format of encoding. Valid values:
 *                          WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] userChain  Indicates buffer holds chain of certificates.
 * @param [in] flags      Flags to modify behaviour of loading. Valid flags:
 *                          WOLFSSL_LOAD_FLAG_IGNORE_ERR,
 *                          WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY,
 *                          WOLFSSL_LOAD_FLAG_PEM_CA_ONLY,
 *                          WOLFSSL_LOAD_FLAG_IGNORE_BAD_PATH_ERR, and
 *                          WOLFSSL_LOAD_FLAG_IGNORE_ZEROFILE.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX.
 * @return  Other values on loading failure.
 */
int wolfSSL_CertManagerLoadCABuffer_ex(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* buff, long sz, int format, int userChain, word32 flags)
{
    return wolfSSL_CertManagerLoadCABufferType(cm, buff, sz, format, userChain,
        flags, WOLFSSL_USER_CA);
}

/* Load certificate/s from buffer into table.
 *
 * Uses default load verification flags and is not a user chain.
 *
 * @param [in] cm         Certificate manager.
 * @param [in] buff       Buffer holding encoding of certificate.
 * @param [in] sz         Length in bytes of data in buffer.
 * @param [in] format     Format of encoding. Valid values:
 *                          WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_FATAL_ERROR when cm is NULL or failed create WOLFSSL_CTX.
 * @return  Other values on loading failure.
 */
int wolfSSL_CertManagerLoadCABuffer(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* buff, long sz, int format)
{
    return wolfSSL_CertManagerLoadCABuffer_ex(cm, buff, sz, format, 0,
        WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS);
}

#ifndef NO_WOLFSSL_CM_VERIFY
/* Set the verification callback into certificate manager.
 *
 * @param [in] cm  Certificate manager.
 * @param [in] vc  Verification callback.
 */
void wolfSSL_CertManagerSetVerify(WOLFSSL_CERT_MANAGER* cm, VerifyCallback vc)
{
    WOLFSSL_ENTER("wolfSSL_CertManagerSetVerify");
    if (cm != NULL) {
        cm->verifyCallback = vc;
    }
}
#endif /* !NO_WOLFSSL_CM_VERIFY */

#ifdef WC_ASN_UNKNOWN_EXT_CB
void wolfSSL_CertManagerSetUnknownExtCallback(WOLFSSL_CERT_MANAGER* cm,
        wc_UnknownExtCallback cb)
{
    WOLFSSL_ENTER("wolfSSL_CertManagerSetUnknownExtCallback");
    if (cm != NULL) {
        cm->unknownExtCallback = cb;
    }

}
#endif /* WC_ASN_UNKNOWN_EXT_CB */

#if (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) || \
    defined(OPENSSL_EXTRA)
/* Verify the certificate.
 *
 * Uses the verification callback if available.
 *
 * @param [in] cm        Certificate manager.
 * @param [in] buff      Buffer holding encoded certificate.
 * @param [in] sz        Size in bytes of data in buffer.
 * @param [in] format    Format of encoding. Valid values:
 *                         WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] prev_err  Previous error. Passed to callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when converting from PEM to DER is not a feature of
 *          the wolfSSL build.
 */
int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff,
    long sz, int format, int prev_err)
{
    int ret = 0;
    int fatal = 0;
    DerBuffer* der = NULL;
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

    WOLFSSL_ENTER("CM_VerifyBuffer_ex");

    (void)prev_err;

    /* Allocate memory for decoded certificate. */
    WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT,
    {
        ret=MEMORY_E;
        fatal=1;
    });
    if (WC_VAR_OK(cert))
    {
        /* Reset fields of decoded certificate. */
        XMEMSET(cert, 0, sizeof(DecodedCert));

        if (format == WOLFSSL_FILETYPE_PEM) {
        #ifndef WOLFSSL_PEM_TO_DER
            ret = NOT_COMPILED_IN;
            fatal = 1;
        #else
            /* Convert to DER from PEM. */
            ret = PemToDer(buff, sz, CERT_TYPE, &der, cm->heap, NULL, NULL);
            if (ret != 0) {
                fatal = 1;
            }
            else {
                /* Replace buffer pointer and size with DER buffer. */
                buff = der->buffer;
                sz = (long)der->length;
            }
        #endif
        }
    }
    if (ret == 0) {
        /* Create a decoded certificate with DER buffer. */
        InitDecodedCert(cert, buff, (word32)sz, cm->heap);

#ifdef WC_ASN_UNKNOWN_EXT_CB
        if (cm->unknownExtCallback != NULL)
            wc_SetUnknownExtCallback(cert, cm->unknownExtCallback);
#endif

        /* Parse DER into decoded certificate fields and verify signature
         * against a known CA. */
        ret = ParseCertRelative(cert, CERT_TYPE, VERIFY, cm, NULL);
     }

#ifdef HAVE_CRL
    if ((ret == 0) && cm->crlEnabled) {
        /* Check for a CRL for the CA and check validity of certificate. */
        ret = CheckCertCRL(cm->crl, cert);
    }
#endif

    (void)fatal;

#if !defined(NO_WOLFSSL_CM_VERIFY) && \
    (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
    /* Use callback to perform verification too if available. */
    if ((!fatal) && cm->verifyCallback) {
        WC_DECLARE_VAR(args, ProcPeerCertArgs, 1, 0);
        buffer certBuf;

    #ifdef WOLFSSL_SMALL_STACK
        /* Allocate memory for object to hold arguments for callback. */
        args = (ProcPeerCertArgs*)XMALLOC(sizeof(ProcPeerCertArgs), cm->heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (args == NULL) {
            ret = MEMORY_E;
            fatal = 1;
        }
        if (!fatal)
    #endif
        {
            XMEMSET(args, 0, sizeof(ProcPeerCertArgs));

            /* DER encoding. */
            certBuf.buffer = (byte*)buff;
            certBuf.length = (unsigned int)sz;

            /* One certificate available. */
            args->totalCerts = 1;
            args->certs = &certBuf;
            args->dCert = cert;
            args->dCertInit = 1;

            /* Replace value in ret with an error value passed in. */
            if (prev_err != 0) {
                ret = prev_err;
            }
            /* Use callback to verify certificate. */
            ret = DoVerifyCallback(cm, NULL, ret, args);
        }
        WC_FREE_VAR_EX(args, cm->heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif

    /* Dispose of allocated memory. */
    FreeDecodedCert(cert);
    FreeDer(&der);
    WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT);

    /* Convert the ret value to a return value. */
    return (ret == 0) ? WOLFSSL_SUCCESS : ret;
}

/* Verify the certificate.
 *
 * Uses the verification callback if available.
 *
 * @param [in] cm        Certificate manager.
 * @param [in] buff      Buffer holding encoded certificate.
 * @param [in] sz        Size in bytes of data in buffer.
 * @param [in] format    Format of encoding. Valid values:
 *                         WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] prev_err  Previous error. Passed to callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or buff is NULL or sz is negative or zero.
 * @return  WOLFSSL_BAD_FILETYPE when format is invalid.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when converting from PEM to DER is not a feature of
 *          the wolfSSL build.
 */
int wolfSSL_CertManagerVerifyBuffer(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* buff, long sz, int format)
{
    int ret;

    WOLFSSL_ENTER("wolfSSL_CertManagerVerifyBuffer");

    /* Validate parameters. */
    if ((cm == NULL) || (buff == NULL) || (sz <= 0)) {
        ret = BAD_FUNC_ARG;
    }
    else if ((format != WOLFSSL_FILETYPE_ASN1) &&
             (format != WOLFSSL_FILETYPE_PEM)) {
        ret = WOLFSSL_BAD_FILETYPE;
    }
    else {
        /* No previous error. */
        ret = CM_VerifyBuffer_ex(cm, buff, sz, format, 0);
    }

    return ret;
}
#endif /* (!NO_WOLFSSL_CLIENT || !WOLFSSL_NO_CLIENT_AUTH) || OPENSSL_EXTRA */

#ifndef NO_FILESYSTEM

#if (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) || \
    defined(OPENSSL_EXTRA)
/* Verify the certificate loaded from a file.
 *
 * Uses the verification callback if available.
 *
 * @param [in] cm        Certificate manager.
 * @param [in] format    Format of encoding. Valid values:
 *                         WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] prev_err  Previous error. Passed to callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or buff is NULL or sz is negative.
 * @return  WOLFSSL_BAD_FILETYPE when format is invalid.
 * @return  WOLFSSL_BAD_FILE when reading the certificate file fails.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when converting from PEM to DER is not a feature of
 *          the wolfSSL build.
 */
int wolfSSL_CertManagerVerify(WOLFSSL_CERT_MANAGER* cm, const char* fname,
    int format)
{
    int    ret = WOLFSSL_SUCCESS;
#ifndef WOLFSSL_SMALL_STACK
    byte   staticBuffer[FILE_BUFFER_SIZE];
#endif
    byte*  buff = NULL;
    long   sz = 0;
    XFILE  file = XBADFILE;

    WOLFSSL_ENTER("wolfSSL_CertManagerVerify");

#ifndef WOLFSSL_SMALL_STACK
    buff = staticBuffer;
#endif

    /* Validate parameters. cm and format validated in:
     *   wolfSSL_CertManagerVerifyBuffer */
    if ((cm == NULL) || (fname == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    /* Open the file containing a certificate. */
    if ((ret == WOLFSSL_SUCCESS) &&
            ((file = XFOPEN(fname, "rb")) == XBADFILE)) {
        ret = WOLFSSL_BAD_FILE;
    }
    /* Get the length of the file. */
    if (ret == WOLFSSL_SUCCESS) {
        ret = wolfssl_file_len(file, &sz);
        if (ret == 0) {
            ret = WOLFSSL_SUCCESS;
        }
    }
    /* Allocate dynamic memory for file contents if no static buffer or too
     * small. */
#ifndef WOLFSSL_SMALL_STACK
    if ((ret == WOLFSSL_SUCCESS) && (sz > (long)sizeof(staticBuffer)))
#else

    if (ret == WOLFSSL_SUCCESS)
#endif
    {
        WOLFSSL_MSG("Getting dynamic buffer");
        buff = (byte*)XMALLOC((size_t)sz, cm->heap, DYNAMIC_TYPE_FILE);
        if (buff == NULL) {
            ret = WOLFSSL_BAD_FILE;
        }
    }
    /* Read all the file into buffer. */
    if ((ret == WOLFSSL_SUCCESS) && (XFREAD(buff, 1, (size_t)sz, file) !=
            (size_t)sz)) {
        ret = WOLFSSL_BAD_FILE;
    }
    /* Close file if opened. */
    if (file != XBADFILE) {
        XFCLOSE(file);
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Verify the certificate read. */
        ret = wolfSSL_CertManagerVerifyBuffer(cm, buff, sz, format);
    }

    /* Dispose of buffer if it was allocated. */
#ifndef WOLFSSL_SMALL_STACK
    if (buff != staticBuffer)
#endif
    {
        if (cm != NULL) {
            XFREE(buff, cm->heap, DYNAMIC_TYPE_FILE);
        }
    }
    return ret;
}
#endif

/* Load the CA file and/or certificate files in a path.
 *
 * @param [in] cm    Certificate manager.
 * @param [in] file  Name of CA file.
 * @param [in] path  Path to a directory containing certificates.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_FATAL_ERROR when cm is NULL or unable to create WOLFSSL_CTX.
 * @return  Otherwise failure.
 */
int wolfSSL_CertManagerLoadCA(WOLFSSL_CERT_MANAGER* cm, const char* file,
                             const char* path)
{
    int ret = WOLFSSL_SUCCESS;
    WOLFSSL_CTX* tmp = NULL;

    WOLFSSL_ENTER("wolfSSL_CertManagerLoadCA");

    /* Validate parameters. file and path validated in:
     *   wolfSSL_CTX_load_verify_locations*/
    if (cm == NULL) {
        WOLFSSL_MSG("No CertManager error");
        ret = WOLFSSL_FATAL_ERROR;
    }
    /* Create temporary WOLFSSL_CTX. */
    if ((ret == WOLFSSL_SUCCESS) && ((tmp =
        wolfSSL_CTX_new_ex(cm_pick_method(cm->heap), cm->heap)) == NULL)) {
        WOLFSSL_MSG("CTX new failed");
        ret = WOLFSSL_FATAL_ERROR;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Some configurations like OPENSSL_COMPATIBLE_DEFAULTS may turn off
         * verification by default. Let's restore our desired defaults. */
        wolfSSL_CTX_set_verify(tmp, WOLFSSL_VERIFY_DEFAULT, NULL);

        /* Replace certificate manager with one to load certificate/s into. */
        wolfSSL_CertManagerFree(tmp->cm);
        tmp->cm = cm;

        /* Load certificate from file and path. */
        ret = wolfSSL_CTX_load_verify_locations(tmp, file, path);

        /* Clear certificate manager in WOLFSSL_CTX so it won't be freed. */
        tmp->cm = NULL;
    }

    /* Dispose of temporary WOLFSSL_CTX. */
    wolfSSL_CTX_free(tmp);
    return ret;
}

#endif /* NO_FILESYSTEM */

#if defined(PERSIST_CERT_CACHE)

/* Version of layout of cache of CA certificates. */
#define WOLFSSL_CACHE_CERT_VERSION 1

/* CA certificates cache information. */
typedef struct {
    /* Cache certificate layout version id. */
    int version;
    /* Number of hash table rows. Maximum of CA_TABLE_SIZE. */
    int rows;
    /* Number of columns per row. */
    int columns[CA_TABLE_SIZE];
    /* Size of Signer object. */
    int signerSz;
} CertCacheHeader;

/* current cert persistence layout is:

   1) CertCacheHeader
   2) caTable

   update WOLFSSL_CERT_CACHE_VERSION if change layout for the following
   PERSIST_CERT_CACHE functions
*/


/* Return number of bytes of memory needed to persist this signer.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] Signer  Signer entry in CA table.
 * @return  Number of bytes.
 */
static WC_INLINE int cm_get_signer_memory(Signer* signer)
{
    int sz = sizeof(signer->pubKeySize) + sizeof(signer->keyOID)
           + sizeof(signer->nameLen)    + sizeof(signer->subjectNameHash);

#if !defined(NO_SKID)
        sz += (int)sizeof(signer->subjectKeyIdHash);
#endif

    /* Add dynamic bytes needed. */
    sz += (int)signer->pubKeySize;
    sz += signer->nameLen;

    return sz;
}


/* Return number of bytes of memory needed to persist this row.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] row  A row of signers from the CA table.
 * @return  Number of bytes.
 */
static WC_INLINE int cm_get_cert_cache_row_memory(Signer* row)
{
    int sz = 0;

    /* Each signer in row. */
    while (row != NULL) {
        /* Add in size of this signer. */
        sz += cm_get_signer_memory(row);
        row = row->next;
    }

    return sz;
}


/* Return the number of bytes of memory to persist cert cache.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] cm  Certificate manager.
 * @return  Number of bytes.
 */
static WC_INLINE int cm_get_cert_cache_mem_size(WOLFSSL_CERT_MANAGER* cm)
{
    int sz;
    int i;

    sz = sizeof(CertCacheHeader);

    /* Each row in table. */
    for (i = 0; i < CA_TABLE_SIZE; i++) {
        /* Add in size of this row. */
        sz += cm_get_cert_cache_row_memory(cm->caTable[i]);
    }

    return sz;
}


/* Get count of columns for each row.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] columns  Array of row counts.
 */
static WC_INLINE void cm_set_cert_header_Columns(WOLFSSL_CERT_MANAGER* cm,
    int* columns)
{
    int     i;
    Signer* row;

    /* Each row in table. */
    for (i = 0; i < CA_TABLE_SIZE; i++) {
        int count = 0;

        /* Get row from table. */
        row = cm->caTable[i];
        /* Each entry in row. */
        while (row != NULL) {
            /* Update count. */
            ++count;
            row = row->next;
        }
        /* Store row count. */
        columns[i] = count;
    }
}


/* Restore whole cert row from memory,
 *
 * Assumes we have locked CA table.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] current  Buffer containing rows.
 * @param [in] row      Row number being restored.
 * @param [in] listSz   Number of entries in row.
 * @param [in] end      End of data in buffer.
 * @return  Number of bytes consumed on success.
 * @return  PARSE_ERROR when listSz is less than zero.
 * @return  BUFFER_E when buffer is too small.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Negative value on error.
 */
static WC_INLINE int cm_restore_cert_row(WOLFSSL_CERT_MANAGER* cm,
    byte* current, int row, int listSz, const byte* end)
{
    int ret = 0;
    int idx = 0;

    /* Validate parameters. */
    if (listSz < 0) {
        WOLFSSL_MSG("Row header corrupted, negative value");
        ret = PARSE_ERROR;
    }

    /* Process all entries. */
    while ((ret == 0) && (listSz > 0)) {
        Signer* signer = NULL;
        byte*   publicKey;
        byte*   start = current + idx;  /* for end checks on this signer */
        int     minSz = sizeof(signer->pubKeySize) + sizeof(signer->keyOID) +
                      sizeof(signer->nameLen) + sizeof(signer->subjectNameHash);
        #ifndef NO_SKID
                minSz += (int)sizeof(signer->subjectKeyIdHash);
        #endif

        /* Check minimal size of bytes available. */
        if (start + minSz > end) {
            WOLFSSL_MSG("Would overread restore buffer");
            ret = BUFFER_E;
        }
        /* Make a new signer. */
        if ((ret == 0) && ((signer = MakeSigner(cm->heap)) == NULL)) {
            ret = MEMORY_E;
        }

        if (ret == 0) {
            /* Copy in public key size. */
            XMEMCPY(&signer->pubKeySize, current + idx,
                sizeof(signer->pubKeySize));
            idx += (int)sizeof(signer->pubKeySize);

            /* Copy in public key OID. */
            XMEMCPY(&signer->keyOID, current + idx, sizeof(signer->keyOID));
            idx += (int)sizeof(signer->keyOID);

            /* Check bytes available for public key. */
            if (start + minSz + signer->pubKeySize > end) {
                WOLFSSL_MSG("Would overread restore buffer");
                ret = BUFFER_E;
            }
        }
        if (ret == 0) {
            /* Allocate memory for public key to be stored in. */
            publicKey = (byte*)XMALLOC(signer->pubKeySize, cm->heap,
                DYNAMIC_TYPE_KEY);
            if (publicKey == NULL) {
                ret = MEMORY_E;
            }
        }

        if (ret == 0) {
            /* Copy in public key. */
            XMEMCPY(publicKey, current + idx, signer->pubKeySize);
            signer->publicKey = publicKey;
            idx += (int)signer->pubKeySize;

            /* Copy in certificate name length. */
            XMEMCPY(&signer->nameLen, current + idx, sizeof(signer->nameLen));
            idx += (int)sizeof(signer->nameLen);

            /* Check bytes available for certificate name. */
            if (start + minSz + signer->pubKeySize + signer->nameLen > end) {
                WOLFSSL_MSG("Would overread restore buffer");
                ret = BUFFER_E;
            }
        }
        if (ret == 0) {
            /* Allocate memory for public key to be stored in. */
            signer->name = (char*)XMALLOC((size_t)signer->nameLen, cm->heap,
                DYNAMIC_TYPE_SUBJECT_CN);
            if (signer->name == NULL) {
                ret = MEMORY_E;
            }
        }

        if (ret == 0) {
            /* Copy in certificate name. */
            /* safe cast -- allocated by above XMALLOC(). */
            XMEMCPY((void *)(wc_ptr_t)signer->name, current + idx,
                    (size_t)signer->nameLen);
            idx += signer->nameLen;

            /* Copy in hash of subject name. */
            XMEMCPY(signer->subjectNameHash, current + idx, SIGNER_DIGEST_SIZE);
            idx += SIGNER_DIGEST_SIZE;

        #ifndef NO_SKID
            /* Copy in hash of subject key. */
            XMEMCPY(signer->subjectKeyIdHash, current + idx,SIGNER_DIGEST_SIZE);
            idx += SIGNER_DIGEST_SIZE;
        #endif

            /* Make next Signer the head of the row. */
            signer->next = cm->caTable[row];
            /* Add Signer to start of row. */
            cm->caTable[row] = signer;

            /* Done one more Signer. */
            --listSz;
        }

        if ((ret != 0) && (signer != NULL)) {
            /* Dispose of allocated signer. */
            FreeSigner(signer, cm->heap);
        }
    }

    if (ret == 0) {
        /* Return the number of bytes used on success. */
        ret = idx;
    }
    return ret;
}


/* Store whole CA certificate row into memory.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] current  Buffer to write to.
 * @param [in] row      Row number being stored.
 * @return  Number of bytes added.
 */
static WC_INLINE int cm_store_cert_row(WOLFSSL_CERT_MANAGER* cm, byte* current,
    int row)
{
    int     added  = 0;
    Signer* list;

    /* Get the row - a linked list. */
    list  = cm->caTable[row];
    /* Each certificate in row. */
    while (list != NULL) {
        /* Public key size. */
        XMEMCPY(current + added, &list->pubKeySize, sizeof(list->pubKeySize));
        added += (int)sizeof(list->pubKeySize);

        /* Public key OID. */
        XMEMCPY(current + added, &list->keyOID,     sizeof(list->keyOID));
        added += (int)sizeof(list->keyOID);

        /* Public key. */
        XMEMCPY(current + added, list->publicKey, (size_t)list->pubKeySize);
        added += (int)list->pubKeySize;

        /* Certificate name length. */
        XMEMCPY(current + added, &list->nameLen, sizeof(list->nameLen));
        added += (int)sizeof(list->nameLen);

        /* Certificate name. */
        XMEMCPY(current + added, list->name, (size_t)list->nameLen);
        added += list->nameLen;

        /* Hash of subject name. */
        XMEMCPY(current + added, list->subjectNameHash, SIGNER_DIGEST_SIZE);
        added += SIGNER_DIGEST_SIZE;

    #ifndef NO_SKID
        /* Hash of public key. */
        XMEMCPY(current + added, list->subjectKeyIdHash,SIGNER_DIGEST_SIZE);
        added += SIGNER_DIGEST_SIZE;
    #endif

        /* Next certificate in row. */
        list = list->next;
    }

    return added;
}


/* Persist CA certificate cache to memory.
 *
 * Assumes we have locked CA table.
 *
 * @param [in] cm   Certificate manager.
 * @param [in] mem  Memory to persist into.
 * @param [in] sz   Size in bytes of memory.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BUFFER_E when memory is too small.
 */
static WC_INLINE int cm_do_mem_save_cert_cache(WOLFSSL_CERT_MANAGER* cm,
    void* mem, int sz)
{
    int ret = WOLFSSL_SUCCESS;
    int realSz;
    int i;

    WOLFSSL_ENTER("cm_do_mem_save_cert_cache");

    /* Calculate amount of memory required to store CA certificate table. */
    realSz = cm_get_cert_cache_mem_size(cm);
    if (realSz > sz) {
        WOLFSSL_MSG("Mem output buffer too small");
        ret = BUFFER_E;
    }
    if (ret == WOLFSSL_SUCCESS) {
        byte*           current;
        CertCacheHeader hdr;

        /* Create header for storage. */
        hdr.version  = WOLFSSL_CACHE_CERT_VERSION;
        hdr.rows     = CA_TABLE_SIZE;
        cm_set_cert_header_Columns(cm, hdr.columns);
        hdr.signerSz = (int)sizeof(Signer);

        /* Copy header into memory. */
        XMEMCPY(mem, &hdr, sizeof(CertCacheHeader));
        current = (byte*)mem + sizeof(CertCacheHeader);

        /* Each row of table. */
        for (i = 0; i < CA_TABLE_SIZE; ++i) {
            /* Append row to memory. */
            current += cm_store_cert_row(cm, current, i);
        }
    }

    return ret;
}


#if !defined(NO_FILESYSTEM)

/* Persist CA certificate cache to file.
 *
 * Locks CA table.
 *
 * @param [in] cm     Certificate manager.
 * @param [in] fname  File name to write to.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_BAD_FILE when opening file fails.
 * @return  BAD_MUTEX_E when locking fails.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  FWRITE_ERROR when writing to file fails.
 */
int CM_SaveCertCache(WOLFSSL_CERT_MANAGER* cm, const char* fname)
{
    XFILE file;
    int   ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("CM_SaveCertCache");

    /* Open file for writing. */
    file = XFOPEN(fname, "w+b");
    if (file == XBADFILE) {
       WOLFSSL_MSG("Couldn't open cert cache save file");
       ret = WOLFSSL_BAD_FILE;
    }

    /* Lock CA table. */
    if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) {
        WOLFSSL_MSG("wc_LockMutex on caLock failed");
        ret = BAD_MUTEX_E;
    }

    if (ret == WOLFSSL_SUCCESS) {
        byte* mem;
        /* Calculate size of memory required to store CA table. */
        size_t memSz = (size_t)cm_get_cert_cache_mem_size(cm);
        /* Allocate memory to hold CA table. */
        mem = (byte*)XMALLOC(memSz, cm->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (mem == NULL) {
            WOLFSSL_MSG("Alloc for tmp buffer failed");
            ret = MEMORY_E;
        }
        if (ret == WOLFSSL_SUCCESS) {
            /* Store CA table in memory. */
            ret = cm_do_mem_save_cert_cache(cm, mem, (int)memSz);
        }
        if (ret == WOLFSSL_SUCCESS) {
            /* Write memory to file. */
            int sz = (int)XFWRITE(mem, memSz, 1, file);
            if (sz != 1) {
                WOLFSSL_MSG("Cert cache file write failed");
                ret = FWRITE_ERROR;
            }
        }
        XFREE(mem, cm->heap, DYNAMIC_TYPE_TMP_BUFFER);

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    /* Close file. */
    if (file != XBADFILE) {
        XFCLOSE(file);
    }
    return ret;
}


/* Restore CA certificate cache from file.
 *
 * @param [in] cm     Certificate manager.
 * @param [in] fname  File name to write to.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_BAD_FILE when opening or using file fails.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  FREAD_ERROR when reading from file fails.
 */
int CM_RestoreCertCache(WOLFSSL_CERT_MANAGER* cm, const char* fname)
{
    XFILE file;
    int   ret = WOLFSSL_SUCCESS;
    int   memSz = 0;
    byte* mem = NULL;

    WOLFSSL_ENTER("CM_RestoreCertCache");

    /* Open file for reading. */
    file = XFOPEN(fname, "rb");
    if (file == XBADFILE) {
       WOLFSSL_MSG("Couldn't open cert cache save file");
       ret = WOLFSSL_BAD_FILE;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Read file into allocated memory. */
        ret = wolfssl_read_file(file, (char**)&mem, &memSz);
        if (ret == 0) {
            ret = WOLFSSL_SUCCESS;
        }
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Create the CA certificate table from memory. */
        ret = CM_MemRestoreCertCache(cm, mem, memSz);
        if (ret != WOLFSSL_SUCCESS) {
            WOLFSSL_MSG("Mem restore cert cache failed");
        }
    }

    /* Dispose of dynamic memory read into. */
    XFREE(mem, cm->heap, DYNAMIC_TYPE_TMP_BUFFER);
    /* Close file. */
    if (file != XBADFILE) {
        XFCLOSE(file);
    }
    return ret;
}

#endif /* NO_FILESYSTEM */


/* Persist CA certificate cache to memory.
 *
 * Locks CA table.
 *
 * @param [in]  cm    Certificate manager.
 * @param [in]  mem   Memory to persist into.
 * @param [in]  sz    Size in bytes of memory.
 * @param [out] used  Number of bytes used when persisting cache.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_MUTEX_E when locking fails.
 * @return  BUFFER_E when memory is too small.
 */
int CM_MemSaveCertCache(WOLFSSL_CERT_MANAGER* cm, void* mem, int sz, int* used)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("CM_MemSaveCertCache");

    /* Lock CA table. */
    if (wc_LockMutex(&cm->caLock) != 0) {
        WOLFSSL_MSG("wc_LockMutex on caLock failed");
        ret = BAD_MUTEX_E;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Save CA table into memory. */
        ret = cm_do_mem_save_cert_cache(cm, mem, sz);
        if (ret == WOLFSSL_SUCCESS) {
            /* Get the number of bytes used. */
            *used  = cm_get_cert_cache_mem_size(cm);
        }

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    return ret;
}


/* Restore CA certificate table from memory,
 *
 * Locks CA table.
 *
 * @param [in] cm   Certificate manager.
 * @param [in] mem  Buffer containing rows.
 * @param [in] sz   Size in bytes of data in buffer.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BUFFER_E when buffer is too small.
 * @return  BAD_MUTEX_E when locking fails.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int CM_MemRestoreCertCache(WOLFSSL_CERT_MANAGER* cm, const void* mem, int sz)
{
    int ret = WOLFSSL_SUCCESS;
    int i;
    CertCacheHeader* hdr = (CertCacheHeader*)mem;
    byte*            current = (byte*)mem + sizeof(CertCacheHeader);
    byte*            end     = (byte*)mem + sz;  /* don't go over */

    WOLFSSL_ENTER("CM_MemRestoreCertCache");

    /* Check memory available is bigger than cache header. */
    if ((sz < (int)sizeof(CertCacheHeader)) || (current > end)) {
        WOLFSSL_MSG("Cert Cache Memory buffer too small");
        ret = BUFFER_E;
    }

    /* Validate the cache header. */
    if ((ret == WOLFSSL_SUCCESS) &&
            ((hdr->version  != WOLFSSL_CACHE_CERT_VERSION) ||
             (hdr->rows     != CA_TABLE_SIZE) ||
             (hdr->signerSz != (int)sizeof(Signer)))) {
        WOLFSSL_MSG("Cert Cache Memory header mismatch");
        ret = CACHE_MATCH_ERROR;
    }

    /* Lock CA table. */
    if ((ret == WOLFSSL_SUCCESS) && (wc_LockMutex(&cm->caLock) != 0)) {
        WOLFSSL_MSG("wc_LockMutex on caLock failed");
        ret = BAD_MUTEX_E;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Dispose of current CA certificate table. */
        FreeSignerTable(cm->caTable, CA_TABLE_SIZE, cm->heap);

        /* Each row. */
        for (i = 0; i < CA_TABLE_SIZE; ++i) {
            /* Restore a row from memory. */
            int added = cm_restore_cert_row(cm, current, i, hdr->columns[i],
                end);
            /* Bail on error. */
            if (added < 0) {
                WOLFSSL_MSG("cm_restore_cert_row error");
                ret = added;
                break;
            }
            /* Update pointer to data of next row. */
            current += added;
        }

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    return ret;
}


/* Calculate size of CA certificate cache when persisted to memory.
 *
 * Locks CA table.
 *
 * @param [in] cm  Certificate manager.
 * @return  Number of bytes on success.
 * @return  BAD_MUTEX_E when locking fails.
 */
int CM_GetCertCacheMemSize(WOLFSSL_CERT_MANAGER* cm)
{
    int ret;

    WOLFSSL_ENTER("CM_GetCertCacheMemSize");

    /* Lock CA table. */
    if (wc_LockMutex(&cm->caLock) != 0) {
        WOLFSSL_MSG("wc_LockMutex on caLock failed");
        ret = BAD_MUTEX_E;
    }
    else {
        /* Calculate memory size. */
        ret = cm_get_cert_cache_mem_size(cm);

        /* Unlock CA table. */
        wc_UnLockMutex(&cm->caLock);
    }

    return ret;
}

#endif /* PERSIST_CERT_CACHE */

/*******************************************************************************
 * CRL handling
 ******************************************************************************/

/* Enables/disables the use of CRLs when validating certificates.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] options  Options for using CRLs. Valid flags:
 *                        WOLFSSL_CRL_CHECKALL, WOLFSSL_CRL_CHECK.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  WOLFSSL_FAILURE when initializing the CRL object fails.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when the CRL feature is disabled.
 */
int wolfSSL_CertManagerEnableCRL(WOLFSSL_CERT_MANAGER* cm, int options)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerEnableCRL");

    (void)options;

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }

#if defined(OPENSSL_COMPATIBLE_DEFAULTS)
    /* If disabling then don't worry about whether CRL feature is enabled. */
    if ((ret == WOLFSSL_SUCCESS) && (options == 0)) {
        /* Disable leaf CRL check. */
        cm->crlEnabled = 0;
        /* Disable all CRL checks. */
        cm->crlCheckAll = 0;
    }
    else
#endif
    if (ret == WOLFSSL_SUCCESS) {
#ifndef HAVE_CRL
        /* CRL feature not enabled. */
        ret = NOT_COMPILED_IN;
#else
        /* Create CRL object if not present. */
        if (cm->crl == NULL) {
            /* Allocate memory for CRL object. */
            cm->crl = (WOLFSSL_CRL*)XMALLOC(sizeof(WOLFSSL_CRL), cm->heap,
                                            DYNAMIC_TYPE_CRL);
            if (cm->crl == NULL) {
                ret = MEMORY_E;
            }
            if (ret == WOLFSSL_SUCCESS) {
                /* Reset fields of CRL object. */
                XMEMSET(cm->crl, 0, sizeof(WOLFSSL_CRL));
                /* Initialize CRL object. */
                if (InitCRL(cm->crl, cm) != 0) {
                    WOLFSSL_MSG("Init CRL failed");
                    /* Dispose of CRL object - indicating dynamically allocated.
                     */
                    FreeCRL(cm->crl, 1);
                    cm->crl = NULL;
                    ret = WOLFSSL_FAILURE;
                }
            }
        }

        if (ret == WOLFSSL_SUCCESS) {
        #if defined(HAVE_CRL_IO) && defined(USE_WOLFSSL_IO)
            /* Use built-in callback to lookup CRL from URL. */
            cm->crl->crlIOCb = EmbedCrlLookup;
        #endif
        #if defined(OPENSSL_COMPATIBLE_DEFAULTS)
            if ((options & WOLFSSL_CRL_CHECKALL) ||
                (options & WOLFSSL_CRL_CHECK))
        #endif
            {
                /* Enable leaf CRL check. */
                cm->crlEnabled = 1;
                if (options & WOLFSSL_CRL_CHECKALL) {
                    /* Enable all CRL check. */
                    cm->crlCheckAll = 1;
                }
            }
        }
#endif
    }

    return ret;
}


/* Disables the CRL checks.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerDisableCRL(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerDisableCRL");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Disable CRL checking. */
        cm->crlEnabled = 0;
        cm->crlCheckAll = 0;
    }

    return ret;
}

#ifdef HAVE_CRL

/* Load CRL for use.
 *
 * @param [in] cm    Certificate manager.
 * @param [in] buff  Buffer holding CRL.
 * @param [in] sz    Size in bytes of CRL in buffer.
 * @param [in] type  Format of encoding. Valid values:
 *                     WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or buff is NULL or sz is negative or zero.
 * @return  DUPE_ENTRY_E if the same or a newer CRL already exists in the cm.
 * @return  WOLFSSL_FATAL_ERROR when creating CRL object fails.
 */
int wolfSSL_CertManagerLoadCRLBuffer(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* buff, long sz, int type)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRLBuffer");

    /* Validate parameters. */
    if ((cm == NULL) || (buff == NULL) || (sz <= 0)) {
        ret = BAD_FUNC_ARG;
    }

    /* Create a CRL object if not available and enable CRL checking. */
    if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) &&
            (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) !=
             WOLFSSL_SUCCESS)) {
        WOLFSSL_MSG("Enable CRL failed");
        ret = WOLFSSL_FATAL_ERROR;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Load CRL into CRL object of the certificate manager. */
        ret = BufferLoadCRL(cm->crl, buff, sz, type, VERIFY);
    }

    return ret;
}

/* Free the CRL object of the certificate manager.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerFreeCRL(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerFreeCRL");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    /* Check whether CRL object exists. */
    if ((ret == WOLFSSL_SUCCESS) && (cm->crl != NULL)) {
        /* Dispose of CRL object - indicating dynamically allocated. */
        FreeCRL(cm->crl, 1);
        cm->crl = NULL;
    }

    return ret;
}

/* Check DER encoded certificate against CRLs if checking enabled.
 *
 * @param [in] cm   Certificate manager.
 * @param [in] der  DER encode certificate.
 * @param [in] sz   Size in bytes of DER encode certificate.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or der is NULL or sz is negative or zero.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wolfSSL_CertManagerCheckCRL(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* der, int sz)
{
    int ret = 0;
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

    WOLFSSL_ENTER("wolfSSL_CertManagerCheckCRL");

    /* Validate parameters. */
    if ((cm == NULL) || (der == NULL) || (sz <= 0)) {
        ret = BAD_FUNC_ARG;
    }

    /* Check if CRL checking enabled. */
    if ((ret == 0) && cm->crlEnabled) {
        /* Allocate memory for decoded certificate. */
        WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT,
            ret=MEMORY_E);
        if (WC_VAR_OK(cert))
        {
            /* Initialize decoded certificate with buffer. */
            InitDecodedCert(cert, der, (word32)sz, NULL);

            /* Parse certificate and perform CRL checks. */
            ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_CRL, cm, NULL);
            if (ret != 0) {
                WOLFSSL_MSG("ParseCert failed");
            }
            /* Do CRL checks with decoded certificate. */
            else if ((ret = CheckCertCRL(cm->crl, cert)) != 0) {
                WOLFSSL_MSG("CheckCertCRL failed");
            }

            /* Dispose of dynamically allocated memory. */
            FreeDecodedCert(cert);
            WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT);
        }
    }

    return (ret == 0) ? WOLFSSL_SUCCESS : ret;
}

/* Set the missing CRL callback.
 *
 * @param [in] cm  Certificate manager.
 * @param [in] cb  Missing CRL callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerSetCRL_Cb(WOLFSSL_CERT_MANAGER* cm, CbMissingCRL cb)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerSetCRL_Cb");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Store callback. */
        cm->cbMissingCRL = cb;
    }

    return ret;
}

int wolfSSL_CertManagerSetCRL_ErrorCb(WOLFSSL_CERT_MANAGER* cm, crlErrorCb cb,
                                      void* ctx)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerSetCRL_Cb");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Store callback. */
        cm->crlCb = cb;
        cm->crlCbCtx = ctx;
    }

    return ret;
}

#ifdef HAVE_CRL_UPDATE_CB
int wolfSSL_CertManagerGetCRLInfo(WOLFSSL_CERT_MANAGER* cm, CrlInfo* info,
    const byte* buff, long sz, int type)
{
    return GetCRLInfo(cm->crl, info, buff, sz, type);
}

/* Set the callback to be called when a CRL entry has
 * been updated (new entry had the same issuer hash and
 * a newer CRL number).
 *
 * @param [in] cm  Certificate manager.
 * @param [in] cb  CRL update callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerSetCRLUpdate_Cb(WOLFSSL_CERT_MANAGER* cm, CbUpdateCRL cb)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerSetCRLUpdate_Cb");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Store callback. */
        cm->cbUpdateCRL = cb;
    }

    return ret;
}
#endif

#ifdef HAVE_CRL_IO
/* Set the CRL I/O callback.
 *
 * @param [in] cm  Certificate manager.
 * @param [in] cb  CRL I/O callback.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerSetCRL_IOCb(WOLFSSL_CERT_MANAGER* cm, CbCrlIO cb)
{
    int ret = WOLFSSL_SUCCESS;

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == WOLFSSL_SUCCESS) && (cm->crl != NULL)) {
        /* Store callback. */
        cm->crl->crlIOCb = cb;
    }

    return ret;
}
#endif

#ifndef NO_FILESYSTEM
/* Load CRL/s from path with the option of monitoring for changes.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] path     Path to a directory containing CRLs.
 * @param [in] type     Format of encoding. Valid values:
 *                        WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @param [in] monitor  Whether to monitor path for changes to files.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or path is NULL.
 * @return  WOLFSSL_FATAL_ERROR when enabling CRLs fails.
 */
int wolfSSL_CertManagerLoadCRL(WOLFSSL_CERT_MANAGER* cm, const char* path,
    int type, int monitor)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRL");

    /* Validate parameters. */
    if ((cm == NULL) || (path == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    /* Create a CRL object if not available. */
    if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) &&
            (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) !=
             WOLFSSL_SUCCESS)) {
        WOLFSSL_MSG("Enable CRL failed");
        ret = WOLFSSL_FATAL_ERROR;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Load CRLs from path into CRL object of certificate manager. */
        ret = LoadCRL(cm->crl, path, type, monitor);
    }

    return ret;
}

/* Load CRL from file.
 *
 * @param [in] cm    Certificate manager.
 * @param [in] file  Path to a directory containing CRLs.
 * @param [in] type  Format of encoding. Valid values:
 *                       WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or file is NULL.
 * @return  WOLFSSL_FATAL_ERROR when enabling CRLs fails.
 */
int wolfSSL_CertManagerLoadCRLFile(WOLFSSL_CERT_MANAGER* cm, const char* file,
    int type)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerLoadCRLFile");

    /* Validate parameters. */
    if ((cm == NULL) || (file == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    /* Create a CRL object if not available. */
    if ((ret == WOLFSSL_SUCCESS) && (cm->crl == NULL) &&
            (wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK) !=
             WOLFSSL_SUCCESS)) {
        WOLFSSL_MSG("Enable CRL failed");
        ret = WOLFSSL_FATAL_ERROR;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Load CRL file into CRL object of certificate manager. */
        ret = ProcessFile(NULL, file, type, CRL_TYPE, NULL, 0, cm->crl, VERIFY);
    }

    return ret;
}
#endif /* !NO_FILESYSTEM */

#endif /* HAVE_CRL */

/*******************************************************************************
 * OCSP handling
 ******************************************************************************/

/* Enables OCSP when validating certificates and sets options.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] options  Options for using OCSP. Valid flags:
 *                        WOLFSSL_OCSP_URL_OVERRIDE, WOLFSSL_OCSP_NO_NONCE,
 *                        WOLFSSL_OCSP_CHECKALL.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  0 when initializing the OCSP object fails.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when the OCSP feature is disabled.
 */
int wolfSSL_CertManagerEnableOCSP(WOLFSSL_CERT_MANAGER* cm, int options)
{
    int ret = WOLFSSL_SUCCESS;

    (void)options;

    WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSP");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }

#ifndef HAVE_OCSP
    if (ret == WOLFSSL_SUCCESS) {
        /* OCSP feature not enabled. */
        ret = NOT_COMPILED_IN;
    }
#else
    if (ret == WOLFSSL_SUCCESS) {
        /* Check whether OCSP object is available. */
        if (cm->ocsp == NULL) {
            /* Allocate memory for OCSP object. */
            cm->ocsp = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm->heap,
                DYNAMIC_TYPE_OCSP);
            if (cm->ocsp == NULL) {
                ret = MEMORY_E;
            }
            if (ret == WOLFSSL_SUCCESS) {
                /* Reset the fields of the OCSP object. */
                XMEMSET(cm->ocsp, 0, sizeof(WOLFSSL_OCSP));
                /* Initialize the OCSP object. */
                if (InitOCSP(cm->ocsp, cm) != 0) {
                    WOLFSSL_MSG("Init OCSP failed");
                    /* Dispose of OCSP object - indicating dynamically
                     * allocated. */
                    FreeOCSP(cm->ocsp, 1);
                    cm->ocsp = NULL;
                    ret = 0;
                }
            }
        }
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Enable OCSP checking. */
        cm->ocspEnabled = 1;
        /* Enable URL override if requested. */
        if (options & WOLFSSL_OCSP_URL_OVERRIDE) {
            cm->ocspUseOverrideURL = 1;
        }
        /* Set nonce option for creating OCSP requests. */
        cm->ocspSendNonce = (options & WOLFSSL_OCSP_NO_NONCE) !=
            WOLFSSL_OCSP_NO_NONCE;
        /* Set all OCSP checks on if requested. */
        if (options & WOLFSSL_OCSP_CHECKALL) {
            cm->ocspCheckAll = 1;
        }
    #ifndef WOLFSSL_USER_IO
        /* Set built-in OCSP lookup. */
        cm->ocspIOCb = EmbedOcspLookup;
        cm->ocspRespFreeCb = EmbedOcspRespFree;
        cm->ocspIOCtx = cm->heap;
    #endif /* WOLFSSL_USER_IO */
    }
#endif /* HAVE_OCSP */

    return ret;
}

/* Disables the OCSP checks.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerDisableOCSP(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSP");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Disable use of OCSP with certificate validation. */
        cm->ocspEnabled = 0;
    }

    return ret;
}

/* Enables OCSP stapling with certificates in manager.
 *
 * @param [in] cm       Certificate manager.
 * @param [in] options  Options for using OCSP. Valid flags:
 *                        WOLFSSL_OCSP_URL_OVERRIDE, WOLFSSL_OCSP_NO_NONCE,
 *                        WOLFSSL_OCSP_CHECKALL.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  0 when initializing the OCSP stapling object fails.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  NOT_COMPILED_IN when the OCSP stapling feature is disabled.
 */
int wolfSSL_CertManagerEnableOCSPStapling(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSPStapling");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }

#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
    !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (ret == WOLFSSL_SUCCESS) {
        /* OCSP stapling feature not enabled. */
        ret = NOT_COMPILED_IN;
    }
#else
#ifndef NO_WOLFSSL_SERVER
    if (ret == WOLFSSL_SUCCESS) {
        /* Check whether OCSP object is available. */
        if (cm->ocsp_stapling == NULL) {
            /* Allocate memory for OCSP stapling object. */
            cm->ocsp_stapling = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP),
                cm->heap, DYNAMIC_TYPE_OCSP);
            if (cm->ocsp_stapling == NULL) {
                ret = MEMORY_E;
            }
            if (ret == WOLFSSL_SUCCESS) {
                /* Reset the fields of the OCSP object. */
                XMEMSET(cm->ocsp_stapling, 0, sizeof(WOLFSSL_OCSP));
                /* Initialize the OCSP stapling object. */
                if (InitOCSP(cm->ocsp_stapling, cm) != 0) {
                    WOLFSSL_MSG("Init OCSP failed");
                    /* Dispose of OCSP stapling object - indicating dynamically
                     * allocated. */
                    FreeOCSP(cm->ocsp_stapling, 1);
                    cm->ocsp_stapling = NULL;
                    ret = 0;
                }
            }
        }
    }
#ifndef WOLFSSL_USER_IO
    if (ret == WOLFSSL_SUCCESS) {
        /* Set built-in OCSP lookup. */
        cm->ocspIOCb = EmbedOcspLookup;
        cm->ocspRespFreeCb = EmbedOcspRespFree;
        cm->ocspIOCtx = cm->heap;
    }
#endif /* WOLFSSL_USER_IO */
#endif /* NO_WOLFSSL_SERVER */
    if (ret == WOLFSSL_SUCCESS) {
        /* Enable OCSP stapling. */
        cm->ocspStaplingEnabled = 1;
    }
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST ||
        * HAVE_CERTIFICATE_STATUS_REQUEST_V2 */

    return ret;
}

/* Disables OCSP Stapling.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerDisableOCSPStapling(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSPStapling");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
    #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
        defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
        /* Disable use of OCSP Stapling. */
        cm->ocspStaplingEnabled = 0;
    #else
        /* OCSP stapling feature not enabled. */
        ret = NOT_COMPILED_IN;
    #endif
    }

    return ret;
}

/* Enable the must use OCSP Stapling option.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerEnableOCSPMustStaple(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerEnableOCSPMustStaple");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    #ifndef NO_WOLFSSL_CLIENT
        /* Enable must use OCSP Stapling option. */
        cm->ocspMustStaple = 1;
    #endif
#else
        /* OCSP stapling feature not enabled. */
        ret = NOT_COMPILED_IN;
#endif
    }

    return ret;
}

/* Disable the must use OCSP Stapling option.
 *
 * @param [in] cm  Certificate manager.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerDisableOCSPMustStaple(WOLFSSL_CERT_MANAGER* cm)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerDisableOCSPMustStaple");

    /* Validate parameter. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == WOLFSSL_SUCCESS) {
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    #ifndef NO_WOLFSSL_CLIENT
        /* Disable must use OCSP Stapling option. */
        cm->ocspMustStaple = 0;
    #endif
#else
        /* OCSP stapling feature not enabled. */
        ret = NOT_COMPILED_IN;
#endif
    }

    return ret;
}

#ifdef HAVE_OCSP
/* Check DER encoded certificate against with OCSP if checking enabled.
 *
 * @param [in] cm   Certificate manager.
 * @param [in] der  DER encode certificate.
 * @param [in] sz   Size in bytes of DER encode certificate.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or der is NULL or sz is negative or 0.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wolfSSL_CertManagerCheckOCSP(WOLFSSL_CERT_MANAGER* cm,
    const unsigned char* der, int sz)
{
    int ret = 0;
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

    WOLFSSL_ENTER("wolfSSL_CertManagerCheckOCSP");

    /* Validate parameters. */
    if ((cm == NULL) || (der == NULL) || (sz <= 0)) {
        ret = BAD_FUNC_ARG;
    }

    /* Check if OCSP checking enabled. */
    if ((ret == 0) && cm->ocspEnabled) {
        /* Allocate memory for decoded certificate. */
        WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT,
            ret=MEMORY_E);
        if (WC_VAR_OK(cert))
        {
            /* Initialize decoded certificate with buffer. */
            InitDecodedCert(cert, der, (word32)sz, NULL);

            /* Parse certificate and perform CRL checks. */
            ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_OCSP, cm, NULL);
            if (ret != 0) {
                WOLFSSL_MSG("ParseCert failed");
            }
            /* Do OCSP checks with decoded certificate. */
            else if ((ret = CheckCertOCSP(cm->ocsp, cert)) != 0) {
                WOLFSSL_MSG("CheckCertOCSP failed");
            }

            /* Dispose of dynamically allocated memory. */
            FreeDecodedCert(cert);
            WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT);
        }
    }

    return (ret == 0) ? WOLFSSL_SUCCESS : ret;
}

/* Check OCSP response.
 *
 * @param [in] cm              Certificate manager.
 * @param [in] response        Buffer holding OCSP response.
 * @param [in] responseSz      Size in bytes of OCSP response.
 * @param [in] responseBuffer  Buffer to copy response into.
 * @param [in] status          Place to store certificate status.
 * @param [in] entry           Place to store OCSP entry.
 * @param [in] ocspRequest     OCSP request to match with response.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm or response is NULL.
 */
int wolfSSL_CertManagerCheckOCSPResponse(WOLFSSL_CERT_MANAGER *cm,
    byte *response, int responseSz, buffer *responseBuffer,
    CertStatus *status, OcspEntry *entry, OcspRequest *ocspRequest)
{
    int ret = 0;

    WOLFSSL_ENTER("wolfSSL_CertManagerCheckOCSPResponse");

    /* Validate parameters. */
    if ((cm == NULL) || (response == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && cm->ocspEnabled) {
        /* Check OCSP response with OCSP object from certificate manager. */
        ret = CheckOcspResponse(cm->ocsp, response, responseSz, responseBuffer,
            status, entry, ocspRequest, NULL);
    }

    return (ret == 0) ? WOLFSSL_SUCCESS : ret;
}

/* Set the OCSP override URL.
 *
 * @param [in] cm   Certificate manager.
 * @param [in] url  URL to get an OCSP response from.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wolfSSL_CertManagerSetOCSPOverrideURL(WOLFSSL_CERT_MANAGER* cm,
    const char* url)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerSetOCSPOverrideURL");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == WOLFSSL_SUCCESS) {
        /* Dispose of old URL. */
        XFREE(cm->ocspOverrideURL, cm->heap, DYNAMIC_TYPE_URL);
        if (url != NULL) {
            /* Calculate size of URL string. Include terminator character. */
            int urlSz = (int)XSTRLEN(url) + 1;
            /* Allocate memory for URL to be copied into. */
            cm->ocspOverrideURL = (char*)XMALLOC((size_t)urlSz, cm->heap,
                DYNAMIC_TYPE_URL);
            if (cm->ocspOverrideURL == NULL) {
                ret = MEMORY_E;
            }
            if (ret == WOLFSSL_SUCCESS) {
                /* Copy URL into certificate manager. */
                XMEMCPY(cm->ocspOverrideURL, url, (size_t)urlSz);
            }
        }
        else {
            /* No URL to set so make it NULL. */
            cm->ocspOverrideURL = NULL;
        }
    }

    return ret;
}

/* Set the OCSP I/O callback, OCSP response free callback and related data.
 *
 * @param [in] cm          Certificate manager.
 * @param [in] ioCb        OCSP callback.
 * @param [in] respFreeCb  Callback to free OCSP response buffer.
 * @param [in] ioCbCtx     Context data to pass to OCSP callbacks.
 * @return  WOLFSSL_SUCCESS on success.
 * @return  BAD_FUNC_ARG when cm is NULL.
 */
int wolfSSL_CertManagerSetOCSP_Cb(WOLFSSL_CERT_MANAGER* cm, CbOCSPIO ioCb,
    CbOCSPRespFree respFreeCb, void* ioCbCtx)
{
    int ret = WOLFSSL_SUCCESS;

    WOLFSSL_ENTER("wolfSSL_CertManagerSetOCSP_Cb");

    /* Validate parameters. */
    if (cm == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == WOLFSSL_SUCCESS) {
        /* Set callbacks and data into certificate manager. */
        cm->ocspIOCb = ioCb;
        cm->ocspRespFreeCb = respFreeCb;
        cm->ocspIOCtx = ioCbCtx;
    }

    return ret;
}

#endif /* HAVE_OCSP */

/******************************************************************************
 * Internal APIs that use WOLFSSL_CERT_MANAGER
 ******************************************************************************/

/* hash is the SHA digest of name, just use first 32 bits as hash */
static WC_INLINE word32 HashSigner(const byte* hash)
{
    return MakeWordFromHash(hash) % CA_TABLE_SIZE;
}


/* does CA already exist on signer list */
int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash)
{
    Signer* signers;
    int     ret = 0;
    word32  row;

    if (cm == NULL || hash == NULL) {
        return ret;
    }

    row = HashSigner(hash);

    if (wc_LockMutex(&cm->caLock) != 0) {
        return ret;
    }
    signers = cm->caTable[row];
    while (signers) {
        byte* subjectHash;

    #ifndef NO_SKID
        subjectHash = signers->subjectKeyIdHash;
    #else
        subjectHash = signers->subjectNameHash;
    #endif

        if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) {
            ret = 1; /* success */
            break;
        }
        signers = signers->next;
    }
    wc_UnLockMutex(&cm->caLock);

    return ret;
}

#ifdef WOLFSSL_TRUST_PEER_CERT
/* hash is the SHA digest of name, just use first 32 bits as hash */
static WC_INLINE word32 TrustedPeerHashSigner(const byte* hash)
{
    return MakeWordFromHash(hash) % TP_TABLE_SIZE;
}

/* does trusted peer already exist on signer list */
int AlreadyTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DecodedCert* cert)
{
    TrustedPeerCert* tp;
    int     ret = 0;
    word32  row = TrustedPeerHashSigner(cert->subjectHash);

    if (wc_LockMutex(&cm->tpLock) != 0)
        return  ret;
    tp = cm->tpTable[row];
    while (tp) {
        if ((XMEMCMP(cert->subjectHash, tp->subjectNameHash,
                SIGNER_DIGEST_SIZE) == 0)
    #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER
         && (XMEMCMP(cert->issuerHash, tp->issuerHash,
                SIGNER_DIGEST_SIZE) == 0)
    #endif
        )
            ret = 1;
    #ifndef NO_SKID
        if (cert->extSubjKeyIdSet) {
            /* Compare SKID as well if available */
            if (ret == 1 && XMEMCMP(cert->extSubjKeyId, tp->subjectKeyIdHash,
                    SIGNER_DIGEST_SIZE) != 0)
                ret = 0;
        }
    #endif
        if (ret == 1)
            break;
        tp = tp->next;
    }
    wc_UnLockMutex(&cm->tpLock);

    return ret;
}

/* return Trusted Peer if found, otherwise NULL
    type is what to match on
 */
TrustedPeerCert* GetTrustedPeer(void* vp, DecodedCert* cert)
{
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    TrustedPeerCert* ret = NULL;
    TrustedPeerCert* tp  = NULL;
    word32  row;

    if (cm == NULL || cert == NULL)
        return NULL;

    row = TrustedPeerHashSigner(cert->subjectHash);

    if (wc_LockMutex(&cm->tpLock) != 0)
        return ret;

    tp = cm->tpTable[row];
    while (tp) {
        if ((XMEMCMP(cert->subjectHash, tp->subjectNameHash,
                SIGNER_DIGEST_SIZE) == 0)
        #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER
             && (XMEMCMP(cert->issuerHash, tp->issuerHash,
                SIGNER_DIGEST_SIZE) == 0)
        #endif
            )
            ret = tp;
    #ifndef NO_SKID
        if (cert->extSubjKeyIdSet) {
            /* Compare SKID as well if available */
            if (ret != NULL && XMEMCMP(cert->extSubjKeyId, tp->subjectKeyIdHash,
                    SIGNER_DIGEST_SIZE) != 0)
                ret = NULL;
        }
    #endif
        if (ret != NULL)
            break;
        tp = tp->next;
    }
    wc_UnLockMutex(&cm->tpLock);

    return ret;
}


int MatchTrustedPeer(TrustedPeerCert* tp, DecodedCert* cert)
{
    if (tp == NULL || cert == NULL)
        return BAD_FUNC_ARG;

    /* subject key id or subject hash has been compared when searching
       tpTable for the cert from function GetTrustedPeer */

    /* compare signatures */
    if (tp->sigLen == cert->sigLength) {
        if (XMEMCMP(tp->sig, cert->signature, cert->sigLength)) {
            return WOLFSSL_FAILURE;
        }
    }
    else {
        return WOLFSSL_FAILURE;
    }

    return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_TRUST_PEER_CERT */

/* return CA if found, otherwise NULL */
Signer* GetCA(void* vp, byte* hash)
{
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    Signer* ret = NULL;
    Signer* signers;
    word32  row = 0;

    if (cm == NULL || hash == NULL)
        return NULL;

    row = HashSigner(hash);

    if (wc_LockMutex(&cm->caLock) != 0)
        return ret;

    signers = cm->caTable[row];
    while (signers) {
        byte* subjectHash;
        #ifndef NO_SKID
            subjectHash = signers->subjectKeyIdHash;
        #else
            subjectHash = signers->subjectNameHash;
        #endif
        if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) {
            ret = signers;
            break;
        }
        signers = signers->next;
    }
    wc_UnLockMutex(&cm->caLock);

    return ret;
}

#if defined(HAVE_OCSP)
Signer* GetCAByKeyHash(void* vp, const byte* keyHash)
{
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    Signer* ret = NULL;
    Signer* signers;
    int row;

    if (cm == NULL || keyHash == NULL)
        return NULL;

    /* try lookup using keyHash as subjKeyID first */
    ret = GetCA(vp, (byte*)keyHash);
    if (ret != NULL && XMEMCMP(ret->subjectKeyHash, keyHash, KEYID_SIZE) == 0) {
        return ret;
    }

    /* if we can't find the cert, we have to scan the full table */
    if (wc_LockMutex(&cm->caLock) != 0)
        return NULL;

    /* Unfortunately we need to look through the entire table */
    for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) {
        for (signers = cm->caTable[row]; signers != NULL;
                signers = signers->next) {
            if (XMEMCMP(signers->subjectKeyHash, keyHash, KEYID_SIZE) == 0) {
                ret = signers;
                break;
            }
        }
    }

    wc_UnLockMutex(&cm->caLock);
    return ret;
}
#endif
#ifdef WOLFSSL_AKID_NAME
Signer* GetCAByAKID(void* vp, const byte* issuer, word32 issuerSz,
        const byte* serial, word32 serialSz)
{
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    Signer* ret = NULL;
    Signer* signers;
    byte nameHash[SIGNER_DIGEST_SIZE];
    byte serialHash[SIGNER_DIGEST_SIZE];
    word32 row;

    if (cm == NULL || issuer == NULL || issuerSz == 0 ||
            serial == NULL || serialSz == 0)
        return NULL;

    if (CalcHashId(issuer, issuerSz, nameHash) != 0 ||
            CalcHashId(serial, serialSz, serialHash) != 0)
        return NULL;

    if (wc_LockMutex(&cm->caLock) != 0)
        return ret;

    /* Unfortunately we need to look through the entire table */
    for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) {
        for (signers = cm->caTable[row]; signers != NULL;
                signers = signers->next) {
            if (XMEMCMP(signers->issuerNameHash, nameHash, SIGNER_DIGEST_SIZE)
                    == 0 && XMEMCMP(signers->serialHash, serialHash,
                                    SIGNER_DIGEST_SIZE) == 0) {
                ret = signers;
                break;
            }
        }
    }

    wc_UnLockMutex(&cm->caLock);

    return ret;
}
#endif

#ifndef NO_SKID
/* return CA if found, otherwise NULL. Walk through hash table. */
Signer* GetCAByName(void* vp, byte* hash)
{
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    Signer* ret = NULL;
    Signer* signers;
    word32  row;

    if (cm == NULL)
        return NULL;

    if (wc_LockMutex(&cm->caLock) != 0)
        return ret;

    for (row = 0; row < CA_TABLE_SIZE && ret == NULL; row++) {
        signers = cm->caTable[row];
        while (signers && ret == NULL) {
            if (XMEMCMP(hash, signers->subjectNameHash,
                        SIGNER_DIGEST_SIZE) == 0) {
                ret = signers;
            }
            signers = signers->next;
        }
    }
    wc_UnLockMutex(&cm->caLock);

    return ret;
}
#endif

#ifdef WOLFSSL_TRUST_PEER_CERT
/* add a trusted peer cert to linked list */
int AddTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int verify)
{
    int ret = 0;
    int row = 0;
    TrustedPeerCert* peerCert;
    DecodedCert* cert;
    DerBuffer*   der = *pDer;

    WOLFSSL_MSG("Adding a Trusted Peer Cert");

    cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), cm->heap,
                                 DYNAMIC_TYPE_DCERT);
    if (cert == NULL) {
        FreeDer(&der);
        return MEMORY_E;
    }

    InitDecodedCert(cert, der->buffer, der->length, cm->heap);
    if ((ret = ParseCert(cert, TRUSTED_PEER_TYPE, verify, cm)) != 0) {
        FreeDecodedCert(cert);
        XFREE(cert, NULL, DYNAMIC_TYPE_DCERT);
        FreeDer(&der);
        return ret;
    }
    WOLFSSL_MSG("\tParsed new trusted peer cert");

    peerCert = (TrustedPeerCert*)XMALLOC(sizeof(TrustedPeerCert), cm->heap,
                                                             DYNAMIC_TYPE_CERT);
    if (peerCert == NULL) {
        FreeDecodedCert(cert);
        XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
        FreeDer(&der);
        return MEMORY_E;
    }
    XMEMSET(peerCert, 0, sizeof(TrustedPeerCert));

    #ifndef IGNORE_NAME_CONSTRAINTS
        if (peerCert->permittedNames)
            FreeNameSubtrees(peerCert->permittedNames, cm->heap);
        if (peerCert->excludedNames)
            FreeNameSubtrees(peerCert->excludedNames, cm->heap);
    #endif

    if (AlreadyTrustedPeer(cm, cert)) {
        WOLFSSL_MSG("\tAlready have this CA, not adding again");
        FreeTrustedPeer(peerCert, cm->heap);
        (void)ret;
    }
    else {
        /* add trusted peer signature */
        peerCert->sigLen = cert->sigLength;
        peerCert->sig = (byte *)XMALLOC(cert->sigLength, cm->heap,
                                                        DYNAMIC_TYPE_SIGNATURE);
        if (peerCert->sig == NULL) {
            FreeDecodedCert(cert);
            XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
            FreeTrustedPeer(peerCert, cm->heap);
            FreeDer(&der);
            return MEMORY_E;
        }
        XMEMCPY(peerCert->sig, cert->signature, cert->sigLength);

        /* add trusted peer name */
        peerCert->nameLen = cert->subjectCNLen;
        peerCert->name    = cert->subjectCN;
        #ifndef IGNORE_NAME_CONSTRAINTS
            peerCert->permittedNames = cert->permittedNames;
            peerCert->excludedNames  = cert->excludedNames;
        #endif

        /* add SKID when available and hash of name */
        #ifndef NO_SKID
            XMEMCPY(peerCert->subjectKeyIdHash, cert->extSubjKeyId,
                   SIGNER_DIGEST_SIZE);
        #endif
            XMEMCPY(peerCert->subjectNameHash, cert->subjectHash,
                    SIGNER_DIGEST_SIZE);
        #ifndef WOLFSSL_NO_ISSUERHASH_TDPEER
            XMEMCPY(peerCert->issuerHash, cert->issuerHash,
                    SIGNER_DIGEST_SIZE);
        #endif
            /* If Key Usage not set, all uses valid. */
            peerCert->next    = NULL;
            cert->subjectCN = 0;
        #ifndef IGNORE_NAME_CONSTRAINTS
            cert->permittedNames = NULL;
            cert->excludedNames = NULL;
        #endif

            row = (int)TrustedPeerHashSigner(peerCert->subjectNameHash);

            if (wc_LockMutex(&cm->tpLock) == 0) {
                peerCert->next = cm->tpTable[row];
                cm->tpTable[row] = peerCert;   /* takes ownership */
                wc_UnLockMutex(&cm->tpLock);
            }
            else {
                WOLFSSL_MSG("\tTrusted Peer Cert Mutex Lock failed");
                FreeDecodedCert(cert);
                XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
                FreeTrustedPeer(peerCert, cm->heap);
                FreeDer(&der);
                return BAD_MUTEX_E;
            }
        }

    WOLFSSL_MSG("\tFreeing parsed trusted peer cert");
    FreeDecodedCert(cert);
    XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
    WOLFSSL_MSG("\tFreeing der trusted peer cert");
    FreeDer(&der);
    WOLFSSL_MSG("\t\tOK Freeing der trusted peer cert");
    WOLFSSL_LEAVE("AddTrustedPeer", ret);

    return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_TRUST_PEER_CERT */

int AddSigner(WOLFSSL_CERT_MANAGER* cm, Signer *s)
{
    byte*   subjectHash;
    Signer* signers;
    word32  row;

    if (cm == NULL || s == NULL)
        return BAD_FUNC_ARG;

#ifndef NO_SKID
    subjectHash = s->subjectKeyIdHash;
#else
    subjectHash = s->subjectNameHash;
#endif

    if (AlreadySigner(cm, subjectHash)) {
        FreeSigner(s, cm->heap);
        return 0;
    }

    row = HashSigner(subjectHash);

    if (wc_LockMutex(&cm->caLock) != 0)
        return BAD_MUTEX_E;

    signers = cm->caTable[row];
    s->next = signers;
    cm->caTable[row] = s;

    wc_UnLockMutex(&cm->caLock);
    return 0;
}

/* owns der, internal now uses too */
/* type flag ids from user or from chain received during verify
   don't allow chain ones to be added w/o isCA extension */
int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify)
{
    int         ret = 0;
    Signer*     signer = NULL;
    word32      row = 0;
    byte*       subjectHash = NULL;
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);
    DerBuffer*   der = *pDer;

    WOLFSSL_MSG_CERT_LOG("Adding a CA");

    if (cm == NULL) {
        FreeDer(pDer);
        return BAD_FUNC_ARG;
    }

    #ifdef WOLFSSL_SMALL_STACK
    cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, DYNAMIC_TYPE_DCERT);
    if (cert == NULL) {
        FreeDer(pDer);
        return MEMORY_E;
    }
    #endif

    InitDecodedCert(cert, der->buffer, der->length, cm->heap);

#ifdef WC_ASN_UNKNOWN_EXT_CB
    if (cm->unknownExtCallback != NULL) {
        wc_SetUnknownExtCallback(cert, cm->unknownExtCallback);
    }
#endif

    WOLFSSL_MSG_CERT("\tParsing new CA");
    ret = ParseCert(cert, CA_TYPE, verify, cm);

    WOLFSSL_MSG("\tParsed new CA");
#ifdef WOLFSSL_DEBUG_CERTS
    {
        const char*  err_msg;
        if (ret == 0) {
            WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "issuer:  '%s'",
                cert->issuer);
            WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "subject: '%s'",
                cert->subject);
        }
        else {
            WOLFSSL_MSG_CERT(
                WOLFSSL_MSG_CERT_INDENT "Failed during parse of new CA");
            err_msg = wc_GetErrorString(ret);
            WOLFSSL_MSG_CERT_EX(WOLFSSL_MSG_CERT_INDENT "error ret: %d; %s",
                ret, err_msg);
        }
    }
#endif /* WOLFSSL_DEBUG_CERTS */

#ifndef NO_SKID
    subjectHash = cert->extSubjKeyId;
#else
    subjectHash = cert->subjectHash;
#endif

    /* check CA key size */
    if (verify && (ret == 0 )) {
        switch (cert->keyOID) {
        #ifndef NO_RSA
            #ifdef WC_RSA_PSS
            case RSAPSSk:
            #endif
            case RSAk:
                if (cm->minRsaKeySz < 0 ||
                                   cert->pubKeySize < (word16)cm->minRsaKeySz) {
                    ret = RSA_KEY_SIZE_E;
                    WOLFSSL_MSG_CERT_LOG("\tCA RSA key size error");
                    WOLFSSL_MSG_CERT_EX("\tCA RSA pubKeySize = %d; "
                                                "minRsaKeySz = %d",
                                   cert->pubKeySize, cm->minRsaKeySz);
                }
                break;
        #endif /* !NO_RSA */
            #ifdef HAVE_ECC
            case ECDSAk:
                if (cm->minEccKeySz < 0 ||
                                   cert->pubKeySize < (word16)cm->minEccKeySz) {
                    ret = ECC_KEY_SIZE_E;
                    WOLFSSL_MSG_CERT_LOG("\tCA ECC key size error");
                    WOLFSSL_MSG_CERT_EX("\tCA ECC pubKeySize = %d; "
                                                 "minEccKeySz = %d",
                                   cert->pubKeySize, cm->minEccKeySz);
                }
                break;
            #endif /* HAVE_ECC */
            #ifdef HAVE_ED25519
            case ED25519k:
                if (cm->minEccKeySz < 0 ||
                                   ED25519_KEY_SIZE < (word16)cm->minEccKeySz) {
                    ret = ECC_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA ECC key size error");
                }
                break;
            #endif /* HAVE_ED25519 */
            #ifdef HAVE_ED448
            case ED448k:
                if (cm->minEccKeySz < 0 ||
                                     ED448_KEY_SIZE < (word16)cm->minEccKeySz) {
                    ret = ECC_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA ECC key size error");
                }
                break;
            #endif /* HAVE_ED448 */
            #if defined(HAVE_FALCON)
            case FALCON_LEVEL1k:
                if (cm->minFalconKeySz < 0 ||
                          FALCON_LEVEL1_KEY_SIZE < (word16)cm->minFalconKeySz) {
                    ret = FALCON_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Falcon level 1 key size error");
                }
                break;
            case FALCON_LEVEL5k:
                if (cm->minFalconKeySz < 0 ||
                          FALCON_LEVEL5_KEY_SIZE < (word16)cm->minFalconKeySz) {
                    ret = FALCON_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Falcon level 5 key size error");
                }
                break;
            #endif /* HAVE_FALCON */
            #if defined(HAVE_DILITHIUM)
            #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
            case DILITHIUM_LEVEL2k:
                if (cm->minDilithiumKeySz < 0 ||
                    DILITHIUM_LEVEL2_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 2 key size error");
                }
                break;
            case DILITHIUM_LEVEL3k:
                if (cm->minDilithiumKeySz < 0 ||
                    DILITHIUM_LEVEL3_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 3 key size error");
                }
                break;
            case DILITHIUM_LEVEL5k:
                if (cm->minDilithiumKeySz < 0 ||
                    DILITHIUM_LEVEL5_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 5 key size error");
                }
                break;
            #endif /* WOLFSSL_DILITHIUM_FIPS204_DRAFT */
            case ML_DSA_LEVEL2k:
                if (cm->minDilithiumKeySz < 0 ||
                    ML_DSA_LEVEL2_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 2 key size error");
                }
                break;
            case ML_DSA_LEVEL3k:
                if (cm->minDilithiumKeySz < 0 ||
                    ML_DSA_LEVEL3_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 3 key size error");
                }
                break;
            case ML_DSA_LEVEL5k:
                if (cm->minDilithiumKeySz < 0 ||
                    ML_DSA_LEVEL5_KEY_SIZE < (word16)cm->minDilithiumKeySz) {
                    ret = DILITHIUM_KEY_SIZE_E;
                    WOLFSSL_MSG("\tCA Dilithium level 5 key size error");
                }
                break;
            #endif /* HAVE_DILITHIUM */

            default:
                WOLFSSL_MSG("\tNo key size check done on CA");
                break; /* no size check if key type is not in switch */
        }
    }

    if (ret == 0 && cert->isCA == 0 && type != WOLFSSL_USER_CA &&
        type != WOLFSSL_TEMP_CA) {
        WOLFSSL_MSG("\tCan't add as CA if not actually one");
        ret = NOT_CA_ERROR;
    }
#ifndef ALLOW_INVALID_CERTSIGN
    else if (ret == 0 && cert->isCA == 1 && type != WOLFSSL_USER_CA &&
        type != WOLFSSL_TEMP_CA && !cert->selfSigned &&
        (cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) == 0) {
        /* Intermediate CA certs are required to have the keyCertSign
        * extension set. User loaded root certs are not. */
        WOLFSSL_MSG("\tDoesn't have key usage certificate signing");
        ret = NOT_CA_ERROR;
    }
#endif
    else if (ret == 0 && AlreadySigner(cm, subjectHash)) {
        WOLFSSL_MSG("\tAlready have this CA, not adding again");
        (void)ret;
    }
    else if (ret == 0) {
        /* take over signer parts */
        signer = MakeSigner(cm->heap);
        if (!signer)
            ret = MEMORY_ERROR;
    }
    if (ret == 0 && signer != NULL) {
        ret = FillSigner(signer, cert, type, der);

        if (ret == 0){
        #ifndef NO_SKID
            row = HashSigner(signer->subjectKeyIdHash);
        #else
            row = HashSigner(signer->subjectNameHash);
        #endif
        }

    #if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
        /* Verify CA by TSIP so that generated tsip key is going to          */
        /* be able to be used for peer's cert verification                   */
        /* TSIP is only able to handle USER CA, and only one CA.             */
        /* Therefore, it doesn't need to call TSIP again if there is already */
        /* verified CA.                                                      */
        if ( ret == 0 && signer != NULL ) {
            signer->cm_idx = row;
            if (type == WOLFSSL_USER_CA) {
                if ((ret = wc_Renesas_cmn_RootCertVerify(cert->source,
                        cert->maxIdx,
                        cert->sigCtx.CertAtt.pubkey_n_start,
                        cert->sigCtx.CertAtt.pubkey_n_len - 1,
                        cert->sigCtx.CertAtt.pubkey_e_start,
                        cert->sigCtx.CertAtt.pubkey_e_len - 1,
                     row/* cm index */))
                    < 0)
                    WOLFSSL_MSG("Renesas_RootCertVerify() failed");
                else
                    WOLFSSL_MSG("Renesas_RootCertVerify() succeed or skipped");
            }
        }
    #endif /* TSIP or SCE */

        if (ret == 0 && wc_LockMutex(&cm->caLock) == 0) {
            signer->next = cm->caTable[row];
            cm->caTable[row] = signer;   /* takes ownership */
            wc_UnLockMutex(&cm->caLock);
            if (cm->caCacheCallback)
                cm->caCacheCallback(der->buffer, (int)der->length, type);
        }
        else {
            WOLFSSL_MSG("\tCA Mutex Lock failed");
            ret = BAD_MUTEX_E;
        }
    }

    WOLFSSL_MSG("\tFreeing Parsed CA");
    FreeDecodedCert(cert);
    if (ret != 0 && signer != NULL)
        FreeSigner(signer, cm->heap);
    WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_DCERT);
    WOLFSSL_MSG("\tFreeing der CA");
    FreeDer(pDer);
    WOLFSSL_MSG("\t\tOK Freeing der CA");

    WOLFSSL_LEAVE("AddCA", ret);

    return ret == 0 ? WOLFSSL_SUCCESS : ret;
}

/* Removes the CA with the passed in subject hash from the
   cert manager's CA cert store. */
int RemoveCA(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type)
{
    Signer* current;
    Signer** prev;
    int     ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
    word32  row;

    WOLFSSL_MSG("Removing a CA");

    if (cm == NULL || hash == NULL) {
        return BAD_FUNC_ARG;
    }

    row = HashSigner(hash);

    if (wc_LockMutex(&cm->caLock) != 0) {
        return BAD_MUTEX_E;
    }
    current = cm->caTable[row];
    prev = &cm->caTable[row];
    while (current) {
        byte* subjectHash;

    #ifndef NO_SKID
        subjectHash = current->subjectKeyIdHash;
    #else
        subjectHash = current->subjectNameHash;
    #endif

        if ((current->type == type) &&
            (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0)) {
            *prev = current->next;
            FreeSigner(current, cm->heap);
            ret = WOLFSSL_SUCCESS;
            break;
        }
        prev = &current->next;
        current = current->next;
    }
    wc_UnLockMutex(&cm->caLock);

    WOLFSSL_LEAVE("RemoveCA", ret);

    return ret;
}

/* Sets the CA with the passed in subject hash
   to the provided type. */
int SetCAType(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type)
{
    Signer* current;
    int     ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
    word32  row;

    WOLFSSL_MSG_EX("Setting CA to type %d", type);

    if (cm == NULL || hash == NULL ||
        type < WOLFSSL_USER_CA || type > WOLFSSL_USER_INTER) {
        return ret;
    }

    row = HashSigner(hash);

    if (wc_LockMutex(&cm->caLock) != 0) {
        return ret;
    }
    current = cm->caTable[row];
    while (current) {
        byte* subjectHash;

    #ifndef NO_SKID
        subjectHash = current->subjectKeyIdHash;
    #else
        subjectHash = current->subjectNameHash;
    #endif

        if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) {
            current->type = (byte)type;
            ret = WOLFSSL_SUCCESS;
            break;
        }
        current = current->next;
    }
    wc_UnLockMutex(&cm->caLock);

    WOLFSSL_LEAVE("SetCAType", ret);

    return ret;
}

#endif /* NO_CERTS */

#endif /* !WOLFSSL_SSL_CERTMAN_INCLUDED */
