Refactor to use compile-time parameter sets

This starts a cleanup / refactor, but there is still some low-hanging fruit.
Este commit está contenido en:
Joost Rijneveld 2017-06-02 14:10:24 +02:00
padre d4bc8656e3
commit 1e00c92c18
No se encontró ninguna clave conocida en la base de datos para esta firma
ID de clave GPG: 307BC77F47D58EE2
Se han modificado 14 ficheros con 864 adiciones y 914 borrados

9
.gitignore vendido
Ver fichero

@ -5,5 +5,12 @@ test/test_xmss
test/test_xmss_fast
test/test_xmssmt
test/test_xmssmt_fast
test/test_xmss_XMSS*
test/test_xmss_fast_XMSS*
test/test_xmssmt_XMSSMT*
test/test_xmssmt_fast_XMSSMT*
test/speed
test/gen_testvectors
test/gen_testvectors
params_XMSS_*.h
params_XMSSMT_*.h
params.h

Ver fichero

@ -2,34 +2,45 @@ CC = /usr/bin/gcc
CFLAGS = -Wall -g -O3 -Wextra
all: test/test_wots \
test/test_xmss \
test/test_xmss_fast \
test/test_xmssmt_fast \
test/test_xmssmt
test/test_wots: hash.c hash_address.c randombytes.c wots.c xmss_commons.c test/test_wots.c hash.h hash_address.h randombytes.h wots.h xmss_commons.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_commons.c test/test_wots.c -o $@ -lcrypto -lm
test/test_xmss: hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c hash.h hash_address.h randombytes.h wots.h xmss.h xmss_commons.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c -o $@ -lcrypto -lm
test/test_xmss_fast: hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmss_fast.c hash.h hash_address.h randombytes.h wots.h xmss_fast.h xmss_commons.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmss_fast.c -o $@ -lcrypto -lm
test/test_xmssmt: hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmssmt.c hash.h hash_address.h randombytes.h wots.h xmss.h xmss_commons.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmssmt.c -o $@ -lcrypto -lm
test/test_xmssmt_fast: hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmssmt_fast.c hash.h hash_address.h randombytes.h wots.h xmss_fast.h xmss_commons.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmssmt_fast.c -o $@ -lcrypto -lm
test/test_xmss_XMSS_SHA2-256_W16_H10 \
test/test_xmss_fast_XMSS_SHA2-256_W16_H10 \
test/test_xmssmt_fast_XMSSMT_SHA2-256_W16_H20_D4 \
test/test_xmssmt_XMSSMT_SHA2-256_W16_H20_D4
.PHONY: clean
.PRECIOUS: params_%.h
params_%.h: params.h.py
python3 params.h.py $(patsubst params_%.h,%,$@) > $@
test/test_wots: params_XMSS_SHA2-256_W16_H10.h hash.c hash_address.c randombytes.c wots.c xmss_commons.c test/test_wots.c hash.h hash_address.h randombytes.h wots.h xmss_commons.h
ln -sf params_XMSS_SHA2-256_W16_H10.h params.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_commons.c test/test_wots.c -o $@ -lcrypto -lm
test/test_xmss_XMSS_%: params_XMSS_%.h hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c hash.h hash_address.h randombytes.h wots.h xmss.h xmss_commons.h
ln -sf params_XMSS_$(patsubst test/test_xmss_XMSS_%,%,$@).h params.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c -o $@ -lcrypto -lm
test/test_xmss_fast_XMSS_%: params_XMSS_%.h hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmss_fast.c hash.h hash_address.h randombytes.h wots.h xmss_fast.h xmss_commons.h
ln -sf params_XMSS_$(patsubst test/test_xmss_fast_XMSS_%,%,$@).h params.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmss_fast.c -o $@ -lcrypto -lm
test/test_xmssmt_XMSSMT_%: params_XMSSMT_%.h hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmssmt.c hash.h hash_address.h randombytes.h wots.h xmss.h xmss_commons.h
ln -sf params_XMSSMT_$(patsubst test/test_xmssmt_XMSSMT_%,%,$@).h params.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmssmt.c -o $@ -lcrypto -lm
test/test_xmssmt_fast_XMSSMT_%: params_XMSSMT_%.h hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmssmt_fast.c hash.h hash_address.h randombytes.h wots.h xmss_fast.h xmss_commons.h
ln -sf params_XMSSMT_$(patsubst test/test_xmssmt_fast_XMSSMT_%,%,$@).h params.h
$(CC) $(CFLAGS) hash.c hash_address.c randombytes.c wots.c xmss_fast.c xmss_commons.c test/test_xmssmt_fast.c -o $@ -lcrypto -lm
clean:
-rm *.o *.s
-rm test/test_wots
-rm test/test_xmss
-rm test/test_xmss_fast
-rm test/test_xmssmt
-rm test/test_xmssmt_fast
-rm test/test_xmss_XMSS*
-rm test/test_xmss_fast_XMSS*
-rm test/test_xmssmt_XMSS*
-rm test/test_xmssmt_fast_XMSS*
distclean:
-rm params.h
-rm params_XMSS*.h

172
params.h.py Archivo normal
Ver fichero

