Skip to content

Commit

Permalink
Improved key generation
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
dcooper16 committed Apr 30, 2020
1 parent 2237b6f commit 3e28db2
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 59 deletions.
18 changes: 18 additions & 0 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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])
{
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
19 changes: 12 additions & 7 deletions wots.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
24 changes: 1 addition & 23 deletions xmss_commons.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 0 additions & 9 deletions xmss_commons.h
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
6 changes: 1 addition & 5 deletions xmss_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down
19 changes: 4 additions & 15 deletions xmss_core_fast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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};

// ---------------------------------
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 3e28db2

Please sign in to comment.