#include <string.h>
#include "hss_derive.h"
#include "hss_internal.h"
#include "hash.h"
#include "endian.h"
#include "config.h"
#if SECRET_METHOD == 2
#include "lm_common.h"
#else
#if SEED_LEN == 32
#define HASH HASH_SHA256
#else
#error We need to define a hash function for this seed length
#endif
#endif
#if SECRET_METHOD == 0 || SECRET_METHOD == 2
bool hss_seed_derive_init( struct seed_derive *derive,
param_set_t lm, param_set_t ots,
const unsigned char *I, const unsigned char *seed ) {
derive->I = I;
derive->master_seed = seed;
LMS_UNUSED(ots);
#if SECRET_METHOD == 2
if (!lm_look_up_parameter_set(lm, &derive->hash, &derive->m, 0)) {
return false;
}
if (derive->m != SEED_LEN) {
return false;
}
#endif
return true;
}
void hss_seed_derive_set_q( struct seed_derive *derive, merkle_index_t q ) {
derive->q = q;
}
void hss_seed_derive_set_j( struct seed_derive *derive, unsigned j ) {
derive->j = j;
}
void hss_seed_derive( unsigned char *seed, struct seed_derive *derive,
bool increment_j ) {
unsigned char buffer[ PRG_MAX_LEN ];
memcpy( buffer + PRG_I, derive->I, I_LEN );
put_bigendian( buffer + PRG_Q, derive->q, 4 );
put_bigendian( buffer + PRG_J, derive->j, 2 );
buffer[PRG_FF] = 0xff;
memcpy( buffer + PRG_SEED, derive->master_seed, SEED_LEN );
#if SECRET_METHOD == 2
int hash = derive->hash;
#else
int hash = HASH;
#endif
hss_hash( seed, hash, buffer, PRG_LEN(SEED_LEN) );
hss_zeroize( buffer, PRG_LEN(SEED_LEN) );
if (increment_j) derive->j += 1;
}
void hss_seed_derive_done( struct seed_derive *derive ) {
LMS_UNUSED(derive);
}
#elif SECRET_METHOD == 1
#include "lm_common.h"
#include "lm_ots_common.h"
static unsigned my_log2(merkle_index_t n);
bool hss_seed_derive_init( struct seed_derive *derive,
param_set_t lm, param_set_t ots,
const unsigned char *I, const unsigned char *seed ) {
derive->I = I;
derive->master_seed = seed;
unsigned height, p;
if (!lm_look_up_parameter_set(lm, 0, 0, &height) ||
!lm_ots_look_up_parameter_set(ots, 0, 0, 0, &p, 0)) {
return false;
}
p += NUM_ARTIFICIAL_SEEDS;
derive->q_levels = (height + SECRET_MAX - 1)/SECRET_MAX;
derive->r_mask = (merkle_index_t)1 << height;
unsigned j_height = my_log2(p);
derive->j_levels = (j_height + SECRET_MAX - 1)/SECRET_MAX;
derive->j_mask = 1 << j_height;
derive->q = derive->r_mask;
return true;
}
void hss_seed_derive_set_q( struct seed_derive *derive, merkle_index_t q ) {
merkle_index_t change = q ^ derive->q;
derive->q = q;
unsigned bits_change = my_log2(change);
unsigned q_levels = derive->q_levels;
unsigned levels_change = (bits_change + SECRET_MAX - 1) / SECRET_MAX;
if (levels_change > q_levels) levels_change = q_levels;
int i;
union hash_context ctx;
unsigned char buffer[ QTREE_MAX_LEN ];
merkle_index_t r = q | derive->r_mask;
for (i = levels_change; i > 0; i--) {
int j = q_levels - i;
int shift = (i-1) * SECRET_MAX;
memcpy( buffer + QTREE_I, derive->I, I_LEN );
put_bigendian( buffer + QTREE_Q, r >> shift, 4 );
SET_D( buffer + QTREE_D, D_QTREE );
if (j == 0) {
memcpy( buffer + QTREE_SEED, derive->master_seed, SEED_LEN );
} else {
memcpy( buffer + QTREE_SEED, derive->q_seed[j-1], SEED_LEN );
}
hss_hash_ctx( derive->q_seed[j], HASH, &ctx, buffer, QTREE_LEN );
}
hss_zeroize( buffer, PRG_LEN(SEED_LEN) );
hss_zeroize( &ctx, sizeof ctx );
}
static void set_j_seed( struct seed_derive *derive, int i,
union hash_context *ctx, unsigned char *buffer) {
memcpy( buffer + PRG_I, derive->I, I_LEN );
put_bigendian( buffer + PRG_Q, derive->q, 4 );
put_bigendian( buffer + PRG_J, derive->j_value[i], 2 );
buffer[PRG_FF] = 0xff;
if (i == 0) {
memcpy( buffer + PRG_SEED, derive->q_seed[ derive->q_levels-1],
SEED_LEN );
} else {
memcpy( buffer + PRG_SEED, derive->j_seed[i-1], SEED_LEN );
}
hss_hash_ctx( derive->j_seed[i], HASH, ctx, buffer, PRG_LEN(SEED_LEN) );
}
void hss_seed_derive_set_j( struct seed_derive *derive, unsigned j ) {
int i;
unsigned j_levels = derive->j_levels;
unsigned shift = SECRET_MAX * j_levels;
unsigned j_mask = derive->j_mask;
j &= j_mask-1;
j |= j_mask;
union hash_context ctx;
unsigned char buffer[ PRG_MAX_LEN ];
for (i = 0; i<j_levels; i++ ) {
shift -= SECRET_MAX;
derive->j_value[i] = (j >> shift);
set_j_seed( derive, i, &ctx, buffer );
}
hss_zeroize( &ctx, sizeof ctx );
hss_zeroize( buffer, PRG_LEN(SEED_LEN) );
}
void hss_seed_derive( unsigned char *seed, struct seed_derive *derive,
bool increment_j ) {
memcpy( seed, derive->j_seed[ derive->j_levels - 1], SEED_LEN );
if (increment_j) {
int i;
for (i = derive->j_levels-1;; i--) {
unsigned index = derive->j_value[i];
index += 1;
derive->j_value[i] = index;
if (0 != (index & SECRET_MAX_MASK)) {
break;
}
if (i == 0) {
break;
}
}
union hash_context ctx;
unsigned char buffer[ PRG_MAX_LEN ];
for (; i < derive->j_levels; i++) {
set_j_seed( derive, i, &ctx, buffer );
}
hss_zeroize( &ctx, sizeof ctx );
hss_zeroize( buffer, PRG_LEN(SEED_LEN) );
}
}
void hss_seed_derive_done( struct seed_derive *derive ) {
hss_zeroize( derive->q_seed, sizeof derive->q_seed );
hss_zeroize( derive->j_seed, sizeof derive->j_seed );
}
static unsigned my_log2(merkle_index_t n) {
unsigned lg;
for (lg = 0; n > 0; lg++) n >>= 1;
return lg;
}
#else
#error Unknown secret method
#endif