/* ocsp.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>

  /* Name change compatibility layer no longer needs to be included here */

/*
 * WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK:
 *     Disable looking for an authorized responder in the verification path of
 *     the issuer. This will make the authorized responder only look at the
 *     OCSP response signer and direct issuer.
 */

/*
 * OCSP responder missing features:
 * - Support for multiple requests and responses in a single OCSP exchange
 * - Support name-based responder ID
 * - Support for singleExtensions
 */

#ifndef WOLFCRYPT_ONLY
#ifdef HAVE_OCSP

#include <wolfssl/error-ssl.h>
#include <wolfssl/ocsp.h>
#include <wolfssl/internal.h>

#ifdef NO_INLINE
    #include <wolfssl/wolfcrypt/misc.h>
#else
    #define WOLFSSL_MISC_INCLUDED
    #include <wolfcrypt/src/misc.c>
#endif

/* Allocates and initializes a WOLFSSL_OCSP object. Returns pointer on success, NULL on failure. */
WOLFSSL_OCSP* wc_NewOCSP(WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_OCSP* ocsp = NULL;
    ocsp = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP);
    if (ocsp == NULL)
        return NULL;
    if (InitOCSP(ocsp, cm) != 0) {
        XFREE(ocsp, cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP);
        return NULL;
    }
    return ocsp;
}

/* Frees a WOLFSSL_OCSP object allocated by wc_NewOCSP. */
void wc_FreeOCSP(WOLFSSL_OCSP* ocsp)
{
    if (ocsp) {
        FreeOCSP(ocsp, 1);
    }
}

int wc_CheckCertOcspResponse(WOLFSSL_OCSP *ocsp, DecodedCert *cert,
        byte *response, int responseSz, void* heap)
{
    int ret = WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E);

#ifdef WOLFSSL_SMALL_STACK
    OcspRequest* ocspRequest;
#else
    OcspRequest ocspRequest[1];
#endif


#ifdef WOLFSSL_SMALL_STACK
    ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
    if (ocspRequest == NULL) {
        WOLFSSL_LEAVE("wc_CheckCertOcspResponse", MEMORY_ERROR);
        return MEMORY_E;
    }
#endif

    if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce,
                                                         ocsp->cm->heap) == 0) {
        ret = CheckOcspResponse(ocsp, response, responseSz, NULL, NULL, NULL,
                ocspRequest, heap);
        FreeOcspRequest(ocspRequest);
    }

#ifdef WOLFSSL_SMALL_STACK
    XFREE(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    WOLFSSL_LEAVE("wc_CheckCertOcspResponse", ret);
    return ret;
}

int InitOCSP(WOLFSSL_OCSP* ocsp, WOLFSSL_CERT_MANAGER* cm)
{
    WOLFSSL_ENTER("InitOCSP");

    ForceZero(ocsp, sizeof(WOLFSSL_OCSP));

    if (wc_InitMutex(&ocsp->ocspLock) != 0)
        return BAD_MUTEX_E;

    ocsp->cm = cm;

    return 0;
}


static int InitOcspEntry(OcspEntry* entry, OcspRequest* request)
{
    WOLFSSL_ENTER("InitOcspEntry");

    ForceZero(entry, sizeof(OcspEntry));

    XMEMCPY(entry->issuerHash,    request->issuerHash,    OCSP_DIGEST_SIZE);
    XMEMCPY(entry->issuerKeyHash, request->issuerKeyHash, OCSP_DIGEST_SIZE);

    return 0;
}


static void FreeOcspEntry(OcspEntry* entry, void* heap)
{
    CertStatus *status, *next;

    if (entry == NULL || !entry->ownStatus)
        return;

    WOLFSSL_ENTER("FreeOcspEntry");

    for (status = entry->status; status; status = next) {
        next = status->next;

        XFREE(status->rawOcspResponse, heap, DYNAMIC_TYPE_OCSP_STATUS);

#ifdef OPENSSL_EXTRA
        if (status->serialInt) {
            if (status->serialInt->isDynamic) {
                XFREE(status->serialInt->data, NULL, DYNAMIC_TYPE_OPENSSL);
            }
            XFREE(status->serialInt, NULL, DYNAMIC_TYPE_OPENSSL);
        }
        status->serialInt = NULL;
#endif

        XFREE(status, heap, DYNAMIC_TYPE_OCSP_STATUS);
    }

    (void)heap;
}


void FreeOCSP(WOLFSSL_OCSP* ocsp, int dynamic)
{
    OcspEntry *entry, *next;
    void* heap = (ocsp->cm != NULL) ? ocsp->cm->heap : NULL;

    WOLFSSL_ENTER("FreeOCSP");

    for (entry = ocsp->ocspList; entry; entry = next) {
        next = entry->next;
        FreeOcspEntry(entry, heap);
        XFREE(entry, heap, DYNAMIC_TYPE_OCSP_ENTRY);
    }

    wc_FreeMutex(&ocsp->ocspLock);

    if (dynamic)
        XFREE(ocsp, heap, DYNAMIC_TYPE_OCSP);

}


static int xstat2err(int st)
{
    switch (st) {
        case CERT_GOOD:
            return 0;
        case CERT_REVOKED:
            return OCSP_CERT_REVOKED;
        default:
            return OCSP_CERT_UNKNOWN;
    }
}

int CheckCertOCSP_ex(WOLFSSL_OCSP* ocsp, DecodedCert* cert, WOLFSSL* ssl)
{
    int ret = WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL);

    WC_DECLARE_VAR(ocspRequest, OcspRequest, 1, 0);

    WOLFSSL_ENTER("CheckCertOCSP");


#ifdef WOLFSSL_SMALL_STACK
    ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
    if (ocspRequest == NULL) {
        WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR);
        return MEMORY_E;
    }
#endif

    if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce,
                                                         ocsp->cm->heap) == 0) {
        ocspRequest->ssl = ssl;
        ret = CheckOcspRequest(ocsp, ocspRequest, NULL, NULL);

        FreeOcspRequest(ocspRequest);
    }

    WC_FREE_VAR_EX(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    WOLFSSL_LEAVE("CheckCertOCSP", ret);
    return ret;
}
int CheckCertOCSP(WOLFSSL_OCSP* ocsp, DecodedCert* cert)
{
    return CheckCertOCSP_ex(ocsp, cert, NULL);
}

static int GetOcspEntry(WOLFSSL_OCSP* ocsp, OcspRequest* request,
                                                              OcspEntry** entry)
{
    WOLFSSL_ENTER("GetOcspEntry");

    *entry = NULL;

    if (wc_LockMutex(&ocsp->ocspLock) != 0) {
        WOLFSSL_LEAVE("GetOcspEntry", BAD_MUTEX_E);
        return BAD_MUTEX_E;
    }

    for (*entry = ocsp->ocspList; *entry; *entry = (*entry)->next)
        if (XMEMCMP((*entry)->issuerHash,    request->issuerHash,
                                                         OCSP_DIGEST_SIZE) == 0
        &&  XMEMCMP((*entry)->issuerKeyHash, request->issuerKeyHash,
                                                         OCSP_DIGEST_SIZE) == 0)
            break;

    if (*entry == NULL) {
        *entry = (OcspEntry*)XMALLOC(sizeof(OcspEntry),
                                       ocsp->cm->heap, DYNAMIC_TYPE_OCSP_ENTRY);
        if (*entry) {
            InitOcspEntry(*entry, request);
            (*entry)->next = ocsp->ocspList;
            ocsp->ocspList = *entry;
        }
    }

    wc_UnLockMutex(&ocsp->ocspLock);

    return *entry ? 0 : MEMORY_ERROR;
}


/* Mallocs responseBuffer->buffer and is up to caller to free on success
 *
 * Returns OCSP status
 */
static int GetOcspStatus(WOLFSSL_OCSP* ocsp, OcspRequest* request,
                  OcspEntry* entry, CertStatus** status, buffer* responseBuffer,
                  void* heap)
{
    int ret = WC_NO_ERR_TRACE(OCSP_INVALID_STATUS);

    WOLFSSL_ENTER("GetOcspStatus");

    (void)heap;
    *status = NULL;

    if (wc_LockMutex(&ocsp->ocspLock) != 0) {
        WOLFSSL_LEAVE("GetOcspStatus", BAD_MUTEX_E);
        return BAD_MUTEX_E;
    }

    for (*status = entry->status; *status; *status = (*status)->next)
        if ((*status)->serialSz == request->serialSz
        &&  !XMEMCMP((*status)->serial, request->serial, (size_t)(*status)->serialSz))
            break;

    if (responseBuffer && *status && !(*status)->rawOcspResponse) {
        /* force fetching again */
        ret = OCSP_INVALID_STATUS;
    }
    else if (*status) {
#ifndef NO_ASN_TIME
        if (XVALIDATE_DATE((*status)->thisDate,
                           (*status)->thisDateFormat, ASN_BEFORE, MAX_DATE_SIZE)
        &&  ((*status)->nextDate[0] != 0)
        &&  XVALIDATE_DATE((*status)->nextDate,
                           (*status)->nextDateFormat, ASN_AFTER, MAX_DATE_SIZE))
#endif
        {
            ret = xstat2err((*status)->status);

            if (responseBuffer) {
                responseBuffer->buffer = (byte*)XMALLOC(
                   (*status)->rawOcspResponseSz, heap,
                   DYNAMIC_TYPE_TMP_BUFFER);

                if (responseBuffer->buffer) {
                    responseBuffer->length = (*status)->rawOcspResponseSz;
                    XMEMCPY(responseBuffer->buffer,
                            (*status)->rawOcspResponse,
                            (*status)->rawOcspResponseSz);
                }
            }
        }
    }

    wc_UnLockMutex(&ocsp->ocspLock);

    return ret;
}

/* Check that the response for validity. Store result in status.
 *
 * ocsp           Context object for OCSP status.
 * response       OCSP response message data.
 * responseSz     Length of OCSP response message data.
 * responseBuffer Buffer object to return the response with.
 * status         The certificate status object.
 * entry          The OCSP entry for this certificate.
 * ocspRequest    Request corresponding to response.
 * heap           Heap hint used for responseBuffer
 * returns OCSP_LOOKUP_FAIL when the response is bad and 0 otherwise.
 */
int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int responseSz,
                      WOLFSSL_BUFFER_INFO *responseBuffer, CertStatus *status,
                      OcspEntry *entry, OcspRequest *ocspRequest, void* heap)
{
#ifdef WOLFSSL_SMALL_STACK
    CertStatus*   newStatus;
    OcspEntry*    newSingle;
    OcspResponse* ocspResponse;
#else
    CertStatus    newStatus[1];
    OcspEntry     newSingle[1];
    OcspResponse  ocspResponse[1];
#endif
    int           ret;
    int           validated      = 0;    /* ocsp validation flag */

    (void)heap;

#ifdef WOLFSSL_SMALL_STACK
    newStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
                                                       DYNAMIC_TYPE_OCSP_STATUS);
    newSingle = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
                                                       DYNAMIC_TYPE_OCSP_ENTRY);
    ocspResponse = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL,
                                                       DYNAMIC_TYPE_OCSP_REQUEST);

    if (newStatus == NULL || newSingle == NULL || ocspResponse == NULL) {
        XFREE(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS);
        XFREE(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
        XFREE(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST);

        WOLFSSL_LEAVE("CheckOcspResponse", MEMORY_ERROR);
        return MEMORY_E;
    }
#endif
    InitOcspResponse(ocspResponse, newSingle, newStatus, response,
                     (word32)responseSz, ocsp->cm->heap);
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_TLS)
    if (ocspRequest != NULL && ocspRequest->ssl != NULL &&
           TLSX_CSR2_IsMulti(((WOLFSSL*)ocspRequest->ssl)->extensions)) {
        ocspResponse->pendingCAs = TLSX_CSR2_GetPendingSigners(((WOLFSSL*)ocspRequest->ssl)->extensions);
    }
