Преглед на файлове

Merge pull request #7500 from SparkiDev/lms_xmss_move_wolfcrypt

LMS, XMSS: move code into wolfCrypt
Daniel Pouzzner преди 3 седмици
родител
ревизия
4e6a34504d
променени са 7 файла, в които са добавени 10884 реда и са изтрити 52 реда
  1. 26 42
      configure.ac
  2. 1121 2
      wolfcrypt/src/wc_lms.c
  3. 3069 3
      wolfcrypt/src/wc_lms_impl.c
  4. 1643 1
      wolfcrypt/src/wc_xmss.c
  5. 4314 2
      wolfcrypt/src/wc_xmss_impl.c
  6. 448 1
      wolfssl/wolfcrypt/wc_lms.h
  7. 263 1
      wolfssl/wolfcrypt/wc_xmss.h

+ 26 - 42
configure.ac

@@ -1229,7 +1229,6 @@ AC_ARG_ENABLE([xmss],
     [ ENABLED_XMSS=no ]
     )
 
-ENABLED_WC_XMSS=no
 for v in `echo $ENABLED_XMSS | tr "," " "`
 do
   case $v in
@@ -1238,15 +1237,9 @@ do
   no)
     ;;
   verify-only)
-    XMSS_VERIFY_ONLY=yes
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_XMSS_VERIFY_ONLY -DXMSS_VERIFY_ONLY"
     ;;
-  wolfssl)
-    ENABLED_WC_XMSS=yes
-    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS"
-    ;;
   small)
-    ENABLED_WC_XMSS=yes
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS_SMALL"
     ;;
   *)
@@ -1255,20 +1248,6 @@ do
   esac
 done
 
-if test "$ENABLED_XMSS" != "no"
-then
-    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_XMSS"
-
-    if test "$ENABLED_WC_XMSS" = "no";
-    then
-        # Default is to use hash-sigs XMSS lib. Make sure it's enabled.
-        if test "$ENABLED_LIBXMSS" = "no"; then
-            AC_MSG_ERROR([The default implementation for XMSS is the xmss-reference lib.
-            Please use --with-libxmss.])
-        fi
-    fi
-fi
-
 # libxmss
 # Get the path to xmss-reference.
 ENABLED_LIBXMSS="no"
@@ -1321,6 +1300,19 @@ AC_ARG_WITH([libxmss],
     [XMSS_ROOT=""]
 )
 
+if test "$ENABLED_XMSS" != "no"
+then
+    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_XMSS"
+
+    # Use hash-sigs XMSS lib if enabled.
+    if test "$ENABLED_LIBXMSS" = "yes"; then
+        ENABLED_WC_XMSS=no
+    else
+        ENABLED_WC_XMSS=yes
+        AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS"
+    fi
+fi
+
 # LMS
 AC_ARG_ENABLE([lms],
     [AS_HELP_STRING([--enable-lms],[Enable stateful LMS/HSS signatures (default: disabled)])],
@@ -1328,7 +1320,6 @@ AC_ARG_ENABLE([lms],
     [ ENABLED_LMS=no ]
     )
 
-ENABLED_WC_LMS=no
 for v in `echo $ENABLED_LMS | tr "," " "`
 do
   case $v in
@@ -1337,37 +1328,17 @@ do
   no)
     ;;
   verify-only)
-    LMS_VERIFY_ONLY=yes
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_LMS_VERIFY_ONLY"
     ;;
   small)
-    ENABLED_WC_LMS=yes
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS_SMALL"
     ;;
-  wolfssl)
-    ENABLED_WC_LMS=yes
-    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS"
-    ;;
   *)
     AC_MSG_ERROR([Invalid choice for LMS []: $ENABLED_LMS.])
     break;;
   esac
 done
 
-if test "$ENABLED_LMS" != "no"
-then
-    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_LMS"
-
-    if test "$ENABLED_WC_LMS" = "no";
-    then
-        # Default is to use hash-sigs LMS lib. Make sure it's enabled.
-        if test "$ENABLED_LIBLMS" = "no"; then
-            AC_MSG_ERROR([The default implementation for LMS is the hash-sigs LMS/HSS lib.
-            Please use --with-liblms.])
-        fi
-    fi
-fi
-
 # liblms
 # Get the path to the hash-sigs LMS HSS lib.
 ENABLED_LIBLMS="no"
@@ -1436,6 +1407,19 @@ AC_ARG_WITH([liblms],
     ]
 )
 
+if test "$ENABLED_LMS" != "no"
+then
+    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_LMS"
+
+    # Use hash-sigs LMS lib if enabled.
+    if test "$ENABLED_LIBLMS" = "yes"; then
+        ENABLED_WC_LMS=no
+    else
+        ENABLED_WC_LMS=yes
+        AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS"
+    fi
+fi
+
 # SINGLE THREADED
 AC_ARG_ENABLE([singlethreaded],
     [AS_HELP_STRING([--enable-singlethreaded],[Enable wolfSSL single threaded (default: disabled)])],

+ 1121 - 2
wolfcrypt/src/wc_lms.c

@@ -19,8 +19,1127 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
 #include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/wolfcrypt/logging.h>
+
+#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS)
+#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
+
+
+/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8.
+ *
+ * @param [in] w  Winternitz width.
+ */
+#define LMS_U(w)                    \
+    (8 * WC_SHA256_DIGEST_SIZE / (w))
+/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8.
+ *
+ * @param [in] w   Winternitz width.
+ * @param [in] wb  Winternitz width length in bits.
+ */
+#define LMS_V(w, wb)                \
+    (2 + (8 - (wb)) / (w))
+/* Calculate ls. Appendix B. Works for w of 1, 2, 4, or 8.
+ *
+ * @param [in] w   Winternitz width.
+ * @param [in] wb  Winternitz width length in bits.
+ */
+#define LMS_LS(w, wb)               \
+    (16 - LMS_V(w, wb) * (w))
+/* Calculate p. Appendix B. Works for w of 1, 2, 4, or 8.
+ *
+ * @param [in] w   Winternitz width.
+ * @param [in] wb  Winternitz width length in bits.
+ */
+#define LMS_P(w, wb)                \
+    (LMS_U(w) + LMS_V(w, wb))
+/* Calculate signature length.
+ *
+ * @param [in] l  Number of levels.
+ * @param [in] h  Height of the trees.
+ * @param [in] p  Number of n-byte string elements in signature for a tree.
+ */
+#define LMS_PARAMS_SIG_LEN(l, h, p)                                     \
+    (4 + (l) * (4 + 4 + 4 + WC_SHA256_DIGEST_SIZE * (1 + (p) + (h))) +  \
+     ((l) - 1) * LMS_PUBKEY_LEN)
+
+#ifndef WOLFSSL_WC_LMS_SMALL
+    /* Root levels and leaf cache bits. */
+    #define LMS_PARAMS_CACHE(h)                             \
+        (((h) < LMS_ROOT_LEVELS) ? (h) : LMS_ROOT_LEVELS),  \
+        (((h) < LMS_CACHE_BITS ) ? (h) : LMS_CACHE_BITS )
+#else
+    /* Root levels and leaf cache bits aren't in structure. */
+    #define LMS_PARAMS_CACHE(h) /* null expansion */
+#endif
+
+/* Define parameters entry for LMS.
+ *
+ * @param [in] l   Number of levels.
+ * @param [in] h   Height of the trees.
+ * @param [in] w   Winternitz width.
+ * @param [in] wb  Winternitz width length in bits.
+ * @param [in] t   LMS type.
+ * @param [in] t2  LM-OTS type.
+ */
+#define LMS_PARAMS(l, h, w, wb, t, t2)                              \
+    { l, h, w, LMS_LS(w, wb), LMS_P(w, wb), t, t2,                  \
+      LMS_PARAMS_SIG_LEN(l, h, LMS_P(w, wb)), LMS_PARAMS_CACHE(h) }
+
+
+/* Initialize the working state for LMS operations.
+ *
+ * @param [in, out] state   LMS state.
+ * @param [in]      params  LMS parameters.
+ */
+static int wc_lmskey_state_init(LmsState* state, const LmsParams* params)
+{
+    int ret;
 
-#ifdef WOLFSSL_HAVE_LMS
-    #error "Contact wolfSSL to get the implementation of this file"
+    /* Zero out every field. */
+    XMEMSET(state, 0, sizeof(LmsState));
+
+    /* Keep a reference to the parameters for use in operations. */
+    state->params = params;
+
+    /* Initialize the two hash algorithms. */
+    ret = wc_InitSha256(&state->hash);
+    if (ret == 0) {
+        ret = wc_InitSha256(&state->hash_k);
+        if (ret != 0) {
+            wc_Sha256Free(&state->hash);
+        }
+    }
+
+    return ret;
+}
+
+/* Free the working state for LMS operations.
+ *
+ * @param [in] state  LMS state.
+ */
+static void wc_lmskey_state_free(LmsState* state)
+{
+    wc_Sha256Free(&state->hash_k);
+    wc_Sha256Free(&state->hash);
+}
+
+/* Supported LMS parameters. */
+static const wc_LmsParamsMap wc_lms_map[] = {
+#if LMS_MAX_HEIGHT >= 15
+    { WC_LMS_PARM_NONE     , "LMS_NONE"         ,
+      LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L1_H15_W2, "LMS/HSS L1_H15_W2",
+      LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L1_H15_W4, "LMS/HSS L1_H15_W4",
+      LMS_PARAMS(1, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) },
+#endif
+#if LMS_MAX_LEVELS >= 2
+#if LMS_MAX_HEIGHT >= 10
+    { WC_LMS_PARM_L2_H10_W2, "LMS/HSS L2_H10_W2",
+      LMS_PARAMS(2, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L2_H10_W4, "LMS/HSS L2_H10_W4",
+      LMS_PARAMS(2, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L2_H10_W8, "LMS/HSS L2_H10_W8",
+      LMS_PARAMS(2, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
+#endif
+#endif
+#if LMS_MAX_LEVELS >= 3
+    { WC_LMS_PARM_L3_H5_W2 , "LMS/HSS L3_H5_W2" ,
+      LMS_PARAMS(3,  5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L3_H5_W4 , "LMS/HSS L3_H5_W4" ,
+      LMS_PARAMS(3,  5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L3_H5_W8 , "LMS/HSS L3_H5_W8" ,
+      LMS_PARAMS(3,  5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
+#if LMS_MAX_HEIGHT >= 10
+    { WC_LMS_PARM_L3_H10_W4, "LMS/HSS L3_H10_W4",
+      LMS_PARAMS(3, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
+#endif
+#endif
+#if LMS_MAX_LEVELS >= 4
+    { WC_LMS_PARM_L4_H5_W8 , "LMS/HSS L4_H5_W8" ,
+      LMS_PARAMS(4,  5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
+#endif
+
+    /* For when user sets L, H, W explicitly. */
+    { WC_LMS_PARM_L1_H5_W1 , "LMS/HSS_L1_H5_W1" ,
+      LMS_PARAMS(1,  5, 1, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W1) },
+    { WC_LMS_PARM_L1_H5_W2 , "LMS/HSS_L1_H5_W2" ,
+      LMS_PARAMS(1,  5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L1_H5_W4 , "LMS/HSS_L1_H5_W4" ,
+      LMS_PARAMS(1,  5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L1_H5_W8 , "LMS/HSS_L1_H5_W8" ,
+      LMS_PARAMS(1,  5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
+#if LMS_MAX_HEIGHT >= 10
+    { WC_LMS_PARM_L1_H10_W2 , "LMS/HSS_L1_H10_W2",
+      LMS_PARAMS(1, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L1_H10_W4 , "LMS/HSS_L1_H10_W4",
+      LMS_PARAMS(1, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L1_H10_W8 , "LMS/HSS_L1_H10_W8",
+      LMS_PARAMS(1, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
+#endif
+#if LMS_MAX_HEIGHT >= 15
+    { WC_LMS_PARM_L1_H15_W8 , "LMS/HSS L1_H15_W8",
+      LMS_PARAMS(1, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) },
+#endif
+#if LMS_MAX_HEIGHT >= 20
+    { WC_LMS_PARM_L1_H20_W2 , "LMS/HSS_L1_H20_W2",
+      LMS_PARAMS(1, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L1_H20_W4 , "LMS/HSS_L1_H20_W4",
+      LMS_PARAMS(1, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L1_H20_W8 , "LMS/HSS_L1_H20_W8",
+      LMS_PARAMS(1, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) },
+#endif
+#if LMS_MAX_LEVELS >= 2
+    { WC_LMS_PARM_L2_H5_W2 , "LMS/HSS_L2_H5_W2" ,
+      LMS_PARAMS(2,  5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L2_H5_W4 , "LMS/HSS_L2_H5_W4" ,
+      LMS_PARAMS(2,  5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L2_H5_W8 , "LMS/HSS_L2_H5_W8" ,
+      LMS_PARAMS(2,  5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) },
+#if LMS_MAX_HEIGHT >= 15
+    { WC_LMS_PARM_L2_H15_W2 , "LMS/HSS_L2_H15_W2",
+      LMS_PARAMS(2, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L2_H15_W4 , "LMS/HSS_L2_H15_W4",
+      LMS_PARAMS(2, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L2_H15_W8 , "LMS/HSS_L2_H15_W8",
+      LMS_PARAMS(2, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) },
+#endif
+#if LMS_MAX_HEIGHT >= 20
+    { WC_LMS_PARM_L2_H20_W2 , "LMS/HSS_L2_H20_W2",
+      LMS_PARAMS(2, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L2_H20_W4 , "LMS/HSS_L2_H20_W4",
+      LMS_PARAMS(2, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L2_H20_W8 , "LMS/HSS_L2_H20_W8",
+      LMS_PARAMS(2, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) },
+#endif
+#endif
+#if LMS_MAX_LEVELS >= 3
+#if LMS_MAX_HEIGHT >= 10
+    { WC_LMS_PARM_L3_H10_W8 , "LMS/HSS L3_H10_W8",
+      LMS_PARAMS(3, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
 #endif
+#endif
+#if LMS_MAX_LEVELS >= 4
+    { WC_LMS_PARM_L4_H5_W2 , "LMS/HSS L4_H5_W2" ,
+      LMS_PARAMS(4,  5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) },
+    { WC_LMS_PARM_L4_H5_W4 , "LMS/HSS L4_H5_W4" ,
+      LMS_PARAMS(4,  5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) },
+#if LMS_MAX_HEIGHT >= 10
+    { WC_LMS_PARM_L4_H10_W4 , "LMS/HSS L4_H10_W4",
+      LMS_PARAMS(4, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) },
+    { WC_LMS_PARM_L4_H10_W8 , "LMS/HSS L4_H10_W8",
+      LMS_PARAMS(4, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) },
+#endif
+#endif
+};
+/* Number of parameter sets supported. */
+#define WC_LMS_MAP_LEN      ((int)(sizeof(wc_lms_map) / sizeof(*wc_lms_map)))
+
+/* Initialize LMS key.
+ *
+ * Call this before setting the params of an LMS key.
+ *
+ * @param [out] key    LMS key to initialize.
+ * @param [in]  heap   Heap hint.
+ * @param [in]  devId  Device identifier.
+ *                     Use INVALID_DEVID when not using a device.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key is NULL.
+ */
+int wc_LmsKey_Init(LmsKey* key, void* heap, int devId)
+{
+    int ret = 0;
+
+    (void)heap;
+    (void)devId;
+
+    /* Validate parameters. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+    if (ret == 0) {
+        /* Zeroize the key data. */
+        ForceZero(key, sizeof(LmsKey));
+
+    #ifndef WOLFSSL_LMS_VERIFY_ONLY
+        /* Initialize other fields. */
+        key->write_private_key = NULL;
+        key->read_private_key = NULL;
+        key->context = NULL;
+        key->heap = heap;
+    #endif
+    #ifdef WOLF_CRYPTO_CB
+        key->devId = devId;
+    #endif
+        /* Start in initialized state. */
+        key->state = WC_LMS_STATE_INITED;
+    }
+
+    return ret;
+}
+
+/* Get the string representation of the LMS parameter set.
+ *
+ * @param [in] lmsParm  LMS parameter set identifier.
+ * @return  String representing LMS parameter set on success.
+ * @return  NULL when parameter set not supported.
+ */
+const char* wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm)
+{
+    const char* str = NULL;
+    int i;
+
+    /* Search through table for matching numeric identifier. */
+    for (i = 0; i < WC_LMS_MAP_LEN; i++) {
+        if (lmsParm == wc_lms_map[i].id) {
+            /* Get string corresponding to numeric identifier. */
+            str = wc_lms_map[i].str;
+            break;
+        }
+    }
+
+    /* Return the string or NULL. */
+    return str;
+}
+
+/* Set the wc_LmsParm of an LMS key.
+ *
+ * Use this if you wish to set a key with a predefined parameter set,
+ * such as WC_LMS_PARM_L2_H10_W8.
+ *
+ * Key must be inited before calling this.
+ *
+ * @param [in, out] key      LMS key to set parameters on.
+ * @param [in]      lmsParm  Identifier of parameters.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key is NULL.
+ * @return  BAD_FUNC_ARG when parameters not supported.
+ */
+int wc_LmsKey_SetLmsParm(LmsKey* key, enum wc_LmsParm lmsParm)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    /* Check state is valid. */
+    if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) {
+        WOLFSSL_MSG("error: LmsKey needs init");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        int i;
+
+        ret = BAD_FUNC_ARG;
+        /* Search through table for matching numeric identifier. */
+        for (i = 0; i < WC_LMS_MAP_LEN; i++) {
+            if (lmsParm == wc_lms_map[i].id) {
+                /* Set the parameters into the key. */
+                key->params = &wc_lms_map[i].params;
+                ret = 0;
+                break;
+            }
+        }
+    }
+
+    if (ret == 0) {
+        /* Move the state to params set.
+         * Key is ready for MakeKey or Reload. */
+        key->state = WC_LMS_STATE_PARMSET;
+    }
+
+    return ret;
+}
+
+/* Set the parameters of an LMS key.
+ *
+ * Use this if you wish to set specific parameters not found in the
+ * wc_LmsParm predefined sets. See comments in lms.h for allowed
+ * parameters.
+ *
+ * Key must be inited before calling this.
+ *
+ * @param [in, out] key         LMS key to set parameters on.
+ * @param [in]      levels      Number of tree levels.
+ * @param [in]      height      Height of each tree.
+ * @param [in]      winternitz  Width or Winternitz coefficient.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key is NULL.
+ * @return  BAD_FUNC_ARG when parameters not supported.
+ * */
+int wc_LmsKey_SetParameters(LmsKey* key, int levels, int height,
+    int winternitz)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    /* Check state is valid. */
+    if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) {
+        WOLFSSL_MSG("error: LmsKey needs init");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        int i;
+
+        ret = BAD_FUNC_ARG;
+        /* Search through table for matching levels, height and width. */
+        for (i = 0; i < WC_LMS_MAP_LEN; i++) {
+            if ((levels == wc_lms_map[i].params.levels) &&
+                    (height == wc_lms_map[i].params.height) &&
+                    (winternitz == wc_lms_map[i].params.width)) {
+                /* Set the parameters into the key. */
+                key->params = &wc_lms_map[i].params;
+                ret = 0;
+                break;
+            }
+        }
+    }
+
+    if (ret == 0) {
+        /* Move the state to params set.
+         * Key is ready for MakeKey or Reload. */
+        key->state = WC_LMS_STATE_PARMSET;
+    }
+
+    return ret;
+}
+
+/* Get the parameters of an LMS key.
+ *
+ * Key must be inited and parameters set before calling this.
+ *
+ * @param [in]  key         LMS key.
+ * @param [out] levels      Number of levels of trees.
+ * @param [out] height      Height of the trees.
+ * @param [out] winternitz  Winternitz width.
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_GetParameters(const LmsKey* key, int* levels, int* height,
+    int* winternitz)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (levels == NULL) || (height == NULL) ||
+            (winternitz == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    /* Validate the parameters are available. */
+    if ((ret == 0) && (key->params == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Set the levels, height and Winternitz width from parameters. */
+        *levels = key->params->levels;
+        *height = key->params->height;
+        *winternitz = key->params->width;
+    }
+
+    return ret;
+}
+
+/* Frees the LMS key from memory.
+ *
+ * This does not affect the private key saved to non-volatile storage.
+ *
+ * @param [in, out] key  LMS key to free.
+ */
+void wc_LmsKey_Free(LmsKey* key)
+{
+    if (key != NULL) {
+    #ifndef WOLFSSL_LMS_VERIFY_ONLY
+        if (key->priv_data != NULL) {
+            const LmsParams* params = key->params;
+
+            ForceZero(key->priv_data, LMS_PRIV_DATA_LEN(params->levels,
+                params->height, params->p, params->rootLevels,
+                params->cacheBits));
+
+            XFREE(key->priv_data, key->heap, DYNAMIC_TYPE_LMS);
+        }
+    #endif
+
+        ForceZero(key, sizeof(LmsKey));
+
+        key->state = WC_LMS_STATE_FREED;
+    }
+}
+
+#ifndef WOLFSSL_LMS_VERIFY_ONLY
+/* Set the write private key callback to the LMS key structure.
+ *
+ * The callback must be able to write/update the private key to
+ * non-volatile storage.
+ *
+ * @param [in, out] key       LMS key.
+ * @param [in]      write_cb  Callback function that stores private key.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or write_cb is NULL.
+ * @return  BAD_STATE_E when key state is invalid.
+ */
+int wc_LmsKey_SetWriteCb(LmsKey* key, wc_lms_write_private_key_cb write_cb)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (write_cb == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Changing the write callback of an already working key is forbidden. */
+    if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetWriteCb: key in use");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Set the callback into the key. */
+        key->write_private_key = write_cb;
+    }
+
+    return ret;
+}
+
+/* Set the read private key callback to the LMS key structure.
+ *
+ * The callback must be able to read the private key from
+ * non-volatile storage.
+ *
+ * @param [in, out] key      LMS key.
+ * @param [in]      read_cb  Callback function that loads private key.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or read_cb is NULL.
+ * @return  BAD_STATE_E when key state is invalid.
+ * */
+int wc_LmsKey_SetReadCb(LmsKey* key, wc_lms_read_private_key_cb read_cb)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (read_cb == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Changing the read callback of an already working key is forbidden. */
+    if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetReadCb: key in use");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Set the callback into the key. */
+        key->read_private_key = read_cb;
+    }
+
+    return ret;
+}
+
+/* Sets the context to be used by write and read callbacks.
+ *
+ * E.g. this could be a filename if the callbacks write/read to file.
+ *
+ * @param [in, out] key      LMS key.
+ * @param [in]      context  Pointer to data for read/write callbacks.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or context is NULL.
+ * @return  BAD_STATE_E when key state is invalid.
+ * */
+int wc_LmsKey_SetContext(LmsKey* key, void* context)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (context == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Setting context of an already working key is forbidden. */
+    if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetContext: key in use");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Set the callback context into the key. */
+        key->context = context;
+    }
+
+    return ret;
+}
+
+/* Make the LMS private/public key pair. The key must have its parameters
+ * set before calling this.
+ *
+ * Write/read callbacks, and context data, must be set prior.
+ * Key must have parameters set.
+ *
+ * @param [in, out] key   LMS key.
+ * @param [in]      rng   Random number generator.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or rng is NULL.
+ * @return  BAD_STATE_E when key is in an invalid state.
+ * @return  BAD_FUNC_ARG when write callback or callback context not set.
+ * @return  BAD_STATE_E when no more signatures can be created.
+ */
+int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (rng == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check state. */
+    if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) {
+        WOLFSSL_MSG("error: LmsKey not ready for generation");
+        ret = BAD_STATE_E;
+    }
+    /* Check write callback set. */
+    if ((ret == 0) && (key->write_private_key == NULL)) {
+        WOLFSSL_MSG("error: LmsKey write callback is not set");
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check callback context set. */
+    if ((ret == 0) && (key->context == NULL)) {
+        WOLFSSL_MSG("error: LmsKey context is not set");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if ((ret == 0) && (key->priv_data == NULL)) {
+        const LmsParams* params = key->params;
+
+        /* Allocate memory for the private key data. */
+        key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels,
+            params->height, params->p, params->rootLevels, params->cacheBits),
+            key->heap, DYNAMIC_TYPE_LMS);
+        /* Check pointer is valid. */
+        if (key->priv_data == NULL) {
+            ret = MEMORY_E;
+        }
+    }
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        LmsState* state;
+    #else
+        LmsState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        /* Allocate memory for working state. */
+        state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize working state for use. */
+            ret = wc_lmskey_state_init(state, key->params);
+            if (ret == 0) {
+                /* Make the HSS key. */
+                ret = wc_hss_make_key(state, rng, key->priv_raw, &key->priv,
+                    key->priv_data, key->pub);
+                wc_lmskey_state_free(state);
+            }
+            ForceZero(state, sizeof(LmsState));
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+    if (ret == 0) {
+        /* Write private key to storage. */
+        int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
+            key->context);
+        if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
+            ret = IO_FAILED_E;
+        }
+    }
+
+    /* This should not happen, but check whether signatures can be created. */
+    if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) {
+        WOLFSSL_MSG("error: generated LMS key signatures exhausted");
+        key->state = WC_LMS_STATE_NOSIGS;
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Update state. */
+        key->state = WC_LMS_STATE_OK;
+    }
+
+    return ret;
+}
+
+/* Reload a key that has been prepared with the appropriate params and
+ * data. Use this if you wish to resume signing with an existing key.
+ *
+ * Write/read callbacks, and context data, must be set prior.
+ * Key must have parameters set.
+ *
+ * @param [in, out] key  LMS key.
+ *
+ * Returns 0 on success. */
+int wc_LmsKey_Reload(LmsKey* key)
+{
+    int ret = 0;
+
+    /* Validate parameter. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check state. */
+    if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) {
+        WOLFSSL_MSG("error: LmsKey not ready for reload");
+        ret = BAD_STATE_E;
+    }
+    /* Check read callback present. */
+    if ((ret == 0) && (key->read_private_key == NULL)) {
+        WOLFSSL_MSG("error: LmsKey read callback is not set");
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check context for callback set */
+    if ((ret == 0) && (key->context == NULL)) {
+        WOLFSSL_MSG("error: LmsKey context is not set");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if ((ret == 0) && (key->priv_data == NULL)) {
+        const LmsParams* params = key->params;
+
+        /* Allocate memory for the private key data. */
+        key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels,
+            params->height, params->p, params->rootLevels, params->cacheBits),
+            key->heap, DYNAMIC_TYPE_LMS);
+        /* Check pointer is valid. */
+        if (key->priv_data == NULL) {
+            ret = MEMORY_E;
+        }
+    }
+    if (ret == 0) {
+        /* Load private key. */
+        int rv = key->read_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
+            key->context);
+        if (rv != WC_LMS_RC_READ_TO_MEMORY) {
+            ret = IO_FAILED_E;
+        }
+    }
+
+    /* Double check the key actually has signatures left. */
+    if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) {
+        WOLFSSL_MSG("error: reloaded LMS key signatures exhausted");
+        key->state = WC_LMS_STATE_NOSIGS;
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        LmsState* state;
+    #else
+        LmsState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        /* Allocate memory for working state. */
+        state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize working state for use. */
+            ret = wc_lmskey_state_init(state, key->params);
+            if (ret == 0) {
+                /* Reload the key ready for signing. */
+                ret = wc_hss_reload_key(state, key->priv_raw, &key->priv,
+                    key->priv_data, NULL);
+            }
+            ForceZero(state, sizeof(LmsState));
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+
+    if (ret == 0) {
+        /* Update state. */
+        key->state = WC_LMS_STATE_OK;
+    }
+
+    return ret;
+}
+
+/* Get the private key length based on parameter set of key.
+ *
+ * @param [in]  key  LMS key.
+ * @param [out] len  Length of private key.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or len is NULL or parameters not set.
+ */
+int wc_LmsKey_GetPrivLen(const LmsKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Return private key length from parameter set. */
+        *len = HSS_PRIVATE_KEY_LEN;
+    }
+
+    return ret;
+}
+
+/* Sign a message.
+ *
+ * @param [in, out] key    LMS key to sign with.
+ * @param [out]     sig    Signature data. Buffer must be big enough to hold
+ *                         signature data.
+ * @param [out]     sigSz  Length of signature data.
+ * @param [in]      msg    Message to sign.
+ * @param [in]      msgSz  Length of message in bytes.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key, sig, sigSz or msg is NULL.
+ * @return  BAD_FUNC_ARG when msgSz is not greater than 0.
+ */
+int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg,
+    int msgSz)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (sig == NULL) || (sigSz == NULL) || (msg == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    if ((ret == 0) && (msgSz <= 0)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check state. */
+    if ((ret == 0) && (key->state == WC_LMS_STATE_NOSIGS)) {
+        WOLFSSL_MSG("error: LMS signatures exhausted");
+        ret = BAD_STATE_E;
+    }
+    if ((ret == 0) && (key->state != WC_LMS_STATE_OK)) {
+       /* The key had an error the last time it was used, and we
+        * can't guarantee its state. */
+        WOLFSSL_MSG("error: can't sign, LMS key not in good state");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        LmsState* state;
+    #else
+        LmsState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        /* Allocate memory for working state. */
+        state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize working state for use. */
+            ret = wc_lmskey_state_init(state, key->params);
+            if (ret == 0) {
+                /* Sign message. */
+                ret = wc_hss_sign(state, key->priv_raw, &key->priv,
+                    key->priv_data, msg, msgSz, sig);
+                wc_lmskey_state_free(state);
+            }
+            ForceZero(state, sizeof(LmsState));
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+    if (ret == 0) {
+        *sigSz = (word32)key->params->sig_len;
+    }
+    if (ret == 0) {
+        /* Write private key to storage. */
+        int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN,
+            key->context);
+        if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
+            ret = IO_FAILED_E;
+        }
+    }
+
+    return ret;
+}
+
+/* Returns whether signatures can be created with key.
+ *
+ * @param [in]  key  LMS key.
+ *
+ * @return  1 if there are signatures remaining.
+ * @return  0 if available signatures are exhausted.
+ */
+int wc_LmsKey_SigsLeft(LmsKey* key)
+{
+    int ret = 0;
+
+    /* NULL keys have no signatures remaining. */
+    if (key != NULL) {
+        ret = wc_hss_sigsleft(key->params, key->priv_raw);
+    }
+
+    return ret;
+}
+
+#endif /* ifndef WOLFSSL_LMS_VERIFY_ONLY*/
+
+/* Get the public key length based on parameter set of key.
+ *
+ * @param [in]  key  LMS key.
+ * @param [out] len  Length of public key.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or len is NULL or parameters not set.
+ */
+int wc_LmsKey_GetPubLen(const LmsKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters */
+    if ((key == NULL) || (len == NULL) || (key->params == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        *len = HSS_PUBLIC_KEY_LEN;
+    }
+
+    return ret;
+}
+
+/* Export a generated public key and parameter set from one LmsKey
+ * to another. Use this to prepare a signature verification LmsKey
+ * that is pub only.
+ *
+ * Though the public key is all that is used to verify signatures,
+ * the parameter set is needed to calculate the signature length
+ * before hand.
+ *
+ * @param [out] keyDst  LMS key to copy into.
+ * @param [in]  keySrc  LMS key to copy.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when keyDst or keySrc is NULL.
+ */
+int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc)
+{
+    int ret = 0;
+
+    if ((keyDst == NULL) || (keySrc == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        ForceZero(keyDst, sizeof(LmsKey));
+
+        keyDst->params = keySrc->params;
+        XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
+
+        /* Mark this key as verify only, to prevent misuse. */
+        keyDst->state = WC_LMS_STATE_VERIFYONLY;
+    }
+
+    return ret;
+}
+
+/* Exports the raw LMS public key buffer from key to out buffer.
+ * The out buffer should be large enough to hold the public key, and
+ * outLen should indicate the size of the buffer.
+ *
+ * Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
+ *
+ * @param [in]      key     LMS key.
+ * @param [out]     out     Buffer to hold encoded public key.
+ * @param [in, out] outLen  On in, length of out in bytes.
+ *                          On out, the length of the public key in bytes.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key, out or outLen is NULL.
+ * @return  BUFFER_E when outLen is too small to hold encoded public key.
+ */
+int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (out == NULL) || (outLen == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check size of out is sufficient. */
+    if ((ret == 0) && (*outLen < HSS_PUBLIC_KEY_LEN)) {
+        ret = BUFFER_E;
+    }
+
+    if (ret == 0) {
+        /* Return encoded public key. */
+        XMEMCPY(out, key->pub, HSS_PUBLIC_KEY_LEN);
+        *outLen = HSS_PUBLIC_KEY_LEN;
+    }
+
+    return ret;
+}
+
+/* Imports a raw public key buffer from in array to LmsKey key.
+ *
+ * The LMS parameters must be set first with wc_LmsKey_SetLmsParm or
+ * wc_LmsKey_SetParameters, and inLen must match the length returned
+ * by wc_LmsKey_GetPubLen.
+ *
+ * Call wc_LmsKey_GetPubLen beforehand to determine pubLen.
+ *
+ * @param [in, out] key    LMS key to put public key in.
+ * @param [in]      in     Buffer holding encoded public key.
+ * @param [in]      inLen  Length of encoded public key in bytes.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or in is NULL.
+ * @return  BUFFER_E when inLen does not match public key length by parameters.
+ */
+int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (in == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    if ((ret == 0) && (inLen != HSS_PUBLIC_KEY_LEN)) {
+        /* Something inconsistent. Parameters weren't set, or input
+         * pub key is wrong.*/
+        return BUFFER_E;
+    }
+
+    if (ret == 0) {
+        XMEMCPY(key->pub, in, inLen);
+
+        key->state = WC_LMS_STATE_VERIFYONLY;
+    }
+
+    return ret;
+}
+
+/* Given a levels, height, winternitz parameter set, determine
+ * the signature length.
+ *
+ * Call this before wc_LmsKey_Sign so you know the length of
+ * the required signature buffer.
+ *
+ * @param [in]  key  LMS key.
+ * @param [out] len  Length of a signature in bytes.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when key or len is NULL.
+ */
+int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (len == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        *len = key->params->sig_len;
+    }
+
+    return ret;
+}
+
+/* Verify the signature of the message with public key.
+ *
+ * @param [in] key    LMS key.
+ * @param [in] sig    Signature to verify.
+ * @param [in] sigSz  Size of signature in bytes.
+ * @param [in] msg    Message to verify.
+ * @param [in] msgSz  Length of the message in bytes.
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a key, sig or msg is NULL.
+ * @return  SIG_VERIFY_E when signature did not verify message.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  BUFFER_E when sigSz is invalid for parameters.
+ */
+int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz,
+    const byte* msg, int msgSz)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (sig == NULL) || (msg == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check state. */
+    if ((ret == 0) && (key->state != WC_LMS_STATE_OK) &&
+            (key->state != WC_LMS_STATE_VERIFYONLY)) {
+        /* LMS key not ready for verification. Param str must be
+         * set first, and Reload() called. */
+        WOLFSSL_MSG("error: LMS key not ready for verification");
+        ret = BAD_STATE_E;
+    }
+    /* Check signature length. */
+    if ((ret == 0) && (sigSz != key->params->sig_len)) {
+        ret = BUFFER_E;
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        LmsState* state;
+    #else
+        LmsState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        /* Allocate memory for working state. */
+        state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize working state for use. */
+            ret = wc_lmskey_state_init(state, key->params);
+            if (ret == 0) {
+                /* Verify signature of message with public key. */
+                ret = wc_hss_verify(state, key->pub, msg, msgSz, sig);
+                wc_lmskey_state_free(state);
+            }
+            ForceZero(state, sizeof(LmsState));
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+
+    return ret;
+}
+
+#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */

+ 3069 - 3
wolfcrypt/src/wc_lms_impl.c

@@ -19,8 +19,3074 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
-#include <wolfssl/wolfcrypt/settings.h>
+/* 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/wc_lms.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS)
+
+/* Length of R in bytes. */
+#define LMS_R_LEN           4
+/* Length of D in bytes. */
+#define LMS_D_LEN           2
+/* 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 = 54 */
+#define LMS_SEED_HASH_LEN  \
+    (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + LMS_MAX_NODE_LEN)
+
+/* Length of data to hash when computing a node:
+ *   16 + 4 + 2 + 32 + 32 = 86 */
+#define LMS_NODE_HASH_LEN   \
+    (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + 2 * LMS_MAX_NODE_LEN)
+
+/* Length of data to hash when computing most results:
+ *   16 + 4 + 2 + 1 + 32 = 55 */
+#define LMS_HASH_BUFFER_LEN \
+    (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + LMS_MAX_NODE_LEN)
+
+/* Length of data to hash when computing Q:
+ *   16 + 4 + 2 + 32 = 54 */
+#define LMS_Q_BUFFER_LEN    \
+    (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_MAX_NODE_LEN)
+
+/* 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 dubgging 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, int 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, int len)
+{
+    int i;
+
+    /* Starting at least-significant byte up to most. */
+    for (i = len - 1; i >= 0; i--) {
+        /* Add one/carry to byte. */
+        if ((++a[i]) != 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 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;
+            int 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;
+}
+
+/* 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) {
+            int 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;
+}
+
+/* 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
+}
+
+/***************************************
+ * 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;
+    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 = 0xff * n;
+            /* For each byte of the hash. */
+            for (i = 0; i < n; i++) {
+                /* Subtract coefficient from sum. */
+                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 = 2 * 0xf * 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 -= qe[0];
+                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 = 4 * 0x3 * n;
+            /* For each byte of the hash. */
+            for (i = 0; i < n; i++) {
+                /* Get coefficients. */
+                qe[0] = (q[i] >> 4)      ;
+                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 -= qe[0];
+                sum -= qe[1];
+                sum -= qe[2];
+                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 = 8 * 0x01 * n;
+            /* For each byte of the hash. */
+            for (i = 0; i < n; i++) {
+                /* Get coefficients. */
+                qe[0] = (q[i] >> 4)      ;
+                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 -= qe[0];
+                sum -= qe[1];
+                sum -= qe[2];
+                sum -= qe[3];
+                sum -= qe[4];
+                sum -= qe[5];
+                sum -= qe[6];
+                sum -= qe[7];
+                /* Move to next coefficients. */
+                qe += 8;
+            }
+            /* Put coefficients of checksum on the end. */
+            qe[0] = (word8)((sum >>  8)      );
+            qe[1] = (word8)((sum >>  7) & 0x1);
+            qe[2] = (word8)((sum >>  6) & 0x1);
+            qe[3] = (word8)((sum >>  5) & 0x1);
+            qe[4] = (word8)((sum >>  4) & 0x1);
+            qe[5] = (word8)((sum >>  3) & 0x1);
+            qe[6] = (word8)((sum >>  2) & 0x1);
+            qe[7] = (word8)((sum >>  1) & 0x1);
+            qe[8] = (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 = ((1 << w) - 1) * ((n * 8) / 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 = a >> (8 - w);
+                /* Subtract coefficient from sum. */
+                sum -= *qe;
+                /* Move to next coefficient. */
+                qe++;
+                /* Remove width bits. */
+                a <<= w;
+            }
+        }
+        /* Shift sum up as required to pack it on the end of hash. */
+        sum <<= ls;
+        /* For each width buts of checksum. */
+        for (j = 16 - w; j >= ls; j--) {
+            /* Get coefficient. */
+            *(qe++) = sum >> (16 - w);
+            /* Remove width bits. */
+            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);
+    /* H(I || u32str(q) || u16str(D_MESG) || ...) */
+    ret = wc_lms_hash_first(&state->hash, buffer, LMS_MSG_PRE_LEN);
+    if (ret == 0) {
+        /* H(... || C || ...) */
+        ret = wc_lms_hash_update(&state->hash, c, LMS_MAX_NODE_LEN);
+    }
+    if (ret == 0) {
+        /* H(... || message) */
+        ret = wc_lms_hash_update(&state->hash, msg, msgSz);
+    }
+    if (ret == 0) {
+        /* Q = H(...) */
+        ret = wc_lms_hash_final(&state->hash, q);
+    }
+
+    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 psueodo 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 = 0;
+    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, LMS_MAX_NODE_LEN, params->width, params->ls,
+             a);
+    }
+    #ifndef WC_LMS_FULL_HASH
+    if (ret == 0) {
+        /* Put in padding for final block. */
+        LMS_SHA256_SET_LEN_55(buffer);
+    }
+    #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;
+        XMEMCPY(tmp, seed, LMS_SEED_LEN);
+    #ifndef WC_LMS_FULL_HASH
+        ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+    #else
+        ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+    #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 = j;
+            /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */
+        #ifndef WC_LMS_FULL_HASH
+            ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+        #else
+            ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+        #endif /* !WC_LMS_FULL_HASH */
+        }
+
+        if (ret == 0) {
+            /* y[i] = tmp */
+            XMEMCPY(y, tmp, LMS_MAX_NODE_LEN);
+            y += LMS_MAX_NODE_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);
+    /* H(I || u32str(q) || u16str(D_PBLC) || ...). */
+    ret = wc_lms_hash_first(&state->hash_k, 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, LMS_MAX_NODE_LEN, params->width, params->ls,
+            a);
+    }
+    #ifndef WC_LMS_FULL_HASH
+    if (ret == 0) {
+        /* Put in padding for final block. */
+        LMS_SHA256_SET_LEN_55(buffer);
+    }
+    #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, LMS_MAX_NODE_LEN);
+        sig_y += LMS_MAX_NODE_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
+            ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+        #else
+            ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+        #endif /* !WC_LMS_FULL_HASH */
+        }
+
+        if (ret == 0) {
+            /* H(... || z[i] || ...) (for calculating Kc). */
+            ret = wc_lms_hash_update(&state->hash_k, tmp, LMS_MAX_NODE_LEN);
+        }
+    }
+
+    if (ret == 0) {
+        /* Kc = H(...) */
+        ret = wc_lms_hash_final(&state->hash_k, kc);
+    }
+
+    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 psueodo 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);
+    /* K = H(I || u32str(q) || u16str(D_PBLC) || ...) */
+    ret = wc_lms_hash_first(&state->hash_k, buffer, LMS_K_PRE_LEN);
+
+#ifndef WC_LMS_FULL_HASH
+    /* Put in padding for final block. */
+    LMS_SHA256_SET_LEN_55(buffer);
+#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;
+        XMEMCPY(tmp, seed, LMS_SEED_LEN);
+    #ifndef WC_LMS_FULL_HASH
+        ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+    #else
+        ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+    #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
+            ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+        #else
+            ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+        #endif /* !WC_LMS_FULL_HASH */
+        }
+        if (ret == 0) {
+            /* K = H(... || y[i] || ...) */
+            ret = wc_lms_hash_update(&state->hash_k, tmp, LMS_MAX_NODE_LEN);
+        }
+    }
+    if (ret == 0) {
+        /* K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1]) */
+        ret = wc_lms_hash_final(&state->hash_k, k);
+    }
+
+    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 + LMS_SEED_LEN;
+
+    /* u32str(type) || ... || T(1) */
+    c32toa(params->lmsType, pub);
+    pub += 4;
+    /* u32str(type) || u32str(otstype) || ... || T(1) */
+    c32toa(params->lmOtsType, 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) {
+        ret = PUBLIC_KEY_E;
+    }
+    if (ret == 0) {
+        /* Get node hash and Winternitz width type. */
+        ato32(pub, &type);
+        /* Compare with parameters. */
+        if (type != params->lmOtsType) {
+            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 + LMS_MAX_NODE_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 [out] priv    Private key data.
+ */
+static int wc_lmots_make_private_key(WC_RNG* rng, byte* priv)
+{
+    return wc_RNG_GenerateBlock(rng, priv, LMS_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;
+    /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */
+    XMEMCPY(tmp, seed, LMS_SEED_LEN);
+    /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED)
+     * sig = u32str(type) || C || ... */
+#ifndef WC_LMS_FULL_HASH
+    /* Put in padding for final block. */
+    LMS_SHA256_SET_LEN_55(buffer);
+    ret = wc_lms_hash_block(&state->hash, buffer, sig_c);
+#else
+    ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, sig_c);
+#endif /* !WC_LMS_FULL_HASH */
+
+    if (ret == 0) {
+        byte* sig_y = sig_c + LMS_MAX_NODE_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 += params->height * LMS_MAX_NODE_LEN;
+
+    /* Stack of nodes. */
+    state->stack.stack = priv_data;
+    priv_data += (params->height + 1) * LMS_MAX_NODE_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);
+
+    /* Cached leaf nodes. */
+    state->leaf.cache = priv_data;
+    priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits);
+    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 += params->height * LMS_MAX_NODE_LEN;
+
+    /* Stack of nodes. */
+    priv_data += (params->height + 1) * LMS_MAX_NODE_LEN;
+    c32toa(state->stack.offset, priv_data);
+    priv_data += 4;
+
+    /* Cached root nodes. */
+    priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels);
+
+    /* Cached leaf nodes. */
+    priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits);
+    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));
+    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]) */
+    #ifndef WC_LMS_FULL_HASH
+        /* Put in padding for final block. */
+        LMS_SHA256_SET_LEN_54(buffer);
+        ret = wc_lms_hash_block(&state->hash, buffer, leaf);
+    #else
+        ret = wc_lms_hash(&state->hash, buffer, LMS_SEED_HASH_LEN, leaf);
+    #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)
+{
+    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);
+    /* left_side = pop(data stack)
+     * I || u32str(r) || u16str(D_INTR) || left_side || temp */
+    XMEMCPY(left, sp, LMS_MAX_NODE_LEN);
+    /* temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) */
+    return wc_lms_hash(&state->hash, buffer, LMS_NODE_HASH_LEN, node);
+}
+
+#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 + LMS_MAX_NODE_LEN;
+#ifdef WOLFSSL_SMALL_STACK
+    byte* stack = NULL;
+#else
+    byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN];
+#endif /* WOLFSSL_SMALL_STACK */
+    byte* sp;
+    word32 i;
+
+    /* I || ... */
+    XMEMCPY(buffer, id, LMS_I_LEN);
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Allocate stack of left side hashes. */
+    stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL,
+        DYNAMIC_TYPE_TMP_BUFFER);
+    if (stack == NULL) {
+        ret = MEMORY_E;
+    }
+#endif /* WOLFSSL_SMALL_STACK */
+    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, LMS_MAX_NODE_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 -= LMS_MAX_NODE_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 * LMS_MAX_NODE_LEN, temp,
+                    LMS_MAX_NODE_LEN);
+            }
+        }
+        /* Push temp onto the data stack. */
+        XMEMCPY(sp, temp, LMS_MAX_NODE_LEN);
+        sp += LMS_MAX_NODE_LEN;
+    }
+
+    if ((ret == 0) && (pub != NULL)) {
+        /* Public key, root node, is top of data stack. */
+        XMEMCPY(pub, stack, LMS_MAX_NODE_LEN);
+    }
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif /* WOLFSSL_SMALL_STACK */
+    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 + LMS_MAX_NODE_LEN;
+#ifdef WOLFSSL_SMALL_STACK
+    byte* stack = NULL;
+#else
+    byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN];
+#endif /* WOLFSSL_SMALL_STACK */
+    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);
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Allocate stack of left side hashes. */
+    stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL,
+        DYNAMIC_TYPE_TMP_BUFFER);
+    if (stack == NULL) {
+        ret = MEMORY_E;
+    }
+#endif /* WOLFSSL_SMALL_STACK */
+
+    /* 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 * LMS_MAX_NODE_LEN, temp, LMS_MAX_NODE_LEN);
+        }
+
+        /* Store the node if on the authentication path. */
+        if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) {
+            XMEMCPY(auth_path, temp, LMS_MAX_NODE_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 -= LMS_MAX_NODE_LEN;
+            ret = wc_lms_interior_hash(state, stack + spi, r, temp);
 
-#ifdef WOLFSSL_HAVE_LMS
-    #error "Contact wolfSSL to get the implementation of this file"
+            /* Copy out top root nodes. */
+            if ((h > params->height - params->rootLevels) &&
+                    ((i >> (h-1)) != ((i + 1) >> (h - 1)))) {
+                int off = (1 << (params->height - h)) + (i >> h) - 1;
+                XMEMCPY(root + off * LMS_MAX_NODE_LEN, temp, LMS_MAX_NODE_LEN);
+            }
+
+            /* Copy out node to authentication path if on path. */
+            if ((ret == 0) && (auth_path != NULL) && ((q >> h) ^ 0x1) == j) {
+                XMEMCPY(auth_path + h * LMS_MAX_NODE_LEN, temp,
+                    LMS_MAX_NODE_LEN);
+            }
+        }
+        /* Push temp onto the data stack. */
+        XMEMCPY(stack + spi, temp, LMS_MAX_NODE_LEN);
+        spi += LMS_MAX_NODE_LEN;
+
+        if (i == q - 1) {
+            XMEMCPY(privState->stack.stack, stack, spi);
+            privState->stack.offset = spi;
+        }
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif /* WOLFSSL_SMALL_STACK */
+    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 + LMS_MAX_NODE_LEN;
+#ifdef WOLFSSL_SMALL_STACK
+    byte* stack = NULL;
+#else
+    byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN];
+#endif /* WOLFSSL_SMALL_STACK */
+    byte* sp;
+    word32 max_cb = (word32)1 << params->cacheBits;
+    word32 i;
+
+    /* I || ... */
+    XMEMCPY(buffer, id, LMS_I_LEN);
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Allocate stack of left side hashes. */
+    stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL,
+        DYNAMIC_TYPE_TMP_BUFFER);
+    if (stack == NULL) {
+        ret = MEMORY_E;
+    }
+#endif /* WOLFSSL_SMALL_STACK */
+
+    /* Public key, root node, is top of data stack. */
+    XMEMCPY(stack, stackCache->stack, params->height * LMS_MAX_NODE_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) *
+                LMS_MAX_NODE_LEN;
+            /* Copy cached node into working buffer. */
+            XMEMCPY(temp, leaf->cache + off, LMS_MAX_NODE_LEN);
+            /* I || u32str(i) || ... */
+            c32toa(i, rp);
+        }
+        else {
+            /* Calculate leaf node hash. */
+            ret = wc_lms_leaf_hash(state, seed, i, r, temp);
+
+            /* Check if this is at the end of the cache and not beyond q plus
+             * the number of leaf nodes. */
+            if ((i == leaf->idx + max_cb) && (i < (q + max_cb))) {
+                /* Copy working node into cache over old first node. */
+                XMEMCPY(leaf->cache + leaf->offset * LMS_MAX_NODE_LEN, temp,
+                    LMS_MAX_NODE_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, LMS_MAX_NODE_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 -= LMS_MAX_NODE_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 * LMS_MAX_NODE_LEN),
+                    LMS_MAX_NODE_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)))) {
+                int off = (1 << (params->height - h)) + (i >> h) - 1;
+                XMEMCPY(privState->root + off * LMS_MAX_NODE_LEN, temp,
+                    LMS_MAX_NODE_LEN);
+            }
+
+            /* Copy out node to authentication path if on path. */
+            if ((ret == 0) && (((q >> h) ^ 0x1) == j)) {
+                XMEMCPY(auth_path + h * LMS_MAX_NODE_LEN, temp,
+                    LMS_MAX_NODE_LEN);
+            }
+        }
+        if (ret == 0) {
+            /* Push temp onto the data stack. */
+            XMEMCPY(sp, temp, LMS_MAX_NODE_LEN);
+            sp += LMS_MAX_NODE_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 (!useRoot) {
+        /* Copy stack back. */
+        XMEMCPY(stackCache->stack, stack, params->height * LMS_MAX_NODE_LEN);
+        stackCache->offset = (word32)((size_t)sp - (size_t)stack);
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif /* WOLFSSL_SMALL_STACK */
+    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 + LMS_SEED_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, 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 += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN;
+        /* S = u32str(q) || ots_signature || u32str(type) || ... */
+        c32toa(params->lmsType, 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, sig);
+    sig += LMS_TYPE_LEN;
+    /* S = u32str(q) || ots_signature || ... */
+    XMEMCPY(sig, y, LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN);
+    sig += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN;
+    /* S = u32str(q) || ots_signature || u32str(type) || ... */
+    c32toa(params->lmsType, 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,                    node + LMS_MAX_NODE_LEN },
+                      { node + LMS_MAX_NODE_LEN, node                    } };
+    /* node_num = 2^h + q */
+    word32 r = (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, LMS_MAX_NODE_LEN);
+    /* Put tmp into offset required for first iteration. */
+#ifndef WC_LMS_FULL_HASH
+    /* Put in padding for final block. */
+    LMS_SHA256_SET_LEN_54(buffer);
+    ret = wc_lms_hash_block(&state->hash, buffer, b[r & 1][0]);
+#else
+    ret = wc_lms_hash(&state->hash, buffer, LMS_SEED_HASH_LEN, b[r & 1][0]);
+#endif /* !WC_LMS_FULL_HASH */
+
+    if (ret == 0) {
+        int i;
+
+        /* I||...||u16str(D_INT)||... */
+        c16toa(LMS_D_INTR, ip);
+
+        /* Do all but last height. */
+        for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
+            /* Put path into offset required. */
+            XMEMCPY(b[r & 1][1], path, LMS_MAX_NODE_LEN);
+            path += LMS_MAX_NODE_LEN;
+
+            /* 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(&state->hash, buffer, LMS_NODE_HASH_LEN,
+                b[r & 1][0]);
+        }
+        if (ret == 0) {
+            /* Last height. */
+            /* Put path into offset required. */
+            XMEMCPY(b[r & 1][1], path, LMS_MAX_NODE_LEN);
+            /* 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(&state->hash, buffer, LMS_NODE_HASH_LEN, tc);
+        }
+    }
+
+    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)
+{
+    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;
+
+    /* 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 2.j. */
+        const byte* sig_path = sig + LMS_Q_LEN + LMS_TYPE_LEN +
+            LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_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, LMS_MAX_NODE_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, LMS_SEED_LEN);
+    /* SEED = H(parent's I || q || D_CHILD_SEED || D_FIXED || parent's SEED) */
+#ifndef WC_LMS_FULL_HASH
+    /* Put in padding for final block. */
+    LMS_SHA256_SET_LEN_55(buffer);
+    ret = wc_lms_hash_block(&state->hash, buffer, seed_i);
+#else
+    ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, seed_i);
+#endif /* !WC_LMS_FULL_HASH */
+
+    if (ret == 0) {
+        seed_i += LMS_SEED_LEN;
+        /* 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) */
+    #ifndef WC_LMS_FULL_HASH
+        ret = wc_lms_hash_block(&state->hash, buffer, tmp);
+    #else
+        ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp);
+    #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)1 << (h)) - 1))
+
+/* 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;
+    int 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, LMS_SEED_I_LEN);
+    }
+
+    /* Compute SEED and I for rest of levels. */
+    for (i = 1; (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, 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 += LMS_SEED_I_LEN;
+
+        /* Get q for level from 64-bit composite. */
+        q32 = w64GetLow32(w64ShiftRight(q, (params->levels - 1 - i) *
+            params->height)) & (((word32)1 << params->height) - 1);
+        /* 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 + LMS_SEED_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;
+
+    priv_q = priv;
+    priv += LMS_Q_LEN;
+    priv_seed = curr + LMS_Q_LEN;
+    priv += LMS_SEED_LEN;
+    priv_i = curr + LMS_Q_LEN + LMS_SEED_LEN;
+    priv += LMS_I_LEN;
+
+    ato32(curr, &pq);
+    pq = (pq + 1) & ((1 << params->height) - 1);
+    c32toa(pq, priv_q);
+
+    privState->stack.offset = 0;
+    privState->leaf.idx = (word32)-(1 << 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 + LMS_SEED_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];
+    int use_tmp = 0;
+    int 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, &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 + LMS_SEED_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 + LMS_SEED_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, LMS_Q_LEN);
+        }
+
+        lastQMax = (qc == ((word32)1 << params->height) - 1);
+        curr += LMS_PRIV_LEN;
+        priv += LMS_PRIV_LEN;
+        p64_hi = cp64_hi;
+        q64_hi = cq64_hi;
+    }
+
+    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);
+    wc_lms_idx_inc(priv, LMS_Q_LEN);
+
+    for (i = 1; (ret == 0) && (i < params->levels); i++) {
+        word32 q;
+
+        ato32(curr + LMS_PRIV_LEN, &q);
+        ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1],
+            curr, priv, q);
+
+        curr += LMS_PRIV_LEN;
+        priv += LMS_PRIV_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 * (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 + LMS_SEED_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;
+    }
+
+    if ((ret == 0) && (pub_root != NULL)) {
+        XMEMCPY(pub_root, priv_key->state[0].root, LMS_MAX_NODE_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 * (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 + LMS_SEED_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)) {
+                    int off = (1 << (params->height - h)) + (qa >> h) - 1;
+                    XMEMCPY(privState->auth_path + h * LMS_MAX_NODE_LEN,
+                        privState->root + off * LMS_MAX_NODE_LEN,
+                        LMS_MAX_NODE_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 + i * LMS_PRIV_LEN +
+                        LMS_Q_LEN;
+                    priv_i = priv_seed + LMS_SEED_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;
+    }
+
+    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];
+    byte* root = pub + LMS_PUBKEY_LEN - LMS_MAX_NODE_LEN;
+    byte* priv = priv_key->priv;
+    int i;
+
+    for (i = params->levels - 2; i >= 0; i--) {
+        const byte* p = priv + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN);
+        const byte* priv_q = p;
+        const byte* priv_seed = priv_q + LMS_Q_LEN;
+        const byte* priv_i = priv_seed + LMS_SEED_LEN;
+
+        /* ... || T(1) */
+        XMEMCPY(root, priv_key->state[i + 1].root, LMS_MAX_NODE_LEN);
+        /* u32str(type) || u32str(otstype) || I || T(1) */
+        p = priv + (i + 1) * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_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,
+            priv_key->y + i * LMS_PRIV_Y_TREE_LEN(params->p));
+    }
+
+    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);
+
+#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);
+    }
+
+#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);
+    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);
+    }
+#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;
+
+    (void)key;
+
+    /* Expanded private keys. */
+    priv_data += LMS_PRIV_KEY_LEN(params->levels);
+
+    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);
+    }
+#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
+    /* Next subtree's expanded private keys. */
+    priv_data += LMS_PRIV_KEY_LEN(params->levels);
+    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);
+    }
+#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;
+
+    (void)pub_root;
+
+    wc_hss_priv_data_load(state->params, priv_key, priv_data);
+#ifndef WOLFSSL_WC_LMS_SMALL
+    priv_key->inited = 0;
+#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 */
+        /* Set initialized flag. */
+        priv_key->inited = (ret == 0);
+    }
+#endif /* WOLFSSL_WC_LMS_SMALL */
+
+    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;
+    int 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] = (params->lmsType << 4) + params->lmOtsType;
+    }
+    /* 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, 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 + LMS_SEED_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);
+    }
+
+    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(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 + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN);
+            byte* root = NULL;
+
+            /* Move to start of next signature at this level. */
+            sig -= LMS_SIG_LEN(params->height, params->p);
+            if (i != 0) {
+                /* Put root node into signature at this index. */
+                root = sig - LMS_MAX_NODE_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 + LMS_MAX_NODE_LEN +
+                    params->p * LMS_MAX_NODE_LEN + LMS_TYPE_LEN;
+                byte* priv_q = p;
+                byte* priv_seed = priv_q + LMS_Q_LEN;
+                byte* priv_i = priv_seed + LMS_SEED_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;
+                msg = sig;
+                msgSz = LMS_PUBKEY_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(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 + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN);
+        byte* root = NULL;
+    #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);
+        if (i != 0) {
+            /* Put root node into signature at this index. */
+            root = sig - LMS_MAX_NODE_LEN;
+        }
+
+    #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 +
+                i * LMS_PRIV_Y_TREE_LEN(params->p), 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 + i * LMS_PRIV_Y_TREE_LEN(params->p), s,
+                    LMS_PRIV_Y_TREE_LEN(params->p));
+            }
+        #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */
+            s += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN +
+                LMS_TYPE_LEN;
+
+            /* Copy the authentication path out of the private key. */
+            XMEMCPY(s, priv_key->state[i].auth_path,
+                params->height * LMS_MAX_NODE_LEN);
+            /* Copy the root node into signature unless at top. */
+            if (i != 0) {
+                XMEMCPY(root, priv_key->state[i].root, LMS_MAX_NODE_LEN);
+            }
+        }
+        if ((ret == 0) && (i != 0)) {
+            /* Create public data for this level if there is another. */
+            sig -= LMS_PUBKEY_LEN;
+            msg = sig;
+            msgSz = LMS_PUBKEY_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 data.
+ * @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)
+{
+    w64wrapper q;
+    w64wrapper cnt;
+
+    /* 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. */
+    return w64LT(q, cnt);
+}
+#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 rifyn.
+ * @param [in]      msgSz  Length of message in bytes.
+ * @param [in]      sig    Signature of message.
+ * @return  0 on success.
+ * @return  SIG_VERFIY_E on failure.
+ */
+int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg,
+    word32 msgSz, const byte* sig)
+{
+    const LmsParams* params = state->params;
+    int ret = 0;
+    word32 nspk;
+    const byte* key = pub + LMS_L_LEN;
+    word32 levels;
+
+    /* 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;
+
+    /* Line 2: Verify that pub and signature match in levels. */
+    if (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++) {
+            /* Line 7: Get start of public key in signature. */
+            const byte* pubList = sig + LMS_Q_LEN + LMS_TYPE_LEN +
+                LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN + LMS_TYPE_LEN +
+                params->height * LMS_MAX_NODE_LEN;
+            /* Line 8: Verify the LMS signature with public key as message. */
+            ret = wc_lms_verify(state, key, pubList, LMS_PUBKEY_LEN, sig);
+            /* Line 10: Next key is from signature. */
+            key = pubList;
+            /* Line 6: Move to start of next signature. */
+            sig = pubList + LMS_PUBKEY_LEN;
+        }
+    }
+    if (ret == 0) {
+        /* Line 12: Verify bottom tree with real message. */
+        ret = wc_lms_verify(state, key, msg, msgSz, sig);
+    }
+
+    return ret;
+}
+
+#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */
+

