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

/* Implementation based on:
 *   RFC 8554: Leighton-Micali Hash-Based Signatures
 *   https://datatracker.ietf.org/doc/html/rfc8554
 * Implementation by Sean Parkinson.
 */

/* Possible LMS options:
 *
 * WC_LMS_FULL_HASH                                      Default: OFF
 *   Performs a full hash instead of assuming internals.
 *   Enable when using hardware SHA-256.
 * WOLFSSL_LMS_VERIFY_ONLY                               Default: OFF
 *   Only compiles in verification code.
 * WOLFSSL_WC_LMS_SMALL                                  Default: OFF
 *   Implementation is smaller code size with slow signing.
 *   Enable when memory is limited.
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

#include <wolfssl/wolfcrypt/wc_lms.h>

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

#ifdef WOLFSSL_HAVE_LMS

/* Length of R in bytes. */
#define LMS_R_LEN           4U
/* Length of D in bytes. */
#define LMS_D_LEN           2U
/* Length of checksum in bytes. */
#define LMS_CKSM_LEN        2

/* Predefined values used in hashes to make them unique. */
/* Fixed value for calculating x. */
#define LMS_D_FIXED         0xff
/* D value when computing public key. */
#define LMS_D_PBLC          0x8080
/* D value when computing message. */
#define LMS_D_MESG          0x8181
/* D value when computing leaf node. */
#define LMS_D_LEAF          0x8282
/* D value when computing interior node. */
#define LMS_D_INTR          0x8383
/* D value when computing C, randomizer value. */
#define LMS_D_C             0xfffd
/* D value when computing child SEED for private key. */
#define LMS_D_CHILD_SEED    0xfffe
/* D value when computing child I for private key. */
#define LMS_D_CHILD_I       0xffff

/* Length of data to hash when computing seed:
 *   16 + 4 + 2 + 32/24 = 54/46 */
#define LMS_SEED_HASH_LEN(hLen)     \
    (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + (word32)(hLen))

/* Length of data to hash when computing a node:
 *   16 + 4 + 2 + 32/24 + 32/24 = 86/70 */
#define LMS_NODE_HASH_LEN(hLen)     \
    (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + 2U * (word32)(hLen))

/* Length of data to hash when computing most results:
 *   16 + 4 + 2 + 1 + 32/24 = 55/47 */
#define LMS_HASH_BUFFER_LEN(hLen)   \
    (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + (word32)(hLen))

/* Length of preliminary data to hash when computing K:
 *   16 + 4 + 2 = 22 */
#define LMS_K_PRE_LEN       (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN)

/* Length of preliminary data to hash when computing message hash:
 *   16 + 4 + 2 = 22 */
#define LMS_MSG_PRE_LEN     (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN)


#ifdef WC_LMS_DEBUG_PRINT_DATA
/* Print data when debugging implementation.
 *
 * @param [in] name  String to print before data.
 * @param [in] data  Array of bytes.
 * @param [in] len   Length of data in array.
 */
static void print_data(const char* name, const byte* data, int len)
{
    int i;

    fprintf(stderr, "%6s: ", name);
    for (i = 0; i < len; i++) {
        fprintf(stderr, "%02x", data[i]);
    }
    fprintf(stderr, "\n");
}
#endif

/***************************************
 * Index APIs
 **************************************/

#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Zero index.
 *
 * @param [out] a    Byte array. Big-endian encoding.
 * @param [in]  len  Length of array in bytes.
 */
static WC_INLINE void wc_lms_idx_zero(unsigned char* a, word32 len)
{
    XMEMSET(a, 0, len);
}

/* Increment big-endian value.
 *
 * @param [in, out] a    Byte array. Big-endian encoding.
 * @param [in]      len  Length of array in bytes.
 */
static WC_INLINE void wc_lms_idx_inc(unsigned char* a, word32 len)
{
    word32 i;

    /* Starting at least-significant byte up to most. */
    for (i = len; i > 0; i--) {
        /* Add one/carry to byte. */
        if ((++a[i - 1]) != 0) {
            /* No more carry. */
            break;
        }
    }
}
#endif /* !WOLFSSL_LMS_VERIFY_ONLY */

/***************************************
 * Hash APIs
 **************************************/

/* Set hash data and length into SHA-256 digest.
 *
 * @param [in, out] state  SHA-256 digest object.
 * @param [in]      data   Data to add to hash.
 * @param [in]      len    Number of bytes in data. Must be less than a block.
 */
#define LMS_SHA256_SET_DATA(sha256, data, len)  \
do {                                            \
    XMEMCPY((sha256)->buffer, (data), (len));   \
    (sha256)->buffLen = (len);                  \
    (sha256)->loLen = (len);                    \
} while (0)

/* Add hash data and length into SHA-256 digest.
 *
 * @param [in, out] state  SHA-256 digest object.
 * @param [in]      data   Data to add to hash.
 * @param [in]      len    Number of bytes in data. Must be less than a block.
 */
#define LMS_SHA256_ADD_DATA(sha256, data, len)                              \
do {                                                                        \
    XMEMCPY((byte*)(sha256)->buffer + (sha256)->buffLen, (data), (len));    \
    (sha256)->buffLen += (len);                                             \
    (sha256)->loLen += (len);                                               \
} while (0)

/* Set the length of 54 bytes in buffer as per SHA-256 final operation.
 *
 * @param [in, out] buffer  Hash data buffer to add length to.
 */
#define LMS_SHA256_SET_LEN_54(buffer)   \
do {                                    \
    (buffer)[54] = 0x80;                \
    (buffer)[55] = 0x00;                \
    (buffer)[56] = 0x00;                \
    (buffer)[57] = 0x00;                \
    (buffer)[58] = 0x00;                \
    (buffer)[59] = 0x00;                \
    (buffer)[60] = 0x00;                \
    (buffer)[61] = 0x00;                \
    (buffer)[62] = 0x01;                \
    (buffer)[63] = 0xb0;                \
} while (0)

/* Set the length of 55 bytes in buffer as per SHA-256 final operation.
 *
 * @param [in, out] buffer  Hash data buffer to add length to.
 */
#define LMS_SHA256_SET_LEN_55(buffer)   \
do {                                    \
    (buffer)[55] = 0x80;                \
    (buffer)[56] = 0x00;                \
    (buffer)[57] = 0x00;                \
    (buffer)[58] = 0x00;                \
    (buffer)[59] = 0x00;                \
    (buffer)[60] = 0x00;                \
    (buffer)[61] = 0x00;                \
    (buffer)[62] = 0x01;                \
    (buffer)[63] = 0xb8;                \
} while (0)

#ifndef WOLFSSL_NO_LMS_SHA256_256
#ifndef WC_LMS_FULL_HASH
/* Hash one full block of data and compute result.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_block(wc_Sha256* sha256, const byte* data,
    byte* hash)
{
    /* Hash the block and reset SHA-256 state. */
    return wc_Sha256HashBlock(sha256, data, hash);
}
#endif /* !WC_LMS_FULL_HASH */

/* Hash data and compute result.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [in]  len     Length of data to hash.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash(wc_Sha256* sha256, byte* data, word32 len,
    byte* hash)
{
    int ret;

#ifndef WC_LMS_FULL_HASH
    if (len < WC_SHA256_BLOCK_SIZE) {
        /* Store data into SHA-256 object's buffer. */
        LMS_SHA256_SET_DATA(sha256, data, len);
        ret = wc_Sha256Final(sha256, hash);
    }
    else if (len < WC_SHA256_BLOCK_SIZE + WC_SHA256_PAD_SIZE) {
        ret = wc_Sha256HashBlock(sha256, data, NULL);
        if (ret == 0) {
            byte* buffer = (byte*)sha256->buffer;
            word32 rem = len - WC_SHA256_BLOCK_SIZE;

            XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE, rem);
            buffer[rem++] = 0x80;
            XMEMSET(buffer + rem, 0, WC_SHA256_BLOCK_SIZE - 2 - rem);
            buffer[WC_SHA256_BLOCK_SIZE - 2] = (byte)(len >> 5);
            buffer[WC_SHA256_BLOCK_SIZE - 1] = (byte)(len << 3);
            ret = wc_Sha256HashBlock(sha256, buffer, hash);
        }
    }
    else {
        ret = wc_Sha256Update(sha256, data, len);
        if (ret == 0) {
            ret = wc_Sha256Final(sha256, hash);
        }
    }
#else
    ret = wc_Sha256Update(sha256, data, len);
    if (ret == 0) {
        ret = wc_Sha256Final(sha256, hash);
    }
#endif /* !WC_LMS_FULL_HASH */

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

/* Update hash with first data.
 *
 * Sets the data directly into SHA-256's buffer if valid.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [in]  len     Length of data to hash.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_first(wc_Sha256* sha256, const byte* data,
    word32 len)
{
    int ret = 0;

#ifndef WC_LMS_FULL_HASH
    if (len < WC_SHA256_BLOCK_SIZE) {
        /* Store data into SHA-256 object's buffer. */
        LMS_SHA256_SET_DATA(sha256, data, len);
    }
    else
#endif /* !WC_LMS_FULL_HASH */
    {
        ret = wc_Sha256Update(sha256, data, len);
    }

    return ret;
}

/* Update hash with further data.
 *
 * Adds the data directly into SHA-256's buffer if valid.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [in]  len     Length of data to hash.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_update(wc_Sha256* sha256, const byte* data,
    word32 len)
{
    int ret = 0;

#ifndef WC_LMS_FULL_HASH
    if (sha256->buffLen + len < WC_SHA256_BLOCK_SIZE) {
        /* Add data to SHA-256 object's buffer. */
        LMS_SHA256_ADD_DATA(sha256, data, len);
    }
    else if (sha256->buffLen + len < 2 * WC_SHA256_BLOCK_SIZE) {
        byte* buffer = (byte*)sha256->buffer;

        XMEMCPY(buffer + sha256->buffLen, data,
            WC_SHA256_BLOCK_SIZE - sha256->buffLen);
        ret = wc_Sha256HashBlock(sha256, buffer, NULL);
        if (ret == 0) {
            word32 rem = len - (WC_SHA256_BLOCK_SIZE - sha256->buffLen);
            XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE - sha256->buffLen, rem);
            sha256->buffLen = rem;
            sha256->loLen += len;
        }
    }
    else {
        ret = wc_Sha256Update(sha256, data, len);
    }
#else
    ret = wc_Sha256Update(sha256, data, len);
#endif /* !WC_LMS_FULL_HASH */

    return ret;
}

#ifndef WOLFSSL_NO_LMS_SHA256_256
/* Finalize hash.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_final(wc_Sha256* sha256, byte* hash)
{
#ifndef WC_LMS_FULL_HASH
    int ret = 0;
    byte* buffer = (byte*)sha256->buffer;

    buffer[sha256->buffLen++] = 0x80;
    if (sha256->buffLen > WC_SHA256_PAD_SIZE) {
        XMEMSET(buffer + sha256->buffLen, 0,
            WC_SHA256_BLOCK_SIZE - sha256->buffLen);
        ret = wc_Sha256HashBlock(sha256, buffer, NULL);
        sha256->buffLen = 0;
    }
    if (ret == 0) {
        XMEMSET(buffer + sha256->buffLen, 0,
            WC_SHA256_BLOCK_SIZE - 8 - sha256->buffLen);
        sha256->hiLen = (sha256->hiLen << 3) + (sha256->loLen >> 29);
        sha256->loLen = sha256->loLen << 3;
    #ifdef LITTLE_ENDIAN_ORDER
        sha256->buffer[14] = ByteReverseWord32(sha256->hiLen);
        sha256->buffer[15] = ByteReverseWord32(sha256->loLen);
    #else
        sha256->buffer[14] = sha256->hiLen;
        sha256->buffer[15] = sha256->loLen;
    #endif
        ret = wc_Sha256HashBlock(sha256, buffer, hash);
        sha256->buffLen = 0;
        sha256->hiLen = 0;
        sha256->loLen = 0;
    }

    return ret;
#else
    return wc_Sha256Final(sha256, hash);
#endif
}
#endif /* !WOLFSSL_NO_LMS_SHA256_256 */

#ifdef WOLFSSL_LMS_SHA256_192
/* Set the length of 46 bytes in buffer as per SHA-256 final operation.
 *
 * @param [in, out] buffer  Hash data buffer to add length to.
 */
#define LMS_SHA256_SET_LEN_46(buffer)   \
do {                                    \
    (buffer)[46] = 0x80;                \
    (buffer)[47] = 0x00;                \
    (buffer)[48] = 0x00;                \
    (buffer)[49] = 0x00;                \
    (buffer)[50] = 0x00;                \
    (buffer)[51] = 0x00;                \
    (buffer)[52] = 0x00;                \
    (buffer)[53] = 0x00;                \
    (buffer)[54] = 0x00;                \
    (buffer)[55] = 0x00;                \
    (buffer)[56] = 0x00;                \
    (buffer)[57] = 0x00;                \
    (buffer)[58] = 0x00;                \
    (buffer)[59] = 0x00;                \
    (buffer)[60] = 0x00;                \
    (buffer)[61] = 0x00;                \
    (buffer)[62] = 0x01;                \
    (buffer)[63] = 0x70;                \
} while (0)

/* Set the length of 47 bytes in buffer as per SHA-256 final operation.
 *
 * @param [in, out] buffer  Hash data buffer to add length to.
 */
#define LMS_SHA256_SET_LEN_47(buffer)   \
do {                                    \
    (buffer)[47] = 0x80;                \
    (buffer)[48] = 0x00;                \
    (buffer)[49] = 0x00;                \
    (buffer)[50] = 0x00;                \
    (buffer)[51] = 0x00;                \
    (buffer)[52] = 0x00;                \
    (buffer)[53] = 0x00;                \
    (buffer)[54] = 0x00;                \
    (buffer)[55] = 0x00;                \
    (buffer)[56] = 0x00;                \
    (buffer)[57] = 0x00;                \
    (buffer)[58] = 0x00;                \
    (buffer)[59] = 0x00;                \
    (buffer)[60] = 0x00;                \
    (buffer)[61] = 0x00;                \
    (buffer)[62] = 0x01;                \
    (buffer)[63] = 0x78;                \
} while (0)

#ifndef WC_LMS_FULL_HASH
/* Hash one full block of data and compute result.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_sha256_192_hash_block(wc_Sha256* sha256,
    const byte* data, byte* hash)
{
    int ret;
    unsigned char output[WC_SHA256_DIGEST_SIZE];

    /* Hash the block and reset SHA-256 state. */
    ret = wc_Sha256HashBlock(sha256, data, output);
    if (ret == 0) {
        XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
    }

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

/* Hash data and compute result.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [in]  data    Data to hash.
 * @param [in]  len     Length of data to hash.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_sha256_192(wc_Sha256* sha256, byte* data,
    word32 len, byte* hash)
{
    int ret;
    unsigned char output[WC_SHA256_DIGEST_SIZE];

#ifndef WC_LMS_FULL_HASH
    if (len < WC_SHA256_BLOCK_SIZE) {
        /* Store data into SHA-256 object's buffer. */
        LMS_SHA256_SET_DATA(sha256, data, len);
        ret = wc_Sha256Final(sha256, output);
        if (ret == 0) {
            XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
        }
    }
    else if (len < WC_SHA256_BLOCK_SIZE + WC_SHA256_PAD_SIZE) {
        ret = wc_Sha256HashBlock(sha256, data, NULL);
        if (ret == 0) {
            byte* buffer = (byte*)sha256->buffer;
            word32 rem = len - WC_SHA256_BLOCK_SIZE;

            XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE, rem);
            buffer[rem++] = 0x80;
            XMEMSET(buffer + rem, 0, WC_SHA256_BLOCK_SIZE - 2 - rem);
            buffer[WC_SHA256_BLOCK_SIZE - 2] = (byte)(len >> 5);
            buffer[WC_SHA256_BLOCK_SIZE - 1] = (byte)(len << 3);
            ret = wc_Sha256HashBlock(sha256, buffer, output);
            if (ret == 0) {
                XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
            }
        }
    }
    else {
        ret = wc_Sha256Update(sha256, data, len);
        if (ret == 0) {
            ret = wc_Sha256Final(sha256, output);
            if (ret == 0) {
                XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
            }
        }
    }
#else
    ret = wc_Sha256Update(sha256, data, len);
    if (ret == 0) {
        ret = wc_Sha256Final(sha256, output);
        if (ret == 0) {
            XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
        }
    }
#endif /* !WC_LMS_FULL_HASH */

    return ret;
}

