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

/*
 * Original (non-template) ASN.1 implementations.
 * This file is included from asn.c when building without WOLFSSL_ASN_TEMPLATE.
 * It must not be compiled as a separate translation unit.
 */

#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>

#ifndef WOLFSSL_ASN_ORIG_INCLUDED
    #ifndef WOLFSSL_IGNORE_FILE_WARN
        #warning asn_orig.c does not need to be compiled separately from asn.c
    #endif
#else

/* Forward declarations for static functions defined later in this file. */
static int SkipObjectId(const byte* input, word32* inOutIdx, word32 maxIdx);
#ifndef NO_DSA
static WC_INLINE void FreeTmpDsas(byte** tmps, void* heap, int ints);
#endif
#ifndef NO_CERTS
static int GetCertHeader(DecodedCert* cert);
static int GetDate(DecodedCert* cert, int dateType, int verify, int maxIdx);
static int GetValidity(DecodedCert* cert, int verify, int maxIdx);
#endif
static word32 SetOctetString8Bit(word32 len, byte* output);
static word32 SetDigest(const byte* digest, word32 digSz, byte* output);
#ifndef NO_CERTS
static void AddAltName(DecodedCert* cert, DNS_entry* dnsEntry);
#if defined(WOLFSSL_SEP)
static int DecodeSepHwAltName(DecodedCert* cert, const byte* input, word32* idxIn, word32 sz);
#endif
static int DecodeConstructedOtherName(DecodedCert* cert, const byte* input, word32* idx, word32 sz, int oid);
#ifdef WOLFSSL_CERT_GEN
#ifdef WOLFSSL_CERT_REQ
static word32 SetPrintableString(word32 len, byte* output);
static word32 SetUTF8String(word32 len, byte* output);
#endif
static int CopyValidity(byte* output, Cert* cert);
static int SetExtensions(byte* out, word32 outSz, int *IdxInOut, const byte* ext, int extSz);
static int SetExtensionsHeader(byte* out, word32 outSz, word32 extSz);
static int SetCaWithPathLen(byte* out, word32 outSz, byte pathLen);
static int SetCaEx(byte* out, word32 outSz, byte isCa);
static int SetCa(byte* out, word32 outSz);
static int SetBC(byte* out, word32 outSz);
#ifdef WOLFSSL_CERT_EXT
static int SetOidValue(byte* out, word32 outSz, const byte *oid, word32 oidSz, byte *in, word32 inSz);
static int SetSKID(byte* output, word32 outSz, const byte *input, word32 length);
static int SetAKID(byte* output, word32 outSz, byte *input, word32 length, byte rawAkid);
static int SetKeyUsage(byte* output, word32 outSz, word16 input);
static int SetOjectIdValue(byte* output, word32 outSz, word32* idx, const byte* oid, word32 oidSz);
#ifndef IGNORE_NETSCAPE_CERT_TYPE
static int SetNsCertType(Cert* cert, byte* output, word32 outSz, byte input);
#endif
static int SetCRLInfo(Cert* cert, byte* output, word32 outSz, byte* input, int inSz);
#endif
#ifdef WOLFSSL_ALT_NAMES
static int SetAltNames(byte *output, word32 outSz, const byte *input, word32 length, int critical);
#endif
#ifdef WOLFSSL_CERT_REQ
static word32 SetReqAttribSingle(byte* output, word32* idx, char* attr, word32 attrSz, const byte* oid, word32 oidSz, byte printable, word32 extSz);
static int SetReqAttrib(byte* output, Cert* cert, word32 extSz);
#ifdef WOLFSSL_CUSTOM_OID
static int SetCustomObjectId(Cert* cert, byte* output, word32 outSz, CertOidField* custom);
#endif
#endif
#endif
#endif
#if defined(HAVE_ECC) || !defined(NO_DSA)
static word32 is_leading_bit_set(const byte* input, word32 sz);
static word32 trim_leading_zeros(const byte** input, word32 sz);
#endif
#ifdef HAVE_ECC
#ifdef WOLFSSL_CUSTOM_CURVES
static int ASNToHexString(const byte* input, word32* inOutIdx, char** out, word32 inSz, void* heap, int heapType);
static int EccKeyParamCopy(char** dst, char* src, void* heap);
#endif
#endif
#if (defined(HAVE_OCSP) || defined(HAVE_CRL)) && !defined(WOLFCRYPT_ONLY)
static int GetBasicDate(const byte* source, word32* idx, byte* date, byte* format, int maxIdx);
#endif
#if defined(HAVE_OCSP) && !defined(WOLFCRYPT_ONLY)
static int GetEnumerated(const byte* input, word32* inOutIdx, int *value, int sz);
#endif

int GetObjectId(const byte* input, word32* inOutIdx, word32* oid,
                                  word32 oidType, word32 maxIdx)
{
    int ret, length;

    WOLFSSL_ENTER("GetObjectId");

    ret = GetASNObjectId(input, inOutIdx, &length, maxIdx);
    if (ret != 0)
        return ret;

    return GetOID(input, inOutIdx, oid, oidType, length);
}

static int SkipObjectId(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    int    length;
    int ret;

    ret = GetASNObjectId(input, &idx, &length, maxIdx);
    if (ret != 0)
        return ret;

    idx += (word32)length;
    *inOutIdx = idx;

    return 0;
}

static int GetAlgoIdImpl(const byte* input, word32* inOutIdx, word32* oid,
                     word32 oidType, word32 maxIdx, byte *absentParams)
{
    int    length;
    word32 idx = *inOutIdx;
    int    ret;
    *oid = 0;

    WOLFSSL_ENTER("GetAlgoId");

    if (GetSequence(input, &idx, &length, maxIdx) < 0)
        return ASN_PARSE_E;

    if (GetObjectId(input, &idx, oid, oidType, maxIdx) < 0)
        return ASN_OBJECT_ID_E;

    /* could have NULL tag and 0 terminator, but may not */
    if (idx < maxIdx) {
        word32 localIdx = idx; /*use localIdx to not advance when checking tag*/
        byte   tag;

        if (GetASNTag(input, &localIdx, &tag, maxIdx) == 0) {
            if (tag == ASN_TAG_NULL) {
                ret = GetASNNull(input, &idx, maxIdx);
                if (ret != 0)
                    return ret;

                if (absentParams != NULL) {
                    *absentParams = FALSE;
                }
            }
        }
    }

    *inOutIdx = idx;

    return 0;
}

#ifndef NO_RSA
static int _RsaPrivateKeyDecode(const byte* input, word32* inOutIdx,
    RsaKey* key, int* keySz, word32 inSz)
{
    int version, length;
    word32 algId = 0;
    int i;

    if (inOutIdx == NULL || input == NULL || (key == NULL && keySz == NULL)) {
        return BAD_FUNC_ARG;
    }

    /* if has pkcs8 header skip it */
    if (ToTraditionalInline_ex(input, inOutIdx, inSz, &algId) < 0) {
        /* ignore error, did not have pkcs8 header */
    }

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

    if (GetMyVersion(input, inOutIdx, &version, inSz) < 0)
        return ASN_PARSE_E;

    if (key == NULL) {
        /* Modulus */
        if (GetASNInt(input, inOutIdx, keySz, inSz) < 0) {
            return ASN_PARSE_E;
        }
        *inOutIdx += (word32)*keySz;
        for (i = 1; i < RSA_INTS; i++) {
            if (SkipInt(input, inOutIdx, inSz) < 0) {
                return ASN_RSA_KEY_E;
            }
        }
    }
    else {
        key->type = RSA_PRIVATE;

    #ifdef WOLFSSL_CHECK_MEM_ZERO
        mp_memzero_add("Decode RSA key d", &key->d);
        mp_memzero_add("Decode RSA key p", &key->p);
        mp_memzero_add("Decode RSA key q", &key->q);
    #if (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || \
        !defined(RSA_LOW_MEM)) && !defined(WOLFSSL_RSA_PUBLIC_ONLY)
        mp_memzero_add("Decode RSA key dP", &key->dP);
        mp_memzero_add("Decode RSA key dQ", &key->dQ);
        mp_memzero_add("Decode RSA key u", &key->u);
    #endif
    #endif

        /* Extract all public fields. */
        for (i = 0; i < RSA_INT_CNT; i++) {
            if (GetInt(GetRsaInt(key, i),  input, inOutIdx, inSz) < 0) {
                for (i--; i >= 0; i--) {
                    mp_clear(GetRsaInt(key, i));
                }
                return ASN_RSA_KEY_E;
            }
         }
    #if RSA_INT_CNT != RSA_MAX_INT_CNT
        for (; i < RSA_MAX_INT_CNT; i++) {
             if (SkipInt(input, inOutIdx, inSz) < 0) {
                for (i = RSA_INT_CNT - 1; i >= 0; i--) {
                    mp_clear(GetRsaInt(key, i));
                }
                return ASN_RSA_KEY_E;
            }
         }
   #endif

    #if defined(WOLFSSL_XILINX_CRYPT) || defined(WOLFSSL_CRYPTOCELL)
        if (wc_InitRsaHw(key) != 0) {
            return BAD_STATE_E;
        }
    #endif
    }

    return 0;
}

#endif
int ToTraditionalInline_ex2(const byte* input, word32* inOutIdx, word32 sz,
                            word32* algId, word32* eccOid)
{
    word32 idx;
    int    version, length;
    int    ret;
    byte   tag;

    if (input == NULL || inOutIdx == NULL)
        return BAD_FUNC_ARG;

    idx = *inOutIdx;

    if (GetSequence(input, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    if (GetMyVersion(input, &idx, &version, sz) < 0)
        return ASN_PARSE_E;

    if (GetAlgoId(input, &idx, algId, oidKeyType, sz) < 0)
        return ASN_PARSE_E;

    if (GetASNTag(input, &idx, &tag, sz) < 0)
        return ASN_PARSE_E;
    idx = idx - 1; /* reset idx after finding tag */

#if defined(WC_RSA_PSS) && !defined(NO_RSA)
    if (*algId == RSAPSSk && tag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
        word32 seqIdx = idx;
        int seqLen;
        /* Not set when -1. */
        enum wc_HashType hash = WC_HASH_TYPE_NONE;
        int mgf = -1;
        int saltLen = 0;

        if (GetSequence(input, &idx, &seqLen, sz) < 0) {
            return ASN_PARSE_E;
        }
        /* Get the private key parameters. */
        ret = DecodeRsaPssParams(input + seqIdx,
            seqLen + idx - seqIdx, &hash, &mgf, &saltLen);
        if (ret != 0) {
            return ASN_PARSE_E;
        }
        /* TODO: store parameters so that usage can be checked. */
        idx += seqLen;
    }
#endif /* WC_RSA_PSS && !NO_RSA */

    if (tag == ASN_OBJECT_ID) {
        if ((*algId == ECDSAk) && (eccOid != NULL)) {
            if (GetObjectId(input, &idx, eccOid, oidCurveType, sz) < 0)
                return ASN_PARSE_E;
        }
        else {
            if (SkipObjectId(input, &idx, sz) < 0)
                return ASN_PARSE_E;
        }
    }

    ret = GetOctetString(input, &idx, &length, sz);
    if (ret < 0) {
        if (ret == WC_NO_ERR_TRACE(BUFFER_E))
            return ASN_PARSE_E;
        /* Some private keys don't expect an octet string - ignore error. */
        WOLFSSL_MSG("Couldn't find Octet string");
        length = 0;
    }

    *inOutIdx = idx;

    return length;
}

#if defined(HAVE_PKCS8)
int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz,
        int algoID, const byte* curveOID, word32 oidSz)
{
    word32 keyIdx = 0;
    word32 tmpSz  = 0;
    word32 sz;
    word32 tmpAlgId = 0;

    /* If out is NULL then return the max size needed
     * + 2 for ASN_OBJECT_ID and ASN_OCTET_STRING tags */
    if (out == NULL && outSz != NULL) {
        *outSz = keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ
                 + MAX_LENGTH_SZ + MAX_LENGTH_SZ + 2;

        if (curveOID != NULL)
            *outSz += oidSz + MAX_LENGTH_SZ + 1;

        WOLFSSL_MSG("Checking size of PKCS8");

        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    WOLFSSL_ENTER("wc_CreatePKCS8Key");

    if (key == NULL || out == NULL || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    /* check the buffer has enough room for largest possible size */
    if (curveOID != NULL) {
        sz = keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ + MAX_LENGTH_SZ +
            MAX_LENGTH_SZ + 3 + oidSz + MAX_LENGTH_SZ;
        if ((keySz > sz) || (oidSz > sz) || (*outSz < sz))
            return BUFFER_E;
    }
    else {
        oidSz = 0; /* with no curveOID oid size must be 0 */
        sz= keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ + MAX_LENGTH_SZ +
            MAX_LENGTH_SZ + 2;
        if ((keySz > sz) || (*outSz < sz))
            return BUFFER_E;
    }

    /* sanity check: make sure the key doesn't already have a PKCS 8 header */
    if (ToTraditionalInline_ex(key, &keyIdx, keySz, &tmpAlgId) >= 0) {
        (void)tmpAlgId;
        return ASN_PARSE_E;
    }

    /* PrivateKeyInfo ::= SEQUENCE */
    keyIdx = MAX_SEQ_SZ; /* save room for sequence */

    /*  version Version
     *  no header information just INTEGER */
    sz = (word32)SetMyVersion(PKCS8v0, out + keyIdx, 0);
    tmpSz += sz; keyIdx += sz;
    /*  privateKeyAlgorithm PrivateKeyAlgorithmIdentifier */
    sz = 0; /* set sz to 0 and get privateKey oid buffer size needed */
    if (curveOID != NULL && oidSz > 0) {
        byte buf[MAX_LENGTH_SZ];
        sz = SetLength(oidSz, buf);
        sz += 1; /* plus one for ASN object id */
    }
    sz = (word32)SetAlgoID(algoID, out + keyIdx, oidKeyType, (int)(oidSz + sz));
    tmpSz += sz; keyIdx += sz;

    /*  privateKey          PrivateKey *
     * pkcs8 ecc uses slightly different format. Places curve oid in
     * buffer */
    if (curveOID != NULL && oidSz > 0) {
        sz = (word32)SetObjectId((int)oidSz, out + keyIdx);
        keyIdx += sz; tmpSz += sz;
        XMEMCPY(out + keyIdx, curveOID, oidSz);
        keyIdx += oidSz; tmpSz += oidSz;
    }

    sz = (word32)SetOctetString(keySz, out + keyIdx);
    keyIdx += sz; tmpSz += sz;
    XMEMCPY(out + keyIdx, key, keySz);
    tmpSz += keySz;

    /*  attributes          optional
     * No attributes currently added */

    /* rewind and add sequence */
    sz = SetSequence(tmpSz, out);
    XMEMMOVE(out + sz, out + MAX_SEQ_SZ, tmpSz);

    *outSz = tmpSz + sz;
    return (int)(tmpSz + sz);
}

#endif
#ifndef NO_PWDBASED
#ifdef HAVE_PKCS8
int DecryptContent(byte* input, word32 sz, const char* password, int passwordSz)
{
    word32 inOutIdx = 0, seqEnd, oid, shaOid = 0, seqPkcs5End = sz;
    int    ret = 0, first, second, length = 0, version, saltSz, id = 0;
    int    iterations = 0, keySz = 0;
#ifdef WOLFSSL_SMALL_STACK
    byte*  salt = NULL;
    byte*  cbcIv = NULL;
#else
    byte   salt[MAX_SALT_SIZE];
    byte   cbcIv[MAX_IV_SIZE];
#endif
    byte   tag;

    if (passwordSz < 0) {
        WOLFSSL_MSG("Bad password size");
        return BAD_FUNC_ARG;
    }

    if (GetAlgoId(input, &inOutIdx, &oid, oidIgnoreType, sz) < 0) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    first  = input[inOutIdx - 2];   /* PKCS version always 2nd to last byte */
    second = input[inOutIdx - 1];   /* version.algo, algo id last byte */

    if (CheckAlgo(first, second, &id, &version, NULL) < 0) {
        ERROR_OUT(ASN_INPUT_E, exit_dc); /* Algo ID error */
    }

    if (version == PKCS5v2) {
        if (GetSequence(input, &inOutIdx, &length, sz) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }
        seqPkcs5End = inOutIdx + length;

        if (GetAlgoId(input, &inOutIdx, &oid, oidKdfType, sz) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }

        if (oid != PBKDF2_OID) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }
    }

    if (GetSequence(input, &inOutIdx, &length, sz) <= 0) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }
    /* Find the end of this SEQUENCE so we can check for the OPTIONAL and
     * DEFAULT items. */
    seqEnd = inOutIdx + (word32)length;

    ret = GetOctetString(input, &inOutIdx, &saltSz, seqEnd);
    if (ret < 0)
        goto exit_dc;

    if (saltSz > MAX_SALT_SIZE) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    WC_ALLOC_VAR_EX(salt, byte, MAX_SALT_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER,
        ERROR_OUT(MEMORY_E,exit_dc));

    XMEMCPY(salt, &input[inOutIdx], (size_t)saltSz);
    inOutIdx += (word32)saltSz;

    if (GetShortInt(input, &inOutIdx, &iterations, seqEnd) < 0) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    /* OPTIONAL key length */
    if (seqEnd > inOutIdx) {
        word32 localIdx = inOutIdx;

        if (GetASNTag(input, &localIdx, &tag, seqEnd) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }

        if (tag == ASN_INTEGER &&
                GetShortInt(input, &inOutIdx, &keySz, seqEnd) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }
    }

    /* DEFAULT HMAC is SHA-1 */
    if (seqEnd > inOutIdx) {
        if (GetAlgoId(input, &inOutIdx, &oid, oidHmacType, seqEnd) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }

        shaOid = oid;
    }

    WC_ALLOC_VAR_EX(cbcIv, byte, MAX_IV_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER,
        ERROR_OUT(MEMORY_E,exit_dc));

    if (version == PKCS5v2) {
        /* get encryption algo */
        if (GetAlgoId(input, &inOutIdx, &oid, oidBlkType, seqPkcs5End) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }

        if (CheckAlgoV2((int)oid, &id, NULL) < 0) {
            ERROR_OUT(ASN_PARSE_E, exit_dc); /* PKCS v2 algo id error */
        }

        if (shaOid == 0)
            shaOid = oid;

        ret = GetOctetString(input, &inOutIdx, &length, seqPkcs5End);
        if (ret < 0)
            goto exit_dc;

        if (length > MAX_IV_SIZE) {
            ERROR_OUT(ASN_PARSE_E, exit_dc);
        }

        XMEMCPY(cbcIv, &input[inOutIdx], (size_t)length);
        inOutIdx += (word32)length;
    }

    if (GetASNTag(input, &inOutIdx, &tag, sz) < 0) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    if (tag != (ASN_CONTEXT_SPECIFIC | 0) && tag != ASN_OCTET_STRING) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    if (GetLength(input, &inOutIdx, &length, sz) < 0) {
        ERROR_OUT(ASN_PARSE_E, exit_dc);
    }

    ret = wc_CryptKey(password, passwordSz, salt, saltSz, iterations, id,
                   input + inOutIdx, length, version, cbcIv, 0, (int)shaOid);

exit_dc:
    WC_FREE_VAR_EX(salt, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(cbcIv, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    if (ret == 0) {
        XMEMMOVE(input, input + inOutIdx, (size_t)length);
        ret = length;
    }

    return ret;
}

#endif
#ifdef HAVE_PKCS12
int EncryptContent(byte* input, word32 inputSz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int vAlgo,
        int encAlgId, byte* salt, word32 saltSz, int itt, int hmacOid,
        WC_RNG* rng, void* heap)
{
    word32 sz;
    word32 inOutIdx = 0;
    word32 tmpIdx   = 0;
    word32 totalSz  = 0;
    word32 seqSz;
    word32 innerSz;
    int    ret;
    int    version, id = PBE_NONE, blockSz = 0;
#ifdef WOLFSSL_SMALL_STACK
    byte*  saltTmp = NULL;
    byte*  cbcIv   = NULL;
#else
    byte   saltTmp[MAX_SALT_SIZE];
    byte   cbcIv[MAX_IV_SIZE];
#endif
    byte   seq[MAX_SEQ_SZ];
    byte   shr[MAX_SHORT_SZ];
    word32 maxShr = MAX_SHORT_SZ;
    word32 algoSz;
    const  byte* algoName;

    (void)encAlgId;
    (void)hmacOid;
    (void)heap;

    (void)EncryptContentPBES2;

    WOLFSSL_ENTER("EncryptContent");

    if (CheckAlgo(vPKCS, vAlgo, &id, &version, &blockSz) < 0)
        return ASN_INPUT_E;  /* Algo ID error */

    if (version == PKCS5v2) {
        WOLFSSL_MSG("PKCS#5 version 2 not supported yet");
        return BAD_FUNC_ARG;
    }

    if (saltSz > MAX_SALT_SIZE)
        return ASN_PARSE_E;

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

    /* calculate size */
    /* size of constructed string at end */
    sz = wc_PkcsPad(NULL, inputSz, (word32)blockSz);
    totalSz  = ASN_TAG_SZ;
    totalSz += SetLength(sz, seq);
    totalSz += sz;

    /* size of sequence holding object id and sub sequence of salt and itt */
    algoName = OidFromId((word32)id, oidPBEType, &algoSz);
    if (algoName == NULL) {
        WOLFSSL_MSG("Unknown Algorithm");
        return 0;
    }
    innerSz = (word32)SetObjectId((int)algoSz, seq);
    innerSz += algoSz;

    /* get subsequence of salt and itt */
    if (salt == NULL || saltSz == 0) {
        sz = 8;
    }
    else {
        sz = saltSz;
    }
    seqSz  = SetOctetString(sz, seq);
    seqSz += sz;

    tmpIdx = 0;
    ret = SetShortInt(shr, &tmpIdx, (word32)itt, maxShr);
    if (ret >= 0) {
        seqSz += (word32)ret;
    }
    else {
        return ret;
    }
    innerSz += seqSz + SetSequence(seqSz, seq);
    totalSz += innerSz + SetSequence(innerSz, seq);

    if (out == NULL) {
        *outSz = totalSz;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    inOutIdx = 0;
    if (totalSz > *outSz)
        return BUFFER_E;

    inOutIdx += SetSequence(innerSz, out + inOutIdx);
    inOutIdx += (word32)SetObjectId((int)algoSz, out + inOutIdx);
    XMEMCPY(out + inOutIdx, algoName, algoSz);
    inOutIdx += algoSz;
    inOutIdx += SetSequence(seqSz, out + inOutIdx);

    /* create random salt if one not provided */
    if (salt == NULL || saltSz == 0) {
        saltSz = 8;
        WC_ALLOC_VAR_EX(saltTmp, byte, saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER,
            return MEMORY_E);
        salt = saltTmp;

        if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
            WOLFSSL_MSG("Error generating random salt");
            WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
            return ret;
        }
    }
    inOutIdx += SetOctetString(saltSz, out + inOutIdx);
    if (saltSz + inOutIdx > *outSz) {
        WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return BUFFER_E;
    }
    XMEMCPY(out + inOutIdx, salt, saltSz);
    inOutIdx += saltSz;

    /* place iteration setting in buffer */
    ret = SetShortInt(out, &inOutIdx, (word32)itt, *outSz);
    if (ret < 0) {
        WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return ret;
    }

    if (inOutIdx + 1 > *outSz) {
        WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return BUFFER_E;
    }
    out[inOutIdx++] = ASN_CONTEXT_SPECIFIC | 0;

    /* get pad size and verify buffer room */
    sz = wc_PkcsPad(NULL, inputSz, (word32)blockSz);
    if (sz + inOutIdx > *outSz) {
        WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return BUFFER_E;
    }
    inOutIdx += SetLength(sz, out + inOutIdx);

    /* copy input to output buffer and pad end */
    XMEMCPY(out + inOutIdx, input, inputSz);
    sz = wc_PkcsPad(out + inOutIdx, inputSz, (word32)blockSz);
#ifdef WOLFSSL_SMALL_STACK
    cbcIv = (byte*)XMALLOC(MAX_IV_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (cbcIv == NULL) {
        XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return MEMORY_E;
    }
#endif

    /* encrypt */
    if ((ret = wc_CryptKey(password, passwordSz, salt, (int)saltSz, itt, id,
                   out + inOutIdx, (int)sz, version, cbcIv, 1, 0)) < 0) {

        WC_FREE_VAR_EX(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return ret;  /* encrypt failure */
    }

    WC_FREE_VAR_EX(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);

    (void)rng;

    return (int)(inOutIdx + sz);
}

#endif
#endif
#ifndef NO_RSA
#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
static int RsaPublicKeyDecodeRawIndex(const byte* input, word32* inOutIdx,
                                      word32 inSz, word32* key_n,
                                      word32* key_n_len, word32* key_e,
                                      word32* key_e_len)
{
    int ret = 0;
    int length = 0;

#if defined(OPENSSL_EXTRA) || defined(RSA_DECODE_EXTRA)
    byte b;
#endif

    if (input == NULL || inOutIdx == NULL)
        return BAD_FUNC_ARG;

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

#if defined(OPENSSL_EXTRA) || defined(RSA_DECODE_EXTRA)
    if ((*inOutIdx + 1) > inSz)
        return BUFFER_E;

    b = input[*inOutIdx];
    if (b != ASN_INTEGER) {
        /* not from decoded cert, will have algo id, skip past */
        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        if (SkipObjectId(input, inOutIdx, inSz) < 0)
            return ASN_PARSE_E;

        /* Option NULL ASN.1 tag */
        if (*inOutIdx  >= inSz) {
            return BUFFER_E;
        }
        if (input[*inOutIdx] == ASN_TAG_NULL) {
            ret = GetASNNull(input, inOutIdx, inSz);
            if (ret != 0)
                return ret;
        }
        /* TODO: support RSA PSS */

        /* should have bit tag length and seq next */
        ret = CheckBitString(input, inOutIdx, NULL, inSz, 1, NULL);
        if (ret != 0)
            return ret;

        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;
    }
#endif /* OPENSSL_EXTRA */

    /* Get modulus */
    ret = GetASNInt(input, inOutIdx, &length, inSz);
    *key_n += *inOutIdx;
    if (ret < 0) {
        return ASN_RSA_KEY_E;
    }
    if (key_n_len)
        *key_n_len = length;
    *inOutIdx += length;

    /* Get exponent */
    ret = GetASNInt(input, inOutIdx, &length, inSz);
    *key_e += *inOutIdx;
    if (ret < 0) {
        return ASN_RSA_KEY_E;
    }
    if (key_e_len)
        *key_e_len = length;
    return ret;
}

#endif
int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz,
    const byte** n, word32* nSz, const byte** e, word32* eSz)
{
    int ret = 0;
    int length = 0;
    int firstLen = 0;
    word32 seqEndIdx = inSz;
#if defined(OPENSSL_EXTRA) || defined(RSA_DECODE_EXTRA)
    word32 localIdx;
    byte   tag;
#endif

    if (input == NULL || inOutIdx == NULL)
        return BAD_FUNC_ARG;

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

#if defined(OPENSSL_EXTRA) || defined(RSA_DECODE_EXTRA)
    localIdx = *inOutIdx;
    if (GetASNTag(input, &localIdx, &tag, inSz) < 0)
        return BUFFER_E;

    if (tag != ASN_INTEGER) {
        /* not from decoded cert, will have algo id, skip past */
        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        if (SkipObjectId(input, inOutIdx, inSz) < 0)
            return ASN_PARSE_E;

        /* Option NULL ASN.1 tag */
        if (*inOutIdx  >= inSz) {
            return BUFFER_E;
        }

        localIdx = *inOutIdx;
        if (GetASNTag(input, &localIdx, &tag, inSz) < 0)
            return ASN_PARSE_E;

        if (tag == ASN_TAG_NULL) {
            ret = GetASNNull(input, inOutIdx, inSz);
            if (ret != 0)
                return ret;
        }
    #ifdef WC_RSA_PSS
        /* Skip RSA PSS parameters. */
        else if (tag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
            if (GetSequence(input, inOutIdx, &length, inSz) < 0)
                return ASN_PARSE_E;
            *inOutIdx += length;
        }
    #endif

        /* should have bit tag length and seq next */
        ret = CheckBitString(input, inOutIdx, NULL, inSz, 1, NULL);
        if (ret != 0)
            return ret;

        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        /* Calculate where the sequence should end for public key validation */
        seqEndIdx = *inOutIdx + (word32)length;
    }
#endif /* OPENSSL_EXTRA */

    /* Get modulus */
    ret = GetASNInt(input, inOutIdx, &firstLen, seqEndIdx);
    if (ret < 0) {
        return ASN_RSA_KEY_E;
    }
    if (nSz)
        *nSz = (word32)firstLen;
    if (n)
        *n = &input[*inOutIdx];
    *inOutIdx += (word32)firstLen;

    /* Get exponent */
    ret = GetASNInt(input, inOutIdx, &length, seqEndIdx);
    if (ret < 0) {
        return ASN_RSA_KEY_E;
    }
    if (eSz)
        *eSz = (word32)length;
    if (e)
        *e = &input[*inOutIdx];
    *inOutIdx += (word32)length;

    /* Detect if this is an RSA private key being passed as public key.
     * An RSA private key has: version (small), modulus (large), exponent,
     * followed by more integers (d, p, q, etc.).
     * An RSA public key has: modulus (large), exponent, and nothing more.
     * If the first integer is small (like version 0) AND there is more data
     * remaining in the sequence, this is likely a private key. */
    if (firstLen <= MAX_VERSION_SZ && *inOutIdx < seqEndIdx) {
        /* First integer is small and there's more data - looks like
         * version field of a private key, not a modulus */
        return ASN_RSA_KEY_E;
    }

    return ret;
}

#endif
#ifndef NO_DH
int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz)
{
    int ret = 0;
    int length;
#ifdef WOLFSSL_DH_EXTRA
    #if !defined(HAVE_FIPS) || \
        (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))
    word32 oid = 0, temp = 0;
    #endif
#endif

    WOLFSSL_ENTER("wc_DhKeyDecode");

    if (inOutIdx == NULL)
        return BAD_FUNC_ARG;

    if (key == NULL)
        return BAD_FUNC_ARG;

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

#ifdef WOLFSSL_DH_EXTRA
    #if !defined(HAVE_FIPS) || \
        (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))
    temp = *inOutIdx;
    #endif
#endif
    /* Assume input started after 1.2.840.113549.1.3.1 dhKeyAgreement */
    if (GetInt(&key->p, input, inOutIdx, inSz) < 0) {
        ret = ASN_DH_KEY_E;
    }
    if (ret == 0 && GetInt(&key->g, input, inOutIdx, inSz) < 0) {
        mp_clear(&key->p);
        ret = ASN_DH_KEY_E;
    }

#ifdef WOLFSSL_DH_EXTRA
    #if !defined(HAVE_FIPS) || \
        (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))
    /* If ASN_DH_KEY_E: Check if input started at beginning of key */
    if (ret == WC_NO_ERR_TRACE(ASN_DH_KEY_E)) {
        *inOutIdx = temp;

        /* the version (0) - private only (for public skip) */
        if (GetASNInt(input, inOutIdx, &length, inSz) == 0) {
            *inOutIdx += (word32)length;
        }

        /* Size of dhKeyAgreement section */
        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        /* Check for dhKeyAgreement */
        ret = GetObjectId(input, inOutIdx, &oid, oidKeyType, inSz);
        if (oid != DHk || ret < 0)
            return ASN_DH_KEY_E;

        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        if (GetInt(&key->p, input, inOutIdx, inSz) < 0) {
            return ASN_DH_KEY_E;
        }
        if (ret == 0 && GetInt(&key->g, input, inOutIdx, inSz) < 0) {
            mp_clear(&key->p);
            return ASN_DH_KEY_E;
        }
    }

    temp = *inOutIdx;
    ret = (CheckBitString(input, inOutIdx, &length, inSz, 0, NULL) == 0);
    if (ret > 0) {
        /* Found Bit String */
        if (GetInt(&key->pub, input, inOutIdx, inSz) == 0) {
            WOLFSSL_MSG("Found Public Key");
            ret = 0;
        }
    } else {
        *inOutIdx = temp;
        ret = (GetOctetString(input, inOutIdx, &length, inSz) >= 0);
        if (ret > 0) {
            /* Found Octet String */
            if (GetInt(&key->priv, input, inOutIdx, inSz) == 0) {
                WOLFSSL_MSG("Found Private Key");

                /* Compute public */
                ret = mp_exptmod(&key->g, &key->priv, &key->p, &key->pub);
            }
        } else {
            /* Don't use length from failed CheckBitString/GetOctetString */
            *inOutIdx = temp;
            ret = 0;
        }
    }
    #endif /* !HAVE_FIPS || HAVE_FIPS_VERSION > 2 */
#endif /* WOLFSSL_DH_EXTRA */

    WOLFSSL_LEAVE("wc_DhKeyDecode", ret);

    return ret;
}

#ifdef WOLFSSL_DH_EXTRA
int wc_DhKeyToDer(DhKey* key, byte* output, word32* outSz, int exportPriv)
{
    int ret, privSz = 0, pubSz = 0;
    word32 keySz, idx, len, total;

    if (key == NULL || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    /* determine size */
    if (exportPriv) {
        /* octet string: priv */
        privSz = SetASNIntMP(&key->priv, -1, NULL);
        if (privSz < 0)
            return privSz;
        idx = 1 + SetLength((word32)privSz, NULL) +
            (word32)privSz; /* +1 for ASN_OCTET_STRING */
    }
    else {
        /* bit string: public */
        pubSz = SetASNIntMP(&key->pub, -1, NULL);
        if (pubSz < 0)
            return pubSz;
        idx = SetBitString((word32)pubSz, 0, NULL) + (word32)pubSz;
    }
    keySz = idx;

    /* DH Parameters sequence with P and G */
    total = 0;
    ret = wc_DhParamsToDer(key, NULL, &total);
    if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E))
        return ret;
    idx += total;

    /* object dhKeyAgreement 1.2.840.113549.1.3.1 */
    idx += (word32)SetObjectId(sizeof(keyDhOid), NULL);
    idx += (word32)sizeof(keyDhOid);
    len = idx - keySz;
    /* sequence - all but pub/priv */
    idx += SetSequence(len, NULL);
    if (exportPriv) {
        /* version: 0 (ASN_INTEGER, 0x01, 0x00) */
        idx += 3;
    }
    /* sequence */
    total = idx + SetSequence(idx, NULL);

    /* if no output, then just getting size */
    if (output == NULL) {
        *outSz = total;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    /* make sure output fits in buffer */
    if (total > *outSz) {
        return BUFFER_E;
    }
    total = idx;

    /* sequence */
    idx = SetSequence(total, output);
    if (exportPriv) {
        /* version: 0 */
        idx += (word32)SetMyVersion(0, output + idx, 0);
    }
    /* sequence - all but pub/priv */
    idx += SetSequence(len, output + idx);
    /* object dhKeyAgreement 1.2.840.113549.1.3.1 */
    idx += (word32)SetObjectId(sizeof(keyDhOid), output + idx);
    XMEMCPY(output + idx, keyDhOid, sizeof(keyDhOid));
    idx += sizeof(keyDhOid);

    /* DH Parameters sequence with P and G */
    total = *outSz - idx;
    ret = wc_DhParamsToDer(key, output + idx, &total);
    if (ret < 0)
        return ret;
    idx += total;

    /* octet string: priv */
    if (exportPriv) {
        idx += (word32)SetOctetString((word32)privSz, output + idx);
        idx += (word32)SetASNIntMP(&key->priv, -1, output + idx);
    }
    else {
        /* bit string: public */
        idx += (word32)SetBitString((word32)pubSz, 0, output + idx);
        idx += (word32)SetASNIntMP(&key->pub, -1, output + idx);
    }
    *outSz = idx;

    return (int)idx;
}