+ 1643 - 1
wolfcrypt/src/wc_xmss.c

@@ -19,8 +19,1650 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
 #include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/wolfcrypt/logging.h>
 
 #ifdef WOLFSSL_HAVE_XMSS
-    #error "Contact wolfSSL to get the implementation of this file"
+#include <wolfssl/wolfcrypt/wc_xmss.h>
+
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+
+/***************************
+ * DIGEST init and free.
+ ***************************/
+
+/* Initialize the digest algorithm to use.
+ *
+ * @param [in, out] state  XMSS/MT state including digest and parameters.
+ * @return  0 on success.
+ * @return  NOT_COMPILED_IN when digest algorithm not supported.
+ * @return  Other negative when digest algorithm initialization failed.
+ */
+static int wc_xmss_digest_init(XmssState* state)
+{
+    int ret;
+    word8 hash = state->params->hash;
+
+#ifdef WC_XMSS_SHA256
+    if (hash == WC_HASH_TYPE_SHA256) {
+        ret = wc_InitSha256(&state->digest.sha256);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHA512
+    if (hash == WC_HASH_TYPE_SHA512) {
+        ret = wc_InitSha512(&state->digest.sha512);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHAKE128
+    if (hash == WC_HASH_TYPE_SHAKE128) {
+        ret = wc_InitShake128(&state->digest.shake, NULL, INVALID_DEVID);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHAKE256
+    if (hash == WC_HASH_TYPE_SHAKE256) {
+        ret = wc_InitShake256(&state->digest.shake, NULL, INVALID_DEVID);
+    }
+    else
+#endif
+    {
+        ret = NOT_COMPILED_IN;
+    }
+
+    return ret;
+}
+/* Free the digest algorithm.
+ *
+ * @param [in, out] state  XMSS/MT state including digest and parameters.
+ */
+static void wc_xmss_digest_free(XmssState* state)
+{
+    word8 hash = state->params->hash;
+
+#ifdef WC_XMSS_SHA256
+    if (hash == WC_HASH_TYPE_SHA256) {
+        wc_Sha256Free(&state->digest.sha256);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHA512
+    if (hash == WC_HASH_TYPE_SHA512) {
+        wc_Sha512Free(&state->digest.sha512);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHAKE128
+    if (hash == WC_HASH_TYPE_SHAKE128) {
+        wc_Shake128_Free(&state->digest.shake);
+    }
+    else
+#endif
+#ifdef WC_XMSS_SHAKE256
+    if (hash == WC_HASH_TYPE_SHAKE256) {
+        wc_Shake256_Free(&state->digest.shake);
+    }
+    else
+#endif
+    {
+        /* Do nothing. */
+    }
+}
+
+/* Initialize the XMSS/MT state.
+ *
+ * @param [in, out] state   XMSS/MT state including digest and parameters.
+ * @param [in]      params  Parameters for key.
+ * @return  0 on success.
+ * @return  NOT_COMPILED_IN when digest algorithm not supported.
+ * @return  Other negative when digest algorithm initialization failed.
+ */
+static WC_INLINE int wc_xmss_state_init(XmssState* state,
+    const XmssParams* params)
+{
+    state->params = params;
+    state->ret = 0;
+    return wc_xmss_digest_init(state);
+}
+
+/* Free the XMSS/MT state.
+ *
+ * @param [in, out] state  XMSS/MT state including digest and parameters.
+ */
+static WC_INLINE void wc_xmss_state_free(XmssState* state)
+{
+    wc_xmss_digest_free(state);
+}
+
+
+/***************************
+ * XMSS PARAMS
+ ***************************/
+
+/* Map of XMSS/MT string name to OID.
+ */
+typedef struct wc_XmssString {
+    /* Name of algorithm as a string. */
+    const char* str;
+    /* OID for algorithm. */
+    word32 oid;
+    /* XMSS parameters. */
+    XmssParams params;
+} wc_XmssString;
+
+#ifndef WOLFSSL_WC_XMSS_SMALL
+
+/* Size of BDS State encoded numbers - offset=1, next=3. */
+#define XMSS_BDS_NUMS_SZ      4
+/* Size of treehash encoding - nextIdx=3, completed|used=1. */
+#define XMSS_TREEHASH_SZ      4
+
+/* Calculate Secret key length.
+ *
+ * See wc_xmss_bds_state_save() and wc_xmss_bds_state_load().
+ *
+ * SK = idx || wots_sk || SK_PRF || root || SEED || BDSs || OTHER
+ * BDSs = (2 * depth - 1) * BDS
+ * BDS = stack || height || authPath || keep || nodes || retain ||
+ *       offset || next || TREEHASHes
+ * TREEHASHes = (Subtree height - BDS k param) * TREEHASH
+ * TREEHASH = nextIdx || completed || used
+ *
+ * @param [in] n  Number of bytes to hash output.
+ * @param [in] h  Height of full tree.
+ * @param [in] d  Depth of trees (number of subtrees).
+ * @param [in] s  Subtree height.
+ * @param [in] i  Length of index encoding in bytes.
+ * @param [in] k  BDS k parameter.
+ * @return  Secret key length in bytes.
+ */
+#define XMSS_SK_LEN(n, h, d, s, i, k)                               \
+    (((i) + 4 * (n)) +                                              \
+     (2 * (d) - 1) * (((s) + 1) * (n) +                             \
+                    (s) + 1 +                                       \
+                    (s) * (n) +                                     \
+                    ((s) >> 1) * (n) +                              \
+                    ((s) - (k)) * XMSS_TREEHASH_SZ +                \
+                    ((s) - (k)) * (n) +                             \
+                    XMSS_RETAIN_LEN(k, n) +                         \
+                    XMSS_BDS_NUMS_SZ) +                             \
+     ((d) - 1) * (n) * ((n) * 2 + 3))
+
+#else
+
+/* Calculate Secret key length.
+ *
+ * SK = idx || wots_sk || SK_PRF || root || SEED
+ *
+ * @param [in] n  Number of bytes to hash output.
+ * @param [in] h  Height of full tree. Unused.
+ * @param [in] d  Depth of trees (number of subtrees). Unused.
+ * @param [in] s  Subtree height. Unused.
+ * @param [in] i  Length of index encoding in bytes.
+ * @param [in] k  BDS k parameter. Unused.
+ * @return  Secret key length.
+ */
+#define XMSS_SK_LEN(n, h, d, s, i, k)                               \
+    ((i) + 4 * (n))
+
+#endif
+
+#ifndef WOLFSSL_XMSS_LARGE_SECRET_KEY
+/* Choose the smaller BDS K parameter. */
+#define XMSS_K(k, kl)   (k)
+#else
+/* Choose the larger BDS K parameter. */
+#define XMSS_K(k, kl)   (kl)
+#endif
+
+/* Calculate all fixed parameter values and output an array declaration.
+ *
+ * @param [in] hash  Hash algorithm to use.
+ * @param [in] n     Number of bytes to hash output.
+ * @param [in] p     Number of bytes of padding.
+ * @param [in] h     Height of full tree.
+ * @param [in] d     Depth of trees (number of subtrees).
+ * @param [in] i     Length of index encoding in bytes.
+ * @param [in] k     BDS k parameter. 0 or >= 2 but (h/d - k) is even.
+ * @param [in] kl    BDS k parameter when large signatures.
+ * @return  XMSS/XMSS^MT parameters array declaration.
+ */
+#define XMSS_PARAMS(hash, n, p, h, d, i, k, kl)                             \
+    { hash, n, p, (n) * 2 + 3, (n) * ((n) * 2 + 3), h, (h) / (d), (d), (i), \
+      (i) + (n) + (d) * (((n) * 2 + 3) * (n)) + (h) * (n),                  \
+      XMSS_SK_LEN(n, h, d, ((h) / (d)), i, XMSS_K(k, kl)), (n) * 2,         \
+      XMSS_K(k, kl) }
+    /* hash, d, pad_len, wots_len, wots_sig_len, h, sub_h, d, idx_len,
+     * sig_len,
+     * sk_len, pk_len,
+     * bds_k */
+
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20
+/* List of known XMSS algorithm strings and their OIDs. */
+static const wc_XmssString wc_xmss_alg[] = {
+#ifdef WC_XMSS_SHA256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHA2_10_256",     WC_XMSS_OID_SHA2_10_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHA2_16_256",     WC_XMSS_OID_SHA2_16_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHA2_20_256",     WC_XMSS_OID_SHA2_20_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHA256 */
+#ifdef WC_XMSS_SHA512
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHA2_10_512",     WC_XMSS_OID_SHA2_10_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHA2_16_512",     WC_XMSS_OID_SHA2_16_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHA2_20_512",     WC_XMSS_OID_SHA2_20_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 512 */
+#endif /* WC_XMSS_SHA512 */
+
+#ifdef WC_XMSS_SHAKE128
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHAKE_10_256",    WC_XMSS_OID_SHAKE_10_256   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHAKE_16_256",    WC_XMSS_OID_SHAKE_16_256   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHAKE_20_256",    WC_XMSS_OID_SHAKE_20_256   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHAKE128 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHAKE_10_512",    WC_XMSS_OID_SHAKE_10_512   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHAKE_16_512",    WC_XMSS_OID_SHAKE_16_512   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHAKE_20_512",    WC_XMSS_OID_SHAKE_20_512   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 512 */
+#endif /* WC_XMSS_SHAKE256 */
+
+#ifdef WC_XMSS_SHA256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHA2_10_192",     WC_XMSS_OID_SHA2_10_192    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHA2_16_192",     WC_XMSS_OID_SHA2_16_192    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHA2_20_192",     WC_XMSS_OID_SHA2_20_192    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 192 */
+#endif /* WC_XMSS_SHA256 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHAKE256_10_256", WC_XMSS_OID_SHAKE256_10_256,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHAKE256_16_256", WC_XMSS_OID_SHAKE256_16_256,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHAKE256_20_256", WC_XMSS_OID_SHAKE256_20_256,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHAKE256 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10
+    { "XMSS-SHAKE256_10_192", WC_XMSS_OID_SHAKE256_10_192,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 10, 1, 4, 0, 4), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16
+    { "XMSS-SHAKE256_16_192", WC_XMSS_OID_SHAKE256_16_192,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 16, 1, 4, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSS-SHAKE256_20_192", WC_XMSS_OID_SHAKE256_20_192,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 20, 1, 4, 0, 0), },
+#endif
+#endif /* HASH_SIZE 192 */
+#endif /* WC_XMSS_SHAKE256 */
+};
+/* Length of array of known XMSS algorithms. */
+#define WC_XMSS_ALG_LEN     (sizeof(wc_xmss_alg) / sizeof(*wc_xmss_alg))
+#endif
+
+/* Convert XMSS algorithm string to an OID - object identifier.
+ *
+ * @param [out] oid     OID value corresponding to string.
+ * @param [in]  s       String to convert.
+ * @param [out] params  XMSS/MT parameters.
+ * @return  0 on success.
+ * @return  NOT_COMPILED_IN on failure.
+ */
+static int wc_xmss_str_to_params(const char *s, word32* oid,
+    const XmssParams** params)
+{
+    int ret = NOT_COMPILED_IN;
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20
+    unsigned int i;
+
+    for (i = 0; i < WC_XMSS_ALG_LEN; i++) {
+         if (XSTRCMP(s, wc_xmss_alg[i].str) == 0) {
+             *oid = wc_xmss_alg[i].oid;
+             *params = &wc_xmss_alg[i].params;
+             ret = 0;
+             break;
+         }
+    }
+#else
+    (void)s;
+    (void)oid;
+    (void)params;
+#endif
+
+    return ret;
+}
+
+#if WOLFSSL_XMSS_MAX_HEIGHT >= 20
+/* List of known XMSS^MT algorithm strings and their OIDs. */
+static const wc_XmssString wc_xmssmt_alg[] = {
+#ifdef WC_XMSS_SHA256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHA2_20/2_256",      WC_XMSSMT_OID_SHA2_20_2_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHA2_20/4_256",      WC_XMSSMT_OID_SHA2_20_4_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHA2_40/2_256",      WC_XMSSMT_OID_SHA2_40_2_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/4_256",      WC_XMSSMT_OID_SHA2_40_4_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/8_256",      WC_XMSSMT_OID_SHA2_40_8_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHA2_60/3_256",      WC_XMSSMT_OID_SHA2_60_3_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/6_256",      WC_XMSSMT_OID_SHA2_60_6_256     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/12_256",     WC_XMSSMT_OID_SHA2_60_12_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   32, 32, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHA256 */
+#ifdef WC_XMSS_SHA512
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHA2_20/2_512",      WC_XMSSMT_OID_SHA2_20_2_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHA2_20/4_512",      WC_XMSSMT_OID_SHA2_20_4_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHA2_40/2_512",      WC_XMSSMT_OID_SHA2_40_2_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/4_512",      WC_XMSSMT_OID_SHA2_40_4_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/8_512",      WC_XMSSMT_OID_SHA2_40_8_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHA2_60/3_512",      WC_XMSSMT_OID_SHA2_60_3_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/6_512",      WC_XMSSMT_OID_SHA2_60_6_512     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/12_512",     WC_XMSSMT_OID_SHA2_60_12_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA512,   64, 64, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 512 */
+#endif /* WC_XMSS_SHA512 */
+
+#ifdef WC_XMSS_SHAKE128
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHAKE_20/2_256",     WC_XMSSMT_OID_SHAKE_20_2_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHAKE_20/4_256",     WC_XMSSMT_OID_SHAKE_20_4_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHAKE_40/2_256",     WC_XMSSMT_OID_SHAKE_40_2_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHAKE_40/4_256",     WC_XMSSMT_OID_SHAKE_40_4_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHAKE_40/8_256",     WC_XMSSMT_OID_SHAKE_40_8_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHAKE_60/3_256",     WC_XMSSMT_OID_SHAKE_60_3_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHAKE_60/6_256",     WC_XMSSMT_OID_SHAKE_60_6_256    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHAKE_60/12_256",    WC_XMSSMT_OID_SHAKE_60_12_256   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHAKE128 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHAKE_20/2_512",     WC_XMSSMT_OID_SHAKE_20_2_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHAKE_20/4_512",     WC_XMSSMT_OID_SHAKE_20_4_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHAKE_40/2_512",     WC_XMSSMT_OID_SHAKE_40_2_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHAKE_40/4_512",     WC_XMSSMT_OID_SHAKE_40_4_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHAKE_40/8_512",     WC_XMSSMT_OID_SHAKE_40_8_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHAKE_60/3_512",     WC_XMSSMT_OID_SHAKE_60_3_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHAKE_60/6_512",     WC_XMSSMT_OID_SHAKE_60_6_512    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHAKE_60/12_512",    WC_XMSSMT_OID_SHAKE_60_12_512   ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 512 */
+#endif /* WC_XMSS_SHAKE256 */
+
+#ifdef WC_XMSS_SHA256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHA2_20/2_192",      WC_XMSSMT_OID_SHA2_20_2_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHA2_20/4_192",      WC_XMSSMT_OID_SHA2_20_4_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHA2_40/2_192",      WC_XMSSMT_OID_SHA2_40_2_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/4_192",      WC_XMSSMT_OID_SHA2_40_4_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHA2_40/8_192",      WC_XMSSMT_OID_SHA2_40_8_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHA2_60/3_192",      WC_XMSSMT_OID_SHA2_60_3_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/6_192",      WC_XMSSMT_OID_SHA2_60_6_192     ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHA2_60/12_192",     WC_XMSSMT_OID_SHA2_60_12_192    ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHA256,   24,  4, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 192 */
+#endif /* WC_XMSS_SHA256 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHAKE256_20/2_256",  WC_XMSSMT_OID_SHAKE256_20_2_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHAKE256_20/4_256",  WC_XMSSMT_OID_SHAKE256_20_4_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHAKE256_40/2_256",  WC_XMSSMT_OID_SHAKE256_40_2_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHAKE256_40/4_256",  WC_XMSSMT_OID_SHAKE256_40_4_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHAKE256_40/8_256",  WC_XMSSMT_OID_SHAKE256_40_8_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHAKE256_60/3_256",  WC_XMSSMT_OID_SHAKE256_60_3_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHAKE256_60/6_256",  WC_XMSSMT_OID_SHAKE256_60_6_256 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHAKE256_60/12_256", WC_XMSSMT_OID_SHAKE256_60_12_256,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60, 12, 8, 0, 0), },
+#endif
+#endif /* HASH_SIZE 256 */
+#endif /* WC_XMSS_SHAKE256 */
+
+#ifdef WC_XMSS_SHAKE256
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    { "XMSSMT-SHAKE256_20/2_192",  WC_XMSSMT_OID_SHAKE256_20_2_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 20,  2, 3, 2, 4), },
+    { "XMSSMT-SHAKE256_20/4_192",  WC_XMSSMT_OID_SHAKE256_20_4_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 20,  4, 3, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40
+    { "XMSSMT-SHAKE256_40/2_192",  WC_XMSSMT_OID_SHAKE256_40_2_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 40,  2, 5, 2, 4), },
+    { "XMSSMT-SHAKE256_40/4_192",  WC_XMSSMT_OID_SHAKE256_40_4_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 40,  4, 5, 2, 4), },
+    { "XMSSMT-SHAKE256_40/8_192",  WC_XMSSMT_OID_SHAKE256_40_8_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 40,  8, 5, 0, 0), },
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60
+    { "XMSSMT-SHAKE256_60/3_192",  WC_XMSSMT_OID_SHAKE256_60_3_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 60,  3, 8, 2, 4), },
+    { "XMSSMT-SHAKE256_60/6_192",  WC_XMSSMT_OID_SHAKE256_60_6_192 ,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 60,  6, 8, 2, 4), },
+    { "XMSSMT-SHAKE256_60/12_192", WC_XMSSMT_OID_SHAKE256_60_12_192,
+      XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24,  4, 60, 12, 8, 0, 0), },
 #endif
+#endif /* HASH_SIZE 192 */
+#endif /* WC_XMSS_SHAKE256 */
+};
+/* Length of array of known XMSS^MT algorithms. */
+#define WC_XMSSMT_ALG_LEN   (sizeof(wc_xmssmt_alg) / sizeof(*wc_xmssmt_alg))
+#endif
+
+/* Convert XMSS^MT algorithm string to an OID - object identifier.
+ *
+ * @param [out] oid     OID value corresponding to string.
+ * @param [in]  s       String to convert.
+ * @param [out] params  XMSS/MT parameters.
+ * @return  0 on success.
+ * @return  NOT_COMPILED_IN on failure.
+ */
+static int wc_xmssmt_str_to_params(const char *s, word32* oid,
+    const XmssParams** params)
+{
+    int ret = NOT_COMPILED_IN;
+#if WOLFSSL_XMSS_MAX_HEIGHT >= 20
+    unsigned int i;
+
+    for (i = 0; i < WC_XMSSMT_ALG_LEN; i++) {
+         if (XSTRCMP(s, wc_xmssmt_alg[i].str) == 0) {
+             *oid = wc_xmssmt_alg[i].oid;
+             *params = &wc_xmssmt_alg[i].params;
+             ret = 0;
+             break;
+         }
+    }
+#else
+    (void)s;
+    (void)oid;
+    (void)params;
+#endif
+
+    return ret;
+}
+
+/***************************
+ * OTHER Internal APIs
+ ***************************/
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+/* Allocates the XMSS secret key (sk) array.
+ *
+ * The XMSS/XMSS^MT secret key length is a function of the
+ * parameters, and can't be allocated until the param string
+ * has been set with SetParamStr.
+ *
+ * This is only called by MakeKey() and Reload().
+ *
+ * Note: the XMSS sk array is force zeroed after every use.
+ *
+ * @param [in] key  The XMSS key.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_FUNC_ARG when private key already allocated.
+ * @return  MEMORY_E when allocating dynamic memory fails.
+ */
+static int wc_xmsskey_alloc_sk(XmssKey* key)
+{
+    int ret = 0;
+
+    /* Validate parameter. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Ensure the private key doesn't exist. */
+    else if (key->sk != NULL) {
+        WOLFSSL_MSG("error: XMSS secret key already exists");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* The XMSS/XMSS^MT secret key length is a function of the
+         * parameters. Therefore can't allocate this until param
+         * string has been set. */
+        ret = wc_XmssKey_GetPrivLen(key, &key->sk_len);
+    }
+    if (ret == 0) {
+        /* Allocate a buffer to hold secret key. */
+        key->sk = (unsigned char *)XMALLOC(key->sk_len, NULL,
+            DYNAMIC_TYPE_TMP_BUFFER);
+        if (key->sk == NULL) {
+            WOLFSSL_MSG("error: malloc XMSS key->sk failed");
+            ret = MEMORY_E;
+        }
+    }
+
+    if (ret == 0) {
+        /* Zeroize private key buffer. */
+        ForceZero(key->sk, key->sk_len);
+    }
+
+    return ret;
+}
+
+/* Signs the message using the XMSS secret key, and
+ * updates the secret key on NV storage.
+ *
+ * Both operations must succeed to be considered
+ * successful.
+ *
+ * On success:  sets key state to WC_XMSS_STATE_OK.
+ * On failure:  sets key state to WC_XMSS_STATE_BAD
+ *
+ * If no signatures are left, sets state to WC_XMSS_STATE_NOSIGS.
+ *
+ * @return  IO_FAILED_E when reading or writing private key failed.
+ * @return  KEY_EXHAUSTED_E when no more keys in private key available.
+ * @return  BAD_COND_E when generated signature length is invalid.
+ */
+static WC_INLINE int wc_xmsskey_signupdate(XmssKey* key, byte* sig,
+    const byte* msg, int msgLen)
+{
+    int            ret = 0;
+    enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE;
+
+    /* Set the key state to bad by default. State is presumed bad unless a
+     * correct sign and update operation happen together. */
+    key->state = WC_XMSS_STATE_BAD;
+
+    /* Read the current secret key from NV storage.*/
+    cb_rc = key->read_private_key(key->sk, key->sk_len, key->context);
+    if (cb_rc != WC_XMSS_RC_READ_TO_MEMORY) {
+        /* Read from NV storage failed. */
+        WOLFSSL_MSG("error: XMSS read_private_key failed");
+        ret = IO_FAILED_E;
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        XmssState* state;
+    #else
+        XmssState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize state for use in signing. */
+            ret = wc_xmss_state_init(state, key->params);
+            if (ret == 0) {
+                /* Read was good. Now sign and update the secret key in memory.
+                 */
+            #ifndef WOLFSSL_WC_XMSS_SMALL
+                if (key->is_xmssmt) {
+                    ret = wc_xmssmt_sign(state, msg, msgLen, key->sk, sig);
+                }
+                else {
+                    ret = wc_xmss_sign(state, msg, msgLen, key->sk, sig);
+                }
+            #else
+                ret = wc_xmssmt_sign(state, msg, msgLen, key->sk, sig);
+            #endif
+                if (ret == KEY_EXHAUSTED_E) {
+                    /* Signature space exhausted. */
+                    key->state = WC_XMSS_STATE_NOSIGS;
+                    WOLFSSL_MSG("error: no XMSS signatures remaining");
+                }
+                else if (ret != 0) {
+                    /* Something failed or inconsistent in signature. Erase the
+                     * signature just to be safe. */
+                    ForceZero(sig, key->params->sig_len);
+                    WOLFSSL_MSG("error: XMSS sign failed");
+                }
+                /* Free state after use. */
+                wc_xmss_state_free(state);
+            }
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+
+    if (ret == 0) {
+        /* The signature succeeded. key->sk is now updated and must be
+         * committed to NV storage. */
+        cb_rc = key->write_private_key(key->sk, key->sk_len, key->context);
+        if (cb_rc != WC_XMSS_RC_SAVED_TO_NV_MEMORY) {
+            /* Write to NV storage failed. Erase the signature from
+             * memory. */
+            ForceZero(sig, key->params->sig_len);
+            WOLFSSL_MSG("error: XMSS write_private_key failed");
+            ret = IO_FAILED_E;
+        }
+    }
+    if (ret == 0) {
+        /* key->sk was successfully committed to NV storage. Set the
+         * key state to OK, and set the sigLen. */
+        key->state = WC_XMSS_STATE_OK;
+    }
+
+    /* Force zero the secret key from memory always. */
+    ForceZero(key->sk, key->sk_len);
+
+    return ret;
+}
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+/***************************
+ * PUBLIC API
+ ***************************/
+
+/* Init an XMSS key.
+ *
+ * Call this before setting the parms of an XMSS key.
+ *
+ * @param [in] key    The XMSS key to init.
+ * @param [in] heap   Unused.
+ * @param [in] devId  Unused.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ */
+int wc_XmssKey_Init(XmssKey* key, void* heap, int devId)
+{
+    int ret = 0;
+
+    (void) heap;
+    (void) devId;
+
+    /* Validate parameters. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Zeroize key and set state to initialized. */
+        ForceZero(key, sizeof(XmssKey));
+        key->state = WC_XMSS_STATE_INITED;
+    }
+
+    return ret;
+}
+
+/* Set the XMSS key parameter string.
+ *
+ * The input string must be one of the supported parm set names in
+ * the "Name" section from the table in wolfssl/wolfcrypt/xmss.h,
+ * e.g. "XMSS-SHA2_10_256" or "XMSSMT-SHA2_20/4_256".
+ *
+ * @param [in] key  The XMSS key to set.
+ * @param [in] str  The XMSS/XMSS^MT parameter string.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_FUNC_ARG when string not recognized.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  NOT_COMPILED_IN when string not supported.
+ */
+int wc_XmssKey_SetParamStr(XmssKey* key, const char* str)
+{
+    int      ret = 0;
+    word32   oid = 0;
+    int      is_xmssmt = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (str == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_INITED)) {
+        WOLFSSL_MSG("error: XMSS key needs init");
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Check which type of algorithm the string is for. */
+        is_xmssmt = (XMEMCMP(str, "XMSS-", 5) != 0);
+
+        /* Convert XMSS param string to OID. */
+        if (is_xmssmt) {
+            ret = wc_xmssmt_str_to_params(str, &oid, &key->params);
+        }
+        else {
+            ret = wc_xmss_str_to_params(str, &oid, &key->params);
+        }
+        if (ret != 0) {
+            WOLFSSL_MSG("error: xmssmt_str_to_params failed");
+            ret = BAD_FUNC_ARG;
+        }
+    }
+
+    if (ret == 0) {
+        /* Set key info. */
+        key->oid = oid;
+        key->is_xmssmt = is_xmssmt;
+        key->state = WC_XMSS_STATE_PARMSET;
+    }
+
+    return ret;
+}
+
+/* Force zeros and frees the XMSS key from memory.
+ *
+ * This does not touch the private key saved to non-volatile storage.
+ *
+ * This is the only function that frees the key->sk array.
+ *
+ * @param [in] key  XMSS key.
+ */
+void wc_XmssKey_Free(XmssKey* key)
+{
+    /* Validate parameter. */
+    if (key != NULL) {
+    #ifndef WOLFSSL_XMSS_VERIFY_ONLY
+        if (key->sk != NULL) {
+            /* Zeroize private key. */
+            ForceZero(key->sk, key->sk_len);
+            XFREE(key->sk, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+            key->sk = NULL;
+            key->sk_len = 0;
+        }
+    #endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+        /* Ensure all data is zeroized. */
+        ForceZero(key, sizeof(XmssKey));
+
+        /* Set the state to freed. */
+        key->state = WC_XMSS_STATE_FREED;
+    }
+}
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+/* Sets the XMSS write private key callback.
+ *
+ * The callback must be able to write/update the private key to
+ * non-volatile storage.
+ *
+ * @param [in] key       The XMSS key.
+ * @param [in] write_cb  The write private key callback.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ */
+int wc_XmssKey_SetWriteCb(XmssKey* key, wc_xmss_write_private_key_cb write_cb)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (write_cb == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Changing the write callback of an already working key is forbidden. */
+    else if (key->state == WC_XMSS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_XmssKey_SetWriteCb: key in use");
+        ret = BAD_STATE_E;
+    }
+    else {
+        /* Set write callback for storing private key. */
+        key->write_private_key = write_cb;
+    }
+
+    return ret;
+}
+
+/* Sets the XMSS read private key callback.
+ *
+ * The callback must be able to read the private key from
+ * non-volatile storage.
+ *
+ * @param [in] key      The XMSS key.
+ * @param [in] read_cb  The read private key callback.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ */
+int wc_XmssKey_SetReadCb(XmssKey* key, wc_xmss_read_private_key_cb read_cb)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (read_cb == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Changing the read callback of an already working key is forbidden. */
+    else if (key->state == WC_XMSS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_XmssKey_SetReadCb: key in use");
+        ret = BAD_STATE_E;
+    }
+    else {
+        /* Set write callback for getting private key. */
+        key->read_private_key = read_cb;
+    }
+
+    return ret;
+}
+
+/* Sets the XMSS context to be used by write and read callbacks.
+ *
+ * E.g. this could be a filename if the callbacks write/read to file.
+ *
+ * @param [in] key      The XMSS key.
+ * @param [in] context  The context pointer.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ */
+int wc_XmssKey_SetContext(XmssKey* key, void* context)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (context == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Setting context of an already working key is forbidden. */
+    else if (key->state == WC_XMSS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_XmssKey_SetContext: key in use");
+        ret = BAD_STATE_E;
+    }
+    else {
+        /* Set read/write callback context for accessing the private key. */
+        key->context = context;
+    }
+
+    return ret;
+}
+
+/* Make the XMSS/XMSS^MT private/public key pair. The key must have its
+ * parameters set before calling this.
+ *
+ * Write/read callbacks, and context data, must be set prior.
+ * Key must have parameters set.
+ *
+ * This function and Reload() are the only functions that allocate
+ * key->sk array. wc_XmssKey_FreeKey is the only function that
+ * deallocates key->sk.
+ *
+ * @param [in] key  The XMSS key to make.
+ * @param [in] rng  Initialized WC_RNG pointer.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_FUNC_ARG when a write private key is not set.
+ * @return  BAD_FUNC_ARG when a read/write private key context is not set.
+ * @return  BAD_FUNC_ARG when private key already allocated.
+ * @return  MEMORY_E when allocating dynamic memory fails.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  IO_FAILED_E when writing private key failed.
+ * @return  Other negative when random number generation failed.
+ */
+int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng)
+{
+    int            ret = 0;
+    enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE;
+#ifdef WOLFSSL_SMALL_STACK
+    unsigned char* seed = NULL;
+#else
+    unsigned char  seed[3 * WC_XMSS_MAX_N];
+#endif
+
+    /* Validate parameters */
+    if ((key == NULL) || (rng == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) {
+        WOLFSSL_MSG("error: XmssKey not ready for generation");
+        ret = BAD_STATE_E;
+    }
+    /* Ensure write callback available. */
+    if ((ret == 0) && (key->write_private_key == NULL)) {
+        WOLFSSL_MSG("error: XmssKey write callback is not set");
+        ret = BAD_FUNC_ARG;
+    }
+    /* Ensure read/write callback context available. */
+    if ((ret == 0) && (key->context == NULL)) {
+        WOLFSSL_MSG("error: XmssKey context is not set");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Allocate sk array. */
+        ret = wc_xmsskey_alloc_sk(key);
+    }
+#ifdef WOLFSSL_SMALL_STACK
+    if (ret == 0) {
+        seed = (unsigned char*)XMALLOC(3 * key->params->n, NULL,
+            DYNAMIC_TYPE_TMP_BUFFER);
+        if (seed == NULL) {
+            ret = MEMORY_E;
+        }
+    }
+#endif
+
+    if (ret == 0) {
+        /* Generate three random seeds. */
+        ret = wc_RNG_GenerateBlock(rng, seed, 3 * key->params->n);
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        XmssState* state;
+    #else
+        XmssState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize state for use in key generation. */
+            ret = wc_xmss_state_init(state, key->params);
+            if (ret == 0) {
+                /* Finally make the private/public key pair. Immediately write
+                 * it to NV storage and then clear from memory. */
+            #ifndef WOLFSSL_WC_XMSS_SMALL
+                if (key->is_xmssmt) {
+                    ret = wc_xmssmt_keygen(state, seed, key->sk, key->pk);
+                }
+                else {
+                    ret = wc_xmss_keygen(state, seed, key->sk, key->pk);
+                }
+            #else
+                ret = wc_xmssmt_keygen(state, seed, key->sk, key->pk);
+            #endif
+                if (ret != 0) {
+                    WOLFSSL_MSG("error: XMSS keygen failed");
+                    key->state = WC_XMSS_STATE_BAD;
+                }
+                /* Free state after use. */
+                wc_xmss_state_free(state);
+            }
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+
+    if (ret == 0) {
+        /* Write out private key. */
+        cb_rc = key->write_private_key(key->sk, key->sk_len, key->context);
+        /* Zeroize private key data whether it was saved or not. */
+        ForceZero(key->sk, key->sk_len);
+        /* Check writing succeeded. */
+        if (cb_rc != WC_XMSS_RC_SAVED_TO_NV_MEMORY) {
+            WOLFSSL_MSG("error: XMSS write to NV storage failed");
+            key->state = WC_XMSS_STATE_BAD;
+            ret = IO_FAILED_E;
+        }
+    }
+
+    if (ret == 0) {
+        key->state = WC_XMSS_STATE_OK;
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(seed, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+    return ret;
+}
+
+/* This function allocates the secret key buffer, and does a
+ * quick sanity check to verify the secret key is readable
+ * from NV storage, and then force zeros the key from memory.
+ *
+ * On success it sets the key state to OK.
+ *
+ * Use this function to resume signing with an already existing
+ * XMSS key pair.
+ *
+ * Write/read callbacks, and context data, must be set prior.
+ * Key must have parameters set.
+ *
+ * This function and MakeKey are the only functions that allocate
+ * key->sk array. wc_XmssKey_FreeKey is the only function that
+ * deallocates key->sk.
+ *
+ * @params [in] key  XMSS key to load.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_FUNC_ARG when a read or write function is not set.
+ * @return  BAD_FUNC_ARG when a read/write function context is not set.
+ * @return  BAD_FUNC_ARG when private key already allocated.
+ * @return  MEMORY_E when allocating dynamic memory fails.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  IO_FAILED_E when reading private key failed.
+ */
+int wc_XmssKey_Reload(XmssKey* key)
+{
+    int            ret = 0;
+    enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE;
+
+    /* Validate parameter. */
+    if (key == NULL) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) {
+        WOLFSSL_MSG("error: XmssKey not ready for reload");
+        ret = BAD_STATE_E;
+    }
+    /* Ensure read and write callbacks are available. */
+    if ((ret == 0) && ((key->write_private_key == NULL) ||
+            (key->read_private_key == NULL))) {
+        WOLFSSL_MSG("error: XmssKey write/read callbacks are not set");
+        ret = BAD_FUNC_ARG;
+    }
+    /* Ensure read and write callback context is available. */
+    if ((ret == 0) && (key->context == NULL)) {
+        WOLFSSL_MSG("error: XmssKey context is not set");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Allocate sk array. */
+        ret = wc_xmsskey_alloc_sk(key);
+    }
+
+    if (ret == 0) {
+        /* Read the current secret key from NV storage. Force clear it
+         * immediately. This is just to sanity check the secret key
+         * is readable from permanent storage. */
+        cb_rc = key->read_private_key(key->sk, key->sk_len, key->context);
+        ForceZero(key->sk, key->sk_len);
+        /* Check reading succeeded. */
+        if (cb_rc != WC_XMSS_RC_READ_TO_MEMORY) {
+            WOLFSSL_MSG("error: XMSS read from NV storage failed");
+            key->state = WC_XMSS_STATE_BAD;
+            ret = IO_FAILED_E;
+        }
+    }
+    if (ret == 0) {
+        key->state = WC_XMSS_STATE_OK;
+    }
+
+    return ret;
+}
+
+/* Gets the XMSS/XMSS^MT private key length.
+ *
+ * Parameters must be set before calling this, as the key size (sk_len)
+ * is a function of the parameters.
+ *
+ * Note: the XMSS/XMSS^MT private key format is implementation specific,
+ * and not standardized. Interoperability of XMSS private keys should
+ * not be expected.
+ *
+ * @param [in]  key  XMSS key.
+ * @param [out] len  Length of the private key in bytes.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * */
+int wc_XmssKey_GetPrivLen(const XmssKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (len == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && ((key->state != WC_XMSS_STATE_OK) &&
+            (key->state != WC_XMSS_STATE_PARMSET))) {
+        /* params->sk_len not set yet. */
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Calculate private key length: OID + private key bytes. */
+        *len = XMSS_OID_LEN + (word32)key->params->sk_len;
+    }
+
+    return ret;
+}
+
+/* Sign the message using the XMSS secret key.
+ *
+ * @param [in]      key     XMSS key to use to sign.
+ * @param [in]      sig     Buffer to write signature into.
+ * @param [in, out] sigLen  On in, size of buffer.
+ *                          On out, the length of the signature in bytes.
+ * @param [in]      msg     Message to sign.
+ * @param [in]      msgLen  Length of the message in bytes.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_FUNC_ARG when a write private key is not set.
+ * @return  BAD_FUNC_ARG when a read/write private key context is not set.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  BUFFER_E when sigLen is too small.
+ * @return  IO_FAILED_E when reading or writing private key failed.
+ * @return  KEY_EXHAUSTED_E when no more keys in private key available.
+ * @return  BAD_COND_E when generated signature length is invalid.
+ */
+int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg,
+    int msgLen)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (sig == NULL) || (sigLen == NULL) || (msg == NULL) ||
+            (msgLen <= 0)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state == WC_XMSS_STATE_NOSIGS)) {
+        WOLFSSL_MSG("error: XMSS signatures exhausted");
+        ret = BAD_STATE_E;
+    }
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_OK)) {
+       /* The key had an error the last time it was used, and we
+        * can't guarantee its state. */
+        WOLFSSL_MSG("error: can't sign, XMSS key not in good state");
+        ret = BAD_STATE_E;
+    }
+    /* Check signature buffer size. */
+    if ((ret == 0) && (*sigLen < key->params->sig_len)) {
+        /* Signature buffer too small. */
+        WOLFSSL_MSG("error: XMSS sig buffer too small");
+        ret = BUFFER_E;
+    }
+    /* Check read and write callbacks available. */
+    if ((ret == 0) && ((key->write_private_key == NULL) ||
+            (key->read_private_key == NULL))) {
+        WOLFSSL_MSG("error: XmssKey write/read callbacks are not set");
+        ret = BAD_FUNC_ARG;
+    }
+    /* Check read/write callback context available. */
+    if ((ret == 0) && (key->context == NULL)) {
+        WOLFSSL_MSG("error: XmssKey context is not set");
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        *sigLen = key->params->sig_len;
+        /* Finally, sign and update the secret key. */
+        ret = wc_xmsskey_signupdate(key, sig, msg, msgLen);
+    }
+
+    return ret;
+}
+
+/* Check if more signatures are possible with key.
+ *
+ * @param [in] key  XMSS key to check.
+ * @return  1 when signatures possible.
+ * @return  0 when key exhausted.
+ */
+int  wc_XmssKey_SigsLeft(XmssKey* key)
+{
+    int ret;
+
+    /* Validate parameter. */
+    if (key == NULL) {
+        ret = 0;
+    }
+    /* Validate state. */
+    else if (key->state == WC_XMSS_STATE_NOSIGS) {
+        WOLFSSL_MSG("error: XMSS signatures exhausted");
+        ret = 0;
+    }
+    else if (key->state != WC_XMSS_STATE_OK) {
+        WOLFSSL_MSG("error: can't sign, XMSS key not in good state");
+        ret = 0;
+    }
+    /* Read the current secret key from NV storage.*/
+    else if (key->read_private_key(key->sk, key->sk_len, key->context) !=
+             WC_XMSS_RC_READ_TO_MEMORY) {
+        WOLFSSL_MSG("error: XMSS read_private_key failed");
+        ret = 0;
+    }
+    else {
+        /* Ask implementation to check index in private key. */
+        ret = wc_xmss_sigsleft(key->params, key->sk);
+    }
+
+    return ret;
+}
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY*/
+
+/* Get the XMSS/XMSS^MT public key length.
+ *
+ * The public key is static in size and does not depend on parameters,
+ * other than the choice of SHA256 as hashing function.
+ *
+ * @param [in]  key  XMSS key.
+ * @param [out] len  Length of the public key.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  NOT_COMPILED_IN when a hash algorithm not supported.
+ */
+int wc_XmssKey_GetPubLen(const XmssKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (len == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    else {
+        *len = XMSS_OID_LEN + key->params->pk_len;
+    }
+
+    return ret;
+}
+
+/* Export public key and parameters from one XmssKey to another.
+ *
+ * Use this to prepare a signature verification XmssKey that is pub only.
+ *
+ * @param [out] keyDst  Destination key for copy.
+ * @param [in]  keySrc  Source key for copy.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a key is NULL.
+ * @return  Other negative when digest algorithm initialization failed.
+ */
+int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((keyDst == NULL) || (keySrc == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    if (ret == 0) {
+        /* Zeroize the new key. */
+        ForceZero(keyDst, sizeof(XmssKey));
+
+        /* Copy over the public key. */
+        XMEMCPY(keyDst->pk, keySrc->pk, sizeof(keySrc->pk));
+
+        /* Copy over the key info. */
+        keyDst->oid = keySrc->oid;
+        keyDst->is_xmssmt = keySrc->is_xmssmt;
+        keyDst->params = keySrc->params;
+    }
+    if (ret == 0) {
+        /* Mark keyDst as verify only, to prevent misuse. */
+        keyDst->state = WC_XMSS_STATE_VERIFYONLY;
+    }
+
+    return 0;
+}
+
+/* Exports the raw XMSS public key buffer from key to out buffer.
+ *
+ * The out buffer should be large enough to hold the public key, and
+ * outLen should indicate the size of the buffer.
+ *
+ * @param [in]       key     XMSS key.
+ * @param [out]      out     Array holding public key.
+ * @param [in, out]  outLen  On in, size of buffer.
+ *                           On out, the length of the public key.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BUFFER_E if array is too small.
+ */
+int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out, word32* outLen)
+{
+    int    ret = 0;
+    word32 pubLen = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (out == NULL) || (outLen == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+
+    /* Get the public key length. */
+    if (ret == 0) {
+        ret = wc_XmssKey_GetPubLen(key, &pubLen);
+    }
+    /* Check the output buffer is large enough. */
+    if ((ret == 0) && (*outLen < pubLen)) {
+        ret = BUFFER_E;
+    }
+
+    if (ret == 0) {
+        int i = 0;
+        /* First copy the oid into buffer. */
+        for (; i < XMSS_OID_LEN; i++) {
+            out[XMSS_OID_LEN - i - 1] = (key->oid >> (8 * i)) & 0xFF;
+        }
+        /* Copy the public key data into buffer after oid. */
+        XMEMCPY(out + XMSS_OID_LEN, key->pk, pubLen - XMSS_OID_LEN);
+        /* Return actual public key length. */
+        *outLen = pubLen;
+    }
+
+    return ret;
+}
+
+/* Imports a raw public key buffer from in array to XmssKey key.
+ *
+ * The XMSS parameters must be set first with wc_XmssKey_SetParamStr,
+ * and inLen must match the length returned by wc_XmssKey_GetPubLen.
+ *
+ * @param [in, out] key     XMSS key.
+ * @param [in]      in      Array holding public key.
+ * @param [in]       inLen  Length of array in bytes.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BUFFER_E if array is incorrect size.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * */
+int wc_XmssKey_ImportPubRaw(XmssKey* key, const byte* in, word32 inLen)
+{
+    int    ret = 0;
+    word32 pubLen = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (in == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) {
+        /* XMSS key not ready for import. Param str must be set first. */
+        WOLFSSL_MSG("error: XMSS key not ready for import");
+        ret = BAD_STATE_E;
+    }
+
+    /* Get the public key length. */
+    if (ret == 0) {
+        ret = wc_XmssKey_GetPubLen(key, &pubLen);
+    }
+    /* Check the input buffer is the right size. */
+    if ((ret == 0) && (inLen != pubLen)) {
+        /* Something inconsistent. Parameters weren't set, or input
+         * pub key is wrong.*/
+        ret = BUFFER_E;
+    }
+
+    if (ret == 0) {
+        /* Copy the public key data into key. */
+        XMEMCPY(key->pk, in + XMSS_OID_LEN, pubLen - XMSS_OID_LEN);
+
+        /* Update state to verify-only as we don't have a private key. */
+        key->state = WC_XMSS_STATE_VERIFYONLY;
+    }
+
+    return ret;
+}
+
+/* Gets the XMSS/XMSS^MT signature length.
+ *
+ * Parameters must be set before calling this, as the signature size
+ * is a function of the parameters.
+ *
+ * Note: call this before wc_XmssKey_Sign or Verify so you know the
+ * length of the required signature buffer.
+ *
+ * @param [in]  key  XMSS key to use to sign.
+ * @param [out] len  The length of the signature in bytes.
+ *
+ * @return  0 on success.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * */
+int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len)
+{
+    int ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (len == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) &&
+            (key->state != WC_XMSS_STATE_PARMSET)) {
+        ret = BAD_STATE_E;
+    }
+
+    if (ret == 0) {
+        /* Return the calculated signature length. */
+        *len = key->params->sig_len;
+    }
+
+    return ret;
+}
+
+/* Verify the signature using the XMSS public key.
+ *
+ * Requires that XMSS parameters have been set with
+ * wc_XmssKey_SetParamStr, and that a public key is available
+ * from importing or MakeKey().
+ *
+ * Call wc_XmssKey_GetSigLen() before this function to determine
+ * length of the signature buffer.
+ *
+ * @param [in] key     XMSS key to use to verify.
+ * @param [in] sig     Signature to verify.
+ * @param [in] sigLen  Size of signature in bytes.
+ * @param [in] m       Message to verify.
+ * @param [in] mLen    Length of the message in bytes.
+ *
+ * @return  0 on success.
+ * @return  SIG_VERIFY_E when signature did not verify message.
+ * @return  BAD_FUNC_ARG when a parameter is NULL.
+ * @return  BAD_STATE_E when wrong state for operation.
+ * @return  BUFFER_E when sigLen is too small.
+ */
+int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen,
+    const byte* m, int mLen)
+{
+    int            ret = 0;
+
+    /* Validate parameters. */
+    if ((key == NULL) || (sig == NULL) || (m == NULL)) {
+        ret = BAD_FUNC_ARG;
+    }
+    /* Validate state. */
+    if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) &&
+            (key->state != WC_XMSS_STATE_VERIFYONLY)) {
+        /* XMSS key not ready for verification. Param str must be
+         * set first, and Reload() called. */
+        WOLFSSL_MSG("error: XMSS key not ready for verification");
+        ret = BAD_STATE_E;
+    }
+    /* Check the signature is the big enough. */
+    if ((ret == 0) && (sigLen < key->params->sig_len)) {
+        /* Signature buffer too small. */
+        ret = BUFFER_E;
+    }
+
+    if (ret == 0) {
+    #ifdef WOLFSSL_SMALL_STACK
+        XmssState* state;
+    #else
+        XmssState state[1];
+    #endif
+
+    #ifdef WOLFSSL_SMALL_STACK
+        state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (state == NULL) {
+            ret = MEMORY_E;
+        }
+        if (ret == 0)
+    #endif
+        {
+            /* Initialize state for use in verification. */
+            ret = wc_xmss_state_init(state, key->params);
+            if (ret == 0) {
+                /* Verify using either XMSS^MT function as it works for both. */
+                ret = wc_xmssmt_verify(state, m, mLen, sig, key->pk);
+                /* Free state after use. */
+                wc_xmss_state_free(state);
+            }
+        #ifdef WOLFSSL_SMALL_STACK
+            XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        #endif
+        }
+    }
+
+    return ret;
+}
+
+#endif /* WOLFSSL_HAVE_XMSS */

