From 3e28db2362f25600699972766e7782635b1826f5 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 28 Apr 2020 10:02:06 -0400 Subject: [PATCH] Improved key generation In the public comments to draft version of NIST Special Publication 800-208, ETSI TC CYBER WG QSC identified a multi-target attack against the method of pseudorandom key generation used in this referrence implementation. ETSI TC CYBER WG QSC suggested using the pseudorandom key generation method from SPHINCS+, however, there is still a multi-user attack against that key generation method. This commit revises the pseudorandom key generation method by using the method from SPINCS+, but adding SEED as an input in order to protect against multi-user attacks. Since prf() only accepts 32-byte inputs, the new key generation method uses a new PRF. The resulting key generation method is sk[i] = prf_keygen(sk_seed, pub_seed || adrs). --- hash.c | 18 ++++++++++++++++++ hash.h | 4 ++++ wots.c | 19 ++++++++++++------- xmss_commons.c | 24 +----------------------- xmss_commons.h | 9 --------- xmss_core.c | 6 +----- xmss_core_fast.c | 19 ++++--------------- 7 files changed, 40 insertions(+), 59 deletions(-) diff --git a/hash.c b/hash.c index 7b49f6a..392ab92 100644 --- a/hash.c +++ b/hash.c @@ -12,6 +12,7 @@ #define XMSS_HASH_PADDING_H 1 #define XMSS_HASH_PADDING_HASH 2 #define XMSS_HASH_PADDING_PRF 3 +#define XMSS_HASH_PADDING_PRF_KEYGEN 4 void addr_to_bytes(unsigned char *bytes, const uint32_t addr[8]) { @@ -59,6 +60,23 @@ int prf(const xmss_params *params, return core_hash(params, out, buf, params->padding_len + params->n + 32); } +/* + * Computes PRF_keygen(key, in), for a key of params->n bytes, and an input + * of 32 + params->n bytes + */ +int prf_keygen(const xmss_params *params, + unsigned char *out, const unsigned char *in, + const unsigned char *key) +{ + unsigned char buf[params->padding_len + 2*params->n + 32]; + + ull_to_bytes(buf, params->padding_len, XMSS_HASH_PADDING_PRF_KEYGEN); + memcpy(buf + params->padding_len, key, params->n); + memcpy(buf + params->padding_len + params->n, in, params->n + 32); + + return core_hash(params, out, buf, params->padding_len + 2*params->n + 32); +} + /* * Computes the message hash using R, the public root, the index of the leaf * node, and the message. Notably, it requires m_with_prefix to have 4*n bytes diff --git a/hash.h b/hash.h index 0c95043..6e13d12 100644 --- a/hash.h +++ b/hash.h @@ -10,6 +10,10 @@ int prf(const xmss_params *params, unsigned char *out, const unsigned char in[32], const unsigned char *key); +int prf_keygen(const xmss_params *params, + unsigned char *out, const unsigned char *in, + const unsigned char *key); + int h_msg(const xmss_params *params, unsigned char *out, const unsigned char *in, unsigned long long inlen, diff --git a/wots.c b/wots.c index 9ce96da..d9c8449 100644 --- a/wots.c +++ b/wots.c @@ -9,17 +9,22 @@ /** * Helper method for pseudorandom key generation. - * Expands an n-byte array into a len*n byte array using the `prf` function. + * Expands an n-byte array into a len*n byte array using the `prf_keygen` function. */ static void expand_seed(const xmss_params *params, - unsigned char *outseeds, const unsigned char *inseed) + unsigned char *outseeds, const unsigned char *inseed, + const unsigned char *pub_seed, uint32_t addr[8]) { uint32_t i; - unsigned char ctr[32]; + unsigned char buf[params->n + 32]; + set_hash_addr(addr, 0); + set_key_and_mask(addr, 0); + memcpy(buf, pub_seed, params->n); for (i = 0; i < params->wots_len; i++) { - ull_to_bytes(ctr, 32, i); - prf(params, outseeds + i*params->n, ctr, inseed); + set_chain_addr(addr, i); + addr_to_bytes(buf + params->n, addr); + prf_keygen(params, outseeds + i*params->n, buf, inseed); } } @@ -116,7 +121,7 @@ void wots_pkgen(const xmss_params *params, uint32_t i; /* The WOTS+ private key is derived from the seed. */ - expand_seed(params, pk, seed); + expand_seed(params, pk, seed, pub_seed, addr); for (i = 0; i < params->wots_len; i++) { set_chain_addr(addr, i); @@ -140,7 +145,7 @@ void wots_sign(const xmss_params *params, chain_lengths(params, lengths, msg); /* The WOTS+ private key is derived from the seed. */ - expand_seed(params, sig, seed); + expand_seed(params, sig, seed, pub_seed, addr); for (i = 0; i < params->wots_len; i++) { set_chain_addr(addr, i); diff --git a/xmss_commons.c b/xmss_commons.c index c20b91d..a8108e7 100644 --- a/xmss_commons.c +++ b/xmss_commons.c @@ -105,35 +105,13 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf, const unsigned char *sk_seed, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8]) { - unsigned char seed[params->n]; unsigned char pk[params->wots_sig_bytes]; - get_seed(params, seed, sk_seed, ots_addr); - wots_pkgen(params, pk, seed, pub_seed, ots_addr); + wots_pkgen(params, pk, sk_seed, pub_seed, ots_addr); l_tree(params, leaf, pk, pub_seed, ltree_addr); } -/** - * Used for pseudo-random key generation. - * Generates the seed for the WOTS key pair at address 'addr'. - * - * Takes n-byte sk_seed and returns n-byte seed using 32 byte address 'addr'. - */ -void get_seed(const xmss_params *params, unsigned char *seed, - const unsigned char *sk_seed, uint32_t addr[8]) -{ - unsigned char bytes[32]; - - /* Make sure that chain addr, hash addr, and key bit are zeroed. */ - set_chain_addr(addr, 0); - set_hash_addr(addr, 0); - set_key_and_mask(addr, 0); - - /* Generate seed. */ - addr_to_bytes(bytes, addr); - prf(params, seed, bytes, sk_seed); -} /** * Verifies a given message signature pair under a given public key. diff --git a/xmss_commons.h b/xmss_commons.h index 498ec5e..9d9f077 100644 --- a/xmss_commons.h +++ b/xmss_commons.h @@ -13,15 +13,6 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf, const unsigned char *sk_seed, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8]); -/** - * Used for pseudo-random key generation. - * Generates the seed for the WOTS key pair at address 'addr'. - * - * Takes n-byte sk_seed and returns n-byte seed using 32 byte address 'addr'. - */ -void get_seed(const xmss_params *params, unsigned char *seed, - const unsigned char *sk_seed, uint32_t addr[8]); - /** * Verifies a given message signature pair under a given public key. * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] diff --git a/xmss_core.c b/xmss_core.c index ee01562..6f159dc 100644 --- a/xmss_core.c +++ b/xmss_core.c @@ -174,7 +174,6 @@ int xmssmt_core_sign(const xmss_params *params, unsigned char root[params->n]; unsigned char *mhash = root; - unsigned char ots_seed[params->n]; unsigned long long idx; unsigned char idx_bytes_32[32]; unsigned int i; @@ -217,13 +216,10 @@ int xmssmt_core_sign(const xmss_params *params, set_tree_addr(ots_addr, idx); set_ots_addr(ots_addr, idx_leaf); - /* Get a seed for the WOTS keypair. */ - get_seed(params, ots_seed, sk_seed, ots_addr); - /* Compute a WOTS signature. */ /* Initially, root = mhash, but on subsequent iterations it is the root of the subtree below the currently processed subtree. */ - wots_sign(params, sm, root, ots_seed, pub_seed, ots_addr); + wots_sign(params, sm, root, sk_seed, pub_seed, ots_addr); sm += params->wots_sig_bytes; /* Compute the authentication path for the used WOTS leaf. */ diff --git a/xmss_core_fast.c b/xmss_core_fast.c index b85d6d2..835658c 100644 --- a/xmss_core_fast.c +++ b/xmss_core_fast.c @@ -623,7 +623,6 @@ int xmss_core_sign(const xmss_params *params, // Init working params unsigned char R[params->n]; unsigned char msg_h[params->n]; - unsigned char ots_seed[params->n]; uint32_t ots_addr[8] = {0}; // --------------------------------- @@ -670,11 +669,8 @@ int xmss_core_sign(const xmss_params *params, set_type(ots_addr, 0); set_ots_addr(ots_addr, idx); - // Compute seed for OTS key pair - get_seed(params, ots_seed, sk_seed, ots_addr); - // Compute WOTS signature - wots_sign(params, sm, msg_h, ots_seed, pub_seed, ots_addr); + wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr); sm += params->wots_sig_bytes; *smlen += params->wots_sig_bytes; @@ -707,7 +703,6 @@ int xmss_core_sign(const xmss_params *params, int xmssmt_core_keypair(const xmss_params *params, unsigned char *pk, unsigned char *sk) { - unsigned char ots_seed[params->n]; uint32_t addr[8] = {0}; unsigned int i; unsigned char *wots_sigs; @@ -745,8 +740,7 @@ int xmssmt_core_keypair(const xmss_params *params, // Compute seed for OTS key pair treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); set_layer_addr(addr, (i+1)); - get_seed(params, ots_seed, sk + params->index_bytes, addr); - wots_sign(params, wots_sigs + i*params->wots_sig_bytes, pk, ots_seed, pk+params->n, addr); + wots_sign(params, wots_sigs + i*params->wots_sig_bytes, pk, sk + params->index_bytes, pk+params->n, addr); } // Address now points to the single tree on layer d-1 treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); @@ -783,7 +777,6 @@ int xmssmt_core_sign(const xmss_params *params, // Init working params unsigned char R[params->n]; unsigned char msg_h[params->n]; - unsigned char ots_seed[params->n]; uint32_t addr[8] = {0}; uint32_t ots_addr[8] = {0}; unsigned char idx_bytes_32[32]; @@ -867,11 +860,8 @@ int xmssmt_core_sign(const xmss_params *params, set_tree_addr(ots_addr, idx_tree); set_ots_addr(ots_addr, idx_leaf); - // Compute seed for OTS key pair - get_seed(params, ots_seed, sk_seed, ots_addr); - // Compute WOTS signature - wots_sign(params, sm, msg_h, ots_seed, pub_seed, ots_addr); + wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr); sm += params->wots_sig_bytes; *smlen += params->wots_sig_bytes; @@ -929,8 +919,7 @@ int xmssmt_core_sign(const xmss_params *params, set_tree_addr(ots_addr, ((idx + 1) >> ((i+2) * params->tree_height))); set_ots_addr(ots_addr, (((idx >> ((i+1) * params->tree_height)) + 1) & ((1 << params->tree_height)-1))); - get_seed(params, ots_seed, sk+params->index_bytes, ots_addr); - wots_sign(params, wots_sigs + i*params->wots_sig_bytes, states[i].stack, ots_seed, pub_seed, ots_addr); + wots_sign(params, wots_sigs + i*params->wots_sig_bytes, states[i].stack, sk_seed, pub_seed, ots_addr); states[params->d + i].stackoffset = 0; states[params->d + i].next_leaf = 0;