static int CheckCertSignature_ex(const byte* cert, word32 certSz, void* heap,
        void* cm, const byte* pubKey, word32 pubKeySz, int pubKeyOID, int req)
{
#ifndef WOLFSSL_SMALL_STACK
    SignatureCtx  sigCtx[1];
#else
    SignatureCtx* sigCtx;
#endif
    byte          hash[KEYID_SIZE];
    Signer*       ca = NULL;
    word32        idx = 0;
    int           len;
    word32        tbsCertIdx = 0;
    word32        sigIndex   = 0;
    word32        signatureOID = 0;
    word32        oid = 0;
    word32        issuerIdx = 0;
    word32        issuerSz  = 0;
#ifndef NO_SKID
    int           extLen = 0;
    word32        extIdx = 0;
    word32        extEndIdx = 0;
    int           extAuthKeyIdSet = 0;
#endif
    int           ret = 0;
    word32        localIdx;
    byte          tag;


    if (cert == NULL) {
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_SMALL_STACK
    sigCtx = (SignatureCtx*)XMALLOC(sizeof(*sigCtx), heap, DYNAMIC_TYPE_SIGNATURE);
    if (sigCtx == NULL)
        return MEMORY_E;
#endif
    InitSignatureCtx(sigCtx, heap, INVALID_DEVID);

    /* Certificate SEQUENCE */
    if (GetSequence(cert, &idx, &len, certSz) < 0)
        ret = ASN_PARSE_E;
    if (ret == 0) {
        tbsCertIdx = idx;

        /* TBSCertificate SEQUENCE */
        if (GetSequence(cert, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        sigIndex = len + idx;

        if ((idx + 1) > certSz)
            ret = BUFFER_E;
    }
    if (ret == 0) {
        /* version - optional */
        localIdx = idx;
        if (GetASNTag(cert, &localIdx, &tag, certSz) == 0) {
            if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) {
                idx++;
                if (GetLength(cert, &idx, &len, certSz) < 0)
                    ret = ASN_PARSE_E;
                idx += len;
            }
        }
    }

    if (ret == 0) {
        /* serialNumber */
        if (GetASNHeader(cert, ASN_INTEGER, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        idx += len;

        /* signature */
        if (!req &&
                GetAlgoId(cert, &idx, &signatureOID, oidSigType, certSz) < 0)
            ret = ASN_PARSE_E;
    }

    if (ret == 0) {
        issuerIdx = idx;
        /* issuer for cert or subject for csr */
        if (GetSequence(cert, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        issuerSz = len + idx - issuerIdx;
    }
#ifndef NO_SKID
    if (!req && ret == 0) {
        idx += len;

        /* validity */
        if (GetSequence(cert, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (!req && ret == 0) {
        idx += len;

        /* subject */
        if (GetSequence(cert, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        idx += len;

        /* subjectPublicKeyInfo */
        if (GetSequence(cert, &idx, &len, certSz) < 0)
            ret = ASN_PARSE_E;
    }
    if (req && ret == 0) {
        idx += len;

        /* attributes */
        if (GetASNHeader_ex(cert,
                ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED, &idx,
                &len, certSz, 1) < 0)
            ret = ASN_PARSE_E;
    }
    if (!req) {
        if (ret == 0) {
            idx += len;

            if ((idx + 1) > certSz)
                ret = BUFFER_E;
        }
        if (ret == 0) {
            /* issuerUniqueID - optional */
            localIdx = idx;
            if (GetASNTag(cert, &localIdx, &tag, certSz) == 0) {
                if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1)) {
                    idx++;
                    if (GetLength(cert, &idx, &len, certSz) < 0)
                        ret = ASN_PARSE_E;
                    idx += len;
                }
            }
        }
        if (ret == 0) {
            if ((idx + 1) > certSz)
                ret = BUFFER_E;
        }
        if (ret == 0) {
            /* subjectUniqueID - optional */
            localIdx = idx;
            if (GetASNTag(cert, &localIdx, &tag, certSz) == 0) {
                if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 2)) {
                    idx++;
                    if (GetLength(cert, &idx, &len, certSz) < 0)
                        ret = ASN_PARSE_E;
                    idx += len;
                }
            }
        }

        if (ret == 0) {
            if ((idx + 1) > certSz)
                ret = BUFFER_E;
        }
        /* extensions - optional */
        localIdx = idx;
        if (ret == 0 && GetASNTag(cert, &localIdx, &tag, certSz) == 0 &&
                tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 3)) {
            idx++;
            if (GetLength(cert, &idx, &extLen, certSz) < 0)
                ret = ASN_PARSE_E;
            if (ret == 0) {
                if (GetSequence(cert, &idx, &extLen, certSz) < 0)
                    ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                extEndIdx = idx + extLen;

                /* Check each extension for the ones we want. */
                while (ret == 0 && idx < extEndIdx) {
                    if (GetSequence(cert, &idx, &len, certSz) < 0)
                        ret = ASN_PARSE_E;
                    if (ret == 0) {
                        extIdx = idx;
                        if (GetObjectId(cert, &extIdx, &oid, oidCertExtType,
                                                                      certSz) < 0) {
                            ret = ASN_PARSE_E;
                        }

                        if (ret == 0) {
                            if ((extIdx + 1) > certSz)
                                ret = BUFFER_E;
                        }
                    }

                    if (ret == 0) {
                        localIdx = extIdx;
                        if (GetASNTag(cert, &localIdx, &tag, certSz) == 0 &&
                                tag == ASN_BOOLEAN) {
                            if (GetBoolean(cert, &extIdx, certSz) < 0)
                                ret = ASN_PARSE_E;
                        }
                    }
                    if (ret == 0) {
                        if (GetOctetString(cert, &extIdx, &extLen, certSz) < 0)
                            ret = ASN_PARSE_E;
                    }

                    if (ret == 0) {
                        switch (oid) {
                        case AUTH_KEY_OID:
                            if (GetSequence(cert, &extIdx, &extLen, certSz) < 0)
                                ret = ASN_PARSE_E;

                            if (ret == 0 && (extIdx + 1) >= certSz)
                                ret = BUFFER_E;

                            if (ret == 0 &&
                                    GetASNTag(cert, &extIdx, &tag, certSz) == 0 &&
                                    tag == (ASN_CONTEXT_SPECIFIC | 0)) {
                                if (GetLength(cert, &extIdx, &extLen, certSz) <= 0)
                                    ret = ASN_PARSE_E;
                                if (ret == 0) {
                                    extAuthKeyIdSet = 1;
                                    if (extLen == KEYID_SIZE)
                                        XMEMCPY(hash, cert + extIdx, extLen);
                                    else {
                                        ret = CalcHashId(cert + extIdx, extLen,
                                                                              hash);
                                    }
                                }
                            }
                            break;

                        default:
                            break;
                        }
                    }
                    idx += len;
                }
            }
        }
    }
    else if (ret == 0) {
        idx += len;
    }

    if (ret == 0 && pubKey == NULL) {
        if (extAuthKeyIdSet)
            ca = GetCA(cm, hash);
        if (ca == NULL) {
            ret = CalcHashId(cert + issuerIdx, issuerSz, hash);
            if (ret == 0)
                ca = GetCAByName(cm, hash);
        }
    }
#else
    if (ret == 0 && pubKey == NULL) {
        ret = CalcHashId(cert + issuerIdx, issuerSz, hash);
        if (ret == 0)
            ca = GetCA(cm, hash);
    }
#endif /* !NO_SKID */
    if (ca == NULL && pubKey == NULL)
        ret = ASN_NO_SIGNER_E;

    if (ret == 0) {
        idx = sigIndex;
        /* signatureAlgorithm */
        if (GetAlgoId(cert, &idx, &oid, oidSigType, certSz) < 0)
            ret = ASN_PARSE_E;
        /* In CSR signature data is not present in body */
        if (req)
            signatureOID = oid;
    }
    if (ret == 0) {
        if (oid != signatureOID)
            ret = ASN_SIG_OID_E;
    }
    if (ret == 0) {
        /* signatureValue */
        if (CheckBitString(cert, &idx, &len, certSz, 1, NULL) < 0)
            ret = ASN_PARSE_E;
    }

    if (ret == 0) {
        if (pubKey != NULL) {
            ret = ConfirmSignature(sigCtx, cert + tbsCertIdx,
                               sigIndex - tbsCertIdx,
                               pubKey, pubKeySz, pubKeyOID,
                               cert + idx, len, signatureOID, NULL);
        }
        else {
            ret = ConfirmSignature(sigCtx, cert + tbsCertIdx,
                               sigIndex - tbsCertIdx,
                               ca->publicKey, ca->pubKeySize, ca->keyOID,
                               cert + idx, len, signatureOID, NULL);
        }
        if (ret != 0) {
            WOLFSSL_MSG("Confirm signature failed");
        }
    }

    FreeSignatureCtx(sigCtx);
#ifdef WOLFSSL_SMALL_STACK
    if (sigCtx != NULL)
        XFREE(sigCtx, heap, DYNAMIC_TYPE_SIGNATURE);
#endif
    return ret;
}