+ 4314 - 2
wolfcrypt/src/wc_xmss_impl.c

@@ -19,8 +19,4320 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
+/* Based on:
+ *  o RFC 8391 - XMSS: eXtended Merkle Signature Scheme
+ *  o [HDSS] "Hash-based Digital Signature Schemes", Buchmann, Dahmen and Szydlo
+ *    from "Post Quantum Cryptography", Springer 2009.
+ *  o [OPX] "Optimal Parameters for XMSS^MT", Hulsing, Rausch and Buchmann
+ *
+ * TODO: "Simple and Memory-efficient Signature Generation of XMSS^MT"
+ *       (https://ece.engr.uvic.ca/~raltawy/SAC2021/9.pdf)
+ */
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
 #include <wolfssl/wolfcrypt/settings.h>
+#include <wolfssl/wolfcrypt/error-crypt.h>
+#include <wolfssl/wolfcrypt/logging.h>
+
+#include <wolfssl/wolfcrypt/wc_xmss.h>
+#include <wolfssl/wolfcrypt/hash.h>
+
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+#if defined(WOLFSSL_HAVE_XMSS)
+
+/* Indices into Hash Address. */
+#define XMSS_ADDR_LAYER                 0
+#define XMSS_ADDR_TREE_HI               1
+#define XMSS_ADDR_TREE                  2
+#define XMSS_ADDR_TYPE                  3
+#define XMSS_ADDR_OTS                   4
+#define XMSS_ADDR_LTREE                 4
+#define XMSS_ADDR_TREE_ZERO             4
+#define XMSS_ADDR_CHAIN                 5
+#define XMSS_ADDR_TREE_HEIGHT           5
+#define XMSS_ADDR_HASH                  6
+#define XMSS_ADDR_TREE_INDEX            6
+#define XMSS_ADDR_KEY_MASK              7
+
+/* Types of hash addresses. */
+#define WC_XMSS_ADDR_TYPE_OTS        0
+#define WC_XMSS_ADDR_TYPE_LTREE      1
+#define WC_XMSS_ADDR_TYPE_TREE       2
+
+/* Byte to include in hash to create unique sequence. */
+#define XMSS_HASH_PADDING_F             0
+#define XMSS_HASH_PADDING_H             1
+#define XMSS_HASH_PADDING_HASH          2
+#define XMSS_HASH_PADDING_PRF           3
+#define XMSS_HASH_PADDING_PRF_KEYGEN    4
+
+/* Fixed parameter values. */
+#define XMSS_WOTS_W                     16
+#define XMSS_WOTS_LOG_W                 4
+#define XMSS_WOTS_LEN2                  3
+#define XMSS_CSUM_SHIFT                 4
+#define XMSS_CSUM_LEN                   2
+
+/* Length of the message to the PRF. */
+#define XMSS_PRF_M_LEN                  32
+
+/* Length of index encoding when doing XMSS. */
+#define XMSS_IDX_LEN                    4
+
+/* Size of the N when using SHA-256 and 32 byte padding. */
+#define XMSS_SHA256_32_N                WC_SHA256_DIGEST_SIZE
+/* Size of the padding when using SHA-256 and 32 byte padding. */
+#define XMSS_SHA256_32_PAD_LEN          32
+
+/* Calculate PRF data length for parameters. */
+#define XMSS_HASH_PRF_DATA_LEN(params)                              \
+    ((params)->pad_len + (params)->n + WC_XMSS_ADDR_LEN)
+/* PRF data length when using SHA-256 with 32 byte padding. */
+#define XMSS_HASH_PRF_DATA_LEN_SHA256_32                            \
+    (XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN)
+
+/* Calculate chain hash data length for parameters. */
+#define XMSS_CHAIN_HASH_DATA_LEN(params)                            \
+    ((params)->pad_len + 2 * (params)->n)
+/* Chain hash data length when using SHA-256 with 32 byte padding. */
+#define XMSS_CHAIN_HASH_DATA_LEN_SHA256_32                          \
+    (XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N)
+
+/* Calculate rand hash data length for parameters. */
+#define XMSS_RAND_HASH_DATA_LEN(params)                             \
+    ((params)->pad_len + 3 * (params)->n)
+/* Rand hash data length when using SHA-256 with 32 byte padding. */
+#define XMSS_RAND_HASH_DATA_LEN_SHA256_32                           \
+    (XMSS_SHA256_32_PAD_LEN + 3 * XMSS_SHA256_32_N)
+
+/* Encode pad value into byte array. Front fill with 0s.
+ *
+ * RFC 8391: 2.4
+ *
+ * @param [in]  n   Number to encode.
+ * @param [out] a   Array to hold encoding.
+ * @param [in]  l   Length of array.
+ */
+#define XMSS_PAD_ENC(n, a, l)   \
+do {                            \
+    XMEMSET(a, 0, l);           \
+    (a)[(l) - 1] = (n);         \
+} while (0)
+
+
+/********************************************
+ * Index 32/64 bits
+ ********************************************/
+
+/* Index of 32 or 64 bits. */
+typedef union wc_Idx {
+#if WOLFSSL_XMSS_MAX_HEIGHT > 32
+    /* 64-bit representation. */
+    w64wrapper u64;
+#endif
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
+    /* 32-bit representation. */
+    word32     u32;
+#endif
+} wc_Idx;
+
+#if WOLFSSL_XMSS_MAX_HEIGHT > 32
+/* Set index to zero.
+ *
+ * Index is up to 64-bits.
+ *
+ * @param [out] idx  32/64-bit index to zero.
+ */
+#define WC_IDX_ZERO(idx)    w64Zero(&(idx).u64)
+#else
+/* Set index to zero.
+ *
+ * Index is no more than 32-bits.
+ *
+ * @param [out] idx  32/64-bit index to zero.
+ */
+#define WC_IDX_ZERO(idx)    idx.u32 = 0
+#endif
+
+#if WOLFSSL_XMSS_MAX_HEIGHT > 32
+/* Decode 64-bit index.
+ *
+ * @param [out] i    Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define IDX64_DECODE(i, c, a, ret)                      \
+    if ((c) == 5) {                                     \
+        word32 t;                                       \
+        ato32((a) + 1, &t);                             \
+        (i) = w64From32((a)[0], t);                     \
+    }                                                   \
+    else if ((c) == 8) {                                \
+        ato64(a, &(i));                                 \
+    }
+
+/* Decode 64-bit index.
+ *
+ * @param [out] i    Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define XMSS_IDX64_DECODE(i, c, a, ret)                 \
+do {                                                    \
+    IDX64_DECODE(i, c, a, ret)                          \
+    else {                                              \
+        (ret) = NOT_COMPILED_IN;                        \
+    }                                                   \
+} while (0)
+
+/* Check whether index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] c  Count of bytes i was encoded in.
+ * @param [in] h  Full tree Height.
+ */
+#define IDX64_INVALID(i, c, h)                              \
+    ((w64GetHigh32(w64Add32(i, 1, NULL)) >> ((h) - 32)) != 0)
+
+/* Set 64-bit index as hash address value for tree.
+ *
+ * @param [in]  i  Index to set.
+ * @param [in]  c  Count of bytes to encode into.
+ * @param [in]  h  Height of tree.
+ * @param [out] a  Hash address to encode into.
+ * @param [out] l  Index of leaf.
+ */
+#define IDX64_SET_ADDR_TREE(i, c, h, a, l)              \
+    if ((c) > 4) {                                      \
+        (l) = w64GetLow32(i) & (((word32)1 << (h)) - 1);\
+        (i) = w64ShiftRight(i, h);                      \
+        (a)[XMSS_ADDR_TREE_HI] = w64GetHigh32(i);       \
+        (a)[XMSS_ADDR_TREE] = w64GetLow32(i);           \
+    }
+#endif /* WOLFSSL_XMSS_MAX_HEIGHT > 32 */
+
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
+/* Decode 32-bit index.
+ *
+ * @param [out] i    Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define IDX32_DECODE(i, c, a, ret)                      \
+    if ((c) == 4) {                                     \
+        ato32(a, &(i));                                 \
+    }                                                   \
+    else if ((c) == 3) {                                \
+        ato24(a, &(i));                                 \
+    }
+
+/* Decode 32-bit index.
+ *
+ * @param [out] i    Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define XMSS_IDX32_DECODE(i, c, a, ret)                 \
+do {                                                    \
+    IDX32_DECODE(i, c, a, ret)                          \
+    else {                                              \
+        (ret) = NOT_COMPILED_IN;                        \
+    }                                                   \
+} while (0)
+
+/* Check whether 32-bit index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] c  Count of bytes i was encoded in.
+ * @param [in] h  Full tree Height.
+ */
+#define IDX32_INVALID(i, c, h)                          \
+    ((((i) + 1) >> (h)) != 0)
+
+/* Set 32-bit index as hash address value for tree.
+ *
+ * @param [in]  i  Index to set.
+ * @param [in]  c  Count of bytes to encode into.
+ * @param [in]  h  Height of tree.
+ * @param [out] a  Hash address to encode into.
+ * @param [out] l  Index of leaf.
+ */
+#define IDX32_SET_ADDR_TREE(i, c, h, a, l)              \
+    if ((c) <= 4) {                                     \
+        (l) = ((i) & ((1 << (h)) - 1));                 \
+        (i) >>= params->sub_h;                          \
+        (a)[XMSS_ADDR_TREE] = (i);                      \
+    }
+
+#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */
+
+#if (WOLFSSL_XMSS_MAX_HEIGHT > 32) && (WOLFSSL_XMSS_MIN_HEIGHT <= 32)
+
+/* Decode 32/64-bit index.
+ *
+ * @param [out] idx  Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define WC_IDX_DECODE(idx, c, a, ret)                   \
+do {                                                    \
+    IDX64_DECODE((idx).u64, c, a, ret)                  \
+    else                                                \
+    IDX32_DECODE((idx).u32, c, a, ret)                  \
+    else {                                              \
+        (ret) = NOT_COMPILED_IN;                        \
+    }                                                   \
+} while (0)
+
+/* Check whether index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] c  Count of bytes i was encoded in.
+ * @param [in] h  Full tree Height.
+ */
+#define WC_IDX_INVALID(i, c, h)                         \
+    ((((c) >  4) && IDX64_INVALID((i).u64, c, h)) ||    \
+     (((c) <= 4) && IDX32_INVALID((i).u32, c, h)))
+
+/* Set 32/64-bit index as hash address value for tree.
+ *
+ * @param [in]  i  Index to set.
+ * @param [in]  c  Count of bytes to encode into.
+ * @param [in]  h  Height of tree.
+ * @param [out] a  Hash address to encode into.
+ * @param [out] l  Index of leaf.
+ */
+#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l)           \
+do {                                                    \
+    IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l)          \
+    else                                                \
+    IDX32_SET_ADDR_TREE((idx).u32, c, h, a, l)          \
+} while (0)
+
+#elif WOLFSSL_XMSS_MAX_HEIGHT > 32
+
+/* Decode 64-bit index.
+ *
+ * @param [out] idx  Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define WC_IDX_DECODE(idx, c, a, ret)                   \
+do {                                                    \
+    IDX64_DECODE((idx).u64, c, a, ret)                  \
+} while (0)
+
+/* Check whether index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] c  Count of bytes i was encoded in.
+ * @param [in] h  Full tree Height.
+ */
+#define WC_IDX_INVALID(i, c, h)                         \
+    IDX64_INVALID((i).u64, c, h)
+
+/* Set 64-bit index as hash address value for tree.
+ *
+ * @param [in]  i  Index to set.
+ * @param [in]  c  Count of bytes to encode into.
+ * @param [in]  h  Height of tree.
+ * @param [out] a  Hash address to encode into.
+ * @param [out] l  Index of leaf.
+ */
+#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l)           \
+do {                                                    \
+    IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l)          \
+} while (0)
+
+#else
+
+/* Decode 32-bit index.
+ *
+ * @param [out] idx  Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ * @param [out] ret  Return value.
+ */
+#define WC_IDX_DECODE(idx, c, a, ret)                   \
+do {                                                    \
+    IDX32_DECODE((idx).u32, c, a, ret)                  \
+    else {                                              \
+        (ret) = NOT_COMPILED_IN;                        \
+    }                                                   \
+} while (0)
+
+/* Check whether index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] c  Count of bytes i was encoded in.
+ * @param [in] h  Full tree Height.
+ */
+#define WC_IDX_INVALID(i, c, h)                         \
+    IDX32_INVALID((i).u32, c, h)
+
+/* Set 32-bit index as hash address value for tree.
+ *
+ * @param [in]  i  Index to set.
+ * @param [in]  c  Count of bytes to encode into.
+ * @param [in]  h  Height of tree.
+ * @param [out] a  Hash address to encode into.
+ * @param [out] l  Index of leaf.
+ */
+#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l)           \
+do {                                                    \
+    IDX32_SET_ADDR_TREE(idx.u32, c, h, a, l)            \
+} while (0)
+
+#endif /* (WOLFSSL_XMSS_MAX_HEIGHT > 32) && (WOLFSSL_XMSS_MIN_HEIGHT <= 32) */
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+/* Update index by adding one to big-endian encoded value.
+ *
+ * @param [in, out] a  Array index is encoded in.
+ * @param [in]      l  Length of encoded index.
+ */
+static void wc_idx_update(unsigned char* a, word8 l)
+{
+    sword8 i;
+
+    for (i = l - 1; i >= 0; i--) {
+        if ((++a[i]) != 0) {
+            break;
+        }
+    }
+}
+
+/* Copy index from source buffer to destination buffer.
+ *
+ * Index is put into the front of the destination buffer with the length of the
+ * source.
+ *
+ * @param [in]      s   Source buffer.
+ * @param [in]      sl  Length of index in source.
+ * @param [in, out] d   Destination buffer.
+ * @param [in]      dl  Length of destination buffer.
+ */
+static void wc_idx_copy(const unsigned char* s, word8 sl, unsigned char* d,
+    word8 dl)
+{
+    XMEMCPY(d, s, sl);
+    XMEMSET(d + sl, 0, dl - sl);
+}
+#endif
+
+/********************************************
+ * Hash Address.
+ ********************************************/
+
+/* Set the hash address based on subtree.
+ *
+ * @param [out] a  Hash address.
+ * @param [in]  s  Subtree hash address.
+ * @param [in]  t  Type of hash address.
+ */
+#define XMSS_ADDR_SET_SUBTREE(a, s, t)                \
+do {                                                  \
+    (a)[XMSS_ADDR_LAYER]   = (s)[XMSS_ADDR_LAYER];    \
+    (a)[XMSS_ADDR_TREE_HI] = (s)[XMSS_ADDR_TREE_HI];  \
+    (a)[XMSS_ADDR_TREE]    = (s)[XMSS_ADDR_TREE];     \
+    (a)[XMSS_ADDR_TYPE]    = (t);                     \
+    XMEMSET((a) + 4, 0, sizeof(a) - 4 * sizeof(*(a)));\
+} while (0)
+
+/* Set the OTS hash address based on subtree.
+ *
+ * @param [out] a  Hash address.
+ * @param [in]  s  Subtree hash address.
+ */
+#define XMSS_ADDR_OTS_SET_SUBTREE(a, s) \
+    XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_OTS)
+/* Set the L-tree address based on subtree.
+ *
+ * @param [out] a  Hash address.
+ * @param [in]  s  Subtree hash address.
+ */
+#define XMSS_ADDR_LTREE_SET_SUBTREE(a, s) \
+    XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_LTREE)
+/* Set the hash tree address based on subtree.
+ *
+ * @param [out] a  Hash address.
+ * @param [in]  s  Subtree hash address.
+ */
+#define XMSS_ADDR_TREE_SET_SUBTREE(a, s) \
+    XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_TREE)
+
+#ifdef LITTLE_ENDIAN_ORDER
+
+/* Set a byte value into a word of an encoded address.
+ *
+ * @param [in, out] a  Encoded hash address.
+ * @param [in]      i  Index of word.
+ * @param [in]      b  Byte to set.
+ */
+#define XMSS_ADDR_SET_BYTE(a, i, b)     \
+    ((word32*)(a))[i] = (word32)(b) << 24
+
+#else
+
+/* Set a byte value into a word of an encoded address.
+ *
+ * @param [in, out] a  Encoded hash address.
+ * @param [in]      i  Index of word.
+ * @param [in]      b  Byte to set.
+ */
+#define XMSS_ADDR_SET_BYTE(a, i, b)     \
+    ((word32*)(a))[i] = (b)
+
+#endif /* LITTLE_ENDIAN_ORDER */
+
+/* Convert hash address to bytes.
+ *
+ * @param [out] bytes  Array to encode into.
+ * @param [in]  addr   Hash address.
+ */
+static void wc_xmss_addr_encode(const HashAddress addr, byte* bytes)
+{
+    c32toa((addr)[0], (bytes) + (0 * 4));
+    c32toa((addr)[1], (bytes) + (1 * 4));
+    c32toa((addr)[2], (bytes) + (2 * 4));
+    c32toa((addr)[3], (bytes) + (3 * 4));
+    c32toa((addr)[4], (bytes) + (4 * 4));
+    c32toa((addr)[5], (bytes) + (5 * 4));
+    c32toa((addr)[6], (bytes) + (6 * 4));
+    c32toa((addr)[7], (bytes) + (7 * 4));
+}
+
+/********************************************
+ * HASHING
+ ********************************************/
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
+    !defined(WC_XMSS_FULL_HASH)
+
+/* Set hash data and length into SHA-256 digest.
+ *
+ * @param [in, out] state      XMSS/MT state with SHA-256 digest.
+ * @param [in]      data       Data to add to hash.
+ * @param [in]      len        Number of bytes in data.
+ *                             Must be less than a block.
+ * @param [in]      total_len  Number of bytes updated so far.
+ */
+#define XMSS_SHA256_SET_DATA(state, data, len, total_len)           \
+do {                                                                \
+    XMEMCPY((state)->digest.sha256.buffer, data, len);              \
+    (state)->digest.sha256.buffLen = (len);                         \
+    (state)->digest.sha256.loLen = (total_len);                     \
+} while (0)
+
+/* Save the SHA-256 state to cache.
+ *
+ * @param [in, out] state  XMSS/MT state with SHA-256 digest and state cache.
+ */
+#define XMSS_SHA256_STATE_CACHE(state)                              \
+    (state)->dgst_state[0] = (state)->digest.sha256.digest[0];      \
+    (state)->dgst_state[1] = (state)->digest.sha256.digest[1];      \
+    (state)->dgst_state[2] = (state)->digest.sha256.digest[2];      \
+    (state)->dgst_state[3] = (state)->digest.sha256.digest[3];      \
+    (state)->dgst_state[4] = (state)->digest.sha256.digest[4];      \
+    (state)->dgst_state[5] = (state)->digest.sha256.digest[5];      \
+    (state)->dgst_state[6] = (state)->digest.sha256.digest[6];      \
+    (state)->dgst_state[7] = (state)->digest.sha256.digest[7];      \
+
+/* Restore the SHA-256 state from cache and set length.
+ *
+ * @param [in, out] state  XMSS/MT state with SHA-256 digest and state cache.
+ * @param [in]      len    Number of bytes of data hashed so far.
+ */
+#define XMSS_SHA256_STATE_RESTORE(state, len)                       \
+do {                                                                \
+    (state)->digest.sha256.digest[0] = (state)->dgst_state[0];      \
+    (state)->digest.sha256.digest[1] = (state)->dgst_state[1];      \
+    (state)->digest.sha256.digest[2] = (state)->dgst_state[2];      \
+    (state)->digest.sha256.digest[3] = (state)->dgst_state[3];      \
+    (state)->digest.sha256.digest[4] = (state)->dgst_state[4];      \
+    (state)->digest.sha256.digest[5] = (state)->dgst_state[5];      \
+    (state)->digest.sha256.digest[6] = (state)->dgst_state[6];      \
+    (state)->digest.sha256.digest[7] = (state)->dgst_state[7];      \
+    (state)->digest.sha256.loLen = (len);                           \
+} while (0)
+
+/* Restore the SHA-256 state from cache and set data and length.
+ *
+ * @param [in, out] state      XMSS/MT state with SHA-256 digest and cache.
+ * @param [in]      data       Data to add to hash.
+ * @param [in]      len        Number of bytes in data.
+ *                             Must be less than a block.
+ * @param [in]      total_len  Number of bytes updated so far.
+ */
+#define XMSS_SHA256_STATE_RESTORE_DATA(state, data, len, total_len) \
+do {                                                                \
+    (state)->digest.sha256.digest[0] = (state)->dgst_state[0];      \
+    (state)->digest.sha256.digest[1] = (state)->dgst_state[1];      \
+    (state)->digest.sha256.digest[2] = (state)->dgst_state[2];      \
+    (state)->digest.sha256.digest[3] = (state)->dgst_state[3];      \
+    (state)->digest.sha256.digest[4] = (state)->dgst_state[4];      \
+    (state)->digest.sha256.digest[5] = (state)->dgst_state[5];      \
+    (state)->digest.sha256.digest[6] = (state)->dgst_state[6];      \
+    (state)->digest.sha256.digest[7] = (state)->dgst_state[7];      \
+    XMSS_SHA256_SET_DATA(state, data, len, total_len);              \
+} while (0)
+
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 && !WC_XMSS_FULL_HASH */
+
+/* Hash the data into output buffer.
+ *
+ * @param [in]  state   XMSS/MT state including digest and parameters.
+ * @param [in]  in      Data to digest.
+ * @param [in]  inlen   Length of data to digest in bytes.
+ * @param [out] out     Buffer to put digest into.
+ */
+static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in,
+    word32 inlen, byte* out)
+{
+    int ret;
+    const XmssParams* params = state->params;
 
-#ifdef WOLFSSL_HAVE_XMSS
-    #error "Contact wolfSSL to get the implementation of this file"
+#ifdef WC_XMSS_SHA256
+    /* Full SHA-256 digest. */
+    if ((params->hash == WC_HASH_TYPE_SHA256) &&
+            (params->n == WC_SHA256_DIGEST_SIZE)) {
+        ret = wc_Sha256Update(&state->digest.sha256, in, inlen);
+        if (ret == 0) {
+            ret = wc_Sha256Final(&state->digest.sha256, out);
+        }
+    }
+#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
+    /* Partial SHA-256 digest. */
+    else if (params->hash == WC_HASH_TYPE_SHA256) {
+        byte buf[WC_SHA256_DIGEST_SIZE];
+        ret = wc_Sha256Update(&state->digest.sha256, in, inlen);
+        if (ret == 0) {
+            ret = wc_Sha256Final(&state->digest.sha256, buf);
+        }
+        if (ret == 0) {
+            XMEMCPY(out, buf, params->n);
+        }
+    }
 #endif
+    else
+#endif /* WC_XMSS_SHA256 */
+#ifdef WC_XMSS_SHA512
+    /* Full SHA-512 digest. */
+    if (params->hash == WC_HASH_TYPE_SHA512) {
+        ret = wc_Sha512Update(&state->digest.sha512, in, inlen);
+        if (ret == 0) {
+            ret = wc_Sha512Final(&state->digest.sha512, out);
+        }
+    }
+    else
+#endif /* WC_XMSS_SHA512 */
+#ifdef WC_XMSS_SHAKE128
+    /* Digest with SHAKE-128. */
+    if (params->hash == WC_HASH_TYPE_SHAKE128) {
+        ret = wc_Shake128_Update(&state->digest.shake, in, inlen);
+        if (ret == 0) {
+            ret = wc_Shake128_Final(&state->digest.shake, out, params->n);
+        }
+    }
+    else
+#endif /* WC_XMSS_SHAKE128 */
+#ifdef WC_XMSS_SHAKE256
+    /* Digest with SHAKE-256. */
+    if (params->hash == WC_HASH_TYPE_SHAKE256) {
+        ret = wc_Shake256_Update(&state->digest.shake, in, inlen);
+        if (ret == 0) {
+            ret = wc_Shake256_Final(&state->digest.shake, out, params->n);
+        }
+    }
+    else
+#endif /* WC_XMSS_SHAKE256 */
+    {
+        /* Unsupported digest function. */
+        ret = NOT_COMPILED_IN;
+    }
+
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+}
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+#ifndef WC_XMSS_FULL_HASH
+/* Chain hashing.
+ *
+ * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM = PRF(SEED, ADRS);
+ *     tmp = F(KEY, tmp XOR BM);
+ *     return tmp;
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  tmp    Temporary buffer holding chain data.
+ * @param [in]  addr   Hash address as a byte array.
+ * @param [out] hash   Buffer to hold hash.
+ */
+static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp,
+    byte* addr, byte* hash)
+{
+    /* Offsets into chain hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* bm = key + XMSS_SHA256_32_N;
+    int ret;
+
+    /* Calculate n-byte key - KEY. */
+    ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
+    /* Copy back state after first 64 bytes. */
+    XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN,
+        XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+    /* Calculate hash. */
+    ret = wc_Sha256Final(&state->digest.sha256, key);
+
+    if (ret == 0) {
+        /* Calculate n-byte bit mask - BM. */
+        addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm);
+    }
+
+    if (ret == 0) {
+        /* Function padding set in caller. */
+        xorbuf(bm, tmp, XMSS_SHA256_32_N);
+        ret = wc_Sha256Update(&state->digest.sha256, state->buf,
+             XMSS_CHAIN_HASH_DATA_LEN_SHA256_32);
+    }
+    if (ret == 0) {
+        /* Calculate the chain hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, hash);
+    }
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+}
+#else
+/* Chain hashing.
+ *
+ * Padding, seed, addr for PRF set by caller into prf_buf.
+ *
+ * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM = PRF(SEED, ADRS);
+ *     tmp = F(KEY, tmp XOR BM);
+ *     return tmp;
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  tmp    Temporary buffer holding chain data.
+ * @param [out] out    Buffer to hold hash.
+ */
+static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp,
+    byte* hash)
+{
+    byte* addr = state->prf_buf + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N;
+    /* Offsets into chain hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* bm = key + XMSS_SHA256_32_N;
+
+    /* Calculate n-byte key - KEY. */
+    ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
+    /* Calculate the n-byte mask. */
+    addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm);
+
+    /* Function padding set in caller. */
+    xorbuf(bm, tmp, XMSS_SHA256_32_N);
+    /* Calculate the chain hash. */
+    wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN_SHA256_32, hash);
+}
+#endif /* !WC_XMSS_FULL_HASH */
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+
+/* Chain hashing.
+ *
+ * Padding, seed, addr for PRF set by caller into prf_buf.
+ *
+ * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM = PRF(SEED, ADRS);
+ *     tmp = F(KEY, tmp XOR BM);
+ *     return tmp;
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  tmp    Temporary buffer holding chain data.
+ * @param [out] hash   Buffer to hold hash.
+ */
+static void wc_xmss_chain_hash(XmssState* state, const byte* tmp, byte* hash)
+{
+    const XmssParams* params = state->params;
+    byte* addr = state->prf_buf + params->pad_len + params->n;
+    /* Offsets into chain hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + params->pad_len;
+    byte* bm = key + params->n;
+
+    /* Calculate n-byte key - KEY. */
+    ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), key);
+    /* Calculate n-byte bit mask - BM. */
+    addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), bm);
+
+    /* Function padding set in caller. */
+    xorbuf(bm, tmp, params->n);
+    /* Calculate the chain hash. */
+    wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN(params), hash);
+}
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+#ifndef WC_XMSS_FULL_HASH
+/* Randomized tree hashing.
+ *
+ * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM_0 = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(2);
+ *     BM_1 = PRF(SEED, ADRS);
+ *     return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1));
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  data   Input data.
+ * @param [in]  addr   Hash address.
+ * @param [out] hash   Buffer to hold hash.
+ */
+static void wc_xmss_rand_hash_sha256_32_prehash(XmssState* state,
+    const byte* data, HashAddress addr, byte* hash)
+{
+    int ret;
+    /* Offsets into rand hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* bm0 = key + XMSS_SHA256_32_N;
+    byte* bm1 = bm0 + XMSS_SHA256_32_N;
+    byte addr_buf[WC_XMSS_ADDR_LEN];
+
+    addr[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+    /* Calculate n-byte key - KEY. */
+    XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+        XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+    /* Calculate hash. */
+    ret = wc_Sha256Final(&state->digest.sha256, key);
+
+    /* Calculate n-byte mask - BM_0. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm0);
+    }
+
+    /* Calculate n-byte mask - BM_1. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm1);
+    }
+
+    if (ret == 0) {
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
+        /* XOR into bm0 and bm1. */
+        xorbuf(bm0, data, XMSS_SHA256_32_N * 2);
+        ret = wc_Sha256Update(&state->digest.sha256, state->buf,
+            XMSS_RAND_HASH_DATA_LEN_SHA256_32);
+    }
+    if (ret == 0) {
+        ret = wc_Sha256Final(&state->digest.sha256, hash);
+    }
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+}
+#endif /* !WC_XMSS_FULL_HASH */
+
+/* Randomized tree hashing.
+ *
+ * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM_0 = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(2);
+ *     BM_1 = PRF(SEED, ADRS);
+ *     return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1));
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  data     Input data.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] hash     Buffer to hold hash.
+ */
+static void wc_xmss_rand_hash_sha256_32(XmssState* state, const byte* data,
+    const byte* pk_seed, HashAddress addr, byte* hash)
+{
+    byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN +
+        XMSS_SHA256_32_N;
+    /* Offsets into rand hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* bm0 = key + XMSS_SHA256_32_N;
+    byte* bm1 = bm0 + XMSS_SHA256_32_N;
+#ifndef WC_XMSS_FULL_HASH
+    int ret;
+
+    /* Encode padding byte for PRF. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
+    /* Append public seed for PRF. */
+    XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
+        XMSS_SHA256_32_N);
+
+    /* Set key mask to initial value and append encoding. */
+    addr[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+    /* Calculate n-byte key - KEY. */
+    ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
+        XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
+    if (ret == 0) {
+        /* Copy state after first 64 bytes. */
+        XMSS_SHA256_STATE_CACHE(state);
+        /* Copy in remaining 32 bytes to buffer. */
+        XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, key);
+    }
+
+    /* Calculate n-byte mask - BM_0. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm0);
+    }
+
+    /* Calculate n-byte mask - BM_1. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm1);
+    }
+
+    if (ret == 0) {
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
+        /* XOR into bm0 and bm1. */
+        xorbuf(bm0, data, 2 * XMSS_SHA256_32_N);
+        ret = wc_Sha256Update(&state->digest.sha256, state->buf,
+            XMSS_RAND_HASH_DATA_LEN_SHA256_32);
+    }
+    if (ret == 0) {
+        ret = wc_Sha256Final(&state->digest.sha256, hash);
+    }
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+#else
+    /* Encode padding byte for PRF. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
+    /* Append public seed for PRF. */
+    XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
+        XMSS_SHA256_32_N);
+
+    /* Set key mask to initial value and append encoding. */
+    addr[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+    /* Calculate n-byte key - KEY. */
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
+    /* Calculate n-byte mask - BM_0. */
+    addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0);
+    /* Calculate n-byte mask - BM_1. */
+    addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1);
+
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN);
+    xorbuf(bm0, data, 2 * XMSS_SHA256_32_N);
+    wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash);
+#endif /* WC_XMSS_FULL_HASH */
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+
+/* Randomized tree hashing.
+ *
+ * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM_0 = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(2);
+ *     BM_1 = PRF(SEED, ADRS);
+ *     return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1));
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  data     Input data.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] hash     Buffer to hold hash.
+ */
+static void wc_xmss_rand_hash(XmssState* state, const byte* data,
+    const byte* pk_seed, HashAddress addr, byte* hash)
+{
+    const XmssParams* params = state->params;
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        wc_xmss_rand_hash_sha256_32(state, data, pk_seed, addr, hash);
+    }
+    else
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+    {
+        byte* addr_buf = state->prf_buf + params->pad_len + params->n;
+        /* Offsets into rand hash data. */
+        byte* pad = state->buf;
+        byte* key = pad + params->pad_len;
+        byte* bm0 = key + params->n;
+        byte* bm1 = bm0 + params->n;
+        const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN;
+
+        /* Encode padding byte for PRF. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len);
+        /* Append public seed for PRF. */
+        XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n);
+
+        /* Set key mask to initial value and append encoding. */
+        addr[XMSS_ADDR_KEY_MASK] = 0;
+        wc_xmss_addr_encode(addr, addr_buf);
+
+        /* Calculate n-byte key - KEY. */
+        wc_xmss_hash(state, state->prf_buf, len, key);
+        /* Calculate n-byte mask - BM_0. */
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        wc_xmss_hash(state, state->prf_buf, len, bm0);
+        /* Calculate n-byte mask - BM_1. */
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+        wc_xmss_hash(state, state->prf_buf, len, bm1);
+
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len);
+        xorbuf(bm0, data, 2 * params->n);
+        wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n,
+            hash);
+    }
+}
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY)
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+/* Randomized tree hashing.
+ *
+ * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM_0 = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(2);
+ *     BM_1 = PRF(SEED, ADRS);
+ *     return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1));
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  left     First half of data.
+ * @param [in]  right    Second half of data.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] hash     Buffer to hold hash.
+ */
+static void wc_xmss_rand_hash_lr_sha256_32(XmssState* state, const byte* left,
+    const byte* right, const byte* pk_seed, HashAddress addr, byte* hash)
+{
+    byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN +
+        XMSS_SHA256_32_N;
+    /* Offsets into rand hash data. */
+    byte* pad = state->buf;
+    byte* key = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* bm0 = key + XMSS_SHA256_32_N;
+    byte* bm1 = bm0 + XMSS_SHA256_32_N;
+#ifndef WC_XMSS_FULL_HASH
+    int ret;
+
+    /* Encode padding byte for PRF. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
+    /* Append public seed for PRF. */
+    XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
+        XMSS_SHA256_32_N);
+
+    /* Set key mask to initial value and append encoding. */
+    addr[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+    /* Calculate n-byte key - KEY. */
+    ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
+        XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
+    if (ret == 0) {
+        /* Copy state after first 64 bytes. */
+        XMSS_SHA256_STATE_CACHE(state);
+        /* Copy in remaining 32 bytes to buffer. */
+        XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, key);
+    }
+
+    /* Calculate n-byte mask - BM_0. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm0);
+    }
+
+    /* Calculate n-byte mask - BM_1. */
+    if (ret == 0) {
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+        /* Copy back state after first 64 bytes. */
+        XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
+            XMSS_HASH_PRF_DATA_LEN_SHA256_32);
+        /* Calculate hash. */
+        ret = wc_Sha256Final(&state->digest.sha256, bm1);
+    }
+
+    if (ret == 0) {
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
+        /* XOR into bm0 and bm1. */
+        XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N);
+        XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N);
+        xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N);
+        ret = wc_Sha256Update(&state->digest.sha256, state->buf,
+            XMSS_RAND_HASH_DATA_LEN_SHA256_32);
+    }
+    if (ret == 0) {
+        ret = wc_Sha256Final(&state->digest.sha256, hash);
+    }
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+#else
+    /* Encode padding byte for PRF. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
+    /* Append public seed for PRF. */
+    XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, XMSS_SHA256_32_N);
+
+    /* Set key mask to initial value and append encoding. */
+    addr[XMSS_ADDR_KEY_MASK] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+    /* Calculate n-byte key - KEY. */
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
+    /* Calculate n-byte mask - BM_0. */
+    addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0);
+    /* Calculate n-byte mask - BM_1. */
+    addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+    wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1);
+
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN);
+    XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N);
+    XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N);
+    xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N);
+    wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash);
+#endif /* WC_XMSS_FULL_HASH */
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+/* Randomized tree hashing - left and right separate parameters.
+ *
+ * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH
+ *     ...
+ *     ADRS.setKeyAndMask(0);
+ *     KEY = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(1);
+ *     BM_0 = PRF(SEED, ADRS);
+ *     ADRS.setKeyAndMask(2);
+ *     BM_1 = PRF(SEED, ADRS);
+ *     return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1));
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  left     First half of data.
+ * @param [in]  right    Second half of data.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] hash     Buffer to hold hash.
+ */
+static void wc_xmss_rand_hash_lr(XmssState* state, const byte* left,
+    const byte* right, const byte* pk_seed, HashAddress addr, byte* hash)
+{
+    const XmssParams* params = state->params;
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        wc_xmss_rand_hash_lr_sha256_32(state, left, right, pk_seed, addr, hash);
+    }
+    else
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+    {
+        byte* addr_buf = state->prf_buf + params->pad_len + params->n;
+        /* Offsets into rand hash data. */
+        byte* pad = state->buf;
+        byte* key = pad + params->pad_len;
+        byte* bm0 = key + params->n;
+        byte* bm1 = bm0 + params->n;
+        const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN;
+
+        /* Encode padding byte for PRF. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len);
+        /* Append public seed for PRF. */
+        XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n);
+
+        /* Set key mask to initial value and append encoding. */
+        addr[XMSS_ADDR_KEY_MASK] = 0;
+        wc_xmss_addr_encode(addr, addr_buf);
+
+        /* Calculate n-byte key - KEY. */
+        wc_xmss_hash(state, state->prf_buf, len, key);
+        /* Calculate n-byte mask - BM_0. */
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
+        wc_xmss_hash(state, state->prf_buf, len, bm0);
+        /* Calculate n-byte mask - BM_1. */
+        addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
+        wc_xmss_hash(state, state->prf_buf, len, bm1);
+
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len);
+        XMEMCPY(state->prf_buf, left, params->n);
+        XMEMCPY(state->prf_buf + params->n, right, params->n);
+        xorbuf(bm0, state->prf_buf, 2 * params->n);
+        wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n,
+            hash);
+    }
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL || WOLFSSL_XMSS_VERIFY_ONLY */
+
+/* Compute message hash from the random r, root, index and message.
+ *
+ * RFC 8391: 4.1.9, Algorithm 12: XMSS_sign
+ *    ...
+ *    byte[n] M' = H_msg(r || getRoot(SK) || (toByte(idx_sig, n)), M);
+ * RFC 8391: 5.1
+ *    H_msg: SHA2-256(toByte(2, 32) || KEY || M)
+ *    H_msg: SHA2-512(toByte(2, 64) || KEY || M)
+ *    H_msg: SHAKE128(toByte(2, 32) || KEY || M, 256)
+ *    H_msg: SHAKE256(toByte(2, 64) || KEY || M, 512)
+ *
+ * @param [in]  state     XMSS/MT state including digest and parameters.
+ * @param [in]  random    Random value of n bytes.
+ * @param [in]  root      Public root.
+ * @param [in]  idx       Buffer holding encoded index.
+ * @param [in]  idx_len   Length of encoded index in bytes.
+ * @param [in]  m         Message to hash.
+ * @param [in]  mlen      Length of message.
+ * @param [out] hash      Buffer to hold hash.
+ */
+static void wc_xmss_hash_message(XmssState* state, const byte* random,
+    const byte* root, const byte* idx, word8 idx_len, const byte* m,
+    word32 mlen, byte* hash)
+{
+    int ret;
+    const XmssParams* params = state->params;
+    word32 padKeyLen = params->pad_len + 3 * params->n;
+    /* Offsets into message hash data. */
+    byte* padKey = state->buf;
+    byte* pad = padKey;
+    byte* key = pad + params->pad_len;
+    byte* root_sk = key + params->n;
+    byte* idx_sig = root_sk + params->n;
+
+    /* Set prefix data before message. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_HASH, pad, params->pad_len);
+    XMEMCPY(key, random, params->n);
+    XMEMCPY(root_sk, root, params->n);
+    XMEMSET(idx_sig, 0, params->n - idx_len);
+    XMEMCPY(idx_sig + params->n - idx_len, idx, idx_len);
+
+    /* Hash the padding and key first. */
+#ifdef WC_XMSS_SHA256
+    if (params->hash == WC_HASH_TYPE_SHA256) {
+        ret = wc_Sha256Update(&state->digest.sha256, padKey, padKeyLen);
+    }
+    else
+#endif /* WC_XMSS_SHA256 */
+#ifdef WC_XMSS_SHA512
+    if (params->hash == WC_HASH_TYPE_SHA512) {
+        ret = wc_Sha512Update(&state->digest.sha512, padKey, padKeyLen);
+    }
+    else
+#endif /* WC_XMSS_SHA512 */
+#ifdef WC_XMSS_SHAKE128
+    if (params->hash == WC_HASH_TYPE_SHAKE128) {
+        ret = wc_Shake128_Update(&state->digest.shake, padKey, padKeyLen);
+    }
+    else
+#endif /* WC_XMSS_SHAKE128 */
+#ifdef WC_XMSS_SHAKE256
+    if (params->hash == WC_HASH_TYPE_SHAKE256) {
+        ret = wc_Shake256_Update(&state->digest.shake, padKey, padKeyLen);
+    }
+    else
+#endif /* WC_XMSS_SHAKE256 */
+    {
+        /* Unsupported digest function. */
+        ret = NOT_COMPILED_IN;
+    }
+    if (ret == 0) {
+        /* Generate hash of message - M'. */
+        wc_xmss_hash(state, m, mlen, hash);
+    }
+    else if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+}
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+
+/* Compute PRF with key and message.
+ *
+ * RFC 8391: 5.1
+ *   PRF: SHA2-256(toByte(3, 32) || KEY || M)
+ *   PRF: SHA2-512(toByte(3, 64) || KEY || M)
+ *   PRF: SHAKE128(toByte(3, 32) || KEY || M, 256)
+ *   PRF: SHAKE256(toByte(3, 64) || KEY || M, 512)
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  key      Key used to derive pseudo-random from.
+ * @param [in]  m        32 bytes of data to derive pseudo-random from.
+ * @param [out] prf      Buffer to hold pseudo-random data.
+ */
+static void wc_xmss_prf(XmssState* state, const byte* key, const byte* m,
+    byte* prf)
+{
+    const XmssParams* params = state->params;
+    byte* pad = state->prf_buf;
+    byte* key_buf = pad + params->pad_len;
+    byte* m_buf = key_buf + params->n;
+
+    /* 00[0..pl-1] || 03 || key[0..n-1] || m[0..31] */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len);
+    XMEMCPY(key_buf, key, params->n);
+    XMEMCPY(m_buf, m, XMSS_PRF_M_LEN);
+
+    /* Hash the PRF data. */
+    wc_xmss_hash(state, state->prf_buf, params->pad_len + params->n +
+        XMSS_PRF_M_LEN, prf);
+}
+
+#ifdef XMSS_CALL_PRF_KEYGEN
+/* Compute PRF for keygen with key and message.
+ *
+ * NIST SP 800-208: 5.1, 5.2, 5.3, 5.4
+ *   PRFkeygen (KEY, M): SHA-256(toByte(4, 32) || KEY || M)
+ *   PRFkeygen (KEY, M): T192(SHA-256(toByte(4, 4) || KEY || M))
+ *   PRFkeygen (KEY, M): SHAKE256(toByte(4, 32) || KEY || M, 256)
+ *   PRFkeygen (KEY, M): SHAKE256(toByte(4, 4) || KEY || M, 192)
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  key      Key of n bytes used to derive pseudo-random from.
+ * @param [in]  m        n + 32 bytes of data to derive pseudo-random from.
+ * @param [out] prf      Buffer to hold pseudo-random data.
+ */
+static void wc_xmss_prf_keygen(XmssState* state, const byte* key,
+    const byte* m, byte* prf)
+{
+    const XmssParams* params = state->params;
+    byte* pad = state->prf_buf;
+    byte* key_buf = pad + params->pad_len;
+    byte* m_buf = key_buf + params->n;
+
+    /* 00[0..pl-1] || 04 || key[0..n-1] || m[0..n+31] */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len);
+    XMEMCPY(key_buf, key, params->n);
+    XMEMCPY(m_buf, m, params->n + XMSS_PRF_M_LEN);
+
+    /* Hash the PRF keygen data. */
+    wc_xmss_hash(state, state->prf_buf, params->pad_len + 2 * params->n +
+        XMSS_PRF_M_LEN, prf);
+}
+#endif /* XMSS_CALL_PRF_KEYGEN */
+
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+/********************************************
+ * WOTS
+ ********************************************/
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+/* Expand private seed with PRF keygen.
+ *
+ * RFC 8391: 4.1.3
+ *   "the existence of a method getWOTS_SK(SK, i) is assumed"
+ * NIST SP 800-208: 7.2.1, Algorithm 10'
+ *     ...
+ *     for ( j=0; j < len; j++) {
+ *       ADRS.setChainAddress(j);
+ *       sk[j] = PRFkeygen(S_XMSS, SEED || ADRS);
+ *     }
+ *
+ * @param [in]  state     XMSS/MT state including digest and parameters.
+ * @param [in]  sk_seed   Buffer holding private seed.
+ * @param [in]  pk_seed   Random public seed.
+ * @param [in]  addr      Hash address as a byte array.
+ * @param [out] gen_seed  Buffer to hold seeds.
+ */
+static void wc_xmss_wots_get_wots_sk_sha256_32(XmssState* state,
+    const byte* sk_seed, const byte* pk_seed, byte* addr, byte* gen_seed)
+{
+    const XmssParams* params = state->params;
+    word32 i;
+    byte* pad = state->prf_buf;
+    byte* s_xmss = pad + XMSS_SHA256_32_PAD_LEN;
+    byte* seed = s_xmss + XMSS_SHA256_32_N;
+    byte* addr_buf = seed + XMSS_SHA256_32_N;
+    int ret;
+
+    ((word32*)addr)[XMSS_ADDR_CHAIN] = 0;
+    ((word32*)addr)[XMSS_ADDR_HASH] = 0;
+    ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
+
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, XMSS_SHA256_32_PAD_LEN);
+    XMEMCPY(s_xmss, sk_seed, XMSS_SHA256_32_N);
+    XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N);
+    XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
+
+#ifndef WC_XMSS_FULL_HASH
+    ret = wc_Sha256Update(&state->digest.sha256, pad, XMSS_SHA256_32_PAD_LEN +
+        XMSS_SHA256_32_N);
+    if (ret == 0) {
+        /* Copy state after first 64 bytes. */
+        XMSS_SHA256_STATE_CACHE(state);
+        ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N +
+            WC_XMSS_ADDR_LEN);
+    }
+    if (ret == 0) {
+        ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
+    }
+    for (i = 1; (ret == 0) && (i < params->wots_len); i++) {
+        gen_seed += XMSS_SHA256_32_N;
+        addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+        XMSS_SHA256_STATE_RESTORE(state, 64);
+        ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N +
+            WC_XMSS_ADDR_LEN);
+        if (ret == 0) {
+            ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
+        }
+    }
+#else
+    ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
+        XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN);
+    if (ret == 0) {
+        ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
+    }
+    for (i = 1; (ret == 0) && i < params->wots_len; i++) {
+        gen_seed += XMSS_SHA256_32_N;
+        addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+        ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
+            XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN);
+        if (ret == 0) {
+            ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
+        }
+    }
+#endif /*  WC_XMSS_FULL_HASH*/
+
+    if (state->ret == 0) {
+        /* Store any digest failures for public APIs to return. */
+        state->ret = ret;
+    }
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+
+/* Expand private seed with PRF keygen.
+ *
+ * RFC 8391: 4.1.3
+ *   "the existence of a method getWOTS_SK(SK, i) is assumed"
+ * NIST SP 800-208: 7.2.1
+ *   Algorithm 10'
+ *     ...
+ *     for ( j=0; j < len; j++) {
+ *       ADRS.setChainAddress(j);
+ *       sk[j] = PRFkeygen(S_XMSS, SEED || ADRS);
+ *     }
+ *
+ * @param [in]  state     XMSS/MT state including digest and parameters.
+ * @param [in]  sk_seed   Buffer holding private seed.
+ * @param [in]  pk_seed   Random public seed.
+ * @param [in]  addr      Hash address as a byte array.
+ * @param [out] gen_seed  Buffer to hold seeds.
+ */
+static void wc_xmss_wots_get_wots_sk(XmssState* state, const byte* sk_seed,
+    const byte* pk_seed, byte* addr, byte* gen_seed)
+{
+    const XmssParams* params = state->params;
+    word32 i;
+#ifdef XMSS_CALL_PRF_KEYGEN
+    byte* seed = state->buf;
+    byte* addr_buf = seed + params->n;
+#else
+    byte* pad = state->prf_buf;
+    byte* s_xmss = pad + params->pad_len;
+    byte* seed = s_xmss + params->n;
+    byte* addr_buf = seed + params->n;
+    const word32 len = params->pad_len + params->n * 2 + WC_XMSS_ADDR_LEN;
+#endif /* XMSS_CALL_PRF_KEYGEN */
+
+    /* Ensure hash address fields are 0. */
+    ((word32*)addr)[XMSS_ADDR_CHAIN] = 0;
+    ((word32*)addr)[XMSS_ADDR_HASH] = 0;
+    ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
+
+#ifdef XMSS_CALL_PRF_KEYGEN
+    /* Copy the seed and address into PRF keygen message buffer. */
+    XMEMCPY(seed, pk_seed, params->n);
+    XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
+
+    wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed);
+    for (i = 1; i < params->wots_len; i++) {
+        gen_seed += params->n;
+        addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+        wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed);
+    }
+#else
+    /* Copy the PRF keygen fields into one buffer. */
+    XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len);
+    XMEMCPY(s_xmss, sk_seed, params->n);
+    XMEMCPY(seed, pk_seed, params->n);
+    XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
+
+    /* Fill output with hashes of different chain hash addresses. */
+    wc_xmss_hash(state, state->prf_buf, len, gen_seed);
+    for (i = 1; i < params->wots_len; i++) {
+        gen_seed += params->n;
+        addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+        wc_xmss_hash(state, state->prf_buf, len, gen_seed);
+    }
+#endif /* XMSS_CALL_PRF_KEYGEN */
+}
+
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+/* Chain hashing to calculate node hash.
+ *
+ * RFC 8391: 3.1.2, Algorithm 2 - recursive.
+ * This function is an iterative version.
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  data     Initial data to hash.
+ * @param [in]  start    Starting hash value in hash address.
+ * @param [in]  steps    Size of step.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address as a byte array.
+ * @param [out] hash     Chained hash.
+ */
+static void wc_xmss_chain_sha256_32(XmssState* state, const byte* data,
+    unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr,
+    byte* hash)
+{
+    if (steps > 0) {
+        word32 i;
+        byte* pad = state->prf_buf;
+        byte* seed = pad + XMSS_SHA256_32_PAD_LEN;
+#ifndef WC_XMSS_FULL_HASH
+        int ret;
+
+        /* Set data for PRF hash. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN);
+        XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N);
+
+        /* Hash first 64 bytes. */
+        ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
+             XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
+        if (ret == 0) {
+            /* Copy state after first 64 bytes. */
+            XMSS_SHA256_STATE_CACHE(state);
+            /* Only do this once for all chain hash calls. */
+            XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf,
+                state->params->pad_len);
+
+            /* Set address. */
+            XMSS_ADDR_SET_BYTE(addr, XMSS_ADDR_HASH, start);
+            wc_xmss_chain_hash_sha256_32(state, data, addr, hash);
+            /* Iterate 'steps' calls to the hash function. */
+            for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
+                addr[XMSS_ADDR_HASH * 4 + 3] = i;
+                wc_xmss_chain_hash_sha256_32(state, hash, addr, hash);
+            }
+        }
+        else if (state->ret == 0) {
+            /* Store any digest failures for public APIs to return. */
+            state->ret = ret;
+        }
+#else
+        const XmssParams* params = state->params;
+        byte* addr_buf = seed + XMSS_SHA256_32_N;
+
+        /* Set data for PRF hash. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN);
+        XMEMCPY(seed, pk_seed, params->n);
+        XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
+
+        /* Only do this once for all chain hash calls. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len);
+
+        /* Set address. */
+        XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start);
+        wc_xmss_chain_hash_sha256_32(state, data, hash);
+        /* Iterate 'steps' calls to the hash function. */
+        for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
+            addr_buf[XMSS_ADDR_HASH * 4 + 3] = i;
+            wc_xmss_chain_hash_sha256_32(state, hash, hash);
+        }
+#endif /* !WC_XMSS_FULL_HASH */
+    }
+    else if (hash != data) {
+        XMEMCPY(hash, data, XMSS_SHA256_32_N);
+    }
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+
+/* Chain hashing to calculate node hash.
+ *
+ * RFC 8391: 3.1.2, Algorithm 2 - recursive.
+ * This function is an iterative version.
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  data     Initial data to hash.
+ * @param [in]  start    Starting hash value in hash address.
+ * @param [in]  steps    Size of step.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address as a byte array.
+ * @param [out] hash     Chained hash.
+ */
+static void wc_xmss_chain(XmssState* state, const byte* data,
+    unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr,
+    byte* hash)
+{
+    const XmssParams* params = state->params;
+
+    if (steps > 0) {
+        word32 i;
+        byte* pad = state->prf_buf;
+        byte* seed = pad + params->pad_len;
+        byte* addr_buf = seed + params->n;
+
+        /* Set data for PRF hash. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len);
+        XMEMCPY(seed, pk_seed, params->n);
+        XMEMCPY(addr_buf, addr, 32);
+
+        /* Only do this once for all chain hash calls. */
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len);
+
+        /* Set address. */
+        XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start);
+        wc_xmss_chain_hash(state, data, hash);
+        /* Iterate 'steps' calls to the hash function. */
+        for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
+            addr_buf[XMSS_ADDR_HASH * 4 + 3] = i;
+            wc_xmss_chain_hash(state, hash, hash);
+        }
+    }
+    else if (hash != data) {
+        XMEMCPY(hash, data, params->n);
+    }
+}
+
+/* Convert base on message and add checksum.
+ *
+ * RFC 8391:, 2.6, Algorithm 1: base_w
+ *     int in = 0;
+ *     int out = 0;
+ *     unsigned int total = 0;
+ *     int bits = 0;
+ *     int consumed;
+ *
+ *     for ( consumed = 0; consumed < out_len; consumed++ ) {
+ *         if ( bits == 0 ) {
+ *             total = X[in];
+ *             in++;
+ *             bits += 8;
+ *         }
+ *         bits -= lg(w);
+ *         basew[out] = (total >> bits) AND (w - 1);
+ *         out++;
+ *     }
+ *     return basew;
+ *
+ * base_w implemented for w == 16 (lg(w) == 4).
+ *
+ * RFC 8391: 3.1.5, Algorithm 5:
+ *     ...
+ *     csum = 0;
+ *
+ *     # Convert message to base w
+ *     msg = base_w(M, w, len_1);
+ *     # Compute checksum
+ *     for ( i = 0; i < len_1; i++ ) {
+ *           csum = csum + w - 1 - msg[i];
+ *     }
+ *
+ *     # Convert csum to base w
+ *     csum = csum << ( 8 - ( ( len_2 * lg(w) ) % 8 ));
+ *     len_2_bytes = ceil( ( len_2 * lg(w) ) / 8 );
+ *     msg = msg || base_w(toByte(csum, len_2_bytes), w, len_2);
+ *
+ * len_1 == 8 * n / 4 = n * 2
+ * Implemented for len_2 == 3
+ *
+ * @param [in]  m      Message data.
+ * @param [in]  n      Number of bytes in hash.
+ * @param [out] msg    Message in new base.
+ */
+static void wc_xmss_msg_convert(const byte* m, word8 n, word8* msg)
+{
+    word8 i;
+    word16 csum = 0;
+
+    /* Split each full byte of m into two bytes of msg. */
+    for (i = 0; i < n; i++) {
+        msg[0] = m[i] >> 4;
+        msg[1] = m[i] & 0xf;
+        csum += XMSS_WOTS_W - 1 - msg[0];
+        csum += XMSS_WOTS_W - 1 - msg[1];
+        msg += 2;
+    }
+
+    /* Append checksum to message. (Maximum value: 1920 = 64 * 2 * 15) */
+    msg[0] = (csum >> 8)       ;
+    msg[1] = (csum >> 4) & 0x0f;
+    msg[2] = (csum     ) & 0x0f;
+}
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+
+/* WOTS+ generate public key with private seed.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9:
+ *     ...
+ *     pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS);
+ * RFC 8391, 3.1.4, Algorithm 4: WOTS_genPK
+ *     ...
+ *     for ( i = 0; i < len; i++ ) {
+ *       ADRS.setChainAddress(i);
+ *       pk[i] = chain(sk[i], 0, w - 1, SEED, ADRS);
+ *     }
+ *     return pk;
+ *
+ * WOTS_genPK only used in Algorithm 9 and it is convenient to combine with
+ * getWOTS_SK due to parameter specific implementations.
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  sk     Random private seed.
+ * @param [in]  seed   Random public seed.
+ * @param [in]  addr   Hashing address.
+ * @param [out] pk     Public key.
+ */
+static void wc_xmss_wots_gen_pk(XmssState* state, const byte* sk,
+    const byte* seed, HashAddress addr, byte* pk)
+{
+    const XmssParams* params = state->params;
+    byte* addr_buf = state->encMsg;
+    word32 i;
+
+    /* Ensure chain address is 0 and encode into a buffer. */
+    addr[XMSS_ADDR_CHAIN] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        /* Expand the private seed - getWOTS_SK */
+        wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf,
+            pk);
+
+        /* Calculate chain hash. */
+        wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf,
+            pk);
+        for (i = 1; i < params->wots_len; i++) {
+            pk += params->n;
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed,
+                addr_buf, pk);
+        }
+    }
+    else
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+    {
+        /* Expand the private seed - getWOTS_SK */
+        wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, pk);
+
+        /* Calculate chain hash. */
+        wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk);
+        for (i = 1; i < params->wots_len; i++) {
+            pk += params->n;
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk);
+        }
+    }
+}
+/* Generate a signature from a privatge key and message.
+ *
+ * RFC 8391: 4.1.9, Algorithm 11: treeSig
+ *     sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig),
+ *                         M', getSEED(SK), ADRS);
+ * RFC 8391: 3.1.5, Algorithm 5: WOTS_sign
+ *     (Convert message to base w and append checksum in base w)
+ *     ...
+ *     for ( i = 0; i < len; i++ ) {
+ *          ADRS.setChainAddress(i);
+ *          sig[i] = chain(sk[i], 0, msg[i], SEED, ADRS);
+ *     }
+ *     return sig;
+ *
+ * WOTS_sign only used in Algorithm 11 and convenient to do getWOTS_SK due to
+ * hash address reuse and parameter specific implementations.
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  m      Message hash to sign.
+ * @param [in]  sk     Random private seed.
+ * @param [in]  seed   Random public seed.
+ * @param [in]  addr   Hashing address.
+ * @param [out] sig    Calculated XMSS/MT signature.
+ */
+static void wc_xmss_wots_sign(XmssState* state, const byte* m,
+    const byte* sk, const byte* seed, HashAddress addr, byte* sig)
+{
+    const XmssParams* params = state->params;
+    byte* addr_buf = state->pk;
+    word32 i;
+
+    /* Convert message to base w and append checksum in base w. */
+    wc_xmss_msg_convert(m, params->n, state->encMsg);
+
+    /* Set initial chain value and encode hash address. */
+    addr[XMSS_ADDR_CHAIN] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        /* Expand the private seed - getWOTS_SK */
+        wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf, sig);
+
+        /* Calculate chain hash. */
+        wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[0], seed, addr_buf,
+            sig);
+        for (i = 1; i < params->wots_len; i++) {
+            sig += params->n;
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[i], seed,
+                addr_buf, sig);
+        }
+    }
+    else
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+    {
+        /* Expand the private seed - getWOTS_SK */
+        wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, sig);
+
+       /* Calculate chain hash. */
+        wc_xmss_chain(state, sig, 0, state->encMsg[0], seed, addr_buf, sig);
+        for (i = 1; i < params->wots_len; i++) {
+            sig += params->n;
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain(state, sig, 0, state->encMsg[i], seed, addr_buf, sig);
+        }
+    }
+}
+
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+/* Compute WOTS+ public key value from signature and message.
+ *
+ * RFC 8319: 3.1.6
+ *   Algorithm 6: WOTS_pkFromSig
+ *     (Convert message to base w and append checksum in base w)
+ *     ...
+ *     for ( i = 0; i < len; i++ ) {
+ *          ADRS.setChainAddress(i);
+ *          tmp_pk[i] = chain(sig[i], msg[i], w - 1 - msg[i], SEED, ADRS);
+ *     }
+ *     return tmp_pk;
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  sig    XMSS/MT Signature.
+ * @param [in]  m      Message to verify.
+ * @param [in]  seed   Random public seed.
+ * @param [in]  addr   Hashing address.
+ * @param [out] pk     Public key.
+ */
+static void wc_xmss_wots_pk_from_sig(XmssState* state, const byte* sig,
+    const byte* m, const byte* seed, HashAddress addr, byte* pk)
+{
+    const XmssParams* params = state->params;
+    byte* addr_buf = state->stack;
+    word32 i;
+
+    /* Convert message to base w and append checksum in base w. */
+    wc_xmss_msg_convert(m, params->n, state->encMsg);
+
+    /* Start with address with chain value of 0. */
+    addr[XMSS_ADDR_CHAIN] = 0;
+    wc_xmss_addr_encode(addr, addr_buf);
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        /* Calculate chain hash. */
+        wc_xmss_chain_sha256_32(state, sig, state->encMsg[0],
+            XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk);
+        for (i = 1; i < params->wots_len; i++) {
+            sig += params->n;
+            pk += params->n;
+            /* Update chain. */
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain_sha256_32(state, sig, state->encMsg[i],
+                XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk);
+        }
+    }
+    else
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */
+    {
+        /* Calculate chain hash. */
+        wc_xmss_chain(state, sig, state->encMsg[0],
+            XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk);
+        for (i = 1; i < params->wots_len; i++) {
+            sig += params->n;
+            pk += params->n;
+            /* Update chain. */
+            addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
+            wc_xmss_chain(state, sig, state->encMsg[i],
+                XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk);
+        }
+    }
+}
+
+/********************************************
+ * L-TREE - unbalanced binary hash tree
+ ********************************************/
+
+/* Compute leaves of L-tree from WOTS+ public key and compress to single value.
+ *
+ * RFC 8391: 4.1.5, Algorithm 8: ltree
+ *     unsigned int len' = len;
+ *     ADRS.setTreeHeight(0);
+ *     while ( len' > 1 ) {
+ *       for ( i = 0; i < floor(len' / 2); i++ ) {
+ *         ADRS.setTreeIndex(i);
+ *         pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS);
+ *       }
+ *       if ( len' % 2 == 1 ) {
+ *         pk[floor(len' / 2)] = pk[len' - 1];
+ *       }
+ *       len' = ceil(len' / 2);
+ *       ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
+ *     }
+ *     return pk[0];
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  pk     WOTS+ public key.
+ * @param [in]  seed   Random public seed.
+ * @param [in]  addr   Hashing address.
+ * @param [out] pk0    N-byte compressed public key value pk[0].
+ */
+static void wc_xmss_ltree(XmssState* state, byte* pk, const byte* seed,
+    HashAddress addr, byte* pk0)
+{
+    const XmssParams* params = state->params;
+    word8 len = params->wots_len;
+    word32 h = 0;
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
+    !defined(WC_XMSS_FULL_HASH)
+    /* Precompute hash state after first 64 bytes (common to all hashes). */
+    if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+            (params->n == XMSS_SHA256_32_N) &&
+            (params->hash == WC_HASH_TYPE_SHA256)) {
+        byte* prf_buf = state->prf_buf;
+        int ret;
+
+        XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, prf_buf, XMSS_SHA256_32_PAD_LEN);
+        XMEMCPY(prf_buf + XMSS_SHA256_32_PAD_LEN, seed, XMSS_SHA256_32_N);
+
+        ret = wc_Sha256Update(&state->digest.sha256, prf_buf,
+            XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
+        if (ret == 0) {
+            /* Copy state after first 64 bytes. */
+            XMSS_SHA256_STATE_CACHE(state);
+        }
+        else if (state->ret == 0) {
+            /* Store any digest failures for public APIs to return. */
+            state->ret = ret;
+        }
+    }
+#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 && !WC_XMSS_FULL_HASH */
+    while (len > 1) {
+        word8 i;
+        word8 len2 = len >> 1;
+
+        addr[XMSS_ADDR_TREE_HEIGHT] = h++;
+
+        for (i = 0; i < len2; i++) {
+            addr[XMSS_ADDR_TREE_INDEX] = i;
+        #if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
+            !defined(WC_XMSS_FULL_HASH)
+            if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
+                    (params->n == XMSS_SHA256_32_N) &&
+                    (params->hash == WC_HASH_TYPE_SHA256)) {
+                wc_xmss_rand_hash_sha256_32_prehash(state,
+                    pk + i * 2 * XMSS_SHA256_32_N, addr,
+                    pk + i * XMSS_SHA256_32_N);
+            }
+            else
+        #endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 &&
+                * !WC_XMSS_FULL_HASH */
+            {
+                wc_xmss_rand_hash(state, pk + i * 2 * params->n,
+                    seed, addr, pk + i * params->n);
+            }
+        }
+        if (len & 1) {
+            XMEMCPY(pk + len2 * params->n, pk + (len - 1) * params->n,
+                params->n);
+        }
+        len = len2 + (len & 1);
+    }
+    /* Return compressed public key value pk[0]. */
+    XMEMCPY(pk0, pk, params->n);
+}
+
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+
+#ifdef WOLFSSL_WC_XMSS_SMALL
+
+/********************************************
+ * TREE HASH
+ ********************************************/
+
+#ifndef WOLFSSL_SMALL_STACK
+/* Compute internal nodes of Merkle tree.
+ *
+ * Implementation always starts at index 0. (s = 0)
+ *
+ * Build authentication path, if required, rather than duplicating work.
+ * When node is generated, copy out to authentication path array of nodes.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9: treeHash
+ *     if( s % (1 << t) != 0 ) return -1;
+ *     for ( i = 0; i < 2^t; i++ ) {
+ *       SEED = getSEED(SK);
+ *       ADRS.setType(0);   # Type = OTS hash address
+ *       ADRS.setOTSAddress(s + i);
+ *       pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS);
+ *       ADRS.setType(1);   # Type = L-tree address
+ *       ADRS.setLTreeAddress(s + i);
+ *       node = ltree(pk, SEED, ADRS);
+ *       ADRS.setType(2);   # Type = hash tree address
+ *       ADRS.setTreeHeight(0);
+ *       ADRS.setTreeIndex(i + s);
+ *       while ( Top node on Stack has same height t' as node ) {
+ *          ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *          node = RAND_HASH(Stack.pop(), node, SEED, ADRS);
+ *          ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
+ *       }
+ *       Stack.push(node);
+ *     }
+ *     return Stack.pop();
+ * RFC 8391: 4.1.9, (Example) buildAuth
+ *     for ( j = 0; j < h; j++ ) {
+ *       k = floor(i / (2^j)) XOR 1;
+ *       auth[j] = treeHash(SK, k * 2^j, j, ADRS);
+ *     }
+ *
+ * @param [in]  state         XMSS/MT state including digest and parameters.
+ * @param [in]  sk_seed       Random private seed.
+ * @param [in]  pk_seed       Random public seed.
+ * @param [in]  leafIdx       Index of lead node.
+ * @param [in]  subtree_addr  Address of subtree.
+ * @param [out] root          Root node of the tree.
+ * @param [out] auth_path     Nodes of the authentication path.
+ */
+static void wc_xmss_treehash(XmssState* state, const byte* sk_seed,
+    const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root,
+    byte* auth_path)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    byte* node = state->stack;
+    HashAddress ots;
+    HashAddress ltree;
+    HashAddress tree;
+    word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
+    word8 offset = 0;
+    word32 max = (word32)1 << params->sub_h;
+    word32 i;
+
+    /* Copy hash address into one for each purpose.  */
+    XMSS_ADDR_OTS_SET_SUBTREE(ots, subtree);
+    XMSS_ADDR_LTREE_SET_SUBTREE(ltree, subtree);
+    XMSS_ADDR_TREE_SET_SUBTREE(tree, subtree);
+
+    for (i = 0; i < max; i++) {
+        word8 h;
+
+        /* Calculate WOTS+ public key. */
+        ots[XMSS_ADDR_OTS] = i;
+        wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, ots, state->pk);
+        /* Calculate public value. */
+        ltree[XMSS_ADDR_LTREE] = i;
+        wc_xmss_ltree(state, state->pk, pk_seed, ltree, node);
+
+        /* Initial height at this offset is 0. */
+        h = height[offset] = 0;
+        /* Copy node, at height 0, out if on authentication path. */
+        if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) {
+            XMEMCPY(auth_path, node, n);
+        }
+
+        /* Top node on Stack has same height t' as node. */
+        while ((offset >= 1) && (h == height[offset - 1])) {
+            word32 tree_idx = i >> (h + 1);
+
+            node -= n;
+            /* Calculate hash of node. */
+            tree[XMSS_ADDR_TREE_HEIGHT] = h;
+            tree[XMSS_ADDR_TREE_INDEX] = tree_idx;
+            wc_xmss_rand_hash(state, node, pk_seed, tree, node);
+
+            /* Update offset and height. */
+            offset--;
+            h = ++height[offset];
+
+            /* Copy node out if on authentication path. */
+            if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) {
+                XMEMCPY(auth_path + h * n, node, n);
+            }
+        }
+        offset++;
+        node += n;
+    }
+
+    /* Copy the root node. */
+    XMEMCPY(root, state->stack, n);
+}
+#else
+/* Compute internal nodes of Merkle tree.
+ *
+ * Implementation always starts at index 0. (s = 0)
+ *
+ * Build authentication path, if required, rather than duplicating work.
+ * When node is generated, copy out to authentication path array of nodes.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9: treeHash
+ *     if( s % (1 << t) != 0 ) return -1;
+ *     for ( i = 0; i < 2^t; i++ ) {
+ *       SEED = getSEED(SK);
+ *       ADRS.setType(0);   # Type = OTS hash address
+ *       ADRS.setOTSAddress(s + i);
+ *       pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS);
+ *       ADRS.setType(1);   # Type = L-tree address
+ *       ADRS.setLTreeAddress(s + i);
+ *       node = ltree(pk, SEED, ADRS);
+ *       ADRS.setType(2);   # Type = hash tree address
+ *       ADRS.setTreeHeight(0);
+ *       ADRS.setTreeIndex(i + s);
+ *       while ( Top node on Stack has same height t' as node ) {
+ *          ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *          node = RAND_HASH(Stack.pop(), node, SEED, ADRS);
+ *          ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
+ *       }
+ *       Stack.push(node);
+ *     }
+ *     return Stack.pop();
+ * RFC 8391: 4.1.9, (Example) buildAuth
+ *     for ( j = 0; j < h; j++ ) {
+ *       k = floor(i / (2^j)) XOR 1;
+ *       auth[j] = treeHash(SK, k * 2^j, j, ADRS);
+ *     }
+ *
+ * @param [in]  state         XMSS/MT state including digest and parameters.
+ * @param [in]  sk_seed       Random private seed.
+ * @param [in]  pk_seed       Random public seed.
+ * @param [in]  leafIdx       Index of lead node.
+ * @param [in]  subtree_addr  Address of subtree.
+ * @param [out] root          Root node of the tree.
+ * @param [out] auth_path     Nodes of the authentication path.
+ */
+static void wc_xmss_treehash(XmssState* state, const byte* sk_seed,
+    const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root,
+    byte* auth_path)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    byte* node = state->stack;
+    HashAddress addr;
+    word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
+    word8 offset = 0;
+    word32 max = (word32)1 << params->sub_h;
+    word32 i;
+
+    XMSS_ADDR_SET_SUBTREE(addr, subtree, 0);
+
+    for (i = 0; i < max; i++) {
+        word8 h;
+
+        /* Calculate WOTS+ public key. */
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+        addr[XMSS_ADDR_LTREE] = i;
+        wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
+        /* Calculate public value. */
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
+        wc_xmss_ltree(state, state->pk, pk_seed, addr, node);
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
+        addr[XMSS_ADDR_TREE_ZERO] = 0;
+
+        /* Initial height at this offset is 0. */
+        h = height[offset] = 0;
+        /* Copy node out if on authentication path. */
+        if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) {
+            XMEMCPY(auth_path, node, n);
+        }
+
+        /* Top node on Stack has same height t' as node. */
+        while ((offset >= 1) && (h == height[offset - 1])) {
+            word32 tree_idx = i >> (h + 1);
+
+            node -= n;
+            /* Calculate hash of node. */
+            addr[XMSS_ADDR_TREE_HEIGHT] = h;
+            addr[XMSS_ADDR_TREE_INDEX] = tree_idx;
+            wc_xmss_rand_hash(state, node, pk_seed, addr, node);
+
+            /* Update offset and height. */
+            offset--;
+            h = ++height[offset];
+
+            /* Copy node out if on authentication path. */
+            if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) {
+                XMEMCPY(auth_path + h * n, node, n);
+            }
+        }
+        offset++;
+        node += n;
+        /* Reset hash address ready for use as OTS and LTREE. */
+        addr[XMSS_ADDR_TREE_HEIGHT] = 0;
+        addr[XMSS_ADDR_TREE_INDEX] = 0;
+    }
+
+    /* Copy the root node. */
+    XMEMCPY(root, state->stack, n);
+}
+#endif /* !WOLFSSL_SMALL_STACK */
+
+/********************************************
+ * MAKE KEY
+ ********************************************/
+
+/* Derives XMSSMT (and XMSS) key pair from seeds.
+ *
+ * RFC 8391: 4.1.7, Algorithm 10: XMSS_keyGen.
+ *     ...
+ *     initialize SK_PRF with a uniformly random n-byte string;
+ *     setSK_PRF(SK, SK_PRF);
+ *
+ *     # Initialization for common contents
+ *     initialize SEED with a uniformly random n-byte string;
+ *     setSEED(SK, SEED);
+ *     setWOTS_SK(SK, wots_sk));
+ *     ADRS = toByte(0, 32);
+ *     root = treeHash(SK, 0, h, ADRS);
+ *
+ *     SK = idx || wots_sk || SK_PRF || root || SEED;
+ *     PK = OID || root || SEED;
+ *     return (SK || PK);
+ *
+ * wots_sk, SK_PRF and SEED passed in as seed.
+ * Store seed for wots_sk instead of generated wots_sk.
+ * OID not stored in PK this is handled in upper layer.
+ *
+ * @param [in]  state   XMSS/MT state including digest and parameters.
+ * @param [in]  seed    Random seeds.
+ * @param [out] sk      Secret/Private key.
+ * @param [out] pk      Public key.
+ * @return  0 on success.
+ * @return  <0 on digest failure.
+ */
+int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed,
+    unsigned char* sk, unsigned char* pk)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const byte* seed_priv = seed;
+    const byte* seed_pub  = seed + 2 * n;
+    /* Offsets into secret/private key. */
+    byte* sk_idx  = sk;
+    byte* sk_seed = sk_idx + params->idx_len;
+    byte* sk_pub  = sk_seed + 2 * n;
+    /* Offsets into public key. */
+    byte* pk_root = pk;
+    byte* pk_seed = pk_root + n;
+
+    /* Set first index to 0 in private key. */
+    XMEMSET(sk_idx, 0, params->idx_len);
+    /* Set private key seed and private key for PRF in to private key. */
+    XMEMCPY(sk_seed, seed_priv, 2 * n);
+    /* Set public key seed into public key. */
+    XMEMCPY(pk_seed, seed_pub, n);
+
+    /* Set all address values to zero. */
+    XMEMSET(state->addr, 0, sizeof(HashAddress));
+    /* Set depth into address. */
+    state->addr[XMSS_ADDR_LAYER] = params->d - 1;
+    /* Compute root node into public key. */
+    wc_xmss_treehash(state, sk_seed, pk_seed, 0, state->addr, pk_root, NULL);
+
+    /* Append public key (root node and public seed) to private key. */
+    XMEMCPY(sk_pub, pk_root, 2 * n);
+
+    /* Return any errors that occurred during hashing. */
+    return state->ret;
+}
+
+/********************************************
+ * SIGN
+ ********************************************/
+
+/**
+ * Sign message using XMSS/XMSS^MT.
+ *
+ * RFC 8391: 4.1.9, Algorithm 11: treeSig
+ *     auth = buildAuth(SK, idx_sig, ADRS);
+ *     ADRS.setType(0);   # Type = OTS hash address
+ *     ADRS.setOTSAddress(idx_sig);
+ *     sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig),
+ *                         M', getSEED(SK), ADRS);
+ *     Sig = sig_ots || auth;
+ *     return Sig;
+ * RFC 8391: 4.2.4, Algorithm 16: XMSSMT_sign
+ *     # Init
+ *     ADRS = toByte(0, 32);
+ *     SEED = getSEED(SK_MT);
+ *     SK_PRF = getSK_PRF(SK_MT);
+ *     idx_sig = getIdx(SK_MT);
+ *
+ *     # Update SK_MT
+ *     setIdx(SK_MT, idx_sig + 1);
+ *
+ *     # Message compression
+ *     byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32));
+ *     byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M);
+ *
+ *     # Sign
+ *     Sig_MT = idx_sig;
+ *     unsigned int idx_tree
+ *                   = (h - h / d) most significant bits of idx_sig;
+ *     unsigned int idx_leaf = (h / d) least significant bits of idx_sig;
+ *     SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, 0) || SK_PRF
+ *           || toByte(0, n) || SEED;
+ *     ADRS.setLayerAddress(0);
+ *     ADRS.setTreeAddress(idx_tree);
+ *     Sig_tmp = treeSig(M', SK, idx_leaf, ADRS);
+ *     Sig_MT = Sig_MT || r || Sig_tmp;
+ *     for ( j = 1; j < d; j++ ) {
+ *        root = treeHash(SK, 0, h / d, ADRS);
+ *        idx_leaf = (h / d) least significant bits of idx_tree;
+ *        idx_tree = (h - j * (h / d)) most significant bits of idx_tree;
+ *        SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, j) || SK_PRF
+ *               || toByte(0, n) || SEED;
+ *        ADRS.setLayerAddress(j);
+ *        ADRS.setTreeAddress(idx_tree);
+ *        Sig_tmp = treeSig(root, SK, idx_leaf, ADRS);
+ *        Sig_MT = Sig_MT || Sig_tmp;
+ *     }
+ *     return SK_MT || Sig_MT
+ *
+ * buildAuth from treeSig done inside treeHash as this is more efficient.
+ *
+ * @param [in]      state   XMSS/MT state including digest and parameters.
+ * @param [in]      m       Buffer holding message.
+ * @param [in]      mlen    Length of message in buffer.
+ * @param [in, out] sk      Secret/Private key.
+ * @param [out]     sig     Signature.
+ * @return  0 on success.
+ * @return  <0 on digest failure.
+ */
+int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen,
+    unsigned char* sk, unsigned char* sig)
+{
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 hs = params->sub_h;
+    const word16 hsn = (word16)hs * n;
+    const byte* sk_seed = sk + params->idx_len;
+    const byte* pk_seed = sk + params->idx_len + 3 * n;
+    wc_Idx idx;
+    byte* sig_r = sig + params->idx_len;
+    byte root[WC_XMSS_MAX_N];
+    unsigned int i;
+
+    WC_IDX_ZERO(idx);
+    /* Set all address values to zero and set type to OTS. */
+    XMEMSET(state->addr, 0, sizeof(HashAddress));
+    state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+
+    /* Copy the index into the signature data: Sig_MT = idx_sig. */
+    XMEMCPY(sig, sk, params->idx_len);
+
+    /* Read index from the secret key. */
+    WC_IDX_DECODE(idx, params->idx_len, sk, ret);
+    /* Validate index in secret key. */
+    if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) {
+        /* Set index to maximum value to distinguish from valid value. */
+        XMEMSET(sk, 0xFF, params->idx_len);
+        /* Zeroize the secret key. */
+        ForceZero(sk + params->idx_len, params->sk_len - params->idx_len);
+        ret = KEY_EXHAUSTED_E;
+    }
+
+    /* Update SK_MT */
+    if (ret == 0) {
+        /* Increment the index in the secret key. */
+        wc_idx_update(sk, params->idx_len);
+    }
+
+    /* Message compression */
+    if (ret == 0) {
+        const byte* sk_prf = sk + params->idx_len + n;
+
+        /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */
+        wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN);
+        wc_xmss_prf(state, sk_prf, state->buf, sig_r);
+        ret = state->ret;
+    }
+    if (ret == 0) {
+        const byte* pub_root = sk + params->idx_len + 2 * n;
+        /* byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M);
+         */
+        wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m,
+            mlen, root);
+        ret = state->ret;
+        /* Place WOTS+ signatures after index and 'r'. */
+        sig += params->idx_len + n;
+    }
+
+    /* Sign. */
+    for (i = 0; (ret == 0) && (i < params->d); i++) {
+        word32 idx_leaf = 0;
+
+        /* Set layer, tree and OTS leaf index into hash address. */
+        state->addr[XMSS_ADDR_LAYER] = i;
+        WC_IDX_SET_ADDR_TREE(idx, params->idx_len, hs, state->addr, idx_leaf);
+        /* treeSig || treeHash = sig_ots || auth */
+        state->addr[XMSS_ADDR_OTS] = idx_leaf;
+        /*   Create WOTS+ signature for tree into signature (sig_ots). */
+        wc_xmss_wots_sign(state, root, sk_seed, pk_seed, state->addr, sig);
+        ret = state->ret;
+        if (ret == 0) {
+            sig += params->wots_sig_len;
+            /*   Add authentication path (auth) and calc new root. */
+            wc_xmss_treehash(state, sk_seed, pk_seed, idx_leaf, state->addr,
+                root, sig);
+            ret = state->ret;
+            sig += hsn;
+        }
+    }
+
+    return ret;
+}
+
+#else
+
+/********************************************
+ * Fast C implementation
+ ********************************************/
+
+/* Tree hash data - needs to be unpacked from binary. */
+typedef struct TreeHash {
+    /* Next index to update in tree - max 20 bits. */
+    word32 nextIdx;
+    /* Number of stack entries used by tree - 0..<subtree height>. */
+    word8  used;
+    /* Tree is finished. */
+    word8  completed;
+} TreeHash;
+
+/* BDS state. */
+typedef struct BdsState {
+    /* Stack of nodes - subtree height + 1 nodes. */
+    byte*     stack;
+    /* Height of stack node - subtree height + 1 of 0..<subtree height - 1>. */
+    byte*     height;
+    /* Authentication path for next index - subtree height nodes. */
+    byte*     authPath;
+    /* Hashes of nodes kept - subtree height / 2 nodes. */
+    byte*     keep;
+    /* Tree hash instances - subtree height minus K instances. */
+    byte*     treeHash;
+    /* Hashes of nodes for tree hash - one for each tree hash instance. */
+    byte*     treeHashNode;
+    /* Hashes of nodes to retain - based on K parameter. */
+    byte*     retain;
+    /* Next leaf to calculate - max 20 bits. */
+    word32    next;
+    /* Current offset into stack - 0..<subtree height>. */
+    word8     offset;
+} BdsState;
+
+/* Index to BDS state accounting for swapping.
+ *
+ * @param [in] idx  Index of node.
+ * @param [in] i    Depth of tree.
+ * @param [in] hs   Height of subtree.
+ * @param [in] d    Depth/number of trees.
+ * @return  Index of working BDS state.
+ */
+#define BDS_IDX(idx, i, hs, d)      \
+    (((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? (i) : ((d) + (i)))
+/* Index to alternate BDS state accounting for swapping.
+ *
+ * @param [in] idx  Index of node.
+ * @param [in] i    Depth of tree.
+ * @param [in] hs   Height of subtree.
+ * @param [in] d    Depth/number of trees.
+ * @return  Index of alternate BDS state.
+ */
+#define BDS_ALT_IDX(idx, i, hs, d)  \
+    (((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? ((d) + (i)) : (i))
+
+/********************************************
+ * Tree Hash APIs
+ ********************************************/
+
+/* Initialize the tree hash data at specified index for the BDS state.
+ *
+ * @param [in, out] bds  BDS state.
+ * @param [in]      i    Index of tree hash.
+ */
+static void wc_xmss_bds_state_treehash_init(BdsState* bds, int i)
+{
+    byte* sk = bds->treeHash + i * 4;
+    c32to24(0, sk);
+    sk[3] = 0 | (1 << 7);
+}
+
+/* Set next index into tree hash data at specified index for the BDS state.
+ *
+ * @param [in, out] bds      BDS state.
+ * @param [in]      i        Index of tree hash.
+ * @param [in]      nextIdx  Next index for tree hash.
+ */
+static void wc_xmss_bds_state_treehash_set_next_idx(BdsState* bds, int i,
+    word32 nextIdx)
+{
+    byte* sk = bds->treeHash + i * 4;
+    c32to24(nextIdx, sk);
+    sk[3] = 0 | (0 << 7);
+}
+
+/* Mark tree hash, at specified index for the BDS state, as complete.
+ *
+ * @param [in, out] bds  BDS state.
+ * @param [in]      i    Index of tree hash.
+ */
+static void wc_xmss_bds_state_treehash_complete(BdsState* bds, int i)
+{
+    byte* sk = bds->treeHash + i * 4;
+    sk[3] |= 1 << 7;
+}
+
+/* Get the tree hash data at specified index for the BDS state.
+ *
+ * @param [in]  bds       BDS state.
+ * @param [in]  i         Index of tree hash.
+ * @param [out] treeHash  Tree hash instance to fill out.
+ */
+static void wc_xmss_bds_state_treehash_get(BdsState* bds, int i,
+    TreeHash* treeHash)
+{
+    byte* sk = bds->treeHash + i * 4;
+    ato24(sk, &treeHash->nextIdx);
+    treeHash->used = sk[3] & 0x7f;
+    treeHash->completed = sk[3] >> 7;
+}
+
+/* Set the tree hash data at specified index for the BDS state.
+ *
+ * @param [in, out]  bds       BDS state.
+ * @param [in]       i         Index of tree hash.
+ * @param [in]       treeHash  Tree hash data.
+ */
+static void wc_xmss_bds_state_treehash_set(BdsState* bds, int i,
+    TreeHash* treeHash)
+{
+    byte* sk = bds->treeHash + i * 4;
+    c32to24(treeHash->nextIdx, sk);
+    sk[3] = treeHash->used | (treeHash->completed << 7);
+}
+
+/********************************************
+ * BDS State APIs
+ ********************************************/
+
+/* Allocate memory for BDS state.
+ *
+ * When using a static BDS state (XMSS) then pass in handle to data for bds.
+ *
+ * @param [in]      params    XMSS/MT parameters.
+ * @param [in, out] bds       Handle to BDS state. May be NULL if not allocated.
+ * @return  0 on success.
+ * @return  MEMORY_E on dynamic memory allocation failure.
+ */
+static int wc_xmss_bds_state_alloc(const XmssParams* params, BdsState** bds)
+{
+    const word8 cnt = 2 * params->d - 1;
+    int ret = 0;
+
+    if (*bds == NULL) {
+        /* Allocate memory for BDS states. */
+        *bds = (BdsState*)XMALLOC(sizeof(BdsState) * cnt, NULL,
+            DYNAMIC_TYPE_TMP_BUFFER);
+        if (*bds == NULL) {
+            ret = MEMORY_E;
+        }
+    }
+
+    return ret;
+}
+
+/* Dispose of allocated memory associated with BDS state.
+ *
+ * @param [in] bds    BDS state.
+ */
+static void wc_xmss_bds_state_free(BdsState* bds)
+{
+    /* BDS states was allocated - must free. */
+    XFREE(bds, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+/* Load the BDS state from the secret/private key.
+ *
+ * @param [in]  state      XMSS/MT state including digest and parameters.
+ * @param [in]  sk         Secret/private key.
+ * @param [out] bds        BDS states.
+ * @param [out] wots_sigs  WOTS signatures when XMSS^MT.
+ */
+static void wc_xmss_bds_state_load(const XmssState* state, byte* sk,
+    BdsState* bds, byte** wots_sigs)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+    const word8 k = params->bds_k;
+    const word32 retainLen = XMSS_RETAIN_LEN(k, n);
+    int i;
+
+    /* Skip past standard SK = idx || wots_sk || SK_PRF || root || SEED; */
+    sk += params->idx_len + 4 * n;
+
+    for (i = 0; i < 2 * (int)params->d - 1; i++) {
+        /* Set pointers into SK. */
+        bds[i].stack = sk;
+        sk += (hs + 1) * n;
+        bds[i].height = sk;
+        sk += hs + 1;
+        bds[i].authPath = sk;
+        sk += hs * n;
+        bds[i].keep = sk;
+        sk += (hs >> 1) * n;
+        bds[i].treeHash = sk;
+        sk += hsk * 4;
+        bds[i].treeHashNode = sk;
+        sk += hsk * n;
+        bds[i].retain = sk;
+        sk += retainLen;
+        /* Load values - big-endian encoded. */
+        ato24(sk, &bds[i].next);
+        sk += 3;
+        bds[i].offset = sk[0];
+        sk += 1;
+    }
+
+    if (wots_sigs != NULL) {
+        *wots_sigs = sk;
+    }
+}
+
+/* Store the BDS state into the secret/private key.
+ *
+ * @param [in]      state   XMSS/MT state including digest and parameters.
+ * @param [in, out] sk      Secret/private key.
+ * @param [in]      bds     BDS states.
+ */
+static void wc_xmss_bds_state_store(const XmssState* state, byte* sk,
+    BdsState* bds)
+{
+    int i;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+    const word8 k = params->bds_k;
+    const word32 skip = (hs + 1) * n +            /* BdsState.stack */
+                        hs + 1 +                  /* BdsState.height */
+                        hs * n +                  /* BdsState.authPath */
+                        (hs >> 1) * n +           /* BdsState.keep */
+                        hsk * 4 +                 /* BdsState.treeHash */
+                        hsk * n +                 /* BdsState.treeHashNode */
+                        XMSS_RETAIN_LEN(k, n);    /* BdsState.retain */
+
+    /* Ignore standard SK = idx || wots_sk || SK_PRF || root || SEED; */
+    sk += params->idx_len + 4 * n;
+
+    for (i = 0; i < 2 * (int)params->d - 1; i++) {
+        /* Skip pointers into sk. */
+        sk += skip;
+        /* Save values - big-endian encoded. */
+        c32to24(bds[i].next, sk);
+        sk += 3;
+        sk[0] = bds[i].offset;
+        sk += 1;
+    }
+}
+
+/********************************************
+ * BDS
+ ********************************************/
+
+/* Compute node at next index.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9: treeHash
+ *       ...
+ *       ADRS.setType(0);   # Type = OTS hash address
+ *       ADRS.setOTSAddress(s + i);
+ *       pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS);
+ *       ADRS.setType(1);   # Type = L-tree address
+ *       ADRS.setLTreeAddress(s + i);
+ *       node = ltree(pk, SEED, ADRS);
+ *       ADRS.setType(2);   # Type = hash tree address
+ *       ADRS.setTreeHeight(0);
+ *       ADRS.setTreeIndex(i + s);
+ *       while ( Top node on Stack has same height t' as node ) {
+ *          ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *          node = RAND_HASH(Stack.pop(), node, SEED, ADRS);
+ *          ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
+ *       }
+ *       Stack.push(node);
+ *       ...
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  bds      BDS state.
+ * @param [in]  sk_seed  Random secret/private seed.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] root     Root node.
+ */
+static void wc_xmss_bds_next_idx(XmssState* state, BdsState* bds,
+    const byte* sk_seed, const byte* pk_seed, HashAddress addr, int i,
+    word8* height, word8* offset, word8** sp)
+{
+    const XmssParams* params = state->params;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+    const word8 n = params->n;
+    word8 o = *offset;
+    word8* node = *sp;
+    word8 h;
+
+    /* Calculate WOTS+ public key. */
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+    addr[XMSS_ADDR_OTS] = i;
+    wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
+    /* Calculate public value. */
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
+    wc_xmss_ltree(state, state->pk, pk_seed, addr, node);
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
+    addr[XMSS_ADDR_TREE_ZERO] = 0;
+
+    /* Initial height at this offset is 0. */
+    h = height[o] = 0;
+    /* HDSS, Section 4.5, 2: TREEHASH[h].push(v[h][3])
+     * Copy right node to tree hash nodes if second right node. */
+    if ((hsk > 0) && (i == 3)) {
+        XMEMCPY(bds->treeHashNode, node + n, n);
+    }
+
+    /* Top node on Stack has same height t' as node. */
+    while ((o >= 1) && (h == height[o - 1])) {
+        /* HDSS, Section 4.5, 1: AUTH[h] = v[h][1], h = 0,...,H-1.
+         * Cache left node if on authentication path. */
+        if ((i >> h) == 1) {
+            XMEMCPY(bds->authPath + h * n, node, n);
+        }
+        /* This is a right node. */
+        else if (h < hsk) {
+            /* HDSS, Section 4.5, 2: TREEHASH[h].push(v[h][3])
+             * Copy right node to tree hash if second right node. */
+            if ((i >> h) == 3) {
+                XMEMCPY(bds->treeHashNode + h * n, node, n);
+            }
+        }
+        else {
+            /* HDSS, Section 4.5, 3: RETAIN[h].push(v[j][2j+3] for
+             *   h = H-K,...,H-2 and j = 2^(H-h-1)-2,...,0.
+             * Retain high right nodes.
+             */
+            word32 ro = (1 << (hs - 1 - h)) + h - hs + (((i >> h) - 3) >> 1);
+            XMEMCPY(bds->retain + ro * n, node, n);
+        }
+
+        node -= n;
+        /* Calculate hash of node. */
+        addr[XMSS_ADDR_TREE_HEIGHT] = h;
+        addr[XMSS_ADDR_TREE_INDEX] = i >> (h + 1);
+        wc_xmss_rand_hash(state, node, pk_seed, addr, node);
+
+        /* Update offset and height. */
+        o--;
+        h = ++height[o];
+    }
+
+    *offset = o;
+    *sp = node;
+}
+
+/* Compute initial Merkle tree and store nodes.
+ *
+ * HDSS, Section 4.5, The algorithm, Initialization.
+ *   1. We store the authentication path for the first leaf (s = 0):
+ *   AUTH[h] = v[h][1], h = 0,...,H-1.
+ *   2. Depending on the parameter K, we store the next right authentication
+ *   node for each height h = 0,...,H-K-1 in the treehash instances:
+ *   TREEHASH[h].push(v[h][3]).
+ *   3. Finally we store the right authentication nodes clode to the root using
+ *   the stacks RETAIN[h]:
+ *   RETAIN[h].push(v[j][2j+3] for h = H-K,...,H-2 and j = 2^(H-h-1)-2,...,0.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9: treeHash
+ *     if( s % (1 << t) != 0 ) return -1;
+ *     for ( i = 0; i < 2^t; i++ ) {
+ *       SEED = getSEED(SK);
+ *       [Compute node at next index]
+ *     }
+ *     return Stack.pop();
+ *
+ * @param [in]  state    XMSS/MT state including digest and parameters.
+ * @param [in]  bds      BDS state.
+ * @param [in]  sk_seed  Random secret/private seed.
+ * @param [in]  pk_seed  Random public seed.
+ * @param [in]  addr     Hash address.
+ * @param [out] root     Root node.
+ */
+static void wc_xmss_bds_treehash_initial(XmssState* state, BdsState* bds,
+    const byte* sk_seed, const byte* pk_seed, const HashAddress addr,
+    byte* root)
+{
+    const XmssParams* params = state->params;
+    const word8 hsk = params->sub_h - params->bds_k;
+    const word8 n = params->n;
+    word8* node = state->stack;
+    HashAddress addrCopy;
+    word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
+    word8 offset = 0;
+    word32 maxIdx = (word32)1 << params->sub_h;
+    word32 i;
+
+    /* First signing index will be 0 - setup BDS state. */
+    bds->offset = 0;
+    bds->next = 0;
+    /* Reset the hash tree status. */
+    for (i = 0; i < hsk; i++) {
+        wc_xmss_bds_state_treehash_init(bds, i);
+    }
+
+    /* Copy hash address into local. */
+    XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr);
+
+    /* Compute each node in tree. */
+    for (i = 0; i < maxIdx; i++) {
+        wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, i, height,
+            &offset, &node);
+        offset++;
+        node += n;
+        /* Rest the hash address for reuse. */
+        addrCopy[XMSS_ADDR_TREE_HEIGHT] = 0;
+        addrCopy[XMSS_ADDR_TREE_INDEX] = 0;
+    }
+
+    /* Copy the root node. */
+    XMEMCPY(root, state->stack, n);
+}
+
+/* Update internal nodes of Merkle tree at next index.
+ *
+ * RFC 8391: 4.1.6, Algorithm 9: treeHash
+ *       ...
+ *       SEED = getSEED(SK);
+ *       ADRS.setType(0);   # Type = OTS hash address
+ *       ADRS.setOTSAddress(s + i);
+ *       pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS);
+ *       ADRS.setType(1);   # Type = L-tree address
+ *       ADRS.setLTreeAddress(s + i);
+ *       node = ltree(pk, SEED, ADRS);
+ *       ADRS.setType(2);   # Type = hash tree address
+ *       ADRS.setTreeHeight(0);
+ *       ADRS.setTreeIndex(i + s);
+ *       while ( Top node on Stack has same height t' as node ) {
+ *          ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *          node = RAND_HASH(Stack.pop(), node, SEED, ADRS);
+ *          ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
+ *       }
+ *       Stack.push(node);
+ *
+ * @param [in]      state    XMSS/MT state including digest and parameters.
+ * @param [in, out] bds      BDS state.
+ * @param [in]      height   Height of nodes to update.
+ * @param [in]      sk_seed  Random secret/private seed.
+ * @param [in]      pk_seed  Random public seed.
+ * @param [in]      addr     Hash address.
+ */
+static void wc_xmss_bds_treehash_update(XmssState* state, BdsState* bds,
+    word8 height, const byte* sk_seed, const byte* pk_seed,
+    const HashAddress addr)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    HashAddress addrLocal;
+    TreeHash treeHash[1];
+    byte* sp = bds->stack + bds->offset * n;
+    byte* node = state->stack + WC_XMSS_MAX_STACK_LEN - n;
+    word8 h;
+
+    /* Get the tree hash data. */
+    wc_xmss_bds_state_treehash_get(bds, height, treeHash);
+    /* Copy hash address into local as OTS type. */
+    XMSS_ADDR_OTS_SET_SUBTREE(addrLocal, addr);
+    /* Calculate WOTS+ public key. */
+    addrLocal[XMSS_ADDR_OTS] = treeHash->nextIdx;
+    wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addrLocal, state->pk);
+    /* Calculate public value. */
+    addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
+    wc_xmss_ltree(state, state->pk, pk_seed, addrLocal, node);
+    addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
+    addrLocal[XMSS_ADDR_TREE_ZERO] = 0;
+
+    /* Initial height is 0. */
+    h = 0;
+
+    /* Top node on Stack has same height t' as node. */
+    while ((treeHash->used > 0) && (h == bds->height[bds->offset - 1])) {
+        sp -= n;
+        /* Copy from stack to before last calculated node. */
+        node -= n;
+        XMEMCPY(node, sp, n);
+
+        /* Calculate hash of node. */
+        addrLocal[XMSS_ADDR_TREE_HEIGHT] = h;
+        addrLocal[XMSS_ADDR_TREE_INDEX] = treeHash->nextIdx >> (h + 1);
+        wc_xmss_rand_hash(state, node, pk_seed, addrLocal, node);
+
+        /* Update used, offset and height. */
+        treeHash->used--;
+        bds->offset--;
+        h++;
+    }
+
+    /* Check whether we reached the height we wanted to update. */
+    if (h == height) {
+        /* Cache node. */
+        XMEMCPY(bds->treeHashNode + height * n, node, n);
+        treeHash->completed = 1;
+    }
+    else {
+        /* Push calculated node onto stack. */
+        XMEMCPY(sp, node, n);
+        treeHash->used++;
+        /* Update BDS state. */
+        bds->height[bds->offset] = h;
+        bds->offset++;
+        treeHash->nextIdx++;
+    }
+
+    /* Set the tree hash data back. */
+    wc_xmss_bds_state_treehash_set(bds, height, treeHash);
+}
+
+/* Updates hash trees that need it most.
+ *
+ * Algorithm 4.6: Authentication path computation, Step 5.
+ *
+ * @param [in]      state    XMSS/MT state including digest and parameters.
+ * @param [in, out] bds      BDS state.
+ * @param [in]      updates  Current number of updates.
+ * @param [in]      sk_seed  Random secret/private seed.
+ * @param [in]      pk_seed  Random public seed.
+ * @param [in]      addr     Hash address.
+ * @return  Number of available updates.
+ */
+static word8 wc_xmss_bds_treehash_updates(XmssState* state, BdsState* bds,
+    word8 updates, const byte* sk_seed, const byte* pk_seed,
+    const HashAddress addr)
+{
+    const XmssParams* params = state->params;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+
+    while (updates > 0) {
+        word8 minH = hs;
+        word8 h = hsk;
+        word8 i;
+
+        /* Step 5.a. k <- min{ h: TREEHASH(h).height() =
+                                  min[j=0..H-K-1]{TREEHASH(j.height()} } */
+        for (i = 0; i < hsk; i++) {
+            TreeHash treeHash[1];
+
+            wc_xmss_bds_state_treehash_get(bds, i, treeHash);
+
+            if (treeHash->completed) {
+                /* Finished - ignore. */
+            }
+            else if (treeHash->used == 0) {
+                /* None used, low height. */
+                if (i < minH) {
+                    h = i;
+                    minH = i;
+                }
+            }
+            /* Find the height of lowest in cache. */
+            else {
+                word8 j;
+                word8 lowH = hs;
+                byte* height = bds->height + bds->offset - treeHash->used;
+
+                for (j = 0; j < treeHash->used; j++) {
+                    lowH = min(height[j], lowH);
+                }
+                if (lowH < minH) {
+                    /* New lowest height. */
+                    h = i;
+                    minH = lowH;
+                }
+            }
+        }
+        /* If none lower, then stop. */
+        if (h == hsk) {
+            break;
+        }
+
+        /* Step 5.b. TREEHASH(k).update() */
+        /* Update tree to the lowest height. */
+        wc_xmss_bds_treehash_update(state, bds, h, sk_seed, pk_seed, addr);
+        updates--;
+    }
+    return updates;
+}
+
+/* Update BDS at next leaf.
+ *
+ * Don't do anything if processed all leaves.
+ *
+ * @param [in]      state     XMSS/MT state including digest and parameters.
+ * @param [in, out] bds       BDS state.
+ * @param [in]      sk_seed   Random secret/private seed.
+ * @param [in]      pk_seed   Random public seed.
+ * @param [in]      addr      Hash address.
+ */
+static void wc_xmss_bds_update(XmssState* state, BdsState* bds,
+    const byte* sk_seed, const byte* pk_seed, const HashAddress addr)
+{
+    if (bds->next < ((word32)1 << state->params->sub_h)) {
+        const XmssParams* params = state->params;
+        byte* sp = bds->stack + bds->offset * params->n;
+        HashAddress addrCopy;
+
+        XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr);
+        wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, bds->next,
+            bds->height, &bds->offset, &sp);
+        bds->offset++;
+        bds->next++;
+    }
+}
+
+/* Find index of lowest zero bit.
+ *
+ * Supports max up to 31.
+ *
+ * @param [in]  n    Number to evaluate.
+ * @param [in]  max  Max number of bits.
+ * @param [out] b    Next bit above first zero bit.
+ * @return  Index of lowest bit that is zero.
+ */
+static word8 wc_xmss_lowest_zero_bit_index(word32 n, word8 max, word8* b)
+{
+    word8 i;
+
+    /* Check each bit from lowest for a zero bit. */
+    for (i = 0; i < max; i++) {
+        if ((n & 1) == 0) {
+            break;
+        }
+        n >>= 1;
+    }
+
+    /* Return next bit after 0 bit. */
+    *b = (n >> 1) & 1;
+    return i;
+}
+
+/* Returns auth path for node leafIdx and computes for next leaf node.
+ *
+ * HDSS, Algorithm 4.6: Authentication path computation, Steps 1-4.
+ *
+ * @param [in]      state    XMSS/MT state including digest and parameters.
+ * @param [in, out] bds      BDS state.
+ * @param [in]      leafIdx  Current leaf index.
+ * @param [in]      sk_seed  Random secret/private seed.
+ * @param [in]      pk_seed  Random public seed.
+ * @param [in]      addr     Hash address.
+ */
+static void wc_xmss_bds_auth_path(XmssState* state, BdsState* bds,
+    const word32 leafIdx, const byte* sk_seed, const byte* pk_seed,
+    HashAddress addr)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+    word8 tau;
+    byte* node = state->encMsg;
+    word8 parent;
+
+    /* Step 1. Find the height of first left node in authentication path. */
+    tau = wc_xmss_lowest_zero_bit_index(leafIdx, hs, &parent);
+    if (tau == 0) {
+        /* Step 2. Keep node if parent is a left node.
+         *     if s/(2^tau+1) is even and tau < H-1 then KEEP[tau] <- AUTH[tau]
+         */
+        if (parent == 0) {
+            XMEMCPY(bds->keep, bds->authPath, n);
+        }
+
+        /* Step 3. if tau = 0 then AUTH[0] <- LEAFCALC(s) */
+        /* Calculate WOTS+ public key. */
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+        addr[XMSS_ADDR_OTS] = leafIdx;
+        wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
+        /* Calculate public value. */
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
+        wc_xmss_ltree(state, state->pk, pk_seed, addr, bds->authPath);
+    }
+    else {
+        byte* authPath;
+        byte* nodes;
+        word8 i;
+
+        authPath = bds->authPath + tau * n;
+        /* Step 4.a. <node> = AUTH[tau-1] || KEEP[tau-1]
+         * Only keeping half of nodes, so need to copy out before updating.
+         */
+        XMEMCPY(node, authPath - n, n);
+        XMEMCPY(node + n, bds->keep + ((tau - 1) >> 1) * n, n);
+
+        /* Step 2. Keep node if parent is a left node.
+         *     if s/(2^tau+1) is even and tau < H-1 then KEEP[tau] <- AUTH[tau]
+         */
+        if ((tau < hs - 1) && (parent == 0)) {
+            XMEMCPY(bds->keep + (tau >> 1) * n, authPath, n);
+        }
+
+        /* Step 4.a. AUTH[tau] <- g(<node>) */
+        addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
+        addr[XMSS_ADDR_TREE_ZERO] = 0;
+        addr[XMSS_ADDR_TREE_HEIGHT] = tau - 1;
+        addr[XMSS_ADDR_TREE_INDEX] = leafIdx >> tau;
+        wc_xmss_rand_hash(state, node, pk_seed, addr, authPath);
+
+        /* Step 4.b. <Calculate new right nodes on lower heights> */
+        authPath = bds->authPath;
+        nodes = bds->treeHashNode;
+        /*   for h = 0 to tau - 1 do */
+        for (i = 0; i < tau; i++) {
+            /*   if h < H - K then AUTH[h] <- TREEHASH[h].pop()*/
+            if (i < hsk) {
+                XMEMCPY(authPath, nodes, n);
+                nodes += n;
+            }
+            /*   if h >= H - K then AUTH[h] <- RETAIN[h].pop()*/
+            else {
+                word32 o = (1 << (hs - 1 - i)) + i - hs +
+                           (((leafIdx >> i) - 1) >> 1);
+                XMEMCPY(authPath, bds->retain + o * n, n);
+            }
+            authPath += n;
+        }
+
+        /* Step 4.c. Initialize treehash instances for heights:
+         *           0, ..., min{tau-1, H - K - 1} */
+        tau = min(tau, hsk);
+        for (i = 0; i < tau; i++) {
+            word32 startIdx = leafIdx + 1 + 3 * (1 << i);
+            if (startIdx < ((word32)1 << hs)) {
+                wc_xmss_bds_state_treehash_set_next_idx(bds, i, startIdx);
+            }
+        }
+    }
+}
+
+/********************************************
+ * XMSS
+ ********************************************/
+
+/* Derives XMSS key pair from seeds.
+ *
+ * RFC 8391: 4.1.7, Algorithm 10: XMSS_keyGen.
+ *     ...
+ *     initialize SK_PRF with a uniformly random n-byte string;
+ *     setSK_PRF(SK, SK_PRF);
+ *
+ *     # Initialization for common contents
+ *     initialize SEED with a uniformly random n-byte string;
+ *     setSEED(SK, SEED);
+ *     setWOTS_SK(SK, wots_sk));
+ *     ADRS = toByte(0, 32);
+ *     root = treeHash(SK, 0, h, ADRS);
+ *
+ *     SK = idx || wots_sk || SK_PRF || root || SEED;
+ *     PK = OID || root || SEED;
+ *     return (SK || PK);
+ *
+ * HDSS, Section 4.5, The algorithm, Initialization.
+ *
+ * wots_sk, SK_PRF and SEED passed in as seed.
+ * Store seed for wots_sk instead of generated wots_sk.
+ * OID not stored in PK this is handled in upper layer.
+ * BDS state is appended to SK:
+ *     SK = idx || wots_sk || SK_PRF || root || SEED || BDS_STATE;
+ *
+ * @param [in]  state  XMSS/MT state including digest and parameters.
+ * @param [in]  seed   Secret/Private and public seed.
+ * @param [out] sk     Secret key.
+ * @param [out] pk     Public key.
+ * @return  0 on success.
+ * @return  MEMORY_E on dynamic memory allocation failure.
+ * @return  <0 on digest failure.
+ */
+int wc_xmss_keygen(XmssState* state, const unsigned char* seed,
+    unsigned char* sk, unsigned char* pk)
+{
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    /* Offset of root node in public key. */
+    byte* pk_root = pk;
+#ifdef WOLFSSL_SMALL_STACK
+    BdsState* bds = NULL;
+#else
+    BdsState bds[1];
+#endif
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Allocate memory for tree hash instances and put in BDS state. */
+    ret = wc_xmss_bds_state_alloc(params, &bds);
+    if (ret == 0)
+#endif
+    {
+        /* Offsets into seed. */
+        const byte* seed_priv = seed;
+        const byte* seed_pub = seed + 2 * n;
+        /* Offsets into secret/private key. */
+        word32* sk_idx = (word32*)sk;
+        byte* sk_seeds = sk + params->idx_len;
+        /* Offsets into public key. */
+        byte* pk_seed = pk + n;
+
+        /* Setup pointers into sk - assumes sk is initialized to zeros. */
+        wc_xmss_bds_state_load(state, sk, bds, NULL);
+
+        /* Set first index to 0 in private key. idx_len always 4. */
+        *sk_idx = 0;
+        /* Set private key seed and private key for PRF in to private key. */
+        XMEMCPY(sk_seeds, seed_priv, 2 * n);
+        /* Set public key seed into public key. */
+        XMEMCPY(pk_seed, seed_pub, n);
+
+        /* Set all address values to zero. */
+        XMEMSET(state->addr, 0, sizeof(HashAddress));
+        /* Hash address layer is 0. */
+        /* Compute root node into public key. */
+        wc_xmss_bds_treehash_initial(state, bds, sk_seeds, pk_seed,
+            state->addr, pk_root);
+        /* Return any errors that occurred during hashing. */
+        ret = state->ret;
+    }
+    if (ret == 0) {
+        /* Offset of root node in private key. */
+        byte* sk_root = sk + params->idx_len + 2 * n;
+
+        /* Append public key (root node and public seed) to private key. */
+        XMEMCPY(sk_root, pk_root, 2 * n);
+
+        /* Store BDS state back into secret/private key. */
+        wc_xmss_bds_state_store(state, sk, bds);
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Dispose of allocated data of BDS states. */
+    wc_xmss_bds_state_free(bds);
+#endif
+    return ret;
+#else
+    (void)state;
+    (void)pk;
+    (void)sk;
+    (void)seed;
+
+    return NOT_COMPILED_IN;
+#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */
+}
+
+/* Sign a message with XMSS.
+ *
+ * RFC 8391: 4.1.9, Algorithm 11: treeSig
+ *     ...
+ *     ADRS.setType(0);   # Type = OTS hash address
+ *     ADRS.setOTSAddress(idx_sig);
+ *     sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig),
+ *                         M', getSEED(SK), ADRS);
+ *     Sig = sig_ots || auth;
+ *     return Sig;
+ * RFC 8391: 4.1.9, Algorithm 12: XMSS_sign
+ *     idx_sig = getIdx(SK);
+ *     setIdx(SK, idx_sig + 1);
+ *     ADRS = toByte(0, 32);
+ *     byte[n] r = PRF(getSK_PRF(SK), toByte(idx_sig, 32));
+ *     byte[n] M' = H_msg(r || getRoot(SK) || (toByte(idx_sig, n)), M);
+ *     Sig = idx_sig || r || treeSig(M', SK, idx_sig, ADRS);
+ *     return (SK || Sig);
+ *
+ * HDSS, Section 4.5, The algorithm, Update and output phase.
+ *
+ * 'auth' was built at key generation or after computing previous signature.
+ * Build next authentication path after signature created.
+ *
+ * @param [in]      state   XMSS/MT state including digest and parameters.
+ * @param [in]      m       Buffer holding message.
+ * @param [in]      mlen    Length of message in buffer.
+ * @param [in, out] sk      Secret/Private key.
+ * @param [out]     sm      Signature and message data.
+ * @param [in, out] smlen   On in, length of signature and message buffer.
+ *                          On out, length of signature and message data.
+ * @return  0 on success.
+ * @return  <0 on digest failure.
+ */
+int wc_xmss_sign(XmssState* state, const unsigned char* m, word32 mlen,
+    unsigned char* sk, unsigned char* sig)
+{
+#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 h = params->h;
+    const word8 hk = params->h - params->bds_k;
+    const byte* sk_seed = sk + XMSS_IDX_LEN;
+    const byte* pk_seed = sk + XMSS_IDX_LEN + 3 * n;
+    byte node[WC_XMSS_MAX_N];
+    word32 idx;
+    byte* sig_r = sig + XMSS_IDX_LEN;
+#ifdef WOLFSSL_SMALL_STACK
+    BdsState* bds = NULL;
+#else
+    BdsState bds[1];
+#endif
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Allocate memory for tree hash instances and put in BDS state. */
+    ret = wc_xmss_bds_state_alloc(params, &bds);
+    if (ret == 0)
+#endif
+    {
+        /* Load the BDS state from secret/private key. */
+        wc_xmss_bds_state_load(state, sk, bds, NULL);
+
+        /* Copy the index into the signature data: Sig = idx_sig || ... */
+        *((word32*)sig) = *((word32*)sk);
+        /* Read index from the secret key. */
+        ato32(sk, &idx);
+
+        /* Check index is valid. */
+        if (IDX32_INVALID(idx, XMSS_IDX_LEN, h)) {
+            /* Set index to maximum value to distinguish from valid value. */
+            XMEMSET(sk, 0xFF, XMSS_IDX_LEN);
+            /* Zeroize the secret key. */
+            ForceZero(sk + XMSS_IDX_LEN, params->sk_len - XMSS_IDX_LEN);
+            ret = KEY_EXHAUSTED_E;
+        }
+    }
+
+    /* Update SK_MT */
+    if (ret == 0) {
+        /* Increment the index in the secret key. */
+        c32toa(idx + 1, sk);
+    }
+
+    /* Message compression */
+    if (ret == 0) {
+        const byte* sk_prf = sk + XMSS_IDX_LEN + n;
+
+        /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */
+        wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN);
+        wc_xmss_prf(state, sk_prf, state->buf, sig_r);
+        ret = state->ret;
+    }
+    if (ret == 0) {
+        const byte* pub_root = sk + XMSS_IDX_LEN + 2 * n;
+
+        /* Compute the message hash. */
+        wc_xmss_hash_message(state, sig_r, pub_root, sig, XMSS_IDX_LEN, m, mlen,
+            node);
+        ret = state->ret;
+        /* Place new signature data after index and 'r'. */
+        sig += XMSS_IDX_LEN + n;
+    }
+
+    if (ret == 0) {
+        /* Set all address values to zero and set type to OTS. */
+        XMEMSET(state->addr, 0, sizeof(HashAddress));
+        state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+        /* treeSig || treeHash = sig_ots || auth */
+        state->addr[XMSS_ADDR_OTS] = idx;
+        /*   Create WOTS+ signature for tree into signature (sig_ots). */
+        wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig);
+        ret = state->ret;
+    }
+    if (ret == 0) {
+        sig += params->wots_sig_len;
+        /*   Add authentication path (auth) and calc new root. */
+        XMEMCPY(sig, bds->authPath, h * n);
+        ret = state->ret;
+    }
+
+    if (ret == 0) {
+        /* Update BDS state - update authentication path for next index. */
+        /* Check not last node. */
+        if (idx < ((word32)1 << h) - 1) {
+            /* Calculate next authentication path node. */
+            wc_xmss_bds_auth_path(state, bds, idx, sk_seed, pk_seed,
+                state->addr);
+            ret = state->ret;
+            if (ret == 0) {
+                /* Algorithm 4.6: Step 5. */
+                wc_xmss_bds_treehash_updates(state, bds, hk >> 1, sk_seed,
+                    pk_seed, state->addr);
+                ret = state->ret;
+            }
+        }
+    }
+    if (ret == 0) {
+        /* Store BDS state back into secret/private key. */
+        wc_xmss_bds_state_store(state, sk, bds);
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    /* Dispose of allocated data of BDS states. */
+    wc_xmss_bds_state_free(bds);
+#endif
+    return ret;
+#else
+    (void)state;
+    (void)m;
+    (void)mlen;
+    (void)sk;
+    (void)sig;
+
+    return NOT_COMPILED_IN;
+#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */
+}
+
+/********************************************
+ * XMSS^MT
+ ********************************************/
+
+/* Generate a XMSS^MT key pair from seeds.
+ *
+ * RFC 8391: 4.2.2, Algorithm 15: XMSS^MT_keyGen.
+ *     ...
+ *     # Example initialization
+ *     idx_MT = 0;
+ *     setIdx(SK_MT, idx_MT);
+ *     initialize SK_PRF with a uniformly random n-byte string;
+ *     setSK_PRF(SK_MT, SK_PRF);
+ *     initialize SEED with a uniformly random n-byte string;
+ *     setSEED(SK_MT, SEED);
+ *
+ *     # Generate reduced XMSS private keys
+ *     ADRS = toByte(0, 32);
+ *     for ( layer = 0; layer < d; layer++ ) {
+ *        ADRS.setLayerAddress(layer);
+ *        for ( tree = 0; tree <
+ *              (1 << ((d - 1 - layer) * (h / d)));
+ *              tree++ ) {
+ *           ADRS.setTreeAddress(tree);
+ *           for ( i = 0; i < 2^(h / d); i++ ) {
+ *             wots_sk[i] = WOTS_genSK();
+ *           }
+ *           setXMSS_SK(SK_MT, wots_sk, tree, layer);
+ *        }
+ *     }
+ *
+ *     SK = getXMSS_SK(SK_MT, 0, d - 1);
+ *     setSEED(SK, SEED);
+ *     root = treeHash(SK, 0, h / d, ADRS);
+ *     setRoot(SK_MT, root);
+ *
+ *     PK_MT = OID || root || SEED;
+ *     return (SK_MT || PK_MT);
+ *
+ * HDSS, Section 4.5, The algorithm, Initialization.
+ * OPX, Section 2, Key Generation.
+ *
+ * wots_sk, SK_PRF and SEED passed in as seed.
+ * Store seed for wots_sk instead of generated wots_sk.
+ * OID not stored in PK this is handled in upper layer.
+ * BDS state is appended to SK:
+ *     SK = idx || wots_sk || SK_PRF || root || SEED || BDS_STATE;
+ *
+ * @param [in]  state   XMSS/MT state including digest and parameters.
+ * @param [in]  seed    Secret/Private and public seed.
+ * @param [out] sk      Secret key.
+ * @param [out] pk      Public key.
+ * @return  0 on success.
+ * @return  MEMORY_E on dynamic memory allocation failure.
+ * @return  <0 on digest failure.
+ */
+int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed,
+    unsigned char* sk, unsigned char* pk)
+{
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    unsigned char* sk_seed = sk + params->idx_len;
+    unsigned char* pk_root = pk;
+    unsigned char* pk_seed = pk + n;
+    word8 i;
+    byte* wots_sigs;
+    BdsState* bds = NULL;
+
+    /* Allocate memory for BDS states and tree hash instances. */
+    ret = wc_xmss_bds_state_alloc(params, &bds);
+    if (ret == 0) {
+        /* Offsets into seed. */
+        const byte* seed_priv = seed;
+        const byte* seed_pub  = seed + 2 * params->n;
+
+        /* Load the BDS state from secret/private key. */
+        wc_xmss_bds_state_load(state, sk, bds, &wots_sigs);
+
+        /* Set first index to 0 in private key. */
+        XMEMSET(sk, 0, params->idx_len);
+        /* Set private key seed and private key for PRF in to private key. */
+        XMEMCPY(sk_seed, seed_priv, 2 * n);
+        /* Set public key seed into public key. */
+        XMEMCPY(pk_seed, seed_pub, n);
+
+        /* Set all address values to zero. */
+        XMEMSET(state->addr, 0, sizeof(HashAddress));
+        /* Hash address layer is 0 = bottom-most layer. */
+    }
+
+    /* Setup state and compute WOTS+ signatures for all but top-most subtree. */
+    for (i = 0; (ret == 0) && (i < params->d - 1); i++) {
+        /* Compute root for subtree. */
+        wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed,
+            state->addr, pk_root);
+        ret = state->ret;
+        if (ret == 0) {
+            /* Create signature for subtree for first index. */
+            state->addr[XMSS_ADDR_LAYER] = i+1;
+            wc_xmss_wots_sign(state, pk_root, sk_seed, pk_seed, state->addr,
+                wots_sigs + i * params->wots_sig_len);
+            ret = state->ret;
+        }
+    }
+    if (ret == 0) {
+        /* Compute root for top-most subtree. */
+        wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed,
+            state->addr, pk_root);
+        /* Return any errors that occurred during hashing. */
+        ret = state->ret;
+    }
+
+    if (ret == 0) {
+        /* Offset of root node in private key. */
+        unsigned char* sk_root = sk_seed + 2 * n;
+
+        /* Append public key (root node and public seed) to private key. */
+        XMEMCPY(sk_root, pk_root, 2 * n);
+
+        /* Store BDS state back into secret/private key. */
+        wc_xmss_bds_state_store(state, sk, bds);
+    }
+
+    /* Dispose of allocated data of BDS states. */
+    wc_xmss_bds_state_free(bds);
+    return ret;
+}
+
+
+#if !defined(WORD64_AVAILABLE) && (WOLFSSL_XMSS_MAX_HEIGHT > 32)
+    #error "Support not available - use XMSS small code option"
+#endif
+
+#if (WOLFSSL_XMSS_MAX_HEIGHT > 32)
+    typedef word64 XmssIdx;
+    #define IDX_MAX_BITS    64
+#else
+    typedef word32 XmssIdx;
+    #define IDX_MAX_BITS    32
+#endif
+
+/* Decode index into word.
+ *
+ * @param [out] idx  Index from encoding.
+ * @param [in]  c    Count of bytes to decode to index.
+ * @param [in]  a    Array to decode from.
+ */
+static void xmss_idx_decode(XmssIdx* idx, word8 c, const unsigned char* a)
+{
+    word8 i;
+    XmssIdx n = 0;
+
+    for (i = 0; i < c; i++) {
+        n <<= 8;
+        n += a[i];
+    }
+
+    *idx = n;
+}
+
+/* Check whether index is valid.
+ *
+ * @param [in] i  Index to check.
+ * @param [in] h  Full tree Height.
+ */
+static int xmss_idx_invalid(XmssIdx i, word8 h)
+{
+    return ((i + 1) >> h) != 0;
+}
+
+/* Get tree and leaf index from index.
+ *
+ * @param [in]  i  Index to split.
+ * @param [in]  h  Tree height.
+ * @param [out] t  Tree index.
+ * @param [out] l  Leaf index.
+ */
+static void xmss_idx_get_tree_leaf(XmssIdx i, word8 h, XmssIdx* t, word32* l)
+{
+    *l = (word32)i & (((word32)1 << h) - 1);
+    *t = i >> h;
+}
+
+/* Set the index into address as the tree index.
+ *
+ * @param [in]      i  Tree index.
+ * @param [in, out] a  Hash address.
+ */
+static void xmss_idx_set_addr_tree(XmssIdx i, HashAddress a)
+{
+#if IDX_MAX_BITS == 32
+    a[XMSS_ADDR_TREE_HI] = 0;
+    a[XMSS_ADDR_TREE]    = i;
+#else
+    a[XMSS_ADDR_TREE_HI] = (word32)(i >> 32);
+    a[XMSS_ADDR_TREE]    = (word32)(i      );
+#endif
+}
+
+/* Sign message with XMSS^MT.
+ *
+ * RFC 8391: 4.1.9, Algorithm 11: treeSig
+ *     ...
+ *     ADRS.setType(0);   # Type = OTS hash address
+ *     ADRS.setOTSAddress(idx_sig);
+ *     sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig),
+ *                         M', getSEED(SK), ADRS);
+ *     Sig = sig_ots || auth;
+ *     return Sig;
+ * RFC 8391: 4.2.4, Algorithm 16: XMSS^MT_sign.
+ *      ...
+ *      # Init
+ *     ADRS = toByte(0, 32);
+ *     SEED = getSEED(SK_MT);
+ *     SK_PRF = getSK_PRF(SK_MT);
+ *     idx_sig = getIdx(SK_MT);
+ *
+ *     # Update SK_MT
+ *     setIdx(SK_MT, idx_sig + 1);
+ *
+ *     # Message compression
+ *     byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32));
+ *     byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M);
+ *
+ *     # Sign
+ *     Sig_MT = idx_sig;
+ *     unsigned int idx_tree
+ *                   = (h - h / d) most significant bits of idx_sig;
+ *     unsigned int idx_leaf = (h / d) least significant bits of idx_sig;
+ *     SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, 0) || SK_PRF
+ *           || toByte(0, n) || SEED;
+ *     ADRS.setLayerAddress(0);
+ *     ADRS.setTreeAddress(idx_tree);
+ *     Sig_tmp = treeSig(M', SK, idx_leaf, ADRS);
+ *     Sig_MT = Sig_MT || r || Sig_tmp;
+ *     for ( j = 1; j < d; j++ ) {
+ *        root = treeHash(SK, 0, h / d, ADRS);
+ *        idx_leaf = (h / d) least significant bits of idx_tree;
+ *        idx_tree = (h - j * (h / d)) most significant bits of idx_tree;
+ *        SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, j) || SK_PRF
+ *               || toByte(0, n) || SEED;
+ *        ADRS.setLayerAddress(j);
+ *        ADRS.setTreeAddress(idx_tree);
+ *        Sig_tmp = treeSig(root, SK, idx_leaf, ADRS);
+ *        Sig_MT = Sig_MT || Sig_tmp;
+ *     }
+ *     return SK_MT || Sig_MT;
+ *
+ * 'auth' was built at key generation or after computing previous signature.
+ *
+ * @param [in]      state      XMSS/MT state including digest and parameters.
+ * @param [in, out] bds        BDS state.
+ * @param [in]      idx        Index to sign with.
+ * @param [in]      wots_sigs  Pre-computed WOTS+ signatures.
+ * @param [in]      m          Buffer holding message.
+ * @param [in]      mlen       Length of message in buffer.
+ * @param [in, out] sk         Secret/Private key.
+ * @param [out]     sig        Signature and message data.
+ * @return  0 on success.
+ * @return  <0 on digest failure.
+ */
+static int wc_xmssmt_sign_msg(XmssState* state, BdsState* bds, XmssIdx idx,
+    byte* wots_sigs, const unsigned char* m, word32 mlen, unsigned char* sk,
+    unsigned char* sig)
+{
+    int ret;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 hs = params->sub_h;
+    const word8 idx_len = params->idx_len;
+    const byte* sk_prf = sk + idx_len + n;
+    byte* sig_mt = sig;
+    byte* sig_r = sig + idx_len;
+    byte node[WC_XMSS_MAX_N];
+
+    /* Message compression */
+    /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */
+    wc_idx_copy(sig_mt, idx_len, state->buf, XMSS_PRF_M_LEN);
+    wc_xmss_prf(state, sk_prf, state->buf, sig_r);
+    ret = state->ret;
+    if (ret == 0) {
+        const byte* pub_root = sk + idx_len + 2 * n;
+        /* byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M);
+         */
+        wc_xmss_hash_message(state, sig_r, pub_root, sig, idx_len, m, mlen,
+            node);
+        ret = state->ret;
+        /* Place new signature data after index and 'r'. */
+        sig += idx_len + n;
+    }
+
+    /* Sign */
+    if (ret == 0) {
+        const byte* sk_seed = sk + idx_len;
+        const byte* pk_seed = sk + idx_len + 3 * n;
+        XmssIdx idx_tree;
+        word32 idx_leaf;
+
+        /* Set all address values to zero and set type to OTS. */
+        XMEMSET(state->addr, 0, sizeof(HashAddress));
+        state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+
+        /* Fist iteration - calculate signature. */
+        /* Set layer, tree and OTS leaf index into hash address. */
+        state->addr[XMSS_ADDR_LAYER] = 0;
+        xmss_idx_get_tree_leaf(idx, hs, &idx_tree, &idx_leaf);
+        xmss_idx_set_addr_tree(idx_tree, state->addr);
+        /* treeSig || treeHash = sig_ots || auth */
+        state->addr[XMSS_ADDR_OTS] = idx_leaf;
+        /*   Create WOTS+ signature for tree into signature (sig_ots). */
+        wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig);
+        ret = state->ret;
+    }
+    if (ret == 0) {
+        word8 i;
+
+        sig += params->wots_sig_len;
+        /*   Add authentication path. */
+        XMEMCPY(sig, bds[BDS_IDX(idx, 0, hs, params->d)].authPath, hs * n);
+        sig += hs * n;
+
+        /* Remaining iterations from storage. */
+        for (i = 1; i < params->d; i++) {
+            /* Copy out precomputed signature into signature (sig_ots). */
+            XMEMCPY(sig, wots_sigs + (i - 1) * params->wots_sig_len,
+                params->wots_sig_len);
+            sig += params->wots_sig_len;
+            /* Add authentication path (auth) and calc new root. */
+            XMEMCPY(sig, bds[BDS_IDX(idx, i, hs, params->d)].authPath, hs * n);
+            sig += hs * n;
+        }
+        ret = state->ret;
+    }
+
+    return ret;
+}
+
+/* Compute BDS state for signing next index.
+ *
+ * HDSS, Section 4.5, The algorithm, Update and output phase.
+ * OPX, Section 2, Signature Generation. Para 2 and 3.
+ *
+ * @param [in]      state      XMSS/MT state including digest and parameters.
+ * @param [in, out] bds        BDS state.
+ * @param [in]      idx        Index to sign with.
+ * @param [in]      wots_sigs  Pre-computed WOTS+ signatures.
+ * @param [in]      m          Buffer holding message.
+ * @param [in]      mlen       Length of message in buffer.
+ * @param [in, out] sk         Secret/Private key.
+ * @param [out]     sig        Signature and message data.
+ * @return  0 on success.
+ * @return  <0 on digest failure.
+ */
+static int wc_xmssmt_sign_next_idx(XmssState* state, BdsState* bds, XmssIdx idx,
+    byte* wots_sigs, unsigned char* sk)
+{
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const word8 h = params->h;
+    const word8 hs = params->sub_h;
+    const word8 hsk = params->sub_h - params->bds_k;
+    const byte* sk_seed = sk + params->idx_len;
+    const byte* pk_seed = sk + params->idx_len + 3 * n;
+    XmssIdx idx_tree;
+    int computeAuthPath = 1;
+    unsigned int updates;
+    word8 i;
+
+    /* Update BDS state - update authentication path for next index. */
+    /* HDSS, Algorithm 4.6, Step 5: repeat (H - K) / 2 times. */
+    updates = hsk >> 1;
+
+    idx_tree = (idx >> hs) + 1;
+    /* Check whether last tree. */
+    if (idx_tree < ((XmssIdx)1 << (h - hs))) {
+        /* Set hash address to next tree. */
+        state->addr[XMSS_ADDR_LAYER] = 0;
+        xmss_idx_set_addr_tree(idx_tree, state->addr);
+        /* Update BDS state. */
+        wc_xmss_bds_update(state, &bds[BDS_ALT_IDX(idx, 0, hs, params->d)],
+            sk_seed, pk_seed, state->addr);
+        ret = state->ret;
+    }
+
+    for (i = 0; (ret == 0) && (i < params->d); i++) {
+        word32 idx_leaf;
+        word8 bds_i = BDS_IDX(idx, i, hs, params->d);
+        word8 alt_i = BDS_ALT_IDX(idx, i, hs, params->d);
+
+        /* Check not last at height. */
+        if (((idx + 1) << (IDX_MAX_BITS - ((i + 1) * hs))) != 0) {
+            state->addr[XMSS_ADDR_LAYER] = i;
+            xmss_idx_get_tree_leaf(idx >> (hs * i), hs, &idx_tree, &idx_leaf);
+            xmss_idx_set_addr_tree(idx_tree, state->addr);
+            idx_tree++;
+
+            if (computeAuthPath) {
+                /* Compute authentication path for tree. */
+                wc_xmss_bds_auth_path(state, &bds[bds_i], idx_leaf, sk_seed,
+                    pk_seed, state->addr);
+                ret = state->ret;
+                computeAuthPath = 0;
+            }
+
+            if (ret == 0) {
+                /* HDSS, Algorithm 4.6: Step 5. */
+                updates = wc_xmss_bds_treehash_updates(state, &bds[bds_i],
+                    updates, sk_seed, pk_seed, state->addr);
+                ret = state->ret;
+            }
+
+            /* Check tree not first, updates to do, tree not last at height and
+             * next leaf in alt state is not last. */
+            if ((ret == 0) && (i > 0) && (updates > 0) &&
+                    (idx_tree < ((XmssIdx)1 << (h - (hs * (i + 1))))) &&
+                    (bds[alt_i].next < ((word32)1 << h))) {
+                xmss_idx_set_addr_tree(idx_tree, state->addr);
+                /* Update alternative BDS state. */
+                wc_xmss_bds_update(state, &bds[alt_i], sk_seed, pk_seed,
+                    state->addr);
+                ret = state->ret;
+                updates--;
+            }
+        }
+        /* Last at height. */
+        else {
+            /* Set layer, tree and OTS leaf index into hash address. */
+            state->addr[XMSS_ADDR_LAYER] = i + 1;
+            idx_tree = (idx + 1) >> ((i + 1) * hs);
+            xmss_idx_get_tree_leaf(idx_tree, hs, &idx_tree, &idx_leaf);
+            xmss_idx_set_addr_tree(idx_tree, state->addr);
+            /* Cache WOTS+ signature for new tree. */
+            state->addr[XMSS_ADDR_OTS] = idx_leaf;
+            wc_xmss_wots_sign(state, bds[alt_i].stack, sk_seed, pk_seed,
+                state->addr, wots_sigs + i * params->wots_sig_len);
+            ret = state->ret;
+
+            if (ret == 0) {
+                word8 d;
+
+                /* Reset old BDS state. */
+                bds[bds_i].offset = 0;
+                bds[bds_i].next = 0;
+
+                /* Done an update. */
+                updates--;
+                /* Need to compute authentication path in next tree up. */
+                computeAuthPath = 1;
+                /* Mark the tree hashes as complete in new BDS state. */
+                for (d = 0; d < hsk; d++) {
+                    wc_xmss_bds_state_treehash_complete(&bds[alt_i], d);
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+
+/* Sign a message with XMSS^MT and update BDS state for signing next index.
+ *
+ * RFC 8391: 4.2.4, Algorithm 16: XMSS^MT_sign.
+ * HDSS, Section 4.5, The algorithm, Update and output phase.
+ *
+ * @param [in]      state   XMSS/MT state including digest and parameters.
+ * @param [in]      m       Buffer holding message.
+ * @param [in]      mlen    Length of message in buffer.
+ * @param [in, out] sk      Secret/Private key.
+ * @param [out]     sig     Signature and message data.
+ * @return  0 on success.
+ * @return  MEMORY_E on dynamic memory allocation failure.
+ * @return  <0 on digest failure.
+ */
+int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen,
+    unsigned char* sk, unsigned char* sig)
+{
+    int ret = 0;
+    const XmssParams* params = state->params;
+    const word8 h = params->h;
+    const word8 idx_len = params->idx_len;
+    XmssIdx idx = 0;
+    byte* sig_mt = sig;
+    byte* wots_sigs;
+    BdsState* bds = NULL;
+
+    /* Allocate memory for BDS states and tree hash instances. */
+    ret = wc_xmss_bds_state_alloc(params, &bds);
+    if (ret == 0) {
+        /* Load the BDS state from secret/private key. */
+        wc_xmss_bds_state_load(state, sk, bds, &wots_sigs);
+
+        /* Copy the index into the signature data: Sig_MT = idx_sig. */
+        XMEMCPY(sig_mt, sk, idx_len);
+
+        /* Read index from the secret key. */
+        xmss_idx_decode(&idx, idx_len, sk);
+    }
+    if ((ret == 0) && xmss_idx_invalid(idx, h)) {
+        /* Set index to maximum value to distinguish from valid value. */
+        XMEMSET(sk, 0xFF, idx_len);
+        /* Zeroize the secret key. */
+        ForceZero(sk + idx_len, params->sk_len - idx_len);
+        ret = KEY_EXHAUSTED_E;
+    }
+
+    if (ret == 0) {
+        /* Increment the index in the secret key. */
+        wc_idx_update(sk, idx_len);
+
+        /* Compute signature. */
+        ret = wc_xmssmt_sign_msg(state, bds, idx, wots_sigs, m, mlen, sk, sig);
+    }
+
+    /* Only update if not last index. */
+    if ((ret == 0) && (idx < (((XmssIdx)1 << h) - 1))) {
+        /* Update BDS state for signing next index. */
+        ret = wc_xmssmt_sign_next_idx(state, bds, idx, wots_sigs, sk);
+    }
+
+    if (ret == 0) {
+        /* Store BDS state back into secret/private key. */
+        wc_xmss_bds_state_store(state, sk, bds);
+    }
+
+    /* Dispose of allocated data of BDS states. */
+    wc_xmss_bds_state_free(bds);
+    return ret;
+}
+
+#endif /* WOLFSSL_WC_XMSS_SMALL */
+
+/* Check if more signatures are possible with secret/private key.
+ *
+ * @param [in] params  XMSS parameters
+ * @param [in] sk      Secret/private key.
+ * @return  1 when signatures possible.
+ * @return  0 when key exhausted.
+ */
+
+int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk)
+{
+    int ret = 0;
+    wc_Idx idx;
+
+    /* Read index from the secret key. */
+    WC_IDX_DECODE(idx, params->idx_len, sk, ret);
+    /* Check validity of index. */
+    if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) {
+        ret = KEY_EXHAUSTED_E;
+    }
+
+    return ret == 0;
+}
+#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */
+
+/********************************************
+ * SIGN OPEN - Verify
+ ********************************************/
+
+#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY)
+/* Compute root node with leaf and authentication path.
+ *
+ * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig
+ *     ...
+ *     for ( k = 0; k < h; k++ ) {
+ *       ADRS.setTreeHeight(k);
+ *       if ( (floor(idx_sig / (2^k)) % 2) == 0 ) {
+ *         ADRS.setTreeIndex(ADRS.getTreeIndex() / 2);
+ *         node[1] = RAND_HASH(node[0], auth[k], SEED, ADRS);
+ *       } else {
+ *         ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *         node[1] = RAND_HASH(auth[k], node[0], SEED, ADRS);
+ *       }
+ *       node[0] = node[1];
+ *     }
+ *     return node[0];
+ *
+ * @param [in]      state      XMSS/MT state including digest and parameters.
+ * @param [in]      idx_leaf   Index of leaf node.
+ * @param [in]      auth_path  Authentication path.
+ * @param [in]      pk_seed    Random public seed.
+ * @param [in]      addr       Hash address.
+ * @param [in, out] root       On in, leaf node. On out, root node.
+ */
+static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf,
+    const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* root)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    const byte* b[2][2] = { { root, auth_path }, { auth_path, root } };
+    word8 i;
+
+    for (i = 0; i < params->sub_h; i++) {
+        /* Get which side the leaf is on. */
+        word8 s = idx_leaf & 1;
+        /* Set tree height and index. */
+        addr[XMSS_ADDR_TREE_HEIGHT] = i;
+        idx_leaf >>= 1;
+        addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
+
+        /* Put the result into buffer position for next RAND_HASH. */
+        wc_xmss_rand_hash_lr(state, b[s][0], b[s][1], pk_seed, addr, root);
+        /* Move to next auth path node. */
+        b[0][1] += n;
+        b[1][0] += n;
+    }
+}
+#else
+/* Compute root node with leaf and authentication path.
+ *
+ * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig
+ *     ...
+ *     for ( k = 0; k < h; k++ ) {
+ *       ADRS.setTreeHeight(k);
+ *       if ( (floor(idx_sig / (2^k)) % 2) == 0 ) {
+ *         ADRS.setTreeIndex(ADRS.getTreeIndex() / 2);
+ *         node[1] = RAND_HASH(node[0], auth[k], SEED, ADRS);
+ *       } else {
+ *         ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2);
+ *         node[1] = RAND_HASH(auth[k], node[0], SEED, ADRS);
+ *       }
+ *       node[0] = node[1];
+ *     }
+ *     return node[0];
+ *
+ * @param [in]      state      XMSS/MT state including digest and parameters.
+ * @param [in]      idx_leaf   Index of leaf node.
+ * @param [in]      auth_path  Authentication path.
+ * @param [in]      pk_seed    Random public seed.
+ * @param [in]      addr       Hash address.
+ * @param [in, out] node       On in, leaf node. On out, root node.
+ */
+static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf,
+    const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* node)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    byte buffer[2 * WC_XMSS_MAX_N];
+    byte* b[2][2] = { { buffer, buffer + n }, { buffer + n, buffer } };
+    word8 i;
+
+    /* Setup buffer for first RAND_HASH. */
+    XMEMCPY(b[idx_leaf & 1][0], node, n);
+    XMEMCPY(b[idx_leaf & 1][1], auth_path, n);
+    auth_path += n;
+
+    for (i = 0; i < params->sub_h - 1; i++) {
+        /* Set tree height and index. */
+        addr[XMSS_ADDR_TREE_HEIGHT] = i;
+        idx_leaf >>= 1;
+        addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
+
+        /* Put the result into buffer position for next RAND_HASH. */
+        wc_xmss_rand_hash(state, buffer, pk_seed, addr, b[idx_leaf & 1][0]);
+        /* Put auth path node into other half of buffer. */
+        XMEMCPY(b[idx_leaf & 1][1], auth_path, n);
+        /* Move to next auth path node. */
+        auth_path += n;
+    }
+
+    addr[XMSS_ADDR_TREE_HEIGHT] = i;
+    idx_leaf >>= 1;
+    addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
+    /* Last iteration into output node. */
+    wc_xmss_rand_hash(state, buffer, pk_seed, addr, node);
+}
+#endif /* !WOLFSSL_WC_XMSS_SMALL || WOLFSSL_XMSS_VERIFY_ONLY */
+
+/* Compute a root node from a tree signature.
+ *
+ * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig
+ *     ADRS.setType(0);   # Type = OTS hash address
+ *     ADRS.setOTSAddress(idx_sig);
+ *     pk_ots = WOTS_pkFromSig(sig_ots, M', SEED, ADRS);
+ *     ADRS.setType(1);   # Type = L-tree address
+ *     ADRS.setLTreeAddress(idx_sig);
+ *     byte[n][2] node;
+ *     node[0] = ltree(pk_ots, SEED, ADRS);
+ *     ADRS.setType(2);   # Type = hash tree address
+ *     ADRS.setTreeIndex(idx_sig);
+ *     [Compute root with leaf and authentication path]
+ *
+ * Computing the root from the leaf and authentication path can be implemented
+ * in different ways and is therefore extracted to its own function.
+ *
+ * @param [in]      state    XMSS/MT state including digest and parameters.
+ * @param [in]      pk_seed  Random public seed.
+ * @param [in]      sig      WOTS+ signature for this tree.
+ * @param [in]      idx_sig  Index of signature leaf in this tree.
+ * @param [in, out] addr     Hash address.
+ * @param [in, out] node     On in, previous root node.
+ *                           On out, root node of this subtree.
+ */
+static void wc_xmss_root_from_sig(XmssState* state, const byte* pk_seed,
+    const byte* sig, word32 idx_sig, HashAddress addr, byte* node)
+{
+    const XmssParams* params = state->params;
+    byte* wots_pk = state->pk;
+    const byte* auth_path = sig + params->wots_sig_len;
+
+    /* Compute WOTS+ public key value from signature. */
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
+    addr[XMSS_ADDR_OTS] = idx_sig;
+    wc_xmss_wots_pk_from_sig(state, sig, node, pk_seed, addr, wots_pk);
+
+    /* Compute leaves of L-tree from WOTS+ public key. */
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
+    /* XMSS_ADDR_LTREE is same as XMSS_ADDR_OTS in index and value. */
+    wc_xmss_ltree(state, wots_pk, pk_seed, addr, node);
+
+    /* Compute root node from leaf and authentication path. */
+    addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
+    addr[XMSS_ADDR_TREE_ZERO] = 0;
+    wc_xmss_compute_root(state, idx_sig, auth_path, pk_seed, addr, node);
+}
+
+/* Verify message with signature using XMSS/MT.
+ *
+ * RFC 8391: 4.2.5, Algorithm 17: XMSSMT_verify
+ *     idx_sig = getIdx(Sig_MT);
+ *     SEED = getSEED(PK_MT);
+ *     ADRS = toByte(0, 32);
+ *
+ *     byte[n] M' = H_msg(getR(Sig_MT) || getRoot(PK_MT)
+ *                        || (toByte(idx_sig, n)), M);
+ *
+ *     unsigned int idx_leaf
+ *                   = (h / d) least significant bits of idx_sig;
+ *     unsigned int idx_tree
+ *                   = (h - h / d) most significant bits of idx_sig;
+ *     Sig' = getXMSSSignature(Sig_MT, 0);
+ *     ADRS.setLayerAddress(0);
+ *     ADRS.setTreeAddress(idx_tree);
+ *     byte[n] node = XMSS_rootFromSig(idx_leaf, getSig_ots(Sig'),
+ *                                      getAuth(Sig'), M', SEED, ADRS);
+ *     for ( j = 1; j < d; j++ ) {
+ *        idx_leaf = (h / d) least significant bits of idx_tree;
+ *        idx_tree = (h - j * h / d) most significant bits of idx_tree;
+ *        Sig' = getXMSSSignature(Sig_MT, j);
+ *        ADRS.setLayerAddress(j);
+ *        ADRS.setTreeAddress(idx_tree);
+ *        node = XMSS_rootFromSig(idx_leaf, getSig_ots(Sig'),
+ *                              getAuth(Sig'), node, SEED, ADRS);
+ *     }
+ *     if ( node == getRoot(PK_MT) ) {
+ *       return true;
+ *     } else {
+ *       return false;
+ *     }
+ *
+ * @param [in]       state    XMSS/MT state including digest and parameters.
+ * @param [in]       m        Message buffer.
+ * @param [in]       mlen     Length of message in bytes.
+ * @param [in]       sig      Buffer holding signature.
+ * @param [in]       pk       Public key.
+ * @return  0 on success.
+ * @return  MEMORY_E on dynamic memory allocation failure.
+ * @return  SIG_VERIFY_E on verification failure.
+ * @return  <0 on digest failure.
+ */
+int wc_xmssmt_verify(XmssState* state, const unsigned char* m, word32 mlen,
+    const unsigned char* sig, const unsigned char* pk)
+{
+    const XmssParams* params = state->params;
+    const word8 n = params->n;
+    int ret = 0;
+    const byte* pub_root = pk;
+    const byte* pk_seed = pk + n;
+    byte node[WC_XMSS_MAX_N];
+    wc_Idx idx;
+    word32 idx_leaf = 0;
+    unsigned int i;
+
+    /* Set 32/64-bit index to 0. */
+    WC_IDX_ZERO(idx);
+    /* Set all address values to zero. */
+    XMEMSET(state->addr, 0, sizeof(HashAddress));
+
+    if (ret == 0) {
+        /* Convert the index bytes from the signature to an integer. */
+        WC_IDX_DECODE(idx, params->idx_len, sig, ret);
+    }
+
+    if (ret == 0) {
+        const byte* sig_r = sig + params->idx_len;
+        /* byte[n] M' = H_msg(getR(Sig_MT) || getRoot(PK_MT) ||
+         *                    (toByte(idx_sig, n)), M);
+         */
+        wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m,
+            mlen, node);
+        ret = state->ret;
+    }
+
+    if (ret == 0) {
+        /* Set tree of hash address. */
+        WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr,
+            idx_leaf);
+
+        /* Skip to first WOTS+ signature and derive root. */
+        sig += params->idx_len + n;
+        wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr,
+            node);
+        ret = state->ret;
+    }
+    /* Calculate root of remaining subtrees up to top. */
+    for (i = 1; (ret == 0) && (i < params->d); i++) {
+        /* Set layer and tree. */
+        state->addr[XMSS_ADDR_LAYER] = i;
+        WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr,
+            idx_leaf);
+        /* Skip to next WOTS+ signature and derive root. */
+        sig += params->wots_sig_len + params->sub_h * n;
+        wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr,
+            node);
+        ret = state->ret;
+    }
+    /* Compare calculated node with public key root. */
+    if ((ret == 0) && (XMEMCMP(node, pub_root, n) != 0)) {
+        ret = SIG_VERIFY_E;
+    }
+
+    return ret;
+}
+#endif /* WOLFSSL_HAVE_XMSS */
+

