#include <stdlib.h>
#include <string.h>
#include "common_defs.h"
#include "hss.h"
#include "hash.h"
#include "endian.h"
#include "hss_internal.h"
#include "hss_aux.h"
#include "hss_thread.h"
#include "hss_reserve.h"
#include "lm_ots.h"
#include "lm_ots_common.h"
#include "hss_derive.h"
enum subtree_build_status {
subtree_got_error,
subtree_more_to_do,
subtree_did_last_node,
subtree_all_done
};
static enum subtree_build_status subtree_add_next_node(
struct subtree *subtree,
struct merkle_level *tree,
int next_tree,
struct thread_collection *col) {
unsigned subtree_size = (subtree->level>0 ? tree->subtree_size :
tree->top_subtree_size);
unsigned log_leafs = subtree_size + subtree->levels_below;
merkle_index_t max_index = (merkle_index_t)1 << log_leafs;
if (subtree->current_index == max_index) return subtree_all_done;
unsigned hash_size = tree->hash_size;
unsigned char cur_val[MAX_HASH];
merkle_index_t i;
unsigned ots_len = (unsigned int)lm_ots_get_public_key_len(tree->lm_ots_type);
unsigned char pub_key[ LEAF_MAX_LEN ];
const unsigned char *I = (next_tree ? tree->I_next : tree->I);
memcpy( pub_key + LEAF_I, I, I_LEN );
SET_D( pub_key + LEAF_D, D_LEAF );
merkle_index_t r = subtree->left_leaf + subtree->current_index;
merkle_index_t q = r | ((merkle_index_t)1 << tree->level);
put_bigendian( pub_key + LEAF_R, q, 4);
const unsigned char *seed = (next_tree ? tree->seed_next : tree->seed);
struct seed_derive derive;
if (!hss_seed_derive_init( &derive, tree->lm_type, tree->lm_ots_type,
I, seed )) return subtree_got_error;
hss_seed_derive_set_q(&derive, r);
if (!lm_ots_generate_public_key(tree->lm_ots_type, I,
r, &derive, pub_key + LEAF_PK, ots_len)) {
hss_seed_derive_done(&derive);
return subtree_got_error;
}
hss_seed_derive_done(&derive);
union hash_context ctx;
hss_hash_ctx( cur_val, tree->h, &ctx, pub_key, LEAF_LEN(hash_size));
merkle_index_t subtree_index = subtree->current_index +
((merkle_index_t)1 << log_leafs);
enum subtree_build_status status = subtree_more_to_do;
for (i=0;; i++) {
if (i >= subtree->levels_below) {
memcpy( &subtree->nodes[ (subtree_index-1) * hash_size ], cur_val, hash_size );
}
if (subtree_index == 1) {
status = subtree_did_last_node;
break;
}
if ((q & 1) == 0) break;
q >>= 1;
unsigned char *left_node;
if (i >= subtree->levels_below) {
left_node = &subtree->nodes[ (subtree_index-2) * hash_size ];
} else {
left_node = subtree->stack + (i * hash_size);
}
hss_combine_internal_nodes( cur_val,
left_node, cur_val,
tree->h, I, hash_size,
q);
subtree_index >>= 1;
}
if (i < subtree->levels_below) {
if (col) hss_thread_before_write(col);
memcpy( subtree->stack + (i * hash_size), cur_val, hash_size );
if (col) hss_thread_after_write(col);
}
subtree->current_index += 1;
return status;
}
static int hss_step_next_tree (struct merkle_level *tree,
const struct hss_working_key *w,
struct thread_collection *col) {
struct subtree *prev_subtree = 0;
struct subtree *subtree = 0;
int j;
LMS_UNUSED(w);
for (j = tree->sublevels-1; j>=0; j--) {
subtree = tree->subtree[j][NEXT_TREE];
if (subtree->current_index < MAX_SUBINDEX) break;
prev_subtree = subtree;
}
unsigned height_subtree = (j == 0) ? tree->top_subtree_size :
tree->subtree_size;
if (j >= 0) {
if (subtree->current_index == 0 && prev_subtree) {
unsigned hash_size = tree->hash_size;
memcpy( &subtree->nodes[ hash_size * (((merkle_index_t)1<<height_subtree)-1) ],
&prev_subtree->nodes[ 0 ],
hash_size );
subtree->current_index = ((merkle_index_t)1 << subtree->levels_below);
}
switch (subtree_add_next_node( subtree, tree, 1, col )) {
case subtree_got_error: default: return 0;
case subtree_more_to_do:
break;
case subtree_did_last_node:
case subtree_all_done:
subtree->current_index = MAX_SUBINDEX;
break;
}
}
return 1;
}
static int generate_merkle_signature(
unsigned char *signature, unsigned signature_len,
struct merkle_level *tree,
const struct hss_working_key *w,
const void *message, size_t message_len) {
LMS_UNUSED(w);
if (signature_len < 4) return 0;
merkle_index_t current_index = tree->current_index;
put_bigendian( signature, current_index, 4 );
signature += 4; signature_len -= 4;
size_t ots_sig_size = lm_ots_get_signature_len( tree->lm_ots_type );
if (ots_sig_size == 0 || ots_sig_size > signature_len) return 0;
if (message == NULL) {
memset( signature, 0, ots_sig_size );
} else {
struct seed_derive derive;
if (!hss_seed_derive_init( &derive,
tree->lm_type, tree->lm_ots_type,
tree->I, tree->seed )) return 0;
hss_seed_derive_set_q(&derive, current_index);
bool success = lm_ots_generate_signature( tree->lm_ots_type, tree->I,
current_index, &derive,
message, message_len, false,
signature, ots_sig_size);
hss_seed_derive_done(&derive);
if (!success) return 0;
}
signature += ots_sig_size; signature_len -= (unsigned)ots_sig_size;
if (signature_len < 4) return 0;
put_bigendian( signature, tree->lm_type, 4 );
signature += 4; signature_len -= 4;
int i, j;
merkle_index_t index = current_index;
unsigned n = tree->hash_size;
for (i = tree->sublevels-1; i>=0; i--) {
int height = (i == 0) ? tree->top_subtree_size : tree->subtree_size;
struct subtree *subtree = tree->subtree[i][ACTIVE_TREE];
merkle_index_t subtree_index = (index &
(((merkle_index_t)1 << height) - 1)) +
((merkle_index_t)1 << height);
for (j = height-1; j>=0; j--) {
if (signature_len < n) return 0;
memcpy( signature, subtree->nodes + n * ((subtree_index^1) - 1), n );
signature += n; signature_len -= n;
subtree_index >>= 1;
}
index >>= height;
}
tree->current_index = current_index + 1;
return 1;
}
bool hss_create_signed_public_key(unsigned char *signed_key,
size_t len_signature,
struct merkle_level *tree,
struct merkle_level *parent,
struct hss_working_key *w) {
unsigned char *public_key = signed_key + len_signature;
put_bigendian( public_key + 0, tree->lm_type, 4 );
put_bigendian( public_key + 4, tree->lm_ots_type, 4 );
memcpy( public_key + 8, tree->I, I_LEN );
unsigned hash_size = tree->hash_size;
memcpy( public_key + 8 + I_LEN,
tree->subtree[0][ACTIVE_TREE]->nodes,
hash_size );
unsigned len_public_key = 8 + I_LEN + hash_size;
if (!generate_merkle_signature( signed_key, (unsigned)len_signature,
parent, w, public_key, len_public_key)) {
return false;
}
parent->update_count = UPDATE_NEXT;
return true;
}
struct gen_sig_detail {
unsigned char *signature;
size_t signature_len;
const unsigned char *message;
size_t message_len;
struct hss_working_key *w;
enum hss_error_code *got_error;
};
static void do_gen_sig( const void *detail, struct thread_collection *col) {
const struct gen_sig_detail *d = detail;
size_t signature_len = d->signature_len;
unsigned char *signature = d->signature;
struct hss_working_key *w = d->w;
unsigned levels = w->levels;
if (signature_len < 4) goto failed;
put_bigendian( signature, levels - 1, 4 );
signature += 4; signature_len -= 4;
unsigned i;
for (i=1; i<levels; i++) {
size_t len_signed_pk = w->signed_pk_len[i];
if (signature_len < len_signed_pk) goto failed;
memcpy( signature, w->signed_pk[i], len_signed_pk );
signature += len_signed_pk; signature_len -= len_signed_pk;
}
if (signature_len < w->siglen[levels-1]) goto failed;
const unsigned char *message = d->message;
size_t message_len = d->message_len;
if (!generate_merkle_signature(signature, (unsigned)signature_len,
w->tree[ levels-1 ], w, message, message_len)) {
goto failed;
}
return;
failed:
hss_thread_before_write(col);
*d->got_error = hss_error_internal;
hss_thread_after_write(col);
}
struct step_next_detail {
struct hss_working_key *w;
struct merkle_level *tree;
enum hss_error_code *got_error;
};
static void do_step_next( const void *detail, struct thread_collection *col) {
const struct step_next_detail *d = detail;
struct hss_working_key *w = d->w;
struct merkle_level *tree = d->tree;
if (!hss_step_next_tree( tree, w, col )) {
hss_thread_before_write(col);
*d->got_error = hss_error_internal;
hss_thread_after_write(col);
}
}
struct step_building_detail {
struct merkle_level *tree;
struct subtree *subtree;
enum hss_error_code *got_error;
};
static void do_step_building( const void *detail,
struct thread_collection *col) {
const struct step_building_detail *d = detail;
struct merkle_level *tree = d->tree;
struct subtree *subtree = d->subtree;
switch (subtree_add_next_node( subtree, tree, 0, col )) {
case subtree_got_error: default:
hss_thread_before_write(col);
*d->got_error = hss_error_internal;
hss_thread_after_write(col);
break;
case subtree_more_to_do:
case subtree_did_last_node:
case subtree_all_done:
break;
}
}
struct update_parent_detail {
struct hss_working_key *w;
enum hss_error_code *got_error;
};
static void do_update_parent( const void *detail,
struct thread_collection *col) {
const struct update_parent_detail *d = detail;
struct hss_working_key *w = d->w;
unsigned levels = w->levels;
unsigned current_level = levels - 2;
for (;;) {
struct merkle_level *tree = w->tree[current_level];
switch (tree->update_count) {
case UPDATE_DONE: return;
case UPDATE_NEXT:
tree->update_count = UPDATE_PARENT;
if (current_level == 0) return;
if (!hss_step_next_tree( tree, w, col )) goto failed;
return;
case UPDATE_PARENT:
tree->update_count = UPDATE_BUILDING + 0;
if (current_level == 0) return;
current_level -= 1;
continue;
default: {
unsigned level_to_update =
(tree->update_count - UPDATE_BUILDING) + 1;
if (level_to_update >= tree->sublevels) {
tree->update_count = UPDATE_DONE;
return;
}
tree->update_count += 1;
struct subtree *subtree =
tree->subtree[level_to_update][BUILDING_TREE];
merkle_index_t tree_leaves = (merkle_index_t)1 << tree->level;
if (subtree->left_leaf >= tree_leaves) {
return;
}
switch (subtree_add_next_node( subtree, tree, 0, col )) {
case subtree_got_error: default: goto failed;
case subtree_did_last_node:
case subtree_all_done:
case subtree_more_to_do:
return;
}
}
}
}
failed:
hss_thread_before_write(col);
*d->got_error = hss_error_internal;
hss_thread_after_write(col);
}
bool hss_generate_signature(
struct hss_working_key *w,
bool (*update_private_key)(unsigned char *private_key,
size_t len_private_key, void *context),
void *context,
const void *message, size_t message_len,
unsigned char *signature, size_t signature_buf_len,
struct hss_extra_info *info) {
struct hss_extra_info temp_info = { 0 };
if (!info) info = &temp_info;
unsigned i;
bool trash_private_key = false;
info->last_signature = false;
if (!w) {
info->error_code = hss_error_got_null;
goto failed;
}
if (w->status != hss_error_none) {
info->error_code = w->status;
goto failed;
}
if (!update_private_key) {
if (0 != memcmp( context, w->private_key, PRIVATE_KEY_LEN)) {
info->error_code = hss_error_key_mismatch;
return false;
}
}
if (w->signature_len > signature_buf_len) {
info->error_code = hss_error_buffer_overflow;
goto failed;
}
unsigned levels = w->levels;
sequence_t current_count = 0;
for (i=0; i < levels; i++) {
struct merkle_level *tree = w->tree[i];
current_count <<= tree->level;
current_count += (sequence_t)tree->current_index - 1;
}
current_count += 1;
if (!hss_advance_count(w, current_count,
update_private_key, context, info,
&trash_private_key)) {
goto failed;
}
struct thread_collection *col = hss_thread_init(info->num_threads);
enum hss_error_code got_error = hss_error_none;
{
struct gen_sig_detail gen_detail;
gen_detail.signature = signature;
gen_detail.signature_len = w->signature_len;
gen_detail.message = message;
gen_detail.message_len = message_len;
gen_detail.w = w;
gen_detail.got_error = &got_error;
hss_thread_issue_work(col, do_gen_sig, &gen_detail, sizeof gen_detail);
}
if (levels > 1) {
struct step_next_detail step_detail;
step_detail.w = w;
step_detail.tree = w->tree[levels-1];
step_detail.got_error = &got_error;
hss_thread_issue_work(col, do_step_next, &step_detail, sizeof step_detail);
}
int skipped_a_level = 0;
{
struct merkle_level *tree = w->tree[levels-1];
merkle_index_t updates_before_end = tree->max_index - tree->current_index + 1;
int h_subtree = tree->subtree_size;
for (i=1; i<tree->sublevels; i++) {
struct subtree *subtree = tree->subtree[i][BUILDING_TREE];
if (updates_before_end < (merkle_index_t)1 <<
(subtree->levels_below + h_subtree)) {
skipped_a_level = 1;
continue;
}
struct step_building_detail step_detail;
step_detail.tree = tree;
step_detail.subtree = subtree;
step_detail.got_error = &got_error;
hss_thread_issue_work(col, do_step_building, &step_detail, sizeof step_detail);
}
if (tree->sublevels == 1) skipped_a_level = 1;
}
if (skipped_a_level &&
levels > 1 && w->tree[levels-2]->update_count != UPDATE_DONE) {
struct update_parent_detail detail;
detail.w = w;
detail.got_error = &got_error;
hss_thread_issue_work(col, do_update_parent, &detail, sizeof detail);
}
hss_thread_done(col);
if (got_error != hss_error_none) {
info->error_code = got_error;
goto failed;
}
current_count += 1;
sequence_t cur_count = current_count;
unsigned merkle_levels_below = 0;
int switch_merkle = w->levels;
struct merkle_level *tree;
for (i = w->levels-1; i>=1; i--, merkle_levels_below += tree->level) {
tree = w->tree[i];
if (0 == (cur_count & (((sequence_t)1 << (merkle_levels_below + tree->level))-1))) {
if ((i-1) == 0) {
w->status = hss_error_private_key_expired;
break;
}
switch_merkle = i;
continue;
}
unsigned subtree_levels_below = 0;
unsigned j;
for (j = tree->sublevels-1; j>0; j--) {
subtree_levels_below += tree->subtree_size;
if (0 != (cur_count & (((sequence_t)1 << (merkle_levels_below + subtree_levels_below))-1))) {
goto done_advancing;
}
struct subtree *next = tree->subtree[j][BUILDING_TREE];
struct subtree *prev = tree->subtree[j][ACTIVE_TREE];
unsigned char *stack = next->stack;
tree->subtree[j][ACTIVE_TREE] = next;
prev->current_index = 0;
prev->left_leaf += (merkle_index_t)2 << subtree_levels_below;
tree->subtree[j][BUILDING_TREE] = prev;
next->stack = NULL;
prev->stack = stack;
}
}
done_advancing:
for (i = switch_merkle; i < w->levels; i++) {
struct merkle_level *tree_l = w->tree[i];
struct merkle_level *parent = w->tree[i-1];
unsigned j;
for (j=0; j<tree->sublevels; j++) {
struct subtree *active = tree_l->subtree[j][NEXT_TREE];
struct subtree *next = tree_l->subtree[j][ACTIVE_TREE];
unsigned char *stack = active->stack;
active->left_leaf = 0;
next->current_index = 0;
next->left_leaf = 0;
tree_l->subtree[j][ACTIVE_TREE] = active;
tree_l->subtree[j][NEXT_TREE] = next;
active->stack = NULL;
next->stack = stack;
if (j > 0) {
struct subtree *building = tree->subtree[j][BUILDING_TREE];
building->current_index = 0;
merkle_index_t size_subtree = (merkle_index_t)1 <<
(tree->subtree_size + building->levels_below);
building->left_leaf = size_subtree;
}
}
memcpy( tree_l->seed, tree->seed_next, SEED_LEN );
memcpy( tree_l->I, tree->I_next, I_LEN );
merkle_index_t index = parent->current_index;
if (index == parent->max_index) {
hss_generate_child_seed_I_value(tree->seed_next, tree->I_next,
parent->seed_next, parent->I_next, 0,
parent->lm_type,
parent->lm_ots_type);
} else {
hss_generate_child_seed_I_value( tree->seed_next, tree->I_next,
parent->seed, parent->I, index+1,
parent->lm_type,
parent->lm_ots_type);
}
tree_l->current_index = 0;
if (!hss_create_signed_public_key( w->signed_pk[i], w->siglen[i-1],
tree_l, parent, w )) {
info->error_code = hss_error_internal;
goto failed;
}
}
if (trash_private_key) {
memset( w->private_key, PARM_SET_END, PRIVATE_KEY_LEN );
}
return true;
failed:
if (trash_private_key) {
memset( w->private_key, PARM_SET_END, PRIVATE_KEY_LEN );
}
memset( signature, 0, signature_buf_len );
return false;
}
size_t hss_get_signature_len_from_working_key(struct hss_working_key *w) {
if (!w || w->status != hss_error_none) return 0;
int levels = w->levels;
if (levels > MAX_HSS_LEVELS) return 0;
param_set_t lm[MAX_HSS_LEVELS], ots[MAX_HSS_LEVELS];
int i;
for (i=0; i<levels; i++) {
lm[i] = w->tree[i]->lm_type;
ots[i] = w->tree[i]->lm_ots_type;
}
return hss_get_signature_len(levels, lm, ots);
}