#include <string.h>
#include "common_defs.h"
#include "hss_verify_inc.h"
#include "lm_verify.h"
#include "lm_common.h"
#include "lm_ots_verify.h"
#include "hash.h"
#include "endian.h"
#include "hss_thread.h"
#include "hss_internal.h"
#include "lm_ots_common.h"
#include "hss.h"
bool hss_validate_signature_init(
struct hss_validate_inc *ctx,
const unsigned char *public_key,
const unsigned char *signature, size_t signature_len,
struct hss_extra_info *info) {
struct hss_extra_info temp_info = { 0 };
if (!info) info = &temp_info;
unsigned i;
if (!ctx) {
info->error_code = hss_error_got_null;
return false;
}
ctx->status = hss_error_ctx_uninitialized;
const unsigned char *orig_signature = signature;
;
if (signature_len < 4) {
ctx->status = info->error_code = hss_error_bad_signature;
return false;
}
uint_fast32_t levels = (uint_fast32_t)get_bigendian( signature, 4 ) + 1;
signature += 4; signature_len -= 4;
if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS ||
levels != (uint_fast32_t)get_bigendian( public_key, 4 )) {
ctx->status = info->error_code = hss_error_bad_signature;
return false;
}
uint_fast32_t pub_levels = (uint_fast32_t)get_bigendian( public_key, 4 );
if (levels != pub_levels) {
ctx->status = info->error_code = hss_error_bad_signature;
return false;
}
public_key += 4;
struct thread_collection *col = NULL;
if (levels > 1) {
col = hss_thread_init(info->num_threads);
enum hss_error_code got_error = hss_error_none;
struct verify_detail detail;
detail.got_error = &got_error;
for (i=0; i<levels-1; i++) {
param_set_t lm_type = (param_set_t)get_bigendian( public_key, 4 );
param_set_t lm_ots_type = (param_set_t)get_bigendian( public_key+4, 4 );
unsigned l_siglen = (unsigned)lm_get_signature_len(lm_type, lm_ots_type);
if (l_siglen == 0 || l_siglen > signature_len) goto failed;
const unsigned char *l_sig = signature;
signature += l_siglen; signature_len -= l_siglen;
if (signature_len < 4) goto failed;
lm_type = (param_set_t)get_bigendian( signature, 4 );
unsigned l_pubkeylen = (unsigned)lm_get_public_key_len(lm_type);
if (l_pubkeylen == 0 || l_pubkeylen > signature_len) goto failed;
const unsigned char *l_pubkey = signature;
signature += l_pubkeylen; signature_len -= l_pubkeylen;
detail.public_key = public_key;
detail.message = l_pubkey;
detail.message_len = l_pubkeylen;
detail.signature = l_sig;
detail.signature_len = l_siglen;
hss_thread_issue_work( col, validate_internal_sig,
&detail, sizeof detail );
public_key = l_pubkey;
}
hss_thread_done(col);
col = NULL;
if (got_error != hss_error_none) {
ctx->status = info->error_code = got_error;
return false;
}
}
ctx->signature_offset = signature - orig_signature;
ctx->signature_len = signature_len;
memcpy( ctx->final_public_key, public_key, 8 + I_LEN + MAX_HASH );
param_set_t ots_type = (param_set_t)get_bigendian( public_key+4, 4 );
unsigned h, n;
if (!lm_ots_look_up_parameter_set(ots_type, &h, &n, NULL, NULL, NULL)) {
ctx->status = info->error_code = hss_error_bad_signature;
return false;
}
ctx->h = h;
hss_init_hash_context( h, &ctx->hash_ctx );
{
unsigned char prefix[ MESG_PREFIX_MAXLEN ];
memcpy( prefix + MESG_I, ctx->final_public_key+8, I_LEN );
memcpy( prefix + MESG_Q, signature, 4 );
SET_D( prefix + MESG_D, D_MESG );
memcpy( prefix + MESG_C, signature+8, n );
hss_update_hash_context(h, &ctx->hash_ctx, prefix, MESG_PREFIX_LEN(n) );
}
ctx->status = hss_error_none;
return true;
failed:
if (col) hss_thread_done(col);
ctx->status = info->error_code = hss_error_bad_signature;
return false;
}
bool hss_validate_signature_update(
struct hss_validate_inc *ctx,
const void *message_segment,
size_t len_message_segment) {
if (!ctx || ctx->status != hss_error_none) return false;
hss_update_hash_context(ctx->h, &ctx->hash_ctx,
message_segment, len_message_segment );
return true;
}
bool hss_validate_signature_finalize(
struct hss_validate_inc *ctx,
const unsigned char *signature,
struct hss_extra_info *info) {
struct hss_extra_info temp_info = { 0 };
if (!info) info = &temp_info;
if (!ctx) {
info->error_code = hss_error_got_null;
return false;
}
if (ctx->status != hss_error_none) {
info->error_code = ctx->status;
return false;
}
ctx->status = hss_error_ctx_already_used;
unsigned char hash[ MAX_HASH ];
unsigned h = ctx->h;
hss_finalize_hash_context( h, &ctx->hash_ctx, hash );
if (lm_validate_signature(
ctx->final_public_key,
hash, sizeof hash, true,
signature + ctx->signature_offset, ctx->signature_len)) {
return true;
}
info->error_code = hss_error_bad_signature;
return false;
}