Browse Source

Merge pull request #6564 from philljj/add_lms_hooks

Add LMS/HSS wolfCrypt hooks.
JacobBarthelmeh 11 months ago
parent
commit
a026d843cf

+ 55 - 0
INSTALL

@@ -254,3 +254,58 @@
 The wolfssl port in vcpkg is kept up to date by wolfSSL.
 
 We also have vcpkg ports for wolftpm, wolfmqtt and curl.
+
+17. Building with hash-sigs lib for LMS/HSS support [EXPERIMENTAL]
+
+    Using LMS/HSS requires that the hash-sigs lib has been built on
+    your system. We support hash-sigs lib at this git commit:
+      b0631b8891295bf2929e68761205337b7c031726
+    At the time of writing this, this is the HEAD of the master
+    branch of the hash-sigs project.
+
+    Currently the hash-sigs project only builds static libraries:
+      - hss_lib.a: a single-threaded static lib.
+      - hss_lib_thread.a: a multi-threaded static lib.
+
+    The multi-threaded version will mainly have speedups for key
+    generation and signing.
+
+    Additionally, the hash-sigs project can be modified to build
+    and install a shared library in /usr/local with either single
+    or multi-threaded versions.  If the shared version has been
+    built, libhss.so is the assumed name.
+
+    wolfSSL supports either option, and by default will look for
+    hss_lib.a first, and hss_lib_thread.a second, and libhss.so
+    lastly, in a specified hash-sigs dir.
+
+    How to get and build the hash-sigs library:
+      $ mkdir ~/hash_sigs
+      $ cd ~/hash_sigs
+      $ git clone https://github.com/cisco/hash-sigs.git src
+      $ cd src
+      $ git checkout b0631b8891295bf2929e68761205337b7c031726
+
+    In sha256.h, set USE_OPENSSL to 0:
+      #define USE_OPENSSL 0
+
+    To build the single-threaded version:
+      $ make hss_lib.a
+      $ ls *.a
+      hss_lib.a
+
+    To build multi-threaded:
+      $ make hss_lib_thread.a
+      $ ls *.a
+      hss_lib_thread.a
+
+    Build wolfSSL with
+    $ ./configure \
+        --enable-static \
+        --disable-shared \
+        --enable-lms=yes \
+        --with-liblms=<path to dir containing hss_lib_thread.a>
+    $ make
+
+    Run the benchmark against LMS/HSS with:
+    $ ./wolfcrypt/benchmark/benchmark -lms_hss

+ 108 - 0
configure.ac

@@ -1144,6 +1144,109 @@ then
 fi
 
 