int wc_DhParamsToDer(DhKey* key, byte* output, word32* outSz)
{
    int ret;
    word32 idx, total;

    if (key == NULL || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    /* determine size */
    /* integer - g */
    ret = SetASNIntMP(&key->g, -1, NULL);
    if (ret < 0)
        return ret;
    idx = (word32)ret;
    /* integer - p */
    ret = SetASNIntMP(&key->p, -1, NULL);
    if (ret < 0)
        return ret;
    idx += (word32)ret;
    total = idx;
     /* sequence */
    idx += SetSequence(idx, NULL);

    if (output == NULL) {
        *outSz = idx;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* make sure output fits in buffer */
    if (idx > *outSz) {
        return BUFFER_E;
    }


    /* write DH parameters */
    /* sequence - for P and G only */
    idx = SetSequence(total, output);
    /* integer - p */
    ret = SetASNIntMP(&key->p, -1, output + idx);
    if (ret < 0)
        return ret;
    idx += (word32)ret;
    /* integer - g */
    ret = SetASNIntMP(&key->g, -1, output + idx);
    if (ret < 0)
        return ret;
    idx += (word32)ret;
    *outSz = idx;

    return (int)idx;
}

#endif
int wc_DhParamsLoad(const byte* input, word32 inSz, byte* p, word32* pInOutSz,
                 byte* g, word32* gInOutSz)
{
    word32 idx = 0;
    int    ret;
    int    length;

    if (GetSequence(input, &idx, &length, inSz) <= 0)
        return ASN_PARSE_E;

    ret = GetASNInt(input, &idx, &length, inSz);
    if (ret != 0)
        return ret;

    if (length <= (int)*pInOutSz) {
        XMEMCPY(p, &input[idx], (size_t)length);
        *pInOutSz = (word32)length;
    }
    else {
        return BUFFER_E;
    }
    idx += (word32)length;

    ret = GetASNInt(input, &idx, &length, inSz);
    if (ret != 0)
        return ret;

    if (length <= (int)*gInOutSz) {
        XMEMCPY(g, &input[idx], (size_t)length);
        *gInOutSz = (word32)length;
    }
    else {
        return BUFFER_E;
    }

    return 0;
}

#endif
#ifndef NO_DSA
int wc_DsaPublicKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                          word32 inSz)
{
    int    length;
    int    ret = 0;
    word32 oid;
    word32 maxIdx;

    if (input == NULL || inOutIdx == NULL || key == NULL)
        return BAD_FUNC_ARG;

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

    maxIdx = (word32)(*inOutIdx + (word32)length);
    if (GetInt(&key->p,  input, inOutIdx, maxIdx) < 0 ||
        GetInt(&key->q,  input, inOutIdx, maxIdx) < 0 ||
        GetInt(&key->g,  input, inOutIdx, maxIdx) < 0 ||
        GetInt(&key->y,  input, inOutIdx, maxIdx) < 0 )
        ret = ASN_DH_KEY_E;

    if (ret != 0) {
        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        ret = GetObjectId(input, inOutIdx, &oid, oidIgnoreType, inSz);
        if (ret != 0)
            return ret;

        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        if (GetInt(&key->p,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->q,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->g,  input, inOutIdx, inSz) < 0)
            return ASN_DH_KEY_E;

        if (CheckBitString(input, inOutIdx, &length, inSz, 0, NULL) < 0)
            return ASN_PARSE_E;

        if (GetInt(&key->y,  input, inOutIdx, inSz) < 0 )
            return ASN_DH_KEY_E;

        ret = 0;
    }

    key->type = DSA_PUBLIC;
    return ret;
}

int wc_DsaPrivateKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                           word32 inSz)
{
    int length, version, ret = 0, temp = 0;
    word32 algId = 0;

    /* Sanity checks on input */
    if (input == NULL || inOutIdx == NULL || key == NULL) {
        return BAD_FUNC_ARG;
    }

    /* if has pkcs8 header skip it */
    if (ToTraditionalInline_ex(input, inOutIdx, inSz, &algId) < 0) {
        /* ignore error, did not have pkcs8 header */
    }

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

    temp = (int)*inOutIdx;

    /* Default case expects a certificate with OctetString but no version ID */
    ret = GetInt(&key->p, input, inOutIdx, inSz);
    if (ret < 0) {
        mp_clear(&key->p);
        ret = ASN_PARSE_E;
    }
    else {
        ret = GetInt(&key->q, input, inOutIdx, inSz);
        if (ret < 0) {
            mp_clear(&key->p);
            mp_clear(&key->q);
            ret = ASN_PARSE_E;
        }
        else {
            ret = GetInt(&key->g, input, inOutIdx, inSz);
            if (ret < 0) {
                mp_clear(&key->p);
                mp_clear(&key->q);
                mp_clear(&key->g);
                ret = ASN_PARSE_E;
            }
            else {
                ret = GetOctetString(input, inOutIdx, &length, inSz);
                if (ret < 0) {
                    mp_clear(&key->p);
                    mp_clear(&key->q);
                    mp_clear(&key->g);
                    ret = ASN_PARSE_E;
                }
                else {
                    ret = GetInt(&key->y, input, inOutIdx, inSz);
                    if (ret < 0) {
                        mp_clear(&key->p);
                        mp_clear(&key->q);
                        mp_clear(&key->g);
                        mp_clear(&key->y);
                        ret = ASN_PARSE_E;
                    }
                }
            }
        }
    }
    /* An alternate pass if default certificate fails parsing */
    if (ret == WC_NO_ERR_TRACE(ASN_PARSE_E)) {
        *inOutIdx = (word32)temp;
        if (GetMyVersion(input, inOutIdx, &version, inSz) < 0)
            return ASN_PARSE_E;

        if (GetInt(&key->p,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->q,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->g,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->y,  input, inOutIdx, inSz) < 0 ||
            GetInt(&key->x,  input, inOutIdx, inSz) < 0 )
            return ASN_DH_KEY_E;
    }

    key->type = DSA_PRIVATE;
    return 0;
}

/* Release Tmp DSA resources */
static WC_INLINE void FreeTmpDsas(byte** tmps, void* heap, int ints)
{
    int i;

    for (i = 0; i < ints; i++)
        XFREE(tmps[i], heap, DYNAMIC_TYPE_DSA);

    (void)heap;
}

