This ensures that xmss_core and xmss_core_fast offer the same API. Note that xmss_core_fast still needs a major refactor, and this wrapper is not exactly very clean. There is a considerable chance this refactor will change the format of the state in the secret key.master
@@ -5,19 +5,15 @@ LDLIBS = -lcrypto | |||||
SOURCES = params.c hash.c fips202.c hash_address.c randombytes.c wots.c xmss.c xmss_core.c xmss_commons.c | SOURCES = params.c hash.c fips202.c hash_address.c randombytes.c wots.c xmss.c xmss_core.c xmss_commons.c | ||||
HEADERS = params.h hash.h fips202.h hash_address.h randombytes.h wots.h xmss.h xmss_core.h xmss_commons.h | HEADERS = params.h hash.h fips202.h hash_address.h randombytes.h wots.h xmss.h xmss_core.h xmss_commons.h | ||||
SOURCES_FAST = $(subst core,core_fast,$(SOURCES)) | |||||
HEADERS_FAST = $(subst core,core_fast,$(HEADERS)) | |||||
SOURCES_FAST = $(subst xmss_core.c,xmss_core_fast.c,$(SOURCES)) | |||||
HEADERS_FAST = $(subst xmss_core.c,xmss_core_fast.c,$(HEADERS)) | |||||
TESTS = test/wots \ | TESTS = test/wots \ | ||||
test/xmss_determinism \ | test/xmss_determinism \ | ||||
test/xmss \ | test/xmss \ | ||||
test/xmss_fast \ | |||||
test/xmssmt \ | test/xmssmt \ | ||||
test/xmss_core_fast \ | |||||
test/xmssmt_core_fast \ | |||||
# These currently do not work, as xmss_core_fast does not match the xmss_core.h API | |||||
# test/xmss_fast \ | |||||
# test/xmssmt_fast \ | |||||
test/xmssmt_fast \ | |||||
UI = ui/xmss_keypair \ | UI = ui/xmss_keypair \ | ||||
ui/xmss_sign \ | ui/xmss_sign \ | ||||
@@ -25,14 +21,12 @@ UI = ui/xmss_keypair \ | |||||
ui/xmssmt_keypair \ | ui/xmssmt_keypair \ | ||||
ui/xmssmt_sign \ | ui/xmssmt_sign \ | ||||
ui/xmssmt_open \ | ui/xmssmt_open \ | ||||
# These currently do not work, as xmss_core_fast does not match the xmss_core.h API | |||||
# ui/xmss_keypair_fast \ | |||||
# ui/xmss_sign_fast \ | |||||
# ui/xmss_open_fast \ | |||||
# ui/xmssmt_keypair_fast \ | |||||
# ui/xmssmt_sign_fast \ | |||||
# ui/xmssmt_open_fast \ | |||||
ui/xmss_keypair_fast \ | |||||
ui/xmss_sign_fast \ | |||||
ui/xmss_open_fast \ | |||||
ui/xmssmt_keypair_fast \ | |||||
ui/xmssmt_sign_fast \ | |||||
ui/xmssmt_open_fast \ | |||||
all: tests ui | all: tests ui | ||||
@@ -58,12 +52,6 @@ test/xmssmt_fast: test/xmss.c $(SOURCES_FAST) $(OBJS) $(HEADERS_FAST) | |||||
test/xmssmt: test/xmss.c $(SOURCES) $(OBJS) $(HEADERS) | test/xmssmt: test/xmss.c $(SOURCES) $(OBJS) $(HEADERS) | ||||
$(CC) -DXMSSMT $(CFLAGS) -o $@ $(SOURCES) $< $(LDLIBS) | $(CC) -DXMSSMT $(CFLAGS) -o $@ $(SOURCES) $< $(LDLIBS) | ||||
test/xmss_core_fast: test/xmss_core_fast.c $(SOURCES_FAST) $(OBJS) $(HEADERS_FAST) | |||||
$(CC) $(CFLAGS) -o $@ $(SOURCES_FAST) $< $(LDLIBS) | |||||
test/xmssmt_core_fast: test/xmss_core_fast.c $(SOURCES_FAST) $(OBJS) $(HEADERS_FAST) | |||||
$(CC) $(CFLAGS) -o $@ $(SOURCES_FAST) $< $(LDLIBS) | |||||
test/%: test/%.c $(SOURCES) $(OBJS) $(HEADERS) | test/%: test/%.c $(SOURCES) $(OBJS) $(HEADERS) | ||||
$(CC) $(CFLAGS) -o $@ $(SOURCES) $< $(LDLIBS) | $(CC) $(CFLAGS) -o $@ $(SOURCES) $< $(LDLIBS) | ||||
@@ -4,9 +4,7 @@ This repository contains the reference implementation that accompanies the Inter | |||||
This reference implementation supports all parameter sets as defined in the Draft at run-time (specified by prefixing the public and private keys with a 32-bit `oid`). Implementations that want to use compile-time parameter sets can remove the `struct xmss_params` function parameter. | This reference implementation supports all parameter sets as defined in the Draft at run-time (specified by prefixing the public and private keys with a 32-bit `oid`). Implementations that want to use compile-time parameter sets can remove the `struct xmss_params` function parameter. | ||||
_While the behavior of the code in this repository is supposed to be stable, the API is not yet fully complete. **In particular, the wrapper for run-time parameters does not yet support the back-end functions that make use of BDS traversal. This means that the `xmss_core_fast.c` back-end is somewhat hard to use. This is the most immediate open TODO, and will be resolved soon.**_ | |||||
_When using the current code base, please be careful, expect changes and watch this document for further documentation._ | |||||
_When using the current code base, please be careful, expect changes and watch this document for further documentation. In particular, `xmss_core_fast.c` is long due for a serious clean-up. While this will not change its public API or output, it may affect the storage format of the BDS state (i.e. part of the secret key)._ | |||||
### Dependencies | ### Dependencies | ||||
@@ -1,150 +0,0 @@ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <stdlib.h> | |||||
#include "../xmss_core_fast.h" | |||||
#include "../params.h" | |||||
#include "../randombytes.h" | |||||
#define MLEN 3491 | |||||
#define SIGNATURES 256 | |||||
unsigned long long t1, t2; | |||||
unsigned long long cpucycles(void) | |||||
{ | |||||
unsigned long long result; | |||||
asm volatile(".byte 15;.byte 49;shlq $32,%%rdx;orq %%rdx,%%rax" : "=a" (result) :: "%rdx"); | |||||
return result; | |||||
} | |||||
int main() | |||||
{ | |||||
xmss_params params; | |||||
// TODO test more different OIDs | |||||
uint32_t oid = 0x01000001; | |||||
xmss_parse_oid(¶ms, oid); | |||||
int r; | |||||
unsigned long long i; | |||||
unsigned int k = params.bds_k; | |||||
unsigned long errors = 0; | |||||
unsigned char sk[4*params.n+4]; | |||||
unsigned char pk[2*params.n]; | |||||
// TODO should we hide this into xmss_fast.c and just allocate a large enough chunk of memory here? | |||||
unsigned char stack[(params.tree_height+1)*params.n]; | |||||
unsigned int stackoffset = 0; | |||||
unsigned char stacklevels[params.tree_height+1]; | |||||
unsigned char auth[(params.tree_height)*params.n]; | |||||
unsigned char keep[(params.tree_height >> 1)*params.n]; | |||||
treehash_inst treehash[params.tree_height-k]; | |||||
unsigned char th_nodes[(params.tree_height-k)*params.n]; | |||||
unsigned char retain[((1 << k) - k - 1)*params.n]; | |||||
bds_state s; | |||||
bds_state *state = &s; | |||||
for (i = 0; i < params.tree_height-k; i++) | |||||
treehash[i].node = &th_nodes[params.n*i]; | |||||
xmss_set_bds_state(state, stack, stackoffset, stacklevels, auth, keep, treehash, retain, 0); | |||||
unsigned long long signature_length = 4+params.n+params.wots_sig_bytes+params.tree_height*params.n; | |||||
unsigned char mi[MLEN]; | |||||
unsigned char mo[MLEN+signature_length]; | |||||
unsigned char sm[MLEN+signature_length]; | |||||
unsigned long long smlen; | |||||
unsigned long long mlen; | |||||
randombytes(mi, MLEN); | |||||
printf("keypair\n"); | |||||
t1 = cpucycles(); | |||||
xmss_core_keypair(¶ms, pk, sk, state); | |||||
t2 = cpucycles(); | |||||
printf("cycles = %llu\n", (t2-t1)); | |||||
double sec = (t2-t1)/3500000; | |||||
printf("ms = %f\n", sec); | |||||
// check pub_seed in SK | |||||
for (i = 0; i < params.n; i++) { | |||||
if (pk[params.n+i] != sk[4+2*params.n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i); | |||||
if (pk[i] != sk[4+3*params.n+i]) printf("pk.root != sk.root %llu",i); | |||||
} | |||||
// check index | |||||
unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; | |||||
if (idx) printf("\nidx != 0 %lu\n",idx); | |||||
for (i = 0; i < SIGNATURES; i++) { | |||||
printf("sign\n"); | |||||
xmss_core_sign(¶ms, sk, state, sm, &smlen, mi, MLEN); | |||||
idx = ((unsigned long)sm[0] << 24) | ((unsigned long)sm[1] << 16) | ((unsigned long)sm[2] << 8) | sm[3]; | |||||
printf("\nidx = %lu\n",idx); | |||||
r = memcmp(mi, sm+signature_length,MLEN); | |||||
printf("%d\n", r); | |||||
/* Test valid signature */ | |||||
printf("verify\n"); | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r); | |||||
if (r != 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", r); | |||||
printf("%llu\n", MLEN-mlen); | |||||
/* Test with modified message */ | |||||
sm[signature_length+10] ^= 1; | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
if (r == 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
/* Test with modified signature */ | |||||
/* Modified index */ | |||||
sm[signature_length+10] ^= 1; | |||||
sm[2] ^= 1; | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
if (r == 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
/* Modified R */ | |||||
sm[2] ^= 1; | |||||
sm[5] ^= 1; | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
if (r == 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
/* Modified OTS sig */ | |||||
sm[5] ^= 1; | |||||
sm[240] ^= 1; | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
if (r == 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
/* Modified AUTH */ | |||||
sm[240] ^= 1; | |||||
sm[signature_length - 10] ^= 1; | |||||
r = xmss_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
if (r == 0) errors++; | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
} | |||||
printf("#errors = %lu\n", errors); | |||||
printf("finished loop\n"); | |||||
return 0; | |||||
} |
@@ -1,138 +0,0 @@ | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "../xmss_core_fast.h" | |||||
#include "../params.h" | |||||
#include "../randombytes.h" | |||||
#define MLEN 3491 | |||||
#define SIGNATURES 128 | |||||
unsigned char mi[MLEN]; | |||||
unsigned long long smlen; | |||||
unsigned long long mlen; | |||||
unsigned long long t1, t2; | |||||
unsigned long long cpucycles(void) | |||||
{ | |||||
unsigned long long result; | |||||
asm volatile(".byte 15;.byte 49;shlq $32,%%rdx;orq %%rdx,%%rax" : "=a" (result) :: "%rdx"); | |||||
return result; | |||||
} | |||||
int main() | |||||
{ | |||||
xmss_params params; | |||||
// TODO test more different OIDs | |||||
uint32_t oid = 0x01000001; | |||||
xmssmt_parse_oid(¶ms, oid); | |||||
int r; | |||||
unsigned long long i,j; | |||||
unsigned int n = params.n; | |||||
unsigned int h = params.full_height; | |||||
unsigned int d = params.d; | |||||
unsigned int k = params.bds_k; | |||||
unsigned int tree_h = h / d; | |||||
// stack needs to be larger than regular (H-K-1), since we re-use for 'next' | |||||
unsigned char stack[(2*d-1) * (tree_h + 1)*n]; | |||||
unsigned char stacklevels[(2*d-1) * (tree_h + 1)*n]; | |||||
unsigned char auth[(2*d-1) * tree_h*n]; | |||||
unsigned char keep[(2*d-1) * (tree_h >> 1)*n]; | |||||
treehash_inst treehash[(2*d-1) * (tree_h-k)]; | |||||
unsigned char th_nodes[(2*d-1) * (tree_h-k)*n]; | |||||
unsigned char retain[(2*d-1) * ((1 << k) - k - 1)*n]; | |||||
unsigned char wots_sigs[(d-1) * params.wots_sig_bytes]; | |||||
// first d are 'regular' states, second d are 'next'; top tree has no 'next' | |||||
bds_state states[2*d-1]; | |||||
for (i = 0; i < 2*d-1; i++) { | |||||
for (j = 0; j < tree_h-k; j++) | |||||
treehash[i*(tree_h-k) + j].node = th_nodes + (i*(tree_h-k) + j) * n; | |||||
xmss_set_bds_state(states + i, | |||||
stack + i*(tree_h + 1)*n, 0, stacklevels + i*(tree_h + 1), | |||||
auth + i*tree_h*n, | |||||
keep + i*(tree_h >> 1)*n, | |||||
treehash + i*(tree_h-k), | |||||
retain + i*((1 << k) - k - 1)*n, | |||||
0 | |||||
); | |||||
} | |||||
unsigned char sk[(params.index_bytes+4*n)]; | |||||
unsigned char pk[2*n]; | |||||
unsigned long long signature_length = params.index_bytes + n + (d*params.wots_sig_bytes) + h*n; | |||||
unsigned char mo[MLEN+signature_length]; | |||||
unsigned char sm[MLEN+signature_length]; | |||||
randombytes(mi, MLEN); | |||||
printf("keypair\n"); | |||||
xmssmt_core_keypair(¶ms, pk, sk, states, wots_sigs); | |||||
// check pub_seed in SK | |||||
for (i = 0; i < n; i++) { | |||||
if (pk[n+i] != sk[params.index_bytes+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i); | |||||
if (pk[i] != sk[params.index_bytes+3*n+i]) printf("pk.root != sk.root %llu",i); | |||||
} | |||||
printf("pk checked\n"); | |||||
unsigned int idx_len = params.index_bytes; | |||||
// check index | |||||
unsigned long long idx = 0; | |||||
for (i = 0; i < idx_len; i++) { | |||||
idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i); | |||||
} | |||||
if (idx) printf("\nidx != 0: %llu\n",idx); | |||||
for (i = 0; i < SIGNATURES; i++) { | |||||
printf("sign\n"); | |||||
t1 = cpucycles(); | |||||
xmssmt_core_sign(¶ms, sk, states, wots_sigs, sm, &smlen, mi, MLEN); | |||||
t2 = cpucycles(); | |||||
printf("signing cycles = %llu\n", (t2-t1)); | |||||
idx = 0; | |||||
for (j = 0; j < idx_len; j++) { | |||||
idx += ((unsigned long long)sm[j]) << 8*(idx_len - 1 - j); | |||||
} | |||||
printf("\nidx = %llu\n",idx); | |||||
r = memcmp(mi, sm+signature_length,MLEN); | |||||
printf("%d\n", r); | |||||
/* Test valid signature */ | |||||
printf("verify\n"); | |||||
t1 = cpucycles(); | |||||
r = xmssmt_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
t2 = cpucycles(); | |||||
printf("verification cycles = %llu\n", (t2-t1)); | |||||
printf("%d\n", r); | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", r); | |||||
printf("%llu\n", MLEN-mlen); | |||||
/* Test with modified message */ | |||||
sm[52] ^= 1; | |||||
r = xmssmt_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
/* Test with modified signature */ | |||||
sm[260] ^= 1; | |||||
sm[52] ^= 1; | |||||
sm[2] ^= 1; | |||||
r = xmssmt_core_sign_open(¶ms, mo, &mlen, sm, smlen, pk); | |||||
printf("%d\n", r+1); | |||||
r = memcmp(mi,mo,MLEN); | |||||
printf("%d\n", (r!=0) - 1); | |||||
printf("%llu\n", mlen+1); | |||||
} | |||||
return 0; | |||||
} |
@@ -8,26 +8,187 @@ | |||||
#include "randombytes.h" | #include "randombytes.h" | ||||
#include "wots.h" | #include "wots.h" | ||||
#include "xmss_commons.h" | #include "xmss_commons.h" | ||||
#include "xmss_core_fast.h" | |||||
#include "xmss_core.h" | |||||
typedef struct{ | |||||
unsigned char h; | |||||
unsigned long next_idx; | |||||
unsigned char stackusage; | |||||
unsigned char completed; | |||||
unsigned char *node; | |||||
} treehash_inst; | |||||
typedef struct { | |||||
unsigned char *stack; | |||||
unsigned int stackoffset; | |||||
unsigned char *stacklevels; | |||||
unsigned char *auth; | |||||
unsigned char *keep; | |||||
treehash_inst *treehash; | |||||
unsigned char *retain; | |||||
unsigned int next_leaf; | |||||
} bds_state; | |||||
/* These serialization functions provide a transition between the current | |||||
way of storing the state in an exposed struct, and storing it as part of the | |||||
byte array that is the secret key. | |||||
They will probably be refactored in a non-backwards-compatible way, soon. */ | |||||
static void xmssmt_serialize_state(const xmss_params *params, | |||||
unsigned char *sk, bds_state *states) | |||||
{ | |||||
unsigned int i, j; | |||||
/* Skip past the 'regular' sk */ | |||||
sk += params->index_bytes + 4*params->n; | |||||
for (i = 0; i < 2*params->d - 1; i++) { | |||||
sk += (params->tree_height + 1) * params->n; /* stack */ | |||||
ull_to_bytes(sk, 4, states[i].stackoffset); | |||||
sk += 4; | |||||
sk += params->tree_height + 1; /* stacklevels */ | |||||
sk += params->tree_height * params->n; /* auth */ | |||||
sk += (params->tree_height >> 1) * params->n; /* keep */ | |||||
for (j = 0; j < params->tree_height - params->bds_k; j++) { | |||||
ull_to_bytes(sk, 1, states[i].treehash[j].h); | |||||
sk += 1; | |||||
ull_to_bytes(sk, 4, states[i].treehash[j].next_idx); | |||||
sk += 4; | |||||
ull_to_bytes(sk, 1, states[i].treehash[j].stackusage); | |||||
sk += 1; | |||||
ull_to_bytes(sk, 1, states[i].treehash[j].completed); | |||||
sk += 1; | |||||
sk += params->n; /* node */ | |||||
} | |||||
/* retain */ | |||||
sk += ((1 << params->bds_k) - params->bds_k - 1) * params->n; | |||||
ull_to_bytes(sk, 4, states[i].next_leaf); | |||||
sk += 4; | |||||
} | |||||
} | |||||
static void xmssmt_deserialize_state(const xmss_params *params, | |||||
bds_state *states, | |||||
unsigned char **wots_sigs, | |||||
unsigned char *sk) | |||||
{ | |||||
unsigned int i, j; | |||||
/* Skip past the 'regular' sk */ | |||||
sk += params->index_bytes + 4*params->n; | |||||
// TODO These data sizes follow from the (former) test xmss_core_fast.c | |||||
// TODO They should be reconsidered / motivated more explicitly | |||||
for (i = 0; i < 2*params->d - 1; i++) { | |||||
states[i].stack = sk; | |||||
sk += (params->tree_height + 1) * params->n; | |||||
states[i].stackoffset = bytes_to_ull(sk, 4); | |||||
sk += 4; | |||||
states[i].stacklevels = sk; | |||||
sk += params->tree_height + 1; | |||||
states[i].auth = sk; | |||||
sk += params->tree_height * params->n; | |||||
states[i].keep = sk; | |||||
sk += (params->tree_height >> 1) * params->n; | |||||
for (j = 0; j < params->tree_height - params->bds_k; j++) { | |||||
states[i].treehash[j].h = bytes_to_ull(sk, 1); | |||||
sk += 1; | |||||
states[i].treehash[j].next_idx = bytes_to_ull(sk, 4); | |||||
sk += 4; | |||||
states[i].treehash[j].stackusage = bytes_to_ull(sk, 1); | |||||
sk += 1; | |||||
states[i].treehash[j].completed = bytes_to_ull(sk, 1); | |||||
sk += 1; | |||||
states[i].treehash[j].node = sk; | |||||
sk += params->n; | |||||
} | |||||
states[i].retain = sk; | |||||
sk += ((1 << params->bds_k) - params->bds_k - 1) * params->n; | |||||
states[i].next_leaf = bytes_to_ull(sk, 4); | |||||
sk += 4; | |||||
} | |||||
if (params->d > 1) { | |||||
*wots_sigs = sk; | |||||
} | |||||
} | |||||
static void xmss_serialize_state(const xmss_params *params, | |||||
unsigned char *sk, bds_state *state) | |||||
{ | |||||
xmssmt_serialize_state(params, sk, state); | |||||
} | |||||
static void xmss_deserialize_state(const xmss_params *params, | |||||
bds_state *state, unsigned char *sk) | |||||
{ | |||||
xmssmt_deserialize_state(params, state, NULL, sk); | |||||
} | |||||
static void memswap(void *a, void *b, void *t, unsigned long long len) | |||||
{ | |||||
memcpy(t, a, len); | |||||
memcpy(a, b, len); | |||||
memcpy(b, t, len); | |||||
} | |||||
/** | /** | ||||
* Initialize BDS state struct | |||||
* parameter names are the same as used in the description of the BDS traversal | |||||
* Swaps the content of two bds_state objects, swapping actual memory rather | |||||
* than pointers. | |||||
* As we're mapping memory chunks in the secret key to bds state objects, | |||||
* it is now necessary to make swaps 'real swaps'. This could be done in the | |||||
* serialization function as well, but that causes more overhead | |||||
*/ | */ | ||||
void xmss_set_bds_state(bds_state *state, unsigned char *stack, | |||||
int stackoffset, unsigned char *stacklevels, | |||||
unsigned char *auth, unsigned char *keep, | |||||
treehash_inst *treehash, unsigned char *retain, | |||||
int next_leaf) | |||||
// TODO this should not be necessary if we keep better track of the states | |||||
static void deep_state_swap(const xmss_params *params, | |||||
bds_state *a, bds_state *b) | |||||
{ | { | ||||
state->stack = stack; | |||||
state->stackoffset = stackoffset; | |||||
state->stacklevels = stacklevels; | |||||
state->auth = auth; | |||||
state->keep = keep; | |||||
state->treehash = treehash; | |||||
state->retain = retain; | |||||
state->next_leaf = next_leaf; | |||||
// TODO this is extremely ugly and should be refactored | |||||
// TODO right now, this ensures that both 'stack' and 'retain' fit | |||||
unsigned char t[ | |||||
((params->tree_height + 1) > ((1 << params->bds_k) - params->bds_k - 1) | |||||
? (params->tree_height + 1) | |||||
: ((1 << params->bds_k) - params->bds_k - 1)) | |||||
* params->n]; | |||||
unsigned int i; | |||||
memswap(a->stack, b->stack, t, (params->tree_height + 1) * params->n); | |||||
memswap(&a->stackoffset, &b->stackoffset, t, sizeof(a->stackoffset)); | |||||
memswap(a->stacklevels, b->stacklevels, t, params->tree_height + 1); | |||||
memswap(a->auth, b->auth, t, params->tree_height * params->n); | |||||
memswap(a->keep, b->keep, t, (params->tree_height >> 1) * params->n); | |||||
for (i = 0; i < params->tree_height - params->bds_k; i++) { | |||||
memswap(&a->treehash[i].h, &b->treehash[i].h, t, sizeof(a->treehash[i].h)); | |||||
memswap(&a->treehash[i].next_idx, &b->treehash[i].next_idx, t, sizeof(a->treehash[i].next_idx)); | |||||
memswap(&a->treehash[i].stackusage, &b->treehash[i].stackusage, t, sizeof(a->treehash[i].stackusage)); | |||||
memswap(&a->treehash[i].completed, &b->treehash[i].completed, t, sizeof(a->treehash[i].completed)); | |||||
memswap(a->treehash[i].node, b->treehash[i].node, t, params->n); | |||||
} | |||||
memswap(a->retain, b->retain, t, ((1 << params->bds_k) - params->bds_k - 1) * params->n); | |||||
memswap(&a->next_leaf, &b->next_leaf, t, sizeof(a->next_leaf)); | |||||
} | } | ||||
static int treehash_minheight_on_stack(const xmss_params *params, | static int treehash_minheight_on_stack(const xmss_params *params, | ||||
@@ -351,7 +512,7 @@ static void bds_round(const xmss_params *params, | |||||
*/ | */ | ||||
unsigned long long xmss_core_sk_bytes(const xmss_params *params) | unsigned long long xmss_core_sk_bytes(const xmss_params *params) | ||||
{ | { | ||||
return params->index_bytes + 4 * params->n; | |||||
return xmssmt_core_sk_bytes(params); | |||||
} | } | ||||
/* | /* | ||||
@@ -360,10 +521,20 @@ unsigned long long xmss_core_sk_bytes(const xmss_params *params) | |||||
* Format pk: [root || PUB_SEED] omitting algo oid. | * Format pk: [root || PUB_SEED] omitting algo oid. | ||||
*/ | */ | ||||
int xmss_core_keypair(const xmss_params *params, | int xmss_core_keypair(const xmss_params *params, | ||||
unsigned char *pk, unsigned char *sk, bds_state *state) | |||||
unsigned char *pk, unsigned char *sk) | |||||
{ | { | ||||
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||
// TODO refactor BDS state not to need separate treehash instances | |||||
bds_state state; | |||||
treehash_inst treehash[params->tree_height - params->bds_k]; | |||||
state.treehash = treehash; | |||||
xmss_deserialize_state(params, &state, sk); | |||||
state.stackoffset = 0; | |||||
state.next_leaf = 0; | |||||
// Set idx = 0 | // Set idx = 0 | ||||
sk[0] = 0; | sk[0] = 0; | ||||
sk[1] = 0; | sk[1] = 0; | ||||
@@ -375,9 +546,13 @@ int xmss_core_keypair(const xmss_params *params, | |||||
memcpy(pk + params->n, sk + params->index_bytes + 2*params->n, params->n); | memcpy(pk + params->n, sk + params->index_bytes + 2*params->n, params->n); | ||||
// Compute root | // Compute root | ||||
treehash_init(params, pk, params->tree_height, 0, state, sk + params->index_bytes, sk + params->index_bytes + 2*params->n, addr); | |||||
treehash_init(params, pk, params->tree_height, 0, &state, sk + params->index_bytes, sk + params->index_bytes + 2*params->n, addr); | |||||
// copy root o sk | // copy root o sk | ||||
memcpy(sk + params->index_bytes + 3*params->n, pk, params->n); | memcpy(sk + params->index_bytes + 3*params->n, pk, params->n); | ||||
/* Write the BDS state into sk. */ | |||||
xmss_serialize_state(params, sk, &state); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -389,7 +564,7 @@ int xmss_core_keypair(const xmss_params *params, | |||||
* | * | ||||
*/ | */ | ||||
int xmss_core_sign(const xmss_params *params, | int xmss_core_sign(const xmss_params *params, | ||||
unsigned char *sk, bds_state *state, | |||||
unsigned char *sk, | |||||
unsigned char *sm, unsigned long long *smlen, | unsigned char *sm, unsigned long long *smlen, | ||||
const unsigned char *m, unsigned long long mlen) | const unsigned char *m, unsigned long long mlen) | ||||
{ | { | ||||
@@ -397,6 +572,14 @@ int xmss_core_sign(const xmss_params *params, | |||||
uint16_t i = 0; | uint16_t i = 0; | ||||
// TODO refactor BDS state not to need separate treehash instances | |||||
bds_state state; | |||||
treehash_inst treehash[params->tree_height - params->bds_k]; | |||||
state.treehash = treehash; | |||||
/* Load the BDS state from sk. */ | |||||
xmss_deserialize_state(params, &state, sk); | |||||
// Extract SK | // Extract SK | ||||
unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; | unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3]; | ||||
unsigned char sk_seed[params->n]; | unsigned char sk_seed[params->n]; | ||||
@@ -479,11 +662,11 @@ int xmss_core_sign(const xmss_params *params, | |||||
*smlen += params->wots_sig_bytes; | *smlen += params->wots_sig_bytes; | ||||
// the auth path was already computed during the previous round | // the auth path was already computed during the previous round | ||||
memcpy(sm, state->auth, params->tree_height*params->n); | |||||
memcpy(sm, state.auth, params->tree_height*params->n); | |||||
if (idx < (1U << params->tree_height) - 1) { | if (idx < (1U << params->tree_height) - 1) { | ||||
bds_round(params, state, idx, sk_seed, pub_seed, ots_addr); | |||||
bds_treehash_update(params, state, (params->tree_height - params->bds_k) >> 1, sk_seed, pub_seed, ots_addr); | |||||
bds_round(params, &state, idx, sk_seed, pub_seed, ots_addr); | |||||
bds_treehash_update(params, &state, (params->tree_height - params->bds_k) >> 1, sk_seed, pub_seed, ots_addr); | |||||
} | } | ||||
sm += params->tree_height*params->n; | sm += params->tree_height*params->n; | ||||
@@ -492,6 +675,9 @@ int xmss_core_sign(const xmss_params *params, | |||||
memcpy(sm, m, mlen); | memcpy(sm, m, mlen); | ||||
*smlen += mlen; | *smlen += mlen; | ||||
/* Write the updated BDS state back into sk. */ | |||||
xmss_serialize_state(params, sk, &state); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -502,7 +688,18 @@ int xmss_core_sign(const xmss_params *params, | |||||
*/ | */ | ||||
unsigned long long xmssmt_core_sk_bytes(const xmss_params *params) | unsigned long long xmssmt_core_sk_bytes(const xmss_params *params) | ||||
{ | { | ||||
return params->index_bytes + 4 * params->n; | |||||
return params->index_bytes + 4 * params->n | |||||
+ (2 * params->d - 1) * ( | |||||
(params->tree_height + 1) * params->n | |||||
+ 4 | |||||
+ params->tree_height + 1 | |||||
+ params->tree_height * params->n | |||||
+ (params->tree_height >> 1) * params->n | |||||
+ (params->tree_height - params->bds_k) * (7 + params->n) | |||||
+ ((1 << params->bds_k) - params->bds_k - 1) * params->n | |||||
+ 4 | |||||
) | |||||
+ (params->d - 1) * params->wots_sig_bytes; | |||||
} | } | ||||
/* | /* | ||||
@@ -511,12 +708,26 @@ unsigned long long xmssmt_core_sk_bytes(const xmss_params *params) | |||||
* Format pk: [root || PUB_SEED] omitting algo oid. | * Format pk: [root || PUB_SEED] omitting algo oid. | ||||
*/ | */ | ||||
int xmssmt_core_keypair(const xmss_params *params, | int xmssmt_core_keypair(const xmss_params *params, | ||||
unsigned char *pk, unsigned char *sk, | |||||
bds_state *states, unsigned char *wots_sigs) | |||||
unsigned char *pk, unsigned char *sk) | |||||
{ | { | ||||
unsigned char ots_seed[params->n]; | unsigned char ots_seed[params->n]; | ||||
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||
unsigned int i; | unsigned int i; | ||||
unsigned char *wots_sigs; | |||||
// TODO refactor BDS state not to need separate treehash instances | |||||
bds_state states[2*params->d - 1]; | |||||
treehash_inst treehash[(2*params->d - 1) * (params->tree_height - params->bds_k)]; | |||||
for (i = 0; i < 2*params->d - 1; i++) { | |||||
states[i].treehash = treehash + i * (params->tree_height - params->bds_k); | |||||
} | |||||
xmssmt_deserialize_state(params, states, &wots_sigs, sk); | |||||
for (i = 0; i < 2 * params->d - 1; i++) { | |||||
states[i].stackoffset = 0; | |||||
states[i].next_leaf = 0; | |||||
} | |||||
// Set idx = 0 | // Set idx = 0 | ||||
for (i = 0; i < params->index_bytes; i++) { | for (i = 0; i < params->index_bytes; i++) { | ||||
@@ -540,6 +751,9 @@ int xmssmt_core_keypair(const xmss_params *params, | |||||
// Address now points to the single tree on layer d-1 | // Address now points to the single tree on layer d-1 | ||||
treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); | treehash_init(params, pk, params->tree_height, 0, states + i, sk+params->index_bytes, pk+params->n, addr); | ||||
memcpy(sk + params->index_bytes + 3*params->n, pk, params->n); | memcpy(sk + params->index_bytes + 3*params->n, pk, params->n); | ||||
xmssmt_serialize_state(params, sk, states); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -552,7 +766,6 @@ int xmssmt_core_keypair(const xmss_params *params, | |||||
*/ | */ | ||||
int xmssmt_core_sign(const xmss_params *params, | int xmssmt_core_sign(const xmss_params *params, | ||||
unsigned char *sk, | unsigned char *sk, | ||||
bds_state *states, unsigned char *wots_sigs, | |||||
unsigned char *sm, unsigned long long *smlen, | unsigned char *sm, unsigned long long *smlen, | ||||
const unsigned char *m, unsigned long long mlen) | const unsigned char *m, unsigned long long mlen) | ||||
{ | { | ||||
@@ -574,7 +787,17 @@ int xmssmt_core_sign(const xmss_params *params, | |||||
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||
uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||||
unsigned char idx_bytes_32[32]; | unsigned char idx_bytes_32[32]; | ||||
bds_state tmp; | |||||
unsigned char *wots_sigs; | |||||
// TODO refactor BDS state not to need separate treehash instances | |||||
bds_state states[2*params->d - 1]; | |||||
treehash_inst treehash[(2*params->d - 1) * (params->tree_height - params->bds_k)]; | |||||
for (i = 0; i < 2*params->d - 1; i++) { | |||||
states[i].treehash = treehash + i * (params->tree_height - params->bds_k); | |||||
} | |||||
xmssmt_deserialize_state(params, states, &wots_sigs, sk); | |||||
// Extract SK | // Extract SK | ||||
unsigned long long idx = 0; | unsigned long long idx = 0; | ||||
@@ -700,9 +923,7 @@ int xmssmt_core_sign(const xmss_params *params, | |||||
} | } | ||||
} | } | ||||
else if (idx < (1ULL << params->full_height) - 1) { | else if (idx < (1ULL << params->full_height) - 1) { | ||||
memcpy(&tmp, states+params->d + i, sizeof(bds_state)); | |||||
memcpy(states+params->d + i, states + i, sizeof(bds_state)); | |||||
memcpy(states + i, &tmp, sizeof(bds_state)); | |||||
deep_state_swap(params, states+params->d + i, states + i); | |||||
set_layer_addr(ots_addr, (i+1)); | set_layer_addr(ots_addr, (i+1)); | ||||
set_tree_addr(ots_addr, ((idx + 1) >> ((i+2) * params->tree_height))); | set_tree_addr(ots_addr, ((idx + 1) >> ((i+2) * params->tree_height))); | ||||
@@ -725,5 +946,7 @@ int xmssmt_core_sign(const xmss_params *params, | |||||
memcpy(sm, m, mlen); | memcpy(sm, m, mlen); | ||||
*smlen += mlen; | *smlen += mlen; | ||||
xmssmt_serialize_state(params, sk, states); | |||||
return 0; | return 0; | ||||
} | } |
@@ -1,104 +0,0 @@ | |||||
#ifndef XMSS_CORE_H | |||||
#define XMSS_CORE_H | |||||
#include "params.h" | |||||
typedef struct{ | |||||
unsigned int h; | |||||
unsigned int next_idx; | |||||
unsigned int stackusage; | |||||
unsigned char completed; | |||||
unsigned char *node; | |||||
} treehash_inst; | |||||
typedef struct { | |||||
unsigned char *stack; | |||||
unsigned int stackoffset; | |||||
unsigned char *stacklevels; | |||||
unsigned char *auth; | |||||
unsigned char *keep; | |||||
treehash_inst *treehash; | |||||
unsigned char *retain; | |||||
unsigned int next_leaf; | |||||
} bds_state; | |||||
/** | |||||
* Initialize BDS state struct | |||||
* parameter names are the same as used in the description of the BDS traversal | |||||
*/ | |||||
void xmss_set_bds_state(bds_state *state, unsigned char *stack, | |||||
int stackoffset, unsigned char *stacklevels, | |||||
unsigned char *auth, unsigned char *keep, | |||||
treehash_inst *treehash, unsigned char *retain, | |||||
int next_leaf); | |||||
/** | |||||
* Given a set of parameters, this function returns the size of the secret key. | |||||
* This is implementation specific, as varying choices in tree traversal will | |||||
* result in varying requirements for state storage. | |||||
*/ | |||||
unsigned long long xmss_core_sk_bytes(const xmss_params *params); | |||||
/** | |||||
* Generates a XMSS key pair for a given parameter set. | |||||
* Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] | |||||
* Format pk: [root || PUB_SEED] omitting algo oid. | |||||
*/ | |||||
int xmss_core_keypair(const xmss_params *params, | |||||
unsigned char *pk, unsigned char *sk, bds_state *state); | |||||
/** | |||||
* Signs a message. | |||||
* Returns | |||||
* 1. an array containing the signature followed by the message AND | |||||
* 2. an updated secret key! | |||||
*/ | |||||
int xmss_core_sign(const xmss_params *params, | |||||
unsigned char *sk, bds_state *state, | |||||
unsigned char *sm, unsigned long long *smlen, | |||||
const unsigned char *m, unsigned long long mlen); | |||||
/** | |||||
* Verifies a given message signature pair under a given public key. | |||||
* | |||||
* Note: msg and mlen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sm which has the form (sig||msg). | |||||
*/ | |||||
int xmss_core_sign_open(const xmss_params *params, | |||||
unsigned char *m, unsigned long long *mlen, | |||||
const unsigned char *sm, unsigned long long smlen, | |||||
const unsigned char *pk); | |||||
/** | |||||
* Given a set of parameters, this function returns the size of the secret key. | |||||
* This is implementation specific, as varying choices in tree traversal will | |||||
* result in varying requirements for state storage. | |||||
*/ | |||||
unsigned long long xmssmt_core_sk_bytes(const xmss_params *params); | |||||
/* | |||||
* Generates a XMSSMT key pair for a given parameter set. | |||||
* Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root] | |||||
* Format pk: [root || PUB_SEED] omitting algo oid. | |||||
*/ | |||||
int xmssmt_core_keypair(const xmss_params *params, | |||||
unsigned char *pk, unsigned char *sk, | |||||
bds_state *states, unsigned char *wots_sigs); | |||||
/** | |||||
* Signs a message. | |||||
* Returns | |||||
* 1. an array containing the signature followed by the message AND | |||||
* 2. an updated secret key! | |||||
*/ | |||||
int xmssmt_core_sign(const xmss_params *params, | |||||
unsigned char *sk, | |||||
bds_state *states, unsigned char *wots_sigs, | |||||
unsigned char *sm, unsigned long long *smlen, | |||||
const unsigned char *m, unsigned long long mlen); | |||||
/** | |||||
* Verifies a given message signature pair under a given public key. | |||||
*/ | |||||
int xmssmt_core_sign_open(const xmss_params *params, | |||||
unsigned char *m, unsigned long long *mlen, | |||||
const unsigned char *sm, unsigned long long smlen, | |||||
const unsigned char *pk); | |||||
#endif |