xref: /openbsd-src/usr.bin/ssh/xmss_fast.c (revision 38344adbbf64f67d07aa168c919cf9da5baee59e)
1*38344adbSmarkus /* $OpenBSD: xmss_fast.c,v 1.3 2018/03/22 07:06:11 markus Exp $ */
2a6be8e7cSmarkus /*
3a6be8e7cSmarkus xmss_fast.c version 20160722
4a6be8e7cSmarkus Andreas Hülsing
5a6be8e7cSmarkus Joost Rijneveld
6a6be8e7cSmarkus Public domain.
7a6be8e7cSmarkus */
8a6be8e7cSmarkus 
9a6be8e7cSmarkus #include <stdlib.h>
10a6be8e7cSmarkus #include <string.h>
11a6be8e7cSmarkus #include <stdint.h>
12a6be8e7cSmarkus 
13*38344adbSmarkus #include "xmss_fast.h"
14a6be8e7cSmarkus #include "crypto_api.h"
15a6be8e7cSmarkus #include "xmss_wots.h"
16a6be8e7cSmarkus #include "xmss_hash.h"
17a6be8e7cSmarkus 
18a6be8e7cSmarkus #include "xmss_commons.h"
19a6be8e7cSmarkus #include "xmss_hash_address.h"
20a6be8e7cSmarkus // For testing
21a6be8e7cSmarkus #include "stdio.h"
22a6be8e7cSmarkus 
23a6be8e7cSmarkus 
24a6be8e7cSmarkus 
25a6be8e7cSmarkus /**
26a6be8e7cSmarkus  * Used for pseudorandom keygeneration,
27a6be8e7cSmarkus  * generates the seed for the WOTS keypair at address addr
28a6be8e7cSmarkus  *
29a6be8e7cSmarkus  * takes n byte sk_seed and returns n byte seed using 32 byte address addr.
30a6be8e7cSmarkus  */
get_seed(unsigned char * seed,const unsigned char * sk_seed,int n,uint32_t addr[8])31a6be8e7cSmarkus static void get_seed(unsigned char *seed, const unsigned char *sk_seed, int n, uint32_t addr[8])
32a6be8e7cSmarkus {
33a6be8e7cSmarkus   unsigned char bytes[32];
34a6be8e7cSmarkus   // Make sure that chain addr, hash addr, and key bit are 0!
35a6be8e7cSmarkus   setChainADRS(addr,0);
36a6be8e7cSmarkus   setHashADRS(addr,0);
37a6be8e7cSmarkus   setKeyAndMask(addr,0);
38a6be8e7cSmarkus   // Generate pseudorandom value
39a6be8e7cSmarkus   addr_to_byte(bytes, addr);
40a6be8e7cSmarkus   prf(seed, bytes, sk_seed, n);
41a6be8e7cSmarkus }
42a6be8e7cSmarkus 
43a6be8e7cSmarkus /**
44a6be8e7cSmarkus  * Initialize xmss params struct
45a6be8e7cSmarkus  * parameter names are the same as in the draft
46a6be8e7cSmarkus  * parameter k is K as used in the BDS algorithm
47a6be8e7cSmarkus  */
xmss_set_params(xmss_params * params,int n,int h,int w,int k)48a6be8e7cSmarkus int xmss_set_params(xmss_params *params, int n, int h, int w, int k)
49a6be8e7cSmarkus {
50a6be8e7cSmarkus   if (k >= h || k < 2 || (h - k) % 2) {
51a6be8e7cSmarkus     fprintf(stderr, "For BDS traversal, H - K must be even, with H > K >= 2!\n");
52a6be8e7cSmarkus     return 1;
53a6be8e7cSmarkus   }
54a6be8e7cSmarkus   params->h = h;
55a6be8e7cSmarkus   params->n = n;
56a6be8e7cSmarkus   params->k = k;
57a6be8e7cSmarkus   wots_params wots_par;
58a6be8e7cSmarkus   wots_set_params(&wots_par, n, w);
59a6be8e7cSmarkus   params->wots_par = wots_par;
60a6be8e7cSmarkus   return 0;
61a6be8e7cSmarkus }
62a6be8e7cSmarkus 
63a6be8e7cSmarkus /**
64a6be8e7cSmarkus  * Initialize BDS state struct
65a6be8e7cSmarkus  * parameter names are the same as used in the description of the BDS traversal
66a6be8e7cSmarkus  */
xmss_set_bds_state(bds_state * state,unsigned char * stack,int stackoffset,unsigned char * stacklevels,unsigned char * auth,unsigned char * keep,treehash_inst * treehash,unsigned char * retain,int next_leaf)67a6be8e7cSmarkus void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf)
68a6be8e7cSmarkus {
69a6be8e7cSmarkus   state->stack = stack;
70a6be8e7cSmarkus   state->stackoffset = stackoffset;
71a6be8e7cSmarkus   state->stacklevels = stacklevels;
72a6be8e7cSmarkus   state->auth = auth;
73a6be8e7cSmarkus   state->keep = keep;
74a6be8e7cSmarkus   state->treehash = treehash;
75a6be8e7cSmarkus   state->retain = retain;
76a6be8e7cSmarkus   state->next_leaf = next_leaf;
77a6be8e7cSmarkus }
78a6be8e7cSmarkus 
79a6be8e7cSmarkus /**
80a6be8e7cSmarkus  * Initialize xmssmt_params struct
81a6be8e7cSmarkus  * parameter names are the same as in the draft
82a6be8e7cSmarkus  *
83a6be8e7cSmarkus  * Especially h is the total tree height, i.e. the XMSS trees have height h/d
84a6be8e7cSmarkus  */
xmssmt_set_params(xmssmt_params * params,int n,int h,int d,int w,int k)85a6be8e7cSmarkus int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k)
86a6be8e7cSmarkus {
87a6be8e7cSmarkus   if (h % d) {
88a6be8e7cSmarkus     fprintf(stderr, "d must divide h without remainder!\n");
89a6be8e7cSmarkus     return 1;
90a6be8e7cSmarkus   }
91a6be8e7cSmarkus   params->h = h;
92a6be8e7cSmarkus   params->d = d;
93a6be8e7cSmarkus   params->n = n;
94a6be8e7cSmarkus   params->index_len = (h + 7) / 8;
95a6be8e7cSmarkus   xmss_params xmss_par;
96a6be8e7cSmarkus   if (xmss_set_params(&xmss_par, n, (h/d), w, k)) {
97a6be8e7cSmarkus     return 1;
98a6be8e7cSmarkus   }
99a6be8e7cSmarkus   params->xmss_par = xmss_par;
100a6be8e7cSmarkus   return 0;
101a6be8e7cSmarkus }
102a6be8e7cSmarkus 
103a6be8e7cSmarkus /**
104a6be8e7cSmarkus  * Computes a leaf from a WOTS public key using an L-tree.
105a6be8e7cSmarkus  */
l_tree(unsigned char * leaf,unsigned char * wots_pk,const xmss_params * params,const unsigned char * pub_seed,uint32_t addr[8])106a6be8e7cSmarkus static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
107a6be8e7cSmarkus {
108a6be8e7cSmarkus   unsigned int l = params->wots_par.len;
109a6be8e7cSmarkus   unsigned int n = params->n;
110a6be8e7cSmarkus   uint32_t i = 0;
111a6be8e7cSmarkus   uint32_t height = 0;
112a6be8e7cSmarkus   uint32_t bound;
113a6be8e7cSmarkus 
114a6be8e7cSmarkus   //ADRS.setTreeHeight(0);
115a6be8e7cSmarkus   setTreeHeight(addr, height);
116a6be8e7cSmarkus 
117a6be8e7cSmarkus   while (l > 1) {
118a6be8e7cSmarkus      bound = l >> 1; //floor(l / 2);
119a6be8e7cSmarkus      for (i = 0; i < bound; i++) {
120a6be8e7cSmarkus        //ADRS.setTreeIndex(i);
121a6be8e7cSmarkus        setTreeIndex(addr, i);
122a6be8e7cSmarkus        //wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS);
123a6be8e7cSmarkus        hash_h(wots_pk+i*n, wots_pk+i*2*n, pub_seed, addr, n);
124a6be8e7cSmarkus      }
125a6be8e7cSmarkus      //if ( l % 2 == 1 ) {
126a6be8e7cSmarkus      if (l & 1) {
127a6be8e7cSmarkus        //pk[floor(l / 2) + 1] = pk[l];
128a6be8e7cSmarkus        memcpy(wots_pk+(l>>1)*n, wots_pk+(l-1)*n, n);
129a6be8e7cSmarkus        //l = ceil(l / 2);
130a6be8e7cSmarkus        l=(l>>1)+1;
131a6be8e7cSmarkus      }
132a6be8e7cSmarkus      else {
133a6be8e7cSmarkus        //l = ceil(l / 2);
134a6be8e7cSmarkus        l=(l>>1);
135a6be8e7cSmarkus      }
136a6be8e7cSmarkus      //ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
137a6be8e7cSmarkus      height++;
138a6be8e7cSmarkus      setTreeHeight(addr, height);
139a6be8e7cSmarkus    }
140a6be8e7cSmarkus    //return pk[0];
141a6be8e7cSmarkus    memcpy(leaf, wots_pk, n);
142a6be8e7cSmarkus }
143a6be8e7cSmarkus 
144a6be8e7cSmarkus /**
145a6be8e7cSmarkus  * Computes the leaf at a given address. First generates the WOTS key pair, then computes leaf using l_tree. As this happens position independent, we only require that addr encodes the right ltree-address.
146a6be8e7cSmarkus  */
gen_leaf_wots(unsigned char * leaf,const unsigned char * sk_seed,const xmss_params * params,const unsigned char * pub_seed,uint32_t ltree_addr[8],uint32_t ots_addr[8])147a6be8e7cSmarkus static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8])
148a6be8e7cSmarkus {
149a6be8e7cSmarkus   unsigned char seed[params->n];
150a6be8e7cSmarkus   unsigned char pk[params->wots_par.keysize];
151a6be8e7cSmarkus 
152a6be8e7cSmarkus   get_seed(seed, sk_seed, params->n, ots_addr);
153a6be8e7cSmarkus   wots_pkgen(pk, seed, &(params->wots_par), pub_seed, ots_addr);
154a6be8e7cSmarkus 
155a6be8e7cSmarkus   l_tree(leaf, pk, params, pub_seed, ltree_addr);
156a6be8e7cSmarkus }
157a6be8e7cSmarkus 
treehash_minheight_on_stack(bds_state * state,const xmss_params * params,const treehash_inst * treehash)158a6be8e7cSmarkus static int treehash_minheight_on_stack(bds_state* state, const xmss_params *params, const treehash_inst *treehash) {
159a6be8e7cSmarkus   unsigned int r = params->h, i;
160a6be8e7cSmarkus   for (i = 0; i < treehash->stackusage; i++) {
161a6be8e7cSmarkus     if (state->stacklevels[state->stackoffset - i - 1] < r) {
162a6be8e7cSmarkus       r = state->stacklevels[state->stackoffset - i - 1];
163a6be8e7cSmarkus     }
164a6be8e7cSmarkus   }
165a6be8e7cSmarkus   return r;
166a6be8e7cSmarkus }
167a6be8e7cSmarkus 
168a6be8e7cSmarkus /**
169a6be8e7cSmarkus  * Merkle's TreeHash algorithm. The address only needs to initialize the first 78 bits of addr. Everything else will be set by treehash.
170a6be8e7cSmarkus  * Currently only used for key generation.
171a6be8e7cSmarkus  *
172a6be8e7cSmarkus  */
treehash_setup(unsigned char * node,int height,int index,bds_state * state,const unsigned char * sk_seed,const xmss_params * params,const unsigned char * pub_seed,const uint32_t addr[8])173a6be8e7cSmarkus static void treehash_setup(unsigned char *node, int height, int index, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8])
174a6be8e7cSmarkus {
175a6be8e7cSmarkus   unsigned int idx = index;
176a6be8e7cSmarkus   unsigned int n = params->n;
177a6be8e7cSmarkus   unsigned int h = params->h;
178a6be8e7cSmarkus   unsigned int k = params->k;
179a6be8e7cSmarkus   // use three different addresses because at this point we use all three formats in parallel
180a6be8e7cSmarkus   uint32_t ots_addr[8];
181a6be8e7cSmarkus   uint32_t ltree_addr[8];
182a6be8e7cSmarkus   uint32_t  node_addr[8];
183a6be8e7cSmarkus   // only copy layer and tree address parts
184a6be8e7cSmarkus   memcpy(ots_addr, addr, 12);
185a6be8e7cSmarkus   // type = ots
186a6be8e7cSmarkus   setType(ots_addr, 0);
187a6be8e7cSmarkus   memcpy(ltree_addr, addr, 12);
188a6be8e7cSmarkus   setType(ltree_addr, 1);
189a6be8e7cSmarkus   memcpy(node_addr, addr, 12);
190a6be8e7cSmarkus   setType(node_addr, 2);
191a6be8e7cSmarkus 
192a6be8e7cSmarkus   uint32_t lastnode, i;
193a6be8e7cSmarkus   unsigned char stack[(height+1)*n];
194a6be8e7cSmarkus   unsigned int stacklevels[height+1];
195a6be8e7cSmarkus   unsigned int stackoffset=0;
196a6be8e7cSmarkus   unsigned int nodeh;
197a6be8e7cSmarkus 
198a6be8e7cSmarkus   lastnode = idx+(1<<height);
199a6be8e7cSmarkus 
200a6be8e7cSmarkus   for (i = 0; i < h-k; i++) {
201a6be8e7cSmarkus     state->treehash[i].h = i;
202a6be8e7cSmarkus     state->treehash[i].completed = 1;
203a6be8e7cSmarkus     state->treehash[i].stackusage = 0;
204a6be8e7cSmarkus   }
205a6be8e7cSmarkus 
206a6be8e7cSmarkus   i = 0;
207a6be8e7cSmarkus   for (; idx < lastnode; idx++) {
208a6be8e7cSmarkus     setLtreeADRS(ltree_addr, idx);
209a6be8e7cSmarkus     setOTSADRS(ots_addr, idx);
210a6be8e7cSmarkus     gen_leaf_wots(stack+stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr);
211a6be8e7cSmarkus     stacklevels[stackoffset] = 0;
212a6be8e7cSmarkus     stackoffset++;
213a6be8e7cSmarkus     if (h - k > 0 && i == 3) {
214a6be8e7cSmarkus       memcpy(state->treehash[0].node, stack+stackoffset*n, n);
215a6be8e7cSmarkus     }
216a6be8e7cSmarkus     while (stackoffset>1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2])
217a6be8e7cSmarkus     {
218a6be8e7cSmarkus       nodeh = stacklevels[stackoffset-1];
219a6be8e7cSmarkus       if (i >> nodeh == 1) {
220a6be8e7cSmarkus         memcpy(state->auth + nodeh*n, stack+(stackoffset-1)*n, n);
221a6be8e7cSmarkus       }
222a6be8e7cSmarkus       else {
223a6be8e7cSmarkus         if (nodeh < h - k && i >> nodeh == 3) {
224a6be8e7cSmarkus           memcpy(state->treehash[nodeh].node, stack+(stackoffset-1)*n, n);
225a6be8e7cSmarkus         }
226a6be8e7cSmarkus         else if (nodeh >= h - k) {
227a6be8e7cSmarkus           memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((i >> nodeh) - 3) >> 1)) * n, stack+(stackoffset-1)*n, n);
228a6be8e7cSmarkus         }
229a6be8e7cSmarkus       }
230a6be8e7cSmarkus       setTreeHeight(node_addr, stacklevels[stackoffset-1]);
231a6be8e7cSmarkus       setTreeIndex(node_addr, (idx >> (stacklevels[stackoffset-1]+1)));
232a6be8e7cSmarkus       hash_h(stack+(stackoffset-2)*n, stack+(stackoffset-2)*n, pub_seed,
233a6be8e7cSmarkus           node_addr, n);
234a6be8e7cSmarkus       stacklevels[stackoffset-2]++;
235a6be8e7cSmarkus       stackoffset--;
236a6be8e7cSmarkus     }
237a6be8e7cSmarkus     i++;
238a6be8e7cSmarkus   }
239a6be8e7cSmarkus 
240a6be8e7cSmarkus   for (i = 0; i < n; i++)
241a6be8e7cSmarkus     node[i] = stack[i];
242a6be8e7cSmarkus }
243a6be8e7cSmarkus 
treehash_update(treehash_inst * treehash,bds_state * state,const unsigned char * sk_seed,const xmss_params * params,const unsigned char * pub_seed,const uint32_t addr[8])244a6be8e7cSmarkus static void treehash_update(treehash_inst *treehash, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) {
245a6be8e7cSmarkus   int n = params->n;
246a6be8e7cSmarkus 
247a6be8e7cSmarkus   uint32_t ots_addr[8];
248a6be8e7cSmarkus   uint32_t ltree_addr[8];
249a6be8e7cSmarkus   uint32_t  node_addr[8];
250a6be8e7cSmarkus   // only copy layer and tree address parts
251a6be8e7cSmarkus   memcpy(ots_addr, addr, 12);
252a6be8e7cSmarkus   // type = ots
253a6be8e7cSmarkus   setType(ots_addr, 0);
254a6be8e7cSmarkus   memcpy(ltree_addr, addr, 12);
255a6be8e7cSmarkus   setType(ltree_addr, 1);
256a6be8e7cSmarkus   memcpy(node_addr, addr, 12);
257a6be8e7cSmarkus   setType(node_addr, 2);
258a6be8e7cSmarkus 
259a6be8e7cSmarkus   setLtreeADRS(ltree_addr, treehash->next_idx);
260a6be8e7cSmarkus   setOTSADRS(ots_addr, treehash->next_idx);
261a6be8e7cSmarkus 
262a6be8e7cSmarkus   unsigned char nodebuffer[2 * n];
263a6be8e7cSmarkus   unsigned int nodeheight = 0;
264a6be8e7cSmarkus   gen_leaf_wots(nodebuffer, sk_seed, params, pub_seed, ltree_addr, ots_addr);
265a6be8e7cSmarkus   while (treehash->stackusage > 0 && state->stacklevels[state->stackoffset-1] == nodeheight) {
266a6be8e7cSmarkus     memcpy(nodebuffer + n, nodebuffer, n);
267a6be8e7cSmarkus     memcpy(nodebuffer, state->stack + (state->stackoffset-1)*n, n);
268a6be8e7cSmarkus     setTreeHeight(node_addr, nodeheight);
269a6be8e7cSmarkus     setTreeIndex(node_addr, (treehash->next_idx >> (nodeheight+1)));
270a6be8e7cSmarkus     hash_h(nodebuffer, nodebuffer, pub_seed, node_addr, n);
271a6be8e7cSmarkus     nodeheight++;
272a6be8e7cSmarkus     treehash->stackusage--;
273a6be8e7cSmarkus     state->stackoffset--;
274a6be8e7cSmarkus   }
275a6be8e7cSmarkus   if (nodeheight == treehash->h) { // this also implies stackusage == 0
276a6be8e7cSmarkus     memcpy(treehash->node, nodebuffer, n);
277a6be8e7cSmarkus     treehash->completed = 1;
278a6be8e7cSmarkus   }
279a6be8e7cSmarkus   else {
280a6be8e7cSmarkus     memcpy(state->stack + state->stackoffset*n, nodebuffer, n);
281a6be8e7cSmarkus     treehash->stackusage++;
282a6be8e7cSmarkus     state->stacklevels[state->stackoffset] = nodeheight;
283a6be8e7cSmarkus     state->stackoffset++;
284a6be8e7cSmarkus     treehash->next_idx++;
285a6be8e7cSmarkus   }
286a6be8e7cSmarkus }
287a6be8e7cSmarkus 
288a6be8e7cSmarkus /**
289a6be8e7cSmarkus  * Computes a root node given a leaf and an authapth
290a6be8e7cSmarkus  */
validate_authpath(unsigned char * root,const unsigned char * leaf,unsigned long leafidx,const unsigned char * authpath,const xmss_params * params,const unsigned char * pub_seed,uint32_t addr[8])291a6be8e7cSmarkus static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
292a6be8e7cSmarkus {
293a6be8e7cSmarkus   unsigned int n = params->n;
294a6be8e7cSmarkus 
295a6be8e7cSmarkus   uint32_t i, j;
296a6be8e7cSmarkus   unsigned char buffer[2*n];
297a6be8e7cSmarkus 
298a6be8e7cSmarkus   // If leafidx is odd (last bit = 1), current path element is a right child and authpath has to go to the left.
299a6be8e7cSmarkus   // Otherwise, it is the other way around
300a6be8e7cSmarkus   if (leafidx & 1) {
301a6be8e7cSmarkus     for (j = 0; j < n; j++)
302a6be8e7cSmarkus       buffer[n+j] = leaf[j];
303a6be8e7cSmarkus     for (j = 0; j < n; j++)
304a6be8e7cSmarkus       buffer[j] = authpath[j];
305a6be8e7cSmarkus   }
306a6be8e7cSmarkus   else {
307a6be8e7cSmarkus     for (j = 0; j < n; j++)
308a6be8e7cSmarkus       buffer[j] = leaf[j];
309a6be8e7cSmarkus     for (j = 0; j < n; j++)
310a6be8e7cSmarkus       buffer[n+j] = authpath[j];
311a6be8e7cSmarkus   }
312a6be8e7cSmarkus   authpath += n;
313a6be8e7cSmarkus 
314a6be8e7cSmarkus   for (i=0; i < params->h-1; i++) {
315a6be8e7cSmarkus     setTreeHeight(addr, i);
316a6be8e7cSmarkus     leafidx >>= 1;
317a6be8e7cSmarkus     setTreeIndex(addr, leafidx);
318a6be8e7cSmarkus     if (leafidx&1) {
319a6be8e7cSmarkus       hash_h(buffer+n, buffer, pub_seed, addr, n);
320a6be8e7cSmarkus       for (j = 0; j < n; j++)
321a6be8e7cSmarkus         buffer[j] = authpath[j];
322a6be8e7cSmarkus     }
323a6be8e7cSmarkus     else {
324a6be8e7cSmarkus       hash_h(buffer, buffer, pub_seed, addr, n);
325a6be8e7cSmarkus       for (j = 0; j < n; j++)
326a6be8e7cSmarkus         buffer[j+n] = authpath[j];
327a6be8e7cSmarkus     }
328a6be8e7cSmarkus     authpath += n;
329a6be8e7cSmarkus   }
330a6be8e7cSmarkus   setTreeHeight(addr, (params->h-1));
331a6be8e7cSmarkus   leafidx >>= 1;
332a6be8e7cSmarkus   setTreeIndex(addr, leafidx);
333a6be8e7cSmarkus   hash_h(root, buffer, pub_seed, addr, n);
334a6be8e7cSmarkus }
335a6be8e7cSmarkus 
336a6be8e7cSmarkus /**
337a6be8e7cSmarkus  * Performs one treehash update on the instance that needs it the most.
338a6be8e7cSmarkus  * Returns 1 if such an instance was not found
339a6be8e7cSmarkus  **/
bds_treehash_update(bds_state * state,unsigned int updates,const unsigned char * sk_seed,const xmss_params * params,unsigned char * pub_seed,const uint32_t addr[8])340a6be8e7cSmarkus static char bds_treehash_update(bds_state *state, unsigned int updates, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) {
341a6be8e7cSmarkus   uint32_t i, j;
342a6be8e7cSmarkus   unsigned int level, l_min, low;
343a6be8e7cSmarkus   unsigned int h = params->h;
344a6be8e7cSmarkus   unsigned int k = params->k;
345a6be8e7cSmarkus   unsigned int used = 0;
346a6be8e7cSmarkus 
347a6be8e7cSmarkus   for (j = 0; j < updates; j++) {
348a6be8e7cSmarkus     l_min = h;
349a6be8e7cSmarkus     level = h - k;
350a6be8e7cSmarkus     for (i = 0; i < h - k; i++) {
351a6be8e7cSmarkus       if (state->treehash[i].completed) {
352a6be8e7cSmarkus         low = h;
353a6be8e7cSmarkus       }
354a6be8e7cSmarkus       else if (state->treehash[i].stackusage == 0) {
355a6be8e7cSmarkus         low = i;
356a6be8e7cSmarkus       }
357a6be8e7cSmarkus       else {
358a6be8e7cSmarkus         low = treehash_minheight_on_stack(state, params, &(state->treehash[i]));
359a6be8e7cSmarkus       }
360a6be8e7cSmarkus       if (low < l_min) {
361a6be8e7cSmarkus         level = i;
362a6be8e7cSmarkus         l_min = low;
363a6be8e7cSmarkus       }
364a6be8e7cSmarkus     }
365a6be8e7cSmarkus     if (level == h - k) {
366a6be8e7cSmarkus       break;
367a6be8e7cSmarkus     }
368a6be8e7cSmarkus     treehash_update(&(state->treehash[level]), state, sk_seed, params, pub_seed, addr);
369a6be8e7cSmarkus     used++;
370a6be8e7cSmarkus   }
371a6be8e7cSmarkus   return updates - used;
372a6be8e7cSmarkus }
373a6be8e7cSmarkus 
374a6be8e7cSmarkus /**
375a6be8e7cSmarkus  * Updates the state (typically NEXT_i) by adding a leaf and updating the stack
376a6be8e7cSmarkus  * Returns 1 if all leaf nodes have already been processed
377a6be8e7cSmarkus  **/
bds_state_update(bds_state * state,const unsigned char * sk_seed,const xmss_params * params,unsigned char * pub_seed,const uint32_t addr[8])378a6be8e7cSmarkus static char bds_state_update(bds_state *state, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) {
379a6be8e7cSmarkus   uint32_t ltree_addr[8];
380a6be8e7cSmarkus   uint32_t node_addr[8];
381a6be8e7cSmarkus   uint32_t ots_addr[8];
382a6be8e7cSmarkus 
383a6be8e7cSmarkus   int n = params->n;
384a6be8e7cSmarkus   int h = params->h;
385a6be8e7cSmarkus   int k = params->k;
386a6be8e7cSmarkus 
387a6be8e7cSmarkus   int nodeh;
388a6be8e7cSmarkus   int idx = state->next_leaf;
389a6be8e7cSmarkus   if (idx == 1 << h) {
390a6be8e7cSmarkus     return 1;
391a6be8e7cSmarkus   }
392a6be8e7cSmarkus 
393a6be8e7cSmarkus   // only copy layer and tree address parts
394a6be8e7cSmarkus   memcpy(ots_addr, addr, 12);
395a6be8e7cSmarkus   // type = ots
396a6be8e7cSmarkus   setType(ots_addr, 0);
397a6be8e7cSmarkus   memcpy(ltree_addr, addr, 12);
398a6be8e7cSmarkus   setType(ltree_addr, 1);
399a6be8e7cSmarkus   memcpy(node_addr, addr, 12);
400a6be8e7cSmarkus   setType(node_addr, 2);
401a6be8e7cSmarkus 
402a6be8e7cSmarkus   setOTSADRS(ots_addr, idx);
403a6be8e7cSmarkus   setLtreeADRS(ltree_addr, idx);
404a6be8e7cSmarkus 
405a6be8e7cSmarkus   gen_leaf_wots(state->stack+state->stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr);
406a6be8e7cSmarkus 
407a6be8e7cSmarkus   state->stacklevels[state->stackoffset] = 0;
408a6be8e7cSmarkus   state->stackoffset++;
409a6be8e7cSmarkus   if (h - k > 0 && idx == 3) {
410a6be8e7cSmarkus     memcpy(state->treehash[0].node, state->stack+state->stackoffset*n, n);
411a6be8e7cSmarkus   }
412a6be8e7cSmarkus   while (state->stackoffset>1 && state->stacklevels[state->stackoffset-1] == state->stacklevels[state->stackoffset-2]) {
413a6be8e7cSmarkus     nodeh = state->stacklevels[state->stackoffset-1];
414a6be8e7cSmarkus     if (idx >> nodeh == 1) {
415a6be8e7cSmarkus       memcpy(state->auth + nodeh*n, state->stack+(state->stackoffset-1)*n, n);
416a6be8e7cSmarkus     }
417a6be8e7cSmarkus     else {
418a6be8e7cSmarkus       if (nodeh < h - k && idx >> nodeh == 3) {
419a6be8e7cSmarkus         memcpy(state->treehash[nodeh].node, state->stack+(state->stackoffset-1)*n, n);
420a6be8e7cSmarkus       }
421a6be8e7cSmarkus       else if (nodeh >= h - k) {
422a6be8e7cSmarkus         memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((idx >> nodeh) - 3) >> 1)) * n, state->stack+(state->stackoffset-1)*n, n);
423a6be8e7cSmarkus       }
424a6be8e7cSmarkus     }
425a6be8e7cSmarkus     setTreeHeight(node_addr, state->stacklevels[state->stackoffset-1]);
426a6be8e7cSmarkus     setTreeIndex(node_addr, (idx >> (state->stacklevels[state->stackoffset-1]+1)));
427a6be8e7cSmarkus     hash_h(state->stack+(state->stackoffset-2)*n, state->stack+(state->stackoffset-2)*n, pub_seed, node_addr, n);
428a6be8e7cSmarkus 
429a6be8e7cSmarkus     state->stacklevels[state->stackoffset-2]++;
430a6be8e7cSmarkus     state->stackoffset--;
431a6be8e7cSmarkus   }
432a6be8e7cSmarkus   state->next_leaf++;
433a6be8e7cSmarkus   return 0;
434a6be8e7cSmarkus }
435a6be8e7cSmarkus 
436a6be8e7cSmarkus /**
437a6be8e7cSmarkus  * Returns the auth path for node leaf_idx and computes the auth path for the
438a6be8e7cSmarkus  * next leaf node, using the algorithm described by Buchmann, Dahmen and Szydlo
439a6be8e7cSmarkus  * in "Post Quantum Cryptography", Springer 2009.
440a6be8e7cSmarkus  */
bds_round(bds_state * state,const unsigned long leaf_idx,const unsigned char * sk_seed,const xmss_params * params,unsigned char * pub_seed,uint32_t addr[8])441a6be8e7cSmarkus static void bds_round(bds_state *state, const unsigned long leaf_idx, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, uint32_t addr[8])
442a6be8e7cSmarkus {
443a6be8e7cSmarkus   unsigned int i;
444a6be8e7cSmarkus   unsigned int n = params->n;
445a6be8e7cSmarkus   unsigned int h = params->h;
446a6be8e7cSmarkus   unsigned int k = params->k;
447a6be8e7cSmarkus 
448a6be8e7cSmarkus   unsigned int tau = h;
449a6be8e7cSmarkus   unsigned int startidx;
450a6be8e7cSmarkus   unsigned int offset, rowidx;
451a6be8e7cSmarkus   unsigned char buf[2 * n];
452a6be8e7cSmarkus 
453a6be8e7cSmarkus   uint32_t ots_addr[8];
454a6be8e7cSmarkus   uint32_t ltree_addr[8];
455a6be8e7cSmarkus   uint32_t  node_addr[8];
456a6be8e7cSmarkus   // only copy layer and tree address parts
457a6be8e7cSmarkus   memcpy(ots_addr, addr, 12);
458a6be8e7cSmarkus   // type = ots
459a6be8e7cSmarkus   setType(ots_addr, 0);
460a6be8e7cSmarkus   memcpy(ltree_addr, addr, 12);
461a6be8e7cSmarkus   setType(ltree_addr, 1);
462a6be8e7cSmarkus   memcpy(node_addr, addr, 12);
463a6be8e7cSmarkus   setType(node_addr, 2);
464a6be8e7cSmarkus 
465a6be8e7cSmarkus   for (i = 0; i < h; i++) {
466a6be8e7cSmarkus     if (! ((leaf_idx >> i) & 1)) {
467a6be8e7cSmarkus       tau = i;
468a6be8e7cSmarkus       break;
469a6be8e7cSmarkus     }
470a6be8e7cSmarkus   }
471a6be8e7cSmarkus 
472a6be8e7cSmarkus   if (tau > 0) {
473a6be8e7cSmarkus     memcpy(buf,     state->auth + (tau-1) * n, n);
474a6be8e7cSmarkus     // we need to do this before refreshing state->keep to prevent overwriting
475a6be8e7cSmarkus     memcpy(buf + n, state->keep + ((tau-1) >> 1) * n, n);
476a6be8e7cSmarkus   }
477a6be8e7cSmarkus   if (!((leaf_idx >> (tau + 1)) & 1) && (tau < h - 1)) {
478a6be8e7cSmarkus     memcpy(state->keep + (tau >> 1)*n, state->auth + tau*n, n);
479a6be8e7cSmarkus   }
480a6be8e7cSmarkus   if (tau == 0) {
481a6be8e7cSmarkus     setLtreeADRS(ltree_addr, leaf_idx);
482a6be8e7cSmarkus     setOTSADRS(ots_addr, leaf_idx);
483a6be8e7cSmarkus     gen_leaf_wots(state->auth, sk_seed, params, pub_seed, ltree_addr, ots_addr);
484a6be8e7cSmarkus   }
485a6be8e7cSmarkus   else {
486a6be8e7cSmarkus     setTreeHeight(node_addr, (tau-1));
487a6be8e7cSmarkus     setTreeIndex(node_addr, leaf_idx >> tau);
488a6be8e7cSmarkus     hash_h(state->auth + tau * n, buf, pub_seed, node_addr, n);
489a6be8e7cSmarkus     for (i = 0; i < tau; i++) {
490a6be8e7cSmarkus       if (i < h - k) {
491a6be8e7cSmarkus         memcpy(state->auth + i * n, state->treehash[i].node, n);
492a6be8e7cSmarkus       }
493a6be8e7cSmarkus       else {
494a6be8e7cSmarkus         offset = (1 << (h - 1 - i)) + i - h;
495a6be8e7cSmarkus         rowidx = ((leaf_idx >> i) - 1) >> 1;
496a6be8e7cSmarkus         memcpy(state->auth + i * n, state->retain + (offset + rowidx) * n, n);
497a6be8e7cSmarkus       }
498a6be8e7cSmarkus     }
499a6be8e7cSmarkus 
500a6be8e7cSmarkus     for (i = 0; i < ((tau < h - k) ? tau : (h - k)); i++) {
501a6be8e7cSmarkus       startidx = leaf_idx + 1 + 3 * (1 << i);
502a6be8e7cSmarkus       if (startidx < 1U << h) {
503a6be8e7cSmarkus         state->treehash[i].h = i;
504a6be8e7cSmarkus         state->treehash[i].next_idx = startidx;
505a6be8e7cSmarkus         state->treehash[i].completed = 0;
506a6be8e7cSmarkus         state->treehash[i].stackusage = 0;
507a6be8e7cSmarkus       }
508a6be8e7cSmarkus     }
509a6be8e7cSmarkus   }
510a6be8e7cSmarkus }
511a6be8e7cSmarkus 
512a6be8e7cSmarkus /*
513a6be8e7cSmarkus  * Generates a XMSS key pair for a given parameter set.
514a6be8e7cSmarkus  * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
515a6be8e7cSmarkus  * Format pk: [root || PUB_SEED] omitting algo oid.
516a6be8e7cSmarkus  */
xmss_keypair(unsigned char * pk,unsigned char * sk,bds_state * state,xmss_params * params)517a6be8e7cSmarkus int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params)
518a6be8e7cSmarkus {
519a6be8e7cSmarkus   unsigned int n = params->n;
520a6be8e7cSmarkus   // Set idx = 0
521a6be8e7cSmarkus   sk[0] = 0;
522a6be8e7cSmarkus   sk[1] = 0;
523a6be8e7cSmarkus   sk[2] = 0;
524a6be8e7cSmarkus   sk[3] = 0;
525a6be8e7cSmarkus   // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
526a6be8e7cSmarkus   randombytes(sk+4, 3*n);
527a6be8e7cSmarkus   // Copy PUB_SEED to public key
528a6be8e7cSmarkus   memcpy(pk+n, sk+4+2*n, n);
529a6be8e7cSmarkus 
530a6be8e7cSmarkus   uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
531a6be8e7cSmarkus 
532a6be8e7cSmarkus   // Compute root
533a6be8e7cSmarkus   treehash_setup(pk, params->h, 0, state, sk+4, params, sk+4+2*n, addr);
534a6be8e7cSmarkus   // copy root to sk
535a6be8e7cSmarkus   memcpy(sk+4+3*n, pk, n);
536a6be8e7cSmarkus   return 0;
537a6be8e7cSmarkus }
538a6be8e7cSmarkus 
539a6be8e7cSmarkus /**
540a6be8e7cSmarkus  * Signs a message.
541a6be8e7cSmarkus  * Returns
542a6be8e7cSmarkus  * 1. an array containing the signature followed by the message AND
543a6be8e7cSmarkus  * 2. an updated secret key!
544a6be8e7cSmarkus  *
545a6be8e7cSmarkus  */
xmss_sign(unsigned char * sk,bds_state * state,unsigned char * sig_msg,unsigned long long * sig_msg_len,const unsigned char * msg,unsigned long long msglen,const xmss_params * params)546a6be8e7cSmarkus int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmss_params *params)
547a6be8e7cSmarkus {
548a6be8e7cSmarkus   unsigned int h = params->h;
549a6be8e7cSmarkus   unsigned int n = params->n;
550a6be8e7cSmarkus   unsigned int k = params->k;
551a6be8e7cSmarkus   uint16_t i = 0;
552a6be8e7cSmarkus 
553a6be8e7cSmarkus   // Extract SK
554a6be8e7cSmarkus   unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3];
555a6be8e7cSmarkus   unsigned char sk_seed[n];
556a6be8e7cSmarkus   memcpy(sk_seed, sk+4, n);
557a6be8e7cSmarkus   unsigned char sk_prf[n];
558a6be8e7cSmarkus   memcpy(sk_prf, sk+4+n, n);
559a6be8e7cSmarkus   unsigned char pub_seed[n];
560a6be8e7cSmarkus   memcpy(pub_seed, sk+4+2*n, n);
561a6be8e7cSmarkus 
562a6be8e7cSmarkus   // index as 32 bytes string
563a6be8e7cSmarkus   unsigned char idx_bytes_32[32];
564a6be8e7cSmarkus   to_byte(idx_bytes_32, idx, 32);
565a6be8e7cSmarkus 
566a6be8e7cSmarkus   unsigned char hash_key[3*n];
567a6be8e7cSmarkus 
568a6be8e7cSmarkus   // Update SK
569a6be8e7cSmarkus   sk[0] = ((idx + 1) >> 24) & 255;
570a6be8e7cSmarkus   sk[1] = ((idx + 1) >> 16) & 255;
571a6be8e7cSmarkus   sk[2] = ((idx + 1) >> 8) & 255;
572a6be8e7cSmarkus   sk[3] = (idx + 1) & 255;
573a6be8e7cSmarkus   // -- Secret key for this non-forward-secure version is now updated.
574a6be8e7cSmarkus   // -- A productive implementation should use a file handle instead and write the updated secret key at this point!
575a6be8e7cSmarkus 
576a6be8e7cSmarkus   // Init working params
577a6be8e7cSmarkus   unsigned char R[n];
578a6be8e7cSmarkus   unsigned char msg_h[n];
579a6be8e7cSmarkus   unsigned char ots_seed[n];
580a6be8e7cSmarkus   uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
581a6be8e7cSmarkus 
582a6be8e7cSmarkus   // ---------------------------------
583a6be8e7cSmarkus   // Message Hashing
584a6be8e7cSmarkus   // ---------------------------------
585a6be8e7cSmarkus 
586a6be8e7cSmarkus   // Message Hash:
587a6be8e7cSmarkus   // First compute pseudorandom value
588a6be8e7cSmarkus   prf(R, idx_bytes_32, sk_prf, n);
589a6be8e7cSmarkus   // Generate hash key (R || root || idx)
590a6be8e7cSmarkus   memcpy(hash_key, R, n);
591a6be8e7cSmarkus   memcpy(hash_key+n, sk+4+3*n, n);
592a6be8e7cSmarkus   to_byte(hash_key+2*n, idx, n);
593a6be8e7cSmarkus   // Then use it for message digest
594a6be8e7cSmarkus   h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
595a6be8e7cSmarkus 
596a6be8e7cSmarkus   // Start collecting signature
597a6be8e7cSmarkus   *sig_msg_len = 0;
598a6be8e7cSmarkus 
599a6be8e7cSmarkus   // Copy index to signature
600a6be8e7cSmarkus   sig_msg[0] = (idx >> 24) & 255;
601a6be8e7cSmarkus   sig_msg[1] = (idx >> 16) & 255;
602a6be8e7cSmarkus   sig_msg[2] = (idx >> 8) & 255;
603a6be8e7cSmarkus   sig_msg[3] = idx & 255;
604a6be8e7cSmarkus 
605a6be8e7cSmarkus   sig_msg += 4;
606a6be8e7cSmarkus   *sig_msg_len += 4;
607a6be8e7cSmarkus 
608a6be8e7cSmarkus   // Copy R to signature
609a6be8e7cSmarkus   for (i = 0; i < n; i++)
610a6be8e7cSmarkus     sig_msg[i] = R[i];
611a6be8e7cSmarkus 
612a6be8e7cSmarkus   sig_msg += n;
613a6be8e7cSmarkus   *sig_msg_len += n;
614a6be8e7cSmarkus 
615a6be8e7cSmarkus   // ----------------------------------
616a6be8e7cSmarkus   // Now we start to "really sign"
617a6be8e7cSmarkus   // ----------------------------------
618a6be8e7cSmarkus 
619a6be8e7cSmarkus   // Prepare Address
620a6be8e7cSmarkus   setType(ots_addr, 0);
621a6be8e7cSmarkus   setOTSADRS(ots_addr, idx);
622a6be8e7cSmarkus 
623a6be8e7cSmarkus   // Compute seed for OTS key pair
624a6be8e7cSmarkus   get_seed(ots_seed, sk_seed, n, ots_addr);
625a6be8e7cSmarkus 
626a6be8e7cSmarkus   // Compute WOTS signature
627a6be8e7cSmarkus   wots_sign(sig_msg, msg_h, ots_seed, &(params->wots_par), pub_seed, ots_addr);
628a6be8e7cSmarkus 
629a6be8e7cSmarkus   sig_msg += params->wots_par.keysize;
630a6be8e7cSmarkus   *sig_msg_len += params->wots_par.keysize;
631a6be8e7cSmarkus 
632a6be8e7cSmarkus   // the auth path was already computed during the previous round
633a6be8e7cSmarkus   memcpy(sig_msg, state->auth, h*n);
634a6be8e7cSmarkus 
635a6be8e7cSmarkus   if (idx < (1U << h) - 1) {
636a6be8e7cSmarkus     bds_round(state, idx, sk_seed, params, pub_seed, ots_addr);
637a6be8e7cSmarkus     bds_treehash_update(state, (h - k) >> 1, sk_seed, params, pub_seed, ots_addr);
638a6be8e7cSmarkus   }
639a6be8e7cSmarkus 
640a6be8e7cSmarkus /* TODO: save key/bds state here! */
641a6be8e7cSmarkus 
642a6be8e7cSmarkus   sig_msg += params->h*n;
643a6be8e7cSmarkus   *sig_msg_len += params->h*n;
644a6be8e7cSmarkus 
645a6be8e7cSmarkus   //Whipe secret elements?
646a6be8e7cSmarkus   //zerobytes(tsk, CRYPTO_SECRETKEYBYTES);
647a6be8e7cSmarkus 
648a6be8e7cSmarkus 
649a6be8e7cSmarkus   memcpy(sig_msg, msg, msglen);
650a6be8e7cSmarkus   *sig_msg_len += msglen;
651a6be8e7cSmarkus 
652a6be8e7cSmarkus   return 0;
653a6be8e7cSmarkus }
654a6be8e7cSmarkus 
655a6be8e7cSmarkus /**
656a6be8e7cSmarkus  * Verifies a given message signature pair under a given public key.
657a6be8e7cSmarkus  */
xmss_sign_open(unsigned char * msg,unsigned long long * msglen,const unsigned char * sig_msg,unsigned long long sig_msg_len,const unsigned char * pk,const xmss_params * params)658a6be8e7cSmarkus int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params)
659a6be8e7cSmarkus {
660a6be8e7cSmarkus   unsigned int n = params->n;
661a6be8e7cSmarkus 
662a6be8e7cSmarkus   unsigned long long i, m_len;
663a6be8e7cSmarkus   unsigned long idx=0;
664a6be8e7cSmarkus   unsigned char wots_pk[params->wots_par.keysize];
665a6be8e7cSmarkus   unsigned char pkhash[n];
666a6be8e7cSmarkus   unsigned char root[n];
667a6be8e7cSmarkus   unsigned char msg_h[n];
668a6be8e7cSmarkus   unsigned char hash_key[3*n];
669a6be8e7cSmarkus 
670a6be8e7cSmarkus   unsigned char pub_seed[n];
671a6be8e7cSmarkus   memcpy(pub_seed, pk+n, n);
672a6be8e7cSmarkus 
673a6be8e7cSmarkus   // Init addresses
674a6be8e7cSmarkus   uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
675a6be8e7cSmarkus   uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
676a6be8e7cSmarkus   uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
677a6be8e7cSmarkus 
678a6be8e7cSmarkus   setType(ots_addr, 0);
679a6be8e7cSmarkus   setType(ltree_addr, 1);
680a6be8e7cSmarkus   setType(node_addr, 2);
681a6be8e7cSmarkus 
682a6be8e7cSmarkus   // Extract index
683a6be8e7cSmarkus   idx = ((unsigned long)sig_msg[0] << 24) | ((unsigned long)sig_msg[1] << 16) | ((unsigned long)sig_msg[2] << 8) | sig_msg[3];
684a6be8e7cSmarkus   printf("verify:: idx = %lu\n", idx);
685a6be8e7cSmarkus 
686a6be8e7cSmarkus   // Generate hash key (R || root || idx)
687a6be8e7cSmarkus   memcpy(hash_key, sig_msg+4,n);
688a6be8e7cSmarkus   memcpy(hash_key+n, pk, n);
689a6be8e7cSmarkus   to_byte(hash_key+2*n, idx, n);
690a6be8e7cSmarkus 
691a6be8e7cSmarkus   sig_msg += (n+4);
692a6be8e7cSmarkus   sig_msg_len -= (n+4);
693a6be8e7cSmarkus 
694a6be8e7cSmarkus   // hash message
695a6be8e7cSmarkus   unsigned long long tmp_sig_len = params->wots_par.keysize+params->h*n;
696a6be8e7cSmarkus   m_len = sig_msg_len - tmp_sig_len;
697a6be8e7cSmarkus   h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
698a6be8e7cSmarkus 
699a6be8e7cSmarkus   //-----------------------
700a6be8e7cSmarkus   // Verify signature
701a6be8e7cSmarkus   //-----------------------
702a6be8e7cSmarkus 
703a6be8e7cSmarkus   // Prepare Address
704a6be8e7cSmarkus   setOTSADRS(ots_addr, idx);
705a6be8e7cSmarkus   // Check WOTS signature
706a6be8e7cSmarkus   wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->wots_par), pub_seed, ots_addr);
707a6be8e7cSmarkus 
708a6be8e7cSmarkus   sig_msg += params->wots_par.keysize;
709a6be8e7cSmarkus   sig_msg_len -= params->wots_par.keysize;
710a6be8e7cSmarkus 
711a6be8e7cSmarkus   // Compute Ltree
712a6be8e7cSmarkus   setLtreeADRS(ltree_addr, idx);
713a6be8e7cSmarkus   l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr);
714a6be8e7cSmarkus 
715a6be8e7cSmarkus   // Compute root
716a6be8e7cSmarkus   validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr);
717a6be8e7cSmarkus 
718a6be8e7cSmarkus   sig_msg += params->h*n;
719a6be8e7cSmarkus   sig_msg_len -= params->h*n;
720a6be8e7cSmarkus 
721a6be8e7cSmarkus   for (i = 0; i < n; i++)
722a6be8e7cSmarkus     if (root[i] != pk[i])
723a6be8e7cSmarkus       goto fail;
724a6be8e7cSmarkus 
725a6be8e7cSmarkus   *msglen = sig_msg_len;
726a6be8e7cSmarkus   for (i = 0; i < *msglen; i++)
727a6be8e7cSmarkus     msg[i] = sig_msg[i];
728a6be8e7cSmarkus 
729a6be8e7cSmarkus   return 0;
730a6be8e7cSmarkus 
731a6be8e7cSmarkus 
732a6be8e7cSmarkus fail:
733a6be8e7cSmarkus   *msglen = sig_msg_len;
734a6be8e7cSmarkus   for (i = 0; i < *msglen; i++)
735a6be8e7cSmarkus     msg[i] = 0;
736a6be8e7cSmarkus   *msglen = -1;
737a6be8e7cSmarkus   return -1;
738a6be8e7cSmarkus }
739a6be8e7cSmarkus 
740a6be8e7cSmarkus /*
741a6be8e7cSmarkus  * Generates a XMSSMT key pair for a given parameter set.
742a6be8e7cSmarkus  * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
743a6be8e7cSmarkus  * Format pk: [root || PUB_SEED] omitting algo oid.
744a6be8e7cSmarkus  */
xmssmt_keypair(unsigned char * pk,unsigned char * sk,bds_state * states,unsigned char * wots_sigs,xmssmt_params * params)745a6be8e7cSmarkus int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params)
746a6be8e7cSmarkus {
747a6be8e7cSmarkus   unsigned int n = params->n;
748a6be8e7cSmarkus   unsigned int i;
749a6be8e7cSmarkus   unsigned char ots_seed[params->n];
750a6be8e7cSmarkus   // Set idx = 0
751a6be8e7cSmarkus   for (i = 0; i < params->index_len; i++) {
752a6be8e7cSmarkus     sk[i] = 0;
753a6be8e7cSmarkus   }
754a6be8e7cSmarkus   // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
755a6be8e7cSmarkus   randombytes(sk+params->index_len, 3*n);
756a6be8e7cSmarkus   // Copy PUB_SEED to public key
757a6be8e7cSmarkus   memcpy(pk+n, sk+params->index_len+2*n, n);
758a6be8e7cSmarkus 
759a6be8e7cSmarkus   // Set address to point on the single tree on layer d-1
760a6be8e7cSmarkus   uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
761a6be8e7cSmarkus   setLayerADRS(addr, (params->d-1));
762a6be8e7cSmarkus   // Set up state and compute wots signatures for all but topmost tree root
763a6be8e7cSmarkus   for (i = 0; i < params->d - 1; i++) {
764a6be8e7cSmarkus     // Compute seed for OTS key pair
765a6be8e7cSmarkus     treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr);
766a6be8e7cSmarkus     setLayerADRS(addr, (i+1));
767a6be8e7cSmarkus     get_seed(ots_seed, sk+params->index_len, n, addr);
768a6be8e7cSmarkus     wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, pk, ots_seed, &(params->xmss_par.wots_par), pk+n, addr);
769a6be8e7cSmarkus   }
770a6be8e7cSmarkus   treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr);
771a6be8e7cSmarkus   memcpy(sk+params->index_len+3*n, pk, n);
772a6be8e7cSmarkus   return 0;
773a6be8e7cSmarkus }
774a6be8e7cSmarkus 
775a6be8e7cSmarkus /**
776a6be8e7cSmarkus  * Signs a message.
777a6be8e7cSmarkus  * Returns
778a6be8e7cSmarkus  * 1. an array containing the signature followed by the message AND
779a6be8e7cSmarkus  * 2. an updated secret key!
780a6be8e7cSmarkus  *
781a6be8e7cSmarkus  */
xmssmt_sign(unsigned char * sk,bds_state * states,unsigned char * wots_sigs,unsigned char * sig_msg,unsigned long long * sig_msg_len,const unsigned char * msg,unsigned long long msglen,const xmssmt_params * params)782a6be8e7cSmarkus int xmssmt_sign(unsigned char *sk, bds_state *states, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params)
783a6be8e7cSmarkus {
784a6be8e7cSmarkus   unsigned int n = params->n;
785a6be8e7cSmarkus 
786a6be8e7cSmarkus   unsigned int tree_h = params->xmss_par.h;
787a6be8e7cSmarkus   unsigned int h = params->h;
788a6be8e7cSmarkus   unsigned int k = params->xmss_par.k;
789a6be8e7cSmarkus   unsigned int idx_len = params->index_len;
790a6be8e7cSmarkus   uint64_t idx_tree;
791a6be8e7cSmarkus   uint32_t idx_leaf;
792a6be8e7cSmarkus   uint64_t i, j;
793a6be8e7cSmarkus   int needswap_upto = -1;
794a6be8e7cSmarkus   unsigned int updates;
795a6be8e7cSmarkus 
796a6be8e7cSmarkus   unsigned char sk_seed[n];
797a6be8e7cSmarkus   unsigned char sk_prf[n];
798a6be8e7cSmarkus   unsigned char pub_seed[n];
799a6be8e7cSmarkus   // Init working params
800a6be8e7cSmarkus   unsigned char R[n];
801a6be8e7cSmarkus   unsigned char msg_h[n];
802a6be8e7cSmarkus   unsigned char hash_key[3*n];
803a6be8e7cSmarkus   unsigned char ots_seed[n];
804a6be8e7cSmarkus   uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
805a6be8e7cSmarkus   uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
806a6be8e7cSmarkus   unsigned char idx_bytes_32[32];
807a6be8e7cSmarkus   bds_state tmp;
808a6be8e7cSmarkus 
809a6be8e7cSmarkus   // Extract SK
810a6be8e7cSmarkus   unsigned long long idx = 0;
811a6be8e7cSmarkus   for (i = 0; i < idx_len; i++) {
812a6be8e7cSmarkus     idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i);
813a6be8e7cSmarkus   }
814a6be8e7cSmarkus 
815a6be8e7cSmarkus   memcpy(sk_seed, sk+idx_len, n);
816a6be8e7cSmarkus   memcpy(sk_prf, sk+idx_len+n, n);
817a6be8e7cSmarkus   memcpy(pub_seed, sk+idx_len+2*n, n);
818a6be8e7cSmarkus 
819a6be8e7cSmarkus   // Update SK
820a6be8e7cSmarkus   for (i = 0; i < idx_len; i++) {
821a6be8e7cSmarkus     sk[i] = ((idx + 1) >> 8*(idx_len - 1 - i)) & 255;
822a6be8e7cSmarkus   }
823a6be8e7cSmarkus   // -- Secret key for this non-forward-secure version is now updated.
824a6be8e7cSmarkus   // -- A productive implementation should use a file handle instead and write the updated secret key at this point!
825a6be8e7cSmarkus 
826a6be8e7cSmarkus 
827a6be8e7cSmarkus   // ---------------------------------
828a6be8e7cSmarkus   // Message Hashing
829a6be8e7cSmarkus   // ---------------------------------
830a6be8e7cSmarkus 
831a6be8e7cSmarkus   // Message Hash:
832a6be8e7cSmarkus   // First compute pseudorandom value
833a6be8e7cSmarkus   to_byte(idx_bytes_32, idx, 32);
834a6be8e7cSmarkus   prf(R, idx_bytes_32, sk_prf, n);
835a6be8e7cSmarkus   // Generate hash key (R || root || idx)
836a6be8e7cSmarkus   memcpy(hash_key, R, n);
837a6be8e7cSmarkus   memcpy(hash_key+n, sk+idx_len+3*n, n);
838a6be8e7cSmarkus   to_byte(hash_key+2*n, idx, n);
839a6be8e7cSmarkus 
840a6be8e7cSmarkus   // Then use it for message digest
841a6be8e7cSmarkus   h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
842a6be8e7cSmarkus 
843a6be8e7cSmarkus   // Start collecting signature
844a6be8e7cSmarkus   *sig_msg_len = 0;
845a6be8e7cSmarkus 
846a6be8e7cSmarkus   // Copy index to signature
847a6be8e7cSmarkus   for (i = 0; i < idx_len; i++) {
848a6be8e7cSmarkus     sig_msg[i] = (idx >> 8*(idx_len - 1 - i)) & 255;
849a6be8e7cSmarkus   }
850a6be8e7cSmarkus 
851a6be8e7cSmarkus   sig_msg += idx_len;
852a6be8e7cSmarkus   *sig_msg_len += idx_len;
853a6be8e7cSmarkus 
854a6be8e7cSmarkus   // Copy R to signature
855a6be8e7cSmarkus   for (i = 0; i < n; i++)
856a6be8e7cSmarkus     sig_msg[i] = R[i];
857a6be8e7cSmarkus 
858a6be8e7cSmarkus   sig_msg += n;
859a6be8e7cSmarkus   *sig_msg_len += n;
860a6be8e7cSmarkus 
861a6be8e7cSmarkus   // ----------------------------------
862a6be8e7cSmarkus   // Now we start to "really sign"
863a6be8e7cSmarkus   // ----------------------------------
864a6be8e7cSmarkus 
865a6be8e7cSmarkus   // Handle lowest layer separately as it is slightly different...
866a6be8e7cSmarkus 
867a6be8e7cSmarkus   // Prepare Address
868a6be8e7cSmarkus   setType(ots_addr, 0);
869a6be8e7cSmarkus   idx_tree = idx >> tree_h;
870a6be8e7cSmarkus   idx_leaf = (idx & ((1 << tree_h)-1));
871a6be8e7cSmarkus   setLayerADRS(ots_addr, 0);
872a6be8e7cSmarkus   setTreeADRS(ots_addr, idx_tree);
873a6be8e7cSmarkus   setOTSADRS(ots_addr, idx_leaf);
874a6be8e7cSmarkus 
875a6be8e7cSmarkus   // Compute seed for OTS key pair
876a6be8e7cSmarkus   get_seed(ots_seed, sk_seed, n, ots_addr);
877a6be8e7cSmarkus 
878a6be8e7cSmarkus   // Compute WOTS signature
879a6be8e7cSmarkus   wots_sign(sig_msg, msg_h, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
880a6be8e7cSmarkus 
881a6be8e7cSmarkus   sig_msg += params->xmss_par.wots_par.keysize;
882a6be8e7cSmarkus   *sig_msg_len += params->xmss_par.wots_par.keysize;
883a6be8e7cSmarkus 
884a6be8e7cSmarkus   memcpy(sig_msg, states[0].auth, tree_h*n);
885a6be8e7cSmarkus   sig_msg += tree_h*n;
886a6be8e7cSmarkus   *sig_msg_len += tree_h*n;
887a6be8e7cSmarkus 
888a6be8e7cSmarkus   // prepare signature of remaining layers
889a6be8e7cSmarkus   for (i = 1; i < params->d; i++) {
890a6be8e7cSmarkus     // put WOTS signature in place
891a6be8e7cSmarkus     memcpy(sig_msg, wots_sigs + (i-1)*params->xmss_par.wots_par.keysize, params->xmss_par.wots_par.keysize);
892a6be8e7cSmarkus 
893a6be8e7cSmarkus     sig_msg += params->xmss_par.wots_par.keysize;
894a6be8e7cSmarkus     *sig_msg_len += params->xmss_par.wots_par.keysize;
895a6be8e7cSmarkus 
896a6be8e7cSmarkus     // put AUTH nodes in place
897a6be8e7cSmarkus     memcpy(sig_msg, states[i].auth, tree_h*n);
898a6be8e7cSmarkus     sig_msg += tree_h*n;
899a6be8e7cSmarkus     *sig_msg_len += tree_h*n;
900a6be8e7cSmarkus   }
901a6be8e7cSmarkus 
902a6be8e7cSmarkus   updates = (tree_h - k) >> 1;
903a6be8e7cSmarkus 
904a6be8e7cSmarkus   setTreeADRS(addr, (idx_tree + 1));
905a6be8e7cSmarkus   // mandatory update for NEXT_0 (does not count towards h-k/2) if NEXT_0 exists
906a6be8e7cSmarkus   if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << h)) {
907a6be8e7cSmarkus     bds_state_update(&states[params->d], sk_seed, &(params->xmss_par), pub_seed, addr);
908a6be8e7cSmarkus   }
909a6be8e7cSmarkus 
910a6be8e7cSmarkus   for (i = 0; i < params->d; i++) {
911a6be8e7cSmarkus     // check if we're not at the end of a tree
912a6be8e7cSmarkus     if (! (((idx + 1) & ((1ULL << ((i+1)*tree_h)) - 1)) == 0)) {
913a6be8e7cSmarkus       idx_leaf = (idx >> (tree_h * i)) & ((1 << tree_h)-1);
914a6be8e7cSmarkus       idx_tree = (idx >> (tree_h * (i+1)));
915a6be8e7cSmarkus       setLayerADRS(addr, i);
916a6be8e7cSmarkus       setTreeADRS(addr, idx_tree);
917a6be8e7cSmarkus       if (i == (unsigned int) (needswap_upto + 1)) {
918a6be8e7cSmarkus         bds_round(&states[i], idx_leaf, sk_seed, &(params->xmss_par), pub_seed, addr);
919a6be8e7cSmarkus       }
920a6be8e7cSmarkus       updates = bds_treehash_update(&states[i], updates, sk_seed, &(params->xmss_par), pub_seed, addr);
921a6be8e7cSmarkus       setTreeADRS(addr, (idx_tree + 1));
922a6be8e7cSmarkus       // if a NEXT-tree exists for this level;
923a6be8e7cSmarkus       if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << (h - tree_h * i))) {
924a6be8e7cSmarkus         if (i > 0 && updates > 0 && states[params->d + i].next_leaf < (1ULL << h)) {
925a6be8e7cSmarkus           bds_state_update(&states[params->d + i], sk_seed, &(params->xmss_par), pub_seed, addr);
926a6be8e7cSmarkus           updates--;
927a6be8e7cSmarkus         }
928a6be8e7cSmarkus       }
929a6be8e7cSmarkus     }
930a6be8e7cSmarkus     else if (idx < (1ULL << h) - 1) {
931a6be8e7cSmarkus       memcpy(&tmp, states+params->d + i, sizeof(bds_state));
932a6be8e7cSmarkus       memcpy(states+params->d + i, states + i, sizeof(bds_state));
933a6be8e7cSmarkus       memcpy(states + i, &tmp, sizeof(bds_state));
934a6be8e7cSmarkus 
935a6be8e7cSmarkus       setLayerADRS(ots_addr, (i+1));
936a6be8e7cSmarkus       setTreeADRS(ots_addr, ((idx + 1) >> ((i+2) * tree_h)));
937a6be8e7cSmarkus       setOTSADRS(ots_addr, (((idx >> ((i+1) * tree_h)) + 1) & ((1 << tree_h)-1)));
938a6be8e7cSmarkus 
939a6be8e7cSmarkus       get_seed(ots_seed, sk+params->index_len, n, ots_addr);
940a6be8e7cSmarkus       wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, states[i].stack, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
941a6be8e7cSmarkus 
942a6be8e7cSmarkus       states[params->d + i].stackoffset = 0;
943a6be8e7cSmarkus       states[params->d + i].next_leaf = 0;
944a6be8e7cSmarkus 
945a6be8e7cSmarkus       updates--; // WOTS-signing counts as one update
946a6be8e7cSmarkus       needswap_upto = i;
947a6be8e7cSmarkus       for (j = 0; j < tree_h-k; j++) {
948a6be8e7cSmarkus         states[i].treehash[j].completed = 1;
949a6be8e7cSmarkus       }
950a6be8e7cSmarkus     }
951a6be8e7cSmarkus   }
952a6be8e7cSmarkus 
953a6be8e7cSmarkus   //Whipe secret elements?
954a6be8e7cSmarkus   //zerobytes(tsk, CRYPTO_SECRETKEYBYTES);
955a6be8e7cSmarkus 
956a6be8e7cSmarkus   memcpy(sig_msg, msg, msglen);
957a6be8e7cSmarkus   *sig_msg_len += msglen;
958a6be8e7cSmarkus 
959a6be8e7cSmarkus   return 0;
960a6be8e7cSmarkus }
961a6be8e7cSmarkus 
962a6be8e7cSmarkus /**
963a6be8e7cSmarkus  * Verifies a given message signature pair under a given public key.
964a6be8e7cSmarkus  */
xmssmt_sign_open(unsigned char * msg,unsigned long long * msglen,const unsigned char * sig_msg,unsigned long long sig_msg_len,const unsigned char * pk,const xmssmt_params * params)965a6be8e7cSmarkus int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params)
966a6be8e7cSmarkus {
967a6be8e7cSmarkus   unsigned int n = params->n;
968a6be8e7cSmarkus 
969a6be8e7cSmarkus   unsigned int tree_h = params->xmss_par.h;
970a6be8e7cSmarkus   unsigned int idx_len = params->index_len;
971a6be8e7cSmarkus   uint64_t idx_tree;
972a6be8e7cSmarkus   uint32_t idx_leaf;
973a6be8e7cSmarkus 
974a6be8e7cSmarkus   unsigned long long i, m_len;
975a6be8e7cSmarkus   unsigned long long idx=0;
976a6be8e7cSmarkus   unsigned char wots_pk[params->xmss_par.wots_par.keysize];
977a6be8e7cSmarkus   unsigned char pkhash[n];
978a6be8e7cSmarkus   unsigned char root[n];
979a6be8e7cSmarkus   unsigned char msg_h[n];
980a6be8e7cSmarkus   unsigned char hash_key[3*n];
981a6be8e7cSmarkus 
982a6be8e7cSmarkus   unsigned char pub_seed[n];
983a6be8e7cSmarkus   memcpy(pub_seed, pk+n, n);
984a6be8e7cSmarkus 
985a6be8e7cSmarkus   // Init addresses
986a6be8e7cSmarkus   uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
987a6be8e7cSmarkus   uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
988a6be8e7cSmarkus   uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
989a6be8e7cSmarkus 
990a6be8e7cSmarkus   // Extract index
991a6be8e7cSmarkus   for (i = 0; i < idx_len; i++) {
992a6be8e7cSmarkus     idx |= ((unsigned long long)sig_msg[i]) << (8*(idx_len - 1 - i));
993a6be8e7cSmarkus   }
994a6be8e7cSmarkus   printf("verify:: idx = %llu\n", idx);
995a6be8e7cSmarkus   sig_msg += idx_len;
996a6be8e7cSmarkus   sig_msg_len -= idx_len;
997a6be8e7cSmarkus 
998a6be8e7cSmarkus   // Generate hash key (R || root || idx)
999a6be8e7cSmarkus   memcpy(hash_key, sig_msg,n);
1000a6be8e7cSmarkus   memcpy(hash_key+n, pk, n);
1001a6be8e7cSmarkus   to_byte(hash_key+2*n, idx, n);
1002a6be8e7cSmarkus 
1003a6be8e7cSmarkus   sig_msg += n;
1004a6be8e7cSmarkus   sig_msg_len -= n;
1005a6be8e7cSmarkus 
1006a6be8e7cSmarkus 
1007a6be8e7cSmarkus   // hash message (recall, R is now on pole position at sig_msg
1008a6be8e7cSmarkus   unsigned long long tmp_sig_len = (params->d * params->xmss_par.wots_par.keysize) + (params->h * n);
1009a6be8e7cSmarkus   m_len = sig_msg_len - tmp_sig_len;
1010a6be8e7cSmarkus   h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
1011a6be8e7cSmarkus 
1012a6be8e7cSmarkus 
1013a6be8e7cSmarkus   //-----------------------
1014a6be8e7cSmarkus   // Verify signature
1015a6be8e7cSmarkus   //-----------------------
1016a6be8e7cSmarkus 
1017a6be8e7cSmarkus   // Prepare Address
1018a6be8e7cSmarkus   idx_tree = idx >> tree_h;
1019a6be8e7cSmarkus   idx_leaf = (idx & ((1 << tree_h)-1));
1020a6be8e7cSmarkus   setLayerADRS(ots_addr, 0);
1021a6be8e7cSmarkus   setTreeADRS(ots_addr, idx_tree);
1022a6be8e7cSmarkus   setType(ots_addr, 0);
1023a6be8e7cSmarkus 
1024a6be8e7cSmarkus   memcpy(ltree_addr, ots_addr, 12);
1025a6be8e7cSmarkus   setType(ltree_addr, 1);
1026a6be8e7cSmarkus 
1027a6be8e7cSmarkus   memcpy(node_addr, ltree_addr, 12);
1028a6be8e7cSmarkus   setType(node_addr, 2);
1029a6be8e7cSmarkus 
1030a6be8e7cSmarkus   setOTSADRS(ots_addr, idx_leaf);
1031a6be8e7cSmarkus 
1032a6be8e7cSmarkus   // Check WOTS signature
1033a6be8e7cSmarkus   wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->xmss_par.wots_par), pub_seed, ots_addr);
1034a6be8e7cSmarkus 
1035a6be8e7cSmarkus   sig_msg += params->xmss_par.wots_par.keysize;
1036a6be8e7cSmarkus   sig_msg_len -= params->xmss_par.wots_par.keysize;
1037a6be8e7cSmarkus 
1038a6be8e7cSmarkus   // Compute Ltree
1039a6be8e7cSmarkus   setLtreeADRS(ltree_addr, idx_leaf);
1040a6be8e7cSmarkus   l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
1041a6be8e7cSmarkus 
1042a6be8e7cSmarkus   // Compute root
1043a6be8e7cSmarkus   validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
1044a6be8e7cSmarkus 
1045a6be8e7cSmarkus   sig_msg += tree_h*n;
1046a6be8e7cSmarkus   sig_msg_len -= tree_h*n;
1047a6be8e7cSmarkus 
1048a6be8e7cSmarkus   for (i = 1; i < params->d; i++) {
1049a6be8e7cSmarkus     // Prepare Address
1050a6be8e7cSmarkus     idx_leaf = (idx_tree & ((1 << tree_h)-1));
1051a6be8e7cSmarkus     idx_tree = idx_tree >> tree_h;
1052a6be8e7cSmarkus 
1053a6be8e7cSmarkus     setLayerADRS(ots_addr, i);
1054a6be8e7cSmarkus     setTreeADRS(ots_addr, idx_tree);
1055a6be8e7cSmarkus     setType(ots_addr, 0);
1056a6be8e7cSmarkus 
1057a6be8e7cSmarkus     memcpy(ltree_addr, ots_addr, 12);
1058a6be8e7cSmarkus     setType(ltree_addr, 1);
1059a6be8e7cSmarkus 
1060a6be8e7cSmarkus     memcpy(node_addr, ltree_addr, 12);
1061a6be8e7cSmarkus     setType(node_addr, 2);
1062a6be8e7cSmarkus 
1063a6be8e7cSmarkus     setOTSADRS(ots_addr, idx_leaf);
1064a6be8e7cSmarkus 
1065a6be8e7cSmarkus     // Check WOTS signature
1066a6be8e7cSmarkus     wots_pkFromSig(wots_pk, sig_msg, root, &(params->xmss_par.wots_par), pub_seed, ots_addr);
1067a6be8e7cSmarkus 
1068a6be8e7cSmarkus     sig_msg += params->xmss_par.wots_par.keysize;
1069a6be8e7cSmarkus     sig_msg_len -= params->xmss_par.wots_par.keysize;
1070a6be8e7cSmarkus 
1071a6be8e7cSmarkus     // Compute Ltree
1072a6be8e7cSmarkus     setLtreeADRS(ltree_addr, idx_leaf);
1073a6be8e7cSmarkus     l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
1074a6be8e7cSmarkus 
1075a6be8e7cSmarkus     // Compute root
1076a6be8e7cSmarkus     validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
1077a6be8e7cSmarkus 
1078a6be8e7cSmarkus     sig_msg += tree_h*n;
1079a6be8e7cSmarkus     sig_msg_len -= tree_h*n;
1080a6be8e7cSmarkus 
1081a6be8e7cSmarkus   }
1082a6be8e7cSmarkus 
1083a6be8e7cSmarkus   for (i = 0; i < n; i++)
1084a6be8e7cSmarkus     if (root[i] != pk[i])
1085a6be8e7cSmarkus       goto fail;
1086a6be8e7cSmarkus 
1087a6be8e7cSmarkus   *msglen = sig_msg_len;
1088a6be8e7cSmarkus   for (i = 0; i < *msglen; i++)
1089a6be8e7cSmarkus     msg[i] = sig_msg[i];
1090a6be8e7cSmarkus 
1091a6be8e7cSmarkus   return 0;
1092a6be8e7cSmarkus 
1093a6be8e7cSmarkus 
1094a6be8e7cSmarkus fail:
1095a6be8e7cSmarkus   *msglen = sig_msg_len;
1096a6be8e7cSmarkus   for (i = 0; i < *msglen; i++)
1097a6be8e7cSmarkus     msg[i] = 0;
1098a6be8e7cSmarkus   *msglen = -1;
1099a6be8e7cSmarkus   return -1;
1100a6be8e7cSmarkus }
1101