From b05c538da58d98b8052df1bf53134e38b3596954 Mon Sep 17 00:00:00 2001 From: sfluhrer Date: Thu, 15 Apr 2021 09:46:25 -0400 Subject: [PATCH 1/5] Add support for SHAKE --- Makefile | 19 +- common_defs.h | 18 ++ demo.c | 67 +++- fips202.c | 762 ++++++++++++++++++++++++++++++++++++++++++++++ fips202.h | 50 +++ hash.c | 72 +++-- hash.h | 4 + hss.c | 3 +- hss.h | 14 +- hss_internal.h | 6 +- hss_param.c | 104 +++++-- lm_common.c | 20 ++ lm_ots_common.c | 16 + programming.notes | 35 ++- test_hss.c | 1 + test_hss.h | 1 + test_keygen.c | 114 +++++-- test_load.c | 10 + test_shake.c | 184 +++++++++++ test_sign.c | 3 +- test_sign_inc.c | 36 +++ test_testvector.c | 325 +++++++++++++++++++- test_verify.c | 40 ++- test_verify_inc.c | 21 ++ 24 files changed, 1789 insertions(+), 136 deletions(-) create mode 100644 fips202.c create mode 100644 fips202.h create mode 100644 test_shake.c diff --git a/Makefile b/Makefile index 3857188..4fa7cfc 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ hss_lib.a: hss.o hss_alloc.o hss_aux.o hss_common.o \ hss_verify.o hss_verify_inc.o hss_derive.o \ hss_derive.o hss_zeroize.o lm_common.o \ lm_ots_common.o lm_ots_sign.o lm_ots_verify.o lm_verify.o endian.o \ - hash.o sha256.o + hash.o sha256.o fips202.o $(AR) rcs $@ $^ hss_lib_thread.a: hss.o hss_alloc.o hss_aux.o hss_common.o \ @@ -23,22 +23,22 @@ hss_lib_thread.a: hss.o hss_alloc.o hss_aux.o hss_common.o \ hss_verify.o hss_verify_inc.o \ hss_derive.o hss_zeroize.o lm_common.o \ lm_ots_common.o lm_ots_sign.o lm_ots_verify.o lm_verify.o endian.o \ - hash.o sha256.o + hash.o sha256.o fips202.o $(AR) rcs $@ $^ hss_verify.a: hss_verify.o hss_verify_inc.o hss_common.o hss_thread_single.o \ hss_zeroize.o lm_common.o lm_ots_common.o lm_ots_verify.o lm_verify.o \ - endian.o hash.o sha256.o + endian.o hash.o sha256.o fips202.o $(AR) rcs $@ $^ demo: demo.c hss_lib_thread.a $(CC) $(CFLAGS) demo.c hss_lib_thread.a -lcrypto -lpthread -o demo -test_1: test_1.c lm_ots_common.o lm_ots_sign.o lm_ots_verify.o endian.o hash.o sha256.o hss_zeroize.o - $(CC) $(CFLAGS) -o test_1 test_1.c lm_ots_common.o lm_ots_sign.o lm_ots_verify.o endian.o hash.o sha256.o hss_zeroize.o -lcrypto +test_1: test_1.c lm_ots_common.o lm_ots_sign.o lm_ots_verify.o endian.o hash.o sha256.o h fips202.o ss_zeroize.o + $(CC) $(CFLAGS) -o test_1 test_1.c lm_ots_common.o lm_ots_sign.o lm_ots_verify.o endian.o hash.o sha256.o fips202.o hss_zeroize.o -lcrypto -test_hss: test_hss.c test_hss.h test_testvector.c test_stat.c test_keygen.c test_load.c test_sign.c test_sign_inc.c test_verify.c test_verify_inc.c test_keyload.c test_reserve.c test_thread.c test_h25.c hss.h hss_lib_thread.a - $(CC) $(CFLAGS) test_hss.c test_testvector.c test_stat.c test_keygen.c test_sign.c test_sign_inc.c test_load.c test_verify.c test_verify_inc.c test_keyload.c test_reserve.c test_thread.c test_h25.c hss_lib_thread.a -lcrypto -lpthread -o test_hss +test_hss: test_hss.c test_hss.h test_testvector.c test_shake.c test_stat.c test_keygen.c test_load.c test_sign.c test_sign_inc.c test_verify.c test_verify_inc.c test_keyload.c test_reserve.c test_thread.c test_h25.c hss.h hss_lib_thread.a + $(CC) $(CFLAGS) test_hss.c test_testvector.c test_shake.c test_stat.c test_keygen.c test_sign.c test_sign_inc.c test_load.c test_verify.c test_verify_inc.c test_keyload.c test_reserve.c test_thread.c test_h25.c hss_lib_thread.a -lcrypto -lpthread -o test_hss hss.o: hss.c hss.h common_defs.h hash.h endian.h hss_internal.h hss_aux.h hss_derive.h config.h $(CC) $(CFLAGS) -c hss.c -o $@ @@ -109,12 +109,15 @@ lm_verify.o: lm_verify.c lm_verify.h lm_common.h lm_ots_common.h lm_ots_verify.h endian.o: endian.c endian.h config.h $(CC) $(CFLAGS) -c endian.c -o $@ -hash.o: hash.c hash.h sha256.h hss_zeroize.h config.h +hash.o: hash.c hash.h sha256.h fips202.h hss_zeroize.h config.h $(CC) $(CFLAGS) -c hash.c -o $@ sha256.o: sha256.c sha256.h endian.h config.h $(CC) $(CFLAGS) -c sha256.c -o $@ +fips202.o: fips202.c fips202.h + $(CC) $(CFLAGS) -c fips202.c -o $@ + clean: -rm *.o *.a demo test_hss diff --git a/common_defs.h b/common_defs.h index 0581027..80a99b0 100644 --- a/common_defs.h +++ b/common_defs.h @@ -65,6 +65,16 @@ typedef uint_fast64_t sequence_t; #define LMS_SHA256_N24_H15 0xe0000003 #define LMS_SHA256_N24_H20 0xe0000004 #define LMS_SHA256_N24_H25 0xe0000005 +#define LMS_SHAKE256_N32_H5 0xe0000006 +#define LMS_SHAKE256_N32_H10 0xe0000007 +#define LMS_SHAKE256_N32_H15 0xe0000008 +#define LMS_SHAKE256_N32_H20 0xe0000009 +#define LMS_SHAKE256_N32_H25 0xe000000a +#define LMS_SHAKE256_N24_H5 0xe000000b +#define LMS_SHAKE256_N24_H10 0xe000000c +#define LMS_SHAKE256_N24_H15 0xe000000d +#define LMS_SHAKE256_N24_H20 0xe000000e +#define LMS_SHAKE256_N24_H25 0xe000000f /* LM-OTS registry */ @@ -76,6 +86,14 @@ typedef uint_fast64_t sequence_t; #define LMOTS_SHA256_N24_W2 0xe0000002 #define LMOTS_SHA256_N24_W4 0xe0000003 #define LMOTS_SHA256_N24_W8 0xe0000004 +#define LMOTS_SHAKE256_N32_W1 0xe0000005 +#define LMOTS_SHAKE256_N32_W2 0xe0000006 +#define LMOTS_SHAKE256_N32_W4 0xe0000007 +#define LMOTS_SHAKE256_N32_W8 0xe0000008 +#define LMOTS_SHAKE256_N24_W1 0xe0000009 +#define LMOTS_SHAKE256_N24_W2 0xe000000a +#define LMOTS_SHAKE256_N24_W4 0xe000000b +#define LMOTS_SHAKE256_N24_W8 0xe000000c /* diff --git a/demo.c b/demo.c index d94d75a..f7318cb 100644 --- a/demo.c +++ b/demo.c @@ -808,6 +808,7 @@ static int parse_parm_set( int *levels, param_set_t *lm_array, size_t aux = DEFAULT_AUX_DATA; int hash_type = 0; /* 1 -> 192 bit SHA256, 0 -> 256 bit SHA256 */ + /* 3 -> 192 bit SHAKE256, 2 -> 256 bit SHAKE256 */ /* Get the hash function. Now, HSS doesn't require us to use the same */ /* hash function everywhere, however allowing different hash functions */ @@ -815,12 +816,16 @@ static int parse_parm_set( int *levels, param_set_t *lm_array, /* specific reason to actually use it */ if (check_string( &parm_set, "SHA192," )) { hash_type = 1; + } else if (check_string( &parm_set, "SHAKE192," )) { + hash_type = 3; + } else if (check_string( &parm_set, "SHAKE256," )) { + hash_type = 2; } else { /* Remove the initial SHA256, string, if present */ (void)check_string( &parm_set, "SHA256," ); hash_type = 0; } - *upper_hash_size = pick( hash_type, 32, 24 ); + *upper_hash_size = pick( hash_type, 32, 24, 32, 24 ); for (i=0;; i++) { if (i == 8) { @@ -832,19 +837,29 @@ static int parse_parm_set( int *levels, param_set_t *lm_array, param_set_t lm; switch (h) { case 5: lm = pick( hash_type, LMS_SHA256_N32_H5, - LMS_SHA256_N24_H5 ); + LMS_SHA256_N24_H5, + LMS_SHAKE256_N32_H5, + LMS_SHAKE256_N24_H5 ); break; case 10: lm = pick( hash_type, LMS_SHA256_N32_H10, - LMS_SHA256_N24_H10 ); + LMS_SHA256_N24_H10, + LMS_SHAKE256_N32_H10, + LMS_SHAKE256_N24_H10 ); break; case 15: lm = pick( hash_type, LMS_SHA256_N32_H15, - LMS_SHA256_N24_H15 ); + LMS_SHA256_N24_H15, + LMS_SHAKE256_N32_H15, + LMS_SHAKE256_N24_H15 ); break; case 20: lm = pick( hash_type, LMS_SHA256_N32_H20, - LMS_SHA256_N24_H20 ); + LMS_SHA256_N24_H20, + LMS_SHAKE256_N32_H20, + LMS_SHAKE256_N24_H20 ); break; case 25: lm = pick( hash_type, LMS_SHA256_N32_H25, - LMS_SHA256_N24_H25 ); + LMS_SHA256_N24_H25, + LMS_SHAKE256_N32_H25, + LMS_SHAKE256_N24_H25 ); break; case 0: printf( "Error: expected height of Merkle tree\n" ); return 0; default: printf( "Error: unsupported Merkle tree height %d\n", h ); @@ -853,22 +868,32 @@ static int parse_parm_set( int *levels, param_set_t *lm_array, } /* Now see if we can get the Winternitz parameter */ param_set_t ots = pick( hash_type, LMOTS_SHA256_N32_W8, - LMOTS_SHA256_N24_W8 ); + LMOTS_SHA256_N24_W8, + LMOTS_SHAKE256_N32_W8, + LMOTS_SHAKE256_N24_W8 ); if (*parm_set == '/') { parm_set++; int w = get_integer( &parm_set ); switch (w) { case 1: ots = pick( hash_type, LMOTS_SHA256_N32_W1, - LMOTS_SHA256_N24_W1 ); + LMOTS_SHA256_N24_W1, + LMOTS_SHAKE256_N32_W1, + LMOTS_SHAKE256_N24_W1 ); break; case 2: ots = pick( hash_type, LMOTS_SHA256_N32_W2, - LMOTS_SHA256_N24_W2 ); + LMOTS_SHA256_N24_W2, + LMOTS_SHAKE256_N32_W2, + LMOTS_SHAKE256_N24_W2 ); break; case 4: ots = pick( hash_type, LMOTS_SHA256_N32_W4, - LMOTS_SHA256_N24_W4 ); + LMOTS_SHA256_N24_W4, + LMOTS_SHAKE256_N32_W4, + LMOTS_SHAKE256_N24_W4 ); break; case 8: ots = pick( hash_type, LMOTS_SHA256_N32_W8, - LMOTS_SHA256_N24_W8 ); + LMOTS_SHA256_N24_W8, + LMOTS_SHAKE256_N32_W8, + LMOTS_SHAKE256_N24_W8 ); break; case 0: printf( "Error: expected Winternitz parameter\n" ); return 0; default: printf( "Error: unsupported Winternitz parameter %d\n", w ); @@ -899,6 +924,8 @@ static const char *hash_name(int hash_type) { switch (hash_type) { case 0: return "SHA-256/192"; case 1: return "SHA-256"; + case 2: return "SHAKE-256/192"; + case 3: return "SHAKE-256"; default: return "???"; } } @@ -921,6 +948,16 @@ static void list_parameter_set(int levels, const param_set_t *lm_array, case LMS_SHA256_N24_H15: h = 15; hash = 0; break; case LMS_SHA256_N24_H20: h = 20; hash = 0; break; case LMS_SHA256_N24_H25: h = 25; hash = 0; break; + case LMS_SHAKE256_N32_H5: h = 5; hash = 3; break; + case LMS_SHAKE256_N32_H10: h = 10; hash = 3; break; + case LMS_SHAKE256_N32_H15: h = 15; hash = 3; break; + case LMS_SHAKE256_N32_H20: h = 20; hash = 3; break; + case LMS_SHAKE256_N32_H25: h = 25; hash = 3; break; + case LMS_SHAKE256_N24_H5: h = 5; hash = 2; break; + case LMS_SHAKE256_N24_H10: h = 10; hash = 2; break; + case LMS_SHAKE256_N24_H15: h = 15; hash = 2; break; + case LMS_SHAKE256_N24_H20: h = 20; hash = 2; break; + case LMS_SHAKE256_N24_H25: h = 25; hash = 2; break; } printf( "Level %d: hash function = %s; ", i, hash_name(hash) ); printf( "%d level Merkle tree; ", h ); @@ -935,6 +972,14 @@ static void list_parameter_set(int levels, const param_set_t *lm_array, case LMOTS_SHA256_N24_W2: w = 2; hash2 = 0; break; case LMOTS_SHA256_N24_W4: w = 4; hash2 = 0; break; case LMOTS_SHA256_N24_W8: w = 8; hash2 = 0; break; + case LMOTS_SHAKE256_N32_W1: w = 1; hash2 = 3; break; + case LMOTS_SHAKE256_N32_W2: w = 2; hash2 = 3; break; + case LMOTS_SHAKE256_N32_W4: w = 4; hash2 = 3; break; + case LMOTS_SHAKE256_N32_W8: w = 8; hash2 = 3; break; + case LMOTS_SHAKE256_N24_W1: w = 1; hash2 = 2; break; + case LMOTS_SHAKE256_N24_W2: w = 2; hash2 = 2; break; + case LMOTS_SHAKE256_N24_W4: w = 4; hash2 = 2; break; + case LMOTS_SHAKE256_N24_W8: w = 8; hash2 = 2; break; } printf( "Winternitz param %d\n", w ); if (hash != hash2) { diff --git a/fips202.c b/fips202.c new file mode 100644 index 0000000..ceeb6a5 --- /dev/null +++ b/fips202.c @@ -0,0 +1,762 @@ +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include +#include + +#include "fips202.h" + +#define NROUNDS 24 +#define ROL(a, offset) (((a) << (offset)) ^ ((a) >> (64 - (offset)))) + +/************************************************* + * Name: load64 + * + * Description: Load 8 bytes into uint64_t in little-endian order + * + * Arguments: - const uint8_t *x: pointer to input byte array + * + * Returns the loaded 64-bit unsigned integer + **************************************************/ +static uint64_t load64(const uint8_t *x) { + uint64_t r = 0; + for (size_t i = 0; i < 8; ++i) { + r |= (uint64_t)x[i] << 8 * i; + } + + return r; +} + +/************************************************* + * Name: store64 + * + * Description: Store a 64-bit integer to a byte array in little-endian order + * + * Arguments: - uint8_t *x: pointer to the output byte array + * - uint64_t u: input 64-bit unsigned integer + **************************************************/ +static void store64(uint8_t *x, uint64_t u) { + for (size_t i = 0; i < 8; ++i) { + x[i] = (uint8_t) (u >> 8 * i); + } +} + +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808aULL, 0x8000000080008000ULL, + 0x000000000000808bULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008aULL, 0x0000000000000088ULL, + 0x0000000080008009ULL, 0x000000008000000aULL, + 0x000000008000808bULL, 0x800000000000008bULL, + 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, + 0x000000000000800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, + 0x0000000080000001ULL, 0x8000000080008008ULL +}; + +/************************************************* + * Name: KeccakF1600_StatePermute + * + * Description: The Keccak F1600 Permutation + * + * Arguments: - uint64_t *state: pointer to input/output Keccak state + **************************************************/ +static void KeccakF1600_StatePermute(uint64_t *state) { + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + // copyFromState(A, state) + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < NROUNDS; round += 2) { + // prepareTheta + BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase; + BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + // thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^ ((~BCe) & BCi); + Eba ^= KeccakF_RoundConstants[round]; + Ebe = BCe ^ ((~BCi) & BCo); + Ebi = BCi ^ ((~BCo) & BCu); + Ebo = BCo ^ ((~BCu) & BCa); + Ebu = BCu ^ ((~BCa) & BCe); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^ ((~BCe) & BCi); + Ege = BCe ^ ((~BCi) & BCo); + Egi = BCi ^ ((~BCo) & BCu); + Ego = BCo ^ ((~BCu) & BCa); + Egu = BCu ^ ((~BCa) & BCe); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^ ((~BCe) & BCi); + Eke = BCe ^ ((~BCi) & BCo); + Eki = BCi ^ ((~BCo) & BCu); + Eko = BCo ^ ((~BCu) & BCa); + Eku = BCu ^ ((~BCa) & BCe); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^ ((~BCe) & BCi); + Eme = BCe ^ ((~BCi) & BCo); + Emi = BCi ^ ((~BCo) & BCu); + Emo = BCo ^ ((~BCu) & BCa); + Emu = BCu ^ ((~BCa) & BCe); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^ ((~BCe) & BCi); + Ese = BCe ^ ((~BCi) & BCo); + Esi = BCi ^ ((~BCo) & BCu); + Eso = BCo ^ ((~BCu) & BCa); + Esu = BCu ^ ((~BCa) & BCe); + + // prepareTheta + BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + // thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^ ((~BCe) & BCi); + Aba ^= KeccakF_RoundConstants[round + 1]; + Abe = BCe ^ ((~BCi) & BCo); + Abi = BCi ^ ((~BCo) & BCu); + Abo = BCo ^ ((~BCu) & BCa); + Abu = BCu ^ ((~BCa) & BCe); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^ ((~BCe) & BCi); + Age = BCe ^ ((~BCi) & BCo); + Agi = BCi ^ ((~BCo) & BCu); + Ago = BCo ^ ((~BCu) & BCa); + Agu = BCu ^ ((~BCa) & BCe); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^ ((~BCe) & BCi); + Ake = BCe ^ ((~BCi) & BCo); + Aki = BCi ^ ((~BCo) & BCu); + Ako = BCo ^ ((~BCu) & BCa); + Aku = BCu ^ ((~BCa) & BCe); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^ ((~BCe) & BCi); + Ame = BCe ^ ((~BCi) & BCo); + Ami = BCi ^ ((~BCo) & BCu); + Amo = BCo ^ ((~BCu) & BCa); + Amu = BCu ^ ((~BCa) & BCe); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^ ((~BCe) & BCi); + Ase = BCe ^ ((~BCi) & BCo); + Asi = BCi ^ ((~BCo) & BCu); + Aso = BCo ^ ((~BCu) & BCa); + Asu = BCu ^ ((~BCa) & BCe); + } + + // copyToState(state, A) + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +/************************************************* + * Name: keccak_absorb + * + * Description: Absorb step of Keccak; + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *m: pointer to input to be absorbed into s + * - size_t mlen: length of input in bytes + * - uint8_t p: domain-separation byte for different + * Keccak-derived functions + **************************************************/ +static void keccak_absorb(uint64_t *s, uint32_t r, const uint8_t *m, + size_t mlen, uint8_t p) { + size_t i; + uint8_t t[200]; + + /* Zero state */ + for (i = 0; i < 25; ++i) { + s[i] = 0; + } + + while (mlen >= r) { + for (i = 0; i < r / 8; ++i) { + s[i] ^= load64(m + 8 * i); + } + + KeccakF1600_StatePermute(s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r / 8; ++i) { + s[i] ^= load64(t + 8 * i); + } +} + +/************************************************* + * Name: keccak_squeezeblocks + * + * Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. + * Modifies the state. Can be called multiple times to keep + * squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *h: pointer to output blocks + * - size_t nblocks: number of blocks to be + * squeezed (written to h) + * - uint64_t *s: pointer to input/output Keccak state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_squeezeblocks(uint8_t *h, size_t nblocks, + uint64_t *s, uint32_t r) { + while (nblocks > 0) { + KeccakF1600_StatePermute(s); + for (size_t i = 0; i < (r >> 3); i++) { + store64(h + 8 * i, s[i]); + } + h += r; + nblocks--; + } +} + +/************************************************* + * Name: keccak_inc_init + * + * Description: Initializes the incremental Keccak state to zero. + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + **************************************************/ +static void keccak_inc_init(uint64_t *s_inc) { + size_t i; + + for (i = 0; i < 25; ++i) { + s_inc[i] = 0; + } + s_inc[25] = 0; +} + +/************************************************* + * Name: keccak_inc_absorb + * + * Description: Incremental keccak absorb + * Preceded by keccak_inc_init, succeeded by keccak_inc_finalize + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *m: pointer to input to be absorbed into s + * - size_t mlen: length of input in bytes + **************************************************/ +static void keccak_inc_absorb(uint64_t *s_inc, uint32_t r, const uint8_t *m, + size_t mlen) { + size_t i; + + /* Recall that s_inc[25] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[25] >= r) { + for (i = 0; i < r - s_inc[25]; i++) { + /* Take the i'th byte from message + xor with the s_inc[25] + i'th byte of the state; little-endian */ + s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07)); + } + mlen -= (size_t)(r - s_inc[25]); + m += r - s_inc[25]; + s_inc[25] = 0; + + KeccakF1600_StatePermute(s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07)); + } + s_inc[25] += mlen; +} + +/************************************************* + * Name: keccak_inc_finalize + * + * Description: Finalizes Keccak absorb phase, prepares for squeezing + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - uint8_t p: domain-separation byte for different + * Keccak-derived functions + **************************************************/ +static void keccak_inc_finalize(uint64_t *s_inc, uint32_t r, uint8_t p) { + /* After keccak_inc_absorb, we are guaranteed that s_inc[25] < r, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[25] >> 3] ^= (uint64_t)p << (8 * (s_inc[25] & 0x07)); + s_inc[(r - 1) >> 3] ^= (uint64_t)128 << (8 * ((r - 1) & 0x07)); + s_inc[25] = 0; +} + +/************************************************* + * Name: keccak_inc_squeeze + * + * Description: Incremental Keccak squeeze; can be called on byte-level + * + * Arguments: - uint8_t *h: pointer to output bytes + * - size_t outlen: number of bytes to be squeezed + * - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_inc_squeeze(uint8_t *h, size_t outlen, + uint64_t *s_inc, uint32_t r) { + size_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[25]; i++) { + /* There are s_inc[25] bytes left, so r - s_inc[25] is the first + available byte. We consume from there, i.e., up to r. */ + h[i] = (uint8_t)(s_inc[(r - s_inc[25] + i) >> 3] >> (8 * ((r - s_inc[25] + i) & 0x07))); + } + h += i; + outlen -= i; + s_inc[25] -= i; + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + KeccakF1600_StatePermute(s_inc); + + for (i = 0; i < outlen && i < r; i++) { + h[i] = (uint8_t)(s_inc[i >> 3] >> (8 * (i & 0x07))); + } + h += i; + outlen -= i; + s_inc[25] = r - i; + } +} + +void shake128_inc_init(uint64_t *s_inc) { + keccak_inc_init(s_inc); +} + +void shake128_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(s_inc, SHAKE128_RATE, input, inlen); +} + +void shake128_inc_finalize(uint64_t *s_inc) { + keccak_inc_finalize(s_inc, SHAKE128_RATE, 0x1F); +} + +void shake128_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) { + keccak_inc_squeeze(output, outlen, s_inc, SHAKE128_RATE); +} + +void shake256_inc_init(uint64_t *s_inc) { + keccak_inc_init(s_inc); +} + +void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(s_inc, SHAKE256_RATE, input, inlen); +} + +void shake256_inc_finalize(uint64_t *s_inc) { + keccak_inc_finalize(s_inc, SHAKE256_RATE, 0x1F); +} + +void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc) { + keccak_inc_squeeze(output, outlen, s_inc, SHAKE256_RATE); +} + + +/************************************************* + * Name: shake128_absorb + * + * Description: Absorb step of the SHAKE128 XOF. + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - const uint8_t *input: pointer to input to be absorbed + * into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { + keccak_absorb(s, SHAKE128_RATE, input, inlen, 0x1F); +} + +/************************************************* + * Name: shake128_squeezeblocks + * + * Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of + * SHAKE128_RATE bytes each. Modifies the state. Can be called + * multiple times to keep squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *output: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed + * (written to output) + * - uint64_t *s: pointer to input/output Keccak state + **************************************************/ +void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) { + keccak_squeezeblocks(output, nblocks, s, SHAKE128_RATE); +} + +/************************************************* + * Name: shake256_absorb + * + * Description: Absorb step of the SHAKE256 XOF. + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - const uint8_t *input: pointer to input to be absorbed + * into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen) { + keccak_absorb(s, SHAKE256_RATE, input, inlen, 0x1F); +} + +/************************************************* + * Name: shake256_squeezeblocks + * + * Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of + * SHAKE256_RATE bytes each. Modifies the state. Can be called + * multiple times to keep squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *output: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed + * (written to output) + * - uint64_t *s: pointer to input/output Keccak state + **************************************************/ +void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s) { + keccak_squeezeblocks(output, nblocks, s, SHAKE256_RATE); +} + +/************************************************* + * Name: shake128 + * + * Description: SHAKE128 XOF with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen) { + size_t nblocks = outlen / SHAKE128_RATE; + uint8_t t[SHAKE128_RATE]; + uint64_t s[25]; + + shake128_absorb(s, input, inlen); + shake128_squeezeblocks(output, nblocks, s); + + output += nblocks * SHAKE128_RATE; + outlen -= nblocks * SHAKE128_RATE; + + if (outlen) { + shake128_squeezeblocks(t, 1, s); + for (size_t i = 0; i < outlen; ++i) { + output[i] = t[i]; + } + } +} + +/************************************************* + * Name: shake256 + * + * Description: SHAKE256 XOF with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen) { + size_t nblocks = outlen / SHAKE256_RATE; + uint8_t t[SHAKE256_RATE]; + uint64_t s[25]; + + shake256_absorb(s, input, inlen); + shake256_squeezeblocks(output, nblocks, s); + + output += nblocks * SHAKE256_RATE; + outlen -= nblocks * SHAKE256_RATE; + + if (outlen) { + shake256_squeezeblocks(t, 1, s); + for (size_t i = 0; i < outlen; ++i) { + output[i] = t[i]; + } + } +} + +void sha3_256_inc_init(uint64_t *s_inc) { + keccak_inc_init(s_inc); +} + +void sha3_256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(s_inc, SHA3_256_RATE, input, inlen); +} + +void sha3_256_inc_finalize(uint8_t *output, uint64_t *s_inc) { + uint8_t t[SHA3_256_RATE]; + keccak_inc_finalize(s_inc, SHA3_256_RATE, 0x06); + + keccak_squeezeblocks(t, 1, s_inc, SHA3_256_RATE); + + for (size_t i = 0; i < 32; i++) { + output[i] = t[i]; + } +} + +/************************************************* + * Name: sha3_256 + * + * Description: SHA3-256 with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen) { + uint64_t s[25]; + uint8_t t[SHA3_256_RATE]; + + /* Absorb input */ + keccak_absorb(s, SHA3_256_RATE, input, inlen, 0x06); + + /* Squeeze output */ + keccak_squeezeblocks(t, 1, s, SHA3_256_RATE); + + for (size_t i = 0; i < 32; i++) { + output[i] = t[i]; + } +} + +void sha3_512_inc_init(uint64_t *s_inc) { + keccak_inc_init(s_inc); +} + +void sha3_512_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(s_inc, SHA3_512_RATE, input, inlen); +} + +void sha3_512_inc_finalize(uint8_t *output, uint64_t *s_inc) { + uint8_t t[SHA3_512_RATE]; + keccak_inc_finalize(s_inc, SHA3_512_RATE, 0x06); + + keccak_squeezeblocks(t, 1, s_inc, SHA3_512_RATE); + + for (size_t i = 0; i < 32; i++) { + output[i] = t[i]; + } +} + +/************************************************* + * Name: sha3_512 + * + * Description: SHA3-512 with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen) { + uint64_t s[25]; + uint8_t t[SHA3_512_RATE]; + + /* Absorb input */ + keccak_absorb(s, SHA3_512_RATE, input, inlen, 0x06); + + /* Squeeze output */ + keccak_squeezeblocks(t, 1, s, SHA3_512_RATE); + + for (size_t i = 0; i < 64; i++) { + output[i] = t[i]; + } +} diff --git a/fips202.h b/fips202.h new file mode 100644 index 0000000..0aca0a1 --- /dev/null +++ b/fips202.h @@ -0,0 +1,50 @@ +#ifndef FIPS202_H_ +#define FIPS202_H_ + +#include +#include + +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 +#define SHA3_256_RATE 136 +#define SHA3_512_RATE 72 + +typedef uint64_t keccak_state[26]; /* index 25 is used to store the */ + /* current offset */ + +void shake128_absorb(uint64_t *s, const uint8_t *input, size_t inlen); + +void shake128_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s); + +void shake128_inc_init(uint64_t *s_inc); +void shake128_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); +void shake128_inc_finalize(uint64_t *s_inc); +void shake128_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc); + +void shake256_absorb(uint64_t *s, const uint8_t *input, size_t inlen); +void shake256_squeezeblocks(uint8_t *output, size_t nblocks, uint64_t *s); + +void shake256_inc_init(uint64_t *s_inc); +void shake256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); +void shake256_inc_finalize(uint64_t *s_inc); +void shake256_inc_squeeze(uint8_t *output, size_t outlen, uint64_t *s_inc); + +void shake128(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen); + +void shake256(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen); + +void sha3_256_inc_init(uint64_t *s_inc); +void sha3_256_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); +void sha3_256_inc_finalize(uint8_t *output, uint64_t *s_inc); + +void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen); + +void sha3_512_inc_init(uint64_t *s_inc); +void sha3_512_inc_absorb(uint64_t *s_inc, const uint8_t *input, size_t inlen); +void sha3_512_inc_finalize(uint8_t *output, uint64_t *s_inc); + +void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen); + +#endif /* FIPS202_H_ */ diff --git a/hash.c b/hash.c index fcf23c1..c242aca 100644 --- a/hash.c +++ b/hash.c @@ -1,6 +1,7 @@ #include #include "hash.h" #include "sha256.h" +#include "fips202.h" #include "hss_zeroize.h" #define ALLOW_VERBOSE 0 /* 1 -> we allow the dumping of intermediate */ @@ -37,17 +38,13 @@ void hss_hash_ctx(void *result, int hash_type, union hash_context *ctx, } #endif + int output_len; switch (hash_type) { case HASH_SHA256: { SHA256_Init(&ctx->sha256); SHA256_Update(&ctx->sha256, message, message_len); SHA256_Final(result, &ctx->sha256); -#if ALLOW_VERBOSE - if (hss_verbose) { - printf( " ->" ); - int i; for (i=0; i<32; i++) printf( " %02x", ((unsigned char *)result)[i] ); printf( "\n" ); - } -#endif + output_len = 32; break; } case HASH_SHA256_24: { @@ -57,15 +54,23 @@ void hss_hash_ctx(void *result, int hash_type, union hash_context *ctx, SHA256_Final(temp, &ctx->sha256); memcpy(result, temp, 24 ); hss_zeroize(temp, sizeof temp); -#if ALLOW_VERBOSE - if (hss_verbose) { - printf( " ->" ); - int i; for (i=0; i<24; i++) printf( " %02x", ((unsigned char *)result)[i] ); printf( "\n" ); - } -#endif + output_len = 24; break; } + case HASH_SHAKE256: case HASH_SHAKE256_24: + shake256_inc_init(ctx->shake256); + shake256_inc_absorb(ctx->shake256, message, message_len); + shake256_inc_finalize(ctx->shake256); + output_len = hss_hash_length(hash_type); + shake256_inc_squeeze(result, output_len, ctx->shake256); + break; + } +#if ALLOW_VERBOSE + if (hss_verbose) { + printf( " ->" ); + int i; for (i=0; isha256 ); break; + case HASH_SHAKE256: case HASH_SHAKE256_24: + shake256_inc_init(ctx->shake256); + break; } } @@ -100,43 +108,52 @@ void hss_update_hash_context(int h, union hash_context *ctx, case HASH_SHA256: case HASH_SHA256_24: SHA256_Update(&ctx->sha256, msg, len_msg); break; + case HASH_SHAKE256: case HASH_SHAKE256_24: + shake256_inc_absorb(ctx->shake256, msg, len_msg); + break; } } void hss_finalize_hash_context(int h, union hash_context *ctx, void *buffer) { + int output_len; switch (h) { case HASH_SHA256: SHA256_Final(buffer, &ctx->sha256); -#if ALLOW_VERBOSE - if (hss_verbose) { - printf( " -->" ); - int i; for (i=0; i<32; i++) printf( " %02x", ((unsigned char*)buffer)[i] ); - printf( "\n" ); - } -#endif + output_len = 32; break; case HASH_SHA256_24: { unsigned char temp[SHA256_LEN]; SHA256_Final(temp, &ctx->sha256); memcpy(buffer, temp, 24); hss_zeroize(temp, sizeof temp); + output_len = 24; + break; + } + case HASH_SHAKE256: + output_len = 32; + shake256_inc_finalize(ctx->shake256); + shake256_inc_squeeze(buffer, output_len, ctx->shake256); + break; + case HASH_SHAKE256_24: + output_len = 24; + shake256_inc_finalize(ctx->shake256); + shake256_inc_squeeze(buffer, output_len, ctx->shake256); + break; + } #if ALLOW_VERBOSE if (hss_verbose) { printf( " -->" ); - int i; for (i=0; i<24; i++) printf( " %02x", ((unsigned char*)buffer)[i] ); + int i; for (i=0; i #include @@ -16,10 +17,13 @@ enum { HASH_SHA256 = 1, /* SHA256 */ HASH_SHA256_24 = 2, /* SHA256 truncated to 24 bytes */ + HASH_SHAKE256 = 3, /* SHAKE256 */ + HASH_SHAKE256_24 = 4, /* SHAKE256 truncated to 24 bytes */ }; union hash_context { SHA256_CTX sha256; + keccak_state shake256; /* Any other hash contexts would go here */ }; diff --git a/hss.c b/hss.c index 0402094..258c2e7 100644 --- a/hss.c +++ b/hss.c @@ -32,7 +32,8 @@ struct hss_working_key *hss_load_private_key( unsigned levels; param_set_t lm[ MAX_HSS_LEVELS ]; param_set_t ots[ MAX_HSS_LEVELS ]; - if (!hss_get_parameter_set( &levels, lm, ots, read_private_key, context)) { + if (!hss_get_parameter_set( &levels, lm, ots, + read_private_key, context, info)) { /* Can't read private key, or private key invalid */ return 0; } diff --git a/hss.h b/hss.h index 9fb622c..7047a9b 100644 --- a/hss.h +++ b/hss.h @@ -297,7 +297,7 @@ bool hss_set_autoreserve( size_t hss_get_private_key_len(unsigned levels, const param_set_t *lm_type, const param_set_t *lm_ots_type); -#define HSS_MAX_PRIVATE_KEY_LEN (8 + 8 + MAX_SEED_LEN + 16) +#define HSS_MAX_PRIVATE_KEY_LEN (8 + 16 + MAX_SEED_LEN + 16) /* * This include file has the functions that contains the lengths of the other @@ -329,7 +329,7 @@ size_t hss_get_aux_data_len(size_t max_length, * This is here to solve a chicken-and-egg problem: the hss_working_key * must be initialized to the same parameter set as the private key, * but (other than this function, or somehow remembering it) there's - * no way to retreive the parameter set. + * no way to retrieve the parameter set. * * read_private_key/context will read the private key (if read_private_key is * NULL, context is assumed to point to the private key) @@ -345,12 +345,13 @@ bool hss_get_parameter_set( unsigned *levels, param_set_t lm_ots_type[ MAX_HSS_LEVELS ], bool (*read_private_key)(unsigned char *private_key, size_t len_private_key, void *context), - void *context); + void *context, + struct hss_extra_info *info); enum hss_error_code { hss_error_none = 0, /* I don't know nothing about any error */ - hss_range_normal_failures, /* There errors happen during normal use */ + hss_range_normal_failures, /* These errors happen during normal use */ /* of the signature scheme */ hss_error_bad_signature, /* Invalid signature */ hss_error_private_key_expired, /* This private key has generated all */ @@ -379,9 +380,10 @@ enum hss_error_code { /* properly */ hss_error_ctx_already_used, /* The ctx has already been used */ hss_error_bad_public_key, /* Somehow, we got an invalid public key */ + hss_error_bad_private_key, /* Somehow, we got an invalid private key */ - hss_range_processing_error, /* These errors are cause by an */ - /* error while processing */ + hss_range_processing_error, /* These errors are caused by an */ + /* error detected by an external function */ hss_error_bad_randomness, /* The RNG claimed failure */ hss_error_private_key_write_failed, /* The write of the private key */ /* to NVRAM failed */ diff --git a/hss_internal.h b/hss_internal.h index be703a3..b1d0c0f 100644 --- a/hss_internal.h +++ b/hss_internal.h @@ -11,9 +11,9 @@ * this subsystem. It should not be used by applications */ -#define PARAM_SET_COMPRESS_LEN 1 /* We assume that we can compress the */ +#define PARAM_SET_COMPRESS_LEN 2 /* We assume that we can compress the */ /* lm_type and the lm_ots type for a */ - /* single level into 1 byte */ + /* single level into 2 bytes */ #define PARM_SET_END 0xff /* We set this marker in the parameter set */ /* when fewer than the maximum levels are used */ @@ -42,7 +42,7 @@ #define PRIVATE_KEY_SEED_LEN(seed_len) SEED_LEN #endif #define PRIVATE_KEY_LEN(seed_len) (PRIVATE_KEY_SEED + \ - PRIVATE_KEY_SEED_LEN(seed_len)) /* That's 48-64 bytes */ + PRIVATE_KEY_SEED_LEN(seed_len)) /* That's 56-72 bytes */ struct merkle_level; struct hss_working_key { diff --git a/hss_param.c b/hss_param.c index 3a56dd0..1a7537e 100644 --- a/hss_param.c +++ b/hss_param.c @@ -9,26 +9,44 @@ static struct map_structure { param_set_t public; unsigned char compressed; } lm_map[] = { - { LMS_SHA256_N32_H5, 0x05 }, - { LMS_SHA256_N32_H10,0x06 }, - { LMS_SHA256_N32_H15,0x07 }, - { LMS_SHA256_N32_H20,0x08 }, - { LMS_SHA256_N32_H25,0x09 }, - { LMS_SHA256_N24_H5, 0x0a }, - { LMS_SHA256_N24_H10,0x0b }, - { LMS_SHA256_N24_H15,0x0c }, - { LMS_SHA256_N24_H20,0x0d }, - { LMS_SHA256_N24_H25,0x0e }, + { LMS_SHA256_N32_H5, 0x01 }, + { LMS_SHA256_N32_H10,0x02 }, + { LMS_SHA256_N32_H15,0x03 }, + { LMS_SHA256_N32_H20,0x04 }, + { LMS_SHA256_N32_H25,0x05 }, + { LMS_SHA256_N24_H5, 0x06 }, + { LMS_SHA256_N24_H10,0x07 }, + { LMS_SHA256_N24_H15,0x08 }, + { LMS_SHA256_N24_H20,0x09 }, + { LMS_SHA256_N24_H25,0x0a }, + { LMS_SHAKE256_N32_H5, 0x0b }, + { LMS_SHAKE256_N32_H10,0x0c }, + { LMS_SHAKE256_N32_H15,0x0d }, + { LMS_SHAKE256_N32_H20,0x0e }, + { LMS_SHAKE256_N32_H25,0x0f }, + { LMS_SHAKE256_N24_H5, 0x10 }, + { LMS_SHAKE256_N24_H10,0x11 }, + { LMS_SHAKE256_N24_H15,0x12 }, + { LMS_SHAKE256_N24_H20,0x13 }, + { LMS_SHAKE256_N24_H25,0x14 }, { 0, 0 }}, ots_map[] = { { LMOTS_SHA256_N32_W1, 0x01 }, { LMOTS_SHA256_N32_W2, 0x02 }, { LMOTS_SHA256_N32_W4, 0x03 }, { LMOTS_SHA256_N32_W8, 0x04 }, - { LMOTS_SHA256_N24_W1, 0x09 }, - { LMOTS_SHA256_N24_W2, 0x0a }, - { LMOTS_SHA256_N24_W4, 0x0b }, - { LMOTS_SHA256_N24_W8, 0x0c }, + { LMOTS_SHA256_N24_W1, 0x05 }, + { LMOTS_SHA256_N24_W2, 0x06 }, + { LMOTS_SHA256_N24_W4, 0x07 }, + { LMOTS_SHA256_N24_W8, 0x08 }, + { LMOTS_SHAKE256_N32_W1, 0x09 }, + { LMOTS_SHAKE256_N32_W2, 0x0a }, + { LMOTS_SHAKE256_N32_W4, 0x0b }, + { LMOTS_SHAKE256_N32_W8, 0x0c }, + { LMOTS_SHAKE256_N24_W1, 0x0d }, + { LMOTS_SHAKE256_N24_W2, 0x0e }, + { LMOTS_SHAKE256_N24_W4, 0x0f }, + { LMOTS_SHAKE256_N24_W8, 0x10 }, { 0, 0 }}; static bool map(param_set_t *a, struct map_structure *map) { @@ -74,8 +92,9 @@ bool hss_compress_param_set( unsigned char *compressed, if (!map( &a, lm_map ) || !map( &b, ots_map )) return false; - *compressed++ = (a<<4) + b; - len_compressed--; + *compressed++ = a; + *compressed++ = b; + len_compressed -= 2; } while (len_compressed) { @@ -107,16 +126,24 @@ bool hss_get_parameter_set( unsigned *levels, param_set_t lm_ots_type[ MAX_HSS_LEVELS ], bool (*read_private_key)(unsigned char *private_key, size_t len_private_key, void *context), - void *context) { + void *context, + struct hss_extra_info *info) { unsigned char private_key[ PRIVATE_KEY_LEN(MAX_SEED_LEN) ]; bool success = false; + enum hss_error_code error_code = hss_error_bad_private_key; /* Most of */ + /* the detected errors are 'what's in the private key does not */ + /* correspond to a supported parameter set' */ if (read_private_key) { if (!read_private_key( private_key, PRIVATE_KEY_SEED, context )) { + error_code = hss_error_private_key_read_failed; goto failed; } } else { - if (!context) return false; + if (!context) { + error_code = hss_error_no_private_buffer; + goto failed; + } memcpy( private_key, context, PRIVATE_KEY_SEED ); } @@ -124,11 +151,12 @@ bool hss_get_parameter_set( unsigned *levels, unsigned total_height = 0; unsigned level; for (level=0; level < MAX_HSS_LEVELS; level++) { - unsigned char c = private_key[PRIVATE_KEY_PARAM_SET + level]; - if (c == PARM_SET_END) break; + unsigned char c = private_key[PRIVATE_KEY_PARAM_SET + 2*level]; + unsigned char d = private_key[PRIVATE_KEY_PARAM_SET + 2*level + 1]; + if (c == PARM_SET_END && d == PARM_SET_END) break; /* Decode this level's parameter set */ - param_set_t lm = (c >> 4); - param_set_t ots = (c & 0x0f); + param_set_t lm = c; + param_set_t ots = d; /* Make sure both are supported */ /* While we're here, add up the total Merkle height */ @@ -138,15 +166,25 @@ bool hss_get_parameter_set( unsigned *levels, /* While we're here, add up the total Merkle height */ switch (lm) { case LMS_SHA256_N32_H5: - case LMS_SHA256_N24_H5: total_height += 5; break; + case LMS_SHA256_N24_H5: + case LMS_SHAKE256_N32_H5: + case LMS_SHAKE256_N24_H5: total_height += 5; break; case LMS_SHA256_N32_H10: - case LMS_SHA256_N24_H10: total_height += 10; break; + case LMS_SHA256_N24_H10: + case LMS_SHAKE256_N32_H10: + case LMS_SHAKE256_N24_H10: total_height += 10; break; case LMS_SHA256_N32_H15: - case LMS_SHA256_N24_H15: total_height += 15; break; + case LMS_SHA256_N24_H15: + case LMS_SHAKE256_N32_H15: + case LMS_SHAKE256_N24_H15: total_height += 15; break; case LMS_SHA256_N32_H20: - case LMS_SHA256_N24_H20: total_height += 20; break; + case LMS_SHA256_N24_H20: + case LMS_SHAKE256_N32_H20: + case LMS_SHAKE256_N24_H20: total_height += 20; break; case LMS_SHA256_N32_H25: - case LMS_SHA256_N24_H25: total_height += 25; break; + case LMS_SHA256_N24_H25: + case LMS_SHAKE256_N32_H25: + case LMS_SHAKE256_N24_H25: total_height += 25; break; default: goto failed; } lm_type[level] = lm; @@ -159,7 +197,7 @@ bool hss_get_parameter_set( unsigned *levels, /* Make sure that the rest of the private key has PARM_SET_END */ unsigned i; - for (i = level+1; i max_count) goto failed; /* Private key expired */ + if (current_count > max_count) { + /* Private key expired */ + error_code = hss_error_private_key_expired; + goto failed; + } success = true; /* It worked! */ + error_code = hss_error_none; failed: + if (info) info->error_code = error_code; /* There might be private keying material here */ hss_zeroize( private_key, sizeof private_key ); return success; @@ -191,7 +235,7 @@ bool hss_get_parameter_set( unsigned *levels, int get_level0_lm_hash_len( const unsigned char *private_key ) { /* Look up the compressed parameter set format */ unsigned char c = private_key[PRIVATE_KEY_PARAM_SET]; - param_set_t lm = (c >> 4); + param_set_t lm = c; if (!unmap( &lm, lm_map )) return 0; unsigned n; if (!lm_look_up_parameter_set(lm, 0, &n, 0)) return 0; diff --git a/lm_common.c b/lm_common.c index 5c7fd4f..46bfe0f 100644 --- a/lm_common.c +++ b/lm_common.c @@ -35,6 +35,26 @@ bool lm_look_up_parameter_set(param_set_t parameter_set, v_h = HASH_SHA256; v_n = 32; v_height = 25; break; case LMS_SHA256_N24_H25: v_h = HASH_SHA256_24; v_n = 24; v_height = 25; break; + case LMS_SHAKE256_N32_H5: + v_h = HASH_SHAKE256; v_n = 32; v_height = 5; break; + case LMS_SHAKE256_N24_H5: + v_h = HASH_SHAKE256_24; v_n = 24; v_height = 5; break; + case LMS_SHAKE256_N32_H10: + v_h = HASH_SHAKE256; v_n = 32; v_height = 10; break; + case LMS_SHAKE256_N24_H10: + v_h = HASH_SHAKE256_24; v_n = 24; v_height = 10; break; + case LMS_SHAKE256_N32_H15: + v_h = HASH_SHAKE256; v_n = 32; v_height = 15; break; + case LMS_SHAKE256_N24_H15: + v_h = HASH_SHAKE256_24; v_n = 24; v_height = 15; break; + case LMS_SHAKE256_N32_H20: + v_h = HASH_SHAKE256; v_n = 32; v_height = 20; break; + case LMS_SHAKE256_N24_H20: + v_h = HASH_SHAKE256_24; v_n = 24; v_height = 20; break; + case LMS_SHAKE256_N32_H25: + v_h = HASH_SHAKE256; v_n = 32; v_height = 25; break; + case LMS_SHAKE256_N24_H25: + v_h = HASH_SHAKE256_24; v_n = 24; v_height = 25; break; default: return false; } diff --git a/lm_ots_common.c b/lm_ots_common.c index cdf1e81..95ab357 100644 --- a/lm_ots_common.c +++ b/lm_ots_common.c @@ -29,6 +29,22 @@ bool lm_ots_look_up_parameter_set(param_set_t parameter_set, v_h = HASH_SHA256; v_n = 32; v_w = 8; v_p = 34; v_ls = 0; break; case LMOTS_SHA256_N24_W8: v_h = HASH_SHA256_24; v_n = 24; v_w = 8; v_p = 26; v_ls = 0; break; + case LMOTS_SHAKE256_N32_W1: + v_h = HASH_SHAKE256; v_n = 32; v_w = 1; v_p = 265; v_ls = 7; break; + case LMOTS_SHAKE256_N24_W1: + v_h = HASH_SHAKE256_24; v_n = 24; v_w = 1; v_p = 200; v_ls = 8; break; + case LMOTS_SHAKE256_N32_W2: + v_h = HASH_SHAKE256; v_n = 32; v_w = 2; v_p = 133; v_ls = 6; break; + case LMOTS_SHAKE256_N24_W2: + v_h = HASH_SHAKE256_24; v_n = 24; v_w = 2; v_p = 101; v_ls = 6; break; + case LMOTS_SHAKE256_N32_W4: + v_h = HASH_SHAKE256; v_n = 32; v_w = 4; v_p = 67; v_ls = 4; break; + case LMOTS_SHAKE256_N24_W4: + v_h = HASH_SHAKE256_24; v_n = 24; v_w = 4; v_p = 51; v_ls = 4; break; + case LMOTS_SHAKE256_N32_W8: + v_h = HASH_SHAKE256; v_n = 32; v_w = 8; v_p = 34; v_ls = 0; break; + case LMOTS_SHAKE256_N24_W8: + v_h = HASH_SHAKE256_24; v_n = 24; v_w = 8; v_p = 26; v_ls = 0; break; default: return false; } diff --git a/programming.notes b/programming.notes index d2a4fa0..30e980a 100644 --- a/programming.notes +++ b/programming.notes @@ -167,17 +167,20 @@ sources. this subsystem, and what they really mean private key This is what the raw private key looks like. It's not a formal C - structure; instead, it is a byte array, currently 48 bytes long, - split up into: + structure; instead, it is a byte array, currently 56-72 bytes long + (depending on the secret method selected), split up into: - 8 bytes of count; this is the state that gets updated with every signature, and consists of a bigendian count of the number of signatures so far. By convention, a setting of 0xffffffffffffffff means 'we're used up all our signatures' - - 8 bytes of parameter set, in a compressed format. This is here so + - 16 bytes of parameter set, in a compressed format. This is here so that the application needn't tell us what the parmaeter set when loading a key (and can't get it wrong) - 32 bytes of random seed; this is where all the security comes from. It is 32 bytes (256 bits) so Grover's algorthm can't recover it. + - (Optional) 16 bytes of top level I value; this is here if + SECRET_METHOD == 2, as that method derives the I value from the + initial randomness (and so we need a place to store it) This is a flat set of bytes, because we hand it to read_private_key and update_private_key routines, which are expected to read/write them to long term storage. @@ -237,8 +240,8 @@ sources. In addition, any such pointer to a (struct thread_collection) may be NULL; this is never considered an error condition, rather it is an indication that we're running in single-threaded mode (either because - that's how we're linked, or because we got an error spawning the - thread). + that's how we're linked, because the application asked us to run in + single-thread mode or because we got an error spawning the thread). struct expanded_aux_data This is an array of pointers to the various node values that occur at various sublevels within the aux data; for any level that isn't @@ -333,7 +336,7 @@ sources. or a bug somewhere struct seed_derive This is the structure we use to derive keys in a potentially side - channel resistant manner. There are two different versions of this + channel resistant manner. There are three different versions of this structure (with the same API, controlled by #define's) that map the current seed, I value, q value (Merkle tree node index) and j value (Winternitz digit index) into unpredictable values for the @@ -548,20 +551,22 @@ sources. common_defs.h This is a central spot to put definitions of general interest of the entire subsystem demo.c This is an example program that uses this subsystem; it - implements a simple file signer. Note: because it + implements a simple file signer. Note: becauSe it doesn't get that great of randomness (due to the need to restrict ourselves to standard C), it probably shouldn't be used as is. It does try to use /dev/urandom; that's of help only on OS's that actually implement /dev/urandom endian.[ch] Routines to read/write values in bigendian format + fips202.[ch] The software SHAKE implementation hash.[ch] Routines to implement the hashing API, as used by the - rest of the subsystem. This currently only implements - SHA-256, it'll support other hashes once LMS does. - Note that there really are three separate APIs to do - hashing (hash this entire string, hash this string - using this context variable as a temp, do on-line - hashing); those are all used at various times. + rest of the subsystem. This currently implements + SHA-256, SHA-256/192 and SHAKE256 (with both 192 and + 256 bit outputs). Note that there really are three + separate APIs to do hashing (hash this entire string, + hash this string using this context variable as a + temp, do on-line hashing); those are all used at + various times. hss.c This used to be where all the code lived; however, we have since migrated the vast majority of the routines to more appropriate source files (so we don't have a @@ -605,7 +610,7 @@ sources. in an incremental fashion. hss_sign_inc.h This is the public include file for the incremental signature routines. It's in its own file because it - needs to pull in some internal files (e.g. hash.h)' + needs to pull in some internal files (e.g. hash.h) that we generally don't need to hand to people hss_thread.h This is the internal prototype for our internal threading abstraction. We have two implementations of @@ -656,7 +661,7 @@ sources. USE_OPENSSL is 0. This is provided in case you don't have OpenSSL available. sha256.h Routine that computes the SHA-256 hash. This is the - same interface that OpenSSL presents. We also + same interface that OpenSSL presents. We alsO include a #define (USE_OPENSSL); if 1, these are direct calls to OpenSSL; if 0, we use our own implementation (in case you don't have OpenSSL diff --git a/test_hss.c b/test_hss.c index cc7bd96..6ba3763 100644 --- a/test_hss.c +++ b/test_hss.c @@ -19,6 +19,7 @@ static struct { bool (*test_enabled)(bool); /* Check if this tests is enabled */ } test_list[] = { { "testvector", test_testvector, "test vectors from the draft", false }, + { "shake", test_shake, "SHAKE-256 hash function test", false }, { "keygen", test_keygen, "key generation function test", false }, { "load", test_load, "key load test", false }, { "sign", test_sign, "signature test", false }, diff --git a/test_hss.h b/test_hss.h index 2ed8784..10251fb 100644 --- a/test_hss.h +++ b/test_hss.h @@ -4,6 +4,7 @@ #include extern bool test_testvector(bool fast_flag, bool quiet_flag); +extern bool test_shake(bool fast_flag, bool quiet_flag); extern bool test_keygen(bool fast_flag, bool quiet_flag); extern bool test_load(bool fast_flag, bool quiet_flag); extern bool test_sign(bool fast_flag, bool quiet_flag); diff --git a/test_keygen.c b/test_keygen.c index cd06021..d52f1c0 100644 --- a/test_keygen.c +++ b/test_keygen.c @@ -58,8 +58,8 @@ static bool gen_signature( unsigned char *privkey, bool test_keygen(bool fast_flag, bool quiet_flag) { /* We'll use this parameter set, unless we need to test out a different one */ - param_set_t default_lm_type[2] = { LMS_SHA256_N32_H10, LMS_SHA256_N32_H15 }; - param_set_t default_ots_type[2] = { LMOTS_SHA256_N32_W2, LMOTS_SHA256_N32_W8 }; + param_set_t default_lm_type[2] = { LMS_SHA256_N24_H10, LMS_SHA256_N24_H15 }; + param_set_t default_ots_type[2] = { LMOTS_SHA256_N24_W2, LMOTS_SHA256_N24_W8 }; int default_d = 2; int default_pubkey_size = hss_get_public_key_len( default_d, default_lm_type, default_ots_type ); int default_privkey_size = hss_get_private_key_len( default_d, default_lm_type, default_ots_type ); @@ -159,8 +159,8 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { { param_set_t lm_type[12], ots_type[12]; int i; - for (i=0; i<12; i++) lm_type[i] = LMS_SHA256_N32_H5; - for (i=0; i<12; i++) ots_type[i] = LMOTS_SHA256_N32_W8; + for (i=0; i<12; i++) lm_type[i] = LMS_SHA256_N24_H5; + for (i=0; i<12; i++) ots_type[i] = LMOTS_SHA256_N24_W4; /* Since some of these parm sets are illegal (and so don't */ /* have a size we can look up), we oversize the buffer */ @@ -190,28 +190,44 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { } d = 3; - ots_type[0] = LMOTS_SHA256_N32_W2; /* Make the keygen tests faster */ + ots_type[0] = LMOTS_SHA256_N24_W2; /* Make the keygen tests faster */ /* Try various lm_types (both legal and illegal) */ for (i=0; i<3; i++) { - int lm; - for (lm = 0; lm < 100; lm++) { /* 100??? Well, failure is */ + param_set_t lm; + for (lm = 0; lm < 100; lm++) /* 100??? Well, failure is */ + do { /* quick, so it's not that expensive to test a lot */ bool expect_success; switch (lm) { case LMS_SHA256_N32_H5: case LMS_SHA256_N32_H10: case LMS_SHA256_N32_H15: + case LMS_SHA256_N24_H5: + case LMS_SHA256_N24_H10: + case LMS_SHA256_N24_H15: + case LMS_SHAKE256_N32_H5: + case LMS_SHAKE256_N32_H10: + case LMS_SHAKE256_N32_H15: + case LMS_SHAKE256_N24_H5: + case LMS_SHAKE256_N24_H10: + case LMS_SHAKE256_N24_H15: expect_success = true; break; case LMS_SHA256_N32_H20: - if (i == 0 && fast_flag) continue; /* This parm set */ - /* takes too long for fast mode */ + case LMS_SHA256_N24_H20: + case LMS_SHAKE256_N32_H20: + case LMS_SHAKE256_N24_H20: + if (i == 0 && fast_flag) goto next_iter; /* This parm */ + /* set takes too long for fast mode */ /* (20 seconds on my test machine) */ expect_success = true; break; case LMS_SHA256_N32_H25: - if (i == 0) continue; /* This parm set takes too long */ - /* even for full mode; 10 minutes */ - /* for a test that doesn't tell */ - /* us much */ + case LMS_SHA256_N24_H25: + case LMS_SHAKE256_N32_H25: + case LMS_SHAKE256_N24_H25: + if (i == 0) goto next_iter; /* This parm set takes too */ + /* long even for full mode; 10 */ + /* minutes for a test that doesn't */ + /* tell us much */ expect_success = true; break; default: /* All unsupported LM types */ expect_success = false; break; @@ -223,10 +239,10 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { d, lm_type, ots_type, ignore_priv_key, NULL, pub_key, len_pub_key, NULL, 0, &info); - lm_type[i] = LMS_SHA256_N32_H5; + lm_type[i] = LMS_SHA256_N24_H5; if (expect_success != got_success) { - printf( "Keygen with lm_type[%d] = %d: success = %d\n", - i, lm, got_success ); + printf( "Keygen with lm_type[%d] = %x: success = %d\n", + i, (unsigned)lm, got_success ); return false; } if (!got_success && hss_extra_info_test_error_code(&info) != @@ -234,19 +250,33 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { printf( "Bad parm set got incorrect error\n" ); return false; } - } - } +next_iter: lm ^= 0xe0000000L; /* Cycle through the e000000 parm sets */ + } while (lm & 0x80000000L); + } /* Now, try various lm_ots_types (both legal and illegal) */ for (i=0; i<3; i++) { - int ots; - for (ots = 0; ots < 100; ots++) { + param_set_t ots; + for (ots = 0; ots < 100; ots++) + do { bool expect_success; switch (ots) { case LMOTS_SHA256_N32_W1: case LMOTS_SHA256_N32_W2: case LMOTS_SHA256_N32_W4: case LMOTS_SHA256_N32_W8: + case LMOTS_SHA256_N24_W1: + case LMOTS_SHA256_N24_W2: + case LMOTS_SHA256_N24_W4: + case LMOTS_SHA256_N24_W8: + case LMOTS_SHAKE256_N32_W1: + case LMOTS_SHAKE256_N32_W2: + case LMOTS_SHAKE256_N32_W4: + case LMOTS_SHAKE256_N32_W8: + case LMOTS_SHAKE256_N24_W1: + case LMOTS_SHAKE256_N24_W2: + case LMOTS_SHAKE256_N24_W4: + case LMOTS_SHAKE256_N24_W8: expect_success = true; break; default: /* All unsupported LM types */ expect_success = false; break; @@ -258,9 +288,9 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { d, lm_type, ots_type, ignore_priv_key, NULL, pub_key, len_pub_key, NULL, 0, &info); - ots_type[i] = LMOTS_SHA256_N32_W2; + ots_type[i] = LMOTS_SHA256_N24_W4; if (expect_success != got_success) { - printf( "Keygen with ots_type[%d] = %d: success = %d\n", i, ots, got_success ); + printf( "Keygen with ots_type[%d] = %x: success = %d\n", i, (unsigned)ots, got_success ); return false; } if (!got_success && hss_extra_info_test_error_code(&info) != @@ -268,7 +298,8 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { printf( "Bad parm set got incorrect error\n" ); return false; } - } + ots ^= 0xe0000000L; /* Cycle through the e000000 parm sets */ + } while (ots & 0x80000000L); } } @@ -343,13 +374,35 @@ bool test_keygen(bool fast_flag, bool quiet_flag) { /* Check out the aux data, for various parameter sets and lengths of */ /* aux data */ int i; - for (i=LMS_SHA256_N32_H5; i<=LMS_SHA256_N32_H20; i++) { + static param_set_t parms_to_test[] = { + LMS_SHA256_N32_H5, + LMS_SHA256_N24_H5, + LMS_SHA256_N32_H10, + LMS_SHA256_N24_H10, + LMS_SHA256_N32_H15, + LMS_SHA256_N24_H15, + LMS_SHAKE256_N32_H5, + LMS_SHAKE256_N24_H5, + LMS_SHAKE256_N32_H10, + LMS_SHAKE256_N24_H10, + LMS_SHAKE256_N32_H15, + LMS_SHAKE256_N24_H15, + 0, /* Stop here if not fastflag */ + LMS_SHA256_N32_H20, + LMS_SHA256_N24_H20, + LMS_SHAKE256_N32_H20, + LMS_SHAKE256_N24_H20, + }; + for (i=0; i HSS_MAX_PUBLIC_KEY_LEN || !sig_size || !privkey_size || privkey_size > HSS_MAX_PRIVATE_KEY_LEN) { - printf( "Internal error: bad parm set %d\n", i ); + printf( "Internal error: bad parm set %x\n", (unsigned)lm_type[0] ); return false; } unsigned char pubkey[HSS_MAX_PUBLIC_KEY_LEN], @@ -432,10 +485,11 @@ static bool gen_signature( unsigned char *privkey, unsigned char *sig, size_t sig_len, const unsigned char *pubkey) { /* Step 1: load the working key */ + struct hss_extra_info info = { 0 }; struct hss_working_key *w = hss_load_private_key(NULL, privkey, - 0, aux_data, aux_len, 0 ); + 0, aux_data, aux_len, &info ); if (!w) { - printf( "Error loading working key\n" ); + printf( "Error loading working key ; error_code = %x\n" , info.error_code); return false; } diff --git a/test_load.c b/test_load.c index 41f750f..5c9d93f 100644 --- a/test_load.c +++ b/test_load.c @@ -145,9 +145,15 @@ bool test_load(bool fast_flag, bool quiet_flag) { if (!test_aux( LMS_SHA256_N24_H10, LMOTS_SHA256_N24_W2, 24)) return false; if (!test_aux( LMS_SHA256_N32_H15, LMOTS_SHA256_N32_W2, 32 )) return false; if (!test_aux( LMS_SHA256_N24_H15, LMOTS_SHA256_N24_W2, 24 )) return false; + if (!test_aux( LMS_SHAKE256_N24_H5, LMOTS_SHAKE256_N24_W2, 24 )) return false; + if (!test_aux( LMS_SHAKE256_N32_H5, LMOTS_SHAKE256_N32_W2, 32 )) return false; if (!fast_flag) { if (!test_aux( LMS_SHA256_N32_H20, LMOTS_SHA256_N32_W2, 32 )) return false; if (!test_aux( LMS_SHA256_N24_H20, LMOTS_SHA256_N24_W2, 24 )) return false; + if (!test_aux( LMS_SHAKE256_N24_H10, LMOTS_SHAKE256_N24_W2, 24 )) return false; + if (!test_aux( LMS_SHAKE256_N32_H10, LMOTS_SHAKE256_N32_W2, 32 )) return false; + if (!test_aux( LMS_SHAKE256_N24_H20, LMOTS_SHAKE256_N24_W2, 24 )) return false; + if (!test_aux( LMS_SHAKE256_N32_H20, LMOTS_SHAKE256_N32_W2, 32 )) return false; } /* @@ -174,6 +180,10 @@ bool test_load(bool fast_flag, bool quiet_flag) { (ul)LMS_SHA256_N24_H10, (ul)LMOTS_SHA256_N24_W2)) return false; if (!load_key( &index, priv_key, w, 1, (ul)LMS_SHA256_N24_H10, (ul)LMOTS_SHA256_N24_W4)) return false; + if (!load_key( &index, priv_key, w, 1, + (ul)LMS_SHAKE256_N24_H10, (ul)LMOTS_SHAKE256_N24_W2)) return false; + if (!load_key( &index, priv_key, w, 1, + (ul)LMS_SHAKE256_N24_H10, (ul)LMOTS_SHAKE256_N24_W4)) return false; if (!load_key( &index, priv_key, w, 2, (ul)LMS_SHA256_N24_H5, (ul)LMOTS_SHA256_N24_W2, (ul)LMS_SHA256_N32_H5, (ul)LMOTS_SHA256_N32_W2)) return false; diff --git a/test_shake.c b/test_shake.c new file mode 100644 index 0000000..5be913f --- /dev/null +++ b/test_shake.c @@ -0,0 +1,184 @@ +/* + * This will test out the underlying SHAKE256 primitive + * + * We do this to verify whether the SHAKE256 implementation we include in + * this package is actually correct + */ + +#include +#include +#include "hash.h" +#include "test_hss.h" + +/* + * Run the test vector on the various internal SHAKE APIs + */ +static bool do_hash_test( const unsigned char *input, unsigned len_input, + const unsigned char *expected_output, + int hash_type, unsigned len_output ) { + unsigned char output[32+1]; + + int fence; + for (fence=3; fence<=4; fence++) { + output[len_output] = fence; /* To detect it if the hash function */ + /* generated a longer output than it is supposed to */ + + /* Check the simple hash implementation */ + memset( output, 0, len_output ); + hss_hash(output, hash_type, input, len_input ); + /* Check if the output is what we expect */ + if (0 != memcmp( output, expected_output, len_output )) return false; + /* Check if the hash function generated more output than */ + /* we expect */ + if (output[len_output] != fence) return false; + + /* Check the context implementation */ + union hash_context ctx; + memset( output, 0, len_output ); + hss_hash_ctx(output, hash_type, &ctx, input, len_input ); + if (0 != memcmp( output, expected_output, len_output )) return false; + if (output[len_output] != fence) return false; + + /* Check the incremental version, stepping through the possible */ + /* increments */ + int incr; + for (incr = 1; incr <= len_input; incr++) { + + hss_init_hash_context( hash_type, &ctx ); + int offset; + for (offset=0; offset < len_input; offset += incr ) { + int this_block = incr; + if (offset + this_block > len_input) { + this_block = len_input - offset; + } + hss_update_hash_context( hash_type, &ctx, + &input[offset], this_block ); + } + memset( output, 0, len_output ); + hss_finalize_hash_context( hash_type, &ctx, output ); + if (0 != memcmp( output, expected_output, len_output )) { + return false; + } + if (output[len_output] != fence) return false; + } + } + return true; +} + +/* + * Run the test vector on both SHAKE hash functions (the 256 bit and the 192 + * bit versions) that we provide + */ +static bool do_test( const unsigned char *input, unsigned len_input, + const unsigned char *expected_output ) { + if (!do_hash_test( input, len_input, expected_output, HASH_SHAKE256, 32 )) + return false; + if (!do_hash_test( input, len_input, expected_output, HASH_SHAKE256_24, 24 )) + return false; + return true; +} + +/* + * These test vectors were extracted from SHAKE256ShortMsg.rsp + * If the inputs appear to be rather arbitrary, well, complain to NIST + */ +bool test_shake(bool fast_flag, bool quiet_flag) { + { + const unsigned char input[] = { + 0x4a, 0x71, 0x96, 0x4b + }; + const unsigned char expected_output[] = { + 0x7b,0x7e,0x12,0xd2,0xa5,0x20,0xe2,0x32, + 0xfd,0xe6,0xc4,0x1d,0xbb,0xb2,0xb8,0xb7, + 0x4c,0x29,0x12,0xfb,0x3f,0x15,0x40,0x4f, + 0x73,0x04,0xfe,0x46,0x69,0x14,0x30,0xc9 + }; + + if (!do_test(input, sizeof input, expected_output)) return false; + } + + /* 47 byte input; the input size we see when doing a OTS computation */ + /* with a 192 bit hash */ + { + const unsigned char input[] = { + 0x43,0xfa,0x7c,0x73,0xc6,0x19,0x6e,0xf2, + 0x8d,0x3a,0xe7,0x34,0xfd,0x80,0x8c,0x1d, + 0x01,0x7e,0xb9,0x64,0xfd,0x54,0x18,0xdf, + 0x04,0x1b,0x73,0x01,0x4a,0x84,0xc6,0xa1, + 0xdc,0xbb,0x99,0xfc,0x8e,0x92,0x8c,0xfe, + 0x35,0xdb,0x34,0xbd,0x17,0x15,0x25 + }; + const unsigned char expected_output[] = { + 0x96,0x20,0xf7,0xda,0x5b,0x74,0x10,0xfe, + 0x8d,0xb4,0xe7,0x77,0x96,0xf5,0x57,0x0d, + 0x5a,0xde,0xf8,0xa3,0x44,0x17,0xbc,0x70, + 0xe6,0x0c,0xe6,0x8c,0x57,0x1e,0x8e,0x1e + }; + + if (!do_test(input, sizeof input, expected_output)) return false; + } + + /* 55 byte input; the input size we see when doing a OTS computation */ + /* with a 256 bit hash */ + { + const unsigned char input[] = { + 0x89,0xdb,0x45,0x54,0xee,0xc0,0x9a,0x89, + 0xf9,0xe4,0x9f,0x64,0xe5,0xe4,0x8e,0x0d, + 0xcd,0xc3,0x6e,0x3a,0x1d,0x8c,0x2c,0xf6, + 0x47,0x38,0xed,0xa2,0xb7,0xd1,0xa3,0x39, + 0x08,0xd8,0xde,0xd8,0x78,0xe5,0xe6,0x7d, + 0x99,0x8d,0x06,0x0e,0x4a,0x88,0x2a,0x9e, + 0xe6,0x13,0xad,0xed,0xbb,0x94,0x6c + }; + const unsigned char expected_output[] = { + 0xfd,0xd0,0xd8,0xe3,0x1a,0xb4,0x50,0x23, + 0x17,0xa9,0x02,0x93,0xa3,0x88,0x8e,0xdc, + 0xe2,0x8a,0xe9,0xa5,0x87,0x4b,0x71,0x67, + 0x38,0xa9,0x3b,0x25,0xdb,0x6d,0xe6,0x6d + }; + + if (!do_test(input, sizeof input, expected_output)) return false; + } + + /* 195 byte input; this is larger than the internal Keccak rate, and */ + /* hence the implemetation will need to perform multiple permutations */ + { + const unsigned char input[] = { + 0xc4,0x36,0xd1,0x9f,0x05,0x55,0x0b,0x69, + 0x79,0xbd,0xc6,0x9b,0xfd,0x27,0xea,0x4c, + 0xd8,0x0c,0x1a,0x60,0xf0,0x0a,0x8b,0x09, + 0x3e,0x89,0x17,0x8c,0x7f,0x9e,0x8d,0x49, + 0x2c,0x30,0x4c,0xf6,0xad,0x59,0x10,0x2b, + 0xca,0x0e,0x0b,0x23,0x62,0x03,0x38,0xc1, + 0x5f,0xc9,0xec,0xd1,0xe9,0x39,0xae,0x91, + 0xda,0x16,0x48,0x6f,0x72,0xee,0x1e,0x15, + 0x4d,0x41,0xbf,0xa3,0x91,0xe6,0xba,0x3b, + 0x6c,0xa9,0xb3,0xc3,0xbe,0x39,0xb5,0xe6, + 0x12,0x42,0xca,0x5c,0xd3,0xd6,0xc9,0x6c, + 0xbd,0x11,0x70,0xaf,0x91,0xfd,0xb2,0x16, + 0x0d,0xb3,0x52,0x2e,0x1b,0xc3,0xb1,0xa3, + 0x49,0xd6,0xe5,0x04,0x79,0x92,0x0a,0xc5, + 0xd9,0xbe,0xdd,0x8a,0x16,0xa7,0x87,0xa3, + 0xcd,0xc2,0xb6,0xd2,0x43,0x92,0xf2,0x55, + 0x55,0xcc,0x2f,0x20,0xb2,0xba,0x9e,0x6b, + 0x47,0xdd,0xc9,0x6c,0xfb,0xd6,0xdf,0x66, + 0x9d,0x87,0x4c,0xe2,0x1a,0x75,0x8d,0x3c, + 0xf4,0x70,0x43,0x62,0xef,0x77,0x86,0xd9, + 0x0e,0xd6,0x7b,0x01,0xbd,0x91,0x29,0x99, + 0x50,0x05,0x88,0x85,0xac,0xcd,0xdb,0xcf, + 0x44,0xe3,0x40,0xed,0x48,0x07,0x86,0x42, + 0x18,0x65,0x3e,0xe7,0xff,0x72,0x15,0xaa, + 0x1e,0x17,0x61 + }; + const unsigned char expected_output[] = { + 0x20,0x6b,0xe7,0x26,0xfc,0x68,0x13,0x67, + 0x38,0x7f,0xf0,0xa1,0x53,0x03,0x53,0x30, + 0x58,0x07,0x0f,0x96,0x55,0x43,0x8a,0xd8, + 0x14,0x2c,0xf3,0x9a,0x05,0x23,0xb2,0xce, + }; + + if (!do_test(input, sizeof input, expected_output)) return false; + } + + return true; +} diff --git a/test_sign.c b/test_sign.c index ea12a09..b10b2e5 100644 --- a/test_sign.c +++ b/test_sign.c @@ -187,7 +187,8 @@ static bool test_parm( int d, long num_sig, ... ) { return false; } if (hss_extra_info_test_error_code(&info) != hss_error_private_key_write_failed) { - printf( "Error: update failure gives wrong error\n" ); + printf( "Error: update failure gives wrong error got %d expected %d\n", + hss_extra_info_test_error_code(&info), hss_error_private_key_write_failed ); hss_free_working_key(w); return false; } diff --git a/test_sign_inc.c b/test_sign_inc.c index b89e7f4..be314dc 100644 --- a/test_sign_inc.c +++ b/test_sign_inc.c @@ -341,6 +341,18 @@ bool test_sign_inc(bool fast_flag, bool quiet_flag) { param_set_t lm_ots_array[1] = { LMOTS_SHA256_N24_W8 }; if (!run_test( d, lm_array, lm_ots_array, 32, true )) return false; } + { + int d = 1; + param_set_t lm_array[1] = { LMS_SHAKE256_N24_H5 }; + param_set_t lm_ots_array[1] = { LMOTS_SHAKE256_N24_W8 }; + if (!run_test( d, lm_array, lm_ots_array, 32, true )) return false; + } + { + int d = 1; + param_set_t lm_array[1] = { LMS_SHAKE256_N32_H5 }; + param_set_t lm_ots_array[1] = { LMOTS_SHAKE256_N32_W8 }; + if (!run_test( d, lm_array, lm_ots_array, 32, true )) return false; + } { int d = 1; param_set_t lm_array[1] = { LMS_SHA256_N32_H10 }; @@ -353,6 +365,18 @@ bool test_sign_inc(bool fast_flag, bool quiet_flag) { param_set_t lm_ots_array[1] = { LMOTS_SHA256_N24_W4 }; if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; } + { + int d = 1; + param_set_t lm_array[1] = { LMS_SHAKE256_N32_H10 }; + param_set_t lm_ots_array[1] = { LMOTS_SHAKE256_N32_W4 }; + if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; + } + { + int d = 1; + param_set_t lm_array[1] = { LMS_SHAKE256_N24_H10 }; + param_set_t lm_ots_array[1] = { LMOTS_SHAKE256_N24_W4 }; + if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; + } { int d = 2; param_set_t lm_array[2] = { LMS_SHA256_N32_H5, LMS_SHA256_N32_H5 }; @@ -383,6 +407,18 @@ bool test_sign_inc(bool fast_flag, bool quiet_flag) { param_set_t lm_ots_array[2] = { LMOTS_SHA256_N32_W4, LMOTS_SHA256_N24_W2 }; if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; } + { + int d = 2; + param_set_t lm_array[2] = { LMS_SHAKE256_N24_H5, LMS_SHAKE256_N24_H5 }; + param_set_t lm_ots_array[2] = { LMOTS_SHAKE256_N24_W4, LMOTS_SHAKE256_N24_W2 }; + if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; + } + { + int d = 2; + param_set_t lm_array[2] = { LMS_SHAKE256_N32_H5, LMS_SHAKE256_N32_H5 }; + param_set_t lm_ots_array[2] = { LMOTS_SHAKE256_N32_W4, LMOTS_SHAKE256_N32_W2 }; + if (!run_test( d, lm_array, lm_ots_array, 1024, true )) return false; + } { int d = 2; param_set_t lm_array[2] = { LMS_SHA256_N24_H5, LMS_SHA256_N24_H5 }; diff --git a/test_testvector.c b/test_testvector.c index 6c0c4b9..89c599f 100644 --- a/test_testvector.c +++ b/test_testvector.c @@ -1041,8 +1041,331 @@ bool test_testvector_3(void) { return verified; } +bool test_testvector_4(void) { + /* This is test case 4 */ + /* This is the SHAKE256/192 test from draft-fluhrer-lms-more-parm_sets */ + static const unsigned char public_key[] = { + 0x00,0x00,0x00,0x01,0xe0,0x00,0x00,0x0b, + 0xe0,0x00,0x00,0x0c, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, + 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, + 0xdb,0x54,0xa4,0x50,0x99,0x01,0x05,0x1c, + 0x01,0xe2,0x6d,0x99,0x90,0xe5,0x50,0x34, + 0x79,0x86,0xda,0x87,0x92,0x4f,0xf0,0xb1, + }; + static const unsigned char message[] = { + 0x54,0x65,0x73,0x74,0x20,0x6d,0x65,0x73, + 0x73,0x61,0x67,0x65,0x20,0x66,0x6f,0x72, + 0x20,0x53,0x48,0x41,0x4b,0x45,0x32,0x35, + 0x36,0x2d,0x31,0x39,0x32,0x0a, + }; + static const unsigned char signature[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06, + 0xe0,0x00,0x00,0x0c, + 0x84,0x21,0x9d,0xa9,0xce,0x9f,0xff,0xb1, + 0x6e,0xdb,0x94,0x52,0x7c,0x6d,0x10,0x56, + 0x55,0x87,0xdb,0x28,0x06,0x2d,0xea,0xc4, + 0x20,0x8e,0x62,0xfc,0x4f,0xbe,0x9d,0x85, + 0xde,0xb3,0xc6,0xbd,0x2c,0x01,0x64,0x0a, + 0xcc,0xb3,0x87,0xd8,0xa6,0x09,0x3d,0x68, + 0x51,0x12,0x34,0xa6,0xa1,0xa5,0x01,0x08, + 0x09,0x1c,0x03,0x4c,0xb1,0x77,0x7e,0x02, + 0xb5,0xdf,0x46,0x61,0x49,0xa6,0x69,0x69, + 0xa4,0x98,0xe4,0x20,0x0c,0x0a,0x0c,0x1b, + 0xf5,0xd1,0x00,0xcd,0xb9,0x7d,0x2d,0xd4, + 0x0e,0xfd,0x3c,0xad,0xa2,0x78,0xac,0xc5, + 0xa5,0x70,0x07,0x1a,0x04,0x39,0x56,0x11, + 0x2c,0x6d,0xee,0xbd,0x1e,0xb3,0xa7,0xb5, + 0x6f,0x5f,0x67,0x91,0x51,0x5a,0x7b,0x5f, + 0xfd,0xdb,0x0e,0xc2,0xd9,0x09,0x4b,0xfb, + 0xc8,0x89,0xea,0x15,0xc3,0xc7,0xb9,0xbe, + 0xa9,0x53,0xef,0xb7,0x5e,0xd6,0x48,0xf5, + 0x35,0xb9,0xac,0xab,0x66,0xa2,0xe9,0x63, + 0x1e,0x42,0x6e,0x4e,0x99,0xb7,0x33,0xca, + 0xa6,0xc5,0x59,0x63,0x92,0x9b,0x77,0xfe, + 0xc5,0x4a,0x7e,0x70,0x3d,0x81,0x62,0xe7, + 0x36,0x87,0x5c,0xb6,0xa4,0x55,0xd4,0xa9, + 0x01,0x5c,0x7a,0x6d,0x8f,0xd5,0xfe,0x75, + 0xe4,0x02,0xb4,0x70,0x36,0xdc,0x37,0x70, + 0xf4,0xa1,0xdd,0x0a,0x55,0x9c,0xb4,0x78, + 0xc7,0xfb,0x17,0x26,0x00,0x53,0x21,0xbe, + 0x9d,0x1a,0xc2,0xde,0x94,0xd7,0x31,0xee, + 0x4c,0xa7,0x9c,0xff,0x45,0x4c,0x81,0x1f, + 0x46,0xd1,0x19,0x80,0x90,0x9f,0x04,0x7b, + 0x20,0x05,0xe8,0x4b,0x6e,0x15,0x37,0x84, + 0x46,0xb1,0xca,0x69,0x1e,0xfe,0x49,0x1e, + 0xa9,0x8a,0xcc,0x9d,0x3c,0x0f,0x78,0x5c, + 0xab,0xa5,0xe2,0xeb,0x3c,0x30,0x68,0x11, + 0xc2,0x40,0xba,0x22,0x80,0x29,0x23,0x82, + 0x7d,0x58,0x26,0x39,0x30,0x4a,0x1e,0x97, + 0x83,0xba,0x5b,0xc9,0xd6,0x9d,0x99,0x9a, + 0x7d,0xb8,0xf7,0x49,0x77,0x0c,0x3c,0x04, + 0xa1,0x52,0x85,0x6d,0xc7,0x26,0xd8,0x06, + 0x79,0x21,0x46,0x5b,0x61,0xb3,0xf8,0x47, + 0xb1,0x3b,0x26,0x35,0xa4,0x53,0x79,0xe5, + 0xad,0xc6,0xff,0x58,0xa9,0x9b,0x00,0xe6, + 0x0a,0xc7,0x67,0xf7,0xf3,0x01,0x75,0xf9, + 0xf7,0xa1,0x40,0x25,0x7e,0x21,0x8b,0xe3, + 0x07,0x95,0x4b,0x12,0x50,0xc9,0xb4,0x19, + 0x02,0xc4,0xfa,0x7c,0x90,0xd8,0xa5,0x92, + 0x94,0x5c,0x66,0xe8,0x6a,0x76,0xde,0xfc, + 0xb8,0x45,0x00,0xb5,0x55,0x98,0xa1,0x99, + 0x0f,0xaa,0xa1,0x00,0x77,0xc7,0x4c,0x94, + 0x89,0x57,0x31,0x58,0x5c,0x8f,0x90,0x0d, + 0xe1,0xa1,0xc6,0x75,0xbd,0x8b,0x0c,0x18, + 0x0e,0xbe,0x2b,0x5e,0xb3,0xef,0x80,0x19, + 0xec,0xe3,0xe1,0xea,0x72,0x23,0xeb,0x79, + 0x06,0xa2,0x04,0x2b,0x62,0x62,0xb4,0xaa, + 0x25,0xc4,0xb8,0xa0,0x5f,0x20,0x5c,0x8b, + 0xef,0xee,0xf1,0x1c,0xef,0xf1,0x28,0x25, + 0x08,0xd7,0x1b,0xc2,0xa8,0xcf,0xa0,0xa9, + 0x9f,0x73,0xf3,0xe3,0xa7,0x4b,0xb4,0xb3, + 0xc0,0xd8,0xca,0x2a,0xbd,0x0e,0x1c,0x2c, + 0x17,0xda,0xfe,0x18,0xb4,0xee,0x22,0x98, + 0xe8,0x7b,0xcf,0xb1,0x30,0x5b,0x3c,0x06, + 0x9e,0x6d,0x38,0x55,0x69,0xa4,0x06,0x7e, + 0xd5,0x47,0x48,0x6d,0xd1,0xa5,0x0d,0x6f, + 0x4a,0x58,0xaa,0xb9,0x6e,0x2f,0xa8,0x83, + 0xa9,0xa3,0x9e,0x1b,0xd4,0x55,0x41,0xee, + 0xe9,0x4e,0xfc,0x32,0xfa,0xa9,0xa9,0x4b, + 0xe6,0x6d,0xc8,0x53,0x8b,0x2d,0xab,0x05, + 0xae,0xe5,0xef,0xa6,0xb3,0xb2,0xef,0xb3, + 0xfd,0x02,0x0f,0xe7,0x89,0x47,0x7a,0x93, + 0xaf,0xff,0x9a,0x3e,0x63,0x6d,0xbb,0xa8, + 0x64,0xa5,0xbf,0xfa,0x3e,0x28,0xd1,0x3d, + 0x49,0xbb,0x59,0x7d,0x94,0x86,0x5b,0xde, + 0x88,0xc4,0x62,0x7f,0x20,0x6a,0xb2,0xb4, + 0x65,0x08,0x4d,0x6b,0x78,0x06,0x66,0xe9, + 0x52,0xf8,0x71,0x0e,0xfd,0x74,0x8b,0xd0, + 0xf1,0xae,0x8f,0x10,0x35,0x08,0x7f,0x50, + 0x28,0xf1,0x4a,0xff,0xcc,0x5f,0xff,0xe3, + 0x32,0x12,0x1a,0xe4,0xf8,0x7a,0xc5,0xf1, + 0xea,0xc9,0x06,0x26,0x08,0xc7,0xd8,0x77, + 0x08,0xf1,0x72,0x3f,0x38,0xb2,0x32,0x37, + 0xa4,0xed,0xf4,0xb4,0x9a,0x5c,0xd3,0xd7, + 0xe0,0x00,0x00,0x0b, + 0xdd,0x4b,0xdc,0x8f,0x92,0x8f,0xb5,0x26, + 0xf6,0xfb,0x7c,0xdb,0x94,0x4a,0x7e,0xba, + 0xa7,0xfb,0x05,0xd9,0x95,0xb5,0x72,0x1a, + 0x27,0x09,0x6a,0x50,0x07,0xd8,0x2f,0x79, + 0xd0,0x63,0xac,0xd4,0x34,0xa0,0x4e,0x97, + 0xf6,0x15,0x52,0xf7,0xf8,0x1a,0x93,0x17, + 0xb4,0xec,0x7c,0x87,0xa5,0xed,0x10,0xc8, + 0x81,0x92,0x8f,0xc6,0xeb,0xce,0x6d,0xfc, + 0xe9,0xda,0xae,0x9c,0xc9,0xdb,0xa6,0x90, + 0x7c,0xa9,0xa9,0xdd,0x5f,0x9f,0x57,0x37, + 0x04,0xd5,0xe6,0xcf,0x22,0xa4,0x3b,0x04, + 0xe6,0x4c,0x1f,0xfc,0x7e,0x1c,0x44,0x2e, + 0xcb,0x49,0x5b,0xa2,0x65,0xf4,0x65,0xc5, + 0x62,0x91,0xa9,0x02,0xe6,0x2a,0x46,0x1f, + 0x6d,0xfd,0xa2,0x32,0x45,0x7f,0xad,0x14, + }; + + bool verified = hss_validate_signature( + public_key, + message, sizeof message, + signature, sizeof signature, 0); + + return verified; +} + +bool test_testvector_5(void) { + /* This is test case 5 */ + /* This is the SHAKE256/256 test from draft-fluhrer-lms-more-parm_sets */ + static const unsigned char public_key[] = { + 0x00,0x00,0x00,0x01,0xe0,0x00,0x00,0x06, + 0xe0,0x00,0x00,0x08, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x9b,0xb7,0xfa,0xee,0x41,0x1c,0xae,0x80, + 0x6c,0x16,0xa4,0x66,0xc3,0x19,0x1a,0x8b, + 0x65,0xd0,0xac,0x31,0x93,0x2b,0xbf,0x0c, + 0x2d,0x07,0xc7,0xa4,0xa3,0x63,0x79,0xfe, + }; + static const unsigned char message[] = { + 0x54,0x65,0x73,0x74,0x20,0x6d,0x65,0x73, + 0x61,0x67,0x65,0x20,0x66,0x6f,0x72,0x20, + 0x53,0x48,0x41,0x4b,0x45,0x32,0x35,0x36, + 0x2d,0x32,0x35,0x36,0x0a, + }; + static const unsigned char signature[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07, + 0xe0,0x00,0x00,0x08, + 0xb8,0x27,0x09,0xf0,0xf0,0x0e,0x83,0x75, + 0x91,0x90,0x99,0x62,0x33,0xd1,0xee,0x4f, + 0x4e,0xc5,0x05,0x34,0x47,0x3c,0x02,0xff, + 0xa1,0x45,0xe8,0xca,0x28,0x74,0xe3,0x2b, + 0x16,0xb2,0x28,0x11,0x8c,0x62,0xb9,0x6c, + 0x9c,0x77,0x67,0x8b,0x33,0x18,0x37,0x30, + 0xde,0xba,0xad,0xe8,0xfe,0x60,0x7f,0x05, + 0xc6,0x69,0x7b,0xc9,0x71,0x51,0x9a,0x34, + 0x1d,0x69,0xc0,0x01,0x29,0x68,0x0b,0x67, + 0xe7,0x5b,0x3b,0xd7,0xd8,0xaa,0x5c,0x8b, + 0x71,0xf0,0x26,0x69,0xd1,0x77,0xa2,0xa0, + 0xee,0xa8,0x96,0xdc,0xd1,0x66,0x0f,0x16, + 0x86,0x4b,0x30,0x2f,0xf3,0x21,0xf9,0xc4, + 0xb8,0x35,0x44,0x08,0xd0,0x67,0x60,0x50, + 0x4f,0x76,0x8e,0xbd,0x4e,0x54,0x5a,0x9b, + 0x0a,0xc0,0x58,0xc5,0x75,0x07,0x8e,0x6c, + 0x14,0x03,0x16,0x0f,0xb4,0x54,0x50,0xd6, + 0x1a,0x9c,0x8c,0x81,0xf6,0xbd,0x69,0xbd, + 0xfa,0x26,0xa1,0x6e,0x12,0xa2,0x65,0xba, + 0xf7,0x9e,0x9e,0x23,0x3e,0xb7,0x1a,0xf6, + 0x34,0xec,0xc6,0x6d,0xc8,0x8e,0x10,0xc6, + 0xe0,0x14,0x29,0x42,0xd4,0x84,0x3f,0x70, + 0xa0,0x24,0x27,0x27,0xbc,0x5a,0x2a,0xab, + 0xf7,0xb0,0xec,0x12,0xa9,0x90,0x90,0xd8, + 0xca,0xee,0xf2,0x13,0x03,0xf8,0xac,0x58, + 0xb9,0xf2,0x00,0x37,0x1d,0xc9,0xe4,0x1a, + 0xb9,0x56,0xe1,0xa3,0xef,0xed,0x9d,0x4b, + 0xbb,0x38,0x97,0x5b,0x46,0xc2,0x8d,0x5f, + 0x5b,0x3e,0xd1,0x9d,0x84,0x7b,0xd0,0xa7, + 0x37,0x17,0x72,0x63,0xcb,0xc1,0xa2,0x26, + 0x2d,0x40,0xe8,0x08,0x15,0xee,0x14,0x9b, + 0x6c,0xce,0x27,0x14,0x38,0x4c,0x9b,0x7f, + 0xce,0xb3,0xbb,0xcb,0xd2,0x52,0x28,0xdd, + 0xa8,0x30,0x65,0x36,0x37,0x6f,0x87,0x93, + 0xec,0xad,0xd6,0x02,0x02,0x65,0xda,0xb9, + 0x07,0x5f,0x64,0xc7,0x73,0xef,0x97,0xd0, + 0x73,0x52,0x91,0x99,0x95,0xb7,0x44,0x04, + 0xcc,0x69,0xa6,0xf3,0xb4,0x69,0x44,0x5c, + 0x92,0x86,0xa6,0xb2,0xc9,0xf6,0xdc,0x83, + 0x9b,0xe7,0x66,0x18,0xf0,0x53,0xde,0x76, + 0x3d,0xa3,0x57,0x1e,0xf7,0x0f,0x80,0x5c, + 0x9c,0xc5,0x4b,0x8e,0x50,0x1a,0x98,0xb9, + 0x8c,0x70,0x78,0x5e,0xeb,0x61,0x73,0x7e, + 0xce,0xd7,0x8b,0x0e,0x38,0x0d,0xed,0x4f, + 0x76,0x9a,0x9d,0x42,0x27,0x86,0xde,0xf5, + 0x97,0x00,0xee,0xf3,0x27,0x80,0x17,0xba, + 0xbb,0xe5,0xf9,0x06,0x3b,0x46,0x8a,0xe0, + 0xdd,0x61,0xd9,0x4f,0x9f,0x99,0xd5,0xcc, + 0x36,0xfb,0xec,0x41,0x78,0xd2,0xbd,0xa3, + 0xad,0x31,0xe1,0x64,0x4a,0x2b,0xcc,0xe2, + 0x08,0xd7,0x2d,0x50,0xa7,0x63,0x78,0x51, + 0xaa,0x90,0x8b,0x94,0xdc,0x43,0x76,0x12, + 0x0d,0x5b,0xea,0xb0,0xfb,0x80,0x5e,0x19, + 0x45,0xc4,0x18,0x34,0xdd,0x60,0x85,0xe6, + 0xdb,0x1a,0x3a,0xa7,0x8f,0xcb,0x59,0xf6, + 0x2b,0xde,0x68,0x23,0x6a,0x10,0x61,0x8c, + 0xff,0x12,0x3a,0xbe,0x64,0xda,0xe8,0xda, + 0xbb,0x2e,0x84,0xca,0x70,0x53,0x09,0xc2, + 0xab,0x98,0x6d,0x4f,0x83,0x26,0xba,0x06, + 0x42,0x27,0x2c,0xb3,0x90,0x4e,0xb9,0x6f, + 0x6f,0x5e,0x3b,0xb8,0x81,0x39,0x97,0x88, + 0x1b,0x6a,0x33,0xca,0xc0,0x71,0x4e,0x4b, + 0x5e,0x7a,0x88,0x2a,0xd8,0x7e,0x14,0x19, + 0x31,0xf9,0x7d,0x61,0x2b,0x84,0xe9,0x03, + 0xe7,0x73,0x13,0x9a,0xe3,0x77,0xf5,0xba, + 0x19,0xac,0x86,0x19,0x8d,0x48,0x5f,0xca, + 0x97,0x74,0x25,0x68,0xf6,0xff,0x75,0x81, + 0x20,0xa8,0x9b,0xf1,0x90,0x59,0xb8,0xa6, + 0xbf,0xe2,0xd8,0x6b,0x12,0x77,0x81,0x64, + 0x43,0x6a,0xb2,0x65,0x9b,0xa8,0x66,0x76, + 0x7f,0xcc,0x43,0x55,0x84,0x12,0x5f,0xb7, + 0x92,0x42,0x01,0xee,0x67,0xb5,0x35,0xda, + 0xf7,0x2c,0x5c,0xb3,0x1f,0x5a,0x0b,0x1d, + 0x92,0x63,0x24,0xc2,0x6e,0x67,0xd4,0xc3, + 0x83,0x6e,0x30,0x1a,0xa0,0x9b,0xae,0x8f, + 0xb3,0xf9,0x1f,0x16,0x22,0xb1,0x81,0x8c, + 0xcf,0x44,0x0f,0x52,0xca,0x9b,0x5b,0x9b, + 0x99,0xab,0xa8,0xa6,0x75,0x4a,0xae,0x2b, + 0x96,0x7c,0x49,0x54,0xfa,0x85,0x29,0x8a, + 0xd9,0xb1,0xe7,0x4f,0x27,0xa4,0x61,0x27, + 0xc3,0x61,0x31,0xc8,0x99,0x1f,0x0c,0xc2, + 0xba,0x57,0xa1,0x5d,0x35,0xc9,0x1c,0xf8, + 0xbc,0x48,0xe8,0xe2,0x0d,0x62,0x5a,0xf4, + 0xe8,0x5d,0x8f,0x94,0x02,0xec,0x44,0xaf, + 0xbd,0x47,0x92,0xb9,0x24,0xb8,0x39,0x33, + 0x2a,0x64,0x78,0x8a,0x77,0x01,0xa3,0x00, + 0x94,0xb9,0xec,0x4b,0x9f,0x4b,0x64,0x8f, + 0x16,0x8b,0xf4,0x57,0xfb,0xb3,0xc9,0x59, + 0x4f,0xa8,0x79,0x20,0xb6,0x45,0xe4,0x2a, + 0xa2,0xfe,0xcc,0x9e,0x21,0xe0,0x00,0xca, + 0x7d,0x3f,0xf9,0x14,0xe1,0x5c,0x40,0xa8, + 0xbc,0x53,0x31,0x29,0xa7,0xfd,0x39,0x52, + 0x93,0x76,0x43,0x0f,0x35,0x5a,0xaf,0x96, + 0xa0,0xa1,0x3d,0x13,0xf2,0x41,0x91,0x41, + 0xb3,0xcc,0x25,0x84,0x3e,0x8c,0x90,0xd0, + 0xe5,0x51,0xa3,0x55,0xdd,0x90,0xad,0x77, + 0x0e,0xa7,0x25,0x52,0x14,0xce,0x11,0x23, + 0x86,0x05,0xde,0x2f,0x00,0x0d,0x20,0x01, + 0x04,0xd0,0xc3,0xa3,0xe3,0x5a,0xe6,0x4e, + 0xa1,0x0a,0x3e,0xff,0x37,0xac,0x7e,0x95, + 0x49,0x21,0x7c,0xdf,0x52,0xf3,0x07,0x17, + 0x2e,0x2f,0x6c,0x7a,0x2a,0x45,0x43,0xe1, + 0x43,0x14,0x03,0x65,0x25,0xb1,0xad,0x53, + 0xee,0xad,0xdf,0x0e,0x24,0xb1,0xf3,0x69, + 0x14,0xed,0x22,0x48,0x3f,0x28,0x89,0xf6, + 0x1e,0x62,0xb6,0xfb,0x78,0xf5,0x64,0x5b, + 0xdb,0xb0,0x2c,0x9e,0x5b,0xf9,0x7d,0xb7, + 0xa0,0x00,0x4e,0x87,0xc2,0xa5,0x53,0x99, + 0xb6,0x19,0x58,0x78,0x6c,0x97,0xbd,0x52, + 0xfa,0x19,0x9c,0x27,0xf6,0xbb,0x4d,0x68, + 0xc4,0x90,0x79,0x33,0x56,0x27,0x55,0xbf, + 0xec,0x5d,0x4f,0xb5,0x2f,0x06,0xc2,0x89, + 0xd6,0xe8,0x52,0xcf,0x6b,0xc7,0x73,0xff, + 0xd4,0xc0,0x7e,0xe2,0xd6,0xcc,0x55,0xf5, + 0x7e,0xdc,0xfb,0xc8,0xe8,0x69,0x2a,0x49, + 0xad,0x47,0xa1,0x21,0xfe,0x3c,0x1b,0x16, + 0xca,0xb1,0xcc,0x28,0x5f,0xaf,0x67,0x93, + 0xff,0xad,0x7a,0x8c,0x34,0x1a,0x49,0xc5, + 0xd2,0xdc,0xe7,0x06,0x9e,0x46,0x4c,0xb9, + 0x0a,0x00,0xb2,0x90,0x36,0x48,0xb2,0x3c, + 0x81,0xa6,0x8e,0x21,0xd7,0x48,0xa7,0xe7, + 0xb1,0xdf,0x8a,0x59,0x3f,0x38,0x94,0xb2, + 0x47,0x7e,0x83,0x16,0x94,0x7c,0xa7,0x25, + 0xd1,0x41,0x13,0x52,0x02,0xa9,0x44,0x2e, + 0x1d,0xb3,0x3b,0xbd,0x39,0x0d,0x2c,0x04, + 0x40,0x1c,0x39,0xb2,0x53,0xb7,0x8c,0xe2, + 0x97,0xb0,0xe1,0x47,0x55,0xe4,0x6e,0xc0, + 0x8a,0x14,0x6d,0x27,0x9c,0x67,0xaf,0x70, + 0xde,0x25,0x68,0x90,0x80,0x4d,0x83,0xd6, + 0xec,0x5c,0xa3,0x28,0x6f,0x1f,0xca,0x9c, + 0x72,0xab,0xf6,0xef,0x86,0x8e,0x7f,0x6e, + 0xb0,0xfd,0xdd,0xa1,0xb0,0x40,0xec,0xec, + 0x9b,0xbc,0x69,0xe2,0xfd,0x86,0x18,0xe9, + 0xdb,0x3b,0xdb,0x0a,0xf1,0x3d,0xda,0x06, + 0xc6,0x61,0x7e,0x95,0xaf,0xa5,0x22,0xd6, + 0xa2,0x55,0x2d,0xe1,0x53,0x24,0xd9,0x91, + 0x19,0xf5,0x5e,0x9a,0xf1,0x1a,0xe3,0xd5, + 0x61,0x4b,0x56,0x4c,0x64,0x2d,0xbf,0xec, + 0x6c,0x64,0x41,0x98,0xce,0x80,0xd2,0x43, + 0x3a,0xc8,0xee,0x73,0x8f,0x9d,0x82,0x5e, + 0xe0,0x00,0x00,0x06, + 0x71,0xd5,0x85,0xa3,0x5c,0x3a,0x90,0x83, + 0x79,0xf4,0x07,0x2d,0x07,0x03,0x11,0xdb, + 0x5d,0x65,0xb2,0x42,0xb7,0x14,0xbc,0x5a, + 0x75,0x6b,0xa5,0xe2,0x28,0xab,0xfa,0x0d, + 0x13,0x29,0x97,0x8a,0x05,0xd5,0xe8,0x15, + 0xcf,0x4d,0x74,0xc1,0xe5,0x47,0xec,0x4a, + 0xa3,0xca,0x95,0x6a,0xe9,0x27,0xdf,0x8b, + 0x29,0xfb,0x9f,0xab,0x39,0x17,0xa7,0xa4, + 0xae,0x61,0xba,0x57,0xe5,0x34,0x2e,0x9d, + 0xb1,0x2c,0xaf,0x6f,0x6d,0xbc,0x52,0x53, + 0xde,0x52,0x68,0xd4,0xb0,0xc4,0xce,0x4e, + 0xbe,0x68,0x52,0xf0,0x12,0xb1,0x62,0xfc, + 0x1c,0x12,0xb9,0xff,0xc3,0xbc,0xb1,0xd3, + 0xac,0x85,0x89,0x77,0x76,0x55,0xe2,0x2c, + 0xd9,0xb9,0x9f,0xf1,0xe4,0x34,0x6f,0xd0, + 0xef,0xea,0xa1,0xda,0x04,0x46,0x92,0xe7, + 0xad,0x6b,0xfc,0x33,0x7d,0xb6,0x98,0x49, + 0xe5,0x44,0x11,0xdf,0x89,0x20,0xc2,0x28, + 0xa2,0xb7,0x76,0x2c,0x11,0xe4,0xb1,0xc4, + 0x9e,0xfb,0x74,0x48,0x6d,0x39,0x31,0xea, + }; + + bool verified = hss_validate_signature( + public_key, + message, sizeof message, + signature, sizeof signature, 0); + + return verified; +} + bool test_testvector(bool fast_flag, bool quiet_flag) { return test_testvector_1() && test_testvector_2() && - test_testvector_3(); + test_testvector_3() && + test_testvector_4() && + test_testvector_5(); } diff --git a/test_verify.c b/test_verify.c index 52f9dfc..9eeee61 100644 --- a/test_verify.c +++ b/test_verify.c @@ -10,6 +10,8 @@ static param_set_t h_array[] = { LMS_SHA256_N24_H5, LMS_SHA256_N32_H5, + LMS_SHAKE256_N24_H5, + LMS_SHAKE256_N32_H5, LMS_SHA256_N24_H10, /* We don't test out the higher heights, because that'd take too */ /* long, and wouldn't tell us that much for this test */ @@ -25,16 +27,37 @@ static param_set_t w_array[] = { LMOTS_SHA256_N24_W4, LMOTS_SHA256_N32_W8, LMOTS_SHA256_N24_W8, + LMOTS_SHAKE256_N32_W1, + LMOTS_SHAKE256_N24_W1, + LMOTS_SHAKE256_N32_W2, + LMOTS_SHAKE256_N24_W2, + LMOTS_SHAKE256_N32_W4, + LMOTS_SHAKE256_N24_W4, + LMOTS_SHAKE256_N32_W8, + LMOTS_SHAKE256_N24_W8, }; #define MAX_W_INDEX (sizeof w_array / sizeof *w_array ) /* This is (roughly) the number of hash compression operatios needed to */ -/* compute various OTS verifications. Really off by a factor of two; */ -/* however that factor of two is consistent */ -int cost_per_sig[4] = { +/* compute various OTS verifications. This ignores a number of factors */ +/* (SHA-256 vs SHAKE computations), however it should be within a couple */ +/* orders of magnitude */ +int cost_per_sig[MAX_W_INDEX] = { (1<<1) * 265, + (1<<1) * 201, (1<<2) * 133, - (1<<4) * 133, + (1<<2) * 101, + (1<<4) * 67, + (1<<4) * 51, (1<<8) * 34, + (1<<8) * 26, + (1<<1) * 265, + (1<<1) * 201, + (1<<2) * 133, + (1<<2) * 101, + (1<<4) * 67, + (1<<4) * 51, + (1<<8) * 34, + (1<<8) * 26, }; static bool do_verify( unsigned char *private_key, unsigned char *public_key, @@ -72,13 +95,20 @@ bool test_verify(bool fast_flag, bool quiet_flag) { param_set_t h = h_array[h_index]; param_set_t w = w_array[w_index]; /* Flag is set if we're testing out a W=8 parameter set */ - int w8 = (w == LMOTS_SHA256_N32_W8 || w == LMOTS_SHA256_N24_W8); + int w8 = (w == LMOTS_SHA256_N32_W8 || w == LMOTS_SHA256_N24_W8 || + w == LMOTS_SHAKE256_N32_W8 || + w == LMOTS_SHAKE256_N24_W8); + /* Flag is set if we're testing out a W=4 parameter set */ + int w4 = (w == LMOTS_SHA256_N32_W4 || w == LMOTS_SHA256_N24_W4 || + w == LMOTS_SHAKE256_N32_W4 || + w == LMOTS_SHAKE256_N24_W4); /* Note: this particular combination takes longer than the */ /* rest combined; it wouldn't tell us much more, so skip it */ if (h == LMS_SHA256_N24_H10 && w8) continue; /* In fast mode, we both testing out W=8 only for d=1 */ if (fast_flag && d > 1 && w8) continue; + if (fast_flag && d > 2 && w4) continue; work_array[w_count].d = max_d = d; work_array[w_count].h = h; diff --git a/test_verify_inc.c b/test_verify_inc.c index 329fe1f..0d108a9 100644 --- a/test_verify_inc.c +++ b/test_verify_inc.c @@ -230,6 +230,16 @@ bool test_verify_inc(bool fast_flag, bool quiet_flag) { if (!do_test( fast_flag, max_d, lm_array, lm_ots_array)) return false; } + { + static param_set_t lm_array[MAX_D] = { + LMS_SHAKE256_N24_H10, LMS_SHAKE256_N24_H5, + LMS_SHAKE256_N24_H5, LMS_SHAKE256_N24_H5 }; + static param_set_t lm_ots_array[MAX_D] = { + LMOTS_SHAKE256_N24_W4, LMOTS_SHAKE256_N24_W4, + LMOTS_SHAKE256_N24_W4, LMOTS_SHAKE256_N24_W4 }; + if (!do_test( fast_flag, max_d, lm_array, lm_ots_array)) + return false; + } if (!fast_flag) { static param_set_t lm_array[MAX_D] = { @@ -242,5 +252,16 @@ bool test_verify_inc(bool fast_flag, bool quiet_flag) { return false; } + if (!fast_flag) { + static param_set_t lm_array[MAX_D] = { + LMS_SHAKE256_N32_H10, LMS_SHAKE256_N32_H5, + LMS_SHAKE256_N32_H5, LMS_SHAKE256_N32_H5 }; + static param_set_t lm_ots_array[MAX_D] = { + LMOTS_SHAKE256_N32_W4, LMOTS_SHAKE256_N32_W4, + LMOTS_SHAKE256_N32_W4, LMOTS_SHAKE256_N32_W4 }; + if (!do_test( fast_flag, max_d, lm_array, lm_ots_array)) + return false; + } + return true; } From b411488daa1e044e7d3fffa67fa0a4268a76987e Mon Sep 17 00:00:00 2001 From: sfluhrer Date: Thu, 15 Apr 2021 10:31:18 -0400 Subject: [PATCH 2/5] Update README --- README | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README b/README index 6ab1c64..283400a 100644 --- a/README +++ b/README @@ -1,10 +1,6 @@ This code attempts to be a usable implementation of the LMS Hash Based Signature Scheme from RFC 8554. -This branch includes the SHA256/192 hashes from draft-fluhrer-lms-more-parm-sets; -currently, it does not include the SHAKE-based ones. +This branch includes the parameter sets from draft-fluhrer-lms-more-parm-sets. See read.me for documentation how to use it. - -This is the ACVP branch - designed to be (optionally) compatible with the -public ACVP server From 33e9768e5f128783edfdcd7ae228bca373292e9d Mon Sep 17 00:00:00 2001 From: sfluhrer Date: Thu, 15 Apr 2021 13:07:53 -0400 Subject: [PATCH 3/5] Correct the shake block size --- hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hash.c b/hash.c index c242aca..efc9d03 100644 --- a/hash.c +++ b/hash.c @@ -165,7 +165,7 @@ unsigned hss_hash_blocksize(int hash_type) { /* We call this function in order to do HMAC; doing HMAC on SHAKE */ /* is not usual (KMAC is preferred), however we fill in a value */ /* anyways */ - case HASH_SHAKE256: case HASH_SHAKE256_24: return 200; + case HASH_SHAKE256: case HASH_SHAKE256_24: return 136; } return 0; } From 846bc77206282fa51ced981810e56f75298a4340 Mon Sep 17 00:00:00 2001 From: sfluhrer Date: Fri, 16 Apr 2021 15:14:02 -0400 Subject: [PATCH 4/5] Corrected read.me --- read.me | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/read.me b/read.me index d334055..625078f 100644 --- a/read.me +++ b/read.me @@ -1,5 +1,5 @@ This code attempts to be a usable implementation of of the LMS Hash Based -Signature Scheme from RFC 8554. +Signature Scheme from RFC 8554 (Leighton-Micali Hash-Based Signatures). This is a signature scheme (that is, someone with the private key can sign messages, someone with the public key can verify sigantures, but cannot @@ -161,12 +161,13 @@ feature-centric order, rather than a problem-centric one: private key into three parts: - A short section which is meant to be saved in secure storage (and this is the part which is referred to as the 'private key'; this includes the - number of signatures generated so far. It's currently 48 bytes long + number of signatures generated so far. It's currently 56-72 bytes long (with the part that needs to be actually updated only 8 bytes), we assume - that it is updated atomically. These 48 bytes consist of an 8 byte count - of the number of sigantures generated so far, 8 bytes of a compressed - version of the parameter set, and 32 bytes of 'seed' (a random value that - is used to derive every secret) + that it is updated atomically. These 56-72 bytes consist of an 8 byte + count of the number of sigantures generated so far, 16 bytes of a + compressed version of the parameter set, and 32 bytes of 'seed' (a random + value that is used to derive every secret), and optionally 16 bytes of the + root I value. - A longer section (the "working key") that resides in memory; we assume that this is private, but need not be saved into long term (nonvolatile) storage. This section include the Merkle tree contents (actually, a @@ -272,7 +273,8 @@ real applications really ought not do. General notes: - Advice about parameter sets: while this supports all the HSS parameter sets - currently allowed in draft-mcgrew-hash-sigs-08, some work better than others + currently allowed in RFC 8554 and draft-fluhrer-more-parm-sets, some work + better than others. The general recommendation is to use a few large trees (that is, with lots of levels), rather than using a number of levels of smaller trees; this package tries to make large trees not that costly (and reducing the number @@ -396,6 +398,14 @@ General notes: struct hss_extra_info info; hss_init_extra_info( &info ); +- This library supports the parameter sets of RFC 8554 and + draft-fluhrer-lms-more-parm-sets. Personally, the 192-bit SHA-256 + parameter sets are nice (as it reduces the signature size quite a bit). On + the other hand, I personally don't see a great deal of benefit to the + SHAKE-based parameter sets (unless you don't trust the cryptographical + strength of SHA-256). On my machine (with no special SHA-256 or SHAKE + instructions), the SHAKE parameter sets tested at over 4 times slower. + Future ideas; I'm not against future ideas, but this package is getting sufficiently complex that I'm not likely to do anything that adds even more complexity unless someone has a real need. If you do have a real need (for From deef02bd88df558c1a88dd3db362891aab19b047 Mon Sep 17 00:00:00 2001 From: sfluhrer Date: Tue, 11 Jan 2022 09:46:46 -0500 Subject: [PATCH 5/5] Use IANA-assigned code points --- common_defs.h | 54 +++++++++++++++++++++++------------------------ programming.notes | 4 ++-- read.me | 2 +- test_testvector.c | 24 ++++++++++----------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/common_defs.h b/common_defs.h index 80a99b0..831420c 100644 --- a/common_defs.h +++ b/common_defs.h @@ -60,21 +60,21 @@ typedef uint_fast64_t sequence_t; #define LMS_SHA256_N32_H15 0x00000007 #define LMS_SHA256_N32_H20 0x00000008 #define LMS_SHA256_N32_H25 0x00000009 -#define LMS_SHA256_N24_H5 0xe0000001 -#define LMS_SHA256_N24_H10 0xe0000002 -#define LMS_SHA256_N24_H15 0xe0000003 -#define LMS_SHA256_N24_H20 0xe0000004 -#define LMS_SHA256_N24_H25 0xe0000005 -#define LMS_SHAKE256_N32_H5 0xe0000006 -#define LMS_SHAKE256_N32_H10 0xe0000007 -#define LMS_SHAKE256_N32_H15 0xe0000008 -#define LMS_SHAKE256_N32_H20 0xe0000009 -#define LMS_SHAKE256_N32_H25 0xe000000a -#define LMS_SHAKE256_N24_H5 0xe000000b -#define LMS_SHAKE256_N24_H10 0xe000000c -#define LMS_SHAKE256_N24_H15 0xe000000d -#define LMS_SHAKE256_N24_H20 0xe000000e -#define LMS_SHAKE256_N24_H25 0xe000000f +#define LMS_SHA256_N24_H5 0x0000000a +#define LMS_SHA256_N24_H10 0x0000000b +#define LMS_SHA256_N24_H15 0x0000000c +#define LMS_SHA256_N24_H20 0x0000000d +#define LMS_SHA256_N24_H25 0x0000000e +#define LMS_SHAKE256_N32_H5 0x0000000f +#define LMS_SHAKE256_N32_H10 0x00000010 +#define LMS_SHAKE256_N32_H15 0x00000011 +#define LMS_SHAKE256_N32_H20 0x00000012 +#define LMS_SHAKE256_N32_H25 0x00000013 +#define LMS_SHAKE256_N24_H5 0x00000014 +#define LMS_SHAKE256_N24_H10 0x00000015 +#define LMS_SHAKE256_N24_H15 0x00000016 +#define LMS_SHAKE256_N24_H20 0x00000017 +#define LMS_SHAKE256_N24_H25 0x00000018 /* LM-OTS registry */ @@ -82,18 +82,18 @@ typedef uint_fast64_t sequence_t; #define LMOTS_SHA256_N32_W2 0x00000002 #define LMOTS_SHA256_N32_W4 0x00000003 #define LMOTS_SHA256_N32_W8 0x00000004 -#define LMOTS_SHA256_N24_W1 0xe0000001 -#define LMOTS_SHA256_N24_W2 0xe0000002 -#define LMOTS_SHA256_N24_W4 0xe0000003 -#define LMOTS_SHA256_N24_W8 0xe0000004 -#define LMOTS_SHAKE256_N32_W1 0xe0000005 -#define LMOTS_SHAKE256_N32_W2 0xe0000006 -#define LMOTS_SHAKE256_N32_W4 0xe0000007 -#define LMOTS_SHAKE256_N32_W8 0xe0000008 -#define LMOTS_SHAKE256_N24_W1 0xe0000009 -#define LMOTS_SHAKE256_N24_W2 0xe000000a -#define LMOTS_SHAKE256_N24_W4 0xe000000b -#define LMOTS_SHAKE256_N24_W8 0xe000000c +#define LMOTS_SHA256_N24_W1 0x00000005 +#define LMOTS_SHA256_N24_W2 0x00000006 +#define LMOTS_SHA256_N24_W4 0x00000007 +#define LMOTS_SHA256_N24_W8 0x00000008 +#define LMOTS_SHAKE256_N32_W1 0x00000009 +#define LMOTS_SHAKE256_N32_W2 0x0000000a +#define LMOTS_SHAKE256_N32_W4 0x0000000b +#define LMOTS_SHAKE256_N32_W8 0x0000000c +#define LMOTS_SHAKE256_N24_W1 0x0000000d +#define LMOTS_SHAKE256_N24_W2 0x0000000e +#define LMOTS_SHAKE256_N24_W4 0x0000000f +#define LMOTS_SHAKE256_N24_W8 0x00000010 /* diff --git a/programming.notes b/programming.notes index 30e980a..a88809c 100644 --- a/programming.notes +++ b/programming.notes @@ -551,7 +551,7 @@ sources. common_defs.h This is a central spot to put definitions of general interest of the entire subsystem demo.c This is an example program that uses this subsystem; it - implements a simple file signer. Note: becauSe it + implements a simple file signer. Note: because it doesn't get that great of randomness (due to the need to restrict ourselves to standard C), it probably shouldn't be used as is. It does try to use @@ -661,7 +661,7 @@ sources. USE_OPENSSL is 0. This is provided in case you don't have OpenSSL available. sha256.h Routine that computes the SHA-256 hash. This is the - same interface that OpenSSL presents. We alsO + same interface that OpenSSL presents. We also include a #define (USE_OPENSSL); if 1, these are direct calls to OpenSSL; if 0, we use our own implementation (in case you don't have OpenSSL diff --git a/read.me b/read.me index 625078f..c61177c 100644 --- a/read.me +++ b/read.me @@ -399,7 +399,7 @@ General notes: hss_init_extra_info( &info ); - This library supports the parameter sets of RFC 8554 and - draft-fluhrer-lms-more-parm-sets. Personally, the 192-bit SHA-256 + draft-fluhrer-lms-more-parm-sets. Personally, I think the 192-bit SHA-256 parameter sets are nice (as it reduces the signature size quite a bit). On the other hand, I personally don't see a great deal of benefit to the SHAKE-based parameter sets (unless you don't trust the cryptographical diff --git a/test_testvector.c b/test_testvector.c index 89c599f..9afa4d3 100644 --- a/test_testvector.c +++ b/test_testvector.c @@ -917,8 +917,8 @@ bool test_testvector_3(void) { /* This is test case 3 */ /* This is the SHA256/192 test from draft-fluhrer-lms-more-parm_sets */ static const unsigned char public_key[] = { - 0x00,0x00,0x00,0x01,0xe0,0x00,0x00,0x01, - 0xe0,0x00,0x00,0x04, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a, + 0x00,0x00,0x00,0x08, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x2c,0x57,0x14,0x50,0xae,0xd9,0x9c,0xfb, @@ -933,7 +933,7 @@ bool test_testvector_3(void) { }; static const unsigned char signature[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05, - 0xe0,0x00,0x00,0x04, + 0x00,0x00,0x00,0x08, 0x0b,0x50,0x40,0xa1,0x8c,0x1b,0x5c,0xab, 0xcb,0xc8,0x5b,0x04,0x74,0x02,0xec,0x62, 0x94,0xa3,0x0d,0xd8,0xda,0x8f,0xc3,0xda, @@ -1015,7 +1015,7 @@ bool test_testvector_3(void) { 0xef,0x3a,0x54,0xc4,0x15,0x5f,0xe3,0xad, 0x81,0x77,0x49,0x62,0x9c,0x30,0xad,0xbe, 0x89,0x7c,0x4f,0x44,0x54,0xc8,0x6c,0x49, - 0xe0,0x00,0x00,0x01, + 0x00,0x00,0x00,0x0a, 0xe9,0xca,0x10,0xea,0xa8,0x11,0xb2,0x2a, 0xe0,0x7f,0xb1,0x95,0xe3,0x59,0x0a,0x33, 0x4e,0xa6,0x42,0x09,0x94,0x2f,0xba,0xe3, @@ -1045,8 +1045,8 @@ bool test_testvector_4(void) { /* This is test case 4 */ /* This is the SHAKE256/192 test from draft-fluhrer-lms-more-parm_sets */ static const unsigned char public_key[] = { - 0x00,0x00,0x00,0x01,0xe0,0x00,0x00,0x0b, - 0xe0,0x00,0x00,0x0c, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14, + 0x00,0x00,0x00,0x10, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0xdb,0x54,0xa4,0x50,0x99,0x01,0x05,0x1c, @@ -1061,7 +1061,7 @@ bool test_testvector_4(void) { }; static const unsigned char signature[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06, - 0xe0,0x00,0x00,0x0c, + 0x00,0x00,0x00,0x10, 0x84,0x21,0x9d,0xa9,0xce,0x9f,0xff,0xb1, 0x6e,0xdb,0x94,0x52,0x7c,0x6d,0x10,0x56, 0x55,0x87,0xdb,0x28,0x06,0x2d,0xea,0xc4, @@ -1143,7 +1143,7 @@ bool test_testvector_4(void) { 0xea,0xc9,0x06,0x26,0x08,0xc7,0xd8,0x77, 0x08,0xf1,0x72,0x3f,0x38,0xb2,0x32,0x37, 0xa4,0xed,0xf4,0xb4,0x9a,0x5c,0xd3,0xd7, - 0xe0,0x00,0x00,0x0b, + 0x00,0x00,0x00,0x14, 0xdd,0x4b,0xdc,0x8f,0x92,0x8f,0xb5,0x26, 0xf6,0xfb,0x7c,0xdb,0x94,0x4a,0x7e,0xba, 0xa7,0xfb,0x05,0xd9,0x95,0xb5,0x72,0x1a, @@ -1173,8 +1173,8 @@ bool test_testvector_5(void) { /* This is test case 5 */ /* This is the SHAKE256/256 test from draft-fluhrer-lms-more-parm_sets */ static const unsigned char public_key[] = { - 0x00,0x00,0x00,0x01,0xe0,0x00,0x00,0x06, - 0xe0,0x00,0x00,0x08, + 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f, + 0x00,0x00,0x00,0x0c, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 0x9b,0xb7,0xfa,0xee,0x41,0x1c,0xae,0x80, @@ -1190,7 +1190,7 @@ bool test_testvector_5(void) { }; static const unsigned char signature[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07, - 0xe0,0x00,0x00,0x08, + 0x00,0x00,0x00,0x0c, 0xb8,0x27,0x09,0xf0,0xf0,0x0e,0x83,0x75, 0x91,0x90,0x99,0x62,0x33,0xd1,0xee,0x4f, 0x4e,0xc5,0x05,0x34,0x47,0x3c,0x02,0xff, @@ -1331,7 +1331,7 @@ bool test_testvector_5(void) { 0x61,0x4b,0x56,0x4c,0x64,0x2d,0xbf,0xec, 0x6c,0x64,0x41,0x98,0xce,0x80,0xd2,0x43, 0x3a,0xc8,0xee,0x73,0x8f,0x9d,0x82,0x5e, - 0xe0,0x00,0x00,0x06, + 0x00,0x00,0x00,0x0f, 0x71,0xd5,0x85,0xa3,0x5c,0x3a,0x90,0x83, 0x79,0xf4,0x07,0x2d,0x07,0x03,0x11,0xdb, 0x5d,0x65,0xb2,0x42,0xb7,0x14,0xbc,0x5a,