Skip to content

Commit 3e28db2

Browse files
author
David Cooper
committed
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).
1 parent 2237b6f commit 3e28db2

File tree

7 files changed

+40
-59
lines changed

7 files changed

+40
-59
lines changed

hash.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define XMSS_HASH_PADDING_H 1
1313
#define XMSS_HASH_PADDING_HASH 2
1414
#define XMSS_HASH_PADDING_PRF 3
15+
#define XMSS_HASH_PADDING_PRF_KEYGEN 4
1516

1617
void addr_to_bytes(unsigned char *bytes, const uint32_t addr[8])
1718
{
@@ -59,6 +60,23 @@ int prf(const xmss_params *params,
5960
return core_hash(params, out, buf, params->padding_len + params->n + 32);
6061
}
6162

63+
/*
64+
* Computes PRF_keygen(key, in), for a key of params->n bytes, and an input
65+
* of 32 + params->n bytes
66+
*/
67+
int prf_keygen(const xmss_params *params,
68+
unsigned char *out, const unsigned char *in,
69+
const unsigned char *key)
70+
{
71+
unsigned char buf[params->padding_len + 2*params->n + 32];
72+
73+
ull_to_bytes(buf, params->padding_len, XMSS_HASH_PADDING_PRF_KEYGEN);
74+
memcpy(buf + params->padding_len, key, params->n);
75+
memcpy(buf + params->padding_len + params->n, in, params->n + 32);
76+
77+
return core_hash(params, out, buf, params->padding_len + 2*params->n + 32);
78+
}
79+
6280
/*
6381
* Computes the message hash using R, the public root, the index of the leaf
6482
* node, and the message. Notably, it requires m_with_prefix to have 4*n bytes

hash.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ int prf(const xmss_params *params,
1010
unsigned char *out, const unsigned char in[32],
1111
const unsigned char *key);
1212

13+
int prf_keygen(const xmss_params *params,
14+
unsigned char *out, const unsigned char *in,
15+
const unsigned char *key);
16+
1317
int h_msg(const xmss_params *params,
1418
unsigned char *out,
1519
const unsigned char *in, unsigned long long inlen,

wots.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99

1010
/**
1111
* Helper method for pseudorandom key generation.
12-
* Expands an n-byte array into a len*n byte array using the `prf` function.
12+
* Expands an n-byte array into a len*n byte array using the `prf_keygen` function.
1313
*/
1414
static void expand_seed(const xmss_params *params,
15-
unsigned char *outseeds, const unsigned char *inseed)
15+
unsigned char *outseeds, const unsigned char *inseed,
16+
const unsigned char *pub_seed, uint32_t addr[8])
1617
{
1718
uint32_t i;
18-
unsigned char ctr[32];
19+
unsigned char buf[params->n + 32];
1920

21+
set_hash_addr(addr, 0);
22+
set_key_and_mask(addr, 0);
23+
memcpy(buf, pub_seed, params->n);
2024
for (i = 0; i < params->wots_len; i++) {
21-
ull_to_bytes(ctr, 32, i);
22-
prf(params, outseeds + i*params->n, ctr, inseed);
25+
set_chain_addr(addr, i);
26+
addr_to_bytes(buf + params->n, addr);
27+
prf_keygen(params, outseeds + i*params->n, buf, inseed);
2328
}
2429
}
2530

