#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#include <wolfssl/wolfcrypt/wc_lms.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS)
#define LMS_R_LEN 4
#define LMS_D_LEN 2
#define LMS_CKSM_LEN 2
#define LMS_D_FIXED 0xff
#define LMS_D_PBLC 0x8080
#define LMS_D_MESG 0x8181
#define LMS_D_LEAF 0x8282
#define LMS_D_INTR 0x8383
#define LMS_D_C 0xfffd
#define LMS_D_CHILD_SEED 0xfffe
#define LMS_D_CHILD_I 0xffff
#define LMS_SEED_HASH_LEN(hLen) \
(LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + (hLen))
#define LMS_NODE_HASH_LEN(hLen) \
(LMS_I_LEN + LMS_R_LEN + LMS_D_LEN + 2 * (hLen))
#define LMS_HASH_BUFFER_LEN(hLen) \
(LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN + LMS_W_LEN + (hLen))
#define LMS_K_PRE_LEN (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN)
#define LMS_MSG_PRE_LEN (LMS_I_LEN + LMS_Q_LEN + LMS_P_LEN)
#ifdef WC_LMS_DEBUG_PRINT_DATA
static void print_data(const char* name, const byte* data, int len)
{
int i;
fprintf(stderr, "%6s: ", name);
for (i = 0; i < len; i++) {
fprintf(stderr, "%02x", data[i]);
}
fprintf(stderr, "\n");
}
#endif
#ifndef WOLFSSL_LMS_VERIFY_ONLY
static WC_INLINE void wc_lms_idx_zero(unsigned char* a, int len)
{
XMEMSET(a, 0, len);
}
static WC_INLINE void wc_lms_idx_inc(unsigned char* a, int len)
{
int i;
for (i = len - 1; i >= 0; i--) {
if ((++a[i]) != 0) {
break;
}
}
}
#endif
#define LMS_SHA256_SET_DATA(sha256, data, len) \
do { \
XMEMCPY((sha256)->buffer, (data), (len)); \
(sha256)->buffLen = (len); \
(sha256)->loLen = (len); \
} while (0)
#define LMS_SHA256_ADD_DATA(sha256, data, len) \
do { \
XMEMCPY((byte*)(sha256)->buffer + (sha256)->buffLen, (data), (len)); \
(sha256)->buffLen += (len); \
(sha256)->loLen += (len); \
} while (0)
#define LMS_SHA256_SET_LEN_54(buffer) \
do { \
(buffer)[54] = 0x80; \
(buffer)[55] = 0x00; \
(buffer)[56] = 0x00; \
(buffer)[57] = 0x00; \
(buffer)[58] = 0x00; \
(buffer)[59] = 0x00; \
(buffer)[60] = 0x00; \
(buffer)[61] = 0x00; \
(buffer)[62] = 0x01; \
(buffer)[63] = 0xb0; \
} while (0)
#define LMS_SHA256_SET_LEN_55(buffer) \
do { \
(buffer)[55] = 0x80; \
(buffer)[56] = 0x00; \
(buffer)[57] = 0x00; \
(buffer)[58] = 0x00; \
(buffer)[59] = 0x00; \
(buffer)[60] = 0x00; \
(buffer)[61] = 0x00; \
(buffer)[62] = 0x01; \
(buffer)[63] = 0xb8; \
} while (0)
#ifndef WOLFSSL_NO_LMS_SHA256_256
#ifndef WC_LMS_FULL_HASH
static WC_INLINE int wc_lms_hash_block(wc_Sha256* sha256, const byte* data,
byte* hash)
{
return wc_Sha256HashBlock(sha256, data, hash);
}
#endif
static WC_INLINE int wc_lms_hash(wc_Sha256* sha256, byte* data, word32 len,
byte* hash)
{
int ret;
#ifndef WC_LMS_FULL_HASH
if (len < WC_SHA256_BLOCK_SIZE) {
LMS_SHA256_SET_DATA(sha256, data, len);
ret = wc_Sha256Final(sha256, hash);
}
else if (len < WC_SHA256_BLOCK_SIZE + WC_SHA256_PAD_SIZE) {
ret = wc_Sha256HashBlock(sha256, data, NULL);
if (ret == 0) {
byte* buffer = (byte*)sha256->buffer;
int rem = len - WC_SHA256_BLOCK_SIZE;
XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE, rem);
buffer[rem++] = 0x80;
XMEMSET(buffer + rem, 0, WC_SHA256_BLOCK_SIZE - 2 - rem);
buffer[WC_SHA256_BLOCK_SIZE - 2] = (byte)(len >> 5);
buffer[WC_SHA256_BLOCK_SIZE - 1] = (byte)(len << 3);
ret = wc_Sha256HashBlock(sha256, buffer, hash);
}
}
else {
ret = wc_Sha256Update(sha256, data, len);
if (ret == 0) {
ret = wc_Sha256Final(sha256, hash);
}
}
#else
ret = wc_Sha256Update(sha256, data, len);
if (ret == 0) {
ret = wc_Sha256Final(sha256, hash);
}
#endif
return ret;
}
#endif
static WC_INLINE int wc_lms_hash_first(wc_Sha256* sha256, const byte* data,
word32 len)
{
int ret = 0;
#ifndef WC_LMS_FULL_HASH
if (len < WC_SHA256_BLOCK_SIZE) {
LMS_SHA256_SET_DATA(sha256, data, len);
}
else
#endif
{
ret = wc_Sha256Update(sha256, data, len);
}
return ret;
}
static WC_INLINE int wc_lms_hash_update(wc_Sha256* sha256, const byte* data,
word32 len)
{
int ret = 0;
#ifndef WC_LMS_FULL_HASH
if (sha256->buffLen + len < WC_SHA256_BLOCK_SIZE) {
LMS_SHA256_ADD_DATA(sha256, data, len);
}
else if (sha256->buffLen + len < 2 * WC_SHA256_BLOCK_SIZE) {
byte* buffer = (byte*)sha256->buffer;
XMEMCPY(buffer + sha256->buffLen, data,
WC_SHA256_BLOCK_SIZE - sha256->buffLen);
ret = wc_Sha256HashBlock(sha256, buffer, NULL);
if (ret == 0) {
int rem = len - (WC_SHA256_BLOCK_SIZE - sha256->buffLen);
XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE - sha256->buffLen, rem);
sha256->buffLen = rem;
sha256->loLen += len;
}
}
else {
ret = wc_Sha256Update(sha256, data, len);
}
#else
ret = wc_Sha256Update(sha256, data, len);
#endif
return ret;
}
#ifndef WOLFSSL_NO_LMS_SHA256_256
static WC_INLINE int wc_lms_hash_final(wc_Sha256* sha256, byte* hash)
{
#ifndef WC_LMS_FULL_HASH
int ret = 0;
byte* buffer = (byte*)sha256->buffer;
buffer[sha256->buffLen++] = 0x80;
if (sha256->buffLen > WC_SHA256_PAD_SIZE) {
XMEMSET(buffer + sha256->buffLen, 0,
WC_SHA256_BLOCK_SIZE - sha256->buffLen);
ret = wc_Sha256HashBlock(sha256, buffer, NULL);
sha256->buffLen = 0;
}
if (ret == 0) {
XMEMSET(buffer + sha256->buffLen, 0,
WC_SHA256_BLOCK_SIZE - 8 - sha256->buffLen);
sha256->hiLen = (sha256->hiLen << 3) + (sha256->loLen >> 29);
sha256->loLen = sha256->loLen << 3;
#ifdef LITTLE_ENDIAN_ORDER
sha256->buffer[14] = ByteReverseWord32(sha256->hiLen);
sha256->buffer[15] = ByteReverseWord32(sha256->loLen);
#else
sha256->buffer[14] = sha256->hiLen;
sha256->buffer[15] = sha256->loLen;
#endif
ret = wc_Sha256HashBlock(sha256, buffer, hash);
sha256->buffLen = 0;
sha256->hiLen = 0;
sha256->loLen = 0;
}
return ret;
#else
return wc_Sha256Final(sha256, hash);
#endif
}
#endif
#ifdef WOLFSSL_LMS_SHA256_192
#define LMS_SHA256_SET_LEN_46(buffer) \
do { \
(buffer)[46] = 0x80; \
(buffer)[47] = 0x00; \
(buffer)[48] = 0x00; \
(buffer)[49] = 0x00; \
(buffer)[50] = 0x00; \
(buffer)[51] = 0x00; \
(buffer)[52] = 0x00; \
(buffer)[53] = 0x00; \
(buffer)[54] = 0x00; \
(buffer)[55] = 0x00; \
(buffer)[56] = 0x00; \
(buffer)[57] = 0x00; \
(buffer)[58] = 0x00; \
(buffer)[59] = 0x00; \
(buffer)[60] = 0x00; \
(buffer)[61] = 0x00; \
(buffer)[62] = 0x01; \
(buffer)[63] = 0x70; \
} while (0)
#define LMS_SHA256_SET_LEN_47(buffer) \
do { \
(buffer)[47] = 0x80; \
(buffer)[48] = 0x00; \
(buffer)[49] = 0x00; \
(buffer)[50] = 0x00; \
(buffer)[51] = 0x00; \
(buffer)[52] = 0x00; \
(buffer)[53] = 0x00; \
(buffer)[54] = 0x00; \
(buffer)[55] = 0x00; \
(buffer)[56] = 0x00; \
(buffer)[57] = 0x00; \
(buffer)[58] = 0x00; \
(buffer)[59] = 0x00; \
(buffer)[60] = 0x00; \
(buffer)[61] = 0x00; \
(buffer)[62] = 0x01; \
(buffer)[63] = 0x78; \
} while (0)
#ifndef WC_LMS_FULL_HASH
static WC_INLINE int wc_lms_sha256_192_hash_block(wc_Sha256* sha256,
const byte* data, byte* hash)
{
int ret;
unsigned char output[WC_SHA256_DIGEST_SIZE];
ret = wc_Sha256HashBlock(sha256, data, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
return ret;
}
#endif
static WC_INLINE int wc_lms_hash_sha256_192(wc_Sha256* sha256, byte* data,
word32 len, byte* hash)
{
int ret;
unsigned char output[WC_SHA256_DIGEST_SIZE];
#ifndef WC_LMS_FULL_HASH
if (len < WC_SHA256_BLOCK_SIZE) {
LMS_SHA256_SET_DATA(sha256, data, len);
ret = wc_Sha256Final(sha256, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
}
else if (len < WC_SHA256_BLOCK_SIZE + WC_SHA256_PAD_SIZE) {
ret = wc_Sha256HashBlock(sha256, data, NULL);
if (ret == 0) {
byte* buffer = (byte*)sha256->buffer;
int rem = len - WC_SHA256_BLOCK_SIZE;
XMEMCPY(buffer, data + WC_SHA256_BLOCK_SIZE, rem);
buffer[rem++] = 0x80;
XMEMSET(buffer + rem, 0, WC_SHA256_BLOCK_SIZE - 2 - rem);
buffer[WC_SHA256_BLOCK_SIZE - 2] = (byte)(len >> 5);
buffer[WC_SHA256_BLOCK_SIZE - 1] = (byte)(len << 3);
ret = wc_Sha256HashBlock(sha256, buffer, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
}
}
else {
ret = wc_Sha256Update(sha256, data, len);
if (ret == 0) {
ret = wc_Sha256Final(sha256, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
}
}
#else
ret = wc_Sha256Update(sha256, data, len);
if (ret == 0) {
ret = wc_Sha256Final(sha256, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
}
#endif
return ret;
}
static WC_INLINE int wc_lms_hash_sha256_192_final(wc_Sha256* sha256, byte* hash)
{
#ifndef WC_LMS_FULL_HASH
int ret = 0;
byte* buffer = (byte*)sha256->buffer;
unsigned char output[WC_SHA256_DIGEST_SIZE];
buffer[sha256->buffLen++] = 0x80;
if (sha256->buffLen > WC_SHA256_PAD_SIZE) {
XMEMSET(buffer + sha256->buffLen, 0,
WC_SHA256_BLOCK_SIZE - sha256->buffLen);
ret = wc_Sha256HashBlock(sha256, buffer, NULL);
sha256->buffLen = 0;
}
if (ret == 0) {
XMEMSET(buffer + sha256->buffLen, 0,
WC_SHA256_BLOCK_SIZE - 8 - sha256->buffLen);
sha256->hiLen = (sha256->hiLen << 3) + (sha256->loLen >> 29);
sha256->loLen = sha256->loLen << 3;
#ifdef LITTLE_ENDIAN_ORDER
sha256->buffer[14] = ByteReverseWord32(sha256->hiLen);
sha256->buffer[15] = ByteReverseWord32(sha256->loLen);
#else
sha256->buffer[14] = sha256->hiLen;
sha256->buffer[15] = sha256->loLen;
#endif
ret = wc_Sha256HashBlock(sha256, buffer, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
sha256->buffLen = 0;
sha256->hiLen = 0;
sha256->loLen = 0;
}
return ret;
#else
int ret;
unsigned char output[WC_SHA256_DIGEST_SIZE];
ret = wc_Sha256Final(sha256, output);
if (ret == 0) {
XMEMCPY(hash, output, WC_SHA256_192_DIGEST_SIZE);
}
return ret;
#endif
}
#endif
static WC_INLINE int wc_lmots_q_expand(byte* q, word8 n, word8 w, word8 ls,
byte* qe)
{
int ret = 0;
word16 sum;
unsigned int i;
#ifndef WOLFSSL_WC_LMS_SMALL
switch (w) {
case 8:
XMEMCPY(qe, q, n);
sum = 0xff * n;
for (i = 0; i < n; i++) {
sum -= q[i];
}
qe[n + 0] = (word8)(sum >> 8);
qe[n + 1] = (word8)(sum );
break;
case 4:
sum = 2 * 0xf * n;
for (i = 0; i < n; i++) {
qe[0] = (q[i] >> 4) ;
qe[1] = (q[i] ) & 0xf;
sum -= qe[0];
sum -= qe[1];
qe += 2;
}
qe[0] = (word8)((sum >> 8) & 0xf);
qe[1] = (word8)((sum >> 4) & 0xf);
qe[2] = (word8)((sum ) & 0xf);
break;
case 2:
sum = 4 * 0x3 * n;
for (i = 0; i < n; i++) {
qe[0] = (q[i] >> 6) ;
qe[1] = (q[i] >> 4) & 0x3;
qe[2] = (q[i] >> 2) & 0x3;
qe[3] = (q[i] ) & 0x3;
sum -= qe[0];
sum -= qe[1];
sum -= qe[2];
sum -= qe[3];
qe += 4;
}
qe[0] = (word8)((sum >> 8) & 0x3);
qe[1] = (word8)((sum >> 6) & 0x3);
qe[2] = (word8)((sum >> 4) & 0x3);
qe[3] = (word8)((sum >> 2) & 0x3);
qe[4] = (word8)((sum ) & 0x3);
break;
case 1:
sum = 8 * 0x01 * n;
for (i = 0; i < n; i++) {
qe[0] = (q[i] >> 7) ;
qe[1] = (q[i] >> 6) & 0x1;
qe[2] = (q[i] >> 5) & 0x1;
qe[3] = (q[i] >> 4) & 0x1;
qe[4] = (q[i] >> 3) & 0x1;
qe[5] = (q[i] >> 2) & 0x1;
qe[6] = (q[i] >> 1) & 0x1;
qe[7] = (q[i] ) & 0x1;
sum -= qe[0];
sum -= qe[1];
sum -= qe[2];
sum -= qe[3];
sum -= qe[4];
sum -= qe[5];
sum -= qe[6];
sum -= qe[7];
qe += 8;
}
#ifdef WOLFSSL_LMS_SHA256_192
if (ls == 7)
#endif
{
qe[0] = (word8)((sum >> 8) );
qe++;
}
qe[0] = (word8)((sum >> 7) & 0x1);
qe[1] = (word8)((sum >> 6) & 0x1);
qe[2] = (word8)((sum >> 5) & 0x1);
qe[3] = (word8)((sum >> 4) & 0x1);
qe[4] = (word8)((sum >> 3) & 0x1);
qe[5] = (word8)((sum >> 2) & 0x1);
qe[6] = (word8)((sum >> 1) & 0x1);
qe[7] = (word8)((sum ) & 0x1);
break;
default:
ret = BAD_FUNC_ARG;
break;
}
(void)ls;
#else
int j;
if ((w != 8) && (w != 4) && (w != 2) && (w != 1)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
sum = (((word16)1 << w) - 1) * ((n * 8) / w);
for (i = 0; i < n; i++) {
byte a = *(q++);
for (j = 8 - w; j >= 0; j -= w) {
*qe = a >> (8 - w);
sum -= *qe;
qe++;
a <<= w;
}
}
sum <<= ls;
for (j = 16 - w; j >= ls; j--) {
*(qe++) = sum >> (16 - w);
sum <<= w;
}
}
#endif
return ret;
}
static int wc_lmots_msg_hash(LmsState* state, const byte* msg, word32 msgSz,
const byte* c, byte* q)
{
int ret;
byte* buffer = state->buffer;
byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
c16toa(LMS_D_MESG, ip);
ret = wc_lms_hash_first(&state->hash, buffer, LMS_MSG_PRE_LEN);
if (ret == 0) {
ret = wc_lms_hash_update(&state->hash, c, state->params->hash_len);
}
if (ret == 0) {
ret = wc_lms_hash_update(&state->hash, msg, msgSz);
}
#ifdef WOLFSSL_LMS_SHA256_192
if ((ret == 0) &&
((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192)) {
ret = wc_lms_hash_sha256_192_final(&state->hash, q);
}
else
#endif
#ifndef WOLFSSL_NO_LMS_SHA256_256
if (ret == 0) {
ret = wc_lms_hash_final(&state->hash, q);
}
else
#endif
{
ret = NOT_COMPILED_IN;
}
return ret;
}
#ifndef WOLFSSL_LMS_VERIFY_ONLY
static int wc_lmots_compute_y_from_seed(LmsState* state, const byte* seed,
const byte* msg, word32 msgSz, const byte* c, byte* y)
{
const LmsParams* params = state->params;
int ret;
word16 i;
byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN];
#ifdef WOLFSSL_SMALL_STACK
byte* a = state->a;
#else
byte a[LMS_MAX_P];
#endif
byte* buffer = state->buffer;
byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
byte* jp = ip + LMS_P_LEN;
byte* tmp = jp + LMS_W_LEN;
ret = wc_lmots_msg_hash(state, msg, msgSz, c, q);
if (ret == 0) {
ret = wc_lmots_q_expand(q, (word8)params->hash_len, params->width,
params->ls, a);
}
#ifndef WC_LMS_FULL_HASH
if (ret == 0) {
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
LMS_SHA256_SET_LEN_47(buffer);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
LMS_SHA256_SET_LEN_55(buffer);
#endif
}
}
#endif
for (i = 0; (ret == 0) && (i < params->p); i++) {
unsigned int j;
c16toa(i, ip);
*jp = LMS_D_FIXED;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
for (j = 0; (ret == 0) && (j < a[i]); j++) {
*jp = j;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
}
if (ret == 0) {
XMEMCPY(y, tmp, params->hash_len);
y += params->hash_len;
}
}
return ret;
}
#endif
static int wc_lmots_compute_kc_from_sig(LmsState* state, const byte* msg,
word32 msgSz, const byte* c, const byte* sig_y, byte* kc)
{
const LmsParams* params = state->params;
int ret;
word16 i;
byte q[LMS_MAX_NODE_LEN + LMS_CKSM_LEN];
#ifdef WOLFSSL_SMALL_STACK
byte* a = state->a;
#else
byte a[LMS_MAX_P];
#endif
byte* buffer = state->buffer;
byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
byte* jp = ip + LMS_P_LEN;
byte* tmp = jp + LMS_W_LEN;
unsigned int max = ((unsigned int)1 << params->width) - 1;
c16toa(LMS_D_PBLC, ip);
ret = wc_lms_hash_first(&state->hash_k, buffer, LMS_K_PRE_LEN);
if (ret == 0) {
ret = wc_lmots_msg_hash(state, msg, msgSz, c, q);
}
if (ret == 0) {
ret = wc_lmots_q_expand(q, (word8)params->hash_len, params->width,
params->ls, a);
}
#ifndef WC_LMS_FULL_HASH
if (ret == 0) {
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
LMS_SHA256_SET_LEN_47(buffer);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
LMS_SHA256_SET_LEN_55(buffer);
#endif
}
}
#endif
for (i = 0; (ret == 0) && (i < params->p); i++) {
unsigned int j;
c16toa(i, ip);
XMEMCPY(tmp, sig_y, params->hash_len);
sig_y += params->hash_len;
for (j = a[i]; (ret == 0) && (j < max); j++) {
*jp = (word8)j;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
}
if (ret == 0) {
ret = wc_lms_hash_update(&state->hash_k, tmp, params->hash_len);
}
}
#ifdef WOLFSSL_LMS_SHA256_192
if ((ret == 0) &&
((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192)) {
ret = wc_lms_hash_sha256_192_final(&state->hash_k, kc);
}
else
#endif
if (ret == 0) {
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_final(&state->hash_k, kc);
#else
ret = NOT_COMPILED_IN;
#endif
}
return ret;
}
#ifndef WOLFSSL_LMS_VERIFY_ONLY
static int wc_lmots_make_public_hash(LmsState* state, const byte* seed, byte* k)
{
const LmsParams* params = state->params;
int ret;
word16 i;
byte* buffer = state->buffer;
byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
byte* jp = ip + LMS_P_LEN;
byte* tmp = jp + LMS_W_LEN;
unsigned int max = ((unsigned int)1 << params->width) - 1;
c16toa(LMS_D_PBLC, ip);
ret = wc_lms_hash_first(&state->hash_k, buffer, LMS_K_PRE_LEN);
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
LMS_SHA256_SET_LEN_47(buffer);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
LMS_SHA256_SET_LEN_55(buffer);
#endif
}
#endif
for (i = 0; (ret == 0) && (i < params->p); i++) {
unsigned int j;
c16toa(i, ip);
*jp = LMS_D_FIXED;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
for (j = 0; (ret == 0) && (j < max); j++) {
*jp = (word8)j;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
}
if (ret == 0) {
ret = wc_lms_hash_update(&state->hash_k, tmp, params->hash_len);
}
}
#ifdef WOLFSSL_LMS_SHA256_192
if ((ret == 0) && ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192)) {
ret = wc_lms_hash_sha256_192_final(&state->hash_k, k);
}
else
#endif
if (ret == 0) {
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_final(&state->hash_k, k);
#else
ret = NOT_COMPILED_IN;
#endif
}
return ret;
}
static void wc_lmots_public_key_encode(const LmsParams* params,
const byte* priv, byte* pub)
{
const byte* priv_i = priv + LMS_Q_LEN + params->hash_len;
c32toa(params->lmsType & LMS_H_W_MASK, pub);
pub += 4;
c32toa(params->lmOtsType & LMS_H_W_MASK, pub);
pub += 4;
XMEMCPY(pub, priv_i, LMS_I_LEN);
}
#endif
static int wc_lmots_public_key_check(const LmsParams* params, const byte* pub)
{
int ret = 0;
word32 type;
ato32(pub, &type);
pub += 4;
if (type != (params->lmsType & LMS_H_W_MASK)) {
ret = PUBLIC_KEY_E;
}
if (ret == 0) {
ato32(pub, &type);
if (type != (params->lmOtsType & LMS_H_W_MASK)) {
ret = PUBLIC_KEY_E;
}
}
return ret;
}
static int wc_lmots_calc_kc(LmsState* state, const byte* pub, const byte* msg,
word32 msgSz, const byte* sig, byte* kc)
{
int ret = 0;
if (XMEMCMP(pub, sig, LMS_TYPE_LEN) != 0) {
ret = SIG_TYPE_E;
}
if (ret == 0) {
const byte* c = sig + LMS_TYPE_LEN;
const byte* y = c + state->params->hash_len;
ret = wc_lmots_compute_kc_from_sig(state, msg, msgSz, c, y, kc);
}
return ret;
}
#ifndef WOLFSSL_LMS_VERIFY_ONLY
static int wc_lmots_make_private_key(WC_RNG* rng, word16 seed_len, byte* priv)
{
return wc_RNG_GenerateBlock(rng, priv, seed_len + LMS_I_LEN);
}
static int wc_lmots_sign(LmsState* state, const byte* seed, const byte* msg,
word32 msgSz, byte* sig)
{
int ret;
byte* buffer = state->buffer;
byte* ip = buffer + LMS_I_LEN + LMS_Q_LEN;
byte* jp = ip + LMS_P_LEN;
byte* tmp = jp + LMS_W_LEN;
byte* sig_c = sig;
c16toa(LMS_D_C, ip);
*jp = LMS_D_FIXED;
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
LMS_SHA256_SET_LEN_47(buffer);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, sig_c);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
LMS_SHA256_SET_LEN_55(buffer);
ret = wc_lms_hash_block(&state->hash, buffer, sig_c);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(tmp, seed, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), sig_c);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(tmp, seed, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), sig_c);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
if (ret == 0) {
byte* sig_y = sig_c + state->params->hash_len;
ret = wc_lmots_compute_y_from_seed(state, seed, msg, msgSz, sig_c,
sig_y);
}
return ret;
}
#endif
#ifndef WOLFSSL_LMS_VERIFY_ONLY
#ifndef WOLFSSL_WC_LMS_SMALL
static void wc_lms_priv_state_load(const LmsParams* params, LmsPrivState* state,
byte* priv_data)
{
state->auth_path = priv_data;
priv_data += params->height * params->hash_len;
state->stack.stack = priv_data;
priv_data += (params->height + 1) * params->hash_len;
ato32(priv_data, &state->stack.offset);
priv_data += 4;
state->root = priv_data;
priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels, params->hash_len);
state->leaf.cache = priv_data;
priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits, params->hash_len);
ato32(priv_data, &state->leaf.idx);
priv_data += 4;
ato32(priv_data, &state->leaf.offset);
}
static void wc_lms_priv_state_store(const LmsParams* params,
LmsPrivState* state, byte* priv_data)
{
priv_data += params->height * params->hash_len;
priv_data += (params->height + 1) * params->hash_len;
c32toa(state->stack.offset, priv_data);
priv_data += 4;
priv_data += LMS_ROOT_CACHE_LEN(params->rootLevels, params->hash_len);
priv_data += LMS_LEAF_CACHE_LEN(params->cacheBits, params->hash_len);
c32toa(state->leaf.idx, priv_data);
priv_data += 4;
c32toa(state->leaf.offset, priv_data);
}
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
static void wc_lms_priv_state_copy(const LmsParams* params,
LmsPrivState* dst, const LmsPrivState* src)
{
XMEMCPY(dst->auth_path, src->auth_path, LMS_PRIV_STATE_LEN(params->height,
params->rootLevels, params->cacheBits, params->hash_len));
dst->stack.offset = src->stack.offset;
dst->leaf.idx = src->leaf.idx;
dst->leaf.offset = src->leaf.offset;
}
#endif
#endif
static int wc_lms_leaf_hash(LmsState* state, const byte* seed, word32 i,
word32 r, byte* leaf)
{
int ret;
byte* buffer = state->buffer;
byte* rp = buffer + LMS_I_LEN;
byte* dp = rp + LMS_R_LEN;
byte* ots_pub_hash = dp + LMS_D_LEN;
c32toa(i, rp);
ret = wc_lmots_make_public_hash(state, seed, ots_pub_hash);
if (ret == 0) {
c32toa(r, rp);
c16toa(LMS_D_LEAF, dp);
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
LMS_SHA256_SET_LEN_46(buffer);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, leaf);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
LMS_SHA256_SET_LEN_54(buffer);
ret = wc_lms_hash_block(&state->hash, buffer, leaf);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_SEED_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), leaf);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_SEED_HASH_LEN(WC_SHA256_DIGEST_SIZE), leaf);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
}
return ret;
}
static int wc_lms_interior_hash(LmsState* state, byte* sp, word32 r,
byte* node)
{
int ret;
byte* buffer = state->buffer;
byte* rp = buffer + LMS_I_LEN;
byte* left = rp + LMS_R_LEN + LMS_D_LEN;
c32toa(r, rp);
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
XMEMCPY(left, sp, WC_SHA256_192_DIGEST_SIZE);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), node);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
XMEMCPY(left, sp, WC_SHA256_DIGEST_SIZE);
ret = wc_lms_hash(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), node);
#else
ret = NOT_COMPILED_IN;
#endif
}
return ret;
}
#ifdef WOLFSSL_WC_LMS_SMALL
static int wc_lms_treehash(LmsState* state, const byte* id, const byte* seed,
word32 q, byte* auth_path, byte* pub)
{
int ret = 0;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte* rp = buffer + LMS_I_LEN;
byte* dp = rp + LMS_R_LEN;
byte* left = dp + LMS_D_LEN;
byte* temp = left + params->hash_len;
WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
byte* sp;
word32 i;
XMEMCPY(buffer, id, LMS_I_LEN);
WC_ALLOC_VAR_EX(stack, byte, (params->height+1)*params->hash_len, NULL,
DYNAMIC_TYPE_TMP_BUFFER, ret=MEMORY_E);
sp = stack;
for (i = 0; (ret == 0) && (i < ((word32)1 << params->height)); i++) {
word32 j = i;
word16 h = 0;
word32 r = i + ((word32)1 << (params->height));
ret = wc_lms_leaf_hash(state, seed, i, r, temp);
if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) {
XMEMCPY(auth_path, temp, params->hash_len);
}
c16toa(LMS_D_INTR, dp);
while ((ret == 0) && ((j & 0x1) == 1)) {
r >>= 1;
j >>= 1;
h++;
sp -= params->hash_len;
ret = wc_lms_interior_hash(state, sp, r, temp);
if ((ret == 0) && (auth_path != NULL) && ((q >> h) ^ 0x1) == j) {
XMEMCPY(auth_path + h * params->hash_len, temp,
params->hash_len);
}
}
XMEMCPY(sp, temp, params->hash_len);
sp += params->hash_len;
}
if ((ret == 0) && (pub != NULL)) {
XMEMCPY(pub, stack, params->hash_len);
}
WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
static int wc_lms_make_public_key(LmsState* state, const byte* id,
const byte* seed, byte* pub)
{
return wc_lms_treehash(state, id, seed, 0, NULL, pub);
}
static int wc_lms_auth_path(LmsState* state, const byte* id, const byte* seed,
word32 q, byte* sig, byte* root)
{
return wc_lms_treehash(state, id, seed, q, sig, root);
}
#else
static int wc_lms_treehash_init(LmsState* state, LmsPrivState* privState,
const byte* id, const byte* seed, word32 q)
{
int ret = 0;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte* auth_path = privState->auth_path;
byte* root = privState->root;
HssLeafCache* leaf = &privState->leaf;
byte* rp = buffer + LMS_I_LEN;
byte* dp = rp + LMS_R_LEN;
byte* left = dp + LMS_D_LEN;
byte* temp = left + params->hash_len;
WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
word32 spi = 0;
word32 i;
word32 max_h = (word32)1 << params->height;
word32 max_cb = (word32)1 << params->cacheBits;
privState->stack.offset = 0;
leaf->offset = 0;
leaf->idx = q;
if ((q + max_cb) > max_h) {
leaf->idx = max_h - max_cb;
}
XMEMCPY(buffer, id, LMS_I_LEN);
WC_ALLOC_VAR_EX(stack, byte, (params->height+1)*params->hash_len, NULL,
DYNAMIC_TYPE_TMP_BUFFER, ret=MEMORY_E);
for (i = 0; (ret == 0) && (i < max_h); i++) {
word32 j = i;
word16 h = 0;
word32 r = i + max_h;
ret = wc_lms_leaf_hash(state, seed, i, r, temp);
if ((ret == 0) && (i >= leaf->idx) && (i < leaf->idx + max_cb)) {
XMEMCPY(leaf->cache + (i - leaf->idx) * params->hash_len, temp,
params->hash_len);
}
if ((ret == 0) && (auth_path != NULL) && ((q ^ 0x1) == i)) {
XMEMCPY(auth_path, temp, params->hash_len);
}
c16toa(LMS_D_INTR, dp);
while ((ret == 0) && ((j & 0x1) == 1)) {
r >>= 1;
j >>= 1;
h++;
spi -= params->hash_len;
ret = wc_lms_interior_hash(state, stack + spi, r, temp);
if ((h > params->height - params->rootLevels) &&
((i >> (h-1)) != ((i + 1) >> (h - 1)))) {
int off = ((int)1 << (params->height - h)) + (i >> h) - 1;
XMEMCPY(root + off * params->hash_len, temp, params->hash_len);
}
if ((ret == 0) && (auth_path != NULL) && ((q >> h) ^ 0x1) == j) {
XMEMCPY(auth_path + h * params->hash_len, temp,
params->hash_len);
}
}
XMEMCPY(stack + spi, temp, params->hash_len);
spi += params->hash_len;
if (i == q - 1) {
XMEMCPY(privState->stack.stack, stack, spi);
privState->stack.offset = spi;
}
}
WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
static int wc_lms_treehash_update(LmsState* state, LmsPrivState* privState,
const byte* id, const byte* seed, word32 min_idx, word32 max_idx, word32 q,
int useRoot)
{
int ret = 0;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte* auth_path = privState->auth_path;
LmsStack* stackCache = &privState->stack;
HssLeafCache* leaf = &privState->leaf;
byte* rp = buffer + LMS_I_LEN;
byte* dp = rp + LMS_R_LEN;
byte* left = dp + LMS_D_LEN;
byte* temp = left + params->hash_len;
WC_DECLARE_VAR(stack, byte, (LMS_MAX_HEIGHT + 1) * LMS_MAX_NODE_LEN, 0);
byte* sp;
word32 max_cb = (word32)1 << params->cacheBits;
word32 i;
XMEMCPY(buffer, id, LMS_I_LEN);
WC_ALLOC_VAR_EX(stack, byte, (params->height+1)*params->hash_len, NULL,
DYNAMIC_TYPE_TMP_BUFFER, ret=MEMORY_E);
if (ret == 0) {
XMEMCPY(stack, stackCache->stack, params->height * params->hash_len);
sp = stack + stackCache->offset;
}
for (i = min_idx; (ret == 0) && (i <= max_idx); i++) {
word32 j = i;
word16 h = 0;
word32 r = i + ((word32)1 << (params->height));
if ((i >= leaf->idx) && (i < leaf->idx + max_cb)) {
word32 off = ((i - (leaf->idx + max_cb) + leaf->offset) % max_cb) *
params->hash_len;
XMEMCPY(temp, leaf->cache + off, params->hash_len);
c32toa(i, rp);
}
else {
ret = wc_lms_leaf_hash(state, seed, i, r, temp);
if ((i == leaf->idx + max_cb) && (i < (q + max_cb))) {
XMEMCPY(leaf->cache + leaf->offset * params->hash_len, temp,
params->hash_len);
leaf->idx++;
leaf->offset = (leaf->offset + 1) & (max_cb - 1);
}
}
if ((ret == 0) && ((q ^ 0x1) == i)) {
XMEMCPY(auth_path, temp, params->hash_len);
}
c16toa(LMS_D_INTR, dp);
while ((ret == 0) && ((j & 0x1) == 1)) {
r >>= 1;
j >>= 1;
h++;
sp -= params->hash_len;
if (useRoot && (h > params->height - params->rootLevels) &&
(h <= params->height)) {
word32 off = ((word32)1U << (params->height - h)) +
(i >> h) - 1;
XMEMCPY(temp, privState->root + (off * params->hash_len),
params->hash_len);
}
else {
ret = wc_lms_interior_hash(state, sp, r, temp);
}
if ((ret == 0) && (q == 0) && (!useRoot) &&
(h > params->height - params->rootLevels) &&
((i >> (h-1)) != ((i + 1) >> (h - 1)))) {
int off = ((int)1 << (params->height - h)) + (i >> h) - 1;
XMEMCPY(privState->root + off * params->hash_len, temp,
params->hash_len);
}
if ((ret == 0) && (((q >> h) ^ 0x1) == j)) {
XMEMCPY(auth_path + h * params->hash_len, temp,
params->hash_len);
}
}
if (ret == 0) {
XMEMCPY(sp, temp, params->hash_len);
sp += params->hash_len;
if (i == min_idx) {
stackCache->offset = (word32)((size_t)sp - (size_t)stack);
XMEMCPY(stackCache->stack, stack, stackCache->offset);
}
}
}
if (!useRoot && (ret == 0)) {
XMEMCPY(stackCache->stack, stack, params->height * params->hash_len);
stackCache->offset = (word32)((size_t)sp - (size_t)stack);
}
WC_FREE_VAR_EX(stack, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
#endif
static int wc_lms_sign(LmsState* state, const byte* priv, const byte* msg,
word32 msgSz, byte* sig)
{
int ret;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte* s = sig;
const byte* priv_q = priv;
const byte* priv_seed = priv_q + LMS_Q_LEN;
const byte* priv_i = priv_seed + params->hash_len;
XMEMCPY(buffer, priv_i, LMS_I_LEN);
XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN);
XMEMCPY(s, priv_q, LMS_Q_LEN);
s += LMS_Q_LEN;
c32toa(state->params->lmOtsType & LMS_H_W_MASK, s);
s += LMS_TYPE_LEN;
ret = wc_lmots_sign(state, priv_seed, msg, msgSz, s);
if (ret == 0) {
s += params->hash_len + params->p * params->hash_len;
c32toa(params->lmsType & LMS_H_W_MASK, s);
}
return ret;
}
#if !defined(WOLFSSL_WC_LMS_SMALL) && !defined(WOLFSSL_LMS_NO_SIG_CACHE)
static void wc_lms_sig_copy(const LmsParams* params, const byte* y,
const byte* priv, byte* sig)
{
XMEMCPY(sig, priv, LMS_Q_LEN);
sig += LMS_Q_LEN;
c32toa(params->lmOtsType & LMS_H_W_MASK, sig);
sig += LMS_TYPE_LEN;
XMEMCPY(sig, y, params->hash_len + params->p * params->hash_len);
sig += params->hash_len + params->p * params->hash_len;
c32toa(params->lmsType & LMS_H_W_MASK, sig);
}
#endif
#endif
static int wc_lms_compute_root(LmsState* state, word32 q, const byte* kc,
const byte* path, byte* tc)
{
int ret;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte* rp = buffer + LMS_I_LEN;
byte* ip = rp + LMS_Q_LEN;
byte* node = ip + LMS_P_LEN;
byte* b[2][2];
word32 r = ((word32)1 << params->height) + q;
c32toa(r, rp);
c16toa(LMS_D_LEAF, ip);
XMEMCPY(node, kc, params->hash_len);
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
b[0][0] = node;
b[0][1] = node + WC_SHA256_192_DIGEST_SIZE;
b[1][0] = node + WC_SHA256_192_DIGEST_SIZE;
b[1][1] = node;
LMS_SHA256_SET_LEN_46(buffer);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, b[r & 1][0]);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
b[0][0] = node;
b[0][1] = node + WC_SHA256_DIGEST_SIZE;
b[1][0] = node + WC_SHA256_DIGEST_SIZE;
b[1][1] = node;
LMS_SHA256_SET_LEN_54(buffer);
ret = wc_lms_hash_block(&state->hash, buffer, b[r & 1][0]);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
b[0][0] = node;
b[0][1] = node + WC_SHA256_192_DIGEST_SIZE;
b[1][0] = node + WC_SHA256_192_DIGEST_SIZE;
b[1][1] = node;
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_SEED_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), b[r & 1][0]);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
b[0][0] = node;
b[0][1] = node + WC_SHA256_DIGEST_SIZE;
b[1][0] = node + WC_SHA256_DIGEST_SIZE;
b[1][1] = node;
ret = wc_lms_hash(&state->hash, buffer,
LMS_SEED_HASH_LEN(WC_SHA256_DIGEST_SIZE), b[r & 1][0]);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
if (ret == 0) {
int i;
c16toa(LMS_D_INTR, ip);
#ifdef WOLFSSL_LMS_SHA256_192
if ((params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
XMEMCPY(b[r & 1][1], path, WC_SHA256_192_DIGEST_SIZE);
path += WC_SHA256_192_DIGEST_SIZE;
r >>= 1;
c32toa(r, rp);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), b[r & 1][0]);
}
if (ret == 0) {
XMEMCPY(b[r & 1][1], path, WC_SHA256_192_DIGEST_SIZE);
r >>= 1;
c32toa(r, rp);
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_192_DIGEST_SIZE), tc);
}
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
for (i = 0; (ret == 0) && (i < params->height - 1); i++) {
XMEMCPY(b[r & 1][1], path, WC_SHA256_DIGEST_SIZE);
path += WC_SHA256_DIGEST_SIZE;
r >>= 1;
c32toa(r, rp);
ret = wc_lms_hash(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), b[r & 1][0]);
}
if (ret == 0) {
XMEMCPY(b[r & 1][1], path, WC_SHA256_DIGEST_SIZE);
r >>= 1;
c32toa(r, rp);
ret = wc_lms_hash(&state->hash, buffer,
LMS_NODE_HASH_LEN(WC_SHA256_DIGEST_SIZE), tc);
}
#else
ret = NOT_COMPILED_IN;
#endif
}
}
return ret;
}
static int wc_lms_verify(LmsState* state, const byte* pub, const byte* msg,
word32 msgSz, const byte* sig)
{
int ret;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
const byte* pub_i = pub + LMS_TYPE_LEN + LMS_TYPE_LEN;
const byte* pub_k = pub_i + LMS_I_LEN;
const byte* sig_q = sig;
byte tc[LMS_MAX_NODE_LEN];
byte* kc = tc;
ret = wc_lmots_public_key_check(params, pub);
if (ret == 0) {
const byte* sig_lmots = sig + LMS_Q_LEN;
XMEMCPY(buffer, pub_i, LMS_I_LEN);
XMEMCPY(buffer + LMS_I_LEN, sig_q, LMS_Q_LEN);
ret = wc_lmots_calc_kc(state, pub + LMS_TYPE_LEN, msg, msgSz,
sig_lmots, kc);
}
if (ret == 0) {
const byte* sig_path = sig + LMS_Q_LEN + LMS_TYPE_LEN +
params->hash_len + params->p * params->hash_len + LMS_TYPE_LEN;
word32 q;
ato32(sig_q, &q);
ret = wc_lms_compute_root(state, q, kc, sig_path, tc);
}
if ((ret == 0) && (XMEMCMP(pub_k, tc, params->hash_len) != 0)) {
ret = SIG_VERIFY_E;
}
return ret;
}
#ifndef WOLFSSL_LMS_VERIFY_ONLY
static int wc_hss_derive_seed_i(LmsState* state, const byte* id,
const byte* seed, const byte* q, byte* seed_i)
{
int ret = 0;
byte buffer[WC_SHA256_BLOCK_SIZE];
byte* idp = buffer;
byte* qp = idp + LMS_I_LEN;
byte* ip = qp + LMS_Q_LEN;
byte* jp = ip + LMS_P_LEN;
byte* tmp = jp + LMS_W_LEN;
XMEMCPY(idp, id, LMS_I_LEN);
XMEMCPY(qp, q, LMS_Q_LEN);
c16toa(LMS_D_CHILD_SEED, ip);
*jp = LMS_D_FIXED;
XMEMCPY(tmp, seed, state->params->hash_len);
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
LMS_SHA256_SET_LEN_47(buffer);
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, seed_i);
if (ret == 0) {
seed_i += WC_SHA256_192_DIGEST_SIZE;
}
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
LMS_SHA256_SET_LEN_55(buffer);
ret = wc_lms_hash_block(&state->hash, buffer, seed_i);
if (ret == 0) {
seed_i += WC_SHA256_DIGEST_SIZE;
}
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), seed_i);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), seed_i);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
if (ret == 0) {
c16toa(LMS_D_CHILD_I, ip);
#ifndef WC_LMS_FULL_HASH
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_sha256_192_hash_block(&state->hash, buffer, tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash_block(&state->hash, buffer, tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#else
#ifdef WOLFSSL_LMS_SHA256_192
if ((state->params->lmOtsType & LMS_HASH_MASK) == LMS_SHA256_192) {
ret = wc_lms_hash_sha256_192(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_192_DIGEST_SIZE), tmp);
}
else
#endif
{
#ifndef WOLFSSL_NO_LMS_SHA256_256
ret = wc_lms_hash(&state->hash, buffer,
LMS_HASH_BUFFER_LEN(WC_SHA256_DIGEST_SIZE), tmp);
#else
ret = NOT_COMPILED_IN;
#endif
}
#endif
XMEMCPY(seed_i, tmp, LMS_I_LEN);
}
return ret;
}
#define LMS_Q_AT_LEVEL(q, ls, l, h) \
(w64GetLow32(w64ShiftRight((q), (((ls) - 1 - (l)) * (h)))) & \
(((word32)1 << (h)) - 1))
static int wc_hss_expand_private_key(LmsState* state, byte* priv,
const byte* priv_raw, int inc)
{
const LmsParams* params = state->params;
int ret = 0;
w64wrapper q;
w64wrapper qm1;
word32 q32;
byte* priv_q;
byte* priv_seed_i;
word32 i;
ato64(priv_raw, &q);
priv_raw += HSS_Q_LEN + HSS_PRIV_KEY_PARAM_SET_LEN;
q32 = LMS_Q_AT_LEVEL(q, params->levels, 0, params->height);
c32toa(q32, priv);
if (inc) {
qm1 = q;
w64Decrement(&qm1);
}
else {
XMEMCPY(priv + LMS_Q_LEN, priv_raw, params->hash_len + LMS_I_LEN);
}
for (i = 1U; (ret == 0) && (i < params->levels); i++) {
int skip = 0;
if (inc) {
word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, i - 1,
params->height);
if (q32 == qm1_32) {
skip = 1;
}
}
priv_q = priv;
priv += LMS_Q_LEN;
priv_seed_i = priv;
priv += params->hash_len + LMS_I_LEN;
q32 = w64GetLow32(w64ShiftRight(q, (params->levels - 1 - i) *
params->height)) & (((word32)1 << params->height) - 1);
c32toa(q32, priv);
if (!skip) {
ret = wc_hss_derive_seed_i(state, priv_seed_i + params->hash_len,
priv_seed_i, priv_q, priv + LMS_Q_LEN);
}
}
return ret;
}
#ifndef WOLFSSL_WC_LMS_SMALL
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
static int wc_lms_next_subtree_init(LmsState* state, LmsPrivState* privState,
byte* curr, byte* priv, word32 q)
{
int ret;
const LmsParams* params = state->params;
byte* priv_q;
byte* priv_seed;
byte* priv_i;
word32 pq;
priv_q = priv;
priv += LMS_Q_LEN;
priv_seed = curr + LMS_Q_LEN;
priv += params->hash_len;
priv_i = curr + LMS_Q_LEN + params->hash_len;
priv += LMS_I_LEN;
ato32(curr, &pq);
pq = (pq + 1U) & ((((word32)1U) << params->height) - (word32)1U);
c32toa(pq, priv_q);
privState->stack.offset = 0;
privState->leaf.idx = (word32)(0U - ((word32)1U << params->cacheBits));
privState->leaf.offset = 0;
ret = wc_hss_derive_seed_i(state, priv_i, priv_seed, priv_q,
priv + LMS_Q_LEN);
if (ret == 0) {
ret = wc_lms_treehash_update(state, privState,
priv + LMS_Q_LEN + params->hash_len, priv + LMS_Q_LEN, 0, q, 0, 0);
}
return ret;
}
static int wc_hss_next_subtree_inc(LmsState* state, HssPrivKey* priv_key,
w64wrapper q64)
{
int ret = 0;
const LmsParams* params = state->params;
byte* curr = priv_key->priv;
byte* priv = priv_key->next_priv;
int i;
w64wrapper p64 = q64;
byte tmp_priv[LMS_PRIV_LEN(LMS_MAX_NODE_LEN)];
int use_tmp = 0;
word32 lastQMax = 0;
w64wrapper p64_hi;
w64wrapper q64_hi;
w64Decrement(&p64);
p64_hi = w64ShiftRight(p64, (params->levels - 1) * params->height);
q64_hi = w64ShiftRight(q64, (params->levels - 1) * params->height);
for (i = 1; (ret == 0) && (i < params->levels); i++) {
word32 qc;
w64wrapper cp64_hi;
w64wrapper cq64_hi;
cp64_hi = w64ShiftRight(p64, (params->levels - i - 1) * params->height);
cq64_hi = w64ShiftRight(q64, (params->levels - i - 1) * params->height);
ato32(curr + LMS_PRIV_LEN(params->hash_len), (unsigned int*)&qc);
if (w64LT(p64_hi, q64_hi)) {
wc_lms_priv_state_copy(params, &priv_key->state[i],
&priv_key->next_state[i-1]);
ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1],
use_tmp ? tmp_priv : curr, priv, 0);
use_tmp = 0;
}
else if ((qc == ((word32)1 << params->height) - 1) &&
w64LT(cp64_hi, cq64_hi)) {
XMEMSET(tmp_priv, 0, LMS_Q_LEN);
if (lastQMax) {
ret = wc_hss_derive_seed_i(state,
priv + LMS_Q_LEN + params->hash_len, priv + LMS_Q_LEN,
tmp_priv, tmp_priv + LMS_Q_LEN);
}
else {
ret = wc_hss_derive_seed_i(state,
curr + LMS_Q_LEN + params->hash_len, curr + LMS_Q_LEN, priv,
tmp_priv + LMS_Q_LEN);
}
use_tmp = 1;
XMEMCPY(tmp_priv, curr + LMS_PRIV_LEN(params->hash_len), LMS_Q_LEN);
}
lastQMax = (qc == (((word32)1U << params->height) - (word32)1U));
curr += LMS_PRIV_LEN(params->hash_len);
priv += LMS_PRIV_LEN(params->hash_len);
p64_hi = cp64_hi;
q64_hi = cq64_hi;
}
return ret;
}
static int wc_hss_next_subtrees_init(LmsState* state, HssPrivKey* priv_key)
{
int ret = 0;
const LmsParams* params = state->params;
byte* curr = priv_key->priv;
byte* priv = priv_key->next_priv;
int i;
XMEMCPY(priv, curr, LMS_PRIV_LEN(params->hash_len));
wc_lms_idx_inc(priv, LMS_Q_LEN);
for (i = 1; (ret == 0) && (i < params->levels); i++) {
word32 q;
ato32(curr + LMS_PRIV_LEN(params->hash_len), &q);
ret = wc_lms_next_subtree_init(state, &priv_key->next_state[i - 1],
curr, priv, q);
curr += LMS_PRIV_LEN(params->hash_len);
priv += LMS_PRIV_LEN(params->hash_len);
}
return ret;
}
#endif
static int wc_hss_init_auth_path(LmsState* state, HssPrivKey* priv_key,
byte* pub_root)
{
int ret = 0;
int levels = state->params->levels;
byte* priv = priv_key->priv +
LMS_PRIV_LEN(state->params->hash_len) * (levels - 1);
int l;
for (l = levels - 1; (ret == 0) && (l >= 0); l--) {
word32 q;
const byte* priv_q = priv;
const byte* priv_seed = priv_q + LMS_Q_LEN;
const byte* priv_i = priv_seed + state->params->hash_len;
ato32(priv_q, &q);
ret = wc_lms_treehash_init(state, &priv_key->state[l], priv_i,
priv_seed, q);
priv -= LMS_PRIV_LEN(state->params->hash_len);
}
if ((ret == 0) && (pub_root != NULL)) {
XMEMCPY(pub_root, priv_key->state[0].root, state->params->hash_len);
}
return ret;
}
#define LMS_AUTH_PATH_IDX(i, h) \
(((i) ^ ((word32)1U << (h))) | (((word32)1U << (h)) - 1))
static int wc_hss_update_auth_path(LmsState* state, HssPrivKey* priv_key,
byte* priv_raw, int levels)
{
const LmsParams* params = state->params;
int ret = 0;
byte* priv = priv_key->priv + LMS_PRIV_LEN(params->hash_len) * (levels - 1);
int i;
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
w64wrapper q64;
#endif
(void)priv_raw;
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
ato64(priv_raw, &q64);
#endif
for (i = levels - 1; (ret == 0) && (i >= 0); i--) {
word32 q;
const byte* priv_q = priv;
const byte* priv_seed = priv_q + LMS_Q_LEN;
const byte* priv_i = priv_seed + params->hash_len;
LmsPrivState* privState = &priv_key->state[i];
ato32(priv_q, &q);
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
if ((levels > 1) && (i == levels - 1) && (q == 0)) {
ret = wc_hss_next_subtree_inc(state, priv_key, q64);
}
if ((ret == 0) && (q != 0))
#else
if (q == 0) {
ret = wc_lms_treehash_init(state, privState, priv_i, priv_seed, 0);
}
else
#endif
{
word32 maxq = q - 1;
int h;
int maxh = params->height;
for (h = 0; (h < maxh) && (h <= maxh - params->rootLevels); h++) {
word32 qa = LMS_AUTH_PATH_IDX(q, h);
word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h);
if ((qa != qm1a) && (qa > maxq)) {
maxq = qa;
}
}
for (; h < maxh; h++) {
word32 qa = LMS_AUTH_PATH_IDX(q, h);
word32 qm1a = LMS_AUTH_PATH_IDX(q - 1, h);
if ((qa != qm1a) && (qa > maxq)) {
int off = ((int)1 << (params->height - h)) + (qa >> h) - 1;
XMEMCPY(privState->auth_path + h * params->hash_len,
privState->root + off * params->hash_len,
params->hash_len);
}
}
ret = wc_lms_treehash_update(state, privState, priv_i, priv_seed,
q - 1, maxq, q, 1);
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
if ((ret == 0) && (i > 0)) {
w64wrapper tmp64 = w64ShiftRight(q64,
(levels - i) * params->height);
w64Increment(&tmp64);
tmp64 = w64ShiftLeft(tmp64, 64 - (i * params->height));
if (!w64IsZero(tmp64)) {
priv_seed = priv_key->next_priv +
i * LMS_PRIV_LEN(params->hash_len) + LMS_Q_LEN;
priv_i = priv_seed + params->hash_len;
privState = &priv_key->next_state[i - 1];
ret = wc_lms_treehash_update(state, privState, priv_i,
priv_seed, q, q, 0, 0);
}
}
#endif
break;
}
priv -= LMS_PRIV_LEN(params->hash_len);
}
return ret;
}
#if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
static int wc_hss_presign(LmsState* state, HssPrivKey* priv_key)
{
int ret = 0;
const LmsParams* params = state->params;
byte* buffer = state->buffer;
byte pub[LMS_PUBKEY_LEN(LMS_MAX_NODE_LEN)];
byte* root = pub + LMS_PUBKEY_LEN(LMS_MAX_NODE_LEN) - params->hash_len;
byte* priv = priv_key->priv;
int i;
for (i = params->levels - 2; i >= 0; i--) {
const byte* p = priv + i * (LMS_Q_LEN + params->hash_len + LMS_I_LEN);
const byte* priv_q = p;
const byte* priv_seed = priv_q + LMS_Q_LEN;
const byte* priv_i = priv_seed + params->hash_len;
XMEMCPY(root, priv_key->state[i + 1].root, params->hash_len);
p = priv + (i + 1) * (LMS_Q_LEN + params->hash_len + LMS_I_LEN);
wc_lmots_public_key_encode(params, p, pub);
XMEMCPY(buffer, priv_i, LMS_I_LEN);
XMEMCPY(buffer + LMS_I_LEN, priv_q, LMS_Q_LEN);
ret = wc_lmots_sign(state, priv_seed, pub,
LMS_PUBKEY_LEN(params->hash_len),
priv_key->y + i * LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len));
}
return ret;
}
#endif
#endif
static void wc_hss_priv_data_load(const LmsParams* params, HssPrivKey* key,
byte* priv_data)
{
#ifndef WOLFSSL_WC_LMS_SMALL
int l;
#endif
key->priv = priv_data;
priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
#ifndef WOLFSSL_WC_LMS_SMALL
for (l = 0; l < params->levels; l++) {
wc_lms_priv_state_load(params, &key->state[l], priv_data);
priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
params->cacheBits, params->hash_len);
}
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
key->next_priv = priv_data;
priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
for (l = 0; l < params->levels - 1; l++) {
wc_lms_priv_state_load(params, &key->next_state[l], priv_data);
priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
params->cacheBits, params->hash_len);
}
#endif
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
key->y = priv_data;
#endif
#endif
}
#ifndef WOLFSSL_WC_LMS_SMALL
static void wc_hss_priv_data_store(const LmsParams* params, HssPrivKey* key,
byte* priv_data)
{
int l;
(void)key;
priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
for (l = 0; l < params->levels; l++) {
wc_lms_priv_state_store(params, &key->state[l], priv_data);
priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
params->cacheBits, params->hash_len);
}
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
priv_data += LMS_PRIV_KEY_LEN(params->levels, params->hash_len);
for (l = 0; l < params->levels - 1; l++) {
wc_lms_priv_state_store(params, &key->next_state[l], priv_data);
priv_data += LMS_PRIV_STATE_LEN(params->height, params->rootLevels,
params->cacheBits, params->hash_len);
}
#endif
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
#endif
}
#endif
int wc_hss_reload_key(LmsState* state, const byte* priv_raw,
HssPrivKey* priv_key, byte* priv_data, byte* pub_root)
{
int ret = 0;
(void)pub_root;
if (state->params->height >= 32U) {
return BAD_FUNC_ARG;
}
#ifndef WOLFSSL_WC_LMS_SMALL
if (state->params->cacheBits >= 32U) {
return BAD_FUNC_ARG;
}
#endif
wc_hss_priv_data_load(state->params, priv_key, priv_data);
#ifndef WOLFSSL_WC_LMS_SMALL
priv_key->inited = 0;
#endif
#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE
if (pub_root != NULL)
#endif
{
ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 0);
#ifndef WOLFSSL_WC_LMS_SMALL
if ((ret == 0) && (!priv_key->inited)) {
ret = wc_hss_init_auth_path(state, priv_key, pub_root);
#ifndef WOLFSSL_LMS_NO_SIGN_SMOOTHING
if (ret == 0) {
ret = wc_hss_next_subtrees_init(state, priv_key);
}
#endif
#if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
if (ret == 0) {
ret = wc_hss_presign(state, priv_key);
}
#endif
}
#endif
}
#ifndef WOLFSSL_WC_LMS_SMALL
priv_key->inited = (ret == 0);
#endif
return ret;
}
int wc_hss_make_key(LmsState* state, WC_RNG* rng, byte* priv_raw,
HssPrivKey* priv_key, byte* priv_data, byte* pub)
{
const LmsParams* params = state->params;
int ret = 0;
int i;
byte* p = priv_raw;
byte* pub_root = pub + LMS_L_LEN + LMS_TYPE_LEN + LMS_TYPE_LEN + LMS_I_LEN;
wc_lms_idx_zero(p, HSS_Q_LEN);
p += HSS_Q_LEN;
for (i = 0; i < params->levels; i++) {
p[i] = ((params->lmsType & LMS_H_W_MASK) << 4) +
(params->lmOtsType & LMS_H_W_MASK);
}
for (; i < HSS_MAX_LEVELS; i++) {
p[i] = 0xff;
}
p += HSS_PRIV_KEY_PARAM_SET_LEN;
ret = wc_lmots_make_private_key(rng, params->hash_len, p);
if (ret == 0) {
c32toa(params->levels, pub);
pub += LMS_L_LEN;
ret = wc_hss_reload_key(state, priv_raw, priv_key, priv_data, pub_root);
}
#ifdef WOLFSSL_WC_LMS_SMALL
if (ret == 0) {
byte* priv_seed = priv_key->priv + LMS_Q_LEN;
byte* priv_i = priv_seed + params->hash_len;
ret = wc_lms_make_public_key(state, priv_i, priv_seed, pub_root);
}
#endif
if (ret == 0) {
wc_lmots_public_key_encode(params, priv_key->priv, pub);
}
#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE
wc_hss_priv_data_store(state->params, priv_key, priv_data);
#endif
return ret;
}
#ifdef WOLFSSL_WC_LMS_SMALL
int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key,
byte* priv_data, const byte* msg, word32 msgSz, byte* sig)
{
const LmsParams* params = state->params;
int ret = 0;
byte* priv = priv_key->priv;
(void)priv_data;
if (!wc_hss_sigsleft(params, priv_raw)) {
ret = KEY_EXHAUSTED_E;
}
if (ret == 0) {
ret = wc_hss_expand_private_key(state, priv, priv_raw, 0);
}
if (ret == 0) {
int i;
w64wrapper q;
w64wrapper qm1;
ato64(priv_raw, &q);
qm1 = q;
w64Decrement(&qm1);
c32toa(params->levels - 1, sig);
sig += params->sig_len;
for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) {
byte* p = priv + i * (LMS_Q_LEN + params->hash_len + LMS_I_LEN);
byte* root = NULL;
sig -= LMS_SIG_LEN(params->height, params->p, params->hash_len);
if (i != 0) {
root = sig - params->hash_len;
}
ret = wc_lms_sign(state, p, msg, msgSz, sig);
if (ret == 0) {
byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN + params->hash_len +
params->p * params->hash_len + LMS_TYPE_LEN;
byte* priv_q = p;
byte* priv_seed = priv_q + LMS_Q_LEN;
byte* priv_i = priv_seed + params->hash_len;
word32 q32;
ato32(priv_q, &q32);
ret = wc_lms_auth_path(state, priv_i, priv_seed, q32, s, root);
}
if ((ret == 0) && (i != 0)) {
sig -= LMS_PUBKEY_LEN(params->hash_len);
msg = sig;
msgSz = LMS_PUBKEY_LEN(params->hash_len);
wc_lmots_public_key_encode(params, p, sig);
}
}
}
if (ret == 0) {
wc_lms_idx_inc(priv_raw, HSS_Q_LEN);
}
return ret;
}
#else
static int wc_hss_sign_build_sig(LmsState* state, byte* priv_raw,
HssPrivKey* priv_key, const byte* msg, word32 msgSz, byte* sig)
{
const LmsParams* params = state->params;
int ret = 0;
int i;
w64wrapper q;
w64wrapper qm1;
byte* priv = priv_key->priv;
ato64(priv_raw, &q);
qm1 = q;
w64Decrement(&qm1);
c32toa(params->levels - 1, sig);
sig += params->sig_len;
for (i = params->levels - 1; (ret == 0) && (i >= 0); i--) {
byte* p = priv + i * (LMS_Q_LEN + params->hash_len + LMS_I_LEN);
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
byte* root = NULL;
#endif
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
int store_p = 0;
word32 q_32 = LMS_Q_AT_LEVEL(q, params->levels, i,
params->height);
word32 qm1_32 = LMS_Q_AT_LEVEL(qm1, params->levels, i,
params->height);
#endif
sig -= LMS_SIG_LEN(params->height, params->p, params->hash_len);
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
if (i != 0) {
root = sig - params->hash_len;
}
#endif
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
if ((i < params->levels - 1) && (q_32 == qm1_32)) {
wc_lms_sig_copy(params, priv_key->y +
i * LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len), p, sig);
}
else
#endif
{
ret = wc_lms_sign(state, p, msg, msgSz, sig);
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
store_p = (i < params->levels - 1);
#endif
}
if (ret == 0) {
byte* s = sig + LMS_Q_LEN + LMS_TYPE_LEN;
#ifndef WOLFSSL_LMS_NO_SIG_CACHE
if (store_p) {
XMEMCPY(priv_key->y +
i * LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len), s,
LMS_PRIV_Y_TREE_LEN(params->p, params->hash_len));
}
#endif
s += params->hash_len + params->p * params->hash_len +
LMS_TYPE_LEN;
XMEMCPY(s, priv_key->state[i].auth_path,
params->height * params->hash_len);
#if !defined(WOLFSSL_LMS_MAX_LEVELS) || WOLFSSL_LMS_MAX_LEVELS > 1
if (i != 0) {
XMEMCPY(root, priv_key->state[i].root, params->hash_len);
}
#endif
}
if ((ret == 0) && (i != 0)) {
sig -= LMS_PUBKEY_LEN(params->hash_len);
msg = sig;
msgSz = LMS_PUBKEY_LEN(params->hash_len);
wc_lmots_public_key_encode(params, p, sig);
}
}
return ret;
}
int wc_hss_sign(LmsState* state, byte* priv_raw, HssPrivKey* priv_key,
byte* priv_data, const byte* msg, word32 msgSz, byte* sig)
{
const LmsParams* params = state->params;
int ret = 0;
if ((params->rootLevels == 0) || (params->rootLevels > params->height)) {
ret = BAD_FUNC_ARG;
}
if ((ret == 0) && (!wc_hss_sigsleft(params, priv_raw))) {
ret = KEY_EXHAUSTED_E;
}
if ((ret == 0) && (!priv_key->inited)) {
ret = wc_hss_init_auth_path(state, priv_key, NULL);
#if !defined(WOLFSSL_LMS_NO_SIG_CACHE) && (LMS_MAX_LEVELS > 1)
if (ret == 0) {
ret = wc_hss_presign(state, priv_key);
}
#endif
priv_key->inited = (ret == 0);
}
if (ret == 0) {
ret = wc_hss_sign_build_sig(state, priv_raw, priv_key, msg, msgSz, sig);
}
if (ret == 0) {
wc_lms_idx_inc(priv_raw, HSS_Q_LEN);
}
if ((ret == 0) && wc_hss_sigsleft(params, priv_raw)) {
ret = wc_hss_expand_private_key(state, priv_key->priv, priv_raw, 1);
if (ret == 0) {
ret = wc_hss_update_auth_path(state, priv_key, priv_raw,
params->levels);
}
}
if (ret == 0) {
wc_hss_priv_data_store(state->params, priv_key, priv_data);
}
return ret;
}
#endif
int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw)
{
w64wrapper q;
w64wrapper cnt;
ato64(priv_raw, &q);
cnt = w64ShiftLeft(w64From32(0, 1), params->levels * params->height);
return w64LT(q, cnt);
}
#endif
int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg,
word32 msgSz, const byte* sig)
{
const LmsParams* params = state->params;
int ret = 0;
word32 nspk;
const byte* key = pub + LMS_L_LEN;
word32 levels;
ato32(pub, &levels);
ato32(sig, &nspk);
sig += LMS_L_LEN;
if (nspk + 1 != levels) {
ret = SIG_VERIFY_E;
}
if (ret == 0) {
word32 i;
for (i = 0; (ret == 0) && (i < nspk); i++) {
const byte* pubList = sig + LMS_Q_LEN + LMS_TYPE_LEN +
params->hash_len + params->p * params->hash_len + LMS_TYPE_LEN +
params->height * params->hash_len;
ret = wc_lms_verify(state, key, pubList,
LMS_PUBKEY_LEN(params->hash_len), sig);
key = pubList;
sig = pubList + LMS_PUBKEY_LEN(params->hash_len);
}
}
if (ret == 0) {
ret = wc_lms_verify(state, key, msg, msgSz, sig);
}
return ret;
}
#endif