@ -0,0 +1,172 @@
#! /usr/bin/env python3
# This script generates params.h files for the XMSS and XMSSMT parameter sets.
# It takes a single parameter, namely the name of the parameter set.
# Its output matches the following parameter tables.
# +-----------------------+-----------+----+----+-----+----+
# | Name | Functions | n | w | len | h |
# +-----------------------+-----------+----+----+-----+----+
# | REQUIRED: | | | | | |
# | | | | | | |
# | XMSS_SHA2-256_W16_H10 | SHA2-256 | 32 | 16 | 67 | 10 |
# | | | | | | |
# | XMSS_SHA2-256_W16_H16 | SHA2-256 | 32 | 16 | 67 | 16 |
# | | | | | | |
# | XMSS_SHA2-256_W16_H20 | SHA2-256 | 32 | 16 | 67 | 20 |
# | | | | | | |
# | OPTIONAL: | | | | | |
# | | | | | | |
# | XMSS_SHA2-512_W16_H10 | SHA2-512 | 64 | 16 | 131 | 10 |
# | | | | | | |
# | XMSS_SHA2-512_W16_H16 | SHA2-512 | 64 | 16 | 131 | 16 |
# | | | | | | |
# | XMSS_SHA2-512_W16_H20 | SHA2-512 | 64 | 16 | 131 | 20 |
# | | | | | | |
# | XMSS_SHAKE128_W16_H10 | SHAKE128 | 32 | 16 | 67 | 10 |
# | | | | | | |
# | XMSS_SHAKE128_W16_H16 | SHAKE128 | 32 | 16 | 67 | 16 |
# | | | | | | |
# | XMSS_SHAKE128_W16_H20 | SHAKE128 | 32 | 16 | 67 | 20 |
# | | | | | | |
# | XMSS_SHAKE256_W16_H10 | SHAKE256 | 64 | 16 | 131 | 10 |
# | | | | | | |
# | XMSS_SHAKE256_W16_H16 | SHAKE256 | 64 | 16 | 131 | 16 |
# | | | | | | |
# | XMSS_SHAKE256_W16_H20 | SHAKE256 | 64 | 16 | 131 | 20 |
# +-----------------------+-----------+----+----+-----+----+
# +-----------------------------+-----------+----+----+-----+----+----+
# | Name | Functions | n | w | len | h | d |
# +-----------------------------+-----------+----+----+-----+----+----+
# | REQUIRED: | | | | | | |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H20_D2 | SHA2-256 | 32 | 16 | 67 | 20 | 2 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H20_D4 | SHA2-256 | 32 | 16 | 67 | 20 | 4 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H40_D2 | SHA2-256 | 32 | 16 | 67 | 40 | 2 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H40_D4 | SHA2-256 | 32 | 16 | 67 | 40 | 4 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H40_D8 | SHA2-256 | 32 | 16 | 67 | 40 | 8 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H60_D3 | SHA2-256 | 32 | 16 | 67 | 60 | 3 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H60_D6 | SHA2-256 | 32 | 16 | 67 | 60 | 6 |
# | | | | | | | |
# | XMSSMT_SHA2-256_W16_H60_D12 | SHA2-256 | 32 | 16 | 67 | 60 | 12 |
# | | | | | | | |
# | OPTIONAL: | | | | | | |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H20_D2 | SHA2-512 | 64 | 16 | 131 | 20 | 2 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H20_D4 | SHA2-512 | 64 | 16 | 131 | 20 | 4 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H40_D2 | SHA2-512 | 64 | 16 | 131 | 40 | 2 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H40_D4 | SHA2-512 | 64 | 16 | 131 | 40 | 4 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H40_D8 | SHA2-512 | 64 | 16 | 131 | 40 | 8 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H60_D3 | SHA2-512 | 64 | 16 | 131 | 60 | 3 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H60_D6 | SHA2-512 | 64 | 16 | 131 | 60 | 6 |
# | | | | | | | |
# | XMSSMT_SHA2-512_W16_H60_D12 | SHA2-512 | 64 | 16 | 131 | 60 | 12 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H20_D2 | SHAKE128 | 32 | 16 | 67 | 20 | 2 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H20_D4 | SHAKE128 | 32 | 16 | 67 | 20 | 4 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H40_D2 | SHAKE128 | 32 | 16 | 67 | 40 | 2 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H40_D4 | SHAKE128 | 32 | 16 | 67 | 40 | 4 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H40_D8 | SHAKE128 | 32 | 16 | 67 | 40 | 8 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H60_D3 | SHAKE128 | 32 | 16 | 67 | 60 | 3 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H60_D6 | SHAKE128 | 32 | 16 | 67 | 60 | 6 |
# | | | | | | | |
# | XMSSMT_SHAKE128_W16_H60_D12 | SHAKE128 | 32 | 16 | 67 | 60 | 12 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H20_D2 | SHAKE256 | 64 | 16 | 131 | 20 | 2 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H20_D4 | SHAKE256 | 64 | 16 | 131 | 20 | 4 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H40_D2 | SHAKE256 | 64 | 16 | 131 | 40 | 2 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H40_D4 | SHAKE256 | 64 | 16 | 131 | 40 | 4 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H40_D8 | SHAKE256 | 64 | 16 | 131 | 40 | 8 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H60_D3 | SHAKE256 | 64 | 16 | 131 | 60 | 3 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H60_D6 | SHAKE256 | 64 | 16 | 131 | 60 | 6 |
# | | | | | | | |
# | XMSSMT_SHAKE256_W16_H60_D12 | SHAKE256 | 64 | 16 | 131 | 60 | 12 |
# +-----------------------------+-----------+----+----+-----+----+----+
import sys
from math import log2, ceil, floor
if len(sys.argv) != 2:
print("Please supply a parameter identifier.", file=sys.stderr)
sys.exit(1)
param = sys.argv[1].split('_')
print("#ifndef PARAMS_H")
print("#define PARAMS_H")
print("")
print("// This file was automatically generated using params.h.py.")
print("// It matches the parameter set defined as", sys.argv[1], end=".\n")
functions = ["SHA2-256", "SHA2-512", "SHAKE128", "SHAKE256"]
nvalues = {
"SHA2-256": 32,
"SHA2-512": 64,
"SHAKE128": 32,
"SHAKE256": 64,
}
for i, func in enumerate(functions):
print("#define XMSS_{} {}".format(func.replace('-', '_'), i))
print("#define XMSS_FUNC", functions.index(param[1]))
XMSS_N = int(nvalues[param[1]])
print("#define XMSS_N", XMSS_N)
XMSS_WOTS_W = int(param[2][1:])
print("#define XMSS_WOTS_W", XMSS_WOTS_W)
WOTS_LOG_W = int(log2(int(param[2][1:])))
WOTS_LEN1 = ceil(((8*XMSS_N) / WOTS_LOG_W))
WOTS_LEN2 = floor(log2(WOTS_LEN1*(XMSS_WOTS_W-1)) / WOTS_LOG_W) + 1
print("#define XMSS_WOTS_LOG_W", WOTS_LOG_W)
print("#define XMSS_WOTS_LEN1", WOTS_LEN1)
print("#define XMSS_WOTS_LEN2", WOTS_LEN2)
print("#define XMSS_WOTS_LEN", WOTS_LEN1 + WOTS_LEN2)
WOTS_KEYSIZE = (WOTS_LEN1 + WOTS_LEN2) * XMSS_N
print("#define XMSS_WOTS_KEYSIZE", WOTS_KEYSIZE)
XMSS_H = int(param[3][1:])
print("#define XMSS_FULLHEIGHT", XMSS_H)
if param[0] == 'XMSSMT':
XMSS_D = int(param[4][1:])
XMSS_INDEX_LEN = floor((XMSS_H + 7) / 8)
else:
XMSS_INDEX_LEN = 4 # TODO fix this in the xmss code
XMSS_D = 1
if int(param[3][1:]) % XMSS_D != 0:
print("Make sure that d divides h!", file=sys.stderr)
sys.exit(1)
print("#define XMSS_TREEHEIGHT", XMSS_H // XMSS_D)
print("#define XMSS_D", XMSS_D)
print("#define XMSS_INDEX_LEN", XMSS_INDEX_LEN)
XMSS_BYTES = XMSS_INDEX_LEN + XMSS_N + XMSS_D*WOTS_KEYSIZE + XMSS_H*XMSS_N;
print("#define XMSS_BYTES", XMSS_BYTES)
print("#define XMSS_PUBLICKEY_BYTES", 2*XMSS_N)
print("#define XMSS_PRIVATEKEY_BYTES", 4*XMSS_N + XMSS_INDEX_LEN)
print("#define XMSS_BDS_K", 2 + ((XMSS_H // XMSS_D) % 2)) # TODO figure out what we should do here
print("#endif")

Ver fichero

@ -3,6 +3,7 @@
#include <stdint.h>
#include "../wots.h"
#include "../randombytes.h"
#include "../params.h"
static void hexdump(unsigned char *a, size_t len)
{
@ -13,30 +14,27 @@ static void hexdump(unsigned char *a, size_t len)
int main()
{
int n = 32;
unsigned char seed[n];
unsigned char pub_seed[n];
wots_params params;
wots_set_params(&params, n, 16);
unsigned char seed[XMSS_N];
unsigned char pub_seed[XMSS_N];
int sig_len = params.len*params.n;
int sig_len = XMSS_WOTS_LEN*XMSS_N;
unsigned char pk1[sig_len];
unsigned char pk2[sig_len];
unsigned char sig[sig_len];
uint32_t addr[8] = {1,2,3,4};
unsigned char msg[n];
unsigned char msg[XMSS_N];
int i;
randombytes(seed, n);
randombytes(pub_seed, n);
randombytes(msg, n);
randombytes(seed, XMSS_N);
randombytes(pub_seed, XMSS_N);
randombytes(msg, XMSS_N);
//randombytes(addr, 16);
wots_pkgen(pk1, seed, &params, pub_seed, addr);
wots_sign(sig, msg, seed, &params, pub_seed, addr);
wots_pkFromSig(pk2, sig, msg, &params, pub_seed, addr);
wots_pkgen(pk1, seed, pub_seed, addr);
wots_sign(sig, msg, seed, pub_seed, addr);
wots_pkFromSig(pk2, sig, msg, pub_seed, addr);
for (i = 0; i < sig_len; i++)
if (pk1[i] != pk2[i]) {

Ver fichero

@ -2,6 +2,8 @@
#include <string.h>
#include "../xmss.h"
#include "../params.h"
#include "../randombytes.h"
#define MLEN 3491
#define SIGNATURES 50
@ -13,32 +15,22 @@ unsigned long long mlen;
int main()
{
int r;
unsigned long long i;
unsigned int n = 32;
unsigned int h = 8;
unsigned int w = 16;
unsigned long long i, j;
unsigned long errors = 0;
unsigned char sk[4*n+4];
unsigned char pk[2*n];
unsigned char sk[4*XMSS_N+4];
unsigned char pk[2*XMSS_N];
xmss_params p;
xmss_params *params = &p;
xmss_set_params(params, n, h, w);
unsigned long long signature_length = 4+n+params->wots_par.keysize+h*n;
unsigned long long signature_length = 4+XMSS_N+XMSS_WOTS_KEYSIZE+XMSS_TREEHEIGHT*XMSS_N;
unsigned char mo[MLEN+signature_length];
unsigned char sm[MLEN+signature_length];
FILE *urandom = fopen("/dev/urandom", "r");
for (i = 0; i < MLEN; i++) mi[i] = fgetc(urandom);
printf("keypair\n");
xmss_keypair(pk, sk, params);
xmss_keypair(pk, sk);
// check pub_seed in SK
for (i = 0; i < n; i++) {
if (pk[n+i] != sk[4+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[4+3*n+i]) printf("pk.root != sk.root %llu",i);
for (i = 0; i < XMSS_N; i++) {
if (pk[XMSS_N+i] != sk[4+2*XMSS_N+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[4+3*XMSS_N+i]) printf("pk.root != sk.root %llu",i);
}
// check index
@ -46,17 +38,24 @@ int main()
if (idx) printf("\nidx != 0 %lu\n",idx);
for (i = 0; i < SIGNATURES; i++) {
randombytes(mi, MLEN);
printf("sign\n");
xmss_sign(sk, sm, &smlen, mi, MLEN, params);
xmss_sign(sk, 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);
for (j = 0; j < smlen; j++) {
printf("%02X", sm[j]);
}
printf("\n");
r = memcmp(mi, sm+signature_length,MLEN);
printf("%d\n", r);
/* Test valid signature */
printf("verify\n");
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r);
if (r != 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -65,7 +64,7 @@ int main()
/* Test with modified message */
sm[signature_length+10] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -76,7 +75,7 @@ int main()
/* Modified index */
sm[signature_length+10] ^= 1;
sm[2] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -86,7 +85,7 @@ int main()
/* Modified R */
sm[2] ^= 1;
sm[5] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -96,7 +95,7 @@ int main()
/* Modified OTS sig */
sm[5] ^= 1;
sm[240] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -106,7 +105,7 @@ int main()
/* Modified AUTH */
sm[240] ^= 1;
sm[signature_length - 10] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -115,7 +114,6 @@ int main()
}
printf("#errors = %lu\n", errors);
fclose(urandom);
return 0;
}

Ver fichero

@ -3,14 +3,12 @@
#include <stdlib.h>
#include "../xmss_fast.h"
#include "../params.h"
#include "../randombytes.h"
#define MLEN 3491
#define SIGNATURES 256
unsigned char mi[MLEN];
unsigned long long smlen;
unsigned long long mlen;
unsigned long long t1, t2;
unsigned long long cpucycles(void)
@ -24,55 +22,48 @@ int main()
{
int r;
unsigned long long i;
unsigned int n = 32;
unsigned int h = 8;
unsigned int w = 16;
unsigned int k = 2;
unsigned int k = XMSS_BDS_K;
unsigned long errors = 0;
unsigned char sk[4*n+4];
unsigned char pk[2*n];
xmss_params p;
xmss_params *params = &p;
xmss_set_params(params, n, h, w, k);
unsigned char sk[4*XMSS_N+4];
unsigned char pk[2*XMSS_N];
// TODO should we hide this into xmss_fast.c and just allocate a large enough chunk of memory here?
unsigned char stack[(h+1)*n];
unsigned char stack[(XMSS_TREEHEIGHT+1)*XMSS_N];
unsigned int stackoffset = 0;
unsigned char stacklevels[h+1];
unsigned char auth[(h)*n];
unsigned char keep[(h >> 1)*n];
treehash_inst treehash[h-k];
unsigned char th_nodes[(h-k)*n];
unsigned char retain[((1 << k) - k - 1)*n];
unsigned char stacklevels[XMSS_TREEHEIGHT+1];
unsigned char auth[(XMSS_TREEHEIGHT)*XMSS_N];
unsigned char keep[(XMSS_TREEHEIGHT >> 1)*XMSS_N];
treehash_inst treehash[XMSS_TREEHEIGHT-k];
unsigned char th_nodes[(XMSS_TREEHEIGHT-k)*XMSS_N];
unsigned char retain[((1 << k) - k - 1)*XMSS_N];
bds_state s;
bds_state *state = &s;
for (i = 0; i < h-k; i++)
treehash[i].node = &th_nodes[n*i];
for (i = 0; i < XMSS_TREEHEIGHT-k; i++)
treehash[i].node = &th_nodes[XMSS_N*i];
xmss_set_bds_state(state, stack, stackoffset, stacklevels, auth, keep, treehash, retain, 0);
unsigned long long signature_length = 4+n+params->wots_par.keysize+h*n;
unsigned long long signature_length = 4+XMSS_N+XMSS_WOTS_KEYSIZE+XMSS_TREEHEIGHT*XMSS_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;
FILE *urandom = fopen("/dev/urandom", "r");
for (i = 0; i < MLEN; i++) mi[i] = fgetc(urandom);
randombytes(mi, MLEN);
printf("keypair\n");
t1 = cpucycles();
xmss_keypair(pk, sk, state, params);
xmss_keypair(pk, sk, state);
t2 = cpucycles();
printf("cycles = %llu\n", (t2-t1));
double sec = (t2-t1)/3500000;
printf("ms = %f\n", sec);
int read;
read = fgetc(stdin);
// check pub_seed in SK
for (i = 0; i < n; i++) {
if (pk[n+i] != sk[4+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[4+3*n+i]) printf("pk.root != sk.root %llu",i);
for (i = 0; i < XMSS_N; i++) {
if (pk[XMSS_N+i] != sk[4+2*XMSS_N+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[4+3*XMSS_N+i]) printf("pk.root != sk.root %llu",i);
}
// check index
@ -81,7 +72,7 @@ int main()
for (i = 0; i < SIGNATURES; i++) {
printf("sign\n");
xmss_sign(sk, state, sm, &smlen, mi, MLEN, params);
xmss_sign(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);
@ -90,7 +81,7 @@ int main()
/* Test valid signature */
printf("verify\n");
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r);
if (r != 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -99,7 +90,7 @@ int main()
/* Test with modified message */
sm[signature_length+10] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -110,7 +101,7 @@ int main()
/* Modified index */
sm[signature_length+10] ^= 1;
sm[2] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -120,7 +111,7 @@ int main()
/* Modified R */
sm[2] ^= 1;
sm[5] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -130,7 +121,7 @@ int main()
/* Modified OTS sig */
sm[5] ^= 1;
sm[240] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -140,7 +131,7 @@ int main()
/* Modified AUTH */
sm[240] ^= 1;
sm[signature_length - 10] ^= 1;
r = xmss_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmss_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
if (r == 0) errors++;
r = memcmp(mi,mo,MLEN);
@ -150,7 +141,5 @@ int main()
printf("#errors = %lu\n", errors);
printf("finished loop\n");
fclose(urandom);
printf("closed urandom\n");
return 0;
}

Ver fichero

@ -2,9 +2,11 @@
#include <string.h>
#include "../xmss.h"
#include "../params.h"
#include "../randombytes.h"
#define MLEN 3491
#define SIGNATURES 1024
#define SIGNATURES 5
unsigned char mi[MLEN];
unsigned long long smlen;
@ -14,35 +16,24 @@ int main()
{
int r;
unsigned long long i,j;
unsigned int n = 32;
unsigned int h = 20;
unsigned int d = 5;
unsigned int w = 16;
xmssmt_params p;
xmssmt_params *params = &p;
xmssmt_set_params(params, n, h, d, w);
unsigned char sk[(XMSS_INDEX_LEN+4*XMSS_N)];
unsigned char pk[2*XMSS_N];
unsigned char sk[(params->index_len+4*n)];
unsigned char pk[2*n];
unsigned long long signature_length = params->index_len + n + (d*params->xmss_par.wots_par.keysize) + h*n;
unsigned long long signature_length = XMSS_INDEX_LEN + XMSS_N + (XMSS_D*XMSS_WOTS_KEYSIZE) + XMSS_FULLHEIGHT*XMSS_N;
unsigned char mo[MLEN+signature_length];
unsigned char sm[MLEN+signature_length];
FILE *urandom = fopen("/dev/urandom", "r");
for (i = 0; i < MLEN; i++) mi[i] = fgetc(urandom);
printf("keypair\n");
xmssmt_keypair(pk, sk, params);
xmssmt_keypair(pk, sk);
// check pub_seed in SK
for (i = 0; i < n; i++) {
if (pk[n+i] != sk[params->index_len+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[params->index_len+3*n+i]) printf("pk.root != sk.root %llu",i);
for (i = 0; i < XMSS_N; i++) {
if (pk[XMSS_N+i] != sk[XMSS_INDEX_LEN+2*XMSS_N+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[XMSS_INDEX_LEN+3*XMSS_N+i]) printf("pk.root != sk.root %llu",i);
}
printf("pk checked\n");
unsigned int idx_len = params->index_len;
unsigned int idx_len = XMSS_INDEX_LEN;
// check index
unsigned long long idx = 0;
for (i = 0; i < idx_len; i++) {
@ -52,8 +43,10 @@ int main()
if (idx) printf("\nidx != 0: %llu\n",idx);
for (i = 0; i < SIGNATURES; i++) {
randombytes(mi, MLEN);
printf("sign\n");
xmssmt_sign(sk, sm, &smlen, mi, MLEN, params);
xmssmt_sign(sk, sm, &smlen, mi, MLEN);
idx = 0;
for (j = 0; j < idx_len; j++) {
idx += ((unsigned long long)sm[j]) << 8*(idx_len - 1 - j);
@ -62,9 +55,14 @@ int main()
r = memcmp(mi, sm+signature_length,MLEN);
printf("%d\n", r);
for (j = 0; j < smlen; j++) {
printf("%02X", sm[j]);
}
printf("\n");
/* Test valid signature */
printf("verify\n");
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r);
r = memcmp(mi,mo,MLEN);
printf("%d\n", r);
@ -72,7 +70,7 @@ int main()
/* Test with modified message */
sm[52] ^= 1;
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
r = memcmp(mi,mo,MLEN);
printf("%d\n", (r!=0) - 1);
@ -82,13 +80,12 @@ int main()
sm[260] ^= 1;
sm[52] ^= 1;
sm[2] ^= 1;
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmssmt_sign_open(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);
}
fclose(urandom);
return 0;
}

Ver fichero

@ -2,29 +2,33 @@
#include <string.h>
#include "../xmss_fast.h"
#include "../params.h"
#include "../randombytes.h"
#define MLEN 3491
#define SIGNATURES 4096
#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()
{
int r;
unsigned long long i,j;
unsigned int n = 32;
unsigned int h = 12;
unsigned int d = 3;
unsigned int w = 16;
unsigned int k = 2;
xmssmt_params p;
xmssmt_params *params = &p;
if (xmssmt_set_params(params, n, h, d, w, k)) {
return 1;
}
unsigned int n = XMSS_N;
unsigned int h = XMSS_FULLHEIGHT;
unsigned int d = XMSS_D;
unsigned int k = XMSS_BDS_K;
unsigned int tree_h = h / d;
@ -36,7 +40,7 @@ int main()
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 * params->xmss_par.wots_par.keysize];
unsigned char wots_sigs[d * XMSS_WOTS_KEYSIZE];
// first d are 'regular' states, second d are 'next'; top tree has no 'next'
bds_state states[2*d-1];
@ -53,26 +57,25 @@ int main()
);
}
unsigned char sk[(params->index_len+4*n)];
unsigned char sk[(XMSS_INDEX_LEN+4*n)];
unsigned char pk[2*n];
unsigned long long signature_length = params->index_len + n + (d*params->xmss_par.wots_par.keysize) + h*n;
unsigned long long signature_length = XMSS_INDEX_LEN + n + (d*XMSS_WOTS_KEYSIZE) + h*n;
unsigned char mo[MLEN+signature_length];
unsigned char sm[MLEN+signature_length];
FILE *urandom = fopen("/dev/urandom", "r");
for (i = 0; i < MLEN; i++) mi[i] = fgetc(urandom);
randombytes(mi, MLEN);
printf("keypair\n");
xmssmt_keypair(pk, sk, states, wots_sigs, params);
xmssmt_keypair(pk, sk, states, wots_sigs);
// check pub_seed in SK
for (i = 0; i < n; i++) {
if (pk[n+i] != sk[params->index_len+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[params->index_len+3*n+i]) printf("pk.root != sk.root %llu",i);
if (pk[n+i] != sk[XMSS_INDEX_LEN+2*n+i]) printf("pk.pub_seed != sk.pub_seed %llu",i);
if (pk[i] != sk[XMSS_INDEX_LEN+3*n+i]) printf("pk.root != sk.root %llu",i);
}
printf("pk checked\n");
unsigned int idx_len = params->index_len;
unsigned int idx_len = XMSS_INDEX_LEN;
// check index
unsigned long long idx = 0;
for (i = 0; i < idx_len; i++) {
@ -83,7 +86,10 @@ int main()
for (i = 0; i < SIGNATURES; i++) {
printf("sign\n");
xmssmt_sign(sk, states, wots_sigs, sm, &smlen, mi, MLEN, params);
t1 = cpucycles();
xmssmt_sign(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++) {
@ -95,7 +101,10 @@ int main()
/* Test valid signature */
printf("verify\n");
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
t1 = cpucycles();
r = xmssmt_sign_open(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);
@ -103,7 +112,7 @@ int main()
/* Test with modified message */
sm[52] ^= 1;
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk);
printf("%d\n", r+1);
r = memcmp(mi,mo,MLEN);
printf("%d\n", (r!=0) - 1);
@ -113,13 +122,12 @@ int main()
sm[260] ^= 1;
sm[52] ^= 1;
sm[2] ^= 1;
r = xmssmt_sign_open(mo, &mlen, sm, smlen, pk, params);
r = xmssmt_sign_open(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);
}
fclose(urandom);
return 0;
}

95
wots.c
Ver fichero

@ -14,31 +14,20 @@ Public domain.
#include "hash.h"
#include "wots.h"
#include "hash_address.h"
void wots_set_params(wots_params *params, int n, int w)
{
params->n = n;
params->w = w;
params->log_w = (int) log2(w);
params->len_1 = (int) ceil(((8*n) / params->log_w));
params->len_2 = (int) floor(log2(params->len_1*(w-1)) / params->log_w) + 1;
params->len = params->len_1 + params->len_2;
params->keysize = params->len*params->n;
}
#include "params.h"
/**
* Helper method for pseudorandom key generation
* Expands an n-byte array into a len*n byte array
* this is done using PRF
*/
static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, const wots_params *params)
static void expand_seed(unsigned char *outseeds, const unsigned char *inseed)
{
uint32_t i = 0;
unsigned char ctr[32];
for(i = 0; i < params->len; i++){
for(i = 0; i < XMSS_WOTS_LEN; i++){
to_byte(ctr, i, 32);
prf((outseeds + (i*params->n)), ctr, inseed, params->n);
prf(outseeds + i*XMSS_N, ctr, inseed, XMSS_N);
}
}
@ -49,15 +38,15 @@ static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, co
* interpretes in as start-th value of the chain
* addr has to contain the address of the chain
*/
static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const unsigned char *pub_seed, uint32_t addr[8])
{
uint32_t i, j;
for (j = 0; j < params->n; j++)
for (j = 0; j < XMSS_N; j++)
out[j] = in[j];
for (i = start; i < (start+steps) && i < params->w; i++) {
for (i = start; i < (start+steps) && i < XMSS_WOTS_W; i++) {
setHashADRS(addr, i);
hash_f(out, out, pub_seed, addr, params->n);
hash_f(out, out, pub_seed, addr, XMSS_N);
}
}
@ -66,7 +55,7 @@ static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int
*
*
*/
static void base_w(int *output, const int out_len, const unsigned char *input, const wots_params *params)
static void base_w(int *output, const int out_len, const unsigned char *input)
{
int in = 0;
int out = 0;
@ -80,84 +69,84 @@ static void base_w(int *output, const int out_len, const unsigned char *input, c
in++;
bits += 8;
}
bits -= params->log_w;
output[out] = (total >> bits) & (params->w - 1);
bits -= XMSS_WOTS_LOG_W;
output[out] = (total >> bits) & (XMSS_WOTS_W - 1);
out++;
}
}
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const unsigned char *pub_seed, uint32_t addr[8])
{
uint32_t i;
expand_seed(pk, sk, params);
for (i=0; i < params->len; i++) {
expand_seed(pk, sk);
for (i=0; i < XMSS_WOTS_LEN; i++) {
setChainADRS(addr, i);
gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr);
gen_chain(pk+i*XMSS_N, pk+i*XMSS_N, 0, XMSS_WOTS_W-1, pub_seed, addr);
}
}
void wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
void wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const unsigned char *pub_seed, uint32_t addr[8])
{
int basew[params->len];
int basew[XMSS_WOTS_LEN];
int csum = 0;
uint32_t i = 0;
base_w(basew, params->len_1, msg, params);
base_w(basew, XMSS_WOTS_LEN1, msg);
for (i=0; i < params->len_1; i++) {
csum += params->w - 1 - basew[i];
for (i=0; i < XMSS_WOTS_LEN1; i++) {
csum += XMSS_WOTS_W - 1 - basew[i];
}
csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
csum = csum << (8 - ((XMSS_WOTS_LEN2 * XMSS_WOTS_LOG_W) % 8));
int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
int len_2_bytes = ((XMSS_WOTS_LEN2 * XMSS_WOTS_LOG_W) + 7) / 8;
unsigned char csum_bytes[len_2_bytes];
to_byte(csum_bytes, csum, len_2_bytes);
int csum_basew[len_2_bytes / params->log_w];
base_w(csum_basew, params->len_2, csum_bytes, params);
int csum_basew[len_2_bytes / XMSS_WOTS_LOG_W];
base_w(csum_basew, XMSS_WOTS_LEN2, csum_bytes);
for (i = 0; i < params->len_2; i++) {
basew[params->len_1 + i] = csum_basew[i];
for (i = 0; i < XMSS_WOTS_LEN2; i++) {
basew[XMSS_WOTS_LEN1 + i] = csum_basew[i];
}
expand_seed(sig, sk, params);
expand_seed(sig, sk);
for (i = 0; i < params->len; i++) {
for (i = 0; i < XMSS_WOTS_LEN; i++) {
setChainADRS(addr, i);
gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr);
gen_chain(sig+i*XMSS_N, sig+i*XMSS_N, 0, basew[i], pub_seed, addr);
}
}
void wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
void wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const unsigned char *pub_seed, uint32_t addr[8])
{
int basew[params->len];
int basew[XMSS_WOTS_LEN];
int csum = 0;
uint32_t i = 0;
base_w(basew, params->len_1, msg, params);
base_w(basew, XMSS_WOTS_LEN1, msg);
for (i=0; i < params->len_1; i++) {
csum += params->w - 1 - basew[i];
for (i=0; i < XMSS_WOTS_LEN1; i++) {
csum += XMSS_WOTS_W - 1 - basew[i];
}
csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
csum = csum << (8 - ((XMSS_WOTS_LEN2 * XMSS_WOTS_LOG_W) % 8));
int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
int len_2_bytes = ((XMSS_WOTS_LEN2 * XMSS_WOTS_LOG_W) + 7) / 8;
unsigned char csum_bytes[len_2_bytes];
to_byte(csum_bytes, csum, len_2_bytes);
int csum_basew[len_2_bytes / params->log_w];
base_w(csum_basew, params->len_2, csum_bytes, params);
int csum_basew[len_2_bytes / XMSS_WOTS_LOG_W];
base_w(csum_basew, XMSS_WOTS_LEN2, csum_bytes);
for (i = 0; i < params->len_2; i++) {
basew[params->len_1 + i] = csum_basew[i];
for (i = 0; i < XMSS_WOTS_LEN2; i++) {
basew[XMSS_WOTS_LEN1 + i] = csum_basew[i];
}
for (i=0; i < params->len; i++) {
for (i=0; i < XMSS_WOTS_LEN; i++) {
setChainADRS(addr, i);
gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr);
gen_chain(pk+i*XMSS_N, sig+i*XMSS_N, basew[i], XMSS_WOTS_W-1-basew[i], pub_seed, addr);
}
}

38
wots.h
Ver fichero

@ -10,50 +10,24 @@ Public domain.
#include "stdint.h"
/**
* WOTS parameter set
*
* Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02
*/
typedef struct {
uint32_t len_1;
uint32_t len_2;
uint32_t len;
uint32_t n;
uint32_t w;
uint32_t log_w;
uint32_t keysize;
} wots_params;
/**
* Set the WOTS parameters,
* only m, n, w are required as inputs,
* len, len_1, and len_2 are computed from those.
*
* Assumes w is a power of 2
*/
void wots_set_params(wots_params *params, int n, int w);
/**
* WOTS key generation. Takes a 32byte seed for the secret key, expands it to a full WOTS secret key and computes the corresponding public key.
* For this it takes the seed pub_seed which is used to generate bitmasks and hash keys and the address of this WOTS key pair addr
*
* params, must have been initialized before using wots_set params for params ! This is not done in this function
*
*
* Places the computed public key at address pk.
*/
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const unsigned char *pub_seed, uint32_t addr[8]);
/**
* Takes a m-byte message and the 32-byte seed for the secret key to compute a signature that is placed at "sig".
*
*
*/
void wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
void wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const unsigned char *pub_seed, uint32_t addr[8]);
/**
* Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk.
*
*
*/
void wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
void wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const unsigned char *pub_seed, uint32_t addr[8]);
#endif

460
xmss.c
Ver fichero

@ -17,89 +17,54 @@ Public domain.
//#include "prg.h"
#include "xmss_commons.h"
#include "hash_address.h"
#include "params.h"
// For testing
#include "stdio.h"
/**
* Used for pseudorandom keygeneration,
* generates the seed for the WOTS keypair at address addr
*
* takes n byte sk_seed and returns n byte seed using 32 byte address addr.
* takes XMSS_N byte sk_seed and returns XMSS_N byte seed using 32 byte address addr.
*/
static void get_seed(unsigned char *seed, const unsigned char *sk_seed, int n, uint32_t addr[8])
static void get_seed(unsigned char *seed, const unsigned char *sk_seed, uint32_t addr[8])
{
unsigned char bytes[32];
// Make sure that chain addr, hash addr, and key bit are 0!
setChainADRS(addr,0);
setHashADRS(addr,0);
setKeyAndMask(addr,0);
setChainADRS(addr, 0);
setHashADRS(addr, 0);
setKeyAndMask(addr, 0);
// Generate pseudorandom value
addr_to_byte(bytes, addr);
prf(seed, bytes, sk_seed, n);
}
/**
* Initialize xmss params struct
* parameter names are the same as in the draft
*/
void xmss_set_params(xmss_params *params, int n, int h, int w)
{
params->h = h;
params->n = n;
wots_params wots_par;
wots_set_params(&wots_par, n, w);
params->wots_par = wots_par;
}
/**
* Initialize xmssmt_params struct
* parameter names are the same as in the draft
*
* Especially h is the total tree height, i.e. the XMSS trees have height h/d
*/
void xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w)
{
if (h % d) {
fprintf(stderr, "d must devide h without remainder!\n");
return;
}
params->h = h;
params->d = d;
params->n = n;
params->index_len = (h + 7) / 8;
xmss_params xmss_par;
xmss_set_params(&xmss_par, n, (h/d), w);
params->xmss_par = xmss_par;
prf(seed, bytes, sk_seed, XMSS_N);
}
/**
* Computes a leaf from a WOTS public key using an L-tree.
*/
static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const unsigned char *pub_seed, uint32_t addr[8])
{
unsigned int l = params->wots_par.len;
unsigned int n = params->n;
unsigned int l = XMSS_WOTS_LEN;
uint32_t i = 0;
uint32_t height = 0;
uint32_t bound;
//ADRS.setTreeHeight(0);
setTreeHeight(addr, height);
while (l > 1) {
bound = l >> 1; //floor(l / 2);
for (i = 0; i < bound; i++) {
//ADRS.setTreeIndex(i);
setTreeIndex(addr, i);
//wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS);
hash_h(wots_pk+i*n, wots_pk+i*2*n, pub_seed, addr, n);
hash_h(wots_pk+i*XMSS_N, wots_pk+i*2*XMSS_N, pub_seed, addr, XMSS_N);
}
//if ( l % 2 == 1 ) {
if (l & 1) {
//pk[floor(l / 2) + 1] = pk[l];
memcpy(wots_pk+(l>>1)*n, wots_pk+(l-1)*n, n);
memcpy(wots_pk+(l>>1)*XMSS_N, wots_pk+(l-1)*XMSS_N, XMSS_N);
//l = ceil(l / 2);
l=(l>>1)+1;
}
@ -112,21 +77,21 @@ static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_param
setTreeHeight(addr, height);
}
//return pk[0];
memcpy(leaf, wots_pk, n);
memcpy(leaf, wots_pk, XMSS_N);
}
/**
* Computes the leaf at a given address. First generates the WOTS key pair, then computes leaf using l_tree. As this happens position independent, we only require that addr encodes the right ltree-address.
*/
static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8])
static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8])
{
unsigned char seed[params->n];
unsigned char pk[params->wots_par.keysize];
unsigned char seed[XMSS_N];
unsigned char pk[XMSS_WOTS_KEYSIZE];
get_seed(seed, sk_seed, params->n, ots_addr);
wots_pkgen(pk, seed, &(params->wots_par), pub_seed, ots_addr);
get_seed(seed, sk_seed, ots_addr);
wots_pkgen(pk, seed, pub_seed, ots_addr);
l_tree(leaf, pk, params, pub_seed, ltree_addr);
l_tree(leaf, pk, pub_seed, ltree_addr);
}
/**
@ -134,15 +99,13 @@ static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, con
* Currently only used for key generation.
*
*/
static void treehash(unsigned char *node, uint16_t height, uint32_t index, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8])
static void treehash(unsigned char *node, uint16_t height, uint32_t index, const unsigned char *sk_seed, const unsigned char *pub_seed, const uint32_t addr[8])
{
uint32_t idx = index;
uint16_t n = params->n;
// use three different addresses because at this point we use all three formats in parallel
uint32_t ots_addr[8];
uint32_t ltree_addr[8];
uint32_t node_addr[8];
uint32_t node_addr[8];
// only copy layer and tree address parts
memcpy(ots_addr, addr, 12);
// type = ots
@ -153,7 +116,7 @@ static void treehash(unsigned char *node, uint16_t height, uint32_t index, const
setType(node_addr, 2);
uint32_t lastnode, i;
unsigned char stack[(height+1)*n];
unsigned char stack[(height+1)*XMSS_N];
uint16_t stacklevels[height+1];
unsigned int stackoffset=0;
@ -162,68 +125,66 @@ static void treehash(unsigned char *node, uint16_t height, uint32_t index, const
for (; idx < lastnode; idx++) {
setLtreeADRS(ltree_addr, idx);
setOTSADRS(ots_addr, idx);
gen_leaf_wots(stack+stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr);
gen_leaf_wots(stack+stackoffset*XMSS_N, sk_seed, pub_seed, ltree_addr, ots_addr);
stacklevels[stackoffset] = 0;
stackoffset++;
while (stackoffset>1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2]) {
setTreeHeight(node_addr, stacklevels[stackoffset-1]);
setTreeIndex(node_addr, (idx >> (stacklevels[stackoffset-1]+1)));
hash_h(stack+(stackoffset-2)*n, stack+(stackoffset-2)*n, pub_seed,
node_addr, n);
hash_h(stack+(stackoffset-2)*XMSS_N, stack+(stackoffset-2)*XMSS_N, pub_seed,
node_addr, XMSS_N);
stacklevels[stackoffset-2]++;
stackoffset--;
}
}
for (i=0; i < n; i++)
for (i=0; i < XMSS_N; i++)
node[i] = stack[i];
}
/**
* Computes a root node given a leaf and an authapth
*/
static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const unsigned char *pub_seed, uint32_t addr[8])
{
unsigned int n = params->n;
uint32_t i, j;
unsigned char buffer[2*n];
unsigned char buffer[2*XMSS_N];
// If leafidx is odd (last bit = 1), current path element is a right child and authpath has to go to the left.
// Otherwise, it is the other way around
if (leafidx & 1) {
for (j = 0; j < n; j++)
buffer[n+j] = leaf[j];
for (j = 0; j < n; j++)
for (j = 0; j < XMSS_N; j++)
buffer[XMSS_N+j] = leaf[j];
for (j = 0; j < XMSS_N; j++)
buffer[j] = authpath[j];
}
else {
for (j = 0; j < n; j++)
for (j = 0; j < XMSS_N; j++)
buffer[j] = leaf[j];
for (j = 0; j < n; j++)
buffer[n+j] = authpath[j];
for (j = 0; j < XMSS_N; j++)
buffer[XMSS_N+j] = authpath[j];
}
authpath += n;
authpath += XMSS_N;
for (i=0; i < params->h-1; i++) {
for (i=0; i < XMSS_TREEHEIGHT-1; i++) {
setTreeHeight(addr, i);
leafidx >>= 1;
setTreeIndex(addr, leafidx);
if (leafidx&1) {
hash_h(buffer+n, buffer, pub_seed, addr, n);
for (j = 0; j < n; j++)
hash_h(buffer+XMSS_N, buffer, pub_seed, addr, XMSS_N);
for (j = 0; j < XMSS_N; j++)
buffer[j] = authpath[j];
}
else {
hash_h(buffer, buffer, pub_seed, addr, n);
for (j = 0; j < n; j++)
buffer[j+n] = authpath[j];
hash_h(buffer, buffer, pub_seed, addr, XMSS_N);
for (j = 0; j < XMSS_N; j++)
buffer[j+XMSS_N] = authpath[j];
}
authpath += n;
authpath += XMSS_N;
}
setTreeHeight(addr, (params->h-1));
setTreeHeight(addr, (XMSS_TREEHEIGHT-1));
leafidx >>= 1;
setTreeIndex(addr, leafidx);
hash_h(root, buffer, pub_seed, addr, n);
hash_h(root, buffer, pub_seed, addr, XMSS_N);
}
/**
@ -231,13 +192,11 @@ static void validate_authpath(unsigned char *root, const unsigned char *leaf, un
* For more efficient algorithms see e.g. the chapter on hash-based signatures in Bernstein, Buchmann, Dahmen. "Post-quantum Cryptography", Springer 2009.
* It returns the authpath in "authpath" with the node on level 0 at index 0.
*/
static void compute_authpath_wots(unsigned char *root, unsigned char *authpath, unsigned long leaf_idx, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, uint32_t addr[8])
static void compute_authpath_wots(unsigned char *root, unsigned char *authpath, unsigned long leaf_idx, const unsigned char *sk_seed, unsigned char *pub_seed, uint32_t addr[8])
{
uint32_t i, j, level;
uint32_t n = params->n;
uint32_t h = params->h;
unsigned char tree[2*(1<<h)*n];
unsigned char tree[2*(1<<XMSS_TREEHEIGHT)*XMSS_N];
uint32_t ots_addr[8];
uint32_t ltree_addr[8];
@ -251,32 +210,32 @@ static void compute_authpath_wots(unsigned char *root, unsigned char *authpath,
setType(node_addr, 2);
// Compute all leaves
for (i = 0; i < (1U << h); i++) {
for (i = 0; i < (1U << XMSS_TREEHEIGHT); i++) {
setLtreeADRS(ltree_addr, i);
setOTSADRS(ots_addr, i);
gen_leaf_wots(tree+((1<<h)*n + i*n), sk_seed, params, pub_seed, ltree_addr, ots_addr);
gen_leaf_wots(tree+((1<<XMSS_TREEHEIGHT)*XMSS_N + i*XMSS_N), sk_seed, pub_seed, ltree_addr, ots_addr);
}
level = 0;
// Compute tree:
// Outer loop: For each inner layer
for (i = (1<<h); i > 1; i>>=1) {
for (i = (1<<XMSS_TREEHEIGHT); i > 1; i>>=1) {
setTreeHeight(node_addr, level);
// Inner loop: for each pair of sibling nodes
for (j = 0; j < i; j+=2) {
setTreeIndex(node_addr, j>>1);
hash_h(tree + (i>>1)*n + (j>>1) * n, tree + i*n + j*n, pub_seed, node_addr, n);
hash_h(tree + (i>>1)*XMSS_N + (j>>1) * XMSS_N, tree + i*XMSS_N + j*XMSS_N, pub_seed, node_addr, XMSS_N);
}
level++;
}
// copy authpath
for (i=0; i < h; i++)
memcpy(authpath + i*n, tree + ((1<<h)>>i)*n + ((leaf_idx >> i) ^ 1) * n, n);
for (i=0; i < XMSS_TREEHEIGHT; i++)
memcpy(authpath + i*XMSS_N, tree + ((1<<XMSS_TREEHEIGHT)>>i)*XMSS_N + ((leaf_idx >> i) ^ 1) * XMSS_N, XMSS_N);
// copy root
memcpy(root, tree+n, n);
memcpy(root, tree+XMSS_N, XMSS_N);
}
@ -285,24 +244,23 @@ static void compute_authpath_wots(unsigned char *root, unsigned char *authpath,
* Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
* Format pk: [root || PUB_SEED] omitting algo oid.
*/
int xmss_keypair(unsigned char *pk, unsigned char *sk, xmss_params *params)
int xmss_keypair(unsigned char *pk, unsigned char *sk)
{
unsigned int n = params->n;
// Set idx = 0
sk[0] = 0;
sk[1] = 0;
sk[2] = 0;
sk[3] = 0;
// Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
randombytes(sk+4, 3*n);
// Init SK_SEED (XMSS_N byte), SK_PRF (XMSS_N byte), and PUB_SEED (XMSS_N byte)
randombytes(sk+4, 3*XMSS_N);
// Copy PUB_SEED to public key
memcpy(pk+n, sk+4+2*n, n);
memcpy(pk+XMSS_N, sk+4+2*XMSS_N, XMSS_N);
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// Compute root
treehash(pk, params->h, 0, sk+4, params, sk+4+2*n, addr);
treehash(pk, XMSS_TREEHEIGHT, 0, sk+4, sk+4+2*XMSS_N, addr);
// copy root to sk
memcpy(sk+4+3*n, pk, n);
memcpy(sk+4+3*XMSS_N, pk, XMSS_N);
return 0;
}
@ -313,26 +271,25 @@ int xmss_keypair(unsigned char *pk, unsigned char *sk, xmss_params *params)
* 2. an updated secret key!
*
*/
int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmss_params *params)
int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen)
{
uint16_t n = params->n;
uint16_t i = 0;
// Extract SK
uint32_t idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3];
unsigned char sk_seed[n];
memcpy(sk_seed, sk+4, n);
unsigned char sk_prf[n];
memcpy(sk_prf, sk+4+n, n);
unsigned char pub_seed[n];
memcpy(pub_seed, sk+4+2*n, n);
unsigned char sk_seed[XMSS_N];
memcpy(sk_seed, sk+4, XMSS_N);
unsigned char sk_prf[XMSS_N];
memcpy(sk_prf, sk+4+XMSS_N, XMSS_N);
unsigned char pub_seed[XMSS_N];
memcpy(pub_seed, sk+4+2*XMSS_N, XMSS_N);
// index as 32 bytes string
unsigned char idx_bytes_32[32];
to_byte(idx_bytes_32, idx, 32);
unsigned char hash_key[3*n];
unsigned char hash_key[3*XMSS_N];
// Update SK
sk[0] = ((idx + 1) >> 24) & 255;
@ -343,10 +300,10 @@ int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig
// -- A productive implementation should use a file handle instead and write the updated secret key at this point!
// Init working params
unsigned char R[n];
unsigned char msg_h[n];
unsigned char root[n];
unsigned char ots_seed[n];
unsigned char R[XMSS_N];
unsigned char msg_h[XMSS_N];
unsigned char root[XMSS_N];
unsigned char ots_seed[XMSS_N];
uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// ---------------------------------
@ -355,13 +312,13 @@ int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig
// Message Hash:
// First compute pseudorandom value
prf(R, idx_bytes_32, sk_prf, n);
prf(R, idx_bytes_32, sk_prf, XMSS_N);
// Generate hash key (R || root || idx)
memcpy(hash_key, R, n);
memcpy(hash_key+n, sk+4+3*n, n);
to_byte(hash_key+2*n, idx, n);
memcpy(hash_key, R, XMSS_N);
memcpy(hash_key+XMSS_N, sk+4+3*XMSS_N, XMSS_N);
to_byte(hash_key+2*XMSS_N, idx, XMSS_N);
// Then use it for message digest
h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
h_msg(msg_h, msg, msglen, hash_key, 3*XMSS_N, XMSS_N);
// Start collecting signature
*sig_msg_len = 0;
@ -376,11 +333,11 @@ int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig
*sig_msg_len += 4;
// Copy R to signature
for (i = 0; i < n; i++)
for (i = 0; i < XMSS_N; i++)
sig_msg[i] = R[i];
sig_msg += n;
*sig_msg_len += n;
sig_msg += XMSS_N;
*sig_msg_len += XMSS_N;
// ----------------------------------
// Now we start to "really sign"
@ -391,17 +348,17 @@ int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig
setOTSADRS(ots_addr, idx);
// Compute seed for OTS key pair
get_seed(ots_seed, sk_seed, n, ots_addr);
get_seed(ots_seed, sk_seed, ots_addr);
// Compute WOTS signature
wots_sign(sig_msg, msg_h, ots_seed, &(params->wots_par), pub_seed, ots_addr);
wots_sign(sig_msg, msg_h, ots_seed, pub_seed, ots_addr);
sig_msg += params->wots_par.keysize;
*sig_msg_len += params->wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
*sig_msg_len += XMSS_WOTS_KEYSIZE;
compute_authpath_wots(root, sig_msg, idx, sk_seed, params, pub_seed, ots_addr);
sig_msg += params->h*n;
*sig_msg_len += params->h*n;
compute_authpath_wots(root, sig_msg, idx, sk_seed, pub_seed, ots_addr);
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
*sig_msg_len += XMSS_TREEHEIGHT*XMSS_N;
//Whipe secret elements?
//zerobytes(tsk, CRYPTO_SECRETKEYBYTES);
@ -415,20 +372,19 @@ int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig
/**
* Verifies a given message signature pair under a given public key.
*/
int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params)
int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk)
{
uint16_t n = params->n;
unsigned long long i, m_len;
unsigned long idx=0;
unsigned char wots_pk[params->wots_par.keysize];
unsigned char pkhash[n];
unsigned char root[n];
unsigned char msg_h[n];
unsigned char hash_key[3*n];
unsigned char wots_pk[XMSS_WOTS_KEYSIZE];
unsigned char pkhash[XMSS_N];
unsigned char root[XMSS_N];
unsigned char msg_h[XMSS_N];
unsigned char hash_key[3*XMSS_N];
unsigned char pub_seed[n];
memcpy(pub_seed, pk+n, n);
unsigned char pub_seed[XMSS_N];
memcpy(pub_seed, pk+XMSS_N, XMSS_N);
// Init addresses
uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -444,18 +400,18 @@ int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigne
printf("verify:: idx = %lu\n", idx);
// Generate hash key (R || root || idx)
memcpy(hash_key, sig_msg+4,n);
memcpy(hash_key+n, pk, n);
to_byte(hash_key+2*n, idx, n);
memcpy(hash_key, sig_msg+4,XMSS_N);
memcpy(hash_key+XMSS_N, pk, XMSS_N);
to_byte(hash_key+2*XMSS_N, idx, XMSS_N);
sig_msg += (n+4);
sig_msg_len -= (n+4);
sig_msg += (XMSS_N+4);
sig_msg_len -= (XMSS_N+4);
// hash message
unsigned long long tmp_sig_len = params->wots_par.keysize+params->h*n;
unsigned long long tmp_sig_len = XMSS_WOTS_KEYSIZE+XMSS_TREEHEIGHT*XMSS_N;
m_len = sig_msg_len - tmp_sig_len;
h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*XMSS_N, XMSS_N);
//-----------------------
// Verify signature
@ -464,22 +420,22 @@ int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigne
// Prepare Address
setOTSADRS(ots_addr, idx);
// Check WOTS signature
wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->wots_par), pub_seed, ots_addr);
wots_pkFromSig(wots_pk, sig_msg, msg_h, pub_seed, ots_addr);
sig_msg += params->wots_par.keysize;
sig_msg_len -= params->wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
sig_msg_len -= XMSS_WOTS_KEYSIZE;
// Compute Ltree
setLtreeADRS(ltree_addr, idx);
l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr);
l_tree(pkhash, wots_pk, pub_seed, ltree_addr);
// Compute root
validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr);
validate_authpath(root, pkhash, idx, sig_msg, pub_seed, node_addr);
sig_msg += params->h*n;
sig_msg_len -= params->h*n;
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
sig_msg_len -= XMSS_TREEHEIGHT*XMSS_N;
for (i=0; i < n; i++)
for (i=0; i < XMSS_N; i++)
if (root[i] != pk[i])
goto fail;
@ -503,26 +459,25 @@ fail:
* Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED]
* Format pk: [root || PUB_SEED] omitting algo oid.
*/
int xmssmt_keypair(unsigned char *pk, unsigned char *sk, xmssmt_params *params)
int xmssmt_keypair(unsigned char *pk, unsigned char *sk)
{
unsigned int n = params->n;
uint16_t i;
// Set idx = 0
for (i = 0; i < params->index_len; i++) {
for (i = 0; i < XMSS_INDEX_LEN; i++) {
sk[i] = 0;
}
// Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
randombytes(sk+params->index_len, 3*n);
// Init SK_SEED (XMSS_N byte), SK_PRF (XMSS_N byte), and PUB_SEED (XMSS_N byte)
randombytes(sk+XMSS_INDEX_LEN, 3*XMSS_N);
// Copy PUB_SEED to public key
memcpy(pk+n, sk+params->index_len+2*n, n);
memcpy(pk+XMSS_N, sk+XMSS_INDEX_LEN+2*XMSS_N, XMSS_N);
// Set address to point on the single tree on layer d-1
uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
setLayerADRS(addr, (params->d-1));
setLayerADRS(addr, (XMSS_D-1));
// Compute root
treehash(pk, params->xmss_par.h, 0, sk+params->index_len, &(params->xmss_par), pk+n, addr);
memcpy(sk+params->index_len+3*n, pk, n);
treehash(pk, XMSS_TREEHEIGHT, 0, sk+XMSS_INDEX_LEN, pk+XMSS_N, addr);
memcpy(sk+XMSS_INDEX_LEN+3*XMSS_N, pk, XMSS_N);
return 0;
}
@ -533,40 +488,37 @@ int xmssmt_keypair(unsigned char *pk, unsigned char *sk, xmssmt_params *params)
* 2. an updated secret key!
*
*/
int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params)
int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen)
{
unsigned int n = params->n;
unsigned int tree_h = params->xmss_par.h;
unsigned int idx_len = params->index_len;
uint64_t idx_tree;
uint32_t idx_leaf;
uint64_t i;
unsigned char sk_seed[n];
unsigned char sk_prf[n];
unsigned char pub_seed[n];
unsigned char sk_seed[XMSS_N];
unsigned char sk_prf[XMSS_N];
unsigned char pub_seed[XMSS_N];
// Init working params
unsigned char R[n];
unsigned char hash_key[3*n];
unsigned char msg_h[n];
unsigned char root[n];
unsigned char ots_seed[n];
unsigned char R[XMSS_N];
unsigned char hash_key[3*XMSS_N];
unsigned char msg_h[XMSS_N];
unsigned char root[XMSS_N];
unsigned char ots_seed[XMSS_N];
uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char idx_bytes_32[32];
// Extract SK
unsigned long long idx = 0;
for (i = 0; i < idx_len; i++) {
idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i);
for (i = 0; i < XMSS_INDEX_LEN; i++) {
idx |= ((unsigned long long)sk[i]) << 8*(XMSS_INDEX_LEN - 1 - i);
}
memcpy(sk_seed, sk+idx_len, n);
memcpy(sk_prf, sk+idx_len+n, n);
memcpy(pub_seed, sk+idx_len+2*n, n);
memcpy(sk_seed, sk+XMSS_INDEX_LEN, XMSS_N);
memcpy(sk_prf, sk+XMSS_INDEX_LEN+XMSS_N, XMSS_N);
memcpy(pub_seed, sk+XMSS_INDEX_LEN+2*XMSS_N, XMSS_N);
// Update SK
for (i = 0; i < idx_len; i++) {
sk[i] = ((idx + 1) >> 8*(idx_len - 1 - i)) & 255;
for (i = 0; i < XMSS_INDEX_LEN; i++) {
sk[i] = ((idx + 1) >> 8*(XMSS_INDEX_LEN - 1 - i)) & 255;
}
// -- Secret key for this non-forward-secure version is now updated.
// -- A productive implementation should use a file handle instead and write the updated secret key at this point!
@ -579,32 +531,32 @@ int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *s
// Message Hash:
// First compute pseudorandom value
to_byte(idx_bytes_32, idx, 32);
prf(R, idx_bytes_32, sk_prf, n);
prf(R, idx_bytes_32, sk_prf, XMSS_N);
// Generate hash key (R || root || idx)
memcpy(hash_key, R, n);
memcpy(hash_key+n, sk+idx_len+3*n, n);
to_byte(hash_key+2*n, idx, n);
memcpy(hash_key, R, XMSS_N);
memcpy(hash_key+XMSS_N, sk+XMSS_INDEX_LEN+3*XMSS_N, XMSS_N);
to_byte(hash_key+2*XMSS_N, idx, XMSS_N);
// Then use it for message digest
h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
h_msg(msg_h, msg, msglen, hash_key, 3*XMSS_N, XMSS_N);
// Start collecting signature
*sig_msg_len = 0;
// Copy index to signature
for (i = 0; i < idx_len; i++) {
sig_msg[i] = (idx >> 8*(idx_len - 1 - i)) & 255;
for (i = 0; i < XMSS_INDEX_LEN; i++) {
sig_msg[i] = (idx >> 8*(XMSS_INDEX_LEN - 1 - i)) & 255;
}
sig_msg += idx_len;
*sig_msg_len += idx_len;
sig_msg += XMSS_INDEX_LEN;
*sig_msg_len += XMSS_INDEX_LEN;
// Copy R to signature
for (i=0; i < n; i++)
for (i=0; i < XMSS_N; i++)
sig_msg[i] = R[i];
sig_msg += n;
*sig_msg_len += n;
sig_msg += XMSS_N;
*sig_msg_len += XMSS_N;
// ----------------------------------
// Now we start to "really sign"
@ -614,47 +566,47 @@ int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *s
// Prepare Address
setType(ots_addr, 0);
idx_tree = idx >> tree_h;
idx_leaf = (idx & ((1 << tree_h)-1));
idx_tree = idx >> XMSS_TREEHEIGHT;
idx_leaf = (idx & ((1 << XMSS_TREEHEIGHT)-1));
setLayerADRS(ots_addr, 0);
setTreeADRS(ots_addr, idx_tree);
setOTSADRS(ots_addr, idx_leaf);
// Compute seed for OTS key pair
get_seed(ots_seed, sk_seed, n, ots_addr);
get_seed(ots_seed, sk_seed, ots_addr);
// Compute WOTS signature
wots_sign(sig_msg, msg_h, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
wots_sign(sig_msg, msg_h, ots_seed, pub_seed, ots_addr);
sig_msg += params->xmss_par.wots_par.keysize;
*sig_msg_len += params->xmss_par.wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
*sig_msg_len += XMSS_WOTS_KEYSIZE;
compute_authpath_wots(root, sig_msg, idx_leaf, sk_seed, &(params->xmss_par), pub_seed, ots_addr);
sig_msg += tree_h*n;
*sig_msg_len += tree_h*n;
compute_authpath_wots(root, sig_msg, idx_leaf, sk_seed, pub_seed, ots_addr);
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
*sig_msg_len += XMSS_TREEHEIGHT*XMSS_N;
// Now loop over remaining layers...
unsigned int j;
for (j = 1; j < params->d; j++) {
for (j = 1; j < XMSS_D; j++) {
// Prepare Address
idx_leaf = (idx_tree & ((1 << tree_h)-1));
idx_tree = idx_tree >> tree_h;
idx_leaf = (idx_tree & ((1 << XMSS_TREEHEIGHT)-1));
idx_tree = idx_tree >> XMSS_TREEHEIGHT;
setLayerADRS(ots_addr, j);
setTreeADRS(ots_addr, idx_tree);
setOTSADRS(ots_addr, idx_leaf);
// Compute seed for OTS key pair
get_seed(ots_seed, sk_seed, n, ots_addr);
get_seed(ots_seed, sk_seed, ots_addr);
// Compute WOTS signature
wots_sign(sig_msg, root, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
wots_sign(sig_msg, root, ots_seed, pub_seed, ots_addr);
sig_msg += params->xmss_par.wots_par.keysize;
*sig_msg_len += params->xmss_par.wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
*sig_msg_len += XMSS_WOTS_KEYSIZE;
compute_authpath_wots(root, sig_msg, idx_leaf, sk_seed, &(params->xmss_par), pub_seed, ots_addr);
sig_msg += tree_h*n;
*sig_msg_len += tree_h*n;
compute_authpath_wots(root, sig_msg, idx_leaf, sk_seed, pub_seed, ots_addr);
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
*sig_msg_len += XMSS_TREEHEIGHT*XMSS_N;
}
//Whipe secret elements?
@ -669,25 +621,21 @@ int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *s
/**
* Verifies a given message signature pair under a given public key.
*/
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params)
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk)
{
unsigned int n = params->n;
unsigned int tree_h = params->xmss_par.h;
unsigned int idx_len = params->index_len;
uint64_t idx_tree;
uint32_t idx_leaf;
unsigned long long i, m_len;
unsigned long long idx=0;
unsigned char wots_pk[params->xmss_par.wots_par.keysize];
unsigned char pkhash[n];
unsigned char root[n];
unsigned char msg_h[n];
unsigned char hash_key[3*n];
unsigned char wots_pk[XMSS_WOTS_KEYSIZE];
unsigned char pkhash[XMSS_N];
unsigned char root[XMSS_N];
unsigned char msg_h[XMSS_N];
unsigned char hash_key[3*XMSS_N];
unsigned char pub_seed[n];
memcpy(pub_seed, pk+n, n);
unsigned char pub_seed[XMSS_N];
memcpy(pub_seed, pk+XMSS_N, XMSS_N);
// Init addresses
uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -695,25 +643,25 @@ int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsig
uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// Extract index
for (i = 0; i < idx_len; i++) {
idx |= ((unsigned long long)sig_msg[i]) << (8*(idx_len - 1 - i));
for (i = 0; i < XMSS_INDEX_LEN; i++) {
idx |= ((unsigned long long)sig_msg[i]) << (8*(XMSS_INDEX_LEN - 1 - i));
}
printf("verify:: idx = %llu\n", idx);
sig_msg += idx_len;
sig_msg_len -= idx_len;
sig_msg += XMSS_INDEX_LEN;
sig_msg_len -= XMSS_INDEX_LEN;
// Generate hash key (R || root || idx)
memcpy(hash_key, sig_msg,n);
memcpy(hash_key+n, pk, n);
to_byte(hash_key+2*n, idx, n);
memcpy(hash_key, sig_msg,XMSS_N);
memcpy(hash_key+XMSS_N, pk, XMSS_N);
to_byte(hash_key+2*XMSS_N, idx, XMSS_N);
sig_msg += n;
sig_msg_len -= n;
sig_msg += XMSS_N;
sig_msg_len -= XMSS_N;
// hash message
unsigned long long tmp_sig_len = (params->d * params->xmss_par.wots_par.keysize) + (params->h * n);
unsigned long long tmp_sig_len = (XMSS_D * XMSS_WOTS_KEYSIZE) + (XMSS_FULLHEIGHT * XMSS_N);
m_len = sig_msg_len - tmp_sig_len;
h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*XMSS_N, XMSS_N);
//-----------------------
@ -721,8 +669,8 @@ int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsig
//-----------------------
// Prepare Address
idx_tree = idx >> tree_h;
idx_leaf = (idx & ((1 << tree_h)-1));
idx_tree = idx >> XMSS_TREEHEIGHT;
idx_leaf = (idx & ((1 << XMSS_TREEHEIGHT)-1));
setLayerADRS(ots_addr, 0);
setTreeADRS(ots_addr, idx_tree);
setType(ots_addr, 0);
@ -736,25 +684,25 @@ int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsig
setOTSADRS(ots_addr, idx_leaf);
// Check WOTS signature
wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->xmss_par.wots_par), pub_seed, ots_addr);
wots_pkFromSig(wots_pk, sig_msg, msg_h, pub_seed, ots_addr);
sig_msg += params->xmss_par.wots_par.keysize;
sig_msg_len -= params->xmss_par.wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
sig_msg_len -= XMSS_WOTS_KEYSIZE;
// Compute Ltree
setLtreeADRS(ltree_addr, idx_leaf);
l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
l_tree(pkhash, wots_pk, pub_seed, ltree_addr);
// Compute root
validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
validate_authpath(root, pkhash, idx_leaf, sig_msg, pub_seed, node_addr);
sig_msg += tree_h*n;
sig_msg_len -= tree_h*n;
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
sig_msg_len -= XMSS_TREEHEIGHT*XMSS_N;
for (i = 1; i < params->d; i++) {
for (i = 1; i < XMSS_D; i++) {
// Prepare Address
idx_leaf = (idx_tree & ((1 << tree_h)-1));
idx_tree = idx_tree >> tree_h;
idx_leaf = (idx_tree & ((1 << XMSS_TREEHEIGHT)-1));
idx_tree = idx_tree >> XMSS_TREEHEIGHT;
setLayerADRS(ots_addr, i);
setTreeADRS(ots_addr, idx_tree);
@ -769,24 +717,24 @@ int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsig
setOTSADRS(ots_addr, idx_leaf);
// Check WOTS signature
wots_pkFromSig(wots_pk, sig_msg, root, &(params->xmss_par.wots_par), pub_seed, ots_addr);
wots_pkFromSig(wots_pk, sig_msg, root, pub_seed, ots_addr);
sig_msg += params->xmss_par.wots_par.keysize;
sig_msg_len -= params->xmss_par.wots_par.keysize;
sig_msg += XMSS_WOTS_KEYSIZE;
sig_msg_len -= XMSS_WOTS_KEYSIZE;
// Compute Ltree
setLtreeADRS(ltree_addr, idx_leaf);
l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
l_tree(pkhash, wots_pk, pub_seed, ltree_addr);
// Compute root
validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
validate_authpath(root, pkhash, idx_leaf, sig_msg, pub_seed, node_addr);
sig_msg += tree_h*n;
sig_msg_len -= tree_h*n;
sig_msg += XMSS_TREEHEIGHT*XMSS_N;
sig_msg_len -= XMSS_TREEHEIGHT*XMSS_N;
}
for (i=0; i < n; i++)
for (i=0; i < XMSS_N; i++)
if (root[i] != pk[i])
goto fail;

37
xmss.h
Ver fichero

@ -15,37 +15,12 @@ typedef struct{
unsigned int subleaf;
} leafaddr;
typedef struct{
wots_params wots_par;
unsigned int n;
unsigned int h;
} xmss_params;
typedef struct{
xmss_params xmss_par;
unsigned int n;
unsigned int h;
unsigned int d;
unsigned int index_len;
} xmssmt_params;
/**
* Initializes parameter set.
* Needed, for any of the other methods.
*/
void xmss_set_params(xmss_params *params, int n, int h, int w);
/**
* Initialize xmssmt_params struct
* parameter names are the same as in the draft
*
* Especially h is the total tree height, i.e. the XMSS trees have height h/d
*/
void xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w);
/**
* 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_keypair(unsigned char *pk, unsigned char *sk, xmss_params *params);
int xmss_keypair(unsigned char *pk, unsigned char *sk);
/**
* Signs a message.
* Returns
@ -53,20 +28,20 @@ int xmss_keypair(unsigned char *pk, unsigned char *sk, xmss_params *params);
* 2. an updated secret key!
*
*/
int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params);
int xmss_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen);
/**
* Verifies a given message signature pair under a given public key.
*
* Note: msg and msglen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sig_msg which has the form (sig||msg).
*/
int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params);
int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk);
/*
* 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_keypair(unsigned char *pk, unsigned char *sk, xmssmt_params *params);
int xmssmt_keypair(unsigned char *pk, unsigned char *sk);
/**
* Signs a message.
* Returns
@ -74,10 +49,10 @@ int xmssmt_keypair(unsigned char *pk, unsigned char *sk, xmssmt_params *params);
* 2. an updated secret key!
*
*/
int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params);
int xmssmt_sign(unsigned char *sk, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen);
/**
* Verifies a given message signature pair under a given public key.
*/
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params);
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk);
#endif

