From 1826fb26ffbeb99fe2b35090b9bb3ba76e761fac Mon Sep 17 00:00:00 2001 From: Andreas Date: Tue, 11 Aug 2015 12:08:27 +0200 Subject: [PATCH] Initial commit --- .kdev4/xmss_ref.kdev4 | 5 + Makefile | 17 +- chacha.c | 208 ++++++++++++++++ chacha.h | 78 ++++++ hash.c | 143 +++++++++++ hash.h | 11 + main.c | 7 - params.h | 13 + prg.c | 31 +++ prg.h | 16 ++ randombytes.c | 34 +++ randombytes.h | 6 + test/gen_testvectors.c | 92 ++++++++ test/speed.c | 124 ++++++++++ test/test.sh | 6 + test/test_chacha | Bin 0 -> 26613 bytes test/test_chacha.c | 33 +++ test/test_wots | Bin 0 -> 62796 bytes test/test_wots.c | 54 +++++ test/test_xmss | Bin 0 -> 102006 bytes test/test_xmss.c | 83 +++++++ wots.c | 200 ++++++++++++++++ wots.h | 52 ++++ xmss.c | 524 +++++++++++++++++++++++++++++++++++++++++ xmss.h | 45 ++++ xmss_commons.c | 19 ++ xmss_commons.h | 8 + zerobytes.c | 9 + zerobytes.h | 6 + 29 files changed, 1815 insertions(+), 9 deletions(-) create mode 100644 .kdev4/xmss_ref.kdev4 create mode 100644 chacha.c create mode 100644 chacha.h create mode 100644 hash.c create mode 100644 hash.h delete mode 100644 main.c create mode 100644 params.h create mode 100644 prg.c create mode 100644 prg.h create mode 100644 randombytes.c create mode 100644 randombytes.h create mode 100644 test/gen_testvectors.c create mode 100644 test/speed.c create mode 100755 test/test.sh create mode 100755 test/test_chacha create mode 100644 test/test_chacha.c create mode 100755 test/test_wots create mode 100644 test/test_wots.c create mode 100755 test/test_xmss create mode 100644 test/test_xmss.c create mode 100644 wots.c create mode 100644 wots.h create mode 100644 xmss.c create mode 100644 xmss.h create mode 100644 xmss_commons.c create mode 100644 xmss_commons.h create mode 100644 zerobytes.c create mode 100644 zerobytes.h diff --git a/.kdev4/xmss_ref.kdev4 b/.kdev4/xmss_ref.kdev4 new file mode 100644 index 0000000..70b24e9 --- /dev/null +++ b/.kdev4/xmss_ref.kdev4 @@ -0,0 +1,5 @@ +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x10\x00x\x00m\x00s\x00s\x00_\x00r\x00e\x00f) + +[Project] +VersionControlSupport=kdevgit diff --git a/Makefile b/Makefile index b158e36..ac67a30 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,18 @@ -CC=gcc -CFLAGS="-Wall" +CC = /usr/bin/gcc +CFLAGS = -Wall -g -O3 + +all: test/test_chacha \ +test/test_wots \ +test/test_xmss + +test/test_chacha: chacha.c prg.c randombytes.c test/test_chacha.c chacha.h prg.h randombytes.h + $(CC) $(CFLAGS) chacha.c prg.c randombytes.c test/test_chacha.c -o $@ #-lcrypto -lm + +test/test_wots: chacha.c hash.c prg.c randombytes.c wots.c xmss_commons.c test/test_wots.c chacha.h hash.h prg.h randombytes.h wots.h xmss_commons.h + $(CC) $(CFLAGS) chacha.c hash.c prg.c randombytes.c wots.c xmss_commons.c test/test_wots.c -o $@ -lcrypto -lm + +test/test_xmss: chacha.c hash.c prg.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c chacha.h hash.h prg.h randombytes.h wots.h xmss.h xmss_commons.h + $(CC) $(CFLAGS) chacha.c hash.c prg.c randombytes.c wots.c xmss.c xmss_commons.c test/test_xmss.c -o $@ -lcrypto -lm debug:clean $(CC) $(CFLAGS) -g -o xmss_ref main.c diff --git a/chacha.c b/chacha.c new file mode 100644 index 0000000..036fa15 --- /dev/null +++ b/chacha.c @@ -0,0 +1,208 @@ +/* + * This code is based on an OpenSSL implementation of chacha20. + * Hence, the copyright below applies. + * + */ +/* ==================================================================== + * Copyright (c) 2011-2013 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* Adapted from the public domain code by D. Bernstein from SUPERCOP. */ + +#include +#include +#include "chacha.h" + +/* sigma contains the ChaCha constants, which happen to be an ASCII string. */ +static const char sigma[16] = "expand 32-byte k"; + +#define ROTATE(v, n) (((v) << (n)) | ((v) >> (32 - (n)))) +#define XOR(v, w) ((v) ^ (w)) +#define PLUS(x, y) ((x) + (y)) +#define PLUSONE(v) (PLUS((v), 1)) + +#define U32TO8_LITTLE(p, v) \ + { (p)[0] = (v >> 0) & 0xff; (p)[1] = (v >> 8) & 0xff; \ + (p)[2] = (v >> 16) & 0xff; (p)[3] = (v >> 24) & 0xff; } +#define U8TO32_LITTLE(p) \ + (((uint32_t)((p)[0]) ) | ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24) ) + +/* QUARTERROUND updates a, b, c, d with a ChaCha "quarter" round. */ +#define QUARTERROUND(a,b,c,d) \ + x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]),16); \ + x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]),12); \ + x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]), 8); \ + x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]), 7); + +/* chacha_core performs |num_rounds| rounds of ChaCha20 on the input words in + * |input| and writes the 64 output bytes to |output|. */ +static void chacha_core(unsigned char output[64], const uint32_t input[16], + int num_rounds) + { + uint32_t x[16]; + int i; + + memcpy(x, input, sizeof(uint32_t) * 16); + for (i = 20; i > 0; i -= 2) + { + QUARTERROUND( 0, 4, 8,12) + QUARTERROUND( 1, 5, 9,13) + QUARTERROUND( 2, 6,10,14) + QUARTERROUND( 3, 7,11,15) + QUARTERROUND( 0, 5,10,15) + QUARTERROUND( 1, 6,11,12) + QUARTERROUND( 2, 7, 8,13) + QUARTERROUND( 3, 4, 9,14) + } + + for (i = 0; i < 16; ++i) + x[i] = PLUS(x[i], input[i]); + for (i = 0; i < 16; ++i) + U32TO8_LITTLE(output + 4 * i, x[i]); + } + +void CRYPTO_chacha_20(unsigned char *out, + const unsigned char *in, size_t in_len, + const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter) + { + uint32_t input[16]; + unsigned char buf[64]; + size_t todo, i; + + input[0] = U8TO32_LITTLE(sigma + 0); + input[1] = U8TO32_LITTLE(sigma + 4); + input[2] = U8TO32_LITTLE(sigma + 8); + input[3] = U8TO32_LITTLE(sigma + 12); + + input[4] = U8TO32_LITTLE(key + 0); + input[5] = U8TO32_LITTLE(key + 4); + input[6] = U8TO32_LITTLE(key + 8); + input[7] = U8TO32_LITTLE(key + 12); + + input[8] = U8TO32_LITTLE(key + 16); + input[9] = U8TO32_LITTLE(key + 20); + input[10] = U8TO32_LITTLE(key + 24); + input[11] = U8TO32_LITTLE(key + 28); + + input[12] = counter; + input[13] = U8TO32_LITTLE(nonce + 0); + input[14] = U8TO32_LITTLE(nonce + 4); + input[15] = U8TO32_LITTLE(nonce + 8); + + while (in_len > 0) + { + todo = sizeof(buf); + if (in_len < todo) + todo = in_len; + + chacha_core(buf, input, 20); + for (i = 0; i < todo; i++) + out[i] = in[i] ^ buf[i]; + + out += todo; + in += todo; + in_len -= todo; + + input[12]++; + if (input[12] == 0) + input[13]++; + } + } + +void CRYPTO_chacha_20_keystream(unsigned char *out, + size_t out_len, + const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter) + { + uint32_t input[16]; + unsigned char buf[64]; + size_t todo, i; + + input[0] = U8TO32_LITTLE(sigma + 0); + input[1] = U8TO32_LITTLE(sigma + 4); + input[2] = U8TO32_LITTLE(sigma + 8); + input[3] = U8TO32_LITTLE(sigma + 12); + + input[4] = U8TO32_LITTLE(key + 0); + input[5] = U8TO32_LITTLE(key + 4); + input[6] = U8TO32_LITTLE(key + 8); + input[7] = U8TO32_LITTLE(key + 12); + + input[8] = U8TO32_LITTLE(key + 16); + input[9] = U8TO32_LITTLE(key + 20); + input[10] = U8TO32_LITTLE(key + 24); + input[11] = U8TO32_LITTLE(key + 28); + + input[12] = counter; + input[13] = U8TO32_LITTLE(nonce + 0); + input[14] = U8TO32_LITTLE(nonce + 4); + input[15] = U8TO32_LITTLE(nonce + 8); + + while (out_len > 0) + { + todo = sizeof(buf); + if (out_len < todo) + todo = out_len; + + chacha_core(buf, input, 20); + for (i = 0; i < todo; i++) + out[i] = buf[i]; + + out += todo; + out_len -= todo; + + input[12]++; + if (input[12] == 0) + input[13]++; + } + } + \ No newline at end of file diff --git a/chacha.h b/chacha.h new file mode 100644 index 0000000..87a1d46 --- /dev/null +++ b/chacha.h @@ -0,0 +1,78 @@ +/* + * This code is based on an OpenSSL implementation of chacha20. + * Hence, the copyright below applies. + * + */ +/* ==================================================================== + * Copyright (c) 2011-2013 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ +#ifndef CHACHA_H +#define CHACHA_H + +#include + +typedef unsigned int uint32_t; +/* CRYPTO_chacha_20 encrypts |in_len| bytes from |in| with the given key and + * nonce and writes the result to |out|, which may be equal to |in|. The + * initial block counter is specified by |counter|. */ +void CRYPTO_chacha_20(unsigned char *out, + const unsigned char *in, size_t in_len, + const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter); + +/* CRYPTO_chacha_20_keystream generates |out_len| bytes from the generated keystream with the given key and + * nonce and writes the result to |out|. The + * initial block counter is specified by |counter|. */ +void CRYPTO_chacha_20_keystream(unsigned char *out, + size_t out_len, + const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter); + +#endif \ No newline at end of file diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..4aeb87b --- /dev/null +++ b/hash.c @@ -0,0 +1,143 @@ +#include "params.h" +#include "prg.h" + +#include +#include "stdio.h" +#include +#include +#include + + +#define SET_KEY_BIT(a,b) (a[15] = (a[15] & 253) | (b << 1)) +#define SET_BLOCK_BIT(a,b) (a[15] = (a[15] & 254) | b) + +#define WOTS_SELECT_KEY(a) (a[15] = (a[15] & 253) | 1) +#define WOTS_SELECT_BLOCK(a) (a[15] = (a[15] & 254) | 0) + +/** + * Implements PRF_m + */ +int prf_m(unsigned char *out, const unsigned char *in, size_t inlen, const unsigned char *key, int keylen) +{ + unsigned int length; + if (keylen == 32){ + HMAC(EVP_sha256(), key, keylen, in, inlen, out, &length); + if(length != 32) + { + fprintf(stderr, "HMAC outputs %d bytes... That should not happen...",length); + } + return 0; + } + else + { + if(keylen == 64) + { + HMAC(EVP_sha512(), key, keylen, in, inlen, out, &length); + if(length != 64) + { + fprintf(stderr, "HMAC outputs %d bytes... That should not happen...",length); + } + return 0; + } + } + return 1; +} + +/* + * Implemts H_m + */ +int hash_m(unsigned char *out,const unsigned char *in,unsigned long long inlen,const unsigned char *key, const int keylen, const int m) +{ + if(keylen != m){ + fprintf(stderr, "H_m takes m-bit keys, we got m=%d but a keylength of %d.\n",m,keylen); + return 1; + } + unsigned long long i; + unsigned char buf[inlen +keylen+m]; + for(i=0;i -#include - -int main(int argc, char **argv) { - printf("Hello World!"); - return 0; -} diff --git a/params.h b/params.h new file mode 100644 index 0000000..897e904 --- /dev/null +++ b/params.h @@ -0,0 +1,13 @@ +#define TREE_HEIGHT 10 +#define WOTS_LOGW 4 // -> w = 16 + +#define SK_RAND_SEED_BYTES 32 +#define MESSAGE_HASH_SEED_BYTES 32 + +#define WOTS_W (1 << WOTS_LOGW) +#define WOTS_L1 ((256+WOTS_LOGW-1)/WOTS_LOGW) +#define WOTS_L 67 // for WOTS_W == 16 +#define WOTS_LOG_L 7 // for WOTS_W == 16 +#define WOTS_SIGBYTES (WOTS_L*HASH_BYTES) + +#define HASH_BYTES 32 diff --git a/prg.c b/prg.c new file mode 100644 index 0000000..01bd7e3 --- /dev/null +++ b/prg.c @@ -0,0 +1,31 @@ +#include "chacha.h" +//#include "params.h" +#include "prg.h" + +const unsigned char zero_nonce[12] = {0}; + +/** + * Generates rlen output bytes using ChaCha20 with a zero nonce and counter = 0 + */ +void prg(unsigned char *r, unsigned long long rlen, const unsigned char *key, uint key_len) +{ + CRYPTO_chacha_20_keystream(r, rlen, key, zero_nonce, 0); +} + +/** + * Generates rlen output bytes using ChaCha20. + * Nonce and counter are set depending on the address addr. + */ +void prg_with_counter(unsigned char *r, unsigned long long rlen, const unsigned char *key, uint key_len, const unsigned char addr[16]) +{ + int i; + unsigned char nonce[12]; + for(i = 0; i < 12; i++) + { + nonce[i] = addr[i]; + } + uint32_t counter; + counter = (addr[12] << 24)|(addr[13] << 16)|(addr[14] << 8)|addr[15]; + // TODO: Check address handling. Endianess? + CRYPTO_chacha_20_keystream(r, rlen, key, nonce, counter); +} \ No newline at end of file diff --git a/prg.h b/prg.h new file mode 100644 index 0000000..ae5bd06 --- /dev/null +++ b/prg.h @@ -0,0 +1,16 @@ +#ifndef PRG_H +#define PRG_H +#include + +/** + * Generates rlen output bytes using key_len-byte key and places them in r. + * + */ +void prg(unsigned char *r, unsigned long long rlen, const unsigned char *key, uint key_len); + +/** + * Generates rlen output bytes using key_len-byte key and hash address addr and places them in r. + * + */ +void prg_with_counter(unsigned char *r, unsigned long long rlen, const unsigned char *key, uint key_len, const unsigned char addr[16]); +#endif diff --git a/randombytes.c b/randombytes.c new file mode 100644 index 0000000..f3b8d41 --- /dev/null +++ b/randombytes.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +/* it's really stupid that there isn't a syscall for this */ + +static int fd = -1; + +void randombytes(unsigned char *x,unsigned long long xlen) +{ + int i; + + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom",O_RDONLY); + if (fd != -1) break; + sleep(1); + } + } + + while (xlen > 0) { + if (xlen < 1048576) i = xlen; else i = 1048576; + + i = read(fd,x,i); + if (i < 1) { + sleep(1); + continue; + } + + x += i; + xlen -= i; + } +} diff --git a/randombytes.h b/randombytes.h new file mode 100644 index 0000000..053d3f1 --- /dev/null +++ b/randombytes.h @@ -0,0 +1,6 @@ +#ifndef RANDOMBYTES_H +#define RANDOMBYTES_H + +extern void randombytes(unsigned char * x,unsigned long long xlen); + +#endif diff --git a/test/gen_testvectors.c b/test/gen_testvectors.c new file mode 100644 index 0000000..3b59df7 --- /dev/null +++ b/test/gen_testvectors.c @@ -0,0 +1,92 @@ +#include +#include +#include "../crypto_sign.h" + +#define MAXMBYTES 2048 + +typedef uint32_t uint32; + +static uint32 seed[32] = { 3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5 } ; +static uint32 in[12]; +static uint32 out[8]; +static int outleft = 0; + +#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) +#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); + +static void surf(void) +{ + uint32 t[12]; uint32 x; uint32 sum = 0; + int r; int i; int loop; + + for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i]; + for (i = 0;i < 8;++i) out[i] = seed[24 + i]; + x = t[11]; + for (loop = 0;loop < 2;++loop) { + for (r = 0;r < 16;++r) { + sum += 0x9e3779b9; + MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) + MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) + MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) + } + for (i = 0;i < 8;++i) out[i] ^= t[i + 4]; + } +} + +void randombytes(unsigned char *x,unsigned long long xlen) +{ + while (xlen > 0) { + if (!outleft) { + if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; + surf(); + outleft = 8; + } + *x = out[--outleft]; + ++x; + --xlen; + } +} + + +unsigned char pk[CRYPTO_PUBLICKEYBYTES]; +unsigned char sk[CRYPTO_SECRETKEYBYTES]; +unsigned char m[MAXMBYTES]; +unsigned char sm[MAXMBYTES+CRYPTO_BYTES]; +//unsigned char mo[MAXMBYTES+CRYPTO_BYTES]; +unsigned long long smlen; +unsigned long long mlen; + +int main(void) +{ + int n,i,r; + for(n=0;n +#include +#include "../crypto_sign.h" +#include "../cpucycles.h" +#include "../randombytes.h" +#include "../horst.h" +#include "../wots.h" +#include "../hash.h" + + +#define MLEN 59 +#define REP 1 +#define NRUNS 100 + +static int ull_cmp(const void *a, const void *b) +{ + const unsigned long long *ia = (const unsigned long long *)a; + const unsigned long long *ib = (const unsigned long long *)b; + if (*ia > *ib) return 1; + if (*ia < *ib) return -1; + return 0; +} + +static const unsigned char seed[32] = { + 0x22, 0x26, 0xb5, 0x64, 0xbb, 0x78, 0xcc, 0xab, 0x4a, 0x4c, 0x0a, 0x64, 0xc2, 0x0b, 0x5d, 0x68, + 0x38, 0x74, 0x1a, 0xc0, 0x03, 0x17, 0xff, 0xd8, 0xe3, 0x53, 0xc8, 0x59, 0xc6, 0x23, 0x5b, 0xaa}; + + +int main() +{ + unsigned long long t[NRUNS]; + int i,j; + + printf("\n=== Benchmarks of signatures ===\n\n"); + unsigned char sk[CRYPTO_SECRETKEYBYTES]; + unsigned char pk[CRYPTO_PUBLICKEYBYTES]; + + unsigned char m[MLEN+CRYPTO_BYTES]; + unsigned char sm[MLEN+CRYPTO_BYTES]; + unsigned long long mlen; + unsigned long long smlen; + + unsigned char masks[2*HORST_LOGT*HASH_BYTES]; + randombytes(masks,N_MASKS*HASH_BYTES); + + unsigned char msg_seed[MSGHASH_BYTES]; + randombytes(msg_seed, MSGHASH_BYTES); + + //Benchmarking signature key generation + for(i=0;i0V9}C1ay&I4WrinT>hb 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 +#include "../prg.h" + +static void hexdump(unsigned char *a, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf("%02x", a[i]); +} + + +int main() +{ + unsigned char seed[32] = {0}; + unsigned char out[64]; + unsigned char addr[16] = {2}; + + printf("Case 1: All 0\n"); + prg(out, 64, seed, 32); + + printf("\n"); + hexdump(out, 64); + printf("\n"); + + printf("Case 2: key = 1\n"); + seed[31] = 1; + prg_with_counter(out, 64, seed, 32, addr); + + printf("\n"); + hexdump(out, 64); + printf("\n"); + return 0; +} diff --git a/test/test_wots b/test/test_wots new file mode 100755 index 0000000000000000000000000000000000000000..8c743cc363b99fadad03d6112061dd6a63608b85 GIT binary patch literal 62796 zcmeFa34D~*)j$3`vt=d;nLq-B)d2$nN=PCit4<(+2?S+HG`NKj5)utb%m!K|Xn-F6mNvN57ptwbwUnyWL@c1Kic6Kcv}mgaS6r&N=KuZP=bmRKnV|OFKEMCx(?@yk zIp>~x?z!ild+zc)^Q0xOV7|jN4Y?e~Sq8Dgi9toO?uN1c1%+zbbi-!^jd8|U!vl&N z|AH!qsRc=TT#J^}tNENrUHCWMVG*V~f(npyq(wF)6%CoPzQ`5Spq2jV3=5UiFanCe za@6(M9EBoXsQ^j;q4hYfQz=K3<-BJpcBCs6An7r!n55D^wvGP^wY)+{5E$_w6C+pM zBPn@#EqSGJHQ~E66^b;d07>t&$cCg+ZYj!9&#!wWYsO`|z7yFcS?e#UZmuzh{EF{A_rgzp zTe|D_-uHKV_AMUqn{n4YPk!=BzbU{lls2T#c&|-d16b^J1f)T+WMk(E=x?5Y&Ta_c zU*GF&5CVOP!0*Ao{^~t^0(#>K=-W>~zvBe-*G@nm4g>b55A7eozrNRU5CVOPz*A2k zXD#yjlizXz`hgSBy(f^re7aW@&>DTzlE2Fa?Z%gHuBC}QqoXantjGJ0~98j!pzX(oOx7gE%ljZ zl$BRi8x_^Hwe?1I?V4<*u9Z_#QdeJ9(^yebR(ToJf<|SR5iY4HMUlFu#%fiLv?w19k^*4-O{zCRW-)B zOY+WHzPzBsveJTr{JHZ=vNN(X&M*Ydnred)==6-KG`0hAz=~onz;68KL~4Z{3->6x z(}=U_j0^nFFc@b7{CDc52!C$lG!k(!_xngCziD`tj}d34o$oSs>wNNmcl-BIKF*kr ze3nVAsv5w^zf|Lm!&u&BT&QtNp2L=R{Opw!qvi^U$;8F4IA<|#$(5%4Ee-^&lFAB1 z(JdQTB_>4CogxHjW)wZT-sw^F=y@R+MQ7b|g`(*87(nKdC^`&aU&T@Mc$;b%E2HQM zQS{0vdSVp4E{g7tqOXslvp?k87)2i_M5J4z=+W_LTNFJdivPwaI%A<+JEQ1>g@|-l z6n#h(eRmXnXcRrv`ldJ3=KkKafDzi<+34`dMDRC$9-3nhtoUU+mr@}8FPWL;yP2tOk( zRQN*TbcLgJ3ZF}yE^u_E!eS06p3@b`$bi;s3H{B`2&+M~OH z7yXV!?+M^`KX0M%D>>(f+J8^oa?Z;s$|+nPy5`MY$XOj~bB&vzSn>{Lb#|}9@XT%d z{d6#bpKVtHa{YJxo(&Bh*v$5W-vC&w!TpM8?%%u=1jqi(%aAztZ(feXm0$L^Q2RW8 zsP)LP(8P!G!>_9QE4X)`HxyFZuimI?$PfRcJI^BQRD|v`ffckX(V_Om8$<2iqxzxt z-%zW9@JHR#pld;UI~6Ku&jUld#qd2cgxZ^ShQhCPj~UiKQxS4sEeOBUy>XOb4?(uJ?VX`{2o_u4Z|D=sHvF5ng4p_?nLFc?roZ3+ zt<0eD0DTkoyY_Qb9sVoTJ98|oe@j$eh2*`{J%V{Zi^^Lld9QW%j4_M{=vv)*AZB%j z+Aco0I&}I-)lE&OgxbpwUzl@I&c!*GKjlaEr!+-Dok=AqCN$D2@?YeE!$ z5`{zI{Z{#xb@`2RR-7I%jHZD_DA~OM-FNYR_k57d_xCX#gj(kmXM&+|w5>vF(E|o{ zA>i*hbn($|posq@<7k7G!r8;eEJrU|UeI=lx1epw^n$jGg0|-5P}@0a1#Ra93fd|r zIWNKb{NX⁢o0{LBU2SWIl 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�cl{AI;bj7lr&4i-ih$2%>yc&I%zqGhxr zBD~U)!|Yt!ng2y=Qc7=oS~iZeV~!ft-rhk>Vww(O@zNoPXW|%%;ur%`%t$ayf*fr; zMn6nWz2`I-PuvgjEn*5aM@4c{tS)3+)6)9*Td)3v+euM&VBh!_ZT(?U9W^}S?wp{a z`g~;HJ|CsJ60%2&Fw9QTOjx3m1(Q<(CRI#JGSQwK32=SmTHnYzUIO4L2?&>iu;!Wo zwe+lNMLZqJIQ2w=)+EbUtk|Vm!PrG#JI!_parAbd|3;4gSwLkJZsh(TYaq zHGiaIG443X2SzjYastedb{B#ZqtHgtA5k-;ie0_?dq{LwCqS{=Z=G=t1l6J!q4Aw?>w(SWSvEVzja|uo|%_edf5RVaxG2 z_`hfDh&FxJG103^sKZsIEJ0B#UV4y~#poynz^Dc;eMgowGIXW4IdsNGf6w{{1gGz#6<-EPibnXpHfv*R^7C^ zdfxvRD@Ul3dE;*PoT8_&=iz!ttk)@duaJ zoSsq1dhmCXSC>}+h9;O;9r6QS=hKGiayTzJ=rWySy$1;v_XbaOTx`yna~l3%G-u|V zSyN`s$)7W8&a6fFH)U2qe*OjT&zUuAO8%U)X5zol!wqN60^6Kf-gOHW&B;G&)~teA zlh5M+v(5rPN?Uj5x(eq2N2BSuIyLm|(6`SkaQxnt;yuF|?>*>jUWgwO%uU`Y z`HQ?A`rpn_f%n-hh2Aw9zo}@k_ulgtc^}5VM`jir!h6IHk7L0g?@v4jK+B(#zlQho zzy5uS;_v5vcuueh;!S5=v1gm7>cfFz1*IXGsOL9`r9LzhJR< zyEJTOfj8^N7d_$~yTz*I@?z(}V#jzmN~3a})9EVztFH6qe%Nq@c0=u{mQRY?^3QNL zG~n!--**(}TU}L~9qW6ISJdHylgcFp#Bl+rl!nzQKLcmN8rx_l1#(U~*V+~kb^i8Fqov$@!rkl|eC-R78< z0*9Zo4c)rH+nDXF_C5oW)4vTp8SpYMgM*>a`xEC$#m?2BJv?g;oy<|2=5dT2l1D=Xxb!|B2+pl%>4ytX<-~vbgeDXVslscRaW0`Zu0+ z4)g{dM(a9yws#bGw=eR3@50X=i&GpE2YTmt&ss4BEvfd-cA4HEI!AaHlC#Ns{`QVV z-YYKfj&@X~Dvo7dHg>r8Cl34=*xU{KIgB>k`}e!A6_38!sZ?j_aHS&@TwRAT)rsOJ zI&du+8-z|+*2J4BpDc5(FZNDzOicZ`V>tBWd-e|FRpj=sv-sK2gC>>7Wyf9;p18?=3 z{3WWx@T2nn&G#?2Ja)2*%xC=ul9-O6)$#S!#s)j8ShHAf14+z){$e(mM-@{pB;f`5 zYfBp|%Sx-O8G~S26|fYK9W>UjZD=Sdt6jUcwuVgV@;T%+P3I_c$o#F&D`yAh;M#u2 zoL3y_DJzxoa>)~5Yw4i?D>bsF)r0{y_rm@j`^t`M=qjP!ORQl z&zf2AoeK^>dGW02k4`-I0%vZ8bN02)T<=2X?AgxT*;NpleQMc_i_SW2%B(GOoa-}k zo$K#$o^7>d6Q%u(w@ltb&L6d8INvn9R_+?ooVT=GtI`(@)uKpS9j6Xa867S7MhX~T zvKX8lEek!x9`DcZUv<&^hDEB;FBrw%Va_sdhBM%(FjeDsx7_1=!noFXR}0$zgfSby zJ+DF8nORjA#9H84qpt}Tzyx{GCh(>z8=MIn{J5pj`FF_vaSP1wcgX&63k`8?FGI|R zA)ax*s(bYUHX+IqrxV9l8*pFG+kDS18<5XmX{SORRJLt)1HF}MXG7b(kxW4vGFGCgM zQ%0qZWbzKi&{SILecoDmo9SKp2-Bat988?g7>HR4x*h#=ftsv-pse*qf7ZhHts8r< zR$_w8f5T)3#ZTc^vI&&vNCjB#nbQ`XE~;k;toycK`(zV8^9 z;{9O7yFd5l!`xlQwFo8iy@LSHyc1wZfzt^tRNyp%i+?i3dAZs@bQu}I=V2+FQ0%}< z6{fu>!RN*CL2rH=sj3DaAU7JSt}LfL47_Jk!iVK+%h#6GU4dMMm6WVjeOFn#wtPxu zQ+ag*wlh=e>T548FKcX=qCBy_yaH1q9yiyNuMVK1dZVPIp}x$hXiwzseR68z*4 zo3!%MwbYs=QTJsf6{S_xMoD#T*=0uFc}q$b=a$SXELI@ayJuu&8>^UCLhBhN`RA5E z!0M7!r41G~KffSP*5NF^WKrGbvc0jq;l2lC`T#${N>0U84jhxU9UsA)*8&D^`BuUAV!yx@rx3GVh!P z`RC*rbxo^E8p_L88)!bQQ&qz|2I1OLyyOlTpO5D_D6g+Ks%vZ31k{bXHPx*yZA3Fm z>PlB<8>OpPL-n$zdUR`b6MnY`)hwel@Vf*hb+rvfMIH9QjX-LvSC`aQR5X-B4Oj)1 z%syip7oR2Tsv0XH0bgY+uQ$r7%S&sT>Y)3Ys+#hs>U+5hO=&gWfFw9I>$CbQ%Cc6M zG}SaUltWv)rMg7Is?4hnFX6Xq_pw1jgu z)mhHK5T(W{jagS;Q3Cf}Tf4g4D67TbRsK;A2X1aGT~%E!Mz_7svPy~W7&^S=GW3|` zp&a%~efi~0RrTeotyZfD!SSI63M%JYdCeGv6;~+L)L>|2S&of*`qGu<^|d86wKZks zdV4|ZF@qj{dN$g|9c!OuIjpqWSWhkvC|UtLC8gC>r47-X$Y`LK(O_sC1`bl?ZzVNq zg%4DXNULN+b$NLmNOf3uG7ix38`RPitwS&|${G-=tb*0GYqAZ{7=d76TB>B7@XoKV zUAr8=5CId2FanH=*rtkVp3?f#wGA9d4MtfxVhFq|F zvAhnkp~M=2s-ycX9xBV%uWnjfhc!7`t0vOuNi^cdh@C35BTB1YRI?av-_(F<6bZ;* za`AE+>vpvWG9^OX*&;|*gm`0%Ag4!&+gb!UBSPHTBFLE$;>H$1!M?6bg8yOv2JfmhhXANpzdzz5KO!Y)Lktdf{8nUy0fK2F!3i)Z*1uh zOdJZ-Z7m&wiF<*%wI#;nLG6(lc*?{Jqe8%!d_ubDRSmuoAz68md#s>B5%f2^)OoRj z&Wnh!AQ7}mYi1ft1r&j%u{>VO9Ba$IM8K&wY59p{M+7YsP@3j4-Zz(h4Ko$@SFreN z6kO3?!4)wDg-cq6HVY^XbQ<1Zhame--0E-ZzWt5e$4dmF zS~eZ3Q`1-$&|r3iG)sevBc$^*SQ#N*uEEv_={gPmFhaUTgTIWB?$O}E2#GJA^ZHwa z^nnKX>o;CdHAjQ(I&~PC9RlBA)3Q1QzS*W_b_g6XG+(anbsf{}`|-q^7Y37n{8gAm zx=6ryn^csb!I?JcqB$Cz8zET|B6q27fPP05;Zi`U7G5LZQ?^p)3HT42v|Kb8X;&9B zFr~TzyVOzvMM(~xr7^B71w|8dgn-cnV@F+V!Q%c3#*RASl7_+z0!o|J;1ML!>X?wx zR}@|5U2Op=Mp~p>dPC~Rb0EPN1msr)csY#iA%TBwk;o?CdzwTx4aOuQ5?wE#NJOzE z0*gqn2^b|k#*d{M5s45iph$%DVu3{@*aVD{UM$l3rXmvVARvEv!wdZ-pj2YTWx=E# zloe2_L|IEn>|LrcjbZ^sAjKDV2qrSYD4>Y982id@)`aVI8gJNN0*v(x9Q5!SrBigd zfO(q4N{TuJ<5wwpflmrN-M#Nv#TQ)Gw9k5F+6EIr*GFyCggi*kV_=`^@KGQB$EMTL9k!20?I;0@d*;qE!InQRicn-tPn6-w-vT-MYe7$1QhY? z;n+2W80dLjka6pC4f}Dn$jdaA2*{t;Sfs+S8qAE4W@<1LAuZG3$_VLF4X%%n`06;X zZ4uHBHMlE6`jrMdBcw+(_;iHyR}FSYNbhOzvj~YFui=%fQw$0IJdam;gfvZq(xu=M zkiT)|MT4pDN&~*%F1bX&12(DX2@Q5fNCFK*Lx9?42ZFMjL?b#%ea8s2{<>3P2h{{;T1DkiR;VobQ)K*bO<6H=rQmuf~=7p zf{RM=_(oM$`c=kQy zzH1sYe{9sYxXZRlk%0H%hZwv}<6;3H(Ijh15$si)u}Hx8dNEpZB6q3uPP@`V0i`C> zI8Q*S#+qmZd&#b}P{8O)3s2b6hwMt13Me(1#zg{3HLP@LhhQ(*l`a)9y3*LysM%NI#B)&;Y z7R9mtjXezzrUWhBY2ep?g8WVs9Y&KOa6}pyBi>>`B2Aba#N@A^~ONjBzO7am{6oL&1jF z+tnfgErWmj4#pwxtMD4f4YVsQ6fnBd*l{S8&bKRFDxlPa zaVVfv!%COhmGX1Xys$|SFuKy%aVV9}wJTjFpwxtMC}4D@%j``Ipj7+q=XIFw3H zvnwqYFuI0fyN2O*4aEXR*AP1nCu+%}IGnj8#+ij_t!9Rg9@`+uYnn)B7C0gejFzDz zcabJc{f$H6k$$Ccfu)>^1`)9qBn5@3zuB!!Yy6z8c|W5`3)bfk@RK3|g@OZzL>k7w$Fy6d6pG6ADijrFc%U9d!_Xrq8~g5Q4|V+$7cS1`7Xu?3g* zS1`7XQjotD;Dt5{7_I7w+jy>34h4$^j4l{EFl0v_)CI>I>l43gdV7Y4($^1AKrnsG z`XmKS2jo}Km=58er^}7O-;4N)BEl+)ech^rzera#0THhFWYLmvL{S5pL(O`oV>+aS z9EMCjaY>4?Td9jnG3mSPuUVY3#7j z(b;ygQA4-rl$&7-yfPWtkg>iY+FHi1}{nA6BumYDOvVecwxNlY!T!`O~kkq z(C>-jv%tt*Ds|eG77BQdO}aopsl^(Xg1v3mSty{?+5Z5RO5eAQu~fi6xh&lrMsrZ$ z+q8TNTiPL*)Q;xwwwurI5b=W73n;26J}WnJ7fs^qwk{J;P6{wu1eB_&$g&tkz$iM2 z>9M{OP}BfdqJLIO)AD$WWb)u60^QlsKKQJwr-1H5o7F5phv zh8GJM-S8FtH2mUNHCFUh17XyvDsq>GZ`Vo;FzUVEb$J8n-oSVq9Yz=}=ylEZp-vrG zlM0-uZRap9Hv|@gxr}Cfl2|ZN)n#no)#4FMv{f>-0mOh7=ZUnr#;!TDLtuWFoEN6F zZ)tE}gmh4YALuj*?U>$ZOixW6F?!_4blbjrv>psS0*k(oEFeE5!;5SJ^W#Chz$T!y zroX@|mKFWUBk&D&`|t!`!#}ra*&PD!vuUSy2>chDHnl_GcWl}j9RjMkzXd|`rSk1m@r5;l*PVfu%zo*u?a`5;VU&Axc8y z)*B=2x`lqS=EFm$mJUHg>%N7AF0DyIYodkrRU%Uh|B>wtvNVry%D|OL3loljGHLzq z&tIa1#9b`g1e6YN7%ho1TEu%v6Zk{jMidn=;*}(*2bzMAX~qQb~}pytM!OU`29m(@KOP#YBp71snKCf?+`d* zrDgwJEh10bmz<>)ib%I-Rq6l!`ankJR}L|&xskgRlu+<>gRPX4n1xe=h)@d>7Ks+7 zasG)|q;@t<;7fF{f#(ZY5gptFk>CZsuOt5J@RaI$q>2OtzW|BTQeOeirV1S-3svtEBKul|YJyeAkuOgKKzpdZRJ+LKlrhJ5pbp!Z|%PYlghy;pja6bjew#@Kl^WK zIsbw%FYKWO6m!$T1(r(GzFH8e24w}58vEUUOP%xVI#&qDKYnVdv0OlDDjU9{LolhJ zpMfIc2Ws(Ov9rEZv+=XAytoS%xZS2*ZU`)c&5?aU4_;&|JL zMFLK@NyYNJkkL_Z?hAa1rbTwvLdnuOQ9J9?HEDj7Ac0S{t=*jaWgS&$^G)jz_!66z z-63$LO*_3q;Ch=jwL@U(ZSJfEmfmKU3M}2towdMU*7X-^`#oXX@gf1mp8f8urQ9XD z9M?nk&Qpl1bf(%_3sMmw)^!LXb@mb?L{a2Rch-$sto)3GZ!rk`OP$A^wZPJ$4kPBJ z31hqFzgeeAXk7FrSm-~~`D$k^h-lrnkkF+yU$wLTtrkAT_J-J&swNZiHtSU~9j?xJP1i1(5vu!LL`6)@tou{&$2QKCPhp@32|cGdz)5_Zf2-l|DzXDx`VRlwI*jV}w;QVn_jtK~Yh zKB7vmowX2ex7$(tU#&-U{jM!)nSfFYn<22&$ep#o5i2eG?`pBz)?&&18jMJ{XI1I{ z{`x>h=2s4}zWyQwB@|$XETBY%euJ%)lbD5Jaw5?ZE&AD6ixjDnO%wQ49c&ic#R@PgfjWde#!>LPHY2{AkCFO&X}ExpjK z*98$tF+1x!HH$14`dQG&vc&8p`?B=6gB6}bx<-$2HD;^W=Td$7`Z=A6W%(NIit)rh zYDGTLDJl~1bDOk8z`=TUa~fB*bO;i$TFeQO==duwiajf^n4ll$6lWcw0lXw2;Ft(W zxca6#OsI!f0^=j1>guPmOr15?W+@V|T9dd(DvDW$#}+K?uVC!UCVdOiDpK-7U1b~| z51`VK9YR>CGrS}R8t_O-6YNAplxqrpnjn8EDu!3SEOc{5P;p~E6Yx)()PJL63l{cQ zFt*Ws3$oEt@?Kq~-Dn}mpUF9Kqea9Yw1_Wj^uz?KvA;`{Mwi^HLHR2l{fvAmb%`#W zfQNdKwX-JvOv_O(W(Xqn^eQ1WJ{4U;5K*I73DrSSw`yHytxma47WlVXD%Z(UlL+l^ z?JTlH<^K!)>$ToZQThw~8(aUY4S}Ui$!L?9{$#)J*zl3DExbC)P8KM&{a?0li*A|Q zZp~GOz`JeQIz!-J+qA0;fu;4ym?3(%T5`lLgZkPfE4?4f#BN9Aujvf4YtQTuSpG&6 z7Up7P`I|)?xP7fG>_66`Fbd`GHO26*&^%GR@@JW1c$;E*<*zTr@QQvimY{?D!K4`8 z0lERF>Qo}~IU1L_eRto6YCKP;A)OZJR9<=2sGZ0Q)mt^CO{eWPM{nTU8ofcM(tz7E zeuqv^q~8-d|0$h1^y@7ioywbNmug(I*~z_{zE7v}JJi2wTpm*Xf1O=hXdOiuo}S|+ zEsZAC+Iml0eW;LaQ}1}`(U7ic0ZTzyF{AKf61-tKvX*=KtpZW@l$-XC`~jnXkLhpd=E_ z@=?WViS+~WcCq!7CH=G`L56Ws{Jxa<5_y|W^3)Xcg8+TY?$GFXi_H7_{_|e3ACOr8 z6?{+Zi0M)<$nxrau*ACb)HS8g*U3)jQr(+RhhpE~1hx5TY^a5}UUl(=rm&|~ zr8;MV8EiTibawT%*Rdy+v#^DDK%0S$wNH+jfGpac%>-lfGbqo+*#C`lu%%XQa&{(R zgvhH_rpiQ+)ZA3v1iU#}P^W#!0&HA1iM(0fD=er?gnO~4R3)6i=4=hiF~V}STAp_d zO+PpaH9L|~SH3R4=_|4Hof_q@J=?-!Z7Pn(y$174{zn=g_BHCi zaE@i3ew4L^E$F96tmn~qAMn3o{3F6YBICgG!XuOFsr45a-(`Sk5dO5h9p_rkB3U>J zI*;i2m9N+T1bpKBgTg;3{F8FQ#9xU&pZ^>%a?xRH&vFaPm_5rmGMBihainv_!&5Ng z`~~4J2!GIP@D?F!{DQ~VaqYPLUs$~=em4A$KWUm5J$}z7%N{+OoTF63894g&Uj#-~ z)b<|~{-E&7Uj4QGK;iHTj&c6s&6e@Qa^v1%D?0e+e+yZ0{#=J;=Q(;X5$^K{{`r^S za~}TWdu8U-Z6#O+F-ML2?X2kjt9=ZK^YN$r)51UfANikxOZ8X2xxEAn;dU4K_4Vz~ z;ik_9F`9(mB>Yp-{@Op4ul?X_aN_4by|qNenA2N1%DsLq=X6V`e7{(4@QYs?uW=={>%6gJ8qXdo^@ zz7#ZWLPFCn#~s&oy13+JdZt+ez{iOBc$edUBF~Yt(!LvV_;*zu;~))MvEZMpBZvRt zV~+IazwmDpzTbYwo^kv;wc|-0^!ys{$T|Pzvfv)w7OOmx%2RYGhrd7%7J}}S;;b~k z6&c!h)Q94h&z$9tU;g>Y*0Ikq-0{d)E~Wtn)9Na(w-Ukh9YD zS3D~&qP1=+*i=>#H6G5yi>985pJfhZ;%8%>VT~V(7?E@x5zi}PrKIbFB;I6n-cRDo zA@6GA5T}b~h0%FBo>|07Nax=qzS8Ktn#7wMBF;(tJfrhq5XZg1QUuM$xr5!kx)6#5_iUJMigj)}%pQaMN;Nkm#FGKsEl=)1@okti? z_mh{UoJFskSJ^+^-)P;EbzL7zIVxABTcCcM*PMrOPvV50X`% z<{UilzFfxTx0<8ZTht&5*D#*GU*wT>-W%9I{k-%8hjd%&IlW(ST|bf+g*h%K{hZDN zZ_9Y@s2{FEe;5ob(T(=QC*Z$|k>ueJaAzcmNHI|rX;yxFKK)lX9AT8{sd zIXL|&Dy#l)0nfVLkAPGA>wKa8{EHm_-+{Me^oJ%a4yZr0t+QP7^=MxTt%^%OS?6GL zvEYkWT1Kkkzd>+wwZrv2UpwPMjzw1dm)1Cc-JkB>9RG)aw-(l>=8H!KKWa;{(v5jK zr<_H`F)zz?RfP4L;HCAJkp~5T7kCTC$rH9Dh>Jd8{{+OwcT!Hm9D|dH(=T<$r5S?5~Gq;I#tR`BB$(^nNv~pKQ#*yBWvxzTR@t^!!uUp5uRy__thO z*-yVozEAK|8yvn;?)w4Y)UW*Zc~tyWX6a!r&6sBy7b6xe=HPEgISU)zdG)7w^y@lq z&lL`DLjR`lKI0kdwD?Z+x=;ORwO*_5-@6xDRAG2W_wKO2Z)i9S4U{S>;}GUE2&KviV72D@eLj^%zxv~|~yV-=R(=$P(Mc9NyP=d)?wH$0#p%43G*SERf^$XChC!t4>ZOiOHRY z^oU +#include "../wots.h" +#include "../randombytes.h" +#include "../params.h" + +static void hexdump(unsigned char *a, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf("%02x", a[i]); +} + +int main() +{ +unsigned char seed[32]; +unsigned char pub_seed[32]; +wots_params params; +wots_set_params(¶ms, 32, 32, 16); + +int sig_len = params.len*params.n; + +unsigned char pk1[sig_len]; +unsigned char pk2[sig_len]; +unsigned char sig[sig_len]; +unsigned char addr[16] = {1,2,3,4}; + + unsigned char msg[32]; + int i; + + randombytes(seed, 32); + randombytes(pub_seed, 32); + randombytes(msg, 32); + //randombytes(addr, 16); + + wots_pkgen(pk1, seed, ¶ms, pub_seed, addr); + wots_sign(sig, msg, seed, ¶ms, pub_seed, addr); + wots_pkFromSig(pk2, sig, msg, ¶ms, pub_seed, addr); + + for(i=0;iphz`MA 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 RJ=njgw_9-%Dm^Qx{{xVH$cq2~ literal 0 HcmV?d00001 diff --git a/test/test_xmss.c b/test/test_xmss.c new file mode 100644 index 0000000..6473ccf --- /dev/null +++ b/test/test_xmss.c @@ -0,0 +1,83 @@ +#include +#include + +#include "../xmss.h" + +#define MLEN 3491 + + + +unsigned char sk[100]; +unsigned char pk[64]; +unsigned char mi[MLEN]; +unsigned long long smlen; +unsigned long long mlen; + +int main() +{ + int r; + unsigned long long i; + int m = 32; + int n = 32; + int h = 8; + int w = 16; + + xmss_params p; + xmss_params *params = &p; + xmss_set_params(params, m, n, h, w); + unsigned long long signature_length = 4+m+params->wots_par->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> 7) & 1);} + +#define SET_CHAIN_ADDRESS(a, v) {\ + a[14] = (a[14] & 1) | ((v << 1) & 255);\ + a[13] = (v >> 7) & 255;\ + a[12] = (a[12] & 254) | ((v >> 15) & 1);} + + +void wots_set_params(wots_params *params, int m, int n, int w) +{ + params->m = m; + params->n = n; + params->w = w; + params->log_w = (int) log2(w); + params->len_1 = (int) ceil(((8*m) / 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; +} + +/** + * Helper method for pseudorandom key generation + * Expands a 32 byte array into a len*n byte array + * this is done using chacha20 with nonce 0 and counter 0 + */ +static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, wots_params *params) +{ + prg(outseeds, params->keysize, inseed, 32); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays + * + * 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, int start, int steps, const wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]) +{ + uint i,j; + for(j=0;jn;j++) + out[j] = in[j]; + + for(i=start;i<(start+steps) && iw;i++){ + SET_HASH_ADDRESS(addr,i); +// printf("Hash %d:",i); +// hexdump(addr,16); +// printf("\n"); + hash_n_n(out,out, pub_seed, addr,params->n); + } +} + +/** + * base_w algorithm as described in draft. + * + * + */ +static void base_w(int *output, const unsigned char *input, int in_len, wots_params *params) +{ + int in = 0; + int out = 0; + int total = 0; + int bits = 0; + int consumed = 0; + + for(consumed = 0; consumed < 8 * in_len; consumed += params->log_w) + { + if(bits == 0){ + total = input[in_len - 1 - in]; + in++; + bits += 8; + } + bits -= params->log_w; + output[out] = (total >> bits) & (params->w - 1); + out++; + } +} + +/** + * Alternative base w algorithm for w = 16 to check... + */ +static void base_w_alternative(int *output, unsigned char *input, int in_len, wots_params *params) +{ + uint i = 0; + for(i = 0; i < in_len; i += 2) + { + output[i] = input[in_len - 1 - (i / 2)] >> 4; + output[i+1] = input[in_len - 1 - (i / 2)] & 0xf; + } +} + +void wots_pkgen(unsigned char *pk, const unsigned char *sk, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]) +{ + uint i; + expand_seed(pk, sk, params); + for(i=0;ilen;i++){ + SET_CHAIN_ADDRESS(addr,i); +// printf("Chain: %d\n",i); +// hexdump(addr,16); +// printf("\n"); + gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr); + } +} + + +void wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]) +{ + int basew[params->len]; + int csum = 0; + uint i=0; + + base_w(basew, msg, params->m, params); + + for(i=0;ilen_1;i++) + { + csum += params->w - 1 - basew[i]; + } + + csum = csum << ( 8 - ( ( params->len_2 * params->log_w ) % 8 )); + + int len_2_bytes = ((params->len_2 * params->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, csum_bytes, len_2_bytes, params); + + for(i = 0; i < params->len_2; i++) + { + basew[params->len_1 + i] = csum_basew[i]; + } + + expand_seed(sig, sk, params); + + for(i=0;ilen;i++){ + SET_CHAIN_ADDRESS(addr,i); +// printf("Chain: %d\n",i); +// hexdump(addr,16); +// printf("\n"); + gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr); + } +} + +void wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]) +{ + int basew[params->len]; + int csum = 0; + uint i=0; + + base_w(basew, msg, params->m, params); + + for(i=0;ilen_1;i++) + { + csum += params->w - 1 - basew[i]; + } + + csum = csum << ( 8 - ( ( params->len_2 * params->log_w ) % 8 )); + + int len_2_bytes = ((params->len_2 * params->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, csum_bytes, len_2_bytes, params); + + for(i = 0; i < params->len_2; i++) + { + basew[params->len_1 + i] = csum_basew[i]; + } + + for(i=0;ilen;i++){ + SET_CHAIN_ADDRESS(addr,i); +// printf("Chain: %d\n",i); +// hexdump(addr,16); +// printf("\n"); + gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr); + } +} diff --git a/wots.h b/wots.h new file mode 100644 index 0000000..3c03469 --- /dev/null +++ b/wots.h @@ -0,0 +1,52 @@ +#ifndef WOTS_H +#define WOTS_H + +#include "params.h" +/** + * WOTS parameter set + * + * Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02 + */ +typedef struct{ + int len_1; + int len_2; + int len; + int m; + int n; + int w; + int log_w; + int 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 m, 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, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]); + +/** + * 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, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]); + +/** + * 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, wots_params *params, const unsigned char *pub_seed, unsigned char addr[16]); + +#endif diff --git a/xmss.c b/xmss.c new file mode 100644 index 0000000..3d05a5f --- /dev/null +++ b/xmss.c @@ -0,0 +1,524 @@ +#include "xmss.h" +#include +#include +#include +#include + +#include "randombytes.h" +#include "wots.h" +#include "hash.h" +#include "prg.h" +#include "xmss_commons.h" + +// For testing +#include "stdio.h" + +/** + * Macros used to manipulate the respective fields + * in the 16byte hash address + */ +#define SET_OTS_BIT(a, b) {\ + a[9] = (a[9] & 253) | (b << 1);} + +#define SET_OTS_ADDRESS(a, v) {\ + a[12] = (a[12] & 1) | ((v << 1) & 255);\ + a[11] = (v >> 7) & 255;\ + a[10] = (v >> 15) & 255;\ + a[9] = (a[9] & 254) | ((v >> 23) & 1);} + +#define ZEROISE_OTS_ADDR(a) {\ + a[12] = (a[12] & 254);\ + a[13] = 0;\ + a[14] = 0;\ + a[15] = 0;} + +#define SET_LTREE_BIT(a, b) {\ + a[9] = (a[9] & 254) | b;} + +#define SET_LTREE_ADDRESS(a, v) {\ + a[12] = v & 255;\ + a[11] = (v >> 8) & 255;\ + a[10] = (v >> 16) & 255;} + +#define SET_LTREE_TREE_HEIGHT(a, v) {\ + a[13] = (a[13] & 3) | ((v << 2) & 255);} + +#define SET_LTREE_TREE_INDEX(a, v) {\ + a[15] = (a[15] & 3) | ((v << 2) & 255);\ + a[14] = (v >> 6) & 255;\ + a[13] = (a[13] & 252) | ((v >> 14) & 3);} + +#define SET_NODE_PADDING(a) {\ + a[10] = 0;\ + a[11] = a[11] & 3;} + +#define SET_NODE_TREE_HEIGHT(a, v) {\ + a[12] = (a[12] & 3) | ((v << 2) & 255);\ + a[11] = (a[11] & 252) | ((v >> 6) & 3);} + +#define SET_NODE_TREE_INDEX(a, v) {\ + a[15] = (a[15] & 3) | ((v << 2) & 255);\ + a[14] = (v >> 6) & 255;\ + a[13] = (v >> 14) & 255;\ + a[12] = (a[12] & 252) | ((v >> 22) & 3);} + + + /** + * Used for pseudorandom keygeneration, + * generates the seed for the WOTS keypair at address addr + */ +static void get_seed(unsigned char seed[32], const unsigned char *sk_seed, unsigned char addr[16]) +{ + // Make sure that chain addr, hash addr, and key bit are 0! + ZEROISE_OTS_ADDR(addr); + // Generate pseudorandom value + prg_with_counter(seed, 32, sk_seed, 32, addr); +} + +/** + * Initialize xmss params struct + * parameter names are the same as in the draft + */ +void xmss_set_params(xmss_params *params, int m, int n, int h, int w) +{ + params->h = h; + params->m = m; + params->n = n; + wots_params wots_par; + wots_set_params(&wots_par, m, n, w); + params->wots_par = &wots_par; +} + +/** + * 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, unsigned char addr[16]) +{ + uint l = params->wots_par->len; + uint n = params->n; + unsigned long i = 0; + uint height = 0; + + //ADRS.setTreeHeight(0); + SET_LTREE_TREE_HEIGHT(addr,height); + unsigned long bound; + while ( l > 1 ) + { + bound = l >> 1; //floor(l / 2); + for ( i = 0; i < bound; i = i + 1 ) { + //ADRS.setTreeIndex(i); + SET_LTREE_TREE_INDEX(addr,i); + //wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS); + hash_2n_n(wots_pk+i*n,wots_pk+i*2*n, pub_seed, addr, 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); + //l = ceil(l / 2); + l=(l>>1)+1; + } + else + { + //l = ceil(l / 2); + l=(l>>1); + } + //ADRS.setTreeHeight(ADRS.getTreeHeight() + 1); + height++; + SET_LTREE_TREE_HEIGHT(addr,height); + } + //return pk[0]; + memcpy(leaf,wots_pk,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, unsigned char ltree_addr[16], unsigned char ots_addr[16]) +{ + unsigned char seed[32]; + unsigned char pk[params->wots_par->keysize]; + + get_seed(seed, sk_seed, ots_addr); + wots_pkgen(pk, seed, params->wots_par, pub_seed, ots_addr); + + l_tree(leaf, pk, params, pub_seed, ltree_addr); +} + +/** + * Merkle's TreeHash algorithm. The address only needs to initialize the first 78 bits of addr. Everything else will be set by treehash. + * Currently only used for key generation. + * + */ +static void treehash(unsigned char *node, int height, int index, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const unsigned char addr[16]) +{ + + uint idx = index; + uint n = params->n; + // use three different addresses because at this point we use all three formats in parallel + unsigned char ots_addr[16]; + unsigned char ltree_addr[16]; + unsigned char node_addr[16]; + memcpy(ots_addr, addr, 10); + SET_OTS_BIT(ots_addr, 1); + memcpy(ltree_addr, addr, 10); + SET_OTS_BIT(ltree_addr, 0); + SET_LTREE_BIT(ltree_addr, 1); + memcpy(node_addr, ltree_addr, 10); + SET_LTREE_BIT(node_addr, 0); + SET_NODE_PADDING(node_addr); + + int lastnode,i; + unsigned char stack[(height+1)*n]; + unsigned int stacklevels[height+1]; + unsigned int stackoffset=0; + + lastnode = idx+(1<1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2]) + { + SET_NODE_TREE_HEIGHT(node_addr,stacklevels[stackoffset-1]); + SET_NODE_TREE_INDEX(node_addr, (idx >> (stacklevels[stackoffset-1]+1))); + hash_2n_n(stack+(stackoffset-2)*n,stack+(stackoffset-2)*n, pub_seed, + node_addr, n); + stacklevels[stackoffset-2]++; + stackoffset--; + } + } + for(i=0;in; + + int i,j; + unsigned char buffer[2*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;jh-1;i++) + { + SET_NODE_TREE_HEIGHT(addr,i); + leafidx >>= 1; + SET_NODE_TREE_INDEX(addr, leafidx); + if(leafidx&1) + { + hash_2n_n(buffer+n,buffer,pub_seed, addr, n); + for(j=0;jh-1)); + leafidx >>= 1; + SET_NODE_TREE_INDEX(addr, leafidx); + hash_2n_n(root,buffer,pub_seed,addr,n); +} + +/** + * Computes the authpath and the root. This method is using a lot of space as we build the whole tree and then select the authpath nodes. + * 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, unsigned char addr[16]) +{ + uint i, j, level; + int n = params->n; + int h = params->h; + + unsigned char tree[2*(1< 0; i>>=1) + { + SET_NODE_TREE_HEIGHT(node_addr, level); + // Inner loop: for each pair of sibling nodes + for (j = 0; j < i; j+=2) + { + SET_NODE_TREE_INDEX(node_addr, j>>1); + hash_2n_n(tree + (i>>1)*n + (j>>1) * n, tree + i*n + j*n, pub_seed, node_addr, n); + } + level++; + } + + // copy authpath + for(i=0;i>i)*n + ((leaf_idx >> i) ^ 1) * n, n); + + // copy root + memcpy(root, tree+n, n); +} + + +/* + * Generates a XMSS key pair for a given parameter set. + * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED] + * Format pk: [root || PUB_SEED] omitting algo oid. + */ +int xmss_keypair(unsigned char *pk, unsigned char *sk, xmss_params *params) +{ + uint n = params->n; + uint m = params->m; + // Set idx = 0 + sk[0] = 0; + sk[1] = 0; + sk[2] = 0; + sk[3] = 0; + // Init SK_SEED (n byte), SK_PRF (m byte), and PUB_SEED (n byte) + randombytes(sk+4,2*n+m); + // Copy PUB_SEED to public key + memcpy(pk+n, sk+4+n+m,n); + + unsigned char addr[16] = {0,0,0,0}; + // Compute root + treehash(pk, params->h, 0, sk+4, params, sk+4+n+m, addr); + return 0; +} + +/** + * Signs a message. + * Returns + * 1. an array containing the signature followed by the message AND + * 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, unsigned char* pk) +{ + uint n = params->n; + uint m = params->m; + + // Extract SK + unsigned long idx = (sk[0] << 24) | (sk[1] << 16) | (sk[2] << 8) || sk[3]; + unsigned char sk_seed[n]; + memcpy(sk_seed,sk+4,n); + unsigned char sk_prf[m]; + memcpy(sk_prf,sk+4+n,m); + unsigned char pub_seed[n]; + memcpy(pub_seed,sk+4+n+m,n); + + // Update SK + sk[0] = ((idx + 1) >> 24) & 255; + sk[1] = ((idx + 1) >> 16) & 255; + sk[2] = ((idx + 1) >> 8) & 255; + sk[3] = (idx + 1) & 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! + + // Init working params + unsigned long long i; + unsigned char R[m]; + unsigned char msg_h[m]; + unsigned char root[n]; + unsigned char ots_seed[n]; + unsigned char ots_addr[16] = {0,0,0,0}; + + // --------------------------------- + // Message Hashing + // --------------------------------- + + // Message Hash: + // First compute pseudorandom key + prf_m(R, msg, msglen, sk_prf, m); + // Then use it for message digest + hash_m(msg_h, msg, msglen, R, m, m); + + // Start collecting signature + *sig_msg_len = 0; + + // Copy index to signature + sig_msg[0] = (idx >> 24) & 255; + sig_msg[1] = (idx >> 16) & 255; + sig_msg[2] = (idx >> 8) & 255; + sig_msg[3] = idx & 255; + + sig_msg += 4; + *sig_msg_len += 4; + + // Copy R to signature + for(i=0; iwots_par, pub_seed, ots_addr); + + sig_msg += params->wots_par->keysize; + *sig_msg_len += params->wots_par->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; + + //DEBUG + for(i=0;in; + uint m = params->m; + + 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[m]; + + unsigned char pub_seed[n]; + memcpy(pub_seed,pk+n,n); + + // Init addresses + unsigned char ots_addr[16] = {0,0,0,0}; + unsigned char ltree_addr[16]; + unsigned char node_addr[16]; + + SET_OTS_BIT(ots_addr, 1); + + memcpy(ltree_addr, ots_addr, 10); + SET_OTS_BIT(ltree_addr, 0); + SET_LTREE_BIT(ltree_addr, 1); + + memcpy(node_addr, ltree_addr, 10); + SET_LTREE_BIT(node_addr, 0); + SET_NODE_PADDING(node_addr); + + // Extract index + idx = (sig_msg[0] << 24) | (sig_msg[1] << 16) | (sig_msg[2] << 8) || sig_msg[3]; + sig_msg += 4; + sig_msg_len -= 4; + + // hash message (recall, R is now on pole position at sig_msg + unsigned long long tmp_sig_len = m+params->wots_par->keysize+params->h*n; + m_len = sig_msg_len - tmp_sig_len; + hash_m(msg_h, sig_msg + tmp_sig_len, m_len, sig_msg, m, m); + + sig_msg += m; + sig_msg_len -= m; + + //----------------------- + // Verify signature + //----------------------- + + // Prepare Address + SET_OTS_ADDRESS(ots_addr,idx); + // Check WOTS signature + wots_pkFromSig(wots_pk, sig_msg, msg_h, params->wots_par, pub_seed, ots_addr); + + sig_msg += params->wots_par->keysize; + sig_msg_len -= params->wots_par->keysize; + + // Compute Ltree + SET_LTREE_ADDRESS(ltree_addr, idx); + l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr); + + // Compute root + validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr); + + sig_msg += params->h*n; + sig_msg_len -= params->h*n; + + for(i=0;i +#include + +void to_byte(unsigned char *out, uint in, int bytes) +{ + int i; + for(i = 0; i < bytes; i++){ + out[i] = in & 0xff; + in = in >> 8; + } +} + +void hexdump(const unsigned char *a, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf("%02x", a[i]); +} \ No newline at end of file diff --git a/xmss_commons.h b/xmss_commons.h new file mode 100644 index 0000000..be21195 --- /dev/null +++ b/xmss_commons.h @@ -0,0 +1,8 @@ +#ifndef XMSS_COMMONS_H +#define XMSS_COMMONS_H + +#include + +void to_byte(unsigned char *output, uint in, int bytes); +void hexdump(const unsigned char *a, size_t len); +#endif \ No newline at end of file diff --git a/zerobytes.c b/zerobytes.c new file mode 100644 index 0000000..bf04984 --- /dev/null +++ b/zerobytes.c @@ -0,0 +1,9 @@ +#include "zerobytes.h" + +unsigned char *zerobytes(unsigned char *r,unsigned long long n) +{ + volatile unsigned char *p=r; + while (n--) + *(p++) = 0; + return r; +} diff --git a/zerobytes.h b/zerobytes.h new file mode 100644 index 0000000..18d0324 --- /dev/null +++ b/zerobytes.h @@ -0,0 +1,6 @@ +#ifndef ZEROBYTES_H +#define ZEROBYTES_H + +unsigned char *zerobytes(unsigned char *r,unsigned long long n); + +#endif