@@ -116,7 +121,7 @@ void wots_pkgen(const xmss_params *params,
116121
uint32_t i;
117122

118123
/* The WOTS+ private key is derived from the seed. */
119-
expand_seed(params, pk, seed);
124+
expand_seed(params, pk, seed, pub_seed, addr);
120125

121126
for (i = 0; i < params->wots_len; i++) {
122127
set_chain_addr(addr, i);
@@ -140,7 +145,7 @@ void wots_sign(const xmss_params *params,
140145
chain_lengths(params, lengths, msg);
141146

142147
/* The WOTS+ private key is derived from the seed. */
143-
expand_seed(params, sig, seed);
148+
expand_seed(params, sig, seed, pub_seed, addr);
144149

145150
for (i = 0; i < params->wots_len; i++) {
146151
set_chain_addr(addr, i);

xmss_commons.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -105,35 +105,13 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf,
105105
const unsigned char *sk_seed, const unsigned char *pub_seed,
106106
uint32_t ltree_addr[8], uint32_t ots_addr[8])
107107
{
108-
unsigned char seed[params->n];
109108
unsigned char pk[params->wots_sig_bytes];
110109

111-
get_seed(params, seed, sk_seed, ots_addr);
112-
wots_pkgen(params, pk, seed, pub_seed, ots_addr);
110+
wots_pkgen(params, pk, sk_seed, pub_seed, ots_addr);
113111

114112
l_tree(params, leaf, pk, pub_seed, ltree_addr);
115113
}
116114

117-
/**
118-
* Used for pseudo-random key generation.
119-
* Generates the seed for the WOTS key pair at address 'addr'.
120-
*
121-
* Takes n-byte sk_seed and returns n-byte seed using 32 byte address 'addr'.
122-
*/
123-
void get_seed(const xmss_params *params, unsigned char *seed,
124-
const unsigned char *sk_seed, uint32_t addr[8])
125-
{
126-
unsigned char bytes[32];
127-
128-
/* Make sure that chain addr, hash addr, and key bit are zeroed. */
129-
set_chain_addr(addr, 0);
130-
set_hash_addr(addr, 0);
131-
set_key_and_mask(addr, 0);
132-
133-
/* Generate seed. */
134-
addr_to_bytes(bytes, addr);
135-
prf(params, seed, bytes, sk_seed);
136-
}
137115

138116
/**
139117
* Verifies a given message signature pair under a given public key.

xmss_commons.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@ void gen_leaf_wots(const xmss_params *params, unsigned char *leaf,
1313
const unsigned char *sk_seed, const unsigned char *pub_seed,
1414
uint32_t ltree_addr[8], uint32_t ots_addr[8]);
1515

16-
/**
17-
* Used for pseudo-random key generation.
18-
* Generates the seed for the WOTS key pair at address 'addr'.
19-
*
20-
* Takes n-byte sk_seed and returns n-byte seed using 32 byte address 'addr'.
21-
*/
22-
void get_seed(const xmss_params *params, unsigned char *seed,
23-
const unsigned char *sk_seed, uint32_t addr[8]);
24-
2516
/**
2617
* Verifies a given message signature pair under a given public key.
2718
* Note that this assumes a pk without an OID, i.e. [root || PUB_SEED]

xmss_core.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ int xmssmt_core_sign(const xmss_params *params,
174174

175175
unsigned char root[params->n];
176176
unsigned char *mhash = root;
177-
unsigned char ots_seed[params->n];
178177
unsigned long long idx;
179178
unsigned char idx_bytes_32[32];
180179
unsigned int i;
@@ -217,13 +216,10 @@ int xmssmt_core_sign(const xmss_params *params,
217216
set_tree_addr(ots_addr, idx);
218217
set_ots_addr(ots_addr, idx_leaf);
219218

220-
/* Get a seed for the WOTS keypair. */
221-
get_seed(params, ots_seed, sk_seed, ots_addr);
222-
223219
/* Compute a WOTS signature. */
224220
/* Initially, root = mhash, but on subsequent iterations it is the root
225221
of the subtree below the currently processed subtree. */
226-
wots_sign(params, sm, root, ots_seed, pub_seed, ots_addr);
222+
wots_sign(params, sm, root, sk_seed, pub_seed, ots_addr);
227223
sm += params->wots_sig_bytes;
228224

229225
/* Compute the authentication path for the used WOTS leaf. */

xmss_core_fast.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,6 @@ int xmss_core_sign(const xmss_params *params,
623623
// Init working params
624624
unsigned char R[params->n];
625625
unsigned char msg_h[params->n];
626-
unsigned char ots_seed[params->n];
627626
uint32_t ots_addr[8] = {0};
628627

629628
// ---------------------------------
@@ -670,11 +669,8 @@ int xmss_core_sign(const xmss_params *params,
670669
set_type(ots_addr, 0);
671670
set_ots_addr(ots_addr, idx);
672671

673-
// Compute seed for OTS key pair
674-
get_seed(params, ots_seed, sk_seed, ots_addr);
675-
676672
// Compute WOTS signature
677-
wots_sign(params, sm, msg_h, ots_seed, pub_seed, ots_addr);
673+
wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr);
678674

679675
sm += params->wots_sig_bytes;
680676
*smlen += params->wots_sig_bytes;
@@ -707,7 +703,6 @@ int xmss_core_sign(const xmss_params *params,
707703
int xmssmt_core_keypair(const xmss_params *params,
708704
unsigned char *pk, unsigned char *sk)
709705
{
710-
unsigned char ots_seed[params->n];
711706
uint32_t addr[8] = {0};
712707
unsigned int i;
713708
unsigned char *wots_sigs;
@@ -745,8 +740,7 @@ int xmssmt_core_keypair(const xmss_params *params,
745740
// Compute seed for OTS key pair
746741
treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr);
747742
set_layer_addr(addr, (i+1));
748-
get_seed(params, ots_seed, sk + params->index_bytes, addr);
749-
wots_sign(params, wots_sigs + i*params->wots_sig_bytes, pk, ots_seed, pk+params->n, addr);
743+
wots_sign(params, wots_sigs + i*params->wots_sig_bytes, pk, sk + params->index_bytes, pk+params->n, addr);
750744
}
751745
// Address now points to the single tree on layer d-1
752746
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,
783777
// Init working params
784778
unsigned char R[params->n];
785779
unsigned char msg_h[params->n];
786-
unsigned char ots_seed[params->n];
787780
uint32_t addr[8] = {0};
788781
uint32_t ots_addr[8] = {0};
789782
unsigned char idx_bytes_32[32];
@@ -867,11 +860,8 @@ int xmssmt_core_sign(const xmss_params *params,
867860
set_tree_addr(ots_addr, idx_tree);
868861
set_ots_addr(ots_addr, idx_leaf);
869862

870-
// Compute seed for OTS key pair
871-
get_seed(params, ots_seed, sk_seed, ots_addr);
872-
873863
// Compute WOTS signature
874-
wots_sign(params, sm, msg_h, ots_seed, pub_seed, ots_addr);
864+
wots_sign(params, sm, msg_h, sk_seed, pub_seed, ots_addr);
875865

876866
sm += params->wots_sig_bytes;
877867
*smlen += params->wots_sig_bytes;
@@ -929,8 +919,7 @@ int xmssmt_core_sign(const xmss_params *params,
929919
set_tree_addr(ots_addr, ((idx + 1) >> ((i+2) * params->tree_height)));
930920
set_ots_addr(ots_addr, (((idx >> ((i+1) * params->tree_height)) + 1) & ((1 << params->tree_height)-1)));
931921

932-
get_seed(params, ots_seed, sk+params->index_bytes, ots_addr);
933-
wots_sign(params, wots_sigs + i*params->wots_sig_bytes, states[i].stack, ots_seed, pub_seed, ots_addr);
922+
wots_sign(params, wots_sigs + i*params->wots_sig_bytes, states[i].stack, sk_seed, pub_seed, ots_addr);
934923

935924
states[params->d + i].stackoffset = 0;
936925
states[params->d + i].next_leaf = 0;

0 commit comments

Comments
 (0)