From e0b5338539a62bef75ce79ae6ed4017ed9d8b616 Mon Sep 17 00:00:00 2001 From: Blake Hartin Date: Sun, 19 Nov 2023 00:49:58 +0000 Subject: [PATCH 1/3] Hybrid ed25519 Dilithium --- CMakeLists.txt | 15 +- common/compat.h | 57 ++ common/fips202.c | 928 ++++++++++++++++++++++++++++ common/fips202.h | 166 +++++ dilithium2/LICENSE | 5 + dilithium2/Makefile | 19 + dilithium2/Makefile.Microsoft_nmake | 23 + dilithium2/api.h | 53 ++ dilithium2/ntt.c | 98 +++ dilithium2/ntt.h | 10 + dilithium2/packing.c | 260 ++++++++ dilithium2/packing.h | 31 + dilithium2/params.h | 41 ++ dilithium2/poly.c | 848 +++++++++++++++++++++++++ dilithium2/poly.h | 52 ++ dilithium2/polyvec.c | 414 +++++++++++++ dilithium2/polyvec.h | 62 ++ dilithium2/reduce.c | 69 +++ dilithium2/reduce.h | 17 + dilithium2/rounding.c | 98 +++ dilithium2/rounding.h | 14 + dilithium2/sign.c | 365 +++++++++++ dilithium2/sign.h | 27 + dilithium2/symmetric-shake.c | 26 + dilithium2/symmetric.h | 33 + hybrid-dilithium/hybrid.c | 378 +++++++++++ hybrid-dilithium/hybrid.h | 37 ++ sphincs/LICENSE | 116 ++++ sphincs/Makefile | 21 + sphincs/Makefile.Microsoft_nmake | 21 + sphincs/address.c | 91 +++ sphincs/address.h | 52 ++ sphincs/api.h | 97 +++ sphincs/context.h | 21 + sphincs/context_shake.c | 12 + sphincs/fors.c | 156 +++++ sphincs/fors.h | 32 + sphincs/hash.h | 26 + sphincs/hash_shake.c | 82 +++ sphincs/merkle.c | 59 ++ sphincs/merkle.h | 21 + sphincs/nistapi.h | 87 +++ sphincs/params.h | 56 ++ sphincs/shake_offsets.h | 21 + sphincs/sign.c | 294 +++++++++ sphincs/thash.h | 13 + sphincs/thash_shake_simple.c | 24 + sphincs/utils.c | 148 +++++ sphincs/utils.h | 55 ++ sphincs/utilsx1.c | 100 +++ sphincs/utilsx1.h | 27 + sphincs/wots.c | 108 ++++ sphincs/wots.h | 25 + sphincs/wotsx1.c | 76 +++ sphincs/wotsx1.h | 39 ++ tests/test_hybrid.c | 642 ++++++++++++++++++- 56 files changed, 6640 insertions(+), 28 deletions(-) create mode 100644 common/compat.h create mode 100644 common/fips202.c create mode 100644 common/fips202.h create mode 100644 dilithium2/LICENSE create mode 100644 dilithium2/Makefile create mode 100644 dilithium2/Makefile.Microsoft_nmake create mode 100644 dilithium2/api.h create mode 100644 dilithium2/ntt.c create mode 100644 dilithium2/ntt.h create mode 100644 dilithium2/packing.c create mode 100644 dilithium2/packing.h create mode 100644 dilithium2/params.h create mode 100644 dilithium2/poly.c create mode 100644 dilithium2/poly.h create mode 100644 dilithium2/polyvec.c create mode 100644 dilithium2/polyvec.h create mode 100644 dilithium2/reduce.c create mode 100644 dilithium2/reduce.h create mode 100644 dilithium2/rounding.c create mode 100644 dilithium2/rounding.h create mode 100644 dilithium2/sign.c create mode 100644 dilithium2/sign.h create mode 100644 dilithium2/symmetric-shake.c create mode 100644 dilithium2/symmetric.h create mode 100644 hybrid-dilithium/hybrid.c create mode 100644 hybrid-dilithium/hybrid.h create mode 100644 sphincs/LICENSE create mode 100644 sphincs/Makefile create mode 100644 sphincs/Makefile.Microsoft_nmake create mode 100644 sphincs/address.c create mode 100644 sphincs/address.h create mode 100644 sphincs/api.h create mode 100644 sphincs/context.h create mode 100644 sphincs/context_shake.c create mode 100644 sphincs/fors.c create mode 100644 sphincs/fors.h create mode 100644 sphincs/hash.h create mode 100644 sphincs/hash_shake.c create mode 100644 sphincs/merkle.c create mode 100644 sphincs/merkle.h create mode 100644 sphincs/nistapi.h create mode 100644 sphincs/params.h create mode 100644 sphincs/shake_offsets.h create mode 100644 sphincs/sign.c create mode 100644 sphincs/thash.h create mode 100644 sphincs/thash_shake_simple.c create mode 100644 sphincs/utils.c create mode 100644 sphincs/utils.h create mode 100644 sphincs/utilsx1.c create mode 100644 sphincs/utilsx1.h create mode 100644 sphincs/wots.c create mode 100644 sphincs/wots.h create mode 100644 sphincs/wotsx1.c create mode 100644 sphincs/wotsx1.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e623fd3..0509d09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,12 +20,10 @@ if(POLICY CMP0066) endif() if(POLICY CMP0067) cmake_policy(SET CMP0067 NEW) -endif() - - +endif() + project(hybridpqc C ASM) - option(OQS_DIST_BUILD "Build distributable library with optimized code for several CPU microarchitectures. Enables run-time CPU feature detection." ON) option(OQS_BUILD_ONLY_LIB "Build only hybridpqc and do not expose build targets for tests, documentation, and pretty-printing available." OFF) set(OQS_MINIMAL_BUILD "" CACHE STRING "Only build specifically listed algorithms.") @@ -107,7 +105,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInf else() set(OQS_DEBUG_BUILD OFF) endif() - + option(OQS_SPEED_USE_ARM_PMU "Use ARM Performance Monitor Unit during benchmarking" OFF) set(CPACK_GENERATOR "DEB") set(CPACK_PACKAGE_VENDOR "www.dogeprotocol.org") @@ -132,9 +130,10 @@ endif() include(GNUInstallDirs) include(CMakePackageConfigHelpers) -add_library(hybridpqc SHARED falcon512/codec.c falcon512/common.c falcon512/fft.c falcon512/fpr.c falcon512/keygen.c falcon512/nist.c falcon512/rng.c falcon512/shake.c falcon512/sign.c falcon512/vrfy.c random/randombytes.c "tweetnacl/tweetnacl.c" "hybrid/hybrid.h" "hybrid/hybrid.c") -add_executable(hybridpqctest falcon512/codec.c falcon512/common.c falcon512/fft.c falcon512/fpr.c falcon512/keygen.c falcon512/nist.c falcon512/rng.c falcon512/shake.c falcon512/sign.c falcon512/vrfy.c random/randombytes.c "tweetnacl/tweetnacl.c" "hybrid/hybrid.h" "hybrid/hybrid.c" "tests/test_hybrid.c") - +set(SOURCE_FILES falcon512/codec.c falcon512/common.c falcon512/fft.c falcon512/fpr.c falcon512/keygen.c falcon512/nist.c falcon512/rng.c falcon512/shake.c falcon512/sign.c falcon512/vrfy.c dilithium2/ntt.c dilithium2/ntt.h dilithium2/packing.c dilithium2/packing.h dilithium2/params.h dilithium2/poly.c dilithium2/poly.h dilithium2/polyvec.c dilithium2/polyvec.h dilithium2/reduce.c dilithium2/reduce.h dilithium2/rounding.c dilithium2/rounding.h dilithium2/sign.c dilithium2/sign.h dilithium2/symmetric.h dilithium2/symmetric-shake.c sphincs/address.c sphincs/context_shake.c sphincs/fors.c sphincs/hash_shake.c sphincs/merkle.c sphincs/sign.c sphincs/thash_shake_simple.c sphincs/utils.c sphincs/utilsx1.c sphincs/wots.c sphincs/wotsx1.c random/randombytes.c common/fips202.c "tweetnacl/tweetnacl.c" "hybrid/hybrid.h" "hybrid/hybrid.c" "hybrid-dilithium/hybrid.h" "hybrid-dilithium/hybrid.c") +add_library(hybridpqc SHARED ${SOURCE_FILES}) +add_executable(hybridpqctest ${SOURCE_FILES} "tests/test_hybrid.c") + set_target_properties(hybridpqc PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" diff --git a/common/compat.h b/common/compat.h new file mode 100644 index 0000000..cbef7cc --- /dev/null +++ b/common/compat.h @@ -0,0 +1,57 @@ +#ifndef PQCLEAN_COMMON_COMPAT_H +#define PQCLEAN_COMMON_COMPAT_H + +/* This file serves to solve compatibility issues between different + * versions of compilers. + * + * This file is allowed to use #ifdefs and toggle things by compiler versions. + */ + +// From https://github.com/gcc-mirror/gcc/blob/af73a8b2027d9ab64944d7dbbb48e207d7790ce6/gcc/config/i386/avxintrin.h#L62-L71 +/* Unaligned versions of the vector types */ +#define UNALIGNED_VECTOR_POLYFILL_GCC \ + typedef float __m256_u __attribute__ ((__vector_size__ (32), __may_alias__, __aligned__ (1))); \ + typedef double __m256d_u __attribute__ ((__vector_size__ (32), __may_alias__, __aligned__ (1))); \ + typedef long long __m256i_u __attribute__ ((__vector_size__ (32), __may_alias__, __aligned__ (1))); + +#if defined(__GNUC__) && !defined(__clang__) +#include + +# if !__GNUC_PREREQ(7, 1) // at least GCC 7.1 +/* Versions of the GCC pre-7.1 don't have __m256*_u types */ +UNALIGNED_VECTOR_POLYFILL_GCC +# endif // __GNUC_PREREQ(7,1) + +#elif defined(__GNUC__) && defined(__clang__) + +# if __clang__major__ < 9 +/* Versions of Clang pre-9.0 don't have __m256*_u types */ +UNALIGNED_VECTOR_POLYFILL_GCC +# endif + +#elif defined(_MSC_VER) +// MSVC simply doesn't have these types +#define __m256_u __m256 +#define __m256d_u __m256d +#define __m256i_u __m256i + +#else +#error UNSUPPORTED COMPILER!?!? +#endif // compiler selector + +/************************ + * Portable VLA support * + ************************/ + +/* To support MSVC use alloca() instead of VLAs. */ +#ifdef _MSC_VER +/* MSVC defines _alloca in malloc.h */ +# include +/* Note: _malloca(), which is recommended over deprecated _alloca, + requires that you call _freea(). So we stick with _alloca */ +# define PQCLEAN_VLA(__t,__x,__s) __t *__x = (__t*)_alloca((__s)*sizeof(__t)) +#else +# define PQCLEAN_VLA(__t,__x,__s) __t __x[__s] +#endif + +#endif // PQCLEAN_COMMON_COMPAT_H diff --git a/common/fips202.c b/common/fips202.c new file mode 100644 index 0000000..a18d1db --- /dev/null +++ b/common/fips202.c @@ -0,0 +1,928 @@ +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include +#include +#include +#include + +#include "fips202.h" + +#define NROUNDS 24 +#define ROL(a, offset) (((a) << (offset)) ^ ((a) >> (64 - (offset)))) + +/************************************************* + * Name: load64 + * + * Description: Load 8 bytes into uint64_t in little-endian order + * + * Arguments: - const uint8_t *x: pointer to input byte array + * + * Returns the loaded 64-bit unsigned integer + **************************************************/ +static uint64_t load64(const uint8_t *x) { + uint64_t r = 0; + for (size_t i = 0; i < 8; ++i) { + r |= (uint64_t)x[i] << 8 * i; + } + + return r; +} + +/************************************************* + * Name: store64 + * + * Description: Store a 64-bit integer to a byte array in little-endian order + * + * Arguments: - uint8_t *x: pointer to the output byte array + * - uint64_t u: input 64-bit unsigned integer + **************************************************/ +static void store64(uint8_t *x, uint64_t u) { + for (size_t i = 0; i < 8; ++i) { + x[i] = (uint8_t) (u >> 8 * i); + } +} + +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808aULL, 0x8000000080008000ULL, + 0x000000000000808bULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008aULL, 0x0000000000000088ULL, + 0x0000000080008009ULL, 0x000000008000000aULL, + 0x000000008000808bULL, 0x800000000000008bULL, + 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, + 0x000000000000800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, + 0x0000000080000001ULL, 0x8000000080008008ULL +}; + +/************************************************* + * Name: KeccakF1600_StatePermute + * + * Description: The Keccak F1600 Permutation + * + * Arguments: - uint64_t *state: pointer to input/output Keccak state + **************************************************/ +static void KeccakF1600_StatePermute(uint64_t *state) { + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + // copyFromState(A, state) + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < NROUNDS; round += 2) { + // prepareTheta + BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase; + BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + // thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^ ((~BCe) & BCi); + Eba ^= KeccakF_RoundConstants[round]; + Ebe = BCe ^ ((~BCi) & BCo); + Ebi = BCi ^ ((~BCo) & BCu); + Ebo = BCo ^ ((~BCu) & BCa); + Ebu = BCu ^ ((~BCa) & BCe); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^ ((~BCe) & BCi); + Ege = BCe ^ ((~BCi) & BCo); + Egi = BCi ^ ((~BCo) & BCu); + Ego = BCo ^ ((~BCu) & BCa); + Egu = BCu ^ ((~BCa) & BCe); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^ ((~BCe) & BCi); + Eke = BCe ^ ((~BCi) & BCo); + Eki = BCi ^ ((~BCo) & BCu); + Eko = BCo ^ ((~BCu) & BCa); + Eku = BCu ^ ((~BCa) & BCe); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^ ((~BCe) & BCi); + Eme = BCe ^ ((~BCi) & BCo); + Emi = BCi ^ ((~BCo) & BCu); + Emo = BCo ^ ((~BCu) & BCa); + Emu = BCu ^ ((~BCa) & BCe); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^ ((~BCe) & BCi); + Ese = BCe ^ ((~BCi) & BCo); + Esi = BCi ^ ((~BCo) & BCu); + Eso = BCo ^ ((~BCu) & BCa); + Esu = BCu ^ ((~BCa) & BCe); + + // prepareTheta + BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + // thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu ^ ROL(BCe, 1); + De = BCa ^ ROL(BCi, 1); + Di = BCe ^ ROL(BCo, 1); + Do = BCi ^ ROL(BCu, 1); + Du = BCo ^ ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^ ((~BCe) & BCi); + Aba ^= KeccakF_RoundConstants[round + 1]; + Abe = BCe ^ ((~BCi) & BCo); + Abi = BCi ^ ((~BCo) & BCu); + Abo = BCo ^ ((~BCu) & BCa); + Abu = BCu ^ ((~BCa) & BCe); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^ ((~BCe) & BCi); + Age = BCe ^ ((~BCi) & BCo); + Agi = BCi ^ ((~BCo) & BCu); + Ago = BCo ^ ((~BCu) & BCa); + Agu = BCu ^ ((~BCa) & BCe); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^ ((~BCe) & BCi); + Ake = BCe ^ ((~BCi) & BCo); + Aki = BCi ^ ((~BCo) & BCu); + Ako = BCo ^ ((~BCu) & BCa); + Aku = BCu ^ ((~BCa) & BCe); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^ ((~BCe) & BCi); + Ame = BCe ^ ((~BCi) & BCo); + Ami = BCi ^ ((~BCo) & BCu); + Amo = BCo ^ ((~BCu) & BCa); + Amu = BCu ^ ((~BCa) & BCe); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^ ((~BCe) & BCi); + Ase = BCe ^ ((~BCi) & BCo); + Asi = BCi ^ ((~BCo) & BCu); + Aso = BCo ^ ((~BCu) & BCa); + Asu = BCu ^ ((~BCa) & BCe); + } + + // copyToState(state, A) + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +/************************************************* + * Name: keccak_absorb + * + * Description: Absorb step of Keccak; + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *m: pointer to input to be absorbed into s + * - size_t mlen: length of input in bytes + * - uint8_t p: domain-separation byte for different + * Keccak-derived functions + **************************************************/ +static void keccak_absorb(uint64_t *s, uint32_t r, const uint8_t *m, + size_t mlen, uint8_t p) { + size_t i; + uint8_t t[200]; + + /* Zero state */ + for (i = 0; i < 25; ++i) { + s[i] = 0; + } + + while (mlen >= r) { + for (i = 0; i < r / 8; ++i) { + s[i] ^= load64(m + 8 * i); + } + + KeccakF1600_StatePermute(s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r / 8; ++i) { + s[i] ^= load64(t + 8 * i); + } +} + +/************************************************* + * Name: keccak_squeezeblocks + * + * Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. + * Modifies the state. Can be called multiple times to keep + * squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *h: pointer to output blocks + * - size_t nblocks: number of blocks to be + * squeezed (written to h) + * - uint64_t *s: pointer to input/output Keccak state + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_squeezeblocks(uint8_t *h, size_t nblocks, + uint64_t *s, uint32_t r) { + while (nblocks > 0) { + KeccakF1600_StatePermute(s); + for (size_t i = 0; i < (r >> 3); i++) { + store64(h + 8 * i, s[i]); + } + h += r; + nblocks--; + } +} + +/************************************************* + * Name: keccak_inc_init + * + * Description: Initializes the incremental Keccak state to zero. + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + **************************************************/ +static void keccak_inc_init(uint64_t *s_inc) { + size_t i; + + for (i = 0; i < 25; ++i) { + s_inc[i] = 0; + } + s_inc[25] = 0; +} + +/************************************************* + * Name: keccak_inc_absorb + * + * Description: Incremental keccak absorb + * Preceded by keccak_inc_init, succeeded by keccak_inc_finalize + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - const uint8_t *m: pointer to input to be absorbed into s + * - size_t mlen: length of input in bytes + **************************************************/ +static void keccak_inc_absorb(uint64_t *s_inc, uint32_t r, const uint8_t *m, + size_t mlen) { + size_t i; + + /* Recall that s_inc[25] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[25] >= r) { + for (i = 0; i < r - (uint32_t)s_inc[25]; i++) { + /* Take the i'th byte from message + xor with the s_inc[25] + i'th byte of the state; little-endian */ + s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07)); + } + mlen -= (size_t)(r - s_inc[25]); + m += r - s_inc[25]; + s_inc[25] = 0; + + KeccakF1600_StatePermute(s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[(s_inc[25] + i) >> 3] ^= (uint64_t)m[i] << (8 * ((s_inc[25] + i) & 0x07)); + } + s_inc[25] += mlen; +} + +/************************************************* + * Name: keccak_inc_finalize + * + * Description: Finalizes Keccak absorb phase, prepares for squeezing + * + * Arguments: - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + * - uint8_t p: domain-separation byte for different + * Keccak-derived functions + **************************************************/ +static void keccak_inc_finalize(uint64_t *s_inc, uint32_t r, uint8_t p) { + /* After keccak_inc_absorb, we are guaranteed that s_inc[25] < r, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[25] >> 3] ^= (uint64_t)p << (8 * (s_inc[25] & 0x07)); + s_inc[(r - 1) >> 3] ^= (uint64_t)128 << (8 * ((r - 1) & 0x07)); + s_inc[25] = 0; +} + +/************************************************* + * Name: keccak_inc_squeeze + * + * Description: Incremental Keccak squeeze; can be called on byte-level + * + * Arguments: - uint8_t *h: pointer to output bytes + * - size_t outlen: number of bytes to be squeezed + * - uint64_t *s_inc: pointer to input/output incremental state + * First 25 values represent Keccak state. + * 26th value represents either the number of absorbed bytes + * that have not been permuted, or not-yet-squeezed bytes. + * - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) + **************************************************/ +static void keccak_inc_squeeze(uint8_t *h, size_t outlen, + uint64_t *s_inc, uint32_t r) { + size_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[25]; i++) { + /* There are s_inc[25] bytes left, so r - s_inc[25] is the first + available byte. We consume from there, i.e., up to r. */ + h[i] = (uint8_t)(s_inc[(r - s_inc[25] + i) >> 3] >> (8 * ((r - s_inc[25] + i) & 0x07))); + } + h += i; + outlen -= i; + s_inc[25] -= i; + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + KeccakF1600_StatePermute(s_inc); + + for (i = 0; i < outlen && i < r; i++) { + h[i] = (uint8_t)(s_inc[i >> 3] >> (8 * (i & 0x07))); + } + h += i; + outlen -= i; + s_inc[25] = r - i; + } +} + +void shake128_inc_init(shake128incctx *state) { + state->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_inc_init(state->ctx); +} + +void shake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(state->ctx, SHAKE128_RATE, input, inlen); +} + +void shake128_inc_finalize(shake128incctx *state) { + keccak_inc_finalize(state->ctx, SHAKE128_RATE, 0x1F); +} + +void shake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state) { + keccak_inc_squeeze(output, outlen, state->ctx, SHAKE128_RATE); +} + +void shake128_inc_ctx_clone(shake128incctx *dest, const shake128incctx *src) { + dest->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKEINCCTX_BYTES); +} + +void shake128_inc_ctx_release(shake128incctx *state) { + free(state->ctx); +} + +void shake256_inc_init(shake256incctx *state) { + state->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_inc_init(state->ctx); +} + +void shake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(state->ctx, SHAKE256_RATE, input, inlen); +} + +void shake256_inc_finalize(shake256incctx *state) { + keccak_inc_finalize(state->ctx, SHAKE256_RATE, 0x1F); +} + +void shake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state) { + keccak_inc_squeeze(output, outlen, state->ctx, SHAKE256_RATE); +} + +void shake256_inc_ctx_clone(shake256incctx *dest, const shake256incctx *src) { + dest->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKEINCCTX_BYTES); +} + +void shake256_inc_ctx_release(shake256incctx *state) { + free(state->ctx); +} + +/************************************************* + * Name: shake128_absorb + * + * Description: Absorb step of the SHAKE128 XOF. + * non-incremental, starts by zeroeing the state. + * + * Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state + * - const uint8_t *input: pointer to input to be absorbed + * into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128_absorb(shake128ctx *state, const uint8_t *input, size_t inlen) { + state->ctx = malloc(PQC_SHAKECTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_absorb(state->ctx, SHAKE128_RATE, input, inlen, 0x1F); +} + +/************************************************* + * Name: shake128_squeezeblocks + * + * Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of + * SHAKE128_RATE bytes each. Modifies the state. Can be called + * multiple times to keep squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *output: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed + * (written to output) + * - shake128ctx *state: pointer to input/output Keccak state + **************************************************/ +void shake128_squeezeblocks(uint8_t *output, size_t nblocks, shake128ctx *state) { + keccak_squeezeblocks(output, nblocks, state->ctx, SHAKE128_RATE); +} + +void shake128_ctx_clone(shake128ctx *dest, const shake128ctx *src) { + dest->ctx = malloc(PQC_SHAKECTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKECTX_BYTES); +} + +/** Release the allocated state. Call only once. */ +void shake128_ctx_release(shake128ctx *state) { + free(state->ctx); +} + +/************************************************* + * Name: shake256_absorb + * + * Description: Absorb step of the SHAKE256 XOF. + * non-incremental, starts by zeroeing the state. + * + * Arguments: - shake256ctx *state: pointer to (uninitialized) output Keccak state + * - const uint8_t *input: pointer to input to be absorbed + * into s + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256_absorb(shake256ctx *state, const uint8_t *input, size_t inlen) { + state->ctx = malloc(PQC_SHAKECTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_absorb(state->ctx, SHAKE256_RATE, input, inlen, 0x1F); +} + +/************************************************* + * Name: shake256_squeezeblocks + * + * Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of + * SHAKE256_RATE bytes each. Modifies the state. Can be called + * multiple times to keep squeezing, i.e., is incremental. + * + * Arguments: - uint8_t *output: pointer to output blocks + * - size_t nblocks: number of blocks to be squeezed + * (written to output) + * - shake256ctx *state: pointer to input/output Keccak state + **************************************************/ +void shake256_squeezeblocks(uint8_t *output, size_t nblocks, shake256ctx *state) { + keccak_squeezeblocks(output, nblocks, state->ctx, SHAKE256_RATE); +} + +void shake256_ctx_clone(shake256ctx *dest, const shake256ctx *src) { + dest->ctx = malloc(PQC_SHAKECTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKECTX_BYTES); +} + +/** Release the allocated state. Call only once. */ +void shake256_ctx_release(shake256ctx *state) { + free(state->ctx); +} + +/************************************************* + * Name: shake128 + * + * Description: SHAKE128 XOF with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake128(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen) { + size_t nblocks = outlen / SHAKE128_RATE; + uint8_t t[SHAKE128_RATE]; + shake128ctx s; + + shake128_absorb(&s, input, inlen); + shake128_squeezeblocks(output, nblocks, &s); + + output += nblocks * SHAKE128_RATE; + outlen -= nblocks * SHAKE128_RATE; + + if (outlen) { + shake128_squeezeblocks(t, 1, &s); + for (size_t i = 0; i < outlen; ++i) { + output[i] = t[i]; + } + } + shake128_ctx_release(&s); +} + +/************************************************* + * Name: shake256 + * + * Description: SHAKE256 XOF with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - size_t outlen: requested output length in bytes + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void shake256(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen) { + size_t nblocks = outlen / SHAKE256_RATE; + uint8_t t[SHAKE256_RATE]; + shake256ctx s; + + shake256_absorb(&s, input, inlen); + shake256_squeezeblocks(output, nblocks, &s); + + output += nblocks * SHAKE256_RATE; + outlen -= nblocks * SHAKE256_RATE; + + if (outlen) { + shake256_squeezeblocks(t, 1, &s); + for (size_t i = 0; i < outlen; ++i) { + output[i] = t[i]; + } + } + shake256_ctx_release(&s); +} + +void sha3_256_inc_init(sha3_256incctx *state) { + state->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_inc_init(state->ctx); +} + +void sha3_256_inc_ctx_clone(sha3_256incctx *dest, const sha3_256incctx *src) { + dest->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKEINCCTX_BYTES); +} + +void sha3_256_inc_ctx_release(sha3_256incctx *state) { + free(state->ctx); +} + +void sha3_256_inc_absorb(sha3_256incctx *state, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(state->ctx, SHA3_256_RATE, input, inlen); +} + +void sha3_256_inc_finalize(uint8_t *output, sha3_256incctx *state) { + uint8_t t[SHA3_256_RATE]; + keccak_inc_finalize(state->ctx, SHA3_256_RATE, 0x06); + + keccak_squeezeblocks(t, 1, state->ctx, SHA3_256_RATE); + + sha3_256_inc_ctx_release(state); + + for (size_t i = 0; i < 32; i++) { + output[i] = t[i]; + } +} + +/************************************************* + * Name: sha3_256 + * + * Description: SHA3-256 with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen) { + uint64_t s[25]; + uint8_t t[SHA3_256_RATE]; + + /* Absorb input */ + keccak_absorb(s, SHA3_256_RATE, input, inlen, 0x06); + + /* Squeeze output */ + keccak_squeezeblocks(t, 1, s, SHA3_256_RATE); + + for (size_t i = 0; i < 32; i++) { + output[i] = t[i]; + } +} + +void sha3_384_inc_init(sha3_384incctx *state) { + state->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_inc_init(state->ctx); +} + +void sha3_384_inc_ctx_clone(sha3_384incctx *dest, const sha3_384incctx *src) { + dest->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKEINCCTX_BYTES); +} + +void sha3_384_inc_absorb(sha3_384incctx *state, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(state->ctx, SHA3_384_RATE, input, inlen); +} + +void sha3_384_inc_ctx_release(sha3_384incctx *state) { + free(state->ctx); +} + +void sha3_384_inc_finalize(uint8_t *output, sha3_384incctx *state) { + uint8_t t[SHA3_384_RATE]; + keccak_inc_finalize(state->ctx, SHA3_384_RATE, 0x06); + + keccak_squeezeblocks(t, 1, state->ctx, SHA3_384_RATE); + + sha3_384_inc_ctx_release(state); + + for (size_t i = 0; i < 48; i++) { + output[i] = t[i]; + } +} + +/************************************************* + * Name: sha3_384 + * + * Description: SHA3-256 with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_384(uint8_t *output, const uint8_t *input, size_t inlen) { + uint64_t s[25]; + uint8_t t[SHA3_384_RATE]; + + /* Absorb input */ + keccak_absorb(s, SHA3_384_RATE, input, inlen, 0x06); + + /* Squeeze output */ + keccak_squeezeblocks(t, 1, s, SHA3_384_RATE); + + for (size_t i = 0; i < 48; i++) { + output[i] = t[i]; + } +} + +void sha3_512_inc_init(sha3_512incctx *state) { + state->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (state->ctx == NULL) { + exit(111); + } + keccak_inc_init(state->ctx); +} + +void sha3_512_inc_ctx_clone(sha3_512incctx *dest, const sha3_512incctx *src) { + dest->ctx = malloc(PQC_SHAKEINCCTX_BYTES); + if (dest->ctx == NULL) { + exit(111); + } + memcpy(dest->ctx, src->ctx, PQC_SHAKEINCCTX_BYTES); +} + +void sha3_512_inc_absorb(sha3_512incctx *state, const uint8_t *input, size_t inlen) { + keccak_inc_absorb(state->ctx, SHA3_512_RATE, input, inlen); +} + +void sha3_512_inc_ctx_release(sha3_512incctx *state) { + free(state->ctx); +} + +void sha3_512_inc_finalize(uint8_t *output, sha3_512incctx *state) { + uint8_t t[SHA3_512_RATE]; + keccak_inc_finalize(state->ctx, SHA3_512_RATE, 0x06); + + keccak_squeezeblocks(t, 1, state->ctx, SHA3_512_RATE); + + sha3_512_inc_ctx_release(state); + + for (size_t i = 0; i < 64; i++) { + output[i] = t[i]; + } +} + +/************************************************* + * Name: sha3_512 + * + * Description: SHA3-512 with non-incremental API + * + * Arguments: - uint8_t *output: pointer to output + * - const uint8_t *input: pointer to input + * - size_t inlen: length of input in bytes + **************************************************/ +void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen) { + uint64_t s[25]; + uint8_t t[SHA3_512_RATE]; + + /* Absorb input */ + keccak_absorb(s, SHA3_512_RATE, input, inlen, 0x06); + + /* Squeeze output */ + keccak_squeezeblocks(t, 1, s, SHA3_512_RATE); + + for (size_t i = 0; i < 64; i++) { + output[i] = t[i]; + } +} diff --git a/common/fips202.h b/common/fips202.h new file mode 100644 index 0000000..0dd0ff2 --- /dev/null +++ b/common/fips202.h @@ -0,0 +1,166 @@ +#ifndef FIPS202_H +#define FIPS202_H + +#include +#include + +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 +#define SHA3_256_RATE 136 +#define SHA3_384_RATE 104 +#define SHA3_512_RATE 72 + +#define PQC_SHAKEINCCTX_BYTES (sizeof(uint64_t)*26) +#define PQC_SHAKECTX_BYTES (sizeof(uint64_t)*25) + +// Context for incremental API +typedef struct { + uint64_t *ctx; +} shake128incctx; + +// Context for non-incremental API +typedef struct { + uint64_t *ctx; +} shake128ctx; + +// Context for incremental API +typedef struct { + uint64_t *ctx; +} shake256incctx; + +// Context for non-incremental API +typedef struct { + uint64_t *ctx; +} shake256ctx; + +// Context for incremental API +typedef struct { + uint64_t *ctx; +} sha3_256incctx; + +// Context for incremental API +typedef struct { + uint64_t *ctx; +} sha3_384incctx; + +// Context for incremental API +typedef struct { + uint64_t *ctx; +} sha3_512incctx; + +/* Initialize the state and absorb the provided input. + * + * This function does not support being called multiple times + * with the same state. + */ +void shake128_absorb(shake128ctx *state, const uint8_t *input, size_t inlen); +/* Squeeze output out of the sponge. + * + * Supports being called multiple times + */ +void shake128_squeezeblocks(uint8_t *output, size_t nblocks, shake128ctx *state); +/* Free the state */ +void shake128_ctx_release(shake128ctx *state); +/* Copy the state. */ +void shake128_ctx_clone(shake128ctx *dest, const shake128ctx *src); + +/* Initialize incremental hashing API */ +void shake128_inc_init(shake128incctx *state); +/* Absorb more information into the XOF. + * + * Can be called multiple times. + */ +void shake128_inc_absorb(shake128incctx *state, const uint8_t *input, size_t inlen); +/* Finalize the XOF for squeezing */ +void shake128_inc_finalize(shake128incctx *state); +/* Squeeze output out of the sponge. + * + * Supports being called multiple times + */ +void shake128_inc_squeeze(uint8_t *output, size_t outlen, shake128incctx *state); +/* Copy the context of the SHAKE128 XOF */ +void shake128_inc_ctx_clone(shake128incctx *dest, const shake128incctx *src); +/* Free the context of the SHAKE128 XOF */ +void shake128_inc_ctx_release(shake128incctx *state); + +/* Initialize the state and absorb the provided input. + * + * This function does not support being called multiple times + * with the same state. + */ +void shake256_absorb(shake256ctx *state, const uint8_t *input, size_t inlen); +/* Squeeze output out of the sponge. + * + * Supports being called multiple times + */ +void shake256_squeezeblocks(uint8_t *output, size_t nblocks, shake256ctx *state); +/* Free the context held by this XOF */ +void shake256_ctx_release(shake256ctx *state); +/* Copy the context held by this XOF */ +void shake256_ctx_clone(shake256ctx *dest, const shake256ctx *src); + +/* Initialize incremental hashing API */ +void shake256_inc_init(shake256incctx *state); +void shake256_inc_absorb(shake256incctx *state, const uint8_t *input, size_t inlen); +/* Prepares for squeeze phase */ +void shake256_inc_finalize(shake256incctx *state); +/* Squeeze output out of the sponge. + * + * Supports being called multiple times + */ +void shake256_inc_squeeze(uint8_t *output, size_t outlen, shake256incctx *state); +/* Copy the state */ +void shake256_inc_ctx_clone(shake256incctx *dest, const shake256incctx *src); +/* Free the state */ +void shake256_inc_ctx_release(shake256incctx *state); + +/* One-stop SHAKE128 call */ +void shake128(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen); + +/* One-stop SHAKE256 call */ +void shake256(uint8_t *output, size_t outlen, + const uint8_t *input, size_t inlen); + +/* Initialize the incremental hashing state */ +void sha3_256_inc_init(sha3_256incctx *state); +/* Absorb blocks into SHA3 */ +void sha3_256_inc_absorb(sha3_256incctx *state, const uint8_t *input, size_t inlen); +/* Obtain the output of the function and free `state` */ +void sha3_256_inc_finalize(uint8_t *output, sha3_256incctx *state); +/* Copy the context */ +void sha3_256_inc_ctx_clone(sha3_256incctx *dest, const sha3_256incctx *src); +/* Release the state, don't use if `_finalize` has been used */ +void sha3_256_inc_ctx_release(sha3_256incctx *state); + +void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen); + +/* Initialize the incremental hashing state */ +void sha3_384_inc_init(sha3_384incctx *state); +/* Absorb blocks into SHA3 */ +void sha3_384_inc_absorb(sha3_384incctx *state, const uint8_t *input, size_t inlen); +/* Obtain the output of the function and free `state` */ +void sha3_384_inc_finalize(uint8_t *output, sha3_384incctx *state); +/* Copy the context */ +void sha3_384_inc_ctx_clone(sha3_384incctx *dest, const sha3_384incctx *src); +/* Release the state, don't use if `_finalize` has been used */ +void sha3_384_inc_ctx_release(sha3_384incctx *state); + +/* One-stop SHA3-384 shop */ +void sha3_384(uint8_t *output, const uint8_t *input, size_t inlen); + +/* Initialize the incremental hashing state */ +void sha3_512_inc_init(sha3_512incctx *state); +/* Absorb blocks into SHA3 */ +void sha3_512_inc_absorb(sha3_512incctx *state, const uint8_t *input, size_t inlen); +/* Obtain the output of the function and free `state` */ +void sha3_512_inc_finalize(uint8_t *output, sha3_512incctx *state); +/* Copy the context */ +void sha3_512_inc_ctx_clone(sha3_512incctx *dest, const sha3_512incctx *src); +/* Release the state, don't use if `_finalize` has been used */ +void sha3_512_inc_ctx_release(sha3_512incctx *state); + +/* One-stop SHA3-512 shop */ +void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen); + +#endif diff --git a/dilithium2/LICENSE b/dilithium2/LICENSE new file mode 100644 index 0000000..08473af --- /dev/null +++ b/dilithium2/LICENSE @@ -0,0 +1,5 @@ +Public Domain (https://creativecommons.org/share-your-work/public-domain/cc0/) + +For Keccak and AES we are using public-domain +code from sources and by authors listed in +comments on top of the respective files. diff --git a/dilithium2/Makefile b/dilithium2/Makefile new file mode 100644 index 0000000..6c1aea7 --- /dev/null +++ b/dilithium2/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libdilithium2_clean.a +HEADERS=api.h ntt.h packing.h params.h poly.h polyvec.h reduce.h rounding.h sign.h symmetric.h +OBJECTS=ntt.o packing.o poly.o polyvec.o reduce.o rounding.o sign.o symmetric-shake.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/dilithium2/Makefile.Microsoft_nmake b/dilithium2/Makefile.Microsoft_nmake new file mode 100644 index 0000000..410bd6a --- /dev/null +++ b/dilithium2/Makefile.Microsoft_nmake @@ -0,0 +1,23 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libdilithium2_clean.lib +OBJECTS=ntt.obj packing.obj poly.obj polyvec.obj reduce.obj rounding.obj sign.obj symmetric-shake.obj + +# Warning C4146 is raised when a unary minus operator is applied to an +# unsigned type; this has nonetheless been standard and portable for as +# long as there has been a C standard, and we need it for constant-time +# computations. Thus, we disable that spurious warning. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4146 + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/dilithium2/api.h b/dilithium2/api.h new file mode 100644 index 0000000..fb14271 --- /dev/null +++ b/dilithium2/api.h @@ -0,0 +1,53 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_API_H +#define PQCLEAN_DILITHIUM2_CLEAN_API_H + +#include +#include + +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES 1312 +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES 2560 +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES 2420 +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_ALGNAME "Dilithium2" + +#if defined(_WIN32) +#define HYBRIDPQC_API __declspec(dllexport) +#else +#define HYBRIDPQC_API __attribute__((visibility("default"))) +#endif + +#if defined(HYBRIDPQC_SYS_UEFI) +#undef HYBRIDPQC_API +#define HYBRIDPQC_API +#endif + + +#if defined(__cplusplus) +extern "C" { +#endif + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(uint8_t* pk, uint8_t* sk, const uint8_t* seed); //seed needs to be 32 chars long + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, + const uint8_t *pk); + +HYBRIDPQC_API int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, + const uint8_t *pk); + +#endif + + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/dilithium2/ntt.c b/dilithium2/ntt.c new file mode 100644 index 0000000..af3c776 --- /dev/null +++ b/dilithium2/ntt.c @@ -0,0 +1,98 @@ +#include "ntt.h" +#include "params.h" +#include "reduce.h" +#include + +static const int32_t zetas[N] = { + 0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468, + 1826347, 2353451, -359251, -2091905, 3119733, -2884855, 3111497, 2680103, + 2725464, 1024112, -1079900, 3585928, -549488, -1119584, 2619752, -2108549, + -2118186, -3859737, -1399561, -3277672, 1757237, -19422, 4010497, 280005, + 2706023, 95776, 3077325, 3530437, -1661693, -3592148, -2537516, 3915439, + -3861115, -3043716, 3574422, -2867647, 3539968, -300467, 2348700, -539299, + -1699267, -1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596, + 811944, 531354, 954230, 3881043, 3900724, -2556880, 2071892, -2797779, + -3930395, -1528703, -3677745, -3041255, -1452451, 3475950, 2176455, -1585221, + -1257611, 1939314, -4083598, -1000202, -3190144, -3157330, -3632928, 126922, + 3412210, -983419, 2147896, 2715295, -2967645, -3693493, -411027, -2477047, + -671102, -1228525, -22981, -1308169, -381987, 1349076, 1852771, -1430430, + -3343383, 264944, 508951, 3097992, 44288, -1100098, 904516, 3958618, + -3724342, -8578, 1653064, -3249728, 2389356, -210977, 759969, -1316856, + 189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589, 1341330, + 1285669, -1584928, -812732, -1439742, -3019102, -3881060, -3628969, 3839961, + 2091667, 3407706, 2316500, 3817976, -3342478, 2244091, -2446433, -3562462, + 266997, 2434439, -1235728, 3513181, -3520352, -3759364, -1197226, -3193378, + 900702, 1859098, 909542, 819034, 495491, -1613174, -43260, -522500, + -655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838, + 342297, 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044, + 2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353, 1595974, + -3767016, 1250494, 2635921, -3548272, -2994039, 1869119, 1903435, -1050970, + -1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115, -1962642, + -1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031, + -542412, -2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993, + -2013608, 2432395, 2454455, -164721, 1957272, 3369112, 185531, -1207385, + -3183426, 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107, + -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735, 472078, + -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893, + -2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687, + -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782 +}; + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_ntt +* +* Description: Forward NTT, in-place. No modular reduction is performed after +* additions or subtractions. Output vector is in bitreversed order. +* +* Arguments: - uint32_t p[N]: input/output coefficient array +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_ntt(int32_t a[N]) { + unsigned int len, start, j, k; + int32_t zeta, t; + + k = 0; + for (len = 128; len > 0; len >>= 1) { + for (start = 0; start < N; start = j + len) { + zeta = zetas[++k]; + for (j = start; j < start + len; ++j) { + t = PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce((int64_t)zeta * a[j + len]); + a[j + len] = a[j] - t; + a[j] = a[j] + t; + } + } + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_invntt_tomont +* +* Description: Inverse NTT and multiplication by Montgomery factor 2^32. +* In-place. No modular reductions after additions or +* subtractions; input coefficients need to be smaller than +* Q in absolute value. Output coefficient are smaller than Q in +* absolute value. +* +* Arguments: - uint32_t p[N]: input/output coefficient array +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_invntt_tomont(int32_t a[N]) { + unsigned int start, len, j, k; + int32_t t, zeta; + const int32_t f = 41978; // mont^2/256 + + k = 256; + for (len = 1; len < N; len <<= 1) { + for (start = 0; start < N; start = j + len) { + zeta = -zetas[--k]; + for (j = start; j < start + len; ++j) { + t = a[j]; + a[j] = t + a[j + len]; + a[j + len] = t - a[j + len]; + a[j + len] = PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce((int64_t)zeta * a[j + len]); + } + } + } + + for (j = 0; j < N; ++j) { + a[j] = PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce((int64_t)f * a[j]); + } +} diff --git a/dilithium2/ntt.h b/dilithium2/ntt.h new file mode 100644 index 0000000..3b0ff00 --- /dev/null +++ b/dilithium2/ntt.h @@ -0,0 +1,10 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_NTT_H +#define PQCLEAN_DILITHIUM2_CLEAN_NTT_H +#include "params.h" +#include + +void PQCLEAN_DILITHIUM2_CLEAN_ntt(int32_t a[N]); + +void PQCLEAN_DILITHIUM2_CLEAN_invntt_tomont(int32_t a[N]); + +#endif diff --git a/dilithium2/packing.c b/dilithium2/packing.c new file mode 100644 index 0000000..2d396c9 --- /dev/null +++ b/dilithium2/packing.c @@ -0,0 +1,260 @@ +#include "packing.h" +#include "params.h" +#include "poly.h" +#include "polyvec.h" + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_pack_pk +* +* Description: Bit-pack public key pk = (rho, t1). +* +* Arguments: - uint8_t pk[]: output byte array +* - const uint8_t rho[]: byte array containing rho +* - const polyveck *t1: pointer to vector t1 +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_pack_pk(uint8_t pk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES], + const uint8_t rho[SEEDBYTES], + const polyveck *t1) { + unsigned int i; + + for (i = 0; i < SEEDBYTES; ++i) { + pk[i] = rho[i]; + } + pk += SEEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyt1_pack(pk + i * POLYT1_PACKEDBYTES, &t1->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_unpack_pk +* +* Description: Unpack public key pk = (rho, t1). +* +* Arguments: - const uint8_t rho[]: output byte array for rho +* - const polyveck *t1: pointer to output vector t1 +* - uint8_t pk[]: byte array containing bit-packed pk +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_unpack_pk(uint8_t rho[SEEDBYTES], + polyveck *t1, + const uint8_t pk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES]) { + unsigned int i; + + for (i = 0; i < SEEDBYTES; ++i) { + rho[i] = pk[i]; + } + pk += SEEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyt1_unpack(&t1->vec[i], pk + i * POLYT1_PACKEDBYTES); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_pack_sk +* +* Description: Bit-pack secret key sk = (rho, tr, key, t0, s1, s2). +* +* Arguments: - uint8_t sk[]: output byte array +* - const uint8_t rho[]: byte array containing rho +* - const uint8_t tr[]: byte array containing tr +* - const uint8_t key[]: byte array containing key +* - const polyveck *t0: pointer to vector t0 +* - const polyvecl *s1: pointer to vector s1 +* - const polyveck *s2: pointer to vector s2 +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_pack_sk(uint8_t sk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES], + const uint8_t rho[SEEDBYTES], + const uint8_t tr[TRBYTES], + const uint8_t key[SEEDBYTES], + const polyveck *t0, + const polyvecl *s1, + const polyveck *s2) { + unsigned int i; + + for (i = 0; i < SEEDBYTES; ++i) { + sk[i] = rho[i]; + } + sk += SEEDBYTES; + + for (i = 0; i < SEEDBYTES; ++i) { + sk[i] = key[i]; + } + sk += SEEDBYTES; + + for (i = 0; i < TRBYTES; ++i) { + sk[i] = tr[i]; + } + sk += TRBYTES; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyeta_pack(sk + i * POLYETA_PACKEDBYTES, &s1->vec[i]); + } + sk += L * POLYETA_PACKEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyeta_pack(sk + i * POLYETA_PACKEDBYTES, &s2->vec[i]); + } + sk += K * POLYETA_PACKEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyt0_pack(sk + i * POLYT0_PACKEDBYTES, &t0->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_unpack_sk +* +* Description: Unpack secret key sk = (rho, tr, key, t0, s1, s2). +* +* Arguments: - const uint8_t rho[]: output byte array for rho +* - const uint8_t tr[]: output byte array for tr +* - const uint8_t key[]: output byte array for key +* - const polyveck *t0: pointer to output vector t0 +* - const polyvecl *s1: pointer to output vector s1 +* - const polyveck *s2: pointer to output vector s2 +* - uint8_t sk[]: byte array containing bit-packed sk +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_unpack_sk(uint8_t rho[SEEDBYTES], + uint8_t tr[TRBYTES], + uint8_t key[SEEDBYTES], + polyveck *t0, + polyvecl *s1, + polyveck *s2, + const uint8_t sk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES]) { + unsigned int i; + + for (i = 0; i < SEEDBYTES; ++i) { + rho[i] = sk[i]; + } + sk += SEEDBYTES; + + for (i = 0; i < SEEDBYTES; ++i) { + key[i] = sk[i]; + } + sk += SEEDBYTES; + + for (i = 0; i < TRBYTES; ++i) { + tr[i] = sk[i]; + } + sk += TRBYTES; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyeta_unpack(&s1->vec[i], sk + i * POLYETA_PACKEDBYTES); + } + sk += L * POLYETA_PACKEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyeta_unpack(&s2->vec[i], sk + i * POLYETA_PACKEDBYTES); + } + sk += K * POLYETA_PACKEDBYTES; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyt0_unpack(&t0->vec[i], sk + i * POLYT0_PACKEDBYTES); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_pack_sig +* +* Description: Bit-pack signature sig = (c, z, h). +* +* Arguments: - uint8_t sig[]: output byte array +* - const uint8_t *c: pointer to PQCLEAN_DILITHIUM2_CLEAN_challenge hash length SEEDBYTES +* - const polyvecl *z: pointer to vector z +* - const polyveck *h: pointer to hint vector h +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_pack_sig(uint8_t sig[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES], + const uint8_t c[CTILDEBYTES], + const polyvecl *z, + const polyveck *h) { + unsigned int i, j, k; + + for (i = 0; i < CTILDEBYTES; ++i) { + sig[i] = c[i]; + } + sig += CTILDEBYTES; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyz_pack(sig + i * POLYZ_PACKEDBYTES, &z->vec[i]); + } + sig += L * POLYZ_PACKEDBYTES; + + /* Encode h */ + for (i = 0; i < OMEGA + K; ++i) { + sig[i] = 0; + } + + k = 0; + for (i = 0; i < K; ++i) { + for (j = 0; j < N; ++j) { + if (h->vec[i].coeffs[j] != 0) { + sig[k++] = (uint8_t) j; + } + } + + sig[OMEGA + i] = (uint8_t) k; + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_unpack_sig +* +* Description: Unpack signature sig = (c, z, h). +* +* Arguments: - uint8_t *c: pointer to output PQCLEAN_DILITHIUM2_CLEAN_challenge hash +* - polyvecl *z: pointer to output vector z +* - polyveck *h: pointer to output hint vector h +* - const uint8_t sig[]: byte array containing +* bit-packed signature +* +* Returns 1 in case of malformed signature; otherwise 0. +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_unpack_sig(uint8_t c[CTILDEBYTES], + polyvecl *z, + polyveck *h, + const uint8_t sig[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES]) { + unsigned int i, j, k; + + for (i = 0; i < CTILDEBYTES; ++i) { + c[i] = sig[i]; + } + sig += CTILDEBYTES; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyz_unpack(&z->vec[i], sig + i * POLYZ_PACKEDBYTES); + } + sig += L * POLYZ_PACKEDBYTES; + + /* Decode h */ + k = 0; + for (i = 0; i < K; ++i) { + for (j = 0; j < N; ++j) { + h->vec[i].coeffs[j] = 0; + } + + if (sig[OMEGA + i] < k || sig[OMEGA + i] > OMEGA) { + return 1; + } + + for (j = k; j < sig[OMEGA + i]; ++j) { + /* Coefficients are ordered for strong unforgeability */ + if (j > k && sig[j] <= sig[j - 1]) { + return 1; + } + h->vec[i].coeffs[sig[j]] = 1; + } + + k = sig[OMEGA + i]; + } + + /* Extra indices are zero for strong unforgeability */ + for (j = k; j < OMEGA; ++j) { + if (sig[j]) { + return 1; + } + } + + return 0; +} diff --git a/dilithium2/packing.h b/dilithium2/packing.h new file mode 100644 index 0000000..8079bae --- /dev/null +++ b/dilithium2/packing.h @@ -0,0 +1,31 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_PACKING_H +#define PQCLEAN_DILITHIUM2_CLEAN_PACKING_H +#include "params.h" +#include "polyvec.h" +#include + +void PQCLEAN_DILITHIUM2_CLEAN_pack_pk(uint8_t pk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES], const uint8_t rho[SEEDBYTES], const polyveck *t1); + +void PQCLEAN_DILITHIUM2_CLEAN_pack_sk(uint8_t sk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES], + const uint8_t rho[SEEDBYTES], + const uint8_t tr[TRBYTES], + const uint8_t key[SEEDBYTES], + const polyveck *t0, + const polyvecl *s1, + const polyveck *s2); + +void PQCLEAN_DILITHIUM2_CLEAN_pack_sig(uint8_t sig[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES], const uint8_t c[CTILDEBYTES], const polyvecl *z, const polyveck *h); + +void PQCLEAN_DILITHIUM2_CLEAN_unpack_pk(uint8_t rho[SEEDBYTES], polyveck *t1, const uint8_t pk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES]); + +void PQCLEAN_DILITHIUM2_CLEAN_unpack_sk(uint8_t rho[SEEDBYTES], + uint8_t tr[TRBYTES], + uint8_t key[SEEDBYTES], + polyveck *t0, + polyvecl *s1, + polyveck *s2, + const uint8_t sk[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES]); + +int PQCLEAN_DILITHIUM2_CLEAN_unpack_sig(uint8_t c[CTILDEBYTES], polyvecl *z, polyveck *h, const uint8_t sig[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES]); + +#endif diff --git a/dilithium2/params.h b/dilithium2/params.h new file mode 100644 index 0000000..d1f908f --- /dev/null +++ b/dilithium2/params.h @@ -0,0 +1,41 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_PARAMS_H +#define PQCLEAN_DILITHIUM2_CLEAN_PARAMS_H + +#define SEEDBYTES 32 +#define CRHBYTES 64 +#define TRBYTES 64 +#define RNDBYTES 32 +#define N 256 +#define Q 8380417 +#define D 13 +#define ROOT_OF_UNITY 1753 + +#define K 4 +#define L 4 +#define ETA 2 +#define TAU 39 +#define BETA 78 +#define GAMMA1 (1 << 17) +#define GAMMA2 ((Q-1)/88) +#define OMEGA 80 +#define CTILDEBYTES 32 + +#define POLYT1_PACKEDBYTES 320 +#define POLYT0_PACKEDBYTES 416 +#define POLYVECH_PACKEDBYTES (OMEGA + K) + +#define POLYZ_PACKEDBYTES 576 + +#define POLYW1_PACKEDBYTES 192 + +#define POLYETA_PACKEDBYTES 96 + +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES (SEEDBYTES + K*POLYT1_PACKEDBYTES) +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES (2*SEEDBYTES \ + + TRBYTES \ + + L*POLYETA_PACKEDBYTES \ + + K*POLYETA_PACKEDBYTES \ + + K*POLYT0_PACKEDBYTES) +#define PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES (CTILDEBYTES + L*POLYZ_PACKEDBYTES + POLYVECH_PACKEDBYTES) + +#endif diff --git a/dilithium2/poly.c b/dilithium2/poly.c new file mode 100644 index 0000000..553015b --- /dev/null +++ b/dilithium2/poly.c @@ -0,0 +1,848 @@ +#include "ntt.h" +#include "params.h" +#include "poly.h" +#include "reduce.h" +#include "rounding.h" +#include "symmetric.h" +#include + +#define DBENCH_START() +#define DBENCH_STOP(t) + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_reduce +* +* Description: Inplace reduction of all coefficients of polynomial to +* representative in [-6283009,6283007]. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_reduce(poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + a->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_reduce32(a->coeffs[i]); + } + + DBENCH_STOP(*tred); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_caddq +* +* Description: For all coefficients of in/out polynomial add Q if +* coefficient is negative. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_caddq(poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + a->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_caddq(a->coeffs[i]); + } + + DBENCH_STOP(*tred); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_add +* +* Description: Add polynomials. No modular reduction is performed. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first summand +* - const poly *b: pointer to second summand +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_add(poly *c, const poly *a, const poly *b) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + c->coeffs[i] = a->coeffs[i] + b->coeffs[i]; + } + + DBENCH_STOP(*tadd); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_sub +* +* Description: Subtract polynomials. No modular reduction is +* performed. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial to be +* subtraced from first input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_sub(poly *c, const poly *a, const poly *b) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + c->coeffs[i] = a->coeffs[i] - b->coeffs[i]; + } + + DBENCH_STOP(*tadd); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_shiftl +* +* Description: Multiply polynomial by 2^D without modular reduction. Assumes +* input coefficients to be less than 2^{31-D} in absolute value. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_shiftl(poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + a->coeffs[i] <<= D; + } + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_ntt +* +* Description: Inplace forward NTT. Coefficients can grow by +* 8*Q in absolute value. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(poly *a) { + DBENCH_START(); + + PQCLEAN_DILITHIUM2_CLEAN_ntt(a->coeffs); + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_invntt_tomont +* +* Description: Inplace inverse NTT and multiplication by 2^{32}. +* Input coefficients need to be less than Q in absolute +* value and output coefficients are again bounded by Q. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_invntt_tomont(poly *a) { + DBENCH_START(); + + PQCLEAN_DILITHIUM2_CLEAN_invntt_tomont(a->coeffs); + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery +* +* Description: Pointwise multiplication of polynomials in NTT domain +* representation and multiplication of resulting polynomial +* by 2^{-32}. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(poly *c, const poly *a, const poly *b) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + c->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]); + } + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_power2round +* +* Description: For all coefficients c of the input polynomial, +* compute c0, c1 such that c mod Q = c1*2^D + c0 +* with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients to be +* standard representatives. +* +* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 +* - poly *a0: pointer to output polynomial with coefficients c0 +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_power2round(poly *a1, poly *a0, const poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + a1->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_power2round(&a0->coeffs[i], a->coeffs[i]); + } + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_decompose +* +* Description: For all coefficients c of the input polynomial, +* compute high and low bits c0, c1 such c mod Q = c1*ALPHA + c0 +* with -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA where we +* set c1 = 0 and -ALPHA/2 <= c0 = c mod Q - Q < 0. +* Assumes coefficients to be standard representatives. +* +* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 +* - poly *a0: pointer to output polynomial with coefficients c0 +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_decompose(poly *a1, poly *a0, const poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + a1->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_decompose(&a0->coeffs[i], a->coeffs[i]); + } + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_make_hint +* +* Description: Compute hint polynomial. The coefficients of which indicate +* whether the low bits of the corresponding coefficient of +* the input polynomial overflow into the high bits. +* +* Arguments: - poly *h: pointer to output hint polynomial +* - const poly *a0: pointer to low part of input polynomial +* - const poly *a1: pointer to high part of input polynomial +* +* Returns number of 1 bits. +**************************************************/ +unsigned int PQCLEAN_DILITHIUM2_CLEAN_poly_make_hint(poly *h, const poly *a0, const poly *a1) { + unsigned int i, s = 0; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + h->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_make_hint(a0->coeffs[i], a1->coeffs[i]); + s += h->coeffs[i]; + } + + DBENCH_STOP(*tround); + return s; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_use_hint +* +* Description: Use hint polynomial to correct the high bits of a polynomial. +* +* Arguments: - poly *b: pointer to output polynomial with corrected high bits +* - const poly *a: pointer to input polynomial +* - const poly *h: pointer to input hint polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_use_hint(poly *b, const poly *a, const poly *h) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N; ++i) { + b->coeffs[i] = PQCLEAN_DILITHIUM2_CLEAN_use_hint(a->coeffs[i], h->coeffs[i]); + } + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_chknorm +* +* Description: Check infinity norm of polynomial against given bound. +* Assumes input coefficients were reduced by PQCLEAN_DILITHIUM2_CLEAN_reduce32(). +* +* Arguments: - const poly *a: pointer to polynomial +* - int32_t B: norm bound +* +* Returns 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise. +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_poly_chknorm(const poly *a, int32_t B) { + unsigned int i; + int32_t t; + DBENCH_START(); + + if (B > (Q - 1) / 8) { + return 1; + } + + /* It is ok to leak which coefficient violates the bound since + the probability for each coefficient is independent of secret + data but we must not leak the sign of the centralized representative. */ + for (i = 0; i < N; ++i) { + /* Absolute value */ + t = a->coeffs[i] >> 31; + t = a->coeffs[i] - (t & 2 * a->coeffs[i]); + + if (t >= B) { + DBENCH_STOP(*tsample); + return 1; + } + } + + DBENCH_STOP(*tsample); + return 0; +} + +/************************************************* +* Name: rej_uniform +* +* Description: Sample uniformly random coefficients in [0, Q-1] by +* performing rejection sampling on array of random bytes. +* +* Arguments: - int32_t *a: pointer to output array (allocated) +* - unsigned int len: number of coefficients to be sampled +* - const uint8_t *buf: array of random bytes +* - unsigned int buflen: length of array of random bytes +* +* Returns number of sampled coefficients. Can be smaller than len if not enough +* random bytes were given. +**************************************************/ +static unsigned int rej_uniform(int32_t *a, + unsigned int len, + const uint8_t *buf, + unsigned int buflen) { + unsigned int ctr, pos; + uint32_t t; + DBENCH_START(); + + ctr = pos = 0; + while (ctr < len && pos + 3 <= buflen) { + t = buf[pos++]; + t |= (uint32_t)buf[pos++] << 8; + t |= (uint32_t)buf[pos++] << 16; + t &= 0x7FFFFF; + + if (t < Q) { + a[ctr++] = t; + } + } + + DBENCH_STOP(*tsample); + return ctr; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_uniform +* +* Description: Sample polynomial with uniformly random coefficients +* in [0,Q-1] by performing rejection sampling on the +* output stream of SHAKE256(seed|nonce) +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t seed[]: byte array with seed of length SEEDBYTES +* - uint16_t nonce: 2-byte nonce +**************************************************/ +#define POLY_UNIFORM_NBLOCKS ((768 + STREAM128_BLOCKBYTES - 1)/STREAM128_BLOCKBYTES) +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform(poly *a, + const uint8_t seed[SEEDBYTES], + uint16_t nonce) { + unsigned int i, ctr, off; + unsigned int buflen = POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES; + uint8_t buf[POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES + 2]; + stream128_state state; + + stream128_init(&state, seed, nonce); + stream128_squeezeblocks(buf, POLY_UNIFORM_NBLOCKS, &state); + + ctr = rej_uniform(a->coeffs, N, buf, buflen); + + while (ctr < N) { + off = buflen % 3; + for (i = 0; i < off; ++i) { + buf[i] = buf[buflen - off + i]; + } + + stream128_squeezeblocks(buf + off, 1, &state); + buflen = STREAM128_BLOCKBYTES + off; + ctr += rej_uniform(a->coeffs + ctr, N - ctr, buf, buflen); + } + stream128_release(&state); +} + +/************************************************* +* Name: rej_eta +* +* Description: Sample uniformly random coefficients in [-ETA, ETA] by +* performing rejection sampling on array of random bytes. +* +* Arguments: - int32_t *a: pointer to output array (allocated) +* - unsigned int len: number of coefficients to be sampled +* - const uint8_t *buf: array of random bytes +* - unsigned int buflen: length of array of random bytes +* +* Returns number of sampled coefficients. Can be smaller than len if not enough +* random bytes were given. +**************************************************/ +static unsigned int rej_eta(int32_t *a, + unsigned int len, + const uint8_t *buf, + unsigned int buflen) { + unsigned int ctr, pos; + uint32_t t0, t1; + DBENCH_START(); + + ctr = pos = 0; + while (ctr < len && pos < buflen) { + t0 = buf[pos] & 0x0F; + t1 = buf[pos++] >> 4; + + if (t0 < 15) { + t0 = t0 - (205 * t0 >> 10) * 5; + a[ctr++] = 2 - t0; + } + if (t1 < 15 && ctr < len) { + t1 = t1 - (205 * t1 >> 10) * 5; + a[ctr++] = 2 - t1; + } + } + + DBENCH_STOP(*tsample); + return ctr; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_eta +* +* Description: Sample polynomial with uniformly random coefficients +* in [-ETA,ETA] by performing rejection sampling on the +* output stream from SHAKE256(seed|nonce) +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t seed[]: byte array with seed of length CRHBYTES +* - uint16_t nonce: 2-byte nonce +**************************************************/ +#define POLY_UNIFORM_ETA_NBLOCKS ((136 + STREAM256_BLOCKBYTES - 1)/STREAM256_BLOCKBYTES) +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_eta(poly *a, + const uint8_t seed[CRHBYTES], + uint16_t nonce) { + unsigned int ctr; + unsigned int buflen = POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES; + uint8_t buf[POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES]; + stream256_state state; + + stream256_init(&state, seed, nonce); + stream256_squeezeblocks(buf, POLY_UNIFORM_ETA_NBLOCKS, &state); + + ctr = rej_eta(a->coeffs, N, buf, buflen); + + while (ctr < N) { + stream256_squeezeblocks(buf, 1, &state); + ctr += rej_eta(a->coeffs + ctr, N - ctr, buf, STREAM256_BLOCKBYTES); + } + stream256_release(&state); +} + +/************************************************* +* Name: poly_uniform_gamma1m1 +* +* Description: Sample polynomial with uniformly random coefficients +* in [-(GAMMA1 - 1), GAMMA1] by unpacking output stream +* of SHAKE256(seed|nonce) +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t seed[]: byte array with seed of length CRHBYTES +* - uint16_t nonce: 16-bit nonce +**************************************************/ +#define POLY_UNIFORM_GAMMA1_NBLOCKS ((POLYZ_PACKEDBYTES + STREAM256_BLOCKBYTES - 1)/STREAM256_BLOCKBYTES) +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_gamma1(poly *a, + const uint8_t seed[CRHBYTES], + uint16_t nonce) { + uint8_t buf[POLY_UNIFORM_GAMMA1_NBLOCKS * STREAM256_BLOCKBYTES]; + stream256_state state; + + stream256_init(&state, seed, nonce); + stream256_squeezeblocks(buf, POLY_UNIFORM_GAMMA1_NBLOCKS, &state); + stream256_release(&state); + PQCLEAN_DILITHIUM2_CLEAN_polyz_unpack(a, buf); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_challenge +* +* Description: Implementation of H. Samples polynomial with TAU nonzero +* coefficients in {-1,1} using the output stream of +* SHAKE256(seed). +* +* Arguments: - poly *c: pointer to output polynomial +* - const uint8_t mu[]: byte array containing seed of length SEEDBYTES +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_poly_challenge(poly *c, const uint8_t seed[SEEDBYTES]) { + unsigned int i, b, pos; + uint64_t signs; + uint8_t buf[SHAKE256_RATE]; + shake256incctx state; + + shake256_inc_init(&state); + shake256_inc_absorb(&state, seed, SEEDBYTES); + shake256_inc_finalize(&state); + shake256_inc_squeeze(buf, sizeof buf, &state); + + signs = 0; + for (i = 0; i < 8; ++i) { + signs |= (uint64_t)buf[i] << 8 * i; + } + pos = 8; + + for (i = 0; i < N; ++i) { + c->coeffs[i] = 0; + } + for (i = N - TAU; i < N; ++i) { + do { + if (pos >= SHAKE256_RATE) { + shake256_inc_squeeze(buf, sizeof buf, &state); + pos = 0; + } + + b = buf[pos++]; + } while (b > i); + + c->coeffs[i] = c->coeffs[b]; + c->coeffs[b] = 1 - 2 * (signs & 1); + signs >>= 1; + } + shake256_inc_ctx_release(&state); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyeta_pack +* +* Description: Bit-pack polynomial with coefficients in [-ETA,ETA]. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLYETA_PACKEDBYTES bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyeta_pack(uint8_t *r, const poly *a) { + unsigned int i; + uint8_t t[8]; + DBENCH_START(); + + for (i = 0; i < N / 8; ++i) { + t[0] = (uint8_t) (ETA - a->coeffs[8 * i + 0]); + t[1] = (uint8_t) (ETA - a->coeffs[8 * i + 1]); + t[2] = (uint8_t) (ETA - a->coeffs[8 * i + 2]); + t[3] = (uint8_t) (ETA - a->coeffs[8 * i + 3]); + t[4] = (uint8_t) (ETA - a->coeffs[8 * i + 4]); + t[5] = (uint8_t) (ETA - a->coeffs[8 * i + 5]); + t[6] = (uint8_t) (ETA - a->coeffs[8 * i + 6]); + t[7] = (uint8_t) (ETA - a->coeffs[8 * i + 7]); + + r[3 * i + 0] = (t[0] >> 0) | (t[1] << 3) | (t[2] << 6); + r[3 * i + 1] = (t[2] >> 2) | (t[3] << 1) | (t[4] << 4) | (t[5] << 7); + r[3 * i + 2] = (t[5] >> 1) | (t[6] << 2) | (t[7] << 5); + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyeta_unpack +* +* Description: Unpack polynomial with coefficients in [-ETA,ETA]. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyeta_unpack(poly *r, const uint8_t *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 8; ++i) { + r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7; + r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7; + r->coeffs[8 * i + 2] = ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7; + r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7; + r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7; + r->coeffs[8 * i + 5] = ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7; + r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7; + r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7; + + r->coeffs[8 * i + 0] = ETA - r->coeffs[8 * i + 0]; + r->coeffs[8 * i + 1] = ETA - r->coeffs[8 * i + 1]; + r->coeffs[8 * i + 2] = ETA - r->coeffs[8 * i + 2]; + r->coeffs[8 * i + 3] = ETA - r->coeffs[8 * i + 3]; + r->coeffs[8 * i + 4] = ETA - r->coeffs[8 * i + 4]; + r->coeffs[8 * i + 5] = ETA - r->coeffs[8 * i + 5]; + r->coeffs[8 * i + 6] = ETA - r->coeffs[8 * i + 6]; + r->coeffs[8 * i + 7] = ETA - r->coeffs[8 * i + 7]; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyt1_pack +* +* Description: Bit-pack polynomial t1 with coefficients fitting in 10 bits. +* Input coefficients are assumed to be standard representatives. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLYT1_PACKEDBYTES bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyt1_pack(uint8_t *r, const poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 4; ++i) { + r[5 * i + 0] = (uint8_t) (a->coeffs[4 * i + 0] >> 0); + r[5 * i + 1] = (uint8_t) ((a->coeffs[4 * i + 0] >> 8) | (a->coeffs[4 * i + 1] << 2)); + r[5 * i + 2] = (uint8_t) ((a->coeffs[4 * i + 1] >> 6) | (a->coeffs[4 * i + 2] << 4)); + r[5 * i + 3] = (uint8_t) ((a->coeffs[4 * i + 2] >> 4) | (a->coeffs[4 * i + 3] << 6)); + r[5 * i + 4] = (uint8_t) (a->coeffs[4 * i + 3] >> 2); + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyt1_unpack +* +* Description: Unpack polynomial t1 with 10-bit coefficients. +* Output coefficients are standard representatives. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyt1_unpack(poly *r, const uint8_t *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 4; ++i) { + r->coeffs[4 * i + 0] = ((a[5 * i + 0] >> 0) | ((uint32_t)a[5 * i + 1] << 8)) & 0x3FF; + r->coeffs[4 * i + 1] = ((a[5 * i + 1] >> 2) | ((uint32_t)a[5 * i + 2] << 6)) & 0x3FF; + r->coeffs[4 * i + 2] = ((a[5 * i + 2] >> 4) | ((uint32_t)a[5 * i + 3] << 4)) & 0x3FF; + r->coeffs[4 * i + 3] = ((a[5 * i + 3] >> 6) | ((uint32_t)a[5 * i + 4] << 2)) & 0x3FF; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyt0_pack +* +* Description: Bit-pack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLYT0_PACKEDBYTES bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyt0_pack(uint8_t *r, const poly *a) { + unsigned int i; + uint32_t t[8]; + DBENCH_START(); + + for (i = 0; i < N / 8; ++i) { + t[0] = (1 << (D - 1)) - a->coeffs[8 * i + 0]; + t[1] = (1 << (D - 1)) - a->coeffs[8 * i + 1]; + t[2] = (1 << (D - 1)) - a->coeffs[8 * i + 2]; + t[3] = (1 << (D - 1)) - a->coeffs[8 * i + 3]; + t[4] = (1 << (D - 1)) - a->coeffs[8 * i + 4]; + t[5] = (1 << (D - 1)) - a->coeffs[8 * i + 5]; + t[6] = (1 << (D - 1)) - a->coeffs[8 * i + 6]; + t[7] = (1 << (D - 1)) - a->coeffs[8 * i + 7]; + + r[13 * i + 0] = (uint8_t) t[0]; + r[13 * i + 1] = (uint8_t) (t[0] >> 8); + r[13 * i + 1] |= (uint8_t) (t[1] << 5); + r[13 * i + 2] = (uint8_t) (t[1] >> 3); + r[13 * i + 3] = (uint8_t) (t[1] >> 11); + r[13 * i + 3] |= (uint8_t) (t[2] << 2); + r[13 * i + 4] = (uint8_t) (t[2] >> 6); + r[13 * i + 4] |= (uint8_t) (t[3] << 7); + r[13 * i + 5] = (uint8_t) (t[3] >> 1); + r[13 * i + 6] = (uint8_t) (t[3] >> 9); + r[13 * i + 6] |= (uint8_t) (t[4] << 4); + r[13 * i + 7] = (uint8_t) (t[4] >> 4); + r[13 * i + 8] = (uint8_t) (t[4] >> 12); + r[13 * i + 8] |= (uint8_t) (t[5] << 1); + r[13 * i + 9] = (uint8_t) (t[5] >> 7); + r[13 * i + 9] |= (uint8_t) (t[6] << 6); + r[13 * i + 10] = (uint8_t) (t[6] >> 2); + r[13 * i + 11] = (uint8_t) (t[6] >> 10); + r[13 * i + 11] |= (uint8_t) (t[7] << 3); + r[13 * i + 12] = (uint8_t) (t[7] >> 5); + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyt0_unpack +* +* Description: Unpack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyt0_unpack(poly *r, const uint8_t *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 8; ++i) { + r->coeffs[8 * i + 0] = a[13 * i + 0]; + r->coeffs[8 * i + 0] |= (uint32_t)a[13 * i + 1] << 8; + r->coeffs[8 * i + 0] &= 0x1FFF; + + r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5; + r->coeffs[8 * i + 1] |= (uint32_t)a[13 * i + 2] << 3; + r->coeffs[8 * i + 1] |= (uint32_t)a[13 * i + 3] << 11; + r->coeffs[8 * i + 1] &= 0x1FFF; + + r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2; + r->coeffs[8 * i + 2] |= (uint32_t)a[13 * i + 4] << 6; + r->coeffs[8 * i + 2] &= 0x1FFF; + + r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7; + r->coeffs[8 * i + 3] |= (uint32_t)a[13 * i + 5] << 1; + r->coeffs[8 * i + 3] |= (uint32_t)a[13 * i + 6] << 9; + r->coeffs[8 * i + 3] &= 0x1FFF; + + r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4; + r->coeffs[8 * i + 4] |= (uint32_t)a[13 * i + 7] << 4; + r->coeffs[8 * i + 4] |= (uint32_t)a[13 * i + 8] << 12; + r->coeffs[8 * i + 4] &= 0x1FFF; + + r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1; + r->coeffs[8 * i + 5] |= (uint32_t)a[13 * i + 9] << 7; + r->coeffs[8 * i + 5] &= 0x1FFF; + + r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6; + r->coeffs[8 * i + 6] |= (uint32_t)a[13 * i + 10] << 2; + r->coeffs[8 * i + 6] |= (uint32_t)a[13 * i + 11] << 10; + r->coeffs[8 * i + 6] &= 0x1FFF; + + r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3; + r->coeffs[8 * i + 7] |= (uint32_t)a[13 * i + 12] << 5; + r->coeffs[8 * i + 7] &= 0x1FFF; + + r->coeffs[8 * i + 0] = (1 << (D - 1)) - r->coeffs[8 * i + 0]; + r->coeffs[8 * i + 1] = (1 << (D - 1)) - r->coeffs[8 * i + 1]; + r->coeffs[8 * i + 2] = (1 << (D - 1)) - r->coeffs[8 * i + 2]; + r->coeffs[8 * i + 3] = (1 << (D - 1)) - r->coeffs[8 * i + 3]; + r->coeffs[8 * i + 4] = (1 << (D - 1)) - r->coeffs[8 * i + 4]; + r->coeffs[8 * i + 5] = (1 << (D - 1)) - r->coeffs[8 * i + 5]; + r->coeffs[8 * i + 6] = (1 << (D - 1)) - r->coeffs[8 * i + 6]; + r->coeffs[8 * i + 7] = (1 << (D - 1)) - r->coeffs[8 * i + 7]; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyz_pack +* +* Description: Bit-pack polynomial with coefficients +* in [-(GAMMA1 - 1), GAMMA1]. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLYZ_PACKEDBYTES bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyz_pack(uint8_t *r, const poly *a) { + unsigned int i; + uint32_t t[4]; + DBENCH_START(); + + for (i = 0; i < N / 4; ++i) { + t[0] = GAMMA1 - a->coeffs[4 * i + 0]; + t[1] = GAMMA1 - a->coeffs[4 * i + 1]; + t[2] = GAMMA1 - a->coeffs[4 * i + 2]; + t[3] = GAMMA1 - a->coeffs[4 * i + 3]; + + r[9 * i + 0] = (uint8_t) t[0]; + r[9 * i + 1] = (uint8_t) (t[0] >> 8); + r[9 * i + 2] = (uint8_t) (t[0] >> 16); + r[9 * i + 2] |= (uint8_t) (t[1] << 2); + r[9 * i + 3] = (uint8_t) (t[1] >> 6); + r[9 * i + 4] = (uint8_t) (t[1] >> 14); + r[9 * i + 4] |= (uint8_t) (t[2] << 4); + r[9 * i + 5] = (uint8_t) (t[2] >> 4); + r[9 * i + 6] = (uint8_t) (t[2] >> 12); + r[9 * i + 6] |= (uint8_t) (t[3] << 6); + r[9 * i + 7] = (uint8_t) (t[3] >> 2); + r[9 * i + 8] = (uint8_t) (t[3] >> 10); + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyz_unpack +* +* Description: Unpack polynomial z with coefficients +* in [-(GAMMA1 - 1), GAMMA1]. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyz_unpack(poly *r, const uint8_t *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 4; ++i) { + r->coeffs[4 * i + 0] = a[9 * i + 0]; + r->coeffs[4 * i + 0] |= (uint32_t)a[9 * i + 1] << 8; + r->coeffs[4 * i + 0] |= (uint32_t)a[9 * i + 2] << 16; + r->coeffs[4 * i + 0] &= 0x3FFFF; + + r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2; + r->coeffs[4 * i + 1] |= (uint32_t)a[9 * i + 3] << 6; + r->coeffs[4 * i + 1] |= (uint32_t)a[9 * i + 4] << 14; + r->coeffs[4 * i + 1] &= 0x3FFFF; + + r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4; + r->coeffs[4 * i + 2] |= (uint32_t)a[9 * i + 5] << 4; + r->coeffs[4 * i + 2] |= (uint32_t)a[9 * i + 6] << 12; + r->coeffs[4 * i + 2] &= 0x3FFFF; + + r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6; + r->coeffs[4 * i + 3] |= (uint32_t)a[9 * i + 7] << 2; + r->coeffs[4 * i + 3] |= (uint32_t)a[9 * i + 8] << 10; + r->coeffs[4 * i + 3] &= 0x3FFFF; + + r->coeffs[4 * i + 0] = GAMMA1 - r->coeffs[4 * i + 0]; + r->coeffs[4 * i + 1] = GAMMA1 - r->coeffs[4 * i + 1]; + r->coeffs[4 * i + 2] = GAMMA1 - r->coeffs[4 * i + 2]; + r->coeffs[4 * i + 3] = GAMMA1 - r->coeffs[4 * i + 3]; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyw1_pack +* +* Description: Bit-pack polynomial w1 with coefficients in [0,15] or [0,43]. +* Input coefficients are assumed to be standard representatives. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLYW1_PACKEDBYTES bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyw1_pack(uint8_t *r, const poly *a) { + unsigned int i; + DBENCH_START(); + + for (i = 0; i < N / 4; ++i) { + r[3 * i + 0] = (uint8_t) a->coeffs[4 * i + 0]; + r[3 * i + 0] |= (uint8_t) (a->coeffs[4 * i + 1] << 6); + r[3 * i + 1] = (uint8_t) (a->coeffs[4 * i + 1] >> 2); + r[3 * i + 1] |= (uint8_t) (a->coeffs[4 * i + 2] << 4); + r[3 * i + 2] = (uint8_t) (a->coeffs[4 * i + 2] >> 4); + r[3 * i + 2] |= (uint8_t) (a->coeffs[4 * i + 3] << 2); + } + + DBENCH_STOP(*tpack); +} diff --git a/dilithium2/poly.h b/dilithium2/poly.h new file mode 100644 index 0000000..0128b3c --- /dev/null +++ b/dilithium2/poly.h @@ -0,0 +1,52 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_POLY_H +#define PQCLEAN_DILITHIUM2_CLEAN_POLY_H +#include "params.h" +#include + +typedef struct { + int32_t coeffs[N]; +} poly; + +void PQCLEAN_DILITHIUM2_CLEAN_poly_reduce(poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_poly_caddq(poly *a); + +void PQCLEAN_DILITHIUM2_CLEAN_poly_add(poly *c, const poly *a, const poly *b); +void PQCLEAN_DILITHIUM2_CLEAN_poly_sub(poly *c, const poly *a, const poly *b); +void PQCLEAN_DILITHIUM2_CLEAN_poly_shiftl(poly *a); + +void PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_poly_invntt_tomont(poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(poly *c, const poly *a, const poly *b); + +void PQCLEAN_DILITHIUM2_CLEAN_poly_power2round(poly *a1, poly *a0, const poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_poly_decompose(poly *a1, poly *a0, const poly *a); +unsigned int PQCLEAN_DILITHIUM2_CLEAN_poly_make_hint(poly *h, const poly *a0, const poly *a1); +void PQCLEAN_DILITHIUM2_CLEAN_poly_use_hint(poly *b, const poly *a, const poly *h); + +int PQCLEAN_DILITHIUM2_CLEAN_poly_chknorm(const poly *a, int32_t B); +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform(poly *a, + const uint8_t seed[SEEDBYTES], + uint16_t nonce); +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_eta(poly *a, + const uint8_t seed[CRHBYTES], + uint16_t nonce); +void PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_gamma1(poly *a, + const uint8_t seed[CRHBYTES], + uint16_t nonce); +void PQCLEAN_DILITHIUM2_CLEAN_poly_challenge(poly *c, const uint8_t seed[SEEDBYTES]); + +void PQCLEAN_DILITHIUM2_CLEAN_polyeta_pack(uint8_t *r, const poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_polyeta_unpack(poly *r, const uint8_t *a); + +void PQCLEAN_DILITHIUM2_CLEAN_polyt1_pack(uint8_t *r, const poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_polyt1_unpack(poly *r, const uint8_t *a); + +void PQCLEAN_DILITHIUM2_CLEAN_polyt0_pack(uint8_t *r, const poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_polyt0_unpack(poly *r, const uint8_t *a); + +void PQCLEAN_DILITHIUM2_CLEAN_polyz_pack(uint8_t *r, const poly *a); +void PQCLEAN_DILITHIUM2_CLEAN_polyz_unpack(poly *r, const uint8_t *a); + +void PQCLEAN_DILITHIUM2_CLEAN_polyw1_pack(uint8_t *r, const poly *a); + +#endif diff --git a/dilithium2/polyvec.c b/dilithium2/polyvec.c new file mode 100644 index 0000000..6879cdf --- /dev/null +++ b/dilithium2/polyvec.c @@ -0,0 +1,414 @@ +#include "params.h" +#include "poly.h" +#include "polyvec.h" +#include + +/************************************************* +* Name: expand_mat +* +* Description: Implementation of ExpandA. Generates matrix A with uniformly +* random coefficients a_{i,j} by performing rejection +* sampling on the output stream of SHAKE128(rho|j|i) +* +* Arguments: - polyvecl mat[K]: output matrix +* - const uint8_t rho[]: byte array containing seed rho +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_expand(polyvecl mat[K], const uint8_t rho[SEEDBYTES]) { + unsigned int i, j; + + for (i = 0; i < K; ++i) { + for (j = 0; j < L; ++j) { + PQCLEAN_DILITHIUM2_CLEAN_poly_uniform(&mat[i].vec[j], rho, (uint16_t) ((i << 8) + j)); + } + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_pointwise_montgomery(polyveck *t, const polyvecl mat[K], const polyvecl *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_acc_montgomery(&t->vec[i], &mat[i], v); + } +} + +/**************************************************************/ +/************ Vectors of polynomials of length L **************/ +/**************************************************************/ + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_eta(&v->vec[i], seed, nonce++); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_gamma1(&v->vec[i], seed, (uint16_t) (L * nonce + i)); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_reduce(polyvecl *v) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_reduce(&v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyvecl_add +* +* Description: Add vectors of polynomials of length L. +* No modular reduction is performed. +* +* Arguments: - polyvecl *w: pointer to output vector +* - const polyvecl *u: pointer to first summand +* - const polyvecl *v: pointer to second summand +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_add(&w->vec[i], &u->vec[i], &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt +* +* Description: Forward NTT of all polynomials in vector of length L. Output +* coefficients can be up to 16*Q larger than input coefficients. +* +* Arguments: - polyvecl *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(polyvecl *v) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(&v->vec[i]); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_invntt_tomont(polyvecl *v) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_invntt_tomont(&v->vec[i]); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_poly_montgomery(polyvecl *r, const poly *a, const polyvecl *v) { + unsigned int i; + + for (i = 0; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_acc_montgomery +* +* Description: Pointwise multiply vectors of polynomials of length L, multiply +* resulting vector by 2^{-32} and add (accumulate) polynomials +* in it. Input/output vectors are in NTT domain representation. +* +* Arguments: - poly *w: output polynomial +* - const polyvecl *u: pointer to first input vector +* - const polyvecl *v: pointer to second input vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_acc_montgomery(poly *w, + const polyvecl *u, + const polyvecl *v) { + unsigned int i; + poly t; + + PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(w, &u->vec[0], &v->vec[0]); + for (i = 1; i < L; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(&t, &u->vec[i], &v->vec[i]); + PQCLEAN_DILITHIUM2_CLEAN_poly_add(w, w, &t); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyvecl_chknorm +* +* Description: Check infinity norm of polynomials in vector of length L. +* Assumes input polyvecl to be reduced by PQCLEAN_DILITHIUM2_CLEAN_polyvecl_reduce(). +* +* Arguments: - const polyvecl *v: pointer to vector +* - int32_t B: norm bound +* +* Returns 0 if norm of all polynomials is strictly smaller than B <= (Q-1)/8 +* and 1 otherwise. +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_polyvecl_chknorm(const polyvecl *v, int32_t bound) { + unsigned int i; + + for (i = 0; i < L; ++i) { + if (PQCLEAN_DILITHIUM2_CLEAN_poly_chknorm(&v->vec[i], bound)) { + return 1; + } + } + + return 0; +} + +/**************************************************************/ +/************ Vectors of polynomials of length K **************/ +/**************************************************************/ + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_uniform_eta(polyveck *v, const uint8_t seed[CRHBYTES], uint16_t nonce) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_uniform_eta(&v->vec[i], seed, nonce++); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce +* +* Description: Reduce coefficients of polynomials in vector of length K +* to representatives in [-6283009,6283007]. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_reduce(&v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq +* +* Description: For all coefficients of polynomials in vector of length K +* add Q if coefficient is negative. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq(polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_caddq(&v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_add +* +* Description: Add vectors of polynomials of length K. +* No modular reduction is performed. +* +* Arguments: - polyveck *w: pointer to output vector +* - const polyveck *u: pointer to first summand +* - const polyveck *v: pointer to second summand +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_add(polyveck *w, const polyveck *u, const polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_add(&w->vec[i], &u->vec[i], &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_sub +* +* Description: Subtract vectors of polynomials of length K. +* No modular reduction is performed. +* +* Arguments: - polyveck *w: pointer to output vector +* - const polyveck *u: pointer to first input vector +* - const polyveck *v: pointer to second input vector to be +* subtracted from first input vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_sub(&w->vec[i], &u->vec[i], &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_shiftl +* +* Description: Multiply vector of polynomials of Length K by 2^D without modular +* reduction. Assumes input coefficients to be less than 2^{31-D}. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_shiftl(polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_shiftl(&v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt +* +* Description: Forward NTT of all polynomials in vector of length K. Output +* coefficients can be up to 16*Q larger than input coefficients. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt(polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(&v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont +* +* Description: Inverse NTT and multiplication by 2^{32} of polynomials +* in vector of length K. Input coefficients need to be less +* than 2*Q. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_invntt_tomont(&v->vec[i]); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_pointwise_poly_montgomery(polyveck *r, const poly *a, const polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_chknorm +* +* Description: Check infinity norm of polynomials in vector of length K. +* Assumes input polyveck to be reduced by PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(). +* +* Arguments: - const polyveck *v: pointer to vector +* - int32_t B: norm bound +* +* Returns 0 if norm of all polynomials are strictly smaller than B <= (Q-1)/8 +* and 1 otherwise. +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_polyveck_chknorm(const polyveck *v, int32_t bound) { + unsigned int i; + + for (i = 0; i < K; ++i) { + if (PQCLEAN_DILITHIUM2_CLEAN_poly_chknorm(&v->vec[i], bound)) { + return 1; + } + } + + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_power2round +* +* Description: For all coefficients a of polynomials in vector of length K, +* compute a0, a1 such that a mod^+ Q = a1*2^D + a0 +* with -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients to be +* standard representatives. +* +* Arguments: - polyveck *v1: pointer to output vector of polynomials with +* coefficients a1 +* - polyveck *v0: pointer to output vector of polynomials with +* coefficients a0 +* - const polyveck *v: pointer to input vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_power2round(&v1->vec[i], &v0->vec[i], &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_decompose +* +* Description: For all coefficients a of polynomials in vector of length K, +* compute high and low bits a0, a1 such a mod^+ Q = a1*ALPHA + a0 +* with -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA where we +* set a1 = 0 and -ALPHA/2 <= a0 = a mod Q - Q < 0. +* Assumes coefficients to be standard representatives. +* +* Arguments: - polyveck *v1: pointer to output vector of polynomials with +* coefficients a1 +* - polyveck *v0: pointer to output vector of polynomials with +* coefficients a0 +* - const polyveck *v: pointer to input vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_decompose(&v1->vec[i], &v0->vec[i], &v->vec[i]); + } +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_make_hint +* +* Description: Compute hint vector. +* +* Arguments: - polyveck *h: pointer to output vector +* - const polyveck *v0: pointer to low part of input vector +* - const polyveck *v1: pointer to high part of input vector +* +* Returns number of 1 bits. +**************************************************/ +unsigned int PQCLEAN_DILITHIUM2_CLEAN_polyveck_make_hint(polyveck *h, + const polyveck *v0, + const polyveck *v1) { + unsigned int i, s = 0; + + for (i = 0; i < K; ++i) { + s += PQCLEAN_DILITHIUM2_CLEAN_poly_make_hint(&h->vec[i], &v0->vec[i], &v1->vec[i]); + } + + return s; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_polyveck_use_hint +* +* Description: Use hint vector to correct the high bits of input vector. +* +* Arguments: - polyveck *w: pointer to output vector of polynomials with +* corrected high bits +* - const polyveck *u: pointer to input vector +* - const polyveck *h: pointer to input hint vector +**************************************************/ +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_use_hint(polyveck *w, const polyveck *u, const polyveck *h) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_poly_use_hint(&w->vec[i], &u->vec[i], &h->vec[i]); + } +} + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_pack_w1(uint8_t r[K * POLYW1_PACKEDBYTES], const polyveck *w1) { + unsigned int i; + + for (i = 0; i < K; ++i) { + PQCLEAN_DILITHIUM2_CLEAN_polyw1_pack(&r[i * POLYW1_PACKEDBYTES], &w1->vec[i]); + } +} diff --git a/dilithium2/polyvec.h b/dilithium2/polyvec.h new file mode 100644 index 0000000..6e3cbd7 --- /dev/null +++ b/dilithium2/polyvec.h @@ -0,0 +1,62 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_POLYVEC_H +#define PQCLEAN_DILITHIUM2_CLEAN_POLYVEC_H +#include "params.h" +#include "poly.h" +#include + +/* Vectors of polynomials of length L */ +typedef struct { + poly vec[L]; +} polyvecl; + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[CRHBYTES], uint16_t nonce); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_reduce(polyvecl *v); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(polyvecl *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_invntt_tomont(polyvecl *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_poly_montgomery(polyvecl *r, const poly *a, const polyvecl *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_acc_montgomery(poly *w, + const polyvecl *u, + const polyvecl *v); + +int PQCLEAN_DILITHIUM2_CLEAN_polyvecl_chknorm(const polyvecl *v, int32_t B); + +/* Vectors of polynomials of length K */ +typedef struct { + poly vec[K]; +} polyveck; + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_uniform_eta(polyveck *v, const uint8_t seed[CRHBYTES], uint16_t nonce); + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq(polyveck *v); + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_add(polyveck *w, const polyveck *u, const polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_shiftl(polyveck *v); + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt(polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_pointwise_poly_montgomery(polyveck *r, const poly *a, const polyveck *v); + +int PQCLEAN_DILITHIUM2_CLEAN_polyveck_chknorm(const polyveck *v, int32_t B); + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v); +unsigned int PQCLEAN_DILITHIUM2_CLEAN_polyveck_make_hint(polyveck *h, + const polyveck *v0, + const polyveck *v1); +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_use_hint(polyveck *w, const polyveck *u, const polyveck *h); + +void PQCLEAN_DILITHIUM2_CLEAN_polyveck_pack_w1(uint8_t r[K * POLYW1_PACKEDBYTES], const polyveck *w1); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_expand(polyvecl mat[K], const uint8_t rho[SEEDBYTES]); + +void PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_pointwise_montgomery(polyveck *t, const polyvecl mat[K], const polyvecl *v); + +#endif diff --git a/dilithium2/reduce.c b/dilithium2/reduce.c new file mode 100644 index 0000000..4d3946f --- /dev/null +++ b/dilithium2/reduce.c @@ -0,0 +1,69 @@ +#include "params.h" +#include "reduce.h" +#include + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce +* +* Description: For finite field element a with -2^{31}Q <= a <= Q*2^31, +* compute r \equiv a*2^{-32} (mod Q) such that -Q < r < Q. +* +* Arguments: - int64_t: finite field element a +* +* Returns r. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce(int64_t a) { + int32_t t; + + t = (int32_t)((uint64_t)a * (uint64_t)QINV); + t = (a - (int64_t)t * Q) >> 32; + return t; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_reduce32 +* +* Description: For finite field element a with a <= 2^{31} - 2^{22} - 1, +* compute r \equiv a (mod Q) such that -6283009 <= r <= 6283007. +* +* Arguments: - int32_t: finite field element a +* +* Returns r. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_reduce32(int32_t a) { + int32_t t; + + t = (a + (1 << 22)) >> 23; + t = a - t * Q; + return t; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_caddq +* +* Description: Add Q if input coefficient is negative. +* +* Arguments: - int32_t: finite field element a +* +* Returns r. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_caddq(int32_t a) { + a += (a >> 31) & Q; + return a; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_freeze +* +* Description: For finite field element a, compute standard +* representative r = a mod^+ Q. +* +* Arguments: - int32_t: finite field element a +* +* Returns r. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_freeze(int32_t a) { + a = PQCLEAN_DILITHIUM2_CLEAN_reduce32(a); + a = PQCLEAN_DILITHIUM2_CLEAN_caddq(a); + return a; +} diff --git a/dilithium2/reduce.h b/dilithium2/reduce.h new file mode 100644 index 0000000..41cd3a3 --- /dev/null +++ b/dilithium2/reduce.h @@ -0,0 +1,17 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_REDUCE_H +#define PQCLEAN_DILITHIUM2_CLEAN_REDUCE_H +#include "params.h" +#include + +#define MONT (-4186625) // 2^32 % Q +#define QINV 58728449 // q^(-1) mod 2^32 + +int32_t PQCLEAN_DILITHIUM2_CLEAN_montgomery_reduce(int64_t a); + +int32_t PQCLEAN_DILITHIUM2_CLEAN_reduce32(int32_t a); + +int32_t PQCLEAN_DILITHIUM2_CLEAN_caddq(int32_t a); + +int32_t PQCLEAN_DILITHIUM2_CLEAN_freeze(int32_t a); + +#endif diff --git a/dilithium2/rounding.c b/dilithium2/rounding.c new file mode 100644 index 0000000..9b49fb2 --- /dev/null +++ b/dilithium2/rounding.c @@ -0,0 +1,98 @@ +#include "params.h" +#include "rounding.h" +#include + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_power2round +* +* Description: For finite field element a, compute a0, a1 such that +* a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}. +* Assumes a to be standard representative. +* +* Arguments: - int32_t a: input element +* - int32_t *a0: pointer to output element a0 +* +* Returns a1. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_power2round(int32_t *a0, int32_t a) { + int32_t a1; + + a1 = (a + (1 << (D - 1)) - 1) >> D; + *a0 = a - (a1 << D); + return a1; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_decompose +* +* Description: For finite field element a, compute high and low bits a0, a1 such +* that a mod^+ Q = a1*ALPHA + a0 with -ALPHA/2 < a0 <= ALPHA/2 except +* if a1 = (Q-1)/ALPHA where we set a1 = 0 and +* -ALPHA/2 <= a0 = a mod^+ Q - Q < 0. Assumes a to be standard +* representative. +* +* Arguments: - int32_t a: input element +* - int32_t *a0: pointer to output element a0 +* +* Returns a1. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_decompose(int32_t *a0, int32_t a) { + int32_t a1; + + a1 = (a + 127) >> 7; + a1 = (a1 * 11275 + (1 << 23)) >> 24; + a1 ^= ((43 - a1) >> 31) & a1; + + *a0 = a - a1 * 2 * GAMMA2; + *a0 -= (((Q - 1) / 2 - *a0) >> 31) & Q; + return a1; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_make_hint +* +* Description: Compute hint bit indicating whether the low bits of the +* input element overflow into the high bits. +* +* Arguments: - int32_t a0: low bits of input element +* - int32_t a1: high bits of input element +* +* Returns 1 if overflow. +**************************************************/ +unsigned int PQCLEAN_DILITHIUM2_CLEAN_make_hint(int32_t a0, int32_t a1) { + if (a0 > GAMMA2 || a0 < -GAMMA2 || (a0 == -GAMMA2 && a1 != 0)) { + return 1; + } + + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_use_hint +* +* Description: Correct high bits according to hint. +* +* Arguments: - int32_t a: input element +* - unsigned int hint: hint bit +* +* Returns corrected high bits. +**************************************************/ +int32_t PQCLEAN_DILITHIUM2_CLEAN_use_hint(int32_t a, unsigned int hint) { + int32_t a0, a1; + + a1 = PQCLEAN_DILITHIUM2_CLEAN_decompose(&a0, a); + if (hint == 0) { + return a1; + } + + if (a0 > 0) { + if (a1 == 43) { + return 0; + } + return a1 + 1; + } + if (a1 == 0) { + return 43; + } + return a1 - 1; +} diff --git a/dilithium2/rounding.h b/dilithium2/rounding.h new file mode 100644 index 0000000..8542a00 --- /dev/null +++ b/dilithium2/rounding.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_ROUNDING_H +#define PQCLEAN_DILITHIUM2_CLEAN_ROUNDING_H +#include "params.h" +#include + +int32_t PQCLEAN_DILITHIUM2_CLEAN_power2round(int32_t *a0, int32_t a); + +int32_t PQCLEAN_DILITHIUM2_CLEAN_decompose(int32_t *a0, int32_t a); + +unsigned int PQCLEAN_DILITHIUM2_CLEAN_make_hint(int32_t a0, int32_t a1); + +int32_t PQCLEAN_DILITHIUM2_CLEAN_use_hint(int32_t a, unsigned int hint); + +#endif diff --git a/dilithium2/sign.c b/dilithium2/sign.c new file mode 100644 index 0000000..50c8dd4 --- /dev/null +++ b/dilithium2/sign.c @@ -0,0 +1,365 @@ +#include "../common/fips202.h" +#include "packing.h" +#include "params.h" +#include "poly.h" +#include "polyvec.h" +#include "../random/randombytes.h" +#include "sign.h" +#include "symmetric.h" +#include + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair +* +* Description: Generates public and private key. +* +* Arguments: - uint8_t *pk: pointer to output public key (allocated +* array of PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES bytes) +* - uint8_t *sk: pointer to output private key (allocated +* array of PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES bytes) +* - uint8_t seed: the seed randombytes value +* Returns 0 (success) +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(uint8_t *pk, uint8_t *sk, const uint8_t* seed) { + uint8_t seedbuf[2 * SEEDBYTES + CRHBYTES]; + uint8_t tr[TRBYTES]; + const uint8_t *rho, *rhoprime, *key; + polyvecl mat[K]; + polyvecl s1, s1hat; + polyveck s2, t1, t0; + + /* Get randomness for rho, rhoprime and key */ + for (int i = 0; i < SEEDBYTES; i++) { + seedbuf[i] = seed[i]; + } + + shake256(seedbuf, 2 * SEEDBYTES + CRHBYTES, seedbuf, SEEDBYTES); + rho = seedbuf; + rhoprime = rho + SEEDBYTES; + key = rhoprime + CRHBYTES; + + /* Expand matrix */ + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_expand(mat, rho); + + /* Sample short vectors s1 and s2 */ + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_eta(&s1, rhoprime, 0); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_uniform_eta(&s2, rhoprime, L); + + /* Matrix-vector multiplication */ + s1hat = s1; + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(&s1hat); + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_pointwise_montgomery(&t1, mat, &s1hat); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(&t1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(&t1); + + /* Add error vector s2 */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_add(&t1, &t1, &s2); + + /* Extract t1 and write public key */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq(&t1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_power2round(&t1, &t0, &t1); + PQCLEAN_DILITHIUM2_CLEAN_pack_pk(pk, rho, &t1); + + /* Compute H(rho, t1) and write secret key */ + shake256(tr, TRBYTES, pk, PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES); + PQCLEAN_DILITHIUM2_CLEAN_pack_sk(sk, rho, tr, key, &t0, &s1, &s2); + + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair +* +* Description: Generates public and private key. +* +* Arguments: - uint8_t *pk: pointer to output public key (allocated +* array of PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES bytes) +* - uint8_t *sk: pointer to output private key (allocated +* array of PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_SECRETKEYBYTES bytes) +* +* Returns 0 (success) +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair(uint8_t* pk, uint8_t* sk) { + uint8_t seed[SEEDBYTES]; + + /* + * Generate Seed + */ + if (randombytes(seed, sizeof seed) != 0) { + return -1; + } + + return PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk, sk, seed); +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature +* +* Description: Computes signature. +* +* Arguments: - uint8_t *sig: pointer to output signature (of length PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES) +* - size_t *siglen: pointer to output length of signature +* - uint8_t *m: pointer to message to be signed +* - size_t mlen: length of message +* - uint8_t *sk: pointer to bit-packed secret key +* +* Returns 0 (success) +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(uint8_t *sig, + size_t *siglen, + const uint8_t *m, + size_t mlen, + const uint8_t *sk) { + unsigned int n; + uint8_t seedbuf[2 * SEEDBYTES + TRBYTES + RNDBYTES + 2 * CRHBYTES]; + uint8_t *rho, *tr, *key, *mu, *rhoprime, *rnd; + uint16_t nonce = 0; + polyvecl mat[K], s1, y, z; + polyveck t0, s2, w1, w0, h; + poly cp; + shake256incctx state; + + rho = seedbuf; + tr = rho + SEEDBYTES; + key = tr + TRBYTES; + rnd = key + SEEDBYTES; + mu = rnd + RNDBYTES; + rhoprime = mu + CRHBYTES; + PQCLEAN_DILITHIUM2_CLEAN_unpack_sk(rho, tr, key, &t0, &s1, &s2, sk); + + /* Compute mu = CRH(tr, msg) */ + shake256_inc_init(&state); + shake256_inc_absorb(&state, tr, TRBYTES); + shake256_inc_absorb(&state, m, mlen); + shake256_inc_finalize(&state); + shake256_inc_squeeze(mu, CRHBYTES, &state); + shake256_inc_ctx_release(&state); + + for (n = 0; n < RNDBYTES; n++) { + rnd[n] = 0; + } + shake256(rhoprime, CRHBYTES, key, SEEDBYTES + RNDBYTES + CRHBYTES); + + /* Expand matrix and transform vectors */ + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_expand(mat, rho); + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(&s1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt(&s2); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt(&t0); + +rej: + /* Sample intermediate vector y */ + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_uniform_gamma1(&y, rhoprime, nonce++); + + /* Matrix-vector multiplication */ + z = y; + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(&z); + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(&w1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(&w1); + + /* Decompose w and call the random oracle */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq(&w1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_decompose(&w1, &w0, &w1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_pack_w1(sig, &w1); + + shake256_inc_init(&state); + shake256_inc_absorb(&state, mu, CRHBYTES); + shake256_inc_absorb(&state, sig, K * POLYW1_PACKEDBYTES); + shake256_inc_finalize(&state); + shake256_inc_squeeze(sig, CTILDEBYTES, &state); + shake256_inc_ctx_release(&state); + PQCLEAN_DILITHIUM2_CLEAN_poly_challenge(&cp, sig); /* uses only the first SEEDBYTES bytes of sig */ + PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(&cp); + + /* Compute z, reject if it reveals secret */ + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_pointwise_poly_montgomery(&z, &cp, &s1); + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_invntt_tomont(&z); + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_add(&z, &z, &y); + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_reduce(&z); + if (PQCLEAN_DILITHIUM2_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) { + goto rej; + } + + /* Check that subtracting cs2 does not change high bits of w and low bits + * do not reveal secret information */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &s2); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(&h); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_sub(&w0, &w0, &h); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(&w0); + if (PQCLEAN_DILITHIUM2_CLEAN_polyveck_chknorm(&w0, GAMMA2 - BETA)) { + goto rej; + } + + /* Compute hints for w1 */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &t0); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(&h); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(&h); + if (PQCLEAN_DILITHIUM2_CLEAN_polyveck_chknorm(&h, GAMMA2)) { + goto rej; + } + + PQCLEAN_DILITHIUM2_CLEAN_polyveck_add(&w0, &w0, &h); + n = PQCLEAN_DILITHIUM2_CLEAN_polyveck_make_hint(&h, &w0, &w1); + if (n > OMEGA) { + goto rej; + } + + /* Write signature */ + PQCLEAN_DILITHIUM2_CLEAN_pack_sig(sig, sig, &z, &h); + *siglen = PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES; + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign +* +* Description: Compute signed message. +* +* Arguments: - uint8_t *sm: pointer to output signed message (allocated +* array with PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES + mlen bytes), +* can be equal to m +* - size_t *smlen: pointer to output length of signed +* message +* - const uint8_t *m: pointer to message to be signed +* - size_t mlen: length of message +* - const uint8_t *sk: pointer to bit-packed secret key +* +* Returns 0 (success) +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(uint8_t *sm, + size_t *smlen, + const uint8_t *m, + size_t mlen, + const uint8_t *sk) { + size_t i; + + for (i = 0; i < mlen; ++i) { + sm[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES + mlen - 1 - i] = m[mlen - 1 - i]; + } + PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(sm, smlen, sm + PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES, mlen, sk); + *smlen += mlen; + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify +* +* Description: Verifies signature. +* +* Arguments: - uint8_t *m: pointer to input signature +* - size_t siglen: length of signature +* - const uint8_t *m: pointer to message +* - size_t mlen: length of message +* - const uint8_t *pk: pointer to bit-packed public key +* +* Returns 0 if signature could be verified correctly and -1 otherwise +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(const uint8_t *sig, + size_t siglen, + const uint8_t *m, + size_t mlen, + const uint8_t *pk) { + unsigned int i; + uint8_t buf[K * POLYW1_PACKEDBYTES]; + uint8_t rho[SEEDBYTES]; + uint8_t mu[CRHBYTES]; + uint8_t c[CTILDEBYTES]; + uint8_t c2[CTILDEBYTES]; + poly cp; + polyvecl mat[K], z; + polyveck t1, w1, h; + shake256incctx state; + + if (siglen != PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES) { + return -1; + } + + PQCLEAN_DILITHIUM2_CLEAN_unpack_pk(rho, &t1, pk); + if (PQCLEAN_DILITHIUM2_CLEAN_unpack_sig(c, &z, &h, sig)) { + return -1; + } + if (PQCLEAN_DILITHIUM2_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) { + return -1; + } + + /* Compute CRH(H(rho, t1), msg) */ + shake256(mu, CRHBYTES, pk, PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_PUBLICKEYBYTES); + shake256_inc_init(&state); + shake256_inc_absorb(&state, mu, CRHBYTES); + shake256_inc_absorb(&state, m, mlen); + shake256_inc_finalize(&state); + shake256_inc_squeeze(mu, CRHBYTES, &state); + shake256_inc_ctx_release(&state); + + /* Matrix-vector multiplication; compute Az - c2^dt1 */ + PQCLEAN_DILITHIUM2_CLEAN_poly_challenge(&cp, c); /* uses only the first SEEDBYTES bytes of c */ + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_expand(mat, rho); + + PQCLEAN_DILITHIUM2_CLEAN_polyvecl_ntt(&z); + PQCLEAN_DILITHIUM2_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z); + + PQCLEAN_DILITHIUM2_CLEAN_poly_ntt(&cp); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_shiftl(&t1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_ntt(&t1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_pointwise_poly_montgomery(&t1, &cp, &t1); + + PQCLEAN_DILITHIUM2_CLEAN_polyveck_sub(&w1, &w1, &t1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_reduce(&w1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_invntt_tomont(&w1); + + /* Reconstruct w1 */ + PQCLEAN_DILITHIUM2_CLEAN_polyveck_caddq(&w1); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_use_hint(&w1, &w1, &h); + PQCLEAN_DILITHIUM2_CLEAN_polyveck_pack_w1(buf, &w1); + + /* Call random oracle and verify PQCLEAN_DILITHIUM2_CLEAN_challenge */ + shake256_inc_init(&state); + shake256_inc_absorb(&state, mu, CRHBYTES); + shake256_inc_absorb(&state, buf, K * POLYW1_PACKEDBYTES); + shake256_inc_finalize(&state); + shake256_inc_squeeze(c2, CTILDEBYTES, &state); + shake256_inc_ctx_release(&state); + for (i = 0; i < CTILDEBYTES; ++i) { + if (c[i] != c2[i]) { + return -1; + } + } + return 0; +} + +/************************************************* +* Name: PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open +* +* Description: Verify signed message. +* +* Arguments: - uint8_t *m: pointer to output message (allocated +* array with smlen bytes), can be equal to sm +* - size_t *mlen: pointer to output length of message +* - const uint8_t *sm: pointer to signed message +* - size_t smlen: length of signed message +* - const uint8_t *pk: pointer to bit-packed public key +* +* Returns 0 if signed message could be verified correctly and -1 otherwise +**************************************************/ +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(uint8_t *m, + size_t *mlen, + const uint8_t *sm, + size_t smlen, + const uint8_t *pk) { + size_t i; + + if (smlen < PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES) { + return -1; + } + + *mlen = smlen - PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES; + if (PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(sm, PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES, sm + PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES, *mlen, pk)) { + return -2; + } else { + /* All good, copy msg, return 0 */ + for (i = 0; i < *mlen; ++i) { + m[i] = sm[PQCLEAN_DILITHIUM2_CLEAN_CRYPTO_BYTES + i]; + } + return 0; + } +} diff --git a/dilithium2/sign.h b/dilithium2/sign.h new file mode 100644 index 0000000..e843d48 --- /dev/null +++ b/dilithium2/sign.h @@ -0,0 +1,27 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_SIGN_H +#define PQCLEAN_DILITHIUM2_CLEAN_SIGN_H +#include "params.h" +#include "poly.h" +#include "polyvec.h" +#include +#include + +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, + const uint8_t *pk); + +int PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, + const uint8_t *pk); + +#endif diff --git a/dilithium2/symmetric-shake.c b/dilithium2/symmetric-shake.c new file mode 100644 index 0000000..30c00af --- /dev/null +++ b/dilithium2/symmetric-shake.c @@ -0,0 +1,26 @@ +#include "../common/fips202.h" +#include "params.h" +#include "symmetric.h" +#include + +void PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake128_stream_init(shake128incctx *state, const uint8_t seed[SEEDBYTES], uint16_t nonce) { + uint8_t t[2]; + t[0] = (uint8_t) nonce; + t[1] = (uint8_t) (nonce >> 8); + + shake128_inc_init(state); + shake128_inc_absorb(state, seed, SEEDBYTES); + shake128_inc_absorb(state, t, 2); + shake128_inc_finalize(state); +} + +void PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake256_stream_init(shake256incctx *state, const uint8_t seed[CRHBYTES], uint16_t nonce) { + uint8_t t[2]; + t[0] = (uint8_t) nonce; + t[1] = (uint8_t) (nonce >> 8); + + shake256_inc_init(state); + shake256_inc_absorb(state, seed, CRHBYTES); + shake256_inc_absorb(state, t, 2); + shake256_inc_finalize(state); +} diff --git a/dilithium2/symmetric.h b/dilithium2/symmetric.h new file mode 100644 index 0000000..8b9d722 --- /dev/null +++ b/dilithium2/symmetric.h @@ -0,0 +1,33 @@ +#ifndef PQCLEAN_DILITHIUM2_CLEAN_SYMMETRIC_H +#define PQCLEAN_DILITHIUM2_CLEAN_SYMMETRIC_H +#include "../common/fips202.h" +#include "params.h" +#include + +typedef shake128incctx stream128_state; +typedef shake256incctx stream256_state; + +void PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake128_stream_init(shake128incctx *state, + const uint8_t seed[SEEDBYTES], + uint16_t nonce); + +void PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake256_stream_init(shake256incctx *state, + const uint8_t seed[CRHBYTES], + uint16_t nonce); + +#define STREAM128_BLOCKBYTES SHAKE128_RATE +#define STREAM256_BLOCKBYTES SHAKE256_RATE + +#define stream128_init(STATE, SEED, NONCE) \ + PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake128_stream_init(STATE, SEED, NONCE) +#define stream128_squeezeblocks(OUT, OUTBLOCKS, STATE) \ + shake128_inc_squeeze(OUT, (OUTBLOCKS)*(SHAKE128_RATE), STATE) +#define stream128_release(STATE) shake128_inc_ctx_release(STATE) + +#define stream256_init(STATE, SEED, NONCE) \ + PQCLEAN_DILITHIUM2_CLEAN_dilithium_shake256_stream_init(STATE, SEED, NONCE) +#define stream256_squeezeblocks(OUT, OUTBLOCKS, STATE) \ + shake256_inc_squeeze(OUT, (OUTBLOCKS)*(SHAKE256_RATE), STATE) +#define stream256_release(STATE) shake256_inc_ctx_release(STATE) + +#endif diff --git a/hybrid-dilithium/hybrid.c b/hybrid-dilithium/hybrid.c new file mode 100644 index 0000000..3c20b02 --- /dev/null +++ b/hybrid-dilithium/hybrid.c @@ -0,0 +1,378 @@ +/* + * Hybrid Post Quantum Cryptography Library + * + * ==========================(LICENSE BEGIN)============================ + +The MIT License + +Copyright (c) 2023 Doge Protocol Community + +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. + + * ===========================(LICENSE END)============================= + * + * WARNING! This is an experimental cryptography library. It is not ready for use yet in any production systems!! + * + * + */ + +#include +#include +#include +#include +#include "hybrid.h" +#include "../dilithium2/api.h" +#include "../tweetnacl/tweetnacl.h" +#include "../random/randombytes.h" + +const int SEED_LENGTH_ED25519_2 = 32; +const int SEED_LENGTH_DILITHIUM = 32; + +const int MIN_MSG_LEN_2 = 1; +const int MAX_MSG_LEN_2 = 64; + +const int CRYPTO_ED25519_PUBLICKEY_BYTES_2 = 32; +const int CRYPTO_ED25519_SECRETKEY_BYTES_2 = 64; +const int CRYPTO_ED25519_SIGNATURE_BYTES_2 = 64; + +const int LEN_BYTES_2 = 2; +const int CRYPTO_DILITHIUM_PUBLICKEY_BYTES = 1312; +const int CRYPTO_DILITHIUM_SECRETKEY_BYTES = 2560; +const int CRYPTO_DILITHIUM_SIGNATURE_BYTES = 2420; + +/* +Secret Key Length = 64 + 2560 + 1312 = 3587 +============================================ +Layout of secret key: + +32 bytes 32 bytes 2560 bytes 1312 bytes +ed25519 secret key | ed25519 public key | dilithium secret key | dilithium public key + +The following signature length includes implementation output, in addition to actual algorithm output. + + +Layout of ED25519 signature +============================ + +64 bytes | {1 to 64 bytes} +ed25519 Signature | Message + + +Layout of Dilithium signature +============================ +2420 bytes +dilithium signature + + +Hybrid Signature Length = 2 + 64 + {1 to 64} + 2420 +======================================================================================================================= +Layout of signature: + + +2 bytes | 64 bytes | {1 to 64 bytes} | dilithium signature +length of message big-endian | ed25519 signature | actual message | 2420 + + +The first 2 bytes contain signature length of ed25519, dilithium +Message is variable length, between 1 to 64 bytes +*/ + +int crypto_sign_dilithium_ed25519_keypair_seed(unsigned char* pk, unsigned char* sk, unsigned char* seed) { + if (pk == NULL || sk == NULL) { + return -1; + } + + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + unsigned char sk2[2560 + 1312] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + + unsigned char seed1[32] = { 0 }; //SEED_LENGTH_ED25519_2 + unsigned char seed2[48] = { 0 }; //SEED_LENGTH_DILITHIUM + + for (int i = 0; i < SEED_LENGTH_ED25519_2; i++) { + seed1[i] = seed[i]; + } + + for (int i = 0; i < SEED_LENGTH_DILITHIUM; i++) { + seed2[i] = seed[i + SEED_LENGTH_ED25519_2]; + } + + int r1 = crypto_sign_ed25519_keypair_seed(pk1, sk1, seed1); + if (r1 != 0) { + return -2; + } + + for (int i = 0; i < CRYPTO_ED25519_PUBLICKEY_BYTES_2; i++) { + pk[i] = pk1[i]; + } + + for (int i = 0; i < CRYPTO_ED25519_SECRETKEY_BYTES_2; i++) { + sk[i] = sk1[i]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk2, sk2, seed2, sizeof seed2); + + if (r2 != 0) { + return -3; + } + + for (int i = 0; i < CRYPTO_DILITHIUM_SECRETKEY_BYTES; i++) { + sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i] = sk2[i]; + } + + for (int i = 0; i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES; i++) { + pk[CRYPTO_ED25519_PUBLICKEY_BYTES_2 + i] = pk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key + } + + + return 0; +} + +int crypto_sign_dilithium_ed25519_keypair(unsigned char* pk, unsigned char* sk) { + if (pk == NULL || sk == NULL) { + return -1; + } + + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + + int r1 = crypto_sign_ed25519_keypair(pk1, sk1); + if (r1 != 0) { + return -2; + } + + for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES_2;i++) { + pk[i] = pk1[i]; + } + + for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES_2;i++) { + sk[i] = sk1[i]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair(pk2, sk2); + + if (r2 != 0) { + return -3; + } + + for (int i = 0;i < CRYPTO_DILITHIUM_SECRETKEY_BYTES;i++) { + sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i] = sk2[i]; + } + + for (int i = 0;i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES;i++) { + pk[CRYPTO_ED25519_PUBLICKEY_BYTES_2 + i] = pk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key + } + + + return 0; +} + +int crypto_sign_dilithium_ed25519(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk) { + + if (sm == NULL || smlen == NULL || m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN_2 || sk == NULL) { + return -1; + } + + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + + //Copy sk1 from input + for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES_2;i++) { + sk1[i] = sk[i]; + } + + //Copy sk2 from input (skip public key part of it) + for (int i = 0;i < CRYPTO_DILITHIUM_SECRETKEY_BYTES;i++) { + sk2[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i]; + } + + //Always call both sign operations, instead of exiting early from first on failure, to reduce risk from timing attacks if any + int r1 = crypto_sign_ed25519(sig1, &sigLen1, m, mlen, sk1); + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(sig2, &sigLen2, m, mlen, sk2); + + if (r1 != 0) { + return -2; + } + + if (r2 != 0) { + return -3; + } + + if (sigLen1 != CRYPTO_ED25519_SIGNATURE_BYTES_2 + mlen) { + return -3; + } + + if (sigLen2 != CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -4; + } + + //Set totalLen of sig, excluding LEN_BYTES_2 + unsigned long long totalLen = sigLen1 + sigLen2; + sm[0] = (unsigned char)(totalLen >> 8); + sm[1] = (unsigned char)totalLen; + + //Copy ed25519 signature (which includes the message), to output + for (int i = 0;i < (int)sigLen1;i++) { + sm[LEN_BYTES_2 + i] = sig1[i]; + } + + //Copy the Dilithium signature to the output + for (int i = 0;i < sigLen2;i++) { + sm[LEN_BYTES_2 + sigLen1 + i] = sig2[i]; + } + + *smlen = LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES + mlen; + + return 0; + +} + +int crypto_sign_dilithium_ed25519_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + MIN_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || + smlen > LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || pk == NULL) { + return -1; + } + + int totalLen = ((size_t)sm[0] << 8) | (size_t)sm[1]; + if (totalLen < CRYPTO_ED25519_SIGNATURE_BYTES_2 + MIN_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || totalLen > CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -2; + } + int msgLen = totalLen - CRYPTO_ED25519_SIGNATURE_BYTES_2 - CRYPTO_DILITHIUM_SIGNATURE_BYTES; + if (msgLen <= 0 || msgLen > MAX_MSG_LEN_2) { + return -3; + } + + if (smlen != LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -15; + } + + int sig1Len = CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen; + int sig2Len = totalLen - sig1Len; + if (sig2Len != CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -4; + } + + unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned long long msgFromSignatureLen1 = 0; + unsigned char msgFromSignature2[64] = { 0 }; //MAX_MSG_LEN_2 + unsigned long long msgFromSignatureLen2 = 0; + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + unsigned char sig2[2420 + 64] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + MAX_MSG_LEN_2 + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + + //Copy Sig1 from source, including message + for (int i = 0;i < (int)sig1Len;i++) { + sig1[i] = sm[LEN_BYTES_2 + i]; + } + + //Copy pk1 from source + for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES_2;i++) { + pk1[i] = pk[i]; + } + + int r1 = crypto_sign_ed25519_open(msgFromSignature1, &msgFromSignatureLen1, sig1, sig1Len, pk1); + if (r1 != 0) { + return -4; + } + + if ((int) msgFromSignatureLen1 != msgLen) { + return -5; + } + + //Copy actual Sig2 from source + for (int i = 0; i < CRYPTO_DILITHIUM_SIGNATURE_BYTES; i++) { + sig2[i] = sm[LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen + i]; + } + + //Copy Message into sig2 + for (int i = 0;i < msgLen;i++) { + sig2[CRYPTO_DILITHIUM_SIGNATURE_BYTES + i] = sm[LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + i]; + } + + //Copy pk2 from source + for (int i = 0;i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES;i++) { + pk2[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES_2]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(msgFromSignature2, &msgFromSignatureLen2, sig2, sig2Len + msgLen, pk2); + if (r2 != 0) { + return -6; + } + + if ((int) msgFromSignatureLen2 != msgLen) { + return -7; + } + + for (int i = 0;i < msgLen;i++) { + if (msgFromSignature1[i] != msgFromSignature2[i]) { + return -8; + } + m[i] = msgFromSignature1[i]; + } + + *mlen = msgLen; + + return 0; +} + +int crypto_verify_dilithium_ed25519(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL|| mlen <= 0 || mlen > MAX_MSG_LEN_2 || sm == NULL || pk == NULL) { + return -1; + } + + unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned long long msgFromSignatureLen1 = 0; + + int r = crypto_sign_dilithium_ed25519_open(msgFromSignature1, &msgFromSignatureLen1, sm, smlen, pk); + if (r != 0) { + return -2; + } + + if (msgFromSignatureLen1 != mlen) { + return -3; + } + + for (int i = 0;i < (int)msgFromSignatureLen1;i++) { + if (msgFromSignature1[i] != m[i]) { + return -4; + } + } + + return 0; +} + diff --git a/hybrid-dilithium/hybrid.h b/hybrid-dilithium/hybrid.h new file mode 100644 index 0000000..e4cc48f --- /dev/null +++ b/hybrid-dilithium/hybrid.h @@ -0,0 +1,37 @@ + +#if defined(_WIN32) +#define HYBRID_API __declspec(dllexport) +#else +#define HYBRID_API __attribute__((visibility("default"))) +#endif + +#if defined(OQS_SYS_UEFI) +#undef HYBRID_API +#define HYBRID_API +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CRYPTO_DILITHIUM_HYBRID_ALGNAME "Dilithium2-ed25519" + +HYBRID_API int crypto_sign_dilithium_ed25519_keypair_seed(unsigned char* pk, unsigned char* sk, unsigned char* seed); //seed needs to be 64 chars in length (32 + 32) + +HYBRID_API int crypto_sign_dilithium_ed25519_keypair(unsigned char* pk, unsigned char* sk); + +HYBRID_API int crypto_sign_dilithium_ed25519(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk); + +HYBRID_API int crypto_sign_dilithium_ed25519_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +HYBRID_API int crypto_verify_dilithium_ed25519(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/sphincs/LICENSE b/sphincs/LICENSE new file mode 100644 index 0000000..670154e --- /dev/null +++ b/sphincs/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/sphincs/Makefile b/sphincs/Makefile new file mode 100644 index 0000000..9115615 --- /dev/null +++ b/sphincs/Makefile @@ -0,0 +1,21 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB = libsphincs-shake-128f-simple_clean.a + +HEADERS = address.h api.h context.h fors.h hash.h merkle.h nistapi.h params.h shake_offsets.h thash.h utils.h utilsx1.h wots.h wotsx1.h +OBJECTS = address.o context_shake.o fors.o hash_shake.o merkle.o sign.o thash_shake_simple.o utils.o utilsx1.o wots.o wotsx1.o + +CFLAGS = -std=c99 -O3 -Wall -Wconversion -Werror -Wextra -Wmissing-prototypes -Wpedantic -Wredundant-decls -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) + diff --git a/sphincs/Makefile.Microsoft_nmake b/sphincs/Makefile.Microsoft_nmake new file mode 100644 index 0000000..ba91e26 --- /dev/null +++ b/sphincs/Makefile.Microsoft_nmake @@ -0,0 +1,21 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY = libsphincs-shake-128f-simple_clean.lib +OBJECTS = address.obj context_shake.obj fors.obj hash_shake.obj merkle.obj sign.obj thash_shake_simple.obj utils.obj utilsx1.obj wots.obj wotsx1.obj + +CFLAGS = /nologo /O2 /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +$(OBJECTS): *.h + + + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) + diff --git a/sphincs/address.c b/sphincs/address.c new file mode 100644 index 0000000..b956e9c --- /dev/null +++ b/sphincs/address.c @@ -0,0 +1,91 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +/* + * Specify which level of Merkle tree (the "layer") we're working on + */ +void set_layer_addr(uint32_t addr[8], uint32_t layer) { + ((unsigned char *)addr)[SPX_OFFSET_LAYER] = (unsigned char)layer; +} + +/* + * Specify which Merkle tree within the level (the "tree address") we're working on + */ +void set_tree_addr(uint32_t addr[8], uint64_t tree) { + ull_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE], 8, tree ); +} + +/* + * Specify the reason we'll use this address structure for, that is, what + * hash will we compute with it. This is used so that unrelated types of + * hashes don't accidentally get the same address structure. The type will be + * one of the SPX_ADDR_TYPE constants + */ +void set_type(uint32_t addr[8], uint32_t type) { + ((unsigned char *)addr)[SPX_OFFSET_TYPE] = (unsigned char)type; +} + +/* + * Copy the layer and tree fields of the address structure. This is used + * when we're doing multiple types of hashes within the same Merkle tree + */ +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]) { + memcpy( out, in, SPX_OFFSET_TREE + 8 ); +} + +/* These functions are used for OTS addresses. */ + +/* + * Specify which Merkle leaf we're working on; that is, which OTS keypair + * we're talking about. + */ +void set_keypair_addr(uint32_t addr[8], uint32_t keypair) { + ((unsigned char *)addr)[SPX_OFFSET_KP_ADDR1] = (unsigned char)keypair; +} + +/* + * Copy the layer, tree and keypair fields of the address structure. This is + * used when we're doing multiple things within the same OTS keypair + */ +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]) { + memcpy( out, in, SPX_OFFSET_TREE + 8 ); + ((unsigned char *)out)[SPX_OFFSET_KP_ADDR1] = ((unsigned char *)in)[SPX_OFFSET_KP_ADDR1]; +} + +/* + * Specify which Merkle chain within the OTS we're working with + * (the chain address) + */ +void set_chain_addr(uint32_t addr[8], uint32_t chain) { + ((unsigned char *)addr)[SPX_OFFSET_CHAIN_ADDR] = (unsigned char)chain; +} + +/* + * Specify where in the Merkle chain we are +* (the hash address) + */ +void set_hash_addr(uint32_t addr[8], uint32_t hash) { + ((unsigned char *)addr)[SPX_OFFSET_HASH_ADDR] = (unsigned char)hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +/* + * Specify the height of the node in the Merkle/FORS tree we are in + * (the tree height) + */ +void set_tree_height(uint32_t addr[8], uint32_t tree_height) { + ((unsigned char *)addr)[SPX_OFFSET_TREE_HGT] = (unsigned char)tree_height; +} + +/* + * Specify the distance from the left edge of the node in the Merkle/FORS tree + * (the tree index) + */ +void set_tree_index(uint32_t addr[8], uint32_t tree_index) { + u32_to_bytes(&((unsigned char *)addr)[SPX_OFFSET_TREE_INDEX], tree_index ); +} diff --git a/sphincs/address.h b/sphincs/address.h new file mode 100644 index 0000000..24a84eb --- /dev/null +++ b/sphincs/address.h @@ -0,0 +1,52 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#include "params.h" + +/* The hash types that are passed to set_type */ +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 +#define SPX_ADDR_TYPE_WOTSPRF 5 +#define SPX_ADDR_TYPE_FORSPRF 6 + +#define set_layer_addr SPX_NAMESPACE(set_layer_addr) +void set_layer_addr(uint32_t addr[8], uint32_t layer); + +#define set_tree_addr SPX_NAMESPACE(set_tree_addr) +void set_tree_addr(uint32_t addr[8], uint64_t tree); + +#define set_type SPX_NAMESPACE(set_type) +void set_type(uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +#define copy_subtree_addr SPX_NAMESPACE(copy_subtree_addr) +void copy_subtree_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +#define set_keypair_addr SPX_NAMESPACE(set_keypair_addr) +void set_keypair_addr(uint32_t addr[8], uint32_t keypair); + +#define set_chain_addr SPX_NAMESPACE(set_chain_addr) +void set_chain_addr(uint32_t addr[8], uint32_t chain); + +#define set_hash_addr SPX_NAMESPACE(set_hash_addr) +void set_hash_addr(uint32_t addr[8], uint32_t hash); + +#define copy_keypair_addr SPX_NAMESPACE(copy_keypair_addr) +void copy_keypair_addr(uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +#define set_tree_height SPX_NAMESPACE(set_tree_height) +void set_tree_height(uint32_t addr[8], uint32_t tree_height); + +#define set_tree_index SPX_NAMESPACE(set_tree_index) +void set_tree_index(uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/sphincs/api.h b/sphincs/api.h new file mode 100644 index 0000000..ca4c28c --- /dev/null +++ b/sphincs/api.h @@ -0,0 +1,97 @@ +#ifndef PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+-shake-128f-simple" + +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_BYTES 17088 + +#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +#if defined(_WIN32) +#define HYBRIDPQC_API __declspec(dllexport) +#else +#define HYBRIDPQC_API __attribute__((visibility("default"))) +#endif + +#if defined(HYBRIDPQC_SYS_UEFI) +#undef HYBRIDPQC_API +#define HYBRIDPQC_API +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, + const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, + const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, + const uint8_t *pk); +#endif + + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/sphincs/context.h b/sphincs/context.h new file mode 100644 index 0000000..9906465 --- /dev/null +++ b/sphincs/context.h @@ -0,0 +1,21 @@ +#ifndef SPX_CONTEXT_H +#define SPX_CONTEXT_H + +#include +#include + +#include "params.h" + +typedef struct { + uint8_t pub_seed[SPX_N]; + uint8_t sk_seed[SPX_N]; + +} spx_ctx; + +#define initialize_hash_function SPX_NAMESPACE(initialize_hash_function) +void initialize_hash_function(spx_ctx *ctx); + +#define free_hash_function SPX_NAMESPACE(free_hash_function) +void free_hash_function(spx_ctx *ctx); + +#endif diff --git a/sphincs/context_shake.c b/sphincs/context_shake.c new file mode 100644 index 0000000..9614a10 --- /dev/null +++ b/sphincs/context_shake.c @@ -0,0 +1,12 @@ +#include "context.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void initialize_hash_function(spx_ctx *ctx) { + (void)ctx; /* Suppress an 'unused parameter' warning. */ +} + +// in case the hash function api is heap-based. +void free_hash_function(spx_ctx *ctx) { + (void)ctx; +} diff --git a/sphincs/fors.c b/sphincs/fors.c new file mode 100644 index 0000000..96ca72e --- /dev/null +++ b/sphincs/fors.c @@ -0,0 +1,156 @@ +#include +#include +#include + +#include "fors.h" + +#include "address.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" +#include "utilsx1.h" + +static void fors_gen_sk(unsigned char *sk, const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) { + prf_addr(sk, ctx, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const spx_ctx *ctx, + uint32_t fors_leaf_addr[8]) { + thash(leaf, sk, 1, ctx, fors_leaf_addr); +} + +struct fors_gen_leaf_info { + uint32_t leaf_addrx[8]; +}; + +static void fors_gen_leafx1(unsigned char *leaf, + const spx_ctx *ctx, + uint32_t addr_idx, void *info) { + struct fors_gen_leaf_info *fors_info = info; + uint32_t *fors_leaf_addr = fors_info->leaf_addrx; + + /* Only set the parts that the caller doesn't set */ + set_tree_index(fors_leaf_addr, addr_idx); + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSPRF); + fors_gen_sk(leaf, ctx, fors_leaf_addr); + + set_type(fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + fors_sk_to_leaf(leaf, leaf, + ctx, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (uint32_t)(((m[offset >> 3] >> (offset & 0x7)) & 0x1) << j); + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + struct fors_gen_leaf_info fors_info = {0}; + uint32_t *fors_leaf_addr = fors_info.leaf_addrx; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_leaf_addr, fors_addr); + + copy_keypair_addr(fors_pk_addr, fors_addr); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSPRF); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, ctx, fors_tree_addr); + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + treehashx1(roots + i * SPX_N, sig, ctx, + indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leafx1, + fors_tree_addr, &fors_info); + + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + copy_keypair_addr(fors_tree_addr, fors_addr); + copy_keypair_addr(fors_pk_addr, fors_addr); + + set_type(fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + set_type(fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + set_tree_height(fors_tree_addr, 0); + set_tree_index(fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, ctx, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + compute_root(roots + i * SPX_N, leaf, indices[i], idx_offset, + sig, SPX_FORS_HEIGHT, ctx, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + thash(pk, roots, SPX_FORS_TREES, ctx, fors_pk_addr); +} diff --git a/sphincs/fors.h b/sphincs/fors.h new file mode 100644 index 0000000..509140a --- /dev/null +++ b/sphincs/fors.h @@ -0,0 +1,32 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "context.h" +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_sign SPX_NAMESPACE(fors_sign) +void fors_sign(unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +#define fors_pk_from_sig SPX_NAMESPACE(fors_pk_from_sig) +void fors_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const spx_ctx *ctx, + const uint32_t fors_addr[8]); + +#endif diff --git a/sphincs/hash.h b/sphincs/hash.h new file mode 100644 index 0000000..842eeae --- /dev/null +++ b/sphincs/hash.h @@ -0,0 +1,26 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include +#include + +#include "context.h" +#include "params.h" + +#define prf_addr SPX_NAMESPACE(prf_addr) +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]); + +#define gen_message_random SPX_NAMESPACE(gen_message_random) +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const spx_ctx *ctx); + +#define hash_message SPX_NAMESPACE(hash_message) +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const spx_ctx *ctx); + +#endif diff --git a/sphincs/hash_shake.c b/sphincs/hash_shake.c new file mode 100644 index 0000000..e101e56 --- /dev/null +++ b/sphincs/hash_shake.c @@ -0,0 +1,82 @@ +#include +#include + +#include "hash.h" + +#include "address.h" +#include "../common/fips202.h" +#include "params.h" +#include "utils.h" + +/* + * Computes PRF(pk_seed, sk_seed, addr) + */ +void prf_addr(unsigned char *out, const spx_ctx *ctx, + const uint32_t addr[8]) { + unsigned char buf[2 * SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_ADDR_BYTES); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, ctx->sk_seed, SPX_N); + + shake256(out, SPX_N, buf, 2 * SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void gen_message_random(unsigned char *R, const unsigned char *sk_prf, + const unsigned char *optrand, + const unsigned char *m, size_t mlen, + const spx_ctx *ctx) { + (void)ctx; + shake256incctx s_inc; + + shake256_inc_init(&s_inc); + shake256_inc_absorb(&s_inc, sk_prf, SPX_N); + shake256_inc_absorb(&s_inc, optrand, SPX_N); + shake256_inc_absorb(&s_inc, m, mlen); + shake256_inc_finalize(&s_inc); + shake256_inc_squeeze(R, SPX_N, &s_inc); + shake256_inc_ctx_release(&s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void hash_message(unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen, + const spx_ctx *ctx) { + (void)ctx; +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + shake256incctx s_inc; + + shake256_inc_init(&s_inc); + shake256_inc_absorb(&s_inc, R, SPX_N); + shake256_inc_absorb(&s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(&s_inc, m, mlen); + shake256_inc_finalize(&s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, &s_inc); + shake256_inc_ctx_release(&s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)bytes_to_ull(bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/sphincs/merkle.c b/sphincs/merkle.c new file mode 100644 index 0000000..9a082a6 --- /dev/null +++ b/sphincs/merkle.c @@ -0,0 +1,59 @@ +#include +#include + +#include "address.h" +#include "merkle.h" +#include "params.h" +#include "utils.h" +#include "utilsx1.h" +#include "wots.h" +#include "wotsx1.h" + +/* + * This generates a Merkle signature (WOTS signature followed by the Merkle + * authentication path). This is in this file because most of the complexity + * is involved with the WOTS signature; the Merkle authentication path logic + * is mostly hidden in treehashx4 + */ +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx *ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf) { + unsigned char *auth_path = sig + SPX_WOTS_BYTES; + struct leaf_info_x1 info = { 0 }; + unsigned steps[ SPX_WOTS_LEN ]; + + info.wots_sig = sig; + chain_lengths(steps, root); + info.wots_steps = steps; + + set_type(&tree_addr[0], SPX_ADDR_TYPE_HASHTREE); + set_type(&info.pk_addr[0], SPX_ADDR_TYPE_WOTSPK); + copy_subtree_addr(&info.leaf_addr[0], wots_addr); + copy_subtree_addr(&info.pk_addr[0], wots_addr); + + info.wots_sign_leaf = idx_leaf; + + treehashx1(root, auth_path, ctx, + idx_leaf, 0, + SPX_TREE_HEIGHT, + wots_gen_leafx1, + tree_addr, &info); +} + +/* Compute root node of the top-most subtree. */ +void merkle_gen_root(unsigned char *root, const spx_ctx *ctx) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N + SPX_WOTS_BYTES]; + uint32_t top_tree_addr[8] = {0}; + uint32_t wots_addr[8] = {0}; + + set_layer_addr(top_tree_addr, SPX_D - 1); + set_layer_addr(wots_addr, SPX_D - 1); + + merkle_sign(auth_path, root, ctx, + wots_addr, top_tree_addr, + ~0U /* ~0 means "don't bother generating an auth path */ ); +} diff --git a/sphincs/merkle.h b/sphincs/merkle.h new file mode 100644 index 0000000..769cf2e --- /dev/null +++ b/sphincs/merkle.h @@ -0,0 +1,21 @@ +#ifndef MERKLE_H_ +#define MERKLE_H_ + +#include + +#include "context.h" +#include "params.h" + +/* Generate a Merkle signature (WOTS signature followed by the Merkle */ +/* authentication path) */ +#define merkle_sign SPX_NAMESPACE(merkle_sign) +void merkle_sign(uint8_t *sig, unsigned char *root, + const spx_ctx *ctx, + uint32_t wots_addr[8], uint32_t tree_addr[8], + uint32_t idx_leaf); + +/* Compute the root node of the top-most subtree. */ +#define merkle_gen_root SPX_NAMESPACE(merkle_gen_root) +void merkle_gen_root(unsigned char *root, const spx_ctx *ctx); + +#endif /* MERKLE_H_ */ diff --git a/sphincs/nistapi.h b/sphincs/nistapi.h new file mode 100644 index 0000000..3cb71af --- /dev/null +++ b/sphincs/nistapi.h @@ -0,0 +1,87 @@ +#ifndef SPX_API_H +#define SPX_API_H + +#include +#include + +#include "params.h" + +#define CRYPTO_ALGNAME "SPHINCS+" + +#define CRYPTO_SECRETKEYBYTES SPX_SK_BYTES +#define CRYPTO_PUBLICKEYBYTES SPX_PK_BYTES +#define CRYPTO_BYTES SPX_BYTES +#define CRYPTO_SEEDBYTES (3*SPX_N) + +/* + * Returns the length of a secret key, in bytes + */ +#define crypto_sign_secretkeybytes SPX_NAMESPACE(crypto_sign_secretkeybytes) +size_t crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +#define crypto_sign_publickeybytes SPX_NAMESPACE(crypto_sign_publickeybytes) +size_t crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +#define crypto_sign_bytes SPX_NAMESPACE(crypto_sign_bytes) +size_t crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +#define crypto_sign_seedbytes SPX_NAMESPACE(crypto_sign_seedbytes) +size_t crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +#define crypto_sign_seed_keypair SPX_NAMESPACE(crypto_sign_seed_keypair) +int crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, + const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +#define crypto_sign_keypair SPX_NAMESPACE(crypto_sign_keypair) +int crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +#define crypto_sign_signature SPX_NAMESPACE(crypto_sign_signature) +int crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +#define crypto_sign_verify SPX_NAMESPACE(crypto_sign_verify) +int crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +#define crypto_sign SPX_NAMESPACE(crypto_sign) +int crypto_sign(uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, + const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +#define crypto_sign_open SPX_NAMESPACE(crypto_sign_open) +int crypto_sign_open(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, + const uint8_t *pk); + +#endif diff --git a/sphincs/params.h b/sphincs/params.h new file mode 100644 index 0000000..865ed51 --- /dev/null +++ b/sphincs/params.h @@ -0,0 +1,56 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +#define SPX_NAMESPACE(s) PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_##s + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 6 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +//#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT +// #error SPX_D should always divide SPX_FULL_HEIGHT +//#endif + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +#include "shake_offsets.h" + +#endif diff --git a/sphincs/shake_offsets.h b/sphincs/shake_offsets.h new file mode 100644 index 0000000..6b28d95 --- /dev/null +++ b/sphincs/shake_offsets.h @@ -0,0 +1,21 @@ +#ifndef SHAKE_OFFSETS_H_ +#define SHAKE_OFFSETS_H_ + +/* + * Offsets of various fields in the address structure when we use SHAKE as + * the Sphincs+ hash function + */ + +#define SPX_OFFSET_LAYER 3 /* The byte used to specify the Merkle tree layer */ +#define SPX_OFFSET_TREE 8 /* The start of the 8 byte field used to specify the tree */ +#define SPX_OFFSET_TYPE 19 /* The byte used to specify the hash type (reason) */ +#define SPX_OFFSET_KP_ADDR2 22 /* The high byte used to specify the key pair (which one-time signature) */ +#define SPX_OFFSET_KP_ADDR1 23 /* The low byte used to specify the key pair */ +#define SPX_OFFSET_CHAIN_ADDR 27 /* The byte used to specify the chain address (which Winternitz chain) */ +#define SPX_OFFSET_HASH_ADDR 31 /* The byte used to specify the hash address (where in the Winternitz chain) */ +#define SPX_OFFSET_TREE_HGT 27 /* The byte used to specify the height of this node in the FORS or Merkle tree */ +#define SPX_OFFSET_TREE_INDEX 28 /* The start of the 4 byte field used to specify the node in the FORS or Merkle tree */ + +#define SPX_SHAKE 1 + +#endif /* SHAKE_OFFSETS_H_ */ diff --git a/sphincs/sign.c b/sphincs/sign.c new file mode 100644 index 0000000..695b254 --- /dev/null +++ b/sphincs/sign.c @@ -0,0 +1,294 @@ +#include +#include +#include + +#include "address.h" +#include "context.h" +#include "fors.h" +#include "hash.h" +#include "merkle.h" +#include "nistapi.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" +#include "../random/randombytes.h" + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, + const uint8_t *seed) { + spx_ctx ctx; + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + memcpy(ctx.pub_seed, pk, SPX_N); + memcpy(ctx.sk_seed, sk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + /* Compute root node of the top-most subtree. */ + merkle_gen_root(sk + 3 * SPX_N, &ctx); + + // cleanup + free_hash_function(&ctx); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk) { + uint8_t seed[CRYPTO_SEEDBYTES]; + + int r = randombytes(seed, CRYPTO_SEEDBYTES); + if (r != 0) { + return -1; + } + + crypto_sign_seed_keypair(pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + spx_ctx ctx; + + const uint8_t *sk_prf = sk + SPX_N; + const uint8_t *pk = sk + 2 * SPX_N; + + uint8_t optrand[SPX_N]; + uint8_t mhash[SPX_FORS_MSG_BYTES]; + uint8_t root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + memcpy(ctx.sk_seed, sk, SPX_N); + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + int r = randombytes(optrand, SPX_N); + if (r != 0) { + return -1; + } + + /* Compute the digest randomization value. */ + gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx); + + /* Derive the message digest and leaf index from R, PK and M. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + fors_sign(sig, root, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + merkle_sign(sig, root, &ctx, wots_addr, tree_addr, idx_leaf); + sig += SPX_WOTS_BYTES + SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + free_hash_function(&ctx); + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + spx_ctx ctx; + const uint8_t *pub_root = pk + SPX_N; + uint8_t mhash[SPX_FORS_MSG_BYTES]; + uint8_t wots_pk[SPX_WOTS_BYTES]; + uint8_t root[SPX_N]; + uint8_t leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + memcpy(ctx.pub_seed, pk, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + initialize_hash_function(&ctx); + + set_type(wots_addr, SPX_ADDR_TYPE_WOTS); + set_type(tree_addr, SPX_ADDR_TYPE_HASHTREE); + set_type(wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + hash_message(mhash, &tree, &idx_leaf, sig, pk, m, mlen, &ctx); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + set_tree_addr(wots_addr, tree); + set_keypair_addr(wots_addr, idx_leaf); + + fors_pk_from_sig(root, sig, mhash, &ctx, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + set_layer_addr(tree_addr, i); + set_tree_addr(tree_addr, tree); + + copy_subtree_addr(wots_addr, tree_addr); + set_keypair_addr(wots_addr, idx_leaf); + + copy_keypair_addr(wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + wots_pk_from_sig(wots_pk, sig, root, &ctx, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + thash(leaf, wots_pk, SPX_WOTS_LEN, &ctx, wots_pk_addr); + + /* Compute the root node of this subtree. */ + compute_root(root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + &ctx, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + // cleanup + free_hash_function(&ctx); + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, + const uint8_t *sk) { + size_t siglen; + + crypto_sign_signature(sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, + const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/sphincs/thash.h b/sphincs/thash.h new file mode 100644 index 0000000..8687ccf --- /dev/null +++ b/sphincs/thash.h @@ -0,0 +1,13 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include "context.h" +#include "params.h" + +#include + +#define thash SPX_NAMESPACE(thash) +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]); + +#endif diff --git a/sphincs/thash_shake_simple.c b/sphincs/thash_shake_simple.c new file mode 100644 index 0000000..560a8e0 --- /dev/null +++ b/sphincs/thash_shake_simple.c @@ -0,0 +1,24 @@ +#include +#include + +#include "thash.h" + +#include "address.h" +#include "params.h" +#include "utils.h" + +#include "../common/fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +void thash(unsigned char *out, const unsigned char *in, unsigned int inblocks, + const spx_ctx *ctx, uint32_t addr[8]) { + PQCLEAN_VLA(uint8_t, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); + + memcpy(buf, ctx->pub_seed, SPX_N); + memcpy(buf + SPX_N, addr, SPX_ADDR_BYTES); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} diff --git a/sphincs/utils.c b/sphincs/utils.c new file mode 100644 index 0000000..177b541 --- /dev/null +++ b/sphincs/utils.c @@ -0,0 +1,148 @@ +#include + +#include "utils.h" + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in) { + int i; + + /* Iterate over out in decreasing order, for big-endianness. */ + for (i = (signed int)outlen - 1; i >= 0; i--) { + out[i] = in & 0xff; + in = in >> 8; + } +} + +void u32_to_bytes(unsigned char *out, uint32_t in) { + out[0] = (unsigned char)(in >> 24); + out[1] = (unsigned char)(in >> 16); + out[2] = (unsigned char)(in >> 8); + out[3] = (unsigned char)in; +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen) { + unsigned long long retval = 0; + unsigned int i; + + for (i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + set_tree_height(addr, i + 1); + set_tree_index(addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + thash(buffer + SPX_N, buffer, 2, ctx, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + thash(buffer, buffer, 2, ctx, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + set_tree_height(addr, tree_height); + set_tree_index(addr, leaf_idx + idx_offset); + thash(root, buffer, 2, ctx, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void treehash(unsigned char *root, unsigned char *auth_path, const spx_ctx *ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const spx_ctx * /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + PQCLEAN_VLA(uint8_t, stack, (tree_height + 1)*SPX_N); + PQCLEAN_VLA(unsigned int, heights, tree_height + 1); + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, ctx, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + set_tree_height(tree_addr, heights[offset - 1] + 1); + set_tree_index(tree_addr, + tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + thash(stack + (offset - 2)*SPX_N, + stack + (offset - 2)*SPX_N, 2, ctx, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} diff --git a/sphincs/utils.h b/sphincs/utils.h new file mode 100644 index 0000000..8be4434 --- /dev/null +++ b/sphincs/utils.h @@ -0,0 +1,55 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include + +#include "../common/compat.h" +#include "context.h" +#include "params.h" + +/* To support MSVC use alloca() instead of VLAs. See #20. */ + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +#define ull_to_bytes SPX_NAMESPACE(ull_to_bytes) +void ull_to_bytes(unsigned char *out, unsigned int outlen, + unsigned long long in); +#define u32_to_bytes SPX_NAMESPACE(u32_to_bytes) +void u32_to_bytes(unsigned char *out, uint32_t in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +#define bytes_to_ull SPX_NAMESPACE(bytes_to_ull) +unsigned long long bytes_to_ull(const unsigned char *in, unsigned int inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +#define compute_root SPX_NAMESPACE(compute_root) +void compute_root(unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const spx_ctx *ctx, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehash SPX_NAMESPACE(treehash) +void treehash(unsigned char *root, unsigned char *auth_path, + const spx_ctx *ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const spx_ctx *ctx /* ctx */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/sphincs/utilsx1.c b/sphincs/utilsx1.c new file mode 100644 index 0000000..fccb69b --- /dev/null +++ b/sphincs/utilsx1.c @@ -0,0 +1,100 @@ +#include + +#include "utilsx1.h" + +#include "address.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/* + * Generate the entire Merkle tree, computing the authentication path for + * leaf_idx, and the resulting root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE) + * + * This expects tree_addr to be initialized to the addr structures for the + * Merkle tree nodes + * + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + * + * This works by using the standard Merkle tree building algorithm, + */ +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx *ctx, + uint32_t leaf_idx, uint32_t idx_offset, + uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* Where to write the leaves */, + const spx_ctx * /* ctx */, + uint32_t idx, void *info), + uint32_t tree_addr[8], + void *info) { + /* This is where we keep the intermediate nodes */ + PQCLEAN_VLA(uint8_t, stack, tree_height * SPX_N); + + uint32_t idx; + uint32_t max_idx = (uint32_t)((1 << tree_height) - 1); + for (idx = 0;; idx++) { + unsigned char current[2 * SPX_N]; /* Current logical node is at */ + /* index[SPX_N]. We do this to minimize the number of copies */ + /* needed during a thash */ + gen_leaf( ¤t[SPX_N], ctx, idx + idx_offset, + info ); + + /* Now combine the freshly generated right node with previously */ + /* generated left ones */ + uint32_t internal_idx_offset = idx_offset; + uint32_t internal_idx = idx; + uint32_t internal_leaf = leaf_idx; + uint32_t h; /* The height we are in the Merkle tree */ + for (h = 0;; h++, internal_idx >>= 1, internal_leaf >>= 1) { + + /* Check if we hit the top of the tree */ + if (h == tree_height) { + /* We hit the root; return it */ + memcpy( root, ¤t[SPX_N], SPX_N ); + return; + } + + /* + * Check if the node we have is a part of the + * authentication path; if it is, write it out + */ + if ((internal_idx ^ internal_leaf) == 0x01) { + memcpy( &auth_path[ h * SPX_N ], + ¤t[SPX_N], + SPX_N ); + } + + /* + * Check if we're at a left child; if so, stop going up the stack + * Exception: if we've reached the end of the tree, keep on going + * (so we combine the last 4 nodes into the one root node in two + * more iterations) + */ + if ((internal_idx & 1) == 0 && idx < max_idx) { + break; + } + + /* Ok, we're at a right node */ + /* Now combine the left and right logical nodes together */ + + /* Set the address of the node we're creating. */ + internal_idx_offset >>= 1; + set_tree_height(tree_addr, h + 1); + set_tree_index(tree_addr, internal_idx / 2 + internal_idx_offset ); + + unsigned char *left = &stack[h * SPX_N]; + memcpy( ¤t[0], left, SPX_N ); + thash( ¤t[1 * SPX_N], + ¤t[0 * SPX_N], + 2, ctx, tree_addr); + } + + /* We've hit a left child; save the current for when we get the */ + /* corresponding right right */ + memcpy( &stack[h * SPX_N], ¤t[SPX_N], SPX_N); + } +} diff --git a/sphincs/utilsx1.h b/sphincs/utilsx1.h new file mode 100644 index 0000000..e911d17 --- /dev/null +++ b/sphincs/utilsx1.h @@ -0,0 +1,27 @@ +#ifndef SPX_UTILSX4_H +#define SPX_UTILSX4_H + +#include + +#include "context.h" +#include "params.h" + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +#define treehashx1 SPX_NAMESPACE(treehashx1) +void treehashx1(unsigned char *root, unsigned char *auth_path, + const spx_ctx *ctx, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* Where to write the leaf */, + const spx_ctx * /* ctx */, + uint32_t addr_idx, void *info), + uint32_t tree_addrx4[8], void *info); + +#endif diff --git a/sphincs/wots.c b/sphincs/wots.c new file mode 100644 index 0000000..249717a --- /dev/null +++ b/sphincs/wots.c @@ -0,0 +1,108 @@ +#include +#include + +#include "wots.h" +#include "wotsx1.h" + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "utilsx1.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const spx_ctx *ctx, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + set_hash_addr(addr, i); + thash(out, out, 1, ctx, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const int out_len, + const unsigned char *input) { + int in = 0; + int out = 0; + unsigned char total = 0; + int bits = 0; + int consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (total >> bits) & (SPX_WOTS_W - 1); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << ((8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)) % 8); + ull_to_bytes(csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], ctx, addr); + } +} diff --git a/sphincs/wots.h b/sphincs/wots.h new file mode 100644 index 0000000..4e7692e --- /dev/null +++ b/sphincs/wots.h @@ -0,0 +1,25 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include + +#include "context.h" +#include "params.h" + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +#define wots_pk_from_sig SPX_NAMESPACE(wots_pk_from_sig) +void wots_pk_from_sig(unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const spx_ctx *ctx, uint32_t addr[8]); + +/* + * Compute the chain lengths needed for a given message hash + */ +#define chain_lengths SPX_NAMESPACE(chain_lengths) +void chain_lengths(unsigned int *lengths, const unsigned char *msg); + +#endif diff --git a/sphincs/wotsx1.c b/sphincs/wotsx1.c new file mode 100644 index 0000000..3adec78 --- /dev/null +++ b/sphincs/wotsx1.c @@ -0,0 +1,76 @@ +#include +#include + +#include "wots.h" +#include "wotsx1.h" + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/* + * This generates a WOTS public key + * It also generates the WOTS signature if leaf_info indicates + * that we're signing with this WOTS key + */ +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info) { + struct leaf_info_x1 *info = v_info; + uint32_t *leaf_addr = info->leaf_addr; + uint32_t *pk_addr = info->pk_addr; + unsigned int i, k; + unsigned char pk_buffer[ SPX_WOTS_BYTES ]; + unsigned char *buffer; + uint32_t wots_k_mask; + + if (leaf_idx == info->wots_sign_leaf) { + /* We're traversing the leaf that's signing; generate the WOTS */ + /* signature */ + wots_k_mask = 0; + } else { + /* Nope, we're just generating pk's; turn off the signature logic */ + wots_k_mask = (uint32_t)~0; + } + + set_keypair_addr( leaf_addr, leaf_idx ); + set_keypair_addr( pk_addr, leaf_idx ); + + for (i = 0, buffer = pk_buffer; i < SPX_WOTS_LEN; i++, buffer += SPX_N) { + uint32_t wots_k = info->wots_steps[i] | wots_k_mask; /* Set wots_k to */ + /* the step if we're generating a signature, ~0 if we're not */ + + /* Start with the secret seed */ + set_chain_addr(leaf_addr, i); + set_hash_addr(leaf_addr, 0); + set_type(leaf_addr, SPX_ADDR_TYPE_WOTSPRF); + + prf_addr(buffer, ctx, leaf_addr); + + set_type(leaf_addr, SPX_ADDR_TYPE_WOTS); + + /* Iterate down the WOTS chain */ + for (k = 0;; k++) { + /* Check if this is the value that needs to be saved as a */ + /* part of the WOTS signature */ + if (k == wots_k) { + memcpy( info->wots_sig + i * SPX_N, buffer, SPX_N ); + } + + /* Check if we hit the top of the chain */ + if (k == SPX_WOTS_W - 1) { + break; + } + + /* Iterate one step on the chain */ + set_hash_addr(leaf_addr, k); + + thash(buffer, buffer, 1, ctx, leaf_addr); + } + } + + /* Do the final thash to generate the public keys */ + thash(dest, pk_buffer, SPX_WOTS_LEN, ctx, pk_addr); +} diff --git a/sphincs/wotsx1.h b/sphincs/wotsx1.h new file mode 100644 index 0000000..e617929 --- /dev/null +++ b/sphincs/wotsx1.h @@ -0,0 +1,39 @@ +#ifndef WOTSX1_H_ +#define WOTSX1_H_ + +#include + +#include "context.h" +#include "params.h" + +/* + * This is here to provide an interface to the internal wots_gen_leafx1 + * routine. While this routine is not referenced in the package outside of + * wots.c, it is called from the stand-alone benchmark code to characterize + * the performance + */ +struct leaf_info_x1 { + unsigned char *wots_sig; + uint32_t wots_sign_leaf; /* The index of the WOTS we're using to sign */ + uint32_t *wots_steps; + uint32_t leaf_addr[8]; + uint32_t pk_addr[8]; +}; + +/* Macro to set the leaf_info to something 'benign', that is, it would */ +/* run with the same time as it does during the real signing process */ +/* Used only by the benchmark code */ +#define INITIALIZE_LEAF_INFO_X1(info, addr, step_buffer) { \ + (info).wots_sig = 0; \ + (info).wots_sign_leaf = ~0; \ + (info).wots_steps = step_buffer; \ + memcpy( &(info).leaf_addr[0], (addr), 32 ); \ + memcpy( &(info).pk_addr[0], (addr), 32 ); \ + } + +#define wots_gen_leafx1 SPX_NAMESPACE(wots_gen_leafx1) +void wots_gen_leafx1(unsigned char *dest, + const spx_ctx *ctx, + uint32_t leaf_idx, void *v_info); + +#endif /* WOTSX1_H_ */ diff --git a/tests/test_hybrid.c b/tests/test_hybrid.c index 5ccd147..31537a6 100644 --- a/tests/test_hybrid.c +++ b/tests/test_hybrid.c @@ -9,12 +9,21 @@ #include #include "../random/randombytes.h" #include "../hybrid/hybrid.h" +#include "../hybrid-dilithium/hybrid.h" #include "../falcon512/api.h" +#include "../dilithium2/api.h" +#include "../sphincs/api.h" clock_t get_nano_sec(void); -void print_elapsed(long startTime, long endTime); +void print_elapsed(clock_t startTime, clock_t endTime); int test_falcon(void); -int test_hybrid(void); +int test_dilithium(void); +int test_sphincs(void); +int test_hybrid_falcon(void); +int test_hybrid_falcon_perf(int count); +int test_hybrid_dilithium(void); +int test_hybrid_dilithium_perf(int count); +int test_hybrid_falcon_deterministic(); int test_multiple(int count); int main(int argc, char* argv[]); @@ -146,7 +155,262 @@ int test_falcon() { return 0; } -int test_hybrid_perf(int count) { +int test_dilithium() { + printf("\n test_dilithium() start"); + + unsigned char pk[1312]; + unsigned char sk[2560]; + unsigned char sig[2420 + 32]; + unsigned char msg1[32]; + unsigned char msg2[32]; + unsigned long long sigLen = 0; + + int r1 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair(pk, sk); + if (r1 != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair failed %d", (int)r1); + return r1; + } + + int r2 = randombytes(msg1, 32 * sizeof(unsigned char)); + if (r2 != 0) { + printf("\n randombytes failed %d", (int)r2); + return r2; + } + for (int i = 0; i < 32; i++) { + msg2[1] = msg1[i]; + } + + int r3 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); + if (r3 != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign failed %d", (int)r3); + return r3; + } + printf("\n dilithium sig len = %lld", sigLen); + + unsigned long long msgLen = 0; + int r4 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); + if (r4 != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open failed %d", (int)r4); + return r4; + } + if (msgLen != 32) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + return -5; + } + + for (int i = 0; i < 32; i++) { + if (msg1[i] != msg2[i]) { + printf("\n verify msg content failed %d", i); + return -6; + } + } + + printf("\n deterministic key generation test"); + unsigned char seed1[32]; + if (randombytes(seed1, sizeof seed1) != 0) { + return -7; + } + + unsigned char pk2[1312]; + unsigned char sk2[2560]; + int r5 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk2, sk2, seed1); + if (r5 != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed failed %d", (int)r5); + return r5; + } + + for (int j = 0; j < 32; j++) { + unsigned char pk3[1312]; + unsigned char sk3[2560]; + int r6 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk3, sk3, seed1); + if (r6 != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed failed %d", (int)r6); + return r6; + } + for (int i = 0; i < 1312; i++) { + if (pk2[i] != pk3[i]) { + printf("\n determienistic key generation failed: pk"); + return -8; + } + } + for (int i = 0; i < 2560; i++) { + if (sk2[i] != sk3[i]) { + printf("\n determienistic key generation failed: sk %d, %d, %d", i, sk2[i], sk3[i]); + return -9; + } + } + + int r = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk3); + if (r != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign failed %d", (int)r); + return r; + } + + unsigned long long msgLen = 0; + r = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk3); + if (r != 0) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open failed %d", (int)r); + return r; + } + if (msgLen != 32) { + printf("\n PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + return -5; + } + + for (int i = 0; i < 32; i++) { + if (msg1[i] != msg2[i]) { + printf("\n verify msg content failed %d", i); + return -6; + } + } + } + + printf("\n test_dilithium() ok"); + + return 0; +} + +int test_sphincs() { + printf("\n test_sphincs() start"); + + unsigned char pk[32]; + unsigned char sk[64]; + unsigned char sig[17088 + 32]; + unsigned char msg1[32]; + unsigned char msg2[32]; + unsigned long long sigLen = 0; + clock_t startTime; + clock_t endTime; + + int r1 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(pk, sk); + if (r1 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair failed %d", (int)r1); + return r1; + } + + int r2 = randombytes(msg1, 32 * sizeof(unsigned char)); + if (r2 != 0) { + printf("\n randombytes failed %d", (int)r2); + return r2; + } + for (int i = 0; i < 32; i++) { + msg2[1] = msg1[i]; + } + + int r3 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); + if (r3 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); + return r3; + } + + unsigned long long msgLen = 0; + int r4 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); + if (r4 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); + return r4; + } + if (msgLen != 32) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + return -5; + } + + for (int i = 0; i < 32; i++) { + if (msg1[i] != msg2[i]) { + printf("\n verify msg content failed %d", i); + return -6; + } + } + + printf("\n deterministic key generation test"); + unsigned char seed1[48]; + if (randombytes(seed1, sizeof seed1) != 0) { + return -7; + } + + unsigned char pk2[32]; + unsigned char sk2[64]; + int r5 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk2, sk2, seed1); + if (r5 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r5); + return r5; + } + + for (int j = 0; j < 32; j++) { + unsigned char pk3[32]; + unsigned char sk3[64]; + int r6 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk3, sk3, seed1); + if (r6 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r6); + return r6; + } + for (int i = 0; i < 32; i++) { + if (pk2[i] != pk3[i]) { + printf("\n determienistic key generation failed: pk %d %d %d", i, pk2[i], pk3[i]); + return -8; + } + } + for (int i = 0; i < 64; i++) { + if (sk2[i] != sk3[i]) { + printf("\n determienistic key generation failed: sk %d, %d, %d", i, sk2[i], sk3[i]); + return -9; + } + } + + int r = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk3); + if (r != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r); + return r; + } + + unsigned long long msgLen = 0; + r = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk3); + if (r != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r); + return r; + } + if (msgLen != 32) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + return -5; + } + + for (int i = 0; i < 32; i++) { + if (msg1[i] != msg2[i]) { + printf("\n verify msg content failed %d", i); + return -6; + } + } + } + + printf("\n sphincs sign perf 1000 iterations start"); + startTime = get_nano_sec(); + for (int i = 0; i < 1000; i++) { + r3 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); + if (r3 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); + return r3; + } + } + endTime = get_nano_sec(); + print_elapsed(startTime, endTime); + + printf("\n sphincs verify (sign open) 10000 iterations perf start"); + startTime = get_nano_sec(); + for (int i = 0; i < 10000; i++) { + r4 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); + if (r4 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); + return r4; + } + } + endTime = get_nano_sec(); + print_elapsed(startTime, endTime); + + printf("\n test_sphincs() ok"); + + return 0; +} + +int test_hybrid_falcon_perf(int count) { unsigned char pk[32 + 897]; unsigned char sk[64 + 1281 + 897]; unsigned char msg[32]; @@ -191,7 +455,7 @@ int test_hybrid_perf(int count) { r = crypto_sign_falcon_ed25519(sig, &sigLen, msg, MSG_LEN, sk); if (r != 0) { - printf("\n crypto_sign_falcon_ed25519 failed %d", (int)r); + printf("\n crypto_sign_falcon_ed25519 failed B %d", (int)r); return -4; } @@ -222,8 +486,8 @@ int test_hybrid_perf(int count) { return 0; } -int test_hybrid() { - printf("\n test_hybrid() start"); +int test_hybrid_falcon() { + printf("\n test_hybrid_falcon() start"); unsigned char pk[32 + 897]; unsigned char pk2[32 + 897]; @@ -254,7 +518,7 @@ int test_hybrid() { r = crypto_sign_falcon_ed25519(sig1, &sigLen1, msg1, MSG_LEN, sk); if (r != 0) { - printf("\n crypto_sign_falcon_ed25519 failed %d", (int)r); + printf("\n crypto_sign_falcon_ed25519 failed C %d", (int)r); return -3; } @@ -295,7 +559,7 @@ int test_hybrid() { r = crypto_sign_falcon_ed25519(sig2, &sigLen2, msg2, MSG_LEN, sk); if (r != 0) { - printf("\n crypto_sign_falcon_ed25519 failed %d", (int)r); + printf("\n crypto_sign_falcon_ed25519 failed D %d", (int)r); return -10; } @@ -418,13 +682,14 @@ int test_hybrid() { //todo add fuzz tests - printf(" \n test_hybrid() ok"); + printf(" \n test_hybrid_falcon() ok"); return 0; } -int test_hybrid_deterministic() { - printf("\n test_hybrid_deterministic() start"); + +int test_hybrid_falcon_deterministic() { + printf("\n test_hybrid_falcon_deterministic() start"); unsigned char pk[32 + 897]; unsigned char pk2[32 + 897]; @@ -533,7 +798,7 @@ int test_hybrid_deterministic() { r = crypto_sign_falcon_ed25519(sig1, &sigLen1, msg1, MSG_LEN, sk); if (r != 0) { - printf("\n crypto_sign_falcon_ed25519 failed %d", (int)r); + printf("\n crypto_sign_falcon_ed25519 failed A %d", (int)r); return -3; } @@ -561,14 +826,333 @@ int test_hybrid_deterministic() { } } - printf("\n test_hybrid_deterministic() ok"); + printf("\n test_hybrid_falcon_deterministic() ok"); + return 0; +} + +int test_hybrid_dilithium() { + printf("\n test_hybrid_dilithium () start"); + + unsigned char pk[32 + 1312]; + unsigned char pk2[32 + 1312]; + unsigned char sk[64 + 2560 + 1312]; + unsigned char sig1[2 + 64 + 32 + 2420]; + unsigned char sig2[2 + 64 + 32 + 2420]; + unsigned char msg1[32]; + unsigned char msg2[32]; + unsigned char msg1output[32]; + unsigned char msg2output[32]; + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long msgLen1 = 0; + unsigned long long msgLen2 = 0; + const int MSG_LEN = 32; + + int r = crypto_sign_dilithium_ed25519_keypair(pk, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_keypair failed %d", (int)r); + return -1; + } + + r = randombytes(msg1, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -2; + } + + r = crypto_sign_dilithium_ed25519(sig1, &sigLen1, msg1, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519 failed %d", (int)r); + return -3; + } + + if (sigLen1 != 2518) { + printf("\n crypto_sign_dilithium_ed25519 sigLen error %d", (int)sigLen1); + return -4; + } + + r = crypto_sign_dilithium_ed25519_open(msg1output, &msgLen1, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_open failed %d", (int)r); + return -5; + } + + if (msgLen1 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen1); + return -6; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg1[i] != msg1output[i]) { + printf("\n verify msg content failed %d", i); + return -7; + } + } + + r = crypto_verify_dilithium_ed25519(msg1, MSG_LEN, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519 failed %d", (int)r); + return -8; + } + + r = randombytes(msg2, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -9; + } + + r = crypto_sign_dilithium_ed25519(sig2, &sigLen2, msg2, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519 failed %d", (int)r); + return -10; + } + + if (sigLen2 != 2518) { + printf("\n crypto_sign_dilithium_ed25519 sigLen error %d", (int)sigLen2); + return -11; + } + + //sanity check + r = crypto_sign_dilithium_ed25519_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_open failed %d", (int)r); + return -12; + } + + if (msgLen2 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen2); + return -13; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + + r = crypto_verify_dilithium_ed25519(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519 failed %d", (int)r); + return -15; + } + + printf("\n test_hybrid_dilithium fuzz start"); + + //signature fuzz test (sign open) + for (int i = 0; i < 2518; i++) { + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_sign_dilithium_ed25519_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_dilithium_ed25519_open was ok when it should have failed %d", (int)r); + return -16; + } + sig2[i] = temp; + r = crypto_sign_dilithium_ed25519_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + //signature fuzz test (verify) + for (int i = 0; i < 2518; i++) { + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_verify_dilithium_ed25519(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_verify_dilithium_ed25519 was ok when it should have failed %d", (int)r); + return -18; + } + sig2[i] = temp; + r = crypto_verify_dilithium_ed25519(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519 failed when it should have been ok %d", (int)r); + return -19; + } + } + + //public key fuzz test + for (int i = 0; i < 32 + 1312; i++) { + unsigned char temp = pk[i]; + pk[i] = temp + 1; + r = crypto_sign_dilithium_ed25519_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_dilithium_ed25519_open was ok when it should have failed %d", (int)r); + return -16; + } + pk[i] = temp; + r = crypto_sign_dilithium_ed25519_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + printf(" \n test_hybrid_dilithium () ok"); + + return 0; +} + +int test_hybrid_dilithium_deterministic() { + printf("\n test_hybrid_dilithium_deterministic() start"); + + unsigned char pk[32 + 1312]; + unsigned char pk2[32 + 1312]; + unsigned char pk3[32 + 1312]; + unsigned char sk[64 + 2560 + 1312]; + unsigned char sk2[64 + 2560 + 1312]; + unsigned char sk3[64 + 2560 + 1312]; + unsigned char sig1[2 + 64 + 32 + 2420]; + unsigned char msg1[32]; + unsigned char msg1output[32]; + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long msgLen1 = 0; + unsigned long long msgLen2 = 0; + const int MSG_LEN = 32; + + unsigned char seed1[64] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,119,175,131,115,192,42,246,9,171,117,239,88,235,16,133,230,150,206,59,220,176,144,178,248,188,213,239,142,236,15,177,197}; + + unsigned char seed3[64]; + if (randombytes(seed3, sizeof seed3) != 0) { + return -1; + } + + int r = crypto_sign_dilithium_ed25519_keypair_seed(pk, sk, seed1); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_keypair_seed failed %d", (int)r); + return -2; + } + + /* + printf("\n pk \n"); + for (int k = 0; k < 32 + 1312; k++) { + printf("%d,", pk[k]); + } + printf("\n sk \n"); + for (int k = 0; k < 64 + 2560 + 1312; k++) { + printf("%d,", sk[k]); + } + */ + + unsigned char pkFromSeed[32 + 1312] = { 240,78,219,55,229,129,35,19,238,250,42,184,21,246,140,49,133,117,234,93,254,183,215,211,206,92,25,21,105,115,247,115,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,3,166,59,195,12,211,93,68,8,73,212,169,81,201,46,231,74,162,153,110,81,197,17,33,199,26,102,208,122,104,52,207,38,215,250,125,66,231,196,39,11,76,38,106,119,57,223,51,224,176,181,25,45,166,56,84,24,246,248,143,206,211,185,64,102,123,41,1,70,43,59,167,65,94,175,70,141,218,215,213,32,22,124,94,115,102,168,74,125,180,149,149,127,142,11,64,39,145,22,16,250,47,48,174,58,155,117,170,108,243,208,177,100,217,39,116,40,76,5,203,237,210,163,118,148,156,172,112,86,185,131,37,111,115,233,190,130,69,237,16,207,146,141,103,114,100,201,9,169,72,64,150,245,18,203,123,125,241,167,147,78,41,212,29,70,169,145,50,123,12,165,57,193,211,62,248,112,126,144,229,21,9,123,111,127,158,137,236,226,142,149,190,142,222,213,89,243,231,202,161,246,152,207,74,206,182,251,22,132,205,132,191,248,63,65,27,32,76,230,148,217,236,37,198,17,193,130,245,72,174,99,132,91,1,170,167,96,152,231,136,195,250,77,4,39,128,224,138,150,179,90,102,18,23,148,59,203,99,61,157,199,112,196,11,7,98,58,32,166,2,55,120,43,108,84,157,243,234,178,247,143,110,128,94,25,179,34,30,139,178,41,133,254,76,49,221,225,135,161,56,245,90,41,215,72,10,104,158,68,93,111,181,42,126,1,37,138,63,145,142,22,60,254,124,222,125,196,227,109,34,78,71,55,3,109,147,223,40,165,81,147,13,57,210,234,108,162,69,191,71,155,91,164,112,187,212,227,72,32,204,100,94,245,205,6,230,72,87,138,92,40,148,211,32,236,1,73,99,29,106,108,116,113,254,210,62,158,103,227,126,47,194,112,55,46,82,92,204,225,138,74,170,29,179,131,183,77,234,186,174,144,53,36,159,53,85,86,203,229,174,196,234,131,99,140,226,84,236,205,197,31,143,15,188,126,214,115,111,168,156,84,14,190,140,145,209,118,177,19,98,25,179,183,191,156,95,80,89,158,173,219,222,135,65,162,210,165,177,127,95,161,162,58,248,132,251,241,102,170,186,211,97,170,200,137,78,46,219,140,66,22,208,219,19,189,29,255,254,222,204,145,14,19,238,99,201,161,103,91,8,249,122,165,255,162,107,115,211,120,41,78,130,219,142,209,254,103,82,216,49,0,205,40,105,223,59,121,55,38,254,162,241,41,51,209,143,48,190,236,0,200,146,239,137,193,223,117,205,56,186,70,137,210,1,224,142,241,2,157,31,239,12,138,141,95,216,247,225,64,45,53,248,165,19,6,178,225,95,111,218,164,218,4,49,142,71,244,78,51,29,11,140,121,230,188,22,232,13,33,140,174,115,255,162,244,112,55,29,16,91,58,28,4,158,85,171,96,151,235,235,213,247,0,90,65,227,119,2,54,104,66,185,57,182,76,234,160,170,218,119,123,162,126,79,54,35,58,36,249,117,110,76,7,114,139,135,140,63,139,133,19,128,196,12,51,35,165,76,158,54,87,133,1,58,210,75,203,183,193,34,255,21,114,182,215,122,191,149,134,135,240,198,182,60,66,1,176,144,143,132,36,184,125,62,56,124,218,85,28,245,142,60,18,0,169,99,88,215,123,181,34,16,43,239,207,40,188,127,255,91,8,224,144,125,221,248,27,4,48,212,148,75,103,62,118,7,89,35,186,154,190,95,114,217,115,213,188,217,102,217,174,90,19,7,150,26,111,68,163,109,227,219,228,109,215,110,161,69,169,114,79,226,72,109,221,84,104,224,97,94,246,85,73,156,24,153,16,115,106,16,91,129,251,74,243,51,233,109,229,76,147,48,54,63,197,74,246,209,59,47,98,205,112,178,103,149,12,106,33,168,211,92,82,32,56,184,224,158,20,30,193,27,198,209,252,37,162,248,86,7,199,142,194,110,158,155,197,254,80,36,93,102,163,82,245,75,84,215,28,99,238,82,143,123,215,75,97,226,217,245,210,178,126,46,68,243,162,54,11,32,157,156,83,51,142,52,209,160,113,116,24,79,249,60,19,23,0,186,248,75,105,200,166,160,150,157,146,148,13,26,22,142,103,115,81,196,41,131,196,91,52,126,26,43,146,102,123,63,56,150,255,21,50,12,49,84,75,76,213,86,187,150,249,166,130,105,69,173,203,137,154,120,224,57,211,249,26,188,48,63,1,21,123,245,169,12,118,247,86,112,102,238,244,162,110,249,19,160,173,79,138,225,183,192,62,28,30,150,151,47,122,75,91,178,110,70,209,50,60,80,194,45,213,214,82,139,117,186,27,11,237,207,253,227,251,108,21,161,133,182,38,114,115,139,78,119,149,17,146,125,126,208,191,38,220,130,205,56,145,3,94,38,182,232,65,151,170,104,55,255,88,93,49,181,42,44,52,213,129,189,246,253,216,250,139,139,122,20,192,129,73,36,160,209,164,71,94,252,225,18,2,200,200,64,119,51,167,59,92,122,86,83,254,10,148,164,244,12,126,25,2,231,175,46,228,164,57,10,158,197,159,85,50,1,1,186,67,40,24,67,129,231,92,115,36,217,110,4,83,150,139,162,136,54,152,183,168,184,19,167,222,190,209,105,173,6,227,129,209,109,186,227,201,123,23,47,184,65,83,16,213,21,214,227,52,36,26,177,250,27,16,227,188,63,32,191,48,155,154,30,159,86,90,155,144,196,84,190,79,242,3,152,213,10,98,16,165,146,116,123,100,186,242,53,72,60,238,218,0,21,114,236,109,242,82,185,236,91,113,49,79,184,8,249,122,113,153,177,15,127,178,79,59,17,165,8,248,32,76,33,180,169,92,71,107,125,42,42,4,95,108,166,6,244,25,207,247,84,42,13 }; + unsigned char skFromSeed[64 + 2560 + 1312] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,240,78,219,55,229,129,35,19,238,250,42,184,21,246,140,49,133,117,234,93,254,183,215,211,206,92,25,21,105,115,247,115,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,222,11,28,105,5,109,52,135,188,243,62,92,102,190,11,156,128,42,160,44,41,25,215,31,247,58,131,169,112,166,19,4,231,247,205,149,190,13,3,215,53,100,154,37,253,226,244,228,199,14,134,226,45,154,130,97,243,45,37,246,72,51,208,252,2,27,37,107,218,149,186,100,158,182,143,11,1,80,56,57,144,188,189,252,211,160,147,199,120,254,141,233,188,238,156,239,27,65,96,64,198,76,66,196,97,83,130,129,3,53,46,136,168,69,209,160,45,148,194,81,152,48,68,74,146,77,220,194,129,137,22,14,154,180,45,25,41,38,0,160,13,33,32,77,65,38,77,195,150,112,227,152,72,200,8,46,68,164,112,25,71,44,100,22,142,84,4,50,19,36,146,209,182,128,36,197,132,3,33,110,147,166,45,212,160,68,81,48,144,66,66,130,146,192,112,163,182,44,209,162,32,34,165,96,161,50,109,28,19,6,17,24,108,193,184,128,64,48,96,11,181,32,217,6,104,76,54,8,84,36,114,220,32,82,72,50,42,3,52,98,17,73,36,219,146,132,36,56,137,217,18,44,72,162,141,208,18,144,1,161,81,227,146,40,65,150,144,80,130,108,96,0,74,209,40,112,19,72,74,82,152,13,26,181,48,25,55,129,211,56,100,12,37,14,90,166,137,154,196,112,3,180,77,92,68,133,138,54,73,1,193,104,2,16,137,209,182,132,19,131,17,11,185,8,0,3,68,68,184,13,0,0,38,9,36,145,200,8,65,216,178,96,32,146,101,26,131,132,224,24,106,154,2,129,68,4,68,17,182,32,28,41,50,148,162,13,16,178,73,64,150,64,28,7,78,27,69,48,28,198,0,193,152,40,84,66,68,216,18,8,152,22,34,16,135,65,9,192,65,32,64,108,9,33,45,0,50,80,130,32,140,74,40,141,155,50,144,226,164,5,97,66,106,34,53,42,20,72,46,9,166,9,11,6,113,211,160,145,3,64,48,226,152,80,139,16,41,145,168,97,224,166,133,208,54,134,89,22,137,26,49,145,204,198,132,36,22,128,17,177,129,216,34,10,12,70,108,35,20,12,73,4,44,72,178,97,192,184,105,19,192,32,208,152,104,20,64,68,130,144,40,218,40,96,224,52,112,145,148,16,20,149,41,138,64,34,83,164,76,217,180,16,33,184,112,220,8,98,138,16,14,89,146,140,2,57,36,76,6,50,163,34,14,163,0,2,28,33,80,96,8,108,3,34,12,12,3,128,17,152,37,220,144,136,36,134,33,82,64,9,226,144,81,164,196,136,220,198,141,128,40,5,4,0,68,100,24,65,24,197,136,2,185,36,16,64,5,216,16,104,11,184,69,228,52,33,72,16,45,201,24,134,19,130,16,218,148,37,203,52,66,33,16,141,91,164,40,65,166,9,209,34,70,90,152,129,76,20,10,145,68,2,18,195,136,3,73,9,67,198,16,0,73,34,225,32,74,200,68,37,210,134,137,75,144,1,9,134,96,89,160,129,218,16,48,36,56,65,26,162,109,161,198,44,75,136,12,140,176,140,194,132,36,10,23,105,98,176,65,28,70,132,81,148,64,3,18,18,160,52,74,12,184,145,8,53,73,34,168,137,203,178,113,211,22,80,212,160,129,9,66,112,194,56,17,4,165,44,163,34,81,32,0,114,220,168,40,3,2,96,3,64,137,89,4,113,200,130,36,128,50,37,25,54,106,164,54,106,163,18,49,32,200,141,16,65,73,65,164,16,219,2,141,27,1,40,138,136,44,28,129,8,81,40,5,73,194,65,90,0,132,163,8,128,140,36,110,196,146,64,218,66,106,80,162,104,82,36,141,156,6,114,4,198,68,92,6,70,66,194,65,194,2,48,36,176,72,32,67,32,84,50,100,73,176,109,26,40,134,66,184,32,80,72,32,153,2,98,16,23,0,161,0,145,245,104,38,14,65,179,182,1,252,0,76,146,216,170,29,22,249,198,40,122,155,162,23,203,92,16,98,169,96,112,46,225,31,89,107,227,142,142,224,230,194,64,185,121,86,69,55,44,211,248,235,170,179,190,109,10,252,66,233,101,120,225,217,51,83,211,74,221,109,84,136,78,245,82,21,46,235,182,182,195,108,87,146,209,216,230,77,206,31,46,75,86,109,73,135,16,218,6,77,188,180,47,197,85,244,194,121,206,24,212,29,218,50,95,149,98,118,83,78,23,139,130,205,130,36,167,181,34,183,159,228,234,114,52,176,40,69,121,216,20,160,162,237,83,110,147,12,182,134,159,193,195,38,10,130,83,21,136,44,148,208,2,83,224,96,131,51,231,39,184,93,91,21,240,183,186,111,12,146,1,105,46,223,105,89,56,145,57,117,80,92,226,5,61,3,100,198,174,92,177,226,205,16,143,9,105,210,113,124,9,239,31,169,157,66,218,242,167,229,17,47,189,32,67,188,220,36,204,167,240,174,30,183,184,7,173,52,50,40,67,199,173,253,197,65,90,96,155,26,249,170,231,212,151,7,215,95,212,99,207,175,238,139,195,55,212,116,22,24,240,38,159,38,192,37,158,74,91,86,195,181,34,230,0,95,16,28,209,219,23,134,188,145,163,148,135,170,141,249,119,98,73,52,48,216,134,199,173,182,207,216,46,69,103,52,150,5,116,92,170,144,198,77,6,242,131,10,92,219,61,132,8,88,249,224,70,44,63,180,81,155,204,98,115,237,134,225,68,33,231,158,141,227,213,231,51,177,61,200,14,32,140,120,14,137,52,31,50,95,59,175,180,14,79,107,91,177,103,120,43,254,43,136,40,173,37,246,225,253,62,176,72,219,117,29,31,118,43,253,0,26,66,5,48,212,162,134,168,73,161,187,124,103,40,114,218,142,247,152,204,150,171,27,125,25,116,187,191,65,1,95,86,147,99,152,106,166,66,33,188,121,125,149,247,26,222,140,224,110,161,165,215,162,23,49,222,18,250,107,164,102,126,31,22,116,9,175,218,28,99,8,50,108,204,188,176,178,223,220,35,76,140,87,234,44,14,224,1,97,45,32,97,13,5,157,227,167,112,86,128,203,225,177,253,100,123,190,154,77,240,207,212,100,122,41,189,155,240,246,157,39,22,103,228,164,84,81,42,139,188,148,57,13,204,9,252,22,221,31,122,76,48,8,34,140,103,252,202,234,29,143,132,73,184,57,42,123,57,94,60,104,176,159,164,253,166,8,26,77,4,124,50,237,76,134,228,128,22,102,165,216,37,122,159,64,182,118,29,57,140,3,242,6,44,56,200,110,170,142,177,254,81,213,224,138,147,92,239,61,75,212,96,33,148,122,187,246,87,119,146,46,148,118,2,92,79,239,209,112,130,255,210,240,39,114,221,184,34,37,37,227,5,250,191,129,122,68,135,251,81,154,97,192,125,10,111,63,37,205,253,10,46,127,38,71,175,246,180,238,3,148,84,254,211,214,181,8,109,238,70,27,42,241,245,254,116,114,185,167,208,157,205,182,217,27,176,49,199,41,193,163,148,144,173,85,208,237,135,61,88,237,249,237,35,115,57,61,89,201,220,247,37,93,114,219,60,110,53,130,131,99,27,103,161,119,210,22,48,196,206,40,182,216,72,220,48,139,62,104,63,196,200,80,185,232,87,1,121,109,240,76,82,136,249,54,118,56,137,138,14,68,66,251,245,231,131,102,47,232,35,1,197,223,160,214,62,224,162,22,134,109,168,109,146,22,34,106,186,188,84,225,21,49,154,123,8,29,28,63,76,63,106,85,184,117,254,182,220,241,4,153,218,151,172,204,198,175,35,15,47,90,236,190,145,208,34,91,163,30,4,103,217,53,99,91,227,179,142,107,152,218,28,87,126,110,170,64,98,144,238,112,175,62,23,94,253,234,173,250,53,242,79,205,79,160,19,107,176,17,186,149,171,121,191,66,25,112,249,237,12,225,83,2,186,65,36,34,117,165,107,131,136,196,87,165,184,40,57,161,32,208,167,225,49,60,234,92,33,128,129,24,252,88,126,35,53,190,199,45,74,242,234,99,13,157,109,118,122,108,81,236,156,192,67,254,79,39,195,139,184,213,17,111,86,164,83,203,244,187,169,156,113,137,249,198,46,191,193,135,12,214,142,252,143,152,112,62,128,129,240,115,78,16,137,113,204,255,194,193,35,23,21,170,53,240,32,166,126,59,75,159,61,196,196,163,29,39,32,61,106,82,103,39,220,74,97,141,77,144,181,100,215,2,43,149,84,200,142,13,152,246,212,102,250,42,143,119,127,131,55,149,18,70,38,46,144,64,159,234,43,32,210,157,168,9,87,230,78,67,27,225,64,75,203,135,203,106,198,84,180,66,111,8,34,84,159,29,236,101,144,3,171,138,39,186,231,248,216,255,131,189,224,141,107,71,157,167,150,47,158,174,204,76,4,253,133,103,168,225,247,203,139,107,152,245,141,20,54,160,255,16,164,130,100,28,30,155,248,235,236,21,192,233,25,129,122,56,62,29,129,223,216,96,246,208,46,185,203,170,122,121,104,52,121,95,87,48,45,148,141,206,82,247,194,80,89,196,5,209,75,31,252,162,176,1,157,201,242,43,216,238,217,164,111,2,89,143,120,231,77,241,50,114,185,46,94,177,38,36,222,252,211,77,226,9,171,121,204,171,71,84,87,130,106,93,72,237,137,31,133,101,113,68,175,234,96,39,38,199,21,159,107,77,209,81,47,15,89,102,213,119,81,118,149,254,249,250,163,156,151,23,159,16,89,113,19,61,190,37,200,36,134,55,13,245,10,202,20,130,30,187,178,75,58,133,136,35,43,69,192,181,31,27,179,96,109,35,52,49,117,199,204,67,157,218,123,50,160,91,201,226,26,11,82,114,133,92,0,39,61,168,36,179,164,107,192,15,40,1,39,188,43,28,169,226,146,63,25,236,88,59,183,220,154,58,158,119,74,162,150,95,41,41,194,67,207,86,196,52,221,209,234,127,235,49,45,34,208,181,170,214,165,5,143,159,31,81,65,184,41,47,119,173,130,214,184,74,19,12,231,63,201,108,235,135,105,33,63,28,157,244,10,15,251,50,141,65,44,238,237,27,212,26,237,93,14,115,62,194,241,213,248,98,80,58,235,17,109,2,26,83,110,252,88,246,45,30,222,166,240,227,116,148,179,162,117,7,48,105,40,192,234,59,54,112,225,7,195,123,22,139,3,10,157,153,133,8,32,108,53,188,168,33,116,140,9,233,204,9,245,38,88,231,102,15,184,184,141,237,212,235,158,98,47,182,252,102,208,22,112,75,58,118,204,226,209,177,220,212,231,215,120,163,101,108,91,153,192,57,33,141,158,210,249,23,60,67,248,103,118,61,244,241,89,163,169,117,49,232,41,243,135,159,211,172,176,115,53,18,211,87,187,20,69,133,68,185,157,20,8,178,68,114,233,77,168,248,243,199,99,32,50,89,248,211,34,201,62,186,91,64,67,173,176,37,245,158,179,145,114,172,21,74,136,112,164,96,45,87,110,176,163,173,220,248,113,159,103,39,3,33,157,168,61,118,75,20,31,94,249,27,220,167,44,251,61,91,182,187,21,212,69,195,42,94,238,12,1,136,178,196,208,217,178,126,252,210,79,213,184,248,99,217,17,96,57,214,90,98,34,207,181,77,142,253,253,196,167,132,245,247,153,236,47,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,3,166,59,195,12,211,93,68,8,73,212,169,81,201,46,231,74,162,153,110,81,197,17,33,199,26,102,208,122,104,52,207,38,215,250,125,66,231,196,39,11,76,38,106,119,57,223,51,224,176,181,25,45,166,56,84,24,246,248,143,206,211,185,64,102,123,41,1,70,43,59,167,65,94,175,70,141,218,215,213,32,22,124,94,115,102,168,74,125,180,149,149,127,142,11,64,39,145,22,16,250,47,48,174,58,155,117,170,108,243,208,177,100,217,39,116,40,76,5,203,237,210,163,118,148,156,172,112,86,185,131,37,111,115,233,190,130,69,237,16,207,146,141,103,114,100,201,9,169,72,64,150,245,18,203,123,125,241,167,147,78,41,212,29,70,169,145,50,123,12,165,57,193,211,62,248,112,126,144,229,21,9,123,111,127,158,137,236,226,142,149,190,142,222,213,89,243,231,202,161,246,152,207,74,206,182,251,22,132,205,132,191,248,63,65,27,32,76,230,148,217,236,37,198,17,193,130,245,72,174,99,132,91,1,170,167,96,152,231,136,195,250,77,4,39,128,224,138,150,179,90,102,18,23,148,59,203,99,61,157,199,112,196,11,7,98,58,32,166,2,55,120,43,108,84,157,243,234,178,247,143,110,128,94,25,179,34,30,139,178,41,133,254,76,49,221,225,135,161,56,245,90,41,215,72,10,104,158,68,93,111,181,42,126,1,37,138,63,145,142,22,60,254,124,222,125,196,227,109,34,78,71,55,3,109,147,223,40,165,81,147,13,57,210,234,108,162,69,191,71,155,91,164,112,187,212,227,72,32,204,100,94,245,205,6,230,72,87,138,92,40,148,211,32,236,1,73,99,29,106,108,116,113,254,210,62,158,103,227,126,47,194,112,55,46,82,92,204,225,138,74,170,29,179,131,183,77,234,186,174,144,53,36,159,53,85,86,203,229,174,196,234,131,99,140,226,84,236,205,197,31,143,15,188,126,214,115,111,168,156,84,14,190,140,145,209,118,177,19,98,25,179,183,191,156,95,80,89,158,173,219,222,135,65,162,210,165,177,127,95,161,162,58,248,132,251,241,102,170,186,211,97,170,200,137,78,46,219,140,66,22,208,219,19,189,29,255,254,222,204,145,14,19,238,99,201,161,103,91,8,249,122,165,255,162,107,115,211,120,41,78,130,219,142,209,254,103,82,216,49,0,205,40,105,223,59,121,55,38,254,162,241,41,51,209,143,48,190,236,0,200,146,239,137,193,223,117,205,56,186,70,137,210,1,224,142,241,2,157,31,239,12,138,141,95,216,247,225,64,45,53,248,165,19,6,178,225,95,111,218,164,218,4,49,142,71,244,78,51,29,11,140,121,230,188,22,232,13,33,140,174,115,255,162,244,112,55,29,16,91,58,28,4,158,85,171,96,151,235,235,213,247,0,90,65,227,119,2,54,104,66,185,57,182,76,234,160,170,218,119,123,162,126,79,54,35,58,36,249,117,110,76,7,114,139,135,140,63,139,133,19,128,196,12,51,35,165,76,158,54,87,133,1,58,210,75,203,183,193,34,255,21,114,182,215,122,191,149,134,135,240,198,182,60,66,1,176,144,143,132,36,184,125,62,56,124,218,85,28,245,142,60,18,0,169,99,88,215,123,181,34,16,43,239,207,40,188,127,255,91,8,224,144,125,221,248,27,4,48,212,148,75,103,62,118,7,89,35,186,154,190,95,114,217,115,213,188,217,102,217,174,90,19,7,150,26,111,68,163,109,227,219,228,109,215,110,161,69,169,114,79,226,72,109,221,84,104,224,97,94,246,85,73,156,24,153,16,115,106,16,91,129,251,74,243,51,233,109,229,76,147,48,54,63,197,74,246,209,59,47,98,205,112,178,103,149,12,106,33,168,211,92,82,32,56,184,224,158,20,30,193,27,198,209,252,37,162,248,86,7,199,142,194,110,158,155,197,254,80,36,93,102,163,82,245,75,84,215,28,99,238,82,143,123,215,75,97,226,217,245,210,178,126,46,68,243,162,54,11,32,157,156,83,51,142,52,209,160,113,116,24,79,249,60,19,23,0,186,248,75,105,200,166,160,150,157,146,148,13,26,22,142,103,115,81,196,41,131,196,91,52,126,26,43,146,102,123,63,56,150,255,21,50,12,49,84,75,76,213,86,187,150,249,166,130,105,69,173,203,137,154,120,224,57,211,249,26,188,48,63,1,21,123,245,169,12,118,247,86,112,102,238,244,162,110,249,19,160,173,79,138,225,183,192,62,28,30,150,151,47,122,75,91,178,110,70,209,50,60,80,194,45,213,214,82,139,117,186,27,11,237,207,253,227,251,108,21,161,133,182,38,114,115,139,78,119,149,17,146,125,126,208,191,38,220,130,205,56,145,3,94,38,182,232,65,151,170,104,55,255,88,93,49,181,42,44,52,213,129,189,246,253,216,250,139,139,122,20,192,129,73,36,160,209,164,71,94,252,225,18,2,200,200,64,119,51,167,59,92,122,86,83,254,10,148,164,244,12,126,25,2,231,175,46,228,164,57,10,158,197,159,85,50,1,1,186,67,40,24,67,129,231,92,115,36,217,110,4,83,150,139,162,136,54,152,183,168,184,19,167,222,190,209,105,173,6,227,129,209,109,186,227,201,123,23,47,184,65,83,16,213,21,214,227,52,36,26,177,250,27,16,227,188,63,32,191,48,155,154,30,159,86,90,155,144,196,84,190,79,242,3,152,213,10,98,16,165,146,116,123,100,186,242,53,72,60,238,218,0,21,114,236,109,242,82,185,236,91,113,49,79,184,8,249,122,113,153,177,15,127,178,79,59,17,165,8,248,32,76,33,180,169,92,71,107,125,42,42,4,95,108,166,6,244,25,207,247,84,42,13 }; + + for (int j = 0; j < 32; j++) { + r = crypto_sign_dilithium_ed25519_keypair_seed(pk2, sk2, seed1); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_keypair_seed failed %d", (int)r); + return -3; + } + + for (int k = 0; k < 32 + 1312; k++) { + if (pk[k] != pkFromSeed[k]) { + printf("\n deterministic generation failed pkFromSeed: pk %d,%d,%d", k, pk[k], pkFromSeed[k]); + return -4; + } + if (pk[k] != pk2[k]) { + printf("\n deterministic generation failed: pk"); + return -5; + } + } + + for (int k = 0; k < 64 + 2560 + 1312; k++) { + if (sk[k] != skFromSeed[k]) { + printf("\n deterministic generation failed skFromSeed: sk"); + return -6; + } + if (sk[k] != sk2[k]) { + printf("\n deterministic generation failed: sk"); + return -7; + } + } + + r = crypto_sign_dilithium_ed25519_keypair_seed(pk3, sk3, seed3); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_keypair_seed failed %d", (int)r); + return -8; + } + + int matchCount = 0; + for (int k = 0; k < 32 + 1312; k++) { + if (pk2[k] == pk3[k]) { + matchCount++; + } + } + if (matchCount == 32 + 1312) { + printf("\n deterministic generation failed repeat: pk"); + return -9; + } + + matchCount = 0; + for (int k = 0; k < 64 + 2560 + 1312; k++) { + if (sk2[k] == sk3[k]) { + matchCount++; + } + } + if (matchCount == 64 + 2560 + 1312) { + printf("\n deterministic generation failed repeat: pk"); + return -10; + } + + r = randombytes(msg1, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -11; + } + + r = crypto_sign_dilithium_ed25519(sig1, &sigLen1, msg1, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519 failed %d", (int)r); + return -12; + } + + if (sigLen1 != 2518) { + printf("\n crypto_sign_dilithium_ed25519 sigLen error %d", (int)sigLen1); + return -13; + } + + r = crypto_sign_dilithium_ed25519_open(msg1output, &msgLen1, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_open failed %d", (int)r); + return -14; + } + + if (msgLen1 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen1); + return -15; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg1[i] != msg1output[i]) { + printf("\n verify msg content failed %d", i); + return -16; + } + } + } + + printf("\n test_hybrid_dilithium_deterministic() ok"); return 0; } int test_multiple(int count) { printf("\n test_multiple hybrid %d", count); for (int i = 0;i < count;i++) { - int r = test_hybrid(); + int r = test_hybrid_falcon(); if (r != 0) { return 2; } @@ -577,12 +1161,27 @@ int test_multiple(int count) { } int main(int argc, char* argv[]) { + int rdh = test_hybrid_dilithium(); + if (rdh != 0) { + return rdh; + } + + rdh = test_hybrid_dilithium_deterministic(); + if (rdh != 0) { + return rdh; + } + + int rd = test_dilithium(); + if (rd != 0) { + return rd; + } + int r0 = test_falcon(); if (r0 != 0) { return r0; } - int r1 = test_hybrid(); + int r1 = test_hybrid_falcon(); if (r1 != 0) { return r1; } @@ -593,16 +1192,19 @@ int main(int argc, char* argv[]) { return r3; } - r3 = test_hybrid_deterministic(); + r3 = test_hybrid_falcon_deterministic(); if (r3 != 0) { return r3; } int perfCount = 1000; - if (argc > 1) { - perfCount = atoi(argv[1]); - } - test_hybrid_perf(perfCount); + test_hybrid_falcon_perf(perfCount); + + /*int rs = test_sphincs(); + if (rs != 0) { + return rs; + }*/ + printf("\n Warning, perf tests uses approximate system clock. Is not suitable for fewer iterations of test."); printf(" \n test suite completed!"); From 1ec732977fda39923d3bc8dfd5b60535aecfef8c Mon Sep 17 00:00:00 2001 From: Blake Hartin <122081314+blakehartin@users.noreply.github.com> Date: Wed, 22 Nov 2023 22:36:22 -0800 Subject: [PATCH 2/3] Fix randombytes buffer (critical) --- random/randombytes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/random/randombytes.c b/random/randombytes.c index c73c257..d6cf8f0 100644 --- a/random/randombytes.c +++ b/random/randombytes.c @@ -85,7 +85,7 @@ static int randombytes_win32_randombytes(void* buf, size_t n) DWORD BufferSize; NTSTATUS Status; - BufferSize = sizeof(buf); + BufferSize = n; memset(buf, 0, BufferSize); Status = BCryptGenRandom( From 8451ee09a19a484e951b0c6cf91e1aa4d92afc73 Mon Sep 17 00:00:00 2001 From: Blake Hartin Date: Sat, 25 Nov 2023 00:49:16 +0000 Subject: [PATCH 3/3] Hybrid ed25519-dilithium-sphincs --- CMakeLists.txt | 11 +- common/hybrid-common.c | 32 ++ common/hybrid-common.h | 35 ++ hybrid-dilithium-sphincs/hybrid.c | 632 +++++++++++++++++++++++++++ hybrid-dilithium-sphincs/hybrid.h | 49 +++ hybrid-dilithium/hybrid.c | 110 +++-- {hybrid => hybrid-falcon}/hybrid.c | 25 +- {hybrid => hybrid-falcon}/hybrid.h | 0 random/.github/workflows/test.yml | 45 ++ random/README.md | 2 - random/randombytes.c | 8 +- random/randombytes_test.c | 217 ++++++++++ sphincs/Makefile | 2 +- sphincs/Makefile.Microsoft_nmake | 2 +- sphincs/api.h | 54 +-- sphincs/params.h | 12 +- sphincs/sign.c | 29 +- tests/test_hybrid.c | 664 ++++++++++++++++++++++++++--- 18 files changed, 1710 insertions(+), 219 deletions(-) create mode 100644 common/hybrid-common.c create mode 100644 common/hybrid-common.h create mode 100644 hybrid-dilithium-sphincs/hybrid.c create mode 100644 hybrid-dilithium-sphincs/hybrid.h rename {hybrid => hybrid-falcon}/hybrid.c (92%) rename {hybrid => hybrid-falcon}/hybrid.h (100%) create mode 100644 random/.github/workflows/test.yml create mode 100644 random/randombytes_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0509d09..ebdbb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ set(HYBRIDPQC_VERSION_TEXT "0.0.1-dev") set(HYBRIDPQC_COMPILE_BUILD_TARGET "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_HOST_SYSTEM}") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set( CMAKE_C_FLAGS "-fstack-protector -fstack-protector-all" ) +set( CMAKE_CXX_FLAGS "-fstack-protector -fstack-protector-all" ) if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") set(ARCH "x86_64") @@ -130,7 +132,7 @@ endif() include(GNUInstallDirs) include(CMakePackageConfigHelpers) -set(SOURCE_FILES falcon512/codec.c falcon512/common.c falcon512/fft.c falcon512/fpr.c falcon512/keygen.c falcon512/nist.c falcon512/rng.c falcon512/shake.c falcon512/sign.c falcon512/vrfy.c dilithium2/ntt.c dilithium2/ntt.h dilithium2/packing.c dilithium2/packing.h dilithium2/params.h dilithium2/poly.c dilithium2/poly.h dilithium2/polyvec.c dilithium2/polyvec.h dilithium2/reduce.c dilithium2/reduce.h dilithium2/rounding.c dilithium2/rounding.h dilithium2/sign.c dilithium2/sign.h dilithium2/symmetric.h dilithium2/symmetric-shake.c sphincs/address.c sphincs/context_shake.c sphincs/fors.c sphincs/hash_shake.c sphincs/merkle.c sphincs/sign.c sphincs/thash_shake_simple.c sphincs/utils.c sphincs/utilsx1.c sphincs/wots.c sphincs/wotsx1.c random/randombytes.c common/fips202.c "tweetnacl/tweetnacl.c" "hybrid/hybrid.h" "hybrid/hybrid.c" "hybrid-dilithium/hybrid.h" "hybrid-dilithium/hybrid.c") +set(SOURCE_FILES falcon512/codec.c falcon512/common.c falcon512/fft.c falcon512/fpr.c falcon512/keygen.c falcon512/nist.c falcon512/rng.c falcon512/shake.c falcon512/sign.c falcon512/vrfy.c dilithium2/ntt.c dilithium2/ntt.h dilithium2/packing.c dilithium2/packing.h dilithium2/params.h dilithium2/poly.c dilithium2/poly.h dilithium2/polyvec.c dilithium2/polyvec.h dilithium2/reduce.c dilithium2/reduce.h dilithium2/rounding.c dilithium2/rounding.h dilithium2/sign.c dilithium2/sign.h dilithium2/symmetric.h dilithium2/symmetric-shake.c sphincs/address.c sphincs/context_shake.c sphincs/fors.c sphincs/hash_shake.c sphincs/merkle.c sphincs/sign.c sphincs/thash_shake_simple.c sphincs/utils.c sphincs/utilsx1.c sphincs/wots.c sphincs/wotsx1.c random/randombytes.c common/fips202.c "common/hybrid-common.c" "tweetnacl/tweetnacl.c" "hybrid-falcon/hybrid.h" "hybrid-falcon/hybrid.c" "hybrid-dilithium/hybrid.h" "hybrid-dilithium/hybrid.c" "hybrid-dilithium-sphincs/hybrid.h" "hybrid-dilithium-sphincs/hybrid.c") add_library(hybridpqc SHARED ${SOURCE_FILES}) add_executable(hybridpqctest ${SOURCE_FILES} "tests/test_hybrid.c") @@ -219,4 +221,9 @@ install(FILES ${HYBRIDPQC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hybridpqc) execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/include/hybridpqc) -execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${HYBRIDPQC_HEADERS} ${PROJECT_BINARY_DIR}/include/hybridpqc) \ No newline at end of file +execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${HYBRIDPQC_HEADERS} ${PROJECT_BINARY_DIR}/include/hybridpqc) + +# enable testing functionality +enable_testing() + +add_test(NAME hybridpqctest COMMAND $) \ No newline at end of file diff --git a/common/hybrid-common.c b/common/hybrid-common.c new file mode 100644 index 0000000..8d1ee6f --- /dev/null +++ b/common/hybrid-common.c @@ -0,0 +1,32 @@ +#include "hybrid-common.h" + +const int MIN_MSG_LEN = 1; +const int MAX_MSG_LEN = 64; +const int LEN_BYTES = 2; + +const int SEED_LENGTH_ED25519 = 32; +const int CRYPTO_ED25519_PUBLICKEY_BYTES = 32; +const int CRYPTO_ED25519_SECRETKEY_BYTES = 64; +const int CRYPTO_ED25519_SIGNATURE_BYTES = 64; +const int CRYPTO_ED25519_SECRETKEY_WITHOUT_PUBLIC_KEY_BYTES = 32; + +const int SEED_LENGTH_DILITHIUM = 32; +const int CRYPTO_DILITHIUM_PUBLICKEY_BYTES = 1312; +const int CRYPTO_DILITHIUM_SECRETKEY_BYTES = 2560; +const int CRYPTO_DILITHIUM_SIGNATURE_BYTES = 2420; + +const int SEED_LENGTH_SPHINCS = 96; +const int CRYPTO_SPHINCS_PUBLICKEY_BYTES = 64; +const int CRYPTO_SPHINCS_SECRETKEY_BYTES = 128; +const int CRYPTO_SPHINCS_SIGNATURE_BYTES = 49856; + +const int SEED_LENGTH_FALCON = 48; +const int CRYPTO_FALCON_PUBLICKEY_BYTES = 897; +const int CRYPTO_FALCON_SECRETKEY_BYTES = 1281; +const int CRYPTO_FALCON_SECRETKEY_WITH_PUBLIC_KEY_BYTES = 1281 + 897; +const int CRYPTO_FALCON_NONCE_LENGTH = 40; +const int CRYPTO_FALCON_MIN_SIGNATURE_BYTES = 600 + 40 + 2; //Signature + Nonce + 2 for size +const int CRYPTO_FALCON_MAX_SIGNATURE_BYTES = 690 + 40 + 2; //Signature + Nonce + 2 for size + +const int NONCE_BYTES = 40; +const int HASH_LENGTH = 64; \ No newline at end of file diff --git a/common/hybrid-common.h b/common/hybrid-common.h new file mode 100644 index 0000000..34aacae --- /dev/null +++ b/common/hybrid-common.h @@ -0,0 +1,35 @@ +#ifndef HYBRIDCOMMON_H +#define HYBRIDCOMMON_H + +extern const int MIN_MSG_LEN; +extern const int MAX_MSG_LEN; +extern const int LEN_BYTES; + +extern const int SEED_LENGTH_ED25519; +extern const int CRYPTO_ED25519_PUBLICKEY_BYTES; +extern const int CRYPTO_ED25519_SECRETKEY_BYTES; +extern const int CRYPTO_ED25519_SIGNATURE_BYTES; +extern const int CRYPTO_ED25519_SECRETKEY_WITHOUT_PUBLIC_KEY_BYTES; + +extern const int SEED_LENGTH_DILITHIUM; +extern const int CRYPTO_DILITHIUM_PUBLICKEY_BYTES; +extern const int CRYPTO_DILITHIUM_SECRETKEY_BYTES; +extern const int CRYPTO_DILITHIUM_SIGNATURE_BYTES; + +extern const int SEED_LENGTH_SPHINCS; +extern const int CRYPTO_SPHINCS_PUBLICKEY_BYTES; +extern const int CRYPTO_SPHINCS_SECRETKEY_BYTES; +extern const int CRYPTO_SPHINCS_SIGNATURE_BYTES; + +extern const int SEED_LENGTH_FALCON; +extern const int CRYPTO_FALCON_PUBLICKEY_BYTES; +extern const int CRYPTO_FALCON_SECRETKEY_BYTES; +extern const int CRYPTO_FALCON_SECRETKEY_WITH_PUBLIC_KEY_BYTES; +extern const int CRYPTO_FALCON_NONCE_LENGTH; +extern const int CRYPTO_FALCON_MIN_SIGNATURE_BYTES; +extern const int CRYPTO_FALCON_MAX_SIGNATURE_BYTES; + +extern const int NONCE_BYTES; +extern const int HASH_LENGTH; + +#endif \ No newline at end of file diff --git a/hybrid-dilithium-sphincs/hybrid.c b/hybrid-dilithium-sphincs/hybrid.c new file mode 100644 index 0000000..4c236de --- /dev/null +++ b/hybrid-dilithium-sphincs/hybrid.c @@ -0,0 +1,632 @@ +/* + * Hybrid Post Quantum Cryptography Library + * + * ==========================(LICENSE BEGIN)============================ + +The MIT License + +Copyright (c) 2023 Doge Protocol Community + +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. + + * ===========================(LICENSE END)============================= + * + * WARNING! This is an experimental cryptography library. It is not ready for use yet in any production systems!! + * + * + */ + +#include +#include +#include +#include +#include "hybrid.h" +#include "../common/hybrid-common.h" +#include "../common/fips202.h" +#include "../dilithium2/api.h" +#include "../sphincs/api.h" +#include "../tweetnacl/tweetnacl.h" +#include "../random/randombytes.h" + +/* +Secret Key Length = 64 + 2560 + 1312 + 128 = 4064 +================================================== +Layout of secret key: + +64 bytes 2560 bytes 1312 bytes 128 bytes +ed25519 secret key with public key | dilithium secret key | dilithium public key | sphincs secret key with public key + +The following signature length includes implementation output, in addition to actual algorithm output. + +Layout of ED25519 signature +============================ + +64 bytes | {1 to 64 bytes} +ed25519 Signature | Message + +Layout of Dilithium signature +============================== +2420 bytes +dilithium signature + +Layout of Sphincs signature +============================== +49856 bytes +dilithium signature + +Layout of Public Key +============================== +32 bytes | 1312 bytes | 64 bytes +ed25519 public key | dilithium public key | sphincs public key + + +Compact Signature +================== +================== +The compact signature scheme does not sign the message using sphincs+, but only using ed25519 and dilithium. During any emergency event, such as if both ed25519 and dilithium are broken or potential attacks found, +the SPHINCS+ key can be used to prove authenticity of signatures signed earlier or enabled for newer signatures with the same key pair. + +In the compact signature mode, a new message digest is created from the original message digest and then hashed using sha3-512. This new message is signed by ed25519 and dilithium + +Hybrid Signature Message (compact mode) +========================================= + +40 bytes | {0 to 64 bytes} | 64 bytes +random nonce | original message | sphincs public key + +hybrid-message-hash = SHA3-512(compact-mode-message) + +Hybrid Signature Length (compact mode) = 2 + 64 + {1 to 64} + 2420 + 64 + 40 +======================================================================================================================= +Layout of signature: + +2 bytes | 64 bytes | 2420 bytres | 40 bytes | {1 to 64 bytes} +length of message | ed25519 signature | dilithium signature | random nonce | original message + +Full Signature +================== +================== + +Hybrid Signature Length (full, used during breakglass) = 2 + 64 + {1 to 64} + 2420 + 49856 +======================================================================================================================= +Layout of signature: + +2 bytes | 64 bytes | {1 to 64 bytes} | 2420 bytes | 49856 +length of message | ed25519 signature | original message | dilithium signature | sphincs signature + +The first 2 bytes contain message length +Message is variable length, between 1 to 64 bytes +*/ + +int crypto_sign_dilithium_ed25519_sphincs_keypair_seed(unsigned char* pk, unsigned char* sk, unsigned char* seed) { + if (pk == NULL || sk == NULL || seed == NULL) { + return -1; + } + + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + unsigned char pk3[64] = { 0 }; //CRYPTO_SPHINCS_PUBLICKEY_BYTES + unsigned char sk3[128] = { 0 }; //CRYPTO_SPHINCS_SECRETKEY_BYTES + + unsigned char seed1[32] = { 0 }; //SEED_LENGTH_ED25519 + unsigned char seed2[32] = { 0 }; //SEED_LENGTH_DILITHIUM + unsigned char seed3[96] = { 0 }; //SEED_LENGTH_SPHINCS + + for (int i = 0; i < SEED_LENGTH_ED25519; i++) { + seed1[i] = seed[i]; + } + + for (int i = 0; i < SEED_LENGTH_DILITHIUM; i++) { + seed2[i] = seed[i + SEED_LENGTH_ED25519]; + } + + for (int i = 0; i < SEED_LENGTH_SPHINCS; i++) { + seed3[i] = seed[i + SEED_LENGTH_ED25519 + SEED_LENGTH_DILITHIUM]; + } + + int r1 = crypto_sign_ed25519_keypair_seed(pk1, sk1, seed1); + if (r1 != 0) { + return -2; + } + + for (int i = 0; i < CRYPTO_ED25519_PUBLICKEY_BYTES; i++) { + pk[i] = pk1[i]; + } + + for (int i = 0; i < CRYPTO_ED25519_SECRETKEY_BYTES; i++) { //secret key includes public key + sk[i] = sk1[i]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk2, sk2, seed2); + + if (r2 != 0) { + return -3; + } + + for (int i = 0; i < CRYPTO_DILITHIUM_SECRETKEY_BYTES; i++) { + sk[CRYPTO_ED25519_SECRETKEY_BYTES + i] = sk2[i]; + } + + for (int i = 0; i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES; i++) { + pk[CRYPTO_ED25519_PUBLICKEY_BYTES + i] = pk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key + } + + int r3 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk3, sk3, seed3); + + if (r3 != 0) { + return -4; + } + + for (int i = 0; i < CRYPTO_SPHINCS_SECRETKEY_BYTES; i++) { + sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES + i] = sk3[i]; + } + + for (int i = 0; i < CRYPTO_SPHINCS_PUBLICKEY_BYTES; i++) { + pk[CRYPTO_ED25519_PUBLICKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES + i] = pk3[i]; + } + + return 0; +} + +int crypto_sign_dilithium_ed25519_sphincs_keypair(unsigned char* pk, unsigned char* sk) { + if (pk == NULL || sk == NULL) { + return -1; + } + + unsigned char seed[160]; + if (randombytes(seed, sizeof seed) != 0) { + return -1; + } + return crypto_sign_dilithium_ed25519_sphincs_keypair_seed(pk, sk, seed); +} + +int crypto_sign_dilithium_ed25519_sphincs(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk) { + + if (sm == NULL || smlen == NULL || m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN || sk == NULL) { + return -1; + } + + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long sigLen3 = 0; + + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + unsigned char sig3[49856] = { 0 }; //CRYPTO_SPHINCS_SIGNATURE_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES + unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + unsigned char sk3[128] = { 0 }; //CRYPTO_SPHINCS_SECRETKEY_BYTES + + //Copy sk1 from input + for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES;i++) { + sk1[i] = sk[i]; + } + + //Copy sk2 from input (skip public key part of it) + for (int i = 0;i < CRYPTO_DILITHIUM_SECRETKEY_BYTES;i++) { + sk2[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES + i]; + } + + //Copy sk3 from input (skip public key part of it) + for (int i = 0; i < CRYPTO_SPHINCS_SECRETKEY_BYTES; i++) { + sk3[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES + i]; + } + + //Always call both sign operations, instead of exiting early from first on failure, to reduce risk from timing attacks if any + int r1 = crypto_sign_ed25519(sig1, &sigLen1, m, mlen, sk1); + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(sig2, &sigLen2, m, mlen, sk2); + int r3 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_signature(sig3, &sigLen3, m, mlen, sk3); + + if (r1 != 0) { + return -2; + } + + if (r2 != 0) { + return -3; + } + + if (r3 != 0) { + return -4; + } + + if (sigLen1 != CRYPTO_ED25519_SIGNATURE_BYTES + mlen) { + return -5; + } + + if (sigLen2 != CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -6; + } + + if (sigLen3 != CRYPTO_SPHINCS_SIGNATURE_BYTES) { + return -6; + } + + //Set totalLen of sig, excluding LEN_BYTES + unsigned long long msgLen = mlen; + sm[0] = (unsigned char)(msgLen >> 8); + sm[1] = (unsigned char)msgLen; + + //Copy ed25519 signature (which includes the message), to output + for (int i = 0;i < (int)sigLen1;i++) { + sm[LEN_BYTES + i] = sig1[i]; + } + + //Copy the dilithium signature to the output + for (int i = 0;i < sigLen2;i++) { + sm[LEN_BYTES + sigLen1 + i] = sig2[i]; + } + + //Copy the sphincs signature to the output + for (int i = 0; i < sigLen3; i++) { + sm[LEN_BYTES + sigLen1 + sigLen2 + i] = sig3[i]; + } + + *smlen = LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + CRYPTO_SPHINCS_SIGNATURE_BYTES + mlen; + + return 0; +} + +int crypto_sign_dilithium_ed25519_sphincs_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MIN_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES + CRYPTO_SPHINCS_SIGNATURE_BYTES || + smlen > LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES + CRYPTO_SPHINCS_SIGNATURE_BYTES || pk == NULL) { + return -1; + } + + int msgLen = ((size_t)sm[0] << 8) | (size_t)sm[1]; + if (msgLen <= 0 || msgLen > MAX_MSG_LEN) { + return -2; + } + + if (smlen != LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + msgLen + CRYPTO_DILITHIUM_SIGNATURE_BYTES + CRYPTO_SPHINCS_SIGNATURE_BYTES) { + return -3; + } + + int sig1Len = CRYPTO_ED25519_SIGNATURE_BYTES + msgLen; + + unsigned char msgFromSignature1[64 + 64] = { 0 }; //MAX_MSG_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + unsigned long long msgFromSignatureLen1 = 0; + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_SIGNATURE_BYTES + unsigned char sig3[49856] = { 0 }; //CRYPTO_SPHINCS_SIGNATURE_BYTES + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + unsigned char pk3[64] = { 0 }; //CRYPTO_SPHINCS_PUBLICKEY_BYTES + + //Copy Sig1 from source, including message + for (int i = 0;i < (int)sig1Len;i++) { + sig1[i] = sm[LEN_BYTES + i]; + } + + //Copy pk1 from source + for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES;i++) { + pk1[i] = pk[i]; + } + + int r1 = crypto_sign_ed25519_open(msgFromSignature1, &msgFromSignatureLen1, sig1, sig1Len, pk1); + if (r1 != 0) { + return -4; + } + + if ((int) msgFromSignatureLen1 != msgLen) { + return -5; + } + + //Copy actual Sig2 from source + for (int i = 0; i < CRYPTO_DILITHIUM_SIGNATURE_BYTES; i++) { + sig2[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + msgLen + i]; + } + + + //Copy pk2 from source + for (int i = 0;i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES;i++) { + pk2[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(sig2, CRYPTO_DILITHIUM_SIGNATURE_BYTES, msgFromSignature1, msgLen, pk2); + if (r2 != 0) { + return -6; + } + + //Copy actual Sig3 from source + for (int i = 0; i < CRYPTO_SPHINCS_SIGNATURE_BYTES; i++) { + sig3[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + msgLen + CRYPTO_DILITHIUM_SIGNATURE_BYTES + i]; + } + + //Copy pk3 from source + for (int i = 0; i < CRYPTO_SPHINCS_PUBLICKEY_BYTES; i++) { + pk3[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES]; + } + + int r3 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_verify(sig3, CRYPTO_SPHINCS_SIGNATURE_BYTES, msgFromSignature1, msgLen, pk3); + if (r3 != 0) { + return -7; + } + + for (int i = 0; i < msgLen; i++) { + m[i] = msgFromSignature1[i]; + } + + *mlen = msgLen; + + return 0; +} + +int crypto_verify_dilithium_ed25519_sphincs(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL|| mlen <= 0 || mlen > MAX_MSG_LEN || sm == NULL || pk == NULL) { //smlen is checked in crypto_sign_dilithium_ed25519_open + return -1; + } + + unsigned char msgFromSignature1[64 + 64 + 32 + 64] = { 0 }; //MAX_MSG_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_ED25519_PUBLICKEY_BYTES + CRYPTO_SPHINCS_PUBLICKEY_BYTES + unsigned long long msgFromSignatureLen1 = 0; + + int r = crypto_sign_dilithium_ed25519_sphincs_open(msgFromSignature1, &msgFromSignatureLen1, sm, smlen, pk); + if (r != 0) { + return -2; + } + + if (msgFromSignatureLen1 != mlen) { + return -3; + } + + for (int i = 0;i < (int)msgFromSignatureLen1;i++) { + if (msgFromSignature1[i] != m[i]) { + return -4; + } + } + + return 0; +} + +int crypto_sign_compact_dilithium_ed25519_sphincs(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk) { + + if (sm == NULL || smlen == NULL || m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN || sk == NULL) { + return -1; + } + + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + HASH_LENGTH + unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES + unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES + unsigned char hybridMsg[40 + 64 + 64] = { 0 }; //NONCE_BYTES + MAX_MSG_LEN + CRYPTO_SPHINCS_PUBLICKEY_BYTES + unsigned char hybridMessageHash[64]; //sha3-512 HASH_LENGTH + unsigned char nonce[40]; //NONCE_BYTES + + //Copy sk1 from input + for (int i = 0; i < CRYPTO_ED25519_SECRETKEY_BYTES; i++) { + sk1[i] = sk[i]; + } + + //Copy sk2 from input (skip public key part of it) + for (int i = 0; i < CRYPTO_DILITHIUM_SECRETKEY_BYTES; i++) { + sk2[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES + i]; + } + + //Form the hybrid msg + if (randombytes(nonce, sizeof nonce) != 0) { //Create nonce + return -1; + } + + for (int i = 0; i < NONCE_BYTES; i++) { //copy nonce to hybridMsg + hybridMsg[i] = nonce[i]; + } + + for (int i = 0; i < mlen; i++) { //copy original message to hybridMsg + hybridMsg[NONCE_BYTES + i] = m[i]; + } + + for (int i = 0; i < CRYPTO_SPHINCS_PUBLICKEY_BYTES; i++) { //Copy the SPHINCS+ public-key to hybridMsg + hybridMsg[NONCE_BYTES + mlen + i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES + (CRYPTO_SPHINCS_SECRETKEY_BYTES / 2) + i]; //(CRYPTO_SPHINCS_SECRETKEY_BYTES / 2) since public key is part of secret key starting from position 64 + } + + //Hash the hybrid message + sha3_512(hybridMessageHash, hybridMsg, NONCE_BYTES + mlen + CRYPTO_SPHINCS_PUBLICKEY_BYTES); + + //Always call both sign operations, instead of exiting early from first on failure, to reduce risk from timing attacks if any + int r1 = crypto_sign_ed25519(sig1, &sigLen1, hybridMessageHash, HASH_LENGTH, sk1); + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_signature(sig2, &sigLen2, hybridMessageHash, HASH_LENGTH, sk2); + + if (r1 != 0) { + return -2; + } + + if (r2 != 0) { + return -3; + } + + if (sigLen1 != CRYPTO_ED25519_SIGNATURE_BYTES + HASH_LENGTH) { + return -4; + } + + if (sigLen2 != CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + return -5; + } + + //Store the original message length + unsigned long long msgLen = mlen; + sm[0] = (unsigned char)(msgLen >> 8); + sm[1] = (unsigned char)msgLen; + + //Copy ed25519 signature to output + for (int i = 0; i < CRYPTO_ED25519_SIGNATURE_BYTES; i++) { + sm[LEN_BYTES + i] = sig1[i]; + } + + //Copy the dilithium signature to the output + for (int i = 0; i < CRYPTO_DILITHIUM_SIGNATURE_BYTES; i++) { + sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + i] = sig2[i]; + } + + //Copy the nonce to the output + for (int i = 0; i < NONCE_BYTES; i++) { + sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + i] = nonce[i]; + } + + //Copy the original message to the output + for (int i = 0; i < mlen; i++) { + sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + i] = m[i]; + } + + *smlen = LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + mlen; + + return 0; +} + +int crypto_sign_compact_dilithium_ed25519_sphincs_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + MIN_MSG_LEN || + smlen > LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + MAX_MSG_LEN || pk == NULL) { + return -1; + } + + int msgLen = ((size_t)sm[0] << 8) | (size_t)sm[1]; + if (msgLen <= 0 || msgLen > MAX_MSG_LEN) { + return -2; + } + + if (smlen != LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + msgLen) { + return -3; + } + + unsigned char msgFromSignature1[64 + 64] = { 0 }; //HASH_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + unsigned long long msgFromSignatureLen1 = 0; + unsigned char hybridMsg[40 + 64 + 64] = { 0 }; //NONCE_BYTES + MAX_MSG_LEN + CRYPTO_SPHINCS_PUBLICKEY_BYTES + unsigned char hybridMessageHash[64]; //sha3-512 HASH_LENGTH + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + HASH_LEN + unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_SIGNATURE_BYTES + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES + unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES + + //Form the hybrid msg + for (int i = 0; i < NONCE_BYTES; i++) { //copy nonce to hybridMsg + hybridMsg[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + i]; + } + for (int i = 0; i < msgLen; i++) { //copy original message to hybridMsg + hybridMsg[NONCE_BYTES + i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + i]; + } + for (int i = 0; i < CRYPTO_SPHINCS_PUBLICKEY_BYTES; i++) { //Copy the SPHINCS+ public-key to hybridMsg + hybridMsg[NONCE_BYTES + msgLen + i] = pk[CRYPTO_ED25519_PUBLICKEY_BYTES + CRYPTO_DILITHIUM_PUBLICKEY_BYTES + i]; + } + + //Hash the hybrid message + sha3_512(hybridMessageHash, hybridMsg, NONCE_BYTES + msgLen + CRYPTO_SPHINCS_PUBLICKEY_BYTES); + + + //Copy Sig1 from source + for (int i = 0; i < CRYPTO_ED25519_SIGNATURE_BYTES; i++) { + sig1[i] = sm[LEN_BYTES + i]; + } + + //Copy hybridMessageHash into Sig1 + for (int i = 0; i < HASH_LENGTH; i++) { + sig1[CRYPTO_ED25519_SIGNATURE_BYTES + i] = hybridMessageHash[i]; + } + + //Copy pk1 from source + for (int i = 0; i < CRYPTO_ED25519_PUBLICKEY_BYTES; i++) { + pk1[i] = pk[i]; + } + + int r1 = crypto_sign_ed25519_open(msgFromSignature1, &msgFromSignatureLen1, sig1, CRYPTO_ED25519_SIGNATURE_BYTES + HASH_LENGTH, pk1); + if (r1 != 0) { + return -4; + } + + if (msgFromSignatureLen1 != HASH_LENGTH) { + return -5; + } + + //Verify hybridMessageHash from message + for (int i = 0; i < HASH_LENGTH; i++) { + if (msgFromSignature1[i] != hybridMessageHash[i]) { + return -6; + } + } + + //Copy actual Sig2 from source + for (int i = 0; i < CRYPTO_DILITHIUM_SIGNATURE_BYTES; i++) { + sig2[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + i]; + } + + //Copy pk2 from source + for (int i = 0; i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES; i++) { + pk2[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES]; + } + + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_verify(sig2, CRYPTO_DILITHIUM_SIGNATURE_BYTES, hybridMessageHash, HASH_LENGTH, pk2); + if (r2 != 0) { + return -7; + } + + //Copy the message to the output + for (int i = 0; i < msgLen; i++) { + m[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + NONCE_BYTES + i]; + } + + *mlen = msgLen; + + return 0; +} + +int crypto_verify_compact_dilithium_ed25519_sphincs(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk) { + + if (m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN || sm == NULL || pk == NULL) { //smlen is checked in crypto_sign_dilithium_ed25519_open + return -1; + } + + unsigned char msgFromSignature1[64] = { 0 }; //MAX_MSG_LEN + unsigned long long msgFromSignatureLen1 = 0; + + int r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msgFromSignature1, &msgFromSignatureLen1, sm, smlen, pk); + if (r != 0) { + return -2; + } + + if (msgFromSignatureLen1 != mlen) { + return -3; + } + + for (int i = 0; i < (int)msgFromSignatureLen1; i++) { + if (msgFromSignature1[i] != m[i]) { + return -4; + } + } + + return 0; +} + diff --git a/hybrid-dilithium-sphincs/hybrid.h b/hybrid-dilithium-sphincs/hybrid.h new file mode 100644 index 0000000..00e655a --- /dev/null +++ b/hybrid-dilithium-sphincs/hybrid.h @@ -0,0 +1,49 @@ + +#if defined(_WIN32) +#define HYBRID_API __declspec(dllexport) +#else +#define HYBRID_API __attribute__((visibility("default"))) +#endif + +#if defined(OQS_SYS_UEFI) +#undef HYBRID_API +#define HYBRID_API +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CRYPTO_DILITHIUM_HYBRID_ALGNAME "dilithium-ed25519-sphincs" + +HYBRID_API int crypto_sign_dilithium_ed25519_sphincs_keypair_seed(unsigned char* pk, unsigned char* sk, unsigned char* seed); //seed needs to be 64 chars in length (32 + 32 + 96) + +HYBRID_API int crypto_sign_dilithium_ed25519_sphincs_keypair(unsigned char* pk, unsigned char* sk); + +HYBRID_API int crypto_sign_dilithium_ed25519_sphincs(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk); + +HYBRID_API int crypto_sign_dilithium_ed25519_sphincs_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +HYBRID_API int crypto_verify_dilithium_ed25519_sphincs(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +HYBRID_API int crypto_sign_compact_dilithium_ed25519_sphincs(unsigned char* sm, unsigned long long* smlen, + const unsigned char* m, unsigned long long mlen, + const unsigned char* sk); + +HYBRID_API int crypto_sign_compact_dilithium_ed25519_sphincs_open(unsigned char* m, unsigned long long* mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +HYBRID_API int crypto_verify_compact_dilithium_ed25519_sphincs(const unsigned char* m, unsigned long long mlen, + const unsigned char* sm, unsigned long long smlen, + const unsigned char* pk); + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/hybrid-dilithium/hybrid.c b/hybrid-dilithium/hybrid.c index 3c20b02..7912976 100644 --- a/hybrid-dilithium/hybrid.c +++ b/hybrid-dilithium/hybrid.c @@ -37,25 +37,11 @@ THE SOFTWARE. #include #include #include "hybrid.h" +#include "../common/hybrid-common.h" #include "../dilithium2/api.h" #include "../tweetnacl/tweetnacl.h" #include "../random/randombytes.h" -const int SEED_LENGTH_ED25519_2 = 32; -const int SEED_LENGTH_DILITHIUM = 32; - -const int MIN_MSG_LEN_2 = 1; -const int MAX_MSG_LEN_2 = 64; - -const int CRYPTO_ED25519_PUBLICKEY_BYTES_2 = 32; -const int CRYPTO_ED25519_SECRETKEY_BYTES_2 = 64; -const int CRYPTO_ED25519_SIGNATURE_BYTES_2 = 64; - -const int LEN_BYTES_2 = 2; -const int CRYPTO_DILITHIUM_PUBLICKEY_BYTES = 1312; -const int CRYPTO_DILITHIUM_SECRETKEY_BYTES = 2560; -const int CRYPTO_DILITHIUM_SIGNATURE_BYTES = 2420; - /* Secret Key Length = 64 + 2560 + 1312 = 3587 ============================================ @@ -98,20 +84,20 @@ int crypto_sign_dilithium_ed25519_keypair_seed(unsigned char* pk, unsigned char* return -1; } - unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 - unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES unsigned char sk2[2560 + 1312] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES - unsigned char seed1[32] = { 0 }; //SEED_LENGTH_ED25519_2 + unsigned char seed1[32] = { 0 }; //SEED_LENGTH_ED25519 unsigned char seed2[48] = { 0 }; //SEED_LENGTH_DILITHIUM - for (int i = 0; i < SEED_LENGTH_ED25519_2; i++) { + for (int i = 0; i < SEED_LENGTH_ED25519; i++) { seed1[i] = seed[i]; } for (int i = 0; i < SEED_LENGTH_DILITHIUM; i++) { - seed2[i] = seed[i + SEED_LENGTH_ED25519_2]; + seed2[i] = seed[i + SEED_LENGTH_ED25519]; } int r1 = crypto_sign_ed25519_keypair_seed(pk1, sk1, seed1); @@ -119,27 +105,27 @@ int crypto_sign_dilithium_ed25519_keypair_seed(unsigned char* pk, unsigned char* return -2; } - for (int i = 0; i < CRYPTO_ED25519_PUBLICKEY_BYTES_2; i++) { + for (int i = 0; i < CRYPTO_ED25519_PUBLICKEY_BYTES; i++) { pk[i] = pk1[i]; } - for (int i = 0; i < CRYPTO_ED25519_SECRETKEY_BYTES_2; i++) { + for (int i = 0; i < CRYPTO_ED25519_SECRETKEY_BYTES; i++) { sk[i] = sk1[i]; } - int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk2, sk2, seed2, sizeof seed2); + int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_keypair_seed(pk2, sk2, seed2); if (r2 != 0) { return -3; } for (int i = 0; i < CRYPTO_DILITHIUM_SECRETKEY_BYTES; i++) { - sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i] = sk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES + i] = sk2[i]; } for (int i = 0; i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES; i++) { - pk[CRYPTO_ED25519_PUBLICKEY_BYTES_2 + i] = pk2[i]; - sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key + pk[CRYPTO_ED25519_PUBLICKEY_BYTES + i] = pk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key } @@ -151,8 +137,8 @@ int crypto_sign_dilithium_ed25519_keypair(unsigned char* pk, unsigned char* sk) return -1; } - unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 - unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES @@ -161,11 +147,11 @@ int crypto_sign_dilithium_ed25519_keypair(unsigned char* pk, unsigned char* sk) return -2; } - for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES_2;i++) { + for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES;i++) { pk[i] = pk1[i]; } - for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES_2;i++) { + for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES;i++) { sk[i] = sk1[i]; } @@ -176,12 +162,12 @@ int crypto_sign_dilithium_ed25519_keypair(unsigned char* pk, unsigned char* sk) } for (int i = 0;i < CRYPTO_DILITHIUM_SECRETKEY_BYTES;i++) { - sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i] = sk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES + i] = sk2[i]; } for (int i = 0;i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES;i++) { - pk[CRYPTO_ED25519_PUBLICKEY_BYTES_2 + i] = pk2[i]; - sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key + pk[CRYPTO_ED25519_PUBLICKEY_BYTES + i] = pk2[i]; + sk[CRYPTO_ED25519_SECRETKEY_BYTES + CRYPTO_DILITHIUM_SECRETKEY_BYTES + i] = pk2[i]; //copy public key } @@ -192,26 +178,26 @@ int crypto_sign_dilithium_ed25519(unsigned char* sm, unsigned long long* smlen, const unsigned char* m, unsigned long long mlen, const unsigned char* sk) { - if (sm == NULL || smlen == NULL || m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN_2 || sk == NULL) { + if (sm == NULL || smlen == NULL || m == NULL || mlen <= 0 || mlen > MAX_MSG_LEN || sk == NULL) { return -1; } unsigned long long sigLen1 = 0; unsigned long long sigLen2 = 0; - unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN unsigned char sig2[2420] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES - unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES_2 + unsigned char sk1[64] = { 0 }; //CRYPTO_ED25519_SECRETKEY_BYTES unsigned char sk2[2560] = { 0 }; //CRYPTO_DILITHIUM_SECRETKEY_BYTES //Copy sk1 from input - for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES_2;i++) { + for (int i = 0;i < CRYPTO_ED25519_SECRETKEY_BYTES;i++) { sk1[i] = sk[i]; } //Copy sk2 from input (skip public key part of it) for (int i = 0;i < CRYPTO_DILITHIUM_SECRETKEY_BYTES;i++) { - sk2[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES_2 + i]; + sk2[i] = sk[CRYPTO_ED25519_SECRETKEY_BYTES + i]; } //Always call both sign operations, instead of exiting early from first on failure, to reduce risk from timing attacks if any @@ -226,7 +212,7 @@ int crypto_sign_dilithium_ed25519(unsigned char* sm, unsigned long long* smlen, return -3; } - if (sigLen1 != CRYPTO_ED25519_SIGNATURE_BYTES_2 + mlen) { + if (sigLen1 != CRYPTO_ED25519_SIGNATURE_BYTES + mlen) { return -3; } @@ -234,22 +220,22 @@ int crypto_sign_dilithium_ed25519(unsigned char* sm, unsigned long long* smlen, return -4; } - //Set totalLen of sig, excluding LEN_BYTES_2 + //Set totalLen of sig, excluding LEN_BYTES unsigned long long totalLen = sigLen1 + sigLen2; sm[0] = (unsigned char)(totalLen >> 8); sm[1] = (unsigned char)totalLen; //Copy ed25519 signature (which includes the message), to output for (int i = 0;i < (int)sigLen1;i++) { - sm[LEN_BYTES_2 + i] = sig1[i]; + sm[LEN_BYTES + i] = sig1[i]; } //Copy the Dilithium signature to the output for (int i = 0;i < sigLen2;i++) { - sm[LEN_BYTES_2 + sigLen1 + i] = sig2[i]; + sm[LEN_BYTES + sigLen1 + i] = sig2[i]; } - *smlen = LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES + mlen; + *smlen = LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_DILITHIUM_SIGNATURE_BYTES + mlen; return 0; @@ -259,46 +245,46 @@ int crypto_sign_dilithium_ed25519_open(unsigned char* m, unsigned long long* mle const unsigned char* sm, unsigned long long smlen, const unsigned char* pk) { - if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + MIN_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || - smlen > LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || pk == NULL) { + if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MIN_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES || + smlen > LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES || pk == NULL) { return -1; } int totalLen = ((size_t)sm[0] << 8) | (size_t)sm[1]; - if (totalLen < CRYPTO_ED25519_SIGNATURE_BYTES_2 + MIN_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES || totalLen > CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + if (totalLen < CRYPTO_ED25519_SIGNATURE_BYTES + MIN_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES || totalLen > CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { return -2; } - int msgLen = totalLen - CRYPTO_ED25519_SIGNATURE_BYTES_2 - CRYPTO_DILITHIUM_SIGNATURE_BYTES; - if (msgLen <= 0 || msgLen > MAX_MSG_LEN_2) { + int msgLen = totalLen - CRYPTO_ED25519_SIGNATURE_BYTES - CRYPTO_DILITHIUM_SIGNATURE_BYTES; + if (msgLen <= 0 || msgLen > MAX_MSG_LEN) { return -3; } - if (smlen != LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { + if (smlen != LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + msgLen + CRYPTO_DILITHIUM_SIGNATURE_BYTES) { return -15; } - int sig1Len = CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen; + int sig1Len = CRYPTO_ED25519_SIGNATURE_BYTES + msgLen; int sig2Len = totalLen - sig1Len; if (sig2Len != CRYPTO_DILITHIUM_SIGNATURE_BYTES) { return -4; } - unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_ED25519_PUBLICKEY_BYTES unsigned long long msgFromSignatureLen1 = 0; - unsigned char msgFromSignature2[64] = { 0 }; //MAX_MSG_LEN_2 + unsigned char msgFromSignature2[64] = { 0 }; //MAX_MSG_LEN unsigned long long msgFromSignatureLen2 = 0; - unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES_2 + MAX_MSG_LEN_2 - unsigned char sig2[2420 + 64] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + MAX_MSG_LEN_2 - unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char sig1[64 + 64] = { 0 }; //CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + unsigned char sig2[2420 + 64] = { 0 }; //CRYPTO_DILITHIUM_MAX_SIGNATURE_BYTES + MAX_MSG_LEN + unsigned char pk1[32] = { 0 }; //CRYPTO_ED25519_PUBLICKEY_BYTES unsigned char pk2[1312] = { 0 }; //CRYPTO_DILITHIUM_PUBLICKEY_BYTES //Copy Sig1 from source, including message for (int i = 0;i < (int)sig1Len;i++) { - sig1[i] = sm[LEN_BYTES_2 + i]; + sig1[i] = sm[LEN_BYTES + i]; } //Copy pk1 from source - for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES_2;i++) { + for (int i = 0;i < CRYPTO_ED25519_PUBLICKEY_BYTES;i++) { pk1[i] = pk[i]; } @@ -313,17 +299,17 @@ int crypto_sign_dilithium_ed25519_open(unsigned char* m, unsigned long long* mle //Copy actual Sig2 from source for (int i = 0; i < CRYPTO_DILITHIUM_SIGNATURE_BYTES; i++) { - sig2[i] = sm[LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + msgLen + i]; + sig2[i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + msgLen + i]; } //Copy Message into sig2 for (int i = 0;i < msgLen;i++) { - sig2[CRYPTO_DILITHIUM_SIGNATURE_BYTES + i] = sm[LEN_BYTES_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + i]; + sig2[CRYPTO_DILITHIUM_SIGNATURE_BYTES + i] = sm[LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + i]; } //Copy pk2 from source for (int i = 0;i < CRYPTO_DILITHIUM_PUBLICKEY_BYTES;i++) { - pk2[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES_2]; + pk2[i] = pk[i + CRYPTO_ED25519_PUBLICKEY_BYTES]; } int r2 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign_open(msgFromSignature2, &msgFromSignatureLen2, sig2, sig2Len + msgLen, pk2); @@ -351,11 +337,11 @@ int crypto_verify_dilithium_ed25519(const unsigned char* m, unsigned long long m const unsigned char* sm, unsigned long long smlen, const unsigned char* pk) { - if (m == NULL|| mlen <= 0 || mlen > MAX_MSG_LEN_2 || sm == NULL || pk == NULL) { + if (m == NULL|| mlen <= 0 || mlen > MAX_MSG_LEN || sm == NULL || pk == NULL) { return -1; } - unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN_2 + CRYPTO_ED25519_SIGNATURE_BYTES_2 + CRYPTO_ED25519_PUBLICKEY_BYTES_2 + unsigned char msgFromSignature1[64 + 64 + 32] = { 0 }; //MAX_MSG_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + CRYPTO_ED25519_PUBLICKEY_BYTES unsigned long long msgFromSignatureLen1 = 0; int r = crypto_sign_dilithium_ed25519_open(msgFromSignature1, &msgFromSignatureLen1, sm, smlen, pk); diff --git a/hybrid/hybrid.c b/hybrid-falcon/hybrid.c similarity index 92% rename from hybrid/hybrid.c rename to hybrid-falcon/hybrid.c index 7ed92dc..1d19efd 100644 --- a/hybrid/hybrid.c +++ b/hybrid-falcon/hybrid.c @@ -37,30 +37,11 @@ THE SOFTWARE. #include #include #include "hybrid.h" +#include "../common/hybrid-common.h" #include "../falcon512/api.h" #include "../tweetnacl/tweetnacl.h" #include "../random/randombytes.h" -const int SEED_LENGTH_ED25519 = 32; -const int SEED_LENGTH_FALCON = 48; - -const int MIN_MSG_LEN = 1; -const int MAX_MSG_LEN = 64; -const int SIZE_LEN = 2; //2 for size - -const int CRYPTO_ED25519_PUBLICKEY_BYTES = 32; -const int CRYPTO_ED25519_SECRETKEY_BYTES = 64; -const int CRYPTO_ED25519_SECRETKEY_WITHOUT_PUBLIC_KEY_BYTES = 32; -const int CRYPTO_ED25519_SIGNATURE_BYTES = 64; - -const int LEN_BYTES = 2; -const int CRYPTO_FALCON_PUBLICKEY_BYTES = 897; -const int CRYPTO_FALCON_SECRETKEY_BYTES = 1281; -const int CRYPTO_FALCON_SECRETKEY_WITH_PUBLIC_KEY_BYTES = 1281 + 897; -const int CRYPTO_FALCON_NONCE_LENGTH = 40; -const int CRYPTO_FALCON_MIN_SIGNATURE_BYTES = 600 + 40 + 2; //Signature + Nonce + 2 for size -const int CRYPTO_FALCON_MAX_SIGNATURE_BYTES = 690 + 40 + 2; //Signature + Nonce + 2 for size - const int CRYPTO_HYBRID_PUBLICKEY_BYTES = 32 + 897; const int CRYPTO_HYBRID_SECRETKEY_BYTES = 64 + 1281; const int CRYPTO_HYBRID_SECRETKEY_WITH_FALCON_PUBLIC_KEY_BYTES = 64 + 1281 + 897; //ED25519 already contains public key @@ -290,8 +271,8 @@ int crypto_sign_falcon_ed25519_open(unsigned char* m, unsigned long long* mlen, const unsigned char* sm, unsigned long long smlen, const unsigned char* pk) { - if (m == NULL || mlen == NULL || sm == NULL || smlen < SIZE_LEN + SIZE_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + MIN_MSG_LEN + CRYPTO_FALCON_MIN_SIGNATURE_BYTES + MIN_MSG_LEN || - smlen > SIZE_LEN + SIZE_LEN + CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + CRYPTO_FALCON_MAX_SIGNATURE_BYTES + MAX_MSG_LEN || pk == NULL) { + if (m == NULL || mlen == NULL || sm == NULL || smlen < LEN_BYTES + LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MIN_MSG_LEN + CRYPTO_FALCON_MIN_SIGNATURE_BYTES + MIN_MSG_LEN || + smlen > LEN_BYTES + LEN_BYTES + CRYPTO_ED25519_SIGNATURE_BYTES + MAX_MSG_LEN + CRYPTO_FALCON_MAX_SIGNATURE_BYTES + MAX_MSG_LEN || pk == NULL) { return -1; } diff --git a/hybrid/hybrid.h b/hybrid-falcon/hybrid.h similarity index 100% rename from hybrid/hybrid.h rename to hybrid-falcon/hybrid.h diff --git a/random/.github/workflows/test.yml b/random/.github/workflows/test.yml new file mode 100644 index 0000000..20591aa --- /dev/null +++ b/random/.github/workflows/test.yml @@ -0,0 +1,45 @@ +--- +name: Tests +on: [push, pull_request] + +jobs: + Test-Randombytes: + runs-on: "${{ matrix.os }}" + strategy: + matrix: + cc: + - gcc + - clang + os: + - ubuntu-latest + - macos-latest + env: + CC: "${{ matrix.cc }}" + steps: + - uses: actions/checkout@v3 + - run: | + make check + + Test-Randombytes-Musl: + runs-on: ubuntu-latest + env: + CC: musl-gcc + steps: + - uses: actions/checkout@v3 + - name: Install musl-tools + run: sudo apt-get install -y musl-tools + - run: make check + + Test-Randombytes-Windows: + runs-on: windows-latest + strategy: + matrix: + arch: + - x64 + - x86 + steps: + - uses: actions/checkout@v3 + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.arch }} + - run: cl /c /nologo /W3 /WX randombytes.c diff --git a/random/README.md b/random/README.md index 1f7c620..4226a13 100644 --- a/random/README.md +++ b/random/README.md @@ -1,7 +1,5 @@ # Pluggable randombytes function -[![GitHub Actions](https://github.com/dsprenkels/randombytes/workflows/Tests/badge.svg)](https://github.com/dsprenkels/randombytes/actions/workflows/test.yml) - `randombytes` is a library that exposes a single function for retrieving _crypto-secure_ random bytes. It is loosely based on [Libsodium's random bytes API][libsodium_randombytes]. If you can, you should use that one. Otherwise, you diff --git a/random/randombytes.c b/random/randombytes.c index d6cf8f0..8b9aa95 100644 --- a/random/randombytes.c +++ b/random/randombytes.c @@ -82,16 +82,14 @@ #if defined(_WIN32) static int randombytes_win32_randombytes(void* buf, size_t n) { - DWORD BufferSize; NTSTATUS Status; - BufferSize = n; - memset(buf, 0, BufferSize); + memset(buf, 0, n); Status = BCryptGenRandom( NULL, // Alg Handle pointer; NUll is passed as BCRYPT_USE_SYSTEM_PREFERRED_RNG flag is used buf, // Address of the buffer that recieves the random number(s) - BufferSize, // Size of the buffer in bytes + n, // Size of the buffer in bytes BCRYPT_USE_SYSTEM_PREFERRED_RNG); // Flags if (NT_SUCCESS(Status)) @@ -351,7 +349,7 @@ static int randombytes_js_randombytes_nodejs(void *buf, size_t n) { errno = ENOSYS; return -1; } - return ret; + return -3; assert(false); // Unreachable } #endif /* defined(__EMSCRIPTEN__) */ diff --git a/random/randombytes_test.c b/random/randombytes_test.c new file mode 100644 index 0000000..cd476ca --- /dev/null +++ b/random/randombytes_test.c @@ -0,0 +1,217 @@ +#include "randombytes.c" +#include +#include +#include +#include +#include + +static void *current_test = NULL; +static int syscall_called = 0; +static int glib_getrandom_called = 0; + +// ======== Helper macros and functions ======== + +#define RUN_TEST(name) \ + printf("%s ... ", #name); \ + padto(' ', sizeof(#name) + sizeof(" ... "), 32); \ + current_test = name; \ + name(); \ + printf("ok\n"); +#define SKIP_TEST(name) \ + printf("%s ... ", #name); \ + padto(' ', sizeof(#name) + sizeof(" ... "), 32); \ + printf("skipped\n"); + +static void padto(const char c, const size_t curlen, const size_t len) { + for (size_t i = curlen; i < len; i++) { + putchar(c); + } +} + +// ======== Forward declarations needed for mocked functions ======== + +#if defined(__linux__) && defined(SYS_getrandom) +int __wrap_syscall(int n, char *buf, size_t buflen, int flags); +int __real_syscall(int n, char *buf, size_t buflen, int flags); +#endif /* defined(__linux__) && defined(SYS_getrandom) */ + +#if defined(__linux__) && !defined(SYS_getrandom) +int __wrap_ioctl(int fd, int code, int* ret); +int __real_ioctl(int fd, int code, int* ret); +#endif /* defined(__linux__) && !defined(SYS_getrandom) */ + +// ======== Test definitions ======== + +static void test_functional(void) { + uint8_t buf1[20] = {}, buf2[sizeof(buf1)] = {}; + const int ret1 = randombytes(buf1, sizeof(buf1)); + const int ret2 = randombytes(buf2, sizeof(buf2)); + if (ret1 != 0 || ret2 != 0) { + printf("error: %s\n", strerror(errno)); + } + assert(ret1 == 0); + assert(ret2 == 0); + assert(memcmp(buf1, buf2, sizeof(buf1)) != 0); +} + +static void test_empty(void) { + const uint8_t zero[20] = {}; + uint8_t buf[sizeof(zero)] = {}; + const int ret = randombytes(buf, 0); + assert(ret == 0); + assert(memcmp(buf, zero, sizeof(zero)) == 0); +} + +static void test_getrandom_syscall_partial(void) { + syscall_called = 0; + uint8_t buf[100] = {}; + const int ret = randombytes(buf, sizeof(buf)); + assert(ret == 0); + assert(syscall_called >= 5); + for (int i = 1; i < 5; i++) { + assert(memcmp(&buf[0], &buf[20*i], 20) != 0); + } +} + +static void test_getrandom_syscall_interrupted(void) { + syscall_called = 0; + uint8_t zero[20] = {}; + uint8_t buf[sizeof(zero)] = {}; + const int ret = randombytes(buf, sizeof(buf)); + assert(ret == 0); + assert(memcmp(buf, zero, 20) != 0); +} + +static void test_getrandom_glib_partial(void) { + glib_getrandom_called = 0; + uint8_t buf[100] = {}; + const int ret = randombytes(buf, sizeof(buf)); + assert(ret == 0); + assert(glib_getrandom_called >= 5); + for (int i = 1; i < 5; i++) { + assert(memcmp(&buf[0], &buf[20*i], 20) != 0); + } +} + +static void test_getrandom_glib_interrupted(void) { + glib_getrandom_called = 0; + uint8_t zero[20] = {}; + uint8_t buf[sizeof(zero)] = {}; + const int ret = randombytes(buf, sizeof(buf)); + assert(ret == 0); + assert(memcmp(buf, zero, 20) != 0); +} + +static void test_issue_17(void) { + uint8_t buf1[20] = {}, buf2[sizeof(buf1)] = {}; + const int ret1 = randombytes(buf1, sizeof(buf1)); + const int ret2 = randombytes(buf2, sizeof(buf2)); + assert(ret1 == 0); + assert(ret2 == 0); + assert(memcmp(buf1, buf2, sizeof(buf1)) != 0); +} + +static void test_issue_22(void) { + uint8_t buf1[20] = {}, buf2[sizeof(buf1)] = {}; + const int ret1 = randombytes(buf1, sizeof(buf1)); + const int ret2 = randombytes(buf2, sizeof(buf2)); + assert(ret1 == 0); + assert(ret2 == 0); + assert(memcmp(buf1, buf2, sizeof(buf1)) != 0); +} + +static void test_issue_33(void) { + for (size_t idx = 0; idx < 100000; idx++) { + uint8_t buf[20] = {}; + const int ret = randombytes(&buf, sizeof(buf)); + if (ret != 0) { + printf("error: %s\n", strerror(errno)); + } + assert(ret == 0); + } +} + +// ======== Mock OS functions to simulate uncommon behavior ======== + +#if defined(__linux__) && defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24)) +int __wrap_getrandom(char *buf, size_t buflen, int flags) { + glib_getrandom_called++; + if (current_test == test_getrandom_glib_partial) { + // Fill only 16 bytes, the caller should retry + const size_t current_buflen = buflen <= 16 ? buflen : 16; + return __real_getrandom(buf, current_buflen, flags); + } else if (current_test == test_getrandom_glib_interrupted) { + if (glib_getrandom_called < 5) { + errno = EINTR; + return -1; + } + } + return __real_getrandom(buf, buflen, flags); +} + +#elif defined(__linux__) && defined(SYS_getrandom) +int __wrap_syscall(int n, char *buf, size_t buflen, int flags) { + syscall_called++; + if (current_test == test_getrandom_syscall_partial) { + // Fill only 16 bytes, the caller should retry + const size_t current_buflen = buflen <= 16 ? buflen : 16; + return __real_syscall(n, buf, current_buflen, flags); + } else if (current_test == test_getrandom_syscall_interrupted) { + if (syscall_called < 5) { + errno = EINTR; + return -1; + } + } + return __real_syscall(n, buf, buflen, flags); +} +#endif /* defined(__linux__) && (defined(SYS_getrandom) or glibc version > 2.24) */ + +#if defined(__linux__) && !defined(SYS_getrandom) +int __wrap_ioctl(int fd, int code, int* ret) { + if (current_test == test_issue_17) { + errno = ENOTTY; + return -1; + } + if (current_test == test_issue_17) { + errno = ENOSYS; + return -1; + } + return __real_ioctl(fd, code, ret); +} +#endif /* defined(__linux__) && !defined(SYS_getrandom) */ + +// ======== Main function ======== + +int main(void) { + // Use `#if defined()` to enable/disable tests on a platform. If disabled, + // please still call `SKIP_TEST` to make sure no tests are skipped silently. + + RUN_TEST(test_functional) + RUN_TEST(test_empty) +#if defined(__linux__) && defined(USE_GLIBC) + RUN_TEST(test_getrandom_glib_partial) + RUN_TEST(test_getrandom_glib_interrupted) + SKIP_TEST(test_getrandom_syscall_partial) + SKIP_TEST(test_getrandom_syscall_interrupted) +#elif defined(__linux__) && defined(SYS_getrandom) + SKIP_TEST(test_getrandom_glib_partial) + SKIP_TEST(test_getrandom_glib_interrupted) + RUN_TEST(test_getrandom_syscall_partial) + RUN_TEST(test_getrandom_syscall_interrupted) +#else + SKIP_TEST(test_getrandom_glib_partial) + SKIP_TEST(test_getrandom_glib_interrupted) + SKIP_TEST(test_getrandom_syscall_partial) + SKIP_TEST(test_getrandom_syscall_interrupted) +#endif /* defined(__linux__) && (defined(SYS_getrandom) or glibc version > 2.24) */ +#if defined(__linux__) && !defined(SYS_getrandom) + RUN_TEST(test_issue_17) + RUN_TEST(test_issue_22) + RUN_TEST(test_issue_33) +#else + SKIP_TEST(test_issue_17) + SKIP_TEST(test_issue_22) + SKIP_TEST(test_issue_33) +#endif /* defined(__linux__) && !defined(SYS_getrandom) */ + return 0; +} diff --git a/sphincs/Makefile b/sphincs/Makefile index 9115615..10f50fe 100644 --- a/sphincs/Makefile +++ b/sphincs/Makefile @@ -1,6 +1,6 @@ # This Makefile can be used with GNU Make or BSD Make -LIB = libsphincs-shake-128f-simple_clean.a +LIB = libsphincs-shake-256f-simple_clean.a HEADERS = address.h api.h context.h fors.h hash.h merkle.h nistapi.h params.h shake_offsets.h thash.h utils.h utilsx1.h wots.h wotsx1.h OBJECTS = address.o context_shake.o fors.o hash_shake.o merkle.o sign.o thash_shake_simple.o utils.o utilsx1.o wots.o wotsx1.o diff --git a/sphincs/Makefile.Microsoft_nmake b/sphincs/Makefile.Microsoft_nmake index ba91e26..5e3477e 100644 --- a/sphincs/Makefile.Microsoft_nmake +++ b/sphincs/Makefile.Microsoft_nmake @@ -1,7 +1,7 @@ # This Makefile can be used with Microsoft Visual Studio's nmake using the command: # nmake /f Makefile.Microsoft_nmake -LIBRARY = libsphincs-shake-128f-simple_clean.lib +LIBRARY = libsphincs-shake-256f-simple_clean.lib OBJECTS = address.obj context_shake.obj fors.obj hash_shake.obj merkle.obj sign.obj thash_shake_simple.obj utils.obj utilsx1.obj wots.obj wotsx1.obj CFLAGS = /nologo /O2 /I ..\..\..\common /W4 /WX diff --git a/sphincs/api.h b/sphincs/api.h index ca4c28c..fbadf6b 100644 --- a/sphincs/api.h +++ b/sphincs/api.h @@ -1,58 +1,43 @@ -#ifndef PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_API_H -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_API_H +#ifndef PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_API_H #include #include -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+-shake-128f-simple" +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+-shake-256f-simple" -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_BYTES 17088 +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_CRYPTO_BYTES 49856 -#define PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 - -#if defined(_WIN32) -#define HYBRIDPQC_API __declspec(dllexport) -#else -#define HYBRIDPQC_API __attribute__((visibility("default"))) -#endif - -#if defined(HYBRIDPQC_SYS_UEFI) -#undef HYBRIDPQC_API -#define HYBRIDPQC_API -#endif - -#if defined(__cplusplus) -extern "C" { -#endif +#define PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 /* * Returns the length of a secret key, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); /* * Returns the length of a public key, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); /* * Returns the length of a signature, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_bytes(void); +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_bytes(void); /* * Returns the length of the seed required to generate a key pair, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seedbytes(void); /* * Generates a SPHINCS+ key pair given a seed. * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] * Format pk: [root || PUB_SEED] */ -HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, const uint8_t *seed); /* @@ -60,38 +45,33 @@ HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair( * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] * Format pk: [root || PUB_SEED] */ -HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk); /** * Returns an array containing a detached signature. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk); /** * Verifies a detached signature and message under a given public key. */ -HYBRIDPQC_API int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk); /** * Returns an array containing the signature followed by the message. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, const uint8_t *m, size_t mlen, const uint8_t *sk); /** * Verifies a given signature-message pair under a given public key. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, const uint8_t *sm, size_t smlen, const uint8_t *pk); #endif - - -#if defined(__cplusplus) -} // extern "C" -#endif diff --git a/sphincs/params.h b/sphincs/params.h index 865ed51..10dfc4a 100644 --- a/sphincs/params.h +++ b/sphincs/params.h @@ -1,17 +1,17 @@ #ifndef SPX_PARAMS_H #define SPX_PARAMS_H -#define SPX_NAMESPACE(s) PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_##s +#define SPX_NAMESPACE(s) PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_##s /* Hash output length in bytes. */ -#define SPX_N 16 +#define SPX_N 32 /* Height of the hypertree. */ -#define SPX_FULL_HEIGHT 66 +#define SPX_FULL_HEIGHT 68 /* Number of subtree layer. */ -#define SPX_D 22 +#define SPX_D 17 /* FORS tree dimensions. */ -#define SPX_FORS_HEIGHT 6 -#define SPX_FORS_TREES 33 +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 35 /* Winternitz parameter, */ #define SPX_WOTS_W 16 diff --git a/sphincs/sign.c b/sphincs/sign.c index 695b254..e01b7fd 100644 --- a/sphincs/sign.c +++ b/sphincs/sign.c @@ -9,36 +9,36 @@ #include "merkle.h" #include "nistapi.h" #include "params.h" +#include "../random/randombytes.h" #include "thash.h" #include "utils.h" #include "wots.h" -#include "../random/randombytes.h" /* * Returns the length of a secret key, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { return CRYPTO_SECRETKEYBYTES; } /* * Returns the length of a public key, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { return CRYPTO_PUBLICKEYBYTES; } /* * Returns the length of a signature, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_bytes(void) { +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_bytes(void) { return CRYPTO_BYTES; } /* * Returns the length of the seed required to generate a key pair, in bytes */ -size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { +size_t PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { return CRYPTO_SEEDBYTES; } @@ -47,7 +47,7 @@ size_t PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] * Format pk: [PUB_SEED || root] */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, uint8_t *sk, const uint8_t *seed) { spx_ctx ctx; @@ -79,14 +79,12 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(uint8_t *pk, u * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] * Format pk: [PUB_SEED || root] */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk) { +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk) { uint8_t seed[CRYPTO_SEEDBYTES]; - int r = randombytes(seed, CRYPTO_SEEDBYTES); if (r != 0) { return -1; } - crypto_sign_seed_keypair(pk, sk, seed); return 0; @@ -95,7 +93,7 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_ /** * Returns an array containing a detached signature. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen, const uint8_t *sk) { spx_ctx ctx; @@ -128,7 +126,6 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, siz if (r != 0) { return -1; } - /* Compute the digest randomization value. */ gen_message_random(sig, sk_prf, optrand, m, mlen, &ctx); @@ -168,7 +165,7 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_signature(uint8_t *sig, siz /** * Verifies a detached signature and message under a given public key. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen, const uint8_t *pk) { spx_ctx ctx; const uint8_t *pub_root = pk + SPX_N; @@ -252,7 +249,7 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_verify(const uint8_t *sig, /** * Returns an array containing the signature followed by the message. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, const uint8_t *m, size_t mlen, const uint8_t *sk) { size_t siglen; @@ -268,21 +265,19 @@ int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(uint8_t *sm, size_t *smlen, /** * Verifies a given signature-message pair under a given public key. */ -int PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, +int PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open(uint8_t *m, size_t *mlen, const uint8_t *sm, size_t smlen, const uint8_t *pk) { /* The API caller does not necessarily know what size a signature should be but SPHINCS+ signatures are always exactly SPX_BYTES. */ if (smlen < SPX_BYTES) { - memset(m, 0, smlen); *mlen = 0; return -1; } *mlen = smlen - SPX_BYTES; - + if (crypto_sign_verify(sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { - memset(m, 0, smlen); *mlen = 0; return -1; } diff --git a/tests/test_hybrid.c b/tests/test_hybrid.c index 31537a6..b686367 100644 --- a/tests/test_hybrid.c +++ b/tests/test_hybrid.c @@ -8,11 +8,13 @@ #include #include #include "../random/randombytes.h" -#include "../hybrid/hybrid.h" +#include "../hybrid-falcon/hybrid.h" #include "../hybrid-dilithium/hybrid.h" +#include "../hybrid-dilithium-sphincs/hybrid.h" #include "../falcon512/api.h" #include "../dilithium2/api.h" #include "../sphincs/api.h" +#include "../common/fips202.h" clock_t get_nano_sec(void); void print_elapsed(clock_t startTime, clock_t endTime); @@ -21,9 +23,12 @@ int test_dilithium(void); int test_sphincs(void); int test_hybrid_falcon(void); int test_hybrid_falcon_perf(int count); -int test_hybrid_dilithium(void); -int test_hybrid_dilithium_perf(int count); int test_hybrid_falcon_deterministic(); +int test_hybrid_dilithium_sphincs(void); +int test_hybrid_dilithium_sphincs_deterministic(); +int test_hybrid_compact_dilithium_sphincs(void); +int test_hybrid_dilithium(void); +int test_hybrid_dilithium_deterministic(void); int test_multiple(int count); int main(int argc, char* argv[]); @@ -176,9 +181,6 @@ int test_dilithium() { printf("\n randombytes failed %d", (int)r2); return r2; } - for (int i = 0; i < 32; i++) { - msg2[1] = msg1[i]; - } int r3 = PQCLEAN_DILITHIUM2_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); if (r3 != 0) { @@ -273,18 +275,18 @@ int test_dilithium() { int test_sphincs() { printf("\n test_sphincs() start"); - unsigned char pk[32]; - unsigned char sk[64]; - unsigned char sig[17088 + 32]; + unsigned char pk[64]; + unsigned char sk[128]; + unsigned char sig[49856 + 32]; unsigned char msg1[32]; unsigned char msg2[32]; unsigned long long sigLen = 0; clock_t startTime; clock_t endTime; - int r1 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair(pk, sk); + int r1 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair(pk, sk); if (r1 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_keypair failed %d", (int)r1); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair failed %d", (int)r1); return r1; } @@ -297,20 +299,20 @@ int test_sphincs() { msg2[1] = msg1[i]; } - int r3 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); + int r3 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); if (r3 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); return r3; } unsigned long long msgLen = 0; - int r4 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); + int r4 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); if (r4 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); return r4; } if (msgLen != 32) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); return -5; } @@ -322,54 +324,54 @@ int test_sphincs() { } printf("\n deterministic key generation test"); - unsigned char seed1[48]; + unsigned char seed1[96]; if (randombytes(seed1, sizeof seed1) != 0) { return -7; } - unsigned char pk2[32]; - unsigned char sk2[64]; - int r5 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk2, sk2, seed1); + unsigned char pk2[64]; + unsigned char sk2[128]; + int r5 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk2, sk2, seed1); if (r5 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r5); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r5); return r5; } for (int j = 0; j < 32; j++) { - unsigned char pk3[32]; - unsigned char sk3[64]; - int r6 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk3, sk3, seed1); + unsigned char pk3[64]; + unsigned char sk3[128]; + int r6 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair(pk3, sk3, seed1); if (r6 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r6); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_seed_keypair failed %d", (int)r6); return r6; } - for (int i = 0; i < 32; i++) { + for (int i = 0; i < 64; i++) { if (pk2[i] != pk3[i]) { printf("\n determienistic key generation failed: pk %d %d %d", i, pk2[i], pk3[i]); return -8; } } - for (int i = 0; i < 64; i++) { + for (int i = 0; i < 128; i++) { if (sk2[i] != sk3[i]) { printf("\n determienistic key generation failed: sk %d, %d, %d", i, sk2[i], sk3[i]); return -9; } } - int r = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk3); + int r = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk3); if (r != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign failed %d", (int)r); return r; } unsigned long long msgLen = 0; - r = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk3); + r = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk3); if (r != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r); return r; } if (msgLen != 32) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open msg check failed %d", (int)msgLen); return -5; } @@ -381,12 +383,24 @@ int test_sphincs() { } } + printf("\n sphincs key gen 1000 iterations perf start"); + startTime = get_nano_sec(); + for (int i = 0; i < 1000; i++) { + r1 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair(pk, sk); + if (r1 != 0) { + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_keypair failed %d", (int)r1); + return r1; + } + } + endTime = get_nano_sec(); + print_elapsed(startTime, endTime); + printf("\n sphincs sign perf 1000 iterations start"); startTime = get_nano_sec(); for (int i = 0; i < 1000; i++) { - r3 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); + r3 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign(sig, &sigLen, msg1, 32, sk); if (r3 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign failed %d", (int)r3); return r3; } } @@ -396,9 +410,9 @@ int test_sphincs() { printf("\n sphincs verify (sign open) 10000 iterations perf start"); startTime = get_nano_sec(); for (int i = 0; i < 10000; i++) { - r4 = PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); + r4 = PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open(msg2, &msgLen, sig, sigLen, pk); if (r4 != 0) { - printf("\n PQCLEAN_SPHINCSSHAKE128FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); + printf("\n PQCLEAN_SPHINCSSHAKE256FSIMPLE_CLEAN_crypto_sign_open failed %d", (int)r4); return r4; } } @@ -687,7 +701,6 @@ int test_hybrid_falcon() { return 0; } - int test_hybrid_falcon_deterministic() { printf("\n test_hybrid_falcon_deterministic() start"); @@ -706,7 +719,6 @@ int test_hybrid_falcon_deterministic() { unsigned long long msgLen2 = 0; const int MSG_LEN = 32; - unsigned char seed1[80] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,119,175,131,115,192,42,246,9,171,117,239,88,235,16,133,230,150,206,59,220,176,144,178,248,188,213,239,142,236,15,177,197,137,102,89,245,200,217,106,31,202,87,135,65,174,105,114,121 }; unsigned char seed3[80]; @@ -830,6 +842,513 @@ int test_hybrid_falcon_deterministic() { return 0; } +int test_hybrid_dilithium_sphincs() { + printf("\n test_hybrid_dilithium_sphincs () start"); + + unsigned char pk[32 + 1312 + 64]; + unsigned char pk2[32 + 1312 + 64]; + unsigned char sk[64 + 2560 + 1312 + 128]; + unsigned char sig1[2 + 64 + 32 + 2420 + 49856]; + unsigned char sig2[2 + 64 + 32 + 2420 + 49856]; + unsigned char msg1[32]; + unsigned char msg2[32]; + unsigned char msg1output[32]; + unsigned char msg2output[32]; + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long msgLen1 = 0; + unsigned long long msgLen2 = 0; + const int MSG_LEN = 32; + const int SIG_LEN = 52374; + + int r = crypto_sign_dilithium_ed25519_sphincs_keypair(pk, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_keypair failed %d", (int)r); + return -1; + } + + r = randombytes(msg1, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -2; + } + + r = crypto_sign_dilithium_ed25519_sphincs(sig1, &sigLen1, msg1, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs failed %d", (int)r); + return -3; + } + + if (sigLen1 != SIG_LEN) { + printf("\n crypto_sign_dilithium_ed25519_sphincs sigLen error %d", (int)sigLen1); + return -4; + } + + r = crypto_sign_dilithium_ed25519_sphincs_open(msg1output, &msgLen1, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open failed %d", (int)r); + return -5; + } + + if (msgLen1 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen1); + return -6; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg1[i] != msg1output[i]) { + printf("\n verify msg content failed %d", i); + return -7; + } + } + + r = crypto_verify_dilithium_ed25519_sphincs(msg1, MSG_LEN, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519 failed %d", (int)r); + return -8; + } + + r = randombytes(msg2, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -9; + } + + r = crypto_sign_dilithium_ed25519_sphincs(sig2, &sigLen2, msg2, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs failed %d", (int)r); + return -10; + } + + if (sigLen2 != SIG_LEN) { + printf("\n crypto_sign_dilithium_ed25519_sphincs sigLen error %d", (int)sigLen2); + return -11; + } + + //sanity check + r = crypto_sign_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open failed %d", (int)r); + return -12; + } + + if (msgLen2 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen2); + return -13; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + + r = crypto_verify_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519_sphincs failed %d", (int)r); + return -15; + } + + //signature fuzz test (sign open) + for (int i = 0; i < SIG_LEN; i++) { + printf("\n test_hybrid_dilithium_sphincs sign open fuzz iteration %d of %d", i, SIG_LEN); + + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_sign_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open was ok when it should have failed %d", (int)r); + return -16; + } + sig2[i] = temp; + r = crypto_sign_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + //signature fuzz test (verify) + for (int i = 0; i < SIG_LEN; i++) { + printf("\n test_hybrid_dilithium_sphincs verify fuzz iteration %d of %d", i, SIG_LEN); + + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_verify_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_verify_dilithium_ed25519_sphincs was ok when it should have failed %d", (int)r); + return -18; + } + sig2[i] = temp; + r = crypto_verify_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_dilithium_ed25519_sphincs failed when it should have been ok %d", (int)r); + return -19; + } + } + + //public key fuzz test + for (int i = 0; i < 32 + 1312 + 64; i++) { + printf("\n test_hybrid_dilithium_sphincs pk sign open fuzz iteration %d of 32 + 1312 + 64", i); + + unsigned char temp = pk[i]; + pk[i] = temp + 1; + r = crypto_sign_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open was ok when it should have failed %d", (int)r); + return -16; + } + pk[i] = temp; + r = crypto_sign_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + printf(" \n test_hybrid_dilithium_sphincs () ok"); + + return 0; +} + +int test_hybrid_compact_dilithium_sphincs() { + printf("\n test_hybrid_compact_dilithium_sphincs () start"); + + unsigned char pk[32 + 1312 + 64]; + unsigned char pk2[32 + 1312 + 64]; + unsigned char sk[64 + 2560 + 1312 + 128]; + unsigned char sig1[2 + 64 + 2420 + 40 + 32]; + unsigned char sig2[2 + 64 + 2420 + 40 + 32]; + unsigned char msg1[32]; + unsigned char msg2[32]; + unsigned char msg1output[32]; + unsigned char msg2output[32]; + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long msgLen1 = 0; + unsigned long long msgLen2 = 0; + const int MSG_LEN = 32; + const int SIG_LEN = 2 + 64 + 2420 + 40 + MSG_LEN; + + int r = crypto_sign_dilithium_ed25519_sphincs_keypair(pk, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_keypair failed %d", (int)r); + return -1; + } + + r = randombytes(msg1, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -2; + } + + r = crypto_sign_compact_dilithium_ed25519_sphincs(sig1, &sigLen1, msg1, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs failed %d", (int)r); + return -3; + } + + if (sigLen1 != SIG_LEN) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs sigLen error %d", (int)sigLen1); + return -4; + } + + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg1output, &msgLen1, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open A failed %d", (int)r); + return -5; + } + + if (msgLen1 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen1); + return -6; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg1[i] != msg1output[i]) { + printf("\n verify msg content failed %d", i); + return -7; + } + } + + r = crypto_verify_compact_dilithium_ed25519_sphincs(msg1, MSG_LEN, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_verify_compact_dilithium_ed25519 failed %d", (int)r); + return -8; + } + + r = randombytes(msg2, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -9; + } + + r = crypto_sign_compact_dilithium_ed25519_sphincs(sig2, &sigLen2, msg2, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs failed %d", (int)r); + return -10; + } + + if (sigLen2 != SIG_LEN) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs sigLen error %d", (int)sigLen2); + return -11; + } + + //sanity check + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open B failed %d", (int)r); + return -12; + } + + if (msgLen2 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen2); + return -13; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + + r = crypto_verify_compact_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_compact_dilithium_ed25519_sphincs failed %d", (int)r); + return -15; + } + + //signature fuzz test (sign open) + for (int i = 0; i < SIG_LEN; i++) { + printf("\n test_hybrid_compact_dilithium_sphincs sign open fuzz iteration %d of %d", i, SIG_LEN); + + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open was ok when it should have failed %d", (int)r); + return -16; + } + sig2[i] = temp; + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + //signature fuzz test (verify) + for (int i = 0; i < SIG_LEN; i++) { + printf("\n test_hybrid_compact_dilithium_sphincs verify fuzz iteration %d of %d", i, SIG_LEN); + + unsigned char temp = sig2[i]; + sig2[i] = temp + 1; + r = crypto_verify_compact_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_verify_compact_dilithium_ed25519_sphincs was ok when it should have failed %d", (int)r); + return -18; + } + sig2[i] = temp; + r = crypto_verify_compact_dilithium_ed25519_sphincs(msg2, MSG_LEN, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_verify_compact_dilithium_ed25519_sphincs failed when it should have been ok %d", (int)r); + return -19; + } + } + + //public key fuzz test + for (int i = 0; i < 32 + 1312 + 64; i++) { + printf("\n test_hybrid_compact_dilithium_sphincs pk sign open fuzz iteration %d of 32 + 1312 + 64", i); + + unsigned char temp = pk[i]; + pk[i] = temp + 1; + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r == 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open was ok when it should have failed %d", (int)r); + return -16; + } + pk[i] = temp; + r = crypto_sign_compact_dilithium_ed25519_sphincs_open(msg2output, &msgLen2, sig2, sigLen2, pk); + if (r != 0) { + printf("\n crypto_sign_compact_dilithium_ed25519_sphincs_open failed when it should have been ok %d", (int)r); + return -17; + } + for (int i = 0; i < MSG_LEN; i++) { + if (msg2[i] != msg2output[i]) { + printf("\n verify msg content failed %d", i); + return -14; + } + } + } + + printf(" \n test_hybrid_compact_dilithium_sphincs () ok"); + + return 0; +} + +int test_hybrid_dilithium_sphincs_deterministic() { + printf("\n test_hybrid_dilithium_sphincs_deterministic() start"); + + unsigned char pk[32 + 1312 + 64]; + unsigned char pk2[32 + 1312 + 64]; + unsigned char pk3[32 + 1312 + 64]; + unsigned char sk[64 + 2560 + 1312 + 128]; + unsigned char sk2[64 + 2560 + 1312 + 128]; + unsigned char sk3[64 + 2560 + 1312 + 128]; + unsigned char sig1[2 + 64 + 32 + 2420 + 49856]; + unsigned char msg1[32]; + unsigned char msg1output[32]; + unsigned long long sigLen1 = 0; + unsigned long long sigLen2 = 0; + unsigned long long msgLen1 = 0; + unsigned long long msgLen2 = 0; + const int MSG_LEN = 32; + const int SIG_LEN = 52374; + + unsigned char seed1[160] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,119,175,131,115,192,42,246,9,171,117,239,88,235,16,133,230,150,206,59,220,176,144,178,248,188,213,239,142,236,15,177,197}; + + unsigned char seed3[160]; + if (randombytes(seed3, sizeof seed3) != 0) { + return -1; + } + + int r = crypto_sign_dilithium_ed25519_sphincs_keypair_seed(pk, sk, seed1); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_keypair_seed failed %d", (int)r); + return -2; + } + + /* + printf("\n pk \n"); + for (int k = 0; k < 32 + 1312 + 64; k++) { + printf("%d,", pk[k]); + } + printf("\n sk \n"); + for (int k = 0; k < 64 + 2560 + 1312 + 128; k++) { + printf("%d,", sk[k]); + }*/ + + unsigned char pkFromSeed[32 + 1312 + 64] = { 240,78,219,55,229,129,35,19,238,250,42,184,21,246,140,49,133,117,234,93,254,183,215,211,206,92,25,21,105,115,247,115,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,3,166,59,195,12,211,93,68,8,73,212,169,81,201,46,231,74,162,153,110,81,197,17,33,199,26,102,208,122,104,52,207,38,215,250,125,66,231,196,39,11,76,38,106,119,57,223,51,224,176,181,25,45,166,56,84,24,246,248,143,206,211,185,64,102,123,41,1,70,43,59,167,65,94,175,70,141,218,215,213,32,22,124,94,115,102,168,74,125,180,149,149,127,142,11,64,39,145,22,16,250,47,48,174,58,155,117,170,108,243,208,177,100,217,39,116,40,76,5,203,237,210,163,118,148,156,172,112,86,185,131,37,111,115,233,190,130,69,237,16,207,146,141,103,114,100,201,9,169,72,64,150,245,18,203,123,125,241,167,147,78,41,212,29,70,169,145,50,123,12,165,57,193,211,62,248,112,126,144,229,21,9,123,111,127,158,137,236,226,142,149,190,142,222,213,89,243,231,202,161,246,152,207,74,206,182,251,22,132,205,132,191,248,63,65,27,32,76,230,148,217,236,37,198,17,193,130,245,72,174,99,132,91,1,170,167,96,152,231,136,195,250,77,4,39,128,224,138,150,179,90,102,18,23,148,59,203,99,61,157,199,112,196,11,7,98,58,32,166,2,55,120,43,108,84,157,243,234,178,247,143,110,128,94,25,179,34,30,139,178,41,133,254,76,49,221,225,135,161,56,245,90,41,215,72,10,104,158,68,93,111,181,42,126,1,37,138,63,145,142,22,60,254,124,222,125,196,227,109,34,78,71,55,3,109,147,223,40,165,81,147,13,57,210,234,108,162,69,191,71,155,91,164,112,187,212,227,72,32,204,100,94,245,205,6,230,72,87,138,92,40,148,211,32,236,1,73,99,29,106,108,116,113,254,210,62,158,103,227,126,47,194,112,55,46,82,92,204,225,138,74,170,29,179,131,183,77,234,186,174,144,53,36,159,53,85,86,203,229,174,196,234,131,99,140,226,84,236,205,197,31,143,15,188,126,214,115,111,168,156,84,14,190,140,145,209,118,177,19,98,25,179,183,191,156,95,80,89,158,173,219,222,135,65,162,210,165,177,127,95,161,162,58,248,132,251,241,102,170,186,211,97,170,200,137,78,46,219,140,66,22,208,219,19,189,29,255,254,222,204,145,14,19,238,99,201,161,103,91,8,249,122,165,255,162,107,115,211,120,41,78,130,219,142,209,254,103,82,216,49,0,205,40,105,223,59,121,55,38,254,162,241,41,51,209,143,48,190,236,0,200,146,239,137,193,223,117,205,56,186,70,137,210,1,224,142,241,2,157,31,239,12,138,141,95,216,247,225,64,45,53,248,165,19,6,178,225,95,111,218,164,218,4,49,142,71,244,78,51,29,11,140,121,230,188,22,232,13,33,140,174,115,255,162,244,112,55,29,16,91,58,28,4,158,85,171,96,151,235,235,213,247,0,90,65,227,119,2,54,104,66,185,57,182,76,234,160,170,218,119,123,162,126,79,54,35,58,36,249,117,110,76,7,114,139,135,140,63,139,133,19,128,196,12,51,35,165,76,158,54,87,133,1,58,210,75,203,183,193,34,255,21,114,182,215,122,191,149,134,135,240,198,182,60,66,1,176,144,143,132,36,184,125,62,56,124,218,85,28,245,142,60,18,0,169,99,88,215,123,181,34,16,43,239,207,40,188,127,255,91,8,224,144,125,221,248,27,4,48,212,148,75,103,62,118,7,89,35,186,154,190,95,114,217,115,213,188,217,102,217,174,90,19,7,150,26,111,68,163,109,227,219,228,109,215,110,161,69,169,114,79,226,72,109,221,84,104,224,97,94,246,85,73,156,24,153,16,115,106,16,91,129,251,74,243,51,233,109,229,76,147,48,54,63,197,74,246,209,59,47,98,205,112,178,103,149,12,106,33,168,211,92,82,32,56,184,224,158,20,30,193,27,198,209,252,37,162,248,86,7,199,142,194,110,158,155,197,254,80,36,93,102,163,82,245,75,84,215,28,99,238,82,143,123,215,75,97,226,217,245,210,178,126,46,68,243,162,54,11,32,157,156,83,51,142,52,209,160,113,116,24,79,249,60,19,23,0,186,248,75,105,200,166,160,150,157,146,148,13,26,22,142,103,115,81,196,41,131,196,91,52,126,26,43,146,102,123,63,56,150,255,21,50,12,49,84,75,76,213,86,187,150,249,166,130,105,69,173,203,137,154,120,224,57,211,249,26,188,48,63,1,21,123,245,169,12,118,247,86,112,102,238,244,162,110,249,19,160,173,79,138,225,183,192,62,28,30,150,151,47,122,75,91,178,110,70,209,50,60,80,194,45,213,214,82,139,117,186,27,11,237,207,253,227,251,108,21,161,133,182,38,114,115,139,78,119,149,17,146,125,126,208,191,38,220,130,205,56,145,3,94,38,182,232,65,151,170,104,55,255,88,93,49,181,42,44,52,213,129,189,246,253,216,250,139,139,122,20,192,129,73,36,160,209,164,71,94,252,225,18,2,200,200,64,119,51,167,59,92,122,86,83,254,10,148,164,244,12,126,25,2,231,175,46,228,164,57,10,158,197,159,85,50,1,1,186,67,40,24,67,129,231,92,115,36,217,110,4,83,150,139,162,136,54,152,183,168,184,19,167,222,190,209,105,173,6,227,129,209,109,186,227,201,123,23,47,184,65,83,16,213,21,214,227,52,36,26,177,250,27,16,227,188,63,32,191,48,155,154,30,159,86,90,155,144,196,84,190,79,242,3,152,213,10,98,16,165,146,116,123,100,186,242,53,72,60,238,218,0,21,114,236,109,242,82,185,236,91,113,49,79,184,8,249,122,113,153,177,15,127,178,79,59,17,165,8,248,32,76,33,180,169,92,71,107,125,42,42,4,95,108,166,6,244,25,207,247,84,42,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,180,198,43,114,215,10,207,139,94,219,3,140,34,47,104,100,23,176,139,21,18,203,76,161,34,67,144,200,155,128,205 }; + unsigned char skFromSeed[64 + 2560 + 1312 + 128] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,240,78,219,55,229,129,35,19,238,250,42,184,21,246,140,49,133,117,234,93,254,183,215,211,206,92,25,21,105,115,247,115,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,222,11,28,105,5,109,52,135,188,243,62,92,102,190,11,156,128,42,160,44,41,25,215,31,247,58,131,169,112,166,19,4,231,247,205,149,190,13,3,215,53,100,154,37,253,226,244,228,199,14,134,226,45,154,130,97,243,45,37,246,72,51,208,252,2,27,37,107,218,149,186,100,158,182,143,11,1,80,56,57,144,188,189,252,211,160,147,199,120,254,141,233,188,238,156,239,27,65,96,64,198,76,66,196,97,83,130,129,3,53,46,136,168,69,209,160,45,148,194,81,152,48,68,74,146,77,220,194,129,137,22,14,154,180,45,25,41,38,0,160,13,33,32,77,65,38,77,195,150,112,227,152,72,200,8,46,68,164,112,25,71,44,100,22,142,84,4,50,19,36,146,209,182,128,36,197,132,3,33,110,147,166,45,212,160,68,81,48,144,66,66,130,146,192,112,163,182,44,209,162,32,34,165,96,161,50,109,28,19,6,17,24,108,193,184,128,64,48,96,11,181,32,217,6,104,76,54,8,84,36,114,220,32,82,72,50,42,3,52,98,17,73,36,219,146,132,36,56,137,217,18,44,72,162,141,208,18,144,1,161,81,227,146,40,65,150,144,80,130,108,96,0,74,209,40,112,19,72,74,82,152,13,26,181,48,25,55,129,211,56,100,12,37,14,90,166,137,154,196,112,3,180,77,92,68,133,138,54,73,1,193,104,2,16,137,209,182,132,19,131,17,11,185,8,0,3,68,68,184,13,0,0,38,9,36,145,200,8,65,216,178,96,32,146,101,26,131,132,224,24,106,154,2,129,68,4,68,17,182,32,28,41,50,148,162,13,16,178,73,64,150,64,28,7,78,27,69,48,28,198,0,193,152,40,84,66,68,216,18,8,152,22,34,16,135,65,9,192,65,32,64,108,9,33,45,0,50,80,130,32,140,74,40,141,155,50,144,226,164,5,97,66,106,34,53,42,20,72,46,9,166,9,11,6,113,211,160,145,3,64,48,226,152,80,139,16,41,145,168,97,224,166,133,208,54,134,89,22,137,26,49,145,204,198,132,36,22,128,17,177,129,216,34,10,12,70,108,35,20,12,73,4,44,72,178,97,192,184,105,19,192,32,208,152,104,20,64,68,130,144,40,218,40,96,224,52,112,145,148,16,20,149,41,138,64,34,83,164,76,217,180,16,33,184,112,220,8,98,138,16,14,89,146,140,2,57,36,76,6,50,163,34,14,163,0,2,28,33,80,96,8,108,3,34,12,12,3,128,17,152,37,220,144,136,36,134,33,82,64,9,226,144,81,164,196,136,220,198,141,128,40,5,4,0,68,100,24,65,24,197,136,2,185,36,16,64,5,216,16,104,11,184,69,228,52,33,72,16,45,201,24,134,19,130,16,218,148,37,203,52,66,33,16,141,91,164,40,65,166,9,209,34,70,90,152,129,76,20,10,145,68,2,18,195,136,3,73,9,67,198,16,0,73,34,225,32,74,200,68,37,210,134,137,75,144,1,9,134,96,89,160,129,218,16,48,36,56,65,26,162,109,161,198,44,75,136,12,140,176,140,194,132,36,10,23,105,98,176,65,28,70,132,81,148,64,3,18,18,160,52,74,12,184,145,8,53,73,34,168,137,203,178,113,211,22,80,212,160,129,9,66,112,194,56,17,4,165,44,163,34,81,32,0,114,220,168,40,3,2,96,3,64,137,89,4,113,200,130,36,128,50,37,25,54,106,164,54,106,163,18,49,32,200,141,16,65,73,65,164,16,219,2,141,27,1,40,138,136,44,28,129,8,81,40,5,73,194,65,90,0,132,163,8,128,140,36,110,196,146,64,218,66,106,80,162,104,82,36,141,156,6,114,4,198,68,92,6,70,66,194,65,194,2,48,36,176,72,32,67,32,84,50,100,73,176,109,26,40,134,66,184,32,80,72,32,153,2,98,16,23,0,161,0,145,245,104,38,14,65,179,182,1,252,0,76,146,216,170,29,22,249,198,40,122,155,162,23,203,92,16,98,169,96,112,46,225,31,89,107,227,142,142,224,230,194,64,185,121,86,69,55,44,211,248,235,170,179,190,109,10,252,66,233,101,120,225,217,51,83,211,74,221,109,84,136,78,245,82,21,46,235,182,182,195,108,87,146,209,216,230,77,206,31,46,75,86,109,73,135,16,218,6,77,188,180,47,197,85,244,194,121,206,24,212,29,218,50,95,149,98,118,83,78,23,139,130,205,130,36,167,181,34,183,159,228,234,114,52,176,40,69,121,216,20,160,162,237,83,110,147,12,182,134,159,193,195,38,10,130,83,21,136,44,148,208,2,83,224,96,131,51,231,39,184,93,91,21,240,183,186,111,12,146,1,105,46,223,105,89,56,145,57,117,80,92,226,5,61,3,100,198,174,92,177,226,205,16,143,9,105,210,113,124,9,239,31,169,157,66,218,242,167,229,17,47,189,32,67,188,220,36,204,167,240,174,30,183,184,7,173,52,50,40,67,199,173,253,197,65,90,96,155,26,249,170,231,212,151,7,215,95,212,99,207,175,238,139,195,55,212,116,22,24,240,38,159,38,192,37,158,74,91,86,195,181,34,230,0,95,16,28,209,219,23,134,188,145,163,148,135,170,141,249,119,98,73,52,48,216,134,199,173,182,207,216,46,69,103,52,150,5,116,92,170,144,198,77,6,242,131,10,92,219,61,132,8,88,249,224,70,44,63,180,81,155,204,98,115,237,134,225,68,33,231,158,141,227,213,231,51,177,61,200,14,32,140,120,14,137,52,31,50,95,59,175,180,14,79,107,91,177,103,120,43,254,43,136,40,173,37,246,225,253,62,176,72,219,117,29,31,118,43,253,0,26,66,5,48,212,162,134,168,73,161,187,124,103,40,114,218,142,247,152,204,150,171,27,125,25,116,187,191,65,1,95,86,147,99,152,106,166,66,33,188,121,125,149,247,26,222,140,224,110,161,165,215,162,23,49,222,18,250,107,164,102,126,31,22,116,9,175,218,28,99,8,50,108,204,188,176,178,223,220,35,76,140,87,234,44,14,224,1,97,45,32,97,13,5,157,227,167,112,86,128,203,225,177,253,100,123,190,154,77,240,207,212,100,122,41,189,155,240,246,157,39,22,103,228,164,84,81,42,139,188,148,57,13,204,9,252,22,221,31,122,76,48,8,34,140,103,252,202,234,29,143,132,73,184,57,42,123,57,94,60,104,176,159,164,253,166,8,26,77,4,124,50,237,76,134,228,128,22,102,165,216,37,122,159,64,182,118,29,57,140,3,242,6,44,56,200,110,170,142,177,254,81,213,224,138,147,92,239,61,75,212,96,33,148,122,187,246,87,119,146,46,148,118,2,92,79,239,209,112,130,255,210,240,39,114,221,184,34,37,37,227,5,250,191,129,122,68,135,251,81,154,97,192,125,10,111,63,37,205,253,10,46,127,38,71,175,246,180,238,3,148,84,254,211,214,181,8,109,238,70,27,42,241,245,254,116,114,185,167,208,157,205,182,217,27,176,49,199,41,193,163,148,144,173,85,208,237,135,61,88,237,249,237,35,115,57,61,89,201,220,247,37,93,114,219,60,110,53,130,131,99,27,103,161,119,210,22,48,196,206,40,182,216,72,220,48,139,62,104,63,196,200,80,185,232,87,1,121,109,240,76,82,136,249,54,118,56,137,138,14,68,66,251,245,231,131,102,47,232,35,1,197,223,160,214,62,224,162,22,134,109,168,109,146,22,34,106,186,188,84,225,21,49,154,123,8,29,28,63,76,63,106,85,184,117,254,182,220,241,4,153,218,151,172,204,198,175,35,15,47,90,236,190,145,208,34,91,163,30,4,103,217,53,99,91,227,179,142,107,152,218,28,87,126,110,170,64,98,144,238,112,175,62,23,94,253,234,173,250,53,242,79,205,79,160,19,107,176,17,186,149,171,121,191,66,25,112,249,237,12,225,83,2,186,65,36,34,117,165,107,131,136,196,87,165,184,40,57,161,32,208,167,225,49,60,234,92,33,128,129,24,252,88,126,35,53,190,199,45,74,242,234,99,13,157,109,118,122,108,81,236,156,192,67,254,79,39,195,139,184,213,17,111,86,164,83,203,244,187,169,156,113,137,249,198,46,191,193,135,12,214,142,252,143,152,112,62,128,129,240,115,78,16,137,113,204,255,194,193,35,23,21,170,53,240,32,166,126,59,75,159,61,196,196,163,29,39,32,61,106,82,103,39,220,74,97,141,77,144,181,100,215,2,43,149,84,200,142,13,152,246,212,102,250,42,143,119,127,131,55,149,18,70,38,46,144,64,159,234,43,32,210,157,168,9,87,230,78,67,27,225,64,75,203,135,203,106,198,84,180,66,111,8,34,84,159,29,236,101,144,3,171,138,39,186,231,248,216,255,131,189,224,141,107,71,157,167,150,47,158,174,204,76,4,253,133,103,168,225,247,203,139,107,152,245,141,20,54,160,255,16,164,130,100,28,30,155,248,235,236,21,192,233,25,129,122,56,62,29,129,223,216,96,246,208,46,185,203,170,122,121,104,52,121,95,87,48,45,148,141,206,82,247,194,80,89,196,5,209,75,31,252,162,176,1,157,201,242,43,216,238,217,164,111,2,89,143,120,231,77,241,50,114,185,46,94,177,38,36,222,252,211,77,226,9,171,121,204,171,71,84,87,130,106,93,72,237,137,31,133,101,113,68,175,234,96,39,38,199,21,159,107,77,209,81,47,15,89,102,213,119,81,118,149,254,249,250,163,156,151,23,159,16,89,113,19,61,190,37,200,36,134,55,13,245,10,202,20,130,30,187,178,75,58,133,136,35,43,69,192,181,31,27,179,96,109,35,52,49,117,199,204,67,157,218,123,50,160,91,201,226,26,11,82,114,133,92,0,39,61,168,36,179,164,107,192,15,40,1,39,188,43,28,169,226,146,63,25,236,88,59,183,220,154,58,158,119,74,162,150,95,41,41,194,67,207,86,196,52,221,209,234,127,235,49,45,34,208,181,170,214,165,5,143,159,31,81,65,184,41,47,119,173,130,214,184,74,19,12,231,63,201,108,235,135,105,33,63,28,157,244,10,15,251,50,141,65,44,238,237,27,212,26,237,93,14,115,62,194,241,213,248,98,80,58,235,17,109,2,26,83,110,252,88,246,45,30,222,166,240,227,116,148,179,162,117,7,48,105,40,192,234,59,54,112,225,7,195,123,22,139,3,10,157,153,133,8,32,108,53,188,168,33,116,140,9,233,204,9,245,38,88,231,102,15,184,184,141,237,212,235,158,98,47,182,252,102,208,22,112,75,58,118,204,226,209,177,220,212,231,215,120,163,101,108,91,153,192,57,33,141,158,210,249,23,60,67,248,103,118,61,244,241,89,163,169,117,49,232,41,243,135,159,211,172,176,115,53,18,211,87,187,20,69,133,68,185,157,20,8,178,68,114,233,77,168,248,243,199,99,32,50,89,248,211,34,201,62,186,91,64,67,173,176,37,245,158,179,145,114,172,21,74,136,112,164,96,45,87,110,176,163,173,220,248,113,159,103,39,3,33,157,168,61,118,75,20,31,94,249,27,220,167,44,251,61,91,182,187,21,212,69,195,42,94,238,12,1,136,178,196,208,217,178,126,252,210,79,213,184,248,99,217,17,96,57,214,90,98,34,207,181,77,142,253,253,196,167,132,245,247,153,236,47,211,10,39,252,190,40,150,237,95,4,192,163,171,100,105,240,107,33,83,68,228,125,29,161,139,146,159,81,61,31,124,158,3,166,59,195,12,211,93,68,8,73,212,169,81,201,46,231,74,162,153,110,81,197,17,33,199,26,102,208,122,104,52,207,38,215,250,125,66,231,196,39,11,76,38,106,119,57,223,51,224,176,181,25,45,166,56,84,24,246,248,143,206,211,185,64,102,123,41,1,70,43,59,167,65,94,175,70,141,218,215,213,32,22,124,94,115,102,168,74,125,180,149,149,127,142,11,64,39,145,22,16,250,47,48,174,58,155,117,170,108,243,208,177,100,217,39,116,40,76,5,203,237,210,163,118,148,156,172,112,86,185,131,37,111,115,233,190,130,69,237,16,207,146,141,103,114,100,201,9,169,72,64,150,245,18,203,123,125,241,167,147,78,41,212,29,70,169,145,50,123,12,165,57,193,211,62,248,112,126,144,229,21,9,123,111,127,158,137,236,226,142,149,190,142,222,213,89,243,231,202,161,246,152,207,74,206,182,251,22,132,205,132,191,248,63,65,27,32,76,230,148,217,236,37,198,17,193,130,245,72,174,99,132,91,1,170,167,96,152,231,136,195,250,77,4,39,128,224,138,150,179,90,102,18,23,148,59,203,99,61,157,199,112,196,11,7,98,58,32,166,2,55,120,43,108,84,157,243,234,178,247,143,110,128,94,25,179,34,30,139,178,41,133,254,76,49,221,225,135,161,56,245,90,41,215,72,10,104,158,68,93,111,181,42,126,1,37,138,63,145,142,22,60,254,124,222,125,196,227,109,34,78,71,55,3,109,147,223,40,165,81,147,13,57,210,234,108,162,69,191,71,155,91,164,112,187,212,227,72,32,204,100,94,245,205,6,230,72,87,138,92,40,148,211,32,236,1,73,99,29,106,108,116,113,254,210,62,158,103,227,126,47,194,112,55,46,82,92,204,225,138,74,170,29,179,131,183,77,234,186,174,144,53,36,159,53,85,86,203,229,174,196,234,131,99,140,226,84,236,205,197,31,143,15,188,126,214,115,111,168,156,84,14,190,140,145,209,118,177,19,98,25,179,183,191,156,95,80,89,158,173,219,222,135,65,162,210,165,177,127,95,161,162,58,248,132,251,241,102,170,186,211,97,170,200,137,78,46,219,140,66,22,208,219,19,189,29,255,254,222,204,145,14,19,238,99,201,161,103,91,8,249,122,165,255,162,107,115,211,120,41,78,130,219,142,209,254,103,82,216,49,0,205,40,105,223,59,121,55,38,254,162,241,41,51,209,143,48,190,236,0,200,146,239,137,193,223,117,205,56,186,70,137,210,1,224,142,241,2,157,31,239,12,138,141,95,216,247,225,64,45,53,248,165,19,6,178,225,95,111,218,164,218,4,49,142,71,244,78,51,29,11,140,121,230,188,22,232,13,33,140,174,115,255,162,244,112,55,29,16,91,58,28,4,158,85,171,96,151,235,235,213,247,0,90,65,227,119,2,54,104,66,185,57,182,76,234,160,170,218,119,123,162,126,79,54,35,58,36,249,117,110,76,7,114,139,135,140,63,139,133,19,128,196,12,51,35,165,76,158,54,87,133,1,58,210,75,203,183,193,34,255,21,114,182,215,122,191,149,134,135,240,198,182,60,66,1,176,144,143,132,36,184,125,62,56,124,218,85,28,245,142,60,18,0,169,99,88,215,123,181,34,16,43,239,207,40,188,127,255,91,8,224,144,125,221,248,27,4,48,212,148,75,103,62,118,7,89,35,186,154,190,95,114,217,115,213,188,217,102,217,174,90,19,7,150,26,111,68,163,109,227,219,228,109,215,110,161,69,169,114,79,226,72,109,221,84,104,224,97,94,246,85,73,156,24,153,16,115,106,16,91,129,251,74,243,51,233,109,229,76,147,48,54,63,197,74,246,209,59,47,98,205,112,178,103,149,12,106,33,168,211,92,82,32,56,184,224,158,20,30,193,27,198,209,252,37,162,248,86,7,199,142,194,110,158,155,197,254,80,36,93,102,163,82,245,75,84,215,28,99,238,82,143,123,215,75,97,226,217,245,210,178,126,46,68,243,162,54,11,32,157,156,83,51,142,52,209,160,113,116,24,79,249,60,19,23,0,186,248,75,105,200,166,160,150,157,146,148,13,26,22,142,103,115,81,196,41,131,196,91,52,126,26,43,146,102,123,63,56,150,255,21,50,12,49,84,75,76,213,86,187,150,249,166,130,105,69,173,203,137,154,120,224,57,211,249,26,188,48,63,1,21,123,245,169,12,118,247,86,112,102,238,244,162,110,249,19,160,173,79,138,225,183,192,62,28,30,150,151,47,122,75,91,178,110,70,209,50,60,80,194,45,213,214,82,139,117,186,27,11,237,207,253,227,251,108,21,161,133,182,38,114,115,139,78,119,149,17,146,125,126,208,191,38,220,130,205,56,145,3,94,38,182,232,65,151,170,104,55,255,88,93,49,181,42,44,52,213,129,189,246,253,216,250,139,139,122,20,192,129,73,36,160,209,164,71,94,252,225,18,2,200,200,64,119,51,167,59,92,122,86,83,254,10,148,164,244,12,126,25,2,231,175,46,228,164,57,10,158,197,159,85,50,1,1,186,67,40,24,67,129,231,92,115,36,217,110,4,83,150,139,162,136,54,152,183,168,184,19,167,222,190,209,105,173,6,227,129,209,109,186,227,201,123,23,47,184,65,83,16,213,21,214,227,52,36,26,177,250,27,16,227,188,63,32,191,48,155,154,30,159,86,90,155,144,196,84,190,79,242,3,152,213,10,98,16,165,146,116,123,100,186,242,53,72,60,238,218,0,21,114,236,109,242,82,185,236,91,113,49,79,184,8,249,122,113,153,177,15,127,178,79,59,17,165,8,248,32,76,33,180,169,92,71,107,125,42,42,4,95,108,166,6,244,25,207,247,84,42,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,180,198,43,114,215,10,207,139,94,219,3,140,34,47,104,100,23,176,139,21,18,203,76,161,34,67,144,200,155,128,205 }; + + for (int j = 0; j < 32; j++) { + printf("\n test_hybrid_dilithium_sphincs_deterministic iteration %d", j); + r = crypto_sign_dilithium_ed25519_sphincs_keypair_seed(pk2, sk2, seed1); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_keypair_seed failed %d", (int)r); + return -3; + } + + for (int k = 0; k < 32 + 1312 + 64; k++) { + if (pk[k] != pkFromSeed[k]) { + printf("\n deterministic generation failed pkFromSeed: pk %d,%d,%d", k, pk[k], pkFromSeed[k]); + return -4; + } + if (pk[k] != pk2[k]) { + printf("\n deterministic generation failed: pk"); + return -5; + } + } + + for (int k = 0; k < 64 + 2560 + 1312 + 128; k++) { + if (sk[k] != skFromSeed[k]) { + printf("\n deterministic generation failed skFromSeed: sk"); + return -6; + } + if (sk[k] != sk2[k]) { + printf("\n deterministic generation failed: sk"); + return -7; + } + } + + r = crypto_sign_dilithium_ed25519_sphincs_keypair_seed(pk3, sk3, seed3); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_keypair_seed failed %d", (int)r); + return -8; + } + + int matchCount = 0; + for (int k = 0; k < 32 + 1312 + 64; k++) { + if (pk2[k] == pk3[k]) { + matchCount++; + } + } + if (matchCount == 32 + 1312 + 64) { + printf("\n deterministic generation failed repeat: pk"); + return -9; + } + + matchCount = 0; + for (int k = 0; k < 64 + 2560 + 1312 + 128; k++) { + if (sk2[k] == sk3[k]) { + matchCount++; + } + } + if (matchCount == 64 + 2560 + 1312 + 128) { + printf("\n deterministic generation failed repeat: pk"); + return -10; + } + + r = randombytes(msg1, MSG_LEN * sizeof(unsigned char)); + if (r != 0) { + printf("\n randombytes failed %d", (int)r); + return -11; + } + + r = crypto_sign_dilithium_ed25519_sphincs(sig1, &sigLen1, msg1, MSG_LEN, sk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs failed %d", (int)r); + return -12; + } + + if (sigLen1 != SIG_LEN) { + printf("\n crypto_sign_dilithium_ed25519_sphincs sigLen error %d", (int)sigLen1); + return -13; + } + + r = crypto_sign_dilithium_ed25519_sphincs_open(msg1output, &msgLen1, sig1, sigLen1, pk); + if (r != 0) { + printf("\n crypto_sign_dilithium_ed25519_sphincs_open failed %d", (int)r); + return -14; + } + + if (msgLen1 != MSG_LEN) { + printf("\n verify msglen failed expected %d got %d", MSG_LEN, (int)msgLen1); + return -15; + } + + for (int i = 0; i < MSG_LEN; i++) { + if (msg1[i] != msg1output[i]) { + printf("\n verify msg content failed %d", i); + return -16; + } + } + } + + printf("\n test_hybrid_dilithium_sphincs_deterministic() ok"); + return 0; +} + int test_hybrid_dilithium() { printf("\n test_hybrid_dilithium () start"); @@ -1025,7 +1544,7 @@ int test_hybrid_dilithium_deterministic() { unsigned long long msgLen2 = 0; const int MSG_LEN = 32; - unsigned char seed1[64] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,119,175,131,115,192,42,246,9,171,117,239,88,235,16,133,230,150,206,59,220,176,144,178,248,188,213,239,142,236,15,177,197}; + unsigned char seed1[64] = { 129,111,254,208,138,196,60,45,101,134,78,177,227,76,82,203,26,114,241,89,26,205,174,187,167,219,156,51,195,197,228,27,119,175,131,115,192,42,246,9,171,117,239,88,235,16,133,230,150,206,59,220,176,144,178,248,188,213,239,142,236,15,177,197 }; unsigned char seed3[64]; if (randombytes(seed3, sizeof seed3) != 0) { @@ -1161,49 +1680,66 @@ int test_multiple(int count) { } int main(int argc, char* argv[]) { - int rdh = test_hybrid_dilithium(); - if (rdh != 0) { - return rdh; + int result; + + result = test_hybrid_compact_dilithium_sphincs(); + if (result != 0) { + return result; } - rdh = test_hybrid_dilithium_deterministic(); - if (rdh != 0) { - return rdh; + result = test_hybrid_dilithium_sphincs_deterministic(); + if (result != 0) { + return result; } - int rd = test_dilithium(); - if (rd != 0) { - return rd; + result = test_hybrid_dilithium_sphincs(); + if (result != 0) { + return result; } - int r0 = test_falcon(); - if (r0 != 0) { - return r0; + result = test_hybrid_dilithium(); + if (result != 0) { + return result; } - int r1 = test_hybrid_falcon(); - if (r1 != 0) { - return r1; + result = test_hybrid_dilithium_deterministic(); + if (result != 0) { + return result; + } + + result = test_dilithium(); + if (result != 0) { + return result; + } + + result = test_falcon(); + if (result != 0) { + return result; + } + + result = test_hybrid_falcon(); + if (result != 0) { + return result; } int count = 10; - int r3 = test_multiple(count); - if (r3 != 0) { - return r3; + result = test_multiple(count); + if (result != 0) { + return result; } - r3 = test_hybrid_falcon_deterministic(); - if (r3 != 0) { - return r3; + result = test_hybrid_falcon_deterministic(); + if (result != 0) { + return result; } int perfCount = 1000; test_hybrid_falcon_perf(perfCount); - /*int rs = test_sphincs(); - if (rs != 0) { - return rs; - }*/ + result = test_sphincs(); + if (result != 0) { + return result; + } printf("\n Warning, perf tests uses approximate system clock. Is not suitable for fewer iterations of test.");