#if !defined(HAVE_SELFTEST) && (defined(WOLFSSL_KEY_GEN) || \
defined(WOLFSSL_CERT_GEN))
int wc_SetDsaPublicKey(byte* output, DsaKey* key, int outLen, int with_header)
{
    /* p, g, q = DSA params, y = public exponent */
#ifdef WOLFSSL_SMALL_STACK
    byte* p = NULL;
    byte* g = NULL;
    byte* q = NULL;
    byte* y = NULL;
#else
    byte p[MAX_DSA_INT_SZ];
    byte g[MAX_DSA_INT_SZ];
    byte q[MAX_DSA_INT_SZ];
    byte y[MAX_DSA_INT_SZ];
#endif
    byte innerSeq[MAX_SEQ_SZ];
    byte outerSeq[MAX_SEQ_SZ];
    byte bitString[1 + MAX_LENGTH_SZ + 1];
    int pSz, gSz, qSz, ySz;
    word32 idx, innerSeqSz, outerSeqSz, bitStringSz = 0;
    WOLFSSL_ENTER("wc_SetDsaPublicKey");

    if (output == NULL || key == NULL || outLen < MAX_SEQ_SZ) {
        return BAD_FUNC_ARG;
    }

    /* p */
    WC_ALLOC_VAR_EX(p, byte, MAX_DSA_INT_SZ, key->heap,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
    if ((pSz = SetASNIntMP(&key->p, MAX_DSA_INT_SZ, p)) < 0) {
        WOLFSSL_MSG("SetASNIntMP Error with p");
        WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return pSz;
    }

    /* q */
    WC_ALLOC_VAR_EX(q, byte, MAX_DSA_INT_SZ, key->heap,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
    if ((qSz = SetASNIntMP(&key->q, MAX_DSA_INT_SZ, q)) < 0) {
        WOLFSSL_MSG("SetASNIntMP Error with q");
        WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return qSz;
    }

    /* g */
    WC_ALLOC_VAR_EX(g, byte, MAX_DSA_INT_SZ, key->heap,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
    if ((gSz = SetASNIntMP(&key->g, MAX_DSA_INT_SZ, g)) < 0) {
        WOLFSSL_MSG("SetASNIntMP Error with g");
        WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(g, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return gSz;
    }

    /* y */
    WC_ALLOC_VAR_EX(y, byte, MAX_DSA_INT_SZ, key->heap,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
    if ((ySz = SetASNIntMP(&key->y, MAX_DSA_INT_SZ, y)) < 0) {
        WOLFSSL_MSG("SetASNIntMP Error with y");
        WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(g, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return ySz;
    }

    if (with_header) {
        word32 algoSz;
#ifdef WOLFSSL_SMALL_STACK
        byte* algo = NULL;

        algo = (byte*)XMALLOC(MAX_ALGO_SZ, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (algo == NULL) {
            XFREE(p,    key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            XFREE(q,    key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            XFREE(g,    key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            XFREE(y,    key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            return MEMORY_E;
        }
#else
        byte algo[MAX_ALGO_SZ];
#endif
        innerSeqSz  = SetSequence((word32)(pSz + qSz + gSz), innerSeq);
        algoSz = SetAlgoID(DSAk, algo, oidKeyType, 0);
        bitStringSz  = SetBitString((word32)ySz, 0, bitString);
        outerSeqSz = SetSequence(algoSz + innerSeqSz +
                                 (word32)(pSz + qSz + gSz), outerSeq);

        idx = SetSequence(algoSz + innerSeqSz + (word32)(pSz + qSz + gSz) +
                          bitStringSz + (word32)ySz + outerSeqSz, output);

        /* check output size */
        if ((idx + algoSz + bitStringSz + innerSeqSz +
             (word32)(pSz + qSz + gSz + ySz)) > (word32)outLen)
        {
                WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
                WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
                WC_FREE_VAR_EX(g, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
                WC_FREE_VAR_EX(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
                WC_FREE_VAR_EX(algo, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            WOLFSSL_MSG("Error, output size smaller than outlen");
            return BUFFER_E;
        }

        /* outerSeq */
        XMEMCPY(output + idx, outerSeq, outerSeqSz);
        idx += outerSeqSz;
        /* algo */
        XMEMCPY(output + idx, algo, algoSz);
        idx += algoSz;
        WC_FREE_VAR_EX(algo, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    } else {
        innerSeqSz  = SetSequence((word32)(pSz + qSz + gSz + ySz), innerSeq);

        /* check output size */
        if ((innerSeqSz + (word32)(pSz + qSz + gSz + ySz)) > (word32)outLen) {
            WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            WC_FREE_VAR_EX(g, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            WC_FREE_VAR_EX(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            WOLFSSL_MSG("Error, output size smaller than outlen");
            return BUFFER_E;
        }

        idx = 0;
    }

    /* innerSeq */
    XMEMCPY(output + idx, innerSeq, innerSeqSz);
    idx += innerSeqSz;
    /* p */
    XMEMCPY(output + idx, p, (size_t)pSz);
    idx += (word32)pSz;
    /* q */
    XMEMCPY(output + idx, q, (size_t)qSz);
    idx += (word32)qSz;
    /* g */
    XMEMCPY(output + idx, g, (size_t)gSz);
    idx += (word32)gSz;
    /* bit string */
    if (bitStringSz > 0) {
        XMEMCPY(output + idx, bitString, bitStringSz);
        idx += bitStringSz;
    }
    /* y */
    XMEMCPY(output + idx, y, (size_t)ySz);
    idx += (word32)ySz;

    WC_FREE_VAR_EX(p, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(q, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(g, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    return (int)idx;
}

#endif
static int DsaKeyIntsToDer(DsaKey* key, byte* output, word32* inLen,
                           int ints, int includeVersion)
{
    word32 seqSz = 0, verSz = 0, intTotalLen = 0, outLen, j;
    word32 sizes[DSA_INTS];
    int    i, ret = 0;

    byte  seq[MAX_SEQ_SZ];
    byte  ver[MAX_VERSION_SZ];
    byte* tmps[DSA_INTS];

    if (ints > DSA_INTS || inLen == NULL)
        return BAD_FUNC_ARG;

    XMEMSET(sizes, 0, sizeof(sizes));
    for (i = 0; i < ints; i++)
        tmps[i] = NULL;

    /* write all big ints from key to DER tmps */
    for (i = 0; i < ints; i++) {
        int mpSz;
        mp_int* keyInt = GetDsaInt(key, i);
        word32 rawLen = (word32)mp_unsigned_bin_size(keyInt) + 1;

        tmps[i] = (byte*)XMALLOC(rawLen + MAX_SEQ_SZ, key->heap,
                                                              DYNAMIC_TYPE_DSA);
        if (tmps[i] == NULL) {
            ret = MEMORY_E;
            break;
        }

        mpSz = SetASNIntMP(keyInt, -1, tmps[i]);
        if (mpSz < 0) {
            ret = mpSz;
            break;
        }
        sizes[i] = (word32)mpSz;
        intTotalLen += (word32)mpSz;
    }

    if (ret != 0) {
        FreeTmpDsas(tmps, key->heap, ints);
        return ret;
    }

    /* make headers */
    if (includeVersion)
        verSz = (word32)SetMyVersion(0, ver, FALSE);
    seqSz = SetSequence(verSz + intTotalLen, seq);

    outLen = seqSz + verSz + intTotalLen;
    if (output == NULL) {
        *inLen = outLen;
        FreeTmpDsas(tmps, key->heap, ints);
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    if (outLen > *inLen) {
        FreeTmpDsas(tmps, key->heap, ints);
        return BAD_FUNC_ARG;
    }
    *inLen = outLen;

    /* write to output */
    XMEMCPY(output, seq, seqSz);
    j = seqSz;
    if (includeVersion) {
        XMEMCPY(output + j, ver, verSz);
        j += verSz;
    }

    for (i = 0; i < ints; i++) {
        XMEMCPY(output + j, tmps[i], sizes[i]);
        j += sizes[i];
    }
    FreeTmpDsas(tmps, key->heap, ints);

    return (int)outLen;
}

#endif
#ifndef NO_CERTS
static int GetCertHeader(DecodedCert* cert)
{
    int ret = 0, len;

    if (GetSequence(cert->source, &cert->srcIdx, &len, cert->maxIdx) < 0)
        return ASN_PARSE_E;

    /* Reset the max index for the size indicated in the outer wrapper. */
    cert->maxIdx = (word32)len + cert->srcIdx;
    cert->certBegin = cert->srcIdx;

    if (GetSequence(cert->source, &cert->srcIdx, &len, cert->maxIdx) < 0)
        return ASN_PARSE_E;

    cert->sigIndex = (word32)len + cert->srcIdx;
    if (cert->sigIndex > cert->maxIdx)
        return ASN_PARSE_E;

    if (GetExplicitVersion(cert->source, &cert->srcIdx, &cert->version,
                                                            cert->sigIndex) < 0)
        return ASN_PARSE_E;

    ret = wc_GetSerialNumber(cert->source, &cert->srcIdx, cert->serial,
        &cert->serialSz, cert->sigIndex);
    if (ret < 0) {
        return ret;
    }

    return ret;
}

#endif
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)
static int SetEccPublicKey(byte* output, ecc_key* key, int outLen,
                           int with_header, int comp)
{
    int ret;
    word32 idx = 0, curveSz, algoSz, pubSz, bitStringSz;
    byte bitString[1 + MAX_LENGTH_SZ + 1]; /* 6 */
    byte algo[MAX_ALGO_SZ];  /* 20 */

    /* public size */
    pubSz = key->dp ? (word32)key->dp->size : MAX_ECC_BYTES;
    if (comp)
        pubSz = 1 + pubSz;
    else
        pubSz = 1 + 2 * pubSz;

    /* check for buffer overflow */
    if (output != NULL && pubSz > (word32)outLen) {
        return BUFFER_E;
    }

    /* headers */
    if (with_header) {
        ret = SetCurve(key, NULL, 0);
        if (ret <= 0) {
            return ret;
        }
        curveSz = (word32)ret;
        ret = 0;

        /* calculate size */
        algoSz  = SetAlgoID(ECDSAk, algo, oidKeyType, (int)curveSz);
        bitStringSz = SetBitString(pubSz, 0, bitString);
        idx = SetSequence(pubSz + curveSz + bitStringSz + algoSz, NULL);

        /* check for buffer overflow */
        if (output != NULL &&
                curveSz + algoSz + bitStringSz + idx + pubSz > (word32)outLen) {
            return BUFFER_E;
        }

        idx = SetSequence(pubSz + curveSz + bitStringSz + algoSz,
            output);
        /* algo */
        if (output)
            XMEMCPY(output + idx, algo, algoSz);
        idx += algoSz;
        /* curve */
        if (output)
            (void)SetCurve(key, output + idx, curveSz);
        idx += curveSz;
        /* bit string */
        if (output)
            XMEMCPY(output + idx, bitString, bitStringSz);
        idx += bitStringSz;
    }

    /* pub */
    if (output) {
        PRIVATE_KEY_UNLOCK();
        ret = wc_ecc_export_x963_ex(key, output + idx, &pubSz, comp);
        PRIVATE_KEY_LOCK();
        if (ret != 0) {
            return ret;
        }
    }
    idx += pubSz;

    return (int)idx;
}

#endif
#ifdef WC_ENABLE_ASYM_KEY_EXPORT
int SetAsymKeyDerPublic(const byte* pubKey, word32 pubKeyLen,
    byte* output, word32 outLen, int keyType, int withHeader)
{
    int ret = 0;
    word32 idx = 0;
    word32 seqDataSz = 0;
    word32 sz;

    /* validate parameters */
    if (pubKey == NULL){
        return BAD_FUNC_ARG;
    }
    if (output != NULL && outLen == 0) {
        return BUFFER_E;
    }

    /* calculate size */
    if (withHeader) {
        word32 algoSz      = SetAlgoID(keyType, NULL, oidKeyType, 0);
        word32 bitStringSz = SetBitString(pubKeyLen, 0, NULL);

        seqDataSz = algoSz + bitStringSz + pubKeyLen;
        sz = SetSequence(seqDataSz, NULL) + seqDataSz;
    }
    else {
        sz = pubKeyLen;
    }

    /* checkout output size */
    if (output != NULL && sz > outLen) {
        ret = BUFFER_E;
    }

    /* headers */
    if (ret == 0 && output != NULL && withHeader) {
        /* sequence */
        idx = SetSequence(seqDataSz, output);
        /* algo */
        idx += SetAlgoID(keyType, output + idx, oidKeyType, 0);
        /* bit string */
        idx += SetBitString(pubKeyLen, 0, output + idx);
    }

    if (ret == 0 && output != NULL) {
        /* pub */
        XMEMCPY(output + idx, pubKey, pubKeyLen);
        idx += pubKeyLen;

        sz = idx;
    }

    if (ret == 0) {
        ret = (int)sz;
    }
    return ret;
}

#endif
#if !defined(NO_RSA) && !defined(NO_CERTS)
static int StoreRsaKey(DecodedCert* cert, const byte* source, word32* srcIdx,
                       word32 maxIdx)
{
    int    length;
    int    pubLen;
    word32 pubIdx;

    if (CheckBitString(source, srcIdx, &pubLen, maxIdx, 1, NULL) != 0)
        return ASN_PARSE_E;
    pubIdx = *srcIdx;

    if (GetSequence(source, srcIdx, &length, pubIdx + (word32)pubLen) < 0)
        return ASN_PARSE_E;

#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    cert->sigCtx.CertAtt.pubkey_n_start =
            cert->sigCtx.CertAtt.pubkey_e_start = pubIdx;
#endif
    cert->pubKeySize = (word32)pubLen;
    cert->publicKey = source + pubIdx;
#ifdef WOLFSSL_MAXQ10XX_TLS
    cert->publicKeyIndex = pubIdx;
#endif
    *srcIdx += (word32)length;

#ifdef HAVE_OCSP
    return CalcHashId_ex(cert->publicKey, cert->pubKeySize,
        cert->subjectKeyHash, HashIdAlg(cert->signatureOID));
#else
    return 0;
#endif
}

#endif
#if defined(HAVE_ECC) && !defined(NO_CERTS)
static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx,
                       word32 maxIdx, const byte* pubKey, word32 pubKeyLen)
{
    int ret;
    word32 localIdx;
    byte* publicKey;
    byte  tag;
    int length;

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

    localIdx = *srcIdx;
    if (GetASNTag(source, &localIdx, &tag, maxIdx) < 0)
        return ASN_PARSE_E;

    if (tag != (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
        if (GetObjectId(source, srcIdx, &cert->pkCurveOID, oidCurveType,
                                                                    maxIdx) < 0)
            return ASN_PARSE_E;

        if ((ret = CheckCurve(cert->pkCurveOID)) < 0)
            return ECC_CURVE_OID_E;

    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
        cert->sigCtx.CertAtt.curve_id = ret;
    #else
        (void)ret;
    #endif
        /* key header */
        ret = CheckBitString(source, srcIdx, &length, maxIdx, 1, NULL);
        if (ret != 0)
            return ret;
    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
        cert->sigCtx.CertAtt.pubkey_n_start =
                cert->sigCtx.CertAtt.pubkey_e_start = (*srcIdx + 1);
        cert->sigCtx.CertAtt.pubkey_n_len = ((length - 1) >> 1);
        cert->sigCtx.CertAtt.pubkey_e_start +=
                cert->sigCtx.CertAtt.pubkey_n_len;
        cert->sigCtx.CertAtt.pubkey_e_len   =
                cert->sigCtx.CertAtt.pubkey_n_len;
    #endif
    #ifdef WOLFSSL_MAXQ10XX_TLS
        cert->publicKeyIndex = *srcIdx + 1;
    #endif

    #ifdef HAVE_OCSP
        ret = CalcHashId_ex(source + *srcIdx, (word32)length,
            cert->subjectKeyHash, HashIdAlg(cert->signatureOID));
        if (ret != 0)
            return ret;
    #endif
        *srcIdx += (word32)length;
    }

    publicKey = (byte*)XMALLOC(pubKeyLen, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    if (publicKey == NULL)
        return MEMORY_E;
    XMEMCPY(publicKey, pubKey, pubKeyLen);
    cert->publicKey = publicKey;
    cert->pubKeyStored = 1;
    cert->pubKeySize   = pubKeyLen;

    return 0;
}

#endif
#ifndef NO_CERTS
#if !defined(NO_DSA)
static int ParseDsaKey(const byte* source, word32* srcIdx, word32 maxIdx,
                       void* heap)
{
    int ret;
    int length;

    (void)heap;

    ret = GetSequence(source, srcIdx, &length, maxIdx);
    if (ret < 0)
        return ret;

    ret = SkipInt(source, srcIdx, maxIdx);
    if (ret != 0)
        return ret;
    ret = SkipInt(source, srcIdx, maxIdx);
    if (ret != 0)
        return ret;
    ret = SkipInt(source, srcIdx, maxIdx);
    if (ret != 0)
        return ret;

    ret = CheckBitString(source, srcIdx, &length, maxIdx, 1, NULL);
    if (ret != 0)
        return ret;

    ret = GetASNInt(source, srcIdx, &length, maxIdx);
    if (ret != 0)
        return ASN_PARSE_E;

    *srcIdx += (word32)length;

    return 0;
}

#endif
#endif
static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType,
                       const byte* input, word32* inOutIdx, word32 maxIdx)
{
    int    length;  /* length of all distinguished names */
    int    dummy;
    int    ret;
    word32 idx;
    word32 srcIdx = *inOutIdx;
#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
    !defined(WOLFCRYPT_ONLY)
    WOLFSSL_X509_NAME* dName = NULL;
#endif

    WOLFSSL_MSG("Getting Cert Name");

    /* For OCSP, RFC2560 section 4.1.1 states the issuer hash should be
     * calculated over the entire DER encoding of the Name field, including
     * the tag and length. */
    if (CalcHashId_ex(input + *inOutIdx, maxIdx - *inOutIdx, hash,
            HashIdAlg(cert->signatureOID)) != 0) {
        return ASN_PARSE_E;
    }

#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
    !defined(WOLFCRYPT_ONLY)
    dName = wolfSSL_X509_NAME_new_ex(cert->heap);
    if (dName == NULL) {
        return MEMORY_E;
    }
#endif /* OPENSSL_EXTRA */

    if (GetSequence(input, &srcIdx, &length, maxIdx) < 0) {
#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
        wolfSSL_X509_NAME_free(dName);
#endif /* OPENSSL_EXTRA */
        return ASN_PARSE_E;
    }

#if defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT)
    /* store pointer to raw issuer */
    if (nameType == ASN_ISSUER) {
        cert->issuerRaw = &input[srcIdx];
        cert->issuerRawLen = length;
    }
#endif
#if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
    if (nameType == ASN_SUBJECT) {
        cert->subjectRaw = &input[srcIdx];
        cert->subjectRawLen = length;
    }
#endif

    length += (int)srcIdx;
    idx = 0;

    while (srcIdx < (word32)length) {
        byte        b       = 0;
        byte        joint[3];
        byte        tooBig  = FALSE;
        int         oidSz;
        const char* copy    = NULL;
        int         copyLen = 0;
        int         strLen  = 0;
        byte        id      = 0;
    #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) \
                && !defined(WOLFCRYPT_ONLY)
         int        nid = WC_NID_undef;
         int        enc;
    #endif /* OPENSSL_EXTRA */

        if (GetSet(input, &srcIdx, &dummy, maxIdx) < 0) {
            WOLFSSL_MSG("Cert name lacks set header, trying sequence");
        }

        if (GetSequence(input, &srcIdx, &dummy, maxIdx) <= 0) {
        #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
            wolfSSL_X509_NAME_free(dName);
        #endif /* OPENSSL_EXTRA */
            return ASN_PARSE_E;
        }

        ret = GetASNObjectId(input, &srcIdx, &oidSz, maxIdx);
        if (ret != 0) {
        #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
            wolfSSL_X509_NAME_free(dName);
        #endif /* OPENSSL_EXTRA */
            return ret;
        }

        /* make sure there is room for joint */
        if ((srcIdx + sizeof(joint)) > (word32)maxIdx) {
        #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
            wolfSSL_X509_NAME_free(dName);
        #endif /* OPENSSL_EXTRA */
            return ASN_PARSE_E;
        }

        XMEMCPY(joint, &input[srcIdx], sizeof(joint));

        /* v1 name types */
        if (joint[0] == 0x55 && joint[1] == 0x04) {
            srcIdx += 3;
            id = joint[2];
            if (GetHeader(input, &b, &srcIdx, &strLen, maxIdx, 1) < 0) {
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
                wolfSSL_X509_NAME_free(dName);
            #endif /* OPENSSL_EXTRA */
                return ASN_PARSE_E;
            }

        #ifndef WOLFSSL_NO_ASN_STRICT
            /* RFC 5280 section 4.1.2.4 lists a DirectoryString as being
             * 1..MAX in length */
            if (strLen < 1) {
                WOLFSSL_MSG("Non conforming DirectoryString of length 0 was"
                            " found");
                WOLFSSL_MSG("Use WOLFSSL_NO_ASN_STRICT if wanting to allow"
                            " empty DirectoryString's");
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
                wolfSSL_X509_NAME_free(dName);
            #endif /* OPENSSL_EXTRA */
                return ASN_PARSE_E;
            }
        #endif

            if (id == ASN_COMMON_NAME) {
                if (nameType == ASN_SUBJECT) {
                    cert->subjectCN = (char *)&input[srcIdx];
                    cert->subjectCNLen = strLen;
                    cert->subjectCNEnc = (char)b;
                }
            #if (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)) && \
                defined(WOLFSSL_HAVE_ISSUER_NAMES)
                else if (nameType == ASN_ISSUER) {
                    cert->issuerCN = (char*)&input[srcIdx];
                    cert->issuerCNLen = strLen;
                    cert->issuerCNEnc = (char)b;
                }
            #endif

                copy = WOLFSSL_COMMON_NAME;
                copyLen = sizeof(WOLFSSL_COMMON_NAME) - 1;
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) \
                && !defined(WOLFCRYPT_ONLY)
                nid = WC_NID_commonName;
            #endif /* OPENSSL_EXTRA */
            }
        #ifdef WOLFSSL_CERT_NAME_ALL
            else if (id == ASN_NAME) {
                copy = WOLFSSL_NAME;
                copyLen = sizeof(WOLFSSL_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectN = (char*)&input[srcIdx];
                        cert->subjectNLen = strLen;
                        cert->subjectNEnc = b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_name;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_INITIALS) {
                copy = WOLFSSL_INITIALS;
                copyLen = sizeof(WOLFSSL_INITIALS) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectI = (char*)&input[srcIdx];
                        cert->subjectILen = strLen;
                        cert->subjectIEnc = b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_initials;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_GIVEN_NAME) {
                copy = WOLFSSL_GIVEN_NAME;
                copyLen = sizeof(WOLFSSL_GIVEN_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectGN = (char*)&input[srcIdx];
                        cert->subjectGNLen = strLen;
                        cert->subjectGNEnc = b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_givenName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_DNQUALIFIER) {
                copy = WOLFSSL_DNQUALIFIER;
                copyLen = sizeof(WOLFSSL_DNQUALIFIER) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectDNQ = (char*)&input[srcIdx];
                        cert->subjectDNQLen = strLen;
                        cert->subjectDNQEnc = b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_dnQualifier;
                #endif /* OPENSSL_EXTRA */
            }
        #endif /* WOLFSSL_CERT_NAME_ALL */
            else if (id == ASN_SUR_NAME) {
                copy = WOLFSSL_SUR_NAME;
                copyLen = sizeof(WOLFSSL_SUR_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectSN = (char*)&input[srcIdx];
                        cert->subjectSNLen = strLen;
                        cert->subjectSNEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerSN = (char*)&input[srcIdx];
                        cert->issuerSNLen = strLen;
                        cert->issuerSNEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_surname;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_COUNTRY_NAME) {
                copy = WOLFSSL_COUNTRY_NAME;
                copyLen = sizeof(WOLFSSL_COUNTRY_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectC = (char*)&input[srcIdx];
                        cert->subjectCLen = strLen;
                        cert->subjectCEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerC = (char*)&input[srcIdx];
                        cert->issuerCLen = strLen;
                        cert->issuerCEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_countryName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_LOCALITY_NAME) {
                copy = WOLFSSL_LOCALITY_NAME;
                copyLen = sizeof(WOLFSSL_LOCALITY_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectL = (char*)&input[srcIdx];
                        cert->subjectLLen = strLen;
                        cert->subjectLEnc = (char)b;
                    }
                    #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerL = (char*)&input[srcIdx];
                        cert->issuerLLen = strLen;
                        cert->issuerLEnc = (char)b;
                    }
                    #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_localityName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_STATE_NAME) {
                copy = WOLFSSL_STATE_NAME;
                copyLen = sizeof(WOLFSSL_STATE_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectST = (char*)&input[srcIdx];
                        cert->subjectSTLen = strLen;
                        cert->subjectSTEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerST = (char*)&input[srcIdx];
                        cert->issuerSTLen = strLen;
                        cert->issuerSTEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT*/
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_stateOrProvinceName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_ORG_NAME) {
                copy = WOLFSSL_ORG_NAME;
                copyLen = sizeof(WOLFSSL_ORG_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectO = (char*)&input[srcIdx];
                        cert->subjectOLen = strLen;
                        cert->subjectOEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerO = (char*)&input[srcIdx];
                        cert->issuerOLen = strLen;
                        cert->issuerOEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_organizationName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_ORGUNIT_NAME) {
                copy = WOLFSSL_ORGUNIT_NAME;
                copyLen = sizeof(WOLFSSL_ORGUNIT_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectOU = (char*)&input[srcIdx];
                        cert->subjectOULen = strLen;
                        cert->subjectOUEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerOU = (char*)&input[srcIdx];
                        cert->issuerOULen = strLen;
                        cert->issuerOUEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_organizationalUnitName;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_SERIAL_NUMBER) {
                copy = WOLFSSL_SERIAL_NUMBER;
                copyLen = sizeof(WOLFSSL_SERIAL_NUMBER) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectSND = (char*)&input[srcIdx];
                        cert->subjectSNDLen = strLen;
                        cert->subjectSNDEnc = (char)b;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES)
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerSND = (char*)&input[srcIdx];
                        cert->issuerSNDLen = strLen;
                        cert->issuerSNDEnc = (char)b;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_serialNumber;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_USER_ID) {
                copy = WOLFSSL_USER_ID;
                copyLen = sizeof(WOLFSSL_USER_ID) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectUID = (char*)&input[srcIdx];
                        cert->subjectUIDLen = strLen;
                        cert->subjectUIDEnc = (char)b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_userId;
                #endif /* OPENSSL_EXTRA */
            }
        #ifdef WOLFSSL_CERT_EXT
            else if (id == ASN_STREET_ADDR) {
                copy = WOLFSSL_STREET_ADDR_NAME;
                copyLen = sizeof(WOLFSSL_STREET_ADDR_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectStreet = (char*)&input[srcIdx];
                        cert->subjectStreetLen = strLen;
                        cert->subjectStreetEnc = (char)b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_streetAddress;
                #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_BUS_CAT) {
                copy = WOLFSSL_BUS_CAT;
                copyLen = sizeof(WOLFSSL_BUS_CAT) - 1;
            #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                if (nameType == ASN_SUBJECT) {
                    cert->subjectBC = (char*)&input[srcIdx];
                    cert->subjectBCLen = strLen;
                    cert->subjectBCEnc = (char)b;
                }
            #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                nid = WC_NID_businessCategory;
            #endif /* OPENSSL_EXTRA */
            }
            else if (id == ASN_POSTAL_CODE) {
                copy = WOLFSSL_POSTAL_NAME;
                copyLen = sizeof(WOLFSSL_POSTAL_NAME) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectPC = (char*)&input[srcIdx];
                        cert->subjectPCLen = strLen;
                        cert->subjectPCEnc = (char)b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT*/
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_postalCode;
                #endif /* OPENSSL_EXTRA */
            }
        #endif /* WOLFSSL_CERT_EXT */
        }
    #ifdef WOLFSSL_CERT_EXT
        else if ((srcIdx + ASN_JOI_PREFIX_SZ + 2 <= (word32)maxIdx) &&
                 (0 == XMEMCMP(&input[srcIdx], ASN_JOI_PREFIX,
                               ASN_JOI_PREFIX_SZ)) &&
                 ((input[srcIdx+ASN_JOI_PREFIX_SZ] == ASN_JOI_C) ||
                  (input[srcIdx+ASN_JOI_PREFIX_SZ] == ASN_JOI_ST)))
        {
            srcIdx += ASN_JOI_PREFIX_SZ;
            id = input[srcIdx++];
            b = input[srcIdx++]; /* encoding */

            if (GetLength(input, &srcIdx, &strLen,
                          maxIdx) < 0) {
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
                wolfSSL_X509_NAME_free(dName);
            #endif /* OPENSSL_EXTRA */
                return ASN_PARSE_E;
            }

            /* Check for jurisdiction of incorporation country name */
            if (id == ASN_JOI_C) {
                copy = WOLFSSL_JOI_C;
                copyLen = sizeof(WOLFSSL_JOI_C) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectJC = (char*)&input[srcIdx];
                        cert->subjectJCLen = strLen;
                        cert->subjectJCEnc = (char)b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_jurisdictionCountryName;
                #endif /* OPENSSL_EXTRA */
            }

            /* Check for jurisdiction of incorporation state name */
            else if (id == ASN_JOI_ST) {
                copy = WOLFSSL_JOI_ST;
                copyLen = sizeof(WOLFSSL_JOI_ST) - 1;
                #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectJS = (char*)&input[srcIdx];
                        cert->subjectJSLen = strLen;
                        cert->subjectJSEnc = (char)b;
                    }
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_jurisdictionStateOrProvinceName;
                #endif /* OPENSSL_EXTRA */
            }

            if ((strLen + copyLen) > (int)(WC_ASN_NAME_MAX - idx)) {
                WOLFSSL_MSG("ASN Name too big, skipping");
                tooBig = TRUE;
            }
        }
    #endif /* WOLFSSL_CERT_EXT */
        else {
            /* skip */
            byte email = FALSE;
            byte pilot = FALSE;

            if (joint[0] == 0x2a && joint[1] == 0x86) {  /* email id hdr 42.134.* */
                id = ASN_EMAIL_NAME;
                email = TRUE;
            }

            if (joint[0] == 0x9  && joint[1] == 0x92) { /* uid id hdr 9.146.* */
                /* last value of OID is the type of pilot attribute */
                id    = input[srcIdx + (word32)oidSz - 1];
                if (id == 0x01)
                    id = ASN_USER_ID;
                pilot = TRUE;
            }

            srcIdx += (word32)oidSz + 1;

            if (GetLength(input, &srcIdx, &strLen, maxIdx) < 0) {
            #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
                wolfSSL_X509_NAME_free(dName);
            #endif /* OPENSSL_EXTRA */
                return ASN_PARSE_E;
            }

            if (strLen > (int)(WC_ASN_NAME_MAX - idx)) {
                WOLFSSL_MSG("ASN name too big, skipping");
                tooBig = TRUE;
            }

            if (email) {
                copyLen = sizeof(WOLFSSL_EMAIL_ADDR) - 1;
                if ((copyLen + strLen) > (int)(WC_ASN_NAME_MAX - idx)) {
                    WOLFSSL_MSG("ASN name too big, skipping");
                    tooBig = TRUE;
                }
                else {
                    copy = WOLFSSL_EMAIL_ADDR;
                }

                #if !defined(IGNORE_NAME_CONSTRAINTS) || \
                     defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
                    if (nameType == ASN_SUBJECT) {
                        cert->subjectEmail = (char*)&input[srcIdx];
                        cert->subjectEmailLen = strLen;
                    }
                #if defined(WOLFSSL_HAVE_ISSUER_NAMES) && \
                    (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT))
                    else if (nameType == ASN_ISSUER) {
                        cert->issuerEmail = (char*)&input[srcIdx];
                        cert->issuerEmailLen = strLen;
                    }
                #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
                #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */
                #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                    nid = WC_NID_emailAddress;
                #endif /* OPENSSL_EXTRA */
            }

            if (pilot) {
                switch (id) {
                    case ASN_USER_ID:
                        copy = WOLFSSL_USER_ID;
                        copyLen = sizeof(WOLFSSL_USER_ID) - 1;
                    #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                        nid = WC_NID_userId;
                    #endif /* OPENSSL_EXTRA */
                        break;
                    case ASN_DOMAIN_COMPONENT:
                        copy = WOLFSSL_DOMAIN_COMPONENT;
                        copyLen = sizeof(WOLFSSL_DOMAIN_COMPONENT) - 1;
                    #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                        nid = WC_NID_domainComponent;
                    #endif /* OPENSSL_EXTRA */
                        break;
                    case ASN_RFC822_MAILBOX:
                        copy = WOLFSSL_RFC822_MAILBOX;
                        copyLen = sizeof(WOLFSSL_RFC822_MAILBOX) - 1;
                    #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                        nid = WC_NID_rfc822Mailbox;
                    #endif /* OPENSSL_EXTRA */
                        break;
                    case ASN_FAVOURITE_DRINK:
                        copy = WOLFSSL_FAVOURITE_DRINK;
                        copyLen = sizeof(WOLFSSL_FAVOURITE_DRINK) - 1;
                    #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                        nid = WC_NID_favouriteDrink;
                    #endif /* OPENSSL_EXTRA */
                        break;
                    case ASN_CONTENT_TYPE:
                        copy = WOLFSSL_CONTENT_TYPE;
                        copyLen = sizeof(WOLFSSL_CONTENT_TYPE) - 1;
                    #if (defined(OPENSSL_EXTRA) || \
                        defined(OPENSSL_EXTRA_X509_SMALL)) \
                        && !defined(WOLFCRYPT_ONLY)
                        nid = WC_NID_pkcs9_contentType;
                    #endif /* OPENSSL_EXTRA */
                        break;
                    default:
                        WOLFSSL_MSG("Unknown pilot attribute type");
                    #if (defined(OPENSSL_EXTRA) || \
                                defined(OPENSSL_EXTRA_X509_SMALL)) && \
                                !defined(WOLFCRYPT_ONLY)
                        wolfSSL_X509_NAME_free(dName);
                    #endif /* OPENSSL_EXTRA */
                        return ASN_PARSE_E;
                }
            }
        }
        if ((copyLen + strLen) > (int)(WC_ASN_NAME_MAX - idx))
        {
            WOLFSSL_MSG("ASN Name too big, skipping");
            tooBig = TRUE;
        }
        if ((copy != NULL) && !tooBig) {
            XMEMCPY(&full[idx], copy, (size_t)copyLen);
            idx += (word32)copyLen;
            XMEMCPY(&full[idx], &input[srcIdx], (size_t)strLen);
            idx += (word32)strLen;
        }
        #if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
        switch (b) {
            case CTC_UTF8:
                enc = WOLFSSL_MBSTRING_UTF8;
                break;
            case CTC_PRINTABLE:
                enc = WOLFSSL_V_ASN1_PRINTABLESTRING;
                break;
            default:
                WOLFSSL_MSG("Unknown encoding type, using UTF8 by default");
                enc = WOLFSSL_MBSTRING_UTF8;
        }

        if (nid != WC_NID_undef) {
            if (wolfSSL_X509_NAME_add_entry_by_NID(dName, nid, enc,
                            &input[srcIdx], strLen, -1, -1) !=
                            WOLFSSL_SUCCESS) {
                wolfSSL_X509_NAME_free(dName);
                return ASN_PARSE_E;
            }
        }
        #endif /* OPENSSL_EXTRA */
        srcIdx += (word32)strLen;
    }
    full[idx++] = 0;

#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
            !defined(WOLFCRYPT_ONLY)
    if (nameType == ASN_ISSUER) {
#if (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)) &&\
    (defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT))
        dName->rawLen = min(cert->issuerRawLen, WC_ASN_NAME_MAX);
        XMEMCPY(dName->raw, cert->issuerRaw, dName->rawLen);
#endif
        cert->issuerName = dName;
    }
    else {
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX)
        dName->rawLen = min(cert->subjectRawLen, WC_ASN_NAME_MAX);
        XMEMCPY(dName->raw, cert->subjectRaw, dName->rawLen);
#endif
        cert->subjectName = dName;
    }
#endif

    *inOutIdx = srcIdx;

    return 0;
}

int GetName(DecodedCert* cert, int nameType, int maxIdx)
{
    char*  full;
    byte*  hash;
    int    length;
    word32 localIdx;
    byte   tag;

    WOLFSSL_MSG("Getting Name");

    if (nameType == ASN_ISSUER) {
        full = cert->issuer;
        hash = cert->issuerHash;
    }
    else {
        full = cert->subject;
        hash = cert->subjectHash;
    }

    if (cert->srcIdx >= (word32)maxIdx) {
        return BUFFER_E;
    }

    localIdx = cert->srcIdx;
    if (GetASNTag(cert->source, &localIdx, &tag, (word32)maxIdx) < 0) {
        return ASN_PARSE_E;
    }

    if (tag == ASN_OBJECT_ID) {
        WOLFSSL_MSG("Trying optional prefix...");

        if (SkipObjectId(cert->source, &cert->srcIdx, (word32)maxIdx) < 0)
            return ASN_PARSE_E;
        WOLFSSL_MSG("Got optional prefix");
    }

    localIdx = cert->srcIdx;
    if (GetASNTag(cert->source, &localIdx, &tag, (word32)maxIdx) < 0) {
        return ASN_PARSE_E;
    }
    localIdx = cert->srcIdx + 1;
    if (GetLength(cert->source, &localIdx, &length, (word32)maxIdx) < 0) {
        return ASN_PARSE_E;
    }
    length += (int)(localIdx - cert->srcIdx);

    return GetCertName(cert, full, hash, nameType, cert->source, &cert->srcIdx,
                       cert->srcIdx + (word32)length);
}

static int GetDateInfo(const byte* source, word32* idx, const byte** pDate,
                        byte* pFormat, int* pLength, word32 maxIdx)
{
    int length;
    byte format;

    if (source == NULL || idx == NULL)
        return BAD_FUNC_ARG;

    /* get ASN format header */
    if (*idx+1 > maxIdx)
        return BUFFER_E;
    format = source[*idx];
    *idx += 1;
    if (format != ASN_UTC_TIME && format != ASN_GENERALIZED_TIME) {
        WOLFSSL_ERROR_VERBOSE(ASN_TIME_E);
        return ASN_TIME_E;
    }

    /* get length */
    if (GetLength(source, idx, &length, maxIdx) < 0)
        return ASN_PARSE_E;
    if (length > MAX_DATE_SIZE || length < MIN_DATE_SIZE)
        return ASN_DATE_SZ_E;

    /* return format, date and length */
    if (pFormat)
        *pFormat = format;
    if (pDate)
        *pDate = &source[*idx];
    if (pLength)
        *pLength = length;

    *idx += (word32)length;

    return 0;
}

#ifndef NO_CERTS
static int GetDate(DecodedCert* cert, int dateType, int verify, int maxIdx)
{
    int    ret, length;
    const byte *datePtr = NULL;
    byte   date[MAX_DATE_SIZE];
    byte   format;
    word32 startIdx = 0;

    if (dateType == ASN_BEFORE)
        cert->beforeDate = &cert->source[cert->srcIdx];
    else
        cert->afterDate = &cert->source[cert->srcIdx];
    startIdx = cert->srcIdx;

    ret = GetDateInfo(cert->source, &cert->srcIdx, &datePtr, &format,
                      &length, (word32)maxIdx);
    if (ret < 0)
        return ret;

    XMEMSET(date, 0, MAX_DATE_SIZE);
    XMEMCPY(date, datePtr, (size_t)length);

    if (dateType == ASN_BEFORE)
        cert->beforeDateLen = (int)(cert->srcIdx - startIdx);
    else
        cert->afterDateLen  = (int)(cert->srcIdx - startIdx);

#ifndef NO_ASN_TIME_CHECK
    if (verify != NO_VERIFY && verify != VERIFY_SKIP_DATE &&
            (! AsnSkipDateCheck) &&
            !XVALIDATE_DATE(date, format, dateType, length)) {
        if (dateType == ASN_BEFORE) {
            WOLFSSL_ERROR_VERBOSE(ASN_BEFORE_DATE_E);
            return ASN_BEFORE_DATE_E;
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ASN_AFTER_DATE_E);
            return ASN_AFTER_DATE_E;
        }
    }
#else
    (void)verify;
#endif

    return 0;
}

static int GetValidity(DecodedCert* cert, int verify, int maxIdx)
{
    int length;
    int badDate = 0;

    if (GetSequence(cert->source, &cert->srcIdx, &length, (word32)maxIdx) < 0)
        return ASN_PARSE_E;

    maxIdx = (int)cert->srcIdx + length;

    if (GetDate(cert, ASN_BEFORE, verify, maxIdx) < 0)
        badDate = ASN_BEFORE_DATE_E; /* continue parsing */

    if (GetDate(cert, ASN_AFTER, verify, maxIdx) < 0)
        return ASN_AFTER_DATE_E;

    if (badDate != 0)
        return badDate;

    return 0;
}
#endif

#ifndef NO_CERTS
static int GetSigAlg(DecodedCert* cert, word32* sigOid, word32 maxIdx)
{
    int length;
    word32 endSeqIdx;

    if (GetSequence(cert->source, &cert->srcIdx, &length, maxIdx) < 0)
        return ASN_PARSE_E;
    endSeqIdx = cert->srcIdx + (word32)length;

    if (GetObjectId(cert->source, &cert->srcIdx, sigOid, oidSigType,
                    maxIdx) < 0) {
        return ASN_OBJECT_ID_E;
    }

    if (cert->srcIdx != endSeqIdx) {
#ifdef WC_RSA_PSS
        if (*sigOid == CTC_RSASSAPSS) {
            /* cert->srcIdx is at start of parameters TLV (NULL or SEQUENCE) */
            word32 tmpIdx = cert->srcIdx;
            byte tag;
            int len;

            WOLFSSL_MSG("Cert sigAlg is RSASSA-PSS; decoding params");
            if (GetHeader(cert->source, &tag, &tmpIdx, &len, endSeqIdx, 1) < 0) {
                return ASN_PARSE_E;
            }
            cert->sigParamsIndex  = cert->srcIdx;
            cert->sigParamsLength = (word32)((tmpIdx - cert->srcIdx) + len);
        }
        else
#endif
        /* Only allowed a ASN NULL header with zero length. */
        if  (endSeqIdx - cert->srcIdx != 2)
            return ASN_PARSE_E;
        else {
            byte tag;
            if (GetASNTag(cert->source, &cert->srcIdx, &tag, endSeqIdx) != 0)
                return ASN_PARSE_E;
            if (tag != ASN_TAG_NULL)
                return ASN_PARSE_E;
        }
    }

    cert->srcIdx = endSeqIdx;

    return 0;
}
#endif

#ifndef NO_CERTS
int wc_GetPubX509(DecodedCert* cert, int verify, int* badDate)
{
    int ret;

    if (cert == NULL || badDate == NULL)
        return BAD_FUNC_ARG;

    *badDate = 0;
    if ( (ret = GetCertHeader(cert)) < 0)
        return ret;

    WOLFSSL_MSG("Got Cert Header");

#ifdef WOLFSSL_CERT_REQ
    if (!cert->isCSR) {
#endif
        /* Using the sigIndex as the upper bound because that's where the
         * actual certificate data ends. */
        if ((ret = GetSigAlg(cert, &cert->signatureOID, cert->sigIndex)) < 0)
            return ret;

        WOLFSSL_MSG("Got Algo ID");

        if ( (ret = GetName(cert, ASN_ISSUER, (int)cert->sigIndex)) < 0)
            return ret;

        if ( (ret = GetValidity(cert, verify, (int)cert->sigIndex)) < 0)
            *badDate = ret;
#ifdef WOLFSSL_CERT_REQ
    }
#endif

    if ( (ret = GetName(cert, ASN_SUBJECT, (int)cert->sigIndex)) < 0)
        return ret;

    WOLFSSL_MSG("Got Subject Name");
    return ret;
}

int DecodeToKey(DecodedCert* cert, int verify)
{
    int badDate = 0;
    int ret;

#if defined(HAVE_RPK)

    /* Raw Public Key certificate has only a SubjectPublicKeyInfo structure
     * as its contents. So try to call GetCertKey to get public key from it.
     * If it fails, the cert should be a X509 cert and proceed to process as
     * x509 cert. */
    ret = GetCertKey(cert, cert->source, &cert->srcIdx, cert->maxIdx);
    if (ret == 0) {
        WOLFSSL_MSG("Raw Public Key certificate found and parsed");
        cert->isRPK = 1;
        return ret;
    }
#endif /* HAVE_RPK */

    if ( (ret = wc_GetPubX509(cert, verify, &badDate)) < 0)
        return ret;

    /* Determine if self signed */
#ifdef WOLFSSL_CERT_REQ
    if (cert->isCSR)
        cert->selfSigned = 1;
    else
#endif
    {
        cert->selfSigned = XMEMCMP(cert->issuerHash, cert->subjectHash,
            KEYID_SIZE) == 0 ? 1 : 0;
    }

    ret = GetCertKey(cert, cert->source, &cert->srcIdx, cert->maxIdx);
    if (ret != 0)
        return ret;

    WOLFSSL_MSG("Got Key");

    if (badDate != 0)
        return badDate;

    return ret;
}

static int GetSignature(DecodedCert* cert)
{
    int length;
    int ret;

    ret = CheckBitString(cert->source, &cert->srcIdx, &length, cert->maxIdx, 1,
                         NULL);
    if (ret != 0)
        return ret;

    cert->sigLength = (word32)length;
    cert->signature = &cert->source[cert->srcIdx];
    cert->srcIdx += cert->sigLength;

    if (cert->srcIdx != cert->maxIdx)
        return ASN_PARSE_E;

    return 0;
}

#endif
/* Set an octet header when length is only 7-bit.
 *
 * @param [in] len     Length of data in OCTET_STRING. Value must be <= 127.
 * @param [in] output  Buffer to encode ASN.1 header.
 * @return  Length of ASN.1 header.
 */
static word32 SetOctetString8Bit(word32 len, byte* output)
{
    output[0] = ASN_OCTET_STRING;
    output[1] = (byte)len;
    return 2;
}
static word32 SetDigest(const byte* digest, word32 digSz, byte* output)
{
    word32 idx = SetOctetString8Bit(digSz, output);
    XMEMCPY(&output[idx], digest, digSz);

    return idx + digSz;
}

static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
                            byte absentParams)
{
    word32 tagSz, idSz, seqSz, algoSz = 0;
    const  byte* algoName = 0;
    byte   ID_Length[1 + MAX_LENGTH_SZ];
    byte   seqArray[MAX_SEQ_SZ + 1];  /* add object_id to end */
    word32    length = 0;

    tagSz = ((type == oidHashType ||
             (type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
             (type == oidKeyType && algoOID == RSAk)) &&
                (absentParams == FALSE)) ? 2U : 0U;
    algoName = OidFromId((word32)algoOID, (word32)type, &algoSz);
    if (algoName == NULL) {
        WOLFSSL_MSG("Unknown Algorithm");
        return 0;
    }

    idSz  = (word32)SetObjectId((int)algoSz, ID_Length);
    seqSz = SetSequence(idSz + algoSz + tagSz + (word32)curveSz, seqArray);

    /* Copy only algo to output for DSA keys */
    if (algoOID == DSAk && output) {
        XMEMCPY(output, ID_Length, idSz);
        XMEMCPY(output + idSz, algoName, algoSz);
        if (tagSz == 2)
            SetASNNull(&output[seqSz + idSz + algoSz]);
    }
    else if (output) {
        XMEMCPY(output, seqArray, seqSz);
        XMEMCPY(output + seqSz, ID_Length, idSz);
        XMEMCPY(output + seqSz + idSz, algoName, algoSz);
        if (tagSz == 2)
            SetASNNull(&output[seqSz + idSz + algoSz]);
    }

    if (algoOID == DSAk)
        length = idSz + algoSz + tagSz;
    else
        length = seqSz + idSz + algoSz + tagSz;

    return length;
}

word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz,
                          int hashOID)
{
    byte digArray[MAX_ENCODED_DIG_SZ];
    byte algoArray[MAX_ALGO_SZ];
    byte seqArray[MAX_SEQ_SZ];
    word32 encDigSz, algoSz, seqSz;

    encDigSz = SetDigest(digest, digSz, digArray);
    algoSz   = SetAlgoID(hashOID, algoArray, oidHashType, 0);
    seqSz    = SetSequence(encDigSz + algoSz, seqArray);

    XMEMCPY(out, seqArray, seqSz);
    XMEMCPY(out + seqSz, algoArray, algoSz);
    XMEMCPY(out + seqSz + algoSz, digArray, encDigSz);

    return encDigSz + algoSz + seqSz;
}

#ifndef NO_CERTS
static void AddAltName(DecodedCert* cert, DNS_entry* dnsEntry)
{
#if (defined(WOLFSSL_ASN_ALL) || defined(OPENSSL_EXTRA)) && \
    !defined(WOLFSSL_ALT_NAMES_NO_REV)
    /* logic to add alt name to end of list */
    dnsEntry->next = NULL;
    if (cert->altNames == NULL) {
        /* First on list */
        cert->altNames = dnsEntry;
    }
    else {
        DNS_entry* temp = cert->altNames;

        /* Find end */
        for (; (temp->next != NULL); temp = temp->next);

        /* Add to end */
        temp->next = dnsEntry;
    }
#else
    dnsEntry->next = cert->altNames;
    cert->altNames = dnsEntry;
#endif
}

#if defined(WOLFSSL_SEP)
/* return 0 on success */
static int DecodeSepHwAltName(DecodedCert* cert, const byte* input,
    word32* idxIn, word32 sz)
{
    word32 idx = *idxIn;
    int  strLen;
    int  ret;
    byte tag;

    /* Certificates issued with this OID in the subject alt name are for
     * verifying signatures created on a module.
     * RFC 4108 Section 5. */
    if (cert->hwType != NULL) {
        WOLFSSL_MSG("\tAlready seen Hardware Module Name");
        return ASN_PARSE_E;
    }

    if (GetASNTag(input, &idx, &tag, sz) < 0) {
        return ASN_PARSE_E;
    }

    if (tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) {
        WOLFSSL_MSG("\twrong type");
        return ASN_PARSE_E;
    }

    if (GetLength(input, &idx, &strLen, sz) < 0) {
        WOLFSSL_MSG("\tfail: str len");
        return ASN_PARSE_E;
    }

    if (GetSequence(input, &idx, &strLen, sz) < 0) {
        WOLFSSL_MSG("\tBad Sequence");
        return ASN_PARSE_E;
    }

    ret = GetASNObjectId(input, &idx, &strLen, sz);
    if (ret != 0) {
        WOLFSSL_MSG("\tbad OID");
        return ret;
    }

    cert->hwType = (byte*)XMALLOC((size_t)strLen, cert->heap,
                                  DYNAMIC_TYPE_X509_EXT);
    if (cert->hwType == NULL) {
        WOLFSSL_MSG("\tOut of Memory");
        return MEMORY_E;
    }

    XMEMCPY(cert->hwType, &input[idx], (size_t)strLen);
    cert->hwTypeSz = strLen;
    idx += (word32)strLen;

    ret = GetOctetString(input, &idx, &strLen, sz);
    if (ret < 0) {
        XFREE(cert->hwType, cert->heap, DYNAMIC_TYPE_X509_EXT);
        cert->hwType = NULL;
        return ret;
    }

    cert->hwSerialNum = (byte*)XMALLOC((size_t)strLen + 1, cert->heap,
                                       DYNAMIC_TYPE_X509_EXT);
    if (cert->hwSerialNum == NULL) {
        WOLFSSL_MSG("\tOut of Memory");
        XFREE(cert->hwType, cert->heap, DYNAMIC_TYPE_X509_EXT);
        cert->hwType = NULL;
        return MEMORY_E;
    }

    XMEMCPY(cert->hwSerialNum, &input[idx], (size_t)strLen);
    cert->hwSerialNum[strLen] = '\0';
    cert->hwSerialNumSz = strLen;
    idx += (word32)strLen;

    *idxIn = idx;
    return 0;
}
#endif

/* return 0 on success */
static int DecodeConstructedOtherName(DecodedCert* cert, const byte* input,
        word32* idx, word32 sz, int oid)
{
    int ret    = 0;
    int strLen = 0;
    byte tag;
    DNS_entry* dnsEntry = NULL;

    if (GetASNTag(input, idx, &tag, sz) < 0) {
        ret = ASN_PARSE_E;
    }

    if (ret == 0 && (tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED))) {
        ret = ASN_PARSE_E;
    }

    if (ret == 0 && (GetLength(input, idx, &strLen, sz) < 0)) {
        ret = ASN_PARSE_E;
    }

    if (ret == 0) {
        dnsEntry = AltNameNew(cert->heap);
        if (dnsEntry == NULL) {
            WOLFSSL_MSG("\tOut of Memory");
            return MEMORY_E;
        }

        switch (oid) {
        #ifdef WOLFSSL_FPKI
            case FASCN_OID:
                ret = GetOctetString(input, idx, &strLen, sz);
                if (ret > 0) {
                    ret = 0;
                }
                break;
        #endif /* WOLFSSL_FPKI */
            case UPN_OID:
                if (GetASNTag(input, idx, &tag, sz) < 0) {
                    ret = ASN_PARSE_E;
                }

                if (ret == 0 &&
                        tag != ASN_PRINTABLE_STRING && tag != ASN_UTF8STRING &&
                                    tag != ASN_IA5_STRING) {
                    WOLFSSL_MSG("Was expecting a string for UPN");
                    ret = ASN_PARSE_E;
                }

                if (ret == 0 && (GetLength(input, idx, &strLen, sz) < 0)) {
                    WOLFSSL_MSG("Was expecting a string for UPN");
                    ret = ASN_PARSE_E;
                }
                break;

            default:
                WOLFSSL_MSG("Unknown constructed other name, skipping");
                XFREE(dnsEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                dnsEntry = NULL;
        }
    }

    if (ret == 0 && dnsEntry != NULL) {
        dnsEntry->type = ASN_OTHER_TYPE;
        dnsEntry->len = strLen;
        dnsEntry->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
            DYNAMIC_TYPE_ALTNAME);
    #ifdef WOLFSSL_FPKI
        dnsEntry->oidSum = oid;
    #endif /* WOLFSSL_FPKI */
        if (dnsEntry->name == NULL) {
            WOLFSSL_MSG("\tOut of Memory");
            ret = MEMORY_E;
        }
        else {
            dnsEntry->nameStored = 1;
            XMEMCPY((void *)(wc_ptr_t)dnsEntry->name, &input[*idx],
                    (size_t)strLen);
            ((char *)(wc_ptr_t)dnsEntry->name)[strLen] = '\0';
            AddAltName(cert, dnsEntry);
        }
    }

    if (ret == 0) {
        *idx += (word32)strLen;
    }
    else {
        XFREE(dnsEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
    }

    return ret;
}

/* Reject IA5String SAN content that cannot legally appear in
 * dNSName / rfc822Name / URI per RFC 5280 4.2.1.6. Currently just NUL. */
static int DecodeGeneralNameCheckChars(const byte* input, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        if (input[i] == 0) {
            return ASN_PARSE_E;
        }
    }
    return 0;
}

static int DecodeAltNames(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    word32 numNames = 0;

    WOLFSSL_ENTER("DecodeAltNames");

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tBad Sequence");
        return ASN_PARSE_E;
    }

    if (length == 0) {
        /* RFC 5280 4.2.1.6.  Subject Alternative Name
           If the subjectAltName extension is present, the sequence MUST
           contain at least one entry. */
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        return ASN_PARSE_E;
    }

#ifdef OPENSSL_ALL
    cert->extSubjAltNameSrc = input;
    cert->extSubjAltNameSz = sz;
#endif

    cert->weOwnAltNames = 1;

    while (length > 0) {
        byte current_byte;

        /* Verify idx can't overflow input buffer */
        if (idx >= (word32)sz) {
            WOLFSSL_MSG("\tBad Index");
            return BUFFER_E;
        }

        numNames++;
        if (numNames > WOLFSSL_MAX_ALT_NAMES) {
            WOLFSSL_MSG("\tToo many subject alternative names");
            return ASN_ALT_NAME_E;
        }

        current_byte = input[idx++];
        length--;

        /* Save DNS Type names in the altNames list. */
        /* Save Other Type names in the cert's OidMap */
        if (current_byte == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) {
            DNS_entry* dnsEntry;
            int strLen;
            word32 lenStartIdx = idx;

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }
            length -= (int)(idx - lenStartIdx);

            if ((word32)strLen + idx > sz) {
                return BUFFER_E;
            }
            if (DecodeGeneralNameCheckChars(&input[idx], strLen) != 0) {
                return ASN_PARSE_E;
            }

            dnsEntry = AltNameNew(cert->heap);
            if (dnsEntry == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }

            dnsEntry->type = ASN_DNS_TYPE;
            dnsEntry->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (dnsEntry->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(dnsEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            dnsEntry->nameStored = 1;
            dnsEntry->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)dnsEntry->name, &input[idx],
                    (size_t)strLen);
            ((char *)(wc_ptr_t)dnsEntry->name)[strLen] = '\0';

            AddAltName(cert, dnsEntry);

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
    #ifndef IGNORE_NAME_CONSTRAINTS
        else if (current_byte ==
                (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE)) {
            DNS_entry* dirEntry;
            int strLen;
            word32 lenStartIdx = idx;

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }

            if (GetSequence(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: seq length");
                return ASN_PARSE_E;
            }
            length -= (int)(idx - lenStartIdx);

            dirEntry = AltNameNew(cert->heap);
            if (dirEntry == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }

            dirEntry->type = ASN_DIR_TYPE;
            dirEntry->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (dirEntry->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(dirEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            dirEntry->nameStored = 1;
            dirEntry->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)dirEntry->name, &input[idx],
                    (size_t)strLen);
            ((char *)(wc_ptr_t)dirEntry->name)[strLen] = '\0';
            dirEntry->next = cert->altDirNames;
            cert->altDirNames = dirEntry;

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
        else if (current_byte == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) {
            DNS_entry* emailEntry;
            int strLen;
            word32 lenStartIdx = idx;

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }
            length -= (int)(idx - lenStartIdx);

            if ((word32)strLen + idx > sz) {
                return BUFFER_E;
            }
            if (DecodeGeneralNameCheckChars(&input[idx], strLen) != 0) {
                return ASN_PARSE_E;
            }

            emailEntry = AltNameNew(cert->heap);
            if (emailEntry == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }
            emailEntry->nameStored = 1;
            emailEntry->type = ASN_RFC822_TYPE;
            emailEntry->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (emailEntry->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(emailEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            emailEntry->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)emailEntry->name, &input[idx],
                    (size_t)strLen);
            ((char *)(wc_ptr_t)emailEntry->name)[strLen] = '\0';

            emailEntry->next = cert->altEmailNames;
            cert->altEmailNames = emailEntry;

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
        else if (current_byte == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) {
            DNS_entry* uriEntry;
            int strLen;
            word32 lenStartIdx = idx;

            WOLFSSL_MSG("\tPutting URI into list but not using");
            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }
            length -= (int)(idx - lenStartIdx);

            /* check that strLen at index is not past input buffer */
            if ((word32)strLen + idx > sz) {
                return BUFFER_E;
            }

            if (DecodeGeneralNameCheckChars(&input[idx], strLen) != 0) {
                return ASN_PARSE_E;
            }

        #if !defined(WOLFSSL_NO_ASN_STRICT) && !defined(WOLFSSL_FPKI)
            /* Verify RFC 5280 Sec 4.2.1.6 rule:
                "The name MUST NOT be a relative URI"
                As per RFC 3986 Sec 4.3, an absolute URI is only required to contain
                a scheme and hier-part.  So the only strict requirement is a ':'
                being present after the scheme.  If a '/' is present as part of the
                hier-part, it must come after the ':' (see RFC 3986 Sec 3). */

            {
                word32 i;

                /* skip past scheme (i.e http,ftp,...) finding first ':' char */
                for (i = 0; i < (word32)strLen; i++) {
                    if (input[idx + i] == ':') {
                        break;
                    }
                    if (input[idx + i] == '/') {
                        WOLFSSL_MSG("\tAlt Name must be absolute URI");
                        WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E);
                        return ASN_ALT_NAME_E;
                    }
                }

                /* test hier-part is empty */
                if (i == 0 || i == (word32)strLen) {
                    WOLFSSL_MSG("\tEmpty or malformed URI");
                    WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E);
                    return ASN_ALT_NAME_E;
                }

                /* test if scheme is missing */
                if (input[idx + i] != ':') {
                    WOLFSSL_MSG("\tAlt Name must be absolute URI");
                    WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E);
                    return ASN_ALT_NAME_E;
                }
            }
        #endif

            uriEntry = AltNameNew(cert->heap);
            if (uriEntry == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }
            uriEntry->nameStored = 1;
            uriEntry->type = ASN_URI_TYPE;
            uriEntry->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (uriEntry->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(uriEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            uriEntry->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)uriEntry->name, &input[idx],
                    (size_t)strLen);
            ((char *)(wc_ptr_t)uriEntry->name)[strLen] = '\0';

            AddAltName(cert, uriEntry);

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
        /* iPAddress is parsed unconditionally so ConfirmNameConstraints
         * can enforce permitted/excluded iPAddress subtrees
         * (RFC 5280 Sec. 4.2.1.10). WOLFSSL_IP_ALT_NAME only gates the
         * human-readable ipString form. */
        else if (current_byte == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) {
            DNS_entry* ipAddr;
            int strLen;
            word32 lenStartIdx = idx;
            WOLFSSL_MSG("Decoding Subject Alt. Name: IP Address");

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }
            length -= (idx - lenStartIdx);
            /* check that strLen at index is not past input buffer */
            if (strLen + idx > sz) {
                return BUFFER_E;
            }

            ipAddr = AltNameNew(cert->heap);
            if (ipAddr == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }
            ipAddr->nameStored = 1;
            ipAddr->type = ASN_IP_TYPE;
            ipAddr->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (ipAddr->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(ipAddr, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            ipAddr->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)ipAddr->name, &input[idx], strLen);
            ((char *)(wc_ptr_t)ipAddr->name)[strLen] = '\0';

#ifdef WOLFSSL_IP_ALT_NAME
            if (GenerateDNSEntryIPString(ipAddr, cert->heap) != 0) {
                WOLFSSL_MSG("\tOut of Memory for IP string");
                XFREE((void *)(wc_ptr_t)ipAddr->name, cert->heap,
                      DYNAMIC_TYPE_ALTNAME);
                XFREE(ipAddr, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
#endif /* WOLFSSL_IP_ALT_NAME */
            AddAltName(cert, ipAddr);

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
        /* registeredID is parsed unconditionally so ConfirmNameConstraints
         * can enforce permitted/excluded subtrees (RFC 5280 Sec. 4.2.1.10).
         * WOLFSSL_RID_ALT_NAME only gates the human-readable ridString. */
        else if (current_byte == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) {
            DNS_entry* rid;
            int strLen;
            word32 lenStartIdx = idx;
            WOLFSSL_MSG("Decoding Subject Alt. Name: Registered Id");

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: str length");
                return ASN_PARSE_E;
            }
            length -= (idx - lenStartIdx);
            /* check that strLen at index is not past input buffer */
            if (strLen + idx > sz) {
                return BUFFER_E;
            }

            rid = AltNameNew(cert->heap);
            if (rid == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                return MEMORY_E;
            }

            rid->type = ASN_RID_TYPE;
            rid->name = (char*)XMALLOC((size_t)strLen + 1, cert->heap,
                                         DYNAMIC_TYPE_ALTNAME);
            if (rid->name == NULL) {
                WOLFSSL_MSG("\tOut of Memory");
                XFREE(rid, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
            rid->nameStored = 1;
            rid->len = strLen;
            XMEMCPY((void *)(wc_ptr_t)rid->name, &input[idx], strLen);
            ((char *)(wc_ptr_t)rid->name)[strLen] = '\0';

#ifdef WOLFSSL_RID_ALT_NAME
            if (GenerateDNSEntryRIDString(rid, cert->heap) != 0) {
                WOLFSSL_MSG("\tOut of Memory for registered Id string");
                XFREE((void *)(wc_ptr_t)rid->name, cert->heap,
                      DYNAMIC_TYPE_ALTNAME);
                XFREE(rid, cert->heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }
#endif /* WOLFSSL_RID_ALT_NAME */

            AddAltName(cert, rid);

            if (strLen > length) {
                return ASN_PARSE_E;
            }
            length -= strLen;
            idx    += (word32)strLen;
        }
#endif /* IGNORE_NAME_CONSTRAINTS */
        else if (current_byte ==
                (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
            int strLen;
            word32 lenStartIdx = idx;
            word32 oid = 0;
            int    ret = 0;

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: other name length");
                return ASN_PARSE_E;
            }
            /* Consume the rest of this sequence. */
            if ((int)((word32)strLen + idx - lenStartIdx) > length) {
                return ASN_PARSE_E;
            }
            length -= (int)(((word32)strLen + idx - lenStartIdx));

        #ifndef IGNORE_NAME_CONSTRAINTS
            /* Store the raw OtherName encoding (OID || [0] EXPLICIT value)
             * on the dedicated altOtherNamesRaw list so
             * ConfirmNameConstraints() can byte-match it against the
             * issuing CA's subtree (RFC 5280 4.2.1.10). Kept separate from
             * altNames so OpenSSL-compat APIs see exactly what the SAN
             * extension carries. */
            {
                DNS_entry* rawEntry = AltNameNew(cert->heap);
                if (rawEntry == NULL) {
                    WOLFSSL_MSG("\tOut of Memory");
                    return MEMORY_E;
                }
                rawEntry->type = ASN_OTHER_TYPE;
                rawEntry->len  = strLen;
                rawEntry->name = (char*)XMALLOC((size_t)strLen + 1,
                    cert->heap, DYNAMIC_TYPE_ALTNAME);
                if (rawEntry->name == NULL) {
                    XFREE(rawEntry, cert->heap, DYNAMIC_TYPE_ALTNAME);
                    return MEMORY_E;
                }
                rawEntry->nameStored = 1;
                XMEMCPY((void*)(wc_ptr_t)rawEntry->name, &input[idx],
                    (size_t)strLen);
                ((char*)(wc_ptr_t)rawEntry->name)[strLen] = '\0';
                rawEntry->next = cert->altOtherNamesRaw;
                cert->altOtherNamesRaw = rawEntry;
            }
        #endif /* IGNORE_NAME_CONSTRAINTS */

            if (GetObjectId(input, &idx, &oid, oidCertAltNameType, sz) < 0) {
                WOLFSSL_MSG("\tbad OID");
                return ASN_PARSE_E;
            }

            /* handle parsing other type alt names */
            switch (oid) {
            #ifdef WOLFSSL_SEP
                case HW_NAME_OID:
                    ret = DecodeSepHwAltName(cert, input, &idx, sz);
                    if (ret != 0)
                        return ret;
                    break;
            #endif /* WOLFSSL_SEP */
            #ifdef WOLFSSL_FPKI
                case FASCN_OID:
                case UPN_OID:
                    ret = DecodeConstructedOtherName(cert, input, &idx, sz,
                            oid);
                    if (ret != 0)
                        return ret;
                    break;
            #endif /* WOLFSSL_FPKI */

                default:
                    WOLFSSL_MSG("\tUnsupported other name type, skipping");
                    if (GetLength(input, &idx, &strLen, sz) < 0) {
                        /* check to skip constructed other names too */
                        if (DecodeConstructedOtherName(cert, input, &idx, sz,
                                    (int)oid) != 0) {
                            WOLFSSL_MSG("\tfail: unsupported other name length");
                            return ASN_PARSE_E;
                        }
                    }
                    else {
                        idx += (word32)strLen;
                    }
            }
            (void)ret;
        }
        else {
            int strLen;
            word32 lenStartIdx = idx;

            WOLFSSL_MSG("\tUnsupported name type, skipping");

            if (GetLength(input, &idx, &strLen, sz) < 0) {
                WOLFSSL_MSG("\tfail: unsupported name length");
                return ASN_PARSE_E;
            }
            if ((int)((word32)strLen + idx - lenStartIdx) > length) {
                return ASN_PARSE_E;
            }
            length -= (int)((word32)strLen + idx - lenStartIdx);
            idx += (word32)strLen;
        }
    }

    return 0;
}

int DecodeBasicCaConstraint(const byte* input, int sz, byte *isCa,
                            word16 *pathLength, byte *pathLengthSet)
{
    word32 idx = 0;
    int length = 0;
    int ret;

    WOLFSSL_ENTER("DecodeBasicCaConstraint");

    if (GetSequence(input, &idx, &length, (word32)sz) < 0) {
        WOLFSSL_MSG("\tfail: bad SEQUENCE");
        return ASN_PARSE_E;
    }

    if (length == 0)
        return 0;

    /* If the basic ca constraint is false, this extension may be named, but
     * left empty. So, if the length is 0, just return. */

    ret = GetBoolean(input, &idx, (word32)sz);

    /* Removed logic for WOLFSSL_X509_BASICCONS_INT which was mistreating the
     * pathlen value as if it were the CA Boolean value 7/2/2021 - KH.
     * When CA Boolean not asserted use the default value "False" */
    if (ret < 0) {
        WOLFSSL_MSG("\tfail: constraint not valid BOOLEAN, set default FALSE");
        ret = 0;
    }

    *isCa = ret ? 1 : 0;

    /* If there isn't any more data, return. */
    if (idx >= (word32)sz) {
        return 0;
    }

    ret = GetInteger16Bit(input, &idx, (word32)sz);
    if (ret < 0)
        return ret;
    else if (ret > WOLFSSL_MAX_PATH_LEN) {
        WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_SIZE_E);
        return ASN_PATHLEN_SIZE_E;
    }

    *pathLength = (word16)ret;
    *pathLengthSet = 1;

    return 0;
}

static int DecodeCrlDist(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0, localIdx;
    int length = 0;
    byte tag   = 0;

    WOLFSSL_ENTER("DecodeCrlDist");

    cert->extCrlInfoRaw = input;
    cert->extCrlInfoRawSz = (int)sz;

    /* Unwrap the list of Distribution Points*/
    if (GetSequence(input, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    /* Unwrap a single Distribution Point */
    if (GetSequence(input, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    /* The Distribution Point has three explicit optional members
     *  First check for a DistributionPointName
     */
    localIdx = idx;
    if (GetASNTag(input, &localIdx, &tag, sz) == 0 &&
            tag == (ASN_CONSTRUCTED | DISTRIBUTION_POINT))
    {
        idx++;
        if (GetLength(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        localIdx = idx;
        if (GetASNTag(input, &localIdx, &tag, sz) == 0 &&
                tag == (ASN_CONSTRUCTED | CRLDP_FULL_NAME))
        {
            idx++;
            if (GetLength(input, &idx, &length, sz) < 0)
                return ASN_PARSE_E;

            localIdx = idx;
            if (GetASNTag(input, &localIdx, &tag, sz) == 0 &&
                    tag == GENERALNAME_URI)
            {
                idx++;
                if (GetLength(input, &idx, &length, sz) < 0)
                    return ASN_PARSE_E;

                cert->extCrlInfoSz = length;
                cert->extCrlInfo = input + idx;
                idx += (word32)length;
            }
            else
                /* This isn't a URI, skip it. */
                idx += (word32)length;
        }
        else {
            /* This isn't a FULLNAME, skip it. */
            idx += (word32)length;
        }
    }

    /* Check for reasonFlags */
    localIdx = idx;
    if (idx < (word32)sz &&
        GetASNTag(input, &localIdx, &tag, sz) == 0 &&
        tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1))
    {
        idx++;
        if (GetLength(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;
        idx += (word32)length;
    }

    /* Check for cRLIssuer */
    localIdx = idx;
    if (idx < (word32)sz &&
        GetASNTag(input, &localIdx, &tag, sz) == 0 &&
        tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 2))
    {
        idx++;
        if (GetLength(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;
        idx += (word32)length;
    }

    if (idx < (word32)sz)
    {
        WOLFSSL_MSG("\tThere are more CRL Distribution Point records, "
                   "but we only use the first one.");
    }

    return 0;
}

static int DecodeAuthInfo(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    byte b = 0;
    word32 oid;
    int aiaIdx;

    WOLFSSL_ENTER("DecodeAuthInfo");

    /* Unwrap the list of AIAs */
    if (GetSequence(input, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    while ((idx < (word32)sz)) {
        /* Unwrap a single AIA */
        if (GetSequence(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        oid = 0;
        if (GetObjectId(input, &idx, &oid, oidCertAuthInfoType, sz) < 0) {
            return ASN_PARSE_E;
        }

        /* Only supporting URIs right now. */
        if (GetASNTag(input, &idx, &b, sz) < 0)
            return ASN_PARSE_E;

        if (GetLength(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        if (b == GENERALNAME_URI) {
            /* Add to AIA list if space. */
            aiaIdx = cert->extAuthInfoListSz;
            if (aiaIdx < WOLFSSL_MAX_AIA_ENTRIES) {
                cert->extAuthInfoList[aiaIdx].method = oid;
                cert->extAuthInfoList[aiaIdx].uri = input + idx;
                cert->extAuthInfoList[aiaIdx].uriSz = (word32)length;
                cert->extAuthInfoListSz++;
            }
            else {
                cert->extAuthInfoListOverflow = 1;
                WOLFSSL_MSG("AIA list overflow");
            }
        }

        /* Set first ocsp entry */
        if (b == GENERALNAME_URI && oid == AIA_OCSP_OID &&
                cert->extAuthInfo == NULL) {
            cert->extAuthInfoSz = length;
            cert->extAuthInfo = input + idx;
        }
    #ifdef WOLFSSL_ASN_CA_ISSUER
        /* Set first CaIssuers entry */
        else if ((b == GENERALNAME_URI) && oid == AIA_CA_ISSUER_OID &&
                cert->extAuthInfoCaIssuer == NULL)
        {
            cert->extAuthInfoCaIssuerSz = length;
            cert->extAuthInfoCaIssuer = input + idx;
        }
    #endif
        idx += (word32)length;
    }

    return 0;
}

int DecodeAuthKeyId(const byte* input, word32 sz, const byte **extAuthKeyId,
        word32 *extAuthKeyIdSz, const byte **extAuthKeyIdIssuer,
        word32 *extAuthKeyIdIssuerSz, const byte **extAuthKeyIdIssuerSN,
        word32 *extAuthKeyIdIssuerSNSz)
{
    word32 idx = 0;
    int length = 0;
    byte tag;

    WOLFSSL_ENTER("DecodeAuthKeyId");

    if (extAuthKeyId)
        *extAuthKeyId = NULL;
    if (extAuthKeyIdSz)
        *extAuthKeyIdSz = 0;

    if (extAuthKeyIdIssuer)
        *extAuthKeyIdIssuer = NULL;
    if (extAuthKeyIdIssuerSz)
        *extAuthKeyIdIssuerSz = 0;

    if (extAuthKeyIdIssuerSN)
        *extAuthKeyIdIssuerSN = NULL;
    if (extAuthKeyIdIssuerSNSz)
        *extAuthKeyIdIssuerSNSz = 0;

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE");
        return ASN_PARSE_E;
    }

    if (GetASNTag(input, &idx, &tag, sz) < 0) {
        return ASN_PARSE_E;
    }

    if (tag != (ASN_CONTEXT_SPECIFIC | 0)) {
        WOLFSSL_MSG("\tinfo: OPTIONAL item 0, not available");
        return 0;
    }

    if (GetLength(input, &idx, &length, sz) <= 0) {
        WOLFSSL_MSG("\tfail: extension data length");
        return ASN_PARSE_E;
    }

    if (extAuthKeyIdSz)
        *extAuthKeyIdSz = length;
    if (extAuthKeyId)
        *extAuthKeyId = &input[idx];
    return 0;

}

int DecodeKeyUsage(const byte* input, word32 sz, word16 *extKeyUsage)
{
    word32 idx = 0;
    int length;
    int ret;
    WOLFSSL_ENTER("DecodeKeyUsage");

    ret = CheckBitString(input, &idx, &length, sz, 0, NULL);
    if (ret != 0)
        return ret;

    if (length == 0 || length > 2)
        return ASN_PARSE_E;

    *extKeyUsage = (word16)(input[idx]);
    if (length == 2)
        *extKeyUsage |= (word16)(input[idx+1] << 8);

    return 0;
}

int DecodeExtKeyUsage(const byte* input, word32 sz,
        const byte **extExtKeyUsageSrc, word32 *extExtKeyUsageSz,
        word32 *extExtKeyUsageCount, byte *extExtKeyUsage,
        byte *extExtKeyUsageSsh)
{
    word32 idx = 0, oid;
    int length, ret;

    WOLFSSL_ENTER("DecodeExtKeyUsage");

    (void) extExtKeyUsageSrc;
    (void) extExtKeyUsageSz;
    (void) extExtKeyUsageCount;
    (void) extExtKeyUsageSsh;

#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    *extExtKeyUsageSrc = NULL;
    *extExtKeyUsageSz = 0;
    *extExtKeyUsageCount = 0;
#endif
    *extExtKeyUsage = 0;
#ifdef WOLFSSL_WOLFSSH
    *extExtKeyUsageSsh = 0;
#endif

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE");
        return ASN_PARSE_E;
    }

#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    *extExtKeyUsageSrc = input + idx;
    *extExtKeyUsageSz = length;
#endif

    while (idx < (word32)sz) {
        ret = GetObjectId(input, &idx, &oid, oidCertKeyUseType, sz);
        if (ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E))
            continue;
        else if (ret < 0)
            return ret;

        switch (oid) {
            case EKU_ANY_OID:
                *extExtKeyUsage |= EXTKEYUSE_ANY;
                break;
            case EKU_SERVER_AUTH_OID:
                *extExtKeyUsage |= EXTKEYUSE_SERVER_AUTH;
                break;
            case EKU_CLIENT_AUTH_OID:
                *extExtKeyUsage |= EXTKEYUSE_CLIENT_AUTH;
                break;
            case EKU_CODESIGNING_OID:
                *extExtKeyUsage |= EXTKEYUSE_CODESIGN;
                break;
            case EKU_EMAILPROTECT_OID:
                *extExtKeyUsage |= EXTKEYUSE_EMAILPROT;
                break;
            case EKU_TIMESTAMP_OID:
                *extExtKeyUsage |= EXTKEYUSE_TIMESTAMP;
                break;
            case EKU_OCSP_SIGN_OID:
                *extExtKeyUsage |= EXTKEYUSE_OCSP_SIGN;
                break;
            #ifdef WOLFSSL_WOLFSSH
            case EKU_SSH_CLIENT_AUTH_OID:
                *extExtKeyUsageSsh |= EXTKEYUSE_SSH_CLIENT_AUTH;
                break;
            case EKU_SSH_MSCL_OID:
                *extExtKeyUsageSsh |= EXTKEYUSE_SSH_MSCL;
                break;
            case EKU_SSH_KP_CLIENT_AUTH_OID:
                *extExtKeyUsageSsh |= EXTKEYUSE_SSH_KP_CLIENT_AUTH;
                break;
            #endif /* WOLFSSL_WOLFSSH */
            default:
                break;
        }

    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
        (*extExtKeyUsageCount)++;
    #endif
    }

    return 0;
}

#ifndef IGNORE_NAME_CONSTRAINTS
/* See doc on the WOLFSSL_ASN_TEMPLATE definition in asn.c. hasUnsupported
 * must not be NULL. */
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
                         word32 limit, byte* hasUnsupported, void* heap)
{
    word32 idx = 0;
    int ret = 0;
    word32 cnt = 0;

    (void)heap;

    while (idx < (word32)sz) {
        int seqLength, strLength;
        word32 nameIdx;
        byte b, bType;

        if (limit > 0) {
            cnt++;
            if (cnt > limit) {
                WOLFSSL_MSG("too many name constraints");
                return ASN_NAME_INVALID_E;
            }
        }

        if (GetSequence(input, &idx, &seqLength, sz) < 0) {
            WOLFSSL_MSG("\tfail: should be a SEQUENCE");
            return ASN_PARSE_E;
        }

        if (idx >= (word32)sz) {
            WOLFSSL_MSG("\tfail: expecting tag");
            return ASN_PARSE_E;
        }

        nameIdx = idx;
        b = input[nameIdx++];

        if (GetLength(input, &nameIdx, &strLength, sz) <= 0) {
            WOLFSSL_MSG("\tinvalid length");
            return ASN_PARSE_E;
        }

        /* Get type, LSB 4-bits */
        bType = (byte)(b & ASN_TYPE_MASK);

        /* registeredID is included so ConfirmNameConstraints can enforce
         * permitted/excluded subtrees of OIDs (RFC 5280 Sec. 4.2.1.10). */
        if (bType == ASN_DNS_TYPE || bType == ASN_RFC822_TYPE ||
            bType == ASN_DIR_TYPE || bType == ASN_IP_TYPE ||
            bType == ASN_URI_TYPE || bType == ASN_OTHER_TYPE ||
            bType == ASN_RID_TYPE) {
            Base_entry* entry;

            /* directoryName is encoded as [4] CONSTRUCTED { Name } where
             * Name is a SEQUENCE; strip the inner SEQUENCE header.
             * otherName is encoded as [0] CONSTRUCTED { OID, [0] EXPLICIT
             * value }; the inner content is NOT a SEQUENCE so keep it
             * as-is (matches the WOLFSSL_ASN_TEMPLATE path). */
            if ((b & ASN_CONSTRUCTED) && bType != ASN_OTHER_TYPE) {
                if (GetSequence(input, &nameIdx, &strLength, sz) < 0) {
                    WOLFSSL_MSG("\tfail: constructed be a SEQUENCE");
                    return ASN_PARSE_E;
                }
            }

            entry = (Base_entry*)XMALLOC(sizeof(Base_entry), heap,
                                                          DYNAMIC_TYPE_ALTNAME);
            if (entry == NULL) {
                WOLFSSL_MSG("allocate error");
                return MEMORY_E;
            }

            entry->name = (char*)XMALLOC((size_t)strLength+1, heap,
                DYNAMIC_TYPE_ALTNAME);
            if (entry->name == NULL) {
                WOLFSSL_MSG("allocate error");
                XFREE(entry, heap, DYNAMIC_TYPE_ALTNAME);
                return MEMORY_E;
            }

            XMEMCPY(entry->name, &input[nameIdx], (size_t)strLength);
            entry->name[strLength] = '\0';
            entry->nameSz = strLength;
            entry->type = bType;

            entry->next = *head;
            *head = entry;
        }
        else {
            /* GeneralName form (e.g. registeredID, x400Address,
             * ediPartyName) we do not enforce. Record so the caller can
             * fail-closed when the nameConstraints extension is critical
             * (RFC 5280 4.2.1.10). */
            *hasUnsupported = 1;
        }

        idx += (word32)seqLength;
    }

    return ret;
}

static int DecodeNameConstraints(const byte* input, word32 sz,
    DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    byte hasUnsupported = 0;

    WOLFSSL_ENTER("DecodeNameConstraints");

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE");
        return ASN_PARSE_E;
    }

    while (idx < (word32)sz) {
        byte b = input[idx++];
        Base_entry** subtree = NULL;

        if (GetLength(input, &idx, &length, sz) <= 0) {
            WOLFSSL_MSG("\tinvalid length");
            return ASN_PARSE_E;
        }

        if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0))
            subtree = &cert->permittedNames;
        else if (b == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1))
            subtree = &cert->excludedNames;
        else {
            WOLFSSL_MSG("\tinvalid subtree");
            return ASN_PARSE_E;
        }

        if (DecodeSubtree(input + idx, (word32)length, subtree,
                WOLFSSL_MAX_NAME_CONSTRAINTS, &hasUnsupported,
                cert->heap) < 0) {
            WOLFSSL_MSG("\terror parsing subtree");
            return ASN_PARSE_E;
        }

        idx += (word32)length;
    }

    if (hasUnsupported)
        cert->extNameConstraintHasUnsupported = 1;

    return 0;
}

#endif
#if defined(WOLFSSL_SEP) || defined(WOLFSSL_CERT_EXT)
static int DecodeCertPolicy(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    word32 oldIdx;
    int policy_length = 0;
    int ret;
    int total_length = 0;
#if defined(WOLFSSL_CERT_EXT) && !defined(WOLFSSL_DUP_CERTPOL)
    int i;
#endif

    WOLFSSL_ENTER("DecodeCertPolicy");

    /* Check if cert is null before dereferencing below */
    if (cert == NULL)
        return BAD_FUNC_ARG;

#if defined(WOLFSSL_CERT_EXT)
        cert->extCertPoliciesNb = 0;
#endif

    if (GetSequence(input, &idx, &total_length, sz) < 0) {
        WOLFSSL_MSG("\tGet CertPolicy total seq failed");
        return ASN_PARSE_E;
    }

    /* Validate total length */
    if (total_length > (int)(sz - idx)) {
        WOLFSSL_MSG("\tCertPolicy length mismatch");
        return ASN_PARSE_E;
    }

    /* Unwrap certificatePolicies */
    do {
        int length = 0;

        if (GetSequence(input, &idx, &policy_length, sz) < 0) {
            WOLFSSL_MSG("\tGet CertPolicy seq failed");
            return ASN_PARSE_E;
        }

        oldIdx = idx;
        ret = GetASNObjectId(input, &idx, &length, sz);
        if (ret != 0)
            return ret;
        policy_length -= (int)(idx - oldIdx);

        if (length > 0) {
            /* Verify length won't overrun buffer */
            if (length > (int)(sz - idx)) {
                WOLFSSL_MSG("\tCertPolicy length exceeds input buffer");
                return ASN_PARSE_E;
            }

    #ifdef WOLFSSL_SEP
            if (cert->deviceType == NULL) {
                cert->deviceType = (byte*)XMALLOC((size_t)length, cert->heap,
                                                        DYNAMIC_TYPE_X509_EXT);
                if (cert->deviceType == NULL) {
                    WOLFSSL_MSG("\tCouldn't alloc memory for deviceType");
                    return MEMORY_E;
                }
                cert->deviceTypeSz = length;
                XMEMCPY(cert->deviceType, input + idx, (size_t)length);
            }
    #endif

    #ifdef WOLFSSL_CERT_EXT
            /* decode cert policy */
            if (DecodePolicyOID(cert->extCertPolicies[
                                cert->extCertPoliciesNb], MAX_CERTPOL_SZ,
                                input + idx, length) <= 0) {
                WOLFSSL_MSG("\tCouldn't decode CertPolicy");
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                return ASN_PARSE_E;
            }
        #ifndef WOLFSSL_DUP_CERTPOL
            /* From RFC 5280 section 4.2.1.4 "A certificate policy OID MUST
             * NOT appear more than once in a certificate policies
             * extension". This is a sanity check for duplicates.
             * extCertPolicies should only have OID values, additional
             * qualifiers need to be stored in a separate array. */
            for (i = 0; i < cert->extCertPoliciesNb; i++) {
                if (XMEMCMP(cert->extCertPolicies[i],
                            cert->extCertPolicies[cert->extCertPoliciesNb],
                            MAX_CERTPOL_SZ) == 0) {
                    WOLFSSL_MSG("Duplicate policy OIDs not allowed");
                    WOLFSSL_MSG("Use WOLFSSL_DUP_CERTPOL if wanted");
                    WOLFSSL_ERROR_VERBOSE(CERTPOLICIES_E);
                    return CERTPOLICIES_E;
                }
            }
        #endif /* !WOLFSSL_DUP_CERTPOL */
            cert->extCertPoliciesNb++;
    #endif
        }
        idx += (word32)policy_length;
    } while((int)idx < total_length
    #ifdef WOLFSSL_CERT_EXT
        && cert->extCertPoliciesNb < MAX_CERTPOL_NB
    #endif
    );

    WOLFSSL_LEAVE("DecodeCertPolicy", 0);
    return 0;
}

#endif
#ifdef WOLFSSL_SUBJ_DIR_ATTR
static int DecodeSubjDirAttr(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    int ret = 0;

    WOLFSSL_ENTER("DecodeSubjDirAttr");

#ifdef OPENSSL_ALL
    cert->extSubjDirAttrSrc = input;
    cert->extSubjDirAttrSz = sz;
#endif /* OPENSSL_ALL */

    /* Unwrap the list of Attributes */
    if (GetSequence(input, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    if (length == 0) {
        /* RFC 5280 4.2.1.8.  Subject Directory Attributes
           If the subjectDirectoryAttributes extension is present, the
           sequence MUST contain at least one entry. */
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* length is the length of the list contents */
    while (idx < (word32)sz) {
        word32 oid;

        if (GetSequence(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        if (GetObjectId(input, &idx, &oid, oidSubjDirAttrType, sz) < 0)
            return ASN_PARSE_E;

        if (GetSet(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        /* There may be more than one countryOfCitizenship, but save the
         * first one for now. */
        if (oid == SDA_COC_OID) {
            byte tag;

            if (GetHeader(input, &tag, &idx, &length, sz, 1) < 0)
                return ASN_PARSE_E;

            if (length != COUNTRY_CODE_LEN)
                return ASN_PARSE_E;

            if (tag == ASN_PRINTABLE_STRING) {
                XMEMCPY(cert->countryOfCitizenship,
                        input + idx, COUNTRY_CODE_LEN);
                cert->countryOfCitizenship[COUNTRY_CODE_LEN] = 0;
            }
        }
        idx += length;
    }

    return ret;
}

#endif
static int DecodeCertExtensions(DecodedCert* cert)
{
    int ret = 0;
    word32 idx = 0;
    word32 sz = (word32)cert->extensionsSz;
    const byte* input = cert->extensions;
    int length;
    word32 oid;
    byte critical = 0;
    byte criticalFail = 0;
    byte tag = 0;

    WOLFSSL_ENTER("DecodeCertExtensions");

    if (input == NULL || sz == 0)
        return BAD_FUNC_ARG;

#ifdef WOLFSSL_CERT_REQ
    if (!cert->isCSR)
#endif
    { /* Not included in CSR */
        if (GetASNTag(input, &idx, &tag, sz) < 0) {
            return ASN_PARSE_E;
        }

        if (tag != ASN_EXTENSIONS) {
            WOLFSSL_MSG("\tfail: should be an EXTENSIONS");
            return ASN_PARSE_E;
        }

        if (GetLength(input, &idx, &length, sz) < 0) {
            WOLFSSL_MSG("\tfail: invalid length");
            return ASN_PARSE_E;
        }
    }

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE (1)");
        return ASN_PARSE_E;
    }

    while (idx < (word32)sz) {
        word32 localIdx;

        if (GetSequence(input, &idx, &length, sz) < 0) {
            WOLFSSL_MSG("\tfail: should be a SEQUENCE");
            return ASN_PARSE_E;
        }

        oid = 0;
        if ((ret = GetObjectId(input, &idx, &oid, oidCertExtType, sz)) < 0) {
            WOLFSSL_MSG("\tfail: OBJECT ID");
            return ret;
        }

        /* check for critical flag */
        critical = 0;
        if ((idx + 1) > (word32)sz) {
            WOLFSSL_MSG("\tfail: malformed buffer");
            return BUFFER_E;
        }

        localIdx = idx;
        if (GetASNTag(input, &localIdx, &tag, sz) == 0) {
            if (tag == ASN_BOOLEAN) {
                ret = GetBoolean(input, &idx, sz);
                if (ret < 0) {
                    WOLFSSL_MSG("\tfail: critical boolean");
                    return ret;
                }

                critical = (byte)ret;
            }
        }

        /* process the extension based on the OID */
        ret = GetOctetString(input, &idx, &length, sz);
        if (ret < 0) {
            WOLFSSL_MSG("\tfail: bad OCTET STRING");
            return ret;
        }

        ret = DecodeExtensionType(input + idx, (word32)length, oid, critical,
            cert, NULL);
        if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
            ret = 0;
            criticalFail = 1;
        }
        if (ret < 0)
            goto end;
        idx += (word32)length;
    }

    ret = criticalFail ? ASN_CRIT_EXT_E : 0;
end:
    return ret;
}

#if defined(WOLFSSL_SMALL_CERT_VERIFY) || defined(OPENSSL_EXTRA)
static int CheckCertSignature_ex(const byte* cert, word32 certSz, void* heap,
        void* cm, const byte* pubKey, word32 pubKeySz, int pubKeyOID, int req)
{
#if !defined(WOLFSSL_SMALL_STACK) || defined(WOLFSSL_NO_MALLOC)
    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;
    const byte*   sigParams = NULL;
    word32        sigParamsSz = 0;


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

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    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) {
            if (GetAlgoId(cert, &idx, &signatureOID, oidSigType, certSz) < 0)
                ret = ASN_PARSE_E;
        #ifdef WC_RSA_PSS
            else if (signatureOID == CTC_RSASSAPSS) {
                int start = idx;
                sigParams = cert + idx;
                if (GetSequence(cert, &idx, &len, certSz) < 0)
                    ret = ASN_PARSE_E;
                if (ret == 0) {
                    idx += len;
                    sigParamsSz = idx - start;
                }
            }
        #endif
        }
    }

    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;
                                    /* Get the hash or hash of the hash if wrong
                                     * size. */
                                    ret = GetHashId(cert + extIdx, extLen,
                                        hash, HashIdAlg(signatureOID));
                                }
                            }
                            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_ex(cert + issuerIdx, issuerSz, hash,
                HashIdAlg(signatureOID));
            if (ret == 0)
                ca = GetCAByName(cm, hash);
        }
    }
#else
    if (ret == 0 && pubKey == NULL) {
        ret = CalcHashId_ex(cert + issuerIdx, issuerSz, hash,
            HashIdAlg(signatureOID));
        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;
    #ifdef WC_RSA_PSS
        else if (signatureOID == CTC_RSASSAPSS) {
            word32 sz = idx;
            const byte* params = cert + idx;
            if (GetSequence(cert, &idx, &len, certSz) < 0)
                ret = ASN_PARSE_E;
            if (ret == 0) {
                idx += len;
                sz = idx - sz;

                if (req) {
                    if ((sz != sigParamsSz) ||
                                        (XMEMCMP(sigParams, params, sz) != 0)) {
                        ret = ASN_PARSE_E;
                    }
                }
                else {
                    sigParams = params;
                    sigParamsSz = sz;
                }
            }
        }
    #endif
        /* 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, sigParams, sigParamsSz, NULL);
        }
        else {
            ret = ConfirmSignature(sigCtx, cert + tbsCertIdx,
                sigIndex - tbsCertIdx, ca->publicKey, ca->pubKeySize,
                ca->keyOID, cert + idx, len, signatureOID, sigParams,
                sigParamsSz, NULL);
        }
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(ret);
            WOLFSSL_MSG("Confirm signature failed");
        }
    }

    FreeSignatureCtx(sigCtx);
    WC_FREE_VAR_EX(sigCtx, heap, DYNAMIC_TYPE_SIGNATURE);
    return ret;
}

#endif
#endif
int wc_GetSerialNumber(const byte* input, word32* inOutIdx,
    byte* serial, int* serialSz, word32 maxIdx)
{
    int result = 0;
    int ret;

    WOLFSSL_ENTER("wc_GetSerialNumber");

    if (serial == NULL || input == NULL || serialSz == NULL) {
        return BAD_FUNC_ARG;
    }

    /* First byte is ASN type */
    if ((*inOutIdx+1) > maxIdx) {
        WOLFSSL_MSG("Bad idx first");
        return BUFFER_E;
    }

    ret = GetASNInt(input, inOutIdx, serialSz, maxIdx);
    if (ret != 0)
        return ret;

    if (*serialSz > EXTERNAL_SERIAL_SIZE || *serialSz <= 0) {
        WOLFSSL_MSG("Serial size bad");
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* return serial */
    XMEMCPY(serial, &input[*inOutIdx], (size_t)*serialSz);
    *inOutIdx += (word32)*serialSz;

    return result;
}

#ifndef NO_CERTS
#if !defined(NO_RSA) && \
(defined(WOLFSSL_KEY_TO_DER) || defined(WOLFSSL_CERT_GEN))
static int SetRsaPublicKey(byte* output, RsaKey* key, int outLen,
                           int with_header)
{
    int  nSz, eSz;
    word32 seqSz, algoSz = 0, headSz = 0, bitStringSz = 0, idx;
    byte seq[MAX_SEQ_SZ];
    byte headSeq[MAX_SEQ_SZ];
    byte bitString[1 + MAX_LENGTH_SZ + 1];
    byte algo[MAX_ALGO_SZ]; /* 20 bytes */

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

    nSz = SetASNIntMP(&key->n, MAX_RSA_INT_SZ, NULL);

    if (nSz < 0)
        return nSz;

    eSz = SetASNIntMP(&key->e, MAX_RSA_INT_SZ, NULL);

    if (eSz < 0)
        return eSz;
    seqSz = SetSequence((word32)(nSz + eSz), seq);

    /* headers */
    if (with_header) {
        algoSz = SetAlgoID(RSAk, algo, oidKeyType, 0);
        bitStringSz = SetBitString(seqSz + (word32)(nSz + eSz), 0, bitString);
        headSz = SetSequence((word32)(nSz + eSz) + seqSz + bitStringSz + algoSz,
                             headSeq);
    }

    /* if getting length only */
    if (output == NULL) {
        return (int)(headSz + algoSz + bitStringSz + seqSz) + nSz + eSz;
    }

    /* check output size */
    if (((int)(headSz + algoSz + bitStringSz + seqSz) + nSz + eSz) > outLen) {
        return BUFFER_E;
    }

    /* write output */
    idx = 0;
    if (with_header) {
        /* header size */
        XMEMCPY(output + idx, headSeq, headSz);
        idx += headSz;
        /* algo */
        XMEMCPY(output + idx, algo, algoSz);
        idx += algoSz;
        /* bit string */
        XMEMCPY(output + idx, bitString, bitStringSz);
        idx += bitStringSz;
    }

    /* seq */
    XMEMCPY(output + idx, seq, seqSz);
    idx += seqSz;
    /* n */
    nSz = SetASNIntMP(&key->n, nSz, output + idx);
    idx += (word32)nSz;
    /* e */
    eSz = SetASNIntMP(&key->e, eSz, output + idx);
    idx += (word32)eSz;

    return (int)idx;
}

#endif
#endif
#if !defined(NO_RSA) && defined(WOLFSSL_KEY_TO_DER)
int wc_RsaKeyToDer(RsaKey* key, byte* output, word32 inLen)
{
    int ret = 0, i;
    int mpSz;
    word32 seqSz = 0, verSz = 0, intTotalLen = 0, outLen = 0;
    byte  seq[MAX_SEQ_SZ];
    byte  ver[MAX_VERSION_SZ];
    mp_int* keyInt;
#ifndef WOLFSSL_NO_MALLOC
    word32 rawLen;
    byte* tmps[RSA_INTS];
    word32 sizes[RSA_INTS];
#endif

    if (key == NULL)
        return BAD_FUNC_ARG;

    if (key->type != RSA_PRIVATE)
        return BAD_FUNC_ARG;

#ifndef WOLFSSL_NO_MALLOC
    for (i = 0; i < RSA_INTS; i++)
        tmps[i] = NULL;
#endif

    /* write all big ints from key to DER tmps */
    for (i = 0; i < RSA_INTS; i++) {
        keyInt = GetRsaInt(key, i);
        ret = mp_unsigned_bin_size(keyInt);
        if (ret < 0)
            break;
#ifndef WOLFSSL_NO_MALLOC
        rawLen = (word32)ret + 1;
        ret = 0;
        if (output != NULL) {
            tmps[i] = (byte*)XMALLOC(rawLen + MAX_SEQ_SZ, key->heap,
                                 DYNAMIC_TYPE_RSA);
            if (tmps[i] == NULL) {
                ret = MEMORY_E;
                break;
            }
        }
        mpSz = SetASNIntMP(keyInt, MAX_RSA_INT_SZ, tmps[i]);
#else
        ret = 0;
        mpSz = SetASNIntMP(keyInt, MAX_RSA_INT_SZ, NULL);
#endif
        if (mpSz < 0) {
            ret = mpSz;
            break;
        }
    #ifndef WOLFSSL_NO_MALLOC
        sizes[i] = (word32)mpSz;
    #endif
        intTotalLen += (word32)mpSz;
    }

    if (ret == 0) {
        /* make headers */
        ret = SetMyVersion(0, ver, FALSE);
    }

    if (ret >= 0) {
        verSz = (word32)ret;
        ret = 0;
        seqSz = SetSequence(verSz + intTotalLen, seq);
        outLen = seqSz + verSz + intTotalLen;
        if (output != NULL && outLen > inLen)
            ret = BUFFER_E;
    }
    if (ret == 0 && output != NULL) {
        word32 j;

        /* write to output */
        XMEMCPY(output, seq, seqSz);
        j = seqSz;
        XMEMCPY(output + j, ver, verSz);
        j += verSz;

        for (i = 0; i < RSA_INTS; i++) {
/* copy from tmps if we have malloc, otherwise re-export with buffer */
#ifndef WOLFSSL_NO_MALLOC
            XMEMCPY(output + j, tmps[i], sizes[i]);
            j += sizes[i];
#else
            keyInt = GetRsaInt(key, i);
            ret = mp_unsigned_bin_size(keyInt);
            if (ret < 0)
                break;
            ret = 0;
            /* This won't overrun output due to the outLen check above */
            mpSz = SetASNIntMP(keyInt, MAX_RSA_INT_SZ, output + j);
            if (mpSz < 0) {
                ret = mpSz;
                break;
            }
            j += mpSz;
#endif
        }
    }

#ifndef WOLFSSL_NO_MALLOC
    for (i = 0; i < RSA_INTS; i++) {
        if (tmps[i])
            XFREE(tmps[i], key->heap, DYNAMIC_TYPE_RSA);
    }
#endif

    if (ret == 0)
        ret = (int)outLen;
    return ret;
}

#endif
#ifndef NO_CERTS
#ifdef WOLFSSL_CERT_GEN
#ifdef WOLFSSL_CERT_REQ

/* Write a set header to output */
static word32 SetPrintableString(word32 len, byte* output)
{
    output[0] = ASN_PRINTABLE_STRING;
    return SetLength(len, output + 1) + 1;
}

static word32 SetUTF8String(word32 len, byte* output)
{
    output[0] = ASN_UTF8STRING;
    return SetLength(len, output + 1) + 1;
}


#endif

/* Copy Dates from cert, return bytes written */
static int CopyValidity(byte* output, Cert* cert)
{
    word32 seqSz;

    WOLFSSL_ENTER("CopyValidity");

    /* headers and output */
    seqSz = SetSequence((word32)(cert->beforeDateSz + cert->afterDateSz),
                        output);
    if (output) {
        XMEMCPY(output + seqSz, cert->beforeDate, (size_t)cert->beforeDateSz);
        XMEMCPY(output + seqSz + cert->beforeDateSz, cert->afterDate,
                (size_t)cert->afterDateSz);
    }
    return (int)seqSz + cert->beforeDateSz + cert->afterDateSz;
}


/*
 Extensions ::= SEQUENCE OF Extension

 Extension ::= SEQUENCE {
 extnId     OBJECT IDENTIFIER,
 critical   BOOLEAN DEFAULT FALSE,
 extnValue  OCTET STRING }
 */

/* encode all extensions, return total bytes written */
static int SetExtensions(byte* out, word32 outSz, int *IdxInOut,
                         const byte* ext, int extSz)
{
    if (out == NULL || IdxInOut == NULL || ext == NULL)
        return BAD_FUNC_ARG;

    if (outSz < (word32)(*IdxInOut+extSz))
        return BUFFER_E;

    XMEMCPY(&out[*IdxInOut], ext, (size_t)extSz);  /* extensions */
    *IdxInOut += extSz;

    return *IdxInOut;
}

/* encode extensions header, return total bytes written */
static int SetExtensionsHeader(byte* out, word32 outSz, word32 extSz)
{
    byte sequence[MAX_SEQ_SZ];
    byte len[MAX_LENGTH_SZ];
    word32 seqSz, lenSz, idx = 0;

    if (out == NULL)
        return BAD_FUNC_ARG;

    if (outSz < 3)
        return BUFFER_E;

    seqSz = SetSequence(extSz, sequence);

    /* encode extensions length provided */
    lenSz = SetLength(extSz+seqSz, len);

    if (outSz < (word32)(lenSz+seqSz+1))
        return BUFFER_E;

    out[idx++] = ASN_EXTENSIONS; /* extensions id */
    XMEMCPY(&out[idx], len, lenSz);  /* length */
    idx += lenSz;

    XMEMCPY(&out[idx], sequence, seqSz);  /* sequence */
    idx += seqSz;

    return (int)idx;
}


/* encode CA basic constraints true with path length
 * return total bytes written */
static int SetCaWithPathLen(byte* out, word32 outSz, byte pathLen)
{
    /* ASN1->DER sequence for Basic Constraints True and path length */
    const byte caPathLenBasicConstASN1[] = {
        0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04,
        0x08, 0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01,
        0x00
    };

    if (out == NULL)
        return BAD_FUNC_ARG;

    if (outSz < sizeof(caPathLenBasicConstASN1))
        return BUFFER_E;

    XMEMCPY(out, caPathLenBasicConstASN1, sizeof(caPathLenBasicConstASN1));

    out[sizeof(caPathLenBasicConstASN1)-1] = pathLen;

    return (int)sizeof(caPathLenBasicConstASN1);
}

/* encode CA basic constraints
 * return total bytes written */
static int SetCaEx(byte* out, word32 outSz, byte isCa)
{
    /* ASN1->DER sequence for Basic Constraints True */
    const byte caBasicConstASN1[] = {
        0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
        0x05, 0x30, 0x03, 0x01, 0x01, 0xff
    };

    if (out == NULL)
        return BAD_FUNC_ARG;

    if (outSz < sizeof(caBasicConstASN1))
        return BUFFER_E;

    XMEMCPY(out, caBasicConstASN1, sizeof(caBasicConstASN1));

    if (!isCa) {
        out[sizeof(caBasicConstASN1)-1] = isCa;
    }

    return (int)sizeof(caBasicConstASN1);
}

/* encode CA basic constraints true
 * return total bytes written */
static int SetCa(byte* out, word32 outSz)
{
    return SetCaEx(out, outSz, 1);
}

/* encode basic constraints without CA Boolean
 * return total bytes written */
static int SetBC(byte* out, word32 outSz)
{
    /* ASN1->DER sequence for Basic Constraint without CA Boolean */
 const byte BasicConstASN1[] = {
        0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
        0x02, 0x30, 0x00
    };

    if (out == NULL)
        return BAD_FUNC_ARG;

    if (outSz < sizeof(BasicConstASN1))
        return BUFFER_E;

    XMEMCPY(out, BasicConstASN1, sizeof(BasicConstASN1));

    return (int)sizeof(BasicConstASN1);
}

#ifdef WOLFSSL_CERT_EXT
/* encode OID and associated value, return total bytes written */
static int SetOidValue(byte* out, word32 outSz, const byte *oid, word32 oidSz,
                       byte *in, word32 inSz)
{
    word32 idx = 0;

    if (out == NULL || oid == NULL || in == NULL)
        return BAD_FUNC_ARG;
    if (inSz >= ASN_LONG_LENGTH)
        return BAD_FUNC_ARG;
    if (oidSz >= ASN_LONG_LENGTH)
        return BAD_FUNC_ARG;
    if (inSz + oidSz + 1 >= ASN_LONG_LENGTH)
        return BAD_FUNC_ARG;

    if (outSz < 3)
        return BUFFER_E;

    /* sequence,  + 1 => byte to put value size */
    idx = SetSequence(inSz + oidSz + 1, out);

    if ((idx + inSz + oidSz + 1) > outSz)
        return BUFFER_E;

    XMEMCPY(out+idx, oid, oidSz);
    idx += oidSz;
    out[idx++] = (byte)inSz;
    XMEMCPY(out+idx, in, inSz);

    return (int)(idx+inSz);
}

/* encode Subject Key Identifier, return total bytes written
 * RFC5280 : non-critical */
static int SetSKID(byte* output, word32 outSz, const byte *input, word32 length)
{
    byte skid_len[1 + MAX_LENGTH_SZ];
    byte skid_enc_len[MAX_LENGTH_SZ];
    word32 idx = 0, skid_lenSz, skid_enc_lenSz;
    const byte skid_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04 };

    if (output == NULL || input == NULL)
        return BAD_FUNC_ARG;

    /* Octet String header */
    skid_lenSz = SetOctetString(length, skid_len);

    /* length of encoded value */
    skid_enc_lenSz = SetLength(length + skid_lenSz, skid_enc_len);

    if (outSz < 3)
        return BUFFER_E;

    idx = SetSequence(length + (word32)sizeof(skid_oid) + skid_lenSz +
                      skid_enc_lenSz, output);

    if ((length + sizeof(skid_oid) + skid_lenSz + skid_enc_lenSz) > outSz)
        return BUFFER_E;

    /* put oid */
    XMEMCPY(output+idx, skid_oid, sizeof(skid_oid));
    idx += sizeof(skid_oid);

    /* put encoded len */
    XMEMCPY(output+idx, skid_enc_len, skid_enc_lenSz);
    idx += skid_enc_lenSz;

    /* put octet header */
    XMEMCPY(output+idx, skid_len, skid_lenSz);
    idx += skid_lenSz;

    /* put value */
    XMEMCPY(output+idx, input, length);
    idx += length;

    return (int)idx;
}

/* encode Authority Key Identifier, return total bytes written
 * RFC5280 : non-critical */
static int SetAKID(byte* output, word32 outSz, byte *input, word32 length,
                   byte rawAkid)
{
    int     enc_valSz;
    byte enc_val_buf[MAX_KID_SZ];
    byte* enc_val;
    const byte akid_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x23 };
    const byte akid_cs[] = { 0x80 };
    word32 inSeqSz, idx;

    (void)rawAkid;

    if (output == NULL || input == NULL)
        return BAD_FUNC_ARG;

#ifdef WOLFSSL_AKID_NAME
    if (rawAkid) {
        enc_val = input;
        enc_valSz = length;
    }
    else
#endif
    {
        enc_val = enc_val_buf;
        enc_valSz = (int)length + 3 + (int)sizeof(akid_cs);
        if (enc_valSz > (int)sizeof(enc_val_buf))
            return BAD_FUNC_ARG;

        /* sequence for ContentSpec & value */
        enc_valSz = SetOidValue(enc_val, (word32)enc_valSz, akid_cs,
                                sizeof(akid_cs), input, length);
        if (enc_valSz <= 0)
            return enc_valSz;
    }

    /* The size of the extension sequence contents */
    inSeqSz = (word32)sizeof(akid_oid) +
        SetOctetString((word32)enc_valSz, NULL) + (word32)enc_valSz;

    if (SetSequence(inSeqSz, NULL) + inSeqSz > outSz)
        return BAD_FUNC_ARG;

    /* Write out the sequence header */
    idx = SetSequence(inSeqSz, output);

    /* Write out OID */
    XMEMCPY(output + idx, akid_oid, sizeof(akid_oid));
    idx += sizeof(akid_oid);

    /* Write out AKID */
    idx += SetOctetString((word32)enc_valSz, output + idx);
    XMEMCPY(output + idx, enc_val, (size_t)enc_valSz);

    return (int)idx + enc_valSz;
}

#ifdef WOLFSSL_ACME_OID
/* encode RFC 8737 id-pe-acmeIdentifier extension, return total bytes written
 * RFC8737 : critical */
static int SetAcmeIdentifier(byte* output, word32 outSz, const byte* digest,
                             word32 digestSz)
{
    byte inner[1 + MAX_LENGTH_SZ + WC_SHA256_DIGEST_SIZE];
    word32 innerSz;
    const byte acmeId_oid[] = { 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07,
                                0x01, 0x1F, 0x01, 0x01, 0xFF, 0x04 };

    if (output == NULL || digest == NULL)
        return BAD_FUNC_ARG;
    if (digestSz != WC_SHA256_DIGEST_SIZE)
        return BAD_FUNC_ARG;

    innerSz = SetOctetString(digestSz, inner);
    XMEMCPY(inner + innerSz, digest, digestSz);
    innerSz += digestSz;

    return SetOidValue(output, outSz, acmeId_oid, sizeof(acmeId_oid),
                       inner, innerSz);
}
#endif /* WOLFSSL_ACME_OID */

/* encode Key Usage, return total bytes written
 * RFC5280 : critical */
static int SetKeyUsage(byte* output, word32 outSz, word16 input)
{
    byte ku[5];
    word32 idx;
    const byte keyusage_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x0f,
                                         0x01, 0x01, 0xff, 0x04};
    if (output == NULL)
        return BAD_FUNC_ARG;

    idx = SetBitString16Bit(input, ku);
    return SetOidValue(output, outSz, keyusage_oid, sizeof(keyusage_oid),
                       ku, idx);
}

static int SetOjectIdValue(byte* output, word32 outSz, word32* idx,
    const byte* oid, word32 oidSz)
{
    /* verify room */
    if (*idx + 2 + oidSz >= outSz)
        return ASN_PARSE_E;

    *idx += (word32)SetObjectId((int)oidSz, &output[*idx]);
    XMEMCPY(&output[*idx], oid, oidSz);
    *idx += oidSz;

    return 0;
}

static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input)
{
    word32 idx = 0, oidListSz = 0, totalSz;
    int ret = 0;
    const byte extkeyusage_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x25 };

    if (output == NULL)
        return BAD_FUNC_ARG;

    /* Skip to OID List */
    totalSz = 2 + sizeof(extkeyusage_oid) + 4;
    idx = totalSz;

    /* Build OID List */
    /* If any set, then just use it */
    if (input & EXTKEYUSE_ANY) {
        ret |= SetOjectIdValue(output, outSz, &idx,
            extExtKeyUsageAnyOid, sizeof(extExtKeyUsageAnyOid));
    }
    else {
        if (input & EXTKEYUSE_SERVER_AUTH)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageServerAuthOid, sizeof(extExtKeyUsageServerAuthOid));
        if (input & EXTKEYUSE_CLIENT_AUTH)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageClientAuthOid, sizeof(extExtKeyUsageClientAuthOid));
        if (input & EXTKEYUSE_CODESIGN)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageCodeSigningOid, sizeof(extExtKeyUsageCodeSigningOid));
        if (input & EXTKEYUSE_EMAILPROT)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageEmailProtectOid, sizeof(extExtKeyUsageEmailProtectOid));
        if (input & EXTKEYUSE_TIMESTAMP)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageTimestampOid, sizeof(extExtKeyUsageTimestampOid));
        if (input & EXTKEYUSE_OCSP_SIGN)
            ret |= SetOjectIdValue(output, outSz, &idx,
                extExtKeyUsageOcspSignOid, sizeof(extExtKeyUsageOcspSignOid));
    #ifdef WOLFSSL_EKU_OID
        /* iterate through OID values */
        if (input & EXTKEYUSE_USER) {
            int i, sz;
            for (i = 0; i < CTC_MAX_EKU_NB; i++) {
                sz = cert->extKeyUsageOIDSz[i];
                if (sz > 0) {
                    ret |= SetOjectIdValue(output, outSz, &idx,
                        cert->extKeyUsageOID[i], sz);
                }
            }
        }
    #endif /* WOLFSSL_EKU_OID */
    }
    if (ret != 0)
        return ASN_PARSE_E;

    /* Calculate Sizes */
    oidListSz = idx - totalSz;
    totalSz = idx - 2; /* exclude first seq/len (2) */

    /* 1. Seq + Total Len (2) */
    idx = SetSequence(totalSz, output);

    /* 2. Object ID (2) */
    XMEMCPY(&output[idx], extkeyusage_oid, sizeof(extkeyusage_oid));
    idx += sizeof(extkeyusage_oid);

    /* 3. Octet String (2) */
    idx += SetOctetString(totalSz - idx, &output[idx]);

    /* 4. Seq + OidListLen (2) */
    idx += SetSequence(oidListSz, &output[idx]);

    /* 5. Oid List (already set in-place above) */
    idx += oidListSz;

    (void)cert;
    return (int)idx;
}

#ifndef IGNORE_NETSCAPE_CERT_TYPE
static int SetNsCertType(Cert* cert, byte* output, word32 outSz, byte input)
{
    word32 idx;
    byte unusedBits = 0;
    byte nsCertType = input;
    word32 totalSz;
    word32 bitStrSz;
    const byte nscerttype_oid[] = { 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
                                    0x86, 0xF8, 0x42, 0x01, 0x01 };

    if (cert == NULL || output == NULL ||
            input == 0)
        return BAD_FUNC_ARG;

    totalSz = sizeof(nscerttype_oid);

    /* Get amount of lsb zero's */
    for (;(input & 1) == 0; input >>= 1)
        unusedBits++;

    /* 1 byte of NS Cert Type extension */
    bitStrSz = SetBitString(1, unusedBits, NULL) + 1;
    totalSz += SetOctetString(bitStrSz, NULL) + bitStrSz;

    if (SetSequence(totalSz, NULL) + totalSz > outSz)
        return BAD_FUNC_ARG;

    /* 1. Seq + Total Len */
    idx = SetSequence(totalSz, output);

    /* 2. Object ID */
    XMEMCPY(&output[idx], nscerttype_oid, sizeof(nscerttype_oid));
    idx += sizeof(nscerttype_oid);

    /* 3. Octet String */
    idx += SetOctetString(bitStrSz, &output[idx]);

    /* 4. Bit String */
    idx += SetBitString(1, unusedBits, &output[idx]);
    output[idx++] = nsCertType;

    return (int)idx;
}

#endif
static int SetCRLInfo(Cert* cert, byte* output, word32 outSz, byte* input,
                      int inSz)
{
    word32 idx;
    word32 totalSz;
    const byte crlinfo_oid[] = { 0x06, 0x03, 0x55, 0x1D, 0x1F };

    if (cert == NULL || output == NULL ||
            input == 0 || inSz <= 0)
        return BAD_FUNC_ARG;

    totalSz = (word32)sizeof(crlinfo_oid) + SetOctetString((word32)inSz, NULL) +
        (word32)inSz;

    if (SetSequence(totalSz, NULL) + totalSz > outSz)
        return BAD_FUNC_ARG;

    /* 1. Seq + Total Len */
    idx = SetSequence(totalSz, output);

    /* 2. Object ID */
    XMEMCPY(&output[idx], crlinfo_oid, sizeof(crlinfo_oid));
    idx += sizeof(crlinfo_oid);

    /* 3. Octet String */
    idx += SetOctetString((word32)inSz, &output[idx]);

    /* 4. CRL Info */
    XMEMCPY(&output[idx], input, (size_t)inSz);
    idx += (word32)inSz;

    return (int)idx;
}

static int SetCertificatePolicies(byte *output,
                                  word32 outputSz,
                                  char input[MAX_CERTPOL_NB][MAX_CERTPOL_SZ],
                                  word16 nb_certpol,
                                  void* heap)
{
    byte    oid[MAX_OID_SZ];
    byte    der_oid[MAX_CERTPOL_NB][MAX_OID_SZ];
    byte    out[MAX_CERTPOL_SZ];
    word32  oidSz;
    word32  outSz;
    word32  i = 0;
    word32  der_oidSz[MAX_CERTPOL_NB];
    int     ret;

    const byte certpol_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04 };
    const byte oid_oid[] = { 0x06 };

    if (output == NULL || input == NULL || nb_certpol > MAX_CERTPOL_NB)
        return BAD_FUNC_ARG;

    for (i = 0; i < nb_certpol; i++) {
        oidSz = sizeof(oid);
        XMEMSET(oid, 0, oidSz);

        ret = EncodePolicyOID(oid, &oidSz, input[i], heap);
        if (ret != 0)
            return ret;

        /* compute sequence value for the oid */
        ret = SetOidValue(der_oid[i], MAX_OID_SZ, oid_oid,
                          sizeof(oid_oid), oid, oidSz);
        if (ret <= 0)
            return ret;
        else
            der_oidSz[i] = (word32)ret;
    }

    /* concatenate oid, keep two byte for sequence/size of the created value */
    for (i = 0, outSz = 2; i < nb_certpol; i++) {
        XMEMCPY(out+outSz, der_oid[i], der_oidSz[i]);
        outSz += der_oidSz[i];
    }

    /* add sequence */
    ret = (int)SetSequence(outSz-2, out);
    if (ret <= 0)
        return ret;

    /* add Policy OID to compute final value */
    return SetOidValue(output, outputSz, certpol_oid, sizeof(certpol_oid),
                      out, outSz);
}

#endif
#ifdef WOLFSSL_ALT_NAMES
/* encode Alternative Names, return total bytes written */
static int SetAltNames(byte *output, word32 outSz,
        const byte *input, word32 length, int critical)
{
    byte san_len[1 + MAX_LENGTH_SZ];
    const byte san_oid[] = { 0x06, 0x03, 0x55, 0x1d, 0x11 };
    const byte san_crit[] = { 0x01, 0x01, 0xff };
    word32 seqSz, san_lenSz, idx = 0;

    if (output == NULL || input == NULL)
        return BAD_FUNC_ARG;

    if (outSz < length)
        return BUFFER_E;

    /* Octet String header */
    san_lenSz = SetOctetString(length, san_len);

    seqSz = length + (word32)sizeof(san_oid) + san_lenSz;
    if (critical)
        seqSz += sizeof(san_crit);
    /* Tag plus encoded length. */
    if (outSz < 1 + ASN_LEN_ENC_LEN(seqSz))
        return BUFFER_E;
    idx = SetSequence(seqSz, output);

    if (idx + seqSz > outSz)
        return BUFFER_E;

    /* put oid */
    XMEMCPY(output+idx, san_oid, sizeof(san_oid));
    idx += sizeof(san_oid);

    if (critical) {
        XMEMCPY(output+idx, san_crit, sizeof(san_crit));
        idx += sizeof(san_crit);
    }

    /* put octet header */
    XMEMCPY(output+idx, san_len, san_lenSz);
    idx += san_lenSz;

    /* put value */
    XMEMCPY(output+idx, input, length);
    idx += length;

    return (int)idx;
}

#endif
#endif
#if defined(WOLFSSL_CERT_GEN) || defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
static int EncodeName(EncodedName* name, const char* nameStr,
                    byte nameTag, byte type, byte emailTag, CertName* cname)
{
    word32 idx = 0;
    /* bottom up */
    byte firstLen[1 + MAX_LENGTH_SZ];
    byte secondLen[MAX_LENGTH_SZ];
    byte sequence[MAX_SEQ_SZ];
    byte set[MAX_SET_SZ];

    word32 strLen;
    word32 thisLen;
    word32 firstSz, secondSz, seqSz, setSz;

    if (nameStr == NULL) {
        name->used = 0;
        return 0;
    }

    thisLen = strLen = (word32)XSTRLEN(nameStr);
#ifdef WOLFSSL_CUSTOM_OID
    if (type == ASN_CUSTOM_NAME) {
        if (cname == NULL || cname->custom.oidSz == 0) {
            name->used = 0;
            return 0;
        }
        thisLen = strLen = (word32)cname->custom.valSz;
    }
#else
    (void)cname;
#endif

    if (strLen == 0) { /* no user data for this item */
        name->used = 0;
        return 0;
    }

    /* Restrict country code size */
    if (type == ASN_COUNTRY_NAME && strLen != CTC_COUNTRY_SIZE) {
        WOLFSSL_MSG("Country code size error");
        WOLFSSL_ERROR_VERBOSE(ASN_COUNTRY_SIZE_E);
        return ASN_COUNTRY_SIZE_E;
    }

    secondSz = SetLength(strLen, secondLen);
    thisLen += secondSz;
    switch (type) {
        case ASN_EMAIL_NAME: /* email */
            thisLen += (int)sizeof(attrEmailOid);
            firstSz  = (int)sizeof(attrEmailOid);
            break;
        case ASN_DOMAIN_COMPONENT:
            thisLen += (int)sizeof(dcOid);
            firstSz  = (int)sizeof(dcOid);
            break;
        case ASN_USER_ID:
            thisLen += (int)sizeof(uidOid);
            firstSz  = (int)sizeof(uidOid);
            break;
        case ASN_RFC822_MAILBOX:
            thisLen += (int)sizeof(rfc822Mlbx);
            firstSz  = (int)sizeof(rfc822Mlbx);
            break;
        case ASN_FAVOURITE_DRINK:
            thisLen += (int)sizeof(fvrtDrk);
            firstSz  = (int)sizeof(fvrtDrk);
            break;
    #ifdef WOLFSSL_CUSTOM_OID
        case ASN_CUSTOM_NAME:
            thisLen += cname->custom.oidSz;
            firstSz = cname->custom.oidSz;
            break;
    #endif
    #ifdef WOLFSSL_CERT_REQ
        case ASN_CONTENT_TYPE:
            thisLen += (int)sizeof(attrPkcs9ContentTypeOid);
            firstSz  = (int)sizeof(attrPkcs9ContentTypeOid);
            break;
    #endif
        default:
            thisLen += DN_OID_SZ;
            firstSz  = DN_OID_SZ;
    }
    thisLen++; /* id  type */
    firstSz  = (word32)SetObjectId((int)firstSz, firstLen);
    thisLen += firstSz;

    seqSz = SetSequence(thisLen, sequence);
    thisLen += seqSz;
    setSz = SetSet(thisLen, set);
    thisLen += setSz;

    if (thisLen > (int)sizeof(name->encoded)) {
        return BUFFER_E;
    }

    /* store it */
    idx = 0;
    /* set */
    XMEMCPY(name->encoded, set, setSz);
    idx += setSz;
    /* seq */
    XMEMCPY(name->encoded + idx, sequence, seqSz);
    idx += seqSz;
    /* asn object id */
    XMEMCPY(name->encoded + idx, firstLen, firstSz);
    idx += firstSz;
    switch (type) {
        case ASN_EMAIL_NAME:
            /* email joint id */
            XMEMCPY(name->encoded + idx, attrEmailOid, sizeof(attrEmailOid));
            idx += (int)sizeof(attrEmailOid);
            name->encoded[idx++] = emailTag;
            break;
        case ASN_DOMAIN_COMPONENT:
            XMEMCPY(name->encoded + idx, dcOid, sizeof(dcOid)-1);
            idx += (int)sizeof(dcOid)-1;
            /* id type */
            name->encoded[idx++] = type;
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
        case ASN_USER_ID:
            XMEMCPY(name->encoded + idx, uidOid, sizeof(uidOid));
            idx += (int)sizeof(uidOid);
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
        case ASN_RFC822_MAILBOX:
            XMEMCPY(name->encoded + idx, rfc822Mlbx, sizeof(rfc822Mlbx));
            idx += (int)sizeof(rfc822Mlbx);
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
        case ASN_FAVOURITE_DRINK:
            XMEMCPY(name->encoded + idx, fvrtDrk, sizeof(fvrtDrk));
            idx += (int)sizeof(fvrtDrk);
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
    #ifdef WOLFSSL_CUSTOM_OID
        case ASN_CUSTOM_NAME:
            XMEMCPY(name->encoded + idx, cname->custom.oid,
                    cname->custom.oidSz);
            idx += cname->custom.oidSz;
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
    #endif
    #ifdef WOLFSSL_CERT_REQ
        case ASN_CONTENT_TYPE:
            XMEMCPY(name->encoded + idx, attrPkcs9ContentTypeOid,
                    sizeof(attrPkcs9ContentTypeOid));
            idx += (int)sizeof(attrPkcs9ContentTypeOid);
            /* str type */
            name->encoded[idx++] = nameTag;
            break;
    #endif
        default:
            name->encoded[idx++] = 0x55;
            name->encoded[idx++] = 0x04;
            /* id type */
            name->encoded[idx++] = type;
            /* str type */
            name->encoded[idx++] = nameTag;
    }
    /* second length */
    XMEMCPY(name->encoded + idx, secondLen, secondSz);
    idx += secondSz;
    /* str value */
    XMEMCPY(name->encoded + idx, nameStr, strLen);
    idx += strLen;

    name->type = type;
    name->totalLen = (int)idx;
    name->used = 1;

    return (int)idx;
}

#endif
#ifdef WOLFSSL_CERT_GEN
int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap)
{
    int ret;
    int i;
    word32 idx, totalBytes = 0;
    WC_DECLARE_VAR(names, EncodedName, NAME_ENTRIES, 0);
#ifdef WOLFSSL_MULTI_ATTRIB
    EncodedName addNames[CTC_MAX_ATTRIB];
    int j, type;
#endif

    if (output == NULL || name == NULL)
        return BAD_FUNC_ARG;

    if (outputSz < 3)
        return BUFFER_E;

    WC_ALLOC_VAR_EX(names, EncodedName, NAME_ENTRIES, NULL,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);

    for (i = 0; i < NAME_ENTRIES; i++) {
        const char* nameStr = GetOneCertName(name, i);

        ret = EncodeName(&names[i], nameStr, (byte)GetNameType(name, i),
                          GetCertNameId(i), ASN_IA5_STRING, name);
        if (ret < 0) {
            WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
            WOLFSSL_MSG("EncodeName failed");
            return BUFFER_E;
        }
        totalBytes += (word32)ret;
    }
#ifdef WOLFSSL_MULTI_ATTRIB
    for (i = 0; i < CTC_MAX_ATTRIB; i++) {
        if (name->name[i].sz > 0) {
            ret = EncodeName(&addNames[i], name->name[i].value,
                             (byte)name->name[i].type, (byte)name->name[i].id,
                        ASN_IA5_STRING, NULL);
            if (ret < 0) {
                WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
                WOLFSSL_MSG("EncodeName on multiple attributes failed");
                return BUFFER_E;
            }
            totalBytes += (word32)ret;
        }
        else {
            addNames[i].used = 0;
        }
    }
#endif /* WOLFSSL_MULTI_ATTRIB */

    /* header */
    idx = SetSequence(totalBytes, output);
    totalBytes += idx;
    if (totalBytes > WC_ASN_NAME_MAX) {
        WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WOLFSSL_MSG("Total Bytes is greater than WC_ASN_NAME_MAX");
        return BUFFER_E;
    }

    for (i = 0; i < NAME_ENTRIES; i++) {
    #ifdef WOLFSSL_MULTI_ATTRIB
        type = GetCertNameId(i);
        for (j = 0; j < CTC_MAX_ATTRIB; j++) {
            if (name->name[j].sz > 0 && type == name->name[j].id) {
                if (outputSz < idx + (word32)addNames[j].totalLen) {
                    WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
                    WOLFSSL_MSG("Not enough space left for DC value");
                    return BUFFER_E;
                }

                XMEMCPY(output + idx, addNames[j].encoded,
                        (size_t)addNames[j].totalLen);
                idx += (word32)addNames[j].totalLen;
            }
        }
    #endif /* WOLFSSL_MULTI_ATTRIB */

        if (names[i].used) {
            if (outputSz < idx + (word32)names[i].totalLen) {
                WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
                return BUFFER_E;
            }

            XMEMCPY(output + idx, names[i].encoded, (size_t)names[i].totalLen);
            idx += (word32)names[i].totalLen;
        }
    }

    WC_FREE_VAR_EX(names, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    (void)heap;

    return (int)totalBytes;
}

/* Set Date validity from now until now + daysValid
 * return size in bytes written to output, 0 on error */
/* TODO https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.5
 * "MUST always encode certificate validity dates through the year 2049 as
 *  UTCTime; certificate validity dates in 2050 or later MUST be encoded as
 *  GeneralizedTime." */
static int SetValidity(byte* output, int daysValid)
{
#ifndef NO_ASN_TIME
    byte before[MAX_DATE_SIZE];
    byte  after[MAX_DATE_SIZE];

    word32 beforeSz, afterSz, seqSz;

    time_t now;
    time_t then;
    struct tm* tmpTime;
    struct tm* expandedTime;
    struct tm localTime;

#if defined(NEED_TMP_TIME)
    /* for use with gmtime_r */
    struct tm tmpTimeStorage;
    tmpTime = &tmpTimeStorage;
#else
    tmpTime = NULL;
#endif
    (void)tmpTime;

    now = wc_Time(0);

    /* before now */
    before[0] = ASN_GENERALIZED_TIME;
    beforeSz = SetLength(ASN_GEN_TIME_SZ, before + 1) + 1;  /* gen tag */

    /* subtract 1 day of seconds for more compliance */
    then = now - 86400;
    expandedTime = XGMTIME(&then, tmpTime);
    if (ValidateGmtime(expandedTime)) {
        WOLFSSL_MSG("XGMTIME failed");
        return 0;   /* error */
    }
    localTime = *expandedTime;

    /* adjust */
    localTime.tm_year += 1900;
    localTime.tm_mon +=    1;

    SetTime(&localTime, before + beforeSz);
    beforeSz += ASN_GEN_TIME_SZ;

    after[0] = ASN_GENERALIZED_TIME;
    afterSz  = SetLength(ASN_GEN_TIME_SZ, after + 1) + 1;  /* gen tag */

    /* add daysValid of seconds */
    then = now + (daysValid * (time_t)86400);
    expandedTime = XGMTIME(&then, tmpTime);
    if (ValidateGmtime(expandedTime)) {
        WOLFSSL_MSG("XGMTIME failed");
        return 0;   /* error */
    }
    localTime = *expandedTime;

    /* adjust */
    localTime.tm_year += 1900;
    localTime.tm_mon  +=    1;

    SetTime(&localTime, after + afterSz);
    afterSz += ASN_GEN_TIME_SZ;

    /* headers and output */
    seqSz = SetSequence(beforeSz + afterSz, output);
    XMEMCPY(output + seqSz, before, beforeSz);
    XMEMCPY(output + seqSz + beforeSz, after, afterSz);

    return (int)(seqSz + beforeSz + afterSz);
#else
    (void)output;
    (void)daysValid;
    return NOT_COMPILED_IN;
#endif
}

/* encode info from cert into DER encoded format */
static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey,
                      WC_RNG* rng, DsaKey* dsaKey, ed25519_key* ed25519Key,
                      ed448_key* ed448Key, falcon_key* falconKey,
                      wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey)
{
    int ret;

    if (cert == NULL || der == NULL || rng == NULL)
        return BAD_FUNC_ARG;

    /* make sure at least one key type is provided */
    if (rsaKey == NULL && eccKey == NULL && ed25519Key == NULL &&
        dsaKey == NULL && ed448Key == NULL && falconKey == NULL &&
        mldsaKey == NULL && slhDsaKey == NULL) {
        return PUBLIC_KEY_E;
    }

    /* init */
    XMEMSET(der, 0, sizeof(DerCert));

    /* version */
    der->versionSz = SetMyVersion((word32)cert->version, der->version, TRUE);

    /* serial number (must be positive) */
    if (cert->serialSz == 0) {
        /* generate random serial */
        cert->serialSz = CTC_GEN_SERIAL_SZ;
        ret = wc_RNG_GenerateBlock(rng, cert->serial, (word32)cert->serialSz);
        if (ret != 0)
            return ret;
        /* Clear the top bit to avoid a negative value */
        cert->serial[0] &= 0x7f;
    }
    der->serialSz = SetSerialNumber(cert->serial, (word32)cert->serialSz,
                                    der->serial, sizeof(der->serial),
                                    CTC_SERIAL_SIZE);
    if (der->serialSz < 0)
        return der->serialSz;

    /* signature algo */
    der->sigAlgoSz = (int)SetAlgoID(cert->sigType, der->sigAlgo, oidSigType, 0);
    if (der->sigAlgoSz <= 0)
        return ALGO_ID_E;

    /* public key */
#ifndef NO_RSA
    if (cert->keyType == RSA_KEY) {
        if (rsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = SetRsaPublicKey(der->publicKey, rsaKey,
                                           sizeof(der->publicKey), 1);
    }
#endif

#ifdef HAVE_ECC
    if (cert->keyType == ECC_KEY) {
        if (eccKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = SetEccPublicKey(der->publicKey, eccKey,
                                           sizeof(der->publicKey), 1, 0);
    }
#endif

#if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
    if (cert->keyType == DSA_KEY) {
        if (dsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_SetDsaPublicKey(der->publicKey, dsaKey,
                                              sizeof(der->publicKey), 1);
    }
#endif

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
    if (cert->keyType == ED25519_KEY) {
        if (ed25519Key == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_Ed25519PublicKeyToDer(ed25519Key, der->publicKey,
            (word32)sizeof(der->publicKey), 1);
    }
#endif

#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)
    if (cert->keyType == ED448_KEY) {
        if (ed448Key == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_Ed448PublicKeyToDer(ed448Key, der->publicKey,
            (word32)sizeof(der->publicKey), 1);
    }
#endif

#if defined(HAVE_FALCON)
    if ((cert->keyType == FALCON_LEVEL1_KEY) ||
        (cert->keyType == FALCON_LEVEL5_KEY)) {
        if (falconKey == NULL)
            return PUBLIC_KEY_E;

        der->publicKeySz =
            wc_Falcon_PublicKeyToDer(falconKey, der->publicKey,
                                     (word32)sizeof(der->publicKey), 1);
    }
#endif /* HAVE_FALCON */
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1)
    if ((cert->keyType == ML_DSA_44_KEY) ||
        (cert->keyType == ML_DSA_65_KEY) ||
        (cert->keyType == ML_DSA_87_KEY)
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
     || (cert->keyType == DILITHIUM_LEVEL2_KEY)
     || (cert->keyType == DILITHIUM_LEVEL3_KEY)
     || (cert->keyType == DILITHIUM_LEVEL5_KEY)
    #endif
        ) {
        if (mldsaKey == NULL)
            return PUBLIC_KEY_E;

        der->publicKeySz =
            wc_MlDsaKey_PublicKeyToDer(mldsaKey, der->publicKey,
                                     (word32)sizeof(der->publicKey), 1);
    }
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
    if ((cert->keyType == SLH_DSA_SHAKE_128F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_192F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_256F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_128S_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_192S_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_256S_KEY)
    #ifdef WOLFSSL_SLHDSA_SHA2
     || (cert->keyType == SLH_DSA_SHA2_128F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_192F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_256F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_128S_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_192S_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_256S_KEY)
    #endif
        ) {
        if (slhDsaKey == NULL)
            return PUBLIC_KEY_E;

        der->publicKeySz =
            wc_SlhDsaKey_PublicKeyToDer(slhDsaKey, der->publicKey,
                                      (word32)sizeof(der->publicKey), 1);
    }
#endif /* WOLFSSL_HAVE_SLHDSA */

    if (der->publicKeySz <= 0)
        return PUBLIC_KEY_E;

    der->validitySz = 0;
    /* copy date validity if already set in cert struct */
    if (cert->beforeDateSz && cert->afterDateSz) {
        der->validitySz = CopyValidity(der->validity, cert);
        if (der->validitySz <= 0)
            return DATE_E;
    }

    /* set date validity using daysValid if not set already */
    if (der->validitySz == 0) {
        der->validitySz = SetValidity(der->validity, cert->daysValid);
        if (der->validitySz <= 0)
            return DATE_E;
    }

    /* subject name */
#if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
    if (XSTRLEN((const char*)cert->sbjRaw) > 0) {
        /* Use the raw subject */
        word32 idx;

        der->subjectSz = (int)min((word32)sizeof(der->subject),
                                  (word32)XSTRLEN((const char*)cert->sbjRaw));
        /* header */
        idx = SetSequence((word32)der->subjectSz, der->subject);
        if ((word32)der->subjectSz + idx > (word32)sizeof(der->subject)) {
            return SUBJECT_E;
        }

        XMEMCPY((char*)der->subject + idx, (const char*)cert->sbjRaw,
                (size_t)der->subjectSz);
        der->subjectSz += (int)idx;
    }
    else
#endif
    {
        /* Use the name structure */
        der->subjectSz = SetNameEx(der->subject, sizeof(der->subject),
                &cert->subject, cert->heap);
    }
    if (der->subjectSz <= 0)
        return SUBJECT_E;

    /* issuer name */
#if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
    if (XSTRLEN((const char*)cert->issRaw) > 0) {
        /* Use the raw issuer */
        word32 idx;

        der->issuerSz = (int)min((word32)sizeof(der->issuer),
                                 (word32)XSTRLEN((const char*)cert->issRaw));

        /* header */
        idx = SetSequence((word32)der->issuerSz, der->issuer);
        if ((word32)der->issuerSz + idx > (word32)sizeof(der->issuer)) {
            return ISSUER_E;
        }

        XMEMCPY((char*)der->issuer + idx, (const char*)cert->issRaw,
                (size_t)der->issuerSz);
        der->issuerSz += (int)idx;
    }
    else
#endif
    {
        /* Use the name structure */
        der->issuerSz = SetNameEx(der->issuer, sizeof(der->issuer),
                cert->selfSigned ? &cert->subject : &cert->issuer, cert->heap);
    }
    if (der->issuerSz <= 0)
        return ISSUER_E;

    /* set the extensions */
    der->extensionsSz = 0;

    /* RFC 5280 : 4.2.1.9. Basic Constraints
     * The pathLenConstraint field is meaningful only if the CA boolean is
     * asserted and the key usage extension, if present, asserts the
     * keyCertSign bit */
    /* Set CA and path length */
    if ((cert->isCA) && (cert->pathLenSet)
#ifdef WOLFSSL_CERT_EXT
        && ((cert->keyUsage & KEYUSE_KEY_CERT_SIGN) || (!cert->keyUsage))
#endif
        ) {
        der->caSz = SetCaWithPathLen(der->ca, sizeof(der->ca), cert->pathLen);
        if (der->caSz <= 0)
            return CA_TRUE_E;

        der->extensionsSz += der->caSz;
    }
#ifdef WOLFSSL_ALLOW_ENCODING_CA_FALSE
    /* Set CA */
    else if (cert->isCaSet) {
        der->caSz = SetCaEx(der->ca, sizeof(der->ca), cert->isCA);
        if (der->caSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->caSz;
    }
#endif
    /* Set CA true */
    else if (cert->isCA) {
        der->caSz = SetCa(der->ca, sizeof(der->ca));
        if (der->caSz <= 0)
            return CA_TRUE_E;

        der->extensionsSz += der->caSz;
    }
    /* Set Basic Constraint */
    else if (cert->basicConstSet) {
        der->caSz = SetBC(der->ca, sizeof(der->ca));
        if (der->caSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->caSz;
    }
    else
        der->caSz = 0;

#ifdef WOLFSSL_ALT_NAMES
    /* Alternative Name */
    if (cert->altNamesSz) {
        der->altNamesSz = SetAltNames(der->altNames, sizeof(der->altNames),
                                      cert->altNames, (word32)cert->altNamesSz,
                                      cert->altNamesCrit);
        if (der->altNamesSz <= 0)
            return ALT_NAME_E;

        der->extensionsSz += der->altNamesSz;
    }
    else
        der->altNamesSz = 0;
#endif

#ifdef WOLFSSL_CERT_EXT
    /* SKID */
    if (cert->skidSz) {
        /* check the provided SKID size */
        if (cert->skidSz > (int)min(CTC_MAX_SKID_SIZE, sizeof(der->skid)))
            return SKID_E;

        /* Note: different skid buffers sizes for der (MAX_KID_SZ) and
            cert (CTC_MAX_SKID_SIZE). */
        der->skidSz = SetSKID(der->skid, sizeof(der->skid),
                              cert->skid, (word32)cert->skidSz);
        if (der->skidSz <= 0)
            return SKID_E;

        der->extensionsSz += der->skidSz;
    }
    else
        der->skidSz = 0;

    /* AKID */
    if (cert->akidSz) {
        /* check the provided AKID size */
        if ((
#ifdef WOLFSSL_AKID_NAME
             !cert->rawAkid &&
#endif
              cert->akidSz > (int)min(CTC_MAX_AKID_SIZE, sizeof(der->akid)))
#ifdef WOLFSSL_AKID_NAME
          || (cert->rawAkid && cert->akidSz > (int)sizeof(der->akid))
#endif
             )
            return AKID_E;

        der->akidSz = SetAKID(der->akid, sizeof(der->akid), cert->akid,
                              (word32)cert->akidSz,
#ifdef WOLFSSL_AKID_NAME
                              cert->rawAkid
#else
                              0
#endif
                              );
        if (der->akidSz <= 0)
            return AKID_E;

        der->extensionsSz += der->akidSz;
    }
    else
        der->akidSz = 0;

    /* Key Usage */
    if (cert->keyUsage != 0){
        der->keyUsageSz = SetKeyUsage(der->keyUsage, sizeof(der->keyUsage),
                                      cert->keyUsage);
        if (der->keyUsageSz <= 0)
            return KEYUSAGE_E;

        der->extensionsSz += der->keyUsageSz;
    }
    else
        der->keyUsageSz = 0;

    /* Extended Key Usage */
    if (cert->extKeyUsage != 0){
        der->extKeyUsageSz = SetExtKeyUsage(cert, der->extKeyUsage,
                                sizeof(der->extKeyUsage), cert->extKeyUsage);
        if (der->extKeyUsageSz <= 0)
            return EXTKEYUSAGE_E;

        der->extensionsSz += der->extKeyUsageSz;
    }
    else
        der->extKeyUsageSz = 0;

#ifndef IGNORE_NETSCAPE_CERT_TYPE
    /* Netscape Certificate Type */
    if (cert->nsCertType != 0) {
        der->nsCertTypeSz = SetNsCertType(cert, der->nsCertType,
                                sizeof(der->nsCertType), cert->nsCertType);
        if (der->nsCertTypeSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->nsCertTypeSz;
    }
    else
        der->nsCertTypeSz = 0;
#endif

    if (cert->crlInfoSz > 0) {
        der->crlInfoSz = SetCRLInfo(cert, der->crlInfo, sizeof(der->crlInfo),
                                cert->crlInfo, cert->crlInfoSz);
        if (der->crlInfoSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->crlInfoSz;
    }
    else
        der->crlInfoSz = 0;

    /* Certificate Policies */
    if (cert->certPoliciesNb != 0) {
        der->certPoliciesSz = SetCertificatePolicies(der->certPolicies,
                                                     sizeof(der->certPolicies),
                                                     cert->certPolicies,
                                                     cert->certPoliciesNb,
                                                     cert->heap);
        if (der->certPoliciesSz <= 0)
            return CERTPOLICIES_E;

        der->extensionsSz += der->certPoliciesSz;
    }
    else
        der->certPoliciesSz = 0;
#endif /* WOLFSSL_CERT_EXT */

#ifdef WOLFSSL_ACME_OID
    /* RFC 8737 id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert).
     * Always critical=TRUE. */
    if (cert->acmeIdentifierSz == WC_SHA256_DIGEST_SIZE) {
        der->acmeIdSz = SetAcmeIdentifier(der->acmeId, sizeof(der->acmeId),
                                          cert->acmeIdentifier,
                                          (word32)cert->acmeIdentifierSz);
        if (der->acmeIdSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->acmeIdSz;
    }
    else
        der->acmeIdSz = 0;
#endif

    /* put extensions */
    if (der->extensionsSz > 0) {

        /* put the start of extensions sequence (ID, Size) */
        der->extensionsSz = SetExtensionsHeader(der->extensions,
                                                sizeof(der->extensions),
                                                (word32)der->extensionsSz);
        if (der->extensionsSz <= 0)
            return EXTENSIONS_E;

        /* put CA */
        if (der->caSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->ca, der->caSz);
            if (ret == 0)
                return EXTENSIONS_E;
        }

#ifdef WOLFSSL_ALT_NAMES
        /* put Alternative Names */
        if (der->altNamesSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->altNames, der->altNamesSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
#endif

#ifdef WOLFSSL_CERT_EXT
        /* put SKID */
        if (der->skidSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->skid, der->skidSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put AKID */
        if (der->akidSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->akid, der->akidSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put CRL Distribution Points */
        if (der->crlInfoSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->crlInfo, der->crlInfoSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put KeyUsage */
        if (der->keyUsageSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->keyUsage, der->keyUsageSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put ExtendedKeyUsage */
        if (der->extKeyUsageSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->extKeyUsage, der->extKeyUsageSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put Netscape Cert Type */
#ifndef IGNORE_NETSCAPE_CERT_TYPE
        if (der->nsCertTypeSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->nsCertType, der->nsCertTypeSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
#endif

        /* put Certificate Policies */
        if (der->certPoliciesSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->certPolicies, der->certPoliciesSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
#endif /* WOLFSSL_CERT_EXT */

#ifdef WOLFSSL_ACME_OID
        /* put ACME Identifier */
        if (der->acmeIdSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->acmeId, der->acmeIdSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
#endif
    }

    der->total = der->versionSz + der->serialSz + der->sigAlgoSz +
        der->publicKeySz + der->validitySz + der->subjectSz + der->issuerSz +
        der->extensionsSz;

    return 0;
}


/* write DER encoded cert to buffer, size already checked */
static int WriteCertBody(DerCert* der, byte* buf)
{
    word32 idx;

    /* signed part header */
    idx = SetSequence((word32)der->total, buf);
    /* version */
    XMEMCPY(buf + idx, der->version, (size_t)der->versionSz);
    idx += (word32)der->versionSz;
    /* serial */
    XMEMCPY(buf + idx, der->serial, (size_t)der->serialSz);
    idx += (word32)der->serialSz;
    /* sig algo */
    XMEMCPY(buf + idx, der->sigAlgo, (size_t)der->sigAlgoSz);
    idx += (word32)der->sigAlgoSz;
    /* issuer */
    XMEMCPY(buf + idx, der->issuer, (size_t)der->issuerSz);
    idx += (word32)der->issuerSz;
    /* validity */
    XMEMCPY(buf + idx, der->validity, (size_t)der->validitySz);
    idx += (word32)der->validitySz;
    /* subject */
    XMEMCPY(buf + idx, der->subject, (size_t)der->subjectSz);
    idx += (word32)der->subjectSz;
    /* public key */
    XMEMCPY(buf + idx, der->publicKey, (size_t)der->publicKeySz);
    idx += (word32)der->publicKeySz;
    if (der->extensionsSz) {
        /* extensions */
        XMEMCPY(buf + idx, der->extensions,
                min((word32)der->extensionsSz,
                    (word32)sizeof(der->extensions)));
        idx += (word32)der->extensionsSz;
    }

    return (int)idx;
}

int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz,
                        int sigAlgoType)
{
    byte seq[MAX_SEQ_SZ];
    word32 idx, seqSz;

    if ((bodySz < 0) || (sigSz < 0))
        return BUFFER_E;

    idx = (word32)bodySz;

    /* algo */
    idx += SetAlgoID(sigAlgoType, buf ? buf + idx : NULL, oidSigType, 0);
    /* bit string */
    idx += SetBitString((word32)sigSz, 0, buf ? buf + idx : NULL);
    /* signature */
    if (buf)
        XMEMCPY(buf + idx, sig, (size_t)sigSz);
    idx += (word32)sigSz;

    /* make room for overall header */
    seqSz = SetSequence(idx, seq);
    if (buf) {
        XMEMMOVE(buf + seqSz, buf, idx);
        XMEMCPY(buf, seq, seqSz);
    }

    return (int)(idx + seqSz);
}

static int MakeAnyCert(Cert* cert, byte* derBuffer, word32 derSz,
                       RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng,
                       DsaKey* dsaKey, ed25519_key* ed25519Key,
                       ed448_key* ed448Key, falcon_key* falconKey,
                       wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey)
{
    int ret;
    WC_DECLARE_VAR(der, DerCert, 1, 0);

    if (derBuffer == NULL)
        return BAD_FUNC_ARG;

    if (eccKey)
        cert->keyType = ECC_KEY;
    else if (rsaKey)
        cert->keyType = RSA_KEY;
    else if (dsaKey)
        cert->keyType = DSA_KEY;
    else if (ed25519Key)
        cert->keyType = ED25519_KEY;
    else if (ed448Key)
        cert->keyType = ED448_KEY;
#ifdef HAVE_FALCON
    else if ((falconKey != NULL) && (falconKey->level == 1))
        cert->keyType = FALCON_LEVEL1_KEY;
    else if ((falconKey != NULL) && (falconKey->level == 5))
        cert->keyType = FALCON_LEVEL5_KEY;
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_44_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL2_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_65_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL3_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_87_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL5_KEY;
    }
    #endif
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_44)) {
        cert->keyType = ML_DSA_44_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_65)) {
        cert->keyType = ML_DSA_65_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_87)) {
        cert->keyType = ML_DSA_87_KEY;
    }
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
    else if ((slhDsaKey != NULL) && (slhDsaKey->params != NULL) &&
             (SlhDsaParamToKeyType(slhDsaKey->params->param) != 0)) {
        cert->keyType = SlhDsaParamToKeyType(slhDsaKey->params->param);
    }
#endif /* WOLFSSL_HAVE_SLHDSA */
    else
        return BAD_FUNC_ARG;

    WC_ALLOC_VAR_EX(der, DerCert, 1, cert->heap, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    ret = EncodeCert(cert, der, rsaKey, eccKey, rng, dsaKey, ed25519Key,
                     ed448Key, falconKey, mldsaKey, slhDsaKey);
    if (ret == 0) {
        if (der->total + MAX_SEQ_SZ * 2 > (int)derSz)
            ret = BUFFER_E;
        else
            ret = cert->bodySz = WriteCertBody(der, derBuffer);
    }

    WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);

    return ret;
}

#ifdef WOLFSSL_CERT_REQ
/* return size of data set on success
 * if getting size only then attr and oid should be NULL
 */
static word32 SetReqAttribSingle(byte* output, word32* idx, char* attr,
        word32 attrSz, const byte* oid, word32 oidSz, byte printable,
        word32 extSz)
{
    word32 totalSz = 0;
    word32 seqSz = 0;
    word32 setSz = 0;
    word32 strSz = 0;
    byte seq[MAX_SEQ_SZ];
    byte set[MAX_SET_SZ];
    byte str[MAX_PRSTR_SZ];

    totalSz = (word32)SetObjectId((int)oidSz, NULL);
    totalSz += oidSz;
    if (extSz > 0) {
        totalSz += setSz = SetSet(extSz, set);
        totalSz += seqSz = SetSequence(totalSz + extSz, seq);
        totalSz += extSz;
    }
    else {
        if (printable) {
            strSz = SetPrintableString(attrSz, str);
            totalSz += strSz;
        }
        else {
            totalSz += strSz = SetUTF8String(attrSz, str);
        }
        totalSz += setSz = SetSet(strSz + attrSz, set);
        totalSz += seqSz = SetSequence(totalSz + attrSz, seq);
        totalSz += attrSz;
    }

    if (oid) {
        XMEMCPY(&output[*idx], seq, seqSz);
        *idx += seqSz;
        *idx += (word32)SetObjectId((int)oidSz, output + *idx);
        XMEMCPY(&output[*idx], oid, oidSz);
        *idx += oidSz;
        XMEMCPY(&output[*idx], set, setSz);
        *idx += setSz;
        if (strSz > 0) {
            XMEMCPY(&output[*idx], str, strSz);
            *idx += strSz;
            if (attrSz > 0) {
                XMEMCPY(&output[*idx], attr, attrSz);
                *idx += attrSz;
            }
        }
    }
    return totalSz;
}



static int SetReqAttrib(byte* output, Cert* cert, word32 extSz)
{
    word32 sz      = 0; /* overall size */
    word32 setSz   = 0;

    output[0] = ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED;
    sz++;

    if (cert->challengePw[0]) {
        setSz += SetReqAttribSingle(output, &sz, NULL,
                (word32)XSTRLEN(cert->challengePw), NULL,
                sizeof(attrChallengePasswordOid),
                (byte)cert->challengePwPrintableString, 0);
    }

    if (cert->unstructuredName[0]) {
        setSz += SetReqAttribSingle(output, &sz, NULL,
                (word32)XSTRLEN(cert->unstructuredName), NULL,
                sizeof(attrUnstructuredNameOid), 1, 0);
    }

    if (extSz) {
        setSz += SetReqAttribSingle(output, &sz, NULL, 0, NULL,
                sizeof(attrExtensionRequestOid), 1, extSz);
    }

    /* Put the pieces together. */
    sz += SetLength(setSz, &output[sz]);
    if (sz + setSz - extSz > MAX_ATTRIB_SZ) {
        WOLFSSL_MSG("Attribute Buffer is not big enough!");
        return REQ_ATTRIBUTE_E;
    }

    if (cert->challengePw[0]) {
        SetReqAttribSingle(output, &sz, cert->challengePw,
                (word32)XSTRLEN(cert->challengePw),
                &attrChallengePasswordOid[0],
                sizeof(attrChallengePasswordOid),
                (byte)cert->challengePwPrintableString, 0);
    }

    if (cert->unstructuredName[0]) {
        SetReqAttribSingle(output, &sz, cert->unstructuredName,
                (word32)XSTRLEN(cert->unstructuredName),
                &attrUnstructuredNameOid[0],
                sizeof(attrUnstructuredNameOid), 1, 0);
    }

    if (extSz) {
        SetReqAttribSingle(output, &sz, NULL, 0, &attrExtensionRequestOid[0],
                sizeof(attrExtensionRequestOid), 1, extSz);
        /* The actual extension data will be tacked onto the output later. */
    }

    return (int)sz;
}

#ifdef WOLFSSL_CUSTOM_OID
/* encode a custom oid and value */
static int SetCustomObjectId(Cert* cert, byte* output, word32 outSz,
    CertOidField* custom)
{
    int idx = 0, cust_lenSz, cust_oidSz;

    if (cert == NULL || output == NULL || custom == NULL) {
        return BAD_FUNC_ARG;
    }
    if (custom->oid == NULL || custom->oidSz <= 0) {
        return 0; /* none set */
    }

    /* Octet String header */
    cust_lenSz = SetOctetString(custom->valSz, NULL);
    cust_oidSz = SetObjectId(custom->oidSz, NULL);

    /* check for output buffer room */
    if ((word32)(custom->valSz + custom->oidSz + cust_lenSz + cust_oidSz) >
                                                                        outSz) {
        return BUFFER_E;
    }

    /* put sequence with total */
    idx = SetSequence(custom->valSz + custom->oidSz + cust_lenSz + cust_oidSz,
                      output);

    /* put oid header */
    idx += SetObjectId(custom->oidSz, output+idx);
    XMEMCPY(output+idx, custom->oid, custom->oidSz);
    idx += custom->oidSz;

    /* put value */
    idx += SetOctetString(custom->valSz, output+idx);
    XMEMCPY(output+idx, custom->val, custom->valSz);
    idx += custom->valSz;

    return idx;
}
#endif /* WOLFSSL_CUSTOM_OID */


/* encode info from cert into DER encoded format */
static int EncodeCertReq(Cert* cert, DerCert* der, RsaKey* rsaKey,
                         DsaKey* dsaKey, ecc_key* eccKey,
                         ed25519_key* ed25519Key, ed448_key* ed448Key,
                         falcon_key* falconKey, wc_MlDsaKey* mldsaKey,
                         SlhDsaKey* slhDsaKey)
{
    int ret;

    (void)eccKey;
    (void)ed25519Key;
    (void)ed448Key;
    (void)falconKey;
    (void)mldsaKey;
    (void)slhDsaKey;

    if (cert == NULL || der == NULL)
        return BAD_FUNC_ARG;

    if (rsaKey == NULL && eccKey == NULL && ed25519Key == NULL &&
        dsaKey == NULL && ed448Key == NULL && falconKey == NULL &&
        mldsaKey == NULL && slhDsaKey == NULL) {
        return PUBLIC_KEY_E;
    }

    /* init */
    XMEMSET(der, 0, sizeof(DerCert));

    /* version */
    der->versionSz = SetMyVersion((word32)cert->version, der->version, FALSE);

    /* subject name */
#if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
    if (XSTRLEN((const char*)cert->sbjRaw) > 0) {
        /* Use the raw subject */
        int idx;

        der->subjectSz = (int)min(sizeof(der->subject),
                (word32)XSTRLEN((const char*)cert->sbjRaw));
        /* header */
        idx = (int)SetSequence((word32)der->subjectSz, der->subject);
        if (der->subjectSz + idx > (int)sizeof(der->subject)) {
            return SUBJECT_E;
        }

        XMEMCPY((char*)der->subject + idx, (const char*)cert->sbjRaw,
                (size_t)der->subjectSz);
        der->subjectSz += idx;
    }
    else
#endif
    {
        der->subjectSz = SetNameEx(der->subject, sizeof(der->subject),
                &cert->subject, cert->heap);
    }
    if (der->subjectSz <= 0)
        return SUBJECT_E;

    /* public key */
#ifndef NO_RSA
    if (cert->keyType == RSA_KEY) {
        if (rsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = SetRsaPublicKey(der->publicKey, rsaKey,
                                           sizeof(der->publicKey), 1);
    }
#endif

#if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
    if (cert->keyType == DSA_KEY) {
        if (dsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_SetDsaPublicKey(der->publicKey, dsaKey,
                                           sizeof(der->publicKey), 1);
    }
#endif

#ifdef HAVE_ECC
    if (cert->keyType == ECC_KEY) {
        if (eccKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = SetEccPublicKey(der->publicKey, eccKey,
                                           sizeof(der->publicKey), 1, 0);
    }
#endif

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
    if (cert->keyType == ED25519_KEY) {
        if (ed25519Key == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_Ed25519PublicKeyToDer(ed25519Key, der->publicKey,
            (word32)sizeof(der->publicKey), 1);
    }
#endif

#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)
    if (cert->keyType == ED448_KEY) {
        if (ed448Key == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_Ed448PublicKeyToDer(ed448Key, der->publicKey,
            (word32)sizeof(der->publicKey), 1);
    }
#endif
#if defined(HAVE_FALCON)
    if ((cert->keyType == FALCON_LEVEL1_KEY) ||
        (cert->keyType == FALCON_LEVEL5_KEY)) {
        if (falconKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_Falcon_PublicKeyToDer(falconKey,
            der->publicKey, (word32)sizeof(der->publicKey), 1);
    }
#endif
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1)
    if ((cert->keyType == ML_DSA_44_KEY) ||
        (cert->keyType == ML_DSA_65_KEY) ||
        (cert->keyType == ML_DSA_87_KEY)
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
     || (cert->keyType == DILITHIUM_LEVEL2_KEY)
     || (cert->keyType == DILITHIUM_LEVEL3_KEY)
     || (cert->keyType == DILITHIUM_LEVEL5_KEY)
   #endif
        ) {
        if (mldsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_MlDsaKey_PublicKeyToDer(mldsaKey,
            der->publicKey, (word32)sizeof(der->publicKey), 1);
    }
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
    if ((cert->keyType == SLH_DSA_SHAKE_128F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_192F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_256F_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_128S_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_192S_KEY) ||
        (cert->keyType == SLH_DSA_SHAKE_256S_KEY)
    #ifdef WOLFSSL_SLHDSA_SHA2
     || (cert->keyType == SLH_DSA_SHA2_128F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_192F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_256F_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_128S_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_192S_KEY) ||
        (cert->keyType == SLH_DSA_SHA2_256S_KEY)
    #endif
        ) {
        if (slhDsaKey == NULL)
            return PUBLIC_KEY_E;
        der->publicKeySz = wc_SlhDsaKey_PublicKeyToDer(slhDsaKey,
            der->publicKey, (word32)sizeof(der->publicKey), 1);
    }
#endif

    if (der->publicKeySz <= 0)
        return PUBLIC_KEY_E;

    /* set the extensions */
    der->extensionsSz = 0;

    /* RFC 5280 : 4.2.1.9. Basic Constraints
     * The pathLenConstraint field is meaningful only if the CA boolean is
     * asserted and the key usage extension, if present, asserts the
     * keyCertSign bit */
    /* Set CA and path length */
    if ((cert->isCA) && (cert->pathLenSet)
#ifdef WOLFSSL_CERT_EXT
        && ((cert->keyUsage & KEYUSE_KEY_CERT_SIGN) || (!cert->keyUsage))
#endif
        ) {
        der->caSz = SetCaWithPathLen(der->ca, sizeof(der->ca), cert->pathLen);
        if (der->caSz <= 0)
            return CA_TRUE_E;

        der->extensionsSz += der->caSz;
    }
#ifdef WOLFSSL_ALLOW_ENCODING_CA_FALSE
    /* Set CA */
    else if (cert->isCaSet) {
        der->caSz = SetCaEx(der->ca, sizeof(der->ca), cert->isCA);
        if (der->caSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->caSz;
    }
#endif
    /* Set CA true */
    else if (cert->isCA) {
        der->caSz = SetCa(der->ca, sizeof(der->ca));
        if (der->caSz <= 0)
            return CA_TRUE_E;

        der->extensionsSz += der->caSz;
    }
    /* Set Basic Constraint */
    else if (cert->basicConstSet) {
        der->caSz = SetBC(der->ca, sizeof(der->ca));
        if (der->caSz <= 0)
            return EXTENSIONS_E;

        der->extensionsSz += der->caSz;
    }
    else
        der->caSz = 0;

#ifdef WOLFSSL_ALT_NAMES
    /* Alternative Name */
    if (cert->altNamesSz) {
        der->altNamesSz = SetAltNames(der->altNames, sizeof(der->altNames),
                                      cert->altNames, (word32)cert->altNamesSz,
                                      cert->altNamesCrit);
        if (der->altNamesSz <= 0)
            return ALT_NAME_E;

        der->extensionsSz += der->altNamesSz;
    }
    else
        der->altNamesSz = 0;
#endif

#ifdef WOLFSSL_CERT_EXT
    /* SKID */
    if (cert->skidSz) {
        /* check the provided SKID size */
        if (cert->skidSz > (int)min(CTC_MAX_SKID_SIZE, sizeof(der->skid)))
            return SKID_E;

        der->skidSz = SetSKID(der->skid, sizeof(der->skid),
                              cert->skid, (word32)cert->skidSz);
        if (der->skidSz <= 0)
            return SKID_E;

        der->extensionsSz += der->skidSz;
    }
    else
        der->skidSz = 0;

    /* Key Usage */
    if (cert->keyUsage != 0) {
        der->keyUsageSz = SetKeyUsage(der->keyUsage, sizeof(der->keyUsage),
                                      cert->keyUsage);
        if (der->keyUsageSz <= 0)
            return KEYUSAGE_E;

        der->extensionsSz += der->keyUsageSz;
    }
    else
        der->keyUsageSz = 0;

    /* Extended Key Usage */
    if (cert->extKeyUsage != 0) {
        der->extKeyUsageSz = SetExtKeyUsage(cert, der->extKeyUsage,
                                sizeof(der->extKeyUsage), cert->extKeyUsage);
        if (der->extKeyUsageSz <= 0)
            return EXTKEYUSAGE_E;

        der->extensionsSz += der->extKeyUsageSz;
    }
    else
        der->extKeyUsageSz = 0;

#endif /* WOLFSSL_CERT_EXT */

#ifdef WOLFSSL_CUSTOM_OID
    /* encode a custom oid and value */
    /* zero returns, means none set */
    ret = SetCustomObjectId(cert, der->extCustom,
        sizeof(der->extCustom), &cert->extCustom);
    if (ret < 0)
        return ret;
    der->extCustomSz = ret;
    der->extensionsSz += der->extCustomSz;
#endif

    /* put extensions */
    if (der->extensionsSz > 0) {
        /* put the start of sequence (ID, Size) */
        der->extensionsSz = (int)SetSequence((word32)der->extensionsSz,
                                             der->extensions);
        if (der->extensionsSz <= 0)
            return EXTENSIONS_E;

        /* put CA */
        if (der->caSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->ca, der->caSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

#ifdef WOLFSSL_ALT_NAMES
        /* put Alternative Names */
        if (der->altNamesSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->altNames, der->altNamesSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
#endif

#ifdef WOLFSSL_CERT_EXT
        /* put SKID */
        if (der->skidSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->skid, der->skidSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put AKID */
        if (der->akidSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->akid, der->akidSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put KeyUsage */
        if (der->keyUsageSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->keyUsage, der->keyUsageSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

        /* put ExtendedKeyUsage */
        if (der->extKeyUsageSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->extKeyUsage, der->extKeyUsageSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }

    #ifdef WOLFSSL_CUSTOM_OID
        if (der->extCustomSz) {
            ret = SetExtensions(der->extensions, sizeof(der->extensions),
                                &der->extensionsSz,
                                der->extCustom, der->extCustomSz);
            if (ret <= 0)
                return EXTENSIONS_E;
        }
    #endif
#endif /* WOLFSSL_CERT_EXT */
    }

    der->attribSz = SetReqAttrib(der->attrib, cert, (word32)der->extensionsSz);
    if (der->attribSz <= 0)
        return REQ_ATTRIBUTE_E;

    der->total = der->versionSz + der->subjectSz + der->publicKeySz +
        der->extensionsSz + der->attribSz;

    return 0;
}


/* write DER encoded cert req to buffer, size already checked */
static int WriteCertReqBody(DerCert* der, byte* buf)
{
    int idx;

    /* signed part header */
    idx = (int)SetSequence((word32)der->total, buf);
    /* version */
    if (buf)
        XMEMCPY(buf + idx, der->version, (size_t)der->versionSz);
    idx += der->versionSz;
    /* subject */
    if (buf)
        XMEMCPY(buf + idx, der->subject, (size_t)der->subjectSz);
    idx += der->subjectSz;
    /* public key */
    if (buf)
        XMEMCPY(buf + idx, der->publicKey, (size_t)der->publicKeySz);
    idx += der->publicKeySz;
    /* attributes */
    if (buf)
        XMEMCPY(buf + idx, der->attrib, (size_t)der->attribSz);
    idx += der->attribSz;
    /* extensions */
    if (der->extensionsSz) {
        if (buf)
            XMEMCPY(buf + idx, der->extensions, min((word32)der->extensionsSz,
                                               sizeof(der->extensions)));
        idx += der->extensionsSz;
    }

    return idx;
}

static int MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
                   RsaKey* rsaKey, DsaKey* dsaKey, ecc_key* eccKey,
                   ed25519_key* ed25519Key, ed448_key* ed448Key,
                   falcon_key* falconKey, wc_MlDsaKey* mldsaKey,
                   SlhDsaKey* slhDsaKey)
{
    int ret;
    WC_DECLARE_VAR(der, DerCert, 1, 0);

    if (eccKey)
        cert->keyType = ECC_KEY;
    else if (rsaKey)
        cert->keyType = RSA_KEY;
    else if (dsaKey)
        cert->keyType = DSA_KEY;
    else if (ed25519Key)
        cert->keyType = ED25519_KEY;
    else if (ed448Key)
        cert->keyType = ED448_KEY;
#ifdef HAVE_FALCON
    else if ((falconKey != NULL) && (falconKey->level == 1))
        cert->keyType = FALCON_LEVEL1_KEY;
    else if ((falconKey != NULL) && (falconKey->level == 5))
        cert->keyType = FALCON_LEVEL5_KEY;
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_44_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL2_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_65_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL3_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_87_DRAFT)) {
        cert->keyType = DILITHIUM_LEVEL5_KEY;
    }
    #endif
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_44)) {
        cert->keyType = ML_DSA_44_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_65)) {
        cert->keyType = ML_DSA_65_KEY;
    }
    else if ((mldsaKey != NULL) &&
                (mldsaKey->params->level == WC_ML_DSA_87)) {
        cert->keyType = ML_DSA_87_KEY;
    }
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
    else if ((slhDsaKey != NULL) && (slhDsaKey->params != NULL) &&
             (SlhDsaParamToKeyType(slhDsaKey->params->param) != 0)) {
        cert->keyType = SlhDsaParamToKeyType(slhDsaKey->params->param);
    }
#endif /* WOLFSSL_HAVE_SLHDSA */
    else
        return BAD_FUNC_ARG;

    WC_ALLOC_VAR_EX(der, DerCert, 1, cert->heap, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    ret = EncodeCertReq(cert, der, rsaKey, dsaKey, eccKey, ed25519Key, ed448Key,
                        falconKey, mldsaKey, slhDsaKey);

    if (ret == 0) {
        if (der->total + MAX_SEQ_SZ * 2 > (int)derSz)
            ret = BUFFER_E;
        else
            ret = cert->bodySz = WriteCertReqBody(der, derBuffer);
    }

    WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);

    return ret;
}

#endif
#endif
#endif
#if !defined(NO_DH) && (defined(WOLFSSL_QT) || defined(OPENSSL_ALL))
int StoreDHparams(byte* out, word32* outLen, mp_int* p, mp_int* g)
{
    word32 idx = 0;
    word32 total;

    WOLFSSL_ENTER("StoreDHparams");

    if (out == NULL) {
        WOLFSSL_MSG("Null buffer error");
        return BUFFER_E;
    }

    /* determine size */
    /* integer - g */
    idx = SetASNIntMP(g, -1, NULL);
    /* integer - p */
    idx += SetASNIntMP(p, -1, NULL);
    total = idx;
     /* sequence */
    idx += SetSequence(idx, NULL);

    /* make sure output fits in buffer */
    if (idx > *outLen) {
        return BUFFER_E;
    }

    /* write DH parameters */
    /* sequence - for P and G only */
    idx = SetSequence(total, out);
    /* integer - p */
    idx += SetASNIntMP(p, -1, out + idx);
    /* integer - g */
    idx += SetASNIntMP(g, -1, out + idx);
    *outLen = idx;

    return 0;
}

#endif
#if defined(HAVE_ECC) || !defined(NO_DSA)
int StoreECC_DSA_Sig(byte* out, word32* outLen, mp_int* r, mp_int* s)
{
    word32 idx = 0;
    int    rSz;                           /* encoding size */
    int    sSz;
    int    headerSz = 4;   /* 2*ASN_TAG + 2*LEN(ENUM) */

    /* If the leading bit on the INTEGER is a 1, add a leading zero */
    int rLeadingZero = mp_leading_bit(r);
    int sLeadingZero = mp_leading_bit(s);
    int rLen = mp_unsigned_bin_size(r);   /* big int size */
    int sLen = mp_unsigned_bin_size(s);

    if (*outLen < (word32)((rLen + rLeadingZero + sLen + sLeadingZero +
            headerSz + 2)))  /* SEQ_TAG + LEN(ENUM) */
        return BUFFER_E;

    idx = SetSequence((word32)(rLen + rLeadingZero + sLen + sLeadingZero +
        headerSz), out);

    /* store r */
    rSz = SetASNIntMP(r, (int)(*outLen - idx), &out[idx]);
    if (rSz < 0)
        return rSz;
    idx += (word32)rSz;

    /* store s */
    sSz = SetASNIntMP(s, (int)(*outLen - idx), &out[idx]);
    if (sSz < 0)
        return sSz;
    idx += (word32)sSz;

    *outLen = idx;

    return 0;
}

/* determine if leading bit is set */
static word32 is_leading_bit_set(const byte* input, word32 sz)
{
    byte c = 0;
    if (sz > 0)
        c = input[0];
    return (c & 0x80) != 0;
}
static word32 trim_leading_zeros(const byte** input, word32 sz)
{
    int i;
    word32 leadingZeroCount = 0;
    const byte* tmp = *input;
    for (i=0; i<(int)sz; i++) {
        if (tmp[i] != 0)
            break;
        leadingZeroCount++;
    }
    /* catch all zero case */
    if (sz > 0 && leadingZeroCount == sz) {
        leadingZeroCount--;
    }
    *input += leadingZeroCount;
    sz -= leadingZeroCount;
    return sz;
}

int StoreECC_DSA_Sig_Bin(byte* out, word32* outLen, const byte* r, word32 rLen,
    const byte* s, word32 sLen)
{
    int ret;
    word32 idx;
    word32 headerSz = 4;   /* 2*ASN_TAG + 2*LEN(ENUM) */
    word32 rAddLeadZero, sAddLeadZero;

    if ((out == NULL) || (outLen == NULL) || (r == NULL) || (s == NULL))
        return BAD_FUNC_ARG;

    /* Trim leading zeros */
    rLen = trim_leading_zeros(&r, rLen);
    sLen = trim_leading_zeros(&s, sLen);
    /* If the leading bit on the INTEGER is a 1, add a leading zero */
    /* Add leading zero if MSB is set */
    rAddLeadZero = is_leading_bit_set(r, rLen);
    sAddLeadZero = is_leading_bit_set(s, sLen);

    if (*outLen < (rLen + rAddLeadZero + sLen + sAddLeadZero +
                   headerSz + 2))  /* SEQ_TAG + LEN(ENUM) */
        return BUFFER_E;

    idx = SetSequence(rLen+rAddLeadZero + sLen+sAddLeadZero + headerSz, out);

    /* store r */
    ret = SetASNInt((int)rLen, (byte)(rAddLeadZero ? 0x80U : 0x00U), &out[idx]);
    if (ret < 0)
        return ret;
    idx += (word32)ret;
    XMEMCPY(&out[idx], r, rLen);
    idx += rLen;

    /* store s */
    ret = SetASNInt((int)sLen, (byte)(sAddLeadZero ? 0x80U : 0x00U), &out[idx]);
    if (ret < 0)
        return ret;
    idx += (word32)ret;
    XMEMCPY(&out[idx], s, sLen);
    idx += sLen;

    *outLen = idx;

    return 0;
}

int DecodeECC_DSA_Sig_Bin(const byte* sig, word32 sigLen, byte* r, word32* rLen,
    byte* s, word32* sLen)
{
    int    ret;
    word32 idx = 0;
    int    len = 0;

    if (GetSequence(sig, &idx, &len, sigLen) < 0) {
        return ASN_ECC_KEY_E;
    }

#ifndef NO_STRICT_ECDSA_LEN
    /* enable strict length checking for signature */
    if (sigLen != idx + (word32)len) {
        return ASN_ECC_KEY_E;
    }
#else
    /* allow extra signature bytes at end */
    if ((word32)len > (sigLen - idx)) {
        return ASN_ECC_KEY_E;
    }
#endif

    ret = GetASNInt(sig, &idx, &len, sigLen);
    if (ret != 0)
        return ret;
    if (rLen) {
        if (*rLen >= (word32)len)
            *rLen = (word32)len;
        else {
            /* Buffer too small to hold r value */
            return BUFFER_E;
        }
    }
    if (r)
        XMEMCPY(r, (byte*)sig + idx, (size_t)len);
    idx += (word32)len;

    ret = GetASNInt(sig, &idx, &len, sigLen);
    if (ret != 0)
        return ret;
    if (sLen) {
        if (*sLen >= (word32)len)
            *sLen = (word32)len;
        else {
            /* Buffer too small to hold s value */
            return BUFFER_E;
        }
    }
    if (s)
        XMEMCPY(s, (byte*)sig + idx, (size_t)len);

#ifndef NO_STRICT_ECDSA_LEN
    /* sanity check that the index has been advanced all the way to the end of
     * the buffer */
    if (idx + (word32)len != sigLen) {
        ret = ASN_ECC_KEY_E;
    }
#endif

    return ret;
}

int DecodeECC_DSA_Sig_Ex(const byte* sig, word32 sigLen, mp_int* r, mp_int* s,
    int init)
{
    word32 idx = 0;
    int    len = 0;

    if (GetSequence(sig, &idx, &len, sigLen) < 0) {
        return ASN_ECC_KEY_E;
    }

#ifndef NO_STRICT_ECDSA_LEN
    /* enable strict length checking for signature */
    if (sigLen != idx + (word32)len) {
        return ASN_ECC_KEY_E;
    }
#else
    /* allow extra signature bytes at end */
    if ((word32)len > (sigLen - idx)) {
        return ASN_ECC_KEY_E;
    }
#endif

    if (GetIntPositive(r, sig, &idx, sigLen, init) < 0) {
        return ASN_ECC_KEY_E;
    }

    if (GetIntPositive(s, sig, &idx, sigLen, init) < 0) {
        mp_clear(r);
        return ASN_ECC_KEY_E;
    }

#ifndef NO_STRICT_ECDSA_LEN
    /* sanity check that the index has been advanced all the way to the end of
     * the buffer */
    if (idx != sigLen) {
        mp_clear(r);
        mp_clear(s);
        return ASN_ECC_KEY_E;
    }
#endif

    return 0;
}

#endif
#ifdef HAVE_ECC
WOLFSSL_ABI
int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key,
                        word32 inSz)
{
    word32 oidSum;
    int    version, length;
    int    privSz, pubSz = 0;
    byte   b;
    int    ret = 0;
    int    curve_id = ECC_CURVE_DEF;
#ifdef WOLFSSL_SMALL_STACK
    byte* priv;
    byte* pub = NULL;
#else
    byte priv[ECC_MAXSIZE+1];
    byte pub[2*(ECC_MAXSIZE+1)]; /* public key has two parts plus header */
#endif
    word32 algId = 0;
    byte* pubData = NULL;

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0)
        return BAD_FUNC_ARG;

    /* if has pkcs8 header skip it */
    if (ToTraditionalInline_ex(input, inOutIdx, inSz, &algId) < 0) {
        /* ignore error, did not have pkcs8 header */
    }
    else {
        curve_id = wc_ecc_get_oid(algId, NULL, NULL);
    }

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

    if (GetMyVersion(input, inOutIdx, &version, inSz) < 0)
        return ASN_PARSE_E;

    if (*inOutIdx >= inSz)
        return ASN_PARSE_E;

    b = input[*inOutIdx];
    *inOutIdx += 1;

    /* priv type */
    if (b != 4 && b != 6 && b != 7)
        return ASN_PARSE_E;

    if (GetLength(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;
    privSz = length;

    if (privSz > ECC_MAXSIZE)
        return BUFFER_E;

    WC_ALLOC_VAR_EX(priv, byte, privSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    /* priv key */
    XMEMCPY(priv, &input[*inOutIdx], (size_t)privSz);
    *inOutIdx += (word32)length;

    if ((*inOutIdx + 1) < inSz) {
        /* prefix 0, may have */
        b = input[*inOutIdx];
        if (b == ECC_PREFIX_0) {
            *inOutIdx += 1;

            if (GetLength(input, inOutIdx, &length, inSz) <= 0)
                ret = ASN_PARSE_E;
            else {
                ret = GetObjectId(input, inOutIdx, &oidSum, oidIgnoreType,
                                  inSz);
                if (ret == 0) {
                    if ((ret = CheckCurve(oidSum)) < 0)
                        ret = ECC_CURVE_OID_E;
                    else {
                        curve_id = ret;
                        ret = 0;
                    }
                }
            }
        }
    }

    if (ret == 0 && (*inOutIdx + 1) < inSz) {
        /* prefix 1 */
        b = input[*inOutIdx];
        *inOutIdx += 1;

        if (b != ECC_PREFIX_1) {
            ret = ASN_ECC_KEY_E;
        }
        else if (GetLength(input, inOutIdx, &length, inSz) <= 0) {
            ret = ASN_PARSE_E;
        }
        else {
            /* key header */
            ret = CheckBitString(input, inOutIdx, &length, inSz, 0, NULL);
            if (ret == 0) {
                /* pub key */
                pubSz = length;
                if (pubSz > 2*(ECC_MAXSIZE+1))
                    ret = BUFFER_E;
                else {
                    WC_ALLOC_VAR_EX(pub, byte, pubSz, key->heap,
                        DYNAMIC_TYPE_TMP_BUFFER, ret=MEMORY_E);
                    if (WC_VAR_OK(pub))
                    {
                        XMEMCPY(pub, &input[*inOutIdx], (size_t)pubSz);
                        *inOutIdx += (word32)length;
                        pubData = pub;
                    }
                }
            }
        }
    }

    if (ret == 0) {
        ret = wc_ecc_import_private_key_ex(priv, (word32)privSz, pubData,
            (word32)pubSz, key, curve_id);
    }

    WC_FREE_VAR_EX(priv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);

    return ret;
}

#ifdef WOLFSSL_CUSTOM_CURVES
/* returns 0 on success */
static int ASNToHexString(const byte* input, word32* inOutIdx, char** out,
                          word32 inSz, void* heap, int heapType)
{
    int len;
    int i;
    char* str;
    word32 localIdx;
    byte   tag;

    if (*inOutIdx >= inSz) {
        return BUFFER_E;
    }

    localIdx = *inOutIdx;
    if (GetASNTag(input, &localIdx, &tag, inSz) == 0 && tag == ASN_INTEGER) {
        if (GetASNInt(input, inOutIdx, &len, inSz) < 0)
            return ASN_PARSE_E;
    }
    else {
        if (GetOctetString(input, inOutIdx, &len, inSz) < 0)
            return ASN_PARSE_E;
    }

    str = (char*)XMALLOC((size_t)len * 2 + 1, heap, heapType);
    if (str == NULL) {
        return MEMORY_E;
    }

    for (i=0; i<len; i++)
        ByteToHexStr(input[*inOutIdx + (word32)i], str + i*2);
    str[len*2] = '\0';

    *inOutIdx += (word32)len;
    *out = str;

    (void)heap;
    (void)heapType;

    return 0;
}

static int EccKeyParamCopy(char** dst, char* src, void* heap)
{
    int ret = 0;
#ifdef WOLFSSL_ECC_CURVE_STATIC
    word32 length;
#endif

    if (dst == NULL || src == NULL)
        return BAD_FUNC_ARG;

#ifndef WOLFSSL_ECC_CURVE_STATIC
    *dst = src;
#else
    length = (int)XSTRLEN(src) + 1;
    if (length > MAX_ECC_STRING) {
        WOLFSSL_MSG("ECC Param too large for buffer");
        ret = BUFFER_E;
    }
    else {
        XSTRNCPY(*dst, src, MAX_ECC_STRING);
    }
    XFREE(src, heap, DYNAMIC_TYPE_ECC_BUFFER);
#endif
    (void)heap;

    return ret;
}

#endif
WOLFSSL_ABI
int wc_EccPublicKeyDecode(const byte* input, word32* inOutIdx,
                          ecc_key* key, word32 inSz)
{
    int    ret;
    int    version, length;
    int    curve_id = ECC_CURVE_DEF;
    word32 oidSum, localIdx;
    byte   tag, isPrivFormat = 0;

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0)
        return BAD_FUNC_ARG;

    if (GetSequence(input, inOutIdx, &length, inSz) < 0)
        return ASN_PARSE_E;

    /* Check if ECC private key is being used and skip private portion */
    if (GetMyVersion(input, inOutIdx, &version, inSz) >= 0) {
        isPrivFormat = 1;

        /* Type private key */
        if (*inOutIdx >= inSz)
            return ASN_PARSE_E;
        tag = input[*inOutIdx];
        *inOutIdx += 1;
        if (tag != 4 && tag != 6 && tag != 7)
            return ASN_PARSE_E;

        /* Skip Private Key */
        if (GetLength(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;
        if (length > ECC_MAXSIZE)
            return BUFFER_E;
        *inOutIdx += (word32)length;

        /* Private Curve Header */
        if (*inOutIdx >= inSz)
            return ASN_PARSE_E;
        tag = input[*inOutIdx];
        *inOutIdx += 1;
        if (tag != ECC_PREFIX_0)
            return ASN_ECC_KEY_E;
        if (GetLength(input, inOutIdx, &length, inSz) <= 0)
            return ASN_PARSE_E;
    }
    /* Standard ECC public key */
    else {
        if (GetSequence(input, inOutIdx, &length, inSz) < 0)
            return ASN_PARSE_E;

        ret = SkipObjectId(input, inOutIdx, inSz);
        if (ret != 0)
            return ret;
    }

    if (*inOutIdx >= inSz) {
        return BUFFER_E;
    }

    localIdx = *inOutIdx;
    if (GetASNTag(input, &localIdx, &tag, inSz) == 0 &&
            tag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
#ifdef WOLFSSL_CUSTOM_CURVES
        ecc_set_type* curve;
        int len;
        char* point = NULL;

        ret = 0;

        curve = (ecc_set_type*)XMALLOC(sizeof(*curve), key->heap,
                                                       DYNAMIC_TYPE_ECC_BUFFER);
        if (curve == NULL)
            ret = MEMORY_E;

        if (ret == 0) {
            static const char customName[] = "Custom";
            XMEMSET(curve, 0, sizeof(*curve));
        #ifndef WOLFSSL_ECC_CURVE_STATIC
            curve->name = customName;
        #else
            XMEMCPY((void*)curve->name, customName, sizeof(customName));
        #endif
            curve->id = ECC_CURVE_CUSTOM;

            if (GetSequence(input, inOutIdx, &length, inSz) < 0)
                ret = ASN_PARSE_E;
        }

        if (ret == 0) {
            GetInteger7Bit(input, inOutIdx, inSz);
            if (GetSequence(input, inOutIdx, &length, inSz) < 0)
                ret = ASN_PARSE_E;
        }
        if (ret == 0) {
            char* p = NULL;
            SkipObjectId(input, inOutIdx, inSz);
            ret = ASNToHexString(input, inOutIdx, &p, inSz,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            if (ret == 0) {
#ifndef WOLFSSL_ECC_CURVE_STATIC
                ret = EccKeyParamCopy((char**)&curve->prime, p, key->heap);
#else
                const char *_tmp_ptr = &curve->prime[0];
                ret = EccKeyParamCopy((char**)&_tmp_ptr, p, key->heap);
#endif
            }
        }
        if (ret == 0) {
            curve->size = (int)XSTRLEN(curve->prime) / 2;

            if (GetSequence(input, inOutIdx, &length, inSz) < 0)
                ret = ASN_PARSE_E;
        }
        if (ret == 0) {
            char* af = NULL;
            ret = ASNToHexString(input, inOutIdx, &af, inSz,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            if (ret == 0) {
#ifndef WOLFSSL_ECC_CURVE_STATIC
                ret = EccKeyParamCopy((char**)&curve->Af, af, key->heap);
#else
                const char *_tmp_ptr = &curve->Af[0];
                ret = EccKeyParamCopy((char**)&_tmp_ptr, af, key->heap);
#endif
            }
        }
        if (ret == 0) {
            char* bf = NULL;
            ret = ASNToHexString(input, inOutIdx, &bf, inSz,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            if (ret == 0) {
#ifndef WOLFSSL_ECC_CURVE_STATIC
                ret = EccKeyParamCopy((char**)&curve->Bf, bf, key->heap);
#else
                const char *_tmp_ptr = &curve->Bf[0];
                ret = EccKeyParamCopy((char**)&_tmp_ptr, bf, key->heap);
#endif
            }
        }
        if (ret == 0) {
            localIdx = *inOutIdx;
            if (*inOutIdx < inSz && GetASNTag(input, &localIdx, &tag, inSz)
                    == 0 && tag == ASN_BIT_STRING) {
                len = 0;
                ret = GetASNHeader(input, ASN_BIT_STRING, inOutIdx, &len, inSz);
                if (ret > 0)
                    ret = 0; /* reset on success */
                *inOutIdx += (word32)len;
            }
        }
        if (ret == 0) {
            ret = ASNToHexString(input, inOutIdx, (char**)&point, inSz,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);

            /* sanity check that point buffer is not smaller than the expected
             * size to hold ( 0 4 || Gx || Gy )
             * where Gx and Gy are each the size of curve->size * 2 */
            if (ret == 0 && (int)XSTRLEN(point) < (curve->size * 4) + 2) {
                XFREE(point, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
                ret = BUFFER_E;
            }
        }
        if (ret == 0) {
        #ifndef WOLFSSL_ECC_CURVE_STATIC
            curve->Gx = (const char*)XMALLOC((size_t)curve->size * 2 + 2,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            curve->Gy = (const char*)XMALLOC((size_t)curve->size * 2 + 2,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            if (curve->Gx == NULL || curve->Gy == NULL) {
                XFREE(point, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
                ret = MEMORY_E;
            }
        #else
            if (curve->size * 2 + 2 > MAX_ECC_STRING) {
                WOLFSSL_MSG("curve size is too large to fit in buffer");
                ret = BUFFER_E;
            }
        #endif
        }
        if (ret == 0) {
            char* o = NULL;

            XMEMCPY((char*)curve->Gx, point + 2, (size_t)curve->size * 2);
            XMEMCPY((char*)curve->Gy, point + curve->size * 2 + 2,
                                                 (size_t)curve->size * 2);
            ((char*)curve->Gx)[curve->size * 2] = '\0';
            ((char*)curve->Gy)[curve->size * 2] = '\0';
            XFREE(point, key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            ret = ASNToHexString(input, inOutIdx, &o, inSz,
                                            key->heap, DYNAMIC_TYPE_ECC_BUFFER);
            if (ret == 0) {
#ifndef WOLFSSL_ECC_CURVE_STATIC
                ret = EccKeyParamCopy((char**)&curve->order, o, key->heap);
#else
                const char *_tmp_ptr = &curve->order[0];
                ret = EccKeyParamCopy((char**)&_tmp_ptr, o, key->heap);
#endif
            }
        }
        if (ret == 0) {
            curve->cofactor = GetInteger7Bit(input, inOutIdx, inSz);

        #ifndef WOLFSSL_ECC_CURVE_STATIC
            curve->oid = NULL;
        #else
            XMEMSET((void*)curve->oid, 0, sizeof(curve->oid));
        #endif
            curve->oidSz = 0;
            curve->oidSum = 0;

            if (wc_ecc_set_custom_curve(key, curve) < 0) {
                ret = ASN_PARSE_E;
            }

            key->deallocSet = 1;

            curve = NULL;
        }
        if (curve != NULL)
            wc_ecc_free_curve(curve, key->heap);

        if (ret < 0)
            return ret;
#else
        return ASN_PARSE_E;
#endif /* WOLFSSL_CUSTOM_CURVES */
    }
    else {
        /* ecc params information */
        ret = GetObjectId(input, inOutIdx, &oidSum, oidIgnoreType, inSz);
        if (ret != 0)
            return ret;

        /* get curve id */
        if ((ret = CheckCurve(oidSum)) < 0)
            return ECC_CURVE_OID_E;
        else {
            curve_id = ret;
        }
    }

    if (isPrivFormat) {
        /* Public Curve Header - skip */
        if (*inOutIdx >= inSz)
            return ASN_PARSE_E;
        tag = input[*inOutIdx];
        *inOutIdx += 1;
        if (tag != ECC_PREFIX_1)
            return ASN_ECC_KEY_E;
        if (GetLength(input, inOutIdx, &length, inSz) <= 0)
            return ASN_PARSE_E;
    }

    /* key header */
    ret = CheckBitString(input, inOutIdx, &length, inSz, 1, NULL);
    if (ret != 0)
        return ret;

    /* This is the raw point data compressed or uncompressed. */
    if (wc_ecc_import_x963_ex(input + *inOutIdx, (word32)length, key,
                                                            curve_id) != 0) {
        return ASN_ECC_KEY_E;
    }

    *inOutIdx += (word32)length;

    return 0;
}

#ifdef HAVE_ECC_KEY_EXPORT
int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
                             int pubIn, int curveIn)
{
    byte   curve[MAX_ALGO_SZ+2];
    byte   ver[MAX_VERSION_SZ];
    byte   seq[MAX_SEQ_SZ];
    int    ret, curveSz, verSz;
    word32 totalSz;
    int    privHdrSz  = ASN_ECC_HEADER_SZ;
    int    pubHdrSz   = ASN_ECC_CONTEXT_SZ + ASN_ECC_HEADER_SZ;
#ifdef WOLFSSL_NO_MALLOC
    byte   prv[MAX_ECC_BYTES + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ];
    byte   pub[(MAX_ECC_BYTES * 2) + 1 + ASN_ECC_CONTEXT_SZ +
                              ASN_ECC_HEADER_SZ + MAX_SEQ_SZ];
#else
    byte   *prv = NULL, *pub = NULL;
#endif

    word32 idx = 0, prvidx = 0, pubidx = 0, curveidx = 0;
    word32 seqSz, privSz, pubSz = ECC_BUFSIZE;

    if (key == NULL || (output == NULL && inLen == NULL))
        return BAD_FUNC_ARG;

    if (curveIn) {
        /* curve */
        curve[curveidx++] = ECC_PREFIX_0;
        curveidx++ /* to put the size after computation */;
        curveSz = SetCurve(key, curve+curveidx, MAX_ALGO_SZ);
        if (curveSz < 0)
            return curveSz;
        /* set computed size */
        curve[1] = (byte)curveSz;
        curveidx += (word32)curveSz;
    }

    /* private */
    privSz = (word32)key->dp->size;

#ifdef WOLFSSL_QNX_CAAM
    /* check if is a black key, and add MAC size if needed */
    if (key->blackKey > 0 && key->blackKey != CAAM_BLACK_KEY_ECB) {
        privSz = privSz + WC_CAAM_MAC_SZ;
    }
#endif

#ifndef WOLFSSL_NO_MALLOC
    prv = (byte*)XMALLOC(privSz + (word32)privHdrSz + MAX_SEQ_SZ,
                         key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (prv == NULL) {
        return MEMORY_E;
    }
#else
    if (sizeof(prv) < privSz + privHdrSz + MAX_SEQ_SZ) {
        return BUFFER_E;
    }
#endif
    if (privSz < ASN_LONG_LENGTH) {
        prvidx += SetOctetString8Bit(privSz, &prv[prvidx]);
    }
    else {
        prvidx += SetOctetString(privSz, &prv[prvidx]);
    }
    ret = wc_ecc_export_private_only(key, prv + prvidx, &privSz);
    if (ret < 0) {
    #ifndef WOLFSSL_NO_MALLOC
        XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    #endif
        return ret;
    }
    prvidx += privSz;

    /* pubIn */
    if (pubIn) {
        PRIVATE_KEY_UNLOCK();
        ret = wc_ecc_export_x963(key, NULL, &pubSz);
        PRIVATE_KEY_LOCK();
        if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
        #ifndef WOLFSSL_NO_MALLOC
            XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
            return ret;
        }

    #ifndef WOLFSSL_NO_MALLOC
        pub = (byte*)XMALLOC(pubSz + (word32)pubHdrSz + MAX_SEQ_SZ,
                             key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (pub == NULL) {
            XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            return MEMORY_E;
        }
    #else
        if (sizeof(pub) < pubSz + pubHdrSz + MAX_SEQ_SZ) {
            return BUFFER_E;
        }
    #endif

        pub[pubidx++] = ECC_PREFIX_1;
        if (pubSz > 128) /* leading zero + extra size byte */
            pubidx += SetLength(pubSz + ASN_ECC_CONTEXT_SZ + 2, pub+pubidx);
        else /* leading zero */
            pubidx += SetLength(pubSz + ASN_ECC_CONTEXT_SZ + 1, pub+pubidx);

        /* SetBitString adds leading zero */
        pubidx += SetBitString(pubSz, 0, pub + pubidx);
        PRIVATE_KEY_UNLOCK();
        ret = wc_ecc_export_x963(key, pub + pubidx, &pubSz);
        PRIVATE_KEY_LOCK();
        if (ret != 0) {
        #ifndef WOLFSSL_NO_MALLOC
            XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
            XFREE(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        #endif
            return ret;
        }
        pubidx += pubSz;
    }

    /* make headers */
    verSz = SetMyVersion(1, ver, FALSE);
    seqSz = SetSequence((word32)verSz + prvidx + pubidx + curveidx, seq);

    totalSz = prvidx + pubidx + curveidx + (word32)verSz + seqSz;
    if (output == NULL) {
        *inLen = totalSz;
    #ifndef WOLFSSL_NO_MALLOC
        XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (pubIn) {
            XFREE(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        }
    #endif
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    if (inLen != NULL && totalSz > *inLen) {
        #ifndef WOLFSSL_NO_MALLOC
        XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (pubIn) {
            XFREE(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
        }
        #endif
        return BAD_FUNC_ARG;
    }

    /* write out */
    /* seq */
    XMEMCPY(output + idx, seq, seqSz);
    idx = seqSz;

    /* ver */
    XMEMCPY(output + idx, ver, (size_t)verSz);
    idx += (word32)verSz;

    /* private */
    XMEMCPY(output + idx, prv, prvidx);
    idx += prvidx;
#ifndef WOLFSSL_NO_MALLOC
    XFREE(prv, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    /* curve */
    XMEMCPY(output + idx, curve, curveidx);
    idx += curveidx;

    /* pubIn */
    if (pubIn) {
        XMEMCPY(output + idx, pub, pubidx);
        /* idx += pubidx;  not used after write, if more data remove comment */
    #ifndef WOLFSSL_NO_MALLOC
        XFREE(pub, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    #endif
    }

    return (int)totalSz;
}

#endif
#endif
#if (defined(HAVE_OCSP) || defined(HAVE_CRL)) && !defined(WOLFCRYPT_ONLY)

/* Get raw Date only, no processing, 0 on success */
static int GetBasicDate(const byte* source, word32* idx, byte* date,
                        byte* format, int maxIdx)
{
    int    ret, length;
    const byte *datePtr = NULL;

    WOLFSSL_ENTER("GetBasicDate");

    ret = GetDateInfo(source, idx, &datePtr, format, &length, maxIdx);
    if (ret < 0)
        return ret;

    XMEMCPY(date, datePtr, length);

    return 0;
}

#endif /* HAVE_OCSP || HAVE_CRL */

#if defined(HAVE_OCSP) && !defined(WOLFCRYPT_ONLY)
static int GetEnumerated(const byte* input, word32* inOutIdx, int *value,
        int sz)
{
    word32 idx = *inOutIdx;
    word32 len;
    byte   tag;

    WOLFSSL_ENTER("GetEnumerated");

    *value = 0;

    if (GetASNTag(input, &idx, &tag, sz) < 0)
        return ASN_PARSE_E;

    if (tag != ASN_ENUMERATED)
        return ASN_PARSE_E;

    if ((int)idx >= sz)
        return BUFFER_E;

    len = input[idx++];
    if (len > 4 || (int)(len + idx) > sz)
        return ASN_PARSE_E;

    while (len--) {
        *value  = *value << 8 | input[idx++];
    }

    *inOutIdx = idx;

    return *value;
}

#ifdef HAVE_OCSP_RESPONDER
WC_MAYBE_UNUSED static int EncodeCertID(OcspEntry* entry, byte* out,
        word32* outSz)
{
    (void)entry;
    (void)out;
    (void)outSz;
    /* Encoding ocsp CertID not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz,
                 OcspEntry* entry)
{
    int length;
    word32 oid;
    int ret;
    int expectedDigestSz;

    /* Hash algorithm */
    ret = GetAlgoId(input, inOutIdx, &oid, oidHashType, inSz);
    if (ret < 0)
        return ret;
    entry->hashAlgoOID = oid;

    /* Validate hash algorithm and get expected digest size */
    expectedDigestSz = wc_HashGetDigestSize(wc_OidGetHash((int)oid));
    if (expectedDigestSz <= 0)
        return ASN_SIG_HASH_E;

    /* Save reference to the hash of CN */
    ret = GetOctetString(input, inOutIdx, &length, inSz);
    if (ret < 0)
        return ret;
    if (length != expectedDigestSz || length > (int)sizeof(entry->issuerHash))
        return ASN_PARSE_E;
    XMEMCPY(entry->issuerHash, input + *inOutIdx, length);
    *inOutIdx += length;
    /* Save reference to the hash of the issuer public key */
    ret = GetOctetString(input, inOutIdx, &length, inSz);
    if (ret < 0)
        return ret;
    if (length != expectedDigestSz || length > (int)sizeof(entry->issuerKeyHash))
        return ASN_PARSE_E;
    XMEMCPY(entry->issuerKeyHash, input + *inOutIdx, length);
    *inOutIdx += length;

    /* Get serial number */
    if (wc_GetSerialNumber(input, inOutIdx, entry->status->serial,
                        &entry->status->serialSz, inSz) < 0)
        return ASN_PARSE_E;
    return 0;
}

#ifdef HAVE_OCSP_RESPONDER
WC_MAYBE_UNUSED static int EncodeSingleResponse(OcspEntry* single, byte* out,
        word32* outSz, void* heap)
{
    (void)single;
    (void)out;
    (void)outSz;
    (void)heap;
    /* Encoding ocsp responses not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
static int DecodeSingleResponse(byte* source, word32* ioIndex, word32 size,
                                int wrapperSz, OcspEntry* single)
{
    word32 idx = *ioIndex, prevIndex, localIdx, certIdIdx;
    int length;
    int ret;
    byte tag;

    WOLFSSL_ENTER("DecodeSingleResponse");

    prevIndex = idx;

    /* Wrapper around the Single Response */
    if (GetSequence(source, &idx, &length, size) < 0)
        return ASN_PARSE_E;

    /* Wrapper around the CertID */
    certIdIdx = idx;
    if (GetSequence(source, &idx, &length, size) < 0)
        return ASN_PARSE_E;
    single->rawCertId = source + certIdIdx;
    ret = OcspDecodeCertIDInt(source, &idx, size, single);
    if (ret < 0)
        return ASN_PARSE_E;
    single->rawCertIdSize = idx - certIdIdx;

    if (idx >= size)
        return BUFFER_E;

    /* CertStatus */
    switch (source[idx++])
    {
        case (ASN_CONTEXT_SPECIFIC | CERT_GOOD):
            single->status->status = CERT_GOOD;
            idx++;
            break;
        case (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | CERT_REVOKED):
            single->status->status = CERT_REVOKED;
            if (GetLength(source, &idx, &length, size) < 0)
                return ASN_PARSE_E;
            idx += length;
            break;
        case (ASN_CONTEXT_SPECIFIC | CERT_UNKNOWN):
            single->status->status = CERT_UNKNOWN;
            idx++;
            break;
        default:
            return ASN_PARSE_E;
    }

    if (idx >= size)
        return BUFFER_E;

#ifdef WOLFSSL_OCSP_PARSE_STATUS
    single->status->thisDateAsn = source + idx;
    localIdx = 0;
    if (GetDateInfo(single->status->thisDateAsn, &localIdx, NULL,
                    (byte*)&single->status->thisDateParsed.type,
                    &single->status->thisDateParsed.length, size - idx) < 0)
        return ASN_PARSE_E;

    if (idx + localIdx >= size)
        return BUFFER_E;

    XMEMCPY(single->status->thisDateParsed.data,
            single->status->thisDateAsn + localIdx - single->status->thisDateParsed.length,
            single->status->thisDateParsed.length);
#endif
    if (GetBasicDate(source, &idx, single->status->thisDate,
                     &single->status->thisDateFormat, size) < 0)
        return ASN_PARSE_E;

#ifndef NO_ASN_TIME_CHECK
#ifndef WOLFSSL_NO_OCSP_DATE_CHECK
    if ((! AsnSkipDateCheck) && !XVALIDATE_DATE(single->status->thisDate,
        single->status->thisDateFormat, ASN_BEFORE, MAX_DATE_SIZE))
        return ASN_BEFORE_DATE_E;
#endif
#endif

    /* The following items are optional. Only check for them if there is more
     * unprocessed data in the singleResponse wrapper. */
    localIdx = idx;
    if (((int)(idx - prevIndex) < wrapperSz) &&
        GetASNTag(source, &localIdx, &tag, size) == 0 &&
        tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0))
    {
        idx++;
        if (GetLength(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;
#ifdef WOLFSSL_OCSP_PARSE_STATUS
        single->status->nextDateAsn = source + idx;
        localIdx = 0;
        if (GetDateInfo(single->status->nextDateAsn, &localIdx, NULL,
                        (byte*)&single->status->nextDateParsed.type,
                        &single->status->nextDateParsed.length, size - idx) < 0)
            return ASN_PARSE_E;

        if (idx + localIdx >= size)
            return BUFFER_E;

        XMEMCPY(single->status->nextDateParsed.data,
                single->status->nextDateAsn + localIdx - single->status->nextDateParsed.length,
                single->status->nextDateParsed.length);
#endif
        if (GetBasicDate(source, &idx, single->status->nextDate,
                         &single->status->nextDateFormat, size) < 0)
            return ASN_PARSE_E;

#ifndef NO_ASN_TIME_CHECK
#ifndef WOLFSSL_NO_OCSP_DATE_CHECK
        if ((! AsnSkipDateCheck) &&
            !XVALIDATE_DATE(single->status->nextDate,
                            single->status->nextDateFormat, ASN_AFTER, MAX_DATE_SIZE))
            return ASN_AFTER_DATE_E;
#endif
#endif
    }

    /* Skip the optional extensions in singleResponse. */
    localIdx = idx;
    if (((int)(idx - prevIndex) < wrapperSz) &&
        GetASNTag(source, &localIdx, &tag, size) == 0 &&
        tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1))
    {
        idx++;
        if (GetLength(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;
        idx += length;
    }

    *ioIndex = idx;

    return 0;
}

static int DecodeOcspRespExtensions(byte* source, word32* ioIndex,
                                    OcspResponse* resp, word32 sz)
{
    word32 idx = *ioIndex;
    int length;
    int ext_bound; /* boundary index for the sequence of extensions */
    word32 oid;
    int ret;
    byte tag;

    WOLFSSL_ENTER("DecodeOcspRespExtensions");

    if ((idx + 1) > sz)
        return BUFFER_E;

    if (GetASNTag(source, &idx, &tag, sz) < 0)
        return ASN_PARSE_E;

    if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1))
        return ASN_PARSE_E;

    if (GetLength(source, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    if (GetSequence(source, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    ext_bound = idx + length;

    while (idx < (word32)ext_bound) {
        word32 localIdx;

        if (GetSequence(source, &idx, &length, sz) < 0) {
            WOLFSSL_MSG("\tfail: should be a SEQUENCE");
            return ASN_PARSE_E;
        }

        oid = 0;
        if (GetObjectId(source, &idx, &oid, oidOcspType, sz) < 0) {
            WOLFSSL_MSG("\tfail: OBJECT ID");
            return ASN_PARSE_E;
        }

        /* check for critical flag */
        if ((idx + 1) > (word32)sz) {
            WOLFSSL_MSG("\tfail: malformed buffer");
            return BUFFER_E;
        }

        localIdx = idx;
        if (GetASNTag(source, &localIdx, &tag, sz) == 0 && tag == ASN_BOOLEAN) {
            WOLFSSL_MSG("\tfound optional critical flag, moving past");
            ret = GetBoolean(source, &idx, sz);
            if (ret < 0)
                return ret;
        }

        ret = GetOctetString(source, &idx, &length, sz);
        if (ret < 0)
            return ret;

        if (oid == OCSP_NONCE_OID) {
            /* get data inside extra OCTET_STRING */
            ret = GetOctetString(source, &idx, &length, sz);
            if (ret < 0)
                return ret;

            resp->nonce = source + idx;
            resp->nonceSz = length;
        }

        idx += length;
    }

    *ioIndex = idx;
    return 0;
}

WC_MAYBE_UNUSED static int EncodeOcspRespExtensions(OcspResponse* resp,
        byte* out, word32* outSz)
{
    (void)resp;
    (void)out;
    (void)outSz;
    /* Encoding ocsp responses not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#ifdef HAVE_OCSP_RESPONDER
WC_MAYBE_UNUSED static int EncodeResponseData(OcspResponse* resp, byte* out,
        word32* outSz)
{
    (void)resp;
    (void)out;
    (void)outSz;
    /* Encoding ocsp responses not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
static int DecodeResponseData(byte* source, word32* ioIndex,
                              OcspResponse* resp, word32 size)
{
    word32 idx = *ioIndex, prev_idx, localIdx;
    int length;
    int version;
    int ret;
    byte tag;
    int wrapperSz;
    OcspEntry* single;

    WOLFSSL_ENTER("DecodeResponseData");

    resp->response = source + idx;
    prev_idx = idx;
    if (GetSequence(source, &idx, &length, size) < 0)
        return ASN_PARSE_E;
    resp->responseSz = length + idx - prev_idx;

    /* Get version. It is an EXPLICIT[0] DEFAULT(0) value. If this
     * item isn't an EXPLICIT[0], then set version to zero and move
     * onto the next item.
     */
    localIdx = idx;
    if (GetASNTag(source, &localIdx, &tag, size) == 0 &&
            tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED))
    {
        idx += 2; /* Eat the value and length */
        if (GetMyVersion(source, &idx, &version, size) < 0)
            return ASN_PARSE_E;
    } else
        version = 0;

    localIdx = idx;
    if (GetASNTag(source, &localIdx, &tag, size) != 0)
        return ASN_PARSE_E;

    resp->responderIdType = OCSP_RESPONDER_ID_INVALID;
    /* parse byName */
    if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1))
    {
        idx++; /* advance past ASN tag */
        if (GetLength(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;
        /* compute the hash of the name */
        resp->responderIdType = OCSP_RESPONDER_ID_NAME;
        ret = CalcHashId_ex(source + idx, length,
                resp->responderId.nameHash, OCSP_RESPONDER_ID_HASH_TYPE);
        if (ret != 0)
            return ret;
        idx += length;
    }
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 2))
    {
        idx++; /* advance past ASN tag */
        if (GetLength(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;

        if (GetOctetString(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;

        if (length != OCSP_RESPONDER_ID_KEY_SZ)
            return ASN_PARSE_E;
        resp->responderIdType = OCSP_RESPONDER_ID_KEY;
        XMEMCPY(resp->responderId.keyHash, source + idx, length);
        idx += length;
    }
    if (resp->responderIdType == OCSP_RESPONDER_ID_INVALID)
        return ASN_PARSE_E;

    /* save pointer to the producedAt time */
    if (GetBasicDate(source, &idx, resp->producedDate,
                                        &resp->producedDateFormat, size) < 0)
        return ASN_PARSE_E;

    /* Outer wrapper of the SEQUENCE OF Single Responses. */
    if (GetSequence(source, &idx, &wrapperSz, size) < 0)
        return ASN_PARSE_E;

    localIdx = idx;
    single = resp->single;
    while (idx - localIdx < (word32)wrapperSz) {
        ret = DecodeSingleResponse(source, &idx, size, wrapperSz, single);
        if (ret < 0)
            return ret; /* ASN_PARSE_E, ASN_BEFORE_DATE_E, ASN_AFTER_DATE_E */
        if (idx - localIdx < (word32)wrapperSz) {
            single->next = (OcspEntry*)XMALLOC(sizeof(OcspEntry), resp->heap,
                DYNAMIC_TYPE_OCSP_ENTRY);
            if (single->next == NULL) {
                return MEMORY_E;
            }
            XMEMSET(single->next, 0, sizeof(OcspEntry));

            single->next->status = (CertStatus*)XMALLOC(sizeof(CertStatus),
                resp->heap, DYNAMIC_TYPE_OCSP_STATUS);
            if (single->next->status == NULL) {
                XFREE(single->next, resp->heap, DYNAMIC_TYPE_OCSP_ENTRY);
                single->next = NULL;
                return MEMORY_E;
            }
            XMEMSET(single->next->status, 0, sizeof(CertStatus));

            single->next->isDynamic = 1;
            single->next->ownStatus = 1;

            single = single->next;
        }
    }

    /*
     * Check the length of the ResponseData against the current index to
     * see if there are extensions, they are optional.
     */
    if (idx - prev_idx < resp->responseSz)
        if (DecodeOcspRespExtensions(source, &idx, resp, size) < 0)
            return ASN_PARSE_E;

    *ioIndex = idx;
    return 0;
}

#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS

static int DecodeCerts(byte* source,
                            word32* ioIndex, OcspResponse* resp, word32 size)
{
    word32 idx = *ioIndex;
    byte tag;

    WOLFSSL_ENTER("DecodeCerts");

    if (GetASNTag(source, &idx, &tag, size) < 0)
        return ASN_PARSE_E;

    if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC))
    {
        int length;

        if (GetLength(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;

        if (GetSequence(source, &idx, &length, size) < 0)
            return ASN_PARSE_E;

        resp->cert = source + idx;
        resp->certSz = length;

        idx += length;
    }
    *ioIndex = idx;
    return 0;
}

#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */

#ifdef HAVE_OCSP_RESPONDER
WC_MAYBE_UNUSED static int EncodeBasicOcspResponse(OcspResponse* resp,
        byte* out, word32* outSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    (void)resp;
    (void)out;
    (void)outSz;
    (void)rsaKey;
    (void)eccKey;
    (void)rng;
    /* Encoding ocsp responses not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
            OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify,
            int noVerifySignature)
{
    int    length;
    word32 idx = *ioIndex;
    #ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
    word32 end_index;
    #endif
    int    ret;
    int    sigLength;
    int    sigValid = 0;
    WOLFSSL_ENTER("DecodeBasicOcspResponse");
    (void)heap;

    if (GetSequence(source, &idx, &length, size) < 0)
        return ASN_PARSE_E;

    if (idx + length > size)
        return ASN_INPUT_E;
    #ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
    end_index = idx + length;
    #endif

    if ((ret = DecodeResponseData(source, &idx, resp, size)) < 0)
        return ret; /* ASN_PARSE_E, ASN_BEFORE_DATE_E, ASN_AFTER_DATE_E */

    /* Get the signature algorithm */
    if (GetAlgoId(source, &idx, &resp->sigOID, oidSigType, size) < 0) {
        return ASN_PARSE_E;
    }
#ifdef WC_RSA_PSS
    else if (resp->sigOID == CTC_RSASSAPSS) {
        word32 sz;
        int len;
        byte* params;

        sz = idx;
        params = source + idx;
        if (GetSequence(source, &idx, &len, size) < 0)
            return ASN_PARSE_E;
        if (ret == 0) {
            idx += len;
            resp->sigParams = params;
            resp->sigParamsSz = idx - sz;
        }
    }
#endif

    ret = CheckBitString(source, &idx, &sigLength, size, 1, NULL);
    if (ret != 0)
        return ret;

    resp->sigSz = sigLength;
    resp->sig = source + idx;
    idx += sigLength;

    /*
     * Check the length of the BasicOcspResponse against the current index to
     * see if there are certificates, they are optional.
     */
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
    if (idx < end_index)
    {
        if (DecodeCerts(source, &idx, resp, size) < 0)
            return ASN_PARSE_E;

        ret = OcspCheckCert(resp, noVerify, noVerifySignature,
            (WOLFSSL_CERT_MANAGER*)cm, heap);
        if (ret == 0) {
            sigValid = 1;
        }
        else {
            WOLFSSL_MSG("OCSP Internal cert can't verify the response\n");
            /* try to verify the OCSP response with CA certs */
            ret = 0;
        }
    }
#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */
    if (!noVerifySignature && !sigValid) {
        Signer* ca;
        SignatureCtx sigCtx;
        ca = OcspFindSigner(resp, (WOLFSSL_CERT_MANAGER*)cm);
        if (ca == NULL)
            return ASN_NO_SIGNER_E;

#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
        if (OcspRespCheck(resp, ca, cm) != 0)
           return BAD_OCSP_RESPONDER;
#endif
        InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);

        /* ConfirmSignature is blocking here */
        sigValid = ConfirmSignature(&sigCtx, resp->response,
            resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID,
            resp->sig, resp->sigSz, resp->sigOID, resp->sigParams,
            resp->sigParamsSz, NULL);
        if (sigValid != 0) {
            WOLFSSL_MSG("\tOCSP Confirm signature failed");
            return ASN_OCSP_CONFIRM_E;
        }
        (void)noVerify;
    }

    *ioIndex = idx;
    return 0;
}

#ifdef HAVE_OCSP_RESPONDER
int OcspResponseEncode(OcspResponse* resp, byte* out, word32* outSz,
        RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    (void)resp;
    (void)out;
    (void)outSz;
    (void)rsaKey;
    (void)eccKey;
    (void)rng;
    /* Encoding ocsp responses not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap,
    int noVerifyCert, int noVerifySignature)
{
    int ret;
    int length = 0;
    word32 idx = 0;
    byte* source = resp->source;
    word32 size = resp->maxIdx;
    word32 oid;
    byte   tag;

    WOLFSSL_ENTER("OcspResponseDecode");

    /* peel the outer SEQUENCE wrapper */
    if (GetSequence(source, &idx, &length, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* First get the responseStatus, an ENUMERATED */
    if (GetEnumerated(source, &idx, &resp->responseStatus, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    if (resp->responseStatus != OCSP_SUCCESSFUL) {
        WOLFSSL_LEAVE("OcspResponseDecode", 0);
        return 0;
    }

    /* Next is an EXPLICIT record called ResponseBytes, OPTIONAL */
    if (idx >= size) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }
    if (GetASNTag(source, &idx, &tag, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }
    if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }
    if (GetLength(source, &idx, &length, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* Get the responseBytes SEQUENCE */
    if (GetSequence(source, &idx, &length, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* Check ObjectID for the resposeBytes */
    if (GetObjectId(source, &idx, &oid, oidOcspType, size) < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }
    if (oid != OCSP_BASIC_OID) {
        WOLFSSL_LEAVE("OcspResponseDecode", ASN_PARSE_E);
        return ASN_PARSE_E;
    }
    ret = GetOctetString(source, &idx, &length, size);
    if (ret < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ret);
        return ret;
    }

    ret = DecodeBasicOcspResponse(source, &idx, resp, size, cm, heap,
         noVerifyCert, noVerifySignature);
    if (ret < 0) {
        WOLFSSL_LEAVE("OcspResponseDecode", ret);
        return ret;
    }

    WOLFSSL_LEAVE("OcspResponseDecode", 0);
    return 0;
}

int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size)
{
    byte seqArray[5][MAX_SEQ_SZ];
    /* The ASN.1 of the OCSP Request is an onion of sequences */
    byte algoArray[MAX_ALGO_SZ];
    byte issuerArray[MAX_ENCODED_DIG_SZ];
    byte issuerKeyArray[MAX_ENCODED_DIG_SZ];
    byte snArray[MAX_SN_SZ];
    byte extArray[MAX_OCSP_EXT_SZ];
    word32 seqSz[5], algoSz, issuerSz, issuerKeySz, extSz, totalSz;
    int i, snSz;
    int keyIdSz;

    WOLFSSL_ENTER("EncodeOcspRequest");

    algoSz = SetAlgoID(req->hashAlg, algoArray, oidHashType, 0);
    keyIdSz = wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg));
    if (keyIdSz <= 0 || keyIdSz > WC_MAX_DIGEST_SIZE)
        return BAD_FUNC_ARG;

    issuerSz    = SetDigest(req->issuerHash,    keyIdSz,    issuerArray);
    issuerKeySz = SetDigest(req->issuerKeyHash, keyIdSz,    issuerKeyArray);
    snSz        = SetSerialNumber(req->serial,  req->serialSz, snArray,
                                                          MAX_SN_SZ, MAX_SN_SZ);
    extSz       = 0;

    if (snSz < 0)
        return snSz;

    if (req->nonceSz) {
        /* TLS Extensions use this function too - put extensions after
         * ASN.1: Context Specific [2].
         */
        extSz = EncodeOcspRequestExtensions(req, extArray + 2,
                                            OCSP_NONCE_EXT_SZ);
        extSz += SetExplicit(2, extSz, extArray, 0);
    }

    totalSz = algoSz + issuerSz + issuerKeySz + snSz;
    for (i = 4; i >= 0; i--) {
        seqSz[i] = SetSequence(totalSz, seqArray[i]);
        totalSz += seqSz[i];
        if (i == 2) totalSz += extSz;
    }

    if (output == NULL)
        return totalSz;
    if (totalSz > size)
        return BUFFER_E;

    totalSz = 0;
    for (i = 0; i < 5; i++) {
        XMEMCPY(output + totalSz, seqArray[i], seqSz[i]);
        totalSz += seqSz[i];
    }

    XMEMCPY(output + totalSz, algoArray, algoSz);
    totalSz += algoSz;

    XMEMCPY(output + totalSz, issuerArray, issuerSz);
    totalSz += issuerSz;

    XMEMCPY(output + totalSz, issuerKeyArray, issuerKeySz);
    totalSz += issuerKeySz;

    XMEMCPY(output + totalSz, snArray, snSz);
    totalSz += snSz;

    if (extSz != 0) {
        XMEMCPY(output + totalSz, extArray, extSz);
        totalSz += extSz;
    }

    return totalSz;
}

#ifdef HAVE_OCSP_RESPONDER
int DecodeOcspRequest(OcspRequest* req, const byte* input, word32 size)
{
    (void)req;
    (void)input;
    (void)size;
    /* Decoding ocsp requests not supported in legacy ASN parsing */
    return NOT_COMPILED_IN;
}

#endif
#endif
int GetNameHash_ex(const byte* source, word32* idx, byte* hash, int maxIdx,
    word32 sigOID)
{
    int    length;  /* length of all distinguished names */
    int    ret;
    word32 dummy;
    byte   tag;

    WOLFSSL_ENTER("GetNameHash");

    dummy = *idx;
    if (GetASNTag(source, &dummy, &tag, (word32)maxIdx) == 0 &&
            tag == ASN_OBJECT_ID) {
        WOLFSSL_MSG("Trying optional prefix...");

        if (GetLength(source, idx, &length, (word32)maxIdx) < 0)
            return ASN_PARSE_E;

        *idx += (word32)length;
        WOLFSSL_MSG("Got optional prefix");
    }

    /* For OCSP, RFC2560 section 4.1.1 states the issuer hash should be
     * calculated over the entire DER encoding of the Name field, including
     * the tag and length. */
    dummy = *idx;
    if (GetSequence(source, idx, &length, (word32)maxIdx) < 0)
        return ASN_PARSE_E;

    ret = CalcHashId_ex(source + dummy, (word32)length + *idx - dummy, hash,
        HashIdAlg(sigOID));

    *idx += (word32)length;

    return ret;
}

#if defined(HAVE_CRL) && !defined(WOLFCRYPT_ONLY)
static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
                      DecodedCRL* dcrl, word32 maxIdx)
{
    int ret;
    int len;
    word32 end;
    RevokedCert* rc;
#ifdef CRL_STATIC_REVOKED_LIST
    int totalCerts = 0;
#endif
    WOLFSSL_ENTER("GetRevoked");

    if (GetSequence(buff, idx, &len, maxIdx) < 0)
        return ASN_PARSE_E;

    end = *idx + len;

#ifdef CRL_STATIC_REVOKED_LIST
    totalCerts = dcrl->totalCerts;

    if (totalCerts >= CRL_MAX_REVOKED_CERTS) {
        return MEMORY_E;
    }

    rc = &rcert[totalCerts];
    ret = wc_GetSerialNumber(buff, idx, rc->serialNumber, &rc->serialSz,maxIdx);
    if (ret < 0) {
        WOLFSSL_MSG("wc_GetSerialNumber error");
        return ret;
    }
#else

    rc = (RevokedCert*)XMALLOC(sizeof(RevokedCert), dcrl->heap,
                                                          DYNAMIC_TYPE_REVOKED);
    if (rc == NULL) {
        WOLFSSL_MSG("Alloc Revoked Cert failed");
        return MEMORY_E;
    }
    XMEMSET(rc, 0, sizeof(RevokedCert));
    ret = wc_GetSerialNumber(buff, idx, rc->serialNumber, &rc->serialSz,maxIdx);
    if (ret < 0) {
        WOLFSSL_MSG("wc_GetSerialNumber error");
        XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
        return ret;
    }

    (void)rcert;
#endif /* CRL_STATIC_REVOKED_LIST */
    /* get date */
#ifndef NO_ASN_TIME
    ret = GetBasicDate(buff, idx, rc->revDate, &rc->revDateFormat, maxIdx);
    if (ret < 0) {
        WOLFSSL_MSG("Expecting Date");
#ifndef CRL_STATIC_REVOKED_LIST
        XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
#endif
        return ret;
    }
#endif
    /* Initialize reason code to absent */
    rc->reasonCode = -1;

    /* Parse CRL entry extensions if present */
    if (*idx < end) {
        word32 extIdx = *idx;
        int extLen;
        byte tag;

        /* Check for SEQUENCE tag (extensions wrapper) */
        if (GetASNTag(buff, &extIdx, &tag, end) == 0 &&
                tag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
            word32 seqIdx = extIdx - 1;  /* back up to re-read tag */
            if (GetSequence(buff, &seqIdx, &extLen, end) >= 0) {
                word32 extEnd = seqIdx + (word32)extLen;

#if defined(OPENSSL_EXTRA)
                /* Store raw DER of extensions for OpenSSL compat API */
                {
                    word32 rawStart = *idx;
                    word32 rawLen = end - rawStart;
                    rc->extensions = (byte*)XMALLOC(rawLen, dcrl->heap,
                                                    DYNAMIC_TYPE_REVOKED);
                    if (rc->extensions != NULL) {
                        XMEMCPY(rc->extensions, buff + rawStart, rawLen);
                        rc->extensionsSz = rawLen;
                    }
                }
#endif

                ret = ParseCRL_EntryExtensions(buff, seqIdx, extEnd,
                    &rc->reasonCode);
                if (ret != 0) {
#if defined(OPENSSL_EXTRA)
                    XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
                    rc->extensions = NULL;
                    rc->extensionsSz = 0;
#endif
#ifndef CRL_STATIC_REVOKED_LIST
                    XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
#endif
                    return ret;
                }
            }
        }
    }

#ifndef CRL_STATIC_REVOKED_LIST
    /* add to list only after all parsing succeeded */
    rc->next = dcrl->certs;
    dcrl->certs = rc;
#endif
    dcrl->totalCerts++;

    *idx = end;

    return 0;
}

/* Get CRL Signature, 0 on success */
static int GetCRL_Signature(const byte* source, word32* idx, DecodedCRL* dcrl,
                            int maxIdx)
{
    int    length;
    int    ret;

    WOLFSSL_ENTER("GetCRL_Signature");

    ret = CheckBitString(source, idx, &length, maxIdx, 1, NULL);
    if (ret != 0)
        return ret;
    dcrl->sigLength = length;

    dcrl->signature = (byte*)&source[*idx];
    *idx += dcrl->sigLength;

    return 0;
}

static int ParseCRL_CertList(RevokedCert* rcert, DecodedCRL* dcrl,
                           const byte* buf,word32* inOutIdx, int sz, int verify)
{
    word32 oid, dateIdx, idx, checkIdx;
    int length;
#ifdef WOLFSSL_NO_CRL_NEXT_DATE
    int doNextDate = 1;
#endif
    byte tag;

    if (dcrl == NULL || inOutIdx == NULL || buf == NULL) {
        return BAD_FUNC_ARG;
    }

    /* may have version */
    idx = *inOutIdx;

    checkIdx = idx;
    if (GetASNTag(buf, &checkIdx, &tag, sz) == 0 && tag == ASN_INTEGER) {
        if (GetMyVersion(buf, &idx, &dcrl->version, sz) < 0)
            return ASN_PARSE_E;
        dcrl->version++;
    }

    if (GetAlgoId(buf, &idx, &oid, oidIgnoreType, sz) < 0) {
        return ASN_PARSE_E;
    }
#ifdef WC_RSA_PSS
    else if (oid == CTC_RSASSAPSS) {
        word32 tmpSz;
        int len;

        tmpSz = idx;
        dcrl->sigParamsIndex = idx;
        if (GetSequence(buf, &idx, &len, sz) < 0) {
            dcrl->sigParamsIndex = 0;
            return ASN_PARSE_E;
        }
        idx += len;
        dcrl->sigParamsLength = idx - tmpSz;
    }
#endif

    checkIdx = idx;
    if (GetSequence(buf, &checkIdx, &length, sz) < 0) {
        return ASN_PARSE_E;
    }
#ifdef OPENSSL_EXTRA
    dcrl->issuerSz = length + (checkIdx - idx);
    dcrl->issuer   = (byte*)GetNameFromDer(buf + idx, (int)dcrl->issuerSz);
#endif

    if (GetNameHash_ex(buf, &idx, dcrl->issuerHash, sz, oid) < 0)
        return ASN_PARSE_E;

    if (GetBasicDate(buf, &idx, dcrl->lastDate, &dcrl->lastDateFormat, sz) < 0)
        return ASN_PARSE_E;

    dateIdx = idx;

    if (GetBasicDate(buf, &idx, dcrl->nextDate, &dcrl->nextDateFormat, sz) < 0)
    {
#ifndef WOLFSSL_NO_CRL_NEXT_DATE
        (void)dateIdx;
        return ASN_PARSE_E;
#else
        dcrl->nextDateFormat = ASN_OTHER_TYPE;  /* skip flag */
        doNextDate = 0;
        idx = dateIdx;
#endif
    }

#ifdef WOLFSSL_NO_CRL_NEXT_DATE
    if (doNextDate)
#endif
    {
#if !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_CRL_DATE_CHECK)
        if (verify != NO_VERIFY &&
            (! AsnSkipDateCheck) &&
            !XVALIDATE_DATE(dcrl->nextDate, dcrl->nextDateFormat, ASN_AFTER,
                            MAX_DATE_SIZE)) {
            WOLFSSL_MSG("CRL after date is no longer valid");
            WOLFSSL_ERROR_VERBOSE(CRL_CERT_DATE_ERR);
            return CRL_CERT_DATE_ERR;
        }
#else
        (void)verify;
#endif
    }

    checkIdx = idx;
    if ((idx != dcrl->sigIndex) && (GetASNTag(buf, &checkIdx, &tag, sz) == 0) &&
            (tag != CRL_EXTENSIONS)) {
        int len;
        word32 tlen;

        if (GetSequence(buf, &idx, &len, sz) < 0)
            return ASN_PARSE_E;
        tlen = (word32)len + idx;
        if (tlen < idx)
            return ASN_PARSE_E;

        while (idx < tlen) {
            if (GetRevoked(rcert, buf, &idx, dcrl, tlen) < 0)
                return ASN_PARSE_E;
        }
    }

    *inOutIdx = idx;

    return 0;
}

#ifndef NO_SKID
static int ParseCRL_AuthKeyIdExt(const byte* input, int sz, DecodedCRL* dcrl)
{
    word32 idx = 0;
    int length = 0, ret = 0;
    byte tag;

    WOLFSSL_ENTER("ParseCRL_AuthKeyIdExt");

    if (GetSequence(input, &idx, &length, sz) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE");
        return ASN_PARSE_E;
    }

    if (GetASNTag(input, &idx, &tag, sz) < 0) {
        return ASN_PARSE_E;
    }

    if (tag != (ASN_CONTEXT_SPECIFIC | 0)) {
        WOLFSSL_MSG("\tinfo: OPTIONAL item 0, not available");
        return 0;
    }

    if (GetLength(input, &idx, &length, sz) <= 0) {
        WOLFSSL_MSG("\tfail: extension data length");
        return ASN_PARSE_E;
    }

    dcrl->extAuthKeyIdSet = 1;

    /* Get the hash or hash of the hash if wrong size. */
    ret = GetHashId(input + idx, length, dcrl->extAuthKeyId,
        HashIdAlg(dcrl->signatureOID));

    return ret;
}

#endif
static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf,
        word32* inOutIdx, word32 sz)
{
    int length;
    word32 idx;
    word32 ext_bound; /* boundary index for the sequence of extensions */
    word32 oid;
    byte tag;

    WOLFSSL_ENTER("ParseCRL_Extensions");
    (void)dcrl;

    if (inOutIdx == NULL)
        return BAD_FUNC_ARG;

    idx = *inOutIdx;

    /* CRL Extensions are optional */
    if ((idx + 1) > sz)
        return 0;

    /* CRL Extensions are optional */
    if (GetASNTag(buf, &idx, &tag, sz) < 0)
        return 0;

    /* CRL Extensions are optional */
    if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0))
        return 0;

    if (GetLength(buf, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    if (GetSequence(buf, &idx, &length, sz) < 0)
        return ASN_PARSE_E;

    ext_bound = idx + length;

    while (idx < (word32)ext_bound) {
        word32 localIdx;
        int ret;
        int critical = 0;

        if (GetSequence(buf, &idx, &length, sz) < 0) {
            WOLFSSL_MSG("\tfail: should be a SEQUENCE");
            return ASN_PARSE_E;
        }

        oid = 0;
        if (GetObjectId(buf, &idx, &oid, oidCrlExtType, sz) < 0) {
            WOLFSSL_MSG("\tfail: OBJECT ID");
            return ASN_PARSE_E;
        }

        /* check for critical flag */
        if ((idx + 1) > (word32)sz) {
            WOLFSSL_MSG("\tfail: malformed buffer");
            return BUFFER_E;
        }

        localIdx = idx;
        if (GetASNTag(buf, &localIdx, &tag, sz) == 0 &&
                tag == ASN_BOOLEAN) {
            WOLFSSL_MSG("\tfound optional critical flag, moving past");
            ret = GetBoolean(buf, &idx, sz);
            if (ret < 0)
                return ret;
            critical = ret;
        }

        ret = GetOctetString(buf, &idx, &length, sz);
        if (ret < 0)
            return ret;

        if (oid == AUTH_KEY_OID) {
        #ifndef NO_SKID
            ret = ParseCRL_AuthKeyIdExt(buf + idx, length, dcrl);
            if (ret < 0) {
                WOLFSSL_MSG("\tcouldn't parse AuthKeyId extension");
                return ret;
            }
        #endif
        }
        else if (oid == CRL_NUMBER_OID) {
            localIdx = idx;
            if (GetASNTag(buf, &localIdx, &tag, sz) == 0 &&
                    tag == ASN_INTEGER) {
                word32 rawIdx = idx;
                int rawLen = 0;
                ret = GetASNInt(buf, &idx, &length, sz);
                if (ret < 0) {
                    WOLFSSL_MSG("\tcouldn't parse CRL number extension");
                    return ret;
                }
                /* RFC 5280 s5.2.3: CRL number must be non-negative.
                 * Check the raw encoding before GetASNInt strips
                 * the leading-zero pad: skip past the INTEGER tag
                 * and length, then reject if the first content byte
                 * has its high bit set (negative value). */
                (void)GetASNHeader(buf, ASN_INTEGER,
                    &rawIdx, &rawLen, sz);
                if (rawLen > 0 && (buf[rawIdx] & 0x80) != 0) {
                    WOLFSSL_MSG("CRL number is negative");
                    return ASN_PARSE_E;
                }
                if (length <= CRL_MAX_NUM_SZ) {
                    DECL_MP_INT_SIZE_DYN(m, CRL_MAX_NUM_SZ_BITS,
                                   CRL_MAX_NUM_SZ_BITS);
                    NEW_MP_INT_SIZE(m, CRL_MAX_NUM_SZ_BITS, NULL,
                                   DYNAMIC_TYPE_TMP_BUFFER);
                #ifdef MP_INT_SIZE_CHECK_NULL
                    if (m == NULL) {
                        ret = MEMORY_E;
                    }
                #endif

                    if (ret == 0 && ((ret = INIT_MP_INT_SIZE(m, CRL_MAX_NUM_SZ
                                    * CHAR_BIT)) != MP_OKAY)) {
                        ret = MP_INIT_E;
                    }

                    if (ret == MP_OKAY)
                        ret = mp_read_unsigned_bin(m, buf + idx, length);

                    if (ret != MP_OKAY)
                        ret = BUFFER_E;

                    if (ret == MP_OKAY && mp_toradix(m, (char*)dcrl->crlNumber,
                                MP_RADIX_HEX) != MP_OKAY)
                        ret = BUFFER_E;

                    if (ret == MP_OKAY) {
                        dcrl->crlNumberSet = 1;
                    }

                    FREE_MP_INT_SIZE(m, NULL, DYNAMIC_TYPE_TMP_BUFFER);

                    if (ret != MP_OKAY)
                        return ret;
                } else {
                    WOLFSSL_MSG("CRL number exceeds limitation");
                    ret = BUFFER_E;
                }
            }
        }
        else if (critical) {
            WOLFSSL_MSG("Unknown critical CRL extension");
            return ASN_CRIT_EXT_E;
        }

        idx += length;
    }

    *inOutIdx = idx;

    return 0;
}

int ParseCRL(RevokedCert* rcert, DecodedCRL* dcrl, const byte* buff, word32 sz,
             int verify, void* cm)
{
    Signer*      ca = NULL;
    SignatureCtx sigCtx;
    int          ret = 0;
    int          len;
    word32       idx = 0;
    const byte* sigParams = NULL;
    int sigParamsSz = 0;

    WOLFSSL_MSG("ParseCRL");

    /* raw crl hash */
    /* hash here if needed for optimized comparisons
     * wc_Sha sha;
     * wc_InitSha(&sha);
     * wc_ShaUpdate(&sha, buff, sz);
     * wc_ShaFinal(&sha, dcrl->crlHash); */

    if (GetSequence(buff, &idx, &len, sz) < 0)
        return ASN_PARSE_E;

    dcrl->certBegin = idx;
    /* Normalize sz for the length inside the outer sequence. */
    sz = len + idx;

    if (GetSequence(buff, &idx, &len, sz) < 0)
        return ASN_PARSE_E;
    dcrl->sigIndex = len + idx;

    if (ParseCRL_CertList(rcert, dcrl, buff, &idx, dcrl->sigIndex, verify) < 0)
        return ASN_PARSE_E;

    if (ParseCRL_Extensions(dcrl, buff, &idx, dcrl->sigIndex) < 0)
        return ASN_PARSE_E;

    idx = dcrl->sigIndex;

    if (GetAlgoId(buff, &idx, &dcrl->signatureOID, oidSigType, sz) < 0) {
        return ASN_PARSE_E;
    }
#ifdef WC_RSA_PSS
    else if (dcrl->signatureOID == CTC_RSASSAPSS) {
        word32 tmpSz;
        const byte* params;

        tmpSz = idx;
        params = buff + idx;
        if (GetSequence(buff, &idx, &len, sz) < 0) {
            return ASN_PARSE_E;
        }
        idx += len;
        sigParams = params;
        sigParamsSz = idx - tmpSz;
    }
#endif

    if (GetCRL_Signature(buff, &idx, dcrl, sz) < 0)
        return ASN_PARSE_E;

    /* openssl doesn't add skid by default for CRLs cause firefox chokes
       if experiencing issues uncomment NO_SKID define in CRL section of
       wolfssl/wolfcrypt/settings.h */
#ifndef NO_SKID
    if (dcrl->extAuthKeyIdSet) {
        ca = GetCA(cm, dcrl->extAuthKeyId); /* more unique than issuerHash */
    }
    if (ca != NULL && XMEMCMP(dcrl->issuerHash, ca->subjectNameHash,
            KEYID_SIZE) != 0) {
        ca = NULL;
    }
    if (ca == NULL) {
        ca = GetCAByName(cm, dcrl->issuerHash); /* last resort */
        /* If AKID is available then this CA doesn't have the public
         * key required */
        if (ca && dcrl->extAuthKeyIdSet) {
            WOLFSSL_MSG("CA SKID doesn't match AKID");
            ca = NULL;
        }
    }
#else
    ca = GetCA(cm, dcrl->issuerHash);
#endif /* !NO_SKID */
    WOLFSSL_MSG("About to verify CRL signature");

    if (ca == NULL) {
        WOLFSSL_MSG("Did NOT find CRL issuer CA");
        ret = ASN_CRL_NO_SIGNER_E;
        WOLFSSL_ERROR_VERBOSE(ret);
        goto end;
    }

    WOLFSSL_MSG("Found CRL issuer CA");
    ret = VerifyCRL_Signature(&sigCtx, buff + dcrl->certBegin,
           dcrl->sigIndex - dcrl->certBegin, dcrl->signature, dcrl->sigLength,
           dcrl->signatureOID, sigParams, sigParamsSz, ca, dcrl->heap);

end:
    return ret;
}

#endif
#ifdef WOLFSSL_CERT_PIV
int wc_ParseCertPIV(wc_CertPIV* piv, const byte* buf, word32 totalSz)
{
    int length = 0;
    word32 idx = 0;

    WOLFSSL_ENTER("wc_ParseCertPIV");

    if (piv == NULL || buf == NULL || totalSz == 0)
        return BAD_FUNC_ARG;

    XMEMSET(piv, 0, sizeof(wc_CertPIV));

    /* Detect Identiv PIV (with 0x0A, 0x0B and 0x0C sections) */
    /* Certificate (0A 82 05FA) */
    if (GetASNHeader(buf, ASN_PIV_CERT, &idx, &length, totalSz) >= 0) {
        /* Identiv Type PIV card */
        piv->isIdentiv = 1;

        piv->cert =   &buf[idx];
        piv->certSz = length;
        idx += length;

        /* Nonce (0B 14) */
        if (GetASNHeader(buf, ASN_PIV_NONCE, &idx, &length, totalSz) >= 0) {
            piv->nonce =   &buf[idx];
            piv->nonceSz = length;
            idx += length;
        }

        /* Signed Nonce (0C 82 0100) */
        if (GetASNHeader(buf, ASN_PIV_SIGNED_NONCE, &idx, &length, totalSz) >= 0) {
            piv->signedNonce =   &buf[idx];
            piv->signedNonceSz = length;
        }

        idx = 0;
        buf = piv->cert;
        totalSz = piv->certSz;
    }

    /* Certificate Buffer Total Size (53 82 05F6) */
    if (GetASNHeader(buf, ASN_APPLICATION | ASN_PRINTABLE_STRING, &idx,
                                                   &length, totalSz) < 0) {
        return ASN_PARSE_E;
    }
    /* PIV Certificate (70 82 05ED) */
    if (GetASNHeader(buf, ASN_PIV_TAG_CERT, &idx, &length,
                                                         totalSz) < 0) {
        return ASN_PARSE_E;
    }

    /* Capture certificate buffer pointer and length */
    piv->cert =   &buf[idx];
    piv->certSz = length;
    idx += length;

    /* PIV Certificate Info (71 01 00) */
    if (GetASNHeader(buf, ASN_PIV_TAG_CERT_INFO, &idx, &length,
                                                        totalSz) >= 0) {
        if (length >= 1) {
            piv->compression = (buf[idx] & ASN_PIV_CERT_INFO_COMPRESSED);
            piv->isX509 =      ((buf[idx] & ASN_PIV_CERT_INFO_ISX509) != 0);
        }
        idx += length;
    }

    /* PIV Error Detection (FE 00) */
    if (GetASNHeader(buf, ASN_PIV_TAG_ERR_DET, &idx, &length,
                                                        totalSz) >= 0) {
        piv->certErrDet =   &buf[idx];
        piv->certErrDetSz = length;
        idx += length;
    }

    return 0;
}

#endif

#endif /* WOLFSSL_ASN_ORIG_INCLUDED */