#endif
    ret = OcspResponseDecode(ocspResponse, ocsp->cm, ocsp->cm->heap, 0, 0);
    if (ret != 0) {
        ocsp->error = ret;
        WOLFSSL_LEAVE("OcspResponseDecode failed", ocsp->error);
        goto end;
    }

    if (ocspResponse->responseStatus != OCSP_SUCCESSFUL) {
        WOLFSSL_MSG("OcspResponse status bad");
        goto end;
    }
    if (ocspRequest != NULL) {
        /* Has the chance to bubble up response changing ocspResponse->single to
           no longer be pointing at newSingle */
        ret = CompareOcspReqResp(ocspRequest, ocspResponse);
        if (ret != 0) {
            goto end;
        }
    }

    if (responseBuffer) {
        responseBuffer->buffer = (byte*)XMALLOC((size_t)responseSz, heap,
                                                DYNAMIC_TYPE_TMP_BUFFER);

        if (responseBuffer->buffer) {
            responseBuffer->length = (unsigned int)responseSz;
            XMEMCPY(responseBuffer->buffer, response, (size_t)responseSz);
        }
    }

    ret = xstat2err(ocspResponse->single->status->status);
    if (ret == 0) {
        validated = 1;
    }

    if (wc_LockMutex(&ocsp->ocspLock) != 0) {
        ret = BAD_MUTEX_E;
        goto end;
    }

    if (status != NULL) {
        XFREE(status->rawOcspResponse, ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS);

        /* Replace existing certificate entry with updated */
        ocspResponse->single->status->next = status->next;
        XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus));
    }
    else if (entry != NULL) {
        /* Save new certificate entry */
        status = (CertStatus*)XMALLOC(sizeof(CertStatus),
                                      ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS);
        if (status != NULL) {
            XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus));
            status->next  = entry->status;
            entry->status = status;
            entry->ownStatus = 1;
            entry->totalStatus++;
        }
    }

    if (status && responseBuffer && responseBuffer->buffer) {
        status->rawOcspResponse = (byte*)XMALLOC(responseBuffer->length,
                                                 ocsp->cm->heap,
                                                 DYNAMIC_TYPE_OCSP_STATUS);

        if (status->rawOcspResponse) {
            status->rawOcspResponseSz = responseBuffer->length;
            XMEMCPY(status->rawOcspResponse, responseBuffer->buffer,
                    responseBuffer->length);
        }
    }

    wc_UnLockMutex(&ocsp->ocspLock);

end:
    if (ret == 0 && validated == 1) {
        WOLFSSL_MSG("New OcspResponse validated");
    }
    else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED)) {
        WOLFSSL_MSG("OCSP revoked");
    }
    else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN)) {
        WOLFSSL_MSG("OCSP unknown");
    }
    else {
        WOLFSSL_MSG("OCSP lookup failure");
        ret = OCSP_LOOKUP_FAIL;
    }

    FreeOcspResponse(ocspResponse);
    WC_FREE_VAR_EX(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS);
    WC_FREE_VAR_EX(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
    WC_FREE_VAR_EX(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
    return ret;
}

/* 0 on success */
/* allow user to override the maximum request size at build-time */
#ifndef OCSP_MAX_REQUEST_SZ
#define OCSP_MAX_REQUEST_SZ 2048
#endif
int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
                     buffer* responseBuffer, void* heap)
{
    OcspEntry*  entry          = NULL;
    CertStatus* status         = NULL;
    byte*       request        = NULL;
    int         requestSz      = OCSP_MAX_REQUEST_SZ;
    int         responseSz     = 0;
    byte*       response       = NULL;
    const char* url            = NULL;
    int         urlSz          = 0;
    int         ret            = -1;
    WOLFSSL*    ssl;
    void*       ioCtx;

    WOLFSSL_ENTER("CheckOcspRequest");

    if (ocsp == NULL || ocspRequest == NULL)
        return BAD_FUNC_ARG;

    if (responseBuffer) {
        responseBuffer->buffer = NULL;
        responseBuffer->length = 0;
    }

    ret = GetOcspEntry(ocsp, ocspRequest, &entry);
    if (ret != 0)
        return ret;

    ret = GetOcspStatus(ocsp, ocspRequest, entry, &status, responseBuffer,
                        heap);
    if (ret != WC_NO_ERR_TRACE(OCSP_INVALID_STATUS))
        return ret;

    if (responseBuffer) {
        XFREE(responseBuffer->buffer, heap, DYNAMIC_TYPE_TMP_BUFFER);
        responseBuffer->buffer = NULL;
    }

    /* get SSL and IOCtx */
    ssl = (WOLFSSL*)ocspRequest->ssl;
    ioCtx = (ssl && ssl->ocspIOCtx != NULL) ?
                                        ssl->ocspIOCtx : ocsp->cm->ocspIOCtx;

    if (ocsp->cm->ocspUseOverrideURL) {
        url = ocsp->cm->ocspOverrideURL;
        if (url != NULL && url[0] != '\0')
            urlSz = (int)XSTRLEN(url);
        else
            return OCSP_NEED_URL;
    }
    else if (ocspRequest->urlSz != 0 && ocspRequest->url != NULL) {
        url = (const char *)ocspRequest->url;
        urlSz = ocspRequest->urlSz;
    }
    else {
        /* cert doesn't have extAuthInfo, assuming CERT_GOOD */
        WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD");
        return 0;
    }

    request = (byte*)XMALLOC((size_t)requestSz, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);
    if (request == NULL) {
        WOLFSSL_LEAVE("CheckOcspRequest", MEMORY_ERROR);
        return MEMORY_ERROR;
    }

    requestSz = EncodeOcspRequest(ocspRequest, request, (word32)requestSz);
    if (requestSz > 0 && ocsp->cm->ocspIOCb) {
        responseSz = ocsp->cm->ocspIOCb(ioCtx, url, urlSz,
                                        request, requestSz, &response);
    }
    if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ)) {
        ret = OCSP_WANT_READ;
    }
    else if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_TIMEOUT)){
        ret = HTTP_TIMEOUT;
    }

    XFREE(request, ocsp->cm->heap, DYNAMIC_TYPE_OCSP);

    if (responseSz >= 0 && response) {
        ret = CheckOcspResponse(ocsp, response, responseSz, responseBuffer, status,
                            entry, ocspRequest, heap);
    }

    if (response != NULL && ocsp->cm->ocspRespFreeCb)
        ocsp->cm->ocspRespFreeCb(ioCtx, response);

    /* Keep responseBuffer in the case of getting to response check. Caller
     * should free responseBuffer after checking OCSP return value in "ret" */
    WOLFSSL_LEAVE("CheckOcspRequest", ret);
    return ret;
}

#ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK
static int CheckOcspResponderChain(OcspEntry* single, byte* issuerHash,
        void* vp, Signer* pendingCAs) {
    /* Attempt to build a chain up to cert's issuer */
    WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp;
    Signer* ca = NULL;
    Signer* prev = NULL;
    int passed = 0;

    /*
     *       Relation between certs:
     *                 CA
     *        /                 \
     *  intermediate(s)   cert in OCSP response
     *        |           with OCSP key usage ext
     *  issuer of cert
     *  in OCSP request
     */

    /* End loop if no more issuers found or if we have found a self
     * signed cert (ca == prev) */
    ca = GetCAByName(cm, single->issuerHash);
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (ca == NULL && pendingCAs != NULL) {
        ca = findSignerByName(pendingCAs, single->issuerHash);
    }
#else
    (void)pendingCAs;
#endif
    for (; ca != NULL && ca != prev;
            prev = ca) {
        if (XMEMCMP(issuerHash, ca->issuerNameHash, OCSP_DIGEST_SIZE) == 0) {
            WOLFSSL_MSG("\tOCSP Response signed by authorized "
                    "responder delegated by issuer "
                    "(found in chain)");
            passed = 1;
            break;
        }
        ca = GetCAByName(cm, ca->issuerNameHash);
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
        if (ca == NULL && pendingCAs != NULL) {
            ca = findSignerByName(pendingCAs, single->issuerHash);
        }
#endif
    }
    return passed;
}
#endif

/**
 * Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2
 * @param bs              The basic OCSP response to verify
 * @param subjectHash     The subject key hash of the OCSP responder certificate
 * @param extExtKeyUsage  The extended key usage bits of the responder certificate
 * @param issuerHash      The issuer name hash of the OCSP responder certificate
 * @param vp              Unused (reserved for future use)
 * @return 1 if the responder is authorized to sign the response, 0 otherwise
 */
int CheckOcspResponder(OcspResponse *bs, byte* subjectHash,
        byte extExtKeyUsage, byte* issuerHash, void* vp)
{
    int ret = 0;
    OcspEntry* single;

    /* Both evaluate to enum values so can't use a pre-processor check */
    WOLFSSL_ASSERT_EQ(OCSP_DIGEST_SIZE, SIGNER_DIGEST_SIZE);

    (void)vp;

    WOLFSSL_ENTER("CheckOcspResponder");

    /* In the future if this API is used more then it could be beneficial to
     * implement calling InitDecodedCert and ParseCertRelative here
     * automatically when cert == NULL. */
    if (bs == NULL || subjectHash == NULL || issuerHash == NULL)
        return BAD_FUNC_ARG;

    /* Traverse the list and check that the cert has the authority to provide
     * an OCSP response for each entry. */
    for (single = bs->single; single != NULL; single = single->next) {
        int passed = 0;

        if (XMEMCMP(subjectHash, single->issuerHash, OCSP_DIGEST_SIZE) == 0) {
            WOLFSSL_MSG("\tOCSP Response signed by issuer");
            passed = 1;
        }
        else if ((extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) {
            if (XMEMCMP(issuerHash, single->issuerHash, OCSP_DIGEST_SIZE)
                    == 0) {
                WOLFSSL_MSG("\tOCSP Response signed by authorized responder "
                            "delegated by issuer");
                passed = 1;
            }
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK
            else if (vp != NULL) {
                passed = CheckOcspResponderChain(single, issuerHash, vp,
                        bs->pendingCAs);
            }
#endif
        }

        if (!passed) {
            WOLFSSL_MSG("\tOCSP Responder not authorized");
            ret = BAD_OCSP_RESPONDER;
            break;
        }
    }
    return ret;
}


OcspRequest* wc_OcspRequest_new(void* heap)
{
    OcspRequest* request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), heap,
                                                 DYNAMIC_TYPE_OCSP_REQUEST);
    if (request != NULL) {
        XMEMSET(request, 0, sizeof(OcspRequest));
        request->heap = heap;
    }

    return request;
}

void wc_OcspRequest_free(OcspRequest* request)
{
    if (request != NULL) {
        FreeOcspRequest(request);
        XFREE(request, request->heap, DYNAMIC_TYPE_OCSP_REQUEST);
    }
}

OcspResponse* wc_OcspResponse_new(void* heap)
{
    OcspResponse* response = (OcspResponse*)XMALLOC(sizeof(OcspResponse),
                                                   heap,
                                                   DYNAMIC_TYPE_OCSP_RESPONSE);
    if (response != NULL) {
        XMEMSET(response, 0, sizeof(OcspResponse));
        response->heap = heap;
    }

    return response;
}

void wc_OcspResponse_free(OcspResponse* response)
{
    if (response != NULL) {
        FreeOcspResponse(response);
        XFREE(response, response->heap, DYNAMIC_TYPE_OCSP_RESPONSE);
    }
}

