#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "hss.h"
#include "hss_internal.h"
#include "lm_common.h"
#define MALLOC_OVERHEAD 8
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) {
unsigned subtree_levels = (total_height + subtree_size - 1) / subtree_size;
unsigned top_subtree_size = total_height - (subtree_levels-1)*subtree_size;
int have_next_subtree = (i == 0) ? 0 : 1;
size_t stack_total = 0;
size_t memory_used = sizeof(struct merkle_level) + MALLOC_OVERHEAD;
unsigned j;
for (j=0; j<subtree_levels-1; j++) {
int num_subtrees = 2 + have_next_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;
}
int num_subtrees = 1 + have_next_subtree;
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;
}
static merkle_index_t compute_updates_generated( unsigned height,
unsigned subtree ) {
if (height <= subtree) return (merkle_index_t)1<<height;
int num_sublevels = (height + subtree - 1) / subtree;
return (merkle_index_t)1 << ((num_sublevels-1) * subtree);
}
static merkle_index_t compute_updates_required( unsigned height,
unsigned subtree ) {
int num_sublevels = (height + subtree - 1) / subtree;
return num_sublevels + 1;
}
int hss_smallest_subtree_size(int tree_height, int i, int n) {
LMS_UNUSED(tree_height);
LMS_UNUSED(i);
LMS_UNUSED(n);
#if MIN_SUBTREE > 2
#error We assume that a subtree of size 2 is allowed
#endif
return 2;
}
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;
}
signed long mem_target;
if (memory_target > LONG_MAX) {
mem_target = LONG_MAX;
} else {
mem_target = (signed long)memory_target;
}
#if 0#endif
struct hss_working_key *w = OQS_MEM_malloc( sizeof *w );
if (!w) {
info->error_code = hss_error_out_of_memory;
return NULL;
}
mem_target -= (signed long)sizeof(*w) + MALLOC_OVERHEAD;
unsigned i;
w->levels = levels;
w->status = hss_error_key_uninitialized;
w->autoreserve = 0;
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;
size_t signature_len = 4;
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_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;
}
if (i == 0) continue;
w->signed_pk_len[i] = w->siglen[i-1] + pklen;
w->signed_pk[i] = OQS_MEM_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 -= (signed long)w->signed_pk_len[i] + MALLOC_OVERHEAD;
}
w->signature_len = signature_len;
mem_target -= (signed long)MALLOC_OVERHEAD;
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;
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];
}
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]);
merkle_index_t updates_generated = compute_updates_generated(
level_height[i],
subtree );
merkle_index_t updates_required;
if (i == 0)
updates_required = 0;
else {
updates_required = compute_updates_required( level_height[i-1],
subtree_size[i-1] );
}
if (updates_generated < updates_required) {
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 -= (signed long)mem;
stack_usage += stack_used;
}
i = levels - 1;
enum {
nothing_yet,
found_overbudget,
found_plenty_memory,
} search_status = nothing_yet;
unsigned long best_mem = 0;
unsigned 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) {
int only_subtree_size = hss_smallest_subtree_size(level_height[i],
0, hash_size[i]);
if (j % only_subtree_size != 0) continue;
} else {
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) {
continue;
}
}
size_t stack_used;
signed long mem = (unsigned long)compute_level_memory_usage(i, j,
level_height[i], hash_size[i], &subtree_levels[i],
&stack_used );
unsigned sub_levels = (level_height[i] + j - 1) / j;
if (mem > mem_target) {
if (search_status != nothing_yet && mem > (signed long)best_mem) continue;
search_status = found_overbudget;
} else {
if (search_status == found_plenty_memory) {
if (sub_levels > best_levels) {
continue;
}
if (sub_levels == best_levels && mem > (signed long)best_mem) {
continue;
}
}
search_status = found_plenty_memory;
}
best_j = j;
best_mem = mem;
best_levels = sub_levels;
best_stack_used = stack_used;
}
if (search_status == nothing_yet) {
hss_free_working_key(w);
info->error_code = hss_error_internal;
return 0;
}
#if 0#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;
} else {
stack = OQS_MEM_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;
for (i = 0; i<levels; i++) {
struct merkle_level *tree = OQS_MEM_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];
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++) {
unsigned height = (j == 0) ? top_subtree_size : subtree_size[i];
levels_below -= height;
for (k=0; k<NUM_SUBTREE; k++) {
if (j == 0 && k == BUILDING_TREE) continue;
if (k == NEXT_TREE && i == 0) continue;
struct subtree *s = OQS_MEM_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) {
s->stack = NULL;
} else if (levels_below == 0) {
s->stack = NULL;
} else {
s->stack = &stack[stack_index];
stack_index += hash_size[i] * levels_below;
}
}
subtree_level += height;
}
}
if (stack_index != stack_usage) {
hss_free_working_key(w);
info->error_code = hss_error_internal;
return 0;
}
if (total_height > 64) total_height = 64;
w->max_count = ((sequence_t)2 << (total_height-1)) - 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++)
OQS_MEM_insecure_free(tree->subtree[j][k]);
hss_zeroize( tree, sizeof *tree );
}
OQS_MEM_insecure_free(tree);
}
for (i=0; i<MAX_HSS_LEVELS-1; i++) {
OQS_MEM_insecure_free(w->signed_pk[i]);
}
OQS_MEM_insecure_free(w->stack);
hss_zeroize( w, sizeof *w );
OQS_MEM_insecure_free(w);
}