#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#include <wolfssl/wolfcrypt/wc_xmss.h>
#include <wolfssl/wolfcrypt/hash.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#if defined(WOLFSSL_HAVE_XMSS)
#define XMSS_ADDR_LAYER 0
#define XMSS_ADDR_TREE_HI 1
#define XMSS_ADDR_TREE 2
#define XMSS_ADDR_TYPE 3
#define XMSS_ADDR_OTS 4
#define XMSS_ADDR_LTREE 4
#define XMSS_ADDR_TREE_ZERO 4
#define XMSS_ADDR_CHAIN 5
#define XMSS_ADDR_TREE_HEIGHT 5
#define XMSS_ADDR_HASH 6
#define XMSS_ADDR_TREE_INDEX 6
#define XMSS_ADDR_KEY_MASK 7
#define WC_XMSS_ADDR_TYPE_OTS 0
#define WC_XMSS_ADDR_TYPE_LTREE 1
#define WC_XMSS_ADDR_TYPE_TREE 2
#define XMSS_HASH_PADDING_F 0
#define XMSS_HASH_PADDING_H 1
#define XMSS_HASH_PADDING_HASH 2
#define XMSS_HASH_PADDING_PRF 3
#define XMSS_HASH_PADDING_PRF_KEYGEN 4
#define XMSS_WOTS_W 16
#define XMSS_WOTS_LOG_W 4
#define XMSS_WOTS_LEN2 3
#define XMSS_CSUM_SHIFT 4
#define XMSS_CSUM_LEN 2
#define XMSS_PRF_M_LEN 32
#define XMSS_IDX_LEN 4
#define XMSS_SHA256_32_N WC_SHA256_DIGEST_SIZE
#define XMSS_SHA256_32_PAD_LEN 32
#define XMSS_HASH_PRF_DATA_LEN(params) \
((params)->pad_len + (params)->n + WC_XMSS_ADDR_LEN)
#define XMSS_HASH_PRF_DATA_LEN_SHA256_32 \
(XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN)
#define XMSS_CHAIN_HASH_DATA_LEN(params) \
((params)->pad_len + 2 * (params)->n)
#define XMSS_CHAIN_HASH_DATA_LEN_SHA256_32 \
(XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N)
#define XMSS_RAND_HASH_DATA_LEN(params) \
((params)->pad_len + 3 * (params)->n)
#define XMSS_RAND_HASH_DATA_LEN_SHA256_32 \
(XMSS_SHA256_32_PAD_LEN + 3 * XMSS_SHA256_32_N)
#define XMSS_PAD_ENC(n, a, l) \
do { \
XMEMSET(a, 0, l); \
(a)[(l) - 1] = (n); \
} while (0)
typedef union wc_Idx {
#if WOLFSSL_XMSS_MAX_HEIGHT > 32
w64wrapper u64;
#endif
#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
word32 u32;
#endif
} wc_Idx;
#if WOLFSSL_XMSS_MAX_HEIGHT > 32
#define WC_IDX_ZERO(idx) w64Zero(&(idx).u64)
#else
#define WC_IDX_ZERO(idx) idx.u32 = 0
#endif
#if WOLFSSL_XMSS_MAX_HEIGHT > 32
#define IDX64_DECODE(i, c, a, ret) \
if ((c) == 5) { \
word32 t; \
ato32((a) + 1, &t); \
(i) = w64From32((a)[0], t); \
} \
else if ((c) == 8) { \
ato64(a, &(i)); \
}
#define XMSS_IDX64_DECODE(i, c, a, ret) \
do { \
IDX64_DECODE(i, c, a, ret) \
else { \
(ret) = NOT_COMPILED_IN; \
} \
} while (0)
#define IDX64_INVALID(i, c, h) \
((w64GetHigh32(w64Add32(i, 1, NULL)) >> ((h) - 32)) != 0)
#define IDX64_SET_ADDR_TREE(i, c, h, a, l) \
if ((c) > 4) { \
(l) = w64GetLow32(i) & (((word32)1 << (h)) - 1);\
(i) = w64ShiftRight(i, h); \
(a)[XMSS_ADDR_TREE_HI] = w64GetHigh32(i); \
(a)[XMSS_ADDR_TREE] = w64GetLow32(i); \
}
#endif
#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
#define IDX32_DECODE(i, c, a, ret) \
if ((c) == 4) { \
ato32(a, &(i)); \
} \
else if ((c) == 3) { \
ato24(a, &(i)); \
}
#define XMSS_IDX32_DECODE(i, c, a, ret) \
do { \
IDX32_DECODE(i, c, a, ret) \
else { \
(ret) = NOT_COMPILED_IN; \
} \
} while (0)
#define IDX32_INVALID(i, c, h) \
((((i) + 1) >> (h)) != 0)
#define IDX32_SET_ADDR_TREE(i, c, h, a, l) \
if ((c) <= 4) { \
(l) = ((i) & ((1 << (h)) - 1)); \
(i) >>= params->sub_h; \
(a)[XMSS_ADDR_TREE] = (i); \
}
#endif
#if (WOLFSSL_XMSS_MAX_HEIGHT > 32) && (WOLFSSL_XMSS_MIN_HEIGHT <= 32)
#define WC_IDX_DECODE(idx, c, a, ret) \
do { \
IDX64_DECODE((idx).u64, c, a, ret) \
else \
IDX32_DECODE((idx).u32, c, a, ret) \
else { \
(ret) = NOT_COMPILED_IN; \
} \
} while (0)
#define WC_IDX_INVALID(i, c, h) \
((((c) > 4) && IDX64_INVALID((i).u64, c, h)) || \
(((c) <= 4) && IDX32_INVALID((i).u32, c, h)))
#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \
do { \
IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l) \
else \
IDX32_SET_ADDR_TREE((idx).u32, c, h, a, l) \
} while (0)
#elif WOLFSSL_XMSS_MAX_HEIGHT > 32
#define WC_IDX_DECODE(idx, c, a, ret) \
do { \
IDX64_DECODE((idx).u64, c, a, ret) \
} while (0)
#define WC_IDX_INVALID(i, c, h) \
IDX64_INVALID((i).u64, c, h)
#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \
do { \
IDX64_SET_ADDR_TREE((idx).u64, c, h, a, l) \
} while (0)
#else
#define WC_IDX_DECODE(idx, c, a, ret) \
do { \
IDX32_DECODE((idx).u32, c, a, ret) \
else { \
(ret) = NOT_COMPILED_IN; \
} \
} while (0)
#define WC_IDX_INVALID(i, c, h) \
IDX32_INVALID((i).u32, c, h)
#define WC_IDX_SET_ADDR_TREE(idx, c, h, a, l) \
do { \
IDX32_SET_ADDR_TREE(idx.u32, c, h, a, l) \
} while (0)
#endif
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
static void wc_idx_update(unsigned char* a, word8 l)
{
sword8 i;
for (i = l - 1; i >= 0; i--) {
if ((++a[i]) != 0) {
break;
}
}
}
static void wc_idx_copy(const unsigned char* s, word8 sl, unsigned char* d,
word8 dl)
{
XMEMSET(d, 0, dl - sl);
XMEMCPY(d + dl - sl, s, sl);
}
#endif
#define XMSS_ADDR_SET_SUBTREE(a, s, t) \
do { \
(a)[XMSS_ADDR_LAYER] = (s)[XMSS_ADDR_LAYER]; \
(a)[XMSS_ADDR_TREE_HI] = (s)[XMSS_ADDR_TREE_HI]; \
(a)[XMSS_ADDR_TREE] = (s)[XMSS_ADDR_TREE]; \
(a)[XMSS_ADDR_TYPE] = (t); \
XMEMSET((a) + 4, 0, sizeof(a) - 4 * sizeof(*(a)));\
} while (0)
#define XMSS_ADDR_OTS_SET_SUBTREE(a, s) \
XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_OTS)
#define XMSS_ADDR_LTREE_SET_SUBTREE(a, s) \
XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_LTREE)
#define XMSS_ADDR_TREE_SET_SUBTREE(a, s) \
XMSS_ADDR_SET_SUBTREE(a, s, WC_XMSS_ADDR_TYPE_TREE)
#ifdef LITTLE_ENDIAN_ORDER
#define XMSS_ADDR_SET_BYTE(a, i, b) \
((word32*)(a))[i] = (word32)(b) << 24
#else
#define XMSS_ADDR_SET_BYTE(a, i, b) \
((word32*)(a))[i] = (b)
#endif
static void wc_xmss_addr_encode(const HashAddress addr, byte* bytes)
{
c32toa((addr)[0], (bytes) + (0 * 4));
c32toa((addr)[1], (bytes) + (1 * 4));
c32toa((addr)[2], (bytes) + (2 * 4));
c32toa((addr)[3], (bytes) + (3 * 4));
c32toa((addr)[4], (bytes) + (4 * 4));
c32toa((addr)[5], (bytes) + (5 * 4));
c32toa((addr)[6], (bytes) + (6 * 4));
c32toa((addr)[7], (bytes) + (7 * 4));
}
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
!defined(WC_XMSS_FULL_HASH)
#define XMSS_SHA256_SET_DATA(state, data, len, total_len) \
do { \
XMEMCPY((state)->digest.sha256.buffer, data, len); \
(state)->digest.sha256.buffLen = (len); \
(state)->digest.sha256.loLen = (total_len); \
} while (0)
#define XMSS_SHA256_STATE_CACHE(state) \
(state)->dgst_state[0] = (state)->digest.sha256.digest[0]; \
(state)->dgst_state[1] = (state)->digest.sha256.digest[1]; \
(state)->dgst_state[2] = (state)->digest.sha256.digest[2]; \
(state)->dgst_state[3] = (state)->digest.sha256.digest[3]; \
(state)->dgst_state[4] = (state)->digest.sha256.digest[4]; \
(state)->dgst_state[5] = (state)->digest.sha256.digest[5]; \
(state)->dgst_state[6] = (state)->digest.sha256.digest[6]; \
(state)->dgst_state[7] = (state)->digest.sha256.digest[7]; \
#define XMSS_SHA256_STATE_RESTORE(state, len) \
do { \
(state)->digest.sha256.digest[0] = (state)->dgst_state[0]; \
(state)->digest.sha256.digest[1] = (state)->dgst_state[1]; \
(state)->digest.sha256.digest[2] = (state)->dgst_state[2]; \
(state)->digest.sha256.digest[3] = (state)->dgst_state[3]; \
(state)->digest.sha256.digest[4] = (state)->dgst_state[4]; \
(state)->digest.sha256.digest[5] = (state)->dgst_state[5]; \
(state)->digest.sha256.digest[6] = (state)->dgst_state[6]; \
(state)->digest.sha256.digest[7] = (state)->dgst_state[7]; \
(state)->digest.sha256.loLen = (len); \
} while (0)
#define XMSS_SHA256_STATE_RESTORE_DATA(state, data, len, total_len) \
do { \
(state)->digest.sha256.digest[0] = (state)->dgst_state[0]; \
(state)->digest.sha256.digest[1] = (state)->dgst_state[1]; \
(state)->digest.sha256.digest[2] = (state)->dgst_state[2]; \
(state)->digest.sha256.digest[3] = (state)->dgst_state[3]; \
(state)->digest.sha256.digest[4] = (state)->dgst_state[4]; \
(state)->digest.sha256.digest[5] = (state)->dgst_state[5]; \
(state)->digest.sha256.digest[6] = (state)->dgst_state[6]; \
(state)->digest.sha256.digest[7] = (state)->dgst_state[7]; \
XMSS_SHA256_SET_DATA(state, data, len, total_len); \
} while (0)
#endif
static WC_INLINE void wc_xmss_hash(XmssState* state, const byte* in,
word32 inlen, byte* out)
{
int ret;
const XmssParams* params = state->params;
#ifdef WC_XMSS_SHA256
if ((params->hash == WC_HASH_TYPE_SHA256) &&
(params->n == WC_SHA256_DIGEST_SIZE)) {
ret = wc_Sha256Update(&state->digest.sha256, in, inlen);
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, out);
}
}
#if WOLFSSL_WC_XMSS_MIN_HASH_SIZE <= 192 && WOLFSSL_WC_XMSS_MAX_HASH_SIZE >= 192
else if (params->hash == WC_HASH_TYPE_SHA256) {
byte buf[WC_SHA256_DIGEST_SIZE];
ret = wc_Sha256Update(&state->digest.sha256, in, inlen);
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, buf);
}
if (ret == 0) {
XMEMCPY(out, buf, params->n);
}
}
#endif
else
#endif
#ifdef WC_XMSS_SHA512
if (params->hash == WC_HASH_TYPE_SHA512) {
ret = wc_Sha512Update(&state->digest.sha512, in, inlen);
if (ret == 0) {
ret = wc_Sha512Final(&state->digest.sha512, out);
}
}
else
#endif
#ifdef WC_XMSS_SHAKE128
if (params->hash == WC_HASH_TYPE_SHAKE128) {
ret = wc_Shake128_Update(&state->digest.shake, in, inlen);
if (ret == 0) {
ret = wc_Shake128_Final(&state->digest.shake, out, params->n);
}
}
else
#endif
#ifdef WC_XMSS_SHAKE256
if (params->hash == WC_HASH_TYPE_SHAKE256) {
ret = wc_Shake256_Update(&state->digest.shake, in, inlen);
if (ret == 0) {
ret = wc_Shake256_Final(&state->digest.shake, out, params->n);
}
}
else
#endif
{
ret = NOT_COMPILED_IN;
}
if (state->ret == 0) {
state->ret = ret;
}
}
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
#ifndef WC_XMSS_FULL_HASH
static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp,
byte* addr, byte* hash)
{
byte* pad = state->buf;
byte* key = pad + XMSS_SHA256_32_PAD_LEN;
byte* bm = key + XMSS_SHA256_32_N;
int ret;
((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, key);
if (ret == 0) {
addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm);
}
if (ret == 0) {
xorbuf(bm, tmp, XMSS_SHA256_32_N);
ret = wc_Sha256Update(&state->digest.sha256, state->buf,
XMSS_CHAIN_HASH_DATA_LEN_SHA256_32);
}
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, hash);
}
if (state->ret == 0) {
state->ret = ret;
}
}
#else
static void wc_xmss_chain_hash_sha256_32(XmssState* state, const byte* tmp,
byte* hash)
{
byte* addr = state->prf_buf + XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N;
byte* pad = state->buf;
byte* key = pad + XMSS_SHA256_32_PAD_LEN;
byte* bm = key + XMSS_SHA256_32_N;
((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm);
xorbuf(bm, tmp, XMSS_SHA256_32_N);
wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN_SHA256_32, hash);
}
#endif
#endif
static void wc_xmss_chain_hash(XmssState* state, const byte* tmp, byte* hash)
{
const XmssParams* params = state->params;
byte* addr = state->prf_buf + params->pad_len + params->n;
byte* pad = state->buf;
byte* key = pad + params->pad_len;
byte* bm = key + params->n;
((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), key);
addr[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN(params), bm);
xorbuf(bm, tmp, params->n);
wc_xmss_hash(state, state->buf, XMSS_CHAIN_HASH_DATA_LEN(params), hash);
}
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
#ifndef WC_XMSS_FULL_HASH
static void wc_xmss_rand_hash_sha256_32_prehash(XmssState* state,
const byte* data, HashAddress addr, byte* hash)
{
int ret;
byte* pad = state->buf;
byte* key = pad + XMSS_SHA256_32_PAD_LEN;
byte* bm0 = key + XMSS_SHA256_32_N;
byte* bm1 = bm0 + XMSS_SHA256_32_N;
byte addr_buf[WC_XMSS_ADDR_LEN];
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, key);
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm0);
}
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm1);
}
if (ret == 0) {
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
xorbuf(bm0, data, XMSS_SHA256_32_N * 2);
ret = wc_Sha256Update(&state->digest.sha256, state->buf,
XMSS_RAND_HASH_DATA_LEN_SHA256_32);
}
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, hash);
}
if (state->ret == 0) {
state->ret = ret;
}
}
#endif
static void wc_xmss_rand_hash_sha256_32(XmssState* state, const byte* data,
const byte* pk_seed, HashAddress addr, byte* hash)
{
byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN +
XMSS_SHA256_32_N;
byte* pad = state->buf;
byte* key = pad + XMSS_SHA256_32_PAD_LEN;
byte* bm0 = key + XMSS_SHA256_32_N;
byte* bm1 = bm0 + XMSS_SHA256_32_N;
#ifndef WC_XMSS_FULL_HASH
int ret;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
XMSS_SHA256_32_N);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
if (ret == 0) {
XMSS_SHA256_STATE_CACHE(state);
XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, key);
}
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm0);
}
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm1);
}
if (ret == 0) {
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
xorbuf(bm0, data, 2 * XMSS_SHA256_32_N);
ret = wc_Sha256Update(&state->digest.sha256, state->buf,
XMSS_RAND_HASH_DATA_LEN_SHA256_32);
}
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, hash);
}
if (state->ret == 0) {
state->ret = ret;
}
#else
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
XMSS_SHA256_32_N);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1);
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN);
xorbuf(bm0, data, 2 * XMSS_SHA256_32_N);
wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash);
#endif
}
#endif
static void wc_xmss_rand_hash(XmssState* state, const byte* data,
const byte* pk_seed, HashAddress addr, byte* hash)
{
const XmssParams* params = state->params;
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_rand_hash_sha256_32(state, data, pk_seed, addr, hash);
}
else
#endif
{
byte* addr_buf = state->prf_buf + params->pad_len + params->n;
byte* pad = state->buf;
byte* key = pad + params->pad_len;
byte* bm0 = key + params->n;
byte* bm1 = bm0 + params->n;
const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len);
XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
wc_xmss_hash(state, state->prf_buf, len, key);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, len, bm0);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
wc_xmss_hash(state, state->prf_buf, len, bm1);
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len);
xorbuf(bm0, data, 2 * params->n);
wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n,
hash);
}
}
#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY)
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
static void wc_xmss_rand_hash_lr_sha256_32(XmssState* state, const byte* left,
const byte* right, const byte* pk_seed, HashAddress addr, byte* hash)
{
byte* addr_buf = state->prf_buf + XMSS_SHA256_32_PAD_LEN +
XMSS_SHA256_32_N;
byte* pad = state->buf;
byte* key = pad + XMSS_SHA256_32_PAD_LEN;
byte* bm0 = key + XMSS_SHA256_32_N;
byte* bm1 = bm0 + XMSS_SHA256_32_N;
#ifndef WC_XMSS_FULL_HASH
int ret;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed,
XMSS_SHA256_32_N);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
if (ret == 0) {
XMSS_SHA256_STATE_CACHE(state);
XMSS_SHA256_SET_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, key);
}
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm0);
}
if (ret == 0) {
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
XMSS_SHA256_STATE_RESTORE_DATA(state, addr_buf, WC_XMSS_ADDR_LEN,
XMSS_HASH_PRF_DATA_LEN_SHA256_32);
ret = wc_Sha256Final(&state->digest.sha256, bm1);
}
if (ret == 0) {
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N);
xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N);
ret = wc_Sha256Update(&state->digest.sha256, state->buf,
XMSS_RAND_HASH_DATA_LEN_SHA256_32);
}
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, hash);
}
if (state->ret == 0) {
state->ret = ret;
}
#else
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_PAD_LEN, pk_seed, XMSS_SHA256_32_N);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, key);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm0);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
wc_xmss_hash(state, state->prf_buf, XMSS_HASH_PRF_DATA_LEN_SHA256_32, bm1);
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, state->buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(state->prf_buf, left, XMSS_SHA256_32_N);
XMEMCPY(state->prf_buf + XMSS_SHA256_32_N, right, XMSS_SHA256_32_N);
xorbuf(bm0, state->prf_buf, 2 * XMSS_SHA256_32_N);
wc_xmss_hash(state, state->buf, XMSS_RAND_HASH_DATA_LEN_SHA256_32, hash);
#endif
}
#endif
static void wc_xmss_rand_hash_lr(XmssState* state, const byte* left,
const byte* right, const byte* pk_seed, HashAddress addr, byte* hash)
{
const XmssParams* params = state->params;
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_rand_hash_lr_sha256_32(state, left, right, pk_seed, addr, hash);
}
else
#endif
{
byte* addr_buf = state->prf_buf + params->pad_len + params->n;
byte* pad = state->buf;
byte* key = pad + params->pad_len;
byte* bm0 = key + params->n;
byte* bm1 = bm0 + params->n;
const word32 len = params->pad_len + params->n + WC_XMSS_ADDR_LEN;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, state->prf_buf, params->pad_len);
XMEMCPY(state->prf_buf + params->pad_len, pk_seed, params->n);
addr[XMSS_ADDR_KEY_MASK] = 0;
wc_xmss_addr_encode(addr, addr_buf);
wc_xmss_hash(state, state->prf_buf, len, key);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 1;
wc_xmss_hash(state, state->prf_buf, len, bm0);
addr_buf[XMSS_ADDR_KEY_MASK * 4 + 3] = 2;
wc_xmss_hash(state, state->prf_buf, len, bm1);
XMSS_PAD_ENC(XMSS_HASH_PADDING_H, pad, params->pad_len);
XMEMCPY(state->prf_buf, left, params->n);
XMEMCPY(state->prf_buf + params->n, right, params->n);
xorbuf(bm0, state->prf_buf, 2 * params->n);
wc_xmss_hash(state, state->buf, params->pad_len + 3 * params->n,
hash);
}
}
#endif
static void wc_xmss_hash_message(XmssState* state, const byte* random,
const byte* root, const byte* idx, word8 idx_len, const byte* m,
word32 mlen, byte* hash)
{
int ret;
const XmssParams* params = state->params;
word32 padKeyLen = params->pad_len + 3 * params->n;
byte* padKey = state->buf;
byte* pad = padKey;
byte* key = pad + params->pad_len;
byte* root_sk = key + params->n;
byte* idx_sig = root_sk + params->n;
XMSS_PAD_ENC(XMSS_HASH_PADDING_HASH, pad, params->pad_len);
XMEMCPY(key, random, params->n);
XMEMCPY(root_sk, root, params->n);
XMEMSET(idx_sig, 0, params->n - idx_len);
XMEMCPY(idx_sig + params->n - idx_len, idx, idx_len);
#ifdef WC_XMSS_SHA256
if (params->hash == WC_HASH_TYPE_SHA256) {
ret = wc_Sha256Update(&state->digest.sha256, padKey, padKeyLen);
}
else
#endif
#ifdef WC_XMSS_SHA512
if (params->hash == WC_HASH_TYPE_SHA512) {
ret = wc_Sha512Update(&state->digest.sha512, padKey, padKeyLen);
}
else
#endif
#ifdef WC_XMSS_SHAKE128
if (params->hash == WC_HASH_TYPE_SHAKE128) {
ret = wc_Shake128_Update(&state->digest.shake, padKey, padKeyLen);
}
else
#endif
#ifdef WC_XMSS_SHAKE256
if (params->hash == WC_HASH_TYPE_SHAKE256) {
ret = wc_Shake256_Update(&state->digest.shake, padKey, padKeyLen);
}
else
#endif
{
ret = NOT_COMPILED_IN;
}
if (ret == 0) {
wc_xmss_hash(state, m, mlen, hash);
}
else if (state->ret == 0) {
state->ret = ret;
}
}
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
static void wc_xmss_prf(XmssState* state, const byte* key, const byte* m,
byte* prf)
{
const XmssParams* params = state->params;
byte* pad = state->prf_buf;
byte* key_buf = pad + params->pad_len;
byte* m_buf = key_buf + params->n;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len);
XMEMCPY(key_buf, key, params->n);
XMEMCPY(m_buf, m, XMSS_PRF_M_LEN);
wc_xmss_hash(state, state->prf_buf, params->pad_len + params->n +
XMSS_PRF_M_LEN, prf);
}
#ifdef XMSS_CALL_PRF_KEYGEN
static void wc_xmss_prf_keygen(XmssState* state, const byte* key,
const byte* m, byte* prf)
{
const XmssParams* params = state->params;
byte* pad = state->prf_buf;
byte* key_buf = pad + params->pad_len;
byte* m_buf = key_buf + params->n;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len);
XMEMCPY(key_buf, key, params->n);
XMEMCPY(m_buf, m, params->n + XMSS_PRF_M_LEN);
wc_xmss_hash(state, state->prf_buf, params->pad_len + 2 * params->n +
XMSS_PRF_M_LEN, prf);
}
#endif
#endif
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
static void wc_xmss_wots_get_wots_sk_sha256_32(XmssState* state,
const byte* sk_seed, const byte* pk_seed, byte* addr, byte* gen_seed)
{
const XmssParams* params = state->params;
word32 i;
byte* pad = state->prf_buf;
byte* s_xmss = pad + XMSS_SHA256_32_PAD_LEN;
byte* seed = s_xmss + XMSS_SHA256_32_N;
byte* addr_buf = seed + XMSS_SHA256_32_N;
int ret;
((word32*)addr)[XMSS_ADDR_CHAIN] = 0;
((word32*)addr)[XMSS_ADDR_HASH] = 0;
((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(s_xmss, sk_seed, XMSS_SHA256_32_N);
XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N);
XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
#ifndef WC_XMSS_FULL_HASH
ret = wc_Sha256Update(&state->digest.sha256, pad, XMSS_SHA256_32_PAD_LEN +
XMSS_SHA256_32_N);
if (ret == 0) {
XMSS_SHA256_STATE_CACHE(state);
ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N +
WC_XMSS_ADDR_LEN);
}
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
}
for (i = 1; (ret == 0) && (i < params->wots_len); i++) {
gen_seed += XMSS_SHA256_32_N;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
XMSS_SHA256_STATE_RESTORE(state, 64);
ret = wc_Sha256Update(&state->digest.sha256, seed, XMSS_SHA256_32_N +
WC_XMSS_ADDR_LEN);
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
}
}
#else
ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN);
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
}
for (i = 1; (ret == 0) && i < params->wots_len; i++) {
gen_seed += XMSS_SHA256_32_N;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
XMSS_SHA256_32_PAD_LEN + 2 * XMSS_SHA256_32_N + WC_XMSS_ADDR_LEN);
if (ret == 0) {
ret = wc_Sha256Final(&state->digest.sha256, gen_seed);
}
}
#endif
if (state->ret == 0) {
state->ret = ret;
}
}
#endif
static void wc_xmss_wots_get_wots_sk(XmssState* state, const byte* sk_seed,
const byte* pk_seed, byte* addr, byte* gen_seed)
{
const XmssParams* params = state->params;
word32 i;
#ifdef XMSS_CALL_PRF_KEYGEN
byte* seed = state->buf;
byte* addr_buf = seed + params->n;
#else
byte* pad = state->prf_buf;
byte* s_xmss = pad + params->pad_len;
byte* seed = s_xmss + params->n;
byte* addr_buf = seed + params->n;
const word32 len = params->pad_len + params->n * 2 + WC_XMSS_ADDR_LEN;
#endif
((word32*)addr)[XMSS_ADDR_CHAIN] = 0;
((word32*)addr)[XMSS_ADDR_HASH] = 0;
((word32*)addr)[XMSS_ADDR_KEY_MASK] = 0;
#ifdef XMSS_CALL_PRF_KEYGEN
XMEMCPY(seed, pk_seed, params->n);
XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed);
for (i = 1; i < params->wots_len; i++) {
gen_seed += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_prf_keygen(state, sk_seed, state->buf, gen_seed);
}
#else
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF_KEYGEN, pad, params->pad_len);
XMEMCPY(s_xmss, sk_seed, params->n);
XMEMCPY(seed, pk_seed, params->n);
XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
wc_xmss_hash(state, state->prf_buf, len, gen_seed);
for (i = 1; i < params->wots_len; i++) {
gen_seed += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_hash(state, state->prf_buf, len, gen_seed);
}
#endif
}
#endif
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
static void wc_xmss_chain_sha256_32(XmssState* state, const byte* data,
unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr,
byte* hash)
{
if (steps > 0) {
word32 i;
byte* pad = state->prf_buf;
byte* seed = pad + XMSS_SHA256_32_PAD_LEN;
#ifndef WC_XMSS_FULL_HASH
int ret;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(seed, pk_seed, XMSS_SHA256_32_N);
ret = wc_Sha256Update(&state->digest.sha256, state->prf_buf,
XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
if (ret == 0) {
XMSS_SHA256_STATE_CACHE(state);
XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf,
state->params->pad_len);
XMSS_ADDR_SET_BYTE(addr, XMSS_ADDR_HASH, start);
wc_xmss_chain_hash_sha256_32(state, data, addr, hash);
for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
addr[XMSS_ADDR_HASH * 4 + 3] = i;
wc_xmss_chain_hash_sha256_32(state, hash, addr, hash);
}
}
else if (state->ret == 0) {
state->ret = ret;
}
#else
const XmssParams* params = state->params;
byte* addr_buf = seed + XMSS_SHA256_32_N;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(seed, pk_seed, params->n);
XMEMCPY(addr_buf, addr, WC_XMSS_ADDR_LEN);
XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len);
XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start);
wc_xmss_chain_hash_sha256_32(state, data, hash);
for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
addr_buf[XMSS_ADDR_HASH * 4 + 3] = i;
wc_xmss_chain_hash_sha256_32(state, hash, hash);
}
#endif
}
else if (hash != data) {
XMEMCPY(hash, data, XMSS_SHA256_32_N);
}
}
#endif
static void wc_xmss_chain(XmssState* state, const byte* data,
unsigned int start, unsigned int steps, const byte* pk_seed, byte* addr,
byte* hash)
{
const XmssParams* params = state->params;
if (steps > 0) {
word32 i;
byte* pad = state->prf_buf;
byte* seed = pad + params->pad_len;
byte* addr_buf = seed + params->n;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, pad, params->pad_len);
XMEMCPY(seed, pk_seed, params->n);
XMEMCPY(addr_buf, addr, 32);
XMSS_PAD_ENC(XMSS_HASH_PADDING_F, state->buf, params->pad_len);
XMSS_ADDR_SET_BYTE(addr_buf, XMSS_ADDR_HASH, start);
wc_xmss_chain_hash(state, data, hash);
for (i = start+1; i < (start+steps) && i < XMSS_WOTS_W; i++) {
addr_buf[XMSS_ADDR_HASH * 4 + 3] = i;
wc_xmss_chain_hash(state, hash, hash);
}
}
else if (hash != data) {
XMEMCPY(hash, data, params->n);
}
}
static void wc_xmss_msg_convert(const byte* m, word8 n, word8* msg)
{
word8 i;
word16 csum = 0;
for (i = 0; i < n; i++) {
msg[0] = m[i] >> 4;
msg[1] = m[i] & 0xf;
csum += XMSS_WOTS_W - 1 - msg[0];
csum += XMSS_WOTS_W - 1 - msg[1];
msg += 2;
}
msg[0] = (csum >> 8) ;
msg[1] = (csum >> 4) & 0x0f;
msg[2] = (csum ) & 0x0f;
}
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
static void wc_xmss_wots_gen_pk(XmssState* state, const byte* sk,
const byte* seed, HashAddress addr, byte* pk)
{
const XmssParams* params = state->params;
byte* addr_buf = state->encMsg;
word32 i;
addr[XMSS_ADDR_CHAIN] = 0;
wc_xmss_addr_encode(addr, addr_buf);
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf,
pk);
wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf,
pk);
for (i = 1; i < params->wots_len; i++) {
pk += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain_sha256_32(state, pk, 0, XMSS_WOTS_W - 1, seed,
addr_buf, pk);
}
}
else
#endif
{
wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, pk);
wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk);
for (i = 1; i < params->wots_len; i++) {
pk += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain(state, pk, 0, XMSS_WOTS_W - 1, seed, addr_buf, pk);
}
}
}
static void wc_xmss_wots_sign(XmssState* state, const byte* m,
const byte* sk, const byte* seed, HashAddress addr, byte* sig)
{
const XmssParams* params = state->params;
byte* addr_buf = state->pk;
word32 i;
wc_xmss_msg_convert(m, params->n, state->encMsg);
addr[XMSS_ADDR_CHAIN] = 0;
wc_xmss_addr_encode(addr, addr_buf);
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_wots_get_wots_sk_sha256_32(state, sk, seed, addr_buf, sig);
wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[0], seed, addr_buf,
sig);
for (i = 1; i < params->wots_len; i++) {
sig += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain_sha256_32(state, sig, 0, state->encMsg[i], seed,
addr_buf, sig);
}
}
else
#endif
{
wc_xmss_wots_get_wots_sk(state, sk, seed, addr_buf, sig);
wc_xmss_chain(state, sig, 0, state->encMsg[0], seed, addr_buf, sig);
for (i = 1; i < params->wots_len; i++) {
sig += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain(state, sig, 0, state->encMsg[i], seed, addr_buf, sig);
}
}
}
#endif
static void wc_xmss_wots_pk_from_sig(XmssState* state, const byte* sig,
const byte* m, const byte* seed, HashAddress addr, byte* pk)
{
const XmssParams* params = state->params;
byte* addr_buf = state->stack;
word32 i;
wc_xmss_msg_convert(m, params->n, state->encMsg);
addr[XMSS_ADDR_CHAIN] = 0;
wc_xmss_addr_encode(addr, addr_buf);
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_chain_sha256_32(state, sig, state->encMsg[0],
XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk);
for (i = 1; i < params->wots_len; i++) {
sig += params->n;
pk += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain_sha256_32(state, sig, state->encMsg[i],
XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk);
}
}
else
#endif
{
wc_xmss_chain(state, sig, state->encMsg[0],
XMSS_WOTS_W - 1 - state->encMsg[0], seed, addr_buf, pk);
for (i = 1; i < params->wots_len; i++) {
sig += params->n;
pk += params->n;
addr_buf[XMSS_ADDR_CHAIN * 4 + 3] = i;
wc_xmss_chain(state, sig, state->encMsg[i],
XMSS_WOTS_W - 1 - state->encMsg[i], seed, addr_buf, pk);
}
}
}
static void wc_xmss_ltree(XmssState* state, byte* pk, const byte* seed,
HashAddress addr, byte* pk0)
{
const XmssParams* params = state->params;
word8 len = params->wots_len;
word32 h = 0;
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
!defined(WC_XMSS_FULL_HASH)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
byte* prf_buf = state->prf_buf;
int ret;
XMSS_PAD_ENC(XMSS_HASH_PADDING_PRF, prf_buf, XMSS_SHA256_32_PAD_LEN);
XMEMCPY(prf_buf + XMSS_SHA256_32_PAD_LEN, seed, XMSS_SHA256_32_N);
ret = wc_Sha256Update(&state->digest.sha256, prf_buf,
XMSS_SHA256_32_PAD_LEN + XMSS_SHA256_32_N);
if (ret == 0) {
XMSS_SHA256_STATE_CACHE(state);
}
else if (state->ret == 0) {
state->ret = ret;
}
}
#endif
while (len > 1) {
word8 i;
word8 len2 = len >> 1;
addr[XMSS_ADDR_TREE_HEIGHT] = h++;
for (i = 0; i < len2; i++) {
addr[XMSS_ADDR_TREE_INDEX] = i;
#if !defined(WOLFSSL_WC_XMSS_SMALL) && defined(WC_XMSS_SHA256) && \
!defined(WC_XMSS_FULL_HASH)
if ((params->pad_len == XMSS_SHA256_32_PAD_LEN) &&
(params->n == XMSS_SHA256_32_N) &&
(params->hash == WC_HASH_TYPE_SHA256)) {
wc_xmss_rand_hash_sha256_32_prehash(state,
pk + i * 2 * XMSS_SHA256_32_N, addr,
pk + i * XMSS_SHA256_32_N);
}
else
#endif
{
wc_xmss_rand_hash(state, pk + i * 2 * params->n,
seed, addr, pk + i * params->n);
}
}
if (len & 1) {
XMEMCPY(pk + len2 * params->n, pk + (len - 1) * params->n,
params->n);
}
len = len2 + (len & 1);
}
XMEMCPY(pk0, pk, params->n);
}
#ifndef WOLFSSL_XMSS_VERIFY_ONLY
#ifdef WOLFSSL_WC_XMSS_SMALL
#ifndef WOLFSSL_SMALL_STACK
static void wc_xmss_treehash(XmssState* state, const byte* sk_seed,
const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root,
byte* auth_path)
{
const XmssParams* params = state->params;
const word8 n = params->n;
byte* node = state->stack;
HashAddress ots;
HashAddress ltree;
HashAddress tree;
word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
word8 offset = 0;
word32 max = (word32)1 << params->sub_h;
word32 i;
XMSS_ADDR_OTS_SET_SUBTREE(ots, subtree);
XMSS_ADDR_LTREE_SET_SUBTREE(ltree, subtree);
XMSS_ADDR_TREE_SET_SUBTREE(tree, subtree);
for (i = 0; i < max; i++) {
word8 h;
ots[XMSS_ADDR_OTS] = i;
wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, ots, state->pk);
ltree[XMSS_ADDR_LTREE] = i;
wc_xmss_ltree(state, state->pk, pk_seed, ltree, node);
h = height[offset] = 0;
if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) {
XMEMCPY(auth_path, node, n);
}
while ((offset >= 1) && (h == height[offset - 1])) {
word32 tree_idx = i >> (h + 1);
node -= n;
tree[XMSS_ADDR_TREE_HEIGHT] = h;
tree[XMSS_ADDR_TREE_INDEX] = tree_idx;
wc_xmss_rand_hash(state, node, pk_seed, tree, node);
offset--;
h = ++height[offset];
if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) {
XMEMCPY(auth_path + h * n, node, n);
}
}
offset++;
node += n;
}
XMEMCPY(root, state->stack, n);
}
#else
static void wc_xmss_treehash(XmssState* state, const byte* sk_seed,
const byte* pk_seed, word32 leafIdx, const word32* subtree, byte* root,
byte* auth_path)
{
const XmssParams* params = state->params;
const word8 n = params->n;
byte* node = state->stack;
HashAddress addr;
word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
word8 offset = 0;
word32 max = (word32)1 << params->sub_h;
word32 i;
XMSS_ADDR_SET_SUBTREE(addr, subtree, 0);
for (i = 0; i < max; i++) {
word8 h;
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
addr[XMSS_ADDR_LTREE] = i;
wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
wc_xmss_ltree(state, state->pk, pk_seed, addr, node);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
addr[XMSS_ADDR_TREE_ZERO] = 0;
h = height[offset] = 0;
if ((auth_path != NULL) && ((leafIdx ^ 0x1) == i)) {
XMEMCPY(auth_path, node, n);
}
while ((offset >= 1) && (h == height[offset - 1])) {
word32 tree_idx = i >> (h + 1);
node -= n;
addr[XMSS_ADDR_TREE_HEIGHT] = h;
addr[XMSS_ADDR_TREE_INDEX] = tree_idx;
wc_xmss_rand_hash(state, node, pk_seed, addr, node);
offset--;
h = ++height[offset];
if ((auth_path != NULL) && (((leafIdx >> h) ^ 0x1) == tree_idx)) {
XMEMCPY(auth_path + h * n, node, n);
}
}
offset++;
node += n;
addr[XMSS_ADDR_TREE_HEIGHT] = 0;
addr[XMSS_ADDR_TREE_INDEX] = 0;
}
XMEMCPY(root, state->stack, n);
}
#endif
int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed,
unsigned char* sk, unsigned char* pk)
{
const XmssParams* params = state->params;
const word8 n = params->n;
const byte* seed_priv = seed;
const byte* seed_pub = seed + 2 * n;
byte* sk_idx = sk;
byte* sk_seed = sk_idx + params->idx_len;
byte* sk_pub = sk_seed + 2 * n;
byte* pk_root = pk;
byte* pk_seed = pk_root + n;
XMEMSET(sk_idx, 0, params->idx_len);
XMEMCPY(sk_seed, seed_priv, 2 * n);
XMEMCPY(pk_seed, seed_pub, n);
XMEMSET(state->addr, 0, sizeof(HashAddress));
state->addr[XMSS_ADDR_LAYER] = params->d - 1;
wc_xmss_treehash(state, sk_seed, pk_seed, 0, state->addr, pk_root, NULL);
XMEMCPY(sk_pub, pk_root, 2 * n);
return state->ret;
}
int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen,
unsigned char* sk, unsigned char* sig)
{
int ret = 0;
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 hs = params->sub_h;
const word16 hsn = (word16)hs * n;
const byte* sk_seed = sk + params->idx_len;
const byte* pk_seed = sk + params->idx_len + 3 * n;
wc_Idx idx;
byte* sig_r = sig + params->idx_len;
byte root[WC_XMSS_MAX_N];
unsigned int i;
WC_IDX_ZERO(idx);
XMEMSET(state->addr, 0, sizeof(HashAddress));
state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
XMEMCPY(sig, sk, params->idx_len);
WC_IDX_DECODE(idx, params->idx_len, sk, ret);
if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) {
XMEMSET(sk, 0xFF, params->idx_len);
ForceZero(sk + params->idx_len, params->sk_len - params->idx_len);
ret = KEY_EXHAUSTED_E;
}
if (ret == 0) {
wc_idx_update(sk, params->idx_len);
}
if (ret == 0) {
const byte* sk_prf = sk + params->idx_len + n;
wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN);
wc_xmss_prf(state, sk_prf, state->buf, sig_r);
ret = state->ret;
}
if (ret == 0) {
const byte* pub_root = sk + params->idx_len + 2 * n;
wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m,
mlen, root);
ret = state->ret;
sig += params->idx_len + n;
}
for (i = 0; (ret == 0) && (i < params->d); i++) {
word32 idx_leaf = 0;
state->addr[XMSS_ADDR_LAYER] = i;
WC_IDX_SET_ADDR_TREE(idx, params->idx_len, hs, state->addr, idx_leaf);
state->addr[XMSS_ADDR_OTS] = idx_leaf;
wc_xmss_wots_sign(state, root, sk_seed, pk_seed, state->addr, sig);
ret = state->ret;
if (ret == 0) {
sig += params->wots_sig_len;
wc_xmss_treehash(state, sk_seed, pk_seed, idx_leaf, state->addr,
root, sig);
ret = state->ret;
sig += hsn;
}
}
return ret;
}
#else
typedef struct TreeHash {
word32 nextIdx;
word8 used;
word8 completed;
} TreeHash;
typedef struct BdsState {
byte* stack;
byte* height;
byte* authPath;
byte* keep;
byte* treeHash;
byte* treeHashNode;
byte* retain;
word32 next;
word8 offset;
} BdsState;
#define BDS_IDX(idx, i, hs, d) \
(((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? (i) : ((d) + (i)))
#define BDS_ALT_IDX(idx, i, hs, d) \
(((((idx) >> ((hs) * ((i) + 1))) & 1) == 0) ? ((d) + (i)) : (i))
static void wc_xmss_bds_state_treehash_init(BdsState* bds, int i)
{
byte* sk = bds->treeHash + i * 4;
c32to24(0, sk);
sk[3] = 0 | (1 << 7);
}
static void wc_xmss_bds_state_treehash_set_next_idx(BdsState* bds, int i,
word32 nextIdx)
{
byte* sk = bds->treeHash + i * 4;
c32to24(nextIdx, sk);
sk[3] = 0 | (0 << 7);
}
static void wc_xmss_bds_state_treehash_complete(BdsState* bds, int i)
{
byte* sk = bds->treeHash + i * 4;
sk[3] |= 1 << 7;
}
static void wc_xmss_bds_state_treehash_get(BdsState* bds, int i,
TreeHash* treeHash)
{
byte* sk = bds->treeHash + i * 4;
ato24(sk, &treeHash->nextIdx);
treeHash->used = sk[3] & 0x7f;
treeHash->completed = sk[3] >> 7;
}
static void wc_xmss_bds_state_treehash_set(BdsState* bds, int i,
TreeHash* treeHash)
{
byte* sk = bds->treeHash + i * 4;
c32to24(treeHash->nextIdx, sk);
sk[3] = treeHash->used | (treeHash->completed << 7);
}
static int wc_xmss_bds_state_alloc(const XmssParams* params, BdsState** bds)
{
const word8 cnt = 2 * params->d - 1;
int ret = 0;
if (*bds == NULL) {
*bds = (BdsState*)XMALLOC(sizeof(BdsState) * cnt, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (*bds == NULL) {
ret = MEMORY_E;
}
else {
XMEMSET(*bds, 0, sizeof(BdsState) * cnt);
}
}
return ret;
}
static void wc_xmss_bds_state_free(BdsState* bds)
{
XFREE(bds, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
static int wc_xmss_bds_state_load(const XmssState* state, byte* sk,
BdsState* bds, byte** wots_sigs)
{
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
const word8 k = params->bds_k;
const word32 retainLen = XMSS_RETAIN_LEN(k, n);
int i;
sk += params->idx_len + 4 * n;
if (2 * (int)params->d - 1 <= 0)
return WC_FAILURE;
for (i = 0; i < 2 * (int)params->d - 1; i++) {
bds[i].stack = sk;
sk += (hs + 1) * n;
bds[i].height = sk;
sk += hs + 1;
bds[i].authPath = sk;
sk += hs * n;
bds[i].keep = sk;
sk += (hs >> 1) * n;
bds[i].treeHash = sk;
sk += hsk * 4;
bds[i].treeHashNode = sk;
sk += hsk * n;
bds[i].retain = sk;
sk += retainLen;
ato24(sk, &bds[i].next);
sk += 3;
bds[i].offset = sk[0];
sk += 1;
}
if (wots_sigs != NULL) {
*wots_sigs = sk;
}
return 0;
}
static int wc_xmss_bds_state_store(const XmssState* state, byte* sk,
BdsState* bds)
{
int i;
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
const word8 k = params->bds_k;
const word32 skip = (hs + 1) * n +
hs + 1 +
hs * n +
(hs >> 1) * n +
hsk * 4 +
hsk * n +
XMSS_RETAIN_LEN(k, n);
sk += params->idx_len + 4 * n;
if (2 * (int)params->d - 1 <= 0)
return WC_FAILURE;
for (i = 0; i < 2 * (int)params->d - 1; i++) {
sk += skip;
c32to24(bds[i].next, sk);
sk += 3;
sk[0] = bds[i].offset;
sk += 1;
}
return 0;
}
static void wc_xmss_bds_next_idx(XmssState* state, BdsState* bds,
const byte* sk_seed, const byte* pk_seed, HashAddress addr, int i,
word8* height, word8* offset, word8** sp)
{
const XmssParams* params = state->params;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
const word8 n = params->n;
word8 o = *offset;
word8* node = *sp;
word8 h;
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
addr[XMSS_ADDR_OTS] = i;
wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
wc_xmss_ltree(state, state->pk, pk_seed, addr, node);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
addr[XMSS_ADDR_TREE_ZERO] = 0;
h = height[o] = 0;
if ((hsk > 0) && (i == 3)) {
XMEMCPY(bds->treeHashNode, node + n, n);
}
while ((o >= 1) && (h == height[o - 1])) {
if ((i >> h) == 1) {
if (bds->authPath == NULL) {
state->ret = WC_FAILURE;
return;
}
XMEMCPY(bds->authPath + h * n, node, n);
}
else if (h < hsk) {
if ((i >> h) == 3) {
XMEMCPY(bds->treeHashNode + h * n, node, n);
}
}
else {
word32 ro = (1 << (hs - 1 - h)) + h - hs + (((i >> h) - 3) >> 1);
XMEMCPY(bds->retain + ro * n, node, n);
}
node -= n;
addr[XMSS_ADDR_TREE_HEIGHT] = h;
addr[XMSS_ADDR_TREE_INDEX] = i >> (h + 1);
wc_xmss_rand_hash(state, node, pk_seed, addr, node);
o--;
h = ++height[o];
}
*offset = o;
*sp = node;
}
static void wc_xmss_bds_treehash_initial(XmssState* state, BdsState* bds,
const byte* sk_seed, const byte* pk_seed, const HashAddress addr,
byte* root)
{
const XmssParams* params = state->params;
const word8 hsk = params->sub_h - params->bds_k;
const word8 n = params->n;
word8* node = state->stack;
HashAddress addrCopy;
word8 height[WC_XMSS_MAX_TREE_HEIGHT + 1];
word8 offset = 0;
word32 maxIdx = (word32)1 << params->sub_h;
word32 i;
bds->offset = 0;
bds->next = 0;
if (bds->treeHash != NULL) {
for (i = 0; i < hsk; i++) {
wc_xmss_bds_state_treehash_init(bds, i);
}
}
XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr);
for (i = 0; i < maxIdx; i++) {
wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, i, height,
&offset, &node);
offset++;
node += n;
addrCopy[XMSS_ADDR_TREE_HEIGHT] = 0;
addrCopy[XMSS_ADDR_TREE_INDEX] = 0;
}
XMEMCPY(root, state->stack, n);
}
static void wc_xmss_bds_treehash_update(XmssState* state, BdsState* bds,
word8 height, const byte* sk_seed, const byte* pk_seed,
const HashAddress addr)
{
const XmssParams* params = state->params;
const word8 n = params->n;
HashAddress addrLocal;
TreeHash treeHash[1];
byte* sp = bds->stack + bds->offset * n;
byte* node = state->stack + WC_XMSS_MAX_STACK_LEN - n;
word8 h;
wc_xmss_bds_state_treehash_get(bds, height, treeHash);
XMSS_ADDR_OTS_SET_SUBTREE(addrLocal, addr);
addrLocal[XMSS_ADDR_OTS] = treeHash->nextIdx;
wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addrLocal, state->pk);
addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
wc_xmss_ltree(state, state->pk, pk_seed, addrLocal, node);
addrLocal[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
addrLocal[XMSS_ADDR_TREE_ZERO] = 0;
h = 0;
while ((treeHash->used > 0) && (h == bds->height[bds->offset - 1])) {
sp -= n;
node -= n;
XMEMCPY(node, sp, n);
addrLocal[XMSS_ADDR_TREE_HEIGHT] = h;
addrLocal[XMSS_ADDR_TREE_INDEX] = treeHash->nextIdx >> (h + 1);
wc_xmss_rand_hash(state, node, pk_seed, addrLocal, node);
treeHash->used--;
bds->offset--;
h++;
}
if (h == height) {
XMEMCPY(bds->treeHashNode + height * n, node, n);
treeHash->completed = 1;
}
else {
XMEMCPY(sp, node, n);
treeHash->used++;
bds->height[bds->offset] = h;
bds->offset++;
treeHash->nextIdx++;
}
wc_xmss_bds_state_treehash_set(bds, height, treeHash);
}
static word8 wc_xmss_bds_treehash_updates(XmssState* state, BdsState* bds,
word8 updates, const byte* sk_seed, const byte* pk_seed,
const HashAddress addr)
{
const XmssParams* params = state->params;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
if (bds->treeHash == NULL) {
state->ret = WC_FAILURE;
return 0;
}
while (updates > 0) {
word8 minH = hs;
word8 h = hsk;
word8 i;
for (i = 0; i < hsk; i++) {
TreeHash treeHash[1];
wc_xmss_bds_state_treehash_get(bds, i, treeHash);
if (treeHash->completed) {
}
else if (treeHash->used == 0) {
if (i < minH) {
h = i;
minH = i;
}
}
else {
word8 j;
word8 lowH = hs;
byte* height = bds->height + bds->offset - treeHash->used;
for (j = 0; j < treeHash->used; j++) {
lowH = min(height[j], lowH);
}
if (lowH < minH) {
h = i;
minH = lowH;
}
}
}
if (h == hsk) {
break;
}
wc_xmss_bds_treehash_update(state, bds, h, sk_seed, pk_seed, addr);
updates--;
}
return updates;
}
static void wc_xmss_bds_update(XmssState* state, BdsState* bds,
const byte* sk_seed, const byte* pk_seed, const HashAddress addr)
{
if (bds->next < ((word32)1 << state->params->sub_h)) {
const XmssParams* params = state->params;
byte* sp = bds->stack + bds->offset * params->n;
HashAddress addrCopy;
XMSS_ADDR_OTS_SET_SUBTREE(addrCopy, addr);
if (bds->height == NULL) {
state->ret = WC_FAILURE;
return;
}
wc_xmss_bds_next_idx(state, bds, sk_seed, pk_seed, addrCopy, bds->next,
bds->height, &bds->offset, &sp);
bds->offset++;
bds->next++;
}
}
static word8 wc_xmss_lowest_zero_bit_index(word32 n, word8 max, word8* b)
{
word8 i;
for (i = 0; i < max; i++) {
if ((n & 1) == 0) {
break;
}
n >>= 1;
}
*b = (n >> 1) & 1;
return i;
}
static void wc_xmss_bds_auth_path(XmssState* state, BdsState* bds,
const word32 leafIdx, const byte* sk_seed, const byte* pk_seed,
HashAddress addr)
{
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
word8 tau;
byte* node = state->encMsg;
word8 parent;
if ((bds->keep == NULL) || (bds->authPath == NULL)) {
state->ret = WC_FAILURE;
return;
}
tau = wc_xmss_lowest_zero_bit_index(leafIdx, hs, &parent);
if (tau == 0) {
if (parent == 0) {
XMEMCPY(bds->keep, bds->authPath, n);
}
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
addr[XMSS_ADDR_OTS] = leafIdx;
wc_xmss_wots_gen_pk(state, sk_seed, pk_seed, addr, state->pk);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
wc_xmss_ltree(state, state->pk, pk_seed, addr, bds->authPath);
}
else {
byte* authPath;
byte* nodes;
word8 i;
authPath = bds->authPath + tau * n;
XMEMCPY(node, authPath - n, n);
XMEMCPY(node + n, bds->keep + ((tau - 1) >> 1) * n, n);
if ((tau < hs - 1) && (parent == 0)) {
XMEMCPY(bds->keep + (tau >> 1) * n, authPath, n);
}
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
addr[XMSS_ADDR_TREE_ZERO] = 0;
addr[XMSS_ADDR_TREE_HEIGHT] = tau - 1;
addr[XMSS_ADDR_TREE_INDEX] = leafIdx >> tau;
wc_xmss_rand_hash(state, node, pk_seed, addr, authPath);
authPath = bds->authPath;
nodes = bds->treeHashNode;
for (i = 0; i < tau; i++) {
if (i < hsk) {
XMEMCPY(authPath, nodes, n);
nodes += n;
}
else {
word32 o = (1 << (hs - 1 - i)) + i - hs +
(((leafIdx >> i) - 1) >> 1);
XMEMCPY(authPath, bds->retain + o * n, n);
}
authPath += n;
}
tau = min(tau, hsk);
for (i = 0; i < tau; i++) {
word32 startIdx = leafIdx + 1 + 3 * (1 << i);
if (startIdx < ((word32)1 << hs)) {
wc_xmss_bds_state_treehash_set_next_idx(bds, i, startIdx);
}
}
}
}
int wc_xmss_keygen(XmssState* state, const unsigned char* seed,
unsigned char* sk, unsigned char* pk)
{
#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
int ret = 0;
const XmssParams* params = state->params;
const word8 n = params->n;
byte* pk_root = pk;
WC_DECLARE_VAR(bds, BdsState, 1, 0);
#ifdef WOLFSSL_SMALL_STACK
ret = wc_xmss_bds_state_alloc(params, &bds);
if (ret == 0)
#endif
{
ret = wc_xmss_bds_state_load(state, sk, bds, NULL);
}
if (ret == 0) {
const byte* seed_priv = seed;
const byte* seed_pub = seed + 2 * n;
word32* sk_idx = (word32*)sk;
byte* sk_seeds = sk + params->idx_len;
byte* pk_seed = pk + n;
*sk_idx = 0;
XMEMCPY(sk_seeds, seed_priv, 2 * n);
XMEMCPY(pk_seed, seed_pub, n);
XMEMSET(state->addr, 0, sizeof(HashAddress));
wc_xmss_bds_treehash_initial(state, bds, sk_seeds, pk_seed,
state->addr, pk_root);
ret = state->ret;
}
if (ret == 0) {
byte* sk_root = sk + params->idx_len + 2 * n;
XMEMCPY(sk_root, pk_root, 2 * n);
ret = wc_xmss_bds_state_store(state, sk, bds);
}
#ifdef WOLFSSL_SMALL_STACK
wc_xmss_bds_state_free(bds);
#endif
return ret;
#else
(void)state;
(void)pk;
(void)sk;
(void)seed;
return NOT_COMPILED_IN;
#endif
}
int wc_xmss_sign(XmssState* state, const unsigned char* m, word32 mlen,
unsigned char* sk, unsigned char* sig)
{
#if WOLFSSL_XMSS_MIN_HEIGHT <= 32
int ret = 0;
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 h = params->h;
const word8 hk = params->h - params->bds_k;
const byte* sk_seed = sk + XMSS_IDX_LEN;
const byte* pk_seed = sk + XMSS_IDX_LEN + 3 * n;
byte node[WC_XMSS_MAX_N];
word32 idx;
byte* sig_r = sig + XMSS_IDX_LEN;
WC_DECLARE_VAR(bds, BdsState, 1, 0);
#ifdef WOLFSSL_SMALL_STACK
ret = wc_xmss_bds_state_alloc(params, &bds);
if (ret == 0)
#endif
{
ret = wc_xmss_bds_state_load(state, sk, bds, NULL);
}
if (ret == 0) {
*((word32*)sig) = *((word32*)sk);
ato32(sk, &idx);
if (IDX32_INVALID(idx, XMSS_IDX_LEN, h)) {
XMEMSET(sk, 0xFF, XMSS_IDX_LEN);
ForceZero(sk + XMSS_IDX_LEN, params->sk_len - XMSS_IDX_LEN);
ret = KEY_EXHAUSTED_E;
}
}
if (ret == 0) {
c32toa(idx + 1, sk);
}
if (ret == 0) {
const byte* sk_prf = sk + XMSS_IDX_LEN + n;
wc_idx_copy(sig, params->idx_len, state->buf, XMSS_PRF_M_LEN);
wc_xmss_prf(state, sk_prf, state->buf, sig_r);
ret = state->ret;
}
if (ret == 0) {
const byte* pub_root = sk + XMSS_IDX_LEN + 2 * n;
wc_xmss_hash_message(state, sig_r, pub_root, sig, XMSS_IDX_LEN, m, mlen,
node);
ret = state->ret;
sig += XMSS_IDX_LEN + n;
}
if (ret == 0) {
XMEMSET(state->addr, 0, sizeof(HashAddress));
state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
state->addr[XMSS_ADDR_OTS] = idx;
wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig);
ret = state->ret;
}
if (ret == 0) {
sig += params->wots_sig_len;
XMEMCPY(sig, bds->authPath, h * n);
ret = state->ret;
}
if (ret == 0) {
if (idx < ((word32)1 << h) - 1) {
wc_xmss_bds_auth_path(state, bds, idx, sk_seed, pk_seed,
state->addr);
ret = state->ret;
if (ret == 0) {
wc_xmss_bds_treehash_updates(state, bds, hk >> 1, sk_seed,
pk_seed, state->addr);
ret = state->ret;
}
}
}
if (ret == 0) {
ret = wc_xmss_bds_state_store(state, sk, bds);
}
#ifdef WOLFSSL_SMALL_STACK
wc_xmss_bds_state_free(bds);
#endif
return ret;
#else
(void)state;
(void)m;
(void)mlen;
(void)sk;
(void)sig;
return NOT_COMPILED_IN;
#endif
}
int wc_xmssmt_keygen(XmssState* state, const unsigned char* seed,
unsigned char* sk, unsigned char* pk)
{
int ret = 0;
const XmssParams* params = state->params;
const word8 n = params->n;
unsigned char* sk_seed = sk + params->idx_len;
unsigned char* pk_root = pk;
unsigned char* pk_seed = pk + n;
word8 i;
byte* wots_sigs;
BdsState* bds = NULL;
ret = wc_xmss_bds_state_alloc(params, &bds);
if (ret == 0) {
ret = wc_xmss_bds_state_load(state, sk, bds, &wots_sigs);
}
if (ret == 0) {
const byte* seed_priv = seed;
const byte* seed_pub = seed + 2 * params->n;
XMEMSET(sk, 0, params->idx_len);
XMEMCPY(sk_seed, seed_priv, 2 * n);
XMEMCPY(pk_seed, seed_pub, n);
XMEMSET(state->addr, 0, sizeof(HashAddress));
}
for (i = 0; (ret == 0) && (i < params->d - 1); i++) {
wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed,
state->addr, pk_root);
ret = state->ret;
if (ret == 0) {
state->addr[XMSS_ADDR_LAYER] = i+1;
wc_xmss_wots_sign(state, pk_root, sk_seed, pk_seed, state->addr,
wots_sigs + i * params->wots_sig_len);
ret = state->ret;
}
}
if (ret == 0) {
wc_xmss_bds_treehash_initial(state, bds + i, sk_seed, pk_seed,
state->addr, pk_root);
ret = state->ret;
}
if (ret == 0) {
unsigned char* sk_root = sk_seed + 2 * n;
XMEMCPY(sk_root, pk_root, 2 * n);
ret = wc_xmss_bds_state_store(state, sk, bds);
}
wc_xmss_bds_state_free(bds);
return ret;
}
#if !defined(WORD64_AVAILABLE) && (WOLFSSL_XMSS_MAX_HEIGHT > 32)
#error "Support not available - use XMSS small code option"
#endif
#if (WOLFSSL_XMSS_MAX_HEIGHT > 32)
typedef word64 XmssIdx;
#define IDX_MAX_BITS 64
#else
typedef word32 XmssIdx;
#define IDX_MAX_BITS 32
#endif
static void xmss_idx_decode(XmssIdx* idx, word8 c, const unsigned char* a)
{
word8 i;
XmssIdx n = 0;
for (i = 0; i < c; i++) {
n <<= 8;
n += a[i];
}
*idx = n;
}
static int xmss_idx_invalid(XmssIdx i, word8 h)
{
return ((i + 1) >> h) != 0;
}
static void xmss_idx_get_tree_leaf(XmssIdx i, word8 h, XmssIdx* t, word32* l)
{
*l = (word32)i & (((word32)1 << h) - 1);
*t = i >> h;
}
static void xmss_idx_set_addr_tree(XmssIdx i, HashAddress a)
{
#if IDX_MAX_BITS == 32
a[XMSS_ADDR_TREE_HI] = 0;
a[XMSS_ADDR_TREE] = i;
#else
a[XMSS_ADDR_TREE_HI] = (word32)(i >> 32);
a[XMSS_ADDR_TREE] = (word32)(i );
#endif
}
static int wc_xmssmt_sign_msg(XmssState* state, BdsState* bds, XmssIdx idx,
byte* wots_sigs, const unsigned char* m, word32 mlen, unsigned char* sk,
unsigned char* sig)
{
int ret;
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 hs = params->sub_h;
const word8 idx_len = params->idx_len;
const byte* sk_prf = sk + idx_len + n;
byte* sig_mt = sig;
byte* sig_r = sig + idx_len;
byte node[WC_XMSS_MAX_N];
wc_idx_copy(sig_mt, idx_len, state->buf, XMSS_PRF_M_LEN);
wc_xmss_prf(state, sk_prf, state->buf, sig_r);
ret = state->ret;
if (ret == 0) {
const byte* pub_root = sk + idx_len + 2 * n;
wc_xmss_hash_message(state, sig_r, pub_root, sig, idx_len, m, mlen,
node);
ret = state->ret;
sig += idx_len + n;
}
if (ret == 0) {
const byte* sk_seed = sk + idx_len;
const byte* pk_seed = sk + idx_len + 3 * n;
XmssIdx idx_tree;
word32 idx_leaf;
XMEMSET(state->addr, 0, sizeof(HashAddress));
state->addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
state->addr[XMSS_ADDR_LAYER] = 0;
xmss_idx_get_tree_leaf(idx, hs, &idx_tree, &idx_leaf);
xmss_idx_set_addr_tree(idx_tree, state->addr);
state->addr[XMSS_ADDR_OTS] = idx_leaf;
wc_xmss_wots_sign(state, node, sk_seed, pk_seed, state->addr, sig);
ret = state->ret;
}
if (ret == 0) {
word8 i;
byte *authPath;
sig += params->wots_sig_len;
authPath = bds[BDS_IDX(idx, 0, hs, params->d)].authPath;
if (authPath == NULL) {
state->ret = WC_FAILURE;
return state->ret;
}
XMEMCPY(sig, authPath, hs * n);
sig += hs * n;
for (i = 1; i < params->d; i++) {
XMEMCPY(sig, wots_sigs + (i - 1) * params->wots_sig_len,
params->wots_sig_len);
sig += params->wots_sig_len;
authPath = bds[BDS_IDX(idx, i, hs, params->d)].authPath;
if (authPath == NULL) {
state->ret = WC_FAILURE;
return state->ret;
}
XMEMCPY(sig, authPath, hs * n);
sig += hs * n;
}
ret = state->ret;
}
return ret;
}
static int wc_xmssmt_sign_next_idx(XmssState* state, BdsState* bds, XmssIdx idx,
byte* wots_sigs, unsigned char* sk)
{
int ret = 0;
const XmssParams* params = state->params;
const word8 n = params->n;
const word8 h = params->h;
const word8 hs = params->sub_h;
const word8 hsk = params->sub_h - params->bds_k;
const byte* sk_seed = sk + params->idx_len;
const byte* pk_seed = sk + params->idx_len + 3 * n;
XmssIdx idx_tree;
int computeAuthPath = 1;
unsigned int updates;
word8 i;
updates = hsk >> 1;
idx_tree = (idx >> hs) + 1;
if (idx_tree < ((XmssIdx)1 << (h - hs))) {
state->addr[XMSS_ADDR_LAYER] = 0;
xmss_idx_set_addr_tree(idx_tree, state->addr);
wc_xmss_bds_update(state, &bds[BDS_ALT_IDX(idx, 0, hs, params->d)],
sk_seed, pk_seed, state->addr);
ret = state->ret;
}
for (i = 0; (ret == 0) && (i < params->d); i++) {
word32 idx_leaf;
word8 bds_i = BDS_IDX(idx, i, hs, params->d);
word8 alt_i = BDS_ALT_IDX(idx, i, hs, params->d);
if (((idx + 1) << (IDX_MAX_BITS - ((i + 1) * hs))) != 0) {
state->addr[XMSS_ADDR_LAYER] = i;
xmss_idx_get_tree_leaf(idx >> (hs * i), hs, &idx_tree, &idx_leaf);
xmss_idx_set_addr_tree(idx_tree, state->addr);
idx_tree++;
if (computeAuthPath) {
wc_xmss_bds_auth_path(state, &bds[bds_i], idx_leaf, sk_seed,
pk_seed, state->addr);
ret = state->ret;
computeAuthPath = 0;
}
if (ret == 0) {
updates = wc_xmss_bds_treehash_updates(state, &bds[bds_i],
updates, sk_seed, pk_seed, state->addr);
ret = state->ret;
}
if ((ret == 0) && (i > 0) && (updates > 0) &&
(idx_tree < ((XmssIdx)1 << (h - (hs * (i + 1))))) &&
(bds[alt_i].next < ((XmssIdx)1 << h))) {
xmss_idx_set_addr_tree(idx_tree, state->addr);
wc_xmss_bds_update(state, &bds[alt_i], sk_seed, pk_seed,
state->addr);
ret = state->ret;
updates--;
}
}
else {
state->addr[XMSS_ADDR_LAYER] = i + 1;
idx_tree = (idx + 1) >> ((i + 1) * hs);
xmss_idx_get_tree_leaf(idx_tree, hs, &idx_tree, &idx_leaf);
xmss_idx_set_addr_tree(idx_tree, state->addr);
state->addr[XMSS_ADDR_OTS] = idx_leaf;
wc_xmss_wots_sign(state, bds[alt_i].stack, sk_seed, pk_seed,
state->addr, wots_sigs + i * params->wots_sig_len);
ret = state->ret;
if (ret == 0) {
word8 d;
bds[bds_i].offset = 0;
bds[bds_i].next = 0;
updates--;
computeAuthPath = 1;
for (d = 0; d < hsk; d++) {
wc_xmss_bds_state_treehash_complete(&bds[alt_i], d);
}
}
}
}
return ret;
}
int wc_xmssmt_sign(XmssState* state, const unsigned char* m, word32 mlen,
unsigned char* sk, unsigned char* sig)
{
int ret = 0;
const XmssParams* params = state->params;
const word8 h = params->h;
const word8 idx_len = params->idx_len;
XmssIdx idx = 0;
byte* sig_mt = sig;
byte* wots_sigs;
BdsState* bds = NULL;
ret = wc_xmss_bds_state_alloc(params, &bds);
if (ret == 0) {
ret = wc_xmss_bds_state_load(state, sk, bds, &wots_sigs);
}
if (ret == 0) {
XMEMCPY(sig_mt, sk, idx_len);
xmss_idx_decode(&idx, idx_len, sk);
}
if ((ret == 0) && xmss_idx_invalid(idx, h)) {
XMEMSET(sk, 0xFF, idx_len);
ForceZero(sk + idx_len, params->sk_len - idx_len);
ret = KEY_EXHAUSTED_E;
}
if (ret == 0) {
wc_idx_update(sk, idx_len);
ret = wc_xmssmt_sign_msg(state, bds, idx, wots_sigs, m, mlen, sk, sig);
}
if ((ret == 0) && (idx < (((XmssIdx)1 << h) - 1))) {
ret = wc_xmssmt_sign_next_idx(state, bds, idx, wots_sigs, sk);
}
if (ret == 0) {
ret = wc_xmss_bds_state_store(state, sk, bds);
}
wc_xmss_bds_state_free(bds);
return ret;
}
#endif
int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk)
{
int ret = 0;
wc_Idx idx;
WC_IDX_ZERO(idx);
WC_IDX_DECODE(idx, params->idx_len, sk, ret);
if ((ret == 0) && (WC_IDX_INVALID(idx, params->idx_len, params->h))) {
ret = KEY_EXHAUSTED_E;
}
return ret == 0;
}
#endif
#if !defined(WOLFSSL_WC_XMSS_SMALL) || defined(WOLFSSL_XMSS_VERIFY_ONLY)
static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf,
const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* root)
{
const XmssParams* params = state->params;
const word8 n = params->n;
const byte* b[2][2] = { { root, auth_path }, { auth_path, root } };
word8 i;
for (i = 0; i < params->sub_h; i++) {
word8 s = idx_leaf & 1;
addr[XMSS_ADDR_TREE_HEIGHT] = i;
idx_leaf >>= 1;
addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
wc_xmss_rand_hash_lr(state, b[s][0], b[s][1], pk_seed, addr, root);
b[0][1] += n;
b[1][0] += n;
}
}
#else
static void wc_xmss_compute_root(XmssState* state, word32 idx_leaf,
const byte* auth_path, const byte* pk_seed, HashAddress addr, byte* node)
{
const XmssParams* params = state->params;
const word8 n = params->n;
byte buffer[2 * WC_XMSS_MAX_N];
byte* b[2][2] = { { buffer, buffer + n }, { buffer + n, buffer } };
word8 i;
XMEMCPY(b[idx_leaf & 1][0], node, n);
XMEMCPY(b[idx_leaf & 1][1], auth_path, n);
auth_path += n;
for (i = 0; i < params->sub_h - 1; i++) {
addr[XMSS_ADDR_TREE_HEIGHT] = i;
idx_leaf >>= 1;
addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
wc_xmss_rand_hash(state, buffer, pk_seed, addr, b[idx_leaf & 1][0]);
XMEMCPY(b[idx_leaf & 1][1], auth_path, n);
auth_path += n;
}
addr[XMSS_ADDR_TREE_HEIGHT] = i;
idx_leaf >>= 1;
addr[XMSS_ADDR_TREE_INDEX] = idx_leaf;
wc_xmss_rand_hash(state, buffer, pk_seed, addr, node);
}
#endif
static void wc_xmss_root_from_sig(XmssState* state, const byte* pk_seed,
const byte* sig, word32 idx_sig, HashAddress addr, byte* node)
{
const XmssParams* params = state->params;
byte* wots_pk = state->pk;
const byte* auth_path = sig + params->wots_sig_len;
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_OTS;
addr[XMSS_ADDR_OTS] = idx_sig;
wc_xmss_wots_pk_from_sig(state, sig, node, pk_seed, addr, wots_pk);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_LTREE;
wc_xmss_ltree(state, wots_pk, pk_seed, addr, node);
addr[XMSS_ADDR_TYPE] = WC_XMSS_ADDR_TYPE_TREE;
addr[XMSS_ADDR_TREE_ZERO] = 0;
wc_xmss_compute_root(state, idx_sig, auth_path, pk_seed, addr, node);
}
int wc_xmssmt_verify(XmssState* state, const unsigned char* m, word32 mlen,
const unsigned char* sig, const unsigned char* pk)
{
const XmssParams* params = state->params;
const word8 n = params->n;
int ret = 0;
const byte* pub_root = pk;
const byte* pk_seed = pk + n;
byte node[WC_XMSS_MAX_N];
wc_Idx idx;
word32 idx_leaf = 0;
unsigned int i;
WC_IDX_ZERO(idx);
XMEMSET(state->addr, 0, sizeof(HashAddress));
if (ret == 0) {
WC_IDX_DECODE(idx, params->idx_len, sig, ret);
}
if (ret == 0) {
const byte* sig_r = sig + params->idx_len;
wc_xmss_hash_message(state, sig_r, pub_root, sig, params->idx_len, m,
mlen, node);
ret = state->ret;
}
if (ret == 0) {
WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr,
idx_leaf);
sig += params->idx_len + n;
wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr,
node);
ret = state->ret;
}
for (i = 1; (ret == 0) && (i < params->d); i++) {
state->addr[XMSS_ADDR_LAYER] = i;
WC_IDX_SET_ADDR_TREE(idx, params->idx_len, params->sub_h, state->addr,
idx_leaf);
sig += params->wots_sig_len + params->sub_h * n;
wc_xmss_root_from_sig(state, pk_seed, sig, idx_leaf, state->addr,
node);
ret = state->ret;
}
if ((ret == 0) && (XMEMCMP(node, pub_root, n) != 0)) {
ret = SIG_VERIFY_E;
}
return ret;
}
#endif