/* compatibility layer OCSP functions */
#ifdef OPENSSL_EXTRA
int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs,
    WOLFSSL_OCSP_CERTID* id, int* status, int* reason,
    WOLFSSL_ASN1_TIME** revtime, WOLFSSL_ASN1_TIME** thisupd,
    WOLFSSL_ASN1_TIME** nextupd)
{
    WOLFSSL_OCSP_SINGLERESP* single;

    if (bs == NULL || id == NULL)
        return WOLFSSL_FAILURE;

    single = bs->single;
    while (single != NULL) {
        if ((XMEMCMP(single->status->serial, id->status->serial, (size_t)single->status->serialSz) == 0)
         && (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0)
         && (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) {
            break;
        }
        single = single->next;
    }

    if (single == NULL)
        return WOLFSSL_FAILURE;

    if (status != NULL)
        *status = single->status->status;
#ifdef WOLFSSL_OCSP_PARSE_STATUS
    if (thisupd != NULL)
        *thisupd = &single->status->thisDateParsed;
    if (nextupd != NULL)
        *nextupd = &single->status->nextDateParsed;
#else
    if (thisupd != NULL)
        *thisupd = NULL;
    if (nextupd != NULL)
        *nextupd = NULL;
#endif

    /* TODO: Not needed for Nginx or httpd */
    if (reason != NULL)
        *reason = 0;
    if (revtime != NULL)
        *revtime = NULL;

    return WOLFSSL_SUCCESS;
}

const char *wolfSSL_OCSP_cert_status_str(long s)
{
    switch (s) {
        case CERT_GOOD:
            return "good";
        case CERT_REVOKED:
            return "revoked";
        case CERT_UNKNOWN:
            return "unknown";
        default:
            return "(UNKNOWN)";
    }
}

int wolfSSL_OCSP_check_validity(WOLFSSL_ASN1_TIME* thisupd,
    WOLFSSL_ASN1_TIME* nextupd, long sec, long maxsec)
{
    (void)thisupd;
    (void)nextupd;
    (void)sec;
    (void)maxsec;
    /* Dates validated in DecodeSingleResponse. */
    return WOLFSSL_SUCCESS;
}

void wolfSSL_OCSP_CERTID_free(WOLFSSL_OCSP_CERTID* certId)
{
    FreeOcspEntry(certId, NULL);
    XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL);
}

WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_cert_to_id(
    const WOLFSSL_EVP_MD *dgst, const WOLFSSL_X509 *subject,
    const WOLFSSL_X509 *issuer)
{
    WOLFSSL_OCSP_CERTID* certId = NULL;
    CertStatus* certStatus = NULL;
    WOLFSSL_CERT_MANAGER* cm = NULL;
    int ret = -1;
    DerBuffer* derCert = NULL;
    int dgstType;
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

    if (dgst == NULL) {
        dgstType = WC_HASH_TYPE_SHA;
    }
    else if (wolfSSL_EVP_get_hashinfo(dgst, &dgstType, NULL) !=
             WOLFSSL_SUCCESS) {
        return NULL;
    }

    if (dgstType != OCSP_DIGEST)
        return NULL;

    cm = wolfSSL_CertManagerNew();
    if (cm == NULL
            || subject == NULL || subject->derCert == NULL
            || issuer  == NULL || issuer->derCert  == NULL)
        goto out;

    WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT,
        goto out);

    ret = AllocDer(&derCert, issuer->derCert->length,
        issuer->derCert->type, NULL);
    if (ret == 0) {
        /* AddCA() frees the buffer. */
        XMEMCPY(derCert->buffer, issuer->derCert->buffer,
                issuer->derCert->length);
        ret = AddCA(cm, &derCert, WOLFSSL_USER_CA, 1);
        if (ret != WOLFSSL_SUCCESS) {
            goto out;
        }
        derCert = NULL;
    }

    ret = -1;

    certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID),
                                           cm->heap, DYNAMIC_TYPE_OPENSSL);
    if (certId == NULL)
        goto out;

    certStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), cm->heap,
                                           DYNAMIC_TYPE_OPENSSL);
    if (certStatus == NULL)
        goto out;

    XMEMSET(certId, 0, sizeof(WOLFSSL_OCSP_CERTID));
    XMEMSET(certStatus, 0, sizeof(CertStatus));

    certId->status = certStatus;
    certId->ownStatus = 1;

    InitDecodedCert(cert, subject->derCert->buffer,
                    subject->derCert->length, NULL);
    if (ParseCertRelative(cert, CERT_TYPE, VERIFY_OCSP, cm, NULL) != 0) {
        FreeDecodedCert(cert);
        goto out;
    }
    else {
        certId->hashAlgoOID = wc_HashGetOID(OCSP_DIGEST);
        XMEMCPY(certId->issuerHash, cert->issuerHash, OCSP_DIGEST_SIZE);
        XMEMCPY(certId->issuerKeyHash, cert->issuerKeyHash, OCSP_DIGEST_SIZE);
        XMEMCPY(certId->status->serial, cert->serial, (size_t)cert->serialSz);
        certId->status->serialSz = cert->serialSz;
        FreeDecodedCert(cert);
    }

    ret = 0;

out:

    if (ret != 0) {
        if (derCert != NULL)
            FreeDer(&derCert);
        if (cm != NULL) {
            XFREE(certId, cm->heap, DYNAMIC_TYPE_OPENSSL);
            certId = NULL;
            XFREE(certStatus, cm->heap, DYNAMIC_TYPE_OPENSSL);
        }
    }

#ifdef WOLFSSL_SMALL_STACK
    if (cm != NULL)
        XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT);
#endif

    if (cm != NULL)
        wolfSSL_CertManagerFree(cm);

    return certId;
}

void wolfSSL_OCSP_BASICRESP_free(WOLFSSL_OCSP_BASICRESP* basicResponse)
{
    wolfSSL_OCSP_RESPONSE_free(basicResponse);
}

/* Calculate ancode CertID DER encoding following RFC 6960:
   CertID ::= SEQUENCE {
       hashAlgorithm       AlgorithmIdentifier,
       issuerNameHash      OCTET STRING,
       issuerKeyHash       OCTET STRING,
       serialNumber        CertificateSerialNumber }
*/
static int OcspEncodeCertID(WOLFSSL_OCSP_CERTID* id, byte* output,
    word32* totalSz, word32* intSize)
{
    word32 idx = 0;
    int ret;

    if (id == NULL || totalSz == NULL || intSize == NULL ||
        (output != NULL && (*totalSz == 0 || *totalSz <= *intSize)))
        return BAD_FUNC_ARG;

    if (output != NULL) {
        ret = SetSequence(*intSize, output);
        if (ret < 0)
            return ret;
        idx += ret;
    }

    ret = SetAlgoID(id->hashAlgoOID, ((output != NULL) ? output + idx : output),
        oidHashType, 0);
    if (ret <= 0)
        return -1;
    idx += ret;

    /* issuerNameHash */
    ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output));
    if (ret < 0)
        return ret;
    idx += ret;
    if (output != NULL)
        XMEMCPY(output + idx, id->issuerHash, OCSP_DIGEST_SIZE);
    idx += OCSP_DIGEST_SIZE;

    /* issuerKeyHash */
    ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output));
    if (ret < 0)
        return ret;
    idx += ret;
    if (output != NULL)
        XMEMCPY(output + idx, id->issuerKeyHash, OCSP_DIGEST_SIZE);
    idx += OCSP_DIGEST_SIZE;

    /* serialNumber */
    ret = SetASNInt(id->status->serialSz, id->status->serial[0], ((output != NULL) ? output + idx : output));
    if (ret < 0)
        return ret;
    idx += ret;
    if (output != NULL)
        XMEMCPY(output + idx, id->status->serial, id->status->serialSz);
    idx += id->status->serialSz;

    if (output == NULL) {
        *intSize = idx;
        ret = SetSequence(idx, NULL);
        if (ret < 0)
            return ret;
        idx += ret;
        *totalSz = idx;
    }
    else if (idx != *totalSz) {
        return BUFFER_E;
    }

    return 0;
}

static int OcspRespIdMatches(OcspResponse* resp, const byte* NameHash,
    const byte* keyHash)
{
    if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
        return XMEMCMP(NameHash, resp->responderId.nameHash,
                   SIGNER_DIGEST_SIZE) == 0;
    }
    else if (resp->responderIdType == OCSP_RESPONDER_ID_KEY) {
        return XMEMCMP(keyHash, resp->responderId.keyHash,
                   OCSP_RESPONDER_ID_KEY_SZ) == 0;
    }

    return 0;
}

static int OcspFindSigner(WOLFSSL_OCSP_BASICRESP *resp,
    WOLF_STACK_OF(WOLFSSL_X509) *certs, DecodedCert **signer, int *embedded,
    unsigned long flags)
{
    WOLFSSL_X509 *signer_x509 = NULL;
    DecodedCert *certDecoded;
    int i;

    certDecoded = (DecodedCert *)XMALLOC(sizeof(*certDecoded), resp->heap,
                                            DYNAMIC_TYPE_DCERT);
    if (certDecoded == NULL)
        return MEMORY_E;

    for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) {
        signer_x509 = wolfSSL_sk_X509_value(certs, i);
        if (signer_x509 == NULL)
            continue;

        InitDecodedCert(certDecoded, signer_x509->derCert->buffer,
                       signer_x509->derCert->length, resp->heap);
        if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY,
                NULL, NULL) == 0) {
                if (OcspRespIdMatches(resp, certDecoded->subjectHash,
                        certDecoded->subjectKeyHash)) {
                    *signer = certDecoded;
                    *embedded = 0;
                    return 0;
                }
        }
        FreeDecodedCert(certDecoded);
    }

    if (flags & WOLFSSL_OCSP_NOINTERN) {
        XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT);
        return ASN_NO_SIGNER_E;
    }

    /* not found in certs, search the cert embedded in the response */
    InitDecodedCert(certDecoded, resp->cert, resp->certSz, resp->heap);
    if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY, NULL, NULL) == 0) {
        if (OcspRespIdMatches(resp, certDecoded->subjectHash,
                certDecoded->subjectKeyHash)) {
            *signer = certDecoded;
            *embedded = 1;
            return 0;
        }
    }
    FreeDecodedCert(certDecoded);

    XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT);
    return ASN_NO_SIGNER_E;
}

static int OcspVerifySigner(WOLFSSL_OCSP_BASICRESP *resp, DecodedCert *cert,
     WOLFSSL_X509_STORE *st, unsigned long flags)
{
    WC_DECLARE_VAR(c, DecodedCert, 1, 0);

    int ret = -1;
    if (st == NULL)
        return ASN_OCSP_CONFIRM_E;

    WC_ALLOC_VAR_EX(c, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT,
        return MEMORY_E);

    InitDecodedCert(c, cert->source, cert->maxIdx, NULL);
    if (ParseCertRelative(c, CERT_TYPE, VERIFY, st->cm, NULL) != 0) {
        ret = ASN_OCSP_CONFIRM_E;
        goto err;
    }
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
    if ((flags & WOLFSSL_OCSP_NOCHECKS) == 0) {
        ret = CheckOcspResponder(resp, c->subjectHash, c->extExtKeyUsage,
                c->issuerHash, st->cm);
    }
    else {
        ret = 0;
    }
#else
    (void)resp;
    (void)flags;
    ret = 0;
#endif

err:
    FreeDecodedCert(c);
    WC_FREE_VAR_EX(c, NULL, DYNAMIC_TYPE_DCERT);
    return ret;
}
/* Signature verified in DecodeBasicOcspResponse.
 * But no store available to verify certificate. */