/* Finalize hash.
 *
 * @param [in]  sha256  SHA-256 hash object.
 * @param [out] hash    Hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* hash)
{
#ifndef WC_LMS_FULL_HASH
    int ret = 0;
    byte* buffer = (byte*)sha256->buffer;
    unsigned char output[WC_SHA256_DIGEST_SIZE];

    buffer[sha256->buffLen++] = 0x80;
    if (sha256->buffLen > WC_SHA256_PAD_SIZE) {
        XMEMSET(buffer + sha256->buffLen, 0,
            WC_SHA256_BLOCK_SIZE - sha256->buffLen);
        ret = wc_Sha256HashBlock(sha256, buffer, NULL);
        sha256->buffLen = 0;
    }
    if (ret == 0) {
        XMEMSET(buffer + sha256->buffLen, 0,
            WC_SHA256_BLOCK_SIZE - 8 - sha256->buffLen);
        sha256->hiLen = (sha256->hiLen << 3) + (sha256->loLen >> 29);
        sha256->loLen = sha256->loLen << 3;
    #ifdef LITTLE_ENDIAN_ORDER
        sha256->buffer[14] = ByteReverseWord32(sha256->hiLen);
        sha256->buffer[15] = ByteReverseWord32(sha256->loLen);
    #else
        sha256->buffer[14] = sha256->hiLen;
        sha256->buffer[15] = sha256->loLen;
    #endif
        ret = wc_Sha256HashBlock(sha256, buffer, output);
        if (ret == 0) {
            XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
        }
        sha256->buffLen = 0;
        sha256->hiLen = 0;
        sha256->loLen = 0;
    }

    return ret;
#else
    int ret;
    unsigned char output[WC_SHA256_DIGEST_SIZE];

    ret = wc_Sha256Final(sha256, output);
    if (ret == 0) {
        XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
    }

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

#ifdef WOLFSSL_LMS_SHAKE256
/* Hash data using SHAKE256 and compute result.
 *
 * @param [in]  shake    SHAKE256 hash object.
 * @param [in]  data     Data to hash.
 * @param [in]  len      Length of data to hash.
 * @param [out] hash     Hash output.
 * @param [in]  hashLen  Length of hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_shake256_hash(wc_Shake* shake, byte* data,
    word32 len, byte* hash, word32 hashLen)
{
    int ret;

    ret = wc_Shake256_Update(shake, data, len);
    if (ret == 0) {
        ret = wc_Shake256_Final(shake, hash, hashLen);
    }

    return ret;
}

/* Update hash with first data using SHAKE256.
 *
 * @param [in]  shake  SHAKE256 hash object.
 * @param [in]  data   Data to hash.
 * @param [in]  len    Length of data to hash.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_shake256_hash_first(wc_Shake* shake,
    const byte* data, word32 len)
{
    return wc_Shake256_Update(shake, data, len);
}

/* Update hash with further data using SHAKE256.
 *
 * @param [in]  shake  SHAKE256 hash object.
 * @param [in]  data   Data to hash.
 * @param [in]  len    Length of data to hash.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_shake256_hash_update(wc_Shake* shake,
    const byte* data, word32 len)
{
    return wc_Shake256_Update(shake, data, len);
}

/* Finalize SHAKE256 hash.
 *
 * @param [in]  shake    SHAKE256 hash object.
 * @param [out] hash     Hash output.
 * @param [in]  hashLen  Length of hash output.
 * @return  0 on success.
 */
static WC_INLINE int wc_lms_shake256_hash_final(wc_Shake* shake, byte* hash,
    word32 hashLen)
{
    return wc_Shake256_Final(shake, hash, hashLen);
}
#endif /* WOLFSSL_LMS_SHAKE256 */

/***************************************
 * LM-OTS APIs
 **************************************/

/* Expand Q to and array of Winternitz width bits values plus checksum.
 *
 * Supported Winternitz widths: 8, 4, 2, 1.
 *
 * Algorithm 2: Checksum Calculation
 *   sum = 0
 *   for ( i = 0; i < (n*8/w); i = i + 1 ) {
 *     sum = sum + (2^w - 1) - coef(S, i, w)
 *   }
 *   return (sum << ls)
 * Section 3.1.3: Strings of w-Bit Elements
 *   coef(S, i, w) = (2^w - 1) AND
 *                   ( byte(S, floor(i * w / 8)) >>
 *                     (8 - (w * (i % (8 / w)) + w)) )
 * Combine coefficient expansion with checksum calculation.
 *
 * @param [in]  q   Q array of bytes.
 * @param [in]  n   Number of bytes in Q.
 * @param [in]  w   Winternitz width in bits.
 * @param [in]  ls  Left shift of checksum.
 * @param [out] qe  Expanded Q with checksum.
 * @return  0 on success.
 * @return  BAD_FUNC_ARG when Winternitz width is not supported.
 */
static WC_INLINE int wc_lmots_q_expand(byte* q, word8 n, word8 w, word8 ls,
    byte* qe)
{
    int ret = 0;
    /* sum is word16: the small-variant checksum loop below relies on
     * arithmetic wrapping at 2^16 (sum <<= w then sum >> (16 - w) reads the
     * top byte of the rolled value). Switching to a wider type would let
     * those high bits leak into subsequent reads. */
    word16 sum;
    unsigned int i;

#ifndef WOLFSSL_WC_LMS_SMALL
    switch (w) {
        /* Winternitz width of 8. */
        case 8:
            /* No expansion required, just copy. */
            XMEMCPY(qe, q, n);
            /* Start sum with all 2^w - 1s and subtract from that. */
            sum = (word16)(0xffU * n);
            /* For each byte of the hash. */
            for (i = 0; i < n; i++) {
                /* Subtract coefficient from sum. */
                sum = (word16)(sum - q[i]);
            }
            /* Put coefficients of checksum on the end. */
            qe[n + 0] = (word8)(sum >> 8);
            qe[n + 1] = (word8)(sum     );
            break;
        /* Winternitz width of 4. */
        case 4:
            sum = (word16)(2U * 0xfU * n);
            /* For each byte of the hash. */
            for (i = 0; i < n; i++) {
                /* Get coefficient. */
                qe[0] = (q[i] >> 4)      ;
                qe[1] = (q[i]     ) & 0xf;
                /* Subtract coefficients from sum. */
                sum = (word16)(sum - qe[0]);
                sum = (word16)(sum - qe[1]);
                /* Move to next coefficients. */
                qe += 2;
            }
            /* Put coefficients of checksum on the end. */
            qe[0] = (word8)((sum >> 8) & 0xf);
            qe[1] = (word8)((sum >> 4) & 0xf);
            qe[2] = (word8)((sum     ) & 0xf);
            break;
        /* Winternitz width of 2. */
        case 2:
            sum = (word16)(4U * 0x3U * n);
            /* For each byte of the hash. */
            for (i = 0; i < n; i++) {
                /* Get coefficients. */
                qe[0] = (q[i] >> 6)      ;
                qe[1] = (q[i] >> 4) & 0x3;
                qe[2] = (q[i] >> 2) & 0x3;
                qe[3] = (q[i]     ) & 0x3;
                /* Subtract coefficients from sum. */
                sum = (word16)(sum - qe[0]);
                sum = (word16)(sum - qe[1]);
                sum = (word16)(sum - qe[2]);
                sum = (word16)(sum - qe[3]);
                /* Move to next coefficients. */
                qe += 4;
            }
            /* Put coefficients of checksum on the end. */
            qe[0] = (word8)((sum >>  8) & 0x3);
            qe[1] = (word8)((sum >>  6) & 0x3);
            qe[2] = (word8)((sum >>  4) & 0x3);
            qe[3] = (word8)((sum >>  2) & 0x3);
            qe[4] = (word8)((sum      ) & 0x3);
            break;
        /* Winternitz width of 1. */
        case 1:
            sum = (word16)(8U * 0x01U * n);
            /* For each byte of the hash. */
            for (i = 0; i < n; i++) {
                /* Get coefficients. */
                qe[0] = (q[i] >> 7)      ;
                qe[1] = (q[i] >> 6) & 0x1;
                qe[2] = (q[i] >> 5) & 0x1;
                qe[3] = (q[i] >> 4) & 0x1;
                qe[4] = (q[i] >> 3) & 0x1;
                qe[5] = (q[i] >> 2) & 0x1;
                qe[6] = (q[i] >> 1) & 0x1;
                qe[7] = (q[i]     ) & 0x1;
                /* Subtract coefficients from sum. */
                sum = (word16)(sum - qe[0]);
                sum = (word16)(sum - qe[1]);
                sum = (word16)(sum - qe[2]);
                sum = (word16)(sum - qe[3]);
                sum = (word16)(sum - qe[4]);
                sum = (word16)(sum - qe[5]);
                sum = (word16)(sum - qe[6]);
                sum = (word16)(sum - qe[7]);
                /* Move to next coefficients. */
                qe += 8;
            }
            /* Put coefficients of checksum on the end. */
#ifdef WOLFSSL_LMS_SHA256_192
            if (ls == 7)
#endif
            {
                qe[0] = (word8)((sum >>  8)  );
                qe++;
            }
            qe[0] = (word8)((sum >>  7) & 0x1);
            qe[1] = (word8)((sum >>  6) & 0x1);
            qe[2] = (word8)((sum >>  5) & 0x1);
            qe[3] = (word8)((sum >>  4) & 0x1);
            qe[4] = (word8)((sum >>  3) & 0x1);
            qe[5] = (word8)((sum >>  2) & 0x1);
            qe[6] = (word8)((sum >>  1) & 0x1);
            qe[7] = (word8)((sum      ) & 0x1);
            break;
        default:
            ret = BAD_FUNC_ARG;
            break;
    }

    (void)ls;
#else
    int j;

    if ((w != 8) && (w != 4) && (w != 2) && (w != 1)) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        /* Start sum with all 2^w - 1s and subtract from that. */
        sum = (word16)(((1U << w) - 1U) * ((n * 8U) / w));
        /* For each byte of the hash. */
        for (i = 0; i < n; i++) {
            /* Get next byte. */
            byte a = *(q++);
            /* For each width bits of byte. */
            for (j = 8 - w; j >= 0; j -= w) {
                /* Get coefficient. */
                *qe = (byte)(a >> (8 - w));
                /* Subtract coefficient from sum. */
                sum = (word16)(sum - *qe);
                /* Move to next coefficient. */
                qe++;
                /* Remove width bits. */
                a = (byte)(a << w);
            }
        }
        /* Shift sum up as required to pack it on the end of hash. */
        sum = (word16)(sum << ls);
        /* For each width bit of checksum. */
        for (j = 16 - w; j >= ls; j--) {
            /* Get coefficient. */
            *(qe++) = (byte)(sum >> (16 - w));
            /* Remove width bits. */
            sum = (word16)(sum << w);
        }
    }
#endif /* !WOLFSSL_WC_LMS_SMALL */

    return ret;
}

/* Calculate the hash for the message.
 *
 * Algorithm 3: Generating a One-Time Signature From a Private Key and a
 * Message
 *   ...
 *   5. Compute the array y as follows:
 *      Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
 * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature,
 * Message, Signature Typecode pubtype, and Identifiers I, q
 *   ...
 *   3. Compute the string Kc as follows:
 *      Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
 *
 * @param [in, out]  state  LMS state.
 * @param [in]       msg    Message to hash.
 * @param [in]       msgSz  Length of message in bytes.
 * @param [in]       c      C or randomizer value.
 * @param [out]      q      Computed Q value.
 * @return  0 on success.
 */
static int wc_lmots_msg_hash(LmsState* state, const byte* msg, word32 msgSz,
    const byte* c, byte* q)
{
    int ret;
    byte* buffer = state->buffer;
    byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;

    /* I || u32str(q) || u16str(D_MESG) */
    c16toa(LMS_D_MESG, ip);
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(state->params->lmOtsType)) {
        /* H(I || u32str(q) || u16str(D_MESG) || ...) */
        ret = wc_lms_shake256_hash_first(LMS_STATE_SHAKE(state), buffer,
            LMS_MSG_PRE_LEN);
        if (ret == 0) {
            /* H(... || C || ...) */
            ret = wc_lms_shake256_hash_update(LMS_STATE_SHAKE(state), c,
                state->params->hash_len);
        }
        if (ret == 0) {
            /* H(... || message) */
            ret = wc_lms_shake256_hash_update(LMS_STATE_SHAKE(state), msg, msgSz);
        }
        if (ret == 0) {
            /* Q = H(...) */
            ret = wc_lms_shake256_hash_final(LMS_STATE_SHAKE(state), q,
                state->params->hash_len);
        }
    }
    else
#endif
    {
        /* H(I || u32str(q) || u16str(D_MESG) || ...) */
        ret = wc_lms_hash_first(LMS_STATE_HASH(state), buffer, LMS_MSG_PRE_LEN);
        if (ret == 0) {
            /* H(... || C || ...) */
            ret = wc_lms_hash_update(LMS_STATE_HASH(state), c,
                state->params->hash_len);
        }
        if (ret == 0) {
            /* H(... || message) */
            ret = wc_lms_hash_update(LMS_STATE_HASH(state), msg, msgSz);
        }
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((ret == 0) &&
                ((state->params->lmOtsType & LMS_HASH_MASK) ==
                 LMS_SHA256_192)) {
            /* Q = H(...) */
            ret = wc_lms_hash_sha256_192_final(LMS_STATE_HASH(state), q);
        }
        else
    #endif
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        if (ret == 0) {
            /* Q = H(...) */
            ret = wc_lms_hash_final(LMS_STATE_HASH(state), q);
        }
        else
    #endif
        if (ret == 0) {
            ret = NOT_COMPILED_IN;
        }
    }

    return ret;
}

#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Compute array y, intermediates of public key calculation, for signature.
 *
 * Verification will perform the remaining iterations of hashing.
 *
 * Algorithm 3: Generating a One-Time Signature From a Private Key and a
 * Message
 *   ...
 *   5. Compute the array y as follows:
 *      Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
 *      for ( i = 0; i < p; i = i + 1 ) {
 *        a = coef(Q || Cksm(Q), i, w)
 *        tmp = x[i]
 *        for ( j = 0; j < a; j = j + 1 ) {
 *          tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
 *        }
 *        y[i] = tmp
 *      }
 * x[i] can be calculated on the fly using pseudo key generation in Appendix A.
 * Appendix A, The elements of the LM-OTS private keys are computed as:
 *   x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED).
 *
 * @param [in, out]  state  LMS state.
 * @param [in]       seed   Seed to hash.
 * @param [in]       msg    Message to sign.
 * @param [in]       msgSZ  Length of message in bytes.
 * @param [in]       c      C or randomizer value to hash.
 * @param [out]      y      Calculated intermediate hashes.
 * @return  0 on success.
 */
static int wc_lmots_compute_y_from_seed(LmsState* state, const byte* seed,
    const byte* msg, word32 msgSz, const byte* c, byte* y)
{
    const LmsParams* params = state->params;
    int ret;
    word16 i;
    byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN];
#ifdef WOLFSSL_SMALL_STACK
    byte* a = state->a;
#else
    byte a[LMS_MAX_P];
#endif /* WOLFSSL_SMALL_STACK */
    byte* buffer = state->buffer;
    byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
    byte* jp = ip + LMS_P_LEN;
    byte* tmp = jp + LMS_W_LEN;

    /* Q = H(I || u32str(q) || u16str(D_MESG) || C || message) */
    ret = wc_lmots_msg_hash(state, msg, msgSz, c, q);
    if (ret == 0) {
        /* Calculate checksum list all coefficients. */
        ret = wc_lmots_q_expand(q, (word8)params->hash_len, params->width,
            params->ls, a);
    }
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHAKE256
    if ((ret == 0) && !LMS_IS_SHAKE(params->lmOtsType))
#else
    if (ret == 0)
#endif
    {
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            /* Put in padding for final block. */
            LMS_SHA256_SET_LEN_47(buffer);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            /* Put in padding for final block. */
            LMS_SHA256_SET_LEN_55(buffer);
        #endif
        }
    }
#endif /* !WC_LMS_FULL_HASH */

    /* Compute y for each coefficient. */
    for (i = 0; (ret == 0) && (i < params->p); i++) {
        unsigned int j;

        /* tmp = x[i]
         *     = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). */
        c16toa(i, ip);
        *jp = LMS_D_FIXED;
#ifdef WOLFSSL_LMS_SHAKE256
        if (LMS_IS_SHAKE(params->lmOtsType)) {
            XMEMCPY(tmp, seed, params->hash_len);
            ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                LMS_HASH_BUFFER_LEN(params->hash_len), tmp, params->hash_len);
        }
        else