+ 448 - 1
wolfssl/wolfcrypt/wc_lms.h

@@ -19,5 +19,452 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
-#error "Contact wolfSSL to get the implementation of this file"
+/* Implementation based on:
+ *   RFC 8554: Leighton-Micali Hash-Based Signatures
+ *   https://datatracker.ietf.org/doc/html/rfc8554
+ * Implementation by Sean Parkinson.
+ */
+
+/* Possible LMS options:
+ *
+ * WOLFSSL_LMS_LARGE_CACHES                             Default: OFF
+ *   Authentication path caches are large and signing faster.
+ * WOLFSSL_LMS_ROOT_LEVELS                              Default: 5 (Large: 7)
+ *   Number of levels of interior nodes from the to to cached.
+ *   Valid value are: 1..height of subtree.
+ *   The bigger the number, the larger the LmsKey but faster signing.
+ *   Only applies when !WOLFSSL_WC_LMS_SMALL.
+ * WOLFSSL_LMS_CACHE_BITS                               Default: 5 (Large: 7)
+ *   2 to the power of the value is the number of leaf nodes to cache.
+ *   Maximum valid value is height of subtree.
+ *   Valid value are: 0..height of subtree.
+ *   The bigger the number, the larger the LmsKey but faster signing.
+ *   Only applies when !WOLFSSL_WC_LMS_SMALL.
+ *
+ * Memory/Level | R/C | Approx. Time (% of 5/5)
+ *      (Bytes) |     |  H=10  |  H=15  |  H=20
+ * -------------+--------------+--------+--------
+ *         2016 | 5/5 | 100.0% | 100.0% | 100.0%
+ *         3040 | 5/6 |  75.5% |  89.2% |
+ *         4064 | 6/6 |  75.3% |  78.8% |
+ *         4576 | 4/7 |  72.4% |  87.6% |
+ *         6112 | 6/7 |  72.1% |  67.5% |
+ *         8160 | 7/7 |  72.2% |  56.8% |
+ *         8416 | 3/8 |  66.4% |  84.9% |
+ *        12256 | 7/8 |  66.5% |  45.9% |
+ *        16352 | 8/8 |  66.0% |  35.0% |
+ *        16416 | 1/9 |  54.1% |  79.5% |
+ * R = Root levels
+ * C = Cache bits
+ * To mimic the dynamic memory usage of XMSS, use 3/3.
+ *
+ * WOLFSSL_LMS_NO_SIGN SMOOTHING                        Default: OFF
+ *   Disable precalculation of next subtree.
+ *   Use less dynamic memory.
+ *   At certain indexes, signing will take a long time compared to the mean.
+ *   When OFF, the private key holds a second copy of caches.
+ *
+ * WOLFSSL_LMS_NO_SIG_CACHE                             Default: OFF
+ *   Signature cache is disabled.
+ *   This will use less dynamic memory and make signing slower when multiple
+ *   levels.
+ *
+ * Sig cache holds the C and y hashes for a tree that is not the lowest.
+ * Sig cache size = (levels - 1) * (1 + p) * 32 bytes
+ * p is the number of y terms based on Winternitz width.
+ *
+ *  w |  p | l | Bytes
+ * ---+----+---+------
+ *  4 | 67 | 2 |  2176
+ *  4 | 67 | 3 |  4353
+ *  4 | 67 | 4 |  6528
+ *  8 | 34 | 2 |  1120
+ *  8 | 34 | 3 |  2240
+ *  8 | 34 | 4 |  3360
+ * w = Winternitz width
+ * l = #levels
+ */
+
+#ifndef WC_LMS_H
+#define WC_LMS_H
+
+#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS)
+
+#include <wolfssl/wolfcrypt/lms.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+
+#ifdef WOLFSSL_LMS_MAX_LEVELS
+    /* Maximum number of levels of trees supported by implementation. */
+    #define LMS_MAX_LEVELS          WOLFSSL_LMS_MAX_LEVELS
+#else
+    /* Maximum number of levels of trees supported by implementation. */
+    #define LMS_MAX_LEVELS          4
+#endif
+#if (LMS_MAX_LEVELS < 1) || (LMS_MAX_LEVELS > 4)
+    #error "LMS parameters only support heights 1-4."
+#endif
+
+/* Smoothing is only used when there are 2 or more levels. */
+#if LMS_MAX_LEVELS == 1 && !defined(WOLFSSL_LMS_NO_SIGN_SMOOTHING)
+    #define WOLFSSL_LMS_NO_SIGN_SMOOTHING
+#endif
+
+#ifdef WOLFSSL_LMS_MAX_HEIGHT
+    /* Maximum height of a tree supported by implementation. */
+    #define LMS_MAX_HEIGHT          WOLFSSL_LMS_MAX_HEIGHT
+#else
+    /* Maximum height of a tree supported by implementation. */
+    #define LMS_MAX_HEIGHT          20
+#endif
+#if (LMS_MAX_HEIGHT < 5) || (LMS_MAX_HEIGHT > 20)
+    #error "LMS parameters only support heights 5-20."
+#endif
+
+/* Length of I in bytes. */
+#define LMS_I_LEN                   16
+/* Length of L in bytes. */
+#define LMS_L_LEN                   4
+/* Length of Q for a level. */
+#define LMS_Q_LEN                   4
+/* Length of P in bytes. */
+#define LMS_P_LEN                   2
+/* Length of W in bytes. */
+#define LMS_W_LEN                   1
+
+/* Length of numeric types when encoding. */
+#define LMS_TYPE_LEN                4
+
+/* Maximum size of a node hash. */
+#define LMS_MAX_NODE_LEN            WC_SHA256_DIGEST_SIZE
+/* Maximum size of SEED (produced by hash). */
+#define LMS_SEED_LEN                WC_SHA256_DIGEST_SIZE
+/* Maximum number of P, number of n-byte string elements in LM-OTS signature.
+ * Value of P when N=32 and W=1.
+ */
+#define LMS_MAX_P                   265
+/* Length of SEED and I in bytes. */
+#define LMS_SEED_I_LEN              (LMS_SEED_LEN + LMS_I_LEN)
+
+
+#ifndef WOLFSSL_LMS_ROOT_LEVELS
+    #ifdef WOLFSSL_LMS_LARGE_CACHES
+        /* Number of root levels of interior nodes to store.  */
+        #define LMS_ROOT_LEVELS         7
+    #else
+        /* Number of root levels of interior nodes to store.  */
+        #define LMS_ROOT_LEVELS         5
+    #endif
+#else
+    #define LMS_ROOT_LEVELS             WOLFSSL_LMS_ROOT_LEVELS
+#endif
+#if LMS_ROOT_LEVELS <= 0
+    #error "LMS_ROOT_LEVELS must be greater than 0."
+#endif
+/* Count of root nodes to store per level. */
+#define LMS_ROOT_COUNT              ((1 << (LMS_ROOT_LEVELS)) - 1)
+
+#ifndef WOLFSSL_LMS_CACHE_BITS
+    #ifdef WOLFSSL_LMS_LARGE_CACHES
+        /* 2 to the power of the value is the number of leaf nodes to cache. */
+        #define LMS_CACHE_BITS          7
+    #else
+        /* 2 to the power of the value is the number of leaf nodes to cache. */
+        #define LMS_CACHE_BITS          5
+    #endif
+#else
+    #define LMS_CACHE_BITS              WOLFSSL_LMS_CACHE_BITS
+#endif
+#if LMS_CACHE_BITS < 0
+    #error "LMS_CACHE_BITS must be greater than or equal to 0."
+#endif
+/* Number of leaf nodes to cache. */
+#define LMS_LEAF_CACHE              (1 << LMS_CACHE_BITS)
+
+/* Maximum number of levels of trees described in private key. */
+#define HSS_MAX_LEVELS              8
+/* Length of full Q in bytes. Q from all levels combined. */
+#define HSS_Q_LEN                   8
+
+/* Compressed parameter set length in bytes. */
+#define HSS_COMPRESS_PARAM_SET_LEN  1
+/* Total compressed parameter set length for private key in bytes. */
+#define HSS_PRIV_KEY_PARAM_SET_LEN  \
+    (HSS_COMPRESS_PARAM_SET_LEN * HSS_MAX_LEVELS)
+
+/* Private key length for one level. */
+#define LMS_PRIV_LEN                \
+    (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN)
+/* Public key length in signature. */
+#define LMS_PUBKEY_LEN              \
+    (LMS_TYPE_LEN + LMS_TYPE_LEN + LMS_I_LEN + LMS_MAX_NODE_LEN)
+
+/* LMS signature data length. */
+#define LMS_SIG_LEN(h, p)                                                   \
+    (LMS_Q_LEN + LMS_TYPE_LEN + LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN + \
+     LMS_TYPE_LEN + (h) * LMS_MAX_NODE_LEN)
+
+/* Length of public key. */
+#define HSS_PUBLIC_KEY_LEN          (LMS_L_LEN + LMS_PUBKEY_LEN)
+/* Length of private key. */
+#define HSS_PRIVATE_KEY_LEN         \
+    (HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN + LMS_SEED_LEN + LMS_I_LEN)
+/* Maximum public key length - length is constant for all parameters. */
+#define HSS_MAX_PRIVATE_KEY_LEN     HSS_PRIVATE_KEY_LEN
+/* Maximum private key length - length is constant for all parameters. */
+#define HSS_MAX_PUBLIC_KEY_LEN      HSS_PUBLIC_KEY_LEN
+/* Maximum signature length. */
+#define HSS_MAX_SIG_LEN                                                        \
+    (LMS_TYPE_LEN +                                                            \
+     LMS_MAX_LEVELS * (LMS_Q_LEN + LMS_TYPE_LEN + LMS_TYPE_LEN +               \
+                       LMS_MAX_NODE_LEN * (1 + LMS_MAX_P + LMS_MAX_HEIGHT)) +  \
+     (LMS_MAX_LEVELS - 1) * LMS_PUBKEY_LEN                                     \
+     )
+
+/* Maximum buffer length required for use when hashing. */
+#define LMS_MAX_BUFFER_LEN          \
+    (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + 2 * LMS_MAX_NODE_LEN)
+
+
+/* Private key data length.
+ *
+ * HSSPrivKey.priv
+ */
+#define LMS_PRIV_KEY_LEN(l) \
+    ((l) * LMS_PRIV_LEN)
+
+/* Stack of nodes. */
+#define LMS_STACK_CACHE_LEN(h)              \
+     (((h) + 1) * LMS_MAX_NODE_LEN)
+
+/* Root cache length. */
+#define LMS_ROOT_CACHE_LEN(rl)              \
+    (((1 << (rl)) - 1) * LMS_MAX_NODE_LEN)
+
+/* Leaf cache length. */
+#define LMS_LEAF_CACHE_LEN(cb)              \
+    ((1 << (cb)) * LMS_MAX_NODE_LEN)
+
+/* Length of LMS private key state.
+ *
+ * LmsPrivState
+ *   auth_path +
+ *   root +
+ *   stack.stack + stack.offset +
+ *   cache.leaf + cache.index + cache.offset
+ */
+#define LMS_PRIV_STATE_LEN(h, rl, cb)   \
+    (((h) * LMS_MAX_NODE_LEN) +         \
+     LMS_STACK_CACHE_LEN(h) + 4 +       \
+     LMS_ROOT_CACHE_LEN(rl) +           \
+     LMS_LEAF_CACHE_LEN(cb) + 4 + 4)
+
+#ifndef WOLFSSL_WC_LMS_SMALL
+    /* Private key data state for all levels. */
+    #define LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb)    \
+         ((l) * LMS_PRIV_STATE_LEN(h, rl, cb))
+#else
+    /* Private key data state for all levels. */
+    #define LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb)    0
+#endif
+
+#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
+    /* Extra private key data for smoothing. */
+    #define LMS_PRIV_SMOOTH_LEN(l, h, rl, cb)       \
+        (LMS_PRIV_KEY_LEN(l) +                      \
+         ((l) - 1) * LMS_PRIV_STATE_LEN(h, rl, cb))
+#else
+    /* Extra private key data for smoothing. */
+    #define LMS_PRIV_SMOOTH_LEN(l, h, rl, cb)       0
+#endif
+
+#ifndef WOLFSSL_LMS_NO_SIG_CACHE
+    #define LMS_PRIV_Y_TREE_LEN(p)                                  \
+        (LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN)
+    /* Length of the y data cached in private key data. */
+    #define LMS_PRIV_Y_LEN(l, p)                                    \
+        (((l) - 1) * (LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN))
+#else
+    /* Length of the y data cached in private key data. */
+    #define LMS_PRIV_Y_LEN(l, p)    0
+#endif
+
+#ifndef WOLFSSL_WC_LMS_SMALL
+/* Length of private key data. */
+#define LMS_PRIV_DATA_LEN(l, h, p, rl, cb)   \
+    (LMS_PRIV_KEY_LEN(l) +                   \
+     LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb) +  \
+     LMS_PRIV_SMOOTH_LEN(l, h, rl, cb) +     \
+     LMS_PRIV_Y_LEN(l, p))
+#else
+#define LMS_PRIV_DATA_LEN(l, h, p, rl, cb)   \
+    LMS_PRIV_KEY_LEN(l)
+#endif
+
+
+/* LMS Parameters. */
+/* SHA-256 hash, 32-bytes of hash used, tree height of 5. */
+#define LMS_SHA256_M32_H5           5
+/* SHA-256 hash, 32-bytes of hash used, tree height of 10. */
+#define LMS_SHA256_M32_H10          6
+/* SHA-256 hash, 32-bytes of hash used, tree height of 15. */
+#define LMS_SHA256_M32_H15          7
+/* SHA-256 hash, 32-bytes of hash used, tree height of 20. */
+#define LMS_SHA256_M32_H20          8
+/* SHA-256 hash, 32-bytes of hash used, tree height of 25. */
+#define LMS_SHA256_M32_H25          9
+
+/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 1 bit. */
+#define LMOTS_SHA256_N32_W1         1
+/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 2 bits. */
+#define LMOTS_SHA256_N32_W2         2
+/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 4 bits. */
+#define LMOTS_SHA256_N32_W4         3
+/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 8 bits. */
+#define LMOTS_SHA256_N32_W8         4
+
+typedef struct LmsParams {
+    /* Number of tree levels. */
+    word8 levels;
+    /* Height of each tree. */
+    word8 height;
+    /* Width or Winternitz coefficient. */
+    word8 width;
+    /* Number of left-shift bits used in checksum calculation. */
+    word8 ls;
+    /* Number of n-byte string elements in LM-OTS signature. */
+    word16 p;
+    /* LMS type. */
+    word16 lmsType;
+    /* LMOTS type. */
+    word16 lmOtsType;
+    /* Length of LM-OTS signature. */
+    word16 sig_len;
+#ifndef WOLFSSL_WC_LMS_SMALL
+    /* Number of root levels of interior nodes to store. */
+    word8 rootLevels;
+    /* 2 to the power of the value is the number of leaf nodes to cache. */
+    word8 cacheBits;
+#endif
+} LmsParams;
+
+/* Mapping of id and string to parameters. */
+typedef struct wc_LmsParamsMap {
+    /* Identifier of parameters. */
+    enum wc_LmsParm id;
+    /* String representation of identifier of parameters. */
+    const char* str;
+    /* LMS parameter set. */
+    LmsParams params;
+} wc_LmsParamsMap;
+
+typedef struct LmsState {
+    /* Buffer to hold data to hash. */
+    ALIGN16 byte buffer[LMS_MAX_BUFFER_LEN];
+#ifdef WOLFSSL_SMALL_STACK
+    /* Buffer to hold expanded Q coefficients. */
+    ALIGN16 byte a[LMS_MAX_P];
+#endif
+    /* LMS parameters. */
+    const LmsParams* params;
+    /* Hash algorithm. */
+    wc_Sha256 hash;
+    /* Hash algorithm for calculating K. */
+    wc_Sha256 hash_k;
+} LmsState;
+
+#ifndef WOLFSSL_WC_LMS_SMALL
+/* Stack of interior node hashes. */
+typedef struct LmsStack {
+    /* Stack nodes. */
+    byte* stack;
+    /* Top of stack offset. */
+    word32 offset;
+} LmsStack;
+
+/* Cache of leaf hashes. */
+typedef struct HssLeafCache {
+    /* Cache of leaf nodes. Circular queue. */
+    byte* cache;
+    /* Start index of cached leaf nodes. */
+    word32 idx;
+    /* Index into cache of first leaf node. */
+    word32 offset;
+} HssLeafCache;
+
+typedef struct LmsPrivState {
+    /* Authentication path for current index. */
+    byte* auth_path;
+    /* Stack nodes. */
+    LmsStack stack;
+    /* Root nodes. */
+    byte* root;
+    /* Cache of leaf nodes. */
+    HssLeafCache leaf;
+} LmsPrivState;
+#endif /* WOLFSSL_WC_LMS_SMALL */
+
+typedef struct HssPrivKey {
+    /* Private key. */
+    byte* priv;
+#ifndef WOLFSSL_WC_LMS_SMALL
+    /* Per level state of the private key. */
+    LmsPrivState state[LMS_MAX_LEVELS];
+#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
+    /* Next private key. */
+    byte* next_priv;
+    /* Next private state. */
+    LmsPrivState next_state[LMS_MAX_LEVELS - 1];
+#endif
+#ifndef WOLFSSL_LMS_NO_SIG_CACHE
+    /* Per level state of the private key. */
+    byte* y;
+#endif
+    /* Indicates the key has all levels initialized. */
+    word8 inited:1;
+#endif
+} HssPrivKey;
+
+struct LmsKey {
+    /* Public key. */
+    ALIGN16 byte pub[HSS_PUBLIC_KEY_LEN];
+#ifndef WOLFSSL_LMS_VERIFY_ONLY
+    /* Encoded private key. */
+    ALIGN16 byte priv_raw[HSS_PRIVATE_KEY_LEN];
+
+    /* Packed private key data. */
+    byte* priv_data;
+    /* HSS Private key. */
+    HssPrivKey priv;
+
+    /* Callback to write/update key. */
+    wc_lms_write_private_key_cb write_private_key;
+    /* Callback to read key. */
+    wc_lms_read_private_key_cb  read_private_key;
+    /* Context arg passed to callbacks. */
+    void*                context;
+    /* Dynamic memory hint. */
+    void* heap;
+#endif /* !WOLFSSL_LMS_VERIFY_ONLY */
+    /* Parameters of key. */
+    const LmsParams* params;
+    /* Current state of key. */
+    enum wc_LmsState state;
+#ifdef WOLF_CRYPTO_CB
+    /* Device Identifier. */
+    int devId;
+#endif
+};
+
+int wc_hss_make_key(LmsState* state, WC_RNG* rng, byte* priv_raw,
+    HssPrivKey* priv_key, byte* priv_data, byte* pub);
+int wc_hss_reload_key(LmsState* state, const byte* priv_raw,
+    HssPrivKey* priv_key, byte* priv_data, byte* pub_root);
+int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key,
+    byte* priv_data, const byte* msg, word32 msgSz, byte* sig);
+int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw);
+int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg,
+    word32 msgSz, const byte* sig);
+
+#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */
 