La diferencia del archivo ha sido suprimido porque es demasiado grande Cargar Diff

Ver fichero

@ -15,21 +15,6 @@ typedef struct{
unsigned int subleaf;
} leafaddr;
typedef struct{
wots_params wots_par;
unsigned int n;
unsigned int h;
unsigned int k;
} xmss_params;
typedef struct{
xmss_params xmss_par;
unsigned int n;
unsigned int h;
unsigned int d;
unsigned int index_len;
} xmssmt_params;
typedef struct{
unsigned int h;
unsigned int next_idx;
@ -54,24 +39,12 @@ typedef 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);
/**
* Initializes parameter set.
* Needed, for any of the other methods.
*/
int xmss_set_params(xmss_params *params, int n, int h, int w, int k);
/**
* Initialize xmssmt_params struct
* parameter names are the same as in the draft
*
* Especially h is the total tree height, i.e. the XMSS trees have height h/d
*/
int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k);
/**
* 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_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params);
int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state);
/**
* Signs a message.
* Returns
@ -79,20 +52,20 @@ int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_pa
* 2. an updated secret key!
*
*/
int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params);
int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen);
/**
* Verifies a given message signature pair under a given public key.
*
* Note: msg and msglen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sig_msg which has the form (sig||msg).
*/
int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params);
int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk);
/*
* 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_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params);
int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs);
/**
* Signs a message.
* Returns
@ -100,10 +73,10 @@ int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsi
* 2. an updated secret key!
*
*/
int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params);
int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen);
/**
* Verifies a given message signature pair under a given public key.
*/
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params);
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk);
#endif