#endif
        {
#ifndef WC_LMS_FULL_HASH
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
            ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, tmp);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
            ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#else
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
            ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
            ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#endif /* !WC_LMS_FULL_HASH */
        }

        /* Apply the hash function coefficient number of times. */
        for (j = 0; (ret == 0) && (j < a[i]); j++) {
            /* I || u32str(q) || u16str(i) || u8str(j) || tmp */
            *jp = (byte)j;
            /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
    #ifdef WOLFSSL_LMS_SHAKE256
            if (LMS_IS_SHAKE(params->lmOtsType)) {
                ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                    LMS_HASH_BUFFER_LEN(params->hash_len), tmp,
                    params->hash_len);
            }
            else
    #endif
            {
    #ifndef WC_LMS_FULL_HASH
        #ifdef WOLFSSL_LMS_SHA256_192
            if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, tmp);
            }
            else
        #endif
            {
            #ifndef WOLFSSL_NO_LMS_SHA256_256
                ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
            #else
                ret = NOT_COMPILED_IN;
            #endif
            }
    #else
        #ifdef WOLFSSL_LMS_SHA256_192
            if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                    LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
            }
            else
        #endif
            {
            #ifndef WOLFSSL_NO_LMS_SHA256_256
                ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                    LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
            #else
                ret = NOT_COMPILED_IN;
            #endif
            }
    #endif /* !WC_LMS_FULL_HASH */
            }
        }

        if (ret == 0) {
            /* y[i] = tmp */
            XMEMCPY(y, tmp, params->hash_len);
            y += params->hash_len;
        }
    }

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

/* Compute public key candidate K from signature.
 *
 * Signing performed the first coefficient number of iterations of hashing.
 *
 * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature,
 * Message, Signature Typecode pubtype, and Identifiers I, q
 *   ...
 *   3. Compute the string Kc as follows:
 *      Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
 *      for ( i = 0; i < p; i = i + 1 ) {
 *        a = coef(Q || Cksm(Q), i, w)
 *        tmp = y[i]
 *        for ( j = a; j < 2^w - 1; j = j + 1 ) {
 *          tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
 *        }
 *        z[i] = tmp
 *      }
 *      Kc = H(I || u32str(q) || u16str(D_PBLC) ||
 *                                    z[0] || z[1] || ... || z[p-1])
 *   4, Return Kc.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      msg    Message to compute Kc for.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [in]      c      C or randomizer value from signature.
 * @param [in]      sig_y  Part of signature containing array y.
 * @param [out]     kc     Kc or public key candidate K.
 * @return  0 on success.
 */
static int wc_lmots_compute_kc_from_sig(LmsState* state, const byte* msg,
    word32 msgSz, const byte* c, const byte* sig_y, byte* kc)
{
    const LmsParams* params = state->params;
    int ret;
    word16 i;
    byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN];
#ifdef WOLFSSL_SMALL_STACK
    byte* a = state->a;
#else
    byte a[LMS_MAX_P];
#endif /* WOLFSSL_SMALL_STACK */
    byte* buffer = state->buffer;
    byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
    byte* jp = ip + LMS_P_LEN;
    byte* tmp = jp + LMS_W_LEN;
    unsigned int max = ((unsigned int)1 << params->width) - 1;

    /* I || u32str(q) || u16str(D_PBLC). */
    c16toa(LMS_D_PBLC, ip);
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(params->lmOtsType)) {
        /* H(I || u32str(q) || u16str(D_PBLC) || ...). */
        ret = wc_lms_shake256_hash_first(LMS_STATE_SHAKE_K(state), buffer,
            LMS_K_PRE_LEN);
        if (ret == 0) {
            /* Q = H(I || u32str(q) || u16str(D_MESG) || C || message) */
            ret = wc_lmots_msg_hash(state, msg, msgSz, c, q);
        }
        if (ret == 0) {
            /* Calculate checksum list all coefficients. */
            ret = wc_lmots_q_expand(q, (word8)params->hash_len, params->width,
                params->ls, a);
        }

        /* Compute z for each coefficient. */
        for (i = 0; (ret == 0) && (i < params->p); i++) {
            unsigned int j;

            /* I || u32(str) || u16str(i) || ... */
            c16toa(i, ip);

            /* tmp = y[i].
             * I || u32(str) || u16str(i) || ... || tmp */
            XMEMCPY(tmp, sig_y, params->hash_len);
            sig_y += params->hash_len;

            /* Finish iterations of hash from coefficient to max. */
            for (j = a[i]; (ret == 0) && (j < max); j++) {
                /* I || u32str(q) || u16str(i) || u8str(j) || tmp */
                *jp = (word8)j;
                /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
                ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                    LMS_HASH_BUFFER_LEN(params->hash_len), tmp,
                    params->hash_len);
            }

            if (ret == 0) {
                /* H(... || z[i] || ...) (for calculating Kc). */
                ret = wc_lms_shake256_hash_update(LMS_STATE_SHAKE_K(state), tmp,
                    params->hash_len);
            }
        }

        if (ret == 0) {
            /* Kc = H(...) */
            ret = wc_lms_shake256_hash_final(LMS_STATE_SHAKE_K(state), kc,
                params->hash_len);
        }
    }
    else
#endif
    {
        /* H(I || u32str(q) || u16str(D_PBLC) || ...). */
        ret = wc_lms_hash_first(LMS_STATE_HASH_K(state), buffer, LMS_K_PRE_LEN);
        if (ret == 0) {
            /* Q = H(I || u32str(q) || u16str(D_MESG) || C || message) */
            ret = wc_lmots_msg_hash(state, msg, msgSz, c, q);
        }
        if (ret == 0) {
            /* Calculate checksum list all coefficients. */
            ret = wc_lmots_q_expand(q, (word8)params->hash_len, params->width,
                params->ls, a);
        }
#ifndef WC_LMS_FULL_HASH
        if (ret == 0) {
        #ifdef WOLFSSL_LMS_SHA256_192
            if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                /* Put in padding for final block. */
                LMS_SHA256_SET_LEN_47(buffer);
            }
            else
        #endif
            {
            #ifndef WOLFSSL_NO_LMS_SHA256_256
                /* Put in padding for final block. */
                LMS_SHA256_SET_LEN_55(buffer);
            #endif
            }
        }
#endif /* !WC_LMS_FULL_HASH */

        /* Compute z for each coefficient. */
        for (i = 0; (ret == 0) && (i < params->p); i++) {
            unsigned int j;

            /* I || u32(str) || u16str(i) || ... */
            c16toa(i, ip);

            /* tmp = y[i].
             * I || u32(str) || u16str(i) || ... || tmp */
            XMEMCPY(tmp, sig_y, params->hash_len);
            sig_y += params->hash_len;

            /* Finish iterations of hash from coefficient to max. */
            for (j = a[i]; (ret == 0) && (j < max); j++) {
                /* I || u32str(q) || u16str(i) || u8str(j) || tmp */
                *jp = (word8)j;
                /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
        #ifndef WC_LMS_FULL_HASH
            #ifdef WOLFSSL_LMS_SHA256_192
                if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                    ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer,
                        tmp);
                }
                else
            #endif
                {
                #ifndef WOLFSSL_NO_LMS_SHA256_256
                    ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
                #else
                    ret = NOT_COMPILED_IN;
                #endif
                }
                /* Apply the hash function coefficient number of times. */
        #else
            #ifdef WOLFSSL_LMS_SHA256_192
                if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                    ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                        LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
                }
                else
            #endif
                {
                #ifndef WOLFSSL_NO_LMS_SHA256_256
                    ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                        LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
                #else
                    ret = NOT_COMPILED_IN;
                #endif
                }
        #endif /* !WC_LMS_FULL_HASH */
            }

            if (ret == 0) {
                /* H(... || z[i] || ...) (for calculating Kc). */
                ret = wc_lms_hash_update(LMS_STATE_HASH_K(state), tmp,
                    params->hash_len);
            }
        }

    #ifdef WOLFSSL_LMS_SHA256_192
        if ((ret == 0) &&
                ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192)) {
            /* Kc = H(...) */
            ret = wc_lms_hash_sha256_192_final(LMS_STATE_HASH_K(state), kc);
        }
        else
    #endif
        if (ret == 0) {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            /* Kc = H(...) */
            ret = wc_lms_hash_final(LMS_STATE_HASH_K(state), kc);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
    }

    return ret;
}

#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Generate LM-OTS public key.
 *
 * Caller set: state->buffer = I || u32str(q)
 *
 * Algorithm 1: Generating a One-Time Signature Public Key From a Private Key
 *   ...
 *   4. Compute the string K as follows:
 *      for ( i = 0; i < p; i = i + 1 ) {
 *        tmp = x[i]
 *        for ( j = 0; j < 2^w - 1; j = j + 1 ) {
 *          tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
 *        }
 *        y[i] = tmp
 *      }
 *      K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1])
 *   ...
 * x[i] can be calculated on the fly using pseudo key generation in Appendix A.
 * Appendix A, The elements of the LM-OTS private keys are computed as:
 *   x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED).
 *
 * @param [in, out]  state   LMS state.
 * @param [in]       seed    Seed to hash.
 * @param [out]      k       K, the public key hash, or OTS_PUB_HASH
 */
static int wc_lmots_make_public_hash(LmsState* state, const byte* seed, byte* k)
{
    const LmsParams* params = state->params;
    int ret;
    word16 i;
    byte* buffer = state->buffer;
    byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
    byte* jp = ip + LMS_P_LEN;
    byte* tmp = jp + LMS_W_LEN;
    unsigned int max = ((unsigned int)1 << params->width) - 1;

    /* I || u32str(q) || u16str(D_PBLC). */
    c16toa(LMS_D_PBLC, ip);
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(params->lmOtsType)) {
        /* K = H(I || u32str(q) || u16str(D_PBLC) || ...) */
        ret = wc_lms_shake256_hash_first(LMS_STATE_SHAKE_K(state), buffer,
            LMS_K_PRE_LEN);

        for (i = 0; (ret == 0) && (i < params->p); i++) {
            unsigned int j;

            /* tmp = x[i]
             *     = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). */
            c16toa(i, ip);
            *jp = LMS_D_FIXED;
            XMEMCPY(tmp, seed, params->hash_len);
            ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                LMS_HASH_BUFFER_LEN(params->hash_len), tmp, params->hash_len);
            /* Do all iterations to calculate y. */
            for (j = 0; (ret == 0) && (j < max); j++) {
                /* I || u32str(q) || u16str(i) || u8str(j) || tmp */
                *jp = (word8)j;
                /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
                ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                    LMS_HASH_BUFFER_LEN(params->hash_len), tmp,
                    params->hash_len);
            }
            if (ret == 0) {
                /* K = H(... || y[i] || ...) */
                ret = wc_lms_shake256_hash_update(LMS_STATE_SHAKE_K(state), tmp,
                    params->hash_len);
            }
        }
        if (ret == 0) {
            /* K = H(I || u32str(q) || u16str(D_PBLC) ||
             *       y[0] || ... || y[p-1]) */
            ret = wc_lms_shake256_hash_final(LMS_STATE_SHAKE_K(state), k,
                params->hash_len);
        }
    }
    else
#endif
    {
        /* K = H(I || u32str(q) || u16str(D_PBLC) || ...) */
        ret = wc_lms_hash_first(LMS_STATE_HASH_K(state), buffer, LMS_K_PRE_LEN);

#ifndef WC_LMS_FULL_HASH
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            /* Put in padding for final block. */
            LMS_SHA256_SET_LEN_47(buffer);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            /* Put in padding for final block. */
            LMS_SHA256_SET_LEN_55(buffer);
        #endif
        }
#endif /* !WC_LMS_FULL_HASH */

        for (i = 0; (ret == 0) && (i < params->p); i++) {
            unsigned int j;

            /* tmp = x[i]
             *     = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). */
            c16toa(i, ip);
            *jp = LMS_D_FIXED;
#ifndef WC_LMS_FULL_HASH
        #ifdef WOLFSSL_LMS_SHA256_192
            if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
                ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, tmp);
            }
            else
        #endif
            {
            #ifndef WOLFSSL_NO_LMS_SHA256_256
                XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
                ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
            #else
                ret = NOT_COMPILED_IN;
            #endif
            }
#else
        #ifdef WOLFSSL_LMS_SHA256_192
            if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
                ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                    LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
            }
            else
        #endif
            {
            #ifndef WOLFSSL_NO_LMS_SHA256_256
                XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
                ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                    LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
            #else
                ret = NOT_COMPILED_IN;
            #endif
            }
#endif /* !WC_LMS_FULL_HASH */
            /* Do all iterations to calculate y. */
            for (j = 0; (ret == 0) && (j < max); j++) {
                /* I || u32str(q) || u16str(i) || u8str(j) || tmp */
                *jp = (word8)j;
                /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
        #ifndef WC_LMS_FULL_HASH
            #ifdef WOLFSSL_LMS_SHA256_192
                if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                    ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer,
                        tmp);
                }
                else
            #endif
                {
                #ifndef WOLFSSL_NO_LMS_SHA256_256
                    ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
                #else
                    ret = NOT_COMPILED_IN;
                #endif
                }
        #else
            #ifdef WOLFSSL_LMS_SHA256_192
                if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
                    ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                        LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
                }
                else
            #endif
                {
                #ifndef WOLFSSL_NO_LMS_SHA256_256
                    ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                        LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
                #else
                    ret = NOT_COMPILED_IN;
                #endif
                }
        #endif /* !WC_LMS_FULL_HASH */
            }
            if (ret == 0) {
                /* K = H(... || y[i] || ...) */
                ret = wc_lms_hash_update(LMS_STATE_HASH_K(state), tmp,
                    params->hash_len);
            }
        }
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((ret == 0) &&
                ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192)) {
            /* K = H(I || u32str(q) || u16str(D_PBLC) ||
             *       y[0] || ... || y[p-1]) */
            ret = wc_lms_hash_sha256_192_final(LMS_STATE_HASH_K(state), k);
        }
        else
    #endif
        if (ret == 0) {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            /* K = H(I || u32str(q) || u16str(D_PBLC) ||
             *       y[0] || ... || y[p-1]) */
            ret = wc_lms_hash_final(LMS_STATE_HASH_K(state), k);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
    }

    return ret;
}

/* Encode the LM-OTS public key.
 *
 * Encoded into public key and signature if more than one level.
 * T[1] is already in place. Putting in: type, ostype and I.
 *
 * Section 4.3:
 *   u32str(type) || u32str(otstype) || I || T[1]
 *
 * @param [in]  params  LMS parameters.
 * @param [in]  priv    LMS private ley.
 * @param [out] pub     LMS public key.
 */
static void wc_lmots_public_key_encode(const LmsParams* params,
    const byte* priv, byte* pub)
{
    const byte* priv_i = priv + LMS_Q_LEN + params->hash_len;

    /* u32str(type) || ... || T(1) */
    c32toa(params->lmsType & LMS_H_W_MASK, pub);
    pub += 4;
    /* u32str(type) || u32str(otstype) || ... || T(1) */
    c32toa(params->lmOtsType & LMS_H_W_MASK, pub);
    pub += 4;
    /* u32str(type) || u32str(otstype) || I || T(1) */
    XMEMCPY(pub, priv_i, LMS_I_LEN);
}
#endif /* !WOLFSSL_LMS_VERIFY_ONLY */

/* Check the public key matches the parameters.
 *
 * @param [in] params  LMS parameters.
 * @param [in] pub     Public key.
 * @return  0 on success.
 * @return  PUBLIC_KEY_E when LMS or LM-OTS type doesn't match.
 */
static int wc_lmots_public_key_check(const LmsParams* params, const byte* pub)
{
    int ret = 0;
    word32 type;

    /* Get message hash and height type. */
    ato32(pub, &type);
    pub += 4;
    /* Compare with parameters. */
    if (type != (params->lmsType & LMS_H_W_MASK)) {
        ret = PUBLIC_KEY_E;
    }
    if (ret == 0) {
        /* Get node hash and Winternitz width type. */
        ato32(pub, &type);
        /* Compare with parameters. */
        if (type != (params->lmOtsType & LMS_H_W_MASK)) {
            ret = PUBLIC_KEY_E;
        }
    }

    return ret;
}

/* Calculate public key candidate K from signature.
 *
 * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature,
 * Message, Signature Typecode pubtype, and Identifiers I, q
 *   ...
 *   2. Parse sigtype, C, and y from the signature as follows:
 *      a. sigtype = strTou32(first 4 bytes of signature)
 *      b. If sigtype is not equal to pubtype, return INVALID.
 *      ...
 *      d. C = next n bytes of signature
 *      e.   y[0] = next n bytes of signature
 *           y[1] = next n bytes of signature
 *           ...
 *         y[p-1] = next n bytes of signature
 *   3. Compute the string Kc as follows:
 *   ...
 *
 * @param [in, out] state  LMS state.
 * @param [in]      pub    LMS public key.
 * @param [in]      msg    Message/next private key to verify.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [in]      sig    Signature including type, C and y[0..p-1].
 * @param [out]     kc     Public key candidate Kc.
 */