int wolfSSL_OCSP_basic_verify(WOLFSSL_OCSP_BASICRESP* bs,
    WOLF_STACK_OF(WOLFSSL_X509) * certs, WOLFSSL_X509_STORE* st,
    unsigned long flags)
{
    int         ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
    int embedded;
    DecodedCert *cert = NULL;

    ret = OcspFindSigner(bs, certs, &cert, &embedded, flags);
    if (ret != 0) {
        WOLFSSL_MSG("OCSP no signer found");
        return WOLFSSL_FAILURE;
    }

    /* skip certificate verification if cert in certs and TRUST_OTHER is true */
    if (!embedded && (flags & WOLFSSL_OCSP_TRUSTOTHER) != 0)
        flags |= WOLFSSL_OCSP_NOVERIFY;

    /* verify response signature */
    ret = ConfirmSignature(
        &cert->sigCtx,
        bs->response, bs->responseSz,
        cert->publicKey, cert->pubKeySize, cert->keyOID,
        bs->sig, bs->sigSz, bs->sigOID, bs->sigParams, bs->sigParamsSz,
        NULL);

    if (ret != 0) {
        WOLFSSL_MSG("OCSP signature verification failed");
        ret = -1;
        goto err;
    }

    if ((flags & WOLFSSL_OCSP_NOVERIFY) == 0) {
        ret = OcspVerifySigner(bs, cert, st, flags);
    }

err:
    FreeDecodedCert(cert);
    XFREE(cert, NULL, DYNAMIC_TYPE_DCERT);
    return ret == 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
}

void wolfSSL_OCSP_RESPONSE_free(OcspResponse* response)
{
    OcspEntry *s, *sNext;
    if (response == NULL)
        return;


    s = response->single;
    while (s != NULL) {
        sNext = s->next;
        FreeOcspEntry(s, NULL);
        XFREE(s, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
        s = sNext;
    }

    XFREE(response->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    XFREE(response, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
}

#ifndef NO_BIO
OcspResponse* wolfSSL_d2i_OCSP_RESPONSE_bio(WOLFSSL_BIO* bio,
    OcspResponse** response)
{
    byte*         data;
    byte*         p;
    int           len;
    int           dataAlloced = 0;
    OcspResponse* ret = NULL;

    if (bio == NULL)
        return NULL;

    if (bio->type == WOLFSSL_BIO_MEMORY) {
        len = wolfSSL_BIO_get_mem_data(bio, &data);
        if (len <= 0 || data == NULL) {
            return NULL;
        }
    }
#ifndef NO_FILESYSTEM
    else if (bio->type == WOLFSSL_BIO_FILE) {
        long fcur;
        long flen;

        if (bio->ptr.fh == NULL)
            return NULL;

        fcur = XFTELL(bio->ptr.fh);
        if (fcur < 0)
            return NULL;
        if(XFSEEK(bio->ptr.fh, 0, SEEK_END) != 0)
            return NULL;
        flen = XFTELL(bio->ptr.fh);
        if (flen < 0)
            return NULL;
        if (XFSEEK(bio->ptr.fh, fcur, SEEK_SET) != 0)
            return NULL;

        /* check calculated length */
        fcur = flen - fcur;
        if (fcur > MAX_WOLFSSL_FILE_SIZE || fcur <= 0)
            return NULL;

        data = (byte*)XMALLOC((size_t)fcur, 0, DYNAMIC_TYPE_TMP_BUFFER);
        if (data == NULL)
            return NULL;
        dataAlloced = 1;

        len = wolfSSL_BIO_read(bio, (char *)data, (int)flen);
    }
#endif
    else
        return NULL;

    if (len > 0) {
        p = data;
        ret = wolfSSL_d2i_OCSP_RESPONSE(response, (const unsigned char **)&p,
            len);
    }

    if (dataAlloced)
        XFREE(data, 0, DYNAMIC_TYPE_TMP_BUFFER);

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

OcspResponse* wolfSSL_d2i_OCSP_RESPONSE(OcspResponse** response,
    const unsigned char** data, int len)
{
    OcspResponse *resp = NULL;
    word32 idx = 0;
    int length = 0;
    int ret;

    if (data == NULL)
        return NULL;

    if (response != NULL)
        resp = *response;
    if (resp == NULL) {
        resp = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL,
                                      DYNAMIC_TYPE_OCSP_REQUEST);
        if (resp == NULL)
            return NULL;
        XMEMSET(resp, 0, sizeof(OcspResponse));
    }

    resp->source = (byte*)XMALLOC((size_t)len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    if (resp->source == NULL) {
        XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
        return NULL;
    }
    resp->single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
                                      DYNAMIC_TYPE_OCSP_ENTRY);
    if (resp->single == NULL) {
        XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
        return NULL;
    }
    XMEMSET(resp->single, 0, sizeof(OcspEntry));
    resp->single->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
                                      DYNAMIC_TYPE_OCSP_STATUS);
    resp->single->ownStatus = 1;
    if (resp->single->status == NULL) {
        XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(resp->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
        XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST);
        return NULL;
    }
    XMEMSET(resp->single->status, 0, sizeof(CertStatus));

    XMEMCPY(resp->source, *data, (size_t)len);
    resp->maxIdx = (word32)len;

    ret = OcspResponseDecode(resp, NULL, NULL, 1, 1);
    if (ret != 0 && ret != WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E)) {
        /* for just converting from a DER to an internal structure the CA may
         * not yet be known to this function for signature verification */
        wolfSSL_OCSP_RESPONSE_free(resp);
        return NULL;
    }

    if (GetSequence(*data, &idx, &length, (word32)len) >= 0)
        (*data) += idx + length;

    if (response != NULL && *response == NULL)
        *response = resp;

    return resp;
}

int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response,
    unsigned char** data)
{
    if (response == NULL)
        return BAD_FUNC_ARG;

    if (data == NULL)
        return (int)response->maxIdx;

    XMEMCPY(*data, response->source, response->maxIdx);
    return (int)response->maxIdx;
}

int wolfSSL_OCSP_response_status(OcspResponse *response)
{
    return response->responseStatus;
}

const char *wolfSSL_OCSP_response_status_str(long s)
{
    switch (s) {
        case OCSP_SUCCESSFUL:
            return "successful";
        case OCSP_MALFORMED_REQUEST:
            return "malformedrequest";
        case OCSP_INTERNAL_ERROR:
            return "internalerror";
        case OCSP_TRY_LATER:
            return "trylater";
        case OCSP_SIG_REQUIRED:
            return "sigrequired";
        case OCSP_UNAUTHORIZED:
            return "unauthorized";
        default:
            return "(UNKNOWN)";
    }
}

WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response)
{
    WOLFSSL_OCSP_BASICRESP* bs;
    const unsigned char *ptr = response->source;

    bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx);
    return bs;
}

OcspRequest* wolfSSL_OCSP_REQUEST_new(void)
{
    OcspRequest* request;

    request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL,
                                    DYNAMIC_TYPE_OPENSSL);
    if (request != NULL)
        XMEMSET(request, 0, sizeof(OcspRequest));

    return request;
}

void wolfSSL_OCSP_REQUEST_free(OcspRequest* request)
{
    FreeOcspRequest(request);
    XFREE(request, NULL, DYNAMIC_TYPE_OPENSSL);
}

int wolfSSL_i2d_OCSP_REQUEST(OcspRequest* request, unsigned char** data)
{
    int size;

    if (request == NULL)
        return BAD_FUNC_ARG;

    size = EncodeOcspRequest(request, NULL, 0);
    if (size <= 0 || data == NULL)
        return size;

    size = EncodeOcspRequest(request, *data, (word32) size);
    if (size > 0)
        *data += size;

    return size;
}

WOLFSSL_OCSP_ONEREQ* wolfSSL_OCSP_request_add0_id(OcspRequest *req,
    WOLFSSL_OCSP_CERTID *cid)
{
    if (req == NULL || cid == NULL || cid->status == NULL)
        return NULL;

    if (req->cid != NULL)
        wolfSSL_OCSP_CERTID_free((WOLFSSL_OCSP_CERTID*)req->cid);
    /* Keep to free */
    req->cid = (void*)cid;

    XMEMCPY(req->issuerHash, cid->issuerHash, WC_MAX_DIGEST_SIZE);
    XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, WC_MAX_DIGEST_SIZE);
    req->hashAlg = (int)cid->hashAlgoOID;
    if (cid->status->serialSz > req->serialSz) {
        XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP);
        req->serial = (byte*)XMALLOC((size_t)cid->status->serialSz,
                req->heap, DYNAMIC_TYPE_OCSP_REQUEST);
        if (req->serial == NULL)
            return NULL;
    }
    XMEMCPY(req->serial, cid->status->serial, (size_t)cid->status->serialSz);
    req->serialSz = cid->status->serialSz;

    return req;
}

WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_CERTID_dup(WOLFSSL_OCSP_CERTID* id)
{
    WOLFSSL_OCSP_CERTID* certId;

    if (id == NULL)
        return NULL;

    certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID),
        NULL, DYNAMIC_TYPE_OPENSSL);
    if (certId == NULL)
        return NULL;

    XMEMCPY(certId, id, sizeof(WOLFSSL_OCSP_CERTID));
    certId->next = NULL;
    certId->rawCertId = NULL;
    certId->rawCertIdSize = 0;

    /* Deep-copy the status to avoid double-free */
    if (id->status != NULL) {
        certId->status = (CertStatus*)XMALLOC(sizeof(CertStatus),
            NULL, DYNAMIC_TYPE_OCSP_STATUS);
        if (certId->status == NULL) {
            XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL);
            return NULL;
        }
        XMEMCPY(certId->status, id->status, sizeof(CertStatus));
        certId->status->next = NULL;
        /* Don't share dynamically allocated fields */
        certId->status->rawOcspResponse = NULL;
        certId->status->rawOcspResponseSz = 0;
        certId->status->serialInt = NULL;
#ifdef WOLFSSL_OCSP_PARSE_STATUS
        certId->status->thisDateAsn = NULL;
        certId->status->nextDateAsn = NULL;
#endif
    }
    certId->ownStatus = 1;

    return certId;
}

#ifndef NO_BIO
int wolfSSL_i2d_OCSP_REQUEST_bio(WOLFSSL_BIO* out,
        WOLFSSL_OCSP_REQUEST *req)
{
    int size = -1;
    unsigned char* data = NULL;

    WOLFSSL_ENTER("wolfSSL_i2d_OCSP_REQUEST_bio");
    if (out == NULL || req == NULL)
        return WOLFSSL_FAILURE;

    size = wolfSSL_i2d_OCSP_REQUEST(req, NULL);
    if (size > 0) {
        data = (unsigned char*) XMALLOC((size_t)size, out->heap,
                DYNAMIC_TYPE_TMP_BUFFER);
    }

    if (data != NULL) {
        unsigned char* dataOrig = data;
        size = wolfSSL_i2d_OCSP_REQUEST(req, &data);
        data = dataOrig;
    }

    if (size <= 0) {
        XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return WOLFSSL_FAILURE;
    }

    if (wolfSSL_BIO_write(out, data, size) == (int)size) {
        XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return WOLFSSL_SUCCESS;
    }

    XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER);
    return WOLFSSL_FAILURE;
}
#endif /* !NO_BIO */