+#endif /* WC_LMS_H */

+ 263 - 1
wolfssl/wolfcrypt/wc_xmss.h

@@ -19,5 +19,267 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  */
 
-#error "Contact wolfSSL to get the implementation of this file"
+/* Based on:
+ *  o RFC 8391 - XMSS: eXtended Merkle Signature Scheme
+ *  o [HDSS] "Hash-based Digital Signature Schemes", Buchmann, Dahmen and Szydlo
+ *    from "Post Quantum Cryptography", Springer 2009.
+ */
+
+#ifndef WC_XMSS_H
+#define WC_XMSS_H
+
+#ifdef WOLFSSL_HAVE_XMSS
+#include <wolfssl/wolfcrypt/xmss.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+#include <wolfssl/wolfcrypt/sha512.h>
+#include <wolfssl/wolfcrypt/sha3.h>
+
+#if !defined(WOLFSSL_WC_XMSS)
+    #error "This code is incompatible with external implementation of XMSS."
+#endif
+
+#if (defined(WC_XMSS_SHA512) || defined(WC_XMSS_SHAKE256)) && \
+        (WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512)
+    #define WC_XMSS_MAX_N               64
+    #define WC_XMSS_MAX_PADDING_LEN     64
+#else
+    #define WC_XMSS_MAX_N               32
+    #define WC_XMSS_MAX_PADDING_LEN     32
+#endif
+#define WC_XMSS_MAX_MSG_PRE_LEN     \
+    (WC_XMSS_MAX_PADDING_LEN + 3 * WC_XMSS_MAX_N)
+#define WC_XMSS_MAX_TREE_HEIGHT     20
+#define WC_XMSS_MAX_CSUM_BYTES       4
+#define WC_XMSS_MAX_WOTS_LEN        (8 * WC_XMSS_MAX_N / 4 + 3)
+#define WC_XMSS_MAX_WOTS_SIG_LEN    (WC_XMSS_MAX_WOTS_LEN * WC_XMSS_MAX_N)
+#define WC_XMSS_MAX_STACK_LEN       \
+    ((WC_XMSS_MAX_TREE_HEIGHT + 1) * WC_XMSS_MAX_N)
+#define WC_XMSS_MAX_D               12
+#define WC_XMSS_MAX_BDS_STATES      (2 * WC_XMSS_MAX_D - 1)
+#define WC_XMSS_MAX_TREE_HASH       \
+    ((2 * WC_XMSS_MAX_D - 1) * WC_XMSS_MAX_TREE_HEIGHT)
+#define WC_XMSS_MAX_BDS_K           0
+
+#define WC_XMSS_ADDR_LEN            32
+
+#define WC_XMSS_HASH_PRF_MAX_DATA_LEN               \
+    (WC_XMSS_MAX_PADDING_LEN + 2 * WC_XMSS_MAX_N + WC_XMSS_ADDR_LEN)
+#define WC_XMSS_HASH_MAX_DATA_LEN                   \
+    (WC_XMSS_MAX_PADDING_LEN + 3 * WC_XMSS_MAX_N)
+
+
+#define WC_XMSS_SHA256_N            32
+#define WC_XMSS_SHA256_PADDING_LEN  32
+#define WC_XMSS_SHA256_WOTS_LEN     67
+
+#define XMSS_OID_LEN                   4
+
+#define XMSS_MAX_HASH_LEN              WC_SHA256_DIGEST_SIZE
+
+#define XMSS_RETAIN_LEN(k, n)   ((!!(k)) * ((1 << (k)) - (k) - 1) * (n))
+
+/* XMMS Algorithm OIDs
+ * Note: values are used in mathematical calculations in OID to parames. */
+#define WC_XMSS_OID_SHA2_10_256        0x01
+#define WC_XMSS_OID_SHA2_16_256        0x02
+#define WC_XMSS_OID_SHA2_20_256        0x03
+#define WC_XMSS_OID_SHA2_10_512        0x04
+#define WC_XMSS_OID_SHA2_16_512        0x05
+#define WC_XMSS_OID_SHA2_20_512        0x06
+#define WC_XMSS_OID_SHAKE_10_256       0x07
+#define WC_XMSS_OID_SHAKE_16_256       0x08
+#define WC_XMSS_OID_SHAKE_20_256       0x09
+#define WC_XMSS_OID_SHAKE_10_512       0x0a
+#define WC_XMSS_OID_SHAKE_16_512       0x0b
+#define WC_XMSS_OID_SHAKE_20_512       0x0c
+#define WC_XMSS_OID_SHA2_10_192        0x0d
+#define WC_XMSS_OID_SHA2_16_192        0x0e
+#define WC_XMSS_OID_SHA2_20_192        0x0f
+#define WC_XMSS_OID_SHAKE256_10_256    0x10
+#define WC_XMSS_OID_SHAKE256_16_256    0x11
+#define WC_XMSS_OID_SHAKE256_20_256    0x12
+#define WC_XMSS_OID_SHAKE256_10_192    0x13
+#define WC_XMSS_OID_SHAKE256_16_192    0x14
+#define WC_XMSS_OID_SHAKE256_20_192    0x15
+#define WC_XMSS_OID_FIRST              WC_XMSS_OID_SHA2_10_256
+#define WC_XMSS_OID_LAST               WC_XMSS_OID_SHAKE256_20_192
+
+/* XMMS^MT Algorithm OIDs
+ * Note: values are used in mathematical calculations in OID to parames. */
+#define WC_XMSSMT_OID_SHA2_20_2_256        0x01
+#define WC_XMSSMT_OID_SHA2_20_4_256        0x02
+#define WC_XMSSMT_OID_SHA2_40_2_256        0x03
+#define WC_XMSSMT_OID_SHA2_40_4_256        0x04
+#define WC_XMSSMT_OID_SHA2_40_8_256        0x05
+#define WC_XMSSMT_OID_SHA2_60_3_256        0x06
+#define WC_XMSSMT_OID_SHA2_60_6_256        0x07
+#define WC_XMSSMT_OID_SHA2_60_12_256       0x08
+#define WC_XMSSMT_OID_SHA2_20_2_512        0x09
+#define WC_XMSSMT_OID_SHA2_20_4_512        0x0a
+#define WC_XMSSMT_OID_SHA2_40_2_512        0x0b
+#define WC_XMSSMT_OID_SHA2_40_4_512        0x0c
+#define WC_XMSSMT_OID_SHA2_40_8_512        0x0d
+#define WC_XMSSMT_OID_SHA2_60_3_512        0x0e
+#define WC_XMSSMT_OID_SHA2_60_6_512        0x0f
+#define WC_XMSSMT_OID_SHA2_60_12_512       0x10
+#define WC_XMSSMT_OID_SHAKE_20_2_256       0x11
+#define WC_XMSSMT_OID_SHAKE_20_4_256       0x12
+#define WC_XMSSMT_OID_SHAKE_40_2_256       0x13
+#define WC_XMSSMT_OID_SHAKE_40_4_256       0x14
+#define WC_XMSSMT_OID_SHAKE_40_8_256       0x15
+#define WC_XMSSMT_OID_SHAKE_60_3_256       0x16
+#define WC_XMSSMT_OID_SHAKE_60_6_256       0x17
+#define WC_XMSSMT_OID_SHAKE_60_12_256      0x18
+#define WC_XMSSMT_OID_SHAKE_20_2_512       0x19
+#define WC_XMSSMT_OID_SHAKE_20_4_512       0x1a
+#define WC_XMSSMT_OID_SHAKE_40_2_512       0x1b
+#define WC_XMSSMT_OID_SHAKE_40_4_512       0x1c
+#define WC_XMSSMT_OID_SHAKE_40_8_512       0x1d
+#define WC_XMSSMT_OID_SHAKE_60_3_512       0x1e
+#define WC_XMSSMT_OID_SHAKE_60_6_512       0x1f
+#define WC_XMSSMT_OID_SHAKE_60_12_512      0x20
+#define WC_XMSSMT_OID_SHA2_20_2_192        0x21
+#define WC_XMSSMT_OID_SHA2_20_4_192        0x22
+#define WC_XMSSMT_OID_SHA2_40_2_192        0x23
+#define WC_XMSSMT_OID_SHA2_40_4_192        0x24
+#define WC_XMSSMT_OID_SHA2_40_8_192        0x25
+#define WC_XMSSMT_OID_SHA2_60_3_192        0x26
+#define WC_XMSSMT_OID_SHA2_60_6_192        0x27
+#define WC_XMSSMT_OID_SHA2_60_12_192       0x28
+#define WC_XMSSMT_OID_SHAKE256_20_2_256    0x29
+#define WC_XMSSMT_OID_SHAKE256_20_4_256    0x2a
+#define WC_XMSSMT_OID_SHAKE256_40_2_256    0x2b
+#define WC_XMSSMT_OID_SHAKE256_40_4_256    0x2c
+#define WC_XMSSMT_OID_SHAKE256_40_8_256    0x2d
+#define WC_XMSSMT_OID_SHAKE256_60_3_256    0x2e
+#define WC_XMSSMT_OID_SHAKE256_60_6_256    0x2f
+#define WC_XMSSMT_OID_SHAKE256_60_12_256   0x30
+#define WC_XMSSMT_OID_SHAKE256_20_2_192    0x31
+#define WC_XMSSMT_OID_SHAKE256_20_4_192    0x32
+#define WC_XMSSMT_OID_SHAKE256_40_2_192    0x33
+#define WC_XMSSMT_OID_SHAKE256_40_4_192    0x34
+#define WC_XMSSMT_OID_SHAKE256_40_8_192    0x35
+#define WC_XMSSMT_OID_SHAKE256_60_3_192    0x36
+#define WC_XMSSMT_OID_SHAKE256_60_6_192    0x37
+#define WC_XMSSMT_OID_SHAKE256_60_12_192   0x38
+#define WC_XMSSMT_OID_FIRST            WC_XMSSMT_OID_SHA2_20_2_256
+#define WC_XMSSMT_OID_LAST             WC_XMSSMT_OID_SHAKE256_60_12_192
+
+
+/* Type for hash address. */
+typedef word32 HashAddress[8];
+
+/* XMSS/XMSS^MT fixed parameters. */
+typedef struct XmssParams {
+    /* Hash algorithm to use. */
+    word8  hash;
+    /* Size of hash output. */
+    word8  n;
+    /* Number of bytes of padding before rest of hash data. */
+    word8  pad_len;
+    /* Number of values to chain = 2 * n + 3. */
+    word8  wots_len;
+    /* Number of bytes in each WOTS+ signature. */
+    word16 wots_sig_len;
+    /* Full height of tree. */
+    word8  h;
+    /* Height of tree each subtree. */
+    word8  sub_h;
+    /* Number of subtrees = h / sub_h. */
+    word8  d;
+    /* Number of bytes to encode index into in private/secret key. */
+    word8  idx_len;
+    /* Number of bytes in a signature. */
+    word32 sig_len;
+    /* Number of bytes in a secret/private key. */
+    word32 sk_len;
+    /* Number of bytes in a public key. */
+    word8  pk_len;
+    /* BDS parameter for fast C implementation. */
+    word8  bds_k;
+} XmssParams;
+
+struct XmssKey {
+    /* Public key. */
+    unsigned char        pk[2 * WC_XMSS_MAX_N];
+    /* OID that identifies parameters. */
+    word32               oid;
+    /* Indicates whether the parameters are for XMSS^MT. */
+    int                  is_xmssmt;
+    /* XMSS/XMSS^MT parameters. */
+    const XmssParams*    params;
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+    /* Secret/private key. */
+    unsigned char*       sk;
+    /* Length of secret key. */
+    word32               sk_len;
+    /* Callback to write/update key. */
+    wc_xmss_write_private_key_cb write_private_key;
+    /* Callback to read key. */
+    wc_xmss_read_private_key_cb  read_private_key;
+    /* Context arg passed to callbacks. */
+    void*                context;
+#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */
+    /* State of key. */
+    enum wc_XmssState    state;
+};
+
+typedef struct XmssState {
+    const XmssParams* params;
+
+    /* Digest is assumed to be at the end. */
+    union {
+    #ifdef WC_XMSS_SHA256
+       wc_Sha256 sha256;
+    #endif
+    #ifdef WC_XMSS_SHA512
+       wc_Sha512 sha512;
+    #endif
+    #if defined(WC_XMSS_SHAKE128) || defined(WC_XMSS_SHAKE256)
+       wc_Shake shake;
+    #endif
+    } digest;
+#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
+    !defined(WC_XMSS_FULL_HASH)
+    ALIGN16 word32 dgst_state[WC_SHA256_DIGEST_SIZE / sizeof(word32)];
+#endif
+    ALIGN16 byte prf_buf[WC_XMSS_HASH_PRF_MAX_DATA_LEN];
+    ALIGN16 byte buf[WC_XMSS_HASH_MAX_DATA_LEN];
+    ALIGN16 byte pk[WC_XMSS_MAX_WOTS_SIG_LEN];
+#ifndef WOLFSSL_XMSS_VERIFY_ONLY
+    ALIGN16 byte stack[WC_XMSS_MAX_STACK_LEN];
+#else
+    ALIGN16 byte stack[WC_XMSS_ADDR_LEN];
+#endif
+    byte encMsg[WC_XMSS_MAX_WOTS_LEN];
+    HashAddress addr;
+
+    int ret;
+} XmssState;
+
+#ifdef __cplusplus
+    extern "C" {
+#endif
+
+WOLFSSL_LOCAL int wc_xmssmt_keygen(XmssState *state, const unsigned char* seed,
+    unsigned char *sk, unsigned char *pk);
+WOLFSSL_LOCAL int wc_xmss_keygen(XmssState *state, const unsigned char* seed,
+    unsigned char *sk, unsigned char *pk);
+
+WOLFSSL_LOCAL int wc_xmssmt_sign(XmssState *state, const unsigned char *m,
+    word32 mlen, unsigned char *sk, unsigned char *sm);
+WOLFSSL_LOCAL int wc_xmss_sign(XmssState *state, const unsigned char *m,
+    word32 mlen, unsigned char *sk, unsigned char *sm);
+
+WOLFSSL_LOCAL int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk);
+
+WOLFSSL_LOCAL int wc_xmssmt_verify(XmssState *state, const unsigned char *m,
+    word32 mlen, const unsigned char *sm, const unsigned char *pk);
+
+#ifdef __cplusplus
+    } /* extern "C" */
+#endif
+
+#endif /* WOLFSSL_HAVE_XMSS */
+#endif /* WC_XMSS_H */