static int wc_lmots_calc_kc(LmsState* state, const byte* pub, const byte* msg,
    word32 msgSz, const byte* sig, byte* kc)
{
    int ret = 0;

    /* Check signature type. */
    if (XMEMCMP(pub, sig, LMS_TYPE_LEN) != 0) {
        ret = SIG_TYPE_E;
    }
    if (ret == 0) {
        /* Get C or randomizer value from signature. */
        const byte* c = sig + LMS_TYPE_LEN;
        /* Get array y from signature. */
        const byte* y = c + state->params->hash_len;

        /* Compute the public key candidate Kc from the signature. */
        ret = wc_lmots_compute_kc_from_sig(state, msg, msgSz, c, y, kc);
    }

    return ret;
}

#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Generate LM-OTS private key.
 *
 * Algorithm 5: Computing an LMS Private Key
 * But use Appendix A to generate x on the fly.
 *   PRIV = SEED | I
 *
 * @param [in]  rng       Random number generator.
 * @param [in]  seed_len  Length of seed to generate.
 * @param [out] priv      Private key data.
 */
static int wc_lmots_make_private_key(WC_RNG* rng, word16 seed_len, byte* priv)
{
    return wc_RNG_GenerateBlock(rng, priv, seed_len + LMS_I_LEN);
}

/* Generate LM-OTS signature.
 *
 * Algorithm 3: Generating a One-Time Signature From a Private Key and a
 * Message
 *   ...
 *   4. Set C to a uniformly random n-byte string
 *   5. Compute the array y as follows:
 *      ...
 *   6. Return u32str(type) || C || y[0] || ... || y[p-1]
 *
 * @param [in, out] state  LMS state.
 * @param [in]      seed   Private key seed.
 * @param [in]      msg    Message to be signed.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [out]     sig    Signature buffer.
 * @return  0 on success.
 */
static int wc_lmots_sign(LmsState* state, const byte* seed, const byte* msg,
    word32 msgSz, byte* sig)
{
    int ret;
    byte* buffer = state->buffer;
    byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
    byte* jp = ip + LMS_P_LEN;
    byte* tmp = jp + LMS_W_LEN;
    byte* sig_c = sig;

    /* I || u32str(q) || u16str(0xFFFD) || ... */
    c16toa(LMS_D_C, ip);
    /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || ... */
    *jp = LMS_D_FIXED;
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(state->params->lmOtsType)) {
        /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
        XMEMCPY(tmp, seed, state->params->hash_len);
        /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
         * sig = u32str(type) || C || ... */
        ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
            LMS_HASH_BUFFER_LEN(state->params->hash_len), sig_c,
            state->params->hash_len);
    }
    else
#endif
    {
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
    if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
        XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
        /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
         * sig = u32str(type) || C || ... */
        /* Put in padding for final block. */
        LMS_SHA256_SET_LEN_47(buffer);
        ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, sig_c);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
        XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
        /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
         * sig = u32str(type) || C || ... */
        /* Put in padding for final block. */
        LMS_SHA256_SET_LEN_55(buffer);
        ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, sig_c);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#else
#ifdef WOLFSSL_LMS_SHA256_192
    if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
        XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
        /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
         * sig = u32str(type) || C || ... */
        ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
            LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), sig_c);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
        XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
        /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
         * sig = u32str(type) || C || ... */
        ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
            LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), sig_c);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#endif /* !WC_LMS_FULL_HASH */
    }

    if (ret == 0) {
        byte* sig_y = sig_c + state->params->hash_len;

        /* Compute array y.
         * sig = u32str(type) || C || y[0] || ... || y[p-1] */
        ret = wc_lmots_compute_y_from_seed(state, seed, msg, msgSz, sig_c,
            sig_y);
    }

    return ret;
}
#endif /* WOLFSSL_LMS_VERIFY_ONLY */

/***************************************
 * LMS APIs
 **************************************/

#ifndef WOLFSSL_LMS_VERIFY_ONLY
#ifndef WOLFSSL_WC_LMS_SMALL
/* Load the LMS private state from data.
 *
 * @param [in]  params     LMS parameters.
 * @param [out] state      Private key state.
 * @param [in]  priv_data  Private key data.
 */
static void wc_lms_priv_state_load(const LmsParams* params, LmsPrivState* state,
    byte* priv_data)
{
    /* Authentication path data. */
    state->auth_path = priv_data;
    priv_data += (word32)params->height * params->hash_len;

    /* Stack of nodes. */
    state->stack.stack = priv_data;
    priv_data += (params->height + 1) * params->hash_len;
    ato32(priv_data, &state->stack.offset);
    priv_data += 4;

    /* Cached root nodes. */
    state->root = priv_data;
    priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels, params->hash_len);

    /* Cached leaf nodes. */
    state->leaf.cache = priv_data;
    priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits, params->hash_len);
    ato32(priv_data, &state->leaf.idx);
    priv_data += 4;
    ato32(priv_data, &state->leaf.offset);
    /* priv_data += 4; */
}

/* Store the LMS private state into data.
 *
 * @param [in]      params     LMS parameters.
 * @param [in]      state      Private key state.
 * @param [in, out] priv_data  Private key data.
 */
static void wc_lms_priv_state_store(const LmsParams* params,
    LmsPrivState* state, byte* priv_data)
{
    /* Authentication path data. */
    priv_data += (word32)params->height * params->hash_len;

    /* Stack of nodes. */
    priv_data += (params->height + 1) * params->hash_len;
    c32toa(state->stack.offset, priv_data);
    priv_data += 4;

    /* Cached root nodes. */
    priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels, params->hash_len);

    /* Cached leaf nodes. */
    priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits, params->hash_len);
    c32toa(state->leaf.idx, priv_data);
    priv_data += 4;
    c32toa(state->leaf.offset, priv_data);
    /* priv_data += 4; */
}

#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
/* Copy LMS private key state.
 *
 * @param [in]  params  LMS parameters.
 * @param [out] dst     LMS private state destination.
 * @param [in]  src     LMS private state source.
 */
static void wc_lms_priv_state_copy(const LmsParams* params,
    LmsPrivState* dst, const LmsPrivState* src)
{
    XMEMCPY(dst->auth_path, src->auth_path, LMS_PRIV_STATE_LEN(params->height,
        params->rootLevels, params->cacheBits, params->hash_len));
    dst->stack.offset = src->stack.offset;
    dst->leaf.idx = src->leaf.idx;
    dst->leaf.offset = src->leaf.offset;
}
#endif /* !WOLFSSL_LMS_NO_SIGN_SMOOTHING */
#endif /* !WOLFSSL_WC_LMS_SMALL */

/* Calculate the leaf node hash.
 *
 * Assumes buffer already contains : I
 *
 * Appendix C.
 *   ...
 *     temp = H(I || u32str(r)|| u16str(D_LEAF) || OTS_PUB_HASH[i])
 *   ...
 * Section 5.3. LMS Public Key
 *                                        ... where we denote the public
 *   key final hash value (namely, the K value computed in Algorithm 1)
 *   associated with the i-th LM-OTS private key as OTS_PUB_HASH[i], ...
 * Algorithm 1: Generating a One-Time Signature Public Key From a
 * Private Key
 *   ...
 *   K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1])
 *   ...
 * Therefore:
 *   OTS_PUB_HASH[i] = H(I || u32str(i) || u16str(D_PBLC) ||
 *                       y[0] || ... || y[p-1])
 *
 * @param [in, out] state  LMS state.
 * @param [in]      seed   Private seed to generate x.
 * @param [in]      i      Index of leaf.
 * @param [in]      r      Leaf hash index.
 * @param [out]     leaf   Leaf node hash.
 */
static int wc_lms_leaf_hash(LmsState* state, const byte* seed, word32 i,
    word32 r, byte* leaf)
{
    int ret;
    byte* buffer = state->buffer;
    byte* rp = buffer + LMS_I_LEN;
    byte* dp = rp + LMS_R_LEN;
    byte* ots_pub_hash = dp + LMS_D_LEN;

    /* I || u32str(i) || ... */
    c32toa(i, rp);
    /* OTS_PUB_HASH[i] = H(I || u32str(i) || u16str(D_PBLC) ||
     *                     y[0] || ... || y[p-1])
     */
    ret = wc_lmots_make_public_hash(state, seed, ots_pub_hash);
    if (ret == 0) {
        /* I || u32str(r) || ... || OTS_PUB_HASH[i] */
        c32toa(r, rp);
        /* I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i] */
        c16toa(LMS_D_LEAF, dp);
        /* temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i]) */
#ifdef WOLFSSL_LMS_SHAKE256
        if (LMS_IS_SHAKE(state->params->lmOtsType)) {
            ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                LMS_SEED_HASH_LEN(state->params->hash_len), leaf,
                state->params->hash_len);
        }
        else
#endif
        {
#ifndef WC_LMS_FULL_HASH
        /* Put in padding for final block. */
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            LMS_SHA256_SET_LEN_46(buffer);
            ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, leaf);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            LMS_SHA256_SET_LEN_54(buffer);
            ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, leaf);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#else
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                LMS_SEED_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), leaf);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                LMS_SEED_HASH_LEN(WC_SHA256_DIGEST_SIZE), leaf);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#endif /* !WC_LMS_FULL_HASH */
        }
    }

    return ret;
}

/* Calculate interior node hash.
 *
 * Appendix C. n Iterative Algorithm for Computing an LMS Public Key
 * Generating an LMS Public Key from an LMS Private Key
 *   ...
 *   left_side = pop(data stack);
 *   temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
 *   ...
 * Popping the stack is done in the caller.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      sp     Stack pointer to left nodes.
 * @param [in]      r      Node hash index.
 * @param [out]     node   Interior node hash.
 */
static int wc_lms_interior_hash(LmsState* state, byte* sp, word32 r,
    byte* node)
{
    int ret;
    byte* buffer = state->buffer;
    byte* rp = buffer + LMS_I_LEN;
    byte* left = rp + LMS_R_LEN + LMS_D_LEN;

    /* I || u32str(r) || u16str(D_INTR) || ... || temp */
    c32toa(r, rp);
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(state->params->lmOtsType)) {
        /* left_side = pop(data stack)
         * I || u32str(r) || u16str(D_INTR) || left_side || temp */
        XMEMCPY(left, sp, state->params->hash_len);
        /* temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) */
        ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
            LMS_NODE_HASH_LEN(state->params->hash_len), node,
            state->params->hash_len);
    }
    else
#endif
#ifdef WOLFSSL_LMS_SHA256_192
    if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        /* left_side = pop(data stack)
         * I || u32str(r) || u16str(D_INTR) || left_side || temp */
        XMEMCPY(left, sp, WC_SHA256_192_DIGEST_SIZE);
        /* temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) */
        ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
            LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), node);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        /* left_side = pop(data stack)
         * I || u32str(r) || u16str(D_INTR) || left_side || temp */
        XMEMCPY(left, sp, WC_SHA256_DIGEST_SIZE);
        /* temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) */
        ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
            LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), node);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }

    return ret;
}

#ifdef WOLFSSL_WC_LMS_SMALL
/* Computes hash of the Merkle tree and gets the authentication path for q.
 *
 * Appendix C: An Iterative Algorithm for Computing an LMS Public Key
 *    for ( i = 0; i < 2^h; i = i + 1 ) {
 *      r = i + num_lmots_keys;
 *      temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i])
 *      j = i;
 *      while (j % 2 == 1) {
 *        r = (r - 1)/2;
 *        j = (j-1) / 2;
 *        left_side = pop(data stack);
 *        temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
 *      }
 *      push temp onto the data stack
 *   }
 *   public_key = pop(data stack)
 *
 * @param [in, out] state      LMS state.
 * @param [in]      id         Unique tree identifier, I.
 * @param [in]      seed       Private seed to generate x.
 * @param [in]      max        Count of leaf nodes to calculate. Must be greater
 *                             than q. Must be a power of 2.
 * @param [in]      q          Index for authentication path.
 * @param [out]     auth_path  Authentication path for index.
 * @param [out]     pub        LMS public key.
 * @param [out]     stack_d    Where to store stack data.
 * @return  0 on success.
 */
static int wc_lms_treehash(LmsState* state, const byte* id, const byte* seed,
    word32 q, byte* auth_path, byte* pub)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte* rp = buffer + LMS_I_LEN;
    byte* dp = rp + LMS_R_LEN;
    byte* left = dp + LMS_D_LEN;
    byte* temp = left + params->hash_len;
    WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
    byte* sp;
    word32 i;

    /* I || ... */
    XMEMCPY(buffer, id, LMS_I_LEN);

    /* Allocate stack of left side hashes. */
    WC_ALLOC_VAR_EX(stack, byte,
        LMS_STACK_CACHE_LEN(params->height, params->hash_len),
        NULL, DYNAMIC_TYPE_TMP_BUFFER,
        { ret=MEMORY_E; });
    sp = stack;

    /* Compute all nodes requested. */
    for (i = 0; (ret == 0) && (i < ((word32)1 << params->height)); i++) {
        word32 j = i;
        word16 h = 0;
        /* r = i + num_lmots_keys */
        word32 r = i + ((word32)1 << (params->height));

        /* Calculate leaf node hash. */
        ret = wc_lms_leaf_hash(state, seed, i, r, temp);

        /* Store the node if on the authentication path. */
        if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) {
            XMEMCPY(auth_path, temp, params->hash_len);
        }

        /* I || ... || u16str(D_INTR) || ... || temp */
        c16toa(LMS_D_INTR, dp);
        /* Calculate parent node is we have both left and right. */
        while ((ret == 0) && ((j & 0x1) == 1)) {
            /* Get parent node index. r and j are odd. */
            r >>= 1;
            j >>= 1;
            h++;

            /* Calculate interior node hash.
             * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
             */
            sp -= params->hash_len;
            ret = wc_lms_interior_hash(state, sp, r, temp);

            /* Copy out node to authentication path if on path. */
            if ((ret == 0) && (auth_path != NULL) && (((q >> h) ^ 0x1) == j)) {
                XMEMCPY(auth_path + h * params->hash_len, temp,
                    params->hash_len);
            }
        }
        /* Push temp onto the data stack. */
        XMEMCPY(sp, temp, params->hash_len);
        sp += params->hash_len;
    }

    if ((ret == 0) && (pub != NULL)) {
        /* Public key, root node, is top of data stack. */
        XMEMCPY(pub, stack, params->hash_len);
    }
    WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return ret;
}

/* Compute the LMS public key - root node of tree.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      id     Unique tree identifier, I.
 * @param [in]      seed   Private seed to generate x.
 * @param [out]     pub    LMS public key.
 * @return  0 on success.
 */
static int wc_lms_make_public_key(LmsState* state, const byte* id,
    const byte* seed, byte* pub)
{
    return wc_lms_treehash(state, id, seed, 0, NULL, pub);
}

/* Calculate the authentication path.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      id     Public random: I.
 * @param [in]      seed   Private random: SEED.
 * @param [in]      q      Index of leaf.
 * @param [out]     sig    Signature buffer to place authentication path into.
 * @param [out]     root   Root node of tree.
 * @return  0 on success.
 */
static int wc_lms_auth_path(LmsState* state, const byte* id, const byte* seed,
    word32 q, byte* sig, byte* root)
{
    return wc_lms_treehash(state, id, seed, q, sig, root);
}
#else
/* Computes hash of the Merkle tree and gets the authentication path for q.
 *
 * Appendix C: An Iterative Algorithm for Computing an LMS Public Key
 *    for ( i = 0; i < 2^h; i = i + 1 ) {
 *      r = i + num_lmots_keys;
 *      temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i])
 *      j = i;
 *      while (j % 2 == 1) {
 *        r = (r - 1)/2;
 *        j = (j-1) / 2;
 *        left_side = pop(data stack);
 *        temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
 *      }
 *      push temp onto the data stack
 *   }
 *   public_key = pop(data stack)
 *
 * @param [in, out] state      LMS state.
 * @param [in, out] privState  LMS state of the private key.
 * @param [in]      id         Unique tree identifier, I.
 * @param [in]      seed       Private seed to generate x.
 * @param [in]      q          Index for authentication path.
 * @return  0 on success.
 */
