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 | |||
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 \ | |||
test/xmss_determinism \ | |||
test/xmss \ | |||
test/xmss_fast \ | |||
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/xmss_sign \ | |||
@@ -25,14 +21,12 @@ UI = ui/xmss_keypair \ | |||
ui/xmssmt_keypair \ | |||
ui/xmssmt_sign \ | |||
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 | |||
@@ -58,12 +52,6 @@ test/xmssmt_fast: test/xmss.c $(SOURCES_FAST) $(OBJS) $(HEADERS_FAST) | |||
test/xmssmt: test/xmss.c $(SOURCES) $(OBJS) $(HEADERS) | |||
$(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) | |||
$(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. | |||
_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 | |||
@@ -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 "wots.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, | |||
@@ -351,7 +512,7 @@ static void bds_round(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. | |||
*/ | |||
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}; | |||
// 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 | |||
sk[0] = 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); | |||
// 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 | |||
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; | |||
} | |||
@@ -389,7 +564,7 @@ int xmss_core_keypair(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, | |||
const unsigned char *m, unsigned long long mlen) | |||
{ | |||
@@ -397,6 +572,14 @@ int xmss_core_sign(const xmss_params *params, | |||
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 | |||
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]; | |||
@@ -479,11 +662,11 @@ int xmss_core_sign(const xmss_params *params, | |||
*smlen += params->wots_sig_bytes; | |||
// 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) { | |||
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; | |||
@@ -492,6 +675,9 @@ int xmss_core_sign(const xmss_params *params, | |||
memcpy(sm, m, mlen); | |||
*smlen += mlen; | |||
/* Write the updated BDS state back into sk. */ | |||
xmss_serialize_state(params, sk, &state); | |||
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) | |||
{ | |||
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. | |||
*/ | |||
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]; | |||
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | |||
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 | |||
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 | |||
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); | |||
xmssmt_serialize_state(params, sk, states); | |||
return 0; | |||
} | |||
@@ -552,7 +766,6 @@ int xmssmt_core_keypair(const xmss_params *params, | |||
*/ | |||
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) | |||
{ | |||
@@ -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 ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | |||
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 | |||
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) { | |||
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_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); | |||
*smlen += mlen; | |||
xmssmt_serialize_state(params, sk, states); | |||
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 |