int wolfSSL_i2d_OCSP_CERTID(WOLFSSL_OCSP_CERTID* id, unsigned char** data)
{
    int allocated = 0;
    word32 derSz = 0;
    word32 intSz = 0;
    int ret;
    WOLFSSL_ENTER("wolfSSL_i2d_OCSP_CERTID");

    if (id == NULL)
        return -1;

    if (id->rawCertId != NULL) {
        derSz = id->rawCertIdSize;
    }
    else {
        ret = OcspEncodeCertID(id, NULL, &derSz, &intSz);
        if (ret != 0) {
            WOLFSSL_MSG("Failed to calculate CertID size");
            return -1;
        }
    }

    if (data == NULL) {
        return derSz;
    }

    if (*data == NULL) {
        /* Allocate buffer for DER encoding */
        *data = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL);
        if (*data == NULL) {
            WOLFSSL_MSG("Failed to allocate memory for CertID DER encoding");
            return -1;
        }
        allocated = 1;
    }

    if (id->rawCertId != NULL) {
        XMEMCPY(*data, id->rawCertId, id->rawCertIdSize);
    }
    else {
        ret = OcspEncodeCertID(id, *data, &derSz, &intSz);
        if (ret < 0) {
            WOLFSSL_MSG("Failed to encode CertID");
            if (allocated) {
                XFREE(*data, NULL, DYNAMIC_TYPE_OPENSSL);
                *data = NULL;
            }
            return -1;
        }
    }

    if (!allocated)
        *data += derSz;

    return derSz;
}

WOLFSSL_OCSP_CERTID* wolfSSL_d2i_OCSP_CERTID(WOLFSSL_OCSP_CERTID** cidOut,
                                             const unsigned char** derIn,
                                             int length)
{
    WOLFSSL_OCSP_CERTID *cid = NULL;
    int isAllocated = 0;
    word32 idx = 0;
    int ret;

    if (derIn == NULL || *derIn == NULL || length <= 0)
        return NULL;

    if (cidOut != NULL && *cidOut != NULL) {
        cid = *cidOut;
        FreeOcspEntry(cid, NULL);
    }
    else {
        cid = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID), NULL,
            DYNAMIC_TYPE_OPENSSL);
        if (cid == NULL)
            return NULL;
        isAllocated = 1;
    }

    XMEMSET(cid, 0, sizeof(WOLFSSL_OCSP_CERTID));
    cid->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL,
        DYNAMIC_TYPE_OCSP_STATUS);
    if (cid->status == NULL) {
        XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL);
        return NULL;
    }
    XMEMSET(cid->status, 0, sizeof(CertStatus));
    cid->ownStatus = 1;

    ret = OcspDecodeCertID(*derIn, &idx, length, cid);
    if (ret != 0) {
        FreeOcspEntry(cid, NULL);
        if (isAllocated) {
            XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL);
        }
        return NULL;
    }

    *derIn += idx;

    if (isAllocated && cidOut != NULL)
        *cidOut = cid;

    return cid;
}

const WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_SINGLERESP_get0_id(
    const WOLFSSL_OCSP_SINGLERESP *single)
{
    return single;
}

/**
 * Compare two WOLFSSL_OCSP_CERTID objects
 * @param a
 * @param b
 * @return 0 on success and when objects have the same id otherwise either
 *         the id's don't match or an error occurred
 */
int wolfSSL_OCSP_id_cmp(WOLFSSL_OCSP_CERTID *a, WOLFSSL_OCSP_CERTID *b)
{
    int ret = 0;
    if (a == NULL || b == NULL)
        return WOLFSSL_FATAL_ERROR;

    ret = a->hashAlgoOID != b->hashAlgoOID;
    if (ret == 0)
        ret = XMEMCMP(a->issuerHash, b->issuerHash, OCSP_DIGEST_SIZE);
    if (ret == 0)
        ret = XMEMCMP(a->issuerKeyHash, b->issuerKeyHash, OCSP_DIGEST_SIZE);
    if (ret == 0) {
        if (a->status != NULL && b->status != NULL) {
            if (a->status->serialSz == b->status->serialSz)
                ret = XMEMCMP(a->status->serial, b->status->serial,
                        (size_t)a->status->serialSz);
            else
                ret = -1;
        }
        else if (a->status != b->status) {
            /* If either is not null then return non-zero */
            ret = -1;
        }
    }
    return ret;
}

int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single,
                                    int *reason,
                                    WOLFSSL_ASN1_TIME **revtime,
                                    WOLFSSL_ASN1_TIME **thisupd,
                                    WOLFSSL_ASN1_TIME **nextupd)
{
    if (single == NULL)
        return WOLFSSL_FAILURE;

#ifdef WOLFSSL_OCSP_PARSE_STATUS
    if (thisupd != NULL)
        *thisupd = &single->status->thisDateParsed;
    if (nextupd != NULL)
        *nextupd = &single->status->nextDateParsed;
#else
    if (thisupd != NULL)
        *thisupd = NULL;
    if (nextupd != NULL)
        *nextupd = NULL;
#endif
    if (reason != NULL)
        *reason = 0;
    if (revtime != NULL)
        *revtime = NULL;

    return single->status->status;
}

int wolfSSL_OCSP_resp_count(WOLFSSL_OCSP_BASICRESP *bs)
{
    WOLFSSL_OCSP_SINGLERESP* single;
    int count = 0;

    if (bs == NULL)
        return WOLFSSL_FAILURE;

    single = bs->single;
    while(single != NULL)
    {
        ++count;
        single = single->next;
    }

    return count;
}

WOLFSSL_OCSP_SINGLERESP* wolfSSL_OCSP_resp_get0(WOLFSSL_OCSP_BASICRESP *bs, int idx)
{
    WOLFSSL_OCSP_SINGLERESP* single;
    int currIdx = 0;

    if (bs == NULL)
        return NULL;

    single = bs->single;
    while(single != NULL && currIdx != idx)
    {
        single = single->next;
        ++currIdx;
    }

    return single;
}

#endif /* OPENSSL_EXTRA */

#ifdef OPENSSL_ALL

/*******************************************************************************
 * START OF WOLFSSL_OCSP_REQ_CTX API
 ******************************************************************************/

enum ocspReqStates {
    ORS_INVALID = 0,
    ORS_HEADER_ADDED,
    ORS_REQ_DONE
};

enum ocspReqIOStates {
    ORIOS_INVALID = 0,
    ORIOS_WRITE,
    ORIOS_READ
};

WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_REQ_CTX_new(WOLFSSL_BIO *bio, int maxline)
{
    WOLFSSL_OCSP_REQ_CTX* ret = NULL;

    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_new");

    if (maxline <= 0)
        maxline = OCSP_MAX_REQUEST_SZ;

    ret = (WOLFSSL_OCSP_REQ_CTX*)XMALLOC(sizeof(*ret), NULL,
            DYNAMIC_TYPE_OPENSSL);
    if (ret != NULL) {
        XMEMSET(ret, 0, sizeof(*ret));
        ret->buf = (byte*)XMALLOC((word32)maxline, NULL, DYNAMIC_TYPE_OPENSSL);
        if (ret->buf == NULL)
            goto error;
        ret->reqResp = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
        ret->bufLen = maxline;
        ret->bio = bio;
        ret->ioState = ORIOS_WRITE;
    }

    return ret;
error:
    wolfSSL_OCSP_REQ_CTX_free(ret);
    return NULL;
}

void wolfSSL_OCSP_REQ_CTX_free(WOLFSSL_OCSP_REQ_CTX *ctx)
{
    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_free");
    if (ctx != NULL) {
        if (ctx->buf != NULL)
            XFREE(ctx->buf, NULL, DYNAMIC_TYPE_OPENSSL);
        if (ctx->reqResp != NULL)
            wolfSSL_BIO_free(ctx->reqResp);
        XFREE(ctx, NULL, DYNAMIC_TYPE_OPENSSL);
    }
}

WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_sendreq_new(WOLFSSL_BIO *bio,
        const char *path, OcspRequest *req, int maxline)
{
    WOLFSSL_OCSP_REQ_CTX* ret = NULL;

    WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_new");

    ret = wolfSSL_OCSP_REQ_CTX_new(bio, maxline);
    if (ret == NULL)
        return NULL;

    if (wolfSSL_OCSP_REQ_CTX_http(ret, "POST", path) != WOLFSSL_SUCCESS)
        goto error;

    if (req != NULL &&
            wolfSSL_OCSP_REQ_CTX_set1_req(ret, req) != WOLFSSL_SUCCESS)
        goto error;

    return ret;
error:
    wolfSSL_OCSP_REQ_CTX_free(ret);
    return NULL;
}

int wolfSSL_OCSP_REQ_CTX_add1_header(WOLFSSL_OCSP_REQ_CTX *ctx,
                             const char *name, const char *value)
{
    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_add1_header");

    if (name == NULL) {
        WOLFSSL_MSG("Bad parameter");
        return WOLFSSL_FAILURE;
    }
    if (wolfSSL_BIO_puts(ctx->reqResp, name) <= 0) {
        WOLFSSL_MSG("wolfSSL_BIO_puts error");
        return WOLFSSL_FAILURE;
    }
    if (value != NULL) {
        if (wolfSSL_BIO_write(ctx->reqResp, ": ", 2) != 2) {
            WOLFSSL_MSG("wolfSSL_BIO_write error");
            return WOLFSSL_FAILURE;
        }
        if (wolfSSL_BIO_puts(ctx->reqResp, value) <= 0) {
            WOLFSSL_MSG("wolfSSL_BIO_puts error");
            return WOLFSSL_FAILURE;
        }
    }
    if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) {
        WOLFSSL_MSG("wolfSSL_BIO_write error");
        return WOLFSSL_FAILURE;
    }

    ctx->state = ORS_HEADER_ADDED;

    return WOLFSSL_SUCCESS;
}

int wolfSSL_OCSP_REQ_CTX_http(WOLFSSL_OCSP_REQ_CTX *ctx, const char *op,
        const char *path)
{
    static const char http_hdr[] = "%s %s HTTP/1.0\r\n";

    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_http");

    if (ctx == NULL || op == NULL) {
        WOLFSSL_MSG("Bad parameter");
        return WOLFSSL_FAILURE;
    }

    if (path == NULL)
        path = "/";

    if (wolfSSL_BIO_printf(ctx->reqResp, http_hdr, op, path) <= 0) {
        WOLFSSL_MSG("WOLFSSL_OCSP_REQ_CTX: wolfSSL_BIO_printf error");
        return WOLFSSL_FAILURE;
    }

    ctx->state = ORS_HEADER_ADDED;

    return WOLFSSL_SUCCESS;
}

int wolfSSL_OCSP_REQ_CTX_set1_req(WOLFSSL_OCSP_REQ_CTX *ctx, OcspRequest *req)
{
    static const char req_hdr[] =
        "Content-Type: application/ocsp-request\r\n"
        "Content-Length: %d\r\n\r\n";
    /* Should be enough to hold Content-Length */
    char req_hdr_buf[sizeof(req_hdr) + 10];
    int req_hdr_buf_len;
    int req_len = wolfSSL_i2d_OCSP_REQUEST(req, NULL);

    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_set1_req");

    if (ctx == NULL || req == NULL) {
        WOLFSSL_MSG("Bad parameters");
        return WOLFSSL_FAILURE;
    }

    if (req_len <= 0) {
        WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request len error");
        return WOLFSSL_FAILURE;
    }

    req_hdr_buf_len =
            XSNPRINTF(req_hdr_buf, sizeof(req_hdr_buf), req_hdr, req_len);
    if (req_hdr_buf_len >= (int)sizeof(req_hdr_buf)) {
        WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request too long");
        return WOLFSSL_FAILURE;
    }

    if (wolfSSL_BIO_write(ctx->reqResp, req_hdr_buf, req_hdr_buf_len) <= 0) {
        WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: wolfSSL_BIO_write error");
        return WOLFSSL_FAILURE;
    }

    if (wolfSSL_i2d_OCSP_REQUEST_bio(ctx->reqResp, req) <= 0) {
        WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request i2d error");
        return WOLFSSL_FAILURE;
    }

    ctx->state = ORS_REQ_DONE;

    return WOLFSSL_SUCCESS;
}