static int wc_lms_treehash_init(LmsState* state, LmsPrivState* privState,
    const byte* id, const byte* seed, word32 q)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte* auth_path = privState->auth_path;
    byte* root = privState->root;
    HssLeafCache* leaf = &privState->leaf;
    byte* rp = buffer + LMS_I_LEN;
    byte* dp = rp + LMS_R_LEN;
    byte* left = dp + LMS_D_LEN;
    byte* temp = left + params->hash_len;
    WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
    word32 spi = 0;
    word32 i;
    word32 max_h = (word32)1 << params->height;
    word32 max_cb = (word32)1 << params->cacheBits;

    privState->stack.offset = 0;
    /* Reset the cached stack. */
    leaf->offset = 0;
    leaf->idx = q;
    if ((q + max_cb) > max_h) {
        leaf->idx = max_h - max_cb;
    }

    /* I || ... */
    XMEMCPY(buffer, id, LMS_I_LEN);

    /* Allocate stack of left side hashes. */
    WC_ALLOC_VAR_EX(stack, byte,
        LMS_STACK_CACHE_LEN(params->height, params->hash_len),
        NULL, DYNAMIC_TYPE_TMP_BUFFER,
        { ret=MEMORY_E; });

    /* Compute all nodes requested. */
    for (i = 0; (ret == 0) && (i < max_h); i++) {
        word32 j = i;
        word16 h = 0;
        /* r = i + num_lmots_keys */
        word32 r = i + max_h;

        /* Calculate leaf node hash. */
        ret = wc_lms_leaf_hash(state, seed, i, r, temp);

        /* Cache leaf node if in range. */
        if ((ret == 0) && (i >= leaf->idx) && (i < leaf->idx + max_cb)) {
            XMEMCPY(leaf->cache + (i - leaf->idx) * params->hash_len, temp,
                    params->hash_len);
        }

        /* Store the node if on the authentication path. */
        if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) {
            XMEMCPY(auth_path, temp, params->hash_len);
        }

        /* I || ... || u16str(D_INTR) || ... || temp */
        c16toa(LMS_D_INTR, dp);
        /* Calculate parent node is we have both left and right. */
        while ((ret == 0) && ((j & 0x1) == 1)) {
            /* Get parent node index. r and j are odd. */
            r >>= 1;
            j >>= 1;
            h++;

            /* Calculate interior node hash.
             * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
             */
            spi -= params->hash_len;
            ret = wc_lms_interior_hash(state, stack + spi, r, temp);

            /* Copy out top root nodes. */
            if ((h > params->height - params->rootLevels) &&
                    ((i >> (h-1)) != ((i + 1) >> (h - 1)))) {
                word32 off = ((word32)1U << (params->height - h)) +
                             (i >> h) - 1U;
                XMEMCPY(root + off * params->hash_len, temp, params->hash_len);
            }

            /* Copy out node to authentication path if on path. */
            if ((ret == 0) && (auth_path != NULL) && (((q >> h) ^ 0x1) == j)) {
                XMEMCPY(auth_path + h * params->hash_len, temp,
                    params->hash_len);
            }
        }
        /* Push temp onto the data stack. */
        XMEMCPY(stack + spi, temp, params->hash_len);
        spi += params->hash_len;

        if (i == q - 1) {
            XMEMCPY(privState->stack.stack, stack, spi);
            privState->stack.offset = spi;
        }
    }

    WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return ret;
}

/* Computes hash of the Merkle tree and gets the authentication path for q.
 *
 * Appendix C: An Iterative Algorithm for Computing an LMS Public Key
 *    for ( i = 0; i < 2^h; i = i + 1 ) {
 *      r = i + num_lmots_keys;
 *      temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i])
 *      j = i;
 *      while (j % 2 == 1) {
 *        r = (r - 1)/2;
 *        j = (j-1) / 2;
 *        left_side = pop(data stack);
 *        temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp)
 *      }
 *      push temp onto the data stack
 *   }
 *   public_key = pop(data stack)
 *
 * @param [in, out] state       LMS state.
 * @param [in, out] privState   LMS state of the private key.
 * @param [in]      id          Unique tree identifier, I.
 * @param [in]      seed        Private seed to generate x.
 * @param [in]      min_idx     Minimum leaf index to process.
 * @param [in]      max_idx     Maximum leaf index to process.
 * @param [in]      q           Index for authentication path.
 * @param [in]      useRoot     Whether to use nodes from root cache.
 * @return  0 on success.
 */
static int wc_lms_treehash_update(LmsState* state, LmsPrivState* privState,
    const byte* id, const byte* seed, word32 min_idx, word32 max_idx, word32 q,
    int useRoot)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte* auth_path = privState->auth_path;
    LmsStack* stackCache = &privState->stack;
    HssLeafCache* leaf = &privState->leaf;
    byte* rp = buffer + LMS_I_LEN;
    byte* dp = rp + LMS_R_LEN;
    byte* left = dp + LMS_D_LEN;
    byte* temp = left + params->hash_len;
    WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
    byte* sp;
    word32 max_cb = (word32)1 << params->cacheBits;
    word32 i;

    /* I || ... */
    XMEMCPY(buffer, id, LMS_I_LEN);

    /* Allocate stack of left side hashes. */
    WC_ALLOC_VAR_EX(stack, byte,
        LMS_STACK_CACHE_LEN(params->height, params->hash_len),
        NULL, DYNAMIC_TYPE_TMP_BUFFER,
        { ret=MEMORY_E; });

    /* Public key, root node, is top of data stack. */
    if (ret == 0) {
        XMEMCPY(stack, stackCache->stack,
                (word32)params->height * params->hash_len);
        sp = stack + stackCache->offset;
    }

    /* Compute all nodes requested. */
    for (i = min_idx; (ret == 0) && (i <= max_idx); i++) {
        word32 j = i;
        word16 h = 0;
        /* r = i + num_lmots_keys */
        word32 r = i + ((word32)1 << (params->height));

        if ((i >= leaf->idx) && (i < leaf->idx + max_cb)) {
            /* Calculate offset of node in cache. */
            word32 off = ((i - (leaf->idx + max_cb) + leaf->offset) % max_cb) *
                params->hash_len;
            /* Copy cached node into working buffer. */
            XMEMCPY(temp, leaf->cache + off, params->hash_len);
        }
        else {
            /* Calculate leaf node hash. */
            ret = wc_lms_leaf_hash(state, seed, i, r, temp);

            /* Slide the leaf cache forward by one slot when i is exactly the
             * leaf immediately past the cached window and still within the
             * window we will need to cover q. Callers (wc_hss_init_auth_path /
             * wc_hss_update_auth_path) advance i contiguously, so i never
             * jumps past leaf->idx + max_cb in normal use; if that invariant
             * is broken, the cache stays put and i is silently uncached
             * (correct, but defeats the cache). */
            if (i > leaf->idx + max_cb) {
                WOLFSSL_MSG("Bad value for index");
            }
            if ((i == leaf->idx + max_cb) && (i < (q + max_cb))) {
                /* Copy working node into cache over old first node. */
                XMEMCPY(leaf->cache + leaf->offset * params->hash_len, temp,
                    params->hash_len);
                /* Increase start index as first node replaced. */
                leaf->idx++;
                /* Update offset of first leaf node. */
                leaf->offset = (leaf->offset + 1) & (max_cb - 1);
            }
        }

        /* Store the node if on the authentication path. */
        if ((ret == 0) && ((q ^ 0x1) == i)) {
            XMEMCPY(auth_path, temp, params->hash_len);
        }

        /* I || ... || u16str(D_INTR) || ... || temp */
        c16toa(LMS_D_INTR, dp);
        /* Calculate parent node if we have both left and right. */
        while ((ret == 0) && ((j & 0x1) == 1)) {
            /* Get parent node index. r and j are odd. */
            r >>= 1;
            j >>= 1;
            h++;

            sp -= params->hash_len;
            if (useRoot && (h > params->height - params->rootLevels) &&
                    (h <= params->height)) {
                /* Calculate offset of cached root node. */
                word32 off = ((word32)1U << (params->height - h)) +
                    (i >> h) - 1;
                XMEMCPY(temp, privState->root + (off * params->hash_len),
                    params->hash_len);
            }
            else {
                /* Calculate interior node hash.
                 * temp = H(I || u32str(r) || u16str(D_INTR) || left_side ||
                 *          temp)
                 */
                ret = wc_lms_interior_hash(state, sp, r, temp);
            }

            /* Copy out top root nodes. */
            if ((ret == 0) && (q == 0) && (!useRoot) &&
                    (h > params->height - params->rootLevels) &&
                    ((i >> (h-1)) != ((i + 1) >> (h - 1)))) {
                word32 off = ((word32)1U << (params->height - h)) +
                             (i >> h) - 1U;
                XMEMCPY(privState->root + off * params->hash_len, temp,
                    params->hash_len);
            }

            /* Copy out node to authentication path if on path. */
            if ((ret == 0) && (((q >> h) ^ 0x1) == j)) {
                XMEMCPY(auth_path + h * params->hash_len, temp,
                    params->hash_len);
            }
        }
        if (ret == 0) {
            /* Push temp onto the data stack. */
            XMEMCPY(sp, temp, params->hash_len);
            sp += params->hash_len;

            /* Save stack after updating first node. */
            if (i == min_idx) {
                /* Copy stack back. */
                stackCache->offset = (word32)((size_t)sp - (size_t)stack);
                XMEMCPY(stackCache->stack, stack, stackCache->offset);
            }
        }
    }

    if (ret == 0) {
        if (!useRoot) {
            /* Copy stack back. */
            XMEMCPY(stackCache->stack, stack,
                    (word32)params->height * params->hash_len);
            stackCache->offset = (word32)((size_t)sp - (size_t)stack);
        }
    }

    WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
    return ret;
}
#endif /* WOLFSSL_WC_LMS_SMALL */

/* Sign message using LMS.
 *
 * Appendix D. Method for Deriving Authentication Path for a Signature.
 * Generating an LMS Signature
 *   ...
 *   3. Create the LM-OTS signature for the message:
 *      ots_signature = lmots_sign(message, LMS_PRIV[q])
 *   4. Compute the array path as follows:
 *      ...
 *   5. S = u32str(q) || ots_signature || u32str(type) ||
 *                           path[0] || path[1] || ... || path[h-1]
 *   ...
 * path[] added by caller as it can come from cache.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      priv   LMS private key.
 * @param [in]      msg    Message/public key to sign.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [out]     sig    LMS signature.
 * @return  0 on success.
 */
static int wc_lms_sign(LmsState* state, const byte* priv, const byte* msg,
    word32 msgSz, byte* sig)
{
    int ret;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte* s = sig;
    const byte* priv_q = priv;
    const byte* priv_seed = priv_q + LMS_Q_LEN;
    const byte* priv_i = priv_seed + params->hash_len;

    /* Setup for hashing: I || Q */
    XMEMCPY(buffer, priv_i, LMS_I_LEN);
    XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN);

    /* Copy q from private key.
     * S = u32str(q) || ... */
    XMEMCPY(s, priv_q, LMS_Q_LEN);
    s += LMS_Q_LEN;

    /* ots_signature = sig = u32str(type) || ... */
    c32toa(state->params->lmOtsType & LMS_H_W_MASK, s);
    s += LMS_TYPE_LEN;
    /* Sign this level.
     * S = u32str(q) || ots_signature || ... */
    ret = wc_lmots_sign(state, priv_seed, msg, msgSz, s);
    if (ret == 0) {
        /* Skip over ots_signature. */
        s += params->hash_len + params->p * params->hash_len;
        /* S = u32str(q) || ots_signature || u32str(type) || ... */
        c32toa(params->lmsType & LMS_H_W_MASK, s);
    }

    return ret;
}

#if !defined(WOLFSSL_WC_LMS_SMALL) && !defined(WOLFSSL_LMS_NO_SIG_CACHE)
/* Copy in the cached signature data.
 *
 * @param [in]  params    LMS parameters.
 * @param [in]  y         y cache.
 * @param [in]  priv      Private key data.
 * @param [out] sig       Signature data.
 */
static void wc_lms_sig_copy(const LmsParams* params, const byte* y,
    const byte* priv, byte* sig)
{
    /* Put in q. */
    XMEMCPY(sig, priv, LMS_Q_LEN);
    sig += LMS_Q_LEN;
    /* S = u32str(q) || ... */
    c32toa(params->lmOtsType & LMS_H_W_MASK, sig);
    sig += LMS_TYPE_LEN;
    /* S = u32str(q) || ots_signature || ... */
    XMEMCPY(sig, y, LMOTS_Y_LEN(params->p, params->hash_len));
    sig += LMOTS_Y_LEN(params->p, params->hash_len);
    /* S = u32str(q) || ots_signature || u32str(type) || ... */
    c32toa(params->lmsType & LMS_H_W_MASK, sig);
}
#endif /* !WOLFSSL_WC_LMS_SMALL && !WOLFSSL_LMS_NO_SIG_CACHE */
#endif /* !WOLFSSL_LMS_VERIFY_ONLY */

/* Compute the root node of the LMS tree.
 *
 * Algorithm 6a: Computing an LMS Public Key Candidate from a Signature,
 * Message, Identifier, and Algorithm Typecodes
 *   ...
 *   4. Compute the candidate LMS root value Tc as follows:
 *      node_num = 2^h + q
 *      tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc)
 *      i = 0
 *      while (node_num > 1) {
 *        if (node_num is odd):
 *          tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
 *        else:
 *          tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
 *        node_num = node_num/2
 *        i = i + 1
 *      }
 *      Tc = tmp
 *   5. Return Tc.
 *
 * @param [in, out]  state  LMS state.
 * @param [in]       q      Index of node.
 * @param [in]       kc     K candidate.
 * @param [in]       path   Authentication path from signature.
 * @param [out]      tc     T candidate.
 * @return  0 on success.
 */
static int wc_lms_compute_root(LmsState* state, word32 q, const byte* kc,
    const byte* path, byte* tc)
{
    int ret;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte* rp = buffer + LMS_I_LEN;
    byte* ip = rp + LMS_Q_LEN;
    byte* node = ip + LMS_P_LEN;
    byte* b[2][2];
    /* node_num = 2^h + q */
    word32 r = ((word32)1 << params->height) + q;

    /* tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc) */
    c32toa(r, rp);
    c16toa(LMS_D_LEAF, ip);
    XMEMCPY(node, kc, params->hash_len);
    /* Put tmp into offset required for first iteration. */
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(params->lmOtsType)) {
        b[0][0] = node;
        b[0][1] = node + params->hash_len;
        b[1][0] = node + params->hash_len;
        b[1][1] = node;
        ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
            LMS_SEED_HASH_LEN(params->hash_len), b[r & 1][0],
            params->hash_len);
    }
    else
#endif
    {
#ifndef WC_LMS_FULL_HASH
    /* Put in padding for final block. */
#ifdef WOLFSSL_LMS_SHA256_192
    if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        b[0][0] = node;
        b[0][1] = node + WC_SHA256_192_DIGEST_SIZE;
        b[1][0] = node + WC_SHA256_192_DIGEST_SIZE;
        b[1][1] = node;
        LMS_SHA256_SET_LEN_46(buffer);
        ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, b[r & 1][0]);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        b[0][0] = node;
        b[0][1] = node + WC_SHA256_DIGEST_SIZE;
        b[1][0] = node + WC_SHA256_DIGEST_SIZE;
        b[1][1] = node;
        LMS_SHA256_SET_LEN_54(buffer);
        ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, b[r & 1][0]);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#else
