diff --git a/config/examples/sim-xmss.config b/config/examples/sim-xmss.config new file mode 100644 index 00000000..57c77f43 --- /dev/null +++ b/config/examples/sim-xmss.config @@ -0,0 +1,26 @@ +# XMSS/XMSS^MT/HSS signature example, based on sim.config example. +# +# + +ARCH=sim +TARGET=sim +SIGN?=XMSS +HASH?=SHA256 +XMSS_PARAMS='XMSS-SHA2_10_256' +WOLFBOOT_SMALL_STACK=0 +SPI_FLASH=0 +DEBUG=0 +DELTA_UPDATES=0 +IMAGE_SIGNATURE_SIZE=2500 +IMAGE_HEADER_SIZE?=5000 + +# sizes should be multiple of system page size +WOLFBOOT_PARTITION_SIZE=0x40000 +WOLFBOOT_SECTOR_SIZE=0x1000 +WOLFBOOT_PARTITION_BOOT_ADDRESS=0x20000 +# if on external flash, it should be multiple of system page size +WOLFBOOT_PARTITION_UPDATE_ADDRESS=0x60000 +WOLFBOOT_PARTITION_SWAP_ADDRESS=0xA0000 + +# required for keytools +WOLFBOOT_FIXED_PARTITIONS=1 diff --git a/docs/PQ.md b/docs/PQ.md index 21bae8e1..92d828d5 100644 --- a/docs/PQ.md +++ b/docs/PQ.md @@ -1,17 +1,27 @@ # Post-Quantum Signatures wolfBoot is adding support for post-quantum signatures. At present, support -for LMS/HSS signatures has been added. +for LMS/HSS (https://www.rfc-editor.org/rfc/rfc8554.html), and XMSS/XMSS^MT +(https://www.rfc-editor.org/rfc/rfc8391.html) has been added. -## LMS/HSS - -LMS/HSS is a post-quantum stateful hash-based signature scheme (HBS). It -is known for having small public and private keys, but larger signatures. -The signature size is tunable via the different LMS parameters. +LMS/HSS and XMSS/XMSS^MT are both post-quantum stateful hash-based signature +(HBS) schemes. They are known for having small public keys, relatively fast +signing and verifying operations, but larger signatures. Their signature sizes +however are tunable via their different parameters, which affords a space-time +tradeoff. Stateful HBS schemes are based on the security of their underlying hash functions and Merkle trees, which are not expected to be broken by the advent -of cryptographically relevant quantum computers. +of cryptographically relevant quantum computers. For this reason they have +been recommended by both NIST SP 800-208, and the NSA’s CNSA 2.0 suite. + +See these links for more info on stateful HBS support and wolfSSL/wolfCrypt: +- https://www.wolfssl.com/documentation/manuals/wolfssl/appendix07.html#post-quantum-stateful-hash-based-signatures +- https://github.com/wolfSSL/wolfssl-examples/tree/master/pq/stateful_hash_sig + + +## LMS/HSS + ### Building with LMS Support @@ -44,7 +54,7 @@ keygen, signing, and verifying functionality. However wolfBoot links directly with the subset of objects in the `hss_verify.a` build rule, as it only requires verify functionality. -### Config +### LMS Config A new LMS sim example has been added here: ``` @@ -69,17 +79,66 @@ In LMS the signature size is a function of the parameters. Use the added helper script `tools/lms/lms_siglen.sh` to calculate your signature length given your LMS parameters: ``` -$./tools/lms/lms_siglen.sh -levels: 3 -height: 5 -winternitz: 8 -# -total_len: 3992 +$ ./tools/lms/lms_siglen.sh 2 5 8 +levels: 2 +height: 5 +winternitz: 8 +signature length: 2644 ``` -### More Info +## XMSS/XMSS^MT -See these links for more info on LMS and wolfSSL/wolfCrypt: -- https://www.wolfssl.com/documentation/manuals/wolfssl/appendix07.html#post-quantum-stateful-hash-based-signatures -- https://github.com/wolfSSL/wolfssl-examples/tree/master/pq/stateful_hash_sig +### Building with XMSS Support +XMSS/XMSS^MT support in wolfCrypt requires a patched version of the +xmss-reference library ( https://github.com/XMSS/xmss-reference.git ). +Use the following procedure to prepare xmss-reference for building with +wolfBoot: + +``` +$ cd lib +$ git clone https://github.com/XMSS/xmss-reference.git xmss +$ ls +CMakeLists.txt wolfPKCS11 wolfTPM wolfssl xmss +$ cd xmss +$ git checkout 171ccbd26f098542a67eb5d2b128281c80bd71a6 +$ git apply ../../../tools/xmss/0001-Patch-to-support-wolfSSL-xmss-reference-integration.patch +``` + +The patch creates an addendum readme, `patch_readme.md`, with further comments. + +Nothing more is needed beyond the patch step, as wolfBoot will handle building +the xmss build artifacts it requires. + +### XMSS Config +A new XMSS sim example has been added here: +``` +config/examples/sim-xmss.config +``` + +The `XMSS_PARAMS`, `IMAGE_SIGNATURE_SIZE`, and (optionally) `IMAGE_HEADER_SIZE` +must be set: + +``` +SIGN?=XMSS +... +XMSS_PARAMS='XMSS-SHA2_10_256' +... +IMAGE_SIGNATURE_SIZE=2500 +IMAGE_HEADER_SIZE?=5000 +``` + +The `XMSS_PARAMS` may be any SHA256 parameter set string from Tables 10 and 11 +from NIST SP 800-208. Use the helper script `tools/xmss/xmss_siglen.sh` to +calculate your signature length given your XMSS/XMSS^MT parameter string, e.g.: +``` +$ ./tools/xmss/xmss_siglen.sh XMSS-SHA2_10_256 +parameter set: XMSS-SHA2_10_256 +signature length: 2500 +``` + +``` +$ ./tools/xmss/xmss_siglen.sh XMSSMT-SHA2_20/2_256 +parameter set: XMSSMT-SHA2_20/2_256 +signature length: 4963 +``` diff --git a/include/loader.h b/include/loader.h index 761f1144..5c6ef0c8 100644 --- a/include/loader.h +++ b/include/loader.h @@ -67,6 +67,9 @@ extern "C" { * options.mk from the .config file. */ extern const unsigned char lms_pub_key[]; extern unsigned int lms_pub_key_len; +#elif defined(WOLFBOOT_SIGN_XMSS) + extern const unsigned char xmss_pub_key[]; + extern unsigned int xmss_pub_key_len; #elif !defined(WOLFBOOT_NO_SIGN) # error "No public key available for given signing algorithm." #endif /* Algorithm selection */ diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index dfc2e751..b46bcd4d 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -85,6 +85,7 @@ extern "C" { #define AUTH_KEY_ECC521 0x07 #define AUTH_KEY_RSA3072 0x08 #define AUTH_KEY_LMS 0x09 +#define AUTH_KEY_XMSS 0x10 @@ -105,6 +106,7 @@ extern "C" { #define HDR_IMG_TYPE_AUTH_ECC521 (AUTH_KEY_ECC521 << 8) #define HDR_IMG_TYPE_AUTH_RSA3072 (AUTH_KEY_RSA3072 << 8) #define HDR_IMG_TYPE_AUTH_LMS (AUTH_KEY_LMS << 8) +#define HDR_IMG_TYPE_AUTH_XMSS (AUTH_KEY_XMSS << 8) #define HDR_IMG_TYPE_DIFF 0x00D0 @@ -122,6 +124,7 @@ extern "C" { #define KEYSTORE_PUBKEY_SIZE_RSA3072 448 #define KEYSTORE_PUBKEY_SIZE_RSA4096 576 #define KEYSTORE_PUBKEY_SIZE_LMS 60 + #define KEYSTORE_PUBKEY_SIZE_XMSS 68 /* Mask for key permissions */ #define KEY_VERIFY_ALL (0xFFFFFFFFU) @@ -222,6 +225,11 @@ extern "C" { # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_LMS # endif + #elif defined(WOLFBOOT_SIGN_XMSS) + # define HDR_IMG_TYPE_AUTH HDR_IMG_TYPE_AUTH_XMSS + # ifndef WOLFBOOT_UNIVERSAL_KEYSTORE + # define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_XMSS + # endif #else # error "No valid authentication mechanism selected. " \ "Please select a valid SIGN= option." diff --git a/options.mk b/options.mk index b9cd5709..f7aaea0c 100644 --- a/options.mk +++ b/options.mk @@ -381,6 +381,46 @@ ifeq ($(SIGN),LMS) endif endif +ifeq ($(SIGN),XMSS) + ifndef XMSS_PARAMS + $(error XMSS_PARAMS not set) + endif + + ifndef IMAGE_SIGNATURE_SIZE + $(error IMAGE_SIGNATURE_SIZE not set) + endif + + ifndef IMAGE_HEADER_SIZE + $(error IMAGE_HEADER_SIZE not set) + endif + + XMSSDIR = lib/xmss + KEYGEN_OPTIONS+=--xmss + SIGN_OPTIONS+=--xmss + WOLFCRYPT_OBJS+= \ + ./$(XMSSDIR)/params.o \ + ./$(XMSSDIR)/thash.o \ + ./$(XMSSDIR)/hash_address.o \ + ./$(XMSSDIR)/wots.o \ + ./$(XMSSDIR)/xmss.o \ + ./$(XMSSDIR)/xmss_core_fast.o \ + ./$(XMSSDIR)/xmss_commons.o \ + ./$(XMSSDIR)/utils.o \ + ./lib/wolfssl/wolfcrypt/src/ext_xmss.o \ + ./lib/wolfssl/wolfcrypt/src/memory.o \ + ./lib/wolfssl/wolfcrypt/src/wc_port.o \ + ./lib/wolfssl/wolfcrypt/src/hash.o + CFLAGS+=-D"WOLFBOOT_SIGN_XMSS" -D"WOLFSSL_HAVE_XMSS" -D"HAVE_LIBXMSS" \ + -DXMSS_PARAMS=\"$(XMSS_PARAMS)\" -I$(XMSSDIR) \ + -D"IMAGE_SIGNATURE_SIZE"=$(IMAGE_SIGNATURE_SIZE) \ + -D"WOLFSSL_XMSS_VERIFY_ONLY" -D"XMSS_VERIFY_ONLY" + ifeq ($(WOLFBOOT_SMALL_STACK),1) + $(error WOLFBOOT_SMALL_STACK with XMSS not supported) + else + STACK_USAGE=18064 + endif +endif + ifeq ($(RAM_CODE),1) CFLAGS+= -D"RAM_CODE" endif diff --git a/src/image.c b/src/image.c index a6fb051d..c272c74c 100644 --- a/src/image.c +++ b/src/image.c @@ -382,6 +382,71 @@ static void wolfBoot_verify_signature(uint8_t key_slot, } #endif /* WOLFBOOT_SIGN_LMS */ +#ifdef WOLFBOOT_SIGN_XMSS +#include +#ifdef HAVE_LIBXMSS + #include +#endif + +static void wolfBoot_verify_signature(uint8_t key_slot, + struct wolfBoot_image *img, uint8_t *sig) +{ + int ret = 0; + XmssKey xmss; + word32 pub_len = 0; + uint8_t * pubkey = NULL; + + wolfBoot_printf("info: XMSS wolfBoot_verify_signature\n"); + + pubkey = keystore_get_buffer(key_slot); + if (pubkey == NULL) { + wolfBoot_printf("error: Xmss pubkey not found\n"); + return; + } + + ret = wc_XmssKey_Init(&xmss, NULL, INVALID_DEVID); + if (ret != 0) { + wolfBoot_printf("error: wc_XmssKey_Init returned %d\n", ret); + return; + } + + wolfBoot_printf("info: using XMSS parameters: %s\n", XMSS_PARAMS); + + /* Set the XMSS parameters. */ + ret = wc_XmssKey_SetParamStr(&xmss, XMSS_PARAMS); + if (ret != 0) { + /* Something is wrong with the pub key or XMSS parameters. */ + wolfBoot_printf("error: wc_XmssKey_SetParamStr(%s)" \ + " returned %d\n", XMSS_PARAMS, ret); + return; + } + + wolfBoot_printf("info: using XMSS parameters: %s\n", XMSS_PARAMS); + + /* Set the public key. */ + ret = wc_XmssKey_ImportPubRaw(&xmss, pubkey, KEYSTORE_PUBKEY_SIZE); + if (ret != 0) { + /* Something is wrong with the pub key or LMS parameters. */ + wolfBoot_printf("error: wc_XmssKey_ImportPubRaw" \ + " returned %d\n", ret); + return; + } + + ret = wc_XmssKey_Verify(&xmss, sig, IMAGE_SIGNATURE_SIZE, img->sha_hash, + WOLFBOOT_SHA_DIGEST_SIZE); + + if (ret == 0) { + wolfBoot_printf("info: wc_XmssKey_Verify returned OK\n"); + wolfBoot_image_confirm_signature_ok(img); + } + else { + wolfBoot_printf("error: wc_XmssKey_Verify returned %d\n", ret); + } + + wc_XmssKey_Free(&xmss); +} +#endif /* WOLFBOOT_SIGN_XMSS */ + #endif /* WOLFBOOT_TPM && WOLFBOOT_TPM_VERIFY */ diff --git a/tools/config.mk b/tools/config.mk index 34361e3f..f6757017 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -34,6 +34,7 @@ ifeq ($(ARCH),) LMS_LEVELS?=0 LMS_HEIGHT?=0 LMS_WINTERNITZ?=0 + XMSS_PARAMS?=XMSS-SHA2_10_256 NO_MPU?=0 ENCRYPT?=0 ENCRYPT_WITH_CHACHA?=0 @@ -93,4 +94,5 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO ENCRYPT_WITH_CHACHA ENCRYPT_WITH_AES128 ENCRYPT_WITH_AES256 ARMORED \ LMS_LEVELS LMS_HEIGHT LMS_WINTERNITZ \ WOLFBOOT_UNIVERSAL_KEYSTORE \ + XMSS_PARAMS \ ELF diff --git a/tools/keytools/Makefile b/tools/keytools/Makefile index 1a691bd4..669c188a 100644 --- a/tools/keytools/Makefile +++ b/tools/keytools/Makefile @@ -25,6 +25,15 @@ ifeq ($(SIGN),LMS) -D"LMS_WINTERNITZ=$(LMS_WINTERNITZ)" endif +ifeq ($(SIGN),XMSS) + $(info xmss params: $(XMSS_PARAMS)) + XMSSDIR = $(WOLFBOOTDIR)/lib/xmss + CFLAGS +=-DWOLFBOOT_SIGN_XMSS -DWOLFSSL_HAVE_XMSS -DHAVE_LIBXMSS -I$(XMSSDIR) \ + -D"IMAGE_SIGNATURE_SIZE"=$(IMAGE_SIGNATURE_SIZE) \ + -DXMSS_PARAMS=\"$(XMSS_PARAMS)\" +endif + + # option variables DEBUG_FLAGS = -g -DDEBUG -DDEBUG_SIGNTOOL -DDEBUG_WOLFSSL -DDEBUG_WOLFSSL_VERBOSE SANITIZE_FLAGS = -fsanitize=address @@ -75,16 +84,33 @@ OBJS_REAL=\ $(WOLFDIR)/wolfcrypt/src/tfm.o \ $(WOLFDIR)/wolfcrypt/src/wc_port.o \ $(WOLFDIR)/wolfcrypt/src/wolfmath.o \ - $(WOLFDIR)/wolfcrypt/src/ext_lms.o + $(WOLFDIR)/wolfcrypt/src/ext_lms.o \ + $(WOLFDIR)/wolfcrypt/src/ext_xmss.o OBJS_REAL+=\ $(WOLFBOOTDIR)/src/delta.o +ifeq ($(SIGN),XMSS) +OBJS_REAL+=\ + $(XMSSDIR)/params.o \ + $(XMSSDIR)/thash.o \ + $(XMSSDIR)/hash_address.o \ + $(XMSSDIR)/wots.o \ + $(XMSSDIR)/xmss.o \ + $(XMSSDIR)/xmss_core_fast.o \ + $(XMSSDIR)/xmss_commons.o \ + $(XMSSDIR)/utils.o +endif + OBJS_VIRT=$(addprefix $(OBJDIR), $(notdir $(OBJS_REAL))) vpath %.c $(WOLFDIR)/wolfcrypt/src/ vpath %.c $(WOLFBOOTDIR)/src/ vpath %.c ./ +ifeq ($(SIGN),XMSS) + vpath %.c $(XMSSDIR)/ +endif + .PHONY: clean all all: $(WOLFBOOTDIR)/include/target.h sign keygen @@ -114,6 +140,9 @@ $(OBJDIR)/%.o: $(WOLFBOOTDIR)/src/%.c $(OBJDIR)/%.o: $(WOLFDIR)/wolfcrypt/src/%.c $(Q)$(CC) $(CFLAGS) -c -o $@ $< +$(XMSSDIR)/src/%.o: $(XMSSDIR)/src/%.c + $(Q)$(CC) $(CFLAGS) -c -o $@ $< + # build templates sign: $(OBJS_VIRT) $(LIBS) sign.o @echo "Building signing tool" diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index c758e7c6..82f88091 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -68,6 +68,13 @@ #endif #endif +#if defined(WOLFSSL_HAVE_XMSS) + #include + #ifdef HAVE_LIBXMSS + #include + #endif +#endif + #include #include #ifdef DEBUG_SIGNTOOL @@ -91,6 +98,7 @@ #define KEYGEN_ECC521 7 #define KEYGEN_RSA3072 8 #define KEYGEN_LMS 9 +#define KEYGEN_XMSS 10 /* Globals */ static FILE *fpub, *fpub_image; @@ -236,7 +244,8 @@ const char KType[][17] = { "AUTH_KEY_ECC384", "AUTH_KEY_ECC521", "AUTH_KEY_RSA3072", - "AUTH_KEY_LMS" + "AUTH_KEY_LMS", + "AUTH_KEY_XMSS" }; const char KSize[][29] = { @@ -249,7 +258,8 @@ const char KSize[][29] = { "KEYSTORE_PUBKEY_SIZE_ECC384", "KEYSTORE_PUBKEY_SIZE_ECC521", "KEYSTORE_PUBKEY_SIZE_RSA3072", - "KEYSTORE_PUBKEY_SIZE_LMS" + "KEYSTORE_PUBKEY_SIZE_LMS", + "KEYSTORE_PUBKEY_SIZE_XMSS" }; const char KName[][8] = { @@ -262,7 +272,8 @@ const char KName[][8] = { "ECC384", "ECC521", "RSA3072", - "LMS" + "LMS", + "XMSS" }; #define MAX_PUBKEYS 64 @@ -306,6 +317,9 @@ static uint32_t get_pubkey_size(uint32_t keyType) case KEYGEN_LMS: size = KEYSTORE_PUBKEY_SIZE_LMS; break; + case KEYGEN_XMSS: + size = KEYSTORE_PUBKEY_SIZE_XMSS; + break; default: size = 0; } @@ -595,6 +609,97 @@ static void keygen_lms(const char *priv_fname, uint32_t id_mask) } #endif /* if defined(WOLFSSL_HAVE_LMS) */ +#if defined(WOLFSSL_HAVE_XMSS) +#include "../xmss/xmss_common.h" + +static void keygen_xmss(const char *priv_fname, uint32_t id_mask) +{ + FILE * fpriv; + XmssKey key; + int ret; + word32 priv_sz = 0; + byte xmss_pub[XMSS_SHA256_PUBLEN]; + word32 pub_len = sizeof(xmss_pub); + + ret = wc_XmssKey_Init(&key, NULL, INVALID_DEVID); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_Init returned %d\n", ret); + exit(1); + } + + ret = wc_XmssKey_SetParamStr(&key, XMSS_PARAMS); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetParamStr(%s)" \ + " returned %d\n", XMSS_PARAMS, ret); + exit(1); + } + + printf("info: using XMSS parameters: %s\n", XMSS_PARAMS); + + ret = wc_XmssKey_SetWriteCb(&key, xmss_write_key); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetWriteCb returned %d\n", ret); + exit(1); + } + + ret = wc_XmssKey_SetReadCb(&key, xmss_read_key); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetReadCb returned %d\n", ret); + exit(1); + } + + ret = wc_XmssKey_SetContext(&key, (void *) priv_fname); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetContext returned %d\n", ret); + exit(1); + } + + /* Make the key pair. */ + ret = wc_XmssKey_MakeKey(&key, &rng); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_MakeKey returned %d\n", ret); + exit(1); + } + + /* Get the XMSS/XMSS^MT secret key length. */ + ret = wc_XmssKey_GetPrivLen(&key, &priv_sz); + if (ret != 0 || priv_sz <= 0) { + printf("error: wc_XmssKey_GetPrivLen returned %d\n", + ret); + exit(1); + } + + ret = wc_XmssKey_ExportPubRaw(&key, xmss_pub, &pub_len); + if (ret != 0) { + fprintf(stderr, "error: wc_XmssKey_ExportPubRaw returned %d\n", ret); + exit(1); + } + + if (pub_len != sizeof(xmss_pub)) { + fprintf(stderr, "error: wc_XmssKey_ExportPubRaw returned pub_len=%d\n" \ + ", expected %zu\n", pub_len, sizeof(xmss_pub)); + exit(1); + } + + /* Append the public key to the private keyfile. */ + fpriv = fopen(priv_fname, "r+"); + if (!fpriv) { + fprintf(stderr, "error: fopen(%s, \"r+\") returned %d\n", priv_fname, + ret); + exit(1); + } + + fseek(fpriv, priv_sz, SEEK_SET); + fwrite(xmss_pub, KEYSTORE_PUBKEY_SIZE_XMSS, 1, fpriv); + fclose(fpriv); + + keystore_add(KEYGEN_XMSS, xmss_pub, KEYSTORE_PUBKEY_SIZE_XMSS, priv_fname, id_mask); + + wc_XmssKey_Free(&key); +} +#endif /* if defined(WOLFSSL_HAVE_XMSS) */ + + static void key_gen_check(const char *kfilename) { FILE *f; @@ -663,6 +768,12 @@ static void key_generate(uint32_t ktype, const char *kfilename, uint32_t id_mask keygen_lms(kfilename, id_mask); break; #endif + +#ifdef WOLFSSL_HAVE_XMSS + case KEYGEN_XMSS: + keygen_xmss(kfilename, id_mask); + break; +#endif } /* end switch */ } @@ -815,6 +926,11 @@ int main(int argc, char** argv) else if (strcmp(argv[i], "--lms") == 0) { keytype = KEYGEN_LMS; } +#endif +#if defined(WOLFSSL_HAVE_XMSS) + else if (strcmp(argv[i], "--xmss") == 0) { + keytype = KEYGEN_XMSS; + } #endif else if (strcmp(argv[i], "--force") == 0) { force = 1; diff --git a/tools/keytools/sign.c b/tools/keytools/sign.c index f7ec12f5..1cb00b65 100644 --- a/tools/keytools/sign.c +++ b/tools/keytools/sign.c @@ -108,6 +108,13 @@ static inline int fp_truncate(FILE *f, size_t len) #endif #endif +#if defined(WOLFSSL_HAVE_XMSS) + #include + #ifdef HAVE_LIBXMSS + #include + #endif +#endif + #ifdef DEBUG_SIGNTOOL #include #endif @@ -169,6 +176,7 @@ static inline int fp_truncate(FILE *f, size_t len) #define SIGN_ECC384 HDR_IMG_TYPE_AUTH_ECC384 #define SIGN_ECC521 HDR_IMG_TYPE_AUTH_ECC521 #define SIGN_LMS HDR_IMG_TYPE_AUTH_LMS +#define SIGN_XMSS HDR_IMG_TYPE_AUTH_XMSS #define ENC_OFF 0 @@ -203,6 +211,10 @@ static void header_append_tag(uint8_t* header, uint32_t* idx, uint16_t tag, #include "../lms/lms_common.h" #endif +#ifdef WOLFSSL_HAVE_XMSS +#include "../xmss/xmss_common.h" +#endif + /* Globals */ static const char wolfboot_delta_file[] = "/tmp/wolfboot-delta.bin"; @@ -222,6 +234,9 @@ static union { #ifdef WOLFSSL_HAVE_LMS LmsKey lms; #endif +#ifdef WOLFSSL_HAVE_XMSS + XmssKey xmss; +#endif } key; struct cmd_options { @@ -736,6 +751,57 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz, } } #endif /* WOLFSSL_HAVE_LMS */ + +#ifdef WOLFSSL_HAVE_XMSS + FALL_THROUGH; + case SIGN_XMSS: + ret = -1; + + if (CMD.sign == SIGN_AUTO) { + /* XMSS is stateful and requires additional config, and is not + * compatible with SIGN_AUTO. */ + printf("error: SIGN_AUTO with XMSS is not supported\n"); + } + else { + /* The XMSS file callbacks will handle writing and reading the + * private key. We only need to set the public key here. + * + * If both priv/pub are present: + * - The first ?? bytes is the private key. + * - The next 60 bytes is the public key. */ + word32 priv_sz = 0; + int xmss_ret = 0; + + xmss_ret = wc_XmssKey_GetPrivLen(&key.xmss, &priv_sz); + if (xmss_ret != 0 || priv_sz <= 0) { + printf("error: wc_XmssKey_GetPrivLen returned %d\n", + xmss_ret); + break; + } + + printf("info: xmss sk len: %d\n", priv_sz); + printf("info: xmss pk len: %d\n", KEYSTORE_PUBKEY_SIZE_XMSS); + + if (*key_buffer_sz == (priv_sz + KEYSTORE_PUBKEY_SIZE_XMSS)) { + /* priv + pub */ + *pubkey = (*key_buffer) + priv_sz; + *pubkey_sz = (*key_buffer_sz) - priv_sz; + ret = 0; + } + else if (*key_buffer_sz == KEYSTORE_PUBKEY_SIZE_XMSS) { + /* pub only. */ + *pubkey = (*key_buffer); + *pubkey_sz = KEYSTORE_PUBKEY_SIZE_XMSS; + ret = 0; + } + else { + /* We don't recognize this as an XMSS pub or private key. */ + printf("error: unrecognized XMSS key size: %d\n", + *key_buffer_sz ); + } + } +#endif /* WOLFSSL_HAVE_XMSS */ + break; } /* end switch (CMD.sign) */ @@ -868,6 +934,35 @@ static int sign_digest(int sign, int hash_algo, } else #endif /* WOLFSSL_HAVE_LMS */ +#ifdef WOLFSSL_HAVE_XMSS + if (sign == SIGN_XMSS) { + ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID); + /* Set the callbacks, so XMSS can update the private key while signing */ + if (ret == 0) { + ret = wc_XmssKey_SetWriteCb(&key.xmss, xmss_write_key); + } + if (ret == 0) { + ret = wc_XmssKey_SetReadCb(&key.xmss, xmss_read_key); + } + if (ret == 0) { + ret = wc_XmssKey_SetContext(&key.xmss, (void*)CMD.key_file); + } + if (ret == 0) { + ret = wc_XmssKey_SetParamStr(&key.xmss, XMSS_PARAMS); + } + if (ret == 0) { + ret = wc_XmssKey_Reload(&key.xmss); + } + if (ret == 0) { + ret = wc_XmssKey_Sign(&key.xmss, signature, signature_sz, digest, + digest_sz); + } + if (ret != 0) { + fprintf(stderr, "error signing with XMSS: %d\n", ret); + } + } + else +#endif /* WOLFSSL_HAVE_XMSS */ { ret = NOT_COMPILED_IN; } @@ -1769,6 +1864,12 @@ int main(int argc, char** argv) CMD.sign = SIGN_LMS; sign_str = "LMS"; } +#endif +#ifdef WOLFSSL_HAVE_XMSS + else if (strcmp(argv[i], "--xmss") == 0) { + CMD.sign = SIGN_XMSS; + sign_str = "XMSS"; + } #endif else if (strcmp(argv[i], "--sha256") == 0) { CMD.hash_algo = HASH_SHA256; @@ -1980,6 +2081,39 @@ int main(int argc, char** argv) CMD.signature_sz = sig_sz; } #endif /* WOLFSSL_HAVE_LMS */ +#ifdef WOLFSSL_HAVE_XMSS + else if (CMD.sign == SIGN_XMSS) { + int xmss_ret = 0; + word32 sig_sz = 0; + + xmss_ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID); + if (xmss_ret != 0) { + fprintf(stderr, "error: wc_XmssKey_Init returned %d\n", xmss_ret); + exit(1); + } + + xmss_ret = wc_XmssKey_SetParamStr(&key.xmss, XMSS_PARAMS); + if (xmss_ret != 0) { + fprintf(stderr, "error: wc_XmssKey_SetParamStr(%s)" \ + " returned %d\n", XMSS_PARAMS, ret); + exit(1); + } + + printf("info: using XMSS parameters: %s\n", XMSS_PARAMS); + + xmss_ret = wc_XmssKey_GetSigLen(&key.xmss, &sig_sz); + if (xmss_ret != 0) { + fprintf(stderr, "error: wc_XmssKey_GetSigLen returned %d\n", + xmss_ret); + exit(1); + } + + printf("info: XMSS signature size: %d\n", sig_sz); + + CMD.header_sz = 2 * sig_sz; + CMD.signature_sz = sig_sz; + } +#endif /* WOLFSSL_HAVE_XMSS */ if (((CMD.sign != NO_SIGN) && (CMD.signature_sz == 0)) || CMD.header_sz == 0) { diff --git a/tools/test.mk b/tools/test.mk index e42253e8..277026db 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -53,6 +53,9 @@ endif ifeq ($(SIGN),LMS) SIGN_ARGS+= --lms endif +ifeq ($(SIGN),XMSS) + SIGN_ARGS+= --xmss +endif # Make sign hash argument ifeq ($(HASH),SHA256) diff --git a/tools/xmss/0001-Patch-to-support-wolfSSL-xmss-reference-integration.patch b/tools/xmss/0001-Patch-to-support-wolfSSL-xmss-reference-integration.patch new file mode 100644 index 00000000..054d62f1 --- /dev/null +++ b/tools/xmss/0001-Patch-to-support-wolfSSL-xmss-reference-integration.patch @@ -0,0 +1,2653 @@ +From 99503968a914ed74693eb0e10199fe3cfa83d652 Mon Sep 17 00:00:00 2001 +From: jordan +Date: Fri, 13 Oct 2023 13:29:46 -0500 +Subject: [PATCH 1/1] Patch to support wolfSSL xmss-reference integration. + +--- + Makefile | 29 +++- + fips202.c | 2 +- + hash_address.c | 2 + + params.h | 15 ++ + patch_readme.md | 42 ++++++ + randombytes.c | 2 + + test/oid.c | 2 +- + test/speed.c | 103 ++++++++++++-- + test/vectors.c | 4 +- + test/wots.c | 79 ++++++++++- + test/xmss.c | 141 ++++++++++++++----- + test/xmss_determinism.c | 97 +++++++++++-- + test/xmss_max_signatures.c | 102 ++++++++++++-- + hash.c => thash.c | 72 ++++++---- + hash.h => thash.h | 5 +- + ui/keypair.c | 73 ++++++++++ + wots.c | 33 ++++- + xmss.c | 49 +++---- + xmss.h | 48 +++---- + xmss_callbacks.h | 12 ++ + xmss_commons.c | 77 +++++++---- + xmss_commons.h | 4 +- + xmss_core.c | 135 +++++++++++++----- + xmss_core.h | 22 +-- + xmss_core_fast.c | 276 ++++++++++++++++++++++++------------- + 25 files changed, 1099 insertions(+), 327 deletions(-) + create mode 100644 patch_readme.md + rename hash.c => thash.c (82%) + rename hash.h => thash.h (96%) + create mode 100644 xmss_callbacks.h + +diff --git a/Makefile b/Makefile +index d1b95d5..9f72e6d 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,9 +1,13 @@ + CC = /usr/bin/gcc +-CFLAGS = -Wall -g -O3 -Wextra -Wpedantic +-LDLIBS = -lcrypto ++CFLAGS = -Wall -O2 -Wextra -Wpedantic ++VERIFY_CFLAGS = -DXMSS_VERIFY_ONLY -Wall -O2 -Wextra -Wpedantic ++LDLIBS = -lwolfssl ++# Alternate build settings to check for memory issues. ++#CC = clang ++#CFLAGS = -g -fsanitize=memory -Wall -Wextra -Wpedantic + +-SOURCES = params.c hash.c fips202.c hash_address.c randombytes.c wots.c xmss.c xmss_core.c xmss_commons.c utils.c +-HEADERS = params.h hash.h fips202.h hash_address.h randombytes.h wots.h xmss.h xmss_core.h xmss_commons.h utils.h ++SOURCES = params.c thash.c hash_address.c randombytes.c wots.c xmss.c xmss_core.c xmss_commons.c utils.c ++HEADERS = params.h thash.h hash_address.h randombytes.h wots.h xmss.h xmss_core.h xmss_commons.h utils.h + + SOURCES_FAST = $(subst xmss_core.c,xmss_core_fast.c,$(SOURCES)) + HEADERS_FAST = $(subst xmss_core.c,xmss_core_fast.c,$(HEADERS)) +@@ -83,7 +87,24 @@ ui/xmss_%: ui/%.c $(SOURCES) $(OBJS) $(HEADERS) + ui/xmssmt_%: ui/%.c $(SOURCES) $(OBJS) $(HEADERS) + $(CC) -DXMSSMT $(CFLAGS) -o $@ $(SOURCES) $< $(LDLIBS) + ++# Only the test xmss executables link with wolfssl. ++# xmss_lib.a does not link with wolfssl, as this would create circular ++# dependencies. ++xmss_lib.a: params.o thash.o hash_address.o wots.o xmss.o xmss_core_fast.o \ ++ xmss_commons.o utils.o ++ $(AR) rcs $@ $^ ++ ++xmss_verify_lib.a: CFLAGS += -DXMSS_VERIFY_ONLY ++ ++xmss_verify_lib.a: params.o thash.o hash_address.o wots.o xmss.o xmss_core_fast.o \ ++ xmss_commons.o utils.o ++ $(AR) rcs $@ $^ ++ + clean: + -$(RM) $(TESTS) + -$(RM) test/vectors + -$(RM) $(UI) ++ -$(RM) *.o ++ -$(RM) *.lo ++ -$(RM) xmss_lib.a ++ -$(RM) xmss_verify_lib.a +diff --git a/fips202.c b/fips202.c +index 5e36d66..4565782 100644 +--- a/fips202.c ++++ b/fips202.c +@@ -62,7 +62,7 @@ static const uint64_t KeccakF_RoundConstants[NROUNDS] = + (uint64_t)0x8000000080008008ULL + }; + +-void KeccakF1600_StatePermute(uint64_t * state) ++static void KeccakF1600_StatePermute(uint64_t * state) + { + int round; + +diff --git a/hash_address.c b/hash_address.c +index 3a02473..5396796 100644 +--- a/hash_address.c ++++ b/hash_address.c +@@ -1,5 +1,7 @@ + #include + ++#include "hash_address.h" ++ + void set_layer_addr(uint32_t addr[8], uint32_t layer) + { + addr[0] = layer; +diff --git a/params.h b/params.h +index 58c0619..c55ebaf 100644 +--- a/params.h ++++ b/params.h +@@ -3,6 +3,21 @@ + + #include + ++/* Compile-time constants, assuming SHA256 parameter sets only. ++ * ++ * See params.c for how WOTS_SIG_BYTES and releated were calculated. ++ * ++ * These are to facilitate integration with embedded targets ++ * where variable-length arrays cannot be used (e.g. wolfBoot). ++ * */ ++#define XMSS_SHA256_N 32 ++#define XMSS_SHA256_PADDING_LEN 32 ++#define XMSS_SHA256_MSG_PREFIX_LEN (3 * XMSS_SHA256_N + XMSS_SHA256_PADDING_LEN) ++#define XMSS_SHA256_MAX_MSG_LEN 128 ++#define XMSS_SHA256_MAX_MSG_HASH_LEN (XMSS_SHA256_MSG_PREFIX_LEN + XMSS_SHA256_MAX_MSG_LEN) ++#define XMSS_SHA256_WOTS_SIG_BYTES 2144 ++#define XMSS_SHA256_WOTS_LEN 67 ++ + /* These are merely internal identifiers for the supported hash functions. */ + #define XMSS_SHA2 0 + #define XMSS_SHAKE128 1 +diff --git a/patch_readme.md b/patch_readme.md +new file mode 100644 +index 0000000..a870353 +--- /dev/null ++++ b/patch_readme.md +@@ -0,0 +1,42 @@ ++# Addendum readme for the wolfssl xmss-reference integration patch ++ ++This patch contains a number of changes that were primarily motivated to ++facilitate wolfBoot XMSS support for embedded-targets. Specifically, the ++following changes have been made: ++ ++- All variable-length arrays (VLAs) necessary have been guarded with the define ++ `WOLFBOOT_SIGN_XMSS`, and replaced with static compile-time constant arrays, ++ with constants such as `XMSS_SHA256_WOTS_LEN` that have been defined in `params.h`. ++ They assume that the choice of SHA256 as the hashing function is a compile ++ time constant, and the function `core_hash()` has been updated to reflect this. ++ The param parsing logic in `params.c` has not been touched though, to allow for ++ in the future changing the underlying hash function at compile time if desired. ++- The signing and verifying APIs (`xmss_sign()`, `xmssmt_sign()`,`xmss_sign_open()`, ++ `xmssmt_sign_open()`) have been updated so that the message and signature are ++ passed as separate args, rather than a concatenated array. Additionally, for ++ wolfBoot builds the message length has been restricted to the compile time ++ constant of `XMSS_SHA256_MAX_MSG_LEN`. The appropriate APIs have had their ++ comments updates, and include an additional comment explaining this: ++``` ++ * Note: in WOLFBOOT_SIGN_XMSS build, the max allowed message length (msglen) ++ * is XMSS_SHA256_MAX_MSG_LEN. This is to facilitate having a manageable small ++ * static array, rather than a variable length array, for the message hash. ++``` ++- SHA256 and RNG operations are handled by registered callback functions. See ++ the header `xmss_callbacks.h` for the callback setter functions. This allows ++ offloading of SHA and RNG operations to wolfCrypt. ++- The tree-hash sources `hash.c` and `hash.h` were renamed to `thash.[c,h]`, to ++ avoid potential confusion with similarly named targets in wolfBoot. ++- The `Makefile` has been updated to build static libraries `xmss_lib.a`, ++ and `xmss_verify_lib.a`. The `xmss_verify_lib.a` is a verify-only build, ++ with an additional define `XMSS_VERIFY_ONLY` that guards out keygen ++ and signing functions. ++- The `Makefile` has been updated to link against wolfSSL `LDLIBS = -lwolfssl` ++ for the building of the tests in `test/`. The linking is *not* done for ++ building the static libs though, to avoid a circular dependency. Linking with ++ wolfssl to build the static libs is not necessary because the RNG and SHA ++ operations are provided by setting callbacks. ++- The tests in `test/` have been updated to reflect these changes. ++- Some minor changes were made to fix warnings from clang `-fsanitize=memory`. ++- Some minor cosmetic changes were made (cleanup trailing space, wrap long lines, ++ etc). +diff --git a/randombytes.c b/randombytes.c +index bfc8d93..ba4d7ac 100644 +--- a/randombytes.c ++++ b/randombytes.c +@@ -5,6 +5,8 @@ This code was taken from the SPHINCS reference implementation and is public doma + #include + #include + ++#include "randombytes.h" ++ + static int fd = -1; + + void randombytes(unsigned char *x, unsigned long long xlen) +diff --git a/test/oid.c b/test/oid.c +index 6c51032..5daf5d1 100644 +--- a/test/oid.c ++++ b/test/oid.c +@@ -22,7 +22,7 @@ + return -1;\ + } + +-int main() ++int main(void) + { + uint32_t oid; + xmss_params params; +diff --git a/test/speed.c b/test/speed.c +index 6ae58b0..80758ec 100644 +--- a/test/speed.c ++++ b/test/speed.c +@@ -4,8 +4,13 @@ + + #include "../xmss.h" + #include "../params.h" ++#include "../xmss_callbacks.h" + #include "../randombytes.h" + ++#include ++#include ++#include ++ + #define XMSS_MLEN 32 + + #ifndef XMSS_SIGNATURES +@@ -34,6 +39,12 @@ + #endif + #endif + ++static int rng_cb(void * output, size_t length); ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++ ++static WC_RNG rng; ++ + static unsigned long long cpucycles(void) + { + unsigned long long result; +@@ -78,7 +89,7 @@ static void print_results(unsigned long long *t, size_t tlen) + printf("\n"); + } + +-int main() ++int main(void) + { + /* Make stdout buffer more responsive. */ + setbuf(stdout, NULL); +@@ -88,6 +99,24 @@ int main() + int ret = 0; + int i; + ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } ++ ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } ++ + // TODO test more different variants + if (XMSS_STR_TO_OID(&oid, XMSS_VARIANT)) { + #ifdef XMSSMT +@@ -101,18 +130,16 @@ int main() + + unsigned char pk[XMSS_OID_LEN + params.pk_bytes]; + unsigned char sk[XMSS_OID_LEN + params.sk_bytes]; +- unsigned char *m = malloc(XMSS_MLEN); +- unsigned char *sm = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned char *mout = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned long long smlen; +- unsigned long long mlen; ++ unsigned char *msg = malloc(XMSS_MLEN); ++ unsigned char *sig = malloc(params.sig_bytes); ++ unsigned long long siglen = params.sig_bytes; + + unsigned long long t0, t1; + unsigned long long *t = malloc(sizeof(unsigned long long) * XMSS_SIGNATURES); + struct timespec start, stop; + double result; + +- randombytes(m, XMSS_MLEN); ++ randombytes(msg, XMSS_MLEN); + + printf("Benchmarking variant %s\n", XMSS_VARIANT); + +@@ -130,15 +157,17 @@ int main() + + for (i = 0; i < XMSS_SIGNATURES; i++) { + t[i] = cpucycles(); +- XMSS_SIGN(sk, sm, &smlen, m, XMSS_MLEN); ++ XMSS_SIGN(sk, sig, &siglen, msg, XMSS_MLEN); + } + print_results(t, XMSS_SIGNATURES); + + printf("Verifying %d signatures..\n", XMSS_SIGNATURES); + + for (i = 0; i < XMSS_SIGNATURES; i++) { ++ unsigned long long msglen = XMSS_MLEN; ++ + t[i] = cpucycles(); +- ret |= XMSS_SIGN_OPEN(mout, &mlen, sm, smlen, pk); ++ ret |= XMSS_SIGN_OPEN(msg, &msglen, sig, siglen, pk); + } + print_results(t, XMSS_SIGNATURES); + +@@ -150,10 +179,58 @@ int main() + printf("Public key size: %d (%.2f KiB)\n", params.pk_bytes, params.pk_bytes / 1024.0); + printf("Secret key size: %llu (%.2f KiB)\n", params.sk_bytes, params.sk_bytes / 1024.0); + +- free(m); +- free(sm); +- free(mout); ++ wc_FreeRng(&rng); ++ ++ free(msg); ++ free(sig); + free(t); + + return ret; +-} +\ No newline at end of file ++} ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} +diff --git a/test/vectors.c b/test/vectors.c +index f21ed6a..f5f5547 100644 +--- a/test/vectors.c ++++ b/test/vectors.c +@@ -1,4 +1,4 @@ +-/* ++/* + * Generate intermediate test vectors useful to test implementations. + */ + +@@ -111,7 +111,7 @@ void vectors_wots(uint32_t oid) { + printf("\n"); + } + +-int main() { ++int main(void) { + for (uint32_t oid = 1; oid <= 0x15; oid += 3) { + vectors_wots(oid); + } +diff --git a/test/wots.c b/test/wots.c +index 8b2b6de..fdaf369 100644 +--- a/test/wots.c ++++ b/test/wots.c +@@ -3,14 +3,26 @@ + #include + + #include "../wots.h" ++#include "../xmss_callbacks.h" + #include "../randombytes.h" + #include "../params.h" + +-int main() ++#include ++#include ++#include ++ ++static int rng_cb(void * output, size_t length); ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++ ++static WC_RNG rng; ++ ++int main(void) + { + xmss_params params; + // TODO test more different OIDs + uint32_t oid = 0x00000001; ++ int ret = -1; + + /* For WOTS it doesn't matter if we use XMSS or XMSSMT. */ + xmss_parse_oid(¶ms, oid); +@@ -23,6 +35,24 @@ int main() + unsigned char m[params.n]; + uint32_t addr[8] = {0}; + ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } ++ ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } ++ + randombytes(seed, params.n); + randombytes(pub_seed, params.n); + randombytes(m, params.n); +@@ -41,3 +71,50 @@ int main() + printf("successful.\n"); + return 0; + } ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} +diff --git a/test/xmss.c b/test/xmss.c +index 3703759..a71a46a 100644 +--- a/test/xmss.c ++++ b/test/xmss.c +@@ -4,9 +4,14 @@ + #include + + #include "../xmss.h" ++#include "../xmss_callbacks.h" + #include "../params.h" + #include "../randombytes.h" + ++#include ++#include ++#include ++ + #define XMSS_MLEN 32 + + #ifndef XMSS_SIGNATURES +@@ -29,7 +34,13 @@ + #define XMSS_VARIANT "XMSS-SHA2_10_256" + #endif + +-int main() ++static int rng_cb(void * output, size_t length); ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++ ++static WC_RNG rng; ++ ++int main(void) + { + xmss_params params; + uint32_t oid; +@@ -42,34 +53,55 @@ int main() + + unsigned char pk[XMSS_OID_LEN + params.pk_bytes]; + unsigned char sk[XMSS_OID_LEN + params.sk_bytes]; +- unsigned char *m = malloc(XMSS_MLEN); +- unsigned char *sm = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned char *mout = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned long long smlen; +- unsigned long long mlen; ++ unsigned char *msg = malloc(XMSS_MLEN); ++ unsigned char *sig = malloc(params.sig_bytes); ++ ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } + +- randombytes(m, XMSS_MLEN); ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } ++ ++ randombytes(msg, XMSS_MLEN); + + XMSS_KEYPAIR(pk, sk, oid); + + printf("Testing %d %s signatures.. \n", XMSS_SIGNATURES, XMSS_VARIANT); + + for (i = 0; i < XMSS_SIGNATURES; i++) { ++ unsigned long long siglen = params.sig_bytes; ++ unsigned long long msglen = XMSS_MLEN; ++ + printf(" - iteration #%d:\n", i); + +- XMSS_SIGN(sk, sm, &smlen, m, XMSS_MLEN); ++ if (XMSS_SIGN(sk, sig, &siglen, msg, XMSS_MLEN)) { ++ printf(" sign failed!\n"); ++ ret = -1; ++ } + +- if (smlen != params.sig_bytes + XMSS_MLEN) { +- printf(" X smlen incorrect [%llu != %u]!\n", +- smlen, params.sig_bytes); ++ if (siglen != params.sig_bytes) { ++ printf(" X siglen incorrect [%llu != %u]!\n", ++ siglen, params.sig_bytes); + ret = -1; + } + else { +- printf(" smlen as expected [%llu].\n", smlen); ++ printf(" siglen as expected [%llu].\n", siglen); + } + + /* Test if signature is valid. */ +- if (XMSS_SIGN_OPEN(mout, &mlen, sm, smlen, pk)) { ++ if (XMSS_SIGN_OPEN(msg, &msglen, sig, siglen, pk)) { + printf(" X verification failed!\n"); + ret = -1; + } +@@ -78,57 +110,96 @@ int main() + } + + /* Test if the correct message was recovered. */ +- if (mlen != XMSS_MLEN) { +- printf(" X mlen incorrect [%llu != %u]!\n", mlen, XMSS_MLEN); ++ if (msglen != XMSS_MLEN) { ++ printf(" X msglen incorrect [%llu != %u]!\n", msglen, XMSS_MLEN); + ret = -1; + } + else { +- printf(" mlen as expected [%llu].\n", mlen); +- } +- if (memcmp(m, mout, XMSS_MLEN)) { +- printf(" X output message incorrect!\n"); +- ret = -1; +- } +- else { +- printf(" output message as expected.\n"); ++ printf(" msglen as expected [%llu].\n", msglen); + } + + /* Test if flipping bits invalidates the signature (it should). */ + + /* Flip the first bit of the message. Should invalidate. */ +- sm[smlen - 1] ^= 1; +- if (!XMSS_SIGN_OPEN(mout, &mlen, sm, smlen, pk)) { ++ msg[0] ^= 1; ++ if (!XMSS_SIGN_OPEN(msg, &msglen, sig, siglen, pk)) { + printf(" X flipping a bit of m DID NOT invalidate signature!\n"); + ret = -1; + } + else { + printf(" flipping a bit of m invalidates signature.\n"); + } +- sm[smlen - 1] ^= 1; ++ msg[0] ^= 1; + + #ifdef XMSS_TEST_INVALIDSIG + int j; + /* Flip one bit per hash; the signature is almost entirely hashes. + This also flips a bit in the index, which is also a useful test. */ +- for (j = 0; j < (int)(smlen - XMSS_MLEN); j += params.n) { +- sm[j] ^= 1; +- if (!XMSS_SIGN_OPEN(mout, &mlen, sm, smlen, pk)) { ++ for (j = 0; j < (int)(siglen); j += params.n) { ++ sig[j] ^= 1; ++ if (!XMSS_SIGN_OPEN(msg, &msglen, sig, siglen, pk)) { + printf(" X flipping bit %d DID NOT invalidate sig + m!\n", j); +- sm[j] ^= 1; ++ sig[j] ^= 1; + ret = -1; + break; + } +- sm[j] ^= 1; ++ sig[j] ^= 1; + } +- if (j >= (int)(smlen - XMSS_MLEN)) { ++ if (j >= (int)(siglen)) { + printf(" changing any signature hash invalidates signature.\n"); + } + #endif + } + +- free(m); +- free(sm); +- free(mout); ++ free(msg); ++ free(sig); + + return ret; + } ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} +diff --git a/test/xmss_determinism.c b/test/xmss_determinism.c +index 49629f8..1984f1e 100644 +--- a/test/xmss_determinism.c ++++ b/test/xmss_determinism.c +@@ -3,17 +3,47 @@ + #include + + #include "../params.h" ++#include "../xmss_callbacks.h" + #include "../xmss.h" + #include "../randombytes.h" + ++#include ++#include ++#include ++ + #define MLEN 32 + +-int main() ++static int rng_cb(void * output, size_t length); ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++ ++static WC_RNG rng; ++ ++int main(void) + { + xmss_params params; + char *oidstr = "XMSS-SHA2_10_256"; + uint32_t oid; + unsigned int i; ++ int ret = -1; ++ ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } ++ ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } + + fprintf(stderr, "Testing if XMSS-SHA2_10_256 signing is deterministic.. "); + +@@ -24,10 +54,10 @@ int main() + unsigned char sk[XMSS_OID_LEN + params.sk_bytes]; + unsigned char sk2[XMSS_OID_LEN + params.sk_bytes]; + +- unsigned char m[MLEN]; +- unsigned char sm[params.sig_bytes + MLEN]; +- unsigned char sm2[params.sig_bytes + MLEN]; +- unsigned long long smlen; ++ unsigned char msg[MLEN]; ++ unsigned char sig[params.sig_bytes]; ++ unsigned char sig2[params.sig_bytes]; ++ unsigned long long siglen = params.sig_bytes; + + xmss_keypair(pk, sk, oid); + +@@ -35,16 +65,16 @@ int main() + memcpy(sk2, sk, XMSS_OID_LEN + params.sk_bytes); + + /* Sign a random message (but twice the same one). */ +- randombytes(m, MLEN); ++ randombytes(msg, MLEN); + +- xmss_sign(sk, sm, &smlen, m, MLEN); +- xmss_sign(sk2, sm2, &smlen, m, MLEN); ++ xmss_sign(sk, sig, &siglen, msg, MLEN); ++ xmss_sign(sk2, sig2, &siglen, msg, MLEN); + + /* Compare signature, and, if applicable, print the differences. */ +- if (memcmp(sm, sm2, params.sig_bytes + MLEN)) { ++ if (memcmp(sig, sig2, params.sig_bytes)) { + fprintf(stderr, "signatures differ!\n"); + for (i = 0; i < params.sig_bytes + MLEN; i++) { +- fprintf(stderr, (sm[i] != sm2[i] ? "x" : ".")); ++ fprintf(stderr, (sig[i] != sig2[i] ? "x" : ".")); + } + fprintf(stderr, "\n"); + return -1; +@@ -55,3 +85,50 @@ int main() + + return 0; + } ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} +diff --git a/test/xmss_max_signatures.c b/test/xmss_max_signatures.c +index d4a45a4..be145b5 100644 +--- a/test/xmss_max_signatures.c ++++ b/test/xmss_max_signatures.c +@@ -5,9 +5,14 @@ + + #include "../xmss.h" + #include "../params.h" ++#include "../xmss_callbacks.h" + #include "../randombytes.h" + #include "../utils.h" + ++#include ++#include ++#include ++ + #define XMSS_MLEN 32 + + // #ifndef XMSS_SIGNATURES +@@ -32,7 +37,13 @@ + #define XMSS_SIGNATURES (1 << 10) + #endif + +-int main() ++static int rng_cb(void * output, size_t length); ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++ ++static WC_RNG rng; ++ ++int main(void) + { + xmss_params params; + uint32_t oid; +@@ -46,14 +57,31 @@ int main() + + unsigned char pk[XMSS_OID_LEN + params.pk_bytes]; + unsigned char sk[XMSS_OID_LEN + params.sk_bytes]; +- unsigned char *m = malloc(XMSS_MLEN); +- unsigned char *sm = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned char *mout = malloc(params.sig_bytes + XMSS_MLEN); +- unsigned long long smlen; ++ unsigned char *msg = malloc(XMSS_MLEN); ++ unsigned char *sig = malloc(params.sig_bytes); ++ unsigned long long siglen = params.sig_bytes; + unsigned long long idx; + unsigned long long j; + +- randombytes(m, XMSS_MLEN); ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } ++ ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } ++ ++ randombytes(msg, XMSS_MLEN); + + XMSS_KEYPAIR(pk, sk, oid); + +@@ -63,7 +91,7 @@ int main() + if( (i & 1023) == 0) + printf(" - iteration #%d:\n", i); + +- return_code = XMSS_SIGN(sk, sm, &smlen, m, XMSS_MLEN); ++ return_code = XMSS_SIGN(sk, sig, &siglen, msg, XMSS_MLEN); + + if (return_code != 0) { + printf(" Error! Return code was %d\n",return_code); +@@ -75,7 +103,7 @@ int main() + for (; i < (XMSS_SIGNATURES) + 2; i++) { + printf(" - iteration #%d:\n", i); + +- return_code = XMSS_SIGN(sk, sm, &smlen, m, XMSS_MLEN); ++ return_code = XMSS_SIGN(sk, sig, &siglen, msg, XMSS_MLEN); + + if (return_code == 0) { + printf(" Error! Return code was %d\n",return_code); +@@ -84,19 +112,65 @@ int main() + else { + printf("Return code as expected [%d].\n", return_code); + } +- ++ + idx = (unsigned long)bytes_to_ull(sk, params.index_bytes); + printf("Index: %llu\n", idx); + printf("Secret key: %llu\n", idx); + for (j = 0; j < XMSS_OID_LEN + params.sk_bytes;j++) + printf("%d ", sk[j]); +- ++ + printf("\n"); + } +- +- free(m); +- free(sm); +- free(mout); ++ ++ free(msg); ++ free(sig); + + return ret; + } ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} +diff --git a/hash.c b/thash.c +similarity index 82% +rename from hash.c +rename to thash.c +index 6724d43..eb01946 100644 +--- a/hash.c ++++ b/thash.c +@@ -1,12 +1,14 @@ + #include ++#include + #include +-#include + ++#include "xmss_callbacks.h" + #include "hash_address.h" + #include "utils.h" + #include "params.h" +-#include "hash.h" +-#include "fips202.h" ++#include "thash.h" ++ ++static sha_cb_t sha_cb = NULL; + + #define XMSS_HASH_PADDING_F 0 + #define XMSS_HASH_PADDING_H 1 +@@ -22,37 +24,39 @@ void addr_to_bytes(unsigned char *bytes, const uint32_t addr[8]) + } + } + ++int xmss_set_sha_cb(sha_cb_t cb) ++{ ++ if (cb == NULL) { ++ return -1; ++ } ++ ++ sha_cb = cb; ++ ++ return 0; ++} ++ + static int core_hash(const xmss_params *params, + unsigned char *out, + const unsigned char *in, unsigned long long inlen) + { +- unsigned char buf[64]; ++ int ret = -1; + +- if (params->n == 24 && params->func == XMSS_SHA2) { +- SHA256(in, inlen, buf); +- memcpy(out, buf, 24); +- } +- else if (params->n == 24 && params->func == XMSS_SHAKE256) { +- shake256(out, 24, in, inlen); +- } +- else if (params->n == 32 && params->func == XMSS_SHA2) { +- SHA256(in, inlen, out); +- } +- else if (params->n == 32 && params->func == XMSS_SHAKE128) { +- shake128(out, 32, in, inlen); +- } +- else if (params->n == 32 && params->func == XMSS_SHAKE256) { +- shake256(out, 32, in, inlen); +- } +- else if (params->n == 64 && params->func == XMSS_SHA2) { +- SHA512(in, inlen, out); ++ if (sha_cb == NULL) { ++ return -1; + } +- else if (params->n == 64 && params->func == XMSS_SHAKE256) { +- shake256(out, 64, in, inlen); ++ ++ if (params == NULL || out == NULL || in == NULL) { ++ return -1; + } +- else { ++ ++ /* The choice of SHA256 and n=32 is set at compile time. */ ++ if (params->n != XMSS_SHA256_N || params->func != XMSS_SHA2) { + return -1; + } ++ ++ ret = sha_cb(in, inlen, out); ++ ++ if (ret != 0) { return ret; } + return 0; + } + +@@ -63,7 +67,11 @@ int prf(const xmss_params *params, + unsigned char *out, const unsigned char in[32], + const unsigned char *key) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buf[XMSS_SHA256_PADDING_LEN + XMSS_SHA256_N + 32]; ++#else + unsigned char buf[params->padding_len + params->n + 32]; ++#endif + + ull_to_bytes(buf, params->padding_len, XMSS_HASH_PADDING_PRF); + memcpy(buf + params->padding_len, key, params->n); +@@ -80,7 +88,11 @@ int prf_keygen(const xmss_params *params, + unsigned char *out, const unsigned char *in, + const unsigned char *key) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buf[XMSS_SHA256_PADDING_LEN + 2 * XMSS_SHA256_N + 32]; ++#else + unsigned char buf[params->padding_len + 2*params->n + 32]; ++#endif + + ull_to_bytes(buf, params->padding_len, XMSS_HASH_PADDING_PRF_KEYGEN); + memcpy(buf + params->padding_len, key, params->n); +@@ -118,8 +130,13 @@ int thash_h(const xmss_params *params, + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buf[XMSS_SHA256_PADDING_LEN + 3 * XMSS_SHA256_N]; ++ unsigned char bitmask[2 * XMSS_SHA256_N]; ++#else + unsigned char buf[params->padding_len + 3 * params->n]; + unsigned char bitmask[2 * params->n]; ++#endif + unsigned char addr_as_bytes[32]; + unsigned int i; + +@@ -150,8 +167,13 @@ int thash_f(const xmss_params *params, + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buf[XMSS_SHA256_PADDING_LEN + 2 * XMSS_SHA256_N]; ++ unsigned char bitmask[XMSS_SHA256_N]; ++#else + unsigned char buf[params->padding_len + 2 * params->n]; + unsigned char bitmask[params->n]; ++#endif + unsigned char addr_as_bytes[32]; + unsigned int i; + +diff --git a/hash.h b/thash.h +similarity index 96% +rename from hash.h +rename to thash.h +index 6e13d12..dcfb781 100644 +--- a/hash.h ++++ b/thash.h +@@ -1,9 +1,10 @@ +-#ifndef XMSS_HASH_H +-#define XMSS_HASH_H ++#ifndef XMSS_THASH_H ++#define XMSS_THASH_H + + #include + #include "params.h" + ++ + void addr_to_bytes(unsigned char *bytes, const uint32_t addr[8]); + + int prf(const xmss_params *params, +diff --git a/ui/keypair.c b/ui/keypair.c +index 612c448..64dc067 100644 +--- a/ui/keypair.c ++++ b/ui/keypair.c +@@ -3,6 +3,11 @@ + + #include "../params.h" + #include "../xmss.h" ++#include "../xmss_callbacks.h" ++ ++#include ++#include ++#include + + #ifdef XMSSMT + #define XMSS_STR_TO_OID xmssmt_str_to_oid +@@ -14,11 +19,61 @@ + #define XMSS_KEYPAIR xmss_keypair + #endif + ++static WC_RNG rng; ++ ++static int rng_cb(void * output, size_t length) ++{ ++ int ret = 0; ++ ++ if (output == NULL) { ++ return -1; ++ } ++ ++ if (length == 0) { ++ return 0; ++ } ++ ++ ret = wc_RNG_GenerateBlock(&rng, output, (word32) length); ++ ++ if (ret) { ++ printf("error: xmss rng_cb failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int sha256_cb(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out) ++{ ++ wc_Sha256 sha; ++ ++ if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { ++ printf("SHA256 Init failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Update(&sha, in, (word32) inlen) != 0) { ++ printf("SHA256 Update failed"); ++ return -1; ++ } ++ ++ if (wc_Sha256Final(&sha, out) != 0) { ++ printf("SHA256 Final failed"); ++ wc_Sha256Free(&sha); ++ return -1; ++ } ++ wc_Sha256Free(&sha); ++ ++ return 0; ++} ++ + int main(int argc, char **argv) + { + xmss_params params; + uint32_t oid = 0; + int parse_oid_result = 0; ++ int ret = -1; + + if (argc != 2) { + fprintf(stderr, "Expected parameter string (e.g. 'XMSS-SHA2_10_256')" +@@ -27,6 +82,24 @@ int main(int argc, char **argv) + return -1; + } + ++ ret = wc_InitRng(&rng); ++ if (ret != 0) { ++ printf("error: init rng failed: %d\n", ret); ++ return -1; ++ } ++ ++ ret = xmss_set_sha_cb(sha256_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_sha_cb failed"); ++ return -1; ++ } ++ ++ ret = xmss_set_rng_cb(rng_cb); ++ if (ret != 0) { ++ printf("error: xmss_set_rng_cb failed"); ++ return -1; ++ } ++ + XMSS_STR_TO_OID(&oid, argv[1]); + parse_oid_result = XMSS_PARSE_OID(¶ms, oid); + if (parse_oid_result != 0) { +diff --git a/wots.c b/wots.c +index d9c8449..844940c 100644 +--- a/wots.c ++++ b/wots.c +@@ -2,7 +2,7 @@ + #include + + #include "utils.h" +-#include "hash.h" ++#include "thash.h" + #include "wots.h" + #include "hash_address.h" + #include "params.h" +@@ -12,11 +12,15 @@ + * Expands an n-byte array into a len*n byte array using the `prf_keygen` function. + */ + static void expand_seed(const xmss_params *params, +- unsigned char *outseeds, const unsigned char *inseed, ++ unsigned char *outseeds, const unsigned char *inseed, + const unsigned char *pub_seed, uint32_t addr[8]) + { + uint32_t i; ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buf[XMSS_SHA256_N + 32]; ++#else + unsigned char buf[params->n + 32]; ++#endif + + set_hash_addr(addr, 0); + set_key_and_mask(addr, 0); +@@ -83,7 +87,23 @@ static void wots_checksum(const xmss_params *params, + int *csum_base_w, const int *msg_base_w) + { + int csum = 0; ++#if defined WOLFBOOT_SIGN_XMSS ++ /* See params.c ++ * ++ * For NIST SP 800-208 parm sets with SHA256, we have: ++ * ++ * params->wots_w == 16 ++ * params->wots_log_w == 4 ++ * params->wots_len2 == 3 ++ * ++ * Therefore: ++ * ++ * (3 * 4 + 7) / 8 = 2 ++ * */ ++ unsigned char csum_bytes[2]; ++#else + unsigned char csum_bytes[(params->wots_len2 * params->wots_log_w + 7) / 8]; ++#endif + unsigned int i; + + /* Compute checksum. */ +@@ -94,7 +114,7 @@ static void wots_checksum(const xmss_params *params, + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((params->wots_len2 * params->wots_log_w) % 8)); +- ull_to_bytes(csum_bytes, sizeof(csum_bytes), csum); ++ ull_to_bytes(csum_bytes, (unsigned int) sizeof(csum_bytes), csum); + base_w(params, csum_base_w, params->wots_len2, csum_bytes); + } + +@@ -139,7 +159,8 @@ void wots_sign(const xmss_params *params, + const unsigned char *seed, const unsigned char *pub_seed, + uint32_t addr[8]) + { +- int lengths[params->wots_len]; ++ /* int lengths[params->wots_len]; */ ++ int lengths[67]; + uint32_t i; + + chain_lengths(params, lengths, msg); +@@ -163,7 +184,11 @@ void wots_pk_from_sig(const xmss_params *params, unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ int lengths[XMSS_SHA256_WOTS_LEN]; ++#else + int lengths[params->wots_len]; ++#endif + uint32_t i; + + chain_lengths(params, lengths, msg); +diff --git a/xmss.c b/xmss.c +index 9030f6e..b5f5168 100644 +--- a/xmss.c ++++ b/xmss.c +@@ -1,5 +1,6 @@ + #include + ++#include "xmss.h" + #include "params.h" + #include "xmss_core.h" + +@@ -7,6 +8,7 @@ + identify the parameter set to be used. After setting the parameters accordingly + it falls back to the regular XMSS core functions. */ + ++#ifndef XMSS_VERIFY_ONLY + int xmss_keypair(unsigned char *pk, unsigned char *sk, const uint32_t oid) + { + xmss_params params; +@@ -26,8 +28,8 @@ int xmss_keypair(unsigned char *pk, unsigned char *sk, const uint32_t oid) + } + + int xmss_sign(unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen) ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen) + { + xmss_params params; + uint32_t oid = 0; +@@ -39,24 +41,7 @@ int xmss_sign(unsigned char *sk, + if (xmss_parse_oid(¶ms, oid)) { + return -1; + } +- return xmss_core_sign(¶ms, sk + XMSS_OID_LEN, sm, smlen, m, mlen); +-} +- +-int xmss_sign_open(unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, +- const unsigned char *pk) +-{ +- xmss_params params; +- uint32_t oid = 0; +- unsigned int i; +- +- for (i = 0; i < XMSS_OID_LEN; i++) { +- oid |= pk[XMSS_OID_LEN - i - 1] << (i * 8); +- } +- if (xmss_parse_oid(¶ms, oid)) { +- return -1; +- } +- return xmss_core_sign_open(¶ms, m, mlen, sm, smlen, pk + XMSS_OID_LEN); ++ return xmss_core_sign(¶ms, sk + XMSS_OID_LEN, sig, siglen, msg, msglen); + } + + int xmssmt_keypair(unsigned char *pk, unsigned char *sk, const uint32_t oid) +@@ -90,9 +75,27 @@ int xmssmt_sign(unsigned char *sk, + } + return xmssmt_core_sign(¶ms, sk + XMSS_OID_LEN, sm, smlen, m, mlen); + } ++#endif /* ifndef XMSS_VERIFY_ONLY */ ++ ++int xmss_sign_open(const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, ++ const unsigned char *pk) ++{ ++ xmss_params params; ++ uint32_t oid = 0; ++ unsigned int i; ++ ++ for (i = 0; i < XMSS_OID_LEN; i++) { ++ oid |= pk[XMSS_OID_LEN - i - 1] << (i * 8); ++ } ++ if (xmss_parse_oid(¶ms, oid)) { ++ return -1; ++ } ++ return xmss_core_sign_open(¶ms, msg, msglen, sig, siglen, pk + XMSS_OID_LEN); ++} + +-int xmssmt_sign_open(unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, ++int xmssmt_sign_open(const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, + const unsigned char *pk) + { + xmss_params params; +@@ -105,5 +108,5 @@ int xmssmt_sign_open(unsigned char *m, unsigned long long *mlen, + if (xmssmt_parse_oid(¶ms, oid)) { + return -1; + } +- return xmssmt_core_sign_open(¶ms, m, mlen, sm, smlen, pk + XMSS_OID_LEN); ++ return xmssmt_core_sign_open(¶ms, msg, msglen, sig, siglen, pk + XMSS_OID_LEN); + } +diff --git a/xmss.h b/xmss.h +index c7b4b69..b2b3980 100644 +--- a/xmss.h ++++ b/xmss.h +@@ -3,6 +3,7 @@ + + #include + ++#ifndef XMSS_VERIFY_ONLY + /** + * Generates a XMSS key pair for a given parameter set. + * Format sk: [OID || (32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] +@@ -13,23 +14,12 @@ int xmss_keypair(unsigned char *pk, unsigned char *sk, const uint32_t oid); + /** + * Signs a message using an XMSS secret key. + * Returns +- * 1. an array containing the signature followed by the message AND ++ * 1. an array containing the signature AND + * 2. an updated secret key! + */ + int xmss_sign(unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen); +- +-/** +- * Verifies a given message signature pair using a given public key. +- * +- * Note: m and mlen are pure outputs which carry the message in case +- * verification succeeds. The (input) message is assumed to be contained in sm +- * which has the form [signature || message]. +- */ +-int xmss_sign_open(unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, +- const unsigned char *pk); ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen); + + /* + * Generates a XMSSMT key pair for a given parameter set. +@@ -41,21 +31,33 @@ int xmssmt_keypair(unsigned char *pk, unsigned char *sk, const uint32_t oid); + /** + * Signs a message using an XMSSMT secret key. + * Returns +- * 1. an array containing the signature followed by the message AND ++ * 1. an array containing the signature AND + * 2. an updated secret key! + */ + int xmssmt_sign(unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen); ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen); ++#endif /* ifndef XMSS_VERIFY_ONLY */ ++ ++/** ++ * Verifies a given signature using a given public key. ++ * ++ * - msg is the input message of length msglen. ++ * - sig is the signature to verify, of length siglen. ++ * - pk is the public key without an OID. ++ */ ++int xmss_sign_open(const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, ++ const unsigned char *pk); + + /** +- * Verifies a given message signature pair using a given public key. ++ * Verifies a given signature using a given public key. + * +- * Note: m and mlen are pure outputs which carry the message in case +- * verification succeeds. The (input) message is assumed to be contained in sm +- * which has the form [signature || message]. ++ * - msg is the input message of length msglen. ++ * - sig is the signature to verify, of length siglen. ++ * - pk is the public key without an OID. + */ +-int xmssmt_sign_open(unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, ++int xmssmt_sign_open(const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, + const unsigned char *pk); + #endif +diff --git a/xmss_callbacks.h b/xmss_callbacks.h +new file mode 100644 +index 0000000..31bd300 +--- /dev/null ++++ b/xmss_callbacks.h +@@ -0,0 +1,12 @@ ++#ifndef XMSS_CALLBACKS_H ++#define XMSS_CALLBACKS_H ++ ++/* Callback used for SHA and RNG operations. */ ++typedef int (*sha_cb_t)(const unsigned char *in, unsigned long long inlen, ++ unsigned char *out); ++typedef int (*rng_cb_t)(void * output, size_t length); ++ ++int xmss_set_sha_cb(sha_cb_t cb); ++int xmss_set_rng_cb(rng_cb_t cb); ++ ++#endif +diff --git a/xmss_commons.c b/xmss_commons.c +index 2b76b94..0599a76 100644 +--- a/xmss_commons.c ++++ b/xmss_commons.c +@@ -2,7 +2,7 @@ + #include + #include + +-#include "hash.h" ++#include "thash.h" + #include "hash_address.h" + #include "params.h" + #include "wots.h" +@@ -57,7 +57,11 @@ static void compute_root(const xmss_params *params, unsigned char *root, + const unsigned char *pub_seed, uint32_t addr[8]) + { + uint32_t i; ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char buffer[2 * XMSS_SHA256_N]; ++#else + unsigned char buffer[2*params->n]; ++#endif + + /* If leafidx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ +@@ -74,7 +78,7 @@ static void compute_root(const xmss_params *params, unsigned char *root, + for (i = 0; i < params->tree_height - 1; i++) { + set_tree_height(addr, i); + leafidx >>= 1; +- set_tree_index(addr, leafidx); ++ set_tree_index(addr, (uint32_t) leafidx); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leafidx & 1) { +@@ -91,7 +95,7 @@ static void compute_root(const xmss_params *params, unsigned char *root, + /* The last iteration is exceptional; we do not copy an auth_path node. */ + set_tree_height(addr, params->tree_height - 1); + leafidx >>= 1; +- set_tree_index(addr, leafidx); ++ set_tree_index(addr, (uint32_t) leafidx); + thash_h(params, root, buffer, pub_seed, addr); + } + +@@ -105,7 +109,11 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t ltree_addr[8], uint32_t ots_addr[8]) + { ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char pk[XMSS_SHA256_WOTS_SIG_BYTES]; ++#else + unsigned char pk[params->wots_sig_bytes]; ++#endif + + wots_pkgen(params, pk, sk_seed, pub_seed, ots_addr); + +@@ -118,31 +126,43 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf, + * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] + */ + int xmss_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, ++ const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, + const unsigned char *pk) + { + /* XMSS signatures are fundamentally an instance of XMSSMT signatures. + For d=1, as is the case with XMSS, some of the calls in the XMSSMT + routine become vacuous (i.e. the loop only iterates once, and address + management can be simplified a bit).*/ +- return xmssmt_core_sign_open(params, m, mlen, sm, smlen, pk); ++ return xmssmt_core_sign_open(params, msg, msglen, sig, siglen, pk); + } + + /** + * Verifies a given message signature pair under a given public key. + * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] ++ * ++ * Note: in WOLFBOOT_SIGN_XMSS build, the max allowed message length (msglen) ++ * is XMSS_SHA256_MAX_MSG_LEN. This is to facilitate having a manageable small ++ * static array, rather than a variable length array, for the message hash. + */ + int xmssmt_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, ++ const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sig, unsigned long long siglen, + const unsigned char *pk) + { + const unsigned char *pub_root = pk; + const unsigned char *pub_seed = pk + params->n; ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char m_with_prefix[XMSS_SHA256_MAX_MSG_HASH_LEN]; ++ unsigned char wots_pk[XMSS_SHA256_WOTS_SIG_BYTES]; ++ unsigned char leaf[XMSS_SHA256_N]; ++ unsigned char root[XMSS_SHA256_N]; ++#else ++ unsigned char m_with_prefix[*msglen + params->padding_len + 3*params->n]; + unsigned char wots_pk[params->wots_sig_bytes]; + unsigned char leaf[params->n]; + unsigned char root[params->n]; ++#endif + unsigned char *mhash = root; + unsigned long long idx = 0; + unsigned int i; +@@ -152,24 +172,33 @@ int xmssmt_core_sign_open(const xmss_params *params, + uint32_t ltree_addr[8] = {0}; + uint32_t node_addr[8] = {0}; + ++#if defined WOLFBOOT_SIGN_XMSS ++ if (*msglen > XMSS_SHA256_MAX_MSG_LEN) { ++ return -1; ++ } ++#endif ++ ++ if (siglen != params->sig_bytes) { ++ /* Some inconsistency has happened. */ ++ return -1; ++ } ++ + set_type(ots_addr, XMSS_ADDR_TYPE_OTS); + set_type(ltree_addr, XMSS_ADDR_TYPE_LTREE); + set_type(node_addr, XMSS_ADDR_TYPE_HASHTREE); + +- *mlen = smlen - params->sig_bytes; +- + /* Convert the index bytes from the signature to an integer. */ +- idx = bytes_to_ull(sm, params->index_bytes); ++ idx = bytes_to_ull(sig, params->index_bytes); + + /* Put the message all the way at the end of the m buffer, so that we can + * prepend the required other inputs for the hash function. */ +- memcpy(m + params->sig_bytes, sm + params->sig_bytes, *mlen); ++ memset(m_with_prefix, 0, sizeof(m_with_prefix)); ++ memcpy(m_with_prefix + params->padding_len + 3*params->n, msg, *msglen); + + /* Compute the message hash. */ +- hash_message(params, mhash, sm + params->index_bytes, pk, idx, +- m + params->sig_bytes - params->padding_len - 3*params->n, +- *mlen); +- sm += params->index_bytes + params->n; ++ hash_message(params, mhash, sig + params->index_bytes, pk, idx, ++ m_with_prefix, *msglen); ++ sig += params->index_bytes + params->n; + + /* For each subtree.. */ + for (i = 0; i < params->d; i++) { +@@ -188,28 +217,24 @@ int xmssmt_core_sign_open(const xmss_params *params, + set_ots_addr(ots_addr, idx_leaf); + /* Initially, root = mhash, but on subsequent iterations it is the root + of the subtree below the currently processed subtree. */ +- wots_pk_from_sig(params, wots_pk, sm, root, pub_seed, ots_addr); +- sm += params->wots_sig_bytes; ++ wots_pk_from_sig(params, wots_pk, sig, root, pub_seed, ots_addr); ++ sig += params->wots_sig_bytes; + + /* Compute the leaf node using the WOTS public key. */ + set_ltree_addr(ltree_addr, idx_leaf); + l_tree(params, leaf, wots_pk, pub_seed, ltree_addr); + + /* Compute the root node of this subtree. */ +- compute_root(params, root, leaf, idx_leaf, sm, pub_seed, node_addr); +- sm += params->tree_height*params->n; ++ compute_root(params, root, leaf, idx_leaf, sig, pub_seed, node_addr); ++ sig += params->tree_height*params->n; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, params->n)) { +- /* If not, zero the message */ +- memset(m, 0, *mlen); +- *mlen = 0; ++ /* If not, set message length to zero */ ++ *msglen = 0; + return -1; + } + +- /* If verification was successful, copy the message from the signature. */ +- memcpy(m, sm, *mlen); +- + return 0; + } +diff --git a/xmss_commons.h b/xmss_commons.h +index 9d9f077..33dbffe 100644 +--- a/xmss_commons.h ++++ b/xmss_commons.h +@@ -18,7 +18,7 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf, + * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] + */ + int xmss_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, ++ const unsigned char *msg, unsigned long long *msglen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + +@@ -27,7 +27,7 @@ int xmss_core_sign_open(const xmss_params *params, + * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] + */ + int xmssmt_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, ++ const unsigned char *msg, unsigned long long *msglen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + #endif +diff --git a/xmss_core.c b/xmss_core.c +index af9f8d1..7e76564 100644 +--- a/xmss_core.c ++++ b/xmss_core.c +@@ -2,15 +2,47 @@ + #include + #include + +-#include "hash.h" ++#include "thash.h" + #include "hash_address.h" + #include "params.h" +-#include "randombytes.h" + #include "wots.h" + #include "utils.h" + #include "xmss_commons.h" + #include "xmss_core.h" + ++#ifndef XMSS_VERIFY_ONLY ++#include "xmss_callbacks.h" ++ ++static rng_cb_t rng_cb = NULL; ++ ++typedef struct{ ++ unsigned char h; ++ unsigned long next_idx; ++ unsigned char stackusage; ++ unsigned char completed; ++ unsigned char *node; ++} treehash_inst; ++ ++typedef struct { ++ unsigned char *stack; ++ unsigned int stackoffset; ++ unsigned char *stacklevels; ++ unsigned char *auth; ++ unsigned char *keep; ++ treehash_inst *treehash; ++ unsigned char *retain; ++ unsigned int next_leaf; ++} bds_state; ++ ++int xmss_set_rng_cb(rng_cb_t cb) ++{ ++ if (cb == NULL) { ++ return -1; ++ } ++ rng_cb = cb; ++ return 0; ++} ++ + /** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. +@@ -85,6 +117,8 @@ static void treehash(const xmss_params *params, + memcpy(root, stack, params->n); + } + ++#endif /* ifndef XMSS_VERIFY_ONLY */ ++ + /** + * Given a set of parameters, this function returns the size of the secret key. + * This is implementation specific, as varying choices in tree traversal will +@@ -95,6 +129,8 @@ unsigned long long xmss_xmssmt_core_sk_bytes(const xmss_params *params) + return params->index_bytes + 4 * params->n; + } + ++#ifndef XMSS_VERIFY_ONLY ++ + /* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) index || SK_SEED || SK_PRF || root || PUB_SEED] +@@ -110,19 +146,19 @@ int xmss_core_keypair(const xmss_params *params, + } + + /** +- * Signs a message. Returns an array containing the signature followed by the +- * message and an updated secret key. ++ * Signs a message. Returns an array containing the signature, ++ * and an updated secret key. + */ + int xmss_core_sign(const xmss_params *params, + unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen) ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen) + { + /* XMSS signatures are fundamentally an instance of XMSSMT signatures. + For d=1, as is the case with XMSS, some of the calls in the XMSSMT + routine become vacuous (i.e. the loop only iterates once, and address + management can be simplified a bit).*/ +- return xmssmt_core_sign(params, sk, sm, smlen, m, mlen); ++ return xmssmt_core_sign(params, sk, sig, siglen, msg, msglen); + } + + /* +@@ -169,21 +205,35 @@ int xmssmt_core_keypair(const xmss_params *params, + unsigned char *pk, unsigned char *sk) + { + unsigned char seed[3 * params->n]; ++ int ret = 0; ++ ++ //ret = wc_RNG_GenerateBlock(rng, seed, (word32) sizeof(seed)); ++ ret = rng_cb(seed, sizeof(seed)); ++ ++ if (ret != 0) { ++ return -1; ++ } + +- randombytes(seed, 3 * params->n); + xmssmt_core_seed_keypair(params, pk, sk, seed); + + return 0; + } + ++ + /** +- * Signs a message. Returns an array containing the signature followed by the +- * message and an updated secret key. ++ * Signs a message. ++ * Returns ++ * 1. an array containing the signature AND ++ * 2. an updated secret key! ++ * ++ * Note: in WOLFBOOT_SIGN_XMSS build, the max allowed message length (msglen) ++ * is XMSS_SHA256_MAX_MSG_LEN. This is to facilitate having a manageable small ++ * static array, rather than a variable length array, for the message hash. + */ + int xmssmt_core_sign(const xmss_params *params, + unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen) ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen) + { + const unsigned char *sk_seed = sk + params->index_bytes; + const unsigned char *sk_prf = sk + params->index_bytes + params->n; +@@ -197,28 +247,45 @@ int xmssmt_core_sign(const xmss_params *params, + unsigned int i; + uint32_t idx_leaf; + ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char m_with_prefix[XMSS_SHA256_MAX_MSG_HASH_LEN]; ++#else ++ unsigned char m_with_prefix[msglen + params->padding_len + 3*params->n]; ++#endif ++ ++#if defined WOLFBOOT_SIGN_XMSS ++ if (msglen > XMSS_SHA256_MAX_MSG_LEN) { ++ return -1; ++ } ++#endif ++ ++ if (*siglen != params->sig_bytes) { ++ /* Some inconsistency has happened. */ ++ return -1; ++ } ++ + uint32_t ots_addr[8] = {0}; + set_type(ots_addr, XMSS_ADDR_TYPE_OTS); + + /* Already put the message in the right place, to make it easier to prepend + * things when computing the hash over the message. */ +- memcpy(sm + params->sig_bytes, m, mlen); +- *smlen = params->sig_bytes + mlen; ++ memset(m_with_prefix, 0, sizeof(m_with_prefix)); ++ memcpy(m_with_prefix + params->padding_len + 3*params->n, msg, msglen); + + /* Read and use the current index from the secret key. */ + idx = (unsigned long)bytes_to_ull(sk, params->index_bytes); +- ++ + /* Check if we can still sign with this sk. + * If not, return -2 +- * +- * If this is the last possible signature (because the max index value +- * is reached), production implementations should delete the secret key ++ * ++ * If this is the last possible signature (because the max index value ++ * is reached), production implementations should delete the secret key + * to prevent accidental further use. +- * +- * For the case of total tree height of 64 we do not use the last signature +- * to be on the safe side (there is no index value left to indicate that the ++ * ++ * For the case of total tree height of 64 we do not use the last signature ++ * to be on the safe side (there is no index value left to indicate that the + * key is finished, hence external handling would be necessary) +- */ ++ */ + if (idx >= ((1ULL << params->full_height) - 1)) { + // Delete secret key here. We only do this in memory, production code + // has to make sure that this happens on disk. +@@ -226,11 +293,11 @@ int xmssmt_core_sign(const xmss_params *params, + memset(sk + params->index_bytes, 0, (params->sk_bytes - params->index_bytes)); + if (idx > ((1ULL << params->full_height) - 1)) + return -2; // We already used all one-time keys +- if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) ++ if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) + return -2; // We already used all one-time keys + } +- +- memcpy(sm, sk, params->index_bytes); ++ ++ memcpy(sig, sk, params->index_bytes); + + /************************************************************************* + * THIS IS WHERE PRODUCTION IMPLEMENTATIONS WOULD UPDATE THE SECRET KEY. * +@@ -240,13 +307,12 @@ int xmssmt_core_sign(const xmss_params *params, + + /* Compute the digest randomization value. */ + ull_to_bytes(idx_bytes_32, 32, idx); +- prf(params, sm + params->index_bytes, idx_bytes_32, sk_prf); ++ prf(params, sig + params->index_bytes, idx_bytes_32, sk_prf); + + /* Compute the message hash. */ +- hash_message(params, mhash, sm + params->index_bytes, pub_root, idx, +- sm + params->sig_bytes - params->padding_len - 3*params->n, +- mlen); +- sm += params->index_bytes + params->n; ++ hash_message(params, mhash, sig + params->index_bytes, pub_root, idx, ++ m_with_prefix, msglen); ++ sig += params->index_bytes + params->n; + + set_type(ots_addr, XMSS_ADDR_TYPE_OTS); + +@@ -261,13 +327,14 @@ int xmssmt_core_sign(const xmss_params *params, + /* Compute a WOTS signature. */ + /* Initially, root = mhash, but on subsequent iterations it is the root + of the subtree below the currently processed subtree. */ +- wots_sign(params, sm, root, sk_seed, pub_seed, ots_addr); +- sm += params->wots_sig_bytes; ++ wots_sign(params, sig, root, sk_seed, pub_seed, ots_addr); ++ sig += params->wots_sig_bytes; + + /* Compute the authentication path for the used WOTS leaf. */ +- treehash(params, root, sm, sk_seed, pub_seed, idx_leaf, ots_addr); +- sm += params->tree_height*params->n; ++ treehash(params, root, sig, sk_seed, pub_seed, idx_leaf, ots_addr); ++ sig += params->tree_height*params->n; + } + + return 0; + } ++#endif /* ifndef XMSS_VERIFY_ONLY */ +diff --git a/xmss_core.h b/xmss_core.h +index e83bc7d..21d72ef 100644 +--- a/xmss_core.h ++++ b/xmss_core.h +@@ -12,6 +12,7 @@ + */ + unsigned long long xmss_xmssmt_core_sk_bytes(const xmss_params *params); + ++#ifndef XMSS_VERIFY_ONLY + /* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) index || SK_SEED || SK_PRF || PUB_SEED || root] +@@ -29,15 +30,6 @@ int xmss_core_sign(const xmss_params *params, + unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen); + +-/** +- * Verifies a given message signature pair under a given public key. +- * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] +- */ +-int xmss_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, +- const unsigned char *sm, unsigned long long smlen, +- const unsigned char *pk); +- + /* + * Generates a XMSSMT key pair for a given parameter set. + * Format sk: [(ceil(h/8) bit) index || SK_SEED || SK_PRF || PUB_SEED || root] +@@ -64,13 +56,23 @@ int xmssmt_core_sign(const xmss_params *params, + unsigned char *sk, + unsigned char *sm, unsigned long long *smlen, + const unsigned char *m, unsigned long long mlen); ++#endif /* ifndef XMSS_VERIFY_ONLY */ ++ ++/** ++ * Verifies a given message signature pair under a given public key. ++ * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] ++ */ ++int xmss_core_sign_open(const xmss_params *params, ++ const unsigned char *msg, unsigned long long *msglen, ++ const unsigned char *sm, unsigned long long smlen, ++ const unsigned char *pk); + + /** + * Verifies a given message signature pair under a given public key. + * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] + */ + int xmssmt_core_sign_open(const xmss_params *params, +- unsigned char *m, unsigned long long *mlen, ++ const unsigned char *msg, unsigned long long *msglen, + const unsigned char *sm, unsigned long long smlen, + const unsigned char *pk); + +diff --git a/xmss_core_fast.c b/xmss_core_fast.c +index cbf87ec..0a31323 100644 +--- a/xmss_core_fast.c ++++ b/xmss_core_fast.c +@@ -2,15 +2,19 @@ + #include + #include + +-#include "hash.h" ++#include "thash.h" + #include "hash_address.h" + #include "params.h" +-#include "randombytes.h" + #include "wots.h" + #include "utils.h" + #include "xmss_commons.h" + #include "xmss_core.h" + ++#ifndef XMSS_VERIFY_ONLY ++#include "xmss_callbacks.h" ++ ++static rng_cb_t rng_cb = NULL; ++ + typedef struct{ + unsigned char h; + unsigned long next_idx; +@@ -30,6 +34,15 @@ typedef struct { + unsigned int next_leaf; + } bds_state; + ++int xmss_set_rng_cb(rng_cb_t cb) ++{ ++ if (cb == NULL) { ++ return -1; ++ } ++ rng_cb = cb; ++ return 0; ++} ++ + /* These serialization functions provide a transition between the current + way of storing the state in an exposed struct, and storing it as part of the + byte array that is the secret key. +@@ -94,7 +107,7 @@ static void xmssmt_deserialize_state(const xmss_params *params, + states[i].stack = sk; + sk += (params->tree_height + 1) * params->n; + +- states[i].stackoffset = bytes_to_ull(sk, 4); ++ states[i].stackoffset = (unsigned int) bytes_to_ull(sk, 4); + sk += 4; + + states[i].stacklevels = sk; +@@ -126,7 +139,7 @@ static void xmssmt_deserialize_state(const xmss_params *params, + states[i].retain = sk; + sk += ((1 << params->bds_k) - params->bds_k - 1) * params->n; + +- states[i].next_leaf = bytes_to_ull(sk, 4); ++ states[i].next_leaf = (unsigned int) bytes_to_ull(sk, 4); + sk += 4; + } + +@@ -299,8 +312,8 @@ static void treehash_update(const xmss_params *params, + copy_subtree_addr(node_addr, addr); + set_type(node_addr, 2); + +- set_ltree_addr(ltree_addr, treehash->next_idx); +- set_ots_addr(ots_addr, treehash->next_idx); ++ set_ltree_addr(ltree_addr, (uint32_t) treehash->next_idx); ++ set_ots_addr(ots_addr, (uint32_t) treehash->next_idx); + + unsigned char nodebuffer[2 * params->n]; + unsigned int nodeheight = 0; +@@ -309,7 +322,7 @@ static void treehash_update(const xmss_params *params, + memcpy(nodebuffer + params->n, nodebuffer, params->n); + memcpy(nodebuffer, state->stack + (state->stackoffset-1)*params->n, params->n); + set_tree_height(node_addr, nodeheight); +- set_tree_index(node_addr, (treehash->next_idx >> (nodeheight+1))); ++ set_tree_index(node_addr, (uint32_t) (treehash->next_idx >> (nodeheight+1))); + thash_h(params, nodebuffer, nodebuffer, pub_seed, node_addr); + nodeheight++; + treehash->stackusage--; +@@ -476,13 +489,13 @@ static void bds_round(const xmss_params *params, + memcpy(state->keep + (tau >> 1)*params->n, state->auth + tau*params->n, params->n); + } + if (tau == 0) { +- set_ltree_addr(ltree_addr, leaf_idx); +- set_ots_addr(ots_addr, leaf_idx); ++ set_ltree_addr(ltree_addr, (uint32_t) leaf_idx); ++ set_ots_addr(ots_addr, (uint32_t) leaf_idx); + gen_leaf_wots(params, state->auth, sk_seed, pub_seed, ltree_addr, ots_addr); + } + else { + set_tree_height(node_addr, (tau-1)); +- set_tree_index(node_addr, leaf_idx >> tau); ++ set_tree_index(node_addr, (uint32_t) leaf_idx >> tau); + thash_h(params, state->auth + tau * params->n, buf, pub_seed, node_addr); + for (i = 0; i < tau; i++) { + if (i < params->tree_height - params->bds_k) { +@@ -490,13 +503,13 @@ static void bds_round(const xmss_params *params, + } + else { + offset = (1 << (params->tree_height - 1 - i)) + i - params->tree_height; +- rowidx = ((leaf_idx >> i) - 1) >> 1; ++ rowidx = (unsigned int) ((leaf_idx >> i) - 1) >> 1; + memcpy(state->auth + i * params->n, state->retain + (offset + rowidx) * params->n, params->n); + } + } + + for (i = 0; i < ((tau < params->tree_height - params->bds_k) ? tau : (params->tree_height - params->bds_k)); i++) { +- startidx = leaf_idx + 1 + 3 * (1 << i); ++ startidx = (unsigned int) leaf_idx + 1 + 3 * (1 << i); + if (startidx < 1U << params->tree_height) { + state->treehash[i].h = i; + state->treehash[i].next_idx = startidx; +@@ -507,6 +520,8 @@ static void bds_round(const xmss_params *params, + } + } + ++#endif /* ifndef XMSS_VERIFY_ONLY */ ++ + /** + * Given a set of parameters, this function returns the size of the secret key. + * This is implementation specific, as varying choices in tree traversal will +@@ -530,6 +545,7 @@ unsigned long long xmss_xmssmt_core_sk_bytes(const xmss_params *params) + + (params->d - 1) * params->wots_sig_bytes; + } + ++#ifndef XMSS_VERIFY_ONLY + /* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || root || PUB_SEED] +@@ -539,6 +555,7 @@ int xmss_core_keypair(const xmss_params *params, + unsigned char *pk, unsigned char *sk) + { + uint32_t addr[8] = {0}; ++ int ret = 0; + + // TODO refactor BDS state not to need separate treehash instances + bds_state state; +@@ -555,16 +572,31 @@ int xmss_core_keypair(const xmss_params *params, + sk[1] = 0; + sk[2] = 0; + sk[3] = 0; ++ + // Init SK_SEED (n byte) and SK_PRF (n byte) +- randombytes(sk + params->index_bytes, 2*params->n); ++ //ret = wc_RNG_GenerateBlock(rng, sk + params->index_bytes, ++ // (word32) 2*params->n); ++ ++ ret = rng_cb(sk + params->index_bytes, 2*params->n); ++ ++ if (ret != 0) { return -1; } + + // Init PUB_SEED (n byte) +- randombytes(sk + params->index_bytes + 3*params->n, params->n); ++ //ret = wc_RNG_GenerateBlock(rng, sk + params->index_bytes + 3*params->n, ++ // (word32) params->n); ++ ++ ret = rng_cb(sk + params->index_bytes + 3*params->n, params->n); ++ ++ if (ret != 0) { return -1; } ++ + // Copy PUB_SEED to public key + memcpy(pk + params->n, sk + params->index_bytes + 3*params->n, params->n); + + // Compute root +- treehash_init(params, pk, params->tree_height, 0, &state, sk + params->index_bytes, sk + params->index_bytes + 3*params->n, addr); ++ treehash_init(params, pk, params->tree_height, 0, &state, ++ sk + params->index_bytes, ++ sk + params->index_bytes + 3*params->n, addr); ++ + // copy root to sk + memcpy(sk + params->index_bytes + 2*params->n, pk, params->n); + +@@ -577,19 +609,39 @@ int xmss_core_keypair(const xmss_params *params, + /** + * Signs a message. + * Returns +- * 1. an array containing the signature followed by the message AND ++ * 1. an array containing the signature AND + * 2. an updated secret key! + * ++ * Note: in WOLFBOOT_SIGN_XMSS build, the max allowed message length (msglen) ++ * is XMSS_SHA256_MAX_MSG_LEN. This is to facilitate having a manageable small ++ * static array, rather than a variable length array, for the message hash. + */ + int xmss_core_sign(const xmss_params *params, + unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen) ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen) + { + const unsigned char *pub_root = sk + params->index_bytes + 2*params->n; + ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char m_with_prefix[XMSS_SHA256_MAX_MSG_HASH_LEN]; ++#else ++ unsigned char m_with_prefix[msglen + params->padding_len + 3*params->n]; ++#endif ++ + uint16_t i = 0; + ++#if defined WOLFBOOT_SIGN_XMSS ++ if (msglen > XMSS_SHA256_MAX_MSG_LEN) { ++ return -1; ++ } ++#endif ++ ++ if (*siglen != params->sig_bytes) { ++ /* Some inconsistency has happened. */ ++ return -1; ++ } ++ + // TODO refactor BDS state not to need separate treehash instances + bds_state state; + treehash_inst treehash[params->tree_height - params->bds_k]; +@@ -599,19 +651,21 @@ int xmss_core_sign(const xmss_params *params, + xmss_deserialize_state(params, &state, sk); + + // Extract SK +- unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; +- ++ unsigned long idx = ((unsigned long)sk[0] << 24) | ++ ((unsigned long)sk[1] << 16) | ++ ((unsigned long)sk[2] << 8) | sk[3]; ++ + /* Check if we can still sign with this sk. + * If not, return -2 +- * +- * If this is the last possible signature (because the max index value +- * is reached), production implementations should delete the secret key ++ * ++ * If this is the last possible signature (because the max index value ++ * is reached), production implementations should delete the secret key + * to prevent accidental further use. +- * +- * For the case of total tree height of 64 we do not use the last signature +- * to be on the safe side (there is no index value left to indicate that the ++ * ++ * For the case of total tree height of 64 we do not use the last signature ++ * to be on the safe side (there is no index value left to indicate that the + * key is finished, hence external handling would be necessary) +- */ ++ */ + if (idx >= ((1ULL << params->full_height) - 1)) { + // Delete secret key here. We only do this in memory, production code + // has to make sure that this happens on disk. +@@ -619,10 +673,10 @@ int xmss_core_sign(const xmss_params *params, + memset(sk + params->index_bytes, 0, (params->sk_bytes - params->index_bytes)); + if (idx > ((1ULL << params->full_height) - 1)) + return -2; // We already used all one-time keys +- if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) ++ if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) + return -2; // We already used all one-time keys + } +- ++ + unsigned char sk_seed[params->n]; + memcpy(sk_seed, sk + params->index_bytes, params->n); + unsigned char sk_prf[params->n]; +@@ -658,32 +712,32 @@ int xmss_core_sign(const xmss_params *params, + + /* Already put the message in the right place, to make it easier to prepend + * things when computing the hash over the message. */ +- memcpy(sm + params->sig_bytes, m, mlen); ++ memset(m_with_prefix, 0, sizeof(m_with_prefix)); ++ memcpy(m_with_prefix + params->padding_len + 3*params->n, msg, msglen); + + /* Compute the message hash. */ + hash_message(params, msg_h, R, pub_root, idx, +- sm + params->sig_bytes - params->padding_len - 3*params->n, +- mlen); ++ m_with_prefix, msglen); + + // Start collecting signature +- *smlen = 0; ++ *siglen = 0; + + // Copy index to signature +- sm[0] = (idx >> 24) & 255; +- sm[1] = (idx >> 16) & 255; +- sm[2] = (idx >> 8) & 255; +- sm[3] = idx & 255; ++ sig[0] = (idx >> 24) & 255; ++ sig[1] = (idx >> 16) & 255; ++ sig[2] = (idx >> 8) & 255; ++ sig[3] = idx & 255; + +- sm += 4; +- *smlen += 4; ++ sig += 4; ++ *siglen += 4; + + // Copy R to signature + for (i = 0; i < params->n; i++) { +- sm[i] = R[i]; ++ sig[i] = R[i]; + } + +- sm += params->n; +- *smlen += params->n; ++ sig += params->n; ++ *siglen += params->n; + + // ---------------------------------- + // Now we start to "really sign" +@@ -691,27 +745,24 @@ int xmss_core_sign(const xmss_params *params, + + // Prepare Address + set_type(ots_addr, 0); +- set_ots_addr(ots_addr, idx); ++ set_ots_addr(ots_addr, (uint32_t) idx); + + // Compute WOTS signature +- wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr); ++ wots_sign(params, sig, msg_h, sk_seed, pub_seed, ots_addr); + +- sm += params->wots_sig_bytes; +- *smlen += params->wots_sig_bytes; ++ sig += params->wots_sig_bytes; ++ *siglen += params->wots_sig_bytes; + + // the auth path was already computed during the previous round +- memcpy(sm, state.auth, params->tree_height*params->n); ++ memcpy(sig, state.auth, params->tree_height*params->n); + + if (idx < (1U << params->tree_height) - 1) { + bds_round(params, &state, idx, sk_seed, pub_seed, ots_addr); + bds_treehash_update(params, &state, (params->tree_height - params->bds_k) >> 1, sk_seed, pub_seed, ots_addr); + } + +- sm += params->tree_height*params->n; +- *smlen += params->tree_height*params->n; +- +- memcpy(sm, m, mlen); +- *smlen += mlen; ++ sig += params->tree_height*params->n; ++ *siglen += params->tree_height*params->n; + + /* Write the updated BDS state back into sk. */ + xmss_serialize_state(params, sk, &state); +@@ -727,9 +778,10 @@ int xmss_core_sign(const xmss_params *params, + int xmssmt_core_keypair(const xmss_params *params, + unsigned char *pk, unsigned char *sk) + { +- uint32_t addr[8] = {0}; +- unsigned int i; +- unsigned char *wots_sigs; ++ uint32_t addr[8] = {0}; ++ unsigned int i; ++ unsigned char * wots_sigs; ++ int ret = 0; + + // TODO refactor BDS state not to need separate treehash instances + bds_state states[2*params->d - 1]; +@@ -750,10 +802,19 @@ int xmssmt_core_keypair(const xmss_params *params, + sk[i] = 0; + } + // Init SK_SEED (params->n byte) and SK_PRF (params->n byte) +- randombytes(sk+params->index_bytes, 2*params->n); ++ //ret = wc_RNG_GenerateBlock(rng, sk+params->index_bytes, ++ // (word32) 2*params->n); ++ ret = rng_cb(sk+params->index_bytes, 2*params->n); ++ ++ if (ret != 0) { return -1; } + + // Init PUB_SEED (params->n byte) +- randombytes(sk+params->index_bytes + 3*params->n, params->n); ++ //ret = wc_RNG_GenerateBlock(rng, sk+params->index_bytes + 3*params->n, ++ // (word32) params->n); ++ ret = rng_cb(sk+params->index_bytes + 3*params->n, params->n); ++ ++ if (ret != 0) { return -1; } ++ + // Copy PUB_SEED to public key + memcpy(pk+params->n, sk+params->index_bytes+3*params->n, params->n); + +@@ -778,17 +839,37 @@ int xmssmt_core_keypair(const xmss_params *params, + /** + * Signs a message. + * Returns +- * 1. an array containing the signature followed by the message AND ++ * 1. an array containing the signature AND + * 2. an updated secret key! + * ++ * Note: in WOLFBOOT_SIGN_XMSS build, the max allowed message length (msglen) ++ * is XMSS_SHA256_MAX_MSG_LEN. This is to facilitate having a manageable small ++ * static array, rather than a variable length array, for the message hash. + */ + int xmssmt_core_sign(const xmss_params *params, + unsigned char *sk, +- unsigned char *sm, unsigned long long *smlen, +- const unsigned char *m, unsigned long long mlen) ++ unsigned char *sig, unsigned long long *siglen, ++ const unsigned char *msg, unsigned long long msglen) + { + const unsigned char *pub_root = sk + params->index_bytes + 2*params->n; + ++#if defined WOLFBOOT_SIGN_XMSS ++ unsigned char m_with_prefix[XMSS_SHA256_MAX_MSG_HASH_LEN]; ++#else ++ unsigned char m_with_prefix[msglen + params->padding_len + 3*params->n]; ++#endif ++ ++#if defined WOLFBOOT_SIGN_XMSS ++ if (msglen > XMSS_SHA256_MAX_MSG_LEN) { ++ return -1; ++ } ++#endif ++ ++ if (*siglen != params->sig_bytes) { ++ /* Some inconsistency has happened. */ ++ return -1; ++ } ++ + uint64_t idx_tree; + uint32_t idx_leaf; + uint64_t i, j; +@@ -824,15 +905,15 @@ int xmssmt_core_sign(const xmss_params *params, + + /* Check if we can still sign with this sk. + * If not, return -2 +- * +- * If this is the last possible signature (because the max index value +- * is reached), production implementations should delete the secret key ++ * ++ * If this is the last possible signature (because the max index value ++ * is reached), production implementations should delete the secret key + * to prevent accidental further use. +- * +- * For the case of total tree height of 64 we do not use the last signature +- * to be on the safe side (there is no index value left to indicate that the ++ * ++ * For the case of total tree height of 64 we do not use the last signature ++ * to be on the safe side (there is no index value left to indicate that the + * key is finished, hence external handling would be necessary) +- */ ++ */ + if (idx >= ((1ULL << params->full_height) - 1)) { + // Delete secret key here. We only do this in memory, production code + // has to make sure that this happens on disk. +@@ -840,10 +921,10 @@ int xmssmt_core_sign(const xmss_params *params, + memset(sk + params->index_bytes, 0, (params->sk_bytes - params->index_bytes)); + if (idx > ((1ULL << params->full_height) - 1)) + return -2; // We already used all one-time keys +- if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) ++ if ((params->full_height == 64) && (idx == ((1ULL << params->full_height) - 1))) + return -2; // We already used all one-time keys + } +- ++ + memcpy(sk_seed, sk+params->index_bytes, params->n); + memcpy(sk_prf, sk+params->index_bytes+params->n, params->n); + memcpy(pub_seed, sk+params->index_bytes+3*params->n, params->n); +@@ -867,31 +948,34 @@ int xmssmt_core_sign(const xmss_params *params, + + /* Already put the message in the right place, to make it easier to prepend + * things when computing the hash over the message. */ +- memcpy(sm + params->sig_bytes, m, mlen); ++ memset(m_with_prefix, 0, sizeof(m_with_prefix)); ++ memcpy(m_with_prefix + params->padding_len + 3*params->n, msg, msglen); ++ + + /* Compute the message hash. */ + hash_message(params, msg_h, R, pub_root, idx, +- sm + params->sig_bytes - params->padding_len - 3*params->n, +- mlen); ++ m_with_prefix, ++ /* sig + params->sig_bytes - params->padding_len - 3*params->n, */ ++ msglen); + + // Start collecting signature +- *smlen = 0; ++ *siglen = 0; + + // Copy index to signature + for (i = 0; i < params->index_bytes; i++) { +- sm[i] = (idx >> 8*(params->index_bytes - 1 - i)) & 255; ++ sig[i] = (idx >> 8*(params->index_bytes - 1 - i)) & 255; + } + +- sm += params->index_bytes; +- *smlen += params->index_bytes; ++ sig += params->index_bytes; ++ *siglen += params->index_bytes; + + // Copy R to signature + for (i = 0; i < params->n; i++) { +- sm[i] = R[i]; ++ sig[i] = R[i]; + } + +- sm += params->n; +- *smlen += params->n; ++ sig += params->n; ++ *siglen += params->n; + + // ---------------------------------- + // Now we start to "really sign" +@@ -908,27 +992,27 @@ int xmssmt_core_sign(const xmss_params *params, + set_ots_addr(ots_addr, idx_leaf); + + // Compute WOTS signature +- wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr); ++ wots_sign(params, sig, msg_h, sk_seed, pub_seed, ots_addr); + +- sm += params->wots_sig_bytes; +- *smlen += params->wots_sig_bytes; ++ sig += params->wots_sig_bytes; ++ *siglen += params->wots_sig_bytes; + +- memcpy(sm, states[0].auth, params->tree_height*params->n); +- sm += params->tree_height*params->n; +- *smlen += params->tree_height*params->n; ++ memcpy(sig, states[0].auth, params->tree_height*params->n); ++ sig += params->tree_height*params->n; ++ *siglen += params->tree_height*params->n; + + // prepare signature of remaining layers + for (i = 1; i < params->d; i++) { + // put WOTS signature in place +- memcpy(sm, wots_sigs + (i-1)*params->wots_sig_bytes, params->wots_sig_bytes); ++ memcpy(sig, wots_sigs + (i-1)*params->wots_sig_bytes, params->wots_sig_bytes); + +- sm += params->wots_sig_bytes; +- *smlen += params->wots_sig_bytes; ++ sig += params->wots_sig_bytes; ++ *siglen += params->wots_sig_bytes; + + // put AUTH nodes in place +- memcpy(sm, states[i].auth, params->tree_height*params->n); +- sm += params->tree_height*params->n; +- *smlen += params->tree_height*params->n; ++ memcpy(sig, states[i].auth, params->tree_height*params->n); ++ sig += params->tree_height*params->n; ++ *siglen += params->tree_height*params->n; + } + + updates = (params->tree_height - params->bds_k) >> 1; +@@ -944,7 +1028,7 @@ int xmssmt_core_sign(const xmss_params *params, + if (! (((idx + 1) & ((1ULL << ((i+1)*params->tree_height)) - 1)) == 0)) { + idx_leaf = (idx >> (params->tree_height * i)) & ((1 << params->tree_height)-1); + idx_tree = (idx >> (params->tree_height * (i+1))); +- set_layer_addr(addr, i); ++ set_layer_addr(addr, (uint32_t) i); + set_tree_addr(addr, idx_tree); + if (i == (unsigned int) (needswap_upto + 1)) { + bds_round(params, &states[i], idx_leaf, sk_seed, pub_seed, addr); +@@ -962,7 +1046,7 @@ int xmssmt_core_sign(const xmss_params *params, + else if (idx < (1ULL << params->full_height) - 1) { + deep_state_swap(params, states+params->d + i, states + i); + +- set_layer_addr(ots_addr, (i+1)); ++ set_layer_addr(ots_addr, (uint32_t) (i+1)); + set_tree_addr(ots_addr, ((idx + 1) >> ((i+2) * params->tree_height))); + set_ots_addr(ots_addr, (((idx >> ((i+1) * params->tree_height)) + 1) & ((1 << params->tree_height)-1))); + +@@ -972,17 +1056,15 @@ int xmssmt_core_sign(const xmss_params *params, + states[params->d + i].next_leaf = 0; + + updates--; // WOTS-signing counts as one update +- needswap_upto = i; ++ needswap_upto = (int) i; + for (j = 0; j < params->tree_height-params->bds_k; j++) { + states[i].treehash[j].completed = 1; + } + } + } + +- memcpy(sm, m, mlen); +- *smlen += mlen; +- + xmssmt_serialize_state(params, sk, states); + + return 0; + } ++#endif /* ifndef XMSS_VERIFY_ONLY */ +-- +2.40.0 + diff --git a/tools/xmss/xmss_common.h b/tools/xmss/xmss_common.h new file mode 100644 index 00000000..92bdf415 --- /dev/null +++ b/tools/xmss/xmss_common.h @@ -0,0 +1,146 @@ +/* xmss_common.h + * + * Common callback functions used by XMSS to write/read the private + * key file. + * + * + * Copyright (C) 2023 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef XMSS_COMMON_H +#define XMSS_COMMON_H +static enum wc_XmssRc xmss_write_key(const byte * priv, word32 privSz, void * context) +{ + FILE * file = NULL; + const char * filename = NULL; + int n_cmp = 0; + size_t n_read = 0; + size_t n_write = 0; + byte * buff = NULL; + int err = 0; + + if (priv == NULL || context == NULL || privSz == 0) { + fprintf(stderr, "error: invalid write args\n"); + return WC_XMSS_RC_BAD_ARG; + } + + filename = context; + + /* Open file for read and write. */ + file = fopen(filename, "r+"); + if (!file) { + /* Create the file if it didn't exist. */ + file = fopen(filename, "w+"); + if (!file) { + fprintf(stderr, "error: fopen(%s, \"w+\") failed: %d\n", filename, + ferror(file)); + return WC_XMSS_RC_WRITE_FAIL; + } + } + + n_write = fwrite(priv, 1, privSz, file); + + if (n_write != privSz) { + fprintf(stderr, "error: wrote %zu, expected %d: %d\n", n_write, privSz, + ferror(file)); + return WC_XMSS_RC_WRITE_FAIL; + } + + err = fclose(file); + if (err) { + fprintf(stderr, "error: fclose returned %d\n", err); + return WC_XMSS_RC_WRITE_FAIL; + } + + /* Verify private key data has actually been written to persistent + * storage correctly. */ + file = fopen(filename, "r+"); + if (!file) { + fprintf(stderr, "error: fopen(%s, \"r+\") failed: %d\n", filename, + ferror(file)); + return WC_XMSS_RC_WRITE_FAIL; + } + + buff = malloc(privSz); + if (buff == NULL) { + fprintf(stderr, "error: malloc(%d) failed\n", privSz); + return WC_XMSS_RC_WRITE_FAIL; + } + + XMEMSET(buff, 0, n_write); + + n_read = fread(buff, 1, n_write, file); + + if (n_read != n_write) { + fprintf(stderr, "error: read %zu, expected %zu: %d\n", n_read, n_write, + ferror(file)); + free(buff); + return WC_XMSS_RC_WRITE_FAIL; + } + + n_cmp = XMEMCMP(buff, priv, n_write); + free(buff); + buff = NULL; + + if (n_cmp != 0) { + fprintf(stderr, "error: write data was corrupted: %d\n", n_cmp); + return WC_XMSS_RC_WRITE_FAIL; + } + + err = fclose(file); + if (err) { + fprintf(stderr, "error: fclose returned %d\n", err); + return WC_XMSS_RC_WRITE_FAIL; + } + + return WC_XMSS_RC_SAVED_TO_NV_MEMORY; +} + +static enum wc_XmssRc xmss_read_key(byte * priv, word32 privSz, void * context) +{ + FILE * file = NULL; + const char * filename = NULL; + size_t n_read = 0; + + if (priv == NULL || context == NULL || privSz == 0) { + fprintf(stderr, "error: invalid read args\n"); + return WC_XMSS_RC_BAD_ARG; + } + + filename = context; + + file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "error: fopen(%s, \"rb\") failed\n", filename); + return WC_XMSS_RC_READ_FAIL; + } + + n_read = fread(priv, 1, privSz, file); + + if (n_read != privSz) { + fprintf(stderr, "error: read %zu, expected %d: %d\n", n_read, privSz, + ferror(file)); + return WC_XMSS_RC_READ_FAIL; + } + + fclose(file); + + return WC_XMSS_RC_READ_TO_MEMORY; +} +#endif /* XMSS_COMMON_H */ diff --git a/tools/xmss/xmss_siglen.sh b/tools/xmss/xmss_siglen.sh new file mode 100755 index 00000000..59c35198 --- /dev/null +++ b/tools/xmss/xmss_siglen.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# +# Prints the XMSS/XMSS^MT signature length given a parameter set string. +# +# References: +# - https://www.rfc-editor.org/rfc/rfc8391.html +# - https://eprint.iacr.org/2017/349.pdf + +print_usage_and_exit() { + echo "usage:" + echo " ./xmss_siglen.sh " + echo "" + echo "examples:" + echo " ./xmss_siglen.sh XMSS-SHA2_10_256" + echo " ./xmss_siglen.sh XMSS-SHA2_16_256" + echo " ./xmss_siglen.sh XMSS-SHA2_20_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_20/2_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_20/4_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_40/2_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_40/4_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_40/8_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_60/3_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_60/6_256" + echo " ./xmss_siglen.sh XMSSMT-SHA2_60/12_256" + exit 1 +} + +if [ $# -ne 1 ]; then + print_usage_and_exit +fi + +param=$1 +len=${#param} + +n=32 +p=67 +h=0 +d=1 + +if [[ "$len" != @("16"|"20"|"21") ]]; then + echo "error: invalid len: $len" + exit 1 +fi + +if [[ $len == 16 ]]; then + # Must be one of these three: + # - "XMSS-SHA2_10_256" + # - "XMSS-SHA2_16_256" + # - "XMSS-SHA2_20_256" + if [[ $param =~ "XMSS-SHA2_" && $param =~ "_256" ]]; then + h=$(echo $param | cut -c 11-12) + else + echo "error: invalid param string: $param" + exit 1 + fi + + # In XMSS the signature length is: + # siglen = 4 + n(p + h + 1) + siglen=$((4 + n * (p + h + 1))) +else + # Must be one of these 8: + # - "XMSSMT-SHA2_20/2_256" + # - "XMSSMT-SHA2_20/4_256" + # - "XMSSMT-SHA2_40/2_256" + # - "XMSSMT-SHA2_40/4_256" + # - "XMSSMT-SHA2_40/8_256" + # - "XMSSMT-SHA2_60/3_256" + # - "XMSSMT-SHA2_60/6_256" + # - "XMSSMT-SHA2_60/12_256" + # + if [[ $param =~ "XMSSMT-SHA2_" && $param =~ "_256" ]]; then + h=$(echo $param | cut -c 13-14) + else + echo "error: invalid param string: $param" + exit 1 + fi + + if [[ $len == 20 ]]; then + d=$(echo $param | cut -c 16-16) + else + d=$(echo $param | cut -c 16-17) + fi + + # In XMSS^MT the signature length is: + # siglen = Sum[h]/8 + n (Sum[p] + Sum[h] + 1) + # + # Where Sum[h] means the total height of the hyper-tree. + # Note: The Sum[h]/8 must be rounded up. + siglen=$((((h + 7) / 8) + n * ((d * p) + h + 1))) +fi + +echo "parameter set: $param" +echo "signature length: $siglen"