+# liblms
+# Get the path to the hash-sigs LMS HSS lib.
+ENABLED_LIBLMS="no"
+tryliblmsdir=""
+AC_ARG_WITH([liblms],
+    [AS_HELP_STRING([--with-liblms=PATH],[PATH to hash-sigs LMS/HSS install (default /usr/local) EXPERIMENTAL!])],
+    [
+        AC_MSG_CHECKING([for liblms])
+
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <hss.h>]], [[ param_set_t lm_type; param_set_t lm_ots_type; hss_get_public_key_len(4, &lm_type, &lm_ots_type); ]])], [ liblms_linked=yes ],[ liblms_linked=no ])
+
+        if test "x$liblms_linked" = "xno" ; then
+            if test "x$withval" != "xno" ; then
+                tryliblmsdir=$withval
+            fi
+            if test "x$withval" = "xyes" ; then
+                tryliblmsdir="/usr/local"
+            fi
+
+            # 1. By default use the hash-sigs single-threaded static library.
+            # 2. If 1 not found, then use the multi-threaded static lib.
+            # 3. If 2 not found, then use the multi-threaded dynamic lib.
+            if test -e $tryliblmsdir/hss_lib.a; then
+                CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir"
+                LIB_STATIC_ADD="$LIB_STATIC_ADD $tryliblmsdir/hss_lib.a"
+                enable_shared=no
+                enable_static=yes
+                liblms_linked=yes
+            elif test -e $tryliblmsdir/hss_lib_thread.a; then
+                CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir"
+                LIB_STATIC_ADD="$LIB_STATIC_ADD $tryliblmsdir/hss_lib_thread.a"
+                enable_shared=no
+                enable_static=yes
+                liblms_linked=yes
+            elif test -e $tryliblmsdir/lib/libhss.so; then
+                LIBS="$LIBS -lhss"
+                CPPFLAGS="$AM_CPPFLAGS -DHAVE_LIBLMS -I$tryliblmsdir/include/hss"
+                LDFLAGS="$AM_LDFLAGS $LDFLAGS -L$tryliblmsdir/lib"
+
+                AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <hss.h>]], [[ param_set_t lm_type; param_set_t lm_ots_type; hss_get_public_key_len(4, &lm_type, &lm_ots_type); ]])], [ liblms_linked=yes ],[ liblms_linked=no ])
+            else
+                AC_MSG_ERROR([liblms isn't found.
+                If it's already installed, specify its path using --with-liblms=/dir/])
+            fi
+
+            if test "x$liblms_linked" = "xno" ; then
+                AC_MSG_ERROR([liblms isn't found.
+                If it's already installed, specify its path using --with-liblms=/dir/])
+            fi
+
+            AC_MSG_RESULT([yes])
+            AM_CPPFLAGS="$CPPFLAGS"
+            AM_LDFLAGS="$LDFLAGS"
+        else
+            AC_MSG_RESULT([yes])
+        fi
+
+        AM_CFLAGS="$AM_CFLAGS -DHAVE_LIBLMS"
+        ENABLED_LIBLMS="yes"
+    ]
+)
+
+
+# LMS
+AC_ARG_ENABLE([lms],
+    [AS_HELP_STRING([--enable-lms],[Enable stateful LMS/HSS signatures (default: disabled)])],
+    [ ENABLED_LMS=$enableval ],
+    [ ENABLED_LMS=no ]
+    )
+
+ENABLED_WC_LMS=no
+for v in `echo $ENABLED_LMS | tr "," " "`
+do
+  case $v in
+  yes)
+    ;;
+  no)
+    ;;
+  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
+
+
 # SINGLE THREADED
 AC_ARG_ENABLE([singlethreaded],
     [AS_HELP_STRING([--enable-singlethreaded],[Enable wolfSSL single threaded (default: disabled)])],
@@ -8753,6 +8856,7 @@ AM_CONDITIONAL([BUILD_FE448], [test "x$ENABLED_FE448" = "xyes" || test "x$ENABLE
 AM_CONDITIONAL([BUILD_GE448], [test "x$ENABLED_GE448" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_CURVE448],[test "x$ENABLED_CURVE448" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_CURVE448_SMALL],[test "x$ENABLED_CURVE448_SMALL" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
+AM_CONDITIONAL([BUILD_WC_LMS],[test "x$ENABLED_WC_LMS" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_WC_KYBER],[test "x$ENABLED_WC_KYBER" != "xno" || test "x$ENABLED_USERSETTINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_ECCSI],[test "x$ENABLED_ECCSI" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_SAKKE],[test "x$ENABLED_SAKKE" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
@@ -8792,6 +8896,7 @@ AM_CONDITIONAL([BUILD_CRL],[test "x$ENABLED_CRL" != "xno" || test "x$ENABLED_USE
 AM_CONDITIONAL([BUILD_CRL_MONITOR],[test "x$ENABLED_CRL_MONITOR" = "xyes"])
 AM_CONDITIONAL([BUILD_USER_RSA],[test "x$ENABLED_USER_RSA" = "xyes"] )
 AM_CONDITIONAL([BUILD_USER_CRYPTO],[test "x$ENABLED_USER_CRYPTO" = "xyes"])
+AM_CONDITIONAL([BUILD_LIBLMS],[test "x$ENABLED_LIBLMS" = "xyes"])
 AM_CONDITIONAL([BUILD_LIBOQS],[test "x$ENABLED_LIBOQS" = "xyes"])
 AM_CONDITIONAL([BUILD_WNR],[test "x$ENABLED_WNR" = "xyes"])
 AM_CONDITIONAL([BUILD_SRP],[test "x$ENABLED_SRP" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
@@ -9242,6 +9347,8 @@ echo "   * ED25519 streaming:          $ENABLED_ED25519_STREAM"
 echo "   * CURVE448:                   $ENABLED_CURVE448"
 echo "   * ED448:                      $ENABLED_ED448"
 echo "   * ED448 streaming:            $ENABLED_ED448_STREAM"
+echo "   * LMS:                        $ENABLED_LMS"
+echo "   * LMS wolfSSL impl:           $ENABLED_WC_LMS"
 echo "   * KYBER:                      $ENABLED_KYBER"
 echo "   * KYBER wolfSSL impl:         $ENABLED_WC_KYBER"
 echo "   * ECCSI                       $ENABLED_ECCSI"
@@ -9297,6 +9404,7 @@ echo "   * Persistent session cache:   $ENABLED_SAVESESSION"
 echo "   * Persistent cert    cache:   $ENABLED_SAVECERT"
 echo "   * Atomic User Record Layer:   $ENABLED_ATOMICUSER"
 echo "   * Public Key Callbacks:       $ENABLED_PKCALLBACKS"
+echo "   * liblms:                     $ENABLED_LIBLMS"
 echo "   * liboqs:                     $ENABLED_LIBOQS"
 echo "   * Whitewood netRandom:        $ENABLED_WNR"
 echo "   * Server Name Indication:     $ENABLED_SNI"

+ 8 - 0
src/include.am

@@ -655,6 +655,10 @@ endif
 endif
 endif
 
+if BUILD_WC_LMS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wc_lms.c
+endif
+
 if BUILD_CURVE25519
 src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/curve25519.c
 endif
@@ -734,6 +738,10 @@ src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/sphincs.c
 src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_kyber.c
 endif
 
+if BUILD_LIBLMS
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_lms.c
+endif
+
 if BUILD_LIBZ
 src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/compress.c
 endif

+ 417 - 0
wolfcrypt/benchmark/benchmark.c

@@ -154,6 +154,12 @@
     #include <wolfssl/wolfcrypt/ext_kyber.h>
 #endif
 #endif
+#ifdef WOLFSSL_HAVE_LMS
+    #include <wolfssl/wolfcrypt/lms.h>
+#ifdef HAVE_LIBLMS
+    #include <wolfssl/wolfcrypt/ext_lms.h>
+#endif
+#endif
 #ifdef WOLFCRYPT_HAVE_ECCSI
     #include <wolfssl/wolfcrypt/eccsi.h>
 #endif
@@ -565,6 +571,9 @@
 #define BENCH_SPHINCS_SMALL_LEVEL3_SIGN 0x00000010
 #define BENCH_SPHINCS_SMALL_LEVEL5_SIGN 0x00000020
 
+/* Post-Quantum Stateful Hash-Based sig algorithms. */
+#define BENCH_LMS_HSS                   0x00000001
+
 /* Other */
 #define BENCH_RNG                0x00000001
 #define BENCH_SCRYPT             0x00000002
@@ -603,6 +612,8 @@ static word32 bench_pq_asym_algs = 0;
 static word32 bench_pq_asym_algs2 = 0;
 /* Other cryptographic algorithms to benchmark. */
 static word32 bench_other_algs = 0;
+/* Post-Quantum Stateful Hash-Based sig algorithms to benchmark. */
+static word32 bench_pq_hash_sig_algs = 0;
 
 #if !defined(WOLFSSL_BENCHMARK_ALL) && !defined(NO_MAIN_DRIVER)
 
@@ -856,6 +867,21 @@ static const bench_alg bench_other_opt[] = {
 
 #endif /* !WOLFSSL_BENCHMARK_ALL && !NO_MAIN_DRIVER */
 
+#if defined(WOLFSSL_HAVE_LMS)
+typedef struct bench_pq_hash_sig_alg {
+    /* Command line option string. */
+    const char* str;
+    /* Bit values to set. */
+    word32 val;
+} bench_pq_hash_sig_alg;
+
+static const bench_pq_hash_sig_alg bench_pq_hash_sig_opt[] = {
+    { "-pq_hash_sig", 0xffffffff},
+    { "-lms_hss", BENCH_LMS_HSS},
+    { NULL, 0}
+};
+#endif /* if defined(WOLFSSL_HAVE_LMS) */
+
 #if defined(HAVE_PQC) && defined(HAVE_LIBOQS)
 /* The post-quantum-specific mapping of command line option to bit values and
  * OQS name. */
@@ -1592,6 +1618,7 @@ static void benchmark_static_init(int force)
         bench_asym_algs = 0;
         bench_pq_asym_algs = 0;
         bench_other_algs = 0;
+        bench_pq_hash_sig_algs = 0;
         csv_format = 0;
     }
 }
@@ -2798,6 +2825,12 @@ static void* benchmarks_do(void* args)
     }
 #endif
 
+#ifdef WOLFSSL_HAVE_LMS
+    if (bench_all || (bench_pq_hash_sig_algs & BENCH_LMS_HSS)) {
+        bench_lms();
+    }
+#endif
+
 #ifdef HAVE_ECC
     if (bench_all || (bench_asym_algs & BENCH_ECC_MAKEKEY) ||
             (bench_asym_algs & BENCH_ECC) ||
@@ -7624,6 +7657,375 @@ void bench_kyber(int type)
 }
 #endif
 
+#ifdef WOLFSSL_HAVE_LMS
+/* WC_LMS_PARM_L2_H10_W2
+ * signature length: 9300 */
+static const byte lms_priv_L2_H10_W2[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x62,0x62,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0xC7,0x74,0x25,0x5B,0x2C,0xE8,0xDA,0x53,
+    0xF0,0x7C,0x04,0x3F,0x64,0x2D,0x26,0x2C,
+    0x46,0x1D,0xC8,0x90,0x77,0x59,0xD6,0xC0,
+    0x56,0x46,0x7D,0x97,0x64,0xF2,0xA3,0xA1,
+    0xF8,0xD0,0x3B,0x5F,0xAC,0x40,0xB9,0x9E,
+    0x83,0x67,0xBF,0x92,0x8D,0xFE,0x45,0x79
+};
+
+static const byte lms_pub_L2_H10_W2[60] =
+{
+    0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x06,
+    0x00,0x00,0x00,0x02,0xF8,0xD0,0x3B,0x5F,
+    0xAC,0x40,0xB9,0x9E,0x83,0x67,0xBF,0x92,
+    0x8D,0xFE,0x45,0x79,0x41,0xBC,0x2A,0x3B,
+    0x9F,0xC0,0x11,0x12,0x93,0xF0,0x5A,0xA5,
+    0xC1,0x88,0x29,0x79,0x6C,0x3E,0x0A,0x0F,
+    0xEC,0x3B,0x3E,0xE4,0x38,0xD3,0xD2,0x34,
+    0x7F,0xC8,0x91,0xB0
+};
+
+/* WC_LMS_PARM_L2_H10_W4
+ * signature length: 5076 */
+static const byte lms_priv_L2_H10_W4[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x63,0x63,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0xAE,0x28,0x87,0x19,0x4F,0x4B,0x68,0x61,
+    0x93,0x9A,0xC7,0x0E,0x33,0xB8,0xCE,0x96,
+    0x66,0x0D,0xC7,0xB1,0xFA,0x94,0x80,0xA2,
+    0x28,0x9B,0xCF,0xE2,0x08,0xB5,0x25,0xAC,
+    0xFB,0xB8,0x65,0x5E,0xD1,0xCC,0x31,0xDA,
+    0x2E,0x49,0x3A,0xEE,0xAF,0x63,0x70,0x5E
+};
+
+static const byte lms_pub_L2_H10_W4[60] =
+{
+    0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x06,
+    0x00,0x00,0x00,0x03,0xFB,0xB8,0x65,0x5E,
+    0xD1,0xCC,0x31,0xDA,0x2E,0x49,0x3A,0xEE,
+    0xAF,0x63,0x70,0x5E,0xA2,0xD5,0xB6,0x15,
+    0x33,0x8C,0x9B,0xE9,0xE1,0x91,0x40,0x1A,
+    0x12,0xE0,0xD7,0xBD,0xE4,0xE0,0x76,0xF5,
+    0x04,0x90,0x76,0xA5,0x9A,0xA7,0x4E,0xFE,
+    0x6B,0x9A,0xD3,0x14
+};
+
+/* WC_LMS_PARM_L3_H5_W4
+ * signature length: 7160 */
+static const byte lms_priv_L3_H5_W4[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x53,0x53,0x53,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0x38,0xD1,0xBE,0x68,0xD1,0x93,0xE1,0x14,
+    0x6C,0x8B,0xED,0xE2,0x25,0x88,0xED,0xAC,
+    0x57,0xBD,0x87,0x9F,0x54,0xF3,0x58,0xD9,
+    0x4D,0xF5,0x6A,0xBD,0x71,0x99,0x6A,0x28,
+    0x2F,0xE1,0xFC,0xD1,0xD1,0x0C,0x7C,0xF8,
+    0xB4,0xDC,0xDF,0x7F,0x14,0x1A,0x7B,0x50
+};
+
+static const byte lms_pub_L3_H5_W4[60] =
+{
+    0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,
+    0x00,0x00,0x00,0x03,0x2F,0xE1,0xFC,0xD1,
+    0xD1,0x0C,0x7C,0xF8,0xB4,0xDC,0xDF,0x7F,
+    0x14,0x1A,0x7B,0x50,0x8E,0x3A,0xD4,0x05,
+    0x0C,0x95,0x59,0xA0,0xCA,0x7A,0xD8,0xD6,
+    0x5D,0xBD,0x42,0xBB,0xD5,0x82,0xB8,0x9C,
+    0x52,0x37,0xB7,0x45,0x03,0xC2,0x06,0xCE,
+    0xAB,0x4B,0x51,0x39
+};
+
+/* WC_LMS_PARM_L3_H5_W8
+ * signature length: 3992 */
+static const byte lms_priv_L3_H5_W8[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x54,0x54,0x54,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0xA5,0x46,0x97,0x0C,0xA1,0x3C,0xEA,0x17,
+    0x5C,0x9D,0x59,0xF4,0x0E,0x27,0x37,0xF3,
+    0x6A,0x1C,0xF7,0x29,0x4A,0xCC,0xCD,0x7B,
+    0x4F,0xE7,0x37,0x6E,0xEF,0xC1,0xBD,0xBD,
+    0x04,0x5D,0x8E,0xDD,0xAA,0x47,0xCC,0xE6,
+    0xCE,0x78,0x46,0x20,0x41,0x87,0xE0,0x85
+};
+
+static const byte lms_pub_L3_H5_W8[60] =
+{
+    0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x05,
+    0x00,0x00,0x00,0x04,0x04,0x5D,0x8E,0xDD,
+    0xAA,0x47,0xCC,0xE6,0xCE,0x78,0x46,0x20,
+    0x41,0x87,0xE0,0x85,0x0D,0x2C,0x46,0xB9,
+    0x39,0x8C,0xA3,0x92,0x4F,0xCE,0x50,0x96,
+    0x90,0x9C,0xF3,0x36,0x2E,0x09,0x15,0x3B,
+    0x4B,0x34,0x17,0xE7,0xE2,0x55,0xFC,0x5B,
+    0x83,0xAB,0x43,0xAF
+};
+
+/* WC_LMS_PARM_L3_H10_W4
+ * signature length: 7640 */
+static const byte lms_priv_L3_H10_W4[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x63,0x63,0x63,0xFF,0xFF,0xFF,0xFF,0xFF,
+    0xDF,0x98,0xAB,0xEC,0xFE,0x13,0x9F,0xF8,
+    0xD7,0x2B,0x4F,0x4C,0x79,0x34,0xB8,0x89,
+    0x24,0x6B,0x26,0x7D,0x7A,0x2E,0xA2,0xCB,
+    0x82,0x75,0x4E,0x96,0x54,0x49,0xED,0xA0,
+    0xAF,0xC7,0xA5,0xEE,0x8A,0xA2,0x83,0x99,
+    0x4B,0x18,0x59,0x2B,0x66,0xC0,0x32,0xDB
+};
+
+static const byte lms_pub_L3_H10_W4[60] =
+{
+    0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x06,
+    0x00,0x00,0x00,0x03,0xAF,0xC7,0xA5,0xEE,
+    0x8A,0xA2,0x83,0x99,0x4B,0x18,0x59,0x2B,
+    0x66,0xC0,0x32,0xDB,0xC4,0x18,0xEB,0x11,
+    0x17,0x7D,0xAA,0x93,0xFD,0xA0,0x70,0x4D,
+    0x68,0x4B,0x63,0x8F,0xC2,0xE7,0xCA,0x34,
+    0x14,0x31,0x0D,0xAA,0x18,0xBF,0x9B,0x32,
+    0x8D,0x78,0xD5,0xA8
+};
+
+/* WC_LMS_PARM_L4_H5_W8
+ * signature length: 5340 */
+static const byte lms_priv_L4_H5_W8[64] =
+{
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x54,0x54,0x54,0x54,0xFF,0xFF,0xFF,0xFF,
+    0x46,0x8F,0x2A,0x4A,0x14,0x26,0xF0,0x89,
+    0xFE,0xED,0x66,0x0F,0x73,0x69,0xB1,0x4C,
+    0x47,0xA1,0x35,0x9F,0x7B,0xBA,0x08,0x03,
+    0xEE,0xA2,0xEB,0xAD,0xB4,0x82,0x52,0x1F,
+    0xFD,0x9B,0x22,0x82,0x42,0x1A,0x96,0x1E,
+    0xE4,0xA1,0x9C,0x33,0xED,0xE6,0x9F,0xAB
+};
+
+static const byte lms_pub_L4_H5_W8[60] =
+{
+    0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x05,
+    0x00,0x00,0x00,0x04,0xFD,0x9B,0x22,0x82,
+    0x42,0x1A,0x96,0x1E,0xE4,0xA1,0x9C,0x33,
+    0xED,0xE6,0x9F,0xAB,0x6B,0x47,0x05,0x5B,
+    0xA7,0xAD,0xF6,0x88,0xA5,0x4F,0xCD,0xF1,
+    0xDA,0x29,0x67,0xC3,0x7F,0x2C,0x11,0xFE,
+    0x85,0x1A,0x7A,0xD8,0xD5,0x46,0x74,0x3B,
+    0x74,0x24,0x12,0xC8
+};
+
+static int lms_write_key_mem(const byte * priv, word32 privSz, void *context)
+{
+   /* WARNING: THIS IS AN INSECURE WRITE CALLBACK THAT SHOULD ONLY
+    * BE USED FOR TESTING PURPOSES! Production applications should
+    * write only to non-volatile storage. */
+    XMEMCPY(context, priv, privSz);
+    return WC_LMS_RC_SAVED_TO_NV_MEMORY;
+}
+
+static int lms_read_key_mem(byte * priv, word32 privSz, void *context)
+{
+   /* WARNING: THIS IS AN INSECURE READ CALLBACK THAT SHOULD ONLY
+    * BE USED FOR TESTING PURPOSES! */
+    XMEMCPY(priv, context, privSz);
+    return WC_LMS_RC_READ_TO_MEMORY;
+}
+
+static void bench_lms_sign_verify(enum wc_LmsParm parm)
+{
+    LmsKey       key;
+    int          ret = 0;
+    const char * msg = TEST_STRING;
+    word32       msgSz = TEST_STRING_SZ;
+    byte *       sig = NULL;
+    word32       sigSz = 0;
+    word32       privLen = 0;
+    int          loaded = 0;
+    int          times = 0;
+    int          count = 0;
+    double       start = 0.0F;
+    byte         priv[HSS_MAX_PRIVATE_KEY_LEN];
+    const char * str = wc_LmsKey_ParmToStr(parm);
+
+    ret = wc_LmsKey_Init(&key, NULL, INVALID_DEVID);
+    if (ret) {
+        printf("wc_LmsKey_Init failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    ret = wc_LmsKey_SetLmsParm(&key, parm);
+    if (ret) {
+        printf("wc_LmsKey_SetLmsParm failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    switch (parm) {
+    case WC_LMS_PARM_L2_H10_W2:
+        XMEMCPY(priv, lms_priv_L2_H10_W2, sizeof(lms_priv_L2_H10_W2));
+        XMEMCPY(key.pub, lms_pub_L2_H10_W2, sizeof(lms_pub_L2_H10_W2));
+        break;
+
+    case WC_LMS_PARM_L2_H10_W4:
+        XMEMCPY(priv, lms_priv_L2_H10_W4, sizeof(lms_priv_L2_H10_W4));
+        XMEMCPY(key.pub, lms_pub_L2_H10_W4, sizeof(lms_pub_L2_H10_W4));
+        break;
+
+    case WC_LMS_PARM_L3_H5_W4:
+        XMEMCPY(priv, lms_priv_L3_H5_W4, sizeof(lms_priv_L3_H5_W4));
+        XMEMCPY(key.pub, lms_pub_L3_H5_W4, sizeof(lms_pub_L3_H5_W4));
+        break;
+
+    case WC_LMS_PARM_L3_H5_W8:
+        XMEMCPY(priv, lms_priv_L3_H5_W8, sizeof(lms_priv_L3_H5_W8));
+        XMEMCPY(key.pub, lms_pub_L3_H5_W8, sizeof(lms_pub_L3_H5_W8));
+        break;
+
+    case WC_LMS_PARM_L3_H10_W4:
+        XMEMCPY(priv, lms_priv_L3_H10_W4, sizeof(lms_priv_L3_H10_W4));
+        XMEMCPY(key.pub, lms_pub_L3_H10_W4, sizeof(lms_pub_L3_H10_W4));
+        break;
+
+    case WC_LMS_PARM_L4_H5_W8:
+        XMEMCPY(priv, lms_priv_L4_H5_W8, sizeof(lms_priv_L4_H5_W8));
+        XMEMCPY(key.pub, lms_pub_L4_H5_W8, sizeof(lms_pub_L4_H5_W8));
+        break;
+
+    case WC_LMS_PARM_NONE:
+    case WC_LMS_PARM_L1_H15_W2:
+    case WC_LMS_PARM_L1_H15_W4:
+    case WC_LMS_PARM_L2_H10_W8:
+    case WC_LMS_PARM_L3_H5_W2:
+        printf("bench_lms_sign_verify: unsupported benchmark option: %d\n",
+               parm);
+        goto exit_lms_sign_verify;
+    }
+
+    ret = wc_LmsKey_SetWriteCb(&key, lms_write_key_mem);
+    if (ret) {
+        fprintf(stderr, "error: wc_LmsKey_SetWriteCb failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    ret = wc_LmsKey_SetReadCb(&key, lms_read_key_mem);
+    if (ret) {
+        fprintf(stderr, "error: wc_LmsKey_SetReadCb failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    ret = wc_LmsKey_SetContext(&key, (void *) priv);
+    if (ret) {
+        fprintf(stderr, "error: wc_LmsKey_SetContext failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    /* Even with saved priv/pub keys, we must still reload the private
+     * key before using it. Reloading the private key is the bottleneck
+     * for larger heights. Only print load time in debug builds. */
+#if defined(DEBUG_WOLFSSL)
+    bench_stats_start(&count, &start);
+#endif /* if defined DEBUG_WOLFSSL*/
+
+    ret = wc_LmsKey_Reload(&key);
+    if (ret) {
+        printf("wc_LmsKey_Reload failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    count +=1;
+
+    ret = wc_LmsKey_GetSigLen(&key, &sigSz);
+    if (ret) {
+        printf("wc_LmsKey_GetSigLen failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+    ret = wc_LmsKey_GetPrivLen(&key, &privLen);
+    if (ret) {
+        printf("wc_LmsKey_GetPrivLen failed: %d\n", ret);
+        goto exit_lms_sign_verify;
+    }
+
+#if defined(DEBUG_WOLFSSL)
+    bench_stats_check(start);
+    bench_stats_asym_finish(str, (int)privLen, "load", 0,
+                            count, start, ret);
+#endif /* if defined DEBUG_WOLFSSL*/
+
+    loaded = 1;
+
+    sig = XMALLOC(sigSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+    if (sig == NULL) {
+        printf("bench_lms_sign_verify malloc failed\n");
+        goto exit_lms_sign_verify;
+    }
+
+    count = 0;
+    bench_stats_start(&count, &start);
+
+    do {
+        /* LMS is stateful. Async queuing not practical. */
+        for (times = 0; times < ntimes; ++times) {
+
+            ret = wc_LmsKey_Sign(&key, sig, &sigSz, (byte *) msg, msgSz);
+            if (ret) {
+                printf("wc_LmsKey_Sign failed: %d\n", ret);
+                goto exit_lms_sign_verify;
+            }
+        }
+
+        count += times;
+    } while (bench_stats_check(start));
+
+    bench_stats_asym_finish(str, (int)sigSz, "sign", 0,
+                            count, start, ret);
+
+    count = 0;
+    bench_stats_start(&count, &start);
+
+    do {
+        /* LMS is stateful. Async queuing not practical. */
+        for (times = 0; times < ntimes; ++times) {
+            ret = wc_LmsKey_Verify(&key, sig, sigSz, (byte *) msg, msgSz);
+            if (ret) {
+                printf("wc_LmsKey_Verify failed: %d\n", ret);
+                goto exit_lms_sign_verify;
+            }
+        }
+
+        count += times;
+    } while (bench_stats_check(start));
+
+exit_lms_sign_verify:
+    bench_stats_asym_finish(str, (int)sigSz, "verify", 0,
+                            count, start, ret);
+
+
+    if (loaded) {
+        wc_LmsKey_Free(&key);
+        loaded = 0;
+    }
+
+    if (sig != NULL) {
+        XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+        sig = NULL;
+    }
+
+    return;
+}
+
+void bench_lms(void)
+{
+    bench_lms_sign_verify(WC_LMS_PARM_L2_H10_W2);
+    bench_lms_sign_verify(WC_LMS_PARM_L2_H10_W4);
+    bench_lms_sign_verify(WC_LMS_PARM_L3_H5_W4);
+    bench_lms_sign_verify(WC_LMS_PARM_L3_H5_W8);
+    bench_lms_sign_verify(WC_LMS_PARM_L3_H10_W4);
+    bench_lms_sign_verify(WC_LMS_PARM_L4_H5_W8);
+    return;
+}
+
+#endif /* ifdef WOLFSSL_HAVE_LMS */
+
 #ifdef HAVE_ECC
 
 /* Maximum ECC name plus null terminator:
@@ -9952,6 +10354,10 @@ static void Usage(void)
         print_alg(bench_pq_asym_opt2[i].str, &line);
 #endif /* HAVE_LIBOQS */
 #endif /* HAVE_PQC */
+#if defined(WOLFSSL_HAVE_LMS)
+    for (i=0; bench_pq_hash_sig_opt[i].str != NULL; i++)
+        print_alg(bench_pq_hash_sig_opt[i].str, &line);
+#endif /* if defined(WOLFSSL_HAVE_LMS) */
     printf("\n");
 #endif /* !WOLFSSL_BENCHMARK_ALL */
     e++;
@@ -10211,6 +10617,17 @@ int wolfcrypt_benchmark_main(int argc, char** argv)
                     optMatched = 1;
                 }
             }
+
+        #if defined(WOLFSSL_HAVE_LMS)
+            /* post-quantum stateful hash-based signatures */
+            for (i=0; !optMatched && bench_pq_hash_sig_opt[i].str != NULL; i++) {
+                if (string_matches(argv[1], bench_pq_hash_sig_opt[i].str)) {
+                    bench_pq_hash_sig_algs |= bench_pq_hash_sig_opt[i].val;
+                    bench_all = 0;
+                    optMatched = 1;
+                }
+            }
+        #endif /* if defined(WOLFSSL_HAVE_LMS) */
 #endif
             if (!optMatched) {
                 printf("Option not recognized: %s\n", argv[1]);

+ 1 - 0
wolfcrypt/benchmark/benchmark.h

@@ -101,6 +101,7 @@ void bench_rsa(int useDeviceID);
 void bench_rsa_key(int useDeviceID, word32 keySz);
 void bench_dh(int useDeviceID);
 void bench_kyber(int type);
+void bench_lms(void);
 void bench_ecc_curve(int curveId);
 void bench_eccMakeKey(int useDeviceID, int curveId);
 void bench_ecc(int useDeviceID, int curveId);

+ 818 - 0
wolfcrypt/src/ext_lms.c

@@ -0,0 +1,818 @@
+/* ext_lms.c
+ *
+ * Copyright (C) 2006-2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+#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_LMS
+#include <wolfssl/wolfcrypt/ext_lms.h>
+
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+/* If built against hss_lib_thread.a, the hash-sigs lib will spawn
+ * worker threads to parallelize cpu intensive tasks. This will mainly
+ * speedup key generation and signing, and to a lesser extent
+ * verifying for larger levels values.
+ *
+ * Their default max is 16 worker threads, but can be capped with
+ * hss_extra_info_set_threads(). To be safe we are capping at 4 here.
+ * */
+#define EXT_LMS_MAX_THREADS (4)
+
+/* The hash-sigs hss_generate_private_key API requires a generate_random
+ * callback that only has output and length args. The RNG struct must be global
+ * to the function. Maybe there should be a wc_LmsKey_SetRngCb. */
+static THREAD_LS_T WC_RNG * LmsRng = NULL;
+
+static bool LmsGenerateRand(void * output, size_t length)
+{
+    int ret = 0;
+
+    if (output == NULL || LmsRng == NULL) {
+        return false;
+    }
+
+    if (length == 0) {
+        return true;
+    }
+
+    ret = wc_RNG_GenerateBlock(LmsRng, output, (word32) length);
+
+    if (ret) {
+        WOLFSSL_MSG("error: LmsGenerateRand failed");
+        return false;
+    }
+
+    return true;
+}
+
+/* Write callback passed into hash-sigs hss lib.
+ *
+ * Returns true on success. */
+static bool LmsWritePrivKey(unsigned char *private_key,
+                            size_t len_private_key, void *lmsKey)
+{
+    LmsKey *      key = (LmsKey *) lmsKey;
+    enum wc_LmsRc ret = WC_LMS_RC_NONE;
+
+    if (private_key == NULL || key == NULL || len_private_key <= 0) {
+        WOLFSSL_MSG("error: LmsWritePrivKey: invalid args");
+        return false;
+    }
+
+    if (key->state != WC_LMS_STATE_PARMSET && key->state != WC_LMS_STATE_OK) {
+       /* The LmsKey is not ready for writing. */
+        WOLFSSL_MSG("error: LmsWritePrivKey: LMS key not in writeable state");
+        return false;
+    }
+
+    if (key->write_private_key == NULL) {
+        WOLFSSL_MSG("error: LmsWritePrivKey: LMS key write callback not set");
+        key->state = WC_LMS_STATE_BAD;
+        return false;
+    }
+
+    /* Use write callback that saves private key to non-volatile storage. */
+    ret = key->write_private_key(private_key, len_private_key, key->context);
+
+    if (ret != WC_LMS_RC_SAVED_TO_NV_MEMORY) {
+        WOLFSSL_MSG("error: LmsKey write_private_key failed");
+        WOLFSSL_MSG(wc_LmsKey_RcToStr(ret));
+        key->state = WC_LMS_STATE_BAD;
+        return false;
+    }
+
+    return true;
+}
+
+/* Read callback passed into hash-sigs hss lib.
+ *
+ * Returns true on success. */
+static bool LmsReadPrivKey(unsigned char *private_key,
+                           size_t len_private_key, void *lmsKey)
+{
+    LmsKey *      key = (LmsKey *) lmsKey;
+    enum wc_LmsRc ret = WC_LMS_RC_NONE;
+
+    if (private_key == NULL || key == NULL || len_private_key <= 0) {
+        WOLFSSL_MSG("error: LmsReadPrivKey: invalid args");
+        return false;
+    }
+
+    if (key->state != WC_LMS_STATE_PARMSET && key->state != WC_LMS_STATE_OK) {
+       /* The LmsKey is not ready for reading. */
+        WOLFSSL_MSG("error: LmsReadPrivKey: LMS key not in readable state");
+        return false;
+    }
+
+    if (key->read_private_key == NULL) {
+        WOLFSSL_MSG("error: LmsReadPrivKey: LMS key read callback not set");
+        key->state = WC_LMS_STATE_BAD;
+        return false;
+    }
+
+    /* Use read callback that reads private key from non-volatile storage. */
+    ret = key->read_private_key(private_key, len_private_key, key->context);
+
+    if (ret != WC_LMS_RC_READ_TO_MEMORY) {
+        WOLFSSL_MSG("error: LmsKey read_private_key failed");
+        WOLFSSL_MSG(wc_LmsKey_RcToStr(ret));
+        key->state = WC_LMS_STATE_BAD;
+        return false;
+    }
+
+    return true;
+}
+
+const char * wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm)
+{
+    switch (lmsParm) {
+    case WC_LMS_PARM_NONE:
+        return "LMS_NONE";
+
+    case WC_LMS_PARM_L1_H15_W2:
+        return "LMS/HSS L1_H15_W2";
+
+    case WC_LMS_PARM_L1_H15_W4:
+        return "LMS/HSS L1_H15_W4";
+
+    case WC_LMS_PARM_L2_H10_W2:
+        return "LMS/HSS L2_H10_W2";
+
+    case WC_LMS_PARM_L2_H10_W4:
+        return "LMS/HSS L2_H10_W4";
+
+    case WC_LMS_PARM_L2_H10_W8:
+        return "LMS/HSS L2_H10_W8";
+
+    case WC_LMS_PARM_L3_H5_W2:
+        return "LMS/HSS L3_H5_W2";
+
+    case WC_LMS_PARM_L3_H5_W4:
+        return "LMS/HSS L3_H5_W4";
+
+    case WC_LMS_PARM_L3_H5_W8:
+        return "LMS/HSS L3_H5_W8";
+
+    case WC_LMS_PARM_L3_H10_W4:
+        return "LMS/HSS L3_H10_W4";
+
+    case WC_LMS_PARM_L4_H5_W8:
+        return "LMS/HSS L4_H5_W8";
+
+    default:
+        WOLFSSL_MSG("error: invalid LMS parameter");
+        break;
+    }
+
+    return "LMS_INVALID";
+}
+
+const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsEc)
+{
+    switch (lmsEc) {
+    case WC_LMS_RC_NONE:
+        return "LMS_RC_NONE";
+
+    case WC_LMS_RC_BAD_ARG:
+        return "LMS_RC_BAD_ARG";
+
+    case WC_LMS_RC_WRITE_FAIL:
+        return "LMS_RC_WRITE_FAIL";
+
+    case WC_LMS_RC_READ_FAIL:
+        return "LMS_RC_READ_FAIL";
+
+    case WC_LMS_RC_SAVED_TO_NV_MEMORY:
+        return "LMS_RC_SAVED_TO_NV_MEMORY";
+
+    case WC_LMS_RC_READ_TO_MEMORY:
+        return "LMS_RC_READ_TO_MEMORY";
+
+    default:
+        WOLFSSL_MSG("error: invalid LMS error code");
+        break;
+    }
+
+    return "LMS_RC_INVALID";
+}
+
+/* Init an LMS key.
+ *
+ * Call this before setting the parms of an LMS key.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_Init(LmsKey * key, void * heap, int devId)
+{
+    if (key == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    (void) heap;
+    (void) devId;
+
+    ForceZero(key, sizeof(LmsKey));
+
+    /* Set the max number of worker threads that hash-sigs can spawn. */
+    hss_init_extra_info(&key->info);
+    hss_extra_info_set_threads(&key->info, EXT_LMS_MAX_THREADS);
+
+    key->working_key = NULL;
+    key->write_private_key = NULL;
+    key->read_private_key = NULL;
+    key->context = NULL;
+    key->state = WC_LMS_STATE_INITED;
+
+    return 0;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_SetLmsParm(LmsKey * key, enum wc_LmsParm lmsParm)
+{
+    if (key == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    /* If NONE is passed, default to the lowest predefined set. */
+    switch (lmsParm) {
+    case WC_LMS_PARM_NONE:
+    case WC_LMS_PARM_L1_H15_W2:
+        return wc_LmsKey_SetParameters(key, 1, 15, 2);
+
+    case WC_LMS_PARM_L1_H15_W4:
+        return wc_LmsKey_SetParameters(key, 1, 15, 4);
+
+    case WC_LMS_PARM_L2_H10_W2:
+        return wc_LmsKey_SetParameters(key, 2, 10, 2);
+
+    case WC_LMS_PARM_L2_H10_W4:
+        return wc_LmsKey_SetParameters(key, 2, 10, 4);
+
+    case WC_LMS_PARM_L2_H10_W8:
+        return wc_LmsKey_SetParameters(key, 2, 10, 8);
+
+    case WC_LMS_PARM_L3_H5_W2:
+        return wc_LmsKey_SetParameters(key, 3, 5, 2);
+
+    case WC_LMS_PARM_L3_H5_W4:
+        return wc_LmsKey_SetParameters(key, 3, 5, 4);
+
+    case WC_LMS_PARM_L3_H5_W8:
+        return wc_LmsKey_SetParameters(key, 3, 5, 8);
+
+    case WC_LMS_PARM_L3_H10_W4:
+        return wc_LmsKey_SetParameters(key, 3, 10, 4);
+
+    case WC_LMS_PARM_L4_H5_W8:
+        return wc_LmsKey_SetParameters(key, 4, 5, 8);
+
+    default:
+        WOLFSSL_MSG("error: invalid LMS parameter set");
+        break;
+    }
+
+    return BAD_FUNC_ARG;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_SetParameters(LmsKey * key, int levels, int height,
+    int winternitz)
+{
+    int         i = 0;
+    param_set_t lm = LMS_SHA256_N32_H5;
+    param_set_t ots = LMOTS_SHA256_N32_W1;
+
+    if (key == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (key->state != WC_LMS_STATE_INITED) {
+        WOLFSSL_MSG("error: LmsKey needs init");
+        return -1;
+    }
+
+    /* Verify inputs make sense.
+     *
+     * Note: there does not seem to be a define for min or
+     * max Winternitz integer in hash-sigs lib or RFC8554. */
+
+    if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) {
+        WOLFSSL_MSG("error: invalid level parameter");
+        return BAD_FUNC_ARG;
+    }
+
+    if (height < MIN_MERKLE_HEIGHT || height > MAX_MERKLE_HEIGHT) {
+        WOLFSSL_MSG("error: invalid height parameter");
+        return BAD_FUNC_ARG;
+    }
+
+    switch (height) {
+    case 5:
+        lm = LMS_SHA256_N32_H5;
+        break;
+    case 10:
+        lm = LMS_SHA256_N32_H10;
+        break;
+    case 15:
+        lm = LMS_SHA256_N32_H15;
+        break;
+    case 20:
+        lm = LMS_SHA256_N32_H20;
+        break;
+    case 25:
+        lm = LMS_SHA256_N32_H25;
+        break;
+    default:
+        WOLFSSL_MSG("error: invalid height parameter");
+        return BAD_FUNC_ARG;
+    }
+
+    switch (winternitz) {
+    case 1:
+        ots = LMOTS_SHA256_N32_W1;
+        break;
+    case 2:
+        ots = LMOTS_SHA256_N32_W2;
+        break;
+    case 4:
+        ots = LMOTS_SHA256_N32_W4;
+        break;
+    case 8:
+        ots = LMOTS_SHA256_N32_W8;
+        break;
+    default:
+        WOLFSSL_MSG("error: invalid winternitz parameter");
+        return BAD_FUNC_ARG;
+    }
+
+    key->levels = levels;
+
+    for (i = 0; i < levels; ++i) {
+        key->lm_type[i] = lm;
+        key->lm_ots_type[i] = ots;
+    }
+
+    /* Move the state to parms set.
+     * Key is ready for MakeKey or Reload. */
+    key->state = WC_LMS_STATE_PARMSET;
+
+    return 0;
+}
+
+/* Frees the LMS key from memory.
+ *
+ * This does not affect the private key saved to non-volatile storage.
+ * */
+void wc_LmsKey_Free(LmsKey* key)
+{
+    if (key == NULL) {
+        return;
+    }
+
+    if (key->working_key != NULL) {
+        hss_free_working_key(key->working_key);
+        key->working_key = NULL;
+    }
+
+    ForceZero(key, sizeof(LmsKey));
+
+    key->state = WC_LMS_STATE_FREED;
+
+    return;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb write_cb)
+{
+    if (key == NULL || write_cb == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    /* Changing the write callback of an already working key is forbidden. */
+    if (key->state == WC_LMS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetWriteCb: key in use");
+        return -1;
+    }
+
+    key->write_private_key = write_cb;
+
+    return 0;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb read_cb)
+{
+    if (key == NULL || read_cb == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    /* Changing the read callback of an already working key is forbidden. */
+    if (key->state == WC_LMS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetReadCb: key in use");
+        return -1;
+    }
+
+    key->read_private_key = read_cb;
+
+    return 0;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_SetContext(LmsKey * key, void * context)
+{
+    if (key == NULL || context == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    /* Setting context of an already working key is forbidden. */
+    if (key->state == WC_LMS_STATE_OK) {
+        WOLFSSL_MSG("error: wc_LmsKey_SetContext: key in use");
+        return -1;
+    }
+
+    key->context = context;
+
+    return 0;
+}
+
+/* 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.
+ *
+ * Returns 0 on success.
+ * */
+int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng)
+{
+    bool result = true;
+
+    if (key == NULL || rng == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (key->state != WC_LMS_STATE_PARMSET) {
+        WOLFSSL_MSG("error: LmsKey not ready for generation");
+        return -1;
+    }
+
+    if (key->write_private_key == NULL || key->read_private_key == NULL) {
+        WOLFSSL_MSG("error: LmsKey write/read callbacks are not set");
+        return -1;
+    }
+
+    if (key->context == NULL) {
+        WOLFSSL_MSG("error: LmsKey context is not set");
+        return -1;
+    }
+
+    LmsRng = rng;
+
+   /* TODO: The hash-sigs lib allows you to save variable length auxiliary
+    * data, which can be used to speed up key reloading when signing. The
+    * aux data can be 300B - 1KB in size.
+    *
+    * Not implemented at the moment.
+    *
+    * key->aux_data_len = hss_get_aux_data_len(AUX_DATA_MAX_LEN, key->levels,
+    *                                          key->lm_type,
+    *                                          key->lm_ots_type);
+    *
+    * key->aux_data = XMALLOC(key->aux_data_len, NULL,
+    *                         DYNAMIC_TYPE_TMP_BUFFER);
+    */
+
+    /* First generate the private key using the parameters and callbacks.
+     * If successful, private key will be saved to non-volatile storage,
+     * and the public key will be in memory. */
+    result = hss_generate_private_key(LmsGenerateRand, key->levels,
+                                      key->lm_type, key->lm_ots_type,
+                                      LmsWritePrivKey, key,
+                                      key->pub, sizeof(key->pub),
+                                      NULL, 0, &key->info);
+
+    if (!result) {
+        WOLFSSL_MSG("error: hss_generate_private_key failed");
+        key->state = WC_LMS_STATE_BAD;
+        return -1;
+    }
+
+    /* Once generated, now we must load the private key so we have
+     * an hss working key for signing operations. */
+    key->working_key = hss_load_private_key(LmsReadPrivKey, key,
+                                            0, NULL, 0, &key->info);
+
+    if (key->working_key == NULL) {
+        WOLFSSL_MSG("error: hss_load_private_key failed");
+        key->state = WC_LMS_STATE_BAD;
+        return -1;
+    }
+
+    /* This should not happen, but check just in case. */
+    if (wc_LmsKey_SigsLeft(key) == 0) {
+        WOLFSSL_MSG("error: generated LMS key signatures exhausted");
+        key->state = WC_LMS_STATE_NOSIGS;
+        return -1;
+    }
+
+    key->state = WC_LMS_STATE_OK;
+
+    return 0;
+}
+
+/* Reload a key that has been prepared with the appropriate parms 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.
+ *
+ * Returns 0 on success. */
+int wc_LmsKey_Reload(LmsKey * key)
+{
+    bool result = true;
+
+    if (key == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (key->state != WC_LMS_STATE_PARMSET) {
+        WOLFSSL_MSG("error: LmsKey not ready for reload");
+        return -1;
+    }
+
+    if (key->write_private_key == NULL || key->read_private_key == NULL) {
+        WOLFSSL_MSG("error: LmsKey write/read callbacks are not set");
+        return -1;
+    }
+
+    if (key->context == NULL) {
+        WOLFSSL_MSG("error: LmsKey context is not set");
+        return -1;
+    }
+
+    key->working_key = hss_load_private_key(LmsReadPrivKey, key,
+                                            0, NULL, 0, &key->info);
+
+    if (key->working_key == NULL) {
+        WOLFSSL_MSG("error: hss_load_private_key failed");
+        key->state = WC_LMS_STATE_BAD;
+        return -1;
+    }
+
+    result = hss_get_parameter_set(&key->levels, key->lm_type,
+                                   key->lm_ots_type, LmsReadPrivKey, key);
+
+    if (!result) {
+        WOLFSSL_MSG("error: hss_get_parameter_set failed");
+        key->state = WC_LMS_STATE_BAD;
+        hss_free_working_key(key->working_key);
+        key->working_key = NULL;
+        return -1;
+    }
+
+    /* Double check the key actually has signatures left. */
+    if (wc_LmsKey_SigsLeft(key) == 0) {
+        WOLFSSL_MSG("error: reloaded LMS key signatures exhausted");
+        key->state = WC_LMS_STATE_NOSIGS;
+        return -1;
+    }
+
+    key->state = WC_LMS_STATE_OK;
+
+    return 0;
+}
+
+/* Given a levels, height, winternitz parameter set, determine
+ * the private key length */
+int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len)
+{
+    if (key == NULL || len == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    *len = (word32) hss_get_private_key_len(key->levels, key->lm_type,
+                                            key->lm_ots_type);
+
+    return 0;
+}
+
+/* Given a levels, height, winternitz parameter set, determine
+ * the public key length */
+int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len)
+{
+    if (key == NULL || len == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    *len = (word32) hss_get_public_key_len(key->levels, key->lm_type,
+                                           key->lm_ots_type);
+
+    return 0;
+}
+
+/* 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. */
+int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc)
+{
+    if (keyDst == NULL || keySrc == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    ForceZero(keyDst, sizeof(LmsKey));
+
+    XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub));
+    XMEMCPY(keyDst->lm_type, keySrc->lm_type, sizeof(keySrc->lm_type));
+    XMEMCPY(keyDst->lm_ots_type, keySrc->lm_ots_type,
+            sizeof(keySrc->lm_ots_type));
+
+    keyDst->levels = keySrc->levels;
+
+    /* Mark this key as verify only, to prevent misuse. */
+    keyDst->state = WC_LMS_STATE_VERIFYONLY;
+
+    return 0;
+}
+
+/* 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. */
+int wc_LmsKey_GetSigLen(LmsKey * key, word32 * len)
+{
+    if (key == NULL || len == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    *len = (word32) hss_get_signature_len(key->levels, key->lm_type,
+                                          key->lm_ots_type);
+
+    return 0;
+}
+
+int wc_LmsKey_Sign(LmsKey* key, byte * sig, word32 * sigSz, const byte * msg,
+    int msgSz)
+{
+    bool   result = true;
+    size_t len = 0;
+
+    if (key == NULL || sig == NULL || sigSz == NULL || msg == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (msgSz <= 0) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (key->state == WC_LMS_STATE_NOSIGS) {
+        WOLFSSL_MSG("error: LMS signatures exhausted");
+        return -1;
+    }
+    else if (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");
+        return -1;
+    }
+
+    len = hss_get_signature_len(key->levels, key->lm_type, key->lm_ots_type);
+
+    if (len == 0) {
+        /* Key parameters are invalid. */
+        WOLFSSL_MSG("error: hss_get_signature_len failed");
+        key->state = WC_LMS_STATE_BAD;
+        return -1;
+    }
+
+    result = hss_generate_signature(key->working_key, LmsWritePrivKey,
+                                    key, (const void *) msg, msgSz,
+                                    sig, len, &key->info);
+
+    if (!result) {
+        if (wc_LmsKey_SigsLeft(key) == 0) {
+            WOLFSSL_MSG("error: LMS signatures exhausted");
+            key->state = WC_LMS_STATE_NOSIGS;
+            return -1;
+        }
+
+        WOLFSSL_MSG("error: hss_generate_signature failed");
+        key->state = WC_LMS_STATE_BAD;
+        return -1;
+    }
+
+    *sigSz = (word32) len;
+
+    return 0;
+}
+
+int wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz,
+    const byte * msg, int msgSz)
+{
+    bool result = true;
+
+    if (key == NULL || sig == NULL || msg == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    result = hss_validate_signature(key->pub, (const void *) msg, msgSz, sig,
+                                    sigSz, &key->info);
+
+    if (!result) {
+        WOLFSSL_MSG("error: hss_validate_signature failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/* Returns 1 if there are signatures remaining.
+ * Returns 0 if available signatures are exhausted.
+ *
+ * Note: the number of remaining signatures is hidden behind an opaque
+ * pointer in the hash-sigs lib. We could add a counter here that is
+ * decremented on every signature. The number of available signatures
+ * grows as
+ *   N = 2 ** (levels * height)
+ * so it would need to be a big integer. */
+int wc_LmsKey_SigsLeft(LmsKey * key)
+{
+    if (key == NULL) {
+        return BAD_FUNC_ARG;
+    }
+
+    if (hss_extra_info_test_last_signature(&key->info)) {
+        return 0;
+    }
+
+    return 1;
+}
+#endif /* WOLFSSL_HAVE_LMS */

+ 26 - 0
wolfcrypt/src/wc_lms.c

@@ -0,0 +1,26 @@
+/* wc_lms.c
+ *
+ * Copyright (C) 2006-2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+#include <wolfssl/wolfcrypt/settings.h>
+
+#ifdef WOLFSSL_HAVE_LMS
+    #error "Contact wolfSSL to get the implementation of this file"
+#endif

+ 154 - 0
wolfcrypt/test/test.c

@@ -284,6 +284,12 @@ const byte const_byte_array[] = "A+Gd\0\0\0";
     #include <wolfssl/wolfcrypt/ext_kyber.h>
 #endif
 #endif
+#ifdef WOLFSSL_HAVE_LMS
+    #include <wolfssl/wolfcrypt/lms.h>
+#ifdef HAVE_LIBLMS
+    #include <wolfssl/wolfcrypt/ext_lms.h>
+#endif
+#endif
 #ifdef WOLFCRYPT_HAVE_ECCSI
     #include <wolfssl/wolfcrypt/eccsi.h>
 #endif
@@ -555,6 +561,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void);
 #ifdef WOLFSSL_HAVE_KYBER
     WOLFSSL_TEST_SUBROUTINE wc_test_ret_t  kyber_test(void);
 #endif
+#ifdef WOLFSSL_HAVE_LMS
+    WOLFSSL_TEST_SUBROUTINE int  lms_test(void);
+#endif
 #ifdef WOLFCRYPT_HAVE_ECCSI
     WOLFSSL_TEST_SUBROUTINE wc_test_ret_t  eccsi_test(void);
 #endif
@@ -1580,6 +1589,13 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
         TEST_PASS("KYBER    test passed!\n");
 #endif
 
+#ifdef WOLFSSL_HAVE_LMS
+    if ( (ret = lms_test()) != 0)
+        TEST_FAIL("LMS      test failed!\n", ret);
+    else
+        TEST_PASS("LMS      test passed!\n");
+#endif
+
 #ifdef WOLFCRYPT_HAVE_ECCSI
     if ( (ret = eccsi_test()) != 0)
         TEST_FAIL("ECCSI    test failed!\n", ret);
@@ -34908,6 +34924,144 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void)
 }
 #endif /* WOLFSSL_HAVE_KYBER */
 
+
+#ifdef WOLFSSL_HAVE_LMS
+static int lms_write_key_mem(const byte * priv, word32 privSz, void *context)
+{
+   /* WARNING: THIS IS AN INSECURE WRITE CALLBACK THAT SHOULD ONLY
+    * BE USED FOR TESTING PURPOSES! Production applications should
+    * write only to non-volatile storage. */
+    XMEMCPY(context, priv, privSz);
+    return WC_LMS_RC_SAVED_TO_NV_MEMORY;
+}
+
+static int lms_read_key_mem(byte * priv, word32 privSz, void *context)
+{
+   /* WARNING: THIS IS AN INSECURE READ CALLBACK THAT SHOULD ONLY
+    * BE USED FOR TESTING PURPOSES! */
+    XMEMCPY(priv, context, privSz);
+    return WC_LMS_RC_READ_TO_MEMORY;
+}
+
+/* LMS signature sizes are a function of their parameters. This
+ * test has a signature of 8688 bytes. */
+#define WC_TEST_LMS_SIG_LEN (8688)
+
+WOLFSSL_TEST_SUBROUTINE int lms_test(void)
+{
+    int           ret;
+    int           sigsLeft = 0;
+    LmsKey        signingKey;
+    LmsKey        verifyKey;
+    WC_RNG        rng;
+    word32        sigSz = 0;
+    const char *  msg = "LMS HSS post quantum signature test";
+    word32        msgSz = (word32) XSTRLEN(msg);
+    unsigned char priv[HSS_MAX_PRIVATE_KEY_LEN];
+    unsigned char old_priv[HSS_MAX_PRIVATE_KEY_LEN];
+#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
+    byte *        sig = XMALLOC(WC_TEST_LMS_SIG_LEN, HEAP_HINT,
+                                DYNAMIC_TYPE_TMP_BUFFER);
+    if (sig == NULL) {
+        return WC_TEST_RET_ENC_ERRNO;
+    }
+#else
+    byte          sig[WC_TEST_LMS_SIG_LEN];
+#endif
+
+    XMEMSET(priv, 0, sizeof(priv));
+    XMEMSET(old_priv, 0, sizeof(old_priv));
+
+#ifndef HAVE_FIPS
+    ret = wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID);
+#else
+    ret = wc_InitRng(&rng);
+#endif
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+   /* This test:
+    * levels: 1
+    * height: 5
+    * winternitz: 1
+    *
+    * max sigs: 2 ** (1 * 5) = 32
+    * signature length: 8688
+    */
+
+    ret = wc_LmsKey_Init(&signingKey, NULL, INVALID_DEVID);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_Init(&verifyKey, NULL, INVALID_DEVID);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_SetParameters(&signingKey, 1, 5, 1);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_SetWriteCb(&signingKey, lms_write_key_mem);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_SetReadCb(&signingKey, lms_read_key_mem);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_SetContext(&signingKey, (void *) priv);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_MakeKey(&signingKey, &rng);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    XMEMCPY(old_priv, priv, sizeof(priv));
+
+    ret = wc_LmsKey_ExportPub(&verifyKey, &signingKey);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    ret = wc_LmsKey_GetSigLen(&verifyKey, &sigSz);
+    if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); }
+
+    if (sigSz != WC_TEST_LMS_SIG_LEN) {
+        printf("error: got %d, expected %d\n", sigSz, WC_TEST_LMS_SIG_LEN);
+        return WC_TEST_RET_ENC_EC(sigSz);
+    }
+
+    /* 2 ** 5 should be the max number of signatures */
+    for (size_t i = 0; i < 32; ++i) {
+        /* We should have remaining signstures. */
+        sigsLeft = wc_LmsKey_SigsLeft(&signingKey);
+        if (sigsLeft == 0) {
+            return WC_TEST_RET_ENC_EC(sigsLeft);
+        }
+
+        /* Sign with key. The private key will be updated on every signature. */
+        ret = wc_LmsKey_Sign(&signingKey, sig, &sigSz, (byte *) msg, msgSz);
+        if (ret != 0) { return WC_TEST_RET_ENC_I(i); }
+
+        /* The updated private key should not match the old one. */
+        if (XMEMCMP(old_priv, priv, sizeof(priv)) == 0) {
+            printf("error: current priv key should not match old: %zu\n", i);
+            return WC_TEST_RET_ENC_I(i);
+        }
+
+        XMEMCPY(old_priv, priv, sizeof(priv));
+
+        ret = wc_LmsKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, msgSz);
+        if (ret != 0) { return WC_TEST_RET_ENC_I(i); }
+    }
+
+    /* This should be the last signature. */
+    sigsLeft = wc_LmsKey_SigsLeft(&signingKey);
+    if (sigsLeft != 0) {
+        return WC_TEST_RET_ENC_EC(sigsLeft);
+    }
+
+    wc_LmsKey_Free(&signingKey);
+    wc_LmsKey_Free(&verifyKey);
+
+    wc_FreeRng(&rng);
+
+    return ret;
+}
+
+#endif /* WOLFSSL_HAVE_LMS */
+
 static const int fiducial3 = WC_TEST_RET_LN; /* source code reference point --
                                               * see print_fiducials() below.
                                               */

+ 63 - 0
wolfssl/wolfcrypt/ext_lms.h

@@ -0,0 +1,63 @@
+/* ext_lms.h
+ *
+ * Copyright (C) 2006-2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+#ifndef EXT_LMS_H
+#define EXT_LMS_H
+
+#ifdef WOLFSSL_HAVE_LMS
+#include <wolfssl/wolfcrypt/lms.h>
+
+#if !defined(HAVE_LIBLMS)
+#error "This code requires liblms"
+#endif
+
+/* hash-sigs LMS HSS includes */
+#include <hss.h>
+
+#if defined(WOLFSSL_WC_LMS)
+#error "This code is incompatible with wolfCrypt's implementation of LMS."
+#endif
+
+/*
+ * The hash-sigs LMS lib supports from MIN_HSS_LEVELS to MAX_HSS_LEVELS
+ * number of levels of Merkle trees. It allows for the tree height and
+ * winternitz parameter to be unique per level.
+ */
+
+/* hss structs */
+typedef struct hss_working_key hss_working_key;
+typedef struct hss_extra_info  hss_extra_info;
+
+struct LmsKey {
+    unsigned             levels;                      /* Number of tree levels. */
+    param_set_t          lm_type[MAX_HSS_LEVELS];     /* Height parm per level. */
+    param_set_t          lm_ots_type[MAX_HSS_LEVELS]; /* Winternitz parm per level. */
+    unsigned char        pub[HSS_MAX_PUBLIC_KEY_LEN];
+    hss_working_key *    working_key;
+    write_private_key_cb write_private_key; /* Callback to write/update key. */
+    read_private_key_cb  read_private_key;  /* Callback to read key. */
+    void *               context;           /* Context arg passed to callbacks. */
+    hss_extra_info       info;
+    enum wc_LmsState     state;
+};
+
+#endif /* WOLFSSL_HAVE_LMS */
+#endif /* EXT_LMS_H */

+ 4 - 1
wolfssl/wolfcrypt/include.am

@@ -78,7 +78,10 @@ nobase_include_HEADERS+= \
                          wolfssl/wolfcrypt/ext_kyber.h \
                          wolfssl/wolfcrypt/sm2.h \
                          wolfssl/wolfcrypt/sm3.h \
-                         wolfssl/wolfcrypt/sm4.h
+                         wolfssl/wolfcrypt/sm4.h \
+                         wolfssl/wolfcrypt/lms.h \
+                         wolfssl/wolfcrypt/wc_lms.h \
+                         wolfssl/wolfcrypt/ext_lms.h
 
 noinst_HEADERS+= \
                          wolfssl/wolfcrypt/port/aria/aria-crypt.h \

+ 136 - 0
wolfssl/wolfcrypt/lms.h

@@ -0,0 +1,136 @@
+/* lms.h
+ *
+ * Copyright (C) 2006-2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+/*!
+    \file wolfssl/wolfcrypt/lms.h
+ */
+
+#ifndef WOLF_CRYPT_LMS_H
+#define WOLF_CRYPT_LMS_H
+
+#include <wolfssl/wolfcrypt/types.h>
+#include <wolfssl/wolfcrypt/random.h>
+
+#ifdef WOLFSSL_HAVE_LMS
+
+typedef struct LmsKey LmsKey;
+
+/* Private key write and read callbacks. */
+typedef int (*write_private_key_cb)(const byte * priv, word32 privSz, void *context);
+typedef int (*read_private_key_cb)(byte * priv, word32 privSz, void *context);
+
+/* Return codes returned by private key callbacks. */
+enum wc_LmsRc {
+  WC_LMS_RC_NONE,
+  WC_LMS_RC_BAD_ARG,            /* Bad arg in read or write callback. */
+  WC_LMS_RC_WRITE_FAIL,         /* Write or update private key failed. */
+  WC_LMS_RC_READ_FAIL,          /* Read private key failed. */
+  WC_LMS_RC_SAVED_TO_NV_MEMORY, /* Wrote private key to nonvolatile storage. */
+  WC_LMS_RC_READ_TO_MEMORY      /* Read private key from storage. */
+};
+
+/* LMS/HSS signatures are defined by 3 parameters:
+ *   levels: number of levels of Merkle trees.
+ *   height: height of an individual Merkle tree.
+ *   winternitz: number of bits from hash used in a Winternitz chain.
+ *
+ * The acceptable parameter values are those in RFC8554:
+ *   levels = {1..8}
+ *   height = {5, 10, 15, 20, 25}
+ *   winternitz = {1, 2, 4, 8}
+ *
+ * The number of available signatures is:
+ *   N = 2 ** (levels * height)
+ *
+ * Signature sizes are determined by levels and winternitz
+ * parameters primarily, and height to a lesser extent:
+ *   - Larger levels values increase signature size significantly.
+ *   - Larger height values increase signature size moderately.
+ *   - Larger winternitz values will reduce the signature size, at
+ *     the expense of longer key generation and sign/verify times.
+ *
+ * Key generation time is strongly determined by the height of
+ * the first level tree. A 3 level, 5 height tree is much faster
+ * than 1 level, 15 height at initial key gen, even if the number
+ * of available signatures is the same.
+ * */
+
+/* Predefined LMS/HSS parameter sets for convenience.
+ *
+ * Not predefining a set with Winternitz=1, because the signatures
+ * will be large. */
+enum wc_LmsParm {
+    WC_LMS_PARM_NONE      =  0,
+    WC_LMS_PARM_L1_H15_W2 =  1, /* 1 level Merkle tree of 15 height. */
+    WC_LMS_PARM_L1_H15_W4 =  2,
+    WC_LMS_PARM_L2_H10_W2 =  3, /* 2 level Merkle tree of 10 height. */
+    WC_LMS_PARM_L2_H10_W4 =  4,
+    WC_LMS_PARM_L2_H10_W8 =  5,
+    WC_LMS_PARM_L3_H5_W2  =  6, /* 3 level Merkle tree of 5 height. */
+    WC_LMS_PARM_L3_H5_W4  =  7,
+    WC_LMS_PARM_L3_H5_W8  =  8,
+    WC_LMS_PARM_L3_H10_W4 =  9, /* 3 level Merkle tree of 10 height. */
+    WC_LMS_PARM_L4_H5_W8  = 10, /* 4 level Merkle tree of 5 height. */
+};
+
+/* enum wc_LmsState is to help track the state of an LMS/HSS Key. */
+enum wc_LmsState {
+    WC_LMS_STATE_FREED,      /* Key has been freed from memory. */
+    WC_LMS_STATE_INITED,     /* Key has been inited, ready to set parms.*/
+    WC_LMS_STATE_PARMSET,    /* Parms are set, ready to MakeKey or Reload. */
+    WC_LMS_STATE_OK,         /* Able to sign signatures and verify. */
+    WC_LMS_STATE_VERIFYONLY, /* A public only LmsKey. */
+    WC_LMS_STATE_BAD,        /* Can't guarantee key's state. */
+    WC_LMS_STATE_NOSIGS      /* Signatures exhausted. */
+};
+
+#ifdef __cplusplus
+    extern "C" {
+#endif
+WOLFSSL_API int  wc_LmsKey_Init(LmsKey * key, void * heap, int devId);
+WOLFSSL_API int  wc_LmsKey_SetLmsParm(LmsKey * key, enum wc_LmsParm lmsParm);
+WOLFSSL_API int  wc_LmsKey_SetParameters(LmsKey * key, int levels,
+    int height, int winternitz);
+WOLFSSL_API int  wc_LmsKey_SetWriteCb(LmsKey * key,
+    write_private_key_cb write_cb);
+WOLFSSL_API int  wc_LmsKey_SetReadCb(LmsKey * key,
+    read_private_key_cb read_cb);
+WOLFSSL_API int  wc_LmsKey_SetContext(LmsKey * key, void * context);
+WOLFSSL_API void wc_LmsKey_Free(LmsKey * key);
+WOLFSSL_API int  wc_LmsKey_MakeKey(LmsKey * key, WC_RNG * rng);
+WOLFSSL_API int  wc_LmsKey_Reload(LmsKey * key);
+WOLFSSL_API int  wc_LmsKey_GetSigLen(LmsKey * key, word32 * len);
+WOLFSSL_API int  wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len);
+WOLFSSL_API int  wc_LmsKey_GetPubLen(LmsKey * key, word32 * len);
+WOLFSSL_API int  wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc);
+WOLFSSL_API int  wc_LmsKey_Sign(LmsKey * key, byte * sig, word32 * sigSz,
+    const byte * msg, int msgSz);
+WOLFSSL_API int  wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz,
+    const byte * msg, int msgSz);
+WOLFSSL_API int  wc_LmsKey_SigsLeft(LmsKey * key);
+WOLFSSL_API const char * wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm);
+WOLFSSL_API const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsRc);
+#ifdef __cplusplus
+    } /* extern "C" */
+#endif
+
+#endif /* WOLFSSL_HAVE_LMS */
+#endif /* WOLF_CRYPT_LMS_H */

+ 23 - 0
wolfssl/wolfcrypt/wc_lms.h

@@ -0,0 +1,23 @@
+/* wc_lms.h
+ *
+ * Copyright (C) 2006-2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+#error "Contact wolfSSL to get the implementation of this file"
+