diff --git a/crypto/sm2/build.info b/crypto/sm2/build.info index 6b650d73..8dcca88e 100644 --- a/crypto/sm2/build.info +++ b/crypto/sm2/build.info @@ -1,3 +1,3 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=sm2_err.c sm2_asn1.c sm2_id.c sm2_sign.c sm2_enc.c \ - sm2_oct.c sm2_exch.c sm2_kmeth.c + sm2_oct.c sm2_exch.c sm2_kmeth.c sm2_cosign.c diff --git a/crypto/sphincs/.clang-format b/crypto/sphincs/.clang-format new file mode 100644 index 00000000..95ba58ea --- /dev/null +++ b/crypto/sphincs/.clang-format @@ -0,0 +1,46 @@ +AccessModifierOffset: 0 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackParameters: false +BreakBeforeBinaryOperators: false +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 80 +CommentPragmas: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 0 +ContinuationIndentWidth: 0 +Cpp11BracedListStyle: false +DerivePointerBinding: false +IndentCaseLabels: false +IndentFunctionDeclarationAfterType: false +IndentWidth: 4 +Language: Cpp +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 100 +PenaltyBreakComment: 100 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 100 +PenaltyExcessCharacter: 1 +PenaltyReturnTypeOnItsOwnLine: 20 +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: Always +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never diff --git a/crypto/sphincs/.gitignore b/crypto/sphincs/.gitignore new file mode 100644 index 00000000..3a09df25 --- /dev/null +++ b/crypto/sphincs/.gitignore @@ -0,0 +1,3 @@ +bench +*.o +*.dSYM diff --git a/crypto/sphincs/LICENSE b/crypto/sphincs/LICENSE new file mode 100644 index 00000000..6ed4a65b --- /dev/null +++ b/crypto/sphincs/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017 Nagravision S.A. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/crypto/sphincs/Makefile b/crypto/sphincs/Makefile new file mode 100644 index 00000000..5f6d6e6a --- /dev/null +++ b/crypto/sphincs/Makefile @@ -0,0 +1,52 @@ +SRC=$(wildcard *.c) $(wildcard primitives/*.c) +OBJ=$(patsubst %.c, %.o, $(SRC)) + +CFLAGS_COMMON=-std=c89 -pedantic -Wall -Wextra -Wno-long-long -march=native +LDFLAGS=-lcrypto + +VERSION_S= -DPORS_k=24 -DMERKLE_h=5 -DGRAVITY_d=1 -DGRAVITY_c=10 +VERSION_M= -DPORS_k=32 -DMERKLE_h=5 -DGRAVITY_d=7 -DGRAVITY_c=15 +VERSION_L= -DPORS_k=28 -DMERKLE_h=5 -DGRAVITY_d=10 -DGRAVITY_c=14 + +VERSION=$(VERSION_S) + +CFLAGS_COMMON+=$(VERSION) +CFLAGS=$(CFLAGS_COMMON) -O3 -fomit-frame-pointer +CFLAGS_SANITIZED=$(CFLAGS_COMMON) -fsanitize=address,undefined -g + +KERNEL_NAME := $(shell uname -s) +ifeq ($(KERNEL_NAME), Darwin) + CFLAGS += -I/usr/local/opt/openssl/include + LDFLAGS += -L/usr/local/opt/openssl/lib +endif + +all: help + +help: + @echo "Please choose a target:" + @echo "\tanalyze\t\t runs static analyzers" + @echo "\tbench\t\t runs speed benchmarks" + @echo "\tclean\t\t cleans up" + @echo "\tformat\t\t formats the code using .clang-format rules" + +bench: $(SRC) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o bench + ./bench + +format: + clang-format -i *.c *.h + +analyze: + cppcheck . --std=c89 + scan-build gcc -c $(CFLAGS_SANITIZED) *.c + cppclean . + rm -f *.o + +cloc: + cloc $(wildcard *.c) $(wildcard *.h) + +clean: + rm -f bench *.dSYM *.o + +.PHONY: clean format analyze cloc bench + diff --git a/crypto/sphincs/aes.h b/crypto/sphincs/aes.h new file mode 100644 index 00000000..8a7b25b4 --- /dev/null +++ b/crypto/sphincs/aes.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include + +int aesctr256 (uint8_t *out, const uint8_t *sk, const void *counter, int bytes); + +int aesctr256_zeroiv (uint8_t *out, const uint8_t *sk, int bytes); diff --git a/crypto/sphincs/api.h b/crypto/sphincs/api.h new file mode 100644 index 00000000..a2a66644 --- /dev/null +++ b/crypto/sphincs/api.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef CRYPTO_BYTES + +/* setting CRYPTO_BYTES to the static structure size, for simplicity, + as opposed to the actual message-dependent signature size */ + +#define CRYPTO_ALGNAME "Gravity-SPHINCS S" +#define CRYPTO_SECRETKEYBYTES 65568 +#define CRYPTO_PUBLICKEYBYTES 32 +#define CRYPTO_BYTES 15728 // 12640 + +#if 0 +#define CRYPTO_ALGNAME "Gravity-SPHINCS M" +#define CRYPTO_SECRETKEYBYTES 2097184 +#define CRYPTO_PUBLICKEYBYTES 32 +#define CRYPTO_BYTES 34064 // 28929 +#endif + +#if 0 +#define CRYPTO_ALGNAME "Gravity-SPHINCS L" +#define CRYPTO_SECRETKEYBYTES 1048608 +#define CRYPTO_PUBLICKEYBYTES 32 +#define CRYPTO_BYTES 38768 // 35168 +#endif + +#endif + +int crypto_sign_keypair (unsigned char *pk, unsigned char *sk); + +int crypto_sign (unsigned char *sm, + unsigned long long *smlen, + const unsigned char *m, + unsigned long long mlen, + const unsigned char *sk); + +int crypto_sign_open (unsigned char *m, + unsigned long long *mlen, + const unsigned char *sm, + unsigned long long smlen, + const unsigned char *pk); diff --git a/crypto/sphincs/batch.c b/crypto/sphincs/batch.c new file mode 100644 index 00000000..3159b591 --- /dev/null +++ b/crypto/sphincs/batch.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "batch.h" + +#include "merkle.h" + +int batch_init (struct batch_buf *buf) { + buf->count = 0; + return GRAVITY_OK; +} + +int batch_append (struct batch_buf *buf, const uint8_t *msg, uint64_t len, uint32_t *index) { + if (buf->count == MAX_BATCH_COUNT) return GRAVITY_ERR_BATCH; + + /* TODO: randomize this hash? */ + hash_to_N (&buf->buf[buf->count], msg, len); + + *index = buf->count; + ++buf->count; + + return GRAVITY_OK; +} + +int batch_group (struct batch_group *group, struct batch_buf *buf) { + int height = LOG_MAX_BATCH_COUNT; + int n = 1 << height; + int offset = n - 1; + + struct hash *src; + struct hash *dst; + uint32_t count; + int i; + + /* Check batch count */ + count = buf->count; + if (count == 0) return GRAVITY_ERR_BATCH; + group->count = count; + + /* Leaves */ + dst = &group->tree[offset]; + + hashcpyN (dst, buf->buf, count); + for (i = count; i < n; ++i) hashcpy (&dst[i], &buf->buf[0]); + + /* Compress until root */ + while (height > 0) { + offset >>= 1; + --height; + + src = dst; + dst = &group->tree[offset]; + hash_compress_pairs (dst, src, 1 << height); + } + + return GRAVITY_OK; +} + +int batch_extract (const struct batch_group *group, struct batch_auth *auth, uint32_t index) { + int height = LOG_MAX_BATCH_COUNT; + int n = 1 << height; + int offset = n - 1; + + uint32_t count; + int i, sibling; + + /* Check batch count */ + count = group->count; + if (index >= count) return GRAVITY_ERR_BATCH; + + /* Convert row index into tree index */ + auth->index = offset + index; + + /* Copy auth path */ + for (i = 0; i < height; ++i) { + sibling = index ^ 1; + hashcpy (&auth->auth[i], &group->tree[offset + sibling]); + index >>= 1; + offset >>= 1; + } + + return GRAVITY_OK; +} + +void batch_compress_auth (struct hash *node, + const struct batch_auth *auth, + const uint8_t *msg, + uint64_t len) { + /* Compute Merkle tree root */ + int height = LOG_MAX_BATCH_COUNT; + hash_to_N (node, msg, len); + merkle_compress_auth (node, auth->index, auth->auth, height); +} diff --git a/crypto/sphincs/batch.h b/crypto/sphincs/batch.h new file mode 100644 index 00000000..e2f65edf --- /dev/null +++ b/crypto/sphincs/batch.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "hash.h" + +struct batch_buf { + struct hash buf[MAX_BATCH_COUNT]; + uint32_t count; +}; + +struct batch_group { + struct hash tree[2 * MAX_BATCH_COUNT - 1]; + uint32_t count; +}; + +struct batch_auth { + struct hash auth[LOG_MAX_BATCH_COUNT]; + uint32_t index; +}; + +int batch_init (struct batch_buf *buf); + +int batch_append (struct batch_buf *buf, const uint8_t *msg, uint64_t len, uint32_t *index); + +int batch_group (struct batch_group *group, struct batch_buf *buf); + +int batch_extract (const struct batch_group *group, struct batch_auth *auth, uint32_t index); + +void batch_compress_auth (struct hash *node, + const struct batch_auth *auth, + const uint8_t *msg, + uint64_t len); diff --git a/crypto/sphincs/bench.c b/crypto/sphincs/bench.c new file mode 100644 index 00000000..a14dc477 --- /dev/null +++ b/crypto/sphincs/bench.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ + +#include "gravity.h" +#include "randombytes.h" +#include "sign.h" +#include +#include +#include +#include + +#define SKLEN sizeof (struct gravity_sk) +#define PKLEN sizeof (struct gravity_pk) +#define SIGLEN sizeof (struct gravity_sign) +#define N 32 +#define ROUNDS 3 + + +int main () { + + unsigned long long smlen; + unsigned long long mlen = N; + uint8_t sk[SKLEN]; + uint8_t pk[PKLEN]; + uint8_t m[N]; + uint8_t *sm = malloc (N + SIGLEN); + struct timeval tm1, tm2; + unsigned long long usecs; + int ret = -1; + int i; + + if (!sm) { + fprintf (stderr, "error: sm malloc failed\n"); + ret = 1; + goto label_exit_0; + } + + printf ("k\t%d\n", PORS_k); + printf ("h\t%d\n", MERKLE_h); + printf ("d\t%d\n", GRAVITY_d); + printf ("c\t%d\n", GRAVITY_c); + printf ("sk len\t%d\n", (int)SKLEN); + printf ("pk len\t%d\n", (int)PKLEN); + printf ("sig len\t%d\n", (int)SIGLEN); + +#define MEASURE(s) \ + do { \ + gettimeofday (&tm2, NULL); \ + usecs = 1000000 * (tm2.tv_sec - tm1.tv_sec) + (tm2.tv_usec - tm1.tv_usec); \ + printf ("\n# %s\n", s); \ + printf ("%.2f usec\n", (double)usecs); \ + } while (0) + + randombytes (m, N); + + gettimeofday (&tm1, NULL); + + if (crypto_sign_keypair (pk, sk)) { + fprintf (stderr, "error: crypto_sign_keypair failed\n"); + ret = 1; + goto label_exit_2; + } + + MEASURE ("crypto_sign_keypair"); + + for (i = 0; i < ROUNDS; ++i) { + + gettimeofday (&tm1, NULL); + + if (crypto_sign (sm, &smlen, m, mlen, sk)) { + fprintf (stderr, "error: crypto_sign failed\n"); + ret = 1; + goto label_exit_2; + } + + MEASURE ("crypto_sign"); + gettimeofday (&tm1, NULL); + + + if (crypto_sign_open (m, &mlen, sm, smlen, pk)) { + fprintf (stderr, "error: crypto_sign_open failed\n"); + ret = 1; + goto label_exit_2; + } + + m[0] ^= sm[33]; + + MEASURE ("crypto_sign_open"); + } + + ret = 0; +label_exit_2: + free (sm); +label_exit_0: + return ret; +} diff --git a/crypto/sphincs/common.h b/crypto/sphincs/common.h new file mode 100644 index 00000000..1318b9f1 --- /dev/null +++ b/crypto/sphincs/common.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#define HASH_SIZE 32 + +#define WOTS_LOG_ell1 6 +#define WOTS_ell1 (1 << (WOTS_LOG_ell1)) +#define WOTS_chksum 3 +#define WOTS_ell ((WOTS_ell1) + (WOTS_chksum)) +#define WOTS_w 16 + +/* set by compile flags */ +#if 0 +#define PORS_k 28 +#define MERKLE_h 5 +#define GRAVITY_d 10 +#define GRAVITY_c 14 +#endif + +#define PORS_tau 16 +#define PORS_t (1 << (PORS_tau)) + +#define MERKLE_hhh (1 << (MERKLE_h)) + +#define GRAVITY_ccc (1 << (GRAVITY_c)) +#define GRAVITY_h ((MERKLE_h) * (GRAVITY_d) + (GRAVITY_c)) + +#if GRAVITY_h < 64 +#define GRAVITY_mask ~(0xFFFFFFFFFFFFFFFFull << (GRAVITY_h)) +#else +#define GRAVITY_mask 0xFFFFFFFFFFFFFFFFull +#endif + +#define LOG_MAX_BATCH_COUNT 10 +#define MAX_BATCH_COUNT (1 << (LOG_MAX_BATCH_COUNT)) + + +#define GRAVITY_OK 0 +#define GRAVITY_ERR_VERIF 1 +#define GRAVITY_ERR_ALLOC 2 +#define GRAVITY_ERR_BATCH 3 + +#define U8TO32(p) \ + (((uint32_t) ((p)[0]) << 24) | ((uint32_t) ((p)[1]) << 16) | \ + ((uint32_t) ((p)[2]) << 8) | ((uint32_t) ((p)[3]))) diff --git a/crypto/sphincs/gravity.c b/crypto/sphincs/gravity.c new file mode 100644 index 00000000..6e8baa94 --- /dev/null +++ b/crypto/sphincs/gravity.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ + +#include "gravity.h" + +#include +#include + + +int gravity_gensk (struct gravity_sk *sk) { + int n = GRAVITY_ccc; + struct hash *src; + struct hash *dst = sk->cache; + struct merkle_pk mpk; + struct address address; + int i; + int res; + + /* Create sub Merkle trees */ + address.layer = 0; + for (i = 0; i < n; ++i) { + address.index = i * MERKLE_hhh; + res = merkle_genpk (&sk->seed, &address, &mpk); + if (res != GRAVITY_OK) return res; + + hashcpy (&dst[i], &mpk.k); + } + + /* Cache layers of Merkle tree */ + for (i = 0; i < GRAVITY_c; ++i) { + src = dst; + dst += n; + n >>= 1; + + hash_compress_pairs (dst, src, n); + } + + return GRAVITY_OK; +} + +int gravity_genpk (const struct gravity_sk *sk, struct gravity_pk *pk) { + hashcpy (&pk->k, &sk->cache[2 * GRAVITY_ccc - 2]); + + return GRAVITY_OK; +} + +int gravity_sign (const struct gravity_sk *sk, struct gravity_sign *sign, const struct hash *msg) { + struct hash buf[2]; + struct address address; + struct pors_subset subset; + struct pors_sk *psk; + struct porst_pk ppk; + struct merkle_pk mpk; + struct hash h; + int layer; + int res; +#if GRAVITY_c > 0 + int offset; + int n = GRAVITY_ccc; + int i, sibling; +#endif + + /* Generate "randomness" from message and secret salt */ + hashcpy (&buf[0], &sk->salt); + hashcpy (&buf[1], msg); + hash_2N_to_N (&sign->rand, buf); + + /* Generate address and PORST indices */ + pors_randsubset (&sign->rand, msg, &address.index, &subset); + + /* PORST */ + psk = malloc (sizeof (struct pors_sk)); + if (psk == NULL) return GRAVITY_ERR_ALLOC; + + address.layer = GRAVITY_d; + pors_gensk (&sk->seed, &address, psk); + + res = octoporst_sign (psk, &sign->op_sign, &ppk, &subset); + /* TODO: wipe key */ + free (psk); + + if (res != GRAVITY_OK) return res; + + /* Store PORST pubkey into h */ + hashcpy (&h, &ppk.k); + + /* Hyper tree */ + for (layer = 0; layer < GRAVITY_d; ++layer) { + /* Sign h with Merkle tree and obtain public key */ + --address.layer; + res = merkle_sign (&sk->seed, &address, &sign->merkle[layer], &h, &mpk); + if (res != GRAVITY_OK) return res; + hashcpy (&h, &mpk.k); + + /* Reduce address for next layer */ + address.index >>= MERKLE_h; + } + +#if GRAVITY_c > 0 + /* Cached Merkle tree */ + offset = 0; + for (i = 0; i < GRAVITY_c; ++i) { + sibling = address.index ^ 1; + hashcpy (&sign->auth[i], &sk->cache[offset + sibling]); + + address.index >>= 1; + offset += n; + n >>= 1; + } +#endif + + return GRAVITY_OK; +} + +int gravity_verify (const struct gravity_pk *pk, + const struct gravity_sign *sign, + const struct hash *msg) { + struct address address; + struct pors_subset subset; + struct porst_pk ppk; + struct merkle_pk mpk; + struct hash h; + int layer; + int res; + + /* Generate address and PORST indices */ + pors_randsubset (&sign->rand, msg, &address.index, &subset); + + /* PORST */ + res = octoporst_extract (&ppk, &sign->op_sign, &subset); + if (res != GRAVITY_OK) return res; + + /* Store PORST pubkey into h */ + hashcpy (&h, &ppk.k); + + /* Hyper tree */ + address.layer = GRAVITY_d; + for (layer = 0; layer < GRAVITY_d; ++layer) { + /* Obtain Merkle tree root */ + --address.layer; + merkle_extract (&mpk, &address, &sign->merkle[layer], &h); + hashcpy (&h, &mpk.k); + + /* Reduce address for next layer */ + address.index >>= MERKLE_h; + } + +#if GRAVITY_c > 0 + /* Cached Merkle tree */ + merkle_compress_auth (&h, address.index, sign->auth, GRAVITY_c); +#endif + + if (hashcmp (&h, &pk->k)) return GRAVITY_ERR_VERIF; + + return GRAVITY_OK; +} + +int gravity_loadsign (struct gravity_sign *sign, const uint8_t *_sign, size_t _len) { + size_t baselen = HASH_SIZE + GRAVITY_d * sizeof (struct merkle_sign) +#if GRAVITY_c > 0 + + GRAVITY_c * HASH_SIZE +#endif + ; + + if (_len < baselen) return GRAVITY_ERR_VERIF; + _len -= baselen; + + memcpy (&sign->rand, _sign, HASH_SIZE); + _sign += HASH_SIZE; + + if (octoporst_loadsign (&sign->op_sign, _sign, _len) != GRAVITY_OK) + return GRAVITY_ERR_VERIF; + _sign += _len; + + memcpy (sign->merkle, _sign, GRAVITY_d * sizeof (struct merkle_sign)); + +#if GRAVITY_c > 0 + _sign += GRAVITY_d * sizeof (struct merkle_sign); + memcpy (sign->auth, _sign, GRAVITY_c * HASH_SIZE); +#endif + + return GRAVITY_OK; +} + +int gravity_signcmp (const struct gravity_sign *sign1, const struct gravity_sign *sign2) { + if (hashcmp (&sign1->rand, &sign2->rand)) return 1; + if (octoporst_signcmp (&sign1->op_sign, &sign2->op_sign)) return 1; + if (memcmp (&sign1->merkle, &sign2->merkle, GRAVITY_d * sizeof (struct merkle_sign))) + return 1; +#if GRAVITY_c > 0 + if (hashcmpN (sign1->auth, sign2->auth, GRAVITY_c)) return 1; +#endif + + return 0; +} diff --git a/crypto/sphincs/gravity.h b/crypto/sphincs/gravity.h new file mode 100644 index 00000000..fc94320a --- /dev/null +++ b/crypto/sphincs/gravity.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "merkle.h" +#include "pors.h" + +struct gravity_sk { + struct hash seed; + struct hash salt; + struct hash cache[2 * GRAVITY_ccc - 1]; +}; + +struct gravity_pk { + struct hash k; +}; + +struct gravity_sign { + struct hash rand; + struct octoporst_sign op_sign; + struct merkle_sign merkle[GRAVITY_d]; +#if GRAVITY_c > 0 + struct hash auth[GRAVITY_c]; +#endif +}; + +int gravity_gensk (struct gravity_sk *sk); + +int gravity_genpk (const struct gravity_sk *sk, struct gravity_pk *pk); + +int gravity_sign (const struct gravity_sk *sk, struct gravity_sign *sign, const struct hash *msg); + +int gravity_verify (const struct gravity_pk *pk, + const struct gravity_sign *sign, + const struct hash *msg); + +/* Serialization */ +int gravity_loadsign (struct gravity_sign *sign, const uint8_t *_sign, size_t _len); + +int gravity_signcmp (const struct gravity_sign *sign1, const struct gravity_sign *sign2); diff --git a/crypto/sphincs/hash.h b/crypto/sphincs/hash.h new file mode 100644 index 00000000..f3065a29 --- /dev/null +++ b/crypto/sphincs/hash.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "common.h" + +#include +#include + +/* TODO: portable alignment */ +struct hash { + uint8_t h[HASH_SIZE]; +} __attribute__ ((aligned (16))); + +struct address { + uint64_t index; + uint32_t layer; +}; + + +void hash_N_to_N (struct hash *dst, const struct hash *src); +void hash_N_to_N_chain (struct hash *dst, const struct hash *src, int chainlen); +void hash_2N_to_N (struct hash *dst, const struct hash *src); +void hash_to_N (struct hash *dst, const uint8_t *src, uint64_t srclen); + +/* Compress 2*count input hashes into count output hashes, pairwise */ +void hash_compress_pairs (struct hash *dst, const struct hash *src, int count); +/* Compress count hashes into a single hash */ +void hash_compress_all (struct hash *dst, const struct hash *src, int count); +/* Compute count hashes in parallel */ +void hash_parallel (struct hash *dst, const struct hash *src, int count); +/* Compute count hash chains of length chainlen in parallel */ +void hash_parallel_chains (struct hash *dst, const struct hash *src, int count, int chainlen); + + +/* int hashcmp(const struct hash *a, const struct hash *b); */ +#define hashcmp(a, b) memcmp ((a)->h, (b)->h, HASH_SIZE) +/* int hashcmpN(const struct hash *a, const struct hash *b, size_t count); */ +#define hashcmpN(a, b, N) memcmp ((a)->h, (b)->h, (N)*HASH_SIZE) +/* void hashcpy(struct hash *dst, const struct hash *src); */ +#define hashcpy(a, b) memcpy ((a)->h, (b)->h, HASH_SIZE) +/* void hashcpyN(struct hash *dst, const struct hash *src, size_t count); */ +#define hashcpyN(a, b, N) memcpy ((a)->h, (b)->h, (N)*HASH_SIZE) +/* void hashzero(struct hash *dst); */ +#define hashzero(a) memset ((a)->h, 0, HASH_SIZE) +/* void swap(struct hash *a, struct hash *b, struct hash *tmp); */ +#define hashswap(a, b, tmp) \ + do { \ + (tmp) = (a); \ + (a) = (b); \ + (b) = (tmp); \ + } while (0); diff --git a/crypto/sphincs/ltree.c b/crypto/sphincs/ltree.c new file mode 100644 index 00000000..6fe40cf4 --- /dev/null +++ b/crypto/sphincs/ltree.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "ltree.h" + +void ltree (struct hash *buf, int count, struct hash *root) { + struct hash *src = &buf[count]; + struct hash *dst = &buf[0]; + struct hash *tmp; + int newcount; + + while (count > 1) { + /* Swap buffers */ + hashswap (src, dst, tmp); + + /* Compute all hashes at current layer */ + newcount = count >> 1; + hash_compress_pairs (dst, src, newcount); + if (count & 1) { + hashcpy (&dst[newcount], &src[count - 1]); + ++newcount; + } + + count = newcount; + } + + hashcpy (root, dst); +} diff --git a/crypto/sphincs/ltree.h b/crypto/sphincs/ltree.h new file mode 100644 index 00000000..6daad929 --- /dev/null +++ b/crypto/sphincs/ltree.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "hash.h" + +void ltree (struct hash *buf, int count, struct hash *root); diff --git a/crypto/sphincs/merkle.c b/crypto/sphincs/merkle.c new file mode 100644 index 00000000..d9814bc4 --- /dev/null +++ b/crypto/sphincs/merkle.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "merkle.h" + +#include + +int merkle_base_address (const struct address *address, struct address *base_address) { + int index = address->index & (MERKLE_hhh - 1); + if (base_address != NULL) { + base_address->layer = address->layer; + base_address->index = address->index - index; + } + return index; +} + +int merkle_genpk (const struct hash *key, const struct address *address, struct merkle_pk *pk) { + struct address base_address; + struct hash *buf; + struct wots_sk wsk; + struct lwots_pk wpk; + int j; + + merkle_base_address (address, &base_address); + + buf = merkle_alloc_buf (MERKLE_h); + if (buf == NULL) return GRAVITY_ERR_ALLOC; + + /* Leaves */ + for (j = 0; j < MERKLE_hhh; ++j) { + wots_gensk (key, &base_address, &wsk); + lwots_genpk (&wsk, &wpk); + hashcpy (&buf[j], &wpk.k); + + ++base_address.index; + } + + /* Merkle tree */ + merkle_compress_all (buf, MERKLE_h, &pk->k); + + merkle_free_buf (buf); + return GRAVITY_OK; +} + +int merkle_sign (const struct hash *key, + const struct address *address, + struct merkle_sign *sign, + const struct hash *msg, + struct merkle_pk *pk) { + struct address base_address; + struct hash *buf; + struct wots_sk wsk; + struct lwots_pk wpk; + int index; + int j; + + index = merkle_base_address (address, &base_address); + + buf = merkle_alloc_buf (MERKLE_h); + if (buf == NULL) return GRAVITY_ERR_ALLOC; + + /* Leaves */ + for (j = 0; j < MERKLE_hhh; ++j) { + wots_gensk (key, &base_address, &wsk); + lwots_genpk (&wsk, &wpk); + hashcpy (&buf[j], &wpk.k); + + ++base_address.index; + + /* WOTS */ + if (j == index) wots_sign (&wsk, &sign->wots, msg); + } + + /* Merkle tree */ + merkle_gen_auth (buf, MERKLE_h, sign->auth, index, pk == NULL ? NULL : &pk->k); + + merkle_free_buf (buf); + return GRAVITY_OK; +} + +void merkle_extract (struct merkle_pk *pk, + const struct address *address, + const struct merkle_sign *sign, + const struct hash *msg) { + struct lwots_pk wpk; + int index = merkle_base_address (address, NULL); + + /* WOTS */ + lwots_extract (&wpk, &sign->wots, msg); + + /* Auth path */ + merkle_compress_auth (&wpk.k, index, sign->auth, MERKLE_h); + hashcpy (&pk->k, &wpk.k); +} + + +/* Helper functions */ +/* Alloc/free a buffer large enough to store leaves and compress them */ +struct hash *merkle_alloc_buf (int height) { + int n = 1 << height; + return malloc (2 * n * HASH_SIZE); +} + +void merkle_free_buf (struct hash *buf) { + /* TODO: wipe buffer? */ + free (buf); +} + +/* Compress leaves from level height until root */ +void merkle_compress_all (struct hash *buf, int height, struct hash *root) { + int n = 1 << height; + struct hash *src = &buf[n]; + struct hash *dst = &buf[0]; + struct hash *tmp; + int l; + + for (l = 0; l < height; ++l) { + /* Swap buffers */ + hashswap (src, dst, tmp); + n >>= 1; + + /* Compute all hashes at current layer */ + hash_compress_pairs (dst, src, n); + } + + hashcpy (root, dst); +} + +/* Compute authentication path */ +void merkle_gen_auth (struct hash *buf, int height, struct hash *auth, int index, struct hash *root) { + int n = 1 << height; + struct hash *src = &buf[n]; + struct hash *dst = &buf[0]; + struct hash *tmp; + int l, sibling; + + for (l = 0; l < height; ++l) { + /* Copy auth path */ + sibling = index ^ 1; + hashcpy (&auth[l], &dst[sibling]); + index >>= 1; + + /* Swap buffers */ + hashswap (src, dst, tmp); + n >>= 1; + + /* Compute all hashes at current layer */ + hash_compress_pairs (dst, src, n); + } + + /* Public key */ + if (root != NULL) hashcpy (root, dst); +} + +/* Compress node with its auth path on height_diff levels */ +int merkle_compress_auth (struct hash *node, int index, const struct hash *auth, int height_diff) { + struct hash buf[2]; + int l; + + for (l = 0; l < height_diff; ++l) { + if (index % 2 == 0) { + hashcpy (&buf[0], node); + hashcpy (&buf[1], &auth[l]); + } else { + hashcpy (&buf[0], &auth[l]); + hashcpy (&buf[1], node); + } + + hash_2N_to_N (node, buf); + + index >>= 1; + } + + return index; +} + +void merkle_gen_octopus (struct hash *buf, + int height, + struct hash *octopus, + int *octolen, + struct hash *root, + int *indices, + int count) { + int n = 1 << height; + struct hash *src = &buf[n]; + struct hash *dst = &buf[0]; + struct hash *tmp; + int i, j, l; + int len = 0; + int index, sibling; + + for (l = 0; l < height; ++l) { + /* Copy auth octopus */ + for (i = 0, j = 0; i < count; ++i, ++j) { + index = indices[i]; + sibling = index ^ 1; + + /* Check redundancy with sibling */ + if (i + 1 < count && indices[i + 1] == sibling) + ++i; + else + hashcpy (&octopus[len++], &dst[sibling]); + + indices[j] = indices[i] >> 1; + } + /* Update count of non-redundant nodes */ + count = j; + + /* Swap buffers */ + hashswap (src, dst, tmp); + n >>= 1; + + /* Compute all hashes at current layer */ + hash_compress_pairs (dst, src, n); + } + + hashcpy (root, dst); + *octolen = len; +} + +int merkle_compress_octopus (struct hash *nodes, + int height, + const struct hash *octopus, + int octolen, + int *indices, + int count) { + struct hash buf[2]; + int i, j, l; + int len = 0; + int index; + + for (l = 0; l < height; ++l) { + for (i = 0, j = 0; i < count; ++i, ++j) { + /* Select 2 values to merge */ + index = indices[i]; + if (index % 2 == 0) { + hashcpy (&buf[0], &nodes[i]); + + if (i + 1 < count && indices[i + 1] == index + 1) { + ++i; + hashcpy (&buf[1], &nodes[i]); + } else { + if (len == octolen) return GRAVITY_ERR_VERIF; + hashcpy (&buf[1], &octopus[len++]); + } + } else { + if (len == octolen) return GRAVITY_ERR_VERIF; + hashcpy (&buf[0], &octopus[len++]); + hashcpy (&buf[1], &nodes[i]); + } + + /* Hash values */ + hash_2N_to_N (&nodes[j], buf); + + indices[j] = indices[i] >> 1; + } + + /* Update count of non-redundant nodes */ + count = j; + } + + /* Check len */ + if (len != octolen) return GRAVITY_ERR_VERIF; + + return GRAVITY_OK; +} diff --git a/crypto/sphincs/merkle.h b/crypto/sphincs/merkle.h new file mode 100644 index 00000000..e64b0bb2 --- /dev/null +++ b/crypto/sphincs/merkle.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "wots.h" + +struct merkle_pk { + struct hash k; +}; + +struct merkle_sign { + struct wots_sign wots; + struct hash auth[MERKLE_h]; +}; + +int merkle_genpk (const struct hash *key, const struct address *address, struct merkle_pk *pk); + +int merkle_sign (const struct hash *key, + const struct address *address, + struct merkle_sign *sign, + const struct hash *msg, + struct merkle_pk *pk); + +void merkle_extract (struct merkle_pk *pk, + const struct address *address, + const struct merkle_sign *sign, + const struct hash *msg); + + +/* Helper functions */ +/* Alloc/free a buffer large enough to store leaves and compress them */ +struct hash *merkle_alloc_buf (int height); + +void merkle_free_buf (struct hash *buf); + +/* Compress leaves from level height to root */ +void merkle_compress_all (struct hash *buf, int height, struct hash *root); + +/* Compute authentication path */ +void merkle_gen_auth (struct hash *buf, int height, struct hash *auth, int index, struct hash *root); + +/* Compress node with its auth path on height_diff levels */ +int merkle_compress_auth (struct hash *node, int index, const struct hash *auth, int height_diff); + +/* "indices" must be sorted */ +void merkle_gen_octopus (struct hash *buf, + int height, + struct hash *octopus, + int *octolen, + struct hash *root, + int *indices, + int count); + +/* Compress a set of leaves with their auth path */ +/* "indices" must be sorted */ +int merkle_compress_octopus (struct hash *nodes, + int height, + const struct hash *octopus, + int octolen, + int *indices, + int count); diff --git a/crypto/sphincs/pors.c b/crypto/sphincs/pors.c new file mode 100644 index 00000000..557e695b --- /dev/null +++ b/crypto/sphincs/pors.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "pors.h" + +#include "aes.h" +#include "merkle.h" +#include + +/* Naive PORS without Merkle tree */ +void pors_gensk (const struct hash *key, const struct address *address, struct pors_sk *sk) { + uint8_t iv[16]; + + iv[0] = (address->index >> 56) & 0xFF; + iv[1] = (address->index >> 48) & 0xFF; + iv[2] = (address->index >> 40) & 0xFF; + iv[3] = (address->index >> 32) & 0xFF; + iv[4] = (address->index >> 24) & 0xFF; + iv[5] = (address->index >> 16) & 0xFF; + iv[6] = (address->index >> 8) & 0xFF; + iv[7] = address->index & 0xFF; + + iv[8] = (address->layer >> 24) & 0xFF; + iv[9] = (address->layer >> 16) & 0xFF; + iv[10] = (address->layer >> 8) & 0xFF; + iv[11] = address->layer & 0xFF; + + /* Counter */ + iv[12] = 0; + iv[13] = 0; + iv[14] = 0; + iv[15] = 0; + aesctr256 (sk->k->h, key->h, iv, PORS_t * HASH_SIZE); +} + +void pors_sign (const struct pors_sk *sk, struct pors_sign *sign, const struct pors_subset *subset) { + int i; + int index; + + for (i = 0; i < PORS_k; ++i) { + index = subset->s[i]; + hashcpy (&sign->s[i], &sk->k[index]); + } +} + + +/* Naive PORST without merging of authentication paths */ +int porst_genpk (const struct pors_sk *sk, struct porst_pk *pk) { + struct hash *buf = merkle_alloc_buf (PORS_tau); + if (buf == NULL) return GRAVITY_ERR_ALLOC; + + /* Leaves */ + hash_parallel (buf, sk->k, PORS_t); + + /* Merkle tree */ + merkle_compress_all (buf, PORS_tau, &pk->k); + + merkle_free_buf (buf); + return GRAVITY_OK; +} + + +/* PORST with authentication octopus */ +void sort_subset (struct pors_subset *subset) { + /* Selection sort */ + int i, j, k, tmp; + for (i = 0; i + 1 < PORS_k; ++i) { + k = i; + tmp = subset->s[k]; + for (j = i + 1; j < PORS_k; ++j) { + if (subset->s[j] < tmp) { + k = j; + tmp = subset->s[k]; + } + } + + subset->s[k] = subset->s[i]; + subset->s[i] = tmp; + } +} + +int octoporst_sign (const struct pors_sk *sk, + struct octoporst_sign *sign, + struct porst_pk *pk, + struct pors_subset *subset) { + struct hash *buf; + + /* Sort subset */ + sort_subset (subset); + + /* Values */ + pors_sign (sk, &sign->s, subset); + + /* Authentication paths */ + buf = merkle_alloc_buf (PORS_tau); + if (buf == NULL) return GRAVITY_ERR_ALLOC; + + /* Leaves */ + hash_parallel (buf, sk->k, PORS_t); + + /* Merkle tree */ + merkle_gen_octopus (buf, PORS_tau, sign->octopus, &sign->octolen, &pk->k, + subset->s, PORS_k); + + merkle_free_buf (buf); + return GRAVITY_OK; +} + +int octoporst_extract (struct porst_pk *pk, + const struct octoporst_sign *sign, + struct pors_subset *subset) { + struct hash tmp[PORS_k]; + int res; + + /* Sort subset */ + sort_subset (subset); + + /* Compute leaves */ + hash_parallel (tmp, sign->s.s, PORS_k); + + /* Auth octopus */ + res = merkle_compress_octopus (tmp, PORS_tau, sign->octopus, sign->octolen, + subset->s, PORS_k); + if (res != GRAVITY_OK) return res; + + hashcpy (&pk->k, &tmp[0]); + return GRAVITY_OK; +} + + +int octoporst_loadsign (struct octoporst_sign *sign, const uint8_t *_sign, size_t _len) { + if (_len < sizeof (struct pors_sign)) return GRAVITY_ERR_VERIF; + _len -= sizeof (struct pors_sign); + + if (_len % HASH_SIZE != 0) return GRAVITY_ERR_VERIF; + _len /= HASH_SIZE; + + if (_len > PORS_k * PORS_tau) return GRAVITY_ERR_VERIF; + + memcpy (&sign->s, _sign, sizeof (struct pors_sign)); + _sign += sizeof (struct pors_sign); + memcpy (sign->octopus, _sign, _len * HASH_SIZE); + sign->octolen = _len; + + return GRAVITY_OK; +} + +int octoporst_signcmp (const struct octoporst_sign *sign1, + const struct octoporst_sign *sign2) { + if (sign1->octolen != sign2->octolen) return 1; + if (hashcmpN (sign1->s.s, sign2->s.s, PORS_k)) return 1; + if (hashcmpN (sign1->octopus, sign2->octopus, sign1->octolen)) return 1; + + return 0; +} + + +void pors_randsubset (const struct hash *rand, + const struct hash *msg, + uint64_t *address, + struct pors_subset *subset) { +#define BYTES_PER_INDEX 4 +#define STREAMLEN \ + ((8 * (PORS_k)) + HASH_SIZE) /* count twice as many indexes as needed */ + uint8_t randstream[STREAMLEN]; + int index, duplicate, i, count = 0; + size_t offset = 0; + struct hash seed; + struct hash buf[2]; + uint64_t addr; + uint8_t byte; + + hashcpy (&buf[0], rand); + hashcpy (&buf[1], msg); + hash_2N_to_N (&seed, buf); + + aesctr256_zeroiv (randstream, seed.h, STREAMLEN); + + /* compute address */ + addr = 0; + for (i = 0; i < HASH_SIZE; ++i) { + byte = randstream[i]; + addr = (addr << 8) | byte; + addr &= GRAVITY_mask; + } + *address = addr; + + while (count < PORS_k) { + /* ok to take mod since T is a power of 2 */ + index = U8TO32 (randstream + HASH_SIZE + offset) % PORS_t; + offset += BYTES_PER_INDEX; + duplicate = 0; + for (i = 0; i < count; ++i) + if (subset->s[i] == index) duplicate++; + if (!duplicate) { + subset->s[count] = index; + count++; + } + } +} diff --git a/crypto/sphincs/pors.h b/crypto/sphincs/pors.h new file mode 100644 index 00000000..15be8af9 --- /dev/null +++ b/crypto/sphincs/pors.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "hash.h" + +/* PRNG to obtain a random subset */ +struct pors_subset { + int s[PORS_k]; +}; + +void pors_randsubset (const struct hash *rand, + const struct hash *msg, + uint64_t *address, + struct pors_subset *subset); + + +/* Naive PORS without Merkle tree */ +struct pors_sk { + struct hash k[PORS_t]; +}; + +struct pors_pk { + struct hash k[PORS_t]; +}; + +struct pors_keypair { + struct pors_sk sk; + struct pors_pk pk; +}; + +struct pors_sign { + struct hash s[PORS_k]; +}; + +void pors_gensk (const struct hash *key, const struct address *address, struct pors_sk *sk); + +void pors_sign (const struct pors_sk *sk, struct pors_sign *sign, const struct pors_subset *subset); + + +/* Naive PORST without merging of authentication paths */ +struct porst_pk { + struct hash k; +}; + +struct porst_keypair { + struct pors_sk sk; + struct porst_pk pk; +}; + +int porst_genpk (const struct pors_sk *sk, struct porst_pk *pk); + + +/* PORST with authentication octopus */ +struct octoporst_sign { + struct pors_sign s; + struct hash octopus[PORS_k * PORS_tau]; /* Large enough buffer */ + int octolen; /* Number of elements in the octopus */ +}; + +int octoporst_sign (const struct pors_sk *sk, + struct octoporst_sign *sign, + struct porst_pk *pk, + struct pors_subset *subset); + +int octoporst_extract (struct porst_pk *pk, + const struct octoporst_sign *sign, + struct pors_subset *subset); + +/* Serialization */ +int octoporst_loadsign (struct octoporst_sign *sign, const uint8_t *_sign, size_t _len); + +int octoporst_signcmp (const struct octoporst_sign *sign1, const struct octoporst_sign *sign2); diff --git a/crypto/sphincs/primitives/aes.c b/crypto/sphincs/primitives/aes.c new file mode 100644 index 00000000..cbda47f1 --- /dev/null +++ b/crypto/sphincs/primitives/aes.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ + +#include "../aes.h" +#include +#include +#include +#include +#include + + +int aesctr256 (uint8_t *out, const uint8_t *sk, const void *counter, int bytes) { + static const uint8_t buffer[4096] = { 0 }; + EVP_CIPHER_CTX *ctx; + int len = 0; + int ret = 0; + + if (bytes == 0) return 0; + + if (!(ctx = EVP_CIPHER_CTX_new ())) { + ret = -2; + goto label_exit_0; + } + + if (1 != EVP_EncryptInit_ex (ctx, EVP_aes_256_ctr (), NULL, sk, counter)) { + ret = -3; + goto label_exit_1; + } + + while (bytes >= (int)sizeof (buffer)) { + if (1 != EVP_EncryptUpdate (ctx, out, &len, buffer, sizeof (buffer))) { + ret = -4; + goto label_exit_1; + } + out += sizeof (buffer); + bytes -= sizeof (buffer); + } + if (bytes) { + if (1 != EVP_EncryptUpdate (ctx, out, &len, buffer, bytes)) { + ret = -4; + goto label_exit_1; + } + } + + if (1 != EVP_EncryptFinal_ex (ctx, out + len, &len)) { + ret = -5; + goto label_exit_1; + } + + ret = 0; +label_exit_1: + EVP_CIPHER_CTX_free (ctx); +label_exit_0: + return ret; +} + +int aesctr256_zeroiv (uint8_t *out, const uint8_t *sk, int bytes) { + uint8_t counter[16] = {0}; + return aesctr256(out, sk, counter, bytes); +} diff --git a/crypto/sphincs/primitives/haraka.c b/crypto/sphincs/primitives/haraka.c new file mode 100644 index 00000000..08f4b24a --- /dev/null +++ b/crypto/sphincs/primitives/haraka.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ + +#include "haraka.h" +#include + + +void aesenc (uint8_t *s, const uint8_t *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) v[((i / 4) + 4 - (i % 4)) % 4][i % 4] = sbox[s[i]]; + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= u ^ XT (v[i][0] ^ v[i][1]); + v[i][1] ^= u ^ XT (v[i][1] ^ v[i][2]); + v[i][2] ^= u ^ XT (v[i][2] ^ v[i][3]); + v[i][3] ^= u ^ XT (v[i][3] ^ t); + } + for (i = 0; i < 16; ++i) s[i] = v[i / 4][i % 4] ^ rk[i]; +} + + +void haraka256_256 (uint8_t *out, const uint8_t *in) { + + uint8_t s0[16], s1[16]; + uint32_t tmp[4]; + int i; + + memcpy (s0, in, 16); + memcpy (s1, in + 16, 16); + + AES2 (0); + MIX2; + AES2 (4); + MIX2; + AES2 (8); + MIX2; + AES2 (12); + MIX2; + AES2 (16); + MIX2; + AES2 (20); + MIX2; + + for (i = 0; i < 16; ++i) { + out[i] = in[i] ^ s0[i]; + out[i + 16] = in[i + 16] ^ s1[i]; + } +} + +void haraka256_256_chain (uint8_t *out, const uint8_t *in, int chainlen) { + + uint8_t s0[16], s1[16]; + uint8_t t0[16], t1[16]; + uint32_t tmp[4]; + int i, j; + + memcpy (s0, in, 16); + memcpy (s1, in + 16, 16); + memcpy (t0, in, 16); + memcpy (t1, in + 16, 16); + + for (j = 0; j < chainlen; ++j) { + + AES2 (0); + MIX2; + AES2 (4); + MIX2; + AES2 (8); + MIX2; + AES2 (12); + MIX2; + AES2 (16); + MIX2; + AES2 (20); + MIX2; + + for (i = 0; i < 16; ++i) { + s0[i] = t0[i] = t0[i] ^ s0[i]; + s1[i] = t1[i] = t1[i] ^ s1[i]; + } + } + for (i = 0; i < 16; ++i) { + out[i] = s0[i]; + out[i + 16] = s1[i]; + } +} + +void haraka512_256 (uint8_t *out, const uint8_t *in) { + + uint8_t s0[16], s1[16], s2[16], s3[16]; + uint32_t tmp[5]; + int i; + + memcpy (s0, in, 16); + memcpy (s1, in + 16, 16); + memcpy (s2, in + 32, 16); + memcpy (s3, in + 48, 16); + + AES4 (0); + MIX4; + AES4 (8); + MIX4; + AES4 (16); + MIX4; + AES4 (24); + MIX4; + AES4 (32); + MIX4; + AES4 (40); + MIX4; + + for (i = 0; i < 16; ++i) { + s0[i] = in[i] ^ s0[i]; + s1[i] = in[i + 16] ^ s1[i]; + s2[i] = in[i + 32] ^ s2[i]; + s3[i] = in[i + 48] ^ s3[i]; + } + + ((uint64_t *)out)[0] = ((uint64_t *)s0)[1]; + ((uint64_t *)out)[1] = ((uint64_t *)s1)[1]; + ((uint64_t *)out)[2] = ((uint64_t *)s2)[0]; + ((uint64_t *)out)[3] = ((uint64_t *)s3)[0]; +} diff --git a/crypto/sphincs/primitives/haraka.h b/crypto/sphincs/primitives/haraka.h new file mode 100644 index 00000000..cb5c5057 --- /dev/null +++ b/crypto/sphincs/primitives/haraka.h @@ -0,0 +1,160 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Stefan Kölbl +original Haraka implementations + +Copyright (c) 2017 Nagravision S.A. +changes by JP Aumasson, Guillaume Endignoux, 2017: improvements, non-ni versions + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ +#pragma once + + +#include + + +static const uint32_t rc[48 * 4] = { + 0x75817b9d, 0xb2c5fef0, 0xe620c00a, 0x0684704c, 0x2f08f717, 0x640f6ba4, + 0x88f3a06b, 0x8b66b4e1, 0x9f029114, 0xcf029d60, 0x53f28498, 0x3402de2d, + 0xfd5b4f79, 0xbbf3bcaf, 0x2e7b4f08, 0x0ed6eae6, 0xbe397044, 0x79eecd1c, + 0x4872448b, 0xcbcfb0cb, 0x2b8a057b, 0x8d5335ed, 0x6e9032b7, 0x7eeacdee, + 0xda4fef1b, 0xe2412761, 0x5e2e7cd0, 0x67c28f43, 0x1fc70b3b, 0x675ffde2, + 0xafcacc07, 0x2924d9b0, 0xb9d465ee, 0xecdb8fca, 0xe6867fe9, 0xab4d63f1, + 0xad037e33, 0x5b2a404f, 0xd4b7cd64, 0x1c30bf84, 0x8df69800, 0x69028b2e, + 0x941723bf, 0xb2cc0bb9, 0x5c9d2d8a, 0x4aaa9ec8, 0xde6f5572, 0xfa0478a6, + 0x29129fd4, 0x0efa4f2e, 0x6b772a12, 0xdfb49f2b, 0xbb6a12ee, 0x32d611ae, + 0xf449a236, 0x1ea10344, 0x9ca8eca6, 0x5f9600c9, 0x4b050084, 0xaf044988, + 0x27e593ec, 0x78a2c7e3, 0x9d199c4f, 0x21025ed8, 0x82d40173, 0xb9282ecd, + 0xa759c9b7, 0xbf3aaaf8, 0x10307d6b, 0x37f2efd9, 0x6186b017, 0x6260700d, + 0xf6fc9ac6, 0x81c29153, 0x21300443, 0x5aca45c2, 0x36d1943a, 0x2caf92e8, + 0x226b68bb, 0x9223973c, 0xe51071b4, 0x6cbab958, 0x225886eb, 0xd3bf9238, + 0x24e1128d, 0x933dfddd, 0xaef0c677, 0xdb863ce5, 0xcb2212b1, 0x83e48de3, + 0xffeba09c, 0xbb606268, 0xc72bf77d, 0x2db91a4e, 0xe2e4d19c, 0x734bd3dc, + 0x2cb3924e, 0x4b1415c4, 0x61301b43, 0x43bb47c3, 0x16eb6899, 0x03b231dd, + 0xe707eff6, 0xdba775a8, 0x7eca472c, 0x8e5e2302, 0x3c755977, 0x6df3614b, + 0xb88617f9, 0x6d1be5b9, 0xd6de7d77, 0xcda75a17, 0xa946ee5d, 0x9d6c069d, + 0x6ba8e9aa, 0xec6b43f0, 0x3bf327c1, 0xa2531159, 0xf957332b, 0xcb1e6950, + 0x600ed0d9, 0xe4ed0353, 0x00da619c, 0x2cee0c75, 0x63a4a350, 0x80bbbabc, + 0x96e90cab, 0xf0b1a5a1, 0x938dca39, 0xab0dde30, 0x5e962988, 0xae3db102, + 0x2e75b442, 0x8814f3a8, 0xd554a40b, 0x17bb8f38, 0x360a16f6, 0xaeb6b779, + 0x5f427fd7, 0x34bb8a5b, 0xffbaafde, 0x43ce5918, 0xcbe55438, 0x26f65241, + 0x839ec978, 0xa2ca9cf7, 0xb9f3026a, 0x4ce99a54, 0x22901235, 0x40c06e28, + 0x1bdff7be, 0xae51a51a, 0x48a659cf, 0xc173bc0f, 0xba7ed22b, 0xa0c1613c, + 0xe9c59da1, 0x4ad6bdfd, 0x02288288, 0x756acc03, 0x848f2ad2, 0x367e4778, + 0x0de7d31e, 0x2ff37238, 0xb73bd58f, 0xee36b135, 0xcf74be8b, 0x08d95c6a, + 0xa3743e4a, 0x66ae1838, 0xc9d6ee98, 0x5880f434, 0x9a9369bd, 0xd0fdf4c7, + 0xaefabd99, 0x593023f0, 0x6f1ecb2a, 0xa5cc637b, 0xeb606e6f, 0x329ae3d1, + 0xcb7594ab, 0xa4dc93d6, 0x49e01594, 0xe00207eb, 0x65208ef8, 0x942366a6, + 0xf751c880, 0x1caa0c4f, 0xe3e67e4a, 0xbd03239f, 0xdb2dc1dd, 0x02f7f57f, +}; + +static const uint8_t sbox[256] = +{ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +#define AES2(rci) \ + aesenc (s0, ((uint8_t *)rc) + 16 * (rci + 0)); \ + aesenc (s1, ((uint8_t *)rc) + 16 * (rci + 1)); \ + aesenc (s0, ((uint8_t *)rc) + 16 * (rci + 2)); \ + aesenc (s1, ((uint8_t *)rc) + 16 * (rci + 3)); + +#define MIX2 \ + tmp[0] = ((uint32_t *)s0)[0]; \ + tmp[1] = ((uint32_t *)s1)[0]; \ + tmp[2] = ((uint32_t *)s0)[1]; \ + tmp[3] = ((uint32_t *)s1)[1]; \ + ((uint32_t *)s1)[0] = ((uint32_t *)s0)[2]; \ + ((uint32_t *)s1)[1] = ((uint32_t *)s1)[2]; \ + ((uint32_t *)s1)[2] = ((uint32_t *)s0)[3]; \ + ((uint32_t *)s0)[0] = tmp[0]; \ + ((uint32_t *)s0)[1] = tmp[1]; \ + ((uint32_t *)s0)[2] = tmp[2]; \ + ((uint32_t *)s0)[3] = tmp[3]; + +#define AES4(rci) \ + aesenc (s0, ((uint8_t *)rc) + 16 * (rci + 0)); \ + aesenc (s1, ((uint8_t *)rc) + 16 * (rci + 1)); \ + aesenc (s2, ((uint8_t *)rc) + 16 * (rci + 2)); \ + aesenc (s3, ((uint8_t *)rc) + 16 * (rci + 3)); \ + aesenc (s0, ((uint8_t *)rc) + 16 * (rci + 4)); \ + aesenc (s1, ((uint8_t *)rc) + 16 * (rci + 5)); \ + aesenc (s2, ((uint8_t *)rc) + 16 * (rci + 6)); \ + aesenc (s3, ((uint8_t *)rc) + 16 * (rci + 7)); + +#define MIX4 \ + tmp[0] = ((uint32_t *)s0)[0]; \ + tmp[1] = ((uint32_t *)s1)[0]; \ + tmp[2] = ((uint32_t *)s0)[1]; \ + tmp[3] = ((uint32_t *)s1)[1]; \ + tmp[4] = ((uint32_t *)s3)[0]; \ + ((uint32_t *)s0)[0] = ((uint32_t *)s0)[2]; \ + ((uint32_t *)s0)[1] = ((uint32_t *)s1)[2]; \ + ((uint32_t *)s0)[2] = ((uint32_t *)s0)[3]; \ + ((uint32_t *)s0)[3] = ((uint32_t *)s1)[3]; \ + ((uint32_t *)s1)[0] = ((uint32_t *)s2)[0]; \ + ((uint32_t *)s1)[1] = ((uint32_t *)s3)[0]; \ + ((uint32_t *)s1)[2] = ((uint32_t *)s2)[1]; \ + ((uint32_t *)s1)[3] = ((uint32_t *)s3)[1]; \ + ((uint32_t *)s2)[0] = ((uint32_t *)s2)[2]; \ + ((uint32_t *)s2)[1] = ((uint32_t *)s3)[2]; \ + ((uint32_t *)s2)[2] = ((uint32_t *)s2)[3]; \ + ((uint32_t *)s2)[3] = ((uint32_t *)s3)[3]; \ + ((uint32_t *)s3)[0] = ((uint32_t *)s0)[0]; \ + ((uint32_t *)s3)[1] = ((uint32_t *)s2)[0]; \ + ((uint32_t *)s3)[2] = ((uint32_t *)s0)[1]; \ + ((uint32_t *)s3)[3] = ((uint32_t *)s2)[1]; \ + ((uint32_t *)s0)[0] = ((uint32_t *)s0)[2]; \ + ((uint32_t *)s0)[1] = ((uint32_t *)s2)[2]; \ + ((uint32_t *)s0)[2] = ((uint32_t *)s0)[3]; \ + ((uint32_t *)s0)[3] = ((uint32_t *)s2)[3]; \ + ((uint32_t *)s2)[0] = ((uint32_t *)s1)[2]; \ + ((uint32_t *)s2)[1] = tmp[2]; \ + ((uint32_t *)s2)[2] = ((uint32_t *)s1)[3]; \ + ((uint32_t *)s2)[3] = tmp[3]; \ + ((uint32_t *)s1)[0] = ((uint32_t *)s1)[0]; \ + ((uint32_t *)s1)[1] = tmp[0]; \ + ((uint32_t *)s1)[2] = tmp[4]; \ + ((uint32_t *)s1)[3] = tmp[1]; + + +void haraka256_256(unsigned char *out, const unsigned char *in); +void haraka256_256_chain(unsigned char *out, const unsigned char *in, int chainlen); +void haraka512_256(unsigned char *out, const unsigned char *in); diff --git a/crypto/sphincs/primitives/hash.c b/crypto/sphincs/primitives/hash.c new file mode 100644 index 00000000..7b990083 --- /dev/null +++ b/crypto/sphincs/primitives/hash.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "../hash.h" + +#include "haraka.h" +#include + +void hash_N_to_N(struct hash *dst, const struct hash *src) +{ + haraka256_256(dst->h, src->h); +} + +void hash_N_to_N_chain(struct hash *dst, const struct hash *src, int chainlen) +{ + haraka256_256_chain(dst->h, src->h, chainlen); +} + +void hash_2N_to_N(struct hash *dst, const struct hash *src) +{ + haraka512_256(dst->h, src->h); +} + +void hash_to_N(struct hash *dst, const uint8_t *src, uint64_t srclen) +{ + SHA256(src, srclen, dst->h); +} + +void hash_compress_pairs(struct hash *dst, const struct hash *src, int count) +{ + int i = 0; + for (; i < count; ++i) + hash_2N_to_N(&dst[i], &src[2*i]); +} + +void hash_compress_all(struct hash *dst, const struct hash *src, int count) +{ + /* Fast implementation with a single call to a large input hash function */ + hash_to_N(dst, src->h, count * HASH_SIZE); + /* TODO: implement a real L-tree with 2N->N compression function */ +} + +void hash_parallel(struct hash *dst, const struct hash *src, int count) +{ + int i = 0; + for (; i < count; ++i) + hash_N_to_N(&dst[i], &src[i]); +} + +void hash_parallel_chains(struct hash *dst, const struct hash *src, int count, int chainlen) +{ + int i = 0; + for (; i < count; ++i) + hash_N_to_N_chain(&dst[i], &src[i], chainlen); +} + diff --git a/crypto/sphincs/randombytes.c b/crypto/sphincs/randombytes.c new file mode 100644 index 00000000..904daf41 --- /dev/null +++ b/crypto/sphincs/randombytes.c @@ -0,0 +1,13 @@ +#include "randombytes.h" + +#include +#include +#include +#include + +/* lazy unsafe emulation of SUPERCOP's randombytes() */ +void randombytes (unsigned char *x, unsigned long long xlen) { + static int fd = -1; + if (fd == -1) fd = open ("/dev/urandom", O_RDONLY); + read (fd, x, xlen); +} diff --git a/crypto/sphincs/randombytes.h b/crypto/sphincs/randombytes.h new file mode 100644 index 00000000..77d6f648 --- /dev/null +++ b/crypto/sphincs/randombytes.h @@ -0,0 +1,4 @@ +#pragma once + +void randombytes (unsigned char *x, unsigned long long xlen); + diff --git a/crypto/sphincs/sign.c b/crypto/sphincs/sign.c new file mode 100644 index 00000000..c656fea7 --- /dev/null +++ b/crypto/sphincs/sign.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "gravity.h" +#include "randombytes.h" + + +int crypto_sign_keypair (unsigned char *pk, unsigned char *sk) { + + struct gravity_sk sk_str; + struct gravity_pk pk_str; + + if (!pk || !sk) return -1; + + randombytes (sk_str.seed.h, HASH_SIZE); + randombytes (sk_str.salt.h, HASH_SIZE); + + gravity_gensk (&sk_str); + + memcpy (sk, (void *)&sk_str, sizeof (struct gravity_sk)); + + gravity_genpk (&sk_str, &pk_str); + + memcpy (pk, pk_str.k.h, HASH_SIZE); + + return 0; +} + +int crypto_sign (unsigned char *sm, + unsigned long long *smlen, + const unsigned char *m, + unsigned long long mlen, + const unsigned char *sk) { + + struct gravity_sk sk_str; + struct hash msg; + struct gravity_sign sig; + int ret; + + if (!sm || !smlen || !m || !sk) return -1; + + hash_to_N (&msg, m, mlen); + + memcpy ((void *)&sk_str, sk, sizeof (struct gravity_sk)); + + memset ((uint8_t *)(&sig), 0, sizeof (struct gravity_sign)); + + ret = gravity_sign (&sk_str, &sig, &msg); + + if (ret != GRAVITY_OK) return ret; + + memcpy (sm + mlen, (uint8_t *)(&sig), sizeof (struct gravity_sign)); + + memmove (sm, m, mlen); + *smlen = mlen + sizeof (struct gravity_sign); + + return 0; +} + +int crypto_sign_open (unsigned char *m, + unsigned long long *mlen, + const unsigned char *sm, + unsigned long long smlen, + const unsigned char *pk) { + + struct gravity_pk pk_str; + struct hash msg; + struct gravity_sign sig; + + if (!m || !mlen || !sm || !pk) return -1; + + if (smlen < sizeof (struct gravity_sign)) return -2; + + *mlen = smlen - sizeof (struct gravity_sign); + + memcpy ((void *)(&pk_str), pk, HASH_SIZE); + + memcpy ((void *)&sig, sm + *mlen, sizeof (struct gravity_sign)); + + memcpy (m, sm, *mlen); + + hash_to_N (&msg, m, *mlen); + + return gravity_verify (&pk_str, &sig, &msg); +} diff --git a/crypto/sphincs/sign.h b/crypto/sphincs/sign.h new file mode 100644 index 00000000..111427b5 --- /dev/null +++ b/crypto/sphincs/sign.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +int crypto_sign_keypair (unsigned char *pk, unsigned char *sk); + +int crypto_sign (unsigned char *sm, + unsigned long long *smlen, + const unsigned char *m, + unsigned long long mlen, + const unsigned char *sk); + +int crypto_sign_open (unsigned char *m, + unsigned long long *mlen, + const unsigned char *sm, + unsigned long long smlen, + const unsigned char *pk); diff --git a/crypto/sphincs/wots.c b/crypto/sphincs/wots.c new file mode 100644 index 00000000..5aa014fd --- /dev/null +++ b/crypto/sphincs/wots.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#include "wots.h" +#include "aes.h" + +#include "ltree.h" + +void wots_chain (const struct hash *src, struct hash *dst, int count) { + hash_N_to_N_chain (dst, src, count); +} + +/* Naive WOTS without L-tree nor masks */ +void wots_gensk (const struct hash *key, const struct address *address, struct wots_sk *sk) { + uint8_t iv[16]; + + iv[0] = (address->index >> 56) & 0xFF; + iv[1] = (address->index >> 48) & 0xFF; + iv[2] = (address->index >> 40) & 0xFF; + iv[3] = (address->index >> 32) & 0xFF; + iv[4] = (address->index >> 24) & 0xFF; + iv[5] = (address->index >> 16) & 0xFF; + iv[6] = (address->index >> 8) & 0xFF; + iv[7] = address->index & 0xFF; + + iv[8] = (address->layer >> 24) & 0xFF; + iv[9] = (address->layer >> 16) & 0xFF; + iv[10] = (address->layer >> 8) & 0xFF; + iv[11] = address->layer & 0xFF; + + /* Counter */ + iv[12] = 0; + iv[13] = 0; + iv[14] = 0; + iv[15] = 0; + aesctr256 (sk->k->h, key->h, iv, WOTS_ell * HASH_SIZE); +} + +void wots_sign (const struct wots_sk *sk, struct wots_sign *sign, const struct hash *msg) { + int checksum = 0; + int i; + + for (i = 0; i < WOTS_ell1; ++i) { + uint8_t v = msg->h[i / 2]; + int a = (v >> 4) & 15; + int b = v & 15; + checksum += (WOTS_w - 1 - a) + (WOTS_w - 1 - b); + + wots_chain (&sk->k[i], &sign->s[i], a); + ++i; + wots_chain (&sk->k[i], &sign->s[i], b); + } + + /* Checksum values */ + for (i = WOTS_ell1; i < WOTS_ell; ++i) { + wots_chain (&sk->k[i], &sign->s[i], checksum & 15); + checksum >>= 4; + } +} + + +/* WOTS with L-tree and without masks */ +void lwots_ltree (const struct wots_pk *pk, struct lwots_pk *root) { + struct hash buf[2 * WOTS_ell]; + + hashcpyN (buf, pk->k, WOTS_ell); + ltree (buf, WOTS_ell, &root->k); +} + +void lwots_genpk (const struct wots_sk *sk, struct lwots_pk *pk) { + struct wots_pk tmp; + + hash_parallel_chains (tmp.k, sk->k, WOTS_ell, WOTS_w - 1); + lwots_ltree (&tmp, pk); +} + +void lwots_extract (struct lwots_pk *pk, const struct wots_sign *sign, const struct hash *msg) { + struct wots_pk tmp; + int i; + int checksum = 0; + + for (i = 0; i < WOTS_ell1; ++i) { + uint8_t v = msg->h[i / 2]; + int a = (v >> 4) & 15; + int b = v & 15; + checksum += (WOTS_w - 1 - a) + (WOTS_w - 1 - b); + + wots_chain (&sign->s[i], &tmp.k[i], WOTS_w - 1 - a); + ++i; + wots_chain (&sign->s[i], &tmp.k[i], WOTS_w - 1 - b); + } + + /* Checksum values */ + for (i = WOTS_ell1; i < WOTS_ell; ++i) { + wots_chain (&sign->s[i], &tmp.k[i], WOTS_w - 1 - (checksum & 15)); + checksum >>= 4; + } + + /* L-tree */ + lwots_ltree (&tmp, pk); +} diff --git a/crypto/sphincs/wots.h b/crypto/sphincs/wots.h new file mode 100644 index 00000000..dd4a7fbd --- /dev/null +++ b/crypto/sphincs/wots.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Nagravision S.A. + */ +#pragma once + +#include "common.h" +#include "hash.h" + +/* Naive WOTS without L-tree nor masks */ +struct wots_sk { + struct hash k[WOTS_ell]; +}; + +struct wots_pk { + struct hash k[WOTS_ell]; +}; + +struct wots_sign { + struct hash s[WOTS_ell]; +}; + +void wots_gensk (const struct hash *key, const struct address *address, struct wots_sk *sk); + +void wots_sign (const struct wots_sk *sk, struct wots_sign *sign, const struct hash *msg); + + +/* WOTS with L-tree and without masks */ +struct lwots_pk { + struct hash k; +}; + +void lwots_genpk (const struct wots_sk *sk, struct lwots_pk *pk); + +void lwots_extract (struct lwots_pk *pk, const struct wots_sign *sign, const struct hash *msg);