#ifdef WOLFSSL_LMS_SHA256_192
    if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        b[0][0] = node;
        b[0][1] = node + WC_SHA256_192_DIGEST_SIZE;
        b[1][0] = node + WC_SHA256_192_DIGEST_SIZE;
        b[1][1] = node;
        ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
            LMS_SEED_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), b[r & 1][0]);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        b[0][0] = node;
        b[0][1] = node + WC_SHA256_DIGEST_SIZE;
        b[1][0] = node + WC_SHA256_DIGEST_SIZE;
        b[1][1] = node;
        ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
            LMS_SEED_HASH_LEN(WC_SHA256_DIGEST_SIZE), b[r & 1][0]);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#endif /* !WC_LMS_FULL_HASH */
    }

    if (ret == 0) {
        int i;

        /* I||...||u16str(D_INT)||... */
        c16toa(LMS_D_INTR, ip);

        /* Do all but last height. */
    #ifdef WOLFSSL_LMS_SHAKE256
        if (LMS_IS_SHAKE(params->lmOtsType)) {
            for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
                /* Put path into offset required. */
                XMEMCPY(b[r & 1][1], path, params->hash_len);
                path += params->hash_len;

                /* node_num = node_num / 2 */
                r >>= 1;
                /*  H(...||u32str(node_num/2)||..) */
                c32toa(r, rp);
                ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                    LMS_NODE_HASH_LEN(params->hash_len), b[r & 1][0],
                    params->hash_len);
            }
            if (ret == 0) {
                /* Last height. */
                XMEMCPY(b[r & 1][1], path, params->hash_len);
                r >>= 1;
                c32toa(r, rp);
                ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                    LMS_NODE_HASH_LEN(params->hash_len), tc, params->hash_len);
            }
        }
        else
    #endif
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
                /* Put path into offset required. */
                XMEMCPY(b[r & 1][1], path, WC_SHA256_192_DIGEST_SIZE);
                path += WC_SHA256_192_DIGEST_SIZE;

                /* node_num = node_num / 2 */
                r >>= 1;
                /*  H(...||u32str(node_num/2)||..) */
                c32toa(r, rp);
                /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
                 * or
                 * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
                 * Put tmp result into offset required for next iteration. */
                ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                    LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), b[r & 1][0]);
            }
            if (ret == 0) {
                /* Last height. */
                /* Put path into offset required. */
                XMEMCPY(b[r & 1][1], path, WC_SHA256_192_DIGEST_SIZE);
                /* node_num = node_num / 2 */
                r >>= 1;
                /*  H(...||u32str(node_num/2)||..) */
                c32toa(r, rp);
                /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
                 * or
                 * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
                 * Put tmp result into Tc.*/
                ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                    LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), tc);
            }
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
                /* Put path into offset required. */
                XMEMCPY(b[r & 1][1], path, WC_SHA256_DIGEST_SIZE);
                path += WC_SHA256_DIGEST_SIZE;

                /* node_num = node_num / 2 */
                r >>= 1;
                /*  H(...||u32str(node_num/2)||..) */
                c32toa(r, rp);
                /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
                 * or
                 * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
                 * Put tmp result into offset required for next iteration. */
                ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                    LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), b[r & 1][0]);
            }
            if (ret == 0) {
                /* Last height. */
                /* Put path into offset required. */
                XMEMCPY(b[r & 1][1], path, WC_SHA256_DIGEST_SIZE);
                /* node_num = node_num / 2 */
                r >>= 1;
                /*  H(...||u32str(node_num/2)||..) */
                c32toa(r, rp);
                /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
                 * or
                 * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
                 * Put tmp result into Tc.*/
                ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                    LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), tc);
            }
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
    }

    return ret;
}

/* LMS verify message using public key and signature.
 *
 * Algorithm 6a: Computing an LMS Public Key Candidate from a Signature,
 * Message, Identifier, and Algorithm Typecodes
 *   ...
 *   2. Parse sigtype, q, lmots_signature, and path from the signature
 *      as follows:
 *      a. q = strTou32(first 4 bytes of signature)
 *      ...
 *      e. lmots_signature = bytes 4 through 7 + n * (p + 1)
 *         of signature
 *      ...
 *      j. Set path as follows:
 *           path[0] = next m bytes of signature
 *           path[1] = next m bytes of signature
 *              ...
 *         path[h-1] = next m bytes of signature
 *   3. Kc = candidate public key computed by applying Algorithm 4b
 *      to the signature lmots_signature, the message, and the
 *      identifiers I, q
 *   4. Compute the candidate LMS root value Tc as follows:
 *      ...
 *   5. Return Tc
 * Algorithm 6: LMS Signature Verification
 *   ...
 *   3. Compute the LMS Public Key Candidate Tc from the signature,
 *      message, identifier, pubtype, and ots_typecode, using
 *      Algorithm 6a.
 *   4. If Tc is equal to T[1], return VALID; otherwise, return INVALID.
 *
 * @param [in, out] state  LMS state.
 * @param [in]      pub    LMS public key.
 * @param [in]      msg    Message/public key to verify.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [in]      sig    LMS signature.
 */
static int wc_lms_verify(LmsState* state, const byte* pub, const byte* msg,
    word32 msgSz, const byte* sig, word32 sigSz)
{
    int ret;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    const byte* pub_i = pub + LMS_TYPE_LEN + LMS_TYPE_LEN;
    const byte* pub_k = pub_i + LMS_I_LEN;
    const byte* sig_q = sig;
    byte tc[LMS_MAX_NODE_LEN];
    byte* kc = tc;
    /* Bytes consumed by this LMS signature: q || lmots_type || C || y[p] ||
     * lms_type || path[height]. wc_lms_verify reads exactly this much from
     * sig; the caller (wc_hss_verify or wc_LmsKey_Verify) has guaranteed
     * sigSz covers it, but check defensively here as well. */
    const word32 lms_sig_required =
        LMS_SIG_LEN(params->height, params->p, params->hash_len);

    if (sigSz < lms_sig_required) {
        return BUFFER_E;
    }

    /* Algorithm 6. Step 3. */
    /* Check the public key LMS type matches parameters. */
    ret = wc_lmots_public_key_check(params, pub);
    if (ret == 0) {
        /* Algorithm 6a. Step 2.e. */
        const byte* sig_lmots = sig + LMS_Q_LEN;

        /* Setup buffer with I || Q. */
        XMEMCPY(buffer, pub_i, LMS_I_LEN);
        XMEMCPY(buffer + LMS_I_LEN, sig_q, LMS_Q_LEN);

        /* Algorithm 6a. Step 3. */
        ret = wc_lmots_calc_kc(state, pub + LMS_TYPE_LEN, msg, msgSz,
            sig_lmots, kc);
    }
    if (ret == 0) {
        /* Algorithm 6a. Step 3.d-e: Check LMS type in signature matches
         * the expected type from the public key.
         *
         * Bounds: the upfront sigSz check above guarantees the 4-byte
         * lms_type field at this offset is within the buffer.
         *
         * Mask: params->lmsType holds wolfSSL-internal flags (0xf000)
         * identifying the hash family alongside the RFC 8554 type code
         * (low 12 bits, LMS_H_W_MASK). The wire format strips the
         * private flags (see encoder lines 2483, 2510, 1559), so the
         * comparison is against the RFC type code only. This is safe so
         * long as the (lmsType, lmOtsType) pair, masked to the low 12 bits,
         * is unique across the static map -- i.e., no two  entries from
         * different hash families happen to have the same RFC code pair.
         * All current entries have matching hash families, so the pair is
         * trivially unique. A future entry mixing families would need this
         * checked explicitly.  Any future parameter set that introduces a
         * collision in the low 12 bits would require this check to compare
         * the full lmsType, not the masked form. */
        const byte* sig_lms_type = sig + LMS_Q_LEN + LMS_TYPE_LEN +
            params->hash_len + params->p * params->hash_len;
        word32 sigType;

        ato32(sig_lms_type, &sigType);
        if (sigType != (params->lmsType & LMS_H_W_MASK)) {
            ret = SIG_TYPE_E;
        }
    }
    if (ret == 0) {
        /* Algorithm 6a. Step 2.j. */
        const byte* sig_path = sig + LMS_Q_LEN + LMS_TYPE_LEN +
            params->hash_len + params->p * params->hash_len + LMS_TYPE_LEN;
        word32 q;

        /* Algorithm 6a. Step 2.a. */
        ato32(sig_q, &q);

        /* Algorithm 6a. Steps 4-5. */
        ret = wc_lms_compute_root(state, q, kc, sig_path, tc);
    }
    /* Algorithm 6. Step 4. */
    if ((ret == 0) && (XMEMCMP(pub_k, tc, params->hash_len) != 0)) {
        ret = SIG_VERIFY_E;
    }

    return ret;
}

/***************************************
 * HSS APIs
 **************************************/

#ifndef WOLFSSL_LMS_VERIFY_ONLY
/* Derive the seed and i for child.
 *
 * @param [in, out] state   LMS state.
 * @param [in]      id      Parent's I.
 * @param [in]      seed    Parent's SEED.
 * @param [in]      q       Parent's q.
 * @param [out]     seed_i  Derived SEED and I.
 * @return  0 on success.
 */
static int wc_hss_derive_seed_i(LmsState* state, const byte* id,
    const byte* seed, const byte* q, byte* seed_i)
{
    int ret = 0;
    byte buffer[WC_SHA256_BLOCK_SIZE];
    byte* idp = buffer;
    byte* qp = idp + LMS_I_LEN;
    byte* ip = qp + LMS_Q_LEN;
    byte* jp = ip + LMS_P_LEN;
    byte* tmp = jp + LMS_W_LEN;

    /* parent's I || ... */
    XMEMCPY(idp, id, LMS_I_LEN);
    /* parent's I || q || ... */
    XMEMCPY(qp, q, LMS_Q_LEN);
    /* parent's I || q || D_CHILD_SEED || ... */
    c16toa(LMS_D_CHILD_SEED, ip);
    /* parent's I || q || D_CHILD_SEED || D_FIXED || ... */
    *jp = LMS_D_FIXED;
    /* parent's I || q || D_CHILD_SEED || D_FIXED || parent's SEED */
    XMEMCPY(tmp, seed, state->params->hash_len);
    /* SEED = H(parent's I || q || D_CHILD_SEED || D_FIXED || parent's SEED) */
#ifdef WOLFSSL_LMS_SHAKE256
    if (LMS_IS_SHAKE(state->params->lmOtsType)) {
        ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
            LMS_HASH_BUFFER_LEN(state->params->hash_len), seed_i,
            state->params->hash_len);
        if (ret == 0) {
            seed_i += state->params->hash_len;
        }
    }
    else
#endif
    {
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
    if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        /* Put in padding for final block. */
        LMS_SHA256_SET_LEN_47(buffer);
        ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, seed_i);
        if (ret == 0) {
            seed_i += WC_SHA256_192_DIGEST_SIZE;
        }
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        /* Put in padding for final block. */
        LMS_SHA256_SET_LEN_55(buffer);
        ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, seed_i);
        if (ret == 0) {
            seed_i += WC_SHA256_DIGEST_SIZE;
        }
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#else
#ifdef WOLFSSL_LMS_SHA256_192
    if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
        ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
            LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), seed_i);
    }
    else
#endif
    {
    #ifndef WOLFSSL_NO_LMS_SHA256_256
        ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
            LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), seed_i);
    #else
        ret = NOT_COMPILED_IN;
    #endif
    }
#endif /* !WC_LMS_FULL_HASH */
    }

    if (ret == 0) {
        /* parent's I || q || D_CHILD_I || D_FIXED || parent's SEED */
        c16toa(LMS_D_CHILD_I, ip);
        /* I = H(parent's I || q || D_CHILD_I || D_FIXED || parent's SEED) */
#ifdef WOLFSSL_LMS_SHAKE256
        if (LMS_IS_SHAKE(state->params->lmOtsType)) {
            ret = wc_lms_shake256_hash(LMS_STATE_SHAKE(state), buffer,
                LMS_HASH_BUFFER_LEN(state->params->hash_len), tmp,
                state->params->hash_len);
        }
        else
#endif
        {
#ifndef WC_LMS_FULL_HASH
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            ret = wc_lms_sha256_192_hash_block(LMS_STATE_HASH(state), buffer, tmp);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            ret = wc_lms_hash_block(LMS_STATE_HASH(state), buffer, tmp);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#else
    #ifdef WOLFSSL_LMS_SHA256_192
        if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
            ret = wc_lms_hash_sha256_192(LMS_STATE_HASH(state), buffer,
                LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
        }
        else
    #endif
        {
        #ifndef WOLFSSL_NO_LMS_SHA256_256
            ret = wc_lms_hash(LMS_STATE_HASH(state), buffer,
                LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
        #else
            ret = NOT_COMPILED_IN;
        #endif
        }
#endif /* !WC_LMS_FULL_HASH */
        }
        /* Copy part of hash as new I into private key. */
        XMEMCPY(seed_i, tmp, LMS_I_LEN);
    }

    return ret;
}

/* Get q, index, of leaf at the specified level. */
#define LMS_Q_AT_LEVEL(q, ls, l, h)                                 \
    (w64GetLow32(w64ShiftRight((q), (((ls) - 1 - (l)) * (h)))) &    \
     (((word32)1U << (h)) - 1U))

/* Expand the seed and I for further levels and set q for each level.
 *
 * @param [in, out] state     LMS state.
 * @param [in, out] priv      Private key for use in signing.
 * @param [in]      priv_raw  Private key read.
 * @param [in]      inc       Whether this is an incremental expansion.
 * @return  0 on success.
 */
static int wc_hss_expand_private_key(LmsState* state, byte* priv,
    const byte* priv_raw, int inc)
{
    const LmsParams* params = state->params;
    int ret = 0;
    w64wrapper q;
    w64wrapper qm1;
    word32 q32;
    byte* priv_q;
    byte* priv_seed_i;
    word32 i;

    /* Get the 64-bit q value from the raw private key. */
    ato64(priv_raw, &q);
    /* Step over q and parameter set. */
    priv_raw += HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN;

    /* Get q of highest level. */
    q32 = LMS_Q_AT_LEVEL(q, params->levels, 0, params->height);
    /* Set q of highest tree. */
    c32toa(q32, priv);

    /* Incremental expansion needs q-1. */
    if (inc) {
        /* Calculate q-1 for comparison. */
        qm1 = q;
        w64Decrement(&qm1);
    }
    else {
        /* Copy out SEED and I into private key. */
        XMEMCPY(priv + LMS_Q_LEN, priv_raw, params->hash_len + LMS_I_LEN);
    }

    /* Compute SEED and I for rest of levels. */
    for (i = 1U; (ret == 0) && (i < params->levels); i++) {
        /* Don't skip calculating SEED and I. */
        int skip = 0;

        /* Incremental means q, SEED and I already present if q unchanged. */
        if (inc) {
            /* Calculate previous levels q for previous 64-bit q value. */
            word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, (int)i - 1,
                params->height);
            /* Same q at previous level means no need to re-compute. */
            if (q32 == qm1_32) {
                /* Do skip calculating SEED and I. */
                skip = 1;
            }
        }

        /* Get pointers into private q to write q and seed + I. */
        priv_q = priv;
        priv += LMS_Q_LEN;
        priv_seed_i = priv;
        priv += params->hash_len + LMS_I_LEN;

        /* Get q for level from 64-bit composite. */
        q32 = w64GetLow32(w64ShiftRight(q, (int)(params->levels - 1U - i) *
            params->height)) & (((word32)1U << params->height) - 1U);
        /* Set q of tree. */
        c32toa(q32, priv);

        if (!skip) {
            /* Derive SEED and I into private key. */
            ret = wc_hss_derive_seed_i(state, priv_seed_i + params->hash_len,
                priv_seed_i, priv_q, priv + LMS_Q_LEN);
        }
    }

    return ret;
}

#ifndef WOLFSSL_WC_LMS_SMALL
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
/* Initialize the next subtree.
 *
 * @param [in] state      LMS state.
 * @param [in] privState  LMS private state.
 * @param [in] curr       Current private key.
 * @param [in] priv       Next private key.
 * @param [in] q          q for this level.
 * @return  0 on success.
 */
static int wc_lms_next_subtree_init(LmsState* state, LmsPrivState* privState,
    byte* curr, byte* priv, word32 q)
{
    int ret;
    const LmsParams* params = state->params;
    byte* priv_q;
    byte* priv_seed;
    byte* priv_i;
    word32 pq;

    /* Get next key pointer. */
    priv_q = priv;
    /* Get pointers of current private. */
    priv_seed = curr + LMS_Q_LEN;
    priv_i = curr + LMS_Q_LEN + params->hash_len;
    /* Move next private key to next leaf for updating.*/
    priv += LMS_Q_LEN + params->hash_len + LMS_I_LEN;

    ato32(curr, &pq);
    pq = (pq + 1U) & ((((word32)1U) << params->height) - (word32)1U);
    c32toa(pq, priv_q);

    privState->stack.offset = 0;
    /* No unary minus on unsigned; avoids MSVC C4146 and passes clang-tidy */
    privState->leaf.idx = (word32)(0U - ((word32)1U << params->cacheBits));
    privState->leaf.offset = 0;

    /* Derive SEED and I for next tree. */
    ret = wc_hss_derive_seed_i(state, priv_i, priv_seed, priv_q,
        priv + LMS_Q_LEN);
    if (ret == 0) {
        /* Update treehash for first leaf. */
        ret = wc_lms_treehash_update(state, privState,
            priv + LMS_Q_LEN + params->hash_len, priv + LMS_Q_LEN, 0, q, 0, 0);
    }

    return ret;
}

/* Increment count on next subtree.
 *
 * @param [in] state     LMS state.
 * @param [in] priv_key  HSS private key.
 * @param [in] q64       64-bit q for all levels.
 * @return  0 on success.
 */