static int OCSP_REQ_CTX_bio_cb(char *buf, int sz, void *ctx)
{
    return BioReceiveInternal((WOLFSSL_BIO*)ctx, NULL, buf, sz);
}

int wolfSSL_OCSP_REQ_CTX_nbio(WOLFSSL_OCSP_REQ_CTX *ctx)
{
    WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_nbio");

    if (ctx == NULL) {
        WOLFSSL_MSG("Bad parameters");
        return WOLFSSL_FAILURE;
    }

    switch ((enum ocspReqIOStates)ctx->ioState) {
        case ORIOS_WRITE:
        case ORIOS_READ:
            break;
        case ORIOS_INVALID:
        default:
            WOLFSSL_MSG("Invalid ctx->ioState state");
            return WOLFSSL_FAILURE;
    }

    if (ctx->ioState == ORIOS_WRITE) {
        switch ((enum ocspReqStates)ctx->state) {
            case ORS_HEADER_ADDED:
                /* Write final new line to complete http header */
                if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) {
                    WOLFSSL_MSG("wolfSSL_BIO_write error");
                    return WOLFSSL_FAILURE;
                }
                break;
            case ORS_REQ_DONE:
                break;
            case ORS_INVALID:
            default:
                WOLFSSL_MSG("Invalid WOLFSSL_OCSP_REQ_CTX state");
                return WOLFSSL_FAILURE;
        }
    }

    switch ((enum ocspReqIOStates)ctx->ioState) {
        case ORIOS_WRITE:
        {
            const unsigned char *req;
            int reqLen = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&req);
            if (reqLen <= 0) {
                WOLFSSL_MSG("wolfSSL_BIO_get_mem_data error");
                return WOLFSSL_FAILURE;
            }
            while (ctx->sent < reqLen) {
                int sent = wolfSSL_BIO_write(ctx->bio, req + ctx->sent,
                        reqLen - ctx->sent);
                if (sent <= 0) {
                    if (wolfSSL_BIO_should_retry(ctx->bio))
                        return WOLFSSL_FATAL_ERROR;
                    WOLFSSL_MSG("wolfSSL_BIO_write error");
                    ctx->ioState = ORIOS_INVALID;
                    return 0;
                }
                ctx->sent += sent;
            }
            ctx->sent = 0;
            ctx->ioState = ORIOS_READ;
            (void)wolfSSL_BIO_reset(ctx->reqResp);
            FALL_THROUGH;
        }
        case ORIOS_READ:
        {
            byte* resp = NULL;
            int respLen;
            int ret;

            if (ctx->buf == NULL) /* Should be allocated in new call */
                return WOLFSSL_FAILURE;

            ret = wolfIO_HttpProcessResponseOcspGenericIO(OCSP_REQ_CTX_bio_cb,
                    ctx->bio, &resp, ctx->buf, ctx->bufLen, NULL);
            if (ret <= 0) {
                if (resp != NULL)
                    XFREE(resp, NULL, DYNAMIC_TYPE_OCSP);
                if (ret == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ) ||
                    ret == WC_NO_ERR_TRACE(OCSP_WANT_READ))
                {
                    return WOLFSSL_FATAL_ERROR;
                }
                return WOLFSSL_FAILURE;
            }
            respLen = ret;
            ret = wolfSSL_BIO_write(ctx->reqResp, resp, respLen);
            XFREE(resp, NULL, DYNAMIC_TYPE_OCSP);
            if (ret != respLen) {
                WOLFSSL_MSG("wolfSSL_BIO_write error");
                return WOLFSSL_FAILURE;
            }
            break;
        }
        case ORIOS_INVALID:
        default:
            WOLFSSL_MSG("Invalid ctx->ioState state");
            return WOLFSSL_FAILURE;
    }

    return WOLFSSL_SUCCESS;
}

int wolfSSL_OCSP_sendreq_nbio(OcspResponse **presp, WOLFSSL_OCSP_REQ_CTX *ctx)
{
    int ret;
    int len;
    const unsigned char *resp = NULL;

    WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_nbio");

    if (presp == NULL)
        return WOLFSSL_FAILURE;

    ret = wolfSSL_OCSP_REQ_CTX_nbio(ctx);
    if (ret != WOLFSSL_SUCCESS)
        return ret;

    len = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&resp);
    if (len <= 0)
        return WOLFSSL_FAILURE;
    return wolfSSL_d2i_OCSP_RESPONSE(presp, &resp, len) != NULL
            ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
}

/*******************************************************************************
 * END OF WOLFSSL_OCSP_REQ_CTX API
 ******************************************************************************/

#ifndef NO_WOLFSSL_STUB
int wolfSSL_OCSP_REQUEST_add_ext(OcspRequest* req, WOLFSSL_X509_EXTENSION* ext,
        int idx)
{
    WOLFSSL_STUB("wolfSSL_OCSP_REQUEST_add_ext");
    (void)req;
    (void)ext;
    (void)idx;
    return WOLFSSL_FATAL_ERROR;
}
#endif

#ifndef NO_WOLFSSL_STUB
OcspResponse* wolfSSL_OCSP_response_create(int status,
    WOLFSSL_OCSP_BASICRESP* bs)
{
    WOLFSSL_STUB("wolfSSL_OCSP_response_create");
    (void)status;
    (void)bs;
    return NULL;
}
#endif

#ifndef NO_WOLFSSL_STUB
const char* wolfSSL_OCSP_crl_reason_str(long s)
{
    WOLFSSL_STUB("wolfSSL_OCSP_crl_reason_str");
    (void)s;
    return NULL;
}
#endif

/* Returns elements of an OCSP_CERTID struct. Currently only supports
 * returning the serial number, and returns an error if user requests
 * any of name, pmd, and/or keyHash.
 * Return 1 on success, 0 on failure */
int wolfSSL_OCSP_id_get0_info(WOLFSSL_ASN1_STRING **name,
  WOLFSSL_ASN1_OBJECT **pmd, WOLFSSL_ASN1_STRING **keyHash,
  WOLFSSL_ASN1_INTEGER **serial, WOLFSSL_OCSP_CERTID *cid)
{
    WOLFSSL_ENTER("wolfSSL_OCSP_id_get0_info");

    if (cid == NULL)
        return 0;

    /* build up ASN1_INTEGER for serial */
    if (serial != NULL) {
        int i = 0;
        WOLFSSL_ASN1_INTEGER* ser;

        ser = wolfSSL_ASN1_INTEGER_new();
        if (ser == NULL)
            return 0;

        if (cid->status->serialSz > (WOLFSSL_ASN1_INTEGER_MAX - 2)) {
            /* allocate data buffer, +2 for type and length */
            ser->data = (unsigned char*)XMALLOC((size_t)cid->status->serialSz + 2, NULL,
                DYNAMIC_TYPE_OPENSSL);
            if (ser->data == NULL) {
                wolfSSL_ASN1_INTEGER_free(ser);
                return 0;
            }
            ser->dataMax = (unsigned int)cid->status->serialSz + 2;
            ser->isDynamic = 1;
        } else {
            /* Use array instead of dynamic memory */
            ser->data    = ser->intData;
            ser->dataMax = WOLFSSL_ASN1_INTEGER_MAX;
        }

        #if defined(WOLFSSL_QT) || defined(WOLFSSL_HAPROXY)
            /* Serial number starts at 0 index of ser->data */
            XMEMCPY(&ser->data[i], cid->status->serial,
                (size_t)cid->status->serialSz);
            ser->length = cid->status->serialSz;
        #else
            ser->data[i++] = ASN_INTEGER;
            i += SetLength(cid->status->serialSz, ser->data + i);
            XMEMCPY(&ser->data[i], cid->status->serial,
                (size_t)cid->status->serialSz);
            ser->length = i + cid->status->serialSz;
        #endif

        cid->status->serialInt = ser;
        *serial = ser;
    }

    /* Not needed for Apache, return error if user is requesting */
    if (name != NULL || pmd != NULL || keyHash != NULL) {
        if (name != NULL)
            *name = NULL;

        if (pmd != NULL)
            *pmd = NULL;

        if (keyHash != NULL)
            *keyHash = NULL;
        return 0;
    }

    return 1;
}

int wolfSSL_OCSP_request_add1_nonce(OcspRequest* req, unsigned char* val,
        int sz)
{
    WC_RNG rng;

    WOLFSSL_ENTER("wolfSSL_OCSP_request_add1_nonce");

    if (req == NULL || sz > MAX_OCSP_NONCE_SZ) {
        WOLFSSL_MSG("Bad parameter");
        return WOLFSSL_FAILURE;
    }

    if (sz <= 0)
        sz = MAX_OCSP_NONCE_SZ;

    if (val != NULL) {
        XMEMCPY(req->nonce, val, (size_t)sz);
    }
    else {
        if (
#ifndef HAVE_FIPS
            wc_InitRng_ex(&rng, req->heap, INVALID_DEVID)
#else
            wc_InitRng(&rng)
#endif
            != 0) {
            WOLFSSL_MSG("RNG init failed");
            return WOLFSSL_FAILURE;
        }
        if (wc_RNG_GenerateBlock(&rng, req->nonce, (word32)sz) != 0) {
            WOLFSSL_MSG("wc_RNG_GenerateBlock failed");
            wc_FreeRng(&rng);
            return WOLFSSL_FAILURE;
        }
        wc_FreeRng(&rng);
    }
    req->nonceSz = sz;

    return WOLFSSL_SUCCESS;
}

/* Returns result of OCSP nonce comparison. Return values:
 *  1 - nonces are both present and equal
 *  2 - both nonces are absent
 *  3 - nonce only present in response
 * -1 - nonce only present in request
 *  0 - both nonces present and equal
 */
int wolfSSL_OCSP_check_nonce(OcspRequest* req, WOLFSSL_OCSP_BASICRESP* bs)
{
    const byte* reqNonce = NULL;
    const byte* rspNonce = NULL;
    int reqNonceSz = 0;
    int rspNonceSz = 0;

    WOLFSSL_ENTER("wolfSSL_OCSP_check_nonce");

    if (req != NULL) {
        reqNonce = req->nonce;
        reqNonceSz = req->nonceSz;
    }

    if (bs != NULL) {
        rspNonce = bs->nonce;
        rspNonceSz = bs->nonceSz;
    }

    /* nonce absent in both req and rsp */
    if (reqNonce == NULL && rspNonce == NULL)
        return 2;

    /* nonce present in rsp only */
    if (reqNonce == NULL && rspNonce != NULL)
        return 3;

    /* nonce present in req only */
    if (reqNonce != NULL && rspNonce == NULL)
        return WOLFSSL_FATAL_ERROR;

    /* nonces are present and equal, return 1. Extra NULL check for fixing
        scan-build warning. */
    if (reqNonceSz == rspNonceSz && reqNonce && rspNonce) {
        if (XMEMCMP(reqNonce, rspNonce, (size_t)reqNonceSz) == 0)
            return 1;
    }

    /* nonces are present but not equal */
    return 0;
}

#endif /* OPENSSL_ALL */

#ifdef HAVE_OCSP_RESPONDER

