From 67bef21185e0e4fb5201b1068f0140de3c2c6f12 Mon Sep 17 00:00:00 2001 From: jordan Date: Thu, 6 Jul 2023 10:55:53 -0500 Subject: [PATCH 1/5] Add LMS/HSS wolfCrypt hooks. --- INSTALL | 40 ++ configure.ac | 99 +++++ src/include.am | 8 + wolfcrypt/benchmark/benchmark.c | 381 ++++++++++++++++++ wolfcrypt/benchmark/benchmark.h | 1 + wolfcrypt/src/ext_lms.c | 690 ++++++++++++++++++++++++++++++++ wolfcrypt/src/wc_lms.c | 26 ++ wolfcrypt/test/test.c | 107 +++++ wolfssl/wolfcrypt/ext_lms.h | 64 +++ wolfssl/wolfcrypt/include.am | 5 +- wolfssl/wolfcrypt/lms.h | 131 ++++++ wolfssl/wolfcrypt/wc_lms.h | 3 + 12 files changed, 1554 insertions(+), 1 deletion(-) create mode 100644 wolfcrypt/src/ext_lms.c create mode 100644 wolfcrypt/src/wc_lms.c create mode 100644 wolfssl/wolfcrypt/ext_lms.h create mode 100644 wolfssl/wolfcrypt/lms.h create mode 100644 wolfssl/wolfcrypt/wc_lms.h diff --git a/INSTALL b/INSTALL index 2f54ac1888..afa36410b4 100644 --- a/INSTALL +++ b/INSTALL @@ -254,3 +254,43 @@ 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. At present we support the current master branch of + the hash-sigs project. + + At present the hash-sigs project only builds static libraries. + It can be modified though to build and install a shared library + in /usr/local. + + wolfSSL supports either option, and by default will look for + hss_lib_thread.a in a specified hash-sigs dir. If hash-sigs has + been built as a shared lib and installed in /usr/local/ , then + wolfSSL will look for libhss.so there. + + 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 + + In sha256.h, set USE_OPENSSl to 0: + #define USE_OPENSSL 0 + + Now build: + $ make + $ ls *.a + hss_lib.a hss_lib_thread.a hss_verify.a + + Build wolfSSL with + $ ./configure \ + --enable-static \ + --disable-shared \ + --enable-lms=yes \ + --with-liblms= + $ make + + Run the benchmark against LMS/HSS with: + $ ./wolfcrypt/benchmark/benchmark -lms_hss diff --git a/configure.ac b/configure.ac index 46e9c1103b..cbbc72d4ce 100644 --- a/configure.ac +++ b/configure.ac @@ -1141,6 +1141,100 @@ 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 ]], [[ 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 + + if 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 ]], [[ 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)])], @@ -8665,6 +8759,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"]) @@ -8704,6 +8799,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"]) @@ -9150,6 +9246,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" @@ -9204,6 +9302,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" diff --git a/src/include.am b/src/include.am index 174be84831..9f54477775 100644 --- a/src/include.am +++ b/src/include.am @@ -654,6 +654,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 @@ -733,6 +737,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 diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 22b505844e..4ad8b45b77 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -154,6 +154,12 @@ #include #endif #endif +#ifdef WOLFSSL_HAVE_LMS + #include +#ifdef HAVE_LIBLMS + #include +#endif +#endif #ifdef WOLFCRYPT_HAVE_ECCSI #include #endif @@ -569,6 +575,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 @@ -607,6 +616,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) @@ -860,6 +871,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. */ @@ -1596,6 +1622,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; } } @@ -2802,6 +2829,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) || @@ -7628,6 +7661,339 @@ 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 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; + const char * str = wc_LmsKey_ParmToStr(parm); + + ret = wc_LmsKey_Init(&key, parm); + if (ret) { + printf("wc_LmsKey_Init failed: %d\n", ret); + goto exit_lms_sign_verify; + } + + switch (parm) { + case WC_LMS_PARM_L2_H10_W2: + XMEMCPY(key.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(key.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(key.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(key.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(key.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(key.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; + } + + /* 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; + } + + /*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)); + + 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: @@ -9956,6 +10322,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++; @@ -10215,6 +10585,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]); diff --git a/wolfcrypt/benchmark/benchmark.h b/wolfcrypt/benchmark/benchmark.h index 29256874db..6e5d80f8c6 100644 --- a/wolfcrypt/benchmark/benchmark.h +++ b/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); diff --git a/wolfcrypt/src/ext_lms.c b/wolfcrypt/src/ext_lms.c new file mode 100644 index 0000000000..0927d7b11f --- /dev/null +++ b/wolfcrypt/src/ext_lms.c @@ -0,0 +1,690 @@ +/* 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 +#endif + +#include +#include +#include + +#ifdef WOLFSSL_HAVE_LMS +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* 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. */ +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_INITED && 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: LmsWritePrivKey: LMS key not in good state"); + return false; + } + + if (key->write_private_key != NULL) { + /* Use write callback. */ + 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; + } + } + else { + /* Save to memory. */ + if (len_private_key > sizeof(key->priv)) { + WOLFSSL_MSG("error: LmsWritePrivKey: private key too large"); + key->state = WC_LMS_STATE_BAD; + return false; + } + + XMEMSET(key->priv, 0, sizeof(key->priv)); + XMEMCPY(key->priv, private_key, len_private_key); + } + + return true; +} + +/* Read callback passed into hash-sigs hss lib. */ +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_INITED && 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: LmsReadPrivKey: LMS key not in good state"); + return false; + } + + if (key->read_private_key != NULL) { + /* Use read callback. */ + 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; + } + } + else { + /* Read from memory. */ + if (len_private_key > sizeof(key->priv)) { + WOLFSSL_MSG("error: LmsReadPrivKey: private key too large"); + key->state = WC_LMS_STATE_BAD; + return false; + } + + XMEMCPY(private_key, key->priv, len_private_key); + } + + 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"; +} + +int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm) +{ + if (key == NULL) { + return BAD_FUNC_ARG; + } + + switch (lmsParm) { + case WC_LMS_PARM_NONE: + return wc_LmsKey_Init_ex(key, 1, 15, 2, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L1_H15_W2: + return wc_LmsKey_Init_ex(key, 1, 15, 2, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L1_H15_W4: + return wc_LmsKey_Init_ex(key, 1, 15, 4, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L2_H10_W2: + return wc_LmsKey_Init_ex(key, 2, 10, 2, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L2_H10_W4: + return wc_LmsKey_Init_ex(key, 2, 10, 4, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L2_H10_W8: + return wc_LmsKey_Init_ex(key, 2, 10, 8, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L3_H5_W2: + return wc_LmsKey_Init_ex(key, 3, 5, 2, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L3_H5_W4: + return wc_LmsKey_Init_ex(key, 3, 5, 4, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L3_H5_W8: + return wc_LmsKey_Init_ex(key, 3, 5, 8, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L3_H10_W4: + return wc_LmsKey_Init_ex(key, 3, 10, 4, NULL, INVALID_DEVID); + + case WC_LMS_PARM_L4_H5_W8: + return wc_LmsKey_Init_ex(key, 4, 5, 8, NULL, INVALID_DEVID); + + default: + WOLFSSL_MSG("error: invalid LMS parameter set"); + break; + } + + return BAD_FUNC_ARG; +} + +int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, + int winternitz, void* heap, int devId) +{ + int ret = 0; + int i = 0; + param_set_t lm = LMS_SHA256_N32_H5; + param_set_t ots = LMOTS_SHA256_N32_W8; + (void) heap; + (void) devId; + + key->state = WC_LMS_STATE_NOT_INITED; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + ForceZero(key, sizeof(LmsKey)); + + /* Verify inputs make sense. Values of 0 may be passed to signify + * using minimum defaults. */ + if (levels == 0) { + levels = MIN_HSS_LEVELS; + } + else if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) { + WOLFSSL_MSG("error: invalid level parameter"); + return BAD_FUNC_ARG; + } + + if (height == 0) { + height = MIN_MERKLE_HEIGHT; + } + else if (height < MIN_MERKLE_HEIGHT || height > MAX_MERKLE_HEIGHT) { + WOLFSSL_MSG("error: invalid height parameter"); + return BAD_FUNC_ARG; + } + + if (winternitz == 0) { + winternitz = 2; + } + + 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; + } + + hss_init_extra_info(&key->info); + + /* The hash-sigs lib API will accept either: + * 1. private key callbacks with context pointer, or + * 2. context that points at private key. + * Do the 2nd by default, unless user sets the callbacks through API. */ + + key->working_key = NULL; + key->write_private_key = NULL; + key->read_private_key = NULL; + key->context = NULL; + key->state = WC_LMS_STATE_INITED; + + return ret; +} + +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_NOT_INITED; + + return; +} + +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_INITED) { + WOLFSSL_MSG("error: LmsKey not ready for generation"); + return -1; + } + + LmsRng = rng; + + /* todo: The has-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); + */ + + 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; + } + + 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; + } + + key->state = WC_LMS_STATE_OK; + + return 0; +} + +int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb wf) +{ + if (key == NULL || wf == NULL) { + return BAD_FUNC_ARG; + } + + key->write_private_key = wf; + + return 0; +} + +int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb rf) +{ + if (key == NULL || rf == NULL) { + return BAD_FUNC_ARG; + } + + key->read_private_key = rf; + + 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. */ +int wc_LmsKey_SetContext(LmsKey * key, void * context) +{ + if (key == NULL || context == NULL) { + return BAD_FUNC_ARG; + } + + key->context = context; + + return 0; +} + +/* Reload a key that has been prepared with the appropriate read callbacks + * or data. */ +int wc_LmsKey_Reload(LmsKey * key) +{ + bool result = true; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_LMS_STATE_INITED) { + WOLFSSL_MSG("error: LmsKey not ready for reload"); + 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; + } + + 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. Use this to prepare a signature verification + * key that is pub only. */ +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; + keyDst->state = keySrc->state; + + 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 sig 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; +} + +int wc_LmsKey_SigsLeft(LmsKey * key) +{ + /* 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. */ + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + if (hss_extra_info_test_last_signature(&key->info)) { + return 0; + } + + return 1; +} +#endif /* WOLFSSL_HAVE_LMS */ diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c new file mode 100644 index 0000000000..60e8519d97 --- /dev/null +++ b/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 + +#ifdef WOLFSSL_HAVE_LMS + #error "Contact wolfSSL to get the implementation of this file" +#endif diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 4b1078c8b3..028bf858b5 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -284,6 +284,12 @@ const byte const_byte_array[] = "A+Gd\0\0\0"; #include #endif #endif +#ifdef WOLFSSL_HAVE_LMS + #include +#ifdef HAVE_LIBLMS + #include +#endif +#endif #ifdef WOLFCRYPT_HAVE_ECCSI #include #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 @@ -1558,6 +1567,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); @@ -34805,6 +34821,97 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void) } #endif /* WOLFSSL_HAVE_KYBER */ + +#ifdef WOLFSSL_HAVE_LMS +WOLFSSL_TEST_SUBROUTINE int lms_test(void) +{ + int ret; + int sigsLeft = 0; + LmsKey signingKey; + LmsKey verifyKey; + WC_RNG rng; + byte sig[8688]; + word32 sigSz = 0; + const char * msg = "LMS HSS post quantum signature test"; + word32 msgSz = (word32) XSTRLEN(msg); + unsigned char old_priv[HSS_MAX_PRIVATE_KEY_LEN]; + + 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); } + + /* + * levels: 1 + * height: 5 + * winternitz: 1 + * + * max sigs: 2 ** (1 * 5) = 32 + * signature length: 8688 + */ + ret = wc_LmsKey_Init_ex(&signingKey, 1, 5, 1, NULL, INVALID_DEVID); + 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, signingKey.priv, sizeof(signingKey.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 != sizeof(sig)) { + printf("error: got %d, expected %zu\n", sigSz, sizeof(sig)); + 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, signingKey.priv, sizeof(signingKey.priv)) == 0) { + printf("error: current priv key should not match old: %zu\n", i); + return WC_TEST_RET_ENC_I(i); + } + + XMEMCPY(old_priv, signingKey.priv, sizeof(signingKey.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. */ diff --git a/wolfssl/wolfcrypt/ext_lms.h b/wolfssl/wolfcrypt/ext_lms.h new file mode 100644 index 0000000000..86cfc79ce2 --- /dev/null +++ b/wolfssl/wolfcrypt/ext_lms.h @@ -0,0 +1,64 @@ +/* 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 + +#if !defined(HAVE_LIBLMS) +#error "This code requires liblms" +#endif + +/* hash-sigs LMS HSS includes */ +#include + +#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 priv[HSS_MAX_PRIVATE_KEY_LEN]; + 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 */ diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index f8f4a81bbd..05c35cafde 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/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/pic32/pic32mz-crypt.h \ diff --git a/wolfssl/wolfcrypt/lms.h b/wolfssl/wolfcrypt/lms.h new file mode 100644 index 0000000000..4085efccea --- /dev/null +++ b/wolfssl/wolfcrypt/lms.h @@ -0,0 +1,131 @@ +/* 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 +#include + +#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. */ +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 { + WC_LMS_STATE_OK = 0, + WC_LMS_STATE_NOT_INITED = 1, + WC_LMS_STATE_INITED = 2, + WC_LMS_STATE_BAD = 3, /* Can't guarantee key's state. */ + WC_LMS_STATE_NOSIGS = 4, /* Signatures exhausted. */ +}; + +#ifdef __cplusplus + extern "C" { +#endif +/* Need an update_key_cb setting function. + * Also maybe a generate rand cb setter. */ +WOLFSSL_API int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm); +WOLFSSL_API int wc_LmsKey_Init_ex(LmsKey * key, int levels, + int height, int winternitz, void* heap, int devId); +WOLFSSL_API int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb wf); +WOLFSSL_API int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb rf); +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 */ + diff --git a/wolfssl/wolfcrypt/wc_lms.h b/wolfssl/wolfcrypt/wc_lms.h new file mode 100644 index 0000000000..62c3ed8118 --- /dev/null +++ b/wolfssl/wolfcrypt/wc_lms.h @@ -0,0 +1,3 @@ + +#error "Contact wolfSSL to get the implementation of this file" + From 55bbd5865c314d2afda39ff5821a630bda579579 Mon Sep 17 00:00:00 2001 From: jordan Date: Fri, 7 Jul 2023 15:30:50 -0500 Subject: [PATCH 2/5] LMS/HSS: cleanup, and safer write/read callbacks --- INSTALL | 2 +- wolfcrypt/benchmark/benchmark.c | 54 ++++++++++++++---- wolfcrypt/src/ext_lms.c | 98 +++++++++++++++++---------------- wolfcrypt/test/test.c | 33 ++++++++++- wolfssl/wolfcrypt/ext_lms.h | 1 - wolfssl/wolfcrypt/lms.h | 8 +-- 6 files changed, 127 insertions(+), 69 deletions(-) diff --git a/INSTALL b/INSTALL index afa36410b4..13611c26b5 100644 --- a/INSTALL +++ b/INSTALL @@ -276,7 +276,7 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. $ git clone https://github.com/cisco/hash-sigs.git src $ cd src - In sha256.h, set USE_OPENSSl to 0: + In sha256.h, set USE_OPENSSL to 0: #define USE_OPENSSL 0 Now build: diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 4ad8b45b77..63e83f7102 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -7818,6 +7818,23 @@ static const byte lms_pub_L4_H5_W8[60] = 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; @@ -7831,6 +7848,7 @@ static void bench_lms_sign_verify(enum wc_LmsParm parm) 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, parm); @@ -7841,32 +7859,32 @@ static void bench_lms_sign_verify(enum wc_LmsParm parm) switch (parm) { case WC_LMS_PARM_L2_H10_W2: - XMEMCPY(key.priv, lms_priv_L2_H10_W2, sizeof(lms_priv_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(key.priv, lms_priv_L2_H10_W4, sizeof(lms_priv_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(key.priv, lms_priv_L3_H5_W4, sizeof(lms_priv_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(key.priv, lms_priv_L3_H5_W8, sizeof(lms_priv_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(key.priv, lms_priv_L3_H10_W4, sizeof(lms_priv_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(key.priv, lms_priv_L4_H5_W8, sizeof(lms_priv_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; @@ -7880,6 +7898,24 @@ static void bench_lms_sign_verify(enum wc_LmsParm 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. */ @@ -7933,12 +7969,6 @@ static void bench_lms_sign_verify(enum wc_LmsParm parm) printf("wc_LmsKey_Sign failed: %d\n", ret); goto exit_lms_sign_verify; } - - /*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; diff --git a/wolfcrypt/src/ext_lms.c b/wolfcrypt/src/ext_lms.c index 0927d7b11f..a7d1007d75 100644 --- a/wolfcrypt/src/ext_lms.c +++ b/wolfcrypt/src/ext_lms.c @@ -83,27 +83,20 @@ static bool LmsWritePrivKey(unsigned char *private_key, return false; } - if (key->write_private_key != NULL) { - /* Use write callback. */ - 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; - } + if (key->write_private_key == NULL) { + WOLFSSL_MSG("error: LmsWritePrivKey: LMS key write callback not set"); + key->state = WC_LMS_STATE_NOT_INITED; + return false; } - else { - /* Save to memory. */ - if (len_private_key > sizeof(key->priv)) { - WOLFSSL_MSG("error: LmsWritePrivKey: private key too large"); - key->state = WC_LMS_STATE_BAD; - return false; - } - XMEMSET(key->priv, 0, sizeof(key->priv)); - XMEMCPY(key->priv, private_key, len_private_key); + /* Use write callback. */ + 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; @@ -128,26 +121,20 @@ static bool LmsReadPrivKey(unsigned char *private_key, return false; } - if (key->read_private_key != NULL) { - /* Use read callback. */ - 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; - } + if (key->read_private_key == NULL) { + WOLFSSL_MSG("error: LmsReadPrivKey: LMS key read callback not set"); + key->state = WC_LMS_STATE_NOT_INITED; + return false; } - else { - /* Read from memory. */ - if (len_private_key > sizeof(key->priv)) { - WOLFSSL_MSG("error: LmsReadPrivKey: private key too large"); - key->state = WC_LMS_STATE_BAD; - return false; - } - XMEMCPY(private_key, key->priv, len_private_key); + /* Use read callback. */ + 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; @@ -362,11 +349,6 @@ int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, hss_init_extra_info(&key->info); - /* The hash-sigs lib API will accept either: - * 1. private key callbacks with context pointer, or - * 2. context that points at private key. - * Do the 2nd by default, unless user sets the callbacks through API. */ - key->working_key = NULL; key->write_private_key = NULL; key->read_private_key = NULL; @@ -407,6 +389,16 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) 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 has-sigs lib allows you to save variable length auxiliary @@ -449,24 +441,24 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) return 0; } -int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb wf) +int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb write_cb) { - if (key == NULL || wf == NULL) { + if (key == NULL || write_cb == NULL) { return BAD_FUNC_ARG; } - key->write_private_key = wf; + key->write_private_key = write_cb; return 0; } -int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb rf) +int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb read_cb) { - if (key == NULL || rf == NULL) { + if (key == NULL || read_cb == NULL) { return BAD_FUNC_ARG; } - key->read_private_key = rf; + key->read_private_key = read_cb; return 0; } @@ -499,6 +491,16 @@ int wc_LmsKey_Reload(LmsKey * key) 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); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 028bf858b5..8caf0d1356 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -34823,6 +34823,23 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void) #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; +} + WOLFSSL_TEST_SUBROUTINE int lms_test(void) { int ret; @@ -34834,6 +34851,7 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) 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]; XMEMSET(old_priv, 0, sizeof(old_priv)); @@ -34856,10 +34874,19 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) ret = wc_LmsKey_Init_ex(&signingKey, 1, 5, 1, NULL, INVALID_DEVID); 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, signingKey.priv, sizeof(signingKey.priv)); + XMEMCPY(old_priv, priv, sizeof(priv)); ret = wc_LmsKey_ExportPub(&verifyKey, &signingKey); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } @@ -34885,12 +34912,12 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) if (ret != 0) { return WC_TEST_RET_ENC_I(i); } /* The updated private key should not match the old one. */ - if (XMEMCMP(old_priv, signingKey.priv, sizeof(signingKey.priv)) == 0) { + 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, signingKey.priv, sizeof(signingKey.priv)); + 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); } diff --git a/wolfssl/wolfcrypt/ext_lms.h b/wolfssl/wolfcrypt/ext_lms.h index 86cfc79ce2..faf833ad14 100644 --- a/wolfssl/wolfcrypt/ext_lms.h +++ b/wolfssl/wolfcrypt/ext_lms.h @@ -50,7 +50,6 @@ 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 priv[HSS_MAX_PRIVATE_KEY_LEN]; 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. */ diff --git a/wolfssl/wolfcrypt/lms.h b/wolfssl/wolfcrypt/lms.h index 4085efccea..9bd917248a 100644 --- a/wolfssl/wolfcrypt/lms.h +++ b/wolfssl/wolfcrypt/lms.h @@ -104,8 +104,10 @@ enum wc_LmsState { WOLFSSL_API int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm); WOLFSSL_API int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, int winternitz, void* heap, int devId); -WOLFSSL_API int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb wf); -WOLFSSL_API int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb rf); +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); @@ -126,6 +128,4 @@ WOLFSSL_API const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsRc); #endif #endif /* WOLFSSL_HAVE_LMS */ - #endif /* WOLF_CRYPT_LMS_H */ - From d7c3a176e256f897b76cb3f35e48e096c1fc70de Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 10 Jul 2023 23:43:07 -0500 Subject: [PATCH 3/5] LMS cleanup. --- INSTALL | 30 +++++++++++++++++++++--------- configure.ac | 11 ++++++++++- wolfcrypt/src/ext_lms.c | 2 +- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/INSTALL b/INSTALL index 13611c26b5..e18d0861fc 100644 --- a/INSTALL +++ b/INSTALL @@ -261,14 +261,21 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. your system. At present we support the current master branch of the hash-sigs project. - At present the hash-sigs project only builds static libraries. - It can be modified though to build and install a shared library - in /usr/local. + 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_thread.a in a specified hash-sigs dir. If hash-sigs has - been built as a shared lib and installed in /usr/local/ , then - wolfSSL will look for libhss.so there. + 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 @@ -279,10 +286,15 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. In sha256.h, set USE_OPENSSL to 0: #define USE_OPENSSL 0 - Now build: - $ make + 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.a hss_lib_thread.a hss_verify.a + hss_lib_thread.a Build wolfSSL with $ ./configure \ diff --git a/configure.ac b/configure.ac index cbbc72d4ce..0bc7b5cde2 100644 --- a/configure.ac +++ b/configure.ac @@ -1160,7 +1160,16 @@ AC_ARG_WITH([liblms], tryliblmsdir="/usr/local" fi - if test -e $tryliblmsdir/hss_lib_thread.a; then + # 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 diff --git a/wolfcrypt/src/ext_lms.c b/wolfcrypt/src/ext_lms.c index a7d1007d75..5f8cca6f97 100644 --- a/wolfcrypt/src/ext_lms.c +++ b/wolfcrypt/src/ext_lms.c @@ -401,7 +401,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) LmsRng = rng; - /* todo: The has-sigs lib allows you to save variable length auxiliary + /* 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. * From 4c125ece77e0caaec4d17d846aebc92cdd165dc2 Mon Sep 17 00:00:00 2001 From: jordan Date: Tue, 11 Jul 2023 15:04:51 -0500 Subject: [PATCH 4/5] LMS: cleanup INSTALL, and cap threads to 4. --- INSTALL | 7 +++++-- wolfcrypt/src/ext_lms.c | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/INSTALL b/INSTALL index e18d0861fc..93467ea58a 100644 --- a/INSTALL +++ b/INSTALL @@ -258,8 +258,10 @@ 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. At present we support the current master branch of - the hash-sigs project. + 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. @@ -282,6 +284,7 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. $ 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 diff --git a/wolfcrypt/src/ext_lms.c b/wolfcrypt/src/ext_lms.c index 5f8cca6f97..0c02740168 100644 --- a/wolfcrypt/src/ext_lms.c +++ b/wolfcrypt/src/ext_lms.c @@ -37,6 +37,16 @@ #include #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. */ @@ -347,7 +357,9 @@ int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, key->lm_ots_type[i] = ots; } + /* 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; From a747e7773c13e1f61cfa44c94e50e8c8f7cdaeff Mon Sep 17 00:00:00 2001 From: jordan Date: Fri, 14 Jul 2023 11:27:59 -0500 Subject: [PATCH 5/5] LMS: comments, SMALL_STACK, cleanup, etc. --- wolfcrypt/benchmark/benchmark.c | 8 +- wolfcrypt/src/ext_lms.c | 352 +++++++++++++++++++++----------- wolfcrypt/test/test.c | 30 ++- wolfssl/wolfcrypt/lms.h | 27 ++- wolfssl/wolfcrypt/wc_lms.h | 20 ++ 5 files changed, 301 insertions(+), 136 deletions(-) diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 63e83f7102..6f7a4a5e4a 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -7851,12 +7851,18 @@ static void bench_lms_sign_verify(enum wc_LmsParm parm) byte priv[HSS_MAX_PRIVATE_KEY_LEN]; const char * str = wc_LmsKey_ParmToStr(parm); - ret = wc_LmsKey_Init(&key, 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)); diff --git a/wolfcrypt/src/ext_lms.c b/wolfcrypt/src/ext_lms.c index 0c02740168..351746f6ee 100644 --- a/wolfcrypt/src/ext_lms.c +++ b/wolfcrypt/src/ext_lms.c @@ -74,7 +74,9 @@ static bool LmsGenerateRand(void * output, size_t length) return true; } -/* Write callback passed into hash-sigs hss lib. */ +/* 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) { @@ -86,20 +88,19 @@ static bool LmsWritePrivKey(unsigned char *private_key, return false; } - if (key->state != WC_LMS_STATE_INITED && 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: LmsWritePrivKey: LMS key not in good state"); + 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_NOT_INITED; + key->state = WC_LMS_STATE_BAD; return false; } - /* Use write callback. */ + /* 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) { @@ -112,7 +113,9 @@ static bool LmsWritePrivKey(unsigned char *private_key, return true; } -/* Read callback passed into hash-sigs hss lib. */ +/* 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) { @@ -124,20 +127,19 @@ static bool LmsReadPrivKey(unsigned char *private_key, return false; } - if (key->state != WC_LMS_STATE_INITED && 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: LmsReadPrivKey: LMS key not in good state"); + 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_NOT_INITED; + key->state = WC_LMS_STATE_BAD; return false; } - /* Use read callback. */ + /* 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) { @@ -223,45 +225,83 @@ const char * wc_LmsKey_RcToStr(enum wc_LmsRc lmsEc) return "LMS_RC_INVALID"; } -int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm) +/* 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: - return wc_LmsKey_Init_ex(key, 1, 15, 2, NULL, INVALID_DEVID); - case WC_LMS_PARM_L1_H15_W2: - return wc_LmsKey_Init_ex(key, 1, 15, 2, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 1, 15, 2); case WC_LMS_PARM_L1_H15_W4: - return wc_LmsKey_Init_ex(key, 1, 15, 4, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 1, 15, 4); case WC_LMS_PARM_L2_H10_W2: - return wc_LmsKey_Init_ex(key, 2, 10, 2, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 2, 10, 2); case WC_LMS_PARM_L2_H10_W4: - return wc_LmsKey_Init_ex(key, 2, 10, 4, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 2, 10, 4); case WC_LMS_PARM_L2_H10_W8: - return wc_LmsKey_Init_ex(key, 2, 10, 8, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 2, 10, 8); case WC_LMS_PARM_L3_H5_W2: - return wc_LmsKey_Init_ex(key, 3, 5, 2, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 3, 5, 2); case WC_LMS_PARM_L3_H5_W4: - return wc_LmsKey_Init_ex(key, 3, 5, 4, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 3, 5, 4); case WC_LMS_PARM_L3_H5_W8: - return wc_LmsKey_Init_ex(key, 3, 5, 8, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 3, 5, 8); case WC_LMS_PARM_L3_H10_W4: - return wc_LmsKey_Init_ex(key, 3, 10, 4, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 3, 10, 4); case WC_LMS_PARM_L4_H5_W8: - return wc_LmsKey_Init_ex(key, 4, 5, 8, NULL, INVALID_DEVID); + return wc_LmsKey_SetParameters(key, 4, 5, 8); default: WOLFSSL_MSG("error: invalid LMS parameter set"); @@ -271,46 +311,47 @@ int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm) return BAD_FUNC_ARG; } -int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, - int winternitz, void* heap, int devId) +/* 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 ret = 0; int i = 0; param_set_t lm = LMS_SHA256_N32_H5; - param_set_t ots = LMOTS_SHA256_N32_W8; - (void) heap; - (void) devId; - - key->state = WC_LMS_STATE_NOT_INITED; + param_set_t ots = LMOTS_SHA256_N32_W1; if (key == NULL) { return BAD_FUNC_ARG; } - ForceZero(key, sizeof(LmsKey)); - - /* Verify inputs make sense. Values of 0 may be passed to signify - * using minimum defaults. */ - if (levels == 0) { - levels = MIN_HSS_LEVELS; + if (key->state != WC_LMS_STATE_INITED) { + WOLFSSL_MSG("error: LmsKey needs init"); + return -1; } - else if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) { + + /* 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 == 0) { - height = MIN_MERKLE_HEIGHT; - } - else if (height < MIN_MERKLE_HEIGHT || height > MAX_MERKLE_HEIGHT) { + if (height < MIN_MERKLE_HEIGHT || height > MAX_MERKLE_HEIGHT) { WOLFSSL_MSG("error: invalid height parameter"); return BAD_FUNC_ARG; } - if (winternitz == 0) { - winternitz = 2; - } - switch (height) { case 5: lm = LMS_SHA256_N32_H5; @@ -357,19 +398,17 @@ int wc_LmsKey_Init_ex(LmsKey * key, int levels, int height, key->lm_ots_type[i] = ots; } - /* 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; + /* Move the state to parms set. + * Key is ready for MakeKey or Reload. */ + key->state = WC_LMS_STATE_PARMSET; - return ret; + 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) { @@ -383,12 +422,91 @@ void wc_LmsKey_Free(LmsKey* key) ForceZero(key, sizeof(LmsKey)); - key->state = WC_LMS_STATE_NOT_INITED; + key->state = WC_LMS_STATE_FREED; return; } -int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) +/* 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; @@ -396,7 +514,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) return BAD_FUNC_ARG; } - if (key->state != WC_LMS_STATE_INITED) { + if (key->state != WC_LMS_STATE_PARMSET) { WOLFSSL_MSG("error: LmsKey not ready for generation"); return -1; } @@ -427,6 +545,9 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) * 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, @@ -439,6 +560,8 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) 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); @@ -448,48 +571,25 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG * rng) return -1; } - key->state = WC_LMS_STATE_OK; - - return 0; -} - -int wc_LmsKey_SetWriteCb(LmsKey * key, write_private_key_cb write_cb) -{ - if (key == NULL || write_cb == NULL) { - return BAD_FUNC_ARG; - } - - key->write_private_key = write_cb; - - return 0; -} - -int wc_LmsKey_SetReadCb(LmsKey * key, read_private_key_cb read_cb) -{ - if (key == NULL || read_cb == NULL) { - return BAD_FUNC_ARG; - } - - 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. */ -int wc_LmsKey_SetContext(LmsKey * key, void * context) -{ - if (key == NULL || context == NULL) { - return BAD_FUNC_ARG; + /* 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->context = context; + key->state = WC_LMS_STATE_OK; return 0; } -/* Reload a key that has been prepared with the appropriate read callbacks - * or data. */ +/* 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; @@ -498,7 +598,7 @@ int wc_LmsKey_Reload(LmsKey * key) return BAD_FUNC_ARG; } - if (key->state != WC_LMS_STATE_INITED) { + if (key->state != WC_LMS_STATE_PARMSET) { WOLFSSL_MSG("error: LmsKey not ready for reload"); return -1; } @@ -533,6 +633,13 @@ int wc_LmsKey_Reload(LmsKey * key) 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; @@ -540,7 +647,7 @@ int wc_LmsKey_Reload(LmsKey * key) /* Given a levels, height, winternitz parameter set, determine * the private key length */ -int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len) +int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len) { if (key == NULL || len == NULL) { return BAD_FUNC_ARG; @@ -554,7 +661,7 @@ int wc_LmsKey_GetPrivLen(LmsKey * key, word32 * len) /* Given a levels, height, winternitz parameter set, determine * the public key length */ -int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len) +int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len) { if (key == NULL || len == NULL) { return BAD_FUNC_ARG; @@ -566,9 +673,14 @@ int wc_LmsKey_GetPubLen(LmsKey * key, word32 * len) return 0; } -/* Export a generated public key. Use this to prepare a signature verification - * key that is pub only. */ -int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc) +/* 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; @@ -582,7 +694,9 @@ int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc) sizeof(keySrc->lm_ots_type)); keyDst->levels = keySrc->levels; - keyDst->state = keySrc->state; + + /* Mark this key as verify only, to prevent misuse. */ + keyDst->state = WC_LMS_STATE_VERIFYONLY; return 0; } @@ -591,8 +705,8 @@ int wc_LmsKey_ExportPub(LmsKey * keyDst, const LmsKey * keySrc) * the signature length. * * Call this before wc_LmsKey_Sign so you know the length of - * the required sig buffer. */ -int wc_LmsKey_GetSigLen(LmsKey * key, word32 * len) + * the required signature buffer. */ +int wc_LmsKey_GetSigLen(LmsKey * key, word32 * len) { if (key == NULL || len == NULL) { return BAD_FUNC_ARG; @@ -679,18 +793,18 @@ int wc_LmsKey_Verify(LmsKey * key, const byte * sig, word32 sigSz, return 0; } -int wc_LmsKey_SigsLeft(LmsKey * key) -{ - /* 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. */ +/* 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; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 8caf0d1356..ca9107f873 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -34840,6 +34840,10 @@ static int lms_read_key_mem(byte * priv, word32 privSz, void *context) 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; @@ -34847,13 +34851,22 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) LmsKey signingKey; LmsKey verifyKey; WC_RNG rng; - byte sig[8688]; 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 @@ -34863,7 +34876,7 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) #endif if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } - /* + /* This test: * levels: 1 * height: 5 * winternitz: 1 @@ -34871,7 +34884,14 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) * max sigs: 2 ** (1 * 5) = 32 * signature length: 8688 */ - ret = wc_LmsKey_Init_ex(&signingKey, 1, 5, 1, NULL, INVALID_DEVID); + + 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); @@ -34894,8 +34914,8 @@ WOLFSSL_TEST_SUBROUTINE int lms_test(void) ret = wc_LmsKey_GetSigLen(&verifyKey, &sigSz); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } - if (sigSz != sizeof(sig)) { - printf("error: got %d, expected %zu\n", sigSz, sizeof(sig)); + 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); } diff --git a/wolfssl/wolfcrypt/lms.h b/wolfssl/wolfcrypt/lms.h index 9bd917248a..6a696ec63d 100644 --- a/wolfssl/wolfcrypt/lms.h +++ b/wolfssl/wolfcrypt/lms.h @@ -73,7 +73,10 @@ enum wc_LmsRc { * of available signatures is the same. * */ -/* Predefined LMS/HSS parameter sets for convenience. */ +/* 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. */ @@ -88,22 +91,24 @@ enum wc_LmsParm { 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_OK = 0, - WC_LMS_STATE_NOT_INITED = 1, - WC_LMS_STATE_INITED = 2, - WC_LMS_STATE_BAD = 3, /* Can't guarantee key's state. */ - WC_LMS_STATE_NOSIGS = 4, /* Signatures exhausted. */ + 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 -/* Need an update_key_cb setting function. - * Also maybe a generate rand cb setter. */ -WOLFSSL_API int wc_LmsKey_Init(LmsKey * key, enum wc_LmsParm lmsParm); -WOLFSSL_API int wc_LmsKey_Init_ex(LmsKey * key, int levels, - int height, int winternitz, void* heap, int devId); +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, diff --git a/wolfssl/wolfcrypt/wc_lms.h b/wolfssl/wolfcrypt/wc_lms.h index 62c3ed8118..a0e06e4131 100644 --- a/wolfssl/wolfcrypt/wc_lms.h +++ b/wolfssl/wolfcrypt/wc_lms.h @@ -1,3 +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"