forked from cisco/hash-sigs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hss_alloc.c
552 lines (500 loc) · 21.7 KB
/
hss_alloc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/*
* This is the code which allocates a working key (and initializes the fields
* that are independent of the key)
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "hss.h"
#include "hss_internal.h"
#include "lm_common.h"
#define MALLOC_OVERHEAD 8 /* Our simplistic model about the overhead */
/* that malloc takes up is that it adds 8 */
/* bytes to any request we make. This isn't */
/* precise (especially if we consider external */
/* fragmentation), it's just a guideline */
/*
* Function to estimate the amount of memory we'd use at a particular level,
* if we went with a particular subtree size
* - i is which tree in the scheme we're talking about; 0 is the root tree
* We have this because we allocate less for the root tree
* - subtree_size is the size of the subtrees we're considering
* - total_length is the size of the trees
* - size_hash is the length of the hash output (always 32 currently)
* - if psubtree_levels is non-NULL, we'll return the number of subtree levels
* here
* - if pstack_total is non-NULL, we'll return the bytes of stack space needed
* by the subtrees of this level here
* The value returned is the amount of space used by the merkle
* level structures, the subtree structures, plus the additional stack
* space required
*/
static size_t compute_level_memory_usage(int i, unsigned subtree_size,
unsigned total_height, unsigned size_hash,
unsigned *psubtree_levels,
size_t *pstack_total) {
/* Compute the number of subtree levels we'd have */
unsigned subtree_levels = (total_height + subtree_size - 1) / subtree_size;
unsigned top_subtree_size = total_height - (subtree_levels-1)*subtree_size;
/* The top level tree has no next subtrees */
int have_next_subtree = (i == 0) ? 0 : 1;
size_t stack_total = 0;
/* Compute the memory this would use */
size_t memory_used = sizeof(struct merkle_level) + MALLOC_OVERHEAD;
unsigned j;
for (j=0; j<subtree_levels-1; j++) {
/* # of subtrees at this level */
int num_subtrees = 2 + have_next_subtree;
/* the size of each subtree */
size_t size_subtree = sizeof(struct subtree) + MALLOC_OVERHEAD +
size_hash * (((size_t)2<<subtree_size)-1);
size_t size_stack = (num_subtrees-1) * size_hash * j * subtree_size;
memory_used += num_subtrees * size_subtree + size_stack;
stack_total += size_stack;
}
/* The top level subtree is a bit different; it has no building subtree */
int num_subtrees = 1 + have_next_subtree; /* No BUILDING subtrees */
size_t size_subtree = sizeof(struct subtree) + MALLOC_OVERHEAD +
size_hash * (((size_t)2<<top_subtree_size)-1);
size_t size_stack = (num_subtrees-1) * size_hash * j * subtree_size;
memory_used += num_subtrees * size_subtree + size_stack;
stack_total += size_stack;
if (psubtree_levels) *psubtree_levels = subtree_levels;
if (pstack_total) *pstack_total = stack_total;
return memory_used;
}
/*
* As we generate signatures, we walk through the Merkle tree. As we walk
* through the tree at a particular level, it will occassionally send an
* update to its parent tree (that is, give an opportunity for the parent tree
* to do a walk itself). This function returns, given the number of levels
* and subtree height within a Merkle tree, the number of updates this level
* would give its parent
*
* It does come up in some corner cases (such as a bottom level tree with H=5
* and subtree height 3 (8 updates sent to the paremt); and a top level tree
* with H=20 or 25).
*/
static merkle_index_t compute_updates_generated( unsigned height,
unsigned subtree ) {
/* Special case: if the tree consists of 1 subtree, we'll send an */
/* update at each signature */
if (height <= subtree) return (merkle_index_t)1<<height;
int num_sublevels = (height + subtree - 1) / subtree;
/* We'll send an update for every node covered by the next-to-top */
/* subtree */
return (merkle_index_t)1 << ((num_sublevels-1) * subtree);
}
/*
* This will compute the number of updates this tree will need from its child
*/
static merkle_index_t compute_updates_required( unsigned height,
unsigned subtree ) {
int num_sublevels = (height + subtree - 1) / subtree;
/*
* This tree will need this many updates:
* num_sublevels-1 for the BUILDING subtrees (the topmost subtree doesn't
* get one)
* 1 for the NEXT tree
* 1 for our parent
*/
return num_sublevels + 1;
}
/*
* Given a tree height (and whether it's the top level, and the hash size),
* this returns the subtree size that yields the least amount of memory.
* This is used for the non-bottom trees (as there really isn't any point in
* trying to using more memory to make them faster)
* i = 0 is when tree level we're talking about; 0 for the root tree; nonzero
* of the others. n is the size of the hash (which, for the current parameter
* sets, is always 32). We included these because they do influence the
* memory allocation; however, with the current parameter sets, these don't
* actually change the answer
*/
int hss_smallest_subtree_size(int tree_height, int i, int n) {
/* Experements have found that subtree height 2 consistently
* uses the least memory
* Actually, in the corner case of H=15,25, i!=0, it turned out
* that height 3 used a bit less, however not enough to call it
* out
* These expirements were done with n=32; different n would likely
* give other results
*/
#if MIN_SUBTREE > 2
#error We assume that a subtree of size 2 is allowed
#endif
return 2;
}
/*
* This allocates a working key for a particular parameter set, and sets up
* the data fields that are key independent; it doesn't set anything that
* does depend on the key. memory_target is used to guide time/memory
* trade-offs; it's the target memory budget that we try to stay below if
* possible
*/
struct hss_working_key *allocate_working_key(
unsigned levels,
const param_set_t *lm_type, const param_set_t *lm_ots_type,
size_t memory_target,
struct hss_extra_info *info) {
struct hss_extra_info temp_info = { 0 };
if (!info) info = &temp_info;
if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) {
info->error_code = hss_error_bad_param_set;
return 0;
}
/* Assign the memory target to a *signed* variable; signed so that it */
/* can take on negative values meaningfully (to account for cases where */
/* we are "overbudget") */
signed long mem_target;
if (memory_target > LONG_MAX) {
mem_target = LONG_MAX;
} else {
mem_target = memory_target;
}
#if 0
signed long initial_mem_target = mem_target; /* DEBUG HACK */
#endif
struct hss_working_key *w = malloc( sizeof *w );
if (!w) {
info->error_code = hss_error_out_of_memory;
return NULL;
}
mem_target -= sizeof(*w) + MALLOC_OVERHEAD;
int i;
w->levels = levels;
w->status = hss_error_key_uninitialized; /* Not usable until we see a */
/* private key */
w->autoreserve = 0;
/* Initialize all the allocated data structures to NULL */
/* We do this up front so that if we hit an error in the middle, we can */
/* just free everything */
for (i=0; i<MAX_HSS_LEVELS-1; i++) {
w->signed_pk[i] = NULL;
}
for (i=0; i<MAX_HSS_LEVELS; i++) {
w->tree[i] = NULL;
}
w->stack = NULL;
/* Allocate all the memory for the level signatures */
size_t signature_len = 4; /* At the same time, ocmpute the sig length */
for (i=0; i < levels; i++) {
w->siglen[i] = lm_get_signature_len( lm_type[i], lm_ots_type[i] );
signature_len += w->siglen[i];
/* Size of this level's Merkle public key */
size_t pklen = lm_get_public_key_len(lm_type[i]);
if (i != 0) signature_len += pklen;
if (w->siglen[i] == 0) {
hss_free_working_key(w);
info->error_code = hss_error_bad_param_set;
return 0;
}
/* We don't need a allocate a signature for the topmost */
if (i == 0) continue;
w->signed_pk_len[i] = w->siglen[i-1] + pklen;
w->signed_pk[i] = malloc( w->signed_pk_len[i] );
if (!w->signed_pk[i]) {
hss_free_working_key(w);
info->error_code = hss_error_out_of_memory;
return 0;
}
mem_target -= w->signed_pk_len[i] + MALLOC_OVERHEAD;
}
w->signature_len = signature_len;
/* Also account for the overhead for the stack allocation (the memory */
/* used by the stack will be accounted as a part of the tree level size */
mem_target -= MALLOC_OVERHEAD;
/*
* Plot out how many subtree sizes we have at each level. We start by
* computing how much memory we'd use if we minimize each level
*/
unsigned subtree_size[MAX_HSS_LEVELS];
unsigned subtree_levels[MAX_HSS_LEVELS];
unsigned level_hash[MAX_HSS_LEVELS];
unsigned level_height[MAX_HSS_LEVELS];
unsigned hash_size[MAX_HSS_LEVELS];
unsigned total_height = 0;
/* Parse the parameter sets */
for (i=0; i<levels; i++) {
if (!lm_look_up_parameter_set(lm_type[i], &level_hash[i],
&hash_size[i], &level_height[i])) {
hss_free_working_key(w);
info->error_code = hss_error_bad_param_set;
return 0;
}
total_height += level_height[i]; /* Also track the number of */
/* signatures we can generate with this parm set */
}
/*
* Select which subtree sizes that is faster, and fit within the memory
* we've been given. For the nonbottom levels, we always use what's the
* smallest for that particular tree height; there's no point in wasting
* extra memory to make them faster (in that each one can be done during
* the time the bottom level BUILDING subtrees don't need updating).
*/
size_t stack_usage = 0;
for (i=0; i<levels-1; i++) {
int subtree = hss_smallest_subtree_size(level_height[i], i,
hash_size[i]);
/*
* Double check to make sure that this subtree size will allow
* enough updates to perculate to the tree above it. I'm pretty
* sure that, with the current logic and parameter sets, we'll
* never actually need to adjust the subtree size; this is here
* as a double check (at the very least, in case someone adds a
* new parameter set)
*/
/* Updates we will to send to the parent */
merkle_index_t updates_generated = compute_updates_generated(
level_height[i],
subtree );
/* Updates that the parent will need */
merkle_index_t updates_required;
if (i == 0)
updates_required = 0; /* no parent; no updates needed */
else {
updates_required = compute_updates_required( level_height[i-1],
subtree_size[i-1] );
}
if (updates_generated < updates_required) {
/*
* The current settings don't have the parent receiving enough
* updates; if we generate enough signatures, we'll end up using
* a partially updated parent (and that's not good eats). Adjust
* things to avoid the possibility
*/
/* A subtree of 5 is always sufficient (as it always generates */
/* at least 32 updates) */
subtree = 5;
}
subtree_size[i] = subtree;
size_t stack_used;
size_t mem = compute_level_memory_usage(i, subtree,
level_height[i], hash_size[i], &subtree_levels[i],
&stack_used );
mem_target -= mem;
stack_usage += stack_used;
}
/*
* For the bottom-most level, look for the size that is the fastest (fewest
* number of sublevels), and fits within the memory we've been given
* Rules of preference:
* - We prefer something that fits within our budget over something that
* doesn't
* - For things that don't fit our budget, we'll take the samllest
* - For things that do fit out budget, we'll take the fastest (and the
* smallest if they're equally fast; no need to waste memory)
*/
i = levels - 1;
enum {
nothing_yet, /* We haven't found anything yet */
found_overbudget, /* We found something, but it used more */
/* memory than the budget we were given */
found_plenty_memory, /* We found something that fits within */
/* out budget */
} search_status = nothing_yet;
size_t best_mem = 0;
signed long best_levels = 0;
unsigned best_j = 0;
size_t best_stack_used = 0;
unsigned j;
for (j = MIN_SUBTREE; j <= level_height[i]; j++) {
if (levels == 1) {
/* If the tree consists of a single level, then we don't need to */
/* make sure that we have enough time to update the higher trees */
/* (as there ain't none). However, to make sure that we work */
/* with aux data, make sure that the number of levels is a */
/* multiple of the default subtree size. We could make the aux */
/* data logic smarter to handle this corner case, but, well, */
/* it's a corner csae */
int only_subtree_size = hss_smallest_subtree_size(level_height[i],
0, hash_size[i]);
if (j % only_subtree_size != 0) continue;
} else {
/* Make sure this size will send enough updates to our parent */
merkle_index_t updates_generated = compute_updates_generated(
level_height[i], j );
merkle_index_t updates_required = compute_updates_required(
level_height[i-1], subtree_size[i-1] );
if (updates_generated < updates_required) {
/* This subtree size won't work */
continue;
}
}
/* Amount of memory this would use */
/* This is a signed type so that the comparison works as */
/* expected if mem_target is negative */
size_t stack_used;
signed long mem = compute_level_memory_usage(i, j,
level_height[i], hash_size[i], &subtree_levels[i],
&stack_used );
/* # of sublevels this would have */
unsigned sub_levels = (level_height[i] + j - 1) / j;
/*
* Now, from a worse-case standpoint, one sublevel isn't any
* better than two (for a couple of the updates, we'll need to
* update the parent). However, from an average time, one
* sublevel is much better. So, we allow one sublevel to be
* considered better than two (even though everywhere else, we
* consider worse-case time).
*/
if (mem > mem_target) {
/* This would use more memory than we'd like; accept it if */
/* either we have no solution, or it uses less memory than what */
/* we've seen */
if (search_status != nothing_yet && mem > best_mem) continue;
/* This solution is the best so far (however, it doesn't fit) */
search_status = found_overbudget;
} else {
/* This is within our budget; accept it if we haven't seen a */
/* previous solution within our budget, or this uses fewer */
/* levels than the previous solution */
if (search_status == found_plenty_memory) {
if (sub_levels > best_levels) {
/* We've already seen a faster solution */
continue;
}
if (sub_levels == best_levels && mem > best_mem) {
/* We've already seen an equally fast solution that */
/* uses less memory */
continue;
}
}
/* This solution is the best so far (and it fits) */
search_status = found_plenty_memory;
}
/* This is the best option so far; record it */
best_j = j;
best_mem = mem;
best_levels = sub_levels;
best_stack_used = stack_used;
}
if (search_status == nothing_yet) {
/* This can't really happen */
hss_free_working_key(w);
info->error_code = hss_error_internal;
return 0;
}
#if 0
printf( "Allocation = %ld\n", initial_mem_target - mem_target + best_mem ); /* DEBUG HACK */
#endif
subtree_size[i] = best_j;
subtree_levels[i] = (level_height[i] + best_j - 1) / best_j;
stack_usage += best_stack_used;
unsigned char *stack;
if (stack_usage == 0) {
stack = NULL; /* Hey! No stack required */
/* Avoid the malloc, as malloc(0) is allowed to fail */
} else {
stack = malloc(stack_usage);
if (!stack) {
hss_free_working_key(w);
info->error_code = hss_error_out_of_memory;
return 0;
}
}
w->stack = stack;
size_t stack_index = 0;
/*
* Ok, we've figured out the sizes for everything; now do the actual
* allocations
*/
for (i = 0; i<levels; i++) {
struct merkle_level *tree = malloc( sizeof *tree );
if (!tree) {
hss_free_working_key(w);
info->error_code = hss_error_out_of_memory;
return 0;
}
unsigned h0 = level_height[i];
tree->level = h0;
tree->h = level_hash[i];
tree->hash_size = hash_size[i];
tree->lm_type = lm_type[i];
tree->lm_ots_type = lm_ots_type[i];
/* We'll initialize current_index from the private key */
tree->max_index = (1L << tree->level) - 1;
tree->sublevels = subtree_levels[i];
tree->subtree_size = subtree_size[i];
unsigned top_subtree_size = h0 - (subtree_levels[i]-1)*subtree_size[i];
tree->top_subtree_size = top_subtree_size;
unsigned k;
for (j=0; j<MAX_SUBLEVELS; j++)
for (k=0; k<NUM_SUBTREE; k++)
tree->subtree[j][k] = NULL;
w->tree[i] = tree;
unsigned subtree_level = 0;
unsigned levels_below = h0;
for (j=0; j<subtree_levels[i]; j++) {
/* The height of the subtrees at this level */
unsigned height = (j == 0) ? top_subtree_size : subtree_size[i];
levels_below -= height;
for (k=0; k<NUM_SUBTREE; k++) {
/* The root subtree doesn't get a 'building subtree' */
if (j == 0 && k == BUILDING_TREE) continue;
/* The subtrees in the topmost tree don't get a */
/* 'next subtree' */
if (k == NEXT_TREE && i == 0) continue;
struct subtree *s = malloc( sizeof *s + hash_size[i] *
(((size_t)2<<height)-1));
if (!s) {
hss_free_working_key(w);
info->error_code = hss_error_out_of_memory;
return 0;
}
s->level = subtree_level;
s->levels_below = levels_below;
tree->subtree[j][k] = s;
if (k == ACTIVE_TREE) {
/* Active trees don't need no stack */
s->stack = NULL;
} else if (levels_below == 0) {
/* Bottom level subtrees don't need no stack */
s->stack = NULL;
} else {
s->stack = &stack[stack_index];
stack_index += hash_size[i] * levels_below;
}
}
subtree_level += height;
}
}
/* SANITY CHECK */
if (stack_index != stack_usage) {
hss_free_working_key(w);
info->error_code = hss_error_internal;
return 0;
}
/* SANITY CHECK */
/* Compute the max number of signatures we can generate */
if (total_height > 64) total_height = 64; /* (bounded by 2**64) */
w->max_count = ((sequence_t)2 << (total_height-1)) - 1; /* height-1 so */
/* we don't try to shift by 64, and hit undefined behavior */
/* We use the count 0xffff..ffff to signify 'we've used up all our */
/* signatures'. Make sure that is above max_count, even for */
/* parameter sets that can literally generate 2**64 signatures (by */
/* letting them generate only 2**64-1) */
if (total_height == 64) w->max_count--;
return w;
}
void hss_free_working_key(struct hss_working_key *w) {
int i;
if (!w) return;
for (i=0; i<MAX_HSS_LEVELS; i++) {
struct merkle_level *tree = w->tree[i];
if (tree) {
unsigned j, k;
for (j=0; j<MAX_SUBLEVELS; j++)
for (k=0; k<3; k++)
free(tree->subtree[j][k]);
hss_zeroize( tree, sizeof *tree ); /* We have seeds here */
}
free(tree);
}
for (i=0; i<MAX_HSS_LEVELS-1; i++) {
free(w->signed_pk[i]);
}
free(w->stack);
hss_zeroize( w, sizeof *w ); /* We have secret information here */
free(w);
}