/* Free a CA entry and all its resources */
static void FreeOcspResponderCa(OcspResponderCa* ca, void* heap)
{
    OcspResponderCertStatus* status;
    OcspResponderCertStatus* nextStatus;

    if (ca == NULL)
        return;

    /* Free private key */
#ifndef NO_RSA
    if (ca->keyType == RSAk) {
        wc_FreeRsaKey(&ca->key.rsa);
    }
#endif
#ifdef HAVE_ECC
    if (ca->keyType == ECDSAk) {
        wc_ecc_free(&ca->key.ecc);
    }
#endif

    /* Free certificate DER if allocated */
    if (ca->certDer != NULL) {
        XFREE(ca->certDer, heap, DYNAMIC_TYPE_OCSP);
    }

    /* Free certificate status list */
    status = ca->statuses;
    while (status != NULL) {
        nextStatus = status->next;
        XFREE(status, heap, DYNAMIC_TYPE_OCSP);
        status = nextStatus;
    }

    XFREE(ca, heap, DYNAMIC_TYPE_OCSP);
}

/* Allocate and initialize an OCSP Responder */
OcspResponder* wc_OcspResponder_new(void* heap, int sendCerts)
{
    OcspResponder* responder;

    WOLFSSL_ENTER("wc_OcspResponder_new");

    responder = (OcspResponder*)XMALLOC(sizeof(OcspResponder), heap,
                                         DYNAMIC_TYPE_OCSP);
    if (responder != NULL) {
        XMEMSET(responder, 0, sizeof(OcspResponder));
        responder->heap = heap;
        responder->sendCerts = sendCerts ? 1 : 0;
        if (wc_InitRng(&responder->rng) != 0) {
            XFREE(responder, heap, DYNAMIC_TYPE_OCSP);
            responder = NULL;
        }
    }
    return responder;
}

/* Free an OCSP Responder */
void wc_OcspResponder_free(OcspResponder* responder)
{
    WOLFSSL_ENTER("wc_OcspResponder_free");
    if (responder != NULL) {
        OcspResponderCa* ca;
        OcspResponderCa* nextCa;
        /* Free all CAs */
        ca = responder->caList;
        while (ca != NULL) {
            nextCa = ca->next;
            FreeOcspResponderCa(ca, responder->heap);
            ca = nextCa;
        }
        wc_FreeRng(&responder->rng);
        XFREE(responder, responder->heap, DYNAMIC_TYPE_OCSP);
    }
}

int wc_OcspResponder_AddSigner(OcspResponder* responder,
    const byte* signerDer, word32 signerDerSz,
    const byte* keyDer, word32 keyDerSz,
    /* Necessary when adding an authorized responder */
    const byte* issuerCertDer, word32 issuerCertDerSz)
{
    int ret = -1;
    OcspResponderCa* ca = NULL;
    DecodedCert* decoded = NULL;
    word32 keyOID = 0;

    WOLFSSL_ENTER("wc_OcspResponder_AddSigner");

    if (responder == NULL || signerDer == NULL || signerDerSz == 0 ||
        keyDer == NULL || keyDerSz == 0 ||
        (issuerCertDerSz != 0 && issuerCertDer == NULL))
        return BAD_FUNC_ARG;

    /* Allocate CA structure */
    ca = (OcspResponderCa*)XMALLOC(sizeof(OcspResponderCa), responder->heap,
                                   DYNAMIC_TYPE_OCSP);
    if (ca == NULL) {
        ret = MEMORY_E;
        goto out;
    }
    XMEMSET(ca, 0, sizeof(OcspResponderCa));

    /* Parse certificate */
    decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), responder->heap,
                                    DYNAMIC_TYPE_OCSP);
    if (decoded == NULL) {
        ret = MEMORY_E;
        goto out;
    }

    wc_InitDecodedCert(decoded, signerDer, signerDerSz, responder->heap);
    ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL);
    if (ret != 0)
        goto out;

    ret = wc_CheckPrivateKeyCert(keyDer, keyDerSz, decoded, 1, responder->heap);
    if (ret != 1) {
        ret = (ret == 0) ? BAD_FUNC_ARG : ret;
        goto out;
    }
    keyOID = decoded->keyOID;

    ret = wc_ShaHash(decoded->publicKeyForHash, decoded->pubKeyForHashSize,
            ca->responderKeyHash);
    if (ret != 0)
        goto out;

    /* Now place the real issuer into decoded and perform cert can actually
     * sign for issuer. */
    if (issuerCertDer != NULL && issuerCertDerSz > 0) {
        char issuer[WC_ASN_NAME_MAX];

        if (signerDerSz == issuerCertDerSz &&
                XMEMCMP(signerDer, issuerCertDer, signerDerSz) == 0) {
            /* API misuse */
            ret = BAD_FUNC_ARG;
            goto out;
        }
        if ((decoded->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) == 0) {
            /* Cert is not authorized for OCSP signing */
            ret = BAD_FUNC_ARG;
            goto out;
        }
        XMEMCPY(issuer, decoded->issuer, WC_ASN_NAME_MAX);

        /* Use allocated DecodedCert */
        wc_FreeDecodedCert(decoded);
        wc_InitDecodedCert(decoded, issuerCertDer, issuerCertDerSz,
                          responder->heap);
        ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL);
        if (ret != 0)
            goto out;

        if (XSTRNCMP(issuer, decoded->subject, WC_ASN_NAME_MAX) != 0) {
            /* Issuer name in responder cert does not match subject of issuer cert */
            ret = BAD_FUNC_ARG;
            goto out;
        }

        ca->authResp = 1;
    }

    /* Use responder cert's subject and public key for hashes */
    if (decoded->subjectRawForHash == NULL ||
            decoded->subjectRawForHashLen <= 0) {
        ret = BAD_FUNC_ARG;
        goto out;
    }
    ret = AsnHashesHash(&ca->issuerHashes, decoded->subjectRawForHash,
            (word32)decoded->subjectRawForHashLen);
    if (ret != 0)
        goto out;
    if (decoded->publicKeyForHash == NULL || decoded->pubKeyForHashSize == 0) {
        ret = BAD_FUNC_ARG;
        goto out;
    }
    ret = AsnHashesHash(&ca->issuerKeyHashes, decoded->publicKeyForHash,
            decoded->pubKeyForHashSize);
    if (ret != 0)
        goto out;

    /* Extract necessary info from decoded cert */
    XMEMCPY(ca->subject, decoded->subject, WC_ASN_NAME_MAX);

    /* Store raw certificate DER if sendCerts is enabled */
    if (responder->sendCerts) {
        ca->certDer = (byte*)XMALLOC(signerDerSz, responder->heap, DYNAMIC_TYPE_OCSP);
        if (ca->certDer == NULL) {
            ret = MEMORY_E;
            goto out;
        }
        XMEMCPY(ca->certDer, signerDer, signerDerSz);
        ca->certDerSz = signerDerSz;
    }

    /* Load the private key */
#ifndef NO_RSA
    if (keyOID == RSAk) {
        ret = wc_InitRsaKey(&ca->key.rsa, responder->heap);
        if (ret == 0) {
            word32 idx = 0;
            ca->keyType = RSAk;
            ret = wc_RsaPrivateKeyDecode(keyDer, &idx, &ca->key.rsa, keyDerSz);
        }
        if (ret != 0)
            goto out;
    }
    else
#endif
#ifdef HAVE_ECC
    if (keyOID == ECDSAk) {
        ret = wc_ecc_init_ex(&ca->key.ecc, responder->heap, INVALID_DEVID);
        if (ret == 0) {
            word32 idx = 0;
            ca->keyType = ECDSAk;
            ret = wc_EccPrivateKeyDecode(keyDer, &idx, &ca->key.ecc, keyDerSz);
        }
        if (ret != 0)
            goto out;
    }
    else
#endif
    {
        ret = NOT_COMPILED_IN;
        goto out;
    }

    /* Check for duplicates before adding */
    {
        OcspResponderCa* existing = responder->caList;
        while (existing != NULL) {
            if (XMEMCMP(&existing->issuerHashes, &ca->issuerHashes,
                       sizeof(AsnHashes)) == 0 &&
                XMEMCMP(&existing->issuerKeyHashes, &ca->issuerKeyHashes,
                       sizeof(AsnHashes)) == 0) {
                ret = DUPE_ENTRY_E;
                goto out;
            }
            existing = existing->next;
        }
    }

    /* Add CA to list */
    ca->next = responder->caList;
    responder->caList = ca;
    ca = NULL; /* Ownership transferred */
    ret = 0;

out:
    if (decoded != NULL) {
        wc_FreeDecodedCert(decoded);
        XFREE(decoded, responder->heap, DYNAMIC_TYPE_OCSP);
    }
    if (ca != NULL)
        FreeOcspResponderCa(ca, responder->heap);
    return ret;
}

/* Find Auth CA by issuer hashes from request */
static OcspResponderCa* FindCaByHashes(OcspResponder* responder,
    const byte* issuerHash, const byte* issuerKeyHash, int hashAlg)
{
    OcspResponderCa* ca = responder->caList;

    while (ca != NULL) {
        int hashSz = 0;
        const byte* caIssuerHash = AsnHashesGetHash(&ca->issuerHashes,
            hashAlg, &hashSz);
        const byte* caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes,
            hashAlg, &hashSz);

        if (caIssuerHash != NULL && caKeyHash != NULL && hashSz > 0 &&
            XMEMCMP(caIssuerHash, issuerHash, (size_t)hashSz) == 0 &&
            XMEMCMP(caKeyHash, issuerKeyHash, (size_t)hashSz) == 0) {
            return ca;
        }
        ca = ca->next;
    }

    return NULL;
}

/* Find certificate status in a CA */
static OcspResponderCertStatus* FindCertStatus(OcspResponderCa* ca,
    const byte* serial, word32 serialSz)
{
    OcspResponderCertStatus* status = ca->statuses;

    while (status != NULL) {
        if (status->serialSz == (int)serialSz &&
            XMEMCMP(status->serial, serial, serialSz) == 0) {
            return status;
        }
        status = status->next;
    }

    return NULL;
}

/* Find Auth CA by subject string */
static OcspResponderCa* FindCaBySubject(OcspResponder* responder,
    const char* caSubject, word32 caSubjectSz)
{
    OcspResponderCa* ca = responder->caList;

    while (ca != NULL) {
        word32 subjectLen = (word32)XSTRLEN(ca->subject);
        if (subjectLen == caSubjectSz &&
            XMEMCMP(ca->subject, caSubject, caSubjectSz) == 0) {
            return ca;
        }
        ca = ca->next;
    }

    return NULL;
}

