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

/*
 * DESCRIPTION
 * This library provides the interface to Abstract Syntax Notation One (ASN.1)
 * objects.
 * ASN.1 is a standard interface description language for defining data
 * structures that can be serialized and deserialized in a cross-platform way.
 *
 * Encoding of ASN.1 is either using Basic Encoding Rules (BER) or
 * Distinguished Encoding Rules (DER). DER has only one possible encoding for a
 * ASN.1 description and the data.
 * Encode using DER and decode BER or DER.
 *
 * Provides routines to convert BER into DER. Replaces indefinite length
 * encoded items with explicit lengths.
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

/*
ASN Options:
 * NO_ASN_TIME_CHECK: Disables ASN time checks (avoiding the ASN_BEFORE_DATE_E
 * and ASN_AFTER_DATE_E errors). Safer ways to avoid date errors would be to
 * set the WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY flag when calling the _ex versions of
 * cert loading functions or to define the WOLFSSL_NO_OCSP_DATE_CHECK macro to
 * skip OCSP date errors. Defining NO_ASN_TIME_CHECK will skip ALL date checks
 * and could pose a security risk.
 * NO_ASN_TIME: Disables time parts of the ASN code for systems without an RTC
    or wishing to save space.
 * IGNORE_NAME_CONSTRAINTS: Skip ASN name checks.
 * ASN_DUMP_OID: Allows dump of OID information for debugging.
 * RSA_DECODE_EXTRA: Decodes extra information in RSA public key.
 * WOLFSSL_CERT_GEN: Cert generation. Saves extra certificate info in GetName.
 * WOLFSSL_NO_ASN_STRICT: Disable strict RFC compliance checks to
    restore 3.13.0 behavior.
 * WOLFSSL_ASN_ALLOW_0_SERIAL: Even if WOLFSSL_NO_ASN_STRICT is not defined,
    allow a length=1, but zero value serial number.
 * WOLFSSL_NO_OCSP_OPTIONAL_CERTS: Skip optional OCSP certs (responder issuer
    must still be trusted)
 * WOLFSSL_NO_TRUSTED_CERTS_VERIFY: Workaround for situation where entire cert
    chain is not loaded. This only matches on subject and public key and
    does not perform a PKI validation, so it is not a secure solution.
    Only enabled for OCSP.
 * WOLFSSL_NO_OCSP_ISSUER_CHECK: Can be defined for backwards compatibility to
    disable checking of https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2.
 * WOLFSSL_SMALL_CERT_VERIFY: Verify the certificate signature without using
    DecodedCert. Doubles up on some code but allows smaller dynamic memory
    usage.
 * WOLFSSL_NO_OCSP_DATE_CHECK: Disable date checks for OCSP responses. This
    may be required when the system's real-time clock is not very accurate.
    It is recommended to enforce the nonce check instead if possible.
 * WOLFSSL_NO_CRL_DATE_CHECK: Disable date checks for CRL's.
 * WOLFSSL_NO_CRL_NEXT_DATE: Do not fail if CRL next date is missing
 * WOLFSSL_FORCE_OCSP_NONCE_CHECK: Require nonces to be available in OCSP
    responses. The nonces are optional and may not be supported by all
    responders. If it can be ensured that the used responder sends nonces this
    option may improve security.
 * WOLFSSL_ASN_TEMPLATE: Encoding and decoding using a template.
 * WOLFSSL_DEBUG_ASN_TEMPLATE: Enables debugging output when using ASN.1
    templates.
 * WOLFSSL_ASN_TEMPLATE_TYPE_CHECK: Use ASN functions to better test compiler
    type issues for testing
 * CRLDP_VALIDATE_DATA: For ASN template only, validates the reason data
 * WOLFSSL_AKID_NAME: Enable support for full AuthorityKeyIdentifier extension.
    Only supports copying full AKID from an existing certificate.
 * WOLFSSL_CUSTOM_OID: Enable custom OID support for subject and request
    extensions
 * WOLFSSL_HAVE_ISSUER_NAMES: Store pointers to issuer name components and their
    lengths and encodings.
 * WOLFSSL_SUBJ_DIR_ATTR: Enable support for SubjectDirectoryAttributes
    extension.
 * WOLFSSL_SUBJ_INFO_ACC: Enable support for SubjectInfoAccess extension.
 * WOLFSSL_FPKI: Enable support for FPKI (Federal PKI) extensions.
 * WOLFSSL_CERT_NAME_ALL: Adds more certificate name capability at the
    cost of taking up more memory. Adds initials, givenname, dnQualifer for
    example.
 * WC_ASN_HASH_SHA256: Force use of SHA2-256 for the internal hash ID calcs.
 * WOLFSSL_ALLOW_ENCODING_CA_FALSE: Allow encoding BasicConstraints CA:FALSE
 *  which is discouraged by X.690 specification - default values shall not
 *  be encoded.
 * NO_TIME_SIGNEDNESS_CHECK: Disabled the time_t signedness check.
 * WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED: Allows the ECDSA/EdDSA signature
 *  algorithms in certificates to have NULL parameter instead of empty.
 *  DO NOT enable this unless required for interoperability.
 * WOLFSSL_ASN_EXTRA: Make more ASN.1 APIs available regardless of internal
 *  usage.
 * WOLFSSL_ALLOW_AKID_SKID_MATCH: By default cert issuer is found using hash
 * of cert subject hash with signers subject hash. This option allows fallback
 * to using AKID and SKID matching.
 *
 * Certificate Generation/Parsing:
 * WOLFSSL_CERT_REQ:         Enable certificate request (CSR) support
 * WOLFSSL_CERT_EXT:         Enable certificate extension support
 * WOLFSSL_CERT_PIV:         Enable PIV certificate support
 * WOLFSSL_CERT_GEN_CACHE:   Cache DER for cert generation
 * WOLFSSL_CERT_SIGN_CB:     Enable certificate signing callback
 * WOLFSSL_CERT_NAME_ALL:    Store all certificate name components
 * WOLFSSL_MULTI_ATTRIB:     Enable multi-valued RDN attributes
 * WOLFSSL_DER_TO_PEM:       Enable DER to PEM conversion
 * WOLFSSL_PEM_TO_DER:       Enable PEM to DER conversion
 * WOLFSSL_PUB_PEM_TO_DER:   Enable public key PEM to DER conversion
 * WOLFSSL_KEY_TO_DER:       Enable key to DER encoding
 * WOLFSSL_ENCRYPTED_KEYS:   Enable encrypted private key support (PKCS#8)
 * ASN_BER_TO_DER:           Enable BER to DER conversion
 * WOLFSSL_DUP_CERTPOL:      Allow duplicate certificate policies
 * WOLFSSL_NAMES_STATIC:     Use static allocation for name strings
 * WOLFSSL_SIGNER_DER_CERT:  Store signer DER cert in cert manager
 *
 * Certificate Validation:
 * NO_VERIFY_OID:            Skip OID verification
 * NO_CHECK_PRIVATE_KEY:     Skip private key pair check
 * NO_SKID:                  Disable Subject Key Identifier
 * NO_STRICT_ECDSA_LEN:      Allow non-strict ECDSA signature length
 * NO_WOLFSSL_CM_VERIFY:     Disable cert manager verify callback
 * NO_WOLFSSL_SKIP_TRAILING_PAD: Don't skip trailing padding
 * ALLOW_SELFSIGNED_INVALID_CERTSIGN: Allow self-signed certs
 *                            without keyCertSign in keyUsage
 * ALLOW_V1_EXTENSIONS:      Allow extensions in v1 certificates
 * USE_WOLF_VALIDDATE:       Use wolfSSL date validation
 * WC_ASN_RUNTIME_DATE_CHECK_CONTROL: Runtime control of date checking
 * WOLFSSL_AFTER_DATE_CLOCK_SKEW: Clock skew tolerance for after-date
 * WOLFSSL_BEFORE_DATE_CLOCK_SKEW: Clock skew tolerance for before-date
 * WOLFSSL_TRUST_PEER_CERT:  Enable trusted peer certificate support
 *
 * Extensions:
 * WOLFSSL_ALT_NAMES:        Enable Subject Alternative Names
 * WOLFSSL_ALT_NAMES_NO_REV: Alt names without reverse order
 * WOLFSSL_IP_ALT_NAME:      Enable IP address in SAN
 * WOLFSSL_RID_ALT_NAME:     Enable Registered ID in SAN
 * WOLFSSL_SEP:              Enable SubjectEntryPoint extension
 * WOLFSSL_EKU_OID:          Enable Extended Key Usage OID support
 * WOLFSSL_ACERT:            Enable attribute certificate support
 * IGNORE_KEY_EXTENSIONS:    Ignore key usage extensions
 * IGNORE_NETSCAPE_CERT_TYPE: Ignore Netscape cert type extension
 * WOLFSSL_ALLOW_CRIT_AIA:   Allow critical Authority Info Access
 * WOLFSSL_ALLOW_CRIT_AKID:  Allow critical Auth Key Identifier
 * WOLFSSL_ALLOW_CRIT_SKID:  Allow critical Subject Key Identifier
 * WC_ASN_UNKNOWN_EXT_CB:    Callback for unknown extensions
 *
 * ASN.1 Parsing:
 * WOLFSSL_ASN_ALL:          Enable all ASN.1 features
 * WOLFSSL_ASN_CA_ISSUER:    Enable CA Issuer in AIA parsing
 * WOLFSSL_ASN_PRINT:        Enable ASN.1 structure printing
 * WOLFSSL_ASN_INT_LEAD_0_ANY: Allow any leading zero in ASN integers
 * WOLFSSL_ASN_PARSE_KEYUSAGE: Parse key usage extension
 * WOLFSSL_ASN_TIME_STRING:  Enable ASN time to string conversion
 * ASN_TEMPLATE_SKIP_ISCA_CHECK: Skip isCA check in ASN template
 *
 * OID:
 * HAVE_OID_ENCODING:        Enable OID encoding support
 * HAVE_OID_DECODING:        Enable OID decoding support
 * WOLFSSL_OLD_OID_SUM:      Use old OID sum calculation
 *
 * CRL:
 * HAVE_CRL:                 Enable Certificate Revocation Lists
 * CRL_STATIC_REVOKED_LIST:  Use static list for revoked certs
 *
 * OCSP:
 * HAVE_OCSP:                Enable OCSP support
 * HAVE_OCSP_RESPONDER:      Enable OCSP responder support
 * WOLFSSL_OCSP_PARSE_STATUS: Parse OCSP response status
 *
 * PKCS:
 * HAVE_PKCS8:               Enable PKCS#8 support
 * HAVE_PKCS12:              Enable PKCS#12 support
 *
 * Algorithms (ASN encoding/decoding):
 * WOLFSSL_HAVE_MLDSA:       Enable ML-DSA ASN support
 * WOLFSSL_MLDSA_NO_ASN1:    Disable ML-DSA ASN.1 encoding
 * WOLFSSL_MLDSA_FIPS204_DRAFT: FIPS 204 draft ML-DSA
 * WOLFSSL_MLDSA_NO_SIGN:    Disable ML-DSA signing
 * WOLFSSL_MLDSA_NO_VERIFY:  Disable ML-DSA verify
 * HAVE_FALCON:              Enable Falcon ASN support
 * WOLFSSL_HAVE_SLHDSA:      Enable SLH-DSA ASN support
 *
 * Key Import/Export:
 * WC_ENABLE_ASYM_KEY_IMPORT: Enable asymmetric key import
 * WC_ENABLE_ASYM_KEY_EXPORT: Enable asymmetric key export
 *
 * Compatibility:
 * WOLFSSL_APACHE_HTTPD:     Apache HTTPD compatibility
 * WOLFSSL_X509_NAME_AVAILABLE: Enable X509_NAME API
 * WOLFSSL_HAVE_ISSUER_NAMES: Store issuer name components
 * WOLFSSL_ASN_KEY_SIZE_ENUM: Use enum for AES key size in ASN
 * WOLFSSL_SM3:              Enable SM3 hash ASN support
 * HAVE_SMIME:               Enable S/MIME support
 * HAVE_LIBZ:                Enable zlib compression for certs
 * WC_RC2:                   Enable RC2 for PKCS#12
 * WOLFSSL_MD2:              Enable MD2 hash (legacy)
*/

#ifndef NO_RSA
    #include <wolfssl/wolfcrypt/rsa.h>
    #if defined(WOLFSSL_XILINX_CRYPT) || defined(WOLFSSL_CRYPTOCELL)
        extern int wc_InitRsaHw(RsaKey* key);
    #endif
#endif

#ifndef NO_ASN

#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/md2.h>
#include <wolfssl/wolfcrypt/hmac.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
#include <wolfssl/wolfcrypt/des3.h>
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/rc2.h>
#include <wolfssl/wolfcrypt/wc_encrypt.h>

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

#ifndef NO_RC4
    #include <wolfssl/wolfcrypt/arc4.h>
#endif

#if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384)
    #include <wolfssl/wolfcrypt/sha512.h>
#endif

#ifndef NO_SHA256
    #include <wolfssl/wolfcrypt/sha256.h>
#endif

#ifdef HAVE_ECC
    #include <wolfssl/wolfcrypt/ecc.h>
#endif

#ifdef WOLFSSL_SM2
    #include <wolfssl/wolfcrypt/sm2.h>
#endif

#ifdef HAVE_ED25519
    #include <wolfssl/wolfcrypt/ed25519.h>
#endif
#ifdef HAVE_CURVE25519
    #include <wolfssl/wolfcrypt/curve25519.h>
#endif

#ifdef HAVE_ED448
    #include <wolfssl/wolfcrypt/ed448.h>
#endif
#ifdef HAVE_CURVE448
    #include <wolfssl/wolfcrypt/curve448.h>
#endif

#if defined(HAVE_FALCON)
    #include <wolfssl/wolfcrypt/falcon.h>
#endif
#if defined(WOLFSSL_HAVE_MLDSA)
    #include <wolfssl/wolfcrypt/wc_mldsa.h>
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
    #include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif

#ifdef WOLFSSL_QNX_CAAM
    #include <wolfssl/wolfcrypt/port/caam/wolfcaam.h>
#endif

#if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
    #include <wolfssl/wolfcrypt/port/Renesas/renesas_cmn.h>
#endif

#ifndef NO_DSA
    #include <wolfssl/wolfcrypt/dsa.h>
#else
    typedef void* DsaKey;
#endif

#ifdef WOLF_CRYPTO_CB
    #include <wolfssl/wolfcrypt/cryptocb.h>
#endif

#ifndef WOLFCRYPT_ONLY
    #include <wolfssl/internal.h>
#endif

#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    #include <wolfssl/openssl/objects.h>
#endif

#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
        !defined(WOLFCRYPT_ONLY)
    #define WOLFSSL_X509_NAME_AVAILABLE
#endif

#ifdef _MSC_VER
    /* 4996 warning to use MS extensions e.g., strcpy_s instead of XSTRNCPY */
    #pragma warning(disable: 4996)
#endif

#define ERROR_OUT(err, eLabel) { ret = (err); goto eLabel; }

/* Get the length of the ASN.1 encoded length.
 *
 * @param [in] len  Length of data.
 * @return  Length of ASN.1 encoded length.
 */
#define ASN_LEN_ENC_LEN(len) \
    (((len) < ASN_LONG_LENGTH) ? 1 : (1 + BytePrecision((word32)(len))))

#if !defined(NO_SKID) && (!defined(HAVE_FIPS) || !defined(HAVE_FIPS_VERSION))
    #if !defined(HAVE_SELFTEST) || (defined(HAVE_SELFTEST) && \
                                   (!defined(HAVE_SELFTEST_VERSION) || \
                                    HAVE_SELFTEST_VERSION < 2))
    #ifndef WOLFSSL_AES_KEY_SIZE_ENUM
    #define WOLFSSL_AES_KEY_SIZE_ENUM
    enum Asn_Misc {
        AES_IV_SIZE         = 16,
        AES_128_KEY_SIZE    = 16,
        AES_192_KEY_SIZE    = 24,
        AES_256_KEY_SIZE    = 32
    };
    #endif
    #endif /* HAVE_SELFTEST */
#endif

#if defined(WOLFSSL_ASN_PRINT) || defined(WOLFSSL_DEBUG_ASN_TEMPLATE)

/* String representations of tags. */
static const char* tagString[4][32] = {
    /* Universal */
    {
        "EOC",
        "BOOLEAN",
        "INTEGER",
        "BIT STRING",
        "OCTET STRING",
        "NULL",
        "OBJECT ID",
        "ObjectDescriptor",
        "INSTANCE OF",
        "REAL",
        "ENUMERATED",
        "EMBEDDED PDV",
        "UT8String",
        "RELATIVE-OID",
        "(0x0e) 14",
        "(0x0f) 15",
        "SEQUENCE",
        "SET",
        "NumericString",
        "PrintableString",
        "T61String",
        "VideotexString",
        "IA5String",
        "UTCTime",
        "GeneralizedTime",
        "GraphicString",
        "ISO646String",
        "GeneralString",
        "UniversalString",
        "CHARACTER STRING",
        "BMPString",
        "(0x1f) 31",
    },
    /* Application */
    {
         "[A 0]",  "[A 1]",  "[A 2]",  "[A 3]",
         "[A 4]",  "[A 5]",  "[A 6]",  "[A 7]",
         "[A 8]",  "[A 9]", "[A 10]", "[A 11]",
        "[A 12]", "[A 13]", "[A 14]", "[A 15]",
        "[A 16]", "[A 17]", "[A 18]", "[A 19]",
        "[A 20]", "[A 21]", "[A 22]", "[A 23]",
        "[A 24]", "[A 25]", "[A 26]", "[A 27]",
        "[A 28]", "[A 20]", "[A 30]", "[A 31]"
    },
    /* Context-Specific */
    {
         "[0]",  "[1]",  "[2]",  "[3]",  "[4]",  "[5]",  "[6]",  "[7]",
         "[8]",  "[9]", "[10]", "[11]", "[12]", "[13]", "[14]", "[15]",
        "[16]", "[17]", "[18]", "[19]", "[20]", "[21]", "[22]", "[23]",
        "[24]", "[25]", "[26]", "[27]", "[28]", "[20]", "[30]", "[31]"
    },
    /* Private */
    {
         "[P 0]",  "[P 1]",  "[P 2]",  "[P 3]",
         "[P 4]",  "[P 5]",  "[P 6]",  "[P 7]",
         "[P 8]",  "[P 9]", "[P 10]", "[P 11]",
        "[P 12]", "[P 13]", "[P 14]", "[P 15]",
        "[P 16]", "[P 17]", "[P 18]", "[P 19]",
        "[P 20]", "[P 21]", "[P 22]", "[P 23]",
        "[P 24]", "[P 25]", "[P 26]", "[P 27]",
        "[P 28]", "[P 20]", "[P 30]", "[P 31]"
    }
};

/* Converts a tag byte to string.
 *
 * @param [in] tag  BER tag value to interpret.
 * @return  String corresponding to tag.
 */
static const char* TagString(byte tag)
{
    return tagString[tag >> 6][tag & ASN_TYPE_MASK];
}

#endif


/* Calculates the minimum number of bytes required to encode the value.
 *
 * @param [in] value  Value to be encoded.
 * @return  Number of bytes to encode value.
 */
static word32 BytePrecision(word32 value)
{
    word32 i;
    for (i = (word32)sizeof(value); i; --i)
        if (value >> ((i - 1) * WOLFSSL_BIT_SIZE))
            break;

    return i;
}

/* DER encodes the length value in output buffer.
 *
 *    0 ->  2^7-1: <len byte>.
 *  2^7 ->       : <0x80 + #bytes> <len big-endian bytes>
 *
 * @param [in]      length  Value to encode.
 * @param [in, out] output  Buffer to encode into.
 * @return  Number of bytes used in encoding.
 */
WOLFSSL_LOCAL word32 SetASNLength(word32 length, byte* output)
{
    word32 i = 0;

    if (length < ASN_LONG_LENGTH)
        output[i++] = (byte)length;
    else {
        word32 j;

        output[i++] = (byte)(BytePrecision(length) | ASN_LONG_LENGTH);

        for (j = BytePrecision(length); j; --j) {
            output[i] = (byte)(length >> ((j - 1) * WOLFSSL_BIT_SIZE));
            i++;
        }
    }

    return i;
}

#ifdef WC_ASN_RUNTIME_DATE_CHECK_CONTROL
static int AsnSkipDateCheck = 0;
int wc_AsnSetSkipDateCheck(int skip_p)
{
    AsnSkipDateCheck = (skip_p != 0);
    return 0;
}
int wc_AsnGetSkipDateCheck(void)
{
    return AsnSkipDateCheck;
}
#else
#define AsnSkipDateCheck 0
int wc_AsnSetSkipDateCheck(int skip_p)
{
    (void)skip_p;
    return NOT_COMPILED_IN;
}
int wc_AsnGetSkipDateCheck(void)
{
    return 0;
}
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
/* Calculate the size of a DER encoded length value.
 *
 *    0 ->  2^7-1: <length byte>.
 *  2^7 ->       : <0x80 + #bytes> <big-endian length bytes>
 *
 * @param [in] length  Value to encode.
 * @return  Number of bytes required to encode.
 */
static word32 SizeASNLength(word32 length)
{
    return 1 + ((length >= ASN_LONG_LENGTH) ? BytePrecision(length) : 0);
}

/* Calculate the size of a DER encoded header.
 *
 * Header = Tag | Encoded length
 *
 * @param [in] length  Length value to encode.
 * @return  Number of bytes required to encode a DER header.
 */
#define SizeASNHeader(length) \
    (1 + SizeASNLength(length))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
#ifdef WOLFSSL_SMALL_STACK
    /* Declare the variable that is the dynamic data for decoding BER data.
     *
     * @param [in] name  Variable name to declare.
     * @param [in] cnt   Number of elements required.
     */
    #define DECL_ASNGETDATA(name, cnt)                                         \
        ASNGetData* name = NULL

    /* Allocates the dynamic BER decoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define ALLOC_ASNGETDATA(name, cnt, err, heap)                             \
    do {                                                                       \
        if ((err) == 0) {                                                      \
            (name) = (ASNGetData*)XMALLOC(sizeof(ASNGetData) * (cnt), (heap),  \
                                        DYNAMIC_TYPE_TMP_BUFFER);              \
            if ((name) == NULL) {                                              \
                (err) = MEMORY_E;                                              \
            }                                                                  \
        }                                                                      \
    }                                                                          \
    while (0)

    /* Allocates the dynamic BER decoding data and clears the memory.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define CALLOC_ASNGETDATA(name, cnt, err, heap)                            \
    do {                                                                       \
        ALLOC_ASNGETDATA(name, cnt, err, heap);                                \
        if ((err) == 0) {                                                      \
            XMEMSET((name), 0, sizeof(ASNGetData) * (cnt));                    \
        }                                                                      \
    }                                                                          \
    while (0)

    /* Disposes of the dynamic BER decoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define FREE_ASNGETDATA(name, heap)                                        \
    do {                                                                       \
        if ((name) != NULL) {                                                  \
            XFREE((name), (heap), DYNAMIC_TYPE_TMP_BUFFER);                    \
        }                                                                      \
    }                                                                          \
    while (0)

    /* Declare the variable that is the dynamic data for encoding DER data.
     *
     * @param [in] name  Variable name to declare.
     * @param [in] cnt   Number of elements required.
     */
    #define DECL_ASNSETDATA(name, cnt)                                         \
        ASNSetData* name = NULL

    /* Allocates the dynamic DER encoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define ALLOC_ASNSETDATA(name, cnt, err, heap)                             \
    do {                                                                       \
        if ((err) == 0) {                                                      \
            (name) = (ASNSetData*)XMALLOC(sizeof(ASNSetData) * (cnt), (heap),  \
                                    DYNAMIC_TYPE_TMP_BUFFER);                  \
            if ((name) == NULL) {                                              \
                (err) = MEMORY_E;                                              \
            }                                                                  \
        }                                                                      \
    }                                                                          \
    while (0)

    /* Allocates the dynamic DER encoding data and clears the memory.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define CALLOC_ASNSETDATA(name, cnt, err, heap)                            \
    do {                                                                       \
        ALLOC_ASNSETDATA(name, cnt, err, heap);                                \
        if ((err) == 0) {                                                      \
            XMEMSET(name, 0, sizeof(ASNSetData) * (cnt));                      \
        }                                                                      \
    }                                                                          \
    while (0)

    /* Disposes of the dynamic DER encoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define FREE_ASNSETDATA(name, heap)                                        \
    do {                                                                       \
        if ((name) != NULL) {                                                  \
            XFREE(name, heap, DYNAMIC_TYPE_TMP_BUFFER);                        \
        }                                                                      \
    }                                                                          \
    while (0)
#else
    /* Declare the variable that is the dynamic data for decoding BER data.
     *
     * @param [in] name  Variable name to declare.
     * @param [in] cnt   Number of elements required.
     */
    #define DECL_ASNGETDATA(name, cnt)                  \
        ASNGetData name[cnt]

    /* No implementation as declaration is static.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define ALLOC_ASNGETDATA(name, cnt, err, heap) \
        do { (void)(cnt); (void)(err); (void)(heap); } while (0)

    /* Clears the memory of the dynamic BER encoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define CALLOC_ASNGETDATA(name, cnt, err, heap)     \
        ((void)(cnt), (void)(err), (void)(heap), XMEMSET(name, 0, sizeof(name)))

    /* No implementation as declaration is static.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define FREE_ASNGETDATA(name, heap) \
        do { (void)(name); (void)(heap); } while (0)

    /* Declare the variable that is the dynamic data for encoding DER data.
     *
     * @param [in] name  Variable name to declare.
     * @param [in] cnt   Number of elements required.
     */
    #define DECL_ASNSETDATA(name, cnt)                  \
        ASNSetData name[cnt]

    /* No implementation as declaration is static.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define ALLOC_ASNSETDATA(name, cnt, err, heap) \
        do { (void)(cnt); (void)(err); (void)(heap); } while (0)

    /* Clears the memory of the dynamic BER encoding data.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      cnt   Number of elements required.
     * @param [in, out] err   Error variable.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define CALLOC_ASNSETDATA(name, cnt, err, heap)     \
        ((void)(cnt), (void)(err), (void)(heap), XMEMSET(name, 0, sizeof(name)))

    /* No implementation as declaration is static.
     *
     * @param [in]      name  Variable name to declare.
     * @param [in]      heap  Dynamic memory allocation hint.
     */
    #define FREE_ASNSETDATA(name, heap) \
        do { (void)(name); (void)(heap); } while (0)
#endif


#ifdef DEBUG_WOLFSSL
    /* Enable this when debugging the parsing or creation of ASN.1 data. */
    #if 0
        #define WOLFSSL_DEBUG_ASN_TEMPLATE
    #endif
#endif

#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE

#include <stdarg.h>

/* Log a message that has the printf format string.
 *
 * @param [in] <va_args>  printf style arguments.
 */
#define WOLFSSL_MSG_VSNPRINTF(...)                    \
    do {                                              \
      char line[81];                                  \
      snprintf(line, sizeof(line) - 1, __VA_ARGS__);  \
      line[sizeof(line) - 1] = '\0';                  \
      WOLFSSL_MSG(line);                              \
    }                                                 \
    while (0)
#endif

/* Returns whether ASN.1 item is an integer and the Most-Significant Bit is set.
 *
 * @param [in] asn     ASN.1 items to encode.
 * @param [in] data_a  Data to place in each item. Lengths set were not known.
 * @param [in] i       Index of item to check.
 * @return  1 when ASN.1 item is an integer and MSB is 1.
 * @return  0 otherwise.
 */
#define ASNIntMSBSet(asn, data_a, i)                  \
    (((asn)[i].tag == ASN_INTEGER) &&                 \
      ((data_a)[i].data.buffer.data != NULL &&        \
      ((data_a)[i].data.buffer.length > 0) &&         \
      ((data_a)[i].data.buffer.data[0] & 0x80) == 0x80))


/* Calculate the size of a DER encoded number.
 *
 * @param [in] n     Number to be encoded.
 * @param [in] bits  Maximum number of bits to encode.
 * @param [in] tag   BER tag e.g. INTEGER, BIT_STRING, etc.
 * @return  Number of bytes to the ASN.1 item.
 */
static word32 SizeASN_Num(word32 n, int bits, byte tag)
{
    int    j;
    word32 len;

    len = 1 + 1 + (word32)bits / 8;
    /* Discover actual size by checking for high zeros. */
    for (j = bits - 8; j > 0; j -= 8) {
        if (n >> j)
            break;
        len--;
    }
    if (tag == ASN_BIT_STRING)
        len++;
    else if ((tag == ASN_INTEGER) && (((n >> j) & 0x80) == 0x80))
        len++;

    return len;
}

/* Calculate the size of the data in the constructed item based on the
 * length of the ASN.1 items below.
 *
 * @param [in]      asn    ASN.1 items to encode.
 * @param [in, out] data   Data to place in each item. Lengths set were not
 *                         known.
 * @param [in]      idx    Index of item working on.
 */
static void SizeASN_CalcDataLength(const ASNItem* asn, ASNSetData *data,
                                   int idx, int maxIdx)
{
    int j;

    data[idx].data.buffer.length = 0;
    /* Sum the item length of all items underneath. */
    for (j = idx + 1; j < maxIdx; j++) {
        /* Stop looking if the next ASN.1 is same level or higher. */
        if (asn[j].depth <= asn[idx].depth)
            break;
        /* Only add in length if it is one level below. */
        if (asn[j].depth - 1 == asn[idx].depth) {
            data[idx].data.buffer.length += data[j].length;
            /* The length of a header only item doesn't include the data unless
             * a replacement buffer is supplied.
             */
            if (asn[j].headerOnly && data[j].data.buffer.data == NULL &&
                    data[j].dataType != ASN_DATA_TYPE_REPLACE_BUFFER) {
                data[idx].data.buffer.length += data[j].data.buffer.length;
            }
        }
    }
}

/* Calculate the size of the DER encoding.
 *
 * Call SetASN_Items() to write encoding to a buffer.
 *
 * @param [in]      asn    ASN.1 items to encode.
 * @param [in, out] data   Data to place in each item. Lengths set where not
 *                         known.
 * @param [in]      count  Count of items to encode.
 * @param [out]     encSz  Length of the DER encoding.
 * @return  0 on success.
 * @return  BAD_STATE_E when the data type is not supported.
 */
int SizeASN_Items(const ASNItem* asn, ASNSetData *data, int count,
    word32* encSz)
{
    int    i;
    word32 sz = 0;
    word32 len;
    word32 dataLen;
    int    length;

#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
    WOLFSSL_ENTER("SizeASN_Items");
#endif

    if (asn == NULL || data == NULL || count <= 0 || encSz == NULL) {
        WOLFSSL_MSG("bad arguments in SizeASN_Items");
        return BAD_FUNC_ARG;
    }

    for (i = count - 1; i >= 0; i--) {
        /* Skip this ASN.1 item when encoding. */
        if (data[i].noOut) {
            /* Set the offset to the current size - used in writing DER. */
            data[i].offset = sz;
            continue;
        }

        len = 0;
        switch (data[i].dataType) {
            /* Calculate the size of the number of different sizes. */
            case ASN_DATA_TYPE_WORD8:
                len = SizeASN_Num(data[i].data.u8, 8, asn[i].tag);
                break;
            case ASN_DATA_TYPE_WORD16:
                len = SizeASN_Num(data[i].data.u16, 16, asn[i].tag);
                break;
        #ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
            /* Not used yet! */
            case ASN_DATA_TYPE_WORD32:
                len = SizeASN_Num(data[i].data.u32, 32, asn[i].tag);
                break;
        #endif

            case ASN_DATA_TYPE_MP:
                /* Calculate the size of the MP integer data. */
                length = mp_unsigned_bin_size(data[i].data.mp);
                if (length < 0) {
                    return ASN_PARSE_E;
                }
                length += mp_leading_bit(data[i].data.mp) ? 1 : 0;
                len = (word32)SizeASNHeader((word32)length) + (word32)length;
                /* Check for overflow: header + length must not wrap word32. */
                if (len < (word32)length) {
                    return ASN_PARSE_E;
                }
                break;

            case ASN_DATA_TYPE_REPLACE_BUFFER:
                /* Buffer is put in directly - use the length. */
                len = data[i].data.buffer.length;
                break;

            case ASN_DATA_TYPE_NONE:
                /* Calculate the size based on the data to be included.
                 * Mostly used for constructed items.
                 */
                if (asn[i].headerOnly) {
                    if (data[i].data.buffer.data != NULL) {
                        /* Force all child nodes to be ignored. Buffer
                         * overwrites children. */
                        {
                            int ii;
                            for (ii = i + 1; ii < count; ii++) {
                                if (asn[ii].depth <= asn[i].depth)
                                    break;
                                sz -= data[ii].length;
                                data[ii].noOut = 1;
                            }
                        }
                    }
                    else {
                        /* Calculate data length from items below if no buffer
                         * supplied. */
                        SizeASN_CalcDataLength(asn, data, i, count);
                    }
                }
                if (asn[i].tag == ASN_BOOLEAN) {
                    dataLen = 1;
                }
                else {
                    dataLen = data[i].data.buffer.length;
                }
                /* BIT_STRING and INTEGER have one byte prepended. */
                if ((asn[i].tag == ASN_BIT_STRING) ||
                                                   ASNIntMSBSet(asn, data, i)) {
                    dataLen++;
                    /* ASN.1 items are below and cannot include extra byte. */
                    if (asn[i].headerOnly) {
                        len++;
                    }
                }
                /* Add in the size of tag and length. */
                len += SizeASNHeader(dataLen);
                /* Include data in length if not header only or if
                 * buffer supplied. */
                if (!asn[i].headerOnly || data[i].data.buffer.data != NULL) {
                    len += dataLen;
                }
                break;

        #ifdef DEBUG_WOLFSSL
            default:
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("%2d: %d", i, data[i].dataType);
                WOLFSSL_MSG("Bad data type");
            #endif
                return BAD_STATE_E;
        #endif
        }

        /* Set the total length of the item. */
        data[i].length = len;
        /* Add length to total size. */
        sz += len;
        if (sz < len)
            return ASN_PARSE_E;
        /* Set the offset to the current size - used in writing DER. */
        data[i].offset = sz;

    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("%2d: %4d %4d %c %*s %-16s", i,
                data[i].offset, data[i].length, asn[i].constructed ? '+' : ' ',
                asn[i].depth, "", TagString(asn[i].tag));
    #endif
    }

    *encSz = sz;
    return 0;
}

/* Create the DER encoding of a number.
 *
 * Assumes that the out buffer is large enough for encoding.
 *
 * @param [in] n     Number to be encoded.
 * @param [in] bits  Maximum number of bits to encode.
 * @param [in] tag   DER tag e.g. INTEGER, BIT_STRING, etc.
 */
static void SetASN_Num(word32 n, int bits, byte* out, byte tag)
{
    int    j;
    word32 idx;
    byte   len;

    /* Encoding: Tag (1 byte) | Length (1 byte) | Data (number) */

    /* Data will start at index 2 unless BIT_STRING or INTEGER */
    idx = 2;

    /* Set the length of the number based on maximum bit length. */
    len = (byte)(bits / 8);
    /* Discover actual size by checking for leading zero bytes. */
    for (j = bits - 8; j > 0; j -= 8) {
        if ((n >> j) != 0) {
            break;
        }
        len--;
    }
    /* Keep j, index of first non-zero byte, for writing out. */

    /* A BIT_STRING has the number of unused bits in last byte prepended to
     * data.
     */
    if (tag == ASN_BIT_STRING) {
        byte unusedBits = 0;
        byte lastByte = (byte)(n >> j);

        /* Quick check last bit. */
        if ((lastByte & 0x01) == 0x00) {
            unusedBits++;
            /* Check each bit for first least significant bit set. */
            while (((lastByte >> unusedBits) & 0x01) == 0x00)
                unusedBits++;
        }
        /* Add unused bits byte. */
        len++;
        out[idx++] = unusedBits;
    }

    /* An INTEGER has a prepended byte if MSB of number is 1 - makes encoded
     * value positive. */
    if ((tag == ASN_INTEGER) && (((n >> j) & 0x80) == 0x80)) {
        len++;
        out[idx++] = 0;
    }

    /* Go back and put in length. */
    out[1] = len;
    /* Place in the required bytes of the number. */
    for (; j >= 0; j -= 8)
        out[idx++] = (byte)(n >> j);
}

/* Creates the DER encoding of the ASN.1 items.
 *
 * Assumes the output buffer is large enough to hold encoding.
 * Must call SizeASN_Items() to determine size of encoding and offsets.
 *
 * @param [in]      asn     ASN.1 items to encode.
 * @param [in]      data    Data to place in each item.
 * @param [in]      count   Count of items to encode.
 * @param [in, out] output  Buffer to write encoding into.
 * @return  Size of the DER encoding in bytes.
 */
int SetASN_Items(const ASNItem* asn, ASNSetData *data, int count, byte* output)
{
    int    i;
    int    length;
    int    err;
    word32 sz;
    word32 idx;
    byte*  out;

#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
    WOLFSSL_ENTER("SetASN_Items");
#endif

    /* Offset of first item is the total length.
     * SizeASN_Items() calculated this. */
    sz = data[0].offset;

    /* Write out each item. */
    for (i = 0; i < count; i++) {
        /* Skip items not writing out. */
        if (data[i].noOut)
            continue;

        /* Start position to write item based on reverse offsets. */
        out = output + sz - data[i].offset;
        /* Index from start of item out. */
        idx = 0;

        if (data[i].dataType != ASN_DATA_TYPE_REPLACE_BUFFER) {
            /* Put in the tag - not dumping in DER from buffer. */
            out[idx++] = asn[i].tag |
                         (asn[i].constructed ? ASN_CONSTRUCTED : 0);
        }

    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("%2d: %4d %4d %c %*s %-16s", i,
                sz - data[i].offset,
                data[i].length, asn[i].constructed ? '+' : ' ', asn[i].depth,
                "", TagString(asn[i].tag));
    #endif

        switch (data[i].dataType) {
            /* Write out the length and data of a number. */
            case ASN_DATA_TYPE_WORD8:
                SetASN_Num(data[i].data.u8, 8, out, asn[i].tag);
                break;
            case ASN_DATA_TYPE_WORD16:
                SetASN_Num(data[i].data.u16, 16, out, asn[i].tag);
                break;
        #ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
            /* Not used yet! */
            case ASN_DATA_TYPE_WORD32:
                SetASN_Num(data[i].data.u32, 32, out, asn[i].tag);
                break;
        #endif

            /* Write out the length and data of a multi-precision number. */
            case ASN_DATA_TYPE_MP:
                /* Get length in bytes. */
                length = mp_unsigned_bin_size(data[i].data.mp);
                /* Add one for leading zero to make encoding a positive num. */
                length += mp_leading_bit(data[i].data.mp) ? 1 : 0;
                /* Write out length. */
                idx += SetASNLength((word32)length, out + idx);
                /* Write out leading zero to make positive. */
                if (mp_leading_bit(data[i].data.mp)) {
                    out[idx++] = 0;
                }
                /* Encode number in big-endian byte array. */
                err = mp_to_unsigned_bin(data[i].data.mp, out + idx);
                if (err != MP_OKAY) {
                    WOLFSSL_MSG("SetASN_Items: Failed to write mp_int");
                    return MP_TO_E;
                }
                break;

            case ASN_DATA_TYPE_REPLACE_BUFFER:
                if (data[i].data.buffer.data == NULL) {
                    /* Return pointer for caller to use. */
                    data[i].data.buffer.data = out + idx;
                }
                else {
                    /* Dump in the DER encoded data. */
                    /* Allow data to come from output buffer. */
                    XMEMMOVE(out + idx, data[i].data.buffer.data,
                             data[i].data.buffer.length);
                }
                break;

            case ASN_DATA_TYPE_NONE:
                if (asn[i].tag == ASN_BOOLEAN) {
                    /* Always one byte of data. */
                    out[idx++] = 1;
                    /* TRUE = 0xff, FALSE = 0x00 */
                    out[idx] = data[i].data.u8 ? 0xffU : 0x00U;
                }
                else if (asn[i].tag == ASN_TAG_NULL) {
                    /* NULL tag is always a zero length item. */
                    out[idx] = 0;
                }
                else {
                    word32 dataLen = data[i].data.buffer.length;
                    /* Add one to data length for BIT_STRING unused bits and
                     * INTEGER leading zero to make positive.
                     */
                    if ((asn[i].tag == ASN_BIT_STRING) ||
                                                   ASNIntMSBSet(asn, data, i)) {
                        dataLen++;
                    }
                    /* Write out length. */
                    idx += SetASNLength(dataLen, out + idx);
                    if ((asn[i].tag == ASN_BIT_STRING) ||
                                                   ASNIntMSBSet(asn, data, i)) {
                       /* Write out leading byte. BIT_STRING has no unused bits
                        * - use number data types if needed. */
                        out[idx++] = 0x00;
                    }
                    /* Record pointer for caller if data not supplied. */
                    if (data[i].data.buffer.data == NULL) {
                        data[i].data.buffer.data = out + idx;
                    }
                    /* Copy supplied data if not putting out header only or
                     * if buffer supplied. */
                    else if (!asn[i].headerOnly ||
                            data[i].data.buffer.data != NULL) {
                        /* Allow data to come from output buffer. */
                        XMEMMOVE(out + idx, data[i].data.buffer.data,
                                 data[i].data.buffer.length);
                    }
                }
                break;

        #ifdef DEBUG_WOLFSSL
            default:
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Bad data type: %d", data[i].dataType);
            #endif
                return BAD_STATE_E;
        #endif
        }
    }

    return (int)sz;
}


static int GetOID(const byte* input, word32* inOutIdx, word32* oid,
                  word32 oidType, int length);

/* Maximum supported depth in ASN.1 description. */
#define GET_ASN_MAX_DEPTH          8
/* Maximum number of checked numbered choices. Only one of the items with the
 * number is allowed.
 */
#define GET_ASN_MAX_CHOICES        2

/* Use existing function to decode BER length encoding. */
#define GetASN_Length GetLength_ex

/* Check an INTEGER's first byte - must be a positive number.
 *
 * @param [in] input    BER encoded data.
 * @param [in] idx      Index of BIT_STRING data.
 * @param [in] length   Length of input data.
 * @param [in] positive Indicates number must be positive.
 * @return  0 on success.
 * @return  ASN_PARSE_E when 0 is not required but seen.
 * @return  ASN_EXPECT_0_E when 0 is required and not seen.
 */
static int GetASN_Integer(const byte* input, word32 idx, int length,
                          int positive)
{
#if (!defined(HAVE_SELFTEST) && !defined(HAVE_FIPS)) || \
    (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))
    /* Check contents consist of one or more octets. */
    if (length == 0) {
        WOLFSSL_MSG("Zero length INTEGER not allowed");
        return ASN_PARSE_E;
    }
#endif
    if (input[idx] == 0) {
        /* Check leading zero byte required. */
        if ((length > 1) && ((input[idx + 1] & 0x80) == 0)) {
            WOLFSSL_MSG("Zero not required on INTEGER");
        #ifndef WOLFSSL_ASN_INT_LEAD_0_ANY
            return ASN_PARSE_E;
        #endif
        }
    }
    /* check for invalid padding on negative integer.
     * c.f. X.690 (ISO/IEC 8825-2:2003 (E)) 10.4.6; RFC 5280 4.1
     */
    else if ((length > 1) && (input[idx] == 0xff) &&
             ((input[idx + 1] & 0x80) != 0)) {
        WOLFSSL_MSG("Bad INTEGER encoding of negative");
    #ifndef WOLFSSL_ASN_INT_LEAD_0_ANY
        return ASN_EXPECT_0_E;
    #endif /* WOLFSSL_ASN_INT_LEAD_0_ANY */
    }
    /* Check whether a leading zero byte was required. */
    else if (positive && (input[idx] & 0x80)) {
        WOLFSSL_MSG("INTEGER is negative");
    #ifndef WOLFSSL_ASN_INT_LEAD_0_ANY
        return ASN_EXPECT_0_E;
    #endif /* WOLFSSL_ASN_INT_LEAD_0_ANY */
    }

    return 0;
}

/* Check a BIT_STRING's first byte - unused bits.
 *
 * @param [in] input   BER encoded data.
 * @param [in] idx     Index of BIT_STRING data.
 * @param [in] length  Length of input data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when unused bits is invalid.
 */
int GetASN_BitString(const byte* input, word32 idx, int length)
{
    /* Check contents consist of one or more octets. */
    if (length == 0) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG("Zero length BIT STRING not allowed");
    #endif
        return ASN_PARSE_E;
    }
    /* Ensure unused bits value is valid range. */
    if (input[idx] > 7) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("BIT STRING unused bits too big: %d > 7",
                input[idx]);
    #endif
        return ASN_PARSE_E;
    }
    /* Ensure unused bits are zero. */
    if ((byte)(input[idx + (word32)length - 1] << (8 - input[idx])) != 0) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("BIT STRING unused bits used: %d %02x",
                input[idx], input[idx + length - 1]);
    #endif
        return ASN_PARSE_E;
    }

    return 0;
}

#ifndef WOLFSSL_NO_ASN_STRICT
/* Check a UTF8STRING's data is valid.
 *
 * @param [in] input   BER encoded data.
 * @param [in] idx     Index of UTF8STRING data.
 * @param [in] length  Length of input data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when data is invalid.
 */
static int GetASN_UTF8String(const byte* input, word32 idx, int length)
{
    int ret = 0;
    word32 i = 0;

    while ((ret == 0) && ((int)i < length)) {
        int cnt;

        /* Check code points and get count of following bytes. */
        if ((input[idx + i] & 0x80) == 0x00) {
            cnt = 0;
        }
        else if ((input[idx + i] & 0xe0) == 0xc0) {
            cnt = 1;
        }
        else if ((input[idx + i] & 0xf0) == 0xe0) {
            cnt = 2;
        }
        else if ((input[idx + i] & 0xf8) == 0xf0) {
            cnt = 3;
        }
        else {
            WOLFSSL_MSG("Invalid character in UTF8STRING\n");
            ret = ASN_PARSE_E;
            break;
        }

        /* Have checked first byte. */
        i++;
        /* Check each following byte. */
        for (; cnt > 0; cnt--) {
            /* Check we have enough data. */
            if ((int)i == length) {
                WOLFSSL_MSG("Missing character in UTF8STRING\n");
                ret = ASN_PARSE_E;
                break;
            }
            /* Check following byte has top bit set. */
            if ((input[idx + i] & 0x80) != 0x80) {
                WOLFSSL_MSG("Invalid character in UTF8STRING\n");
                ret = ASN_PARSE_E;
                break;
            }
            i++;
        }
    }

    return ret;
}
#endif

/* Check an OBJECT IDENTIFIER's data is valid.
 *
 * X.690 8.19
 *
 * @param [in] input   BER encoded data.
 * @param [in] idx     Index of OBJECT IDENTIFIER data.
 * @param [in] length  Length of input data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when data is invalid.
 */
static int GetASN_ObjectId(const byte* input, word32 idx, int length)
{
    int ret = 0;

    /* OID data must be at least 3 bytes. */
    if (length < 3) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("OID length must be 3 or more: %d", length);
    #else
        WOLFSSL_MSG("OID length less than 3");
    #endif
        ret = ASN_PARSE_E;
    }
    /* Last octet of a sub-identifier has bit 8 clear. Last octet must be last
     * of a subidentifier. Ensure last octet hasn't got top bit set.
     */
    else if ((input[(int)idx + length - 1] & 0x80) == 0x80) {
        WOLFSSL_MSG("OID last octet has top bit set");
        ret = ASN_PARSE_E;
    }

    return ret;
}

/* Get the ASN.1 items from the BER encoding.
 *
 * @param [in] asn         ASN.1 item expected.
 * @param [in] data        Data array to place found item into.
 * @param [in] input       BER encoded data.
 * @param [in] idx         Starting index of item data.
 * @param [in] len         Length of input buffer upto end of this item's data.
 * @param [in] zeroPadded  INTEGER was zero padded to make positive.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data is invalid.
 * @return  ASN_EXPECT_0_E when NULL tagged item has a non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 * @return  BAD_STATE_E when the data type is not supported.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
static int GetASN_StoreData(const ASNItem* asn, ASNGetData* data,
                            const byte* input, word32 idx, int len,
                            int zeroPadded)
{
    int i;
    int err;

    /* Parse data based on data type to extract. */
    switch (data->dataType) {
        /* Parse a data into a number of specified bits. */
        case ASN_DATA_TYPE_WORD8:
            /* Check data is small enough to fit. */
            if (len != 1) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Expecting one byte: %d", len);
            #endif
                return ASN_PARSE_E;
            }
            if ((asn->tag != ASN_BOOLEAN) && (!zeroPadded) &&
                (input[idx] >= 0x80U)) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Unexpected negative INTEGER value");
            #endif
                return ASN_EXPECT_0_E;
            }
            /* Fill number with all of data. */
            *data->data.u8 = input[idx];
            break;
        case ASN_DATA_TYPE_WORD16:
            /* Check data is small enough to fit. */
            if (len == 0 || len > 2) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Expecting 1 or 2 bytes: %d", len);
            #endif
                return ASN_PARSE_E;
            }
            if (!zeroPadded && (input[idx] >= 0x80U)) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Unexpected negative INTEGER value");
            #endif
                return ASN_EXPECT_0_E;
            }
            /* Fill number with all of data. */
            *data->data.u16 = 0;
            for (i = 0; i < len; i++) {
                *data->data.u16 = (word16)(*data->data.u16 << 8U);
                *data->data.u16 = (word16)(*data->data.u16 | input[idx + (word32)i]);
            }
            break;
        case ASN_DATA_TYPE_WORD32:
            /* Check data is small enough to fit. */
            if (len == 0 || len > 4) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Expecting 1 to 4 bytes: %d", len);
            #endif
                return ASN_PARSE_E;
            }
            if (!zeroPadded && (input[idx] >= 0x80U)) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Unexpected negative INTEGER value");
            #endif
                return ASN_EXPECT_0_E;
            }
            /* Fill number with all of data. */
            *data->data.u32 = 0;
            for (i = 0; i < len; i++) {
                *data->data.u32 <<= 8;
                *data->data.u32 |= input[idx + (word32)i] ;
            }
            break;

        case ASN_DATA_TYPE_BUFFER:
            /* Check buffer is big enough to hold data. */
            if (len > (int)*data->data.buffer.length) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Buffer too small for data: %d %d", len,
                        *data->data.buffer.length);
            #endif
                return BUFFER_E;
            }
            /* Copy in data and record actual length seen. */
            XMEMCPY(data->data.buffer.data, input + idx, (size_t)len);
            *data->data.buffer.length = (word32)len;
            break;

        case ASN_DATA_TYPE_EXP_BUFFER:
            /* Check data is same size expected. */
            if (len != (int)data->data.ref.length) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Data not expected length: %d %d", len,
                        data->data.ref.length);
            #endif
                return ASN_PARSE_E;
            }
            /* Check data is same as expected. */
            if (XMEMCMP(data->data.ref.data, input + idx, (size_t)len) != 0) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG("Data not as expected");
            #endif
                return ASN_PARSE_E;
            }
            break;

        case ASN_DATA_TYPE_MP:
        case ASN_DATA_TYPE_MP_POS_NEG:
            /* Initialize mp_int and read in big-endian byte array. */
            if (mp_init(data->data.mp) != MP_OKAY) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Failed to init mp: %p", data->data.mp);
            #endif
                return MP_INIT_E;
            }
            FALL_THROUGH;
        case ASN_DATA_TYPE_MP_INITED:
            err = mp_read_unsigned_bin(data->data.mp, (const byte*)input + idx,
                                       (word32)len);
            if (err != 0) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Failed to read mp: %d", err);
            #endif
                mp_clear(data->data.mp);
                return ASN_GETINT_E;
            }
        #ifdef HAVE_WOLF_BIGINT
            err = wc_bigint_from_unsigned_bin(&data->data.mp->raw, input + idx,
                    len);
            if (err != 0) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Failed to create bigint: %d", err);
            #endif
                mp_clear(data->data.mp);
                return ASN_GETINT_E;
            }
        #endif /* HAVE_WOLF_BIGINT */

        #ifdef WOLFSSL_SP_INT_NEGATIVE
            /* Don't always read as positive. */
            if ((data->dataType == ASN_DATA_TYPE_MP_POS_NEG) && (!zeroPadded) &&
                (input[idx] & 0x80)) {
                #ifdef MP_NEG
                    data->data.mp->sign = MP_NEG;
                #else
                    #ifdef OPENSSL_EXTRA
                        /* public API wolfSSL_ASN1_INTEGER_get() depends
                         * indirectly on negative bignum handling here.
                         */
                        #error OPENSSL_EXTRA requires negative bignum support.
                    #endif
                    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                    WOLFSSL_MSG_VSNPRINTF("ASN negative integer without bignum support.");
                    #endif
                    mp_clear(data->data.mp);
                    return ASN_GETINT_E;
                #endif
            }
        #else
            (void)zeroPadded;
        #endif
            break;

        case ASN_DATA_TYPE_CHOICE:
            /* Check if tag matched any of the choices specified. */
            for (i = 0; data->data.choice[i] != 0; i++)
                if (data->data.choice[i] == data->tag)
                    break;
            if (data->data.choice[i] == 0) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG("Tag didn't match a choice");
            #endif
                return ASN_PARSE_E;
            }

            /* Store data pointer and length for caller. */
            data->data.ref.data = input + idx;
            data->data.ref.length = (word32)len;
            break;

        case ASN_DATA_TYPE_NONE:
            /* Default behaviour based on tag. */
            if (asn->tag == ASN_BOOLEAN) {
                /* BOOLEAN has only one byte of data in BER. */
                if (len != 1) {
                #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                    WOLFSSL_MSG_VSNPRINTF("BOOLEAN length too long: %d", len);
                #endif
                    return ASN_PARSE_E;
                }
                if (data->data.u8 == NULL)
                    return BAD_STATE_E;
                /* Store C boolean value. */
                *data->data.u8 = (input[idx] != 0);
                break;
            }
            if (asn->tag == ASN_TAG_NULL) {
                /* NULL has no data in BER. */
                if (len != 0) {
                #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                    WOLFSSL_MSG_VSNPRINTF("NULL length too long: %d", len);
                #endif
                    return ASN_EXPECT_0_E;
                }
                data->data.ref.data = input + idx;
                break;
            }
            if (asn->tag == ASN_OBJECT_ID) {
                word32 oidIdx = 0;
                /* Store OID data pointer and length */
                data->data.oid.data = input + idx;
                data->data.oid.length = (word32)len;
                /* Get the OID sum. */
                err = GetOID(input + idx, &oidIdx, &data->data.oid.sum,
                        data->data.oid.type, len);
                if (err < 0) {
                #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                    WOLFSSL_MSG_VSNPRINTF("OID check failed: %d", err);
                #endif
                    return err;
                }
                break;
            }

            /* Otherwise store data pointer and length. */
            data->data.ref.data = input + idx;
            data->data.ref.length = (word32)len;
            break;

    #ifdef DEBUG_WOLFSSL
        default:
            /* Bad ASN data type. */
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG_VSNPRINTF("Bad data type: %d", data->dataType);
        #endif
            return BAD_STATE_E;
    #endif
    }

    return 0;
}

/* Get the ASN.1 items from the BER encoding.
 *
 * @param [in]      asn       ASN.1 items expected.
 * @param [in]      data      Data array to place found items into.
 * @param [in]      count     Count of items to parse.
 * @param [in]      complete  Whether the whole buffer is to be used up.
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of data.
 *                            On out, end of parsed data.
 * @param [in]      length    Length of input buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 * @return  BAD_STATE_E when the data type is not supported.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
int GetASN_Items(const ASNItem* asn, ASNGetData *data, int count, int complete,
                 const byte* input, word32* inOutIdx, const word32 length)
{
    int    i;
    int    j;
    int    err;
    int    len;
    /* Current index into buffer. */
    word32 idx = *inOutIdx;
    /* Declare the end index array. */
    word32 endIdx[GET_ASN_MAX_DEPTH];
    /* Set choices to -1 to indicate they haven't been seen or found. */
    signed char   choiceMet[GET_ASN_MAX_CHOICES] = { -1, -1 };
    /* Not matching a choice right now. */
    int    choice = 0;
    /* Current depth of ASN.1 item. */
    int    depth;
    /* Minimum depth value seen. */
    int    minDepth;
    /* Integer had a zero prepended. */
    int    zeroPadded;
    word32 tmpW32Val;
    signed char tmpScharVal;

#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
    WOLFSSL_ENTER("GetASN_Items");
#endif

    /* Set the end index at each depth to be the length. */
    for (i=0; i<GET_ASN_MAX_DEPTH; i++) {
        endIdx[i] = length;
    }

    /* Start depth at first items depth. */
    minDepth = depth = asn[0].depth;
    /* Check every ASN.1 item. */
    for (i = 0; i < count; i++) {
        /* Store offset of ASN.1 item. */
        data[i].offset = idx;
        /* Length of data in ASN.1 item starts empty. */
        data[i].length = 0;
        /* Get current item depth. */
        depth = asn[i].depth;
        if (depth >= GET_ASN_MAX_DEPTH) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG("Depth in template too large");
    #endif
            return ASN_PARSE_E;
        }
        /* Keep track of minimum depth. */
        if (depth < minDepth) {
            minDepth = depth;
        }

        /* Reset choice if different from previous. */
        if (choice > 0 && asn[i].optional != choice) {
            choice = 0;
        }
        /* Check if first of numbered choice. */
        if (choice == 0 && asn[i].optional > 1) {
            choice = asn[i].optional;
            tmpScharVal = choiceMet[choice - 2];
            XFENCE(); /* Prevent memory access */
            if (tmpScharVal == -1) {
                /* Choice seen but not found a match yet. */
                choiceMet[choice - 2] = 0;
            }
        }

        /* Check for end of data or not a choice and tag not matching. */
        tmpW32Val = endIdx[depth];
        XFENCE(); /* Prevent memory access */
        if (idx == tmpW32Val || (data[i].dataType != ASN_DATA_TYPE_CHOICE &&
                              (input[idx] & ~ASN_CONSTRUCTED) != asn[i].tag)) {
            if (asn[i].optional) {
                /* Skip over ASN.1 items underneath this optional item. */
                for (j = i + 1; j < count; j++) {
                    if (asn[i].depth >= asn[j].depth)
                        break;
                    data[j].offset = idx;
                    data[j].length = 0;
                }
                i = j - 1;
                continue;
            }

            /* Check for end of data. */
            if (idx == length) {
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF(
                    "%2d: %4d %4d %c %*s %-16s%*s  (index past end)",
                    i, data[i].offset, data[i].length,
                    asn[i].constructed ? '+' : ' ', asn[i].depth, "",
                    TagString(asn[i].tag), 6 - asn[i].depth, "");
                WOLFSSL_MSG_VSNPRINTF("Index past end of data: %d %d", idx,
                        length);
        #endif
                return BUFFER_E;
            }
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            /* Show expected versus found. */
            WOLFSSL_MSG_VSNPRINTF(
                "%2d: %4d %4d %c %*s %-16s%*s  Tag=0x%02x (%s)",
                i, data[i].offset, data[i].length,
                asn[i].constructed ? '+' : ' ', asn[i].depth, "",
                TagString(asn[i].tag), 6 - asn[i].depth, "",
                input[idx], TagString(input[idx]));
        #endif
            /* Check for end of data at this depth. */
            if (idx == endIdx[depth]) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF("Index past outer item: %d %d", idx,
                        endIdx[depth]);
            #endif
                return ASN_PARSE_E;
            }

            /* Expecting an OBJECT_ID */
            if (asn[i].tag == ASN_OBJECT_ID) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG("Expecting OBJECT ID");
            #endif
                return ASN_OBJECT_ID_E;
            }
            /* Expecting a BIT_STRING */
            if (asn[i].tag == ASN_BIT_STRING) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG("Expecting BIT STRING");
            #endif
                return ASN_BITSTR_E;
            }
            /* Not the expected tag. */
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG("Bad tag");
        #endif
            return ASN_PARSE_E;
        }

        /* Store found tag in data. */
        data[i].tag = input[idx];
        XFENCE(); /* Prevent memory access */
        if (data[i].dataType != ASN_DATA_TYPE_CHOICE) {
            int constructed = (input[idx] & ASN_CONSTRUCTED) == ASN_CONSTRUCTED;
            /* Check constructed match expected for non-choice ASN.1 item. */
            if (asn[i].constructed != constructed) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF(
                        "%2d: %4d %4d %c %*s %-16s%*s  Tag=0x%02x (%s)",
                        i, data[i].offset, data[i].length,
                        asn[i].constructed ? '+' : ' ', asn[i].depth, "",
                        TagString(asn[i].tag), 6 - asn[i].depth, "",
                        input[idx], TagString(input[idx]));
                if (!constructed) {
                    WOLFSSL_MSG("Not constructed");
                }
                else {
                    WOLFSSL_MSG("Not expected to be constructed");
                }
            #endif
                return ASN_PARSE_E;
            }
        }
        /* Move index to start of length. */
        idx++;
        /* Get the encoded length. */
        if (GetASN_Length(input, &idx, &len, endIdx[depth], 1) < 0) {
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG_VSNPRINTF("%2d: idx=%d len=%d end=%d", i, idx, len,
                    endIdx[depth]);
        #endif
            return ASN_PARSE_E;
        }
        /* Store length of data. */
        data[i].length = (word32)len;
        if (depth + 1 >= GET_ASN_MAX_DEPTH) {
    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG("Depth in template too large");
    #endif
            return ASN_PARSE_E;
        }
        /* Note the max length of items under this one. */
        endIdx[depth + 1] = idx + (word32)len;
        if (choice > 1) {
            /* Note we found a number choice. */
            choiceMet[choice - 2] = 1;
        }

    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        WOLFSSL_MSG_VSNPRINTF("%2d: %4d %4d %c %*s %-16s", i,
                data[i].offset, data[i].length, asn[i].constructed ? '+' : ' ',
                asn[i].depth, "", TagString(data[i].tag));
    #endif

        /* Assume no zero padding on INTEGER. */
        zeroPadded = 0;
        /* Check data types that prepended a byte. */
        if (asn[i].tag == ASN_INTEGER) {
            /* Check validity of first byte. */
            err = GetASN_Integer(input, idx, len,
                    data[i].dataType == ASN_DATA_TYPE_MP ||
                    data[i].dataType == ASN_DATA_TYPE_MP_INITED);
            if (err != 0)
                return err;
            if (len > 1 && input[idx] == 0) {
                zeroPadded = 1;
                /* Move over prepended byte. */
                idx++;
                len--;
            }
        }
        else if (asn[i].tag == ASN_BIT_STRING) {
            /* Check prepended byte is correct. */
            err = GetASN_BitString(input, idx, len);
            if (err != 0)
                return err;
            /* Move over prepended byte. */
            idx++;
            len--;
        }
    #ifndef WOLFSSL_NO_ASN_STRICT
        else if ((asn[i].tag == ASN_UTF8STRING) ||
                 (data[i].tag == ASN_UTF8STRING)) {
            /* Check validity of data. */
            err = GetASN_UTF8String(input, idx, len);
            if (err != 0)
                return err;
        }
    #endif
        else if (asn[i].tag == ASN_OBJECT_ID) {
            /* Check validity of data. */
            err = GetASN_ObjectId(input, idx, len);
            if (err != 0)
                return err;
        }

        /* Don't parse data if only header required. */
        if (asn[i].headerOnly) {
            /* Store reference to data and length. */
            data[i].data.ref.data = input + idx;
            data[i].data.ref.length = (word32)len;
            continue;
        }

        /* Store the data at idx in the ASN data item. */
        err = GetASN_StoreData(&asn[i], &data[i], input, idx, len, zeroPadded);
        if (err != 0) {
            return err;
        }

        /* Move index to next item. */
        idx += (word32)len;

        /* When matched numbered choice ... */
        if (asn[i].optional > 1) {
            /* Skip over other ASN.1 items of the same number. */
            for (j = i + 1; j < count; j++) {
                if (asn[j].depth <= asn[i].depth &&
                                           asn[j].optional != asn[i].optional) {
                   break;
                }
            }
            i = j - 1;
        }
    }

    if (complete) {
        /* When expecting ASN.1 items to completely use data, check we did. */
        for (j = depth; j > minDepth; j--) {
            if (idx < endIdx[j]) {
            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
                WOLFSSL_MSG_VSNPRINTF(
                    "More data in constructed item at depth: %d", j - 1);
            #endif
                return ASN_PARSE_E;
            }
        }
    }

    /* Check all choices where met - found an item for them. */
    for (j = 0; j < GET_ASN_MAX_CHOICES; j++) {
        if (choiceMet[j] == 0) {
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG_VSNPRINTF("No choice seen: %d", j + 2);
        #endif
            return ASN_PARSE_E;
        }
    }

    /* Return index after ASN.1 data has been parsed. */
    *inOutIdx = idx;

    return 0;
}

#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
/* Calculate the size of the DER encoding.
 *
 * Call SetASN_Items() to write encoding to a buffer.
 *
 * @param [in]      asn    ASN.1 items to encode.
 * @param [in, out] data   Data to place in each item. Lengths set were not
 *                         known.
 * @param [in]      count  Count of items to encode.
 * @param [out]     len    Length of the DER encoding.
 * @return  Size of the DER encoding in bytes.
 */
static int SizeASN_ItemsDebug(const char* name, const ASNItem* asn,
    ASNSetData *data, int count, word32* encSz)
{
    WOLFSSL_MSG_VSNPRINTF("TEMPLATE: %s", name);
    return SizeASN_Items(asn, data, count, encSz);
}

/* Creates the DER encoding of the ASN.1 items.
 *
 * Assumes the output buffer is large enough to hold encoding.
 * Must call SizeASN_Items() to determine size of encoding and offsets.
 *
 * Displays the template name first.
 *
 * @param [in]      name    Name of ASN.1 template.
 * @param [in]      asn     ASN.1 items to encode.
 * @param [in]      data    Data to place in each item.
 * @param [in]      count   Count of items to encode.
 * @param [in, out] output  Buffer to write encoding into.
 * @return  Size of the DER encoding in bytes.
 */
static int SetASN_ItemsDebug(const char* name, const ASNItem* asn,
    ASNSetData *data, int count, byte* output)
{
    WOLFSSL_MSG_VSNPRINTF("TEMPLATE: %s", name);
    return SetASN_Items(asn, data, count, output);
}

/* Get the ASN.1 items from the BER encoding.
 *
 * Displays the template name first.
 *
 * @param [in]      name      Name of ASN.1 template.
 * @param [in]      asn       ASN.1 items expected.
 * @param [in]      data      Data array to place found items into.
 * @param [in]      count     Count of items to parse.
 * @param [in]      complete  Whether the whole buffer is to be used up.
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of data.
 *                            On out, end of parsed data.
 * @param [in]      maxIdx    Maximum index of input data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 * is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 * @return  BAD_STATE_E when the data type is not supported.
 */
static int GetASN_ItemsDebug(const char* name, const ASNItem* asn,
    ASNGetData *data, int count, int complete, const byte* input,
    word32* inOutIdx, word32 maxIdx)
{
    WOLFSSL_MSG_VSNPRINTF("TEMPLATE: %s", name);
    return GetASN_Items(asn, data, count, complete, input, inOutIdx, maxIdx);
}

/* Calculate the size of the DER encoding.
 *
 * Call SetASN_Items() to write encoding to a buffer.
 *
 * @param [in]      asn    ASN.1 items to encode.
 * @param [in, out] data   Data to place in each item. Lengths set were not
 *                         known.
 * @param [in]      count  Count of items to encode.
 * @param [out]     len    Length of the DER encoding.
 * @return  Size of the DER encoding in bytes.
 */
#define SizeASN_Items(asn, data, count, encSz)  \
    SizeASN_ItemsDebug(#asn, asn, data, count, encSz)

/* Creates the DER encoding of the ASN.1 items.
 *
 * Assumes the output buffer is large enough to hold encoding.
 * Must call SizeASN_Items() to determine size of encoding and offsets.
 *
 * Displays the template name first.
 *
 * @param [in]      name    Name of ASN.1 template.
 * @param [in]      asn     ASN.1 items to encode.
 * @param [in]      data    Data to place in each item.
 * @param [in]      count   Count of items to encode.
 * @param [in, out] output  Buffer to write encoding into.
 * @return  Size of the DER encoding in bytes.
 */
#define SetASN_Items(asn, data, count, output)  \
    SetASN_ItemsDebug(#asn, asn, data, count, output)

/* Get the ASN.1 items from the BER encoding.
 *
 * Displays the template name first.
 *
 * @param [in]      name      Name of ASN.1 template.
 * @param [in]      asn       ASN.1 items expected.
 * @param [in]      data      Data array to place found items into.
 * @param [in]      count     Count of items to parse.
 * @param [in]      complete  Whether the whole buffer is to be used up.
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of data.
 *                            On out, end of parsed data.
 * @param [in]      maxIdx    Maximum index of input data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 * is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 * @return  BAD_STATE_E when the data type is not supported.
 */
#define GetASN_Items(asn, data, count, complete, input, inOutIdx, maxIdx)  \
    GetASN_ItemsDebug(#asn, asn, data, count, complete, input, inOutIdx, maxIdx)
#endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */

/* Decode a BER encoded constructed sequence.
 *
 * @param [in]       input     Buffer of BER encoded data.
 * @param [in, out]  inOutIdx  On in, index to start decoding from.
 *                             On out, index of next encoded byte.
 * @param [out]      len       Length of data under SEQUENCE.
 * @param [in]       maxIdx    Maximum index of data. Index of byte after SEQ.
 * @param [in]       complete  All data used with SEQUENCE and data under.
 * @return  0 on success.
 * @return  BUFFER_E when not enough data to complete decode.
 * @return  ASN_PARSE when decoding failed.
 */
static int GetASN_Sequence(const byte* input, word32* inOutIdx, int* len,
                           word32 maxIdx, int complete)
{
    int ret = 0;
    word32 idx = *inOutIdx;

    /* Check buffer big enough for tag. */
    if (idx + 1 > maxIdx) {
        ret = BUFFER_E;
    }
    /* Check it is a constructed SEQUENCE. */
    if ((ret == 0) && (input[idx++] != (ASN_SEQUENCE | ASN_CONSTRUCTED))) {
        ret = ASN_PARSE_E;
    }
    /* Get the length. */
    if ((ret == 0) && (GetASN_Length(input, &idx, len, maxIdx, 1) < 0)) {
        ret = ASN_PARSE_E;
    }
    /* Check all data used if complete set. */
    if ((ret == 0) && complete && (idx + (word32)*len != maxIdx)) {
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Return index of next byte of encoded data. */
        *inOutIdx = idx;
    }

    return ret;
}


#ifdef WOLFSSL_ASN_TEMPLATE_TYPE_CHECK
/* Setup ASN data item to get an 8-bit number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Pointer to an 8-bit variable.
 */
void GetASN_Int8Bit(ASNGetData *dataASN, byte* num)
{
    dataASN->dataType = ASN_DATA_TYPE_WORD8;
    dataASN->data.u8  = num;
}

/* Setup ASN data item to get a 16-bit number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Pointer to a 16-bit variable.
 */
void GetASN_Int16Bit(ASNGetData *dataASN, word16* num)
{
    dataASN->dataType = ASN_DATA_TYPE_WORD16;
    dataASN->data.u16 = num;
}

/* Setup ASN data item to get a 32-bit number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Pointer to a 32-bit variable.
 */
void GetASN_Int32Bit(ASNGetData *dataASN, word32* num)
{
    dataASN->dataType = ASN_DATA_TYPE_WORD32;
    dataASN->data.u32 = num;
}

/* Setup ASN data item to get data into a buffer of a specific length.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] data     Buffer to hold data.
 * @param [in] length   Length of buffer in bytes.
 */
void GetASN_Buffer(ASNGetData *dataASN, byte* data, word32* length)
{
    dataASN->dataType           = ASN_DATA_TYPE_BUFFER;
    dataASN->data.buffer.data   = data;
    dataASN->data.buffer.length = length;
}

/* Setup ASN data item to check parsed data against expected buffer.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] data     Buffer containing expected data.
 * @param [in] length   Length of buffer in bytes.
 */
void GetASN_ExpBuffer(ASNGetData *dataASN, const byte* data, word32 length)
{
    dataASN->dataType        = ASN_DATA_TYPE_EXP_BUFFER;
    dataASN->data.ref.data   = data;
    dataASN->data.ref.length = length;
}

/* Setup ASN data item to get a number into an mp_int.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Multi-precision number object.
 */
void GetASN_MP(ASNGetData *dataASN, mp_int* num)
{
    dataASN->dataType = ASN_DATA_TYPE_MP;
    dataASN->data.mp  = num;
}

/* Setup ASN data item to get a number into an mp_int that is initialized.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Multi-precision number object.
 */
void GetASN_MP_Inited(ASNGetData *dataASN, mp_int* num)
{
    dataASN->dataType = ASN_DATA_TYPE_MP_INITED;
    dataASN->data.mp  = num;
}

/* Setup ASN data item to get a positive or negative number into an mp_int.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Multi-precision number object.
 */
void GetASN_MP_PosNeg(ASNGetData *dataASN, mp_int* num)
{
    dataASN->dataType = ASN_DATA_TYPE_MP_POS_NEG;
    dataASN->data.mp  = num;
}

/* Setup ASN data item to be a choice of tags.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] options  0 terminated list of tags that are valid.
 */
void GetASN_Choice(ASNGetData *dataASN, const byte* options)
{
    dataASN->dataType    = ASN_DATA_TYPE_CHOICE;
    dataASN->data.choice = options;
}

/* Setup ASN data item to get a boolean value.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Pointer to an 8-bit variable.
 */
void GetASN_Boolean(ASNGetData *dataASN, byte* num)
{
    dataASN->dataType    = ASN_DATA_TYPE_NONE;
    dataASN->data.choice = num;
}

/* Setup ASN data item to be a an OID of a specific type.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] oidType  Type of OID to expect.
 */
void GetASN_OID(ASNGetData *dataASN, int oidType)
{
    dataASN->data.oid.type = oidType;
}

/* Get the data and length from an ASN data item.
 *
 * @param [in]  dataASN  Dynamic ASN data item.
 * @param [out] data     Pointer to data of item.
 * @param [out] length   Length of buffer in bytes.
 */
void GetASN_GetConstRef(ASNGetData * dataASN, const byte** data, word32* length)
{
    *data   = dataASN->data.ref.data;
    *length = dataASN->data.ref.length;
}

/* Get the data and length from an ASN data item.
 *
 * @param [in]  dataASN  Dynamic ASN data item.
 * @param [out] data     Pointer to data of item.
 * @param [out] length   Length of buffer in bytes.
 */
void GetASN_GetRef(const ASNGetData * dataASN, const byte** data,
                   word32* length)
{
    *data   = (const byte*)dataASN->data.ref.data;
    *length =        dataASN->data.ref.length;
}

/* Get the data and length from an ASN data item that is an OID.
 *
 * @param [in]  dataASN  Dynamic ASN data item.
 * @param [out] data     Pointer to .
 * @param [out] length   Length of buffer in bytes.
 */
void GetASN_OIDData(const ASNGetData * dataASN, const byte** data,
                    word32* length)
{
    *data   = (const byte*)dataASN->data.oid.data;
    *length =        dataASN->data.oid.length;
}

/* Setup an ASN data item to set a boolean.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] val      Boolean value.
 */
void SetASN_Boolean(ASNSetData *dataASN, byte val)
{
    dataASN->dataType = ASN_DATA_TYPE_NONE;
    dataASN->data.u8  = val;
}

/* Setup an ASN data item to set an 8-bit number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      8-bit number to set.
 */
void SetASN_Int8Bit(ASNSetData *dataASN, byte num)
{
    dataASN->dataType = ASN_DATA_TYPE_WORD8;
    dataASN->data.u8  = num;
}

/* Setup an ASN data item to set a 16-bit number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      16-bit number to set.
 */
void SetASN_Int16Bit(ASNSetData *dataASN, word16 num)
{
    dataASN->dataType = ASN_DATA_TYPE_WORD16;
    dataASN->data.u16 = num;
}

/* Setup an ASN data item to set the data in a buffer.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] data     Buffer containing data to set.
 * @param [in] length   Length of data in buffer in bytes.
 */
void SetASN_Buffer(ASNSetData *dataASN, const byte* data, word32 length)
{
    dataASN->data.buffer.data   = data;
    dataASN->data.buffer.length = length;
}

/* Setup an ASN data item to set the DER encode data in a buffer.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] data     Buffer containing BER encoded data to set.
 * @param [in] length   Length of data in buffer in bytes.
 */
void SetASN_ReplaceBuffer(ASNSetData *dataASN, const byte* data, word32 length)
{
    dataASN->dataType           = ASN_DATA_TYPE_REPLACE_BUFFER;
    dataASN->data.buffer.data   = data;
    dataASN->data.buffer.length = length;
}

/* Setup an ASN data item to set an multi-precision number.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] num      Multi-precision number.
 */
void SetASN_MP(ASNSetData *dataASN, mp_int* num)
{
    dataASN->dataType = ASN_DATA_TYPE_MP;
    dataASN->data.mp  = num;
}

/* Setup an ASN data item to set an OID based on id and type.
 *
 * oid and oidType pair are unique.
 *
 * @param [in] dataASN  Dynamic ASN data item.
 * @param [in] oid      OID identifier.
 * @param [in] oidType  Type of OID.
 */
void SetASN_OID(ASNSetData *dataASN, int oid, int oidType)
{
    dataASN->data.buffer.data = OidFromId(oid, oidType,
                                                  &dataASN->data.buffer.length);
}
#endif /* WOLFSSL_ASN_TEMPLATE_TYPE_CHECK */

#ifdef CRLDP_VALIDATE_DATA
/* Get the data of the BIT_STRING as a 16-bit number.
 *
 * @param [in]  dataASN  Dynamic ASN data item.
 * @param [out] val      ASN.1 item's data as a 16-bit number.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BITSTRING value is more than 2 bytes.
 * @return  ASN_PARSE_E when unused bits of BITSTRING is invalid.
 */
static int GetASN_BitString_Int16Bit(ASNGetData* dataASN, word16* val)
{
    int ret;
    int i;
    const byte* input = dataASN->data.ref.data;
    int length = dataASN->data.ref.length;

    /* Validate the BIT_STRING data. */
    ret = GetASN_BitString(input, 0, length);
    if (ret == 0) {
        /* Skip unused bits byte. */
        input++;
        length--;

        /* Check the data is usable. */
        if (length == 0 || length > 2) {
#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            WOLFSSL_MSG_VSNPRINTF("Expecting 1 or 2 bytes: %d", length);
#endif
            ret = ASN_PARSE_E;
        }
    }
    if (ret == 0) {
        /* Fill 16-bit var with all the data. */
        *val = 0;
        for (i = 0; i < length; i++) {
            *val <<= 8;
            *val |= input[i];
        }
    }
    return ret;
}
#endif /* CRLDP_VALIDATE_DATA */

#endif /* WOLFSSL_ASN_TEMPLATE */


/* Decode the BER/DER length field.
 *
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of length.
 *                            On out, end of parsed length.
 * @param [out]     len       Length value decoded.
 * @param [in]      maxIdx    Maximum index of input data.
 * @return  Length on success.
 * @return  ASN_PARSE_E if the encoding is invalid.
 * @return  BUFFER_E when not enough data to complete decode.
 */
int GetLength(const byte* input, word32* inOutIdx, int* len, word32 maxIdx)
{
    return GetLength_ex(input, inOutIdx, len, maxIdx, 1);
}


/* Decode the BER/DER length field and check the length is valid on request.
 *
 * BER/DER has Type-Length-Value triplets.
 * When requested will check that the Length decoded, indicating the number
 * of bytes in the Value, is available in the buffer after the Length bytes.
 *
 * Only supporting a length upto INT_MAX.
 *
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of length.
 *                            On out, end of parsed length.
 * @param [out]     len       Length value decoded.
 * @param [in]      maxIdx    Maximum index of input data.
 * @param [in]      check     Whether to check the buffer has at least the
 *                            decoded length of bytes remaining.
 * @return  Length on success.
 * @return  ASN_PARSE_E if the encoding is invalid.
 * @return  BUFFER_E when not enough data to complete decode.
 */
int GetLength_ex(const byte* input, word32* inOutIdx, int* len, word32 maxIdx,
                 int check)
{
    word32 length = 0;
    word32 idx = (word32)*inOutIdx;
    byte   b;

    /* Ensure zero return length on error. */
    *len = 0;

    /* Check there is at least one byte available containing length information.
     */
    if ((idx + 1) > maxIdx) {
        WOLFSSL_MSG("GetLength - bad index on input");
        return BUFFER_E;
    }

    /* Get the first length byte. */
    b = input[idx++];
    /* Check if the first byte indicates the count of bytes. */
    if (b >= ASN_LONG_LENGTH) {
        /* Bottom 7 bits are the number of bytes to calculate length with.
         * Note: 0 indicates indefinite length encoding *not* 0 bytes of length.
         */
        int bytes = (int)(b & 0x7F);
        word32 minLen;

        /* Calculate minimum length to be encoded with bytes. */
        if (b == ASN_INDEF_LENGTH) {
            /* Indefinite length encoding - no length bytes. */
            minLen = 0;
        }
        else if (bytes == 1) {
            minLen = 0x80;
        }
        /* Only support up to the number of bytes that fit into return var. */
        else if (bytes > (int)sizeof(length)) {
            WOLFSSL_MSG("GetLength - overlong data length spec");
            return ASN_PARSE_E;
        }
        else {
            minLen = (word32)1 << ((bytes - 1) * 8);
        }

        /* Check the number of bytes required are available. */
        if ((idx + (word32)bytes) > maxIdx) {
            WOLFSSL_MSG("GetLength - bad long length");
            return BUFFER_E;
        }

        /* Big-endian encoding of number. */
        while (bytes--) {
            b = input[idx++];
            length = (length << 8) | b;
        }
        /* Top bit set means value too big for signed int. */
        if ((length >> 31) == 1) {
            return ASN_PARSE_E;
        }
        /* Don't allow lengths that are longer than strictly required. */
        if (length < minLen) {
            return ASN_PARSE_E;
        }
    }
    else {
        /* Length in first byte. */
        length = b;
    }

    /* When requested, check the buffer has at least length bytes left. */
    if (check && ((idx + length) > maxIdx)) {
        WOLFSSL_MSG("GetLength - value exceeds buffer length");
        return BUFFER_E;
    }

    /* Return index after length encoding. */
    *inOutIdx = idx;
    /* Return length if valid. */
    if (length > 0) {
        *len = (int)length;
    }

    /* Return length calculated or error code. */
    return (int)length;
}


/* Gets the tag of next BER/DER encoded item.
 *
 * Checks there is enough data in the buffer for the tag byte.
 *
 * @param [in]      input     BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of tag.
 *                            On out, end of parsed tag.
 * @param [out]     tag       Tag value found.
 * @param [in]      maxIdx    Maximum index of input data.
 *
 * return  0 on success
 * return  BAD_FUNC_ARG when tag, inOutIdx or input is NULL.
 * return  BUFFER_E when not enough space in buffer for tag.
 */
int GetASNTag(const byte* input, word32* inOutIdx, byte* tag, word32 maxIdx)
{
    int ret = 0;
    word32 idx = 0;

    /* Check validity of parameters. */
    if ((tag == NULL) || (inOutIdx == NULL) || (input == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Get index and ensure space for tag. */
        idx = *inOutIdx;
        if (idx + ASN_TAG_SZ > maxIdx) {
            WOLFSSL_MSG("Buffer too small for ASN tag");
            ret = BUFFER_E;
        }
    }
    if (ret == 0) {
        /* Return the tag and the index after tag. */
        *tag = input[idx];
        *inOutIdx = idx + ASN_TAG_SZ;
    }
    /* Return error code. */
    return ret;
}


/* Decode the DER/BER header (Type-Length) and check the length when requested.
 *
 * BER/DER has Type-Length-Value triplets.
 * Check that the tag/type is the required value.
 * When requested will check that the Length decoded, indicating the number
 * of bytes in the Value, is available in the buffer after the Length bytes.
 *
 * Only supporting a length upto INT_MAX.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in]      tag       ASN.1 tag value expected in header.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @param [in]      check     Whether to check the buffer has at least the
 *                            decoded length of bytes remaining.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the expected tag is not found or length is invalid.
 */
static int GetASNHeader_ex(const byte* input, byte tag, word32* inOutIdx,
                           int* len, word32 maxIdx, int check)
{
    int    ret = 0;
    word32 idx = *inOutIdx;
    byte   tagFound;
    int    length = 0;

    /* Get tag/type. */
    if (GetASNTag(input, &idx, &tagFound, maxIdx) != 0) {
        ret = ASN_PARSE_E;
    }
    /* Ensure tag is the expected value. */
    if ((ret == 0) && (tagFound != tag)) {
        ret = ASN_PARSE_E;
    }
    /* Get the encoded length. */
    if ((ret == 0) && (GetLength_ex(input, &idx, &length, maxIdx, check) < 0)) {
        ret = ASN_PARSE_E;
    }
    if (ret == 0 && tag == ASN_OBJECT_ID) {
        if (length < 3) {
            /* OID data must be at least 3 bytes. */
            WOLFSSL_MSG("OID length less than 3");
            ret = ASN_PARSE_E;
        }
        /* Check enough space for data if not checking in GetLength_ex(). */
        else if ((!check) && ((word32)length > maxIdx - idx)) {
            WOLFSSL_MSG("OID length too long");
            ret = ASN_PARSE_E;
        }
        else if ((input[(int)idx + length - 1] & 0x80) == 0x80) {
            /* Last octet of a sub-identifier has bit 8 clear. Last octet must
             * be last of a subidentifier. Ensure last octet hasn't got top bit
             * set. */
            WOLFSSL_MSG("OID last octet has top bit set");
            ret = ASN_PARSE_E;
        }
    }
    if (ret == 0) {
        /* Return the length of data and index after header. */
        *len      = length;
        *inOutIdx = idx;
        ret = length;
    }
    /* Return number of data bytes or error code. */
    return ret;
}


/* Decode the DER/BER header (Type-Length) and check the length.
 *
 * BER/DER has Type-Length-Value triplets.
 * Check that the tag/type is the required value.
 * Checks that the Length decoded, indicating the number of bytes in the Value,
 * is available in the buffer after the Length bytes.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in]      tag       ASN.1 tag value expected in header.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the expected tag is not found or length is invalid.
 */
int GetASNHeader(const byte* input, byte tag, word32* inOutIdx, int* len,
                        word32 maxIdx)
{
    return GetASNHeader_ex(input, tag, inOutIdx, len, maxIdx, 1);
}

#if !defined(WOLFSSL_ASN_TEMPLATE)
static int GetHeader(const byte* input, byte* tag, word32* inOutIdx, int* len,
                     word32 maxIdx, int check)
{
    word32 idx = *inOutIdx;
    int    length;

    if ((idx + 1) > maxIdx)
        return BUFFER_E;

    *tag = input[idx++];

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

    *len      = length;
    *inOutIdx = idx;
    return length;
}
#endif

/* Decode the header of a BER/DER encoded SEQUENCE.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a SEQUENCE or length is invalid.
 */
int GetSequence(const byte* input, word32* inOutIdx, int* len,
                           word32 maxIdx)
{
    return GetASNHeader(input, ASN_SEQUENCE | ASN_CONSTRUCTED, inOutIdx, len,
                        maxIdx);
}

/* Decode the header of a BER/DER encoded SEQUENCE.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @param [in]      check     Whether to check the buffer has at least the
 *                            decoded length of bytes remaining.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a SEQUENCE or length is invalid.
 */
int GetSequence_ex(const byte* input, word32* inOutIdx, int* len,
                           word32 maxIdx, int check)
{
    return GetASNHeader_ex(input, ASN_SEQUENCE | ASN_CONSTRUCTED, inOutIdx, len,
                        maxIdx, check);
}

/**
 * Index a SEQUENCE OF object to get to a specific element.
 *
 * @param[in] seqOf Buffer holding DER/BER SEQUENCE OF object.
 * @param[in] seqOfSz Size of the seqOf SEQUENCE OF object.
 * @param[in] seqIndex Index of the SEQUENCE OF element being requested.
 * @param[out] out Buffer in which to store pointer to the <seqIndex>th element
 * of the SEQUENCE OF object.
 * @param[out] outSz Buffer in which to store the length of the <seqIndex>th
 * element of the SEQUENCE OF object.
 *
 * @return 0 on success.
 * @return BUFFER_E when there is not enough data to parse.
 * @return BAD_INDEX_E when the given seqIndex is out of range.
 * @return ASN_PARSE_E when the seqOf is not in the expected format.
 */
int wc_IndexSequenceOf(const byte * seqOf, word32 seqOfSz, size_t seqIndex,
        const byte ** out, word32 * outSz)
{
    int length;
    word32 seqOfIdx = 0U;
    byte tagFound;
    size_t i;
    word32 elementIdx = 0U;
    int ret = 0;

    /* Validate the SEQUENCE OF header. */
    if (GetSequence(seqOf, &seqOfIdx, &length, seqOfSz) < 0) {
        ret = ASN_PARSE_E;
    }
    else {
        seqOfSz = seqOfIdx + (word32)length;

        for (i = 0U; i <= seqIndex; i++) {
            if (seqOfIdx >= seqOfSz) {
                ret = BAD_INDEX_E;
                break;
            }

            elementIdx = seqOfIdx;

            /* Validate the element tag. */
            if (GetASNTag(seqOf, &seqOfIdx, &tagFound, seqOfSz) != 0) {
                ret = ASN_PARSE_E;
                break;
            }

            /* Validate and get the element's encoded length. */
            if (GetLength(seqOf, &seqOfIdx, &length, seqOfSz) < 0) {
                ret = ASN_PARSE_E;
                break;
            }

            seqOfIdx += (word32)length;
        }
    }

    /* If the tag and length checks above passed then we've found the requested
     * element and validated it fits within seqOfSz. */
    if (ret == 0) {
        *out = &seqOf[elementIdx];
        *outSz = (seqOfIdx - elementIdx);
    }

    return ret;
}

/* Decode the header of a BER/DER encoded SET.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a SET or length is invalid.
 */
int GetSet(const byte* input, word32* inOutIdx, int* len,
                        word32 maxIdx)
{
    return GetASNHeader(input, ASN_SET | ASN_CONSTRUCTED, inOutIdx, len,
                        maxIdx);
}

/* Decode the header of a BER/DER encoded SET.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @param [in]      check     Whether to check the buffer has at least the
 *                            decoded length of bytes remaining.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a SET or length is invalid.
 */
int GetSet_ex(const byte* input, word32* inOutIdx, int* len,
                        word32 maxIdx, int check)
{
    return GetASNHeader_ex(input, ASN_SET | ASN_CONSTRUCTED, inOutIdx, len,
                        maxIdx, check);
}

#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_OCSP)
/* Decode the BER/DER encoded NULL.
 *
 * No data in a NULL ASN.1 item.
 * Ensure that the all fields are as expected and move index past the element.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of NULL item.
 *                            On out, end of parsed NULL item.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  0 on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_TAG_NULL_E when the NULL tag is not found.
 * @return  ASN_EXPECT_0_E when the length is not zero.
 */
static int GetASNNull(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    int ret = 0;
    word32 idx = *inOutIdx;

    /* Check buffer has enough data for a NULL item. */
    if ((idx + 2) > maxIdx) {
        ret = BUFFER_E;
    }
    /* Check the tag is NULL. */
    if ((ret == 0) && (input[idx++] != ASN_TAG_NULL)) {
        ret = ASN_TAG_NULL_E;
    }
    /* Check the length is zero. */
    if ((ret == 0) && (input[idx++] != 0)) {
        ret = ASN_EXPECT_0_E;
    }
    if (ret == 0) {
        /* Return the index after NULL tag. */
        *inOutIdx = idx;
    }
    /* Return error code. */
    return ret;
}
#endif

#ifndef WOLFSSL_ASN_TEMPLATE
/* Set the DER/BER encoding of the ASN.1 NULL element.
 *
 * output  Buffer to write into.
 * returns the number of bytes added to the buffer.
 */
static int SetASNNull(byte* output)
{
    output[0] = ASN_TAG_NULL;
    output[1] = 0;

    return 2;
}
#endif

#ifndef NO_CERTS
#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_CRL)
/* Get the DER/BER encoding of an ASN.1 BOOLEAN.
 *
 * input     Buffer holding DER/BER encoded data.
 * inOutIdx  Current index into buffer to parse.
 * maxIdx    Length of data in buffer.
 * returns BUFFER_E when there is not enough data to parse.
 *         ASN_PARSE_E when the BOOLEAN tag is not found or length is not 1.
 *         Otherwise, 0 to indicate the value was false and 1 to indicate true.
 */
WC_MAYBE_UNUSED static int GetBoolean(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    byte   b;

    if ((idx + 3) > maxIdx)
        return BUFFER_E;

    b = input[idx++];
    if (b != ASN_BOOLEAN)
        return ASN_PARSE_E;

    if (input[idx++] != 1)
        return ASN_PARSE_E;

    b = input[idx++] != 0;

    *inOutIdx = idx;
    return b;
}
#endif /* !WOLFSSL_ASN_TEMPLATE || HAVE_CRL */
#endif /* !NO_CERTS*/


/* Decode the header of a BER/DER encoded OCTET STRING.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  Number of bytes in the ASN.1 data on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a OCTET STRING or length is invalid.
 */
int GetOctetString(const byte* input, word32* inOutIdx, int* len, word32 maxIdx)
{
    return GetASNHeader(input, ASN_OCTET_STRING, inOutIdx, len, maxIdx);
}

/* Get the DER/BER encoding of an ASN.1 INTEGER header.
 *
 * Removes the leading zero byte when found.
 *
 * input     Buffer holding DER/BER encoded data.
 * inOutIdx  Current index into buffer to parse.
 * len       The number of bytes in the ASN.1 data (excluding any leading zero).
 * maxIdx    Length of data in buffer.
 * returns BUFFER_E when there is not enough data to parse.
 *         ASN_PARSE_E when the INTEGER tag is not found, length is invalid,
 *         or invalid use of or missing leading zero.
 *         Otherwise, 0 to indicate success.
 */
int GetASNInt(const byte* input, word32* inOutIdx, int* len,
                     word32 maxIdx)
{
    int    ret;

    ret = GetASNHeader(input, ASN_INTEGER, inOutIdx, len, maxIdx);
    if (ret < 0)
        return ret;

    if (*len > 0) {
#ifndef WOLFSSL_ASN_INT_LEAD_0_ANY
        /* check for invalid padding on negative integer.
         * c.f. X.690 (ISO/IEC 8825-2:2003 (E)) 10.4.6; RFC 5280 4.1
         */
        if (*len > 1) {
            if ((input[*inOutIdx] == 0xff) && (input[*inOutIdx + 1] & 0x80)) {
                WOLFSSL_MSG("Bad INTEGER encoding of negative");
                return ASN_EXPECT_0_E;
            }
        }
#endif

        /* remove leading zero, unless there is only one 0x00 byte */
        if ((input[*inOutIdx] == 0x00) && (*len > 1)) {
            (*inOutIdx)++;
            (*len)--;

#ifndef WOLFSSL_ASN_INT_LEAD_0_ANY
            if (*len > 0 && (input[*inOutIdx] & 0x80) == 0) {
                WOLFSSL_MSG("INTEGER is negative");
                return ASN_EXPECT_0_E;
            }
#endif
        }
    }

    return 0;
}

#ifndef WOLFSSL_ASN_TEMPLATE
#if !defined(NO_CERTS) && defined(WOLFSSL_CUSTOM_CURVES)
/* Get the DER/BER encoding of an ASN.1 INTEGER that has a value of no more than
 * 7 bits.
 *
 * input     Buffer holding DER/BER encoded data.
 * inOutIdx  Current index into buffer to parse.
 * maxIdx    Length of data in buffer.
 * returns BUFFER_E when there is not enough data to parse.
 *         ASN_PARSE_E when the INTEGER tag is not found or length is invalid.
 *         Otherwise, the 7-bit value.
 */
static int GetInteger7Bit(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    byte   b;

    if ((idx + 3) > maxIdx)
        return BUFFER_E;

    if (GetASNTag(input, &idx, &b, maxIdx) != 0)
        return ASN_PARSE_E;
    if (b != ASN_INTEGER)
        return ASN_PARSE_E;
    if (input[idx++] != 1)
        return ASN_PARSE_E;
    b = input[idx++];

    *inOutIdx = idx;
    return b;
}
#endif /* !NO_CERTS */
#endif /* !WOLFSSL_ASN_TEMPLATE */

#if !defined(WOLFSSL_ASN_TEMPLATE)
#if ((defined(WC_RSA_PSS) && !defined(NO_RSA)) || !defined(NO_CERTS))
/* Get the DER/BER encoding of an ASN.1 INTEGER that has a value of no more than
 * 16 bits.
 *
 * input     Buffer holding DER/BER encoded data.
 * inOutIdx  Current index into buffer to parse.
 * maxIdx    Length of data in buffer.
 * returns BUFFER_E when there is not enough data to parse.
 *         ASN_PARSE_E when the INTEGER tag is not found or length is invalid.
 *         Otherwise, the 16-bit value.
 */
static int GetInteger16Bit(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    byte tag;
    word16 n;

    if ((idx + 2) > maxIdx)
        return BUFFER_E;

    if (GetASNTag(input, &idx, &tag, maxIdx) != 0)
        return ASN_PARSE_E;
    if (tag != ASN_INTEGER)
        return ASN_PARSE_E;
    if (input[idx] == 1) {
        idx++;
        if ((idx + 1) > maxIdx) {
            return ASN_PARSE_E;
        }
        n = input[idx++];
    }
    else if (input[idx] == 2) {
        idx++;
        if ((idx + 2) > maxIdx) {
            return ASN_PARSE_E;
        }
        n = input[idx++];
        n = (word16)((n << 8) | input[idx++]);
    }
    else
        return ASN_PARSE_E;

    *inOutIdx = idx;
    return n;
}
#endif /* WC_RSA_PSS || !NO_CERTS */
#endif /* !WOLFSSL_ASN_TEMPLATE */

#if !defined(NO_DSA) && !defined(NO_SHA)
static const char sigSha1wDsaName[] = "SHAwDSA";
static const char sigSha256wDsaName[] = "SHA256wDSA";
#endif /* NO_DSA */
#ifndef NO_RSA
#ifdef WOLFSSL_MD2
    static const char  sigMd2wRsaName[] = "md2WithRSAEncryption";
#endif
#ifndef NO_MD5
    static const char  sigMd5wRsaName[] = "md5WithRSAEncryption";
#endif
#ifndef NO_SHA
    static const char  sigSha1wRsaName[] = "sha1WithRSAEncryption";
#endif
#ifdef WOLFSSL_SHA224
    static const char sigSha224wRsaName[] = "sha224WithRSAEncryption";
#endif
#ifndef NO_SHA256
    static const char sigSha256wRsaName[] = "sha256WithRSAEncryption";
#endif
#ifdef WOLFSSL_SHA384
    static const char sigSha384wRsaName[] = "sha384WithRSAEncryption";
#endif
#ifdef WOLFSSL_SHA512
    static const char sigSha512wRsaName[] = "sha512WithRSAEncryption";
#endif
#ifdef WOLFSSL_SHA3
#ifndef WOLFSSL_NOSHA3_224
    static const char sigSha3_224wRsaName[] = "sha3_224WithRSAEncryption";
#endif
#ifndef WOLFSSL_NOSHA3_256
    static const char sigSha3_256wRsaName[] = "sha3_256WithRSAEncryption";
#endif
#ifndef WOLFSSL_NOSHA3_384
    static const char sigSha3_384wRsaName[] = "sha3_384WithRSAEncryption";
#endif
#ifndef WOLFSSL_NOSHA3_512
    static const char sigSha3_512wRsaName[] = "sha3_512WithRSAEncryption";
#endif
#endif
#ifdef WC_RSA_PSS
    static const char sigRsaSsaPssName[] = "rsassaPss";
#endif
#endif /* NO_RSA */
#ifdef HAVE_ECC
#ifndef NO_SHA
    static const char sigSha1wEcdsaName[] = "SHAwECDSA";
#endif
#ifdef WOLFSSL_SHA224
    static const char sigSha224wEcdsaName[] = "SHA224wECDSA";
#endif
#ifndef NO_SHA256
    static const char sigSha256wEcdsaName[] = "SHA256wECDSA";
#endif
#ifdef WOLFSSL_SHA384
    static const char sigSha384wEcdsaName[] = "SHA384wECDSA";
#endif
#ifdef WOLFSSL_SHA512
    static const char sigSha512wEcdsaName[] = "SHA512wECDSA";
#endif
#ifdef WOLFSSL_SHA3
#ifndef WOLFSSL_NOSHA3_224
    static const char sigSha3_224wEcdsaName[] = "SHA3_224wECDSA";
#endif
#ifndef WOLFSSL_NOSHA3_256
    static const char sigSha3_256wEcdsaName[] = "SHA3_256wECDSA";
#endif
#ifndef WOLFSSL_NOSHA3_384
    static const char sigSha3_384wEcdsaName[] = "SHA3_384wECDSA";
#endif
#ifndef WOLFSSL_NOSHA3_512
    static const char sigSha3_512wEcdsaName[] = "SHA3_512wECDSA";
#endif
#endif
#endif /* HAVE_ECC */
static const char sigUnknownName[] = "Unknown";


/* Get the human readable string for a signature type
 *
 * oid  Oid value for signature
 */
const char* GetSigName(int oid) {
    switch (oid) {
    #if !defined(NO_DSA) && !defined(NO_SHA)
        case CTC_SHAwDSA:
            return sigSha1wDsaName;
        case CTC_SHA256wDSA:
            return sigSha256wDsaName;
    #endif /* NO_DSA && NO_SHA */
    #ifndef NO_RSA
        #ifdef WOLFSSL_MD2
        case CTC_MD2wRSA:
            return sigMd2wRsaName;
        #endif
        #ifndef NO_MD5
        case CTC_MD5wRSA:
            return sigMd5wRsaName;
        #endif
        #ifndef NO_SHA
        case CTC_SHAwRSA:
            return sigSha1wRsaName;
        #endif
        #ifdef WOLFSSL_SHA224
        case CTC_SHA224wRSA:
            return sigSha224wRsaName;
        #endif
        #ifndef NO_SHA256
        case CTC_SHA256wRSA:
            return sigSha256wRsaName;
        #endif
        #ifdef WOLFSSL_SHA384
        case CTC_SHA384wRSA:
            return sigSha384wRsaName;
        #endif
        #ifdef WOLFSSL_SHA512
        case CTC_SHA512wRSA:
            return sigSha512wRsaName;
        #endif
        #ifdef WOLFSSL_SHA3
        #ifndef WOLFSSL_NOSHA3_224
        case CTC_SHA3_224wRSA:
            return sigSha3_224wRsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_256
        case CTC_SHA3_256wRSA:
            return sigSha3_256wRsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_384
        case CTC_SHA3_384wRSA:
            return sigSha3_384wRsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_512
        case CTC_SHA3_512wRSA:
            return sigSha3_512wRsaName;
        #endif
        #endif
        #ifdef WC_RSA_PSS
        case CTC_RSASSAPSS:
            return sigRsaSsaPssName;
        #endif
    #endif /* NO_RSA */
    #ifdef HAVE_ECC
        #ifndef NO_SHA
        case CTC_SHAwECDSA:
            return sigSha1wEcdsaName;
        #endif
        #ifdef WOLFSSL_SHA224
        case CTC_SHA224wECDSA:
            return sigSha224wEcdsaName;
        #endif
        #ifndef NO_SHA256
        case CTC_SHA256wECDSA:
            return sigSha256wEcdsaName;
        #endif
        #ifdef WOLFSSL_SHA384
        case CTC_SHA384wECDSA:
            return sigSha384wEcdsaName;
        #endif
        #ifdef WOLFSSL_SHA512
        case CTC_SHA512wECDSA:
            return sigSha512wEcdsaName;
        #endif
        #ifdef WOLFSSL_SHA3
        #ifndef WOLFSSL_NOSHA3_224
        case CTC_SHA3_224wECDSA:
            return sigSha3_224wEcdsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_256
        case CTC_SHA3_256wECDSA:
            return sigSha3_256wEcdsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_384
        case CTC_SHA3_384wECDSA:
            return sigSha3_384wEcdsaName;
        #endif
        #ifndef WOLFSSL_NOSHA3_512
        case CTC_SHA3_512wECDSA:
            return sigSha3_512wEcdsaName;
        #endif
        #endif
    #endif /* HAVE_ECC */
        default:
            return sigUnknownName;
    }
}


#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7) || \
    defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) || defined(WC_RSA_PSS)
#if !defined(NO_DSA) || defined(HAVE_ECC) || !defined(NO_CERTS) || \
    defined(WOLFSSL_CERT_GEN) || \
   (!defined(NO_RSA) && \
        (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) || \
   (defined(WC_RSA_PSS) && !defined(NO_RSA))
/* Set the DER/BER encoding of the ASN.1 INTEGER header.
 *
 * When output is NULL, calculate the header length only.
 *
 * @param [in]  len        Length of INTEGER data in bytes.
 * @param [in]  firstByte  First byte of data, most significant byte of integer,
 *                         to encode.
 * @param [out] output     Buffer to write into.
 * @return  Number of bytes added to the buffer.
 */
WOLFSSL_LOCAL int SetASNInt(int len, byte firstByte, byte* output)
{
    int idx = 0;

    if (output) {
        /* Write out tag. */
        output[idx] = ASN_INTEGER;
    }
    /* Step over tag. */
    idx += ASN_TAG_SZ;
    /* Check if first byte has top bit set in which case a 0 is needed to
     * maintain positive value. */
    if (firstByte & 0x80) {
        /* Add pre-prepended byte to length of data in INTEGER. */
        len++;
    }
    /* Encode length - passing NULL for output will not encode. */
    idx += (int)SetLength((word32)len, output ? output + idx : NULL);
    /* Put out prepended 0 as well. */
    if (firstByte & 0x80) {
        if (output) {
            /* Write out 0 byte. */
            output[idx] = 0x00;
        }
        /* Update index. */
        idx++;
    }

    /* Return index after header. */
    return idx;
}
#endif
#endif

#ifndef WOLFSSL_ASN_TEMPLATE
#if !defined(NO_DSA) || defined(HAVE_ECC) || (defined(WOLFSSL_CERT_GEN) && \
    !defined(NO_RSA)) || ((defined(WOLFSSL_KEY_GEN) || \
    (!defined(NO_DH) && defined(WOLFSSL_DH_EXTRA)) || \
    defined(OPENSSL_EXTRA)) && !defined(NO_RSA))
/* Set the DER/BER encoding of the ASN.1 INTEGER element with an mp_int.
 * The number is assumed to be positive.
 *
 * n       Multi-precision integer to encode.
 * maxSz   Maximum size of the encoded integer.
 *         A negative value indicates no check of length requested.
 * output  Buffer to write into.
 * returns BUFFER_E when the data is too long for the buffer.
 *         MP_TO_E when encoding the integer fails.
 *         Otherwise, the number of bytes added to the buffer.
 */
static int SetASNIntMP(mp_int* n, int maxSz, byte* output)
{
    int idx = 0;
    int leadingBit;
    int length;

    leadingBit = mp_leading_bit(n);
    length = mp_unsigned_bin_size(n);
    if (maxSz >= 0 && (1 + length + (leadingBit ? 1 : 0)) > maxSz)
        return BUFFER_E;
    idx = SetASNInt(length, (byte)(leadingBit ? 0x80U : 0x00U), output);
    if (maxSz >= 0 && (idx + length) > maxSz)
        return BUFFER_E;

    if (output) {
        int err = mp_to_unsigned_bin(n, output + idx);
        if (err != MP_OKAY)
            return MP_TO_E;
    }
    idx += length;

    return idx;
}
#endif
#endif /* !WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for an INTEGER. */
static const ASNItem intASN[] = {
/* INT */ { 0, ASN_INTEGER, 0, 0, 0 }
};
enum {
    INTASN_IDX_INT = 0
};

/* Number of items in ASN.1 template for an INTEGER. */
#define intASN_Length (sizeof(intASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Windows header clash for WinCE using GetVersion */
/* Decode Version - one byte INTEGER.
 *
 * @param [in]      input     Buffer of BER data.
 * @param [in, out] inOutIdx  On in, start of encoded Version.
 *                            On out, start of next encode ASN.1 item.
 * @param [out]     version   Number encoded in INTEGER.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the most significant bit is set.
 */
int GetMyVersion(const byte* input, word32* inOutIdx,
                               int* version, word32 maxIdx)
{
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 idx = *inOutIdx;
    byte   tag;

    if ((idx + MIN_VERSION_SZ) > maxIdx)
        return ASN_PARSE_E;

    if (GetASNTag(input, &idx, &tag, maxIdx) != 0)
        return ASN_PARSE_E;

    if (tag != ASN_INTEGER)
        return ASN_PARSE_E;

    if (input[idx++] != 0x01)
        return ASN_VERSION_E;

    *version  = input[idx++];
    *inOutIdx = idx;

    return *version;
#else
    ASNGetData dataASN[intASN_Length];
    int ret;
    byte num = 0;

    /* Clear dynamic data and set the version number variable. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    GetASN_Int8Bit(&dataASN[INTASN_IDX_INT], &num);
    /* Decode the version (INTEGER). */
    ret = GetASN_Items(intASN, dataASN, intASN_Length, 0, input, inOutIdx,
                       maxIdx);
    if (ret == 0) {
        /* Return version through variable and return value. */
        *version = num;
        ret = num;
    }
    return ret;
#endif /* WOLFSSL_ASN_TEMPLATE */
}


#if !defined(NO_PWDBASED) || defined(WOLFSSL_ASN_EXTRA)
/* Decode small positive integer, 32 bits or less.
 *
 * @param [in]      input     Buffer of BER data.
 * @param [in, out] inOutIdx  On in, start of encoded INTEGER.
 *                            On out, start of next encode ASN.1 item.
 * @param [out]     number    Number encoded in INTEGER.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the most significant bit is set.
 */
int GetShortInt(const byte* input, word32* inOutIdx, int* number, word32 maxIdx)
{
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 idx = *inOutIdx;
    word32 len;
    byte   tag;

    *number = 0;

    /* check for type and length bytes */
    if ((idx + 2) > maxIdx)
        return BUFFER_E;

    if (GetASNTag(input, &idx, &tag, maxIdx) != 0)
        return ASN_PARSE_E;

    if (tag != ASN_INTEGER)
        return ASN_PARSE_E;

    len = input[idx++];
    if (len > 4)
        return ASN_PARSE_E;

    /* to be consistent with GetASN_Integer */
    #if (!defined(HAVE_SELFTEST) && !defined(HAVE_FIPS)) || \
        (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))
    if (len == 0)
        return ASN_PARSE_E;
    #endif

    if (len + idx > maxIdx)
        return ASN_PARSE_E;

    if (input[idx] >= 0x80U) {
        /* This function only expects positive INTEGER values. */
        return ASN_EXPECT_0_E;
    }

    while (len--) {
        *number  = *number << 8 | input[idx++];
    }

    *inOutIdx = idx;

    return *number;
#else
    ASNGetData dataASN[intASN_Length];
    int ret;
    word32 num = 0;

    /* Clear dynamic data and set the 32-bit number variable. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    GetASN_Int32Bit(&dataASN[INTASN_IDX_INT], &num);
    /* Decode the short int (INTEGER). */
    ret = GetASN_Items(intASN, dataASN, intASN_Length, 0, input, inOutIdx,
                       maxIdx);
    if (ret == 0) {
        /* Return number through variable and return value. */
        *number = (int)num;
        ret = (int)num;
    }
    return ret;
#endif
}
#endif /* !NO_PWDBASED || WOLFSSL_ASN_EXTRA */


#ifndef NO_PWDBASED
#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS8) || \
     defined(HAVE_PKCS12)
/* Set small integer, 32 bits or less. DER encoding with no leading 0s
 * returns total amount written including ASN tag and length byte on success */
int SetShortInt(byte* output, word32* inOutIdx, word32 number, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    word32 len;
    int    i;
    word32 extraByte = 0;

    if (number == 0)
        len = 1;
    else
        len = BytePrecision(number);

    /* clarify the len range to prepare for the next right bit shifting */
    if (len < 1 || len > sizeof(number)) {
        return ASN_PARSE_E;
    }
    if (number >> (WOLFSSL_BIT_SIZE * len - 1)) {
        /* Need one byte of zero value not to be negative number */
        extraByte = 1;
    }

    /* check for room for type and length bytes. */
    if ((idx + 2 + extraByte + len) > maxIdx)
        return BUFFER_E;

    /* check that MAX_SHORT_SZ allows this size of ShortInt. */
    if (2 + extraByte + len > MAX_SHORT_SZ)
        return ASN_PARSE_E;

    output[idx++] = ASN_INTEGER;
    output[idx++] = (byte)(len + extraByte);
    if (extraByte) {
        output[idx++] = 0x00;
    }

    for (i = (int)len - 1; i >= 0; --i)
        output[idx++] = (byte)(number >> (i * WOLFSSL_BIT_SIZE));

    len = idx - *inOutIdx;
    *inOutIdx = idx;

    return (int)len;
}
#endif /* !WOLFSSL_ASN_TEMPLATE || HAVE_PKCS8 || HAVE_PKCS12 */
#endif /* !NO_PWDBASED */

#if !defined(WOLFSSL_ASN_TEMPLATE) && !defined(NO_CERTS)
/* May not have one, not an error */
static int GetExplicitVersion(const byte* input, word32* inOutIdx, int* version,
                              word32 maxIdx)
{
    word32 idx = *inOutIdx;
    byte tag;

    WOLFSSL_ENTER("GetExplicitVersion");

    if (GetASNTag(input, &idx, &tag, maxIdx) != 0)
        return ASN_PARSE_E;

    if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED)) {
        int ret;

        *inOutIdx = ++idx;  /* skip header */
        ret = GetMyVersion(input, inOutIdx, version, maxIdx);
        if (ret >= 0) {
            /* check if version is expected value rfc 5280 4.1 {0, 1, 2} */
            if (*version > MAX_X509_VERSION || *version < MIN_X509_VERSION) {
                WOLFSSL_MSG("Unexpected certificate version");
                WOLFSSL_ERROR_VERBOSE(ASN_VERSION_E);
                ret = ASN_VERSION_E;
            }
        }
        return ret;
    }

    /* go back as is */
    *version = 0;

    return 0;
}
#endif

/* Decode small integer, 32 bits or less.
 *
 * mp_int is initialized.
 *
 * @param [out]     mpi       mp_int to hold number.
 * @param [in]      input     Buffer of BER data.
 * @param [in, out] inOutIdx  On in, start of encoded INTEGER.
 *                            On out, start of next encode ASN.1 item.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the most significant bit is set.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 */
int GetInt(mp_int* mpi, const byte* input, word32* inOutIdx, word32 maxIdx)
{
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 idx = *inOutIdx;
    int    ret;
    int    length;

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

    if (mp_init(mpi) != MP_OKAY)
        return MP_INIT_E;

    if (mp_read_unsigned_bin(mpi, input + idx, (word32)length) != 0) {
        mp_clear(mpi);
        return ASN_GETINT_E;
    }

#ifdef HAVE_WOLF_BIGINT
    if (wc_bigint_from_unsigned_bin(&mpi->raw, input + idx, length) != 0) {
        mp_clear(mpi);
        return ASN_GETINT_E;
    }
#endif /* HAVE_WOLF_BIGINT */

    *inOutIdx = idx + (word32)length;

    return 0;
#else
    ASNGetData dataASN[intASN_Length];

    /* Clear dynamic data and set the mp_int to fill with value. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    GetASN_MP_PosNeg(&dataASN[INTASN_IDX_INT], mpi);
    /* Decode the big number (INTEGER). */
    return GetASN_Items(intASN, dataASN, intASN_Length, 0, input, inOutIdx,
                        maxIdx);
#endif
}

#if (defined(HAVE_ECC) || !defined(NO_DSA)) && !defined(WOLFSSL_ASN_TEMPLATE)
static int GetIntPositive(mp_int* mpi, const byte* input, word32* inOutIdx,
    word32 maxIdx, int initNum)
{
    word32 idx = *inOutIdx;
    int    ret;
    int    length;

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

    /* should not be hit but adding in an additional sanity check */
    if (idx + length > maxIdx) {
        return MP_INIT_E;
    }

    if ((input[idx] & 0x80) == 0x80) {
        if (idx < 1) {
            /* needs at least one byte for length value */
            return MP_INIT_E;
        }

        if (input[idx - 1] != 0x00) {
            return MP_INIT_E;
        }
    }

    if (initNum) {
        if (mp_init(mpi) != MP_OKAY)
            return MP_INIT_E;
    }

    if (mp_read_unsigned_bin(mpi, input + idx, (word32)length) != 0) {
        mp_clear(mpi);
        return ASN_GETINT_E;
    }

#ifdef HAVE_WOLF_BIGINT
    if (wc_bigint_from_unsigned_bin(&mpi->raw, input + idx, length) != 0) {
        mp_clear(mpi);
        return ASN_GETINT_E;
    }
#endif /* HAVE_WOLF_BIGINT */

    *inOutIdx = idx + (word32)length;

    return 0;
}
#endif /* (ECC || !NO_DSA) && !WOLFSSL_ASN_TEMPLATE */

#ifndef WOLFSSL_ASN_TEMPLATE
#if !defined(NO_RSA) || !defined(NO_DSA)
static int SkipInt(const byte* input, word32* inOutIdx, word32 maxIdx)
{
    word32 idx = *inOutIdx;
    int    ret;
    int    length;

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

    *inOutIdx = idx + (word32)length;

    return 0;
}
#endif
#endif /* !WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for a BIT_STRING. */
static const ASNItem bitStringASN[] = {
/* BIT_STR */ { 0, ASN_BIT_STRING, 0, 1, 0 }
};
enum {
    BITSTRINGASN_IDX_BIT_STR = 0
};

/* Number of items in ASN.1 template for a BIT_STRING. */
#define bitStringASN_Length (sizeof(bitStringASN) / sizeof(ASNItem))
#endif

/* Decode and check the BIT_STRING is valid. Return length and unused bits.
 *
 * @param [in]      input       Buffer holding BER encoding.
 * @param [in, out] inOutIdx    On in, start of BIT_STRING.
 *                              On out, start of ASN.1 item after BIT_STRING.
 * @param [out]     len         Length of BIT_STRING data.
 * @param [in]      maxIdx      Maximum index of data in buffer.
 * @param [in]      zeroBits    Indicates whether zero unused bits is expected.
 * @param [in]      unusedBits  Number of unused bits in last byte.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when unused bits is not zero when expected.
 */
int CheckBitString(const byte* input, word32* inOutIdx, int* len,
                          word32 maxIdx, int zeroBits, byte* unusedBits)
{
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 idx = *inOutIdx;
    int    length;
    byte   b;

    if (GetASNTag(input, &idx, &b, maxIdx) != 0) {
        return ASN_BITSTR_E;
    }

    if (b != ASN_BIT_STRING) {
        return ASN_BITSTR_E;
    }

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

    /* extra sanity check that length is greater than 0 */
    if (length <= 0) {
        WOLFSSL_MSG("Error length was 0 in CheckBitString");
        return BUFFER_E;
    }

    if (idx + 1 > maxIdx) {
        WOLFSSL_MSG("Attempted buffer read larger than input buffer");
        return BUFFER_E;
    }

    b = input[idx];
    if (zeroBits && (b != 0x00))
        return ASN_EXPECT_0_E;
    if (b >= 0x08)
        return ASN_PARSE_E;
    if (b != 0) {
        if ((byte)(input[idx + (word32)length - 1] << (8 - b)) != 0)
            return ASN_PARSE_E;
    }
    idx++;
    length--; /* length has been checked for greater than 0 */

    *inOutIdx = idx;
    if (len != NULL)
        *len = length;
    if (unusedBits != NULL)
        *unusedBits = b;

    return 0;
#else
    ASNGetData dataASN[bitStringASN_Length];
    int ret;
    int bits = 0;

    /* Parse BIT_STRING and check validity of unused bits. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    /* Decode BIT_STRING. */
    ret = GetASN_Items(bitStringASN, dataASN, bitStringASN_Length, 0, input,
            inOutIdx, maxIdx);
    if (ret == 0) {
        /* Get unused bits from dynamic ASN.1 data. */
        bits = GetASNItem_UnusedBits(dataASN[BITSTRINGASN_IDX_BIT_STR]);
        /* Check unused bits is 0 when expected. */
        if (zeroBits && (bits != 0)) {
            ret = ASN_EXPECT_0_E;
        }
    }
    if (ret == 0) {
        /* Return length of data and unused bits if required. */
        if (len != NULL) {
            *len = (int)dataASN[BITSTRINGASN_IDX_BIT_STR].data.ref.length;
        }
        if (unusedBits != NULL) {
            *unusedBits = (byte)bits;
        }
    }

    return ret;
#endif
}

/* RSA (with CertGen or KeyGen) OR ECC OR ED25519 OR ED448 (with CertGen or
 * KeyGen) OR CRL */
#if (!defined(NO_RSA) && \
     (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN) || \
      defined(OPENSSL_EXTRA))) || \
    (defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)) || \
    ((defined(HAVE_ED25519) || defined(HAVE_ED448)) && \
     (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_KEY_GEN) || \
      defined(OPENSSL_EXTRA))) || \
    (defined(WC_ENABLE_ASYM_KEY_EXPORT) && !defined(NO_CERTS)) || \
    (!defined(NO_DSA) && !defined(HAVE_SELFTEST) && defined(WOLFSSL_KEY_GEN)) || \
    (!defined(NO_DH) && defined(WOLFSSL_DH_EXTRA)) || \
    defined(HAVE_CRL)

/* Set the DER/BER encoding of the ASN.1 BIT STRING header.
 *
 * When output is NULL, calculate the header length only.
 *
 * @param [in]  len         Length of BIT STRING data.
 *                          That is, the number of least significant zero bits
 *                          before a one.
 *                          The last byte is the most-significant non-zero byte
 *                          of a number.
 * @param [out] output      Buffer to write into.
 * @return  Number of bytes added to the buffer.
 */
word32 SetBitString(word32 len, byte unusedBits, byte* output)
{
    word32 idx = 0;

    if (output) {
        /* Write out tag. */
        output[idx] = ASN_BIT_STRING;
    }
    /* Step over tag. */
    idx += ASN_TAG_SZ;

    /* Encode length - passing NULL for output will not encode.
     * Add one to length for unused bits. */
    idx += SetLength(len + 1, output ? output + idx : NULL);
    if (output) {
        /* Write out unused bits. */
        output[idx] = unusedBits;
    }
    /* Skip over unused bits. */
    idx++;

    /* Return index after header. */
    return idx;
}
#endif /* !NO_RSA || HAVE_ECC || HAVE_ED25519 || HAVE_ED448 || HAVE_CRL */

#ifdef ASN_BER_TO_DER

#ifndef BER_OCTET_LENGTH
    #define BER_OCTET_LENGTH 4096
#endif

/* sets the terminating 0x00 0x00 at the end of an indefinite length
 * returns the number of bytes written */
word32 SetIndefEnd(byte* output)
{
    byte terminate[ASN_INDEF_END_SZ] = { 0x00, 0x00 };

    if (output != NULL) {
        XMEMCPY(output, terminate, ASN_INDEF_END_SZ);
    }

    return (word32)ASN_INDEF_END_SZ;
}


/* Breaks an octet string up into chunks for use with streaming
 * returns 0 on success and updates idx */
int StreamOctetString(const byte* inBuf, word32 inBufSz, byte* out,
                      word32* outSz, word32* idx)
{
    word32 i  = 0;
    word32 outIdx = *idx;
    byte* tmp = out;

    if (tmp) tmp += outIdx;

    while (i < inBufSz) {
        word32 ret, sz;

        sz = BER_OCTET_LENGTH;

        if ((sz + i) > inBufSz) {
            sz = inBufSz - i;
        }

        ret = SetOctetString(sz, tmp);
        if (ret > 0) {
            outIdx += ret;
        }

        if (tmp) {
            if ((word32)ret + sz + i + outIdx > *outSz) {
                return BUFFER_E;
            }
            XMEMCPY(tmp + ret, inBuf + i, sz);
            tmp += sz + ret;
        }
        outIdx += sz;
        i      += sz;
    }

    if (tmp) {
        *idx = outIdx;
        return 0;
    }
    else {
        *outSz = outIdx;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
}


/* Convert BER to DER */

/* Pull information from the ASN.1 BER encoded item header */
static int GetBerHeader(const byte* data, word32* idx, word32 maxIdx,
                        byte* pTag, word32* pLen, int* indef)
{
    int len = 0;
    byte tag;
    word32 i = *idx;

    *indef = 0;

    /* Check there is enough data for a minimal header */
    if (i + 2 > maxIdx) {
        return ASN_PARSE_E;
    }

    /* Retrieve tag */
    tag = data[i++];

    /* Indefinite length handled specially */
    if (data[i] == ASN_INDEF_LENGTH) {
        /* Check valid tag for indefinite */
        if (((tag & 0xc0) == 0) && ((tag & ASN_CONSTRUCTED) == 0x00)) {
            return ASN_PARSE_E;
        }
        i++;
        *indef = 1;
    }
    else if (GetLength(data, &i, &len, maxIdx) < 0) {
        return ASN_PARSE_E;
    }

    /* Return tag, length and index after BER item header */
    *pTag = tag;
    *pLen = (word32)len;
    *idx = i;
    return 0;
}

#ifndef INDEF_ITEMS_MAX
#define INDEF_ITEMS_MAX       20
#endif

/* Indef length item data */
typedef struct Indef {
    word32 start;
    int depth;
    int headerLen;
    word32 len;
} Indef;

/* Indef length items */
typedef struct IndefItems
{
    Indef len[INDEF_ITEMS_MAX];
    int cnt;
    int idx;
    int depth;
} IndefItems;


/* Get header length of current item */
static int IndefItems_HeaderLen(IndefItems* items)
{
    return items->len[items->idx].headerLen;
}

/* Get data length of current item */
static word32 IndefItems_Len(IndefItems* items)
{
    return items->len[items->idx].len;
}

/* Add a indefinite length item */
static int IndefItems_AddItem(IndefItems* items, word32 start)
{
    int ret = 0;
    int i;

    if (items->cnt == INDEF_ITEMS_MAX) {
        ret = MEMORY_E;
    }
    else {
        i = items->cnt++;
        items->len[i].start = start;
        items->len[i].depth = items->depth++;
        items->len[i].headerLen = 1;
        items->len[i].len = 0;
        items->idx = i;
    }

    return ret;
}

/* Increase data length of current item */
static void IndefItems_AddData(IndefItems* items, word32 length)
{
    items->len[items->idx].len += length;
}

/* Update header length of current item to reflect data length */
static void IndefItems_UpdateHeaderLen(IndefItems* items)
{
    items->len[items->idx].headerLen +=
                               (int)SetLength(items->len[items->idx].len, NULL);
}

/* Go to indefinite parent of current item */
static void IndefItems_Up(IndefItems* items)
{
    int i;
    int depth = items->len[items->idx].depth - 1;

    for (i = items->cnt - 1; i >= 0; i--) {
        if (items->len[i].depth == depth) {
            break;
        }
    }
    items->idx = i;
    items->depth = depth + 1;
}

/* Calculate final length by adding length of indefinite child items */
static void IndefItems_CalcLength(IndefItems* items)
{
    int i;
    int idx = items->idx;

    for (i = idx + 1; i < items->cnt; i++) {
        if (items->len[i].depth == items->depth) {
            items->len[idx].len += (word32)items->len[i].headerLen;
            items->len[idx].len += items->len[i].len;
        }
    }
    items->len[idx].headerLen += (int)SetLength(items->len[idx].len, NULL);
}

/* Add more data to indefinite length item */
static void IndefItems_MoreData(IndefItems* items, word32 length)
{
    if (items->cnt > 0 && items->idx >= 0) {
        items->len[items->idx].len += length;
    }
}

/* Convert a BER encoding with indefinite length items to DER.
 *
 * ber    BER encoded data.
 * berSz  Length of BER encoded data.
 * der    Buffer to hold DER encoded version of data.
 *        NULL indicates only the length is required.
 * derSz  The size of the buffer to hold the DER encoded data.
 *        Will be set if der is NULL, otherwise the value is checked as der is
 *        filled.
 * returns ASN_PARSE_E if the BER data is invalid and BAD_FUNC_ARG if ber or
 * derSz are NULL.
 */
int wc_BerToDer(const byte* ber, word32 berSz, byte* der, word32* derSz)
{
    int ret = 0;
    word32 i, j;
    WC_DECLARE_VAR(indefItems, IndefItems, 1, 0);
    byte tag, basic;
    word32 length;
    int indef;

    if (ber == NULL || derSz == NULL)
        return BAD_FUNC_ARG;

    WC_ALLOC_VAR_EX(indefItems, IndefItems, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER,
    {
        ret=MEMORY_E;
        goto end;
    });

    XMEMSET(indefItems, 0, sizeof(*indefItems));

    /* Calculate indefinite item lengths */
    for (i = 0; i < berSz; ) {
        word32 start = i;

        /* Get next BER item */
        ret = GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
        if (ret != 0) {
            goto end;
        }

        if (indef) {
            /* Indefinite item - add to list */
            ret = IndefItems_AddItem(indefItems, i);
            if (ret != 0) {
                goto end;
            }

            if ((tag & 0xC0) == 0 &&
                tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
                tag != (ASN_SET      | ASN_CONSTRUCTED)) {
                /* Constructed basic type - get repeating tag */
                basic = (byte)(tag & (~ASN_CONSTRUCTED));

                /* Add up lengths of each item below */
                for (; i < berSz; ) {
                    /* Get next BER_item */
                    ret = GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
                    if (ret != 0) {
                        goto end;
                    }

                    /* End of content closes item */
                    if (tag == ASN_EOC) {
                        /* Must be zero length */
                        if (length != 0) {
                            ret = ASN_PARSE_E;
                            goto end;
                        }
                        break;
                    }

                    /* Must not be indefinite and tag must match parent */
                    if (indef || tag != basic) {
                        ret = ASN_PARSE_E;
                        goto end;
                    }

                    /* Add to length */
                    IndefItems_AddData(indefItems, length);
                    /* Skip data */
                    i += length;
                }

                /* Ensure we got an EOC and not end of data */
                if (tag != ASN_EOC) {
                    ret = ASN_PARSE_E;
                    goto end;
                }

                /* Set the header length to include the length field */
                IndefItems_UpdateHeaderLen(indefItems);
                /* Go to indefinite parent item */
                IndefItems_Up(indefItems);
            }
        }
        else if (tag == ASN_EOC) {
            /* End-of-content must be 0 length */
            if (length != 0) {
                ret = ASN_PARSE_E;
                goto end;
            }
            /* Check there is an item to close - missing EOC */
            if (indefItems->depth == 0) {
                ret = ASN_PARSE_E;
                goto end;
            }

            /* Finish calculation of data length for indefinite item */
            IndefItems_CalcLength(indefItems);
            /* Go to indefinite parent item */
            IndefItems_Up(indefItems);
        }
        else {
            /* Known length item to add in - make sure enough data for it */
            if (i + length > berSz) {
                ret = ASN_PARSE_E;
                goto end;
            }

            /* Include all data - can't have indefinite inside definite */
            i += length;
            /* Add entire item to current indefinite item */
            IndefItems_MoreData(indefItems, i - start);
        }
    }
    /* Check we had a EOC for each indefinite item */
    if (indefItems->depth != 0) {
        ret = ASN_PARSE_E;
        goto end;
    }

    /* Write out DER */

    j = 0;
    /* Reset index */
    indefItems->idx = 0;
    for (i = 0; i < berSz; ) {
        word32 start = i;

        /* Get item - checked above */
        (void)GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
        if (indef) {
            if (der != NULL) {
                /* Check enough space for header */
                if (j + (word32)IndefItems_HeaderLen(indefItems) > *derSz) {
                    ret = BUFFER_E;
                    goto end;
                }

                if ((tag & 0xC0) == 0 &&
                    tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
                    tag != (ASN_SET      | ASN_CONSTRUCTED)) {
                    /* Remove constructed tag for basic types */
                    tag &= (byte)~ASN_CONSTRUCTED;
                }
                /* Add tag and length */
                der[j] = tag;
                (void)SetLength(IndefItems_Len(indefItems), der + j + 1);
            }
            /* Add header length of indefinite item */
            j += (word32)IndefItems_HeaderLen(indefItems);

            if ((tag & 0xC0) == 0 &&
                tag != (ASN_SEQUENCE | ASN_CONSTRUCTED) &&
                tag != (ASN_SET      | ASN_CONSTRUCTED)) {
                /* For basic type - get each child item and add data */
                for (; i < berSz; ) {
                    (void)GetBerHeader(ber, &i, berSz, &tag, &length, &indef);
                    if (tag == ASN_EOC) {
                        break;
                    }
                    if (der != NULL) {
                        if (j + length > *derSz) {
                            ret = BUFFER_E;
                            goto end;
                        }
                        XMEMCPY(der + j, ber + i, length);
                    }
                    j += length;
                    i += length;
                }
            }

            /* Move to next indef item in list */
            indefItems->idx++;
        }
        else if (tag == ASN_EOC) {
            /* End-Of-Content is not written out in DER */
        }
        else {
            /* Write out definite length item as is. */
            i += length;
            if (der != NULL) {
                /* Ensure space for item */
                if (j + i - start > *derSz) {
                    ret = BUFFER_E;
                    goto end;
                }
                /* Copy item as is */
                XMEMCPY(der + j, ber + start, i - start);
            }
            j += i - start;
        }
    }

    /* Return the length of the DER encoded ASN.1 */
    *derSz = j;
    if (der == NULL) {
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
end:
    WC_FREE_VAR_EX(indefItems, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return ret;
}
#endif

#ifndef WOLFSSL_ASN_TEMPLATE
#if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN)
/* Set the DER/BER encoding of the ASN.1 BIT_STRING with a 16-bit value.
 *
 * val         16-bit value to encode.
 * output      Buffer to write into.
 * returns the number of bytes added to the buffer.
 */
static word32 SetBitString16Bit(word16 val, byte* output)
{
    word32 idx;
    int    len;
    byte   lastByte;
    byte   unusedBits = 0;

    if ((val >> 8) != 0) {
        len = 2;
        lastByte = (byte)(val >> 8);
    }
    else {
        len = 1;
        lastByte = (byte)val;
    }

    while (((lastByte >> unusedBits) & 0x01) == 0x00)
        unusedBits++;

    idx = SetBitString((word32)len, unusedBits, output);
    output[idx++] = (byte)val;
    if (len > 1)
        output[idx++] = (byte)(val >> 8);

    return idx;
}
#endif /* WOLFSSL_CERT_EXT || WOLFSSL_CERT_GEN */
#endif /* !WOLFSSL_ASN_TEMPLATE */


/* Forward declarations for original ASN functions in asn_orig.c.
 * Needed so shared code can call them in non-template mode. */
#ifndef WOLFSSL_ASN_TEMPLATE
static int GetAlgoIdImpl(const byte* input, word32* inOutIdx, word32* oid, word32 oidType, word32 maxIdx, byte *absentParams);
#ifndef NO_RSA
static int _RsaPrivateKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key, int* keySz, word32 inSz);
#endif
#ifndef NO_DSA
static int DsaKeyIntsToDer(DsaKey* key, byte* output, word32* outLen, int ints, int includeVersion);
#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);
#endif
#if !defined(NO_RSA) && !defined(NO_CERTS)
static int StoreRsaKey(DecodedCert* cert, const byte* source, word32* srcIdx, word32 maxIdx);
#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);
#endif
#ifndef NO_CERTS
#if !defined(NO_DSA)
static int ParseDsaKey(const byte* source, word32* srcIdx, word32 maxIdx, void* heap);
#endif
#endif
static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, const byte* input, word32* inOutIdx, word32 maxIdx);
static int GetDateInfo(const byte* source, word32* idx, const byte** pDate, byte* pFormat, int* pLength, word32 maxIdx);
#ifndef NO_CERTS
static int GetSigAlg(DecodedCert* cert, word32* sigOid, word32 maxIdx);
static int GetSignature(DecodedCert* cert);
#endif
static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz, byte absentParams);
#ifndef NO_CERTS
static int DecodeAltNames(const byte* input, word32 sz, DecodedCert* cert);
static int DecodeCrlDist(const byte* input, word32 sz, DecodedCert* cert);
static int DecodeAuthInfo(const byte* input, word32 sz, DecodedCert* cert);
#ifndef IGNORE_NAME_CONSTRAINTS
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
                         word32 limit, byte* hasUnsupported, void* heap);
static int DecodeNameConstraints(const byte* input, word32 sz, DecodedCert* cert);
#endif
#if defined(WOLFSSL_SEP) || defined(WOLFSSL_CERT_EXT)
static int DecodeCertPolicy(const byte* input, word32 sz, DecodedCert* cert);
#endif
#ifdef WOLFSSL_SUBJ_DIR_ATTR
static int DecodeSubjDirAttr(const byte* input, word32 sz, DecodedCert* cert);
#endif
static int DecodeCertExtensions(DecodedCert* cert);
#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);
#endif
#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);
#endif
#ifdef WOLFSSL_CERT_GEN
#ifdef WOLFSSL_CERT_EXT
static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input);
static int SetCertificatePolicies(byte *output, word32 outputSz, char input[MAX_CERTPOL_NB][MAX_CERTPOL_SZ], word16 nb_certpol, void* heap);
#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);
#endif
#ifdef WOLFSSL_CERT_GEN
static int SetValidity(byte* output, int daysValid);
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, LmsKey* lmsKey, XmssKey* xmssKey);
#ifdef WOLFSSL_CERT_REQ
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, LmsKey* lmsKey, XmssKey* xmssKey);
#endif
#endif
#endif
#if defined(HAVE_OCSP) && !defined(WOLFCRYPT_ONLY)
static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz, OcspEntry* entry);
static int DecodeSingleResponse(byte* source, word32* ioIndex, word32 size, int wrapperSz, OcspEntry* single);
static int DecodeOcspRespExtensions(byte* source, word32* ioIndex, OcspResponse* resp, word32 sz);
static int DecodeResponseData(byte* source, word32* ioIndex, OcspResponse* resp, word32 size);
static int DecodeBasicOcspResponse(byte* source, word32* ioIndex, OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify, int noVerifySignature);
#endif
#if defined(HAVE_CRL) && !defined(WOLFCRYPT_ONLY)
static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx, DecodedCRL* dcrl, word32 maxIdx);
#ifndef NO_SKID
static int ParseCRL_AuthKeyIdExt(const byte* input, int sz, DecodedCRL* dcrl);
#endif
static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32* inOutIdx, word32 sz);
#endif
#endif /* !WOLFSSL_ASN_TEMPLATE */

/* hashType */
#ifdef WOLFSSL_MD2
    static const byte hashMd2hOid[] = {42, 134, 72, 134, 247, 13, 2, 2};
#endif
#ifndef NO_MD5
    static const byte hashMd5hOid[] = {42, 134, 72, 134, 247, 13, 2, 5};
#endif
#ifndef NO_SHA
    static const byte hashSha1hOid[] = {43, 14, 3, 2, 26};
#endif
#ifdef WOLFSSL_SHA224
    static const byte hashSha224hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 4};
#endif
#ifndef NO_SHA256
    static const byte hashSha256hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 1};
#endif
#ifdef WOLFSSL_SHA384
    static const byte hashSha384hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 2};
#endif
#ifdef WOLFSSL_SHA512
    static const byte hashSha512hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 3};
    #ifndef WOLFSSL_NOSHA512_224
    static const byte hashSha512_224hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 5};
    #endif
    #ifndef WOLFSSL_NOSHA512_256
    static const byte hashSha512_256hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 6};
    #endif
#endif
#ifdef WOLFSSL_SHA3
#ifndef WOLFSSL_NOSHA3_224
    static const byte hashSha3_224hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 7};
#endif /* WOLFSSL_NOSHA3_224 */
#ifndef WOLFSSL_NOSHA3_256
    static const byte hashSha3_256hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 8};
#endif /* WOLFSSL_NOSHA3_256 */
#ifndef WOLFSSL_NOSHA3_384
    static const byte hashSha3_384hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 9};
#endif /* WOLFSSL_NOSHA3_384 */
#ifndef WOLFSSL_NOSHA3_512
    static const byte hashSha3_512hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 10};
#endif /* WOLFSSL_NOSHA3_512 */
#ifdef WOLFSSL_SHAKE128
    static const byte hashShake128hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 11};
#endif /* WOLFSSL_SHAKE128 */
#ifdef WOLFSSL_SHAKE256
    static const byte hashShake256hOid[] = {96, 134, 72, 1, 101, 3, 4, 2, 12};
#endif /* WOLFSSL_SHAKE256 */
#endif /* WOLFSSL_SHA3 */

/* hmacType */
#ifndef NO_HMAC
    #ifdef WOLFSSL_SHA224
    static const byte hmacSha224Oid[] = {42, 134, 72, 134, 247, 13, 2, 8};
    #endif
    #ifndef NO_SHA256
    static const byte hmacSha256Oid[] = {42, 134, 72, 134, 247, 13, 2, 9};
    #endif
    #ifdef WOLFSSL_SHA384
    static const byte hmacSha384Oid[] = {42, 134, 72, 134, 247, 13, 2, 10};
    #endif
    #ifdef WOLFSSL_SHA512
    static const byte hmacSha512Oid[] = {42, 134, 72, 134, 247, 13, 2, 11};
    #endif
#endif

/* sigType */
#if !defined(NO_DSA) && !defined(NO_SHA)
    static const byte sigSha1wDsaOid[] = {42, 134, 72, 206, 56, 4, 3};
    static const byte sigSha256wDsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 2};
#endif /* NO_DSA */
#ifndef NO_RSA
    #ifdef WOLFSSL_MD2
    static const byte sigMd2wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 2};
    #endif
    #ifndef NO_MD5
    static const byte sigMd5wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 4};
    #endif
    #ifndef NO_SHA
    static const byte sigSha1wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 5};
    #endif
    #ifdef WOLFSSL_SHA224
    static const byte sigSha224wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1,14};
    #endif
    #ifndef NO_SHA256
    static const byte sigSha256wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1,11};
    #endif
    #ifdef WOLFSSL_SHA384
    static const byte sigSha384wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1,12};
    #endif
    #ifdef WOLFSSL_SHA512
    static const byte sigSha512wRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1,13};
    #endif
    #ifdef WOLFSSL_SHA3
    #ifndef WOLFSSL_NOSHA3_224
    static const byte sigSha3_224wRsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 13};
    #endif
    #ifndef WOLFSSL_NOSHA3_256
    static const byte sigSha3_256wRsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 14};
    #endif
    #ifndef WOLFSSL_NOSHA3_384
    static const byte sigSha3_384wRsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 15};
    #endif
    #ifndef WOLFSSL_NOSHA3_512
    static const byte sigSha3_512wRsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 16};
    #endif
    #endif
    #ifdef WC_RSA_PSS
    static const byte sigRsaSsaPssOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 10};
    #endif
#endif /* NO_RSA */
#ifdef HAVE_ECC
    #ifndef NO_SHA
    static const byte sigSha1wEcdsaOid[] = {42, 134, 72, 206, 61, 4, 1};
    #endif
    #ifdef WOLFSSL_SHA224
    static const byte sigSha224wEcdsaOid[] = {42, 134, 72, 206, 61, 4, 3, 1};
    #endif
    #ifndef NO_SHA256
    static const byte sigSha256wEcdsaOid[] = {42, 134, 72, 206, 61, 4, 3, 2};
    #endif
    #ifdef WOLFSSL_SHA384
    static const byte sigSha384wEcdsaOid[] = {42, 134, 72, 206, 61, 4, 3, 3};
    #endif
    #ifdef WOLFSSL_SHA512
    static const byte sigSha512wEcdsaOid[] = {42, 134, 72, 206, 61, 4, 3, 4};
    #endif
    #ifdef WOLFSSL_SHA3
    #ifndef WOLFSSL_NOSHA3_224
    static const byte sigSha3_224wEcdsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 9};
    #endif
    #ifndef WOLFSSL_NOSHA3_256
    static const byte sigSha3_256wEcdsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 10};
    #endif
    #ifndef WOLFSSL_NOSHA3_384
    static const byte sigSha3_384wEcdsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 11};
    #endif
    #ifndef WOLFSSL_NOSHA3_512
    static const byte sigSha3_512wEcdsaOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 12};
    #endif
    #endif
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    /* 0x2A, 0x81, 0x1C, 0xCF, 0x55, 0x01, 0x83, 0x75 */
    static const byte sigSm3wSm2Oid[] = {42, 129, 28, 207, 85, 1, 131, 117};
    #endif
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
    static const byte sigEd25519Oid[] = {43, 101, 112};
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
    static const byte sigEd448Oid[] = {43, 101, 113};
#endif /* HAVE_ED448 */
#ifdef HAVE_FALCON
    /* Falcon Level 1: 1 3 9999 3 11 */
    static const byte sigFalcon_Level1Oid[] = {43, 206, 15, 3, 11};

    /* Falcon Level 5: 1 3 9999 3 14 */
    static const byte sigFalcon_Level5Oid[] = {43, 206, 15, 3, 14};
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    /* Dilithium Level 2: 1.3.6.1.4.1.2.267.12.4.4 */
    static const byte sigDilithium_Level2Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 4, 4};

    /* Dilithium Level 3: 1.3.6.1.4.1.2.267.12.6.5 */
    static const byte sigDilithium_Level3Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 6, 5};

    /* Dilithium Level 5: 1.3.6.1.4.1.2.267.12.8.7 */
    static const byte sigDilithium_Level5Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 8, 7};
#endif /* WOLFSSL_MLDSA_FIPS204_DRAFT */

    /* ML-DSA-44: 2.16.840.1.101.3.4.3.17 */
    static const byte sigMlDsa_44Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 17};

    /* ML-DSA-65: 2.16.840.1.101.3.4.3.18 */
    static const byte sigMlDsa_65Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 18};

    /* ML-DSA-87: 2.16.840.1.101.3.4.3.19 */
    static const byte sigMlDsa_87Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 19};
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
    /* SLH-DSA-SHA2-128s: 2.16.840.1.101.3.4.3.20 */
    static const byte sigSlhDsa_Sha2_128sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 20};
    /* SLH-DSA-SHA2-128f: 2.16.840.1.101.3.4.3.21 */
    static const byte sigSlhDsa_Sha2_128fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 21};
    /* SLH-DSA-SHA2-192s: 2.16.840.1.101.3.4.3.22 */
    static const byte sigSlhDsa_Sha2_192sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 22};
    /* SLH-DSA-SHA2-192f: 2.16.840.1.101.3.4.3.23 */
    static const byte sigSlhDsa_Sha2_192fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 23};
    /* SLH-DSA-SHA2-256s: 2.16.840.1.101.3.4.3.24 */
    static const byte sigSlhDsa_Sha2_256sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 24};
    /* SLH-DSA-SHA2-256f: 2.16.840.1.101.3.4.3.25 */
    static const byte sigSlhDsa_Sha2_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 25};
    /* SLH-DSA-SHAKE-128s: 2.16.840.1.101.3.4.3.26 */
    static const byte sigSlhDsa_Shake_128sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 26};
    /* SLH-DSA-SHAKE-128f: 2.16.840.1.101.3.4.3.27 */
    static const byte sigSlhDsa_Shake_128fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 27};
    /* SLH-DSA-SHAKE-192s: 2.16.840.1.101.3.4.3.28 */
    static const byte sigSlhDsa_Shake_192sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 28};
    /* SLH-DSA-SHAKE-192f: 2.16.840.1.101.3.4.3.29 */
    static const byte sigSlhDsa_Shake_192fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 29};
    /* SLH-DSA-SHAKE-256s: 2.16.840.1.101.3.4.3.30 */
    static const byte sigSlhDsa_Shake_256sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 30};
    /* SLH-DSA-SHAKE-256f: 2.16.840.1.101.3.4.3.31 */
    static const byte sigSlhDsa_Shake_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 31};
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
    /* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
    static const byte sigHssLmsOid[] =
        {42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
    /* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
    static const byte sigXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
    /* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
    static const byte sigXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
#endif /* WOLFSSL_HAVE_XMSS */

/* keyType */
#ifndef NO_DSA
    static const byte keyDsaOid[] = {42, 134, 72, 206, 56, 4, 1};
#endif /* NO_DSA */
#ifndef NO_RSA
    static const byte keyRsaOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 1};
#ifdef WC_RSA_PSS
    static const byte keyRsaPssOid[] = {42, 134, 72, 134, 247, 13, 1, 1, 10};
#endif
#endif /* NO_RSA */
#ifdef HAVE_ECC
    static const byte keyEcdsaOid[] = {42, 134, 72, 206, 61, 2, 1};
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
    static const byte keyEd25519Oid[] = {43, 101, 112};
#endif /* HAVE_ED25519 */
#ifdef HAVE_CURVE25519
    static const byte keyCurve25519Oid[] = {43, 101, 110};
#endif
#ifdef HAVE_ED448
    static const byte keyEd448Oid[] = {43, 101, 113};
#endif /* HAVE_ED448 */
#ifdef HAVE_CURVE448
    static const byte keyCurve448Oid[] = {43, 101, 111};
#endif /* HAVE_CURVE448 */
#ifndef NO_DH
    static const byte keyDhOid[] = {42, 134, 72, 134, 247, 13, 1, 3, 1};
#endif /* !NO_DH */
#ifdef HAVE_FALCON
    /* Falcon Level 1: 1 3 9999 3 11 */
    static const byte keyFalcon_Level1Oid[] = {43, 206, 15, 3, 11};

    /* Falcon Level 5: 1 3 9999 3 14 */
    static const byte keyFalcon_Level5Oid[] = {43, 206, 15, 3, 14};
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    /* Dilithium Level 2: 1.3.6.1.4.1.2.267.12.4.4 */
    static const byte keyDilithium_Level2Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 4, 4};

    /* Dilithium Level 3: 1.3.6.1.4.1.2.267.12.6.5 */
    static const byte keyDilithium_Level3Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 6, 5};

    /* Dilithium Level 5: 1.3.6.1.4.1.2.267.12.8.7 */
    static const byte keyDilithium_Level5Oid[] =
        {43, 6, 1, 4, 1, 2, 130, 11, 12, 8, 7};
#endif

    /* ML-DSA-44: 2.16.840.1.101.3.4.3.17 */
    static const byte keyMlDsa_44Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 17};

    /* ML-DSA-65: 2.16.840.1.101.3.4.3.18 */
    static const byte keyMlDsa_65Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 18};

    /* ML-DSA-87: 2.16.840.1.101.3.4.3.19 */
    static const byte keyMlDsa_87Oid[] =
        {96, 134, 72, 1, 101, 3, 4, 3, 19};
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
    /* SLH-DSA-SHA2-128s: 2.16.840.1.101.3.4.3.20 */
    static const byte keySlhDsa_Sha2_128sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 20};
    /* SLH-DSA-SHA2-128f: 2.16.840.1.101.3.4.3.21 */
    static const byte keySlhDsa_Sha2_128fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 21};
    /* SLH-DSA-SHA2-192s: 2.16.840.1.101.3.4.3.22 */
    static const byte keySlhDsa_Sha2_192sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 22};
    /* SLH-DSA-SHA2-192f: 2.16.840.1.101.3.4.3.23 */
    static const byte keySlhDsa_Sha2_192fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 23};
    /* SLH-DSA-SHA2-256s: 2.16.840.1.101.3.4.3.24 */
    static const byte keySlhDsa_Sha2_256sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 24};
    /* SLH-DSA-SHA2-256f: 2.16.840.1.101.3.4.3.25 */
    static const byte keySlhDsa_Sha2_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 25};
    /* SLH-DSA-SHAKE-128s: 2.16.840.1.101.3.4.3.26 */
    static const byte keySlhDsa_Shake_128sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 26};
    /* SLH-DSA-SHAKE-128f: 2.16.840.1.101.3.4.3.27 */
    static const byte keySlhDsa_Shake_128fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 27};
    /* SLH-DSA-SHAKE-192s: 2.16.840.1.101.3.4.3.28 */
    static const byte keySlhDsa_Shake_192sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 28};
    /* SLH-DSA-SHAKE-192f: 2.16.840.1.101.3.4.3.29 */
    static const byte keySlhDsa_Shake_192fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 29};
    /* SLH-DSA-SHAKE-256s: 2.16.840.1.101.3.4.3.30 */
    static const byte keySlhDsa_Shake_256sOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 30};
    /* SLH-DSA-SHAKE-256f: 2.16.840.1.101.3.4.3.31 */
    static const byte keySlhDsa_Shake_256fOid[] = {96, 134, 72, 1, 101, 3, 4, 3, 31};

/* Single source of truth for all SLH-DSA OID/param/cert-type mappings.
 *
 * Replaces the five parallel switches that each had to be kept in lockstep
 * with the same set of WOLFSSL_SLHDSA_PARAM_NO_* macros. The `builtIn`
 * column is the only place those macros appear now, so adding or
 * disabling a variant is a one-line change.
 *
 * Rows for unbuilt variants are kept (with builtIn=0) so OID-keyed
 * lookups can distinguish "known SLH-DSA OID, variant disabled"
 * (NOT_COMPILED_IN) from "OID not recognised" (-1). Param-keyed lookups
 * filter on builtIn=1 so disabled-variant params never escape.
 *
 * The certKeyType column carries cert_enums values (SLH_DSA_*_KEY) which
 * are declared in asn.h only under WOLFSSL_CERT_GEN; the column and its
 * single reader (SlhDsaParamToKeyType) are gated accordingly.
 */
typedef struct {
    int param;       /* enum SlhDsaParam (held as int for uniformity). */
    int oidKeySum;   /* SLH_DSA_*k from oid_sum.h. */
#ifdef WOLFSSL_CERT_GEN
    int certKeyType; /* SLH_DSA_*_KEY from cert_enums; cert generation only. */
#endif
    int certType;    /* SLH_DSA_*_TYPE from CertType (asn_public.h). */
    int builtIn;     /* 1 if this variant is compiled in. */
} SlhDsaOidMap;

#if !defined(WOLFSSL_SLHDSA_PARAM_NO_128S)
    #define SLHDSA_BUILT_128S 1
#else
    #define SLHDSA_BUILT_128S 0
#endif
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_128F)
    #define SLHDSA_BUILT_128F 1
#else
    #define SLHDSA_BUILT_128F 0
#endif
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_192S)
    #define SLHDSA_BUILT_192S 1
#else
    #define SLHDSA_BUILT_192S 0
#endif
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_192F)
    #define SLHDSA_BUILT_192F 1
#else
    #define SLHDSA_BUILT_192F 0
#endif
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_256S)
    #define SLHDSA_BUILT_256S 1
#else
    #define SLHDSA_BUILT_256S 0
#endif
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_256F)
    #define SLHDSA_BUILT_256F 1
#else
    #define SLHDSA_BUILT_256F 0
#endif

#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S)
    #define SLHDSA_BUILT_SHA2_128S 1
#else
    #define SLHDSA_BUILT_SHA2_128S 0
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F)
    #define SLHDSA_BUILT_SHA2_128F 1
#else
    #define SLHDSA_BUILT_SHA2_128F 0
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S)
    #define SLHDSA_BUILT_SHA2_192S 1
#else
    #define SLHDSA_BUILT_SHA2_192S 0
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F)
    #define SLHDSA_BUILT_SHA2_192F 1
#else
    #define SLHDSA_BUILT_SHA2_192F 0
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S)
    #define SLHDSA_BUILT_SHA2_256S 1
#else
    #define SLHDSA_BUILT_SHA2_256S 0
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && \
    !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F)
    #define SLHDSA_BUILT_SHA2_256F 1
#else
    #define SLHDSA_BUILT_SHA2_256F 0
#endif

/* Helper to keep the row literals readable while certKeyType is
 * conditional on WOLFSSL_CERT_GEN. */
#ifdef WOLFSSL_CERT_GEN
    #define SLHDSA_OID_ROW(p, k, certK, t, b) { (p), (k), (certK), (t), (b) }
#else
    #define SLHDSA_OID_ROW(p, k, certK, t, b) { (p), (k), (t), (b) }
#endif

/* SHA2 rows: in non-SHA2 builds the public enum SlhDsaParam doesn't
 * declare SLHDSA_SHA2_*, so substitute -1 (a value no real param can
 * take) for the param column and discard the macro arg. The OID is still
 * present, so wc_SlhDsaOidToParam(SLH_DSA_SHA2_*k) returns NOT_COMPILED_IN
 * (via builtIn=0) instead of -1 (unknown). The sentinel never escapes
 * because lookups by param go through builtIn=1 rows only. */
#ifdef WOLFSSL_SLHDSA_SHA2
    #define SLHDSA_SHA2_OID_ROW(p, k, certK, t, b) \
        SLHDSA_OID_ROW(p, k, certK, t, b)
#else
    #define SLHDSA_SHA2_OID_ROW(p, k, certK, t, b) \
        SLHDSA_OID_ROW(-1, k, certK, t, b)
#endif

static const SlhDsaOidMap slhDsaOidMap[] = {
    SLHDSA_OID_ROW(SLHDSA_SHAKE128S, SLH_DSA_SHAKE_128Sk,
        SLH_DSA_SHAKE_128S_KEY, SLH_DSA_SHAKE_128S_TYPE, SLHDSA_BUILT_128S),
    SLHDSA_OID_ROW(SLHDSA_SHAKE128F, SLH_DSA_SHAKE_128Fk,
        SLH_DSA_SHAKE_128F_KEY, SLH_DSA_SHAKE_128F_TYPE, SLHDSA_BUILT_128F),
    SLHDSA_OID_ROW(SLHDSA_SHAKE192S, SLH_DSA_SHAKE_192Sk,
        SLH_DSA_SHAKE_192S_KEY, SLH_DSA_SHAKE_192S_TYPE, SLHDSA_BUILT_192S),
    SLHDSA_OID_ROW(SLHDSA_SHAKE192F, SLH_DSA_SHAKE_192Fk,
        SLH_DSA_SHAKE_192F_KEY, SLH_DSA_SHAKE_192F_TYPE, SLHDSA_BUILT_192F),
    SLHDSA_OID_ROW(SLHDSA_SHAKE256S, SLH_DSA_SHAKE_256Sk,
        SLH_DSA_SHAKE_256S_KEY, SLH_DSA_SHAKE_256S_TYPE, SLHDSA_BUILT_256S),
    SLHDSA_OID_ROW(SLHDSA_SHAKE256F, SLH_DSA_SHAKE_256Fk,
        SLH_DSA_SHAKE_256F_KEY, SLH_DSA_SHAKE_256F_TYPE, SLHDSA_BUILT_256F),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_128S, SLH_DSA_SHA2_128Sk,
        SLH_DSA_SHA2_128S_KEY, SLH_DSA_SHA2_128S_TYPE, SLHDSA_BUILT_SHA2_128S),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_128F, SLH_DSA_SHA2_128Fk,
        SLH_DSA_SHA2_128F_KEY, SLH_DSA_SHA2_128F_TYPE, SLHDSA_BUILT_SHA2_128F),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_192S, SLH_DSA_SHA2_192Sk,
        SLH_DSA_SHA2_192S_KEY, SLH_DSA_SHA2_192S_TYPE, SLHDSA_BUILT_SHA2_192S),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_192F, SLH_DSA_SHA2_192Fk,
        SLH_DSA_SHA2_192F_KEY, SLH_DSA_SHA2_192F_TYPE, SLHDSA_BUILT_SHA2_192F),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_256S, SLH_DSA_SHA2_256Sk,
        SLH_DSA_SHA2_256S_KEY, SLH_DSA_SHA2_256S_TYPE, SLHDSA_BUILT_SHA2_256S),
    SLHDSA_SHA2_OID_ROW(SLHDSA_SHA2_256F, SLH_DSA_SHA2_256Fk,
        SLH_DSA_SHA2_256F_KEY, SLH_DSA_SHA2_256F_TYPE, SLHDSA_BUILT_SHA2_256F)
};

#define SLHDSA_OID_MAP_LEN \
    ((int)(sizeof(slhDsaOidMap) / sizeof(slhDsaOidMap[0])))

/* Map SLH-DSA OID key type (SLH_DSA_*k) to enum SlhDsaParam.
 *
 * A known OID whose parameter set is disabled returns NOT_COMPILED_IN so
 * callers can render a "variant unavailable" diagnostic; an unknown OID
 * returns -1. */
int wc_SlhDsaOidToParam(int oid)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].oidKeySum == oid) {
            return slhDsaOidMap[i].builtIn ? slhDsaOidMap[i].param
                                           : NOT_COMPILED_IN;
        }
    }
    return -1;
}

/* Map SLH-DSA OID key type (SLH_DSA_*k) to CertType (SLH_DSA_*_TYPE).
 *
 * Returns NOT_COMPILED_IN for a known but disabled OID; -1 for unknown. */
int wc_SlhDsaOidToCertType(int oid)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].oidKeySum == oid) {
            return slhDsaOidMap[i].builtIn ? slhDsaOidMap[i].certType
                                           : NOT_COMPILED_IN;
        }
    }
    return -1;
}

/* True if oid is any SLH-DSA OID, even one whose parameter set is not
 * built. The x509 dispatch uses this to route to the SLH-DSA branch so
 * unbuilt-variant errors surface as NOT_COMPILED_IN rather than the
 * generic "No public key found" diagnostic. */
int wc_IsSlhDsaOid(int oid)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].oidKeySum == oid) {
            return 1;
        }
    }
    return 0;
}

/* Map enum SlhDsaParam to its OID Key_Sum identifier (SLH_DSA_*k).
 *
 * Symmetric with wc_SlhDsaOidToParam: a known SLH-DSA param whose
 * variant is disabled returns NOT_COMPILED_IN; an unknown / out-of-range
 * value returns BAD_FUNC_ARG. */
int wc_SlhDsaParamToOid(enum SlhDsaParam param)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].param == (int)param) {
            return slhDsaOidMap[i].builtIn ? slhDsaOidMap[i].oidKeySum
                                           : NOT_COMPILED_IN;
        }
    }
    return BAD_FUNC_ARG;
}

#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_REQ)
/* True if certType (SLH_DSA_*_TYPE) is any built-in SLH-DSA cert key
 * type. */
static int IsSlhDsaKeyType(int certType)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].builtIn &&
                slhDsaOidMap[i].certType == certType) {
            return 1;
        }
    }
    return 0;
}
#endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */

#ifdef WOLFSSL_CERT_GEN
/* Map enum SlhDsaParam to Cert keyType identifier (SLH_DSA_*_KEY), or 0
 * on unknown / unbuilt. */
static int SlhDsaParamToKeyType(enum SlhDsaParam param)
{
    int i;
    for (i = 0; i < SLHDSA_OID_MAP_LEN; i++) {
        if (slhDsaOidMap[i].builtIn &&
                slhDsaOidMap[i].param == (int)param) {
            return slhDsaOidMap[i].certKeyType;
        }
    }
    return 0;
}
#endif /* WOLFSSL_CERT_GEN */
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_HAVE_LMS
    /* RFC 9802 id-alg-hss-lms-hashsig: 1.2.840.113549.1.9.16.3.17 */
    static const byte keyHssLmsOid[] =
        {42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 17};
#endif /* WOLFSSL_HAVE_LMS */
#ifdef WOLFSSL_HAVE_XMSS
    /* RFC 9802 id-alg-xmss-hashsig: 1.3.6.1.5.5.7.6.34 */
    static const byte keyXmssOid[] = {43, 6, 1, 5, 5, 7, 6, 34};
    /* RFC 9802 id-alg-xmssmt-hashsig: 1.3.6.1.5.5.7.6.35 */
    static const byte keyXmssMtOid[] = {43, 6, 1, 5, 5, 7, 6, 35};
#endif /* WOLFSSL_HAVE_XMSS */

/* curveType */
#ifdef HAVE_ECC
    /* See "ecc_sets" table in ecc.c */
#endif /* HAVE_ECC */

#ifdef HAVE_AES_CBC
/* blkType */
    #ifdef WOLFSSL_AES_128
    static const byte blkAes128CbcOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 2};
    #endif
    #ifdef WOLFSSL_AES_192
    static const byte blkAes192CbcOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 22};
    #endif
    #ifdef WOLFSSL_AES_256
    static const byte blkAes256CbcOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 42};
    #endif
#endif /* HAVE_AES_CBC */
#ifdef HAVE_AESGCM
    #ifdef WOLFSSL_AES_128
    static const byte blkAes128GcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 6};
    #endif
    #ifdef WOLFSSL_AES_192
    static const byte blkAes192GcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 26};
    #endif
    #ifdef WOLFSSL_AES_256
    static const byte blkAes256GcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 46};
    #endif
#endif /* HAVE_AESGCM */
#ifdef HAVE_AESCCM
    #ifdef WOLFSSL_AES_128
    static const byte blkAes128CcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 7};
    #endif
    #ifdef WOLFSSL_AES_192
    static const byte blkAes192CcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 27};
    #endif
    #ifdef WOLFSSL_AES_256
    static const byte blkAes256CcmOid[] = {96, 134, 72, 1, 101, 3, 4, 1, 47};
    #endif
#endif /* HAVE_AESCCM */

#ifndef NO_DES3
    static const byte blkDesCbcOid[]  = {43, 14, 3, 2, 7};
    static const byte blkDes3CbcOid[] = {42, 134, 72, 134, 247, 13, 3, 7};
#endif

/* keyWrapType */
#ifdef WOLFSSL_AES_128
    static const byte wrapAes128Oid[] = {96, 134, 72, 1, 101, 3, 4, 1, 5};
#endif
#ifdef WOLFSSL_AES_192
    static const byte wrapAes192Oid[] = {96, 134, 72, 1, 101, 3, 4, 1, 25};
#endif
#ifdef WOLFSSL_AES_256
    static const byte wrapAes256Oid[] = {96, 134, 72, 1, 101, 3, 4, 1, 45};
#endif
#ifdef HAVE_PKCS7
/* From RFC 3211 */
static const byte wrapPwriKekOid[] = {42, 134, 72, 134, 247, 13, 1, 9, 16, 3,9};
#endif

/* cmsKeyAgreeType */
#ifndef NO_SHA
    static const byte dhSinglePass_stdDH_sha1kdf_Oid[]   =
                                          {43, 129, 5, 16, 134, 72, 63, 0, 2};
#endif
#ifdef WOLFSSL_SHA224
    static const byte dhSinglePass_stdDH_sha224kdf_Oid[] = {43, 129, 4, 1, 11, 0};
#endif
#ifndef NO_SHA256
    static const byte dhSinglePass_stdDH_sha256kdf_Oid[] = {43, 129, 4, 1, 11, 1};
#endif
#ifdef WOLFSSL_SHA384
    static const byte dhSinglePass_stdDH_sha384kdf_Oid[] = {43, 129, 4, 1, 11, 2};
#endif
#ifdef WOLFSSL_SHA512
    static const byte dhSinglePass_stdDH_sha512kdf_Oid[] = {43, 129, 4, 1, 11, 3};
#endif

/* ocspType */
#ifdef HAVE_OCSP
    static const byte ocspBasicOid[]    = {43, 6, 1, 5, 5, 7, 48, 1, 1};
    static const byte ocspNonceOid[]    = {43, 6, 1, 5, 5, 7, 48, 1, 2};
    static const byte ocspNoCheckOid[]  = {43, 6, 1, 5, 5, 7, 48, 1, 5};
#endif /* HAVE_OCSP */

/* certExtType */
static const byte extBasicCaOid[] = {85, 29, 19};
static const byte extAltNamesOid[] = {85, 29, 17};
static const byte extCrlDistOid[] = {85, 29, 31};
static const byte extAuthInfoOid[] = {43, 6, 1, 5, 5, 7, 1, 1};
static const byte extAuthKeyOid[] = {85, 29, 35};
static const byte extSubjKeyOid[] = {85, 29, 14};
static const byte extCertPolicyOid[] = {85, 29, 32};
static const byte extKeyUsageOid[] = {85, 29, 15};
static const byte extInhibitAnyOid[] = {85, 29, 54};
static const byte extExtKeyUsageOid[] = {85, 29, 37};
#ifndef IGNORE_NAME_CONSTRAINTS
    static const byte extNameConsOid[] = {85, 29, 30};
#endif
#ifdef HAVE_CRL
static const byte extCrlNumberOid[] = {85, 29, 20};
#endif
#ifdef WOLFSSL_SUBJ_DIR_ATTR
    static const byte extSubjDirAttrOid[] = {85, 29, 9};
#endif
#ifdef WOLFSSL_SUBJ_INFO_ACC
    static const byte extSubjInfoAccessOid[] = {43, 6, 1, 5, 5, 7, 1, 11};
#endif

/* certAuthInfoType */
static const byte extAuthInfoOcspOid[] = {43, 6, 1, 5, 5, 7, 48, 1};
static const byte extAuthInfoCaIssuerOid[] = {43, 6, 1, 5, 5, 7, 48, 2};
#ifdef WOLFSSL_SUBJ_INFO_ACC
    static const byte extAuthInfoCaRespOid[] = {43, 6, 1, 5, 5, 7, 48, 5};
#endif /* WOLFSSL_SUBJ_INFO_ACC */

/* certPolicyType */
static const byte extCertPolicyAnyOid[] = {85, 29, 32, 0};
static const byte extCertPolicyIsrgDomainValid[] =
    {43, 6, 1, 4, 1, 130, 223, 19, 1, 1, 1};
#ifdef WOLFSSL_FPKI
#define CERT_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 3, num}
    static const byte extCertPolicyFpkiHighAssuranceOid[] =
            CERT_POLICY_TYPE_OID_BASE(4);
    static const byte extCertPolicyFpkiCommonHardwareOid[] =
            CERT_POLICY_TYPE_OID_BASE(7);
    static const byte extCertPolicyFpkiMediumHardwareOid[] =
            CERT_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyFpkiCommonAuthOid[] =
            CERT_POLICY_TYPE_OID_BASE(13);
    static const byte extCertPolicyFpkiCommonHighOid[] =
            CERT_POLICY_TYPE_OID_BASE(16);
    static const byte extCertPolicyFpkiCommonDevicesHardwareOid[] =
            CERT_POLICY_TYPE_OID_BASE(36);
    static const byte extCertPolicyFpkiCommonPivContentSigningOid[] =
            CERT_POLICY_TYPE_OID_BASE(39);
    static const byte extCertPolicyFpkiPivAuthOid[] =
            CERT_POLICY_TYPE_OID_BASE(40);
    static const byte extCertPolicyFpkiPivAuthHwOid[] =
            CERT_POLICY_TYPE_OID_BASE(41);
    static const byte extCertPolicyFpkiPiviAuthOid[] =
            CERT_POLICY_TYPE_OID_BASE(45);

    /* Federal PKI Test OIDs - 2.16.840.1.101.3.2.1.48.x */
    #define TEST_CERT_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 48, num}
    static const byte extCertPolicyFpkiAuthTestOid[] =
        TEST_CERT_POLICY_TYPE_OID_BASE(11);
    static const byte extCertPolicyFpkiCardauthTestOid[] =
        TEST_CERT_POLICY_TYPE_OID_BASE(13);
    static const byte extCertPolicyFpkiPivContentTestOid[] =
        TEST_CERT_POLICY_TYPE_OID_BASE(86);
    static const byte extCertPolicyFpkiAuthDerivedTestOid[] =
        TEST_CERT_POLICY_TYPE_OID_BASE(109);
    static const byte extCertPolicyFpkiAuthDerivedHwTestOid[] =
        TEST_CERT_POLICY_TYPE_OID_BASE(110);

    /* DoD PKI OIDs - 2.16.840.1.101.2.1.11.X */
    #define DOD_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 2, 1, 11, num}
    static const byte extCertPolicyDodMediumOid[] =
            DOD_POLICY_TYPE_OID_BASE(5);
    static const byte extCertPolicyDodMediumHardwareOid[] =
            DOD_POLICY_TYPE_OID_BASE(9);
    static const byte extCertPolicyDodPivAuthOid[] =
            DOD_POLICY_TYPE_OID_BASE(10);
    static const byte extCertPolicyDodMediumNpeOid[] =
            DOD_POLICY_TYPE_OID_BASE(17);
    static const byte extCertPolicyDodMedium2048Oid[] =
            DOD_POLICY_TYPE_OID_BASE(18);
    static const byte extCertPolicyDodMediumHardware2048Oid[] =
            DOD_POLICY_TYPE_OID_BASE(19);
    static const byte extCertPolicyDodPivAuth2048Oid[] =
            DOD_POLICY_TYPE_OID_BASE(20);
    static const byte extCertPolicyDodPeerInteropOid[] =
            DOD_POLICY_TYPE_OID_BASE(31);
    static const byte extCertPolicyDodMediumNpe112Oid[] =
            DOD_POLICY_TYPE_OID_BASE(36);
    static const byte extCertPolicyDodMediumNpe128Oid[] =
            DOD_POLICY_TYPE_OID_BASE(37);
    static const byte extCertPolicyDodMediumNpe192Oid[] =
            DOD_POLICY_TYPE_OID_BASE(38);
    static const byte extCertPolicyDodMedium112Oid[] =
            DOD_POLICY_TYPE_OID_BASE(39);
    static const byte extCertPolicyDodMedium128Oid[] =
            DOD_POLICY_TYPE_OID_BASE(40);
    static const byte extCertPolicyDodMedium192Oid[] =
            DOD_POLICY_TYPE_OID_BASE(41);
    static const byte extCertPolicyDodMediumHardware112Oid[] =
            DOD_POLICY_TYPE_OID_BASE(42);
    static const byte extCertPolicyDodMediumHardware128Oid[] =
            DOD_POLICY_TYPE_OID_BASE(43);
    static const byte extCertPolicyDodMediumHardware192Oid[] =
            DOD_POLICY_TYPE_OID_BASE(44);
    static const byte extCertPolicyDodAdminOid[] =
            DOD_POLICY_TYPE_OID_BASE(59);
    static const byte extCertPolicyDodInternalNpe112Oid[] =
            DOD_POLICY_TYPE_OID_BASE(60);
    static const byte extCertPolicyDodInternalNpe128Oid[] =
            DOD_POLICY_TYPE_OID_BASE(61);
    static const byte extCertPolicyDodInternalNpe192Oid[] =
            DOD_POLICY_TYPE_OID_BASE(62);

    /* ECA PKI OIDs - 2.16.840.1.101.3.2.1.12.X */
    #define ECA_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 12, num}
    static const byte extCertPolicyEcaMediumOid[] =
            ECA_POLICY_TYPE_OID_BASE(1);
    static const byte extCertPolicyEcaMediumHardwareOid[] =
            ECA_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyEcaMediumTokenOid[] =
            ECA_POLICY_TYPE_OID_BASE(3);
    static const byte extCertPolicyEcaMediumSha256Oid[] =
            ECA_POLICY_TYPE_OID_BASE(4);
    static const byte extCertPolicyEcaMediumTokenSha256Oid[] =
            ECA_POLICY_TYPE_OID_BASE(5);
    static const byte extCertPolicyEcaMediumHardwarePiviOid[] =
            ECA_POLICY_TYPE_OID_BASE(6);
    static const byte extCertPolicyEcaContentSigningPiviOid[] =
            ECA_POLICY_TYPE_OID_BASE(8);
    static const byte extCertPolicyEcaMediumDeviceSha256Oid[] =
            ECA_POLICY_TYPE_OID_BASE(9);
    static const byte extCertPolicyEcaMediumHardwareSha256Oid[] =
            ECA_POLICY_TYPE_OID_BASE(10);

    /* Department of State PKI OIDs - 2.16.840.1.101.3.2.1.6.X */
    #define STATE_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 6, num}
    static const byte extCertPolicyStateBasicOid[] =
            STATE_POLICY_TYPE_OID_BASE(1);
    static const byte extCertPolicyStateLowOid[] =
            STATE_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyStateModerateOid[] =
            STATE_POLICY_TYPE_OID_BASE(3);
    static const byte extCertPolicyStateHighOid[] =
            STATE_POLICY_TYPE_OID_BASE(4);
    static const byte extCertPolicyStateMedHwOid[] =
            STATE_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyStateMediumDeviceHardwareOid[] =
            STATE_POLICY_TYPE_OID_BASE(38);

    /* U.S. Treasury SSP PKI OIDs - 2.16.840.1.101.3.2.1.5.X */
    #define TREASURY_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 101, 3, 2, 1, 5, num}
    static const byte extCertPolicyTreasuryMediumHardwareOid[] =
            TREASURY_POLICY_TYPE_OID_BASE(4);
    static const byte extCertPolicyTreasuryHighOid[] =
            TREASURY_POLICY_TYPE_OID_BASE(5);
    static const byte extCertPolicyTreasuryPiviHardwareOid[] =
            TREASURY_POLICY_TYPE_OID_BASE(10);
    static const byte extCertPolicyTreasuryPiviContentSigningOid[] =
            TREASURY_POLICY_TYPE_OID_BASE(12);

    /* Boeing PKI OIDs - 1.3.6.1.4.1.73.15.3.1.X */
    #define BOEING_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 73, 15, 3, 1, num}
    static const byte extCertPolicyBoeingMediumHardwareSha256Oid[] =
            BOEING_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyBoeingMediumHardwareContentSigningSha256Oid[] =
            BOEING_POLICY_TYPE_OID_BASE(17);

    /* Carillon Federal Services OIDs - 1.3.6.1.4.1.45606.3.1.X */
    #define CARILLON_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 130, 228, 38, 3, 1, num}
    static const byte extCertPolicyCarillonMediumhw256Oid[] =
            CARILLON_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyCarillonAivhwOid[] =
            CARILLON_POLICY_TYPE_OID_BASE(20);
    static const byte extCertPolicyCarillonAivcontentOid[] =
            CARILLON_POLICY_TYPE_OID_BASE(22);

    /* Carillon Information Security OIDs - 1.3.6.1.4.1.25054.3.1.X */
    #define CIS_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 129, 195, 94, 3, 1, num}
    static const byte extCertPolicyCisMediumhw256Oid[] =
            CIS_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyCisMeddevhw256Oid[] =
            CIS_POLICY_TYPE_OID_BASE(14);
    static const byte extCertPolicyCisIcecapHwOid[] =
            CIS_POLICY_TYPE_OID_BASE(20);
    static const byte extCertPolicyCisIcecapContentOid[] =
            CIS_POLICY_TYPE_OID_BASE(22);

    /* CertiPath Bridge OIDs - 1.3.6.1.4.1.24019.1.1.1.X */
    #define CERTIPATH_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 129, 187, 83, 1, 1, 1, num}
    static const byte extCertPolicyCertipathMediumhwOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyCertipathHighhwOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(3);
    static const byte extCertPolicyCertipathIcecapHwOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(7);
    static const byte extCertPolicyCertipathIcecapContentOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(9);
    static const byte extCertPolicyCertipathVarMediumhwOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(18);
    static const byte extCertPolicyCertipathVarHighhwOid[] =
            CERTIPATH_POLICY_TYPE_OID_BASE(19);

    /* TSCP Bridge OIDs - 1.3.6.1.4.1.38099.1.1.1.X */
    #define TSCP_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 130, 169, 83, 1, 1, 1, num}
    static const byte extCertPolicyTscpMediumhwOid[] =
            TSCP_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyTscpPiviOid[] =
            TSCP_POLICY_TYPE_OID_BASE(5);
    static const byte extCertPolicyTscpPiviContentOid[] =
            TSCP_POLICY_TYPE_OID_BASE(7);

    /* DigiCert NFI PKI OIDs - 2.16.840.1.113733.1.7.23.3.1.X */
    #define DIGICERT_NFI_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 134, 248, 69, 1, 7, 23, 3, 1, num}
    static const byte extCertPolicyDigicertNfiMediumHardwareOid[] =
            DIGICERT_NFI_POLICY_TYPE_OID_BASE(7);
    static const byte extCertPolicyDigicertNfiAuthOid[] =
            DIGICERT_NFI_POLICY_TYPE_OID_BASE(13);
    static const byte extCertPolicyDigicertNfiPiviHardwareOid[] =
            DIGICERT_NFI_POLICY_TYPE_OID_BASE(18);
    static const byte extCertPolicyDigicertNfiPiviContentSigningOid[] =
            DIGICERT_NFI_POLICY_TYPE_OID_BASE(20);
    static const byte extCertPolicyDigicertNfiMediumDevicesHardwareOid[] =
            DIGICERT_NFI_POLICY_TYPE_OID_BASE(36);

    /* Entrust Managed Services NFI PKI OIDs - 2.16.840.1.114027.200.3.10.7.X */
    #define ENTRUST_NFI_POLICY_TYPE_OID_BASE(num) {96, 134, 72, 1, 134, 250, 107, 129, 72, 3, 10, 7, num}
    static const byte extCertPolicyEntrustNfiMediumHardwareOid[] =
            ENTRUST_NFI_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyEntrustNfiMediumAuthenticationOid[] =
            ENTRUST_NFI_POLICY_TYPE_OID_BASE(4);
    static const byte extCertPolicyEntrustNfiPiviHardwareOid[] =
            ENTRUST_NFI_POLICY_TYPE_OID_BASE(6);
    static const byte extCertPolicyEntrustNfiPiviContentSigningOid[] =
            ENTRUST_NFI_POLICY_TYPE_OID_BASE(9);
    static const byte extCertPolicyEntrustNfiMediumDevicesHwOid[] =
            ENTRUST_NFI_POLICY_TYPE_OID_BASE(16);

    /* Exostar LLC PKI OIDs - 1.3.6.1.4.1.13948.1.1.1.X */
    #define EXOSTAR_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 236, 124, 1, 1, 1, num}
    static const byte extCertPolicyExostarMediumHardwareSha2Oid[] =
            EXOSTAR_POLICY_TYPE_OID_BASE(6);

    /* IdenTrust NFI OIDs - 2.16.840.1.113839.0.100.X.Y */
    #define IDENTRUST_POLICY_TYPE_OID_BASE(num1, num2) {96, 134, 72, 1, 134, 249, 47, 0, 100, num1, num2}
    static const byte extCertPolicyIdentrustMediumhwSignOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(12, 1);
    static const byte extCertPolicyIdentrustMediumhwEncOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(12, 2);
    static const byte extCertPolicyIdentrustPiviHwIdOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(18, 0);
    static const byte extCertPolicyIdentrustPiviHwSignOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(18, 1);
    static const byte extCertPolicyIdentrustPiviHwEncOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(18, 2);
    static const byte extCertPolicyIdentrustPiviContentOid[] =
            IDENTRUST_POLICY_TYPE_OID_BASE(20, 1);

    /* Lockheed Martin PKI OIDs - 1.3.6.1.4.1.103.100.1.1.3.X */
    #define LOCKHEED_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 103, 100, 1, 1, 3, num}
    static const byte extCertPolicyLockheedMediumAssuranceHardwareOid[] =
            LOCKHEED_POLICY_TYPE_OID_BASE(3);

    /* Northrop Grumman PKI OIDs - 1.3.6.1.4.1.16334.509.2.X */
    #define NORTHROP_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 255, 78, 131, 125, 2, num}
    static const byte extCertPolicyNorthropMediumAssurance256HardwareTokenOid[] =
            NORTHROP_POLICY_TYPE_OID_BASE(8);
    static const byte extCertPolicyNorthropPiviAssurance256HardwareTokenOid[] =
            NORTHROP_POLICY_TYPE_OID_BASE(9);
    static const byte extCertPolicyNorthropPiviAssurance256ContentSigningOid[] =
            NORTHROP_POLICY_TYPE_OID_BASE(11);
    static const byte extCertPolicyNorthropMediumAssurance384HardwareTokenOid[] =
            NORTHROP_POLICY_TYPE_OID_BASE(14);

    /* Raytheon PKI OIDs - 1.3.6.1.4.1.1569.10.1.X and 1.3.6.1.4.1.26769.10.1.X */
    #define RAYTHEON_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 140, 33, 10, 1, num}
    static const byte extCertPolicyRaytheonMediumHardwareOid[] =
            RAYTHEON_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyRaytheonMediumDeviceHardwareOid[] =
            RAYTHEON_POLICY_TYPE_OID_BASE(18);

    #define RAYTHEON_SHA2_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 129, 209, 17, 10, 1, num}
    static const byte extCertPolicyRaytheonSha2MediumHardwareOid[] =
            RAYTHEON_SHA2_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyRaytheonSha2MediumDeviceHardwareOid[] =
            RAYTHEON_SHA2_POLICY_TYPE_OID_BASE(18);

    /* WidePoint NFI PKI OIDs - 1.3.6.1.4.1.3922.1.1.1.X */
    #define WIDEPOINT_NFI_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 158, 82, 1, 1, 1, num}
    static const byte extCertPolicyWidepointNfiMediumHardwareOid[] =
            WIDEPOINT_NFI_POLICY_TYPE_OID_BASE(12);
    static const byte extCertPolicyWidepointNfiPiviHardwareOid[] =
            WIDEPOINT_NFI_POLICY_TYPE_OID_BASE(18);
    static const byte extCertPolicyWidepointNfiPiviContentSigningOid[] =
            WIDEPOINT_NFI_POLICY_TYPE_OID_BASE(20);
    static const byte extCertPolicyWidepointNfiMediumDevicesHardwareOid[] =
            WIDEPOINT_NFI_POLICY_TYPE_OID_BASE(38);

    /* Australian Defence Organisation PKI OIDs - 1.2.36.1.334.1.2.X.X */
    #define ADO_POLICY_TYPE_OID_BASE(type, num) {42, 36, 1, 130, 78, 1, 2, type, num}
    static const byte extCertPolicyAdoIndividualMediumAssuranceOid[] =
            ADO_POLICY_TYPE_OID_BASE(1, 2);
    static const byte extCertPolicyAdoIndividualHighAssuranceOid[] =
            ADO_POLICY_TYPE_OID_BASE(1, 3);
    static const byte extCertPolicyAdoResourceMediumAssuranceOid[] =
            ADO_POLICY_TYPE_OID_BASE(2, 2);

    /* Comodo Ltd PKI OID  1.3.6.1.4.1.6449.1.2.1.3.4 */
    #define COMODO_POLICY_TYPE_OID_BASE(num) {43, 6, 1, 4, 1, 178, 49, 1, 2, 1, 3, num}
    static const byte extCertPolicyComodoLtdOid[] =
            COMODO_POLICY_TYPE_OID_BASE(4);

    /* Netherlands Ministry of Defence PKI OIDs - 2.16.528.1.1003.1.2.5.X */
    #define NL_MOD_POLICY_TYPE_OID_BASE(num) {96, 132, 16, 1, 135, 107, 1, 2, 5, num}
    static const byte extCertPolicyNlModAuthenticityOid[] =
            NL_MOD_POLICY_TYPE_OID_BASE(1);
    static const byte extCertPolicyNlModIrrefutabilityOid[] =
            NL_MOD_POLICY_TYPE_OID_BASE(2);
    static const byte extCertPolicyNlModConfidentialityOid[] =
            NL_MOD_POLICY_TYPE_OID_BASE(3);
#endif /* WOLFSSL_FPKI */

/* certAltNameType */
static const byte extAltNamesHwNameOid[] = {43, 6, 1, 5, 5, 7, 8, 4};

/* certKeyUseType */
static const byte extExtKeyUsageAnyOid[] = {85, 29, 37, 0};
static const byte extExtKeyUsageServerAuthOid[]   = {43, 6, 1, 5, 5, 7, 3, 1};
static const byte extExtKeyUsageClientAuthOid[]   = {43, 6, 1, 5, 5, 7, 3, 2};
static const byte extExtKeyUsageCodeSigningOid[]  = {43, 6, 1, 5, 5, 7, 3, 3};
static const byte extExtKeyUsageEmailProtectOid[] = {43, 6, 1, 5, 5, 7, 3, 4};
static const byte extExtKeyUsageTimestampOid[]    = {43, 6, 1, 5, 5, 7, 3, 8};
static const byte extExtKeyUsageOcspSignOid[]     = {43, 6, 1, 5, 5, 7, 3, 9};
#ifdef WOLFSSL_WOLFSSH
#define EXT_KEY_USAGE_OID_BASE(num) {43, 6, 1, 5, 5, 7, 3, num}
    static const byte extExtKeyUsageSshClientAuthOid[] =
            EXT_KEY_USAGE_OID_BASE(21);
    static const byte extExtKeyUsageSshMSCLOid[] =
            {43, 6, 1, 4, 1, 130, 55, 20, 2, 2};
    static const byte extExtKeyUsageSshKpClientAuthOid[] =
            {43, 6, 1, 5, 2, 3, 4};
#endif /* WOLFSSL_WOLFSSH */

#ifdef WOLFSSL_SUBJ_DIR_ATTR
#define SUBJ_DIR_ATTR_TYPE_OID_BASE(num) {43, 6, 1, 5, 5, 7, 9, num}
    static const byte extSubjDirAttrDobOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(1);
    static const byte extSubjDirAttrPobOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(2);
    static const byte extSubjDirAttrGenderOid[] =
            SUBJ_DIR_ATTR_TYPE_OID_BASE(3);
    static const byte extSubjDirAttrCocOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(4);
    static const byte extSubjDirAttrCorOid[] = SUBJ_DIR_ATTR_TYPE_OID_BASE(5);
#endif

#if defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_GEN) || \
    defined(WOLFSSL_ASN_TEMPLATE) || defined(OPENSSL_EXTRA) || \
    defined(OPENSSL_EXTRA_X509_SMALL)
/* csrAttrType */
#define CSR_ATTR_TYPE_OID_BASE(num) {42, 134, 72, 134, 247, 13, 1, 9, num}
#if !defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_GEN) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(WOLFSSL_ASN_TEMPLATE)
static const byte attrEmailOid[] =             CSR_ATTR_TYPE_OID_BASE(1);
#endif
#ifdef WOLFSSL_CERT_REQ
static const byte attrUnstructuredNameOid[] =  CSR_ATTR_TYPE_OID_BASE(2);
static const byte attrPkcs9ContentTypeOid[] =  CSR_ATTR_TYPE_OID_BASE(3);
static const byte attrChallengePasswordOid[] = CSR_ATTR_TYPE_OID_BASE(7);
static const byte attrExtensionRequestOid[] =  CSR_ATTR_TYPE_OID_BASE(14);
static const byte attrSerialNumberOid[] = {85, 4, 5};
static const byte attrDnQualifier[] = {85, 4, 46};
static const byte attrInitals[] = {85, 4, 43};
static const byte attrSurname[] = {85, 4, 4};
static const byte attrGivenName[] = {85, 4, 42};
#endif
#endif

/* kdfType */
static const byte pbkdf2Oid[] = {42, 134, 72, 134, 247, 13, 1, 5, 12};

/* PKCS5 */
#if !defined(NO_DES3) && !defined(NO_MD5)
static const byte pbeMd5Des[] = {42, 134, 72, 134, 247, 13, 1, 5, 3};
#endif
#if !defined(NO_DES3) && !defined(NO_SHA)
static const byte pbeSha1Des[] = {42, 134, 72, 134, 247, 13, 1, 5, 10};
#endif
static const byte pbes2[] = {42, 134, 72, 134, 247, 13, 1, 5, 13};

/* PKCS12 */
#if !defined(NO_RC4) && !defined(NO_SHA)
static const byte pbeSha1RC4128[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 1};
#endif
#if !defined(NO_DES3) && !defined(NO_SHA)
static const byte pbeSha1Des3[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 3};
#endif
#if defined(WC_RC2) && !defined(NO_SHA)
static const byte pbe40Rc2Cbc[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 6};
#endif

#ifdef HAVE_LIBZ
/* zlib compression */
static const byte zlibCompress[] = {42, 134, 72, 134, 247, 13, 1, 9, 16, 3, 8};
#endif
#ifdef WOLFSSL_APACHE_HTTPD
/* tlsExtType */
static const byte tlsFeatureOid[] = {43, 6, 1, 5, 5, 7, 1, 24};
/* certNameType */
static const byte dnsSRVOid[] = {43, 6, 1, 5, 5, 7, 8, 7};
#endif

#if defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_GEN) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(WOLFSSL_ASN_TEMPLATE)
/* Pilot attribute types (0.9.2342.19200300.100.1.*) */
#define PLT_ATTR_TYPE_OID_BASE(num) {9, 146, 38, 137, 147, 242, 44, 100, 1, num}
static const byte uidOid[] = PLT_ATTR_TYPE_OID_BASE(1); /* user id */
static const byte rfc822Mlbx[] = PLT_ATTR_TYPE_OID_BASE(3); /* RFC822 mailbox */
static const byte fvrtDrk[] = PLT_ATTR_TYPE_OID_BASE(5);/* favourite drink*/
#endif

#if defined(WOLFSSL_CERT_GEN) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(WOLFSSL_ASN_TEMPLATE)
static const byte dcOid[] = {9, 146, 38, 137, 147, 242, 44, 100, 1, 25}; /* domain component */
#endif


/* Looks up the ID/type of an OID.
 *
 * When known returns the OID as a byte array and its length.
 * ID-type are unique.
 *
 * Use oidIgnoreType to autofail.
 *
 * Note that while this function currently handles a large
 * number of FPKI certificate policy OIDs, these OIDs are not
 * currently being handled in the code, they are just recognized
 * as valid OIDs.
 *
 * @param [in]  id     OID id.
 * @param [in]  type   Type of OID (enum Oid_Types).
 * @param [out] oidSz  Length of OID byte array returned.
 * @return  Array of bytes for the OID.
 * @return  NULL when ID/type not recognized.
 */
const byte* OidFromId(word32 id, word32 type, word32* oidSz)
{
    const byte* oid = NULL;

    *oidSz = 0;

    switch (type) {

        case oidHashType:
            switch (id) {
            #ifdef WOLFSSL_MD2
                case MD2h:
                    oid = hashMd2hOid;
                    *oidSz = sizeof(hashMd2hOid);
                    break;
            #endif
            #ifndef NO_MD5
                case MD5h:
                    oid = hashMd5hOid;
                    *oidSz = sizeof(hashMd5hOid);
                    break;
            #endif
            #ifndef NO_SHA
                case SHAh:
                    oid = hashSha1hOid;
                    *oidSz = sizeof(hashSha1hOid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA224
                case SHA224h:
                    oid = hashSha224hOid;
                    *oidSz = sizeof(hashSha224hOid);
                    break;
            #endif
            #ifndef NO_SHA256
                case SHA256h:
                    oid = hashSha256hOid;
                    *oidSz = sizeof(hashSha256hOid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA384
                case SHA384h:
                    oid = hashSha384hOid;
                    *oidSz = sizeof(hashSha384hOid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA512
                #ifndef WOLFSSL_NOSHA512_224
                case SHA512_224h:
                    oid = hashSha512_224hOid;
                    *oidSz = sizeof(hashSha512_224hOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA512_256
                case SHA512_256h:
                    oid = hashSha512_256hOid;
                    *oidSz = sizeof(hashSha512_256hOid);
                    break;
                #endif
                case SHA512h:
                    oid = hashSha512hOid;
                    *oidSz = sizeof(hashSha512hOid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA3
            #ifndef WOLFSSL_NOSHA3_224
                case SHA3_224h:
                    oid = hashSha3_224hOid;
                    *oidSz = sizeof(hashSha3_224hOid);
                    break;
            #endif /* WOLFSSL_NOSHA3_224 */
            #ifndef WOLFSSL_NOSHA3_256
                case SHA3_256h:
                    oid = hashSha3_256hOid;
                    *oidSz = sizeof(hashSha3_256hOid);
                    break;
            #endif /* WOLFSSL_NOSHA3_256 */
            #ifndef WOLFSSL_NOSHA3_384
                case SHA3_384h:
                    oid = hashSha3_384hOid;
                    *oidSz = sizeof(hashSha3_384hOid);
                    break;
            #endif /* WOLFSSL_NOSHA3_384 */
            #ifndef WOLFSSL_NOSHA3_512
                case SHA3_512h:
                    oid = hashSha3_512hOid;
                    *oidSz = sizeof(hashSha3_512hOid);
                    break;
            #endif /* WOLFSSL_NOSHA3_512 */
            #ifdef WOLFSSL_SHAKE128
                case SHAKE128h:
                    oid = hashShake128hOid;
                    *oidSz = sizeof(hashShake128hOid);
                    break;
            #endif /* WOLFSSL_SHAKE128 */
            #ifdef WOLFSSL_SHAKE256
                case SHAKE256h:
                    oid = hashShake256hOid;
                    *oidSz = sizeof(hashShake256hOid);
                    break;
            #endif /* WOLFSSL_SHAKE256 */
            #endif /* WOLFSSL_SHA3 */
                default:
                    break;
            }
            break;

        case oidSigType:
            switch (id) {
                #if !defined(NO_DSA) && !defined(NO_SHA)
                case CTC_SHAwDSA:
                    oid = sigSha1wDsaOid;
                    *oidSz = sizeof(sigSha1wDsaOid);
                    break;
                case CTC_SHA256wDSA:
                    oid = sigSha256wDsaOid;
                    *oidSz = sizeof(sigSha256wDsaOid);
                    break;
                #endif /* NO_DSA */
                #ifndef NO_RSA
                #ifdef WOLFSSL_MD2
                case CTC_MD2wRSA:
                    oid = sigMd2wRsaOid;
                    *oidSz = sizeof(sigMd2wRsaOid);
                    break;
                #endif
                #ifndef NO_MD5
                case CTC_MD5wRSA:
                    oid = sigMd5wRsaOid;
                    *oidSz = sizeof(sigMd5wRsaOid);
                    break;
                #endif
                #ifndef NO_SHA
                case CTC_SHAwRSA:
                    oid = sigSha1wRsaOid;
                    *oidSz = sizeof(sigSha1wRsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA224
                case CTC_SHA224wRSA:
                    oid = sigSha224wRsaOid;
                    *oidSz = sizeof(sigSha224wRsaOid);
                    break;
                #endif
                #ifndef NO_SHA256
                case CTC_SHA256wRSA:
                    oid = sigSha256wRsaOid;
                    *oidSz = sizeof(sigSha256wRsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA384
                case CTC_SHA384wRSA:
                    oid = sigSha384wRsaOid;
                    *oidSz = sizeof(sigSha384wRsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA512
                case CTC_SHA512wRSA:
                    oid = sigSha512wRsaOid;
                    *oidSz = sizeof(sigSha512wRsaOid);
                    break;
                #endif /* WOLFSSL_SHA512 */
                #ifdef WOLFSSL_SHA3
                #ifndef WOLFSSL_NOSHA3_224
                case CTC_SHA3_224wRSA:
                    oid = sigSha3_224wRsaOid;
                    *oidSz = sizeof(sigSha3_224wRsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_256
                case CTC_SHA3_256wRSA:
                    oid = sigSha3_256wRsaOid;
                    *oidSz = sizeof(sigSha3_256wRsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_384
                case CTC_SHA3_384wRSA:
                    oid = sigSha3_384wRsaOid;
                    *oidSz = sizeof(sigSha3_384wRsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_512
                case CTC_SHA3_512wRSA:
                    oid = sigSha3_512wRsaOid;
                    *oidSz = sizeof(sigSha3_512wRsaOid);
                    break;
                #endif
                #endif
                #ifdef WC_RSA_PSS
                case CTC_RSASSAPSS:
                    oid = sigRsaSsaPssOid;
                    *oidSz = sizeof(sigRsaSsaPssOid);
                    break;
                #endif
                #endif /* NO_RSA */
                #ifdef HAVE_ECC
                #ifndef NO_SHA
                case CTC_SHAwECDSA:
                    oid = sigSha1wEcdsaOid;
                    *oidSz = sizeof(sigSha1wEcdsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA224
                case CTC_SHA224wECDSA:
                    oid = sigSha224wEcdsaOid;
                    *oidSz = sizeof(sigSha224wEcdsaOid);
                    break;
                #endif
                #ifndef NO_SHA256
                case CTC_SHA256wECDSA:
                    oid = sigSha256wEcdsaOid;
                    *oidSz = sizeof(sigSha256wEcdsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA384
                case CTC_SHA384wECDSA:
                    oid = sigSha384wEcdsaOid;
                    *oidSz = sizeof(sigSha384wEcdsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA512
                case CTC_SHA512wECDSA:
                    oid = sigSha512wEcdsaOid;
                    *oidSz = sizeof(sigSha512wEcdsaOid);
                    break;
                #endif
                #ifdef WOLFSSL_SHA3
                #ifndef WOLFSSL_NOSHA3_224
                case CTC_SHA3_224wECDSA:
                    oid = sigSha3_224wEcdsaOid;
                    *oidSz = sizeof(sigSha3_224wEcdsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_256
                case CTC_SHA3_256wECDSA:
                    oid = sigSha3_256wEcdsaOid;
                    *oidSz = sizeof(sigSha3_256wEcdsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_384
                case CTC_SHA3_384wECDSA:
                    oid = sigSha3_384wEcdsaOid;
                    *oidSz = sizeof(sigSha3_384wEcdsaOid);
                    break;
                #endif
                #ifndef WOLFSSL_NOSHA3_512
                case CTC_SHA3_512wECDSA:
                    oid = sigSha3_512wEcdsaOid;
                    *oidSz = sizeof(sigSha3_512wEcdsaOid);
                    break;
                #endif
                #endif
                #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                case CTC_SM3wSM2:
                    oid = sigSm3wSm2Oid;
                    *oidSz = sizeof(sigSm3wSm2Oid);
                    break;
                #endif
                #endif /* HAVE_ECC */
                #ifdef HAVE_ED25519
                case CTC_ED25519:
                    oid = sigEd25519Oid;
                    *oidSz = sizeof(sigEd25519Oid);
                    break;
                #endif
                #ifdef HAVE_ED448
                case CTC_ED448:
                    oid = sigEd448Oid;
                    *oidSz = sizeof(sigEd448Oid);
                    break;
                #endif
                #ifdef HAVE_FALCON
                case CTC_FALCON_LEVEL1:
                    oid = sigFalcon_Level1Oid;
                    *oidSz = sizeof(sigFalcon_Level1Oid);
                    break;
                case CTC_FALCON_LEVEL5:
                    oid = sigFalcon_Level5Oid;
                    *oidSz = sizeof(sigFalcon_Level5Oid);
                    break;
                #endif /* HAVE_FALCON */
            #ifdef WOLFSSL_HAVE_MLDSA
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                case CTC_DILITHIUM_LEVEL2:
                    oid = sigDilithium_Level2Oid;
                    *oidSz = sizeof(sigDilithium_Level2Oid);
                    break;
                case CTC_DILITHIUM_LEVEL3:
                    oid = sigDilithium_Level3Oid;
                    *oidSz = sizeof(sigDilithium_Level3Oid);
                    break;
                case CTC_DILITHIUM_LEVEL5:
                    oid = sigDilithium_Level5Oid;
                    *oidSz = sizeof(sigDilithium_Level5Oid);
                    break;
                #endif
                case CTC_ML_DSA_44:
                    oid = sigMlDsa_44Oid;
                    *oidSz = sizeof(sigMlDsa_44Oid);
                    break;
                case CTC_ML_DSA_65:
                    oid = sigMlDsa_65Oid;
                    *oidSz = sizeof(sigMlDsa_65Oid);
                    break;
                case CTC_ML_DSA_87:
                    oid = sigMlDsa_87Oid;
                    *oidSz = sizeof(sigMlDsa_87Oid);
                    break;
            #endif /* WOLFSSL_HAVE_MLDSA */
            #ifdef WOLFSSL_HAVE_SLHDSA
                case CTC_SLH_DSA_SHA2_128S:
                    oid = sigSlhDsa_Sha2_128sOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_128sOid);
                    break;
                case CTC_SLH_DSA_SHA2_128F:
                    oid = sigSlhDsa_Sha2_128fOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_128fOid);
                    break;
                case CTC_SLH_DSA_SHA2_192S:
                    oid = sigSlhDsa_Sha2_192sOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_192sOid);
                    break;
                case CTC_SLH_DSA_SHA2_192F:
                    oid = sigSlhDsa_Sha2_192fOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_192fOid);
                    break;
                case CTC_SLH_DSA_SHA2_256S:
                    oid = sigSlhDsa_Sha2_256sOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_256sOid);
                    break;
                case CTC_SLH_DSA_SHA2_256F:
                    oid = sigSlhDsa_Sha2_256fOid;
                    *oidSz = sizeof(sigSlhDsa_Sha2_256fOid);
                    break;
                case CTC_SLH_DSA_SHAKE_128S:
                    oid = sigSlhDsa_Shake_128sOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_128sOid);
                    break;
                case CTC_SLH_DSA_SHAKE_128F:
                    oid = sigSlhDsa_Shake_128fOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_128fOid);
                    break;
                case CTC_SLH_DSA_SHAKE_192S:
                    oid = sigSlhDsa_Shake_192sOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_192sOid);
                    break;
                case CTC_SLH_DSA_SHAKE_192F:
                    oid = sigSlhDsa_Shake_192fOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_192fOid);
                    break;
                case CTC_SLH_DSA_SHAKE_256S:
                    oid = sigSlhDsa_Shake_256sOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_256sOid);
                    break;
                case CTC_SLH_DSA_SHAKE_256F:
                    oid = sigSlhDsa_Shake_256fOid;
                    *oidSz = sizeof(sigSlhDsa_Shake_256fOid);
                    break;
            #endif /* WOLFSSL_HAVE_SLHDSA */
            #ifdef WOLFSSL_HAVE_LMS
                case CTC_HSS_LMS:
                    oid = sigHssLmsOid;
                    *oidSz = sizeof(sigHssLmsOid);
                    break;
            #endif /* WOLFSSL_HAVE_LMS */
            #ifdef WOLFSSL_HAVE_XMSS
                case CTC_XMSS:
                    oid = sigXmssOid;
                    *oidSz = sizeof(sigXmssOid);
                    break;
                case CTC_XMSSMT:
                    oid = sigXmssMtOid;
                    *oidSz = sizeof(sigXmssMtOid);
                    break;
            #endif /* WOLFSSL_HAVE_XMSS */
                default:
                    break;
            }
            break;

        case oidKeyType:
            switch (id) {
                #ifndef NO_DSA
                case DSAk:
                    oid = keyDsaOid;
                    *oidSz = sizeof(keyDsaOid);
                    break;
                #endif /* NO_DSA */
            #ifndef NO_RSA
                case RSAk:
                    oid = keyRsaOid;
                    *oidSz = sizeof(keyRsaOid);
                    break;
                #ifdef WC_RSA_PSS
                case RSAPSSk:
                    oid = keyRsaPssOid;
                    *oidSz = sizeof(keyRsaPssOid);
                    break;
                #endif
            #endif /* NO_RSA */
                #ifdef HAVE_ECC
                case ECDSAk:
                    oid = keyEcdsaOid;
                    *oidSz = sizeof(keyEcdsaOid);
                    break;
                #endif /* HAVE_ECC */
                #ifdef HAVE_ED25519
                case ED25519k:
                    oid = keyEd25519Oid;
                    *oidSz = sizeof(keyEd25519Oid);
                    break;
                #endif /* HAVE_ED25519 */
                #ifdef HAVE_CURVE25519
                case X25519k:
                    oid = keyCurve25519Oid;
                    *oidSz = sizeof(keyCurve25519Oid);
                    break;
                #endif /* HAVE_CURVE25519 */
                #ifdef HAVE_ED448
                case ED448k:
                    oid = keyEd448Oid;
                    *oidSz = sizeof(keyEd448Oid);
                    break;
                #endif /* HAVE_ED448 */
                #ifdef HAVE_CURVE448
                case X448k:
                    oid = keyCurve448Oid;
                    *oidSz = sizeof(keyCurve448Oid);
                    break;
                #endif /* HAVE_CURVE448 */
                #ifndef NO_DH
                case DHk:
                    oid = keyDhOid;
                    *oidSz = sizeof(keyDhOid);
                    break;
                #endif /* !NO_DH */
                #ifdef HAVE_FALCON
                case FALCON_LEVEL1k:
                    oid = keyFalcon_Level1Oid;
                    *oidSz = sizeof(keyFalcon_Level1Oid);
                    break;
                case FALCON_LEVEL5k:
                    oid = keyFalcon_Level5Oid;
                    *oidSz = sizeof(keyFalcon_Level5Oid);
                    break;
                #endif /* HAVE_FALCON */
            #ifdef WOLFSSL_HAVE_MLDSA
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                case DILITHIUM_LEVEL2k:
                    oid = keyDilithium_Level2Oid;
                    *oidSz = sizeof(keyDilithium_Level2Oid);
                    break;
                case DILITHIUM_LEVEL3k:
                    oid = keyDilithium_Level3Oid;
                    *oidSz = sizeof(keyDilithium_Level3Oid);
                    break;
                case DILITHIUM_LEVEL5k:
                    oid = keyDilithium_Level5Oid;
                    *oidSz = sizeof(keyDilithium_Level5Oid);
                    break;
                #endif
                case ML_DSA_44k:
                    oid = keyMlDsa_44Oid;
                    *oidSz = sizeof(keyMlDsa_44Oid);
                    break;
                case ML_DSA_65k:
                    oid = keyMlDsa_65Oid;
                    *oidSz = sizeof(keyMlDsa_65Oid);
                    break;
                case ML_DSA_87k:
                    oid = keyMlDsa_87Oid;
                    *oidSz = sizeof(keyMlDsa_87Oid);
                    break;
            #endif /* WOLFSSL_HAVE_MLDSA */
            #ifdef WOLFSSL_HAVE_SLHDSA
                case SLH_DSA_SHA2_128Sk:
                    oid = keySlhDsa_Sha2_128sOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_128sOid);
                    break;
                case SLH_DSA_SHA2_128Fk:
                    oid = keySlhDsa_Sha2_128fOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_128fOid);
                    break;
                case SLH_DSA_SHA2_192Sk:
                    oid = keySlhDsa_Sha2_192sOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_192sOid);
                    break;
                case SLH_DSA_SHA2_192Fk:
                    oid = keySlhDsa_Sha2_192fOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_192fOid);
                    break;
                case SLH_DSA_SHA2_256Sk:
                    oid = keySlhDsa_Sha2_256sOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_256sOid);
                    break;
                case SLH_DSA_SHA2_256Fk:
                    oid = keySlhDsa_Sha2_256fOid;
                    *oidSz = sizeof(keySlhDsa_Sha2_256fOid);
                    break;
                case SLH_DSA_SHAKE_128Sk:
                    oid = keySlhDsa_Shake_128sOid;
                    *oidSz = sizeof(keySlhDsa_Shake_128sOid);
                    break;
                case SLH_DSA_SHAKE_128Fk:
                    oid = keySlhDsa_Shake_128fOid;
                    *oidSz = sizeof(keySlhDsa_Shake_128fOid);
                    break;
                case SLH_DSA_SHAKE_192Sk:
                    oid = keySlhDsa_Shake_192sOid;
                    *oidSz = sizeof(keySlhDsa_Shake_192sOid);
                    break;
                case SLH_DSA_SHAKE_192Fk:
                    oid = keySlhDsa_Shake_192fOid;
                    *oidSz = sizeof(keySlhDsa_Shake_192fOid);
                    break;
                case SLH_DSA_SHAKE_256Sk:
                    oid = keySlhDsa_Shake_256sOid;
                    *oidSz = sizeof(keySlhDsa_Shake_256sOid);
                    break;
                case SLH_DSA_SHAKE_256Fk:
                    oid = keySlhDsa_Shake_256fOid;
                    *oidSz = sizeof(keySlhDsa_Shake_256fOid);
                    break;
            #endif /* WOLFSSL_HAVE_SLHDSA */
            #ifdef WOLFSSL_HAVE_LMS
                case HSS_LMSk:
                    oid = keyHssLmsOid;
                    *oidSz = sizeof(keyHssLmsOid);
                    break;
            #endif /* WOLFSSL_HAVE_LMS */
            #ifdef WOLFSSL_HAVE_XMSS
                case XMSSk:
                    oid = keyXmssOid;
                    *oidSz = sizeof(keyXmssOid);
                    break;
                case XMSSMTk:
                    oid = keyXmssMtOid;
                    *oidSz = sizeof(keyXmssMtOid);
                    break;
            #endif /* WOLFSSL_HAVE_XMSS */
                default:
                    break;
            }
            break;

        #ifdef HAVE_ECC
        case oidCurveType:
            if (wc_ecc_get_oid(id, &oid, oidSz) < 0) {
                WOLFSSL_MSG("ECC OID not found");
            }
            break;
        #endif /* HAVE_ECC */

        case oidBlkType:
            switch (id) {
    #ifdef HAVE_AES_CBC
        #ifdef WOLFSSL_AES_128
                case AES128CBCb:
                    oid = blkAes128CbcOid;
                    *oidSz = sizeof(blkAes128CbcOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_192
                case AES192CBCb:
                    oid = blkAes192CbcOid;
                    *oidSz = sizeof(blkAes192CbcOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_256
                case AES256CBCb:
                    oid = blkAes256CbcOid;
                    *oidSz = sizeof(blkAes256CbcOid);
                    break;
        #endif
    #endif /* HAVE_AES_CBC */
    #ifdef HAVE_AESGCM
        #ifdef WOLFSSL_AES_128
                case AES128GCMb:
                    oid = blkAes128GcmOid;
                    *oidSz = sizeof(blkAes128GcmOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_192
                case AES192GCMb:
                    oid = blkAes192GcmOid;
                    *oidSz = sizeof(blkAes192GcmOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_256
                case AES256GCMb:
                    oid = blkAes256GcmOid;
                    *oidSz = sizeof(blkAes256GcmOid);
                    break;
        #endif
    #endif /* HAVE_AESGCM */
    #ifdef HAVE_AESCCM
        #ifdef WOLFSSL_AES_128
                case AES128CCMb:
                    oid = blkAes128CcmOid;
                    *oidSz = sizeof(blkAes128CcmOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_192
                case AES192CCMb:
                    oid = blkAes192CcmOid;
                    *oidSz = sizeof(blkAes192CcmOid);
                    break;
        #endif
        #ifdef WOLFSSL_AES_256
                case AES256CCMb:
                    oid = blkAes256CcmOid;
                    *oidSz = sizeof(blkAes256CcmOid);
                    break;
        #endif
    #endif /* HAVE_AESCCM */
    #ifndef NO_DES3
                case DESb:
                    oid = blkDesCbcOid;
                    *oidSz = sizeof(blkDesCbcOid);
                    break;
                case DES3b:
                    oid = blkDes3CbcOid;
                    *oidSz = sizeof(blkDes3CbcOid);
                    break;
    #endif /* !NO_DES3 */
                default:
                    break;
            }
            break;

        #ifdef HAVE_OCSP
        case oidOcspType:
            switch (id) {
                case OCSP_BASIC_OID:
                    oid = ocspBasicOid;
                    *oidSz = sizeof(ocspBasicOid);
                    break;
                case OCSP_NONCE_OID:
                    oid = ocspNonceOid;
                    *oidSz = sizeof(ocspNonceOid);
                    break;
                default:
                    break;
            }
            break;
        #endif /* HAVE_OCSP */

        case oidCertExtType:
            switch (id) {
                case BASIC_CA_OID:
                    oid = extBasicCaOid;
                    *oidSz = sizeof(extBasicCaOid);
                    break;
                case ALT_NAMES_OID:
                    oid = extAltNamesOid;
                    *oidSz = sizeof(extAltNamesOid);
                    break;
                case CRL_DIST_OID:
                    oid = extCrlDistOid;
                    *oidSz = sizeof(extCrlDistOid);
                    break;
                case AUTH_INFO_OID:
                    oid = extAuthInfoOid;
                    *oidSz = sizeof(extAuthInfoOid);
                    break;
                case AUTH_KEY_OID:
                    oid = extAuthKeyOid;
                    *oidSz = sizeof(extAuthKeyOid);
                    break;
                case SUBJ_KEY_OID:
                    oid = extSubjKeyOid;
                    *oidSz = sizeof(extSubjKeyOid);
                    break;
                case CERT_POLICY_OID:
                    oid = extCertPolicyOid;
                    *oidSz = sizeof(extCertPolicyOid);
                    break;
                case KEY_USAGE_OID:
                    oid = extKeyUsageOid;
                    *oidSz = sizeof(extKeyUsageOid);
                    break;
                case INHIBIT_ANY_OID:
                    oid = extInhibitAnyOid;
                    *oidSz = sizeof(extInhibitAnyOid);
                    break;
                case EXT_KEY_USAGE_OID:
                    oid = extExtKeyUsageOid;
                    *oidSz = sizeof(extExtKeyUsageOid);
                    break;
            #ifndef IGNORE_NAME_CONSTRAINTS
                case NAME_CONS_OID:
                    oid = extNameConsOid;
                    *oidSz = sizeof(extNameConsOid);
                    break;
            #endif
            #ifdef HAVE_OCSP
                case OCSP_NOCHECK_OID:
                    oid = ocspNoCheckOid;
                    *oidSz = sizeof(ocspNoCheckOid);
                    break;
            #endif
            #ifdef WOLFSSL_SUBJ_DIR_ATTR
                case SUBJ_DIR_ATTR_OID:
                    oid = extSubjDirAttrOid;
                    *oidSz = sizeof(extSubjDirAttrOid);
                    break;
            #endif
            #ifdef WOLFSSL_SUBJ_INFO_ACC
                case SUBJ_INFO_ACC_OID:
                    oid = extSubjInfoAccessOid;
                    *oidSz = sizeof(extSubjInfoAccessOid);
                    break;
            #endif
                default:
                    break;
            }
            break;

        case oidCrlExtType:
            #ifdef HAVE_CRL
            switch (id) {
                case AUTH_KEY_OID:
                    oid = extAuthKeyOid;
                    *oidSz = sizeof(extAuthKeyOid);
                    break;
                case CRL_NUMBER_OID:
                    oid = extCrlNumberOid;
                    *oidSz = sizeof(extCrlNumberOid);
                    break;
                default:
                    break;
            }
            #endif
            break;

        case oidCertAuthInfoType:
            switch (id) {
                case AIA_OCSP_OID:
                    oid = extAuthInfoOcspOid;
                    *oidSz = sizeof(extAuthInfoOcspOid);
                    break;
                case AIA_CA_ISSUER_OID:
                    oid = extAuthInfoCaIssuerOid;
                    *oidSz = sizeof(extAuthInfoCaIssuerOid);
                    break;
                #ifdef WOLFSSL_SUBJ_INFO_ACC
                case AIA_CA_REPO_OID:
                    oid = extAuthInfoCaRespOid;
                    *oidSz = sizeof(extAuthInfoCaRespOid);
                    break;
                #endif /* WOLFSSL_SUBJ_INFO_ACC */
                default:
                    break;
            }
            break;

        case oidCertPolicyType:
            switch (id) {
                case CP_ANY_OID:
                    oid = extCertPolicyAnyOid;
                    *oidSz = sizeof(extCertPolicyAnyOid);
                    break;
                case CP_ISRG_DOMAIN_VALID:
                    oid = extCertPolicyIsrgDomainValid;
                    *oidSz = sizeof(extCertPolicyIsrgDomainValid);
                    break;
                #if defined(WOLFSSL_FPKI)
                case CP_FPKI_HIGH_ASSURANCE_OID:
                    oid = extCertPolicyFpkiHighAssuranceOid;
                    *oidSz = sizeof(extCertPolicyFpkiHighAssuranceOid);
                    break;
                case CP_FPKI_COMMON_HARDWARE_OID:
                    oid = extCertPolicyFpkiCommonHardwareOid;
                    *oidSz = sizeof(extCertPolicyFpkiCommonHardwareOid);
                    break;
                case CP_FPKI_MEDIUM_HARDWARE_OID:
                    oid = extCertPolicyFpkiMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyFpkiMediumHardwareOid);
                    break;
                case CP_FPKI_COMMON_HIGH_OID:
                    oid = extCertPolicyFpkiCommonHighOid;
                    *oidSz = sizeof(extCertPolicyFpkiCommonHighOid);
                    break;
                case CP_FPKI_COMMON_DEVICES_HARDWARE_OID:
                    oid = extCertPolicyFpkiCommonDevicesHardwareOid;
                    *oidSz = sizeof(extCertPolicyFpkiCommonDevicesHardwareOid);
                    break;
                case CP_FPKI_COMMON_PIV_CONTENT_SIGNING_OID:
                    oid = extCertPolicyFpkiCommonPivContentSigningOid;
                    *oidSz = sizeof(extCertPolicyFpkiCommonPivContentSigningOid);
                    break;
                case CP_FPKI_COMMON_AUTH_OID:
                    oid = extCertPolicyFpkiCommonAuthOid;
                    *oidSz = sizeof(extCertPolicyFpkiCommonAuthOid);
                    break;
                case CP_FPKI_PIV_AUTH_OID:
                    oid = extCertPolicyFpkiPivAuthOid;
                    *oidSz = sizeof(extCertPolicyFpkiPivAuthOid);
                    break;
                case CP_FPKI_PIV_AUTH_HW_OID: /* collision with AES256CBCb */
                    oid = extCertPolicyFpkiPivAuthHwOid;
                    *oidSz = sizeof(extCertPolicyFpkiPivAuthHwOid);
                    break;
                case CP_FPKI_PIVI_AUTH_OID:
                    oid = extCertPolicyFpkiPiviAuthOid;
                    *oidSz = sizeof(extCertPolicyFpkiPiviAuthOid);
                    break;
                case CP_FPKI_AUTH_TEST_OID:
                    oid = extCertPolicyFpkiAuthTestOid;
                    *oidSz = sizeof(extCertPolicyFpkiAuthTestOid);
                    break;
                case CP_FPKI_CARDAUTH_TEST_OID:
                    oid = extCertPolicyFpkiCardauthTestOid;
                    *oidSz = sizeof(extCertPolicyFpkiCardauthTestOid);
                    break;
                case CP_FPKI_PIV_CONTENT_TEST_OID:
                    oid = extCertPolicyFpkiPivContentTestOid;
                    *oidSz = sizeof(extCertPolicyFpkiPivContentTestOid);
                    break;
                case CP_FPKI_PIV_AUTH_DERIVED_TEST_OID:
                    oid = extCertPolicyFpkiAuthDerivedTestOid;
                    *oidSz = sizeof(extCertPolicyFpkiAuthDerivedTestOid);
                    break;
                case CP_FPKI_PIV_AUTH_DERIVED_HW_TEST_OID:
                    oid = extCertPolicyFpkiAuthDerivedHwTestOid;
                    *oidSz = sizeof(extCertPolicyFpkiAuthDerivedHwTestOid);
                    break;
                case CP_DOD_MEDIUM_OID:
                    oid = extCertPolicyDodMediumOid;
                    *oidSz = sizeof(extCertPolicyDodMediumOid);
                    break;
                case CP_DOD_MEDIUM_HARDWARE_OID:
                    oid = extCertPolicyDodMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyDodMediumHardwareOid);
                    break;
                case CP_DOD_PIV_AUTH_OID:
                    oid = extCertPolicyDodPivAuthOid;
                    *oidSz = sizeof(extCertPolicyDodPivAuthOid);
                    break;
                case CP_DOD_MEDIUM_NPE_OID:
                    oid = extCertPolicyDodMediumNpeOid;
                    *oidSz = sizeof(extCertPolicyDodMediumNpeOid);
                    break;
                case CP_DOD_MEDIUM_2048_OID:
                    oid = extCertPolicyDodMedium2048Oid;
                    *oidSz = sizeof(extCertPolicyDodMedium2048Oid);
                    break;
                case CP_DOD_MEDIUM_HARDWARE_2048_OID:
                    oid = extCertPolicyDodMediumHardware2048Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumHardware2048Oid);
                    break;
                case CP_DOD_PIV_AUTH_2048_OID:
                    oid = extCertPolicyDodPivAuth2048Oid;
                    *oidSz = sizeof(extCertPolicyDodPivAuth2048Oid);
                    break;
                case CP_DOD_PEER_INTEROP_OID:
                    oid = extCertPolicyDodPeerInteropOid;
                    *oidSz = sizeof(extCertPolicyDodPeerInteropOid);
                    break;
                case CP_DOD_MEDIUM_NPE_112_OID:
                    oid = extCertPolicyDodMediumNpe112Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumNpe112Oid);
                    break;
                case CP_DOD_MEDIUM_NPE_128_OID:
                    oid = extCertPolicyDodMediumNpe128Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumNpe128Oid);
                    break;
                case CP_DOD_MEDIUM_NPE_192_OID:
                    oid = extCertPolicyDodMediumNpe192Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumNpe192Oid);
                    break;
                case CP_DOD_MEDIUM_112_OID:
                    oid = extCertPolicyDodMedium112Oid;
                    *oidSz = sizeof(extCertPolicyDodMedium112Oid);
                    break;
                case CP_DOD_MEDIUM_128_OID:
                    oid = extCertPolicyDodMedium128Oid;
                    *oidSz = sizeof(extCertPolicyDodMedium128Oid);
                    break;
                case CP_DOD_MEDIUM_192_OID:
                    oid = extCertPolicyDodMedium192Oid;
                    *oidSz = sizeof(extCertPolicyDodMedium192Oid);
                    break;
                case CP_DOD_MEDIUM_HARDWARE_112_OID:
                    oid = extCertPolicyDodMediumHardware112Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumHardware112Oid);
                    break;
                case CP_DOD_MEDIUM_HARDWARE_128_OID:
                    oid = extCertPolicyDodMediumHardware128Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumHardware128Oid);
                    break;
                case CP_DOD_MEDIUM_HARDWARE_192_OID:
                    oid = extCertPolicyDodMediumHardware192Oid;
                    *oidSz = sizeof(extCertPolicyDodMediumHardware192Oid);
                    break;
                case CP_DOD_ADMIN_OID:
                    oid = extCertPolicyDodAdminOid;
                    *oidSz = sizeof(extCertPolicyDodAdminOid);
                    break;
                case CP_DOD_INTERNAL_NPE_112_OID:
                    oid = extCertPolicyDodInternalNpe112Oid;
                    *oidSz = sizeof(extCertPolicyDodInternalNpe112Oid);
                    break;
                case CP_DOD_INTERNAL_NPE_128_OID:
                    oid = extCertPolicyDodInternalNpe128Oid;
                    *oidSz = sizeof(extCertPolicyDodInternalNpe128Oid);
                    break;
                case CP_DOD_INTERNAL_NPE_192_OID:
                    oid = extCertPolicyDodInternalNpe192Oid;
                    *oidSz = sizeof(extCertPolicyDodInternalNpe192Oid);
                    break;
                case CP_ECA_MEDIUM_OID:
                    oid = extCertPolicyEcaMediumOid;
                    *oidSz = sizeof(extCertPolicyEcaMediumOid);
                    break;
                case CP_ECA_MEDIUM_HARDWARE_OID:
                    oid = extCertPolicyEcaMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyEcaMediumHardwareOid);
                    break;
                case CP_ECA_MEDIUM_TOKEN_OID:
                    oid = extCertPolicyEcaMediumTokenOid;
                    *oidSz = sizeof(extCertPolicyEcaMediumTokenOid);
                    break;
                case CP_ECA_MEDIUM_SHA256_OID:
                    oid = extCertPolicyEcaMediumSha256Oid;
                    *oidSz = sizeof(extCertPolicyEcaMediumSha256Oid);
                    break;
                case CP_ECA_MEDIUM_TOKEN_SHA256_OID:
                    oid = extCertPolicyEcaMediumTokenSha256Oid;
                    *oidSz = sizeof(extCertPolicyEcaMediumTokenSha256Oid);
                    break;
                case CP_ECA_MEDIUM_HARDWARE_PIVI_OID:
                    oid = extCertPolicyEcaMediumHardwarePiviOid;
                    *oidSz = sizeof(extCertPolicyEcaMediumHardwarePiviOid);
                    break;
                case CP_ECA_CONTENT_SIGNING_PIVI_OID:
                    oid = extCertPolicyEcaContentSigningPiviOid;
                    *oidSz = sizeof(extCertPolicyEcaContentSigningPiviOid);
                    break;
                case CP_ECA_MEDIUM_DEVICE_SHA256_OID:
                    oid = extCertPolicyEcaMediumDeviceSha256Oid;
                    *oidSz = sizeof(extCertPolicyEcaMediumDeviceSha256Oid);
                    break;
                case CP_ECA_MEDIUM_HARDWARE_SHA256_OID:
                    oid = extCertPolicyEcaMediumHardwareSha256Oid;
                    *oidSz = sizeof(extCertPolicyEcaMediumHardwareSha256Oid);
                    break;

                /* Department of State PKI OIDs */
                case CP_STATE_BASIC_OID:
                    oid = extCertPolicyStateBasicOid;
                    *oidSz = sizeof(extCertPolicyStateBasicOid);
                    break;
                case CP_STATE_LOW_OID:
                    oid = extCertPolicyStateLowOid;
                    *oidSz = sizeof(extCertPolicyStateLowOid);
                    break;
                case CP_STATE_MODERATE_OID:
                    oid = extCertPolicyStateModerateOid;
                    *oidSz = sizeof(extCertPolicyStateModerateOid);
                    break;
                case CP_STATE_HIGH_OID:
                    oid = extCertPolicyStateHighOid;
                    *oidSz = sizeof(extCertPolicyStateHighOid);
                    break;
                case CP_STATE_MEDHW_OID:
                    oid = extCertPolicyStateMedHwOid;
                    *oidSz = sizeof(extCertPolicyStateMedHwOid);
                    break;
                case CP_STATE_MEDDEVHW_OID:
                    oid = extCertPolicyStateMediumDeviceHardwareOid;
                    *oidSz = sizeof(extCertPolicyStateMediumDeviceHardwareOid);
                    break;

                /* U.S. Treasury SSP PKI OIDs */
                case CP_TREAS_MEDIUMHW_OID:
                    oid = extCertPolicyTreasuryMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyTreasuryMediumHardwareOid);
                    break;
                case CP_TREAS_HIGH_OID:
                    oid = extCertPolicyTreasuryHighOid;
                    *oidSz = sizeof(extCertPolicyTreasuryHighOid);
                    break;
                case CP_TREAS_PIVI_HW_OID:
                    oid = extCertPolicyTreasuryPiviHardwareOid;
                    *oidSz = sizeof(extCertPolicyTreasuryPiviHardwareOid);
                    break;
                case CP_TREAS_PIVI_CONTENT_OID:
                    oid = extCertPolicyTreasuryPiviContentSigningOid;
                    *oidSz = sizeof(extCertPolicyTreasuryPiviContentSigningOid);
                    break;

                /* Boeing PKI OIDs */
                case CP_BOEING_MEDIUMHW_SHA256_OID:
                    oid = extCertPolicyBoeingMediumHardwareSha256Oid;
                    *oidSz = sizeof(extCertPolicyBoeingMediumHardwareSha256Oid);
                    break;
                case CP_BOEING_MEDIUMHW_CONTENT_SHA256_OID:
                    oid = extCertPolicyBoeingMediumHardwareContentSigningSha256Oid;
                    *oidSz = sizeof(extCertPolicyBoeingMediumHardwareContentSigningSha256Oid);
                    break;

                /* DigiCert NFI PKI OIDs */
                case CP_DIGICERT_NFSSP_MEDIUMHW_OID:
                    oid = extCertPolicyDigicertNfiMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyDigicertNfiMediumHardwareOid);
                    break;
                case CP_DIGICERT_NFSSP_AUTH_OID:
                    oid = extCertPolicyDigicertNfiAuthOid;
                    *oidSz = sizeof(extCertPolicyDigicertNfiAuthOid);
                    break;
                case CP_DIGICERT_NFSSP_PIVI_HW_OID:
                    oid = extCertPolicyDigicertNfiPiviHardwareOid;
                    *oidSz = sizeof(extCertPolicyDigicertNfiPiviHardwareOid);
                    break;
                case CP_DIGICERT_NFSSP_PIVI_CONTENT_OID:
                    oid = extCertPolicyDigicertNfiPiviContentSigningOid;
                    *oidSz = sizeof(extCertPolicyDigicertNfiPiviContentSigningOid);
                    break;
                case CP_DIGICERT_NFSSP_MEDDEVHW_OID:
                    oid = extCertPolicyDigicertNfiMediumDevicesHardwareOid;
                    *oidSz = sizeof(extCertPolicyDigicertNfiMediumDevicesHardwareOid);
                    break;

                /* Entrust Managed Services NFI PKI OIDs */
                case CP_ENTRUST_NFSSP_MEDIUMHW_OID:
                    oid = extCertPolicyEntrustNfiMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyEntrustNfiMediumHardwareOid);
                    break;
                case CP_ENTRUST_NFSSP_MEDAUTH_OID:
                    oid = extCertPolicyEntrustNfiMediumAuthenticationOid;
                    *oidSz = sizeof(extCertPolicyEntrustNfiMediumAuthenticationOid);
                    break;
                case CP_ENTRUST_NFSSP_PIVI_HW_OID:
                    oid = extCertPolicyEntrustNfiPiviHardwareOid;
                    *oidSz = sizeof(extCertPolicyEntrustNfiPiviHardwareOid);
                    break;
                case CP_ENTRUST_NFSSP_PIVI_CONTENT_OID:
                    oid = extCertPolicyEntrustNfiPiviContentSigningOid;
                    *oidSz = sizeof(extCertPolicyEntrustNfiPiviContentSigningOid);
                    break;
                case CP_ENTRUST_NFSSP_MEDDEVHW_OID:
                    oid = extCertPolicyEntrustNfiMediumDevicesHwOid;
                    *oidSz = sizeof(extCertPolicyEntrustNfiMediumDevicesHwOid);
                    break;

                /* Exostar LLC PKI OIDs */
                case CP_EXOSTAR_MEDIUMHW_SHA2_OID:
                    oid = extCertPolicyExostarMediumHardwareSha2Oid;
                    *oidSz = sizeof(extCertPolicyExostarMediumHardwareSha2Oid);
                    break;

                /* Lockheed Martin PKI OIDs */
                case CP_LOCKHEED_MEDIUMHW_OID:
                    oid = extCertPolicyLockheedMediumAssuranceHardwareOid;
                    *oidSz = sizeof(extCertPolicyLockheedMediumAssuranceHardwareOid);
                    break;

                /* Northrop Grumman PKI OIDs */
                case CP_NORTHROP_MEDIUM_256_HW_OID:
                    oid = extCertPolicyNorthropMediumAssurance256HardwareTokenOid;
                    *oidSz = sizeof(extCertPolicyNorthropMediumAssurance256HardwareTokenOid);
                    break;
                case CP_NORTHROP_PIVI_256_HW_OID:
                    oid = extCertPolicyNorthropPiviAssurance256HardwareTokenOid;
                    *oidSz = sizeof(extCertPolicyNorthropPiviAssurance256HardwareTokenOid);
                    break;
                case CP_NORTHROP_PIVI_256_CONTENT_OID:
                    oid = extCertPolicyNorthropPiviAssurance256ContentSigningOid;
                    *oidSz = sizeof(extCertPolicyNorthropPiviAssurance256ContentSigningOid);
                    break;
                case CP_NORTHROP_MEDIUM_384_HW_OID:
                    oid = extCertPolicyNorthropMediumAssurance384HardwareTokenOid;
                    *oidSz = sizeof(extCertPolicyNorthropMediumAssurance384HardwareTokenOid);
                    break;

                /* Raytheon PKI OIDs */
                case CP_RAYTHEON_MEDIUMHW_OID:
                    oid = extCertPolicyRaytheonMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyRaytheonMediumHardwareOid);
                    break;
                case CP_RAYTHEON_MEDDEVHW_OID:
                    oid = extCertPolicyRaytheonMediumDeviceHardwareOid;
                    *oidSz = sizeof(extCertPolicyRaytheonMediumDeviceHardwareOid);
                    break;
                case CP_RAYTHEON_SHA2_MEDIUMHW_OID:
                    oid = extCertPolicyRaytheonSha2MediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyRaytheonSha2MediumHardwareOid);
                    break;
                case CP_RAYTHEON_SHA2_MEDDEVHW_OID:
                    oid = extCertPolicyRaytheonSha2MediumDeviceHardwareOid;
                   *oidSz = sizeof(extCertPolicyRaytheonSha2MediumDeviceHardwareOid);
                    break;

                /* WidePoint NFI PKI OIDs */
                case CP_WIDEPOINT_MEDIUMHW_OID:
                    oid = extCertPolicyWidepointNfiMediumHardwareOid;
                    *oidSz = sizeof(extCertPolicyWidepointNfiMediumHardwareOid);
                    break;
                case CP_WIDEPOINT_PIVI_HW_OID:
                    oid = extCertPolicyWidepointNfiPiviHardwareOid;
                    *oidSz = sizeof(extCertPolicyWidepointNfiPiviHardwareOid);
                    break;
                case CP_WIDEPOINT_PIVI_CONTENT_OID:
                    oid = extCertPolicyWidepointNfiPiviContentSigningOid;
                    *oidSz = sizeof(extCertPolicyWidepointNfiPiviContentSigningOid);
                    break;
                case CP_WIDEPOINT_MEDDEVHW_OID:
                    oid = extCertPolicyWidepointNfiMediumDevicesHardwareOid;
                    *oidSz = sizeof(extCertPolicyWidepointNfiMediumDevicesHardwareOid);
                    break;

                /* Australian Defence Organisation PKI OIDs */
                case CP_ADO_MEDIUM_OID:
                    oid = extCertPolicyAdoIndividualMediumAssuranceOid;
                    *oidSz = sizeof(extCertPolicyAdoIndividualMediumAssuranceOid);
                    break;
                case CP_ADO_HIGH_OID:
                    oid = extCertPolicyAdoIndividualHighAssuranceOid;
                    *oidSz = sizeof(extCertPolicyAdoIndividualHighAssuranceOid);
                    break;
                case CP_ADO_RESOURCE_MEDIUM_OID:
                    oid = extCertPolicyAdoResourceMediumAssuranceOid;
                    *oidSz = sizeof(extCertPolicyAdoResourceMediumAssuranceOid);
                    break;

                /* Netherlands Ministry of Defence PKI OIDs */
                case CP_NL_MOD_AUTH_OID:
                    oid = extCertPolicyNlModAuthenticityOid;
                    *oidSz = sizeof(extCertPolicyNlModAuthenticityOid);
                    break;
                case CP_NL_MOD_IRREFUT_OID:
                    oid = extCertPolicyNlModIrrefutabilityOid;
                    *oidSz = sizeof(extCertPolicyNlModIrrefutabilityOid);
                    break;
                case CP_NL_MOD_CONFID_OID:
                    oid = extCertPolicyNlModConfidentialityOid;
                    *oidSz = sizeof(extCertPolicyNlModConfidentialityOid);
                    break;

                /* IdenTrust NFI OIDs */
                case CP_IDENTRUST_MEDIUMHW_SIGN_OID:
                    oid = extCertPolicyIdentrustMediumhwSignOid;
                    *oidSz = sizeof(extCertPolicyIdentrustMediumhwSignOid);
                    break;
                case CP_IDENTRUST_MEDIUMHW_ENC_OID:
                    oid = extCertPolicyIdentrustMediumhwEncOid;
                    *oidSz = sizeof(extCertPolicyIdentrustMediumhwEncOid);
                    break;
                case CP_IDENTRUST_PIVI_HW_ID_OID:
                    oid = extCertPolicyIdentrustPiviHwIdOid;
                    *oidSz = sizeof(extCertPolicyIdentrustPiviHwIdOid);
                    break;
                case CP_IDENTRUST_PIVI_HW_SIGN_OID:
                    oid = extCertPolicyIdentrustPiviHwSignOid;
                    *oidSz = sizeof(extCertPolicyIdentrustPiviHwSignOid);
                    break;
                case CP_IDENTRUST_PIVI_HW_ENC_OID:
                    oid = extCertPolicyIdentrustPiviHwEncOid;
                    *oidSz = sizeof(extCertPolicyIdentrustPiviHwEncOid);
                    break;
                case CP_IDENTRUST_PIVI_CONTENT_OID:
                    oid = extCertPolicyIdentrustPiviContentOid;
                    *oidSz = sizeof(extCertPolicyIdentrustPiviContentOid);
                    break;

                /* TSCP Bridge OIDs */
                case CP_TSCP_MEDIUMHW_OID:
                    oid = extCertPolicyTscpMediumhwOid;
                    *oidSz = sizeof(extCertPolicyTscpMediumhwOid);
                    break;
                case CP_TSCP_PIVI_OID:
                    oid = extCertPolicyTscpPiviOid;
                    *oidSz = sizeof(extCertPolicyTscpPiviOid);
                    break;
                case CP_TSCP_PIVI_CONTENT_OID:
                    oid = extCertPolicyTscpPiviContentOid;
                    *oidSz = sizeof(extCertPolicyTscpPiviContentOid);
                    break;

                /* Carillon Federal Services OIDs */
                case CP_CARILLON_MEDIUMHW_256_OID:
                    oid = extCertPolicyCarillonMediumhw256Oid;
                    *oidSz = sizeof(extCertPolicyCarillonMediumhw256Oid);
                    break;
                case CP_CARILLON_AIVHW_OID:
                    oid = extCertPolicyCarillonAivhwOid;
                    *oidSz = sizeof(extCertPolicyCarillonAivhwOid);
                    break;
                case CP_CARILLON_AIVCONTENT_OID:
                    oid = extCertPolicyCarillonAivcontentOid;
                    *oidSz = sizeof(extCertPolicyCarillonAivcontentOid);
                    break;

                /* Carillon Information Security OIDs */
                case CP_CIS_MEDIUMHW_256_OID:
                    oid = extCertPolicyCisMediumhw256Oid;
                    *oidSz = sizeof(extCertPolicyCisMediumhw256Oid);
                    break;
                case CP_CIS_MEDDEVHW_256_OID:
                    oid = extCertPolicyCisMeddevhw256Oid;
                    *oidSz = sizeof(extCertPolicyCisMeddevhw256Oid);
                    break;
                case CP_CIS_ICECAP_HW_OID:
                    oid = extCertPolicyCisIcecapHwOid;
                    *oidSz = sizeof(extCertPolicyCisIcecapHwOid);
                    break;
                case CP_CIS_ICECAP_CONTENT_OID:
                    oid = extCertPolicyCisIcecapContentOid;
                    *oidSz = sizeof(extCertPolicyCisIcecapContentOid);
                    break;

                /* CertiPath Bridge OIDs */
                case CP_CERTIPATH_MEDIUMHW_OID:
                    oid = extCertPolicyCertipathMediumhwOid;
                    *oidSz = sizeof(extCertPolicyCertipathMediumhwOid);
                    break;
                case CP_CERTIPATH_HIGHHW_OID:
                    oid = extCertPolicyCertipathHighhwOid;
                    *oidSz = sizeof(extCertPolicyCertipathHighhwOid);
                    break;
                case CP_CERTIPATH_ICECAP_HW_OID:
                    oid = extCertPolicyCertipathIcecapHwOid;
                    *oidSz = sizeof(extCertPolicyCertipathIcecapHwOid);
                    break;
                case CP_CERTIPATH_ICECAP_CONTENT_OID:
                    oid = extCertPolicyCertipathIcecapContentOid;
                    *oidSz = sizeof(extCertPolicyCertipathIcecapContentOid);
                    break;
                case CP_CERTIPATH_VAR_MEDIUMHW_OID:
                    oid = extCertPolicyCertipathVarMediumhwOid;
                    *oidSz = sizeof(extCertPolicyCertipathVarMediumhwOid);
                    break;
                case CP_CERTIPATH_VAR_HIGHHW_OID:
                    oid = extCertPolicyCertipathVarHighhwOid;
                    *oidSz = sizeof(extCertPolicyCertipathVarHighhwOid);
                    break;
                case CP_COMODO_OID:
                    oid = extCertPolicyComodoLtdOid;
                    *oidSz = sizeof(extCertPolicyComodoLtdOid);
                    break;
                /* FPKI OIDs */
                #endif /* WOLFSSL_FPKI */
                default:
                    break;
            }
            break;

        case oidCertAltNameType:
            switch (id) {
                case HW_NAME_OID:
                    oid = extAltNamesHwNameOid;
                    *oidSz = sizeof(extAltNamesHwNameOid);
                    break;
                default:
                    break;
            }
            break;

        case oidCertKeyUseType:
            switch (id) {
                case EKU_ANY_OID:
                    oid = extExtKeyUsageAnyOid;
                    *oidSz = sizeof(extExtKeyUsageAnyOid);
                    break;
                case EKU_SERVER_AUTH_OID:
                    oid = extExtKeyUsageServerAuthOid;
                    *oidSz = sizeof(extExtKeyUsageServerAuthOid);
                    break;
                case EKU_CLIENT_AUTH_OID:
                    oid = extExtKeyUsageClientAuthOid;
                    *oidSz = sizeof(extExtKeyUsageClientAuthOid);
                    break;
                case EKU_CODESIGNING_OID:
                    oid = extExtKeyUsageCodeSigningOid;
                    *oidSz = sizeof(extExtKeyUsageCodeSigningOid);
                    break;
                case EKU_EMAILPROTECT_OID:
                    oid = extExtKeyUsageEmailProtectOid;
                    *oidSz = sizeof(extExtKeyUsageEmailProtectOid);
                    break;
                case EKU_TIMESTAMP_OID:
                    oid = extExtKeyUsageTimestampOid;
                    *oidSz = sizeof(extExtKeyUsageTimestampOid);
                    break;
                case EKU_OCSP_SIGN_OID:
                    oid = extExtKeyUsageOcspSignOid;
                    *oidSz = sizeof(extExtKeyUsageOcspSignOid);
                    break;
                #ifdef WOLFSSL_WOLFSSH
                case EKU_SSH_CLIENT_AUTH_OID:
                    oid = extExtKeyUsageSshClientAuthOid;
                    *oidSz = sizeof(extExtKeyUsageSshClientAuthOid);
                    break;
                case EKU_SSH_MSCL_OID:
                    oid = extExtKeyUsageSshMSCLOid;
                    *oidSz = sizeof(extExtKeyUsageSshMSCLOid);
                    break;
                case EKU_SSH_KP_CLIENT_AUTH_OID:
                    oid = extExtKeyUsageSshKpClientAuthOid;
                    *oidSz = sizeof(extExtKeyUsageSshKpClientAuthOid);
                    break;
                #endif /* WOLFSSL_WOLFSSH */
                default:
                    break;
            }
            break;

        case oidKdfType:
            switch (id) {
                case PBKDF2_OID:
                    oid = pbkdf2Oid;
                    *oidSz = sizeof(pbkdf2Oid);
                    break;
                default:
                    break;
            }
            break;

        case oidPBEType:
            switch (id) {
        #if !defined(NO_SHA) && !defined(NO_RC4)
                case PBE_SHA1_RC4_128_SUM:
                case PBE_SHA1_RC4_128:
                    oid = pbeSha1RC4128;
                    *oidSz = sizeof(pbeSha1RC4128);
                    break;
        #endif
        #if !defined(NO_MD5) && !defined(NO_DES3)
                case PBE_MD5_DES_SUM:
                case PBE_MD5_DES:
                    oid = pbeMd5Des;
                    *oidSz = sizeof(pbeMd5Des);
                    break;

        #endif
        #if !defined(NO_SHA) && !defined(NO_DES3)
                case PBE_SHA1_DES_SUM:
                case PBE_SHA1_DES:
                    oid = pbeSha1Des;
                    *oidSz = sizeof(pbeSha1Des);
                    break;

        #endif
        #if !defined(NO_SHA) && !defined(NO_DES3)
                case PBE_SHA1_DES3_SUM:
                case PBE_SHA1_DES3:
                    oid = pbeSha1Des3;
                    *oidSz = sizeof(pbeSha1Des3);
                    break;
        #endif
        #if !defined(NO_SHA) && defined(WC_RC2)
                case PBE_SHA1_40RC2_CBC_SUM:
                case PBE_SHA1_40RC2_CBC:
                    oid = pbe40Rc2Cbc;
                    *oidSz = sizeof(pbe40Rc2Cbc);
                    break;
        #endif
                case PBES2_SUM:
                case PBES2:
                    oid = pbes2;
                    *oidSz = sizeof(pbes2);
                    break;
                default:
                    break;
            }
            break;

        case oidKeyWrapType:
            switch (id) {
            #ifdef WOLFSSL_AES_128
                case AES128_WRAP:
                    oid = wrapAes128Oid;
                    *oidSz = sizeof(wrapAes128Oid);
                    break;
            #endif
            #ifdef WOLFSSL_AES_192
                case AES192_WRAP:
                    oid = wrapAes192Oid;
                    *oidSz = sizeof(wrapAes192Oid);
                    break;
            #endif
            #ifdef WOLFSSL_AES_256
                case AES256_WRAP:
                    oid = wrapAes256Oid;
                    *oidSz = sizeof(wrapAes256Oid);
                    break;
            #endif
            #ifdef HAVE_PKCS7
                case PWRI_KEK_WRAP:
                    oid = wrapPwriKekOid;
                    *oidSz = sizeof(wrapPwriKekOid);
                    break;
            #endif
                default:
                    break;
            }
            break;

        case oidCmsKeyAgreeType:
            switch (id) {
            #ifndef NO_SHA
                case dhSinglePass_stdDH_sha1kdf_scheme:
                    oid = dhSinglePass_stdDH_sha1kdf_Oid;
                    *oidSz = sizeof(dhSinglePass_stdDH_sha1kdf_Oid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA224
                case dhSinglePass_stdDH_sha224kdf_scheme:
                    oid = dhSinglePass_stdDH_sha224kdf_Oid;
                    *oidSz = sizeof(dhSinglePass_stdDH_sha224kdf_Oid);
                    break;
            #endif
            #ifndef NO_SHA256
                case dhSinglePass_stdDH_sha256kdf_scheme:
                    oid = dhSinglePass_stdDH_sha256kdf_Oid;
                    *oidSz = sizeof(dhSinglePass_stdDH_sha256kdf_Oid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA384
                case dhSinglePass_stdDH_sha384kdf_scheme:
                    oid = dhSinglePass_stdDH_sha384kdf_Oid;
                    *oidSz = sizeof(dhSinglePass_stdDH_sha384kdf_Oid);
                    break;
            #endif
            #ifdef WOLFSSL_SHA512
                case dhSinglePass_stdDH_sha512kdf_scheme:
                    oid = dhSinglePass_stdDH_sha512kdf_Oid;
                    *oidSz = sizeof(dhSinglePass_stdDH_sha512kdf_Oid);
                    break;
            #endif
                default:
                    break;
            }
            break;

#ifndef NO_HMAC
        case oidHmacType:
            switch (id) {
        #ifdef WOLFSSL_SHA224
                case HMAC_SHA224_OID:
                    oid = hmacSha224Oid;
                    *oidSz = sizeof(hmacSha224Oid);
                    break;
        #endif
        #ifndef NO_SHA256
                case HMAC_SHA256_OID:
                    oid = hmacSha256Oid;
                    *oidSz = sizeof(hmacSha256Oid);
                    break;
        #endif
        #ifdef WOLFSSL_SHA384
                case HMAC_SHA384_OID:
                    oid = hmacSha384Oid;
                    *oidSz = sizeof(hmacSha384Oid);
                    break;
        #endif
        #ifdef WOLFSSL_SHA512
                case HMAC_SHA512_OID:
                    oid = hmacSha512Oid;
                    *oidSz = sizeof(hmacSha512Oid);
                    break;
        #endif
                default:
                    break;
            }
            break;
#endif /* !NO_HMAC */

#ifdef HAVE_LIBZ
        case oidCompressType:
            switch (id) {
                case ZLIBc:
                    oid = zlibCompress;
                    *oidSz = sizeof(zlibCompress);
                    break;
                default:
                    break;
            }
            break;
#endif /* HAVE_LIBZ */
#ifdef WOLFSSL_APACHE_HTTPD
        case oidCertNameType:
            switch (id) {
                 case WC_NID_id_on_dnsSRV:
                    oid = dnsSRVOid;
                    *oidSz = sizeof(dnsSRVOid);
                    break;
                default:
                    break;
            }
            break;
        case oidTlsExtType:
            switch (id) {
                case TLS_FEATURE_OID:
                    oid = tlsFeatureOid;
                    *oidSz = sizeof(tlsFeatureOid);
                    break;
                default:
                    break;
            }
            break;
#endif /* WOLFSSL_APACHE_HTTPD */
#ifdef WOLFSSL_CERT_REQ
        case oidCsrAttrType:
            switch (id) {
                case GIVEN_NAME_OID:
                    oid = attrGivenName;
                    *oidSz = sizeof(attrGivenName);
                    break;
                case SURNAME_OID:
                    oid = attrSurname;
                    *oidSz = sizeof(attrSurname);
                    break;
                case INITIALS_OID:
                    oid = attrInitals;
                    *oidSz = sizeof(attrInitals);
                    break;
                case DNQUALIFIER_OID:
                    oid = attrDnQualifier;
                    *oidSz = sizeof(attrDnQualifier);
                    break;
                case UNSTRUCTURED_NAME_OID:
                    oid = attrUnstructuredNameOid;
                    *oidSz = sizeof(attrUnstructuredNameOid);
                    break;
                case PKCS9_CONTENT_TYPE_OID:
                    oid = attrPkcs9ContentTypeOid;
                    *oidSz = sizeof(attrPkcs9ContentTypeOid);
                    break;
                case CHALLENGE_PASSWORD_OID:
                    oid = attrChallengePasswordOid;
                    *oidSz = sizeof(attrChallengePasswordOid);
                    break;
                case SERIAL_NUMBER_OID:
                    oid = attrSerialNumberOid;
                    *oidSz = sizeof(attrSerialNumberOid);
                    break;
                case USER_ID_OID:
                    oid = uidOid;
                    *oidSz = sizeof(uidOid);
                    break;
                case EXTENSION_REQUEST_OID:
                    oid = attrExtensionRequestOid;
                    *oidSz = sizeof(attrExtensionRequestOid);
                    break;
                default:
                    break;
            }
            break;
#endif
#ifdef WOLFSSL_SUBJ_DIR_ATTR
        case oidSubjDirAttrType:
            switch (id) {
                case SDA_DOB_OID:
                    oid = extSubjDirAttrDobOid;
                    *oidSz = sizeof(extSubjDirAttrDobOid);
                    break;
                case SDA_POB_OID:
                    oid = extSubjDirAttrPobOid;
                    *oidSz = sizeof(extSubjDirAttrPobOid);
                    break;
                case SDA_GENDER_OID:
                    oid = extSubjDirAttrGenderOid;
                    *oidSz = sizeof(extSubjDirAttrGenderOid);
                    break;
                case SDA_COC_OID:
                    oid = extSubjDirAttrCocOid;
                    *oidSz = sizeof(extSubjDirAttrCocOid);
                    break;
                case SDA_COR_OID:
                    oid = extSubjDirAttrCorOid;
                    *oidSz = sizeof(extSubjDirAttrCorOid);
                    break;
                default:
                    break;
            }
            break;
#endif /* WOLFSSL_SUBJ_DIR_ATTR */
        case oidIgnoreType:
        default:
            break;
    }

    return oid;
}

#ifdef HAVE_ECC

/* Check the OID id is for a known elliptic curve.
 *
 * @param [in]  oid  OID id.
 * @return  ECC set id on success.
 * @return  ECC_CURVE_OID_E when OID id is 0 or not supported.
 */
static int CheckCurve(word32 oid)
{
    int ret;
    word32 oidSz;

    /* Lookup OID id. */
    ret = wc_ecc_get_oid(oid, NULL, &oidSz);
    /* Check for error or zero length OID size (can't get OID for encoding). */
    if ((ret < 0) || (oidSz == 0)) {
        WOLFSSL_MSG("CheckCurve not found");
        WOLFSSL_ERROR_VERBOSE(ECC_CURVE_OID_E);
        ret = ECC_CURVE_OID_E;
    }

    /* Return ECC set id or error code. */
    return ret;
}

#endif

#ifdef HAVE_OID_ENCODING
/* Encode dotted form of OID into byte array version.
 *
 * @param [in]      in     Dotted form of OID.
 * @param [in]      inSz   Count of numbers in dotted form.
 * @param [in]      out    Buffer to hold OID.
 * @param [in, out] outSz  On in, size of buffer.
 *                         On out, number of bytes in buffer.
 * @return  0 on success
 * @return  BAD_FUNC_ARG when in or outSz is NULL.
 * @return  BUFFER_E when buffer too small.
 */
int wc_EncodeObjectId(const word16* in, word32 inSz, byte* out, word32* outSz)
{
    return EncodeObjectId(in, inSz, out, outSz);
}

int EncodeObjectId(const word16* in, word32 inSz, byte* out, word32* outSz)
{
    int i, x, len;
    word32 d, t;

    /* check args */
    if (in == NULL || outSz == NULL || inSz <= 0) {
        return BAD_FUNC_ARG;
    }

    /* compute length of encoded OID */
    d = ((word32)in[0] * 40) + in[1];
    len = 0;
    for (i = 1; i < (int)inSz; i++) {
        x = 0;
        t = d;
        while (t) {
            x++;
            t >>= 1;
        }
        len += (x / 7) + ((x % 7) ? 1 : 0) + (d == 0 ? 1 : 0);

        if (i < (int)inSz - 1) {
            d = in[i + 1];
        }
    }

    if (out) {
        /* verify length */
        if ((int)*outSz < len) {
            return BUFFER_E; /* buffer provided is not large enough */
        }

        /* calc first byte */
        d = ((word32)in[0] * 40) + in[1];

        /* encode bytes */
        x = 0;
        for (i = 1; i < (int)inSz; i++) {
            if (d) {
                int y = x, z;
                byte mask = 0;
                while (d) {
                    out[x++] = (byte)((d & 0x7F) | mask);
                    d     >>= 7;
                    mask  |= 0x80;  /* upper bit is set on all but the last byte */
                }
                /* now swap bytes y...x-1 */
                z = x - 1;
                while (y < z) {
                    mask = out[y];
                    out[y] = out[z];
                    out[z] = mask;
                    ++y;
                    --z;
                }
            }
            else {
              out[x++] = 0x00; /* zero value */
            }

            /* next word */
            if (i < (int)inSz - 1) {
                d = in[i + 1];
            }
        }
    }

    /* return length */
    *outSz = (word32)len;

    return 0;
}
#endif /* HAVE_OID_ENCODING */

#if defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT)
/* Encode dotted form of OID into byte array version.
 *
 * @param [in]      in     Byte array containing OID.
 * @param [in]      inSz   Size of OID in bytes.
 * @param [in]      out    Array to hold dotted form of OID.
 * @param [in, out] outSz  On in, number of elements in array.
 *                         On out, count of numbers in dotted form.
 * @return  0 on success
 * @return  BAD_FUNC_ARG when in or outSz is NULL.
 * @return  BUFFER_E when dotted form buffer too small.
 */
int DecodeObjectId(const byte* in, word32 inSz, word16* out, word32* outSz)
{
    int x = 0, y = 0;
    word32 t = 0;
    int cnt = 0;

    /* check args */
    if (in == NULL || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    /* decode bytes */
    while (inSz--) {
        if (cnt == 4)
            return ASN_OBJECT_ID_E;
        t = (t << 7) | (in[x] & 0x7F);
        cnt++;
        if (!(in[x] & 0x80)) {
            if (y == 0) {
                if ((int)*outSz < 2) {
                    return BUFFER_E;
                }
                out[0] = (word16)(t / 40);
                out[1] = (word16)(t % 40);
                y = 2;
            }
            else {
                if (y >= (int)*outSz) {
                    return BUFFER_E;
                }
                out[y++] = (word16)t;
            }
            t = 0; /* reset tmp */
            cnt = 0;
        }
        x++;
    }

    /* return length */
    *outSz = (word32)y;

    return 0;
}
#endif /* HAVE_OID_DECODING || WOLFSSL_ASN_PRINT */

/* Decode the header of a BER/DER encoded OBJECT ID.
 *
 * @param [in]      input     Buffer holding DER/BER encoded data.
 * @param [in, out] inOutIdx  On in, starting index of header.
 *                            On out, end of parsed header.
 * @param [out]     len       Number of bytes in the ASN.1 data.
 * @param [in]      maxIdx    Length of data in buffer.
 * @return  0 on success.
 * @return  BUFFER_E when there is not enough data to parse.
 * @return  ASN_PARSE_E when the tag is not a OBJECT ID or length is invalid.
 */
int GetASNObjectId(const byte* input, word32* inOutIdx, int* len, word32 maxIdx)
{
    int ret = GetASNHeader(input, ASN_OBJECT_ID, inOutIdx, len, maxIdx);
    if (ret > 0) {
        /* Only return 0 on success. */
        ret = 0;
    }
    return ret;
}

/* Set the DER/BER encoding of the ASN.1 OBJECT ID header.
 *
 * When output is NULL, calculate the header length only.
 *
 * @param [in]  len        Length of OBJECT ID data in bytes.
 * @param [out] output     Buffer to write into.
 * @return  Number of bytes added to the buffer.
 */
int SetObjectId(int len, byte* output)
{
    int idx = 0;

    if (output) {
        /* Write out tag. */
        output[idx] = ASN_OBJECT_ID;
    }
    /* Skip tag. */
    idx += ASN_TAG_SZ;
    /* Encode length - passing NULL for output will not encode. */
    idx += (int)SetLength((word32)len, output ? output + idx : NULL);

    /* Return index after header. */
    return idx;
}

#ifdef ASN_DUMP_OID
/* Dump the OID information.
 *
 * Decode the OID too if function available.
 *
 * @param [in] oidData  OID data from buffer.
 * @param [in] oidSz    Size of OID data in buffer.
 * @param [in] oid      OID id.
 * @param [in] oidType  Type of OID.
 * @return  0 on success.
 * @return  BUFFER_E when not enough bytes for proper decode.
 *          (HAVE_OID_DECODING)
 */
static int DumpOID(const byte* oidData, word32 oidSz, word32 oid,
                   word32 oidType)
{
    int    ret = 0;
    word32 i;

    /* support for dumping OID information */
    printf("OID (Type %d, Sz %d, Sum %d): ", oidType, oidSz, oid);
    /* Dump bytes in decimal. */
    for (i = 0; i < oidSz; i++) {
        printf("%d, ", oidData[i]);
    }
    printf("\n");
    /* Dump bytes in hexadecimal. */
    for (i = 0; i < oidSz; i++) {
        printf("%02x, ", oidData[i]);
    }
    printf("\n");

    #ifdef HAVE_OID_DECODING
    {
        word16 decOid[MAX_OID_SZ];
        word32 decOidSz = MAX_OID_SZ;
        /* Decode the OID into dotted form. */
        ret = DecodeObjectId(oidData, oidSz, decOid, &decOidSz);
        if (ret == 0) {
            printf("  Decoded (Sz %d): ", decOidSz);
            for (i=0; i<decOidSz; i++) {
                printf("%d.", decOid[i]);
            }
            printf("\n");
        }
        else {
            printf("DecodeObjectId failed: %d\n", ret);
        }
    }
    #endif /* HAVE_OID_DECODING */

    return ret;
}
#endif /* ASN_DUMP_OID */

#ifdef WOLFSSL_OLD_OID_SUM
#ifdef WOLFSSL_FPKI
/* Handles the large number of collisions from FPKI certificate policy
 * OID sums.  Returns a special value (100000 + actual sum) if a
 * collision is detected.
 * @param [in]      oid      Buffer holding OID.
 * @param [in]      oidSz    Length of OID data in buffer.
 * @param [in]      oidSum   The sum of the OID being passed in.
 */
static word32 fpkiCertPolOid(const byte* oid, word32 oidSz, word32 oidSum) {

    switch (oidSum) {
        case CP_ADO_MEDIUM_OID:
            if ((word32)sizeof(extCertPolicyComodoLtdOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyComodoLtdOid,
            sizeof(extCertPolicyComodoLtdOid)) == 0)
                return CP_COMODO_OID;
            break;
        case CP_FPKI_HIGH_ASSURANCE_OID:
            if ((word32)sizeof(extCertPolicyStateBasicOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyStateBasicOid,
            sizeof(extCertPolicyStateBasicOid)) == 0)
                return CP_STATE_BASIC_OID;
            break;
        case CP_FPKI_COMMON_DEVICES_HARDWARE_OID:
            if ((word32)sizeof(extCertPolicyDodPeerInteropOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyDodPeerInteropOid,
            sizeof(extCertPolicyDodPeerInteropOid)) == 0)
                return CP_DOD_PEER_INTEROP_OID;
            break;
        case CP_FPKI_PIV_AUTH_HW_OID:
            if ((word32)sizeof(extCertPolicyDodMediumNpe112Oid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyDodMediumNpe112Oid,
            sizeof(extCertPolicyDodMediumNpe112Oid)) == 0)
                return CP_DOD_MEDIUM_NPE_112_OID;
            else if ((word32)sizeof(extCertPolicyStateMediumDeviceHardwareOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyStateMediumDeviceHardwareOid,
            sizeof(extCertPolicyStateMediumDeviceHardwareOid)) == 0)
                return CP_STATE_MEDDEVHW_OID;
            break;
        case CP_FPKI_PIVI_AUTH_OID:
            if ((word32)sizeof(extCertPolicyDodMedium128Oid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyDodMedium128Oid,
            sizeof(extCertPolicyDodMedium128Oid)) == 0)
                return CP_DOD_MEDIUM_128_OID;
            break;
        case CP_FPKI_COMMON_PIVI_CONTENT_SIGNING_OID:
            if ((word32)sizeof(extCertPolicyDodMediumHardware112Oid) == (word32)oidSz &&
                XMEMCMP(oid, extCertPolicyDodMediumHardware112Oid,
                sizeof(extCertPolicyDodMediumHardware112Oid)) == 0)
                    return CP_DOD_MEDIUM_HARDWARE_112_OID;
            else if ((word32)sizeof(extCertPolicyCertipathHighhwOid) == (word32)oidSz &&
                XMEMCMP(oid, extCertPolicyCertipathHighhwOid,
                sizeof(extCertPolicyCertipathHighhwOid)) == 0)
                    return CP_CERTIPATH_HIGHHW_OID;
            break;
        case CP_DOD_MEDIUM_OID:
            if ((word32)sizeof(extCertPolicyEcaMediumOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaMediumOid,
            sizeof(extCertPolicyEcaMediumOid)) == 0)
                return CP_ECA_MEDIUM_OID;
            break;
        case CP_FPKI_COMMON_AUTH_OID:
            if ((word32)sizeof(extCertPolicyEcaMediumSha256Oid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaMediumSha256Oid,
            sizeof(extCertPolicyEcaMediumSha256Oid)) == 0)
                return CP_ECA_MEDIUM_SHA256_OID;
            break;
        case CP_FPKI_MEDIUM_HARDWARE_OID:
            if ((word32)sizeof(extCertPolicyEcaMediumTokenOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaMediumTokenOid,
            sizeof(extCertPolicyEcaMediumTokenOid)) == 0)
                return CP_ECA_MEDIUM_TOKEN_OID;
            else if ((word32)sizeof(extCertPolicyTreasuryPiviHardwareOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyTreasuryPiviHardwareOid,
            sizeof(extCertPolicyTreasuryPiviHardwareOid)) == 0)
                return CP_TREAS_PIVI_HW_OID;
            break;
        case CP_DOD_MEDIUM_HARDWARE_OID:
            if ((word32)sizeof(extCertPolicyEcaMediumTokenSha256Oid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaMediumTokenSha256Oid,
            sizeof(extCertPolicyEcaMediumTokenSha256Oid)) == 0)
                return CP_ECA_MEDIUM_TOKEN_SHA256_OID;
            else if ((word32)sizeof(extCertPolicyTreasuryPiviContentSigningOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyTreasuryPiviContentSigningOid,
            sizeof(extCertPolicyTreasuryPiviContentSigningOid)) == 0)
                return CP_TREAS_PIVI_CONTENT_OID;
            break;
        case CP_DOD_PIV_AUTH_OID:
            if ((word32)sizeof(extCertPolicyEcaMediumHardwarePiviOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaMediumHardwarePiviOid,
            sizeof(extCertPolicyEcaMediumHardwarePiviOid)) == 0)
                return CP_ECA_MEDIUM_HARDWARE_PIVI_OID;
            else if ((word32)sizeof(extCertPolicyStateMedHwOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyStateMedHwOid,
            sizeof(extCertPolicyStateMedHwOid)) == 0)
                return CP_STATE_MEDHW_OID;
            break;
        case CP_FPKI_COMMON_HARDWARE_OID:
            if ((word32)sizeof(extCertPolicyStateHighOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyStateHighOid,
            sizeof(extCertPolicyStateHighOid)) == 0)
                return CP_STATE_HIGH_OID;
            else if ((word32)sizeof(extCertPolicyTreasuryHighOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyTreasuryHighOid,
            sizeof(extCertPolicyTreasuryHighOid)) == 0)
                return CP_TREAS_HIGH_OID;
            break;
        case CP_ECA_MEDIUM_HARDWARE_OID:
            if ((word32)sizeof(extCertPolicyExostarMediumHardwareSha2Oid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyExostarMediumHardwareSha2Oid,
            sizeof(extCertPolicyExostarMediumHardwareSha2Oid)) == 0)
                return CP_EXOSTAR_MEDIUMHW_SHA2_OID;
            break;
        case CP_ADO_HIGH_OID:
            if ((word32)sizeof(extCertPolicyAdoResourceMediumAssuranceOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyAdoResourceMediumAssuranceOid,
            sizeof(extCertPolicyAdoResourceMediumAssuranceOid)) == 0)
                return CP_ADO_RESOURCE_MEDIUM_OID;
            break;
        case CP_DOD_ADMIN_OID:
            if ((word32)sizeof(extCertPolicyCarillonAivcontentOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyCarillonAivcontentOid,
            sizeof(extCertPolicyCarillonAivcontentOid)) == 0)
                return CP_CARILLON_AIVCONTENT_OID;
            break;
        case CP_TREAS_MEDIUMHW_OID:
            if ((word32)sizeof(extCertPolicyStateModerateOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyStateModerateOid,
            sizeof(extCertPolicyStateModerateOid)) == 0)
                return CP_STATE_MODERATE_OID;
            break;
        case CP_CIS_ICECAP_HW_OID:
            if ((word32)sizeof(extCertPolicyNlModIrrefutabilityOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyNlModIrrefutabilityOid,
            sizeof(extCertPolicyNlModIrrefutabilityOid)) == 0)
                return CP_NL_MOD_IRREFUT_OID;
            break;
        case CP_DOD_MEDIUM_192_OID:
            if ((word32)sizeof(extCertPolicyCertipathMediumhwOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyCertipathMediumhwOid,
            sizeof(extCertPolicyCertipathMediumhwOid)) == 0)
                return CP_CERTIPATH_MEDIUMHW_OID;
            break;
        case CP_CARILLON_AIVHW_OID:
            if ((word32)sizeof(extCertPolicyCertipathVarMediumhwOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyCertipathVarMediumhwOid,
            sizeof(extCertPolicyCertipathVarMediumhwOid)) == 0)
                return CP_CERTIPATH_VAR_MEDIUMHW_OID;
            break;
        case CP_ISRG_DOMAIN_VALID:
            if ((word32)sizeof(extCertPolicyEcaContentSigningPiviOid) == (word32)oidSz &&
            XMEMCMP(oid, extCertPolicyEcaContentSigningPiviOid,
            sizeof(extCertPolicyEcaContentSigningPiviOid)) == 0)
                return CP_ECA_CONTENT_SIGNING_PIVI_OID;
            break;
        default:
            break;
    }

    return 0;
}
#endif
#endif /* WOLFSSL_OLD_OID_SUM */

word32 wc_oid_sum(const byte* input, int length)
{
    int i;
    word32 oid = 0;
#ifndef WOLFSSL_OLD_OID_SUM
    int shift = 0;
#endif

    /* Check for valid input. */
    if (input == NULL || length > MAX_OID_SZ) {
        WOLFSSL_MSG("wc_oid_sum: invalid args");
        return 0;
    }

    /* Sum it up for now. */
    for (i = 0; i < length; i++) {
    #ifdef WOLFSSL_OLD_OID_SUM
        oid += (word32)input[i];
    #else
        oid ^= ((word32)(~input[i])) << shift;
        shift = (shift + 8) & 0x1f;
    #endif
    }
#ifndef WOLFSSL_OLD_OID_SUM
    oid &= 0x7fffffff;
#endif

    return oid;
}

/* Get the OID data and verify it is of the type specified when compiled in.
 *
 * @param [in]      input     Buffer holding OID.
 * @param [in, out] inOutIdx  On in, starting index of OID.
 *                            On out, end of parsed OID.
 * @param [out]     oid       OID id.
 * @param [in]      oidType   Expected type of OID. Define NO_VERIFY_OID to
 *                            not compile in check.
 * @param [in]      length    Length of OID data in buffer.
 * @return  0 on success.
 * @return  ASN_UNKNOWN_OID_E when OID is not recognized.
 * @return  BUFFER_E when not enough bytes for proper decode. (ASN_DUMP_OID and
 *          HAVE_OID_DECODING)
 */
static int GetOID(const byte* input, word32* inOutIdx, word32* oid,
                  word32 oidType, int length)
{
    int    ret = 0;
    word32 idx = *inOutIdx;
    word32 actualOidSz;
    const byte* actualOid;
#ifndef NO_VERIFY_OID
    const byte* checkOid = NULL;
    word32 checkOidSz;
#endif /* NO_VERIFY_OID */
#ifdef WOLFSSL_OLD_OID_SUM
#if defined(WOLFSSL_FPKI)
    word32 found_collision = 0;
#endif
#endif
    (void)oidType;
    *oid = 0;

    /* Keep references to OID data and length for sum and (optional) check. */
    actualOid = &input[idx];
    actualOidSz = (word32)length;

    *oid = wc_oid_sum(actualOid, (int)actualOidSz);
    idx += actualOidSz;

#ifdef WOLFSSL_OLD_OID_SUM
#ifdef WOLFSSL_FPKI
    /* Due to the large number of OIDs for FPKI certificate policy, there
       are multiple collsisions.  Handle them in a dedicated function,
       if a collision is detected, the OID is adjusted. */
    if (oidType == oidCertPolicyType) {
        found_collision = fpkiCertPolOid(actualOid, actualOidSz, *oid);
    }
#endif

#if defined(WOLFSSL_FPKI)
    if (found_collision) {
        *oid = found_collision;
    }
#endif /* WOLFSSL_FPKI */
#endif

    /* Return the index after the OID data. */
    *inOutIdx = idx;

#ifndef NO_VERIFY_OID
    /* 'Ignore' type means we don't care which OID it is. */
    if (oidType != oidIgnoreType) {
        /* Get the OID data for the id-type. */
        checkOid = OidFromId(*oid, oidType, &checkOidSz);

#ifdef WOLFSSL_OLD_OID_SUM
    #if defined(WOLFSSL_FPKI)
        /* Handle OID sum collision of
            AES256CBCb (454) 2.16.840.1.101.3.4.1.42
            CP_FPKI_PIV_AUTH_HW_OID (454) 2.16.840.1.101.3.2.1.3.41
        */
        #if defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_256)
        if ((actualOidSz == (word32)sizeof(blkAes256CbcOid)) &&
                (XMEMCMP(actualOid, blkAes256CbcOid,
                 sizeof(blkAes256CbcOid)) == 0)) {

            checkOid   = blkAes256CbcOid;
            checkOidSz = sizeof(blkAes256CbcOid);
        }
        #endif /* HAVE_AES_CBC */
    #endif /* WOLFSSL_FPKI */
#endif

    #ifdef ASN_DUMP_OID
        /* Dump out the data for debug. */
        ret = DumpOID(actualOid, actualOidSz, *oid, oidType);
    #endif

        /* TODO: Want to fail when checkOid is NULL.
         * Can't as too many situations where unknown OID is to be
         * supported. Extra parameter for must not be NULL?
         */
        /* Check that the OID data matches what we found for the OID id. */
        if ((ret == 0) && (checkOid != NULL) && ((checkOidSz != actualOidSz) ||
                (XMEMCMP(actualOid, checkOid, checkOidSz) != 0))) {
            WOLFSSL_MSG("OID Check Failed");
            WOLFSSL_ERROR_VERBOSE(ASN_UNKNOWN_OID_E);
            ret = ASN_UNKNOWN_OID_E;
        }
    }
#endif /* NO_VERIFY_OID */

    return ret;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for an OBJECT_ID. */
static const ASNItem objectIdASN[] = {
/* OID */ { 0, ASN_OBJECT_ID, 0, 0, 0 }
};
enum {
    OBJECTIDASN_IDX_OID = 0
};

/* Number of items in ASN.1 template for an OBJECT_ID. */
#define objectIdASN_Length (sizeof(objectIdASN) / sizeof(ASNItem))
#endif

/* Get the OID id/sum from the BER encoded OBJECT_ID.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of OBJECT_ID.
 *                            On out, start of ASN.1 item after OBJECT_ID.
 * @param [out]     oid       Id of OID in OBJECT_ID data.
 * @param [in]      oidType   Type of OID to expect.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int GetObjectId(const byte* input, word32* inOutIdx, word32* oid,
                                  word32 oidType, word32 maxIdx)
{
    ASNGetData dataASN[objectIdASN_Length];
    int ret;

    WOLFSSL_ENTER("GetObjectId");

    /* Clear dynamic data and set OID type expected. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    GetASN_OID(&dataASN[OBJECTIDASN_IDX_OID], oidType);
    /* Decode OBJECT_ID. */
    ret = GetASN_Items(objectIdASN, dataASN, objectIdASN_Length, 0, input,
                       inOutIdx, maxIdx);
    if (ret == 0) {
        /* Return the id/sum. */
        *oid = dataASN[OBJECTIDASN_IDX_OID].data.oid.sum;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for an algorithm identifier. */
static const ASNItem algoIdASN[] = {
/*  SEQ  */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/*  OID  */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/*  NULL */        { 1, ASN_TAG_NULL, 0, 0, 1 },
};
enum {
    ALGOIDASN_IDX_SEQ = 0,
    ALGOIDASN_IDX_OID,
    ALGOIDASN_IDX_NULL
};

/* Number of items in ASN.1 template for an algorithm identifier. */
#define algoIdASN_Length (sizeof(algoIdASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
static int GetAlgoIdImpl(const byte* input, word32* inOutIdx, word32* oid,
                     word32 oidType, word32 maxIdx, byte *absentParams)
{
    DECL_ASNGETDATA(dataASN, algoIdASN_Length);
    int ret = 0;

    WOLFSSL_ENTER("GetAlgoId");

    CALLOC_ASNGETDATA(dataASN, algoIdASN_Length, ret, NULL);
    if (ret == 0) {
        /* Set OID type expected. */
        GetASN_OID(&dataASN[ALGOIDASN_IDX_OID], oidType);
        /* Decode the algorithm identifier. */
        ret = GetASN_Items(algoIdASN, dataASN, algoIdASN_Length, 0, input,
            inOutIdx, maxIdx);
    }
    if (ret == 0) {
        /* Return the OID id/sum. */
        *oid = dataASN[ALGOIDASN_IDX_OID].data.oid.sum;

        if ((absentParams != NULL) &&
            (dataASN[ALGOIDASN_IDX_NULL].tag == ASN_TAG_NULL)) {
            *absentParams = FALSE;
        }
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Get the OID id/sum from the BER encoding of an algorithm identifier.
 *
 * NULL tag is skipped if present.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of algorithm identifier.
 *                            On out, start of ASN.1 item after algorithm id.
 * @param [out]     oid       Id of OID in algorithm identifier data.
 * @param [in]      oidType   Type of OID to expect.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when encoding is invalid.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
int GetAlgoId(const byte* input, word32* inOutIdx, word32* oid,
                     word32 oidType, word32 maxIdx)
{
    return GetAlgoIdImpl(input, inOutIdx, oid, oidType, maxIdx, NULL);
}

int GetAlgoIdEx(const byte* input, word32* inOutIdx, word32* oid,
                     word32 oidType, word32 maxIdx, byte *absentParams)
{
    /* Assume absent until proven otherwise */
    if (absentParams != NULL) {
        *absentParams = TRUE;
    }

    return GetAlgoIdImpl(input, inOutIdx, oid, oidType, maxIdx, absentParams);
}

#ifndef NO_RSA

#ifdef WC_RSA_PSS
/* RFC 8017 - PKCS #1 has RSA PSS parameter ASN definition. */

/* Convert a hash OID to a hash type.
 *
 * @param  [in]   oid   Hash OID.
 * @param  [out]  type  Hash type.
 * @return  0 on success.
 * @return  ASN_PARSE_E when hash OID not supported for RSA PSS.
 */
static int RsaPssHashOidToType(word32 oid, enum wc_HashType* type)
{
    int ret = 0;

    switch (oid) {
    /* SHA-1 is missing as it is the default is not allowed to appear. */
#ifdef WOLFSSL_SHA224
    case SHA224h:
        *type = WC_HASH_TYPE_SHA224;
        break;
#endif
#ifndef NO_SHA256
    case SHA256h:
        *type = WC_HASH_TYPE_SHA256;
        break;
#endif
#ifdef WOLFSSL_SHA384
    case SHA384h:
        *type = WC_HASH_TYPE_SHA384;
        break;
#endif
#ifdef WOLFSSL_SHA512
    case SHA512h:
        *type = WC_HASH_TYPE_SHA512;
        break;
    /* TODO: SHA512_224h */
    /* TODO: SHA512_256h */
#endif
    default:
        ret = ASN_PARSE_E;
        break;
    }

    return ret;
}

/* Convert a hash OID to a MGF1 type.
 *
 * @param  [in]   oid   Hash OID.
 * @param  [out]  mgf   MGF type.
 * @return  0 on success.
 * @return  ASN_PARSE_E when hash OID not supported for RSA PSS.
 */
static int RsaPssHashOidToMgf1(word32 oid, int* mgf)
{
    int ret = 0;

    switch (oid) {
    /* SHA-1 is missing as it is the default is not allowed to appear. */
#ifdef WOLFSSL_SHA224
    case SHA224h:
        *mgf = WC_MGF1SHA224;
        break;
#endif
#ifndef NO_SHA256
    case SHA256h:
        *mgf = WC_MGF1SHA256;
        break;
#endif
#ifdef WOLFSSL_SHA384
    case SHA384h:
        *mgf = WC_MGF1SHA384;
        break;
#endif
#ifdef WOLFSSL_SHA512
    case SHA512h:
        *mgf = WC_MGF1SHA512;
        break;
    /* TODO: SHA512_224h */
    /* TODO: SHA512_256h */
#endif
    default:
        ret = ASN_PARSE_E;
        break;
    }

    return ret;
}

#if !defined(NO_CERTS) && !defined(NO_ASN_CRYPT)

/* Convert a hash OID to a fake signature OID.
 *
 * @param  [in]   oid     Hash OID.
 * @param  [out]  sigOid  Signature OID to pass wto HashForSignature().
 * @return  0 on success.
 * @return  ASN_PARSE_E when hash OID not supported for RSA PSS.
 */
static int RsaPssHashOidToSigOid(word32 oid, word32* sigOid)
{
    int ret = 0;

    switch (oid) {
#ifndef NO_SHA
    case WC_HASH_TYPE_SHA:
        *sigOid = CTC_SHAwRSA;
        break;
#endif
#ifdef WOLFSSL_SHA224
    case WC_HASH_TYPE_SHA224:
        *sigOid = CTC_SHA224wRSA;
        break;
#endif
#ifndef NO_SHA256
    case WC_HASH_TYPE_SHA256:
        *sigOid = CTC_SHA256wRSA;
        break;
#endif
#ifdef WOLFSSL_SHA384
    case WC_HASH_TYPE_SHA384:
        *sigOid = CTC_SHA384wRSA;
        break;
#endif
#ifdef WOLFSSL_SHA512
    case WC_HASH_TYPE_SHA512:
        *sigOid = CTC_SHA512wRSA;
        break;
#endif
    /* TODO: SHA512_224h */
    /* TODO: SHA512_256h */
    /* Not supported by HashForSignature() */
    default:
        ret = ASN_PARSE_E;
        break;
    }

    return ret;
}
#endif

/* RSASSA-PSS-params context-specific tags.
 * RFC 4055, 3.1
 */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN tag for hashAlgorithm. */
#define ASN_TAG_RSA_PSS_HASH        (ASN_CONTEXT_SPECIFIC | 0)
/* ASN tag for maskGenAlgorithm. */
#define ASN_TAG_RSA_PSS_MGF         (ASN_CONTEXT_SPECIFIC | 1)
/* ASN tag for saltLength. */
#define ASN_TAG_RSA_PSS_SALTLEN     (ASN_CONTEXT_SPECIFIC | 2)
/* ASN tag for trailerField. */
#define ASN_TAG_RSA_PSS_TRAILER     (ASN_CONTEXT_SPECIFIC | 3)

/* ASN.1 template for RSA PSS parameters. */
static const ASNItem rsaPssParamsASN[] = {
/*  SEQ         */  { 0, ASN_SEQUENCE, 1, 1, 0 },
/*  HASH        */      { 1, ASN_TAG_RSA_PSS_HASH, 1, 1, 1 },
/*  HASHSEQ     */          { 2, ASN_SEQUENCE, 1, 1, 0 },
/*  HASHOID     */              { 3, ASN_OBJECT_ID, 0, 0, 0 },
/*  HASHNULL    */              { 3, ASN_TAG_NULL, 0, 0, 1 },
/*  MGF         */      { 1, ASN_TAG_RSA_PSS_MGF, 1, 1, 1 },
/*  MGFSEQ      */          { 2, ASN_SEQUENCE, 1, 1, 0 },
/*  MGFOID      */              { 3, ASN_OBJECT_ID, 0, 0, 0 },
/*  MGFPARAM    */              { 3, ASN_SEQUENCE, 1, 1, 0 },
/*  MGFHOID     */                  { 4, ASN_OBJECT_ID, 0, 0, 0 },
/*  MGFHNULL    */                  { 4, ASN_TAG_NULL, 0, 0, 1 },
/*  SALTLEN     */      { 1, ASN_TAG_RSA_PSS_SALTLEN, 1, 1, 1 },
/*  SALTLENINT  */          { 2, ASN_INTEGER, 0, 0, 0 },
/*  TRAILER     */      { 1, ASN_TAG_RSA_PSS_TRAILER, 1, 1, 1 },
/*  TRAILERINT  */          { 2, ASN_INTEGER, 0, 0, 0 },
};
enum {
    RSAPSSPARAMSASN_IDX_SEQ = 0,
    RSAPSSPARAMSASN_IDX_HASH,
    RSAPSSPARAMSASN_IDX_HASHSEQ,
    RSAPSSPARAMSASN_IDX_HASHOID,
    RSAPSSPARAMSASN_IDX_HASHNULL,
    RSAPSSPARAMSASN_IDX_MGF,
    RSAPSSPARAMSASN_IDX_MGFSEQ,
    RSAPSSPARAMSASN_IDX_MGFOID,
    RSAPSSPARAMSASN_IDX_MGFPARAM,
    RSAPSSPARAMSASN_IDX_MGFHOID,
    RSAPSSPARAMSASN_IDX_MGFHNULL,
    RSAPSSPARAMSASN_IDX_SALTLEN,
    RSAPSSPARAMSASN_IDX_SALTLENINT,
    RSAPSSPARAMSASN_IDX_TRAILER,
    RSAPSSPARAMSASN_IDX_TRAILERINT
};

/* Number of items in ASN.1 template for RSA PSS parameters. */
#define rsaPssParamsASN_Length (sizeof(rsaPssParamsASN) / sizeof(ASNItem))
#else
/* ASN tag for hashAlgorithm. */
#define ASN_TAG_RSA_PSS_HASH        (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)
/* ASN tag for maskGenAlgorithm. */
#define ASN_TAG_RSA_PSS_MGF         (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1)
/* ASN tag for saltLength. */
#define ASN_TAG_RSA_PSS_SALTLEN     (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 2)
/* ASN tag for trailerField. */
#define ASN_TAG_RSA_PSS_TRAILER     (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 3)
#endif

/* Decode the RSA PSS parameters.
 *
 * @param  [in]   params   Buffer holding BER encoded RSA PSS parameters.
 * @param  [in]   sz       Size of data in buffer in bytes.
 * @param  [out]  hash     Hash algorithm to use on message.
 * @param  [out]  mgf      MGF algorithm to use with PSS padding.
 * @param  [out]  saltLen  Length of salt in PSS padding.
 * @return  BAD_FUNC_ARG when the params is NULL.
 * @return  ASN_PARSE_E when the decoding fails.
 * @return  0 on success.
 */
static int DecodeRsaPssParams(const byte* params, word32 sz,
    enum wc_HashType* hash, int* mgf, int* saltLen)
{
    if (params == NULL)
        return BAD_FUNC_ARG;

    /* Empty or NULL-tag parameters mean all defaults per RFC 4055 */
    if (sz == 0) {
        *hash = WC_HASH_TYPE_SHA;
        *mgf = WC_MGF1SHA1;
        *saltLen = 20;
        return 0;
    }
    if (params[0] == ASN_TAG_NULL) {
        if (sz >= 2 && params[1] == 0) {
            *hash = WC_HASH_TYPE_SHA;
            *mgf = WC_MGF1SHA1;
            *saltLen = 20;
            return 0;
        }
        return ASN_PARSE_E;
    }
    if (params[0] != (ASN_SEQUENCE | ASN_CONSTRUCTED))
        return ASN_PARSE_E;

#ifdef WOLFSSL_ASN_TEMPLATE
{
    DECL_ASNGETDATA(dataASN, rsaPssParamsASN_Length);
    int ret = 0;
#ifndef WOLFSSL_NO_ASN_STRICT
    word16 trailerVal = 1;
#endif
    word16 sLen = 20;

    /* Default values. */
    *hash = WC_HASH_TYPE_SHA;
    *mgf = WC_MGF1SHA1;

    CALLOC_ASNGETDATA(dataASN, rsaPssParamsASN_Length, ret, NULL);
    if (ret == 0) {
        word32 inOutIdx = 0;
        /* Set OID type expected. */
        GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_HASHOID], oidHashType);
        GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFOID], oidIgnoreType);
        GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFHOID], oidHashType);
        /* Place the salt length into 16-bit var sLen. */
        GetASN_Int16Bit(&dataASN[RSAPSSPARAMSASN_IDX_SALTLENINT], &sLen);
#ifndef WOLFSSL_NO_ASN_STRICT
        /* Capture trailerField value for RFC 8017 A.2.3 validation. */
        GetASN_Int16Bit(&dataASN[RSAPSSPARAMSASN_IDX_TRAILERINT], &trailerVal);
#endif
        /* Decode the algorithm identifier. */
        ret = GetASN_Items(rsaPssParamsASN, dataASN, rsaPssParamsASN_Length, 1,
            params, &inOutIdx, sz);
    }
    if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_HASHOID].tag != 0)) {
        word32 oid = dataASN[RSAPSSPARAMSASN_IDX_HASHOID].data.oid.sum;
        ret = RsaPssHashOidToType(oid, hash);
    }
    if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].tag != 0)) {
        if (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].data.oid.sum != MGF1_OID)
            ret = ASN_PARSE_E;
    }
    if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].tag != 0)) {
        word32 oid = dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].data.oid.sum;
        ret = RsaPssHashOidToMgf1(oid, mgf);
    }
#ifndef WOLFSSL_NO_ASN_STRICT
    /* RFC 8017 A.2.3: trailerField SHALL be trailerFieldBC(1). */
    if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_TRAILERINT].tag != 0)) {
        if (trailerVal != 1) {
            WOLFSSL_MSG("DecodeRsaPssParams: trailerField must be 1");
            ret = ASN_PARSE_E;
        }
    }
#endif
    if (ret == 0) {
        *saltLen = sLen;
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#else /* !WOLFSSL_ASN_TEMPLATE */
{
    int ret = 0;
    word32 idx = 0;
    int len = 0;
    word32 oid = 0;
    byte tag;
    int length;

    /* Decode RSASSA-PSS-params SEQUENCE content. */
    if (GetSequence_ex(params, &idx, &len, sz, 1) < 0) {
        WOLFSSL_MSG("DecodeRsaPssParams: fail at sequence");
        return ASN_PARSE_E;
    }

    /* [0] hashAlgorithm */
    if (ret == 0) {
        if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_HASH)) {
            /* Hash algorithm to use on message. */
            if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
                WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_header");
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                if (GetAlgoId(params, &idx, &oid, oidHashType, sz) < 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_algo");
                    ret = ASN_PARSE_E;
                }
            }
            if (ret == 0) {
                ret = RsaPssHashOidToType(oid, hash);
                if (ret != 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_oid");
                }
            }
        }
        else {
            /* Default hash algorithm. */
            *hash = WC_HASH_TYPE_SHA;
        }
    }

    /* [1] maskGenAlgorithm -- AlgorithmIdentifier { OID id-mgf1, hash AlgoId }
     * Parse manually: read the MGF SEQUENCE + OID, then the hash AlgoId
     * parameter, because GetAlgoId consumes the entire AlgorithmIdentifier
     * including the hash parameter inside it. */
    if (ret == 0) {
        if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_MGF)) {
            int mgfSeqLen = 0;
            word32 mgfEnd;

            if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
                WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_header");
                ret = ASN_PARSE_E;
            }
            /* Read MGF AlgorithmIdentifier SEQUENCE header */
            if (ret == 0) {
                if (GetSequence(params, &idx, &mgfSeqLen, sz) < 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_seq");
                    ret = ASN_PARSE_E;
                }
            }
            /* Bound subsequent reads to the MGF SEQUENCE content */
            mgfEnd = idx + (word32)mgfSeqLen;
            if (mgfEnd > sz) {
                WOLFSSL_MSG("DecodeRsaPssParams: mgf_seq overflows buffer");
                ret = ASN_PARSE_E;
            }
            /* Read MGF OID (id-mgf1) directly */
            if (ret == 0) {
                if (GetObjectId(params, &idx, &oid, oidIgnoreType,
                                mgfEnd) < 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid");
                    ret = ASN_PARSE_E;
                }
            }
            if ((ret == 0) && (oid != MGF1_OID)) {
                WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid_value");
                ret = ASN_PARSE_E;
            }
            /* Read hash AlgorithmIdentifier (parameter of MGF1) */
            if (ret == 0) {
                ret = GetAlgoId(params, &idx, &oid, oidHashType, mgfEnd);
                if (ret == 0) {
                    ret = RsaPssHashOidToMgf1(oid, mgf);
                    if (ret != 0) {
                        WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_oid");
                    }
                }
                else {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_algo");
                }
            }
            if ((ret == 0) && (idx != mgfEnd)) {
                WOLFSSL_MSG("DecodeRsaPssParams: extra data in mgf_seq");
                ret = ASN_PARSE_E;
            }
        }
        else {
            /* Default MGF/Hash algorithm. */
            *mgf = WC_MGF1SHA1;
        }
    }

    /* [2] saltLength */
    if (ret == 0) {
        if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_SALTLEN)) {
            /* Salt length to use with padding. */
            if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
                WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_header");
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                ret = GetInteger16Bit(params, &idx, sz);
                if (ret >= 0) {
                    *saltLen = ret;
                    ret = 0;
                }
                else {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_value");
                }
            }
        }
        else {
            /* Default salt length. */
            *saltLen = 20;
        }
    }

    /* [3] trailerField */
    if (ret == 0) {
        if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_TRAILER)) {
            /* Unused - trailerField. */
            if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
                WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_header");
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                ret = GetInteger16Bit(params, &idx, sz);
#ifndef WOLFSSL_NO_ASN_STRICT
                /* RFC 8017 A.2.3: trailerField SHALL be trailerFieldBC(1). */
                if (ret == 1) {
                    ret = 0;
                }
                else if (ret >= 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: trailerField must be 1");
                    ret = ASN_PARSE_E;
                }
                else {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_value");
                }
#else
                if (ret > 0) {
                    ret = 0;
                }
                else if (ret != 0) {
                    WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_value");
                }
#endif
            }
        }
    }

    if ((ret == 0) && (idx != sz)) {
        WOLFSSL_MSG("DecodeRsaPssParams: fail at extra_data");
        ret = ASN_PARSE_E;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
}

WOLFSSL_TEST_VIS int wc_DecodeRsaPssParams(const byte* params, word32 sz,
    enum wc_HashType* hash, int* mgf, int* saltLen)
{
    return DecodeRsaPssParams(params, sz, hash, mgf, saltLen);
}

/* Encode AlgorithmIdentifier for id-RSASSA-PSS with full RSASSA-PSS-params.
 * hashOID: e.g. SHA256h; saltLen: e.g. 32; trailerField is encoded as 1.
 * When out is NULL returns required length only.
 * Returns encoded length on success, 0 on error.
 * Note: saltLength is encoded as a single-byte INTEGER; values >255 would
 * require multi-byte encoding (not implemented; CMS profiles use hash-length
 * salts, e.g. 20-64 bytes). */
/* Scratch buffer for building RSA-PSS AlgorithmIdentifier sub-encodings.
 * Must hold the largest single sub-encoding (hash AlgoId, typically ~15 bytes)
 * plus room for integer TLVs.  64 bytes is sufficient; 128 gives margin. */
#define RSA_PSS_ALGOID_TMPBUF_SZ 128

word32 wc_EncodeRsaPssAlgoId(int hashOID, int saltLen, byte* out, word32 outSz)
{
    word32 idx = 0;
    word32 hashAlgSz, mgf1ParamSz, tag0Sz, tag1Sz, tag2Sz, tag3Sz;
    word32 paramsSz, outerSz;
    const byte* rsapssOid = sigRsaSsaPssOid;
    word32 rsapssOidSz = sizeof(sigRsaSsaPssOid);
    /* MGF1 OID 1.2.840.113549.1.1.8 */
    static const byte mgf1Oid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08 };
    int setIntRet;
#ifdef WOLFSSL_SMALL_STACK
    byte* tmpBuf;
#else
    byte tmpBuf[RSA_PSS_ALGOID_TMPBUF_SZ];
#endif

    if (saltLen < 0 || saltLen > 255) {
        WOLFSSL_MSG("Salt length must be 0-255 for single-byte encoding");
        return 0;
    }

#ifdef WOLFSSL_SMALL_STACK
    tmpBuf = (byte*)XMALLOC(RSA_PSS_ALGOID_TMPBUF_SZ, NULL,
                            DYNAMIC_TYPE_TMP_BUFFER);
    if (tmpBuf == NULL)
        return 0;
#endif

    hashAlgSz = SetAlgoID(hashOID, out ? tmpBuf : NULL, oidHashType, 0);
    if (hashAlgSz == 0) {
        idx = 0; goto pss_algoid_done;
    }
    mgf1ParamSz = hashAlgSz;
    tag0Sz = SetExplicit(0, hashAlgSz, NULL, 0) + hashAlgSz;
    {
        /* MGF AlgorithmIdentifier: SEQUENCE { OID id-mgf1, hash AlgoId }
         * The hash AlgoId (from SetAlgoID) is the parameter of MGF1. */
        word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid);
        word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz;
        word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent;
        tag1Sz = SetExplicit(1, mgf1SeqSz, NULL, 0) + mgf1SeqSz;
    }
    /* SetASNInt writes only tag+length (+ optional leading 0); value byte(s) appended by caller. */
    setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), NULL);
    if (setIntRet <= 0) {
        idx = 0; goto pss_algoid_done;
    }
    {
        word32 saltIntSz = (word32)setIntRet + 1; /* header + one value byte (two if high bit set) */
        tag2Sz = SetExplicit(2, saltIntSz, NULL, 0) + saltIntSz;
    }
    setIntRet = SetASNInt(1, 0x01, NULL);
    if (setIntRet <= 0) {
        idx = 0; goto pss_algoid_done;
    }
    {
        word32 trailerIntSz = (word32)setIntRet + 1;
        tag3Sz = SetExplicit(3, trailerIntSz, NULL, 0) + trailerIntSz;
    }

    paramsSz = tag0Sz + tag1Sz + tag2Sz + tag3Sz;
    {
        word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz;
        word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz;
        outerSz = SetSequence(idPart + seqPart, NULL) + idPart + seqPart;
    }

    if (out == NULL) {
        idx = outerSz; goto pss_algoid_done;
    }
    if (outSz < outerSz) {
        idx = 0; goto pss_algoid_done;
    }
    if (hashAlgSz > RSA_PSS_ALGOID_TMPBUF_SZ) {
        idx = 0; goto pss_algoid_done;
    }

    {
        word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz;
        word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz;
        idx += SetSequence(idPart + seqPart, out + idx);
    }
    idx += (word32)SetObjectId((int)rsapssOidSz, out + idx);
    XMEMCPY(out + idx, rsapssOid, rsapssOidSz);
    idx += rsapssOidSz;
    idx += SetSequence(paramsSz, out + idx);

    idx += SetExplicit(0, hashAlgSz, out + idx, 0);
    XMEMCPY(out + idx, tmpBuf, hashAlgSz);
    idx += hashAlgSz;

    {
        /* [1] EXPLICIT { SEQUENCE { OID id-mgf1, hash AlgoId } } */
        word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid);
        word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz;
        word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent;
        idx += SetExplicit(1, mgf1SeqSz, out + idx, 0);
        idx += SetSequence(mgf1SeqContent, out + idx);
        idx += (word32)SetObjectId((int)sizeof(mgf1Oid), out + idx);
        XMEMCPY(out + idx, mgf1Oid, sizeof(mgf1Oid));
        idx += (word32)sizeof(mgf1Oid);
        XMEMCPY(out + idx, tmpBuf, hashAlgSz);
        idx += hashAlgSz;
    }

    /* INTEGER saltLength (single byte; see function comment for >255 limitation). */
    setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), tmpBuf);
    if (setIntRet > 0) {
        tmpBuf[setIntRet] = (byte)(saltLen & 0xff);
    }
    {
        word32 saltIntSz = (word32)setIntRet + 1;
        idx += SetExplicit(2, saltIntSz, out + idx, 0);
        XMEMCPY(out + idx, tmpBuf, saltIntSz);
        idx += saltIntSz;
    }

    /* INTEGER trailerField (1): same pattern. */
    setIntRet = SetASNInt(1, 0x01, tmpBuf);
    if (setIntRet > 0) {
        tmpBuf[setIntRet] = 0x01;
    }
    {
        word32 trailerIntSz = (word32)setIntRet + 1;
        idx += SetExplicit(3, trailerIntSz, out + idx, 0);
        XMEMCPY(out + idx, tmpBuf, trailerIntSz);
        idx += trailerIntSz;
    }

pss_algoid_done:
#ifdef WOLFSSL_SMALL_STACK
    XFREE(tmpBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
    return idx;
}

#endif /* WC_RSA_PSS */

/* Byte offset of numbers in RSA key. */
size_t rsaIntOffset[] = {
    WC_OFFSETOF(RsaKey, n),
    WC_OFFSETOF(RsaKey, e),
#ifndef WOLFSSL_RSA_PUBLIC_ONLY
    WC_OFFSETOF(RsaKey, d),
    WC_OFFSETOF(RsaKey, p),
    WC_OFFSETOF(RsaKey, q),
#if defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || !defined(RSA_LOW_MEM)
    WC_OFFSETOF(RsaKey, dP),
    WC_OFFSETOF(RsaKey, dQ),
    WC_OFFSETOF(RsaKey, u)
#endif
#endif
};

/* Get a number from the RSA key based on an index.
 *
 * Order: { n, e, d, p, q, dP, dQ, u }
 *
 * Caller must ensure index is not invalid!
 *
 * @param [in] key  RSA key object.
 * @param [in] idx  Index of number.
 * @return  A pointer to an mp_int when valid index.
 * @return  NULL when invalid index.
 */
static mp_int* GetRsaInt(RsaKey* key, int idx)
{
    /* Cast key to byte array to and use offset to get to mp_int field. */
    return (mp_int*)(((byte*)key) + rsaIntOffset[idx]);
}

#ifdef WOLFSSL_RSA_PUBLIC_ONLY
    #define RSA_INT_CNT   2
#elif !(defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || \
      !defined(RSA_LOW_MEM))
    #define RSA_INT_CNT   5
#else
    #define RSA_INT_CNT   8
#endif
#define RSA_MAX_INT_CNT   8

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for an RSA private key.
 * PKCS #1: RFC 8017, A.1.2 - RSAPrivateKey
 */
static const ASNItem rsaKeyASN[] = {
/*  SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/*  VER */        { 1, ASN_INTEGER, 0, 0, 0 },
                /* Integers need to be in this specific order
                 * as asn code depends on this. */
/*  N   */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  E   */        { 1, ASN_INTEGER, 0, 0, 0 },
#if !defined(WOLFSSL_RSA_PUBLIC_ONLY) || defined(WOLFSSL_KEY_GEN)
/*  D   */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  P   */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  Q   */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  DP  */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  DQ  */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  U   */        { 1, ASN_INTEGER, 0, 0, 0 },
                /* otherPrimeInfos  OtherPrimeInfos OPTIONAL
                 * v2 - multiprime */
#endif
};
enum {
    RSAKEYASN_IDX_SEQ = 0,
    RSAKEYASN_IDX_VER,
    /* Integers need to be in this specific order
     * as asn code depends on this. */
    RSAKEYASN_IDX_N,
    RSAKEYASN_IDX_E,
#if !defined(WOLFSSL_RSA_PUBLIC_ONLY) || defined(WOLFSSL_KEY_GEN)
    RSAKEYASN_IDX_D,
    RSAKEYASN_IDX_P,
    RSAKEYASN_IDX_Q,
    RSAKEYASN_IDX_DP,
    RSAKEYASN_IDX_DQ,
    RSAKEYASN_IDX_U,
#endif
    WOLF_ENUM_DUMMY_LAST_ELEMENT(RSAKEYASN_IDX)
};

/* Number of items in ASN.1 template for an RSA private key. */
#define rsaKeyASN_Length (sizeof(rsaKeyASN) / sizeof(ASNItem))
#endif

/* Decode RSA private key.
 *
 * PKCS #1: RFC 8017, A.1.2 - RSAPrivateKey
 *
 * Compiling with WOLFSSL_RSA_PUBLIC_ONLY will result in only the public fields
 * being extracted.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA private key.
 *                            On out, start of ASN.1 item after RSA private key.
 * @param [in, out] key       RSA key object. May be NULL.
 * @param [out]     keySz     Size of key in bytes. May be NULL.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when input or inOutIdx is NULL.
 * @return  BAD_FUNC_ARG when key and keySz are NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int _RsaPrivateKeyDecode(const byte* input, word32* inOutIdx,
    RsaKey* key, int* keySz, word32 inSz)
{
    DECL_ASNGETDATA(dataASN, rsaKeyASN_Length);
    int        ret = 0;
    byte       version = (byte)-1;
#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)
    word32 algId = 0;
#endif
    void*      heap = NULL;

    /* Check validity of parameters. */
    if ((inOutIdx == NULL) || (input == NULL) || ((key == NULL) &&
            (keySz == NULL))) {
        ret = BAD_FUNC_ARG;
    }

    if ((ret == 0) && (key != NULL)) {
        heap = key->heap;
    }

#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)
    if (ret == 0) {
        /* if has pkcs8 header skip it */
        if (ToTraditionalInline_ex(input, inOutIdx, inSz, &algId) < 0) {
            /* ignore error, did not have pkcs8 header */
        }
    }
#endif

    (void)heap;
    CALLOC_ASNGETDATA(dataASN, rsaKeyASN_Length, ret, heap);

    if (ret == 0) {
        /* Register variable to hold version field. */
        GetASN_Int8Bit(&dataASN[RSAKEYASN_IDX_VER], &version);
        /* Setup data to store INTEGER data in mp_int's in RSA object. */
    #if defined(WOLFSSL_RSA_PUBLIC_ONLY)
        #define RSA_ASN_INTS        RSA_PUB_INTS
        /* Not extracting all data from BER encoding. */
        #define RSA_ASN_COMPLETE    0
    #else
        #define RSA_ASN_INTS        RSA_INTS
        /* Extracting all data from BER encoding. */
        #define RSA_ASN_COMPLETE    1
    #endif
        if (key != NULL) {
            int i;
            /* Extract all public fields. */
            for (i = 0; i < RSA_ASN_INTS; i++) {
                GetASN_MP(&dataASN[(byte)RSAKEYASN_IDX_N + i],
                    GetRsaInt(key, i));
            }
        }
        /* Parse BER encoding for RSA private key. */
        ret = GetASN_Items(rsaKeyASN, dataASN, rsaKeyASN_Length,
            RSA_ASN_COMPLETE, input, inOutIdx, inSz);
    }
    /* Check version: 0 - two prime, 1 - multi-prime
     * Multi-prime has optional sequence after coefficient for extra primes.
     * If extra primes, parsing will fail as not all the buffer was used.
     */
    if ((ret == 0) && (version > PKCS1v1)) {
        ret = ASN_PARSE_E;
    }
    if ((ret == 0) && (key != NULL)) {
    #if !defined(WOLFSSL_RSA_PUBLIC_ONLY)
        /* RSA key object has all private key values. */
        key->type = RSA_PRIVATE;
    #else
        /* RSA key object has all public key values. */
        key->type = RSA_PUBLIC;
    #endif

    #ifdef WOLFSSL_XILINX_CRYPT
        if (wc_InitRsaHw(key) != 0)
            ret = BAD_STATE_E;
    #endif
    }
    else if (ret == 0) {
        /* Not filling in key but do want key size. */
        *keySz = (int)dataASN[(byte)RSAKEYASN_IDX_N].length;
        /* Check whether first byte of data is 0x00 and drop it. */
        if (input[(int)dataASN[RSAKEYASN_IDX_E].offset - *keySz] == 0) {
            (*keySz)--;
        }
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode RSA private key.
 *
 * PKCS #1: RFC 8017, A.1.2 - RSAPrivateKey
 *
 * Compiling with WOLFSSL_RSA_PUBLIC_ONLY will result in only the public fields
 * being extracted.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA private key.
 *                            On out, start of ASN.1 item after RSA private key.
 * @param [in, out] key       RSA key object.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when input, inOutIdx or key is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 */
int wc_RsaPrivateKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key,
    word32 inSz)
{
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
    int cbRet = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
    int tmpErr = 0;
    word32 tmpIdx;
    WC_DECLARE_VAR(tmpKey, RsaKey, 1, NULL);
#endif

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

#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
    #ifndef WOLF_CRYPTO_CB_FIND
    if (key->devId != INVALID_DEVID)
    #endif
    {
        tmpIdx = *inOutIdx;

        WC_ALLOC_VAR(tmpKey, RsaKey, 1, key->heap);
        if (!WC_VAR_OK(tmpKey)) {
            return MEMORY_E;
        }
        XMEMSET(tmpKey, 0, sizeof(RsaKey));

        tmpErr = wc_InitRsaKey_ex(tmpKey, key->heap, INVALID_DEVID);
        if (tmpErr != 0) {
            WC_FREE_VAR(tmpKey, key->heap);
            return tmpErr;
        }

        /* Decode into temp key (software-only, no callback recursion
         * since tmpKey has INVALID_DEVID) */
        tmpErr = _RsaPrivateKeyDecode(input, &tmpIdx, tmpKey, NULL, inSz);
        if (tmpErr == 0) {
            cbRet = wc_CryptoCb_SetKey(key->devId,
                WC_SETKEY_RSA_PRIV, key, tmpKey,
                wc_RsaEncryptSize(tmpKey), NULL, 0, 0);
        }

        /* wc_FreeRsaKey calls mp_forcezero on all private key components,
         * so no separate ForceZero of the struct is needed here. */
        wc_FreeRsaKey(tmpKey);
        WC_FREE_VAR(tmpKey, key->heap);

        if (tmpErr != 0) {
            return tmpErr;
        }
        if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
            *inOutIdx = tmpIdx;
            return cbRet;
        }
        /* CRYPTOCB_UNAVAILABLE: fall through to software import */
    }
#endif

    return _RsaPrivateKeyDecode(input, inOutIdx, key, NULL, inSz);
}

/* Valdidate RSA private key ASN.1 encoding.
 *
 * PKCS #1: RFC 8017, A.1.2 - RSAPrivateKey
 *
 * Compiling with WOLFSSL_RSA_PUBLIC_ONLY will result in only the public fields
 * being extracted.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA private key.
 *                            On out, start of ASN.1 item after RSA private key.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when input, inOutIdx or keySz is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 */
int wc_RsaPrivateKeyValidate(const byte* input, word32* inOutIdx, int* keySz,
     word32 inSz)
{
    return _RsaPrivateKeyDecode(input, inOutIdx, NULL, keySz, inSz);
}
#endif /* NO_RSA */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for a PKCS #8 PrivateKeyInfo / RFC 5958 OneAsymmetricKey.
 * Includes the optional [0] attributes and [1] publicKey trailing fields.
 */
static const ASNItem pkcs8KeyASN[] = {
/*  SEQ                 */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/*  VER                 */        { 1, ASN_INTEGER, 0, 0, 0 },
/*  PKEY_ALGO_SEQ       */        { 1, ASN_SEQUENCE, 1, 1, 0 },
/*  PKEY_ALGO_OID_KEY   */            { 2, ASN_OBJECT_ID, 0, 0, 0 },
/*  PKEY_ALGO_OID_CURVE */            { 2, ASN_OBJECT_ID, 0, 0, 1 },
/*  PKEY_ALGO_NULL      */            { 2, ASN_TAG_NULL, 0, 0, 1 },
#ifdef WC_RSA_PSS
/*  PKEY_ALGO_PARAM_SEQ */            { 2, ASN_SEQUENCE, 1, 0, 1 },
#endif
/*  PKEY_DATA           */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
/*  Attributes [0] OPTIONAL */
                                  { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 1 },
/*  publicKey [1] OPTIONAL */
                                  { 1, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 },
};
enum {
    PKCS8KEYASN_IDX_SEQ = 0,
    PKCS8KEYASN_IDX_VER,
    PKCS8KEYASN_IDX_PKEY_ALGO_SEQ,
    PKCS8KEYASN_IDX_PKEY_ALGO_OID_KEY,
    PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE,
    PKCS8KEYASN_IDX_PKEY_ALGO_NULL,
#ifdef WC_RSA_PSS
    PKCS8KEYASN_IDX_PKEY_ALGO_PARAM_SEQ,
#endif
    PKCS8KEYASN_IDX_PKEY_DATA,
    PKCS8KEYASN_IDX_PKEY_ATTRIBUTES,
    PKCS8KEYASN_IDX_PKEY_PUBKEY,
    WOLF_ENUM_DUMMY_LAST_ELEMENT(PKCS8KEYASN_IDX)
};

/* Number of items in ASN.1 template for a PKCS #8 key. */
#define pkcs8KeyASN_Length (sizeof(pkcs8KeyASN) / sizeof(ASNItem))
#endif

/* Remove PKCS #8 header around an RSA, ECDSA, Ed25519, or Ed448.
 *
 * @param [in]       input     Buffer holding BER data.
 * @param [in, out]  inOutIdx  On in, start of PKCS #8 encoding.
 *                             On out, start of encoded key.
 * @param [in]       sz        Size of data in buffer.
 * @param [out]      algId     Key's algorithm id from PKCS #8 header.
 * @param [out]      eccOid    ECC curve OID.
 * @return  Length of key data on success.
 * @return  BAD_FUNC_ARG when input or inOutIdx is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int ToTraditionalInline_ex2(const byte* input, word32* inOutIdx, word32 sz,
                            word32* algId, word32* eccOid)
{
    DECL_ASNGETDATA(dataASN, pkcs8KeyASN_Length);
    int ret = 0;
    word32 oid = 9;
    byte version = 0;
    word32 idx;

    (void)eccOid;

    /* Check validity of parameters. */
    if (input == NULL || inOutIdx == NULL) {
        return BAD_FUNC_ARG;
    }

    idx = *inOutIdx;

    CALLOC_ASNGETDATA(dataASN, pkcs8KeyASN_Length, ret, NULL);

    if (ret == 0) {
        /* Get version, check key type and curve type. */
        GetASN_Int8Bit(&dataASN[PKCS8KEYASN_IDX_VER], &version);
        GetASN_OID(&dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_KEY], oidKeyType);
        GetASN_OID(&dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE], oidCurveType);
        /* Parse data. */
        ret = GetASN_Items(pkcs8KeyASN, dataASN, pkcs8KeyASN_Length, 1, input,
                           &idx, sz);
    }

    if (ret == 0) {
        /* Key type OID. */
        oid = dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_KEY].data.oid.sum;

        /* Only v1(0) and v2(1) are supported (RFC 5958). The [1] publicKey
         * trailer is permitted only when version == v1. */
        if (version > PKCS8v1) {
            ret = ASN_PARSE_E;
        }
        else if ((version < PKCS8v1) &&
                 (dataASN[PKCS8KEYASN_IDX_PKEY_PUBKEY].tag != 0)) {
            ret = ASN_PARSE_E;
        }
    }
    if (ret == 0) {
        switch (oid) {
    #ifndef NO_RSA
            case RSAk:
                /* Must have NULL item but not OBJECT_ID item. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag == 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #ifdef WC_RSA_PSS
            case RSAPSSk:
                /* Must not have NULL item. */
                if (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) {
                    ret = ASN_PARSE_E;
                }
                if (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_PARAM_SEQ].tag != 0) {
                    enum wc_HashType hash;
                    int mgf;
                    int saltLen;
                    const byte* params = GetASNItem_Addr(
                        dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_PARAM_SEQ], input);
                    word32 paramsSz = GetASNItem_Length(
                        dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_PARAM_SEQ], input);

                    /* Validate the private key parameters. */
                    ret = DecodeRsaPssParams(params, paramsSz, &hash, &mgf,
                        &saltLen);
                    if (ret != 0) {
                        return ASN_PARSE_E;
                    }
                    /* TODO: store parameters so that usage can be checked. */
                }
                break;
        #endif
    #endif
        #ifdef HAVE_ECC
            case ECDSAk:
                /* Must not have NULL item. */
                if (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) {
                    ret = ASN_PARSE_E;
                }
                if (eccOid != NULL) {
                    ASNGetData* oidCurve =
                        &dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE];
                    *eccOid = oidCurve->data.oid.sum;
                }
                break;
        #endif
        #ifdef HAVE_ED25519
            case ED25519k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef HAVE_CURVE25519
            case X25519k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef HAVE_ED448
            case ED448k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef HAVE_CURVE448
            case X448k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifndef NO_DH
            case DHk:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef HAVE_FALCON
            case FALCON_LEVEL1k:
            case FALCON_LEVEL5k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef HAVE_DILITHIUM
            #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
            case DILITHIUM_LEVEL2k:
            case DILITHIUM_LEVEL3k:
            case DILITHIUM_LEVEL5k:
            #endif
            case ML_DSA_LEVEL2k:
            case ML_DSA_LEVEL3k:
            case ML_DSA_LEVEL5k:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef WOLFSSL_HAVE_SLHDSA
            case SLH_DSA_SHA2_128Sk:
            case SLH_DSA_SHA2_128Fk:
            case SLH_DSA_SHA2_192Sk:
            case SLH_DSA_SHA2_192Fk:
            case SLH_DSA_SHA2_256Sk:
            case SLH_DSA_SHA2_256Fk:
            case SLH_DSA_SHAKE_128Sk:
            case SLH_DSA_SHAKE_128Fk:
            case SLH_DSA_SHAKE_192Sk:
            case SLH_DSA_SHAKE_192Fk:
            case SLH_DSA_SHAKE_256Sk:
            case SLH_DSA_SHAKE_256Fk:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef WOLFSSL_HAVE_LMS
            case HSS_LMSk:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
        #ifdef WOLFSSL_HAVE_XMSS
            case XMSSk:
                /* Neither NULL item nor OBJECT_ID item allowed. */
                if ((dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) ||
                    (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].tag != 0)) {
                    ret = ASN_PARSE_E;
                }
                break;
        #endif
            /* Other OIDs (DSAk), no parameter validation. */
            default:
                break;
        }
    }
    if (ret == 0) {
        /* Return algorithm id of internal key. */
        *algId = oid;
        /* Return index to start of internal key. */
        *inOutIdx = GetASNItem_DataIdx(dataASN[PKCS8KEYASN_IDX_PKEY_DATA], input);
        /* Return value is length of internal key. */
        ret = (int)dataASN[PKCS8KEYASN_IDX_PKEY_DATA].data.ref.length;
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Remove PKCS #8 header around an RSA, ECDSA, Ed25519, or Ed448.
 *
 * @param [in]       input     Buffer holding BER data.
 * @param [in, out]  inOutIdx  On in, start of PKCS #8 encoding.
 *                             On out, start of encoded key.
 * @param [in]       sz        Size of data in buffer.
 * @param [out]      algId     Key's algorithm id from PKCS #8 header.
 * @return  Length of key data on success.
 * @return  BAD_FUNC_ARG when input or inOutIdx is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz,
                           word32* algId)
{
    return ToTraditionalInline_ex2(input, inOutIdx, sz, algId, NULL);
}


/* TODO: test case  */
int ToTraditionalInline(const byte* input, word32* inOutIdx, word32 sz)
{
    word32 oid;

    return ToTraditionalInline_ex(input, inOutIdx, sz, &oid);
}

#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)

/* Remove PKCS8 header, move beginning of traditional to beginning of input */
int ToTraditional_ex(byte* input, word32 sz, word32* algId)
{
    word32 inOutIdx = 0;
    int    length;

    if (input == NULL)
        return BAD_FUNC_ARG;

    length = ToTraditionalInline_ex(input, &inOutIdx, sz, algId);
    if (length < 0)
        return length;

    if ((word32)length + inOutIdx > sz)
        return BUFFER_E;

    XMEMMOVE(input, input + inOutIdx, (size_t)length);

    return length;
}

int ToTraditional(byte* input, word32 sz)
{
    word32 oid;

    return ToTraditional_ex(input, sz, &oid);
}

#endif /* HAVE_PKCS8 || HAVE_PKCS12 */

#if defined(HAVE_PKCS8)

int wc_GetPkcs8TraditionalOffset(byte* input, word32* inOutIdx, word32 sz)
{
    int length;
    word32 algId;

    if (input == NULL || inOutIdx == NULL || (*inOutIdx > sz))
        return BAD_FUNC_ARG;

    length = ToTraditionalInline_ex(input, inOutIdx, sz, &algId);

    return length;
}

#ifdef WOLFSSL_ASN_TEMPLATE
int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz,
        int algoID, const byte* curveOID, word32 oidSz)
{
    /* pkcs8KeyASN_Length-2, the -2 is because we are not adding the optional
     * set of attributes or publicKey */
    DECL_ASNSETDATA(dataASN, pkcs8KeyASN_Length-2);
    word32 sz = 0;
    int ret = 0;
    word32 keyIdx = 0;
    word32 tmpAlgId = 0;

    WOLFSSL_ENTER("wc_CreatePKCS8Key");

    /* Check validity of parameters. */
    if (out == NULL && outSz != NULL) {
    }
    else if (key == NULL || out == NULL || outSz == NULL) {
        ret = BAD_FUNC_ARG;
    }

#ifndef WOLFSSL_NO_ASN_STRICT
    /* Sanity check: make sure key doesn't have PKCS #8 header. */
    if (ToTraditionalInline_ex(key, &keyIdx, keySz, &tmpAlgId) >= 0) {
        (void)tmpAlgId;
        ret = ASN_PARSE_E;
    }
#else
    (void)keyIdx;
    (void)tmpAlgId;
#endif

    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, pkcs8KeyASN_Length-2, ret, NULL);

    if (ret == 0) {
        /* Only support default PKCS #8 format - v0. */
        SetASN_Int8Bit(&dataASN[PKCS8KEYASN_IDX_VER], PKCS8v0);
        /* Set key OID that corresponds to key data. */
        SetASN_OID(&dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_KEY], (word32)algoID,
            oidKeyType);
        if (curveOID != NULL && oidSz > 0) {
            /* ECC key and curveOID set to write. */
            SetASN_Buffer(&dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE],
                curveOID, oidSz);
        }
        else {
            /* EC curve OID to encode. */
            dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE].noOut = 1;
        }
        /* Only RSA keys have NULL tagged item after OID. */
        dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].noOut = (algoID != RSAk);
    #ifdef WC_RSA_PSS
        dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_PARAM_SEQ].noOut = 1;
    #endif
        /* Set key data to encode. */
        SetASN_Buffer(&dataASN[PKCS8KEYASN_IDX_PKEY_DATA], key, keySz);

        /* Get the size of the DER encoding. */
        ret = SizeASN_Items(pkcs8KeyASN, dataASN, pkcs8KeyASN_Length-2, &sz);
    }
    if ((ret == 0) || (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E))) {
        /* Always return the calculated size. */
        *outSz = sz;
    }
    /* Check for buffer to encoded into. */
    if ((ret == 0) && (out == NULL)) {
        WOLFSSL_MSG("Checking size of PKCS8");
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    if (ret == 0) {
        /*  Encode PKCS #8 key into buffer. */
        SetASN_Items(pkcs8KeyASN, dataASN, pkcs8KeyASN_Length-2, out);
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_PKCS8 */

#if defined(HAVE_PKCS12) || !defined(NO_CHECK_PRIVATE_KEY)
/* check that the private key is a pair for the public key
 * return 1 (true) on match
 * return 0 or negative value on failure/error
 *
 * privKey   : buffer holding DER format private key
 * privKeySz : size of private key buffer
 * pubKey    : buffer holding DER format public key
 * pubKeySz  : size of public key buffer
 * ks        : type of key
 * heap      : heap hint to use */
int wc_CheckPrivateKey(const byte* privKey, word32 privKeySz,
                       const byte* pubKey, word32 pubKeySz, enum Key_Sum ks,
                       void* heap)
{
    int ret;
    (void)privKeySz;
    (void)pubKeySz;
    (void)ks;

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

    #if !defined(NO_RSA) && !defined(NO_ASN_CRYPT)
    /* test if RSA key */
    if (ks == RSAk
    #ifdef WC_RSA_PSS
        || ks == RSAPSSk
    #endif
        ) {
    #if defined(WOLFSSL_RSA_PUBLIC_ONLY) || defined(WOLFSSL_RSA_VERIFY_ONLY)
        ret = NOT_COMPILED_IN;
    #else
    #ifdef WOLFSSL_SMALL_STACK
        RsaKey* a;
        RsaKey* b = NULL;
    #else
        RsaKey a[1], b[1];
    #endif
        word32 keyIdx = 0;

    #ifdef WOLFSSL_SMALL_STACK
        a = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_RSA);
        if (a == NULL)
            return MEMORY_E;
        b = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_RSA);
        if (b == NULL) {
            XFREE(a, NULL, DYNAMIC_TYPE_RSA);
            return MEMORY_E;
        }
    #endif

        if ((ret = wc_InitRsaKey(a, heap)) < 0) {
            WC_FREE_VAR_EX(b, NULL, DYNAMIC_TYPE_RSA);
            WC_FREE_VAR_EX(a, NULL, DYNAMIC_TYPE_RSA);
            return ret;
        }
        if ((ret = wc_InitRsaKey(b, heap)) < 0) {
            wc_FreeRsaKey(a);
            WC_FREE_VAR_EX(b, NULL, DYNAMIC_TYPE_RSA);
            WC_FREE_VAR_EX(a, NULL, DYNAMIC_TYPE_RSA);
            return ret;
        }
        if ((ret = wc_RsaPrivateKeyDecode(privKey, &keyIdx, a, privKeySz)) == 0) {
            WOLFSSL_MSG("Checking RSA key pair");
            keyIdx = 0; /* reset to 0 for parsing public key */

            if ((ret = wc_RsaPublicKeyDecode(pubKey, &keyIdx, b,
                    pubKeySz)) == 0) {
                /* both keys extracted successfully now check n and e
                 * values are the same. This is dereferencing RsaKey */
                if (mp_cmp(&(a->n), &(b->n)) != MP_EQ ||
                    mp_cmp(&(a->e), &(b->e)) != MP_EQ) {
                    ret = MP_CMP_E;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
                else
                    ret = 1;
            }
            else {
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        }
        wc_FreeRsaKey(b);
        wc_FreeRsaKey(a);
        WC_FREE_VAR_EX(b, NULL, DYNAMIC_TYPE_RSA);
        WC_FREE_VAR_EX(a, NULL, DYNAMIC_TYPE_RSA);
    #endif /* !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */
    }
    else
    #endif /* !NO_RSA && !NO_ASN_CRYPT */

    #if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT) && !defined(NO_ASN_CRYPT)
    if (ks == ECDSAk) {
    #ifdef WOLFSSL_SMALL_STACK
        ecc_key* key_pair;
        byte*    privDer;
    #else
        ecc_key  key_pair[1];
        byte     privDer[MAX_ECC_BYTES];
    #endif
        word32   privSz = MAX_ECC_BYTES;
        word32   keyIdx = 0;

    #ifdef WOLFSSL_SMALL_STACK
        key_pair = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, DYNAMIC_TYPE_ECC);
        if (key_pair == NULL)
            return MEMORY_E;
        privDer = (byte*)XMALLOC(MAX_ECC_BYTES, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        if (privDer == NULL) {
            XFREE(key_pair, NULL, DYNAMIC_TYPE_ECC);
            return MEMORY_E;
        }
    #endif

        if ((ret = wc_ecc_init_ex(key_pair, heap, INVALID_DEVID)) < 0) {
            WC_FREE_VAR_EX(privDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_ECC);
            return ret;
        }

        if ((ret = wc_EccPrivateKeyDecode(privKey, &keyIdx, key_pair,
                privKeySz)) == 0) {
            WOLFSSL_MSG("Checking ECC key pair");

            if ((ret = wc_ecc_export_private_only(key_pair, privDer, &privSz))
                                                                         == 0) {
            #ifdef WOLFSSL_CHECK_MEM_ZERO
                wc_MemZero_Add("wc_CheckPrivateKey privDer", privDer, privSz);
            #endif
                wc_ecc_free(key_pair);
                ret = wc_ecc_init_ex(key_pair, heap, INVALID_DEVID);
                if (ret == 0) {
                    ret = wc_ecc_import_private_key(privDer,
                                            privSz, pubKey,
                                            pubKeySz, key_pair);
                }

                /* public and private extracted successfully now check if is
                 * a pair and also do sanity checks on key. wc_ecc_check_key
                 * checks that private * base generator equals pubkey */
                if (ret == 0) {
                    if ((ret = wc_ecc_check_key(key_pair)) == 0) {
                        ret = 1;
                    }
                    else {
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                }
                ForceZero(privDer, privSz);
            }
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ret);
        }
        wc_ecc_free(key_pair);
    #ifdef WOLFSSL_SMALL_STACK
        XFREE(privDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(key_pair, NULL, DYNAMIC_TYPE_ECC);
    #elif defined(WOLFSSL_CHECK_MEM_ZERO)
        wc_MemZero_Check(privDer, MAX_ECC_BYTES);
    #endif
    }
    else
    #endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT && !NO_ASN_CRYPT */

    #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) && !defined(NO_ASN_CRYPT)
    if (ks == ED25519k) {
        WC_DECLARE_VAR(key_pair, ed25519_key, 1, 0);
        word32       keyIdx = 0;

        WC_ALLOC_VAR_EX(key_pair, ed25519_key, 1, NULL, DYNAMIC_TYPE_ED25519,
            return MEMORY_E);

        if ((ret = wc_ed25519_init_ex(key_pair, heap, INVALID_DEVID)) < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_ED25519);
            return ret;
        }
        if ((ret = wc_Ed25519PrivateKeyDecode(privKey, &keyIdx, key_pair,
                privKeySz)) == 0) {
            WOLFSSL_MSG("Checking ED25519 key pair");
            keyIdx = 0;
            if ((ret = wc_ed25519_import_public(pubKey, pubKeySz,
                    key_pair)) == 0) {
                /* public and private extracted successfully no check if is
                 * a pair and also do sanity checks on key. wc_ecc_check_key
                 * checks that private * base generator equals pubkey */
                if ((ret = wc_ed25519_check_key(key_pair)) == 0) {
                    ret = 1;
                }
                else {
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ret);
        }
        wc_ed25519_free(key_pair);
        WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_ED25519);
    }
    else
    #endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT && !NO_ASN_CRYPT */

    #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) && !defined(NO_ASN_CRYPT)
    if (ks == ED448k) {
        WC_DECLARE_VAR(key_pair, ed448_key, 1, 0);
        word32     keyIdx = 0;

        WC_ALLOC_VAR_EX(key_pair, ed448_key, 1, NULL, DYNAMIC_TYPE_ED448,
            return MEMORY_E);

        if ((ret = wc_ed448_init_ex(key_pair, heap, INVALID_DEVID)) < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_ED448);
            return ret;
        }
        if ((ret = wc_Ed448PrivateKeyDecode(privKey, &keyIdx, key_pair,
                privKeySz)) == 0) {
            WOLFSSL_MSG("Checking ED448 key pair");
            keyIdx = 0;
            if ((ret = wc_ed448_import_public(pubKey, pubKeySz,
                    key_pair)) == 0) {
                /* public and private extracted successfully no check if is
                 * a pair and also do sanity checks on key. wc_ecc_check_key
                 * checks that private * base generator equals pubkey */
                if ((ret = wc_ed448_check_key(key_pair)) == 0) {
                    ret = 1;
                }
                else {
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ret);
        }
        wc_ed448_free(key_pair);
        WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_ED448);
    }
    else
    #endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT && !NO_ASN_CRYPT */
    #if defined(HAVE_FALCON)
    if ((ks == FALCON_LEVEL1k) || (ks == FALCON_LEVEL5k)) {
        WC_DECLARE_VAR(key_pair, falcon_key, 1, 0);
        word32     keyIdx = 0;

        WC_ALLOC_VAR_EX(key_pair, falcon_key, 1, NULL, DYNAMIC_TYPE_FALCON,
            return MEMORY_E);
        ret = wc_falcon_init(key_pair);
        if (ret  < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_FALCON);
            return ret;
        }

        if (ks == FALCON_LEVEL1k) {
            ret = wc_falcon_set_level(key_pair, 1);
        }
        else if (ks == FALCON_LEVEL5k) {
            ret = wc_falcon_set_level(key_pair, 5);
        }

        if (ret  < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_FALCON);
            return ret;
        }
        if ((ret = wc_Falcon_PrivateKeyDecode(privKey, &keyIdx, key_pair,
                                             privKeySz)) == 0) {
            WOLFSSL_MSG("Checking Falcon key pair");
            keyIdx = 0;
            if ((ret = wc_falcon_import_public(pubKey, pubKeySz,
                                               key_pair)) == 0) {
                /* Public and private extracted successfully. Sanity check. */
                if ((ret = wc_falcon_check_key(key_pair)) == 0) {
                    ret = 1;
                }
                else {
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ret);
        }
        wc_falcon_free(key_pair);
        WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_FALCON);
    }
    else
    #endif /* HAVE_FALCON */
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
    !defined(WOLFSSL_MLDSA_NO_VERIFY) && !defined(WOLFSSL_MLDSA_NO_ASN1)
    if ((ks == ML_DSA_44k) ||
        (ks == ML_DSA_65k) ||
        (ks == ML_DSA_87k)
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
     || (ks == DILITHIUM_LEVEL2k)
     || (ks == DILITHIUM_LEVEL3k)
     || (ks == DILITHIUM_LEVEL5k)
    #endif
        ) {
        WC_DECLARE_VAR(key_pair, wc_MlDsaKey, 1, 0);
        word32     keyIdx = 0;

        WC_ALLOC_VAR_EX(key_pair, wc_MlDsaKey, 1, NULL,
            DYNAMIC_TYPE_MLDSA, return MEMORY_E);
        ret = wc_MlDsaKey_Init(key_pair, NULL, INVALID_DEVID);
        if (ret  < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_MLDSA);
            return ret;
        }


        if (ks == ML_DSA_44k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_44);
        }
        else if (ks == ML_DSA_65k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_65);
        }
        else if (ks == ML_DSA_87k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_87);
        }
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        else if (ks == DILITHIUM_LEVEL2k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_44_DRAFT);
        }
        else if (ks == DILITHIUM_LEVEL3k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_65_DRAFT);
        }
        else if (ks == DILITHIUM_LEVEL5k) {
            ret = wc_MlDsaKey_SetParams(key_pair, WC_ML_DSA_87_DRAFT);
        }
    #endif

        if (ret  < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_MLDSA);
            return ret;
        }
        if ((ret = wc_MlDsaKey_PrivateKeyDecode(key_pair, privKey,
                                             privKeySz, &keyIdx)) == 0) {
            WOLFSSL_MSG("Checking ML-DSA key pair");
            keyIdx = 0;
            if ((ret = wc_MlDsaKey_ImportPubRaw(key_pair, pubKey,
                                               pubKeySz)) == 0) {
                /* Public and private extracted successfully. Sanity check. */
                if ((ret = wc_MlDsaKey_CheckKey(key_pair)) == 0)
                    ret = 1;
            }
        }
        wc_MlDsaKey_Free(key_pair);
        WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_MLDSA);
    }
    else
#endif /* WOLFSSL_HAVE_MLDSA && !WOLFSSL_MLDSA_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    if (wc_IsSlhDsaOid(ks)) {
        WC_DECLARE_VAR(key_pair, SlhDsaKey, 1, 0);
        word32     keyIdx = 0;
        int        slhDsaParam;

        WC_ALLOC_VAR_EX(key_pair, SlhDsaKey, 1, NULL, DYNAMIC_TYPE_SLHDSA,
            return MEMORY_E);

        slhDsaParam = wc_SlhDsaOidToParam(ks);

        if (slhDsaParam < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_SLHDSA);
            return NOT_COMPILED_IN;
        }

        ret = wc_SlhDsaKey_Init(key_pair, (enum SlhDsaParam)slhDsaParam,
                                NULL, INVALID_DEVID);
        if (ret  < 0) {
            WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_SLHDSA);
            return ret;
        }

        if ((ret = wc_SlhDsaKey_PrivateKeyDecode(privKey, &keyIdx, key_pair,
                                             privKeySz)) == 0) {
            WOLFSSL_MSG("Checking SLH-DSA key pair");
            keyIdx = 0;
            if ((ret = wc_SlhDsaKey_ImportPublic(key_pair, pubKey,
                                               pubKeySz)) == 0) {
                /* Public and private extracted successfully. Sanity check. */
                if ((ret = wc_SlhDsaKey_CheckKey(key_pair)) == 0)
                    ret = 1;
            }
        }
        wc_SlhDsaKey_Free(key_pair);
        WC_FREE_VAR_EX(key_pair, NULL, DYNAMIC_TYPE_SLHDSA);
    }
    else
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */
    {
        ret = 0;
    }
    (void)ks;
    (void)heap;

    return ret;
}

/* check that the private key is a pair for the public key in certificate
 * return 1 (true) on match
 * return 0 or negative value on failure/error
 *
 * key      : buffer holding DER format key
 * keySz    : size of key buffer
 * der      : a initialized and parsed DecodedCert holding a certificate
 * checkAlt : indicate if we check primary or alternative key
 */
int wc_CheckPrivateKeyCert(const byte* key, word32 keySz, DecodedCert* der,
                           int checkAlt, void* heap)
{
    int ret = 0;

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

#ifdef WOLFSSL_DUAL_ALG_CERTS
    if (checkAlt && der->sapkiDer != NULL) {
        /* We have to decode the public key first */
        /* Default to max pub key size. */
        word32 pubKeyLen = MAX_PUBLIC_KEY_SZ;
        byte* decodedPubKey = (byte*)XMALLOC(pubKeyLen, heap,
                                             DYNAMIC_TYPE_PUBLIC_KEY);
        if (decodedPubKey == NULL) {
            ret = MEMORY_E;
        }
        if (ret == 0) {
            if (der->sapkiOID == RSAk || der->sapkiOID == ECDSAk) {
                /* Simply copy the data */
                if ((word32)der->sapkiLen > pubKeyLen) {
                    ret = BUFFER_E;
                }
                else {
                    XMEMCPY(decodedPubKey, der->sapkiDer, der->sapkiLen);
                    pubKeyLen = der->sapkiLen;
                }
            }
            else {
            #if defined(WC_ENABLE_ASYM_KEY_IMPORT)
                word32 idx = 0;
                ret = DecodeAsymKeyPublic(der->sapkiDer, &idx, der->sapkiLen,
                                          decodedPubKey, &pubKeyLen,
                                          der->sapkiOID);
            #else
                ret = NOT_COMPILED_IN;
            #endif /* WC_ENABLE_ASYM_KEY_IMPORT */
            }
        }
        if (ret == 0) {
            ret = wc_CheckPrivateKey(key, keySz, decodedPubKey, pubKeyLen,
                                     (enum Key_Sum) der->sapkiOID, heap);
        }
        XFREE(decodedPubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY);
    }
    else
#endif
    {
        ret = wc_CheckPrivateKey(key, keySz, der->publicKey,
                der->pubKeySize, (enum Key_Sum) der->keyOID, heap);
    }

    (void)checkAlt;

    return ret;
}

#endif /* HAVE_PKCS12 || !NO_CHECK_PRIVATE_KEY */

#ifndef NO_PWDBASED

#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)
/* Check the PBE algorithm is supported and return wolfSSL id, version and block
 * size of encryption algorithm.
 *
 * When PBES2, version is PKCS5v2, CheckAlgoV2() must be called to get id and
 * blockSz based on encryption algorithm.
 *
 * @param [in]  first    First byte of OID to use in check.
 * @param [in]  second   Second byte of OID to use in check.
 * @param [out] id       wolfSSL id for PBE algorithm.
 * @param [out] version  Version of PBE OID:
 *                       PKCS12v1 (PBE), PKCS5 (PBES1), PKCS5v2 (PBES2).
 * @param [out] blockSz  Block size of encryption algorithm.
 * @return  0 on success.
 * @return  ALGO_ID_E when OID not supported.
 * @return  ASN_INPUT_E when first byte is invalid.
 */
static int CheckAlgo(int first, int second, int* id, int* version, int* blockSz)
{
    int ret = 0;

    (void)id;
    (void)blockSz;

    *version = -1;

    /* pkcs-12 1 = pkcs-12PbeIds */
    if (first == 1) {
        /* PKCS #12: Appendix C */
        switch (second) {
#if !defined(NO_SHA)
    #ifndef NO_RC4
        case PBE_SHA1_RC4_128:
            *id = PBE_SHA1_RC4_128;
            *version = PKCS12v1;
            if (blockSz != NULL) {
                *blockSz = 1;
            }
            break;
    #endif
    #ifndef NO_DES3
        case PBE_SHA1_DES3:
            *id = PBE_SHA1_DES3;
            *version = PKCS12v1;
            if (blockSz != NULL) {
                *blockSz = DES_BLOCK_SIZE;
            }
            break;
    #endif
    #ifdef WC_RC2
        case PBE_SHA1_40RC2_CBC:
            *id = PBE_SHA1_40RC2_CBC;
            *version = PKCS12v1;
            if (blockSz != NULL) {
                *blockSz = RC2_BLOCK_SIZE;
            }
            break;
    #endif
#endif /* !NO_SHA */
        default:
            ret = ALGO_ID_E;
            break;
        }
    }
    else if (first != PKCS5) {
        /* Bad OID. */
        ret = ASN_INPUT_E;
    }
    /* PKCS #5 PBES2: Appendix A.4
     * pkcs-5 13 = id-PBES2 */
    else if (second == PBES2) {
        *version = PKCS5v2;
        /* Id and block size come from CheckAlgoV2() */
    }
    else  {
        /* PKCS #5 PBES1: Appendix A.3 */
        /* see RFC 2898 for ids */
        switch (second) {
    #ifndef NO_DES3
        #ifndef NO_MD5
        case PBES1_MD5_DES:
            *id = PBE_MD5_DES;
            *version = PKCS5;
            if (blockSz != NULL) {
                *blockSz = DES_BLOCK_SIZE;
            }
            break;
        #endif
        #ifndef NO_SHA
        case PBES1_SHA1_DES:
            *id = PBE_SHA1_DES;
            *version = PKCS5;
            if (blockSz != NULL) {
                *blockSz = DES_BLOCK_SIZE;
            }
            break;
        #endif
    #endif /* !NO_DES3 */
        default:
            ret = ALGO_ID_E;
            break;
        }
    }

    /* Return error code. */
    return ret;
}

#endif /* HAVE_PKCS8 || HAVE_PKCS12 */

#ifdef HAVE_PKCS8

/* Check the encryption algorithm with PBES2 is supported and return block size
 * and wolfSSL id for the PBE.
 *
 * @param [in]  oid      Encryption algorithm OID id.
 * @param [out] id       wolfSSL id for PBE algorithm.
 * @param [out] version  Version of PBE OID:
 *                       PKCS12v1 (PBE), PKCS5 (PBES1), PKCS5v2 (PBES2).
 * @return  0 on success.
 * @return  ALGO_ID_E when encryption algorithm is not supported with PBES2.
 */
static int CheckAlgoV2(int oid, int* id, int* blockSz)
{
    int ret = 0;

    (void)id;
    (void)blockSz;

    switch (oid) {
#if !defined(NO_DES3) && !defined(NO_SHA)
    case DESb:
        *id = PBE_SHA1_DES;
        if (blockSz != NULL) {
            *blockSz = DES_BLOCK_SIZE;
        }
        break;
    case DES3b:
        *id = PBE_SHA1_DES3;
        if (blockSz != NULL) {
            *blockSz = DES_BLOCK_SIZE;
        }
        break;
#endif
#ifdef WOLFSSL_AES_256
    case AES256CBCb:
        *id = PBE_AES256_CBC;
        if (blockSz != NULL) {
            *blockSz = WC_AES_BLOCK_SIZE;
        }
        break;
#endif
#ifdef WOLFSSL_AES_128
    case AES128CBCb:
        *id = PBE_AES128_CBC;
        if (blockSz != NULL) {
            *blockSz = WC_AES_BLOCK_SIZE;
        }
        break;
#endif
    default:
        WOLFSSL_MSG("No PKCS v2 algo found");
        ret = ALGO_ID_E;
        break;
    }

    /* Return error code. */
    return ret;
}

#endif /* HAVE_PKCS8 */

#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)

int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, word32* oidSz,
        int* algoID, void* heap)
{
    word32 tmpIdx = 0;

    if (key == NULL || algoID == NULL)
        return BAD_FUNC_ARG;

    *algoID = 0;

    #if !defined(NO_RSA) && !defined(NO_ASN_CRYPT)
    {
        RsaKey *rsa = (RsaKey *)XMALLOC(sizeof *rsa, heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (rsa == NULL)
            return MEMORY_E;

        if (wc_InitRsaKey(rsa, heap) == 0) {
            if (wc_RsaPrivateKeyDecode(key, &tmpIdx, rsa, keySz) == 0) {
                *algoID = RSAk;
            }
            else {
                WOLFSSL_MSG("Not RSA DER key");
            }
            wc_FreeRsaKey(rsa);
        }
        XFREE(rsa, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
    #endif /* !NO_RSA && !NO_ASN_CRYPT */
    #if defined(HAVE_ECC) && !defined(NO_ASN_CRYPT)
    if (*algoID == 0) {
        ecc_key *ecc = (ecc_key *)XMALLOC(sizeof *ecc, heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (ecc == NULL)
            return MEMORY_E;

        tmpIdx = 0;
        if (wc_ecc_init_ex(ecc, heap, INVALID_DEVID) == 0) {
            if (wc_EccPrivateKeyDecode(key, &tmpIdx, ecc, keySz) == 0) {
                *algoID = ECDSAk;

                /* now find oid */
                if (wc_ecc_get_oid(ecc->dp->oidSum, curveOID, oidSz) < 0) {
                    WOLFSSL_MSG("Error getting ECC curve OID");
                    wc_ecc_free(ecc);
                    XFREE(ecc, heap, DYNAMIC_TYPE_TMP_BUFFER);
                    return BAD_FUNC_ARG;
                }
            }
            else {
                WOLFSSL_MSG("Not ECC DER key either");
            }
            wc_ecc_free(ecc);
        }
        XFREE(ecc, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* HAVE_ECC && !NO_ASN_CRYPT */
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) && !defined(NO_ASN_CRYPT)
    if (*algoID == 0) {
        ed25519_key *ed25519 = (ed25519_key *)XMALLOC(sizeof *ed25519, heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (ed25519 == NULL)
            return MEMORY_E;

        tmpIdx = 0;
        if (wc_ed25519_init_ex(ed25519, heap, INVALID_DEVID) == 0) {
            if (wc_Ed25519PrivateKeyDecode(key, &tmpIdx, ed25519, keySz) == 0) {
                *algoID = ED25519k;
            }
            else {
                WOLFSSL_MSG("Not ED25519 DER key");
            }
            wc_ed25519_free(ed25519);
        }
        else {
            WOLFSSL_MSG("GetKeyOID wc_ed25519_init failed");
        }
        XFREE(ed25519, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT && !NO_ASN_CRYPT */
#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) && !defined(NO_ASN_CRYPT)
    if (*algoID == 0) {
        ed448_key *ed448 = (ed448_key *)XMALLOC(sizeof *ed448, heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (ed448 == NULL)
            return MEMORY_E;

        tmpIdx = 0;
        if (wc_ed448_init(ed448) == 0) {
            if (wc_Ed448PrivateKeyDecode(key, &tmpIdx, ed448, keySz) == 0) {
                *algoID = ED448k;
            }
            else {
                WOLFSSL_MSG("Not ED448 DER key");
            }
            wc_ed448_free(ed448);
        }
        else {
            WOLFSSL_MSG("GetKeyOID wc_ed448_init failed");
        }
        XFREE(ed448, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT && !NO_ASN_CRYPT */
#if defined(HAVE_FALCON)
    if (*algoID == 0) {
        falcon_key *falcon = (falcon_key *)XMALLOC(sizeof(*falcon), heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (falcon == NULL)
            return MEMORY_E;

        if (wc_falcon_init(falcon) == 0) {
            if ((*algoID == 0) && (wc_falcon_set_level(falcon, 1) == 0)) {
                tmpIdx = 0;
                if (wc_Falcon_PrivateKeyDecode(key, &tmpIdx, falcon, keySz)
                    == 0) {
                    *algoID = FALCON_LEVEL1k;
                }
                else {
                    WOLFSSL_MSG("Not Falcon Level 1 DER key");
                }
            }
            if ((*algoID == 0) && (wc_falcon_set_level(falcon, 5) == 0)) {
                tmpIdx = 0;
                if (wc_Falcon_PrivateKeyDecode(key, &tmpIdx, falcon, keySz)
                    == 0) {
                    *algoID = FALCON_LEVEL5k;
                }
                else {
                    WOLFSSL_MSG("Not Falcon Level 5 DER key");
                }
            }
            if (*algoID == 0) {
                WOLFSSL_MSG("GetKeyOID could not match Falcon DER key");
            }
            wc_falcon_free(falcon);
        }
        XFREE(falcon, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* HAVE_FALCON */
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
    !defined(WOLFSSL_MLDSA_NO_VERIFY) && !defined(WOLFSSL_MLDSA_NO_ASN1)
    if (*algoID == 0) {
        wc_MlDsaKey *mldsa = (wc_MlDsaKey *)XMALLOC(sizeof(*mldsa),
             heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (mldsa == NULL)
            return MEMORY_E;

        /* wc_MlDsaKey_Init() returns 0 on success and a non-zero value on
         * failure. */
        if (wc_MlDsaKey_Init(mldsa, NULL, INVALID_DEVID) == 0) {
            if ((*algoID == 0) &&
                (wc_MlDsaKey_SetParams(mldsa, WC_ML_DSA_44) == 0)) {
                tmpIdx = 0;
                if (wc_MlDsaKey_PrivateKeyDecode(mldsa, key, keySz,
                        &tmpIdx) == 0) {
                    *algoID = ML_DSA_44k;
                }
                else {
                    WOLFSSL_MSG("Not ML-DSA-44 DER key");
                }
            }
            if ((*algoID == 0) &&
                (wc_MlDsaKey_SetParams(mldsa, WC_ML_DSA_65) == 0)) {
                tmpIdx = 0;
                if (wc_MlDsaKey_PrivateKeyDecode(mldsa, key, keySz,
                        &tmpIdx) == 0) {
                    *algoID = ML_DSA_65k;
                }
                else {
                    WOLFSSL_MSG("Not ML-DSA-65 DER key");
                }
            }
            if ((*algoID == 0) &&
                (wc_MlDsaKey_SetParams(mldsa, WC_ML_DSA_87) == 0)) {
                tmpIdx = 0;
                if (wc_MlDsaKey_PrivateKeyDecode(mldsa, key, keySz,
                        &tmpIdx) == 0) {
                    *algoID = ML_DSA_87k;
                }
                else {
                    WOLFSSL_MSG("Not ML-DSA-87 DER key");
                }
            }
            if (*algoID == 0) {
                WOLFSSL_MSG("GetKeyOID ML-DSA initialization failed");
            }
            wc_MlDsaKey_Free(mldsa);
        }
        XFREE(mldsa, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* WOLFSSL_HAVE_MLDSA && !WOLFSSL_MLDSA_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    if (*algoID == 0) {
        enum SlhDsaParam placeholder;
        SlhDsaKey *slhDsa = (SlhDsaKey *)XMALLOC(sizeof(*slhDsa),
             heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (slhDsa == NULL)
            return MEMORY_E;

        /* wc_SlhDsaKey_PrivateKeyDecode auto-detects the parameter set from
         * the OID in the DER encoding, so a single call handles all twelve
         * SLH-DSA variants. The initial parameter is only a placeholder;
         * it is overwritten by the decoder. Pick whichever variant is
         * compiled in so wc_SlhDsaKey_Init does not fail with
         * NOT_COMPILED_IN when a specific variant (like 128F) is disabled. */
    #if defined(WOLFSSL_SLHDSA_PARAM_128F)
        placeholder = SLHDSA_SHAKE128F;
    #elif defined(WOLFSSL_SLHDSA_PARAM_128S)
        placeholder = SLHDSA_SHAKE128S;
    #elif defined(WOLFSSL_SLHDSA_PARAM_192F)
        placeholder = SLHDSA_SHAKE192F;
    #elif defined(WOLFSSL_SLHDSA_PARAM_192S)
        placeholder = SLHDSA_SHAKE192S;
    #elif defined(WOLFSSL_SLHDSA_PARAM_256F)
        placeholder = SLHDSA_SHAKE256F;
    #elif defined(WOLFSSL_SLHDSA_PARAM_256S)
        placeholder = SLHDSA_SHAKE256S;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
        placeholder = SLHDSA_SHA2_128F;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
        placeholder = SLHDSA_SHA2_128S;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
        placeholder = SLHDSA_SHA2_192F;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
        placeholder = SLHDSA_SHA2_192S;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
        placeholder = SLHDSA_SHA2_256F;
    #elif defined(WOLFSSL_SLHDSA_SHA2) && \
          defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
        placeholder = SLHDSA_SHA2_256S;
    #else
        #error "WOLFSSL_HAVE_SLHDSA requires at least one parameter set"
    #endif
        if (wc_SlhDsaKey_Init(slhDsa, placeholder, NULL, INVALID_DEVID) == 0) {
            tmpIdx = 0;
            if (wc_SlhDsaKey_PrivateKeyDecode(key, &tmpIdx, slhDsa,
                    keySz) == 0) {
                switch (slhDsa->params->param) {
                    case SLHDSA_SHAKE128S:
                        *algoID = SLH_DSA_SHAKE_128Sk;
                        break;
                    case SLHDSA_SHAKE128F:
                        *algoID = SLH_DSA_SHAKE_128Fk;
                        break;
                    case SLHDSA_SHAKE192S:
                        *algoID = SLH_DSA_SHAKE_192Sk;
                        break;
                    case SLHDSA_SHAKE192F:
                        *algoID = SLH_DSA_SHAKE_192Fk;
                        break;
                    case SLHDSA_SHAKE256S:
                        *algoID = SLH_DSA_SHAKE_256Sk;
                        break;
                    case SLHDSA_SHAKE256F:
                        *algoID = SLH_DSA_SHAKE_256Fk;
                        break;
                #ifdef WOLFSSL_SLHDSA_SHA2
                    case SLHDSA_SHA2_128S:
                        *algoID = SLH_DSA_SHA2_128Sk;
                        break;
                    case SLHDSA_SHA2_128F:
                        *algoID = SLH_DSA_SHA2_128Fk;
                        break;
                    case SLHDSA_SHA2_192S:
                        *algoID = SLH_DSA_SHA2_192Sk;
                        break;
                    case SLHDSA_SHA2_192F:
                        *algoID = SLH_DSA_SHA2_192Fk;
                        break;
                    case SLHDSA_SHA2_256S:
                        *algoID = SLH_DSA_SHA2_256Sk;
                        break;
                    case SLHDSA_SHA2_256F:
                        *algoID = SLH_DSA_SHA2_256Fk;
                        break;
                #endif
                    default:
                        WOLFSSL_MSG("Unexpected SLH-DSA parameter set");
                        break;
                }
            }
            else {
                WOLFSSL_MSG("Not an SLH-DSA DER key");
            }
            wc_SlhDsaKey_Free(slhDsa);
        }
        else {
            WOLFSSL_MSG("GetKeyOID SLH-DSA initialization failed");
        }
        XFREE(slhDsa, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */

    /* if flag is not set then this is not a key that we understand. */
    if (*algoID == 0) {
        WOLFSSL_MSG("Bad key DER or compile options");
        return BAD_FUNC_ARG;
    }

    (void)tmpIdx;
    (void)curveOID;
    (void)oidSz;
    (void)keySz;
    (void)heap;

    return 1;
}

#endif /* HAVE_PKCS8 || HAVE_PKCS12 */

#ifdef WOLFSSL_ASN_TEMPLATE
#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)
/* ASN.1 template for PBES2 parameters.
 * PKCS #5: RFC 8018, A.4 - PBES2-params without outer SEQUENCE
 *                    A.2 - PBKDF2-params
 *                    B.2 - Encryption schemes
 *                    C   - AlgorithmIdentifier
 */
static const ASNItem pbes2ParamsASN[] = {
/* KDF_SEQ                */ { 0, ASN_SEQUENCE, 1, 1, 0 },
               /* PBKDF2 */
/* KDF_OID                */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* PBKDF2_PARAMS_SEQ      */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                   /* Salt */
/* PBKDF2_PARAMS_SALT     */         { 2, ASN_OCTET_STRING, 0, 0, 0 },
                   /* Iteration count */
/* PBKDF2_PARAMS_ITER     */         { 2, ASN_INTEGER, 0, 0, 0 },
                   /* Key length */
/* PBKDF2_PARAMS_KEYLEN   */         { 2, ASN_INTEGER, 0, 0, 1 },
                   /* PRF - default is HMAC-SHA1 */
/* PBKDF2_PARAMS_PRF      */         { 2, ASN_SEQUENCE, 1, 1, 1 },
/* PBKDF2_PARAMS_PRF_OID  */             { 3, ASN_OBJECT_ID, 0, 0, 0 },
/* PBKDF2_PARAMS_PRF_NULL */             { 3, ASN_TAG_NULL, 0, 0, 1 },
/* ENCS_SEQ               */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                   /* Encryption algorithm */
/* ENCS_OID               */   { 1, ASN_OBJECT_ID, 0, 0, 0 },
                   /* IV for CBC */
/* ENCS_PARAMS            */   { 1, ASN_OCTET_STRING, 0, 0, 0 },
};
enum {
    PBES2PARAMSASN_IDX_KDF_SEQ = 0,
    PBES2PARAMSASN_IDX_KDF_OID,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_SEQ,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_SALT,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_ITER,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_KEYLEN,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_PRF,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_PRF_OID,
    PBES2PARAMSASN_IDX_PBKDF2_PARAMS_PRF_NULL,
    PBES2PARAMSASN_IDX_ENCS_SEQ,
    PBES2PARAMSASN_IDX_ENCS_OID,
    PBES2PARAMSASN_IDX_ENCS_PARAMS
};

/* Number of items in ASN.1 template for PBES2 parameters. */
#define pbes2ParamsASN_Length (sizeof(pbes2ParamsASN) / sizeof(ASNItem))

/* ASN.1 template for PBES1 parameters.
 * PKCS #5: RFC 8018, A.3. - PBEParameter without outer SEQUENCE
 */
static const ASNItem pbes1ParamsASN[] = {
            /* Salt */
/* SALT */    { 0, ASN_OCTET_STRING, 0, 0, 0 },
            /* Iteration count */
/* ITER */    { 0, ASN_INTEGER, 0, 0, 0 },
};
enum {
    PBES1PARAMSASN_IDX_SALT = 0,
    PBES1PARAMSASN_IDX_ITER
};

/* Number of items in ASN.1 template for PBES1 parameters. */
#define pbes1ParamsASN_Length (sizeof(pbes1ParamsASN) / sizeof(ASNItem))
#endif /* HAVE_PKCS8 || HAVE_PKCS12 */
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef HAVE_PKCS8

/*
 * Equivalent to calling TraditionalEnc with the same parameters but with
 * encAlgId set to 0. This function must be kept alive because it's sometimes
 * part of the API (WOLFSSL_ASN_API).
 */
int UnTraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int vAlgo,
        byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap)
{
    return TraditionalEnc(key, keySz, out, outSz, password, passwordSz,
                vPKCS, vAlgo, 0, salt, saltSz, itt, rng, heap);
}

static int GetAlgoV2(int encAlgId, const byte** oid, int *len, int* id,
                     int *blkSz)
{
    int ret = 0;

    switch (encAlgId) {
#if !defined(NO_DES3) && !defined(NO_SHA)
    case DESb:
        *len = sizeof(blkDesCbcOid);
        *oid = blkDesCbcOid;
        *id = PBE_SHA1_DES;
        *blkSz = 8;
        break;
    case DES3b:
        *len = sizeof(blkDes3CbcOid);
        *oid = blkDes3CbcOid;
        *id = PBE_SHA1_DES3;
        *blkSz = 8;
        break;
#endif
#if defined(WOLFSSL_AES_128) && defined(HAVE_AES_CBC)
    case AES128CBCb:
        *len = sizeof(blkAes128CbcOid);
        *oid = blkAes128CbcOid;
        *id = PBE_AES128_CBC;
        *blkSz = 16;
        break;
#endif
#if defined(WOLFSSL_AES_256) && defined(HAVE_AES_CBC)
    case AES256CBCb:
        *len = sizeof(blkAes256CbcOid);
        *oid = blkAes256CbcOid;
        *id = PBE_AES256_CBC;
        *blkSz = 16;
        break;
#endif
    default:
        (void)len;
        (void)oid;
        (void)id;
        (void)blkSz;
        ret = ALGO_ID_E;
    }

    return ret;
}

int wc_EncryptPKCS8Key_ex(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int pbeOid,
        int encAlgId, byte* salt, word32 saltSz, int itt, int hmacOid,
        WC_RNG* rng, void* heap)
{
    WC_DECLARE_VAR(saltTmp, byte, MAX_SALT_SIZE, 0);
    int genSalt = 0;
    int ret = 0;
    int version = 0;
    int pbeId = 0;
    int blockSz = 0;
    const byte* encOid = NULL;
    int encOidSz = 0;
    word32 padSz = 0;
    word32 innerLen = 0;
    const byte* pbeOidBuf = NULL;
    word32 pbeOidBufSz = 0;
    word32 pbeLen = 0;
    word32 kdfLen = 0;
    word32 encLen = 0;
    byte cbcIv[MAX_IV_SIZE];
    word32 idx = 0;
    word32 encIdx = 0;
    const byte* hmacOidBuf = NULL;
    word32 hmacOidBufSz = 0;
    byte tmpShort[MAX_SHORT_SZ];
    word32 tmpIdx = 0;

    (void)heap;

    WOLFSSL_ENTER("wc_EncryptPKCS8Key_ex");

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

    if (ret == 0) {
        ret = CheckAlgo(vPKCS, pbeOid, &pbeId, &version, &blockSz);
    }
    if (ret == 0 && (salt == NULL || saltSz == 0)) {
        genSalt = 1;
        saltSz = 8;
    }
    if (ret == 0 && version == PKCS5v2) {
        ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz);
    }
    if (ret == 0) {
        padSz = (word32)((blockSz - ((int)keySz & (blockSz - 1))) &
            (blockSz - 1));
        ret = SetShortInt(tmpShort, &tmpIdx, (word32)itt, MAX_SHORT_SZ);
        if (ret > 0) {
            /* inner = OCT salt INT itt */
            innerLen = 2 + saltSz + (word32)ret;
            ret = 0;
        }
    }
    if (ret == 0) {
        if (version != PKCS5v2) {
            pbeOidBuf = OidFromId((word32)pbeId, oidPBEType, &pbeOidBufSz);
            /* pbe = OBJ pbse1 SEQ [ inner ] */
            pbeLen = 2 + pbeOidBufSz + 2 + innerLen;
        }
        else {
            if (hmacOid > 0) {
                hmacOidBuf = OidFromId((word32)hmacOid, oidHmacType,
                                &hmacOidBufSz);
                innerLen += 2 + 2 + hmacOidBufSz;
            }
            pbeOidBuf = pbes2;
            pbeOidBufSz = sizeof(pbes2);
            /* kdf = OBJ pbkdf2 [ SEQ innerLen ] */
            kdfLen = 2U + (word32)sizeof(pbkdf2Oid) + 2U + innerLen;
            /* enc = OBJ enc_alg OCT iv */
            encLen = 2U + (word32)encOidSz + 2U + (word32)blockSz;
            /* pbe = OBJ pbse2 SEQ [ SEQ [ kdf ] SEQ [ enc ] ] */
            pbeLen = 2U + (word32)sizeof(pbes2) + 2U + 2U + kdfLen + 2U +
                encLen;

            ret = wc_RNG_GenerateBlock(rng, cbcIv, (word32)blockSz);
        }
    }
    if (ret == 0) {
        /* outerLen = length of PBE encoding + octet string data */
        /* Plus 2 for tag and length for pbe */
        word32 outerLen = 2 + pbeLen;
        /* Octet string tag, length */
        outerLen += 1 + SetLength(keySz + padSz, NULL);
        /* Octet string bytes */
        outerLen += keySz + padSz;
        if (out == NULL) {
            /* Sequence tag, length */
            *outSz = 1 + SetLength(outerLen, NULL) + outerLen;
            return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
        }
        SetOctetString(keySz + padSz, out);

        idx += SetSequence(outerLen, out + idx);

        encIdx = idx + outerLen - keySz - padSz;
        /* Put Encrypted content in place. */
        XMEMCPY(out + encIdx, key, keySz);
        if (padSz > 0) {
            XMEMSET(out + encIdx + keySz, (int)padSz, padSz);
            keySz += padSz;
        }

        if (genSalt == 1) {
            WC_ALLOC_VAR_EX(saltTmp, byte, saltSz, heap,
                DYNAMIC_TYPE_TMP_BUFFER, ret=MEMORY_E);
            if (WC_VAR_OK(saltTmp))
            {
                salt = saltTmp;
                if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
                    WOLFSSL_MSG("Error generating random salt");
                }
            }
        }
    }
    if (ret == 0) {
        ret = wc_CryptKey(password, passwordSz, salt, (int)saltSz, itt, pbeId,
                  out + encIdx, (int)keySz, version, cbcIv, 1, hmacOid);
    }
    if (ret == 0) {
        if (version != PKCS5v2) {
            /* PBE algorithm */
            idx += SetSequence(pbeLen, out + idx);
            idx += (word32)SetObjectId((int)pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
        }
        else {
            /* PBES2 algorithm identifier */
            idx += SetSequence(pbeLen, out + idx);
            idx += (word32)SetObjectId((int)pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
            /* PBES2 Parameters: SEQ [ kdf ] SEQ [ enc ] */
            idx += SetSequence(2 + kdfLen + 2 + encLen, out + idx);
            /* KDF Algorithm Identifier */
            idx += SetSequence(kdfLen, out + idx);
            idx += (word32)SetObjectId((int)sizeof(pbkdf2Oid), out + idx);
            XMEMCPY(out + idx, pbkdf2Oid, sizeof(pbkdf2Oid));
            idx += (word32)sizeof(pbkdf2Oid);
        }
        idx += SetSequence(innerLen, out + idx);
        idx += SetOctetString(saltSz, out + idx);
        XMEMCPY(out + idx, salt, saltSz); idx += saltSz;
        ret = SetShortInt(out, &idx, (word32)itt, *outSz);
        if (ret > 0)
            ret = 0;
        if (version == PKCS5v2) {
            if (hmacOid > 0) {
                idx += SetSequence(2+hmacOidBufSz, out + idx);
                idx += (word32)SetObjectId((int)hmacOidBufSz, out + idx);
                XMEMCPY(out + idx, hmacOidBuf, hmacOidBufSz);
                idx += (word32)hmacOidBufSz;
            }
        }
    }
    if (ret == 0) {
        if (version == PKCS5v2) {
            /* Encryption Algorithm Identifier */
            idx += SetSequence(encLen, out + idx);
            idx += (word32)SetObjectId(encOidSz, out + idx);
            XMEMCPY(out + idx, encOid, (size_t)encOidSz);
            idx += (word32)encOidSz;
            /* Encryption Algorithm Parameter: CBC IV */
            idx += SetOctetString((word32)blockSz, out + idx);
            XMEMCPY(out + idx, cbcIv, (size_t)blockSz);
            idx += (word32)blockSz;
        }
        idx += SetOctetString(keySz, out + idx);
        /* Default PRF - no need to write out OID */
        idx += keySz;

        ret = (int)idx;
    }

    WC_FREE_VAR_EX(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);

    WOLFSSL_LEAVE("wc_EncryptPKCS8Key_ex", ret);

    return ret;
}

int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int pbeOid,
        int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng,
        void* heap)
{
    return wc_EncryptPKCS8Key_ex(key, keySz, out, outSz, password, passwordSz,
                vPKCS, pbeOid, encAlgId, salt, saltSz, itt, 0, rng, heap);
}

int wc_DecryptPKCS8Key(byte* input, word32 sz, const char* password,
        int passwordSz)
{
    int ret;
    int length;
    word32 inOutIdx = 0;

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

    if (GetSequence(input, &inOutIdx, &length, sz) < 0) {
        ret = ASN_PARSE_E;
    }
    else {
        ret = DecryptContent(input + inOutIdx, sz - inOutIdx, password,
                passwordSz);
        if (ret > 0) {
            XMEMMOVE(input, input + inOutIdx, (size_t)ret);
        }
    }

    if (ret > 0) {
        /* DecryptContent will decrypt the data, but it will leave any padding
         * bytes intact. This code calculates the length without the padding
         * and we return that to the user. */
        inOutIdx = 0;
        if (GetSequence(input, &inOutIdx, &length, (word32)ret) < 0) {
            ret = ASN_PARSE_E;
        }
        else {
            ret = (int)inOutIdx + length;
        }
    }

    return ret;
}

/* Takes an unencrypted, traditional DER-encoded key and converts it to a PKCS#8
 * encrypted key. If out is not NULL, it will hold the encrypted key. If it's
 * NULL, LENGTH_ONLY_E will be returned and outSz will have the required out
 * buffer size. */
int TraditionalEnc_ex(byte* key, word32 keySz, 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)
{
    int ret = 0;
    byte *pkcs8Key = NULL;
    word32 pkcs8KeySz = 0;
    int algId = 0;
    const byte* curveOid = NULL;
    word32 curveOidSz = 0;

    if (ret == 0) {
        /* check key type and get OID if ECC */
        ret = wc_GetKeyOID(key, keySz, &curveOid, &curveOidSz, &algId, heap);
        if (ret == 1)
            ret = 0;
    }
    if (ret == 0) {
        ret = wc_CreatePKCS8Key(NULL, &pkcs8KeySz, key, keySz, algId, curveOid,
                                                                    curveOidSz);
        if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E))
            ret = 0;
    }
    if (ret == 0) {
        pkcs8Key = (byte*)XMALLOC(pkcs8KeySz, heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (pkcs8Key == NULL)
            ret = MEMORY_E;
    }
    if (ret == 0) {
        ret = wc_CreatePKCS8Key(pkcs8Key, &pkcs8KeySz, key, keySz, algId,
            curveOid, curveOidSz);
        if (ret >= 0) {
            pkcs8KeySz = (word32)ret;
            ret = 0;
        }
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    if (ret == 0) {
        wc_MemZero_Add("TraditionalEnc pkcs8Key", pkcs8Key, pkcs8KeySz);
    }
#endif
    if (ret == 0) {
        ret = wc_EncryptPKCS8Key_ex(pkcs8Key, pkcs8KeySz, out, outSz, password,
            passwordSz, vPKCS, vAlgo, encAlgId, salt, saltSz, itt, hmacOid, rng,
            heap);
    }

    if (pkcs8Key != NULL) {
        ForceZero(pkcs8Key, pkcs8KeySz);
        XFREE(pkcs8Key, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }

    (void)rng;

    return ret;
}

/* Takes an unencrypted, traditional DER-encoded key and converts it to a PKCS#8
 * encrypted key. If out is not NULL, it will hold the encrypted key. If it's
 * NULL, LENGTH_ONLY_E will be returned and outSz will have the required out
 * buffer size. */
int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int vAlgo,
        int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng,
        void* heap)
{
    return TraditionalEnc_ex(key, keySz, out, outSz, password, passwordSz,
                vPKCS, vAlgo, encAlgId, salt, saltSz, itt, 0, rng, heap);

}

/* Same as TraditionalEnc, but in the public API. */
int wc_CreateEncryptedPKCS8Key(byte* key, word32 keySz, byte* out,
        word32* outSz, const char* password, int passwordSz, int vPKCS,
        int pbeOid, int encAlgId, byte* salt, word32 saltSz, int itt,
        WC_RNG* rng, void* heap)
{
    return TraditionalEnc(key, keySz, out, outSz, password, passwordSz, vPKCS,
        pbeOid, encAlgId, salt, saltSz, itt, rng, heap);
}


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for PKCS #8/#7 encrypted key for decrypting
 * PKCS #8: RFC 5958, 3 - EncryptedPrivateKeyInfo without outer SEQUENCE
 * PKCS #7: RFC 2315, 10.1 - EncryptedContentInfo without outer SEQUENCE
 */
static const ASNItem pkcs8DecASN[] = {
/* ENCALGO_SEQ    */ { 1, ASN_SEQUENCE, 1, 1, 0 },
/* ENCALGO_OID    */     { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* ENCALGO_PARAMS */     { 2, ASN_SEQUENCE, 1, 0, 0 },
            /* PKCS #7 */
/* ENCCONTENT     */ { 1, ASN_CONTEXT_SPECIFIC | ASN_ENC_CONTENT,
                                       0, 0, 2 },
            /* PKCS #8 */
/* ENCDATA        */ { 1, ASN_OCTET_STRING, 0, 0, 2 },
};
enum {
    PKCS8DECASN_IDX_ENCALGO_SEQ = 0,
    PKCS8DECASN_IDX_ENCALGO_OID,
    PKCS8DECASN_IDX_ENCALGO_PARAMS,
    PKCS8DECASN_IDX_ENCCONTENT,
    PKCS8DECASN_IDX_ENCDATA
};

/* Number of items in ASN.1 template for PKCS #8/#7 encrypted key. */
#define pkcs8DecASN_Length (sizeof(pkcs8DecASN) / sizeof(ASNItem))
#endif

/* Decrypt data using PBE algorithm.
 *
 * PKCS #8: RFC 5958, 3 - EncryptedPrivateKeyInfo without outer SEQUENCE
 * PKCS #7: RFC 2315, 10.1 - EncryptedContentInfo without outer SEQUENCE
 *
 * Note: input buffer is overwritten with decrypted data!
 *
 * Salt is in KDF parameters and IV is PBE parameters when needed.
 *
 * @param [in] input       Data to decrypt and unwrap.
 * @param [in] sz          Size of encrypted data.
 * @param [in] password    Password to derive encryption key with.
 * @param [in] passwordSz  Size of password in bytes.
 * @return  Length of decrypted data on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  Other when decryption fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecryptContent(byte* input, word32 sz, const char* password, int passwordSz)
{
    /* pbes2ParamsASN longer than pkcs8DecASN_Length/pbes1ParamsASN_Length. */
    DECL_ASNGETDATA(dataASN, pbes2ParamsASN_Length);
    int    ret = 0;
    int    id = 0;
    int    version = 0;
    word32 idx = 0;
    word32 pIdx = 0;
    word32 iterations = 0;
    word32 keySz = 0;
    word32 saltSz = 0;
    word32 shaOid = 0;
    const byte* salt = NULL;
    const byte* key = NULL;
    byte   cbcIv[MAX_IV_SIZE];
    const byte* params = NULL;

    WOLFSSL_ENTER("DecryptContent");

    CALLOC_ASNGETDATA(dataASN, pbes2ParamsASN_Length, ret, NULL);

    if (ret == 0) {
        /* Check OID is a PBE Type */
        GetASN_OID(&dataASN[PKCS8DECASN_IDX_ENCALGO_OID], oidPBEType);
        ret = GetASN_Items(pkcs8DecASN, dataASN, pkcs8DecASN_Length, 0, input,
                           &idx, sz);
    }
    if (ret == 0) {
        /* Check the PBE algorithm and get the version and id. */
        idx = dataASN[PKCS8DECASN_IDX_ENCALGO_OID].data.oid.length;
        /* All supported PBE algorithms are 9 or 10 bytes long. */
        if ((idx != 9) && (idx != 10)) {
            ret = ASN_UNKNOWN_OID_E;
        }
    }
    if (ret == 0) {
        /* Second last byte: 1 (PKCS #12 PBE Id) or 5 (PKCS #5)
         * Last byte: Alg or PBES2 */
        ret = CheckAlgo(
            dataASN[PKCS8DECASN_IDX_ENCALGO_OID].data.oid.data[idx - 2],
            dataASN[PKCS8DECASN_IDX_ENCALGO_OID].data.oid.data[idx - 1], &id,
            &version, NULL);
    }
    if (ret == 0) {
        /* Get the parameters data. */
        GetASN_GetRef(&dataASN[PKCS8DECASN_IDX_ENCALGO_PARAMS], &params, &sz);
        /* Having a numbered choice means none or both will have errored out. */
        if (dataASN[PKCS8DECASN_IDX_ENCCONTENT].tag != 0)
            GetASN_GetRef(&dataASN[PKCS8DECASN_IDX_ENCCONTENT], &key, &keySz);
        else if (dataASN[PKCS8DECASN_IDX_ENCDATA].tag != 0)
            GetASN_GetRef(&dataASN[PKCS8DECASN_IDX_ENCDATA], &key, &keySz);
        else
            ret = ASN_RSA_KEY_E;
    }

    if (ret == 0) {
        if (version != PKCS5v2) {
            /* Initialize for PBES1 parameters and put iterations in var. */
            XMEMSET(dataASN, 0, sizeof(*dataASN) * pbes1ParamsASN_Length);
            GetASN_Int32Bit(&dataASN[PBES1PARAMSASN_IDX_ITER], &iterations);
            /* Parse the PBES1 parameters. */
            ret = GetASN_Items(pbes1ParamsASN, dataASN, pbes1ParamsASN_Length,
                               0, params, &pIdx, sz);
            if (ret == 0) {
                /* Get the salt data. */
                GetASN_GetRef(&dataASN[PBES1PARAMSASN_IDX_SALT], &salt,
                    &saltSz);
            }
        }
        else {
            word32 ivSz = MAX_IV_SIZE;

            /* Initialize for PBES2 parameters. Put iterations in var; match
             * KDF, HMAC and cipher, and copy CBC into buffer. */
            XMEMSET(dataASN, 0, sizeof(*dataASN) * pbes2ParamsASN_Length);
            GetASN_ExpBuffer(&dataASN[PBES2PARAMSASN_IDX_KDF_OID], pbkdf2Oid,
                sizeof(pbkdf2Oid));
            GetASN_Int32Bit(&dataASN[PBES2PARAMSASN_IDX_PBKDF2_PARAMS_ITER],
                &iterations);
            GetASN_OID(&dataASN[PBES2PARAMSASN_IDX_PBKDF2_PARAMS_PRF_OID],
                oidHmacType);
            GetASN_OID(&dataASN[PBES2PARAMSASN_IDX_ENCS_OID], oidBlkType);
            GetASN_Buffer(&dataASN[PBES2PARAMSASN_IDX_ENCS_PARAMS], cbcIv,
                &ivSz);
            /* Parse the PBES2 parameters  */
            ret = GetASN_Items(pbes2ParamsASN, dataASN, pbes2ParamsASN_Length,
                               0, params, &pIdx, sz);
            if (ret == 0) {
                /* Get the salt data. */
                GetASN_GetRef(&dataASN[PBES2PARAMSASN_IDX_PBKDF2_PARAMS_SALT],
                    &salt, &saltSz);
                /* Get the digest and encryption algorithm id. */
                shaOid = dataASN[PBES2PARAMSASN_IDX_PBKDF2_PARAMS_PRF_OID].data.oid.sum; /* Default HMAC-SHA1 */
                id     = (int)dataASN[PBES2PARAMSASN_IDX_ENCS_OID].data.oid.sum;
                /* Convert encryption algorithm to a PBE algorithm if needed. */
                CheckAlgoV2(id, &id, NULL);
            }
        }
    }

    if (ret == 0) {
        /* Decrypt the key. */
        /* safe cast -- key is actually inside the readwrite "input" buffer. */
        ret = wc_CryptKey(
            password, passwordSz, salt, (int)saltSz, (int)iterations, id,
            (byte *)(wc_ptr_t)key, (int)keySz, version, cbcIv, 0, (int)shaOid);
    }
    if (ret == 0) {
        /* Copy the decrypted key into the input (inline). */
        XMEMMOVE(input, key, keySz);
        ret = (int)keySz;
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decrypt data using PBE algorithm and get key from PKCS#8 wrapping.
 *
 * PKCS #8: RFC 5958, 3 - EncryptedPrivateKeyInfo
 * PKCS #7: RFC 2315, 10.1 - EncryptedContentInfo
 *
 * Note: input buffer is overwritten with decrypted key!
 *
 * Salt is in KDF parameters and IV is PBE parameters when needed.
 *
 * @param [in]  input       Data to decrypt and unwrap.
 * @param [in]  sz          Size of encrypted data.
 * @param [in]  password    Password to derive encryption key with.
 * @param [in]  passwordSz  Size of password in bytes.
 * @param [out] algId       Key algorithm from PKCS#8 wrapper.
 * @return  Length of decrypted data on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  Other when decryption fails.
 */
int ToTraditionalEnc(byte* input, word32 sz, const char* password,
                     int passwordSz, word32* algId)
{
    int ret;

    ret = wc_DecryptPKCS8Key(input, sz, password, passwordSz);
    if (ret > 0) {
        ret = ToTraditional_ex(input, (word32)ret, algId);
    }

    return ret;
}

#endif /* HAVE_PKCS8 */

#ifdef HAVE_PKCS12

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for PKCS #8 encrypted key with PBES2 parameters.
 * PKCS #8: RFC 5958, 3 - EncryptedPrivateKeyInfo
 * PKCS #5: RFC 8018, A.4 - PBES2
 */
static const ASNItem p8EncPbes2ASN[] = {
/* SEQ */           { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ALGO_SEQ */          { 1, ASN_SEQUENCE, 1, 1, 0 },
                    /* PBE algorithm */
/* ALGO_OID */              { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* ALGO_PARAMS_SEQ */       { 2, ASN_SEQUENCE, 1, 1, 0 },
/* ALGO_PARAMS_KDF_SEQ */       { 3, ASN_SEQUENCE, 1, 1, 0 },
               /* PBKDF2 */
/* ALGO_PARAMS_KDF_OID */           { 4, ASN_OBJECT_ID, 0, 0, 0 },
/* ALGO_PARAMS_PBKDF2_SEQ */        { 4, ASN_SEQUENCE, 1, 1, 0 },
                   /* Salt */
/* ALGO_PARAMS_PBKDF2_SALT */           { 5, ASN_OCTET_STRING, 0, 0, 0 },
                   /* Iteration count */
/* ALGO_PARAMS_PBKDF2_ITER */           { 5, ASN_INTEGER, 0, 0, 0 },
                   /* Key length */
/* ALGO_PARAMS_PBKDF2_KEYLEN */         { 5, ASN_INTEGER, 0, 0, 1 },
                   /* PRF - default is HMAC-SHA1 */
/* ALGO_PARAMS_PBKDF2_PRF */            { 5, ASN_SEQUENCE, 1, 1, 1 },
/* ALGO_PARAMS_PBKDF2_PRF_OID */            { 6, ASN_OBJECT_ID, 0, 0, 0 },
/* ALGO_PARAMS_PBKDF2_PRF_NULL */           { 6, ASN_TAG_NULL, 0, 0, 1 },
/* ALGO_ENCS_SEQ */             { 3, ASN_SEQUENCE, 1, 1, 0 },
                   /* Encryption algorithm */
/* ALGO_ENCS_OID */                 { 4, ASN_OBJECT_ID, 0, 0, 0 },
                   /* IV for CBC */
/* ALGO_ENCS_PARAMS */              { 4, ASN_OCTET_STRING, 0, 0, 0 },
/* ENCDATA */           { 1, (ASN_CONTEXT_SPECIFIC | 0), 0, 0, 0 },
};
enum {
    P8ENCPBES2ASN_IDX_SEQ = 0,
    P8ENCPBES2ASN_IDX_ALGO_SEQ,
    P8ENCPBES2ASN_IDX_ALGO_OID,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_SEQ,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_KDF_SEQ,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_KDF_OID,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_SEQ,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_SALT,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_ITER,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_KEYLEN,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF_OID,
    P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF_NULL,
    P8ENCPBES2ASN_IDX_ALGO_ENCS_SEQ,
    P8ENCPBES2ASN_IDX_ALGO_ENCS_OID,
    P8ENCPBES2ASN_IDX_ALGO_ENCS_PARAMS,
    P8ENCPBES2ASN_IDX_ENCDATA
};

#define p8EncPbes2ASN_Length (sizeof(p8EncPbes2ASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

static int EncryptContentPBES2(byte* input, word32 inputSz, byte* out,
        word32* outSz, const char* password, int passwordSz, int encAlgId,
        byte* salt, word32 saltSz, int itt, int hmacOid, WC_RNG* rng,
        void* heap)
{
    int ret = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
    (void)input;
    (void)inputSz;
    (void)out;
    (void)outSz;
    (void)password;
    (void)passwordSz;
    (void)encAlgId;
    (void)salt;
    (void)saltSz;
    (void)itt;
    (void)hmacOid;
    (void)rng;
    (void)heap;
    ret = ASN_VERSION_E;
#else /* WOLFSSL_ASN_TEMPLATE */
    /* PBES2 is only supported when enabling the ASN template */

    DECL_ASNSETDATA(dataASN, p8EncPbes2ASN_Length);
    const byte* blkOidBuf = NULL;
    int blkOidSz = 0;
    int pbesId = -1;
    int blockSz = 0;
    word32 asnSz = 0;
    word32 pkcs8Sz = 0;
    byte* cbcIv = NULL;
    byte* saltEnc = NULL;
    int genSalt = (salt == NULL || saltSz == 0);

    WOLFSSL_ENTER("EncryptContentPBES2");

    /* Must have a output size to return or check. */
    if (outSz == NULL) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && genSalt) {
        salt = NULL;
        saltSz = PKCS5V2_SALT_SZ;
        /* Salt generated into encoding below. */
    }
    /* Check salt size is valid. */
    if ((ret == 0) && (saltSz > MAX_SALT_SIZE)) {
        ret = ASN_PARSE_E;
    }
    if ((ret == 0) && GetAlgoV2(encAlgId, &blkOidBuf, &blkOidSz, &pbesId,
        &blockSz) < 0) {
        ret = ASN_INPUT_E;
    }
    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, p8EncPbes2ASN_Length, ret, heap);

    if (ret == 0) {
        /* Setup data to go into encoding including PBE algorithm, salt,
         * iteration count, and padded key length. */
        SetASN_OID(&dataASN[P8ENCPBES2ASN_IDX_ALGO_OID], (word32)PBES2,
                   oidPBEType);
        SetASN_Buffer(&dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_KDF_OID],
            pbkdf2Oid, sizeof(pbkdf2Oid));
        SetASN_Buffer(&dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_SALT], NULL,
            saltSz);
        SetASN_Int16Bit(&dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_ITER],
            (word16)itt);
        dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_KEYLEN].noOut = 1;
        if (hmacOid > 0) {
            const byte* hmacOidBuf = NULL;
            word32 hmacOidSz = 0;
            hmacOidBuf = OidFromId((word32)hmacOid, oidHmacType, &hmacOidSz);
            if (hmacOidBuf == NULL) {
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                SetASN_Buffer(
                    &dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF_OID],
                    hmacOidBuf, hmacOidSz);
            }
        }
        else {
            /* SHA1 will be used as default without PRF parameters */
            dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF].noOut = 1;
            dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF_OID].noOut = 1;
            dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_PRF_NULL].noOut = 1;
        }
        SetASN_Buffer(&dataASN[P8ENCPBES2ASN_IDX_ALGO_ENCS_OID], blkOidBuf,
            blkOidSz);
        SetASN_Buffer(&dataASN[P8ENCPBES2ASN_IDX_ALGO_ENCS_PARAMS], NULL,
            blockSz);
        pkcs8Sz = wc_PkcsPad(NULL, inputSz, (word32)blockSz);
        SetASN_Buffer(&dataASN[P8ENCPBES2ASN_IDX_ENCDATA], NULL, pkcs8Sz);

        /* Calculate size of encoding. */
        ret = SizeASN_Items(p8EncPbes2ASN + P8ENCPBES2ASN_IDX_ALGO_SEQ,
                dataASN + P8ENCPBES2ASN_IDX_ALGO_SEQ,
                (int)(p8EncPbes2ASN_Length - P8ENCPBES2ASN_IDX_ALGO_SEQ),
                &asnSz);
    }
    /* Return size when no output buffer. */
    if ((ret == 0) && (out == NULL)) {
        *outSz = asnSz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check output buffer is big enough for encoded data. */
    if ((ret == 0) && (asnSz > *outSz)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Encode PKCS#8 key. */
        SetASN_Items(p8EncPbes2ASN + P8ENCPBES2ASN_IDX_ALGO_SEQ,
                 dataASN + P8ENCPBES2ASN_IDX_ALGO_SEQ,
                 (int)(p8EncPbes2ASN_Length - P8ENCPBES2ASN_IDX_ALGO_SEQ),
                 out);

        /* safe cast -- the pointer is actually inside the output buffer. */
        saltEnc = (byte*)(wc_ptr_t)
            dataASN[P8ENCPBES2ASN_IDX_ALGO_PARAMS_PBKDF2_SALT].
                data.buffer.data;
        if (genSalt) {
            /* Generate salt into encoding. */
            ret = wc_RNG_GenerateBlock(rng, saltEnc, saltSz);
        }
        else {
            XMEMCPY(saltEnc, salt, saltSz);
        }
    }
    if (ret == 0) {
        /* safe cast -- the pointer is actually inside the output buffer. */
        cbcIv = (byte*)(wc_ptr_t)
            dataASN[P8ENCPBES2ASN_IDX_ALGO_ENCS_PARAMS].data.buffer.data;
        ret = wc_RNG_GenerateBlock(rng, cbcIv, (word32)blockSz);
    }
    if (ret == 0) {
        /* Store PKCS#8 key in output buffer. */
        /* safe cast -- the pointer is actually inside the output buffer. */
        byte* pkcs8 = (byte*)(wc_ptr_t)
            dataASN[P8ENCPBES2ASN_IDX_ENCDATA].data.buffer.data;
        XMEMCPY(pkcs8, input, inputSz);
        (void)wc_PkcsPad(pkcs8, inputSz, (word32)blockSz);

        /* Encrypt PKCS#8 key inline. */
        ret = wc_CryptKey(password, passwordSz, saltEnc, (int)saltSz, itt,
            pbesId, pkcs8, (int)pkcs8Sz, PKCS5v2, cbcIv, 1, hmacOid);
    }
    if (ret == 0) {
        /* Returning size on success. */
        ret = (int)asnSz;
    }

    FREE_ASNSETDATA(dataASN, heap);
    (void)heap;
#endif /* WOLFSSL_ASN_TEMPLATE */
    return ret;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for PKCS #8 encrypted key with PBES1 parameters.
 * PKCS #8: RFC 5958, 3 - EncryptedPrivateKeyInfo
 * PKCS #5: RFC 8018, A.3 - PBEParameter
 */
static const ASNItem p8EncPbes1ASN[] = {
/* SEQ                   */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ENCALGO_SEQ           */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                    /* PBE algorithm */
/* ENCALGO_OID           */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* ENCALGO_PBEPARAM_SEQ  */         { 2, ASN_SEQUENCE, 1, 1, 0 },
                        /* Salt */
/* ENCALGO_PBEPARAM_SALT */             { 3, ASN_OCTET_STRING, 0, 0, 0 },
                        /* Iteration Count */
/* ENCALGO_PBEPARAM_ITER */             { 3, ASN_INTEGER, 0, 0, 0 },
/* ENCDATA               */     { 1, (ASN_CONTEXT_SPECIFIC | 0), 0, 0, 0 },
};
enum {
    P8ENCPBES1ASN_IDX_SEQ = 0,
    P8ENCPBES1ASN_IDX_ENCALGO_SEQ,
    P8ENCPBES1ASN_IDX_ENCALGO_OID,
    P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_SEQ,
    P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_SALT,
    P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_ITER,
    P8ENCPBES1ASN_IDX_ENCDATA
};

#define p8EncPbes1ASN_Length (sizeof(p8EncPbes1ASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Wrap a private key in PKCS#8 and encrypt.
 *
 * Used for PKCS#12 and PKCS#7.
 * vPKCS is the version of PKCS to use.
 * vAlgo is the algorithm version to use.
 *
 * When salt is NULL, a random number is generated.
 *
 * data returned is :
 * [ seq - obj [ seq -salt,itt]] , construct with encrypted data
 *
 * @param [in]  input       Data to encrypt.
 * @param [in]  inputSz     Length of data in bytes.
 * @param [out] out         Buffer to write wrapped encrypted data into.
 * @param [out] outSz       Length of encrypted data in bytes.
 * @param [in]  password    Password used to create encryption key.
 * @param [in]  passwordSz  Length of password in bytes.
 * @param [in]  vPKCS       First byte used to determine PBE algorithm.
 * @param [in]  vAlgo       Second byte used to determine PBE algorithm.
 * @param [in]  encAlgId    Encryption Algorithm for PBES2.
 * @param [in]  salt        Salt to use with KDF.
 * @param [in]  saltSz      Length of salt in bytes.
 * @param [in]  itt         Number of iterations to use in KDF.
 * @param [in]  hmacOid     HMAC Algorithm for PBES2.
 * @param [in]  rng         Random number generator to use to generate salt.
 * @param [in]  heap        Dynamic memory allocator hint.
 * @return  The size of encrypted data on success
 * @return  LENGTH_ONLY_E when out is NULL and able to encode.
 * @return  ASN_PARSE_E when the salt size is too large.
 * @return  ASN_VERSION_E when attempting to use a PBES2 algorithm (use
 *          TraditionalEnc).
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other when encryption or random number generation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
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)
{
    /* PBES2 is only supported when enabling the ASN template */

    DECL_ASNSETDATA(dataASN, p8EncPbes1ASN_Length);
    int ret = 0;
    word32 sz = 0;
    int version = 0;
    int id = -1;
    int blockSz = 0;
    word32 pkcs8Sz = 0;

    (void)heap;

    WOLFSSL_ENTER("EncryptContent");

    /* Must have a output size to return or check. */
    if (outSz == NULL) {
        ret = BAD_FUNC_ARG;
    }
    /* Check salt size is valid. */
    if ((ret == 0) && (saltSz > MAX_SALT_SIZE)) {
        ret = ASN_PARSE_E;
    }
    /* Get algorithm parameters for algorithm identifier. */
    if ((ret == 0) && CheckAlgo(vPKCS, vAlgo, &id, &version, &blockSz) < 0) {
        ret = ASN_INPUT_E;
    }
    /* Check PKCS #5 version - only PBSE1 parameters supported. */
    if ((ret == 0) && (version == PKCS5v2)) {
        return EncryptContentPBES2(input, inputSz, out, outSz, password,
            passwordSz, encAlgId, salt, saltSz, itt, hmacOid, rng, heap);
    }
    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, p8EncPbes1ASN_Length, ret, heap);

    if (ret == 0) {
        /* Setup data to go into encoding including PBE algorithm, salt,
         * iteration count, and padded key length. */
        SetASN_OID(&dataASN[P8ENCPBES1ASN_IDX_ENCALGO_OID], (word32)id,
                   oidPBEType);
        if (salt == NULL || saltSz == 0) {
            salt = NULL;
            saltSz = PKCS5_SALT_SZ;
            /* Salt generated into encoding below. */
        }
        SetASN_Buffer(&dataASN[P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_SALT],
                salt, saltSz);
        SetASN_Int16Bit(&dataASN[P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_ITER],
                        (word16)itt);
        pkcs8Sz = wc_PkcsPad(NULL, inputSz, (word32)blockSz);
        SetASN_Buffer(&dataASN[P8ENCPBES1ASN_IDX_ENCDATA], NULL, pkcs8Sz);

        /* Calculate size of encoding. */
        ret = SizeASN_Items(p8EncPbes1ASN + P8ENCPBES1ASN_IDX_ENCALGO_SEQ,
                dataASN + P8ENCPBES1ASN_IDX_ENCALGO_SEQ,
                (int)(p8EncPbes1ASN_Length - P8ENCPBES1ASN_IDX_ENCALGO_SEQ),
                &sz);
    }
    /* Return size when no output buffer. */
    if ((ret == 0) && (out == NULL)) {
        *outSz = sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check output buffer is big enough for encoded data. */
    if ((ret == 0) && (sz > *outSz)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Encode PKCS#8 key. */
        SetASN_Items(p8EncPbes1ASN + P8ENCPBES1ASN_IDX_ENCALGO_SEQ,
                 dataASN + P8ENCPBES1ASN_IDX_ENCALGO_SEQ,
                 (int)(p8EncPbes1ASN_Length - P8ENCPBES1ASN_IDX_ENCALGO_SEQ),
                 out);

        if (salt == NULL) {
            /* Generate salt into encoding. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            salt = (byte*)(wc_ptr_t)
                dataASN[P8ENCPBES1ASN_IDX_ENCALGO_PBEPARAM_SALT].
                    data.buffer.data;
            ret = wc_RNG_GenerateBlock(rng, salt, saltSz);
        }
    }
    if (ret == 0) {
        byte cbcIv[MAX_IV_SIZE];
        /* Store PKCS#8 key in output buffer. */
        /* safe cast -- the pointer is actually inside the output buffer. */
        byte* pkcs8 = (byte*)(wc_ptr_t)
            dataASN[P8ENCPBES1ASN_IDX_ENCDATA].data.buffer.data;
        XMEMCPY(pkcs8, input, inputSz);
        (void)wc_PkcsPad(pkcs8, inputSz, (word32)blockSz);

        /* Encrypt PKCS#8 key inline. */
        ret = wc_CryptKey(password, passwordSz, salt, (int)saltSz, itt, id,
                          pkcs8, (int)pkcs8Sz, version, cbcIv, 1, 0);
    }
    if (ret == 0) {
        /* Returning size on success. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#endif /* HAVE_PKCS12 */
#endif /* NO_PWDBASED */

/* Block padding used for PKCS#5, PKCS#7, PKCS#8 and PKCS#12.
 *
 * The length of padding is the value of each padding byte.
 *
 * When buf is NULL, the padded size is returned.
 *
 * @param [in, out] buf      Buffer of data to be padded. May be NULL.
 * @param [in]      sz       Size of data in bytes.
 * @param [in]      blockSz  Size of block, in bytes, which buffer size must be
 *                           a multiple of. Assumed to be less than 256 and
 *                           a power of 2.
 * @return  Size of padded buffer in bytes.
 */
word32 wc_PkcsPad(byte* buf, word32 sz, word32 blockSz)
{
    /* Calculate number of padding bytes. */
    word32 padSz = blockSz - (sz & (blockSz - 1));

    /* Pad with padSz byte. */
    if (buf != NULL) {
        word32 i;
        for (i = 0; i < padSz; i++) {
            buf[sz+i] = (byte)(padSz & 0xFF);
        }
    }

    /* Return padded buffer size in bytes. */
    return sz + padSz;
}

#ifndef NO_RSA
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for an RSA public key.
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 */
static const ASNItem rsaPublicKeyASN[] = {
/*  SEQ            */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/*  ALGOID_SEQ     */     { 1, ASN_SEQUENCE, 1, 1, 0 },
/*  ALGOID_OID     */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/*  ALGOID_NULL    */         { 2, ASN_TAG_NULL, 0, 0, 1 },
#ifdef WC_RSA_PSS
/*  ALGOID_P_SEQ   */         { 2, ASN_SEQUENCE, 1, 0, 1 },
#endif
/*  PUBKEY         */     { 1, ASN_BIT_STRING, 0, 1, 0 },
                                                  /* RSAPublicKey */
/*  PUBKEY_RSA_SEQ */         { 2, ASN_SEQUENCE, 1, 1, 0 },
/*  PUBKEY_RSA_N   */             { 3, ASN_INTEGER, 0, 0, 0 },
/*  PUBKEY_RSA_E   */             { 3, ASN_INTEGER, 0, 0, 0 },
};
enum {
    RSAPUBLICKEYASN_IDX_SEQ = 0,
    RSAPUBLICKEYASN_IDX_ALGOID_SEQ,
    RSAPUBLICKEYASN_IDX_ALGOID_OID,
    RSAPUBLICKEYASN_IDX_ALGOID_NULL,
#ifdef WC_RSA_PSS
    RSAPUBLICKEYASN_IDX_ALGOID_P_SEQ,
#endif
    RSAPUBLICKEYASN_IDX_PUBKEY,
    RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ,
    RSAPUBLICKEYASN_IDX_PUBKEY_RSA_N,
    RSAPUBLICKEYASN_IDX_PUBKEY_RSA_E
};

/* Number of items in ASN.1 template for an RSA public key. */
#define rsaPublicKeyASN_Length (sizeof(rsaPublicKeyASN) / sizeof(ASNItem))
#endif

#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
/* This function is to retrieve key position information in a cert.*
 * The information will be used to call TSIP TLS-linked API for    *
 * certificate verification.                                       */
#ifdef WOLFSSL_ASN_TEMPLATE
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;
    const byte*  n   = NULL;
    const byte*  e   = NULL; /* pointer to modulus/exponent */
    word32 rawIndex = 0;

    ret = wc_RsaPublicKeyDecode_ex(input, inOutIdx, (word32)inSz,
                                                &n, key_n_len, &e, key_e_len);
    if (ret == 0) {
        /* convert pointer to offset */
        if (key_n != NULL) {
            rawIndex = n - input;
            *key_n += rawIndex;
        }
        if (key_e != NULL) {
            rawIndex = e - input;
            *key_e += rawIndex;
        }
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_RENESAS_TSIP */
/* Decode RSA public key.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA public key.
 *                            On out, start of ASN.1 item after RSA public key.
 * @param [in]      inSz      Number of bytes in buffer.
 * @param [out]     n         Pointer to modulus in buffer.
 * @param [out]     nSz       Size of modulus in bytes.
 * @param [out]     e         Pointer to exponent in buffer.
 * @param [out]     eSz       Size of exponent in bytes.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz,
    const byte** n, word32* nSz, const byte** e, word32* eSz)
{
    DECL_ASNGETDATA(dataASN, rsaPublicKeyASN_Length);
    int ret = 0;
    word32 startIdx = (inOutIdx != NULL) ? *inOutIdx : 0;
#ifdef WC_RSA_PSS
    word32 oid = RSAk;
#endif

    /* Check validity of parameters. */
    if (input == NULL || inOutIdx == NULL) {
        ret = BAD_FUNC_ARG;
    }

    CALLOC_ASNGETDATA(dataASN, rsaPublicKeyASN_Length, ret, NULL);

    if (ret == 0) {
        /* Try decoding PKCS #1 public key by ignoring rest of ASN.1. */
        ret = GetASN_Items(&rsaPublicKeyASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ],
           &dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ],
           (int)(rsaPublicKeyASN_Length - RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ),
           0, input, inOutIdx, inSz);
        if (ret != 0) {
            /* Didn't work - try whole SubjectKeyInfo instead. Reset index
             * to caller's start since the previous attempt advanced it. */
            *inOutIdx = startIdx;
        #ifdef WC_RSA_PSS
            /* Could be RSA or RSA PSS key. */
            GetASN_OID(&dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID], oidKeyType);
        #else
            /* Set the OID to expect. */
            GetASN_ExpBuffer(&dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID],
                    keyRsaOid, sizeof(keyRsaOid));
        #endif
            /* Decode SubjectKeyInfo. */
            ret = GetASN_Items(rsaPublicKeyASN, dataASN,
                               rsaPublicKeyASN_Length, 1, input, inOutIdx,
                               inSz);
        }
    }
#ifdef WC_RSA_PSS
    if ((ret == 0) && (dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID].tag != 0)) {
        /* Two possible OIDs supported - RSA and RSA PSS. */
        oid = dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID].data.oid.sum;
        if ((oid != RSAk) && (oid != RSAPSSk)) {
            ret = ASN_PARSE_E;
        }
    }
    if ((ret == 0) && (dataASN[RSAPUBLICKEYASN_IDX_ALGOID_P_SEQ].tag != 0)) {
        /* Can't have NULL and SEQ. */
        if (dataASN[RSAPUBLICKEYASN_IDX_ALGOID_NULL].tag != 0) {
            ret = ASN_PARSE_E;
        }
        /* SEQ present only with RSA PSS. */
        if ((ret == 0) && (oid != RSAPSSk)) {
            ret = ASN_PARSE_E;
        }
        if (ret == 0) {
            enum wc_HashType hash;
            int mgf;
            int saltLen;
            const byte* params = GetASNItem_Addr(
                dataASN[RSAPUBLICKEYASN_IDX_ALGOID_P_SEQ], input);
            word32 paramsSz = GetASNItem_Length(
                dataASN[RSAPUBLICKEYASN_IDX_ALGOID_P_SEQ], input);

            /* Validate the private key parameters. */
            ret = DecodeRsaPssParams(params, paramsSz, &hash, &mgf, &saltLen);
            /* TODO: store parameters so that usage can be checked. */
        }
    }
#endif
    if (ret == 0) {
        /* Detect if this is an RSA private key being passed as public key.
         * An RSA private key has: version (small int), modulus, exponent, ...
         * An RSA public key has: modulus (large int), exponent, nothing more.
         * If the first integer is small (like version 0) and there's more data
         * after what we consumed, this is likely a private key. */
        word32 nLen = dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_N].data.ref.length;
        if (nLen <= MAX_VERSION_SZ && *inOutIdx < inSz) {
            /* Check if next byte could be an INTEGER tag - indicating more
             * fields like in a private key structure */
            if (input[*inOutIdx] == ASN_INTEGER) {
                ret = ASN_RSA_KEY_E;
            }
        }
    }
    if (ret == 0) {
        /* Return the buffers and lengths asked for. */
        if (n != NULL) {
            *n   = dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_N].data.ref.data;
        }
        if (nSz != NULL) {
            *nSz = dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_N].data.ref.length;
        }
        if (e != NULL) {
            *e   = dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_E].data.ref.data;
        }
        if (eSz != NULL) {
            *eSz = dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_E].data.ref.length;
        }
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode RSA public key.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA public key.
 *                            On out, start of ASN.1 item after RSA public key.
 * @param [in, out] key       RSA key object.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
int wc_RsaPublicKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key,
                       word32 inSz)
{
    int ret;
    const byte *n = NULL, *e = NULL;
    word32 nSz = 0, eSz = 0;

    if (key == NULL)
        return BAD_FUNC_ARG;

    ret = wc_RsaPublicKeyDecode_ex(input, inOutIdx, inSz, &n, &nSz, &e, &eSz);
    if (ret == 0) {
        ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key);
    }

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

#ifndef NO_DH
#if defined(WOLFSSL_DH_EXTRA)
/*
 * Decodes DH public key to fill specified DhKey.
 *
 * return 0 on success, negative on failure
 */
int wc_DhPublicKeyDecode(const byte* input, word32* inOutIdx,
                DhKey* key, word32 inSz)
{
    int ret = 0;
    int length;
    word32 oid = 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;

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

    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 (GetInt(&key->g, input, inOutIdx, inSz) < 0) {
        mp_clear(&key->p);
        return ASN_DH_KEY_E;
    }
    ret = (CheckBitString(input, inOutIdx, &length, inSz, 0, NULL) == 0);
    if (ret > 0) {
        /* Found Bit String WOLFSSL_DH_EXTRA is required to access DhKey.pub */
        if (GetInt(&key->pub, input, inOutIdx, inSz) < 0) {
            mp_clear(&key->p);
            mp_clear(&key->g);
            return ASN_DH_KEY_E;
        }
    }
    else {
        mp_clear(&key->p);
        mp_clear(&key->g);
        return ASN_DH_KEY_E;
    }
    return 0;
}
#endif /* WOLFSSL_DH_EXTRA */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for DH key.
 * PKCS #3, 9 - DHParameter.
 * (Also in: RFC 2786, 3)
 */
static const ASNItem dhParamASN[] = {
/* SEQ     */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                /* prime */
/* PRIME   */        { 1, ASN_INTEGER, 0, 0, 0 },
                /* base */
/* BASE    */        { 1, ASN_INTEGER, 0, 0, 0 },
                /* privateValueLength */
/* PRIVLEN */        { 1, ASN_INTEGER, 0, 0, 1 },
};
enum {
    DHPARAMASN_IDX_SEQ = 0,
    DHPARAMASN_IDX_PRIME,
    DHPARAMASN_IDX_BASE,
    DHPARAMASN_IDX_PRIVLEN
};

/* Number of items in ASN.1 template for DH key. */
#define dhParamASN_Length (sizeof(dhParamASN) / sizeof(ASNItem))

#ifdef WOLFSSL_DH_EXTRA
/* ASN.1 template for DH key wrapped in PKCS #8 or SubjectPublicKeyInfo.
 * PKCS #8: RFC 5208, 5 - PrivateKeyInfo
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * RFC 3279, 2.3.3 - DH in SubjectPublicKeyInfo
 */
static const ASNItem dhKeyPkcs8ASN[] = {
/* SEQ                  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* VER                  */     { 1, ASN_INTEGER, 0, 0, 1 },
/* PKEYALGO_SEQ         */     { 1, ASN_SEQUENCE, 1, 1, 0 },
/* PKEYALGO_OID         */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
                                                     /* DHParameter */
/* PKEYALGO_PARAM_SEQ   */         { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                         /* p */
/* PKEYALGO_PARAM_P     */             { 3, ASN_INTEGER, 0, 0, 0 },
                                                         /* g */
/* PKEYALGO_PARAM_G     */             { 3, ASN_INTEGER, 0, 0, 0 },
                                                         /* q - factor of p-1 */
/* PKEYALGO_PARAM_Q     */             { 3, ASN_INTEGER, 0, 0, 1 },
                                                         /* j - subgroup factor */
/* PKEYALGO_PARAM_J     */             { 3, ASN_INTEGER, 0, 0, 1 },
                                                         /* ValidationParms */
/* PKEYALGO_PARAM_VALID */             { 3, ASN_SEQUENCE, 0, 0, 1 },
                                                 /* PrivateKey - PKCS #8 */
/* PKEY_STR             */     { 1, ASN_OCTET_STRING, 0, 1, 2 },
/* PKEY_INT             */         { 2, ASN_INTEGER, 0, 0, 0 },
                                                 /* PublicKey - SubjectPublicKeyInfo. */
/* PUBKEY_STR           */     { 1, ASN_BIT_STRING, 0, 1, 2 },
/* PUBKEY_INT           */         { 2, ASN_INTEGER, 0, 0, 0 },
};
enum {
    DHKEYPKCS8ASN_IDX_SEQ = 0,
    DHKEYPKCS8ASN_IDX_VER,
    DHKEYPKCS8ASN_IDX_PKEYALGO_SEQ,
    DHKEYPKCS8ASN_IDX_PKEYALGO_OID,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_SEQ,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_P,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_G,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_Q,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_J,
    DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_VALID,
    DHKEYPKCS8ASN_IDX_PKEY_STR,
    DHKEYPKCS8ASN_IDX_PKEY_INT,
    DHKEYPKCS8ASN_IDX_PUBKEY_STR,
    DHKEYPKCS8ASN_IDX_PUBKEY_INT
};

#define dhKeyPkcs8ASN_Length (sizeof(dhKeyPkcs8ASN) / sizeof(ASNItem))
#endif
#endif

/* Decodes either PKCS#3 DH parameters or PKCS#8 DH key file (WOLFSSL_DH_EXTRA).
 *
 * See also wc_DhParamsLoad(). Loads directly into buffers rather than key
 * object.
 *
 * @param [in]      input     BER/DER encoded data.
 * @param [in, out] inOutIdx  On in, start of DH key data.
 *                            On out, end of DH key data.
 * @param [in, out] key       DH key object.
 * @param [in]      inSz      Size of data in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when input, inOutIDx or key is NULL.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  MP_INIT_E when the unable to initialize an mp_int.
 * @return  ASN_GETINT_E when the unable to convert data to an mp_int.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz)
{
#ifdef WOLFSSL_DH_EXTRA
    DECL_ASNGETDATA(dataASN, dhKeyPkcs8ASN_Length);
#else
    DECL_ASNGETDATA(dataASN, dhParamASN_Length);
#endif
    int ret = 0;

    /* Check input parameters are valid. */
    if ((input == NULL) || (inOutIdx == NULL) || (key == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
#ifdef WOLFSSL_DH_EXTRA
        ALLOC_ASNGETDATA(dataASN, dhKeyPkcs8ASN_Length, ret, key->heap);
#else
        ALLOC_ASNGETDATA(dataASN, dhParamASN_Length, ret, key->heap);
#endif
    }

    if (ret == 0) {
        /* Initialize data and set mp_ints to hold p and g. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * dhParamASN_Length);
        GetASN_MP(&dataASN[DHPARAMASN_IDX_PRIME], &key->p);
        GetASN_MP(&dataASN[DHPARAMASN_IDX_BASE], &key->g);
        /* Try simple PKCS #3 template. */
        ret = GetASN_Items(dhParamASN, dataASN, dhParamASN_Length, 1, input,
                           inOutIdx, inSz);
#ifdef WOLFSSL_DH_EXTRA
        if (ret != 0) {
            mp_free(&key->p);
            mp_free(&key->g);

            /* Initialize data and set mp_ints to hold p, g, q, priv and pub. */
            XMEMSET(dataASN, 0, sizeof(*dataASN) * dhKeyPkcs8ASN_Length);
            GetASN_ExpBuffer(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_OID],
                    keyDhOid, sizeof(keyDhOid));
            GetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_P], &key->p);
            GetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_G], &key->g);
            GetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_Q], &key->q);
            GetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEY_INT], &key->priv);
            GetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PUBKEY_INT], &key->pub);
            /* Try PKCS #8 wrapped template. */
            ret = GetASN_Items(dhKeyPkcs8ASN, dataASN, dhKeyPkcs8ASN_Length, 1,
                               input, inOutIdx, inSz);
            if (ret == 0) {
                /* VERSION only present in PKCS #8 private key structure */
                if ((dataASN[DHKEYPKCS8ASN_IDX_PKEY_INT].length != 0) &&
                        (dataASN[DHKEYPKCS8ASN_IDX_VER].length == 0)) {
                    ret = ASN_PARSE_E;
                }
                else if ((dataASN[DHKEYPKCS8ASN_IDX_PUBKEY_INT].length != 0) &&
                        (dataASN[DHKEYPKCS8ASN_IDX_VER].length != 0)) {
                    ret = ASN_PARSE_E;
                }
            }
            if ((ret == 0) && mp_iszero(&key->pub)) {
                ret = mp_exptmod(&key->g, &key->priv, &key->p, &key->pub);
            }
        }
#endif
    }

    FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_DH_EXTRA

/* Export DH Key (private or public) */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DhKeyToDer(DhKey* key, byte* output, word32* outSz, int exportPriv)
{
    ASNSetData dataASN[dhKeyPkcs8ASN_Length];
    int ret = 0;
    word32 sz = 0;

    WOLFSSL_ENTER("wc_DhKeyToDer");

    XMEMSET(dataASN, 0, sizeof(dataASN));
    SetASN_Int8Bit(&dataASN[DHKEYPKCS8ASN_IDX_VER], 0);
    SetASN_OID(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_OID], DHk, oidKeyType);
    /* Set mp_int containing p and g. */
    SetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_P], &key->p);
    SetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_G], &key->g);
    dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_Q].noOut = 1;
    dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_J].noOut = 1;
    dataASN[DHKEYPKCS8ASN_IDX_PKEYALGO_PARAM_VALID].noOut = 1;

    if (exportPriv) {
        SetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PKEY_INT], &key->priv);
        dataASN[DHKEYPKCS8ASN_IDX_PUBKEY_STR].noOut = 1;
        dataASN[DHKEYPKCS8ASN_IDX_PUBKEY_INT].noOut = 1;
    }
    else {
        dataASN[DHKEYPKCS8ASN_IDX_VER].noOut = 1;
        dataASN[DHKEYPKCS8ASN_IDX_PKEY_STR].noOut = 1;
        dataASN[DHKEYPKCS8ASN_IDX_PKEY_INT].noOut = 1;
        SetASN_MP(&dataASN[DHKEYPKCS8ASN_IDX_PUBKEY_INT], &key->pub);
    }

    /* Calculate the size of the DH parameters. */
    ret = SizeASN_Items(dhKeyPkcs8ASN, dataASN, dhKeyPkcs8ASN_Length, &sz);
    if (output == NULL) {
        *outSz = sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (*outSz < sz)) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode the DH parameters into buffer. */
        SetASN_Items(dhKeyPkcs8ASN, dataASN, dhKeyPkcs8ASN_Length, output);
        /* Set the actual encoding size. */
        *outSz = sz;
        /* Return the actual encoding size. */
        ret = (int)sz;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
int wc_DhPubKeyToDer(DhKey* key, byte* out, word32* outSz)
{
    return wc_DhKeyToDer(key, out, outSz, 0);
}
int wc_DhPrivKeyToDer(DhKey* key, byte* out, word32* outSz)
{
    return wc_DhKeyToDer(key, out, outSz, 1);
}


/* Convert DH key parameters to DER format, write to output (outSz)
 * If output is NULL then max expected size is set to outSz and LENGTH_ONLY_E is
 * returned.
 *
 * Note : static function due to redefinition complications with DhKey and FIPS
 * version 2 build.
 *
 * return bytes written on success */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DhParamsToDer(DhKey* key, byte* output, word32* outSz)
{
    ASNSetData dataASN[dhParamASN_Length];
    int ret = 0;
    word32 sz = 0;

    WOLFSSL_ENTER("wc_DhParamsToDer");

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

    if (ret == 0) {
        XMEMSET(dataASN, 0, sizeof(dataASN));
        /* Set mp_int containing p and g. */
        SetASN_MP(&dataASN[DHPARAMASN_IDX_PRIME], &key->p);
        SetASN_MP(&dataASN[DHPARAMASN_IDX_BASE], &key->g);
        /* privateValueLength not encoded. */
        dataASN[DHPARAMASN_IDX_PRIVLEN].noOut = 1;

        /* Calculate the size of the DH parameters. */
        ret = SizeASN_Items(dhParamASN, dataASN, dhParamASN_Length, &sz);
    }
    if ((ret == 0) && (output == NULL)) {
        *outSz = sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (*outSz < sz)) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode the DH parameters into buffer. */
        SetASN_Items(dhParamASN, dataASN, dhParamASN_Length, output);
        /* Set the actual encoding size. */
        *outSz = sz;
        /* Return count of bytes written. */
        ret = (int)sz;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_DH_EXTRA */

/* Decode DH parameters.
 *
 * PKCS #3, 9 - DHParameter.
 * (Also in: RFC 2786, 3)
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of RSA public key.
 *                            On out, start of ASN.1 item after RSA public key.
 * @param [in]      inSz      Number of bytes in buffer.
 * @param [in, out] p         Buffer to hold prime.
 * @param [out]     pInOutSz  On in, size of buffer to hold prime in bytes.
 *                            On out, size of prime in bytes.
 * @param [in, out] g         Buffer to hold base.
 * @param [out]     gInOutSz  On in, size of buffer to hold base in bytes.
 *                            On out, size of base in bytes.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DhParamsLoad(const byte* input, word32 inSz, byte* p, word32* pInOutSz,
                 byte* g, word32* gInOutSz)
{
    DECL_ASNGETDATA(dataASN, dhParamASN_Length);
    word32 idx = 0;
    int ret = 0;

    /* Make sure pointers are valid before use. */
    if ((input == NULL) || (p == NULL) || (pInOutSz == NULL) || (g == NULL) ||
            (gInOutSz == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    CALLOC_ASNGETDATA(dataASN, dhParamASN_Length, ret, NULL);

    if (ret == 0) {
        /* Set the buffers to copy p and g into. */
        GetASN_Buffer(&dataASN[DHPARAMASN_IDX_PRIME], p, pInOutSz);
        GetASN_Buffer(&dataASN[DHPARAMASN_IDX_BASE], g, gInOutSz);
        /* Decode the DH Parameters. */
        ret = GetASN_Items(dhParamASN, dataASN, dhParamASN_Length, 1, input,
                           &idx, inSz);
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_DH */


#ifndef NO_DSA

static mp_int* GetDsaInt(DsaKey* key, int idx)
{
    if (idx == 0)
        return &key->p;
    if (idx == 1)
        return &key->q;
    if (idx == 2)
        return &key->g;
    if (idx == 3)
        return &key->y;
    if (idx == 4)
        return &key->x;

    return NULL;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for DSA public and private keys.
 * Public key: seq, p, q, g, y
 * Private key: seq, version, p, q, g, y, x
 * RFC 3279, 2.3.2 - DSA in SubjectPublicKeyInfo
 */
static const ASNItem dsaKeyASN[] = {
/* SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* VER */        { 1, ASN_INTEGER, 0, 0, 0 },
/* P   */        { 1, ASN_INTEGER, 0, 0, 0 },
/* Q   */        { 1, ASN_INTEGER, 0, 0, 0 },
/* G   */        { 1, ASN_INTEGER, 0, 0, 0 },
/* Y   */        { 1, ASN_INTEGER, 0, 0, 0 },
/* X   */        { 1, ASN_INTEGER, 0, 0, 0 },
};
enum {
    DSAKEYASN_IDX_SEQ = 0,
    DSAKEYASN_IDX_VER,
    DSAKEYASN_IDX_P,
    DSAKEYASN_IDX_Q,
    DSAKEYASN_IDX_G,
    DSAKEYASN_IDX_Y,
    DSAKEYASN_IDX_X
};

/* Number of items in ASN.1 template for DSA private key. */
#define dsaKeyASN_Length (sizeof(dsaKeyASN) / sizeof(ASNItem))
/* Number of items in ASN.1 template for DSA public key. */
#define dsaPublicKeyASN_Length ((sizeof(dsaKeyASN) / sizeof(ASNItem)) - 2)

/* ASN.1 template for PublicKeyInfo with DSA.
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * RFC 3279, 2.3.2 - DSA in SubjectPublicKeyInfo
 */
static const ASNItem dsaPubKeyASN[] = {
/* SEQ             */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ALGOID_SEQ      */     { 1, ASN_SEQUENCE, 1, 1, 0 },
/* ALGOID_OID      */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* ALGOID_PARAMS   */         { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* p */
/* ALGOID_PARAMS_P */             { 3, ASN_INTEGER, 0, 0, 0 },
                                                   /* q */
/* ALGOID_PARAMS_Q */             { 3, ASN_INTEGER, 0, 0, 0 },
                                                   /* g */
/* ALGOID_PARAMS_G */             { 3, ASN_INTEGER, 0, 0, 0 },
/* PUBKEY_STR      */     { 1, ASN_BIT_STRING, 0, 1, 1 },
                                               /* y */
/* PUBKEY_Y        */         { 2, ASN_INTEGER, 0, 0, 0 },
};
enum {
    DSAPUBKEYASN_IDX_SEQ = 0,
    DSAPUBKEYASN_IDX_ALGOID_SEQ,
    DSAPUBKEYASN_IDX_ALGOID_OID,
    DSAPUBKEYASN_IDX_ALGOID_PARAMS,
    DSAPUBKEYASN_IDX_ALGOID_PARAMS_P,
    DSAPUBKEYASN_IDX_ALGOID_PARAMS_Q,
    DSAPUBKEYASN_IDX_ALGOID_PARAMS_G,
    DSAPUBKEYASN_IDX_PUBKEY_STR,
    DSAPUBKEYASN_IDX_PUBKEY_Y
};

/* Number of items in ASN.1 template for PublicKeyInfo with DSA. */
#define dsaPubKeyASN_Length (sizeof(dsaPubKeyASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Decode DSA public key.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * RFC 3279, 2.3.2 - DSA in SubjectPublicKeyInfo
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of DSA public key.
 *                            On out, start of ASN.1 item after DSA public key.
 * @param [in, out] key       DSA key object.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DsaPublicKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                          word32 inSz)
{
    /* dsaPubKeyASN is longer than dsaPublicKeyASN. */
    DECL_ASNGETDATA(dataASN, dsaPubKeyASN_Length);
    int ret = 0;

    /* Validated parameters. */
    if ((input == NULL) || (inOutIdx == NULL) || (key == NULL)) {
        return BAD_FUNC_ARG;
    }

    ALLOC_ASNGETDATA(dataASN, dsaPubKeyASN_Length, ret, key->heap);

    if (ret != 0)
        return ret;

    {
        int i;

        /* Clear dynamic data items. */
        XMEMSET(dataASN, 0, sizeof(ASNGetData) * dsaPublicKeyASN_Length);
        /* seq
         *   p, q, g, y
         * Start DSA ints from DSAKEYASN_IDX_VER instead of DSAKEYASN_IDX_P */
        for (i = 0; i < DSA_INTS - 1; i++)
            GetASN_MP(&dataASN[(int)DSAKEYASN_IDX_VER + i], GetDsaInt(key, i));
        /* Parse as simple form. */
        ret = GetASN_Items(dsaKeyASN, dataASN, dsaPublicKeyASN_Length, 0, input,
                           inOutIdx, inSz);
        if (ret != 0) {
            /* Clear dynamic data items. */
            XMEMSET(dataASN, 0, sizeof(ASNGetData) * dsaPubKeyASN_Length);
            /* Set DSA OID to expect. */
            GetASN_ExpBuffer(&dataASN[DSAPUBKEYASN_IDX_ALGOID_OID],
                    keyDsaOid, sizeof(keyDsaOid));
            /* p, q, g */
            for (i = 0; i < DSA_INTS - 2; i++)
                GetASN_MP(&dataASN[(int)DSAPUBKEYASN_IDX_ALGOID_PARAMS_P + i],
                        GetDsaInt(key, i));
            /* y */
            GetASN_MP(&dataASN[DSAPUBKEYASN_IDX_PUBKEY_Y], GetDsaInt(key, i));
            /* Parse as SubjectPublicKeyInfo. */
            ret = GetASN_Items(dsaPubKeyASN, dataASN, dsaPubKeyASN_Length, 1,
                input, inOutIdx, inSz);
        }
    }

    if (ret == 0) {
        /* Data parsed - set type of key parsed. */
        key->type = DSA_PUBLIC;
    }

    FREE_ASNGETDATA(dataASN, key->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
int wc_DsaParamsDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                        word32 inSz)
{
    int    length;
    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)
        return ASN_DH_KEY_E;

    return 0;
}


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for a DSA key holding private key in an OCTET_STRING. */
static const ASNItem dsaKeyOctASN[] = {
/*  SEQ      */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                /* p */
/*  P        */     { 1, ASN_INTEGER, 0, 0, 0 },
                /* q */
/*  Q        */     { 1, ASN_INTEGER, 0, 0, 0 },
                /* g */
/*  G        */     { 1, ASN_INTEGER, 0, 0, 0 },
                /* Private key */
/*  PKEY_STR */     { 1, ASN_OCTET_STRING, 0, 1, 0 },
                    /* x */
/*  X        */         { 2, ASN_INTEGER, 0, 0, 0 },
};
enum {
    DSAKEYOCTASN_IDX_SEQ = 0,
    DSAKEYOCTASN_IDX_P,
    DSAKEYOCTASN_IDX_Q,
    DSAKEYOCTASN_IDX_G,
    DSAKEYOCTASN_IDX_PKEY_STR,
    DSAKEYOCTASN_IDX_X
};

/* Number of items in ASN.1 template for a DSA key (OCTET_STRING version). */
#define dsaKeyOctASN_Length (sizeof(dsaKeyOctASN) / sizeof(ASNItem))
#endif

/* Decode DSA private key.
 *
 * @param [in]      input     Buffer holding BER encoded data.
 * @param [in, out] inOutIdx  On in, start of DSA public key.
 *                            On out, start of ASN.1 item after DSA public key.
 * @param [in, out] key       DSA key object.
 * @param [in]      inSz      Number of bytes in buffer.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_DsaPrivateKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key,
                           word32 inSz)
{
    /* dsaKeyASN is longer than dsaKeyOctASN. */
    DECL_ASNGETDATA(dataASN, dsaKeyASN_Length);
    int ret = 0;
    byte version = 0;

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

    if (ret == 0)
        CALLOC_ASNGETDATA(dataASN, dsaKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        int i;

        /* Try dsaKeyOctASN */
        /* Initialize key data and set mp_ints for params */
        for (i = 0; i < DSA_INTS - 2; i++) {
            GetASN_MP(&dataASN[(int)DSAKEYOCTASN_IDX_P + i], GetDsaInt(key, i));
        }
        /* and priv */
        GetASN_MP(&dataASN[DSAKEYOCTASN_IDX_X], GetDsaInt(key, i));
        /* Try simple form. */
        ret = GetASN_Items(dsaKeyOctASN, dataASN, dsaKeyOctASN_Length, 1, input,
                           inOutIdx, inSz);

        if (ret != 0) {
            /* Try dsaKeyASN */
            XMEMSET(dataASN, 0, sizeof(*dataASN) * dsaKeyASN_Length);
            GetASN_Int8Bit(&dataASN[DSAKEYASN_IDX_VER], &version);
            for (i = 0; i < DSA_INTS; i++) {
                mp_int* n = GetDsaInt(key, i);
                mp_clear(n);
                GetASN_MP(&dataASN[(int)DSAKEYASN_IDX_P + i], n);
            }

            /* Try simple OCTET_STRING form. */
            ret = GetASN_Items(dsaKeyASN, dataASN, dsaKeyASN_Length, 1, input,
                               inOutIdx, inSz);
        }
    }

    if (ret == 0) {
        /* Set the contents to be a private key. */
        key->type = DSA_PRIVATE;
    }

    FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#if !defined(HAVE_SELFTEST) && (defined(WOLFSSL_KEY_GEN) || \
        defined(WOLFSSL_CERT_GEN))
/* Encode a DSA public key into buffer.
 *
 * @param [out] output       Buffer to hold encoded data.
 * @param [in]  key          DSA key object.
 * @param [out] outLen       Length of buffer.
 * @param [out] with_header  Whether to encode in SubjectPublicKeyInfo block.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when output or key is NULL, or buffer size is less
 *          than a minimal size (5 bytes), or buffer size is smaller than
 *          encoding size.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_SetDsaPublicKey(byte* output, DsaKey* key, int outLen, int with_header)
{
    DECL_ASNSETDATA(dataASN, dsaPubKeyASN_Length);
    int ret = 0;
    int i;
    word32 sz = 0;
    const ASNItem *data = NULL;
    int count = 0;

    WOLFSSL_ENTER("wc_SetDsaPublicKey");

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

    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, dsaPubKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        if (with_header) {
            /* Using dsaPubKeyASN */
            data = dsaPubKeyASN;
            count = dsaPubKeyASN_Length;
            /* Set the algorithm OID to write out. */
            SetASN_OID(&dataASN[DSAPUBKEYASN_IDX_ALGOID_OID], DSAk, oidKeyType);
            /* Set the mp_ints to encode - parameters and public value. */
            for (i = 0; i < DSA_INTS - 2; i++) {
                SetASN_MP(&dataASN[(int)DSAPUBKEYASN_IDX_ALGOID_PARAMS_P + i],
                        GetDsaInt(key, i));
            }
            SetASN_MP(&dataASN[DSAPUBKEYASN_IDX_PUBKEY_Y], GetDsaInt(key, i));
        }
        else {
            /* Using dsaKeyASN */
            data = dsaKeyASN;
            count = dsaPublicKeyASN_Length;
            /* Set the mp_ints to encode - parameters and public value. */
            for (i = 0; i < DSA_INTS - 1; i++) {
                /* Move all DSA ints up one slot (ignore VERSION so now
                 * it means P) */
                SetASN_MP(&dataASN[(int)DSAKEYASN_IDX_VER + i],
                        GetDsaInt(key, i));
            }
        }
        ret = SizeASN_Items(data, dataASN, count, &sz);
    }
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (sz > (word32)outLen)) {
        ret = BAD_FUNC_ARG;
    }
    /* Encode the DSA public key into output buffer. */
    if (ret == 0) {
        ret = SetASN_Items(data, dataASN, count, output);
    }

    FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Encode a DSA public key into buffer.
 *
 * @param [out] output       Buffer to hold encoded data.
 * @param [in]  key          DSA key object.
 * @param [out] outLen       Length of buffer.
 * @param [out] with_header  Whether to encode in SubjectPublicKeyInfo block.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when output or key is NULL, or buffer size is less
 *          than a minimal size (5 bytes), or buffer size is smaller than
 *          encoding size.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wc_DsaKeyToPublicDer(DsaKey* key, byte* output, word32 outLen)
{
    return wc_SetDsaPublicKey(output, key, (int)outLen, 1);
}
#endif /* !HAVE_SELFTEST && (WOLFSSL_KEY_GEN || WOLFSSL_CERT_GEN) */

#ifdef WOLFSSL_ASN_TEMPLATE
static int DsaKeyIntsToDer(DsaKey* key, byte* output, word32* outLen,
                           int ints, int includeVersion)
{
    DECL_ASNSETDATA(dataASN, dsaKeyASN_Length);
    int ret = 0;
    word32 sz = 0;

    (void)ints;

    if ((key == NULL) || (outLen == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && (ints > DSA_INTS)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, dsaKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        int i;

        if (includeVersion) {
            /* Set the version. */
            SetASN_Int8Bit(&dataASN[DSAKEYASN_IDX_VER], 0);
        }
        else {
            dataASN[DSAKEYASN_IDX_VER].noOut = 1;
        }
        dataASN[DSAKEYASN_IDX_Y].noOut = mp_iszero(&key->y);
        dataASN[DSAKEYASN_IDX_X].noOut = mp_iszero(&key->x);
        /* Set the mp_ints to encode - params, public and private value. */
        for (i = 0; i < DSA_INTS; i++) {
            if (i < ints)
                SetASN_MP(&dataASN[(int)DSAKEYASN_IDX_P + i], GetDsaInt(key, i));
            else
                dataASN[(int)DSAKEYASN_IDX_P + i].noOut = 1;
        }
        /* Calculate size of the encoding. */
        ret = SizeASN_Items(dsaKeyASN, dataASN, dsaKeyASN_Length, &sz);
    }
    if ((ret == 0) && (output == NULL)) {
        *outLen = sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (sz > *outLen)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Encode the DSA private key into output buffer. */
        SetASN_Items(dsaKeyASN, dataASN, dsaKeyASN_Length, output);
        /* Return the size of the encoding. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Encode a DSA private key into buffer.
 *
 * @param [in]  key          DSA key object.
 * @param [out] output       Buffer to hold encoded data.
 * @param [out] outLen       Length of buffer.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key or output is NULL, or key is not a private key
 *          or, buffer size is smaller than encoding size.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int wc_DsaKeyToDer(DsaKey* key, byte* output, word32 outLen)
{
    if (!key || !output)
        return BAD_FUNC_ARG;

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

    return DsaKeyIntsToDer(key, output, &outLen, DSA_INTS, 1);
}

/* Convert DsaKey parameters to DER format, write to output (outLen),
   return bytes written. Version is excluded to be compatible with
   OpenSSL d2i_DSAparams */
int wc_DsaKeyToParamsDer(DsaKey* key, byte* output, word32 outLen)
{
    if (!key || !output)
        return BAD_FUNC_ARG;

    return DsaKeyIntsToDer(key, output, &outLen, DSA_PARAM_INTS, 0);
}

/* This version of the function allows output to be NULL. In that case, the
   DsaKeyIntsToDer will return WC_NO_ERR_TRACE(LENGTH_ONLY_E) and the required
   output buffer size will be pointed to by outLen. */
int wc_DsaKeyToParamsDer_ex(DsaKey* key, byte* output, word32* outLen)
{
    if (!key || !outLen)
        return BAD_FUNC_ARG;

    return DsaKeyIntsToDer(key, output, outLen, DSA_PARAM_INTS, 0);
}

#endif /* NO_DSA */

#ifndef NO_CERTS
/* Initialize decoded certificate object with buffer of DER encoding.
 *
 * @param [in, out] cert    Decoded certificate object.
 * @param [in]      source  Buffer containing DER encoded certificate.
 * @param [in]      inSz    Size of DER data in buffer in bytes.
 * @param [in]      heap    Dynamic memory hint.
 */
void InitDecodedCert(DecodedCert* cert,
                     const byte* source, word32 inSz, void* heap)
{
    InitDecodedCert_ex(cert, source, inSz, heap, INVALID_DEVID);
}


/* Initialize decoded certificate object with buffer of DER encoding.
 *
 * @param [in, out] cert    Decoded certificate object.
 * @param [in]      source  Buffer containing DER encoded certificate.
 * @param [in]      inSz    Size of DER data in buffer in bytes.
 * @param [in]      heap    Dynamic memory hint.
 * @param [in]      devId   Crypto callback ID to use.
 */
void InitDecodedCert_ex(DecodedCert* cert,
                     const byte* source, word32 inSz, void* heap, int devId)
{
    if (cert != NULL) {
        XMEMSET(cert, 0, sizeof(DecodedCert));

        cert->subjectCNEnc    = CTC_UTF8;
        cert->issuer[0]       = '\0';
        cert->subject[0]      = '\0';
        cert->source          = source;  /* don't own */
        cert->maxIdx          = inSz;    /* can't go over this index */
        cert->heap            = heap;
        cert->maxPathLen      = WOLFSSL_MAX_PATH_LEN;
    #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        #ifdef WOLFSSL_CERT_NAME_ALL
        cert->subjectNEnc     = CTC_UTF8;
        cert->subjectIEnc     = CTC_UTF8;
        cert->subjectDNQEnc   = CTC_UTF8;
        cert->subjectGNEnc    = CTC_UTF8;
        #endif
        cert->subjectSNEnc    = CTC_UTF8;
        cert->subjectCEnc     = CTC_PRINTABLE;
        cert->subjectLEnc     = CTC_UTF8;
        cert->subjectSTEnc    = CTC_UTF8;
        cert->subjectOEnc     = CTC_UTF8;
        cert->subjectOUEnc    = CTC_UTF8;
    #ifdef WOLFSSL_HAVE_ISSUER_NAMES
        cert->issuerSNEnc    = CTC_UTF8;
        cert->issuerCEnc     = CTC_PRINTABLE;
        cert->issuerLEnc     = CTC_UTF8;
        cert->issuerSTEnc    = CTC_UTF8;
        cert->issuerOEnc     = CTC_UTF8;
        cert->issuerOUEnc    = CTC_UTF8;
    #endif /* WOLFSSL_HAVE_ISSUER_NAMES */
    #endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_EXT */

        InitSignatureCtx(&cert->sigCtx, heap, devId);
    }
}

void wc_InitDecodedCert(DecodedCert* cert, const byte* source, word32 inSz,
                        void* heap)
{
    InitDecodedCert(cert, source, inSz, heap);
}

/* Free the alternative names object.
 *
 * Frees each linked list items and its name.
 *
 * @param [in, out] altNames  Alternative names.
 * @param [in]      heap      Dynamic memory hint.
 */
void FreeAltNames(DNS_entry* altNames, void* heap)
{
    (void)heap;

    while (altNames) {
        DNS_entry* tmp = altNames->next;

        if (altNames->nameStored) {
            /* safe cast -- .nameStored signifies that the pointer comes from
             * our own earlier XMALLOC().
             */
            XFREE((void *)(wc_ptr_t)altNames->name, heap,
                  DYNAMIC_TYPE_ALTNAME);
            altNames->nameStored = 0;
        }
    #ifdef WOLFSSL_IP_ALT_NAME
        if (altNames->ipStringStored) {
            /* safe cast -- .ipStringStored signifies that the pointer comes
             * from our own earlier XMALLOC().
             */
            XFREE((void *)(wc_ptr_t)altNames->ipString, heap,
                  DYNAMIC_TYPE_ALTNAME);
            altNames->ipStringStored = 0;
        }
    #endif
    #ifdef WOLFSSL_RID_ALT_NAME
        if (altNames->ridStringStored) {
            /* safe cast -- .ridStringStored signifies that the pointer comes
             * from our own earlier XMALLOC().
             */
            XFREE((void *)(wc_ptr_t)altNames->ridString, heap,
                  DYNAMIC_TYPE_ALTNAME);
            altNames->ridStringStored = 0;
        }
    #endif
        XFREE(altNames,       heap, DYNAMIC_TYPE_ALTNAME);
        altNames = tmp;
    }
}

/* malloc and initialize a new alt name structure */
DNS_entry* AltNameNew(void* heap)
{
    DNS_entry* ret;
    ret = (DNS_entry*)XMALLOC(sizeof(DNS_entry), heap, DYNAMIC_TYPE_ALTNAME);
    if (ret != NULL) {
        XMEMSET(ret, 0, sizeof(DNS_entry));
    }
    (void)heap;
    return ret;
}

DNS_entry* AltNameDup(DNS_entry* from, void* heap)
{
    DNS_entry* ret;

    ret = AltNameNew(heap);
    if (ret == NULL) {
        WOLFSSL_MSG("\tOut of Memory");
        return NULL;
    }

    ret->type = from->type;
    ret->len = from->len;


    ret->name = CopyString(from->name, from->len, heap, DYNAMIC_TYPE_ALTNAME);
    ret->nameStored = 1;
#ifdef WOLFSSL_IP_ALT_NAME
    ret->ipString = CopyString(from->ipString, 0, heap, DYNAMIC_TYPE_ALTNAME);
    ret->ipStringStored = 1;
#endif
#ifdef WOLFSSL_RID_ALT_NAME
    ret->ridString = CopyString(from->ridString, 0, heap, DYNAMIC_TYPE_ALTNAME);
    ret->ridStringStored = 1;
#endif
    if (ret->name == NULL
#ifdef WOLFSSL_IP_ALT_NAME
            || (from->ipString != NULL && ret->ipString == NULL)
#endif
#ifdef WOLFSSL_RID_ALT_NAME
            || (from->ridString != NULL && ret->ridString == NULL)
#endif
            ) {
        WOLFSSL_MSG("\tOut of Memory");
        FreeAltNames(ret, heap);
        return NULL;
    }

#ifdef WOLFSSL_FPKI
    ret->oidSum = from->oidSum;
#endif

    return ret;
}


#ifndef IGNORE_NAME_CONSTRAINTS

/* Free the subtree names object.
 *
 * Frees each linked list items and its name.
 *
 * @param [in, out] names  Subtree names.
 * @param [in]      heap   Dynamic memory hint.
 */
void FreeNameSubtrees(Base_entry* names, void* heap)
{
    (void)heap;

    while (names) {
        Base_entry* tmp = names->next;

        XFREE(names->name, heap, DYNAMIC_TYPE_ALTNAME);
        XFREE(names,       heap, DYNAMIC_TYPE_ALTNAME);
        names = tmp;
    }
}

#endif /* IGNORE_NAME_CONSTRAINTS */

/* Free the decoded cert object's dynamic data.
 *
 * @param [in, out] cert  Decoded certificate object.
 */
void FreeDecodedCert(DecodedCert* cert)
{
    if (cert == NULL)
        return;
    if (cert->subjectCNStored == 1) {
        /* safe cast -- .subjectCNStored signifies that the pointer comes from
         * our own earlier XMALLOC().
         */
        XFREE((void*)(wc_ptr_t)cert->subjectCN, cert->heap,
              DYNAMIC_TYPE_SUBJECT_CN);
    }
    if (cert->pubKeyStored == 1) {
        /* safe cast -- .pubKeyStored signifies that the pointer comes from our
         * own earlier XMALLOC().
         */
        XFREE((void*)(wc_ptr_t)cert->publicKey, cert->heap,
              DYNAMIC_TYPE_PUBLIC_KEY);
    }
    if (cert->weOwnAltNames && cert->altNames)
        FreeAltNames(cert->altNames, cert->heap);
#ifndef IGNORE_NAME_CONSTRAINTS
    if (cert->altEmailNames)
        FreeAltNames(cert->altEmailNames, cert->heap);
    if (cert->altDirNames)
        FreeAltNames(cert->altDirNames, cert->heap);
    if (cert->altOtherNamesRaw)
        FreeAltNames(cert->altOtherNamesRaw, cert->heap);
    if (cert->permittedNames)
        FreeNameSubtrees(cert->permittedNames, cert->heap);
    if (cert->excludedNames)
        FreeNameSubtrees(cert->excludedNames, cert->heap);
#endif /* IGNORE_NAME_CONSTRAINTS */
#ifdef WOLFSSL_SEP
    XFREE(cert->deviceType, cert->heap, DYNAMIC_TYPE_X509_EXT);
    XFREE(cert->hwType, cert->heap, DYNAMIC_TYPE_X509_EXT);
    XFREE(cert->hwSerialNum, cert->heap, DYNAMIC_TYPE_X509_EXT);
#endif /* WOLFSSL_SEP */
#ifdef WOLFSSL_X509_NAME_AVAILABLE
    if (cert->issuerName != NULL)
        wolfSSL_X509_NAME_free((WOLFSSL_X509_NAME*)cert->issuerName);
    if (cert->subjectName != NULL)
        wolfSSL_X509_NAME_free((WOLFSSL_X509_NAME*)cert->subjectName);
#endif /* WOLFSSL_X509_NAME_AVAILABLE */
#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    XFREE(cert->sce_tsip_encRsaKeyIdx, cert->heap, DYNAMIC_TYPE_RSA);
#endif
    FreeSignatureCtx(&cert->sigCtx);
}

void wc_FreeDecodedCert(DecodedCert* cert)
{
    FreeDecodedCert(cert);
}

#if defined(HAVE_ED25519) || defined(HAVE_ED448) || defined(HAVE_FALCON) || \
    defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA) || \
    defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
/* Store the key data under the BIT_STRING in dynamically allocated data.
 *
 * @param [in, out] cert    Certificate object.
 * @param [in]      source  Buffer containing encoded key.
 * @param [in, out] srcIdx  On in, start of key data.
 *                          On out, start of element after key data.
 * @param [in]      maxIdx  Maximum index of certificate data.
 */
static int StoreKey(DecodedCert* cert, const byte* source, word32* srcIdx,
                    word32 maxIdx)
{
    int ret;
    int length;
    byte* publicKey;

    ret = CheckBitString(source, srcIdx, &length, maxIdx, 1, NULL);
    if (ret == 0) {
    #ifdef HAVE_OCSP
        ret = CalcHashId_ex(source + *srcIdx, (word32)length,
            cert->subjectKeyHash, HashIdAlg(cert->signatureOID));
    }
    if (ret == 0) {
    #endif
        publicKey = (byte*)XMALLOC((size_t)length, cert->heap,
                                   DYNAMIC_TYPE_PUBLIC_KEY);
        if (publicKey == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMCPY(publicKey, &source[*srcIdx], (size_t)length);
            cert->publicKey = publicKey;
            cert->pubKeyStored = 1;
            cert->pubKeySize   = (word32)length;

#ifdef HAVE_OCSP_RESPONDER
            cert->publicKeyForHash = cert->publicKey;
            cert->pubKeyForHashSize = cert->pubKeySize;
#endif

            *srcIdx += (word32)length;
        }
    }

    return ret;
}
#endif /* HAVE_ED25519 || HAVE_ED448 */
#endif

#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)

static int SetCurve(ecc_key* key, byte* output, size_t outSz)
{
#ifdef HAVE_OID_ENCODING
    int ret;
#endif
    int idx;
    word32 oidSz = 0;

    /* validate key */
    if (key == NULL || key->dp == NULL) {
        return BAD_FUNC_ARG;
    }

#ifdef HAVE_OID_ENCODING
    ret = EncodeObjectId(key->dp->oid, key->dp->oidSz, NULL, &oidSz);
    if (ret != 0) {
        return ret;
    }
#else
    oidSz = key->dp->oidSz;
#endif

    idx = SetObjectId((int)oidSz, output);

    /* length only */
    if (output == NULL) {
        return idx + (int)oidSz;
    }

    /* verify output buffer has room */
    if (oidSz > outSz)
        return BUFFER_E;

#ifdef HAVE_OID_ENCODING
    ret = EncodeObjectId(key->dp->oid, key->dp->oidSz, output+idx, &oidSz);
    if (ret != 0) {
        return ret;
    }
#else
    XMEMCPY(output+idx, key->dp->oid, oidSz);
#endif
    idx += (int)oidSz;

    return idx;
}

#endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */

#ifdef HAVE_ECC
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for ECC public key (SubjectPublicKeyInfo).
 * RFC 5480, 2 - Subject Public Key Information Fields
 *           2.1.1 - Unrestricted Algorithm Identifier and Parameters
 * X9.62 ECC point format.
 * See ASN.1 template 'eccSpecifiedASN' for specifiedCurve.
 */
static const ASNItem eccPublicKeyASN[] = {
/* SEQ            */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                             /* AlgorithmIdentifier */
/* ALGOID_SEQ     */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                 /* algorithm */
/* ALGOID_OID     */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
                                                 /* namedCurve */
/* ALGOID_CURVEID */         { 2, ASN_OBJECT_ID, 0, 0, 2 },
                                                 /* specifiedCurve - explicit parameters */
/* ALGOID_PARAMS  */         { 2, ASN_SEQUENCE, 1, 0, 2 },
                                             /* Public Key */
/* PUBKEY         */     { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    ECCPUBLICKEYASN_IDX_SEQ = 0,
    ECCPUBLICKEYASN_IDX_ALGOID_SEQ,
    ECCPUBLICKEYASN_IDX_ALGOID_OID,
    ECCPUBLICKEYASN_IDX_ALGOID_CURVEID,
    ECCPUBLICKEYASN_IDX_ALGOID_PARAMS,
    ECCPUBLICKEYASN_IDX_PUBKEY
};

/* Number of items in ASN.1 template for ECC public key. */
#define eccPublicKeyASN_Length (sizeof(eccPublicKeyASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_ECC */

#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)

/* Encode public ECC key in DER format.
 *
 * RFC 5480, 2 - Subject Public Key Information Fields
 *           2.1.1 - Unrestricted Algorithm Identifier and Parameters
 * X9.62 ECC point format.
 * SEC 1 Ver. 2.0, C.2 - Syntax for Elliptic Curve Domain Parameters
 *
 * @param [out] output       Buffer to put encoded data in.
 * @param [in]  key          ECC key object.
 * @param [in]  outLen       Size of buffer in bytes.
 * @param [in]  with_header  Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key or key's parameters is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int SetEccPublicKey(byte* output, ecc_key* key, int outLen,
                           int with_header, int comp)
{
    word32 pubSz = 0;
    word32 sz = 0;
    int ret = 0;
    int curveIdSz = 0;
    byte* curveOid = NULL;

    /* Check key validity. */
    if ((key == NULL) || (key->dp == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        /* Calculate the size of the encoded public point. */
        PRIVATE_KEY_UNLOCK();
    #if defined(HAVE_COMP_KEY) && \
    (defined(HAVE_SELFTEST) || (defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0)))
        /* in earlier versions of FIPS the get length functionality is not
         * available with compressed keys */
        pubSz = key->dp ? key->dp->size : MAX_ECC_BYTES;
        if (comp)
            pubSz = 1 + pubSz;
        else
            pubSz = 1 + 2 * pubSz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    #else
        ret = wc_ecc_export_x963_ex(key, NULL, &pubSz, comp);
    #endif
        PRIVATE_KEY_LOCK();
        /* LENGTH_ONLY_E on success. */
        if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
            ret = 0;
        }
    }
    if ((ret == 0) && with_header) {
        /* Including SubjectPublicKeyInfo header. */
        DECL_ASNSETDATA(dataASN, eccPublicKeyASN_Length);

        CALLOC_ASNSETDATA(dataASN, eccPublicKeyASN_Length, ret, key->heap);

        /* Get the length of the named curve OID to put into the encoding. */
        curveIdSz = SetCurve(key, NULL, 0);
        if (curveIdSz < 0) {
            ret = curveIdSz;
        }

        if (ret == 0) {
            /* Set the key type OID. */
            SetASN_OID(&dataASN[ECCPUBLICKEYASN_IDX_ALGOID_OID], ECDSAk,
                    oidKeyType);
            /* Set the curve OID. */
            SetASN_ReplaceBuffer(&dataASN[ECCPUBLICKEYASN_IDX_ALGOID_CURVEID],
                NULL, (word32)curveIdSz);
            /* Don't try to write out explicit parameters. */
            dataASN[ECCPUBLICKEYASN_IDX_ALGOID_PARAMS].noOut = 1;
            /* Set size of public point to ensure space is made for it. */
            SetASN_Buffer(&dataASN[ECCPUBLICKEYASN_IDX_PUBKEY], NULL, pubSz);
            /* Calculate size of ECC public key. */
            ret = SizeASN_Items(eccPublicKeyASN, dataASN,
                                eccPublicKeyASN_Length, &sz);
        }

        /* Check buffer, if passed in, is big enough for encoded data. */
        if ((ret == 0) && (output != NULL) && (sz > (word32)outLen)) {
            ret = BUFFER_E;
        }
        if ((ret == 0) && (output != NULL)) {
            /* Encode ECC public key. */
            SetASN_Items(eccPublicKeyASN, dataASN, eccPublicKeyASN_Length,
                         output);
            /* Skip to where public point is to be encoded. */
            output += sz - pubSz;
            /* Cache the location to place the name curve OID. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            curveOid = (byte*)(wc_ptr_t)
                dataASN[ECCPUBLICKEYASN_IDX_ALGOID_CURVEID].data.buffer.data;
        }

        FREE_ASNSETDATA(dataASN, key->heap);
    }
    else if ((ret == 0) && (output != NULL) && (pubSz > (word32)outLen)) {
        ret = BUFFER_E;
    }
    else {
        /* Total size is the public point size. */
        sz = pubSz;
    }

    if ((ret == 0) && (output != NULL)) {
        /* Put named curve OID data into encoding. */
        curveIdSz = SetCurve(key, curveOid, (size_t)curveIdSz);
        if (curveIdSz < 0) {
            ret = curveIdSz;
        }
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode public point. */
        PRIVATE_KEY_UNLOCK();
        ret = wc_ecc_export_x963_ex(key, output, &pubSz, comp);
        PRIVATE_KEY_LOCK();
    }
    if (ret == 0) {
        /* Return the size of the encoding. */
        ret = (int)sz;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Encode the public part of an ECC key in a DER.
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  key            ECC key object.
 * @param [out] output         Buffer to hold DER encoding.
 * @param [in]  outLen         Size of buffer in bytes.
 * @param [in]  with_AlgCurve  Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key or key's parameters is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
WOLFSSL_ABI
int wc_EccPublicKeyToDer(ecc_key* key, byte* output, word32 outLen,
                                                              int with_AlgCurve)
{
    return SetEccPublicKey(output, key, (int)outLen, with_AlgCurve, 0);
}

int wc_EccPublicKeyToDer_ex(ecc_key* key, byte* output, word32 outLen,
                                                    int with_AlgCurve, int comp)
{
    return SetEccPublicKey(output, key, (int)outLen, with_AlgCurve, comp);
}

int wc_EccPublicKeyDerSize(ecc_key* key, int with_AlgCurve)
{
    return SetEccPublicKey(NULL, key, 0, with_AlgCurve, 0);
}

#endif /* HAVE_ECC && HAVE_ECC_KEY_EXPORT */

#ifdef WOLFSSL_ASN_TEMPLATE
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) || defined(WC_ENABLE_ASYM_KEY_IMPORT)
/* ASN.1 template for the SubjectPublicKeyInfo of a general asymmetric key.
 * Used with Ed448/Ed25519, Curve448/Curve25519, SLH-DSA, falcon, dilithium,
 * etc.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * RFC 8410, 4 - Subject Public Key Fields
 */
static const ASNItem publicKeyASN[] = {
            /* SubjectPublicKeyInfo */
/* SEQ        */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                     /* AlgorithmIdentifier */
/* ALGOID_SEQ */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                         /* Ed25519/Ed448 OID, etc. */
/* ALGOID_OID */         { 2, ASN_OBJECT_ID, 0, 0, 1 },
                                     /* Public key stream */
/* PUBKEY     */     { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    PUBKEYASN_IDX_SEQ = 0,
    PUBKEYASN_IDX_ALGOID_SEQ,
    PUBKEYASN_IDX_ALGOID_OID,
    PUBKEYASN_IDX_PUBKEY
};

/* Number of items in ASN.1 template for public key SubjectPublicKeyInfo. */
#define publicKeyASN_Length (sizeof(publicKeyASN) / sizeof(ASNItem))
#endif /* WC_ENABLE_ASYM_KEY_EXPORT || WC_ENABLE_ASYM_KEY_IMPORT */
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WC_ENABLE_ASYM_KEY_EXPORT

/* Build ASN.1 formatted public key based on RFC 5280 and RFC 8410
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  pubKey       public key buffer
 * @param [in]  pubKeyLen    public key buffer length
 * @param [out] output       Buffer to put encoded data in (optional)
 * @param [in]  outLen       Size of buffer in bytes
 * @param [in]  keyType      is "enum Key_Sum" like ED25519k
 * @param [in]  withHeader   Whether to include SubjectPublicKeyInfo around key.
 * @return  Size of encoded data in bytes on success
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int SetAsymKeyDerPublic(const byte* pubKey, word32 pubKeyLen,
    byte* output, word32 outLen, int keyType, int withHeader)
{
    int ret = 0;
    word32 sz = 0;
    DECL_ASNSETDATA(dataASN, publicKeyASN_Length);

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

    if (withHeader) {
        CALLOC_ASNSETDATA(dataASN, publicKeyASN_Length, ret, NULL);

        if (ret == 0) {
            /* Set the OID. */
            SetASN_OID(&dataASN[PUBKEYASN_IDX_ALGOID_OID], (word32)keyType,
                    oidKeyType);
            /* Leave space for public point. */
            SetASN_Buffer(&dataASN[PUBKEYASN_IDX_PUBKEY], NULL, pubKeyLen);
            /* Calculate size of public key encoding. */
            ret = SizeASN_Items(publicKeyASN, dataASN, publicKeyASN_Length,
                                &sz);
        }
        if ((ret == 0) && (output != NULL) && (sz > outLen)) {
            ret = BUFFER_E;
        }
        if ((ret == 0) && (output != NULL)) {
            /* Encode public key. */
            SetASN_Items(publicKeyASN, dataASN, publicKeyASN_Length, output);
            /* Set location to encode public point. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            output = (byte*)(wc_ptr_t)
                dataASN[PUBKEYASN_IDX_PUBKEY].data.buffer.data;
        }

        FREE_ASNSETDATA(dataASN, NULL);
    }
    else if ((output != NULL) && (pubKeyLen > outLen)) {
        ret = BUFFER_E;
    }
    else if (ret == 0) {
        sz = pubKeyLen;
    }

    if ((ret == 0) && (output != NULL)) {
        /* Put public key into space provided. */
        XMEMCPY(output, pubKey, pubKeyLen);
    }
    if (ret == 0) {
        ret = (int)sz;
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WC_ENABLE_ASYM_KEY_EXPORT */

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
/* Encode the public part of an Ed25519 key in DER.
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  key       Ed25519 key object.
 * @param [out] output    Buffer to put encoded data in.
 * @param [in]  outLen    Size of buffer in bytes.
 * @param [in]  withAlg   Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
int wc_Ed25519PublicKeyToDer(const ed25519_key* key, byte* output, word32 outLen,
                             int withAlg)
{
    int    ret;
    byte   pubKey[ED25519_PUB_KEY_SIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

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

    #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
    ret = wc_ed25519_export_public((ed25519_key *)key, pubKey, &pubKeyLen);
    #else
    ret = wc_ed25519_export_public(key, pubKey, &pubKeyLen);
    #endif
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            ED25519k, withAlg);
    }
    return ret;
}
#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT */

#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)
/* Encode the public part of an Ed448 key in DER.
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  key       Ed448 key object.
 * @param [out] output    Buffer to put encoded data in.
 * @param [in]  outLen    Size of buffer in bytes.
 * @param [in]  withAlg   Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
int wc_Ed448PublicKeyToDer(const ed448_key* key, byte* output, word32 outLen,
                           int withAlg)
{
    int    ret;
    byte   pubKey[ED448_PUB_KEY_SIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

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

    #if defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
    ret = wc_ed448_export_public((ed448_key *)key, pubKey, &pubKeyLen);
    #else
    ret = wc_ed448_export_public(key, pubKey, &pubKeyLen);
    #endif
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            ED448k, withAlg);
    }
    return ret;
}
#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT */

#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
/* Encode the public part of an LMS/HSS key in DER.
 *
 * Per RFC 9802, the SubjectPublicKeyInfo for HSS/LMS uses the
 * id-alg-hss-lms-hashsig OID and carries the raw HSS public key in the
 * BIT STRING with no additional wrapping.
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  key       LMS key object.
 * @param [out] output    Buffer to put encoded data in.
 * @param [in]  outLen    Size of buffer in bytes.
 * @param [in]  withAlg   Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 */
int wc_LmsKey_PublicKeyToDer(const LmsKey* key, byte* output, word32 outLen,
                             int withAlg)
{
    int    ret;
    byte   pubKey[HSS_MAX_PUBLIC_KEY_LEN];
    word32 pubKeyLen = (word32)sizeof(pubKey);

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

    ret = wc_LmsKey_ExportPubRaw(key, pubKey, &pubKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            HSS_LMSk, withAlg);
    }
    return ret;
}
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */

#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
/* Encode the public part of an XMSS/XMSS^MT key in DER.
 *
 * Per RFC 9802, the SubjectPublicKeyInfo for XMSS/XMSS^MT uses the
 * id-alg-xmss-hashsig / id-alg-xmssmt-hashsig OID and carries the raw
 * public key in the BIT STRING with no additional wrapping.
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  key       XMSS key object.
 * @param [out] output    Buffer to put encoded data in.
 * @param [in]  outLen    Size of buffer in bytes.
 * @param [in]  withAlg   Whether to use SubjectPublicKeyInfo format.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 */
int wc_XmssKey_PublicKeyToDer(const XmssKey* key, byte* output, word32 outLen,
                              int withAlg)
{
    int    ret;
    byte   pubKey[2 * WC_XMSS_MAX_N + XMSS_OID_LEN];
    word32 pubKeyLen = (word32)sizeof(pubKey);
    int    keyType;

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

    keyType = key->is_xmssmt ? XMSSMTk : XMSSk;

    ret = wc_XmssKey_ExportPubRaw(key, pubKey, &pubKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            keyType, withAlg);
    }
    return ret;
}
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */

#if !defined(NO_RSA) && !defined(NO_CERTS)
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for header before RSA key in certificate. */
static const ASNItem rsaCertKeyASN[] = {
/* STR */ { 0, ASN_BIT_STRING, 0, 1, 0 },
/* SEQ */     { 1, ASN_SEQUENCE, 1, 0, 0 },
};
enum {
    RSACERTKEYASN_IDX_STR = 0,
    RSACERTKEYASN_IDX_SEQ
};

/* Number of items in ASN.1 template for header before RSA key in cert. */
#define rsaCertKeyASN_Length (sizeof(rsaCertKeyASN) / sizeof(ASNItem))
#endif

/* Store RSA key pointer and length in certificate object.
 *
 * @param [in, out] cert    Certificate object.
 * @param [in]      source  Buffer containing encoded key.
 * @param [in, out] srcIdx  On in, start of RSA key data.
 *                          On out, start of element after RSA key data.
 * @param [in]      maxIdx  Maximum index of key data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int StoreRsaKey(DecodedCert* cert, const byte* source, word32* srcIdx,
                       word32 maxIdx)
{
    ASNGetData dataASN[rsaCertKeyASN_Length];
    int ret;

    /* No dynamic data. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    /* Decode the header before the key data. */
    ret = GetASN_Items(rsaCertKeyASN, dataASN, rsaCertKeyASN_Length, 1, source,
                       srcIdx, maxIdx);
    if (ret == 0) {
        /* Store the pointer and length in certificate object starting at
         * SEQUENCE. */
        GetASN_GetConstRef(&dataASN[RSACERTKEYASN_IDX_STR],
                &cert->publicKey, &cert->pubKeySize);

#ifdef WOLFSSL_MAXQ10XX_TLS
    cert->publicKeyIndex = dataASN[RSACERTKEYASN_IDX_SEQ].offset;
#endif

    #if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
        /* Start of SEQUENCE. */
        cert->sigCtx.CertAtt.pubkey_n_start =
            cert->sigCtx.CertAtt.pubkey_e_start = dataASN[RSACERTKEYASN_IDX_SEQ].offset;
    #endif
    #ifdef HAVE_OCSP_RESPONDER
        cert->publicKeyForHash = cert->publicKey;
        cert->pubKeyForHashSize = cert->pubKeySize;
    #endif
    #ifdef HAVE_OCSP
        /* Calculate the hash of the public key for OCSP. */
        ret = CalcHashId_ex(cert->publicKey, cert->pubKeySize,
                         cert->subjectKeyHash, HashIdAlg(cert->signatureOID));
    #endif
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_RSA && !NO_CERTS */

#if defined(HAVE_ECC) && !defined(NO_CERTS)

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for header before ECC key in certificate. */
static const ASNItem eccCertKeyASN[] = {
/* OID        */     { 1, ASN_OBJECT_ID, 0, 0, 2 },
                            /* Algo parameters */
/* PARAMS     */     { 1, ASN_SEQUENCE, 1, 0, 2 },
                            /* Subject public key */
/* SUBJPUBKEY */ { 0, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    ECCCERTKEYASN_IDX_OID = 0,
    ECCCERTKEYASN_IDX_PARAMS,
    ECCCERTKEYASN_IDX_SUBJPUBKEY
};

/* Number of items in ASN.1 template for header before ECC key in cert. */
#define eccCertKeyASN_Length (sizeof(eccCertKeyASN) / sizeof(ASNItem))

#ifdef WOLFSSL_CUSTOM_CURVES
static int EccSpecifiedECDomainDecode(const byte* input, word32 inSz,
                                      ecc_key* key, void* heap, int* curveSz);
#endif /* WOLFSSL_CUSTOM_CURVES */
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Store public ECC key in certificate object.
 *
 * Parse parameters and store public key data.
 *
 * @param [in, out] cert       Certificate object.
 * @param [in]      source     Buffer containing encoded key.
 * @param [in, out] srcIdx     On in, start of ECC key data.
 *                             On out, start of element after ECC key data.
 * @param [in]      maxIdx     Maximum index of key data.
 * @param [in]      pubKey     Buffer holding encoded public key.
 * @param [in]      pubKeyLen  Length of encoded public key in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when pubKey is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx,
                       word32 maxIdx, const byte* pubKey, word32 pubKeyLen)
{
    int ret = 0;
    DECL_ASNGETDATA(dataASN, eccCertKeyASN_Length);
    byte* publicKey;

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

    /* Clear dynamic data and check OID is a curve. */
    CALLOC_ASNGETDATA(dataASN, eccCertKeyASN_Length, ret, cert->heap);
    if (ret == 0) {
        GetASN_OID(&dataASN[ECCCERTKEYASN_IDX_OID], oidCurveType);
        /* Parse ECC public key header. */
        ret = GetASN_Items(eccCertKeyASN, dataASN, eccCertKeyASN_Length, 1,
                source, srcIdx, maxIdx);
    }
    if (ret == 0) {
        if (dataASN[ECCCERTKEYASN_IDX_OID].tag != 0) {
            /* Store curve OID. */
            cert->pkCurveOID = dataASN[ECCCERTKEYASN_IDX_OID].data.oid.sum;
        }
        else {
    #ifdef WOLFSSL_CUSTOM_CURVES
            /* Parse explicit parameters. */
            ret = EccSpecifiedECDomainDecode(
                    dataASN[ECCCERTKEYASN_IDX_PARAMS].data.ref.data,
                    dataASN[ECCCERTKEYASN_IDX_PARAMS].data.ref.length, NULL,
                    NULL, &cert->pkCurveSize);
    #else
            /* Explicit parameters not supported in build configuration. */
            ret = ASN_PARSE_E;
    #endif
        }
    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
        cert->sigCtx.CertAtt.pubkey_n_start =
           cert->sigCtx.CertAtt.pubkey_e_start =
            GetASNItem_DataIdx(
                dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY], source) + 1;
        cert->sigCtx.CertAtt.pubkey_n_len =
            ((dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.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 =
            GetASNItem_DataIdx(dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY], source)
            + 1;
    #endif

    #ifdef HAVE_OCSP_RESPONDER
        cert->publicKeyForHash =
                dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.data;
        cert->pubKeyForHashSize =
                dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.length;
    #endif

    #ifdef HAVE_OCSP
        if (ret == 0) {
            /* Calculate the hash of the subject public key for OCSP. */
            ret = CalcHashId_ex(
                    dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.data,
                    dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.length,
                    cert->subjectKeyHash, HashIdAlg(cert->signatureOID));
        }
    }
    if (ret == 0) {
    #endif
        /* Store public key data length. */
        cert->pubKeySize = pubKeyLen;
        /* Must allocated space for key.
         * Don't memcpy into constant pointer so use temp. */
        publicKey = (byte*)XMALLOC(cert->pubKeySize, cert->heap,
                                   DYNAMIC_TYPE_PUBLIC_KEY);
        if (publicKey == NULL) {
            ret = MEMORY_E;
        }
        else {
            /* Copy in whole public key and store pointer. */
            XMEMCPY(publicKey, pubKey, cert->pubKeySize);
            cert->publicKey = publicKey;
            /* Indicate publicKey needs to be freed. */
            cert->pubKeyStored = 1;
        }
    }
    FREE_ASNGETDATA(dataASN, cert->heap);

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_ECC && !NO_CERTS */

#ifndef NO_CERTS
#if !defined(NO_DSA)
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for DSA key in certificate.
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * RFC 3279, 2.3.2 - DSA in SubjectPublicKeyInfo
 */
static const ASNItem dsaCertKeyASN[] = {
/*  0 */        { 1, ASN_SEQUENCE, 1, 1, 0 },
/*  1 */            { 2, ASN_INTEGER, 0, 0, 0 },
/*  2 */            { 2, ASN_INTEGER, 0, 0, 0 },
/*  3 */            { 2, ASN_INTEGER, 0, 0, 0 },
/*  4 */    { 0, ASN_BIT_STRING, 0, 1, 0 },
/*  5 */        { 1, ASN_INTEGER, 0, 0, 0 },
};

/* Number of items in ASN.1 template for DSA key in certificate. */
#define dsaCertKeyASN_Length (sizeof(dsaCertKeyASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Parse DSA parameters to ensure valid.
 *
 * @param [in]      source  Buffer containing encoded key.
 * @param [in, out] srcIdx  On in, start of DSA key data.
 *                          On out, start of element after DSA key data.
 * @param [in]      maxIdx  Maximum index of key data.
 * @param [in]      heap    Dynamic memory hint.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int ParseDsaKey(const byte* source, word32* srcIdx, word32 maxIdx,
                       void* heap)
{
    DECL_ASNGETDATA(dataASN, dsaCertKeyASN_Length);
    int ret = 0;

    (void)heap;

    CALLOC_ASNGETDATA(dataASN, dsaCertKeyASN_Length, ret, heap);
    if (ret == 0) {
        /* Parse the DSA key data to ensure valid. */
        ret = GetASN_Items(dsaCertKeyASN, dataASN, dsaCertKeyASN_Length, 1,
                           source, srcIdx, maxIdx);
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_DSA */

/* Decode the SubjectPublicKeyInfo block in a certificate.
 *
 * Stores the public key in fields of the certificate object.
 * Validates the BER/DER items and does not store in a key object.
 *
 * @param [in, out] cert      Decoded certificate object.
 * @param [in]      source    BER/DER encoded SubjectPublicKeyInfo block.
 * @param [in, out] inOutIdx  On in, start of public key.
 *                            On out, start of ASN.1 item after public key.
 * @param [in]      maxIdx    Maximum index of key data.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 */
static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx,
                      word32 maxIdx)
{
    word32 srcIdx = *inOutIdx;
#if defined(HAVE_ECC) || !defined(NO_DSA)
    int pubLen;
#endif
#if defined(HAVE_ECC) || !defined(NO_DSA)
    int pubIdx = (int)srcIdx;
#endif
    int ret = 0;
    int length;

    /* Validate parameters. */
    if (source == NULL) {
        return ASN_PARSE_E;
    }

#ifndef WOLFSSL_ASN_TEMPLATE
    if (GetSequence(source, &srcIdx, &length, maxIdx) < 0)
#else
    /* Get SEQUENCE and expect all data to be accounted for. */
    if (GetASN_Sequence(source, &srcIdx, &length, maxIdx, 1) != 0)
#endif
    {
        return ASN_PARSE_E;
    }

#if defined(HAVE_ECC) || !defined(NO_DSA)
    pubLen = (int)srcIdx - pubIdx + length;
#endif
    maxIdx = srcIdx + (word32)length;

    /* Decode the algorithm identifier for the key. */
    if (GetAlgoId(source, &srcIdx, &cert->keyOID, oidKeyType, maxIdx) < 0) {
        return ASN_PARSE_E;
    }

    (void)length;

    /* Parse each type of public key. */
    switch (cert->keyOID) {
#ifndef NO_RSA
    #ifdef WC_RSA_PSS
        case RSAPSSk:
            if (srcIdx != maxIdx &&
                          source[srcIdx] == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
                word32 seqIdx = srcIdx;
                int seqLen;
                /* Not set when -1. */
                enum wc_HashType hash = WC_HASH_TYPE_NONE;
                int mgf = -1;
                int saltLen = 0;
                /* Defaults for sig algorithm parameters. */
                enum wc_HashType sigHash = WC_HASH_TYPE_SHA;
                int sigMgf = WC_MGF1SHA1;
                int sigSaltLen = 20;

                if (GetSequence(source, &srcIdx, &seqLen, maxIdx) < 0) {
                    return ASN_PARSE_E;
                }
                /* Get the pubic key parameters. */
                ret = DecodeRsaPssParams(source + seqIdx,
                    (word32)seqLen + srcIdx - seqIdx, &hash, &mgf, &saltLen);
                if (ret != 0) {
                    return ASN_PARSE_E;
                }
                /* Get the signature parameters. */
                ret = DecodeRsaPssParams(source + cert->sigParamsIndex,
                    cert->sigParamsLength, &sigHash, &sigMgf, &sigSaltLen);
                if (ret != 0) {
                    return ASN_PARSE_E;
                }
                /* Validated signature params match public key params. */
                if (hash != WC_HASH_TYPE_NONE && hash != sigHash) {
                    WOLFSSL_MSG("RSA PSS: hash not matching signature hash");
                    return ASN_PARSE_E;
                }
                if (mgf != -1 && mgf != sigMgf) {
                    WOLFSSL_MSG("RSA PSS: MGF not matching signature MGF");
                    return ASN_PARSE_E;
                }
                if (saltLen > sigSaltLen) {
                    WOLFSSL_MSG("RSA PSS: sig salt length too small");
                    return ASN_PARSE_E;
                }
                srcIdx += (word32)seqLen;
            }
            FALL_THROUGH;
    #endif /* WC_RSA_PSS */
        case RSAk:
            ret = StoreRsaKey(cert, source, &srcIdx, maxIdx);
            break;
#endif /* NO_RSA */
    #ifdef HAVE_ECC
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        case SM2k:
    #endif
        case ECDSAk:
            ret = StoreEccKey(cert, source, &srcIdx, maxIdx, source + pubIdx,
                              (word32)pubLen);
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case ED25519k:
            cert->pkCurveOID = ED25519k;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* HAVE_ED25519 */
    #ifdef HAVE_ED448
        case ED448k:
            cert->pkCurveOID = ED448k;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* HAVE_ED448 */
    #ifdef HAVE_FALCON
        case FALCON_LEVEL1k:
            cert->pkCurveOID = FALCON_LEVEL1k;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
        case FALCON_LEVEL5k:
            cert->pkCurveOID = FALCON_LEVEL5k;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* HAVE_FALCON */
    #ifdef WOLFSSL_HAVE_MLDSA
        #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case DILITHIUM_LEVEL2k:
        case DILITHIUM_LEVEL3k:
        case DILITHIUM_LEVEL5k:
        #endif
        case ML_DSA_44k:
        case ML_DSA_65k:
        case ML_DSA_87k:
            cert->pkCurveOID = cert->keyOID;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* WOLFSSL_HAVE_MLDSA */
    #ifdef WOLFSSL_HAVE_SLHDSA
        case SLH_DSA_SHAKE_128Fk:
        case SLH_DSA_SHAKE_192Fk:
        case SLH_DSA_SHAKE_256Fk:
        case SLH_DSA_SHAKE_128Sk:
        case SLH_DSA_SHAKE_192Sk:
        case SLH_DSA_SHAKE_256Sk:
        case SLH_DSA_SHA2_128Fk:
        case SLH_DSA_SHA2_192Fk:
        case SLH_DSA_SHA2_256Fk:
        case SLH_DSA_SHA2_128Sk:
        case SLH_DSA_SHA2_192Sk:
        case SLH_DSA_SHA2_256Sk:
            cert->pkCurveOID = cert->keyOID;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* WOLFSSL_HAVE_SLHDSA */
    #ifdef WOLFSSL_HAVE_LMS
        case HSS_LMSk:
            cert->pkCurveOID = HSS_LMSk;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* WOLFSSL_HAVE_LMS */
    #ifdef WOLFSSL_HAVE_XMSS
        case XMSSk:
            cert->pkCurveOID = XMSSk;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
        case XMSSMTk:
            cert->pkCurveOID = XMSSMTk;
            ret = StoreKey(cert, source, &srcIdx, maxIdx);
            break;
    #endif /* WOLFSSL_HAVE_XMSS */
    #ifndef NO_DSA
        case DSAk:
            cert->publicKey = source + pubIdx;
            cert->pubKeySize = (word32)pubLen;
            ret = ParseDsaKey(source, &srcIdx, maxIdx, cert->heap);
            break;
    #endif /* NO_DSA */
        default:
            WOLFSSL_MSG("Unknown or not compiled in key OID");
            WOLFSSL_ERROR_VERBOSE(ASN_UNKNOWN_OID_E);
            ret = ASN_UNKNOWN_OID_E;
    }

    /* Return index after public key. */
    *inOutIdx = srcIdx;

    /* Return error code. */
    return ret;
}
#endif

/* Return the hash algorithm to use with the signature algorithm.
 *
 * @param [in] oidSum  Signature id.
 * @return  Hash algorithm id.
 */
int HashIdAlg(word32 oidSum)
{
    (void)oidSum;

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if (oidSum == CTC_SM3wSM2) {
        return WC_SM3;
    }
    if (oidSum == SM2k) {
        return WC_SM3;
    }
#endif
#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256))
    return WC_SHA256;
#else
    return WC_SHA;
#endif
}

/* Calculate hash of the id using the SHA-1 or SHA-256.
 *
 * @param [in]  data  Data to hash.
 * @param [in]  len   Length of data to hash.
 * @param [out] hash  Buffer to hold hash.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int CalcHashId(const byte* data, word32 len, byte* hash)
{
    /* Use default hash algorithm. */
    return CalcHashId_ex(data, len, hash,
#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256))
        WC_SHA256
#else
        WC_SHA
#endif
        );
}

/* Calculate hash of the id using the SHA-1 or SHA-256.
 *
 * @param [in]  data  Data to hash.
 * @param [in]  len   Length of data to hash.
 * @param [out] hash  Buffer to hold hash.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int CalcHashId_ex(const byte* data, word32 len, byte* hash, int hashAlg)
{
    int ret;

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if (hashAlg == WC_SM3) {
        ret = wc_Sm3Hash(data, len, hash);
    }
    else
#endif
#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256))
    if (hashAlg == WC_SHA256) {
        ret = wc_Sha256Hash(data, len, hash);
    }
    else
#elif !defined(NO_SHA)
    if (hashAlg == WC_SHA) {
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        XMEMSET(hash + WC_SHA_DIGEST_SIZE, 0, KEYID_SIZE - WC_SHA_DIGEST_SIZE);
    #endif
        ret = wc_ShaHash(data, len, hash);
    }
    else
#else
    (void)data;
    (void)len;
    (void)hash;
#endif
    {
        ret = NOT_COMPILED_IN;
    }

    return ret;
}

#ifndef NO_CERTS
/* Get the hash of the id using the SHA-1 or SHA-256.
 *
 * If the id is not the length of the hash, then hash it.
 *
 * @param [in]  id    Id to get hash for.
 * @param [in]  len   Length of id in bytes.
 * @param [out] hash  Buffer to hold hash.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
int GetHashId(const byte* id, int length, byte* hash, int hashAlg)
{
    int ret;

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if (length == wc_HashGetDigestSize(wc_HashTypeConvert(hashAlg)))
#else
    if (length == KEYID_SIZE)
#endif
    {
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        XMEMSET(hash + length, 0, KEYID_SIZE - length);
    #endif
        XMEMCPY(hash, id, (size_t)length);
        ret = 0;
    }
    else {
        ret = CalcHashId_ex(id, (word32)length, hash, hashAlg);
    }

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

#ifdef WOLFSSL_ASN_TEMPLATE
/* Id for email address. */
#define ASN_EMAIL     0x100
/* Id for domain component. */
#define ASN_DC        0x102
/* Id for jurisdiction country. */
#define ASN_JURIS_C   0x203
/* Id for jurisdiction state. */
#define ASN_JURIS_ST  0x202

/* Set the string for a name component into the subject name. */
#define SetCertNameSubject(cert, id, val) \
    *((const char**)(((byte *)(cert)) + certNameSubject[(id) - 3].data)) = \
        (val)
/* Set the string length for a name component into the subject name. */
#define SetCertNameSubjectLen(cert, id, val) \
    *((int*)(((byte *)(cert)) + certNameSubject[(id) - 3].len)) = (int)(val)
/* Set the encoding for a name component into the subject name. */
#define SetCertNameSubjectEnc(cert, id, val) \
    *((byte*)(((byte *)(cert)) + certNameSubject[(id) - 3].enc)) = (val)

/* Get the string of a name component from the subject name. */
#ifdef WOLFSSL_NAMES_STATIC
    #define GetCertNameSubjectStr(id) \
        ((certNameSubject[(id) - 3].strLen) ? \
         (certNameSubject[(id) - 3].str) : \
         NULL)
#else
    #define GetCertNameSubjectStr(id) \
        (certNameSubject[(id) - 3].str)
#endif
/* Get the string length of a name component from the subject name. */
#define GetCertNameSubjectStrLen(id) \
    (certNameSubject[(id) - 3].strLen)
/* Get the NID of a name component from the subject name. */
#define GetCertNameSubjectNID(id) \
    (certNameSubject[(id) - 3].nid)

#define ValidCertNameSubject(id) \
    (((id) - 3) >= 0 && ((id) - 3) < certNameSubjectSz && \
            (certNameSubject[(id) - 3].strLen > 0))

/* Set the string for a name component into the issuer name. */
#define SetCertNameIssuer(cert, id, val) \
    *((const char**)(((byte *)(cert)) + certNameSubject[(id) - 3].dataI)) = \
        (val)
/* Set the string length for a name component into the issuer name. */
#define SetCertNameIssuerLen(cert, id, val) \
    *((int*)(((byte *)(cert)) + certNameSubject[(id) - 3].lenI)) = (int)(val)
/* Set the encoding for a name component into the issuer name. */
#define SetCertNameIssuerEnc(cert, id, val) \
    *((byte*)(((byte *)(cert)) + certNameSubject[(id) - 3].encI)) = (val)


/* Mapping of certificate name component to useful information. */
typedef struct CertNameData {
    /* Type string of name component. */
#ifdef WOLFSSL_NAMES_STATIC
    const char str[20]; /* large enough for largest string in certNameSubject[]
                         * below
                         */
    #define EMPTY_STR { 0 }
#else
    const char* str;
    #define EMPTY_STR NULL
#endif
    /* Length of type string of name component. */
    byte        strLen;
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
    /* Offset of data in subject name component. */
    size_t      data;
    /* Offset of length in subject name component. */
    size_t      len;
    /* Offset of encoding in subject name component. */
    size_t      enc;
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
    /* Offset of data in subject name component. */
    size_t      dataI;
    /* Offset of length in subject name component. */
    size_t      lenI;
    /* Offset of encoding in subject name component. */
    size_t      encI;
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
    /* NID of type for subject name component. */
    int         nid;
#endif
} CertNameData;

/* List of data for common name components. */
static const CertNameData certNameSubject[] = {
    /* Common Name */
    {
        "/CN=", 4,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectCN),
        WC_OFFSETOF(DecodedCert, subjectCNLen),
        WC_OFFSETOF(DecodedCert, subjectCNEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerCN),
        WC_OFFSETOF(DecodedCert, issuerCNLen),
        WC_OFFSETOF(DecodedCert, issuerCNEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_commonName
#endif
    },
    /* Surname */
    {
        "/SN=", 4,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectSN),
        WC_OFFSETOF(DecodedCert, subjectSNLen),
        WC_OFFSETOF(DecodedCert, subjectSNEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerSN),
        WC_OFFSETOF(DecodedCert, issuerSNLen),
        WC_OFFSETOF(DecodedCert, issuerSNEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_surname
#endif
    },
    /* Serial Number */
    {
        "/serialNumber=", 14,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectSND),
        WC_OFFSETOF(DecodedCert, subjectSNDLen),
        WC_OFFSETOF(DecodedCert, subjectSNDEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerSND),
        WC_OFFSETOF(DecodedCert, issuerSNDLen),
        WC_OFFSETOF(DecodedCert, issuerSNDEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_serialNumber
#endif
    },
    /* Country Name */
    {
        "/C=", 3,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectC),
        WC_OFFSETOF(DecodedCert, subjectCLen),
        WC_OFFSETOF(DecodedCert, subjectCEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerC),
        WC_OFFSETOF(DecodedCert, issuerCLen),
        WC_OFFSETOF(DecodedCert, issuerCEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_countryName
#endif
    },
    /* Locality Name */
    {
        "/L=", 3,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectL),
        WC_OFFSETOF(DecodedCert, subjectLLen),
        WC_OFFSETOF(DecodedCert, subjectLEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerL),
        WC_OFFSETOF(DecodedCert, issuerLLen),
        WC_OFFSETOF(DecodedCert, issuerLEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_localityName
#endif
    },
    /* State Name */
    {
        "/ST=", 4,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectST),
        WC_OFFSETOF(DecodedCert, subjectSTLen),
        WC_OFFSETOF(DecodedCert, subjectSTEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerST),
        WC_OFFSETOF(DecodedCert, issuerSTLen),
        WC_OFFSETOF(DecodedCert, issuerSTEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_stateOrProvinceName
#endif
    },
    /* Street Address */
    {
        "/street=", 8,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectStreet),
        WC_OFFSETOF(DecodedCert, subjectStreetLen),
        WC_OFFSETOF(DecodedCert, subjectStreetEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_streetAddress
#endif
    },
    /* Organization Name */
    {
        "/O=", 3,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectO),
        WC_OFFSETOF(DecodedCert, subjectOLen),
        WC_OFFSETOF(DecodedCert, subjectOEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerO),
        WC_OFFSETOF(DecodedCert, issuerOLen),
        WC_OFFSETOF(DecodedCert, issuerOEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_organizationName
#endif
    },
    /* Organization Unit Name */
    {
        "/OU=", 4,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectOU),
        WC_OFFSETOF(DecodedCert, subjectOULen),
        WC_OFFSETOF(DecodedCert, subjectOUEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        WC_OFFSETOF(DecodedCert, issuerOU),
        WC_OFFSETOF(DecodedCert, issuerOULen),
        WC_OFFSETOF(DecodedCert, issuerOUEnc),
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_organizationalUnitName
#endif
    },
    /* Title */
    {
        EMPTY_STR, 0,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        0,
        0,
        0,
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        0,
#endif
    },
    /* Undefined */
    {
        EMPTY_STR, 0,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        0,
        0,
        0,
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        0,
#endif
    },
    /* Undefined */
    {
        EMPTY_STR, 0,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        0,
        0,
        0,
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        0,
#endif
    },
    /* Business Category */
    {
        "/businessCategory=", 18,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectBC),
        WC_OFFSETOF(DecodedCert, subjectBCLen),
        WC_OFFSETOF(DecodedCert, subjectBCEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_businessCategory
#endif
    },
    /* Undefined */
    {
        EMPTY_STR, 0,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        0,
        0,
        0,
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        0,
#endif
    },
    /* Postal Code */
    {
        "/postalCode=", 12,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectPC),
        WC_OFFSETOF(DecodedCert, subjectPCLen),
        WC_OFFSETOF(DecodedCert, subjectPCEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_postalCode
#endif
    },
    /* User Id */
    {
        "/userid=", 8,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectUID),
        WC_OFFSETOF(DecodedCert, subjectUIDLen),
        WC_OFFSETOF(DecodedCert, subjectUIDEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
#endif
#ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_userId
#endif
    },
#ifdef WOLFSSL_CERT_NAME_ALL
    /* Name, id 41 */
    {
        "/N=", 3,
    #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectN),
        WC_OFFSETOF(DecodedCert, subjectNLen),
        WC_OFFSETOF(DecodedCert, subjectNEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
    #endif
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_name
    #endif
    },
    /* Given Name, id 42 */
    {
        "/GN=", 4,
    #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectGN),
        WC_OFFSETOF(DecodedCert, subjectGNLen),
        WC_OFFSETOF(DecodedCert, subjectGNEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
    #endif
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_givenName
    #endif
    },
    /* initials, id 43 */
    {
        "/initials=", 10,
    #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectI),
        WC_OFFSETOF(DecodedCert, subjectILen),
        WC_OFFSETOF(DecodedCert, subjectIEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
    #endif
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_initials
    #endif
    },
    /* DN Qualifier Name, id 46 */
    {
        "/dnQualifier=", 13,
    #if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
        WC_OFFSETOF(DecodedCert, subjectDNQ),
        WC_OFFSETOF(DecodedCert, subjectDNQLen),
        WC_OFFSETOF(DecodedCert, subjectDNQEnc),
#ifdef WOLFSSL_HAVE_ISSUER_NAMES
        0,
        0,
        0,
#endif
    #endif
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        WC_NID_dnQualifier
    #endif
    },
#endif /* WOLFSSL_CERT_NAME_ALL */
};

static const int certNameSubjectSz =
        (int) (sizeof(certNameSubject) / sizeof(CertNameData));


/* ASN.1 template for an RDN.
 * X.509: RFC 5280, 4.1.2.4 - RelativeDistinguishedName
 */
static const ASNItem rdnASN[] = {
/* SET       */ { 1, ASN_SET, 1, 1, 0 },
                           /* AttributeTypeAndValue */
/* ATTR_SEQ  */     { 2, ASN_SEQUENCE, 1, 1, 0 },
                                   /* AttributeType */
/* ATTR_TYPE */         { 3, ASN_OBJECT_ID, 0, 0, 0 },
                           /* AttributeValue: Choice of tags - rdnChoice. */
/* ATTR_VAL  */         { 3, 0, 0, 0, 0 },
};
enum {
    RDNASN_IDX_SET = 0,
    RDNASN_IDX_ATTR_SEQ,
    RDNASN_IDX_ATTR_TYPE,
    RDNASN_IDX_ATTR_VAL
};

/* Number of items in ASN.1 template for an RDN. */
#define rdnASN_Length (sizeof(rdnASN) / sizeof(ASNItem))

/* Supported types of encodings (tags) for RDN strings.
 * X.509: RFC 5280, 4.1.2.4 - DirectoryString
 * (IA5 String not listed in RFC but required for alternative types)
 */
static const byte rdnChoice[] = {
    ASN_PRINTABLE_STRING, ASN_IA5_STRING, ASN_UTF8STRING, ASN_T61STRING,
    ASN_UNIVERSALSTRING, ASN_BMPSTRING, 0
};
#endif

#ifdef WOLFSSL_IP_ALT_NAME
/* used to set the human readable string for the IP address with a ASN_IP_TYPE
 * DNS entry
 * return 0 on success
 */
static int GenerateDNSEntryIPString(DNS_entry* entry, void* heap)
{
    int ret = 0;
    size_t nameSz = 0;
    char tmpName[WOLFSSL_MAX_IPSTR];
    const unsigned char* ip;

    if (entry == NULL || entry->type != ASN_IP_TYPE) {
        return BAD_FUNC_ARG;
    }

    if (entry->len != WOLFSSL_IP4_ADDR_LEN &&
            entry->len != WOLFSSL_IP6_ADDR_LEN) {
        WOLFSSL_MSG("Unexpected IP size");
        return BAD_FUNC_ARG;
    }
    ip = (const unsigned char*)entry->name;

    XMEMSET(tmpName, 0, sizeof(tmpName));

    /* store IP addresses as a string */
    if (entry->len == WOLFSSL_IP4_ADDR_LEN) {
        if (XSNPRINTF(tmpName, sizeof(tmpName), "%u.%u.%u.%u", 0xFFU & ip[0],
                      0xFFU & ip[1], 0xFFU & ip[2], 0xFFU & ip[3])
            >= (int)sizeof(tmpName))
        {
            WOLFSSL_MSG("IP buffer overrun");
            return BUFFER_E;
        }
    }

    if (entry->len == WOLFSSL_IP6_ADDR_LEN) {
        size_t i;
        for (i = 0; i < 8; i++) {
            if (XSNPRINTF(tmpName + i * 5, sizeof(tmpName) - i * 5,
                    "%02X%02X%s", 0xFF & ip[2 * i], 0xFF & ip[2 * i + 1],
                    (i < 7) ? ":" : "")
                >= (int)sizeof(tmpName))
            {
                WOLFSSL_MSG("IPv6 buffer overrun");
                return BUFFER_E;
            }
        }
    }

    nameSz = XSTRLEN(tmpName);
    entry->ipString = (char*)XMALLOC(nameSz + 1, heap,
        DYNAMIC_TYPE_ALTNAME);
    if (entry->ipString == NULL) {
        ret = MEMORY_E;
    }
    else {
        entry->ipStringStored = 1;
    }

    if (ret == 0) {
        XMEMCPY(entry->ipString, tmpName, nameSz);
        entry->ipString[nameSz] = '\0';
    }

    (void)heap;

    return ret;
}
#endif /* WOLFSSL_IP_ALT_NAME */

#ifdef WOLFSSL_RID_ALT_NAME
/* used to set the human readable string for the registeredID with an
 * ASN_RID_TYPE DNS entry
 * return 0 on success
 */
static int GenerateDNSEntryRIDString(DNS_entry* entry, void* heap)
{
    int i, j, ret   = 0;
    word16 nameSz   = 0;
#if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
    int nid         = 0;
#endif
    int tmpSize     = MAX_OID_SZ;
    word32 oid      = 0;
    word32 idx      = 0;
    word16 tmpName[MAX_OID_SZ];
    char   oidName[MAX_OID_SZ];
    char*  finalName = NULL;

    if (entry == NULL || entry->type != ASN_RID_TYPE) {
        return BAD_FUNC_ARG;
    }

    if (entry->len <= 0) {
        return BAD_FUNC_ARG;
    }

    XMEMSET(&oidName, 0, MAX_OID_SZ);

    ret = GetOID((const byte*)entry->name, &idx, &oid, oidIgnoreType,
                 entry->len);
    if (ret == 0) {
    #if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
        if ((nid = oid2nid(oid, oidCsrAttrType)) > 0) {
            /* OID has known string value */
            finalName = (char*)wolfSSL_OBJ_nid2ln(nid);
        }
        else
    #endif
        {
            /* Decode OBJECT_ID into dotted form array. */
            ret = DecodeObjectId((const byte*)(entry->name),(word32)entry->len,
                    tmpName, (word32*)&tmpSize);

            if (ret == 0) {
                j = 0;
                /* Append each number of dotted form. */
                for (i = 0; i < tmpSize; i++) {
                    if (j >= MAX_OID_SZ) {
                        return BUFFER_E;
                    }

                    if (i < tmpSize - 1) {
                        ret = XSNPRINTF(oidName + j, (word32)(MAX_OID_SZ - j),
                            "%d.", tmpName[i]);
                    }
                    else {
                        ret = XSNPRINTF(oidName + j, (word32)(MAX_OID_SZ - j),
                            "%d", tmpName[i]);
                    }

                    if (ret >= 0) {
                        j += ret;
                    }
                    else {
                        return BUFFER_E;
                    }
                }
                ret = 0;
                finalName = oidName;
            }
        }
    }

    if (ret == 0) {
        nameSz = (word16)XSTRLEN((const char*)finalName);
        if (nameSz > MAX_OID_SZ) {
            return BUFFER_E;
        }

        entry->ridString = (char*)XMALLOC((word32)(nameSz + 1), heap,
                DYNAMIC_TYPE_ALTNAME);

        if (entry->ridString == NULL) {
            ret = MEMORY_E;
        }
        else {
            entry->ridStringStored = 1;
        }

        if (ret == 0) {
            XMEMCPY(entry->ridString, finalName, (word32)(nameSz + 1));
        }
    }

    return ret;
}
#endif /* WOLFSSL_RID_ALT_NAME */

#ifdef WOLFSSL_ASN_TEMPLATE

#if defined(WOLFSSL_CERT_GEN) || !defined(NO_CERTS)

/* Adds a DNS entry to a list of DNS entries
 *
 * @param [in, out] lst      Linked list of DNS name entries.
 * @param [in]      entry    Entry to add to the list
 * @return  0 on success.
 */
static int AddDNSEntryToList(DNS_entry** lst, DNS_entry* entry)
{
#if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_ALT_NAMES_NO_REV)
    entry->next = NULL;
    if (*lst == NULL) {
        /* First on list */
        *lst = entry;
    }
    else {
        DNS_entry* temp = *lst;

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

        /* Add to end */
        temp->next = entry;
    }
#else
    /* Prepend entry to linked list. */
    entry->next = *lst;
    *lst = entry;
#endif

    return 0;
}


/* Allocate a DNS entry and set the fields.
 *
 * @param [in]      heap     Heap hint.
 * @param [in]      str      DNS name string.
 * @param [in]      strLen   Length of DNS name string.
 * @param [in]      type     Type of DNS name string.
 * @param [in, out] entries  Linked list of DNS name entries.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int SetDNSEntry(void* heap, const char* str, int strLen,
                       int type, DNS_entry** entries)
{
    DNS_entry* dnsEntry;
    int ret = 0;
    char *dnsEntry_name = NULL;

    /* TODO: consider one malloc. */
    /* Allocate DNS Entry object. */
    dnsEntry = AltNameNew(heap);
    if (dnsEntry == NULL) {
        ret = MEMORY_E;
    }
    if (ret == 0) {
        /* Allocate DNS Entry name - length of string plus 1 for NUL. */
        dnsEntry->name = dnsEntry_name = (char*)XMALLOC((size_t)strLen + 1,
                                                    heap, DYNAMIC_TYPE_ALTNAME);
        if (dnsEntry->name == NULL) {
            ret = MEMORY_E;
        }
        else {
            dnsEntry->nameStored = 1;
        }
    }
    if (ret == 0) {
        /* Set tag type, name length, name and NUL terminate name. */
        dnsEntry->type = type;
        dnsEntry->len = strLen;
        XMEMCPY(dnsEntry_name, str, (size_t)strLen);
        dnsEntry_name[strLen] = '\0';

#ifdef WOLFSSL_RID_ALT_NAME
        /* store registeredID as a string */
        if (type == ASN_RID_TYPE)
            ret = GenerateDNSEntryRIDString(dnsEntry, heap);
#endif
    }
#ifdef WOLFSSL_IP_ALT_NAME
    /* store IP addresses as a string */
    if (ret == 0 && type == ASN_IP_TYPE)
        ret = GenerateDNSEntryIPString(dnsEntry, heap);
#endif
    if (ret == 0) {
        ret = AddDNSEntryToList(entries, dnsEntry);
    }

    /* failure cleanup */
    if (ret != 0 && dnsEntry != NULL) {
        XFREE(dnsEntry_name, heap, DYNAMIC_TYPE_ALTNAME);
        XFREE(dnsEntry, heap, DYNAMIC_TYPE_ALTNAME);
    }

    return ret;
}
#endif

/* Set the details of a subject name component into a certificate.
 *
 * @param [in, out] cert    Certificate object.
 * @param [in]      id      Id of component.
 * @param [in]      str     String for component.
 * @param [in]      strLen  Length of string.
 * @param [in]      tag     BER tag representing encoding of string.
 * @return  0 on success, negative values on failure.
 */
static int SetSubject(DecodedCert* cert, int id, const byte* str, int strLen,
                      byte tag)
{
    int ret = 0;

    /* Put string and encoding into certificate. */
    if (id == ASN_COMMON_NAME) {
        cert->subjectCN = (const char *)str;
        cert->subjectCNLen = (int)strLen;
        cert->subjectCNEnc = (char)tag;
    }
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
    else if (id > ASN_COMMON_NAME && id <= ASN_USER_ID) {
        /* Use table and offsets to put data into appropriate fields. */
        SetCertNameSubject(cert, id, (const char*)str);
        SetCertNameSubjectLen(cert, id, strLen);
        SetCertNameSubjectEnc(cert, id, tag);
    }
#endif
#if !defined(IGNORE_NAME_CONSTRAINTS) || \
     defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)
    else if (id == ASN_EMAIL) {
        cert->subjectEmail = (const char*)str;
        cert->subjectEmailLen = strLen;
    }
#endif
#ifdef WOLFSSL_CERT_EXT
    /* TODO: consider mapping id to an index and using SetCertNameSubect*(). */
    else if (id == ASN_JURIS_C) {
        cert->subjectJC = (const char*)str;
        cert->subjectJCLen = strLen;
        cert->subjectJCEnc = (char)tag;
    }
    else if (id == ASN_JURIS_ST) {
        cert->subjectJS = (const char*)str;
        cert->subjectJSLen = strLen;
        cert->subjectJSEnc = (char)tag;
    }
#endif

    return ret;
}

#if (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)) && \
    defined(WOLFSSL_HAVE_ISSUER_NAMES)
/* Set the details of an issuer name component into a certificate.
 *
 * @param [in, out] cert    Certificate object.
 * @param [in]      id      Id of component.
 * @param [in]      str     String for component.
 * @param [in]      strLen  Length of string.
 * @param [in]      tag     BER tag representing encoding of string.
 * @return  0 on success, negative values on failure.
 */
static int SetIssuer(DecodedCert* cert, int id, const byte* str, int strLen,
                      byte tag)
{
    int ret = 0;

    /* Put string and encoding into certificate. */
    if (id == ASN_COMMON_NAME) {
        cert->issuerCN = (const char *)str;
        cert->issuerCNLen = (int)strLen;
        cert->issuerCNEnc = (char)tag;
    }
    else if (id > ASN_COMMON_NAME && id <= ASN_USER_ID) {
        /* Use table and offsets to put data into appropriate fields. */
        SetCertNameIssuer(cert, id, (const char*)str);
        SetCertNameIssuerLen(cert, id, strLen);
        SetCertNameIssuerEnc(cert, id, tag);
    }
    else if (id == ASN_EMAIL) {
        cert->issuerEmail = (const char*)str;
        cert->issuerEmailLen = strLen;
    }

    return ret;
}
#endif

/* Get a RelativeDistinguishedName from the encoding and put in certificate.
 *
 * @param [in, out] cert       Certificate object.
 * @param [in, out] full       Full name string. ([/<type>=<value>]*)
 * @param [in, out] idx        Index int full name to place next component.
 * @param [in, out] nid        NID of component type.
 * @param [in]      isSubject  Whether this data is for a subject name.
 * @param [in]      dataASN    Decoded data of RDN. Expected rdnASN type.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when type not supported.
 */
static int GetRDN(DecodedCert* cert, char* full, word32* idx, int* nid,
                  int isSubject, ASNGetData* dataASN)
{
    int         ret = 0;
    const char* typeStr = NULL;
    byte        typeStrLen = 0;
    const byte* oid;
    word32      oidSz;
    int         id = 0;

    (void)nid;

    /* Get name type OID from data items. */
    GetASN_OIDData(&dataASN[RDNASN_IDX_ATTR_TYPE], &oid, &oidSz);

    /* v1 name types */
    if ((oidSz == 3) && (oid[0] == 0x55) && (oid[1] == 0x04)) {
        id = oid[2];
        /* Check range of supported ids in table. */
        if (ValidCertNameSubject(id)) {
            /* Get the type string, length and NID from table. */
            typeStr = GetCertNameSubjectStr(id);
            typeStrLen = GetCertNameSubjectStrLen(id);
        #ifdef WOLFSSL_X509_NAME_AVAILABLE
            *nid = GetCertNameSubjectNID(id);
        #endif
        }
    }
    else if (oidSz == sizeof(attrEmailOid) && XMEMCMP(oid, attrEmailOid, oidSz) == 0) {
        /* Set the email id, type string, length and NID. */
        id = ASN_EMAIL;
        typeStr =  WOLFSSL_EMAIL_ADDR;
        typeStrLen = sizeof(WOLFSSL_EMAIL_ADDR) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_emailAddress;
    #endif
    }
    else if (oidSz == sizeof(uidOid) && XMEMCMP(oid, uidOid, oidSz) == 0) {
        /* Set the user id, type string, length and NID. */
        id = ASN_USER_ID;
        typeStr = WOLFSSL_USER_ID;
        typeStrLen = sizeof(WOLFSSL_USER_ID) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_userId;
    #endif
    }
    else if (oidSz == sizeof(dcOid) && XMEMCMP(oid, dcOid, oidSz) == 0) {
        /* Set the domain component, type string, length and NID. */
        id = ASN_DC;
        typeStr = WOLFSSL_DOMAIN_COMPONENT;
        typeStrLen = sizeof(WOLFSSL_DOMAIN_COMPONENT) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_domainComponent;
    #endif
    }
    else if (oidSz == sizeof(rfc822Mlbx) && XMEMCMP(oid, rfc822Mlbx, oidSz) == 0) {
        /* Set the RFC822 mailbox, type string, length and NID. */
        id = ASN_RFC822_MAILBOX;
        typeStr = WOLFSSL_RFC822_MAILBOX;
        typeStrLen = sizeof(WOLFSSL_RFC822_MAILBOX) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_rfc822Mailbox;
    #endif
    }
    else if (oidSz == sizeof(fvrtDrk) && XMEMCMP(oid, fvrtDrk, oidSz) == 0) {
        /* Set the favourite drink, type string, length and NID. */
        id = ASN_FAVOURITE_DRINK;
        typeStr = WOLFSSL_FAVOURITE_DRINK;
        typeStrLen = sizeof(WOLFSSL_FAVOURITE_DRINK) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_favouriteDrink;
    #endif
    }
#ifdef WOLFSSL_CERT_REQ
    else if (oidSz == sizeof(attrPkcs9ContentTypeOid) &&
             XMEMCMP(oid, attrPkcs9ContentTypeOid, oidSz) == 0) {
        /* Set the pkcs9_contentType, type string, length and NID. */
        id = ASN_CONTENT_TYPE;
        typeStr = WOLFSSL_CONTENT_TYPE;
        typeStrLen = sizeof(WOLFSSL_CONTENT_TYPE) - 1;
    #ifdef WOLFSSL_X509_NAME_AVAILABLE
        *nid = WC_NID_pkcs9_contentType;
    #endif
    }
#endif
    /* Other OIDs that start with the same values. */
    else if (oidSz == sizeof(dcOid) && XMEMCMP(oid, dcOid, oidSz-1) == 0) {
        WOLFSSL_MSG("Unknown pilot attribute type");
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        ret = ASN_PARSE_E;
    }
    else if (oidSz == ASN_JOI_PREFIX_SZ + 1 &&
                         XMEMCMP(oid, ASN_JOI_PREFIX, ASN_JOI_PREFIX_SZ) == 0) {
        /* Set the jurisdiction id. */
        id = 0x200 + oid[ASN_JOI_PREFIX_SZ];

        /* Set the jurisdiction type string, length and NID if known. */
        if (oid[ASN_JOI_PREFIX_SZ] == ASN_JOI_C) {
            typeStr = WOLFSSL_JOI_C;
            typeStrLen = sizeof(WOLFSSL_JOI_C) - 1;
        #ifdef WOLFSSL_X509_NAME_AVAILABLE
            *nid = WC_NID_jurisdictionCountryName;
        #endif /* WOLFSSL_X509_NAME_AVAILABLE */
        }
        else if (oid[ASN_JOI_PREFIX_SZ] == ASN_JOI_ST) {
            typeStr = WOLFSSL_JOI_ST;
            typeStrLen = sizeof(WOLFSSL_JOI_ST) - 1;
        #ifdef WOLFSSL_X509_NAME_AVAILABLE
            *nid = WC_NID_jurisdictionStateOrProvinceName;
        #endif /* WOLFSSL_X509_NAME_AVAILABLE */
        }
        else {
            WOLFSSL_MSG("Unknown Jurisdiction, skipping");
        }
    }

    if ((ret == 0) && (typeStr != NULL)) {
        /* OID type to store for subject name and add to full string. */
        const byte* str;
        word32 strLen;
        byte   tag = dataASN[RDNASN_IDX_ATTR_VAL].tag;

        /* Get the string reference and length. */
        GetASN_GetRef(&dataASN[RDNASN_IDX_ATTR_VAL], &str, &strLen);

        if (isSubject) {
            /* Store subject field components. */
            ret = SetSubject(cert, id, str, (int)strLen, tag);
        }
    #if (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT)) && \
        defined(WOLFSSL_HAVE_ISSUER_NAMES)
        /* Put issuer common name string and encoding into certificate. */
        else {
            ret = SetIssuer(cert, id, str, (int)strLen, tag);
        }
    #endif
        if (ret == 0) {
            /* Check there is space for this in the full name string and
             * terminating NUL character. */
            if ((typeStrLen + strLen) < (word32)(WC_ASN_NAME_MAX - *idx))
            {
                /* Add RDN to full string. */
                XMEMCPY(&full[*idx], typeStr, typeStrLen);
                *idx += typeStrLen;
                XMEMCPY(&full[*idx], str, strLen);
                *idx += strLen;
            }
            else {
                WOLFSSL_MSG("ASN Name too big, skipping");
            }
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Get a certificate name into the certificate object.
 *
 * @param [in, out] cert      Decoded certificate object.
 * @param [out]     full      Buffer to hold full name as a string.
 * @param [out]     hash      Buffer to hold hash of name.
 * @param [in]      nameType  ASN_ISSUER or ASN_SUBJECT.
 * @param [in]      input     Buffer holding certificate name.
 * @param [in, out] inOutIdx  On in, start of certificate name.
 *                            On out, start of ASN.1 item after cert name.
 * @param [in]      maxIdx    Index of next item after certificate name.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType,
                       const byte* input, word32* inOutIdx, word32 maxIdx)
{
    DECL_ASNGETDATA(dataASN, rdnASN_Length);
    int    ret = 0;
    word32 idx = 0;
    int    len = 0;
    word32 srcIdx = *inOutIdx;
#ifdef WOLFSSL_X509_NAME_AVAILABLE
    WOLFSSL_X509_NAME* dName = NULL;
#endif /* WOLFSSL_X509_NAME_AVAILABLE */

    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 + srcIdx, maxIdx - srcIdx, hash,
            HashIdAlg(cert->signatureOID)) != 0) {
        ret = ASN_PARSE_E;
    }

#ifdef HAVE_OCSP_RESPONDER
    if (ret == 0 && nameType == ASN_SUBJECT) {
        cert->subjectRawForHash = input + srcIdx;
        cert->subjectRawForHashLen = (int)(maxIdx - srcIdx);
    }
#endif

    CALLOC_ASNGETDATA(dataASN, rdnASN_Length, ret, cert->heap);

#ifdef WOLFSSL_X509_NAME_AVAILABLE
    if (ret == 0) {
        /* Create an X509_NAME to hold data for OpenSSL compatibility APIs. */
        dName = wolfSSL_X509_NAME_new_ex(cert->heap);
        if (dName == NULL) {
            ret = MEMORY_E;
        }
    }
#endif /* WOLFSSL_X509_NAME_AVAILABLE */

    if (ret == 0) {
        /* Expecting a SEQUENCE using up all data. */
        ret = GetASN_Sequence(input, &srcIdx, &len, maxIdx, 1);
    }
    if (ret == 0) {
    #if defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT)
        /* Store pointer and length to raw issuer. */
        if (nameType == ASN_ISSUER) {
            cert->issuerRaw = &input[srcIdx];
            cert->issuerRawLen = len;
        }
    #endif
    #if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
        /* Store pointer and length to raw subject. */
        if (nameType == ASN_SUBJECT) {
            cert->subjectRaw = &input[srcIdx];
            cert->subjectRawLen = len;
        }
    #endif

        /* Process all RDNs in name. */
        while ((ret == 0) && (srcIdx < maxIdx)) {
            int nid = 0;

            /* Initialize for data and setup RDN choice. */
            GetASN_Choice(&dataASN[RDNASN_IDX_ATTR_VAL], rdnChoice);
            /* Ignore type OID as too many to store in table. */
            GetASN_OID(&dataASN[RDNASN_IDX_ATTR_TYPE], oidIgnoreType);
            /* Parse RDN. */
            ret = GetASN_Items(rdnASN, dataASN, rdnASN_Length, 1, input,
                               &srcIdx, maxIdx);
            if (ret == 0) {
                /* Put RDN data into certificate. */
                ret = GetRDN(cert, full, &idx, &nid, nameType == ASN_SUBJECT,
                             dataASN);
            }
        #ifdef WOLFSSL_X509_NAME_AVAILABLE
            /* TODO: push this back up to ssl.c
             * (do parsing for WOLFSSL_X509_NAME on demand) */
            if (ret == 0) {
                int enc;
                const byte* str;
                word32 strLen;
                byte   tag = dataASN[RDNASN_IDX_ATTR_VAL].tag;

                /* Get string reference. */
                GetASN_GetRef(&dataASN[RDNASN_IDX_ATTR_VAL], &str, &strLen);

            #ifndef WOLFSSL_NO_ASN_STRICT
                /* RFC 5280 section 4.1.2.4 lists a DirectoryString as being
                 * 1..MAX in length */
                if (ret == 0 && 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");
                    ret = ASN_PARSE_E;
                }
            #endif

                /* Convert BER tag to a OpenSSL type. */
                switch (tag) {
                    case CTC_UTF8:
                        enc = WOLFSSL_MBSTRING_UTF8;
                        break;
                    case CTC_PRINTABLE:
                        enc = WOLFSSL_V_ASN1_PRINTABLESTRING;
                        break;
                    default:
                        WOLFSSL_MSG("Unknown encoding type, default UTF8");
                        enc = WOLFSSL_MBSTRING_UTF8;
                }
                if (nid != 0) {
                    /* Add an entry to the X509_NAME. */
                    if (wolfSSL_X509_NAME_add_entry_by_NID(dName, nid, enc, str,
                            (int)strLen, -1, -1) != WOLFSSL_SUCCESS) {
                        ret = ASN_PARSE_E;
                    }
                }
            }
        #endif
        }
    }
    if (ret == 0) {
        /* Terminate string. */
        full[idx] = 0;
        /* Return index into encoding after name. */
        *inOutIdx = srcIdx;

#ifdef WOLFSSL_X509_NAME_AVAILABLE
        /* Store X509_NAME in certificate. */
        if (nameType == ASN_ISSUER) {
        #if (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
             defined(HAVE_LIGHTY)) && \
            (defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT))
            dName->rawLen = (int)min((word32)cert->issuerRawLen,
                WC_ASN_NAME_MAX);
            XMEMCPY(dName->raw, cert->issuerRaw, (size_t)dName->rawLen);
        #endif
            cert->issuerName = dName;
        }
        else {
        #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX)
            dName->rawLen = (int)min((word32)cert->subjectRawLen,
                WC_ASN_NAME_MAX);
            XMEMCPY(dName->raw, cert->subjectRaw, (size_t)dName->rawLen);
        #endif
            cert->subjectName = dName;
        }
    }
    else {
        /* Dispose of unused X509_NAME. */
        wolfSSL_X509_NAME_free(dName);
#endif
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for certificate name. */
static const ASNItem certNameASN[] = {
/* OID  */ { 0, ASN_OBJECT_ID, 0, 0, 1 },
/* NAME */ { 0, ASN_SEQUENCE, 1, 0, 0 },
};
enum {
    CERTNAMEASN_IDX_OID = 0,
    CERTNAMEASN_IDX_NAME
};

/* Number of items in ASN.1 template for certificate name. */
#define certNameASN_Length (sizeof(certNameASN) / sizeof(ASNItem))
#endif

/* Get a certificate name into the certificate object.
 *
 * Either the issuer or subject name.
 *
 * @param [in, out] cert      Decoded certificate object.
 * @param [in]      nameType  Type being decoded: ASN_ISSUER or ASN_SUBJECT.
 * @param [in]      maxIdx    Index of next item after certificate name.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int GetName(DecodedCert* cert, int nameType, int maxIdx)
{
    ASNGetData dataASN[certNameASN_Length];
    word32 idx = cert->srcIdx;
    int    ret = 0;

    WOLFSSL_MSG("Getting Name");

    XMEMSET(dataASN, 0, sizeof(dataASN));
    /* Initialize for data and don't check optional prefix OID. */
    GetASN_OID(&dataASN[CERTNAMEASN_IDX_OID], oidIgnoreType);
    ret = GetASN_Items(certNameASN, dataASN, certNameASN_Length, 0,
                       cert->source, &idx, (word32)maxIdx);
    if (ret == 0) {
        char* full;
        byte* hash;

        /* Store offset of SEQUENCE that is start of name. */
        cert->srcIdx = dataASN[CERTNAMEASN_IDX_NAME].offset;

        /* Get fields to fill in based on name type. */
        if (nameType == ASN_ISSUER) {
            full = cert->issuer;
            hash = cert->issuerHash;
        }
        else {
            full = cert->subject;
            hash = cert->subjectHash;
        }

        /* Parse certificate name. */
        ret = GetCertName(cert, full, hash, nameType, cert->source,
                          &cert->srcIdx, idx);
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifndef NO_ASN_TIME

/* two byte date/time, add to value */
static WC_INLINE int GetTime(int* value, const byte* date, int* idx)
{
    int i = *idx;

    if (date[i] < 0x30 || date[i] > 0x39 || date[i+1] < 0x30 ||
                                                             date[i+1] > 0x39) {
        return ASN_PARSE_E;
    }

    *value += (int)btoi(date[i++]) * 10;
    *value += (int)btoi(date[i++]);

    *idx = i;

    return 0;
}

#ifdef WOLFSSL_LINUXKM
static WC_INLINE int GetTime_Long(long* value, const byte* date, int* idx)
{
    int i = *idx;

    if (date[i] < 0x30 || date[i] > 0x39 || date[i+1] < 0x30 ||
                                                             date[i+1] > 0x39) {
        return ASN_PARSE_E;
    }

    *value += (long)btoi(date[i++]) * 10;
    *value += (long)btoi(date[i++]);

    *idx = i;

    return 0;
}
#endif

/* Extract certTime from date string parameter.
 * Reminder: idx is incremented in each call to GetTime()
 * Return 0 on failure, 1 for success.  */
int ExtractDate(const unsigned char* date, unsigned char format,
                struct tm* certTime, int* idx, int len)
{
    int i = *idx;

    /* Validate date string length based on format. Can not assume null
     * terminated strings. Must check for the 'Z'.
     * Subtract 2; one for zero indexing and one to exclude null terminator
     * built into macro values. */
    if (format == ASN_UTC_TIME) {
        /* UTCTime format requires YYMMDDHHMMSSZ (13 chars). */
        /* Bounds check: ensure we have enough data before accessing. */
        if (len < i + ASN_UTC_TIME_SIZE - 1) {
            return 0;
        }
        if (date[i + ASN_UTC_TIME_SIZE - 2] != 'Z') {
            return 0;
        }
    }
    else if (format == ASN_GENERALIZED_TIME) {
        /* GeneralizedTime format requires YYYYMMDDHHMMSSZ (15 chars). */
        /* Bounds check: ensure we have enough data before accessing. */
        if (len < i + ASN_GENERALIZED_TIME_SIZE - 1) {
            return 0;
        }
        if (date[ i + ASN_GENERALIZED_TIME_SIZE - 2] != 'Z') {
            return 0;
        }
    }
    else {
        return 0;
    }

    XMEMSET(certTime, 0, sizeof(struct tm));

    /* Get the first two bytes of the year (century) */
    if (format == ASN_UTC_TIME) {
        if (btoi(date[*idx]) >= 5)
            certTime->tm_year = 1900;
        else
            certTime->tm_year = 2000;
    }
    else {
        /* format == GENERALIZED_TIME */
#ifdef WOLFSSL_LINUXKM
        if (GetTime_Long(&certTime->tm_year, date, idx) != 0) return 0;
#else
        if (GetTime(&certTime->tm_year, date, idx) != 0) return 0;
#endif
        certTime->tm_year *= 100;
    }

#ifdef AVR
    /* Extract the time from the struct tm and adjust tm_year, tm_mon */
    /* AVR libc stores these as uint8_t instead of int */
    /* AVR time_t also offsets from midnight 1 Jan 2000 */
    int tm_year = certTime->tm_year - 2000;
    int tm_mon  = certTime->tm_mon - 1;
    int tm_mday = certTime->tm_mday;
    int tm_hour = certTime->tm_hour;
    int tm_min  = certTime->tm_min;
    int tm_sec  = certTime->tm_sec;

    if (GetTime(&tm_year, date, idx) != 0) return 0;
    if (GetTime(&tm_mon , date, idx) != 0) return 0;
    if (GetTime(&tm_mday, date, idx) != 0) return 0;
    if (GetTime(&tm_hour, date, idx) != 0) return 0;
    if (GetTime(&tm_min , date, idx) != 0) return 0;
    if (GetTime(&tm_sec , date, idx) != 0) return 0;

    /* Re-populate certTime with computed values */
    certTime->tm_year = tm_year;
    certTime->tm_mon  = tm_mon;
    certTime->tm_mday = tm_mday;
    certTime->tm_hour = tm_hour;
    certTime->tm_min  = tm_min;
    certTime->tm_sec  = tm_sec;
#else /* !AVR */
    /* Get the next two bytes of the year. */
    #ifdef WOLFSSL_LINUXKM
        if (GetTime_Long(&certTime->tm_year, date, idx) != 0) return 0;
    #else
        if (GetTime(&certTime->tm_year, date, idx) != 0) return 0;
    #endif
    certTime->tm_year -= 1900;

    /* The next fields are expected in specific order in [date] string: */
    if (GetTime(&certTime->tm_mon , date, idx) != 0) return 0;
    certTime->tm_mon  -= 1;
    if (GetTime(&certTime->tm_mday, date, idx) != 0) return 0;
    if (GetTime(&certTime->tm_hour, date, idx) != 0) return 0;
    if (GetTime(&certTime->tm_min , date, idx) != 0) return 0;
    if (GetTime(&certTime->tm_sec , date, idx) != 0) return 0;

#endif /* !AVR */

    return 1;
}


#ifdef WOLFSSL_ASN_TIME_STRING
int GetTimeString(byte* date, int format, char* buf, int len, int dateLen)
{
    struct tm t;
    int idx = 0;

    if (!ExtractDate(date, (unsigned char)format, &t, &idx, dateLen)) {
        return 0;
    }

    if (date[idx] != 'Z') {
        WOLFSSL_MSG("UTCtime, not Zulu") ;
        return 0;
    }

    /* place month in buffer */
    buf[0] = '\0';
    switch(t.tm_mon) {
        case 0:  XSTRNCAT(buf, "Jan ", 5); break;
        case 1:  XSTRNCAT(buf, "Feb ", 5); break;
        case 2:  XSTRNCAT(buf, "Mar ", 5); break;
        case 3:  XSTRNCAT(buf, "Apr ", 5); break;
        case 4:  XSTRNCAT(buf, "May ", 5); break;
        case 5:  XSTRNCAT(buf, "Jun ", 5); break;
        case 6:  XSTRNCAT(buf, "Jul ", 5); break;
        case 7:  XSTRNCAT(buf, "Aug ", 5); break;
        case 8:  XSTRNCAT(buf, "Sep ", 5); break;
        case 9:  XSTRNCAT(buf, "Oct ", 5); break;
        case 10: XSTRNCAT(buf, "Nov ", 5); break;
        case 11: XSTRNCAT(buf, "Dec ", 5); break;
        default:
            return 0;

    }
    idx = 4; /* use idx now for char buffer */

    if (XSNPRINTF(buf + idx, (size_t)(len - idx), "%2d %02d:%02d:%02d %d GMT",
              t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, (int)t.tm_year + 1900)
        >= len - idx)
    {
        WOLFSSL_MSG("buffer overrun in GetTimeString");
        return 0;
    }

    return 1;
}
#endif /* WOLFSSL_ASN_TIME_STRING */

/* Check time struct for valid values. Returns 0 for success */
int ValidateGmtime(struct tm* inTime)
{
    int ret = 1;
    if ((inTime != NULL) &&
        (inTime->tm_sec >= 0) && (inTime->tm_sec <= 61) &&
        (inTime->tm_min >= 0) && (inTime->tm_min <= 59) &&
        (inTime->tm_hour >= 0) && (inTime->tm_hour <= 23) &&
        (inTime->tm_mday >= 1) && (inTime->tm_mday <= 31) &&
        (inTime->tm_mon >= 0) && (inTime->tm_mon <= 11) &&
        (inTime->tm_wday >= 0) && (inTime->tm_wday <= 6) &&
        (inTime->tm_yday >= 0) && (inTime->tm_yday <= 365)) {
        ret = 0;
    }

    return ret;
}

#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \
    !defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || \
            defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER))
/* Set current time string, either UTC or GeneralizedTime.
 * (void*) currTime should be a pointer to time_t, output is placed in buf.
 *
 * Return time string length placed in buf on success, negative on error */
int GetAsnTimeString(void* currTime, byte* buf, word32 len)
{
    byte* data_ptr  = buf;
    byte  uf_time[ASN_GENERALIZED_TIME_SIZE];
    int data_len = 0;

    WOLFSSL_ENTER("GetAsnTimeString");

    if (buf == NULL || len == 0)
        return BAD_FUNC_ARG;

    XMEMSET(uf_time, 0, sizeof(uf_time));
    /* GetFormattedTime returns length without null terminator */
    data_len = GetFormattedTime(currTime, uf_time, (word32)sizeof(uf_time));
    if (data_len <= 0) {
        return ASN_TIME_E;
    }
    /* ensure room to add 2 bytes (ASN type and length) before proceeding */
    else if (len < (word32)data_len + 2) {
        return BUFFER_E;
    }

    if (data_len == ASN_UTC_TIME_SIZE-1) {
        /* increment data_len for ASN length byte after adding the data_ptr */
        *data_ptr = (byte)ASN_UTC_TIME; data_ptr++; data_len++;
        /* -1 below excludes null terminator */
        *data_ptr = (byte)ASN_UTC_TIME_SIZE - 1; data_ptr++; data_len++;
        XMEMCPY(data_ptr, (byte *)uf_time, ASN_UTC_TIME_SIZE - 1);
        data_ptr += ASN_UTC_TIME_SIZE - 1;
    }
    else if (data_len == ASN_GENERALIZED_TIME_SIZE-1) {
        /* increment data_len for ASN length byte after adding the data_ptr */
        *data_ptr = (byte)ASN_GENERALIZED_TIME; data_ptr++; data_len++;
        /* -1 below excludes null terminator */
        *data_ptr = (byte)ASN_GENERALIZED_TIME_SIZE - 1; data_ptr++; data_len++;
        XMEMCPY(data_ptr, (byte*)uf_time, ASN_GENERALIZED_TIME_SIZE - 1);
        data_ptr += ASN_GENERALIZED_TIME_SIZE - 1;
    }
    else {
        WOLFSSL_MSG("Invalid time size returned");
        return ASN_TIME_E;
    }
    /* append null terminator */
    *data_ptr = 0;

    /* return length without null terminator */
    return data_len;
}

/* return just the time string as either UTC or Generalized Time*/
int GetFormattedTime(void* currTime, byte* buf, word32 len)
{
    WOLFSSL_ENTER("GetFormattedTime");

    return GetFormattedTime_ex(currTime, buf, len, 0);
}

int GetFormattedTime_ex(void* currTime, byte* buf, word32 len, byte format)
{
    struct tm* ts      = NULL;
    struct tm* tmpTime = NULL;
    int year, mon, day, hour, mini, sec;
    int ret;
#if defined(NEED_TMP_TIME)
    struct tm tmpTimeStorage;
    tmpTime = &tmpTimeStorage;
#endif
    /* Needed in case XGMTIME does not use the tmpTime argument. */
    (void)tmpTime;

    WOLFSSL_ENTER("GetFormattedTime_ex");

    if (buf == NULL || len == 0 || (format != 0 && format != ASN_UTC_TIME &&
            format != ASN_GENERALIZED_TIME))
        return BAD_FUNC_ARG;

    ts = (struct tm *)XGMTIME((time_t*)currTime, tmpTime);
    if (ValidateGmtime(ts)) {
        WOLFSSL_MSG("failed to get time data.");
        return ASN_TIME_E;
    }

    /* Note ASN_UTC_TIME_SIZE and ASN_GENERALIZED_TIME_SIZE include space for
     * the null terminator. ASN encoded values leave off the terminator. */
    if (format == 0) {
        if (ts->tm_year >= 50 && ts->tm_year < 150) {
            format = ASN_UTC_TIME;
        }
        else {
            format = ASN_GENERALIZED_TIME;
        }
    }

    if (format == ASN_UTC_TIME) {
        /* UTC Time */
        if (ts->tm_year >= 50 && ts->tm_year < 100) {
            year = ts->tm_year;
        }
        else {
            year = ts->tm_year - 100;
        }
        mon  = ts->tm_mon + 1;
        day  = ts->tm_mday;
        hour = ts->tm_hour;
        mini = ts->tm_min;
        sec  = ts->tm_sec;
        if (len < ASN_UTC_TIME_SIZE) {
            WOLFSSL_MSG("buffer for GetFormattedTime is too short.");
            return BUFFER_E;
        }
        ret = XSNPRINTF((char*)buf, len,
                        "%02d%02d%02d%02d%02d%02dZ", year, mon, day,
                        hour, mini, sec);
    }
    else {
        /* GeneralizedTime */
        year = ts->tm_year + 1900;
        mon  = ts->tm_mon + 1;
        day  = ts->tm_mday;
        hour = ts->tm_hour;
        mini = ts->tm_min;
        sec  = ts->tm_sec;
        if (len < ASN_GENERALIZED_TIME_SIZE) {
            WOLFSSL_MSG("buffer for GetFormattedTime is too short.");
            return BUFFER_E;
        }
        ret = XSNPRINTF((char*)buf, len,
                        "%4d%02d%02d%02d%02d%02dZ", year, mon, day,
                        hour, mini, sec);
    }

    return ret;
}

#endif /* !NO_ASN_TIME && !USER_TIME && !TIME_OVERRIDES &&
        * (OPENSSL_EXTRA || HAVE_PKCS7) */

#if defined(USE_WOLF_VALIDDATE)

/* to the second */
int DateGreaterThan(const struct tm* a, const struct tm* b)
{
    if (a->tm_year > b->tm_year)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon > b->tm_mon)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
           a->tm_mday > b->tm_mday)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour > b->tm_hour)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour == b->tm_hour &&
        a->tm_min > b->tm_min)
        return 1;

    if (a->tm_year == b->tm_year && a->tm_mon == b->tm_mon &&
        a->tm_mday == b->tm_mday && a->tm_hour == b->tm_hour &&
        a->tm_min  == b->tm_min  && a->tm_sec > b->tm_sec)
        return 1;

    return 0; /* false */
}


static WC_INLINE int DateLessThan(const struct tm* a, const struct tm* b)
{
    return DateGreaterThan(b,a);
}

/* like atoi but only use first byte */
/* Make sure before and after dates are valid */
/* date = ASN.1 raw */
/* format = ASN_UTC_TIME or ASN_GENERALIZED_TIME */
/* dateType = ASN_AFTER or ASN_BEFORE */
int wc_ValidateDate(const byte* date, byte format, int dateType, int len)
{
    return wc_ValidateDateWithTime(date, format, dateType, 0, len);
}

int wc_ValidateDateWithTime(const byte* date, byte format, int dateType,
    time_t checkTime, int len)
{
    time_t ltime;
    struct tm  certTime;
    struct tm* localTime;
    struct tm* tmpTime;
    int    i = 0;
    int    timeDiff = 0;
    int    diffHH = 0, diffMM = 0;

#if defined(NEED_TMP_TIME)
    struct tm tmpTimeStorage;
    tmpTime = &tmpTimeStorage;
#else
    tmpTime = NULL;
#endif
    (void)tmpTime;

    /* Use checkTime if provided (non-zero), otherwise use current time */
    if (checkTime != 0) {
        ltime = checkTime;
    }
    else {
        ltime = wc_Time(0);
    }

#ifndef NO_TIME_SIGNEDNESS_CHECK
    if (sizeof(ltime) == sizeof(word32) && (sword32)ltime < 0){
        /* A negative response here could be due to a 32-bit time_t
         * where the year is 2038 or later. */
        WOLFSSL_MSG("wc_Time failed to return a valid value");
        return 0;
    }
#endif

#ifdef WOLFSSL_BEFORE_DATE_CLOCK_SKEW
    if (dateType == ASN_BEFORE) {
        WOLFSSL_MSG("Skewing local time for before date check");
        ltime += WOLFSSL_BEFORE_DATE_CLOCK_SKEW;
    }
#endif

#ifdef WOLFSSL_AFTER_DATE_CLOCK_SKEW
    if (dateType == ASN_AFTER) {
        WOLFSSL_MSG("Skewing local time for after date check");
        ltime -= WOLFSSL_AFTER_DATE_CLOCK_SKEW;
    }
#endif

    if (!ExtractDate(date, format, &certTime, &i, len)) {
        WOLFSSL_MSG("Error extracting the date");
        return 0;
    }

    if ((date[i] == '+') || (date[i] == '-')) {
        int diffSign;

        WOLFSSL_MSG("Using time differential, not Zulu") ;
        diffSign = date[i++] == '+' ? 1 : -1 ;
        if (GetTime(&diffHH, date, &i) != 0)
            return 0;
        if (GetTime(&diffMM, date, &i) != 0)
            return 0;
        timeDiff = diffSign * (diffHH*60 + diffMM) * 60 ;
    } else if (date[i] != 'Z') {
        WOLFSSL_MSG("UTCtime, neither Zulu or time differential") ;
        return 0;
    }

    ltime -= (time_t)timeDiff;
    localTime = XGMTIME(&ltime, tmpTime);

    if (ValidateGmtime(localTime)) {
        WOLFSSL_MSG("XGMTIME failed");
        return 0;
    }

    if (dateType == ASN_BEFORE) {
        if (DateLessThan(localTime, &certTime)) {
            WOLFSSL_MSG("Date BEFORE check failed");
            return 0;
        }
    }
    else {  /* dateType == ASN_AFTER */
        if (DateGreaterThan(localTime, &certTime)) {
            WOLFSSL_MSG("Date AFTER check failed");
            return 0;
        }
    }

    return 1;
}
#endif /* USE_WOLF_VALIDDATE */

int wc_GetTime(void* timePtr, word32 timeSize)
{
    time_t* ltime = (time_t*)timePtr;

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

    if ((word32)sizeof(time_t) > timeSize) {
        return BUFFER_E;
    }

    *ltime = wc_Time(0);

    return 0;
}

#ifdef TIME_OVERRIDES
    #ifndef HAVE_TIME_T_TYPE
        typedef long time_t;
    #endif
    extern time_t XTIME(time_t* t);
#endif

static wc_time_cb timeFunc = NULL;

int wc_SetTimeCb(wc_time_cb f)
{
    timeFunc = f;
    return 0;
}

time_t wc_Time(time_t* t)
{
    if (timeFunc != NULL) {
        return timeFunc(t);
    }
    return XTIME(t);
}

#endif /* !NO_ASN_TIME */


#ifdef WOLFSSL_ASN_TEMPLATE
/* TODO: use a CHOICE instead of two items? */
/* ASN.1 template for a date - either UTC or Generalized Time. */
static const ASNItem dateASN[] = {
/* UTC */ { 0, ASN_UTC_TIME, 0, 0, 2 },
/* GT  */ { 0, ASN_GENERALIZED_TIME, 0, 0, 2 },
};
enum {
    DATEASN_IDX_UTC = 0,
    DATEASN_IDX_GT
};

/* Number of items in ASN.1 template for a date. */
#define dateASN_Length (sizeof(dateASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Get date buffer, format and length. Returns 0=success or error */
/* Decode a DateInfo - choice of UTC TIME or GENERALIZED TIME.
 *
 * @param [in]      source   Buffer containing encoded date.
 * @param [in, out] idx      On in, the index of the date.
 *                           On out, index after date.
 * @param [out]     pDate    Pointer into buffer of data bytes.
 * @param [out]     pFormat  Format of date - BER/DER tag.
 * @param [out]     pLength  Length of date bytes.
 * @param [in]      maxIdx   Index of next item after date.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when source or idx is NULL.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int GetDateInfo(const byte* source, word32* idx, const byte** pDate,
                        byte* pFormat, int* pLength, word32 maxIdx)
{
    ASNGetData dataASN[dateASN_Length];
    int ret = 0;

    if ((source == NULL) || (idx == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Initialize data. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        /* Parse date. */
        ret = GetASN_Items(dateASN, dataASN, dateASN_Length, 0, source, idx,
                           maxIdx);
    }
    if (ret == 0) {
        /* Determine which tag was seen. */
        int i = (dataASN[DATEASN_IDX_UTC].tag != 0) ? DATEASN_IDX_UTC
                                                    : DATEASN_IDX_GT;
        /* Return data from seen item. */
        if (pFormat != NULL) {
            *pFormat = dataASN[i].tag;
        }
        if (pDate != NULL) {
            *pDate = dataASN[i].data.ref.data;
        }
        if (pLength != NULL) {
            *pLength = (int)dataASN[i].data.ref.length;
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

int wc_GetDateInfo(const byte* certDate, int certDateSz, const byte** date,
    byte* format, int* length)
{
    int ret;
    word32 idx = 0;

    ret = GetDateInfo(certDate, &idx, date, format, length, (word32)certDateSz);

    return ret;
}

#ifndef NO_ASN_TIME
int wc_GetDateAsCalendarTime(const byte* date, int length, byte format,
    struct tm* timearg)
{
    int idx = 0;
    (void)length;
    if (!ExtractDate(date, format, timearg, &idx, length))
        return ASN_TIME_E;
    return 0;
}

#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES)
int wc_GetCertDates(Cert* cert, struct tm* before, struct tm* after)
{
    int ret = 0;
    const byte* date;
    byte format;
    int length;

    if (cert == NULL)
        return BAD_FUNC_ARG;

    if (before && cert->beforeDateSz > 0) {
        ret = wc_GetDateInfo(cert->beforeDate, cert->beforeDateSz, &date,
                             &format, &length);
        if (ret == 0)
            ret = wc_GetDateAsCalendarTime(date, length, format, before);
    }
    if (after && cert->afterDateSz > 0) {
        ret = wc_GetDateInfo(cert->afterDate, cert->afterDateSz, &date,
                             &format, &length);
        if (ret == 0)
            ret = wc_GetDateAsCalendarTime(date, length, format, after);
    }

    return ret;
}
#endif /* WOLFSSL_CERT_GEN && WOLFSSL_ALT_NAMES */
#endif /* !NO_ASN_TIME */

#ifndef NO_CERTS
#ifdef WOLFSSL_ASN_TEMPLATE
/* TODO: move code around to not require this. */
static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
                              int* badDateRet, int stopAtPubKey,
                              int stopAfterPubKey);
#endif

/* Assumes the target is a Raw-Public-Key certificate and parsed up to the
 * public key. Returns CRYPTOCB_UNAVAILABLE if it determines that the cert is
 * different from the Paw-Public-Key cert. In that case, cert->srcIdx is not
 * consumed so as succeeding parse function can take over.
 * In case that the target is Raw-Public-Key cert and contains a public key,
 * returns 0  and consumes cert->srcIdx so as a public key retrieval function
 * can follow.
 */
#if defined(HAVE_RPK)
int TryDecodeRPKToKey(DecodedCert* cert)
{
    int ret = 0, len;
    word32 tmpIdx;
    word32 oid;

    WOLFSSL_ENTER("TryDecodeRPKToKey");

    if (cert == NULL)
        return BAD_FUNC_ARG;

    tmpIdx = cert->srcIdx;

    /* both X509 cert and RPK cert should start with a Sequence tag */
    if (ret == 0) {
        if (GetSequence(cert->source, &tmpIdx, &len, cert->maxIdx) < 0)
            ret = ASN_PARSE_E;
    }
    /* TBSCertificate of X509 or AlgorithmIdentifier of RPK cert */
    if (ret == 0) {
        if (GetSequence(cert->source, &tmpIdx, &len, cert->maxIdx) < 0)
            ret = ASN_PARSE_E;
    }
    /* OBJ ID should be next in RPK cert */
    if (ret == 0) {
        if (GetObjectId(cert->source, &tmpIdx, &oid, oidKeyType, cert->maxIdx)
                                                                            < 0)
            ret = CRYPTOCB_UNAVAILABLE;
    }
    /* consume cert->srcIdx */
    if (ret == 0) {
        WOLFSSL_MSG("Looks like RPK certificate");
        cert->srcIdx = tmpIdx;
    }
    WOLFSSL_LEAVE("TryDecodeRPKToKey", ret);
    return ret;
}
#endif /* HAVE_RPK */

/* Parse the certificate up to the X.509 public key.
 *
 * If cert data is invalid then badDate get set to error value.
 *
 * @param [in, out] cert     Decoded certificate object.
 * @param [in]      verify   Whether to verify dates.
 * @param [out]     badDate  Error code when verify dates.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when cert or badDate is NULL.
 * @return  ASN_TIME_E when date BER tag is nor UTC or GENERALIZED time.
 * @return  ASN_DATE_SZ_E when time data is not supported.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_GetPubX509(DecodedCert* cert, int verify, int* badDate)
{
    /* Use common decode routine and stop at public key. */
    int ret;

    *badDate = 0;

    ret = DecodeCertInternal(cert, verify, NULL, badDate, 1, 0);
    if (ret >= 0) {
        /* Store current index: public key. */
        cert->srcIdx = (word32)ret;
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Parse the certificate up to and including X.509 public key.
 *
 * @param [in, out] cert     Decoded certificate object.
 * @param [in]      verify   Whether to verify dates.
 * @return  0 on success.
 * @return  ASN_TIME_E when date BER tag is nor UTC or GENERALIZED time.
 * @return  ASN_DATE_SZ_E when time data is not supported.
 * @return  ASN_BEFORE_DATE_E when ASN_BEFORE date is invalid.
 * @return  ASN_AFTER_DATE_E when ASN_AFTER date is invalid.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeToKey(DecodedCert* cert, int verify)
{
    int ret;
    int badDate = 0;

#ifdef WOLFSSL_DUAL_ALG_CERTS
    /* Call internal version and decode completely to also handle extensions.
     * This is required to parse a potential alternative public key in the
     * SubjectAlternativeKey extension. */
    ret = DecodeCertInternal(cert, verify, NULL, &badDate, 0, 0);
#else
    /* Call internal version and stop after public key. */
    ret = DecodeCertInternal(cert, verify, NULL, &badDate, 0, 1);
#endif
    /* Always return date errors. */
    if (ret == 0) {
        ret = badDate;
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_CERTS */


/* Encode a length for DER.
 *
 * @param [in]  length  Value to encode.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetLength(word32 length, byte* output)
{
    /* Start encoding at start of buffer. */
    word32 i = 0;

    if (length < ASN_LONG_LENGTH) {
        /* Only one byte needed to encode. */
        if (output) {
            /* Write out length value. */
            output[i] = (byte)length;
        }
        /* Skip over length. */
        i++;
    }
    else {
        /* Calculate the number of bytes required to encode value. */
        byte j = (byte)BytePrecision(length);

        if (output) {
            /* Encode count byte. */
            output[i] = (byte)(j | ASN_LONG_LENGTH);
        }
        /* Skip over count byte. */
        i++;

        /* Encode value as a big-endian byte array. */
        for (; j > 0; --j) {
            if (output) {
                /* Encode next most-significant byte. */
                output[i] = (byte)(length >> ((j - 1) * WOLFSSL_BIT_SIZE));
            }
            /* Skip over byte. */
            i++;
        }
    }

    /* Return number of bytes in encoded length. */
    return i;
}

word32 SetLengthEx(word32 length, byte* output, byte isIndef)
{
    if (isIndef) {
        if (output != NULL) {
            output[0] = ASN_INDEF_LENGTH;
        }
        return 1;
    }
    else {
        return SetLength(length, output);
    }
}
/* Encode a DER header - type/tag and length.
 *
 * @param [in]  tag     DER tag of ASN.1 item.
 * @param [in]  len     Length of data in ASN.1 item.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetHeader(byte tag, word32 len, byte* output, byte isIndef)
{
    if (output) {
        /* Encode tag first. */
        output[0] = tag;
    }
    /* Encode the length. */
    return SetLengthEx(len, output ? output + ASN_TAG_SZ : NULL, isIndef) +
        ASN_TAG_SZ;
}

/* Encode a SEQUENCE header in DER.
 *
 * @param [in]  len     Length of data in SEQUENCE.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetSequence(word32 len, byte* output)
{
    return SetHeader(ASN_SEQUENCE | ASN_CONSTRUCTED, len, output, 0);
}

word32 SetSequenceEx(word32 len, byte* output, byte isIndef)
{
    return SetHeader(ASN_SEQUENCE | ASN_CONSTRUCTED, len, output, isIndef);
}

/* Encode an OCTET STRING header in DER.
 *
 * @param [in]  len     Length of data in OCTET STRING.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetOctetString(word32 len, byte* output)
{
    return SetHeader(ASN_OCTET_STRING, len, output, 0);
}

word32 SetOctetStringEx(word32 len, byte* output, byte indef)
{
    if (indef)
        return SetHeader(ASN_OCTET_STRING | ASN_CONSTRUCTED, len, output, indef);
    return SetOctetString(len, output);
}

/* Encode a SET header in DER.
 *
 * @param [in]  len     Length of data in SET.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetSet(word32 len, byte* output)
{
    return SetHeader(ASN_SET | ASN_CONSTRUCTED, len, output, 0);
}

/* Encode an implicit context specific header in DER.
 *
 * Implicit means that it is constructed only if the included ASN.1 item is.
 *
 * @param [in]  tag     Tag for the implicit ASN.1 item.
 * @param [in]  number  Context specific number.
 * @param [in]  len     Length of data in SET.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetImplicit(byte tag, byte number, word32 len, byte* output, byte isIndef)
{
    byte useIndef = 0;

    if ((tag == ASN_OCTET_STRING) && isIndef) {
        tag = ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | number;
    }
    else {
        tag = (byte)(((tag == ASN_SEQUENCE || tag == ASN_SET) ?
            ASN_CONSTRUCTED : 0) | ASN_CONTEXT_SPECIFIC | number);
    }

    if (isIndef && (tag & ASN_CONSTRUCTED)) {
        useIndef = 1;
    }

    return SetHeader(tag, len, output, useIndef);
}

/* Encode an explicit context specific header in DER.
 *
 * Explicit means that there will be an ASN.1 item underneath.
 *
 * @param [in]  number  Context specific number.
 * @param [in]  len     Length of data in SET.
 * @param [out] output  Buffer to encode into.
 * @return  Number of bytes encoded.
 */
word32 SetExplicit(byte number, word32 len, byte* output, byte isIndef)
{
    return SetHeader((byte)(ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | number),
                     len, output, isIndef);
}

#if defined(OPENSSL_EXTRA)
/* Encode an Othername into DER.
 *
 * @param [in]  name    Pointer to the WOLFSSL_ASN1_OTHERNAME to be encoded.
 * @param [out] output  Buffer to encode into. If NULL, don't encode.
 * @return  Number of bytes encoded or WOLFSSL_FAILURE if name parameter is bad.
 */
word32 SetOthername(void *name, byte *output)
{
    WOLFSSL_ASN1_OTHERNAME *nm = (WOLFSSL_ASN1_OTHERNAME *)name;
    char *nameStr = NULL;
    word32 nameSz = 0;

    if ((nm == NULL) || (nm->value == NULL)) {
        WOLFSSL_MSG("otherName value is NULL");
        return WOLFSSL_FAILURE;
    }

    nameStr = nm->value->value.utf8string->data;
    nameSz = (word32)nm->value->value.utf8string->length;

    if (output == NULL) {
        return nm->type_id->objSz +
            SetHeader(ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC, nameSz + 2, NULL, 0) +
            SetHeader(CTC_UTF8, nameSz, NULL, 0) + nameSz;
    }
    else {
        const byte *output_start = output;
        /* otherName OID */
        XMEMCPY(output, nm->type_id->obj, nm->type_id->objSz);
        output += nm->type_id->objSz;

        output += SetHeader(ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC, nameSz + 2,
                            output, 0);

        /* work around false positive from -fstack-protector */
        PRAGMA_GCC_DIAG_PUSH
        PRAGMA_GCC("GCC diagnostic ignored \"-Wstringop-overflow\"")

        output += SetHeader(CTC_UTF8, nameSz, output, 0);

        PRAGMA_GCC_DIAG_POP

        XMEMCPY(output, nameStr, nameSz);

        output += nameSz;
        return (word32)(output - output_start);
    }
}
#endif /* OPENSSL_EXTRA */


#ifdef HAVE_ECC
/* Determines whether the signature algorithm is using ECDSA.
 *
 * @param [in] algoOID  Signature algorithm identifier.
 * @return  1 when algorithm is using ECDSA.
 * @return  0 otherwise.
 */
static WC_INLINE int IsSigAlgoECDSA(word32 algoOID)
{
    /* ECDSA sigAlgo must not have ASN1 NULL parameters */
    if (algoOID == CTC_SHAwECDSA || algoOID == CTC_SHA256wECDSA ||
        algoOID == CTC_SHA384wECDSA || algoOID == CTC_SHA512wECDSA) {
        return 1;
    }

    return 0;
}
#endif

/* Determines whether the signature algorithm's AlgorithmIdentifier omits
 * the trailing NULL parameters element. True for ECC / EdDSA / SM2 and
 * for the post-quantum families (Falcon, ML-DSA , SLH-DSA, LMS, XMSS).
 *
 * @param [in] algoOID  Algorithm OID.
 * @return  1 when the algorithm encodes its AlgorithmIdentifier without
 *          a NULL parameters element.
 * @return  0 otherwise.
 */
static WC_INLINE int IsSigAlgoNoParams(word32 algoOID)
{
    (void)algoOID;

    return (0
        #ifdef HAVE_ECC
              || IsSigAlgoECDSA(algoOID)
        #endif
        #ifdef WOLFSSL_SM2
              || (algoOID == SM2k)
        #endif
        #ifdef HAVE_ED25519
              || (algoOID == ED25519k)
        #endif
        #ifdef HAVE_CURVE25519
              || (algoOID == X25519k)
        #endif
        #ifdef HAVE_ED448
              || (algoOID == ED448k)
        #endif
        #ifdef HAVE_CURVE448
              || (algoOID == X448k)
        #endif
        #ifdef HAVE_FALCON
              || (algoOID == FALCON_LEVEL1k)
              || (algoOID == FALCON_LEVEL5k)
        #endif
        #ifdef WOLFSSL_HAVE_MLDSA
            #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
              || (algoOID == DILITHIUM_LEVEL2k)
              || (algoOID == DILITHIUM_LEVEL3k)
              || (algoOID == DILITHIUM_LEVEL5k)
            #endif
              || (algoOID == ML_DSA_44k)
              || (algoOID == ML_DSA_65k)
              || (algoOID == ML_DSA_87k)
        #endif
        #ifdef WOLFSSL_HAVE_SLHDSA
              || (algoOID == SLH_DSA_SHAKE_128Fk)
              || (algoOID == SLH_DSA_SHAKE_192Fk)
              || (algoOID == SLH_DSA_SHAKE_256Fk)
              || (algoOID == SLH_DSA_SHAKE_128Sk)
              || (algoOID == SLH_DSA_SHAKE_192Sk)
              || (algoOID == SLH_DSA_SHAKE_256Sk)
              || (algoOID == SLH_DSA_SHA2_128Fk)
              || (algoOID == SLH_DSA_SHA2_192Fk)
              || (algoOID == SLH_DSA_SHA2_256Fk)
              || (algoOID == SLH_DSA_SHA2_128Sk)
              || (algoOID == SLH_DSA_SHA2_192Sk)
              || (algoOID == SLH_DSA_SHA2_256Sk)
        #endif
        #ifdef WOLFSSL_HAVE_LMS
              || (algoOID == HSS_LMSk)
        #endif
        #ifdef WOLFSSL_HAVE_XMSS
              || (algoOID == XMSSk)
              || (algoOID == XMSSMTk)
        #endif
    );
}

/* Encode an algorithm identifier.
 *
 * [algoOID, type] is unique.
 *
 * @param [in]  algoOID   Algorithm identifier.
 * @param [out] output    Buffer to hold encoding.
 * @param [in]  type      Type of OID being encoded.
 * @param [in]  curveSz   Add extra space for curve data.
 * @return  Encoded data size on success.
 * @return  0 when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static word32 SetAlgoIDImpl(int algoOID, byte* output, int type, int curveSz,
                            byte absentParams)
{
    DECL_ASNSETDATA(dataASN, algoIdASN_Length);
    int ret = 0;
    const byte* algoName = 0;
    word32 algoSz = 0;

    CALLOC_ASNSETDATA(dataASN, algoIdASN_Length, ret, NULL);

#ifdef WOLFSSL_SMALL_STACK
    if(ret < 0) {
        /* Catch MEMORY_E */
        return 0;
    }
#endif

    algoName = OidFromId((word32)algoOID, (word32)type, &algoSz);
    if (algoName == NULL) {
        WOLFSSL_MSG("Unknown Algorithm");
    }
    else {
        word32 sz;
        int o = 0;

        /* Set the OID and OID type to encode. */
        SetASN_OID(&dataASN[ALGOIDASN_IDX_OID], (word32)algoOID, (word32)type);
        /* Hashes, signatures not ECC and keys not RSA output NULL tag. */
        if (!(type == oidHashType ||
                 (type == oidSigType && !IsSigAlgoNoParams((word32)algoOID)) ||
                 (type == oidKeyType && algoOID == RSAk))) {
            /* Don't put out NULL DER item. */
            dataASN[ALGOIDASN_IDX_NULL].noOut = 1;
        }
        /* Override for absent (not NULL) params */
        if (TRUE == absentParams) {
            dataASN[ALGOIDASN_IDX_NULL].noOut = 1;
        }
        if (algoOID == DSAk) {
            /* Don't include SEQUENCE for DSA keys. */
            o = 1;
        }
        else if (curveSz > 0) {
            /* Don't put out NULL DER item. */
            dataASN[ALGOIDASN_IDX_NULL].noOut = 0;
            /* Include space for extra data of length curveSz.
             * Subtract 1 for sequence and 1 for length encoding. */
            SetASN_Buffer(&dataASN[ALGOIDASN_IDX_NULL], NULL,
                          (word32)curveSz - 2);
        }

        /* Calculate size of encoding. */
        ret = SizeASN_Items(algoIdASN + o, dataASN + o,
                            (int)algoIdASN_Length - (int)o, &sz);
        if (ret == 0 && output != NULL) {
            /* Encode into buffer. */
            SetASN_Items(algoIdASN + o, dataASN + o,
                         (int)algoIdASN_Length - (int)o, output);
            if (curveSz > 0) {
                /* Return size excluding curve data. */
                sz = (dataASN[o].offset - dataASN[ALGOIDASN_IDX_NULL].offset);
            }
        }

        if (ret == 0) {
            /* Return encoded size. */
            ret = (int)sz;
        }
        else {
            /* Unsigned return type so 0 indicates error. */
            ret = 0;
        }
    }

    FREE_ASNSETDATA(dataASN, NULL);
    return (word32)ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Encode an algorithm identifier.
 *
 * [algoOID, type] is unique.
 *
 * @param [in]  algoOID   Algorithm identifier.
 * @param [out] output    Buffer to hold encoding.
 * @param [in]  type      Type of OID being encoded.
 * @param [in]  curveSz   Add extra space for curve data.
 * @return  Encoded data size on success.
 * @return  0 when dynamic memory allocation fails.
 */
word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz)
{
    return SetAlgoIDImpl(algoOID, output, type, curveSz, FALSE);
}

word32 SetAlgoIDEx(int algoOID, byte* output, int type, int curveSz,
                   byte absentParams)
{
    return SetAlgoIDImpl(algoOID, output, type, curveSz, absentParams);
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* Always encode PKCS#1 v1.5 RSA signature and compare to encoded data. */
/* ASN.1 template for DigestInfo for a PKCS#1 v1.5 RSA signature.
 * PKCS#1 v2.2: RFC 8017, A.2.4 - DigestInfo
 */
static const ASNItem digestInfoASN[] = {
/* SEQ          */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* digestAlgorithm */
/* DIGALGO_SEQ  */     { 1, ASN_SEQUENCE, 1, 1, 0 },
/* DIGALGO_OID  */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* DIGALGO_NULL */         { 2, ASN_TAG_NULL, 0, 0, 0 },
                                         /* digest */
/* DIGEST       */     { 1, ASN_OCTET_STRING, 0, 0, 0 }
};
enum {
    DIGESTINFOASN_IDX_SEQ = 0,
    DIGESTINFOASN_IDX_DIGALGO_SEQ,
    DIGESTINFOASN_IDX_DIGALGO_OID,
    DIGESTINFOASN_IDX_DIGALGO_NULL,
    DIGESTINFOASN_IDX_DIGEST
};

/* Number of items in ASN.1 template for DigestInfo for RSA. */
#define digestInfoASN_Length (sizeof(digestInfoASN) / sizeof(ASNItem))
#endif

/* Encode signature.
 *
 * @param [out] out     Buffer to hold encoding.
 * @param [in]  digest  Buffer holding digest.
 * @param [in]  digSz   Length of digest in bytes.
 * @return  Encoded data size on success.
 * @return  0 when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz,
                          int hashOID)
{
    DECL_ASNSETDATA(dataASN, digestInfoASN_Length);
    int ret = 0;
    word32 sz = 0;
    unsigned char dgst[WC_MAX_DIGEST_SIZE];

    CALLOC_ASNSETDATA(dataASN, digestInfoASN_Length, ret, NULL);

    if (ret == 0) {
        /* Set hash OID and type. */
        SetASN_OID(&dataASN[DIGESTINFOASN_IDX_DIGALGO_OID], (word32)hashOID,
                   oidHashType);
        /* Set digest. */
        if (digest == out) {
            XMEMCPY(dgst, digest, digSz);
            digest = dgst;
        }
        SetASN_Buffer(&dataASN[DIGESTINFOASN_IDX_DIGEST], digest, digSz);

        /* Calculate size of encoding. */
        ret = SizeASN_Items(digestInfoASN, dataASN, digestInfoASN_Length, &sz);
    }
    if (ret == 0) {
        /* Encode PKCS#1 v1.5 RSA signature. */
        SetASN_Items(digestInfoASN, dataASN, digestInfoASN_Length, out);
        ret = (int)sz;
    }
    else {
        /* Unsigned return type so 0 indicates error. */
        ret = 0;
    }

    FREE_ASNSETDATA(dataASN, NULL);
    return (word32)ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifndef NO_CERTS

int wc_GetCTC_HashOID(int type)
{
    int ret;
    enum wc_HashType hType;

    hType = wc_HashTypeConvert(type);
    ret = wc_HashGetOID(hType);
    if (ret < 0) {
        ret = 0; /* backwards compatibility */
    }

    return ret;
}

/* Initialize a signature context object.
 *
 * Object used for signing and verifying a certificate signature.
 *
 * @param [in, out] sigCtx  Signature context object.
 * @param [in]      heap    Dynamic memory hint.
 * @param [in]      devId   Hardware device identifier.
 */
void InitSignatureCtx(SignatureCtx* sigCtx, void* heap, int devId)
{
    if (sigCtx) {
        XMEMSET(sigCtx, 0, sizeof(SignatureCtx));
        sigCtx->devId = devId;
        sigCtx->heap = heap;
    }
}

/* Free dynamic data in a signature context object.
 *
 * @param [in, out] sigCtx  Signature context object.
 */
void FreeSignatureCtx(SignatureCtx* sigCtx)
{
    if (sigCtx == NULL)
        return;

#ifndef WOLFSSL_NO_MALLOC
    XFREE(sigCtx->digest, sigCtx->heap, DYNAMIC_TYPE_DIGEST);
    sigCtx->digest = NULL;
#if !defined(NO_RSA) || !defined(NO_DSA)
    XFREE(sigCtx->sigCpy, sigCtx->heap, DYNAMIC_TYPE_SIGNATURE);
    sigCtx->sigCpy = NULL;
#endif
#endif

#ifndef NO_ASN_CRYPT
#ifndef WOLFSSL_NO_MALLOC
    if (sigCtx->key.ptr)
#endif
    {
        switch (sigCtx->keyOID) {
        #ifndef NO_RSA
            #ifdef WC_RSA_PSS
            case RSAPSSk:
            #endif
            case RSAk:
                wc_FreeRsaKey(sigCtx->key.rsa);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.rsa, sigCtx->heap, DYNAMIC_TYPE_RSA);
                sigCtx->key.rsa = NULL;
            #endif
                break;
        #endif /* !NO_RSA */
        #ifndef NO_DSA
            case DSAk:
                wc_FreeDsaKey(sigCtx->key.dsa);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.dsa, sigCtx->heap, DYNAMIC_TYPE_DSA);
                sigCtx->key.dsa = NULL;
            #endif
                break;
        #endif
        #ifdef HAVE_ECC
            case ECDSAk:
        #ifdef WOLFSSL_SM2
            case SM2k:
        #endif
            #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
                defined(WC_ASYNC_ENABLE_ECC)
                if (sigCtx->key.ecc->nb_ctx != NULL) {
                    XFREE(sigCtx->key.ecc->nb_ctx, sigCtx->heap,
                          DYNAMIC_TYPE_TMP_BUFFER);
                    sigCtx->key.ecc->nb_ctx = NULL;
                }
            #endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                      WC_ASYNC_ENABLE_ECC */
                wc_ecc_free(sigCtx->key.ecc);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.ecc, sigCtx->heap, DYNAMIC_TYPE_ECC);
                sigCtx->key.ecc = NULL;
            #endif
                break;
        #endif /* HAVE_ECC */
        #ifdef HAVE_ED25519
            case ED25519k:
                wc_ed25519_free(sigCtx->key.ed25519);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.ed25519, sigCtx->heap, DYNAMIC_TYPE_ED25519);
                sigCtx->key.ed25519 = NULL;
            #endif
                break;
        #endif /* HAVE_ED25519 */
        #ifdef HAVE_ED448
            case ED448k:
                wc_ed448_free(sigCtx->key.ed448);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.ed448, sigCtx->heap, DYNAMIC_TYPE_ED448);
                sigCtx->key.ed448 = NULL;
            #endif
                break;
        #endif /* HAVE_ED448 */
        #if defined(HAVE_FALCON)
            case FALCON_LEVEL1k:
            case FALCON_LEVEL5k:
                wc_falcon_free(sigCtx->key.falcon);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.falcon, sigCtx->heap, DYNAMIC_TYPE_FALCON);
                sigCtx->key.falcon = NULL;
            #endif
                break;
        #endif /* HAVE_FALCON */
        #if defined(WOLFSSL_HAVE_MLDSA)
            #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
            case DILITHIUM_LEVEL2k:
            case DILITHIUM_LEVEL3k:
            case DILITHIUM_LEVEL5k:
            #endif
            case ML_DSA_44k:
            case ML_DSA_65k:
            case ML_DSA_87k:
                wc_MlDsaKey_Free(sigCtx->key.mldsa);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.mldsa, sigCtx->heap,
                    DYNAMIC_TYPE_MLDSA);
                sigCtx->key.mldsa = NULL;
            #endif
                break;
        #endif /* WOLFSSL_HAVE_MLDSA */
        #if defined(WOLFSSL_HAVE_SLHDSA)
            case SLH_DSA_SHAKE_128Fk:
            case SLH_DSA_SHAKE_192Fk:
            case SLH_DSA_SHAKE_256Fk:
            case SLH_DSA_SHAKE_128Sk:
            case SLH_DSA_SHAKE_192Sk:
            case SLH_DSA_SHAKE_256Sk:
        #ifdef WOLFSSL_SLHDSA_SHA2
            case SLH_DSA_SHA2_128Fk:
            case SLH_DSA_SHA2_192Fk:
            case SLH_DSA_SHA2_256Fk:
            case SLH_DSA_SHA2_128Sk:
            case SLH_DSA_SHA2_192Sk:
            case SLH_DSA_SHA2_256Sk:
        #endif
                wc_SlhDsaKey_Free(sigCtx->key.slhdsa);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.slhdsa, sigCtx->heap, DYNAMIC_TYPE_SLHDSA);
                sigCtx->key.slhdsa = NULL;
            #endif
                break;
        #endif /* WOLFSSL_HAVE_SLHDSA */
        #ifdef WOLFSSL_HAVE_LMS
            case HSS_LMSk:
                wc_LmsKey_Free(sigCtx->key.lms);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.lms, sigCtx->heap, DYNAMIC_TYPE_LMS);
                sigCtx->key.lms = NULL;
            #endif
                break;
        #endif /* WOLFSSL_HAVE_LMS */
        #ifdef WOLFSSL_HAVE_XMSS
            case XMSSk:
            case XMSSMTk:
                wc_XmssKey_Free(sigCtx->key.xmss);
            #ifndef WOLFSSL_NO_MALLOC
                XFREE(sigCtx->key.xmss, sigCtx->heap, DYNAMIC_TYPE_XMSS);
                sigCtx->key.xmss = NULL;
            #endif
                break;
        #endif /* WOLFSSL_HAVE_XMSS */
            default:
                break;
        } /* switch (keyOID) */
    #ifndef WOLFSSL_NO_MALLOC
        sigCtx->key.ptr = NULL;
    #endif
        sigCtx->keyOID = 0; /* mark key as freed (guards re-entry without malloc) */
    }
#endif /* !NO_ASN_CRYPT */

    /* reset state, we are done */
    sigCtx->state = SIG_STATE_BEGIN;
}

#if !defined(NO_ASN_CRYPT) && !defined(NO_HASH_WRAPPER)
static int HashForSignature(const byte* buf, word32 bufSz, word32 sigOID,
                            byte* digest, int* typeH, int* digestSz, int verify,
                            void* heap, int devId)
{
    int ret = 0;

    switch (sigOID) {
    #if defined(WOLFSSL_MD2)
        case CTC_MD2wRSA:
            if (!verify) {
                ret = HASH_TYPE_E;
                WOLFSSL_MSG("MD2 not supported for signing");
            }
            else if ((ret = wc_Md2Hash(buf, bufSz, digest)) == 0) {
                *typeH    = MD2h;
                *digestSz = WC_MD2_DIGEST_SIZE;
            }
        break;
    #endif
    #ifndef NO_MD5
        case CTC_MD5wRSA:
        #ifndef WOLFSSL_ALLOW_MD5_CERT_SIGS
            if (verify) {
                ret = HASH_TYPE_E;
                WOLFSSL_MSG("MD5 not supported for certificate verification");
                break;
            }
        #endif
            if ((ret = wc_Md5Hash_ex(buf, bufSz, digest, heap, devId)) == 0) {
                *typeH    = MD5h;
                *digestSz = WC_MD5_DIGEST_SIZE;
            }
            break;
    #endif
    #ifndef NO_SHA
        case CTC_SHAwRSA:
        case CTC_SHAwDSA:
        case CTC_SHAwECDSA:
            if ((ret = wc_ShaHash_ex(buf, bufSz, digest, heap, devId)) == 0) {
                *typeH    = SHAh;
                *digestSz = WC_SHA_DIGEST_SIZE;
            }
            break;
    #endif
    #ifdef WOLFSSL_SHA224
        case CTC_SHA224wRSA:
        case CTC_SHA224wECDSA:
            if ((ret = wc_Sha224Hash_ex(buf, bufSz, digest, heap, devId)) == 0)
            {
                *typeH    = SHA224h;
                *digestSz = WC_SHA224_DIGEST_SIZE;
            }
            break;
    #endif
    #ifndef NO_SHA256
        case CTC_SHA256wRSA:
        case CTC_SHA256wECDSA:
        case CTC_SHA256wDSA:
            if ((ret = wc_Sha256Hash_ex(buf, bufSz, digest, heap, devId)) == 0)
            {
                *typeH    = SHA256h;
                *digestSz = WC_SHA256_DIGEST_SIZE;
            }
            break;
    #endif
    #ifdef WOLFSSL_SHA384
        case CTC_SHA384wRSA:
        case CTC_SHA384wECDSA:
            if ((ret = wc_Sha384Hash_ex(buf, bufSz, digest, heap, devId)) == 0)
            {
                *typeH    = SHA384h;
                *digestSz = WC_SHA384_DIGEST_SIZE;
            }
            break;
    #endif
    #ifdef WOLFSSL_SHA512
        case CTC_SHA512wRSA:
        case CTC_SHA512wECDSA:
            if ((ret = wc_Sha512Hash_ex(buf, bufSz, digest, heap, devId)) == 0)
            {
                *typeH    = SHA512h;
                *digestSz = WC_SHA512_DIGEST_SIZE;
            }
            break;
    #endif
    #ifdef WOLFSSL_SHA3
    #ifndef WOLFSSL_NOSHA3_224
        case CTC_SHA3_224wRSA:
        case CTC_SHA3_224wECDSA:
            if ((ret = wc_Sha3_224Hash_ex(buf, bufSz, digest, heap, devId))
                    == 0) {
                *typeH    = SHA3_224h;
                *digestSz = WC_SHA3_224_DIGEST_SIZE;
            }
            break;
    #endif
    #ifndef WOLFSSL_NOSHA3_256
        case CTC_SHA3_256wRSA:
        case CTC_SHA3_256wECDSA:
            if ((ret = wc_Sha3_256Hash_ex(buf, bufSz, digest, heap, devId))
                    == 0) {
                *typeH    = SHA3_256h;
                *digestSz = WC_SHA3_256_DIGEST_SIZE;
            }
            break;
    #endif
    #ifndef WOLFSSL_NOSHA3_384
        case CTC_SHA3_384wRSA:
        case CTC_SHA3_384wECDSA:
            if ((ret = wc_Sha3_384Hash_ex(buf, bufSz, digest, heap, devId))
                    == 0) {
                *typeH    = SHA3_384h;
                *digestSz = WC_SHA3_384_DIGEST_SIZE;
            }
            break;
    #endif
    #ifndef WOLFSSL_NOSHA3_512
        case CTC_SHA3_512wRSA:
        case CTC_SHA3_512wECDSA:
            if ((ret = wc_Sha3_512Hash_ex(buf, bufSz, digest, heap, devId))
                    == 0) {
                *typeH    = SHA3_512h;
                *digestSz = WC_SHA3_512_DIGEST_SIZE;
            }
            break;
    #endif
    #endif
    #if defined(WOLFSSL_SM2) & defined(WOLFSSL_SM3)
        case CTC_SM3wSM2:
            if ((ret = wc_Sm3Hash_ex(buf, bufSz, digest, heap, devId)) == 0) {
                *typeH    = SM3h;
                *digestSz = WC_SM3_DIGEST_SIZE;
            }
            break;
    #endif
    #ifdef HAVE_ED25519
        case CTC_ED25519:
            /* Hashes done in signing operation.
             * Two dependent hashes with prefixes performed.
             */
            break;
    #endif
    #ifdef HAVE_ED448
        case CTC_ED448:
            /* Hashes done in signing operation.
             * Two dependent hashes with prefixes performed.
             */
            break;
    #endif
    #ifdef HAVE_FALCON
        case CTC_FALCON_LEVEL1:
        case CTC_FALCON_LEVEL5:
            /* Hashes done in signing operation. */
            break;
    #endif
    #ifdef WOLFSSL_HAVE_MLDSA
        #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case CTC_DILITHIUM_LEVEL2:
        case CTC_DILITHIUM_LEVEL3:
        case CTC_DILITHIUM_LEVEL5:
        #endif
        case CTC_ML_DSA_44:
        case CTC_ML_DSA_65:
        case CTC_ML_DSA_87:
            /* Hashes done in signing operation. */
            break;
    #endif
    #ifdef WOLFSSL_HAVE_SLHDSA
        case CTC_SLH_DSA_SHA2_128S:
        case CTC_SLH_DSA_SHA2_128F:
        case CTC_SLH_DSA_SHA2_192S:
        case CTC_SLH_DSA_SHA2_192F:
        case CTC_SLH_DSA_SHA2_256S:
        case CTC_SLH_DSA_SHA2_256F:
        case CTC_SLH_DSA_SHAKE_128S:
        case CTC_SLH_DSA_SHAKE_128F:
        case CTC_SLH_DSA_SHAKE_192S:
        case CTC_SLH_DSA_SHAKE_192F:
        case CTC_SLH_DSA_SHAKE_256S:
        case CTC_SLH_DSA_SHAKE_256F:
            /* Hashes done in signing operation. */
            break;
    #endif
    #ifdef WOLFSSL_HAVE_LMS
        case CTC_HSS_LMS:
            /* RFC 9802 sec 2: no digest is applied before signing. */
            break;
    #endif
    #ifdef WOLFSSL_HAVE_XMSS
        case CTC_XMSS:
        case CTC_XMSSMT:
            /* RFC 9802 sec 2: no digest is applied before signing. */
            break;
    #endif

        default:
            ret = HASH_TYPE_E;
            WOLFSSL_MSG("Hash for Signature has unsupported type");
    }

    (void)buf;
    (void)bufSz;
    (void)sigOID;
    (void)digest;
    (void)digestSz;
    (void)typeH;
    (void)verify;

    return ret;
}
#endif /* !NO_ASN_CRYPT && !NO_HASH_WRAPPER */

#if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
/* Try to parse as ASN.1 bitstring */
static int DecodeDsaAsn1Sig(const byte* sig, word32 sigSz, byte* sigCpy,
    void* heap)
{
    int ret = 0;
    int rSz = 0, sSz = 0, mpinit = 0;
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    mp_int* r = NULL;
    mp_int* s = NULL;
#else
    mp_int r[1];
    mp_int s[1];
#endif

#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    r = (mp_int*)XMALLOC(sizeof(*r), heap, DYNAMIC_TYPE_TMP_BUFFER);
    s = (mp_int*)XMALLOC(sizeof(*s), heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (r == NULL || s == NULL) {
        ret = MEMORY_E;
    }
#endif
    if (ret == 0) {
        ret = mp_init_multi(r, s, NULL, NULL, NULL, NULL);
    }
    if (ret == 0) {
        mpinit = 1;

        if (DecodeECC_DSA_Sig(sig, sigSz, r, s) != 0) {
            WOLFSSL_MSG("DSA sig decode ASN.1 failed!");
            ret = ASN_SIG_CONFIRM_E;
        }
    }
    if (ret == 0) {
        rSz = mp_unsigned_bin_size(r);
        sSz = mp_unsigned_bin_size(s);
        if (rSz + sSz > (int)sigSz) {
            WOLFSSL_MSG("DSA sig size invalid");
            ret = ASN_SIG_CONFIRM_E;
        }
    }
    if (ret == 0) {
        if (mp_to_unsigned_bin(r, sigCpy) != MP_OKAY ||
            mp_to_unsigned_bin(s, sigCpy + rSz) != MP_OKAY) {
            WOLFSSL_MSG("DSA sig to unsigned bin failed!");
            ret = ASN_SIG_CONFIRM_E;
        }
    }

    if (mpinit) {
        mp_free(r);
        mp_free(s);
    }
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
    XFREE(r, heap, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(s, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
    (void)heap;
    return ret;
}
#endif

/* The certificate's signatureAlgorithm (sigOID) must match the issuer's
 * key type (keyOID). sigOID picks the pre-hash; keyOID picks the
 * verifier. They need to agree or the verifier gets the wrong input. */
static int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID)
{
    switch (keyOID) {
    #ifndef NO_RSA
        case RSAk:
            switch (sigOID) {
                case CTC_MD2wRSA:
                case CTC_MD5wRSA:
                case CTC_SHAwRSA:
                case CTC_SHA224wRSA:
                case CTC_SHA256wRSA:
                case CTC_SHA384wRSA:
                case CTC_SHA512wRSA:
                case CTC_SHA3_224wRSA:
                case CTC_SHA3_256wRSA:
                case CTC_SHA3_384wRSA:
                case CTC_SHA3_512wRSA:
                case CTC_RSASSAPSS:
                    return 1;
            }
            return 0;
        #ifdef WC_RSA_PSS
        case RSAPSSk:
            return (sigOID == CTC_RSASSAPSS);
        #endif
    #endif
    #if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
        case DSAk:
            switch (sigOID) {
                case CTC_SHAwDSA:
                case CTC_SHA256wDSA:
                    return 1;
            }
            return 0;
    #endif
    #if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY)
        case ECDSAk:
        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        case SM2k:
        #endif
            switch (sigOID) {
                case CTC_SHAwECDSA:
                case CTC_SHA224wECDSA:
                case CTC_SHA256wECDSA:
                case CTC_SHA384wECDSA:
                case CTC_SHA512wECDSA:
                case CTC_SHA3_224wECDSA:
                case CTC_SHA3_256wECDSA:
                case CTC_SHA3_384wECDSA:
                case CTC_SHA3_512wECDSA:
            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                case CTC_SM3wSM2:
            #endif
                    return 1;
            }
            return 0;
    #endif
    #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
        case ED25519k:
            return (sigOID == CTC_ED25519);
    #endif
    #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
        case ED448k:
            return (sigOID == CTC_ED448);
    #endif
    #if defined(HAVE_FALCON)
        case FALCON_LEVEL1k:
            return (sigOID == CTC_FALCON_LEVEL1);
        case FALCON_LEVEL5k:
            return (sigOID == CTC_FALCON_LEVEL5);
    #endif
    #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY) && \
        !defined(WOLFSSL_MLDSA_NO_ASN1)
        #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case DILITHIUM_LEVEL2k:
            return (sigOID == CTC_DILITHIUM_LEVEL2);
        case DILITHIUM_LEVEL3k:
            return (sigOID == CTC_DILITHIUM_LEVEL3);
        case DILITHIUM_LEVEL5k:
            return (sigOID == CTC_DILITHIUM_LEVEL5);
        #endif
        case ML_DSA_44k:
            return (sigOID == CTC_ML_DSA_44);
        case ML_DSA_65k:
            return (sigOID == CTC_ML_DSA_65);
        case ML_DSA_87k:
            return (sigOID == CTC_ML_DSA_87);
    #endif
    #if defined(WOLFSSL_HAVE_SLHDSA)
        case SLH_DSA_SHAKE_128Fk:
            return (sigOID == CTC_SLH_DSA_SHAKE_128F);
        case SLH_DSA_SHAKE_192Fk:
            return (sigOID == CTC_SLH_DSA_SHAKE_192F);
        case SLH_DSA_SHAKE_256Fk:
            return (sigOID == CTC_SLH_DSA_SHAKE_256F);
        case SLH_DSA_SHAKE_128Sk:
            return (sigOID == CTC_SLH_DSA_SHAKE_128S);
        case SLH_DSA_SHAKE_192Sk:
            return (sigOID == CTC_SLH_DSA_SHAKE_192S);
        case SLH_DSA_SHAKE_256Sk:
            return (sigOID == CTC_SLH_DSA_SHAKE_256S);
        case SLH_DSA_SHA2_128Fk:
            return (sigOID == CTC_SLH_DSA_SHA2_128F);
        case SLH_DSA_SHA2_192Fk:
            return (sigOID == CTC_SLH_DSA_SHA2_192F);
        case SLH_DSA_SHA2_256Fk:
            return (sigOID == CTC_SLH_DSA_SHA2_256F);
        case SLH_DSA_SHA2_128Sk:
            return (sigOID == CTC_SLH_DSA_SHA2_128S);
        case SLH_DSA_SHA2_192Sk:
            return (sigOID == CTC_SLH_DSA_SHA2_192S);
        case SLH_DSA_SHA2_256Sk:
            return (sigOID == CTC_SLH_DSA_SHA2_256S);
    #endif
    #ifdef WOLFSSL_HAVE_LMS
        case HSS_LMSk:
            return (sigOID == CTC_HSS_LMS);
    #endif
    #ifdef WOLFSSL_HAVE_XMSS
        case XMSSk:
            return (sigOID == CTC_XMSS);
        case XMSSMTk:
            return (sigOID == CTC_XMSSMT);
    #endif
    }

    /* Default to reject unknown key types */
    (void)sigOID;
    return 0;
}

/* Return codes: 0=Success, Negative (see error-crypt.h), ASN_SIG_CONFIRM_E */
int ConfirmSignature(SignatureCtx* sigCtx,
    const byte* buf, word32 bufSz,
    const byte* key, word32 keySz, word32 keyOID,
    const byte* sig, word32 sigSz, word32 sigOID,
    const byte* sigParams, word32 sigParamsSz,
    byte* rsaKeyIdx)
{
    int ret = WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E); /* default to failure */

#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    CertAttribute* certatt = NULL;
#endif

    if (sigCtx == NULL || buf == NULL || bufSz == 0 || key == NULL ||
        keySz == 0 || sig == NULL || sigSz == 0) {
        return BAD_FUNC_ARG;
    }

    (void)key;
    (void)keySz;
    (void)sig;
    (void)sigSz;
    (void)sigParams;
    (void)sigParamsSz;

    WOLFSSL_ENTER("ConfirmSignature");

#if !defined(WOLFSSL_RENESAS_TSIP_TLS) && !defined(WOLFSSL_RENESAS_FSPSM_TLS)
    (void)rsaKeyIdx;
#else
    #if !defined(NO_RSA) || defined(HAVE_ECC)
    certatt = (CertAttribute*)&sigCtx->CertAtt;
    #endif
    if (certatt) {
        certatt->keyIndex = rsaKeyIdx;
        certatt->cert = buf;
        certatt->certSz = bufSz;
    }
#endif

#ifndef NO_ASN_CRYPT
    switch (sigCtx->state) {
        case SIG_STATE_BEGIN:
        {
            sigCtx->keyOID = keyOID; /* must set early for cleanup */

#ifndef WOLFSSL_NO_MALLOC
            sigCtx->digest = (byte*)XMALLOC(WC_MAX_DIGEST_SIZE, sigCtx->heap,
                                                    DYNAMIC_TYPE_DIGEST);
            if (sigCtx->digest == NULL) {
                ERROR_OUT(MEMORY_E, exit_cs);
            }
#endif

        #if !defined(NO_RSA) && defined(WC_RSA_PSS)
            /* RSA PSS Defaults */
            sigCtx->hash = WC_HASH_TYPE_SHA;
            sigCtx->mgf = WC_MGF1SHA1;
            sigCtx->saltLen = 20;
        #endif

            sigCtx->state = SIG_STATE_HASH;
        } /* SIG_STATE_BEGIN */
        FALL_THROUGH;

        case SIG_STATE_HASH:
        {
            if (!SigOidMatchesKeyOid(sigOID, keyOID)) {
                WOLFSSL_MSG("sigOID incompatible with issuer keyOID");
                ERROR_OUT(ASN_SIG_OID_E, exit_cs);
            }

        #if !defined(NO_RSA) && defined(WC_RSA_PSS)
            if (sigOID == RSAPSSk) {
                word32 fakeSigOID = 0;
                ret = DecodeRsaPssParams(sigParams, sigParamsSz, &sigCtx->hash,
                    &sigCtx->mgf, &sigCtx->saltLen);
                if (ret != 0) {
                    goto exit_cs;
                }
                ret = RsaPssHashOidToSigOid(sigCtx->hash, &fakeSigOID);
                if (ret != 0) {
                    goto exit_cs;
                }
                /* Decode parameters. */
                ret = HashForSignature(buf, bufSz, fakeSigOID, sigCtx->digest,
                    &sigCtx->typeH, &sigCtx->digestSz, 1, sigCtx->heap,
                    sigCtx->devId);
                if (ret != 0) {
                    goto exit_cs;
                }
            }
            else
        #endif
        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
            if (sigOID == CTC_SM3wSM2) {
                ; /* SM2 hash requires public key. Done later. */
            }
            else
        #endif
            {
                ret = HashForSignature(buf, bufSz, sigOID, sigCtx->digest,
                                       &sigCtx->typeH, &sigCtx->digestSz, 1,
                                       sigCtx->heap, sigCtx->devId);
                if (ret != 0) {
                    goto exit_cs;
                }
            }

            sigCtx->state = SIG_STATE_KEY;
        } /* SIG_STATE_HASH */
        FALL_THROUGH;

        case SIG_STATE_KEY:
        {
            switch (keyOID) {
            #ifndef NO_RSA
                #ifdef WC_RSA_PSS
                case RSAPSSk:
                #endif
                case RSAk:
                {
                    word32 idx = 0;

                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.rsa = (RsaKey*)XMALLOC(sizeof(RsaKey),
                                                sigCtx->heap, DYNAMIC_TYPE_RSA);
                    if (sigCtx->key.rsa == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_InitRsaKey_ex(sigCtx->key.rsa, sigCtx->heap,
                                                        sigCtx->devId)) != 0) {
                        goto exit_cs;
                    }
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->sigCpy = (byte*)XMALLOC(sigSz, sigCtx->heap,
                                                        DYNAMIC_TYPE_SIGNATURE);
                    if (sigCtx->sigCpy == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    /* RSA signature copied into sigCpy, which under
                     * WOLFSSL_NO_MALLOC is sized to MAX_ENCODED_CLASSIC_SIG_SZ. */
                    if (sigSz > MAX_ENCODED_CLASSIC_SIG_SZ) {
                        WOLFSSL_MSG("Verify Signature is too big");
                        ERROR_OUT(BUFFER_E, exit_cs);
                    }
                    if ((ret = wc_RsaPublicKeyDecode(key, &idx, sigCtx->key.rsa,
                                                                 keySz)) != 0) {
                        WOLFSSL_MSG("ASN Key decode error RSA");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                    XMEMCPY(sigCtx->sigCpy, sig, sigSz);
                    sigCtx->out = NULL;

                #ifdef WOLFSSL_ASYNC_CRYPT
                    sigCtx->asyncDev = &sigCtx->key.rsa->asyncDev;
                #endif
                    break;
                }
            #endif /* !NO_RSA */
            #if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
                case DSAk:
                {
                    word32 idx = 0;

                    if (sigSz < DSA_MIN_SIG_SIZE) {
                        WOLFSSL_MSG("Verify Signature is too small");
                        ERROR_OUT(BUFFER_E, exit_cs);
                    }
                    else if (sigSz > MAX_ENCODED_CLASSIC_SIG_SZ) {
                        WOLFSSL_MSG("Verify Signature is too big");
                        ERROR_OUT(BUFFER_E, exit_cs);
                    }
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.dsa = (DsaKey*)XMALLOC(sizeof(DsaKey),
                                                sigCtx->heap, DYNAMIC_TYPE_DSA);
                    if (sigCtx->key.dsa == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_InitDsaKey_h(sigCtx->key.dsa, sigCtx->heap)) != 0) {
                        WOLFSSL_MSG("wc_InitDsaKey_h error");
                        goto exit_cs;
                    }
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->sigCpy = (byte*)XMALLOC(sigSz,
                                         sigCtx->heap, DYNAMIC_TYPE_SIGNATURE);
                    if (sigCtx->sigCpy == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_DsaPublicKeyDecode(key, &idx, sigCtx->key.dsa,
                                                                 keySz)) != 0) {
                        WOLFSSL_MSG("ASN Key decode error DSA");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                    if (sigSz != DSA_160_SIG_SIZE &&
                        sigSz != DSA_256_SIG_SIZE) {
                        ret = DecodeDsaAsn1Sig(sig, sigSz, sigCtx->sigCpy,
                            sigCtx->heap);
                    }
                    else {
                        XMEMCPY(sigCtx->sigCpy, sig, sigSz);
                    }
                    break;
                }
            #endif /* !NO_DSA && !HAVE_SELFTEST */
            #ifdef HAVE_ECC
            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                case SM2k:
            #endif
                case ECDSAk:
                {
                    word32 idx = 0;
            #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
                defined(WC_ASYNC_ENABLE_ECC)
                    ecc_nb_ctx_t* nb_ctx;
            #endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                      WC_ASYNC_ENABLE_ECC */

                    sigCtx->verify = 0;
            #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.ecc = (ecc_key*)XMALLOC(sizeof(ecc_key),
                                                sigCtx->heap, DYNAMIC_TYPE_ECC);
                    if (sigCtx->key.ecc == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
            #endif
                    if ((ret = wc_ecc_init_ex(sigCtx->key.ecc, sigCtx->heap,
                                                          sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
            #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
                defined(WC_ASYNC_ENABLE_ECC)
                    /* Only set non-blocking context when async device is
                     * active. With INVALID_DEVID there is no async loop to
                     * retry on FP_WOULDBLOCK, so let the WC_ECC_NONBLOCK_ONLY
                     * blocking fallback handle it instead. */
                    if (sigCtx->devId != INVALID_DEVID) {
                        nb_ctx = (ecc_nb_ctx_t*)XMALLOC(sizeof(ecc_nb_ctx_t),
                                    sigCtx->heap, DYNAMIC_TYPE_TMP_BUFFER);
                        if (nb_ctx == NULL) {
                            ERROR_OUT(MEMORY_E, exit_cs);
                        }

                        ret = wc_ecc_set_nonblock(sigCtx->key.ecc, nb_ctx);
                        if (ret != 0) {
                            XFREE(nb_ctx, sigCtx->heap,
                                DYNAMIC_TYPE_TMP_BUFFER);
                            goto exit_cs;
                        }
                    }
            #endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                      WC_ASYNC_ENABLE_ECC */
                    ret = wc_EccPublicKeyDecode(key, &idx, sigCtx->key.ecc,
                                                                         keySz);
                    if (ret < 0) {
                        WOLFSSL_MSG("ASN Key import error ECC");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                #ifdef WOLFSSL_ASYNC_CRYPT
                    sigCtx->asyncDev = &sigCtx->key.ecc->asyncDev;
                #endif
                    break;
                }
            #endif /* HAVE_ECC */
            #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
                case ED25519k:
                {
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.ed25519 = (ed25519_key*)XMALLOC(
                                              sizeof(ed25519_key), sigCtx->heap,
                                              DYNAMIC_TYPE_ED25519);
                    if (sigCtx->key.ed25519 == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_ed25519_init_ex(sigCtx->key.ed25519,
                                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_ed25519_import_public(key, keySz,
                                                    sigCtx->key.ed25519)) < 0) {
                        WOLFSSL_MSG("ASN Key import error ED25519");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                #ifdef WOLFSSL_ASYNC_CRYPT
                    sigCtx->asyncDev = &sigCtx->key.ed25519->asyncDev;
                #endif
                    break;
                }
            #endif
            #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
                case ED448k:
                {
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.ed448 = (ed448_key*)XMALLOC(
                                                sizeof(ed448_key), sigCtx->heap,
                                                DYNAMIC_TYPE_ED448);
                    if (sigCtx->key.ed448 == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_ed448_init(sigCtx->key.ed448)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_ed448_import_public(key, keySz,
                                                      sigCtx->key.ed448)) < 0) {
                        WOLFSSL_MSG("ASN Key import error ED448");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                #ifdef WOLFSSL_ASYNC_CRYPT
                    sigCtx->asyncDev = &sigCtx->key.ed448->asyncDev;
                #endif
                    break;
                }
            #endif
            #if defined(HAVE_FALCON)
                case FALCON_LEVEL1k:
                {
                    word32 idx = 0;
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.falcon =
                        (falcon_key*)XMALLOC(sizeof(falcon_key),
                                             sigCtx->heap,
                                             DYNAMIC_TYPE_FALCON);
                    if (sigCtx->key.falcon == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_falcon_init_ex(sigCtx->key.falcon,
                                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_falcon_set_level(sigCtx->key.falcon, 1))
                        < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_Falcon_PublicKeyDecode(key, &idx,
                        sigCtx->key.falcon, keySz)) < 0) {
                        WOLFSSL_MSG("ASN Key import error Falcon Level 1");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                    break;
                }
                case FALCON_LEVEL5k:
                {
                    word32 idx = 0;
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.falcon =
                        (falcon_key*)XMALLOC(sizeof(falcon_key),
                                             sigCtx->heap,
                                             DYNAMIC_TYPE_FALCON);
                    if (sigCtx->key.falcon == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_falcon_init_ex(sigCtx->key.falcon,
                                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_falcon_set_level(sigCtx->key.falcon, 5))
                        < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_Falcon_PublicKeyDecode(key, &idx,
                        sigCtx->key.falcon, keySz)) < 0) {
                        WOLFSSL_MSG("ASN Key import error Falcon Level 5");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                    break;
                }
            #endif /* HAVE_FALCON */
            #if defined(WOLFSSL_HAVE_MLDSA) && \
                !defined(WOLFSSL_MLDSA_NO_VERIFY) && \
                !defined(WOLFSSL_MLDSA_NO_ASN1)
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                case DILITHIUM_LEVEL2k:
                case DILITHIUM_LEVEL3k:
                case DILITHIUM_LEVEL5k:
                #endif
                case ML_DSA_44k:
                case ML_DSA_65k:
                case ML_DSA_87k:
                {
                    word32 idx = 0;
                    int level;
                    if (keyOID == ML_DSA_44k) {
                        level = WC_ML_DSA_44;
                    }
                    else if (keyOID == ML_DSA_65k) {
                        level = WC_ML_DSA_65;
                    }
                    else if (keyOID == ML_DSA_87k) {
                        level = WC_ML_DSA_87;
                    }
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                    else if (keyOID == DILITHIUM_LEVEL2k) {
                        level = WC_ML_DSA_44_DRAFT;
                    }
                    else if (keyOID == DILITHIUM_LEVEL3k) {
                        level = WC_ML_DSA_65_DRAFT;
                    }
                    else if (keyOID == DILITHIUM_LEVEL5k) {
                        level = WC_ML_DSA_87_DRAFT;
                    }
                #endif
                    else {
                        WOLFSSL_MSG("Invalid ML-DSA key OID");
                        goto exit_cs;
                    }
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.mldsa = (wc_MlDsaKey*)XMALLOC(
                        sizeof(wc_MlDsaKey), sigCtx->heap,
                        DYNAMIC_TYPE_MLDSA);
                    if (sigCtx->key.mldsa == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_MlDsaKey_Init(sigCtx->key.mldsa,
                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_MlDsaKey_SetParams(sigCtx->key.mldsa,
                            (byte)level)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_MlDsaKey_PublicKeyDecode(sigCtx->key.mldsa,
                        key, keySz, &idx)) < 0) {
                        WOLFSSL_MSG("ASN Key import error ML-DSA");
                        goto exit_cs;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_MLDSA */
            #if defined(WOLFSSL_HAVE_SLHDSA)
            #ifndef WOLFSSL_SLHDSA_SHA2
                /* SHA-2 OIDs recognised but backend not built; emit a
                 * specific NOT_COMPILED_IN so callers can render a
                 * "variant unavailable" diagnostic instead of the
                 * generic ASN_UNKNOWN_OID_E for "malformed DER". */
                case SLH_DSA_SHA2_128Fk:
                case SLH_DSA_SHA2_192Fk:
                case SLH_DSA_SHA2_256Fk:
                case SLH_DSA_SHA2_128Sk:
                case SLH_DSA_SHA2_192Sk:
                case SLH_DSA_SHA2_256Sk:
                    WOLFSSL_MSG("SHA2-SLH-DSA recognised but not compiled in");
                    ERROR_OUT(NOT_COMPILED_IN, exit_cs);
            #else
                case SLH_DSA_SHA2_128Fk:
                case SLH_DSA_SHA2_192Fk:
                case SLH_DSA_SHA2_256Fk:
                case SLH_DSA_SHA2_128Sk:
                case SLH_DSA_SHA2_192Sk:
                case SLH_DSA_SHA2_256Sk:
            #endif
                case SLH_DSA_SHAKE_128Fk:
                case SLH_DSA_SHAKE_192Fk:
                case SLH_DSA_SHAKE_256Fk:
                case SLH_DSA_SHAKE_128Sk:
                case SLH_DSA_SHAKE_192Sk:
                case SLH_DSA_SHAKE_256Sk:
                {
                    int slhDsaParam = wc_SlhDsaOidToParam((int)keyOID);
                    sigCtx->verify = 0;

                    /* Mirror PrivateKeyDecode/PublicKeyDecode: a recognised
                     * SLH-DSA OID with a per-variant disable returns
                     * NOT_COMPILED_IN; pass it through so callers can render
                     * a "variant unavailable" diagnostic instead of the
                     * malformed-DER ASN_UNKNOWN_OID_E. */
                    if (slhDsaParam == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) {
                        WOLFSSL_MSG("SLH-DSA variant not compiled in");
                        ERROR_OUT(NOT_COMPILED_IN, exit_cs);
                    }
                    if (slhDsaParam < 0) {
                        ERROR_OUT(ASN_UNKNOWN_OID_E, exit_cs);
                    }

                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.slhdsa =
                        (SlhDsaKey*)XMALLOC(sizeof(SlhDsaKey),
                                             sigCtx->heap,
                                             DYNAMIC_TYPE_SLHDSA);
                    if (sigCtx->key.slhdsa == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_SlhDsaKey_Init(sigCtx->key.slhdsa,
                                                 (enum SlhDsaParam)slhDsaParam,
                                                 NULL, INVALID_DEVID)) < 0) {
                        WOLFSSL_MSG("ASN Key init err: SLH-DSA");
                        goto exit_cs;
                    }
                    /* StoreKey() stashes the BIT STRING contents (raw
                     * public-key bytes) in cert->publicKey, not the
                     * SPKI envelope. Use ImportPublic which accepts
                     * raw bytes; _PublicKeyDecode would expect an SPKI
                     * SEQUENCE and fail with ASN_PARSE_E. */
                    if ((ret = wc_SlhDsaKey_ImportPublic(sigCtx->key.slhdsa,
                        key, keySz)) < 0) {
                        WOLFSSL_MSG("ASN Key import err: SLH-DSA");
                        goto exit_cs;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_SLHDSA */
            #ifdef WOLFSSL_HAVE_LMS
                case HSS_LMSk:
                {
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.lms = (LmsKey*)XMALLOC(sizeof(LmsKey),
                        sigCtx->heap, DYNAMIC_TYPE_LMS);
                    if (sigCtx->key.lms == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_LmsKey_Init(sigCtx->key.lms,
                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_LmsKey_ImportPubRaw(sigCtx->key.lms,
                            key, keySz)) < 0) {
                        WOLFSSL_MSG("ASN Key import error HSS/LMS");
                        goto exit_cs;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_LMS */
            #ifdef WOLFSSL_HAVE_XMSS
                case XMSSk:
                case XMSSMTk:
                {
                    int is_xmssmt = (keyOID == XMSSMTk);
                    sigCtx->verify = 0;
                #ifndef WOLFSSL_NO_MALLOC
                    sigCtx->key.xmss = (XmssKey*)XMALLOC(sizeof(XmssKey),
                        sigCtx->heap, DYNAMIC_TYPE_XMSS);
                    if (sigCtx->key.xmss == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #endif
                    if ((ret = wc_XmssKey_Init(sigCtx->key.xmss,
                            sigCtx->heap, sigCtx->devId)) < 0) {
                        goto exit_cs;
                    }
                    if ((ret = wc_XmssKey_ImportPubRaw_ex(sigCtx->key.xmss,
                            key, keySz, is_xmssmt)) < 0) {
                        WOLFSSL_MSG("ASN Key import error XMSS/XMSS^MT");
                        goto exit_cs;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_XMSS */
                default:
                    WOLFSSL_MSG("Verify Key type unknown");
                    ret = ASN_UNKNOWN_OID_E;
                    WOLFSSL_ERROR_VERBOSE(ret);
                    break;
            } /* switch (keyOID) */

            if (ret != 0) {
                goto exit_cs;
            }

            sigCtx->state = SIG_STATE_DO;

        #ifdef WOLFSSL_ASYNC_CRYPT
            if (sigCtx->devId != INVALID_DEVID && sigCtx->asyncDev && sigCtx->asyncCtx) {
                /* make sure event is initialized */
                WOLF_EVENT* event = &sigCtx->asyncDev->event;
                ret = wolfAsync_EventInit(event, WOLF_EVENT_TYPE_ASYNC_WOLFSSL,
                    sigCtx->asyncCtx, WC_ASYNC_FLAG_CALL_AGAIN);
            }
        #endif
        } /* SIG_STATE_KEY */
        FALL_THROUGH;

        case SIG_STATE_DO:
        {
            switch (keyOID) {
            #ifndef NO_RSA
                case RSAk:
                #ifdef WC_RSA_PSS
                case RSAPSSk:
                if (sigOID == RSAPSSk) {
                    /* TODO: pkCbRsaPss - RSA PSS callback. */
                    ret = wc_RsaPSS_VerifyInline_ex(sigCtx->sigCpy, sigSz,
                        &sigCtx->out, sigCtx->hash, sigCtx->mgf,
                        sigCtx->saltLen, sigCtx->key.rsa);
                }
                else
                #endif
                {
                #if defined(HAVE_PK_CALLBACKS)
                    if (sigCtx->pkCbRsa) {
                        ret = sigCtx->pkCbRsa(
                                sigCtx->sigCpy, sigSz, &sigCtx->out,
                                key, keySz,
                                sigCtx->pkCtxRsa);
                    }
                #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
                    !defined(WOLFSSL_RENESAS_TSIP_TLS)
                    else
                #else
                    if (!sigCtx->pkCbRsa ||
                        ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
                #endif /* WOLFSSL_RENESAS_FSPSM_TLS */
                #endif /* HAVE_PK_CALLBACKS */
                    {
                        ret = wc_RsaSSL_VerifyInline(sigCtx->sigCpy, sigSz,
                                                 &sigCtx->out, sigCtx->key.rsa);
                    }
                }
                break;
            #endif /* !NO_RSA */
            #if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
                case DSAk:
                {
                    ret = wc_DsaVerify(sigCtx->digest, sigCtx->sigCpy,
                            sigCtx->key.dsa, &sigCtx->verify);
                    break;
                }
            #endif /* !NO_DSA && !HAVE_SELFTEST */
            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                case SM2k:
                {
                    /* OpenSSL creates signature without CERT_SIG_ID. */
                    ret = wc_ecc_sm2_create_digest(CERT_SIG_ID, 0, buf, bufSz,
                        WC_HASH_TYPE_SM3, sigCtx->digest, WC_SM3_DIGEST_SIZE,
                        sigCtx->key.ecc);
                    if (ret == 0) {
                        sigCtx->typeH    = SM3h;
                        sigCtx->digestSz = WC_SM3_DIGEST_SIZE;
                    }
                    else {
                        WOLFSSL_MSG("SM2wSM3 create digest failed");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        goto exit_cs;
                    }
                    ret = wc_ecc_sm2_verify_hash(sig, sigSz, sigCtx->digest,
                        sigCtx->digestSz, &sigCtx->verify, sigCtx->key.ecc);
                    break;
                }
            #endif
            #if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY)
                case ECDSAk:
                {
                #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                    if (sigOID == CTC_SM3wSM2) {
                        /* OpenSSL creates signature without CERT_SIG_ID. */
                        ret = wc_ecc_sm2_create_digest(CERT_SIG_ID,
                            0, buf, bufSz, WC_HASH_TYPE_SM3,
                            sigCtx->digest, WC_SM3_DIGEST_SIZE,
                            sigCtx->key.ecc);
                        if (ret == 0) {
                            sigCtx->typeH    = SM3h;
                            sigCtx->digestSz = WC_SM3_DIGEST_SIZE;
                        }
                        else {
                            WOLFSSL_MSG("SM2wSM3 create digest failed");
                            WOLFSSL_ERROR_VERBOSE(ret);
                            goto exit_cs;
                        }
                        ret = wc_ecc_sm2_verify_hash(sig, sigSz, sigCtx->digest,
                            sigCtx->digestSz, &sigCtx->verify, sigCtx->key.ecc);
                    }
                    else
                #endif
                #if defined(HAVE_PK_CALLBACKS)
                    if (sigCtx->pkCbEcc) {
                        ret = sigCtx->pkCbEcc(
                                sig, sigSz,
                                sigCtx->digest, (unsigned int)sigCtx->digestSz,
                                key, keySz, &sigCtx->verify,
                                sigCtx->pkCtxEcc);
                    }
                #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
                    !defined(WOLFSSL_RENESAS_TSIP_TLS)
                    else
                #else
                    if (!sigCtx->pkCbEcc ||
                        ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
                #endif /* WOLFSSL_RENESAS_FSPSM_TLS */
                #endif /* HAVE_PK_CALLBACKS */
                    {
                        ret = wc_ecc_verify_hash(sig, sigSz, sigCtx->digest,
                            (word32)sigCtx->digestSz, &sigCtx->verify,
                            sigCtx->key.ecc);
                    }
                    break;
                }
            #endif /* HAVE_ECC */
            #if defined(HAVE_ED25519) && defined(HAVE_ED25519_VERIFY)
                case ED25519k:
                {
                    ret = wc_ed25519_verify_msg(sig, sigSz, buf, bufSz,
                                          &sigCtx->verify, sigCtx->key.ed25519);
                    break;
                }
            #endif
            #if defined(HAVE_ED448) && defined(HAVE_ED448_VERIFY)
                case ED448k:
                {
                    ret = wc_ed448_verify_msg(sig, sigSz, buf, bufSz,
                                             &sigCtx->verify, sigCtx->key.ed448,
                                             NULL, 0);
                    break;
                }
            #endif
            #if defined(HAVE_FALCON)
                case FALCON_LEVEL1k:
                case FALCON_LEVEL5k:
                {
                    ret = wc_falcon_verify_msg(sig, sigSz, buf, bufSz,
                                               &sigCtx->verify,
                                               sigCtx->key.falcon);
                    break;
                }
            #endif /* HAVE_FALCON */
            #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY)
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                case DILITHIUM_LEVEL2k:
                case DILITHIUM_LEVEL3k:
                case DILITHIUM_LEVEL5k:
                {
                    ret = wc_MlDsaKey_Verify(sigCtx->key.mldsa, sig, sigSz,
                                             buf, bufSz, &sigCtx->verify);
                    break;
                }
                #endif
                case ML_DSA_44k:
                case ML_DSA_65k:
                case ML_DSA_87k:
                {
                    ret = wc_MlDsaKey_VerifyCtx(sigCtx->key.mldsa, sig, sigSz,
                        NULL, 0, buf, bufSz, &sigCtx->verify);
                    break;
                }
            #endif /* WOLFSSL_HAVE_MLDSA */
            #if defined(WOLFSSL_HAVE_SLHDSA)
            #ifdef WOLFSSL_SLHDSA_SHA2
                case SLH_DSA_SHA2_128Fk:
                case SLH_DSA_SHA2_192Fk:
                case SLH_DSA_SHA2_256Fk:
                case SLH_DSA_SHA2_128Sk:
                case SLH_DSA_SHA2_192Sk:
                case SLH_DSA_SHA2_256Sk:
            #endif
                case SLH_DSA_SHAKE_128Fk:
                case SLH_DSA_SHAKE_192Fk:
                case SLH_DSA_SHAKE_256Fk:
                case SLH_DSA_SHAKE_128Sk:
                case SLH_DSA_SHAKE_192Sk:
                case SLH_DSA_SHAKE_256Sk:
                {
                    ret = wc_SlhDsaKey_Verify(sigCtx->key.slhdsa,
                                              NULL, 0, buf, bufSz,
                                              sig, sigSz);
                    if (ret == 0) {
                        sigCtx->verify = 1;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_SLHDSA */
            #ifdef WOLFSSL_HAVE_LMS
                case HSS_LMSk:
                {
                    ret = wc_LmsKey_Verify(sigCtx->key.lms, sig, sigSz,
                        buf, (int)bufSz);
                    sigCtx->verify = (ret == 0);
                    break;
                }
            #endif /* WOLFSSL_HAVE_LMS */
            #ifdef WOLFSSL_HAVE_XMSS
                case XMSSk:
                case XMSSMTk:
                {
                    ret = wc_XmssKey_Verify(sigCtx->key.xmss, sig, sigSz,
                        buf, (int)bufSz);
                    sigCtx->verify = (ret == 0);
                    break;
                }
            #endif /* WOLFSSL_HAVE_XMSS */
                default:
                    break;
            }  /* switch (keyOID) */

        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                goto exit_cs;
            }
        #endif

            if (ret < 0) {
                /* treat all errors as ASN_SIG_CONFIRM_E */
                ret = ASN_SIG_CONFIRM_E;
                WOLFSSL_ERROR_VERBOSE(ret);
                goto exit_cs;
            }

            sigCtx->state = SIG_STATE_CHECK;
        } /* SIG_STATE_DO */
        FALL_THROUGH;

        case SIG_STATE_CHECK:
        {
            switch (keyOID) {
            #ifndef NO_RSA
                case RSAk:
                #ifdef WC_RSA_PSS
                case RSAPSSk:
                if (sigOID == RSAPSSk) {
                #if (defined(HAVE_SELFTEST) && \
                     (!defined(HAVE_SELFTEST_VERSION) || \
                      (HAVE_SELFTEST_VERSION < 2))) || \
                    (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \
                     (HAVE_FIPS_VERSION < 2))
                    ret = wc_RsaPSS_CheckPadding_ex(sigCtx->digest,
                        sigCtx->digestSz, sigCtx->out, ret, sigCtx->hash,
                        sigCtx->saltLen);
                #elif (defined(HAVE_SELFTEST) && \
                       (HAVE_SELFTEST_VERSION == 2)) || \
                      (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \
                       (HAVE_FIPS_VERSION == 2))
                    ret = wc_RsaPSS_CheckPadding_ex(sigCtx->digest,
                        sigCtx->digestSz, sigCtx->out, ret, sigCtx->hash,
                        sigCtx->saltLen, 0);
                #else
                    ret = wc_RsaPSS_CheckPadding_ex2(sigCtx->digest,
                        (word32)sigCtx->digestSz, sigCtx->out, (word32)ret, sigCtx->hash,
                        sigCtx->saltLen, wc_RsaEncryptSize(sigCtx->key.rsa) * 8,
                        sigCtx->heap);
                #endif
                    break;
                }
                else
                #endif
                {
                    int encodedSigSz, verifySz;
                #if defined(WOLFSSL_RENESAS_TSIP_TLS) || \
                                            defined(WOLFSSL_RENESAS_FSPSM_TLS)
                    if (sigCtx->CertAtt.verifyByTSIP_SCE == 1) break;
                #endif
                #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
                    /* Holds a PKCS#1 DigestInfo encoding for RSA verification,
                     * never a PQC signature -- size to the classic tier. */
                    byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ,
                                        sigCtx->heap, DYNAMIC_TYPE_TMP_BUFFER);
                    if (encodedSig == NULL) {
                        ERROR_OUT(MEMORY_E, exit_cs);
                    }
                #else
                    byte encodedSig[MAX_ENCODED_CLASSIC_SIG_SZ];
                #endif

                    verifySz = ret;

                    /* make sure we're right justified */
                    encodedSigSz = (int)wc_EncodeSignature(encodedSig,
                            sigCtx->digest, (word32)sigCtx->digestSz,
                            sigCtx->typeH);
                    if (encodedSigSz == verifySz && sigCtx->out != NULL &&
                        XMEMCMP(sigCtx->out, encodedSig,
                            (size_t)encodedSigSz) == 0) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("RSA SSL verify match encode error");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }

                #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
                    XFREE(encodedSig, sigCtx->heap, DYNAMIC_TYPE_TMP_BUFFER);
                #endif
                    break;
                }
            #endif /* NO_RSA */
            #if !defined(NO_DSA) && !defined(HAVE_SELFTEST)
                case DSAk:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("DSA Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
            #endif /* !NO_DSA && !HAVE_SELFTEST */
            #ifdef HAVE_ECC
            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                case SM2k:
            #endif
                case ECDSAk:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("ECC Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
            #endif /* HAVE_ECC */
            #ifdef HAVE_ED25519
                case ED25519k:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("ED25519 Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
            #endif /* HAVE_ED25519 */
            #ifdef HAVE_ED448
                case ED448k:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("ED448 Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
            #endif /* HAVE_ED448 */
            #ifdef HAVE_FALCON
                case FALCON_LEVEL1k:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("FALCON_LEVEL1 Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
                case FALCON_LEVEL5k:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("FALCON_LEVEL5 Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    break;
                }
            #endif /* HAVE_FALCON */
            #ifdef WOLFSSL_HAVE_MLDSA
                #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
                case DILITHIUM_LEVEL2k:
                case DILITHIUM_LEVEL3k:
                case DILITHIUM_LEVEL5k:
                #endif /* WOLFSSL_MLDSA_FIPS204_DRAFT */
                case ML_DSA_44k:
                case ML_DSA_65k:
                case ML_DSA_87k:
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("DILITHIUM Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                    }
                    break;
            #endif /* WOLFSSL_HAVE_MLDSA */
            #ifdef WOLFSSL_HAVE_SLHDSA
            #ifdef WOLFSSL_SLHDSA_SHA2
                case SLH_DSA_SHA2_128Fk:
                case SLH_DSA_SHA2_192Fk:
                case SLH_DSA_SHA2_256Fk:
                case SLH_DSA_SHA2_128Sk:
                case SLH_DSA_SHA2_192Sk:
                case SLH_DSA_SHA2_256Sk:
            #endif
                case SLH_DSA_SHAKE_128Fk:
                case SLH_DSA_SHAKE_192Fk:
                case SLH_DSA_SHAKE_256Fk:
                case SLH_DSA_SHAKE_128Sk:
                case SLH_DSA_SHAKE_192Sk:
                case SLH_DSA_SHAKE_256Sk:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("SLH-DSA Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_SLHDSA */
            #ifdef WOLFSSL_HAVE_LMS
                case HSS_LMSk:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("HSS/LMS Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_LMS */
            #ifdef WOLFSSL_HAVE_XMSS
                case XMSSk:
                case XMSSMTk:
                {
                    if (sigCtx->verify == 1) {
                        ret = 0;
                    }
                    else {
                        WOLFSSL_MSG("XMSS/XMSS^MT Verify didn't match");
                        ret = ASN_SIG_CONFIRM_E;
                    }
                    break;
                }
            #endif /* WOLFSSL_HAVE_XMSS */
                default:
                    break;
            }  /* switch (keyOID) */

            break;
        } /* SIG_STATE_CHECK */

        default:
            break;
    } /* switch (sigCtx->state) */

exit_cs:

#else
    /* For NO_ASN_CRYPT return "not compiled in" */
    ret = NOT_COMPILED_IN;
#endif /* !NO_ASN_CRYPT */

    (void)keyOID;
    (void)sigOID;

    WOLFSSL_LEAVE("ConfirmSignature", ret);

#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
        return ret;
#endif

    FreeSignatureCtx(sigCtx);

    return ret;
}

#ifndef IGNORE_NAME_CONSTRAINTS

int wolfssl_local_MatchBaseName(int type, const char* name, int nameSz,
    const char* base, int baseSz)
{
    if (base == NULL || baseSz <= 0 || name == NULL || nameSz <= 0 ||
            name[0] == '.' ||
            (type != ASN_RFC822_TYPE && type != ASN_DNS_TYPE &&
             type != ASN_DIR_TYPE)) {
        return 0;
    }

    if (type == ASN_DNS_TYPE) {
        /* MatchDomainName treats one trailing dot as the absolute-FQDN marker.
         * Apply the same normalization before enforcing DNS name constraints.
         */
        if (name[nameSz - 1] == '.') {
            nameSz--;
        }
        if (base[baseSz - 1] == '.') {
            baseSz--;
        }
        if (nameSz <= 0 || baseSz <= 0) {
            return 0;
        }
    }

    if (nameSz < baseSz) {
        return 0;
    }

    if (type == ASN_DIR_TYPE)
        return XMEMCMP(name, base, (size_t)baseSz) == 0;

    /* If an email type, handle special cases where the base is only
     * a domain, or is an email address itself. */
    if (type == ASN_RFC822_TYPE) {
        const char* p = NULL;
        int count = 0;
        int baseIsEmail = 0;
        int atPos = -1;

        if (base[0] != '.') {
            p = base;
            count = 0;

            /* find the '@' in the base */
            while (*p != '@' && count < baseSz) {
                count++;
                p++;
            }

            if (count < baseSz) {
                /* '@' found in base - validate it's not at start/end and only one */
                if (count == 0 || count == baseSz - 1)
                    return 0; /* '@' at start or end of base is invalid */
                baseIsEmail = 1;
            }
        }

        /* verify that name is a valid email address, store @ position */
        p = name;
        count = 0;
        while (count < nameSz) {
            if (*p == '@') {
                if (atPos >= 0)
                    return 0; /* Multiple '@' in name is invalid */
                atPos = count;
            }
            count++;
            p++;
        }

        /* Validate '@' exists and is not at start or end */
        if (atPos < 0 || atPos == 0 || atPos == nameSz - 1)
            return 0;

        if (!baseIsEmail) {
            /* Base isn't an email address but a domain or host.
             * wind the name forward one character past its '@'. */
            name = name + atPos + 1;
            nameSz -= atPos + 1;
        }
    }

    /* RFC 5280 section 4.2.1.10
     * "...Any DNS name that can be constructed by simply adding zero or more
     *  labels to the left-hand side of the name satisfies the name constraint."
     * i.e www.host.example.com works for host.example.com name constraint and
     * host1.example.com does not.
     *
     * Note: For DNS type, RFC 5280 does not allow leading dot in constraint.
     * However, we accept it here for backwards compatibility. */
    if (type == ASN_DNS_TYPE || (type == ASN_RFC822_TYPE && base[0] == '.')) {
        int szAdjust = nameSz - baseSz;
        /* Check dot boundary: if there's a prefix and base doesn't start with
         * '.', the character before the matched suffix must be '.'.
         * When base starts with '.', the dot is included in the comparison. */
        if (szAdjust > 0 && base[0] != '.' && name[szAdjust - 1] != '.')
            return 0;
        name += szAdjust;
        nameSz -= szAdjust;
    }

    if (nameSz != baseSz)
        return 0;

    while (nameSz > 0) {
        if (XTOLOWER((unsigned char)*name) != XTOLOWER((unsigned char)*base)) {
            return 0;
        }
        name++;
        base++;
        nameSz--;
    }

    return 1;
}

int wolfssl_local_MatchUriNameConstraint(const char* uri, int uriSz,
    const char* base, int baseSz)
{
    const char* hostStart;
    const char* hostEnd;
    const char* p;
    const char* uriEnd;
    int hostSz;

    /* Need at least 3 bytes for the "://" scheme separator; rejecting short
     * inputs early also keeps the loop bound (uriEnd - 2) from forming a
     * pointer before `uri`. */
    if (uri == NULL || uriSz < 3 || base == NULL || baseSz <= 0) {
        return 0;
    }

    uriEnd = uri + uriSz;
    hostStart = NULL;
    for (p = uri; p < uriEnd - 2; p++) {
        if (p[0] == ':' && p[1] == '/' && p[2] == '/') {
            hostStart = p + 3;
            break;
        }
    }
    if (hostStart == NULL || hostStart >= uriEnd) {
        return 0;
    }

    for (p = hostStart; p < uriEnd; p++) {
        if (*p == '@') {
            hostStart = p + 1;
            break;
        }
        if (*p == '/' || *p == '?' || *p == '#') {
            break;
        }
        if (*p == '[') {
            break;
        }
    }
    if (hostStart >= uriEnd) {
        return 0;
    }

    if (*hostStart == '[') {
        hostStart++;
        hostEnd = hostStart;
        while (hostEnd < uriEnd && *hostEnd != ']') {
            hostEnd++;
        }
        if (hostEnd >= uriEnd) {
            return 0;
        }
        hostSz = (int)(hostEnd - hostStart);
    }
    else {
        hostEnd = hostStart;
        while (hostEnd < uriEnd && *hostEnd != ':' && *hostEnd != '/' &&
               *hostEnd != '?' && *hostEnd != '#') {
            hostEnd++;
        }
        hostSz = (int)(hostEnd - hostStart);
    }

    if (hostSz <= 0) {
        return 0;
    }

    /* RFC 5280 4.2.1.10: for URIs the constraint applies to the host part.
     * A constraint that begins with a '.' matches any host with one or more
     * additional leading labels (the bare host is excluded) - this is the
     * DNS subtree behaviour. A constraint without a leading '.' specifies a
     * single host and must match it exactly. */
    if (base[0] == '.') {
        return wolfssl_local_MatchBaseName(ASN_DNS_TYPE, hostStart, hostSz,
            base, baseSz);
    }
    else {
        int i;
        if (hostSz != baseSz) {
            return 0;
        }
        for (i = 0; i < baseSz; i++) {
            if (XTOLOWER((unsigned char)hostStart[i]) !=
                XTOLOWER((unsigned char)base[i])) {
                return 0;
            }
        }
        return 1;
    }
}

/* Locate the right-most label of `s` that ends (exclusive) at index `end`.
 * Sets *outStart to its starting index (the index of its first byte). If
 * outHasWild is non-NULL, sets *outHasWild to 1 iff the label contains '*'.
 * The '.' immediately before *outStart (if any) is the label separator and is
 * not part of either the current or the preceding label. */
static void PrevDnsLabel(const char* s, int end, int* outStart,
    int* outHasWild)
{
    int start = end;
    int hasWild = 0;
    while (start > 0 && s[start - 1] != '.') {
        if (s[start - 1] == '*') {
            hasWild = 1;
        }
        start--;
    }
    *outStart = start;
    if (outHasWild != NULL) {
        *outHasWild = hasWild;
    }
}

/* Match a wildcard DNS SAN against a DNS name-constraint subtree.
 *
 * A wildcard SAN denotes the set of names its '*'(s) can expand to. Because a
 * '*' never crosses a label boundary (see MatchDomainName), every expansion
 * has the same number of labels and only the content of '*'-bearing labels
 * varies. Matching is therefore done label-by-label from the right against the
 * constraint base.
 *
 * permitted != 0: containment. Accept only if EVERY expansion stays inside the
 *   subtree, i.e. each of the right-most base-length labels of the name is
 *   wildcard-free and equal to the corresponding base label. Extra labels to
 *   the left may be anything (adding labels on the left stays in-subtree).
 *
 * permitted == 0: intersection (for excluded subtrees). Reject if SOME
 *   expansion falls inside the subtree. A label containing a '*' is
 *   conservatively treated as able to match any single base label; a literal
 *   label must equal the base label.
 *
 * A leading '.' on the base denotes a proper subtree (the apex is excluded),
 * which requires at least one extra label on the left of the name.
 *
 * Returns 1 on match (contained / intersecting), 0 otherwise.
 */
int wolfssl_local_MatchDnsConstraintWildcard(const char* name, int nameSz,
    const char* base, int baseSz, int permitted)
{
    int baseLead;
    int ni, bi;

    if (name == NULL || base == NULL || nameSz <= 0 || baseSz <= 0) {
        return 0;
    }

    /* MatchDomainName treats one trailing dot as the absolute-FQDN marker.
     * Apply the same normalization before label-wise constraint matching.
     */
    if (name[nameSz - 1] == '.') {
        nameSz--;
    }
    if (base[baseSz - 1] == '.') {
        baseSz--;
    }
    if (nameSz <= 0 || baseSz <= 0 || name[0] == '.') {
        return 0;
    }

    baseLead = (base[0] == '.');
    if (baseLead) {
        base++;
        baseSz--;
    }
    /* A base of only dots (".", "..") has no labels to match. */
    if (baseSz <= 0 || base[0] == '.') {
        return 0;
    }

    ni = nameSz; /* exclusive end of the unconsumed name */
    bi = baseSz; /* exclusive end of the unconsumed base */

    /* Compare each base label (right to left) with the aligned name label. */
    while (bi > 0) {
        int nStart, bStart, nLen, bLen, k;
        int hasWild = 0;

        /* Base labels remain but the name has none left -> name is shorter in
         * labels and cannot contain the base. */
        if (ni <= 0) {
            return 0;
        }

        PrevDnsLabel(name, ni, &nStart, &hasWild);
        PrevDnsLabel(base, bi, &bStart, NULL);
        nLen = ni - nStart;
        bLen = bi - bStart;

        /* Empty label (e.g. "a..b" or an extra trailing dot) is invalid. */
        if (nLen == 0 || bLen == 0) {
            return 0;
        }

        if (hasWild) {
            /* permitted: a wildcard label cannot prove containment.
             * excluded: a wildcard label is conservatively treated as
             * compatible, so nothing more to check. */
            if (permitted) {
                return 0;
            }
        }
        else {
            /* Literal label: both modes require exact case-insensitive
             * equality with the base label. */
            if (nLen != bLen) {
                return 0;
            }
            for (k = 0; k < bLen; k++) {
                if (XTOLOWER((unsigned char)name[nStart + k]) !=
                    XTOLOWER((unsigned char)base[bStart + k])) {
                    return 0;
                }
            }
        }

        /* Consume both labels and the '.' that precedes them (if any). */
        ni = nStart - 1;
        bi = bStart - 1;
    }

    /* All base labels matched. ni >= 0 means name[ni] == '.' and at least one
     * extra label remains on the left; ni < 0 means the name had exactly the
     * base's label count (an apex match). A leading-dot base is a proper
     * subtree and requires at least one extra left label. */
    if (baseLead && ni < 0) {
        return 0;
    }

    return 1;
}

/* Check if IP address matches a name constraint.
 * IP name constraints contain IP address and subnet mask.
 * IPv4: ip is 4 bytes, constraint is 8 bytes (4 IP + 4 mask)
 * IPv6: ip is 16 bytes, constraint is 32 bytes (16 IP + 16 mask)
 *
 * ip: IP address bytes
 * ipSz: length of ip
 * constraint: constraint bytes (IP + mask)
 * constraintSz: length of constraint
 *
 * return 1 if IP matches constraint, otherwise 0
 */
int wolfssl_local_MatchIpSubnet(const byte* ip, int ipSz,
    const byte* constraint, int constraintSz)
{
    int i;
    int match = 1;

    if (ip == NULL || constraint == NULL || ipSz <= 0 || constraintSz <= 0) {
        return 0;
    }

    /* Constraint should be 2x address length (IP + mask) */
    if (constraintSz != ipSz * 2) {
        return 0;
    }

    for (i = 0; i < ipSz; i++) {
        if ((ip[i] & constraint[ipSz + i]) !=
            (constraint[i] & constraint[ipSz + i])) {
            match = 0;
        }
    }

    return match;
}

/* RFC 5280 4.2.1.10: otherName matching is byte-exact comparison of the
 * full OtherName encoding (OID || [0] EXPLICIT value). Both the leaf SAN
 * (cert->altOtherNamesRaw) and the constraint subtree (Base_entry from
 * DecodeSubtree) store the same form, so a memcmp suffices. */
static int MatchOtherNameConstraint(DNS_entry* name, Base_entry* current)
{
    if (name == NULL || current == NULL)
        return 0;
    if (name->len != current->nameSz)
        return 0;
    return XMEMCMP(name->name, current->name, (size_t)current->nameSz) == 0;
}

/* Return 1 if the name contains a wildcard '*' character. */
static int DnsNameHasWildcard(const char* name, int nameSz)
{
    int i;
    for (i = 0; i < nameSz; i++) {
        if (name[i] == '*') {
            return 1;
        }
    }
    return 0;
}

/* Search through the list to find if the name is permitted.
 * name     The DNS name to search for
 * dnsList  The list to search through
 * nameType Type of DNS name to currently searching
 * return 1 if found in list or if not needed
 * return 0 if not found in the list but is needed
 */
static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
{
    Base_entry* current = dnsList;
    int match = 0;
    int need  = 0;
    int ret   = 1; /* is ok unless needed and no match found */

    while (current != NULL) {
        if (current->type == nameType) {
            need = 1; /* restriction on permitted names is set for this type */
            if (nameType == ASN_IP_TYPE) {
                /* IP address uses subnet matching (IP + mask) */
                if (wolfssl_local_MatchIpSubnet((const byte*)name->name,
                        name->len, (const byte*)current->name,
                        current->nameSz)) {
                    match = 1;
                    break;
                }
            }
            else if (nameType == ASN_URI_TYPE) {
                if (wolfssl_local_MatchUriNameConstraint(name->name, name->len,
                        current->name, current->nameSz)) {
                    match = 1;
                    break;
                }
            }
            else if (nameType == ASN_OTHER_TYPE) {
                if (MatchOtherNameConstraint(name, current)) {
                    match = 1;
                    break;
                }
            }
            else if (nameType == ASN_RID_TYPE) {
                /* registeredID matches when the OID bodies are bytewise
                 * equal. RFC 5280 Sec. 4.2.1.10 does not define a
                 * subtree relation for OIDs, so use exact-match. */
                if (name->len == current->nameSz &&
                    XMEMCMP(name->name, current->name,
                            (size_t)name->len) == 0) {
                    match = 1;
                    break;
                }
            }
            else if (nameType == ASN_DNS_TYPE &&
                     DnsNameHasWildcard(name->name, name->len)) {
                /* Wildcard DNS SAN: a '*' can expand to a longer label, so the
                 * byte-length guard used for literal names below is invalid.
                 * Permit only if every expansion stays inside the subtree. */
                if (wolfssl_local_MatchDnsConstraintWildcard(name->name,
                        name->len, current->name, current->nameSz, 1)) {
                    match = 1;
                    break;
                }
            }
            else if (name->len >= current->nameSz &&
                wolfssl_local_MatchBaseName(nameType, name->name, name->len,
                                            current->name, current->nameSz)) {
                match = 1; /* found the current name in the permitted list*/
                break;
            }
        }
        current = current->next;
    }

    /* check if permitted name restriction was set and no matching name found */
    if (need && !match)
        ret = 0;

    return ret;
}


/* Search through the list to find if the name is excluded.
 * name     The DNS name to search for
 * dnsList  The list to search through
 * nameType Type of DNS name to currently searching
 * return 1 if found in list and 0 if not found in the list
 */
static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
{
    int ret = 0; /* default of not found in the list */
    Base_entry* current = dnsList;

    while (current != NULL) {
        if (current->type == nameType) {
            if (nameType == ASN_IP_TYPE) {
                /* IP address uses subnet matching (IP + mask) */
                if (wolfssl_local_MatchIpSubnet((const byte*)name->name,
                        name->len, (const byte*)current->name,
                        current->nameSz)) {
                    ret = 1;
                    break;
                }
            }
            else if (nameType == ASN_URI_TYPE) {
                if (wolfssl_local_MatchUriNameConstraint(name->name, name->len,
                        current->name, current->nameSz)) {
                    ret = 1;
                    break;
                }
            }
            else if (nameType == ASN_OTHER_TYPE) {
                if (MatchOtherNameConstraint(name, current)) {
                    ret = 1;
                    break;
                }
            }
            else if (nameType == ASN_RID_TYPE) {
                /* registeredID matches when the OID bodies are bytewise
                 * equal. See PermittedListOk for the rationale. */
                if (name->len == current->nameSz &&
                    XMEMCMP(name->name, current->name,
                            (size_t)name->len) == 0) {
                    ret = 1;
                    break;
                }
            }
            else if (nameType == ASN_DNS_TYPE &&
                     DnsNameHasWildcard(name->name, name->len)) {
                /* Wildcard DNS SAN: a '*' can expand to a longer label, so the
                 * byte-length guard used for literal names below is invalid.
                 * Exclude if any expansion can fall inside the subtree. */
                if (wolfssl_local_MatchDnsConstraintWildcard(name->name,
                        name->len, current->name, current->nameSz, 0)) {
                    ret = 1;
                    break;
                }
            }
            else if (name->len >= current->nameSz &&
                wolfssl_local_MatchBaseName(nameType, name->name, name->len,
                                            current->name, current->nameSz)) {
                ret = 1;
                break;
            }
        }
        current = current->next;
    }

    return ret;
}


static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
{
    const byte nameTypes[] = {ASN_RFC822_TYPE, ASN_DNS_TYPE, ASN_DIR_TYPE,
                              ASN_IP_TYPE, ASN_URI_TYPE, ASN_OTHER_TYPE,
                              ASN_RID_TYPE};
    int i;

    if (signer == NULL || cert == NULL)
        return 0;

    if (signer->excludedNames == NULL && signer->permittedNames == NULL &&
            !signer->extNameConstraintHasUnsupported)
        return 1;

    for (i=0; i < (int)sizeof(nameTypes); i++) {
        byte nameType = nameTypes[i];
        DNS_entry* name = NULL;
        DNS_entry  subjectDnsName; /* temporary node used for subject name */

        XMEMSET(&subjectDnsName, 0, sizeof(DNS_entry));
        switch (nameType) {
            case ASN_DNS_TYPE:
                name = cert->altNames;

                /* When no SAN is present, apply DNS name constraints to the
                 * Subject CN. */
                if (cert->subjectCN != NULL && cert->altNames == NULL) {
                    subjectDnsName.next = NULL;
                    subjectDnsName.type = ASN_DNS_TYPE;
                    subjectDnsName.len  = cert->subjectCNLen;
                    subjectDnsName.name = cert->subjectCN;
                }
                break;
            case ASN_IP_TYPE:
                /* IP addresses are stored in altNames with type ASN_IP_TYPE */
                name = cert->altNames;
                break;
            case ASN_RFC822_TYPE:
                /* Shouldn't it validate E= in subject as well? */
                name = cert->altEmailNames;

                /* Add subject email for checking. */
                if (cert->subjectEmail != NULL) {
                    /* RFC 5280 section 4.2.1.10
                     * "When constraints are imposed on the rfc822Name name
                     * form, but the certificate does not include a subject
                     * alternative name, the rfc822Name constraint MUST be
                     * applied to the attribute of type emailAddress in the
                     * subject distinguished name" */
                    subjectDnsName.next = NULL;
                    subjectDnsName.type = ASN_RFC822_TYPE;
                    subjectDnsName.len  = cert->subjectEmailLen;
                    subjectDnsName.name = cert->subjectEmail;
                }
                break;
            case ASN_DIR_TYPE:
            #ifndef WOLFSSL_NO_ASN_STRICT
                name = cert->altDirNames;
            #endif

                /* RFC 5280 section 4.2.1.10
                    "Restrictions of the form directoryName MUST be
                    applied to the subject field .... and to any names
                    of type directoryName in the subjectAltName
                    extension"
                */
                if (cert->subjectRaw != NULL) {
                    subjectDnsName.next = NULL;
                    subjectDnsName.type = ASN_DIR_TYPE;
                    subjectDnsName.len = cert->subjectRawLen;
                    subjectDnsName.name = (const char *)cert->subjectRaw;
                }
                break;
            case ASN_URI_TYPE:
                name = cert->altNames;
                break;
            case ASN_OTHER_TYPE:
                /* otherName SAN entries are stored on cert->altOtherNamesRaw
                 * (kept separate from altNames so the public altNames view
                 * is unaffected). Each entry holds the raw OtherName
                 * encoding (OID || [0] EXPLICIT value) and is byte-matched
                 * against the issuing CA's subtree. */
                name = cert->altOtherNamesRaw;
                break;
            case ASN_RID_TYPE:
                /* registeredID entries also live on cert->altNames as
                 * raw OID body bytes. */
                name = cert->altNames;
                break;
            default:
                return 0;
        }

        while (name != NULL) {
            /* Only check entries that match the current nameType. */
            if (name->type == nameType) {
                if (IsInExcludedList(name, signer->excludedNames,
                        nameType) == 1) {
                    WOLFSSL_MSG("Excluded name was found!");
                    return 0;
                }

                /* Check against the permitted list */
                if (PermittedListOk(name, signer->permittedNames,
                        nameType) != 1) {
                    WOLFSSL_MSG("Permitted name was not found!");
                    return 0;
                }
            }

            name = name->next;
        }

        /* handle comparing against subject name too */
        if (subjectDnsName.len > 0 && subjectDnsName.name != NULL) {
            if (IsInExcludedList(&subjectDnsName, signer->excludedNames,
                        nameType) == 1) {
                WOLFSSL_MSG("Excluded name was found!");
                return 0;
            }

            /* Check against the permitted list */
            if (PermittedListOk(&subjectDnsName, signer->permittedNames,
                        nameType) != 1) {
                WOLFSSL_MSG("Permitted name was not found!");
                return 0;
            }
        }
    }

    /* RFC 5280 4.2.1.10: "If a name constraints extension that is marked as
     * critical imposes constraints on a particular name form ... the
     * application MUST either process the constraint or reject the
     * certificate." otherName and registeredID are both processed by
     * byte-comparison above; any remaining unsupported forms
     * (x400Address, ediPartyName) trigger the fail-closed reject below. */
    if (signer->extNameConstraintCrit &&
            signer->extNameConstraintHasUnsupported) {
        WOLFSSL_MSG("Critical nameConstraints contains unsupported "
                    "GeneralName form; rejecting");
        return 0;
    }

    return 1;
}

#endif /* IGNORE_NAME_CONSTRAINTS */

#ifdef WOLFSSL_ASN_TEMPLATE
#if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
/* ASN.1 template for OtherName of an X.509 certificate.
 * X.509: RFC 5280, 4.2.1.6 - OtherName (without implicit outer SEQUENCE).
 * HW Name: RFC 4108, 5 - Hardware Module Name
 * Only support HW Name where the type is a HW serial number.
 *
 * Other Names handled for FPKI (Federal PKI) use:
 * UPN (Universal Principal Name), a non-standard Other Name
 *  (RFC3280 sec 4.2.1.7). Often used with FIPS 201 smartcard login.
 * FASC-N (Federal Agency Smart Credential Number), defined in the document
 *  fpki-x509-cert-policy-common.pdf. Used for a smart card ID.
 */
static const ASNItem otherNameASN[] = {
/* TYPEID   */ { 0, ASN_OBJECT_ID, 0, 0, 0 },
/* VALUE    */ { 0, ASN_CONTEXT_SPECIFIC | ASN_OTHERNAME_VALUE, 1, 1, 0 },
/* UPN      */     { 1, ASN_UTF8STRING, 0, 0, 2 },
/* FASC-N   */     { 1, ASN_OCTET_STRING, 0, 0, 2 },
/* HWN_SEQ  */     { 1, ASN_SEQUENCE, 1, 0, 2 },
/* HWN_TYPE */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* HWN_NUM  */         { 2, ASN_OCTET_STRING, 0, 0, 0 }
};
enum {
    OTHERNAMEASN_IDX_TYPEID = 0,
    OTHERNAMEASN_IDX_VALUE,
    OTHERNAMEASN_IDX_UPN,
    OTHERNAMEASN_IDX_FASCN,
    OTHERNAMEASN_IDX_HWN_SEQ,
    OTHERNAMEASN_IDX_HWN_TYPE,
    OTHERNAMEASN_IDX_HWN_NUM
};

/* Number of items in ASN.1 template for OtherName of an X.509 certificate. */
#define otherNameASN_Length (sizeof(otherNameASN) / sizeof(ASNItem))

#ifdef WOLFSSL_SEP
static int DecodeSEP(ASNGetData* dataASN, DecodedCert* cert)
{
    int ret = 0;
    word32 oidLen, serialLen;

    oidLen = dataASN[OTHERNAMEASN_IDX_HWN_TYPE].data.oid.length;
    serialLen = dataASN[OTHERNAMEASN_IDX_HWN_NUM].data.ref.length;

    /* Allocate space for HW type OID. */
    cert->hwType = (byte*)XMALLOC(oidLen, cert->heap,
                                  DYNAMIC_TYPE_X509_EXT);
    if (cert->hwType == NULL)
        ret = MEMORY_E;

    if (ret == 0) {
        /* Copy, into cert HW type OID */
        XMEMCPY(cert->hwType,
                dataASN[OTHERNAMEASN_IDX_HWN_TYPE].data.oid.data, oidLen);
        cert->hwTypeSz = (int)oidLen;
        /* TODO: check this is the HW serial number OID - no test data. */

        /* Allocate space for HW serial number, +1 for null terminator. */
        cert->hwSerialNum = (byte*)XMALLOC(serialLen + 1, cert->heap,
                                           DYNAMIC_TYPE_X509_EXT);
        if (cert->hwSerialNum == NULL) {
            WOLFSSL_MSG("\tOut of Memory");
            ret = MEMORY_E;
        }
    }
    if (ret == 0) {
        /* Copy into cert HW serial number. */
        XMEMCPY(cert->hwSerialNum,
                dataASN[OTHERNAMEASN_IDX_HWN_NUM].data.ref.data, serialLen);
        cert->hwSerialNum[serialLen] = '\0';
        cert->hwSerialNumSz = (int)serialLen;
    }
    return ret;
}
#endif /* WOLFSSL_SEP */

static int DecodeOtherHelper(ASNGetData* dataASN, DecodedCert* cert, int oid)
{
    DNS_entry* entry = NULL;
    int ret = 0;
    word32 bufLen   = 0;
    const char* buf = NULL;

    switch (oid) {
#ifdef WOLFSSL_FPKI
        case FASCN_OID:
            bufLen = dataASN[OTHERNAMEASN_IDX_FASCN].data.ref.length;
            buf    = (const char*)dataASN[OTHERNAMEASN_IDX_FASCN].data.ref.data;
            break;
#endif /* WOLFSSL_FPKI */
        case UPN_OID:
            bufLen = dataASN[OTHERNAMEASN_IDX_UPN].data.ref.length;
            buf    = (const char*)dataASN[OTHERNAMEASN_IDX_UPN].data.ref.data;
            break;
        default:
            WOLFSSL_ERROR_VERBOSE(ASN_UNKNOWN_OID_E);
            ret = ASN_UNKNOWN_OID_E;
            break;
    }

    if (ret == 0) {
        ret = SetDNSEntry(cert->heap, buf, (int)bufLen, ASN_OTHER_TYPE, &entry);
        if (ret == 0) {
        #ifdef WOLFSSL_FPKI
            entry->oidSum = oid;
        #endif
            AddDNSEntryToList(&cert->altNames, entry);
        }
    }
    return ret;
}

/* Decode data with OtherName format from after implicit SEQUENCE.
 *
 * @param [in, out] cert      Certificate object.
 * @param [in]      input     Buffer containing encoded OtherName.
 * @param [in, out] inOutIdx  On in, the index of the start of the OtherName.
 *                            On out, index after OtherName.
 * @param [in]      maxIdx    Maximum index of data in buffer.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  ASN_PARSE_E when OID does is not HW Name.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  BUFFER_E when data in buffer is too small.
 */
static int DecodeOtherName(DecodedCert* cert, const byte* input,
                           word32* inOutIdx, int len)
{
    DECL_ASNGETDATA(dataASN, otherNameASN_Length);
    int ret = 0;
    word32 maxIdx = *inOutIdx + (word32)len;
    const char* name = (const char*)input + *inOutIdx;

    CALLOC_ASNGETDATA(dataASN, otherNameASN_Length, ret, cert->heap);

    if (ret == 0) {
        /* Check the first OID is a recognized Alt Cert Name type. */
        GetASN_OID(&dataASN[OTHERNAMEASN_IDX_TYPEID], oidCertAltNameType);
        /* Parse OtherName. */
        ret = GetASN_Items(otherNameASN, dataASN, otherNameASN_Length, 1, input,
                           inOutIdx, maxIdx);
    }
    if (ret == 0) {
        /* Ensure expected OID. */
        switch (dataASN[OTHERNAMEASN_IDX_TYPEID].data.oid.sum) {
        #ifdef WOLFSSL_SEP
            case HW_NAME_OID:
                /* Only support HW serial number. */
                GetASN_OID(&dataASN[OTHERNAMEASN_IDX_HWN_TYPE], oidIgnoreType);
                ret = DecodeSEP(dataASN, cert);
                break;
        #endif /* WOLFSSL_SEP */
        #ifdef WOLFSSL_FPKI
            case FASCN_OID:
        #endif /* WOLFSSL_FPKI */
            case UPN_OID:
                ret = DecodeOtherHelper(dataASN, cert,
                           (int)dataASN[OTHERNAMEASN_IDX_TYPEID].data.oid.sum);
                break;
            default:
                WOLFSSL_MSG("\tadding unsupported OID");
                ret = SetDNSEntry(cert->heap, name, len, ASN_OTHER_TYPE,
                        &cert->altNames);
                break;
        }
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_SEP || WOLFSSL_FPKI */

/* Decode a GeneralName.
 *
 * @param [in]      input     Buffer containing encoded OtherName.
 * @param [in, out] inOutIdx  On in, the index of the start of the OtherName.
 *                            On out, index after OtherName.
 * @param [in]      len       Length of data in buffer.
 * @param [in]      cert      Decoded certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
/* 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 DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag,
                             int len, DecodedCert* cert)
{
    int ret = 0;
    word32 idx = *inOutIdx;

    /* GeneralName choice: dnsName */
    if (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) {
        ret = DecodeGeneralNameCheckChars(input + idx, len);
        if (ret != 0) {
            return ret;
        }
        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_DNS_TYPE, &cert->altNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
#ifndef IGNORE_NAME_CONSTRAINTS
    /* GeneralName choice: directoryName */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE)) {
        int strLen;
        word32 idxDir = idx;

        /* Expecting a SEQUENCE using up all data. */
        if (GetASN_Sequence(input, &idxDir, &strLen, idx + (word32)len, 1) < 0)
        {
            WOLFSSL_MSG("\tfail: seq length");
            return ASN_PARSE_E;
        }

        ret = SetDNSEntry(cert->heap, (const char*)(input + idxDir), strLen,
                ASN_DIR_TYPE, &cert->altDirNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: rfc822Name */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) {
        ret = DecodeGeneralNameCheckChars(input + idx, len);
        if (ret != 0) {
            return ret;
        }
        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_RFC822_TYPE, &cert->altEmailNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: uniformResourceIdentifier */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) {
        ret = DecodeGeneralNameCheckChars(input + idx, len);
        if (ret != 0) {
            return ret;
        }
        WOLFSSL_MSG("\tPutting URI into list but not using");

    #ifndef WOLFSSL_NO_ASN_STRICT
        /* 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). */
        {
            int i;

            /* skip past scheme (i.e http,ftp,...) finding first ':' char */
            for (i = 0; i < len; i++) {
                if (input[idx + (word32)i] == ':') {
                    break;
                }
                if (input[idx + (word32)i] == '/') {
                    /* path is relative since '/' was encountered before ':'. */
                    return ASN_ALT_NAME_E;
                }
            }

            /* test hier-part is empty */
            if (i == 0 || i == len) {
                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 + (word32)i] != ':') {
                WOLFSSL_MSG("\tAlt Name must be absolute URI");
                WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E);
                return ASN_ALT_NAME_E;
            }
        }
    #endif

        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_URI_TYPE, &cert->altNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: iPAddress
     *
     * Always parse iPAddress into cert->altNames so ConfirmNameConstraints
     * can enforce permitted/excluded iPAddress subtrees (RFC 5280
     * Sec. 4.2.1.10). The entry holds raw 4/16 octet payloads;
     * WOLFSSL_IP_ALT_NAME still gates the human-readable ipString
     * generation in SetDNSEntry.
     *
     * Consequences for downstream consumers when WOLFSSL_IP_ALT_NAME is
     * undefined:
     *   - wolfSSL_X509_get_next_altname (string iterator): explicitly
     *     skips iPAddress entries, since returning raw bytes as a C
     *     string would truncate at any embedded NUL. This preserves the
     *     pre-fix behavior for that getter.
     *   - CheckForAltNames (TLS hostname matching): the iPAddress branch
     *     is compiled out, so iPAddress entries cannot match anything;
     *     they are also excluded from the *checkCN decision so an
     *     IP-only-SAN cert still falls back to CN matching as before.
     *   - All other altNames walkers (e.g. ALT_NAMES_OID handling in
     *     wolfSSL_X509_get_ext_d2i, wolfssl_x509_alt_names_to_gn,
     *     FlattenAltNames in cert generation) now see iPAddress entries
     *     unconditionally. This is intentional and brings wolfSSL closer
     *     to OpenSSL's SAN-exposure semantics; the OPENSSL_EXTRA APIs
     *     surface the raw octets as OCTET_STRING already (see the
     *     ASN_IP_TYPE case under WOLFSSL_GEN_IPADD in src/x509.c).
     */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) {
        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_IP_TYPE, &cert->altNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: registeredID
     *
     * Always parse registeredID into cert->altNames so
     * ConfirmNameConstraints can enforce permitted/excluded subtrees
     * (RFC 5280 Sec. 4.2.1.10). The entry holds raw OID body bytes;
     * WOLFSSL_RID_ALT_NAME only gates the human-readable ridString
     * generation in SetDNSEntry. Downstream consumer treatment in
     * default builds:
     *   - wolfSSL_X509_get_next_altname (string iterator): skips
     *     ASN_RID_TYPE entries (raw OID bytes are not a C string).
     *   - CheckForAltNames (TLS hostname matching): skips ASN_RID_TYPE
     *     unconditionally and excludes them from *checkCN, so a cert
     *     with only registeredID SANs still falls back to CN.
     *   - CheckForAltNames (TLS hostname matching): skips ASN_URI_TYPE
     *     for DNS hostname checks (RFC 9525 Sec. 6.3) but URI SAN presence
     *     still suppresses CN fallback because URI-ID is a distinct presented
     *     identifier.
     *   - DNS_to_GENERAL_NAME (used by wolfSSL_X509_get_ext) and the
     *     ALT_NAMES_OID arm of wolfSSL_X509_get_ext_d2i: build a proper
     *     ASN1_OBJECT in d.registeredID from raw OID bytes regardless
     *     of WOLFSSL_RID_ALT_NAME, so OPENSSL_EXTRA-style callers see
     *     correctly-typed GENERAL_NAME entries.
     *   - X509_print_name_entry: emits "Registered ID:<unavailable>"
     *     when ridString is not generated, instead of failing the
     *     whole print operation. */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) {
        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_RID_TYPE, &cert->altNames);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
#endif /* IGNORE_NAME_CONSTRAINTS */
#ifndef IGNORE_NAME_CONSTRAINTS
    /* GeneralName choice: otherName.
     * Store the raw OtherName encoding (OID || [0] EXPLICIT value) on a
     * dedicated internal list so ConfirmNameConstraints() can byte-match
     * it against the issuing CA's nameConstraints subtree (RFC 5280
     * 4.2.1.10). The raw form is kept separate from cert->altNames so
     * the public altNames view (used by OpenSSL-compat APIs) reflects
     * exactly what the SAN extension carries. */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
        ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
                ASN_OTHER_TYPE, &cert->altOtherNamesRaw);
        if (ret != 0) {
            return ret;
        }
    #if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
        /* FPKI/SEP also OID-decode the otherName into a separate altNames
         * entry that holds the parsed UPN/FASCN value (with oidSum != 0).
         * That parsed entry is consumed by wc_GetUUIDFromCert /
         * wc_GetFASCNFromCert; ConfirmNameConstraints() does not look at
         * it - it iterates altOtherNamesRaw instead. */
        ret = DecodeOtherName(cert, input, &idx, len);
    #else
        idx += (word32)len;
    #endif
    }
#elif defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
    /* No name constraints support in the build, but FPKI/SEP still need
     * the parsed otherName entry for wc_GetUUIDFromCert /
     * wc_GetFASCNFromCert. */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
        ret = DecodeOtherName(cert, input, &idx, len);
    }
#endif
    /* GeneralName choice: dNSName, x400Address, ediPartyName */
    else {
        WOLFSSL_MSG("\tUnsupported name type, skipping");
        idx += (word32)len;
    }

    if (ret == 0) {
        /* Return index of next encoded byte. */
        *inOutIdx = idx;
    }
    return ret;
}

/* ASN.1 choices for GeneralName.
 * X.509: RFC 5280, 4.2.1.6 - GeneralName.
 */
static const byte generalNameChoice[] = {
    ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0,
    ASN_CONTEXT_SPECIFIC                   | 1,
    ASN_CONTEXT_SPECIFIC                   | 2,
    ASN_CONTEXT_SPECIFIC                   | 3,
    ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 4,
    ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 5,
    ASN_CONTEXT_SPECIFIC                   | 6,
    ASN_CONTEXT_SPECIFIC                   | 7,
    ASN_CONTEXT_SPECIFIC                   | 8,
    0
};

/* ASN.1 template for GeneralName.
 * X.509: RFC 5280, 4.2.1.6 - GeneralName.
 */
static const ASNItem altNameASN[] = {
    { 0, ASN_CONTEXT_SPECIFIC | 0, 0, 1, 0 }
};
enum {
    ALTNAMEASN_IDX_GN = 0
};

/* Number of items in ASN.1 template for GeneralName. */
#define altNameASN_Length (sizeof(altNameASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Decode subject alternative names extension.
 *
 * RFC 5280 4.2.1.6.  Subject Alternative Name
 *
 * @param [in]      input  Buffer holding encoded data.
 * @param [in]      sz     Size of encoded data in bytes.
 * @param [in, out] cert   Decoded certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeAltNames(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    int ret = 0;
    word32 numNames = 0;

    WOLFSSL_ENTER("DecodeAltNames");

    /* Get SEQUENCE and expect all data to be accounted for. */
    if (GetASN_Sequence(input, &idx, &length, sz, 1) != 0) {
        WOLFSSL_MSG("\tBad Sequence");
        ret = ASN_PARSE_E;
    }

    if ((ret == 0) && (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);
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
    #ifdef OPENSSL_ALL
        cert->extSubjAltNameSrc = input;
        cert->extSubjAltNameSz = sz;
    #endif

        cert->weOwnAltNames = 1;

        if ((word32)length + idx != sz) {
            ret = ASN_PARSE_E;
        }
    }

    while ((ret == 0) && (idx < sz)) {
        ASNGetData dataASN[altNameASN_Length];

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

        /* Clear dynamic data items. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        /* Parse GeneralName with the choices supported. */
        GetASN_Choice(&dataASN[ALTNAMEASN_IDX_GN], generalNameChoice);
        /* Decode a GeneralName choice. */
        ret = GetASN_Items(altNameASN, dataASN, altNameASN_Length, 0, input,
                           &idx, sz);
        if (ret == 0) {
            ret = DecodeGeneralName(input, &idx, dataASN[ALTNAMEASN_IDX_GN].tag,
                                  (int)dataASN[ALTNAMEASN_IDX_GN].length, cert);
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for BasicConstraints.
 * X.509: RFC 5280, 4.2.1.9 - BasicConstraints.
 */
static const ASNItem basicConsASN[] = {
/* SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CA   */     { 1, ASN_BOOLEAN, 0, 0, 1 },
/* PLEN */     { 1, ASN_INTEGER, 0, 0, 1 }
};
enum {
    BASICCONSASN_IDX_SEQ = 0,
    BASICCONSASN_IDX_CA,
    BASICCONSASN_IDX_PLEN
};

/* Number of items in ASN.1 template for BasicContraints. */
#define basicConsASN_Length (sizeof(basicConsASN) / sizeof(ASNItem))
#endif

/* Decode basic constraints extension
 *
 * X.509: RFC 5280, 4.2.1.9 - BasicConstraints.
 *
 * @param [in]      input          Buffer holding data.
 * @param [in]      sz             Size of data in buffer.
 * @param [out]     isCa           Whether it is a CA.
 * @param [out]     pathLength     CA path length.
 * @param [out]     pathLengthSet  Whether pathLength is valid on return.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when CA boolean is present and false (default is false).
 * @return  ASN_PARSE_E when CA boolean is not present unless
 *          WOLFSSL_X509_BASICCONS_INT is defined. Only a CA extension.
 * @return  ASN_PARSE_E when path length more than 7 bits.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeBasicCaConstraint(const byte* input, int sz, byte *isCa,
                            word16 *pathLength, byte *pathLengthSet)
{
    DECL_ASNGETDATA(dataASN, basicConsASN_Length);
    int ret = 0;
    word32 idx = 0;
    byte innerIsCA = 0;

    WOLFSSL_ENTER("DecodeBasicCaConstraint");

    CALLOC_ASNGETDATA(dataASN, basicConsASN_Length, ret, NULL);

    if (ret == 0) {
        /* Get the CA boolean and path length when present. */
        GetASN_Boolean(&dataASN[BASICCONSASN_IDX_CA], &innerIsCA);
        GetASN_Int16Bit(&dataASN[BASICCONSASN_IDX_PLEN], pathLength);

        ret = GetASN_Items(basicConsASN, dataASN, basicConsASN_Length, 1, input,
                           &idx, (word32)sz);
    }

    /* Empty SEQUENCE is OK - nothing to store. */
    if ((ret == 0) && (dataASN[BASICCONSASN_IDX_SEQ].length != 0)) {
        /* Bad encoding when CA Boolean is false
         * (default when not present). */
#if !defined(ASN_TEMPLATE_SKIP_ISCA_CHECK) && \
    !defined(WOLFSSL_ALLOW_ENCODING_CA_FALSE)
        if ((dataASN[BASICCONSASN_IDX_CA].length != 0) && (!innerIsCA)) {
            WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
            ret = ASN_PARSE_E;
        }
#endif
        /* Path length must be a 7-bit value. */
        if ((ret == 0) && (*pathLength >= (1 << 7))) {
            WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
            ret = ASN_PARSE_E;
        }
        if ((ret == 0) && *pathLength > WOLFSSL_MAX_PATH_LEN) {
            WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_SIZE_E);
            ret = ASN_PATHLEN_SIZE_E;
        }
        /* Store CA boolean and whether a path length was seen. */
        if (ret == 0) {
            /* isCA in certificate is a 1 bit of a byte. */
            *isCa = innerIsCA ? 1 : 0;
            *pathLengthSet = (dataASN[BASICCONSASN_IDX_PLEN].length > 0);
        }
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode basic constraints extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.9 - BasicConstraints.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when CA boolean is present and false (default is false).
 * @return  ASN_PARSE_E when CA boolean is not present unless
 *          WOLFSSL_X509_BASICCONS_INT is defined. Only a CA extension.
 * @return  ASN_PARSE_E when path length more than 7 bits.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 */
static int DecodeBasicCaConstraintInternal(const byte* input, int sz,
                                           DecodedCert* cert)
{
    int ret;
    byte isCa = 0;
    word16 pathLength = 0;
    byte pathLengthSet = 0;
    ret = DecodeBasicCaConstraint(input, sz, &isCa, &pathLength,
                                  &pathLengthSet);
    if (ret != 0)
        return ret;

    cert->isCA = isCa ? 1 : 0;
    cert->pathLengthSet = pathLengthSet ? 1 : 0;
    if (pathLengthSet) {
        cert->pathLength = pathLength;
    }

    return 0;
}


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

    WOLFSSL_ENTER("DecodePolicyConstraints");

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

    if (length == 0)
        return ASN_PARSE_E;

    if (GetASNTag(input, &idx, &tag, (word32)sz) < 0) {
        WOLFSSL_MSG("\tfail: bad TAG");
        return ASN_PARSE_E;
    }

    if (tag == (ASN_CONTEXT_SPECIFIC | 0)) {
        /* requireExplicitPolicy */
        cert->extPolicyConstRxpSet = 1;
    }
    else if (tag == (ASN_CONTEXT_SPECIFIC | 1)) {
        /* inhibitPolicyMapping */
        cert->extPolicyConstIpmSet = 1;
    }
    else {
        WOLFSSL_MSG("\tfail: invalid TAG");
        return ASN_PARSE_E;
    }

    ret = GetLength(input, &idx, &skipLength, (word32)sz);
    if (ret < 0) {
        WOLFSSL_MSG("\tfail: invalid length");
        return ret;
    }
    if (skipLength > 1) {
        WOLFSSL_MSG("\tfail: skip value too big");
        return BUFFER_E;
    }
    if (idx >= (word32)sz) {
        WOLFSSL_MSG("\tfail: no policy const skip to read");
        return BUFFER_E;
    }
    cert->policyConstSkip = input[idx];

    return 0;
}


/* Context-Specific value for: DistributionPoint.distributionPoint
 * From RFC5280 SS4.2.1.13, Distribution Point */
#define DISTRIBUTION_POINT  (ASN_CONTEXT_SPECIFIC | 0)
/* Context-Specific value for: DistributionPoint.DistributionPointName.fullName
 *  From RFC3280 SS4.2.1.13, Distribution Point Name */
#define CRLDP_FULL_NAME     (ASN_CONTEXT_SPECIFIC | 0)
/* Context-Specific value for choice: GeneralName.uniformResourceIdentifier
 * From RFC3280 SS4.2.1.7, GeneralName */
#define GENERALNAME_URI     (ASN_CONTEXT_SPECIFIC | 6)

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for CRL distribution points.
 * X.509: RFC 5280, 4.2.1.13 - CRL Distribution Points.
 */
static const ASNItem crlDistASN[] = {
/* SEQ                */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* DP_SEQ             */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                /* Distribution point name */
/* DP_DISTPOINT       */         { 2, DISTRIBUTION_POINT, 1, 1, 1 },
                                                    /* fullName */
/* DP_DISTPOINT_FN    */             { 3, CRLDP_FULL_NAME, 1, 1, 2 },
/* DP_DISTPOINT_FN_GN */                 { 4, GENERALNAME_URI, 0, 0, 0 },
                                                    /* nameRelativeToCRLIssuer */
/* DP_DISTPOINT_RN    */             { 3, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 2 },
                                                /* reasons: IMPLICIT BIT STRING */
/* DP_REASONS         */         { 2, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 },
                                                /* cRLIssuer */
/* DP_CRLISSUER       */         { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 0, 1 },
};
enum {
    CRLDISTASN_IDX_SEQ = 0,
    CRLDISTASN_IDX_DP_SEQ,
    CRLDISTASN_IDX_DP_DISTPOINT,
    CRLDISTASN_IDX_DP_DISTPOINT_FN,
    CRLDISTASN_IDX_DP_DISTPOINT_FN_GN,
    CRLDISTASN_IDX_DP_DISTPOINT_RN, /* Relative name */
    CRLDISTASN_IDX_DP_REASONS,
    CRLDISTASN_IDX_DP_CRLISSUER
};

/* Number of items in ASN.1 template for CRL distribution points. */
#define crlDistASN_Length (sizeof(crlDistASN) / sizeof(ASNItem))
#endif

/* Decode CRL distribution point extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.13 - CRL Distribution Points.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when invalid bits of reason are set.
 * @return  ASN_PARSE_E when BITSTRING value is more than 2 bytes.
 * @return  ASN_PARSE_E when unused bits of BITSTRING is invalid.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeCrlDist(const byte* input, word32 sz, DecodedCert* cert)
{
    DECL_ASNGETDATA(dataASN, crlDistASN_Length);
    word32 idx = 0;
    int ret = 0;
#ifdef CRLDP_VALIDATE_DATA
    word16 reason;
#endif

    WOLFSSL_ENTER("DecodeCrlDist");

    CALLOC_ASNGETDATA(dataASN, crlDistASN_Length, ret, cert->heap);

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

    if  (ret == 0) {
        /* Get the GeneralName choice */
        GetASN_Choice(&dataASN[CRLDISTASN_IDX_DP_DISTPOINT_FN_GN], generalNameChoice);
        /* Parse CRL distribution point. */
        ret = GetASN_Items(crlDistASN, dataASN, crlDistASN_Length, 0, input,
                           &idx, sz);
    }
    if (ret == 0) {
        /* If the choice was a URI, store it in certificate. */
        if (dataASN[CRLDISTASN_IDX_DP_DISTPOINT_FN_GN].tag == GENERALNAME_URI) {
            word32 sz32;
            GetASN_GetConstRef(&dataASN[CRLDISTASN_IDX_DP_DISTPOINT_FN_GN],
                    &cert->extCrlInfo, &sz32);
            cert->extCrlInfoSz = (int)sz32;
        }

    #ifdef CRLDP_VALIDATE_DATA
        if (dataASN[CRLDISTASN_IDX_DP_REASONS].data.ref.data != NULL) {
             /* TODO: test case */
             /* Validate ReasonFlags. */
             ret = GetASN_BitString_Int16Bit(&dataASN[CRLDISTASN_IDX_DP_REASONS],
                     &reason);
             /* First bit (LSB) unused and eight other bits defined. */
             if ((ret == 0) && ((reason >> 9) || (reason & 0x01))) {
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                ret = ASN_PARSE_E;
             }
        }
    #endif
    }

    /* Only parsing the first one. */
    if (ret == 0 && idx < (word32)sz) {
        WOLFSSL_MSG("\tThere are more CRL Distribution Point records, "
                    "but we only use the first one.");
    }
    /* TODO: validate other points. */

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for the access description.
 * X.509: RFC 5280, 4.2.2.1 - Authority Information Access.
 */
static const ASNItem accessDescASN[] = {
/* SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                 /* accessMethod */
/* METH */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                                 /* accessLocation: GeneralName */
/* LOC  */     { 1, ASN_CONTEXT_SPECIFIC | 0, 0, 0, 0 },
};
enum {
    ACCESSDESCASN_IDX_SEQ = 0,
    ACCESSDESCASN_IDX_METH,
    ACCESSDESCASN_IDX_LOC
};

/* Number of items in ASN.1 template for the access description. */
#define accessDescASN_Length (sizeof(accessDescASN) / sizeof(ASNItem))
#endif

/* Decode authority information access extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.2.1 - Authority Information Access.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeAuthInfo(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;
    int ret    = 0;
    int aiaIdx;

    WOLFSSL_ENTER("DecodeAuthInfo");

    /* Unwrap the list of AIAs */
    if (GetASN_Sequence(input, &idx, &length, sz, 1) < 0) {
        ret = ASN_PARSE_E;
    }

    while ((ret == 0) && (idx < (word32)sz)) {
        ASNGetData dataASN[accessDescASN_Length];

        /* Clear dynamic data and retrieve OID and name. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        GetASN_OID(&dataASN[ACCESSDESCASN_IDX_METH], oidCertAuthInfoType);
        GetASN_Choice(&dataASN[ACCESSDESCASN_IDX_LOC], generalNameChoice);
        /* Parse AccessDescription. */
        ret = GetASN_Items(accessDescASN, dataASN, accessDescASN_Length, 0,
                           input, &idx, sz);
        if (ret == 0) {
            word32 sz32;

            if (dataASN[ACCESSDESCASN_IDX_LOC].tag == GENERALNAME_URI) {
                const byte* uri = NULL;

                GetASN_GetConstRef(&dataASN[ACCESSDESCASN_IDX_LOC], &uri, &sz32);

                /* Add to AIA list if space. */
                aiaIdx = cert->extAuthInfoListSz;
                if (aiaIdx < WOLFSSL_MAX_AIA_ENTRIES) {
                    cert->extAuthInfoList[aiaIdx].method =
                        dataASN[ACCESSDESCASN_IDX_METH].data.oid.sum;
                    cert->extAuthInfoList[aiaIdx].uri = uri;
                    cert->extAuthInfoList[aiaIdx].uriSz = sz32;
                    cert->extAuthInfoListSz++;
                }
                else {
                    cert->extAuthInfoListOverflow = 1;
                    WOLFSSL_MSG("AIA list overflow");
                }

                /* Set first OCSP entry. */
                if ((dataASN[ACCESSDESCASN_IDX_METH].data.oid.sum ==
                            AIA_OCSP_OID) && (cert->extAuthInfo == NULL)) {
                    cert->extAuthInfo = uri;
                    cert->extAuthInfoSz = (int)sz32;
                }
            #ifdef WOLFSSL_ASN_CA_ISSUER
                /* Set first CA Issuer entry. */
                else if ((dataASN[ACCESSDESCASN_IDX_METH].data.oid.sum ==
                            AIA_CA_ISSUER_OID) &&
                        (cert->extAuthInfoCaIssuer == NULL)) {
                    cert->extAuthInfoCaIssuer = uri;
                    cert->extAuthInfoCaIssuerSz = (int)sz32;
                }
            #endif
            }
            /* Otherwise skip. */
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for AuthorityKeyIdentifier.
 * X.509: RFC 5280, 4.2.1.1 - Authority Key Identifier.
 */
static const ASNItem authKeyIdASN[] = {
/* SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                     /* keyIdentifier */
/* KEYID  */        { 1, ASN_CONTEXT_SPECIFIC | ASN_AUTHKEYID_KEYID, 0, 0, 1 },
                                     /* authorityCertIssuer */
/* ISSUER */        { 1, ASN_CONTEXT_SPECIFIC | ASN_AUTHKEYID_ISSUER, 1, 0, 1 },
                                     /* authorityCertSerialNumber */
/* SERIAL */        { 1, ASN_CONTEXT_SPECIFIC | ASN_AUTHKEYID_SERIAL, 0, 0, 1 },
};
enum {
    AUTHKEYIDASN_IDX_SEQ = 0,
    AUTHKEYIDASN_IDX_KEYID,
    AUTHKEYIDASN_IDX_ISSUER,
    AUTHKEYIDASN_IDX_SERIAL
};

/* Number of items in ASN.1 template for AuthorityKeyIdentifier. */
#define authKeyIdASN_Length (sizeof(authKeyIdASN) / sizeof(ASNItem))
#endif

/* Decode authority key identifier extension.
 *
 * X.509: RFC 5280, 4.2.1.1 - Authority Key Identifier.
 *
 * @param [in]   input                   Buffer holding data.
 * @param [in]   sz                      Size of data in buffer.
 * @param [out]  extAuthKeyId            Beginning of the ID. NULL if not
 *                                       present.
 * @param [out]  extAuthKeyIdSz          Size of data in extAuthKeyId. 0 if not
 *                                       present.
 * @param [out]  extAuthKeyIdIssuer      Beginning of the Issuer. NULL if not
 *                                       present.
 * @param [out]  extAuthKeyIdIssuerSz    Size of data in extAuthKeyIdIssuer. 0
 *                                       if not present.
 * @param [out]  extAuthKeyIdIssuerSN    Beginning of the Issuer Serial. NULL
 *                                       if not present.
 * @param [out]  extAuthKeyIdIssuerSNSz  Size of data in extAuthKeyIdIssuerSN.
 *                                       0 if not present.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeAuthKeyId(const byte* input, word32 sz, const byte **extAuthKeyId,
        word32 *extAuthKeyIdSz, const byte **extAuthKeyIdIssuer,
        word32 *extAuthKeyIdIssuerSz, const byte **extAuthKeyIdIssuerSN,
        word32 *extAuthKeyIdIssuerSNSz)
{
    DECL_ASNGETDATA(dataASN, authKeyIdASN_Length);
    int ret = 0;

    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;

    CALLOC_ASNGETDATA(dataASN, authKeyIdASN_Length, ret, NULL);

    if (ret == 0) {
        /* Parse an authority key identifier. */
        word32 idx = 0;
        ret = GetASN_Items(authKeyIdASN, dataASN, authKeyIdASN_Length, 1, input,
                           &idx, sz);
    }
    /* Each field is optional */
    if (ret == 0 && extAuthKeyId && extAuthKeyIdSz &&
            dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.data != NULL) {
        GetASN_GetConstRef(&dataASN[AUTHKEYIDASN_IDX_KEYID],
                extAuthKeyId, extAuthKeyIdSz);
    }
#ifdef WOLFSSL_AKID_NAME
    if (ret == 0 && dataASN[AUTHKEYIDASN_IDX_ISSUER].data.ref.data != NULL) {
        /* We only support using one (first) name. Parse the name to perform
         * a sanity check. */
        word32 idx = 0;
        ASNGetData nameASN[altNameASN_Length];
        XMEMSET(nameASN, 0, sizeof(nameASN));
        /* Parse GeneralName with the choices supported. */
        GetASN_Choice(&nameASN[ALTNAMEASN_IDX_GN], generalNameChoice);
        /* Decode a GeneralName choice. */
        ret = GetASN_Items(altNameASN, nameASN, altNameASN_Length, 0,
                dataASN[AUTHKEYIDASN_IDX_ISSUER].data.ref.data, &idx,
                dataASN[AUTHKEYIDASN_IDX_ISSUER].data.ref.length);

        if (ret == 0 && extAuthKeyIdIssuer && extAuthKeyIdIssuerSz) {
            GetASN_GetConstRef(&nameASN[ALTNAMEASN_IDX_GN],
                    extAuthKeyIdIssuer, extAuthKeyIdIssuerSz);
        }
    }
    if (ret == 0 && extAuthKeyIdIssuerSN && extAuthKeyIdIssuerSNSz &&
            dataASN[AUTHKEYIDASN_IDX_SERIAL].data.ref.data != NULL) {
        GetASN_GetConstRef(&dataASN[AUTHKEYIDASN_IDX_SERIAL],
                extAuthKeyIdIssuerSN, extAuthKeyIdIssuerSNSz);
    }
    if (ret == 0 && extAuthKeyIdIssuerSz && extAuthKeyIdIssuerSNSz) {
        if ((*extAuthKeyIdIssuerSz > 0) ^
                (*extAuthKeyIdIssuerSNSz > 0)) {
            WOLFSSL_MSG("authorityCertIssuer and authorityCertSerialNumber MUST"
                       " both be present or both be absent");
        }
    }
#endif /* WOLFSSL_AKID_NAME */

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode authority key identifier extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.1 - Authority Key Identifier.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 */
static int DecodeAuthKeyIdInternal(const byte* input, word32 sz,
                                   DecodedCert* cert)
{
    int ret;
    const byte *extAuthKeyId = NULL;
    word32 extAuthKeyIdSz = 0;
    const byte *extAuthKeyIdIssuer = NULL;
    word32 extAuthKeyIdIssuerSz = 0;
    const byte *extAuthKeyIdIssuerSN = NULL;
    word32 extAuthKeyIdIssuerSNSz = 0;

    ret = DecodeAuthKeyId(input, sz, &extAuthKeyId, &extAuthKeyIdSz,
            &extAuthKeyIdIssuer, &extAuthKeyIdIssuerSz, &extAuthKeyIdIssuerSN,
            &extAuthKeyIdIssuerSNSz);
    if (ret != 0) {
        cert->extAuthKeyIdSet = 0;
        return ret;
    }

    /* Each field is optional */
    if (extAuthKeyIdSz > 0) {
        cert->extAuthKeyIdSet = 1;
        cert->extAuthKeyIdSz = extAuthKeyIdSz;

#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
        cert->extAuthKeyIdSrc = extAuthKeyId;
#endif

        /* Get the hash or hash of the hash if wrong size. */
        ret = GetHashId(extAuthKeyId, (int)extAuthKeyIdSz, cert->extAuthKeyId,
                        HashIdAlg(cert->signatureOID));
    }
    else {
        cert->extAuthKeyIdSet = 0;
    }

#ifdef WOLFSSL_AKID_NAME
    if (ret == 0 && extAuthKeyIdIssuerSz > 0) {
        cert->extAuthKeyIdIssuer = extAuthKeyIdIssuer;
        cert->extAuthKeyIdIssuerSz = extAuthKeyIdIssuerSz;
    }
    if (ret == 0 && extAuthKeyIdIssuerSNSz > 0) {
        cert->extAuthKeyIdIssuerSN = extAuthKeyIdIssuerSN;
        cert->extAuthKeyIdIssuerSNSz = extAuthKeyIdIssuerSNSz;
    }
#endif /* WOLFSSL_AKID_NAME */
    if (ret == 0) {
#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
     defined(WOLFSSL_AKID_NAME)
        /* Store the raw authority key id. */
        cert->extRawAuthKeyIdSrc = input;
        cert->extRawAuthKeyIdSz = sz;
#endif
    }

    return ret;
}

/* Decode subject key id extension.
 *
 * X.509: RFC 5280, 4.2.1.2 - Subject Key Identifier.
 *
 * @param [in]   input          Buffer holding data.
 * @param [in]   sz             Size of data in buffer.
 * @param [out]  extSubjKeyId   Beginning of the ID.
 * @param [out]  extSubjKeyIdSz Size of data in extSubjKeyId.
 * @return  0 on success.
 * @return  ASN_PARSE_E when the OCTET_STRING tag is not found or length is
 *          invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
int DecodeSubjKeyId(const byte* input, word32 sz, const byte **extSubjKeyId,
                    word32 *extSubjKeyIdSz)
{
    word32 idx = 0;
    int length = 0;
    int ret = 0;

    WOLFSSL_ENTER("DecodeSubjKeyId");

    ret = GetOctetString(input, &idx, &length, sz);
    if (ret < 0)
        return ret;

    *extSubjKeyIdSz = (word32)length;
    *extSubjKeyId = &input[idx];
    return 0;
}

/* Decode subject key id extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.2 - Subject Key Identifier.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when the OCTET_STRING tag is not found or length is
 *          invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int DecodeSubjKeyIdInternal(const byte* input, word32 sz,
                                   DecodedCert* cert)
{
    int ret = 0;
    const byte *extSubjKeyId = NULL;
    word32 extSubjKeyIdSz = 0;

    ret = DecodeSubjKeyId(input, sz, &extSubjKeyId, &extSubjKeyIdSz);
    if (ret != 0)
        return ret;

    cert->extSubjKeyIdSz = extSubjKeyIdSz;

#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    cert->extSubjKeyIdSrc = extSubjKeyId;
#endif /* OPENSSL_EXTRA */

    /* Get the hash or hash of the hash if wrong size. */
    ret = GetHashId(extSubjKeyId, (int)extSubjKeyIdSz, cert->extSubjKeyId,
        HashIdAlg(cert->signatureOID));

    return ret;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for KeyUsage.
 * X.509: RFC 5280, 4.2.1.3 - Key Usage.
 */
static const ASNItem keyUsageASN[] = {
/* STR */ { 0, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    KEYUSAGEASN_IDX_STR = 0
};

/* Number of items in ASN.1 template for KeyUsage. */
#define keyUsageASN_Length (sizeof(keyUsageASN) / sizeof(ASNItem))
#endif

/* Decode key usage extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.3 - Key Usage.
 *
 * @param [in]      input        Buffer holding data.
 * @param [in]      sz           Size of data in buffer.
 * @param [out]     extKeyUsage  Key usage bitfield.
 * @return  0 on success.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeKeyUsage(const byte* input, word32 sz, word16 *extKeyUsage)
{
    ASNGetData dataASN[keyUsageASN_Length];
    word32 idx = 0;
    byte keyUsage[2];
    word32 keyUsageSz = sizeof(keyUsage);
    int ret;
    WOLFSSL_ENTER("DecodeKeyUsage");

    /* Clear dynamic data and set where to store extended key usage. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    XMEMSET(keyUsage, 0, sizeof(keyUsage));
    GetASN_Buffer(&dataASN[KEYUSAGEASN_IDX_STR], keyUsage, &keyUsageSz);
    /* Parse key usage. */
    ret = GetASN_Items(keyUsageASN, dataASN, keyUsageASN_Length, 0, input,
                        &idx, sz);
    if (ret == 0) {
        /* Decode the bit string number as LE */
        *extKeyUsage = (word16)(keyUsage[0]);
        if (keyUsageSz == 2)
            *extKeyUsage |= (word16)(keyUsage[1] << 8);
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode key usage extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.3 - Key Usage.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int DecodeKeyUsageInternal(const byte* input, word32 sz,
                                  DecodedCert* cert)
{
    return DecodeKeyUsage(input, sz, &cert->extKeyUsage);
}

#ifdef WOLFSSL_ACME_OID
/* Decodes the RFC 8737 id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31)
 * extension value into cert->acmeIdentifier.
 *
 * The extnValue is an OCTET STRING wrapping a SHA-256 digest of the
 * ACME keyAuth, per RFC 8737 3. Length is verified against
 * WC_SHA256_DIGEST_SIZE.
 *
 * @param [in]      input  ASN.1 DER-encoded extension value.
 * @param [in]      sz     Length of input in bytes.
 * @param [in, out] cert   DecodedCert to populate (acmeIdentifier and
 *                         acmeIdentifierSz fields).
 *
 * @return  0 on success.
 * @return  ASN_PARSE_E when the inner OCTET STRING is missing or not
 *          exactly WC_SHA256_DIGEST_SIZE bytes.
 */
static int DecodeAcmeId(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 hashIdx = 0;
    int    hashLen = 0;

    if (GetOctetString(input, &hashIdx, &hashLen, sz) < 0)
        return ASN_PARSE_E;
    if (hashLen != WC_SHA256_DIGEST_SIZE)
        return ASN_PARSE_E;

    XMEMCPY(cert->acmeIdentifier, &input[hashIdx], WC_SHA256_DIGEST_SIZE);
    cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE;
    return 0;
}
#endif /* WOLFSSL_ACME_OID */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for KeyPurposeId.
 * X.509: RFC 5280, 4.2.1.12 - Extended Key Usage.
 */
static const ASNItem keyPurposeIdASN[] = {
/* OID */ { 0, ASN_OBJECT_ID, 0, 0, 0 },
};
enum {
    KEYPURPOSEIDASN_IDX_OID = 0
};

/* Number of items in ASN.1 template for KeyPurposeId. */
#define keyPurposeIdASN_Length (sizeof(keyPurposeIdASN) / sizeof(ASNItem))
#endif

/* Decode extended key usage extension.
 *
 * X.509: RFC 5280, 4.2.1.12 - Extended Key Usage.
 *
 * @param [in]  input  Buffer holding data.
 * @param [in]  sz     Size of data in buffer.
 * @param [out] extExtKeyUsageSrc   Beginning of the OIDs.
 * @param [out] extExtKeyUsageSz    Size of data in extExtKeyUsageSrc.
 * @param [out] extExtKeyUsageCount Number of usages read.
 * @param [out] extExtKeyUsage      Usages read.
 * @param [out] extExtKeyUsageSsh   SSH usages read.
 * @return  0 on success.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeExtKeyUsage(const byte* input, word32 sz,
        const byte **extExtKeyUsageSrc, word32 *extExtKeyUsageSz,
        word32 *extExtKeyUsageCount, byte *extExtKeyUsage,
        byte *extExtKeyUsageSsh)
{
    word32 idx = 0;
    int length;
    int ret = 0;

    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

    /* Strip SEQUENCE OF and expect to account for all the data. */
    if (GetASN_Sequence(input, &idx, &length, sz, 1) < 0) {
        WOLFSSL_MSG("\tfail: should be a SEQUENCE");
        ret = ASN_PARSE_E;
    }

    if (ret == 0) {
    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
        /* Keep reference for WOLFSSL_X509. */
        *extExtKeyUsageSrc = input + idx;
        *extExtKeyUsageSz = (word32)length;
    #endif
    }

    /* Check all OIDs. */
    while ((ret == 0) && (idx < (word32)sz)) {
        ASNGetData dataASN[keyPurposeIdASN_Length];

        /* Clear dynamic data items and set OID type expected. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        GetASN_OID(&dataASN[KEYPURPOSEIDASN_IDX_OID], oidIgnoreType);
        /* Decode KeyPurposeId. */
        ret = GetASN_Items(keyPurposeIdASN, dataASN, keyPurposeIdASN_Length, 0,
                           input, &idx, sz);
        /* Skip unknown OIDs. */
        if (ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E)) {
            ret = 0;
        }
        else if (ret == 0) {
            /* Store the bit for the OID. */
            switch (dataASN[KEYPURPOSEIDASN_IDX_OID].data.oid.sum) {
                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;
            }

        #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
            /* Keep count for WOLFSSL_X509. */
            (*extExtKeyUsageCount)++;
        #endif
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Decode extended key usage extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.12 - Extended Key Usage.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int DecodeExtKeyUsageInternal(const byte* input, word32 sz,
                                     DecodedCert* cert)
{
    int ret = 0;


    ret = DecodeExtKeyUsage(input, sz,
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
            &cert->extExtKeyUsageSrc, &cert->extExtKeyUsageSz,
            &cert->extExtKeyUsageCount,
#else
            NULL, NULL, NULL,
#endif
            &cert->extExtKeyUsage,
#ifdef WOLFSSL_WOLFSSH
            &cert->extExtKeyUsageSsh
#else
            NULL
#endif
            );

    if (ret != 0)
        return ret;

    return 0;
}

#ifndef IGNORE_NETSCAPE_CERT_TYPE

static int DecodeNsCertType(const byte* input, int sz, DecodedCert* cert)
{
    word32 idx = 0;
    int len = 0;

    WOLFSSL_ENTER("DecodeNsCertType");

    if (CheckBitString(input, &idx, &len, (word32)sz, 0, NULL) < 0)
        return ASN_PARSE_E;

    /* Don't need to worry about unused bits as CheckBitString makes sure
     * they're zero. */
    if (idx < (word32)sz)
        cert->nsCertType = input[idx];
    else
        return ASN_PARSE_E;

    return 0;
}
#endif


#ifndef IGNORE_NAME_CONSTRAINTS
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for GeneralSubtree.
 * X.509: RFC 5280, 4.2.1.10 - Name Constraints.
 */
static const ASNItem subTreeASN[] = {
/* SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                              /* base     GeneralName */
/* BASE */     { 1, ASN_CONTEXT_SPECIFIC | 0, 0, 0, 0 },
                              /* minimum  BaseDistance DEFAULT 0*/
/* MIN  */     { 1, ASN_CONTEXT_SPECIFIC | ASN_SUBTREE_MIN, 0, 0, 1 },
                              /* maximum  BaseDistance OPTIONAL  */
/* MAX  */     { 1, ASN_CONTEXT_SPECIFIC | ASN_SUBTREE_MAX, 0, 0, 1 },
};
enum {
    SUBTREEASN_IDX_SEQ = 0,
    SUBTREEASN_IDX_BASE,
    SUBTREEASN_IDX_MIN,
    SUBTREEASN_IDX_MAX
};

/* Number of items in ASN.1 template for GeneralSubtree. */
#define subTreeASN_Length (sizeof(subTreeASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
/* Decode the Subtree's GeneralName.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in]      tag    BER tag on GeneralName.
 * @param [in, out] head   Linked list of subtree names.
 * @param [in]      heap   Dynamic memory hint.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when SEQUENCE is not found as expected.
 */
static int DecodeSubtreeGeneralName(const byte* input, word32 sz, byte tag,
                                    Base_entry** head, void* heap)
{
    Base_entry* entry = NULL;
    word32 nameIdx = 0;
    word32 len = sz;
    int strLen;
    int ret = 0;

    (void)heap;

    /* 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 }
     * where the inner content is NOT a SEQUENCE; keep the bytes as-is so
     * we can byte-match a leaf SAN otherName against the constraint.
     */
    if ((tag & ASN_CONSTRUCTED) == ASN_CONSTRUCTED &&
            (tag & ASN_TYPE_MASK) != ASN_OTHER_TYPE) {
        ret = GetASN_Sequence(input, &nameIdx, &strLen, sz, 0);
        if (ret < 0) {
            ret = ASN_PARSE_E;
        }
        else {
            len = (word32)strLen;
            ret = 0;
        }
    }
    if (ret == 0) {
        /* TODO: consider one malloc. */
        /* Allocate Base Entry object. */
        entry = (Base_entry*)XMALLOC(sizeof(Base_entry), heap,
                                     DYNAMIC_TYPE_ALTNAME);
        if (entry == NULL) {
            ret = MEMORY_E;
        }
    }
    if (ret == 0) {
        /* Allocate name. */
        entry->name = (char*)XMALLOC(len + 1, heap, DYNAMIC_TYPE_ALTNAME);
        if (entry->name == NULL) {
            XFREE(entry, heap, DYNAMIC_TYPE_ALTNAME);
            ret = MEMORY_E;
        }
    }
    if (ret == 0) {
        /* Store name, size and tag in object. */
        XMEMCPY(entry->name, &input[nameIdx], len);
        entry->name[len] = '\0';
        entry->nameSz = (int)len;
        entry->type = tag & ASN_TYPE_MASK;

        /* Put entry at front of linked list. */
        entry->next = *head;
        *head = entry;
    }

    return ret;
}
#endif

/* Decode a subtree of a name constraints in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.10 - Name Constraints.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] head   Linked list of subtree names.
 * @param [in]      limit  If > 0, limit on number of tree
 *                         entries to  process, exceeding
 *                         is an error.
 * @param [in]      heap   Dynamic memory hint.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  ASN_PARSE_E when SEQUENCE is not found as expected.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
/* Decode a sub-tree of name constraints.
 *
 * @param [out]     hasUnsupported  Set to 1 when an entry with a GeneralName
 *                                  form we cannot fully enforce was
 *                                  encountered. Drives the RFC 5280 4.2.1.10
 *                                  fail-closed requirement for critical
 *                                  nameConstraints extensions; must not be
 *                                  NULL.
 */
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
                         word32 limit, byte* hasUnsupported, void* heap)
{
    DECL_ASNGETDATA(dataASN, subTreeASN_Length);
    word32 idx = 0;
    int ret = 0;
    word32 cnt = 0;

    (void)heap;

    ALLOC_ASNGETDATA(dataASN, subTreeASN_Length, ret, heap);

    /* Process all subtrees. */
    while ((ret == 0) && (idx < (word32)sz)) {
        byte minVal = 0;
        byte maxVal = 0;
        if (limit > 0) {
            cnt++;
            if (cnt > limit) {
                WOLFSSL_MSG("too many name constraints");
                ret = ASN_NAME_INVALID_E;
                break;
            }
        }

        /* Clear dynamic data and set choice for GeneralName and location to
         * store minimum and maximum.
         */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * subTreeASN_Length);
        GetASN_Choice(&dataASN[SUBTREEASN_IDX_BASE], generalNameChoice);
        GetASN_Int8Bit(&dataASN[SUBTREEASN_IDX_MIN], &minVal);
        GetASN_Int8Bit(&dataASN[SUBTREEASN_IDX_MAX], &maxVal);
        /* Parse GeneralSubtree. */
        ret = GetASN_Items(subTreeASN, dataASN, subTreeASN_Length, 0, input,
                           &idx, sz);
        if (ret == 0) {
            byte t = dataASN[SUBTREEASN_IDX_BASE].tag;

            /* Check GeneralName tag is one of the types we can handle.
             * registeredID is included so that ConfirmNameConstraints can
             * enforce permitted/excluded subtrees of OIDs (RFC 5280
             * Sec. 4.2.1.10). */
            if (t == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED |
                      ASN_OTHER_TYPE) ||
                t == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) {
                /* Parse the general name and store a new entry. */
                ret = DecodeSubtreeGeneralName(input +
                    GetASNItem_DataIdx(dataASN[SUBTREEASN_IDX_BASE], input),
                    dataASN[SUBTREEASN_IDX_BASE].length, t, head, heap);
            }
            else {
                /* GeneralName form (e.g. 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).
                 * registeredID is handled above; this branch covers truly
                 * unrecognised forms. */
                *hasUnsupported = 1;
            }
        }
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for NameConstraints.
 * X.509: RFC 5280, 4.2.1.10 - Name Constraints.
 */
static const ASNItem nameConstraintsASN[] = {
/* SEQ     */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* permittedSubtrees */
/* PERMIT  */     { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 1 },
                                         /* excludededSubtrees */
/* EXCLUDE */     { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 1 },
};
enum {
    NAMECONSTRAINTSASN_IDX_SEQ = 0,
    NAMECONSTRAINTSASN_IDX_PERMIT,
    NAMECONSTRAINTSASN_IDX_EXCLUDE
};

/* Number of items in ASN.1 template for NameConstraints. */
#define nameConstraintsASN_Length (sizeof(nameConstraintsASN) / sizeof(ASNItem))
#endif

/* Decode name constraints extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.10 - Name Constraints.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeNameConstraints(const byte* input, word32 sz,
    DecodedCert* cert)
{
    DECL_ASNGETDATA(dataASN, nameConstraintsASN_Length);
    word32 idx = 0;
    int    ret = 0;
    byte   hasUnsupported = 0;

    CALLOC_ASNGETDATA(dataASN, nameConstraintsASN_Length, ret, cert->heap);

    if (ret == 0) {
        /* Parse NameConstraints. */
        ret = GetASN_Items(nameConstraintsASN, dataASN,
                           nameConstraintsASN_Length, 1, input, &idx, sz);
    }
    if (ret == 0) {
        /* If there was a permittedSubtrees then parse it. */
        if (dataASN[NAMECONSTRAINTSASN_IDX_PERMIT].data.ref.data != NULL) {
            ret = DecodeSubtree(
                    dataASN[NAMECONSTRAINTSASN_IDX_PERMIT].data.ref.data,
                    dataASN[NAMECONSTRAINTSASN_IDX_PERMIT].data.ref.length,
                    &cert->permittedNames, WOLFSSL_MAX_NAME_CONSTRAINTS,
                    &hasUnsupported, cert->heap);
        }
    }
    if (ret == 0) {
        /* If there was a excludedSubtrees then parse it. */
        if (dataASN[NAMECONSTRAINTSASN_IDX_EXCLUDE].data.ref.data != NULL) {
            ret = DecodeSubtree(
                    dataASN[NAMECONSTRAINTSASN_IDX_EXCLUDE].data.ref.data,
                    dataASN[NAMECONSTRAINTSASN_IDX_EXCLUDE].data.ref.length,
                    &cert->excludedNames, WOLFSSL_MAX_NAME_CONSTRAINTS,
                    &hasUnsupported, cert->heap);
        }
    }

    if (ret == 0 && hasUnsupported) {
        cert->extNameConstraintHasUnsupported = 1;
    }

    FREE_ASNGETDATA(dataASN, cert->heap);

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* IGNORE_NAME_CONSTRAINTS */

#if defined(WOLFSSL_CERT_EXT) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)

/* Decode ITU-T X.690 OID format to a string representation
 * return string length */
int DecodePolicyOID(char *out, word32 outSz, const byte *in, word32 inSz)
{
    word32 val, inIdx = 0, outIdx = 0;
    int w = 0;
    int cnt = 0;

    if (out == NULL || in == NULL || outSz < 4 || inSz < 2)
        return BAD_FUNC_ARG;
    if (inSz >= ASN_LONG_LENGTH)
        return BAD_FUNC_ARG;

    /* The first byte expands into b/40 dot b%40. */
    val = in[inIdx++];

    w = XSNPRINTF(out, outSz, "%u.%u", val / 40, val % 40);
    if (w < 0) {
        w = BUFFER_E;
        goto exit;
    }
    outIdx += (word32)w;
    val = 0;

    while ((inIdx < inSz) && (outIdx < outSz)) {
        /* extract the next OID digit from in to val */
        /* first bit is used to set if value is coded on 1 or multiple bytes */
        if (in[inIdx] & 0x80) {
            if (cnt == 4) {
                w = ASN_OBJECT_ID_E;
                goto exit;
            }
            val += in[inIdx] & 0x7F;
            val <<= 7;
            cnt++;
        }
        else {
            /* write val as text into out */
            val += in[inIdx];
            w = XSNPRINTF(out + outIdx, outSz - outIdx, ".%u", val);
            if (w < 0 || (word32)w > outSz - outIdx) {
                w = BUFFER_E;
                goto exit;
            }
            outIdx += (word32)w;
            val = 0;
            cnt = 0;
        }
        inIdx++;
    }
    if (outIdx == outSz)
        outIdx--;
    out[outIdx] = 0;

    w = (int)outIdx;

exit:
    return w;
}
#endif /* WOLFSSL_CERT_EXT || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */

#if defined(WOLFSSL_SEP) || defined(WOLFSSL_CERT_EXT)
#ifdef WOLFSSL_ASN_TEMPLATE
    /* ASN.1 template for PolicyInformation.
     * X.509: RFC 5280, 4.2.1.4 - Certificate Policies.
     */
    static const ASNItem policyInfoASN[] = {
    /* SEQ   */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                      /* policyIdentifier */
    /* ID    */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                                      /* policyQualifiers */
    /* QUALI */     { 1, ASN_SEQUENCE, 1, 0, 1 },
    };
    enum {
        POLICYINFOASN_IDX_SEQ = 0,
        POLICYINFOASN_IDX_ID,
        POLICYINFOASN_IDX_QUALI
    };

    /* Number of items in ASN.1 template for PolicyInformation. */
    #define policyInfoASN_Length (sizeof(policyInfoASN) / sizeof(ASNItem))
#endif

/* Reference: https://tools.ietf.org/html/rfc5280#section-4.2.1.4 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeCertPolicy(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int ret = 0;
    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) {
        ret = BAD_FUNC_ARG;
    }

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

        /* Strip SEQUENCE OF and check using all data. */
        if (GetASN_Sequence(input, &idx, &total_length, (word32)sz, 1) < 0)
        {
            ret = ASN_PARSE_E;
        }
    }

    /* Unwrap certificatePolicies */
    while ((ret == 0) && ((int)idx < total_length)
    #if defined(WOLFSSL_CERT_EXT)
        && (cert->extCertPoliciesNb < MAX_CERTPOL_NB)
    #endif
            ) {
        ASNGetData dataASN[policyInfoASN_Length];
        const byte* data = NULL;
        word32 length = 0;

        /* Clear dynamic data and check OID is a cert policy type. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        GetASN_OID(&dataASN[POLICYINFOASN_IDX_ID], oidCertPolicyType);
        ret = GetASN_Items(policyInfoASN, dataASN, policyInfoASN_Length, 1,
                            input, &idx, (word32)sz);
        if (ret == 0) {
            /* Get the OID. */
            GetASN_OIDData(&dataASN[POLICYINFOASN_IDX_ID], &data, &length);
            if (length == 0) {
                ret = ASN_PARSE_E;
            }
        }
    #ifdef WOLFSSL_SEP
        /* Store OID in device type. */
        if (ret == 0 && cert->deviceType == NULL) {
            cert->deviceType = (byte*)XMALLOC(length, cert->heap,
                                                DYNAMIC_TYPE_X509_EXT);
            if (cert->deviceType != NULL) {
                /* Store device type data and length. */
                cert->deviceTypeSz = (int)length;
                XMEMCPY(cert->deviceType, data, length);
            }
            else {
                WOLFSSL_MSG("\tCouldn't alloc memory for deviceType");
                ret = MEMORY_E;
            }
        }
    #endif /* WOLFSSL_SEP */

    #ifdef WOLFSSL_CERT_EXT
        if (ret == 0) {
            /* Decode cert policy. */
            if (DecodePolicyOID(
                    cert->extCertPolicies[cert->extCertPoliciesNb],
                    MAX_CERTPOL_SZ, data, length) <= 0) {
                WOLFSSL_MSG("\tCouldn't decode CertPolicy");
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                ret = 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; (ret == 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);
                ret = CERTPOLICIES_E;
            }
        }
        #endif /* !WOLFSSL_DUP_CERTPOL */
        if (ret == 0) {
            /* Keep count of policies seen. */
            cert->extCertPoliciesNb++;
        }
    #endif /* WOLFSSL_CERT_EXT */
    }

    WOLFSSL_LEAVE("DecodeCertPolicy", 0);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_SEP || WOLFSSL_CERT_EXT  */

#ifdef WOLFSSL_SUBJ_DIR_ATTR
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for subject dir attribute.
 * X.509: RFC 5280, 4.2.1.8 - Subject Directory Attributes.
 */
static const ASNItem subjDirAttrASN[] = {
/* SEQ  */     { 1, ASN_SEQUENCE, 1, 1, 0 },
/* OID  */          { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* PLEN */          { 2, ASN_SET, 1, 0, 0 },
};
enum {
    SUBJDIRATTRASN_IDX_SEQ = 0,
    SUBJDIRATTRASN_IDX_OID,
    SUBJDIRATTRASN_IDX_SET
};

/* Number of items in ASN.1 template for BasicConstraints. */
#define subjDirAttrASN_Length (sizeof(subjDirAttrASN) / sizeof(ASNItem))
#endif
/* Decode subject directory attributes extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.1.8 - Subject Directory Attributes.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeSubjDirAttr(const byte* input, word32 sz, DecodedCert* cert)
{
    DECL_ASNGETDATA(dataASN, subjDirAttrASN_Length);
    int ret = 0;
    word32 idx = 0;
    int length;

    WOLFSSL_ENTER("DecodeSubjDirAttr");

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

    CALLOC_ASNGETDATA(dataASN, subjDirAttrASN_Length, ret, cert->heap);

    /* Strip outer SEQUENCE. */
    if ((ret == 0) && (GetSequence(input, &idx, &length, sz) < 0)) {
        ret = ASN_PARSE_E;
    }
    /* Handle each inner SEQUENCE. */
    while ((ret == 0) && (idx < (word32)sz)) {
        ret = GetASN_Items(subjDirAttrASN, dataASN, subjDirAttrASN_Length, 1,
            input, &idx, sz);

        /* There may be more than one countryOfCitizenship, but save the
         * first one for now. */
        if ((ret == 0) &&
                (dataASN[SUBJDIRATTRASN_IDX_OID].data.oid.sum == SDA_COC_OID)) {
            int cuLen;
            word32 setIdx = 0;
            const byte* setData;
            word32 setLen;

            GetASN_GetRef(&dataASN[SUBJDIRATTRASN_IDX_SET], &setData, &setLen);
            if (GetASNHeader(setData, ASN_PRINTABLE_STRING, &setIdx, &cuLen,
                    setLen) < 0) {
                ret = ASN_PARSE_E;
            }
            if ((ret == 0) && (cuLen != COUNTRY_CODE_LEN)) {
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                XMEMCPY(cert->countryOfCitizenship, setData + setIdx,
                    (size_t)cuLen);
                cert->countryOfCitizenship[COUNTRY_CODE_LEN] = 0;
            }
        }
    }
    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_SUBJ_DIR_ATTR */

#ifdef WOLFSSL_SUBJ_INFO_ACC
/* Decode subject information access extension in a certificate.
 *
 * X.509: RFC 5280, 4.2.2.2 - Subject Information Access.
 *
 * @param [in]      input  Buffer holding data.
 * @param [in]      sz     Size of data in buffer.
 * @param [in, out] cert   Certificate object.
 * @return  0 on success.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int DecodeSubjInfoAcc(const byte* input, word32 sz, DecodedCert* cert)
{
    word32 idx = 0;
    int length = 0;

    WOLFSSL_ENTER("DecodeSubjInfoAcc");

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

    if (length == 0) {
        /* RFC 5280 4.2.2.2.  Subject Information Access
           If the subjectInformationAccess extension is present, the
           sequence MUST contain at least one entry. */
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    /* RFC 5280 specifies that at least one entry must be present but does not
     * specify any particular OID must be present. For certificates following
     * fpki-x509-cert-profile-common, we extract the id-ad-caRepository caRepo
     * entry to cert->extSubjInfoAccCaRepo / cert->extSubjInfoAccCaRepoSz for
     * convenient user access. */

    while (idx < (word32)sz) {
        word32 oid = 0;
        byte b;

        /* Unwrap an AccessDescription */
        if (GetSequence(input, &idx, &length, sz) < 0)
            return ASN_PARSE_E;

        /* Get the accessMethod */
        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;

        /* Set caRepo entry */
        if (b == GENERALNAME_URI && oid == AIA_CA_REPO_OID) {
            cert->extSubjInfoAccCaRepoSz = (word32)length;
            cert->extSubjInfoAccCaRepo = input + idx;
            break;
        }
        idx += (word32)length;
    }

    WOLFSSL_LEAVE("DecodeSubjInfoAcc", 0);
    return 0;
}
#endif /* WOLFSSL_SUBJ_INFO_ACC */

#ifdef WOLFSSL_DUAL_ALG_CERTS
/* The subject alternative public key is an extension that holds the same thing
 * as a subject public key. */
static const ASNItem subjAltPubKeyInfoASN[] = {
                           /* subjectPublicKeyInfo SubjectPublicKeyInfo */
/* ALT_SPUBKEYINFO_SEQ          */      { 0, ASN_SEQUENCE, 1, 1, 0 },
                           /* algorithm          AlgorithmIdentifier */
                           /* AlgorithmIdentifier ::= SEQUENCE */
/* ALT_SPUBKEYINFO_ALGO_SEQ     */         { 1, ASN_SEQUENCE, 1, 1, 0 },
                          /* Algorithm    OBJECT IDENTIFIER */
/* ALT_SPUBKEYINFO_ALGO_OID     */             { 2, ASN_OBJECT_ID, 0, 0, 0 },
                           /* parameters   ANY defined by algorithm OPTIONAL */
/* ALT_SPUBKEYINFO_ALGO_NULL    */             { 2, ASN_TAG_NULL, 0, 0, 1 },
/* ALT_SPUBKEYINFO_ALGO_CURVEID */             { 2, ASN_OBJECT_ID, 0, 0, 1 },
#ifdef WC_RSA_PSS
/* ALT_SPUBKEYINFO_ALGO_P_SEQ   */             { 2, ASN_SEQUENCE, 1, 0, 1 },
#endif
                           /* subjectPublicKey   BIT STRING */
/* ALT_SPUBKEYINFO_PUBKEY       */          { 1, ASN_BIT_STRING, 0, 0, 0 }
};

#define subjAltPubKeyInfoASN_Length (sizeof(subjAltPubKeyInfoASN) / \
                                     sizeof(ASNItem))

enum {
    ALT_SPUBKEYINFO_SEQ = 0,
    ALT_SPUBKEYINFO_ALGO_SEQ,
    ALT_SPUBKEYINFO_ALGO_OID,
    ALT_SPUBKEYINFO_ALGO_NULL,
    ALT_SPUBKEYINFO_ALGO_CURVEID,
#ifdef WC_RSA_PSS
    ALT_SPUBKEYINFO_ALGO_P_SEQ,
#endif
    ALT_SPUBKEYINFO_PUBKEY
};

static int DecodeSubjAltPubKeyInfo(const byte* input, int sz, DecodedCert* cert)
{
    int ret = 0;
    word32 idx = 0;
    DECL_ASNGETDATA(dataASN, subjAltPubKeyInfoASN_Length);

    WOLFSSL_ENTER("DecodeSubjAltPubKeyInfo");

    if (ret == 0) {
        CALLOC_ASNGETDATA(dataASN, subjAltPubKeyInfoASN_Length, ret,
                          cert->heap);
        (void)cert;
    }

    if (ret == 0) {
        GetASN_OID(&dataASN[ALT_SPUBKEYINFO_ALGO_OID], oidKeyType);
        GetASN_OID(&dataASN[ALT_SPUBKEYINFO_ALGO_CURVEID], oidCurveType);

        ret = GetASN_Items(subjAltPubKeyInfoASN, dataASN,
                           subjAltPubKeyInfoASN_Length, 1, input, &idx,
                           (word32)sz);
    }

    if (ret == 0) {
        /* dataASN[ALT_SPUBKEYINFO_SEQ].data.u8 */
        cert->sapkiDer = (byte *)input;
        /* dataASN[ALT_SPUBKEYINFO_SEQ].length */
        cert->sapkiLen = sz;
        cert->sapkiOID = dataASN[ALT_SPUBKEYINFO_ALGO_OID].data.oid.sum;
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    WOLFSSL_LEAVE("DecodeSubjAltPubKeyInfo", ret);
    return ret;
}

/* The alternative signature algorithm extension holds the same thing as a
 * as a signature algorithm identifier. */
static const ASNItem altSigAlgASN[] = {
                          /* AltSigAlg            AlgorithmIdentifier */
                          /* AlgorithmIdentifier ::= SEQUENCE */
/* ALTSIG_ALGOID_SEQ                */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                          /* Algorithm    OBJECT IDENTIFIER */
/* ALTSIG_ALGOID_OID                */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                          /* parameters   ANY defined by algorithm OPTIONAL */
/* ALTSIG_ALGOID_PARAMS_NULL        */     { 1, ASN_TAG_NULL, 0, 0, 1 },
#ifdef WC_RSA_PSS
/* ALTSIG_ALGOID_PARAMS             */     { 1, ASN_SEQUENCE, 1, 0, 1 },
#endif
};

#define altSigAlgASN_Length (sizeof(altSigAlgASN) / sizeof(ASNItem))

enum {
    ALTSIG_ALGOID_SEQ = 0,
    ALTSIG_ALGOID_OID,
    ALTSIG_ALGOID_PARAMS_NULL,
#ifdef WC_RSA_PSS
    ALTSIG_ALGOID_PARAMS,
#endif
};

static int DecodeAltSigAlg(const byte* input, int sz, DecodedCert* cert)
{
    int ret = 0;
    word32 idx = 0;
    DECL_ASNGETDATA(dataASN, altSigAlgASN_Length);

    WOLFSSL_ENTER("DecodeAltSigAlg");

    if (ret == 0) {
        CALLOC_ASNGETDATA(dataASN, altSigAlgASN_Length, ret, cert->heap);
        (void)cert;
    }

    /* We do this to make sure the format of the extension is correct. */
    if (ret == 0) {
        GetASN_OID(&dataASN[ALTSIG_ALGOID_OID], oidSigType);

        ret = GetASN_Items(altSigAlgASN, dataASN,
                           altSigAlgASN_Length, 1, input, &idx,
                           (word32)sz);
    }

    if (ret == 0) {
        cert->altSigAlgDer = (byte *)input;
        cert->altSigAlgLen = sz;
        cert->altSigAlgOID = dataASN[ALTSIG_ALGOID_OID].data.oid.sum;
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    WOLFSSL_LEAVE("DecodeAltSigAlg", ret);
    return ret;
}

/* The alternative signature value extension holds an ASN.1 bitstring just
 * like a traditional signature in the certificate. */
static int DecodeAltSigVal(const byte* input, int sz, DecodedCert* cert)
{
    int ret = 0;
    word32 idx = 0;
    int len = 0;

    (void)cert;

    WOLFSSL_ENTER("DecodeAltSigVal");

    if (ret == 0) {
        ret = CheckBitString(input, &idx, &len, sz, 1, NULL);
    }

    if (ret == 0) {
        cert->altSigValDer = (byte *)input + idx;
        cert->altSigValLen = len;
    }

    WOLFSSL_LEAVE("DecodeAltSigVal", ret);
    return ret;
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */

/* Macro to check if bit is set, if not sets and return success.
    Otherwise returns failure */
/* Macro required here because bit-field operation */
#ifndef WOLFSSL_NO_ASN_STRICT
    #define VERIFY_AND_SET_OID(bit) \
        if ((bit) == 0) \
            (bit) = 1; \
        else \
            return ASN_OBJECT_ID_E;
#else
    /* With no strict defined, the verify is skipped */
#define VERIFY_AND_SET_OID(bit) bit = 1;
#endif

/* Parse extension type specific data based on OID sum.
 *
 * Supported extensions:
 *   Basic Constraints - BASIC_CA_OID
 *   CRL Distribution Points - CRL_DIST_OID
 *   Authority Information Access - AUTH_INFO_OID
 *   Subject Alternative Name - ALT_NAMES_OID
 *   Authority Key Identifier - AUTH_KEY_OID
 *   Subject Key Identifier - SUBJ_KEY_OID
 *   Certificate Policies - CERT_POLICY_OID (conditional parsing)
 *   Key Usage - KEY_USAGE_OID
 *   Extended Key Usage - EXT_KEY_USAGE_OID
 *   Name Constraints - NAME_CONS_OID
 *   Inhibit anyPolicy - INHIBIT_ANY_OID
 *   Netscape Certificate Type - NETSCAPE_CT_OID (able to be excluded)
 *   OCSP no check - OCSP_NOCHECK_OID (when compiling OCSP)
 *   Subject Directory Attributes - SUBJ_DIR_ATTR_OID
 *   Subject Information Access - SUBJ_INFO_ACC_OID
 * Unsupported extensions from RFC 5280:
 *   4.2.1.5 - Policy mappings
 *   4.2.1.7 - Issuer Alternative Name
 *   4.2.1.11 - Policy Constraints
 *   4.2.1.15 - Freshest CRL
 *
 * @param [in]      input     Buffer containing extension type specific data.
 * @param [in]      length    Length of data.
 * @param [in]      oid       OID sum for extension.
 * @param [in]      critical  Whether extension is critical.
 * @param [in, out] cert      Certificate object.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoding is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 * @return  Other negative values on error.
 */
WOLFSSL_TEST_VIS int DecodeExtensionType(const byte* input, word32 length,
                                         word32 oid, byte critical,
                                         DecodedCert* cert, int *isUnknownExt)
{
    int ret = 0;
    word32 idx = 0;

    if (isUnknownExt != NULL)
        *isUnknownExt = 0;

    switch (oid) {
        /* Basic Constraints. */
        case BASIC_CA_OID:
            VERIFY_AND_SET_OID(cert->extBasicConstSet);
            cert->extBasicConstCrit = critical ? 1 : 0;
            if (DecodeBasicCaConstraintInternal(input, (int)length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
            break;

        /* CRL Distribution point. */
        case CRL_DIST_OID:
            VERIFY_AND_SET_OID(cert->extCRLdistSet);
            cert->extCRLdistCrit = critical ? 1 : 0;
            if (DecodeCrlDist(input, length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
            break;

        /* Authority information access. */
        case AUTH_INFO_OID:
            VERIFY_AND_SET_OID(cert->extAuthInfoSet);
            cert->extAuthInfoCrit = critical ? 1 : 0;
        #ifndef WOLFSSL_ALLOW_CRIT_AIA
            /* This check is added due to RFC 5280 section 4.2.2.1
            * stating that conforming CA's must mark this extension
            * as non-critical. When parsing extensions check that
            * certificate was made in compliance with this. */
            if (critical) {
                WOLFSSL_MSG("Critical Authority Information Access is not"
                            "allowed");
                WOLFSSL_MSG("Use macro WOLFSSL_ALLOW_CRIT_AIA if wanted");
                ret = ASN_CRIT_EXT_E;
            }
        #endif
            if ((ret == 0) && (DecodeAuthInfo(input, length, cert) < 0)) {
                ret = ASN_PARSE_E;
            }
            break;

        /* Subject alternative name. */
        case ALT_NAMES_OID:
            VERIFY_AND_SET_OID(cert->extSubjAltNameSet);
            cert->extSubjAltNameCrit = critical ? 1 : 0;
            ret = DecodeAltNames(input, length, cert);
            break;

        /* Authority Key Identifier. */
        case AUTH_KEY_OID:
            VERIFY_AND_SET_OID(cert->extAuthKeyIdSet);
            cert->extAuthKeyIdCrit = critical ? 1 : 0;
        #ifndef WOLFSSL_ALLOW_CRIT_AKID
            /* This check is added due to RFC 5280 section 4.2.1.1
             * stating that conforming CA's must mark this extension
             * as non-critical. When parsing extensions check that
             * certificate was made in compliance with this. */
            if (critical) {
                WOLFSSL_MSG("Critical Auth Key ID is not allowed");
                WOLFSSL_MSG("Use macro WOLFSSL_ALLOW_CRIT_AKID if wanted");
                ret = ASN_CRIT_EXT_E;
            }
        #endif
            if ((ret == 0) &&
                (DecodeAuthKeyIdInternal(input, length, cert) < 0)) {
                ret = ASN_PARSE_E;
            }
            break;

        /* Subject Key Identifier. */
        case SUBJ_KEY_OID:
            VERIFY_AND_SET_OID(cert->extSubjKeyIdSet);
            cert->extSubjKeyIdCrit = critical ? 1 : 0;
        #ifndef WOLFSSL_ALLOW_CRIT_SKID
            /* This check is added due to RFC 5280 section 4.2.1.2
             * stating that conforming CA's must mark this extension
             * as non-critical. When parsing extensions check that
             * certificate was made in compliance with this. */
            if (critical) {
                WOLFSSL_MSG("Critical Subject Key ID is not allowed");
                WOLFSSL_MSG("Use macro WOLFSSL_ALLOW_CRIT_SKID if wanted");
                ret = ASN_CRIT_EXT_E;
            }
        #endif

            if ((ret == 0) &&
                (DecodeSubjKeyIdInternal(input, length, cert) < 0)) {
                ret = ASN_PARSE_E;
            }
            break;

        /* Certificate policies. */
        case CERT_POLICY_OID:
        #if defined(WOLFSSL_SEP) || defined(WOLFSSL_CERT_EXT)
            /* certificatePolicies is non-repeatable (RFC 5280 4.2). In strict
             * mode (the default; VERIFY_AND_SET_OID is a no-op under
             * WOLFSSL_NO_ASN_STRICT, like every other extension) reject a
             * duplicate regardless of WOLFSSL_SEP - otherwise the second one
             * silently overwrites the first (DecodeCertPolicy resets
             * extCertPoliciesNb), a policy-authorization confusion. */
            VERIFY_AND_SET_OID(cert->extCertPolicySet);
        #ifdef WOLFSSL_SEP
            cert->extCertPolicyCrit = critical ? 1 : 0;
        #endif
            if (DecodeCertPolicy(input, length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
        #else
            WOLFSSL_MSG("Certificate Policy extension not supported.");
            #ifndef WOLFSSL_NO_ASN_STRICT
            if (critical) {
                WOLFSSL_ERROR_VERBOSE(ASN_CRIT_EXT_E);
                ret = ASN_CRIT_EXT_E;
            }
            #endif
        #endif
            break;

        /* Key usage. */
        case KEY_USAGE_OID:
            VERIFY_AND_SET_OID(cert->extKeyUsageSet);
            cert->extKeyUsageCrit = critical ? 1 : 0;
            if (DecodeKeyUsageInternal(input, length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
            break;

        /* Extended key usage. */
        case EXT_KEY_USAGE_OID:
            VERIFY_AND_SET_OID(cert->extExtKeyUsageSet);
            cert->extExtKeyUsageCrit = critical ? 1 : 0;
            if (DecodeExtKeyUsageInternal(input, length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
            break;

        #ifndef IGNORE_NAME_CONSTRAINTS
        /* Name constraints. */
        case NAME_CONS_OID:
        #ifndef WOLFSSL_NO_ASN_STRICT
            /* Verify RFC 5280 Sec 4.2.1.10 rule:
                "The name constraints extension,
                which MUST be used only in a CA certificate" */
            if (!cert->isCA) {
                WOLFSSL_MSG("Name constraints allowed only for CA certs");
                WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
                ret = ASN_NAME_INVALID_E;
            }
        #endif
            VERIFY_AND_SET_OID(cert->extNameConstraintSet);
            cert->extNameConstraintCrit = critical ? 1 : 0;
            if (DecodeNameConstraints(input, length, cert) < 0) {
                ret = ASN_PARSE_E;
            }
            break;
        #endif /* IGNORE_NAME_CONSTRAINTS */

        /* Inhibit anyPolicy. */
        case INHIBIT_ANY_OID:
            VERIFY_AND_SET_OID(cert->inhibitAnyOidSet);
            WOLFSSL_MSG("Inhibit anyPolicy extension not supported yet.");
            break;

   #ifndef IGNORE_NETSCAPE_CERT_TYPE
        /* Netscape's certificate type. */
        case NETSCAPE_CT_OID:
            if (DecodeNsCertType(input, (int)length, cert) < 0)
                ret = ASN_PARSE_E;
            break;
    #endif
    #ifdef HAVE_OCSP
        /* OCSP no check. */
        case OCSP_NOCHECK_OID:
            VERIFY_AND_SET_OID(cert->ocspNoCheckSet);
            ret = GetASNNull(input, &idx, length);
            if (ret != 0) {
                ret = ASN_PARSE_E;
            }
            break;
    #endif
        case POLICY_CONST_OID:
            VERIFY_AND_SET_OID(cert->extPolicyConstSet);
            cert->extPolicyConstCrit = critical ? 1 : 0;
            if (DecodePolicyConstraints(&input[idx], (int)length, cert) < 0)
                return ASN_PARSE_E;
            break;
    #ifdef WOLFSSL_SUBJ_DIR_ATTR
        case SUBJ_DIR_ATTR_OID:
            VERIFY_AND_SET_OID(cert->extSubjDirAttrSet);
            if (DecodeSubjDirAttr(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
    #endif
    #ifdef WOLFSSL_SUBJ_INFO_ACC
        case SUBJ_INFO_ACC_OID:
            VERIFY_AND_SET_OID(cert->extSubjInfoAccSet);
            if (DecodeSubjInfoAcc(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
    #endif
    #ifdef WOLFSSL_DUAL_ALG_CERTS
        case SUBJ_ALT_PUB_KEY_INFO_OID:
            VERIFY_AND_SET_OID(cert->extSapkiSet);
            cert->extSapkiCrit = critical ? 1 : 0;
            if (DecodeSubjAltPubKeyInfo(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
        case ALT_SIG_ALG_OID:
            VERIFY_AND_SET_OID(cert->extAltSigAlgSet);
            cert->extAltSigAlgCrit = critical ? 1 : 0;
            if (DecodeAltSigAlg(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
        case ALT_SIG_VAL_OID:
            VERIFY_AND_SET_OID(cert->extAltSigValSet);
            cert->extAltSigValCrit = critical ? 1 : 0;
            if (DecodeAltSigVal(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
    #endif /* WOLFSSL_DUAL_ALG_CERTS */
    #ifdef WOLFSSL_ACME_OID
        case ACME_IDENTIFIER_OID:
            VERIFY_AND_SET_OID(cert->extAcmeIdentifierSet);
            cert->extAcmeIdentifierCrit = critical ? 1 : 0;
            if (DecodeAcmeId(&input[idx], length, cert) < 0)
                return ASN_PARSE_E;
            break;
    #endif
        default:
            if (isUnknownExt != NULL)
                *isUnknownExt = 1;
        #ifndef WOLFSSL_NO_ASN_STRICT
            /* While it is a failure to not support critical extensions,
             * still parse the certificate ignoring the unsupported
             * extension to allow caller to accept it with the verify
             * callback. */
            if (critical) {
                WOLFSSL_ERROR_VERBOSE(ASN_CRIT_EXT_E);
                ret = ASN_CRIT_EXT_E;
            }
        #endif
            break;
    }

    return ret;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for extensions.
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields.
 */
static const ASNItem certExtHdrASN[] = {
/* EXTTAG */ { 0, ASN_CONTEXT_SPECIFIC | 3, 1, 1, 0 },
/* EXTSEQ */     { 1, ASN_SEQUENCE, 1, 1, 0 },
};
enum {
    CERTEXTHDRASN_IDX_EXTTAG = 0,
    CERTEXTHDRASN_IDX_EXTSEQ
};

/* Number of items in ASN.1 template for extensions. */
#define certExtHdrASN_Length (sizeof(certExtHdrASN) / sizeof(ASNItem))

/* ASN.1 template for Extension.
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields.
 */
static const ASNItem certExtASN[] = {
/* SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                              /* Extension object id */
/* OID  */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                              /* critical - when true, must be parseable. */
/* CRIT */     { 1, ASN_BOOLEAN, 0, 0, 1 },
                              /* Data for extension - leave index at start of data. */
/* VAL  */     { 1, ASN_OCTET_STRING, 0, 1, 0 },
};
enum {
    CERTEXTASN_IDX_SEQ = 0,
    CERTEXTASN_IDX_OID,
    CERTEXTASN_IDX_CRIT,
    CERTEXTASN_IDX_VAL
};

/* Number of items in ASN.1 template for Extension. */
#define certExtASN_Length (sizeof(certExtASN) / sizeof(ASNItem))
#endif

#ifdef WC_ASN_UNKNOWN_EXT_CB
int wc_SetUnknownExtCallback(DecodedCert* cert,
                             wc_UnknownExtCallback cb) {
    if (cert == NULL) {
        return BAD_FUNC_ARG;
    }

    cert->unknownExtCallback = cb;
    return 0;
}

int wc_SetUnknownExtCallbackEx(DecodedCert* cert,
                               wc_UnknownExtCallbackEx cb, void *ctx) {
    if (cert == NULL) {
        return BAD_FUNC_ARG;
    }

    cert->unknownExtCallbackEx = cb;
    cert->unknownExtCallbackExCtx = ctx;
    return 0;
}
#endif /* WC_ASN_UNKNOWN_EXT_CB */

/*
 *  Processing the Certificate Extensions. This does not modify the current
 *  index. It is works starting with the recorded extensions pointer.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeCertExtensions(DecodedCert* cert)
{
    DECL_ASNGETDATA(dataASN, certExtASN_Length);
    ASNGetData dataExtsASN[certExtHdrASN_Length];
    int ret = 0;
    const byte* input = cert->extensions;
    int sz = cert->extensionsSz;
    word32 idx = 0;
    int criticalRet = 0;
    int offset = 0;

    WOLFSSL_ENTER("DecodeCertExtensions");

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

    ALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, cert->heap);

#ifdef WOLFSSL_CERT_REQ
    if (cert->isCSR) {
        offset = CERTEXTHDRASN_IDX_EXTSEQ;
    }
#endif
    if (ret == 0) {
        /* Clear dynamic data. */
        XMEMSET(dataExtsASN, 0, sizeof(dataExtsASN));
        /* Parse extensions header. */
        ret = GetASN_Items(certExtHdrASN + offset, dataExtsASN + offset,
                           (int)(certExtHdrASN_Length - (size_t)offset), 0,
                           input, &idx, (word32)sz);
    }
    /* Parse each extension. */
    while ((ret == 0) && (idx < (word32)sz)) {
        byte critical = 0;
        int isUnknownExt = 0;

        /* Clear dynamic data. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
        /* Ensure OID is an extension type. */
        GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidCertExtType);
        /* Set criticality variable. */
        GetASN_Int8Bit(&dataASN[CERTEXTASN_IDX_CRIT], &critical);
        /* Parse extension wrapper. */
        ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0, input,
                           &idx, (word32)sz);
        if (ret == 0) {
            word32 oid = dataASN[CERTEXTASN_IDX_OID].data.oid.sum;
            word32 length = dataASN[CERTEXTASN_IDX_VAL].length;

            /* Decode the extension by type. */
            ret = DecodeExtensionType(input + idx, length, oid, critical, cert,
                                      &isUnknownExt);
#ifdef WC_ASN_UNKNOWN_EXT_CB
            if (isUnknownExt && (cert->unknownExtCallback != NULL ||
                                 cert->unknownExtCallbackEx != NULL)) {
                word16 decOid[MAX_OID_SZ];
                word32 decOidSz = MAX_OID_SZ;
                ret = DecodeObjectId(
                          dataASN[CERTEXTASN_IDX_OID].data.oid.data,
                          dataASN[CERTEXTASN_IDX_OID].data.oid.length,
                          decOid, &decOidSz);
                if (ret != 0) {
                    /* Should never get here as the extension was successfully
                     * decoded earlier. Something might be corrupted. */
                    WOLFSSL_MSG("DecodeObjectId() failed. Corruption?");
                    WOLFSSL_ERROR(ret);
                }

                if ((ret == 0) && (cert->unknownExtCallback != NULL)) {
                    ret = cert->unknownExtCallback(decOid, decOidSz, critical,
                              dataASN[CERTEXTASN_IDX_VAL].data.buffer.data,
                              dataASN[CERTEXTASN_IDX_VAL].length);
                }

                if ((ret == 0) && (cert->unknownExtCallbackEx != NULL)) {
                    ret = cert->unknownExtCallbackEx(decOid, decOidSz, critical,
                              dataASN[CERTEXTASN_IDX_VAL].data.buffer.data,
                              dataASN[CERTEXTASN_IDX_VAL].length,
                              cert->unknownExtCallbackExCtx);
                }
            }
#else
            (void)isUnknownExt;
#endif

            /* Move index on to next extension. */
            idx += length;
        }
        /* Don't fail criticality until all other extensions have been checked.
         */
        if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
            criticalRet = ASN_CRIT_EXT_E;
            ret = 0;
        }
    }

    if (ret == 0) {
        /* Use criticality return. */
        ret = criticalRet;
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE

#if defined(HAVE_RPK)
/* ASN template for a Raw Public Key certificate defined RFC7250. */
static const ASNItem RPKCertASN[] = {
/* SubjectPublicKeyInfo ::= SEQUENCE */ { 0, ASN_SEQUENCE, 1, 1, 0 },
    /* algorithm    AlgorithmIdentifier */
    /* AlgorithmIdentifier ::= SEQUENCE */   { 1, ASN_SEQUENCE, 1, 1, 0 },
        /* Algorithm  OBJECT IDENTIFIER */
        /* TBS_SPUBKEYINFO_ALGO_OID     */       { 2, ASN_OBJECT_ID, 0, 0, 0 },
        /* parameters   ANY defined by algorithm OPTIONAL */
        /* TBS_SPUBKEYINFO_ALGO_NULL     */      { 2, ASN_TAG_NULL, 0, 0, 1 },
        /* TBS_SPUBKEYINFO_ALGO_CURVEID  */      { 2, ASN_OBJECT_ID, 0, 0, 1 },
#ifdef WC_RSA_PSS
        /* TBS_SPUBKEYINFO_ALGO_P_SEQ    */      { 2, ASN_SEQUENCE, 1, 0, 1 },
#endif
        /* subjectPublicKey   BIT STRING */
        /* TBS_SPUBKEYINFO_PUBKEY        */   { 1, ASN_BIT_STRING, 0, 0, 0 },
};
/* Number of items in ASN template for a RawPublicKey certificate. */
#define RPKCertASN_Length (sizeof(RPKCertASN) / sizeof(ASNItem))

enum {
    RPKCERTASN_IDX_SPUBKEYINFO_SEQ = 0,
    RPKCERTASN_IDX_SPUBKEYINFO_ALGO_SEQ,
    RPKCERTASN_IDX_SPUBKEYINFO_ALGO_OID,
    RPKCERTASN_IDX_SPUBKEYINFO_ALGO_NULL,
    RPKCERTASN_IDX_SPUBKEYINFO_ALGO_CURVEID,
#ifdef WC_RSA_PSS
    RPKCERTASN_IDX_SPUBKEYINFO_ALGO_P_SEQ,
#endif
    RPKCERTASN_IDX_SPUBKEYINFO_PUBKEY
};

#endif /* HAVE_RPK */

/* ASN template for an X509 certificate.
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields.
 */
static const ASNItem x509CertASN[] = {
        /* Certificate ::= SEQUENCE */
/* SEQ                           */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* tbsCertificate       TBSCertificate */
                                                   /* TBSCertificate ::= SEQUENCE */
/* TBS_SEQ                       */        { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* version         [0]  EXPLICIT Version DEFAULT v1 */
/* TBS_VER                       */            { 2, ASN_CONTEXT_SPECIFIC | ASN_X509_CERT_VERSION, 1, 1, 1 },
                                                   /* Version ::= INTEGER { v1(0), v2(1), v3(2) */
/* TBS_VER_INT                   */                { 3, ASN_INTEGER, 0, 0, 0 },
                                                   /* serialNumber         CertificateSerialNumber */
                                                   /* CertificateSerialNumber ::= INTEGER */
/* TBS_SERIAL                    */            { 2, ASN_INTEGER, 0, 0, 0 },
                                                   /* signature            AlgorithmIdentifier */
                                                   /* AlgorithmIdentifier ::= SEQUENCE */
/* TBS_ALGOID_SEQ                */            { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* Algorithm    OBJECT IDENTIFIER */
/* TBS_ALGOID_OID                */                { 3, ASN_OBJECT_ID, 0, 0, 0 },
                                                   /* parameters   ANY defined by algorithm OPTIONAL */
/* TBS_ALGOID_PARAMS_NULL        */                { 3, ASN_TAG_NULL, 0, 0, 2 },
#ifdef WC_RSA_PSS
/* TBS_ALGOID_PARAMS             */                { 3, ASN_SEQUENCE, 1, 0, 2 },
#endif
                                                   /* issuer               Name */
/* TBS_ISSUER_SEQ                */            { 2, ASN_SEQUENCE, 1, 0, 0 },
                                                   /* validity             Validity */
                                                   /* Validity ::= SEQUENCE */
/* TBS_VALIDITY_SEQ              */            { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* notBefore   Time */
                                                   /* Time :: CHOICE { UTCTime, GeneralizedTime } */
/* TBS_VALIDITY_NOTB_UTC         */                { 3, ASN_UTC_TIME, 0, 0, 2 },
/* TBS_VALIDITY_NOTB_GT          */                { 3, ASN_GENERALIZED_TIME, 0, 0, 2 },
                                                   /* notAfter   Time */
                                                   /* Time :: CHOICE { UTCTime, GeneralizedTime } */
/* TBS_VALIDITY_NOTA_UTC         */                { 3, ASN_UTC_TIME, 0, 0, 3 },
/* TBS_VALIDITY_NOTA_GT          */                { 3, ASN_GENERALIZED_TIME, 0, 0, 3 },
                                                   /* subject              Name */
/* TBS_SUBJECT_SEQ               */            { 2, ASN_SEQUENCE, 1, 0, 0 },
                                                   /* subjectPublicKeyInfo SubjectPublicKeyInfo */
/* TBS_SPUBKEYINFO_SEQ           */            { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* algorithm          AlgorithmIdentifier */
                                                   /* AlgorithmIdentifier ::= SEQUENCE */
/* TBS_SPUBKEYINFO_ALGO_SEQ      */                { 3, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* Algorithm    OBJECT IDENTIFIER */
/* TBS_SPUBKEYINFO_ALGO_OID      */                    { 4, ASN_OBJECT_ID, 0, 0, 0 },
                                                   /* parameters   ANY defined by algorithm OPTIONAL */
/* TBS_SPUBKEYINFO_ALGO_NULL     */                    { 4, ASN_TAG_NULL, 0, 0, 2 },
/* TBS_SPUBKEYINFO_ALGO_CURVEID  */                    { 4, ASN_OBJECT_ID, 0, 0, 2 },
#ifdef WC_RSA_PSS
/* TBS_SPUBKEYINFO_ALGO_P_SEQ    */                    { 4, ASN_SEQUENCE, 1, 0, 2 },
#endif
                                                   /* subjectPublicKey   BIT STRING */
/* TBS_SPUBKEYINFO_PUBKEY        */                { 3, ASN_BIT_STRING, 0, 0, 0 },
                                                   /* issuerUniqueID       UniqueIdentfier OPTIONAL */
/* TBS_ISSUERUID                 */            { 2, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 },
                                                   /* subjectUniqueID      UniqueIdentfier OPTIONAL */
/* TBS_SUBJECTUID                */            { 2, ASN_CONTEXT_SPECIFIC | 2, 0, 0, 1 },
                                                   /* extensions           Extensions OPTIONAL */
/* TBS_EXT                       */            { 2, ASN_CONTEXT_SPECIFIC | 3, 1, 1, 1 },
/* TBS_EXT_SEQ                   */                { 3, ASN_SEQUENCE, 1, 0, 0 },
                                                   /* signatureAlgorithm   AlgorithmIdentifier */
                                                   /* AlgorithmIdentifier ::= SEQUENCE */
/* SIGALGO_SEQ                   */        { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* Algorithm    OBJECT IDENTIFIER */
/* SIGALGO_OID                   */            { 2, ASN_OBJECT_ID, 0, 0, 0 },
                                                   /* parameters   ANY defined by algorithm OPTIONAL */
/* SIGALGO_PARAMS_NULL           */            { 2, ASN_TAG_NULL, 0, 0, 2 },
#ifdef WC_RSA_PSS
/* SIGALGO_PARAMS                */            { 2, ASN_SEQUENCE, 1, 0, 2 },
#endif
                                                   /* signature            BIT STRING */
/* SIGNATURE                     */        { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    X509CERTASN_IDX_SEQ = 0,
    X509CERTASN_IDX_TBS_SEQ,
    X509CERTASN_IDX_TBS_VER,
    X509CERTASN_IDX_TBS_VER_INT,
    X509CERTASN_IDX_TBS_SERIAL,
    X509CERTASN_IDX_TBS_ALGOID_SEQ,
    X509CERTASN_IDX_TBS_ALGOID_OID,
    X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL,
#ifdef WC_RSA_PSS
    X509CERTASN_IDX_TBS_ALGOID_PARAMS,
#endif
    X509CERTASN_IDX_TBS_ISSUER_SEQ,
    X509CERTASN_IDX_TBS_VALIDITY_SEQ,
    X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC,
    X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT,
    X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC,
    X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT,
    X509CERTASN_IDX_TBS_SUBJECT_SEQ,
    X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ,
    X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_SEQ,
    X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_OID,
    X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_NULL,
    X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_CURVEID,
#ifdef WC_RSA_PSS
    X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_P_SEQ,
#endif
    X509CERTASN_IDX_TBS_SPUBKEYINFO_PUBKEY,
    X509CERTASN_IDX_TBS_ISSUERUID,
    X509CERTASN_IDX_TBS_SUBJECTUID,
    X509CERTASN_IDX_TBS_EXT,
    X509CERTASN_IDX_TBS_EXT_SEQ,
    X509CERTASN_IDX_SIGALGO_SEQ,
    X509CERTASN_IDX_SIGALGO_OID,
    X509CERTASN_IDX_SIGALGO_PARAMS_NULL,
#ifdef WC_RSA_PSS
    X509CERTASN_IDX_SIGALGO_PARAMS,
#endif
    X509CERTASN_IDX_SIGNATURE,
    WOLF_ENUM_DUMMY_LAST_ELEMENT(X509CERTASN_IDX)
};

/* Number of items in ASN template for an X509 certificate. */
#define x509CertASN_Length (sizeof(x509CertASN) / sizeof(ASNItem))

/* Check the data data.
 *
 * @param [in] dataASN   ASN template dynamic data item.
 * @param [in] dataType  ASN_BEFORE or ASN_AFTER date.
 * @return  0 on success.
 * @return  ASN_TIME_E when BER tag is nor UTC or GENERALIZED time.
 * @return  ASN_DATE_SZ_E when time data is not supported.
 * @return  ASN_BEFORE_DATE_E when ASN_BEFORE date is invalid.
 * @return  ASN_AFTER_DATE_E when ASN_AFTER date is invalid.
 */
static int CheckDate(ASNGetData *dataASN, int dateType)
{
    int ret = 0;

    /* Check BER tag is valid. */
    if ((dataASN->tag != ASN_UTC_TIME) &&
            (dataASN->tag != ASN_GENERALIZED_TIME)) {
        ret = ASN_TIME_E;
    }
    /* Check date length is valid. */
    if ((ret == 0) && ((dataASN->length > MAX_DATE_SIZE) ||
                       (dataASN->length < MIN_DATE_SIZE))) {
        ret = ASN_DATE_SZ_E;
    }

#ifndef NO_ASN_TIME_CHECK
    /* Check date is a valid string and ASN_BEFORE or ASN_AFTER now. */
    if ((ret == 0) && (! AsnSkipDateCheck)) {
        if (!XVALIDATE_DATE(dataASN->data.ref.data, dataASN->tag, dateType,
                            (int)dataASN->data.ref.length)) {
            if (dateType == ASN_BEFORE) {
                ret = ASN_BEFORE_DATE_E;
            }
            else if (dateType == ASN_AFTER) {
                ret = ASN_AFTER_DATE_E;
            }
            else {
                ret = ASN_TIME_E;
            }
        }
    }
#endif
    (void)dateType;

    return ret;
}

/* Decode a certificate. Internal/non-public API.
 *
 * @param [in]  cert             Certificate object.
 * @param [in]  verify           Whether to verify dates before and after now.
 * @param [out] criticalExt      Critical extension return code.
 * @param [out] badDateRet       Bad date return code.
 * @param [in]  stopAtPubKey     Stop parsing before subjectPublicKeyInfo.
 * @param [in]  stopAfterPubKey  Stop parsing after subjectPublicKeyInfo.
 * @return  0 on success if of the stop arguments is not set, otherwise set to
 *          the corresponding byte offset at which the parsing stopped.
 * @return  ASN_CRIT_EXT_E when a critical extension was not recognized.
 * @return  ASN_TIME_E when date BER tag is nor UTC or GENERALIZED time.
 * @return  ASN_DATE_SZ_E when time data is not supported.
 * @return  ASN_BEFORE_DATE_E when ASN_BEFORE date is invalid.
 * @return  ASN_AFTER_DATE_E when ASN_AFTER date is invalid.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
                              int* badDateRet, int stopAtPubKey,
                              int stopAfterPubKey)
{
    DECL_ASNGETDATA(dataASN, x509CertASN_Length);
    int ret = 0;
    int badDate = 0;
    byte version = 0;
    word32 idx;
    word32 serialSz = 0;
    const unsigned char* issuer = NULL;
    word32 issuerSz = 0;
    const unsigned char* subject = NULL;
    word32 subjectSz = 0;
    word32 pubKeyOffset = 0;
    word32 pubKeyEnd = 0;
    int done = 0;

#if defined(HAVE_RPK)
    /* try to parse the cert as Raw Public Key cert */
    DECL_ASNGETDATA(RPKdataASN, RPKCertASN_Length);
    CALLOC_ASNGETDATA(RPKdataASN, RPKCertASN_Length, ret, cert->heap);
    GetASN_OID(&RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_OID],
                                                                oidKeyType);
    GetASN_OID(&RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_CURVEID],
                                                                oidCurveType);
    ret = GetASN_Items(RPKCertASN, RPKdataASN, RPKCertASN_Length, 1,
                           cert->source, &cert->srcIdx, cert->maxIdx);

    if (ret == 0) {
        if (( RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_NULL].length &&
              RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_CURVEID].length)
#ifdef WC_RSA_PSS
         || ( RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_P_SEQ].length &&
            ( RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_NULL].length ||
              RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_CURVEID].length))
#endif
        ) {
            WOLFSSL_MSG("Multiple RPK algorithm parameters set.");
            ret = ASN_PARSE_E;
        }
    }
    if (ret == 0) {
        cert->keyOID =
                RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_ALGO_OID].data.oid.sum;

        /* Parse the public key. */
        pubKeyOffset = RPKdataASN[RPKCERTASN_IDX_SPUBKEYINFO_SEQ].offset;
        pubKeyEnd = cert->maxIdx;
        ret = GetCertKey(cert, cert->source, &pubKeyOffset, pubKeyEnd);
        if (ret == 0) {
            WOLFSSL_MSG("Raw Public Key certificate found and parsed");
            cert->isRPK = 1;
        }
    }
    /* Dispose of memory before allocating for extension decoding. */
    FREE_ASNGETDATA(RPKdataASN, cert->heap);

    if (ret == 0) {
        return ret;
    }
    else {
        ret = 0;    /* proceed to the original x509 parsing */
    }
#endif /* HAVE_RPK */

    CALLOC_ASNGETDATA(dataASN, x509CertASN_Length, ret, cert->heap);

    if (ret == 0) {
        version = 0;
        serialSz = EXTERNAL_SERIAL_SIZE;

        /* Get the version and put the serial number into the buffer. */
        GetASN_Int8Bit(&dataASN[X509CERTASN_IDX_TBS_VER_INT], &version);
        GetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_SERIAL], cert->serial,
                &serialSz);
        /* Check OID types for signature, algorithm, ECC curve and sigAlg. */
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_ALGOID_OID], oidSigType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_OID],
                oidKeyType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_CURVEID],
                oidCurveType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_SIGALGO_OID], oidSigType);
        /* Parse the X509 certificate. */
        ret = GetASN_Items(x509CertASN, dataASN, x509CertASN_Length, 1,
                           cert->source, &cert->srcIdx, cert->maxIdx);
#ifdef WOLFSSL_CLANG_TIDY
        /* work around clang-tidy false positive re cert->source. */
        if ((ret == 0) && (cert->source == NULL)) {
            ret = ASN_PARSE_E;
        }
#endif
    }
    /* Check version is valid/supported - can't be negative. */
    if ((ret == 0) && (version > MAX_X509_VERSION)) {
        WOLFSSL_MSG("Unexpected certificate version");
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        int i;

        pubKeyOffset = dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ].offset;
        /* Set fields extracted from data. */
        cert->version = version;
        cert->serialSz = (int)serialSz;

        /* RFC 5280 requires serial number to be present and at least 1 byte */
        if (cert->serialSz == 0) {
            WOLFSSL_MSG("Error: certificate serial number is empty "
                        "(zero-length serial is invalid per RFC 5280)");
            ret = ASN_PARSE_E;
        }

        cert->signatureOID = dataASN[X509CERTASN_IDX_TBS_ALGOID_OID].data.oid.sum;
        cert->keyOID = dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_OID].data.oid.sum;
        cert->certBegin = dataASN[X509CERTASN_IDX_TBS_SEQ].offset;

        /* No bad date error - don't always care. */
        badDate = 0;
        /* Find the item with the ASN_BEFORE date and check it. */
        i = (dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].tag != 0)
                ? X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC
                : X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT;
        if ((CheckDate(&dataASN[i], ASN_BEFORE) < 0) && (verify != NO_VERIFY) &&
                (verify != VERIFY_SKIP_DATE) && (! AsnSkipDateCheck)) {
            badDate = ASN_BEFORE_DATE_E;
        }
        /* Store reference to ASN_BEFORE date. */
        cert->beforeDate = GetASNItem_Addr(dataASN[i], cert->source);
        cert->beforeDateLen = (int)GetASNItem_Length(dataASN[i], cert->source);

        /* Find the item with the ASN_AFTER date and check it. */
        i = (dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].tag != 0)
                ? X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC
                : X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT;
        if ((CheckDate(&dataASN[i], ASN_AFTER) < 0) && (verify != NO_VERIFY) &&
                (verify != VERIFY_SKIP_DATE) && (! AsnSkipDateCheck)) {
            badDate = ASN_AFTER_DATE_E;
        }
        /* Store reference to ASN_AFTER date. */
        cert->afterDate = GetASNItem_Addr(dataASN[i], cert->source);
        cert->afterDateLen = (int)GetASNItem_Length(dataASN[i], cert->source);

        /* Get the issuer name. */
        issuer = cert->source + dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ].offset;
        issuerSz = dataASN[X509CERTASN_IDX_TBS_VALIDITY_SEQ].offset -
            dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ].offset;

        /* Get the subject name. */
        subject = cert->source +
            dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].offset;
        subjectSz = dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ].offset -
            dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].offset;
    }
    if ((ret == 0) && stopAtPubKey) {
        /* Return any bad date error through badDateRet and return offset of
         * subjectPublicKeyInfo.
         */
        if (badDateRet != NULL) {
            *badDateRet = badDate;
        }
        done = 1;
    }

    if ((ret == 0) && (!done)) {
        /* Store the signature information. */
        cert->sigIndex = dataASN[X509CERTASN_IDX_SIGALGO_SEQ].offset;
        GetASN_GetConstRef(&dataASN[X509CERTASN_IDX_SIGNATURE],
                &cert->signature, &cert->sigLength);
        /* Make sure 'signature' and 'signatureAlgorithm' are the same. */
        if (dataASN[X509CERTASN_IDX_SIGALGO_OID].data.oid.sum
                != cert->signatureOID) {
            WOLFSSL_ERROR_VERBOSE(ASN_SIG_OID_E);
            ret = ASN_SIG_OID_E;
        }
        /* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
        else if (IsSigAlgoNoParams(cert->signatureOID)) {
        #ifndef WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED
            if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS_NULL].tag != 0) {
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                ret = ASN_PARSE_E;
            }
        #endif
        #ifdef WC_RSA_PSS
            if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].tag != 0) {
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                ret = ASN_PARSE_E;
            }
        #endif
        }
    #ifdef WC_RSA_PSS
        /* Check parameters starting with a SEQUENCE. */
        else if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].tag != 0) {
            word32 oid = dataASN[X509CERTASN_IDX_SIGALGO_OID].data.oid.sum;
            word32 sigAlgParamsSz = 0;

            /* Parameters only with RSA PSS. */
            if (oid != CTC_RSASSAPSS) {
                WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                ret = ASN_PARSE_E;
            }
            if (ret == 0) {
                const byte* tbsParams;
                word32 tbsParamsSz;
                const byte* sigAlgParams;

                /* Check RSA PSS parameters are the same. */
                tbsParams =
                    GetASNItem_Addr(dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS],
                        cert->source);
                tbsParamsSz =
                    GetASNItem_Length(dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS],
                        cert->source);
                sigAlgParams =
                    GetASNItem_Addr(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert->source);
                sigAlgParamsSz =
                    GetASNItem_Length(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert->source);
                if ((tbsParamsSz != sigAlgParamsSz) ||
                        (XMEMCMP(tbsParams, sigAlgParams, tbsParamsSz) != 0)) {
                    WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                    ret = ASN_PARSE_E;
                }
            }
            if (ret == 0) {
                /* Store parameters for use in signature verification.
                 * Use full TLV length: tag (1) + length bytes + content.
                 * GetASNItem_Length can be content-only when buffer.data unset. */
                word32 off = dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].offset;
                word32 contentLen =
                    (word32)dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].length;
                cert->sigParamsIndex = off;
                cert->sigParamsLength = (contentLen <= 127)
                    ? (2 + contentLen)
                    : GetASNItem_Length(
                        dataASN[X509CERTASN_IDX_SIGALGO_PARAMS], cert->source);
            }
        }
    #endif
    }
    if ((ret == 0) && (!done)) {
        pubKeyEnd = dataASN[X509CERTASN_IDX_TBS_ISSUERUID].offset;
        if (stopAfterPubKey) {
            /* Return any bad date error through badDateRed and return offset
             * after subjectPublicKeyInfo.
             */
            if (badDateRet != NULL) {
                *badDateRet = badDate;
            }
            done = 1;
        }
    }
    if ((ret == 0) && (!done) &&
            (dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].data.ref.data != NULL)) {
    #ifndef ALLOW_V1_EXTENSIONS
        /* Certificate extensions were only defined in version 2. */
        if (cert->version < 2) {
            WOLFSSL_MSG("\tv1 and v2 certs not allowed extensions");
            WOLFSSL_ERROR_VERBOSE(ASN_VERSION_E);
            ret = ASN_VERSION_E;
        }
    #endif
        if (ret == 0) {
            /* Save references to extension data. */
            cert->extensions    = GetASNItem_Addr(
                    dataASN[X509CERTASN_IDX_TBS_EXT], cert->source);
            cert->extensionsSz  = (int)GetASNItem_Length(
                    dataASN[X509CERTASN_IDX_TBS_EXT], cert->source);
            cert->extensionsIdx = dataASN[X509CERTASN_IDX_TBS_EXT].offset;
        }
    }

    /* Dispose of memory before allocating for extension decoding. */
    FREE_ASNGETDATA(dataASN, cert->heap);

    if ((ret == 0) && (issuer != NULL)) {
        idx = 0;
        /* Put issuer into cert and calculate hash. */
        ret = GetCertName(cert, cert->issuer, cert->issuerHash, ASN_ISSUER, issuer,
            &idx, issuerSz);
    }
    if ((ret == 0) && (subject != NULL)) {
        idx = 0;
        /* Put subject into cert and calculate hash. */
        ret = GetCertName(cert, cert->subject, cert->subjectHash, ASN_SUBJECT,
            subject, &idx, subjectSz);
    }
    if (ret == 0) {
        /* Determine if self signed by comparing issuer and subject hashes. */
    #ifdef WOLFSSL_CERT_REQ
        if (cert->isCSR) {
            cert->selfSigned = 1;
        }
        else
    #endif
        {
            cert->selfSigned = (XMEMCMP(cert->issuerHash, cert->subjectHash,
                                        KEYID_SIZE) == 0);
        }
        if (stopAtPubKey) {
            ret = (int)pubKeyOffset;
        }
    }

    if ((ret == 0) && (!stopAtPubKey)) {
        /* Parse the public key. */
        idx = pubKeyOffset;
        ret = GetCertKey(cert, cert->source, &idx, pubKeyEnd);
    }
    if ((ret == 0) && (!stopAtPubKey) && (!stopAfterPubKey) &&
            (cert->extensions != NULL)) {
        /* Decode the extension data starting at [3]. */
        ret = DecodeCertExtensions(cert);
        if (criticalExt != NULL) {
            if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
                /* Return critical extension not recognized. */
                *criticalExt = ret;
                ret = 0;
            }
            else {
                /* No critical extension error. */
                *criticalExt = 0;
            }
        }
    }

    if ((ret == 0) && (!done) && (badDate != 0)) {
        /* Parsed whole certificate fine but return any date errors. */
        ret = badDate;
    }

#if !defined(WOLFSSL_NO_ASN_STRICT) && !defined(WOLFSSL_PYTHON) && \
    !defined(WOLFSSL_ASN_ALLOW_0_SERIAL)
    /* Structural serial-0 rejection. RFC 5280 4.1.2.2 requires positive
     * serials; reject here so non-trust-bearing pubkey-extraction callers
     * (DecodeToKey, wc_GetPubX509, d2i_X509-style decode) do not silently
     * accept malformed certs. ParseCertRelative performs the authoritative
     * trust-bearing check (with the CA_TYPE/TRUSTED_PEER_TYPE exemption
     * for legacy self-signed root CAs); the exemption here is intentionally
     * narrow: when extensions are not parsed (stopAtPubKey/stopAfterPubKey)
     * isCA is not populated, so the exemption never fires and serial-0 is
     * always rejected on those paths. */
    if ((ret == 0) && (cert->serialSz == 1) && (cert->serial[0] == 0)) {
        if (!(cert->isCA && cert->selfSigned)
#ifdef WOLFSSL_CERT_REQ
            && !cert->isCSR
#endif
        ) {
            WOLFSSL_MSG("Error serial number of 0 for non-root certificate");
            ret = ASN_PARSE_E;
        }
    }
#endif

    return ret;
}

/* Decode BER/DER data into certificate object.
 *
 * BER/DER data information held in source, srcIdx and maxIdx fields of
 * certificate object.
 *
 * @param [in] cert         Decoded certificate object.
 * @param [in] verify       Whether to find CA and verify certificate.
 * @param [in] criticalExt  Any error for critical extensions not recognized.
 * @return  0 on success.
 * @return  ASN_CRIT_EXT_E when a critical extension was not recognized.
 * @return  ASN_TIME_E when date BER tag is nor UTC or GENERALIZED time.
 * @return  ASN_DATE_SZ_E when time data is not supported.
 * @return  ASN_BEFORE_DATE_E when ASN_BEFORE date is invalid.
 * @return  ASN_AFTER_DATE_E when ASN_AFTER date is invalid.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_BITSTR_E when the expected BIT_STRING tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
int DecodeCert(DecodedCert* cert, int verify, int* criticalExt)
{
    return DecodeCertInternal(cert, verify, criticalExt, NULL, 0, 0);
}

#ifdef WOLFSSL_CERT_REQ
/* ASN.1 template for certificate request Attribute.
 * PKCS #10: RFC 2986, 4.1 - CertificationRequestInfo
 */
static const ASNItem reqAttrASN[] = {
/* SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                              /* type */
/* TYPE */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                              /* values */
/* VALS */     { 1, ASN_SET, 1, 0, 0 },
};
enum {
    REQATTRASN_IDX_SEQ = 0,
    REQATTRASN_IDX_TYPE,
    REQATTRASN_IDX_VALS
};

/* Number of items in ASN.1 template for certificate request Attribute. */
#define reqAttrASN_Length (sizeof(reqAttrASN) / sizeof(ASNItem))

/* ASN.1 template for a string choice. */
static const ASNItem strAttrASN[] = {
    { 0, 0, 0, 0, 0 },
};
enum {
    STRATTRASN_IDX_STR = 0
};

/* Number of items in ASN.1 template for a string choice. */
#define strAttrASN_Length (sizeof(strAttrASN) / sizeof(ASNItem))

/* ASN.1 choices for types for a string in an attribute. */
static const byte strAttrChoice[] = {
    ASN_PRINTABLE_STRING, ASN_IA5_STRING, ASN_UTF8STRING, 0
};

/* Decode a certificate request attribute's value.
 *
 * @param [in]  cert         Certificate request object.
 * @param [out] criticalExt  Critical extension return code.
 * @param [in]  oid          OID describing which attribute was found.
 * @param [in]  aIdx         Index into certificate source to start parsing.
 * @param [in]  input        Attribute value data.
 * @param [in]  maxIdx       Maximum index to parse to.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 */
static int DecodeCertReqAttrValue(DecodedCert* cert, int* criticalExt,
    word32 oid, word32 aIdx, const byte* input, word32 maxIdx)
{
    int ret = 0;
    word32 idx = 0;
    ASNGetData strDataASN[strAttrASN_Length];

    switch (oid) {
        case PKCS9_CONTENT_TYPE_OID:
            /* Clear dynamic data and specify choices acceptable. */
            XMEMSET(strDataASN, 0, sizeof(strDataASN));
            GetASN_Choice(&strDataASN[STRATTRASN_IDX_STR], strAttrChoice);
            /* Parse a string. */
            ret = GetASN_Items(strAttrASN, strDataASN, strAttrASN_Length,
                               1, input, &idx, maxIdx);
            if (ret == 0) {
                /* Store references to password data. */
                cert->contentType = (const char*)
                        strDataASN[STRATTRASN_IDX_STR].data.ref.data;
                cert->contentTypeLen =
                        (int)strDataASN[STRATTRASN_IDX_STR].data.ref.length;
            }
            break;

        /* A password by which the entity may request certificate revocation.
         * PKCS#9: RFC 2985, 5.4.1 - Challenge password
         */
        case CHALLENGE_PASSWORD_OID:
            /* Clear dynamic data and specify choices acceptable. */
            XMEMSET(strDataASN, 0, sizeof(strDataASN));
            GetASN_Choice(&strDataASN[STRATTRASN_IDX_STR], strAttrChoice);
            /* Parse a string. */
            ret = GetASN_Items(strAttrASN, strDataASN, strAttrASN_Length,
                               1, input, &idx, maxIdx);
            if (ret == 0) {
                /* Store references to password data. */
                cert->cPwd = (const char*)
                    strDataASN[STRATTRASN_IDX_STR].data.ref.data;
                cert->cPwdLen = (int)strDataASN[STRATTRASN_IDX_STR].
                    data.ref.length;
            }
            break;

        /* Requested serial number to issue with.
         * PKCS#9: RFC 2985, 5.2.10 - Serial Number
         * (References: ISO/IEC 9594-6:1997)
         */
        case SERIAL_NUMBER_OID:
            /* Clear dynamic data and specify choices acceptable. */
            XMEMSET(strDataASN, 0, sizeof(strDataASN));
            GetASN_Choice(&strDataASN[STRATTRASN_IDX_STR], strAttrChoice);
            /* Parse a string. */
            ret = GetASN_Items(strAttrASN, strDataASN, strAttrASN_Length,
                               1, input, &idx, maxIdx);
            if (ret == 0) {
                /* Store references to serial number. */
                cert->sNum = (const char*)
                    strDataASN[STRATTRASN_IDX_STR].data.ref.data;
                cert->sNumLen = (int)strDataASN[STRATTRASN_IDX_STR].
                    data.ref.length;
                /* Store serial number if small enough. */
                if (cert->sNumLen <= EXTERNAL_SERIAL_SIZE) {
                    XMEMCPY(cert->serial, cert->sNum, (size_t)cert->sNumLen);
                    cert->serialSz = cert->sNumLen;
                }
            }
            break;

        case UNSTRUCTURED_NAME_OID:
            /* Clear dynamic data and specify choices acceptable. */
            XMEMSET(strDataASN, 0, sizeof(strDataASN));
            GetASN_Choice(&strDataASN[STRATTRASN_IDX_STR], strAttrChoice);
            /* Parse a string. */
            ret = GetASN_Items(strAttrASN, strDataASN, strAttrASN_Length,
                               1, input, &idx, maxIdx);
            if (ret == 0) {
                /* Store references to unstructured name. */
                cert->unstructuredName = (const char*)
                    strDataASN[STRATTRASN_IDX_STR].data.ref.data;
                cert->unstructuredNameLen = (int)strDataASN[STRATTRASN_IDX_STR].
                    data.ref.length;
            }
            break;

        /* Certificate extensions to be included in generated certificate.
         * PKCS#9: RFC 2985, 5.4.2 - Extension request
         */
        case EXTENSION_REQUEST_OID:
            /* Store references to all extensions. */
            cert->extensions    = input;
            cert->extensionsSz  = (int)maxIdx;
            cert->extensionsIdx = aIdx;

            /* Decode and validate extensions. */
            ret = DecodeCertExtensions(cert);
            if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
                /* Return critical extension not recognized. */
                *criticalExt = ret;
                ret = 0;
            }
            else {
                /* No critical extension error. */
                *criticalExt = 0;
            }
            break;

        default:
            ret = ASN_PARSE_E;
            break;
    }

    return ret;
}

/* Decode attributes of a BER encoded certificate request.
 *
 * RFC 2986 - PKCS #10: Certification Request Syntax Specification Version 1.7
 *
 * Outer sequence has been removed.
 *
 * @param [in]  cert         Certificate request object.
 * @param [out] criticalExt  Critical extension return code.
 * @param [in]  idx          Index into certificate source to start parsing.
 * @param [in]  maxIdx       Maximum index to parse to.
 * @return  0 on success.
 * @return  ASN_CRIT_EXT_E when a critical extension was not recognized.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 */
static int DecodeCertReqAttributes(DecodedCert* cert, int* criticalExt,
                                   word32 idx, word32 maxIdx)
{
    DECL_ASNGETDATA(dataASN, reqAttrASN_Length);
    int ret = 0;

    WOLFSSL_ENTER("DecodeCertReqAttributes");

    ALLOC_ASNGETDATA(dataASN, reqAttrASN_Length, ret, cert->heap);

    /* Parse each attribute until all data used up. */
    while ((ret == 0) && (idx < maxIdx)) {
        /* Clear dynamic data. */
        XMEMSET(dataASN, 0, sizeof(ASNGetData) * reqAttrASN_Length);
        GetASN_OID(&dataASN[REQATTRASN_IDX_TYPE], oidIgnoreType);

        /* Parse an attribute. */
        ret = GetASN_Items(reqAttrASN, dataASN, reqAttrASN_Length, 0,
                           cert->source, &idx, maxIdx);
        /* idx is now at end of attribute data. */
        if (ret == 0) {
            ret = DecodeCertReqAttrValue(cert, criticalExt,
                dataASN[REQATTRASN_IDX_TYPE].data.oid.sum,
                GetASNItem_DataIdx(dataASN[REQATTRASN_IDX_VALS], cert->source),
                dataASN[REQATTRASN_IDX_VALS].data.ref.data,
                dataASN[REQATTRASN_IDX_VALS].data.ref.length);
        }
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}

/* ASN.1 template for a certificate request.
 * PKCS#10: RFC 2986, 4.1 - CertificationRequestInfo
 * PKCS#10: RFC 2986, 4.2 - CertificationRequest
 */
static const ASNItem certReqASN[] = {
            /* CertificationRequest */
/* SEQ                              */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                                          /* CertificationRequestInfo */
/* INFO_SEQ                         */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                              /* version              INTEGER { v1(0), v2(1), v3(2) */
/* INFO_VER                         */         { 2, ASN_INTEGER, 0, 0, 0 },
                                                              /* subject              Name */
/* INFO_SUBJ_SEQ                    */         { 2, ASN_SEQUENCE, 1, 0, 0 },
                                                              /* subjectPublicKeyInfo SubjectPublicKeyInfo */
/* INFO_SPUBKEYINFO_SEQ             */         { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                                  /* algorithm          AlgorithmIdentifier */
/* INFO_SPUBKEYINFO_ALGOID_SEQ      */             { 3, ASN_SEQUENCE, 1, 1, 0 },
                                                                      /* Algorithm    OBJECT IDENTIFIER */
/* INFO_SPUBKEYINFO_ALGOID_OID      */                 { 4, ASN_OBJECT_ID, 0, 0, 0 },
                                                                      /* parameters   ANY defined by algorithm OPTIONAL */
/* INFO_SPUBKEYINFO_ALGOID_NULL     */                 { 4, ASN_TAG_NULL, 0, 0, 1 },
/* INFO_SPUBKEYINFO_ALGOID_CURVEID  */                 { 4, ASN_OBJECT_ID, 0, 0, 1 },
/* INFO_SPUBKEYINFO_ALGOID_PARAMS   */                 { 4, ASN_SEQUENCE, 1, 0, 1 },
                                                                  /* subjectPublicKey   BIT STRING */
/* INFO_SPUBKEYINFO_PUBKEY          */             { 3, ASN_BIT_STRING, 0, 0, 0 },
                                                              /* attributes       [0] Attributes */
/* INFO_ATTRS                       */         { 2, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 1 },
                                                          /* signatureAlgorithm   AlgorithmIdentifier */
/* INFO_SIGALGO_SEQ                 */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                              /* Algorithm    OBJECT IDENTIFIER */
/* INFO_SIGALGO_OID                 */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
                                                              /* parameters   ANY defined by algorithm OPTIONAL */
/* INFO_SIGALGO_NULL                */         { 2, ASN_TAG_NULL, 0, 0, 1 },
                                                          /* signature            BIT STRING */
/* INFO_SIGNATURE                   */     { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    CERTREQASN_IDX_SEQ = 0,
    CERTREQASN_IDX_INFO_SEQ,
    CERTREQASN_IDX_INFO_VER,
    CERTREQASN_IDX_INFO_SUBJ_SEQ,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_SEQ,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_SEQ,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_OID,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_NULL,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_CURVEID,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_PARAMS,
    CERTREQASN_IDX_INFO_SPUBKEYINFO_PUBKEY,
    CERTREQASN_IDX_INFO_ATTRS,
    CERTREQASN_IDX_INFO_SIGALGO_SEQ,
    CERTREQASN_IDX_INFO_SIGALGO_OID,
    CERTREQASN_IDX_INFO_SIGALGO_NULL,
    CERTREQASN_IDX_INFO_SIGNATURE
};

/* Number of items in ASN.1 template for a certificate request. */
#define certReqASN_Length (sizeof(certReqASN) / sizeof(ASNItem))

/* Parse BER encoded certificate request.
 *
 * RFC 2986 - PKCS #10: Certification Request Syntax Specification Version 1.7
 *
 * @param [in]  cert         Certificate request object.
 * @param [out] criticalExt  Critical extension return code.
 * @return  0 on success.
 * @return  ASN_CRIT_EXT_E when a critical extension was not recognized.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found.
 * @return  ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a
 *          non-zero length.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int DecodeCertReq(DecodedCert* cert, int* criticalExt)
{
    DECL_ASNGETDATA(dataASN, certReqASN_Length);
    int ret = 0;
    byte version = 0;
    word32 idx;

    CALLOC_ASNGETDATA(dataASN, certReqASN_Length, ret, cert->heap);

    if (ret == 0) {
        /* Default version is 0. */
        version = 0;

        /* Set version var and OID types to expect. */
        GetASN_Int8Bit(&dataASN[CERTREQASN_IDX_INFO_VER], &version);
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_OID],
                oidKeyType);
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_CURVEID],
                oidCurveType);
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SIGALGO_OID], oidSigType);
        /* Parse a certificate request. */
        ret = GetASN_Items(certReqASN, dataASN, certReqASN_Length, 1,
                           cert->source, &cert->srcIdx, cert->maxIdx);
    }
    /* Check version is valid/supported - can't be negative. */
    if ((ret == 0) && (version > MAX_X509_VERSION)) {
        WOLFSSL_MSG("Unexpected certificate request version");
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Set fields of certificate request. */
        cert->version = version;
        cert->signatureOID =
              dataASN[CERTREQASN_IDX_INFO_SIGALGO_OID].data.oid.sum;
        cert->keyOID =
              dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_OID].data.oid.sum;
        cert->certBegin = dataASN[CERTREQASN_IDX_INFO_SEQ].offset;

        /* Parse the subject name. */
        idx = dataASN[CERTREQASN_IDX_INFO_SUBJ_SEQ].offset;
        ret = GetCertName(cert, cert->subject, cert->subjectHash, ASN_SUBJECT,
                          cert->source, &idx,
                          dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_SEQ].offset);
    }
    if (ret == 0) {
        /* Parse the certificate request Attributes. */
        ret = DecodeCertReqAttributes(cert, criticalExt,
                GetASNItem_DataIdx(dataASN[CERTREQASN_IDX_INFO_ATTRS],
                        cert->source),
                dataASN[CERTREQASN_IDX_INFO_SIGALGO_SEQ].offset);
    }
    if (ret == 0) {
        /* Parse the certificate request's key. */
        idx = dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_SEQ].offset;
        ret = GetCertKey(cert, cert->source, &idx,
                dataASN[CERTREQASN_IDX_INFO_ATTRS].offset);
    }
    if (ret == 0) {
        /* Store references to signature. */
        cert->sigIndex = dataASN[CERTREQASN_IDX_INFO_SIGALGO_SEQ].offset;
        GetASN_GetConstRef(&dataASN[CERTREQASN_IDX_INFO_SIGNATURE],
                &cert->signature, &cert->sigLength);
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return ret;
}

#endif /* WOLFSSL_CERT_REQ */

#endif /* WOLFSSL_ASN_TEMPLATE */

int ParseCert(DecodedCert* cert, int type, int verify, void* cm)
{
    int   ret;
#if (!defined(WOLFSSL_NO_MALLOC) && !defined(NO_WOLFSSL_CM_VERIFY)) || \
    defined(WOLFSSL_DYN_CERT)
    char* ptr;
#endif

    ret = ParseCertRelative(cert, type, verify, cm, NULL);
    if (ret < 0)
        return ret;

#if (!defined(WOLFSSL_NO_MALLOC) && !defined(NO_WOLFSSL_CM_VERIFY)) || \
    defined(WOLFSSL_DYN_CERT)
    /* cert->subjectCN not stored as copy of WOLFSSL_NO_MALLOC defined */
    if (cert->subjectCNLen > 0) {
        ptr = (char*)XMALLOC((size_t)cert->subjectCNLen + 1, cert->heap,
                              DYNAMIC_TYPE_SUBJECT_CN);
        if (ptr == NULL)
            return MEMORY_E;
        XMEMCPY(ptr, cert->subjectCN, (size_t)cert->subjectCNLen);
        ptr[cert->subjectCNLen] = '\0';
        cert->subjectCN = ptr;
        cert->subjectCNStored = 1;
    }
#endif

#if (!defined(WOLFSSL_NO_MALLOC) && !defined(NO_WOLFSSL_CM_VERIFY)) || \
    defined(WOLFSSL_DYN_CERT)
    /* cert->publicKey not stored as copy if WOLFSSL_NO_MALLOC defined */
    if ((cert->keyOID == RSAk
    #ifdef WC_RSA_PSS
         || cert->keyOID == RSAPSSk
    #endif
         ) && cert->publicKey != NULL && cert->pubKeySize > 0) {
        ptr = (char*)XMALLOC(cert->pubKeySize, cert->heap,
                              DYNAMIC_TYPE_PUBLIC_KEY);
        if (ptr == NULL)
            return MEMORY_E;
        XMEMCPY(ptr, cert->publicKey, cert->pubKeySize);
        cert->publicKey = (byte *)ptr;
        cert->pubKeyStored = 1;
    }
#endif

    return ret;
}

int wc_ParseCert(DecodedCert* cert, int type, int verify, void* cm)
{
    return ParseCert(cert, type, verify, cm);
}

int wc_GetDecodedCertSubject(const struct DecodedCert* cert, char* buf,
                             word32* bufSz)
{
    word32 sz;

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

    sz = (word32)XSTRLEN(cert->subject);

    if (buf == NULL) {
        *bufSz = sz;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    if (*bufSz < sz)
        return BUFFER_E;

    XMEMCPY(buf, cert->subject, sz);
    *bufSz = sz;
    return 0;
}

int wc_GetDecodedCertIssuer(const struct DecodedCert* cert, char* buf,
                            word32* bufSz)
{
    word32 sz;

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

    sz = (word32)XSTRLEN(cert->issuer);

    if (buf == NULL) {
        *bufSz = sz;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    if (*bufSz < sz)
        return BUFFER_E;

    XMEMCPY(buf, cert->issuer, sz);
    *bufSz = sz;
    return 0;
}

int wc_GetDecodedCertSerial(const struct DecodedCert* cert, byte* buf,
                            word32* bufSz)
{
    if (cert == NULL || bufSz == NULL)
        return BAD_FUNC_ARG;

    if (buf == NULL) {
        *bufSz = (word32)cert->serialSz;
        return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    if (*bufSz < (word32)cert->serialSz)
        return BUFFER_E;

    XMEMCPY(buf, cert->serial, (size_t)cert->serialSz);
    *bufSz = (word32)cert->serialSz;
    return 0;
}

#ifdef WOLFCRYPT_ONLY

/* dummy functions, not using wolfSSL so don't need actual ones */
Signer* GetCA(void* signers, byte* hash);
Signer* GetCA(void* signers, byte* hash)
{
    (void)hash;

    return (Signer*)signers;
}

#ifndef NO_SKID
Signer* GetCAByName(void* signers, byte* hash);
Signer* GetCAByName(void* signers, byte* hash)
{
    (void)hash;

    return (Signer*)signers;
}
#endif /* NO_SKID */

#ifdef WOLFSSL_AKID_NAME
Signer* GetCAByAKID(void* vp, const byte* issuer, word32 issuerSz,
        const byte* serial, word32 serialSz);
Signer* GetCAByAKID(void* vp, const byte* issuer, word32 issuerSz,
        const byte* serial, word32 serialSz)
{
    (void)issuer;
    (void)issuerSz;
    (void)serial;
    (void)serialSz;

    return (Signer*)vp;
}
#endif

#endif /* WOLFCRYPT_ONLY */

#if defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY) && !defined(NO_SKID)
static Signer* GetCABySubjectAndPubKey(DecodedCert* cert, void* cm)
{
    Signer* ca = NULL;
    if (cert->extSubjKeyIdSet)
        ca = GetCA(cm, cert->extSubjKeyId);
    if (ca == NULL)
        ca = GetCAByName(cm, cert->subjectHash);
    if (ca) {
        if ((ca->pubKeySize == cert->pubKeySize) &&
               (XMEMCMP(ca->publicKey, cert->publicKey, ca->pubKeySize) == 0)) {
            return ca;
        }
    }
    return NULL;
}
#endif

#if defined(WOLFSSL_SMALL_CERT_VERIFY) || defined(OPENSSL_EXTRA)
#ifdef WOLFSSL_ASN_TEMPLATE
/* Get the Hash of the Authority Key Identifier from the list of extensions.
 *
 * @param [in]  input   Input data.
 * @param [in]  maxIdx  Maximum index for data.
 * @param [in]  sigOID  Signature OID for determining hash algorithm.
 * @param [out] hash    Hash of AKI.
 * @param [out] set     Whether the hash buffer was set.
 * @param [in]  heap    Dynamic memory allocation hint.
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  MEMORY_E on dynamic memory allocation failure.
 */
static int GetAKIHash(const byte* input, word32 maxIdx, word32 sigOID,
                      byte* hash, int* set, void* heap)
{
    /* AKI and Certificate Extension ASN.1 templates are the same length. */
    DECL_ASNGETDATA(dataASN, certExtASN_Length);
    int ret = 0;
    word32 idx = 0;
    word32 extEndIdx;
    const byte* extData;
    word32 extDataSz;
    byte critical;

    ALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, heap);
    (void)heap;

    extEndIdx = idx + maxIdx;

    /* Step through each extension looking for AKI. */
    while ((ret == 0) && (idx < extEndIdx)) {
        /* Clear dynamic data and check for certificate extension type OIDs. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
        GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidCertExtType);
        /* Set criticality variable. */
        GetASN_Int8Bit(&dataASN[CERTEXTASN_IDX_CRIT], &critical);
        /* Parse an extension. */
        ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0, input,
                &idx, extEndIdx);
        if (ret == 0) {
            /* Get reference to extension data and move index on past this
             * extension. */
            GetASN_GetRef(&dataASN[CERTEXTASN_IDX_VAL], &extData, &extDataSz);
            idx += extDataSz;

            /* Check whether we have the AKI extension. */
            if (dataASN[CERTEXTASN_IDX_OID].data.oid.sum == AUTH_KEY_OID) {
                /* Clear dynamic data. */
                XMEMSET(dataASN, 0, sizeof(*dataASN) * authKeyIdASN_Length);
                /* Start parsing extension data from the start. */
                idx = 0;
                /* Parse AKI extension data. */
                ret = GetASN_Items(authKeyIdASN, dataASN, authKeyIdASN_Length,
                        1, extData, &idx, extDataSz);
                if ((ret == 0) &&
                        (dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.data
                                != NULL)) {
                    /* We parsed successfully and have data. */
                    *set = 1;
                    /* Get the hash or hash of the hash if wrong size. */
                    ret = GetHashId(
                        dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.data,
                        (int)dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.length,
                        hash, HashIdAlg(sigOID));
                }
                break;
            }
        }
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif

/* Only quick step through the certificate to find fields that are then used
 * in certificate signature verification.
 * Must use the signature OID from the signed part of the certificate.
 * Works also on certificate signing requests.
 *
 * This is only for minimizing dynamic memory usage during TLS certificate
 * chain processing.
 * Doesn't support:
 *   OCSP Only: alt lookup using subject and pub key w/o sig check
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int CheckCertSignature_ex(const byte* cert, word32 certSz, void* heap,
        void* cm, const byte* pubKey, word32 pubKeySz, int pubKeyOID, int req)
{
    /* X509 ASN.1 template longer than Certificate Request template. */
    DECL_ASNGETDATA(dataASN, x509CertASN_Length);
    WC_DECLARE_VAR(sigCtx, SignatureCtx, 1, 0);
    byte hash[KEYID_SIZE];
    Signer* ca = NULL;
    int ret = 0;
    word32 idx = 0;
#ifndef NO_SKID
    int extAuthKeyIdSet = 0;
#endif
    const byte* tbs = NULL;
    word32 tbsSz = 0;
#ifdef WC_RSA_PSS
    const byte* tbsParams = NULL;
    word32 tbsParamsSz = 0;
#endif
    const byte* sig = NULL;
    word32 sigSz = 0;
    word32 sigOID = 0;
    const byte* sigParams = NULL;
    word32 sigParamsSz = 0;
    const byte* caName = NULL;
    word32 caNameLen = 0;
#ifndef NO_SKID
    const byte* akiData = NULL;
    word32 akiLen = 0;
#endif

    (void)req;
    (void)heap;

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

    CALLOC_ASNGETDATA(dataASN, x509CertASN_Length, ret, heap);

    if ((ret == 0) && (!req)) {
        /* Set OID types expected for signature and public key. */
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_ALGOID_OID], oidSigType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_OID],
                oidKeyType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_CURVEID],
                oidCurveType);
        GetASN_OID(&dataASN[X509CERTASN_IDX_SIGALGO_OID], oidSigType);
        /* Parse certificate. */
        ret = GetASN_Items(x509CertASN, dataASN, x509CertASN_Length, 1, cert,
                           &idx, certSz);

        /* Check signature OIDs match. */
        if ((ret == 0) && dataASN[X509CERTASN_IDX_TBS_ALGOID_OID].data.oid.sum
                != dataASN[X509CERTASN_IDX_SIGALGO_OID].data.oid.sum) {
            ret = ASN_SIG_OID_E;
        }
        /* Store the data for verification in the certificate. */
        if (ret == 0) {
            tbs = GetASNItem_Addr(dataASN[X509CERTASN_IDX_TBS_SEQ], cert);
            tbsSz = GetASNItem_Length(dataASN[X509CERTASN_IDX_TBS_SEQ], cert);
            caName = GetASNItem_Addr(dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ],
                    cert);
            caNameLen = GetASNItem_Length(dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ],
                    cert);
            sigOID = dataASN[X509CERTASN_IDX_SIGALGO_OID].data.oid.sum;
        #ifdef WC_RSA_PSS
            if (dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS].tag != 0) {
                tbsParams =
                    GetASNItem_Addr(dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS],
                        cert);
                tbsParamsSz =
                    GetASNItem_Length(dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS],
                        cert);
            }
            if (dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].tag != 0) {
                sigParams =
                    GetASNItem_Addr(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert);
                sigParamsSz =
                    GetASNItem_Length(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert);
            }
        #endif
            GetASN_GetConstRef(&dataASN[X509CERTASN_IDX_SIGNATURE], &sig, &sigSz);
        #ifdef WC_RSA_PSS
            if (tbsParamsSz != sigParamsSz) {
                ret = ASN_PARSE_E;
            }
            else if ((tbsParamsSz > 0) && (sigOID != CTC_RSASSAPSS)) {
                ret = ASN_PARSE_E;
            }
            else if ((tbsParamsSz > 0) &&
                     (XMEMCMP(tbsParams, sigParams, tbsParamsSz) != 0)) {
                ret = ASN_PARSE_E;
            }
        #endif
        }
    }
    else if (ret == 0) {
#ifndef WOLFSSL_CERT_REQ
        ret = NOT_COMPILED_IN;
#else
        /* Set OID types expected for signature and public key. */
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_OID],
                oidKeyType);
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SPUBKEYINFO_ALGOID_CURVEID],
                oidCurveType);
        GetASN_OID(&dataASN[CERTREQASN_IDX_INFO_SIGALGO_OID], oidSigType);
        /* Parse certificate request. */
        ret = GetASN_Items(certReqASN, dataASN, certReqASN_Length, 1, cert,
                           &idx, certSz);

        if (ret == 0) {
            /* Store the data for verification in the certificate. */
            tbs = GetASNItem_Addr(dataASN[CERTREQASN_IDX_INFO_SEQ], cert);
            tbsSz = GetASNItem_Length(dataASN[CERTREQASN_IDX_INFO_SEQ], cert);
            caName = GetASNItem_Addr(
                    dataASN[CERTREQASN_IDX_INFO_SUBJ_SEQ], cert);
            caNameLen = GetASNItem_Length(
                    dataASN[CERTREQASN_IDX_INFO_SUBJ_SEQ], cert);
            sigOID = dataASN[CERTREQASN_IDX_INFO_SIGALGO_OID].data.oid.sum;
        #ifdef WC_RSA_PSS
            if (GetASNItem_HaveData(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS])) {
                sigParams =
                    GetASNItem_Addr(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert);
                sigParamsSz =
                    GetASNItem_Length(dataASN[X509CERTASN_IDX_SIGALGO_PARAMS],
                        cert);
            }
        #endif
            GetASN_GetConstRef(&dataASN[CERTREQASN_IDX_INFO_SIGNATURE], &sig,
                    &sigSz);
        }
#endif
    }

#ifndef NO_SKID
    if ((ret == 0) && (pubKey == NULL) && !req) {
        akiData = dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].data.ref.data;
        akiLen = dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].data.ref.length;
    }
#endif

    FREE_ASNGETDATA(dataASN, heap);

    /* If no public passed, then find the CA. */
    if ((ret == 0) && (pubKey == NULL)) {
#ifndef NO_SKID
        /* Find the AKI extension in list of extensions and get hash. */
        if ((!req) && (akiData != NULL)) {
            /* TODO: test case */
            ret = GetAKIHash(akiData, akiLen, sigOID, hash, &extAuthKeyIdSet,
                heap);
        }

        /* Get the CA by hash one was found. */
        if (extAuthKeyIdSet) {
            ca = GetCA(cm, hash);
        }
        if (ca == NULL)
#endif
        {
            /* Try hash of issuer name. */
            ret = CalcHashId_ex(caName, caNameLen, hash, HashIdAlg(sigOID));
            if (ret == 0) {
                ca = GetCAByName(cm, hash);
            }
        }

        if (ca != NULL) {
            /* Extract public key information. */
            pubKey = ca->publicKey;
            pubKeySz = ca->pubKeySize;
            pubKeyOID = (int)ca->keyOID;
        }
        else {
            /* No public key to verify with. */
            ret = ASN_NO_SIGNER_E;
        }
    }

    if (ret == 0) {
        WC_ALLOC_VAR_EX(sigCtx, SignatureCtx, 1, heap, DYNAMIC_TYPE_SIGNATURE,
            ret=MEMORY_E);
        if (WC_VAR_OK(sigCtx))
        {
            InitSignatureCtx(sigCtx, heap, INVALID_DEVID);

            /* Check signature. */
            ret = ConfirmSignature(sigCtx, tbs, tbsSz, pubKey, pubKeySz,
                (word32)pubKeyOID, sig, sigSz, sigOID, sigParams, sigParamsSz,
                NULL);
            if (ret != 0) {
                WOLFSSL_MSG("Confirm signature failed");
            }

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

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Call CheckCertSignature_ex using a public key buffer for verification */
int CheckCertSignaturePubKey(const byte* cert, word32 certSz, void* heap,
        const byte* pubKey, word32 pubKeySz, int pubKeyOID)
{
    return CheckCertSignature_ex(cert, certSz, heap, NULL,
            pubKey, pubKeySz, pubKeyOID, 0);
}

/* Call CheckCertSignature_ex using a public key and oid */
int wc_CheckCertSigPubKey(const byte* cert, word32 certSz, void* heap,
        const byte* pubKey, word32 pubKeySz, int pubKeyOID)
{
        return CheckCertSignaturePubKey(cert, certSz, heap, pubKey, pubKeySz,
                                        pubKeyOID);
}

#ifdef WOLFSSL_CERT_REQ
int CheckCSRSignaturePubKey(const byte* cert, word32 certSz, void* heap,
        const byte* pubKey, word32 pubKeySz, int pubKeyOID)
{
    return CheckCertSignature_ex(cert, certSz, heap, NULL,
            pubKey, pubKeySz, pubKeyOID, 1);
}
#endif /* WOLFSSL_CERT_REQ */

/* Call CheckCertSignature_ex using a certificate manager (cm) */
int wc_CheckCertSignature(const byte* cert, word32 certSz, void* heap, void* cm)
{
    return CheckCertSignature_ex(cert, certSz, heap, cm, NULL, 0, 0, 0);
}
#endif /* WOLFSSL_SMALL_CERT_VERIFY || OPENSSL_EXTRA */

#if (defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) || \
    (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)))
/* ASN.1 DER decode instruction. */
typedef struct DecodeInstr {
    /* Tag expected. */
    byte tag;
    /* Operation to perform: step in or go over */
    WC_BITFIELD op:1;
    /* ASN.1 item is optional. */
    WC_BITFIELD optional:1;
} DecodeInstr;

/* Step into ASN.1 item. */
#define DECODE_INSTR_IN    0
/* Step over ASN.1 item. */
#define DECODE_INSTR_OVER  1

/* Get the public key data from the DER encoded X.509 certificate.
 *
 * Assumes data has previously been parsed for complete validity.
 *
 * @param [in]  cert      DER encoded X.509 certificate data.
 * @param [in]  certSz    Length of DER encoding.
 * @param [out] pubKey    Public key data. (From the BIT_STRING.)
 * @param [out] pubKeySz  Length of public key data in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when cert, pubKey or pubKeySz is NULL.
 * @return  ASN_PARSE_E when certificate encoding is invalid.
 */
int wc_CertGetPubKey(const byte* cert, word32 certSz,
    const unsigned char** pubKey, word32* pubKeySz)
{
    int ret = 0;
    int l = 0;
    word32 o = 0;
    int i;
    static DecodeInstr ops[] = {
        /* Outer SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_IN  , 0 },
        /* TBSCertificate: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_IN  , 0 },
        /* Version: [0] */
        { ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_X509_CERT_VERSION,
          DECODE_INSTR_OVER, 1 },
        /* CertificateSerialNumber: INT  */
        { ASN_INTEGER,                    DECODE_INSTR_OVER, 0 },
        /* AlgorithmIdentifier: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_OVER, 0 },
        /* issuer: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_OVER, 0 },
        /* Validity: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_OVER, 0 },
        /* subject: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_OVER, 0 },
        /* subjectPublicKeyInfo SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_IN  , 0 },
        /* AlgorithmIdentifier: SEQ */
        { ASN_SEQUENCE | ASN_CONSTRUCTED, DECODE_INSTR_OVER, 0 },
        /* PublicKey: BIT_STRING  */
        { ASN_BIT_STRING,                 DECODE_INSTR_IN  , 0 },
    };

    /* Validate parameters. */
    if ((cert == NULL) || (pubKey == NULL) || (pubKeySz == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    /* Process each instruction to take us to public key data. */
    for (i = 0; (ret == 0) && (i < (int)(sizeof(ops) / sizeof(*ops))); i++) {
        DecodeInstr op = ops[i];

        /* Check the current ASN.1 item has the expected tag. */
        if (cert[o] != op.tag) {
            /* If not optional then error, otherwise skip op. */
            if (!op.optional) {
                ret = ASN_PARSE_E;
            }
        }
        else {
            /* Move past tag. */
            o++;
            /* Get the length of ASN.1 item and move past length encoding. */
            if (GetLength(cert, &o, &l, certSz) < 0) {
                ret = ASN_PARSE_E;
            }
            /* Skip data if required. */
            else if (op.op == DECODE_INSTR_OVER) {
                o += (word32)l;
            }
        }
    }

    if (ret == 0) {
        /* Return the public key data and length.
         * Skip first byte of BIT_STRING data: unused bits. */
        *pubKey = cert + o + 1;
        *pubKeySz = (word32)(l - 1);
    }

    return ret;
}
#endif

/*
 * @brief Export the SubjectPublicKeyInfo from an X.509 certificate
 *
 * This function extracts the SubjectPublicKeyInfo (SPKI) section from an X.509
 * certificate in DER format. The SPKI contains the public key algorithm and
 * the public key itself.
 *
 * @param certDer      [in]  Pointer to the DER encoded certificate
 * @param certSz       [in]  Size of the DER encoded certificate
 * @param pubKeyDer    [out] Buffer to hold the extracted SPKI (can be NULL to
 *                           get size)
 * @param pubKeyDerSz  [in,out] On input, size of pubKeyDer buffer
 *                              On output, actual size of the SPKI
 *
 * @return 0 on success, negative on error
 * @return BAD_FUNC_ARG if certDer is NULL, certSz is 0, or pubKeyDerSz is NULL
 * @return BUFFER_E if the provided buffer is too small
 */
int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer,
                                                   word32 certDerSz,
                                                   byte* pubKeyDer,
                                                   word32* pubKeyDerSz)
{
    WC_DECLARE_VAR(cert, DecodedCert, 1, 0);
    int         ret;
    word32      startIdx;
    word32      idx;
    word32      length;
    int         badDate;

    if (certDer == NULL || certDerSz == 0 || pubKeyDerSz == NULL) {
        return BAD_FUNC_ARG;
    }

    WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    length = 0;
    badDate = 0;

    wc_InitDecodedCert(cert, certDer, certDerSz, NULL);

    /* Parse up to the SubjectPublicKeyInfo */
    ret = wc_GetPubX509(cert, 0, &badDate);
    if (ret >= 0) {
        /* Save the starting index of SubjectPublicKeyInfo */
        startIdx = cert->srcIdx;

        /* Get the length of the SubjectPublicKeyInfo sequence */
        idx = startIdx;
        ret = GetSequence(certDer, &idx, (int*)&length, certDerSz);
        if (ret >= 0) {
            /* Calculate total length including sequence header */
            length += (idx - startIdx);

            /* Copy the SubjectPublicKeyInfo if buffer provided */
            if (pubKeyDer != NULL) {
                if (*pubKeyDerSz < (word32)length) {
                    ret = BUFFER_E;
                }
                else {
                    XMEMCPY(pubKeyDer, &certDer[startIdx], length);
                }
            }
        }
    }

    if (ret >= 0) {
        ret = 0;
    }

    *pubKeyDerSz = length;
    wc_FreeDecodedCert(cert);

    WC_FREE_VAR_EX(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    return ret;
}


#ifdef HAVE_OCSP
Signer* findSignerByKeyHash(Signer *list, byte *hash)
{
    Signer *s;
    for (s = list; s != NULL; s = s->next) {
        if (XMEMCMP(s->subjectKeyHash, hash, KEYID_SIZE) == 0) {
            return s;
        }
    }
    return NULL;
}
#endif /* WOLFSSL_OCSP */

Signer* findSignerByName(Signer *list, byte *hash)
{
    Signer *s;
    for (s = list; s != NULL; s = s->next) {
        if (XMEMCMP(s->subjectNameHash, hash, SIGNER_DIGEST_SIZE) == 0) {
            return s;
        }
    }
    return NULL;
}

int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
                      Signer *extraCAList)
{
    int    ret = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 confirmOID = 0;
#ifdef WOLFSSL_CERT_REQ
    int    len = 0;
#endif
#endif
#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    int    idx = 0;
#endif
    byte*  sce_tsip_encRsaKeyIdx;
    (void)extraCAList;

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

#ifdef WOLFSSL_CERT_REQ
    if (type == CERTREQ_TYPE)
        cert->isCSR = 1;
#endif

    if (cert->sigCtx.state == SIG_STATE_BEGIN) {
#ifndef WOLFSSL_ASN_TEMPLATE
        cert->badDate = 0;
        cert->criticalExt = 0;
        if ((ret = DecodeToKey(cert, verify)) < 0) {
            if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) ||
                ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) {
                cert->badDate = ret;
                if ((verify == VERIFY_SKIP_DATE) || AsnSkipDateCheck)
                    ret = 0;
            }
            else
                return ret;
        }

        WOLFSSL_MSG("Parsed Past Key");
#if defined(HAVE_RPK)
        if (cert->isRPK) {
            return ret;
        }
#endif /* HAVE_RPK */

#ifdef WOLFSSL_CERT_REQ
        /* Read attributes */
        if (cert->isCSR) {
            if (GetASNHeader_ex(cert->source,
                    ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED, &cert->srcIdx,
                    &len, cert->maxIdx, 1) < 0) {
                WOLFSSL_MSG("GetASNHeader_ex error");
                return ASN_PARSE_E;
            }

            if (len) {
                word32 attrMaxIdx = cert->srcIdx + (word32)len;
                word32 oid;
                byte   tag;

                if (attrMaxIdx > cert->maxIdx) {
                    WOLFSSL_MSG("Attribute length greater than CSR length");
                    return ASN_PARSE_E;
                }

                while (cert->srcIdx < attrMaxIdx) {
                    /* Attributes have the structure:
                     * SEQ -> OID -> SET -> ATTRIBUTE */
                    if (GetSequence(cert->source, &cert->srcIdx, &len,
                            attrMaxIdx) < 0) {
                        WOLFSSL_MSG("attr GetSequence error");
                        return ASN_PARSE_E;
                    }
                    if (GetObjectId(cert->source, &cert->srcIdx, &oid,
                            oidCsrAttrType, attrMaxIdx) < 0) {
                        WOLFSSL_MSG("attr GetObjectId error");
                        return ASN_PARSE_E;
                    }
                    if (GetSet(cert->source, &cert->srcIdx, &len,
                            attrMaxIdx) < 0) {
                        WOLFSSL_MSG("attr GetSet error");
                        return ASN_PARSE_E;
                    }
                    switch (oid) {
                    case PKCS9_CONTENT_TYPE_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        if (tag != ASN_PRINTABLE_STRING && tag != ASN_UTF8STRING &&
                                tag != ASN_IA5_STRING) {
                            WOLFSSL_MSG("Unsupported attribute value format");
                            return ASN_PARSE_E;
                        }
                        cert->contentType = (char*)cert->source + cert->srcIdx;
                        cert->contentTypeLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case CHALLENGE_PASSWORD_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        if (tag != ASN_PRINTABLE_STRING && tag != ASN_UTF8STRING &&
                                tag != ASN_IA5_STRING) {
                            WOLFSSL_MSG("Unsupported attribute value format");
                            return ASN_PARSE_E;
                        }
                        cert->cPwd = (char*)cert->source + cert->srcIdx;
                        cert->cPwdLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case SERIAL_NUMBER_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        if (tag != ASN_PRINTABLE_STRING && tag != ASN_UTF8STRING &&
                                tag != ASN_IA5_STRING) {
                            WOLFSSL_MSG("Unsupported attribute value format");
                            return ASN_PARSE_E;
                        }
                        cert->sNum = (char*)cert->source + cert->srcIdx;
                        cert->sNumLen = len;
                        cert->srcIdx += (word32)len;
                        if (cert->sNumLen <= EXTERNAL_SERIAL_SIZE) {
                            XMEMCPY(cert->serial, cert->sNum,
                                    (size_t)cert->sNumLen);
                            cert->serialSz = cert->sNumLen;
                        }
                        break;
                    case DNQUALIFIER_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        cert->dnQualifier = (char*)cert->source + cert->srcIdx;
                        cert->dnQualifierLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case INITIALS_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        cert->initials = (char*)cert->source + cert->srcIdx;
                        cert->initialsLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case SURNAME_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        cert->surname = (char*)cert->source + cert->srcIdx;
                        cert->surnameLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case GIVEN_NAME_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        cert->givenName = (char*)cert->source + cert->srcIdx;
                        cert->givenNameLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case UNSTRUCTURED_NAME_OID:
                        if (GetHeader(cert->source, &tag,
                                &cert->srcIdx, &len, attrMaxIdx, 1) < 0) {
                            WOLFSSL_MSG("attr GetHeader error");
                            return ASN_PARSE_E;
                        }
                        cert->unstructuredName =
                            (char*)cert->source + cert->srcIdx;
                        cert->unstructuredNameLen = len;
                        cert->srcIdx += (word32)len;
                        break;
                    case EXTENSION_REQUEST_OID:
                        /* save extensions */
                        cert->extensions    = &cert->source[cert->srcIdx];
                        cert->extensionsSz  = len;
                        cert->extensionsIdx = cert->srcIdx; /* for potential later use */

                        if ((ret = DecodeCertExtensions(cert)) < 0) {
                            if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
                                cert->criticalExt = ret;
                            }
                            else {
                                return ret;
                            }
                        }
                        cert->srcIdx += (word32)len;
                        break;
                    default:
                        WOLFSSL_MSG("Unsupported attribute type");
                        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
                        return ASN_PARSE_E;
                    }
                }
            }
        }
#endif

        if (cert->srcIdx < cert->sigIndex) {
        #ifndef ALLOW_V1_EXTENSIONS
            if (cert->version < 2) {
                WOLFSSL_MSG("\tv1 and v2 certs not allowed extensions");
                WOLFSSL_ERROR_VERBOSE(ASN_VERSION_E);
                return ASN_VERSION_E;
            }
        #endif

            /* save extensions */
            cert->extensions    = &cert->source[cert->srcIdx];
            cert->extensionsSz  = (int)(cert->sigIndex - cert->srcIdx);
            cert->extensionsIdx = cert->srcIdx;   /* for potential later use */

            if ((ret = DecodeCertExtensions(cert)) < 0) {
                if (ret == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E))
                    cert->criticalExt = ret;
                else
                    return ret;
            }

        #ifdef HAVE_OCSP
            if (verify == VERIFY_OCSP_CERT) {
                /* trust for the lifetime of the responder's cert*/
                if (cert->ocspNoCheckSet)
                    verify = VERIFY;
                else
                    verify = VERIFY_OCSP;
            }
        #endif
            /* advance past extensions */
            cert->srcIdx = cert->sigIndex;
        }

        if ((ret = GetSigAlg(cert,
#ifdef WOLFSSL_CERT_REQ
                !cert->isCSR ? &confirmOID : &cert->signatureOID,
#else
                &confirmOID,
#endif
                cert->maxIdx)) < 0) {
            return ret;
        }

        if ((ret = GetSignature(cert)) < 0) {
            return ret;
        }

        if (confirmOID != cert->signatureOID
#ifdef WOLFSSL_CERT_REQ
                && !cert->isCSR
#endif
                ) {
            WOLFSSL_ERROR_VERBOSE(ASN_SIG_OID_E);
            return ASN_SIG_OID_E;
        }
#else
#ifdef WOLFSSL_CERT_REQ
        if (cert->isCSR) {
            ret = DecodeCertReq(cert, &cert->criticalExt);
            if (ret < 0) {
                return ret;
            }
        }
        else
#endif
        {
            ret = DecodeCert(cert, verify, &cert->criticalExt);
            if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) ||
                ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) {
                cert->badDate = ret;
                if ((verify == VERIFY_SKIP_DATE) || AsnSkipDateCheck)
                    ret = 0;
            }
            else if (ret < 0) {
                WOLFSSL_ERROR_VERBOSE(ret);
                return ret;
            }
#if defined(HAVE_RPK)
            if (cert->isRPK) {
                return ret;
            }
#endif /* HAVE_RPK */
        }
#endif

#if !defined(WOLFSSL_NO_ASN_STRICT) && !defined(WOLFSSL_PYTHON) && \
    !defined(WOLFSSL_ASN_ALLOW_0_SERIAL)
        /* RFC 5280 section 4.1.2.2 requires conforming CAs to issue
         * positive serial numbers; the same section notes that verifiers
         * SHOULD gracefully handle non-conforming certs with zero or
         * negative serials. wolfSSL's policy is to reject as a security
         * guard, with an exemption for self-signed CA certs loaded as
         * explicitly-trusted anchors (some legacy real-world roots have
         * serial 0).
         *
         * Note: cert->selfSigned is a subject/issuer name-hash compare
         * (see DecodeCertInternal where it's set), not a validated
         * self-signature. That is acceptable here because the trust
         * decision is user-driven via CertManagerLoadCA; this check is
         * only a structural sanity guard. */
        if ((ret == 0) && (cert->serialSz == 1) && (cert->serial[0] == 0)) {
            int isTrustAnchorLoad =
                (type == CA_TYPE || type == TRUSTED_PEER_TYPE)
                && cert->isCA && cert->selfSigned;
            int isCsr = 0;
        #ifdef WOLFSSL_CERT_REQ
            isCsr = cert->isCSR;
        #endif
            if (!isTrustAnchorLoad && !isCsr) {
                WOLFSSL_MSG("Error serial number of 0 for non-root certificate");
                return ASN_PARSE_E;
            }
        }
#endif

    #ifndef ALLOW_INVALID_CERTSIGN
        /* https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
         *   If the cA boolean is not asserted, then the keyCertSign bit in the
         *   key usage extension MUST NOT be asserted. */
        if (!cert->isCA && cert->extKeyUsageSet &&
                (cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) != 0
        #ifdef ALLOW_SELFSIGNED_INVALID_CERTSIGN
                && !cert->selfSigned
        #endif
        ) {
            WOLFSSL_ERROR_VERBOSE(KEYUSAGE_E);
            return KEYUSAGE_E;
        }
    #endif

    #ifndef NO_SKID
        if (cert->extSubjKeyIdSet == 0 && cert->publicKey != NULL &&
                                                         cert->pubKeySize > 0) {
            if (cert->signatureOID == CTC_SM3wSM2) {
                if (cert->pubKeySize < 65) {
                    WOLFSSL_ERROR_VERBOSE(BUFFER_E);
                    return BUFFER_E;
                }
                /* TODO: GmSSL creates IDs this way but whole public key info
                 * block should be hashed. */
                ret = CalcHashId_ex(cert->publicKey + cert->pubKeySize - 65, 65,
                    cert->extSubjKeyId, HashIdAlg(cert->signatureOID));
            }
            else {
                ret = CalcHashId_ex(cert->publicKey, cert->pubKeySize,
                    cert->extSubjKeyId, HashIdAlg(cert->signatureOID));
            }
            if (ret != 0) {
                WOLFSSL_ERROR_VERBOSE(ret);
                return ret;
            }
        }
    #endif /* !NO_SKID */

        if (!cert->selfSigned || (verify != NO_VERIFY && type != CA_TYPE &&
                                                   type != TRUSTED_PEER_TYPE)) {
            cert->ca = NULL;
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
        if (extraCAList != NULL) {
            cert->ca = findSignerByName(extraCAList, cert->issuerHash);
        }
#endif
    #ifndef NO_SKID
            if (cert->ca == NULL && cert->extAuthKeyIdSet) {
                cert->ca = GetCA(cm, cert->extAuthKeyId);
        #ifdef WOLFSSL_AKID_NAME
                if (cert->ca == NULL) {
                    cert->ca = GetCAByAKID(cm, cert->extAuthKeyIdIssuer,
                        cert->extAuthKeyIdIssuerSz, cert->extAuthKeyIdIssuerSN,
                        cert->extAuthKeyIdIssuerSNSz);
                }
        #endif
            }
            if (cert->ca == NULL && cert->extSubjKeyIdSet
                                 && verify != VERIFY_OCSP) {
                cert->ca = GetCA(cm, cert->extSubjKeyId);
            }
            if (cert->ca != NULL && XMEMCMP(cert->issuerHash,
                    cert->ca->subjectNameHash, KEYID_SIZE) != 0) {
            #ifdef WOLFSSL_ALLOW_AKID_SKID_MATCH
                /* if hash of cert subject does not match hash of issuer
                 * then try with AKID/SKID if available */
                if (cert->extAuthKeyIdSet && cert->extAuthKeyIdSz > 0 &&
                    cert->extAuthKeyIdSz ==
                        (word32)sizeof(cert->ca->subjectKeyIdHash) &&
                    XMEMCMP(cert->extAuthKeyId, cert->ca->subjectKeyIdHash,
                            cert->extAuthKeyIdSz) == 0) {
                    WOLFSSL_MSG("Cert AKID matches CA SKID");
                }
                else
            #endif
                {
                    WOLFSSL_MSG("Cert subject hash does not match issuer hash");
                    cert->ca = NULL;
                }
            }
            if (cert->ca == NULL) {
                cert->ca = GetCAByName(cm, cert->issuerHash);
                /* If AKID is available then this CA doesn't have the public
                 * key required */
                if (cert->ca && cert->extAuthKeyIdSet) {
                    WOLFSSL_MSG("CA SKID doesn't match AKID");
                    cert->ca = NULL;
                }
            }

            /* OCSP Only: alt lookup using subject and pub key w/o sig check */
        #ifdef WOLFSSL_NO_TRUSTED_CERTS_VERIFY
            if (cert->ca == NULL && verify == VERIFY_OCSP) {
                cert->ca = GetCABySubjectAndPubKey(cert, cm);
                if (cert->ca) {
                    ret = 0; /* success */
                    goto exit_pcr;
                }
            }
        #endif /* WOLFSSL_NO_TRUSTED_CERTS_VERIFY */
    #else
            cert->ca = GetCA(cm, cert->issuerHash);
    #endif /* !NO_SKID */

            if (cert->ca) {
                WOLFSSL_MSG("CA found");
            }
        }

        /* Set to WOLFSSL_MAX_PATH_LEN by default in InitDecodedCert_ex */
        if (cert->pathLengthSet)
            cert->maxPathLen = cert->pathLength;

        /* RFC 5280 6.1.4: Check issuer's pathLen constraint.
         * Need to perform a pathlen check on anything that will be used
         * to sign certificates later on. Otherwise, pathLen doesn't
         * mean anything.
         * Nothing to check if we don't have the issuer of this cert.
         *
         * Per RFC 5280, when the KeyUsage extension is absent, all key
         * uses are implicitly valid (including keyCertSign), so pathLen
         * enforcement must not be gated on KeyUsage presence. */
        if (type != CERT_TYPE && cert->isCA && cert->ca &&
            (!cert->extKeyUsageSet ||
             (cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) != 0)) {
            if (!cert->selfSigned) {
                /* RFC 5280 6.1.4(l): Non-self-issued cert decrements and
                 * checks the issuer's max_path_length. */
                if (cert->ca->maxPathLen == 0) {
                    /* This cert CAN NOT be used as an intermediate cert.
                     * The issuer does not allow it. */
                    cert->maxPathLen = 0;
                    if (verify != NO_VERIFY) {
                        WOLFSSL_MSG("\tNon-entity cert, maxPathLen is 0");
                        WOLFSSL_MSG("\tmaxPathLen status: ERROR");
                        WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_INV_E);
                        return ASN_PATHLEN_INV_E;
                    }
                }
                else {
                    cert->maxPathLen = (word16)min(cert->ca->maxPathLen - 1U,
                                           cert->maxPathLen);
                }
            }
            else {
                /* RFC 5280 6.1.4(l): Self-issued certs do NOT decrement
                 * max_path_length, but the issuer's constraint still
                 * applies. A self-issued cert from a CA with maxPathLen=0
                 * cannot act as an intermediate CA. */
                if (cert->publicKey != NULL &&
                        cert->ca->publicKey != NULL &&
                        cert->pubKeySize > 0 &&
                        cert->pubKeySize == cert->ca->pubKeySize &&
                        XMEMCMP(cert->publicKey, cert->ca->publicKey,
                                cert->pubKeySize) == 0) {
                    /* Exclude the trust anchor itself from step (l). Per
                     * RFC 5280 6.1, when the trust anchor is supplied as a
                     * self-signed certificate it "is not included as part
                     * of the prospective certification path" */

                    /* Trust anchor: honor issuer's constraint */
                    cert->maxPathLen = (word16)min(cert->ca->maxPathLen,
                                           cert->maxPathLen);
                }
                else if (cert->ca->maxPathLen == 0) {
                    cert->maxPathLen = 0;
                    if (verify != NO_VERIFY) {
                        WOLFSSL_MSG("\tSelf-issued cert, maxPathLen is 0");
                        WOLFSSL_MSG("\tmaxPathLen status: ERROR");
                        WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_INV_E);
                        return ASN_PATHLEN_INV_E;
                    }
                }
                else {
                    /* Self-issued: honor issuer's constraint without
                     * decrementing. */
                    cert->maxPathLen = (word16)min(cert->ca->maxPathLen,
                                           cert->maxPathLen);
                }
            }
        }

        #ifdef HAVE_OCSP
        /* Needed for OCSP requests, and for binding responder authorization
         * to CertID's issuerKeyHash when this cert becomes a Signer. */
        if (cert->ca) {
            XMEMCPY(cert->issuerKeyHash, cert->ca->subjectKeyHash,
                KEYID_SIZE);
        }
        else if (cert->selfSigned) {
            XMEMCPY(cert->issuerKeyHash, cert->subjectKeyHash,
                KEYID_SIZE);
        }
        #endif /* HAVE_OCSP */
    }
#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    /* prepare for TSIP TLS cert verification API use */
    if (cert->keyOID == RSAk) {
        /* to call TSIP API, it needs keys position info in bytes */
        if ((ret = RsaPublicKeyDecodeRawIndex(cert->publicKey, (word32*)&idx,
                                   cert->pubKeySize,
                                   &cert->sigCtx.CertAtt.pubkey_n_start,
                                   &cert->sigCtx.CertAtt.pubkey_n_len,
                                   &cert->sigCtx.CertAtt.pubkey_e_start,
                                   &cert->sigCtx.CertAtt.pubkey_e_len)) != 0) {
            WOLFSSL_MSG("Decoding index from cert failed.");
            return ret;
        }
        cert->sigCtx.CertAtt.certBegin = cert->certBegin;
    }
    else if (cert->keyOID == ECDSAk) {
        cert->sigCtx.CertAtt.certBegin = cert->certBegin;
    }
    /* check if we can use TSIP for cert verification */
    /* if the ca is verified as tsip root ca.         */
    /* TSIP can only handle 2048 bits(256 byte) key.  */
    if (cert->ca && Renesas_cmn_checkCA(cert->ca->cm_idx) != 0 &&
        (cert->sigCtx.CertAtt.pubkey_n_len == 256 ||
         cert->sigCtx.CertAtt.curve_id == ECC_SECP256R1)) {

        /* assign memory to encrypted tsip Rsa key index */
        if (!cert->sce_tsip_encRsaKeyIdx)
            cert->sce_tsip_encRsaKeyIdx =
                            (byte*)XMALLOC(TSIP_TLS_ENCPUBKEY_SZ_BY_CERTVRFY,
                             cert->heap, DYNAMIC_TYPE_RSA);
        if (cert->sce_tsip_encRsaKeyIdx == NULL)
                return MEMORY_E;
    }
    else {
        if (cert->ca) {
            /* TSIP isn't usable */
            if (Renesas_cmn_checkCA(cert->ca->cm_idx) == 0)
                WOLFSSL_MSG("SCE-TSIP isn't usable because the ca isn't verified "
                            "by TSIP.");
            else if (cert->sigCtx.CertAtt.pubkey_n_len != 256)
                WOLFSSL_MSG("SCE-TSIP isn't usable because the ca isn't signed by "
                            "RSA 2048.");
            else
                WOLFSSL_MSG("SCE-TSIP isn't usable");
        }
        cert->sce_tsip_encRsaKeyIdx = NULL;
    }

    sce_tsip_encRsaKeyIdx = cert->sce_tsip_encRsaKeyIdx;

#else
    sce_tsip_encRsaKeyIdx = NULL;
#endif

    if (verify != NO_VERIFY && type != CA_TYPE && type != TRUSTED_PEER_TYPE) {
        if (cert->ca) {
            if (verify == VERIFY || verify == VERIFY_OCSP ||
                                                 verify == VERIFY_SKIP_DATE) {
                word32 keyOID = cert->ca->keyOID;
            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                if (cert->selfSigned && (cert->signatureOID == CTC_SM3wSM2)) {
                    keyOID = SM2k;
                }
            #endif
                /* try to confirm/verify signature */
                if ((ret = ConfirmSignature(&cert->sigCtx,
                        cert->source + cert->certBegin,
                        cert->sigIndex - cert->certBegin,
                        cert->ca->publicKey, cert->ca->pubKeySize,
                        keyOID, cert->signature, cert->sigLength,
                        cert->signatureOID,
                    #ifdef WC_RSA_PSS
                        cert->source + cert->sigParamsIndex,
                        cert->sigParamsLength,
                    #else
                        NULL, 0,
                    #endif
                        sce_tsip_encRsaKeyIdx)) != 0) {
                    if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
                        WOLFSSL_MSG("Confirm signature failed");
                    }
                    WOLFSSL_ERROR_VERBOSE(ret);
                    return ret;
                }

            #ifdef WOLFSSL_DUAL_ALG_CERTS
                if ((ret == 0) && cert->extAltSigAlgSet &&
                    cert->extAltSigValSet) {
                #ifndef WOLFSSL_SMALL_STACK
                    byte der[WC_MAX_CERT_VERIFY_SZ];
                #else
                    byte *der = (byte*)XMALLOC(WC_MAX_CERT_VERIFY_SZ, cert->heap,
                                            DYNAMIC_TYPE_DCERT);
                    if (der == NULL) {
                        ret = MEMORY_E;
                    } else
                #endif /* ! WOLFSSL_SMALL_STACK */
                    {
                        ret = wc_GeneratePreTBS(cert, der, WC_MAX_CERT_VERIFY_SZ);

                        if (ret > 0) {
                            ret = ConfirmSignature(&cert->sigCtx, der, ret,
                                    cert->ca->sapkiDer, cert->ca->sapkiLen,
                                    cert->ca->sapkiOID, cert->altSigValDer,
                                    cert->altSigValLen, cert->altSigAlgOID,
                                    NULL, 0, NULL);
                        }
                        WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_DCERT);

                        if (ret != 0) {
                            WOLFSSL_MSG("Confirm alternative signature failed");
                            WOLFSSL_ERROR_VERBOSE(ret);
                            return ret;
                        }
                        else {
                            WOLFSSL_MSG("Alt signature has been verified!");
                        }
                    }
                }
            #endif /* WOLFSSL_DUAL_ALG_CERTS */
            }
        #ifndef IGNORE_NAME_CONSTRAINTS
            if (verify == VERIFY || verify == VERIFY_OCSP ||
                        verify == VERIFY_NAME || verify == VERIFY_SKIP_DATE) {
                /* check that this cert's name is permitted by the signer's
                 * name constraints */
                if (!ConfirmNameConstraints(cert->ca, cert)) {
                    WOLFSSL_MSG("Confirm name constraint failed");
                    WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
                    return ASN_NAME_INVALID_E;
                }
            }
        #endif /* IGNORE_NAME_CONSTRAINTS */
        } /* cert->ca */
#ifdef WOLFSSL_CERT_REQ
        else if (type == CERTREQ_TYPE) {
            /* try to confirm/verify signature */
            if ((ret = ConfirmSignature(&cert->sigCtx,
                    cert->source + cert->certBegin,
                    cert->sigIndex - cert->certBegin,
                    cert->publicKey, cert->pubKeySize,
                    cert->keyOID, cert->signature,
                    cert->sigLength, cert->signatureOID,
                #ifdef WC_RSA_PSS
                    cert->source + cert->sigParamsIndex, cert->sigParamsLength,
                #else
                    NULL, 0,
                #endif
                    sce_tsip_encRsaKeyIdx)) != 0) {
                if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
                    WOLFSSL_MSG("Confirm signature failed");
                }
                WOLFSSL_ERROR_VERBOSE(ret);
                return ret;
            }

        #ifdef WOLFSSL_DUAL_ALG_CERTS
            if ((ret == 0) && cert->extAltSigAlgSet &&
                cert->extAltSigValSet) {
            #ifndef WOLFSSL_SMALL_STACK
                byte der[WC_MAX_CERT_VERIFY_SZ];
            #else
                byte *der = (byte*)XMALLOC(WC_MAX_CERT_VERIFY_SZ, cert->heap,
                                        DYNAMIC_TYPE_DCERT);
                if (der == NULL) {
                    ret = MEMORY_E;
                } else
            #endif /* ! WOLFSSL_SMALL_STACK */
                {
                    ret = wc_GeneratePreTBS(cert, der, WC_MAX_CERT_VERIFY_SZ);

                    if (ret > 0) {
                        ret = ConfirmSignature(&cert->sigCtx, der, ret,
                                cert->sapkiDer, cert->sapkiLen,
                                cert->sapkiOID, cert->altSigValDer,
                                cert->altSigValLen, cert->altSigAlgOID,
                                NULL, 0, NULL);
                    }
                    WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_DCERT);

                    if (ret != 0) {
                        WOLFSSL_MSG("Confirm alternative signature failed");
                        WOLFSSL_ERROR_VERBOSE(ret);
                        return ret;
                    }
                    else {
                        WOLFSSL_MSG("Alt signature has been verified!");
                    }
                }
            }
        #endif /* WOLFSSL_DUAL_ALG_CERTS */
        }
#endif
        else {
            /* no signer */
            WOLFSSL_MSG_CERT_LOG("No CA signer to verify with");
            /* If you end up here with error -188,
             * consider using WOLFSSL_ALT_CERT_CHAINS. */
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
            /* ret needs to be self-signer error for openssl compatibility */
            if (cert->selfSigned) {
                WOLFSSL_ERROR_VERBOSE(ASN_SELF_SIGNED_E);
                return ASN_SELF_SIGNED_E;
            }
            else
#endif
            {
                WOLFSSL_ERROR_VERBOSE(ASN_NO_SIGNER_E);
                WOLFSSL_MSG_CERT("Consider using WOLFSSL_ALT_CERT_CHAINS.");
                return ASN_NO_SIGNER_E;
            }
        }
    } /* verify != NO_VERIFY && type != CA_TYPE && type != TRUSTED_PEER_TYPE */

#if defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY) && !defined(NO_SKID)
exit_pcr:
#endif

    if (cert->badDate != 0) {
        if (verify != VERIFY_SKIP_DATE) {
            return cert->badDate;
        }
        WOLFSSL_MSG_CERT_LOG("Date error: Verify option is skipping");
    }

    if (cert->criticalExt != 0)
        return cert->criticalExt;

    return ret;
}

int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der)
{
    int ret = 0;

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

#ifdef WOLFSSL_DUAL_ALG_CERTS
    if (ret == 0 && signer != NULL) {
        if (cert->extSapkiSet && cert->sapkiLen > 0) {
            /* Allocated space for alternative public key. */
            signer->sapkiDer = (byte*)XMALLOC(cert->sapkiLen, cert->heap,
                                              DYNAMIC_TYPE_PUBLIC_KEY);
            if (signer->sapkiDer == NULL) {
                ret = MEMORY_E;
            }
            else {
                XMEMCPY(signer->sapkiDer, cert->sapkiDer, cert->sapkiLen);
                signer->sapkiLen = cert->sapkiLen;
                signer->sapkiOID = cert->sapkiOID;
            }
        }
    }
#endif /* WOLFSSL_DUAL_ALG_CERTS */

#if defined(WOLFSSL_AKID_NAME) || defined(HAVE_CRL)
    if (ret == 0 && signer != NULL)
        ret = CalcHashId(cert->serial, (word32)cert->serialSz,
                         signer->serialHash);
#endif
    if (ret == 0 && signer != NULL) {
    #ifdef WOLFSSL_SIGNER_DER_CERT
        ret = AllocDer(&signer->derCert, der->length, der->type, NULL);
    }
    if (ret == 0 && signer != NULL) {
        XMEMCPY(signer->derCert->buffer, der->buffer, der->length);
    #else
    (void)der;
    #endif
        signer->keyOID         = cert->keyOID;
        if (cert->pubKeyStored) {
            signer->publicKey      = cert->publicKey;
            signer->pubKeySize     = cert->pubKeySize;
        }

        if (cert->subjectCNStored) {
            signer->nameLen        = cert->subjectCNLen;
            signer->name           = cert->subjectCN;
        }
        signer->maxPathLen     = cert->maxPathLen;
        signer->selfSigned     = cert->selfSigned;
    #ifndef IGNORE_NAME_CONSTRAINTS
        signer->permittedNames = cert->permittedNames;
        signer->excludedNames  = cert->excludedNames;
        signer->extNameConstraintCrit = cert->extNameConstraintCrit;
        signer->extNameConstraintHasUnsupported =
            cert->extNameConstraintHasUnsupported;
    #endif
    #ifndef NO_SKID
        XMEMCPY(signer->subjectKeyIdHash, cert->extSubjKeyId,
                SIGNER_DIGEST_SIZE);
    #endif
        XMEMCPY(signer->subjectNameHash, cert->subjectHash,
                SIGNER_DIGEST_SIZE);
    #if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME)
        XMEMCPY(signer->issuerNameHash, cert->issuerHash,
                SIGNER_DIGEST_SIZE);
    #endif
    #ifdef HAVE_OCSP
        XMEMCPY(signer->subjectKeyHash, cert->subjectKeyHash,
                KEYID_SIZE);
        XMEMCPY(signer->issuerKeyHash, cert->issuerKeyHash,
                KEYID_SIZE);
    #endif
        signer->keyUsage = cert->extKeyUsageSet ? cert->extKeyUsage
                                                : 0xFFFF;
        signer->extKeyUsage = cert->extExtKeyUsage;
        signer->next    = NULL; /* If Key Usage not set, all uses valid. */
        cert->publicKey = 0;    /* in case lock fails don't free here.   */
        cert->pubKeyStored = 0;
        cert->subjectCN = 0;
        cert->subjectCNStored = 0;
    #ifndef IGNORE_NAME_CONSTRAINTS
        cert->permittedNames = NULL;
        cert->excludedNames = NULL;
    #endif
        signer->type = (byte)type;
    }
    return ret;
}

/* Create and init an new signer */
Signer* MakeSigner(void* heap)
{
    Signer* signer = (Signer*) XMALLOC(sizeof(Signer), heap,
                                       DYNAMIC_TYPE_SIGNER);
    if (signer) {
        XMEMSET(signer, 0, sizeof(Signer));
    }
    (void)heap;

    return signer;
}


/* Free an individual signer.
 *
 * Used by Certificate Manager.
 *
 * @param [in, out] signer  On in, signer object.
 *                          On out, pointer is no longer valid.
 * @param [in]      heap    Dynamic memory hint.
 */
void FreeSigner(Signer* signer, void* heap)
{
    (void)signer;
    (void)heap;

    /* this cast is safe because signer->name is only set in FillSigner()
     * from cert->subjectCN, and only if cert->subjectCNStored, in which case
     * cert->subjectCN is set to NULL, imparting ownership to the Signer object.
     */
    XFREE((void *)(wc_ptr_t)signer->name, heap, DYNAMIC_TYPE_SUBJECT_CN);
    /* this cast is safe because signer->publicKey is only set in FillSigner()
     * from cert->publicKey, and only if cert->pubKeyStored, in which case
     * cert->publicKey is set to NULL, imparting ownership to the Signer object.
     */
    XFREE((void*)(wc_ptr_t)signer->publicKey, heap, DYNAMIC_TYPE_PUBLIC_KEY);
#ifdef WOLFSSL_DUAL_ALG_CERTS
    XFREE(signer->sapkiDer, heap, DYNAMIC_TYPE_PUBLIC_KEY);
#endif
#ifndef IGNORE_NAME_CONSTRAINTS
    if (signer->permittedNames)
        FreeNameSubtrees(signer->permittedNames, heap);
    if (signer->excludedNames)
        FreeNameSubtrees(signer->excludedNames, heap);
#endif
#ifdef WOLFSSL_SIGNER_DER_CERT
    FreeDer(&signer->derCert);
#endif
    XFREE(signer, heap, DYNAMIC_TYPE_SIGNER);
}


/* Free the whole singer table with number of rows.
 *
 * Each table entry is a linked list of signers.
 * Used by Certificate Manager.
 *
 * @param [in, out] table   Array of signer objects.
 * @param [in]      rows    Number of entries in table.
 * @param [in]      heap    Dynamic memory hint.
 */
void FreeSignerTable(Signer** table, int rows, void* heap)
{
    int i;

    for (i = 0; i < rows; i++) {
        Signer* signer = table[i];
        while (signer) {
            Signer* next = signer->next;
            FreeSigner(signer, heap);
            signer = next;
        }
        table[i] = NULL;
    }
}

void FreeSignerTableType(Signer** table, int rows, byte type, void* heap)
{
    int i;

    for (i = 0; i < rows; i++) {
        Signer* signer = table[i];
        Signer** next = &table[i];

        while (signer) {
            if (signer->type == type) {
                *next = signer->next;
                FreeSigner(signer, heap);
                signer = *next;
            }
            else {
                next = &signer->next;
                signer = signer->next;
            }
        }
    }
}

#ifdef WOLFSSL_TRUST_PEER_CERT
/* Free an individual trusted peer cert.
 *
 * @param [in, out] tp    Trusted peer certificate object.
 * @param [in]      heap  Dynamic memory hint.
 */
void FreeTrustedPeer(TrustedPeerCert* tp, void* heap)
{
    if (tp == NULL) {
        return;
    }

    /* safe cast -- when .name is set in AddTrustedPeer() from cert->subjectCN,
     * it inherits the allocation from ParseCert(), and cert->subjectCN is set
     * to NULL.
     */
    XFREE((void *)(wc_ptr_t)tp->name, heap, DYNAMIC_TYPE_SUBJECT_CN);

    XFREE(tp->sig, heap, DYNAMIC_TYPE_SIGNATURE);
#ifndef IGNORE_NAME_CONSTRAINTS
    if (tp->permittedNames)
        FreeNameSubtrees(tp->permittedNames, heap);
    if (tp->excludedNames)
        FreeNameSubtrees(tp->excludedNames, heap);
#endif
    XFREE(tp, heap, DYNAMIC_TYPE_CERT);

    (void)heap;
}

/* Free the whole Trusted Peer linked list.
 *
 * Each table entry is a linked list of trusted peer certificates.
 * Used by Certificate Manager.
 *
 * @param [in, out] table   Array of trusted peer certificate objects.
 * @param [in]      rows    Number of entries in table.
 * @param [in]      heap    Dynamic memory hint.
 */
void FreeTrustedPeerTable(TrustedPeerCert** table, int rows, void* heap)
{
    int i;

    for (i = 0; i < rows; i++) {
        TrustedPeerCert* tp = table[i];
        while (tp) {
            TrustedPeerCert* next = tp->next;
            FreeTrustedPeer(tp, heap);
            tp = next;
        }
        table[i] = NULL;
    }
}
#endif /* WOLFSSL_TRUST_PEER_CERT */

#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7)
int SetSerialNumber(const byte* sn, word32 snSz, byte* output,
    word32 outputSz, int maxSnSz)
{
    int i;
    int snSzInt = (int)snSz;

    if (sn == NULL || output == NULL || snSzInt < 0)
        return BAD_FUNC_ARG;

    /* remove leading zeros */
    while (snSzInt > 0 && sn[0] == 0) {
        snSzInt--;
        sn++;
    }
    /* RFC 5280 - 4.1.2.2:
     *   Serial numbers must be a positive value (and not zero) */
    if (snSzInt == 0) {
        WOLFSSL_ERROR_VERBOSE(BAD_FUNC_ARG);
        return BAD_FUNC_ARG;
    }

    if (sn[0] & 0x80)
        maxSnSz--;
    /* truncate if input is too long */
    if (snSzInt > maxSnSz)
        snSzInt = maxSnSz;

    i = SetASNInt(snSzInt, sn[0], NULL);
    /* truncate if input is too long */
    if (snSzInt > (int)outputSz - i)
        snSzInt = (int)outputSz - i;
    /* sanity check number of bytes to copy */
    if (snSzInt <= 0) {
        return BUFFER_E;
    }

    /* write out ASN.1 Integer */
    (void)SetASNInt(snSzInt, sn[0], output);
    XMEMCPY(output + i, sn, (size_t)snSzInt);

    /* compute final length */
    i += snSzInt;

    return i;
}
#endif /* !WOLFSSL_ASN_TEMPLATE */

#endif /* !NO_CERTS */

#if defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS12) || \
    (defined(HAVE_ECC_KEY_EXPORT) && !defined(NO_ASN_CRYPT)) || \
    (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN))
int SetMyVersion(word32 version, byte* output, int header)
{
    int i = 0;

    if (output == NULL)
        return BAD_FUNC_ARG;

    if (header) {
        output[i++] = ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED;
        output[i++] = 3;
    }
    output[i++] = ASN_INTEGER;
    output[i++] = 0x01;
    output[i++] = (byte)version;

    return i;
}
#endif

#ifndef NO_CERTS

/* TODO: consider moving PEM code out to a different file. */

int AllocDer(DerBuffer** pDer, word32 length, int type, void* heap)
{
    int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    if (pDer) {
        int dynType = 0;
        DerBuffer* der;

        /* Determine dynamic type */
        switch (type) {
            case CA_TYPE:   dynType = DYNAMIC_TYPE_CA;   break;
            case CHAIN_CERT_TYPE:
            case CERT_TYPE: dynType = DYNAMIC_TYPE_CERT; break;
            case CRL_TYPE:  dynType = DYNAMIC_TYPE_CRL;  break;
            case DSA_TYPE:  dynType = DYNAMIC_TYPE_DSA;  break;
            case ECC_TYPE:  dynType = DYNAMIC_TYPE_ECC;  break;
            case RSA_TYPE:  dynType = DYNAMIC_TYPE_RSA;  break;
            default:        dynType = DYNAMIC_TYPE_KEY;  break;
        }

        /* Setup new buffer */
        *pDer = (DerBuffer*)XMALLOC(sizeof(DerBuffer) + length, heap, dynType);
        if (*pDer == NULL) {
            return MEMORY_E;
        }
        XMEMSET(*pDer, 0, sizeof(DerBuffer) + length);

        der = *pDer;
        der->type = type;
        der->dynType = dynType; /* Cache this for FreeDer */
        der->heap = heap;
        der->buffer = (byte*)der + sizeof(DerBuffer);
        der->length = length;
        ret = 0; /* Success */
    } else {
        ret = BAD_FUNC_ARG;
    }
    return ret;
}

int AllocCopyDer(DerBuffer** pDer, const unsigned char* buff, word32 length,
    int type, void* heap)
{
    int ret = AllocDer(pDer, length, type, heap);
    if (ret == 0) {
        XMEMCPY((*pDer)->buffer, buff, length);
    }

    return ret;
}

void FreeDer(DerBuffer** pDer)
{
    if (pDer && *pDer) {
        DerBuffer* der = (DerBuffer*)*pDer;

        /* ForceZero private keys */
        if (((der->type == PRIVATEKEY_TYPE) ||
             (der->type == ALT_PRIVATEKEY_TYPE)) && der->buffer != NULL) {
            ForceZero(der->buffer, der->length);
        }
        der->buffer = NULL;
        der->length = 0;
        XFREE(der, der->heap, der->dynType);

        *pDer = NULL;
    }
}

#ifndef WOLFSSL_API_PREFIX_MAP
int wc_AllocDer(DerBuffer** pDer, word32 length, int type, void* heap)
{
    return AllocDer(pDer, length, type, heap);
}
void wc_FreeDer(DerBuffer** pDer)
{
    FreeDer(pDer);
}
#endif


#if defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM)

/* Note: If items added make sure MAX_X509_HEADER_SZ is
    updated to reflect maximum length and pem_struct_min_sz
    to reflect minimum size */
static wcchar BEGIN_CERT           = "-----BEGIN CERTIFICATE-----";
static wcchar END_CERT             = "-----END CERTIFICATE-----";
#ifdef WOLFSSL_CERT_REQ
    static wcchar BEGIN_CERT_REQ   = "-----BEGIN CERTIFICATE REQUEST-----";
    static wcchar END_CERT_REQ     = "-----END CERTIFICATE REQUEST-----";
#endif
#if defined(WOLFSSL_ACERT)
    static wcchar BEGIN_ACERT      = "-----BEGIN ATTRIBUTE CERTIFICATE-----";
    static wcchar END_ACERT        = "-----END ATTRIBUTE CERTIFICATE-----";
#endif /* WOLFSSL_ACERT */
#ifndef NO_DH
    static wcchar BEGIN_DH_PARAM   = "-----BEGIN DH PARAMETERS-----";
    static wcchar END_DH_PARAM     = "-----END DH PARAMETERS-----";
    static wcchar BEGIN_X942_PARAM = "-----BEGIN X9.42 DH PARAMETERS-----";
    static wcchar END_X942_PARAM   = "-----END X9.42 DH PARAMETERS-----";
#endif
#ifndef NO_DSA
    static wcchar BEGIN_DSA_PARAM  = "-----BEGIN DSA PARAMETERS-----";
    static wcchar END_DSA_PARAM    = "-----END DSA PARAMETERS-----";
#endif
static wcchar BEGIN_X509_CRL       = "-----BEGIN X509 CRL-----";
static wcchar END_X509_CRL         = "-----END X509 CRL-----";
static wcchar BEGIN_TRUSTED_CERT   = "-----BEGIN TRUSTED CERTIFICATE-----";
static wcchar END_TRUSTED_CERT     = "-----END TRUSTED CERTIFICATE-----";
static wcchar BEGIN_RSA_PRIV       = "-----BEGIN RSA PRIVATE KEY-----";
static wcchar END_RSA_PRIV         = "-----END RSA PRIVATE KEY-----";
static wcchar BEGIN_RSA_PUB        = "-----BEGIN RSA PUBLIC KEY-----";
static wcchar END_RSA_PUB          = "-----END RSA PUBLIC KEY-----";
static wcchar BEGIN_PRIV_KEY       = "-----BEGIN PRIVATE KEY-----";
static wcchar END_PRIV_KEY         = "-----END PRIVATE KEY-----";
static wcchar BEGIN_ENC_PRIV_KEY   = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
static wcchar END_ENC_PRIV_KEY     = "-----END ENCRYPTED PRIVATE KEY-----";
#ifdef HAVE_ECC
    static wcchar BEGIN_EC_PRIV    = "-----BEGIN EC PRIVATE KEY-----";
    static wcchar END_EC_PRIV      = "-----END EC PRIVATE KEY-----";
#ifdef OPENSSL_EXTRA
    static wcchar BEGIN_EC_PARAM   = "-----BEGIN EC PARAMETERS-----";
    static wcchar END_EC_PARAM     = "-----END EC PARAMETERS-----";
#endif
#endif
#ifdef HAVE_PKCS7
static wcchar BEGIN_PKCS7          = "-----BEGIN PKCS7-----";
static wcchar END_PKCS7            = "-----END PKCS7-----";
#endif
#if (defined(HAVE_ECC) || !defined(NO_DSA)) && defined(WOLFSSL_PEM_TO_DER)
    static wcchar BEGIN_DSA_PRIV   = "-----BEGIN DSA PRIVATE KEY-----";
    static wcchar END_DSA_PRIV     = "-----END DSA PRIVATE KEY-----";
#endif
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_PEM_TO_DER)
    static wcchar BEGIN_PRIV_KEY_PREFIX = "-----BEGIN";
    static wcchar PRIV_KEY_SUFFIX = "PRIVATE KEY-----";
    static wcchar END_PRIV_KEY_PREFIX   = "-----END";
#endif
static wcchar BEGIN_PUB_KEY        = "-----BEGIN PUBLIC KEY-----";
static wcchar END_PUB_KEY          = "-----END PUBLIC KEY-----";
#if defined(HAVE_ED25519) || defined(HAVE_ED448)
    static wcchar BEGIN_EDDSA_PRIV = "-----BEGIN EDDSA PRIVATE KEY-----";
    static wcchar END_EDDSA_PRIV   = "-----END EDDSA PRIVATE KEY-----";
#endif
#if defined(HAVE_FALCON)
    static wcchar BEGIN_FALCON_LEVEL1_PRIV  = "-----BEGIN FALCON_LEVEL1 PRIVATE KEY-----";
    static wcchar END_FALCON_LEVEL1_PRIV    = "-----END FALCON_LEVEL1 PRIVATE KEY-----";
    static wcchar BEGIN_FALCON_LEVEL5_PRIV  = "-----BEGIN FALCON_LEVEL5 PRIVATE KEY-----";
    static wcchar END_FALCON_LEVEL5_PRIV    = "-----END FALCON_LEVEL5 PRIVATE KEY-----";
#endif /* HAVE_FALCON */
#if defined(WOLFSSL_HAVE_MLDSA)
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    static wcchar BEGIN_DILITHIUM_LEVEL2_PRIV = "-----BEGIN DILITHIUM_LEVEL2 PRIVATE KEY-----";
    static wcchar END_DILITHIUM_LEVEL2_PRIV   = "-----END DILITHIUM_LEVEL2 PRIVATE KEY-----";
    static wcchar BEGIN_DILITHIUM_LEVEL3_PRIV = "-----BEGIN DILITHIUM_LEVEL3 PRIVATE KEY-----";
    static wcchar END_DILITHIUM_LEVEL3_PRIV   = "-----END DILITHIUM_LEVEL3 PRIVATE KEY-----";
    static wcchar BEGIN_DILITHIUM_LEVEL5_PRIV = "-----BEGIN DILITHIUM_LEVEL5 PRIVATE KEY-----";
    static wcchar END_DILITHIUM_LEVEL5_PRIV   = "-----END DILITHIUM_LEVEL5 PRIVATE KEY-----";
    #endif
    static wcchar BEGIN_ML_DSA_LEVEL2_PRIV = "-----BEGIN ML_DSA_LEVEL2 PRIVATE KEY-----";
    static wcchar END_ML_DSA_LEVEL2_PRIV   = "-----END ML_DSA_LEVEL2 PRIVATE KEY-----";
    static wcchar BEGIN_ML_DSA_LEVEL3_PRIV = "-----BEGIN ML_DSA_LEVEL3 PRIVATE KEY-----";
    static wcchar END_ML_DSA_LEVEL3_PRIV   = "-----END ML_DSA_LEVEL3 PRIVATE KEY-----";
    static wcchar BEGIN_ML_DSA_LEVEL5_PRIV = "-----BEGIN ML_DSA_LEVEL5 PRIVATE KEY-----";
    static wcchar END_ML_DSA_LEVEL5_PRIV   = "-----END ML_DSA_LEVEL5 PRIVATE KEY-----";
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
    static wcchar BEGIN_SLH_DSA_SHAKE_128F_PRIV = "-----BEGIN SLH_DSA_SHAKE_128F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_128F_PRIV   = "-----END SLH_DSA_SHAKE_128F PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHAKE_192F_PRIV = "-----BEGIN SLH_DSA_SHAKE_192F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_192F_PRIV   = "-----END SLH_DSA_SHAKE_192F PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHAKE_256F_PRIV = "-----BEGIN SLH_DSA_SHAKE_256F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_256F_PRIV   = "-----END SLH_DSA_SHAKE_256F PRIVATE KEY-----";

    static wcchar BEGIN_SLH_DSA_SHAKE_128S_PRIV = "-----BEGIN SLH_DSA_SHAKE_128S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_128S_PRIV   = "-----END SLH_DSA_SHAKE_128S PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHAKE_192S_PRIV = "-----BEGIN SLH_DSA_SHAKE_192S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_192S_PRIV   = "-----END SLH_DSA_SHAKE_192S PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHAKE_256S_PRIV = "-----BEGIN SLH_DSA_SHAKE_256S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHAKE_256S_PRIV   = "-----END SLH_DSA_SHAKE_256S PRIVATE KEY-----";
#ifdef WOLFSSL_SLHDSA_SHA2
    static wcchar BEGIN_SLH_DSA_SHA2_128F_PRIV = "-----BEGIN SLH_DSA_SHA2_128F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_128F_PRIV   = "-----END SLH_DSA_SHA2_128F PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHA2_192F_PRIV = "-----BEGIN SLH_DSA_SHA2_192F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_192F_PRIV   = "-----END SLH_DSA_SHA2_192F PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHA2_256F_PRIV = "-----BEGIN SLH_DSA_SHA2_256F PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_256F_PRIV   = "-----END SLH_DSA_SHA2_256F PRIVATE KEY-----";

    static wcchar BEGIN_SLH_DSA_SHA2_128S_PRIV = "-----BEGIN SLH_DSA_SHA2_128S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_128S_PRIV   = "-----END SLH_DSA_SHA2_128S PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHA2_192S_PRIV = "-----BEGIN SLH_DSA_SHA2_192S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_192S_PRIV   = "-----END SLH_DSA_SHA2_192S PRIVATE KEY-----";
    static wcchar BEGIN_SLH_DSA_SHA2_256S_PRIV = "-----BEGIN SLH_DSA_SHA2_256S PRIVATE KEY-----";
    static wcchar END_SLH_DSA_SHA2_256S_PRIV   = "-----END SLH_DSA_SHA2_256S PRIVATE KEY-----";
#endif /* WOLFSSL_SLHDSA_SHA2 */
#endif /* WOLFSSL_HAVE_SLHDSA */

const int pem_struct_min_sz = XSTR_SIZEOF("-----BEGIN X509 CRL-----"
                                             "-----END X509 CRL-----");

#ifdef WOLFSSL_PEM_TO_DER
static WC_INLINE const char* SkipEndOfLineChars(const char* line,
                                                const char* endOfLine)
{
    /* eat end of line characters */
    while (line < endOfLine &&
              (line[0] == '\r' || line[0] == '\n')) {
        line++;
    }
    return line;
}
#endif

int wc_PemGetHeaderFooter(int type, const char** header, const char** footer)
{
    int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);

    switch (type) {
        case CA_TYPE:       /* same as below */
        case TRUSTED_PEER_TYPE:
        case CHAIN_CERT_TYPE:
        case CERT_TYPE:
            if (header) *header = BEGIN_CERT;
            if (footer) *footer = END_CERT;
            ret = 0;
            break;

        case CRL_TYPE:
            if (header) *header = BEGIN_X509_CRL;
            if (footer) *footer = END_X509_CRL;
            ret = 0;
            break;
    #ifndef NO_DH
        case DH_PARAM_TYPE:
            if (header) *header = BEGIN_DH_PARAM;
            if (footer) *footer = END_DH_PARAM;
            ret = 0;
            break;
        case X942_PARAM_TYPE:
            if (header) *header = BEGIN_X942_PARAM;
            if (footer) *footer = END_X942_PARAM;
            ret = 0;
            break;
    #endif
    #ifndef NO_DSA
        case DSA_PARAM_TYPE:
            if (header) *header = BEGIN_DSA_PARAM;
            if (footer) *footer = END_DSA_PARAM;
            ret = 0;
            break;
    #endif
    #ifdef WOLFSSL_CERT_REQ
        case CERTREQ_TYPE:
            if (header) *header = BEGIN_CERT_REQ;
            if (footer) *footer = END_CERT_REQ;
            ret = 0;
            break;
    #endif
    #ifdef HAVE_PKCS7
        case PKCS7_TYPE:
            if (header) *header = BEGIN_PKCS7;
            if (footer) *footer = END_PKCS7;
            ret = 0;
            break;
    #endif
    #if defined(WOLFSSL_ACERT)
        case ACERT_TYPE:
            if (header) *header = BEGIN_ACERT;
            if (footer) *footer = END_ACERT;
            ret = 0;
            break;
    #endif /* WOLFSSL_ACERT */
    #ifndef NO_DSA
        case DSA_TYPE:
        case DSA_PRIVATEKEY_TYPE:
            if (header) *header = BEGIN_DSA_PRIV;
            if (footer) *footer = END_DSA_PRIV;
            ret = 0;
            break;
    #endif
    #ifdef HAVE_ECC
        case ECC_TYPE:
        case ECC_PRIVATEKEY_TYPE:
            if (header) *header = BEGIN_EC_PRIV;
            if (footer) *footer = END_EC_PRIV;
            ret = 0;
            break;
    #ifdef OPENSSL_EXTRA
        case ECC_PARAM_TYPE:
            if (header) *header = BEGIN_EC_PARAM;
            if (footer) *footer = END_EC_PARAM;
            ret = 0;
            break;
    #endif
    #endif
        case RSA_TYPE:
        case PRIVATEKEY_TYPE:
    #ifdef WOLFSSL_DUAL_ALG_CERTS
        case ALT_PRIVATEKEY_TYPE:
    #endif
            if (header) *header = BEGIN_RSA_PRIV;
            if (footer) *footer = END_RSA_PRIV;
            ret = 0;
            break;
    #ifdef HAVE_ED25519
        case ED25519_TYPE:
    #endif
    #ifdef HAVE_ED448
        case ED448_TYPE:
    #endif
    #if defined(HAVE_ED25519) || defined(HAVE_ED448)
        case EDDSA_PRIVATEKEY_TYPE:
            if (header) *header = BEGIN_EDDSA_PRIV;
            if (footer) *footer = END_EDDSA_PRIV;
            ret = 0;
            break;
    #endif
#ifdef HAVE_FALCON
        case FALCON_LEVEL1_TYPE:
            if (header) *header = BEGIN_FALCON_LEVEL1_PRIV;
            if (footer) *footer = END_FALCON_LEVEL1_PRIV;
            ret = 0;
            break;
        case FALCON_LEVEL5_TYPE:
            if (header) *header = BEGIN_FALCON_LEVEL5_PRIV;
            if (footer) *footer = END_FALCON_LEVEL5_PRIV;
            ret = 0;
            break;
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case DILITHIUM_LEVEL2_TYPE:
            if (header) *header = BEGIN_DILITHIUM_LEVEL2_PRIV;
            if (footer) *footer = END_DILITHIUM_LEVEL2_PRIV;
            ret = 0;
            break;
        case DILITHIUM_LEVEL3_TYPE:
            if (header) *header = BEGIN_DILITHIUM_LEVEL3_PRIV;
            if (footer) *footer = END_DILITHIUM_LEVEL3_PRIV;
            ret = 0;
            break;
        case DILITHIUM_LEVEL5_TYPE:
            if (header) *header = BEGIN_DILITHIUM_LEVEL5_PRIV;
            if (footer) *footer = END_DILITHIUM_LEVEL5_PRIV;
            ret = 0;
            break;
    #endif
        case ML_DSA_44_TYPE:
            if (header) *header = BEGIN_ML_DSA_LEVEL2_PRIV;
            if (footer) *footer = END_ML_DSA_LEVEL2_PRIV;
            ret = 0;
            break;
        case ML_DSA_65_TYPE:
            if (header) *header = BEGIN_ML_DSA_LEVEL3_PRIV;
            if (footer) *footer = END_ML_DSA_LEVEL3_PRIV;
            ret = 0;
            break;
        case ML_DSA_87_TYPE:
            if (header) *header = BEGIN_ML_DSA_LEVEL5_PRIV;
            if (footer) *footer = END_ML_DSA_LEVEL5_PRIV;
            ret = 0;
            break;
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
        case SLH_DSA_SHAKE_128F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_128F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_128F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHAKE_192F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_192F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_192F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHAKE_256F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_256F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_256F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHAKE_128S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_128S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_128S_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHAKE_192S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_192S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_192S_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHAKE_256S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHAKE_256S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHAKE_256S_PRIV;
            ret = 0;
            break;
    #ifdef WOLFSSL_SLHDSA_SHA2
        case SLH_DSA_SHA2_128F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_128F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_128F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHA2_192F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_192F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_192F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHA2_256F_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_256F_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_256F_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHA2_128S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_128S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_128S_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHA2_192S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_192S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_192S_PRIV;
            ret = 0;
            break;
        case SLH_DSA_SHA2_256S_TYPE:
            if (header) *header = BEGIN_SLH_DSA_SHA2_256S_PRIV;
            if (footer) *footer = END_SLH_DSA_SHA2_256S_PRIV;
            ret = 0;
            break;
    #endif /* WOLFSSL_SLHDSA_SHA2 */
#endif /* WOLFSSL_HAVE_SLHDSA */
        case PUBLICKEY_TYPE:
        case ECC_PUBLICKEY_TYPE:
            if (header) *header = BEGIN_PUB_KEY;
            if (footer) *footer = END_PUB_KEY;
            ret = 0;
            break;
        case RSA_PUBLICKEY_TYPE:
            if (header) *header = BEGIN_RSA_PUB;
            if (footer) *footer = END_RSA_PUB;
            ret = 0;
            break;
    #ifndef NO_DH
        case DH_PRIVATEKEY_TYPE:
    #endif
        case PKCS8_PRIVATEKEY_TYPE:
            if (header) *header = BEGIN_PRIV_KEY;
            if (footer) *footer = END_PRIV_KEY;
            ret = 0;
            break;
        case PKCS8_ENC_PRIVATEKEY_TYPE:
            if (header) *header = BEGIN_ENC_PRIV_KEY;
            if (footer) *footer = END_ENC_PRIV_KEY;
            ret = 0;
            break;
        case TRUSTED_CERT_TYPE:
            if (header) *header = BEGIN_TRUSTED_CERT;
            if (footer) *footer = END_TRUSTED_CERT;
            ret = 0;
            break;
        default:
            ret = BAD_FUNC_ARG;
            break;
    }
    return ret;
}

#ifdef WOLFSSL_ENCRYPTED_KEYS

static wcchar kProcTypeHeader = "Proc-Type";
static wcchar kDecInfoHeader = "DEK-Info";

#ifdef WOLFSSL_PEM_TO_DER
#ifndef NO_DES3
    static wcchar kEncTypeDes = "DES-CBC";
    static wcchar kEncTypeDes3 = "DES-EDE3-CBC";
#endif
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128)
    static wcchar kEncTypeAesCbc128 = "AES-128-CBC";
#endif
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_192)
    static wcchar kEncTypeAesCbc192 = "AES-192-CBC";
#endif
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_256)
    static wcchar kEncTypeAesCbc256 = "AES-256-CBC";
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_128)
    static wcchar kEncTypeAesCtr128 = "AES-128-CTR";
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_192)
    static wcchar kEncTypeAesCtr192 = "AES-192-CTR";
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_256)
    static wcchar kEncTypeAesCtr256 = "AES-256-CTR";
#endif

int wc_EncryptedInfoGet(EncryptedInfo* info, const char* cipherInfo)
{
    int ret = 0;

    if (info == NULL || cipherInfo == NULL)
        return BAD_FUNC_ARG;

    /* determine cipher information */
#ifndef NO_DES3
    if (XSTRCMP(cipherInfo, kEncTypeDes) == 0) {
        info->cipherType = WC_CIPHER_DES;
        info->keySz = DES_KEY_SIZE;
/* DES_IV_SIZE is incorrectly 16 in FIPS v2. It should be 8, same as the
 * block size. */
#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 2)
        if (info->ivSz == 0) info->ivSz  = DES_BLOCK_SIZE;
#else
        if (info->ivSz == 0) info->ivSz  = DES_IV_SIZE;
#endif
    }
    else if (XSTRCMP(cipherInfo, kEncTypeDes3) == 0) {
        info->cipherType = WC_CIPHER_DES3;
        info->keySz = DES3_KEY_SIZE;
#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 2)
        if (info->ivSz == 0) info->ivSz  = DES_BLOCK_SIZE;
#else
        if (info->ivSz == 0) info->ivSz  = DES_IV_SIZE;
#endif
    }
    else
#endif /* !NO_DES3 */
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128)
    if (XSTRCMP(cipherInfo, kEncTypeAesCbc128) == 0) {
        info->cipherType = WC_CIPHER_AES_CBC;
        info->keySz = AES_128_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_192)
    if (XSTRCMP(cipherInfo, kEncTypeAesCbc192) == 0) {
        info->cipherType = WC_CIPHER_AES_CBC;
        info->keySz = AES_192_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_256)
    if (XSTRCMP(cipherInfo, kEncTypeAesCbc256) == 0) {
        info->cipherType = WC_CIPHER_AES_CBC;
        info->keySz = AES_256_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_128)
    if (XSTRCMP(cipherInfo, kEncTypeAesCtr128) == 0) {
        info->cipherType = WC_CIPHER_AES_CTR;
        info->keySz = AES_128_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_192)
    if (XSTRCMP(cipherInfo, kEncTypeAesCtr192) == 0) {
        info->cipherType = WC_CIPHER_AES_CTR;
        info->keySz = AES_192_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_256)
    if (XSTRCMP(cipherInfo, kEncTypeAesCtr256) == 0) {
        info->cipherType = WC_CIPHER_AES_CTR;
        info->keySz = AES_256_KEY_SIZE;
        if (info->ivSz == 0) info->ivSz  = AES_IV_SIZE;
    }
    else
#endif
    {
        ret = NOT_COMPILED_IN;
    }
    return ret;
}

int wc_EncryptedInfoParse(EncryptedInfo* info, const char** pBuffer,
                          size_t bufSz)
{
    int         err = 0;
    const char* bufferStart;
    const char* bufferEnd;
    const char* line;

    if (info == NULL || pBuffer == NULL || bufSz == 0)
        return BAD_FUNC_ARG;

    bufferStart = *pBuffer;
    bufferEnd = bufferStart + bufSz;

    /* find encrypted info marker */
    line = XSTRNSTR(bufferStart, kProcTypeHeader,
                    min((word32)bufSz, PEM_LINE_LEN));
    if (line != NULL) {
        word32      lineSz;
        const char* finish;
        const char* start;
        word32      startSz;
        const char* newline = NULL;

        if (line >= bufferEnd) {
            return BUFFER_E;
        }

        lineSz = (word32)(bufferEnd - line);

        /* find DEC-Info marker */
        start = XSTRNSTR(line, kDecInfoHeader, min(lineSz, PEM_LINE_LEN));

        if (start == NULL)
            return BUFFER_E;

        /* skip dec-info and ": " */
        start += XSTRLEN(kDecInfoHeader);
        if (start >= bufferEnd)
            return BUFFER_E;

        if (start[0] == ':') {
            start++;
            if (start >= bufferEnd)
                return BUFFER_E;
        }
        if (start[0] == ' ')
            start++;

        startSz = (word32)(bufferEnd - start);
        finish = XSTRNSTR(start, ",", min(startSz, PEM_LINE_LEN));

        if ((start != NULL) && (finish != NULL) && (start < finish)) {
            word32 finishSz;

            if (finish >= bufferEnd) {
                return BUFFER_E;
            }

            finishSz = (word32)(bufferEnd - finish);
            newline = XSTRNSTR(finish, "\r", min(finishSz, PEM_LINE_LEN));

            /* get cipher name */
            if (NAME_SZ <= (finish - start)) /* buffer size of info->name */
                return BUFFER_E;
            if (XMEMCPY(info->name, start, (size_t)(finish - start)) == NULL)
                return BUFFER_E;
            info->name[finish - start] = '\0'; /* null term */

            /* populate info */
            err = wc_EncryptedInfoGet(info, info->name);
            if (err != 0)
                return err;

            /* get IV */
            if (finishSz < info->ivSz + 1)
                return BUFFER_E;

            if (newline == NULL) {
                newline = XSTRNSTR(finish, "\n", min(finishSz,
                                                     PEM_LINE_LEN));
            }
            if ((newline != NULL) && (newline > finish)) {
                finish++;
                info->ivSz = (word32)(newline - finish);
                if (info->ivSz > IV_SZ)
                    return BUFFER_E;
                if (XMEMCPY(info->iv, finish, info->ivSz) == NULL)
                    return BUFFER_E;
                info->set = 1;
            }
            else
                return BUFFER_E;
        }
        else
            return BUFFER_E;

        /* eat end of line characters */
        newline = SkipEndOfLineChars(newline, bufferEnd);

        /* return new headerEnd */

        *pBuffer = newline;
    }

    return err;
}
#endif /* WOLFSSL_PEM_TO_DER */

#ifdef WOLFSSL_DER_TO_PEM
static int wc_EncryptedInfoAppend(char* dest, int destSz, char* cipherInfo)
{
    if (cipherInfo != NULL) {
        int cipherInfoStrLen = (int)XSTRLEN((char*)cipherInfo);

        if (cipherInfoStrLen > HEADER_ENCRYPTED_KEY_SIZE - (9+14+10+3))
            cipherInfoStrLen = HEADER_ENCRYPTED_KEY_SIZE - (9+14+10+3);

        if (destSz - (int)XSTRLEN(dest) >= cipherInfoStrLen + (9+14+8+2+2+1)) {
            /* strncat's src length needs to include the NULL */
            XSTRNCAT(dest, kProcTypeHeader, 10);
            XSTRNCAT(dest, ": 4,ENCRYPTED\n", 15);
            XSTRNCAT(dest, kDecInfoHeader, 9);
            XSTRNCAT(dest, ": ", 3);
            XSTRNCAT(dest, cipherInfo, (size_t)destSz - XSTRLEN(dest) - 1);
            XSTRNCAT(dest, "\n\n", 4);
        }
    }
    return 0;
}
#endif /* WOLFSSL_DER_TO_PEM */
#endif /* WOLFSSL_ENCRYPTED_KEYS */

#ifdef WOLFSSL_DER_TO_PEM

/* Used for compatibility API */
WOLFSSL_ABI
int wc_DerToPem(const byte* der, word32 derSz,
                byte* output, word32 outSz, int type)
{
    return wc_DerToPemEx(der, derSz, output, outSz, NULL, type);
}

/* convert der buffer to pem into output, can't do inplace, der and output
   need to be different */
int wc_DerToPemEx(const byte* der, word32 derSz, byte* output, word32 outSz,
             byte *cipher_info, int type)
{
    const char* headerStr = NULL;
    const char* footerStr = NULL;
#ifdef WOLFSSL_SMALL_STACK
    char* header = NULL;
    char* footer = NULL;
#else
    char header[MAX_X509_HEADER_SZ + HEADER_ENCRYPTED_KEY_SIZE];
    char footer[MAX_X509_HEADER_SZ];
#endif
    size_t headerLen = MAX_X509_HEADER_SZ + HEADER_ENCRYPTED_KEY_SIZE;
    size_t footerLen = MAX_X509_HEADER_SZ;
    int i;
    int err;
    int outLen;   /* return length or error */

    (void)cipher_info;

    if (der == output)      /* no in place conversion */
        return BAD_FUNC_ARG;

    err = wc_PemGetHeaderFooter(type, &headerStr, &footerStr);
    if (err != 0)
        return err;

#ifdef WOLFSSL_SMALL_STACK
    header = (char*)XMALLOC(headerLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    if (header == NULL)
        return MEMORY_E;

    footer = (char*)XMALLOC(footerLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    if (footer == NULL) {
        XFREE(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        return MEMORY_E;
    }
#endif

    /* build header and footer based on type */
    XSTRNCPY(header, headerStr, headerLen - 1);
    header[headerLen - 2] = 0;
    XSTRNCPY(footer, footerStr, footerLen - 1);
    footer[footerLen - 2] = 0;

    /* add new line to end */
    XSTRNCAT(header, "\n", 2);
    XSTRNCAT(footer, "\n", 2);

#ifdef WOLFSSL_ENCRYPTED_KEYS
    err = wc_EncryptedInfoAppend(header, (int)headerLen, (char*)cipher_info);
    if (err != 0) {
        WC_FREE_VAR_EX(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        return err;
    }
#endif

    headerLen = XSTRLEN(header);
    footerLen = XSTRLEN(footer);

    /* if null output and 0 size passed in then return size needed */
    if (!output && outSz == 0) {
        WC_FREE_VAR_EX(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        outLen = 0;
        if ((err = Base64_Encode(der, derSz, NULL, (word32*)&outLen))
                != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
            WOLFSSL_ERROR_VERBOSE(err);
            return err;
        }
        return (int)headerLen + (int)footerLen + outLen;
    }

    if (!der || !output) {
        WC_FREE_VAR_EX(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        return BAD_FUNC_ARG;
    }

    /* don't even try if outSz too short */
    if (outSz < (word32)headerLen + (word32)footerLen + derSz) {
        WC_FREE_VAR_EX(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        return BAD_FUNC_ARG;
    }

    /* header */
    XMEMCPY(output, header, (size_t)headerLen);
    i = (int)headerLen;

    WC_FREE_VAR_EX(header, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    /* body */
    outLen = (int)outSz - (int)(headerLen + footerLen);  /* input to Base64_Encode */
    if ( (err = Base64_Encode(der, derSz, output + i, (word32*)&outLen)) < 0) {
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        WOLFSSL_ERROR_VERBOSE(err);
        return err;
    }
    i += outLen;

    /* footer */
    if ( (i + (int)footerLen) > (int)outSz) {
        WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        return BAD_FUNC_ARG;
    }
    XMEMCPY(output + i, footer, (size_t)footerLen);

    WC_FREE_VAR_EX(footer, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    return outLen + (int)headerLen + (int)footerLen;
}

#endif /* WOLFSSL_DER_TO_PEM */

#ifdef WOLFSSL_PEM_TO_DER

/* Remove PEM header/footer, convert to ASN1, store any encrypted data
   info->consumed tracks of PEM bytes consumed in case multiple parts */
int PemToDer(const unsigned char* buff, long longSz, int type,
              DerBuffer** pDer, void* heap, EncryptedInfo* info, int* keyFormat)
{
    const char* header      = NULL;
    const char* footer      = NULL;
    const char* headerEnd   = NULL;
    const char* footerEnd   = NULL;
    const char* consumedEnd = NULL;
    const char* bufferEnd   = NULL;
    long        neededSz;
    int         ret         = 0;
    word32      sz          = 0;
    int         encrypted_key = 0;
    DerBuffer*  der;
    word32      algId = 0;
    word32      idx;
#ifdef OPENSSL_EXTRA
    char        beginBuf[PEM_LINE_LEN + 1]; /* add 1 for null terminator */
    char        endBuf[PEM_LINE_LEN + 1];   /* add 1 for null terminator */
    int         origType    = type;
#endif
#ifdef WOLFSSL_ENCRYPTED_KEYS
    int hashType = WC_HASH_TYPE_NONE;
#if !defined(NO_MD5)
    hashType = WC_MD5;
#elif !defined(NO_SHA)
    hashType = WC_SHA;
#endif
#endif

    WOLFSSL_ENTER("PemToDer");

    /* Reject negative size - would wrap word32 and corrupt pointer arithmetic. */
    if (longSz < 0) {
        return BAD_FUNC_ARG;
    }

    bufferEnd = (const char*)(buff + longSz);
    sz        = (word32)longSz;

    /* get PEM header and footer based on type */
    ret = wc_PemGetHeaderFooter(type, &header, &footer);
    if (ret != 0)
        return ret;

    /* map header if not found for type */
    for (;;) {
        headerEnd = XSTRNSTR((const char *)buff, header, sz);
        if (headerEnd) {
            break;
        }

        if (type == PRIVATEKEY_TYPE
#ifdef WOLFSSL_DUAL_ALG_CERTS
            || type == ALT_PRIVATEKEY_TYPE
#endif
           ) {
            if (header == BEGIN_RSA_PRIV) {
                header = BEGIN_PRIV_KEY;
                footer = END_PRIV_KEY;
            }
            else if (header == BEGIN_PRIV_KEY) {
                header = BEGIN_ENC_PRIV_KEY;
                footer = END_ENC_PRIV_KEY;
            }
#ifdef HAVE_ECC
            else if (header == BEGIN_ENC_PRIV_KEY) {
                header = BEGIN_EC_PRIV;
                footer = END_EC_PRIV;
            }
            else if (header == BEGIN_EC_PRIV) {
                header = BEGIN_DSA_PRIV;
                footer = END_DSA_PRIV;
            }
#endif
#if defined(HAVE_ED25519) || defined(HAVE_ED448)
    #ifdef HAVE_ECC
            else if (header == BEGIN_DSA_PRIV) {
    #else
            else if (header == BEGIN_ENC_PRIV_KEY) {
    #endif
                header = BEGIN_EDDSA_PRIV;
                footer = END_EDDSA_PRIV;
            }
#endif
            else {
            #ifdef WOLF_PRIVATE_KEY_ID
                /* allow loading a public key for use with crypto or PK callbacks */
                type = PUBLICKEY_TYPE;
                header = BEGIN_PUB_KEY;
                footer = END_PUB_KEY;
            #else
                break;
            #endif
            }
        }
        else if (type == PUBLICKEY_TYPE) {
            if (header == BEGIN_PUB_KEY) {
                header = BEGIN_RSA_PUB;
                footer = END_RSA_PUB;
            }
            else {
                break;
            }
        }
#if defined(HAVE_ECC) && defined(OPENSSL_EXTRA)
        else if (type == ECC_PARAM_TYPE) {
            if (header == BEGIN_EC_PARAM) {
                header = BEGIN_EC_PARAM;
                footer = END_EC_PARAM;
            }
            else {
                break;
            }
        }
#endif
#ifdef HAVE_CRL
        else if ((type == CRL_TYPE) && (header != BEGIN_X509_CRL)) {
            header =  BEGIN_X509_CRL;
            footer = END_X509_CRL;
        }
#endif
        else if (type == CERT_TYPE
              || type == CA_TYPE
              || type == CHAIN_CERT_TYPE
              || type == TRUSTED_PEER_TYPE) {
            if (header == BEGIN_CERT) {
                header = BEGIN_TRUSTED_CERT;
                footer = END_TRUSTED_CERT;
            }
            else {
                break;
            }
        }
        else if (type == TRUSTED_CERT_TYPE) {
            if (header == BEGIN_TRUSTED_CERT) {
                header = BEGIN_CERT;
                footer = END_CERT;
            }
            else {
                break;
            }
        }
        else {
            break;
        }
    }

    if (!headerEnd) {
#ifdef OPENSSL_EXTRA
        if (origType == PRIVATEKEY_TYPE
#ifdef WOLFSSL_DUAL_ALG_CERTS
            || origType == ALT_PRIVATEKEY_TYPE
#endif
           ) {
            /* see if there is a -----BEGIN * PRIVATE KEY----- header */
            headerEnd = XSTRNSTR((char*)buff, PRIV_KEY_SUFFIX, sz);
            if (headerEnd) {
                const char* beginEnd;
                unsigned int endLen;

                beginEnd = headerEnd + XSTR_SIZEOF(PRIV_KEY_SUFFIX);
                if (beginEnd >= (char*)buff + sz) {
                    return BUFFER_E;
                }

                /* back up to BEGIN_PRIV_KEY_PREFIX */
                while (headerEnd > (char*)buff &&
                        XSTRNCMP(headerEnd, BEGIN_PRIV_KEY_PREFIX,
                                XSTR_SIZEOF(BEGIN_PRIV_KEY_PREFIX)) != 0 &&
                        *headerEnd != '\n') {
                    headerEnd--;
                }
                if (headerEnd <= (char*)buff ||
                        XSTRNCMP(headerEnd, BEGIN_PRIV_KEY_PREFIX,
                        XSTR_SIZEOF(BEGIN_PRIV_KEY_PREFIX)) != 0 ||
                        beginEnd - headerEnd > PEM_LINE_LEN) {
                    WOLFSSL_MSG("Couldn't find PEM header");
                    WOLFSSL_ERROR(ASN_NO_PEM_HEADER);
                    return ASN_NO_PEM_HEADER;
                }

                /* headerEnd now points to beginning of header */
                XMEMCPY(beginBuf, headerEnd, (size_t)(beginEnd - headerEnd));
                beginBuf[beginEnd - headerEnd] = '\0';
                /* look for matching footer */
                footer = XSTRNSTR(beginEnd,
                                beginBuf + XSTR_SIZEOF(BEGIN_PRIV_KEY_PREFIX),
                                (unsigned int)((char*)buff + sz - beginEnd));
                if (!footer) {
                    WOLFSSL_MSG("Couldn't find PEM footer");
                    WOLFSSL_ERROR(ASN_NO_PEM_HEADER);
                    return ASN_NO_PEM_HEADER;
                }

                footer -= XSTR_SIZEOF(END_PRIV_KEY_PREFIX);
                if (footer > (char*)buff + sz - XSTR_SIZEOF(END_PRIV_KEY_PREFIX)
                        || XSTRNCMP(footer, END_PRIV_KEY_PREFIX,
                            XSTR_SIZEOF(END_PRIV_KEY_PREFIX)) != 0) {
                    WOLFSSL_MSG("Unexpected footer for PEM");
                    return BUFFER_E;
                }

                endLen = (unsigned int)((size_t)(beginEnd - headerEnd) -
                            (XSTR_SIZEOF(BEGIN_PRIV_KEY_PREFIX) -
                                    XSTR_SIZEOF(END_PRIV_KEY_PREFIX)));
                XMEMCPY(endBuf, footer, (size_t)endLen);
                endBuf[endLen] = '\0';

                header = beginBuf;
                footer = endBuf;
                headerEnd = beginEnd;
            }
        }

        if (!headerEnd) {
            WOLFSSL_MSG("Couldn't find PEM header");
            WOLFSSL_ERROR(ASN_NO_PEM_HEADER);
            return ASN_NO_PEM_HEADER;
        }
#else
        WOLFSSL_MSG("Couldn't find PEM header");
        return ASN_NO_PEM_HEADER;
#endif
    } else {
        headerEnd += XSTRLEN(header);
    }

    /* eat end of line characters */
    headerEnd = SkipEndOfLineChars(headerEnd, bufferEnd);

    if (keyFormat) {
        /* keyFormat is Key_Sum enum */
        if (type == PRIVATEKEY_TYPE
        #ifdef WOLFSSL_DUAL_ALG_CERTS
            || type == ALT_PRIVATEKEY_TYPE
        #endif
           ) {
        #ifndef NO_RSA
            if (header == BEGIN_RSA_PRIV)
                *keyFormat = RSAk;
        #endif
        #ifdef HAVE_ECC
            if (header == BEGIN_EC_PRIV)
                *keyFormat = ECDSAk;
        #endif
        #ifndef NO_DSA
            if (header == BEGIN_DSA_PRIV)
                *keyFormat = DSAk;
        #endif
        }
    #ifdef WOLF_PRIVATE_KEY_ID
        else if (type == PUBLICKEY_TYPE) {
        #ifndef NO_RSA
            if (header == BEGIN_RSA_PUB)
                *keyFormat = RSAk;
        #endif
        }
    #endif
    }

#ifdef WOLFSSL_ENCRYPTED_KEYS
    if (info) {
        ret = wc_EncryptedInfoParse(info, &headerEnd,
                                    (size_t)(bufferEnd - headerEnd));
        if (ret < 0)
            return ret;
        if (info->set)
            encrypted_key = 1;
    }
#endif /* WOLFSSL_ENCRYPTED_KEYS */

    /* find footer */
    footerEnd = XSTRNSTR(headerEnd, footer,
        (size_t)((const char*)buff + sz - headerEnd));
    if (!footerEnd) {
        if (info)
            info->consumed = longSz; /* No more certs if no footer */
        return BUFFER_E;
    }

    consumedEnd = footerEnd + XSTRLEN(footer);

    if (consumedEnd < bufferEnd) { /* handle no end of line on last line */
        /* eat end of line characters */
        consumedEnd = SkipEndOfLineChars(consumedEnd, bufferEnd);
        /* skip possible null term */
        if (consumedEnd < bufferEnd && consumedEnd[0] == '\0')
            consumedEnd++;
    }

    if (info)
        info->consumed = (long)(consumedEnd - (const char*)buff);

    /* set up der buffer */
    neededSz = (long)(footerEnd - headerEnd);
    if (neededSz > (long)sz || neededSz <= 0)
        return BUFFER_E;

    ret = AllocDer(pDer, (word32)neededSz, type, heap);
    if (ret < 0) {
        return ret;
    }
    der = *pDer;

    switch (type) {
    case PUBLICKEY_TYPE:
    case ECC_PUBLICKEY_TYPE:
    case RSA_PUBLICKEY_TYPE:
    case CERT_TYPE:
    case TRUSTED_CERT_TYPE:
    case CRL_TYPE:
        if (Base64_Decode_nonCT((const byte*)headerEnd, (word32)neededSz,
                          der->buffer, &der->length) < 0)
        {
            WOLFSSL_ERROR(BUFFER_E);
            return BUFFER_E;
        }
        break;
    default:
        if (Base64_Decode((const byte*)headerEnd, (word32)neededSz,
                          der->buffer, &der->length) < 0) {
            WOLFSSL_ERROR(BUFFER_E);
            return BUFFER_E;
        }
        break;
    }

    if ((header == BEGIN_PRIV_KEY
#ifdef OPENSSL_EXTRA
         || header == beginBuf
#endif
#ifdef HAVE_ECC
         || header == BEGIN_EC_PRIV
#endif
        ) && !encrypted_key)
    {
        /* detect pkcs8 key and get alg type */
        /* keep PKCS8 header */
        idx = 0;
        ret = ToTraditionalInline_ex(der->buffer, &idx, der->length, &algId);
        if (ret >= 0) {
            if (keyFormat)
                *keyFormat = (int)algId;
        }
        else {
            /* ignore failure here and assume key is not pkcs8 wrapped */
        }
        return 0;
    }

#ifdef WOLFSSL_ENCRYPTED_KEYS
    if (encrypted_key || header == BEGIN_ENC_PRIV_KEY) {
        int   passwordSz = NAME_SZ;
        WC_DECLARE_VAR(password, char, NAME_SZ, 0);

        if (!info || !info->passwd_cb) {
            WOLFSSL_MSG("No password callback set");
            WOLFSSL_ERROR_VERBOSE(NO_PASSWORD);
            return NO_PASSWORD;
        }

    #ifdef WOLFSSL_SMALL_STACK
        password = (char*)XMALLOC((size_t)passwordSz, heap, DYNAMIC_TYPE_STRING);
        if (password == NULL) {
            return MEMORY_E;
        }
    #endif

        /* get password */
        ret = info->passwd_cb(password, passwordSz, PEM_PASS_READ,
            info->passwd_userdata);
        if (ret >= 0) {
            passwordSz = ret;
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Add("PEM password", password, passwordSz);
        #endif

            /* convert and adjust length */
            if (header == BEGIN_ENC_PRIV_KEY) {
            #ifndef NO_PWDBASED
                ret = wc_DecryptPKCS8Key(der->buffer, der->length,
                    password, passwordSz);
                if (ret > 0) {
                    /* update length by decrypted content */
                    der->length = (word32)ret;
                    idx = 0;
                    /* detect pkcs8 key and get alg type */
                    /* keep PKCS8 header */
                    ret = ToTraditionalInline_ex(der->buffer, &idx, der->length,
                        &algId);
                    if (ret >= 0) {
                        if (keyFormat)
                            *keyFormat = (int)algId;
                        ret = 0;
                    }
                }
            #else
                WOLFSSL_ERROR_VERBOSE(NOT_COMPILED_IN);
                ret = NOT_COMPILED_IN;
            #endif
            }
            /* decrypt the key */
            else {
                if (passwordSz == 0) {
                    /* The key is encrypted but does not have a password */
                    WOLFSSL_MSG("No password for encrypted key");
                    WOLFSSL_ERROR_VERBOSE(NO_PASSWORD);
                    ret = NO_PASSWORD;
                }
                else {
                #if ((defined(WOLFSSL_ENCRYPTED_KEYS) && !defined(NO_DES3)) || \
                         (!defined(NO_AES) && defined(HAVE_AES_CBC) && \
                          defined(HAVE_AES_DECRYPT))) && \
                        !defined(NO_WOLFSSL_SKIP_TRAILING_PAD)
                    int     padVal = 0;
                #endif

                    ret = wc_BufferKeyDecrypt(info, der->buffer, der->length,
                        (byte*)password, passwordSz, hashType);

#ifndef NO_WOLFSSL_SKIP_TRAILING_PAD
                #ifndef NO_DES3
                    if (info->cipherType == WC_CIPHER_DES3) {
                        /* Assuming there is padding:
                         *      (der->length > 0 && der->length > DES_BLOCK_SIZE &&
                         *       (der->length % DES_BLOCK_SIZE) != 0)
                         * and assuming the last value signifies the number of
                         * padded bytes IE if last value is 0x08 then there are
                         * 8 bytes of padding:
                         *      padVal = der->buffer[der->length-1];
                         * then strip this padding before proceeding:
                         * der->length -= padVal;
                         */
                        if (der->length > DES_BLOCK_SIZE &&
                            (der->length % DES_BLOCK_SIZE) != 0) {
                            padVal = der->buffer[der->length-1];
                            if (padVal < DES_BLOCK_SIZE) {
                                der->length -= (word32)padVal;
                            }
                        }
                    }
                #endif /* !NO_DES3 */
                #if !defined(NO_AES) && defined(HAVE_AES_CBC) && \
                    defined(HAVE_AES_DECRYPT)
                    if (info->cipherType == WC_CIPHER_AES_CBC) {
                        if (der->length > WC_AES_BLOCK_SIZE) {
                            padVal = der->buffer[der->length-1];
                            if (padVal <= WC_AES_BLOCK_SIZE) {
                                der->length -= (word32)padVal;
                            }
                        }
                    }
                #endif
#endif /* !NO_WOLFSSL_SKIP_TRAILING_PAD */
                }
            }
#ifdef OPENSSL_EXTRA
            if (ret) {
                WOLFSSL_PEMerr(0, WOLFSSL_PEM_R_BAD_DECRYPT_E);
            }
#endif
            ForceZero(password, (word32)passwordSz);
        }
#ifdef OPENSSL_EXTRA
        else {
            WOLFSSL_PEMerr(0, WOLFSSL_PEM_R_BAD_PASSWORD_READ_E);
        }
#endif

    #ifdef WOLFSSL_SMALL_STACK
        XFREE(password, heap, DYNAMIC_TYPE_STRING);
    #elif defined(WOLFSSL_CHECK_MEM_ZERO)
        wc_MemZero_Check(password, NAME_SZ);
    #endif
    }
#endif /* WOLFSSL_ENCRYPTED_KEYS */

    return ret;
}

int wc_PemToDer(const unsigned char* buff, long longSz, int type,
              DerBuffer** pDer, void* heap, EncryptedInfo* info, int* keyFormat)
{
    int ret;

    if (buff == NULL || longSz <= 0) {
        WOLFSSL_MSG("Bad pem der args");
        return BAD_FUNC_ARG;
    }

    ret = PemToDer(buff, longSz, type, pDer, heap, info, keyFormat);
#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)
    if (ret == 0 && type == PRIVATEKEY_TYPE) {
        DerBuffer* der = *pDer;
        /* if a PKCS8 key header exists remove it */
        ret = ToTraditional(der->buffer, der->length);
        if (ret >= 0) {
            der->length = (word32)ret;
        }
        ret = 0; /* ignore error removing PKCS8 header */
    }
#endif
    return ret;
}

#ifdef WOLFSSL_ENCRYPTED_KEYS
/* our KeyPemToDer password callback, password in userData */
static int KeyPemToDerPassCb(char* passwd, int sz, int rw, void* userdata)
{
    (void)rw;

    if (userdata == NULL)
        return 0;

    XSTRLCPY(passwd, (char*)userdata, (size_t)sz);
    return (int)min((word32)(sz - 1), (word32)XSTRLEN((char*)userdata));
}
#endif

/* Return bytes written to buff or < 0 for error */
int wc_KeyPemToDer(const unsigned char* pem, int pemSz,
                        unsigned char* buff, int buffSz, const char* pass)
{
    int ret;
    DerBuffer* der = NULL;
    WC_DECLARE_VAR(info, EncryptedInfo, 1, 0);

    WOLFSSL_ENTER("wc_KeyPemToDer");

    if (pem == NULL || (buff != NULL && buffSz <= 0) || pemSz <= 0) {
        WOLFSSL_MSG("Bad pem der args");
        return BAD_FUNC_ARG;
    }

    WC_ALLOC_VAR_EX(info, EncryptedInfo, 1, NULL, DYNAMIC_TYPE_ENCRYPTEDINFO,
        return MEMORY_E);

    XMEMSET(info, 0, sizeof(EncryptedInfo));
#ifdef WOLFSSL_ENCRYPTED_KEYS
    info->passwd_cb = KeyPemToDerPassCb;
    /* if user passes readonly data, user must only access it readonly. */
    info->passwd_userdata = (void*)(wc_ptr_t)pass;
#else
    (void)pass;
#endif

    ret = PemToDer(pem, pemSz, PRIVATEKEY_TYPE, &der, NULL, info, NULL);

    WC_FREE_VAR_EX(info, NULL, DYNAMIC_TYPE_ENCRYPTEDINFO);

    if (ret < 0 || der == NULL) {
        WOLFSSL_MSG("Bad Pem To Der");
    }
    else if (buff == NULL) {
        WOLFSSL_MSG("Return needed der buff length");
        ret = (int)der->length;
    }
    else if (der->length <= (word32)buffSz) {
        XMEMCPY(buff, der->buffer, der->length);
        ret = (int)der->length;
    }
    else {
        WOLFSSL_MSG("Bad der length");
        ret = BAD_FUNC_ARG;
    }

    FreeDer(&der);
    return ret;
}


/* Return bytes written to buff or < 0 for error */
int wc_CertPemToDer(const unsigned char* pem, int pemSz,
                        unsigned char* buff, int buffSz, int type)
{
    int ret;
    DerBuffer* der = NULL;

    WOLFSSL_ENTER("wc_CertPemToDer");

    if (pem == NULL || buff == NULL || buffSz <= 0 || pemSz <= 0) {
        WOLFSSL_MSG("Bad pem der args");
        return BAD_FUNC_ARG;
    }

    if (type != CERT_TYPE && type != CHAIN_CERT_TYPE && type != CA_TYPE &&
            type != CERTREQ_TYPE && type != PKCS7_TYPE) {
        WOLFSSL_MSG("Bad cert type");
        return BAD_FUNC_ARG;
    }


    ret = PemToDer(pem, pemSz, type, &der, NULL, NULL, NULL);
    if (ret < 0 || der == NULL) {
        WOLFSSL_MSG("Bad Pem To Der");
    }
    else {
        if (der->length <= (word32)buffSz) {
            XMEMCPY(buff, der->buffer, der->length);
            ret = (int)der->length;
        }
        else {
            WOLFSSL_MSG("Bad der length");
            ret = BAD_FUNC_ARG;
        }
    }

    FreeDer(&der);
    return ret;
}

#endif /* WOLFSSL_PEM_TO_DER */
#endif /* WOLFSSL_PEM_TO_DER || WOLFSSL_DER_TO_PEM */


#ifdef WOLFSSL_PEM_TO_DER
#if defined(WOLFSSL_CERT_EXT) || defined(WOLFSSL_PUB_PEM_TO_DER)
/* Return bytes written to buff, needed buff size if buff is NULL, or less than
   zero for error */
int wc_PubKeyPemToDer(const unsigned char* pem, int pemSz,
                           unsigned char* buff, int buffSz)
{
    int ret;
    DerBuffer* der = NULL;

    WOLFSSL_ENTER("wc_PubKeyPemToDer");

    if (pem == NULL || (buff != NULL && buffSz <= 0) || pemSz <= 0) {
        WOLFSSL_MSG("Bad pem der args");
        return BAD_FUNC_ARG;
    }

    ret = PemToDer(pem, pemSz, PUBLICKEY_TYPE, &der, NULL, NULL, NULL);
    if (ret < 0 || der == NULL) {
        WOLFSSL_MSG("Bad Pem To Der");
    }
    else if (buff == NULL) {
        WOLFSSL_MSG("Return needed der buff length");
        ret = (int)der->length;
    }
    else if (der->length <= (word32)buffSz) {
        XMEMCPY(buff, der->buffer, der->length);
        ret = (int)der->length;
    }
    else {
        WOLFSSL_MSG("Bad der length");
        ret = BAD_FUNC_ARG;
    }

    FreeDer(&der);
    return ret;
}
#endif /* WOLFSSL_CERT_EXT || WOLFSSL_PUB_PEM_TO_DER */
#endif /* WOLFSSL_PEM_TO_DER */

#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_PEM_TO_DER)

#ifdef WOLFSSL_CERT_GEN
int wc_PemCertToDer_ex(const char* fileName, DerBuffer** der)
{
#ifndef WOLFSSL_SMALL_STACK
    byte   staticBuffer[FILE_BUFFER_SIZE];
#endif
    byte*  fileBuf = NULL;
    int    ret     = 0;
    XFILE  file    = XBADFILE;
    int    dynamic = 0;
    long   sz      = 0;

    WOLFSSL_ENTER("wc_PemCertToDer");

    if (fileName == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else {
        file = XFOPEN(fileName, "rb");
        if (file == XBADFILE) {
            ret = IO_FAILED_E;
        }
    }

    if (ret == 0) {
        if (XFSEEK(file, 0, XSEEK_END) != 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
        sz = XFTELL(file);
        if (sz <= 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
        if (XFSEEK(file, 0, XSEEK_SET) != 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
#ifndef WOLFSSL_SMALL_STACK
        if (sz <= (long)sizeof(staticBuffer))
            fileBuf = staticBuffer;
        else
#endif
        {
            fileBuf = (byte*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_FILE);
            if (fileBuf == NULL)
                ret = MEMORY_E;
            else
                dynamic = 1;
        }
    }
    if (ret == 0) {
        if ((size_t)XFREAD(fileBuf, 1, (size_t)sz, file) != (size_t)sz) {
            ret = IO_FAILED_E;
        }
        else {
            ret = PemToDer(fileBuf, sz, CA_TYPE, der,  0, NULL,NULL);
        }
    }

    if (file != XBADFILE)
        XFCLOSE(file);
    if (dynamic)
        XFREE(fileBuf, NULL, DYNAMIC_TYPE_FILE);

    return ret;
}
/* load pem cert from file into der buffer, return der size or error */
int wc_PemCertToDer(const char* fileName, unsigned char* derBuf, int derSz)
{
    int ret;
    DerBuffer* converted = NULL;
    ret = wc_PemCertToDer_ex(fileName, &converted);
    if (ret == 0) {
        if (converted->length < (word32)derSz) {
            XMEMCPY(derBuf, converted->buffer, converted->length);
            ret = (int)converted->length;
        }
        else
            ret = BUFFER_E;

        FreeDer(&converted);
    }
    return ret;
}
#endif /* WOLFSSL_CERT_GEN */

#if defined(WOLFSSL_CERT_EXT) || defined(WOLFSSL_PUB_PEM_TO_DER)
/* load pem public key from file into der buffer, return der size or error */
int wc_PemPubKeyToDer_ex(const char* fileName, DerBuffer** der)
{
#ifndef WOLFSSL_SMALL_STACK
    byte   staticBuffer[FILE_BUFFER_SIZE];
#endif
    byte*  fileBuf = NULL;
    int    dynamic = 0;
    int    ret     = 0;
    long   sz      = 0;
    XFILE  file    = XBADFILE;

    WOLFSSL_ENTER("wc_PemPubKeyToDer");

    if (fileName == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else {
        file = XFOPEN(fileName, "rb");
        if (file == XBADFILE) {
            ret = IO_FAILED_E;
        }
    }

    if (ret == 0) {
        if (XFSEEK(file, 0, XSEEK_END) != 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
        sz = XFTELL(file);
        if (sz <= 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
        if (XFSEEK(file, 0, XSEEK_SET) != 0) {
            ret = IO_FAILED_E;
        }
    }
    if (ret == 0) {
#ifndef WOLFSSL_SMALL_STACK
        if (sz <= (long)sizeof(staticBuffer))
            fileBuf = staticBuffer;
        else
#endif
        {
            fileBuf = (byte*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_FILE);
            if (fileBuf == NULL)
                ret = MEMORY_E;
            else
                dynamic = 1;
        }
    }
    if (ret == 0) {
        if ((size_t)XFREAD(fileBuf, 1, (size_t)sz, file) != (size_t)sz) {
            ret = BUFFER_E;
        }
        else {
            ret = PemToDer(fileBuf, sz, PUBLICKEY_TYPE, der,
                           0, NULL, NULL);
        }
    }

    if (file != XBADFILE)
        XFCLOSE(file);
    if (dynamic)
        XFREE(fileBuf, NULL, DYNAMIC_TYPE_FILE);

    return ret;
}
/* load pem public key from file into der buffer, return der size or error */
int wc_PemPubKeyToDer(const char* fileName,
                           unsigned char* derBuf, int derSz)
{
    int ret;
    DerBuffer* converted = NULL;
    ret = wc_PemPubKeyToDer_ex(fileName, &converted);
    if (ret == 0) {
        if (converted->length < (word32)derSz) {
            XMEMCPY(derBuf, converted->buffer, converted->length);
            ret = (int)converted->length;
        }
        else
            ret = BUFFER_E;

        FreeDer(&converted);
    }
    return ret;
}
#endif /* WOLFSSL_CERT_EXT || WOLFSSL_PUB_PEM_TO_DER */

#endif /* !NO_FILESYSTEM && WOLFSSL_PEM_TO_DER */

/* Get public key in DER format from a populated DecodedCert struct.
 *
 * Users must call wc_InitDecodedCert() and wc_ParseCert() before calling
 * this API. wc_InitDecodedCert() accepts a DER/ASN.1 encoded certificate.
 * To convert a PEM cert to DER first use wc_CertPemToDer() before calling
 * wc_InitDecodedCert().
 *
 * cert   - populated DecodedCert struct holding X.509 certificate
 * derKey - output buffer to place DER/ASN.1 encoded public key
 * derKeySz [IN/OUT] - size of derKey buffer on input, size of public key
 *                     on return. If derKey is passed in as NULL, derKeySz
 *                     will be set to required buffer size for public key
 *                     and LENGTH_ONLY_E will be returned from function.
 * Returns 0 on success, or negative error code on failure. LENGTH_ONLY_E
 * if derKey is NULL and returning length only.
 */
int wc_GetPubKeyDerFromCert(struct DecodedCert* cert,
                            byte* derKey, word32* derKeySz)
{
    int ret = 0;

    /* derKey may be NULL to return length only */
    if (cert == NULL || derKeySz == NULL ||
        (derKey != NULL && *derKeySz == 0)) {
        return BAD_FUNC_ARG;
    }

    if (cert->publicKey == NULL) {
        WOLFSSL_MSG("DecodedCert does not contain public key\n");
        return BAD_FUNC_ARG;
    }

    /* if derKey is NULL, return required output buffer size in derKeySz */
    if (derKey == NULL) {
        *derKeySz = cert->pubKeySize;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    if (ret == 0) {
        if (cert->pubKeySize > *derKeySz) {
            WOLFSSL_MSG("Output buffer not large enough for public key DER");
            ret = BAD_FUNC_ARG;
        }
        else {
            XMEMCPY(derKey, cert->publicKey, cert->pubKeySize);
            *derKeySz = cert->pubKeySize;
        }
    }

    return ret;
}

#ifdef WOLFSSL_FPKI
/* Search through list for first matching alt name of the same type
 * If 'current' is null then the search starts at the head of the list
 * otherwise the search starts from the node after 'current' alt name.
 * Returns 0 on success
 */
static DNS_entry* FindAltName(struct DecodedCert* cert, int nameType,
    DNS_entry* current)
{
    DNS_entry* entry;

    if (current == NULL) {
        entry = cert->altNames;
    }
    else {
        entry = current->next;
    }

    /* cycle through alt names to check for needed types */
    while (entry != NULL) {
        if (entry->type == nameType) {
            break;
        }
        entry = entry->next;
    }

    return entry;
}


/* returns 0 on success */
int wc_GetUUIDFromCert(struct DecodedCert* cert, byte* uuid, word32* uuidSz)
{
    int ret = WC_NO_ERR_TRACE(ALT_NAME_E);
    DNS_entry* id = NULL;

    do {
        id = FindAltName(cert, ASN_URI_TYPE, id);
        if (id != NULL) {
            /* check if URI string matches expected format for UUID */
            if (id->len != DEFAULT_UUID_SZ) {
                continue; /* size not right not a UUID URI */
            }

            if (XMEMCMP(id->name, "urn:uuid:", 9) != 0) {
                continue; /* beginning text not right for a UUID URI */
            }

            if (uuid == NULL) {
                *uuidSz = (word32)id->len;
                return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
            }

            if ((int)*uuidSz < id->len) {
                return BUFFER_E;
            }

            XMEMCPY(uuid, id->name, (size_t)id->len);
            ret = 0; /* success */
            break;
        }
    } while (id != NULL);

    return ret;
}


/* returns 0 on success */
int wc_GetFASCNFromCert(struct DecodedCert* cert, byte* fascn, word32* fascnSz)
{
    int ret = WC_NO_ERR_TRACE(ALT_NAME_E);
    DNS_entry* id = NULL;

    do {
        id = FindAltName(cert, ASN_OTHER_TYPE, id);
        if (id != NULL && id->oidSum == FASCN_OID) {
            if (fascn == NULL) {
                *fascnSz = (word32)id->len;
                return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
            }

            if ((int)*fascnSz < id->len) {
                return BUFFER_E;
            }

            XMEMCPY(fascn, id->name, (size_t)id->len);
            ret = 0; /* success */
        }
    } while (id != NULL);

    return ret;
}
#endif /* WOLFSSL_FPKI */

#if !defined(NO_RSA) && \
    (defined(WOLFSSL_KEY_TO_DER) || defined(WOLFSSL_CERT_GEN))
/* USER RSA ifdef portions used instead of refactor in consideration for
   possible fips build */
/* Encode a public RSA key to output.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 *
 * Encoded data can either be SubjectPublicKeyInfo (with header) or just the key
 * (RSAPublicKey).
 *
 * @param [out] output       Buffer to put encoded data in.
 * @param [in]  key          RSA key object.
 * @param [in]  outLen       Size of the output buffer in bytes.
 * @param [in]  with_header  Whether to include SubjectPublicKeyInfo around key.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when output or key is NULL, or outLen is less than
 *          minimum length (5 bytes).
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int SetRsaPublicKey(byte* output, RsaKey* key, int outLen,
                           int with_header)
{
    DECL_ASNSETDATA(dataASN, rsaPublicKeyASN_Length);
    word32 sz = 0;
    int ret = 0;
    int o = 0;

    /* Check parameter validity. */
    if ((key == NULL) || ((output != NULL) && (outLen < MAX_SEQ_SZ))) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, rsaPublicKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        if (!with_header) {
            /* Start encoding with items after header. */
            o = RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ;
        }
        /* Set OID for RSA key. */
        SetASN_OID(&dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID], RSAk, oidKeyType);
    #ifdef WC_RSA_PSS
        dataASN[RSAPUBLICKEYASN_IDX_ALGOID_P_SEQ].noOut = 1;
    #endif
        /* Set public key mp_ints. */
        SetASN_MP(&dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_N], &key->n);
        SetASN_MP(&dataASN[RSAPUBLICKEYASN_IDX_PUBKEY_RSA_E], &key->e);
        /* Calculate size of RSA public key. */
        ret = SizeASN_Items(rsaPublicKeyASN + o, dataASN + o,
                            (int)rsaPublicKeyASN_Length - o, &sz);
    }
    /* Check output buffer is big enough for encoding. */
    if ((ret == 0) && (output != NULL) && (sz > (word32)outLen)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode RSA public key. */
        SetASN_Items(rsaPublicKeyASN + o, dataASN + o,
                     (int)rsaPublicKeyASN_Length - o, output);
    }
    if (ret == 0) {
        /* Return size of encoding. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Calculate size of encoded public RSA key in bytes.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 *
 * Encoded data can either be SubjectPublicKeyInfo (with header) or just the key
 * (RSAPublicKey).
 *
 * @param [in]  key          RSA key object.
 * @param [in]  with_header  Whether to include SubjectPublicKeyInfo around key.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
int wc_RsaPublicKeyDerSize(RsaKey* key, int with_header)
{
    return SetRsaPublicKey(NULL, key, 0, with_header);
}

/* Encode public RSA key in DER format.
 *
 * X.509: RFC 5280, 4.1 - SubjectPublicKeyInfo
 * PKCS #1: RFC 8017, A.1.1 - RSAPublicKey
 *
 * @param [in]  key     RSA key object.
 * @param [out] output  Buffer to put encoded data in.
 * @param [in]  outLen  Size of buffer in bytes.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key or output is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
int wc_RsaKeyToPublicDer(RsaKey* key, byte* output, word32 outLen)
{
    return SetRsaPublicKey(output, key, (int)outLen, 1);
}

/* Returns public DER version of the RSA key. If with_header is 0 then only a
 * seq + n + e is returned in ASN.1 DER format */
int wc_RsaKeyToPublicDer_ex(RsaKey* key, byte* output, word32 outLen,
    int with_header)
{
    return SetRsaPublicKey(output, key, (int)outLen, with_header);
}

#endif /* !NO_RSA && WOLFSSL_KEY_TO_DER */
#endif /* NO_CERTS */

#if !defined(NO_RSA) && defined(WOLFSSL_KEY_TO_DER)

/* Encode private RSA key in DER format.
 *
 * PKCS #1: RFC 8017, A.1.2 - RSAPrivateKey
 *
 * @param [in]  key     RSA key object.
 * @param [out] output  Buffer to put encoded data in.
 * @param [in]  outLen  Size of buffer in bytes.
 * @return  Size of encoded data in bytes on success.
 * @return  BAD_FUNC_ARG when key is NULL or not a private key.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_RsaKeyToDer(RsaKey* key, byte* output, word32 outLen)
{
    DECL_ASNSETDATA(dataASN, rsaKeyASN_Length);
    int i;
    word32 sz = 0;
    int ret = 0;

    if ((key == NULL) || (key->type != RSA_PRIVATE)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, rsaKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        /* Set the version. */
        SetASN_Int8Bit(&dataASN[RSAKEYASN_IDX_VER], 0);
        /* Set all the mp_ints in private key. */
        for (i = 0; i < RSA_INTS; i++) {
            SetASN_MP(&dataASN[(byte)RSAKEYASN_IDX_N + i], GetRsaInt(key, i));
        }

        /* Calculate size of RSA private key encoding. */
        ret = SizeASN_Items(rsaKeyASN, dataASN, rsaKeyASN_Length, &sz);
    }
    /* Check output buffer has enough space for encoding. */
    if ((ret == 0) && (output != NULL) && (sz > outLen)) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode RSA private key. */
        SetASN_Items(rsaKeyASN, dataASN, rsaKeyASN_Length, output);
    }

    if (ret == 0) {
        /* Return size of encoding. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_RSA && WOLFSSL_KEY_TO_DER */

#ifndef NO_CERTS

#ifdef WOLFSSL_CERT_GEN

/* Initialize and Set Certificate defaults:
   version    = 3 (0x2)
   serial     = 0
   sigType    = SHA_WITH_RSA
   issuer     = blank
   daysValid  = 500
   selfSigned = 1 (true) use subject as issuer
   subject    = blank
*/
int wc_InitCert_ex(Cert* cert, void* heap, int devId)
{
#ifdef WOLFSSL_MULTI_ATTRIB
    int i = 0;
#endif
    if (cert == NULL) {
        return BAD_FUNC_ARG;
    }

    XMEMSET(cert, 0, sizeof(Cert));

    cert->version    = 2;   /* version 3 is hex 2 */
#if !defined(NO_SHA256)
    cert->sigType    = CTC_SHA256wRSA;
#elif defined(WOLFSSL_SHA384)
    cert->sigType    = CTC_SHA384wRSA;
#elif defined(WOLFSSL_SHA512)
    cert->sigType    = CTC_SHA512wRSA;
#elif defined(WOLFSSL_SHA224)
    cert->sigType    = CTC_SHA224wRSA;
#elif !defined(NO_SHA)
    cert->sigType    = CTC_SHAwRSA;
#else
    cert->sigType    = 0;
#endif
    cert->daysValid  = 500;
    cert->selfSigned = 1;
    cert->keyType    = RSA_KEY;

    cert->issuer.countryEnc = CTC_PRINTABLE;
    cert->issuer.stateEnc = CTC_UTF8;
    cert->issuer.streetEnc = CTC_UTF8;
    cert->issuer.localityEnc = CTC_UTF8;
    cert->issuer.surEnc = CTC_UTF8;
#ifdef WOLFSSL_CERT_NAME_ALL
    cert->issuer.givenNameEnc = CTC_UTF8;
    cert->issuer.initialsEnc = CTC_UTF8;
    cert->issuer.dnQualifierEnc = CTC_UTF8;
    cert->issuer.dnNameEnc = CTC_UTF8;
#endif
    cert->issuer.orgEnc = CTC_UTF8;
    cert->issuer.unitEnc = CTC_UTF8;
    cert->issuer.commonNameEnc = CTC_UTF8;
    cert->issuer.serialDevEnc = CTC_PRINTABLE;
    cert->issuer.userIdEnc = CTC_UTF8;
    cert->issuer.postalCodeEnc = CTC_UTF8;
#ifdef WOLFSSL_CERT_EXT
    cert->issuer.busCatEnc = CTC_UTF8;
    cert->issuer.joiCEnc = CTC_UTF8;
    cert->issuer.joiStEnc = CTC_UTF8;
#endif

    cert->subject.countryEnc = CTC_PRINTABLE;
    cert->subject.stateEnc = CTC_UTF8;
    cert->subject.streetEnc = CTC_UTF8;
    cert->subject.localityEnc = CTC_UTF8;
    cert->subject.surEnc = CTC_UTF8;
#ifdef WOLFSSL_CERT_NAME_ALL
    cert->subject.givenNameEnc = CTC_UTF8;
    cert->subject.initialsEnc = CTC_UTF8;
    cert->subject.dnQualifierEnc = CTC_UTF8;
    cert->subject.dnNameEnc = CTC_UTF8;
#endif
    cert->subject.orgEnc = CTC_UTF8;
    cert->subject.unitEnc = CTC_UTF8;
    cert->subject.commonNameEnc = CTC_UTF8;
    cert->subject.serialDevEnc = CTC_PRINTABLE;
    cert->subject.userIdEnc = CTC_UTF8;
    cert->subject.postalCodeEnc = CTC_UTF8;
#ifdef WOLFSSL_CERT_EXT
    cert->subject.busCatEnc = CTC_UTF8;
    cert->subject.joiCEnc = CTC_UTF8;
    cert->subject.joiStEnc = CTC_UTF8;
#endif

#ifdef WOLFSSL_MULTI_ATTRIB
    for (i = 0; i < CTC_MAX_ATTRIB; i++) {
        cert->issuer.name[i].type   = CTC_UTF8;
        cert->subject.name[i].type  = CTC_UTF8;
    }
#endif /* WOLFSSL_MULTI_ATTRIB */

    cert->heap = heap;
    (void)devId; /* future */

    return 0;
}

WOLFSSL_ABI
int wc_InitCert(Cert* cert)
{
    return wc_InitCert_ex(cert, NULL, INVALID_DEVID);
}

WOLFSSL_ABI
Cert* wc_CertNew(void* heap)
{
    Cert* certNew;

    certNew = (Cert*)XMALLOC(sizeof(Cert), heap, DYNAMIC_TYPE_CERT);

    if (certNew) {
        if (wc_InitCert_ex(certNew, heap, INVALID_DEVID) != 0) {
            XFREE(certNew, heap, DYNAMIC_TYPE_CERT);
            certNew = NULL;
        }
    }

    return certNew;
}

WOLFSSL_ABI
void  wc_CertFree(Cert* cert)
{
    if (cert) {
         void* heap = cert->heap;

         ForceZero(cert, sizeof(Cert));
         XFREE(cert, heap, DYNAMIC_TYPE_CERT);
         (void)heap;
     }
}

/* DER encoded x509 Certificate */
typedef struct DerCert {
    byte size[MAX_LENGTH_SZ];          /* length encoded */
    byte version[MAX_VERSION_SZ];      /* version encoded */
    byte serial[(int)CTC_SERIAL_SIZE + (int)MAX_LENGTH_SZ]; /* serial number encoded */
    byte sigAlgo[MAX_ALGO_SZ];         /* signature algo encoded */
    byte issuer[WC_ASN_NAME_MAX];         /* issuer  encoded */
    byte subject[WC_ASN_NAME_MAX];        /* subject encoded */
    byte validity[MAX_DATE_SIZE*2 + MAX_SEQ_SZ*2];  /* before and after dates */
    byte publicKey[MAX_PUBLIC_KEY_SZ]; /* rsa public key encoded */
    byte ca[MAX_CA_SZ];                /* basic constraint CA true size */
    byte extensions[MAX_EXTENSIONS_SZ]; /* all extensions */
#ifdef WOLFSSL_CERT_EXT
    byte skid[MAX_KID_SZ];             /* Subject Key Identifier extension */
    byte akid[MAX_KID_SZ
#ifdef WOLFSSL_AKID_NAME
              + sizeof(CertName) + CTC_SERIAL_SIZE
#endif
              ]; /* Authority Key Identifier extension */
    byte keyUsage[MAX_KEYUSAGE_SZ];    /* Key Usage extension */
    byte extKeyUsage[MAX_EXTKEYUSAGE_SZ]; /* Extended Key Usage extension */
#ifndef IGNORE_NETSCAPE_CERT_TYPE
    byte nsCertType[MAX_NSCERTTYPE_SZ]; /* Extended Key Usage extension */
#endif
    byte certPolicies[MAX_CERTPOL_NB*MAX_CERTPOL_SZ]; /* Certificate Policies */
    byte crlInfo[CTC_MAX_CRLINFO_SZ];  /* CRL Distribution Points */
#ifdef WOLFSSL_ACME_OID
    byte acmeId[MAX_ACMEID_SZ];        /* RFC 8737 id-pe-acmeIdentifier */
#endif
#endif
#ifdef WOLFSSL_CERT_REQ
    byte attrib[MAX_ATTRIB_SZ];        /* Cert req attributes encoded */
    #ifdef WOLFSSL_CUSTOM_OID
    byte extCustom[MAX_ATTRIB_SZ];     /* Encoded user oid and value */
    #endif
#endif
#ifdef WOLFSSL_ALT_NAMES
    byte altNames[CTC_MAX_ALT_SIZE];   /* Alternative Names encoded */
#endif
    int  sizeSz;                       /* encoded size length */
    int  versionSz;                    /* encoded version length */
    int  serialSz;                     /* encoded serial length */
    int  sigAlgoSz;                    /* encoded sig algo length */
    int  issuerSz;                     /* encoded issuer length */
    int  subjectSz;                    /* encoded subject length */
    int  validitySz;                   /* encoded validity length */
    int  publicKeySz;                  /* encoded public key length */
    int  caSz;                         /* encoded CA extension length */
#ifdef WOLFSSL_CERT_EXT
    int  skidSz;                       /* encoded SKID extension length */
    int  akidSz;                       /* encoded SKID extension length */
    int  keyUsageSz;                   /* encoded KeyUsage extension length */
    int  extKeyUsageSz;                /* encoded ExtendedKeyUsage extension length */
#ifndef IGNORE_NETSCAPE_CERT_TYPE
    int  nsCertTypeSz;                 /* encoded Netscape Certificate Type
                                        * extension length */
#endif
    int  certPoliciesSz;               /* encoded CertPolicies extension length*/
    int  crlInfoSz;                    /* encoded CRL Dist Points length */
#ifdef WOLFSSL_ACME_OID
    int  acmeIdSz;                     /* encoded acmeIdentifier length */
#endif
#endif
#ifdef WOLFSSL_ALT_NAMES
    int  altNamesSz;                   /* encoded AltNames extension length */
#endif
    int  extensionsSz;                 /* encoded extensions total length */
    int  total;                        /* total encoded lengths */
#ifdef WOLFSSL_CERT_REQ
    int  attribSz;
    #ifdef WOLFSSL_CUSTOM_OID
    int  extCustomSz;
    #endif
#endif
} DerCert;


#ifdef WOLFSSL_CERT_REQ
#endif /* WOLFSSL_CERT_REQ */


#ifndef WOLFSSL_CERT_GEN_CACHE
/* wc_SetCert_Free is only public when WOLFSSL_CERT_GEN_CACHE is not defined */
static
#endif
WOLFSSL_ABI
void wc_SetCert_Free(Cert* cert)
{
    if (cert != NULL) {
        cert->der = NULL;
        if (cert->decodedCert) {
            FreeDecodedCert((DecodedCert*)cert->decodedCert);

            XFREE(cert->decodedCert, cert->heap, DYNAMIC_TYPE_DCERT);
            cert->decodedCert = NULL;
        }
    }
}

static int wc_SetCert_LoadDer(Cert* cert, const byte* der, word32 derSz,
    int devId)
{
    int ret;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Allocate DecodedCert struct and Zero */
        cert->decodedCert = (void*)XMALLOC(sizeof(DecodedCert), cert->heap,
            DYNAMIC_TYPE_DCERT);

        if (cert->decodedCert == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMSET(cert->decodedCert, 0, sizeof(DecodedCert));

            InitDecodedCert_ex((DecodedCert*)cert->decodedCert, der, derSz,
                    cert->heap, devId);
            ret = ParseCertRelative((DecodedCert*)cert->decodedCert,
                    CERT_TYPE, 0, NULL, NULL);
            if (ret >= 0) {
                cert->der = der;
            }
            else {
                wc_SetCert_Free(cert);
            }
        }
    }

    return ret;
}

#endif /* WOLFSSL_CERT_GEN */

#ifdef WOLFSSL_CERT_GEN

#ifndef NO_ASN_TIME
static WC_INLINE byte itob(int number)
{
    return (byte)(number + 0x30);
}


/* write time to output, format */
static void SetTime(struct tm* date, byte* output)
{
    int i = 0;

    output[i++] = itob((date->tm_year % 10000) / 1000);
    output[i++] = itob((date->tm_year % 1000)  /  100);
    output[i++] = itob((date->tm_year % 100)   /   10);
    output[i++] = itob( date->tm_year % 10);

    output[i++] = itob(date->tm_mon / 10);
    output[i++] = itob(date->tm_mon % 10);

    output[i++] = itob(date->tm_mday / 10);
    output[i++] = itob(date->tm_mday % 10);

    output[i++] = itob(date->tm_hour / 10);
    output[i++] = itob(date->tm_hour % 10);

    output[i++] = itob(date->tm_min / 10);
    output[i++] = itob(date->tm_min % 10);

    output[i++] = itob(date->tm_sec / 10);
    output[i++] = itob(date->tm_sec % 10);

    output[i] = 'Z';  /* Zulu profile */
}
#endif


/* Simple name OID size. */
#define NAME_OID_SZ     3

/* Domain name OIDs. */
static const byte nameOid[][NAME_OID_SZ] = {
    { 0x55, 0x04, ASN_COUNTRY_NAME },
    { 0x55, 0x04, ASN_STATE_NAME },
    { 0x55, 0x04, ASN_STREET_ADDR },
    { 0x55, 0x04, ASN_LOCALITY_NAME },
#ifdef WOLFSSL_CERT_NAME_ALL
    { 0x55, 0x04, ASN_NAME },
    { 0x55, 0x04, ASN_GIVEN_NAME },
    { 0x55, 0x04, ASN_INITIALS },
    { 0x55, 0x04, ASN_DNQUALIFIER },
#endif
    { 0x55, 0x04, ASN_SUR_NAME },
    { 0x55, 0x04, ASN_ORG_NAME },
    { 0x00, 0x00, ASN_DOMAIN_COMPONENT}, /* not actual OID - see dcOid */
                                         /* list all DC values before OUs */
    { 0x55, 0x04, ASN_ORGUNIT_NAME },
    { 0x55, 0x04, ASN_COMMON_NAME },
    { 0x55, 0x04, ASN_SERIAL_NUMBER },
#ifdef WOLFSSL_CERT_EXT
    { 0x55, 0x04, ASN_BUS_CAT },
#endif
    { 0x55, 0x04, ASN_POSTAL_CODE },
    { 0x00, 0x00, ASN_EMAIL_NAME},       /* not actual OID - see attrEmailOid */
    { 0x00, 0x00, ASN_USER_ID},          /* not actual OID - see uidOid */
#ifdef WOLFSSL_CUSTOM_OID
    { 0x00, 0x00, ASN_CUSTOM_NAME} /* OID comes from CertOidField */
#endif
};
#define NAME_ENTRIES (int)(sizeof(nameOid)/NAME_OID_SZ)


/* Get ASN Name from index */
byte GetCertNameId(int idx)
{
    if (idx < NAME_ENTRIES)
        return nameOid[idx][2];
    return 0;
}

/* Get Which Name from index */
const char* GetOneCertName(CertName* name, int idx)
{
    byte type = GetCertNameId(idx);
    switch (type) {
    case ASN_COUNTRY_NAME:
       return name->country;
    case ASN_STATE_NAME:
       return name->state;
    case ASN_STREET_ADDR:
       return name->street;
    case ASN_LOCALITY_NAME:
       return name->locality;
#ifdef WOLFSSL_CERT_NAME_ALL
    case ASN_NAME:
       return name->dnName;
    case ASN_GIVEN_NAME:
       return name->givenName;
    case ASN_INITIALS:
       return name->initials;
    case ASN_DNQUALIFIER:
       return name->dnQualifier;
#endif /* WOLFSSL_CERT_NAME_ALL */
    case ASN_SUR_NAME:
       return name->sur;
    case ASN_ORG_NAME:
       return name->org;
    case ASN_ORGUNIT_NAME:
       return name->unit;
    case ASN_COMMON_NAME:
       return name->commonName;
    case ASN_SERIAL_NUMBER:
       return name->serialDev;
    case ASN_USER_ID:
       return name->userId;
    case ASN_POSTAL_CODE:
       return name->postalCode;
    case ASN_EMAIL_NAME:
       return name->email;
#ifdef WOLFSSL_CERT_EXT
    case ASN_BUS_CAT:
       return name->busCat;
#endif
#ifdef WOLFSSL_CUSTOM_OID
    case ASN_CUSTOM_NAME:
        return (const char*)name->custom.val;
#endif
    default:
       return NULL;
    }
}


/* Get Which Name Encoding from index */
static char GetNameType(CertName* name, int idx)
{
    byte type = GetCertNameId(idx);
    switch (type) {
    case ASN_COUNTRY_NAME:
       return name->countryEnc;
    case ASN_STATE_NAME:
       return name->stateEnc;
    case ASN_STREET_ADDR:
       return name->streetEnc;
    case ASN_LOCALITY_NAME:
       return name->localityEnc;
#ifdef WOLFSSL_CERT_NAME_ALL
    case ASN_NAME:
       return name->dnNameEnc;
    case ASN_GIVEN_NAME:
       return name->givenNameEnc;
    case ASN_INITIALS:
       return name->initialsEnc;
    case ASN_DNQUALIFIER:
       return name->dnQualifierEnc;
#endif /* WOLFSSL_CERT_NAME_ALL */
    case ASN_SUR_NAME:
       return name->surEnc;
    case ASN_ORG_NAME:
       return name->orgEnc;
    case ASN_ORGUNIT_NAME:
       return name->unitEnc;
    case ASN_COMMON_NAME:
       return name->commonNameEnc;
    case ASN_SERIAL_NUMBER:
       return name->serialDevEnc;
    case ASN_USER_ID:
       return name->userIdEnc;
    case ASN_POSTAL_CODE:
       return name->postalCodeEnc;
    case ASN_EMAIL_NAME:
       return 0; /* special */
#ifdef WOLFSSL_CERT_EXT
    case ASN_BUS_CAT:
       return name->busCatEnc;
#endif
#ifdef WOLFSSL_CUSTOM_OID
    case ASN_CUSTOM_NAME:
        return name->custom.enc;
#endif
    default:
       return 0;
    }
}


#ifdef WOLFSSL_CERT_EXT
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for extended key usage.
 * X.509: RFC 5280, 4.2.12 - Extended Key Usage
 * Dynamic creation of template for encoding.
 */
static const ASNItem ekuASN[] = {
/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* OID */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
};
enum {
    EKUASN_IDX_SEQ = 0,
    EKUASN_IDX_OID
};

/* OIDs corresponding to extended key usage. */
struct {
    const byte* oid;
    word32 oidSz;
} ekuOid[] = {
    { extExtKeyUsageServerAuthOid,   sizeof(extExtKeyUsageServerAuthOid) },
    { extExtKeyUsageClientAuthOid,   sizeof(extExtKeyUsageClientAuthOid) },
    { extExtKeyUsageCodeSigningOid,  sizeof(extExtKeyUsageCodeSigningOid) },
    { extExtKeyUsageEmailProtectOid, sizeof(extExtKeyUsageEmailProtectOid) },
    { extExtKeyUsageTimestampOid,    sizeof(extExtKeyUsageTimestampOid) },
    { extExtKeyUsageOcspSignOid,     sizeof(extExtKeyUsageOcspSignOid) },
};

#define EKU_OID_LO      1
#define EKU_OID_HI      6
#endif /* WOLFSSL_ASN_TEMPLATE */

/* encode Extended Key Usage (RFC 5280 4.2.1.12), return total bytes written */
#ifdef WOLFSSL_ASN_TEMPLATE
static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input)
{
    /* TODO: consider calculating size of OBJECT_IDs, setting length into
     * SEQUENCE, encode SEQUENCE, encode OBJECT_IDs into buffer.  */
    ASNSetData* dataASN;
    ASNItem* extKuASN = NULL;
    int asnIdx = 1;
    size_t cnt = 1 + EKU_OID_HI;
    int i;
    int ret = 0;
    word32 sz = 0;

#ifdef WOLFSSL_EKU_OID
    cnt += CTC_MAX_EKU_NB;
#endif

    /* Allocate memory for dynamic data items. */
    dataASN = (ASNSetData*)XMALLOC(cnt * sizeof(ASNSetData), cert->heap,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
    if (dataASN == NULL) {
        ret = MEMORY_E;
    }
    if (ret == 0) {
        /* Allocate memory for dynamic ASN.1 template. */
        extKuASN = (ASNItem*)XMALLOC(cnt * sizeof(ASNItem), cert->heap,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
        if (extKuASN == NULL) {
            ret = MEMORY_E;
        }
    }

    if (ret == 0) {
        /* Copy Sequence into dynamic ASN.1 template. */
        XMEMCPY(&extKuASN[EKUASN_IDX_SEQ], ekuASN, sizeof(ASNItem));
        /* Clear dynamic data. */
        XMEMSET(dataASN, 0, cnt * sizeof(ASNSetData));

        /* Build up the template and data. */
        /* If 'any' set, then just use it. */
        if ((input & EXTKEYUSE_ANY) == EXTKEYUSE_ANY) {
            /* Set template item. */
            XMEMCPY(&extKuASN[EKUASN_IDX_OID], &ekuASN[EKUASN_IDX_OID],
                    sizeof(ASNItem));
            /* Set data item. */
            SetASN_Buffer(&dataASN[asnIdx], extExtKeyUsageAnyOid,
                sizeof(extExtKeyUsageAnyOid));
            asnIdx++;
        }
        else {
            /* Step through the flagged purposes. */
            for (i = EKU_OID_LO; i <= EKU_OID_HI; i++) {
                if ((input & (1 << i)) != 0) {
                    /* Set template item. */
                    XMEMCPY(&extKuASN[asnIdx], &ekuASN[EKUASN_IDX_OID],
                            sizeof(ASNItem));
                    /* Set data item. */
                    SetASN_Buffer(&dataASN[asnIdx], ekuOid[i - 1].oid,
                        ekuOid[i - 1].oidSz);
                    asnIdx++;
                }
            }
        #ifdef WOLFSSL_EKU_OID
            if (input & EXTKEYUSE_USER) {
                /* Iterate through OID values */
                for (i = 0; i < CTC_MAX_EKU_NB; i++) {
                    sz = cert->extKeyUsageOIDSz[i];
                    if (sz > 0) {
                        /* Set template item. */
                        XMEMCPY(&extKuASN[asnIdx], &ekuASN[EKUASN_IDX_OID],
                                sizeof(ASNItem));
                        /* Set data item. */
                        SetASN_Buffer(&dataASN[asnIdx], cert->extKeyUsageOID[i],
                            sz);
                        asnIdx++;
                    }
                }
            }
        #endif /* WOLFSSL_EKU_OID */
            (void)cert;
        }

        /* Calculate size of encoding. */
        sz = 0;
        ret = SizeASN_Items(extKuASN, dataASN, asnIdx, &sz);
    }
    /* When buffer to write to, ensure it's big enough. */
    if ((ret == 0) && (output != NULL) && (sz > outSz)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode extended key usage. */
        SetASN_Items(extKuASN, dataASN, asnIdx, output);
    }
    if (ret == 0) {
        /* Return the encoding size. */
        ret = (int)sz;
    }

    /* Dispose of allocated data. */
    XFREE(extKuASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(dataASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifndef IGNORE_NETSCAPE_CERT_TYPE
#endif

/* encode Certificate Policies, return total bytes written
 * each input value must be ITU-T X.690 formatted : a.b.c...
 * input must be an array of values with a NULL terminated for the latest
 * RFC5280 : non-critical */
#ifdef WOLFSSL_ASN_TEMPLATE
static int SetCertificatePolicies(byte *output,
                                  word32 outputSz,
                                  char input[MAX_CERTPOL_NB][MAX_CERTPOL_SZ],
                                  word16 nb_certpol,
                                  void* heap)
{
    int    i;
    int    ret = 0;
    byte   oid[MAX_OID_SZ];
    word32 oidSz;
    word32 sz = 0;
    word32 piSz = 0;

    if ((input == NULL) || (nb_certpol > MAX_CERTPOL_NB)) {
        ret = BAD_FUNC_ARG;
    }
    /* Put in policyIdentifier but not policyQualifiers. */
    for (i = 0; (ret == 0) && (i < nb_certpol); i++) {
        ASNSetData dataASN[policyInfoASN_Length];

        oidSz = sizeof(oid);
        XMEMSET(oid, 0, oidSz);
        dataASN[POLICYINFOASN_IDX_QUALI].noOut = 1;

        ret = EncodePolicyOID(oid, &oidSz, input[i], heap);
        if (ret == 0) {
            XMEMSET(dataASN, 0, sizeof(dataASN));
            SetASN_Buffer(&dataASN[POLICYINFOASN_IDX_ID], oid, oidSz);
            ret = SizeASN_Items(policyInfoASN, dataASN, policyInfoASN_Length,
                                &piSz);
        }
        if ((ret == 0) && (output != NULL) && (sz + piSz > outputSz)) {
            ret = BUFFER_E;
        }
        if (ret == 0) {
            if (output != NULL) {
                SetASN_Items(policyInfoASN, dataASN, policyInfoASN_Length,
                    output);
                output += piSz;
            }
            sz += piSz;
        }
    }

    if (ret == 0) {
        ret = (int)sz;
    }
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_CERT_EXT */


#ifdef WOLFSSL_ALT_NAMES

int FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names)
{
    word32 idx;
    const DNS_entry* curName;
    word32 namesSz = 0;
#ifdef WOLFSSL_ALT_NAMES_NO_REV
    word32 i;
#endif

    if (output == NULL)
        return BAD_FUNC_ARG;

    if (names == NULL)
        return 0;

    curName = names;
    do {
        word32 nameSz = (word32)curName->len + 1 +
            ASN_LEN_ENC_LEN(curName->len);
        if (namesSz + nameSz < namesSz)
            return BUFFER_E;
        namesSz += nameSz;
        curName = curName->next;
    } while (curName != NULL);

    if (outputSz < 1 + ASN_LEN_ENC_LEN(namesSz))
        return BUFFER_E;
    if (outputSz - (1 + ASN_LEN_ENC_LEN(namesSz)) < namesSz)
        return BUFFER_E;

    idx = SetSequence(namesSz, output);
#ifdef WOLFSSL_ALT_NAMES_NO_REV
    namesSz += idx;
    i = namesSz;
#endif

    curName = names;
    do {
#ifdef WOLFSSL_ALT_NAMES_NO_REV
        word32 len = SetLength(curName->len, NULL);
        idx = i - curName->len - len - 1;
        i = idx;
#endif
        output[idx] = (byte) (ASN_CONTEXT_SPECIFIC | curName->type);
        if (curName->type == ASN_DIR_TYPE || curName->type == ASN_OTHER_TYPE) {
            output[idx] |= ASN_CONSTRUCTED;
        }
        idx++;
        idx += SetLength((word32)curName->len, output + idx);
        XMEMCPY(output + idx, curName->name, (size_t)curName->len);
#ifndef WOLFSSL_ALT_NAMES_NO_REV
        idx += (word32)curName->len;
#endif
        curName = curName->next;
    } while (curName != NULL);

#ifdef WOLFSSL_ALT_NAMES_NO_REV
    idx = namesSz;
#endif
    return (int)idx;
}

#endif /* WOLFSSL_ALT_NAMES */
#endif /* WOLFSSL_CERT_GEN */

#if defined(WOLFSSL_CERT_GEN) || defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
/* Simple domain name OID size. */
#define DN_OID_SZ     3

/* Encodes one attribute of the name (issuer/subject)
 *
 * name     structure to hold result of encoding
 * nameStr  value to be encoded
 * nameTag  tag of encoding i.e CTC_UTF8
 * type     id of attribute i.e ASN_COMMON_NAME
 * emailTag tag of email i.e CTC_UTF8
 * returns length on success
 */
#ifdef WOLFSSL_ASN_TEMPLATE
static int EncodeName(EncodedName* name, const char* nameStr,
                    byte nameTag, byte type, byte emailTag, CertName* cname)
{
    DECL_ASNSETDATA(dataASN, rdnASN_Length);
    ASNItem namesASN[rdnASN_Length];
    byte dnOid[DN_OID_SZ] = { 0x55, 0x04, 0x00 };
    int ret = 0;
    word32 sz = 0;
    const byte* oid;
    word32 oidSz = 0;
    word32 nameSz = 0;

    /* Validate input parameters. */
    if ((name == NULL) || (nameStr == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
    #ifdef WOLFSSL_CUSTOM_OID
        if (type == ASN_CUSTOM_NAME) {
            if (cname == NULL || cname->custom.oidSz == 0) {
                name->used = 0;
                return 0;
            }
        }
    #else
        (void)cname;
    #endif
        CALLOC_ASNSETDATA(dataASN, rdnASN_Length, ret, NULL);
        nameSz = (word32)XSTRLEN(nameStr);
        /* Copy the RDN encoding template. ASN.1 tag for the name string is set
         * based on type. */
        XMEMCPY(namesASN, rdnASN, sizeof(namesASN));

        /* Set OID and ASN.1 tag for name depending on type. */
        switch (type) {
            case ASN_EMAIL_NAME:
                /* email OID different to standard types. */
                oid = attrEmailOid;
                oidSz = sizeof(attrEmailOid);
                /* Use email specific type/tag. */
                nameTag = emailTag;
                break;
            case ASN_DOMAIN_COMPONENT:
                /* Domain component OID different to standard types. */
                oid = dcOid;
                oidSz = sizeof(dcOid);
                break;
            case ASN_USER_ID:
                /* Domain component OID different to standard types. */
                oid = uidOid;
                oidSz = sizeof(uidOid);
                break;
            case ASN_RFC822_MAILBOX:
                oid = rfc822Mlbx;
                oidSz = sizeof(rfc822Mlbx);
                break;
            case ASN_FAVOURITE_DRINK:
                oid = fvrtDrk;
                oidSz = sizeof(fvrtDrk);
                break;
        #ifdef WOLFSSL_CUSTOM_OID
            case ASN_CUSTOM_NAME:
                #ifdef __s390x__
                    /* inhibit arch-specific false positive. */
                    PRAGMA_GCC_DIAG_PUSH;
                    PRAGMA_GCC("GCC diagnostic ignored \"-Wnull-dereference\"");
                #endif
                nameSz = (word32)cname->custom.valSz;
                oid = cname->custom.oid;
                oidSz = (word32)cname->custom.oidSz;
                #ifdef __s390x__
                    PRAGMA_GCC_DIAG_POP;
                #endif
                break;
        #endif
        #ifdef WOLFSSL_CERT_REQ
            case ASN_CONTENT_TYPE:
                oid = attrPkcs9ContentTypeOid;
                oidSz = sizeof(attrPkcs9ContentTypeOid);
                break;
        #endif
            default:
                /* Construct OID using type. */
                dnOid[2] = type;
                oid = dnOid;
                oidSz = DN_OID_SZ;
                break;
        }

        /* Set OID corresponding to the name type. */
        SetASN_Buffer(&dataASN[RDNASN_IDX_ATTR_TYPE], oid, oidSz);
        /* Set name string. */
        SetASN_Buffer(&dataASN[RDNASN_IDX_ATTR_VAL], (const byte *)nameStr,
                      nameSz);
        /* Set the ASN.1 tag for the name string. */
        namesASN[RDNASN_IDX_ATTR_VAL].tag = nameTag;

        /* Calculate size of encoded name and indexes of components. */
        ret = SizeASN_Items(namesASN, dataASN, rdnASN_Length, &sz);
    }
    /* Check if name's buffer is big enough. */
    if ((ret == 0) && (sz > (word32)sizeof(name->encoded))) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode name into the buffer. */
        SetASN_Items(namesASN, dataASN, rdnASN_Length, name->encoded);
        /* Cache the type and size, and set that it is used. */
        name->type = type;
        name->totalLen = (int)sz;
        name->used = 1;

        /* Return size of encoding. */
        ret = (int)sz;
    }
    (void)cname;

    FREE_ASNSETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* canonical encoding one attribute of the name (issuer/subject)
 * call EncodeName with CTC_UTF8 for email type
 *
 * name     structure to hold result of encoding
 * nameStr  value to be encoded
 * nameType type of encoding i.e CTC_UTF8
 * type     id of attribute i.e ASN_COMMON_NAME
 *
 * returns length on success
 */
int wc_EncodeNameCanonical(EncodedName* name, const char* nameStr,
                           char nameType, byte type)
{
    return EncodeName(name, nameStr, (byte)nameType, type,
        ASN_UTF8STRING, NULL);
}
#endif /* WOLFSSL_CERT_GEN || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */

#ifdef WOLFSSL_ASN_PARSE_KEYUSAGE

/* Convert key usage string (comma delimited, null terminated) to word16
 * Returns 0 on success, negative on error */
int ParseKeyUsageStr(const char* value, word16* keyUsage, void* heap)
{
    int ret = 0;
    char *token, *str, *ptr;
    word32 len = 0;
    word16 usage = 0;

    if (value == NULL || keyUsage == NULL) {
        return BAD_FUNC_ARG;
    }

    /* duplicate string (including terminator) */
    len = (word32)XSTRLEN(value);
    str = (char*)XMALLOC(len + 1, heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (str == NULL) {
        return MEMORY_E;
    }
    XMEMCPY(str, value, len + 1);

    /* parse value, and set corresponding Key Usage value */
    if ((token = XSTRTOK(str, ",", &ptr)) == NULL) {
        XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return KEYUSAGE_E;
    }
    while (token != NULL) {
        if (!XSTRCASECMP(token, "digitalSignature"))
            usage |= KEYUSE_DIGITAL_SIG;
        else if (!XSTRCASECMP(token, "nonRepudiation") ||
                 !XSTRCASECMP(token, "contentCommitment"))
            usage |= KEYUSE_CONTENT_COMMIT;
        else if (!XSTRCASECMP(token, "keyEncipherment"))
            usage |= KEYUSE_KEY_ENCIPHER;
        else if (!XSTRCASECMP(token, "dataEncipherment"))
            usage |= KEYUSE_DATA_ENCIPHER;
        else if (!XSTRCASECMP(token, "keyAgreement"))
            usage |= KEYUSE_KEY_AGREE;
        else if (!XSTRCASECMP(token, "keyCertSign"))
            usage |= KEYUSE_KEY_CERT_SIGN;
        else if (!XSTRCASECMP(token, "cRLSign"))
            usage |= KEYUSE_CRL_SIGN;
        else if (!XSTRCASECMP(token, "encipherOnly"))
            usage |= KEYUSE_ENCIPHER_ONLY;
        else if (!XSTRCASECMP(token, "decipherOnly"))
            usage |= KEYUSE_DECIPHER_ONLY;
        else {
            ret = KEYUSAGE_E;
            break;
        }

        token = XSTRTOK(NULL, ",", &ptr);
    }

    XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);

    if (ret == 0) {
        *keyUsage = usage;
    }

    return ret;
}

/* Convert extended key usage string (comma delimited, null terminated) to byte
 * Returns 0 on success, negative on error */
int ParseExtKeyUsageStr(const char* value, byte* extKeyUsage, void* heap)
{
    int ret = 0;
    char *token, *str, *ptr;
    word32 len = 0;
    byte usage = 0;

    if (value == NULL || extKeyUsage == NULL) {
        return BAD_FUNC_ARG;
    }

    /* duplicate string (including terminator) */
    len = (word32)XSTRLEN(value);
    str = (char*)XMALLOC(len + 1, heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (str == NULL) {
        return MEMORY_E;
    }
    XMEMCPY(str, value, len + 1);

    /* parse value, and set corresponding Key Usage value */
    if ((token = XSTRTOK(str, ",", &ptr)) == NULL) {
        XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return EXTKEYUSAGE_E;
    }
    while (token != NULL) {
        if (!XSTRCASECMP(token, "any"))
            usage |= EXTKEYUSE_ANY;
        else if (!XSTRCASECMP(token, "serverAuth"))
            usage |= EXTKEYUSE_SERVER_AUTH;
        else if (!XSTRCASECMP(token, "clientAuth"))
            usage |= EXTKEYUSE_CLIENT_AUTH;
        else if (!XSTRCASECMP(token, "codeSigning"))
            usage |= EXTKEYUSE_CODESIGN;
        else if (!XSTRCASECMP(token, "emailProtection"))
            usage |= EXTKEYUSE_EMAILPROT;
        else if (!XSTRCASECMP(token, "timeStamping"))
            usage |= EXTKEYUSE_TIMESTAMP;
        else if (!XSTRCASECMP(token, "OCSPSigning"))
            usage |= EXTKEYUSE_OCSP_SIGN;
        else {
            ret = EXTKEYUSAGE_E;
            break;
        }

        token = XSTRTOK(NULL, ",", &ptr);
    }

    XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);

    if (ret == 0) {
        *extKeyUsage = usage;
    }

    return ret;
}

#endif /* WOLFSSL_ASN_PARSE_KEYUSAGE */

#ifdef WOLFSSL_CERT_GEN
/* Encodes one attribute of the name (issuer/subject)
 * call we_EncodeName_ex with 0x16, IA5String for email type
 * name     structure to hold result of encoding
 * nameStr  value to be encoded
 * nameType type of encoding i.e CTC_UTF8
 * type     id of attribute i.e ASN_COMMON_NAME
 *
 * returns length on success
 */
int wc_EncodeName(EncodedName* name, const char* nameStr, char nameType,
                  byte type)
{
    return EncodeName(name, nameStr, (byte)nameType, type,
        ASN_IA5_STRING, NULL);
}

#ifdef WOLFSSL_ASN_TEMPLATE
static void SetRdnItems(ASNItem* namesASN, ASNSetData* dataASN, const byte* oid,
    word32 oidSz, byte tag, const byte* data, word32 sz)
{
    XMEMCPY(namesASN, rdnASN, sizeof(rdnASN));
    SetASN_Buffer(&dataASN[RDNASN_IDX_ATTR_TYPE], oid, oidSz);
    namesASN[RDNASN_IDX_ATTR_VAL].tag = tag;
    SetASN_Buffer(&dataASN[RDNASN_IDX_ATTR_VAL], data, sz);
}

#ifdef WOLFSSL_MULTI_ATTRIB
static int FindMultiAttrib(CertName* name, int id, int* idx)
{
    int i;
    for (i = *idx + 1; i >= 0 && i < CTC_MAX_ATTRIB; i++) {
        if (name->name[i].sz > 0 && name->name[i].id == id) {
            break;
        }
    }
    if (i == CTC_MAX_ATTRIB) {
        i = -1;
    }
    *idx = i;
    return i >= 0;
}
#endif

/* ASN.1 template for the SEQUENCE around the RDNs.
 * X.509: RFC 5280, 4.1.2.4 - RDNSequence
 */
static const ASNItem nameASN[] = {
    { 0, ASN_SEQUENCE, 1, 1, 0 },
};
enum {
    NAMEASN_IDX_SEQ = 0
};

/* Number of items in ASN.1 template for the SEQUENCE around the RDNs. */
#define nameASN_Length (sizeof(nameASN) / sizeof(ASNItem))

static int SetNameRdnItems(ASNSetData* dataASN, ASNItem* namesASN,
        int maxIdx, CertName* name)
{
    int         i;
    int         idx;
    int         ret = 0;
    word32      nameLen[NAME_ENTRIES];
#ifdef WOLFSSL_MULTI_ATTRIB
    int         j;
#endif

    for (i = 0; i < NAME_ENTRIES; i++) {
        /* Keep name length to identify component is to be encoded. */
        const char* nameStr = GetOneCertName(name, i);
        nameLen[i] = nameStr ? (word32)XSTRLEN(nameStr) : 0;
    }

    idx = nameASN_Length;
    for (i = 0; i < NAME_ENTRIES; i++) {
        int type = GetCertNameId(i);

    #ifdef WOLFSSL_MULTI_ATTRIB
        j = -1;
        /* Put DomainComponents before OrgUnitName. */
        while (FindMultiAttrib(name, type, &j)) {
            if (GetCertNameId(i) != ASN_DOMAIN_COMPONENT) {
                continue;
            }
            if (dataASN != NULL && namesASN != NULL) {
                if (idx > maxIdx - (int)rdnASN_Length) {
                    WOLFSSL_MSG("Wanted to write more ASN than allocated");
                    ret = BUFFER_E;
                    break;
                }
                /* Copy data into dynamic vars. */
                SetRdnItems(namesASN + idx, dataASN + idx, dcOid,
                            sizeof(dcOid), (byte)name->name[j].type,
                            (byte*)name->name[j].value,
                            (word32)name->name[j].sz);
            }
            idx += (int)rdnASN_Length;
        }
        if (ret != 0)
            break;
    #endif

        if (nameLen[i] > 0) {
            if (dataASN != NULL) {
                if (idx > maxIdx - (int)rdnASN_Length) {
                    WOLFSSL_MSG("Wanted to write more ASN than allocated");
                    ret = BUFFER_E;
                    break;
                }
                /* Write out first instance of attribute type. */
                if (type == ASN_EMAIL_NAME) {
                    /* Copy email data into dynamic vars. */
                    SetRdnItems(namesASN + idx, dataASN + idx, attrEmailOid,
                        sizeof(attrEmailOid), ASN_IA5_STRING,
                        (const byte*)GetOneCertName(name, i), nameLen[i]);
                }
                else if (type == ASN_USER_ID) {
                    /* Copy userID data into dynamic vars. */
                    SetRdnItems(namesASN + idx, dataASN + idx, uidOid,
                                sizeof(uidOid), (byte)GetNameType(name, i),
                        (const byte*)GetOneCertName(name, i), nameLen[i]);
                }
                else if (type == ASN_RFC822_MAILBOX) {
                    /* Copy RFC822 mailbox data into dynamic vars. */
                    SetRdnItems(namesASN + idx, dataASN + idx, rfc822Mlbx,
                                sizeof(rfc822Mlbx), (byte)GetNameType(name, i),
                        (const byte*)GetOneCertName(name, i), nameLen[i]);
                }
                else if (type == ASN_FAVOURITE_DRINK) {
                    /* Copy favourite drink data into dynamic vars. */
                    SetRdnItems(namesASN + idx, dataASN + idx, fvrtDrk,
                                sizeof(fvrtDrk), (byte)GetNameType(name, i),
                        (const byte*)GetOneCertName(name, i), nameLen[i]);
                }
                else if (type == ASN_CUSTOM_NAME) {
                #ifdef WOLFSSL_CUSTOM_OID
                    SetRdnItems(namesASN + idx, dataASN + idx, name->custom.oid,
                        (word32)name->custom.oidSz, (byte)name->custom.enc,
                        name->custom.val, (word32)name->custom.valSz);
                #endif
                }
                else {
                    /* Copy name data into dynamic vars. */
                    SetRdnItems(namesASN + idx, dataASN + idx, nameOid[i],
                        NAME_OID_SZ, (byte)GetNameType(name, i),
                        (const byte*)GetOneCertName(name, i), nameLen[i]);
                }
            }
            idx += (int)rdnASN_Length;
        }

    #ifdef WOLFSSL_MULTI_ATTRIB
        j = -1;
        /* Write all other attributes of this type. */
        while (FindMultiAttrib(name, type, &j)) {
            if (GetCertNameId(i) == ASN_DOMAIN_COMPONENT) {
                continue;
            }
            if (dataASN != NULL && namesASN != NULL) {
                if (idx > maxIdx - (int)rdnASN_Length) {
                    WOLFSSL_MSG("Wanted to write more ASN than allocated");
                    ret = BUFFER_E;
                    break;
                }
                /* Copy data into dynamic vars. */
                SetRdnItems(namesASN + idx, dataASN + idx, nameOid[i],
                    NAME_OID_SZ, (byte)name->name[j].type,
                    (byte*)name->name[j].value, (word32)name->name[j].sz);
            }
            idx += (int)rdnASN_Length;
        }
        if (ret != 0)
            break;
    #endif
    }
    if (ret == 0)
        ret = idx;
    return ret;
}
#endif

/* encode CertName into output, return total bytes written */
#ifdef WOLFSSL_ASN_TEMPLATE
int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap)
{
    /* TODO: consider calculating size of entries, putting length into
     * SEQUENCE, encode SEQUENCE, encode entries into buffer.  */
    ASNSetData* dataASN = NULL; /* Can't use DECL_ASNSETDATA. Always dynamic. */
    ASNItem*    namesASN = NULL;
    word32      items = 0;
    int         ret = 0;
    word32      sz = 0;

    /* Calculate length of name entries and size for allocating. */
    ret = SetNameRdnItems(NULL, NULL, 0, name);
    if (ret > 0) {
        items = (word32)ret;
        ret = 0;
    }

    if (items == 0) {
        /* if zero items, short-circuit return to avoid frivolous zero-size
         * allocations.
         */
        return 0;
    }

    /* Allocate dynamic data items. */
    dataASN = (ASNSetData*)XMALLOC(items * sizeof(ASNSetData), heap,
                                   DYNAMIC_TYPE_TMP_BUFFER);
    if (dataASN == NULL) {
        ret = MEMORY_E;
    }
    else {
        /* Allocate dynamic ASN.1 template items. */
        namesASN = (ASNItem*)XMALLOC(items * sizeof(ASNItem), heap,
                                     DYNAMIC_TYPE_TMP_BUFFER);
        if (namesASN == NULL) {
            ret = MEMORY_E;
        }
    }

    if (ret == 0) {
        /* Clear the dynamic data. */
        XMEMSET(dataASN, 0, items * sizeof(ASNSetData));
        /* Copy in the outer sequence. */
        XMEMCPY(namesASN, nameASN, sizeof(nameASN));

        ret = SetNameRdnItems(dataASN, namesASN, (int)items, name);
        if (ret == (int)items)
            ret = 0;
        else if (ret > 0) {
            WOLFSSL_MSG("SetNameRdnItems returned different length");
            ret = BUFFER_E;
        }
    }
    if (ret == 0) {
        /* Calculate size of encoding. */
        ret = SizeASN_Items(namesASN, dataASN, (int)items, &sz);
    }
    /* Check buffer size if passed in. */
    if (ret == 0 && output != NULL && sz > outputSz) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        if (output != NULL) {
            /* Encode Name. */
            ret = SetASN_Items(namesASN, dataASN, (int)items, output);
        }
        else {
            /* Return the encoding size. */
            ret = (int)sz;
        }
    }

    XFREE(namesASN, heap, DYNAMIC_TYPE_TMP_BUFFER);
    XFREE(dataASN, heap, DYNAMIC_TYPE_TMP_BUFFER);
    (void)heap;
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
int SetName(byte* output, word32 outputSz, CertName* name)
{
    return SetNameEx(output, outputSz, name, NULL);
}

#ifdef WOLFSSL_ASN_TEMPLATE
static int EncodePublicKey(int keyType, byte* output, int outLen,
                           RsaKey* rsaKey, ecc_key* eccKey,
                           ed25519_key* ed25519Key, ed448_key* ed448Key,
                           DsaKey* dsaKey, falcon_key* falconKey,
                           wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
                           LmsKey* lmsKey, XmssKey* xmssKey)
{
    int ret = 0;

    (void)outLen;
    (void)rsaKey;
    (void)eccKey;
    (void)ed25519Key;
    (void)ed448Key;
    (void)dsaKey;
    (void)falconKey;
    (void)mldsaKey;
    (void)slhDsaKey;
    (void)lmsKey;
    (void)xmssKey;

    switch (keyType) {
    #ifndef NO_RSA
        case RSA_KEY:
            ret = SetRsaPublicKey(output, rsaKey, outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif
    #ifdef HAVE_ECC
        case ECC_KEY:
            ret = SetEccPublicKey(output, eccKey, outLen, 1, 0);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case ED25519_KEY:
            ret = wc_Ed25519PublicKeyToDer(ed25519Key, output,
                                           (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif
    #ifdef HAVE_ED448
        case ED448_KEY:
            ret = wc_Ed448PublicKeyToDer(ed448Key, output, (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif
    #if defined(HAVE_FALCON)
        case FALCON_LEVEL1_KEY:
        case FALCON_LEVEL5_KEY:
            ret = wc_Falcon_PublicKeyToDer(falconKey, output,
                                           (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* HAVE_FALCON */
    #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1)
        #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case DILITHIUM_LEVEL2_KEY:
        case DILITHIUM_LEVEL3_KEY:
        case DILITHIUM_LEVEL5_KEY:
        #endif
        case ML_DSA_44_KEY:
        case ML_DSA_65_KEY:
        case ML_DSA_87_KEY:
            ret = wc_MlDsaKey_PublicKeyToDer(mldsaKey, output,
                                              (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* WOLFSSL_HAVE_MLDSA */
    #if defined(WOLFSSL_HAVE_SLHDSA)
        case SLH_DSA_SHAKE_128F_KEY:
        case SLH_DSA_SHAKE_192F_KEY:
        case SLH_DSA_SHAKE_256F_KEY:
        case SLH_DSA_SHAKE_128S_KEY:
        case SLH_DSA_SHAKE_192S_KEY:
        case SLH_DSA_SHAKE_256S_KEY:
        #ifdef WOLFSSL_SLHDSA_SHA2
        case SLH_DSA_SHA2_128F_KEY:
        case SLH_DSA_SHA2_192F_KEY:
        case SLH_DSA_SHA2_256F_KEY:
        case SLH_DSA_SHA2_128S_KEY:
        case SLH_DSA_SHA2_192S_KEY:
        case SLH_DSA_SHA2_256S_KEY:
        #endif
            ret = wc_SlhDsaKey_PublicKeyToDer(slhDsaKey, output,
                                            (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* WOLFSSL_HAVE_SLHDSA */
    #if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
        case LMS_KEY:
            ret = wc_LmsKey_PublicKeyToDer(lmsKey, output, (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
    #if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
        case XMSS_KEY:
        case XMSSMT_KEY:
            ret = wc_XmssKey_PublicKeyToDer(xmssKey, output, (word32)outLen, 1);
            if (ret <= 0) {
                ret = PUBLIC_KEY_E;
            }
            break;
    #endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
        default:
            ret = PUBLIC_KEY_E;
            break;
    }

    return ret;
}

/* ASN.1 template for certificate extensions.
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields.
 * All extensions supported for encoding are described.
 */
static const ASNItem static_certExtsASN[] = {
            /* Basic Constraints Extension - 4.2.1.9 */
/* BC_SEQ        */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* BC_OID        */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* BC_CRIT       */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* BC_STR        */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* BC_STR_SEQ    */            { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                   /* cA */
/* BC_CA         */                { 3, ASN_BOOLEAN, 0, 0, 0 },
                                                   /* pathLenConstraint */
/* BC_PATHLEN    */                { 3, ASN_INTEGER, 0, 0, 1 },
                                       /* Subject Alternative Name - 4.2.1.6  */
/* SAN_SEQ       */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* SAN_OID       */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* SAN_CRIT      */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* SAN_STR       */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
            /* Subject Key Identifier - 4.2.1.2 */
/* SKID_SEQ      */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* SKID_OID      */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* SKID_STR      */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* SKID_KEYID    */            { 2, ASN_OCTET_STRING, 0, 0, 0 },
                                       /* Authority Key Identifier - 4.2.1.1 */
/* AKID_SEQ      */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* AKID_OID      */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* AKID_STR      */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* AKID_STR_SEQ, */            { 2, ASN_SEQUENCE, 1, 1, 0 },
/* AKID_KEYID    */                { 3, ASN_CONTEXT_SPECIFIC | 0, 0, 0, 0 },
                                       /* Key Usage - 4.2.1.3 */
/* KU_SEQ        */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* KU_OID        */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* KU_CRIT       */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* KU_STR        */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* KU_USAGE      */            { 2, ASN_BIT_STRING, 0, 0, 0 },
                                       /* Extended Key Usage - 4,2,1,12 */
/* EKU_SEQ       */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* EKU_OID       */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* EKU_STR       */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
                                       /* Certificate Policies - 4.2.1.4 */
/* POLICIES_SEQ, */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* POLICIES_OID, */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* POLICIES_STR, */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* POLICIES_INFO */            { 2, ASN_SEQUENCE, 1, 0, 0 },
                                       /* Netscape Certificate Type */
/* NSTYPE_SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* NSTYPE_OID    */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* NSTYPE_STR    */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* NSTYPE_USAGE, */            { 2, ASN_BIT_STRING, 0, 0, 0 },
/* CRLINFO_SEQ   */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CRLINFO_OID   */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CRLINFO_STR   */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
                                       /* RFC 8737 id-pe-acmeIdentifier */
/* ACMEID_SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ACMEID_OID    */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* ACMEID_CRIT   */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* ACMEID_STR    */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* ACMEID_HASH   */            { 2, ASN_OCTET_STRING, 0, 0, 0 },
#ifdef WOLFSSL_DUAL_ALG_CERTS
/* SAPKI_SEQ     */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* SAPKI_OID     */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* SAPKI_CRIT    */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* SAPKI_STR     */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
/* ALTSIGALG_SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ALTSIGALG_OID */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* ALTSIGALG_CRIT*/        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* ALTSIGALG_STR */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
/* ALTSIGVAL_SEQ */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* ALTSIGVAL_OID */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* ALTSIGVAL_CRIT*/        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* ALTSIGVAL_STR */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
#endif /* WOLFSSL_DUAL_ALG_CERTS */
/* CUSTOM_SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CUSTOM_OID    */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CUSTOM_STR    */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
};
enum {
    CERTEXTSASN_IDX_BC_SEQ = 0,
    CERTEXTSASN_IDX_BC_OID,
    CERTEXTSASN_IDX_BC_CRIT,
    CERTEXTSASN_IDX_BC_STR,
    CERTEXTSASN_IDX_BC_STR_SEQ,
    CERTEXTSASN_IDX_BC_CA,
    CERTEXTSASN_IDX_BC_PATHLEN,
    CERTEXTSASN_IDX_SAN_SEQ,
    CERTEXTSASN_IDX_SAN_OID,
    CERTEXTSASN_IDX_SAN_CRIT,
    CERTEXTSASN_IDX_SAN_STR,
    CERTEXTSASN_IDX_SKID_SEQ,
    CERTEXTSASN_IDX_SKID_OID,
    CERTEXTSASN_IDX_SKID_STR,
    CERTEXTSASN_IDX_SKID_KEYID,
    CERTEXTSASN_IDX_AKID_SEQ,
    CERTEXTSASN_IDX_AKID_OID,
    CERTEXTSASN_IDX_AKID_STR,
    CERTEXTSASN_IDX_AKID_STR_SEQ,
    CERTEXTSASN_IDX_AKID_KEYID,
    CERTEXTSASN_IDX_KU_SEQ,
    CERTEXTSASN_IDX_KU_OID,
    CERTEXTSASN_IDX_KU_CRIT,
    CERTEXTSASN_IDX_KU_STR,
    CERTEXTSASN_IDX_KU_USAGE,
    CERTEXTSASN_IDX_EKU_SEQ,
    CERTEXTSASN_IDX_EKU_OID,
    CERTEXTSASN_IDX_EKU_STR,
    CERTEXTSASN_IDX_POLICIES_SEQ,
    CERTEXTSASN_IDX_POLICIES_OID,
    CERTEXTSASN_IDX_POLICIES_STR,
    CERTEXTSASN_IDX_POLICIES_INFO,
    CERTEXTSASN_IDX_NSTYPE_SEQ,
    CERTEXTSASN_IDX_NSTYPE_OID,
    CERTEXTSASN_IDX_NSTYPE_STR,
    CERTEXTSASN_IDX_NSTYPE_USAGE,
    CERTEXTSASN_IDX_CRLINFO_SEQ,
    CERTEXTSASN_IDX_CRLINFO_OID,
    CERTEXTSASN_IDX_CRLINFO_STR,
    CERTEXTSASN_IDX_ACMEID_SEQ,
    CERTEXTSASN_IDX_ACMEID_OID,
    CERTEXTSASN_IDX_ACMEID_CRIT,
    CERTEXTSASN_IDX_ACMEID_STR,
    CERTEXTSASN_IDX_ACMEID_HASH,
#ifdef WOLFSSL_DUAL_ALG_CERTS
    CERTEXTSASN_IDX_SAPKI_SEQ,
    CERTEXTSASN_IDX_SAPKI_OID,
    CERTEXTSASN_IDX_SAPKI_CRIT,
    CERTEXTSASN_IDX_SAPKI_STR,
    CERTEXTSASN_IDX_ALTSIGALG_SEQ,
    CERTEXTSASN_IDX_ALTSIGALG_OID,
    CERTEXTSASN_IDX_ALTSIGALG_CRIT,
    CERTEXTSASN_IDX_ALTSIGALG_STR,
    CERTEXTSASN_IDX_ALTSIGVAL_SEQ,
    CERTEXTSASN_IDX_ALTSIGVAL_OID,
    CERTEXTSASN_IDX_ALTSIGVAL_CRIT,
    CERTEXTSASN_IDX_ALTSIGVAL_STR,
#endif /* WOLFSSL_DUAL_ALG_CERTS */
    CERTEXTSASN_IDX_CUSTOM_SEQ,
    CERTEXTSASN_IDX_CUSTOM_OID,
    CERTEXTSASN_IDX_CUSTOM_STR,
    CERTEXTSASN_IDX_START_CUSTOM
};

/* Number of items in ASN.1 template for certificate extensions. We multiply
 * by 4 because there are 4 things (seq, OID, crit flag, octet string). */
#define certExtsASN_Length ((sizeof(static_certExtsASN) / sizeof(ASNItem)) \
                            + (NUM_CUSTOM_EXT * 4))

static const ASNItem customExtASN[] = {
/* CUSTOM_SEQ    */    { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CUSTOM_OID    */        { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CUSTOM_CRIT   */        { 1, ASN_BOOLEAN, 0, 0, 0 },
/* CUSTOM_STR    */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
};

static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
                            int forRequest)
{
    DECL_ASNSETDATA(dataASN, certExtsASN_Length);
#ifdef WOLFSSL_CERT_EXT
    int dataSz = 0;
#endif
    word32 sz = 0;
    int ret = 0;
    int i = 0;
    static const byte bcOID[]   = { 0x55, 0x1d, 0x13 };
#ifdef WOLFSSL_ALT_NAMES
    static const byte sanOID[]  = { 0x55, 0x1d, 0x11 };
#endif
#ifdef WOLFSSL_CERT_EXT
    static const byte skidOID[] = { 0x55, 0x1d, 0x0e };
    static const byte akidOID[] = { 0x55, 0x1d, 0x23 };
    static const byte kuOID[]   = { 0x55, 0x1d, 0x0f };
    static const byte ekuOID[]  = { 0x55, 0x1d, 0x25 };
    static const byte cpOID[]   = { 0x55, 0x1d, 0x20 };
    static const byte nsCertOID[] = { 0x60, 0x86, 0x48, 0x01,
                                      0x86, 0xF8, 0x42, 0x01, 0x01 };
    static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F };
#ifdef WOLFSSL_ACME_OID
    static const byte acmeIdOID[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07,
                                      0x01, 0x1F };
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
    static const byte sapkiOID[] = { 0x55, 0x1d, 0x48 };
    static const byte altSigAlgOID[] = { 0x55, 0x1d, 0x49 };
    static const byte altSigValOID[] = { 0x55, 0x1d, 0x4a };
#endif /* WOLFSSL_DUAL_ALG_CERTS */
#endif /* WOLFSSL_CERT_EXT */

#ifdef WOLFSSL_SMALL_STACK
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
    byte *encodedOids;
#endif
    ASNItem *certExtsASN = (ASNItem *)XMALLOC(certExtsASN_Length *
                                              sizeof(ASNItem), cert->heap,
                                              DYNAMIC_TYPE_TMP_BUFFER);
    if (certExtsASN == NULL) {
        return MEMORY_E;
    }

#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
    encodedOids = (byte *)XMALLOC(NUM_CUSTOM_EXT * MAX_OID_SZ, cert->heap,
                                  DYNAMIC_TYPE_TMP_BUFFER);
    if (encodedOids == NULL) {
        XFREE(certExtsASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return MEMORY_E;
    }
#endif
#else
    ASNItem certExtsASN[certExtsASN_Length];
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
    byte encodedOids[NUM_CUSTOM_EXT * MAX_OID_SZ];
#endif
#endif

    /* Clone static_certExtsASN into a certExtsASN and then fill the rest of it
     * with (NUM_CUSTOM_EXT*4) more ASNItems specifying extensions. See comment
     * above definition of certExtsASN_Length. */
    XMEMCPY(certExtsASN, static_certExtsASN, sizeof(static_certExtsASN));
    for (i = sizeof(static_certExtsASN) / sizeof(ASNItem);
         i < (int)certExtsASN_Length; i += 4) {
        XMEMCPY(&certExtsASN[i], customExtASN, sizeof(customExtASN));
    }

    (void)forRequest;

    CALLOC_ASNSETDATA(dataASN, certExtsASN_Length, ret, cert->heap);

    if (ret == 0) {
        if (cert->isCA) {
            /* Set Basic Constraints to be a Certificate Authority. */
            SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CA], 1);
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID));
            if (cert->basicConstCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1;
            }
            if (cert->pathLenSet
            #ifdef WOLFSSL_CERT_EXT
                && ((cert->keyUsage & KEYUSE_KEY_CERT_SIGN) || (!cert->keyUsage))
            #endif
            ) {
                SetASN_Int8Bit(&dataASN[CERTEXTSASN_IDX_BC_PATHLEN],
                        cert->pathLen);
            }
            else {
                dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1;
            }
        }
    #ifdef WOLFSSL_ALLOW_ENCODING_CA_FALSE
        else if (cert->isCaSet) {
            SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CA], 0);
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID));
            if (cert->basicConstCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1;
            }
            dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1;
        }
    #endif
        else if (cert->basicConstSet) {
            /* Set Basic Constraints to be a non Certificate Authority. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_BC_OID], bcOID, sizeof(bcOID));
            if (cert->basicConstCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_BC_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_BC_CRIT].noOut = 1;
            }
            dataASN[CERTEXTSASN_IDX_BC_CA].noOut = 1;
            dataASN[CERTEXTSASN_IDX_BC_PATHLEN].noOut = 1;
        }
        else {
            /* Don't write out Basic Constraints extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_BC_SEQ,
                    CERTEXTSASN_IDX_BC_PATHLEN);
        }
    #ifdef WOLFSSL_ALT_NAMES
        if (cert->altNamesSz > 0) {
            /* Set Subject Alternative Name OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAN_OID],
                    sanOID, sizeof(sanOID));
            if (cert->altNamesCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_SAN_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_SAN_CRIT].noOut = 1;
            }
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAN_STR],
                          cert->altNames, (word32)cert->altNamesSz);
        }
        else
    #endif
        {
            /* Don't write out Subject Alternative Name extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_SAN_SEQ,
                    CERTEXTSASN_IDX_SAN_STR);
        }
    #ifdef WOLFSSL_CERT_EXT
        if (cert->skidSz > 0) {
            /* Set Subject Key Identifier OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SKID_OID],
                    skidOID, sizeof(skidOID));
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SKID_KEYID],
                          cert->skid, (word32)cert->skidSz);
        }
        else
    #endif
        {
            /* Don't write out Subject Key Identifier extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_SKID_SEQ,
                    CERTEXTSASN_IDX_SKID_KEYID);
        }
    #ifdef WOLFSSL_CERT_EXT
        if (cert->akidSz > 0) {
            /* Set Authority Key Identifier OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_AKID_OID],
                    akidOID, sizeof(akidOID));
        #ifdef WOLFSSL_AKID_NAME
            if (cert->rawAkid) {
                SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_AKID_STR],
                        cert->akid, (word32)cert->akidSz);
                /* cert->akid contains the internal ext structure */
                SetASNItem_NoOutBelow(dataASN, certExtsASN,
                        CERTEXTSASN_IDX_AKID_STR, certExtsASN_Length);
            }
            else
        #endif
            {
                SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_AKID_KEYID],
                        cert->akid, (word32)cert->akidSz);
            }
        }
        else
    #endif
        {
            /* Don't write out Authority Key Identifier extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_AKID_SEQ,
                    CERTEXTSASN_IDX_AKID_KEYID);
        }
    #ifdef WOLFSSL_CERT_EXT
        if (cert->keyUsage != 0) {
            /* Set Key Usage OID, critical and value. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_KU_OID],
                    kuOID, sizeof(kuOID));
            SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_KU_CRIT], 1);
            SetASN_Int16Bit(&dataASN[CERTEXTSASN_IDX_KU_USAGE],
                    cert->keyUsage);
        }
        else
    #endif
        {
            /* Don't write out Key Usage extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_KU_SEQ,
                    CERTEXTSASN_IDX_KU_USAGE);
        }
    #ifdef WOLFSSL_CERT_EXT
        if (cert->extKeyUsage != 0) {
            /* Calculate size of Extended Key Usage data. */
            dataSz = SetExtKeyUsage(cert, NULL, 0, cert->extKeyUsage);
            if (dataSz <= 0) {
                ret = KEYUSAGE_E;
            }
            /* Set Extended Key Usage OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_EKU_OID],
                    ekuOID, sizeof(ekuOID));
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_EKU_STR],
                    NULL, (word32)dataSz);
        }
        else
    #endif
        {
            /* Don't write out Extended Key Usage extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_EKU_SEQ,
                    CERTEXTSASN_IDX_EKU_STR);
        }

    #ifdef WOLFSSL_CERT_EXT
        if ((!forRequest) && (cert->certPoliciesNb > 0)) {
            /* Calculate size of certificate policies. */
            dataSz = SetCertificatePolicies(NULL, 0, cert->certPolicies,
                    cert->certPoliciesNb, cert->heap);
            if (dataSz > 0) {
                /* Set Certificate Policies OID. */
                SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_POLICIES_OID],
                        cpOID, sizeof(cpOID));
                /* Make space for data. */
                SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_POLICIES_INFO],
                        NULL, (word32)dataSz);
            }
            else {
                ret = CERTPOLICIES_E;
            }
        }
        else
    #endif
        {
            /* Don't write out Certificate Policies extension items. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_POLICIES_SEQ,
                    CERTEXTSASN_IDX_POLICIES_INFO);
        }
    #if defined(WOLFSSL_CERT_EXT) && !defined(IGNORE_NETSCAPE_CERT_TYPE)
        /* Netscape Certificate Type */
        if (cert->nsCertType != 0) {
            /* Set Netscape Certificate Type OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_NSTYPE_OID],
                    nsCertOID, sizeof(nsCertOID));
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_NSTYPE_USAGE],
                    &cert->nsCertType, 1);
        }
        else
    #endif
        {
            /* Don't write out Netscape Certificate Type. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_NSTYPE_SEQ,
                    CERTEXTSASN_IDX_NSTYPE_USAGE);
        }
    #ifdef WOLFSSL_CERT_EXT
        if (cert->crlInfoSz > 0) {
            /* Set CRL Distribution Points OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_CRLINFO_OID],
                    crlInfoOID, sizeof(crlInfoOID));
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_CRLINFO_STR],
                    cert->crlInfo, (word32)cert->crlInfoSz);
        }
        else
    #endif
        {
            /* Don't write out CRL Distribution Points. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_CRLINFO_SEQ,
                    CERTEXTSASN_IDX_CRLINFO_STR);
        }

    #ifdef WOLFSSL_ACME_OID
        /* id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert).
         * Always critical=TRUE. */
        if (cert->acmeIdentifierSz == WC_SHA256_DIGEST_SIZE) {
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_OID],
                    acmeIdOID, sizeof(acmeIdOID));
            SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ACMEID_CRIT], 1);
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_HASH],
                    cert->acmeIdentifier, (word32)cert->acmeIdentifierSz);
        }
        else
    #endif /* WOLFSSL_ACME_OID */
        {
            /* Don't write out the ACME identifier extension. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ACMEID_SEQ,
                    CERTEXTSASN_IDX_ACMEID_HASH);
        }

    #ifdef WOLFSSL_DUAL_ALG_CERTS
        if (cert->sapkiDer != NULL) {
            /* Set subject alternative public key info OID, criticality and
             * data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_OID], sapkiOID,
                    sizeof(sapkiOID));
            if (cert->sapkiCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_SAPKI_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_SAPKI_CRIT].noOut = 1;
            }
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_SAPKI_STR], cert->sapkiDer,
                    cert->sapkiLen);
        }
        else {
            /* Don't write out subject alternative public key info. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_SAPKI_SEQ,
                    CERTEXTSASN_IDX_SAPKI_STR);
        }

        if (cert->altSigAlgDer != NULL) {
            /* Set alternative signature algorithm OID, criticality and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_OID], altSigAlgOID,
                    sizeof(altSigAlgOID));
            if (cert->altSigAlgCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_ALTSIGALG_CRIT].noOut = 1;
            }
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGALG_STR],
                    cert->altSigAlgDer, cert->altSigAlgLen);
        }
        else {
            /* Don't write out alternative signature algorithm. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ALTSIGALG_SEQ,
                    CERTEXTSASN_IDX_ALTSIGALG_STR);
        }

        if (cert->altSigValDer != NULL) {
            /* Set alternative signature value OID, criticality and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_OID], altSigValOID,
                    sizeof(altSigValOID));
            if (cert->altSigValCrit) {
                SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_CRIT], 1);
            }
            else {
                dataASN[CERTEXTSASN_IDX_ALTSIGVAL_CRIT].noOut = 1;
            }
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ALTSIGVAL_STR],
                    cert->altSigValDer, cert->altSigValLen);
        }
        else {
            /* Don't write out alternative signature value. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ALTSIGVAL_SEQ,
                    CERTEXTSASN_IDX_ALTSIGVAL_STR);
        }
    #endif /* WOLFSSL_DUAL_ALG_CERTS */

    #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CUSTOM_OID)
        /* encode a custom oid and value */
        if (cert->extCustom.oidSz > 0) {
            /* Set CRL Distribution Points OID and data. */
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_CUSTOM_OID],
                    cert->extCustom.oid, cert->extCustom.oidSz);
            SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_CUSTOM_STR],
                    cert->extCustom.val, cert->extCustom.valSz);
        }
        else
    #endif
        {
            /* Don't write out custom OID. */
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_CUSTOM_SEQ,
                    CERTEXTSASN_IDX_CUSTOM_STR);
        }

        i = 0;
    #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CUSTOM_OID)
        for (; i < cert->customCertExtCount; i++) {
             int idx = CERTEXTSASN_IDX_START_CUSTOM + (i * 4);
             word32 encodedOidSz = MAX_OID_SZ;
             idx++; /* Skip one for for SEQ. */
             /* EncodePolicyOID() will never return error since we parsed this
              * OID when it was set. */
             EncodePolicyOID(&encodedOids[i * MAX_OID_SZ], &encodedOidSz,
                             cert->customCertExt[i].oid, NULL);
             SetASN_Buffer(&dataASN[idx], &encodedOids[i * MAX_OID_SZ],
                           encodedOidSz);
             idx++;
             if (cert->customCertExt[i].crit) {
                 SetASN_Boolean(&dataASN[idx], 1);
             } else {
                 dataASN[idx].noOut = 1;
             }
             idx++;
             SetASN_Buffer(&dataASN[idx], cert->customCertExt[i].val,
                           cert->customCertExt[i].valSz);
        }
    #endif

        while (i < NUM_CUSTOM_EXT) {
            SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_START_CUSTOM + (i * 4),
                             CERTEXTSASN_IDX_START_CUSTOM + (i * 4) + 3);
            i++;
        }
    }

    if (ret == 0) {
        /* Calculate size of encoded extensions. */
        ret = SizeASN_Items(certExtsASN, dataASN, certExtsASN_Length, &sz);
    }
    if (ret == 0) {
        /* Only SEQUENCE - don't encode extensions. */
        if (sz == 2) {
            sz = 0;
        }
        /* Check buffer is big enough. */
        else if ((output != NULL) && (sz > maxSz)) {
            ret = BUFFER_E;
        }
    }

    if ((ret == 0) && (output != NULL) && (sz > 0)) {
        /* Encode certificate extensions into buffer. */
        SetASN_Items(certExtsASN, dataASN, certExtsASN_Length, output);

    #ifdef WOLFSSL_CERT_EXT
        if (cert->extKeyUsage != 0){
            /* Encode Extended Key Usage into space provided. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            if (SetExtKeyUsage(
                    cert,
                    (byte*)(wc_ptr_t)
                        dataASN[CERTEXTSASN_IDX_EKU_STR].data.buffer.data,
                    dataASN[CERTEXTSASN_IDX_EKU_STR].data.buffer.length,
                    cert->extKeyUsage) <= 0) {
                ret = KEYUSAGE_E;
            }
        }
        if ((!forRequest) && (cert->certPoliciesNb > 0)) {
            /* Encode Certificate Policies into space provided. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            if (SetCertificatePolicies(
                    (byte*)(wc_ptr_t)
                        dataASN[CERTEXTSASN_IDX_POLICIES_INFO].data.buffer.data,
                    dataASN[CERTEXTSASN_IDX_POLICIES_INFO].data.buffer.length,
                    cert->certPolicies, cert->certPoliciesNb, cert->heap) <= 0) {
                ret = CERTPOLICIES_E;
            }
        }
    #endif
    }
    if (ret == 0) {
        /* Return the encoding size. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, cert->heap);
#ifdef WOLFSSL_SMALL_STACK
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
    XFREE(encodedOids, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
    XFREE(certExtsASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
static int SetValidity(byte* before, byte* after, int daysValid)
{
#ifndef NO_ASN_TIME
    int ret = 0;
    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);

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

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

        SetTime(&localTime, before);

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

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

        SetTime(&localTime, after);
    }

    return ret;
#else
    (void)before;
    (void)after;
    (void)daysValid;
    return NOT_COMPILED_IN;
#endif
}
#endif /* WOLFSSL_ASN_TEMPLATE */



#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_REQ)
/* Forward declaration for internal use */
static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf,
    word32 sz, byte* sig, word32 sigSz, int sigAlgoType, int keyType,
    wc_SignCertCb signCb, void* signCtx, WC_RNG* rng, void* heap);

/* Internal context for default signing operations (when no callback provided) */
typedef struct {
    void* key;
    int keyType;
    WC_RNG* rng;
} InternalSignCtx;

/* Internal signing callback that uses wolfCrypt APIs
 * This is used by MakeSignature to delegate to MakeSignatureCb internally */
static int InternalSignCb(const byte* in, word32 inLen,
    byte* out, word32* outLen, int sigAlgo, int keyType, void* ctx)
{
    InternalSignCtx* signCtx = (InternalSignCtx*)ctx;
    int ret = WC_NO_ERR_TRACE(ALGO_ID_E);

    (void)sigAlgo; /* Algorithm determined by key type */

#if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(WOLFSSL_RSA_VERIFY_ONLY)
    if (keyType == RSA_TYPE && signCtx->key) {
        /* For RSA, input is already encoded digest */
        ret = wc_RsaSSL_Sign(in, inLen, out, *outLen,
                             (RsaKey*)signCtx->key, signCtx->rng);
        if (ret > 0) {
            *outLen = (word32)ret;
            ret = 0;
        }
    }
    else
#endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */
#if defined(HAVE_ECC) && defined(HAVE_ECC_SIGN)
    if (keyType == ECC_TYPE && signCtx->key) {
        /* For ECC, input is the raw hash */
        ret = wc_ecc_sign_hash(in, inLen, out, outLen,
                               signCtx->rng, (ecc_key*)signCtx->key);
    }
    else
#endif /* HAVE_ECC && HAVE_ECC_SIGN */
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN)
    if (keyType == ED25519_TYPE && signCtx->key) {
        /* Ed25519 signs messages, not hashes - cannot use callback path */
        ret = SIG_TYPE_E;
    }
    else
#endif /* HAVE_ED25519 && HAVE_ED25519_SIGN */
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN)
    if (keyType == ED448_TYPE && signCtx->key) {
        /* Ed448 signs messages, not hashes - cannot use callback path */
        ret = SIG_TYPE_E;
    }
    else
#endif /* HAVE_ED448 && HAVE_ED448_SIGN */
#if defined(HAVE_FALCON)
    if ((keyType == FALCON_LEVEL1_TYPE || keyType == FALCON_LEVEL5_TYPE) &&
        signCtx->key) {
        /* Falcon signs messages, not hashes - cannot use callback path */
        ret = SIG_TYPE_E;
    }
    else
#endif /* HAVE_FALCON */
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN)
    if ((keyType == DILITHIUM_LEVEL2_TYPE || keyType == DILITHIUM_LEVEL3_TYPE ||
        keyType == DILITHIUM_LEVEL5_TYPE) && signCtx->key) {
        /* Dilithium signs messages, not hashes - cannot use callback path */
        ret = SIG_TYPE_E;
    }
    else
#endif /* WOLFSSL_HAVE_MLDSA && !WOLFSSL_MLDSA_NO_SIGN */
#if defined(WOLFSSL_HAVE_SLHDSA)
    if (IsSlhDsaKeyType(keyType) && signCtx->key) {
        /* SLH-DSA signs messages, not hashes - cannot use callback path */
        ret = SIG_TYPE_E;
    }
    else
#endif /* WOLFSSL_HAVE_SLHDSA */
    {
        /* Unhandled key type */
        (void)in;
        (void)inLen;
        (void)out;
        (void)outLen;
        (void)keyType;
        (void)signCtx;
    }

    return ret;
}
#endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */


/* Return the buffer size needed to hold the raw signature that
 * MakeSignature() will produce for the provided key, or a negative error code
 * if it cannot be determined. The per-algorithm branches mirror the dispatch
 * in MakeSignature() so the two stay in agreement; exactly one key pointer is
 * expected to be non-NULL.
 *
 * Sizing the signature buffer from the key (rather than the worst-case
 * MAX_ENCODED_SIG_SZ) avoids allocating tens of KB for classic keys in builds
 * that merely enable a PQC algorithm, and is required for LMS/XMSS whose
 * parameter-dependent signatures can exceed MAX_ENCODED_SIG_SZ. */
static int GetSignatureBufferSz(RsaKey* rsaKey, ecc_key* eccKey,
    ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey,
    wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey,
    XmssKey* xmssKey)
{
    int sigSz = ALGO_ID_E;

    (void)rsaKey;     (void)eccKey;    (void)ed25519Key; (void)ed448Key;
    (void)falconKey;  (void)mldsaKey;  (void)slhDsaKey;  (void)lmsKey;
    (void)xmssKey;

#ifndef NO_RSA
    if (rsaKey != NULL)
        sigSz = wc_RsaEncryptSize(rsaKey);
#endif
#ifdef HAVE_ECC
    if (eccKey != NULL)
        sigSz = wc_ecc_sig_size(eccKey);
#endif
#if defined(HAVE_ED25519)
    if (ed25519Key != NULL)
        sigSz = wc_ed25519_sig_size(ed25519Key);
#endif
#if defined(HAVE_ED448)
    if (ed448Key != NULL)
        sigSz = wc_ed448_sig_size(ed448Key);
#endif
#if defined(HAVE_FALCON)
    if (falconKey != NULL)
        sigSz = wc_falcon_sig_size(falconKey);
#endif
#if defined(WOLFSSL_HAVE_MLDSA)
    if (mldsaKey != NULL) {
        int len = 0;
        int rc = wc_MlDsaKey_GetSigLen(mldsaKey, &len);
        sigSz = (rc == 0) ? len : rc;
    }
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
    if (slhDsaKey != NULL)
        sigSz = wc_SlhDsaKey_SigSize(slhDsaKey);
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    if (lmsKey != NULL) {
        word32 len = 0;
        int rc = wc_LmsKey_GetSigLen(lmsKey, &len);
        sigSz = (rc == 0) ? (int)len : rc;
    }
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    if (xmssKey != NULL) {
        word32 len = 0;
        int rc = wc_XmssKey_GetSigLen(xmssKey, &len);
        sigSz = (rc == 0) ? (int)len : rc;
    }
#endif

    /* sigSz still ALGO_ID_E means no (compiled-in) key matched; a getter
     * returning 0 means the matched key was not usable. */
    if (sigSz == 0)
        sigSz = BAD_FUNC_ARG;
    return sigSz;
}

#ifndef NO_RSA
/* True if sType is an RSA signature-algorithm OID (any hash, incl. PSS). */
static int IsRsaSigType(int sType)
{
    switch (sType) {
        case CTC_MD2wRSA:
        case CTC_MD5wRSA:
        case CTC_SHAwRSA:
        case CTC_SHA224wRSA:
        case CTC_SHA256wRSA:
        case CTC_SHA384wRSA:
        case CTC_SHA512wRSA:
        case CTC_SHA3_224wRSA:
        case CTC_SHA3_256wRSA:
        case CTC_SHA3_384wRSA:
        case CTC_SHA3_512wRSA:
        case CTC_RSASSAPSS:
            return 1;
        default:
            return 0;
    }
}
#endif /* !NO_RSA */
#ifdef HAVE_ECC
/* True if sType is an ECDSA (or SM2) signature-algorithm OID (any hash). */
static int IsEccSigType(int sType)
{
    switch (sType) {
        case CTC_SHAwECDSA:
        case CTC_SHA224wECDSA:
        case CTC_SHA256wECDSA:
        case CTC_SHA384wECDSA:
        case CTC_SHA512wECDSA:
        case CTC_SHA3_224wECDSA:
        case CTC_SHA3_256wECDSA:
        case CTC_SHA3_384wECDSA:
        case CTC_SHA3_512wECDSA:
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        case CTC_SM3wSM2:
    #endif
            return 1;
        default:
            return 0;
    }
}
#endif /* HAVE_ECC */

/* Return the heap hint of whichever signing key is set, or NULL if none does
 * (falcon_key has no heap member). Centralizes the lookup shared by the
 * certificate/CSR signing entry points. */
static void* GetSigningKeyHeap(RsaKey* rsaKey, ecc_key* eccKey,
    ed25519_key* ed25519Key, ed448_key* ed448Key, wc_MlDsaKey* mldsaKey,
    SlhDsaKey* slhDsaKey, LmsKey* lmsKey, XmssKey* xmssKey)
{
    (void)rsaKey;    (void)eccKey;   (void)ed25519Key; (void)ed448Key;
    (void)mldsaKey;  (void)slhDsaKey; (void)lmsKey;    (void)xmssKey;
#ifndef NO_RSA
    if (rsaKey != NULL)
        return rsaKey->heap;
#endif
#ifdef HAVE_ECC
    if (eccKey != NULL)
        return eccKey->heap;
#endif
#ifdef HAVE_ED25519
    if (ed25519Key != NULL)
        return ed25519Key->heap;
#endif
#ifdef HAVE_ED448
    if (ed448Key != NULL)
        return ed448Key->heap;
#endif
#ifdef WOLFSSL_HAVE_MLDSA
    if (mldsaKey != NULL)
        return mldsaKey->heap;
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
    if (slhDsaKey != NULL)
        return slhDsaKey->heap;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    if (lmsKey != NULL)
        return lmsKey->heap;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    if (xmssKey != NULL)
        return xmssKey->heap;
#endif
    return NULL;
}

/* Verify that the requested signature type -- which becomes the certificate's
 * signatureAlgorithm OID -- is consistent with the signing key. The signature
 * is produced by dispatching on the key while the OID is written from sType,
 * so a mismatch would emit a certificate whose advertised algorithm
 * contradicts the key that signed it (e.g. leaving the wc_InitCert default of
 * CTC_SHA256wRSA on a non-RSA key). For RSA/ECC the hash is free to vary, so
 * the whole signature-algorithm family is accepted; the remaining algorithms
 * have a single (level/parameter-determined) OID. Returns 0 if consistent or
 * if there is no key to check, ALGO_ID_E on a mismatch. */
static int CheckSigTypeForKey(int sType, RsaKey* rsaKey, ecc_key* eccKey,
    ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey,
    wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey,
    XmssKey* xmssKey)
{
    (void)sType;
    (void)rsaKey;    (void)eccKey;    (void)ed25519Key; (void)ed448Key;
    (void)falconKey; (void)mldsaKey;  (void)slhDsaKey;  (void)lmsKey;
    (void)xmssKey;

#ifdef WOLFSSL_DUAL_ALG_CERTS
    /* sigType 0 indicates preTBS encoding: no signatureAlgorithm to validate. */
    if (sType == 0)
        return 0;
#endif

#ifndef NO_RSA
    if (rsaKey != NULL)
        return IsRsaSigType(sType) ? 0 : ALGO_ID_E;
#endif
#ifdef HAVE_ECC
    if (eccKey != NULL)
        return IsEccSigType(sType) ? 0 : ALGO_ID_E;
#endif
#ifdef HAVE_ED25519
    if (ed25519Key != NULL)
        return (sType == CTC_ED25519) ? 0 : ALGO_ID_E;
#endif
#ifdef HAVE_ED448
    if (ed448Key != NULL)
        return (sType == CTC_ED448) ? 0 : ALGO_ID_E;
#endif
#ifdef HAVE_FALCON
    if (falconKey != NULL) {
        if (falconKey->level == 1)
            return (sType == CTC_FALCON_LEVEL1) ? 0 : ALGO_ID_E;
        if (falconKey->level == 5)
            return (sType == CTC_FALCON_LEVEL5) ? 0 : ALGO_ID_E;
        return ALGO_ID_E;
    }
#endif
#ifdef WOLFSSL_HAVE_MLDSA
    if (mldsaKey != NULL) {
        if (mldsaKey->params == NULL)
            return ALGO_ID_E;
    #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        if (mldsaKey->params->level == WC_ML_DSA_44_DRAFT)
            return (sType == CTC_DILITHIUM_LEVEL2) ? 0 : ALGO_ID_E;
        if (mldsaKey->params->level == WC_ML_DSA_65_DRAFT)
            return (sType == CTC_DILITHIUM_LEVEL3) ? 0 : ALGO_ID_E;
        if (mldsaKey->params->level == WC_ML_DSA_87_DRAFT)
            return (sType == CTC_DILITHIUM_LEVEL5) ? 0 : ALGO_ID_E;
    #endif
        if (mldsaKey->level == WC_ML_DSA_44)
            return (sType == CTC_ML_DSA_44) ? 0 : ALGO_ID_E;
        if (mldsaKey->level == WC_ML_DSA_65)
            return (sType == CTC_ML_DSA_65) ? 0 : ALGO_ID_E;
        if (mldsaKey->level == WC_ML_DSA_87)
            return (sType == CTC_ML_DSA_87) ? 0 : ALGO_ID_E;
        return ALGO_ID_E;
    }
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
    if (slhDsaKey != NULL) {
        /* SLH-DSA uses one OID per parameter set for both the key and the
         * signature, so the key OID sum equals the CTC signature value. */
        int oid;
        if (slhDsaKey->params == NULL)
            return ALGO_ID_E;
        oid = wc_SlhDsaParamToOid(slhDsaKey->params->param);
        if (oid < 0)
            return ALGO_ID_E;
        return (sType == oid) ? 0 : ALGO_ID_E;
    }
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    if (lmsKey != NULL)
        return (sType == CTC_HSS_LMS) ? 0 : ALGO_ID_E;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    if (xmssKey != NULL) {
        if (xmssKey->is_xmssmt)
            return (sType == CTC_XMSSMT) ? 0 : ALGO_ID_E;
        return (sType == CTC_XMSS) ? 0 : ALGO_ID_E;
    }
#endif

    return 0;
}

/* Make signature from buffer (sz), write to sig (sigSz)
 * This function now uses MakeSignatureCb internally for RSA and ECC,
 * eliminating code duplication. Ed25519, Ed448, and post-quantum algorithms
 * still use direct signing since they sign messages, not hashes. */
static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
    byte* sig, word32 sigSz, RsaKey* rsaKey, ecc_key* eccKey,
    ed25519_key* ed25519Key, ed448_key* ed448Key, falcon_key* falconKey,
    wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey, LmsKey* lmsKey,
    XmssKey* xmssKey, WC_RNG* rng, word32 sigAlgoType, void* heap)
{
    int ret = 0;

    (void)buf;
    (void)sz;
    (void)sig;
    (void)sigSz;
    (void)ed25519Key;
    (void)ed448Key;
    (void)falconKey;
    (void)mldsaKey;
    (void)slhDsaKey;
    (void)lmsKey;
    (void)xmssKey;
    (void)rng;
    (void)heap;

    /* For RSA and ECC, use the callback path to eliminate duplication */
#if (!defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
    (defined(HAVE_ECC) && defined(HAVE_ECC_SIGN))
    if (rsaKey || eccKey) {
        InternalSignCtx signCtx;

        /* Setup internal signing context */
        XMEMSET(&signCtx, 0, sizeof(signCtx));
        signCtx.rng = rng;

        /* Determine key type and set key pointer */
        if (rsaKey) {
            signCtx.key = rsaKey;
            signCtx.keyType = RSA_TYPE;
        }
        else {
            signCtx.key = eccKey;
            signCtx.keyType = ECC_TYPE;
        }

        /* Use unified callback path */
        ret = MakeSignatureCb(certSignCtx, buf, sz, sig, sigSz,
                             (int)sigAlgoType, signCtx.keyType,
                             InternalSignCb, &signCtx, rng, heap);
        goto exit_ms;
    }
#endif

    /* Ed25519, Ed448, and post-quantum algorithms sign messages (not hashes),
     * so they cannot use the callback path. Keep original implementation. */
    ret = -1; /* default to error, reassigned to ALGO_ID_E below. */

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN)
    if (ed25519Key) {
        word32 outSz = sigSz;
        ret = wc_ed25519_sign_msg(buf, sz, sig, &outSz, ed25519Key);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* HAVE_ED25519 && HAVE_ED25519_SIGN */

#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN)
    if (ed448Key) {
        word32 outSz = sigSz;
        ret = wc_ed448_sign_msg(buf, sz, sig, &outSz, ed448Key, NULL, 0);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* HAVE_ED448 && HAVE_ED448_SIGN */

#if defined(HAVE_FALCON)
    if (falconKey) {
        word32 outSz = sigSz;
        ret = wc_falcon_sign_msg(buf, sz, sig, &outSz, falconKey, rng);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* HAVE_FALCON */

#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN)
    if (mldsaKey) {
        word32 outSz = sigSz;
        #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        if ((mldsaKey->params->level == WC_ML_DSA_44_DRAFT) ||
                (mldsaKey->params->level == WC_ML_DSA_65_DRAFT) ||
                (mldsaKey->params->level == WC_ML_DSA_87_DRAFT)) {
            ret = wc_MlDsaKey_Sign(mldsaKey, sig, &outSz, buf, sz, rng);
            if (ret == 0)
                ret = (int)outSz;
        }
        else
        #endif
        {
            ret = wc_MlDsaKey_SignCtx(mldsaKey, NULL, 0, sig, &outSz,
                buf, sz, rng);
            if (ret == 0)
                ret = (int)outSz;
        }
    }
#endif /* WOLFSSL_HAVE_MLDSA && !WOLFSSL_MLDSA_NO_SIGN */

#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
    if (slhDsaKey) {
        word32 outSz = sigSz;
        ret = wc_SlhDsaKey_Sign(slhDsaKey, NULL, 0, buf, sz, sig, &outSz, rng);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* WOLFSSL_HAVE_SLHDSA && !WOLFSSL_SLHDSA_VERIFY_ONLY */

#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    if (lmsKey) {
        word32 outSz = sigSz;
        /* RFC 9802: the TBS is signed directly with no pre-hash. */
        ret = wc_LmsKey_Sign(lmsKey, sig, &outSz, buf, (int)sz);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */

#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    if (xmssKey) {
        word32 outSz = sigSz;
        /* RFC 9802: the TBS is signed directly with no pre-hash. */
        ret = wc_XmssKey_Sign(xmssKey, sig, &outSz, buf, (int)sz);
        if (ret == 0)
            ret = (int)outSz;
    }
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */

    if (ret == -1)
        ret = ALGO_ID_E;

exit_ms:
    if (ret < 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}


#ifdef WOLFSSL_ASN_TEMPLATE
/* Generate a random integer value of at most len bytes.
 *
 * Most-significant bit will not be set when maximum size.
 * Random value may be smaller than maximum size in bytes.
 *
 * @param [in]  rng  Random number generator.
 * @param [out] out  Buffer to hold integer value.
 * @param [in]  len  Maximum number of bytes of integer.
 * @return  0 on success.
 * @return  -ve when random number generation failed.
 */
static int GenerateInteger(WC_RNG* rng, byte* out, word32 len)
{
    int ret;

    /* Generate random number. */
    ret = wc_RNG_GenerateBlock(rng, out, len);
    if (ret == 0) {
        int i;

        /* Clear the top bit to make positive. */
        out[0] &= 0x7f;

        /* Find first non-zero byte. One zero byte is valid though. */
        for (i = 0; i < (int)len - 1; i++) {
            if (out[i] != 0) {
                break;
            }
        }
        if (i != 0) {
            /* Remove leading zeros. */
            XMEMMOVE(out, out + i, (size_t)len - (size_t)i);
        }
    }

    return ret;
}

/* ASN.1 template for a Certificate.
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields.
 */
static const ASNItem sigASN[] = {
/* SEQ          */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                     /* tbsCertificate */
/* TBS_SEQ      */        { 1, ASN_SEQUENCE, 1, 0, 0 },
                                     /* signatureAlgorithm */
/* SIGALGO_SEQ  */        { 1, ASN_SEQUENCE, 1, 1, 0 },
/* SIGALGO_OID  */            { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* SIGALGO_NULL */            { 2, ASN_TAG_NULL, 0, 0, 0 },
                                     /* signatureValue */
/* SIGNATURE    */        { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    SIGASN_IDX_SEQ = 0,
    SIGASN_IDX_TBS_SEQ,
    SIGASN_IDX_SIGALGO_SEQ,
    SIGASN_IDX_SIGALGO_OID,
    SIGASN_IDX_SIGALGO_NULL,
    SIGASN_IDX_SIGNATURE
};

/* Number of items in ASN.1 template for a Certificate. */
#define sigASN_Length (sizeof(sigASN) / sizeof(ASNItem))
#endif

/* add signature to end of buffer, size of buffer assumed checked, return
   new length */
#ifdef WOLFSSL_ASN_TEMPLATE
int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz,
                        int sigAlgoType)
{
    DECL_ASNSETDATA(dataASN, sigASN_Length);
    word32 seqSz = 0;
    word32 sz = 0;
    int ret = 0;

    CALLOC_ASNSETDATA(dataASN, sigASN_Length, ret, NULL);

    /* In place, put body between SEQUENCE and signature. */
    if (ret == 0) {
        /* Set signature OID and signature data. */
        SetASN_OID(&dataASN[SIGASN_IDX_SIGALGO_OID], (word32)sigAlgoType,
                   oidSigType);
        if (dataASN[SIGASN_IDX_SIGALGO_OID].data.buffer.data == NULL) {
            /* The OID was not found or compiled in! */
            ret = ASN_UNKNOWN_OID_E;
        }
    }
    if (ret == 0) {
        if (IsSigAlgoNoParams((word32)sigAlgoType)) {
            /* ECDSA and EdDSA doesn't have NULL tagged item. */
            dataASN[SIGASN_IDX_SIGALGO_NULL].noOut = 1;
        }
        SetASN_Buffer(&dataASN[SIGASN_IDX_SIGNATURE], sig, (word32)sigSz);
        /* Calculate size of signature data. */
        ret = SizeASN_Items(&sigASN[SIGASN_IDX_SIGALGO_SEQ],
                &dataASN[SIGASN_IDX_SIGALGO_SEQ], sigASN_Length - 2, &sz);
    }
    if (ret == 0) {
        /* Calculate size of outer sequence by calculating size of the encoded
         * length and adding 1 for tag. */
        seqSz = SizeASNHeader((word32)bodySz + sz);
        if (buf != NULL) {
            /* Move body to after sequence. */
            XMEMMOVE(buf + seqSz, buf, (size_t)bodySz);
        }
        /* Leave space for body in encoding. */
        SetASN_ReplaceBuffer(&dataASN[SIGASN_IDX_TBS_SEQ], NULL,
                             (word32)bodySz);

        /* Calculate overall size and put in offsets and lengths. */
        ret = SizeASN_Items(sigASN, dataASN, sigASN_Length, &sz);
    }
    if ((ret == 0) && (buf != NULL)) {
        /* Write SEQUENCE and signature around body. */
        SetASN_Items(sigASN, dataASN, sigASN_Length, buf);
    }

    if (ret == 0) {
        /* Return the encoding size. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Make an x509 Certificate v3 any key type from cert input, write to buffer */
#ifdef WOLFSSL_ASN_TEMPLATE
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,
                       LmsKey* lmsKey, XmssKey* xmssKey)
{
    /* TODO: issRaw and sbjRaw should be NUL terminated. */
    DECL_ASNSETDATA(dataASN, x509CertASN_Length);
    word32 publicKeySz = 0;
    word32 issuerSz = 0;
    word32 subjectSz = 0;
    word32 extSz = 0;
    word32 sz = 0;
    int ret = 0;
    word32 issRawLen = 0;
    word32 sbjRawLen = 0;

    /* Unused without PQC */
    (void)falconKey;
    (void)mldsaKey;
    (void)slhDsaKey;
    (void)lmsKey;
    (void)xmssKey;

    CALLOC_ASNSETDATA(dataASN, x509CertASN_Length, ret, cert->heap);

    if (ret == 0) {
        /* Set key type into certificate object based on key passed in. */
        if (rsaKey) {
            cert->keyType = RSA_KEY;
        }
        else if (eccKey) {
            cert->keyType = ECC_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->level == WC_ML_DSA_44)) {
            cert->keyType = ML_DSA_44_KEY;
        }
        else if ((mldsaKey != NULL) &&
                    (mldsaKey->level == WC_ML_DSA_65)) {
            cert->keyType = ML_DSA_65_KEY;
        }
        else if ((mldsaKey != NULL) &&
                    (mldsaKey->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)) {
            int slhdsaKt = SlhDsaParamToKeyType(slhDsaKey->params->param);
            if (slhdsaKt != 0) {
                cert->keyType = slhdsaKt;
            }
            else {
                ret = BAD_FUNC_ARG;
            }
        }
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
        else if (lmsKey != NULL) {
            cert->keyType = LMS_KEY;
        }
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
        else if (xmssKey != NULL) {
            cert->keyType = xmssKey->is_xmssmt ? XMSSMT_KEY : XMSS_KEY;
        }
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
        else {
            ret = BAD_FUNC_ARG;
        }
    }
    if ((ret == 0) && (cert->serialSz == 0)) {
        /* Generate random serial number. */
        cert->serialSz = CTC_GEN_SERIAL_SZ;
        ret = GenerateInteger(rng, cert->serial, CTC_GEN_SERIAL_SZ);
    }
    if (ret == 0) {
        /* Determine issuer name size. */
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA) || \
        defined(WOLFSSL_CERT_REQ)
        issRawLen = (word32)XSTRLEN((const char*)cert->issRaw);
        if (issRawLen > 0) {
            issuerSz = min(sizeof(cert->issRaw), issRawLen);
        }
        else
    #endif
        {
            /* Calculate issuer name encoding size. If the cert is self-signed
             * use the subject instead of the issuer. */
            ret = SetNameEx(NULL, WC_ASN_NAME_MAX, cert->selfSigned ?
                                 &cert->subject : &cert->issuer, cert->heap);
            issuerSz = (word32)ret;
        }
    }
    if (ret >= 0) {
        /* Determine subject name size. */
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA) || \
        defined(WOLFSSL_CERT_REQ)
        sbjRawLen = (word32)XSTRLEN((const char*)cert->sbjRaw);
        if (sbjRawLen > 0) {
            subjectSz = min(sizeof(cert->sbjRaw), sbjRawLen);
        }
        else
    #endif
        {
            /* Calculate subject name encoding size. */
            ret = SetNameEx(NULL, WC_ASN_NAME_MAX, &cert->subject,
                                  cert->heap);
            subjectSz = (word32)ret;
        }
    }
    if (ret >= 0) {
        /* Calculate public key encoding size. */
        ret = EncodePublicKey(cert->keyType, NULL, 0, rsaKey,
                eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
                mldsaKey, slhDsaKey, lmsKey, xmssKey);
        publicKeySz = (word32)ret;
    }
    if (ret >= 0) {
        /* Calculate extensions encoding size - may be 0. */
        ret = EncodeExtensions(cert, NULL, 0, 0);
        extSz = (word32)ret;
    }
    if (ret >= 0) {
        /* Don't write out outer sequence - only doing body. */
        dataASN[X509CERTASN_IDX_SEQ].noOut = 1;
        /* Set version, serial number and signature OID */
        SetASN_Int8Bit(&dataASN[X509CERTASN_IDX_TBS_VER_INT],
                       (byte)cert->version);
        SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_SERIAL], cert->serial,
                (word32)cert->serialSz);
#ifdef WOLFSSL_DUAL_ALG_CERTS
        if (cert->sigType == 0) {
            /* sigOID being 0 indicates preTBS. Do not encode signature. */
            dataASN[X509CERTASN_IDX_TBS_ALGOID_SEQ].noOut = 1;
            dataASN[X509CERTASN_IDX_TBS_ALGOID_OID].noOut = 1;
            dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
    #ifdef WC_RSA_PSS
            dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS].noOut = 1;
    #endif

        }
        else
#endif /* WOLFSSL_DUAL_ALG_CERTS */
        {
            SetASN_OID(&dataASN[X509CERTASN_IDX_TBS_ALGOID_OID],
                       (word32)cert->sigType, oidSigType);
        }

        if (IsSigAlgoNoParams((word32)cert->sigType)) {
            /* No NULL tagged item with ECDSA and EdDSA signature OIDs. */
            dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS_NULL].noOut = 1;
        }
    #ifdef WC_RSA_PSS
        /* TODO: Encode RSA PSS parameters. */
        dataASN[X509CERTASN_IDX_TBS_ALGOID_PARAMS].noOut = 1;
    #endif
        if (issRawLen > 0) {
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA) || \
        defined(WOLFSSL_CERT_REQ)
            /* Put in encoded issuer name. */
            SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ],
                    cert->issRaw, issuerSz);
    #endif
        }
        else {
            /* Leave space for issuer name. */
            SetASN_ReplaceBuffer(&dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ],
                    NULL, issuerSz);
        }

        if (cert->beforeDateSz && cert->afterDateSz) {
            if (cert->beforeDate[0] == ASN_UTC_TIME) {
                /* Make space for before date data. */
                SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC],
                        cert->beforeDate + 2, ASN_UTC_TIME_SIZE - 1);
                /* Don't put out Generalized Time before data. */
                dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT].noOut = 1;
            }
            else {
                /* Don't put out UTC before data. */
                dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1;
                /* Make space for before date data. */
                SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT],
                        cert->beforeDate + 2, ASN_GEN_TIME_SZ);
            }
            if (cert->afterDate[0] == ASN_UTC_TIME) {
                /* Make space for after date data. */
                SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC],
                        cert->afterDate + 2, ASN_UTC_TIME_SIZE - 1);
                /* Don't put out UTC Generalized Time after data. */
                dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT].noOut = 1;
            }
            else {
                /* Don't put out UTC after data. */
                dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1;
                /* Make space for after date data. */
                SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT],
                        cert->afterDate + 2, ASN_GEN_TIME_SZ);
            }
        }
        else
        {
            /* Don't put out UTC before data. */
            dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_UTC].noOut = 1;
            /* Make space for before date data. */
            SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT],
                    NULL, ASN_GEN_TIME_SZ);
            /* Don't put out UTC after data. */
            dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_UTC].noOut = 1;
            /* Make space for after date data. */
            SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT],
                    NULL, ASN_GEN_TIME_SZ);
        }
        if (sbjRawLen > 0) {
            /* Put in encoded subject name. */
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA) || \
        defined(WOLFSSL_CERT_REQ)
            SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ],
                    cert->sbjRaw, subjectSz);
    #endif
        }
        else {
            /* Leave space for subject name. */
            SetASN_ReplaceBuffer(&dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ],
                    NULL, subjectSz);
        }
        /* Leave space for public key. */
        SetASN_ReplaceBuffer(&dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ],
                NULL, publicKeySz);
        /* Replacement buffer instead of algorithm identifier items. */
        SetASNItem_NoOut(dataASN,
                X509CERTASN_IDX_TBS_SPUBKEYINFO_ALGO_SEQ,
                X509CERTASN_IDX_TBS_SPUBKEYINFO_PUBKEY);
        /* issuerUniqueID and subjectUniqueID not supported. */
        dataASN[X509CERTASN_IDX_TBS_ISSUERUID].noOut = 1;
        dataASN[X509CERTASN_IDX_TBS_SUBJECTUID].noOut = 1;
        /* Leave space for extensions if any set into certificate object. */
        if (extSz > 0) {
            SetASN_Buffer(&dataASN[X509CERTASN_IDX_TBS_EXT_SEQ], NULL, extSz);
        }
        else {
            SetASNItem_NoOutNode(dataASN, x509CertASN,
                    X509CERTASN_IDX_TBS_EXT, x509CertASN_Length);
        }
        /* No signature - added later. */
        SetASNItem_NoOut(dataASN, X509CERTASN_IDX_SIGALGO_SEQ,
                X509CERTASN_IDX_SIGNATURE);

        /* Calculate encoded certificate body size. */
        ret = SizeASN_Items(x509CertASN, dataASN, x509CertASN_Length, &sz);
    }
    /* Check buffer is big enough for encoded data. */
    if ((ret == 0) && (sz > derSz)) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode certificate body into buffer. */
        SetASN_Items(x509CertASN, dataASN, x509CertASN_Length, derBuffer);

        if (issRawLen == 0) {
            /* Encode issuer name into buffer. Use the subject as the issuer
             * if it is self-signed. Size will be correct because we did the
             * same for size. */
            /* safe cast -- the pointer is actually inside derBuffer. */
            ret = SetNameEx(
                (byte*)(wc_ptr_t)
                    dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ].data.buffer.data,
                dataASN[X509CERTASN_IDX_TBS_ISSUER_SEQ].data.buffer.length,
                cert->selfSigned ? &cert->subject : &cert->issuer, cert->heap);
        }
    }
    if ((ret >= 0) && (sbjRawLen == 0)) {
        /* Encode subject name into buffer. */
        /* safe cast -- the pointer is actually inside derBuffer. */
        ret = SetNameEx(
            (byte*)(wc_ptr_t)
                dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].data.buffer.data,
            dataASN[X509CERTASN_IDX_TBS_SUBJECT_SEQ].data.buffer.length,
            &cert->subject, cert->heap);
    }
    if (ret >= 0) {
        if (cert->beforeDateSz == 0 || cert->afterDateSz == 0)
        {
            /* Encode validity into buffer. */
            /* safe casts -- the pointers are actually inside derBuffer. */
            ret = SetValidity(
                (byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTB_GT]
                               .data.buffer.data,
                (byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_VALIDITY_NOTA_GT]
                               .data.buffer.data, cert->daysValid);
        }
    }
    if (ret >= 0) {
        /* Encode public key into buffer. */
        /* safe cast -- the pointer is actually inside derBuffer. */
        ret = EncodePublicKey(cert->keyType,
            (byte*)(wc_ptr_t)dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ]
                           .data.buffer.data,
            (int)dataASN[X509CERTASN_IDX_TBS_SPUBKEYINFO_SEQ]
                           .data.buffer.length,
            rsaKey, eccKey, ed25519Key, ed448Key, dsaKey,
            falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey);
    }
    if ((ret >= 0) && (!dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].noOut)) {
        /* Encode extensions into buffer. */
        /* safe cast -- the pointer is actually inside derBuffer. */
        ret = EncodeExtensions(
            cert,
            (byte*)(wc_ptr_t)
                dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].data.buffer.data,
            dataASN[X509CERTASN_IDX_TBS_EXT_SEQ].data.buffer.length, 0);
    }
    if (ret >= 0) {
        /* Store encoded certificate body size. */
        cert->bodySz = (int)sz;
        /* Return the encoding size. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Make an x509 Certificate v3 from cert input using any
 * key type, and write to buffer.
 *
 * @param [in, out] cert      Certificate object.
 * @param [out]     derBuffer Buffer to write der in.
 * @param [in]      derSz     Der buffer size.
 * @param [in]      keyType   The type of key.
 * @param [in]      key       Key data.
 * @param [in]      rng       Random number generator.
 * @return          Size of encoded data in bytes on success.
 * @return          < 0 on error
 * */
int wc_MakeCert_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
                   void* key, WC_RNG* rng)
{
    RsaKey*            rsaKey = NULL;
    DsaKey*            dsaKey = NULL;
    ecc_key*           eccKey = NULL;
    ed25519_key*       ed25519Key = NULL;
    ed448_key*         ed448Key = NULL;
    falcon_key*        falconKey = NULL;
    wc_MlDsaKey*       mldsaKey = NULL;
    SlhDsaKey*         slhDsaKey = NULL;
    LmsKey*            lmsKey = NULL;
    XmssKey*           xmssKey = NULL;

    if (keyType == RSA_TYPE)
        rsaKey = (RsaKey*)key;
    else if (keyType == DSA_TYPE)
        dsaKey = (DsaKey*)key;
    else if (keyType == ECC_TYPE)
        eccKey = (ecc_key*)key;
    else if (keyType == ED25519_TYPE)
        ed25519Key = (ed25519_key*)key;
    else if (keyType == ED448_TYPE)
        ed448Key = (ed448_key*)key;
    else if (keyType == FALCON_LEVEL1_TYPE)
        falconKey = (falcon_key*)key;
    else if (keyType == FALCON_LEVEL5_TYPE)
        falconKey = (falcon_key*)key;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if (keyType == DILITHIUM_LEVEL2_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL3_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL5_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#endif
    else if (keyType == ML_DSA_44_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_65_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_87_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#ifdef WOLFSSL_HAVE_SLHDSA
    else if (IsSlhDsaKeyType(keyType))
        slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    else if (keyType == LMS_TYPE)
        lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    /* The selector must match the key's actual tree variant so XMSS_TYPE and
     * XMSSMT_TYPE are not silently interchangeable. */
    else if (keyType == XMSS_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
    else if (keyType == XMSSMT_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && !xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
#endif

    return MakeAnyCert(cert, derBuffer, derSz, rsaKey, eccKey, rng, dsaKey,
                       ed25519Key, ed448Key, falconKey, mldsaKey,
                       slhDsaKey, lmsKey, xmssKey);
}

/* Make an x509 Certificate v3 RSA or ECC from cert input, write to buffer */
WOLFSSL_ABI
int wc_MakeCert(Cert* cert, byte* derBuffer, word32 derSz, RsaKey* rsaKey,
             ecc_key* eccKey, WC_RNG* rng)
{
    return MakeAnyCert(cert, derBuffer, derSz, rsaKey, eccKey, rng, NULL, NULL,
                       NULL, NULL, NULL, NULL, NULL, NULL);
}


#ifdef WOLFSSL_CERT_REQ

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for Certificate Request body.
 * PKCS #10: RFC 2986, 4.1 - CertificationRequestInfo
 */
static const ASNItem certReqBodyASN[] = {
/* SEQ             */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                             /* version */
/* VER             */     { 1, ASN_INTEGER, 0, 0, 0 },
                                             /* subject */
/* SUBJ_SEQ        */     { 1, ASN_SEQUENCE, 1, 0, 0 },
                                             /* subjectPKInfo */
/* SPUBKEYINFO_SEQ */     { 1, ASN_SEQUENCE, 1, 0, 0 },
                                             /*  attributes*/
/* ATTRS           */     { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
                                                 /* Challenge Password Attribute */
/* ATTRS_CPW_SEQ   */         { 2, ASN_SEQUENCE, 1, 1, 1 },
/* ATTRS_CPW_OID   */             { 3, ASN_OBJECT_ID, 0, 0, 0 },
/* ATTRS_CPW_SET   */             { 3, ASN_SET, 1, 1, 0 },
/* ATTRS_CPW_PS    */                 { 4, ASN_PRINTABLE_STRING, 0, 0, 0 },
/* ATTRS_CPW_UTF   */                 { 4, ASN_UTF8STRING, 0, 0, 0 },
/* ATTRS_USN_SEQ   */         { 2, ASN_SEQUENCE, 1, 1, 1 },
/* ATTRS_USN_OID   */             { 3, ASN_OBJECT_ID, 0, 0, 0 },
/* ATTRS_USN_SET   */             { 3, ASN_SET, 1, 1, 0 },
/* ATTRS_USN_PS    */                 { 4, ASN_PRINTABLE_STRING, 0, 0, 0 },
/* ATTRS_USN_UTF   */                 { 4, ASN_UTF8STRING, 0, 0, 0 },
                                                 /* Extensions Attribute */
/* EXT_SEQ         */         { 2, ASN_SEQUENCE, 1, 1, 1 },
/* EXT_OID         */             { 3, ASN_OBJECT_ID, 0, 0, 0 },
/* EXT_SET         */             { 3, ASN_SET, 1, 1, 0 },
/* EXT_BODY        */                 { 4, ASN_SEQUENCE, 1, 0, 0 },
};
enum {
    CERTREQBODYASN_IDX_SEQ = 0,
    CERTREQBODYASN_IDX_VER,
    CERTREQBODYASN_IDX_SUBJ_SEQ,
    CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ,
    CERTREQBODYASN_IDX_ATTRS,
    CERTREQBODYASN_IDX_ATTRS_CPW_SEQ,
    CERTREQBODYASN_IDX_ATTRS_CPW_OID,
    CERTREQBODYASN_IDX_ATTRS_CPW_SET,
    CERTREQBODYASN_IDX_ATTRS_CPW_PS,
    CERTREQBODYASN_IDX_ATTRS_CPW_UTF,
    CERTREQBODYASN_IDX_ATTRS_USN_SEQ,
    CERTREQBODYASN_IDX_ATTRS_USN_OID,
    CERTREQBODYASN_IDX_ATTRS_USN_SET,
    CERTREQBODYASN_IDX_ATTRS_USN_PS,
    CERTREQBODYASN_IDX_ATTRS_USN_UTF,
    CERTREQBODYASN_IDX_EXT_SEQ,
    CERTREQBODYASN_IDX_EXT_OID,
    CERTREQBODYASN_IDX_EXT_SET,
    CERTREQBODYASN_IDX_EXT_BODY
};

/* Number of items in ASN.1 template for Certificate Request body. */
#define certReqBodyASN_Length (sizeof(certReqBodyASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
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, LmsKey* lmsKey, XmssKey* xmssKey)
{
    DECL_ASNSETDATA(dataASN, certReqBodyASN_Length);
    word32 publicKeySz = 0;
    word32 subjectSz = 0;
    word32 extSz = 0;
    word32 sz = 0;
    int ret = 0;
#if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
    word32 sbjRawSz = 0;
#endif

    /* Unused without PQC */
    (void)falconKey;
    (void)mldsaKey;
    (void)slhDsaKey;
    (void)lmsKey;
    (void)xmssKey;

    CALLOC_ASNSETDATA(dataASN, certReqBodyASN_Length, ret, cert->heap);

    if (ret == 0) {
        /* Set key type into certificate object based on key passed in. */
        if (rsaKey != NULL) {
            cert->keyType = RSA_KEY;
        }
        else if (eccKey != NULL) {
            cert->keyType = ECC_KEY;
        }
        else if (dsaKey != NULL) {
            cert->keyType = DSA_KEY;
        }
        else if (ed25519Key != NULL) {
            cert->keyType = ED25519_KEY;
        }
        else if (ed448Key != NULL) {
            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->level == WC_ML_DSA_44)) {
            cert->keyType = ML_DSA_44_KEY;
        }
        else if ((mldsaKey != NULL) &&
                    (mldsaKey->level == WC_ML_DSA_65)) {
            cert->keyType = ML_DSA_65_KEY;
        }
        else if ((mldsaKey != NULL) &&
                    (mldsaKey->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)) {
            int slhdsaKt = SlhDsaParamToKeyType(slhDsaKey->params->param);
            if (slhdsaKt != 0) {
                cert->keyType = slhdsaKt;
            }
            else {
                ret = BAD_FUNC_ARG;
            }
        }
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
        else if (lmsKey != NULL) {
            cert->keyType = LMS_KEY;
        }
#endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
        else if (xmssKey != NULL) {
            cert->keyType = xmssKey->is_xmssmt ? XMSSMT_KEY : XMSS_KEY;
        }
#endif /* WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */
        else {
            ret = BAD_FUNC_ARG;
        }
    }
    if (ret == 0) {
        /* Determine subject name size. */
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
        sbjRawSz = (word32)XSTRLEN((const char*)cert->sbjRaw);
        if (sbjRawSz > 0) {
            subjectSz = min(sizeof(cert->sbjRaw), sbjRawSz);
        }
        else
    #endif
        {
            ret = SetNameEx(NULL, WC_ASN_NAME_MAX, &cert->subject, cert->heap);
            subjectSz = (word32)ret;
        }
    }
    if (ret >= 0) {
        /* Determine encode public key size. */
         ret = EncodePublicKey(cert->keyType, NULL, 0, rsaKey,
             eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
             mldsaKey, slhDsaKey, lmsKey, xmssKey);
         publicKeySz = (word32)ret;
    }
    if (ret >= 0) {
        /* Determine encode extensions size. */
        ret = EncodeExtensions(cert, NULL, 0, 1);
        extSz = (word32)ret;
    }
    if (ret >= 0) {
        /* Set version. */
        SetASN_Int8Bit(&dataASN[CERTREQBODYASN_IDX_VER], (byte)cert->version);
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
        if (sbjRawSz > 0) {
            /* Put in encoded subject name. */
            SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_SUBJ_SEQ], cert->sbjRaw,
                    subjectSz);
        }
        else
    #endif
        {
            /* Leave space for subject name. */
            SetASN_ReplaceBuffer(&dataASN[CERTREQBODYASN_IDX_SUBJ_SEQ], NULL,
                    subjectSz);
        }
        /* Leave space for public key. */
        SetASN_ReplaceBuffer(&dataASN[CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ],
                NULL, publicKeySz);
        if (cert->challengePw[0] != '\0') {
            /* Add challenge password attribute. */
            /* Set challenge password OID. */
            SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_ATTRS_CPW_OID],
                attrChallengePasswordOid, sizeof(attrChallengePasswordOid));
            /* Enable the ASN template item with the appropriate tag. */
            if (cert->challengePwPrintableString) {
                /* PRINTABLE_STRING - set buffer */
                SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_ATTRS_CPW_PS],
                        (byte*)cert->challengePw,
                        (word32)XSTRLEN(cert->challengePw));
                /* UTF8STRING - don't encode */
                dataASN[CERTREQBODYASN_IDX_ATTRS_CPW_UTF].noOut = 1;
            }
            else {
                /* PRINTABLE_STRING - don't encode */
                dataASN[CERTREQBODYASN_IDX_ATTRS_CPW_PS].noOut = 1;
                /* UTF8STRING - set buffer */
                SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_ATTRS_CPW_UTF],
                        (byte*)cert->challengePw,
                        (word32)XSTRLEN(cert->challengePw));
            }
        }
        else {
            /* Leave out challenge password attribute items. */
            SetASNItem_NoOutNode(dataASN, certReqBodyASN,
                    CERTREQBODYASN_IDX_ATTRS_CPW_SEQ, certReqBodyASN_Length);
        }
        if (cert->unstructuredName[0] != '\0') {
            /* Add unstructured name attribute. */
            /* Set unstructured name OID. */
            SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_ATTRS_USN_OID],
                attrUnstructuredNameOid, sizeof(attrUnstructuredNameOid));
                /* PRINTABLE_STRING - set buffer */
                SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_ATTRS_USN_PS],
                        (byte*)cert->unstructuredName,
                        (word32)XSTRLEN(cert->unstructuredName));
                /* UTF8STRING - don't encode */
                dataASN[CERTREQBODYASN_IDX_ATTRS_USN_UTF].noOut = 1;
        }
        else {
            /* Leave out unstructured name attribute item. */
            SetASNItem_NoOutNode(dataASN, certReqBodyASN,
                    CERTREQBODYASN_IDX_ATTRS_USN_SEQ, certReqBodyASN_Length);
        }
        if (extSz > 0) {
            /* Set extension attribute OID. */
            SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_EXT_OID], attrExtensionRequestOid,
                sizeof(attrExtensionRequestOid));
            /* Leave space for data. */
            SetASN_Buffer(&dataASN[CERTREQBODYASN_IDX_EXT_BODY], NULL, extSz);
        }
        else {
            /* Leave out extension attribute items. */
            SetASNItem_NoOutNode(dataASN, certReqBodyASN,
                    CERTREQBODYASN_IDX_EXT_SEQ, certReqBodyASN_Length);
        }

        /* Calculate size of encoded certificate request body. */
        ret = SizeASN_Items(certReqBodyASN, dataASN, certReqBodyASN_Length,
                            &sz);
    }
    /* Check buffer is big enough for encoded data. */
    if ((ret == 0) && (sz > derSz)) {
        ret = BUFFER_E;
    }
    if (ret == 0 && derBuffer != NULL) {
        /* Encode certificate request body into buffer. */
        SetASN_Items(certReqBodyASN, dataASN, certReqBodyASN_Length, derBuffer);

        /* Put in generated data */
    #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA)
        if (sbjRawSz == 0)
    #endif
        {
            /* Encode subject name into space in buffer. */
            /* safe cast -- the pointer is actually inside derBuffer. */
            ret = SetNameEx(
                (byte*)(wc_ptr_t)
                    dataASN[CERTREQBODYASN_IDX_SUBJ_SEQ].data.buffer.data,
                dataASN[CERTREQBODYASN_IDX_SUBJ_SEQ].data.buffer.length,
                &cert->subject, cert->heap);
        }
    }
    if (ret >= 0 && derBuffer != NULL) {
        /* Encode public key into space in buffer. */
        /* safe cast -- the pointer is actually inside derBuffer. */
        ret = EncodePublicKey(
            cert->keyType,
            (byte*)(wc_ptr_t)
                dataASN[CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ].data.buffer.data,
            (int)dataASN[CERTREQBODYASN_IDX_SPUBKEYINFO_SEQ].data.buffer.length,
            rsaKey, eccKey, ed25519Key, ed448Key, dsaKey, falconKey,
            mldsaKey, slhDsaKey, lmsKey, xmssKey);
    }
    if ((ret >= 0 && derBuffer != NULL) &&
            (!dataASN[CERTREQBODYASN_IDX_EXT_BODY].noOut)) {
        /* Encode extensions into space in buffer. */
        /* safe cast -- the pointer is actually inside derBuffer. */
        ret = EncodeExtensions(
            cert,
            (byte*)(wc_ptr_t)
                dataASN[CERTREQBODYASN_IDX_EXT_BODY].data.buffer.data,
            dataASN[CERTREQBODYASN_IDX_EXT_BODY].data.buffer.length, 1);
    }
    if (ret >= 0) {
        /* Store encoded certificate request body size. */
        cert->bodySz = (int)sz;
        /* Return the encoding size. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, cert->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
int wc_MakeCertReq_ex(Cert* cert, byte* derBuffer, word32 derSz, int keyType,
                      void* key)
{
    RsaKey*        rsaKey = NULL;
    DsaKey*        dsaKey = NULL;
    ecc_key*       eccKey = NULL;
    ed25519_key*   ed25519Key = NULL;
    ed448_key*     ed448Key = NULL;
    falcon_key*    falconKey = NULL;
    wc_MlDsaKey*   mldsaKey = NULL;
    SlhDsaKey*     slhDsaKey = NULL;
    LmsKey*        lmsKey = NULL;
    XmssKey*       xmssKey = NULL;

    if (keyType == RSA_TYPE)
        rsaKey = (RsaKey*)key;
    else if (keyType == DSA_TYPE)
        dsaKey = (DsaKey*)key;
    else if (keyType == ECC_TYPE)
        eccKey = (ecc_key*)key;
    else if (keyType == ED25519_TYPE)
        ed25519Key = (ed25519_key*)key;
    else if (keyType == ED448_TYPE)
        ed448Key = (ed448_key*)key;
    else if (keyType == FALCON_LEVEL1_TYPE)
        falconKey = (falcon_key*)key;
    else if (keyType == FALCON_LEVEL5_TYPE)
        falconKey = (falcon_key*)key;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if (keyType == DILITHIUM_LEVEL2_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL3_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL5_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#endif
    else if (keyType == ML_DSA_44_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_65_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_87_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#ifdef WOLFSSL_HAVE_SLHDSA
    else if (IsSlhDsaKeyType(keyType))
        slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    else if (keyType == LMS_TYPE)
        lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    /* The selector must match the key's actual tree variant so XMSS_TYPE and
     * XMSSMT_TYPE are not silently interchangeable. */
    else if (keyType == XMSS_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
    else if (keyType == XMSSMT_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && !xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
#endif

    return MakeCertReq(cert, derBuffer, derSz, rsaKey, dsaKey, eccKey,
                       ed25519Key, ed448Key, falconKey, mldsaKey,
                       slhDsaKey, lmsKey, xmssKey);
}

WOLFSSL_ABI
int wc_MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
                   RsaKey* rsaKey, ecc_key* eccKey)
{
    return MakeCertReq(cert, derBuffer, derSz, rsaKey, NULL, eccKey, NULL,
                       NULL, NULL, NULL, NULL, NULL, NULL);
}
#endif /* WOLFSSL_CERT_REQ */



#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_REQ)
/* Internal function to create signature using callback
 * This allows external signing implementations (e.g., TPM, HSM) without
 * requiring the crypto callback infrastructure.
 */
static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf,
    word32 sz, byte* sig, word32 sigSz, int sigAlgoType, int keyType,
    wc_SignCertCb signCb, void* signCtx, WC_RNG* rng, void* heap)
{
    int ret = 0;
    word32 outLen;

    (void)rng;
#ifdef WOLFSSL_NO_MALLOC
    (void)heap;
#endif

    /* Validate keyType - only RSA and ECC are supported for callback signing.
     * Ed25519, Ed448, and post-quantum algorithms sign messages directly,
     * not hashes, so they cannot use the callback path. */
#if !defined(NO_RSA) && defined(HAVE_ECC)
    if (keyType != RSA_TYPE && keyType != ECC_TYPE) {
        return BAD_FUNC_ARG;
    }
#elif !defined(NO_RSA)
    if (keyType != RSA_TYPE) {
        return BAD_FUNC_ARG;
    }
#elif defined(HAVE_ECC)
    if (keyType != ECC_TYPE) {
        return BAD_FUNC_ARG;
    }
#else
    (void)keyType;
    return NOT_COMPILED_IN;
#endif

    switch (certSignCtx->state) {
    case CERTSIGN_STATE_BEGIN:
    case CERTSIGN_STATE_DIGEST:
        certSignCtx->state = CERTSIGN_STATE_DIGEST;
#ifndef WOLFSSL_NO_MALLOC
        certSignCtx->digest = (byte*)XMALLOC(WC_MAX_DIGEST_SIZE, heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (certSignCtx->digest == NULL) {
            ret = MEMORY_E;
            goto exit_ms;
        }
#endif
        ret = HashForSignature(buf, sz, (word32)sigAlgoType, certSignCtx->digest,
                               &certSignCtx->typeH, &certSignCtx->digestSz, 0,
                               NULL, INVALID_DEVID);
        certSignCtx->state = CERTSIGN_STATE_ENCODE;
        if (ret != 0) {
            goto exit_ms;
        }
        FALL_THROUGH;

    case CERTSIGN_STATE_ENCODE:
        /* For RSA, encode the digest with algorithm identifier */
#ifndef NO_RSA
        if (keyType == RSA_TYPE) {
#ifndef WOLFSSL_NO_MALLOC
            certSignCtx->encSig = (byte*)XMALLOC(MAX_DER_DIGEST_SZ, heap,
                DYNAMIC_TYPE_TMP_BUFFER);
            if (certSignCtx->encSig == NULL) {
                ret = MEMORY_E;
                goto exit_ms;
            }
#endif
            /* typeH was stored in certSignCtx by HashForSignature */
            certSignCtx->encSigSz = (int)wc_EncodeSignature(certSignCtx->encSig,
                certSignCtx->digest, (word32)certSignCtx->digestSz,
                certSignCtx->typeH);
        }
#endif /* !NO_RSA */
        FALL_THROUGH;

    case CERTSIGN_STATE_DO:
        certSignCtx->state = CERTSIGN_STATE_DO;
        outLen = sigSz;

        /* Call the user-provided signing callback */
#ifndef NO_RSA
        if (keyType == RSA_TYPE) {
            /* RSA: pass encoded digest */
            ret = signCb(certSignCtx->encSig, (word32)certSignCtx->encSigSz,
                         sig, &outLen, sigAlgoType, keyType, signCtx);
        }
        else
#endif /* !NO_RSA */
        {
            /* ECC: pass raw hash */
            ret = signCb(certSignCtx->digest, (word32)certSignCtx->digestSz,
                         sig, &outLen, sigAlgoType, keyType, signCtx);
        }

    #ifdef WOLFSSL_ASYNC_CRYPT
        /* If callback returns WC_PENDING_E, preserve state for re-entry */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            return ret;
        }
    #endif

        if (ret == 0) {
            ret = (int)outLen;
        }
        break;
    }

exit_ms:
#ifndef WOLFSSL_NO_MALLOC
#ifndef NO_RSA
    if (keyType == RSA_TYPE) {
        XFREE(certSignCtx->encSig, heap, DYNAMIC_TYPE_TMP_BUFFER);
        certSignCtx->encSig = NULL;
    }
#endif /* !NO_RSA */
    XFREE(certSignCtx->digest, heap, DYNAMIC_TYPE_TMP_BUFFER);
    certSignCtx->digest = NULL;
#endif

    /* reset state */
    certSignCtx->state = CERTSIGN_STATE_BEGIN;
    certSignCtx->digestSz = 0;
    certSignCtx->typeH = 0;

    if (ret < 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}
#endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */



static int SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
                    RsaKey* rsaKey, ecc_key* eccKey, ed25519_key* ed25519Key,
                    ed448_key* ed448Key, falcon_key* falconKey,
                    wc_MlDsaKey* mldsaKey, SlhDsaKey* slhDsaKey,
                    LmsKey* lmsKey, XmssKey* xmssKey, WC_RNG* rng)
{
    int sigSz = 0;
    int ret;
    void* heap = NULL;
    /* The signature buffer is sized from the key at runtime. */
    int maxSigSz;
    CertSignCtx  certSignCtx_lcl;
    CertSignCtx* certSignCtx = &certSignCtx_lcl;

    XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));

    if (requestSz < 0)
        return requestSz;

    /* Async crypto reuses the signing key's embedded CertSignCtx; only RSA and
     * ECC keys carry one. */
    if (rsaKey) {
    #ifndef NO_RSA
    #ifdef WOLFSSL_ASYNC_CRYPT
        certSignCtx = &rsaKey->certSignCtx;
    #endif
    #else
        return NOT_COMPILED_IN;
    #endif /* NO_RSA */
    }
    else if (eccKey) {
    #ifdef HAVE_ECC
    #ifdef WOLFSSL_ASYNC_CRYPT
        certSignCtx = &eccKey->certSignCtx;
    #endif
    #else
        return NOT_COMPILED_IN;
    #endif /* HAVE_ECC */
    }
    heap = GetSigningKeyHeap(rsaKey, eccKey, ed25519Key, ed448Key, mldsaKey,
        slhDsaKey, lmsKey, xmssKey);

    /* The signatureAlgorithm OID is written from sType while the signature is
     * produced from the key, so reject a mismatch rather than emit a cert
     * whose advertised algorithm contradicts the signing key. */
    ret = CheckSigTypeForKey(sType, rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey);
    if (ret != 0) {
        WOLFSSL_MSG("Signature type does not match signing key");
        return ret;
    }

    /* Size the signature buffer from the key in use. */
    maxSigSz = GetSignatureBufferSz(rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey);
    if (maxSigSz <= 0)
        return (maxSigSz < 0) ? maxSigSz : ALGO_ID_E;

#ifndef WOLFSSL_NO_MALLOC
    if (certSignCtx->sig == NULL) {
        certSignCtx->sig = (byte*)XMALLOC((word32)maxSigSz, heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (certSignCtx->sig == NULL)
            return MEMORY_E;
    }
#else
    /* Without dynamic memory the signature buffer is a fixed
     * WOLFSSL_MAX_SIG_SZ array in CertSignCtx. LMS/XMSS signatures are
     * parameter-dependent and can be larger, so reject rather than overflow
     * the fixed buffer. */
    if ((word32)maxSigSz > WOLFSSL_MAX_SIG_SZ) {
        WOLFSSL_MSG("Signature larger than fixed CertSignCtx buffer");
        return BUFFER_E;
    }
#endif

    sigSz = MakeSignature(certSignCtx, buf, (word32)requestSz, certSignCtx->sig,
        (word32)maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey, rng, (word32)sType,
        heap);
#ifdef WOLFSSL_ASYNC_CRYPT
    if (sigSz == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        /* Not free'ing certSignCtx->sig here because it could still be in use
         * with async operations. */
        return sigSz;
    }
#endif

    if (sigSz >= 0) {
        if (requestSz + MAX_SEQ_SZ * 2 + sigSz > (int)buffSz)
            sigSz = BUFFER_E;
        else
            sigSz = AddSignature(buf, requestSz, certSignCtx->sig, sigSz,
                                 sType);
    }

#ifndef WOLFSSL_NO_MALLOC
    XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
    certSignCtx->sig = NULL;
#endif

    return sigSz;
}

#ifdef WOLFSSL_DUAL_ALG_CERTS
/* Generate a signature from input buffer using
 * any key type.
 *
 * @param [out] sig     The signature buffer to write in.
 * @param [out] sigsz   The signature buffer size.
 * @param [in]  sType   The signature type.
 * @param [in]  buf     The input buf to sign.
 * @param [in]  bufSz   The buffer size
 * @param [in]  keyType The key type.
 * @param [in]  key     Key data.
 * @param [in]  rng     Random number generator.
 *
 * @return  Size of signature on success.
 * @return  < 0 on error.
 * */
int wc_MakeSigWithBitStr(byte *sig, int sigSz, int sType, byte* buf,
                         word32 bufSz, int keyType, void* key, WC_RNG* rng)
{
    RsaKey*            rsaKey = NULL;
    ecc_key*           eccKey = NULL;
    ed25519_key*       ed25519Key = NULL;
    ed448_key*         ed448Key = NULL;
    falcon_key*        falconKey = NULL;
    wc_MlDsaKey*       mldsaKey = NULL;
    SlhDsaKey*         slhDsaKey = NULL;
    int ret = 0;
    int headerSz;
    int maxSigSz;
    void* heap = NULL;
    CertSignCtx  certSignCtx_lcl;
    CertSignCtx* certSignCtx = &certSignCtx_lcl;

    WOLFSSL_ENTER("wc_MakeSigWithBitStr");

    if ((sig == NULL) || (sigSz <= 0)) {
        return BAD_FUNC_ARG;
    }

    XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));

    switch (keyType)
    {
        case RSA_TYPE:
            rsaKey = (RsaKey*)key;
            break;
        case ECC_TYPE:
            eccKey = (ecc_key*)key;
            break;
        case ED25519_TYPE:
            ed25519Key = (ed25519_key*)key;
            break;
        case ED448_TYPE:
            ed448Key = (ed448_key*)key;
            break;
        case FALCON_LEVEL1_TYPE:
        case FALCON_LEVEL5_TYPE:
            falconKey = (falcon_key*)key;
            break;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
        case DILITHIUM_LEVEL2_TYPE:
        case DILITHIUM_LEVEL3_TYPE:
        case DILITHIUM_LEVEL5_TYPE:
#endif
        case ML_DSA_44_TYPE:
        case ML_DSA_65_TYPE:
        case ML_DSA_87_TYPE:
            mldsaKey = (wc_MlDsaKey*)key;
            break;
        case SLH_DSA_SHAKE_128F_TYPE:
        case SLH_DSA_SHAKE_192F_TYPE:
        case SLH_DSA_SHAKE_256F_TYPE:
        case SLH_DSA_SHAKE_128S_TYPE:
        case SLH_DSA_SHAKE_192S_TYPE:
        case SLH_DSA_SHAKE_256S_TYPE:
    #ifdef WOLFSSL_SLHDSA_SHA2
        case SLH_DSA_SHA2_128F_TYPE:
        case SLH_DSA_SHA2_192F_TYPE:
        case SLH_DSA_SHA2_256F_TYPE:
        case SLH_DSA_SHA2_128S_TYPE:
        case SLH_DSA_SHA2_192S_TYPE:
        case SLH_DSA_SHA2_256S_TYPE:
    #endif
            slhDsaKey = (SlhDsaKey*)key;
            break;
        default:
            return BAD_FUNC_ARG;
    }

    /* Async crypto reuses the signing key's embedded CertSignCtx; only RSA and
     * ECC keys carry one. */
    if (rsaKey) {
    #ifndef NO_RSA
    #ifdef WOLFSSL_ASYNC_CRYPT
        certSignCtx = &rsaKey->certSignCtx;
    #endif
    #else
        return NOT_COMPILED_IN;
    #endif /* NO_RSA */
    }
    else if (eccKey) {
    #ifdef HAVE_ECC
    #ifdef WOLFSSL_ASYNC_CRYPT
        certSignCtx = &eccKey->certSignCtx;
    #endif
    #else
        return NOT_COMPILED_IN;
    #endif /* HAVE_ECC */
    }
    heap = GetSigningKeyHeap(rsaKey, eccKey, ed25519Key, ed448Key, mldsaKey,
        slhDsaKey, NULL, NULL);

    /* The signatureAlgorithm OID is written from sType while the signature is
     * produced from the key, so reject a mismatch rather than emit a cert
     * whose advertised algorithm contradicts the signing key. */
    ret = CheckSigTypeForKey(sType, rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, NULL, NULL);
    if (ret != 0) {
        WOLFSSL_MSG("Signature type does not match signing key");
        return ret;
    }

    /* Size the signature buffer from the key in use. */
    maxSigSz = GetSignatureBufferSz(rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, NULL, NULL);
    if (maxSigSz <= 0)
        return (maxSigSz < 0) ? maxSigSz : ALGO_ID_E;

#ifndef WOLFSSL_NO_MALLOC
    if (certSignCtx->sig == NULL) {
        certSignCtx->sig = (byte*)XMALLOC((word32)maxSigSz, heap,
            DYNAMIC_TYPE_TMP_BUFFER);
        if (certSignCtx->sig == NULL)
            return MEMORY_E;
    }
#else
    if ((word32)maxSigSz > WOLFSSL_MAX_SIG_SZ) {
        WOLFSSL_MSG("Signature larger than fixed CertSignCtx buffer");
        return BUFFER_E;
    }
#endif

    ret = MakeSignature(certSignCtx, buf, (word32)bufSz, certSignCtx->sig,
        (word32)maxSigSz, rsaKey, eccKey, ed25519Key, ed448Key,
        falconKey, mldsaKey, slhDsaKey, NULL, NULL, rng, (word32)sType, heap);
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        /* Not free'ing certSignCtx->sig here because it could still be in use
         * with async operations. */
        return ret;
    }
#endif

    if (ret <= 0) {
    #ifndef WOLFSSL_NO_MALLOC
        XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
        certSignCtx->sig = NULL;
    #endif
        return ret;
    }

    headerSz = SetBitString(ret, 0, NULL);
    if (headerSz + ret > sigSz) {
        ret = BUFFER_E;
    }

    if (ret > 0) {
       sig += SetBitString(ret, 0, sig);
       XMEMCPY(sig, certSignCtx->sig, ret);
       ret += headerSz;
    }

#ifndef WOLFSSL_NO_MALLOC
    XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
    certSignCtx->sig = NULL;
#endif
    return ret;
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */

/* Sign an x509 Certificate v3 from cert input using any
 * key type, and write to buffer.
 *
 * @param [in]     requestSz Size of requested data to sign.
 * @param [in]     sType     The signature type.
 * @param [in,out] buf       Der buffer to sign.
 * @param [in]     buffSz    Der buffer size.
 * @param [in]     keyType   The type of key.
 * @param [in]     key       Key data.
 * @param [in]     rng       Random number generator.
 *
 * @return  Size of signature on success.
 * @return  < 0 on error
 * */
int wc_SignCert_ex(int requestSz, int sType, byte* buf, word32 buffSz,
                   int keyType, void* key, WC_RNG* rng)
{
    RsaKey*            rsaKey = NULL;
    ecc_key*           eccKey = NULL;
    ed25519_key*       ed25519Key = NULL;
    ed448_key*         ed448Key = NULL;
    falcon_key*        falconKey = NULL;
    wc_MlDsaKey*       mldsaKey = NULL;
    SlhDsaKey*         slhDsaKey = NULL;
    LmsKey*            lmsKey = NULL;
    XmssKey*           xmssKey = NULL;

    if (keyType == RSA_TYPE)
        rsaKey = (RsaKey*)key;
    else if (keyType == ECC_TYPE)
        eccKey = (ecc_key*)key;
    else if (keyType == ED25519_TYPE)
        ed25519Key = (ed25519_key*)key;
    else if (keyType == ED448_TYPE)
        ed448Key = (ed448_key*)key;
    else if (keyType == FALCON_LEVEL1_TYPE)
        falconKey = (falcon_key*)key;
    else if (keyType == FALCON_LEVEL5_TYPE)
        falconKey = (falcon_key*)key;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if (keyType == DILITHIUM_LEVEL2_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL3_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL5_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#endif
    else if (keyType == ML_DSA_44_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_65_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_87_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#ifdef WOLFSSL_HAVE_SLHDSA
    else if (IsSlhDsaKeyType(keyType))
        slhDsaKey = (SlhDsaKey*)key;
#endif
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
    else if (keyType == LMS_TYPE)
        lmsKey = (LmsKey*)key;
#endif
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
    /* The selector must match the key's actual tree variant so XMSS_TYPE and
     * XMSSMT_TYPE are not silently interchangeable. */
    else if (keyType == XMSS_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
    else if (keyType == XMSSMT_TYPE) {
        xmssKey = (XmssKey*)key;
        if (xmssKey != NULL && !xmssKey->is_xmssmt)
            return BAD_FUNC_ARG;
    }
#endif

    return SignCert(requestSz, sType, buf, buffSz, rsaKey, eccKey, ed25519Key,
                    ed448Key, falconKey, mldsaKey, slhDsaKey, lmsKey, xmssKey,
                    rng);
}

int wc_SignCert(int requestSz, int sType, byte* buf, word32 buffSz,
                RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    return SignCert(requestSz, sType, buf, buffSz, rsaKey, eccKey, NULL, NULL,
                    NULL, NULL, NULL, NULL, NULL, rng);
}

/* Sign certificate/CSR using a callback function
 * This allows external signing implementations (e.g., TPM, HSM)
 * without requiring the crypto callback infrastructure.
 *
 * NOTE: This function does NOT support async crypto (WOLFSSL_ASYNC_CRYPT).
 * The certSignCtx is local to this function and cannot persist across
 * async re-entry. Use wc_SignCert or wc_SignCert_ex for async operations.
 *
 * @param [in]     requestSz Size of certificate body to sign.
 * @param [in]     sType     The signature type.
 * @param [in,out] buf       Der buffer to sign.
 * @param [in]     buffSz    Der buffer size.
 * @param [in]     keyType   The type of key (RSA_TYPE or ECC_TYPE only).
 * @param [in]     signCb    User signing callback.
 * @param [in]     signCtx   Context passed to callback.
 * @param [in]     rng       Random number generator (may be NULL).
 *
 * @return  Size of signature on success.
 * @return  BAD_FUNC_ARG if signCb or buf is NULL, buffSz is 0, or invalid
 *          keyType.
 * @return  < 0 on error
 */
#ifdef WOLFSSL_CERT_SIGN_CB
int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz,
                   int keyType, wc_SignCertCb signCb, void* signCtx,
                   WC_RNG* rng)
{
    int sigSz = 0;
    word32 sigCap = MAX_ENCODED_CLASSIC_SIG_SZ;
    CertSignCtx certSignCtx_lcl;
    CertSignCtx* certSignCtx = &certSignCtx_lcl;

    /* Validate parameters */
    if (signCb == NULL || buf == NULL || buffSz == 0) {
        return BAD_FUNC_ARG;
    }

    /* Validate keyType - only RSA and ECC supported */
#if !defined(NO_RSA) && defined(HAVE_ECC)
    if (keyType != RSA_TYPE && keyType != ECC_TYPE) {
        return BAD_FUNC_ARG;
    }
#elif !defined(NO_RSA)
    if (keyType != RSA_TYPE) {
        return BAD_FUNC_ARG;
    }
#elif defined(HAVE_ECC)
    if (keyType != ECC_TYPE) {
        return BAD_FUNC_ARG;
    }
#else
    (void)keyType;
    return NOT_COMPILED_IN;
#endif

    /* The callback produces the signature for keyType while the cert's
     * signatureAlgorithm OID is written from sType, so reject a family
     * mismatch (e.g. an ECDSA OID with an RSA key). */
#ifndef NO_RSA
    if (keyType == RSA_TYPE && !IsRsaSigType(sType))
        return ALGO_ID_E;
#endif
#ifdef HAVE_ECC
    if (keyType == ECC_TYPE && !IsEccSigType(sType))
        return ALGO_ID_E;
#endif

    XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));

    if (requestSz < 0) {
        return requestSz;
    }

    /* keyType is restricted to RSA_TYPE/ECC_TYPE above, so the signature is
     * a classic (non-PQC) one and fits MAX_ENCODED_CLASSIC_SIG_SZ. */
#ifndef WOLFSSL_NO_MALLOC
    certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, NULL,
        DYNAMIC_TYPE_TMP_BUFFER);
    if (certSignCtx->sig == NULL) {
        return MEMORY_E;
    }
#else
    /* Don't claim more capacity than the fixed sig buffer really has. */
    if (sigCap > (word32)sizeof(certSignCtx->sig))
        sigCap = (word32)sizeof(certSignCtx->sig);
#endif

    sigSz = MakeSignatureCb(certSignCtx, buf, (word32)requestSz,
        certSignCtx->sig, sigCap, sType, keyType,
        signCb, signCtx, rng, NULL);

#ifdef WOLFSSL_ASYNC_CRYPT
    if (sigSz == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        /* Async crypto not supported with wc_SignCert_cb because certSignCtx
         * is local and cannot persist across re-entry. Clean up and return
         * error. */
    #ifndef WOLFSSL_NO_MALLOC
        XFREE(certSignCtx->sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
        certSignCtx->sig = NULL;
    #endif
        return NOT_COMPILED_IN;
    }
#endif

    if (sigSz >= 0) {
        /* Check buffer has room for signature structure. This is an estimate
         * using MAX_SEQ_SZ * 2 to account for sequence headers and algorithm
         * identifier overhead. For precise sizing, call AddSignature with
         * NULL buffer first, but this estimate matches the existing pattern
         * used in SignCert. */
        if (requestSz + MAX_SEQ_SZ * 2 + sigSz > (int)buffSz) {
            sigSz = BUFFER_E;
        }
        else {
            sigSz = AddSignature(buf, requestSz, certSignCtx->sig, sigSz, sType);
        }
    }

#ifndef WOLFSSL_NO_MALLOC
    XFREE(certSignCtx->sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    certSignCtx->sig = NULL;
#endif

    return sigSz;
}
#endif /* WOLFSSL_CERT_SIGN_CB */




WOLFSSL_ABI
int wc_MakeSelfCert(Cert* cert, byte* buf, word32 buffSz,
                    RsaKey* key, WC_RNG* rng)
{
    int ret;

    ret = wc_MakeCert(cert, buf, buffSz, key, NULL, rng);
    if (ret < 0)
        return ret;

    return wc_SignCert(cert->bodySz, cert->sigType,
                       buf, buffSz, key, NULL, rng);
}


#ifdef WOLFSSL_CERT_EXT

/* Get raw subject from cert, which may contain OIDs not parsed by Decode.
   The raw subject pointer will only be valid while "cert" is valid. */
WOLFSSL_ABI
int wc_GetSubjectRaw(byte **subjectRaw, Cert *cert)
{
    int rc = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    if ((subjectRaw != NULL) && (cert != NULL)) {
        *subjectRaw = cert->sbjRaw;
        rc = 0;
    }
    return rc;
}

/* Set KID from public key */
static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey,
                                 ed25519_key* ed25519Key, ed448_key* ed448Key,
                                 falcon_key* falconKey,
                                 wc_MlDsaKey* mldsaKey,
                                 SlhDsaKey *slhDsaKey, int kid_type)
{
    byte *buf;
    int   bufferSz, ret;

    if (cert == NULL ||
        (rsakey == NULL && eckey == NULL && ed25519Key == NULL &&
         ed448Key == NULL && falconKey == NULL && mldsaKey == NULL &&
         slhDsaKey == NULL) ||
        (kid_type != SKID_TYPE && kid_type != AKID_TYPE))
        return BAD_FUNC_ARG;

    buf = (byte *)XMALLOC(MAX_PUBLIC_KEY_SZ, cert->heap,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
    if (buf == NULL)
        return MEMORY_E;

    /* Public Key */
    bufferSz = -1;
#ifndef NO_RSA
    /* RSA public key */
    if (rsakey != NULL)
        bufferSz = SetRsaPublicKey(buf, rsakey, MAX_PUBLIC_KEY_SZ, 0);
#endif
#ifdef HAVE_ECC
    /* ECC public key */
    if (eckey != NULL)
        bufferSz = SetEccPublicKey(buf, eckey, MAX_PUBLIC_KEY_SZ, 0, 0);
#endif
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
    /* ED25519 public key */
    if (ed25519Key != NULL) {
        bufferSz = wc_Ed25519PublicKeyToDer(ed25519Key, buf, MAX_PUBLIC_KEY_SZ, 0);
    }
#endif
#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)
    /* ED448 public key */
    if (ed448Key != NULL) {
        bufferSz = wc_Ed448PublicKeyToDer(ed448Key, buf, MAX_PUBLIC_KEY_SZ, 0);
    }
#endif
#if defined(HAVE_FALCON)
    if (falconKey != NULL) {
        bufferSz = wc_Falcon_PublicKeyToDer(falconKey, buf, MAX_PUBLIC_KEY_SZ,
                                            0);
    }
#endif
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1)
    if (mldsaKey != NULL) {
        bufferSz = wc_MlDsaKey_PublicKeyToDer(mldsaKey, buf,
                                               MAX_PUBLIC_KEY_SZ, 0);
    }
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
    if (slhDsaKey != NULL) {
        bufferSz = wc_SlhDsaKey_PublicKeyToDer(slhDsaKey, buf,
                                               MAX_PUBLIC_KEY_SZ, 0);
    }
#endif

    if (bufferSz <= 0) {
        XFREE(buf, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
        return PUBLIC_KEY_E;
    }

    /* Compute SKID by hashing public key */
    if (kid_type == SKID_TYPE) {
        int hashId = HashIdAlg((word32)cert->sigType);
        ret = CalcHashId_ex(buf, (word32)bufferSz, cert->skid, hashId);
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        cert->skidSz = wc_HashGetDigestSize(wc_HashTypeConvert(hashId));
    #else
        cert->skidSz = KEYID_SIZE;
    #endif
    }
    else {
        int hashId = HashIdAlg((word32)cert->sigType);
        ret = CalcHashId_ex(buf, (word32)bufferSz, cert->akid, hashId);
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        cert->akidSz = wc_HashGetDigestSize(wc_HashTypeConvert(hashId));
    #else
        cert->akidSz = KEYID_SIZE;
    #endif
    }

    XFREE(buf, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
    return ret;
}

int wc_SetSubjectKeyIdFromPublicKey_ex(Cert *cert, int keyType, void* key)
{
    RsaKey*            rsaKey = NULL;
    ecc_key*           eccKey = NULL;
    ed25519_key*       ed25519Key = NULL;
    ed448_key*         ed448Key = NULL;
    falcon_key*        falconKey = NULL;
    wc_MlDsaKey*       mldsaKey = NULL;
    SlhDsaKey*         slhDsaKey = NULL;

    if (keyType == RSA_TYPE)
        rsaKey = (RsaKey*)key;
    else if (keyType == ECC_TYPE)
        eccKey = (ecc_key*)key;
    else if (keyType == ED25519_TYPE)
        ed25519Key = (ed25519_key*)key;
    else if (keyType == ED448_TYPE)
        ed448Key = (ed448_key*)key;
    else if (keyType == FALCON_LEVEL1_TYPE)
        falconKey = (falcon_key*)key;
    else if (keyType == FALCON_LEVEL5_TYPE)
        falconKey = (falcon_key*)key;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if (keyType == DILITHIUM_LEVEL2_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL3_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL5_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#endif
    else if (keyType == ML_DSA_44_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_65_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_87_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#ifdef WOLFSSL_HAVE_SLHDSA
    else if (IsSlhDsaKeyType(keyType))
        slhDsaKey = (SlhDsaKey*)key;
#endif

    return SetKeyIdFromPublicKey(cert, rsaKey, eccKey, ed25519Key, ed448Key,
                                 falconKey, mldsaKey, slhDsaKey,
                                 SKID_TYPE);
}

/* Set SKID from RSA or ECC public key */
int wc_SetSubjectKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey)
{
    return SetKeyIdFromPublicKey(cert, rsakey, eckey, NULL, NULL, NULL, NULL,
                                 NULL, SKID_TYPE);
}

int wc_SetAuthKeyIdFromPublicKey_ex(Cert *cert, int keyType, void* key)
{
    RsaKey*            rsaKey = NULL;
    ecc_key*           eccKey = NULL;
    ed25519_key*       ed25519Key = NULL;
    ed448_key*         ed448Key = NULL;
    falcon_key*        falconKey = NULL;
    wc_MlDsaKey*       mldsaKey = NULL;
    SlhDsaKey*         slhDsaKey = NULL;

    if (keyType == RSA_TYPE)
        rsaKey = (RsaKey*)key;
    else if (keyType == ECC_TYPE)
        eccKey = (ecc_key*)key;
    else if (keyType == ED25519_TYPE)
        ed25519Key = (ed25519_key*)key;
    else if (keyType == ED448_TYPE)
        ed448Key = (ed448_key*)key;
    else if (keyType == FALCON_LEVEL1_TYPE)
        falconKey = (falcon_key*)key;
    else if (keyType == FALCON_LEVEL5_TYPE)
        falconKey = (falcon_key*)key;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
    else if (keyType == DILITHIUM_LEVEL2_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL3_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == DILITHIUM_LEVEL5_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#endif
    else if (keyType == ML_DSA_44_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_65_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
    else if (keyType == ML_DSA_87_TYPE)
        mldsaKey = (wc_MlDsaKey*)key;
#ifdef WOLFSSL_HAVE_SLHDSA
    else if (IsSlhDsaKeyType(keyType))
        slhDsaKey = (SlhDsaKey*)key;
#endif

    return SetKeyIdFromPublicKey(cert, rsaKey, eccKey, ed25519Key, ed448Key,
                                 falconKey, mldsaKey, slhDsaKey,
                                 AKID_TYPE);
}

/* Set SKID from RSA or ECC public key */
int wc_SetAuthKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey)
{
    return SetKeyIdFromPublicKey(cert, rsakey, eckey, NULL, NULL, NULL, NULL,
                                 NULL, AKID_TYPE);
}


#if !defined(NO_FILESYSTEM) && !defined(NO_ASN_CRYPT)

/* Set SKID from public key file in PEM */
int wc_SetSubjectKeyId(Cert *cert, const char* file)
{
    int     ret, derSz;
    byte*   der;
    word32  idx;
    RsaKey  *rsakey = NULL;
    ecc_key *eckey = NULL;

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

    der = (byte*)XMALLOC(MAX_PUBLIC_KEY_SZ, cert->heap, DYNAMIC_TYPE_CERT);
    if (der == NULL) {
        WOLFSSL_MSG("wc_SetSubjectKeyId memory Problem");
        return MEMORY_E;
    }
    derSz = MAX_PUBLIC_KEY_SZ;

    XMEMSET(der, 0, (size_t)derSz);
    derSz = wc_PemPubKeyToDer(file, der, derSz);
    if (derSz <= 0) {
        XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
        return derSz;
    }

    /* Load PubKey in internal structure */
#ifndef NO_RSA
    rsakey = (RsaKey*) XMALLOC(sizeof(RsaKey), cert->heap, DYNAMIC_TYPE_RSA);
    if (rsakey == NULL) {
        XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
        return MEMORY_E;
    }

    if (wc_InitRsaKey(rsakey, cert->heap) != 0) {
        WOLFSSL_MSG("wc_InitRsaKey failure");
        XFREE(rsakey, cert->heap, DYNAMIC_TYPE_RSA);
        XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
        return MEMORY_E;
    }

    idx = 0;
    ret = wc_RsaPublicKeyDecode(der, &idx, rsakey, (word32)derSz);
    if (ret != 0)
#endif
    {
#ifndef NO_RSA
        WOLFSSL_MSG("wc_RsaPublicKeyDecode failed");
        wc_FreeRsaKey(rsakey);
        XFREE(rsakey, cert->heap, DYNAMIC_TYPE_RSA);
        rsakey = NULL;
#endif
#ifdef HAVE_ECC
        /* Check to load ecc public key */
        eckey = (ecc_key*) XMALLOC(sizeof(ecc_key), cert->heap,
                                                              DYNAMIC_TYPE_ECC);
        if (eckey == NULL) {
            XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
            return MEMORY_E;
        }

        if (wc_ecc_init(eckey) != 0) {
            WOLFSSL_MSG("wc_ecc_init failure");
            wc_ecc_free(eckey);
            XFREE(eckey, cert->heap, DYNAMIC_TYPE_ECC);
            XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
            return MEMORY_E;
        }

        idx = 0;
        ret = wc_EccPublicKeyDecode(der, &idx, eckey, (word32)derSz);
        if (ret != 0) {
            WOLFSSL_MSG("wc_EccPublicKeyDecode failed");
            XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
            wc_ecc_free(eckey);
            XFREE(eckey, cert->heap, DYNAMIC_TYPE_ECC);
            return PUBLIC_KEY_E;
        }
#else
        XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);
        return PUBLIC_KEY_E;
#endif /* HAVE_ECC */
    }

    XFREE(der, cert->heap, DYNAMIC_TYPE_CERT);

    ret = wc_SetSubjectKeyIdFromPublicKey(cert, rsakey, eckey);

#ifndef NO_RSA
    wc_FreeRsaKey(rsakey);
    XFREE(rsakey, cert->heap, DYNAMIC_TYPE_RSA);
#endif
#ifdef HAVE_ECC
    wc_ecc_free(eckey);
    XFREE(eckey, cert->heap, DYNAMIC_TYPE_ECC);
#endif
#if defined(NO_RSA) && !defined(HAVE_ECC)
    (void)idx;
#endif
    return ret;
}

#endif /* !NO_FILESYSTEM && !NO_ASN_CRYPT */

static int SetAuthKeyIdFromDcert(Cert* cert, DecodedCert* decoded)
{
    int ret = 0;

    /* Subject Key Id not found !! */
    if (decoded->extSubjKeyIdSet == 0) {
        ret = ASN_NO_SKID;
    }

    /* SKID invalid size */
    else if (sizeof(cert->akid) < sizeof(decoded->extSubjKeyId)) {
        ret = MEMORY_E;
    }

    else {
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        cert->akidSz = wc_HashGetDigestSize(wc_HashTypeConvert(HashIdAlg(
            cert->sigType)));
        if (cert->akidSz <= 0) {
            ret = HASH_TYPE_E;
        }
    #else
        cert->akidSz = KEYID_SIZE;
    #endif
        if (ret == 0) {
            /* Put the SKID of CA to AKID of certificate */
            XMEMCPY(cert->akid, decoded->extSubjKeyId, (size_t)cert->akidSz);
        }
    }

    return ret;
}

/* Set AKID from certificate contains in buffer (DER encoded) */
int wc_SetAuthKeyIdFromCert(Cert *cert, const byte *der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            ret = SetAuthKeyIdFromDcert(cert, (DecodedCert*)cert->decodedCert);
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }

    return ret;
}


#ifndef NO_FILESYSTEM

/* Set AKID from certificate file in PEM */
int wc_SetAuthKeyId(Cert *cert, const char* file)
{
    int         ret;
    DerBuffer*  der = NULL;

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

    ret = wc_PemCertToDer_ex(file, &der);
    if (ret == 0)
    {
        ret = wc_SetAuthKeyIdFromCert(cert, der->buffer, (int)der->length);
        FreeDer(&der);
    }

    return ret;
}

#endif /* !NO_FILESYSTEM */

/* Set KeyUsage from human readable string */
int wc_SetKeyUsage(Cert *cert, const char *value)
{
    int ret = 0;

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

    cert->keyUsage = 0;

    ret = ParseKeyUsageStr(value, &cert->keyUsage, cert->heap);

    return ret;
}

/* Set ExtendedKeyUsage from human readable string */
int wc_SetExtKeyUsage(Cert *cert, const char *value)
{
    int ret = 0;

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

    cert->extKeyUsage = 0;

    ret = ParseExtKeyUsageStr(value, &cert->extKeyUsage, cert->heap);

    return ret;
}

#ifdef WOLFSSL_ACME_OID
/* Set the id-pe-acmeIdentifier extension value from the ACME
 * keyAuth string. Computes SHA-256 over keyAuth and stores the digest
 * as the extension value. RFC 8737 3 requires critical=TRUE; that's
 * applied at encode time in EncodeExtensions.
 *
 * keyAuth is the raw bytes of the key authorization string per
 * RFC 8555 8.1: token "." JWK_thumbprint.
 */
int wc_SetAcmeIdentifierExt(Cert *cert, const byte *keyAuth, word32 keyAuthSz)
{
    int    ret;
    byte   digest[WC_SHA256_DIGEST_SIZE];
    wc_Sha256 sha;

    if (cert == NULL || keyAuth == NULL || keyAuthSz == 0)
        return BAD_FUNC_ARG;

    ret = wc_InitSha256(&sha);
    if (ret != 0)
        return ret;
    ret = wc_Sha256Update(&sha, keyAuth, keyAuthSz);
    if (ret == 0)
        ret = wc_Sha256Final(&sha, digest);
    wc_Sha256Free(&sha);
    if (ret != 0)
        return ret;

    XMEMCPY(cert->acmeIdentifier, digest, WC_SHA256_DIGEST_SIZE);
    cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE;
    return 0;
}
#endif /* WOLFSSL_ACME_OID */

#ifdef WOLFSSL_EKU_OID
/*
 * cert structure to set EKU oid in
 * oid  the oid in byte representation
 * sz   size of oid buffer
 * idx  index of array to place oid
 *
 * returns 0 on success
 */
int wc_SetExtKeyUsageOID(Cert *cert, const char *in, word32 sz, byte idx,
        void* heap)
{
    byte oid[CTC_MAX_EKU_OID_SZ];
    word32 oidSz = CTC_MAX_EKU_OID_SZ;

    XMEMSET(oid, 0, sizeof(oid));

    if (idx >= CTC_MAX_EKU_NB || sz >= CTC_MAX_EKU_OID_SZ) {
        WOLFSSL_MSG("Either idx or sz was too large");
        return BAD_FUNC_ARG;
    }

    if (EncodePolicyOID(oid, &oidSz, in, heap) != 0) {
        return BUFFER_E;
    }

    XMEMCPY(cert->extKeyUsageOID[idx], oid, oidSz);
    cert->extKeyUsageOIDSz[idx] = (byte)oidSz;
    cert->extKeyUsage |= EXTKEYUSE_USER;

    return 0;
}
#endif /* WOLFSSL_EKU_OID */

#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CERT_GEN) && \
    defined(WOLFSSL_CUSTOM_OID) && defined(HAVE_OID_ENCODING) && \
    defined(WOLFSSL_CERT_EXT)
int wc_SetCustomExtension(Cert *cert, int critical, const char *oid,
                          const byte *der, word32 derSz) {
    CertExtension *ext;
    byte encodedOid[MAX_OID_SZ];
    word32 encodedOidSz = MAX_OID_SZ;
    int ret;

    XMEMSET(encodedOid, 0, sizeof(encodedOid));

    if (cert == NULL || oid == NULL || der == NULL || derSz == 0) {
        return BAD_FUNC_ARG;
    }

    if (cert->customCertExtCount >= NUM_CUSTOM_EXT) {
        return MEMORY_E;
    }

    /* Make sure we can properly parse the OID. */
    ret = EncodePolicyOID(encodedOid, &encodedOidSz, oid, NULL);
    if (ret != 0) {
        return ret;
    }

    ext = &cert->customCertExt[cert->customCertExtCount];

    /* if supplied oid is readonly, user must access ext->oid readonly. */
    ext->oid = (char*)(wc_ptr_t)oid;
    ext->crit = (critical == 0) ? 0 : 1;
    /* if supplied der is readonly, user must access ext->der readonly. */
    ext->val = (byte*)(wc_ptr_t)der;
    ext->valSz = (int)derSz;

    cert->customCertExtCount++;
    return 0;
}
#endif

#endif /* WOLFSSL_CERT_EXT */


#ifdef WOLFSSL_ALT_NAMES

static int SetAltNamesFromDcert(Cert* cert, DecodedCert* decoded)
{
    int ret = 0;

    cert->altNamesSz = 0;
    if (decoded->altNames) {
        ret = FlattenAltNames(cert->altNames,
            sizeof(cert->altNames), decoded->altNames);
        if (ret >= 0) {
            cert->altNamesSz = ret;
            ret = 0;
        }
    }

    return ret;
}

#ifndef NO_FILESYSTEM

/* Set Alt Names from der cert, return 0 on success */
static int SetAltNamesFromCert(Cert* cert, const byte* der, int derSz,
    int devId)
{
    int ret;
    WC_DECLARE_VAR(decoded, DecodedCert, 1, 0);

    if (derSz < 0)
        return derSz;

    WC_ALLOC_VAR_EX(decoded, DecodedCert, 1, cert->heap,
        DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);

    InitDecodedCert_ex(decoded, der, (word32)derSz, NULL, devId);
    ret = ParseCertRelative(decoded, CA_TYPE, NO_VERIFY, 0, NULL);

    if (ret < 0) {
        WOLFSSL_MSG("ParseCertRelative error");
    }
    else {
        ret = SetAltNamesFromDcert(cert, decoded);
    }

    FreeDecodedCert(decoded);
    WC_FREE_VAR_EX(decoded, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);

    return ret < 0 ? ret : 0;
}

#endif

static int SetDatesFromDcert(Cert* cert, DecodedCert* decoded)
{
    int ret = 0;

    if (decoded->beforeDate == NULL || decoded->afterDate == NULL) {
        WOLFSSL_MSG("Couldn't extract dates");
        ret = -1;
    }
    else if (decoded->beforeDateLen > MAX_DATE_SIZE ||
                                        decoded->afterDateLen > MAX_DATE_SIZE) {
        WOLFSSL_MSG("Bad date size");
        ret = -1;
    }
    else {
        XMEMCPY(cert->beforeDate, decoded->beforeDate,
                (size_t)decoded->beforeDateLen);
        XMEMCPY(cert->afterDate,  decoded->afterDate,
                (size_t)decoded->afterDateLen);

        cert->beforeDateSz = decoded->beforeDateLen;
        cert->afterDateSz  = decoded->afterDateLen;
    }

    return ret;
}

#endif /* WOLFSSL_ALT_NAMES */

static void SetNameFromDcert(CertName* cn, DecodedCert* decoded)
{
    int sz;

    if (decoded->subjectCN) {
        sz = (decoded->subjectCNLen < CTC_NAME_SIZE) ? decoded->subjectCNLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->commonName, decoded->subjectCN, (size_t)sz);
        cn->commonName[sz] = '\0';
        cn->commonNameEnc = decoded->subjectCNEnc;
    }
    if (decoded->subjectC) {
        sz = (decoded->subjectCLen < CTC_NAME_SIZE) ? decoded->subjectCLen
                                                    : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->country, decoded->subjectC, (size_t)sz);
        cn->country[sz] = '\0';
        cn->countryEnc = decoded->subjectCEnc;
    }
    if (decoded->subjectST) {
        sz = (decoded->subjectSTLen < CTC_NAME_SIZE) ? decoded->subjectSTLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->state, decoded->subjectST, (size_t)sz);
        cn->state[sz] = '\0';
        cn->stateEnc = decoded->subjectSTEnc;
    }
    if (decoded->subjectL) {
        sz = (decoded->subjectLLen < CTC_NAME_SIZE) ? decoded->subjectLLen
                                                    : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->locality, decoded->subjectL, (size_t)sz);
        cn->locality[sz] = '\0';
        cn->localityEnc = decoded->subjectLEnc;
    }
    if (decoded->subjectO) {
        sz = (decoded->subjectOLen < CTC_NAME_SIZE) ? decoded->subjectOLen
                                                    : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->org, decoded->subjectO, (size_t)sz);
        cn->org[sz] = '\0';
        cn->orgEnc = decoded->subjectOEnc;
    }
    if (decoded->subjectOU) {
        sz = (decoded->subjectOULen < CTC_NAME_SIZE) ? decoded->subjectOULen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->unit, decoded->subjectOU, (size_t)sz);
        cn->unit[sz] = '\0';
        cn->unitEnc = decoded->subjectOUEnc;
    }
    if (decoded->subjectSN) {
        sz = (decoded->subjectSNLen < CTC_NAME_SIZE) ? decoded->subjectSNLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->sur, decoded->subjectSN, (size_t)sz);
        cn->sur[sz] = '\0';
        cn->surEnc = decoded->subjectSNEnc;
    }
    if (decoded->subjectSND) {
        sz = (decoded->subjectSNDLen < CTC_NAME_SIZE) ? decoded->subjectSNDLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->serialDev, decoded->subjectSND, (size_t)sz);
        cn->serialDev[sz] = '\0';
        cn->serialDevEnc = decoded->subjectSNDEnc;
    }
    if (decoded->subjectUID) {
        sz = (decoded->subjectUIDLen < CTC_NAME_SIZE) ? decoded->subjectUIDLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->userId, decoded->subjectUID, (size_t)sz);
        cn->userId[sz] = '\0';
        cn->userIdEnc = decoded->subjectUIDEnc;
    }
#ifdef WOLFSSL_CERT_EXT
    if (decoded->subjectBC) {
        sz = (decoded->subjectBCLen < CTC_NAME_SIZE) ? decoded->subjectBCLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->busCat, decoded->subjectBC, (size_t)sz);
        cn->busCat[sz] = '\0';
        cn->busCatEnc = decoded->subjectBCEnc;
    }
    if (decoded->subjectJC) {
        sz = (decoded->subjectJCLen < CTC_NAME_SIZE) ? decoded->subjectJCLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->joiC, decoded->subjectJC, (size_t)sz);
        cn->joiC[sz] = '\0';
        cn->joiCEnc = decoded->subjectJCEnc;
    }
    if (decoded->subjectJS) {
        sz = (decoded->subjectJSLen < CTC_NAME_SIZE) ? decoded->subjectJSLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->joiSt, decoded->subjectJS, (size_t)sz);
        cn->joiSt[sz] = '\0';
        cn->joiStEnc = decoded->subjectJSEnc;
    }
#endif
    if (decoded->subjectEmail) {
        sz = (decoded->subjectEmailLen < CTC_NAME_SIZE)
           ?  decoded->subjectEmailLen : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->email, decoded->subjectEmail, (size_t)sz);
        cn->email[sz] = '\0';
    }
#if defined(WOLFSSL_CERT_NAME_ALL) && \
    (defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_EXT))
    if (decoded->subjectN) {
        sz = (decoded->subjectNLen < CTC_NAME_SIZE) ? decoded->subjectNLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->dnName, decoded->subjectN, (size_t)sz);
        cn->dnName[sz] = '\0';
        cn->dnNameEnc = decoded->subjectNEnc;
    }
    if (decoded->subjectI) {
        sz = (decoded->subjectILen < CTC_NAME_SIZE) ? decoded->subjectILen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->initials, decoded->subjectI, (size_t)sz);
        cn->initials[sz] = '\0';
        cn->initialsEnc = decoded->subjectIEnc;
    }
    if (decoded->subjectGN) {
        sz = (decoded->subjectGNLen < CTC_NAME_SIZE) ? decoded->subjectGNLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->givenName, decoded->subjectGN, (size_t)sz);
        cn->givenName[sz] = '\0';
        cn->givenNameEnc = decoded->subjectGNEnc;
    }
    if (decoded->subjectDNQ) {
        sz = (decoded->subjectDNQLen < CTC_NAME_SIZE) ? decoded->subjectDNQLen
                                                     : CTC_NAME_SIZE - 1;
        XSTRNCPY(cn->dnQualifier, decoded->subjectDNQ, (size_t)sz);
        cn->dnQualifier[sz] = '\0';
        cn->dnQualifierEnc = decoded->subjectDNQEnc;
    }
#endif /* WOLFSSL_CERT_NAME_ALL */
}

#ifndef NO_FILESYSTEM

/* Set cn name from der buffer, return 0 on success */
static int SetNameFromCert(CertName* cn, const byte* der, int derSz, int devId)
{
    int ret;
    WC_DECLARE_VAR(decoded, DecodedCert, 1, 0);

    if (derSz < 0)
        return derSz;

    WC_ALLOC_VAR_EX(decoded, DecodedCert, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    InitDecodedCert_ex(decoded, der, (word32)derSz, NULL, devId);
    ret = ParseCertRelative(decoded, CA_TYPE, NO_VERIFY, 0, NULL);

    if (ret < 0) {
        WOLFSSL_MSG("ParseCertRelative error");
    }
    else {
        SetNameFromDcert(cn, decoded);
    }

    FreeDecodedCert(decoded);

    WC_FREE_VAR_EX(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER);

    return ret < 0 ? ret : 0;
}

/* Set cert issuer from issuerFile in PEM */
WOLFSSL_ABI
int wc_SetIssuer(Cert* cert, const char* issuerFile)
{
    int         ret;
    DerBuffer*  der = NULL;

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

    ret = wc_PemCertToDer_ex(issuerFile, &der);
    if (ret == 0) {
        cert->selfSigned = 0;
        ret = SetNameFromCert(&cert->issuer, der->buffer, (int)der->length,
            INVALID_DEVID);

        FreeDer(&der);
    }

    return ret;
}


/* Set cert subject from subjectFile in PEM */
WOLFSSL_ABI
int wc_SetSubject(Cert* cert, const char* subjectFile)
{
    int         ret;
    DerBuffer*  der = NULL;

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

    ret = wc_PemCertToDer_ex(subjectFile, &der);
    if (ret == 0) {
        ret = SetNameFromCert(&cert->subject, der->buffer, (int)der->length,
            INVALID_DEVID);

        FreeDer(&der);
    }

    return ret;
}

#ifdef WOLFSSL_ALT_NAMES

/* Set alt names from file in PEM */
WOLFSSL_ABI
int wc_SetAltNames(Cert* cert, const char* file)
{
    int         ret;
    DerBuffer*  der = NULL;

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

    ret = wc_PemCertToDer_ex(file, &der);
    if (ret == 0) {
        ret = SetAltNamesFromCert(cert, der->buffer, (int)der->length,
            INVALID_DEVID);

        FreeDer(&der);
    }

    return ret;
}

#endif /* WOLFSSL_ALT_NAMES */

#endif /* !NO_FILESYSTEM */

/* Set cert issuer from DER buffer */
WOLFSSL_ABI
int wc_SetIssuerBuffer(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        cert->selfSigned = 0;

        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            SetNameFromDcert(&cert->issuer, (DecodedCert*)cert->decodedCert);
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }

    return ret;
}

/* Set cert subject from DER buffer */
WOLFSSL_ABI
int wc_SetSubjectBuffer(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            SetNameFromDcert(&cert->subject, (DecodedCert*)cert->decodedCert);
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }

    return ret;
}
#ifdef WOLFSSL_CERT_EXT
/* Set cert raw subject from DER buffer */
WOLFSSL_ABI
int wc_SetSubjectRaw(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            if ((((DecodedCert*)cert->decodedCert)->subjectRaw) &&
                (((DecodedCert*)cert->decodedCert)->subjectRawLen <=
                        (int)sizeof(CertName))) {
                XMEMCPY(cert->sbjRaw,
                        ((DecodedCert*)cert->decodedCert)->subjectRaw,
                        (size_t)((DecodedCert*)cert->decodedCert)->
                        subjectRawLen);
            }
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }

    return ret;
}

/* Set cert raw issuer from DER buffer */
WOLFSSL_ABI
int wc_SetIssuerRaw(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            if ((((DecodedCert*)cert->decodedCert)->subjectRaw) &&
                (((DecodedCert*)cert->decodedCert)->subjectRawLen <=
                        (int)sizeof(CertName))) {
                /* Copy the subject to the issuer field */
                XMEMCPY(cert->issRaw,
                        ((DecodedCert*)cert->decodedCert)->subjectRaw,
                        (size_t)((DecodedCert*)cert->decodedCert)->
                        subjectRawLen);
            }
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }
    return ret;
}
#endif

#ifdef WOLFSSL_ALT_NAMES

/* Set cert alt names from DER buffer */
WOLFSSL_ABI
int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
       ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            ret = SetAltNamesFromDcert(cert, (DecodedCert*)cert->decodedCert);
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
       }
    }

    return(ret);
}

/* Set cert dates from DER buffer */
WOLFSSL_ABI
int wc_SetDatesBuffer(Cert* cert, const byte* der, int derSz)
{
    int ret = 0;

    if (cert == NULL) {
     ret = BAD_FUNC_ARG;
    }
    else if (derSz < 0) {
        ret = BAD_FUNC_ARG;
    }
    else {
        /* Check if decodedCert is cached */
        if (cert->der != der) {
            /* Allocate cache for the decoded cert */
            ret = wc_SetCert_LoadDer(cert, der, (word32)derSz, INVALID_DEVID);
        }

        if (ret >= 0) {
            ret = SetDatesFromDcert(cert, (DecodedCert*)cert->decodedCert);
#ifndef WOLFSSL_CERT_GEN_CACHE
            wc_SetCert_Free(cert);
#endif
        }
    }

    return(ret);
}

#endif /* WOLFSSL_ALT_NAMES */

#endif /* WOLFSSL_CERT_GEN */

#if (defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT)) \
        || defined(OPENSSL_EXTRA)
/* Encode OID string representation to ITU-T X.690 format */
int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap)
{
    word32 idx = 0, nb_val;
    char *token, *str, *ptr;
    word32 len;

    (void)heap;

    if (out == NULL || outSz == NULL || *outSz < 2 || in == NULL)
        return BAD_FUNC_ARG;

    /* duplicate string (including terminator) */
    len = (word32)XSTRLEN(in);
    str = (char *)XMALLOC(len+1, heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (str == NULL)
        return MEMORY_E;
    XMEMCPY(str, in, len+1);

    nb_val = 0;

    /* parse value, and set corresponding Policy OID value */
    token = XSTRTOK(str, ".", &ptr);
    while (token != NULL)
    {
        word32 val = (word32)XATOI(token);

        if (nb_val == 0) {
            if (val > 2) {
                XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
                return ASN_OBJECT_ID_E;
            }

            out[idx] = (byte)(40 * val);
        }
        else if (nb_val == 1) {
            if (val > 127) {
                XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
                return ASN_OBJECT_ID_E;
            }

            if (idx > *outSz) {
                XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
                return BUFFER_E;
            }

            out[idx] = (byte)(out[idx] + val);
            ++idx;
        }
        else {
            word32  tb = 0;
            int     i = 0;
            byte    oid[MAX_OID_SZ];

            while (val >= 128) {
                word32 x = val % 128;
                val /= 128;
                oid[i++] = (byte) (((tb++) ? 0x80 : 0) | x);
            }

            if ((idx+(word32)i) >= *outSz) {
                XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
                return BUFFER_E;
            }

            oid[i] = (byte) (((tb++) ? 0x80 : 0) | val);

            /* push value in the right order */
            while (i >= 0)
                out[idx++] = oid[i--];
        }

        token = XSTRTOK(NULL, ".", &ptr);
        nb_val++;
    }

    *outSz = idx;

    XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER);
    return 0;
}
#endif /* WOLFSSL_CERT_EXT || OPENSSL_EXTRA */

#endif /* !NO_CERTS */

#if !defined(NO_DH) && (defined(WOLFSSL_QT) || defined(OPENSSL_ALL))
/* Helper function for wolfSSL_i2d_DHparams */
#ifdef WOLFSSL_ASN_TEMPLATE
int StoreDHparams(byte* out, word32* outLen, mp_int* p, mp_int* g)
{
    ASNSetData dataASN[dhParamASN_Length];
    int ret = 0;
    word32 sz = 0;

    WOLFSSL_ENTER("StoreDHparams");
    if (out == NULL) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        XMEMSET(dataASN, 0, sizeof(dataASN));
        /* Set mp_int containing p and g. */
        SetASN_MP(&dataASN[DHPARAMASN_IDX_PRIME], p);
        SetASN_MP(&dataASN[DHPARAMASN_IDX_BASE], g);
        /* privateValueLength not encoded. */
        dataASN[DHPARAMASN_IDX_PRIVLEN].noOut = 1;

        /* Calculate the size of the DH parameters. */
        ret = SizeASN_Items(dhParamASN, dataASN, dhParamASN_Length, &sz);
    }
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (*outLen < sz)) {
        ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode the DH parameters into buffer. */
        SetASN_Items(dhParamASN, dataASN, dhParamASN_Length, out);
        /* Set the actual encoding size. */
        *outLen = sz;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* !NO_DH && (WOLFSSL_QT || OPENSSL_ALL) */

#if defined(HAVE_ECC) || !defined(NO_DSA)

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for DSA signature.
 * RFC 5912, 6 - DSA-Sig-Value
 */
static const ASNItem dsaSigASN[] = {
/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                            /* r */
/* R   */     { 1, ASN_INTEGER, 0, 0, 0 },
                            /* s */
/* S   */     { 1, ASN_INTEGER, 0, 0, 0 },
};
enum {
    DSASIGASN_IDX_SEQ = 0,
    DSASIGASN_IDX_R,
    DSASIGASN_IDX_S
};

#define dsaSigASN_Length (sizeof(dsaSigASN) / sizeof(ASNItem))
#endif

/* Der Encode r & s ints into out, outLen is (in/out) size */
#ifdef WOLFSSL_ASN_TEMPLATE
int StoreECC_DSA_Sig(byte* out, word32* outLen, mp_int* r, mp_int* s)
{
    ASNSetData dataASN[dsaSigASN_Length];
    int ret;
    word32 sz;

    /* Clear dynamic data and set mp_ints r and s */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    SetASN_MP(&dataASN[DSASIGASN_IDX_R], r);
    SetASN_MP(&dataASN[DSASIGASN_IDX_S], s);

    /* Calculate size of encoding. */
    ret = SizeASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, &sz);
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (*outLen < sz)) {
       ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode DSA signature into buffer. */
        ret = SetASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, out);
        if (ret >= 0) {
            if ((word32)ret == sz) {
                /* Set the actual encoding size. */
                *outLen = sz;
                ret = 0;
            } else {
                ret = BAD_STATE_E;
            }
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Der Encode r & s ints into out, outLen is (in/out) size */
/* All input/outputs are assumed to be big-endian */
#ifdef WOLFSSL_ASN_TEMPLATE
int StoreECC_DSA_Sig_Bin(byte* out, word32* outLen, const byte* r, word32 rLen,
    const byte* s, word32 sLen)
{
    ASNSetData dataASN[dsaSigASN_Length];
    int ret;
    word32 sz;

    /* Clear dynamic data and set buffers for r and s */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    while ((rLen > 1) && (r[0] == 0)) {
        rLen--;
        r++;
    }
    while ((sLen > 1) && (s[0] == 0)) {
        sLen--;
        s++;
    }
    SetASN_Buffer(&dataASN[DSASIGASN_IDX_R], r, rLen);
    SetASN_Buffer(&dataASN[DSASIGASN_IDX_S], s, sLen);

    /* Calculate size of encoding. */
    ret = SizeASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, &sz);
    /* Check buffer is big enough for encoding. */
    if ((ret == 0) && (*outLen < sz)) {
       ret = BUFFER_E;
    }
    if (ret == 0) {
        /* Encode DSA signature into buffer. */
        SetASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, out);
        /* Set the actual encoding size. */
        *outLen = sz;
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Der Decode ECC-DSA Signature with R/S as unsigned bin */
/* All input/outputs are assumed to be big-endian */
#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeECC_DSA_Sig_Bin(const byte* sig, word32 sigLen, byte* r, word32* rLen,
    byte* s, word32* sLen)
{
    ASNGetData dataASN[dsaSigASN_Length];
    word32 idx = 0;

    /* Clear dynamic data and set buffers to put r and s into. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    GetASN_Buffer(&dataASN[DSASIGASN_IDX_R], r, rLen);
    GetASN_Buffer(&dataASN[DSASIGASN_IDX_S], s, sLen);

    /* Decode the DSA signature. */
    return GetASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, 1, sig, &idx,
                        sigLen);
}
#endif /* WOLFSSL_ASN_TEMPLATE */
int DecodeECC_DSA_Sig(const byte* sig, word32 sigLen, mp_int* r, mp_int* s)
{
    return DecodeECC_DSA_Sig_Ex(sig, sigLen, r, s, 1);
}

#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeECC_DSA_Sig_Ex(const byte* sig, word32 sigLen, mp_int* r, mp_int* s,
    int init)
{
    ASNGetData dataASN[dsaSigASN_Length];
    word32 idx = 0;
    int ret;

    /* Clear dynamic data and set mp_ints to put r and s into. */
    XMEMSET(dataASN, 0, sizeof(dataASN));
    if (init) {
        GetASN_MP(&dataASN[DSASIGASN_IDX_R], r);
        GetASN_MP(&dataASN[DSASIGASN_IDX_S], s);
    }
    else {
        GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_R], r);
        GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_S], s);
    }

    /* Decode the DSA signature. */
    ret = GetASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, 0, sig, &idx,
                       sigLen);

    if (ret != 0) {
        ret = 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 ((ret == 0) && (idx != sigLen)) {
        ret = ASN_ECC_KEY_E;
    }
#endif
    if (ret != 0) {
        mp_clear(r);
        mp_clear(s);
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif


#ifdef WOLFSSL_ASN_TEMPLATE
#if defined(HAVE_ECC) && defined(WOLFSSL_CUSTOM_CURVES)
/* Convert data to hex string.
 *
 * Big-endian byte array is converted to big-endian hexadecimal string.
 *
 * @param [in]  input  Buffer containing data.
 * @param [in]  inSz   Size of data in buffer.
 * @param [out] out    Buffer to hold hex string.
 */
static void DataToHexString(const byte* input, word32 inSz, char* out)
{
    static const char hexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7',
                                    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    word32 i;

    /* Converting a byte of data at a time to two hex characters. */
    for (i = 0; i < inSz; i++) {
        out[i*2 + 0] = hexChar[input[i] >> 4];
        out[i*2 + 1] = hexChar[input[i] & 0xf];
    }
    /* NUL terminate string. */
    out[i * 2] = '\0';
}

#ifndef WOLFSSL_ECC_CURVE_STATIC
/* Convert data to hex string and place in allocated buffer.
 *
 * Big-endian byte array is converted to big-endian hexadecimal string.
 *
 * @param [in]  input     Buffer containing data.
 * @param [in]  inSz      Size of data in buffer.
 * @param [out] out       Allocated buffer holding hex string.
 * @param [in]  heap      Dynamic memory allocation hint.
 * @param [in]  heapType  Type of heap to use.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int DataToHexStringAlloc(const byte* input, word32 inSz, char** out,
                                void* heap, int heapType)
{
    int ret = 0;
    char* str;

    /* Allocate for 2 string characters ber byte plus NUL. */
    str = (char*)XMALLOC(inSz * 2 + 1, heap, heapType);
    if (str == NULL) {
        ret = MEMORY_E;
    }
    else {
        /* Convert to hex string. */
        DataToHexString(input, inSz, str);
        *out = str;
    }

    (void)heap;
    (void)heapType;

    return ret;
}
#endif /* WOLFSSL_ECC_CURVE_STATIC */

/* ASN.1 template for SpecifiedECDomain.
 * SEC 1 Ver. 2.0, C.2 - Syntax for Elliptic Curve Domain Parameters
 * NOTE: characteristic-two-field not supported. */
static const ASNItem eccSpecifiedASN[] = {
            /* version */
/* VER        */ { 0, ASN_INTEGER, 0, 0, 0 },
                                     /* fieldID */
/* PRIME_SEQ  */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* prime-field or characteristic-two-field */
/* PRIME_OID  */     { 1, ASN_OBJECT_ID, 0, 0, 0 },
                                         /* Prime-p */
/* PRIME_P    */     { 1, ASN_INTEGER, 0, 0, 0 },
                                     /* fieldID */
/* PARAM_SEQ, */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* a */
/* PARAM_A    */     { 1, ASN_OCTET_STRING, 0, 0, 0 },
                                         /* b */
/* PARAM_B    */     { 1, ASN_OCTET_STRING, 0, 0, 0 },
                                         /* seed */
/* PARAM_SEED */     { 1, ASN_BIT_STRING, 0, 0, 1 },
                                     /* base */
/* BASE       */ { 0, ASN_OCTET_STRING, 0, 0, 0 },
                                     /* order */
/* ORDER      */ { 0, ASN_INTEGER, 0, 0, 0 },
                                     /* cofactor */
/* COFACTOR   */ { 0, ASN_INTEGER, 0, 0, 1 },
                                     /* hash */
/* HASH_SEQ   */ { 0, ASN_SEQUENCE, 0, 0, 1 },
};
enum {
    ECCSPECIFIEDASN_IDX_VER = 0,
    ECCSPECIFIEDASN_IDX_PRIME_SEQ,
    ECCSPECIFIEDASN_IDX_PRIME_OID,
    ECCSPECIFIEDASN_IDX_PRIME_P,
    ECCSPECIFIEDASN_IDX_PARAM_SEQ,
    ECCSPECIFIEDASN_IDX_PARAM_A,
    ECCSPECIFIEDASN_IDX_PARAM_B,
    ECCSPECIFIEDASN_IDX_PARAM_SEED,
    ECCSPECIFIEDASN_IDX_BASE,
    ECCSPECIFIEDASN_IDX_ORDER,
    ECCSPECIFIEDASN_IDX_COFACTOR,
    ECCSPECIFIEDASN_IDX_HASH_SEQ
};

/* Number of items in ASN.1 template for SpecifiedECDomain. */
#define eccSpecifiedASN_Length (sizeof(eccSpecifiedASN) / sizeof(ASNItem))

/* OID indicating the prime field is explicitly defined. */
static const byte primeFieldOID[] = {
    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01, 0x01
};
static const char ecSetCustomName[] = "Custom";

/* Explicit EC parameter values. */
static int EccSpecifiedECDomainDecode(const byte* input, word32 inSz,
                                      ecc_key* key, void* heap, int* curveSz)
{
    DECL_ASNGETDATA(dataASN, eccSpecifiedASN_Length);
    int ret = 0;
    ecc_set_type* curve = NULL;
    word32 idx = 0;
    byte version = 0;
    byte cofactor = 0;
    const byte *base = NULL;
    word32 baseLen = 0;

    /* Allocate a new parameter set. */
    curve = (ecc_set_type*)XMALLOC(sizeof(*curve), heap,
                DYNAMIC_TYPE_ECC_BUFFER);
    if (curve == NULL) {
        ret = MEMORY_E;
    }
    else {
        /* Clear out parameters and set fields to indicate it is custom. */
        XMEMSET(curve, 0, sizeof(*curve));
    }

    CALLOC_ASNGETDATA(dataASN, eccSpecifiedASN_Length, ret, heap);

    if (ret == 0) {
        /* Set name to be: "Custom" */
    #ifndef WOLFSSL_ECC_CURVE_STATIC
        curve->name = ecSetCustomName;
    #else
        XMEMCPY((void*)curve->name, ecSetCustomName, sizeof(ecSetCustomName));
    #endif
        curve->id = ECC_CURVE_CUSTOM;

        /* Get version, must have prime field OID and get co-factor. */
        GetASN_Int8Bit(&dataASN[ECCSPECIFIEDASN_IDX_VER], &version);
        GetASN_ExpBuffer(&dataASN[ECCSPECIFIEDASN_IDX_PRIME_OID],
                primeFieldOID, sizeof(primeFieldOID));
        GetASN_Int8Bit(&dataASN[ECCSPECIFIEDASN_IDX_COFACTOR], &cofactor);
        /* Decode the explicit parameters. */
        ret = GetASN_Items(eccSpecifiedASN, dataASN, eccSpecifiedASN_Length, 1,
                           input, &idx, inSz);
    }
    /* Version must be 1 or 2 for supporting explicit parameters. */
    if ((ret == 0) && (version < 1 || version > 3)) {
        ret = ASN_PARSE_E;
    }
#ifndef WOLFSSL_NO_ASN_STRICT
    /* Only version 2 and above can have a seed. */
    if (ret == 0) {
        if ((dataASN[ECCSPECIFIEDASN_IDX_PARAM_SEED].tag != 0) &&
            (version < 2)) {
            ret = ASN_PARSE_E;
        }
    }
#endif
    /* Only version 2 and above can have a hash algorithm. */
    if (ret == 0) {
        if ((dataASN[ECCSPECIFIEDASN_IDX_HASH_SEQ].tag != 0) &&
            (version < 2)) {
            ret = ASN_PARSE_E;
        }
    }
    if ((ret == 0) && (dataASN[ECCSPECIFIEDASN_IDX_COFACTOR].tag != 0)) {
        /* Store optional co-factor. */
        curve->cofactor = cofactor;
    }
    if (ret == 0) {
        /* Length of the prime in bytes is the curve size. */
        curve->size =
                (int)dataASN[ECCSPECIFIEDASN_IDX_PRIME_P].data.ref.length;
        /* Base point: 0x04 <x> <y> (must be uncompressed). */
        GetASN_GetConstRef(&dataASN[ECCSPECIFIEDASN_IDX_BASE], &base,
                &baseLen);
        if ((baseLen < (word32)curve->size * 2 + 1) || (base[0] != 0x4)) {
            ret = ASN_PARSE_E;
        }
    }
    /* Put the curve parameters into the set.
     * Convert the big-endian number byte array to a big-endian string.
     */
    #ifndef WOLFSSL_ECC_CURVE_STATIC
    /* Allocate buffer to put hex strings into. */
    if (ret == 0) {
        /* Base X-ordinate */
        char *curve_Gx = NULL;
        ret = DataToHexStringAlloc(base + 1, (word32)curve->size,
                                   &curve_Gx, heap,
                                   DYNAMIC_TYPE_ECC_BUFFER);
        curve->Gx = curve_Gx;
    }
    if (ret == 0) {
        /* Base Y-ordinate */
        char *curve_Gy = NULL;
        ret = DataToHexStringAlloc(base + 1 + curve->size, (word32)curve->size,
                                   &curve_Gy, heap,
                                   DYNAMIC_TYPE_ECC_BUFFER);
        curve->Gy = curve_Gy;
    }
    if (ret == 0) {
        /* Prime */
        char *curve_prime = NULL;
        ret = DataToHexStringAlloc(
                dataASN[ECCSPECIFIEDASN_IDX_PRIME_P].data.ref.data,
                dataASN[ECCSPECIFIEDASN_IDX_PRIME_P].data.ref.length,
                &curve_prime, heap, DYNAMIC_TYPE_ECC_BUFFER);
        curve->prime = curve_prime;
    }
    if (ret == 0) {
        /* Parameter A */
        char *curve_Af = NULL;
        ret = DataToHexStringAlloc(
                dataASN[ECCSPECIFIEDASN_IDX_PARAM_A].data.ref.data,
                dataASN[ECCSPECIFIEDASN_IDX_PARAM_A].data.ref.length,
                &curve_Af, heap, DYNAMIC_TYPE_ECC_BUFFER);
        curve->Af = curve_Af;
    }
    if (ret == 0) {
        /* Parameter B */
        char *curve_Bf = NULL;
        ret = DataToHexStringAlloc(
                dataASN[ECCSPECIFIEDASN_IDX_PARAM_B].data.ref.data,
                dataASN[ECCSPECIFIEDASN_IDX_PARAM_B].data.ref.length,
                &curve_Bf, heap, DYNAMIC_TYPE_ECC_BUFFER);
        curve->Bf = curve_Bf;
    }
    if (ret == 0) {
        /* Order of curve */
        char *curve_order = NULL;
        ret = DataToHexStringAlloc(
                dataASN[ECCSPECIFIEDASN_IDX_ORDER].data.ref.data,
                dataASN[ECCSPECIFIEDASN_IDX_ORDER].data.ref.length,
                &curve_order, heap, DYNAMIC_TYPE_ECC_BUFFER);
        curve->order = curve_order;
    }
    #else
    if (ret == 0) {
        /* Base X-ordinate */
        DataToHexString(base + 1, (word32)curve->size, (char *)curve->Gx);
        /* Base Y-ordinate */
        DataToHexString(base + 1 + curve->size, (word32)curve->size, (char *)curve->Gy);
        /* Prime */
        DataToHexString(dataASN[ECCSPECIFIEDASN_IDX_PRIME_P].data.ref.data,
                        dataASN[ECCSPECIFIEDASN_IDX_PRIME_P].data.ref.length,
                        (char *)curve->prime);
        /* Parameter A */
        DataToHexString(dataASN[ECCSPECIFIEDASN_IDX_PARAM_A].data.ref.data,
                        dataASN[ECCSPECIFIEDASN_IDX_PARAM_A].data.ref.length,
                        (char *)curve->Af);
        /* Parameter B */
        DataToHexString(dataASN[ECCSPECIFIEDASN_IDX_PARAM_B].data.ref.data,
                        dataASN[ECCSPECIFIEDASN_IDX_PARAM_B].data.ref.length,
                        (char *)curve->Bf);
        /* Order of curve */
        DataToHexString(dataASN[ECCSPECIFIEDASN_IDX_ORDER].data.ref.data,
                        dataASN[ECCSPECIFIEDASN_IDX_ORDER].data.ref.length,
                        (char *)curve->order);
    }
    #endif /* WOLFSSL_ECC_CURVE_STATIC */

    if ((ret == 0) && (curveSz)) {
        *curveSz = curve->size;
    }

    if (key) {
        /* Store parameter set in key. */
        if (ret == 0) {
            if (wc_ecc_set_custom_curve(key, curve) < 0) {
                ret = ASN_PARSE_E;
            }
            else {
                /* The parameter set was allocated.. */
                key->deallocSet = 1;
                /* Don't deallocate below. */
                curve = NULL;
            }
        }
    }

    if (curve != NULL) { /* NOLINT(clang-analyzer-unix.Malloc) */
        wc_ecc_free_curve(curve, heap);
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_CUSTOM_CURVES */
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef HAVE_ECC

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for ECC private key.
 * SEC.1 Ver 2.0, C.4 - Syntax for Elliptic Curve Private Keys
 */
static const ASNItem eccKeyASN[] = {
/* SEQ         */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                       /* version */
/* VER         */        { 1, ASN_INTEGER, 0, 0, 0 },
                                       /* privateKey */
/* PKEY        */        { 1, ASN_OCTET_STRING, 0, 0, 0 },
                                       /* parameters */
/* PARAMS      */        { 1, ASN_CONTEXT_SPECIFIC | ASN_ECC_PARAMS, 1, 1, 1 },
                                           /* named */
/* CURVEID     */            { 2, ASN_OBJECT_ID, 0, 0, 2 },
                                           /* specified */
/* CURVEPARAMS */            { 2, ASN_SEQUENCE, 1, 0, 2 },
                                       /* publicKey */
/* PUBKEY      */        { 1, ASN_CONTEXT_SPECIFIC | ASN_ECC_PUBKEY, 1, 1, 1 },
                                           /* Uncompressed point - X9.62. */
/* PUBKEY_VAL, */            { 2, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    ECCKEYASN_IDX_SEQ = 0,
    ECCKEYASN_IDX_VER,
    ECCKEYASN_IDX_PKEY,
    ECCKEYASN_IDX_PARAMS,
    ECCKEYASN_IDX_CURVEID,
    ECCKEYASN_IDX_CURVEPARAMS,
    ECCKEYASN_IDX_PUBKEY,
    ECCKEYASN_IDX_PUBKEY_VAL
};

/* Number of items in ASN.1 template for ECC private key. */
#define eccKeyASN_Length (sizeof(eccKeyASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
WOLFSSL_ABI
int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key,
                        word32 inSz)
{
    DECL_ASNGETDATA(dataASN, eccKeyASN_Length);
    byte version = 0;
    int ret = 0;
    int curve_id = ECC_CURVE_DEF;
#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12) || defined(WOLFSSL_SM2)
    word32 algId = 0;
    word32 eccOid = 0;
#endif

    /* Validate parameters. */
    if ((input == NULL) || (inOutIdx == NULL) || (key == NULL) || (inSz == 0)) {
        ret = BAD_FUNC_ARG;
    }

#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12) || defined(WOLFSSL_SM2)
    /* if has pkcs8 header skip it */
    if (ToTraditionalInline_ex2(input, inOutIdx, inSz, &algId, &eccOid) < 0) {
        /* ignore error, did not have pkcs8 header */
    }
    else {
        curve_id = wc_ecc_get_oid(eccOid, NULL, NULL);
    }
#endif

    if (ret == 0) {
        CALLOC_ASNGETDATA(dataASN, eccKeyASN_Length, ret, key->heap);
    }

    if (ret == 0) {
        /* Get the version and set the expected OID type. */
        GetASN_Int8Bit(&dataASN[ECCKEYASN_IDX_VER], &version);
        GetASN_OID(&dataASN[ECCKEYASN_IDX_CURVEID], oidCurveType);
        /* Decode the private ECC key. */
        ret = GetASN_Items(eccKeyASN, dataASN, eccKeyASN_Length, 1, input,
                           inOutIdx, inSz);
    }
    /* Only version 1 supported. */
    if (ret == 0) {
        if (version != 1) {
            ret = ASN_PARSE_E;
        }
    }
    /* Curve Parameters are optional. */
    if ((ret == 0) && (dataASN[ECCKEYASN_IDX_PARAMS].tag != 0)) {
        if (dataASN[ECCKEYASN_IDX_CURVEID].tag != 0) {
            /* Named curve - check and get id. */
            curve_id = CheckCurve(dataASN[ECCKEYASN_IDX_CURVEID].data.oid.sum);
            if (curve_id < 0) {
                ret = ECC_CURVE_OID_E;
            }
        }
        else {
    #ifdef WOLFSSL_CUSTOM_CURVES
            /* Parse explicit parameters. */
            ret = EccSpecifiedECDomainDecode(
                    dataASN[ECCKEYASN_IDX_CURVEPARAMS].data.ref.data,
                    dataASN[ECCKEYASN_IDX_CURVEPARAMS].data.ref.length, key,
                    key->heap, NULL);
    #else
            /* Explicit parameters not supported in build configuration. */
            ret = ASN_PARSE_E;
    #endif
        }
    }
    if (ret == 0) {
        /* Import private key value and public point (may be NULL). */
        ret = wc_ecc_import_private_key_ex(
                dataASN[ECCKEYASN_IDX_PKEY].data.ref.data,
                dataASN[ECCKEYASN_IDX_PKEY].data.ref.length,
                dataASN[ECCKEYASN_IDX_PUBKEY_VAL].data.ref.data,
                dataASN[ECCKEYASN_IDX_PUBKEY_VAL].data.ref.length,
                key, curve_id);
    }

    FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_CUSTOM_CURVES
#endif /* WOLFSSL_CUSTOM_CURVES */

#ifdef WOLFSSL_ASN_TEMPLATE
WOLFSSL_ABI
int wc_EccPublicKeyDecode(const byte* input, word32* inOutIdx,
                          ecc_key* key, word32 inSz)
{
    /* eccKeyASN is longer than eccPublicKeyASN. */
    DECL_ASNGETDATA(dataASN, eccKeyASN_Length);
    int ret = 0;
    int curve_id = ECC_CURVE_DEF;
    int oidIdx = ECCPUBLICKEYASN_IDX_ALGOID_CURVEID;
#ifdef WOLFSSL_CUSTOM_CURVES
    int specIdx = ECCPUBLICKEYASN_IDX_ALGOID_PARAMS;
#endif
    int pubIdx = ECCPUBLICKEYASN_IDX_PUBKEY;

    if ((input == NULL) || (inOutIdx == NULL) || (key == NULL) || (inSz == 0)) {
        return BAD_FUNC_ARG;
    }

    ALLOC_ASNGETDATA(dataASN, eccKeyASN_Length, ret, key->heap);
    if (ret != 0)
        return ret;

    /* Clear dynamic data for ECC public key. */
    XMEMSET(dataASN, 0, sizeof(*dataASN) * eccPublicKeyASN_Length);
#if !defined(WOLFSSL_SM2) || !defined(WOLFSSL_SM3)
    /* Set required ECDSA OID and ignore the curve OID type. */
    GetASN_ExpBuffer(&dataASN[ECCPUBLICKEYASN_IDX_ALGOID_OID], keyEcdsaOid,
            sizeof(keyEcdsaOid));
#else
    GetASN_OID(&dataASN[ECCPUBLICKEYASN_IDX_ALGOID_OID], oidKeyType);
#endif
    GetASN_OID(&dataASN[oidIdx], oidCurveType);
    /* Decode the public ECC key. */
    ret = GetASN_Items(eccPublicKeyASN, dataASN, eccPublicKeyASN_Length, 1,
                       input, inOutIdx, inSz);
    if (ret != 0) {
        oidIdx = ECCKEYASN_IDX_CURVEID;
    #ifdef WOLFSSL_CUSTOM_CURVES
        specIdx = ECCKEYASN_IDX_CURVEPARAMS;
    #endif
        pubIdx = ECCKEYASN_IDX_PUBKEY_VAL;

        /* Clear dynamic data for ECC private key. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * eccKeyASN_Length);
        /* Check named curve OID type. */
        GetASN_OID(&dataASN[oidIdx], oidCurveType);
        /* Try private key format .*/
        ret = GetASN_Items(eccKeyASN, dataASN, eccKeyASN_Length, 1, input,
                           inOutIdx, inSz);
        if (ret != 0) {
            ret = ASN_PARSE_E;
        }
    }

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if ((ret == 0) && (oidIdx == ECCPUBLICKEYASN_IDX_ALGOID_CURVEID)) {
        int oidSum = dataASN[ECCPUBLICKEYASN_IDX_ALGOID_OID].data.oid.sum;
        if ((oidSum != ECDSAk) && (oidSum != SM2k)) {
            ret = ASN_PARSE_E;
        }
    }
#endif
    if (ret == 0) {
        if (dataASN[oidIdx].tag != 0) {
            /* Named curve - check and get id. */
            curve_id = CheckCurve(dataASN[oidIdx].data.oid.sum);
            if (curve_id < 0) {
                ret = ASN_OBJECT_ID_E;
            }
        }
        else {
        #ifdef WOLFSSL_CUSTOM_CURVES
            /* Parse explicit parameters. */
            ret = EccSpecifiedECDomainDecode(dataASN[specIdx].data.ref.data,
                                         dataASN[specIdx].data.ref.length, key,
                                         key->heap, NULL);
        #else
            /* Explicit parameters not supported in build configuration. */
            ret = ASN_PARSE_E;
        #endif
        }
    }
    if (ret == 0) {
        /* Import public point. */
        ret = wc_ecc_import_x963_ex(dataASN[pubIdx].data.ref.data,
                dataASN[pubIdx].data.ref.length, key, curve_id);
        if (ret != 0) {
            ret = ASN_ECC_KEY_E;
        }
    }

    FREE_ASNGETDATA(dataASN, key->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef HAVE_ECC_KEY_EXPORT
/* build DER formatted ECC key, include optional public key if requested,
 * return length on success, negative on error */
#ifdef WOLFSSL_ASN_TEMPLATE
int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *outLen,
                             int pubIn, int curveIn)
{
    DECL_ASNSETDATA(dataASN, eccKeyASN_Length);
    word32 privSz = 0;
    word32 pubSz = 0;
    word32 sz = 0;
    int ret = 0;
    int curveIdSz = 0;

    /* Check validity of parameters. */
    if ((key == NULL) || ((output == NULL) && (outLen == NULL))) {
        ret = BAD_FUNC_ARG;
    }

    /* Check key has parameters when encoding curve. */
    if ((ret == 0) && curveIn && (key->dp == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    if (ret == 0)
        CALLOC_ASNSETDATA(dataASN, eccKeyASN_Length, ret, key->heap);

    if (ret == 0) {
        /* Private key size is the curve size. */
        privSz = (word32)key->dp->size;
        if (pubIn) {
            /* Get the length of the public key. */
            PRIVATE_KEY_UNLOCK();
            ret = wc_ecc_export_x963(key, NULL, &pubSz);
            PRIVATE_KEY_LOCK();
            if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E))
                ret = 0;
        }
    }
    if (ret == 0) {
        /* Version: 1 */
        SetASN_Int8Bit(&dataASN[ECCKEYASN_IDX_VER], 1);
        /* Leave space for private key. */
        SetASN_Buffer(&dataASN[ECCKEYASN_IDX_PKEY], NULL, privSz);
        if (curveIn) {
            /* Get length of the named curve OID to put into the encoding. */
            curveIdSz = SetCurve(key, NULL, 0);
            if (curveIdSz < 0) {
                ret = curveIdSz;
            }
            /* Curve OID */
            SetASN_ReplaceBuffer(&dataASN[ECCKEYASN_IDX_CURVEID], NULL,
                (word32)curveIdSz);
            /* TODO: add support for SpecifiedECDomain curve. */
            dataASN[ECCKEYASN_IDX_CURVEPARAMS].noOut = 1;
        }
        else {
            SetASNItem_NoOutNode(dataASN, eccKeyASN, ECCKEYASN_IDX_PARAMS,
                    eccKeyASN_Length);
        }
        if (ret == 0) {
            if (pubIn) {
                /* Leave space for public key. */
                SetASN_Buffer(&dataASN[ECCKEYASN_IDX_PUBKEY_VAL], NULL, pubSz);
            }
            else {
                /* Don't write out public key. */
                SetASNItem_NoOutNode(dataASN, eccKeyASN, ECCKEYASN_IDX_PUBKEY,
                                     eccKeyASN_Length);
            }
            /* Calculate size of the private key encoding. */
            ret = SizeASN_Items(eccKeyASN, dataASN, eccKeyASN_Length, &sz);
        }
    }
    /* Return the size if no buffer. */
    if ((ret == 0) && (output == NULL)) {
        *outLen = sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }
    /* Check the buffer is big enough. */
    if ((ret == 0) && (outLen != NULL) && (sz > *outLen)) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode the private key. */
        SetASN_Items(eccKeyASN, dataASN, eccKeyASN_Length, output);

        if (curveIn) {
            /* Put named curve OID data into encoding. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            curveIdSz = SetCurve(
                key,
                (byte *)(wc_ptr_t)
                    dataASN[ECCKEYASN_IDX_CURVEID].data.buffer.data,
                (size_t)curveIdSz);
            if (curveIdSz < 0) {
                ret = curveIdSz;
            }
        }
        if (ret == 0) {
            /* Export the private value into the buffer. */
            /* safe cast -- the pointer is actually inside the output buffer. */
            ret = wc_ecc_export_private_only(
                key,
                (byte*)(wc_ptr_t)
                    dataASN[ECCKEYASN_IDX_PKEY].data.buffer.data,
                &privSz);
        }
        if ((ret == 0) && pubIn) {
            /* Export the public point into the buffer. */
            PRIVATE_KEY_UNLOCK();
            /* safe cast -- the pointer is actually inside the output buffer. */
            ret = wc_ecc_export_x963(
                key,
                (byte*)(wc_ptr_t)
                    dataASN[ECCKEYASN_IDX_PUBKEY_VAL].data.buffer.data,
                &pubSz);
            PRIVATE_KEY_LOCK();
        }
    }
    if (ret == 0) {
        /* Return the encoding size. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
/* Write a Private ecc key, including public to DER format,
 * length on success else < 0 */
/* Note: use wc_EccKeyDerSize to get length only */
WOLFSSL_ABI
int wc_EccKeyToDer(ecc_key* key, byte* output, word32 outLen)
{
    return wc_BuildEccKeyDer(key, output, &outLen, 1, 1);
}

/* Write only private ecc key to DER format,
 * length on success else < 0 */
int wc_EccKeyDerSize(ecc_key* key, int pub)
{
    word32 sz = 0;
    int ret = wc_BuildEccKeyDer(key, NULL, &sz, pub, 1);
    if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
        return ret;
    }
    return (int)sz;
 }

/* Write only private ecc key to DER format,
 * length on success else < 0 */
int wc_EccPrivateKeyToDer(ecc_key* key, byte* output, word32 outLen)
{
    int ret = wc_BuildEccKeyDer(key, output, &outLen, 0, 1);
    if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
        return (int)outLen;
    }
    return ret;
}

#ifdef HAVE_PKCS8

/* Write only private ecc key or both private and public parts to unencrypted
 * PKCS#8 format.
 *
 * If output is NULL, places required PKCS#8 buffer size in outLen and
 * returns LENGTH_ONLY_E.
 *
 * return length on success else < 0 */
static int eccToPKCS8(ecc_key* key, byte* output, word32* outLen,
        int includePublic)
{
    int ret;
    word32 tmpDerSz;
    int algoID = 0;
    word32 oidSz = 0;
    word32 pkcs8Sz = 0;
    const byte* curveOID = NULL;
#ifdef WOLFSSL_NO_MALLOC
    byte  tmpDer[ECC_BUFSIZE];
#else
    byte* tmpDer = NULL;
#endif
    word32 sz = ECC_BUFSIZE;

    if (key == NULL || key->dp == NULL || outLen == NULL)
        return BAD_FUNC_ARG;

    /* set algoID, get curve OID */
    algoID = ECDSAk;
    ret = wc_ecc_get_oid(key->dp->oidSum, &curveOID, &oidSz);
    if (ret < 0)
        return ret;

#ifndef WOLFSSL_NO_MALLOC
    /* temp buffer for plain DER key */
    tmpDer = (byte*)XMALLOC(ECC_BUFSIZE, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (tmpDer == NULL)
        return MEMORY_E;
#endif
    XMEMSET(tmpDer, 0, ECC_BUFSIZE);

    ret = wc_BuildEccKeyDer(key, tmpDer, &sz, includePublic, 0);
    if (ret < 0) {
        goto exit;
    }
    tmpDerSz = (word32)ret;

    /* get pkcs8 expected output size */
    ret = wc_CreatePKCS8Key(NULL, &pkcs8Sz, tmpDer, tmpDerSz, algoID,
                            curveOID, oidSz);
    if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
        goto exit;
    }

    if (output == NULL) {
        *outLen = pkcs8Sz;
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
        goto exit;
    }
    else if (*outLen < pkcs8Sz) {
        WOLFSSL_MSG("Input buffer too small for ECC PKCS#8 key");
        ret = BUFFER_E;
        goto exit;
    }

    ret = wc_CreatePKCS8Key(output, &pkcs8Sz, tmpDer, tmpDerSz,
                            algoID, curveOID, oidSz);
    if (ret < 0) {
        goto exit;
    }

    *outLen = (word32)ret;

exit:
    /* tmpDer holds a plaintext copy of the ECC private key - always zeroize
     * it before releasing (or before the stack buffer goes out of scope). */
    ForceZero(tmpDer, ECC_BUFSIZE);
#ifndef WOLFSSL_NO_MALLOC
    XFREE(tmpDer, key->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    return ret;
}

/* Write only private ecc key to unencrypted PKCS#8 format.
 *
 * return length on success else < 0 */
int wc_EccPrivateKeyToPKCS8(ecc_key* key, byte* output, word32* outLen)
{
    return eccToPKCS8(key, output, outLen, 0);
}

/* Write both private and public ecc keys to unencrypted PKCS#8 format.
 *
 * return length on success else < 0 */
int wc_EccKeyToPKCS8(ecc_key* key, byte* output,
                     word32* outLen)
{
    return eccToPKCS8(key, output, outLen, 1);
}
#endif /* HAVE_PKCS8 */
#endif /* HAVE_ECC_KEY_EXPORT */
#endif /* HAVE_ECC */

#ifdef WC_ENABLE_ASYM_KEY_IMPORT
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for a general asymmetric private key: Ed25519, Ed448,
 * falcon, dilithium, etc.
 * RFC 8410, 7 - Private Key Format (but public value is EXPLICIT OCTET_STRING)
 * Check draft-ietf-lamps-dilithium-certificates of draft RFC also.
 */
static const ASNItem privateKeyASN[] = {
/* SEQ            */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* Version */
/* VER            */        { 1, ASN_INTEGER, 0, 0, 0 },
                                         /* privateKeyAlgorithm */
/* PKEYALGO_SEQ   */        { 1, ASN_SEQUENCE, 1, 1, 0 },
/* PKEYALGO_OID   */            { 2, ASN_OBJECT_ID, 0, 0, 1 },
                                         /* privateKey */
/* PKEY           */        { 1, ASN_OCTET_STRING, 0, 1, 0 },
                                         /* CurvePrivateKey */
/* PKEY_CURVEPKEY */            { 2, ASN_OCTET_STRING, 0, 0, 2 },
/* PKEY_SEED_ONLY */            { 2, ASN_CONTEXT_SPECIFIC | ASN_PKEY_SEED,
                                    0, 0, 2 },
/* PKEY_BOTH_SEQ  */            { 2, ASN_SEQUENCE, 1, 1, 2 },
/* PKEY_BOTH_SEED */                { 3, ASN_OCTET_STRING, 0, 0, 0 },
/* PKEY_BOTH_KEY  */                { 3, ASN_OCTET_STRING, 0, 0, 0 },
                                         /* attributes */
/* ATTRS          */        { 1, ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_ATTRS, 1, 1, 1 },
                                         /* publicKey */
/* PUBKEY         */        { 1, ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_PUBKEY, 0, 0, 1 },
};
enum {
    PRIVKEYASN_IDX_SEQ = 0,
    PRIVKEYASN_IDX_VER,
    PRIVKEYASN_IDX_PKEYALGO_SEQ,
    PRIVKEYASN_IDX_PKEYALGO_OID,
    PRIVKEYASN_IDX_PKEY,
    PRIVKEYASN_IDX_PKEY_CURVEPKEY,
    PRIVKEYASN_IDX_PKEY_SEED_ONLY,
    PRIVKEYASN_IDX_PKEY_BOTH_SEQ,
    PRIVKEYASN_IDX_PKEY_BOTH_SEED,
    PRIVKEYASN_IDX_PKEY_BOTH_KEY,
    PRIVKEYASN_IDX_ATTRS,
    PRIVKEYASN_IDX_PUBKEY
};

/* Number of items in ASN.1 template for private key. */
#define privateKeyASN_Length (sizeof(privateKeyASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

#if ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) \
    || (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)) \
    || (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) \
    || (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT)) \
    || defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA))


int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz,
    const byte** seed, word32* seedLen,
    const byte** privKey, word32* privKeyLen,
    const byte** pubKey, word32* pubKeyLen, int* inOutKeyType)
{
    int allowSeed = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 oid;
    int version, length, endKeyIdx, privSz, pubSz;
    const byte* priv;
    const byte* pub;
#else
    int ret = 0;
    DECL_ASNGETDATA(dataASN, privateKeyASN_Length);
    CALLOC_ASNGETDATA(dataASN, privateKeyASN_Length, ret, NULL);
#endif

    if (input == NULL || inOutIdx == NULL || inSz == 0 ||
        (seed == NULL && seedLen != NULL) ||
        (seed != NULL && seedLen == NULL) ||
        privKey == NULL || privKeyLen == NULL ||
        pubKey == NULL || pubKeyLen == NULL ||
        inOutKeyType == NULL) {
    #ifdef WOLFSSL_ASN_TEMPLATE
        FREE_ASNGETDATA(dataASN, NULL);
    #endif
        return BAD_FUNC_ARG;
    }

    allowSeed = (seed != NULL && seedLen != NULL);

#ifndef WOLFSSL_ASN_TEMPLATE
    /* The seed can't be parsed without WOLFSSL_ASN_TEMPLATE */
    if (allowSeed) {
        return ASN_PARSE_E;
    }

    if (GetSequence(input, inOutIdx, &length, inSz) >= 0) {
        endKeyIdx = (int)*inOutIdx + length;

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

        /* RFC 5958: v1 (0) for privateKey only, v2 (1) when publicKey added. */
        if (version != 0 && version != 1) {
            WOLFSSL_MSG("Unrecognized version of private key");
            return ASN_PARSE_E;
        }

        if (GetAlgoId(input, inOutIdx, &oid, oidKeyType, inSz) < 0)
            return ASN_PARSE_E;

        /* If user supplies ANONk (0) key type, we want to auto-detect from
         * DER and copy it back to user */
        if (*inOutKeyType == ANONk) {
            *inOutKeyType = oid;
        }
        /* Otherwise strictly validate against the expected type */
        else if (oid != (word32)*inOutKeyType) {
            return ASN_PARSE_E;
        }

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

        if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0) {
            return ASN_PARSE_E;
        }

        priv = input + *inOutIdx;
        *inOutIdx += (word32)privSz;
    }
    else {
        if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0)
            return ASN_PARSE_E;

        priv = input + *inOutIdx;
        *inOutIdx += (word32)privSz;
        endKeyIdx = (int)*inOutIdx;
    }

    if (endKeyIdx == (int)*inOutIdx) {
        *privKeyLen = (word32)privSz;
        *privKey = priv;
        if (pubKeyLen != NULL)
            *pubKeyLen = 0;
    }
    else {
        if (pubKeyLen == NULL) {
            return BAD_FUNC_ARG;
        }

        if (GetASNHeader(input, ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_PUBKEY | 1,
                         inOutIdx, &pubSz, inSz) < 0) {
            return ASN_PARSE_E;
        }

        pub = input + *inOutIdx;
        *inOutIdx += (word32)pubSz;

        *privKeyLen = (word32)privSz;
        *privKey = priv;
        *pubKeyLen = (word32)pubSz;
        if (pubKey != NULL)
            *pubKey = pub;
    }
    if (endKeyIdx != (int)*inOutIdx)
        return ASN_PARSE_E;
    return 0;
#else
    if (ret == 0) {
        /* If user supplies an expected keyType (algorithm OID sum), attempt to
         * process DER accordingly */
        if (*inOutKeyType != ANONk) {
            word32 oidSz;
            /* Explicit OID check - use expected type */
            const byte* oidDerBytes = OidFromId((word32)*inOutKeyType,
                                                oidKeyType, &oidSz);
            GetASN_ExpBuffer(&dataASN[PRIVKEYASN_IDX_PKEYALGO_OID], oidDerBytes,
                            oidSz);
        }
        else {
            /* Auto-detect OID using template */
            GetASN_OID(&dataASN[PRIVKEYASN_IDX_PKEYALGO_OID], oidKeyType);
        }

        /* Parse full private key. */
        ret = GetASN_Items(privateKeyASN, dataASN, privateKeyASN_Length, 1,
                input, inOutIdx, inSz);
        if (ret == 0) {
            /* Store detected OID if requested */
            if (ret == 0 && *inOutKeyType == ANONk) {
                *inOutKeyType =
                    (int)dataASN[PRIVKEYASN_IDX_PKEYALGO_OID].data.oid.sum;
            }
        }
        /* Parse traditional format (a part of full private key). */
        else if (ret != 0) {
            ret = GetASN_Items(&privateKeyASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY],
                    &dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY],
                    PRIVKEYASN_IDX_ATTRS - PRIVKEYASN_IDX_PKEY_CURVEPKEY, 0,
                    input, inOutIdx, inSz);
            if (ret != 0) {
                ret = ASN_PARSE_E;
            }
        }
    }
    if (ret == 0) {
        /* priv-only */
        if (dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length != 0) {
            if (allowSeed) {
               *seedLen = 0;
               *seed = NULL;
            }
            *privKeyLen
                = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length;
            *privKey = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.data;
        }
        /* seed-only */
        else if (allowSeed &&
            dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.length != 0) {
            *seedLen = dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.length;
            *seed = dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.data;
            *privKeyLen = 0;
            *privKey = NULL;
        }
        /* seed-priv */
        else if (allowSeed &&
            dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEQ].data.ref.length != 0) {
            *seedLen = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEED].data.ref.length;
            *seed = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEED].data.ref.data;
            *privKeyLen = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_KEY].data.ref.length;
            *privKey = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_KEY].data.ref.data;
        }
        else {
            ret = ASN_PARSE_E;
        }
    }

    if (ret == 0) {
        if (dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.length != 0) {
            /* Import public value. */
            *pubKeyLen = dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.length;
            *pubKey = dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.data;
        }
        else {
            /* Set public length to 0 as not seen. */
            *pubKeyLen = 0;
            *pubKey = NULL;
        }
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
#endif /* WOLFSSL_ASN_TEMPLATE */
}

int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz,
    byte* privKey, word32* privKeyLen,
    byte* pubKey, word32* pubKeyLen, int keyType)
{
    int ret = 0;
    const byte* privKeyPtr = NULL;
    const byte* pubKeyPtr = NULL;
    word32 privKeyPtrLen = 0;
    word32 pubKeyPtrLen = 0;

    if (privKey == NULL) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, NULL, NULL,
            &privKeyPtr, &privKeyPtrLen, &pubKeyPtr, &pubKeyPtrLen, &keyType);
    }
    if ((ret == 0) && (privKeyPtrLen > *privKeyLen)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (pubKeyLen != NULL) && (pubKeyPtrLen > *pubKeyLen)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (privKeyPtr != NULL)) {
        XMEMCPY(privKey, privKeyPtr, privKeyPtrLen);
        *privKeyLen = privKeyPtrLen;
    }
    if ((ret == 0) && (pubKey != NULL) && (pubKeyPtr != NULL)) {
        XMEMCPY(pubKey, pubKeyPtr, pubKeyPtrLen);
    }
    if ((ret == 0) && (pubKeyLen != NULL)) {
        *pubKeyLen = pubKeyPtrLen;
    }

    return ret;
}

int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz,
    const byte** pubKey, word32* pubKeyLen, int *inOutKeyType)
{
    int ret = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
    int length;
    word32 oid;
#else
    word32 len;
    DECL_ASNGETDATA(dataASN, publicKeyASN_Length);
#endif

    if (input == NULL || inSz == 0 || inOutIdx == NULL ||
        pubKey == NULL || pubKeyLen == NULL || inOutKeyType == NULL) {
        return BAD_FUNC_ARG;
    }

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

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

    if (GetObjectId(input, inOutIdx, &oid, oidKeyType, inSz) < 0)
        return ASN_PARSE_E;

    /* If user supplies ANONk (0) key type, we want to auto-detect from
     * DER and copy it back to user */
    if (*inOutKeyType == ANONk) {
        *inOutKeyType = oid;
    }
    /* Otherwise strictly validate against the expected type */
    else if (oid != (word32)*inOutKeyType) {
        return ASN_PARSE_E;
    }

    /* key header */
    ret = CheckBitString(input, inOutIdx, &length, inSz, 1, NULL);
    if (ret != 0)
        return ret;

    /* check that input buffer is exhausted */
    if (*inOutIdx + (word32)length != inSz)
        return ASN_PARSE_E;

    /* This is the raw point data compressed or uncompressed. */
    *pubKeyLen = (word32)length;
    *pubKey = input + *inOutIdx;
#else
    len = inSz - *inOutIdx;

    CALLOC_ASNGETDATA(dataASN, publicKeyASN_Length, ret, NULL);

    if (ret == 0) {
        /* If user supplies an expected keyType (algorithm OID sum), attempt to
         * process DER accordingly */
        if (*inOutKeyType != ANONk) {
            word32 oidSz;
            /* Explicit OID check - use expected type */
            const byte* oidDerBytes = OidFromId((word32)*inOutKeyType,
                                              oidKeyType, &oidSz);
            GetASN_ExpBuffer(&dataASN[PUBKEYASN_IDX_ALGOID_OID], oidDerBytes,
                           oidSz);
        }
        else {
            /* Auto-detect OID using template */
            GetASN_OID(&dataASN[PUBKEYASN_IDX_ALGOID_OID], oidKeyType);
        }
        /* Decode public key. */
        ret = GetASN_Items(publicKeyASN, dataASN, publicKeyASN_Length, 1,
                           input, inOutIdx, inSz);
        if (ret != 0)
            ret = ASN_PARSE_E;
        /* check that input buffer is exhausted */
        if (*inOutIdx != inSz)
            ret = ASN_PARSE_E;

        /* Store detected OID if requested */
        if (ret == 0 && *inOutKeyType == ANONk) {
            *inOutKeyType =
                (int)dataASN[PUBKEYASN_IDX_ALGOID_OID].data.oid.sum;
        }
    }
    /* Check that the all the buffer was used. */
    if ((ret == 0) &&
            (GetASNItem_Length(dataASN[PUBKEYASN_IDX_SEQ], input) != len)) {
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        *pubKeyLen = dataASN[PUBKEYASN_IDX_PUBKEY].data.ref.length;
        *pubKey = dataASN[PUBKEYASN_IDX_PUBKEY].data.ref.data;
    }

    FREE_ASNGETDATA(dataASN, NULL);
#endif /* WOLFSSL_ASN_TEMPLATE */
    return ret;
}

int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz,
    byte* pubKey, word32* pubKeyLen, int keyType)
{
    int ret = 0;
    const byte* pubKeyPtr = NULL;
    word32 pubKeyPtrLen = 0;

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

    if (ret == 0) {
        ret = DecodeAsymKeyPublic_Assign(input, inOutIdx, inSz, &pubKeyPtr,
            &pubKeyPtrLen, &keyType);
    }
    if ((ret == 0) && (pubKeyPtrLen > *pubKeyLen)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (pubKeyPtr != NULL)) {
        XMEMCPY(pubKey, pubKeyPtr, pubKeyPtrLen);
        *pubKeyLen = pubKeyPtrLen;
    }

    return ret;
}
#endif /* HAVE_ED25519 || etc... ||  WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */
#endif /* WC_ENABLE_ASYM_KEY_IMPORT */

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx,
                               ed25519_key* key, word32 inSz)
{
    int ret;
    byte privKey[ED25519_KEY_SIZE], pubKey[2*ED25519_PUB_KEY_SIZE+1];
    word32 privKeyLen = (word32)sizeof(privKey);
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen,
        pubKey, &pubKeyLen, ED25519k);
    if (ret == 0) {
        if (pubKeyLen == 0) {
            ret = wc_ed25519_import_private_only(privKey, privKeyLen, key);
        }
        else {
            ret = wc_ed25519_import_private_key(privKey, privKeyLen,
                pubKey, pubKeyLen, key);
        }
    }
    return ret;
}

int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx,
                              ed25519_key* key, word32 inSz)
{
    int ret;
    byte pubKey[2*ED25519_PUB_KEY_SIZE+1];
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    /* init pubKey */
    XMEMSET(pubKey, 0, sizeof(pubKey));

    ret = DecodeAsymKeyPublic(input, inOutIdx, inSz,
        pubKey, &pubKeyLen, ED25519k);
    if (ret == 0) {
        ret = wc_ed25519_import_public(pubKey, pubKeyLen, key);
    }
    return ret;
}
#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT */

#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)
int wc_Curve25519PrivateKeyDecode(const byte* input, word32* inOutIdx,
                               curve25519_key* key, word32 inSz)
{
    int ret;
    byte privKey[CURVE25519_KEYSIZE];
    word32 privKeyLen = CURVE25519_KEYSIZE;

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen,
        NULL, NULL, X25519k);
    if (ret == 0) {
        ret = wc_curve25519_import_private(privKey, privKeyLen, key);
    }
    return ret;
}

int wc_Curve25519PublicKeyDecode(const byte* input, word32* inOutIdx,
                              curve25519_key* key, word32 inSz)
{
    int ret;
    byte pubKey[CURVE25519_KEYSIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    /* init pubKey */
    XMEMSET(pubKey, 0, sizeof(pubKey));

    ret = DecodeAsymKeyPublic(input, inOutIdx, inSz,
        pubKey, &pubKeyLen, X25519k);
    if (ret == 0) {
        ret = wc_curve25519_import_public(pubKey, pubKeyLen, key);
    }
    return ret;
}

/* Decode Curve25519 key from DER format - can handle private only,
 * public only, or private+public key pairs.
 * return 0 on success, negative on error */
int wc_Curve25519KeyDecode(const byte* input, word32* inOutIdx,
                          curve25519_key* key, word32 inSz)
{
    int ret;
    byte privKey[CURVE25519_KEYSIZE];
    byte pubKey[CURVE25519_PUB_KEY_SIZE];
    word32 privKeyLen = CURVE25519_KEYSIZE;
    word32 pubKeyLen = CURVE25519_PUB_KEY_SIZE;

    /* sanity check */
    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    /* Try to decode as private key first (may include public) */
    ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen,
                        pubKey, &pubKeyLen, X25519k);

    if (ret == 0) {
        /* Successfully decoded private key */
        if (pubKeyLen > 0) {
            /* Have both private and public */
            ret = wc_curve25519_import_private_raw(privKey, privKeyLen,
                                                   pubKey, pubKeyLen, key);
        }
        else {
            /* Private only */
            ret = wc_curve25519_import_private(privKey, privKeyLen, key);
        }
    }
    else {
        /* Try decoding as public key */
        *inOutIdx = 0; /* Reset index */
        pubKeyLen = CURVE25519_KEYSIZE;
        ret = DecodeAsymKeyPublic(input, inOutIdx, inSz,
                                  pubKey, &pubKeyLen, X25519k);
        if (ret == 0) {
            /* Successfully decoded public key */
            ret = wc_curve25519_import_public(pubKey, pubKeyLen, key);
        }
    }

    return ret;
}

#endif /* HAVE_CURVE25519 && HAVE_ED25519_KEY_IMPORT */


#ifdef WC_ENABLE_ASYM_KEY_EXPORT

/* Build ASN.1 formatted key based on RFC 5958 (Asymmetric Key Packages)
 *
 * Pass NULL for output to get the size of the encoding.
 *
 * @param [in]  privKey      private key buffer
 * @param [in]  privKeyLen   private key buffer length
 * @param [in]  pubKey       public key buffer (optional)
 * @param [in]  pubKeyLen    public key buffer length
 * @param [out] output       Buffer to put encoded data in (optional)
 * @param [in]  outLen       Size of buffer in bytes
 * @param [in]  keyType      is "enum Key_Sum" like ED25519k
 * @return  Size of encoded data in bytes on success
 * @return  BAD_FUNC_ARG when key is NULL.
 * @return  MEMORY_E when dynamic memory allocation failed.
 */
int SetAsymKeyDer(const byte* privKey, word32 privKeyLen,
    const byte* pubKey, word32 pubKeyLen,
    byte* output, word32 outLen, int keyType)
{
    int ret = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
    word32 idx = 0, seqSz, verSz, algoSz, tmpSz, privSz, pubSz = 0, sz;
#else
    DECL_ASNSETDATA(dataASN, privateKeyASN_Length);
    word32 sz = 0;
#endif

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

#ifndef WOLFSSL_ASN_TEMPLATE
    /* calculate size */
    if (pubKey) {
        pubSz = SetHeader(ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_PUBKEY, pubKeyLen,
            NULL, 0) + pubKeyLen;
    }

    tmpSz  = SetOctetString(privKeyLen, NULL) + privKeyLen;

    privSz = SetOctetString(tmpSz, NULL) + tmpSz;
    algoSz = SetAlgoID(keyType, NULL, oidKeyType, 0);
    verSz  = 3; /* version is 3 bytes (enum + id + version(byte)) */
    seqSz  = SetSequence(verSz + algoSz + privSz + pubSz, NULL);
    sz = seqSz + verSz + algoSz + privSz + pubSz;

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

    if (ret == 0 && output != NULL) {
        /* write out */
        /* seq */
        seqSz = SetSequence(verSz + algoSz + privSz + pubSz, output);
        idx = seqSz;
        /* ver: RFC 5958 requires v2 (1) iff publicKey present, else v1 (0). */
        SetMyVersion((word32)(pubKey ? PKCS8v1 : PKCS8v0), output + idx, FALSE);
        idx += verSz;
        /* algo */
        algoSz = SetAlgoID(keyType, output + idx, oidKeyType, 0);
        idx += algoSz;
        /* privKey */
        idx += SetOctetString(tmpSz, output + idx);
        idx += SetOctetString(privKeyLen, output + idx);
        XMEMCPY(output + idx, privKey, privKeyLen);
        idx += privKeyLen;
        /* pubKey */
        if (pubKey) {
            idx += SetHeader(ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_PUBKEY,
                pubKeyLen, output + idx, 0);
            XMEMCPY(output + idx, pubKey, pubKeyLen);
            idx += pubKeyLen;
        }
        sz = idx;
    }
    if (ret == 0) {
        /* Return size of encoding. */
        ret = (int)sz;
    }
#else

    CALLOC_ASNSETDATA(dataASN, privateKeyASN_Length, ret, NULL);

    if (ret == 0) {
        /* RFC 5958: v2 (1) iff publicKey present, else v1 (0). */
        SetASN_Int8Bit(&dataASN[PRIVKEYASN_IDX_VER],
            (byte)(pubKey ? PKCS8v1 : PKCS8v0));
        /* Set OID. */
        SetASN_OID(&dataASN[PRIVKEYASN_IDX_PKEYALGO_OID], (word32)keyType,
                   oidKeyType);
        /* Leave space for private key. */
        SetASN_Buffer(&dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY], NULL,
                      privKeyLen);
        /* Don't write ML-DSA specific things. */
        SetASNItem_NoOut(dataASN, PRIVKEYASN_IDX_PKEY_SEED_ONLY,
            PRIVKEYASN_IDX_ATTRS);
        /* Don't write out attributes. */
        dataASN[PRIVKEYASN_IDX_ATTRS].noOut = 1;
        if (pubKey) {
            /* Leave space for public key. */
            SetASN_Buffer(&dataASN[PRIVKEYASN_IDX_PUBKEY], NULL, pubKeyLen);
        }
        else {
            /* Don't put out public part. */
            SetASNItem_NoOutNode(dataASN, privateKeyASN, PRIVKEYASN_IDX_PUBKEY,
                    privateKeyASN_Length);
        }

        /* Calculate the size of encoding. */
        ret = SizeASN_Items(privateKeyASN, dataASN, privateKeyASN_Length, &sz);
    }

    /* Check buffer is big enough. */
    if ((ret == 0) && (output != NULL) && (sz > outLen)) {
        ret = BAD_FUNC_ARG;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode private key. */
        SetASN_Items(privateKeyASN, dataASN, privateKeyASN_Length, output);

        /* Put private value into space provided. */
        /* safe cast -- the pointer is actually inside output buffer. */
        XMEMCPY(
            (byte*)(wc_ptr_t)
                dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.buffer.data,
            privKey, privKeyLen);

        if (pubKey != NULL) {
            /* Put public value into space provided. */
            /* safe cast -- the pointer is actually inside output buffer. */
            XMEMCPY(
                (byte*)(wc_ptr_t)
                    dataASN[PRIVKEYASN_IDX_PUBKEY].data.buffer.data,
                pubKey, pubKeyLen);
        }
    }
    if (ret == 0) {
        /* Return size of encoding. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, NULL);
#endif
    return ret;
}
#endif /* WC_ENABLE_ASYM_KEY_EXPORT */

#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
/* Write a Private ED25519 key, including public to DER format,
 * length on success else < 0 */
int wc_Ed25519KeyToDer(const ed25519_key* key, byte* output, word32 outLen)
{
    if (key == NULL) {
        return BAD_FUNC_ARG;
    }
    return SetAsymKeyDer(key->k, ED25519_KEY_SIZE,
        key->p, ED25519_PUB_KEY_SIZE, output, outLen, ED25519k);
}

/* Write only private ED25519 key to DER format,
 * length on success else < 0 */
int wc_Ed25519PrivateKeyToDer(const ed25519_key* key, byte* output, word32 outLen)
{
    if (key == NULL) {
        return BAD_FUNC_ARG;
    }
    return SetAsymKeyDer(key->k, ED25519_KEY_SIZE,
        NULL, 0, output, outLen, ED25519k);
}
#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT */

#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT)
/* Write only private Curve25519 key to DER format,
 * length on success else < 0 */
int wc_Curve25519PrivateKeyToDer(curve25519_key* key, byte* output,
                                 word32 outLen)
{
    int    ret;
    byte   privKey[CURVE25519_KEYSIZE];
    word32 privKeyLen = CURVE25519_KEYSIZE;

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

    ret = wc_curve25519_export_private_raw(key, privKey, &privKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDer(privKey, privKeyLen, NULL, 0, output, outLen,
            X25519k);
    }
    return ret;
}

/* Write a public Curve25519 key to DER format,
 * length on success else < 0 */
int wc_Curve25519PublicKeyToDer(curve25519_key* key, byte* output, word32 outLen,
                             int withAlg)
{
    int    ret;
    byte   pubKey[CURVE25519_PUB_KEY_SIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

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

    ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            X25519k, withAlg);
    }
    return ret;
}

/* Export Curve25519 key to DER format - handles private only, public only,
 * or private+public key pairs based on what's set in the key structure.
 * Returns length written on success, negative on error */
int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, word32 outLen,
                          int withAlg)
{
    int ret;
    byte privKey[CURVE25519_KEYSIZE];
    byte pubKey[CURVE25519_PUB_KEY_SIZE];
    word32 privKeyLen = CURVE25519_KEYSIZE;
    word32 pubKeyLen = CURVE25519_PUB_KEY_SIZE;

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

    /* Check what we have in the key structure */
    if (key->privSet) {
        /* Export private key to buffer */
        ret = wc_curve25519_export_private_raw(key, privKey, &privKeyLen);
        if (ret != 0) {
            return ret;
        }

        if (key->pubSet) {
            /* Export public key if available */
            ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen);
            if (ret != 0) {
                return ret;
            }
            /* Export both private and public */
            ret = SetAsymKeyDer(privKey, privKeyLen,
                                pubKey, pubKeyLen,
                                output, outLen, X25519k);
        }
        else {
            /* Export private only */
            ret = SetAsymKeyDer(privKey, privKeyLen,
                                NULL, 0,
                                output, outLen, X25519k);
        }
    }
    else if (key->pubSet) {
        /* Export public key only */
        ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen);
        if (ret == 0) {
            ret = SetAsymKeyDerPublic(pubKey, pubKeyLen,
                                      output, outLen, X25519k, withAlg);
        }
    }
    else {
        /* Neither public nor private key is set */
        ret = BAD_FUNC_ARG;
    }

    return ret;
}
#endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_EXPORT */

#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx,
                               ed448_key* key, word32 inSz)
{
    int ret;
    byte privKey[ED448_KEY_SIZE], pubKey[ED448_PUB_KEY_SIZE];
    word32 privKeyLen = (word32)sizeof(privKey);
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen,
        pubKey, &pubKeyLen, ED448k);
    if (ret == 0) {
        if (pubKeyLen == 0) {
            ret = wc_ed448_import_private_only(privKey, privKeyLen, key);
        }
        else {
            ret = wc_ed448_import_private_key(privKey, privKeyLen,
                pubKey, pubKeyLen, key);
        }
    }
    return ret;
}

int wc_Ed448PublicKeyDecode(const byte* input, word32* inOutIdx,
                              ed448_key* key, word32 inSz)
{
    int ret;
    byte pubKey[2 * ED448_PUB_KEY_SIZE + 1];
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    ret = DecodeAsymKeyPublic(input, inOutIdx, inSz,
        pubKey, &pubKeyLen, ED448k);
    if (ret == 0) {
        ret = wc_ed448_import_public(pubKey, pubKeyLen, key);
    }
    return ret;
}
#endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */

#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT)
int wc_Curve448PrivateKeyDecode(const byte* input, word32* inOutIdx,
                               curve448_key* key, word32 inSz)
{
    int ret;
    byte privKey[CURVE448_KEY_SIZE];
    word32 privKeyLen = CURVE448_KEY_SIZE;

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen,
        NULL, NULL, X448k);
    if (ret == 0) {
        ret = wc_curve448_import_private(privKey, privKeyLen, key);
    }
    return ret;
}

int wc_Curve448PublicKeyDecode(const byte* input, word32* inOutIdx,
                              curve448_key* key, word32 inSz)
{
    int ret;
    byte pubKey[CURVE448_PUB_KEY_SIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

    if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) {
        return BAD_FUNC_ARG;
    }

    /* init pubKey */
    XMEMSET(pubKey, 0, sizeof(pubKey));

    ret = DecodeAsymKeyPublic(input, inOutIdx, inSz,
        pubKey, &pubKeyLen, X448k);
    if (ret == 0) {
        ret = wc_curve448_import_public(pubKey, pubKeyLen, key);
    }
    return ret;
}
#endif /* HAVE_CURVE448 && HAVE_ED448_KEY_IMPORT */

#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)
/* Write a Private ecc key, including public to DER format,
 * length on success else < 0 */
int wc_Ed448KeyToDer(const ed448_key* key, byte* output, word32 outLen)
{
    if (key == NULL) {
        return BAD_FUNC_ARG;
    }
    return SetAsymKeyDer(key->k, ED448_KEY_SIZE,
        key->p, ED448_KEY_SIZE, output, outLen, ED448k);
}

/* Write only private ecc key to DER format,
 * length on success else < 0 */
int wc_Ed448PrivateKeyToDer(const ed448_key* key, byte* output, word32 outLen)
{
    if (key == NULL) {
        return BAD_FUNC_ARG;
    }
    return SetAsymKeyDer(key->k, ED448_KEY_SIZE,
        NULL, 0, output, outLen, ED448k);
}

#endif /* HAVE_ED448 && HAVE_ED448_KEY_EXPORT */

#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT)
/* Write private Curve448 key to DER format,
 * length on success else < 0 */
int wc_Curve448PrivateKeyToDer(curve448_key* key, byte* output, word32 outLen)
{
    int    ret;
    byte   privKey[CURVE448_KEY_SIZE];
    word32 privKeyLen = CURVE448_KEY_SIZE;

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

    ret = wc_curve448_export_private_raw(key, privKey, &privKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDer(privKey, privKeyLen, NULL, 0, output, outLen,
            X448k);
    }
    return ret;
}
/* Write a public Curve448 key to DER format,
 * length on success else < 0 */
int wc_Curve448PublicKeyToDer(curve448_key* key, byte* output, word32 outLen,
                             int withAlg)
{
    int    ret;
    byte   pubKey[CURVE448_PUB_KEY_SIZE];
    word32 pubKeyLen = (word32)sizeof(pubKey);

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

    ret = wc_curve448_export_public(key, pubKey, &pubKeyLen);
    if (ret == 0) {
        ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, output, outLen,
            X448k, withAlg);
    }
    return ret;
}
#endif /* HAVE_CURVE448 && HAVE_CURVE448_KEY_EXPORT */



#if defined(HAVE_OCSP) && !defined(WOLFCRYPT_ONLY)


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSP single response.
 * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response
 */
static const ASNItem singleResponseASN[] = {
/* SEQ                   */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                                      /* certId */
/* CID_SEQ               */     { 1, ASN_SEQUENCE, 1, 0, 0 },
                                                      /* certStatus - CHOICE */
                                                      /* good              [0] IMPLICIT NULL */
/* CS_GOOD               */     { 1, ASN_CONTEXT_SPECIFIC | 0, 0, 0, 2 },
                                                      /* revoked           [1] IMPLICIT RevokedInfo */
/* CS_REVOKED            */     { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 1, 2 },
                                                          /* revocationTime */
/* CS_REVOKED_TIME       */         { 2, ASN_GENERALIZED_TIME, 0, 0, 0 },
                                                          /* revocationReason  [0] EXPLICIT CRLReason OPTIONAL */
/* CS_REVOKED_REASON     */         { 2, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
                                                              /* crlReason */
/* CS_REVOKED_REASON_VAL */             { 3, ASN_ENUMERATED, 0, 0, 0 },
                                                      /* unknown           [2] IMPLICIT UnknownInfo ::= NULL */
/* UNKNOWN               */     { 1, ASN_CONTEXT_SPECIFIC | 2, 0, 0, 2 },

                                                      /* thisUpdate */
/* THISUPDATE_GT         */     { 1, ASN_GENERALIZED_TIME, 0, 0, 0 },
                                                      /* nextUpdate */
/* NEXTUPDATE            */     { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
/* NEXTUPDATE_GT         */         { 2, ASN_GENERALIZED_TIME, 0, 0, 0 },
                                                      /* singleExtensions */
/* EXT                   */     { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 1 },
};
enum {
    SINGLERESPONSEASN_IDX_SEQ = 0,
    SINGLERESPONSEASN_IDX_CID_SEQ,
    SINGLERESPONSEASN_IDX_CS_GOOD,
    SINGLERESPONSEASN_IDX_CS_REVOKED,
    SINGLERESPONSEASN_IDX_CS_REVOKED_TIME,
    SINGLERESPONSEASN_IDX_CS_REVOKED_REASON,
    SINGLERESPONSEASN_IDX_CS_REVOKED_REASON_VAL,
    SINGLERESPONSEASN_IDX_UNKNOWN,
    SINGLERESPONSEASN_IDX_THISUPDATE_GT,
    SINGLERESPONSEASN_IDX_NEXTUPDATE,
    SINGLERESPONSEASN_IDX_NEXTUPDATE_GT,
    SINGLERESPONSEASN_IDX_EXT,
};

/* Number of items in ASN.1 template for OCSP single response. */
#define singleResponseASN_Length (sizeof(singleResponseASN) / sizeof(ASNItem))

static const ASNItem certIDASNItems[] = {
                                                            /* hashAlgorithm */
/* CID_HASHALGO_SEQ      */         { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CID_HASHALGO_OID      */             { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CID_HASHALGO_NULL     */             { 1, ASN_TAG_NULL, 0, 0, 1 },
                                                          /* issuerNameHash */
/* CID_ISSUERHASH        */         { 0, ASN_OCTET_STRING, 0, 0, 0 },
                                                          /* issuerKeyHash */
/* CID_ISSUERKEYHASH     */         { 0, ASN_OCTET_STRING, 0, 0, 0 },
                                                          /* serialNumber */
/* CID_SERIAL            */         { 0, ASN_INTEGER, 0, 0, 0 },
};

enum {
    CERTIDASN_IDX_CID_HASHALGO_SEQ,
    CERTIDASN_IDX_CID_HASHALGO_OID,
    CERTIDASN_IDX_CID_HASHALGO_NULL,
    CERTIDASN_IDX_CID_ISSUERHASH,
    CERTIDASN_IDX_CID_ISSUERKEYHASH,
    CERTIDASN_IDX_CID_SERIAL,
};

#define certidasn_Length (sizeof(certIDASNItems) / sizeof(ASNItem))
#endif

#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
WC_MAYBE_UNUSED static int EncodeCertID(OcspEntry* entry, byte* out,
        word32* outSz)
{
    DECL_ASNSETDATA(dataASN, certidasn_Length);
    int ret = 0;
    word32 sz = 0;
    word32 digestSz = 0;

    if (entry == NULL || entry->status == NULL ||
            entry->status->serialSz <= 0 || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    WOLFSSL_ENTER("EncodeCertID");

    CALLOC_ASNSETDATA(dataASN, certidasn_Length, ret, NULL);

    if (ret == 0) {
        digestSz = (word32)wc_HashGetDigestSize(
            wc_OidGetHash((int)entry->hashAlgoOID));
        if (digestSz <= 0)
            ret = ASN_SIG_HASH_E;
    }
    if (ret == 0) {
        SetASN_OID(&dataASN[CERTIDASN_IDX_CID_HASHALGO_OID],
            entry->hashAlgoOID, oidHashType);
        SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERHASH],
            entry->issuerHash, digestSz);
        SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERKEYHASH],
            entry->issuerKeyHash, digestSz);
        SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_SERIAL],
            entry->status->serial, entry->status->serialSz);
        ret = SizeASN_Items(certIDASNItems, dataASN, certidasn_Length, &sz);
    }
    /* Check buffer big enough for encoding if supplied. */
    if (ret == 0 && out != NULL && sz > *outSz) {
        ret = BUFFER_E;
    }
    if (ret == 0 && out != NULL)
        if (SetASN_Items(certIDASNItems, dataASN, certidasn_Length, out) !=
                (int)sz)
            ret = ASN_PARSE_E;
    if (ret == 0)
        *outSz = sz;

    FREE_ASNSETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_ASN_TEMPLATE
static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz,
                 OcspEntry* entry)
{
    DECL_ASNGETDATA(dataASN, certidasn_Length);
    word32 issuerKeyHashLen = WC_MAX_DIGEST_SIZE;
    word32 issuerHashLen = WC_MAX_DIGEST_SIZE;
    word32 serialSz = EXTERNAL_SERIAL_SIZE;
    word32 digestSz;
    int ret = 0;

    WOLFSSL_ENTER("DecodeCertIdTemplate");
    CALLOC_ASNGETDATA(dataASN, certidasn_Length, ret, NULL);
    if (ret != 0)
        return ret;

    GetASN_OID(&dataASN[CERTIDASN_IDX_CID_HASHALGO_OID], oidHashType);
    GetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERHASH], entry->issuerHash,
        &issuerHashLen);
    GetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERKEYHASH],
        entry->issuerKeyHash, &issuerKeyHashLen);
    GetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_SERIAL], entry->status->serial,
                    &serialSz);
    ret = GetASN_Items(certIDASNItems, dataASN, certidasn_Length,
                1, input, inOutIdx, inSz);
    if (ret != 0) {
       goto out;
    }
    entry->status->serialSz = serialSz;
    entry->hashAlgoOID =
        dataASN[CERTIDASN_IDX_CID_HASHALGO_OID].data.oid.sum;
    digestSz = wc_HashGetDigestSize(wc_OidGetHash(entry->hashAlgoOID));
    if (issuerKeyHashLen != digestSz || issuerHashLen != digestSz) {
        ret = ASN_PARSE_E;
        goto out;
    }
out:
    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

int OcspDecodeCertID(const byte *input, word32 *inOutIdx, word32 inSz,
    OcspEntry *entry)
{
    word32 seqIdx = 0;
    int len = inSz;
    int ret;

#ifndef WOLFSSL_ASN_TEMPLATE
    ret = GetSequence(input, inOutIdx, &len, inSz);
#else
    ret = GetASN_Sequence(input, inOutIdx, &len, inSz, 0);
#endif
    if (ret < 0)
        return ASN_PARSE_E;
    ret = OcspDecodeCertIDInt(input + *inOutIdx, &seqIdx, len, entry);
    if (ret < 0)
        return ASN_PARSE_E;
    if (seqIdx != (word32)len)
        return ASN_PARSE_E;
    *inOutIdx += len;

    return 0;
}

#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
WC_MAYBE_UNUSED static int EncodeSingleResponse(OcspEntry* single, byte* out,
        word32* outSz, void* heap)
{
    DECL_ASNSETDATA(dataASN, singleResponseASN_Length);
    int ret = 0;
    word32 sz = 0;
    word32 cidSz = 0;

    if (single == NULL ||
            /* Only one status is allowed per single response */
            single->status == NULL || single->status->next != NULL ||
            /* thisDate has to be set */
            single->status->thisDateSz == 0 ||
            single->status->thisDateSz > MAX_DATE_SIZE ||
            single->status->thisDateFormat != ASN_GENERALIZED_TIME ||
            /* nextDate is optional but if set, must be valid */
            single->status->nextDateSz > MAX_DATE_SIZE ||
            (single->status->nextDateSz > 0 &&
             single->status->nextDateFormat != ASN_GENERALIZED_TIME) ||
            outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    WOLFSSL_ENTER("EncodeSingleResponse");

    CALLOC_ASNSETDATA(dataASN, singleResponseASN_Length, ret, heap);

    if (ret == 0)
        ret = EncodeCertID(single, NULL, &cidSz);
    if (ret == 0) {
        SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CID_SEQ], NULL, cidSz);

        if (single->status->status == CERT_GOOD) {
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_CS_REVOKED,
                    singleResponseASN_Length);
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_UNKNOWN,
                    singleResponseASN_Length);
        }
        else if (single->status->status == CERT_REVOKED) {
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_CS_GOOD,
                    singleResponseASN_Length);
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_UNKNOWN,
                    singleResponseASN_Length);
            /* Set revocation time - always GeneralizedTime format */
            if (single->status->revocationDateSz == 0 ||
                single->status->revocationDateSz > MAX_DATE_SIZE) {
                ret = BAD_FUNC_ARG;
            }
            else {
                SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_TIME],
                        single->status->revocationDate,
                        single->status->revocationDateSz);
                /* Set revocation reason - optional field */
                SetASN_Int8Bit(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_REASON_VAL],
                        single->status->revocationReason);
            }
        }
        else if (single->status->status == CERT_UNKNOWN) {
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_CS_GOOD,
                    singleResponseASN_Length);
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_CS_REVOKED,
                    singleResponseASN_Length);
        }
        else {
            ret = BAD_FUNC_ARG;
        }
    }
    if (ret == 0) {
        SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_THISUPDATE_GT],
                single->status->thisDate,
                single->status->thisDateSz);
        /* Handle optional nextUpdate */
        if (single->status->nextDateSz > 0) {
            SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT],
                    single->status->nextDate,
                    single->status->nextDateSz);
        }
        else {
            SetASNItem_NoOutNode(dataASN, singleResponseASN,
                    SINGLERESPONSEASN_IDX_NEXTUPDATE,
                    singleResponseASN_Length);
        }
        /* TODO add singleExtensions support */
        SetASNItem_NoOutNode(dataASN, singleResponseASN,
                SINGLERESPONSEASN_IDX_EXT,
                singleResponseASN_Length);
        /* Calculate size of encoding. */
        ret = SizeASN_Items(singleResponseASN, dataASN,
                singleResponseASN_Length, &sz);
    }
    /* Check buffer big enough for encoding if supplied. */
    if (ret == 0 && out != NULL && sz > *outSz)
        ret = BUFFER_E;
    if (ret == 0 && out != NULL) {
        if (SetASN_Items(singleResponseASN, dataASN,
                singleResponseASN_Length, out) != (int)sz)
            ret = ASN_PARSE_E;
        if (ret == 0) {
            ret = EncodeCertID(single,
                 (byte*)dataASN[SINGLERESPONSEASN_IDX_CID_SEQ].data.buffer.data,
                 &cidSz);
        }
    }
    if (ret == 0)
        *outSz = sz;

    FREE_ASNSETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeSingleResponse(const byte* source, word32* ioIndex,
                                word32 size, int wrapperSz, OcspEntry* single)
{
    DECL_ASNGETDATA(dataASN, singleResponseASN_Length);
    int ret = 0;
    CertStatus* cs = NULL;
    word32 thisDateLen;
    word32 nextDateLen;
    word32 certIdSeqIdx;

    (void)wrapperSz;

    WOLFSSL_ENTER("DecodeSingleResponse");

    CALLOC_ASNGETDATA(dataASN, singleResponseASN_Length, ret, NULL);

    if (ret == 0) {
        cs = single->status;
        /* Set maximum lengths for data. */
        thisDateLen      = MAX_DATE_SIZE;
        nextDateLen      = MAX_DATE_SIZE;

        /* Set OID type, buffers to hold data and variables to hold size. */
        GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_THISUPDATE_GT],
                cs->thisDate, &thisDateLen);
        GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT],
                cs->nextDate, &nextDateLen);
        cs->revocationDateSz = MAX_DATE_SIZE;
        GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_TIME],
                cs->revocationDate, &cs->revocationDateSz);
        GetASN_Int8Bit(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_REASON_VAL],
                &cs->revocationReason);
        /* Decode OCSP single response. */
        ret = GetASN_Items(singleResponseASN, dataASN, singleResponseASN_Length,
                1, source, ioIndex, size);
    }
    if (ret == 0) {
        certIdSeqIdx = 0;
        ret = OcspDecodeCertIDInt(dataASN[SINGLERESPONSEASN_IDX_CID_SEQ].data.ref.data,
            &certIdSeqIdx, dataASN[SINGLERESPONSEASN_IDX_CID_SEQ].data.ref.length, single);
    }
    if (ret == 0) {

        /* Determine status by which item was found. */
        if (dataASN[SINGLERESPONSEASN_IDX_CS_GOOD].tag != 0) {
            cs->status = CERT_GOOD;
        }
        if (dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED].tag != 0) {
            cs->status = CERT_REVOKED;
        }
        if (dataASN[SINGLERESPONSEASN_IDX_UNKNOWN].tag != 0) {
            cs->status = CERT_UNKNOWN;
        }

        /* Store the thisDate format - only one possible. */
        cs->thisDateFormat = ASN_GENERALIZED_TIME;
    #if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFSSL_NO_OCSP_DATE_CHECK)
        /* Check date is a valid string and ASN_BEFORE now. */
        if ((! AsnSkipDateCheck) &&
            !XVALIDATE_DATE(cs->thisDate, ASN_GENERALIZED_TIME, ASN_BEFORE,
                            MAX_DATE_SIZE))
        {
            ret = ASN_BEFORE_DATE_E;
        }
    #endif /* !NO_ASN_TIME_CHECK && !WOLFSSL_NO_OCSP_DATE_CHECK */
    }
#ifdef WOLFSSL_OCSP_PARSE_STATUS
    if (ret == 0) {
        /* Store ASN.1 version of thisDate. */
        WOLFSSL_ASN1_TIME *at;
        cs->thisDateAsn = GetASNItem_Addr(
                dataASN[SINGLERESPONSEASN_IDX_THISUPDATE_GT], source);
        at = &cs->thisDateParsed;
        at->type = ASN_GENERALIZED_TIME;
        XMEMCPY(at->data, cs->thisDate, thisDateLen);
        at->length = (int)thisDateLen;
    }
#endif
    if ((ret == 0) &&
            (dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT].tag != 0)) {
        /* Store the nextDate format - only one possible. */
        cs->nextDateFormat = ASN_GENERALIZED_TIME;
    #if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFSSL_NO_OCSP_DATE_CHECK)
        /* Check date is a valid string and ASN_AFTER now. */
        if ((! AsnSkipDateCheck) &&
            !XVALIDATE_DATE(cs->nextDate, ASN_GENERALIZED_TIME, ASN_AFTER,
                            MAX_DATE_SIZE))
        {
            ret = ASN_AFTER_DATE_E;
        }
    #endif /* !NO_ASN_TIME_CHECK && !WOLFSSL_NO_OCSP_DATE_CHECK */
    }
#ifdef WOLFSSL_OCSP_PARSE_STATUS
    if ((ret == 0) &&
            (dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT].tag != 0))
    {
        /* Store ASN.1 version of thisDate. */
        WOLFSSL_ASN1_TIME *at;
        cs->nextDateAsn = GetASNItem_Addr(
                dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT], source);
        at = &cs->nextDateParsed;
        at->type = ASN_GENERALIZED_TIME;
        XMEMCPY(at->data, cs->nextDate, nextDateLen);
        at->length = (int)nextDateLen;
    }
#endif
    if (ret == 0) {
        /* OcspEntry now used. */
        single->used = 1;
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSP response extension header.
 * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response
 */
static const ASNItem respExtHdrASN[] = {
                                   /* responseExtensions */
/* EXT     */    { 0, ASN_CONTEXT_SPECIFIC | 1, 1, 1, 0 },
                                       /* extensions */
/* EXT_SEQ */        { 1, ASN_SEQUENCE, 1, 1, 0 },
};
enum {
    RESPEXTHDRASN_IDX_EXT = 0,
    RESPEXTHDRASN_IDX_EXT_SEQ,
};

/* Number of items in ASN.1 template for OCSP response extension header. */
#define respExtHdrASN_Length (sizeof(respExtHdrASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeOcspRespExtensions(const byte* source, word32* ioIndex,
                                    OcspResponse* resp, word32 sz)
{
    /* certExtASN_Length is greater than respExtHdrASN_Length */
    DECL_ASNGETDATA(dataASN, certExtASN_Length);
    int ret = 0;
    word32 idx = *ioIndex;
    word32 maxIdx = 0;

    WOLFSSL_ENTER("DecodeOcspRespExtensions");

    CALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, resp->heap);

    if (ret == 0) {
        /* Check for header and move past. */
        ret = GetASN_Items(respExtHdrASN, dataASN, respExtHdrASN_Length, 0,
            source, &idx, sz);
    }
    if (ret == 0) {
        /* Keep end extensions index for total length check. */
        maxIdx = idx + dataASN[RESPEXTHDRASN_IDX_EXT_SEQ].length;
    }

    /* Step through all extensions. */
    while ((ret == 0) && (idx < maxIdx)) {
        byte isCrit = 0;
        /* Clear dynamic data, set OID type to expect. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
        GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidOcspType);
        GetASN_Boolean(&dataASN[CERTEXTASN_IDX_CRIT], &isCrit);
        /* Decode OCSP response extension. */
        ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0,
                           source, &idx, sz);
        if (ret == 0) {
            word32 oid = dataASN[CERTEXTASN_IDX_OID].data.oid.sum;
            int length = (int)dataASN[CERTEXTASN_IDX_VAL].length;

            if (oid == OCSP_NONCE_OID) {
                /* Extract nonce data. */
                ret = GetOctetString(source, &idx, &length, sz);
                if (ret >= 0) {
                    ret = 0;
                    /* get data inside extra OCTET_STRING */
                    resp->nonce = source + idx;
                    resp->nonceSz = length;
                }
            }
            else if (isCrit) {
                ret = ASN_PARSE_E;
            }
            /* Ignore all other extension types. */

            /* Skip over rest of extension. */
            idx += (word32)length;
        }
    }

    /* Return index after extensions. */
    *ioIndex = idx;

    FREE_ASNGETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSP nonce extension.
 * RFC 6960, 4.4.1 - Nonce
 * X.509: RFC 5280, 4.1 - Basic Certificate Fields. (Extension)
 */
static const ASNItem ocspNonceExtASN[] = {
/* SEQ       */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                     /* Extension */
/* EXT       */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                        /* extnId */
/* EXT_OID   */        {2, ASN_OBJECT_ID, 0, 0, 0 },
                                        /* critical not encoded. */
                                        /* extnValue */
/* EXT_VAL   */        {2, ASN_OCTET_STRING, 0, 1, 0 },
                                               /* nonce */
/* EXT_NONCE */            {3, ASN_OCTET_STRING, 0, 0, 0 },
};
enum {
    OCSPNONCEEXTASN_IDX_SEQ = 0,
    OCSPNONCEEXTASN_IDX_EXT,
    OCSPNONCEEXTASN_IDX_EXT_OID,
    OCSPNONCEEXTASN_IDX_EXT_VAL,
    OCSPNONCEEXTASN_IDX_EXT_NONCE,
};

/* Number of items in ASN.1 template for OCSP nonce extension. */
#define ocspNonceExtASN_Length (sizeof(ocspNonceExtASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* Encode OCSP response extensions (currently only nonce) */
#ifdef WOLFSSL_ASN_TEMPLATE
WC_MAYBE_UNUSED static int EncodeOcspRespExtensions(OcspResponse* resp,
        byte* out, word32* outSz)
{
    DECL_ASNSETDATA(dataASN, ocspNonceExtASN_Length);
    word32 sz = 0;
    int ret = 0;

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

    CALLOC_ASNSETDATA(dataASN, ocspNonceExtASN_Length, ret, resp->heap);

    if (ret == 0) {
        SetASN_OID(&dataASN[OCSPNONCEEXTASN_IDX_EXT_OID], OCSP_NONCE_OID,
                oidOcspType);
        SetASN_Buffer(&dataASN[OCSPNONCEEXTASN_IDX_EXT_NONCE],
                resp->nonce, resp->nonceSz);
        /* Calculate size of encoding. */
        ret = SizeASN_Items(ocspNonceExtASN, dataASN,
                ocspNonceExtASN_Length, &sz);
    }
    /* Check buffer big enough for encoding if supplied. */
    if (ret == 0 && out != NULL && sz > *outSz) {
        ret = BUFFER_E;
    }
    if (ret == 0 && out != NULL) {
        if (SetASN_Items(ocspNonceExtASN, dataASN,
                ocspNonceExtASN_Length, out) != (int)sz)
            ret = ASN_PARSE_E;
    }
    if (ret == 0)
        *outSz = sz;

    FREE_ASNSETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSP ResponseData.
 * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response
 */
static const ASNItem ocspRespDataASN[] = {
/* SEQ         */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                             /* version DEFAULT v1 */
/* VER_PRESENT */        { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
/* VER         */            { 2, ASN_INTEGER, 0, 0, 0 },
                                             /* byName */
/* BYNAME      */        { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 2 },
                                             /* byKey */
/* BYKEY       */        { 1, ASN_CONTEXT_SPECIFIC | 2, 1, 1, 2 },
/* BYKEY_OCT   */            { 2, ASN_OCTET_STRING, 0, 0, 0 },
                                             /* producedAt */
/* PA          */        { 1, ASN_GENERALIZED_TIME, 0, 0, 0, },
                                             /* responses */
/* RESP        */        { 1, ASN_SEQUENCE, 1, 0, 0 },
                                             /* responseExtensions */
/* RESPEXT     */        { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 1 }
};
enum {
    OCSPRESPDATAASN_IDX_SEQ = 0,
    OCSPRESPDATAASN_IDX_VER_PRESENT,
    OCSPRESPDATAASN_IDX_VER,
    OCSPRESPDATAASN_IDX_BYNAME,
    OCSPRESPDATAASN_IDX_BYKEY,
    OCSPRESPDATAASN_IDX_BYKEY_OCT,
    OCSPRESPDATAASN_IDX_PA,
    OCSPRESPDATAASN_IDX_RESP,
    OCSPRESPDATAASN_IDX_RESPEXT,
};

/* Number of items in ASN.1 template for OCSP ResponseData. */
#define ocspRespDataASN_Length (sizeof(ocspRespDataASN) / sizeof(ASNItem))
#endif

#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
WC_MAYBE_UNUSED static int EncodeResponseData(OcspResponse* resp, byte* out,
        word32* outSz)
{
    DECL_ASNSETDATA(dataASN, ocspRespDataASN_Length);
    int ret = 0;
    word32 sz = 0;
    word32 respListSz = 0;
    word32 respExtSz = 0;
    OcspEntry* single = NULL;

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

    WOLFSSL_ENTER("EncodeResponseData");

    CALLOC_ASNSETDATA(dataASN, ocspRespDataASN_Length, ret, resp->heap);

    if (ret == 0) {
        if (resp->single == NULL)
            ret = BAD_FUNC_ARG;
    }
    for (single = resp->single; ret == 0 && single != NULL;
         single = single->next) {
        word32 singleRespSz = 0;
        singleRespSz = *outSz - respListSz;
        ret = EncodeSingleResponse(single, NULL, &singleRespSz, resp->heap);
        if (ret == 0)
            respListSz += singleRespSz;
    }
    if (ret == 0) {
        if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
            /* TODO support name-based responder ID */
            ret = NOT_COMPILED_IN;
        }
        else if (resp->responderIdType == OCSP_RESPONDER_ID_KEY) {
            SetASNItem_NoOutNode(dataASN, ocspRespDataASN,
                    OCSPRESPDATAASN_IDX_BYNAME,
                    ocspRespDataASN_Length);
            SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT],
                    resp->responderId.keyHash,
                    OCSP_RESPONDER_ID_KEY_SZ);
        }
        else
            ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        if (resp->producedDateSz > 0 && resp->producedDateSz < MAX_DATE_SIZE) {
            SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_PA],
                    resp->producedDate, resp->producedDateSz);
        }
        else
            ret = BAD_FUNC_ARG;
    }
    if (ret == 0) {
        /* Encode responseExtensions if nonce is present */
        if (resp->nonceSz > 0) {
            ret = EncodeOcspRespExtensions(resp, NULL, &respExtSz);
            if (ret == 0 && respExtSz > 0) {
                SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_RESPEXT],
                        NULL, respExtSz);
            }
        }
        else {
            SetASNItem_NoOutNode(dataASN, ocspRespDataASN,
                    OCSPRESPDATAASN_IDX_RESPEXT,
                    ocspRespDataASN_Length);
        }
    }
    if (ret == 0) {
        SetASN_Int8Bit(&dataASN[OCSPRESPDATAASN_IDX_VER], 0);
        SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_RESP], NULL,
                respListSz);
        /* Calculate size of encoding. */
        ret = SizeASN_Items(ocspRespDataASN, dataASN, ocspRespDataASN_Length,
                &sz);
    }
    /* Check buffer big enough for encoding if supplied. */
    if (ret == 0 && out != NULL && sz > *outSz)
        ret = BUFFER_E;
    if (ret == 0 && out != NULL) {
        byte* respList = NULL;
        if (SetASN_Items(ocspRespDataASN, dataASN,
                ocspRespDataASN_Length, out) != (int)sz)
            ret = ASN_PARSE_E;
        respList = (byte*)dataASN[OCSPRESPDATAASN_IDX_RESP].data.buffer.data;
        for (single = resp->single; ret == 0 && single != NULL;
             single = single->next) {
            word32 singleRespSz = respListSz;
            ret = EncodeSingleResponse(single, respList, &singleRespSz,
                    resp->heap);
            if (ret == 0) {
                respList += singleRespSz;
                respListSz -= singleRespSz;
            }
        }
        if (ret == 0 && respListSz != 0)
            ret = ASN_PARSE_E;
        /* Encode response extensions if buffer was allocated */
        if (ret == 0 && respExtSz > 0) {
            ret = EncodeOcspRespExtensions(resp,
                   (byte*)dataASN[OCSPRESPDATAASN_IDX_RESPEXT].data.buffer.data,
                   &respExtSz);
        }
    }
    if (ret == 0)
        *outSz = sz;

    FREE_ASNSETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeResponseData(const byte* source, word32* ioIndex,
                              OcspResponse* resp, word32 size)
{
    DECL_ASNGETDATA(dataASN, ocspRespDataASN_Length);
    int ret = 0;
    /* Default, not present, is v1 = 0. */
    byte version = 0;
    word32 dateSz = 0;
    word32 responderByKeySz = OCSP_RESPONDER_ID_KEY_SZ;
    word32 idx = *ioIndex;
    OcspEntry* single = NULL;

    WOLFSSL_ENTER("DecodeResponseData");

    CALLOC_ASNGETDATA(dataASN, ocspRespDataASN_Length, ret, resp->heap);

    if (ret == 0) {
        resp->response = source + idx;
        /* Max size of date supported. */
        dateSz = MAX_DATE_SIZE;

        /* Set the where to put version an produced date. */
        GetASN_Int8Bit(&dataASN[OCSPRESPDATAASN_IDX_VER], &version);
        GetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_PA], resp->producedDate,
                &dateSz);
        GetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT],
                resp->responderId.keyHash, &responderByKeySz);
        /* Decode the ResponseData. */
        ret = GetASN_Items(ocspRespDataASN, dataASN, ocspRespDataASN_Length,
                1, source, ioIndex, size);
    }
    /* Only support v1 == 0 */
    if (ret == 0) {
        if (version != 0) {
            ret = ASN_PARSE_E;
        }
    }
    /* Ensure date is a minimal size. */
    if (ret == 0) {
        if  (dateSz < MIN_DATE_SIZE) {
            ret = ASN_PARSE_E;
        }
        resp->producedDateSz = (byte)dateSz;
    }
    if (ret == 0) {
        if (dataASN[OCSPRESPDATAASN_IDX_BYNAME].tag != 0) {
            resp->responderIdType = OCSP_RESPONDER_ID_NAME;
            ret = CalcHashId_ex(
                dataASN[OCSPRESPDATAASN_IDX_BYNAME].data.ref.data,
                dataASN[OCSPRESPDATAASN_IDX_BYNAME].data.ref.length,
                resp->responderId.nameHash, OCSP_RESPONDER_ID_HASH_TYPE);
        } else {
            resp->responderIdType = OCSP_RESPONDER_ID_KEY;
            if (dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT].length
                    != OCSP_RESPONDER_ID_KEY_SZ)
                ret = ASN_PARSE_E;
        }
    }
    if (ret == 0) {
        /* Store size of response. */
        resp->responseSz = *ioIndex - idx;
        /* Store date format/tag. */
        resp->producedDateFormat = dataASN[OCSPRESPDATAASN_IDX_PA].tag;

        /* Get the index of the responses SEQUENCE. */
        idx = GetASNItem_DataIdx(dataASN[OCSPRESPDATAASN_IDX_RESP], source);
        /* Start with the pre-existing OcspEntry. */
        single = resp->single;
    }
    while ((ret == 0) && (idx < dataASN[OCSPRESPDATAASN_IDX_RESPEXT].offset)) {
        /* Allocate and use a new OCSP entry if this is used. */
        if (single->used) {
            single->next = (OcspEntry*)XMALLOC(sizeof(OcspEntry), resp->heap,
                    DYNAMIC_TYPE_OCSP_ENTRY);
            if (single->next == NULL) {
                ret = MEMORY_E;
            }
            else {
                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;
                    ret = MEMORY_E;
                }
                else {
                    XMEMSET(single->next->status, 0, sizeof(CertStatus));

                    /* Entry to be freed. */
                    single->next->isDynamic = 1;
                    single->next->ownStatus = 1;
                    /* used will be 0 (false) */

                    single = single->next;
                }
            }
        }
        if (ret == 0) {
            /* Decode SingleResponse into OcspEntry. */
            ret = DecodeSingleResponse(source, &idx,
                /* max index is start of next item */
                dataASN[OCSPRESPDATAASN_IDX_RESPEXT].offset,
                (int)dataASN[OCSPRESPDATAASN_IDX_RESP].length, single);
            /* single->used set on successful decode. */
        }
    }

    /* Check if there were extensions. */
    if ((ret == 0) &&
            (dataASN[OCSPRESPDATAASN_IDX_RESPEXT].data.buffer.data != NULL)) {
        /* Get index of [1] */
        idx = dataASN[OCSPRESPDATAASN_IDX_RESPEXT].offset;
        /* Decode the response extensions. */
        if (DecodeOcspRespExtensions(source, &idx, resp, *ioIndex) < 0) {
            ret = ASN_PARSE_E;
        }
    }

    FREE_ASNGETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for BasicOCSPResponse.
 * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response
 */
static const ASNItem ocspBasicRespASN[] = {
/* SEQ          */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                            /* tbsResponseData */
/* TBS_SEQ      */     { 1, ASN_SEQUENCE, 1, 0, 0, },
                                            /* signatureAlgorithm */
/* SIGALGO      */     { 1, ASN_SEQUENCE, 1, 1, 0, },
/* SIGALGO_OID  */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* SIGALGO_NULL */         { 2, ASN_TAG_NULL, 0, 0, 1 },
                                            /* parameters */
#ifdef WC_RSA_PSS
/* SIGALGO_PARAMS      */  { 2, ASN_SEQUENCE, 1, 0, 1 },
#endif
                                            /* signature */
/* SIGNATURE    */     { 1, ASN_BIT_STRING, 0, 0, 0 },
                                            /* certs */
/* CERTS        */     { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
/* CERTS_SEQ    */         { 2, ASN_SEQUENCE, 1, 0, 0, },
};
enum {
    OCSPBASICRESPASN_IDX_SEQ = 0,
    OCSPBASICRESPASN_IDX_TBS_SEQ,
    OCSPBASICRESPASN_IDX_SIGALGO,
    OCSPBASICRESPASN_IDX_SIGALGO_OID,
    OCSPBASICRESPASN_IDX_SIGALGO_NULL,
#ifdef WC_RSA_PSS
    OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS,
#endif
    OCSPBASICRESPASN_IDX_SIGNATURE,
    OCSPBASICRESPASN_IDX_CERTS,
    OCSPBASICRESPASN_IDX_CERTS_SEQ,
};

/* Number of items in ASN.1 template for BasicOCSPResponse. */
#define ocspBasicRespASN_Length (sizeof(ocspBasicRespASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

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

#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
static int OcspRespCheck(OcspResponse *resp, Signer *responder)
{
    OcspEntry *s;
    int ret;

    s = resp->single;
    if (s == NULL)
        return -1;

    if (OcspRespIdMatch(resp, responder->subjectNameHash,
            responder->subjectKeyHash) == 0)
        return -1;

    ret = CheckOcspResponder(resp, responder->subjectNameHash,
            responder->subjectKeyHash, responder->extKeyUsage,
            responder->issuerNameHash, responder->issuerKeyHash);
    if (ret != 0)
        return -1;

    return 0;
}
#endif

static Signer *OcspFindSigner(OcspResponse *resp, WOLFSSL_CERT_MANAGER *cm)
{
    Signer *s;

    if (cm == NULL)
        return NULL;

    if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
#ifndef NO_SKID
        s = GetCAByName(cm, resp->responderId.nameHash);
#else
        s = GetCA(cm, resp->responderId.nameHash);
#endif
        if (s)
            return s;
    }
    else if (KEYID_SIZE >= OCSP_RESPONDER_ID_KEY_SZ) {
        /* Responder key hash is OCSP_RESPONDER_ID_KEY_SZ bytes (SHA-1 per
         * RFC 6960) but lookup functions compare KEYID_SIZE bytes. Zero-pad
         * to avoid buffer over-read when KEYID_SIZE > OCSP_RESPONDER_ID_KEY_SZ
         * (e.g. when SM2/SM3 is enabled). */
        byte keyHash[KEYID_SIZE];
        XMEMSET(keyHash, 0, KEYID_SIZE);
        XMEMCPY(keyHash, resp->responderId.keyHash, OCSP_RESPONDER_ID_KEY_SZ);
        s = GetCAByKeyHash(cm, keyHash);
        if (s)
            return s;
    }
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (resp->pendingCAs == NULL)
        return NULL;

    if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
        s = findSignerByName(resp->pendingCAs, resp->responderId.nameHash);
        if (s)
            return s;
    }
    else if (KEYID_SIZE >= OCSP_RESPONDER_ID_KEY_SZ) {
        byte keyHash[KEYID_SIZE];
        XMEMSET(keyHash, 0, KEYID_SIZE);
        XMEMCPY(keyHash, resp->responderId.keyHash, OCSP_RESPONDER_ID_KEY_SZ);
        s = findSignerByKeyHash(resp->pendingCAs, keyHash);
        if (s)
            return s;
    }
#endif
    return NULL;
}

static int OcspCheckCert(OcspResponse *resp, int noVerify,
    int noVerifySignature, WOLFSSL_CERT_MANAGER *cm, void *heap)
{
    int ret = 0;
#ifdef WOLFSSL_SMALL_STACK
    DecodedCert *cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL,
                                              DYNAMIC_TYPE_TMP_BUFFER);
    if (cert == NULL)
        return MEMORY_E;
#else
    DecodedCert cert[1];
#endif

    InitDecodedCert(cert, resp->cert, resp->certSz, heap);
    ret = ParseCertRelative(cert, CERT_TYPE,
                            noVerify ? NO_VERIFY : VERIFY_OCSP_CERT,
                            cm, resp->pendingCAs);
    if (ret < 0) {
        WOLFSSL_MSG("\tOCSP Responder certificate parsing failed");
    }

    if (ret == 0 &&
            OcspRespIdMatch(resp,
                cert->subjectHash, cert->subjectKeyHash) == 0) {
        WOLFSSL_MSG("\tInternal check doesn't match responder ID, ignoring\n");
        ret = BAD_OCSP_RESPONDER;
        goto err;
    }

#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
    if (ret == 0 && !noVerify) {
        ret = CheckOcspResponder(resp, cert->subjectHash, cert->subjectKeyHash,
                cert->extExtKeyUsage, cert->issuerHash,
                (cert->ca != NULL) ? cert->ca->subjectKeyHash : NULL);
        if (ret != 0) {
            WOLFSSL_MSG("\tOCSP Responder certificate issuer check failed");
            goto err;
        }
    }
#endif /* WOLFSSL_NO_OCSP_ISSUER_CHECK */
    if (ret == 0 && !noVerifySignature) {
        ret = ConfirmSignature(
            &cert->sigCtx,
            resp->response, resp->responseSz,
            cert->publicKey, cert->pubKeySize, cert->keyOID,
            resp->sig, resp->sigSz, resp->sigOID, resp->sigParams,
            resp->sigParamsSz, NULL);
    }
err:
    FreeDecodedCert(cert);

#ifdef WOLFSSL_SMALL_STACK
    if (cert != NULL) {
        XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif

    return ret;
}

#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
WC_MAYBE_UNUSED static int EncodeBasicOcspResponse(OcspResponse* resp,
        byte* out, word32* outSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    DECL_ASNSETDATA(dataASN, ocspBasicRespASN_Length);
    int ret = 0;
    word32 sz = 0;
    word32 respDataSz = 0;
    word32 sigSz = 0;

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

    WOLFSSL_ENTER("EncodeBasicOcspResponse");

    CALLOC_ASNSETDATA(dataASN, ocspBasicRespASN_Length, ret, resp->heap);

    if (ret == 0) {
        respDataSz = *outSz;
        ret = EncodeResponseData(resp, NULL, &respDataSz);
    }
    if (ret == 0) {
        if (resp->sigOID == CTC_SHA256wRSA)
            ret = wc_RsaEncryptSize(rsaKey);
        else if (resp->sigOID == CTC_SHA256wECDSA)
            ret = wc_ecc_sig_size(eccKey);
        else
            ret = BAD_FUNC_ARG;
        if (ret > 0) {
            sigSz = (word32)ret;
            ret = 0;
        }
        else
            ret = ret == 0 ? BAD_FUNC_ARG : ret;
    }
    if (ret == 0) {
        SetASN_OID(&dataASN[OCSPBASICRESPASN_IDX_SIGALGO_OID], resp->sigOID,
                oidSigType);
    #ifdef WC_RSA_PSS
        if (resp->sigParams != NULL && resp->sigParamsSz != 0) {
            SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS],
                resp->sigParams, resp->sigParamsSz);
        }
        else {
            SetASNItem_NoOutNode(dataASN, ocspBasicRespASN,
                OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS,
                ocspBasicRespASN_Length);
        }
    #endif
        if (resp->cert != NULL && resp->certSz > 0) {
            SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ],
                resp->cert, resp->certSz);
        }
        else {
            SetASNItem_NoOutNode(dataASN, ocspBasicRespASN,
                OCSPBASICRESPASN_IDX_CERTS,
                ocspBasicRespASN_Length);
        }
        if (out == NULL) {
            /* Calculate size of encoding. */
            SetASN_ReplaceBuffer(&dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ], NULL,
                    respDataSz);
            SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE], NULL,
                    sigSz);
            ret = SizeASN_Items(ocspBasicRespASN, dataASN,
                    ocspBasicRespASN_Length, &sz);
        }
        else {
            /* The real ECC signature might differ from the size returned by
             * wc_ecc_sig_size. We handle this by placing the signature at
             * the end of the buffer and then moving it into place. */
            byte* respData = out + (*outSz - respDataSz - sigSz);
            byte* sigData = out + (*outSz - sigSz);
            if (respDataSz + sigSz > *outSz)
                ret = BUFFER_E;
            if (ret == 0)
                ret = EncodeResponseData(resp, respData, &respDataSz);
            if (ret == 0) {
                CertSignCtx certSignCtx;
                XMEMSET(&certSignCtx, 0, sizeof(CertSignCtx));
                ret = MakeSignature(&certSignCtx, respData, respDataSz,
                        sigData, sigSz, rsaKey, eccKey, NULL, NULL, NULL, NULL,
                        NULL, NULL, NULL, rng, resp->sigOID, resp->heap);
                if (ret > 0) {
                    sigSz = (word32)ret;
                    ret = 0;
                }
                else
                    ret = ret == 0 ? WC_FAILURE : ret;
            }
            if (ret == 0) {
                SetASN_ReplaceBuffer(&dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ],
                        respData, respDataSz);
                SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE],
                        sigData, sigSz);
                /* Re-calculate size of encoding. */
                ret = SizeASN_Items(ocspBasicRespASN, dataASN,
                        ocspBasicRespASN_Length, &sz);
            }
            if (ret == 0) {
                if (SetASN_Items(ocspBasicRespASN, dataASN,
                        ocspBasicRespASN_Length, out) != (int)sz)
                    ret = ASN_PARSE_E;
            }
        }
    }

    if (ret == 0)
        *outSz = sz;
    FREE_ASNSETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_ASN_TEMPLATE
static int DecodeBasicOcspResponse(const byte* source, word32* ioIndex,
            OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify,
            int noVerifySignature)
{
    DECL_ASNGETDATA(dataASN, ocspBasicRespASN_Length);
    int ret = 0;
    word32 idx = *ioIndex;
    Signer* ca = NULL;
    int sigValid = 0;

    WOLFSSL_ENTER("DecodeBasicOcspResponse");
    (void)heap;

    CALLOC_ASNGETDATA(dataASN, ocspBasicRespASN_Length, ret, heap);

    if (ret == 0) {
        /* Set expecting signature OID. */
        GetASN_OID(&dataASN[OCSPBASICRESPASN_IDX_SIGALGO_OID], oidSigType);
        /* Decode BasicOCSPResponse. */
        ret = GetASN_Items(ocspBasicRespASN, dataASN, ocspBasicRespASN_Length,
                1, source, &idx, size);
    }
    if (ret == 0) {
        word32 dataIdx = 0;
        /* Decode the response data. */
        ret = DecodeResponseData(
                GetASNItem_Addr(dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ], source),
                &dataIdx, resp,
                GetASNItem_Length(dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ], source)
                );
    }
#ifdef WC_RSA_PSS
    if (ret == 0 && (dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS].tag != 0)) {
        resp->sigParams = GetASNItem_Addr(
                dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS],
                source);
        resp->sigParamsSz =
               GetASNItem_Length(dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS],
               source);
    }
#endif
    if (ret == 0) {
        /* Get the signature OID and signature. */
        resp->sigOID = dataASN[OCSPBASICRESPASN_IDX_SIGALGO_OID].data.oid.sum;
        GetASN_GetRef(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE], &resp->sig,
                &resp->sigSz);
    }
    resp->certSz = 0;
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
    if ((ret == 0) &&
            (dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data != NULL)) {
        /* TODO: support more than one certificate. */
        /* Store reference to certificate BER data. */
        GetASN_GetRef(&dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ], &resp->cert,
                &resp->certSz);
    }

    if ((ret == 0) && resp->certSz > 0) {
        ret = OcspCheckCert(resp, noVerify, noVerifySignature,
                            (WOLFSSL_CERT_MANAGER*)cm, heap);
        if (ret == 0) {
            sigValid = 1;
        }
        ret = 0; /* try to verify the OCSP response with CA certs */
    }
#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */
    /* try to verify using cm certs */
    if (ret == 0 && !noVerifySignature && !sigValid)
    {
        ca = OcspFindSigner(resp, (WOLFSSL_CERT_MANAGER*)cm);
        if (ca == NULL)
            ret = ASN_NO_SIGNER_E;
    }
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
    if (ret == 0 && !noVerifySignature && !sigValid) {
        if (OcspRespCheck(resp, ca) != 0) {
            ret = BAD_OCSP_RESPONDER;
        }
    }
#endif
    if (ret == 0 && !noVerifySignature && !sigValid) {
        /* Extra NULL check to satisfy compiler */
        if (ca == NULL)
            ret = ASN_NO_SIGNER_E;
        if (ret == 0) {
            SignatureCtx sigCtx;
            /* Initialize the signature context. */
            InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);
            /* TODO: ConfirmSignature is blocking here */
            /* Check the signature of the response CA public key. */
            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");
                ret = ASN_OCSP_CONFIRM_E;
            }
        }
    }
    if (ret == 0) {
        /* Update the position to after response data. */
        *ioIndex = idx;
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

void InitOcspResponse(OcspResponse* resp, OcspEntry* single, CertStatus* status,
                      byte* source, word32 inSz, void* heap)
{
    WOLFSSL_ENTER("InitOcspResponse");

    if (status != NULL) {
        XMEMSET(status, 0, sizeof(CertStatus));
    }
    if (single != NULL) {
        XMEMSET(single,  0, sizeof(OcspEntry));
        single->status = status;
    }
    if (resp != NULL) {
        XMEMSET(resp,   0, sizeof(OcspResponse));
        resp->responseStatus = -1;
        resp->single         = single;
        resp->source         = source;
        resp->maxIdx         = inSz;
        resp->heap           = heap;
        resp->pendingCAs     = NULL;
        resp->sigParams      = NULL;
        resp->sigParamsSz    = 0;
        resp->responderIdType = OCSP_RESPONDER_ID_INVALID;
    }
}

void FreeOcspResponse(OcspResponse* resp)
{
    OcspEntry *single, *next;

    if (resp != NULL) {
        for (single = resp->single; single; single = next) {
            next = single->next;
            if (single->isDynamic) {
                XFREE(single->status, resp->heap, DYNAMIC_TYPE_OCSP_STATUS);
                XFREE(single, resp->heap, DYNAMIC_TYPE_OCSP_ENTRY);
            }
        }
    }
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSPResponse.
 * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response
 */
static const ASNItem ocspResponseASN[] = {
                                     /* OCSPResponse ::= SEQUENCE */
/* SEQ        */ { 0, ASN_SEQUENCE, 1, 1, 0 },
                                         /* responseStatus      OCSPResponseStatus */
/* STATUS     */     { 1, ASN_ENUMERATED, 0, 0, 0, },
                                         /* responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL */
/* BYTES      */     { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
                                             /* ResponseBytes ::= SEQUENCE */
/* BYTES_SEQ  */         { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                /* responseType   OBJECT IDENTIFIER */
/* BYTES_TYPE */            { 3, ASN_OBJECT_ID, 0, 0, 0 },
                                                /* response       OCTET STRING */
/* BYTES_VAL  */            { 3, ASN_OCTET_STRING, 0, 0, 0 },
};
enum {
    OCSPRESPONSEASN_IDX_SEQ = 0,

    OCSPRESPONSEASN_IDX_STATUS,

    OCSPRESPONSEASN_IDX_BYTES,

    OCSPRESPONSEASN_IDX_BYTES_SEQ,

    OCSPRESPONSEASN_IDX_BYTES_TYPE,

    OCSPRESPONSEASN_IDX_BYTES_VAL,
};

/* Number of items in ASN.1 template for OCSPResponse. */
#define ocspResponseASN_Length (sizeof(ocspResponseASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
int OcspResponseEncode(OcspResponse* resp, byte* out, word32* outSz,
        RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    DECL_ASNSETDATA(dataASN, ocspResponseASN_Length);
    int ret = 0;
    word32 sz = 0;
    word32 basicRespSz = 0;

    WOLFSSL_ENTER("OcspResponseEncode");

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

    CALLOC_ASNSETDATA(dataASN, ocspResponseASN_Length, ret, resp->heap);

    if (ret == 0) {
        SetASN_Int8Bit(&dataASN[OCSPRESPONSEASN_IDX_STATUS],
                (byte)resp->responseStatus);
    }
    if (ret == 0 && resp->responseStatus == OCSP_SUCCESSFUL) {
        SetASN_OID(&dataASN[OCSPRESPONSEASN_IDX_BYTES_TYPE], OCSP_BASIC_OID,
                oidOcspType);

        basicRespSz = *outSz;
        ret = EncodeBasicOcspResponse(resp, NULL, &basicRespSz, rsaKey, eccKey,
                rng);
        if (ret == 0 && out == NULL) {
            SetASN_Buffer(&dataASN[OCSPRESPONSEASN_IDX_BYTES_VAL], NULL,
                    basicRespSz);
            ret = SizeASN_Items(ocspResponseASN, dataASN,
                    ocspResponseASN_Length, &sz);
        }
        else if (ret == 0 && out != NULL) {
            /* The size of the basic response may change because of the
             * signature encoding. */
            byte* basicResp = out + (*outSz - basicRespSz);
            if (basicRespSz > *outSz)
                ret = BUFFER_E;
            if (ret == 0) {
                ret = EncodeBasicOcspResponse(resp, basicResp,
                        &basicRespSz, rsaKey, eccKey, rng);
            }
            if (ret == 0) {
                SetASN_Buffer(&dataASN[OCSPRESPONSEASN_IDX_BYTES_VAL],
                        basicResp, basicRespSz);
                ret = SizeASN_Items(ocspResponseASN, dataASN,
                        ocspResponseASN_Length, &sz);
            }
            if (ret == 0 && sz > *outSz)
                ret = BUFFER_E;
            if (ret == 0) {
                if (SetASN_Items(ocspResponseASN, dataASN,
                        ocspResponseASN_Length, out) != (int)sz)
                    ret = ASN_PARSE_E;
            }
        }

    }
    else if (ret == 0 && resp->responseStatus != OCSP_SUCCESSFUL) {
        SetASNItem_NoOutNode(dataASN, ocspResponseASN,
            OCSPRESPONSEASN_IDX_BYTES,
            ocspResponseASN_Length);
        ret = SizeASN_Items(ocspResponseASN, dataASN, ocspResponseASN_Length,
                &sz);
        if (ret == 0 && out != NULL && sz > *outSz)
            ret = BUFFER_E;
        if (ret == 0 && out != NULL) {
            if (SetASN_Items(ocspResponseASN, dataASN,
                    ocspResponseASN_Length, out) != (int)sz)
                ret = ASN_PARSE_E;
        }
    }

    if (ret == 0)
        *outSz = sz;
    FREE_ASNSETDATA(dataASN, resp->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_ASN_TEMPLATE
int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap,
    int noVerifyCert, int noVerifySignature)
{
    DECL_ASNGETDATA(dataASN, ocspResponseASN_Length);
    int ret = 0;
    word32 idx = 0, size = resp->maxIdx;
    byte* source = resp->source;
    byte status = 0;
    const byte* basic;
    word32 basicSz;

    WOLFSSL_ENTER("OcspResponseDecode");

    CALLOC_ASNGETDATA(dataASN, ocspResponseASN_Length, ret, resp->heap);

    if (ret == 0) {
        /* Set variable to put status in and expect OCSP OID. */
        GetASN_Int8Bit(&dataASN[OCSPRESPONSEASN_IDX_STATUS], &status);
        GetASN_OID(&dataASN[OCSPRESPONSEASN_IDX_BYTES_TYPE], oidOcspType);
        /* Decode OCSPResponse (and ResponseBytes). */
        ret = GetASN_Items(ocspResponseASN, dataASN, ocspResponseASN_Length, 1,
            source, &idx, size);
    }
    if (ret == 0) {
        /* Get response. */
        resp->responseStatus = status;
        if (dataASN[OCSPRESPONSEASN_IDX_BYTES_TYPE].data.oid.sum
                == OCSP_BASIC_OID) {
            /* Get reference to BasicOCSPResponse. */
            GetASN_GetRef(&dataASN[OCSPRESPONSEASN_IDX_BYTES_VAL], &basic,
                    &basicSz);
            idx = 0;
            /* Decode BasicOCSPResponse. */
            ret = DecodeBasicOcspResponse(basic, &idx, resp, basicSz, cm, heap,
                noVerifyCert, noVerifySignature);
        }
        /* Only support BasicOCSPResponse. */
        else {
            ret = ASN_PARSE_E;
        }
    }

    FREE_ASNGETDATA(dataASN, resp->heap);
    WOLFSSL_LEAVE("OcspResponseDecode", ret);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
word32 EncodeOcspRequestExtensions(OcspRequest* req, byte* output, word32 size)
{
    const byte NonceObjId[] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
                                       0x30, 0x01, 0x02 };
#ifndef WOLFSSL_ASN_TEMPLATE
    byte seqArray[5][MAX_SEQ_SZ];
    word32 seqSz[5], totalSz = (word32)sizeof(NonceObjId);

    WOLFSSL_ENTER("SetOcspReqExtensions");

    if (!req || !output || !req->nonceSz)
        return 0;

    totalSz += req->nonceSz;
    totalSz += seqSz[0] = SetOctetString(req->nonceSz, seqArray[0]);
    totalSz += seqSz[1] = SetOctetString(req->nonceSz + seqSz[0], seqArray[1]);
    totalSz += seqSz[2] = SetObjectId(sizeof(NonceObjId), seqArray[2]);
    totalSz += seqSz[3] = SetSequence(totalSz, seqArray[3]);
    totalSz += seqSz[4] = SetSequence(totalSz, seqArray[4]);

    if (totalSz > size)
        return 0;

    totalSz = 0;

    XMEMCPY(output + totalSz, seqArray[4], seqSz[4]);
    totalSz += seqSz[4];

    XMEMCPY(output + totalSz, seqArray[3], seqSz[3]);
    totalSz += seqSz[3];

    XMEMCPY(output + totalSz, seqArray[2], seqSz[2]);
    totalSz += seqSz[2];

    XMEMCPY(output + totalSz, NonceObjId, sizeof(NonceObjId));
    totalSz += (word32)sizeof(NonceObjId);

    XMEMCPY(output + totalSz, seqArray[1], seqSz[1]);
    totalSz += seqSz[1];

    XMEMCPY(output + totalSz, seqArray[0], seqSz[0]);
    totalSz += seqSz[0];

    XMEMCPY(output + totalSz, req->nonce, req->nonceSz);
    totalSz += req->nonceSz;

    return totalSz;
#else
    int ret = 0;

    WOLFSSL_ENTER("SetOcspReqExtensions");

    /* Check request has nonce to write in extension. */
    if (req != NULL && req->nonceSz != 0) {
        DECL_ASNSETDATA(dataASN, ocspNonceExtASN_Length);
        word32 sz = 0;

        CALLOC_ASNSETDATA(dataASN, ocspNonceExtASN_Length, ret, req->heap);

        if (ret == 0) {
            /* Set nonce extension OID and nonce. */
            SetASN_Buffer(&dataASN[OCSPNONCEEXTASN_IDX_EXT_OID], NonceObjId,
                    sizeof(NonceObjId));
            SetASN_Buffer(&dataASN[OCSPNONCEEXTASN_IDX_EXT_NONCE], req->nonce,
                    (word32)req->nonceSz);
            /* Calculate size of nonce extension. */
            ret = SizeASN_Items(ocspNonceExtASN, dataASN,
                                ocspNonceExtASN_Length, &sz);
        }
        /* Check buffer big enough for encoding if supplied. */
        if ((ret == 0) && (output != NULL) && (sz > size)) {
            ret = BUFFER_E;
        }
        if ((ret == 0) && (output != NULL)) {
            /* Encode nonce extension. */
            SetASN_Items(ocspNonceExtASN, dataASN, ocspNonceExtASN_Length,
                         output);
        }
        if (ret == 0) {
            /* Return size of encoding. */
            ret = (int)sz;
        }

        FREE_ASNSETDATA(dataASN, req->heap);
    }

    return (word32)ret;
#endif /* WOLFSSL_ASN_TEMPLATE */
}


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for OCSPRequest.
 * RFC 6960, 4.1.1 - ASN.1 Specification of the OCSP Request
 */
static const ASNItem ocspRequestASN[] = {
                                              /* OCSPRequest */
/* SEQ               */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                                  /* tbsRequest */
/* TBS               */        { 1, ASN_SEQUENCE, 1, 1, 0 },
                                                      /* version not written - v1 */
                                                      /* requestorName not written */
                                                      /* requestList */
/* TBS_SEQ           */            { 2, ASN_SEQUENCE, 1, 1, 0 },
                                                          /* Request */
/* TBS_LIST          */                { 3, ASN_SEQUENCE, 1, 1, 0 },
                                                              /* reqCert */
/* TBS_REQ_CID       */                    { 4, ASN_SEQUENCE, 1, 1, 0 },
                                                                  /* hashAlgorithm */
/* TBS_REQ_HASH      */                        { 5, ASN_SEQUENCE, 1, 1, 0 },
/* TBS_REQ_HASH_OID  */                            { 6, ASN_OBJECT_ID, 0, 0, 0 },
                                                                  /* optional NULL params */
/* TBS_REQ_HASH_NULL */                            { 6, ASN_TAG_NULL, 0, 0, 1 },
                                                                  /* issuerNameHash */
/* TBS_REQ_ISSUER    */                        { 5, ASN_OCTET_STRING, 0, 0, 0 },
                                                                  /* issuerKeyHash */
/* TBS_REQ_ISSUERKEY */                        { 5, ASN_OCTET_STRING, 0, 0, 0 },
                                                                  /* serialNumber */
/* TBS_REQ_SERIAL    */                        { 5, ASN_INTEGER, 0, 0, 0 },
                                                      /* requestExtensions */
/* TBS_REQEXT        */            { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 0, 1 },
                                                  /* optionalSignature */
/* OPT_SIG           */        { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 }
};
enum {
    OCSPREQUESTASN_IDX_SEQ = 0,
    OCSPREQUESTASN_IDX_TBS,
    OCSPREQUESTASN_IDX_TBS_SEQ,
    OCSPREQUESTASN_IDX_TBS_LIST,
    OCSPREQUESTASN_IDX_TBS_REQ_CID,
    OCSPREQUESTASN_IDX_TBS_REQ_HASH,
    OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID,
    OCSPREQUESTASN_IDX_TBS_REQ_HASH_NULL,
    OCSPREQUESTASN_IDX_TBS_REQ_ISSUER,
    OCSPREQUESTASN_IDX_TBS_REQ_ISSUERKEY,
    OCSPREQUESTASN_IDX_TBS_REQ_SERIAL,
    OCSPREQUESTASN_IDX_TBS_REQEXT,
    OCSPREQUESTASN_IDX_OPT_SIG,
};

/* Number of items in ASN.1 template for OCSPRequest. */
#define ocspRequestASN_Length (sizeof(ocspRequestASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size)
{
    DECL_ASNSETDATA(dataASN, ocspRequestASN_Length);
    word32 extSz = 0;
    word32 sz = 0;
    int ret = 0;
    word32 keyIdSz;

    WOLFSSL_ENTER("EncodeOcspRequest");

    CALLOC_ASNSETDATA(dataASN, ocspRequestASN_Length, ret, req->heap);

    if (ret == 0) {
        int digestSz = wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg));
        if (digestSz <= 0)
            ret = BAD_FUNC_ARG;
        else
            keyIdSz = (word32)digestSz;
    }
    if (ret == 0) {
        /* Set OID of hash algorithm use on issuer and key. */
        SetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], req->hashAlg,
                oidHashType);
        /* Set issuer, issuer key hash and serial number of certificate being
         * checked. */
        SetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUER],
                req->issuerHash, keyIdSz);
        SetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUERKEY],
                req->issuerKeyHash, keyIdSz);
        SetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_SERIAL],
                req->serial, (word32)req->serialSz);
        /* Only extension to write is nonce - check if one to encode. */
        if (req->nonceSz) {
            /* Get size of extensions and leave space for them in encoding. */
            ret = (int)(extSz = EncodeOcspRequestExtensions(req, NULL, 0));
            SetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT], NULL, extSz);
            if (ret > 0) {
                ret = 0;
            }
        }
        else {
            /* Don't write out extensions. */
            dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].noOut = 1;
        }
        /* Don't write out signature. */
        dataASN[OCSPREQUESTASN_IDX_OPT_SIG].noOut = 1;
    }
    if (ret == 0) {
        /* Calculate size of encoding. */
        ret = SizeASN_Items(ocspRequestASN, dataASN, ocspRequestASN_Length,
                &sz);
    }
    /* Check buffer big enough for encoding if supplied. */
    if ((ret == 0) && (output != NULL) && (sz > size)) {
        ret = BUFFER_E;
    }
    if ((ret == 0) && (output != NULL)) {
        /* Encode OCSPRequest. */
        SetASN_Items(ocspRequestASN, dataASN, ocspRequestASN_Length, output);
        if (req->nonceSz) {
            /* Encode extensions into space provided. */
            ret = (int)EncodeOcspRequestExtensions(req,
                (byte*)dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.buffer.data,
                extSz);
            if (ret > 0) {
                ret = 0;
            }
        }
    }

    if (ret == 0) {
        /* Return size of encoding. */
        ret = (int)sz;
    }

    FREE_ASNSETDATA(dataASN, req->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef HAVE_OCSP_RESPONDER

#ifdef WOLFSSL_ASN_TEMPLATE
/* Decode OCSP request extensions.
 * RFC 6960, 4.1.1 - ASN.1 Specification of the OCSP Request
 *
 * @param [in]      source  Buffer containing encoded extensions (inside [2]).
 * @param [in]      sz      Length of buffer in bytes.
 * @param [in, out] req     OCSP request object to store nonce.
 * @return  0 on success.
 * @return  ASN_PARSE_E when data is malformed.
 */
static int DecodeOcspReqExtensions(const byte* source, word32 sz,
                                   OcspRequest* req)
{
    DECL_ASNGETDATA(dataASN, certExtASN_Length);
    int ret = 0;
    word32 idx = 0;
    word32 maxIdx;
    int seqLen;

    WOLFSSL_ENTER("DecodeOcspReqExtensions");

    /* Skip the outer SEQUENCE that wraps all extensions */
    if (GetSequence(source, &idx, &seqLen, sz) < 0) {
        return ASN_PARSE_E;
    }
    maxIdx = idx + (word32)seqLen;

    CALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, req->heap);

    /* Step through all extensions. */
    while ((ret == 0) && (idx < maxIdx)) {
        byte isCrit = 0;
        /* Clear dynamic data, set OID type to expect. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
        GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidOcspType);
        GetASN_Boolean(&dataASN[CERTEXTASN_IDX_CRIT], &isCrit);
        /* Decode extension. */
        ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0,
                           source, &idx, sz);
        if (ret == 0) {
            word32 oid = dataASN[CERTEXTASN_IDX_OID].data.oid.sum;
            int length = (int)dataASN[CERTEXTASN_IDX_VAL].length;

            if (oid == OCSP_NONCE_OID) {
                /* Parse inner OCTET STRING from the extension value buffer
                 * using a local index to avoid relying on the outer idx. */
                const byte* extData = dataASN[CERTEXTASN_IDX_VAL].data.ref.data;
                word32 extDataSz = dataASN[CERTEXTASN_IDX_VAL].data.ref.length;
                word32 localIdx = 0;
                int innerLen = (int)extDataSz;

                ret = GetOctetString(extData, &localIdx, &innerLen, extDataSz);
                if (ret >= 0) {
                    ret = 0;
                    if (innerLen <= (int)sizeof(req->nonce)) {
                        XMEMCPY(req->nonce, extData + localIdx, (size_t)innerLen);
                        req->nonceSz = innerLen;
                    }
                    else {
                        /* Nonce too large */
                        ret = BUFFER_E;
                    }
                }
            }
            else if (isCrit) {
                /* Unknown critical extension - fail */
                ret = ASN_PARSE_E;
            }
            /* Ignore all other extension types. */

            /* Skip over rest of extension. */
            idx += (word32)length;
        }
    }

    /* Ensure all extension data was consumed */
    if (ret == 0 && idx != maxIdx) {
        ret = ASN_PARSE_E;
    }

    FREE_ASNGETDATA(dataASN, req->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

#ifdef WOLFSSL_ASN_TEMPLATE
int DecodeOcspRequest(OcspRequest* req, const byte* input, word32 size)
{
    /* TODO: support multiple Requested in a requestList */
    DECL_ASNGETDATA(dataASN, ocspRequestASN_Length);
    int ret = 0;
    word32 idx = 0;
    word32 issuerHashSz = sizeof(req->issuerHash);
    word32 issuerKeyHashSz = sizeof(req->issuerKeyHash);
    const byte* serial = NULL;
    word32 serialSz = 0;

    WOLFSSL_ENTER("DecodeOcspRequest");

    CALLOC_ASNGETDATA(dataASN, ocspRequestASN_Length, ret, req->heap);

    if (ret == 0) {
        GetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], oidHashType);
        GetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUER],
                req->issuerHash, &issuerHashSz);
        GetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUERKEY],
                req->issuerKeyHash, &issuerKeyHashSz);
        ret = GetASN_Items(ocspRequestASN, dataASN, ocspRequestASN_Length, 1,
            input, &idx, size);
    }
    if (ret == 0) {
        /* Make sure all input was consumed */
        if (idx != size)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Store hash algorithm from the request */
        int digestSz;
        req->hashAlg = dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID].data.oid.sum;
        digestSz = wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg));
        if (digestSz <= 0)
            ret = ASN_SIG_HASH_E;
        else if ((int)issuerHashSz != digestSz ||
                 (int)issuerKeyHashSz != digestSz)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Parse optional extensions */
        if (dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.data != NULL) {
            ret = DecodeOcspReqExtensions(
                dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.data,
                dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.length,
                req);
        }
    }
    if (ret == 0) {
        /* Optional signature not supported yet */
        if (dataASN[OCSPREQUESTASN_IDX_OPT_SIG].length > 0 ||
                dataASN[OCSPREQUESTASN_IDX_OPT_SIG].data.ref.data != NULL)
            ret = NOT_COMPILED_IN;
    }
    if (ret == 0) {
        GetASN_GetRef(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_SERIAL],
                &serial, &serialSz);
        if (serialSz == 0 || serial == NULL || serialSz > EXTERNAL_SERIAL_SIZE)
            ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Copy serial number */
        req->serial = (byte*)XMALLOC((size_t)serialSz, req->heap,
                                     DYNAMIC_TYPE_OCSP_REQUEST);
        if (req->serial == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMCPY(req->serial, serial, (size_t)serialSz);
            req->serialSz = serialSz;
        }
    }

    FREE_ASNGETDATA(dataASN, req->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* HAVE_OCSP_RESPONDER */


int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce,
                                                                     void* heap)
{
    int ret;

    WOLFSSL_ENTER("InitOcspRequest");

    if (req == NULL)
        return BAD_FUNC_ARG;

    XMEMSET(req, 0, sizeof(OcspRequest));
    req->heap = heap;

#ifdef NO_SHA
    req->hashAlg = SHA256h;
#else
    req->hashAlg = SHAh;
#endif

    if (cert) {
        XMEMCPY(req->issuerHash,    cert->issuerHash,    KEYID_SIZE);
        XMEMCPY(req->issuerKeyHash, cert->issuerKeyHash, KEYID_SIZE);

        req->serial = (byte*)XMALLOC((size_t)cert->serialSz, req->heap,
                                                     DYNAMIC_TYPE_OCSP_REQUEST);
        if (req->serial == NULL)
            return MEMORY_E;

        XMEMCPY(req->serial, cert->serial, (size_t)cert->serialSz);
        req->serialSz = cert->serialSz;

        if (cert->extAuthInfoSz != 0 && cert->extAuthInfo != NULL) {
            req->url = (byte*)XMALLOC((size_t)cert->extAuthInfoSz + 1,
                                          req->heap, DYNAMIC_TYPE_OCSP_REQUEST);
            if (req->url == NULL) {
                XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP);
                req->serial = NULL;
                return MEMORY_E;
            }

            XMEMCPY(req->url, cert->extAuthInfo, (size_t)cert->extAuthInfoSz);
            req->urlSz = cert->extAuthInfoSz;
            req->url[req->urlSz] = 0;
        }
    }

    if (useNonce) {
        WC_RNG rng;

    #ifndef HAVE_FIPS
        ret = wc_InitRng_ex(&rng, req->heap, INVALID_DEVID);
    #else
        ret = wc_InitRng(&rng);
    #endif
        if (ret != 0) {
            WOLFSSL_MSG("\tCannot initialize RNG. Skipping the OCSP Nonce.");
        } else {
            if (wc_RNG_GenerateBlock(&rng, req->nonce, MAX_OCSP_NONCE_SZ) != 0)
                WOLFSSL_MSG("\tCannot run RNG. Skipping the OCSP Nonce.");
            else
                req->nonceSz = MAX_OCSP_NONCE_SZ;

            wc_FreeRng(&rng);
        }
    }

    return 0;
}

void FreeOcspRequest(OcspRequest* req)
{
    WOLFSSL_ENTER("FreeOcspRequest");

    if (req) {
        XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP_REQUEST);
        req->serial = NULL;

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

        XFREE(req->url, req->heap, DYNAMIC_TYPE_OCSP_REQUEST);
        req->url = NULL;

#ifdef OPENSSL_EXTRA
        if (req->cid != NULL)
            wolfSSL_OCSP_CERTID_free((WOLFSSL_OCSP_CERTID*)req->cid);
        req->cid = NULL;
#endif
    }
}


int CompareOcspReqResp(OcspRequest* req, OcspResponse* resp)
{
    int cmp = -1; /* default as not matching, cmp gets set on each check */
    int ocspDigestSize;
    OcspEntry *single, *next, *prev = NULL, *top;

    WOLFSSL_ENTER("CompareOcspReqResp");

    if (req == NULL) {
        WOLFSSL_MSG("\tReq missing");
        return -1;
    }
    if (resp == NULL || resp->single == NULL) {
        WOLFSSL_MSG("\tResp missing");
        return 1;
    }

    /* Nonces are not critical. The responder may not necessarily add
     * the nonce to the response. */
    if (req->nonceSz && resp->nonce != NULL
#ifndef WOLFSSL_FORCE_OCSP_NONCE_CHECK
            && resp->nonceSz != 0
#endif
    ) {
        cmp = req->nonceSz - resp->nonceSz;
        if (cmp != 0) {
            WOLFSSL_MSG("\tnonceSz mismatch");
            return cmp;
        }

        cmp = XMEMCMP(req->nonce, resp->nonce, (size_t)req->nonceSz);
        if (cmp != 0) {
            WOLFSSL_MSG("\tnonce mismatch");
            return cmp;
        }
    }

    /* match based on found status and return */
    for (single = resp->single; single; single = next) {
        ocspDigestSize = wc_HashGetDigestSize(
            wc_OidGetHash(single->hashAlgoOID));
        if (ocspDigestSize <= 0) {
            WOLFSSL_MSG("\tinvalid hash algorithm in response");
            return -1;
        }
        cmp = req->serialSz - single->status->serialSz;
        if (cmp == 0) {
            cmp = XMEMCMP(req->serial, single->status->serial,
                                                          (size_t)req->serialSz)
               || XMEMCMP(req->issuerHash, single->issuerHash,
                                                         (size_t)ocspDigestSize)
               || XMEMCMP(req->issuerKeyHash, single->issuerKeyHash,
                                                        (size_t)ocspDigestSize);
            if (cmp == 0) {
                /* match found */
                if (resp->single != single && prev) {
                    /* move to top of list */
                    top = resp->single;
                    resp->single = single;
                    prev->next = single->next;
                    single->next = top;
                }
                break;
            }
        }
        next = single->next;
        prev = single;
    }

    if (cmp != 0) {
        WOLFSSL_MSG("\trequest and response mismatch");
        return cmp;
    }

    return 0;
}

#endif /* HAVE_OCSP */


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for certificate name hash. */
static const ASNItem nameHashASN[] = {
/* OID  */ { 0, ASN_OBJECT_ID, 0, 0, 1 },
/* NAME */ { 0, ASN_SEQUENCE, 1, 0, 0 },
};
enum {
    NAMEHASHASN_IDX_OID = 0,
    NAMEHASHASN_IDX_NAME
};

/* Number of items in ASN.1 template for certificate name hash. */
#define nameHashASN_Length (sizeof(nameHashASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */

/* store WC_SHA hash of NAME */
int GetNameHash(const byte* source, word32* idx, byte* hash, int maxIdx)
{
    /* Use summy signature OID. */
    return GetNameHash_ex(source, idx, hash, maxIdx, 0);
}

/* store WC_SHA hash of NAME */
#ifdef WOLFSSL_ASN_TEMPLATE
int GetNameHash_ex(const byte* source, word32* idx, byte* hash, int maxIdx,
    word32 sigOID)
{
    ASNGetData dataASN[nameHashASN_Length];
    int ret;

    XMEMSET(dataASN, 0, sizeof(dataASN));
    /* Ignore the OID even when present. */
    GetASN_OID(&dataASN[NAMEHASHASN_IDX_OID], oidIgnoreType);
    /* Decode certificate name. */
    ret = GetASN_Items(nameHashASN, dataASN, nameHashASN_Length, 0, source, idx,
           (word32)maxIdx);
    if (ret == 0) {
        /* 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. */
        /* Calculate hash of complete name including SEQUENCE. */
        ret = CalcHashId_ex(
                GetASNItem_Addr(dataASN[NAMEHASHASN_IDX_NAME], source),
                GetASNItem_Length(dataASN[NAMEHASHASN_IDX_NAME], source),
                hash, HashIdAlg(sigOID));
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#if defined(HAVE_CRL) && !defined(WOLFCRYPT_ONLY)

#ifdef OPENSSL_EXTRA
static char* GetNameFromDer(const byte* source, int sz)
{
    char* out;

    out = (char*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_OPENSSL);
    if (out == NULL) {
        WOLFSSL_MSG("Name malloc failed");
        return NULL;
    }

    XMEMCPY(out, source, (size_t)sz);

    return out;
}
#endif

/* initialize decoded CRL */
void InitDecodedCRL(DecodedCRL* dcrl, void* heap)
{
    WOLFSSL_MSG("InitDecodedCRL");

    XMEMSET(dcrl, 0, sizeof(DecodedCRL));
    dcrl->heap = heap;
#ifdef WOLFSSL_HEAP_TEST
    dcrl->heap = (void*)WOLFSSL_HEAP_TEST;
#endif
}


/* free decoded CRL resources */
void FreeDecodedCRL(DecodedCRL* dcrl)
{
    RevokedCert* tmp = dcrl->certs;

    WOLFSSL_MSG("FreeDecodedCRL");

    while(tmp) {
        RevokedCert* next = tmp->next;
#if defined(OPENSSL_EXTRA)
        XFREE(tmp->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
#endif
        XFREE(tmp, dcrl->heap, DYNAMIC_TYPE_REVOKED);
        tmp = next;
    }
#ifdef OPENSSL_EXTRA
    XFREE(dcrl->issuer, NULL, DYNAMIC_TYPE_OPENSSL);
#endif
}


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for revoked certificates.
 * X.509: RFC 5280, 5.1 - CRL Fields
 */
static const ASNItem revokedASN[] = {
/* SEQ      */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                     /* userCertificate    CertificateSerialNumber */
/* CERT     */        { 1, ASN_INTEGER, 0, 0, 0 },
                                     /* revocationDate     Time */
/* TIME_UTC */        { 1, ASN_UTC_TIME, 0, 0, 2 },
/* TIME_GT  */        { 1, ASN_GENERALIZED_TIME, 0, 0, 2 },
                                     /* crlEntryExensions  Extensions */
/* TIME_EXT */        { 1, ASN_SEQUENCE, 1, 0, 1 },
};
enum {
    REVOKEDASN_IDX_SEQ = 0,
    REVOKEDASN_IDX_CERT,
    REVOKEDASN_IDX_TIME_UTC,
    REVOKEDASN_IDX_TIME_GT,
    REVOKEDASN_IDX_TIME_EXT,
};

/* Number of items in ASN.1 template for revoked certificates. */
#define revokedASN_Length (sizeof(revokedASN) / sizeof(ASNItem))
#endif

/* CRL Reason Code OID: 2.5.29.21 */
static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };

/* Parse CRL entry extensions.
 * Extracts the reason code into *reasonCode if the CRL Reason extension
 * is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
 * unknown extension is marked critical. Returns 0 on success. */
static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
                                    int* reasonCode)
{
    while (idx < maxIdx) {
        int len;
        int oidLen;
        word32 end;
        word32 localIdx;
        word32 oidContent;
        byte tag;
        int critical = 0;
        int isReasonOid = 0;

        /* Each extension is a SEQUENCE */
        if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
            break;
        }
        end = idx + (word32)len;

        /* Parse OID: tag, length (short or long form), content */
        if (GetASNTag(buff, &idx, &tag, end) < 0 ||
                tag != ASN_OBJECT_ID) {
            break;
        }
        if (GetLength(buff, &idx, &oidLen, end) < 0) {
            break;
        }
        oidContent = idx;
        if (idx + (word32)oidLen > end) {
            break;
        }

        /* Check if it's the CRL Reason OID: 2.5.29.21 */
        if ((word32)oidLen == sizeof(crlReasonOid) &&
                XMEMCMP(buff + oidContent, crlReasonOid,
                        sizeof(crlReasonOid)) == 0) {
            isReasonOid = 1;
        }
        idx = oidContent + (word32)oidLen;

        /* Parse optional critical BOOLEAN */
        localIdx = idx;
        if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
                tag == ASN_BOOLEAN) {
            int ret = GetBoolean(buff, &idx, end);
            if (ret < 0) {
                break;
            }
            critical = ret;
        }

        if (isReasonOid) {
            /* Get OCTET STRING wrapping the ENUMERATED */
            if (GetOctetString(buff, &idx, &len, end) >= 0) {
                /* Parse ENUMERATED reason value */
                localIdx = idx;
                if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
                        tag == ASN_ENUMERATED) {
                    int reasonLen;
                    idx = localIdx;
                    if (GetLength(buff, &idx, &reasonLen, end) >= 0 &&
                            reasonLen == 1) {
                        *reasonCode = (int)buff[idx];
                    }
                }
            }
        }
        else if (critical) {
            /* RFC 5280 Section 5.3: reject CRL with unknown critical
             * entry extension. */
            WOLFSSL_MSG("Unknown critical CRL entry extension");
            return ASN_CRIT_EXT_E;
        }
        idx = end;
    }
    return 0;
}

#ifdef HAVE_CRL
/* Test-visible helper: parse reasonCode from encoded Extension list bytes. */
WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
                                                     word32 extSz,
                                                     int* reasonCode)
{
    if (ext == NULL || reasonCode == NULL) {
        return BAD_FUNC_ARG;
    }

    return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
}
#endif

/* Get Revoked Cert list, 0 on success */
#ifdef WOLFSSL_ASN_TEMPLATE
static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
                      DecodedCRL* dcrl, word32 maxIdx)
{
    DECL_ASNGETDATA(dataASN, revokedASN_Length);
    int ret = 0;
    word32 serialSz = EXTERNAL_SERIAL_SIZE;
    word32 revDateSz = MAX_DATE_SIZE;
    RevokedCert* rc;
#ifdef CRL_STATIC_REVOKED_LIST
    int totalCerts = dcrl->totalCerts;

    if (totalCerts >= CRL_MAX_REVOKED_CERTS) {
        return MEMORY_E;
    }

    rc = &rcert[totalCerts];

#else
    /* Allocate a new revoked certificate object. */
    rc = (RevokedCert*)XMALLOC(sizeof(RevokedCert), dcrl->heap,
            DYNAMIC_TYPE_CRL);
    if (rc == NULL) {
        ret = MEMORY_E;
    }
    if (ret == 0) {
        XMEMSET(rc, 0, sizeof(RevokedCert));
    }
#endif /* CRL_STATIC_REVOKED_LIST */

    CALLOC_ASNGETDATA(dataASN, revokedASN_Length, ret, dcrl->heap);

    if (ret == 0) {
        /* Set buffer to place serial number into. */
        GetASN_Buffer(&dataASN[REVOKEDASN_IDX_CERT], rc->serialNumber,
                &serialSz);
        /* Set buffer to store revocation date. */
        GetASN_Buffer(&dataASN[REVOKEDASN_IDX_TIME_UTC], rc->revDate,
                &revDateSz);
        GetASN_Buffer(&dataASN[REVOKEDASN_IDX_TIME_GT], rc->revDate,
                &revDateSz);
        /* Decode the Revoked */
        ret = GetASN_Items(revokedASN, dataASN, revokedASN_Length, 1, buff, idx,
                maxIdx);
    }
    if (ret == 0) {
        /* Store size of serial number. */
        rc->serialSz = (int)serialSz;
        rc->revDateFormat = (dataASN[REVOKEDASN_IDX_TIME_UTC].tag != 0)
                ? dataASN[REVOKEDASN_IDX_TIME_UTC].tag
                : dataASN[REVOKEDASN_IDX_TIME_GT].tag;

        /* Initialize reason code to absent */
        rc->reasonCode = -1;

        /* Parse CRL entry extensions (v2 only) */
        if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
            word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
            word32 extTagEnd = extOff +
                    dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
            int extLen;

            /* .offset points at the outer SEQUENCE tag. Re-parse the
             * SEQUENCE header to locate the content start (list of
             * Extension SEQUENCEs), which handles long-form length.
             * extTagEnd adds 6 to cover the worst-case tag+long-form-length
             * header for the outer SEQUENCE. */
            if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
                ret = ASN_PARSE_E;
            }
            else {
                word32 extEnd = extOff + (word32)extLen;

#if defined(OPENSSL_EXTRA)
                /* Store raw DER of extension contents for OpenSSL compat. */
                rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
                                                DYNAMIC_TYPE_REVOKED);
                if (rc->extensions != NULL) {
                    XMEMCPY(rc->extensions, buff + extOff, (size_t)extLen);
                    rc->extensionsSz = (word32)extLen;
                }
#endif

                ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
                    &rc->reasonCode);
            }
        }

        if (ret == 0) {
            /* Add revoked certificate to chain. */
#ifndef CRL_STATIC_REVOKED_LIST
            rc->next = dcrl->certs;
            dcrl->certs = rc;
#endif
            dcrl->totalCerts++;
        }
    }

    FREE_ASNGETDATA(dataASN, dcrl->heap);
    if ((ret != 0) && (rc != NULL)) {
#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_CRL);
#endif
    }
#ifndef CRL_STATIC_REVOKED_LIST
    (void)rcert;
#endif
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_ASN_TEMPLATE
/* Parse the revoked certificates of a CRL.
 *
 * @param [in] dcrl    Decoded CRL object.
 * @param [in] buff    Buffer holding CRL.
 * @param [in] idx     Index into buffer of revoked certificates.
 * @param [in] maxIdx  Maximum index of revoked cartificates data.
 * @return  0 on success.
 * @return  ASN_PARSE_E on failure.
 */
static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
                                 const byte* buff, word32 idx, word32 maxIdx)
{
    int ret = 0;

    /* Parse each revoked certificate. */
    while ((ret == 0) && (idx < maxIdx)) {
        /* Parse a revoked certificate. */
        int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
        if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
            /* Preserve the specific error so callers can distinguish a
             * rejected critical extension from a generic parse failure. */
            ret = r;
        }
        else if (r < 0) {
            ret = ASN_PARSE_E;
        }
    }

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */

int VerifyCRL_Signature(SignatureCtx* sigCtx, const byte* toBeSigned,
                        word32 tbsSz, const byte* signature, word32 sigSz,
                        word32 signatureOID, const byte* sigParams,
                        int sigParamsSz, Signer *ca, void* heap)
{
    /* try to confirm/verify signature */
#ifndef IGNORE_KEY_EXTENSIONS
    if ((ca->keyUsage & KEYUSE_CRL_SIGN) == 0) {
        WOLFSSL_MSG("CA cannot sign CRLs");
        WOLFSSL_ERROR_VERBOSE(ASN_CRL_NO_SIGNER_E);
        return ASN_CRL_NO_SIGNER_E;
    }
#endif /* IGNORE_KEY_EXTENSIONS */

    InitSignatureCtx(sigCtx, heap, INVALID_DEVID);
    if (ConfirmSignature(sigCtx, toBeSigned, tbsSz, ca->publicKey,
                         ca->pubKeySize, ca->keyOID, signature, sigSz,
                         signatureOID, sigParams, (word32)sigParamsSz, NULL) != 0) {
        WOLFSSL_MSG("CRL Confirm signature failed");
        WOLFSSL_ERROR_VERBOSE(ASN_CRL_CONFIRM_E);
        return ASN_CRL_CONFIRM_E;
    }

    return 0;
}

#ifdef WOLFSSL_ASN_TEMPLATE
/* Find the signer for the CRL and verify the signature.
 *
 * @param [in] dcrl  Decoded CRL object.
 * @param [in] buff  Buffer holding CRL.
 * @param [in] cm    Certificate manager object.
 * @return  0 on success.
 * @return  ASN_CRL_NO_SIGNER_E when no signer found.
 * @return  ASN_CRL_CONFIRM_E when signature did not verify.
 */
static int PaseCRL_CheckSignature(DecodedCRL* dcrl, const byte* sigParams,
    int sigParamsSz, const byte* buff, void* cm)
{
    int ret = 0;
    Signer* ca = NULL;
    SignatureCtx sigCtx;

    /* 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) {
        /* more unique than issuerHash */
        ca = GetCA(cm, dcrl->extAuthKeyId);
    }
    /* Check issuerHash matched CA's subjectNameHash. */
    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);
    }

    if (ret == 0) {
        WOLFSSL_MSG("Found CRL issuer CA");
        /* Verify CRL signature with CA. */
        ret = VerifyCRL_Signature(&sigCtx, buff + dcrl->certBegin,
           dcrl->sigIndex - dcrl->certBegin, dcrl->signature, dcrl->sigLength,
           dcrl->signatureOID, sigParams, sigParamsSz, ca, dcrl->heap);
    }

    return ret;
}
#endif


#ifndef NO_SKID
#ifdef WOLFSSL_ASN_TEMPLATE
static int ParseCRL_AuthKeyIdExt(const byte* input, int sz, DecodedCRL* dcrl)
{
    DECL_ASNGETDATA(dataASN, authKeyIdASN_Length);
    int ret = 0;
    word32 idx = 0;

    WOLFSSL_ENTER("ParseCRL_AuthKeyIdExt");

    CALLOC_ASNGETDATA(dataASN, authKeyIdASN_Length, ret, dcrl->heap);

    if (ret == 0) {
        /* Parse an authority key identifier. */
        ret = GetASN_Items(authKeyIdASN, dataASN, authKeyIdASN_Length, 1, input,
                           &idx, (word32)sz);
    }
    if (ret == 0) {
        /* Key id is optional. */
        if (dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.data == NULL) {
            WOLFSSL_MSG("\tinfo: OPTIONAL item 0, not available");
        }
        else {
            dcrl->extAuthKeyIdSet = 1;

            /* Get the hash or hash of the hash if wrong size. */
            ret = GetHashId(dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.data,
                (int)dataASN[AUTHKEYIDASN_IDX_KEYID].data.ref.length,
                dcrl->extAuthKeyId, HashIdAlg(dcrl->signatureOID));
        }
    }

    FREE_ASNGETDATA(dataASN, dcrl->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
/* Parse the extensions of a CRL.
 *
 * @param [in] dcrl    Decoded CRL object.
 * @param [in] buff    Buffer holding CRL.
 * @param [in] idx     Index into buffer of extensions.
 * @param [in] maxIdx  Maximum index of extension data.
 * @return  0 on success.
 * @return  ASN_PARSE_E on failure.
 */
static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32 idx,
        word32 maxIdx)
{
    DECL_ASNGETDATA(dataASN, certExtASN_Length);
    int ret = 0;
    /* Track if we've seen these extensions already */
    word32 seenAuthKey = 0;
    word32 seenCrlNum = 0;

    ALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, dcrl->heap);

    while ((ret == 0) && (idx < maxIdx)) {
        byte critical = 0;

        /* Clear dynamic data. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
        /* Ensure OID is an extension type. */
        GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidCertExtType);
        /* Set criticality variable. */
        GetASN_Int8Bit(&dataASN[CERTEXTASN_IDX_CRIT], &critical);
        /* Parse extension wrapper. */
        ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0, buf, &idx,
                maxIdx);
        if (ret == 0) {
            word32 localIdx = idx;
            /* OID in extension. */
            word32 oid = dataASN[CERTEXTASN_IDX_OID].data.oid.sum;
            /* Length of extension data. */
            int length = (int)dataASN[CERTEXTASN_IDX_VAL].length;

            /* Check for duplicate extension. RFC 5280 Section 4.2 states that
             * a certificate must not include more than one instance of a
             * particular extension. Note that the same guidance does not appear
             * for CRLs but the same reasoning should apply. */
            if ((oid == AUTH_KEY_OID && seenAuthKey) ||
                (oid == CRL_NUMBER_OID && seenCrlNum)) {
                WOLFSSL_MSG("Duplicate CRL extension found");
                /* Gating !WOLFSSL_NO_ASN_STRICT will allow wolfCLU to have same
                 * behaviour as OpenSSL */
#ifndef WOLFSSL_NO_ASN_STRICT
                ret = ASN_PARSE_E;
#endif
            }

            /* Track this extension if no duplicate found */
            if (ret == 0) {
                if (oid == AUTH_KEY_OID)
                    seenAuthKey = 1;
                else if (oid == CRL_NUMBER_OID)
                    seenCrlNum = 1;
            }

            if (ret == 0) {
                if (oid == AUTH_KEY_OID) {
                #ifndef NO_SKID
                    /* Parse Authority Key Id extension.
                     * idx is at start of OCTET_STRING data. */
                    ret = ParseCRL_AuthKeyIdExt(buf + localIdx, length, dcrl);
                    if (ret != 0) {
                        WOLFSSL_MSG("\tcouldn't parse AuthKeyId extension");
                    }
                #endif
                }
                else if (oid == CRL_NUMBER_OID) {
                    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 && (INIT_MP_INT_SIZE(m, CRL_MAX_NUM_SZ *
                        CHAR_BIT) != MP_OKAY)) {
                        ret = MP_INIT_E;
                    }

                    if (ret == 0) {
                        int crlNumLen = 0;
                        word32 rawIdx = localIdx;
                        word32 tmpIdx = localIdx;
                        ret = GetASNInt(buf, &tmpIdx, &crlNumLen, maxIdx);
                        if (ret == 0 && (crlNumLen > CRL_MAX_NUM_SZ)) {
                            WOLFSSL_MSG("CRL number exceeds limitation");
                            ret = BUFFER_E;
                        }
                        /* 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). A leading
                         * 0x00 pad means the value is positive. */
                        if (ret == 0) {
                            int rawLen = 0;
                            (void)GetASNHeader(buf, ASN_INTEGER,
                                &rawIdx, &rawLen, maxIdx);
                            if (rawLen > 0 && (buf[rawIdx] & 0x80) != 0) {
                                WOLFSSL_MSG("CRL number is negative");
                                ret = ASN_PARSE_E;
                            }
                        }
                        if (ret == 0) {
                            ret = GetInt(m, buf, &localIdx, maxIdx);
                        }
                    }
                    if (ret == 0 && mp_toradix(m, (char*)dcrl->crlNumber,
                                 MP_RADIX_HEX) != MP_OKAY)
                        ret = BUFFER_E;

                    if (ret == 0) {
                        dcrl->crlNumberSet = 1;
                    }

                    mp_free(m);
                    FREE_MP_INT_SIZE(m, NULL, DYNAMIC_TYPE_TMP_BUFFER);
                }
                else if (critical) {
                    WOLFSSL_MSG("Unknown critical CRL extension");
                    ret = ASN_CRIT_EXT_E;
                }
            }
            /* Move index on to next extension. */
            idx += (word32)length;
        }
    }

    if (ret < 0 && ret != WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
        ret = ASN_PARSE_E;
    }

    FREE_ASNGETDATA(dataASN, dcrl->heap);

    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */


#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN.1 template for a CRL- CertificateList.
 * X.509: RFC 5280, 5.1 - CRL Fields
 */
static const ASNItem crlASN[] = {
                                       /* CertificateList */
/* SEQ                */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                                           /* tbsCertList */
/* TBS                */        { 1, ASN_SEQUENCE, 1, 1, 0 },
                                               /* version     Version OPTIONAL if present must be v2 */
/* TBS_VER            */            { 2, ASN_INTEGER, 0, 0, 1 },
                                               /* signature */
/* TBS_SIGALGO        */            { 2, ASN_SEQUENCE, 1, 1, 0 },
/* TBS_SIGALGO_OID    */                { 3, ASN_OBJECT_ID, 0, 0, 0 },
/* TBS_SIGALGO_NULL   */                { 3, ASN_TAG_NULL, 0, 0, 1 },
                                               /* issuer */
#ifdef WC_RSA_PSS
/* TBS_SIGALGO_P_SEQ  */                { 3, ASN_SEQUENCE, 1, 0, 2 },
#endif
/* TBS_ISSUER         */            { 2, ASN_SEQUENCE, 1, 0, 0 },
                                               /* thisUpdate */
/* TBS_THISUPDATE_UTC */            { 2, ASN_UTC_TIME, 0, 0, 2 },
/* TBS_THISUPDATE_GT  */            { 2, ASN_GENERALIZED_TIME, 0, 0, 2 },
                                               /* nextUpdate */
/* TBS_NEXTUPDATE_UTC */            { 2, ASN_UTC_TIME, 0, 0, 3 },
/* TBS_NEXTUPDATE_GT  */            { 2, ASN_GENERALIZED_TIME, 0, 0, 3 },
                                               /* revokedCertificates */
/* TBS_REVOKEDCERTS   */            { 2, ASN_SEQUENCE, 1, 0, 1 },
                                               /* crlExtensions */
/* TBS_EXT            */            { 2, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 },
/* TBS_EXT_SEQ        */                { 3, ASN_SEQUENCE, 1, 0, 0 },
                                           /* signatureAlgorithm */
/* SIGALGO            */        { 1, ASN_SEQUENCE, 1, 1, 0 },
/* SIGALGO_OID        */            { 2, ASN_OBJECT_ID, 0, 0, 0 },
/* SIGALGO_NULL       */            { 2, ASN_TAG_NULL, 0, 0, 1 },
#ifdef WC_RSA_PSS
/* SIGALGO_PARAMS     */            { 2, ASN_SEQUENCE, 1, 0, 2 },
#endif
                                           /* signatureValue */
/* SIGNATURE          */        { 1, ASN_BIT_STRING, 0, 0, 0 },
};
enum {
    CRLASN_IDX_SEQ = 0,
    CRLASN_IDX_TBS,
    CRLASN_IDX_TBS_VER,
    CRLASN_IDX_TBS_SIGALGO,
    CRLASN_IDX_TBS_SIGALGO_OID,
    CRLASN_IDX_TBS_SIGALGO_NULL,
#ifdef WC_RSA_PSS
    CRLASN_IDX_TBS_SIGALGO_PARAMS,
#endif
    CRLASN_IDX_TBS_ISSUER,
    CRLASN_IDX_TBS_THISUPDATE_UTC,
    CRLASN_IDX_TBS_THISUPDATE_GT,
    CRLASN_IDX_TBS_NEXTUPDATE_UTC,
    CRLASN_IDX_TBS_NEXTUPDATE_GT,
    CRLASN_IDX_TBS_REVOKEDCERTS,
    CRLASN_IDX_TBS_EXT,
    CRLASN_IDX_TBS_EXT_SEQ,
    CRLASN_IDX_SIGALGO,
    CRLASN_IDX_SIGALGO_OID,
    CRLASN_IDX_SIGALGO_NULL,
#ifdef WC_RSA_PSS
    CRLASN_IDX_SIGALGO_PARAMS,
#endif
    CRLASN_IDX_SIGNATURE,
};

/* Number of items in ASN.1 template for a CRL- CertificateList. */
#define crlASN_Length (sizeof(crlASN) / sizeof(ASNItem))
#endif

/* parse crl buffer into decoded state, 0 on success */
#ifdef WOLFSSL_ASN_TEMPLATE
int ParseCRL(RevokedCert* rcert, DecodedCRL* dcrl, const byte* buff, word32 sz,
             int verify, void* cm)
{
    DECL_ASNGETDATA(dataASN, crlASN_Length);
    int ret = 0;
    /* Default version - v1 = 0 */
    byte version = 0;
    word32 idx = 0;
    /* Size of buffer for date. */
    word32 lastDateSz = MAX_DATE_SIZE;
    word32 nextDateSz = MAX_DATE_SIZE;
    const byte* sigParams = NULL;
    int   sigParamsSz = 0;
#ifdef WC_RSA_PSS
    const byte* tbsParams = NULL;
    int   tbsParamsSz = 0;
#endif

    /* When NO_ASN_TIME is defined, verify not used. */
    (void)verify;

    WOLFSSL_MSG("ParseCRL");

    CALLOC_ASNGETDATA(dataASN, crlASN_Length, ret, dcrl->heap);

    if (ret == 0) {
        /* Set variable to store version. */
        GetASN_Int8Bit(&dataASN[CRLASN_IDX_TBS_VER], &version);
        /* Set expecting signature OID. */
        GetASN_OID(&dataASN[CRLASN_IDX_TBS_SIGALGO_OID], oidSigType);
        /* Set buffer to put last and next date into. */
        GetASN_Buffer(&dataASN[CRLASN_IDX_TBS_THISUPDATE_UTC], dcrl->lastDate,
                &lastDateSz);
        GetASN_Buffer(&dataASN[CRLASN_IDX_TBS_THISUPDATE_GT], dcrl->lastDate,
                &lastDateSz);
        GetASN_Buffer(&dataASN[CRLASN_IDX_TBS_NEXTUPDATE_UTC], dcrl->nextDate,
                &nextDateSz);
        GetASN_Buffer(&dataASN[CRLASN_IDX_TBS_NEXTUPDATE_GT], dcrl->nextDate,
                &nextDateSz);
        /* Set expecting signature OID. */
        GetASN_OID(&dataASN[CRLASN_IDX_SIGALGO_OID], oidSigType);
        /* Decode the CRL. */
        ret = GetASN_Items(crlASN, dataASN, crlASN_Length, 1, buff, &idx, sz);
    }
    /* Version must be v2 = 1 if present. */
    if ((ret == 0) && (dataASN[CRLASN_IDX_TBS_VER].tag != 0) &&
            (version != 1)) {
        ret = ASN_PARSE_E;
    }
    /* Check minimum size of last date. */
    if ((ret == 0) && (lastDateSz < MIN_DATE_SIZE)) {
        ret = ASN_PARSE_E;
    }
    /* Check minimum size of next date. */
    if ((ret == 0) && (nextDateSz < MIN_DATE_SIZE)) {
        ret = ASN_PARSE_E;
    }
    /* 'signatureAlgorithm' OID must be the same as 'signature' OID. */
    if ((ret == 0) && (dataASN[CRLASN_IDX_SIGALGO_OID].data.oid.sum !=
            dataASN[CRLASN_IDX_TBS_SIGALGO_OID].data.oid.sum)) {
        ret = ASN_PARSE_E;
    }
    if (ret == 0) {
        /* Store version */
        dcrl->version = ++version;
        /* Store offset of to be signed part. */
        dcrl->certBegin = dataASN[CRLASN_IDX_TBS].offset;
        /* Store index of signature. */
        dcrl->sigIndex = dataASN[CRLASN_IDX_SIGALGO].offset;

    #ifdef WC_RSA_PSS
        /* get TBS and Signature parameters for PSS */
        if (dataASN[CRLASN_IDX_TBS_SIGALGO_PARAMS].tag != 0) {
            tbsParams =
                GetASNItem_Addr(dataASN[CRLASN_IDX_TBS_SIGALGO_PARAMS],
                    buff);
            tbsParamsSz =(int)
                GetASNItem_Length(dataASN[CRLASN_IDX_TBS_SIGALGO_PARAMS],
                    buff);
        }
        if (dataASN[CRLASN_IDX_SIGALGO_PARAMS].tag != 0) {
            sigParams =
                GetASNItem_Addr(dataASN[CRLASN_IDX_SIGALGO_PARAMS],
                    buff);
            sigParamsSz = (int)
                GetASNItem_Length(dataASN[CRLASN_IDX_SIGALGO_PARAMS],
                    buff);
            dcrl->sigParamsIndex =
                dataASN[CRLASN_IDX_SIGALGO_PARAMS].offset;
            dcrl->sigParamsLength = (word32)sigParamsSz;
        }
    #endif

        /* Store address and length of signature data. */
        GetASN_GetRef(&dataASN[CRLASN_IDX_SIGNATURE], &dcrl->signature,
                &dcrl->sigLength);
        /* Get the signature OID. */
        dcrl->signatureOID = dataASN[CRLASN_IDX_SIGALGO_OID].data.oid.sum;

    #ifdef WC_RSA_PSS
        /* Sanity check on parameters found */
        if (tbsParamsSz != sigParamsSz) {
            WOLFSSL_MSG("CRL TBS and signature parameter sizes mismatch");
            ret = ASN_PARSE_E;
        }
        else if ((tbsParamsSz > 0) &&
          (dataASN[CRLASN_IDX_TBS_SIGALGO_OID].data.oid.sum != CTC_RSASSAPSS)) {
            WOLFSSL_MSG("CRL unexpected signature parameters found");
            ret = ASN_PARSE_E;
        }
        else if ((tbsParamsSz > 0) &&
                 (XMEMCMP(tbsParams, sigParams, (word32)tbsParamsSz) != 0)) {
            WOLFSSL_MSG("CRL TBS and signature parameter mismatch");
            ret = ASN_PARSE_E;
        }
    #endif

        /* Get the format/tag of the last and next date. */
        dcrl->lastDateFormat = (dataASN[CRLASN_IDX_TBS_THISUPDATE_UTC].tag != 0)
                ? dataASN[CRLASN_IDX_TBS_THISUPDATE_UTC].tag
                : dataASN[CRLASN_IDX_TBS_THISUPDATE_GT].tag;
        dcrl->nextDateFormat = (dataASN[CRLASN_IDX_TBS_NEXTUPDATE_UTC].tag != 0)
                ? dataASN[CRLASN_IDX_TBS_NEXTUPDATE_UTC].tag
                : dataASN[CRLASN_IDX_TBS_NEXTUPDATE_GT].tag;
    #if !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_CRL_DATE_CHECK)
        if (dcrl->nextDateFormat != 0) {
            /* Next date was set, so validate it. */
            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");
                ret = CRL_CERT_DATE_ERR;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        }
    }
    if (ret == 0) { /* in "no time" cases above "ret" is not set */
    #endif /* !NO_ASN_TIME && !WOLFSSL_NO_CRL_DATE_CHECK */
    #ifdef OPENSSL_EXTRA
        /* Parse and store the issuer name. */
        dcrl->issuerSz = GetASNItem_Length(dataASN[CRLASN_IDX_TBS_ISSUER],
                            buff);
        dcrl->issuer   = (byte*)GetNameFromDer((byte*)GetASNItem_Addr(
                            dataASN[CRLASN_IDX_TBS_ISSUER], buff),
                            (int)dcrl->issuerSz);
    #endif
        /* Calculate the Hash id from the issuer name. */
        ret = CalcHashId_ex(
                GetASNItem_Addr(dataASN[CRLASN_IDX_TBS_ISSUER], buff),
                GetASNItem_Length(dataASN[CRLASN_IDX_TBS_ISSUER], buff),
                dcrl->issuerHash, HashIdAlg(dcrl->signatureOID));
        if (ret < 0) {
            ret = ASN_PARSE_E;
        }
    }

    if ((ret == 0) && (dataASN[CRLASN_IDX_TBS_REVOKEDCERTS].tag != 0)) {
        /* Parse revoked certificates - starting after SEQUENCE OF. */
        ret = ParseCRL_RevokedCerts(rcert, dcrl, buff,
            GetASNItem_DataIdx(dataASN[CRLASN_IDX_TBS_REVOKEDCERTS], buff),
            GetASNItem_EndIdx(dataASN[CRLASN_IDX_TBS_REVOKEDCERTS], buff));
    }
    if ((ret == 0) && GetASNItem_HaveIdx(dataASN[CRLASN_IDX_TBS_EXT_SEQ])) {
        /* Parse the extensions - starting after SEQUENCE OF. */
        ret = ParseCRL_Extensions(dcrl, buff,
            GetASNItem_DataIdx(dataASN[CRLASN_IDX_TBS_EXT_SEQ], buff),
            GetASNItem_EndIdx(dataASN[CRLASN_IDX_TBS_EXT_SEQ], buff));
    }
    if (ret == 0) {
        /* Find signer and verify signature. */
        ret = PaseCRL_CheckSignature(dcrl, sigParams, sigParamsSz, buff, cm);
    }

    FREE_ASNGETDATA(dataASN, dcrl->heap);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#ifdef WOLFSSL_CERT_GEN
/* Encode a date as ASN.1 (UTC or GeneralizedTime).
 * Returns length written to output (including tag and length bytes).
 * If output is NULL, just returns the required size.
 */
static word32 EncodeCrlDate(byte* output, const byte* date, byte format)
{
    word32 idx = 0;
    word32 dateLen;

    /* Determine date length based on format */
    if (format == ASN_UTC_TIME) {
        dateLen = ASN_UTC_TIME_SIZE - 1; /* exclude null terminator */
    }
    else if (format == ASN_GENERALIZED_TIME) {
        dateLen = ASN_GENERALIZED_TIME_SIZE - 1;
    }
    else {
        return 0; /* unsupported format */
    }

    if (output != NULL) {
        output[idx] = format;
    }
    idx++;

    if (output != NULL) {
        idx += SetLength(dateLen, output + idx);
        XMEMCPY(output + idx, date, dateLen);
    }
    else {
        idx += SetLength(dateLen, NULL);
    }
    idx += dateLen;

    return idx;
}

/* Encode a serial number as ASN.1 INTEGER.
 * Similar to SetSerialNumber but always available.
 */
static int EncodeCrlSerial(const byte* sn, word32 snSz, byte* output,
                            word32 outputSz)
{
    int i;
    int snSzInt = (int)snSz;
    const byte* snPtr = sn;

    if (sn == NULL || snSzInt < 0)
        return BAD_FUNC_ARG;

    /* remove leading zeros */
    while (snSzInt > 0 && snPtr[0] == 0) {
        snSzInt--;
        snPtr++;
    }
    /* Serial numbers must be non-negative; allow zero for tolerance */
    if (snSzInt == 0) {
        i = SetASNInt(1, 0x00, output);
        if (1 > (int)outputSz - i) {
            return BUFFER_E;
        }
        if (output != NULL) {
            output[i] = 0x00;
        }
        return i + 1;
    }

    i = SetASNInt(snSzInt, snPtr[0], NULL);
    /* sanity check number of bytes to copy */
    if (snSzInt > (int)outputSz - i || snSzInt <= 0) {
        return BUFFER_E;
    }

    if (output != NULL) {
        /* write out ASN.1 Integer */
        (void)SetASNInt(snSzInt, snPtr[0], output);
        XMEMCPY(output + i, snPtr, (size_t)snSzInt);
    }

    return i + snSzInt;
}

/* Encode a single revoked certificate entry.
 * Returns length written to output.
 */
static word32 EncodeRevokedCert(byte* output, const RevokedCert* rc)
{
    int tmpSnSz;
    word32 idx = 0;
    word32 snSz, dateSz, seqSz;
    byte snBuf[MAX_SN_SZ];
    byte dateBuf[MAX_DATE_SIZE + 2]; /* tag + length + data */
    byte seqBuf[MAX_SEQ_SZ];

    /* Encode serial number */
    tmpSnSz = EncodeCrlSerial(rc->serialNumber, (word32)rc->serialSz,
                                   snBuf, sizeof(snBuf));
    if (tmpSnSz < 0)
        return 0;
    snSz = (word32)tmpSnSz;

    /* Encode revocation date */
    dateSz = EncodeCrlDate(dateBuf, rc->revDate, rc->revDateFormat);
    if (dateSz == 0)
        return 0;

    /* Wrap in SEQUENCE */
    seqSz = SetSequence(snSz + dateSz, seqBuf);

    if (output != NULL) {
        XMEMCPY(output + idx, seqBuf, seqSz);
        idx += seqSz;
        XMEMCPY(output + idx, snBuf, snSz);
        idx += snSz;
        XMEMCPY(output + idx, dateBuf, dateSz);
        idx += dateSz;
    }
    else {
        idx = seqSz + snSz + dateSz;
    }

    return idx;
}

/* Encode the CRL Number extension.
 * Returns length written to output.
 */
static word32 EncodeCrlNumberExt(byte* output, const byte* crlNum,
                                  word32 crlNumSz)
{
    int tmpIntSz;
    word32 idx = 0;
    word32 oidSz, intSz, octetSz, seqSz;
    byte seqBuf[MAX_SEQ_SZ];
    byte octetBuf[MAX_OCTET_STR_SZ];
    byte intBuf[MAX_SN_SZ];

    /* CRL Number OID: 2.5.29.20 */
    static const byte crlNumOid[] = { 0x06, 0x03, 0x55, 0x1d, 0x14 };
    oidSz = sizeof(crlNumOid);

    /* Encode the INTEGER for CRL number */
    tmpIntSz = EncodeCrlSerial(crlNum, crlNumSz, intBuf, sizeof(intBuf));
    if (tmpIntSz < 0)
        return 0;
    intSz = (word32)tmpIntSz;

    /* Wrap INTEGER in OCTET STRING */
    octetSz = SetOctetString(intSz, octetBuf);

    /* Wrap in extension SEQUENCE */
    seqSz = SetSequence(oidSz + octetSz + intSz, seqBuf);

    if (output != NULL) {
        XMEMCPY(output + idx, seqBuf, seqSz);
        idx += seqSz;
        XMEMCPY(output + idx, crlNumOid, oidSz);
        idx += oidSz;
        XMEMCPY(output + idx, octetBuf, octetSz);
        idx += octetSz;
        XMEMCPY(output + idx, intBuf, intSz);
        idx += intSz;
    }
    else {
        idx = seqSz + oidSz + octetSz + intSz;
    }

    return idx;
}

/* Build CRL TBSCertList from fields.
 * issuerDer: DER-encoded issuer Name
 * issuerSz: size of issuer DER
 * lastDate/lastDateFmt: thisUpdate time and format
 * nextDate/nextDateFmt: nextUpdate time and format
 * certs: linked list of revoked certificates (may be NULL)
 * crlNumber/crlNumberSz: CRL number extension data (may be NULL)
 * sigType: signature algorithm type (e.g., CTC_SHA256wRSA)
 * version: CRL version (1 or 2; 2 required for extensions)
 * output: buffer to write TBS (NULL to calculate size)
 * outputSz: size of output buffer
 *
 * Returns: size of TBS on success, negative error code on failure
 */
int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz,
                  const byte* lastDate, byte lastDateFmt,
                  const byte* nextDate, byte nextDateFmt,
                  RevokedCert* certs, const byte* crlNumber, word32 crlNumberSz,
                  int sigType, int version, byte* output, word32 outputSz)
{
    word32 idx = 0;
    word32 tbsContentSz = 0;
    word32 versionSz = 0, algoSz = 0, lastDateSz = 0, nextDateSz = 0;
    word32 revokedSz = 0, extSz = 0, extSeqSz = 0, extCtxSz = 0;
    word32 tbsSeqSz;
    byte tbsSeqBuf[MAX_SEQ_SZ];
    byte versionBuf[MAX_VERSION_SZ];
    byte algoBuf[MAX_ALGO_SZ];
    byte lastDateBuf[MAX_DATE_SIZE + 2];
    byte nextDateBuf[MAX_DATE_SIZE + 2];
    byte revokedSeqBuf[MAX_SEQ_SZ];
    byte extSeqBuf[MAX_SEQ_SZ];
    byte extCtxBuf[MAX_SEQ_SZ + 1]; /* context tag + length */
    RevokedCert* rc;

    if (issuerDer == NULL || issuerSz == 0 || lastDate == NULL)
        return BAD_FUNC_ARG;

    /* Version: only include if v2 (version = 2 means value 1 in ASN.1) */
    if (version >= 2) {
        versionSz = (word32)SetMyVersion(version - 1, versionBuf, FALSE);
    }

    /* Signature AlgorithmIdentifier */
    algoSz = SetAlgoID(sigType, algoBuf, oidSigType, 0);
    if (algoSz == 0 || algoSz > MAX_ALGO_SZ)
        return ALGO_ID_E;

    /* thisUpdate */
    lastDateSz = EncodeCrlDate(lastDateBuf, lastDate, lastDateFmt);
    if (lastDateSz == 0)
        return ASN_DATE_SZ_E;

    /* nextUpdate (optional) */
    if (nextDate != NULL && nextDateFmt != 0) {
        nextDateSz = EncodeCrlDate(nextDateBuf, nextDate, nextDateFmt);
    }

    /* revokedCertificates (optional) */
    if (certs != NULL) {
        word32 contentSz = 0;
        /* First pass: calculate size */
        for (rc = certs; rc != NULL; rc = rc->next) {
            word32 entrySz = EncodeRevokedCert(NULL, rc);
            if (entrySz == 0)
                return ASN_PARSE_E;
            contentSz += entrySz;
        }
        revokedSz = SetSequence(contentSz, revokedSeqBuf) + contentSz;
    }

    /* crlExtensions (optional) - CRL Number */
    if (crlNumber != NULL && crlNumberSz > 0 && version >= 2) {
        word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz);
        if (crlNumExtSz > 0) {
            extSeqSz = SetSequence(crlNumExtSz, extSeqBuf);
            /* Context tag [0] EXPLICIT */
            extCtxBuf[0] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0);
            extCtxSz = 1 + SetLength(extSeqSz + crlNumExtSz, extCtxBuf + 1);
            extSz = extCtxSz + extSeqSz + crlNumExtSz;
        }
    }

    /* Calculate total TBS content size */
    tbsContentSz = versionSz + algoSz + issuerSz + lastDateSz + nextDateSz +
                   revokedSz + extSz;

    /* TBS SEQUENCE header */
    tbsSeqSz = SetSequence(tbsContentSz, tbsSeqBuf);

    /* Check buffer size */
    if (output != NULL && (tbsSeqSz + tbsContentSz > outputSz))
        return BUFFER_E;

    /* If output is NULL, just return required size */
    if (output == NULL)
        return (int)(tbsSeqSz + tbsContentSz);

    /* Encode TBS */
    idx = 0;

    /* TBS SEQUENCE header */
    XMEMCPY(output + idx, tbsSeqBuf, tbsSeqSz);
    idx += tbsSeqSz;

    /* Version (optional) */
    if (versionSz > 0) {
        XMEMCPY(output + idx, versionBuf, versionSz);
        idx += versionSz;
    }

    /* Signature AlgorithmIdentifier */
    XMEMCPY(output + idx, algoBuf, algoSz);
    idx += algoSz;

    /* Issuer Name */
    XMEMCPY(output + idx, issuerDer, issuerSz);
    idx += issuerSz;

    /* thisUpdate */
    XMEMCPY(output + idx, lastDateBuf, lastDateSz);
    idx += lastDateSz;

    /* nextUpdate (optional) */
    if (nextDateSz > 0) {
        XMEMCPY(output + idx, nextDateBuf, nextDateSz);
        idx += nextDateSz;
    }

    /* revokedCertificates (optional) */
    if (revokedSz > 0) {
        word32 contentSz = 0;
        for (rc = certs; rc != NULL; rc = rc->next) {
            contentSz += EncodeRevokedCert(NULL, rc);
        }
        idx += SetSequence(contentSz, output + idx);
        for (rc = certs; rc != NULL; rc = rc->next) {
            idx += EncodeRevokedCert(output + idx, rc);
        }
    }

    /* crlExtensions (optional) */
    if (extSz > 0) {
        word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz);
        /* Context tag [0] */
        output[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0);
        idx += SetLength(extSeqSz + crlNumExtSz, output + idx);
        /* Extensions SEQUENCE */
        idx += SetSequence(crlNumExtSz, output + idx);
        /* CRL Number extension */
        idx += EncodeCrlNumberExt(output + idx, crlNumber, crlNumberSz);
    }

    return (int)idx;
}

/* Sign a CRL TBS and produce complete CRL DER.
 * tbsBuf: contains the TBS at the beginning
 * tbsSz: size of TBS in tbsBuf
 * sType: signature type (e.g., CTC_SHA256wRSA)
 * buf: output buffer for complete CRL. May be the same as tbsBuf.
 * bufSz: size of output buffer
 * rsaKey/eccKey: signing key (one must be non-NULL)
 * rng: random number generator
 *
 * Returns: size of complete CRL on success, negative error on failure
 */
int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType,
                  byte* buf, word32 bufSz,
                  RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng)
{
    int ret;
    int sigSz;
    word32 sigCap = MAX_ENCODED_CLASSIC_SIG_SZ;
    CertSignCtx  certSignCtx_lcl;
    CertSignCtx* certSignCtx = &certSignCtx_lcl;
    void* heap = NULL;

    if (tbsBuf == NULL || tbsSz <= 0 || buf == NULL || rng == NULL)
        return BAD_FUNC_ARG;
    if (rsaKey == NULL && eccKey == NULL)
        return BAD_FUNC_ARG;
    if (rsaKey != NULL && eccKey != NULL)
        return BAD_FUNC_ARG;

    /* The CRL's signatureAlgorithm OID is written from sType while the
     * signature is produced from the key, so reject a mismatch. */
    ret = CheckSigTypeForKey(sType, rsaKey, eccKey, NULL, NULL, NULL, NULL,
        NULL, NULL, NULL);
    if (ret != 0) {
        WOLFSSL_MSG("Signature type does not match signing key");
        return ret;
    }

    XMEMSET(certSignCtx, 0, sizeof(*certSignCtx));

    heap = GetSigningKeyHeap(rsaKey, eccKey, NULL, NULL, NULL, NULL, NULL, NULL);

    /* Copy TBS to output buffer first */
    if ((word32)tbsSz > bufSz)
        return BUFFER_E;
    XMEMCPY(buf, tbsBuf, (size_t)tbsSz);

    /* Only RSA/ECC keys are accepted above, so the signature is a classic
     * (non-PQC) one and fits MAX_ENCODED_CLASSIC_SIG_SZ. */
#ifndef WOLFSSL_NO_MALLOC
    certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_CLASSIC_SIG_SZ, heap,
        DYNAMIC_TYPE_TMP_BUFFER);
    if (certSignCtx->sig == NULL)
        return MEMORY_E;
    /* Initialize first byte to avoid static analysis warnings about using
     * uninitialized memory if MakeSignature fails before writing sig. */
    certSignCtx->sig[0] = 0;
#else
    /* Don't claim more capacity than the fixed sig buffer really has. */
    if (sigCap > (word32)sizeof(certSignCtx->sig))
        sigCap = (word32)sizeof(certSignCtx->sig);
#endif

    /* Create signature */
    sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig,
                          sigCap, rsaKey, eccKey, NULL, NULL,
                          NULL, NULL, NULL, NULL, NULL, rng, (word32)sType, heap);
    if (sigSz < 0) {
#ifndef WOLFSSL_NO_MALLOC
        XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
        return sigSz;
    }

    /* Ensure output buffer is large enough for signature wrapper */
    ret = AddSignature(NULL, tbsSz, certSignCtx->sig, sigSz, sType);
    if (ret < 0) {
#ifndef WOLFSSL_NO_MALLOC
        XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
        return ret;
    }
    if ((word32)ret > bufSz) {
#ifndef WOLFSSL_NO_MALLOC
        XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
        return BUFFER_E;
    }

    /* Add signature algorithm and signature to buffer */
    ret = AddSignature(buf, tbsSz, certSignCtx->sig, sigSz, sType);

#ifndef WOLFSSL_NO_MALLOC
    XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    return ret;
}
#endif /* WOLFSSL_CERT_GEN */

#endif /* HAVE_CRL */



#ifdef WOLFSSL_CERT_PIV

#ifdef WOLFSSL_ASN_TEMPLATE
/* Template for PIV. */
static const ASNItem pivASN[] = {
/* CERT        */ { 0, ASN_PIV_CERT, 0, 0, 0 },
/* NONCE       */ { 0, ASN_PIV_NONCE, 0, 0, 1 },
/* SIGNEDNONCE */ { 0, ASN_PIV_SIGNED_NONCE, 0, 0, 1 },
};
enum {
    PIVASN_IDX_CERT = 0,
    PIVASN_IDX_NONCE,
    PIVASN_IDX_SIGNEDNONCE,
};

#define pivASN_Length (sizeof(pivASN) / sizeof(ASNItem))

static const ASNItem pivCertASN[] = {
                          /* 0x53 = 0x40 | 0x13 */
/* CERT */ { 1, ASN_APPLICATION | 0x13, 0, 1, 0 },
                               /* 0x70 = 0x40 | 0x10 + 0x20 (CONSTRUCTED) */
/* X509 */      { 2, ASN_APPLICATION | 0x10, 1, 0, 0 },
                               /* 0x71 = 0x40 | 0x11 + 0x20 (CONSTRUCTED) */
/* INFO */      { 2, ASN_APPLICATION | 0x11, 1, 0, 1 },
                               /* 0xFE = 0xC0 | 0x1E + 0x20 (CONSTRUCTED) */
/* ERR */      { 2, ASN_PRIVATE | 0x1e, 1, 0, 1 },
};
enum {
    PIVCERTASN_IDX_CERT,
    PIVCERTASN_IDX_X509,
    PIVCERTASN_IDX_INFO,
    PIVCERTASN_IDX_ERR,
};

#define pivCertASN_Length (sizeof(pivCertASN) / sizeof(ASNItem))
#endif

#ifdef WOLFSSL_ASN_TEMPLATE
int wc_ParseCertPIV(wc_CertPIV* piv, const byte* buf, word32 totalSz)
{
    /* pivCertASN_Length is longer than pivASN_Length */
    DECL_ASNGETDATA(dataASN, pivCertASN_Length);
    int ret = 0;
    word32 idx;
    byte info = 0;

    WOLFSSL_ENTER("wc_ParseCertPIV");

    ALLOC_ASNGETDATA(dataASN, pivCertASN_Length, ret, NULL);

    if (ret == 0) {
        /* Clear dynamic data. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * pivASN_Length);
        /* Start parsing from start of buffer. */
        idx = 0;
        /* Parse Identiv wrapper. */
        ret = GetASN_Items(pivASN, dataASN, pivASN_Length, 1, buf, &idx,
                totalSz);
        if (ret == 0) {
            /* Identiv wrapper found. */
            piv->isIdentiv = 1;
            /* Get nonce reference. */
            if (dataASN[PIVASN_IDX_NONCE].tag != 0) {
                GetASN_GetConstRef(&dataASN[PIVASN_IDX_NONCE], &piv->nonce,
                        &piv->nonceSz);
            }
            /* Get signedNonce reference. */
            if (dataASN[PIVASN_IDX_SIGNEDNONCE].tag != 0) {
                GetASN_GetConstRef(&dataASN[PIVASN_IDX_SIGNEDNONCE],
                        &piv->signedNonce, &piv->signedNonceSz);
            }
            /* Get the certificate data for parsing. */
            GetASN_GetConstRef(&dataASN[PIVASN_IDX_CERT], &buf, &totalSz);
        }
        ret = 0;
    }
    if (ret == 0) {
        /* Clear dynamic data and set variable to put cert info into. */
        XMEMSET(dataASN, 0, sizeof(*dataASN) * pivCertASN_Length);
        GetASN_Int8Bit(&dataASN[PIVCERTASN_IDX_INFO], &info);
        /* Start parsing from start of buffer. */
        idx = 0;
        /* Parse PIV certificate data. */
        ret = GetASN_Items(pivCertASN, dataASN, pivCertASN_Length, 1, buf, &idx,
                totalSz);
        if (ret == 0) {
            /* Get X.509 certificate reference. */
            GetASN_GetConstRef(&dataASN[PIVCERTASN_IDX_X509], &piv->cert,
                    &piv->certSz);
            /* Set the certificate info if available. */
            if (dataASN[PIVCERTASN_IDX_INFO].tag != 0) {
                /* Bits 1 and 2 are compression. */
                piv->compression = info & ASN_PIV_CERT_INFO_COMPRESSED;
                /* Bits 3 is X509 flag. */
                piv->isX509 = ((info & ASN_PIV_CERT_INFO_ISX509) != 0);
            }
            /* Get X.509 certificate error detection reference. */
            GetASN_GetConstRef(&dataASN[PIVCERTASN_IDX_ERR], &piv->certErrDet,
                     &piv->certErrDetSz);
        }
        ret = 0;
    }

    FREE_ASNGETDATA(dataASN, NULL);
    return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
#endif /* WOLFSSL_CERT_PIV */



#ifdef HAVE_SMIME

/*****************************************************************************
* wc_MIME_parse_headers - Reads the char array in and parses out MIME headers
* and parameters into headers.  Will continue until in has no more content.
*
* RETURNS:
* returns zero on success, non-zero on error.
*/
int wc_MIME_parse_headers(char* in, int inLen, MimeHdr** headers)
{
    MimeHdr* nextHdr = NULL;
    MimeHdr* curHdr = NULL;
    MimeParam* nextParam = NULL;
    size_t start = 0;
    size_t end = 0;
    char* nameAttr = NULL;
    char* bodyVal = NULL;
    MimeTypes mimeType = MIME_HDR;
    MimeStatus mimeStatus = MIME_NAMEATTR;
    int ret = -1;
    size_t pos = 0;
    size_t lineLen = 0;
    char* curLine = NULL;
    char* ptr = NULL;

    if (in == NULL || inLen <= 0 || in[inLen] != '\0' || headers == NULL) {
        ret = BAD_FUNC_ARG;
        goto error;
    }
    nextHdr = (MimeHdr*)XMALLOC(sizeof(MimeHdr), NULL, DYNAMIC_TYPE_PKCS7);
    if (nextHdr == NULL) {
        ret = MEMORY_E;
        goto error;
    }
    XMEMSET(nextHdr, 0, sizeof(MimeHdr));
    nextParam = (MimeParam*)XMALLOC(sizeof(MimeParam), NULL,
                                    DYNAMIC_TYPE_PKCS7);
    if (nextParam == NULL) {
        ret = MEMORY_E;
        goto error;
    }
    XMEMSET(nextParam, 0, sizeof(MimeParam));

    curLine = XSTRTOK(in, "\r\n", &ptr);
    if (curLine == NULL) {
        ret = ASN_PARSE_E;
        goto error;
    }

    while (curLine != NULL) {
        /* Leftover from previous line, add params to previous header. */
        if (curLine[0] == ' ' && curHdr) {
            mimeType = MIME_PARAM;
        }
        else {
            mimeType = MIME_HDR;
        }
        start = 0;
        lineLen = XSTRLEN(curLine);
        if (lineLen == 0) {
            ret = BAD_FUNC_ARG;
            goto error;
        }

        for (pos = 0; pos < lineLen; pos++) {
            char cur = curLine[pos];

            if (mimeStatus == MIME_NAMEATTR && ((cur == ':' &&
                mimeType == MIME_HDR) || (cur == '=' &&
                mimeType == MIME_PARAM)) && pos >= 1) {
                mimeStatus = MIME_BODYVAL;
                end = pos-1;
                XFREE(nameAttr, NULL, DYNAMIC_TYPE_PKCS7);
                nameAttr = NULL;
                ret = wc_MIME_header_strip(curLine, &nameAttr, start, end);
                if (ret) {
                    goto error;
                }
                start = pos+1;
            }
            else if (mimeStatus == MIME_BODYVAL && cur == ';' && pos >= 1) {
                end = pos-1;
                XFREE(bodyVal, NULL, DYNAMIC_TYPE_PKCS7);
                bodyVal = NULL;
                ret = wc_MIME_header_strip(curLine, &bodyVal, start, end);
                if (ret) {
                    goto error;
                }
                if (mimeType == MIME_HDR) {
                    nextHdr->name = nameAttr;
                    nameAttr = NULL;
                    nextHdr->body = bodyVal;
                    bodyVal = NULL;
                    nextHdr->next = curHdr;
                    curHdr = nextHdr;
                    nextHdr = (MimeHdr*)XMALLOC(sizeof(MimeHdr), NULL,
                                                DYNAMIC_TYPE_PKCS7);
                    if (nextHdr == NULL) {
                        ret = MEMORY_E;
                        goto error;
                    }
                    XMEMSET(nextHdr, 0, sizeof(MimeHdr));
                }
                else {
                    nextParam->attribute = nameAttr;
                    nameAttr = NULL;
                    nextParam->value = bodyVal;
                    bodyVal = NULL;
                    nextParam->next = curHdr->params;
                    curHdr->params = nextParam;
                    nextParam = (MimeParam*)XMALLOC(sizeof(MimeParam), NULL,
                                                    DYNAMIC_TYPE_PKCS7);
                    if (nextParam == NULL) {
                        ret = MEMORY_E;
                        goto error;
                    }
                    XMEMSET(nextParam, 0, sizeof(MimeParam));
                }
                mimeType = MIME_PARAM;
                mimeStatus = MIME_NAMEATTR;
                start = pos+1;
            }
        }

        end = lineLen-1;
        /* Omit newline characters. */
        while ((curLine[end] == '\r' || curLine[end] == '\n') && end > 0) {
            end--;
        }
        if (end >= start && mimeStatus == MIME_BODYVAL) {
            ret = wc_MIME_header_strip(curLine, &bodyVal, start, end);
            if (ret) {
                goto error;
            }
            if (mimeType == MIME_HDR) {
                nextHdr->name = nameAttr;
                nameAttr = NULL;
                nextHdr->body = bodyVal;
                bodyVal = NULL;
                nextHdr->next = curHdr;
                curHdr = nextHdr;
                nextHdr = (MimeHdr*)XMALLOC(sizeof(MimeHdr), NULL,
                                            DYNAMIC_TYPE_PKCS7);
                if (nextHdr == NULL) {
                    ret = MEMORY_E;
                    goto error;
                }
                XMEMSET(nextHdr, 0, sizeof(MimeHdr));
            } else {
                nextParam->attribute = nameAttr;
                nameAttr = NULL;
                nextParam->value = bodyVal;
                bodyVal = NULL;
                nextParam->next = curHdr->params;
                curHdr->params = nextParam;
                nextParam = (MimeParam*)XMALLOC(sizeof(MimeParam), NULL,
                                                DYNAMIC_TYPE_PKCS7);
                if (nextParam == NULL) {
                    ret = MEMORY_E;
                    goto error;
                }
                XMEMSET(nextParam, 0, sizeof(MimeParam));
            }
        }

        curLine = XSTRTOK(NULL, "\r\n", &ptr);
        mimeStatus = MIME_NAMEATTR;
    }

    *headers = curHdr;
    ret = 0; /* success if at this point */

error:
    if (ret != 0)
        wc_MIME_free_hdrs(curHdr);
    wc_MIME_free_hdrs(nextHdr);
    XFREE(nameAttr, NULL, DYNAMIC_TYPE_PKCS7);
    XFREE(bodyVal, NULL, DYNAMIC_TYPE_PKCS7);
    XFREE(nextParam, NULL, DYNAMIC_TYPE_PKCS7);

    return ret;
}

/*****************************************************************************
* wc_MIME_header_strip - Reads the string in from indices start to end, strips
* out disallowed/separator characters and places the rest into *out.
*
* RETURNS:
* returns zero on success, non-zero on error.
*/
int wc_MIME_header_strip(char* in, char** out, size_t start, size_t end)
{
    size_t inPos = start;
    size_t outPos = 0;
    size_t inLen = 0;

    if (end < start || in == NULL || out == NULL) {
        return BAD_FUNC_ARG;
    }

    inLen = XSTRLEN(in);
    if (start > inLen || end > inLen) {
        return BAD_FUNC_ARG;
    }

    *out = (char*)XMALLOC(((end-start)+2)*sizeof(char), NULL,
                          DYNAMIC_TYPE_PKCS7);
    if (*out == NULL) {
        return MEMORY_E;
    }

    while (inPos <= end) {
        if (in[inPos] >= MIME_HEADER_ASCII_MIN && in[inPos] <=
            MIME_HEADER_ASCII_MAX && in[inPos] != ';' && in[inPos] != '\"') {
            (*out)[outPos] = in[inPos];
            outPos++;
        }
        inPos++;
    }
    (*out)[outPos] = '\0';

    return 0;
}

/*****************************************************************************
* wc_MIME_find_header_name - Searches through all given headers until a header
* with a name matching the provided name is found.
*
* RETURNS:
* returns a pointer to the found header, if no match was found, returns NULL.
*/
MimeHdr* wc_MIME_find_header_name(const char* name, MimeHdr* header)
{
    while (header) {
        if (!XSTRCMP(name, header->name)) {
            return header;
        }
        header = header->next;
    }

    return header;
}

/*****************************************************************************
* wc_MIME_find_param_attr - Searches through all parameters until a parameter
* with a attribute matching the provided attribute is found.
*
* RETURNS:
* returns a pointer to the found parameter, if no match was found,
* returns NULL.
*/
MimeParam* wc_MIME_find_param_attr(const char* attribute,
                                    MimeParam* param)
{
    while (param) {
        if (!XSTRCMP(attribute, param->attribute)) {
            return param;
        }
        param = param->next;
    }

    return param;
}

/*****************************************************************************
* wc_MIME_single_canonicalize - Canonicalize a line by converting the trailing
* line ending to CRLF.
*
* line - input line to canonicalize
* len  - length of line in chars on input, length of output array on return
*
* RETURNS:
* returns a pointer to a canonicalized line on success, NULL on error.
*/
char* wc_MIME_single_canonicalize(const char* line, word32* len)
{
    size_t end = 0;
    char* canonLine = NULL;

    if (line == NULL || len == NULL || *len == 0) {
        return NULL;
    }

    end = *len;
    while (end >= 1 && ((line[end-1] == '\r') || (line[end-1] == '\n'))) {
        end--;
    }

    /* Need 2 chars for \r\n and 1 for EOL */
    canonLine = (char*)XMALLOC((end+3)*sizeof(char), NULL, DYNAMIC_TYPE_PKCS7);
    if (canonLine == NULL) {
        return NULL;
    }

    XMEMCPY(canonLine, line, end);
    canonLine[end] = '\r';
    canonLine[end+1] = '\n';
    canonLine[end+2] = '\0';
    *len = (word32)(end + 3);

    return canonLine;
}

/*****************************************************************************
* wc_MIME_free_hdrs - Frees all MIME headers, parameters and strings starting
* from the provided header pointer.
*
* RETURNS:
* returns zero on success, non-zero on error.
*/
int wc_MIME_free_hdrs(MimeHdr* head)
{
    MimeHdr* curHdr = NULL;
    MimeParam* curParam = NULL;

    while (head) {
        while (head->params) {
            curParam = head->params;
            head->params = head->params->next;
            XFREE(curParam->attribute, NULL, DYNAMIC_TYPE_PKCS7);
            XFREE(curParam->value, NULL, DYNAMIC_TYPE_PKCS7);
            XFREE(curParam, NULL, DYNAMIC_TYPE_PKCS7);
        }
        curHdr = head;
        head = head->next;
        XFREE(curHdr->name, NULL, DYNAMIC_TYPE_PKCS7);
        XFREE(curHdr->body, NULL, DYNAMIC_TYPE_PKCS7);
        XFREE(curHdr, NULL, DYNAMIC_TYPE_PKCS7);
    }

    return 0;
}

#endif /* HAVE_SMIME */




#ifdef WOLFSSL_ASN_PRINT

/*******************************************************************************
 * ASN.1 Parsing and Printing Implementation
 ******************************************************************************/

/* Initialize ASN.1 print options.
 *
 * @param [in, out] opts  ASN.1 options for printing.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 is NULL.
 */
int wc_Asn1PrintOptions_Init(Asn1PrintOptions* opts)
{
    int ret = 0;

    if (opts == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else {
        XMEMSET(opts, 0, sizeof(*opts));
    }

    return ret;
}

/* Set a print option into Asn1PrintOptions object.
 *
 * @param [in, out] opts  ASN.1 options for printing.
 * @param [in]      opt   Option to set value of.
 * @param [in]      val   Value to set for option.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 is NULL.
 * @return  BAD_FUNC_ARG when val is out of range for option.
 */
int wc_Asn1PrintOptions_Set(Asn1PrintOptions* opts, enum Asn1PrintOpt opt,
    word32 val)
{
    int ret = 0;

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

    if (ret == 0) {
        switch (opt) {
        /* Offset into DER/BER data to start decoding from. */
        case ASN1_PRINT_OPT_OFFSET:
            opts->offset = val;
            break;
        /* Length of DER/BER encoding to parse. */
        case ASN1_PRINT_OPT_LENGTH:
            opts->length = val;
            break;
        /* Number of spaces to indent for each change in depth. */
        case ASN1_PRINT_OPT_INDENT:
            /* Only 4 bits allowed for value. */
            if (val >= (1 << 4)) {
                ret = BAD_FUNC_ARG;
            }
            else {
                opts->indent = (word8)val;
            }
            break;
        /* Draw branches instead of indenting. */
        case ASN1_PRINT_OPT_DRAW_BRANCH:
            /* Boolean value. */
            opts->draw_branch = (val > 0);
            break;
        /* Show raw data of primitive types as octets. */
        case ASN1_PRINT_OPT_SHOW_DATA:
            /* Boolean value. */
            opts->show_data = (val > 0);
            break;
        /* Show header data as octets. */
        case ASN1_PRINT_OPT_SHOW_HEADER_DATA:
            /* Boolean value. */
            opts->show_header_data = (val > 0);
            break;
        /* Show the wolfSSL OID value for OBJECT_ID. */
        case ASN1_PRINT_OPT_SHOW_OID:
            /* Boolean value. */
            opts->show_oid = (val > 0);
            break;
        /* Don't show text representations of primitive types. */
        case ASN1_PRINT_OPT_SHOW_NO_TEXT:
            /* Boolean value. */
            opts->show_no_text = (val > 0);
            break;
        /* Don't show dump text representations of primitive types. */
        case ASN1_PRINT_OPT_SHOW_NO_DUMP_TEXT:
            /* Boolean value. */
            opts->show_no_dump_text = (val > 0);
            break;
        }
    }

    return ret;
}

/* Initialize an ASN.1 parse object.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 is NULL.
 */
int wc_Asn1_Init(Asn1* asn1)
{
    int ret = 0;

    if (asn1 == NULL) {
        ret = BAD_FUNC_ARG;
    }
    else {
        XMEMSET(asn1, 0, sizeof(*asn1));
        asn1->file = XBADFILE;
    }

    return ret;
}

/* Set the file to use when printing.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 * @param [in]      file  File to print to.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 is NULL.
 * @return  BAD_FUNC_ARG when file is XBADFILE.
 */
int wc_Asn1_SetFile(Asn1* asn1, XFILE file)
{
    int ret = 0;

    if ((asn1 == NULL) || (file == XBADFILE)) {
        ret = BAD_FUNC_ARG;
    }
    else {
        asn1->file = file;
    }

    return ret;
}

/* Set the OID name callback to use when printing.
 *
 * @param [in, out] asn1    ASN.1 parse object.
 * @param [in]      nameCb  OID name callback.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 is NULL.
 * @return  BAD_FUNC_ARG when nameCb is NULL.
 */
int wc_Asn1_SetOidToNameCb(Asn1* asn1, Asn1OidToNameCb nameCb)
{
    int ret = 0;

    if ((asn1 == NULL) || (nameCb == NULL)) {
        ret = BAD_FUNC_ARG;
    }
    else {
        asn1->nameCb = nameCb;
    }

    return ret;
}

/* Encode dotted form of OID into byte array version.
 *
 * @param [in]      in     Byte array containing OID.
 * @param [in]      inSz   Size of OID in bytes.
 * @param [in]      out    Array to hold dotted form of OID.
 * @param [in, out] outSz  On in, number of elements in array.
 *                         On out, count of numbers in dotted form.
 * @return  0 on success
 * @return  BAD_FUNC_ARG when in or outSz is NULL.
 * @return  BUFFER_E when dotted form buffer too small.
 */
static int EncodedDottedForm(const byte* in, word32 inSz, word32* out,
    word32* outSz)
{
    int x = 0, y = 0;
    word32 t = 0;

    /* check args */
    if (in == NULL || outSz == NULL) {
        return BAD_FUNC_ARG;
    }

    if (*outSz < 2) {
        return BUFFER_E;
    }

    /* decode bytes */
    while (inSz--) {
        t = (t << 7) | (in[x] & 0x7F);
        if (!(in[x] & 0x80)) {
            if (y >= (int)*outSz) {
                return BUFFER_E;
            }
            if (y == 0) {
                /* Special case for when first arc is 2 */
                if (t < 80) {
                    out[0] = t / 40;
                    out[1] = t % 40;
                }
                else {
                    out[0] = 2;
                    out[1] = t - 80;
                }
                y = 2;
            }
            else {
                out[y++] = t;
            }
            t = 0; /* reset tmp */
        }
        x++;
    }

    /* return length */
    *outSz = (word32)y;

    return 0;
}
/* Print OID in dotted form or as hex bytes.
 *
 * @param [in]  file        File pointer to write to.
 * @param [in]  oid         OBJECT_ID data.
 * @param [in]  oid_len     Length of OBJECT_ID data.
 */
static void PrintObjectIdNum(XFILE file, unsigned char* oid, word32 len)
{
    word32 dotted_nums[ASN1_OID_DOTTED_MAX_SZ];
    word32 num = ASN1_OID_DOTTED_MAX_SZ;
    word32 i;

    /* Decode OBJECT_ID into dotted form array. */
    if (EncodedDottedForm(oid, len, dotted_nums, &num) == 0) {
        /* Print out each number of dotted form. */
        for (i = 0; i < num; i++) {
            XFPRINTF(file, "%d", dotted_nums[i]);
            /* Add separator. */
            if (i < num - 1) {
                XFPRINTF(file, ".");
            }
        }
    }
    else {
        /* Print out bytes as we couldn't decode. */
        for (i = 0; i < len; i++) {
            XFPRINTF(file, "%02x", oid[i]);
            /* Add separator. */
            if (i < len - 1) {
                XFPRINTF(file, ":");
            }
        }
    }
}

/* OID value to name mapping. */
typedef struct OidName {
    /* wolfSSL OID value. */
    word32 oid;
    /* Long name to print when OID seen. */
    const char* name;
} OidName;

/* Extra OID to name mappings. */
static const OidName extraOids[] = {
    { 0x005c, "commonName" },
    { 0x005d, "surname" },
    { 0x005e, "serialNumber" },
    { 0x005f, "countryName" },
    { 0x0060, "localityName" },
    { 0x0061, "stateOrProvinceName" },
    { 0x0062, "streetAddress" },
    { 0x0063, "organizationName" },
    { 0x0064, "organizationUnitName" },
    { 0x0065, "title" },
    { 0x0086, "certificateExtension" },
    { 0x028d, "emailAddress" },
    { 0x0293, "challengePassword" },
    { 0x029a, "extensionReq" },
};
/* Length of table of extra OID to name mappings. */
#define EXTRA_OIDS_LEN   ((int)(sizeof(extraOids) / sizeof(*extraOids)))

/* Convert OID value to long name.
 *
 * @param [in]  oid   OID value.
 * @param [out] name  Long name for OID when known.
 * @return  1 when OID known.
 * @return  0 when OID not known.
 */
static int Oid2LongName(word32 oid, const char** name)
{
    int ret = 0;
    int i;

    /* Step through each entry in table. */
    for (i = 0; i < EXTRA_OIDS_LEN; i++) {
        if (extraOids[i].oid == oid) {
            /* Return the name associated with the OID value. */
            *name = extraOids[i].name;
            ret = 1;
            break;
        }
    }

    return ret;
}

/* Print the text version of the OBJECT_ID.
 *
 * @param [in] asn1  ASN.1 parse object.
 * @param [in] opts  ASN.1 options for printing.
 */
static void PrintObjectIdText(Asn1* asn1, Asn1PrintOptions* opts)
{
    word32 oid = (word32)-1;
#if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
    int nid;
#endif
    const char* ln = NULL;
    word32 idx = 0;
    int known = 1;

    /* Get the OID value for the OBJECT_ID. */
    if (GetObjectId(asn1->data + asn1->offset, &idx, &oid, oidIgnoreType,
            asn1->item.len + 2) == WC_NO_ERR_TRACE(ASN_PARSE_E)) {
        known = 0;
    }
    else
#if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
    /* Lookup NID for OID value. */
    if ((nid = oid2nid(oid, oidIgnoreType)) != -1) {
        /* Lookup long name for NID. */
        ln = wolfSSL_OBJ_nid2ln(nid);
    }
    else
#endif
    /* Lookup long name for extra known OID values. */
    if (Oid2LongName(oid, &ln) != 0) {
    }
    else if ((asn1->nameCb != NULL) && (idx >= 2) &&
             ((ln = asn1->nameCb(asn1->data + asn1->offset + 2,
                                 idx - 2))) != NULL) {
    }
    else {
        /* Unknown OID value. */
        ln = NULL;
        known = 0;
    }
    XFPRINTF(asn1->file, ":");
    /* Show OID value if not known or asked to. */
    if ((!known) || opts->show_oid) {
        XFPRINTF(asn1->file, "(0x%x) ", oid);
    }
    if (ln != NULL) {
        /* Print long name. */
        XFPRINTF(asn1->file, "%s", ln);
    }
    else {
        /* Print out as numbers - either dotted or hex values. */
        PrintObjectIdNum(asn1->file, asn1->data + asn1->item.data_idx,
            asn1->item.len);
    }
}

/* Print ASN.1 data as a character string.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void PrintText(Asn1* asn1)
{
    word32 i;

    XFPRINTF(asn1->file, ":");
    /* Print all data bytes as characters. */
    for (i = 0; i < asn1->item.len; i++) {
        XFPRINTF(asn1->file, "%c", asn1->data[asn1->item.data_idx + i]);
    }
}

/* Print data as a hex bytes.
 *
 * @param [in] file  File pointer to write to.
 * @param [in] data  Data to print.
 * @param [in] len   Number of bytes to print.
 */
static void PrintHex(XFILE file, unsigned char* data, word32 len)
{
    word32 i;

    /* Print data bytes as hex numbers. */
    for (i = 0; i < len; i++) {
        XFPRINTF(file, "%02x", data[i]);
    }
}

/* Print ASN.1 data as a hex bytes.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void PrintHexText(Asn1* asn1)
{
    XFPRINTF(asn1->file, ":");
    PrintHex(asn1->file, asn1->data + asn1->item.data_idx, asn1->item.len);
}

/* Print ASN.1 BIT_STRING data as hex bytes noting special first byte.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void PrintBitStringText(Asn1* asn1)
{
    if (asn1->item.len > 0) {
        XFPRINTF(asn1->file, ":[%02x]", asn1->data[asn1->item.data_idx]);
        PrintHex(asn1->file, asn1->data + asn1->item.data_idx + 1,
            asn1->item.len - 1);
    }
}

/* Print ASN.1 BOOLEAN data as text with value.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void PrintBooleanText(Asn1* asn1)
{
    /* Booleans should be 1 byte of data. */
    if (asn1->item.len == 1) {
        XFPRINTF(asn1->file, ":%s (%d)",
            (asn1->data[asn1->item.data_idx] == 0) ? "FALSE" : "TRUE",
            asn1->data[asn1->item.data_idx]);
    }
}

/* Print ASN.1 data as single byte +/- number.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void PrintNumberText(Asn1* asn1)
{
    /* Only supporting 1 byte of data for now. */
    if (asn1->item.len == 1) {
       int num = asn1->data[asn1->item.data_idx];

       XFPRINTF(asn1->file, ":%d", num >= 0x80 ? num - 0x100 : num);
    }
}

/* Print ASN.1 data as a text based on the tag.
 *
 * TODO: handle more tags.
 *
 * @param [in] asn1  ASN.1 parse object.
 * @param [in] opts  ASN.1 options for printing.
 */
static void PrintAsn1Text(Asn1* asn1, Asn1PrintOptions* opts)
{
    /* Get the long name for OBJECT_ID where possible. */
    if (asn1->item.tag == ASN_OBJECT_ID) {
        PrintObjectIdText(asn1, opts);
    }
    /* Data is an array of printable characters. */
    else if ((asn1->item.tag == ASN_UTF8STRING) ||
             (asn1->item.tag == ASN_IA5_STRING) ||
             (asn1->item.tag == ASN_PRINTABLE_STRING) ||
             (asn1->item.tag == ASN_T61STRING) ||
             (asn1->item.tag == ASN_BMPSTRING) ||
             (asn1->item.tag == ASN_UTC_TIME) ||
             (asn1->item.tag == ASN_GENERALIZED_TIME) ||
             (asn1->item.tag == ASN_UNIVERSALSTRING) ||
             (asn1->item.tag == ASN_OBJECT_DESC) ||
             (asn1->item.tag == ASN_CHARACTER_STRING)) {
        PrintText(asn1);
    }
    /* Show TRUE and FALSE with number. */
    else if (asn1->item.tag == ASN_BOOLEAN) {
        PrintBooleanText(asn1);
    }
    /* Show number. */
    else if (asn1->item.tag == ASN_ENUMERATED) {
        PrintNumberText(asn1);
    }
    /* Dumping potentially long string of hex digites. */
    else if (!opts->show_no_dump_text) {
        /* Dump all bytes. */
        if ((asn1->item.tag == ASN_INTEGER) ||
            (asn1->item.tag == ASN_OCTET_STRING) ||
            ((asn1->item.tag > ASN_APPLICATION) && (asn1->item.cons))) {
            PrintHexText(asn1);
        }
        /* First byte is number of unused bits in last byte.
         * Print first specially and dump rest of the bytes. */
        else if (asn1->item.tag == ASN_BIT_STRING) {
            PrintBitStringText(asn1);
        }
    }
}

#define HexToChar(n) ((((n) >= 32) && ((n) < 127)) ? (n) : '.')

/* Dump data as hex bytes.
 *
 * @param [in] file  File pointer to write to.
 * @param [in] data  Data to print.
 * @param [in] len   Number of bytes to print.
 */
static void DumpData(XFILE file, unsigned char* data, word32 len)
{
    word32 i;
    word32 j;

    for (i = 0; i < len; i += j) {
        /* Print offset. */
        XFPRINTF(file, "       %04x:", i);
        for (j = 0; (j < 16) && (i + j < len); j++) {
            /* Print byte as hex number. */
            XFPRINTF(file, "%s%02x", (j == 8) ? "  " : " ", data[i + j]);
        }
        /* Print spaces between hex and characters. */
        XFPRINTF(file, "   %*s", (16 - j) * 3 + ((j < 8) ? 1 : 0), "");
        for (j = 0; (j < 16) && (i + j < len); j++) {
            /* Print byte as hex number. */
            XFPRINTF(file, "%c", HexToChar(data[i + j]));
        }
        XFPRINTF(file, "\n");
    }
}

/* Update current depth based on the current position.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 */
static void UpdateDepth(Asn1* asn1)
{
    /* If current index is greater than or equal end index then it is done. */
    while ((asn1->depth > 0) &&
           (asn1->end_idx[asn1->depth-1] <= asn1->curr)) {
        /* Move up a depth. */
        asn1->depth--;
    }
}

/* Check validity of end index of constructed ASN.1 items.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 * @return  0 on success.
 * @return  ASN_DEPTH_E when end offset invalid.
 */
static int CheckDepth(Asn1* asn1)
{
    int ret = 0;
    int i;
    word32 curr_end = asn1->curr + asn1->item.len;

    for (i = 0; (ret == 0) && (i < asn1->depth); i++) {
        /* Each end index must be at least as large as the current one. */
        if (asn1->end_idx[i] < asn1->end_idx[asn1->depth]) {
            ret = ASN_DEPTH_E;
        }
        /* Each end index must be at least as large as current index. */
        if (asn1->end_idx[i] < curr_end) {
            ret = ASN_DEPTH_E;
        }
    }

    return ret;
}

/* Draw branching based on depth for an ASN.1 item.
 *
 * @param [in] asn1  ASN.1 parse object.
 */
static void DrawBranch(Asn1* asn1)
{
    int i;
    word32 end = asn1->curr + asn1->item.len;

    /* Write out the character for all depths but current. */
    for (i = 0; i < asn1->depth; i++) {
        if (asn1->item.cons || (end < asn1->end_idx[i])) {
            if (i < asn1->depth - 1) {
                /* Constructed or not end index and not current depth: | */
                XFPRINTF(asn1->file, "\xe2\x94\x82");
            }
            else {
                /* Constructed or not end index and current depth: |- */
                XFPRINTF(asn1->file, "\xe2\x94\x9c");
            }
        }
        else if ((i > 1) && (end >= asn1->end_idx[i-1])) {
            /* End index for previous: _|_ (in top half) */
            XFPRINTF(asn1->file, "\xe2\x94\xb4");
        }
        else {
            /* End index but not for previous: L (in top half) */
            XFPRINTF(asn1->file, "\xe2\x94\x94");
        }
    }
    /* Prefix to tag name. */
    if (asn1->item.cons) {
        if (asn1->depth > 0) {
            /* Have other line to connect to: T (in bottom half) */
            XFPRINTF(asn1->file, "\xe2\x94\xac");
        }
        else {
            /* Have no other line to connect to: r */
            XFPRINTF(asn1->file, "\xe2\x94\x8c");
        }
    }
    else {
        /* In a sequence: - */
        XFPRINTF(asn1->file, "\xe2\x94\x80");
    }
}

/* Print data as hex bytes separated by space.
 *
 * @param [in] file  File pointer to write to.
 * @param [in] data  Data to print.
 * @param [in] len   Number of bytes to print.
 */
static void PrintHexBytes(XFILE file, unsigned char* data, word32 len)
{
    word32 i;

    for (i = 0; i < len; i++) {
        XFPRINTF(file, " %02x", data[i]);
    }
}

/* Dump header data.
 *
 * @param [in] asn1  ASN.1 parse object.
 * @param [in] opts  ASN.1 options for printing.
 */
static void DumpHeader(Asn1* asn1, Asn1PrintOptions* opts)
{
    /* Put on same line when not showing data too and not showing text data. */
    if ((!opts->show_data) && opts->show_no_text) {
        XFPRINTF(asn1->file, "%10s", "");
    }
    else {
        /* Align with start of data. */
        XFPRINTF(asn1->file, "\n%12s", "");
    }
    XFPRINTF(asn1->file, " %02x", asn1->item.tag);
    if (asn1->curr >= asn1->offset + 1) {
        /* Print the header bytes as hex bytes separated by a space. */
        PrintHexBytes(asn1->file, asn1->data + asn1->offset + 1,
            asn1->curr - (asn1->offset + 1));
    }
}

/* Print ASN.1 item info based on header and indices.
 *
 * @param [in] asn1  ASN.1 parse object.
 * @param [in] opts  ASN.1 options for printing.
 */
static void PrintInfo(Asn1* asn1, Asn1PrintOptions* opts)
{
    /* Print offset of this ASN.1 item. */
    XFPRINTF(asn1->file, "%4d: ", asn1->offset);
    /* Print length of header. */
    XFPRINTF(asn1->file, "%1d ", asn1->curr - asn1->offset);
    /* Print data length. */
    XFPRINTF(asn1->file, "%c%4d%c", asn1->item.cons ? '[' : '+', asn1->item.len,
                      asn1->item.cons ? ']' : ' ');
    /* Print depth. */
    XFPRINTF(asn1->file, " %s(%d)", (asn1->depth < 10) ? " " : "", asn1->depth);
    if (!opts->draw_branch) {
        /* Indent to depth as required. */
        XFPRINTF(asn1->file, "%*s ", asn1->depth * opts->indent, "");
        if (!opts->indent) {
            /* Indicate constructed if no indent. */
            XFPRINTF(asn1->file, "%c", asn1->item.cons ? '+' : ' ');
        }
    }
    else {
        /* Draw branch structure for ASN.1 item. */
        XFPRINTF(asn1->file, " ");
        DrawBranch(asn1);
    }
    /* Print tag name. */
    XFPRINTF(asn1->file, "%-16s", TagString(asn1->item.tag));
}

/* Expecting tag part of ASN.1 item. */
#define ASN_PART_TAG        0
/* Expecting length part of ASN.1 item. */
#define ASN_PART_LENGTH     1
/* Expecting data part of ASN.1 item. */
#define ASN_PART_DATA       2

/* Print next ASN.1 item.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 * @param [in]      opts  ASN.1 print options.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1 or opts is NULL.
 * @return  ASN_LEN_E when ASN.1 item's length too long.
 * @return  ASN_DEPTH_E when end offset invalid.
 */
static int wc_Asn1_Print(Asn1* asn1, Asn1PrintOptions* opts)
{
    int ret = 0;

    /* Process tag. */
    if (asn1->part == ASN_PART_TAG) {
        /* Recalculate which depth we are at. */
        UpdateDepth(asn1);
        /* Get tag. */
        asn1->item.tag = asn1->data[asn1->curr] & (byte)~ASN_CONSTRUCTED;
        /* Store whether tag indicates constructed. */
        asn1->item.cons = (asn1->data[asn1->curr] & ASN_CONSTRUCTED) ==
                     ASN_CONSTRUCTED;
        /* Start of ASN.1 item is current index. */
        asn1->offset = asn1->curr;
        /* Step over tag. */
        asn1->curr++;
        /* Next part is length. */
        asn1->part = ASN_PART_LENGTH;
    }
    /* Process length. */
    if (asn1->part == ASN_PART_LENGTH) {
        int len;

        /* Decode length and step over it. */
        if (GetLength(asn1->data, &asn1->curr, &len, asn1->max) < 0) {
            ret = ASN_LEN_E;
        }
        else {
            /* Store ASN.1 item data offset. */
            asn1->item.data_idx = asn1->curr;
            /* Store ASN.1 item data length. */
            asn1->item.len = (word32)len;

            /* Print info about ASN.1 item. */
            PrintInfo(asn1, opts);

            if (!asn1->item.cons) {
                /* Move on to print data. */
                asn1->part = ASN_PART_DATA;
            }
            else {
                /* Print header now if not printing data. */
                if (opts->show_header_data) {
                    DumpHeader(asn1, opts);
                }
                XFPRINTF(asn1->file, "\n");
                /* Record end offset for this depth. */
                asn1->end_idx[asn1->depth++] = asn1->curr + asn1->item.len;
                /* Done with this ASN.1 item. */
                asn1->part = ASN_PART_TAG;
            }
            /* Check end indices are valid. */
            ret = CheckDepth(asn1);
        }
    }
    /* Process data. */
    if ((ret == 0) && (asn1->part == ASN_PART_DATA)) {
        if (!opts->show_no_text) {
            /* Print text representation of data. */
            PrintAsn1Text(asn1, opts);
        }
        if (opts->show_header_data) {
            /* Dump header bytes. */
            DumpHeader(asn1, opts);
        }
        XFPRINTF(asn1->file, "\n");
        if (opts->show_data) {
            /* Dump data bytes. */
            DumpData(asn1->file, asn1->data + asn1->item.data_idx,
                asn1->item.len);
        }
        /* Step past data to next ASN.1 item. */
        asn1->curr += asn1->item.len;
        /* Update the depth based on end indices. */
        UpdateDepth(asn1);
        /* Done with this ASN.1 item. */
        asn1->part = ASN_PART_TAG;
    }

    /* Make ASN.1 item printing go out. */
    fflush(asn1->file);

    return ret;
}

/* Print all ASN.1 items.
 *
 * @param [in, out] asn1  ASN.1 parse object.
 * @param [in]      opts  ASN.1 print options.
 * @param [in]      data  BER/DER data to print.
 * @param [in]      len   Length of data to print in bytes.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when asn1, opts or data is NULL.
 * @return  ASN_LEN_E when ASN.1 item's length too long.
 * @return  ASN_DEPTH_E when end offset invalid.
 * @return  ASN_PARSE_E when not all of an ASN.1 item parsed.
 */
int wc_Asn1_PrintAll(Asn1* asn1, Asn1PrintOptions* opts, unsigned char* data,
    word32 len)
{
    int ret = 0;

    if ((asn1 == NULL) || (opts == NULL) || (data == NULL)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        /* Initialize start position. */
        asn1->curr = 0;
        /* Start parsing at tag. */
        asn1->part = ASN_PART_TAG;
        /* Start depth at 0. */
        asn1->depth = 0;

        /* Store the starting point of the data to parse. */
        asn1->data = data + opts->offset;
        if (opts->length > 0) {
            /* Use user specified maximum length. */
            asn1->max = opts->length;
        }
        else {
            /* Maximum length is up to end from offset. */
            asn1->max = len - opts->offset;
        }

        /* Keep going while no error and have data to parse. */
        while ((ret == 0) && (asn1->curr < asn1->max)) {
            /* Print an ASN.1 item. */
            ret = wc_Asn1_Print(asn1, opts);
        }
    }
    if ((ret == 0) && (asn1->part != ASN_PART_TAG)) {
        /* Stopped before finishing ASN.1 item. */
        ret = ASN_PARSE_E;
    }
    if ((ret == 0) && (asn1->depth != 0)) {
        /* Stopped without seeing all items in a constructed item. */
        ret = ASN_DEPTH_E;
    }

    return ret;
}

#endif /* WOLFSSL_ASN_PRINT */

/* Include original (non-template) ASN implementations.
 * asn_orig.c must not be compiled separately. */
#ifndef WOLFSSL_ASN_TEMPLATE
#define WOLFSSL_ASN_ORIG_INCLUDED
#include "wolfcrypt/src/asn_orig.c"
#endif /* !WOLFSSL_ASN_TEMPLATE */

#endif /* !NO_ASN */

/* Functions that parse, but are not using ASN.1 */
#if !defined(NO_RSA) && (!defined(NO_BIG_INT) || defined(WOLFSSL_SP_MATH))
/* Software-only import of RSA public key elements (n, e) into RsaKey.
 * This internal helper avoids recursion when called from the SETKEY path. */
static int _RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, const byte* e,
                                  word32 eSz, RsaKey* key)
{
    if (n == NULL || e == NULL || key == NULL) {
        return BAD_FUNC_ARG;
    }

    key->type = RSA_PUBLIC;

    if (mp_init(&key->n) != MP_OKAY) {
        return MP_INIT_E;
    }

    if (mp_read_unsigned_bin(&key->n, n, nSz) != 0) {
        mp_clear(&key->n);
        return ASN_GETINT_E;
    }
#ifdef HAVE_WOLF_BIGINT
    if ((int)nSz > 0 && wc_bigint_from_unsigned_bin(&key->n.raw, n, nSz) != 0) {
        mp_clear(&key->n);
        return ASN_GETINT_E;
    }
#endif /* HAVE_WOLF_BIGINT */

    if (mp_init(&key->e) != MP_OKAY) {
        mp_clear(&key->n);
        return MP_INIT_E;
    }

    if (mp_read_unsigned_bin(&key->e, e, eSz) != 0) {
        mp_clear(&key->n);
        mp_clear(&key->e);
        return ASN_GETINT_E;
    }
#ifdef HAVE_WOLF_BIGINT
    if ((int)eSz > 0 && wc_bigint_from_unsigned_bin(&key->e.raw, e, eSz) != 0) {
        mp_clear(&key->n);
        mp_clear(&key->e);
        return ASN_GETINT_E;
    }
#endif /* HAVE_WOLF_BIGINT */

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

    return 0;
}

/* import RSA public key elements (n, e) into RsaKey structure (key) */
/* this function does not use any ASN.1 parsing */
int wc_RsaPublicKeyDecodeRaw(const byte* n, word32 nSz, const byte* e,
                             word32 eSz, RsaKey* key)
{
#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
    int cbRet = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
    int tmpErr = 0;
    WC_DECLARE_VAR(tmpKey, RsaKey, 1, NULL);
#endif

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

#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_SETKEY)
    #ifndef WOLF_CRYPTO_CB_FIND
    if (key->devId != INVALID_DEVID)
    #endif
    {
        /* Allocate temp key for callback to export from */
        WC_ALLOC_VAR(tmpKey, RsaKey, 1, key->heap);
        if (!WC_VAR_OK(tmpKey)) {
            return MEMORY_E;
        }
        XMEMSET(tmpKey, 0, sizeof(RsaKey));

        tmpErr = wc_InitRsaKey_ex(tmpKey, key->heap, INVALID_DEVID);
        if (tmpErr != 0) {
            WC_FREE_VAR(tmpKey, key->heap);
            return tmpErr;
        }

        /* Import into temp via software helper (no callback recursion) */
        tmpErr = _RsaPublicKeyDecodeRaw(n, nSz, e, eSz, tmpKey);
        if (tmpErr == 0) {
            cbRet = wc_CryptoCb_SetKey(key->devId,
                WC_SETKEY_RSA_PUB, key, tmpKey,
                wc_RsaEncryptSize(tmpKey), NULL, 0, 0);
        }

        wc_FreeRsaKey(tmpKey);
        WC_FREE_VAR(tmpKey, key->heap);

        if (tmpErr != 0) {
            return tmpErr;
        }
        if (cbRet != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
            return cbRet;
        }
        /* CRYPTOCB_UNAVAILABLE: fall through to software import */
    }
#endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_SETKEY */

    return _RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key);
}
#endif /* !NO_RSA && (!NO_BIG_INT || WOLFSSL_SP_MATH) */

#if defined(WOLFSSL_ACERT) && defined(WOLFSSL_ASN_TEMPLATE)
/* Initialize decoded attribute certificate object with buffer of DER encoding.
 *
 * @param [in, out] acert   Decoded attribute certificate object.
 * @param [in]      source  Buffer containing DER encoded certificate.
 * @param [in]      inSz    Size of DER data in buffer in bytes.
 * @param [in]      heap    Dynamic memory hint.
 */
void InitDecodedAcert(DecodedAcert* acert, const byte* source, word32 inSz,
                      void* heap)
{
    WOLFSSL_MSG("InitDecodedAcert");

    if (acert == NULL) {
        return;
    }

    XMEMSET(acert, 0, sizeof(DecodedAcert));
    acert->heap = heap;
    acert->source = source;  /* don't own */
    acert->maxIdx = inSz;    /* can't go over this index */
    acert->heap = heap;

    InitSignatureCtx(&acert->sigCtx, heap, INVALID_DEVID);

    return;
}

/* Free the decoded attribute cert object's dynamic data.
 *
 * @param [in, out] acert   Decoded attribute certificate object.
 */
void FreeDecodedAcert(DecodedAcert * acert)
{
    WOLFSSL_MSG("FreeDecodedAcert");

    if (acert == NULL) {
        return;
    }

    if (acert->holderIssuerName) {
        FreeAltNames(acert->holderIssuerName, acert->heap);
        acert->holderIssuerName = NULL;
    }

    if (acert->holderEntityName) {
        FreeAltNames(acert->holderEntityName, acert->heap);
        acert->holderEntityName = NULL;
    }

    if (acert->AttCertIssuerName) {
        FreeAltNames(acert->AttCertIssuerName, acert->heap);
        acert->AttCertIssuerName = NULL;
    }

    FreeSignatureCtx(&acert->sigCtx);

    XMEMSET(acert, 0, sizeof(DecodedAcert));
    return;
}

/* Decode an Attribute Cert GeneralName field.
 *
 * @param [in]      input     Buffer containing encoded OtherName.
 * @param [in, out] inOutIdx  On in, the index of the start of the OtherName.
 *                            On out, index after OtherName.
 * @param [in]      len       Length of data in buffer.
 * @param [in]      acert     Decoded attribute certificate object.
 * @param [in, out] entries   Linked list of DNS name entries.
 *
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx,
                                  byte tag, int len, DecodedAcert* acert,
                                  DNS_entry** entries)
{
    int    ret = 0;
    word32 idx = *inOutIdx;

    /* GeneralName choice: dnsName */
    if (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) {
        ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len,
                          ASN_DNS_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
#ifndef IGNORE_NAME_CONSTRAINTS
    /* GeneralName choice: directoryName */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE)) {
        int    strLen = 0;
        word32 idxDir = idx;

        /* Expecting a SEQUENCE using up all data. */
        if (GetASN_Sequence(input, &idxDir, &strLen, idx + (word32)len, 1) < 0)
        {
            WOLFSSL_MSG("\tfail: seq length");
            return ASN_PARSE_E;
        }

        ret = SetDNSEntry(acert->heap, (const char*)(input + idxDir), strLen,
                          ASN_DIR_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: rfc822Name */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) {
        ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len,
                ASN_RFC822_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    /* GeneralName choice: uniformResourceIdentifier */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) {
        WOLFSSL_MSG("\tPutting URI into list but not using");

    #ifndef WOLFSSL_NO_ASN_STRICT
        /* 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). */
        {
            int i = 0;

            /* skip past scheme (i.e http,ftp,...) finding first ':' char */
            for (i = 0; i < len; i++) {
                if (input[idx + (word32)i] == ':') {
                    break;
                }
                if (input[idx + (word32)i] == '/') {
                    /* path is relative since '/' was encountered before ':'. */
                    return ASN_ALT_NAME_E;
                }
            }

            /* test hier-part is empty */
            if (i == 0 || i == len) {
                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 + (word32)i] != ':') {
                WOLFSSL_MSG("\tAlt Name must be absolute URI");
                WOLFSSL_ERROR_VERBOSE(ASN_ALT_NAME_E);
                return ASN_ALT_NAME_E;
            }
        }
    #endif

        ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len,
                          ASN_URI_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    #if defined(WOLFSSL_QT) || defined(OPENSSL_ALL) || \
                                            defined(WOLFSSL_IP_ALT_NAME)
    /* GeneralName choice: iPAddress
     *
     * Asymmetric with the X.509 DecodeGeneralName path on purpose:
     * attribute-certificate names (RFC 5755) are not consumed by
     * ConfirmNameConstraints, which only walks DecodedCert lists. These
     * entries flow into AC holder/issuer name fields where the iPAddress
     * is only consumed by callers that opt in (Qt, OpenSSL_ALL, or the
     * IP-SAN compat layer). If iPAddress name-constraint enforcement is
     * ever extended to attribute certificates, this gate must drop. */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) {
        ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len,
                          ASN_IP_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    #endif /* WOLFSSL_QT || OPENSSL_ALL || WOLFSSL_IP_ALT_NAME */

    #ifdef OPENSSL_ALL
    /* GeneralName choice: registeredID */
    else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) {
        ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len,
                ASN_RID_TYPE, entries);
        if (ret == 0) {
            idx += (word32)len;
        }
    }
    #endif
#endif /* IGNORE_NAME_CONSTRAINTS */
    /* GeneralName choice: dNSName, x400Address, ediPartyName */
    else {
        WOLFSSL_MSG("\tUnsupported name type, skipping");
        idx += (word32)len;
    }

    if (ret == 0) {
        /* Return index of next encoded byte. */
        *inOutIdx = idx;
    }
    return ret;
}

/* Decode General Names from an ACERT input.
 *
 * @param [in]      input    Buffer holding encoded data.
 * @param [in]      sz       Size of encoded data in bytes.
 * @param [in]      tag      ASN.1 tag value expected in header.
 * @param [in, out] acert    Decoded attribute certificate object.
 * @param [in, out] entries  Linked list of DNS name entries.
 *
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 */
static int DecodeAcertGeneralNames(const byte* input, word32 sz,
                                   byte tag, DecodedAcert* acert,
                                   DNS_entry** entries)
{
    word32 idx = 0;
    int    length = 0;
    int    ret = 0;
    word32 numNames = 0;

    if (GetASNHeader(input, tag, &idx, &length, sz) <= 0) {
        WOLFSSL_MSG("error: acert general names: bad header");
        return ASN_PARSE_E;
    }

    if (length == 0) {
        WOLFSSL_MSG("error: acert general names: zero length");
        return ASN_PARSE_E;
    }

    if ((word32)length + idx != sz) {
        #ifdef DEBUG_WOLFSSL
        WOLFSSL_MSG_EX("error: acert general names: got %d, expected %d",
                       (word32)length + idx, sz);
        #endif
        return ASN_PARSE_E;
    }

    while ((ret == 0) && (idx < sz)) {
        ASNGetData dataASN[altNameASN_Length];

        /* Not sure what a reasonable max would be for attribute certs,
         * therefore observing WOLFSSL_MAX_ALT_NAMES limit. */
        numNames++;
        if (numNames > WOLFSSL_MAX_ALT_NAMES) {
            #ifdef DEBUG_WOLFSSL
            WOLFSSL_MSG_EX("error: acert general names: too many names, %d",
                           numNames);
            #endif
            ret = ASN_ALT_NAME_E;
            break;
        }

        /* Clear dynamic data items. */
        XMEMSET(dataASN, 0, sizeof(dataASN));
        /* Parse GeneralName with the choices supported. */
        GetASN_Choice(&dataASN[ALTNAMEASN_IDX_GN], generalNameChoice);
        /* Decode a GeneralName choice. */
        ret = GetASN_Items(altNameASN, dataASN, altNameASN_Length, 0, input,
                           &idx, sz);

        if (ret != 0) {
            break;
        }

        ret = DecodeAcertGeneralName(input, &idx,
                                     dataASN[ALTNAMEASN_IDX_GN].tag,
                                     (int)dataASN[ALTNAMEASN_IDX_GN].length,
                                     acert, entries);
    }

    return ret;
}

/* Holder has three potential forms:
 *   Holder ::= SEQUENCE {
 *     baseCertificateID   [0] IssuerSerial OPTIONAL,
 *         -- the issuer and serial number of
 *         -- the holder's Public Key Certificate
 *     entityName          [1] GeneralNames OPTIONAL,
 *         -- the name of the claimant or role
 *     objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
 *         -- used to directly authenticate the holder,
 *         -- for example, an executable
 *   }
 *
 * where IssuerSerial is:
 *   IssuerSerial  ::=  SEQUENCE {
 *     issuer         GeneralNames,
 *     serial         CertificateSerialNumber,
 *     issuerUID      UniqueIdentifier OPTIONAL
 *   }
 *
 * Note:
 *   - Holder Option 2 objectDigestInfo is not mandatory
 *     for the spec and is not implemented here yet.
 *
 *   - issuerUniqueID not supported yet.
 * */
static const ASNItem HolderASN[] =
{
                     /* Holder root sequence. */
/* HOLDER_SEQ  */    { 0, ASN_SEQUENCE, 1, 1, 0 },
                         /* Holder Option 0:*/
                         /* baseCertificateID   [0] IssuerSerial OPTIONAL */
/* ISSUERSERIAL_SEQ  */  { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 2 },
                             /* issuer         GeneralNames, */
/* GN_SEQ  */                { 2, ASN_SEQUENCE, 1, 0, 0 },
                             /* serial     CertificateSerialNumber */
/* SERIAL_INT  */            { 2, ASN_INTEGER, 0, 0, 0 },
                         /* Holder Option 1: */
                         /* entityName          [1] GeneralNames OPTIONAL */
/* ENTITYNAME_SEQ  */  { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 1, 2 },
};

enum {
    HOLDER_IDX_SEQ = 0,
    HOLDER_IDX_ISSUERSERIAL_SEQ,
    HOLDER_IDX_GN_SEQ,
    HOLDER_IDX_SERIAL_INT,
    HOLDER_IDX_GN_SEQ_OPT1
};

/* Number of items in ASN template for an X509 Acert. */
#define HolderASN_Length (sizeof(HolderASN) / sizeof(ASNItem))

/* Decode the Holder field of an x509 attribute certificate.
 *
 * @param [in]      input     Buffer containing encoded Holder field.
 * @param [in]      len       Length of Holder field.
 * @param [in, out] acert     Decoded attribute certificate object.
 *
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * */
static int DecodeHolder(const byte* input, word32 len, DecodedAcert* acert)
{
    DECL_ASNGETDATA(dataASN, HolderASN_Length);
    int    ret = 0;
    word32 idx = 0;
    word32 holderSerialSz = 0;

    if (input == NULL || len <= 0 || acert == NULL) {
        return BUFFER_E;
    }

    #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
    printf("debug: decode holder: holder len: %d\n", len);
    #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */

    CALLOC_ASNGETDATA(dataASN, HolderASN_Length, ret, acert->heap);

    if (ret != 0) {
        FREE_ASNGETDATA(dataASN, acert->heap);
        return MEMORY_E;
    }

    holderSerialSz = EXTERNAL_SERIAL_SIZE;

    GetASN_Buffer(&dataASN[HOLDER_IDX_SERIAL_INT], acert->holderSerial,
                  &holderSerialSz);

    ret = GetASN_Items(HolderASN, dataASN, HolderASN_Length, 0, input,
                       &idx, len);

    if (ret != 0) {
        WOLFSSL_MSG("error: Holder: GetASN_Items failed");
        FREE_ASNGETDATA(dataASN, acert->heap);
        return ret;
    }

    if (dataASN[HOLDER_IDX_SERIAL_INT].tag != 0) {
        acert->holderSerialSz = (int)holderSerialSz;
    }
    else {
        acert->holderSerialSz = 0;
    }

    {
        /* Now parse the GeneralNames field.
         * Use the HOLDER_IDX_GN_SEQ offset for input. */
        const byte * gn_input = NULL;
        word32       gn_len = 0;
        byte         tag = 0x00;

        /* Determine which tag was seen. */
        if (dataASN[HOLDER_IDX_GN_SEQ].tag != 0) {
            gn_input = input + dataASN[HOLDER_IDX_GN_SEQ].offset;
            gn_len = dataASN[HOLDER_IDX_GN_SEQ].length;
            tag = dataASN[HOLDER_IDX_GN_SEQ].tag;

            if (gn_len >= ASN_LONG_LENGTH) {
                gn_len += 3;
            }
            else {
                gn_len += 2;
            }

            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            printf("debug: decode holder: holder index: %d\n",
                   HOLDER_IDX_GN_SEQ);
            #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */

            ret = DecodeAcertGeneralNames(gn_input, gn_len, tag, acert,
                                          &acert->holderIssuerName);
        }

        if (dataASN[HOLDER_IDX_GN_SEQ_OPT1].tag != 0) {
            gn_input = input + dataASN[HOLDER_IDX_GN_SEQ_OPT1].offset;
            gn_len = dataASN[HOLDER_IDX_GN_SEQ_OPT1].length;
            tag = dataASN[HOLDER_IDX_GN_SEQ_OPT1].tag;

            if (gn_len >= ASN_LONG_LENGTH) {
                gn_len += 3;
            }
            else {
                gn_len += 2;
            }

            #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
            printf("debug: decode holder: holder index: %d\n",
                   HOLDER_IDX_GN_SEQ_OPT1);
            #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */

            ret = DecodeAcertGeneralNames(gn_input, gn_len, tag, acert,
                                          &acert->holderEntityName);
        }

        if (ret != 0) {
            WOLFSSL_MSG("error: Holder: DecodeAcertGeneralNames failed");
            FREE_ASNGETDATA(dataASN, acert->heap);
            return ret;
        }
    }

    FREE_ASNGETDATA(dataASN, acert->heap);
    return 0;
}

/* Note on AttCertIssuer field. ACERTs are supposed to follow
 * v2form, but some (acert_bc1.pem) follow v1form. Because
 * of the limited set of example ACERTs, the v1form will be
 * tolerated for now but the field will not be parsed.
 *
 * More info from RFC below:
 *
 * From RFC 5755.
 * 4.2.3.  Issuer
 *
 * ACs conforming to this profile MUST use the v2Form choice, which MUST
 * contain one and only one GeneralName in the issuerName, which MUST
 * contain a non-empty distinguished name in the directoryName field.
 * This means that all AC issuers MUST have non-empty distinguished
 * names.  ACs conforming to this profile MUST omit the
 * baseCertificateID and objectDigestInfo fields.
 *
 * 4.1.  X.509 Attribute Certificate Definition
 *
 * AttCertIssuer ::= CHOICE {
 *   v1Form      GeneralNames,  -- MUST NOT be used in this
 *                              -- profile
 *   v2Form  [0] V2Form         -- v2 only
 * }
 *
 * V2Form ::= SEQUENCE {
 *   issuerName             GeneralNames  OPTIONAL,
 *   baseCertificateID  [0] IssuerSerial  OPTIONAL,
 *   objectDigestInfo   [1] ObjectDigestInfo  OPTIONAL
 *          -- issuerName MUST be present in this profile
 *          -- baseCertificateID and objectDigestInfo MUST
 *          -- NOT be present in this profile
 * }
 * */
static const ASNItem AttCertIssuerASN[] =
{
                           /* V2Form ::= SEQUENCE { */
/* AttCertIssuer_GN_SEQ */ { 0, ASN_SEQUENCE, 1, 0, 0 },
};

enum {
    ATTCERTISSUER_IDX_GN_SEQ
};

/* Number of items in ASN template for an X509 Acert. */
#define AttCertIssuerASN_Length (sizeof(AttCertIssuerASN) / sizeof(ASNItem))

/* Decode the AttCertIssuer Field of an x509 attribute certificate.
 *
 * @param [in]      input     Buffer containing encoded AttCertIssuer field.
 * @param [in]      len       Length of Holder field.
 * @param [in,out]  acert     Decoded attribute certificate object.
 *
 * @return  0 on success.
 * @return  ASN_PARSE_E when BER encoded data does not match ASN.1 items or
 *          is invalid.
 * @return  BUFFER_E when data in buffer is too small.
 * @return  ASN_UNKNOWN_OID_E when the OID cannot be verified.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * */
static int DecodeAttCertIssuer(const byte* input, word32 len,
                               DecodedAcert* cert)
{
    DECL_ASNGETDATA(dataASN, AttCertIssuerASN_Length);
    int          ret = 0;
    word32       idx = 0;
    const byte * gn_input = NULL;
    word32       gn_len = 0;
    byte         tag = 0x00;

    if (input == NULL || len <= 0 || cert == NULL) {
        return BUFFER_E;
    }

    CALLOC_ASNGETDATA(dataASN, AttCertIssuerASN_Length, ret, cert->heap);

    if (ret != 0) {
        return MEMORY_E;
    }

    ret = GetASN_Items(AttCertIssuerASN, dataASN, AttCertIssuerASN_Length,
                       0, input, &idx, len);

    if (ret != 0) {
        FREE_ASNGETDATA(dataASN, cert->heap);
        WOLFSSL_MSG("error: AttCertIssuer: GetASN_Items failed");
        return ret;
    }

    /* Now parse the GeneralNames field.
     * Use the HOLDER_IDX_GN_SEQ offset for input. */
    gn_input = input + dataASN[ATTCERTISSUER_IDX_GN_SEQ].offset;
    gn_len = dataASN[ATTCERTISSUER_IDX_GN_SEQ].length;
    tag = dataASN[ATTCERTISSUER_IDX_GN_SEQ].tag;

    if (gn_len >= ASN_LONG_LENGTH) {
        gn_len += 3;
    }
    else {
        gn_len += 2;
    }

    ret = DecodeAcertGeneralNames(gn_input, gn_len, tag, cert,
                                  &cert->AttCertIssuerName);

    if (ret != 0) {
        FREE_ASNGETDATA(dataASN, cert->heap);
        WOLFSSL_MSG("error: AttCertIssuer: DecodeAcertGeneralNames failed");
        return ret;
    }

    FREE_ASNGETDATA(dataASN, cert->heap);
    return 0;
}


/* ASN template for an X509 Attribute Certificate,
 * from RFC 5755
 */
static const ASNItem AcertASN[] =
{
                          /* AttributeCertificate ::= SEQUENCE */
/* SEQ                */  { 0, ASN_SEQUENCE, 1, 1, 0 },
                              /* AttributeCertificateInfo ::= SEQUENCE  */
/* ACINFO_SEQ         */      { 1, ASN_SEQUENCE, 1, 1, 0 },
                                  /* AttCertVersion ::= INTEGER { v2(1) } */
/* ACINFO_VER_INT     */          { 2, ASN_INTEGER, 0, 0, 0 },
                                  /* holder   Holder */
/* ACINFO_HOLDER_SEQ  */          { 2, ASN_SEQUENCE, 1, 0, 0 },
                                  /* issuer   AttCertIssuer */
                                  /* v2Form   [0] V2Form  */
/* ACINFO_ISSUER_V2FORM */        { 2, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 2 },
                                  /* v1Form   GeneralNames */
/* ACINFO_ISSUER_V1FORM  */       { 2, ASN_SEQUENCE, 1, 0, 2 },
                                  /* signature    AlgorithmIdentifier */
                                  /* AlgorithmIdentifier ::= SEQUENCE */
/* ACINFO_ALGOID_SEQ */           { 2, ASN_SEQUENCE, 1, 1, 0 },
                                      /* Algorithm    OBJECT IDENTIFIER */
/* ACINFO_ALGOID_OID */               { 3, ASN_OBJECT_ID, 0, 0, 0 },
                                      /* parameters */
/* ACINFO_ALGOID_PARAMS_NULL */       { 3, ASN_TAG_NULL, 0, 0, 2 },
#ifdef WC_RSA_PSS
/* ACINFO_ALGOID_PARAMS */            { 3, ASN_SEQUENCE, 1, 0, 2 },
#endif
                                  /* CertificateSerialNumber ::= INTEGER */
/* ACINFO_SERIAL        */        { 2, ASN_INTEGER, 0, 0, 0 },
                                  /* Validity ::= SEQUENCE */
/* ACINFO_VALIDITY_SEQ      */    { 2, ASN_SEQUENCE, 1, 1, 0 },
                                      /* notBeforeTime  GeneralizedTime,  */
/* ACINFO_VALIDITY_NOTB_GT  */        { 3, ASN_GENERALIZED_TIME, 0, 0, 2 },
                                      /* notAfterTime   GeneralizedTime */
/* ACINFO_VALIDITY_NOTA_GT  */        { 3, ASN_GENERALIZED_TIME, 0, 0, 3 },
                                  /* attributes        SEQUENCE OF Attribute */
/* ACINFO_ATTRIBUTES_SEQ    */    { 2, ASN_SEQUENCE, 1, 0, 0 },
                                  /* issuerUniqueID    OPTIONAL, */
/* ACINFO_UNIQUE_ID         */    { 2, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 },
                                  /* extensions        OPTIONAL */
/* ACINFO_EXT               */    { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 1, 1 },
/* ACINFO_EXT_SEQ           */    { 2, ASN_SEQUENCE, 1, 0, 1 },
                              /* signature    AlgorithmIdentifier */
                              /* AlgorithmIdentifier ::= SEQUENCE */
/* SIGALGO_SEQ         */     { 1, ASN_SEQUENCE, 1, 1, 0 },
                                  /* Algorithm    OBJECT IDENTIFIER */
/* SIGALGO_OID         */         { 2, ASN_OBJECT_ID, 0, 0, 0 },
                                  /* parameters */
/* SIGALGO_PARAMS_NULL */         { 2, ASN_TAG_NULL, 0, 0, 2 },
#ifdef WC_RSA_PSS
/* SIGALGO_PARAMS      */         { 2, ASN_SEQUENCE, 1, 0, 2 },
#endif
                              /* signature    BIT STRING */
/* SIGNATURE           */     { 1, ASN_BIT_STRING, 0, 0, 0 },
};

enum {
    ACERT_IDX_SEQ = 0,
    ACERT_IDX_ACINFO_SEQ,
    ACERT_IDX_ACINFO_VER_INT,
    /* ACINFO holder and issuer */
    ACERT_IDX_ACINFO_HOLDER_SEQ,
    /* The issuer should be in V2 form, but tolerate V1 for now. */
    ACERT_IDX_ACINFO_ISSUER_V2,
    ACERT_IDX_ACINFO_ISSUER_V1,
    /* ACINFO sig alg*/
    ACERT_IDX_ACINFO_ALGOID_SEQ,
    ACERT_IDX_ACINFO_ALGOID_OID,
    ACERT_IDX_ACINFO_ALGOID_PARAMS_NULL,
#ifdef WC_RSA_PSS
    /* Additional RSA-PSS params. */
    ACERT_IDX_ACINFO_ALGOID_PARAMS,
#endif
    /* serial number */
    ACERT_IDX_ACINFO_SERIAL,
    /* validity time */
    ACERT_IDX_ACINFO_VALIDITY_SEQ,
    ACERT_IDX_ACINFO_VALIDITY_NOTB_GT,
    ACERT_IDX_ACINFO_VALIDITY_NOTA_GT,
    /* attributes */
    ACERT_IDX_ACINFO_ATTRIBUTES_SEQ,
    /* unique identifier */
    ACERT_IDX_ACINFO_UNIQUE_ID,
    /* extensions */
    ACERT_ACINFO_EXT,
    ACERT_ACINFO_EXT_SEQ,
    /* sig alg */
    ACERT_IDX_SIGALGO_SEQ,
    ACERT_IDX_SIGALGO_OID,
    ACERT_IDX_SIGALGO_PARAMS_NULL,
#ifdef WC_RSA_PSS
    /* Additional RSA-PSS params. */
    ACERT_IDX_SIGALGO_PARAMS,
#endif
    /* signature */
    ACERT_IDX_SIGNATURE,
    WOLF_ENUM_DUMMY_LAST_ELEMENT(ACERT_IDX)
};

/* Number of items in ASN template for an X509 Acert. */
#define AcertASN_Length (sizeof(AcertASN) / sizeof(ASNItem))

/* Initial implementation for parsing and verifying an
 * X509 Attribute Certificate (RFC 5755).
 *
 * At present these fields are NOT parsed:
 *   - issuerUniqueID
 *   - extensions
 *   - attributes
 *
 * @param [in, out] acert   Decoded attribute certificate object.
 * @param [in]      verify  Whether to verify dates.
 * @return  0 on success.
 * @return  negative error code on error/fail.
 * */
int ParseX509Acert(DecodedAcert* acert, int verify)
{
    DECL_ASNGETDATA(dataASN, AcertASN_Length);
    int    ret = 0;
    word32 idx = 0;
    int    badDate = 0;
    byte   version = 0;
    word32 serialSz = EXTERNAL_SERIAL_SIZE;

    WOLFSSL_MSG("ParseX509Acert");

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

    CALLOC_ASNGETDATA(dataASN, AcertASN_Length, ret, acert->heap);

    if (ret != 0) {
        return MEMORY_E;
    }

    /* Get the version and put the serial number into the buffer. */
    GetASN_Int8Bit(&dataASN[ACERT_IDX_ACINFO_VER_INT], &version);

    GetASN_Buffer(&dataASN[ACERT_IDX_ACINFO_SERIAL], acert->serial,
                  &serialSz);

    /* Check OID types for signature algorithm. */
    GetASN_OID(&dataASN[ACERT_IDX_ACINFO_ALGOID_OID], oidSigType);
    GetASN_OID(&dataASN[ACERT_IDX_SIGALGO_OID], oidSigType);

    /* Parse the X509 certificate. */
    ret = GetASN_Items(AcertASN, dataASN, AcertASN_Length, 1,
                       acert->source, &acert->srcIdx, acert->maxIdx);

    if (ret != 0) {
        FREE_ASNGETDATA(dataASN, acert->heap);
        return ret;
    }

    /* Check version is valid/supported - can't be negative. */
    if (version > MAX_X509_VERSION) {
        FREE_ASNGETDATA(dataASN, acert->heap);
        WOLFSSL_MSG("Unexpected attribute certificate version");
        WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
        return ASN_PARSE_E;
    }

    acert->version = version;
    acert->serialSz = (int)serialSz;

    acert->signatureOID = dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum;
    acert->certBegin = dataASN[ACERT_IDX_ACINFO_SEQ].offset;

    /* check BEFORE date. */
    idx = ACERT_IDX_ACINFO_VALIDITY_NOTB_GT;
    if (CheckDate(&dataASN[idx], BEFORE) < 0) {
        if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE) &&
            (! AsnSkipDateCheck))
        {
            badDate = ASN_BEFORE_DATE_E;
        }
    }

    /* Store reference to BEFORE date. */
    acert->beforeDate = GetASNItem_Addr(dataASN[idx], acert->source);
    acert->beforeDateLen = (int)GetASNItem_Length(dataASN[idx], acert->source);

    /* check AFTER date. */
    idx = ACERT_IDX_ACINFO_VALIDITY_NOTA_GT;
    if (CheckDate(&dataASN[idx], AFTER) < 0) {
        if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE) &&
            (! AsnSkipDateCheck))
        {
            badDate = ASN_BEFORE_DATE_E;
        }
    }

    /* Store reference to AFTER date. */
    acert->afterDate = GetASNItem_Addr(dataASN[idx], acert->source);
    acert->afterDateLen = (int)GetASNItem_Length(dataASN[idx], acert->source);

    /* Store the signature information. */
    acert->sigIndex = dataASN[ACERT_IDX_SIGALGO_SEQ].offset;
    GetASN_GetConstRef(&dataASN[ACERT_IDX_SIGNATURE],
                       &acert->signature, &acert->sigLength);

    /* Make sure 'signature' and 'signatureAlgorithm' are the same. */
    if (dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum != acert->signatureOID) {
        FREE_ASNGETDATA(dataASN, acert->heap);
        WOLFSSL_ERROR_VERBOSE(ASN_SIG_OID_E);
        return ASN_SIG_OID_E;
    }

    /* Parameters not allowed after ECDSA or EdDSA algorithm OID. */
    if (IsSigAlgoNoParams(acert->signatureOID)) {
        if ((dataASN[ACERT_IDX_SIGALGO_PARAMS_NULL].tag != 0)
    #ifdef WC_RSA_PSS
            || (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0)
    #endif
            ) {
            FREE_ASNGETDATA(dataASN, acert->heap);
            WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
            return ASN_PARSE_E;
        }
    }

    #ifdef WC_RSA_PSS
    /* Check parameters starting with a SEQUENCE. */
    if (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0) {
        word32       oid = dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum;
        word32       sigAlgParamsSz = 0;
        const byte * acParams = NULL;
        word32       acParamsSz = 0;
        const byte * sigAlgParams = NULL;

        /* Parameters only with RSA PSS. */
        if (oid != CTC_RSASSAPSS) {
            FREE_ASNGETDATA(dataASN, acert->heap);
            WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
            return ASN_PARSE_E;
        }

        /* Check RSA PSS parameters are the same. */
        acParams = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS],
                                   acert->source);
        acParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS],
                                       acert->source);
        sigAlgParams = GetASNItem_Addr(dataASN[ACERT_IDX_SIGALGO_PARAMS],
                                       acert->source);
        sigAlgParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_SIGALGO_PARAMS],
                                           acert->source);

        if ((acParamsSz != sigAlgParamsSz) ||
            (XMEMCMP(acParams, sigAlgParams, acParamsSz) != 0)) {

            FREE_ASNGETDATA(dataASN, acert->heap);
            WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
            return ASN_PARSE_E;
        }

        /* Store RSA PSS parameters for use in signature verification. */
        acert->sigParamsIndex = dataASN[ACERT_IDX_SIGALGO_PARAMS].offset;
        acert->sigParamsLength = sigAlgParamsSz;
    }
    #endif

    /* Store the raw Attributes field. */
    GetASN_GetConstRef(&dataASN[ACERT_IDX_ACINFO_ATTRIBUTES_SEQ],
                       &acert->rawAttr, &acert->rawAttrLen);

    {
        /* Now parse the Holder and AttCertIssuer fields.
         * Use the ACINFO holder and issuer sequence offset for input. */
        const byte * holder_input = NULL;
        word32       holder_len = 0;
        const byte * issuer_input = NULL;
        word32       issuer_len = 0;
        word32       i_holder = ACERT_IDX_ACINFO_HOLDER_SEQ;
        word32       i_issuer = 0;

        /* Determine which issuer tag was seen. We need this to determine
         * the holder_input. */
        i_issuer = (dataASN[ACERT_IDX_ACINFO_ISSUER_V2].tag != 0) ?
                    ACERT_IDX_ACINFO_ISSUER_V2 : ACERT_IDX_ACINFO_ISSUER_V1;

        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        printf("debug: parse acert: issuer index: %d\n", i_issuer);
        printf("debug: parse acert: issuer seq offset: %d\n",
               dataASN[i_issuer].offset);
        #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */

        holder_input = acert->source + dataASN[i_holder].offset;
        holder_len = dataASN[i_issuer].offset - dataASN[i_holder].offset;

        ret = DecodeHolder(holder_input, holder_len, acert);

        if (ret != 0) {
            FREE_ASNGETDATA(dataASN, acert->heap);
            return ret;
        }

        GetASN_GetConstRef(&dataASN[i_issuer], &issuer_input, &issuer_len);

        if (i_issuer == ACERT_IDX_ACINFO_ISSUER_V2 && issuer_len > 0) {
            /* Try to decode the AttCertIssuer as well. */
            ret = DecodeAttCertIssuer(issuer_input, issuer_len, acert);

            if (ret != 0) {
                FREE_ASNGETDATA(dataASN, acert->heap);
                return ret;
            }
        }
        #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
        else {
            printf("debug: parse acert: unsupported issuer format: %d, %d\n",
                   i_issuer, issuer_len);
        }
        #endif /* WOLFSSL_DEBUG_ASN_TEMPLATE */
    }

    if (badDate) {
        if ((verify != NO_VERIFY) && (verify != VERIFY_SKIP_DATE)) {
            ret = badDate;
        }
    }

    FREE_ASNGETDATA(dataASN, acert->heap);
    return ret;
}

/* Given the parsed attribute cert info, verify the signature.
 * The sigCtx is alloced and freed here.
 *
 * @param [in]      acinfo        the parsed acinfo sequence
 * @param [in]      acinfoSz      the parsed acinfo sequence length
 * @param [in]      pubKey        public key
 * @param [in]      pubKeySz      public key length
 * @param [in]      pubKeyOID     public key oid
 * @param [in]      sig           the parsed signature
 * @param [in]      sigSz         the parsed signature length
 * @param [in]      sigOID        the parsed signature OID
 * @param [in]      sigParams     the parsed signature RSA-PSS params
 * @param [in]      sigParamsSz   the parsed signature RSA-PSS params length
 * @param [in]      heap          heap hint
 *
 * @return  0   on verify success
 * @return  < 0 on error
 * */
static int acert_sig_verify(const byte * acinfo, word32 acinfoSz,
                            const byte * pubKey, word32 pubKeySz,
                            int pubKeyOID, const byte * sig, word32 sigSz,
                            word32 sigOID, const byte * sigParams,
                            word32 sigParamsSz, void * heap)
{
    WC_DECLARE_VAR(sigCtx, SignatureCtx, 1, 0);
    int            ret = 0;

    #ifdef WOLFSSL_SMALL_STACK
    sigCtx = (SignatureCtx*)XMALLOC(sizeof(*sigCtx), heap,
                                    DYNAMIC_TYPE_SIGNATURE);
    if (sigCtx == NULL) {
        WOLFSSL_MSG("error: VerifyX509Acert: malloc sigCtx failed");
        return MEMORY_E;
    }
    #endif

    InitSignatureCtx(sigCtx, heap, INVALID_DEVID);

    /* Check x509 acert signature. */
    ret = ConfirmSignature(sigCtx, acinfo, acinfoSz, pubKey, pubKeySz,
                           (word32)pubKeyOID, sig, sigSz, sigOID,
                           sigParams, sigParamsSz, NULL);

    if (ret == WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E)) {
        WOLFSSL_MSG("info: VerifyX509Acert: confirm signature failed");
    }

    FreeSignatureCtx(sigCtx);
    #ifdef WOLFSSL_SMALL_STACK
    XFREE(sigCtx, heap, DYNAMIC_TYPE_SIGNATURE);
    sigCtx = NULL;
    #endif

    return ret;
}

/* Verify the X509 ACERT signature, using the given pubkey.
 *
 * @param [in]      der         input acert in der format
 * @param [in]      derSz       acert length
 * @param [in]      pubKey      public key
 * @param [in]      pubKeySz    public key length
 * @param [in]      pubKeyOID   public key oid
 * @param [in]      heap        heap hint
 *
 * @return  0   on success
 * @return  < 0 on error
 * */
int VerifyX509Acert(const byte* der, word32 derSz,
                    const byte* pubKey, word32 pubKeySz, int pubKeyOID,
                    void * heap)
{
    DECL_ASNGETDATA(dataASN, AcertASN_Length);
    word32         idx = 0;
    int            ret = 0;
    const byte *   acinfo = NULL; /* The acinfo sequence. */
    word32         acinfoSz = 0;  /* The acinfo sequence length. */
#ifdef WC_RSA_PSS
    const byte *   acParams = NULL;
    word32         acParamsSz = 0;
#endif
    const byte *   sig = NULL;
    word32         sigSz = 0;
    word32         sigOID = 0;
    const byte *   sigParams = NULL;
    word32         sigParamsSz = 0;

    WOLFSSL_MSG("VerifyX509Acert");

    if (der == NULL || pubKey == NULL || derSz == 0 || pubKeySz == 0) {
        WOLFSSL_MSG("error: VerifyX509Acert: bad args");
        return BAD_FUNC_ARG;
    }

    CALLOC_ASNGETDATA(dataASN, AcertASN_Length, ret, heap);

    if (ret != 0) {
        WOLFSSL_MSG("error: VerifyX509Acert: calloc dataASN failed");
        return MEMORY_E;
    }

    /* Check OID types for signature algorithm. */
    GetASN_OID(&dataASN[ACERT_IDX_ACINFO_ALGOID_OID], oidSigType);
    GetASN_OID(&dataASN[ACERT_IDX_SIGALGO_OID], oidSigType);

    /* Parse the X509 certificate. */
    ret = GetASN_Items(AcertASN, dataASN, AcertASN_Length, 1,
                       der, &idx, derSz);

    if (ret != 0) {
        WOLFSSL_MSG("error: VerifyX509Acert: GetASN_Items failed");
        FREE_ASNGETDATA(dataASN, heap);
        return ret;
    }

    /* Check signature OIDs match. */
    if (dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum
        != dataASN[ACERT_IDX_SIGALGO_OID].data.oid.sum) {
        WOLFSSL_MSG("error: VerifyX509Acert: sig OID mismatch");
        FREE_ASNGETDATA(dataASN, heap);
        return ASN_SIG_OID_E;
    }

    /* Get the attribute certificate info. */
    acinfo = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_SEQ], der);
    acinfoSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_SEQ], der);

    if (acinfo == NULL || acinfoSz == 0) {
        WOLFSSL_MSG("error: VerifyX509Acert: empty acinfo");
        FREE_ASNGETDATA(dataASN, heap);
        return ASN_PARSE_E;
    }

    /* Get acert signature and sig info. */
    sigOID = dataASN[ACERT_IDX_ACINFO_ALGOID_OID].data.oid.sum;
    #ifdef WC_RSA_PSS
    if (dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS].tag != 0) {
        acParams = GetASNItem_Addr(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS],
                                  der);
        acParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_ACINFO_ALGOID_PARAMS],
                                       der);
    }
    if (dataASN[ACERT_IDX_SIGALGO_PARAMS].tag != 0) {
        sigParams = GetASNItem_Addr(dataASN[ACERT_IDX_SIGALGO_PARAMS], der);
        sigParamsSz = GetASNItem_Length(dataASN[ACERT_IDX_SIGALGO_PARAMS],
                                        der);
    }
    #endif

    GetASN_GetConstRef(&dataASN[ACERT_IDX_SIGNATURE], &sig, &sigSz);

    #ifdef WC_RSA_PSS
    if (acParamsSz != sigParamsSz) {
        ret = ASN_PARSE_E;
    }
    else if ((acParamsSz > 0) && (sigOID != CTC_RSASSAPSS)) {
        ret = ASN_PARSE_E;
    }
    else if ((acParamsSz > 0) &&
             (XMEMCMP(acParams, sigParams, acParamsSz) != 0)) {
        ret = ASN_PARSE_E;
    }
    #endif

    if (ret == 0) {
        /* Finally, do the verification. */
        ret = acert_sig_verify(acinfo, acinfoSz,
                               pubKey, pubKeySz, pubKeyOID,
                               sig, sigSz, sigOID, sigParams, sigParamsSz,
                               heap);
    }

    FREE_ASNGETDATA(dataASN, heap);
    return ret;
}

/**
 * Wrapper API to expose Acert ASN functions. See Acert ASN functions
 * for comments.
 * */
void wc_InitDecodedAcert(DecodedAcert* acert, const byte* source, word32 inSz,
                         void* heap)
{
    InitDecodedAcert(acert, source, inSz, heap);
}

void wc_FreeDecodedAcert(DecodedAcert * acert)
{
    FreeDecodedAcert(acert);
}

int wc_ParseX509Acert(DecodedAcert* acert, int verify)
{
    return ParseX509Acert(acert, verify);
}

int wc_VerifyX509Acert(const byte* acert, word32 acertSz,
                       const byte* pubKey, word32 pubKeySz,
                       int pubKeyOID, void * heap)
{
    return VerifyX509Acert(acert, acertSz, pubKey, pubKeySz,
                           pubKeyOID, heap);
}

#endif /* WOLFSSL_ACERT && WOLFSSL_ASN_TEMPLATE */

#ifdef HAVE_OCSP_RESPONDER
int AsnHashesHash(AsnHashes* hashes, const byte* data, word32 dataSz)
{
    int ret = 0;

    if (hashes == NULL || data == NULL)
        ret = BAD_FUNC_ARG;
#if !defined(NO_MD5)
    if (ret == 0)
        ret = wc_Md5Hash(data, dataSz, hashes->md5);
#endif
#if !defined(NO_SHA)
    if (ret == 0)
        ret = wc_ShaHash(data, dataSz, hashes->sha);
#endif
#ifndef NO_SHA256
    if (ret == 0)
        ret = wc_Sha256Hash(data, dataSz, hashes->sha256);
#endif
#ifdef WOLFSSL_SHA384
    if (ret == 0)
        ret = wc_Sha384Hash(data, dataSz, hashes->sha384);
#endif
#ifdef WOLFSSL_SHA512
    if (ret == 0)
        ret = wc_Sha512Hash(data, dataSz, hashes->sha512);
#endif
#ifdef WOLFSSL_SM3
    if (ret == 0)
        ret = wc_Sm3Hash(data, dataSz, hashes->sm3);
#endif

    return ret;
}

const byte* AsnHashesGetHash(const AsnHashes* hashes, int hashAlg, int* size)
{
    if (hashes == NULL || size == NULL)
        return NULL;

    switch (hashAlg) {
#if !defined(NO_SHA)
        case SHAh:
            *size = WC_SHA_DIGEST_SIZE;
            return hashes->sha;
#endif
#ifndef NO_SHA256
        case SHA256h:
            *size = WC_SHA256_DIGEST_SIZE;
            return hashes->sha256;
#endif
#ifdef WOLFSSL_SHA384
        case SHA384h:
            *size = WC_SHA384_DIGEST_SIZE;
            return hashes->sha384;
#endif
#ifdef WOLFSSL_SHA512
        case SHA512h:
            *size = WC_SHA512_DIGEST_SIZE;
            return hashes->sha512;
#endif
#ifdef WOLFSSL_SM3
        case SM3h:
            *size = WC_SM3_DIGEST_SIZE;
            return hashes->sm3;
#endif
        default:
            *size = 0;
            return NULL;
    }
}
#endif /* HAVE_OCSP_RESPONDER */

#ifdef WOLFSSL_SEP


#endif /* WOLFSSL_SEP */

#undef ERROR_OUT