static int wc_hss_next_subtree_inc(LmsState* state, HssPrivKey* priv_key,
    w64wrapper q64)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* curr = priv_key->priv;
    byte* priv = priv_key->next_priv;
    int i;
    w64wrapper p64 = q64;
    byte tmp_priv[LMS_PRIV_LEN(LMS_MAX_NODE_LEN)];
    int use_tmp = 0;
    word32 lastQMax = 0;
    w64wrapper p64_hi;
    w64wrapper q64_hi;

    /* Get previous index. */
    w64Decrement(&p64);
    /* Get index of previous and current parent. */
    p64_hi = w64ShiftRight(p64, (params->levels - 1) * params->height);
    q64_hi = w64ShiftRight(q64, (params->levels - 1) * params->height);
    for (i = 1; (ret == 0) && (i < params->levels); i++) {
        word32 qc;
        w64wrapper cp64_hi;
        w64wrapper cq64_hi;

        /* Get index of previous and current child. */
        cp64_hi = w64ShiftRight(p64, (params->levels - i - 1) * params->height);
        cq64_hi = w64ShiftRight(q64, (params->levels - i - 1) * params->height);
        /* Get the q for the child. */
        ato32(curr + LMS_PRIV_LEN(params->hash_len), &qc);

        /* Compare index of parent node with previous value. */
        if (w64LT(p64_hi, q64_hi)) {
            wc_lms_priv_state_copy(params, &priv_key->state[i],
                &priv_key->next_state[i-1]);
            ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1],
                use_tmp ? tmp_priv : curr, priv, 0);
            use_tmp = 0;
        }
        /* Check whether the child is in a new subtree. */
        else if ((qc == ((word32)1 << params->height) - 1) &&
                w64LT(cp64_hi, cq64_hi)) {
            XMEMSET(tmp_priv, 0, LMS_Q_LEN);
            /* Check whether the node at the previous level is also in a new
             * subtree. */
            if (lastQMax) {
                /* Calculate new SEED and I based on new subtree. */
                ret = wc_hss_derive_seed_i(state,
                    priv + LMS_Q_LEN + params->hash_len, priv + LMS_Q_LEN,
                    tmp_priv, tmp_priv + LMS_Q_LEN);
            }
            else {
                /* Calculate new SEED and I based on parent. */
                ret = wc_hss_derive_seed_i(state,
                    curr + LMS_Q_LEN + params->hash_len, curr + LMS_Q_LEN, priv,
                    tmp_priv + LMS_Q_LEN);
            }
            /* Values not stored so note that they are in temporary. */
            use_tmp = 1;

            /* Set the the q. */
            XMEMCPY(tmp_priv, curr + LMS_PRIV_LEN(params->hash_len), LMS_Q_LEN);
        }

        lastQMax = (qc == (((word32)1U << params->height) - (word32)1U));
        curr += LMS_PRIV_LEN(params->hash_len);
        priv += LMS_PRIV_LEN(params->hash_len);
        p64_hi = cp64_hi;
        q64_hi = cq64_hi;
    }

    ForceZero(tmp_priv, sizeof(tmp_priv));
    return ret;
}

/* Initialize the next subtree for each level bar the highest.
 *
 * @param [in, out] state     LMS state.
 * @param [out]     priv_key  Private key data.
 * @return  0 on success.
 */
static int wc_hss_next_subtrees_init(LmsState* state, HssPrivKey* priv_key)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* curr = priv_key->priv;
    byte* priv = priv_key->next_priv;
    int i;

    XMEMCPY(priv, curr, LMS_PRIV_LEN(params->hash_len));
    wc_lms_idx_inc(priv, LMS_Q_LEN);

    for (i = 1; (ret == 0) && (i < params->levels); i++) {
        word32 q;

        ato32(curr + LMS_PRIV_LEN(params->hash_len), &q);
        ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1],
            curr, priv, q);

        curr += LMS_PRIV_LEN(params->hash_len);
        priv += LMS_PRIV_LEN(params->hash_len);
    }

    return ret;
}
#endif

/* Update the authentication path and caches.
 *
 * @param [in, out] state     LMS state.
 * @param [in, out] priv_key  Private key information.
 * @param [in]      levels    Number of level to start at.
 * @param [out]     pub_root  Public root.
 * @return  0 on success.
 */
static int wc_hss_init_auth_path(LmsState* state, HssPrivKey* priv_key,
    byte* pub_root)
{
    int ret = 0;
    int levels = state->params->levels;
    byte* priv = priv_key->priv +
        LMS_PRIV_LEN(state->params->hash_len) * (word32)(levels - 1);
    int l;

    for (l = levels - 1; (ret == 0) && (l >= 0); l--) {
        word32 q;
        const byte* priv_q = priv;
        const byte* priv_seed = priv_q + LMS_Q_LEN;
        const byte* priv_i = priv_seed + state->params->hash_len;

        /* Get current q for tree at level. */
        ato32(priv_q, &q);
        /* Set cache start to a value that indicates no numbers available. */
        ret = wc_lms_treehash_init(state, &priv_key->state[l], priv_i,
             priv_seed, q);

        /* Move onto next level's data. */
        priv -= LMS_PRIV_LEN(state->params->hash_len);
    }

    if ((ret == 0) && (pub_root != NULL)) {
        XMEMCPY(pub_root, priv_key->state[0].root, state->params->hash_len);
    }

    return ret;
}

/* Calculate the corresponding authentication path index at that height.
 *
 * @param [in] i  Leaf node index.
 * @param [in] h  Height to calculate for.
 * @return  Index on authentication path.
 */
#define LMS_AUTH_PATH_IDX(i, h)                                 \
    (((i) ^ ((word32)1U << (h))) | (((word32)1U << (h)) - 1))

/* Update the authentication path.
 *
 * @param [in, out] state     LMS state.
 * @param [in, out] priv_key  Private key information.
 * @param [in]      levels    Number of level to start at.
 * @return  0 on success.
 */
static int wc_hss_update_auth_path(LmsState* state, HssPrivKey* priv_key,
    byte* priv_raw, int levels)
{
    const LmsParams* params = state->params;
    int ret = 0;
    byte* priv = priv_key->priv +
        LMS_PRIV_LEN(params->hash_len) * (word32)(levels - 1);
    int i;
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
    w64wrapper q64;
#endif

    (void)priv_raw;
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
    ato64(priv_raw, &q64);
#endif

    for (i = levels - 1; (ret == 0) && (i >= 0); i--) {
        word32 q;
        const byte* priv_q = priv;
        const byte* priv_seed = priv_q + LMS_Q_LEN;
        const byte* priv_i = priv_seed + params->hash_len;
        LmsPrivState* privState = &priv_key->state[i];

        /* Get q for tree at level. */
        ato32(priv_q, &q);
    #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
        if ((levels > 1) && (i == levels - 1) && (q == 0)) {
            /* New sub-tree. */
            ret = wc_hss_next_subtree_inc(state, priv_key, q64);
        }
        if ((ret == 0) && (q != 0))
    #else
        if (q == 0) {
            /* New sub-tree. */
            ret = wc_lms_treehash_init(state, privState, priv_i, priv_seed, 0);
        }
        else
    #endif
        {
            word32 maxq = q - 1;
            int h;
            int maxh = params->height;

            /* Check each index at each height needed for the auth path. */
            for (h = 0; (h < maxh) && (h <= maxh - params->rootLevels); h++) {
                /* Calculate the index for current q and q-1. */
                word32 qa = LMS_AUTH_PATH_IDX(q, h);
                word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h);
                /* If different then needs to be computed so keep highest. */
                if ((qa != qm1a) && (qa > maxq)) {
                    maxq = qa;
                }
            }
            for (; h < maxh; h++) {
                /* Calculate the index for current q and q-1. */
                word32 qa = LMS_AUTH_PATH_IDX(q, h);
                word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h);
                /* If different then copy in cached hash. */
                if ((qa != qm1a) && (qa > maxq)) {
                    word32 off = ((word32)1U << (params->height - h)) +
                                 (qa >> h) - 1U;
                    XMEMCPY(privState->auth_path + h * params->hash_len,
                        privState->root + off * params->hash_len,
                        params->hash_len);
                }
            }
            /* Update the treehash and calculate the extra indices for
             * authentication path. */
            ret = wc_lms_treehash_update(state, privState, priv_i, priv_seed,
                q - 1, maxq, q, 1);
        #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
            if ((ret == 0) && (i > 0)) {
                w64wrapper tmp64 = w64ShiftRight(q64,
                    (levels - i) * params->height);
                w64Increment(&tmp64);
                tmp64 = w64ShiftLeft(tmp64, 64 - (i * params->height));
                if (!w64IsZero(tmp64)) {
                    priv_seed = priv_key->next_priv +
                        (word32)i * LMS_PRIV_LEN(params->hash_len) + LMS_Q_LEN;
                    priv_i = priv_seed + params->hash_len;
                    privState = &priv_key->next_state[i - 1];

                    ret = wc_lms_treehash_update(state, privState, priv_i,
                        priv_seed, q, q, 0, 0);
                }
            }
        #endif
            break;
        }

        /* Move onto next level's data. */
        priv -= LMS_PRIV_LEN(params->hash_len);
    }

    return ret;
}

#if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
/* Pre-sign for current q so that it isn't needed in signing.
 *
 * @param [in, out] state     LMS state.
 * @param [in, out] priv_key  Private key.
 */
static int wc_hss_presign(LmsState* state, HssPrivKey* priv_key)
{
    int ret = 0;
    const LmsParams* params = state->params;
    byte* buffer = state->buffer;
    byte pub[LMS_PUBKEY_LEN(LMS_MAX_NODE_LEN)];
    byte* root = pub + LMS_PUBKEY_LEN(params->hash_len) - params->hash_len;
    byte* priv = priv_key->priv;
    int i;

    for (i = params->levels - 2; i >= 0; i--) {
        const byte* p = priv + (word32)i * LMS_PRIV_LEN(params->hash_len);
        const byte* priv_q = p;
        const byte* priv_seed = priv_q + LMS_Q_LEN;
        const byte* priv_i = priv_seed + params->hash_len;

        /* ... || T(1) */
        XMEMCPY(root, priv_key->state[i + 1].root, params->hash_len);
        /* u32str(type) || u32str(otstype) || I || T(1) */
        p = priv + ((word32)i + 1U) * LMS_PRIV_LEN(params->hash_len);
        wc_lmots_public_key_encode(params, p, pub);

        /* Setup for hashing: I || Q || ... */
        XMEMCPY(buffer, priv_i, LMS_I_LEN);
        XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN);

        /* LM-OTS Sign this level. */
        ret = wc_lmots_sign(state, priv_seed, pub,
                LMS_PUBKEY_LEN(params->hash_len),
                priv_key->y + (word32)i *
                    LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len));
    }

    return ret;
}
#endif /* !WOLFSSL_LMS_NO_SIG_CACHE && LMS_MAX_LEVELS > 1 */
#endif /* !WOLFSSL_WC_LMS_SMALL */

/* Load the private key data into HSS private key structure.
 *
 * @param [in]      params     LMS parameters.
 * @param [in, out] key        HSS private key.
 * @param [in]      priv_data  Private key data.
 */
static void wc_hss_priv_data_load(const LmsParams* params, HssPrivKey* key,
    byte* priv_data)
{
#ifndef WOLFSSL_WC_LMS_SMALL
    int l;
#endif

    /* Expanded private keys. */
    key->priv = priv_data;
    priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);

#ifndef WOLFSSL_WC_LMS_SMALL
    for (l = 0; l < params->levels; l++) {
        /* Caches for subtree. */
        wc_lms_priv_state_load(params, &key->state[l], priv_data);
        priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
            params->cacheBits, params->hash_len);
    }

#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
    /* Next subtree's expanded private keys. */
    key->next_priv = priv_data;
    priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
    for (l = 0; l < params->levels - 1; l++) {
        /* Next subtree's caches. */
        wc_lms_priv_state_load(params, &key->next_state[l], priv_data);
        priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
            params->cacheBits, params->hash_len);
    }
#endif /* WOLFSSL_LMS_NO_SIGN_SMOOTHING */

#ifndef WOLFSSL_LMS_NO_SIG_CACHE
    /* Signature cache. */
    key->y = priv_data;
#endif /* WOLFSSL_LMS_NO_SIG_CACHE */
#endif /* WOLFSSL_WC_LMS_SMALL */
}

#ifndef WOLFSSL_WC_LMS_SMALL
/* Store the private key data from HSS private key structure.
 *
 * @param [in]      params     LMS parameters.
 * @param [in]      key        HSS private key.
 * @param [in, out] priv_data  Private key data.
 */
static void wc_hss_priv_data_store(const LmsParams* params, HssPrivKey* key,
    byte* priv_data)
{
    int l;

    /* Expanded private keys. */
    priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);

    for (l = 0; l < params->levels; l++) {
        /* Caches for subtrees. */
        wc_lms_priv_state_store(params, &key->state[l], priv_data);
        priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
            params->cacheBits, params->hash_len);
    }
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
    /* Next subtree's expanded private keys. */
    priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
    for (l = 0; l < params->levels - 1; l++) {
        /* Next subtree's caches. */
        wc_lms_priv_state_store(params, &key->next_state[l], priv_data);
        priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
            params->cacheBits, params->hash_len);
    }
#endif /* WOLFSSL_LMS_NO_SIGN_SMOOTHING */

#ifndef WOLFSSL_LMS_NO_SIG_CACHE
    /* Signature cache. */
#endif /* WOLFSSL_LMS_NO_SIG_CACHE */
}
#endif /* WOLFSSL_WC_LMS_SMALL */

/* Expand private key for each level and calculating auth path..
 *
 * @param [in, out] state      LMS state.
 * @param [in]      priv_raw   Raw private key bytes.
 * @param [out]     priv_key   Private key data.
 * @param [out]     priv_data  Private key data.
 * @param [out]     pub_root   Public key root node.
 * @return  0 on success.
 */
int wc_hss_reload_key(LmsState* state, const byte* priv_raw,
    HssPrivKey* priv_key, byte* priv_data, byte* pub_root)
{
    int ret = 0;

    (void)pub_root;

    /* Defend against undefined shifts; LmsParams* params = state->params */
    if (state->params->height >= 32U) {
        return BAD_FUNC_ARG;
    }
#ifndef WOLFSSL_WC_LMS_SMALL
    if (state->params->cacheBits >= 32U) {
        return BAD_FUNC_ARG;
    }
#endif

    wc_hss_priv_data_load(state->params, priv_key, priv_data);
#ifndef WOLFSSL_WC_LMS_SMALL
    priv_key->inited = 0;
#endif

#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE
    if (pub_root != NULL)
#endif
    {
        /* Expand the raw private key into the private key data. */
        ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 0);
    #ifndef WOLFSSL_WC_LMS_SMALL
        if ((ret == 0) && (!priv_key->inited)) {
            /* Initialize the authentication paths and caches for all trees. */
            ret = wc_hss_init_auth_path(state, priv_key, pub_root);
        #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
            if (ret == 0) {
                ret = wc_hss_next_subtrees_init(state, priv_key);
            }
        #endif
        #if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
            if (ret == 0) {
                /* Calculate signatures for trees not at bottom. */
                ret = wc_hss_presign(state, priv_key);
            }
        #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
        }
    #endif /* WOLFSSL_WC_LMS_SMALL */
    }
#ifndef WOLFSSL_WC_LMS_SMALL
    /* Set initialized flag. */
    priv_key->inited = (ret == 0);
#endif

    return ret;
}

/* Make an HSS key pair.
 *
 * @param [in, out] state      LMS state.
 * @param [in]      rng        Random number generator.
 * @param [out]     priv_raw   Private key to write.
 * @param [out]     priv_key   Private key.
 * @param [out]     priv_data  Private key data.
 * @param [out]     pub        Public key.
 * @return  0 on success.
 */
int wc_hss_make_key(LmsState* state, WC_RNG* rng, byte* priv_raw,
    HssPrivKey* priv_key, byte* priv_data, byte* pub)
{
    const LmsParams* params = state->params;
    int ret = 0;
    word32 i;
    byte* p = priv_raw;
    byte* pub_root = pub + LMS_L_LEN + LMS_TYPE_LEN + LMS_TYPE_LEN + LMS_I_LEN;

    /* The 64-bit q starts at 0 - set into raw private key. */
    wc_lms_idx_zero(p, HSS_Q_LEN);
    p += HSS_Q_LEN;

    /* Set the LMS and LM-OTS types for each level. */
    for (i = 0; i < params->levels; i++) {
        p[i] = (byte)(((params->lmsType & LMS_H_W_MASK) << 4) +
                      (params->lmOtsType & LMS_H_W_MASK));
    }
    /* Set rest of levels to an invalid value. */
    for (; i < HSS_MAX_LEVELS; i++) {
        p[i] = 0xff;
    }
    p += HSS_PRIV_KEY_PARAM_SET_LEN;

    /* Make the private key. */
    ret = wc_lmots_make_private_key(rng, params->hash_len, p);

    if (ret == 0) {
        /* Set the levels into the public key data. */
        c32toa(params->levels, pub);
        pub += LMS_L_LEN;

        ret = wc_hss_reload_key(state, priv_raw, priv_key, priv_data, pub_root);
    }
    #ifdef WOLFSSL_WC_LMS_SMALL
    if (ret == 0) {
        byte* priv_seed = priv_key->priv + LMS_Q_LEN;
        byte* priv_i = priv_seed + params->hash_len;

        /* Compute the root of the highest tree to get the root for public key.
         */
        ret = wc_lms_make_public_key(state, priv_i, priv_seed, pub_root);
    }
    #endif /* !WOLFSSL_WC_LMS_SMALL */
    if (ret == 0) {
        /* Encode the public key with remaining fields from the private key. */
        wc_lmots_public_key_encode(params, priv_key->priv, pub);
    }

#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE
    wc_hss_priv_data_store(state->params, priv_key, priv_data);
#endif

    return ret;
}

