diff --git a/INSTALL b/INSTALL index e8f4c39be..f98e7ad02 100644 --- a/INSTALL +++ b/INSTALL @@ -371,7 +371,7 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. resulting packages are placed in the root directory of the project. -18. Building for RHEL, Fedora, CentOS, SUSE, and openSUSE +19. Building for RHEL, Fedora, CentOS, SUSE, and openSUSE To generate a .rpm package, configure wolfSSL with the desired configuration. Then run `make rpm` to generate a .rpm package @@ -380,3 +380,44 @@ We also have vcpkg ports for wolftpm, wolfmqtt and curl. resulting packages are placed in the root directory of the project. +20. Building with xmss-reference lib for XMSS/XMSS^MT support [EXPERIMENTAL] + + Experimental support for XMSS/XMSS^MT has been achieved by integration + with the xmss-reference implementation from RFC 8391 (XMSS: eXtended + Merkle Signature Scheme). We support a patched version of xmss-reference + based on this git commit: + 171ccbd26f098542a67eb5d2b128281c80bd71a6 + At the time of writing this, this is the HEAD of the master branch of + the xmss-reference project. + + How to get the xmss-reference library: + $ mkdir ~/xmss + $ cd ~/xmss + $ git clone https://github.com/XMSS/xmss-reference.git src + $ cd src + $ git checkout 171ccbd26f098542a67eb5d2b128281c80bd71a6 + $ git apply + + The patch may be found in the wolfssl-examples repo here: + pq/stateful_hash_sig/0001-Patch-to-support-xmss-reference-integration.patch + + Note that this patch adds wolfCrypt SHA256 hashing to xmss-reference, and + thus benefits from all the same asm speedups as wolfCrypt SHA hashing. + Depending on architecture you may build with --enable-intelasm, or + and --enable-armasm, and see 30-50% speedups in XMSS/XMSS^MT. + + For full keygen, signing, verifying, and benchmarking support, build + wolfSSL with: + $ ./configure \ + --enable-xmss \ + --with-libxmss= + $ make + + Run the benchmark against XMSS/XMSS^MT with: + $ ./wolfcrypt/benchmark/benchmark -xmss_xmssmt + + For a leaner xmss verify-only build, build with + $ ./configure \ + --enable-xmss=verify-only \ + --with-libxmss= + $ make diff --git a/configure.ac b/configure.ac index b4e06b7c2..64222a44e 100644 --- a/configure.ac +++ b/configure.ac @@ -1141,6 +1141,79 @@ then fi +# XMSS +AC_ARG_ENABLE([xmss], + [AS_HELP_STRING([--enable-xmss],[Enable stateful XMSS/XMSS^MT signatures (default: disabled)])], + [ ENABLED_XMSS=$enableval ], + [ ENABLED_XMSS=no ] + ) + +ENABLED_WC_XMSS=no +for v in `echo $ENABLED_XMSS | tr "," " "` +do + case $v in + yes) + ;; + no) + ;; + verify-only) + XMSS_VERIFY_ONLY=yes + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_XMSS_VERIFY_ONLY -DXMSS_VERIFY_ONLY" + ;; + wolfssl) + ENABLED_WC_XMSS=yes + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS" + ;; + *) + AC_MSG_ERROR([Invalid choice for XMSS []: $ENABLED_XMSS.]) + break;; + esac +done + +if test "$ENABLED_XMSS" != "no" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_XMSS" + + if test "$ENABLED_WC_XMSS" = "no"; + then + # Default is to use hash-sigs XMSS lib. Make sure it's enabled. + if test "$ENABLED_LIBXMSS" = "no"; then + AC_MSG_ERROR([The default implementation for XMSS is the xmss-reference lib. + Please use --with-libxmss.]) + fi + fi +fi + +# libxmss +# Get the path to xmss-reference. +ENABLED_LIBXMSS="no" +trylibxmssdir="" +AC_ARG_WITH([libxmss], + [AS_HELP_STRING([--with-libxmss=PATH],[PATH to xmss-reference root dir. EXPERIMENTAL!])], + [ + AC_MSG_CHECKING([for libxmss]) + + trylibxmssdir=$withval + + if test -e $trylibxmssdir; then + libxmss_linked=yes + else + AC_MSG_ERROR([libxmss isn't found. + If it's already installed, specify its path using --with-libxmss=/dir/]) + fi + + XMSS_ROOT=$trylibxmssdir + + AC_MSG_RESULT([yes]) + + AM_CFLAGS="$AM_CFLAGS -DHAVE_LIBXMSS -I$trylibxmssdir" + ENABLED_LIBXMSS="yes" + AC_SUBST([XMSS_ROOT]) + ], + [XMSS_ROOT=""] +) + + # LMS AC_ARG_ENABLE([lms], [AS_HELP_STRING([--enable-lms],[Enable stateful LMS/HSS signatures (default: disabled)])], @@ -8999,6 +9072,7 @@ 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_LIBXMSS],[test "x$ENABLED_LIBXMSS" = "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"]) @@ -9431,6 +9505,11 @@ echo " * ED448: $ENABLED_ED448" echo " * ED448 streaming: $ENABLED_ED448_STREAM" echo " * LMS: $ENABLED_LMS" echo " * LMS wolfSSL impl: $ENABLED_WC_LMS" +echo " * XMSS: $ENABLED_XMSS" +echo " * XMSS wolfSSL impl: $ENABLED_WC_XMSS" +if test "$ENABLED_LIBXMSS" = "yes"; then +echo " * XMSS_ROOT: $XMSS_ROOT" +fi echo " * KYBER: $ENABLED_KYBER" echo " * KYBER wolfSSL impl: $ENABLED_WC_KYBER" echo " * ECCSI $ENABLED_ECCSI" @@ -9486,6 +9565,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 " * libxmss: $ENABLED_LIBXMSS" echo " * liblms: $ENABLED_LIBLMS" echo " * liboqs: $ENABLED_LIBOQS" echo " * Whitewood netRandom: $ENABLED_WNR" diff --git a/src/include.am b/src/include.am index c42b8495e..93713f196 100644 --- a/src/include.am +++ b/src/include.am @@ -807,6 +807,18 @@ if BUILD_LIBLMS src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_lms.c endif +if BUILD_LIBXMSS +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/ext_xmss.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/params.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/thash.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/hash_address.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/wots.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/xmss.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/xmss_core_fast.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/xmss_commons.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += $(XMSS_ROOT)/utils.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 78a75e36d..f9967f3ad 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -150,18 +150,24 @@ #endif #ifdef WOLFSSL_HAVE_KYBER #include -#ifdef WOLFSSL_WC_KYBER - #include -#endif -#if defined(HAVE_LIBOQS) || defined(HAVE_PQM4) - #include -#endif + #ifdef WOLFSSL_WC_KYBER + #include + #endif + #if defined(HAVE_LIBOQS) || defined(HAVE_PQM4) + #include + #endif #endif #if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) #include -#ifdef HAVE_LIBLMS - #include + #ifdef HAVE_LIBLMS + #include + #endif #endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + #include + #ifdef HAVE_LIBXMSS + #include + #endif #endif #ifdef WOLFCRYPT_HAVE_ECCSI #include @@ -576,6 +582,7 @@ /* Post-Quantum Stateful Hash-Based sig algorithms. */ #define BENCH_LMS_HSS 0x00000001 +#define BENCH_XMSS_XMSSMT 0x00000002 /* Other */ #define BENCH_RNG 0x00000001 @@ -596,6 +603,11 @@ #endif #endif +#if (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \ + (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)) + #define BENCH_PQ_STATEFUL_HBS +#endif + /* Benchmark all compiled in algorithms. * When 1, ignore other benchmark algorithm values. * 0, only benchmark algorithm values set. @@ -870,7 +882,7 @@ static const bench_alg bench_other_opt[] = { #endif /* !WOLFSSL_BENCHMARK_ALL && !NO_MAIN_DRIVER */ -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) +#if defined(BENCH_PQ_STATEFUL_HBS) typedef struct bench_pq_hash_sig_alg { /* Command line option string. */ const char* str; @@ -880,10 +892,15 @@ typedef struct bench_pq_hash_sig_alg { static const bench_pq_hash_sig_alg bench_pq_hash_sig_opt[] = { { "-pq_hash_sig", 0xffffffff}, +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) { "-lms_hss", BENCH_LMS_HSS}, +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + { "-xmss_xmssmt", BENCH_XMSS_XMSSMT}, +#endif { NULL, 0} }; -#endif /* if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) */ +#endif /* BENCH_PQ_STATEFUL_HBS */ #if defined(HAVE_PQC) && defined(HAVE_LIBOQS) /* The post-quantum-specific mapping of command line option to bit values and @@ -2856,6 +2873,12 @@ static void* benchmarks_do(void* args) } #endif /* if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if (bench_all || (bench_pq_hash_sig_algs & BENCH_XMSS_XMSSMT)) { + bench_xmss(); + } +#endif /* if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) */ + #ifdef HAVE_ECC if (bench_all || (bench_asym_algs & BENCH_ECC_MAKEKEY) || (bench_asym_algs & BENCH_ECC) || @@ -8075,6 +8098,248 @@ void bench_lms(void) #endif /* if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + +static int xmss_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_XMSS_RC_SAVED_TO_NV_MEMORY; +} + +static int xmss_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_XMSS_RC_READ_TO_MEMORY; +} + +static void bench_xmss_sign_verify(const char * params) +{ + WC_RNG rng; + XmssKey key; + word32 pkSz = 0; + word32 skSz = 0; + int freeRng = 0; + int freeKey = 0; + unsigned char * sk = NULL; + const char * msg = "XMSS post quantum signature test"; + word32 msgSz = (word32) XSTRLEN(msg); + int ret = 0; + byte * sig = NULL; + word32 sigSz = 0; + int times = 0; + int count = 0; + double start = 0.0F; + +#ifndef HAVE_FIPS + ret = wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID); +#else + ret = wc_InitRng(&rng); +#endif + if (ret != 0) { + fprintf(stderr, "error: wc_InitRng failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + freeRng = 1; + + ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); + if (ret != 0) { + fprintf(stderr, "wc_XmssKey_Init failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_SetParamStr(&key, params); + if (ret != 0) { + fprintf(stderr, "wc_XmssKey_SetParamStr failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_GetPubLen(&key, &pkSz); + if (pkSz != XMSS_SHA256_PUBLEN) { + fprintf(stderr, "error: xmss pub len: got %d, expected %d\n", pkSz, + XMSS_SHA256_PUBLEN); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_GetPrivLen(&key, &skSz); + if (ret != 0 || skSz <= 0) { + fprintf(stderr, "error: wc_XmssKey_GetPrivLen failed\n"); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_GetSigLen(&key, &sigSz); + if (ret != 0 || sigSz <= 0) { + fprintf(stderr, "error: wc_XmssKey_GetSigLen failed\n"); + goto exit_xmss_sign_verify; + } + + /* Allocate secret keys.*/ + sk = XMALLOC(skSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (sk == NULL) { + fprintf(stderr, "error: allocate xmss sk failed\n"); + goto exit_xmss_sign_verify; + } + + /* Allocate signature array. */ + sig = XMALLOC(sigSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (sig == NULL) { + fprintf(stderr, "error: allocate xmss sig failed\n"); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_SetWriteCb(&key, xmss_write_key_mem); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetWriteCb failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_SetReadCb(&key, xmss_read_key_mem); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetReadCb failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + ret = wc_XmssKey_SetContext(&key, (void *) sk); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetContext failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + +#if defined(DEBUG_WOLFSSL) || defined(WOLFSSL_DEBUG_NONBLOCK) + fprintf(stderr, "params: %s\n", params); + fprintf(stderr, "pkSz: %d\n", pkSz); + fprintf(stderr, "skSz: %d\n", skSz); + fprintf(stderr, "sigSz: %d\n", sigSz); +#endif + + /* Making 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_XmssKey_MakeKey(&key, &rng); + if (ret != 0) { + printf("wc_XmssKey_MakeKey failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + + count +=1; + +#if defined(DEBUG_WOLFSSL) + bench_stats_check(start); + bench_stats_asym_finish(params, (int)skSz, "load", 0, + count, start, ret); +#endif /* if defined DEBUG_WOLFSSL*/ + + freeKey = 1; + + count = 0; + bench_stats_start(&count, &start); + + do { + /* XMSS is stateful. Async queuing not practical. */ + for (times = 0; times < ntimes; ++times) { + + ret = wc_XmssKey_Sign(&key, sig, &sigSz, (byte *) msg, msgSz); + if (ret) { + printf("wc_XmssKey_Sign failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + } + + count += times; + } while (bench_stats_check(start)); + + bench_stats_asym_finish(params, (int)sigSz, "sign", 0, + count, start, ret); + + count = 0; + bench_stats_start(&count, &start); + + do { + /* XMSS is stateful. Async queuing not practical. */ + for (times = 0; times < ntimes; ++times) { + ret = wc_XmssKey_Verify(&key, sig, sigSz, (byte *) msg, msgSz); + if (ret) { + printf("wc_XmssKey_Verify failed: %d\n", ret); + goto exit_xmss_sign_verify; + } + } + + count += times; + } while (bench_stats_check(start)); + +exit_xmss_sign_verify: + bench_stats_asym_finish(params, (int)sigSz, "verify", 0, + count, start, ret); + + /* Cleanup everything. */ + if (sig != NULL) { + XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + sig = NULL; + } + + if (sk != NULL) { + XFREE(sk, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + sk = NULL; + } + + if (freeRng) { + wc_FreeRng(&rng); + freeRng = 0; + } + + if (freeKey) { + wc_XmssKey_Free(&key); + freeKey = 0; + } + + if (sig != NULL) { + XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + sig = NULL; + } + + return; +} + +void bench_xmss(void) +{ + /* All NIST SP 800-208 approved SHA256 XMSS/XMSS^MT parameter + * sets. + * + * note: not testing "XMSS-SHA2_16_256", "XMSS-SHA2_20_256", + * and "XMSSMT-SHA2_60/3_256", because their keygen can be + * very slow, their signatures and private keys quite large, + * and xmss private keys are not portable across different + * XMSS/XMSS^MT implementations. + * + * The bottleneck in key generation is the height of the first + * level tree (or h/d). + * + * h is the total height of the hyper tree, and d the number of + * trees. + * */ + /* h/d h d */ + bench_xmss_sign_verify("XMSS-SHA2_10_256"); /* 10 10 1 */ + /* bench_xmss_sign_verify("XMSS-SHA2_16_256"); */ /* 16 16 1 */ + /* bench_xmss_sign_verify("XMSS-SHA2_20_256"); */ /* 20 20 1 */ + bench_xmss_sign_verify("XMSSMT-SHA2_20/2_256"); /* 10 20 2 */ + bench_xmss_sign_verify("XMSSMT-SHA2_20/4_256"); /* 5 20 4 */ + bench_xmss_sign_verify("XMSSMT-SHA2_40/4_256"); /* 10 40 4 */ + bench_xmss_sign_verify("XMSSMT-SHA2_40/8_256"); /* 5 40 8 */ + /* bench_xmss_sign_verify("XMSSMT-SHA2_60/3_256"); */ /* 20 60 3 */ + bench_xmss_sign_verify("XMSSMT-SHA2_60/6_256"); /* 10 60 6 */ + bench_xmss_sign_verify("XMSSMT-SHA2_60/12_256"); /* 5 60 12 */ + return; +} +#endif /* if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) */ + #ifdef HAVE_ECC /* Maximum ECC name plus null terminator: @@ -10410,10 +10675,10 @@ static void Usage(void) print_alg(bench_pq_asym_opt2[i].str, &line); #endif /* HAVE_LIBOQS && HAVE_SPHINCS */ #endif /* HAVE_PQC */ -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) +#if defined(BENCH_PQ_STATEFUL_HBS) 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) && !defined(WOLFSSL_LMS_VERIFY_ONLY) */ +#endif /* BENCH_PQ_STATEFUL_HBS */ printf("\n"); #endif /* !WOLFSSL_BENCHMARK_ALL */ e++; @@ -10676,7 +10941,7 @@ int wolfcrypt_benchmark_main(int argc, char** argv) } } - #if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + #if defined(BENCH_PQ_STATEFUL_HBS) /* 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)) { @@ -10685,7 +10950,7 @@ int wolfcrypt_benchmark_main(int argc, char** argv) optMatched = 1; } } - #endif + #endif /* BENCH_PQ_STATEFUL_HBS */ #endif if (!optMatched) { printf("Option not recognized: %s\n", argv[1]); diff --git a/wolfcrypt/benchmark/benchmark.h b/wolfcrypt/benchmark/benchmark.h index 744c667dd..765d15f55 100644 --- a/wolfcrypt/benchmark/benchmark.h +++ b/wolfcrypt/benchmark/benchmark.h @@ -102,6 +102,7 @@ void bench_rsa_key(int useDeviceID, word32 keySz); void bench_dh(int useDeviceID); void bench_kyber(int type); void bench_lms(void); +void bench_xmss(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_xmss.c b/wolfcrypt/src/ext_xmss.c new file mode 100644 index 000000000..b12ae4440 --- /dev/null +++ b/wolfcrypt/src/ext_xmss.c @@ -0,0 +1,894 @@ +/* ext_xmss.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_XMSS +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* Init an Xmss key. + * + * Call this before setting the parms of an Xmss key. + * + * key [in] The Xmss key to init. + * heap [in] Unused. + * devId [in] Unused. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * */ +int wc_XmssKey_Init(XmssKey * key, void * heap, int devId) +{ + if (key == NULL) { + return BAD_FUNC_ARG; + } + + (void) heap; + (void) devId; + + ForceZero(key, sizeof(XmssKey)); + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + key->sk = NULL; + key->write_private_key = NULL; + key->read_private_key = NULL; + key->context = NULL; +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */ + key->state = WC_XMSS_STATE_INITED; + + return 0; +} + +/* Sets the Xmss key parameters, given an oid. + * + * Note: XMSS and XMSS^MT parameter sets do have overlapping + * oids, therefore is_xmssmt is necessary to toggle. + * + * key [in] The Xmss key to set. + * oid [in] The Xmss parameter set oid. + * is_xmssmt [in] 1 The oid is assumed to be XMSS^MT. + * 0 The oid is assumed to be XMSS. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on parse failure. + * */ +static int wc_XmssKey_SetOid(XmssKey * key, uint32_t oid, int is_xmssmt) +{ + int ret = 0; + + if (key == NULL || oid == 0) { + return BAD_FUNC_ARG; + } + + /* Parse the oid and load the xmss params structure. */ + if (is_xmssmt) { + ret = xmssmt_parse_oid(&key->params, oid); + } + else { + ret = xmss_parse_oid(&key->params, oid); + } + + if (ret != 0) { + WOLFSSL_MSG("error: xmss parse oid failed"); + return -1; + } + + /* Finally, sanity check that this is a supported parameter set. + * + * We are only supporting XMSS/XMSS^MT with SHA256 parameter sets + * that NIST SP 800-208 has standardized. See patched xmss-reference + * params.h for the defines. */ + if (key->params.func != XMSS_SHA2 || + key->params.n != XMSS_SHA256_N || + key->params.padding_len != XMSS_SHA256_PADDING_LEN || + key->params.wots_w != 16 || + key->params.wots_len != XMSS_SHA256_WOTS_LEN) { + WOLFSSL_MSG("error: unsupported XMSS/XMSS^MT parameter set"); + return -1; + } + + key->oid = oid; + key->is_xmssmt = is_xmssmt; + key->state = WC_XMSS_STATE_PARMSET; + + return 0; +} + +/* Set the Xmss key parameter string. + * + * The input string must be one of the supported parm set names in + * the "Name" section from the table in wolfssl/wolfcrypt/xmss.h, + * e.g. "XMSS-SHA2_10_256" or "XMSSMT-SHA2_20/4_256". + * + * key [in] The Xmss key to set. + * str [in] The XMSS/XMSS^MT parameter string. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on failure. + * */ +int wc_XmssKey_SetParamStr(XmssKey * key, const char * str) +{ + int ret = 0; + uint32_t oid = 0; + int is_xmssmt = 0; + + if (key == NULL || str == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_INITED) { + WOLFSSL_MSG("error: xmss key needs init"); + return BAD_FUNC_ARG; + } + + switch(XSTRLEN(str)) { + case XMSS_NAME_LEN: + is_xmssmt = 0; + break; + case XMSSMT_NAME_MIN_LEN: + case XMSSMT_NAME_MAX_LEN: + is_xmssmt = 1; + break; + default: + WOLFSSL_MSG("error: xmss param str invalid length"); + return BAD_FUNC_ARG; + } + + /* Convert xmss param string to oid. */ + if (is_xmssmt) { + ret = xmssmt_str_to_oid(&oid, str); + } + else { + ret = xmss_str_to_oid(&oid, str); + } + + if (ret != 0) { + WOLFSSL_MSG("error: xmssmt_str_to_oid failed"); + return -1; + } + + return wc_XmssKey_SetOid(key, oid, is_xmssmt); +} + +/* Force zeros and frees the Xmss key from memory. + * + * This does not touch the private key saved to non-volatile storage. + * + * This is the only function that frees the key->sk array. + * + * key [in] The Xmss key. + * + * returns void + * */ +void wc_XmssKey_Free(XmssKey* key) +{ + if (key == NULL) { + return; + } + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + if (key->sk != NULL) { + ForceZero(key->sk, key->sk_len); + XFREE(key->sk, NULL, DYNAMIC_TYPE_TMP_BUFFER); + key->sk = NULL; + } +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */ + + ForceZero(key, sizeof(XmssKey)); + + key->state = WC_XMSS_STATE_FREED; + + return; +} + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY +/* Sets the Xmss write private key callback. + * + * The callback must be able to write/update the private key to + * non-volatile storage. + * + * key [in] The Xmss key. + * write_cb [in] The write private key callback. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on failure. + * */ +int wc_XmssKey_SetWriteCb(XmssKey * 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_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetWriteCb: key in use"); + return -1; + } + + key->write_private_key = write_cb; + + return 0; +} + +/* Sets the Xmss read private key callback. + * + * The callback must be able to read the private key from + * non-volatile storage. + * + * key [in] The Xmss key. + * read_cb [in] The read private key callback. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on failure. + * */ +int wc_XmssKey_SetReadCb(XmssKey * 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_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetReadCb: key in use"); + return -1; + } + + key->read_private_key = read_cb; + + return 0; +} + +/* Sets the Xmss context to be used by write and read callbacks. + * + * E.g. this could be a filename if the callbacks write/read to file. + * + * key [in] The Xmss key. + * context [in] The context pointer. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on failure. + * */ +int wc_XmssKey_SetContext(XmssKey * 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_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetContext: key in use"); + return -1; + } + + key->context = context; + + return 0; +} + + +/* Allocates the Xmss secret key (sk) array. + * + * The XMSS/XMSS^MT secret key length is a function of the + * parameters, and can't be allocated until the param string + * has been set with SetParamStr. + * + * This is only called by MakeKey() and Reload(). + * + * Note: the Xmss sk array is force zeroed after every use. + * + * key [in] The Xmss key. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on failure. + * */ +static int wc_XmssKey_AllocSk(XmssKey* key) +{ + int ret = 0; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + if (key->sk != NULL) { + WOLFSSL_MSG("error: xmss secret key already exists"); + return -1; + } + + /* The XMSS/XMSS^MT secret key length is a function of the + * parameters. Therefore can't allocate this until param + * string has been set. */ + ret = wc_XmssKey_GetPrivLen(key, &key->sk_len); + + if (ret != 0 || key->sk_len <= 0) { + WOLFSSL_MSG("error: wc_XmssKey_GetPrivLen failed"); + return -1; + } + + key->sk = XMALLOC(key->sk_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + if (key->sk == NULL) { + WOLFSSL_MSG("error: malloc Xmss key->sk failed"); + return -1; + } + + ForceZero(key->sk, key->sk_len); + + return 0; +} + +/* Make the XMSS/XMSS^MT private/public key pair. The key must have its parameters + * set before calling this. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * This function and Reload() are the only functions that allocate + * key->sk array. wc_XmssKey_FreeKey is the only function that + * deallocates key->sk. + * + * key [in] The Xmss key to make. + * rng [in] Initialized WC_RNG pointer. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on verify fail. + * */ +int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG * rng) +{ + int ret = 0; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; + + if (key == NULL || rng == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_PARMSET) { + WOLFSSL_MSG("error: XmssKey not ready for generation"); + return -1; + } + + if (key->write_private_key == NULL || key->read_private_key == NULL) { + WOLFSSL_MSG("error: XmssKey write/read callbacks are not set"); + return -1; + } + + if (key->context == NULL) { + WOLFSSL_MSG("error: XmssKey context is not set"); + return -1; + } + + /* Allocate sk array. */ + ret = wc_XmssKey_AllocSk(key); + + if (ret != 0) { + return ret; + } + + /* Finally make the secret public key pair. Immediately write it to NV + * storage and then clear from memory. */ + if (key->is_xmssmt) { + ret = xmssmt_keypair(key->pk, key->sk, key->oid, rng); + } + else { + ret = xmss_keypair(key->pk, key->sk, key->oid, rng); + } + + if (ret == 0) { + cb_rc = key->write_private_key(key->sk, key->sk_len, key->context); + } + + ForceZero(key->sk, key->sk_len); + + if (ret != 0) { + WOLFSSL_MSG("error: xmss keypair failed"); + key->state = WC_XMSS_STATE_BAD; + return -1; + } + + if (cb_rc != WC_XMSS_RC_SAVED_TO_NV_MEMORY) { + WOLFSSL_MSG("error: xmss write to NV storage failed"); + key->state = WC_XMSS_STATE_BAD; + return -1; + } + + key->state = WC_XMSS_STATE_OK; + + return 0; +} + +/* This function allocates the secret key buffer, and does a + * quick sanity check to verify the secret key is readable + * from NV storage, and then force zeros the key from memory. + * + * On success it sets the key state to OK. + * + * Use this function to resume signing with an already existing + * xmss key pair. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * Returns 0 on success. + * + * This function and MakeKey are the only functions that allocate + * key->sk array. wc_XmssKey_FreeKey is the only function that + * deallocates key->sk. + * + * key [in] Xmss key to load. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on load fail. + * */ +int wc_XmssKey_Reload(XmssKey * key) +{ + int ret = 0; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_PARMSET) { + WOLFSSL_MSG("error: XmssKey not ready for reload"); + return -1; + } + + if (key->write_private_key == NULL || key->read_private_key == NULL) { + WOLFSSL_MSG("error: XmssKey write/read callbacks are not set"); + return -1; + } + + if (key->context == NULL) { + WOLFSSL_MSG("error: XmssKey context is not set"); + return -1; + } + + /* Allocate sk array. */ + ret = wc_XmssKey_AllocSk(key); + + if (ret != 0) { + return ret; + } + + /* Read the current secret key from NV storage. Force clear it + * immediately. This is just to sanity check the secret key + * is readable from permanent storage. */ + cb_rc = key->read_private_key(key->sk, key->sk_len, key->context); + ForceZero(key->sk, key->sk_len); + + if (cb_rc != WC_XMSS_RC_READ_TO_MEMORY) { + WOLFSSL_MSG("error: xmss read from NV storage failed"); + key->state = WC_XMSS_STATE_BAD; + return -1; + } + + key->state = WC_XMSS_STATE_OK; + + return 0; +} + +/* Gets the XMSS/XMSS^MT private key length. + * + * Parameters must be set before calling this, as the key size (sk_bytes) + * is a function of the parameters. + * + * Note: the XMSS/XMSS^MT private key format is implementation specific, + * and not standardized. Interoperability of Xmss private keys should + * not be expected. + * + * key [in] The Xmss key. + * len [out] The length of the private key in bytes. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on sign fail. + * */ +int wc_XmssKey_GetPrivLen(const XmssKey * key, word32 * len) +{ + if (key == NULL || len == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_OK && key->state != WC_XMSS_STATE_PARMSET) { + /* params.sk_bytes not set yet. */ + return -1; + } + + *len = XMSS_OID_LEN + (word32) key->params.sk_bytes; + + return 0; +} + +/* Signs the message using the Xmss secret key, and + * updates the secret key on NV storage. + * + * Both operations must succeed to be considered + * successful. + * + * On success: sets key state to WC_XMSS_STATE_OK. + * On failure: sets key state to WC_XMSS_STATE_BAD + * + * If no signatures are left, sets state to WC_XMSS_STATE_NOSIGS. + */ +static void wc_XmssKey_SignUpdate(XmssKey* key, byte * sig, word32 * sigLen, + const byte * msg, int msgLen) +{ + int ret = -1; + unsigned long long len = *sigLen; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; + + /* Set the key state to bad by default. State is presumed bad + * unless a correct sign and update operation happen together. */ + key->state = WC_XMSS_STATE_BAD; + *sigLen = 0; + + /* Read the current secret key from NV storage.*/ + cb_rc = key->read_private_key(key->sk, key->sk_len, key->context); + + if (cb_rc == WC_XMSS_RC_READ_TO_MEMORY) { + /* Read was good. Now sign and update the secret key in memory. */ + if (key->is_xmssmt) { + ret = xmssmt_sign(key->sk, sig, &len, msg, msgLen); + } + else { + ret = xmss_sign(key->sk, sig, &len, msg, msgLen); + } + + if (ret == 0 && len == key->params.sig_bytes) { + /* The signature succeeded. key->sk is now updated and must be + * committed to NV storage. */ + cb_rc = key->write_private_key(key->sk, key->sk_len, key->context); + + if (cb_rc == WC_XMSS_RC_SAVED_TO_NV_MEMORY) { + /* key->sk was successfully committed to NV storage. Set the + * key state to OK, and set the sigLen. */ + key->state = WC_XMSS_STATE_OK; + *sigLen = (word32) len; + } + else { + /* Write to NV storage failed. Erase the signature from + * memory. */ + ForceZero(sig, key->params.sig_bytes); + WOLFSSL_MSG("error: xmss write_private_key failed"); + } + } + else if (ret == -2) { + /* Signature space exhausted. */ + key->state = WC_XMSS_STATE_NOSIGS; + WOLFSSL_MSG("error: no xmss signatures remaining"); + } + else { + /* Something failed or inconsistent in signature. Erase the + * signature just to be safe. */ + ForceZero(sig, key->params.sig_bytes); + WOLFSSL_MSG("error: xmss sign failed"); + } + } + else { + /* Read from NV storage failed. */ + WOLFSSL_MSG("error: xmss read_private_key failed"); + } + + /* Force zero the secret key from memory always. */ + ForceZero(key->sk, key->sk_len); + + return; +} + +/* Sign the message using the Xmss secret key. + * + * key [in] Xmss key to use to sign. + * sig [in] Buffer to write signature into. + * sigLen [in/out] On in, size of buffer. + * On out, the length of the signature in bytes. + * msg [in] Message to sign. + * msgLen [in] Length of the message in bytes. + * + * returns 0 on success. + * returns -1 on sign fail. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns BUFFER_E when sigLen is too small. + */ +int wc_XmssKey_Sign(XmssKey* key, byte * sig, word32 * sigLen, const byte * msg, + int msgLen) +{ + if (key == NULL || sig == NULL || sigLen == NULL || msg == NULL) { + return BAD_FUNC_ARG; + } + + if (msgLen <= 0) { + return BAD_FUNC_ARG; + } + + if (*sigLen < key->params.sig_bytes) { + /* Signature buffer too small. */ + WOLFSSL_MSG("error: xmss sig buffer too small"); + return BUFFER_E; + } + + if (key->state == WC_XMSS_STATE_NOSIGS) { + WOLFSSL_MSG("error: xmss signatures exhausted"); + return -1; + } + else if (key->state != WC_XMSS_STATE_OK) { + /* The key had an error the last time it was used, and we + * can't guarantee its state. */ + WOLFSSL_MSG("error: can't sign, xmss key not in good state"); + return -1; + } + + /* Finally, sign and update the secret key. */ + wc_XmssKey_SignUpdate(key, sig, sigLen, msg, msgLen); + + return (key->state == WC_XMSS_STATE_OK) ? 0 : -1; +} +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY*/ + +/* Get the XMSS/XMSS^MT public key length. The public key + * is static in size and does not depend on parameters, + * other than the choice of SHA256 as hashing function. + * + * key [in] The Xmss key. + * len [out] The length of the public key. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + */ +int wc_XmssKey_GetPubLen(const XmssKey * key, word32 * len) +{ + if (key == NULL || len == NULL) { + return BAD_FUNC_ARG; + } + + *len = XMSS_SHA256_PUBLEN; + + return 0; +} + +/* Export a generated public key and parameter set from one XmssKey + * to another. Use this to prepare a signature verification XmssKey + * that is pub only. + * + * keyDst [out] Destination key for copy. + * keySrc [in] Source key for copy. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * */ +int wc_XmssKey_ExportPub(XmssKey * keyDst, const XmssKey * keySrc) +{ + if (keyDst == NULL || keySrc == NULL) { + return BAD_FUNC_ARG; + } + + ForceZero(keyDst, sizeof(XmssKey)); + + XMEMCPY(keyDst->pk, keySrc->pk, sizeof(keySrc->pk)); + + keyDst->oid = keySrc->oid; + keyDst->is_xmssmt = keySrc->is_xmssmt; + + /* Mark keyDst as verify only, to prevent misuse. */ + keyDst->state = WC_XMSS_STATE_VERIFYONLY; + + return 0; +} + +/* Exports the raw XMSS public key buffer from key to out buffer. + * The out buffer should be large enough to hold the public key, and + * outLen should indicate the size of the buffer. + * + * key [in] Xmss key. + * out [out] Array holding public key. + * outLen [in/out] On in, size of buffer. + * On out, the length of the public key. + * + * returns 0 on success. + * returns -1 on failure. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns BUFFER_E if array is too small. + * */ +int wc_XmssKey_ExportPubRaw(const XmssKey * key, byte * out, word32 * outLen) +{ + int ret = 0; + word32 pubLen = 0; + + if (key == NULL || out == NULL || outLen == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_XmssKey_GetPubLen(key, &pubLen); + + if (ret != 0) { + WOLFSSL_MSG("error: wc_XmssKey_GetPubLen failed"); + return -1; + } + + if (*outLen < pubLen) { + return BUFFER_E; + } + + XMEMCPY(out, key->pk, pubLen); + *outLen = pubLen; + + return 0; +} + +/* Imports a raw public key buffer from in array to XmssKey key. + * + * The XMSS parameters must be set first with wc_XmssKey_SetParamStr, + * and inLen must match the length returned by wc_XmssKey_GetPubLen. + * + * key [in] Xmss key. + * in [in] Array holding public key. + * inLen [in] Length of array in bytes. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns BUFFER_E if array is incorrect size. + * returns -1 on failure. + * */ +int wc_XmssKey_ImportPubRaw(XmssKey * key, const byte * in, word32 inLen) +{ + int ret = 0; + word32 pubLen = 0; + + if (key == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_PARMSET) { + /* Xmss key not ready for import. Param str must be set first. */ + WOLFSSL_MSG("error: xmss key not ready for import"); + return -1; + } + + ret = wc_XmssKey_GetPubLen(key, &pubLen); + + if (ret != 0) { + WOLFSSL_MSG("error: wc_XmssKey_GetPubLen failed"); + return -1; + } + + if (inLen != pubLen) { + /* Something inconsistent. Parameters weren't set, or input + * pub key is wrong.*/ + return BUFFER_E; + } + + XMEMCPY(key->pk, in, pubLen); + + key->state = WC_XMSS_STATE_VERIFYONLY; + + return 0; +} + +/* Gets the XMSS/XMSS^MT signature length. + * + * Parameters must be set before calling this, as the signature size + * is a function of the parameters. + * + * Note: call this before wc_XmssKey_Sign or Verify so you know the + * length of the required signature buffer. + * + * key [in] Xmss key to use to sign. + * len [out] The length of the signature in bytes. + * + * returns 0 on success. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns -1 on sign fail. + * */ +int wc_XmssKey_GetSigLen(const XmssKey * key, word32 * len) +{ + if (key == NULL || len == NULL) { + return BAD_FUNC_ARG; + } + + if (key->state != WC_XMSS_STATE_OK && key->state != WC_XMSS_STATE_PARMSET) { + return -1; + } + + *len = key->params.sig_bytes; + + return 0; +} + +/* Verify the signature using the Xmss public key. + * + * Requires that Xmss parameters have been set with + * wc_XmssKey_SetParamStr, and that a public key is available + * from importing or MakeKey(). + * + * Call wc_XmssKey_GetSigLen() before this function to determine + * length of the signature buffer. + * + * key [in] Xmss key to use to verify. + * sig [in] Signature to verify. + * sigLen [in] Size of signature in bytes. + * msg [in] Message to verify. + * msgLen [in] Length of the message in bytes. + * + * returns 0 on success. + * returns -1 on verify fail. + * returns BAD_FUNC_ARG when a parameter is NULL. + * returns BUFFER_E when sigLen is too small. + */ +int wc_XmssKey_Verify(XmssKey * key, const byte * sig, word32 sigLen, + const byte * msg, int msgLen) +{ + int ret = 0; + unsigned long long msg_len = 0; + + msg_len = msgLen; + + if (key == NULL || sig == NULL || msg == NULL) { + return BAD_FUNC_ARG; + } + + if (sigLen < key->params.sig_bytes) { + /* Signature buffer too small. */ + return BUFFER_E; + } + + if (key->state != WC_XMSS_STATE_OK && + key->state != WC_XMSS_STATE_VERIFYONLY) { + /* Xmss key not ready for verification. Param str must be + * set first, and Reload() called. */ + WOLFSSL_MSG("error: xmss key not ready for verification"); + return -1; + } + + if (key->is_xmssmt) { + ret = xmssmt_sign_open(msg, &msg_len, sig, sigLen, key->pk); + } + else { + ret = xmss_sign_open(msg, &msg_len, sig, sigLen, key->pk); + } + + if (ret != 0 || (int) msg_len != msgLen) { + WOLFSSL_MSG("error: xmss verify failed"); + return -1; + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_XMSS */ diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c new file mode 100644 index 000000000..545b531ce --- /dev/null +++ b/wolfcrypt/src/wc_xmss.c @@ -0,0 +1,26 @@ +/* wc_xmss.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_XMSS + #error "Contact wolfSSL to get the implementation of this file" +#endif diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 4f6623531..9ececfb91 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -293,6 +293,12 @@ const byte const_byte_array[] = "A+Gd\0\0\0"; #include #endif #endif +#if defined(WOLFSSL_HAVE_XMSS) + #include +#ifdef HAVE_LIBXMSS + #include +#endif +#endif #if defined(WOLFSSL_HAVE_LMS) #include #ifdef HAVE_LIBLMS @@ -577,6 +583,14 @@ 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 +#if defined(WOLFSSL_HAVE_XMSS) + #if !defined(WOLFSSL_XMSS_VERIFY_ONLY) + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t xmss_test(void); + #endif + #if defined(WOLFSSL_XMSS_VERIFY_ONLY) && !defined(WOLFSSL_SMALL_STACK) + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t xmss_test_verify_only(void); + #endif +#endif #if defined(WOLFSSL_HAVE_LMS) #if !defined(WOLFSSL_LMS_VERIFY_ONLY) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void); @@ -1623,6 +1637,22 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("KYBER test passed!\n"); #endif +#if defined(WOLFSSL_HAVE_XMSS) + #if !defined(WOLFSSL_XMSS_VERIFY_ONLY) + if ( (ret = xmss_test()) != 0) + TEST_FAIL("XMSS test failed!\n", ret); + else + TEST_PASS("XMSS test passed!\n"); + #endif + + #if defined(WOLFSSL_XMSS_VERIFY_ONLY) && !defined(WOLFSSL_SMALL_STACK) + if ( (ret = xmss_test_verify_only()) != 0) + TEST_FAIL("XMSS test failed!\n", ret); + else + TEST_PASS("XMSS test passed!\n"); + #endif +#endif /* if defined(WOLFSSL_HAVE_XMSS) */ + #if defined(WOLFSSL_HAVE_LMS) #if !defined(WOLFSSL_LMS_VERIFY_ONLY) if ( (ret = lms_test()) != 0) @@ -35091,6 +35121,616 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t kyber_test(void) } #endif /* WOLFSSL_HAVE_KYBER */ +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) +static int xmss_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_XMSS_RC_SAVED_TO_NV_MEMORY; +} + +static int xmss_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_XMSS_RC_READ_TO_MEMORY; +} + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t xmss_test(void) +{ + int i = 0; + int j = 0; + int ret = -1; + int ret2 = -1; + XmssKey signingKey; + XmssKey verifyKey; + WC_RNG rng; + word32 pkSz = 0; + word32 skSz = 0; + word32 sigSz = 0; + word32 bufSz = 0; + unsigned char * sk = NULL; + unsigned char * old_sk = NULL; + const char * msg = "XMSS post quantum signature test"; + word32 msgSz = (word32) XSTRLEN(msg); + const char * param = "XMSSMT-SHA2_20/4_256"; + byte * sig = NULL; + +#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); } + + ret = wc_XmssKey_Init(&signingKey, NULL, INVALID_DEVID); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_Init(&verifyKey, NULL, INVALID_DEVID); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + /* Set the parameter string to the signing key, and + * get sizes for secret key, pub key, and signature. */ + ret = wc_XmssKey_SetParamStr(&signingKey, param); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_GetPubLen(&signingKey, &pkSz); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + if (pkSz != XMSS_SHA256_PUBLEN) { + return WC_TEST_RET_ENC_EC(pkSz); + } + + ret = wc_XmssKey_GetPrivLen(&signingKey, &skSz); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_GetSigLen(&signingKey, &sigSz); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + /* Allocate signature array. */ + sig = XMALLOC(sigSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (sig == NULL) { return WC_TEST_RET_ENC_ERRNO; } + + bufSz = sigSz; + +#if defined(DEBUG_WOLFSSL) || defined(WOLFSSL_DEBUG_NONBLOCK) + fprintf(stderr, "param: %s\n", param); + fprintf(stderr, "pkSz: %d\n", pkSz); + fprintf(stderr, "skSz: %d\n", skSz); + fprintf(stderr, "sigSz: %d\n", sigSz); +#endif + + /* Allocate current and old secret keys.*/ + sk = XMALLOC(skSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (sk == NULL) { return WC_TEST_RET_ENC_ERRNO; } + + old_sk = XMALLOC(skSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (old_sk == NULL) { return WC_TEST_RET_ENC_ERRNO; } + + XMEMSET(sk, 0, skSz); + XMEMSET(old_sk, 0, skSz); + XMEMSET(sig, 0, sigSz); + + ret = wc_XmssKey_SetWriteCb(&signingKey, xmss_write_key_mem); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_SetReadCb(&signingKey, xmss_read_key_mem); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_SetContext(&signingKey, (void *) sk); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_MakeKey(&signingKey, &rng); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + /* Export the pub to a verify key. */ + ret = wc_XmssKey_ExportPub(&verifyKey, &signingKey); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + /* Repeat a few times to check that: + * 1. The secret key is mutated on each sign. + * 2. We can verify each new signature. + * Only do a few times, because the full signature space + * for this parameter set is huge. */ + for (i = 0; i < 10; ++i) { + XMEMCPY(old_sk, sk, skSz); + + ret = wc_XmssKey_Sign(&signingKey, sig, &sigSz, (byte *) msg, msgSz); + if (ret != 0) { return WC_TEST_RET_ENC_I(i); } + if (sigSz != bufSz) { return WC_TEST_RET_ENC_I(i); } + + /* Old secret key and current secret key should not match. */ + ret = XMEMCMP(old_sk, sk, skSz); + if (ret == 0) { return WC_TEST_RET_ENC_I(i); } + + ret = wc_XmssKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, msgSz); + if (ret != 0) { return WC_TEST_RET_ENC_I(i); } + + /* Flip bits in a few places throughout the signature, stepping in multiple + * of hash size. These should all fail with -1. */ + for (j = 0; j < (int) sigSz; j+= 4 * 32) { + sig[j] ^= 1; + + ret2 = wc_XmssKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, + msgSz); + if (ret2 != -1) { + /* Verify passed when it should have failed. */ + return WC_TEST_RET_ENC_I(j); + } + + /* Flip this spot back. */ + sig[j] ^= 1; + } + } + + /* Cleanup everything. */ + if (sig != NULL) { + XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + sig = NULL; + } + + if (sk != NULL) { + XFREE(sk, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + sk = NULL; + } + + if (old_sk != NULL) { + XFREE(old_sk, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + old_sk = NULL; + } + + wc_XmssKey_Free(&signingKey); + wc_FreeRng(&rng); + + return ret; +} +#endif /*if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)*/ + +#if defined(WOLFSSL_HAVE_XMSS) && defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + !defined(WOLFSSL_SMALL_STACK) + +/* A simple xmss verify only test using: + * XMSS-SHA2_10_256 + * pub len: 68 + * msg len: 32 + * sig len: 2500 + * + * These were generated with the test xmss_fast, from the unpatched + * xmss-reference repository: + * https://github.com/XMSS/xmss-reference + * */ + +static byte xmss_pub[XMSS_SHA256_PUBLEN] = +{ + 0x00,0x00,0x00,0x01,0xA5,0x41,0x31,0x96, + 0x0A,0xF9,0xF3,0xB2,0x4B,0x2E,0x5B,0x3E, + 0xCA,0x74,0xAD,0x6C,0xA5,0x89,0xAD,0x2C, + 0x0E,0x96,0xB3,0x54,0xFB,0x5B,0x63,0x50, + 0x96,0x81,0xE2,0x59,0x72,0x10,0x09,0x54, + 0xBB,0x39,0xAC,0xEE,0x78,0xEF,0x95,0xEC, + 0x01,0x1D,0xF0,0x36,0x68,0xE2,0xC4,0xA5, + 0x2F,0x60,0x42,0x7E,0xD3,0x8E,0xAA,0x27, + 0xC9,0xB7,0x39,0x4E +}; + +static byte xmss_msg[32] = +{ + 0x07,0x9F,0x80,0x86,0xDB,0x76,0x27,0xDF, + 0xED,0x5B,0x2A,0x81,0x60,0x60,0x7D,0xB4, + 0xE8,0x7A,0x69,0x45,0x20,0x6B,0xA2,0x96, + 0xC0,0x21,0xA5,0x46,0x29,0x63,0x9B,0x37 +}; + +/* This was actually the 5th signature produced from + * xmss_fast test in xmss-reference. */ +static byte xmss_sig[2500] = +{ + 0x00,0x00,0x00,0x05,0xF0,0x15,0x34,0xBA, + 0x92,0x03,0x6A,0xB9,0xA5,0x23,0x86,0x11, + 0xAE,0x65,0x0A,0x5C,0x78,0x2C,0xC9,0xBE, + 0x7E,0xA6,0xDC,0xA2,0x8B,0xA9,0x9C,0x50, + 0xF6,0x61,0x8D,0x9D,0xD7,0xE9,0xC0,0xF8, + 0x67,0xCD,0x8A,0xC4,0x9B,0x74,0x96,0x07, + 0x5D,0xF2,0xC9,0xCC,0x28,0x05,0xB1,0xBE, + 0x5E,0xA4,0xBA,0xBE,0xAB,0xD8,0x21,0x6B, + 0x21,0x5F,0xAB,0xB7,0x6C,0xEC,0x2F,0xC8, + 0xC6,0x74,0x3E,0x97,0x1B,0xC3,0x45,0x57, + 0xAF,0xAA,0x1E,0xA8,0xF2,0x86,0xA8,0xAA, + 0x43,0x6D,0x66,0xE9,0x81,0x14,0xDE,0x09, + 0x39,0xD2,0xAF,0xD1,0x4C,0xE7,0x75,0x18, + 0x0D,0xAA,0x29,0xA1,0x92,0x53,0xCC,0xE9, + 0xF3,0x0B,0x1E,0x3B,0xE2,0xAE,0x80,0x0C, + 0xE7,0x7A,0x7C,0x13,0x8A,0x28,0xC6,0x5F, + 0x0A,0xA4,0xA3,0x73,0x0A,0x3A,0xC2,0xA6, + 0x3B,0xB4,0x30,0x67,0xC0,0x36,0x18,0xA1, + 0x58,0xCD,0xAD,0x54,0x36,0x64,0xCE,0xFD, + 0x52,0xFF,0x70,0x7E,0x09,0xFB,0x13,0xA2, + 0xEA,0xDF,0x67,0x8D,0x6C,0x42,0xB2,0x78, + 0xF5,0x7D,0x5C,0x4B,0xF7,0x8E,0xCF,0x3E, + 0xB7,0xC6,0xC1,0x23,0xFA,0x65,0xDE,0xD2, + 0xFA,0x40,0x51,0x97,0x0D,0x52,0x32,0x76, + 0x7E,0x82,0x8D,0xD0,0xB9,0x1E,0x62,0xD9, + 0x1E,0xC1,0xDB,0x40,0x43,0x37,0x4A,0x23, + 0x8A,0x1D,0x35,0xFA,0xF4,0x53,0x11,0x5A, + 0xB5,0x6D,0x1E,0x8B,0x22,0xC8,0x7D,0x2A, + 0xE4,0x94,0xAA,0x25,0x20,0x40,0x96,0xDB, + 0x82,0x62,0xBA,0x8F,0x8B,0x45,0xCB,0x4F, + 0x35,0x88,0x33,0xEB,0xEF,0xB3,0xBA,0xA7, + 0x09,0x72,0xB3,0x4C,0xEC,0xF2,0xC3,0xC7, + 0x5E,0x02,0x6C,0x41,0x93,0xCB,0x3C,0x89, + 0x12,0x09,0x68,0x54,0x8E,0xEC,0x6A,0x7E, + 0x20,0xE1,0x70,0x3D,0x8C,0xEB,0xB4,0x36, + 0xBE,0x91,0xBE,0x97,0xB5,0xA6,0x34,0x16, + 0x95,0x0F,0x10,0x26,0xA9,0x13,0x80,0x88, + 0x9C,0xAA,0x68,0xEC,0x34,0x70,0x4A,0x15, + 0x9B,0x5E,0x57,0x05,0x87,0x1C,0xF8,0x35, + 0x45,0x29,0xE9,0x6E,0xF2,0x70,0x13,0x42, + 0x89,0x4E,0x77,0xC0,0x18,0xC7,0x55,0x6D, + 0xE7,0xFA,0x0D,0x63,0x83,0x16,0x19,0x01, + 0x2D,0xFD,0x31,0x14,0x94,0xCA,0x3E,0x0E, + 0xD6,0x11,0x34,0x81,0x57,0x58,0xEC,0x24, + 0xA4,0x17,0x63,0xD3,0x25,0x00,0xBF,0x7D, + 0x78,0x5D,0xC5,0xD8,0xC6,0xC1,0xBD,0x8C, + 0xD0,0x94,0x0A,0xB1,0x33,0xA5,0x4B,0x31, + 0x25,0xF5,0xAF,0xE7,0x84,0x26,0xAA,0x05, + 0xBB,0xF3,0x9A,0xAF,0x58,0x36,0x40,0xEF, + 0x3D,0xA2,0xBD,0xCA,0xA1,0x8D,0x2F,0x6D, + 0x54,0xD2,0x62,0x33,0x09,0xAE,0xE6,0x73, + 0xD6,0x44,0xE8,0x7C,0x5C,0x39,0x2B,0x78, + 0x94,0x14,0xC7,0xC9,0xAF,0xEC,0x77,0x36, + 0xA1,0x61,0x61,0xF1,0xD0,0x09,0xA2,0xEE, + 0xE7,0x55,0xD7,0x35,0x89,0x89,0x9B,0xCF, + 0xFA,0xA6,0x09,0x1E,0x3B,0xBD,0x5D,0xD9, + 0x25,0xE7,0xED,0xDD,0x7C,0xF0,0x1C,0x57, + 0xE0,0x06,0xBB,0x08,0x39,0x59,0xDF,0xD7, + 0xAF,0x4B,0x88,0x0D,0x87,0x8F,0x4A,0xF3, + 0x1C,0xD4,0x4B,0xB3,0xE2,0xF3,0x1B,0x86, + 0x4F,0xCD,0x35,0x75,0xE2,0x03,0xF9,0x1D, + 0xBF,0x3E,0xD1,0x7B,0xC7,0x23,0x11,0x75, + 0x5F,0x92,0x0D,0x98,0xEE,0x14,0xE1,0xDA, + 0x7A,0x02,0x17,0x47,0x6B,0x41,0xEA,0x47, + 0xA1,0xAF,0x06,0x79,0x1A,0x52,0x6F,0x19, + 0x31,0x70,0x71,0xBD,0xC2,0x61,0x8D,0xB7, + 0xEE,0x6B,0x69,0x2A,0xE8,0x21,0x7A,0x95, + 0xBE,0x86,0x2A,0xA1,0xF4,0xE2,0x2F,0x17, + 0x02,0xFD,0xAD,0x17,0x9F,0x0A,0x0A,0x78, + 0xA9,0x92,0x30,0x21,0x72,0x2B,0x28,0xF8, + 0xF2,0x3E,0x05,0xD5,0xAC,0xC0,0x82,0xF8, + 0xD2,0xDA,0xD0,0xA3,0xBC,0x93,0xDB,0xA5, + 0x46,0xDE,0x14,0x1E,0xD4,0x3A,0x5D,0x79, + 0x3D,0x31,0x4B,0x06,0xCE,0x22,0x29,0x3C, + 0x98,0xB6,0x18,0x8A,0xAE,0xF7,0xBA,0x22, + 0x88,0xA1,0xEE,0xC0,0x14,0x4C,0x4A,0xA0, + 0x57,0x0A,0xD3,0x18,0xA2,0x3D,0xDD,0xC7, + 0x83,0x73,0xFC,0x38,0x9B,0x31,0xA3,0xE1, + 0x17,0x76,0xA1,0xA2,0x69,0xFC,0xAB,0x08, + 0x80,0x72,0x8D,0xF5,0xE4,0x14,0xB7,0x6B, + 0x03,0xFF,0xE8,0x11,0x4B,0x06,0x55,0x7E, + 0x36,0x21,0x2F,0xD7,0x54,0x82,0xC9,0x31, + 0xB4,0x85,0x68,0x41,0xEF,0x75,0xB0,0x3A, + 0xEA,0x4F,0xE0,0xEC,0x72,0xCC,0x33,0x96, + 0xCE,0x7D,0xAD,0xDD,0x0D,0x27,0x05,0x6E, + 0xA2,0xD4,0x11,0x07,0xD8,0x7D,0x27,0xD4, + 0x80,0x8F,0x00,0x22,0xE4,0xFC,0x2C,0x9D, + 0xD5,0xD8,0x18,0x7F,0x4E,0xF4,0xB9,0x7F, + 0xEF,0xD6,0x00,0x08,0x5C,0x05,0x04,0x1E, + 0x9A,0xC6,0x8D,0xCC,0x19,0xD9,0x0B,0x06, + 0xCC,0x6A,0x17,0xE2,0x03,0x23,0xDB,0x1C, + 0xBC,0xA2,0xB9,0xA2,0x95,0x3C,0x73,0xD8, + 0xFF,0xE6,0x0E,0xAE,0x04,0xB2,0xFC,0x91, + 0x4F,0xEF,0x8A,0x58,0xB7,0x31,0x68,0x4C, + 0x1E,0xD0,0x5B,0x85,0xCC,0x03,0xDC,0xF4, + 0xAC,0xDB,0x03,0x9B,0x35,0x33,0x08,0x71, + 0xD0,0x50,0x8D,0xDC,0xE3,0x3A,0x98,0x40, + 0x41,0x80,0xDD,0x35,0xE1,0xA2,0xAF,0x14, + 0x9A,0xDB,0xD3,0x68,0x14,0xE2,0x50,0x7A, + 0x76,0x3F,0xE4,0xA4,0x1B,0xAA,0xC1,0x06, + 0x87,0x9A,0x92,0xF9,0xBE,0x9E,0x86,0x8C, + 0x92,0x1D,0x74,0xB1,0x7F,0x27,0x43,0xC0, + 0xEE,0x2E,0xC2,0x6C,0x6D,0xAA,0x0C,0x0E, + 0x71,0xC9,0x56,0xD6,0x3A,0x56,0xCB,0x90, + 0xD1,0x7E,0x6E,0x1C,0x6A,0x00,0x2D,0x02, + 0x2C,0x96,0xF0,0x2A,0x37,0x37,0x18,0x07, + 0x0B,0xF4,0xB4,0x8C,0x30,0xF2,0xA4,0xAB, + 0x66,0xFB,0x8B,0x22,0xC0,0x00,0x7E,0x05, + 0xB6,0xF9,0x95,0x49,0x33,0xA1,0xDC,0x97, + 0x0C,0x5C,0x61,0x46,0xE2,0xD7,0x87,0x4B, + 0xC4,0xC7,0x5F,0x26,0x06,0x84,0xD7,0x47, + 0x05,0xF1,0x33,0xFF,0x85,0x85,0xB2,0xBD, + 0x1F,0x44,0xC6,0xC2,0x7D,0x51,0xBE,0x0E, + 0xB5,0xC4,0x44,0x2F,0xFE,0x73,0x5F,0xF4, + 0xA4,0xEF,0xE2,0xF1,0x73,0x0B,0xEF,0x3E, + 0x2B,0xD7,0xCC,0x9F,0xDA,0x1A,0x7E,0x92, + 0x39,0xA1,0x55,0xBF,0x60,0x0A,0xDB,0x23, + 0x74,0xFE,0xE7,0x05,0x63,0xA9,0x85,0x52, + 0x9F,0xCC,0xC3,0xFF,0xF6,0x6C,0x1B,0x4E, + 0x4F,0x01,0xBD,0xC3,0xEB,0x37,0xEC,0x29, + 0x21,0x3B,0x2C,0xC9,0x2E,0x93,0x20,0x3E, + 0x19,0xC0,0x8B,0xE8,0x33,0xCD,0xC6,0x6A, + 0x6E,0x72,0x13,0x15,0xA1,0x90,0x20,0x0C, + 0x14,0x66,0xED,0xCC,0xA4,0xDD,0x7F,0x58, + 0x53,0xBC,0x4A,0x68,0xFC,0x86,0x3E,0xAA, + 0xF1,0x17,0x0F,0x3E,0x20,0x54,0x93,0xF4, + 0x98,0xBF,0xB4,0x07,0x05,0xBD,0x70,0xE7, + 0xD7,0x34,0xFD,0xE3,0x69,0xDF,0xCD,0xF5, + 0x1A,0x73,0x6E,0xC9,0x2B,0x21,0xFB,0xB8, + 0x7E,0x44,0x10,0x83,0x56,0xCE,0xD5,0x15, + 0x9A,0x75,0xFC,0x91,0x8E,0x6B,0x9E,0x1A, + 0x3A,0x33,0x39,0x35,0xB4,0x0D,0x74,0xF4, + 0xFB,0x4C,0x0E,0x37,0xFE,0x82,0x95,0x46, + 0x6B,0xD2,0x6E,0xEE,0xCD,0x4D,0x38,0xAF, + 0x0A,0xAA,0xF1,0xD5,0xA4,0x7C,0x04,0xD8, + 0xB9,0xDB,0x11,0x68,0x88,0x35,0x41,0xDE, + 0x31,0x33,0x0C,0xDC,0x2D,0x4C,0xA8,0x20, + 0xCC,0x2C,0x4C,0x63,0xAB,0xBA,0xDF,0x48, + 0x84,0xD5,0x25,0xBC,0x70,0xE3,0x49,0xAA, + 0x43,0xCA,0x8B,0xE7,0x9F,0xDD,0x20,0x76, + 0x9B,0x38,0xF4,0xBA,0x4D,0x4E,0x34,0x4A, + 0xAF,0x81,0xE7,0x0B,0xEC,0xE9,0x59,0xC1, + 0x35,0x22,0x7F,0x69,0x46,0x62,0xD2,0x18, + 0x6E,0x1F,0x79,0xD1,0xAD,0xC3,0x84,0x95, + 0x96,0xB2,0x18,0x58,0x5E,0x7E,0x0C,0x25, + 0x0A,0x0F,0x69,0xA3,0x1D,0xEC,0x29,0xCB, + 0xDA,0xA2,0xD1,0x1A,0x10,0xA5,0x52,0xC3, + 0x62,0x1E,0xC5,0x83,0xFF,0xA3,0x56,0xC2, + 0xFD,0x87,0x3B,0x57,0x52,0x98,0x36,0x95, + 0x77,0x6B,0xE5,0x49,0x10,0x8E,0x39,0xDD, + 0xCA,0x4B,0xB3,0x9F,0x4C,0x0C,0x11,0x62, + 0xF3,0x22,0x78,0xDB,0x48,0xEB,0x68,0xFE, + 0xE4,0x2A,0xE9,0xAA,0x8F,0x7A,0x2F,0x69, + 0xA5,0xC5,0x03,0x2D,0xEF,0x62,0xA8,0x71, + 0x65,0x06,0x40,0x84,0x10,0x0F,0xF2,0xED, + 0xBC,0x70,0x71,0x69,0x24,0xA2,0xBF,0x83, + 0x39,0xDD,0xFA,0xA2,0x7B,0xE5,0xEC,0x3D, + 0xFE,0x3B,0x52,0x6E,0x3D,0x82,0xA6,0x2A, + 0x86,0x01,0x61,0x51,0x63,0xBF,0xF9,0x0A, + 0x06,0x72,0xF1,0xD5,0x39,0x0C,0xBA,0xC9, + 0x78,0xC6,0x77,0x22,0xE4,0x96,0x6E,0xB1, + 0x48,0x62,0x84,0x62,0x2D,0xEA,0x49,0x56, + 0x50,0x86,0x3F,0x90,0xC3,0x01,0x42,0x45, + 0xED,0xE6,0x9A,0x65,0x19,0x93,0x7F,0x48, + 0x16,0xF2,0x50,0xA7,0x70,0xB3,0xF5,0xDB, + 0x0E,0x5E,0x22,0x9E,0x64,0x04,0x26,0x69, + 0xC1,0x16,0xEE,0x65,0x08,0x82,0x27,0x65, + 0xEC,0x3D,0xDF,0x51,0x5E,0x2D,0xE8,0x76, + 0xF2,0xE3,0xE4,0x24,0x04,0x88,0x06,0x0F, + 0xB2,0x7B,0x9B,0x72,0x3D,0x4C,0x7D,0x6A, + 0x1F,0xB2,0xA2,0xD2,0x35,0xD6,0x40,0x25, + 0xC2,0x0B,0x25,0xF9,0xDF,0x26,0xE4,0xDC, + 0xFB,0xB1,0x84,0x84,0x77,0x1B,0x45,0x51, + 0x60,0xD5,0xF0,0xB6,0x09,0xE6,0xBC,0xE3, + 0x1C,0x70,0x96,0x2C,0xD3,0x9D,0x7D,0x7F, + 0xB1,0x70,0xDA,0x79,0xB8,0x74,0x99,0xBF, + 0x84,0x95,0xCC,0x93,0xD7,0x51,0xDD,0x66, + 0xD3,0x70,0x0C,0x75,0x86,0x09,0x06,0xFD, + 0x66,0x14,0x80,0xCD,0xF3,0x59,0xB4,0x92, + 0x5F,0xE4,0xEE,0x00,0xA8,0xB0,0x8B,0x5C, + 0x3E,0xDB,0x8A,0x9C,0x0B,0xB5,0x99,0xC2, + 0x0D,0x81,0x09,0x06,0x6C,0x28,0xC0,0x7E, + 0xA5,0x07,0x70,0x64,0xD7,0x41,0xF4,0xC3, + 0x66,0x61,0x1C,0xA8,0x51,0xF6,0x3C,0xBA, + 0xE0,0x94,0xA3,0x11,0x8C,0x2E,0xBA,0x13, + 0xB2,0x47,0x48,0x93,0xB4,0x1A,0x2C,0x9A, + 0x6E,0x8E,0x30,0x66,0x7B,0xD3,0xBB,0x3B, + 0x5D,0x97,0x0D,0xE4,0xEA,0x24,0x28,0x9E, + 0xB4,0x88,0xCE,0x1D,0x7D,0x6F,0x39,0xB3, + 0x87,0x21,0xE5,0x08,0x93,0xF0,0xD4,0x9D, + 0x2D,0x91,0xC9,0xFD,0x0C,0x74,0x34,0xB4, + 0x1F,0xFE,0xDA,0xDC,0x10,0x5B,0x8D,0x2B, + 0x87,0xD3,0x42,0xB4,0xAE,0x32,0x9C,0xAE, + 0x4C,0x99,0xD8,0xED,0x44,0x41,0x07,0xE0, + 0x8F,0xBD,0xA5,0x7C,0x5A,0xDF,0x91,0x29, + 0x00,0xB5,0x4B,0xC3,0x3A,0x40,0x6C,0x48, + 0xAB,0x2A,0xF3,0x02,0xCB,0xB3,0x69,0xDA, + 0x06,0x0C,0x4D,0x5C,0x45,0xC3,0x28,0xAC, + 0x7A,0x01,0xD4,0xF8,0xCB,0x07,0x63,0x89, + 0x09,0x34,0x78,0xA7,0x14,0x39,0xCF,0x2D, + 0x94,0x8D,0x7A,0x4E,0x4E,0xBD,0xC4,0x32, + 0xAB,0x21,0xC9,0xDA,0x3F,0x5F,0x04,0x6B, + 0x14,0x40,0x18,0x18,0x2F,0xF9,0x46,0x17, + 0x57,0x54,0x9B,0x28,0x7B,0xBD,0xF9,0xA2, + 0x13,0xAC,0x69,0x24,0xB1,0x31,0x39,0xBF, + 0x8D,0x75,0xC3,0xFD,0x03,0x54,0x5A,0xFD, + 0xD4,0x7A,0xB7,0x56,0x4F,0x66,0x43,0x57, + 0x1B,0xFB,0xF9,0x92,0x7A,0x83,0xE6,0xFF, + 0xB4,0xBA,0x83,0xD2,0x61,0x8E,0x4A,0x82, + 0x82,0xA8,0xF5,0x0C,0xD2,0x43,0x53,0xA8, + 0x85,0x0A,0xD4,0x69,0x7B,0x04,0x71,0x3B, + 0x80,0x49,0x27,0x47,0x12,0xB6,0xB0,0xEA, + 0x90,0x0A,0xFA,0xA8,0xC8,0x78,0x61,0xDE, + 0x30,0x12,0xBB,0xDC,0xA6,0x57,0x56,0x30, + 0x6E,0xF1,0xA8,0x3B,0xF6,0x09,0x07,0xEA, + 0x31,0xE2,0x08,0x23,0x31,0x0F,0xD4,0x34, + 0xE3,0x60,0xC2,0x2B,0xDB,0x5A,0x99,0xCF, + 0xD4,0x6B,0x4E,0x75,0x65,0x35,0xE8,0x8B, + 0x93,0x7D,0xCA,0x11,0x47,0xF0,0x3E,0x11, + 0x5C,0xD1,0xEE,0x4B,0x11,0xB4,0x65,0x2B, + 0x6B,0x79,0xC0,0x86,0x60,0xA4,0x4B,0x24, + 0xA0,0x5C,0x70,0x34,0xC3,0x7C,0xE7,0x4F, + 0x97,0x89,0x4D,0xFE,0x22,0x89,0x3A,0xE9, + 0x07,0xB9,0x1A,0x86,0xB8,0x7A,0x12,0x38, + 0xE1,0x24,0x46,0xBC,0x9B,0x21,0xCD,0xAC, + 0x30,0xAB,0x98,0x21,0x31,0xC5,0x17,0x3F, + 0x1E,0x56,0xC3,0x18,0xCE,0xF0,0xA1,0xCC, + 0xFF,0x9D,0xA8,0x53,0xAF,0x74,0x77,0x54, + 0x02,0x9A,0x8F,0xA4,0xD4,0xBD,0xB2,0x1A, + 0xBA,0x52,0x2E,0x19,0xBE,0x49,0x11,0x45, + 0x02,0x01,0x7A,0xBF,0x28,0xD6,0x18,0xED, + 0xBD,0xCE,0xE4,0xDE,0xB5,0xF1,0x53,0x5D, + 0x65,0xF9,0x5F,0x83,0x8F,0x2D,0xF2,0x82, + 0xA0,0x2D,0x28,0xD3,0x0A,0x9E,0x0F,0x7F, + 0xC7,0xC4,0x43,0x7F,0xC3,0x0E,0x06,0xEB, + 0x4E,0xB4,0x2D,0xFA,0xDD,0x48,0xAB,0xF4, + 0x7D,0x41,0x48,0x33,0x5A,0xE6,0x70,0x02, + 0xE7,0x71,0x8D,0xD9,0x6B,0x0C,0x5A,0x8F, + 0xA4,0xC1,0xB7,0x4E,0x96,0x83,0xD6,0xA7, + 0x1D,0xF1,0x88,0xB3,0x6E,0xF4,0x12,0xA9, + 0xF6,0x31,0x69,0x66,0xFE,0xFE,0x02,0xF2, + 0x86,0x6D,0xBB,0x57,0x51,0x8C,0x4C,0xE9, + 0x7C,0x92,0x3E,0x3A,0xD3,0x2D,0xA8,0x82, + 0x53,0x84,0x26,0x89,0xBB,0xCC,0x13,0x12, + 0x3D,0x94,0xBB,0xDF,0x3D,0x4C,0xDF,0x27, + 0x9B,0x1F,0xB8,0xB6,0xE4,0xEA,0xA2,0x07, + 0xF8,0x4D,0x42,0x8F,0x29,0x90,0xFE,0x21, + 0x20,0xE9,0x55,0x02,0xAD,0x90,0xA7,0x77, + 0x4E,0x29,0xB6,0xD9,0x14,0x94,0xB2,0x25, + 0xA4,0xB2,0x0E,0x96,0x31,0xAB,0x9E,0x93, + 0x49,0xAC,0xA9,0xCB,0x68,0x22,0xBA,0xB8, + 0x57,0x5C,0x9D,0x65,0xC1,0xF1,0xFC,0x99, + 0x7C,0x3C,0xE9,0xEA,0x4B,0x29,0x22,0x2F, + 0xDB,0x17,0x21,0x8D,0xB0,0x13,0xBF,0xEE, + 0x7D,0xE4,0x8B,0x6D,0x17,0xE0,0x53,0x92, + 0x0B,0x32,0x6B,0xB1,0x65,0x2E,0xA7,0x83, + 0xFD,0x62,0x62,0xE3,0xAA,0x81,0xE8,0xD6, + 0xF7,0xB1,0x30,0x65,0x80,0x9F,0x77,0x1E, + 0x4A,0xEA,0xE8,0x45,0x32,0x12,0x3A,0xFB, + 0x22,0xE9,0xA9,0xF6,0xCB,0xAB,0xA8,0x0C, + 0x20,0xA8,0x7C,0xF9,0xF7,0x53,0xC1,0xB4, + 0xC0,0x5D,0x06,0x45,0xDD,0x7E,0xA7,0x34, + 0xA1,0x21,0xC2,0x62,0xAB,0x22,0x45,0x3D, + 0x73,0x4C,0x26,0xD1,0x1A,0xB2,0xF0,0xB2, + 0x6D,0x11,0x70,0x58,0xAA,0xF5,0xA4,0xF5, + 0xF8,0x0B,0x3D,0xC1,0xF6,0x17,0x70,0x15, + 0xCD,0x72,0x02,0x7E,0x4E,0x94,0x96,0x0A, + 0x56,0xCC,0xA5,0xA3,0xB3,0x7E,0xDD,0x5A, + 0x72,0xD2,0xFB,0xAC,0x3D,0x0E,0x66,0x65, + 0xE9,0x08,0x6C,0xB0,0x1C,0xE2,0x1A,0x82, + 0xF6,0xF3,0x34,0x89,0x73,0x02,0x5B,0x42, + 0x6D,0x40,0x61,0xB6,0xE0,0xE6,0x53,0x32, + 0xA5,0x72,0x17,0x4F,0x3B,0x51,0x4F,0xBC, + 0x00,0xE0,0x69,0x26,0xA9,0xAE,0x83,0xE3, + 0x73,0x7F,0x71,0x97,0xE0,0xDC,0x7C,0x63, + 0x9C,0x85,0x5F,0xDF,0x7D,0xE4,0x6C,0xD8, + 0xA9,0x3A,0x6F,0x5E,0x4A,0x2E,0xB0,0xE7, + 0x8B,0x45,0xE2,0x90,0x05,0x37,0xE8,0xAB, + 0x49,0x48,0x4C,0xC0,0x59,0x1D,0x8C,0x46, + 0x5B,0x84,0xE0,0x83,0xCE,0xEA,0x4B,0xF9, + 0xD4,0xDC,0x63,0xDF,0x79,0xB7,0x5C,0x11, + 0x25,0x7F,0x90,0x2E,0x0A,0x38,0x03,0xEA, + 0xEA,0xA1,0x26,0x52,0x20,0x19,0xA3,0xBE, + 0xFC,0x9D,0xB7,0x6E,0xA6,0x58,0x8E,0x6D, + 0xC5,0x58,0xE9,0xED,0x2F,0x55,0x43,0x8B, + 0x03,0x8B,0xE6,0xA4,0xC2,0x25,0x4B,0x36, + 0xBA,0xD3,0x27,0x48,0x40,0x2E,0x87,0xA2, + 0xD4,0x12,0xC6,0x05,0x36,0x03,0x11,0x51, + 0xD1,0xF2,0xAC,0x71,0x2C,0xB6,0xC3,0xA5, + 0x57,0x0F,0xAF,0x4B,0xBD,0xCD,0x47,0x4C, + 0x3A,0x52,0x6F,0x47,0xE7,0x0B,0xB7,0xD5, + 0xF7,0xA6,0x39,0x63,0x82,0x08,0x4C,0x41, + 0x0E,0x2A,0x52,0x42,0x5A,0xEA,0x59,0xC7, + 0x94,0xFB,0xD0,0x88,0x47,0x27,0xF6,0x97, + 0x03,0x9E,0x29,0xB8,0x3A,0x67,0xE6,0xF3, + 0x95,0xA7,0x42,0xC1,0x96,0xD1,0x9A,0xA6, + 0xF0,0x09,0x0C,0xEA,0xE0,0xAB,0x0F,0x15, + 0xE9,0xC3,0xEB,0xA5,0x89,0x86,0x98,0x32, + 0x83,0xAB,0x30,0x33,0xAE,0x90,0x8D,0x2E, + 0xB3,0xAA,0x91,0xA6,0xD9,0xA4,0x4A,0x54, + 0xE0,0xD3,0x08,0xCC,0x79,0xCE,0xE4,0x15, + 0x31,0xA6,0xCE,0x61,0xCF,0x03,0x06,0xEE, + 0x8E,0xE2,0x64,0x29,0xD1,0x54,0x9B,0xD0, + 0x5F,0x09,0x2B,0x8B,0xD5,0xF8,0xD4,0x7D, + 0xF1,0x97,0x32,0xD9,0xEA,0x5A,0x0E,0x10, + 0x8C,0x4D,0xFB,0x55,0xE6,0x27,0x0C,0xBA, + 0xC1,0x73,0xC1,0x73,0xE3,0x1C,0x09,0xB3, + 0x6F,0xB4,0x12,0xFA,0xF3,0x29,0xDC,0x23, + 0x32,0xED,0x80,0x87,0x83,0xC2,0xF6,0x07, + 0xB5,0xA9,0x22,0xDE,0x66,0x1A,0xA7,0x4A, + 0x86,0xF1,0x39,0x9B,0xF4,0xE7,0x50,0x15, + 0x4A,0x55,0x3C,0x93,0xB9,0xF9,0xFD,0xDC, + 0xB3,0x5D,0x73,0x52 +}; + +WOLFSSL_TEST_SUBROUTINE wc_test_ret_t xmss_test_verify_only(void) +{ + int ret = -1; + int ret2 = -1; + int j = 0; + XmssKey verifyKey; + word32 pkSz = 0; + word32 sigSz = 0; + const char * param = "XMSS-SHA2_10_256"; + + ret = wc_XmssKey_Init(&verifyKey, NULL, INVALID_DEVID); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_SetParamStr(&verifyKey, param); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_GetPubLen(&verifyKey, &pkSz); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + if (pkSz != XMSS_SHA256_PUBLEN) { + return WC_TEST_RET_ENC_EC(pkSz); + } + + ret = wc_XmssKey_GetSigLen(&verifyKey, &sigSz); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + +#if defined(DEBUG_WOLFSSL) || defined(WOLFSSL_DEBUG_NONBLOCK) + fprintf(stderr, "param: %s\n", param); + fprintf(stderr, "pkSz: %d\n", pkSz); + fprintf(stderr, "sigSz: %d\n", sigSz); +#endif + + if (sigSz != sizeof(xmss_sig)) { + return WC_TEST_RET_ENC_EC(sigSz); + } + + ret = wc_XmssKey_ImportPubRaw(&verifyKey, xmss_pub, XMSS_SHA256_PUBLEN); + if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } + + ret = wc_XmssKey_Verify(&verifyKey, xmss_sig, sizeof(xmss_sig), + (byte *) xmss_msg, sizeof(xmss_msg)); + if (ret != 0) { + printf("error: wc_XmssKey_Verify returned %d, expected 0\n", ret); + return WC_TEST_RET_ENC_EC(ret); + } + + /* Flip bits in message. This should fail. */ + xmss_msg[sizeof(xmss_msg) / 2] ^= 1; + ret2 = wc_XmssKey_Verify(&verifyKey, xmss_sig, sizeof(xmss_sig), + (byte *) xmss_msg, sizeof(xmss_msg)); + if (ret2 != -1) { + printf("error: wc_XmssKey_Verify returned %d, expected -1\n", ret2); + return WC_TEST_RET_ENC_EC(ret); + } + + /* Flip it back. This should pass again. */ + xmss_msg[sizeof(xmss_msg) / 2] ^= 1; + ret = wc_XmssKey_Verify(&verifyKey, xmss_sig, sizeof(xmss_sig), + (byte *) xmss_msg, sizeof(xmss_msg)); + if (ret != 0) { + printf("error: wc_XmssKey_Verify returned %d, expected 0\n", ret); + return WC_TEST_RET_ENC_EC(ret); + } + + /* Flip bits in a few places throughout the signature, stepping in multiple + * of hash size. These should all fail with -1. */ + for (j = 0; j < (int) sizeof(xmss_sig); j+= 4 * 32) { + xmss_sig[j] ^= 1; + + ret2 = wc_XmssKey_Verify(&verifyKey, xmss_sig, sizeof(xmss_sig), + (byte *) xmss_msg, sizeof(xmss_msg)); + if (ret2 != -1) { + /* Verify passed when it should have failed. */ + return WC_TEST_RET_ENC_I(j); + } + + /* Flip this spot back. */ + xmss_sig[j] ^= 1; + } + + /* Cleanup everything. */ + wc_XmssKey_Free(&verifyKey); + + return ret; +} +#endif /* if defined(WOLFSSL_HAVE_XMSS) && defined(WOLFSSL_XMSS_VERIFY_ONLY) && + * !defined(WOLFSSL_SMALL_STACK) */ + #if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) static int lms_write_key_mem(const byte * priv, word32 privSz, void *context) @@ -35116,7 +35756,10 @@ static int lms_read_key_mem(byte * priv, word32 privSz, void *context) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) { - int ret; + int i = 0; + int j = 0; + int ret = -1; + int ret2 = -1; int sigsLeft = 0; LmsKey signingKey; LmsKey verifyKey; @@ -35138,6 +35781,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) XMEMSET(priv, 0, sizeof(priv)); XMEMSET(old_priv, 0, sizeof(old_priv)); + XMEMSET(sig, 0, WC_TEST_LMS_SIG_LEN); #ifndef HAVE_FIPS ret = wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID); @@ -35190,7 +35834,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) } /* 2 ** 5 should be the max number of signatures */ - for (size_t i = 0; i < 32; ++i) { + for (i = 0; i < 32; ++i) { /* We should have remaining signstures. */ sigsLeft = wc_LmsKey_SigsLeft(&signingKey); if (sigsLeft == 0) { @@ -35203,7 +35847,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) /* The updated private key should not match the old one. */ if (XMEMCMP(old_priv, priv, sizeof(priv)) == 0) { - printf("error: current priv key should not match old: %zu\n", i); + printf("error: current priv key should not match old: %d\n", i); return WC_TEST_RET_ENC_I(i); } @@ -35211,6 +35855,22 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) ret = wc_LmsKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, msgSz); if (ret != 0) { return WC_TEST_RET_ENC_I(i); } + + /* Flip bits in a few places throughout the signature, stepping in multiple + * of hash size. These should all fail with -1. */ + for (j = 0; j < (int) sigSz; j+= 4 * 32) { + sig[j] ^= 1; + + ret2 = wc_LmsKey_Verify(&verifyKey, sig, sigSz, (byte *) msg, + msgSz); + if (ret2 != -1) { + /* Verify passed when it should have failed. */ + return WC_TEST_RET_ENC_I(j); + } + + /* Flip this spot back. */ + sig[j] ^= 1; + } } /* This should be the last signature. */ @@ -35243,6 +35903,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) * signature length: 1456 * */ +/* "wolfSSL LMS example message!" without null terminator. */ +static byte lms_msg[28] = +{ + 0x77,0x6F,0x6C,0x66,0x53,0x53,0x4C,0x20, + 0x4C,0x4D,0x53,0x20,0x65,0x78,0x61,0x6D, + 0x70,0x6C,0x65,0x20,0x6D,0x65,0x73,0x73, + 0x61,0x67,0x65,0x21 +}; + static byte lms_L1H10W8_pub[HSS_MAX_PUBLIC_KEY_LEN] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06, @@ -35445,15 +36114,16 @@ static byte lms_L1H10W8_sig[LMS_L1H10W8_SIGLEN] = WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test_verify_only(void) { - int ret; - LmsKey verifyKey; - word32 sigSz = 0; - const char * msg = "wolfSSL LMS example message!"; - word32 msgSz = (word32) XSTRLEN(msg); - word32 pubLen = 0; - int levels = 0; - int height = 0; - int winternitz = 0; + int ret = -1; + int ret2 = -1; + int j = 0; + LmsKey verifyKey; + word32 sigSz = 0; + word32 msgSz = sizeof(lms_msg); + word32 pubLen = 0; + int levels = 0; + int height = 0; + int winternitz = 0; ret = wc_LmsKey_Init(&verifyKey, NULL, INVALID_DEVID); if (ret != 0) { return WC_TEST_RET_ENC_EC(ret); } @@ -35492,12 +36162,47 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test_verify_only(void) } ret = wc_LmsKey_Verify(&verifyKey, lms_L1H10W8_sig, LMS_L1H10W8_SIGLEN, - (byte *) msg, msgSz); + (byte *) lms_msg, msgSz); if (ret != 0) { printf("error: wc_LmsKey_Verify returned %d\n", ret); return WC_TEST_RET_ENC_EC(ret); } + /* Flip bits in message. This should fail. */ + lms_msg[msgSz / 2] ^= 1; + ret2 = wc_LmsKey_Verify(&verifyKey, lms_L1H10W8_sig, LMS_L1H10W8_SIGLEN, + (byte *) lms_msg, msgSz); + if (ret2 != -1) { + printf("error: wc_LmsKey_Verify returned %d, expected -1\n", ret2); + return WC_TEST_RET_ENC_EC(ret); + } + + /* Flip it back. This should pass again. */ + lms_msg[msgSz / 2] ^= 1; + ret = wc_LmsKey_Verify(&verifyKey, lms_L1H10W8_sig, LMS_L1H10W8_SIGLEN, + (byte *) lms_msg, msgSz); + if (ret != 0) { + printf("error: wc_LmsKey_Verify returned %d, expected 0\n", ret); + return WC_TEST_RET_ENC_EC(ret); + } + + /* Flip bits in a few places throughout the signature, stepping in multiple + * of hash size. These should all fail with -1. */ + for (j = 0; j < (int) sigSz; j+= 4 * 32) { + lms_L1H10W8_sig[j] ^= 1; + + ret2 = wc_LmsKey_Verify(&verifyKey, lms_L1H10W8_sig, + LMS_L1H10W8_SIGLEN, + (byte *) lms_msg, msgSz); + if (ret2 != -1) { + /* Verify passed when it should have failed. */ + return WC_TEST_RET_ENC_I(j); + } + + /* Flip this spot back. */ + lms_L1H10W8_sig[j] ^= 1; + } + wc_LmsKey_Free(&verifyKey); return ret; } diff --git a/wolfssl/wolfcrypt/ext_xmss.h b/wolfssl/wolfcrypt/ext_xmss.h new file mode 100644 index 000000000..9abf15835 --- /dev/null +++ b/wolfssl/wolfcrypt/ext_xmss.h @@ -0,0 +1,56 @@ +/* ext_xmss.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_XMSS_H +#define EXT_XMSS_H + +#ifdef WOLFSSL_HAVE_XMSS +#include + +#if !defined(HAVE_LIBXMSS) + #error "This code requires libxmss" +#endif + +#include +#include + +#if defined(WOLFSSL_WC_XMSS) + #error "This code is incompatible with wolfCrypt's implementation of XMSS." +#endif + +struct XmssKey { + unsigned char pk[XMSS_SHA256_PUBLEN]; + uint32_t oid; + int is_xmssmt; + xmss_params params; +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + /* The secret key length is a function of xmss_params. */ + unsigned char * sk; + word32 sk_len; + 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. */ +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */ + enum wc_XmssState state; +}; + +#endif /* WOLFSSL_HAVE_XMSS */ +#endif /* EXT_XMSS_H */ diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index 927030406..dfdc80aca 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -81,7 +81,10 @@ nobase_include_HEADERS+= \ wolfssl/wolfcrypt/sm4.h \ wolfssl/wolfcrypt/lms.h \ wolfssl/wolfcrypt/wc_lms.h \ - wolfssl/wolfcrypt/ext_lms.h + wolfssl/wolfcrypt/ext_lms.h \ + wolfssl/wolfcrypt/xmss.h \ + wolfssl/wolfcrypt/wc_xmss.h \ + wolfssl/wolfcrypt/ext_xmss.h noinst_HEADERS+= \ wolfssl/wolfcrypt/port/aria/aria-crypt.h \ diff --git a/wolfssl/wolfcrypt/wc_xmss.h b/wolfssl/wolfcrypt/wc_xmss.h new file mode 100644 index 000000000..96274d794 --- /dev/null +++ b/wolfssl/wolfcrypt/wc_xmss.h @@ -0,0 +1,23 @@ +/* wc_xmss.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" + diff --git a/wolfssl/wolfcrypt/xmss.h b/wolfssl/wolfcrypt/xmss.h new file mode 100644 index 000000000..386e27eae --- /dev/null +++ b/wolfssl/wolfcrypt/xmss.h @@ -0,0 +1,153 @@ +/* xmss.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/xmss.h + */ + +#ifndef WOLF_CRYPT_XMSS_H +#define WOLF_CRYPT_XMSS_H + +#include +#include + +#ifdef WOLFSSL_HAVE_XMSS + +/* Note on XMSS/XMSS^MT pub/priv key sizes: + * - The XMSS/XMSS^MT pub key has a defined format and size. + * - The XMSS/XMSS^MT private key is implementation and parameter + * specific. It does not have a standardized format or size. + * + * The XMSS/XMSS^MT public and secret key format and length is: + * PK = OID || root || SEED; + * PK_len = 4 + 2 * n + * + * SK = OID || (implementation defined) + * SK_len = 4 + (implementation defined) + * + * where n is the number of bytes in the hash function, which is 32 + * in this SHA256 implementation. + * + * However the private key is implementation specific. For example, + * in xmss-reference the private key size varies from 137 bytes to + * 1377 bytes between slow and fast implementations with param name + * "XMSSMT-SHA2_20/2_256". + * + * References: + * - RFC 8391 + * - Table 2 of Kampanakis, Fluhrer, IACR, 2017. + * */ + +#define XMSS_SHA256_PUBLEN (68) + +/* Supported XMSS/XMSS^MT parameter set names: + * We are supporting all SHA256 parameter sets with n=32 and + * Winternitz=16, from RFC 8391 and NIST SP 800-208. + * + * ---------------------------------------------------------- + * | Name Oid n w len h d | + * XMSS: | "XMSS-SHA2_10_256" 0x00000001 32 16 67 10 1 | + * | "XMSS-SHA2_16_256" 0x00000002 32 16 67 16 1 | + * | "XMSS-SHA2_20_256" 0x00000003 32 16 67 20 1 | + * | | + * XMSSMT: | "XMSSMT-SHA2_20/2_256" 0x00000001 32 16 67 20 2 | + * | "XMSSMT-SHA2_20/4_256" 0x00000002 32 16 67 20 4 | + * | "XMSSMT-SHA2_40/2_256" 0x00000003 32 16 67 40 2 | + * | "XMSSMT-SHA2_40/4_256" 0x00000004 32 16 67 40 4 | + * | "XMSSMT-SHA2_40/8_256" 0x00000005 32 16 67 40 8 | + * | "XMSSMT-SHA2_60/3_256" 0x00000006 32 16 67 60 3 | + * | "XMSSMT-SHA2_60/6_256" 0x00000007 32 16 67 60 6 | + * | "XMSSMT-SHA2_60/12_256" 0x00000008 32 16 67 60 12 | + * ---------------------------------------------------------- + * + * Note that some XMSS and XMSSMT names do have overlapping Oids. + * + * References: + * 1. NIST SP 800-208 + * 2. RFC 8391 + * */ + +#define XMSS_NAME_LEN (16) /* strlen("XMSS-SHA2_10_256") */ +#define XMSSMT_NAME_MIN_LEN (20) /* strlen("XMSSMT-SHA2_20/2_256") */ +#define XMSSMT_NAME_MAX_LEN (21) /* strlen("XMSSMT-SHA2_60/12_256") */ + +typedef struct XmssKey XmssKey; + +/* 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_XmssRc { + WC_XMSS_RC_NONE, + WC_XMSS_RC_BAD_ARG, /* Bad arg in read or write callback. */ + WC_XMSS_RC_WRITE_FAIL, /* Write or update private key failed. */ + WC_XMSS_RC_READ_FAIL, /* Read private key failed. */ + WC_XMSS_RC_SAVED_TO_NV_MEMORY, /* Wrote private key to nonvolatile storage. */ + WC_XMSS_RC_READ_TO_MEMORY /* Read private key from storage. */ +}; + +/* enum wc_XmssState is to help track the state of an XMSS Key. */ +enum wc_XmssState { + WC_XMSS_STATE_FREED, /* Key has been freed from memory. */ + WC_XMSS_STATE_INITED, /* Key has been inited, ready to set parms.*/ + WC_XMSS_STATE_PARMSET, /* Parms are set, ready to MakeKey or Reload. */ + WC_XMSS_STATE_OK, /* Able to sign signatures and verify. */ + WC_XMSS_STATE_VERIFYONLY, /* A public only XmssKey. */ + WC_XMSS_STATE_BAD, /* Can't guarantee key's state. */ + WC_XMSS_STATE_NOSIGS /* Signatures exhausted. */ +}; + +#ifdef __cplusplus + extern "C" { +#endif +WOLFSSL_API int wc_XmssKey_Init(XmssKey * key, void * heap, int devId); +WOLFSSL_API int wc_XmssKey_SetParamStr(XmssKey * key, const char * str); +#ifndef WOLFSSL_XMSS_VERIFY_ONLY +WOLFSSL_API int wc_XmssKey_SetWriteCb(XmssKey * key, + write_private_key_cb write_cb); +WOLFSSL_API int wc_XmssKey_SetReadCb(XmssKey * key, + read_private_key_cb read_cb); +WOLFSSL_API int wc_XmssKey_SetContext(XmssKey * key, void * context); +WOLFSSL_API int wc_XmssKey_MakeKey(XmssKey * key, WC_RNG * rng); +WOLFSSL_API int wc_XmssKey_Reload(XmssKey * key); +WOLFSSL_API int wc_XmssKey_GetPrivLen(const XmssKey * key, word32 * len); +WOLFSSL_API int wc_XmssKey_Sign(XmssKey * key, byte * sig, word32 * sigSz, + const byte * msg, int msgSz); +WOLFSSL_API int wc_XmssKey_SigsLeft(XmssKey * key); +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */ +WOLFSSL_API void wc_XmssKey_Free(XmssKey * key); +WOLFSSL_API int wc_XmssKey_GetSigLen(const XmssKey * key, word32 * len); +WOLFSSL_API int wc_XmssKey_GetPubLen(const XmssKey * key, word32 * len); +WOLFSSL_API int wc_XmssKey_ExportPub(XmssKey * keyDst, const XmssKey * keySrc); +WOLFSSL_API int wc_XmssKey_ExportPubRaw(const XmssKey * key, byte * out, + word32 * outLen); +WOLFSSL_API int wc_XmssKey_ImportPubRaw(XmssKey * key, const byte * in, + word32 inLen); +WOLFSSL_API int wc_XmssKey_Verify(XmssKey * key, const byte * sig, word32 sigSz, + const byte * msg, int msgSz); +WOLFSSL_API const char * wc_XmssKey_RcToStr(enum wc_XmssRc lmsRc); +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFSSL_HAVE_XMSS */ +#endif /* WOLF_CRYPT_XMSS_H */