/* Add a certificate status for a specific CA */
int wc_OcspResponder_SetCertStatus(OcspResponder* responder,
    const char* caSubject, word32 caSubjectSz,
    const byte* serial, word32 serialSz, enum Ocsp_Cert_Status status,
    time_t revocationTime, enum WC_CRL_Reason revocationReason,
    word32 validityPeriod)
{
    OcspResponderCa* ca = NULL;
    OcspResponderCertStatus* certStatus = NULL;
    int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    int isNew = 0;

    WOLFSSL_ENTER("wc_OcspResponder_SetCertStatus");

    if (responder == NULL || caSubject == NULL || caSubjectSz == 0 ||
        serial == NULL || serialSz == 0 || serialSz > EXTERNAL_SERIAL_SIZE)
        goto out;

    if (status != CERT_GOOD && status != CERT_REVOKED &&
        status != CERT_UNKNOWN)
        goto out;

    /* Validate revocation parameters when status is REVOKED */
    if (status == CERT_REVOKED) {
        if (revocationTime <= 0)
            goto out;
        if (revocationReason < WC_CRL_REASON_UNSPECIFIED ||
            revocationReason > WC_CRL_REASON_AA_COMPROMISE)
            goto out;
        /* Skip value 7 which is not used */
        if (revocationReason == 7)
            goto out;
    }

    if (status == CERT_GOOD && validityPeriod == 0)
        goto out;
    if (status != CERT_GOOD && validityPeriod != 0)
        goto out;

    /* Find the CA */
    ca = FindCaBySubject(responder, caSubject, caSubjectSz);
    if (ca == NULL) {
        ret = ASN_NO_SIGNER_E;
        goto out;
    }

    /* Check if status already exists for this serial */
    certStatus = FindCertStatus(ca, serial, serialSz);
    if (certStatus == NULL) {
        /* Allocate new status entry */
        certStatus = (OcspResponderCertStatus*)XMALLOC(
            sizeof(OcspResponderCertStatus), responder->heap, DYNAMIC_TYPE_OCSP);
        if (certStatus == NULL) {
            ret = MEMORY_E;
            goto out;
        }
        XMEMSET(certStatus, 0, sizeof(OcspResponderCertStatus));
        XMEMCPY(certStatus->serial, serial, serialSz);
        certStatus->serialSz = serialSz;
        isNew = 1;
    }

    certStatus->status = status;
    certStatus->validityPeriod = 0;
    if (status == CERT_REVOKED) {
        ret = GetFormattedTime_ex(&revocationTime, certStatus->revocationDate,
            sizeof(certStatus->revocationDate), ASN_GENERALIZED_TIME);
        if (ret <= 0) {
            WOLFSSL_MSG("Failed to format revocation time");
            goto out;
        }
        certStatus->revocationDateSz = (word32)ret;
        certStatus->revocationReason = revocationReason;
    }
    else if (status == CERT_GOOD) {
        certStatus->validityPeriod = validityPeriod;
    }

    if (isNew) {
        /* Add to CA's status list */
        certStatus->next = ca->statuses;
        ca->statuses = certStatus;
        certStatus = NULL; /* Ownership transferred */
    }
    ret = 0;

out:
    if (isNew && certStatus != NULL)
        XFREE(certStatus, responder->heap, DYNAMIC_TYPE_OCSP);

    return ret;
}

static int OcspResponse_WriteResponse(OcspResponder* responder, byte* response,
        word32* responseSz, OcspResponderCa* ca,
        OcspResponderCertStatus* certStatus, OcspRequest* req)
{
    OcspResponse resp;
    CertStatus status;
    OcspEntry entry;
    int ret = 0;
    time_t now;
    RsaKey* rsaKey = NULL;
    ecc_key* eccKey = NULL;
    int respInited = 0;
    int hashSz = 0;
    const byte* caIssuerHash = NULL;
    const byte* caKeyHash = NULL;
    int hashAlg = -1;

    WOLFSSL_ASSERT_SIZEOF_EQ(resp.responderId.keyHash, ca->responderKeyHash);

    WOLFSSL_ENTER("OcspResponse_WriteResponse");

    if (responseSz == NULL || ca == NULL || certStatus == NULL ||
            certStatus->serialSz > EXTERNAL_SERIAL_SIZE) {
        ret = BAD_FUNC_ARG;
        goto out;
    }

    InitOcspResponse(&resp, &entry, &status, NULL, 0, responder->heap);
    respInited = 1;

    resp.responseStatus = OCSP_SUCCESSFUL;

    status.status = certStatus->status;

    XMEMCPY(status.serial, certStatus->serial, certStatus->serialSz);
    status.serialSz = (byte)certStatus->serialSz;

    /* Copy revocation information if status is REVOKED */
    if (certStatus->status == CERT_REVOKED) {
        XMEMCPY(status.revocationDate, certStatus->revocationDate,
                certStatus->revocationDateSz);
        status.revocationDateSz = certStatus->revocationDateSz;
        status.revocationReason = (byte)certStatus->revocationReason;
    }

    now = wc_Time(NULL);
    ret = GetFormattedTime_ex(&now, status.thisDate, sizeof(status.thisDate),
            ASN_GENERALIZED_TIME);
    if (ret <= 0) {
        WOLFSSL_MSG("Failed to format thisUpdate time");
        goto out;
    }
    XMEMCPY(resp.producedDate, status.thisDate, ret);
    resp.producedDateSz = status.thisDateSz = (byte)(ret);
    resp.producedDateFormat = status.thisDateFormat = ASN_GENERALIZED_TIME;

    /* Set nextUpdate if validity period is specified (for CERT_GOOD) */
    if (certStatus->status == CERT_GOOD && certStatus->validityPeriod > 0) {
        time_t nextTime = now + (time_t)certStatus->validityPeriod;
        ret = GetFormattedTime_ex(&nextTime, status.nextDate,
                sizeof(status.nextDate), ASN_GENERALIZED_TIME);
        if (ret <= 0) {
            WOLFSSL_MSG("Failed to format nextUpdate time");
            goto out;
        }
        status.nextDateSz = (byte)ret;
        status.nextDateFormat = ASN_GENERALIZED_TIME;
    }

    /* Set certificate if sendCerts is enabled */
    if (responder->sendCerts && ca->certDer != NULL) {
        resp.cert = ca->certDer;
        resp.certSz = ca->certDerSz;
    }

    /* Copy nonce from request to response if present */
    if (req != NULL && req->nonceSz > 0) {
        resp.nonce = req->nonce;
        resp.nonceSz = req->nonceSz;
    }

    /* Echo the hash algorithm from the request */
    if (req == NULL) {
        hashAlg = SHAh; /* Fall back to SHA-1 as its required for OCSP */
    }
    else {
        hashAlg = req->hashAlg;
    }
    caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, hashAlg, &hashSz);
    caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, hashAlg, &hashSz);
    if (caIssuerHash == NULL || caKeyHash == NULL || hashSz <= 0) {
        ret = ASN_SIG_HASH_E;
        goto out;
    }
    entry.hashAlgoOID = (word32)hashAlg;
    XMEMCPY(entry.issuerHash, caIssuerHash, (size_t)hashSz);
    XMEMCPY(entry.issuerKeyHash, caKeyHash, (size_t)hashSz);

    resp.responderIdType = OCSP_RESPONDER_ID_KEY;
    XMEMCPY(resp.responderId.keyHash, ca->responderKeyHash, WC_SHA_DIGEST_SIZE);

    /* TODO allow user to set algo */
    if (ca->keyType == RSAk) {
        rsaKey = &ca->key.rsa;
        resp.sigOID = CTC_SHA256wRSA;
    }
    else if (ca->keyType == ECDSAk) {
        eccKey = &ca->key.ecc;
        resp.sigOID = CTC_SHA256wECDSA;
    }
    else {
        ret = NOT_COMPILED_IN;
        goto out;
    }

    ret = OcspResponseEncode(&resp, response, responseSz, rsaKey, eccKey,
            &responder->rng);
    if (ret != 0) {
        WOLFSSL_MSG("Failed to encode OCSP response");
        goto out;
    }

    ret = 0;
out:
    if (respInited)
        FreeOcspResponse(&resp);
    return ret;
}

/* Generate OCSP response for a request */
int wc_OcspResponder_WriteResponse(OcspResponder* responder,
    const byte* request, word32 requestSz,
    byte* response, word32* responseSz)
{
    int ret = 0;
    OcspRequest req;
    OcspResponderCa* ca = NULL;
    OcspResponderCertStatus* certStatus = NULL;
    int reqInited = 0;

    WOLFSSL_ENTER("wc_OcspResponder_WriteResponse");

    if (responder == NULL || request == NULL || requestSz == 0) {
        ret = BAD_FUNC_ARG;
        goto out;
    }

    XMEMSET(&req, 0, sizeof(OcspRequest));

    /* Decode the OCSP request */
    ret = DecodeOcspRequest(&req, request, requestSz);
    if (ret != 0) {
        WOLFSSL_MSG("Failed to decode OCSP request");
        goto out;
    }
    reqInited = 1;

    /* Find the CA by issuer hashes */
    ca = FindCaByHashes(responder, req.issuerHash, req.issuerKeyHash,
                        req.hashAlg);
    if (ca == NULL) {
        WOLFSSL_MSG("No matching CA found for request");
        ret = ASN_NO_SIGNER_E;
        goto out;
    }

    /* Find the certificate status */
    certStatus = FindCertStatus(ca, req.serial, req.serialSz);
    if (certStatus == NULL) {
        /* RFC 6960: 'unknown' is a per-certificate status inside a successful
         * OCSPResponse, not an error response. Generate a successful response
         * with CERT_UNKNOWN so clients can distinguish it from UNAUTHORIZED. */
        OcspResponderCertStatus unknownStatus;
        WOLFSSL_MSG("No status for requested certificate, responding unknown");
        if (req.serialSz > EXTERNAL_SERIAL_SIZE) {
            ret = BUFFER_E;
            goto out;
        }
        XMEMSET(&unknownStatus, 0, sizeof(unknownStatus));
        XMEMCPY(unknownStatus.serial, req.serial, req.serialSz);
        unknownStatus.serialSz = req.serialSz;
        unknownStatus.status = CERT_UNKNOWN;
        ret = OcspResponse_WriteResponse(responder, response, responseSz, ca,
                &unknownStatus, &req);
    }
    else {
        WOLFSSL_MSG("Found CA and certificate status");
        ret = OcspResponse_WriteResponse(responder, response, responseSz, ca,
                certStatus, &req);
    }

out:
    if (reqInited)
        FreeOcspRequest(&req);
    WOLFSSL_LEAVE("wc_OcspResponder_WriteResponse", ret);
    return ret;
}

int wc_OcspResponder_WriteErrorResponse(enum Ocsp_Response_Status status,
    byte* response, word32* responseSz)
{
    int ret = 0;
    OcspResponse resp;
    int respInited = 0;

    WOLFSSL_ENTER("wc_OcspResponder_WriteErrorResponse");

    if (responseSz == NULL) {
        ret = BAD_FUNC_ARG;
        goto out;
    }

    /* Validate status - OCSP_SUCCESSFUL is not allowed for error responses */
    if (status == OCSP_SUCCESSFUL) {
        WOLFSSL_MSG("OCSP_SUCCESSFUL is not a valid error status");
        ret = BAD_FUNC_ARG;
        goto out;
    }

    /* Validate that status is a known enumeration value */
    if (status != OCSP_MALFORMED_REQUEST &&
        status != OCSP_INTERNAL_ERROR &&
        status != OCSP_TRY_LATER &&
        status != OCSP_SIG_REQUIRED &&
        status != OCSP_UNAUTHORIZED) {
        WOLFSSL_MSG("Invalid OCSP response status");
        ret = BAD_FUNC_ARG;
        goto out;
    }

    /* Initialize a minimal OCSP response structure */
    InitOcspResponse(&resp, NULL, NULL, NULL, 0, NULL);
    respInited = 1;

    /* Set the error status */
    resp.responseStatus = (byte)status;

    /* Encode the error response (no responseBytes, just status) */
    ret = OcspResponseEncode(&resp, response, responseSz, NULL, NULL, NULL);
    if (ret != 0) {
        WOLFSSL_MSG("Failed to encode OCSP error response");
        goto out;
    }

    ret = 0;
out:
    if (respInited)
        FreeOcspResponse(&resp);
    return ret;
}

#endif /* HAVE_OCSP_RESPONDER */

/* Helper functions for testing */
int wc_InitOcspRequest(OcspRequest* req, DecodedCert* cert,
                                    byte useNonce, void* heap)
{
    return InitOcspRequest(req, cert, useNonce, heap);
}

int wc_EncodeOcspRequest(OcspRequest* req, byte* output,
                                      word32 size)
{
    return EncodeOcspRequest(req, output, size);
}

#else /* HAVE_OCSP */


#ifdef _MSC_VER
    /* 4206 warning for blank file */
    #pragma warning(disable: 4206)
#endif


#endif /* HAVE_OCSP */
#endif /* WOLFCRYPT_ONLY */
