From e47f1d419047cdba03a37d3f42fefc5b62b4721d Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Fri, 3 May 2024 10:58:54 +1000 Subject: [PATCH] LMS, XMSS: move code into wolfCrypt Move implementations of LMS and XMSS into wolfCrypt and use by default. --- configure.ac | 68 +- wolfcrypt/src/wc_lms.c | 1123 ++++++++- wolfcrypt/src/wc_lms_impl.c | 3072 +++++++++++++++++++++++- wolfcrypt/src/wc_xmss.c | 1644 ++++++++++++- wolfcrypt/src/wc_xmss_impl.c | 4316 +++++++++++++++++++++++++++++++++- wolfssl/wolfcrypt/wc_lms.h | 449 +++- wolfssl/wolfcrypt/wc_xmss.h | 264 ++- 7 files changed, 10884 insertions(+), 52 deletions(-) diff --git a/configure.ac b/configure.ac index d277a513d4..353110418b 100644 --- a/configure.ac +++ b/configure.ac @@ -1229,7 +1229,6 @@ AC_ARG_ENABLE([xmss], [ ENABLED_XMSS=no ] ) -ENABLED_WC_XMSS=no for v in `echo $ENABLED_XMSS | tr "," " "` do case $v in @@ -1238,15 +1237,9 @@ do no) ;; verify-only) - XMSS_VERIFY_ONLY=yes AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_XMSS_VERIFY_ONLY -DXMSS_VERIFY_ONLY" ;; - wolfssl) - ENABLED_WC_XMSS=yes - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS" - ;; small) - ENABLED_WC_XMSS=yes AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS_SMALL" ;; *) @@ -1255,20 +1248,6 @@ do esac done -if test "$ENABLED_XMSS" != "no" -then - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_XMSS" - - if test "$ENABLED_WC_XMSS" = "no"; - then - # Default is to use hash-sigs XMSS lib. Make sure it's enabled. - if test "$ENABLED_LIBXMSS" = "no"; then - AC_MSG_ERROR([The default implementation for XMSS is the xmss-reference lib. - Please use --with-libxmss.]) - fi - fi -fi - # libxmss # Get the path to xmss-reference. ENABLED_LIBXMSS="no" @@ -1321,6 +1300,19 @@ AC_ARG_WITH([libxmss], [XMSS_ROOT=""] ) +if test "$ENABLED_XMSS" != "no" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_XMSS" + + # Use hash-sigs XMSS lib if enabled. + if test "$ENABLED_LIBXMSS" = "yes"; then + ENABLED_WC_XMSS=no + else + ENABLED_WC_XMSS=yes + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_XMSS" + fi +fi + # LMS AC_ARG_ENABLE([lms], [AS_HELP_STRING([--enable-lms],[Enable stateful LMS/HSS signatures (default: disabled)])], @@ -1328,7 +1320,6 @@ AC_ARG_ENABLE([lms], [ ENABLED_LMS=no ] ) -ENABLED_WC_LMS=no for v in `echo $ENABLED_LMS | tr "," " "` do case $v in @@ -1337,37 +1328,17 @@ do no) ;; verify-only) - LMS_VERIFY_ONLY=yes AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_LMS_VERIFY_ONLY" ;; small) - ENABLED_WC_LMS=yes AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS_SMALL" ;; - wolfssl) - ENABLED_WC_LMS=yes - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS" - ;; *) AC_MSG_ERROR([Invalid choice for LMS []: $ENABLED_LMS.]) break;; esac done -if test "$ENABLED_LMS" != "no" -then - AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_LMS" - - if test "$ENABLED_WC_LMS" = "no"; - then - # Default is to use hash-sigs LMS lib. Make sure it's enabled. - if test "$ENABLED_LIBLMS" = "no"; then - AC_MSG_ERROR([The default implementation for LMS is the hash-sigs LMS/HSS lib. - Please use --with-liblms.]) - fi - fi -fi - # liblms # Get the path to the hash-sigs LMS HSS lib. ENABLED_LIBLMS="no" @@ -1436,6 +1407,19 @@ AC_ARG_WITH([liblms], ] ) +if test "$ENABLED_LMS" != "no" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_HAVE_LMS" + + # Use hash-sigs LMS lib if enabled. + if test "$ENABLED_LIBLMS" = "yes"; then + ENABLED_WC_LMS=no + else + ENABLED_WC_LMS=yes + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_LMS" + fi +fi + # SINGLE THREADED AC_ARG_ENABLE([singlethreaded], [AS_HELP_STRING([--enable-singlethreaded],[Enable wolfSSL single threaded (default: disabled)])], diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c index cdc732f6e4..0ef0b59a24 100644 --- a/wolfcrypt/src/wc_lms.c +++ b/wolfcrypt/src/wc_lms.c @@ -19,8 +19,1127 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +#ifdef HAVE_CONFIG_H + #include +#endif + #include +#include +#include + +#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + + +/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8. + * + * @param [in] w Winternitz width. + */ +#define LMS_U(w) \ + (8 * WC_SHA256_DIGEST_SIZE / (w)) +/* Calculate u. Appendix B. Works for w of 1, 2, 4, or 8. + * + * @param [in] w Winternitz width. + * @param [in] wb Winternitz width length in bits. + */ +#define LMS_V(w, wb) \ + (2 + (8 - (wb)) / (w)) +/* Calculate ls. Appendix B. Works for w of 1, 2, 4, or 8. + * + * @param [in] w Winternitz width. + * @param [in] wb Winternitz width length in bits. + */ +#define LMS_LS(w, wb) \ + (16 - LMS_V(w, wb) * (w)) +/* Calculate p. Appendix B. Works for w of 1, 2, 4, or 8. + * + * @param [in] w Winternitz width. + * @param [in] wb Winternitz width length in bits. + */ +#define LMS_P(w, wb) \ + (LMS_U(w) + LMS_V(w, wb)) +/* Calculate signature length. + * + * @param [in] l Number of levels. + * @param [in] h Height of the trees. + * @param [in] p Number of n-byte string elements in signature for a tree. + */ +#define LMS_PARAMS_SIG_LEN(l, h, p) \ + (4 + (l) * (4 + 4 + 4 + WC_SHA256_DIGEST_SIZE * (1 + (p) + (h))) + \ + ((l) - 1) * LMS_PUBKEY_LEN) + +#ifndef WOLFSSL_WC_LMS_SMALL + /* Root levels and leaf cache bits. */ + #define LMS_PARAMS_CACHE(h) \ + (((h) < LMS_ROOT_LEVELS) ? (h) : LMS_ROOT_LEVELS), \ + (((h) < LMS_CACHE_BITS ) ? (h) : LMS_CACHE_BITS ) +#else + /* Root levels and leaf cache bits aren't in structure. */ + #define LMS_PARAMS_CACHE(h) /* null expansion */ +#endif + +/* Define parameters entry for LMS. + * + * @param [in] l Number of levels. + * @param [in] h Height of the trees. + * @param [in] w Winternitz width. + * @param [in] wb Winternitz width length in bits. + * @param [in] t LMS type. + * @param [in] t2 LM-OTS type. + */ +#define LMS_PARAMS(l, h, w, wb, t, t2) \ + { l, h, w, LMS_LS(w, wb), LMS_P(w, wb), t, t2, \ + LMS_PARAMS_SIG_LEN(l, h, LMS_P(w, wb)), LMS_PARAMS_CACHE(h) } + + +/* Initialize the working state for LMS operations. + * + * @param [in, out] state LMS state. + * @param [in] params LMS parameters. + */ +static int wc_lmskey_state_init(LmsState* state, const LmsParams* params) +{ + int ret; -#ifdef WOLFSSL_HAVE_LMS - #error "Contact wolfSSL to get the implementation of this file" + /* Zero out every field. */ + XMEMSET(state, 0, sizeof(LmsState)); + + /* Keep a reference to the parameters for use in operations. */ + state->params = params; + + /* Initialize the two hash algorithms. */ + ret = wc_InitSha256(&state->hash); + if (ret == 0) { + ret = wc_InitSha256(&state->hash_k); + if (ret != 0) { + wc_Sha256Free(&state->hash); + } + } + + return ret; +} + +/* Free the working state for LMS operations. + * + * @param [in] state LMS state. + */ +static void wc_lmskey_state_free(LmsState* state) +{ + wc_Sha256Free(&state->hash_k); + wc_Sha256Free(&state->hash); +} + +/* Supported LMS parameters. */ +static const wc_LmsParamsMap wc_lms_map[] = { +#if LMS_MAX_HEIGHT >= 15 + { WC_LMS_PARM_NONE , "LMS_NONE" , + LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L1_H15_W2, "LMS/HSS L1_H15_W2", + LMS_PARAMS(1, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L1_H15_W4, "LMS/HSS L1_H15_W4", + LMS_PARAMS(1, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) }, +#endif +#if LMS_MAX_LEVELS >= 2 +#if LMS_MAX_HEIGHT >= 10 + { WC_LMS_PARM_L2_H10_W2, "LMS/HSS L2_H10_W2", + LMS_PARAMS(2, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L2_H10_W4, "LMS/HSS L2_H10_W4", + LMS_PARAMS(2, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L2_H10_W8, "LMS/HSS L2_H10_W8", + LMS_PARAMS(2, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) }, +#endif +#endif +#if LMS_MAX_LEVELS >= 3 + { WC_LMS_PARM_L3_H5_W2 , "LMS/HSS L3_H5_W2" , + LMS_PARAMS(3, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L3_H5_W4 , "LMS/HSS L3_H5_W4" , + LMS_PARAMS(3, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L3_H5_W8 , "LMS/HSS L3_H5_W8" , + LMS_PARAMS(3, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) }, +#if LMS_MAX_HEIGHT >= 10 + { WC_LMS_PARM_L3_H10_W4, "LMS/HSS L3_H10_W4", + LMS_PARAMS(3, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) }, +#endif +#endif +#if LMS_MAX_LEVELS >= 4 + { WC_LMS_PARM_L4_H5_W8 , "LMS/HSS L4_H5_W8" , + LMS_PARAMS(4, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) }, +#endif + + /* For when user sets L, H, W explicitly. */ + { WC_LMS_PARM_L1_H5_W1 , "LMS/HSS_L1_H5_W1" , + LMS_PARAMS(1, 5, 1, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W1) }, + { WC_LMS_PARM_L1_H5_W2 , "LMS/HSS_L1_H5_W2" , + LMS_PARAMS(1, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L1_H5_W4 , "LMS/HSS_L1_H5_W4" , + LMS_PARAMS(1, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L1_H5_W8 , "LMS/HSS_L1_H5_W8" , + LMS_PARAMS(1, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) }, +#if LMS_MAX_HEIGHT >= 10 + { WC_LMS_PARM_L1_H10_W2 , "LMS/HSS_L1_H10_W2", + LMS_PARAMS(1, 10, 2, 1, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L1_H10_W4 , "LMS/HSS_L1_H10_W4", + LMS_PARAMS(1, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L1_H10_W8 , "LMS/HSS_L1_H10_W8", + LMS_PARAMS(1, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) }, +#endif +#if LMS_MAX_HEIGHT >= 15 + { WC_LMS_PARM_L1_H15_W8 , "LMS/HSS L1_H15_W8", + LMS_PARAMS(1, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) }, +#endif +#if LMS_MAX_HEIGHT >= 20 + { WC_LMS_PARM_L1_H20_W2 , "LMS/HSS_L1_H20_W2", + LMS_PARAMS(1, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L1_H20_W4 , "LMS/HSS_L1_H20_W4", + LMS_PARAMS(1, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L1_H20_W8 , "LMS/HSS_L1_H20_W8", + LMS_PARAMS(1, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) }, +#endif +#if LMS_MAX_LEVELS >= 2 + { WC_LMS_PARM_L2_H5_W2 , "LMS/HSS_L2_H5_W2" , + LMS_PARAMS(2, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L2_H5_W4 , "LMS/HSS_L2_H5_W4" , + LMS_PARAMS(2, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L2_H5_W8 , "LMS/HSS_L2_H5_W8" , + LMS_PARAMS(2, 5, 8, 3, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W8) }, +#if LMS_MAX_HEIGHT >= 15 + { WC_LMS_PARM_L2_H15_W2 , "LMS/HSS_L2_H15_W2", + LMS_PARAMS(2, 15, 2, 1, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L2_H15_W4 , "LMS/HSS_L2_H15_W4", + LMS_PARAMS(2, 15, 4, 2, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L2_H15_W8 , "LMS/HSS_L2_H15_W8", + LMS_PARAMS(2, 15, 8, 3, LMS_SHA256_M32_H15, LMOTS_SHA256_N32_W8) }, +#endif +#if LMS_MAX_HEIGHT >= 20 + { WC_LMS_PARM_L2_H20_W2 , "LMS/HSS_L2_H20_W2", + LMS_PARAMS(2, 20, 2, 1, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L2_H20_W4 , "LMS/HSS_L2_H20_W4", + LMS_PARAMS(2, 20, 4, 2, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L2_H20_W8 , "LMS/HSS_L2_H20_W8", + LMS_PARAMS(2, 20, 8, 3, LMS_SHA256_M32_H20, LMOTS_SHA256_N32_W8) }, +#endif +#endif +#if LMS_MAX_LEVELS >= 3 +#if LMS_MAX_HEIGHT >= 10 + { WC_LMS_PARM_L3_H10_W8 , "LMS/HSS L3_H10_W8", + LMS_PARAMS(3, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) }, #endif +#endif +#if LMS_MAX_LEVELS >= 4 + { WC_LMS_PARM_L4_H5_W2 , "LMS/HSS L4_H5_W2" , + LMS_PARAMS(4, 5, 2, 1, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W2) }, + { WC_LMS_PARM_L4_H5_W4 , "LMS/HSS L4_H5_W4" , + LMS_PARAMS(4, 5, 4, 2, LMS_SHA256_M32_H5 , LMOTS_SHA256_N32_W4) }, +#if LMS_MAX_HEIGHT >= 10 + { WC_LMS_PARM_L4_H10_W4 , "LMS/HSS L4_H10_W4", + LMS_PARAMS(4, 10, 4, 2, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W4) }, + { WC_LMS_PARM_L4_H10_W8 , "LMS/HSS L4_H10_W8", + LMS_PARAMS(4, 10, 8, 3, LMS_SHA256_M32_H10, LMOTS_SHA256_N32_W8) }, +#endif +#endif +}; +/* Number of parameter sets supported. */ +#define WC_LMS_MAP_LEN ((int)(sizeof(wc_lms_map) / sizeof(*wc_lms_map))) + +/* Initialize LMS key. + * + * Call this before setting the params of an LMS key. + * + * @param [out] key LMS key to initialize. + * @param [in] heap Heap hint. + * @param [in] devId Device identifier. + * Use INVALID_DEVID when not using a device. + * @return 0 on success. + * @return BAD_FUNC_ARG when key is NULL. + */ +int wc_LmsKey_Init(LmsKey* key, void* heap, int devId) +{ + int ret = 0; + + (void)heap; + (void)devId; + + /* Validate parameters. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + /* Zeroize the key data. */ + ForceZero(key, sizeof(LmsKey)); + + #ifndef WOLFSSL_LMS_VERIFY_ONLY + /* Initialize other fields. */ + key->write_private_key = NULL; + key->read_private_key = NULL; + key->context = NULL; + key->heap = heap; + #endif + #ifdef WOLF_CRYPTO_CB + key->devId = devId; + #endif + /* Start in initialized state. */ + key->state = WC_LMS_STATE_INITED; + } + + return ret; +} + +/* Get the string representation of the LMS parameter set. + * + * @param [in] lmsParm LMS parameter set identifier. + * @return String representing LMS parameter set on success. + * @return NULL when parameter set not supported. + */ +const char* wc_LmsKey_ParmToStr(enum wc_LmsParm lmsParm) +{ + const char* str = NULL; + int i; + + /* Search through table for matching numeric identifier. */ + for (i = 0; i < WC_LMS_MAP_LEN; i++) { + if (lmsParm == wc_lms_map[i].id) { + /* Get string corresponding to numeric identifier. */ + str = wc_lms_map[i].str; + break; + } + } + + /* Return the string or NULL. */ + return str; +} + +/* Set the wc_LmsParm of an LMS key. + * + * Use this if you wish to set a key with a predefined parameter set, + * such as WC_LMS_PARM_L2_H10_W8. + * + * Key must be inited before calling this. + * + * @param [in, out] key LMS key to set parameters on. + * @param [in] lmsParm Identifier of parameters. + * @return 0 on success. + * @return BAD_FUNC_ARG when key is NULL. + * @return BAD_FUNC_ARG when parameters not supported. + */ +int wc_LmsKey_SetLmsParm(LmsKey* key, enum wc_LmsParm lmsParm) +{ + int ret = 0; + + /* Validate parameters. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + + /* Check state is valid. */ + if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) { + WOLFSSL_MSG("error: LmsKey needs init"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + int i; + + ret = BAD_FUNC_ARG; + /* Search through table for matching numeric identifier. */ + for (i = 0; i < WC_LMS_MAP_LEN; i++) { + if (lmsParm == wc_lms_map[i].id) { + /* Set the parameters into the key. */ + key->params = &wc_lms_map[i].params; + ret = 0; + break; + } + } + } + + if (ret == 0) { + /* Move the state to params set. + * Key is ready for MakeKey or Reload. */ + key->state = WC_LMS_STATE_PARMSET; + } + + return ret; +} + +/* Set the parameters of an LMS key. + * + * Use this if you wish to set specific parameters not found in the + * wc_LmsParm predefined sets. See comments in lms.h for allowed + * parameters. + * + * Key must be inited before calling this. + * + * @param [in, out] key LMS key to set parameters on. + * @param [in] levels Number of tree levels. + * @param [in] height Height of each tree. + * @param [in] winternitz Width or Winternitz coefficient. + * @return 0 on success. + * @return BAD_FUNC_ARG when key is NULL. + * @return BAD_FUNC_ARG when parameters not supported. + * */ +int wc_LmsKey_SetParameters(LmsKey* key, int levels, int height, + int winternitz) +{ + int ret = 0; + + /* Validate parameters. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + + /* Check state is valid. */ + if ((ret == 0) && (key->state != WC_LMS_STATE_INITED)) { + WOLFSSL_MSG("error: LmsKey needs init"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + int i; + + ret = BAD_FUNC_ARG; + /* Search through table for matching levels, height and width. */ + for (i = 0; i < WC_LMS_MAP_LEN; i++) { + if ((levels == wc_lms_map[i].params.levels) && + (height == wc_lms_map[i].params.height) && + (winternitz == wc_lms_map[i].params.width)) { + /* Set the parameters into the key. */ + key->params = &wc_lms_map[i].params; + ret = 0; + break; + } + } + } + + if (ret == 0) { + /* Move the state to params set. + * Key is ready for MakeKey or Reload. */ + key->state = WC_LMS_STATE_PARMSET; + } + + return ret; +} + +/* Get the parameters of an LMS key. + * + * Key must be inited and parameters set before calling this. + * + * @param [in] key LMS key. + * @param [out] levels Number of levels of trees. + * @param [out] height Height of the trees. + * @param [out] winternitz Winternitz width. + * Returns 0 on success. + * */ +int wc_LmsKey_GetParameters(const LmsKey* key, int* levels, int* height, + int* winternitz) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (levels == NULL) || (height == NULL) || + (winternitz == NULL)) { + ret = BAD_FUNC_ARG; + } + + /* Validate the parameters are available. */ + if ((ret == 0) && (key->params == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Set the levels, height and Winternitz width from parameters. */ + *levels = key->params->levels; + *height = key->params->height; + *winternitz = key->params->width; + } + + return ret; +} + +/* Frees the LMS key from memory. + * + * This does not affect the private key saved to non-volatile storage. + * + * @param [in, out] key LMS key to free. + */ +void wc_LmsKey_Free(LmsKey* key) +{ + if (key != NULL) { + #ifndef WOLFSSL_LMS_VERIFY_ONLY + if (key->priv_data != NULL) { + const LmsParams* params = key->params; + + ForceZero(key->priv_data, LMS_PRIV_DATA_LEN(params->levels, + params->height, params->p, params->rootLevels, + params->cacheBits)); + + XFREE(key->priv_data, key->heap, DYNAMIC_TYPE_LMS); + } + #endif + + ForceZero(key, sizeof(LmsKey)); + + key->state = WC_LMS_STATE_FREED; + } +} + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Set the write private key callback to the LMS key structure. + * + * The callback must be able to write/update the private key to + * non-volatile storage. + * + * @param [in, out] key LMS key. + * @param [in] write_cb Callback function that stores private key. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or write_cb is NULL. + * @return BAD_STATE_E when key state is invalid. + */ +int wc_LmsKey_SetWriteCb(LmsKey* key, wc_lms_write_private_key_cb write_cb) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (write_cb == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Changing the write callback of an already working key is forbidden. */ + if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) { + WOLFSSL_MSG("error: wc_LmsKey_SetWriteCb: key in use"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Set the callback into the key. */ + key->write_private_key = write_cb; + } + + return ret; +} + +/* Set the read private key callback to the LMS key structure. + * + * The callback must be able to read the private key from + * non-volatile storage. + * + * @param [in, out] key LMS key. + * @param [in] read_cb Callback function that loads private key. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or read_cb is NULL. + * @return BAD_STATE_E when key state is invalid. + * */ +int wc_LmsKey_SetReadCb(LmsKey* key, wc_lms_read_private_key_cb read_cb) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (read_cb == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Changing the read callback of an already working key is forbidden. */ + if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) { + WOLFSSL_MSG("error: wc_LmsKey_SetReadCb: key in use"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Set the callback into the key. */ + key->read_private_key = read_cb; + } + + return ret; +} + +/* Sets the context to be used by write and read callbacks. + * + * E.g. this could be a filename if the callbacks write/read to file. + * + * @param [in, out] key LMS key. + * @param [in] context Pointer to data for read/write callbacks. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or context is NULL. + * @return BAD_STATE_E when key state is invalid. + * */ +int wc_LmsKey_SetContext(LmsKey* key, void* context) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (context == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Setting context of an already working key is forbidden. */ + if ((ret == 0) && (key->state == WC_LMS_STATE_OK)) { + WOLFSSL_MSG("error: wc_LmsKey_SetContext: key in use"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Set the callback context into the key. */ + key->context = context; + } + + return ret; +} + +/* Make the LMS private/public key pair. The key must have its parameters + * set before calling this. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * @param [in, out] key LMS key. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or rng is NULL. + * @return BAD_STATE_E when key is in an invalid state. + * @return BAD_FUNC_ARG when write callback or callback context not set. + * @return BAD_STATE_E when no more signatures can be created. + */ +int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (rng == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Check state. */ + if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) { + WOLFSSL_MSG("error: LmsKey not ready for generation"); + ret = BAD_STATE_E; + } + /* Check write callback set. */ + if ((ret == 0) && (key->write_private_key == NULL)) { + WOLFSSL_MSG("error: LmsKey write callback is not set"); + ret = BAD_FUNC_ARG; + } + /* Check callback context set. */ + if ((ret == 0) && (key->context == NULL)) { + WOLFSSL_MSG("error: LmsKey context is not set"); + ret = BAD_FUNC_ARG; + } + + if ((ret == 0) && (key->priv_data == NULL)) { + const LmsParams* params = key->params; + + /* Allocate memory for the private key data. */ + key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels, + params->height, params->p, params->rootLevels, params->cacheBits), + key->heap, DYNAMIC_TYPE_LMS); + /* Check pointer is valid. */ + if (key->priv_data == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + LmsState* state; + #else + LmsState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for working state. */ + state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize working state for use. */ + ret = wc_lmskey_state_init(state, key->params); + if (ret == 0) { + /* Make the HSS key. */ + ret = wc_hss_make_key(state, rng, key->priv_raw, &key->priv, + key->priv_data, key->pub); + wc_lmskey_state_free(state); + } + ForceZero(state, sizeof(LmsState)); + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + if (ret == 0) { + /* Write private key to storage. */ + int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN, + key->context); + if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) { + ret = IO_FAILED_E; + } + } + + /* This should not happen, but check whether signatures can be created. */ + if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) { + WOLFSSL_MSG("error: generated LMS key signatures exhausted"); + key->state = WC_LMS_STATE_NOSIGS; + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Update state. */ + key->state = WC_LMS_STATE_OK; + } + + return ret; +} + +/* Reload a key that has been prepared with the appropriate params and + * data. Use this if you wish to resume signing with an existing key. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * @param [in, out] key LMS key. + * + * Returns 0 on success. */ +int wc_LmsKey_Reload(LmsKey* key) +{ + int ret = 0; + + /* Validate parameter. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + /* Check state. */ + if ((ret == 0) && (key->state != WC_LMS_STATE_PARMSET)) { + WOLFSSL_MSG("error: LmsKey not ready for reload"); + ret = BAD_STATE_E; + } + /* Check read callback present. */ + if ((ret == 0) && (key->read_private_key == NULL)) { + WOLFSSL_MSG("error: LmsKey read callback is not set"); + ret = BAD_FUNC_ARG; + } + /* Check context for callback set */ + if ((ret == 0) && (key->context == NULL)) { + WOLFSSL_MSG("error: LmsKey context is not set"); + ret = BAD_FUNC_ARG; + } + + if ((ret == 0) && (key->priv_data == NULL)) { + const LmsParams* params = key->params; + + /* Allocate memory for the private key data. */ + key->priv_data = XMALLOC(LMS_PRIV_DATA_LEN(params->levels, + params->height, params->p, params->rootLevels, params->cacheBits), + key->heap, DYNAMIC_TYPE_LMS); + /* Check pointer is valid. */ + if (key->priv_data == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + /* Load private key. */ + int rv = key->read_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN, + key->context); + if (rv != WC_LMS_RC_READ_TO_MEMORY) { + ret = IO_FAILED_E; + } + } + + /* Double check the key actually has signatures left. */ + if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) { + WOLFSSL_MSG("error: reloaded LMS key signatures exhausted"); + key->state = WC_LMS_STATE_NOSIGS; + ret = BAD_STATE_E; + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + LmsState* state; + #else + LmsState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for working state. */ + state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize working state for use. */ + ret = wc_lmskey_state_init(state, key->params); + if (ret == 0) { + /* Reload the key ready for signing. */ + ret = wc_hss_reload_key(state, key->priv_raw, &key->priv, + key->priv_data, NULL); + } + ForceZero(state, sizeof(LmsState)); + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + + if (ret == 0) { + /* Update state. */ + key->state = WC_LMS_STATE_OK; + } + + return ret; +} + +/* Get the private key length based on parameter set of key. + * + * @param [in] key LMS key. + * @param [out] len Length of private key. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL or parameters not set. + */ +int wc_LmsKey_GetPrivLen(const LmsKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL) || (key->params == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Return private key length from parameter set. */ + *len = HSS_PRIVATE_KEY_LEN; + } + + return ret; +} + +/* Sign a message. + * + * @param [in, out] key LMS key to sign with. + * @param [out] sig Signature data. Buffer must be big enough to hold + * signature data. + * @param [out] sigSz Length of signature data. + * @param [in] msg Message to sign. + * @param [in] msgSz Length of message in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, sig, sigSz or msg is NULL. + * @return BAD_FUNC_ARG when msgSz is not greater than 0. + */ +int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, + int msgSz) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (sig == NULL) || (sigSz == NULL) || (msg == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (msgSz <= 0)) { + ret = BAD_FUNC_ARG; + } + /* Check state. */ + if ((ret == 0) && (key->state == WC_LMS_STATE_NOSIGS)) { + WOLFSSL_MSG("error: LMS signatures exhausted"); + ret = BAD_STATE_E; + } + if ((ret == 0) && (key->state != WC_LMS_STATE_OK)) { + /* The key had an error the last time it was used, and we + * can't guarantee its state. */ + WOLFSSL_MSG("error: can't sign, LMS key not in good state"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + LmsState* state; + #else + LmsState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for working state. */ + state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize working state for use. */ + ret = wc_lmskey_state_init(state, key->params); + if (ret == 0) { + /* Sign message. */ + ret = wc_hss_sign(state, key->priv_raw, &key->priv, + key->priv_data, msg, msgSz, sig); + wc_lmskey_state_free(state); + } + ForceZero(state, sizeof(LmsState)); + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + if (ret == 0) { + *sigSz = (word32)key->params->sig_len; + } + if (ret == 0) { + /* Write private key to storage. */ + int rv = key->write_private_key(key->priv_raw, HSS_PRIVATE_KEY_LEN, + key->context); + if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) { + ret = IO_FAILED_E; + } + } + + return ret; +} + +/* Returns whether signatures can be created with key. + * + * @param [in] key LMS key. + * + * @return 1 if there are signatures remaining. + * @return 0 if available signatures are exhausted. + */ +int wc_LmsKey_SigsLeft(LmsKey* key) +{ + int ret = 0; + + /* NULL keys have no signatures remaining. */ + if (key != NULL) { + ret = wc_hss_sigsleft(key->params, key->priv_raw); + } + + return ret; +} + +#endif /* ifndef WOLFSSL_LMS_VERIFY_ONLY*/ + +/* Get the public key length based on parameter set of key. + * + * @param [in] key LMS key. + * @param [out] len Length of public key. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL or parameters not set. + */ +int wc_LmsKey_GetPubLen(const LmsKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters */ + if ((key == NULL) || (len == NULL) || (key->params == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + *len = HSS_PUBLIC_KEY_LEN; + } + + return ret; +} + +/* Export a generated public key and parameter set from one LmsKey + * to another. Use this to prepare a signature verification LmsKey + * that is pub only. + * + * Though the public key is all that is used to verify signatures, + * the parameter set is needed to calculate the signature length + * before hand. + * + * @param [out] keyDst LMS key to copy into. + * @param [in] keySrc LMS key to copy. + * @return 0 on success. + * @return BAD_FUNC_ARG when keyDst or keySrc is NULL. + */ +int wc_LmsKey_ExportPub(LmsKey* keyDst, const LmsKey* keySrc) +{ + int ret = 0; + + if ((keyDst == NULL) || (keySrc == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ForceZero(keyDst, sizeof(LmsKey)); + + keyDst->params = keySrc->params; + XMEMCPY(keyDst->pub, keySrc->pub, sizeof(keySrc->pub)); + + /* Mark this key as verify only, to prevent misuse. */ + keyDst->state = WC_LMS_STATE_VERIFYONLY; + } + + return ret; +} + +/* Exports the raw LMS public key buffer from key to out buffer. + * The out buffer should be large enough to hold the public key, and + * outLen should indicate the size of the buffer. + * + * Call wc_LmsKey_GetPubLen beforehand to determine pubLen. + * + * @param [in] key LMS key. + * @param [out] out Buffer to hold encoded public key. + * @param [in, out] outLen On in, length of out in bytes. + * On out, the length of the public key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, out or outLen is NULL. + * @return BUFFER_E when outLen is too small to hold encoded public key. + */ +int wc_LmsKey_ExportPubRaw(const LmsKey* key, byte* out, word32* outLen) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (out == NULL) || (outLen == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Check size of out is sufficient. */ + if ((ret == 0) && (*outLen < HSS_PUBLIC_KEY_LEN)) { + ret = BUFFER_E; + } + + if (ret == 0) { + /* Return encoded public key. */ + XMEMCPY(out, key->pub, HSS_PUBLIC_KEY_LEN); + *outLen = HSS_PUBLIC_KEY_LEN; + } + + return ret; +} + +/* Imports a raw public key buffer from in array to LmsKey key. + * + * The LMS parameters must be set first with wc_LmsKey_SetLmsParm or + * wc_LmsKey_SetParameters, and inLen must match the length returned + * by wc_LmsKey_GetPubLen. + * + * Call wc_LmsKey_GetPubLen beforehand to determine pubLen. + * + * @param [in, out] key LMS key to put public key in. + * @param [in] in Buffer holding encoded public key. + * @param [in] inLen Length of encoded public key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or in is NULL. + * @return BUFFER_E when inLen does not match public key length by parameters. + */ +int wc_LmsKey_ImportPubRaw(LmsKey* key, const byte* in, word32 inLen) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (inLen != HSS_PUBLIC_KEY_LEN)) { + /* Something inconsistent. Parameters weren't set, or input + * pub key is wrong.*/ + return BUFFER_E; + } + + if (ret == 0) { + XMEMCPY(key->pub, in, inLen); + + key->state = WC_LMS_STATE_VERIFYONLY; + } + + return ret; +} + +/* Given a levels, height, winternitz parameter set, determine + * the signature length. + * + * Call this before wc_LmsKey_Sign so you know the length of + * the required signature buffer. + * + * @param [in] key LMS key. + * @param [out] len Length of a signature in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + */ +int wc_LmsKey_GetSigLen(const LmsKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + *len = key->params->sig_len; + } + + return ret; +} + +/* Verify the signature of the message with public key. + * + * @param [in] key LMS key. + * @param [in] sig Signature to verify. + * @param [in] sigSz Size of signature in bytes. + * @param [in] msg Message to verify. + * @param [in] msgSz Length of the message in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when a key, sig or msg is NULL. + * @return SIG_VERIFY_E when signature did not verify message. + * @return BAD_STATE_E when wrong state for operation. + * @return BUFFER_E when sigSz is invalid for parameters. + */ +int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz, + const byte* msg, int msgSz) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (sig == NULL) || (msg == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Check state. */ + if ((ret == 0) && (key->state != WC_LMS_STATE_OK) && + (key->state != WC_LMS_STATE_VERIFYONLY)) { + /* LMS key not ready for verification. Param str must be + * set first, and Reload() called. */ + WOLFSSL_MSG("error: LMS key not ready for verification"); + ret = BAD_STATE_E; + } + /* Check signature length. */ + if ((ret == 0) && (sigSz != key->params->sig_len)) { + ret = BUFFER_E; + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + LmsState* state; + #else + LmsState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for working state. */ + state = XMALLOC(sizeof(LmsState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize working state for use. */ + ret = wc_lmskey_state_init(state, key->params); + if (ret == 0) { + /* Verify signature of message with public key. */ + ret = wc_hss_verify(state, key->pub, msg, msgSz, sig); + wc_lmskey_state_free(state); + } + ForceZero(state, sizeof(LmsState)); + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */ diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index dbd5ed68b0..3f48420b4a 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -19,8 +19,3074 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -#include +/* Implementation based on: + * RFC 8554: Leighton-Micali Hash-Based Signatures + * https://datatracker.ietf.org/doc/html/rfc8554 + * Implementation by Sean Parkinson. + */ + +/* Possible LMS options: + * + * WC_LMS_FULL_HASH Default: OFF + * Performs a full hash instead of assuming internals. + * Enable when using hardware SHA-256. + * WOLFSSL_LMS_VERIFY_ONLY Default: OFF + * Only compiles in verification code. + * WOLFSSL_WC_LMS_SMALL Default: OFF + * Implementation is smaller code size with slow signing. + * Enable when memory is limited. + */ + +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) + +/* Length of R in bytes. */ +#define LMS_R_LEN 4 +/* Length of D in bytes. */ +#define LMS_D_LEN 2 +/* Length of checksum in bytes. */ +#define LMS_CKSM_LEN 2 + +/* Predefined values used in hashes to make them unique. */ +/* Fixed value for calculating x. */ +#define LMS_D_FIXED 0xff +/* D value when computing public key. */ +#define LMS_D_PBLC 0x8080 +/* D value when computing message. */ +#define LMS_D_MESG 0x8181 +/* D value when computing leaf node. */ +#define LMS_D_LEAF 0x8282 +/* D value when computing interior node. */ +#define LMS_D_INTR 0x8383 +/* D value when computing C, randomizer value. */ +#define LMS_D_C 0xfffd +/* D value when computing child SEED for private key. */ +#define LMS_D_CHILD_SEED 0xfffe +/* D value when computing child I for private key. */ +#define LMS_D_CHILD_I 0xffff + +/* Length of data to hash when computing seed: + * 16 + 4 + 2 + 32 = 54 */ +#define LMS_SEED_HASH_LEN \ + (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + LMS_MAX_NODE_LEN) + +/* Length of data to hash when computing a node: + * 16 + 4 + 2 + 32 + 32 = 86 */ +#define LMS_NODE_HASH_LEN \ + (LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + 2 * LMS_MAX_NODE_LEN) + +/* Length of data to hash when computing most results: + * 16 + 4 + 2 + 1 + 32 = 55 */ +#define LMS_HASH_BUFFER_LEN \ + (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + LMS_MAX_NODE_LEN) + +/* Length of data to hash when computing Q: + * 16 + 4 + 2 + 32 = 54 */ +#define LMS_Q_BUFFER_LEN \ + (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_MAX_NODE_LEN) + +/* Length of preliminary data to hash when computing K: + * 16 + 4 + 2 = 22 */ +#define LMS_K_PRE_LEN (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN) + +/* Length of preliminary data to hash when computing message hash: + * 16 + 4 + 2 = 22 */ +#define LMS_MSG_PRE_LEN (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN) + + +#ifdef WC_LMS_DEBUG_PRINT_DATA +/* Print data when dubgging implementation. + * + * @param [in] name String to print before data. + * @param [in] data Array of bytes. + * @param [in] len Length of data in array. + */ +static void print_data(const char* name, const byte* data, int len) +{ + int i; + + fprintf(stderr, "%6s: ", name); + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x", data[i]); + } + fprintf(stderr, "\n"); +} +#endif + +/*************************************** + * Index APIs + **************************************/ + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Zero index. + * + * @param [out] a Byte array. Big-endian encoding. + * @param [in] len Length of array in bytes. + */ +static WC_INLINE void wc_lms_idx_zero(unsigned char* a, int len) +{ + XMEMSET(a, 0, len); +} + +/* Increment big-endian value. + * + * @param [in, out] a Byte array. Big-endian encoding. + * @param [in] len Length of array in bytes. + */ +static WC_INLINE void wc_lms_idx_inc(unsigned char* a, int len) +{ + int i; + + /* Starting at least-significant byte up to most. */ + for (i = len - 1; i >= 0; i--) { + /* Add one/carry to byte. */ + if ((++a[i]) != 0) { + /* No more carry. */ + break; + } + } +} +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +/*************************************** + * Hash APIs + **************************************/ + +/* Set hash data and length into SHA-256 digest. + * + * @param [in, out] state SHA-256 digest object. + * @param [in] data Data to add to hash. + * @param [in] len Number of bytes in data. Must be less than a block. + */ +#define LMS_SHA256_SET_DATA(sha256, data, len) \ +do { \ + XMEMCPY((sha256)->buffer, (data), (len)); \ + (sha256)->buffLen = (len); \ + (sha256)->loLen = (len); \ +} while (0) + +/* Add hash data and length into SHA-256 digest. + * + * @param [in, out] state SHA-256 digest object. + * @param [in] data Data to add to hash. + * @param [in] len Number of bytes in data. Must be less than a block. + */ +#define LMS_SHA256_ADD_DATA(sha256, data, len) \ +do { \ + XMEMCPY((byte*)(sha256)->buffer + (sha256)->buffLen, (data), (len)); \ + (sha256)->buffLen += (len); \ + (sha256)->loLen += (len); \ +} while (0) + +/* Set the length of 54 bytes in buffer as per SHA-256 final operation. + * + * @param [in, out] buffer Hash data buffer to add length to. + */ +#define LMS_SHA256_SET_LEN_54(buffer) \ +do { \ + (buffer)[54] = 0x80; \ + (buffer)[55] = 0x00; \ + (buffer)[56] = 0x00; \ + (buffer)[57] = 0x00; \ + (buffer)[58] = 0x00; \ + (buffer)[59] = 0x00; \ + (buffer)[60] = 0x00; \ + (buffer)[61] = 0x00; \ + (buffer)[62] = 0x01; \ + (buffer)[63] = 0xb0; \ +} while (0) + +/* Set the length of 55 bytes in buffer as per SHA-256 final operation. + * + * @param [in, out] buffer Hash data buffer to add length to. + */ +#define LMS_SHA256_SET_LEN_55(buffer) \ +do { \ + (buffer)[55] = 0x80; \ + (buffer)[56] = 0x00; \ + (buffer)[57] = 0x00; \ + (buffer)[58] = 0x00; \ + (buffer)[59] = 0x00; \ + (buffer)[60] = 0x00; \ + (buffer)[61] = 0x00; \ + (buffer)[62] = 0x01; \ + (buffer)[63] = 0xb8; \ +} while (0) + +#ifndef WC_LMS_FULL_HASH +/* Hash one full block of data and compute result. + * + * @param [in] sha256 SHA-256 hash object. + * @param [in] data Data to hash. + * @param [out] hash Hash output. + * @return 0 on success. + */ +static WC_INLINE int wc_lms_hash_block(wc_Sha256* sha256, const byte* data, + byte* hash) +{ + /* Hash the block and reset SHA-256 state. */ + return wc_Sha256HashBlock(sha256, data, hash); +} +#endif /* !WC_LMS_FULL_HASH */ + +/* Hash data and compute result. + * + * @param [in] sha256 SHA-256 hash object. + * @param [in] data Data to hash. + * @param [in] len Length of data to hash. + * @param [out] hash Hash output. + * @return 0 on success. + */ +static WC_INLINE int wc_lms_hash(wc_Sha256* sha256, byte* data, word32 len, + byte* hash) +{ + int ret; + +#ifndef WC_LMS_FULL_HASH + if (len < WC_SHA256_BLOCK_SIZE) { + /* Store data into SHA-256 object's buffer. */ + LMS_SHA256_SET_DATA(sha256, data, len); + ret = wc_Sha256Final(sha256, hash); + } + else if (len < WC_SHA256_BLOCK_SIZE + WC_SHA256_PAD_SIZE) { + ret = wc_Sha256HashBlock(sha256, data, NULL); + if (ret == 0) { + byte* buffer = (byte*)sha256->buffer; + int rem = len - WC_SHA256_BLOCK_SIZE; + + XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE, rem); + buffer[rem++] = 0x80; + XMEMSET(buffer + rem, 0, WC_SHA256_BLOCK_SIZE - 2 - rem); + buffer[WC_SHA256_BLOCK_SIZE - 2] = (byte)(len >> 5); + buffer[WC_SHA256_BLOCK_SIZE - 1] = (byte)(len << 3); + ret = wc_Sha256HashBlock(sha256, buffer, hash); + } + } + else { + ret = wc_Sha256Update(sha256, data, len); + if (ret == 0) { + ret = wc_Sha256Final(sha256, hash); + } + } +#else + ret = wc_Sha256Update(sha256, data, len); + if (ret == 0) { + ret = wc_Sha256Final(sha256, hash); + } +#endif /* !WC_LMS_FULL_HASH */ + + return ret; +} + +/* Update hash with first data. + * + * Sets the data directly into SHA-256's buffer if valid. + * + * @param [in] sha256 SHA-256 hash object. + * @param [in] data Data to hash. + * @param [in] len Length of data to hash. + * @return 0 on success. + */ +static WC_INLINE int wc_lms_hash_first(wc_Sha256* sha256, const byte* data, + word32 len) +{ + int ret = 0; + +#ifndef WC_LMS_FULL_HASH + if (len < WC_SHA256_BLOCK_SIZE) { + /* Store data into SHA-256 object's buffer. */ + LMS_SHA256_SET_DATA(sha256, data, len); + } + else +#endif /* !WC_LMS_FULL_HASH */ + { + ret = wc_Sha256Update(sha256, data, len); + } + + return ret; +} + +/* Update hash with further data. + * + * Adds the data directly into SHA-256's buffer if valid. + * + * @param [in] sha256 SHA-256 hash object. + * @param [in] data Data to hash. + * @param [in] len Length of data to hash. + * @return 0 on success. + */ +static WC_INLINE int wc_lms_hash_update(wc_Sha256* sha256, const byte* data, + word32 len) +{ + int ret = 0; + +#ifndef WC_LMS_FULL_HASH + if (sha256->buffLen + len < WC_SHA256_BLOCK_SIZE) { + /* Add data to SHA-256 object's buffer. */ + LMS_SHA256_ADD_DATA(sha256, data, len); + } + else if (sha256->buffLen + len < 2 * WC_SHA256_BLOCK_SIZE) { + byte* buffer = (byte*)sha256->buffer; + + XMEMCPY(buffer + sha256->buffLen, data, + WC_SHA256_BLOCK_SIZE - sha256->buffLen); + ret = wc_Sha256HashBlock(sha256, buffer, NULL); + if (ret == 0) { + int rem = len - (WC_SHA256_BLOCK_SIZE - sha256->buffLen); + XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE - sha256->buffLen, rem); + sha256->buffLen = rem; + sha256->loLen += len; + } + } + else { + ret = wc_Sha256Update(sha256, data, len); + } +#else + ret = wc_Sha256Update(sha256, data, len); +#endif /* !WC_LMS_FULL_HASH */ + + return ret; +} + +/* Finalize hash. + * + * @param [in] sha256 SHA-256 hash object. + * @param [out] hash Hash output. + * @return 0 on success. + */ +static WC_INLINE int wc_lms_hash_final(wc_Sha256* sha256, byte* hash) +{ +#ifndef WC_LMS_FULL_HASH + int ret = 0; + byte* buffer = (byte*)sha256->buffer; + + buffer[sha256->buffLen++] = 0x80; + if (sha256->buffLen > WC_SHA256_PAD_SIZE) { + XMEMSET(buffer + sha256->buffLen, 0, + WC_SHA256_BLOCK_SIZE - sha256->buffLen); + ret = wc_Sha256HashBlock(sha256, buffer, NULL); + sha256->buffLen = 0; + } + if (ret == 0) { + XMEMSET(buffer + sha256->buffLen, 0, + WC_SHA256_BLOCK_SIZE - 8 - sha256->buffLen); + sha256->hiLen = (sha256->hiLen << 3) + (sha256->loLen >> 29); + sha256->loLen = sha256->loLen << 3; + #ifdef LITTLE_ENDIAN_ORDER + sha256->buffer[14] = ByteReverseWord32(sha256->hiLen); + sha256->buffer[15] = ByteReverseWord32(sha256->loLen); + #else + sha256->buffer[14] = sha256->hiLen; + sha256->buffer[15] = sha256->loLen; + #endif + ret = wc_Sha256HashBlock(sha256, buffer, hash); + sha256->buffLen = 0; + sha256->hiLen = 0; + sha256->loLen = 0; + } + + return ret; +#else + return wc_Sha256Final(sha256, hash); +#endif +} + +/*************************************** + * LM-OTS APIs + **************************************/ + +/* Expand Q to and array of Winternitz width bits values plus checksum. + * + * Supported Winternitz widths: 8, 4, 2, 1. + * + * Algorithm 2: Checksum Calculation + * sum = 0 + * for ( i = 0; i < (n*8/w); i = i + 1 ) { + * sum = sum + (2^w - 1) - coef(S, i, w) + * } + * return (sum << ls) + * Section 3.1.3: Strings of w-Bit Elements + * coef(S, i, w) = (2^w - 1) AND + * ( byte(S, floor(i * w / 8)) >> + * (8 - (w * (i % (8 / w)) + w)) ) + * Combine coefficient expansion with checksum calculation. + * + * @param [in] q Q array of bytes. + * @param [in] n Number of bytes in Q. + * @param [in] w Winternitz width in bits. + * @param [in] ls Left shift of checksum. + * @param [out] qe Expanded Q with checksum. + * @return 0 on success. + * @return BAD_FUNC_ARG when Winternitz width is not supported. + */ +static WC_INLINE int wc_lmots_q_expand(byte* q, word8 n, word8 w, word8 ls, + byte* qe) +{ + int ret = 0; + word16 sum; + unsigned int i; + +#ifndef WOLFSSL_WC_LMS_SMALL + switch (w) { + /* Winternitz width of 8. */ + case 8: + /* No expansion required, just copy. */ + XMEMCPY(qe, q, n); + /* Start sum with all 2^w - 1s and subtract from that. */ + sum = 0xff * n; + /* For each byte of the hash. */ + for (i = 0; i < n; i++) { + /* Subtract coefficient from sum. */ + sum -= q[i]; + } + /* Put coefficients of checksum on the end. */ + qe[n + 0] = (word8)(sum >> 8); + qe[n + 1] = (word8)(sum ); + break; + /* Winternitz width of 4. */ + case 4: + sum = 2 * 0xf * n; + /* For each byte of the hash. */ + for (i = 0; i < n; i++) { + /* Get coefficient. */ + qe[0] = (q[i] >> 4) ; + qe[1] = (q[i] ) & 0xf; + /* Subtract coefficients from sum. */ + sum -= qe[0]; + sum -= qe[1]; + /* Move to next coefficients. */ + qe += 2; + } + /* Put coefficients of checksum on the end. */ + qe[0] = (word8)((sum >> 8) & 0xf); + qe[1] = (word8)((sum >> 4) & 0xf); + qe[2] = (word8)((sum ) & 0xf); + break; + /* Winternitz width of 2. */ + case 2: + sum = 4 * 0x3 * n; + /* For each byte of the hash. */ + for (i = 0; i < n; i++) { + /* Get coefficients. */ + qe[0] = (q[i] >> 4) ; + qe[0] = (q[i] >> 6) ; + qe[1] = (q[i] >> 4) & 0x3; + qe[2] = (q[i] >> 2) & 0x3; + qe[3] = (q[i] ) & 0x3; + /* Subtract coefficients from sum. */ + sum -= qe[0]; + sum -= qe[1]; + sum -= qe[2]; + sum -= qe[3]; + /* Move to next coefficients. */ + qe += 4; + } + /* Put coefficients of checksum on the end. */ + qe[0] = (word8)((sum >> 8) & 0x3); + qe[1] = (word8)((sum >> 6) & 0x3); + qe[2] = (word8)((sum >> 4) & 0x3); + qe[3] = (word8)((sum >> 2) & 0x3); + qe[4] = (word8)((sum ) & 0x3); + break; + /* Winternitz width of 1. */ + case 1: + sum = 8 * 0x01 * n; + /* For each byte of the hash. */ + for (i = 0; i < n; i++) { + /* Get coefficients. */ + qe[0] = (q[i] >> 4) ; + qe[0] = (q[i] >> 7) ; + qe[1] = (q[i] >> 6) & 0x1; + qe[2] = (q[i] >> 5) & 0x1; + qe[3] = (q[i] >> 4) & 0x1; + qe[4] = (q[i] >> 3) & 0x1; + qe[5] = (q[i] >> 2) & 0x1; + qe[6] = (q[i] >> 1) & 0x1; + qe[7] = (q[i] ) & 0x1; + /* Subtract coefficients from sum. */ + sum -= qe[0]; + sum -= qe[1]; + sum -= qe[2]; + sum -= qe[3]; + sum -= qe[4]; + sum -= qe[5]; + sum -= qe[6]; + sum -= qe[7]; + /* Move to next coefficients. */ + qe += 8; + } + /* Put coefficients of checksum on the end. */ + qe[0] = (word8)((sum >> 8) ); + qe[1] = (word8)((sum >> 7) & 0x1); + qe[2] = (word8)((sum >> 6) & 0x1); + qe[3] = (word8)((sum >> 5) & 0x1); + qe[4] = (word8)((sum >> 4) & 0x1); + qe[5] = (word8)((sum >> 3) & 0x1); + qe[6] = (word8)((sum >> 2) & 0x1); + qe[7] = (word8)((sum >> 1) & 0x1); + qe[8] = (word8)((sum ) & 0x1); + break; + default: + ret = BAD_FUNC_ARG; + break; + } + + (void)ls; +#else + int j; + + if ((w != 8) && (w != 4) && (w != 2) && (w != 1)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Start sum with all 2^w - 1s and subtract from that. */ + sum = ((1 << w) - 1) * ((n * 8) / w); + /* For each byte of the hash. */ + for (i = 0; i < n; i++) { + /* Get next byte. */ + byte a = *(q++); + /* For each width bits of byte. */ + for (j = 8 - w; j >= 0; j -= w) { + /* Get coefficient. */ + *qe = a >> (8 - w); + /* Subtract coefficient from sum. */ + sum -= *qe; + /* Move to next coefficient. */ + qe++; + /* Remove width bits. */ + a <<= w; + } + } + /* Shift sum up as required to pack it on the end of hash. */ + sum <<= ls; + /* For each width buts of checksum. */ + for (j = 16 - w; j >= ls; j--) { + /* Get coefficient. */ + *(qe++) = sum >> (16 - w); + /* Remove width bits. */ + sum <<= w; + } + } +#endif /* !WOLFSSL_WC_LMS_SMALL */ + + return ret; +} + +/* Calculate the hash for the message. + * + * Algorithm 3: Generating a One-Time Signature From a Private Key and a + * Message + * ... + * 5. Compute the array y as follows: + * Q = H(I || u32str(q) || u16str(D_MESG) || C || message) + * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature, + * Message, Signature Typecode pubtype, and Identifiers I, q + * ... + * 3. Compute the string Kc as follows: + * Q = H(I || u32str(q) || u16str(D_MESG) || C || message) + * + * @param [in, out] state LMS state. + * @param [in] msg Message to hash. + * @param [in] msgSz Length of message in bytes. + * @param [in] c C or randomizer value. + * @param [out] q Computed Q value. + * @return 0 on success. + */ +static int wc_lmots_msg_hash(LmsState* state, const byte* msg, word32 msgSz, + const byte* c, byte* q) +{ + int ret; + byte* buffer = state->buffer; + byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN; + + /* I || u32str(q) || u16str(D_MESG) */ + c16toa(LMS_D_MESG, ip); + /* H(I || u32str(q) || u16str(D_MESG) || ...) */ + ret = wc_lms_hash_first(&state->hash, buffer, LMS_MSG_PRE_LEN); + if (ret == 0) { + /* H(... || C || ...) */ + ret = wc_lms_hash_update(&state->hash, c, LMS_MAX_NODE_LEN); + } + if (ret == 0) { + /* H(... || message) */ + ret = wc_lms_hash_update(&state->hash, msg, msgSz); + } + if (ret == 0) { + /* Q = H(...) */ + ret = wc_lms_hash_final(&state->hash, q); + } + + return ret; +} + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Compute array y, intermediates of public key calculation, for signature. + * + * Verification will perform the remaining iterations of hashing. + * + * Algorithm 3: Generating a One-Time Signature From a Private Key and a + * Message + * ... + * 5. Compute the array y as follows: + * Q = H(I || u32str(q) || u16str(D_MESG) || C || message) + * for ( i = 0; i < p; i = i + 1 ) { + * a = coef(Q || Cksm(Q), i, w) + * tmp = x[i] + * for ( j = 0; j < a; j = j + 1 ) { + * tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) + * } + * y[i] = tmp + * } + * x[i] can be calculated on the fly using psueodo key generation in Appendix A. + * Appendix A, The elements of the LM-OTS private keys are computed as: + * x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). + * + * @param [in, out] state LMS state. + * @param [in] seed Seed to hash. + * @param [in] msg Message to sign. + * @param [in] msgSZ Length of message in bytes. + * @param [in] c C or randomizer value to hash. + * @param [out] y Calculated intermediate hashes. + * @return 0 on success. + */ +static int wc_lmots_compute_y_from_seed(LmsState* state, const byte* seed, + const byte* msg, word32 msgSz, const byte* c, byte* y) +{ + const LmsParams* params = state->params; + int ret = 0; + word16 i; + byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN]; +#ifdef WOLFSSL_SMALL_STACK + byte* a = state->a; +#else + byte a[LMS_MAX_P]; +#endif /* WOLFSSL_SMALL_STACK */ + byte* buffer = state->buffer; + byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN; + byte* jp = ip + LMS_P_LEN; + byte* tmp = jp + LMS_W_LEN; + + /* Q = H(I || u32str(q) || u16str(D_MESG) || C || message) */ + ret = wc_lmots_msg_hash(state, msg, msgSz, c, q); + if (ret == 0) { + /* Calculate checksum list all coefficients. */ + ret = wc_lmots_q_expand(q, LMS_MAX_NODE_LEN, params->width, params->ls, + a); + } + #ifndef WC_LMS_FULL_HASH + if (ret == 0) { + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_55(buffer); + } + #endif /* !WC_LMS_FULL_HASH */ + + /* Compute y for each coefficient. */ + for (i = 0; (ret == 0) && (i < params->p); i++) { + unsigned int j; + + /* tmp = x[i] + * = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). */ + c16toa(i, ip); + *jp = LMS_D_FIXED; + XMEMCPY(tmp, seed, LMS_SEED_LEN); + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + + /* Apply the hash function coefficient number of times. */ + for (j = 0; (ret == 0) && (j < a[i]); j++) { + /* I || u32str(q) || u16str(i) || u8str(j) || tmp */ + *jp = j; + /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */ + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + } + + if (ret == 0) { + /* y[i] = tmp */ + XMEMCPY(y, tmp, LMS_MAX_NODE_LEN); + y += LMS_MAX_NODE_LEN; + } + } + + return ret; +} +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +/* Compute public key candidate K from signature. + * + * Signing performed the first coefficient number of iterations of hashing. + * + * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature, + * Message, Signature Typecode pubtype, and Identifiers I, q + * ... + * 3. Compute the string Kc as follows: + * Q = H(I || u32str(q) || u16str(D_MESG) || C || message) + * for ( i = 0; i < p; i = i + 1 ) { + * a = coef(Q || Cksm(Q), i, w) + * tmp = y[i] + * for ( j = a; j < 2^w - 1; j = j + 1 ) { + * tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) + * } + * z[i] = tmp + * } + * Kc = H(I || u32str(q) || u16str(D_PBLC) || + * z[0] || z[1] || ... || z[p-1]) + * 4, Return Kc. + * + * @param [in, out] state LMS state. + * @param [in] msg Message to compute Kc for. + * @param [in] msgSz Length of message in bytes. + * @param [in] c C or randomizer value from signature. + * @param [in] sig_y Part of signature containing array y. + * @param [out] kc Kc or public key candidate K. + * @return 0 on success. + */ +static int wc_lmots_compute_kc_from_sig(LmsState* state, const byte* msg, + word32 msgSz, const byte* c, const byte* sig_y, byte* kc) +{ + const LmsParams* params = state->params; + int ret; + word16 i; + byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN]; +#ifdef WOLFSSL_SMALL_STACK + byte* a = state->a; +#else + byte a[LMS_MAX_P]; +#endif /* WOLFSSL_SMALL_STACK */ + byte* buffer = state->buffer; + byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN; + byte* jp = ip + LMS_P_LEN; + byte* tmp = jp + LMS_W_LEN; + unsigned int max = ((unsigned int)1 << params->width) - 1; + + /* I || u32str(q) || u16str(D_PBLC). */ + c16toa(LMS_D_PBLC, ip); + /* H(I || u32str(q) || u16str(D_PBLC) || ...). */ + ret = wc_lms_hash_first(&state->hash_k, buffer, LMS_K_PRE_LEN); + if (ret == 0) { + /* Q = H(I || u32str(q) || u16str(D_MESG) || C || message) */ + ret = wc_lmots_msg_hash(state, msg, msgSz, c, q); + } + if (ret == 0) { + /* Calculate checksum list all coefficients. */ + ret = wc_lmots_q_expand(q, LMS_MAX_NODE_LEN, params->width, params->ls, + a); + } + #ifndef WC_LMS_FULL_HASH + if (ret == 0) { + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_55(buffer); + } + #endif /* !WC_LMS_FULL_HASH */ + + /* Compute z for each coefficient. */ + for (i = 0; (ret == 0) && (i < params->p); i++) { + unsigned int j; + + /* I || u32(str) || u16str(i) || ... */ + c16toa(i, ip); + + /* tmp = y[i]. + * I || u32(str) || u16str(i) || ... || tmp */ + XMEMCPY(tmp, sig_y, LMS_MAX_NODE_LEN); + sig_y += LMS_MAX_NODE_LEN; + + /* Finish iterations of hash from coefficient to max. */ + for (j = a[i]; (ret == 0) && (j < max); j++) { + /* I || u32str(q) || u16str(i) || u8str(j) || tmp */ + *jp = (word8)j; + /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */ + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + } + + if (ret == 0) { + /* H(... || z[i] || ...) (for calculating Kc). */ + ret = wc_lms_hash_update(&state->hash_k, tmp, LMS_MAX_NODE_LEN); + } + } + + if (ret == 0) { + /* Kc = H(...) */ + ret = wc_lms_hash_final(&state->hash_k, kc); + } + + return ret; +} + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Generate LM-OTS public key. + * + * Caller set: state->buffer = I || u32str(q) + * + * Algorithm 1: Generating a One-Time Signature Public Key From a Private Key + * ... + * 4. Compute the string K as follows: + * for ( i = 0; i < p; i = i + 1 ) { + * tmp = x[i] + * for ( j = 0; j < 2^w - 1; j = j + 1 ) { + * tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) + * } + * y[i] = tmp + * } + * K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1]) + * ... + * x[i] can be calculated on the fly using psueodo key generation in Appendix A. + * Appendix A, The elements of the LM-OTS private keys are computed as: + * x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). + * + * @param [in, out] state LMS state. + * @param [in] seed Seed to hash. + * @param [out] k K, the public key hash, or OTS_PUB_HASH + */ +static int wc_lmots_make_public_hash(LmsState* state, const byte* seed, byte* k) +{ + const LmsParams* params = state->params; + int ret; + word16 i; + byte* buffer = state->buffer; + byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN; + byte* jp = ip + LMS_P_LEN; + byte* tmp = jp + LMS_W_LEN; + unsigned int max = ((unsigned int)1 << params->width) - 1; + + /* I || u32str(q) || u16str(D_PBLC). */ + c16toa(LMS_D_PBLC, ip); + /* K = H(I || u32str(q) || u16str(D_PBLC) || ...) */ + ret = wc_lms_hash_first(&state->hash_k, buffer, LMS_K_PRE_LEN); + +#ifndef WC_LMS_FULL_HASH + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_55(buffer); +#endif /* !WC_LMS_FULL_HASH */ + + for (i = 0; (ret == 0) && (i < params->p); i++) { + unsigned int j; + + /* tmp = x[i] + * = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). */ + c16toa(i, ip); + *jp = LMS_D_FIXED; + XMEMCPY(tmp, seed, LMS_SEED_LEN); + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + /* Do all iterations to calculate y. */ + for (j = 0; (ret == 0) && (j < max); j++) { + /* I || u32str(q) || u16str(i) || u8str(j) || tmp */ + *jp = (word8)j; + /* tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp) */ + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + } + if (ret == 0) { + /* K = H(... || y[i] || ...) */ + ret = wc_lms_hash_update(&state->hash_k, tmp, LMS_MAX_NODE_LEN); + } + } + if (ret == 0) { + /* K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1]) */ + ret = wc_lms_hash_final(&state->hash_k, k); + } + + return ret; +} + +/* Encode the LM-OTS public key. + * + * Encoded into public key and signature if more than one level. + * T[1] is already in place. Putting in: type, ostype and I. + * + * Section 4.3: + * u32str(type) || u32str(otstype) || I || T[1] + * + * @param [in] params LMS parameters. + * @param [in] priv LMS private ley. + * @param [out] pub LMS public key. + */ +static void wc_lmots_public_key_encode(const LmsParams* params, + const byte* priv, byte* pub) +{ + const byte* priv_i = priv + LMS_Q_LEN + LMS_SEED_LEN; + + /* u32str(type) || ... || T(1) */ + c32toa(params->lmsType, pub); + pub += 4; + /* u32str(type) || u32str(otstype) || ... || T(1) */ + c32toa(params->lmOtsType, pub); + pub += 4; + /* u32str(type) || u32str(otstype) || I || T(1) */ + XMEMCPY(pub, priv_i, LMS_I_LEN); +} +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +/* Check the public key matches the parameters. + * + * @param [in] params LMS parameters. + * @param [in] pub Public key. + * @return 0 on success. + * @return PUBLIC_KEY_E when LMS or LM-OTS type doesn't match. + */ +static int wc_lmots_public_key_check(const LmsParams* params, const byte* pub) +{ + int ret = 0; + word32 type; + + /* Get message hash and height type. */ + ato32(pub, &type); + pub += 4; + /* Compare with parameters. */ + if (type != params->lmsType) { + ret = PUBLIC_KEY_E; + } + if (ret == 0) { + /* Get node hash and Winternitz width type. */ + ato32(pub, &type); + /* Compare with parameters. */ + if (type != params->lmOtsType) { + ret = PUBLIC_KEY_E; + } + } + + return ret; +} + +/* Calculate public key candidate K from signature. + * + * Algorithm 4b: Computing a Public Key Candidate Kc from a Signature, + * Message, Signature Typecode pubtype, and Identifiers I, q + * ... + * 2. Parse sigtype, C, and y from the signature as follows: + * a. sigtype = strTou32(first 4 bytes of signature) + * b. If sigtype is not equal to pubtype, return INVALID. + * ... + * d. C = next n bytes of signature + * e. y[0] = next n bytes of signature + * y[1] = next n bytes of signature + * ... + * y[p-1] = next n bytes of signature + * 3. Compute the string Kc as follows: + * ... + * + * @param [in, out] state LMS state. + * @param [in] pub LMS public key. + * @param [in] msg Message/next private key to verify. + * @param [in] msgSz Length of message in bytes. + * @param [in] sig Signature including type, C and y[0..p-1]. + * @param [out] kc Public key candidate Kc. + */ +static int wc_lmots_calc_kc(LmsState* state, const byte* pub, const byte* msg, + word32 msgSz, const byte* sig, byte* kc) +{ + int ret = 0; + + /* Check signature type. */ + if (XMEMCMP(pub, sig, LMS_TYPE_LEN) != 0) { + ret = SIG_TYPE_E; + } + if (ret == 0) { + /* Get C or randomizer value from signature. */ + const byte* c = sig + LMS_TYPE_LEN; + /* Get array y from signature. */ + const byte* y = c + LMS_MAX_NODE_LEN; + + /* Compute the public key candidate Kc from the signature. */ + ret = wc_lmots_compute_kc_from_sig(state, msg, msgSz, c, y, kc); + } + + return ret; +} + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Generate LM-OTS private key. + * + * Algorithm 5: Computing an LMS Private Key + * But use Appendix A to generate x on the fly. + * PRIV = SEED | I + * + * @param [in] rng Random number generator. + * @param [out] priv Private key data. + */ +static int wc_lmots_make_private_key(WC_RNG* rng, byte* priv) +{ + return wc_RNG_GenerateBlock(rng, priv, LMS_SEED_LEN + LMS_I_LEN); +} + +/* Generate LM-OTS signature. + * + * Algorithm 3: Generating a One-Time Signature From a Private Key and a + * Message + * ... + * 4. Set C to a uniformly random n-byte string + * 5. Compute the array y as follows: + * ... + * 6. Return u32str(type) || C || y[0] || ... || y[p-1] + * + * @param [in, out] state LMS state. + * @param [in] seed Private key seed. + * @param [in] msg Message to be signed. + * @param [in] msgSz Length of message in bytes. + * @param [out] sig Signature buffer. + * @return 0 on success. + */ +static int wc_lmots_sign(LmsState* state, const byte* seed, const byte* msg, + word32 msgSz, byte* sig) +{ + int ret; + byte* buffer = state->buffer; + byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN; + byte* jp = ip + LMS_P_LEN; + byte* tmp = jp + LMS_W_LEN; + byte* sig_c = sig; + + /* I || u32str(q) || u16str(0xFFFD) || ... */ + c16toa(LMS_D_C, ip); + /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || ... */ + *jp = LMS_D_FIXED; + /* I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED */ + XMEMCPY(tmp, seed, LMS_SEED_LEN); + /* C = H(I || u32str(q) || u16str(0xFFFD) || u8str(0xFF) || SEED) + * sig = u32str(type) || C || ... */ +#ifndef WC_LMS_FULL_HASH + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_55(buffer); + ret = wc_lms_hash_block(&state->hash, buffer, sig_c); +#else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, sig_c); +#endif /* !WC_LMS_FULL_HASH */ + + if (ret == 0) { + byte* sig_y = sig_c + LMS_MAX_NODE_LEN; + + /* Compute array y. + * sig = u32str(type) || C || y[0] || ... || y[p-1] */ + ret = wc_lmots_compute_y_from_seed(state, seed, msg, msgSz, sig_c, + sig_y); + } + + return ret; +} +#endif /* WOLFSSL_LMS_VERIFY_ONLY */ + +/*************************************** + * LMS APIs + **************************************/ + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +#ifndef WOLFSSL_WC_LMS_SMALL +/* Load the LMS private state from data. + * + * @param [in] params LMS parameters. + * @param [out] state Private key state. + * @param [in] priv_data Private key data. + */ +static void wc_lms_priv_state_load(const LmsParams* params, LmsPrivState* state, + byte* priv_data) +{ + /* Authentication path data. */ + state->auth_path = priv_data; + priv_data += params->height * LMS_MAX_NODE_LEN; + + /* Stack of nodes. */ + state->stack.stack = priv_data; + priv_data += (params->height + 1) * LMS_MAX_NODE_LEN; + ato32(priv_data, &state->stack.offset); + priv_data += 4; + + /* Cached root nodes. */ + state->root = priv_data; + priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels); + + /* Cached leaf nodes. */ + state->leaf.cache = priv_data; + priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits); + ato32(priv_data, &state->leaf.idx); + priv_data += 4; + ato32(priv_data, &state->leaf.offset); + /* priv_data += 4; */ +} + +/* Store the LMS private state into data. + * + * @param [in] params LMS parameters. + * @param [in] state Private key state. + * @param [in, out] priv_data Private key data. + */ +static void wc_lms_priv_state_store(const LmsParams* params, + LmsPrivState* state, byte* priv_data) +{ + /* Authentication path data. */ + priv_data += params->height * LMS_MAX_NODE_LEN; + + /* Stack of nodes. */ + priv_data += (params->height + 1) * LMS_MAX_NODE_LEN; + c32toa(state->stack.offset, priv_data); + priv_data += 4; + + /* Cached root nodes. */ + priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels); + + /* Cached leaf nodes. */ + priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits); + c32toa(state->leaf.idx, priv_data); + priv_data += 4; + c32toa(state->leaf.offset, priv_data); + /* priv_data += 4; */ +} + +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING +/* Copy LMS private key state. + * + * @param [in] params LMS parameters. + * @param [out] dst LMS private state destination. + * @param [in] src LMS private state source. + */ +static void wc_lms_priv_state_copy(const LmsParams* params, + LmsPrivState* dst, const LmsPrivState* src) +{ + XMEMCPY(dst->auth_path, src->auth_path, LMS_PRIV_STATE_LEN(params->height, + params->rootLevels, params->cacheBits)); + dst->stack.offset = src->stack.offset; + dst->leaf.idx = src->leaf.idx; + dst->leaf.offset = src->leaf.offset; +} +#endif /* !WOLFSSL_LMS_NO_SIGN_SMOOTHING */ +#endif /* !WOLFSSL_WC_LMS_SMALL */ + +/* Calculate the leaf node hash. + * + * Assumes buffer already contains : I + * + * Appendix C. + * ... + * temp = H(I || u32str(r)|| u16str(D_LEAF) || OTS_PUB_HASH[i]) + * ... + * Section 5.3. LMS Public Key + * ... where we denote the public + * key final hash value (namely, the K value computed in Algorithm 1) + * associated with the i-th LM-OTS private key as OTS_PUB_HASH[i], ... + * Algorithm 1: Generating a One-Time Signature Public Key From a + * Private Key + * ... + * K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1]) + * ... + * Therefore: + * OTS_PUB_HASH[i] = H(I || u32str(i) || u16str(D_PBLC) || + * y[0] || ... || y[p-1]) + * + * @param [in, out] state LMS state. + * @param [in] seed Private seed to generate x. + * @param [in] i Index of leaf. + * @param [in] r Leaf hash index. + * @param [out] leaf Leaf node hash. + */ +static int wc_lms_leaf_hash(LmsState* state, const byte* seed, word32 i, + word32 r, byte* leaf) +{ + int ret; + byte* buffer = state->buffer; + byte* rp = buffer + LMS_I_LEN; + byte* dp = rp + LMS_R_LEN; + byte* ots_pub_hash = dp + LMS_D_LEN; + + /* I || u32str(i) || ... */ + c32toa(i, rp); + /* OTS_PUB_HASH[i] = H(I || u32str(i) || u16str(D_PBLC) || + * y[0] || ... || y[p-1]) + */ + ret = wc_lmots_make_public_hash(state, seed, ots_pub_hash); + if (ret == 0) { + /* I || u32str(r) || ... || OTS_PUB_HASH[i] */ + c32toa(r, rp); + /* I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i] */ + c16toa(LMS_D_LEAF, dp); + /* temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i]) */ + #ifndef WC_LMS_FULL_HASH + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_54(buffer); + ret = wc_lms_hash_block(&state->hash, buffer, leaf); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_SEED_HASH_LEN, leaf); + #endif /* !WC_LMS_FULL_HASH */ + } + + return ret; +} + +/* Calculate interior node hash. + * + * Appendix C. n Iterative Algorithm for Computing an LMS Public Key + * Generating an LMS Public Key from an LMS Private Key + * ... + * left_side = pop(data stack); + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + * ... + * Popping the stack is done in the caller. + * + * @param [in, out] state LMS state. + * @param [in] sp Stack pointer to left nodes. + * @param [in] r Node hash index. + * @param [out] node Interior node hash. + */ +static int wc_lms_interior_hash(LmsState* state, byte* sp, word32 r, + byte* node) +{ + byte* buffer = state->buffer; + byte* rp = buffer + LMS_I_LEN; + byte* left = rp + LMS_R_LEN + LMS_D_LEN; + + /* I || u32str(r) || u16str(D_INTR) || ... || temp */ + c32toa(r, rp); + /* left_side = pop(data stack) + * I || u32str(r) || u16str(D_INTR) || left_side || temp */ + XMEMCPY(left, sp, LMS_MAX_NODE_LEN); + /* temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) */ + return wc_lms_hash(&state->hash, buffer, LMS_NODE_HASH_LEN, node); +} + +#ifdef WOLFSSL_WC_LMS_SMALL +/* Computes hash of the Merkle tree and gets the authentication path for q. + * + * Appendix C: An Iterative Algorithm for Computing an LMS Public Key + * for ( i = 0; i < 2^h; i = i + 1 ) { + * r = i + num_lmots_keys; + * temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i]) + * j = i; + * while (j % 2 == 1) { + * r = (r - 1)/2; + * j = (j-1) / 2; + * left_side = pop(data stack); + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + * } + * push temp onto the data stack + * } + * public_key = pop(data stack) + * + * @param [in, out] state LMS state. + * @param [in] id Unique tree identifier, I. + * @param [in] seed Private seed to generate x. + * @param [in] max Count of leaf nodes to calculate. Must be greater + * than q. Must be a power of 2. + * @param [in] q Index for authentication path. + * @param [out] auth_path Authentication path for index. + * @param [out] pub LMS public key. + * @param [out] stack_d Where to store stack data. + * @return 0 on success. + */ +static int wc_lms_treehash(LmsState* state, const byte* id, const byte* seed, + word32 q, byte* auth_path, byte* pub) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte* rp = buffer + LMS_I_LEN; + byte* dp = rp + LMS_R_LEN; + byte* left = dp + LMS_D_LEN; + byte* temp = left + LMS_MAX_NODE_LEN; +#ifdef WOLFSSL_SMALL_STACK + byte* stack = NULL; +#else + byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN]; +#endif /* WOLFSSL_SMALL_STACK */ + byte* sp; + word32 i; + + /* I || ... */ + XMEMCPY(buffer, id, LMS_I_LEN); + +#ifdef WOLFSSL_SMALL_STACK + /* Allocate stack of left side hashes. */ + stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (stack == NULL) { + ret = MEMORY_E; + } +#endif /* WOLFSSL_SMALL_STACK */ + sp = stack; + + /* Compute all nodes requested. */ + for (i = 0; (ret == 0) && (i < ((word32)1 << params->height)); i++) { + word32 j = i; + word16 h = 0; + /* r = i + num_lmots_keys */ + word32 r = i + ((word32)1 << (params->height)); + + /* Calculate leaf node hash. */ + ret = wc_lms_leaf_hash(state, seed, i, r, temp); + + /* Store the node if on the authentication path. */ + if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) { + XMEMCPY(auth_path, temp, LMS_MAX_NODE_LEN); + } + + /* I || ... || u16str(D_INTR) || ... || temp */ + c16toa(LMS_D_INTR, dp); + /* Calculate parent node is we have both left and right. */ + while ((ret == 0) && ((j & 0x1) == 1)) { + /* Get parent node index. r and j are odd. */ + r >>= 1; + j >>= 1; + h++; + + /* Calculate interior node hash. + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + */ + sp -= LMS_MAX_NODE_LEN; + ret = wc_lms_interior_hash(state, sp, r, temp); + + /* Copy out node to authentication path if on path. */ + if ((ret == 0) && (auth_path != NULL) && ((q >> h) ^ 0x1) == j) { + XMEMCPY(auth_path + h * LMS_MAX_NODE_LEN, temp, + LMS_MAX_NODE_LEN); + } + } + /* Push temp onto the data stack. */ + XMEMCPY(sp, temp, LMS_MAX_NODE_LEN); + sp += LMS_MAX_NODE_LEN; + } + + if ((ret == 0) && (pub != NULL)) { + /* Public key, root node, is top of data stack. */ + XMEMCPY(pub, stack, LMS_MAX_NODE_LEN); + } +#ifdef WOLFSSL_SMALL_STACK + XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLFSSL_SMALL_STACK */ + return ret; +} + +/* Compute the LMS public key - root node of tree. + * + * @param [in, out] state LMS state. + * @param [in] id Unique tree identifier, I. + * @param [in] seed Private seed to generate x. + * @param [out] pub LMS public key. + * @return 0 on success. + */ +static int wc_lms_make_public_key(LmsState* state, const byte* id, + const byte* seed, byte* pub) +{ + return wc_lms_treehash(state, id, seed, 0, NULL, pub); +} + +/* Calculate the authentication path. + * + * @param [in, out] state LMS state. + * @param [in] id Public random: I. + * @param [in] seed Private random: SEED. + * @param [in] q Index of leaf. + * @param [out] sig Signature buffer to place authentication path into. + * @param [out] root Root node of tree. + * @return 0 on success. + */ +static int wc_lms_auth_path(LmsState* state, const byte* id, const byte* seed, + word32 q, byte* sig, byte* root) +{ + return wc_lms_treehash(state, id, seed, q, sig, root); +} +#else +/* Computes hash of the Merkle tree and gets the authentication path for q. + * + * Appendix C: An Iterative Algorithm for Computing an LMS Public Key + * for ( i = 0; i < 2^h; i = i + 1 ) { + * r = i + num_lmots_keys; + * temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i]) + * j = i; + * while (j % 2 == 1) { + * r = (r - 1)/2; + * j = (j-1) / 2; + * left_side = pop(data stack); + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + * } + * push temp onto the data stack + * } + * public_key = pop(data stack) + * + * @param [in, out] state LMS state. + * @param [in, out] privState LMS state of the private key. + * @param [in] id Unique tree identifier, I. + * @param [in] seed Private seed to generate x. + * @param [in] q Index for authentication path. + * @return 0 on success. + */ +static int wc_lms_treehash_init(LmsState* state, LmsPrivState* privState, + const byte* id, const byte* seed, word32 q) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte* auth_path = privState->auth_path; + byte* root = privState->root; + HssLeafCache* leaf = &privState->leaf; + byte* rp = buffer + LMS_I_LEN; + byte* dp = rp + LMS_R_LEN; + byte* left = dp + LMS_D_LEN; + byte* temp = left + LMS_MAX_NODE_LEN; +#ifdef WOLFSSL_SMALL_STACK + byte* stack = NULL; +#else + byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN]; +#endif /* WOLFSSL_SMALL_STACK */ + word32 spi = 0; + word32 i; + word32 max_h = (word32)1 << params->height; + word32 max_cb = (word32)1 << params->cacheBits; + + privState->stack.offset = 0; + /* Reset the cached stack. */ + leaf->offset = 0; + leaf->idx = q; + if ((q + max_cb) > max_h) { + leaf->idx = max_h - max_cb; + } + + /* I || ... */ + XMEMCPY(buffer, id, LMS_I_LEN); + +#ifdef WOLFSSL_SMALL_STACK + /* Allocate stack of left side hashes. */ + stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (stack == NULL) { + ret = MEMORY_E; + } +#endif /* WOLFSSL_SMALL_STACK */ + + /* Compute all nodes requested. */ + for (i = 0; (ret == 0) && (i < max_h); i++) { + word32 j = i; + word16 h = 0; + /* r = i + num_lmots_keys */ + word32 r = i + max_h; + + /* Calculate leaf node hash. */ + ret = wc_lms_leaf_hash(state, seed, i, r, temp); + + /* Cache leaf node if in range. */ + if ((ret == 0) && (i >= leaf->idx) && (i < leaf->idx + max_cb)) { + XMEMCPY(leaf->cache + i * LMS_MAX_NODE_LEN, temp, LMS_MAX_NODE_LEN); + } + + /* Store the node if on the authentication path. */ + if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) { + XMEMCPY(auth_path, temp, LMS_MAX_NODE_LEN); + } + + /* I || ... || u16str(D_INTR) || ... || temp */ + c16toa(LMS_D_INTR, dp); + /* Calculate parent node is we have both left and right. */ + while ((ret == 0) && ((j & 0x1) == 1)) { + /* Get parent node index. r and j are odd. */ + r >>= 1; + j >>= 1; + h++; + + /* Calculate interior node hash. + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + */ + spi -= LMS_MAX_NODE_LEN; + ret = wc_lms_interior_hash(state, stack + spi, r, temp); -#ifdef WOLFSSL_HAVE_LMS - #error "Contact wolfSSL to get the implementation of this file" + /* Copy out top root nodes. */ + if ((h > params->height - params->rootLevels) && + ((i >> (h-1)) != ((i + 1) >> (h - 1)))) { + int off = (1 << (params->height - h)) + (i >> h) - 1; + XMEMCPY(root + off * LMS_MAX_NODE_LEN, temp, LMS_MAX_NODE_LEN); + } + + /* Copy out node to authentication path if on path. */ + if ((ret == 0) && (auth_path != NULL) && ((q >> h) ^ 0x1) == j) { + XMEMCPY(auth_path + h * LMS_MAX_NODE_LEN, temp, + LMS_MAX_NODE_LEN); + } + } + /* Push temp onto the data stack. */ + XMEMCPY(stack + spi, temp, LMS_MAX_NODE_LEN); + spi += LMS_MAX_NODE_LEN; + + if (i == q - 1) { + XMEMCPY(privState->stack.stack, stack, spi); + privState->stack.offset = spi; + } + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLFSSL_SMALL_STACK */ + return ret; +} + +/* Computes hash of the Merkle tree and gets the authentication path for q. + * + * Appendix C: An Iterative Algorithm for Computing an LMS Public Key + * for ( i = 0; i < 2^h; i = i + 1 ) { + * r = i + num_lmots_keys; + * temp = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[i]) + * j = i; + * while (j % 2 == 1) { + * r = (r - 1)/2; + * j = (j-1) / 2; + * left_side = pop(data stack); + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || temp) + * } + * push temp onto the data stack + * } + * public_key = pop(data stack) + * + * @param [in, out] state LMS state. + * @param [in, out] privState LMS state of the private key. + * @param [in] id Unique tree identifier, I. + * @param [in] seed Private seed to generate x. + * @param [in] min_idx Minimum leaf index to process. + * @param [in] max_idx Maximum leaf index to process. + * @param [in] q Index for authentication path. + * @param [in] useRoot Whether to use nodes from root cache. + * @return 0 on success. + */ +static int wc_lms_treehash_update(LmsState* state, LmsPrivState* privState, + const byte* id, const byte* seed, word32 min_idx, word32 max_idx, word32 q, + int useRoot) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte* auth_path = privState->auth_path; + LmsStack* stackCache = &privState->stack; + HssLeafCache* leaf = &privState->leaf; + byte* rp = buffer + LMS_I_LEN; + byte* dp = rp + LMS_R_LEN; + byte* left = dp + LMS_D_LEN; + byte* temp = left + LMS_MAX_NODE_LEN; +#ifdef WOLFSSL_SMALL_STACK + byte* stack = NULL; +#else + byte stack[(LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN]; +#endif /* WOLFSSL_SMALL_STACK */ + byte* sp; + word32 max_cb = (word32)1 << params->cacheBits; + word32 i; + + /* I || ... */ + XMEMCPY(buffer, id, LMS_I_LEN); + +#ifdef WOLFSSL_SMALL_STACK + /* Allocate stack of left side hashes. */ + stack = XMALLOC((params->height + 1) * LMS_MAX_NODE_LEN, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (stack == NULL) { + ret = MEMORY_E; + } +#endif /* WOLFSSL_SMALL_STACK */ + + /* Public key, root node, is top of data stack. */ + XMEMCPY(stack, stackCache->stack, params->height * LMS_MAX_NODE_LEN); + sp = stack + stackCache->offset; + + /* Compute all nodes requested. */ + for (i = min_idx; (ret == 0) && (i <= max_idx); i++) { + word32 j = i; + word16 h = 0; + /* r = i + num_lmots_keys */ + word32 r = i + ((word32)1 << (params->height)); + + if ((i >= leaf->idx) && (i < leaf->idx + max_cb)) { + /* Calculate offset of node in cache. */ + word32 off = ((i - (leaf->idx + max_cb) + leaf->offset) % max_cb) * + LMS_MAX_NODE_LEN; + /* Copy cached node into working buffer. */ + XMEMCPY(temp, leaf->cache + off, LMS_MAX_NODE_LEN); + /* I || u32str(i) || ... */ + c32toa(i, rp); + } + else { + /* Calculate leaf node hash. */ + ret = wc_lms_leaf_hash(state, seed, i, r, temp); + + /* Check if this is at the end of the cache and not beyond q plus + * the number of leaf nodes. */ + if ((i == leaf->idx + max_cb) && (i < (q + max_cb))) { + /* Copy working node into cache over old first node. */ + XMEMCPY(leaf->cache + leaf->offset * LMS_MAX_NODE_LEN, temp, + LMS_MAX_NODE_LEN); + /* Increase start index as first node replaced. */ + leaf->idx++; + /* Update offset of first leaf node. */ + leaf->offset = (leaf->offset + 1) & (max_cb - 1); + } + } + + /* Store the node if on the authentication path. */ + if ((ret == 0) && ((q ^ 0x1) == i)) { + XMEMCPY(auth_path, temp, LMS_MAX_NODE_LEN); + } + + /* I || ... || u16str(D_INTR) || ... || temp */ + c16toa(LMS_D_INTR, dp); + /* Calculate parent node if we have both left and right. */ + while ((ret == 0) && ((j & 0x1) == 1)) { + /* Get parent node index. r and j are odd. */ + r >>= 1; + j >>= 1; + h++; + + sp -= LMS_MAX_NODE_LEN; + if (useRoot && (h > params->height - params->rootLevels) && + (h <= params->height)) { + /* Calculate offset of cached root node. */ + word32 off = ((word32)1U << (params->height - h)) + + (i >> h) - 1; + XMEMCPY(temp, privState->root + (off * LMS_MAX_NODE_LEN), + LMS_MAX_NODE_LEN); + } + else { + /* Calculate interior node hash. + * temp = H(I || u32str(r) || u16str(D_INTR) || left_side || + * temp) + */ + ret = wc_lms_interior_hash(state, sp, r, temp); + } + + /* Copy out top root nodes. */ + if ((ret == 0) && (q == 0) && (!useRoot) && + (h > params->height - params->rootLevels) && + ((i >> (h-1)) != ((i + 1) >> (h - 1)))) { + int off = (1 << (params->height - h)) + (i >> h) - 1; + XMEMCPY(privState->root + off * LMS_MAX_NODE_LEN, temp, + LMS_MAX_NODE_LEN); + } + + /* Copy out node to authentication path if on path. */ + if ((ret == 0) && (((q >> h) ^ 0x1) == j)) { + XMEMCPY(auth_path + h * LMS_MAX_NODE_LEN, temp, + LMS_MAX_NODE_LEN); + } + } + if (ret == 0) { + /* Push temp onto the data stack. */ + XMEMCPY(sp, temp, LMS_MAX_NODE_LEN); + sp += LMS_MAX_NODE_LEN; + + /* Save stack after updating first node. */ + if (i == min_idx) { + /* Copy stack back. */ + stackCache->offset = (word32)((size_t)sp - (size_t)stack); + XMEMCPY(stackCache->stack, stack, stackCache->offset); + } + } + } + + if (!useRoot) { + /* Copy stack back. */ + XMEMCPY(stackCache->stack, stack, params->height * LMS_MAX_NODE_LEN); + stackCache->offset = (word32)((size_t)sp - (size_t)stack); + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLFSSL_SMALL_STACK */ + return ret; +} +#endif /* WOLFSSL_WC_LMS_SMALL */ + +/* Sign message using LMS. + * + * Appendix D. Method for Deriving Authentication Path for a Signature. + * Generating an LMS Signature + * ... + * 3. Create the LM-OTS signature for the message: + * ots_signature = lmots_sign(message, LMS_PRIV[q]) + * 4. Compute the array path as follows: + * ... + * 5. S = u32str(q) || ots_signature || u32str(type) || + * path[0] || path[1] || ... || path[h-1] + * ... + * path[] added by caller as it can come from cache. + * + * @param [in, out] state LMS state. + * @param [in] priv LMS private key. + * @param [in] msg Message/public key to sign. + * @param [in] msgSz Length of message in bytes. + * @param [out] sig LMS signature. + * @return 0 on success. + */ +static int wc_lms_sign(LmsState* state, const byte* priv, const byte* msg, + word32 msgSz, byte* sig) +{ + int ret; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte* s = sig; + const byte* priv_q = priv; + const byte* priv_seed = priv_q + LMS_Q_LEN; + const byte* priv_i = priv_seed + LMS_SEED_LEN; + + /* Setup for hashing: I || Q */ + XMEMCPY(buffer, priv_i, LMS_I_LEN); + XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN); + + /* Copy q from private key. + * S = u32str(q) || ... */ + XMEMCPY(s, priv_q, LMS_Q_LEN); + s += LMS_Q_LEN; + + /* ots_signature = sig = u32str(type) || ... */ + c32toa(state->params->lmOtsType, s); + s += LMS_TYPE_LEN; + /* Sign this level. + * S = u32str(q) || ots_signature || ... */ + ret = wc_lmots_sign(state, priv_seed, msg, msgSz, s); + if (ret == 0) { + /* Skip over ots_signature. */ + s += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN; + /* S = u32str(q) || ots_signature || u32str(type) || ... */ + c32toa(params->lmsType, s); + } + + return ret; +} + +#if !defined(WOLFSSL_WC_LMS_SMALL) && !defined(WOLFSSL_LMS_NO_SIG_CACHE) +/* Copy in the cached signature data. + * + * @param [in] params LMS parameters. + * @param [in] y y cache. + * @param [in] priv Private key data. + * @param [out] sig Signature data. + */ +static void wc_lms_sig_copy(const LmsParams* params, const byte* y, + const byte* priv, byte* sig) +{ + /* Put in q. */ + XMEMCPY(sig, priv, LMS_Q_LEN); + sig += LMS_Q_LEN; + /* S = u32str(q) || ... */ + c32toa(params->lmOtsType, sig); + sig += LMS_TYPE_LEN; + /* S = u32str(q) || ots_signature || ... */ + XMEMCPY(sig, y, LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN); + sig += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN; + /* S = u32str(q) || ots_signature || u32str(type) || ... */ + c32toa(params->lmsType, sig); +} +#endif /* !WOLFSSL_WC_LMS_SMALL && !WOLFSSL_LMS_NO_SIG_CACHE */ +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +/* Compute the root node of the LMS tree. + * + * Algorithm 6a: Computing an LMS Public Key Candidate from a Signature, + * Message, Identifier, and Algorithm Typecodes + * ... + * 4. Compute the candidate LMS root value Tc as follows: + * node_num = 2^h + q + * tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc) + * i = 0 + * while (node_num > 1) { + * if (node_num is odd): + * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp) + * else: + * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i]) + * node_num = node_num/2 + * i = i + 1 + * } + * Tc = tmp + * 5. Return Tc. + * + * @param [in, out] state LMS state. + * @param [in] q Index of node. + * @param [in] kc K candidate. + * @param [in] path Authentication path from signature. + * @param [out] tc T candidate. + * @return 0 on success. + */ +static int wc_lms_compute_root(LmsState* state, word32 q, const byte* kc, + const byte* path, byte* tc) +{ + int ret; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte* rp = buffer + LMS_I_LEN; + byte* ip = rp + LMS_Q_LEN; + byte* node = ip + LMS_P_LEN; + byte* b[2][2] = { { node, node + LMS_MAX_NODE_LEN }, + { node + LMS_MAX_NODE_LEN, node } }; + /* node_num = 2^h + q */ + word32 r = (1 << params->height) + q; + + /* tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc) */ + c32toa(r, rp); + c16toa(LMS_D_LEAF, ip); + XMEMCPY(node, kc, LMS_MAX_NODE_LEN); + /* Put tmp into offset required for first iteration. */ +#ifndef WC_LMS_FULL_HASH + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_54(buffer); + ret = wc_lms_hash_block(&state->hash, buffer, b[r & 1][0]); +#else + ret = wc_lms_hash(&state->hash, buffer, LMS_SEED_HASH_LEN, b[r & 1][0]); +#endif /* !WC_LMS_FULL_HASH */ + + if (ret == 0) { + int i; + + /* I||...||u16str(D_INT)||... */ + c16toa(LMS_D_INTR, ip); + + /* Do all but last height. */ + for (i = 0; (ret == 0) && (i < params->height - 1); i++) { + /* Put path into offset required. */ + XMEMCPY(b[r & 1][1], path, LMS_MAX_NODE_LEN); + path += LMS_MAX_NODE_LEN; + + /* node_num = node_num / 2 */ + r >>= 1; + /* H(...||u32str(node_num/2)||..) */ + c32toa(r, rp); + /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp) or + * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i]) + * Put tmp result into offset required for next iteration. */ + ret = wc_lms_hash(&state->hash, buffer, LMS_NODE_HASH_LEN, + b[r & 1][0]); + } + if (ret == 0) { + /* Last height. */ + /* Put path into offset required. */ + XMEMCPY(b[r & 1][1], path, LMS_MAX_NODE_LEN); + /* node_num = node_num / 2 */ + r >>= 1; + /* H(...||u32str(node_num/2)||..) */ + c32toa(r, rp); + /* tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp) or + * tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i]) + * Put tmp result into Tc.*/ + ret = wc_lms_hash(&state->hash, buffer, LMS_NODE_HASH_LEN, tc); + } + } + + return ret; +} + +/* LMS verify message using public key and signature. + * + * Algorithm 6a: Computing an LMS Public Key Candidate from a Signature, + * Message, Identifier, and Algorithm Typecodes + * ... + * 2. Parse sigtype, q, lmots_signature, and path from the signature + * as follows: + * a. q = strTou32(first 4 bytes of signature) + * ... + * e. lmots_signature = bytes 4 through 7 + n * (p + 1) + * of signature + * ... + * j. Set path as follows: + * path[0] = next m bytes of signature + * path[1] = next m bytes of signature + * ... + * path[h-1] = next m bytes of signature + * 3. Kc = candidate public key computed by applying Algorithm 4b + * to the signature lmots_signature, the message, and the + * identifiers I, q + * 4. Compute the candidate LMS root value Tc as follows: + * ... + * 5. Return Tc + * Algorithm 6: LMS Signature Verification + * ... + * 3. Compute the LMS Public Key Candidate Tc from the signature, + * message, identifier, pubtype, and ots_typecode, using + * Algorithm 6a. + * 4. If Tc is equal to T[1], return VALID; otherwise, return INVALID. + * + * @param [in, out] state LMS state. + * @param [in] pub LMS public key. + * @param [in] msg Message/public key to verify. + * @param [in] msgSz Length of message in bytes. + * @param [in] sig LMS signature. + */ +static int wc_lms_verify(LmsState* state, const byte* pub, const byte* msg, + word32 msgSz, const byte* sig) +{ + int ret; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + const byte* pub_i = pub + LMS_TYPE_LEN + LMS_TYPE_LEN; + const byte* pub_k = pub_i + LMS_I_LEN; + const byte* sig_q = sig; + byte tc[LMS_MAX_NODE_LEN]; + byte* kc = tc; + + /* Algorithm 6. Step 3. */ + /* Check the public key LMS type matches parameters. */ + ret = wc_lmots_public_key_check(params, pub); + if (ret == 0) { + /* Algorithm 6a. Step 2.e. */ + const byte* sig_lmots = sig + LMS_Q_LEN; + + /* Setup buffer with I || Q. */ + XMEMCPY(buffer, pub_i, LMS_I_LEN); + XMEMCPY(buffer + LMS_I_LEN, sig_q, LMS_Q_LEN); + + /* Algorithm 6a. Step 3. */ + ret = wc_lmots_calc_kc(state, pub + LMS_TYPE_LEN, msg, msgSz, + sig_lmots, kc); + } + if (ret == 0) { + /* Algorithm 6a. Step 2.j. */ + const byte* sig_path = sig + LMS_Q_LEN + LMS_TYPE_LEN + + LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN + LMS_TYPE_LEN; + word32 q; + + /* Algorithm 6a. Step 2.a. */ + ato32(sig_q, &q); + + /* Algorithm 6a. Steps 4-5. */ + ret = wc_lms_compute_root(state, q, kc, sig_path, tc); + } + /* Algorithm 6. Step 4. */ + if ((ret == 0) && (XMEMCMP(pub_k, tc, LMS_MAX_NODE_LEN) != 0)) { + ret = SIG_VERIFY_E; + } + + return ret; +} + +/*************************************** + * HSS APIs + **************************************/ + +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Derive the seed and i for child. + * + * @param [in, out] state LMS state. + * @param [in] id Parent's I. + * @param [in] seed Parent's SEED. + * @param [in] q Parent's q. + * @param [out] seed_i Derived SEED and I. + * @return 0 on success. + */ +static int wc_hss_derive_seed_i(LmsState* state, const byte* id, + const byte* seed, const byte* q, byte* seed_i) +{ + int ret = 0; + byte buffer[WC_SHA256_BLOCK_SIZE]; + byte* idp = buffer; + byte* qp = idp + LMS_I_LEN; + byte* ip = qp + LMS_Q_LEN; + byte* jp = ip + LMS_P_LEN; + byte* tmp = jp + LMS_W_LEN; + + /* parent's I || ... */ + XMEMCPY(idp, id, LMS_I_LEN); + /* parent's I || q || ... */ + XMEMCPY(qp, q, LMS_Q_LEN); + /* parent's I || q || D_CHILD_SEED || ... */ + c16toa(LMS_D_CHILD_SEED, ip); + /* parent's I || q || D_CHILD_SEED || D_FIXED || ... */ + *jp = LMS_D_FIXED; + /* parent's I || q || D_CHILD_SEED || D_FIXED || parent's SEED */ + XMEMCPY(tmp, seed, LMS_SEED_LEN); + /* SEED = H(parent's I || q || D_CHILD_SEED || D_FIXED || parent's SEED) */ +#ifndef WC_LMS_FULL_HASH + /* Put in padding for final block. */ + LMS_SHA256_SET_LEN_55(buffer); + ret = wc_lms_hash_block(&state->hash, buffer, seed_i); +#else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, seed_i); +#endif /* !WC_LMS_FULL_HASH */ + + if (ret == 0) { + seed_i += LMS_SEED_LEN; + /* parent's I || q || D_CHILD_I || D_FIXED || parent's SEED */ + c16toa(LMS_D_CHILD_I, ip); + /* I = H(parent's I || q || D_CHILD_I || D_FIXED || parent's SEED) */ + #ifndef WC_LMS_FULL_HASH + ret = wc_lms_hash_block(&state->hash, buffer, tmp); + #else + ret = wc_lms_hash(&state->hash, buffer, LMS_HASH_BUFFER_LEN, tmp); + #endif /* !WC_LMS_FULL_HASH */ + /* Copy part of hash as new I into private key. */ + XMEMCPY(seed_i, tmp, LMS_I_LEN); + } + + return ret; +} + +/* Get q, index, of leaf at the specified level. */ +#define LMS_Q_AT_LEVEL(q, ls, l, h) \ + (w64GetLow32(w64ShiftRight((q), (((ls) - 1 - (l)) * (h)))) & \ + (((word32)1 << (h)) - 1)) + +/* Expand the seed and I for further levels and set q for each level. + * + * @param [in, out] state LMS state. + * @param [in, out] priv Private key for use in signing. + * @param [in] priv_raw Private key read. + * @param [in] inc Whether this is an incremental expansion. + * @return 0 on success. + */ +static int wc_hss_expand_private_key(LmsState* state, byte* priv, + const byte* priv_raw, int inc) +{ + const LmsParams* params = state->params; + int ret = 0; + w64wrapper q; + w64wrapper qm1; + word32 q32; + byte* priv_q; + byte* priv_seed_i; + int i; + + /* Get the 64-bit q value from the raw private key. */ + ato64(priv_raw, &q); + /* Step over q and parameter set. */ + priv_raw += HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN; + + /* Get q of highest level. */ + q32 = LMS_Q_AT_LEVEL(q, params->levels, 0, params->height); + /* Set q of highest tree. */ + c32toa(q32, priv); + + /* Incremental expansion needs q-1. */ + if (inc) { + /* Calculate q-1 for comparison. */ + qm1 = q; + w64Decrement(&qm1); + } + else { + /* Copy out SEED and I into private key. */ + XMEMCPY(priv + LMS_Q_LEN, priv_raw, LMS_SEED_I_LEN); + } + + /* Compute SEED and I for rest of levels. */ + for (i = 1; (ret == 0) && (i < params->levels); i++) { + /* Don't skip calculating SEED and I. */ + int skip = 0; + + /* Incremental means q, SEED and I already present if q unchanged. */ + if (inc) { + /* Calculate previous levels q for previous 64-bit q value. */ + word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, i - 1, + params->height); + /* Same q at previous level means no need to re-compute. */ + if (q32 == qm1_32) { + /* Do skip calculating SEED and I. */ + skip = 1; + } + } + + /* Get pointers into private q to write q and seed + I. */ + priv_q = priv; + priv += LMS_Q_LEN; + priv_seed_i = priv; + priv += LMS_SEED_I_LEN; + + /* Get q for level from 64-bit composite. */ + q32 = w64GetLow32(w64ShiftRight(q, (params->levels - 1 - i) * + params->height)) & (((word32)1 << params->height) - 1); + /* Set q of tree. */ + c32toa(q32, priv); + + if (!skip) { + /* Derive SEED and I into private key. */ + ret = wc_hss_derive_seed_i(state, priv_seed_i + LMS_SEED_LEN, + priv_seed_i, priv_q, priv + LMS_Q_LEN); + } + } + + return ret; +} + +#ifndef WOLFSSL_WC_LMS_SMALL +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING +/* Initialize the next subtree. + * + * @param [in] state LMS state. + * @param [in] privState LMS private state. + * @param [in] curr Current private key. + * @param [in] priv Next private key. + * @param [in] q q for this level. + * @return 0 on success. + */ +static int wc_lms_next_subtree_init(LmsState* state, LmsPrivState* privState, + byte* curr, byte* priv, word32 q) +{ + int ret; + const LmsParams* params = state->params; + byte* priv_q; + byte* priv_seed; + byte* priv_i; + word32 pq; + + priv_q = priv; + priv += LMS_Q_LEN; + priv_seed = curr + LMS_Q_LEN; + priv += LMS_SEED_LEN; + priv_i = curr + LMS_Q_LEN + LMS_SEED_LEN; + priv += LMS_I_LEN; + + ato32(curr, &pq); + pq = (pq + 1) & ((1 << params->height) - 1); + c32toa(pq, priv_q); + + privState->stack.offset = 0; + privState->leaf.idx = (word32)-(1 << params->cacheBits); + privState->leaf.offset = 0; + + /* Derive SEED and I for next tree. */ + ret = wc_hss_derive_seed_i(state, priv_i, priv_seed, priv_q, + priv + LMS_Q_LEN); + if (ret == 0) { + /* Update treehash for first leaf. */ + ret = wc_lms_treehash_update(state, privState, + priv + LMS_Q_LEN + LMS_SEED_LEN, priv + LMS_Q_LEN, 0, q, 0, 0); + } + + return ret; +} + +/* Increment count on next subtree. + * + * @param [in] state LMS state. + * @param [in] priv_key HSS private key. + * @param [in] q64 64-bit q for all levels. + * @return 0 on success. + */ +static int wc_hss_next_subtree_inc(LmsState* state, HssPrivKey* priv_key, + w64wrapper q64) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* curr = priv_key->priv; + byte* priv = priv_key->next_priv; + int i; + w64wrapper p64 = q64; + byte tmp_priv[LMS_PRIV_LEN]; + int use_tmp = 0; + int lastQMax = 0; + w64wrapper p64_hi; + w64wrapper q64_hi; + + /* Get previous index. */ + w64Decrement(&p64); + /* Get index of previous and current parent. */ + p64_hi = w64ShiftRight(p64, (params->levels - 1) * params->height); + q64_hi = w64ShiftRight(q64, (params->levels - 1) * params->height); + for (i = 1; (ret == 0) && (i < params->levels); i++) { + word32 qc; + w64wrapper cp64_hi; + w64wrapper cq64_hi; + + /* Get index of previous and current child. */ + cp64_hi = w64ShiftRight(p64, (params->levels - i - 1) * params->height); + cq64_hi = w64ShiftRight(q64, (params->levels - i - 1) * params->height); + /* Get the q for the child. */ + ato32(curr + LMS_PRIV_LEN, &qc); + + /* Compare index of parent node with previous value. */ + if (w64LT(p64_hi, q64_hi)) { + wc_lms_priv_state_copy(params, &priv_key->state[i], + &priv_key->next_state[i-1]); + ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1], + use_tmp ? tmp_priv : curr, priv, 0); + use_tmp = 0; + } + /* Check whether the child is in a new subtree. */ + else if ((qc == ((word32)1 << params->height) - 1) && + w64LT(cp64_hi, cq64_hi)) { + XMEMSET(tmp_priv, 0, LMS_Q_LEN); + /* Check whether the node at the previous level is also in a new + * subtree. */ + if (lastQMax) { + /* Calculate new SEED and I based on new subtree. */ + ret = wc_hss_derive_seed_i(state, + priv + LMS_Q_LEN + LMS_SEED_LEN, priv + LMS_Q_LEN, tmp_priv, + tmp_priv + LMS_Q_LEN); + } + else { + /* Calculate new SEED and I based on parent. */ + ret = wc_hss_derive_seed_i(state, + curr + LMS_Q_LEN + LMS_SEED_LEN, curr + LMS_Q_LEN, priv, + tmp_priv + LMS_Q_LEN); + } + /* Values not stored so note that they are in temporary. */ + use_tmp = 1; + + /* Set the the q. */ + XMEMCPY(tmp_priv, curr + LMS_PRIV_LEN, LMS_Q_LEN); + } + + lastQMax = (qc == ((word32)1 << params->height) - 1); + curr += LMS_PRIV_LEN; + priv += LMS_PRIV_LEN; + p64_hi = cp64_hi; + q64_hi = cq64_hi; + } + + return ret; +} + +/* Initialize the next subtree for each level bar the highest. + * + * @param [in, out] state LMS state. + * @param [out] priv_key Private key data. + * @return 0 on success. + */ +static int wc_hss_next_subtrees_init(LmsState* state, HssPrivKey* priv_key) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* curr = priv_key->priv; + byte* priv = priv_key->next_priv; + int i; + + XMEMCPY(priv, curr, LMS_PRIV_LEN); + wc_lms_idx_inc(priv, LMS_Q_LEN); + + for (i = 1; (ret == 0) && (i < params->levels); i++) { + word32 q; + + ato32(curr + LMS_PRIV_LEN, &q); + ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1], + curr, priv, q); + + curr += LMS_PRIV_LEN; + priv += LMS_PRIV_LEN; + } + + return ret; +} #endif + +/* Update the authentication path and caches. + * + * @param [in, out] state LMS state. + * @param [in, out] priv_key Private key information. + * @param [in] levels Number of level to start at. + * @param [out] pub_root Public root. + * @return 0 on success. + */ +static int wc_hss_init_auth_path(LmsState* state, HssPrivKey* priv_key, + byte* pub_root) +{ + int ret = 0; + int levels = state->params->levels; + byte* priv = priv_key->priv + LMS_PRIV_LEN * (levels - 1); + int l; + + for (l = levels - 1; (ret == 0) && (l >= 0); l--) { + word32 q; + const byte* priv_q = priv; + const byte* priv_seed = priv_q + LMS_Q_LEN; + const byte* priv_i = priv_seed + LMS_SEED_LEN; + + /* Get current q for tree at level. */ + ato32(priv_q, &q); + /* Set cache start to a value that indicates no numbers available. */ + ret = wc_lms_treehash_init(state, &priv_key->state[l], priv_i, + priv_seed, q); + + /* Move onto next level's data. */ + priv -= LMS_PRIV_LEN; + } + + if ((ret == 0) && (pub_root != NULL)) { + XMEMCPY(pub_root, priv_key->state[0].root, LMS_MAX_NODE_LEN); + } + + return ret; +} + +/* Calculate the corresponding authentication path index at that height. + * + * @param [in] i Leaf node index. + * @param [in] h Height to calculate for. + * @return Index on authentication path. + */ +#define LMS_AUTH_PATH_IDX(i, h) \ + (((i) ^ ((word32)1U << (h))) | (((word32)1U << (h)) - 1)) + +/* Update the authentication path. + * + * @param [in, out] state LMS state. + * @param [in, out] priv_key Private key information. + * @param [in] levels Number of level to start at. + * @return 0 on success. + */ +static int wc_hss_update_auth_path(LmsState* state, HssPrivKey* priv_key, + byte* priv_raw, int levels) +{ + const LmsParams* params = state->params; + int ret = 0; + byte* priv = priv_key->priv + LMS_PRIV_LEN * (levels - 1); + int i; +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + w64wrapper q64; +#endif + + (void)priv_raw; +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + ato64(priv_raw, &q64); +#endif + + for (i = levels - 1; (ret == 0) && (i >= 0); i--) { + word32 q; + const byte* priv_q = priv; + const byte* priv_seed = priv_q + LMS_Q_LEN; + const byte* priv_i = priv_seed + LMS_SEED_LEN; + LmsPrivState* privState = &priv_key->state[i]; + + /* Get q for tree at level. */ + ato32(priv_q, &q); + #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + + if ((levels > 1) && (i == levels - 1) && (q == 0)) { + /* New sub-tree. */ + ret = wc_hss_next_subtree_inc(state, priv_key, q64); + } + if ((ret == 0) && (q != 0)) + #else + if (q == 0) { + /* New sub-tree. */ + ret = wc_lms_treehash_init(state, privState, priv_i, priv_seed, 0); + } + else + #endif + { + word32 maxq = q - 1; + int h; + int maxh = params->height; + + /* Check each index at each height needed for the auth path. */ + for (h = 0; (h < maxh) && (h <= maxh - params->rootLevels); h++) { + /* Calculate the index for current q and q-1. */ + word32 qa = LMS_AUTH_PATH_IDX(q, h); + word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h); + /* If different then needs to be computed so keep highest. */ + if ((qa != qm1a) && (qa > maxq)) { + maxq = qa; + } + } + for (; h < maxh; h++) { + /* Calculate the index for current q and q-1. */ + word32 qa = LMS_AUTH_PATH_IDX(q, h); + word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h); + /* If different then copy in cached hash. */ + if ((qa != qm1a) && (qa > maxq)) { + int off = (1 << (params->height - h)) + (qa >> h) - 1; + XMEMCPY(privState->auth_path + h * LMS_MAX_NODE_LEN, + privState->root + off * LMS_MAX_NODE_LEN, + LMS_MAX_NODE_LEN); + } + } + /* Update the treehash and calculate the extra indices for + * authentication path. */ + ret = wc_lms_treehash_update(state, privState, priv_i, priv_seed, + q - 1, maxq, q, 1); + #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + if ((ret == 0) && (i > 0)) { + w64wrapper tmp64 = w64ShiftRight(q64, + (levels - i) * params->height); + w64Increment(&tmp64); + tmp64 = w64ShiftLeft(tmp64, 64 - (i * params->height)); + if (!w64IsZero(tmp64)) { + priv_seed = priv_key->next_priv + i * LMS_PRIV_LEN + + LMS_Q_LEN; + priv_i = priv_seed + LMS_SEED_LEN; + privState = &priv_key->next_state[i - 1]; + + ret = wc_lms_treehash_update(state, privState, priv_i, + priv_seed, q, q, 0, 0); + } + } + #endif + break; + } + + /* Move onto next level's data. */ + priv -= LMS_PRIV_LEN; + } + + return ret; +} + +#if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1) +/* Pre-sign for current q so that it isn't needed in signing. + * + * @param [in, out] state LMS state. + * @param [in, out] priv_key Private key. + */ +static int wc_hss_presign(LmsState* state, HssPrivKey* priv_key) +{ + int ret = 0; + const LmsParams* params = state->params; + byte* buffer = state->buffer; + byte pub[LMS_PUBKEY_LEN]; + byte* root = pub + LMS_PUBKEY_LEN - LMS_MAX_NODE_LEN; + byte* priv = priv_key->priv; + int i; + + for (i = params->levels - 2; i >= 0; i--) { + const byte* p = priv + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN); + const byte* priv_q = p; + const byte* priv_seed = priv_q + LMS_Q_LEN; + const byte* priv_i = priv_seed + LMS_SEED_LEN; + + /* ... || T(1) */ + XMEMCPY(root, priv_key->state[i + 1].root, LMS_MAX_NODE_LEN); + /* u32str(type) || u32str(otstype) || I || T(1) */ + p = priv + (i + 1) * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN); + wc_lmots_public_key_encode(params, p, pub); + + /* Setup for hashing: I || Q || ... */ + XMEMCPY(buffer, priv_i, LMS_I_LEN); + XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN); + + /* LM-OTS Sign this level. */ + ret = wc_lmots_sign(state, priv_seed, pub, LMS_PUBKEY_LEN, + priv_key->y + i * LMS_PRIV_Y_TREE_LEN(params->p)); + } + + return ret; +} +#endif /* !WOLFSSL_LMS_NO_SIG_CACHE && LMS_MAX_LEVELS > 1 */ +#endif /* !WOLFSSL_WC_LMS_SMALL */ + +/* Load the private key data into HSS private key structure. + * + * @param [in] params LMS parameters. + * @param [in, out] key HSS private key. + * @param [in] priv_data Private key data. + */ +static void wc_hss_priv_data_load(const LmsParams* params, HssPrivKey* key, + byte* priv_data) +{ +#ifndef WOLFSSL_WC_LMS_SMALL + int l; +#endif + + /* Expanded private keys. */ + key->priv = priv_data; + priv_data += LMS_PRIV_KEY_LEN(params->levels); + +#ifndef WOLFSSL_WC_LMS_SMALL + for (l = 0; l < params->levels; l++) { + /* Caches for subtree. */ + wc_lms_priv_state_load(params, &key->state[l], priv_data); + priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels, + params->cacheBits); + } + +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + /* Next subtree's expanded private keys. */ + key->next_priv = priv_data; + priv_data += LMS_PRIV_KEY_LEN(params->levels); + for (l = 0; l < params->levels - 1; l++) { + /* Next subtree's caches. */ + wc_lms_priv_state_load(params, &key->next_state[l], priv_data); + priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels, + params->cacheBits); + } +#endif /* WOLFSSL_LMS_NO_SIGN_SMOOTHING */ + +#ifndef WOLFSSL_LMS_NO_SIG_CACHE + /* Signature cache. */ + key->y = priv_data; +#endif /* WOLFSSL_LMS_NO_SIG_CACHE */ +#endif /* WOLFSSL_WC_LMS_SMALL */ +} + +#ifndef WOLFSSL_WC_LMS_SMALL +/* Store the private key data from HSS private key structure. + * + * @param [in] params LMS parameters. + * @param [in] key HSS private key. + * @param [in, out] priv_data Private key data. + */ +static void wc_hss_priv_data_store(const LmsParams* params, HssPrivKey* key, + byte* priv_data) +{ + int l; + + (void)key; + + /* Expanded private keys. */ + priv_data += LMS_PRIV_KEY_LEN(params->levels); + + for (l = 0; l < params->levels; l++) { + /* Caches for subtrees. */ + wc_lms_priv_state_store(params, &key->state[l], priv_data); + priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels, + params->cacheBits); + } +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + /* Next subtree's expanded private keys. */ + priv_data += LMS_PRIV_KEY_LEN(params->levels); + for (l = 0; l < params->levels - 1; l++) { + /* Next subtree's caches. */ + wc_lms_priv_state_store(params, &key->next_state[l], priv_data); + priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels, + params->cacheBits); + } +#endif /* WOLFSSL_LMS_NO_SIGN_SMOOTHING */ + +#ifndef WOLFSSL_LMS_NO_SIG_CACHE + /* Signature cache. */ +#endif /* WOLFSSL_LMS_NO_SIG_CACHE */ +} +#endif /* WOLFSSL_WC_LMS_SMALL */ + +/* Expand private key for each level and calculating auth path.. + * + * @param [in, out] state LMS state. + * @param [in] priv_raw Raw private key bytes. + * @param [out] priv_key Private key data. + * @param [out] priv_data Private key data. + * @param [out] pub_root Public key root node. + * @return 0 on success. + */ +int wc_hss_reload_key(LmsState* state, const byte* priv_raw, + HssPrivKey* priv_key, byte* priv_data, byte* pub_root) +{ + int ret; + + (void)pub_root; + + wc_hss_priv_data_load(state->params, priv_key, priv_data); +#ifndef WOLFSSL_WC_LMS_SMALL + priv_key->inited = 0; +#endif + + /* Expand the raw private key into the private key data. */ + ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 0); +#ifndef WOLFSSL_WC_LMS_SMALL + if ((ret == 0) && (!priv_key->inited)) { + /* Initialize the authentication paths and caches for all trees. */ + ret = wc_hss_init_auth_path(state, priv_key, pub_root); + #ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + if (ret == 0) { + ret = wc_hss_next_subtrees_init(state, priv_key); + } + #endif + #if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1) + if (ret == 0) { + /* Calculate signatures for trees not at bottom. */ + ret = wc_hss_presign(state, priv_key); + } + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + /* Set initialized flag. */ + priv_key->inited = (ret == 0); + } +#endif /* WOLFSSL_WC_LMS_SMALL */ + + return ret; +} + +/* Make an HSS key pair. + * + * @param [in, out] state LMS state. + * @param [in] rng Random number generator. + * @param [out] priv_raw Private key to write. + * @param [out] priv_key Private key. + * @param [out] priv_data Private key data. + * @param [out] pub Public key. + * @return 0 on success. + */ +int wc_hss_make_key(LmsState* state, WC_RNG* rng, byte* priv_raw, + HssPrivKey* priv_key, byte* priv_data, byte* pub) +{ + const LmsParams* params = state->params; + int ret = 0; + int i; + byte* p = priv_raw; + byte* pub_root = pub + LMS_L_LEN + LMS_TYPE_LEN + LMS_TYPE_LEN + LMS_I_LEN; + + /* The 64-bit q starts at 0 - set into raw private key. */ + wc_lms_idx_zero(p, HSS_Q_LEN); + p += HSS_Q_LEN; + + /* Set the LMS and LM-OTS types for each level. */ + for (i = 0; i < params->levels; i++) { + p[i] = (params->lmsType << 4) + params->lmOtsType; + } + /* Set rest of levels to an invalid value. */ + for (; i < HSS_MAX_LEVELS; i++) { + p[i] = 0xff; + } + p += HSS_PRIV_KEY_PARAM_SET_LEN; + + /* Make the private key. */ + ret = wc_lmots_make_private_key(rng, p); + + if (ret == 0) { + /* Set the levels into the public key data. */ + c32toa(params->levels, pub); + pub += LMS_L_LEN; + + ret = wc_hss_reload_key(state, priv_raw, priv_key, priv_data, pub_root); + } + #ifdef WOLFSSL_WC_LMS_SMALL + if (ret == 0) { + byte* priv_seed = priv_key->priv + LMS_Q_LEN; + byte* priv_i = priv_seed + LMS_SEED_LEN; + + /* Compute the root of the highest tree to get the root for public key. + */ + ret = wc_lms_make_public_key(state, priv_i, priv_seed, pub_root); + } + #endif /* !WOLFSSL_WC_LMS_SMALL */ + if (ret == 0) { + /* Encode the public key with remaining fields from the private key. */ + wc_lmots_public_key_encode(params, priv_key->priv, pub); + } + + return ret; +} + +#ifdef WOLFSSL_WC_LMS_SMALL +/* Sign message using HSS. + * + * Algorithm 8: Generating an HSS signature + * 1. If the message-signing key prv[L-1] is exhausted, regenerate + * that key pair, together with any parent key pairs that might + * be necessary. + * If the root key pair is exhausted, then the HSS key pair is + * exhausted and MUST NOT generate any more signatures. + * d = L + * while (prv[d-1].q == 2^(prv[d-1].h)) { + * d = d - 1 + * if (d == 0) + * return FAILURE + * } + * while (d < L) { + * create lms key pair pub[d], prv[d] + * sig[d-1] = lms_signature( pub[d], prv[d-1] ) + * d = d + 1 + * } + * 2. Sign the message. + * sig[L-1] = lms_signature( msg, prv[L-1] ) + * 3. Create the list of signed public keys. + * i = 0; + * while (i < L-1) { + * signed_pub_key[i] = sig[i] || pub[i+1] + * i = i + 1 + * } + * 4. Return u32str(L-1) || signed_pub_key[0] || ... + * || signed_pub_key[L-2] || sig[L-1] + * + * @param [in, out] state LMS state. + * @param [in, out] priv_raw Raw private key bytes. + * @param [in, out] priv_key Private key data. + * @param [in] msg Message to sign. + * @param [in] msgSz Length of message in bytes. + * @param [out] sig Signature of message. + * @return 0 on success. + */ +int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key, + byte* priv_data, const byte* msg, word32 msgSz, byte* sig) +{ + const LmsParams* params = state->params; + int ret = 0; + byte* priv = priv_key->priv; + + (void)priv_data; + + /* Step 1. Part 2: Check for total key exhaustion. */ + if (!wc_hss_sigsleft(params, priv_raw)) { + ret = KEY_EXHAUSTED_E; + } + + if (ret == 0) { + /* Expand the raw private key into the private key data. */ + ret = wc_hss_expand_private_key(state, priv, priv_raw, 0); + } + if (ret == 0) { + int i; + w64wrapper q; + w64wrapper qm1; + + /* Get 64-bit q from raw private key. */ + ato64(priv_raw, &q); + /* Calculate q-1 for comparison. */ + qm1 = q; + w64Decrement(&qm1); + + /* Set number of signed public keys. */ + c32toa(params->levels - 1, sig); + sig += params->sig_len; + + /* Build from bottom up. */ + for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) { + byte* p = priv + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN); + byte* root = NULL; + + /* Move to start of next signature at this level. */ + sig -= LMS_SIG_LEN(params->height, params->p); + if (i != 0) { + /* Put root node into signature at this index. */ + root = sig - LMS_MAX_NODE_LEN; + } + + /* Sign using LMS for this level. */ + ret = wc_lms_sign(state, p, msg, msgSz, sig); + if (ret == 0) { + byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN + LMS_MAX_NODE_LEN + + params->p * LMS_MAX_NODE_LEN + LMS_TYPE_LEN; + byte* priv_q = p; + byte* priv_seed = priv_q + LMS_Q_LEN; + byte* priv_i = priv_seed + LMS_SEED_LEN; + word32 q32; + + /* Get Q from private key as a number. */ + ato32(priv_q, &q32); + /* Calculate authentication path. */ + ret = wc_lms_auth_path(state, priv_i, priv_seed, q32, s, root); + } + if ((ret == 0) && (i != 0)) { + /* Create public data for this level if there is another. */ + sig -= LMS_PUBKEY_LEN; + msg = sig; + msgSz = LMS_PUBKEY_LEN; + wc_lmots_public_key_encode(params, p, sig); + } + } + } + if (ret == 0) { + /* Increment index of leaf node to sign with in raw data. */ + wc_lms_idx_inc(priv_raw, HSS_Q_LEN); + } + + return ret; +} +#else +/* Build signature for HSS signed message. + * + * Algorithm 8: Generating an HSS signature + * 1. ... + * while (prv[d-1].q == 2^(prv[d-1].h)) { + * d = d - 1 + * if (d == 0) + * return FAILURE + * } + * while (d < L) { + * create lms key pair pub[d], prv[d] + * sig[d-1] = lms_signature( pub[d], prv[d-1] ) + * d = d + 1 + * } + * 2. Sign the message. + * sig[L-1] = lms_signature( msg, prv[L-1] ) + * 3. Create the list of signed public keys. + * i = 0; + * while (i < L-1) { + * signed_pub_key[i] = sig[i] || pub[i+1] + * i = i + 1 + * } + * 4. Return u32str(L-1) || signed_pub_key[0] || ... + * || signed_pub_key[L-2] || sig[L-1] + * + * @param [in, out] state LMS state. + * @param [in, out] priv_raw Raw private key bytes. + * @param [in, out] priv_key Private key data. + * @param [in] msg Message to sign. + * @param [in] msgSz Length of message in bytes. + * @param [out] sig Signature of message. + * @return 0 on success. + */ +static int wc_hss_sign_build_sig(LmsState* state, byte* priv_raw, + HssPrivKey* priv_key, const byte* msg, word32 msgSz, byte* sig) +{ + const LmsParams* params = state->params; + int ret = 0; + int i; + w64wrapper q; + w64wrapper qm1; + byte* priv = priv_key->priv; + + /* Get 64-bit q from raw private key. */ + ato64(priv_raw, &q); + /* Calculate q-1 for comparison. */ + qm1 = q; + w64Decrement(&qm1); + + /* Set number of signed public keys. */ + c32toa(params->levels - 1, sig); + sig += params->sig_len; + + /* Build from bottom up. */ + for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) { + byte* p = priv + i * (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN); + byte* root = NULL; + #ifndef WOLFSSL_LMS_NO_SIG_CACHE + int store_p = 0; + word32 q_32 = LMS_Q_AT_LEVEL(q, params->levels, i, + params->height); + word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, i, + params->height); + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + + /* Move to start of next signature at this level. */ + sig -= LMS_SIG_LEN(params->height, params->p); + if (i != 0) { + /* Put root node into signature at this index. */ + root = sig - LMS_MAX_NODE_LEN; + } + + #ifndef WOLFSSL_LMS_NO_SIG_CACHE + /* Check if we have a cached version of C and the p hashes that we + * can reuse. */ + if ((i < params->levels - 1) && (q_32 == qm1_32)) { + wc_lms_sig_copy(params, priv_key->y + + i * LMS_PRIV_Y_TREE_LEN(params->p), p, sig); + } + else + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + { + /* Sign using LMS for this level. */ + ret = wc_lms_sign(state, p, msg, msgSz, sig); + #ifndef WOLFSSL_LMS_NO_SIG_CACHE + store_p = (i < params->levels - 1); + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + } + if (ret == 0) { + byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN; + + #ifndef WOLFSSL_LMS_NO_SIG_CACHE + /* Check if we computed new C and p hashes. */ + if (store_p) { + /* Cache the C and p hashes. */ + XMEMCPY(priv_key->y + i * LMS_PRIV_Y_TREE_LEN(params->p), s, + LMS_PRIV_Y_TREE_LEN(params->p)); + } + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + s += LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN + + LMS_TYPE_LEN; + + /* Copy the authentication path out of the private key. */ + XMEMCPY(s, priv_key->state[i].auth_path, + params->height * LMS_MAX_NODE_LEN); + /* Copy the root node into signature unless at top. */ + if (i != 0) { + XMEMCPY(root, priv_key->state[i].root, LMS_MAX_NODE_LEN); + } + } + if ((ret == 0) && (i != 0)) { + /* Create public data for this level if there is another. */ + sig -= LMS_PUBKEY_LEN; + msg = sig; + msgSz = LMS_PUBKEY_LEN; + wc_lmots_public_key_encode(params, p, sig); + } + } + + return ret; +} + +/* Sign message using HSS. + * + * Algorithm 8: Generating an HSS signature + * 1. If the message-signing key prv[L-1] is exhausted, regenerate + * that key pair, together with any parent key pairs that might + * be necessary. + * If the root key pair is exhausted, then the HSS key pair is + * exhausted and MUST NOT generate any more signatures. + * d = L + * while (prv[d-1].q == 2^(prv[d-1].h)) { + * d = d - 1 + * if (d == 0) + * return FAILURE + * } + * while (d < L) { + * create lms key pair pub[d], prv[d] + * sig[d-1] = lms_signature( pub[d], prv[d-1] ) + * d = d + 1 + * } + * 2. Sign the message. + * sig[L-1] = lms_signature( msg, prv[L-1] ) + * 3. Create the list of signed public keys. + * i = 0; + * while (i < L-1) { + * signed_pub_key[i] = sig[i] || pub[i+1] + * i = i + 1 + * } + * 4. Return u32str(L-1) || signed_pub_key[0] || ... + * || signed_pub_key[L-2] || sig[L-1] + * + * @param [in, out] state LMS state. + * @param [in, out] priv_raw Raw private key bytes. + * @param [in, out] priv_key Private key data. + * @param [in, out] priv_data Private key data. + * @param [in] msg Message to sign. + * @param [in] msgSz Length of message in bytes. + * @param [out] sig Signature of message. + * @return 0 on success. + */ +int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key, + byte* priv_data, const byte* msg, word32 msgSz, byte* sig) +{ + const LmsParams* params = state->params; + int ret = 0; + + /* Validate fixed parameters for static code analyzers. */ + if ((params->rootLevels == 0) || (params->rootLevels > params->height)) { + ret = BAD_FUNC_ARG; + } + + /* Step 1. Part 2: Check for total key exhaustion. */ + if ((ret == 0) && (!wc_hss_sigsleft(params, priv_raw))) { + ret = KEY_EXHAUSTED_E; + } + + if ((ret == 0) && (!priv_key->inited)) { + /* Initialize the authentication paths and caches for all trees. */ + ret = wc_hss_init_auth_path(state, priv_key, NULL); + #if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1) + if (ret == 0) { + ret = wc_hss_presign(state, priv_key); + } + #endif /* !WOLFSSL_LMS_NO_SIG_CACHE */ + /* Set initialized flag. */ + priv_key->inited = (ret == 0); + } + if (ret == 0) { + ret = wc_hss_sign_build_sig(state, priv_raw, priv_key, msg, msgSz, sig); + } + if (ret == 0) { + /* Increment index of leaf node to sign with in raw data. */ + wc_lms_idx_inc(priv_raw, HSS_Q_LEN); + } + /* Check we will produce another signature. */ + if ((ret == 0) && wc_hss_sigsleft(params, priv_raw)) { + /* Update the expanded private key data. */ + ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 1); + if (ret == 0) { + /* Update authentication path and caches for all trees. */ + ret = wc_hss_update_auth_path(state, priv_key, priv_raw, + params->levels); + } + } + if (ret == 0) { + /* Store the updated private key data. */ + wc_hss_priv_data_store(state->params, priv_key, priv_data); + } + + return ret; +} +#endif + +/* Check whether key is exhausted. + * + * First 8 bytes of raw key is the index. + * Check index is less than count of leaf nodes. + * + * @param [in] params LMS parameters. + * @param [in] priv_raw HSS raw private key. + * @return 1 when signature possible. + * @return 0 when private key exhausted. + */ +int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw) +{ + w64wrapper q; + w64wrapper cnt; + + /* Get current q - next leaf index to sign with. */ + ato64(priv_raw, &q); + /* 1 << total_height = total leaf nodes. */ + cnt = w64ShiftLeft(w64From32(0, 1), params->levels * params->height); + /* Check q is less than total leaf node count. */ + return w64LT(q, cnt); +} +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +/* Verify message using HSS. + * + * Section 6.3. Signature Verification + * 1. Nspk = strTou32(first four bytes of S) + * 2. if Nspk+1 is not equal to the number of levels L in pub: + * 3. return INVALID + * 4. key = pub + * 5. for (i = 0; i < Nspk; i = i + 1) { + * 6. sig = siglist[i] + * 7. msg = publist[i] + * 8. if (lms_verify(msg, key, sig) != VALID): + * 9. return INVALID + * 10. key = msg + * 11. } + * 12. return lms_verify(message, key, siglist[Nspk]) + * + * @param [in, out] state LMS state. + * @param [in] pub HSS public key. + * @param [in] msg Message to rifyn. + * @param [in] msgSz Length of message in bytes. + * @param [in] sig Signature of message. + * @return 0 on success. + * @return SIG_VERFIY_E on failure. + */ +int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg, + word32 msgSz, const byte* sig) +{ + const LmsParams* params = state->params; + int ret = 0; + word32 nspk; + const byte* key = pub + LMS_L_LEN; + word32 levels; + + /* Get number of levels from public key. */ + ato32(pub, &levels); + /* Line 1: Get number of signed public keys from signature. */ + ato32(sig, &nspk); + /* Line 6 (First iteration): Move to start of next signature. */ + sig += LMS_L_LEN; + + /* Line 2: Verify that pub and signature match in levels. */ + if (nspk + 1 != levels) { + /* Line 3: Return invalid signature. */ + ret = SIG_VERIFY_E; + } + if (ret == 0) { + word32 i; + + /* Line 5: For all but last LMS signature. */ + for (i = 0; (ret == 0) && (i < nspk); i++) { + /* Line 7: Get start of public key in signature. */ + const byte* pubList = sig + LMS_Q_LEN + LMS_TYPE_LEN + + LMS_MAX_NODE_LEN + params->p * LMS_MAX_NODE_LEN + LMS_TYPE_LEN + + params->height * LMS_MAX_NODE_LEN; + /* Line 8: Verify the LMS signature with public key as message. */ + ret = wc_lms_verify(state, key, pubList, LMS_PUBKEY_LEN, sig); + /* Line 10: Next key is from signature. */ + key = pubList; + /* Line 6: Move to start of next signature. */ + sig = pubList + LMS_PUBKEY_LEN; + } + } + if (ret == 0) { + /* Line 12: Verify bottom tree with real message. */ + ret = wc_lms_verify(state, key, msg, msgSz, sig); + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */ + diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index 545b531ce2..bdce86545d 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -19,8 +19,1650 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +#ifdef HAVE_CONFIG_H + #include +#endif + #include +#include +#include #ifdef WOLFSSL_HAVE_XMSS - #error "Contact wolfSSL to get the implementation of this file" +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + + +/*************************** + * DIGEST init and free. + ***************************/ + +/* Initialize the digest algorithm to use. + * + * @param [in, out] state XMSS/MT state including digest and parameters. + * @return 0 on success. + * @return NOT_COMPILED_IN when digest algorithm not supported. + * @return Other negative when digest algorithm initialization failed. + */ +static int wc_xmss_digest_init(XmssState* state) +{ + int ret; + word8 hash = state->params->hash; + +#ifdef WC_XMSS_SHA256 + if (hash == WC_HASH_TYPE_SHA256) { + ret = wc_InitSha256(&state->digest.sha256); + } + else +#endif +#ifdef WC_XMSS_SHA512 + if (hash == WC_HASH_TYPE_SHA512) { + ret = wc_InitSha512(&state->digest.sha512); + } + else +#endif +#ifdef WC_XMSS_SHAKE128 + if (hash == WC_HASH_TYPE_SHAKE128) { + ret = wc_InitShake128(&state->digest.shake, NULL, INVALID_DEVID); + } + else +#endif +#ifdef WC_XMSS_SHAKE256 + if (hash == WC_HASH_TYPE_SHAKE256) { + ret = wc_InitShake256(&state->digest.shake, NULL, INVALID_DEVID); + } + else +#endif + { + ret = NOT_COMPILED_IN; + } + + return ret; +} +/* Free the digest algorithm. + * + * @param [in, out] state XMSS/MT state including digest and parameters. + */ +static void wc_xmss_digest_free(XmssState* state) +{ + word8 hash = state->params->hash; + +#ifdef WC_XMSS_SHA256 + if (hash == WC_HASH_TYPE_SHA256) { + wc_Sha256Free(&state->digest.sha256); + } + else +#endif +#ifdef WC_XMSS_SHA512 + if (hash == WC_HASH_TYPE_SHA512) { + wc_Sha512Free(&state->digest.sha512); + } + else +#endif +#ifdef WC_XMSS_SHAKE128 + if (hash == WC_HASH_TYPE_SHAKE128) { + wc_Shake128_Free(&state->digest.shake); + } + else +#endif +#ifdef WC_XMSS_SHAKE256 + if (hash == WC_HASH_TYPE_SHAKE256) { + wc_Shake256_Free(&state->digest.shake); + } + else +#endif + { + /* Do nothing. */ + } +} + +/* Initialize the XMSS/MT state. + * + * @param [in, out] state XMSS/MT state including digest and parameters. + * @param [in] params Parameters for key. + * @return 0 on success. + * @return NOT_COMPILED_IN when digest algorithm not supported. + * @return Other negative when digest algorithm initialization failed. + */ +static WC_INLINE int wc_xmss_state_init(XmssState* state, + const XmssParams* params) +{ + state->params = params; + state->ret = 0; + return wc_xmss_digest_init(state); +} + +/* Free the XMSS/MT state. + * + * @param [in, out] state XMSS/MT state including digest and parameters. + */ +static WC_INLINE void wc_xmss_state_free(XmssState* state) +{ + wc_xmss_digest_free(state); +} + + +/*************************** + * XMSS PARAMS + ***************************/ + +/* Map of XMSS/MT string name to OID. + */ +typedef struct wc_XmssString { + /* Name of algorithm as a string. */ + const char* str; + /* OID for algorithm. */ + word32 oid; + /* XMSS parameters. */ + XmssParams params; +} wc_XmssString; + +#ifndef WOLFSSL_WC_XMSS_SMALL + +/* Size of BDS State encoded numbers - offset=1, next=3. */ +#define XMSS_BDS_NUMS_SZ 4 +/* Size of treehash encoding - nextIdx=3, completed|used=1. */ +#define XMSS_TREEHASH_SZ 4 + +/* Calculate Secret key length. + * + * See wc_xmss_bds_state_save() and wc_xmss_bds_state_load(). + * + * SK = idx || wots_sk || SK_PRF || root || SEED || BDSs || OTHER + * BDSs = (2 * depth - 1) * BDS + * BDS = stack || height || authPath || keep || nodes || retain || + * offset || next || TREEHASHes + * TREEHASHes = (Subtree height - BDS k param) * TREEHASH + * TREEHASH = nextIdx || completed || used + * + * @param [in] n Number of bytes to hash output. + * @param [in] h Height of full tree. + * @param [in] d Depth of trees (number of subtrees). + * @param [in] s Subtree height. + * @param [in] i Length of index encoding in bytes. + * @param [in] k BDS k parameter. + * @return Secret key length in bytes. + */ +#define XMSS_SK_LEN(n, h, d, s, i, k) \ + (((i) + 4 * (n)) + \ + (2 * (d) - 1) * (((s) + 1) * (n) + \ + (s) + 1 + \ + (s) * (n) + \ + ((s) >> 1) * (n) + \ + ((s) - (k)) * XMSS_TREEHASH_SZ + \ + ((s) - (k)) * (n) + \ + XMSS_RETAIN_LEN(k, n) + \ + XMSS_BDS_NUMS_SZ) + \ + ((d) - 1) * (n) * ((n) * 2 + 3)) + +#else + +/* Calculate Secret key length. + * + * SK = idx || wots_sk || SK_PRF || root || SEED + * + * @param [in] n Number of bytes to hash output. + * @param [in] h Height of full tree. Unused. + * @param [in] d Depth of trees (number of subtrees). Unused. + * @param [in] s Subtree height. Unused. + * @param [in] i Length of index encoding in bytes. + * @param [in] k BDS k parameter. Unused. + * @return Secret key length. + */ +#define XMSS_SK_LEN(n, h, d, s, i, k) \ + ((i) + 4 * (n)) + +#endif + +#ifndef WOLFSSL_XMSS_LARGE_SECRET_KEY +/* Choose the smaller BDS K parameter. */ +#define XMSS_K(k, kl) (k) +#else +/* Choose the larger BDS K parameter. */ +#define XMSS_K(k, kl) (kl) +#endif + +/* Calculate all fixed parameter values and output an array declaration. + * + * @param [in] hash Hash algorithm to use. + * @param [in] n Number of bytes to hash output. + * @param [in] p Number of bytes of padding. + * @param [in] h Height of full tree. + * @param [in] d Depth of trees (number of subtrees). + * @param [in] i Length of index encoding in bytes. + * @param [in] k BDS k parameter. 0 or >= 2 but (h/d - k) is even. + * @param [in] kl BDS k parameter when large signatures. + * @return XMSS/XMSS^MT parameters array declaration. + */ +#define XMSS_PARAMS(hash, n, p, h, d, i, k, kl) \ + { hash, n, p, (n) * 2 + 3, (n) * ((n) * 2 + 3), h, (h) / (d), (d), (i), \ + (i) + (n) + (d) * (((n) * 2 + 3) * (n)) + (h) * (n), \ + XMSS_SK_LEN(n, h, d, ((h) / (d)), i, XMSS_K(k, kl)), (n) * 2, \ + XMSS_K(k, kl) } + /* hash, d, pad_len, wots_len, wots_sig_len, h, sub_h, d, idx_len, + * sig_len, + * sk_len, pk_len, + * bds_k */ + +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 +/* List of known XMSS algorithm strings and their OIDs. */ +static const wc_XmssString wc_xmss_alg[] = { +#ifdef WC_XMSS_SHA256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHA2_10_256", WC_XMSS_OID_SHA2_10_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHA2_16_256", WC_XMSS_OID_SHA2_16_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHA2_20_256", WC_XMSS_OID_SHA2_20_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHA256 */ +#ifdef WC_XMSS_SHA512 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHA2_10_512", WC_XMSS_OID_SHA2_10_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHA2_16_512", WC_XMSS_OID_SHA2_16_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHA2_20_512", WC_XMSS_OID_SHA2_20_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 512 */ +#endif /* WC_XMSS_SHA512 */ + +#ifdef WC_XMSS_SHAKE128 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHAKE_10_256", WC_XMSS_OID_SHAKE_10_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHAKE_16_256", WC_XMSS_OID_SHAKE_16_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHAKE_20_256", WC_XMSS_OID_SHAKE_20_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHAKE128 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHAKE_10_512", WC_XMSS_OID_SHAKE_10_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHAKE_16_512", WC_XMSS_OID_SHAKE_16_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHAKE_20_512", WC_XMSS_OID_SHAKE_20_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 512 */ +#endif /* WC_XMSS_SHAKE256 */ + +#ifdef WC_XMSS_SHA256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHA2_10_192", WC_XMSS_OID_SHA2_10_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHA2_16_192", WC_XMSS_OID_SHA2_16_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHA2_20_192", WC_XMSS_OID_SHA2_20_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 192 */ +#endif /* WC_XMSS_SHA256 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHAKE256_10_256", WC_XMSS_OID_SHAKE256_10_256, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHAKE256_16_256", WC_XMSS_OID_SHAKE256_16_256, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHAKE256_20_256", WC_XMSS_OID_SHAKE256_20_256, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHAKE256 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 10 && WOLFSSL_XMSS_MAX_HEIGHT >= 10 + { "XMSS-SHAKE256_10_192", WC_XMSS_OID_SHAKE256_10_192, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 10, 1, 4, 0, 4), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 16 && WOLFSSL_XMSS_MAX_HEIGHT >= 16 + { "XMSS-SHAKE256_16_192", WC_XMSS_OID_SHAKE256_16_192, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 16, 1, 4, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSS-SHAKE256_20_192", WC_XMSS_OID_SHAKE256_20_192, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 20, 1, 4, 0, 0), }, +#endif +#endif /* HASH_SIZE 192 */ +#endif /* WC_XMSS_SHAKE256 */ +}; +/* Length of array of known XMSS algorithms. */ +#define WC_XMSS_ALG_LEN (sizeof(wc_xmss_alg) / sizeof(*wc_xmss_alg)) +#endif + +/* Convert XMSS algorithm string to an OID - object identifier. + * + * @param [out] oid OID value corresponding to string. + * @param [in] s String to convert. + * @param [out] params XMSS/MT parameters. + * @return 0 on success. + * @return NOT_COMPILED_IN on failure. + */ +static int wc_xmss_str_to_params(const char *s, word32* oid, + const XmssParams** params) +{ + int ret = NOT_COMPILED_IN; +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 + unsigned int i; + + for (i = 0; i < WC_XMSS_ALG_LEN; i++) { + if (XSTRCMP(s, wc_xmss_alg[i].str) == 0) { + *oid = wc_xmss_alg[i].oid; + *params = &wc_xmss_alg[i].params; + ret = 0; + break; + } + } +#else + (void)s; + (void)oid; + (void)params; +#endif + + return ret; +} + +#if WOLFSSL_XMSS_MAX_HEIGHT >= 20 +/* List of known XMSS^MT algorithm strings and their OIDs. */ +static const wc_XmssString wc_xmssmt_alg[] = { +#ifdef WC_XMSS_SHA256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHA2_20/2_256", WC_XMSSMT_OID_SHA2_20_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHA2_20/4_256", WC_XMSSMT_OID_SHA2_20_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHA2_40/2_256", WC_XMSSMT_OID_SHA2_40_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHA2_40/4_256", WC_XMSSMT_OID_SHA2_40_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHA2_40/8_256", WC_XMSSMT_OID_SHA2_40_8_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHA2_60/3_256", WC_XMSSMT_OID_SHA2_60_3_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHA2_60/6_256", WC_XMSSMT_OID_SHA2_60_6_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHA2_60/12_256", WC_XMSSMT_OID_SHA2_60_12_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 32, 32, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHA256 */ +#ifdef WC_XMSS_SHA512 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHA2_20/2_512", WC_XMSSMT_OID_SHA2_20_2_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHA2_20/4_512", WC_XMSSMT_OID_SHA2_20_4_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHA2_40/2_512", WC_XMSSMT_OID_SHA2_40_2_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHA2_40/4_512", WC_XMSSMT_OID_SHA2_40_4_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHA2_40/8_512", WC_XMSSMT_OID_SHA2_40_8_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHA2_60/3_512", WC_XMSSMT_OID_SHA2_60_3_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHA2_60/6_512", WC_XMSSMT_OID_SHA2_60_6_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHA2_60/12_512", WC_XMSSMT_OID_SHA2_60_12_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHA512, 64, 64, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 512 */ +#endif /* WC_XMSS_SHA512 */ + +#ifdef WC_XMSS_SHAKE128 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHAKE_20/2_256", WC_XMSSMT_OID_SHAKE_20_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHAKE_20/4_256", WC_XMSSMT_OID_SHAKE_20_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHAKE_40/2_256", WC_XMSSMT_OID_SHAKE_40_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHAKE_40/4_256", WC_XMSSMT_OID_SHAKE_40_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHAKE_40/8_256", WC_XMSSMT_OID_SHAKE_40_8_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHAKE_60/3_256", WC_XMSSMT_OID_SHAKE_60_3_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHAKE_60/6_256", WC_XMSSMT_OID_SHAKE_60_6_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHAKE_60/12_256", WC_XMSSMT_OID_SHAKE_60_12_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE128, 32, 32, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHAKE128 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 512 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHAKE_20/2_512", WC_XMSSMT_OID_SHAKE_20_2_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHAKE_20/4_512", WC_XMSSMT_OID_SHAKE_20_4_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHAKE_40/2_512", WC_XMSSMT_OID_SHAKE_40_2_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHAKE_40/4_512", WC_XMSSMT_OID_SHAKE_40_4_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHAKE_40/8_512", WC_XMSSMT_OID_SHAKE_40_8_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHAKE_60/3_512", WC_XMSSMT_OID_SHAKE_60_3_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHAKE_60/6_512", WC_XMSSMT_OID_SHAKE_60_6_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHAKE_60/12_512", WC_XMSSMT_OID_SHAKE_60_12_512 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 64, 64, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 512 */ +#endif /* WC_XMSS_SHAKE256 */ + +#ifdef WC_XMSS_SHA256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHA2_20/2_192", WC_XMSSMT_OID_SHA2_20_2_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHA2_20/4_192", WC_XMSSMT_OID_SHA2_20_4_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHA2_40/2_192", WC_XMSSMT_OID_SHA2_40_2_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHA2_40/4_192", WC_XMSSMT_OID_SHA2_40_4_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHA2_40/8_192", WC_XMSSMT_OID_SHA2_40_8_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHA2_60/3_192", WC_XMSSMT_OID_SHA2_60_3_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHA2_60/6_192", WC_XMSSMT_OID_SHA2_60_6_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHA2_60/12_192", WC_XMSSMT_OID_SHA2_60_12_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHA256, 24, 4, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 192 */ +#endif /* WC_XMSS_SHA256 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 256 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 256 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHAKE256_20/2_256", WC_XMSSMT_OID_SHAKE256_20_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHAKE256_20/4_256", WC_XMSSMT_OID_SHAKE256_20_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHAKE256_40/2_256", WC_XMSSMT_OID_SHAKE256_40_2_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHAKE256_40/4_256", WC_XMSSMT_OID_SHAKE256_40_4_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHAKE256_40/8_256", WC_XMSSMT_OID_SHAKE256_40_8_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHAKE256_60/3_256", WC_XMSSMT_OID_SHAKE256_60_3_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHAKE256_60/6_256", WC_XMSSMT_OID_SHAKE256_60_6_256 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHAKE256_60/12_256", WC_XMSSMT_OID_SHAKE256_60_12_256, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 32, 32, 60, 12, 8, 0, 0), }, +#endif +#endif /* HASH_SIZE 256 */ +#endif /* WC_XMSS_SHAKE256 */ + +#ifdef WC_XMSS_SHAKE256 +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192 +#if WOLFSSL_XMSS_MIN_HEIGHT <= 20 && WOLFSSL_XMSS_MAX_HEIGHT >= 20 + { "XMSSMT-SHAKE256_20/2_192", WC_XMSSMT_OID_SHAKE256_20_2_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 20, 2, 3, 2, 4), }, + { "XMSSMT-SHAKE256_20/4_192", WC_XMSSMT_OID_SHAKE256_20_4_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 20, 4, 3, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 40 && WOLFSSL_XMSS_MAX_HEIGHT >= 40 + { "XMSSMT-SHAKE256_40/2_192", WC_XMSSMT_OID_SHAKE256_40_2_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 40, 2, 5, 2, 4), }, + { "XMSSMT-SHAKE256_40/4_192", WC_XMSSMT_OID_SHAKE256_40_4_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 40, 4, 5, 2, 4), }, + { "XMSSMT-SHAKE256_40/8_192", WC_XMSSMT_OID_SHAKE256_40_8_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 40, 8, 5, 0, 0), }, +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 60 && WOLFSSL_XMSS_MAX_HEIGHT >= 60 + { "XMSSMT-SHAKE256_60/3_192", WC_XMSSMT_OID_SHAKE256_60_3_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 60, 3, 8, 2, 4), }, + { "XMSSMT-SHAKE256_60/6_192", WC_XMSSMT_OID_SHAKE256_60_6_192 , + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 60, 6, 8, 2, 4), }, + { "XMSSMT-SHAKE256_60/12_192", WC_XMSSMT_OID_SHAKE256_60_12_192, + XMSS_PARAMS(WC_HASH_TYPE_SHAKE256, 24, 4, 60, 12, 8, 0, 0), }, #endif +#endif /* HASH_SIZE 192 */ +#endif /* WC_XMSS_SHAKE256 */ +}; +/* Length of array of known XMSS^MT algorithms. */ +#define WC_XMSSMT_ALG_LEN (sizeof(wc_xmssmt_alg) / sizeof(*wc_xmssmt_alg)) +#endif + +/* Convert XMSS^MT algorithm string to an OID - object identifier. + * + * @param [out] oid OID value corresponding to string. + * @param [in] s String to convert. + * @param [out] params XMSS/MT parameters. + * @return 0 on success. + * @return NOT_COMPILED_IN on failure. + */ +static int wc_xmssmt_str_to_params(const char *s, word32* oid, + const XmssParams** params) +{ + int ret = NOT_COMPILED_IN; +#if WOLFSSL_XMSS_MAX_HEIGHT >= 20 + unsigned int i; + + for (i = 0; i < WC_XMSSMT_ALG_LEN; i++) { + if (XSTRCMP(s, wc_xmssmt_alg[i].str) == 0) { + *oid = wc_xmssmt_alg[i].oid; + *params = &wc_xmssmt_alg[i].params; + ret = 0; + break; + } + } +#else + (void)s; + (void)oid; + (void)params; +#endif + + return ret; +} + +/*************************** + * OTHER Internal APIs + ***************************/ + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY +/* Allocates the XMSS secret key (sk) array. + * + * The XMSS/XMSS^MT secret key length is a function of the + * parameters, and can't be allocated until the param string + * has been set with SetParamStr. + * + * This is only called by MakeKey() and Reload(). + * + * Note: the XMSS sk array is force zeroed after every use. + * + * @param [in] key The XMSS key. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_FUNC_ARG when private key already allocated. + * @return MEMORY_E when allocating dynamic memory fails. + */ +static int wc_xmsskey_alloc_sk(XmssKey* key) +{ + int ret = 0; + + /* Validate parameter. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + /* Ensure the private key doesn't exist. */ + else if (key->sk != NULL) { + WOLFSSL_MSG("error: XMSS secret key already exists"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* The XMSS/XMSS^MT secret key length is a function of the + * parameters. Therefore can't allocate this until param + * string has been set. */ + ret = wc_XmssKey_GetPrivLen(key, &key->sk_len); + } + if (ret == 0) { + /* Allocate a buffer to hold secret key. */ + key->sk = (unsigned char *)XMALLOC(key->sk_len, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (key->sk == NULL) { + WOLFSSL_MSG("error: malloc XMSS key->sk failed"); + ret = MEMORY_E; + } + } + + if (ret == 0) { + /* Zeroize private key buffer. */ + ForceZero(key->sk, key->sk_len); + } + + return ret; +} + +/* Signs the message using the XMSS secret key, and + * updates the secret key on NV storage. + * + * Both operations must succeed to be considered + * successful. + * + * On success: sets key state to WC_XMSS_STATE_OK. + * On failure: sets key state to WC_XMSS_STATE_BAD + * + * If no signatures are left, sets state to WC_XMSS_STATE_NOSIGS. + * + * @return IO_FAILED_E when reading or writing private key failed. + * @return KEY_EXHAUSTED_E when no more keys in private key available. + * @return BAD_COND_E when generated signature length is invalid. + */ +static WC_INLINE int wc_xmsskey_signupdate(XmssKey* key, byte* sig, + const byte* msg, int msgLen) +{ + int ret = 0; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; + + /* Set the key state to bad by default. State is presumed bad unless a + * correct sign and update operation happen together. */ + key->state = WC_XMSS_STATE_BAD; + + /* Read the current secret key from NV storage.*/ + cb_rc = key->read_private_key(key->sk, key->sk_len, key->context); + if (cb_rc != WC_XMSS_RC_READ_TO_MEMORY) { + /* Read from NV storage failed. */ + WOLFSSL_MSG("error: XMSS read_private_key failed"); + ret = IO_FAILED_E; + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + XmssState* state; + #else + XmssState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize state for use in signing. */ + ret = wc_xmss_state_init(state, key->params); + if (ret == 0) { + /* Read was good. Now sign and update the secret key in memory. + */ + #ifndef WOLFSSL_WC_XMSS_SMALL + if (key->is_xmssmt) { + ret = wc_xmssmt_sign(state, msg, msgLen, key->sk, sig); + } + else { + ret = wc_xmss_sign(state, msg, msgLen, key->sk, sig); + } + #else + ret = wc_xmssmt_sign(state, msg, msgLen, key->sk, sig); + #endif + if (ret == KEY_EXHAUSTED_E) { + /* Signature space exhausted. */ + key->state = WC_XMSS_STATE_NOSIGS; + WOLFSSL_MSG("error: no XMSS signatures remaining"); + } + else if (ret != 0) { + /* Something failed or inconsistent in signature. Erase the + * signature just to be safe. */ + ForceZero(sig, key->params->sig_len); + WOLFSSL_MSG("error: XMSS sign failed"); + } + /* Free state after use. */ + wc_xmss_state_free(state); + } + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + + if (ret == 0) { + /* The signature succeeded. key->sk is now updated and must be + * committed to NV storage. */ + cb_rc = key->write_private_key(key->sk, key->sk_len, key->context); + if (cb_rc != WC_XMSS_RC_SAVED_TO_NV_MEMORY) { + /* Write to NV storage failed. Erase the signature from + * memory. */ + ForceZero(sig, key->params->sig_len); + WOLFSSL_MSG("error: XMSS write_private_key failed"); + ret = IO_FAILED_E; + } + } + if (ret == 0) { + /* key->sk was successfully committed to NV storage. Set the + * key state to OK, and set the sigLen. */ + key->state = WC_XMSS_STATE_OK; + } + + /* Force zero the secret key from memory always. */ + ForceZero(key->sk, key->sk_len); + + return ret; +} +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +/*************************** + * PUBLIC API + ***************************/ + +/* Init an XMSS key. + * + * Call this before setting the parms of an XMSS key. + * + * @param [in] key The XMSS key to init. + * @param [in] heap Unused. + * @param [in] devId Unused. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + */ +int wc_XmssKey_Init(XmssKey* key, void* heap, int devId) +{ + int ret = 0; + + (void) heap; + (void) devId; + + /* Validate parameters. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Zeroize key and set state to initialized. */ + ForceZero(key, sizeof(XmssKey)); + key->state = WC_XMSS_STATE_INITED; + } + + return ret; +} + +/* Set the XMSS key parameter string. + * + * The input string must be one of the supported parm set names in + * the "Name" section from the table in wolfssl/wolfcrypt/xmss.h, + * e.g. "XMSS-SHA2_10_256" or "XMSSMT-SHA2_20/4_256". + * + * @param [in] key The XMSS key to set. + * @param [in] str The XMSS/XMSS^MT parameter string. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_FUNC_ARG when string not recognized. + * @return BAD_STATE_E when wrong state for operation. + * @return NOT_COMPILED_IN when string not supported. + */ +int wc_XmssKey_SetParamStr(XmssKey* key, const char* str) +{ + int ret = 0; + word32 oid = 0; + int is_xmssmt = 0; + + /* Validate parameters. */ + if ((key == NULL) || (str == NULL)) { + ret = BAD_FUNC_ARG; + } + + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_INITED)) { + WOLFSSL_MSG("error: XMSS key needs init"); + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Check which type of algorithm the string is for. */ + is_xmssmt = (XMEMCMP(str, "XMSS-", 5) != 0); + + /* Convert XMSS param string to OID. */ + if (is_xmssmt) { + ret = wc_xmssmt_str_to_params(str, &oid, &key->params); + } + else { + ret = wc_xmss_str_to_params(str, &oid, &key->params); + } + if (ret != 0) { + WOLFSSL_MSG("error: xmssmt_str_to_params failed"); + ret = BAD_FUNC_ARG; + } + } + + if (ret == 0) { + /* Set key info. */ + key->oid = oid; + key->is_xmssmt = is_xmssmt; + key->state = WC_XMSS_STATE_PARMSET; + } + + return ret; +} + +/* Force zeros and frees the XMSS key from memory. + * + * This does not touch the private key saved to non-volatile storage. + * + * This is the only function that frees the key->sk array. + * + * @param [in] key XMSS key. + */ +void wc_XmssKey_Free(XmssKey* key) +{ + /* Validate parameter. */ + if (key != NULL) { + #ifndef WOLFSSL_XMSS_VERIFY_ONLY + if (key->sk != NULL) { + /* Zeroize private key. */ + ForceZero(key->sk, key->sk_len); + XFREE(key->sk, NULL, DYNAMIC_TYPE_TMP_BUFFER); + key->sk = NULL; + key->sk_len = 0; + } + #endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + + /* Ensure all data is zeroized. */ + ForceZero(key, sizeof(XmssKey)); + + /* Set the state to freed. */ + key->state = WC_XMSS_STATE_FREED; + } +} + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY +/* Sets the XMSS write private key callback. + * + * The callback must be able to write/update the private key to + * non-volatile storage. + * + * @param [in] key The XMSS key. + * @param [in] write_cb The write private key callback. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + */ +int wc_XmssKey_SetWriteCb(XmssKey* key, wc_xmss_write_private_key_cb write_cb) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (write_cb == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Changing the write callback of an already working key is forbidden. */ + else if (key->state == WC_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetWriteCb: key in use"); + ret = BAD_STATE_E; + } + else { + /* Set write callback for storing private key. */ + key->write_private_key = write_cb; + } + + return ret; +} + +/* Sets the XMSS read private key callback. + * + * The callback must be able to read the private key from + * non-volatile storage. + * + * @param [in] key The XMSS key. + * @param [in] read_cb The read private key callback. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + */ +int wc_XmssKey_SetReadCb(XmssKey* key, wc_xmss_read_private_key_cb read_cb) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (read_cb == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Changing the read callback of an already working key is forbidden. */ + else if (key->state == WC_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetReadCb: key in use"); + ret = BAD_STATE_E; + } + else { + /* Set write callback for getting private key. */ + key->read_private_key = read_cb; + } + + return ret; +} + +/* Sets the XMSS context to be used by write and read callbacks. + * + * E.g. this could be a filename if the callbacks write/read to file. + * + * @param [in] key The XMSS key. + * @param [in] context The context pointer. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + */ +int wc_XmssKey_SetContext(XmssKey* key, void* context) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (context == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Setting context of an already working key is forbidden. */ + else if (key->state == WC_XMSS_STATE_OK) { + WOLFSSL_MSG("error: wc_XmssKey_SetContext: key in use"); + ret = BAD_STATE_E; + } + else { + /* Set read/write callback context for accessing the private key. */ + key->context = context; + } + + return ret; +} + +/* Make the XMSS/XMSS^MT private/public key pair. The key must have its + * parameters set before calling this. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * This function and Reload() are the only functions that allocate + * key->sk array. wc_XmssKey_FreeKey is the only function that + * deallocates key->sk. + * + * @param [in] key The XMSS key to make. + * @param [in] rng Initialized WC_RNG pointer. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_FUNC_ARG when a write private key is not set. + * @return BAD_FUNC_ARG when a read/write private key context is not set. + * @return BAD_FUNC_ARG when private key already allocated. + * @return MEMORY_E when allocating dynamic memory fails. + * @return BAD_STATE_E when wrong state for operation. + * @return IO_FAILED_E when writing private key failed. + * @return Other negative when random number generation failed. + */ +int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) +{ + int ret = 0; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; +#ifdef WOLFSSL_SMALL_STACK + unsigned char* seed = NULL; +#else + unsigned char seed[3 * WC_XMSS_MAX_N]; +#endif + + /* Validate parameters */ + if ((key == NULL) || (rng == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) { + WOLFSSL_MSG("error: XmssKey not ready for generation"); + ret = BAD_STATE_E; + } + /* Ensure write callback available. */ + if ((ret == 0) && (key->write_private_key == NULL)) { + WOLFSSL_MSG("error: XmssKey write callback is not set"); + ret = BAD_FUNC_ARG; + } + /* Ensure read/write callback context available. */ + if ((ret == 0) && (key->context == NULL)) { + WOLFSSL_MSG("error: XmssKey context is not set"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Allocate sk array. */ + ret = wc_xmsskey_alloc_sk(key); + } +#ifdef WOLFSSL_SMALL_STACK + if (ret == 0) { + seed = (unsigned char*)XMALLOC(3 * key->params->n, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (seed == NULL) { + ret = MEMORY_E; + } + } +#endif + + if (ret == 0) { + /* Generate three random seeds. */ + ret = wc_RNG_GenerateBlock(rng, seed, 3 * key->params->n); + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + XmssState* state; + #else + XmssState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize state for use in key generation. */ + ret = wc_xmss_state_init(state, key->params); + if (ret == 0) { + /* Finally make the private/public key pair. Immediately write + * it to NV storage and then clear from memory. */ + #ifndef WOLFSSL_WC_XMSS_SMALL + if (key->is_xmssmt) { + ret = wc_xmssmt_keygen(state, seed, key->sk, key->pk); + } + else { + ret = wc_xmss_keygen(state, seed, key->sk, key->pk); + } + #else + ret = wc_xmssmt_keygen(state, seed, key->sk, key->pk); + #endif + if (ret != 0) { + WOLFSSL_MSG("error: XMSS keygen failed"); + key->state = WC_XMSS_STATE_BAD; + } + /* Free state after use. */ + wc_xmss_state_free(state); + } + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + + if (ret == 0) { + /* Write out private key. */ + cb_rc = key->write_private_key(key->sk, key->sk_len, key->context); + /* Zeroize private key data whether it was saved or not. */ + ForceZero(key->sk, key->sk_len); + /* Check writing succeeded. */ + if (cb_rc != WC_XMSS_RC_SAVED_TO_NV_MEMORY) { + WOLFSSL_MSG("error: XMSS write to NV storage failed"); + key->state = WC_XMSS_STATE_BAD; + ret = IO_FAILED_E; + } + } + + if (ret == 0) { + key->state = WC_XMSS_STATE_OK; + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(seed, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; +} + +/* This function allocates the secret key buffer, and does a + * quick sanity check to verify the secret key is readable + * from NV storage, and then force zeros the key from memory. + * + * On success it sets the key state to OK. + * + * Use this function to resume signing with an already existing + * XMSS key pair. + * + * Write/read callbacks, and context data, must be set prior. + * Key must have parameters set. + * + * This function and MakeKey are the only functions that allocate + * key->sk array. wc_XmssKey_FreeKey is the only function that + * deallocates key->sk. + * + * @params [in] key XMSS key to load. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_FUNC_ARG when a read or write function is not set. + * @return BAD_FUNC_ARG when a read/write function context is not set. + * @return BAD_FUNC_ARG when private key already allocated. + * @return MEMORY_E when allocating dynamic memory fails. + * @return BAD_STATE_E when wrong state for operation. + * @return IO_FAILED_E when reading private key failed. + */ +int wc_XmssKey_Reload(XmssKey* key) +{ + int ret = 0; + enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; + + /* Validate parameter. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) { + WOLFSSL_MSG("error: XmssKey not ready for reload"); + ret = BAD_STATE_E; + } + /* Ensure read and write callbacks are available. */ + if ((ret == 0) && ((key->write_private_key == NULL) || + (key->read_private_key == NULL))) { + WOLFSSL_MSG("error: XmssKey write/read callbacks are not set"); + ret = BAD_FUNC_ARG; + } + /* Ensure read and write callback context is available. */ + if ((ret == 0) && (key->context == NULL)) { + WOLFSSL_MSG("error: XmssKey context is not set"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Allocate sk array. */ + ret = wc_xmsskey_alloc_sk(key); + } + + if (ret == 0) { + /* Read the current secret key from NV storage. Force clear it + * immediately. This is just to sanity check the secret key + * is readable from permanent storage. */ + cb_rc = key->read_private_key(key->sk, key->sk_len, key->context); + ForceZero(key->sk, key->sk_len); + /* Check reading succeeded. */ + if (cb_rc != WC_XMSS_RC_READ_TO_MEMORY) { + WOLFSSL_MSG("error: XMSS read from NV storage failed"); + key->state = WC_XMSS_STATE_BAD; + ret = IO_FAILED_E; + } + } + if (ret == 0) { + key->state = WC_XMSS_STATE_OK; + } + + return ret; +} + +/* Gets the XMSS/XMSS^MT private key length. + * + * Parameters must be set before calling this, as the key size (sk_len) + * is a function of the parameters. + * + * Note: the XMSS/XMSS^MT private key format is implementation specific, + * and not standardized. Interoperability of XMSS private keys should + * not be expected. + * + * @param [in] key XMSS key. + * @param [out] len Length of the private key in bytes. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + * */ +int wc_XmssKey_GetPrivLen(const XmssKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && ((key->state != WC_XMSS_STATE_OK) && + (key->state != WC_XMSS_STATE_PARMSET))) { + /* params->sk_len not set yet. */ + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Calculate private key length: OID + private key bytes. */ + *len = XMSS_OID_LEN + (word32)key->params->sk_len; + } + + return ret; +} + +/* Sign the message using the XMSS secret key. + * + * @param [in] key XMSS key to use to sign. + * @param [in] sig Buffer to write signature into. + * @param [in, out] sigLen On in, size of buffer. + * On out, the length of the signature in bytes. + * @param [in] msg Message to sign. + * @param [in] msgLen Length of the message in bytes. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_FUNC_ARG when a write private key is not set. + * @return BAD_FUNC_ARG when a read/write private key context is not set. + * @return BAD_STATE_E when wrong state for operation. + * @return BUFFER_E when sigLen is too small. + * @return IO_FAILED_E when reading or writing private key failed. + * @return KEY_EXHAUSTED_E when no more keys in private key available. + * @return BAD_COND_E when generated signature length is invalid. + */ +int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, + int msgLen) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (sig == NULL) || (sigLen == NULL) || (msg == NULL) || + (msgLen <= 0)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state == WC_XMSS_STATE_NOSIGS)) { + WOLFSSL_MSG("error: XMSS signatures exhausted"); + ret = BAD_STATE_E; + } + if ((ret == 0) && (key->state != WC_XMSS_STATE_OK)) { + /* The key had an error the last time it was used, and we + * can't guarantee its state. */ + WOLFSSL_MSG("error: can't sign, XMSS key not in good state"); + ret = BAD_STATE_E; + } + /* Check signature buffer size. */ + if ((ret == 0) && (*sigLen < key->params->sig_len)) { + /* Signature buffer too small. */ + WOLFSSL_MSG("error: XMSS sig buffer too small"); + ret = BUFFER_E; + } + /* Check read and write callbacks available. */ + if ((ret == 0) && ((key->write_private_key == NULL) || + (key->read_private_key == NULL))) { + WOLFSSL_MSG("error: XmssKey write/read callbacks are not set"); + ret = BAD_FUNC_ARG; + } + /* Check read/write callback context available. */ + if ((ret == 0) && (key->context == NULL)) { + WOLFSSL_MSG("error: XmssKey context is not set"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + *sigLen = key->params->sig_len; + /* Finally, sign and update the secret key. */ + ret = wc_xmsskey_signupdate(key, sig, msg, msgLen); + } + + return ret; +} + +/* Check if more signatures are possible with key. + * + * @param [in] key XMSS key to check. + * @return 1 when signatures possible. + * @return 0 when key exhausted. + */ +int wc_XmssKey_SigsLeft(XmssKey* key) +{ + int ret; + + /* Validate parameter. */ + if (key == NULL) { + ret = 0; + } + /* Validate state. */ + else if (key->state == WC_XMSS_STATE_NOSIGS) { + WOLFSSL_MSG("error: XMSS signatures exhausted"); + ret = 0; + } + else if (key->state != WC_XMSS_STATE_OK) { + WOLFSSL_MSG("error: can't sign, XMSS key not in good state"); + ret = 0; + } + /* Read the current secret key from NV storage.*/ + else if (key->read_private_key(key->sk, key->sk_len, key->context) != + WC_XMSS_RC_READ_TO_MEMORY) { + WOLFSSL_MSG("error: XMSS read_private_key failed"); + ret = 0; + } + else { + /* Ask implementation to check index in private key. */ + ret = wc_xmss_sigsleft(key->params, key->sk); + } + + return ret; +} +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY*/ + +/* Get the XMSS/XMSS^MT public key length. + * + * The public key is static in size and does not depend on parameters, + * other than the choice of SHA256 as hashing function. + * + * @param [in] key XMSS key. + * @param [out] len Length of the public key. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return NOT_COMPILED_IN when a hash algorithm not supported. + */ +int wc_XmssKey_GetPubLen(const XmssKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + else { + *len = XMSS_OID_LEN + key->params->pk_len; + } + + return ret; +} + +/* Export public key and parameters from one XmssKey to another. + * + * Use this to prepare a signature verification XmssKey that is pub only. + * + * @param [out] keyDst Destination key for copy. + * @param [in] keySrc Source key for copy. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a key is NULL. + * @return Other negative when digest algorithm initialization failed. + */ +int wc_XmssKey_ExportPub(XmssKey* keyDst, const XmssKey* keySrc) +{ + int ret = 0; + + /* Validate parameters. */ + if ((keyDst == NULL) || (keySrc == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Zeroize the new key. */ + ForceZero(keyDst, sizeof(XmssKey)); + + /* Copy over the public key. */ + XMEMCPY(keyDst->pk, keySrc->pk, sizeof(keySrc->pk)); + + /* Copy over the key info. */ + keyDst->oid = keySrc->oid; + keyDst->is_xmssmt = keySrc->is_xmssmt; + keyDst->params = keySrc->params; + } + if (ret == 0) { + /* Mark keyDst as verify only, to prevent misuse. */ + keyDst->state = WC_XMSS_STATE_VERIFYONLY; + } + + return 0; +} + +/* Exports the raw XMSS public key buffer from key to out buffer. + * + * The out buffer should be large enough to hold the public key, and + * outLen should indicate the size of the buffer. + * + * @param [in] key XMSS key. + * @param [out] out Array holding public key. + * @param [in, out] outLen On in, size of buffer. + * On out, the length of the public key. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BUFFER_E if array is too small. + */ +int wc_XmssKey_ExportPubRaw(const XmssKey* key, byte* out, word32* outLen) +{ + int ret = 0; + word32 pubLen = 0; + + /* Validate parameters. */ + if ((key == NULL) || (out == NULL) || (outLen == NULL)) { + ret = BAD_FUNC_ARG; + } + + /* Get the public key length. */ + if (ret == 0) { + ret = wc_XmssKey_GetPubLen(key, &pubLen); + } + /* Check the output buffer is large enough. */ + if ((ret == 0) && (*outLen < pubLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + int i = 0; + /* First copy the oid into buffer. */ + for (; i < XMSS_OID_LEN; i++) { + out[XMSS_OID_LEN - i - 1] = (key->oid >> (8 * i)) & 0xFF; + } + /* Copy the public key data into buffer after oid. */ + XMEMCPY(out + XMSS_OID_LEN, key->pk, pubLen - XMSS_OID_LEN); + /* Return actual public key length. */ + *outLen = pubLen; + } + + return ret; +} + +/* Imports a raw public key buffer from in array to XmssKey key. + * + * The XMSS parameters must be set first with wc_XmssKey_SetParamStr, + * and inLen must match the length returned by wc_XmssKey_GetPubLen. + * + * @param [in, out] key XMSS key. + * @param [in] in Array holding public key. + * @param [in] inLen Length of array in bytes. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BUFFER_E if array is incorrect size. + * @return BAD_STATE_E when wrong state for operation. + * */ +int wc_XmssKey_ImportPubRaw(XmssKey* key, const byte* in, word32 inLen) +{ + int ret = 0; + word32 pubLen = 0; + + /* Validate parameters. */ + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_PARMSET)) { + /* XMSS key not ready for import. Param str must be set first. */ + WOLFSSL_MSG("error: XMSS key not ready for import"); + ret = BAD_STATE_E; + } + + /* Get the public key length. */ + if (ret == 0) { + ret = wc_XmssKey_GetPubLen(key, &pubLen); + } + /* Check the input buffer is the right size. */ + if ((ret == 0) && (inLen != pubLen)) { + /* Something inconsistent. Parameters weren't set, or input + * pub key is wrong.*/ + ret = BUFFER_E; + } + + if (ret == 0) { + /* Copy the public key data into key. */ + XMEMCPY(key->pk, in + XMSS_OID_LEN, pubLen - XMSS_OID_LEN); + + /* Update state to verify-only as we don't have a private key. */ + key->state = WC_XMSS_STATE_VERIFYONLY; + } + + return ret; +} + +/* Gets the XMSS/XMSS^MT signature length. + * + * Parameters must be set before calling this, as the signature size + * is a function of the parameters. + * + * Note: call this before wc_XmssKey_Sign or Verify so you know the + * length of the required signature buffer. + * + * @param [in] key XMSS key to use to sign. + * @param [out] len The length of the signature in bytes. + * + * @return 0 on success. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + * */ +int wc_XmssKey_GetSigLen(const XmssKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) && + (key->state != WC_XMSS_STATE_PARMSET)) { + ret = BAD_STATE_E; + } + + if (ret == 0) { + /* Return the calculated signature length. */ + *len = key->params->sig_len; + } + + return ret; +} + +/* Verify the signature using the XMSS public key. + * + * Requires that XMSS parameters have been set with + * wc_XmssKey_SetParamStr, and that a public key is available + * from importing or MakeKey(). + * + * Call wc_XmssKey_GetSigLen() before this function to determine + * length of the signature buffer. + * + * @param [in] key XMSS key to use to verify. + * @param [in] sig Signature to verify. + * @param [in] sigLen Size of signature in bytes. + * @param [in] m Message to verify. + * @param [in] mLen Length of the message in bytes. + * + * @return 0 on success. + * @return SIG_VERIFY_E when signature did not verify message. + * @return BAD_FUNC_ARG when a parameter is NULL. + * @return BAD_STATE_E when wrong state for operation. + * @return BUFFER_E when sigLen is too small. + */ +int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen, + const byte* m, int mLen) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (sig == NULL) || (m == NULL)) { + ret = BAD_FUNC_ARG; + } + /* Validate state. */ + if ((ret == 0) && (key->state != WC_XMSS_STATE_OK) && + (key->state != WC_XMSS_STATE_VERIFYONLY)) { + /* XMSS key not ready for verification. Param str must be + * set first, and Reload() called. */ + WOLFSSL_MSG("error: XMSS key not ready for verification"); + ret = BAD_STATE_E; + } + /* Check the signature is the big enough. */ + if ((ret == 0) && (sigLen < key->params->sig_len)) { + /* Signature buffer too small. */ + ret = BUFFER_E; + } + + if (ret == 0) { + #ifdef WOLFSSL_SMALL_STACK + XmssState* state; + #else + XmssState state[1]; + #endif + + #ifdef WOLFSSL_SMALL_STACK + state = XMALLOC(sizeof(XmssState), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (state == NULL) { + ret = MEMORY_E; + } + if (ret == 0) + #endif + { + /* Initialize state for use in verification. */ + ret = wc_xmss_state_init(state, key->params); + if (ret == 0) { + /* Verify using either XMSS^MT function as it works for both. */ + ret = wc_xmssmt_verify(state, m, mLen, sig, key->pk); + /* Free state after use. */ + wc_xmss_state_free(state); + } + #ifdef WOLFSSL_SMALL_STACK + XFREE(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + } + + return ret; +} + +#endif /* WOLFSSL_HAVE_XMSS */ diff --git a/wolfcrypt/src/wc_xmss_impl.c b/wolfcrypt/src/wc_xmss_impl.c index 4a91f9969a..45daa704cd 100644 --- a/wolfcrypt/src/wc_xmss_impl.c +++ b/wolfcrypt/src/wc_xmss_impl.c @@ -19,8 +19,4320 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +/* Based on: + * o RFC 8391 - XMSS: eXtended Merkle Signature Scheme + * o [HDSS] "Hash-based Digital Signature Schemes", Buchmann, Dahmen and Szydlo + * from "Post Quantum Cryptography", Springer 2009. + * o [OPX] "Optimal Parameters for XMSS^MT", Hulsing, Rausch and Buchmann + * + * TODO: "Simple and Memory-efficient Signature Generation of XMSS^MT" + * (https://ece.engr.uvic.ca/~raltawy/SAC2021/9.pdf) + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + #include +#include +#include + +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#if defined(WOLFSSL_HAVE_XMSS) + +/* Indices into Hash Address. */ +#define XMSS_ADDR_LAYER 0 +#define XMSS_ADDR_TREE_HI 1 +#define XMSS_ADDR_TREE 2 +#define XMSS_ADDR_TYPE 3 +#define XMSS_ADDR_OTS 4 +#define XMSS_ADDR_LTREE 4 +#define XMSS_ADDR_TREE_ZERO 4 +#define XMSS_ADDR_CHAIN 5 +#define XMSS_ADDR_TREE_HEIGHT 5 +#define XMSS_ADDR_HASH 6 +#define XMSS_ADDR_TREE_INDEX 6 +#define XMSS_ADDR_KEY_MASK 7 + +/* Types of hash addresses. */ +#define WC_XMSS_ADDR_TYPE_OTS 0 +#define WC_XMSS_ADDR_TYPE_LTREE 1 +#define WC_XMSS_ADDR_TYPE_TREE 2 + +/* Byte to include in hash to create unique sequence. */ +#define XMSS_HASH_PADDING_F 0 +#define XMSS_HASH_PADDING_H 1 +#define XMSS_HASH_PADDING_HASH 2 +#define XMSS_HASH_PADDING_PRF 3 +#define XMSS_HASH_PADDING_PRF_KEYGEN 4 + +/* Fixed parameter values. */ +#define XMSS_WOTS_W 16 +#define XMSS_WOTS_LOG_W 4 +#define XMSS_WOTS_LEN2 3 +#define XMSS_CSUM_SHIFT 4 +#define XMSS_CSUM_LEN 2 + +/* Length of the message to the PRF. */ +#define XMSS_PRF_M_LEN 32 + +/* Length of index encoding when doing XMSS. */ +#define XMSS_IDX_LEN 4 + +/* Size of the N when using SHA-256 and 32 byte padding. */ +#define XMSS_SHA256_32_N WC_SHA256_DIGEST_SIZE +/* Size of the padding when using SHA-256 and 32 byte padding. */ +#define XMSS_SHA256_32_PAD_LEN 32 + +/* Calculate PRF data length for parameters. */ +#define XMSS_HASH_PRF_DATA_LEN(params) \ + ((params)->pad_len + (params)->n + WC_XMSS_ADDR_LEN) +/* PRF data length when using SHA-256 with 32 byte padding. */ +#define XMSS_HASH_PRF_DATA_LEN_SHA256_32 \ + (XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN) + +/* Calculate chain hash data length for parameters. */ +#define XMSS_CHAIN_HASH_DATA_LEN(params) \ + ((params)->pad_len + 2 * (params)->n) +/* Chain hash data length when using SHA-256 with 32 byte padding. */ +#define XMSS_CHAIN_HASH_DATA_LEN_SHA256_32 \ + (XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N) + +/* Calculate rand hash data length for parameters. */ +#define XMSS_RAND_HASH_DATA_LEN(params) \ + ((params)->pad_len + 3 * (params)->n) +/* Rand hash data length when using SHA-256 with 32 byte padding. */ +#define XMSS_RAND_HASH_DATA_LEN_SHA256_32 \ + (XMSS_SHA256_32_PAD_LEN + 3 * XMSS_SHA256_32_N) + +/* Encode pad value into byte array. Front fill with 0s. + * + * RFC 8391: 2.4 + * + * @param [in] n Number to encode. + * @param [out] a Array to hold encoding. + * @param [in] l Length of array. + */ +#define XMSS_PAD_ENC(n, a, l) \ +do { \ + XMEMSET(a, 0, l); \ + (a)[(l) - 1] = (n); \ +} while (0) + + +/******************************************** + * Index 32/64 bits + ********************************************/ + +/* Index of 32 or 64 bits. */ +typedef union wc_Idx { +#if WOLFSSL_XMSS_MAX_HEIGHT > 32 + /* 64-bit representation. */ + w64wrapper u64; +#endif +#if WOLFSSL_XMSS_MIN_HEIGHT <= 32 + /* 32-bit representation. */ + word32 u32; +#endif +} wc_Idx; + +#if WOLFSSL_XMSS_MAX_HEIGHT > 32 +/* Set index to zero. + * + * Index is up to 64-bits. + * + * @param [out] idx 32/64-bit index to zero. + */ +#define WC_IDX_ZERO(idx) w64Zero(&(idx).u64) +#else +/* Set index to zero. + * + * Index is no more than 32-bits. + * + * @param [out] idx 32/64-bit index to zero. + */ +#define WC_IDX_ZERO(idx) idx.u32 = 0 +#endif + +#if WOLFSSL_XMSS_MAX_HEIGHT > 32 +/* Decode 64-bit index. + * + * @param [out] i Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define IDX64_DECODE(i, c, a, ret) \ + if ((c) == 5) { \ + word32 t; \ + ato32((a) + 1, &t); \ + (i) = w64From32((a)[0], t); \ + } \ + else if ((c) == 8) { \ + ato64(a, &(i)); \ + } + +/* Decode 64-bit index. + * + * @param [out] i Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define XMSS_IDX64_DECODE(i, c, a, ret) \ +do { \ + IDX64_DECODE(i, c, a, ret) \ + else { \ + (ret) = NOT_COMPILED_IN; \ + } \ +} while (0) + +/* Check whether index is valid. + * + * @param [in] i Index to check. + * @param [in] c Count of bytes i was encoded in. + * @param [in] h Full tree Height. + */ +#define IDX64_INVALID(i, c, h) \ + ((w64GetHigh32(w64Add32(i, 1, NULL)) >> ((h) - 32)) != 0) + +/* Set 64-bit index as hash address value for tree. + * + * @param [in] i Index to set. + * @param [in] c Count of bytes to encode into. + * @param [in] h Height of tree. + * @param [out] a Hash address to encode into. + * @param [out] l Index of leaf. + */ +#define IDX64_SET_ADDR_TREE(i, c, h, a, l) \ + if ((c) > 4) { \ + (l) = w64GetLow32(i) & (((word32)1 << (h)) - 1);\ + (i) = w64ShiftRight(i, h); \ + (a)[XMSS_ADDR_TREE_HI] = w64GetHigh32(i); \ + (a)[XMSS_ADDR_TREE] = w64GetLow32(i); \ + } +#endif /* WOLFSSL_XMSS_MAX_HEIGHT > 32 */ + +#if WOLFSSL_XMSS_MIN_HEIGHT <= 32 +/* Decode 32-bit index. + * + * @param [out] i Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define IDX32_DECODE(i, c, a, ret) \ + if ((c) == 4) { \ + ato32(a, &(i)); \ + } \ + else if ((c) == 3) { \ + ato24(a, &(i)); \ + } + +/* Decode 32-bit index. + * + * @param [out] i Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define XMSS_IDX32_DECODE(i, c, a, ret) \ +do { \ + IDX32_DECODE(i, c, a, ret) \ + else { \ + (ret) = NOT_COMPILED_IN; \ + } \ +} while (0) + +/* Check whether 32-bit index is valid. + * + * @param [in] i Index to check. + * @param [in] c Count of bytes i was encoded in. + * @param [in] h Full tree Height. + */ +#define IDX32_INVALID(i, c, h) \ + ((((i) + 1) >> (h)) != 0) + +/* Set 32-bit index as hash address value for tree. + * + * @param [in] i Index to set. + * @param [in] c Count of bytes to encode into. + * @param [in] h Height of tree. + * @param [out] a Hash address to encode into. + * @param [out] l Index of leaf. + */ +#define IDX32_SET_ADDR_TREE(i, c, h, a, l) \ + if ((c) <= 4) { \ + (l) = ((i) & ((1 << (h)) - 1)); \ + (i) >>= params->sub_h; \ + (a)[XMSS_ADDR_TREE] = (i); \ + } + +#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */ + +#if (WOLFSSL_XMSS_MAX_HEIGHT > 32) && (WOLFSSL_XMSS_MIN_HEIGHT <= 32) + +/* Decode 32/64-bit index. + * + * @param [out] idx Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define WC_IDX_DECODE(idx, c, a, ret) \ +do { \ + IDX64_DECODE((idx).u64, c, a, ret) \ + else \ + IDX32_DECODE((idx).u32, c, a, ret) \ + else { \ + (ret) = NOT_COMPILED_IN; \ + } \ +} while (0) + +/* Check whether index is valid. + * + * @param [in] i Index to check. + * @param [in] c Count of bytes i was encoded in. + * @param [in] h Full tree Height. + */ +#define WC_IDX_INVALID(i, c, h) \ + ((((c) > 4) && IDX64_INVALID((i).u64, c, h)) || \ + (((c) <= 4) && IDX32_INVALID((i).u32, c, h))) + +/* Set 32/64-bit index as hash address value for tree. + * + * @param [in] i Index to set. + * @param [in] c Count of bytes to encode into. + * @param [in] h Height of tree. + * @param [out] a Hash address to encode into. + * @param [out] l Index of leaf. + */ +#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \ +do { \ + IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l) \ + else \ + IDX32_SET_ADDR_TREE((idx).u32, c, h, a, l) \ +} while (0) + +#elif WOLFSSL_XMSS_MAX_HEIGHT > 32 + +/* Decode 64-bit index. + * + * @param [out] idx Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define WC_IDX_DECODE(idx, c, a, ret) \ +do { \ + IDX64_DECODE((idx).u64, c, a, ret) \ +} while (0) + +/* Check whether index is valid. + * + * @param [in] i Index to check. + * @param [in] c Count of bytes i was encoded in. + * @param [in] h Full tree Height. + */ +#define WC_IDX_INVALID(i, c, h) \ + IDX64_INVALID((i).u64, c, h) + +/* Set 64-bit index as hash address value for tree. + * + * @param [in] i Index to set. + * @param [in] c Count of bytes to encode into. + * @param [in] h Height of tree. + * @param [out] a Hash address to encode into. + * @param [out] l Index of leaf. + */ +#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \ +do { \ + IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l) \ +} while (0) + +#else + +/* Decode 32-bit index. + * + * @param [out] idx Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + * @param [out] ret Return value. + */ +#define WC_IDX_DECODE(idx, c, a, ret) \ +do { \ + IDX32_DECODE((idx).u32, c, a, ret) \ + else { \ + (ret) = NOT_COMPILED_IN; \ + } \ +} while (0) + +/* Check whether index is valid. + * + * @param [in] i Index to check. + * @param [in] c Count of bytes i was encoded in. + * @param [in] h Full tree Height. + */ +#define WC_IDX_INVALID(i, c, h) \ + IDX32_INVALID((i).u32, c, h) + +/* Set 32-bit index as hash address value for tree. + * + * @param [in] i Index to set. + * @param [in] c Count of bytes to encode into. + * @param [in] h Height of tree. + * @param [out] a Hash address to encode into. + * @param [out] l Index of leaf. + */ +#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \ +do { \ + IDX32_SET_ADDR_TREE(idx.u32, c, h, a, l) \ +} while (0) + +#endif /* (WOLFSSL_XMSS_MAX_HEIGHT > 32) && (WOLFSSL_XMSS_MIN_HEIGHT <= 32) */ + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY +/* Update index by adding one to big-endian encoded value. + * + * @param [in, out] a Array index is encoded in. + * @param [in] l Length of encoded index. + */ +static void wc_idx_update(unsigned char* a, word8 l) +{ + sword8 i; + + for (i = l - 1; i >= 0; i--) { + if ((++a[i]) != 0) { + break; + } + } +} + +/* Copy index from source buffer to destination buffer. + * + * Index is put into the front of the destination buffer with the length of the + * source. + * + * @param [in] s Source buffer. + * @param [in] sl Length of index in source. + * @param [in, out] d Destination buffer. + * @param [in] dl Length of destination buffer. + */ +static void wc_idx_copy(const unsigned char* s, word8 sl, unsigned char* d, + word8 dl) +{ + XMEMCPY(d, s, sl); + XMEMSET(d + sl, 0, dl - sl); +} +#endif + +/******************************************** + * Hash Address. + ********************************************/ + +/* Set the hash address based on subtree. + * + * @param [out] a Hash address. + * @param [in] s Subtree hash address. + * @param [in] t Type of hash address. + */ +#define XMSS_ADDR_SET_SUBTREE(a, s, t) \ +do { \ + (a)[XMSS_ADDR_LAYER] = (s)[XMSS_ADDR_LAYER]; \ + (a)[XMSS_ADDR_TREE_HI] = (s)[XMSS_ADDR_TREE_HI]; \ + (a)[XMSS_ADDR_TREE] = (s)[XMSS_ADDR_TREE]; \ + (a)[XMSS_ADDR_TYPE] = (t); \ + XMEMSET((a) + 4, 0, sizeof(a) - 4 * sizeof(*(a)));\ +} while (0) + +/* Set the OTS hash address based on subtree. + * + * @param [out] a Hash address. + * @param [in] s Subtree hash address. + */ +#define XMSS_ADDR_OTS_SET_SUBTREE(a, s) \ + XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_OTS) +/* Set the L-tree address based on subtree. + * + * @param [out] a Hash address. + * @param [in] s Subtree hash address. + */ +#define XMSS_ADDR_LTREE_SET_SUBTREE(a, s) \ + XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_LTREE) +/* Set the hash tree address based on subtree. + * + * @param [out] a Hash address. + * @param [in] s Subtree hash address. + */ +#define XMSS_ADDR_TREE_SET_SUBTREE(a, s) \ + XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_TREE) + +#ifdef LITTLE_ENDIAN_ORDER + +/* Set a byte value into a word of an encoded address. + * + * @param [in, out] a Encoded hash address. + * @param [in] i Index of word. + * @param [in] b Byte to set. + */ +#define XMSS_ADDR_SET_BYTE(a, i, b) \ + ((word32*)(a))[i] = (word32)(b) << 24 + +#else + +/* Set a byte value into a word of an encoded address. + * + * @param [in, out] a Encoded hash address. + * @param [in] i Index of word. + * @param [in] b Byte to set. + */ +#define XMSS_ADDR_SET_BYTE(a, i, b) \ + ((word32*)(a))[i] = (b) + +#endif /* LITTLE_ENDIAN_ORDER */ + +/* Convert hash address to bytes. + * + * @param [out] bytes Array to encode into. + * @param [in] addr Hash address. + */ +static void wc_xmss_addr_encode(const HashAddress addr, byte* bytes) +{ + c32toa((addr)[0], (bytes) + (0 * 4)); + c32toa((addr)[1], (bytes) + (1 * 4)); + c32toa((addr)[2], (bytes) + (2 * 4)); + c32toa((addr)[3], (bytes) + (3 * 4)); + c32toa((addr)[4], (bytes) + (4 * 4)); + c32toa((addr)[5], (bytes) + (5 * 4)); + c32toa((addr)[6], (bytes) + (6 * 4)); + c32toa((addr)[7], (bytes) + (7 * 4)); +} + +/******************************************** + * HASHING + ********************************************/ + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \ + !defined(WC_XMSS_FULL_HASH) + +/* Set hash data and length into SHA-256 digest. + * + * @param [in, out] state XMSS/MT state with SHA-256 digest. + * @param [in] data Data to add to hash. + * @param [in] len Number of bytes in data. + * Must be less than a block. + * @param [in] total_len Number of bytes updated so far. + */ +#define XMSS_SHA256_SET_DATA(state, data, len, total_len) \ +do { \ + XMEMCPY((state)->digest.sha256.buffer, data, len); \ + (state)->digest.sha256.buffLen = (len); \ + (state)->digest.sha256.loLen = (total_len); \ +} while (0) + +/* Save the SHA-256 state to cache. + * + * @param [in, out] state XMSS/MT state with SHA-256 digest and state cache. + */ +#define XMSS_SHA256_STATE_CACHE(state) \ + (state)->dgst_state[0] = (state)->digest.sha256.digest[0]; \ + (state)->dgst_state[1] = (state)->digest.sha256.digest[1]; \ + (state)->dgst_state[2] = (state)->digest.sha256.digest[2]; \ + (state)->dgst_state[3] = (state)->digest.sha256.digest[3]; \ + (state)->dgst_state[4] = (state)->digest.sha256.digest[4]; \ + (state)->dgst_state[5] = (state)->digest.sha256.digest[5]; \ + (state)->dgst_state[6] = (state)->digest.sha256.digest[6]; \ + (state)->dgst_state[7] = (state)->digest.sha256.digest[7]; \ + +/* Restore the SHA-256 state from cache and set length. + * + * @param [in, out] state XMSS/MT state with SHA-256 digest and state cache. + * @param [in] len Number of bytes of data hashed so far. + */ +#define XMSS_SHA256_STATE_RESTORE(state, len) \ +do { \ + (state)->digest.sha256.digest[0] = (state)->dgst_state[0]; \ + (state)->digest.sha256.digest[1] = (state)->dgst_state[1]; \ + (state)->digest.sha256.digest[2] = (state)->dgst_state[2]; \ + (state)->digest.sha256.digest[3] = (state)->dgst_state[3]; \ + (state)->digest.sha256.digest[4] = (state)->dgst_state[4]; \ + (state)->digest.sha256.digest[5] = (state)->dgst_state[5]; \ + (state)->digest.sha256.digest[6] = (state)->dgst_state[6]; \ + (state)->digest.sha256.digest[7] = (state)->dgst_state[7]; \ + (state)->digest.sha256.loLen = (len); \ +} while (0) + +/* Restore the SHA-256 state from cache and set data and length. + * + * @param [in, out] state XMSS/MT state with SHA-256 digest and cache. + * @param [in] data Data to add to hash. + * @param [in] len Number of bytes in data. + * Must be less than a block. + * @param [in] total_len Number of bytes updated so far. + */ +#define XMSS_SHA256_STATE_RESTORE_DATA(state, data, len, total_len) \ +do { \ + (state)->digest.sha256.digest[0] = (state)->dgst_state[0]; \ + (state)->digest.sha256.digest[1] = (state)->dgst_state[1]; \ + (state)->digest.sha256.digest[2] = (state)->dgst_state[2]; \ + (state)->digest.sha256.digest[3] = (state)->dgst_state[3]; \ + (state)->digest.sha256.digest[4] = (state)->dgst_state[4]; \ + (state)->digest.sha256.digest[5] = (state)->dgst_state[5]; \ + (state)->digest.sha256.digest[6] = (state)->dgst_state[6]; \ + (state)->digest.sha256.digest[7] = (state)->dgst_state[7]; \ + XMSS_SHA256_SET_DATA(state, data, len, total_len); \ +} while (0) + +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 && !WC_XMSS_FULL_HASH */ + +/* Hash the data into output buffer. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] in Data to digest. + * @param [in] inlen Length of data to digest in bytes. + * @param [out] out Buffer to put digest into. + */ +static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in, + word32 inlen, byte* out) +{ + int ret; + const XmssParams* params = state->params; -#ifdef WOLFSSL_HAVE_XMSS - #error "Contact wolfSSL to get the implementation of this file" +#ifdef WC_XMSS_SHA256 + /* Full SHA-256 digest. */ + if ((params->hash == WC_HASH_TYPE_SHA256) && + (params->n == WC_SHA256_DIGEST_SIZE)) { + ret = wc_Sha256Update(&state->digest.sha256, in, inlen); + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, out); + } + } +#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192 + /* Partial SHA-256 digest. */ + else if (params->hash == WC_HASH_TYPE_SHA256) { + byte buf[WC_SHA256_DIGEST_SIZE]; + ret = wc_Sha256Update(&state->digest.sha256, in, inlen); + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, buf); + } + if (ret == 0) { + XMEMCPY(out, buf, params->n); + } + } #endif + else +#endif /* WC_XMSS_SHA256 */ +#ifdef WC_XMSS_SHA512 + /* Full SHA-512 digest. */ + if (params->hash == WC_HASH_TYPE_SHA512) { + ret = wc_Sha512Update(&state->digest.sha512, in, inlen); + if (ret == 0) { + ret = wc_Sha512Final(&state->digest.sha512, out); + } + } + else +#endif /* WC_XMSS_SHA512 */ +#ifdef WC_XMSS_SHAKE128 + /* Digest with SHAKE-128. */ + if (params->hash == WC_HASH_TYPE_SHAKE128) { + ret = wc_Shake128_Update(&state->digest.shake, in, inlen); + if (ret == 0) { + ret = wc_Shake128_Final(&state->digest.shake, out, params->n); + } + } + else +#endif /* WC_XMSS_SHAKE128 */ +#ifdef WC_XMSS_SHAKE256 + /* Digest with SHAKE-256. */ + if (params->hash == WC_HASH_TYPE_SHAKE256) { + ret = wc_Shake256_Update(&state->digest.shake, in, inlen); + if (ret == 0) { + ret = wc_Shake256_Final(&state->digest.shake, out, params->n); + } + } + else +#endif /* WC_XMSS_SHAKE256 */ + { + /* Unsupported digest function. */ + ret = NOT_COMPILED_IN; + } + + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +} + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) +#ifndef WC_XMSS_FULL_HASH +/* Chain hashing. + * + * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM = PRF(SEED, ADRS); + * tmp = F(KEY, tmp XOR BM); + * return tmp; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] tmp Temporary buffer holding chain data. + * @param [in] addr Hash address as a byte array. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp, + byte* addr, byte* hash) +{ + /* Offsets into chain hash data. */ + byte* pad = state->buf; + byte* key = pad + XMSS_SHA256_32_PAD_LEN; + byte* bm = key + XMSS_SHA256_32_N; + int ret; + + /* Calculate n-byte key - KEY. */ + ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, key); + + if (ret == 0) { + /* Calculate n-byte bit mask - BM. */ + addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm); + } + + if (ret == 0) { + /* Function padding set in caller. */ + xorbuf(bm, tmp, XMSS_SHA256_32_N); + ret = wc_Sha256Update(&state->digest.sha256, state->buf, + XMSS_CHAIN_HASH_DATA_LEN_SHA256_32); + } + if (ret == 0) { + /* Calculate the chain hash. */ + ret = wc_Sha256Final(&state->digest.sha256, hash); + } + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +} +#else +/* Chain hashing. + * + * Padding, seed, addr for PRF set by caller into prf_buf. + * + * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM = PRF(SEED, ADRS); + * tmp = F(KEY, tmp XOR BM); + * return tmp; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] tmp Temporary buffer holding chain data. + * @param [out] out Buffer to hold hash. + */ +static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp, + byte* hash) +{ + byte* addr = state->prf_buf + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N; + /* Offsets into chain hash data. */ + byte* pad = state->buf; + byte* key = pad + XMSS_SHA256_32_PAD_LEN; + byte* bm = key + XMSS_SHA256_32_N; + + /* Calculate n-byte key - KEY. */ + ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key); + /* Calculate the n-byte mask. */ + addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm); + + /* Function padding set in caller. */ + xorbuf(bm, tmp, XMSS_SHA256_32_N); + /* Calculate the chain hash. */ + wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN_SHA256_32, hash); +} +#endif /* !WC_XMSS_FULL_HASH */ +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + +/* Chain hashing. + * + * Padding, seed, addr for PRF set by caller into prf_buf. + * + * RFC 8391: 3.1.2, Algorithm 2: chain - Chaining Function + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM = PRF(SEED, ADRS); + * tmp = F(KEY, tmp XOR BM); + * return tmp; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] tmp Temporary buffer holding chain data. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_chain_hash(XmssState* state, const byte* tmp, byte* hash) +{ + const XmssParams* params = state->params; + byte* addr = state->prf_buf + params->pad_len + params->n; + /* Offsets into chain hash data. */ + byte* pad = state->buf; + byte* key = pad + params->pad_len; + byte* bm = key + params->n; + + /* Calculate n-byte key - KEY. */ + ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), key); + /* Calculate n-byte bit mask - BM. */ + addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), bm); + + /* Function padding set in caller. */ + xorbuf(bm, tmp, params->n); + /* Calculate the chain hash. */ + wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN(params), hash); +} + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) +#ifndef WC_XMSS_FULL_HASH +/* Randomized tree hashing. + * + * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM_0 = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(2); + * BM_1 = PRF(SEED, ADRS); + * return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1)); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] data Input data. + * @param [in] addr Hash address. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_rand_hash_sha256_32_prehash(XmssState* state, + const byte* data, HashAddress addr, byte* hash) +{ + int ret; + /* Offsets into rand hash data. */ + byte* pad = state->buf; + byte* key = pad + XMSS_SHA256_32_PAD_LEN; + byte* bm0 = key + XMSS_SHA256_32_N; + byte* bm1 = bm0 + XMSS_SHA256_32_N; + byte addr_buf[WC_XMSS_ADDR_LEN]; + + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, key); + + /* Calculate n-byte mask - BM_0. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm0); + } + + /* Calculate n-byte mask - BM_1. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm1); + } + + if (ret == 0) { + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN); + /* XOR into bm0 and bm1. */ + xorbuf(bm0, data, XMSS_SHA256_32_N * 2); + ret = wc_Sha256Update(&state->digest.sha256, state->buf, + XMSS_RAND_HASH_DATA_LEN_SHA256_32); + } + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, hash); + } + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +} +#endif /* !WC_XMSS_FULL_HASH */ + +/* Randomized tree hashing. + * + * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM_0 = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(2); + * BM_1 = PRF(SEED, ADRS); + * return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1)); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] data Input data. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_rand_hash_sha256_32(XmssState* state, const byte* data, + const byte* pk_seed, HashAddress addr, byte* hash) +{ + byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN + + XMSS_SHA256_32_N; + /* Offsets into rand hash data. */ + byte* pad = state->buf; + byte* key = pad + XMSS_SHA256_32_PAD_LEN; + byte* bm0 = key + XMSS_SHA256_32_N; + byte* bm1 = bm0 + XMSS_SHA256_32_N; +#ifndef WC_XMSS_FULL_HASH + int ret; + + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, + XMSS_SHA256_32_N); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf, + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N); + if (ret == 0) { + /* Copy state after first 64 bytes. */ + XMSS_SHA256_STATE_CACHE(state); + /* Copy in remaining 32 bytes to buffer. */ + XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, key); + } + + /* Calculate n-byte mask - BM_0. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm0); + } + + /* Calculate n-byte mask - BM_1. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm1); + } + + if (ret == 0) { + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN); + /* XOR into bm0 and bm1. */ + xorbuf(bm0, data, 2 * XMSS_SHA256_32_N); + ret = wc_Sha256Update(&state->digest.sha256, state->buf, + XMSS_RAND_HASH_DATA_LEN_SHA256_32); + } + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, hash); + } + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +#else + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, + XMSS_SHA256_32_N); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key); + /* Calculate n-byte mask - BM_0. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0); + /* Calculate n-byte mask - BM_1. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1); + + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN); + xorbuf(bm0, data, 2 * XMSS_SHA256_32_N); + wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash); +#endif /* WC_XMSS_FULL_HASH */ +} +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + +/* Randomized tree hashing. + * + * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM_0 = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(2); + * BM_1 = PRF(SEED, ADRS); + * return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1)); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] data Input data. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_rand_hash(XmssState* state, const byte* data, + const byte* pk_seed, HashAddress addr, byte* hash) +{ + const XmssParams* params = state->params; + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + wc_xmss_rand_hash_sha256_32(state, data, pk_seed, addr, hash); + } + else +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + { + byte* addr_buf = state->prf_buf + params->pad_len + params->n; + /* Offsets into rand hash data. */ + byte* pad = state->buf; + byte* key = pad + params->pad_len; + byte* bm0 = key + params->n; + byte* bm1 = bm0 + params->n; + const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN; + + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + wc_xmss_hash(state, state->prf_buf, len, key); + /* Calculate n-byte mask - BM_0. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, len, bm0); + /* Calculate n-byte mask - BM_1. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + wc_xmss_hash(state, state->prf_buf, len, bm1); + + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len); + xorbuf(bm0, data, 2 * params->n); + wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n, + hash); + } +} + +#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY) +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) +/* Randomized tree hashing. + * + * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM_0 = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(2); + * BM_1 = PRF(SEED, ADRS); + * return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1)); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] left First half of data. + * @param [in] right Second half of data. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_rand_hash_lr_sha256_32(XmssState* state, const byte* left, + const byte* right, const byte* pk_seed, HashAddress addr, byte* hash) +{ + byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN + + XMSS_SHA256_32_N; + /* Offsets into rand hash data. */ + byte* pad = state->buf; + byte* key = pad + XMSS_SHA256_32_PAD_LEN; + byte* bm0 = key + XMSS_SHA256_32_N; + byte* bm1 = bm0 + XMSS_SHA256_32_N; +#ifndef WC_XMSS_FULL_HASH + int ret; + + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, + XMSS_SHA256_32_N); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf, + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N); + if (ret == 0) { + /* Copy state after first 64 bytes. */ + XMSS_SHA256_STATE_CACHE(state); + /* Copy in remaining 32 bytes to buffer. */ + XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, key); + } + + /* Calculate n-byte mask - BM_0. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm0); + } + + /* Calculate n-byte mask - BM_1. */ + if (ret == 0) { + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + /* Copy back state after first 64 bytes. */ + XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN, + XMSS_HASH_PRF_DATA_LEN_SHA256_32); + /* Calculate hash. */ + ret = wc_Sha256Final(&state->digest.sha256, bm1); + } + + if (ret == 0) { + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN); + /* XOR into bm0 and bm1. */ + XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N); + XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N); + xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N); + ret = wc_Sha256Update(&state->digest.sha256, state->buf, + XMSS_RAND_HASH_DATA_LEN_SHA256_32); + } + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, hash); + } + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +#else + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, XMSS_SHA256_32_N); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key); + /* Calculate n-byte mask - BM_0. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0); + /* Calculate n-byte mask - BM_1. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1); + + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN); + XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N); + XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N); + xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N); + wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash); +#endif /* WC_XMSS_FULL_HASH */ +} +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ +/* Randomized tree hashing - left and right separate parameters. + * + * RFC 8391: 4.1.4, Algorithm 7: RAND_HASH + * ... + * ADRS.setKeyAndMask(0); + * KEY = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(1); + * BM_0 = PRF(SEED, ADRS); + * ADRS.setKeyAndMask(2); + * BM_1 = PRF(SEED, ADRS); + * return H(KEY, (LEFT XOR BM_0) || (RIGHT XOR BM_1)); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] left First half of data. + * @param [in] right Second half of data. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_rand_hash_lr(XmssState* state, const byte* left, + const byte* right, const byte* pk_seed, HashAddress addr, byte* hash) +{ + const XmssParams* params = state->params; + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + wc_xmss_rand_hash_lr_sha256_32(state, left, right, pk_seed, addr, hash); + } + else +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + { + byte* addr_buf = state->prf_buf + params->pad_len + params->n; + /* Offsets into rand hash data. */ + byte* pad = state->buf; + byte* key = pad + params->pad_len; + byte* bm0 = key + params->n; + byte* bm1 = bm0 + params->n; + const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN; + + /* Encode padding byte for PRF. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len); + /* Append public seed for PRF. */ + XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n); + + /* Set key mask to initial value and append encoding. */ + addr[XMSS_ADDR_KEY_MASK] = 0; + wc_xmss_addr_encode(addr, addr_buf); + + /* Calculate n-byte key - KEY. */ + wc_xmss_hash(state, state->prf_buf, len, key); + /* Calculate n-byte mask - BM_0. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1; + wc_xmss_hash(state, state->prf_buf, len, bm0); + /* Calculate n-byte mask - BM_1. */ + addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2; + wc_xmss_hash(state, state->prf_buf, len, bm1); + + XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len); + XMEMCPY(state->prf_buf, left, params->n); + XMEMCPY(state->prf_buf + params->n, right, params->n); + xorbuf(bm0, state->prf_buf, 2 * params->n); + wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n, + hash); + } +} +#endif /* !WOLFSSL_WC_XMSS_SMALL || WOLFSSL_XMSS_VERIFY_ONLY */ + +/* Compute message hash from the random r, root, index and message. + * + * RFC 8391: 4.1.9, Algorithm 12: XMSS_sign + * ... + * byte[n] M' = H_msg(r || getRoot(SK) || (toByte(idx_sig, n)), M); + * RFC 8391: 5.1 + * H_msg: SHA2-256(toByte(2, 32) || KEY || M) + * H_msg: SHA2-512(toByte(2, 64) || KEY || M) + * H_msg: SHAKE128(toByte(2, 32) || KEY || M, 256) + * H_msg: SHAKE256(toByte(2, 64) || KEY || M, 512) + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] random Random value of n bytes. + * @param [in] root Public root. + * @param [in] idx Buffer holding encoded index. + * @param [in] idx_len Length of encoded index in bytes. + * @param [in] m Message to hash. + * @param [in] mlen Length of message. + * @param [out] hash Buffer to hold hash. + */ +static void wc_xmss_hash_message(XmssState* state, const byte* random, + const byte* root, const byte* idx, word8 idx_len, const byte* m, + word32 mlen, byte* hash) +{ + int ret; + const XmssParams* params = state->params; + word32 padKeyLen = params->pad_len + 3 * params->n; + /* Offsets into message hash data. */ + byte* padKey = state->buf; + byte* pad = padKey; + byte* key = pad + params->pad_len; + byte* root_sk = key + params->n; + byte* idx_sig = root_sk + params->n; + + /* Set prefix data before message. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_HASH, pad, params->pad_len); + XMEMCPY(key, random, params->n); + XMEMCPY(root_sk, root, params->n); + XMEMSET(idx_sig, 0, params->n - idx_len); + XMEMCPY(idx_sig + params->n - idx_len, idx, idx_len); + + /* Hash the padding and key first. */ +#ifdef WC_XMSS_SHA256 + if (params->hash == WC_HASH_TYPE_SHA256) { + ret = wc_Sha256Update(&state->digest.sha256, padKey, padKeyLen); + } + else +#endif /* WC_XMSS_SHA256 */ +#ifdef WC_XMSS_SHA512 + if (params->hash == WC_HASH_TYPE_SHA512) { + ret = wc_Sha512Update(&state->digest.sha512, padKey, padKeyLen); + } + else +#endif /* WC_XMSS_SHA512 */ +#ifdef WC_XMSS_SHAKE128 + if (params->hash == WC_HASH_TYPE_SHAKE128) { + ret = wc_Shake128_Update(&state->digest.shake, padKey, padKeyLen); + } + else +#endif /* WC_XMSS_SHAKE128 */ +#ifdef WC_XMSS_SHAKE256 + if (params->hash == WC_HASH_TYPE_SHAKE256) { + ret = wc_Shake256_Update(&state->digest.shake, padKey, padKeyLen); + } + else +#endif /* WC_XMSS_SHAKE256 */ + { + /* Unsupported digest function. */ + ret = NOT_COMPILED_IN; + } + if (ret == 0) { + /* Generate hash of message - M'. */ + wc_xmss_hash(state, m, mlen, hash); + } + else if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +} + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + +/* Compute PRF with key and message. + * + * RFC 8391: 5.1 + * PRF: SHA2-256(toByte(3, 32) || KEY || M) + * PRF: SHA2-512(toByte(3, 64) || KEY || M) + * PRF: SHAKE128(toByte(3, 32) || KEY || M, 256) + * PRF: SHAKE256(toByte(3, 64) || KEY || M, 512) + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] key Key used to derive pseudo-random from. + * @param [in] m 32 bytes of data to derive pseudo-random from. + * @param [out] prf Buffer to hold pseudo-random data. + */ +static void wc_xmss_prf(XmssState* state, const byte* key, const byte* m, + byte* prf) +{ + const XmssParams* params = state->params; + byte* pad = state->prf_buf; + byte* key_buf = pad + params->pad_len; + byte* m_buf = key_buf + params->n; + + /* 00[0..pl-1] || 03 || key[0..n-1] || m[0..31] */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len); + XMEMCPY(key_buf, key, params->n); + XMEMCPY(m_buf, m, XMSS_PRF_M_LEN); + + /* Hash the PRF data. */ + wc_xmss_hash(state, state->prf_buf, params->pad_len + params->n + + XMSS_PRF_M_LEN, prf); +} + +#ifdef XMSS_CALL_PRF_KEYGEN +/* Compute PRF for keygen with key and message. + * + * NIST SP 800-208: 5.1, 5.2, 5.3, 5.4 + * PRFkeygen (KEY, M): SHA-256(toByte(4, 32) || KEY || M) + * PRFkeygen (KEY, M): T192(SHA-256(toByte(4, 4) || KEY || M)) + * PRFkeygen (KEY, M): SHAKE256(toByte(4, 32) || KEY || M, 256) + * PRFkeygen (KEY, M): SHAKE256(toByte(4, 4) || KEY || M, 192) + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] key Key of n bytes used to derive pseudo-random from. + * @param [in] m n + 32 bytes of data to derive pseudo-random from. + * @param [out] prf Buffer to hold pseudo-random data. + */ +static void wc_xmss_prf_keygen(XmssState* state, const byte* key, + const byte* m, byte* prf) +{ + const XmssParams* params = state->params; + byte* pad = state->prf_buf; + byte* key_buf = pad + params->pad_len; + byte* m_buf = key_buf + params->n; + + /* 00[0..pl-1] || 04 || key[0..n-1] || m[0..n+31] */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len); + XMEMCPY(key_buf, key, params->n); + XMEMCPY(m_buf, m, params->n + XMSS_PRF_M_LEN); + + /* Hash the PRF keygen data. */ + wc_xmss_hash(state, state->prf_buf, params->pad_len + 2 * params->n + + XMSS_PRF_M_LEN, prf); +} +#endif /* XMSS_CALL_PRF_KEYGEN */ + +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +/******************************************** + * WOTS + ********************************************/ + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) +/* Expand private seed with PRF keygen. + * + * RFC 8391: 4.1.3 + * "the existence of a method getWOTS_SK(SK, i) is assumed" + * NIST SP 800-208: 7.2.1, Algorithm 10' + * ... + * for ( j=0; j < len; j++) { + * ADRS.setChainAddress(j); + * sk[j] = PRFkeygen(S_XMSS, SEED || ADRS); + * } + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk_seed Buffer holding private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address as a byte array. + * @param [out] gen_seed Buffer to hold seeds. + */ +static void wc_xmss_wots_get_wots_sk_sha256_32(XmssState* state, + const byte* sk_seed, const byte* pk_seed, byte* addr, byte* gen_seed) +{ + const XmssParams* params = state->params; + word32 i; + byte* pad = state->prf_buf; + byte* s_xmss = pad + XMSS_SHA256_32_PAD_LEN; + byte* seed = s_xmss + XMSS_SHA256_32_N; + byte* addr_buf = seed + XMSS_SHA256_32_N; + int ret; + + ((word32*)addr)[XMSS_ADDR_CHAIN] = 0; + ((word32*)addr)[XMSS_ADDR_HASH] = 0; + ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0; + + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, XMSS_SHA256_32_PAD_LEN); + XMEMCPY(s_xmss, sk_seed, XMSS_SHA256_32_N); + XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N); + XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN); + +#ifndef WC_XMSS_FULL_HASH + ret = wc_Sha256Update(&state->digest.sha256, pad, XMSS_SHA256_32_PAD_LEN + + XMSS_SHA256_32_N); + if (ret == 0) { + /* Copy state after first 64 bytes. */ + XMSS_SHA256_STATE_CACHE(state); + ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N + + WC_XMSS_ADDR_LEN); + } + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, gen_seed); + } + for (i = 1; (ret == 0) && (i < params->wots_len); i++) { + gen_seed += XMSS_SHA256_32_N; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + XMSS_SHA256_STATE_RESTORE(state, 64); + ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N + + WC_XMSS_ADDR_LEN); + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, gen_seed); + } + } +#else + ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf, + XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN); + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, gen_seed); + } + for (i = 1; (ret == 0) && i < params->wots_len; i++) { + gen_seed += XMSS_SHA256_32_N; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf, + XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN); + if (ret == 0) { + ret = wc_Sha256Final(&state->digest.sha256, gen_seed); + } + } +#endif /* WC_XMSS_FULL_HASH*/ + + if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +} +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + +/* Expand private seed with PRF keygen. + * + * RFC 8391: 4.1.3 + * "the existence of a method getWOTS_SK(SK, i) is assumed" + * NIST SP 800-208: 7.2.1 + * Algorithm 10' + * ... + * for ( j=0; j < len; j++) { + * ADRS.setChainAddress(j); + * sk[j] = PRFkeygen(S_XMSS, SEED || ADRS); + * } + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk_seed Buffer holding private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address as a byte array. + * @param [out] gen_seed Buffer to hold seeds. + */ +static void wc_xmss_wots_get_wots_sk(XmssState* state, const byte* sk_seed, + const byte* pk_seed, byte* addr, byte* gen_seed) +{ + const XmssParams* params = state->params; + word32 i; +#ifdef XMSS_CALL_PRF_KEYGEN + byte* seed = state->buf; + byte* addr_buf = seed + params->n; +#else + byte* pad = state->prf_buf; + byte* s_xmss = pad + params->pad_len; + byte* seed = s_xmss + params->n; + byte* addr_buf = seed + params->n; + const word32 len = params->pad_len + params->n * 2 + WC_XMSS_ADDR_LEN; +#endif /* XMSS_CALL_PRF_KEYGEN */ + + /* Ensure hash address fields are 0. */ + ((word32*)addr)[XMSS_ADDR_CHAIN] = 0; + ((word32*)addr)[XMSS_ADDR_HASH] = 0; + ((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0; + +#ifdef XMSS_CALL_PRF_KEYGEN + /* Copy the seed and address into PRF keygen message buffer. */ + XMEMCPY(seed, pk_seed, params->n); + XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN); + + wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed); + for (i = 1; i < params->wots_len; i++) { + gen_seed += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed); + } +#else + /* Copy the PRF keygen fields into one buffer. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len); + XMEMCPY(s_xmss, sk_seed, params->n); + XMEMCPY(seed, pk_seed, params->n); + XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN); + + /* Fill output with hashes of different chain hash addresses. */ + wc_xmss_hash(state, state->prf_buf, len, gen_seed); + for (i = 1; i < params->wots_len; i++) { + gen_seed += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_hash(state, state->prf_buf, len, gen_seed); + } +#endif /* XMSS_CALL_PRF_KEYGEN */ +} + +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) +/* Chain hashing to calculate node hash. + * + * RFC 8391: 3.1.2, Algorithm 2 - recursive. + * This function is an iterative version. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] data Initial data to hash. + * @param [in] start Starting hash value in hash address. + * @param [in] steps Size of step. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address as a byte array. + * @param [out] hash Chained hash. + */ +static void wc_xmss_chain_sha256_32(XmssState* state, const byte* data, + unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr, + byte* hash) +{ + if (steps > 0) { + word32 i; + byte* pad = state->prf_buf; + byte* seed = pad + XMSS_SHA256_32_PAD_LEN; +#ifndef WC_XMSS_FULL_HASH + int ret; + + /* Set data for PRF hash. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN); + XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N); + + /* Hash first 64 bytes. */ + ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf, + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N); + if (ret == 0) { + /* Copy state after first 64 bytes. */ + XMSS_SHA256_STATE_CACHE(state); + /* Only do this once for all chain hash calls. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, + state->params->pad_len); + + /* Set address. */ + XMSS_ADDR_SET_BYTE(addr, XMSS_ADDR_HASH, start); + wc_xmss_chain_hash_sha256_32(state, data, addr, hash); + /* Iterate 'steps' calls to the hash function. */ + for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) { + addr[XMSS_ADDR_HASH * 4 + 3] = i; + wc_xmss_chain_hash_sha256_32(state, hash, addr, hash); + } + } + else if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } +#else + const XmssParams* params = state->params; + byte* addr_buf = seed + XMSS_SHA256_32_N; + + /* Set data for PRF hash. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN); + XMEMCPY(seed, pk_seed, params->n); + XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN); + + /* Only do this once for all chain hash calls. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len); + + /* Set address. */ + XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start); + wc_xmss_chain_hash_sha256_32(state, data, hash); + /* Iterate 'steps' calls to the hash function. */ + for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) { + addr_buf[XMSS_ADDR_HASH * 4 + 3] = i; + wc_xmss_chain_hash_sha256_32(state, hash, hash); + } +#endif /* !WC_XMSS_FULL_HASH */ + } + else if (hash != data) { + XMEMCPY(hash, data, XMSS_SHA256_32_N); + } +} +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + +/* Chain hashing to calculate node hash. + * + * RFC 8391: 3.1.2, Algorithm 2 - recursive. + * This function is an iterative version. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] data Initial data to hash. + * @param [in] start Starting hash value in hash address. + * @param [in] steps Size of step. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address as a byte array. + * @param [out] hash Chained hash. + */ +static void wc_xmss_chain(XmssState* state, const byte* data, + unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr, + byte* hash) +{ + const XmssParams* params = state->params; + + if (steps > 0) { + word32 i; + byte* pad = state->prf_buf; + byte* seed = pad + params->pad_len; + byte* addr_buf = seed + params->n; + + /* Set data for PRF hash. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len); + XMEMCPY(seed, pk_seed, params->n); + XMEMCPY(addr_buf, addr, 32); + + /* Only do this once for all chain hash calls. */ + XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len); + + /* Set address. */ + XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start); + wc_xmss_chain_hash(state, data, hash); + /* Iterate 'steps' calls to the hash function. */ + for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) { + addr_buf[XMSS_ADDR_HASH * 4 + 3] = i; + wc_xmss_chain_hash(state, hash, hash); + } + } + else if (hash != data) { + XMEMCPY(hash, data, params->n); + } +} + +/* Convert base on message and add checksum. + * + * RFC 8391:, 2.6, Algorithm 1: base_w + * int in = 0; + * int out = 0; + * unsigned int total = 0; + * int bits = 0; + * int consumed; + * + * for ( consumed = 0; consumed < out_len; consumed++ ) { + * if ( bits == 0 ) { + * total = X[in]; + * in++; + * bits += 8; + * } + * bits -= lg(w); + * basew[out] = (total >> bits) AND (w - 1); + * out++; + * } + * return basew; + * + * base_w implemented for w == 16 (lg(w) == 4). + * + * RFC 8391: 3.1.5, Algorithm 5: + * ... + * csum = 0; + * + * # Convert message to base w + * msg = base_w(M, w, len_1); + * # Compute checksum + * for ( i = 0; i < len_1; i++ ) { + * csum = csum + w - 1 - msg[i]; + * } + * + * # Convert csum to base w + * csum = csum << ( 8 - ( ( len_2 * lg(w) ) % 8 )); + * len_2_bytes = ceil( ( len_2 * lg(w) ) / 8 ); + * msg = msg || base_w(toByte(csum, len_2_bytes), w, len_2); + * + * len_1 == 8 * n / 4 = n * 2 + * Implemented for len_2 == 3 + * + * @param [in] m Message data. + * @param [in] n Number of bytes in hash. + * @param [out] msg Message in new base. + */ +static void wc_xmss_msg_convert(const byte* m, word8 n, word8* msg) +{ + word8 i; + word16 csum = 0; + + /* Split each full byte of m into two bytes of msg. */ + for (i = 0; i < n; i++) { + msg[0] = m[i] >> 4; + msg[1] = m[i] & 0xf; + csum += XMSS_WOTS_W - 1 - msg[0]; + csum += XMSS_WOTS_W - 1 - msg[1]; + msg += 2; + } + + /* Append checksum to message. (Maximum value: 1920 = 64 * 2 * 15) */ + msg[0] = (csum >> 8) ; + msg[1] = (csum >> 4) & 0x0f; + msg[2] = (csum ) & 0x0f; +} + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + +/* WOTS+ generate public key with private seed. + * + * RFC 8391: 4.1.6, Algorithm 9: + * ... + * pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS); + * RFC 8391, 3.1.4, Algorithm 4: WOTS_genPK + * ... + * for ( i = 0; i < len; i++ ) { + * ADRS.setChainAddress(i); + * pk[i] = chain(sk[i], 0, w - 1, SEED, ADRS); + * } + * return pk; + * + * WOTS_genPK only used in Algorithm 9 and it is convenient to combine with + * getWOTS_SK due to parameter specific implementations. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk Random private seed. + * @param [in] seed Random public seed. + * @param [in] addr Hashing address. + * @param [out] pk Public key. + */ +static void wc_xmss_wots_gen_pk(XmssState* state, const byte* sk, + const byte* seed, HashAddress addr, byte* pk) +{ + const XmssParams* params = state->params; + byte* addr_buf = state->encMsg; + word32 i; + + /* Ensure chain address is 0 and encode into a buffer. */ + addr[XMSS_ADDR_CHAIN] = 0; + wc_xmss_addr_encode(addr, addr_buf); + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + /* Expand the private seed - getWOTS_SK */ + wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf, + pk); + + /* Calculate chain hash. */ + wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, + pk); + for (i = 1; i < params->wots_len; i++) { + pk += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed, + addr_buf, pk); + } + } + else +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + { + /* Expand the private seed - getWOTS_SK */ + wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, pk); + + /* Calculate chain hash. */ + wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk); + for (i = 1; i < params->wots_len; i++) { + pk += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk); + } + } +} +/* Generate a signature from a privatge key and message. + * + * RFC 8391: 4.1.9, Algorithm 11: treeSig + * sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig), + * M', getSEED(SK), ADRS); + * RFC 8391: 3.1.5, Algorithm 5: WOTS_sign + * (Convert message to base w and append checksum in base w) + * ... + * for ( i = 0; i < len; i++ ) { + * ADRS.setChainAddress(i); + * sig[i] = chain(sk[i], 0, msg[i], SEED, ADRS); + * } + * return sig; + * + * WOTS_sign only used in Algorithm 11 and convenient to do getWOTS_SK due to + * hash address reuse and parameter specific implementations. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] m Message hash to sign. + * @param [in] sk Random private seed. + * @param [in] seed Random public seed. + * @param [in] addr Hashing address. + * @param [out] sig Calculated XMSS/MT signature. + */ +static void wc_xmss_wots_sign(XmssState* state, const byte* m, + const byte* sk, const byte* seed, HashAddress addr, byte* sig) +{ + const XmssParams* params = state->params; + byte* addr_buf = state->pk; + word32 i; + + /* Convert message to base w and append checksum in base w. */ + wc_xmss_msg_convert(m, params->n, state->encMsg); + + /* Set initial chain value and encode hash address. */ + addr[XMSS_ADDR_CHAIN] = 0; + wc_xmss_addr_encode(addr, addr_buf); + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + /* Expand the private seed - getWOTS_SK */ + wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf, sig); + + /* Calculate chain hash. */ + wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[0], seed, addr_buf, + sig); + for (i = 1; i < params->wots_len; i++) { + sig += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[i], seed, + addr_buf, sig); + } + } + else +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + { + /* Expand the private seed - getWOTS_SK */ + wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, sig); + + /* Calculate chain hash. */ + wc_xmss_chain(state, sig, 0, state->encMsg[0], seed, addr_buf, sig); + for (i = 1; i < params->wots_len; i++) { + sig += params->n; + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain(state, sig, 0, state->encMsg[i], seed, addr_buf, sig); + } + } +} + +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +/* Compute WOTS+ public key value from signature and message. + * + * RFC 8319: 3.1.6 + * Algorithm 6: WOTS_pkFromSig + * (Convert message to base w and append checksum in base w) + * ... + * for ( i = 0; i < len; i++ ) { + * ADRS.setChainAddress(i); + * tmp_pk[i] = chain(sig[i], msg[i], w - 1 - msg[i], SEED, ADRS); + * } + * return tmp_pk; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sig XMSS/MT Signature. + * @param [in] m Message to verify. + * @param [in] seed Random public seed. + * @param [in] addr Hashing address. + * @param [out] pk Public key. + */ +static void wc_xmss_wots_pk_from_sig(XmssState* state, const byte* sig, + const byte* m, const byte* seed, HashAddress addr, byte* pk) +{ + const XmssParams* params = state->params; + byte* addr_buf = state->stack; + word32 i; + + /* Convert message to base w and append checksum in base w. */ + wc_xmss_msg_convert(m, params->n, state->encMsg); + + /* Start with address with chain value of 0. */ + addr[XMSS_ADDR_CHAIN] = 0; + wc_xmss_addr_encode(addr, addr_buf); + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + /* Calculate chain hash. */ + wc_xmss_chain_sha256_32(state, sig, state->encMsg[0], + XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk); + for (i = 1; i < params->wots_len; i++) { + sig += params->n; + pk += params->n; + /* Update chain. */ + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain_sha256_32(state, sig, state->encMsg[i], + XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk); + } + } + else +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 */ + { + /* Calculate chain hash. */ + wc_xmss_chain(state, sig, state->encMsg[0], + XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk); + for (i = 1; i < params->wots_len; i++) { + sig += params->n; + pk += params->n; + /* Update chain. */ + addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i; + wc_xmss_chain(state, sig, state->encMsg[i], + XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk); + } + } +} + +/******************************************** + * L-TREE - unbalanced binary hash tree + ********************************************/ + +/* Compute leaves of L-tree from WOTS+ public key and compress to single value. + * + * RFC 8391: 4.1.5, Algorithm 8: ltree + * unsigned int len' = len; + * ADRS.setTreeHeight(0); + * while ( len' > 1 ) { + * for ( i = 0; i < floor(len' / 2); i++ ) { + * ADRS.setTreeIndex(i); + * pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS); + * } + * if ( len' % 2 == 1 ) { + * pk[floor(len' / 2)] = pk[len' - 1]; + * } + * len' = ceil(len' / 2); + * ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + * } + * return pk[0]; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] pk WOTS+ public key. + * @param [in] seed Random public seed. + * @param [in] addr Hashing address. + * @param [out] pk0 N-byte compressed public key value pk[0]. + */ +static void wc_xmss_ltree(XmssState* state, byte* pk, const byte* seed, + HashAddress addr, byte* pk0) +{ + const XmssParams* params = state->params; + word8 len = params->wots_len; + word32 h = 0; + +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \ + !defined(WC_XMSS_FULL_HASH) + /* Precompute hash state after first 64 bytes (common to all hashes). */ + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + byte* prf_buf = state->prf_buf; + int ret; + + XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, prf_buf, XMSS_SHA256_32_PAD_LEN); + XMEMCPY(prf_buf + XMSS_SHA256_32_PAD_LEN, seed, XMSS_SHA256_32_N); + + ret = wc_Sha256Update(&state->digest.sha256, prf_buf, + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N); + if (ret == 0) { + /* Copy state after first 64 bytes. */ + XMSS_SHA256_STATE_CACHE(state); + } + else if (state->ret == 0) { + /* Store any digest failures for public APIs to return. */ + state->ret = ret; + } + } +#endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 && !WC_XMSS_FULL_HASH */ + while (len > 1) { + word8 i; + word8 len2 = len >> 1; + + addr[XMSS_ADDR_TREE_HEIGHT] = h++; + + for (i = 0; i < len2; i++) { + addr[XMSS_ADDR_TREE_INDEX] = i; + #if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \ + !defined(WC_XMSS_FULL_HASH) + if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) && + (params->n == XMSS_SHA256_32_N) && + (params->hash == WC_HASH_TYPE_SHA256)) { + wc_xmss_rand_hash_sha256_32_prehash(state, + pk + i * 2 * XMSS_SHA256_32_N, addr, + pk + i * XMSS_SHA256_32_N); + } + else + #endif /* !WOLFSSL_WC_XMSS_SMALL && WC_XMSS_SHA256 && + * !WC_XMSS_FULL_HASH */ + { + wc_xmss_rand_hash(state, pk + i * 2 * params->n, + seed, addr, pk + i * params->n); + } + } + if (len & 1) { + XMEMCPY(pk + len2 * params->n, pk + (len - 1) * params->n, + params->n); + } + len = len2 + (len & 1); + } + /* Return compressed public key value pk[0]. */ + XMEMCPY(pk0, pk, params->n); +} + +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + +#ifdef WOLFSSL_WC_XMSS_SMALL + +/******************************************** + * TREE HASH + ********************************************/ + +#ifndef WOLFSSL_SMALL_STACK +/* Compute internal nodes of Merkle tree. + * + * Implementation always starts at index 0. (s = 0) + * + * Build authentication path, if required, rather than duplicating work. + * When node is generated, copy out to authentication path array of nodes. + * + * RFC 8391: 4.1.6, Algorithm 9: treeHash + * if( s % (1 << t) != 0 ) return -1; + * for ( i = 0; i < 2^t; i++ ) { + * SEED = getSEED(SK); + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(s + i); + * pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS); + * ADRS.setType(1); # Type = L-tree address + * ADRS.setLTreeAddress(s + i); + * node = ltree(pk, SEED, ADRS); + * ADRS.setType(2); # Type = hash tree address + * ADRS.setTreeHeight(0); + * ADRS.setTreeIndex(i + s); + * while ( Top node on Stack has same height t' as node ) { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node = RAND_HASH(Stack.pop(), node, SEED, ADRS); + * ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + * } + * Stack.push(node); + * } + * return Stack.pop(); + * RFC 8391: 4.1.9, (Example) buildAuth + * for ( j = 0; j < h; j++ ) { + * k = floor(i / (2^j)) XOR 1; + * auth[j] = treeHash(SK, k * 2^j, j, ADRS); + * } + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk_seed Random private seed. + * @param [in] pk_seed Random public seed. + * @param [in] leafIdx Index of lead node. + * @param [in] subtree_addr Address of subtree. + * @param [out] root Root node of the tree. + * @param [out] auth_path Nodes of the authentication path. + */ +static void wc_xmss_treehash(XmssState* state, const byte* sk_seed, + const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root, + byte* auth_path) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + byte* node = state->stack; + HashAddress ots; + HashAddress ltree; + HashAddress tree; + word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1]; + word8 offset = 0; + word32 max = (word32)1 << params->sub_h; + word32 i; + + /* Copy hash address into one for each purpose. */ + XMSS_ADDR_OTS_SET_SUBTREE(ots, subtree); + XMSS_ADDR_LTREE_SET_SUBTREE(ltree, subtree); + XMSS_ADDR_TREE_SET_SUBTREE(tree, subtree); + + for (i = 0; i < max; i++) { + word8 h; + + /* Calculate WOTS+ public key. */ + ots[XMSS_ADDR_OTS] = i; + wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, ots, state->pk); + /* Calculate public value. */ + ltree[XMSS_ADDR_LTREE] = i; + wc_xmss_ltree(state, state->pk, pk_seed, ltree, node); + + /* Initial height at this offset is 0. */ + h = height[offset] = 0; + /* Copy node, at height 0, out if on authentication path. */ + if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) { + XMEMCPY(auth_path, node, n); + } + + /* Top node on Stack has same height t' as node. */ + while ((offset >= 1) && (h == height[offset - 1])) { + word32 tree_idx = i >> (h + 1); + + node -= n; + /* Calculate hash of node. */ + tree[XMSS_ADDR_TREE_HEIGHT] = h; + tree[XMSS_ADDR_TREE_INDEX] = tree_idx; + wc_xmss_rand_hash(state, node, pk_seed, tree, node); + + /* Update offset and height. */ + offset--; + h = ++height[offset]; + + /* Copy node out if on authentication path. */ + if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) { + XMEMCPY(auth_path + h * n, node, n); + } + } + offset++; + node += n; + } + + /* Copy the root node. */ + XMEMCPY(root, state->stack, n); +} +#else +/* Compute internal nodes of Merkle tree. + * + * Implementation always starts at index 0. (s = 0) + * + * Build authentication path, if required, rather than duplicating work. + * When node is generated, copy out to authentication path array of nodes. + * + * RFC 8391: 4.1.6, Algorithm 9: treeHash + * if( s % (1 << t) != 0 ) return -1; + * for ( i = 0; i < 2^t; i++ ) { + * SEED = getSEED(SK); + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(s + i); + * pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS); + * ADRS.setType(1); # Type = L-tree address + * ADRS.setLTreeAddress(s + i); + * node = ltree(pk, SEED, ADRS); + * ADRS.setType(2); # Type = hash tree address + * ADRS.setTreeHeight(0); + * ADRS.setTreeIndex(i + s); + * while ( Top node on Stack has same height t' as node ) { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node = RAND_HASH(Stack.pop(), node, SEED, ADRS); + * ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + * } + * Stack.push(node); + * } + * return Stack.pop(); + * RFC 8391: 4.1.9, (Example) buildAuth + * for ( j = 0; j < h; j++ ) { + * k = floor(i / (2^j)) XOR 1; + * auth[j] = treeHash(SK, k * 2^j, j, ADRS); + * } + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk_seed Random private seed. + * @param [in] pk_seed Random public seed. + * @param [in] leafIdx Index of lead node. + * @param [in] subtree_addr Address of subtree. + * @param [out] root Root node of the tree. + * @param [out] auth_path Nodes of the authentication path. + */ +static void wc_xmss_treehash(XmssState* state, const byte* sk_seed, + const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root, + byte* auth_path) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + byte* node = state->stack; + HashAddress addr; + word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1]; + word8 offset = 0; + word32 max = (word32)1 << params->sub_h; + word32 i; + + XMSS_ADDR_SET_SUBTREE(addr, subtree, 0); + + for (i = 0; i < max; i++) { + word8 h; + + /* Calculate WOTS+ public key. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + addr[XMSS_ADDR_LTREE] = i; + wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk); + /* Calculate public value. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE; + wc_xmss_ltree(state, state->pk, pk_seed, addr, node); + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE; + addr[XMSS_ADDR_TREE_ZERO] = 0; + + /* Initial height at this offset is 0. */ + h = height[offset] = 0; + /* Copy node out if on authentication path. */ + if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) { + XMEMCPY(auth_path, node, n); + } + + /* Top node on Stack has same height t' as node. */ + while ((offset >= 1) && (h == height[offset - 1])) { + word32 tree_idx = i >> (h + 1); + + node -= n; + /* Calculate hash of node. */ + addr[XMSS_ADDR_TREE_HEIGHT] = h; + addr[XMSS_ADDR_TREE_INDEX] = tree_idx; + wc_xmss_rand_hash(state, node, pk_seed, addr, node); + + /* Update offset and height. */ + offset--; + h = ++height[offset]; + + /* Copy node out if on authentication path. */ + if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) { + XMEMCPY(auth_path + h * n, node, n); + } + } + offset++; + node += n; + /* Reset hash address ready for use as OTS and LTREE. */ + addr[XMSS_ADDR_TREE_HEIGHT] = 0; + addr[XMSS_ADDR_TREE_INDEX] = 0; + } + + /* Copy the root node. */ + XMEMCPY(root, state->stack, n); +} +#endif /* !WOLFSSL_SMALL_STACK */ + +/******************************************** + * MAKE KEY + ********************************************/ + +/* Derives XMSSMT (and XMSS) key pair from seeds. + * + * RFC 8391: 4.1.7, Algorithm 10: XMSS_keyGen. + * ... + * initialize SK_PRF with a uniformly random n-byte string; + * setSK_PRF(SK, SK_PRF); + * + * # Initialization for common contents + * initialize SEED with a uniformly random n-byte string; + * setSEED(SK, SEED); + * setWOTS_SK(SK, wots_sk)); + * ADRS = toByte(0, 32); + * root = treeHash(SK, 0, h, ADRS); + * + * SK = idx || wots_sk || SK_PRF || root || SEED; + * PK = OID || root || SEED; + * return (SK || PK); + * + * wots_sk, SK_PRF and SEED passed in as seed. + * Store seed for wots_sk instead of generated wots_sk. + * OID not stored in PK this is handled in upper layer. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] seed Random seeds. + * @param [out] sk Secret/Private key. + * @param [out] pk Public key. + * @return 0 on success. + * @return <0 on digest failure. + */ +int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed, + unsigned char* sk, unsigned char* pk) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + const byte* seed_priv = seed; + const byte* seed_pub = seed + 2 * n; + /* Offsets into secret/private key. */ + byte* sk_idx = sk; + byte* sk_seed = sk_idx + params->idx_len; + byte* sk_pub = sk_seed + 2 * n; + /* Offsets into public key. */ + byte* pk_root = pk; + byte* pk_seed = pk_root + n; + + /* Set first index to 0 in private key. */ + XMEMSET(sk_idx, 0, params->idx_len); + /* Set private key seed and private key for PRF in to private key. */ + XMEMCPY(sk_seed, seed_priv, 2 * n); + /* Set public key seed into public key. */ + XMEMCPY(pk_seed, seed_pub, n); + + /* Set all address values to zero. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + /* Set depth into address. */ + state->addr[XMSS_ADDR_LAYER] = params->d - 1; + /* Compute root node into public key. */ + wc_xmss_treehash(state, sk_seed, pk_seed, 0, state->addr, pk_root, NULL); + + /* Append public key (root node and public seed) to private key. */ + XMEMCPY(sk_pub, pk_root, 2 * n); + + /* Return any errors that occurred during hashing. */ + return state->ret; +} + +/******************************************** + * SIGN + ********************************************/ + +/** + * Sign message using XMSS/XMSS^MT. + * + * RFC 8391: 4.1.9, Algorithm 11: treeSig + * auth = buildAuth(SK, idx_sig, ADRS); + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(idx_sig); + * sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig), + * M', getSEED(SK), ADRS); + * Sig = sig_ots || auth; + * return Sig; + * RFC 8391: 4.2.4, Algorithm 16: XMSSMT_sign + * # Init + * ADRS = toByte(0, 32); + * SEED = getSEED(SK_MT); + * SK_PRF = getSK_PRF(SK_MT); + * idx_sig = getIdx(SK_MT); + * + * # Update SK_MT + * setIdx(SK_MT, idx_sig + 1); + * + * # Message compression + * byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); + * byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M); + * + * # Sign + * Sig_MT = idx_sig; + * unsigned int idx_tree + * = (h - h / d) most significant bits of idx_sig; + * unsigned int idx_leaf = (h / d) least significant bits of idx_sig; + * SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, 0) || SK_PRF + * || toByte(0, n) || SEED; + * ADRS.setLayerAddress(0); + * ADRS.setTreeAddress(idx_tree); + * Sig_tmp = treeSig(M', SK, idx_leaf, ADRS); + * Sig_MT = Sig_MT || r || Sig_tmp; + * for ( j = 1; j < d; j++ ) { + * root = treeHash(SK, 0, h / d, ADRS); + * idx_leaf = (h / d) least significant bits of idx_tree; + * idx_tree = (h - j * (h / d)) most significant bits of idx_tree; + * SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, j) || SK_PRF + * || toByte(0, n) || SEED; + * ADRS.setLayerAddress(j); + * ADRS.setTreeAddress(idx_tree); + * Sig_tmp = treeSig(root, SK, idx_leaf, ADRS); + * Sig_MT = Sig_MT || Sig_tmp; + * } + * return SK_MT || Sig_MT + * + * buildAuth from treeSig done inside treeHash as this is more efficient. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] m Buffer holding message. + * @param [in] mlen Length of message in buffer. + * @param [in, out] sk Secret/Private key. + * @param [out] sig Signature. + * @return 0 on success. + * @return <0 on digest failure. + */ +int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen, + unsigned char* sk, unsigned char* sig) +{ + int ret = 0; + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 hs = params->sub_h; + const word16 hsn = (word16)hs * n; + const byte* sk_seed = sk + params->idx_len; + const byte* pk_seed = sk + params->idx_len + 3 * n; + wc_Idx idx; + byte* sig_r = sig + params->idx_len; + byte root[WC_XMSS_MAX_N]; + unsigned int i; + + WC_IDX_ZERO(idx); + /* Set all address values to zero and set type to OTS. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + + /* Copy the index into the signature data: Sig_MT = idx_sig. */ + XMEMCPY(sig, sk, params->idx_len); + + /* Read index from the secret key. */ + WC_IDX_DECODE(idx, params->idx_len, sk, ret); + /* Validate index in secret key. */ + if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) { + /* Set index to maximum value to distinguish from valid value. */ + XMEMSET(sk, 0xFF, params->idx_len); + /* Zeroize the secret key. */ + ForceZero(sk + params->idx_len, params->sk_len - params->idx_len); + ret = KEY_EXHAUSTED_E; + } + + /* Update SK_MT */ + if (ret == 0) { + /* Increment the index in the secret key. */ + wc_idx_update(sk, params->idx_len); + } + + /* Message compression */ + if (ret == 0) { + const byte* sk_prf = sk + params->idx_len + n; + + /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */ + wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN); + wc_xmss_prf(state, sk_prf, state->buf, sig_r); + ret = state->ret; + } + if (ret == 0) { + const byte* pub_root = sk + params->idx_len + 2 * n; + /* byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M); + */ + wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m, + mlen, root); + ret = state->ret; + /* Place WOTS+ signatures after index and 'r'. */ + sig += params->idx_len + n; + } + + /* Sign. */ + for (i = 0; (ret == 0) && (i < params->d); i++) { + word32 idx_leaf = 0; + + /* Set layer, tree and OTS leaf index into hash address. */ + state->addr[XMSS_ADDR_LAYER] = i; + WC_IDX_SET_ADDR_TREE(idx, params->idx_len, hs, state->addr, idx_leaf); + /* treeSig || treeHash = sig_ots || auth */ + state->addr[XMSS_ADDR_OTS] = idx_leaf; + /* Create WOTS+ signature for tree into signature (sig_ots). */ + wc_xmss_wots_sign(state, root, sk_seed, pk_seed, state->addr, sig); + ret = state->ret; + if (ret == 0) { + sig += params->wots_sig_len; + /* Add authentication path (auth) and calc new root. */ + wc_xmss_treehash(state, sk_seed, pk_seed, idx_leaf, state->addr, + root, sig); + ret = state->ret; + sig += hsn; + } + } + + return ret; +} + +#else + +/******************************************** + * Fast C implementation + ********************************************/ + +/* Tree hash data - needs to be unpacked from binary. */ +typedef struct TreeHash { + /* Next index to update in tree - max 20 bits. */ + word32 nextIdx; + /* Number of stack entries used by tree - 0... */ + word8 used; + /* Tree is finished. */ + word8 completed; +} TreeHash; + +/* BDS state. */ +typedef struct BdsState { + /* Stack of nodes - subtree height + 1 nodes. */ + byte* stack; + /* Height of stack node - subtree height + 1 of 0... */ + byte* height; + /* Authentication path for next index - subtree height nodes. */ + byte* authPath; + /* Hashes of nodes kept - subtree height / 2 nodes. */ + byte* keep; + /* Tree hash instances - subtree height minus K instances. */ + byte* treeHash; + /* Hashes of nodes for tree hash - one for each tree hash instance. */ + byte* treeHashNode; + /* Hashes of nodes to retain - based on K parameter. */ + byte* retain; + /* Next leaf to calculate - max 20 bits. */ + word32 next; + /* Current offset into stack - 0... */ + word8 offset; +} BdsState; + +/* Index to BDS state accounting for swapping. + * + * @param [in] idx Index of node. + * @param [in] i Depth of tree. + * @param [in] hs Height of subtree. + * @param [in] d Depth/number of trees. + * @return Index of working BDS state. + */ +#define BDS_IDX(idx, i, hs, d) \ + (((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? (i) : ((d) + (i))) +/* Index to alternate BDS state accounting for swapping. + * + * @param [in] idx Index of node. + * @param [in] i Depth of tree. + * @param [in] hs Height of subtree. + * @param [in] d Depth/number of trees. + * @return Index of alternate BDS state. + */ +#define BDS_ALT_IDX(idx, i, hs, d) \ + (((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? ((d) + (i)) : (i)) + +/******************************************** + * Tree Hash APIs + ********************************************/ + +/* Initialize the tree hash data at specified index for the BDS state. + * + * @param [in, out] bds BDS state. + * @param [in] i Index of tree hash. + */ +static void wc_xmss_bds_state_treehash_init(BdsState* bds, int i) +{ + byte* sk = bds->treeHash + i * 4; + c32to24(0, sk); + sk[3] = 0 | (1 << 7); +} + +/* Set next index into tree hash data at specified index for the BDS state. + * + * @param [in, out] bds BDS state. + * @param [in] i Index of tree hash. + * @param [in] nextIdx Next index for tree hash. + */ +static void wc_xmss_bds_state_treehash_set_next_idx(BdsState* bds, int i, + word32 nextIdx) +{ + byte* sk = bds->treeHash + i * 4; + c32to24(nextIdx, sk); + sk[3] = 0 | (0 << 7); +} + +/* Mark tree hash, at specified index for the BDS state, as complete. + * + * @param [in, out] bds BDS state. + * @param [in] i Index of tree hash. + */ +static void wc_xmss_bds_state_treehash_complete(BdsState* bds, int i) +{ + byte* sk = bds->treeHash + i * 4; + sk[3] |= 1 << 7; +} + +/* Get the tree hash data at specified index for the BDS state. + * + * @param [in] bds BDS state. + * @param [in] i Index of tree hash. + * @param [out] treeHash Tree hash instance to fill out. + */ +static void wc_xmss_bds_state_treehash_get(BdsState* bds, int i, + TreeHash* treeHash) +{ + byte* sk = bds->treeHash + i * 4; + ato24(sk, &treeHash->nextIdx); + treeHash->used = sk[3] & 0x7f; + treeHash->completed = sk[3] >> 7; +} + +/* Set the tree hash data at specified index for the BDS state. + * + * @param [in, out] bds BDS state. + * @param [in] i Index of tree hash. + * @param [in] treeHash Tree hash data. + */ +static void wc_xmss_bds_state_treehash_set(BdsState* bds, int i, + TreeHash* treeHash) +{ + byte* sk = bds->treeHash + i * 4; + c32to24(treeHash->nextIdx, sk); + sk[3] = treeHash->used | (treeHash->completed << 7); +} + +/******************************************** + * BDS State APIs + ********************************************/ + +/* Allocate memory for BDS state. + * + * When using a static BDS state (XMSS) then pass in handle to data for bds. + * + * @param [in] params XMSS/MT parameters. + * @param [in, out] bds Handle to BDS state. May be NULL if not allocated. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + */ +static int wc_xmss_bds_state_alloc(const XmssParams* params, BdsState** bds) +{ + const word8 cnt = 2 * params->d - 1; + int ret = 0; + + if (*bds == NULL) { + /* Allocate memory for BDS states. */ + *bds = (BdsState*)XMALLOC(sizeof(BdsState) * cnt, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (*bds == NULL) { + ret = MEMORY_E; + } + } + + return ret; +} + +/* Dispose of allocated memory associated with BDS state. + * + * @param [in] bds BDS state. + */ +static void wc_xmss_bds_state_free(BdsState* bds) +{ + /* BDS states was allocated - must free. */ + XFREE(bds, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +/* Load the BDS state from the secret/private key. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] sk Secret/private key. + * @param [out] bds BDS states. + * @param [out] wots_sigs WOTS signatures when XMSS^MT. + */ +static void wc_xmss_bds_state_load(const XmssState* state, byte* sk, + BdsState* bds, byte** wots_sigs) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + const word8 k = params->bds_k; + const word32 retainLen = XMSS_RETAIN_LEN(k, n); + int i; + + /* Skip past standard SK = idx || wots_sk || SK_PRF || root || SEED; */ + sk += params->idx_len + 4 * n; + + for (i = 0; i < 2 * (int)params->d - 1; i++) { + /* Set pointers into SK. */ + bds[i].stack = sk; + sk += (hs + 1) * n; + bds[i].height = sk; + sk += hs + 1; + bds[i].authPath = sk; + sk += hs * n; + bds[i].keep = sk; + sk += (hs >> 1) * n; + bds[i].treeHash = sk; + sk += hsk * 4; + bds[i].treeHashNode = sk; + sk += hsk * n; + bds[i].retain = sk; + sk += retainLen; + /* Load values - big-endian encoded. */ + ato24(sk, &bds[i].next); + sk += 3; + bds[i].offset = sk[0]; + sk += 1; + } + + if (wots_sigs != NULL) { + *wots_sigs = sk; + } +} + +/* Store the BDS state into the secret/private key. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] sk Secret/private key. + * @param [in] bds BDS states. + */ +static void wc_xmss_bds_state_store(const XmssState* state, byte* sk, + BdsState* bds) +{ + int i; + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + const word8 k = params->bds_k; + const word32 skip = (hs + 1) * n + /* BdsState.stack */ + hs + 1 + /* BdsState.height */ + hs * n + /* BdsState.authPath */ + (hs >> 1) * n + /* BdsState.keep */ + hsk * 4 + /* BdsState.treeHash */ + hsk * n + /* BdsState.treeHashNode */ + XMSS_RETAIN_LEN(k, n); /* BdsState.retain */ + + /* Ignore standard SK = idx || wots_sk || SK_PRF || root || SEED; */ + sk += params->idx_len + 4 * n; + + for (i = 0; i < 2 * (int)params->d - 1; i++) { + /* Skip pointers into sk. */ + sk += skip; + /* Save values - big-endian encoded. */ + c32to24(bds[i].next, sk); + sk += 3; + sk[0] = bds[i].offset; + sk += 1; + } +} + +/******************************************** + * BDS + ********************************************/ + +/* Compute node at next index. + * + * RFC 8391: 4.1.6, Algorithm 9: treeHash + * ... + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(s + i); + * pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS); + * ADRS.setType(1); # Type = L-tree address + * ADRS.setLTreeAddress(s + i); + * node = ltree(pk, SEED, ADRS); + * ADRS.setType(2); # Type = hash tree address + * ADRS.setTreeHeight(0); + * ADRS.setTreeIndex(i + s); + * while ( Top node on Stack has same height t' as node ) { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node = RAND_HASH(Stack.pop(), node, SEED, ADRS); + * ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + * } + * Stack.push(node); + * ... + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] bds BDS state. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] root Root node. + */ +static void wc_xmss_bds_next_idx(XmssState* state, BdsState* bds, + const byte* sk_seed, const byte* pk_seed, HashAddress addr, int i, + word8* height, word8* offset, word8** sp) +{ + const XmssParams* params = state->params; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + const word8 n = params->n; + word8 o = *offset; + word8* node = *sp; + word8 h; + + /* Calculate WOTS+ public key. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + addr[XMSS_ADDR_OTS] = i; + wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk); + /* Calculate public value. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE; + wc_xmss_ltree(state, state->pk, pk_seed, addr, node); + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE; + addr[XMSS_ADDR_TREE_ZERO] = 0; + + /* Initial height at this offset is 0. */ + h = height[o] = 0; + /* HDSS, Section 4.5, 2: TREEHASH[h].push(v[h][3]) + * Copy right node to tree hash nodes if second right node. */ + if ((hsk > 0) && (i == 3)) { + XMEMCPY(bds->treeHashNode, node + n, n); + } + + /* Top node on Stack has same height t' as node. */ + while ((o >= 1) && (h == height[o - 1])) { + /* HDSS, Section 4.5, 1: AUTH[h] = v[h][1], h = 0,...,H-1. + * Cache left node if on authentication path. */ + if ((i >> h) == 1) { + XMEMCPY(bds->authPath + h * n, node, n); + } + /* This is a right node. */ + else if (h < hsk) { + /* HDSS, Section 4.5, 2: TREEHASH[h].push(v[h][3]) + * Copy right node to tree hash if second right node. */ + if ((i >> h) == 3) { + XMEMCPY(bds->treeHashNode + h * n, node, n); + } + } + else { + /* HDSS, Section 4.5, 3: RETAIN[h].push(v[j][2j+3] for + * h = H-K,...,H-2 and j = 2^(H-h-1)-2,...,0. + * Retain high right nodes. + */ + word32 ro = (1 << (hs - 1 - h)) + h - hs + (((i >> h) - 3) >> 1); + XMEMCPY(bds->retain + ro * n, node, n); + } + + node -= n; + /* Calculate hash of node. */ + addr[XMSS_ADDR_TREE_HEIGHT] = h; + addr[XMSS_ADDR_TREE_INDEX] = i >> (h + 1); + wc_xmss_rand_hash(state, node, pk_seed, addr, node); + + /* Update offset and height. */ + o--; + h = ++height[o]; + } + + *offset = o; + *sp = node; +} + +/* Compute initial Merkle tree and store nodes. + * + * HDSS, Section 4.5, The algorithm, Initialization. + * 1. We store the authentication path for the first leaf (s = 0): + * AUTH[h] = v[h][1], h = 0,...,H-1. + * 2. Depending on the parameter K, we store the next right authentication + * node for each height h = 0,...,H-K-1 in the treehash instances: + * TREEHASH[h].push(v[h][3]). + * 3. Finally we store the right authentication nodes clode to the root using + * the stacks RETAIN[h]: + * RETAIN[h].push(v[j][2j+3] for h = H-K,...,H-2 and j = 2^(H-h-1)-2,...,0. + * + * RFC 8391: 4.1.6, Algorithm 9: treeHash + * if( s % (1 << t) != 0 ) return -1; + * for ( i = 0; i < 2^t; i++ ) { + * SEED = getSEED(SK); + * [Compute node at next index] + * } + * return Stack.pop(); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] bds BDS state. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [out] root Root node. + */ +static void wc_xmss_bds_treehash_initial(XmssState* state, BdsState* bds, + const byte* sk_seed, const byte* pk_seed, const HashAddress addr, + byte* root) +{ + const XmssParams* params = state->params; + const word8 hsk = params->sub_h - params->bds_k; + const word8 n = params->n; + word8* node = state->stack; + HashAddress addrCopy; + word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1]; + word8 offset = 0; + word32 maxIdx = (word32)1 << params->sub_h; + word32 i; + + /* First signing index will be 0 - setup BDS state. */ + bds->offset = 0; + bds->next = 0; + /* Reset the hash tree status. */ + for (i = 0; i < hsk; i++) { + wc_xmss_bds_state_treehash_init(bds, i); + } + + /* Copy hash address into local. */ + XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr); + + /* Compute each node in tree. */ + for (i = 0; i < maxIdx; i++) { + wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, i, height, + &offset, &node); + offset++; + node += n; + /* Rest the hash address for reuse. */ + addrCopy[XMSS_ADDR_TREE_HEIGHT] = 0; + addrCopy[XMSS_ADDR_TREE_INDEX] = 0; + } + + /* Copy the root node. */ + XMEMCPY(root, state->stack, n); +} + +/* Update internal nodes of Merkle tree at next index. + * + * RFC 8391: 4.1.6, Algorithm 9: treeHash + * ... + * SEED = getSEED(SK); + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(s + i); + * pk = WOTS_genPK (getWOTS_SK(SK, s + i), SEED, ADRS); + * ADRS.setType(1); # Type = L-tree address + * ADRS.setLTreeAddress(s + i); + * node = ltree(pk, SEED, ADRS); + * ADRS.setType(2); # Type = hash tree address + * ADRS.setTreeHeight(0); + * ADRS.setTreeIndex(i + s); + * while ( Top node on Stack has same height t' as node ) { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node = RAND_HASH(Stack.pop(), node, SEED, ADRS); + * ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + * } + * Stack.push(node); + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] height Height of nodes to update. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + */ +static void wc_xmss_bds_treehash_update(XmssState* state, BdsState* bds, + word8 height, const byte* sk_seed, const byte* pk_seed, + const HashAddress addr) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + HashAddress addrLocal; + TreeHash treeHash[1]; + byte* sp = bds->stack + bds->offset * n; + byte* node = state->stack + WC_XMSS_MAX_STACK_LEN - n; + word8 h; + + /* Get the tree hash data. */ + wc_xmss_bds_state_treehash_get(bds, height, treeHash); + /* Copy hash address into local as OTS type. */ + XMSS_ADDR_OTS_SET_SUBTREE(addrLocal, addr); + /* Calculate WOTS+ public key. */ + addrLocal[XMSS_ADDR_OTS] = treeHash->nextIdx; + wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addrLocal, state->pk); + /* Calculate public value. */ + addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE; + wc_xmss_ltree(state, state->pk, pk_seed, addrLocal, node); + addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE; + addrLocal[XMSS_ADDR_TREE_ZERO] = 0; + + /* Initial height is 0. */ + h = 0; + + /* Top node on Stack has same height t' as node. */ + while ((treeHash->used > 0) && (h == bds->height[bds->offset - 1])) { + sp -= n; + /* Copy from stack to before last calculated node. */ + node -= n; + XMEMCPY(node, sp, n); + + /* Calculate hash of node. */ + addrLocal[XMSS_ADDR_TREE_HEIGHT] = h; + addrLocal[XMSS_ADDR_TREE_INDEX] = treeHash->nextIdx >> (h + 1); + wc_xmss_rand_hash(state, node, pk_seed, addrLocal, node); + + /* Update used, offset and height. */ + treeHash->used--; + bds->offset--; + h++; + } + + /* Check whether we reached the height we wanted to update. */ + if (h == height) { + /* Cache node. */ + XMEMCPY(bds->treeHashNode + height * n, node, n); + treeHash->completed = 1; + } + else { + /* Push calculated node onto stack. */ + XMEMCPY(sp, node, n); + treeHash->used++; + /* Update BDS state. */ + bds->height[bds->offset] = h; + bds->offset++; + treeHash->nextIdx++; + } + + /* Set the tree hash data back. */ + wc_xmss_bds_state_treehash_set(bds, height, treeHash); +} + +/* Updates hash trees that need it most. + * + * Algorithm 4.6: Authentication path computation, Step 5. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] updates Current number of updates. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @return Number of available updates. + */ +static word8 wc_xmss_bds_treehash_updates(XmssState* state, BdsState* bds, + word8 updates, const byte* sk_seed, const byte* pk_seed, + const HashAddress addr) +{ + const XmssParams* params = state->params; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + + while (updates > 0) { + word8 minH = hs; + word8 h = hsk; + word8 i; + + /* Step 5.a. k <- min{ h: TREEHASH(h).height() = + min[j=0..H-K-1]{TREEHASH(j.height()} } */ + for (i = 0; i < hsk; i++) { + TreeHash treeHash[1]; + + wc_xmss_bds_state_treehash_get(bds, i, treeHash); + + if (treeHash->completed) { + /* Finished - ignore. */ + } + else if (treeHash->used == 0) { + /* None used, low height. */ + if (i < minH) { + h = i; + minH = i; + } + } + /* Find the height of lowest in cache. */ + else { + word8 j; + word8 lowH = hs; + byte* height = bds->height + bds->offset - treeHash->used; + + for (j = 0; j < treeHash->used; j++) { + lowH = min(height[j], lowH); + } + if (lowH < minH) { + /* New lowest height. */ + h = i; + minH = lowH; + } + } + } + /* If none lower, then stop. */ + if (h == hsk) { + break; + } + + /* Step 5.b. TREEHASH(k).update() */ + /* Update tree to the lowest height. */ + wc_xmss_bds_treehash_update(state, bds, h, sk_seed, pk_seed, addr); + updates--; + } + return updates; +} + +/* Update BDS at next leaf. + * + * Don't do anything if processed all leaves. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + */ +static void wc_xmss_bds_update(XmssState* state, BdsState* bds, + const byte* sk_seed, const byte* pk_seed, const HashAddress addr) +{ + if (bds->next < ((word32)1 << state->params->sub_h)) { + const XmssParams* params = state->params; + byte* sp = bds->stack + bds->offset * params->n; + HashAddress addrCopy; + + XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr); + wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, bds->next, + bds->height, &bds->offset, &sp); + bds->offset++; + bds->next++; + } +} + +/* Find index of lowest zero bit. + * + * Supports max up to 31. + * + * @param [in] n Number to evaluate. + * @param [in] max Max number of bits. + * @param [out] b Next bit above first zero bit. + * @return Index of lowest bit that is zero. + */ +static word8 wc_xmss_lowest_zero_bit_index(word32 n, word8 max, word8* b) +{ + word8 i; + + /* Check each bit from lowest for a zero bit. */ + for (i = 0; i < max; i++) { + if ((n & 1) == 0) { + break; + } + n >>= 1; + } + + /* Return next bit after 0 bit. */ + *b = (n >> 1) & 1; + return i; +} + +/* Returns auth path for node leafIdx and computes for next leaf node. + * + * HDSS, Algorithm 4.6: Authentication path computation, Steps 1-4. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] leafIdx Current leaf index. + * @param [in] sk_seed Random secret/private seed. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + */ +static void wc_xmss_bds_auth_path(XmssState* state, BdsState* bds, + const word32 leafIdx, const byte* sk_seed, const byte* pk_seed, + HashAddress addr) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + word8 tau; + byte* node = state->encMsg; + word8 parent; + + /* Step 1. Find the height of first left node in authentication path. */ + tau = wc_xmss_lowest_zero_bit_index(leafIdx, hs, &parent); + if (tau == 0) { + /* Step 2. Keep node if parent is a left node. + * if s/(2^tau+1) is even and tau < H-1 then KEEP[tau] <- AUTH[tau] + */ + if (parent == 0) { + XMEMCPY(bds->keep, bds->authPath, n); + } + + /* Step 3. if tau = 0 then AUTH[0] <- LEAFCALC(s) */ + /* Calculate WOTS+ public key. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + addr[XMSS_ADDR_OTS] = leafIdx; + wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk); + /* Calculate public value. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE; + wc_xmss_ltree(state, state->pk, pk_seed, addr, bds->authPath); + } + else { + byte* authPath; + byte* nodes; + word8 i; + + authPath = bds->authPath + tau * n; + /* Step 4.a. = AUTH[tau-1] || KEEP[tau-1] + * Only keeping half of nodes, so need to copy out before updating. + */ + XMEMCPY(node, authPath - n, n); + XMEMCPY(node + n, bds->keep + ((tau - 1) >> 1) * n, n); + + /* Step 2. Keep node if parent is a left node. + * if s/(2^tau+1) is even and tau < H-1 then KEEP[tau] <- AUTH[tau] + */ + if ((tau < hs - 1) && (parent == 0)) { + XMEMCPY(bds->keep + (tau >> 1) * n, authPath, n); + } + + /* Step 4.a. AUTH[tau] <- g() */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE; + addr[XMSS_ADDR_TREE_ZERO] = 0; + addr[XMSS_ADDR_TREE_HEIGHT] = tau - 1; + addr[XMSS_ADDR_TREE_INDEX] = leafIdx >> tau; + wc_xmss_rand_hash(state, node, pk_seed, addr, authPath); + + /* Step 4.b. */ + authPath = bds->authPath; + nodes = bds->treeHashNode; + /* for h = 0 to tau - 1 do */ + for (i = 0; i < tau; i++) { + /* if h < H - K then AUTH[h] <- TREEHASH[h].pop()*/ + if (i < hsk) { + XMEMCPY(authPath, nodes, n); + nodes += n; + } + /* if h >= H - K then AUTH[h] <- RETAIN[h].pop()*/ + else { + word32 o = (1 << (hs - 1 - i)) + i - hs + + (((leafIdx >> i) - 1) >> 1); + XMEMCPY(authPath, bds->retain + o * n, n); + } + authPath += n; + } + + /* Step 4.c. Initialize treehash instances for heights: + * 0, ..., min{tau-1, H - K - 1} */ + tau = min(tau, hsk); + for (i = 0; i < tau; i++) { + word32 startIdx = leafIdx + 1 + 3 * (1 << i); + if (startIdx < ((word32)1 << hs)) { + wc_xmss_bds_state_treehash_set_next_idx(bds, i, startIdx); + } + } + } +} + +/******************************************** + * XMSS + ********************************************/ + +/* Derives XMSS key pair from seeds. + * + * RFC 8391: 4.1.7, Algorithm 10: XMSS_keyGen. + * ... + * initialize SK_PRF with a uniformly random n-byte string; + * setSK_PRF(SK, SK_PRF); + * + * # Initialization for common contents + * initialize SEED with a uniformly random n-byte string; + * setSEED(SK, SEED); + * setWOTS_SK(SK, wots_sk)); + * ADRS = toByte(0, 32); + * root = treeHash(SK, 0, h, ADRS); + * + * SK = idx || wots_sk || SK_PRF || root || SEED; + * PK = OID || root || SEED; + * return (SK || PK); + * + * HDSS, Section 4.5, The algorithm, Initialization. + * + * wots_sk, SK_PRF and SEED passed in as seed. + * Store seed for wots_sk instead of generated wots_sk. + * OID not stored in PK this is handled in upper layer. + * BDS state is appended to SK: + * SK = idx || wots_sk || SK_PRF || root || SEED || BDS_STATE; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] seed Secret/Private and public seed. + * @param [out] sk Secret key. + * @param [out] pk Public key. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + * @return <0 on digest failure. + */ +int wc_xmss_keygen(XmssState* state, const unsigned char* seed, + unsigned char* sk, unsigned char* pk) +{ +#if WOLFSSL_XMSS_MIN_HEIGHT <= 32 + int ret = 0; + const XmssParams* params = state->params; + const word8 n = params->n; + /* Offset of root node in public key. */ + byte* pk_root = pk; +#ifdef WOLFSSL_SMALL_STACK + BdsState* bds = NULL; +#else + BdsState bds[1]; +#endif + +#ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for tree hash instances and put in BDS state. */ + ret = wc_xmss_bds_state_alloc(params, &bds); + if (ret == 0) +#endif + { + /* Offsets into seed. */ + const byte* seed_priv = seed; + const byte* seed_pub = seed + 2 * n; + /* Offsets into secret/private key. */ + word32* sk_idx = (word32*)sk; + byte* sk_seeds = sk + params->idx_len; + /* Offsets into public key. */ + byte* pk_seed = pk + n; + + /* Setup pointers into sk - assumes sk is initialized to zeros. */ + wc_xmss_bds_state_load(state, sk, bds, NULL); + + /* Set first index to 0 in private key. idx_len always 4. */ + *sk_idx = 0; + /* Set private key seed and private key for PRF in to private key. */ + XMEMCPY(sk_seeds, seed_priv, 2 * n); + /* Set public key seed into public key. */ + XMEMCPY(pk_seed, seed_pub, n); + + /* Set all address values to zero. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + /* Hash address layer is 0. */ + /* Compute root node into public key. */ + wc_xmss_bds_treehash_initial(state, bds, sk_seeds, pk_seed, + state->addr, pk_root); + /* Return any errors that occurred during hashing. */ + ret = state->ret; + } + if (ret == 0) { + /* Offset of root node in private key. */ + byte* sk_root = sk + params->idx_len + 2 * n; + + /* Append public key (root node and public seed) to private key. */ + XMEMCPY(sk_root, pk_root, 2 * n); + + /* Store BDS state back into secret/private key. */ + wc_xmss_bds_state_store(state, sk, bds); + } + +#ifdef WOLFSSL_SMALL_STACK + /* Dispose of allocated data of BDS states. */ + wc_xmss_bds_state_free(bds); +#endif + return ret; +#else + (void)state; + (void)pk; + (void)sk; + (void)seed; + + return NOT_COMPILED_IN; +#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */ +} + +/* Sign a message with XMSS. + * + * RFC 8391: 4.1.9, Algorithm 11: treeSig + * ... + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(idx_sig); + * sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig), + * M', getSEED(SK), ADRS); + * Sig = sig_ots || auth; + * return Sig; + * RFC 8391: 4.1.9, Algorithm 12: XMSS_sign + * idx_sig = getIdx(SK); + * setIdx(SK, idx_sig + 1); + * ADRS = toByte(0, 32); + * byte[n] r = PRF(getSK_PRF(SK), toByte(idx_sig, 32)); + * byte[n] M' = H_msg(r || getRoot(SK) || (toByte(idx_sig, n)), M); + * Sig = idx_sig || r || treeSig(M', SK, idx_sig, ADRS); + * return (SK || Sig); + * + * HDSS, Section 4.5, The algorithm, Update and output phase. + * + * 'auth' was built at key generation or after computing previous signature. + * Build next authentication path after signature created. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] m Buffer holding message. + * @param [in] mlen Length of message in buffer. + * @param [in, out] sk Secret/Private key. + * @param [out] sm Signature and message data. + * @param [in, out] smlen On in, length of signature and message buffer. + * On out, length of signature and message data. + * @return 0 on success. + * @return <0 on digest failure. + */ +int wc_xmss_sign(XmssState* state, const unsigned char* m, word32 mlen, + unsigned char* sk, unsigned char* sig) +{ +#if WOLFSSL_XMSS_MIN_HEIGHT <= 32 + int ret = 0; + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 h = params->h; + const word8 hk = params->h - params->bds_k; + const byte* sk_seed = sk + XMSS_IDX_LEN; + const byte* pk_seed = sk + XMSS_IDX_LEN + 3 * n; + byte node[WC_XMSS_MAX_N]; + word32 idx; + byte* sig_r = sig + XMSS_IDX_LEN; +#ifdef WOLFSSL_SMALL_STACK + BdsState* bds = NULL; +#else + BdsState bds[1]; +#endif + +#ifdef WOLFSSL_SMALL_STACK + /* Allocate memory for tree hash instances and put in BDS state. */ + ret = wc_xmss_bds_state_alloc(params, &bds); + if (ret == 0) +#endif + { + /* Load the BDS state from secret/private key. */ + wc_xmss_bds_state_load(state, sk, bds, NULL); + + /* Copy the index into the signature data: Sig = idx_sig || ... */ + *((word32*)sig) = *((word32*)sk); + /* Read index from the secret key. */ + ato32(sk, &idx); + + /* Check index is valid. */ + if (IDX32_INVALID(idx, XMSS_IDX_LEN, h)) { + /* Set index to maximum value to distinguish from valid value. */ + XMEMSET(sk, 0xFF, XMSS_IDX_LEN); + /* Zeroize the secret key. */ + ForceZero(sk + XMSS_IDX_LEN, params->sk_len - XMSS_IDX_LEN); + ret = KEY_EXHAUSTED_E; + } + } + + /* Update SK_MT */ + if (ret == 0) { + /* Increment the index in the secret key. */ + c32toa(idx + 1, sk); + } + + /* Message compression */ + if (ret == 0) { + const byte* sk_prf = sk + XMSS_IDX_LEN + n; + + /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */ + wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN); + wc_xmss_prf(state, sk_prf, state->buf, sig_r); + ret = state->ret; + } + if (ret == 0) { + const byte* pub_root = sk + XMSS_IDX_LEN + 2 * n; + + /* Compute the message hash. */ + wc_xmss_hash_message(state, sig_r, pub_root, sig, XMSS_IDX_LEN, m, mlen, + node); + ret = state->ret; + /* Place new signature data after index and 'r'. */ + sig += XMSS_IDX_LEN + n; + } + + if (ret == 0) { + /* Set all address values to zero and set type to OTS. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + /* treeSig || treeHash = sig_ots || auth */ + state->addr[XMSS_ADDR_OTS] = idx; + /* Create WOTS+ signature for tree into signature (sig_ots). */ + wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig); + ret = state->ret; + } + if (ret == 0) { + sig += params->wots_sig_len; + /* Add authentication path (auth) and calc new root. */ + XMEMCPY(sig, bds->authPath, h * n); + ret = state->ret; + } + + if (ret == 0) { + /* Update BDS state - update authentication path for next index. */ + /* Check not last node. */ + if (idx < ((word32)1 << h) - 1) { + /* Calculate next authentication path node. */ + wc_xmss_bds_auth_path(state, bds, idx, sk_seed, pk_seed, + state->addr); + ret = state->ret; + if (ret == 0) { + /* Algorithm 4.6: Step 5. */ + wc_xmss_bds_treehash_updates(state, bds, hk >> 1, sk_seed, + pk_seed, state->addr); + ret = state->ret; + } + } + } + if (ret == 0) { + /* Store BDS state back into secret/private key. */ + wc_xmss_bds_state_store(state, sk, bds); + } + +#ifdef WOLFSSL_SMALL_STACK + /* Dispose of allocated data of BDS states. */ + wc_xmss_bds_state_free(bds); +#endif + return ret; +#else + (void)state; + (void)m; + (void)mlen; + (void)sk; + (void)sig; + + return NOT_COMPILED_IN; +#endif /* WOLFSSL_XMSS_MIN_HEIGHT <= 32 */ +} + +/******************************************** + * XMSS^MT + ********************************************/ + +/* Generate a XMSS^MT key pair from seeds. + * + * RFC 8391: 4.2.2, Algorithm 15: XMSS^MT_keyGen. + * ... + * # Example initialization + * idx_MT = 0; + * setIdx(SK_MT, idx_MT); + * initialize SK_PRF with a uniformly random n-byte string; + * setSK_PRF(SK_MT, SK_PRF); + * initialize SEED with a uniformly random n-byte string; + * setSEED(SK_MT, SEED); + * + * # Generate reduced XMSS private keys + * ADRS = toByte(0, 32); + * for ( layer = 0; layer < d; layer++ ) { + * ADRS.setLayerAddress(layer); + * for ( tree = 0; tree < + * (1 << ((d - 1 - layer) * (h / d))); + * tree++ ) { + * ADRS.setTreeAddress(tree); + * for ( i = 0; i < 2^(h / d); i++ ) { + * wots_sk[i] = WOTS_genSK(); + * } + * setXMSS_SK(SK_MT, wots_sk, tree, layer); + * } + * } + * + * SK = getXMSS_SK(SK_MT, 0, d - 1); + * setSEED(SK, SEED); + * root = treeHash(SK, 0, h / d, ADRS); + * setRoot(SK_MT, root); + * + * PK_MT = OID || root || SEED; + * return (SK_MT || PK_MT); + * + * HDSS, Section 4.5, The algorithm, Initialization. + * OPX, Section 2, Key Generation. + * + * wots_sk, SK_PRF and SEED passed in as seed. + * Store seed for wots_sk instead of generated wots_sk. + * OID not stored in PK this is handled in upper layer. + * BDS state is appended to SK: + * SK = idx || wots_sk || SK_PRF || root || SEED || BDS_STATE; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] seed Secret/Private and public seed. + * @param [out] sk Secret key. + * @param [out] pk Public key. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + * @return <0 on digest failure. + */ +int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed, + unsigned char* sk, unsigned char* pk) +{ + int ret = 0; + const XmssParams* params = state->params; + const word8 n = params->n; + unsigned char* sk_seed = sk + params->idx_len; + unsigned char* pk_root = pk; + unsigned char* pk_seed = pk + n; + word8 i; + byte* wots_sigs; + BdsState* bds = NULL; + + /* Allocate memory for BDS states and tree hash instances. */ + ret = wc_xmss_bds_state_alloc(params, &bds); + if (ret == 0) { + /* Offsets into seed. */ + const byte* seed_priv = seed; + const byte* seed_pub = seed + 2 * params->n; + + /* Load the BDS state from secret/private key. */ + wc_xmss_bds_state_load(state, sk, bds, &wots_sigs); + + /* Set first index to 0 in private key. */ + XMEMSET(sk, 0, params->idx_len); + /* Set private key seed and private key for PRF in to private key. */ + XMEMCPY(sk_seed, seed_priv, 2 * n); + /* Set public key seed into public key. */ + XMEMCPY(pk_seed, seed_pub, n); + + /* Set all address values to zero. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + /* Hash address layer is 0 = bottom-most layer. */ + } + + /* Setup state and compute WOTS+ signatures for all but top-most subtree. */ + for (i = 0; (ret == 0) && (i < params->d - 1); i++) { + /* Compute root for subtree. */ + wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed, + state->addr, pk_root); + ret = state->ret; + if (ret == 0) { + /* Create signature for subtree for first index. */ + state->addr[XMSS_ADDR_LAYER] = i+1; + wc_xmss_wots_sign(state, pk_root, sk_seed, pk_seed, state->addr, + wots_sigs + i * params->wots_sig_len); + ret = state->ret; + } + } + if (ret == 0) { + /* Compute root for top-most subtree. */ + wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed, + state->addr, pk_root); + /* Return any errors that occurred during hashing. */ + ret = state->ret; + } + + if (ret == 0) { + /* Offset of root node in private key. */ + unsigned char* sk_root = sk_seed + 2 * n; + + /* Append public key (root node and public seed) to private key. */ + XMEMCPY(sk_root, pk_root, 2 * n); + + /* Store BDS state back into secret/private key. */ + wc_xmss_bds_state_store(state, sk, bds); + } + + /* Dispose of allocated data of BDS states. */ + wc_xmss_bds_state_free(bds); + return ret; +} + + +#if !defined(WORD64_AVAILABLE) && (WOLFSSL_XMSS_MAX_HEIGHT > 32) + #error "Support not available - use XMSS small code option" +#endif + +#if (WOLFSSL_XMSS_MAX_HEIGHT > 32) + typedef word64 XmssIdx; + #define IDX_MAX_BITS 64 +#else + typedef word32 XmssIdx; + #define IDX_MAX_BITS 32 +#endif + +/* Decode index into word. + * + * @param [out] idx Index from encoding. + * @param [in] c Count of bytes to decode to index. + * @param [in] a Array to decode from. + */ +static void xmss_idx_decode(XmssIdx* idx, word8 c, const unsigned char* a) +{ + word8 i; + XmssIdx n = 0; + + for (i = 0; i < c; i++) { + n <<= 8; + n += a[i]; + } + + *idx = n; +} + +/* Check whether index is valid. + * + * @param [in] i Index to check. + * @param [in] h Full tree Height. + */ +static int xmss_idx_invalid(XmssIdx i, word8 h) +{ + return ((i + 1) >> h) != 0; +} + +/* Get tree and leaf index from index. + * + * @param [in] i Index to split. + * @param [in] h Tree height. + * @param [out] t Tree index. + * @param [out] l Leaf index. + */ +static void xmss_idx_get_tree_leaf(XmssIdx i, word8 h, XmssIdx* t, word32* l) +{ + *l = (word32)i & (((word32)1 << h) - 1); + *t = i >> h; +} + +/* Set the index into address as the tree index. + * + * @param [in] i Tree index. + * @param [in, out] a Hash address. + */ +static void xmss_idx_set_addr_tree(XmssIdx i, HashAddress a) +{ +#if IDX_MAX_BITS == 32 + a[XMSS_ADDR_TREE_HI] = 0; + a[XMSS_ADDR_TREE] = i; +#else + a[XMSS_ADDR_TREE_HI] = (word32)(i >> 32); + a[XMSS_ADDR_TREE] = (word32)(i ); +#endif +} + +/* Sign message with XMSS^MT. + * + * RFC 8391: 4.1.9, Algorithm 11: treeSig + * ... + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(idx_sig); + * sig_ots = WOTS_sign(getWOTS_SK(SK, idx_sig), + * M', getSEED(SK), ADRS); + * Sig = sig_ots || auth; + * return Sig; + * RFC 8391: 4.2.4, Algorithm 16: XMSS^MT_sign. + * ... + * # Init + * ADRS = toByte(0, 32); + * SEED = getSEED(SK_MT); + * SK_PRF = getSK_PRF(SK_MT); + * idx_sig = getIdx(SK_MT); + * + * # Update SK_MT + * setIdx(SK_MT, idx_sig + 1); + * + * # Message compression + * byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); + * byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M); + * + * # Sign + * Sig_MT = idx_sig; + * unsigned int idx_tree + * = (h - h / d) most significant bits of idx_sig; + * unsigned int idx_leaf = (h / d) least significant bits of idx_sig; + * SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, 0) || SK_PRF + * || toByte(0, n) || SEED; + * ADRS.setLayerAddress(0); + * ADRS.setTreeAddress(idx_tree); + * Sig_tmp = treeSig(M', SK, idx_leaf, ADRS); + * Sig_MT = Sig_MT || r || Sig_tmp; + * for ( j = 1; j < d; j++ ) { + * root = treeHash(SK, 0, h / d, ADRS); + * idx_leaf = (h / d) least significant bits of idx_tree; + * idx_tree = (h - j * (h / d)) most significant bits of idx_tree; + * SK = idx_leaf || getXMSS_SK(SK_MT, idx_tree, j) || SK_PRF + * || toByte(0, n) || SEED; + * ADRS.setLayerAddress(j); + * ADRS.setTreeAddress(idx_tree); + * Sig_tmp = treeSig(root, SK, idx_leaf, ADRS); + * Sig_MT = Sig_MT || Sig_tmp; + * } + * return SK_MT || Sig_MT; + * + * 'auth' was built at key generation or after computing previous signature. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] idx Index to sign with. + * @param [in] wots_sigs Pre-computed WOTS+ signatures. + * @param [in] m Buffer holding message. + * @param [in] mlen Length of message in buffer. + * @param [in, out] sk Secret/Private key. + * @param [out] sig Signature and message data. + * @return 0 on success. + * @return <0 on digest failure. + */ +static int wc_xmssmt_sign_msg(XmssState* state, BdsState* bds, XmssIdx idx, + byte* wots_sigs, const unsigned char* m, word32 mlen, unsigned char* sk, + unsigned char* sig) +{ + int ret; + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 hs = params->sub_h; + const word8 idx_len = params->idx_len; + const byte* sk_prf = sk + idx_len + n; + byte* sig_mt = sig; + byte* sig_r = sig + idx_len; + byte node[WC_XMSS_MAX_N]; + + /* Message compression */ + /* byte[n] r = PRF(SK_PRF, toByte(idx_sig, 32)); */ + wc_idx_copy(sig_mt, idx_len, state->buf, XMSS_PRF_M_LEN); + wc_xmss_prf(state, sk_prf, state->buf, sig_r); + ret = state->ret; + if (ret == 0) { + const byte* pub_root = sk + idx_len + 2 * n; + /* byte[n] M' = H_msg(r || getRoot(SK_MT) || (toByte(idx_sig, n)), M); + */ + wc_xmss_hash_message(state, sig_r, pub_root, sig, idx_len, m, mlen, + node); + ret = state->ret; + /* Place new signature data after index and 'r'. */ + sig += idx_len + n; + } + + /* Sign */ + if (ret == 0) { + const byte* sk_seed = sk + idx_len; + const byte* pk_seed = sk + idx_len + 3 * n; + XmssIdx idx_tree; + word32 idx_leaf; + + /* Set all address values to zero and set type to OTS. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + + /* Fist iteration - calculate signature. */ + /* Set layer, tree and OTS leaf index into hash address. */ + state->addr[XMSS_ADDR_LAYER] = 0; + xmss_idx_get_tree_leaf(idx, hs, &idx_tree, &idx_leaf); + xmss_idx_set_addr_tree(idx_tree, state->addr); + /* treeSig || treeHash = sig_ots || auth */ + state->addr[XMSS_ADDR_OTS] = idx_leaf; + /* Create WOTS+ signature for tree into signature (sig_ots). */ + wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig); + ret = state->ret; + } + if (ret == 0) { + word8 i; + + sig += params->wots_sig_len; + /* Add authentication path. */ + XMEMCPY(sig, bds[BDS_IDX(idx, 0, hs, params->d)].authPath, hs * n); + sig += hs * n; + + /* Remaining iterations from storage. */ + for (i = 1; i < params->d; i++) { + /* Copy out precomputed signature into signature (sig_ots). */ + XMEMCPY(sig, wots_sigs + (i - 1) * params->wots_sig_len, + params->wots_sig_len); + sig += params->wots_sig_len; + /* Add authentication path (auth) and calc new root. */ + XMEMCPY(sig, bds[BDS_IDX(idx, i, hs, params->d)].authPath, hs * n); + sig += hs * n; + } + ret = state->ret; + } + + return ret; +} + +/* Compute BDS state for signing next index. + * + * HDSS, Section 4.5, The algorithm, Update and output phase. + * OPX, Section 2, Signature Generation. Para 2 and 3. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in, out] bds BDS state. + * @param [in] idx Index to sign with. + * @param [in] wots_sigs Pre-computed WOTS+ signatures. + * @param [in] m Buffer holding message. + * @param [in] mlen Length of message in buffer. + * @param [in, out] sk Secret/Private key. + * @param [out] sig Signature and message data. + * @return 0 on success. + * @return <0 on digest failure. + */ +static int wc_xmssmt_sign_next_idx(XmssState* state, BdsState* bds, XmssIdx idx, + byte* wots_sigs, unsigned char* sk) +{ + int ret = 0; + const XmssParams* params = state->params; + const word8 n = params->n; + const word8 h = params->h; + const word8 hs = params->sub_h; + const word8 hsk = params->sub_h - params->bds_k; + const byte* sk_seed = sk + params->idx_len; + const byte* pk_seed = sk + params->idx_len + 3 * n; + XmssIdx idx_tree; + int computeAuthPath = 1; + unsigned int updates; + word8 i; + + /* Update BDS state - update authentication path for next index. */ + /* HDSS, Algorithm 4.6, Step 5: repeat (H - K) / 2 times. */ + updates = hsk >> 1; + + idx_tree = (idx >> hs) + 1; + /* Check whether last tree. */ + if (idx_tree < ((XmssIdx)1 << (h - hs))) { + /* Set hash address to next tree. */ + state->addr[XMSS_ADDR_LAYER] = 0; + xmss_idx_set_addr_tree(idx_tree, state->addr); + /* Update BDS state. */ + wc_xmss_bds_update(state, &bds[BDS_ALT_IDX(idx, 0, hs, params->d)], + sk_seed, pk_seed, state->addr); + ret = state->ret; + } + + for (i = 0; (ret == 0) && (i < params->d); i++) { + word32 idx_leaf; + word8 bds_i = BDS_IDX(idx, i, hs, params->d); + word8 alt_i = BDS_ALT_IDX(idx, i, hs, params->d); + + /* Check not last at height. */ + if (((idx + 1) << (IDX_MAX_BITS - ((i + 1) * hs))) != 0) { + state->addr[XMSS_ADDR_LAYER] = i; + xmss_idx_get_tree_leaf(idx >> (hs * i), hs, &idx_tree, &idx_leaf); + xmss_idx_set_addr_tree(idx_tree, state->addr); + idx_tree++; + + if (computeAuthPath) { + /* Compute authentication path for tree. */ + wc_xmss_bds_auth_path(state, &bds[bds_i], idx_leaf, sk_seed, + pk_seed, state->addr); + ret = state->ret; + computeAuthPath = 0; + } + + if (ret == 0) { + /* HDSS, Algorithm 4.6: Step 5. */ + updates = wc_xmss_bds_treehash_updates(state, &bds[bds_i], + updates, sk_seed, pk_seed, state->addr); + ret = state->ret; + } + + /* Check tree not first, updates to do, tree not last at height and + * next leaf in alt state is not last. */ + if ((ret == 0) && (i > 0) && (updates > 0) && + (idx_tree < ((XmssIdx)1 << (h - (hs * (i + 1))))) && + (bds[alt_i].next < ((word32)1 << h))) { + xmss_idx_set_addr_tree(idx_tree, state->addr); + /* Update alternative BDS state. */ + wc_xmss_bds_update(state, &bds[alt_i], sk_seed, pk_seed, + state->addr); + ret = state->ret; + updates--; + } + } + /* Last at height. */ + else { + /* Set layer, tree and OTS leaf index into hash address. */ + state->addr[XMSS_ADDR_LAYER] = i + 1; + idx_tree = (idx + 1) >> ((i + 1) * hs); + xmss_idx_get_tree_leaf(idx_tree, hs, &idx_tree, &idx_leaf); + xmss_idx_set_addr_tree(idx_tree, state->addr); + /* Cache WOTS+ signature for new tree. */ + state->addr[XMSS_ADDR_OTS] = idx_leaf; + wc_xmss_wots_sign(state, bds[alt_i].stack, sk_seed, pk_seed, + state->addr, wots_sigs + i * params->wots_sig_len); + ret = state->ret; + + if (ret == 0) { + word8 d; + + /* Reset old BDS state. */ + bds[bds_i].offset = 0; + bds[bds_i].next = 0; + + /* Done an update. */ + updates--; + /* Need to compute authentication path in next tree up. */ + computeAuthPath = 1; + /* Mark the tree hashes as complete in new BDS state. */ + for (d = 0; d < hsk; d++) { + wc_xmss_bds_state_treehash_complete(&bds[alt_i], d); + } + } + } + } + + return ret; +} + +/* Sign a message with XMSS^MT and update BDS state for signing next index. + * + * RFC 8391: 4.2.4, Algorithm 16: XMSS^MT_sign. + * HDSS, Section 4.5, The algorithm, Update and output phase. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] m Buffer holding message. + * @param [in] mlen Length of message in buffer. + * @param [in, out] sk Secret/Private key. + * @param [out] sig Signature and message data. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + * @return <0 on digest failure. + */ +int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen, + unsigned char* sk, unsigned char* sig) +{ + int ret = 0; + const XmssParams* params = state->params; + const word8 h = params->h; + const word8 idx_len = params->idx_len; + XmssIdx idx = 0; + byte* sig_mt = sig; + byte* wots_sigs; + BdsState* bds = NULL; + + /* Allocate memory for BDS states and tree hash instances. */ + ret = wc_xmss_bds_state_alloc(params, &bds); + if (ret == 0) { + /* Load the BDS state from secret/private key. */ + wc_xmss_bds_state_load(state, sk, bds, &wots_sigs); + + /* Copy the index into the signature data: Sig_MT = idx_sig. */ + XMEMCPY(sig_mt, sk, idx_len); + + /* Read index from the secret key. */ + xmss_idx_decode(&idx, idx_len, sk); + } + if ((ret == 0) && xmss_idx_invalid(idx, h)) { + /* Set index to maximum value to distinguish from valid value. */ + XMEMSET(sk, 0xFF, idx_len); + /* Zeroize the secret key. */ + ForceZero(sk + idx_len, params->sk_len - idx_len); + ret = KEY_EXHAUSTED_E; + } + + if (ret == 0) { + /* Increment the index in the secret key. */ + wc_idx_update(sk, idx_len); + + /* Compute signature. */ + ret = wc_xmssmt_sign_msg(state, bds, idx, wots_sigs, m, mlen, sk, sig); + } + + /* Only update if not last index. */ + if ((ret == 0) && (idx < (((XmssIdx)1 << h) - 1))) { + /* Update BDS state for signing next index. */ + ret = wc_xmssmt_sign_next_idx(state, bds, idx, wots_sigs, sk); + } + + if (ret == 0) { + /* Store BDS state back into secret/private key. */ + wc_xmss_bds_state_store(state, sk, bds); + } + + /* Dispose of allocated data of BDS states. */ + wc_xmss_bds_state_free(bds); + return ret; +} + +#endif /* WOLFSSL_WC_XMSS_SMALL */ + +/* Check if more signatures are possible with secret/private key. + * + * @param [in] params XMSS parameters + * @param [in] sk Secret/private key. + * @return 1 when signatures possible. + * @return 0 when key exhausted. + */ + +int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk) +{ + int ret = 0; + wc_Idx idx; + + /* Read index from the secret key. */ + WC_IDX_DECODE(idx, params->idx_len, sk, ret); + /* Check validity of index. */ + if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) { + ret = KEY_EXHAUSTED_E; + } + + return ret == 0; +} +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +/******************************************** + * SIGN OPEN - Verify + ********************************************/ + +#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY) +/* Compute root node with leaf and authentication path. + * + * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig + * ... + * for ( k = 0; k < h; k++ ) { + * ADRS.setTreeHeight(k); + * if ( (floor(idx_sig / (2^k)) % 2) == 0 ) { + * ADRS.setTreeIndex(ADRS.getTreeIndex() / 2); + * node[1] = RAND_HASH(node[0], auth[k], SEED, ADRS); + * } else { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node[1] = RAND_HASH(auth[k], node[0], SEED, ADRS); + * } + * node[0] = node[1]; + * } + * return node[0]; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] idx_leaf Index of leaf node. + * @param [in] auth_path Authentication path. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [in, out] root On in, leaf node. On out, root node. + */ +static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf, + const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* root) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + const byte* b[2][2] = { { root, auth_path }, { auth_path, root } }; + word8 i; + + for (i = 0; i < params->sub_h; i++) { + /* Get which side the leaf is on. */ + word8 s = idx_leaf & 1; + /* Set tree height and index. */ + addr[XMSS_ADDR_TREE_HEIGHT] = i; + idx_leaf >>= 1; + addr[XMSS_ADDR_TREE_INDEX] = idx_leaf; + + /* Put the result into buffer position for next RAND_HASH. */ + wc_xmss_rand_hash_lr(state, b[s][0], b[s][1], pk_seed, addr, root); + /* Move to next auth path node. */ + b[0][1] += n; + b[1][0] += n; + } +} +#else +/* Compute root node with leaf and authentication path. + * + * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig + * ... + * for ( k = 0; k < h; k++ ) { + * ADRS.setTreeHeight(k); + * if ( (floor(idx_sig / (2^k)) % 2) == 0 ) { + * ADRS.setTreeIndex(ADRS.getTreeIndex() / 2); + * node[1] = RAND_HASH(node[0], auth[k], SEED, ADRS); + * } else { + * ADRS.setTreeIndex((ADRS.getTreeIndex() - 1) / 2); + * node[1] = RAND_HASH(auth[k], node[0], SEED, ADRS); + * } + * node[0] = node[1]; + * } + * return node[0]; + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] idx_leaf Index of leaf node. + * @param [in] auth_path Authentication path. + * @param [in] pk_seed Random public seed. + * @param [in] addr Hash address. + * @param [in, out] node On in, leaf node. On out, root node. + */ +static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf, + const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* node) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + byte buffer[2 * WC_XMSS_MAX_N]; + byte* b[2][2] = { { buffer, buffer + n }, { buffer + n, buffer } }; + word8 i; + + /* Setup buffer for first RAND_HASH. */ + XMEMCPY(b[idx_leaf & 1][0], node, n); + XMEMCPY(b[idx_leaf & 1][1], auth_path, n); + auth_path += n; + + for (i = 0; i < params->sub_h - 1; i++) { + /* Set tree height and index. */ + addr[XMSS_ADDR_TREE_HEIGHT] = i; + idx_leaf >>= 1; + addr[XMSS_ADDR_TREE_INDEX] = idx_leaf; + + /* Put the result into buffer position for next RAND_HASH. */ + wc_xmss_rand_hash(state, buffer, pk_seed, addr, b[idx_leaf & 1][0]); + /* Put auth path node into other half of buffer. */ + XMEMCPY(b[idx_leaf & 1][1], auth_path, n); + /* Move to next auth path node. */ + auth_path += n; + } + + addr[XMSS_ADDR_TREE_HEIGHT] = i; + idx_leaf >>= 1; + addr[XMSS_ADDR_TREE_INDEX] = idx_leaf; + /* Last iteration into output node. */ + wc_xmss_rand_hash(state, buffer, pk_seed, addr, node); +} +#endif /* !WOLFSSL_WC_XMSS_SMALL || WOLFSSL_XMSS_VERIFY_ONLY */ + +/* Compute a root node from a tree signature. + * + * RFC 8391: 4.1.10, Algorithm 13: XMSS_rootFromSig + * ADRS.setType(0); # Type = OTS hash address + * ADRS.setOTSAddress(idx_sig); + * pk_ots = WOTS_pkFromSig(sig_ots, M', SEED, ADRS); + * ADRS.setType(1); # Type = L-tree address + * ADRS.setLTreeAddress(idx_sig); + * byte[n][2] node; + * node[0] = ltree(pk_ots, SEED, ADRS); + * ADRS.setType(2); # Type = hash tree address + * ADRS.setTreeIndex(idx_sig); + * [Compute root with leaf and authentication path] + * + * Computing the root from the leaf and authentication path can be implemented + * in different ways and is therefore extracted to its own function. + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] pk_seed Random public seed. + * @param [in] sig WOTS+ signature for this tree. + * @param [in] idx_sig Index of signature leaf in this tree. + * @param [in, out] addr Hash address. + * @param [in, out] node On in, previous root node. + * On out, root node of this subtree. + */ +static void wc_xmss_root_from_sig(XmssState* state, const byte* pk_seed, + const byte* sig, word32 idx_sig, HashAddress addr, byte* node) +{ + const XmssParams* params = state->params; + byte* wots_pk = state->pk; + const byte* auth_path = sig + params->wots_sig_len; + + /* Compute WOTS+ public key value from signature. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS; + addr[XMSS_ADDR_OTS] = idx_sig; + wc_xmss_wots_pk_from_sig(state, sig, node, pk_seed, addr, wots_pk); + + /* Compute leaves of L-tree from WOTS+ public key. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE; + /* XMSS_ADDR_LTREE is same as XMSS_ADDR_OTS in index and value. */ + wc_xmss_ltree(state, wots_pk, pk_seed, addr, node); + + /* Compute root node from leaf and authentication path. */ + addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE; + addr[XMSS_ADDR_TREE_ZERO] = 0; + wc_xmss_compute_root(state, idx_sig, auth_path, pk_seed, addr, node); +} + +/* Verify message with signature using XMSS/MT. + * + * RFC 8391: 4.2.5, Algorithm 17: XMSSMT_verify + * idx_sig = getIdx(Sig_MT); + * SEED = getSEED(PK_MT); + * ADRS = toByte(0, 32); + * + * byte[n] M' = H_msg(getR(Sig_MT) || getRoot(PK_MT) + * || (toByte(idx_sig, n)), M); + * + * unsigned int idx_leaf + * = (h / d) least significant bits of idx_sig; + * unsigned int idx_tree + * = (h - h / d) most significant bits of idx_sig; + * Sig' = getXMSSSignature(Sig_MT, 0); + * ADRS.setLayerAddress(0); + * ADRS.setTreeAddress(idx_tree); + * byte[n] node = XMSS_rootFromSig(idx_leaf, getSig_ots(Sig'), + * getAuth(Sig'), M', SEED, ADRS); + * for ( j = 1; j < d; j++ ) { + * idx_leaf = (h / d) least significant bits of idx_tree; + * idx_tree = (h - j * h / d) most significant bits of idx_tree; + * Sig' = getXMSSSignature(Sig_MT, j); + * ADRS.setLayerAddress(j); + * ADRS.setTreeAddress(idx_tree); + * node = XMSS_rootFromSig(idx_leaf, getSig_ots(Sig'), + * getAuth(Sig'), node, SEED, ADRS); + * } + * if ( node == getRoot(PK_MT) ) { + * return true; + * } else { + * return false; + * } + * + * @param [in] state XMSS/MT state including digest and parameters. + * @param [in] m Message buffer. + * @param [in] mlen Length of message in bytes. + * @param [in] sig Buffer holding signature. + * @param [in] pk Public key. + * @return 0 on success. + * @return MEMORY_E on dynamic memory allocation failure. + * @return SIG_VERIFY_E on verification failure. + * @return <0 on digest failure. + */ +int wc_xmssmt_verify(XmssState* state, const unsigned char* m, word32 mlen, + const unsigned char* sig, const unsigned char* pk) +{ + const XmssParams* params = state->params; + const word8 n = params->n; + int ret = 0; + const byte* pub_root = pk; + const byte* pk_seed = pk + n; + byte node[WC_XMSS_MAX_N]; + wc_Idx idx; + word32 idx_leaf = 0; + unsigned int i; + + /* Set 32/64-bit index to 0. */ + WC_IDX_ZERO(idx); + /* Set all address values to zero. */ + XMEMSET(state->addr, 0, sizeof(HashAddress)); + + if (ret == 0) { + /* Convert the index bytes from the signature to an integer. */ + WC_IDX_DECODE(idx, params->idx_len, sig, ret); + } + + if (ret == 0) { + const byte* sig_r = sig + params->idx_len; + /* byte[n] M' = H_msg(getR(Sig_MT) || getRoot(PK_MT) || + * (toByte(idx_sig, n)), M); + */ + wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m, + mlen, node); + ret = state->ret; + } + + if (ret == 0) { + /* Set tree of hash address. */ + WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr, + idx_leaf); + + /* Skip to first WOTS+ signature and derive root. */ + sig += params->idx_len + n; + wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr, + node); + ret = state->ret; + } + /* Calculate root of remaining subtrees up to top. */ + for (i = 1; (ret == 0) && (i < params->d); i++) { + /* Set layer and tree. */ + state->addr[XMSS_ADDR_LAYER] = i; + WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr, + idx_leaf); + /* Skip to next WOTS+ signature and derive root. */ + sig += params->wots_sig_len + params->sub_h * n; + wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr, + node); + ret = state->ret; + } + /* Compare calculated node with public key root. */ + if ((ret == 0) && (XMEMCMP(node, pub_root, n) != 0)) { + ret = SIG_VERIFY_E; + } + + return ret; +} +#endif /* WOLFSSL_HAVE_XMSS */ + diff --git a/wolfssl/wolfcrypt/wc_lms.h b/wolfssl/wolfcrypt/wc_lms.h index f51dad7bfc..6f90eaa3bd 100644 --- a/wolfssl/wolfcrypt/wc_lms.h +++ b/wolfssl/wolfcrypt/wc_lms.h @@ -19,5 +19,452 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -#error "Contact wolfSSL to get the implementation of this file" +/* Implementation based on: + * RFC 8554: Leighton-Micali Hash-Based Signatures + * https://datatracker.ietf.org/doc/html/rfc8554 + * Implementation by Sean Parkinson. + */ + +/* Possible LMS options: + * + * WOLFSSL_LMS_LARGE_CACHES Default: OFF + * Authentication path caches are large and signing faster. + * WOLFSSL_LMS_ROOT_LEVELS Default: 5 (Large: 7) + * Number of levels of interior nodes from the to to cached. + * Valid value are: 1..height of subtree. + * The bigger the number, the larger the LmsKey but faster signing. + * Only applies when !WOLFSSL_WC_LMS_SMALL. + * WOLFSSL_LMS_CACHE_BITS Default: 5 (Large: 7) + * 2 to the power of the value is the number of leaf nodes to cache. + * Maximum valid value is height of subtree. + * Valid value are: 0..height of subtree. + * The bigger the number, the larger the LmsKey but faster signing. + * Only applies when !WOLFSSL_WC_LMS_SMALL. + * + * Memory/Level | R/C | Approx. Time (% of 5/5) + * (Bytes) | | H=10 | H=15 | H=20 + * -------------+--------------+--------+-------- + * 2016 | 5/5 | 100.0% | 100.0% | 100.0% + * 3040 | 5/6 | 75.5% | 89.2% | + * 4064 | 6/6 | 75.3% | 78.8% | + * 4576 | 4/7 | 72.4% | 87.6% | + * 6112 | 6/7 | 72.1% | 67.5% | + * 8160 | 7/7 | 72.2% | 56.8% | + * 8416 | 3/8 | 66.4% | 84.9% | + * 12256 | 7/8 | 66.5% | 45.9% | + * 16352 | 8/8 | 66.0% | 35.0% | + * 16416 | 1/9 | 54.1% | 79.5% | + * R = Root levels + * C = Cache bits + * To mimic the dynamic memory usage of XMSS, use 3/3. + * + * WOLFSSL_LMS_NO_SIGN SMOOTHING Default: OFF + * Disable precalculation of next subtree. + * Use less dynamic memory. + * At certain indexes, signing will take a long time compared to the mean. + * When OFF, the private key holds a second copy of caches. + * + * WOLFSSL_LMS_NO_SIG_CACHE Default: OFF + * Signature cache is disabled. + * This will use less dynamic memory and make signing slower when multiple + * levels. + * + * Sig cache holds the C and y hashes for a tree that is not the lowest. + * Sig cache size = (levels - 1) * (1 + p) * 32 bytes + * p is the number of y terms based on Winternitz width. + * + * w | p | l | Bytes + * ---+----+---+------ + * 4 | 67 | 2 | 2176 + * 4 | 67 | 3 | 4353 + * 4 | 67 | 4 | 6528 + * 8 | 34 | 2 | 1120 + * 8 | 34 | 3 | 2240 + * 8 | 34 | 4 | 3360 + * w = Winternitz width + * l = #levels + */ + +#ifndef WC_LMS_H +#define WC_LMS_H + +#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) + +#include +#include + +#ifdef WOLFSSL_LMS_MAX_LEVELS + /* Maximum number of levels of trees supported by implementation. */ + #define LMS_MAX_LEVELS WOLFSSL_LMS_MAX_LEVELS +#else + /* Maximum number of levels of trees supported by implementation. */ + #define LMS_MAX_LEVELS 4 +#endif +#if (LMS_MAX_LEVELS < 1) || (LMS_MAX_LEVELS > 4) + #error "LMS parameters only support heights 1-4." +#endif + +/* Smoothing is only used when there are 2 or more levels. */ +#if LMS_MAX_LEVELS == 1 && !defined(WOLFSSL_LMS_NO_SIGN_SMOOTHING) + #define WOLFSSL_LMS_NO_SIGN_SMOOTHING +#endif + +#ifdef WOLFSSL_LMS_MAX_HEIGHT + /* Maximum height of a tree supported by implementation. */ + #define LMS_MAX_HEIGHT WOLFSSL_LMS_MAX_HEIGHT +#else + /* Maximum height of a tree supported by implementation. */ + #define LMS_MAX_HEIGHT 20 +#endif +#if (LMS_MAX_HEIGHT < 5) || (LMS_MAX_HEIGHT > 20) + #error "LMS parameters only support heights 5-20." +#endif + +/* Length of I in bytes. */ +#define LMS_I_LEN 16 +/* Length of L in bytes. */ +#define LMS_L_LEN 4 +/* Length of Q for a level. */ +#define LMS_Q_LEN 4 +/* Length of P in bytes. */ +#define LMS_P_LEN 2 +/* Length of W in bytes. */ +#define LMS_W_LEN 1 + +/* Length of numeric types when encoding. */ +#define LMS_TYPE_LEN 4 + +/* Maximum size of a node hash. */ +#define LMS_MAX_NODE_LEN WC_SHA256_DIGEST_SIZE +/* Maximum size of SEED (produced by hash). */ +#define LMS_SEED_LEN WC_SHA256_DIGEST_SIZE +/* Maximum number of P, number of n-byte string elements in LM-OTS signature. + * Value of P when N=32 and W=1. + */ +#define LMS_MAX_P 265 +/* Length of SEED and I in bytes. */ +#define LMS_SEED_I_LEN (LMS_SEED_LEN + LMS_I_LEN) + + +#ifndef WOLFSSL_LMS_ROOT_LEVELS + #ifdef WOLFSSL_LMS_LARGE_CACHES + /* Number of root levels of interior nodes to store. */ + #define LMS_ROOT_LEVELS 7 + #else + /* Number of root levels of interior nodes to store. */ + #define LMS_ROOT_LEVELS 5 + #endif +#else + #define LMS_ROOT_LEVELS WOLFSSL_LMS_ROOT_LEVELS +#endif +#if LMS_ROOT_LEVELS <= 0 + #error "LMS_ROOT_LEVELS must be greater than 0." +#endif +/* Count of root nodes to store per level. */ +#define LMS_ROOT_COUNT ((1 << (LMS_ROOT_LEVELS)) - 1) + +#ifndef WOLFSSL_LMS_CACHE_BITS + #ifdef WOLFSSL_LMS_LARGE_CACHES + /* 2 to the power of the value is the number of leaf nodes to cache. */ + #define LMS_CACHE_BITS 7 + #else + /* 2 to the power of the value is the number of leaf nodes to cache. */ + #define LMS_CACHE_BITS 5 + #endif +#else + #define LMS_CACHE_BITS WOLFSSL_LMS_CACHE_BITS +#endif +#if LMS_CACHE_BITS < 0 + #error "LMS_CACHE_BITS must be greater than or equal to 0." +#endif +/* Number of leaf nodes to cache. */ +#define LMS_LEAF_CACHE (1 << LMS_CACHE_BITS) + +/* Maximum number of levels of trees described in private key. */ +#define HSS_MAX_LEVELS 8 +/* Length of full Q in bytes. Q from all levels combined. */ +#define HSS_Q_LEN 8 + +/* Compressed parameter set length in bytes. */ +#define HSS_COMPRESS_PARAM_SET_LEN 1 +/* Total compressed parameter set length for private key in bytes. */ +#define HSS_PRIV_KEY_PARAM_SET_LEN \ + (HSS_COMPRESS_PARAM_SET_LEN * HSS_MAX_LEVELS) + +/* Private key length for one level. */ +#define LMS_PRIV_LEN \ + (LMS_Q_LEN + LMS_SEED_LEN + LMS_I_LEN) +/* Public key length in signature. */ +#define LMS_PUBKEY_LEN \ + (LMS_TYPE_LEN + LMS_TYPE_LEN + LMS_I_LEN + LMS_MAX_NODE_LEN) + +/* LMS signature data length. */ +#define LMS_SIG_LEN(h, p) \ + (LMS_Q_LEN + LMS_TYPE_LEN + LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN + \ + LMS_TYPE_LEN + (h) * LMS_MAX_NODE_LEN) + +/* Length of public key. */ +#define HSS_PUBLIC_KEY_LEN (LMS_L_LEN + LMS_PUBKEY_LEN) +/* Length of private key. */ +#define HSS_PRIVATE_KEY_LEN \ + (HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN + LMS_SEED_LEN + LMS_I_LEN) +/* Maximum public key length - length is constant for all parameters. */ +#define HSS_MAX_PRIVATE_KEY_LEN HSS_PRIVATE_KEY_LEN +/* Maximum private key length - length is constant for all parameters. */ +#define HSS_MAX_PUBLIC_KEY_LEN HSS_PUBLIC_KEY_LEN +/* Maximum signature length. */ +#define HSS_MAX_SIG_LEN \ + (LMS_TYPE_LEN + \ + LMS_MAX_LEVELS * (LMS_Q_LEN + LMS_TYPE_LEN + LMS_TYPE_LEN + \ + LMS_MAX_NODE_LEN * (1 + LMS_MAX_P + LMS_MAX_HEIGHT)) + \ + (LMS_MAX_LEVELS - 1) * LMS_PUBKEY_LEN \ + ) + +/* Maximum buffer length required for use when hashing. */ +#define LMS_MAX_BUFFER_LEN \ + (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + 2 * LMS_MAX_NODE_LEN) + + +/* Private key data length. + * + * HSSPrivKey.priv + */ +#define LMS_PRIV_KEY_LEN(l) \ + ((l) * LMS_PRIV_LEN) + +/* Stack of nodes. */ +#define LMS_STACK_CACHE_LEN(h) \ + (((h) + 1) * LMS_MAX_NODE_LEN) + +/* Root cache length. */ +#define LMS_ROOT_CACHE_LEN(rl) \ + (((1 << (rl)) - 1) * LMS_MAX_NODE_LEN) + +/* Leaf cache length. */ +#define LMS_LEAF_CACHE_LEN(cb) \ + ((1 << (cb)) * LMS_MAX_NODE_LEN) + +/* Length of LMS private key state. + * + * LmsPrivState + * auth_path + + * root + + * stack.stack + stack.offset + + * cache.leaf + cache.index + cache.offset + */ +#define LMS_PRIV_STATE_LEN(h, rl, cb) \ + (((h) * LMS_MAX_NODE_LEN) + \ + LMS_STACK_CACHE_LEN(h) + 4 + \ + LMS_ROOT_CACHE_LEN(rl) + \ + LMS_LEAF_CACHE_LEN(cb) + 4 + 4) + +#ifndef WOLFSSL_WC_LMS_SMALL + /* Private key data state for all levels. */ + #define LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb) \ + ((l) * LMS_PRIV_STATE_LEN(h, rl, cb)) +#else + /* Private key data state for all levels. */ + #define LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb) 0 +#endif + +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + /* Extra private key data for smoothing. */ + #define LMS_PRIV_SMOOTH_LEN(l, h, rl, cb) \ + (LMS_PRIV_KEY_LEN(l) + \ + ((l) - 1) * LMS_PRIV_STATE_LEN(h, rl, cb)) +#else + /* Extra private key data for smoothing. */ + #define LMS_PRIV_SMOOTH_LEN(l, h, rl, cb) 0 +#endif + +#ifndef WOLFSSL_LMS_NO_SIG_CACHE + #define LMS_PRIV_Y_TREE_LEN(p) \ + (LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN) + /* Length of the y data cached in private key data. */ + #define LMS_PRIV_Y_LEN(l, p) \ + (((l) - 1) * (LMS_MAX_NODE_LEN + (p) * LMS_MAX_NODE_LEN)) +#else + /* Length of the y data cached in private key data. */ + #define LMS_PRIV_Y_LEN(l, p) 0 +#endif + +#ifndef WOLFSSL_WC_LMS_SMALL +/* Length of private key data. */ +#define LMS_PRIV_DATA_LEN(l, h, p, rl, cb) \ + (LMS_PRIV_KEY_LEN(l) + \ + LMS_PRIV_STATE_ALL_LEN(l, h, rl, cb) + \ + LMS_PRIV_SMOOTH_LEN(l, h, rl, cb) + \ + LMS_PRIV_Y_LEN(l, p)) +#else +#define LMS_PRIV_DATA_LEN(l, h, p, rl, cb) \ + LMS_PRIV_KEY_LEN(l) +#endif + + +/* LMS Parameters. */ +/* SHA-256 hash, 32-bytes of hash used, tree height of 5. */ +#define LMS_SHA256_M32_H5 5 +/* SHA-256 hash, 32-bytes of hash used, tree height of 10. */ +#define LMS_SHA256_M32_H10 6 +/* SHA-256 hash, 32-bytes of hash used, tree height of 15. */ +#define LMS_SHA256_M32_H15 7 +/* SHA-256 hash, 32-bytes of hash used, tree height of 20. */ +#define LMS_SHA256_M32_H20 8 +/* SHA-256 hash, 32-bytes of hash used, tree height of 25. */ +#define LMS_SHA256_M32_H25 9 + +/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 1 bit. */ +#define LMOTS_SHA256_N32_W1 1 +/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 2 bits. */ +#define LMOTS_SHA256_N32_W2 2 +/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 4 bits. */ +#define LMOTS_SHA256_N32_W4 3 +/* SHA-256 hash, 32-bytes of hash used, Winternitz width of 8 bits. */ +#define LMOTS_SHA256_N32_W8 4 + +typedef struct LmsParams { + /* Number of tree levels. */ + word8 levels; + /* Height of each tree. */ + word8 height; + /* Width or Winternitz coefficient. */ + word8 width; + /* Number of left-shift bits used in checksum calculation. */ + word8 ls; + /* Number of n-byte string elements in LM-OTS signature. */ + word16 p; + /* LMS type. */ + word16 lmsType; + /* LMOTS type. */ + word16 lmOtsType; + /* Length of LM-OTS signature. */ + word16 sig_len; +#ifndef WOLFSSL_WC_LMS_SMALL + /* Number of root levels of interior nodes to store. */ + word8 rootLevels; + /* 2 to the power of the value is the number of leaf nodes to cache. */ + word8 cacheBits; +#endif +} LmsParams; + +/* Mapping of id and string to parameters. */ +typedef struct wc_LmsParamsMap { + /* Identifier of parameters. */ + enum wc_LmsParm id; + /* String representation of identifier of parameters. */ + const char* str; + /* LMS parameter set. */ + LmsParams params; +} wc_LmsParamsMap; + +typedef struct LmsState { + /* Buffer to hold data to hash. */ + ALIGN16 byte buffer[LMS_MAX_BUFFER_LEN]; +#ifdef WOLFSSL_SMALL_STACK + /* Buffer to hold expanded Q coefficients. */ + ALIGN16 byte a[LMS_MAX_P]; +#endif + /* LMS parameters. */ + const LmsParams* params; + /* Hash algorithm. */ + wc_Sha256 hash; + /* Hash algorithm for calculating K. */ + wc_Sha256 hash_k; +} LmsState; + +#ifndef WOLFSSL_WC_LMS_SMALL +/* Stack of interior node hashes. */ +typedef struct LmsStack { + /* Stack nodes. */ + byte* stack; + /* Top of stack offset. */ + word32 offset; +} LmsStack; + +/* Cache of leaf hashes. */ +typedef struct HssLeafCache { + /* Cache of leaf nodes. Circular queue. */ + byte* cache; + /* Start index of cached leaf nodes. */ + word32 idx; + /* Index into cache of first leaf node. */ + word32 offset; +} HssLeafCache; + +typedef struct LmsPrivState { + /* Authentication path for current index. */ + byte* auth_path; + /* Stack nodes. */ + LmsStack stack; + /* Root nodes. */ + byte* root; + /* Cache of leaf nodes. */ + HssLeafCache leaf; +} LmsPrivState; +#endif /* WOLFSSL_WC_LMS_SMALL */ + +typedef struct HssPrivKey { + /* Private key. */ + byte* priv; +#ifndef WOLFSSL_WC_LMS_SMALL + /* Per level state of the private key. */ + LmsPrivState state[LMS_MAX_LEVELS]; +#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING + /* Next private key. */ + byte* next_priv; + /* Next private state. */ + LmsPrivState next_state[LMS_MAX_LEVELS - 1]; +#endif +#ifndef WOLFSSL_LMS_NO_SIG_CACHE + /* Per level state of the private key. */ + byte* y; +#endif + /* Indicates the key has all levels initialized. */ + word8 inited:1; +#endif +} HssPrivKey; + +struct LmsKey { + /* Public key. */ + ALIGN16 byte pub[HSS_PUBLIC_KEY_LEN]; +#ifndef WOLFSSL_LMS_VERIFY_ONLY + /* Encoded private key. */ + ALIGN16 byte priv_raw[HSS_PRIVATE_KEY_LEN]; + + /* Packed private key data. */ + byte* priv_data; + /* HSS Private key. */ + HssPrivKey priv; + + /* Callback to write/update key. */ + wc_lms_write_private_key_cb write_private_key; + /* Callback to read key. */ + wc_lms_read_private_key_cb read_private_key; + /* Context arg passed to callbacks. */ + void* context; + /* Dynamic memory hint. */ + void* heap; +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + /* Parameters of key. */ + const LmsParams* params; + /* Current state of key. */ + enum wc_LmsState state; +#ifdef WOLF_CRYPTO_CB + /* Device Identifier. */ + int devId; +#endif +}; + +int wc_hss_make_key(LmsState* state, WC_RNG* rng, byte* priv_raw, + HssPrivKey* priv_key, byte* priv_data, byte* pub); +int wc_hss_reload_key(LmsState* state, const byte* priv_raw, + HssPrivKey* priv_key, byte* priv_data, byte* pub_root); +int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key, + byte* priv_data, const byte* msg, word32 msgSz, byte* sig); +int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw); +int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg, + word32 msgSz, const byte* sig); + +#endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS */ +#endif /* WC_LMS_H */ diff --git a/wolfssl/wolfcrypt/wc_xmss.h b/wolfssl/wolfcrypt/wc_xmss.h index 96274d7941..9d88fbf71d 100644 --- a/wolfssl/wolfcrypt/wc_xmss.h +++ b/wolfssl/wolfcrypt/wc_xmss.h @@ -19,5 +19,267 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ -#error "Contact wolfSSL to get the implementation of this file" +/* Based on: + * o RFC 8391 - XMSS: eXtended Merkle Signature Scheme + * o [HDSS] "Hash-based Digital Signature Schemes", Buchmann, Dahmen and Szydlo + * from "Post Quantum Cryptography", Springer 2009. + */ + +#ifndef WC_XMSS_H +#define WC_XMSS_H + +#ifdef WOLFSSL_HAVE_XMSS +#include +#include +#include +#include + +#if !defined(WOLFSSL_WC_XMSS) + #error "This code is incompatible with external implementation of XMSS." +#endif + +#if (defined(WC_XMSS_SHA512) || defined(WC_XMSS_SHAKE256)) && \ + (WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 512) + #define WC_XMSS_MAX_N 64 + #define WC_XMSS_MAX_PADDING_LEN 64 +#else + #define WC_XMSS_MAX_N 32 + #define WC_XMSS_MAX_PADDING_LEN 32 +#endif +#define WC_XMSS_MAX_MSG_PRE_LEN \ + (WC_XMSS_MAX_PADDING_LEN + 3 * WC_XMSS_MAX_N) +#define WC_XMSS_MAX_TREE_HEIGHT 20 +#define WC_XMSS_MAX_CSUM_BYTES 4 +#define WC_XMSS_MAX_WOTS_LEN (8 * WC_XMSS_MAX_N / 4 + 3) +#define WC_XMSS_MAX_WOTS_SIG_LEN (WC_XMSS_MAX_WOTS_LEN * WC_XMSS_MAX_N) +#define WC_XMSS_MAX_STACK_LEN \ + ((WC_XMSS_MAX_TREE_HEIGHT + 1) * WC_XMSS_MAX_N) +#define WC_XMSS_MAX_D 12 +#define WC_XMSS_MAX_BDS_STATES (2 * WC_XMSS_MAX_D - 1) +#define WC_XMSS_MAX_TREE_HASH \ + ((2 * WC_XMSS_MAX_D - 1) * WC_XMSS_MAX_TREE_HEIGHT) +#define WC_XMSS_MAX_BDS_K 0 + +#define WC_XMSS_ADDR_LEN 32 + +#define WC_XMSS_HASH_PRF_MAX_DATA_LEN \ + (WC_XMSS_MAX_PADDING_LEN + 2 * WC_XMSS_MAX_N + WC_XMSS_ADDR_LEN) +#define WC_XMSS_HASH_MAX_DATA_LEN \ + (WC_XMSS_MAX_PADDING_LEN + 3 * WC_XMSS_MAX_N) + + +#define WC_XMSS_SHA256_N 32 +#define WC_XMSS_SHA256_PADDING_LEN 32 +#define WC_XMSS_SHA256_WOTS_LEN 67 + +#define XMSS_OID_LEN 4 + +#define XMSS_MAX_HASH_LEN WC_SHA256_DIGEST_SIZE + +#define XMSS_RETAIN_LEN(k, n) ((!!(k)) * ((1 << (k)) - (k) - 1) * (n)) + +/* XMMS Algorithm OIDs + * Note: values are used in mathematical calculations in OID to parames. */ +#define WC_XMSS_OID_SHA2_10_256 0x01 +#define WC_XMSS_OID_SHA2_16_256 0x02 +#define WC_XMSS_OID_SHA2_20_256 0x03 +#define WC_XMSS_OID_SHA2_10_512 0x04 +#define WC_XMSS_OID_SHA2_16_512 0x05 +#define WC_XMSS_OID_SHA2_20_512 0x06 +#define WC_XMSS_OID_SHAKE_10_256 0x07 +#define WC_XMSS_OID_SHAKE_16_256 0x08 +#define WC_XMSS_OID_SHAKE_20_256 0x09 +#define WC_XMSS_OID_SHAKE_10_512 0x0a +#define WC_XMSS_OID_SHAKE_16_512 0x0b +#define WC_XMSS_OID_SHAKE_20_512 0x0c +#define WC_XMSS_OID_SHA2_10_192 0x0d +#define WC_XMSS_OID_SHA2_16_192 0x0e +#define WC_XMSS_OID_SHA2_20_192 0x0f +#define WC_XMSS_OID_SHAKE256_10_256 0x10 +#define WC_XMSS_OID_SHAKE256_16_256 0x11 +#define WC_XMSS_OID_SHAKE256_20_256 0x12 +#define WC_XMSS_OID_SHAKE256_10_192 0x13 +#define WC_XMSS_OID_SHAKE256_16_192 0x14 +#define WC_XMSS_OID_SHAKE256_20_192 0x15 +#define WC_XMSS_OID_FIRST WC_XMSS_OID_SHA2_10_256 +#define WC_XMSS_OID_LAST WC_XMSS_OID_SHAKE256_20_192 + +/* XMMS^MT Algorithm OIDs + * Note: values are used in mathematical calculations in OID to parames. */ +#define WC_XMSSMT_OID_SHA2_20_2_256 0x01 +#define WC_XMSSMT_OID_SHA2_20_4_256 0x02 +#define WC_XMSSMT_OID_SHA2_40_2_256 0x03 +#define WC_XMSSMT_OID_SHA2_40_4_256 0x04 +#define WC_XMSSMT_OID_SHA2_40_8_256 0x05 +#define WC_XMSSMT_OID_SHA2_60_3_256 0x06 +#define WC_XMSSMT_OID_SHA2_60_6_256 0x07 +#define WC_XMSSMT_OID_SHA2_60_12_256 0x08 +#define WC_XMSSMT_OID_SHA2_20_2_512 0x09 +#define WC_XMSSMT_OID_SHA2_20_4_512 0x0a +#define WC_XMSSMT_OID_SHA2_40_2_512 0x0b +#define WC_XMSSMT_OID_SHA2_40_4_512 0x0c +#define WC_XMSSMT_OID_SHA2_40_8_512 0x0d +#define WC_XMSSMT_OID_SHA2_60_3_512 0x0e +#define WC_XMSSMT_OID_SHA2_60_6_512 0x0f +#define WC_XMSSMT_OID_SHA2_60_12_512 0x10 +#define WC_XMSSMT_OID_SHAKE_20_2_256 0x11 +#define WC_XMSSMT_OID_SHAKE_20_4_256 0x12 +#define WC_XMSSMT_OID_SHAKE_40_2_256 0x13 +#define WC_XMSSMT_OID_SHAKE_40_4_256 0x14 +#define WC_XMSSMT_OID_SHAKE_40_8_256 0x15 +#define WC_XMSSMT_OID_SHAKE_60_3_256 0x16 +#define WC_XMSSMT_OID_SHAKE_60_6_256 0x17 +#define WC_XMSSMT_OID_SHAKE_60_12_256 0x18 +#define WC_XMSSMT_OID_SHAKE_20_2_512 0x19 +#define WC_XMSSMT_OID_SHAKE_20_4_512 0x1a +#define WC_XMSSMT_OID_SHAKE_40_2_512 0x1b +#define WC_XMSSMT_OID_SHAKE_40_4_512 0x1c +#define WC_XMSSMT_OID_SHAKE_40_8_512 0x1d +#define WC_XMSSMT_OID_SHAKE_60_3_512 0x1e +#define WC_XMSSMT_OID_SHAKE_60_6_512 0x1f +#define WC_XMSSMT_OID_SHAKE_60_12_512 0x20 +#define WC_XMSSMT_OID_SHA2_20_2_192 0x21 +#define WC_XMSSMT_OID_SHA2_20_4_192 0x22 +#define WC_XMSSMT_OID_SHA2_40_2_192 0x23 +#define WC_XMSSMT_OID_SHA2_40_4_192 0x24 +#define WC_XMSSMT_OID_SHA2_40_8_192 0x25 +#define WC_XMSSMT_OID_SHA2_60_3_192 0x26 +#define WC_XMSSMT_OID_SHA2_60_6_192 0x27 +#define WC_XMSSMT_OID_SHA2_60_12_192 0x28 +#define WC_XMSSMT_OID_SHAKE256_20_2_256 0x29 +#define WC_XMSSMT_OID_SHAKE256_20_4_256 0x2a +#define WC_XMSSMT_OID_SHAKE256_40_2_256 0x2b +#define WC_XMSSMT_OID_SHAKE256_40_4_256 0x2c +#define WC_XMSSMT_OID_SHAKE256_40_8_256 0x2d +#define WC_XMSSMT_OID_SHAKE256_60_3_256 0x2e +#define WC_XMSSMT_OID_SHAKE256_60_6_256 0x2f +#define WC_XMSSMT_OID_SHAKE256_60_12_256 0x30 +#define WC_XMSSMT_OID_SHAKE256_20_2_192 0x31 +#define WC_XMSSMT_OID_SHAKE256_20_4_192 0x32 +#define WC_XMSSMT_OID_SHAKE256_40_2_192 0x33 +#define WC_XMSSMT_OID_SHAKE256_40_4_192 0x34 +#define WC_XMSSMT_OID_SHAKE256_40_8_192 0x35 +#define WC_XMSSMT_OID_SHAKE256_60_3_192 0x36 +#define WC_XMSSMT_OID_SHAKE256_60_6_192 0x37 +#define WC_XMSSMT_OID_SHAKE256_60_12_192 0x38 +#define WC_XMSSMT_OID_FIRST WC_XMSSMT_OID_SHA2_20_2_256 +#define WC_XMSSMT_OID_LAST WC_XMSSMT_OID_SHAKE256_60_12_192 + + +/* Type for hash address. */ +typedef word32 HashAddress[8]; + +/* XMSS/XMSS^MT fixed parameters. */ +typedef struct XmssParams { + /* Hash algorithm to use. */ + word8 hash; + /* Size of hash output. */ + word8 n; + /* Number of bytes of padding before rest of hash data. */ + word8 pad_len; + /* Number of values to chain = 2 * n + 3. */ + word8 wots_len; + /* Number of bytes in each WOTS+ signature. */ + word16 wots_sig_len; + /* Full height of tree. */ + word8 h; + /* Height of tree each subtree. */ + word8 sub_h; + /* Number of subtrees = h / sub_h. */ + word8 d; + /* Number of bytes to encode index into in private/secret key. */ + word8 idx_len; + /* Number of bytes in a signature. */ + word32 sig_len; + /* Number of bytes in a secret/private key. */ + word32 sk_len; + /* Number of bytes in a public key. */ + word8 pk_len; + /* BDS parameter for fast C implementation. */ + word8 bds_k; +} XmssParams; + +struct XmssKey { + /* Public key. */ + unsigned char pk[2 * WC_XMSS_MAX_N]; + /* OID that identifies parameters. */ + word32 oid; + /* Indicates whether the parameters are for XMSS^MT. */ + int is_xmssmt; + /* XMSS/XMSS^MT parameters. */ + const XmssParams* params; +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + /* Secret/private key. */ + unsigned char* sk; + /* Length of secret key. */ + word32 sk_len; + /* Callback to write/update key. */ + wc_xmss_write_private_key_cb write_private_key; + /* Callback to read key. */ + wc_xmss_read_private_key_cb read_private_key; + /* Context arg passed to callbacks. */ + void* context; +#endif /* ifndef WOLFSSL_XMSS_VERIFY_ONLY */ + /* State of key. */ + enum wc_XmssState state; +}; + +typedef struct XmssState { + const XmssParams* params; + + /* Digest is assumed to be at the end. */ + union { + #ifdef WC_XMSS_SHA256 + wc_Sha256 sha256; + #endif + #ifdef WC_XMSS_SHA512 + wc_Sha512 sha512; + #endif + #if defined(WC_XMSS_SHAKE128) || defined(WC_XMSS_SHAKE256) + wc_Shake shake; + #endif + } digest; +#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \ + !defined(WC_XMSS_FULL_HASH) + ALIGN16 word32 dgst_state[WC_SHA256_DIGEST_SIZE / sizeof(word32)]; +#endif + ALIGN16 byte prf_buf[WC_XMSS_HASH_PRF_MAX_DATA_LEN]; + ALIGN16 byte buf[WC_XMSS_HASH_MAX_DATA_LEN]; + ALIGN16 byte pk[WC_XMSS_MAX_WOTS_SIG_LEN]; +#ifndef WOLFSSL_XMSS_VERIFY_ONLY + ALIGN16 byte stack[WC_XMSS_MAX_STACK_LEN]; +#else + ALIGN16 byte stack[WC_XMSS_ADDR_LEN]; +#endif + byte encMsg[WC_XMSS_MAX_WOTS_LEN]; + HashAddress addr; + + int ret; +} XmssState; + +#ifdef __cplusplus + extern "C" { +#endif + +WOLFSSL_LOCAL int wc_xmssmt_keygen(XmssState *state, const unsigned char* seed, + unsigned char *sk, unsigned char *pk); +WOLFSSL_LOCAL int wc_xmss_keygen(XmssState *state, const unsigned char* seed, + unsigned char *sk, unsigned char *pk); + +WOLFSSL_LOCAL int wc_xmssmt_sign(XmssState *state, const unsigned char *m, + word32 mlen, unsigned char *sk, unsigned char *sm); +WOLFSSL_LOCAL int wc_xmss_sign(XmssState *state, const unsigned char *m, + word32 mlen, unsigned char *sk, unsigned char *sm); + +WOLFSSL_LOCAL int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk); + +WOLFSSL_LOCAL int wc_xmssmt_verify(XmssState *state, const unsigned char *m, + word32 mlen, const unsigned char *sm, const unsigned char *pk); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFSSL_HAVE_XMSS */ +#endif /* WC_XMSS_H */