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).master
@@ -12,6 +12,7 @@ | |||||
#define XMSS_HASH_PADDING_H 1 | #define XMSS_HASH_PADDING_H 1 | ||||
#define XMSS_HASH_PADDING_HASH 2 | #define XMSS_HASH_PADDING_HASH 2 | ||||
#define XMSS_HASH_PADDING_PRF 3 | #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]) | 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); | 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 | * 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 | * node, and the message. Notably, it requires m_with_prefix to have 4*n bytes | ||||
@@ -10,6 +10,10 @@ int prf(const xmss_params *params, | |||||
unsigned char *out, const unsigned char in[32], | unsigned char *out, const unsigned char in[32], | ||||
const unsigned char *key); | 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, | int h_msg(const xmss_params *params, | ||||
unsigned char *out, | unsigned char *out, | ||||
const unsigned char *in, unsigned long long inlen, | const unsigned char *in, unsigned long long inlen, | ||||
@@ -9,17 +9,22 @@ | |||||
/** | /** | ||||
* Helper method for pseudorandom key generation. | * 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, | 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; | 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++) { | 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; | uint32_t i; | ||||
/* The WOTS+ private key is derived from the seed. */ | /* 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++) { | for (i = 0; i < params->wots_len; i++) { | ||||
set_chain_addr(addr, i); | set_chain_addr(addr, i); | ||||
@@ -140,7 +145,7 @@ void wots_sign(const xmss_params *params, | |||||
chain_lengths(params, lengths, msg); | chain_lengths(params, lengths, msg); | ||||
/* The WOTS+ private key is derived from the seed. */ | /* 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++) { | for (i = 0; i < params->wots_len; i++) { | ||||
set_chain_addr(addr, i); | set_chain_addr(addr, i); | ||||
@@ -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, | const unsigned char *sk_seed, const unsigned char *pub_seed, | ||||
uint32_t ltree_addr[8], uint32_t ots_addr[8]) | uint32_t ltree_addr[8], uint32_t ots_addr[8]) | ||||
{ | { | ||||
unsigned char seed[params->n]; | |||||
unsigned char pk[params->wots_sig_bytes]; | 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); | 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. | * Verifies a given message signature pair under a given public key. | ||||
@@ -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, | const unsigned char *sk_seed, const unsigned char *pub_seed, | ||||
uint32_t ltree_addr[8], uint32_t ots_addr[8]); | 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. | * Verifies a given message signature pair under a given public key. | ||||
* Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] | * Note that this assumes a pk without an OID, i.e. [root || PUB_SEED] | ||||
@@ -174,7 +174,6 @@ int xmssmt_core_sign(const xmss_params *params, | |||||
unsigned char root[params->n]; | unsigned char root[params->n]; | ||||
unsigned char *mhash = root; | unsigned char *mhash = root; | ||||
unsigned char ots_seed[params->n]; | |||||
unsigned long long idx; | unsigned long long idx; | ||||
unsigned char idx_bytes_32[32]; | unsigned char idx_bytes_32[32]; | ||||
unsigned int i; | unsigned int i; | ||||
@@ -217,13 +216,10 @@ int xmssmt_core_sign(const xmss_params *params, | |||||
set_tree_addr(ots_addr, idx); | set_tree_addr(ots_addr, idx); | ||||
set_ots_addr(ots_addr, idx_leaf); | 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. */ | /* Compute a WOTS signature. */ | ||||
/* Initially, root = mhash, but on subsequent iterations it is the root | /* Initially, root = mhash, but on subsequent iterations it is the root | ||||
of the subtree below the currently processed subtree. */ | 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; | sm += params->wots_sig_bytes; | ||||
/* Compute the authentication path for the used WOTS leaf. */ | /* Compute the authentication path for the used WOTS leaf. */ | ||||
@@ -623,7 +623,6 @@ int xmss_core_sign(const xmss_params *params, | |||||
// Init working params | // Init working params | ||||
unsigned char R[params->n]; | unsigned char R[params->n]; | ||||
unsigned char msg_h[params->n]; | unsigned char msg_h[params->n]; | ||||
unsigned char ots_seed[params->n]; | |||||
uint32_t ots_addr[8] = {0}; | uint32_t ots_addr[8] = {0}; | ||||
// --------------------------------- | // --------------------------------- | ||||
@@ -670,11 +669,8 @@ int xmss_core_sign(const xmss_params *params, | |||||
set_type(ots_addr, 0); | set_type(ots_addr, 0); | ||||
set_ots_addr(ots_addr, idx); | set_ots_addr(ots_addr, idx); | ||||
// Compute seed for OTS key pair | |||||
get_seed(params, ots_seed, sk_seed, ots_addr); | |||||
// Compute WOTS signature | // 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; | sm += params->wots_sig_bytes; | ||||
*smlen += 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, | int xmssmt_core_keypair(const xmss_params *params, | ||||
unsigned char *pk, unsigned char *sk) | unsigned char *pk, unsigned char *sk) | ||||
{ | { | ||||
unsigned char ots_seed[params->n]; | |||||
uint32_t addr[8] = {0}; | uint32_t addr[8] = {0}; | ||||
unsigned int i; | unsigned int i; | ||||
unsigned char *wots_sigs; | unsigned char *wots_sigs; | ||||
@@ -745,8 +740,7 @@ int xmssmt_core_keypair(const xmss_params *params, | |||||
// Compute seed for OTS key pair | // Compute seed for OTS key pair | ||||
treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); | treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); | ||||
set_layer_addr(addr, (i+1)); | 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 | // 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); | 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 | // Init working params | ||||
unsigned char R[params->n]; | unsigned char R[params->n]; | ||||
unsigned char msg_h[params->n]; | unsigned char msg_h[params->n]; | ||||
unsigned char ots_seed[params->n]; | |||||
uint32_t addr[8] = {0}; | uint32_t addr[8] = {0}; | ||||
uint32_t ots_addr[8] = {0}; | uint32_t ots_addr[8] = {0}; | ||||
unsigned char idx_bytes_32[32]; | 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_tree_addr(ots_addr, idx_tree); | ||||
set_ots_addr(ots_addr, idx_leaf); | 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 | // 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; | sm += params->wots_sig_bytes; | ||||
*smlen += 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_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))); | 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].stackoffset = 0; | ||||
states[params->d + i].next_leaf = 0; | states[params->d + i].next_leaf = 0; | ||||