#ifdef WOLFSSL_WC_LMS_SMALL
/* Sign message using HSS.
 *
 * Algorithm 8: Generating an HSS signature
 *   1. If the message-signing key prv[L-1] is exhausted, regenerate
 *      that key pair, together with any parent key pairs that might
 *      be necessary.
 *      If the root key pair is exhausted, then the HSS key pair is
 *      exhausted and MUST NOT generate any more signatures.
 *      d = L
 *      while (prv[d-1].q == 2^(prv[d-1].h)) {
 *        d = d - 1
 *        if (d == 0)
 *          return FAILURE
 *      }
 *      while (d < L) {
 *        create lms key pair pub[d], prv[d]
 *        sig[d-1] = lms_signature( pub[d], prv[d-1] )
 *        d = d + 1
 *      }
 *   2. Sign the message.
 *      sig[L-1] = lms_signature( msg, prv[L-1] )
 *   3. Create the list of signed public keys.
 *      i = 0;
 *      while (i < L-1) {
 *        signed_pub_key[i] = sig[i] || pub[i+1]
 *        i = i + 1
 *      }
 *   4. Return u32str(L-1) || signed_pub_key[0] || ...
 *                               || signed_pub_key[L-2] || sig[L-1]
 *
 * @param [in, out] state     LMS state.
 * @param [in, out] priv_raw  Raw private key bytes.
 * @param [in, out] priv_key  Private key data.
 * @param [in]      msg       Message to sign.
 * @param [in]      msgSz     Length of message in bytes.
 * @param [out]     sig       Signature of message.
 * @return  0 on success.
 */
int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key,
    byte* priv_data, const byte* msg, word32 msgSz, byte* sig)
{
    const LmsParams* params = state->params;
    int ret = 0;
    byte* priv = priv_key->priv;

    (void)priv_data;

    /* Step 1. Part 2: Check for total key exhaustion. */
    if (!wc_hss_sigsleft(params, priv_raw)) {
        ret = KEY_EXHAUSTED_E;
    }

    if (ret == 0) {
        /* Expand the raw private key into the private key data. */
        ret = wc_hss_expand_private_key(state, priv, priv_raw, 0);
    }
    if (ret == 0) {
        int i;
        w64wrapper q;
        w64wrapper qm1;

        /* Get 64-bit q from raw private key. */
        ato64(priv_raw, &q);
        /* Calculate q-1 for comparison. */
        qm1 = q;
        w64Decrement(&qm1);

        /* Set number of signed public keys. */
        c32toa((word32)(params->levels - 1), sig);
        sig += params->sig_len;

        /* Build from bottom up. */
        for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) {
            byte* p = priv + (word32)i * LMS_PRIV_LEN(params->hash_len);
            byte* root = NULL;

            /* Move to start of next signature at this level. */
            sig -= LMS_SIG_LEN(params->height, params->p, params->hash_len);
            if (i != 0) {
                /* Put root node into signature at this index. */
                root = sig - params->hash_len;
            }

            /* Sign using LMS for this level. */
            ret = wc_lms_sign(state, p, msg, msgSz, sig);
            if (ret == 0) {
                byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN +
                            LMOTS_Y_LEN(params->p, params->hash_len) +
                            LMS_TYPE_LEN;
                byte* priv_q = p;
                byte* priv_seed = priv_q + LMS_Q_LEN;
                byte* priv_i = priv_seed + params->hash_len;
                word32 q32;

                /* Get Q from private key as a number. */
                ato32(priv_q, &q32);
                /* Calculate authentication path. */
                ret = wc_lms_auth_path(state, priv_i, priv_seed, q32, s, root);
            }
            if ((ret == 0) && (i != 0)) {
                /* Create public data for this level if there is another. */
                sig -= LMS_PUBKEY_LEN(params->hash_len);
                msg = sig;
                msgSz = LMS_PUBKEY_LEN(params->hash_len);
                wc_lmots_public_key_encode(params, p, sig);
            }
        }
    }
    if (ret == 0) {
        /* Increment index of leaf node to sign with in raw data. */
        wc_lms_idx_inc(priv_raw, HSS_Q_LEN);
    }

    return ret;
}
#else
/* Build signature for HSS signed message.
 *
 * Algorithm 8: Generating an HSS signature
 *   1. ...
 *      while (prv[d-1].q == 2^(prv[d-1].h)) {
 *        d = d - 1
 *        if (d == 0)
 *          return FAILURE
 *      }
 *      while (d < L) {
 *        create lms key pair pub[d], prv[d]
 *        sig[d-1] = lms_signature( pub[d], prv[d-1] )
 *        d = d + 1
 *      }
 *   2. Sign the message.
 *      sig[L-1] = lms_signature( msg, prv[L-1] )
 *   3. Create the list of signed public keys.
 *      i = 0;
 *      while (i < L-1) {
 *        signed_pub_key[i] = sig[i] || pub[i+1]
 *        i = i + 1
 *      }
 *   4. Return u32str(L-1) || signed_pub_key[0] || ...
 *                               || signed_pub_key[L-2] || sig[L-1]
 *
 * @param [in, out] state      LMS state.
 * @param [in, out] priv_raw   Raw private key bytes.
 * @param [in, out] priv_key   Private key data.
 * @param [in]      msg        Message to sign.
 * @param [in]      msgSz      Length of message in bytes.
 * @param [out]     sig        Signature of message.
 * @return  0 on success.
 */
static int wc_hss_sign_build_sig(LmsState* state, byte* priv_raw,
    HssPrivKey* priv_key, const byte* msg, word32 msgSz, byte* sig)
{
    const LmsParams* params = state->params;
    int ret = 0;
    int i;
    w64wrapper q;
    w64wrapper qm1;
    byte* priv = priv_key->priv;

    /* Get 64-bit q from raw private key. */
    ato64(priv_raw, &q);
    /* Calculate q-1 for comparison. */
    qm1 = q;
    w64Decrement(&qm1);

    /* Set number of signed public keys. */
    c32toa((word32)(params->levels - 1), sig);
    sig += params->sig_len;

    /* Build from bottom up. */
    for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) {
        byte* p = priv + (word32)i * LMS_PRIV_LEN(params->hash_len);
    #if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
        byte* root = NULL;
    #endif
    #ifndef WOLFSSL_LMS_NO_SIG_CACHE
        int store_p = 0;
        word32 q_32 = LMS_Q_AT_LEVEL(q, params->levels, i,
            params->height);
        word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, i,
            params->height);
    #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */

        /* Move to start of next signature at this level. */
        sig -= LMS_SIG_LEN(params->height, params->p, params->hash_len);
    #if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
        if (i != 0) {
            /* Put root node into signature at this index. */
            root = sig - params->hash_len;
        }
    #endif

    #ifndef WOLFSSL_LMS_NO_SIG_CACHE
        /* Check if we have a cached version of C and the p hashes that we
         * can reuse. */
        if ((i < params->levels - 1) && (q_32 == qm1_32)) {
            wc_lms_sig_copy(params, priv_key->y +
                (word32)i * LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len),
                p, sig);
        }
        else
    #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
        {
            /* Sign using LMS for this level. */
            ret = wc_lms_sign(state, p, msg, msgSz, sig);
        #ifndef WOLFSSL_LMS_NO_SIG_CACHE
            store_p = (i < params->levels - 1);
        #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
        }
        if (ret == 0) {
            byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN;

        #ifndef WOLFSSL_LMS_NO_SIG_CACHE
            /* Check if we computed new C and p hashes. */
            if (store_p) {
                /* Cache the C and p hashes. */
                XMEMCPY(priv_key->y +
                    (word32)i *
                        LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len), s,
                    LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len));
            }
        #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
            s += LMOTS_Y_LEN(params->p, params->hash_len) + LMS_TYPE_LEN;

            /* Copy the authentication path out of the private key. */
            XMEMCPY(s, priv_key->state[i].auth_path,
                (word32)params->height * params->hash_len);
        #if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
            /* Copy the root node into signature unless at top. */
            if (i != 0) {
                XMEMCPY(root, priv_key->state[i].root, params->hash_len);
            }
        #endif
        }
        if ((ret == 0) && (i != 0)) {
            /* Create public data for this level if there is another. */
            sig -= LMS_PUBKEY_LEN(params->hash_len);
            msg = sig;
            msgSz = LMS_PUBKEY_LEN(params->hash_len);
            wc_lmots_public_key_encode(params, p, sig);
        }
    }

    return ret;
}

/* Sign message using HSS.
 *
 * Algorithm 8: Generating an HSS signature
 *   1. If the message-signing key prv[L-1] is exhausted, regenerate
 *      that key pair, together with any parent key pairs that might
 *      be necessary.
 *      If the root key pair is exhausted, then the HSS key pair is
 *      exhausted and MUST NOT generate any more signatures.
 *      d = L
 *      while (prv[d-1].q == 2^(prv[d-1].h)) {
 *        d = d - 1
 *        if (d == 0)
 *          return FAILURE
 *      }
 *      while (d < L) {
 *        create lms key pair pub[d], prv[d]
 *        sig[d-1] = lms_signature( pub[d], prv[d-1] )
 *        d = d + 1
 *      }
 *   2. Sign the message.
 *      sig[L-1] = lms_signature( msg, prv[L-1] )
 *   3. Create the list of signed public keys.
 *      i = 0;
 *      while (i < L-1) {
 *        signed_pub_key[i] = sig[i] || pub[i+1]
 *        i = i + 1
 *      }
 *   4. Return u32str(L-1) || signed_pub_key[0] || ...
 *                               || signed_pub_key[L-2] || sig[L-1]
 *
 * @param [in, out] state      LMS state.
 * @param [in, out] priv_raw   Raw private key bytes.
 * @param [in, out] priv_key   Private key.
 * @param [in, out] priv_data  Private key data.
 * @param [in]      msg        Message to sign.
 * @param [in]      msgSz      Length of message in bytes.
 * @param [out]     sig        Signature of message.
 * @return  0 on success.
 */
int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key,
    byte* priv_data, const byte* msg, word32 msgSz, byte* sig)
{
    const LmsParams* params = state->params;
    int ret = 0;

    /* Validate fixed parameters for static code analyzers. */
    if ((params->rootLevels == 0) || (params->rootLevels > params->height)) {
        ret = BAD_FUNC_ARG;
    }

    /* Step 1. Part 2: Check for total key exhaustion. */
    if ((ret == 0) && (!wc_hss_sigsleft(params, priv_raw))) {
        ret = KEY_EXHAUSTED_E;
    }

    if ((ret == 0) && (!priv_key->inited)) {
        /* Initialize the authentication paths and caches for all trees. */
        ret = wc_hss_init_auth_path(state, priv_key, NULL);
    #if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
        if (ret == 0) {
            ret = wc_hss_presign(state, priv_key);
        }
    #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
        /* Set initialized flag. */
        priv_key->inited = (ret == 0);
    }
    if (ret == 0) {
        ret = wc_hss_sign_build_sig(state, priv_raw, priv_key, msg, msgSz, sig);
    }
    if (ret == 0) {
        /* Increment index of leaf node to sign with in raw data. */
        wc_lms_idx_inc(priv_raw, HSS_Q_LEN);
    }
    /* Check we will produce another signature. */
    if ((ret == 0) && wc_hss_sigsleft(params, priv_raw)) {
        /* Update the expanded private key data. */
        ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 1);
        if (ret == 0) {
            /* Update authentication path and caches for all trees. */
            ret = wc_hss_update_auth_path(state, priv_key, priv_raw,
                params->levels);
        }
    }
    if (ret == 0) {
        /* Store the updated private key data. */
        wc_hss_priv_data_store(state->params, priv_key, priv_data);
    }

    return ret;
}
#endif

/* Check whether key is exhausted.
 *
 * First 8 bytes of raw key is the index.
 * Check index is less than count of leaf nodes.
 *
 * @param [in] params    LMS parameters.
 * @param [in] priv_raw  HSS raw private key.
 * @return  1 when signature possible.
 * @return  0 when private key exhausted.
 */
int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw)
{
    int ret;
    w64wrapper q;
    w64wrapper cnt;

    if (params->levels * params->height >= 64) {
        ret = 1;
    }
    else {
        /* Get current q - next leaf index to sign with. */
        ato64(priv_raw, &q);
        /* 1 << total_height = total leaf nodes. */
        cnt = w64ShiftLeft(w64From32(0, 1), params->levels * params->height);
        /* Check q is less than total leaf node count. */
        ret = w64LT(q, cnt);
    }

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

/* Verify message using HSS.
 *
 * Section 6.3. Signature Verification
 *  1. Nspk = strTou32(first four bytes of S)
 *  2. if Nspk+1 is not equal to the number of levels L in pub:
 *  3.   return INVALID
 *  4. key = pub
 *  5. for (i = 0; i < Nspk; i = i + 1) {
 *  6.   sig = siglist[i]
 *  7.   msg = publist[i]
 *  8.   if (lms_verify(msg, key, sig) != VALID):
 *  9.     return INVALID
 * 10.   key = msg
 * 11. }
 * 12. return lms_verify(message, key, siglist[Nspk])
 *
 * @param [in, out] state  LMS state.
 * @param [in]      pub    HSS public key.
 * @param [in]      msg    Message to verify.
 * @param [in]      msgSz  Length of message in bytes.
 * @param [in]      sig    Signature of message.
 * @return  0 on success.
 * @return  SIG_VERIFY_E on failure.
 */
int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg,
    word32 msgSz, const byte* sig, word32 sigSz)
{
    const LmsParams* params = state->params;
    int ret = 0;
    word32 nspk;
    const byte* key = pub + LMS_L_LEN;
    word32 levels;
    word32 sigRem;
    /* Bytes consumed by one LMS signature in the HSS chain (matches the
     * lms_sig_required calculation in wc_lms_verify). */
    const word32 lms_sig_bytes =
        LMS_SIG_LEN(params->height, params->p, params->hash_len);
    const word32 next_pubkey_bytes = LMS_PUBKEY_LEN(params->hash_len);

    /* Need at least the leading L (number of signed public keys). */
    if (sigSz < LMS_L_LEN) {
        return BUFFER_E;
    }

    /* Get number of levels from public key. */
    ato32(pub, &levels);
    /* Line 1: Get number of signed public keys from signature. */
    ato32(sig, &nspk);
    /* Line 6 (First iteration): Move to start of next signature. */
    sig += LMS_L_LEN;
    sigRem = sigSz - LMS_L_LEN;

    /* Validate that the count of levels matches the parameters. */
    if (levels != state->params->levels) {
        ret = SIG_VERIFY_E;
    }
    /* Line 2: Verify that pub and signature match in levels. */
    if ((ret == 0) && (nspk + 1 != levels)) {
        /* Line 3: Return invalid signature. */
        ret = SIG_VERIFY_E;
    }
    if (ret == 0) {
        word32 i;

        /* Line 5: For all but last LMS signature. */
        for (i = 0; (ret == 0) && (i < nspk); i++) {
            const byte* pubList;

            /* Defensive bounds: each non-final iteration consumes one
             * LMS signature plus the next-level public key. */
            if (sigRem < lms_sig_bytes + next_pubkey_bytes) {
                ret = BUFFER_E;
                break;
            }

            /* Line 7: Get start of public key in signature. */
            pubList = sig + lms_sig_bytes;
            /* Line 8: Verify the LMS signature with public key as message. */
            ret = wc_lms_verify(state, key, pubList,
                next_pubkey_bytes, sig, lms_sig_bytes);
            /* Line 10: Next key is from signature. */
            key = pubList;
            /* Line 6: Move to start of next signature. */
            sig = pubList + next_pubkey_bytes;
            sigRem -= (lms_sig_bytes + next_pubkey_bytes);
        }
    }
    if (ret == 0) {
        /* Line 12: Verify bottom tree with real message. The bottom-tree
         * LMS signature consumes exactly the remaining sigSz; pass that
         * as the bound and let wc_lms_verify enforce its own minimum. */
        ret = wc_lms_verify(state, key, msg, msgSz, sig, sigRem);
    }

    return ret;
}

#endif /* WOLFSSL_HAVE_LMS */

