diff --git a/common/nistseedexpander.c b/common/nistseedexpander.c new file mode 100644 index 00000000..49b4d3b6 --- /dev/null +++ b/common/nistseedexpander.c @@ -0,0 +1,103 @@ +// +// rng.c +// +// Created by Bassham, Lawrence E (Fed) on 8/29/17. +// Copyright © 2017 Bassham, Lawrence E (Fed). All rights reserved. +// Modified for PQClean by Sebastian Verschoor +// + +#include "nistseedexpander.h" +#include "aes.h" +#include + +/* + seedexpander_init() + ctx - stores the current state of an instance of the seed expander + seed - a 32 byte random value + diversifier - an 8 byte diversifier + maxlen - maximum number of bytes (less than 2**32) generated under this seed and diversifier + */ +int +seedexpander_init(AES_XOF_struct *ctx, + const uint8_t *seed, + const uint8_t *diversifier, + size_t maxlen) +{ + ctx->length_remaining = maxlen; + + memcpy(ctx->key, seed, 32); + memcpy(ctx->ctr, diversifier, 8); + + ctx->ctr[11] = maxlen % 256; + maxlen >>= 8; + ctx->ctr[10] = maxlen % 256; + maxlen >>= 8; + ctx->ctr[9] = maxlen % 256; + maxlen >>= 8; + ctx->ctr[8] = maxlen % 256; + memset(ctx->ctr+12, 0x00, 4); + + ctx->buffer_pos = 16; + memset(ctx->buffer, 0x00, 16); + + return RNG_SUCCESS; +} + +static void AES256_ECB(uint8_t *key, uint8_t *ctr, uint8_t *buffer) { + aes256ctx ctx; + aes256_keyexp(&ctx, key); + aes256_ecb(buffer, ctr, 1, &ctx); + aes256_ctx_release(&ctx); +} + +/* + seedexpander() + ctx - stores the current state of an instance of the seed expander + x - returns the XOF data + xlen - number of bytes to return + */ +int +seedexpander(AES_XOF_struct *ctx, uint8_t *x, size_t xlen) +{ + size_t offset; + + if ( x == NULL ) { + return RNG_BAD_OUTBUF; + } + if ( xlen >= ctx->length_remaining ) { + return RNG_BAD_REQ_LEN; + } + + ctx->length_remaining -= xlen; + + offset = 0; + while ( xlen > 0 ) { + if ( xlen <= (16-ctx->buffer_pos) ) { // buffer has what we need + memcpy(x+offset, ctx->buffer+ctx->buffer_pos, xlen); + ctx->buffer_pos += xlen; + + return RNG_SUCCESS; + } + + // take what's in the buffer + memcpy(x+offset, ctx->buffer+ctx->buffer_pos, 16-ctx->buffer_pos); + xlen -= 16-ctx->buffer_pos; + offset += 16-ctx->buffer_pos; + + AES256_ECB(ctx->key, ctx->ctr, ctx->buffer); + ctx->buffer_pos = 0; + + //increment the counter + for (size_t i=15; i>=12; i--) { + if ( ctx->ctr[i] == 0xff ) { + ctx->ctr[i] = 0x00; + } else { + ctx->ctr[i]++; + break; + } + } + + } + + return RNG_SUCCESS; +} diff --git a/common/nistseedexpander.h b/common/nistseedexpander.h new file mode 100644 index 00000000..5f503ccb --- /dev/null +++ b/common/nistseedexpander.h @@ -0,0 +1,39 @@ +#ifndef NISTSEEDEXPANDER_H +#define NISTSEEDEXPANDER_H + +// +// rng.h +// +// Created by Bassham, Lawrence E (Fed) on 8/29/17. +// Copyright © 2017 Bassham, Lawrence E (Fed). All rights reserved. +// Modified for PQClean by Sebastian Verschoor +// + +#include +#include + +#define NISTSEEDEXPANDER_SEED_LEN 32 + +#define RNG_SUCCESS ( 0) +#define RNG_BAD_MAXLEN (-1) +#define RNG_BAD_OUTBUF (-2) +#define RNG_BAD_REQ_LEN (-3) + +typedef struct { + uint8_t buffer[16]; + size_t buffer_pos; + size_t length_remaining; + uint8_t key[NISTSEEDEXPANDER_SEED_LEN]; + uint8_t ctr[16]; +} AES_XOF_struct; + +int +seedexpander_init(AES_XOF_struct *ctx, + const uint8_t *seed, + const uint8_t *diversifier, + size_t maxlen); + +int +seedexpander(AES_XOF_struct *ctx, uint8_t *x, size_t xlen); + +#endif /* NISTSEEDEXPANDER_H */ diff --git a/common/randombytes.h b/common/randombytes.h index 5d7df020..37353cce 100644 --- a/common/randombytes.h +++ b/common/randombytes.h @@ -8,6 +8,6 @@ #include #endif -int randombytes(uint8_t *buf, size_t xlen); +int randombytes(uint8_t *buf, size_t n); #endif diff --git a/crypto_kem/hqc-128-1-cca2/META.yml b/crypto_kem/hqc-128-1-cca2/META.yml new file mode 100644 index 00000000..9ea157c9 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_128_1_CCA2 +type: kem +claimed-nist-level: 1 +claimed-security: IND-CCA2 +length-public-key: 3125 +length-ciphertext: 6234 +length-secret-key: 3165 +length-shared-secret: 64 +nistkat-sha256: 29b6545c85a9aaf75572f112b4d4cf9078c716147f84072c4efe4ce5160f18e0 +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/LICENSE b/crypto_kem/hqc-128-1-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/Makefile b/crypto_kem/hqc-128-1-cca2/leaktime/Makefile new file mode 100644 index 00000000..466c8aaf --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-128-1-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-128-1-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-128-1-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..f8c06ff5 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/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=libhqc-128-1-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-128-1-cca2/leaktime/api.h b/crypto_kem/hqc-128-1-cca2/leaktime/api.h new file mode 100644 index 00000000..2972fb6e --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_128_1_CCA2" + +#define PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 3165 +#define PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 3125 +#define PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 6234 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/bch.c b/crypto_kem/hqc-128-1-cca2/leaktime/bch.c new file mode 100644 index 00000000..3c5b0acc --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC1281CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC1281CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC1281CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/bch.h b/crypto_kem/hqc-128-1-cca2/leaktime/bch.h new file mode 100644 index 00000000..07f301ec --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/fft.c b/crypto_kem/hqc-128-1-cca2/leaktime/fft.c new file mode 100644 index 00000000..3d1f5ae3 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1281CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1281CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/fft.h b/crypto_kem/hqc-128-1-cca2/leaktime/fft.h new file mode 100644 index 00000000..8927b53f --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC1281CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/gf.c b/crypto_kem/hqc-128-1-cca2/leaktime/gf.c new file mode 100644 index 00000000..8f1fd20d --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/gf.h b/crypto_kem/hqc-128-1-cca2/leaktime/gf.h new file mode 100644 index 00000000..7c609439 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC1281CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.c b/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..4f3af030 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC1281CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC1281CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.h b/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..8d376936 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/hqc.c b/crypto_kem/hqc-128-1-cca2/leaktime/hqc.c new file mode 100644 index 00000000..6c922279 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/hqc.h b/crypto_kem/hqc-128-1-cca2/leaktime/hqc.h new file mode 100644 index 00000000..5ac79a4a --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/kem.c b/crypto_kem/hqc-128-1-cca2/leaktime/kem.c new file mode 100644 index 00000000..143c4497 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC1281CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC1281CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/parameters.h b/crypto_kem/hqc-128-1-cca2/leaktime/parameters.h new file mode 100644 index 00000000..429e2ffd --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/parameters.h @@ -0,0 +1,112 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 24677 +#define PARAM_N1 796 +#define PARAM_N2 31 +#define PARAM_N1N2 24676 +#define PARAM_OMEGA 67 +#define PARAM_OMEGA_E 77 +#define PARAM_OMEGA_R 77 +#define PARAM_SECURITY 128 +#define PARAM_DFR_EXP 128 + +#define SECRET_KEY_BYTES PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC1281CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16755683 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 15 + +#define PARAM_DELTA 60 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 541 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0, \ + 1,0,0,1,0,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,1,0,1,1,0,0, \ + 1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,0,1,0,1,1,0,1,0, \ + 0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,0,1,1,1,1,0,1,0,0,1,1,1,1,0,1, \ + 0,0,0,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,0,1,0,0,1,0,0,0, \ + 1,1,0,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,1,0,1,0,0,1,0,0,1,1,0,1,0, \ + 0,0,0,1,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1, \ + 1,0,1,1,0,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0, \ + 0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,0,1,1, \ + 1,1,1,1,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,0,1, \ + 0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,0,0,0,1,0,1,1,0,0,0, \ + 1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,0,1,1, \ + 0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,1,1,1, \ + 0,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,0,0,1,1,0,0, \ + 0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,0,1,1, \ + 1,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0, \ + 1,0,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/parsing.c b/crypto_kem/hqc-128-1-cca2/leaktime/parsing.c new file mode 100644 index 00000000..9e94fa1f --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/parsing.h b/crypto_kem/hqc-128-1-cca2/leaktime/parsing.h new file mode 100644 index 00000000..130efdc4 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC1281CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/repetition.c b/crypto_kem/hqc-128-1-cca2/leaktime/repetition.c new file mode 100644 index 00000000..8e83d7fe --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/repetition.h b/crypto_kem/hqc-128-1-cca2/leaktime/repetition.h new file mode 100644 index 00000000..cc53cadd --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/tensor.c b/crypto_kem/hqc-128-1-cca2/leaktime/tensor.c new file mode 100644 index 00000000..2fc24179 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1281CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC1281CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/tensor.h b/crypto_kem/hqc-128-1-cca2/leaktime/tensor.h new file mode 100644 index 00000000..9f4e2b6d --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1281CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/util.c b/crypto_kem/hqc-128-1-cca2/leaktime/util.c new file mode 100644 index 00000000..1253d4c8 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC1281CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC1281CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC1281CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC1281CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC1281CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/util.h b/crypto_kem/hqc-128-1-cca2/leaktime/util.h new file mode 100644 index 00000000..65fd6491 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC1281CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC1281CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC1281CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/vector.c b/crypto_kem/hqc-128-1-cca2/leaktime/vector.c new file mode 100644 index 00000000..e0c7e7f6 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC1281CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-128-1-cca2/leaktime/vector.h b/crypto_kem/hqc-128-1-cca2/leaktime/vector.h new file mode 100644 index 00000000..6c8cd3a6 --- /dev/null +++ b/crypto_kem/hqc-128-1-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC1281CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC1281CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC1281CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC1281CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/META.yml b/crypto_kem/hqc-192-1-cca2/META.yml new file mode 100644 index 00000000..3ca1d9bd --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_192_1_CCA2 +type: kem +claimed-nist-level: 3 +claimed-security: IND-CCA2 +length-public-key: 5499 +length-ciphertext: 10981 +length-secret-key: 5539 +length-shared-secret: 64 +nistkat-sha256: ddff72bfd7bf33a9fa1b3c70a05378b0544e57207b5bb9205cacd6d69002d597 +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/LICENSE b/crypto_kem/hqc-192-1-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/Makefile b/crypto_kem/hqc-192-1-cca2/leaktime/Makefile new file mode 100644 index 00000000..3893c887 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-192-1-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-192-1-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-192-1-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..9c053300 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/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=libhqc-192-1-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-192-1-cca2/leaktime/api.h b/crypto_kem/hqc-192-1-cca2/leaktime/api.h new file mode 100644 index 00000000..c8916267 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_192_1_CCA2" + +#define PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 5539 +#define PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 5499 +#define PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 10981 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/bch.c b/crypto_kem/hqc-192-1-cca2/leaktime/bch.c new file mode 100644 index 00000000..891a7fa1 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC1921CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC1921CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC1921CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/bch.h b/crypto_kem/hqc-192-1-cca2/leaktime/bch.h new file mode 100644 index 00000000..040fb253 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/fft.c b/crypto_kem/hqc-192-1-cca2/leaktime/fft.c new file mode 100644 index 00000000..e14c6179 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1921CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1921CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/fft.h b/crypto_kem/hqc-192-1-cca2/leaktime/fft.h new file mode 100644 index 00000000..e64a5410 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC1921CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/gf.c b/crypto_kem/hqc-192-1-cca2/leaktime/gf.c new file mode 100644 index 00000000..82bb043e --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/gf.h b/crypto_kem/hqc-192-1-cca2/leaktime/gf.h new file mode 100644 index 00000000..044a7871 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC1921CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.c b/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..c53efa0f --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC1921CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC1921CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.h b/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..9b7a0a6f --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/hqc.c b/crypto_kem/hqc-192-1-cca2/leaktime/hqc.c new file mode 100644 index 00000000..a7758de7 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/hqc.h b/crypto_kem/hqc-192-1-cca2/leaktime/hqc.h new file mode 100644 index 00000000..f493bce4 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/kem.c b/crypto_kem/hqc-192-1-cca2/leaktime/kem.c new file mode 100644 index 00000000..52868ae2 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC1921CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC1921CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/parameters.h b/crypto_kem/hqc-192-1-cca2/leaktime/parameters.h new file mode 100644 index 00000000..094d8274 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/parameters.h @@ -0,0 +1,109 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 43669 +#define PARAM_N1 766 +#define PARAM_N2 57 +#define PARAM_N1N2 43662 +#define PARAM_OMEGA 101 +#define PARAM_OMEGA_E 117 +#define PARAM_OMEGA_R 117 +#define PARAM_SECURITY 192 +#define PARAM_DFR_EXP 128 + +#define SECRET_KEY_BYTES PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC1921CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16768896 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 28 + +#define PARAM_DELTA 57 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 511 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,0,0,0,1,0,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1, \ + 1,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0, \ + 0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, \ + 1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0, \ + 0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,1,1,1,0, \ + 1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,0,0,0,0,1,0, \ + 0,1,0,0,1,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1, \ + 1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,0, \ + 1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1, \ + 1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,0,1,0,1, \ + 0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1, \ + 1,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0,0,1,1, \ + 0,1,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,1,1,1,1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1, \ + 1,0,1,1,1,0,0,0,0,1,1,0,1,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/parsing.c b/crypto_kem/hqc-192-1-cca2/leaktime/parsing.c new file mode 100644 index 00000000..c7b81b0b --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/parsing.h b/crypto_kem/hqc-192-1-cca2/leaktime/parsing.h new file mode 100644 index 00000000..23d8908e --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC1921CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/repetition.c b/crypto_kem/hqc-192-1-cca2/leaktime/repetition.c new file mode 100644 index 00000000..09a0149f --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/repetition.h b/crypto_kem/hqc-192-1-cca2/leaktime/repetition.h new file mode 100644 index 00000000..66485ab9 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/tensor.c b/crypto_kem/hqc-192-1-cca2/leaktime/tensor.c new file mode 100644 index 00000000..8ab2ed87 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1921CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC1921CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/tensor.h b/crypto_kem/hqc-192-1-cca2/leaktime/tensor.h new file mode 100644 index 00000000..e4c8e4cc --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1921CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/util.c b/crypto_kem/hqc-192-1-cca2/leaktime/util.c new file mode 100644 index 00000000..868c0790 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC1921CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC1921CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC1921CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC1921CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC1921CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/util.h b/crypto_kem/hqc-192-1-cca2/leaktime/util.h new file mode 100644 index 00000000..1928a443 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC1921CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC1921CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC1921CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/vector.c b/crypto_kem/hqc-192-1-cca2/leaktime/vector.c new file mode 100644 index 00000000..3aa7b520 --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC1921CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-192-1-cca2/leaktime/vector.h b/crypto_kem/hqc-192-1-cca2/leaktime/vector.h new file mode 100644 index 00000000..049adeea --- /dev/null +++ b/crypto_kem/hqc-192-1-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC1921CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC1921CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC1921CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC1921CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/META.yml b/crypto_kem/hqc-192-2-cca2/META.yml new file mode 100644 index 00000000..cd4ce8da --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_192_2_CCA2 +type: kem +claimed-nist-level: 3 +claimed-security: IND-CCA2 +length-public-key: 5884 +length-ciphertext: 11749 +length-secret-key: 5924 +length-shared-secret: 64 +nistkat-sha256: 838916e26585828d15cabb7a0a0b9dabb63986e432735b7f6cf2ee0e823bcca3 +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/LICENSE b/crypto_kem/hqc-192-2-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/Makefile b/crypto_kem/hqc-192-2-cca2/leaktime/Makefile new file mode 100644 index 00000000..26b0cdf9 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-192-2-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-192-2-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-192-2-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..3f0c1590 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/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=libhqc-192-2-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-192-2-cca2/leaktime/api.h b/crypto_kem/hqc-192-2-cca2/leaktime/api.h new file mode 100644 index 00000000..489ea798 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_192_2_CCA2" + +#define PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 5924 +#define PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 5884 +#define PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 11749 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/bch.c b/crypto_kem/hqc-192-2-cca2/leaktime/bch.c new file mode 100644 index 00000000..3e222c04 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC1922CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC1922CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC1922CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/bch.h b/crypto_kem/hqc-192-2-cca2/leaktime/bch.h new file mode 100644 index 00000000..8db18705 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/fft.c b/crypto_kem/hqc-192-2-cca2/leaktime/fft.c new file mode 100644 index 00000000..4cf68aaf --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1922CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC1922CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/fft.h b/crypto_kem/hqc-192-2-cca2/leaktime/fft.h new file mode 100644 index 00000000..785fe766 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC1922CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/gf.c b/crypto_kem/hqc-192-2-cca2/leaktime/gf.c new file mode 100644 index 00000000..fb70d97d --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/gf.h b/crypto_kem/hqc-192-2-cca2/leaktime/gf.h new file mode 100644 index 00000000..574340a5 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC1922CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.c b/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..2505dab3 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC1922CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC1922CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.h b/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..5e8f14b7 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/hqc.c b/crypto_kem/hqc-192-2-cca2/leaktime/hqc.c new file mode 100644 index 00000000..9b883442 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/hqc.h b/crypto_kem/hqc-192-2-cca2/leaktime/hqc.h new file mode 100644 index 00000000..15a7a3ff --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/kem.c b/crypto_kem/hqc-192-2-cca2/leaktime/kem.c new file mode 100644 index 00000000..1a48651f --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC1922CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC1922CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/parameters.h b/crypto_kem/hqc-192-2-cca2/leaktime/parameters.h new file mode 100644 index 00000000..f095b43d --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/parameters.h @@ -0,0 +1,109 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 46747 +#define PARAM_N1 766 +#define PARAM_N2 61 +#define PARAM_N1N2 46726 +#define PARAM_OMEGA 101 +#define PARAM_OMEGA_E 117 +#define PARAM_OMEGA_R 117 +#define PARAM_SECURITY 192 +#define PARAM_DFR_EXP 192 + +#define SECRET_KEY_BYTES PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC1922CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16735426 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 30 + +#define PARAM_DELTA 57 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 511 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,0,0,0,1,0,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1, \ + 1,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0, \ + 0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, \ + 1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0, \ + 0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,1,1,1,0, \ + 1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,0,0,0,0,1,0, \ + 0,1,0,0,1,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1, \ + 1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,0, \ + 1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1, \ + 1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,0,1,0,1, \ + 0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1, \ + 1,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0,0,1,1, \ + 0,1,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,1,1,1,1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1, \ + 1,0,1,1,1,0,0,0,0,1,1,0,1,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/parsing.c b/crypto_kem/hqc-192-2-cca2/leaktime/parsing.c new file mode 100644 index 00000000..36bf5678 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/parsing.h b/crypto_kem/hqc-192-2-cca2/leaktime/parsing.h new file mode 100644 index 00000000..ebb725f0 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC1922CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/repetition.c b/crypto_kem/hqc-192-2-cca2/leaktime/repetition.c new file mode 100644 index 00000000..996b55d8 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/repetition.h b/crypto_kem/hqc-192-2-cca2/leaktime/repetition.h new file mode 100644 index 00000000..da49ca70 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/tensor.c b/crypto_kem/hqc-192-2-cca2/leaktime/tensor.c new file mode 100644 index 00000000..f211033a --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC1922CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC1922CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/tensor.h b/crypto_kem/hqc-192-2-cca2/leaktime/tensor.h new file mode 100644 index 00000000..e7dc6e42 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC1922CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/util.c b/crypto_kem/hqc-192-2-cca2/leaktime/util.c new file mode 100644 index 00000000..3ee71a44 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC1922CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC1922CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC1922CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC1922CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC1922CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/util.h b/crypto_kem/hqc-192-2-cca2/leaktime/util.h new file mode 100644 index 00000000..ab992499 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC1922CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC1922CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC1922CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/vector.c b/crypto_kem/hqc-192-2-cca2/leaktime/vector.c new file mode 100644 index 00000000..d4454f03 --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC1922CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-192-2-cca2/leaktime/vector.h b/crypto_kem/hqc-192-2-cca2/leaktime/vector.h new file mode 100644 index 00000000..951c54cf --- /dev/null +++ b/crypto_kem/hqc-192-2-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC1922CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC1922CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC1922CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC1922CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/META.yml b/crypto_kem/hqc-256-1-cca2/META.yml new file mode 100644 index 00000000..85f4afc9 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_256_1_CCA2 +type: kem +claimed-nist-level: 5 +claimed-security: IND-CCA2 +length-public-key: 7989 +length-ciphertext: 15961 +length-secret-key: 8029 +length-shared-secret: 64 +nistkat-sha256: 339bd96be8b2d6bfb12315550b16827c612b41ab7aa4585ded55d2bf87410968 +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/LICENSE b/crypto_kem/hqc-256-1-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/Makefile b/crypto_kem/hqc-256-1-cca2/leaktime/Makefile new file mode 100644 index 00000000..847cfde0 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-256-1-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-256-1-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-256-1-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..41cb3809 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/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=libhqc-256-1-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-256-1-cca2/leaktime/api.h b/crypto_kem/hqc-256-1-cca2/leaktime/api.h new file mode 100644 index 00000000..51fe5cad --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_256_1_CCA2" + +#define PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 8029 +#define PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 7989 +#define PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 15961 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/bch.c b/crypto_kem/hqc-256-1-cca2/leaktime/bch.c new file mode 100644 index 00000000..2600ec1a --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC2561CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC2561CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC2561CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/bch.h b/crypto_kem/hqc-256-1-cca2/leaktime/bch.h new file mode 100644 index 00000000..ab94cdee --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/fft.c b/crypto_kem/hqc-256-1-cca2/leaktime/fft.c new file mode 100644 index 00000000..e0ebf626 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2561CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2561CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/fft.h b/crypto_kem/hqc-256-1-cca2/leaktime/fft.h new file mode 100644 index 00000000..2091eda8 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC2561CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/gf.c b/crypto_kem/hqc-256-1-cca2/leaktime/gf.c new file mode 100644 index 00000000..81da2f02 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/gf.h b/crypto_kem/hqc-256-1-cca2/leaktime/gf.h new file mode 100644 index 00000000..4dee7806 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC2561CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.c b/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..519acb23 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC2561CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC2561CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.h b/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..4da1c16a --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/hqc.c b/crypto_kem/hqc-256-1-cca2/leaktime/hqc.c new file mode 100644 index 00000000..642a794e --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/hqc.h b/crypto_kem/hqc-256-1-cca2/leaktime/hqc.h new file mode 100644 index 00000000..72157a68 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/kem.c b/crypto_kem/hqc-256-1-cca2/leaktime/kem.c new file mode 100644 index 00000000..daeb9aa9 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC2561CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC2561CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/parameters.h b/crypto_kem/hqc-256-1-cca2/leaktime/parameters.h new file mode 100644 index 00000000..dd96ea38 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/parameters.h @@ -0,0 +1,109 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 63587 +#define PARAM_N1 766 +#define PARAM_N2 83 +#define PARAM_N1N2 63578 +#define PARAM_OMEGA 133 +#define PARAM_OMEGA_E 153 +#define PARAM_OMEGA_R 153 +#define PARAM_SECURITY 256 +#define PARAM_DFR_EXP 128 + +#define SECRET_KEY_BYTES PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC2561CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16723381 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 41 + +#define PARAM_DELTA 57 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 511 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,0,0,0,1,0,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1, \ + 1,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,1,1,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0, \ + 0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, \ + 1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,1,0,0,1,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0, \ + 0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,1,1,1,0, \ + 1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,1,0,0,0,0,1,0, \ + 0,1,0,0,1,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,1, \ + 1,1,1,1,1,0,1,0,1,0,1,1,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,0, \ + 1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1, \ + 1,1,1,0,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,1,0,0,1,1,1,1,1,0,1,0,1, \ + 0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,0,0,1,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1, \ + 1,0,1,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,0,0,0,1,1, \ + 0,1,0,0,1,0,0,0,1,0,1,0,1,0,0,0,1,1,1,1,1,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,1, \ + 1,0,1,1,1,0,0,0,0,1,1,0,1,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,1,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/parsing.c b/crypto_kem/hqc-256-1-cca2/leaktime/parsing.c new file mode 100644 index 00000000..98de6dfd --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/parsing.h b/crypto_kem/hqc-256-1-cca2/leaktime/parsing.h new file mode 100644 index 00000000..652b51a8 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC2561CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/repetition.c b/crypto_kem/hqc-256-1-cca2/leaktime/repetition.c new file mode 100644 index 00000000..1119cf03 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/repetition.h b/crypto_kem/hqc-256-1-cca2/leaktime/repetition.h new file mode 100644 index 00000000..0a32a261 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/tensor.c b/crypto_kem/hqc-256-1-cca2/leaktime/tensor.c new file mode 100644 index 00000000..52981c33 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2561CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC2561CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/tensor.h b/crypto_kem/hqc-256-1-cca2/leaktime/tensor.h new file mode 100644 index 00000000..ea1cc11e --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2561CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/util.c b/crypto_kem/hqc-256-1-cca2/leaktime/util.c new file mode 100644 index 00000000..fb5a9540 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC2561CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC2561CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC2561CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC2561CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC2561CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/util.h b/crypto_kem/hqc-256-1-cca2/leaktime/util.h new file mode 100644 index 00000000..93f7b921 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC2561CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC2561CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC2561CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/vector.c b/crypto_kem/hqc-256-1-cca2/leaktime/vector.c new file mode 100644 index 00000000..feab7d84 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC2561CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-256-1-cca2/leaktime/vector.h b/crypto_kem/hqc-256-1-cca2/leaktime/vector.h new file mode 100644 index 00000000..750c9240 --- /dev/null +++ b/crypto_kem/hqc-256-1-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC2561CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC2561CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC2561CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC2561CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/META.yml b/crypto_kem/hqc-256-2-cca2/META.yml new file mode 100644 index 00000000..09130c43 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_256_2_CCA2 +type: kem +claimed-nist-level: 5 +claimed-security: IND-CCA2 +length-public-key: 8503 +length-ciphertext: 16985 +length-secret-key: 8543 +length-shared-secret: 64 +nistkat-sha256: df224b5438e4958b636d0d5353c869c65c9b881cc8e8fc940295013b191e213c +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/LICENSE b/crypto_kem/hqc-256-2-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/Makefile b/crypto_kem/hqc-256-2-cca2/leaktime/Makefile new file mode 100644 index 00000000..78248987 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-256-2-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-256-2-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-256-2-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..dab014ac --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/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=libhqc-256-2-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-256-2-cca2/leaktime/api.h b/crypto_kem/hqc-256-2-cca2/leaktime/api.h new file mode 100644 index 00000000..f35b126f --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_256_2_CCA2" + +#define PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 8543 +#define PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 8503 +#define PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 16985 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/bch.c b/crypto_kem/hqc-256-2-cca2/leaktime/bch.c new file mode 100644 index 00000000..581d407e --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC2562CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC2562CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC2562CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/bch.h b/crypto_kem/hqc-256-2-cca2/leaktime/bch.h new file mode 100644 index 00000000..eef59649 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/fft.c b/crypto_kem/hqc-256-2-cca2/leaktime/fft.c new file mode 100644 index 00000000..12beff4a --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2562CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2562CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/fft.h b/crypto_kem/hqc-256-2-cca2/leaktime/fft.h new file mode 100644 index 00000000..3f42ad82 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC2562CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/gf.c b/crypto_kem/hqc-256-2-cca2/leaktime/gf.c new file mode 100644 index 00000000..c8e82c42 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/gf.h b/crypto_kem/hqc-256-2-cca2/leaktime/gf.h new file mode 100644 index 00000000..f81709a4 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC2562CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.c b/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..4058ed70 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC2562CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC2562CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.h b/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..9cdb82fc --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/hqc.c b/crypto_kem/hqc-256-2-cca2/leaktime/hqc.c new file mode 100644 index 00000000..66bd7934 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/hqc.h b/crypto_kem/hqc-256-2-cca2/leaktime/hqc.h new file mode 100644 index 00000000..44a9713c --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/kem.c b/crypto_kem/hqc-256-2-cca2/leaktime/kem.c new file mode 100644 index 00000000..b83fef61 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC2562CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC2562CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/parameters.h b/crypto_kem/hqc-256-2-cca2/leaktime/parameters.h new file mode 100644 index 00000000..04dec2f8 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/parameters.h @@ -0,0 +1,112 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 67699 +#define PARAM_N1 796 +#define PARAM_N2 85 +#define PARAM_N1N2 67660 +#define PARAM_OMEGA 133 +#define PARAM_OMEGA_E 153 +#define PARAM_OMEGA_R 153 +#define PARAM_SECURITY 256 +#define PARAM_DFR_EXP 192 + +#define SECRET_KEY_BYTES PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC2562CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16721653 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 42 + +#define PARAM_DELTA 60 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 541 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0, \ + 1,0,0,1,0,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,1,0,1,1,0,0, \ + 1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,0,1,0,1,1,0,1,0, \ + 0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,0,1,1,1,1,0,1,0,0,1,1,1,1,0,1, \ + 0,0,0,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,0,1,0,0,1,0,0,0, \ + 1,1,0,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,1,0,1,0,0,1,0,0,1,1,0,1,0, \ + 0,0,0,1,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1, \ + 1,0,1,1,0,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0, \ + 0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,0,1,1, \ + 1,1,1,1,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,0,1, \ + 0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,0,0,0,1,0,1,1,0,0,0, \ + 1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,0,1,1, \ + 0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,1,1,1, \ + 0,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,0,0,1,1,0,0, \ + 0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,0,1,1, \ + 1,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0, \ + 1,0,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/parsing.c b/crypto_kem/hqc-256-2-cca2/leaktime/parsing.c new file mode 100644 index 00000000..05a5993d --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/parsing.h b/crypto_kem/hqc-256-2-cca2/leaktime/parsing.h new file mode 100644 index 00000000..1f9a08d3 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC2562CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/repetition.c b/crypto_kem/hqc-256-2-cca2/leaktime/repetition.c new file mode 100644 index 00000000..11e10f3e --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/repetition.h b/crypto_kem/hqc-256-2-cca2/leaktime/repetition.h new file mode 100644 index 00000000..18473a72 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/tensor.c b/crypto_kem/hqc-256-2-cca2/leaktime/tensor.c new file mode 100644 index 00000000..9b4ef5d2 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2562CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC2562CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/tensor.h b/crypto_kem/hqc-256-2-cca2/leaktime/tensor.h new file mode 100644 index 00000000..61940065 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2562CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/util.c b/crypto_kem/hqc-256-2-cca2/leaktime/util.c new file mode 100644 index 00000000..c85867bb --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC2562CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC2562CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC2562CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC2562CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC2562CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/util.h b/crypto_kem/hqc-256-2-cca2/leaktime/util.h new file mode 100644 index 00000000..4851d4a7 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC2562CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC2562CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC2562CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/vector.c b/crypto_kem/hqc-256-2-cca2/leaktime/vector.c new file mode 100644 index 00000000..1afe22f2 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC2562CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-256-2-cca2/leaktime/vector.h b/crypto_kem/hqc-256-2-cca2/leaktime/vector.h new file mode 100644 index 00000000..aab09565 --- /dev/null +++ b/crypto_kem/hqc-256-2-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC2562CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC2562CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC2562CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC2562CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/META.yml b/crypto_kem/hqc-256-3-cca2/META.yml new file mode 100644 index 00000000..dd7c367a --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/META.yml @@ -0,0 +1,23 @@ +name: HQC_256_3_CCA2 +type: kem +claimed-nist-level: 5 +claimed-security: IND-CCA2 +length-public-key: 8897 +length-ciphertext: 17777 +length-secret-key: 8937 +length-shared-secret: 64 +nistkat-sha256: e0bb4e73a1a27f05ddb1138685922bf4a40c2e535b5152b93135c06a73777770 +principal-submitters: + - Carlos Aguilar Melchor + - Nicolas Aragon + - Slim Bettaieb + - Loïc Bidoux + - Olivier Blazy + - Jean-Christophe Deneuville + - Philippe Gaborit + - Edoardo Persichetti + - Gilles Zémor +auxiliary-submitters: [] +implementations: + - name: leaktime + version: https://pqc-hqc.org/doc/hqc-reference-implementation_2019-08-24.zip diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/LICENSE b/crypto_kem/hqc-256-3-cca2/leaktime/LICENSE new file mode 100644 index 00000000..85265b0a --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/LICENSE @@ -0,0 +1 @@ +Public domain diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/Makefile b/crypto_kem/hqc-256-3-cca2/leaktime/Makefile new file mode 100644 index 00000000..2e9689c6 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/Makefile @@ -0,0 +1,19 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libhqc-256-3-cca2_leaktime.a +HEADERS=api.h bch.h fft.h gf.h gf2x.h hqc.h parameters.h parsing.h repetition.h tensor.h vector.h util.h +OBJECTS=bch.o fft.o gf.o gf2x.o hqc.o kem.o parsing.o repetition.o tensor.o vector.o util.o + +CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -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/crypto_kem/hqc-256-3-cca2/leaktime/Makefile.Microsoft_nmake b/crypto_kem/hqc-256-3-cca2/leaktime/Makefile.Microsoft_nmake new file mode 100644 index 00000000..767f0cf4 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/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=libhqc-256-3-cca2_leaktime.lib +OBJECTS=bch.obj fft.obj gf.obj gf2x.obj hqc.obj kem.obj parsing.obj repetition.obj tensor.obj vector.obj util.obj + +# We ignore warning C4127: we sometimes use a conditional that depending +# on the parameters results in a case where if (const) is the case. +# The compiler should just optimise this away, but on MSVC we get +# a compiler complaint. +CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX /wd4127 + +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/crypto_kem/hqc-256-3-cca2/leaktime/api.h b/crypto_kem/hqc-256-3-cca2/leaktime/api.h new file mode 100644 index 00000000..e11adee3 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/api.h @@ -0,0 +1,25 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_API_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_API_H + +/** + * \file api.h + * \brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme + */ + +#include + +#define PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_ALGNAME "HQC_256_3_CCA2" + +#define PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES 8937 +#define PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES 8897 +#define PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_BYTES 64 +#define PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES 17777 + +// As a technicality, the public key is appended to the secret key in order to respect the NIST API. +// Without this constraint, CRYPTO_SECRETKEYBYTES would be defined as 32 + +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk); +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk); +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/bch.c b/crypto_kem/hqc-256-3-cca2/leaktime/bch.c new file mode 100644 index 00000000..d9a819a5 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/bch.c @@ -0,0 +1,295 @@ +/** + * @file bch.c + * Constant time implementation of BCH codes + */ + +#include "bch.h" +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include "vector.h" +#include +#include + +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message); +static void lfsr_encode(uint8_t *codeword, const uint8_t *message); +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked); +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes); +static void message_from_codeword(uint8_t *message, const uint8_t *codeword); +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector); +static void compute_roots(uint8_t *error, const uint16_t *sigma); + + +/** + * @brief Unpacks the message message to the array message_unpacked where each byte stores a bit of the message + * + * @param[out] message_unpacked Array of VEC_K_SIZE_BYTES bytes receiving the unpacked message + * @param[in] message Array of PARAM_K bytes storing the packed message + */ +static void unpack_message(uint8_t *message_unpacked, const uint8_t *message) { + for (size_t i = 0; i < (VEC_K_SIZE_BYTES - (PARAM_K % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + message_unpacked[j + 8 * i] = (message[i] >> j) & 0x01; + } + } + + for (int8_t j = 0; j < PARAM_K % 8; ++j) { + message_unpacked[j + 8 * (VEC_K_SIZE_BYTES - 1)] = (message[VEC_K_SIZE_BYTES - 1] >> j) & 0x01; + } +} + + + +/** + * @brief Encodes the message message to a codeword codeword using the generator polynomial bch_poly of the code + * + * @param[out] codeword Array of PARAM_N1 bytes receiving the codeword + * @param[in] message Array of PARAM_K bytes storing the message to encode + */ +static void lfsr_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t gate_value = 0; + uint8_t bch_poly[PARAM_G] = PARAM_BCH_POLY; + + // Compute the Parity-check digits + for (int16_t i = PARAM_K - 1; i >= 0; --i) { + gate_value = message[i] ^ codeword[PARAM_N1 - PARAM_K - 1]; + + for (size_t j = PARAM_N1 - PARAM_K - 1; j; --j) { + codeword[j] = codeword[j - 1] ^ (-gate_value & bch_poly[j]); + } + + codeword[0] = gate_value; + } + + // Add the message + memcpy(codeword + PARAM_N1 - PARAM_K, message, PARAM_K); +} + + + +/** + * @brief Packs the codeword from an array codeword_unpacked where each byte stores a bit to a compact array codeword + * + * @param[out] codeword Array of VEC_N1_SIZE_BYTES bytes receiving the packed codeword + * @param[in] codeword_unpacked Array of PARAM_N1 bytes storing the unpacked codeword + */ +static void pack_codeword(uint8_t *codeword, const uint8_t *codeword_unpacked) { + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0)); ++i) { + for (size_t j = 0; j < 8; ++j) { + codeword[i] |= codeword_unpacked[j + 8 * i] << j; + } + } + + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + codeword[VEC_N1_SIZE_BYTES - 1] |= codeword_unpacked[j + 8 * (VEC_N1_SIZE_BYTES - 1)] << j; + } +} + + + +/** + * @brief Encodes a message message of PARAM_K bits to a BCH codeword codeword of PARAM_N1 bits + * + * Following @cite lin1983error (Chapter 4 - Cyclic Codes), + * We perform a systematic encoding using a linear (PARAM_N1 - PARAM_K)-stage shift register + * with feedback connections based on the generator polynomial bch_poly of the BCH code. + * + * @param[out] codeword Array of size VEC_N1_SIZE_BYTES receiving the encoded message + * @param[in] message Array of size VEC_K_SIZE_BYTES storing the message + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message) { + uint8_t message_unpacked[PARAM_K]; + uint8_t codeword_unpacked[PARAM_N1] = {0}; + + unpack_message(message_unpacked, message); + lfsr_encode(codeword_unpacked, message_unpacked); + pack_codeword(codeword, codeword_unpacked); +} + + + +/** + * @brief Computes the error locator polynomial (ELP) sigma + * + * This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding).
+ * We use the letter p for rho which is initialized at -1/2.
+ * The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X).
+ * Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p.
+ * sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated.
+ * We can properly correct only if the degree of sigma does not exceed PARAM_DELTA. + * This means only the first PARAM_DELTA + 1 coefficients of sigma are of value + * and we only need to save its first PARAM_DELTA - 1 coefficients. + * + * @returns the degree of the ELP sigma + * @param[out] sigma Array of size (at least) PARAM_DELTA receiving the ELP + * @param[in] syndromes Array of size (at least) 2*PARAM_DELTA storing the syndromes + */ +static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes) { + sigma[0] = 1; + size_t deg_sigma = 0; + size_t deg_sigma_p = 0; + uint16_t sigma_copy[PARAM_DELTA - 1] = {0}; + size_t deg_sigma_copy = 0; + uint16_t X_sigma_p[PARAM_DELTA + 1] = {0, 1}; + int32_t pp = -1; // 2*rho + uint16_t d_p = 1; + uint16_t d = syndromes[0]; + + for (size_t mu = 0; mu < PARAM_DELTA; ++mu) { + // Save sigma in case we need it to update X_sigma_p + memcpy(sigma_copy, sigma, 2 * (PARAM_DELTA - 1)); + deg_sigma_copy = deg_sigma; + + uint16_t dd = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(d, PQCLEAN_HQC2563CCA2_LEAKTIME_gf_inverse(d_p)); // 0 if(d == 0) + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + sigma[i] ^= PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(dd, X_sigma_p[i]); + } + + size_t deg_X = 2 * mu - pp; // 2*(mu-rho) + size_t deg_X_sigma_p = deg_X + deg_sigma_p; + + // mask1 = 0xffff if(d != 0) and 0 otherwise + int16_t mask1 = -((uint16_t) - d >> 15); + + // mask2 = 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise + int16_t mask2 = -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15); + + // mask12 = 0xffff if the deg_sigma increased and 0 otherwise + int16_t mask12 = mask1 & mask2; + deg_sigma = (mask12 & deg_X_sigma_p) ^ (~mask12 & deg_sigma); + + if (mu == PARAM_DELTA - 1) { + break; + } + + // Update pp, d_p and X_sigma_p if needed + pp = (mask12 & (2 * mu)) ^ (~mask12 & pp); + d_p = (mask12 & d) ^ (~mask12 & d_p); + for (size_t i = PARAM_DELTA - 1; i; --i) { + X_sigma_p[i + 1] = (mask12 & sigma_copy[i - 1]) ^ (~mask12 & X_sigma_p[i - 1]); + } + X_sigma_p[1] = 0; + X_sigma_p[0] = 0; + deg_sigma_p = (mask12 & deg_sigma_copy) ^ (~mask12 & deg_sigma_p); + + // Compute the next discrepancy + d = syndromes[2 * mu + 2]; + for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) { + d ^= PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(sigma[i], syndromes[2 * mu + 2 - i]); + } + } + + return deg_sigma; +} + + + +/** + * @brief Retrieves the message message from the codeword codeword + * + * Since we performed a systematic encoding, the message is the last PARAM_K bits of the codeword. + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the message + * @param[in] codeword Array of size VEC_N1_SIZE_BYTES storing the codeword + */ +static void message_from_codeword(uint8_t *message, const uint8_t *codeword) { + int32_t val = PARAM_N1 - PARAM_K; + + uint8_t mask1 = 0xff << val % 8; + uint8_t mask2 = 0xff >> (8 - val % 8); + size_t index = val / 8; + + for (size_t i = 0; i < VEC_K_SIZE_BYTES - 1; ++i) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[i] = message1 | message2; + } + + // Last byte (8-val % 8 is the number of bits given by message1) + if ((PARAM_K % 8 == 0) || (8 - val % 8 < PARAM_K % 8)) { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + uint8_t message2 = (codeword[++index] & mask2) << (8 - val % 8); + message[VEC_K_SIZE_BYTES - 1] = message1 | message2; + } else { + uint8_t message1 = (codeword[index] & mask1) >> val % 8; + message[VEC_K_SIZE_BYTES - 1] = message1; + } +} + + + +/** + * @brief Computes the 2^PARAM_DELTA syndromes from the received vector vector + * + * Syndromes are the sum of powers of alpha weighted by vector's coefficients. + * To do so, we use the additive FFT transpose, which takes as input a family w of GF(2^PARAM_M) elements + * and outputs the weighted power sums of these w.
+ * Therefore, this requires twisting and applying a permutation before feeding vector to the fft transpose.
+ * For more details see Berstein, Chou and Schawbe's explanations: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +static void compute_syndromes(uint16_t *syndromes, const uint8_t *vector) { + uint16_t w[1 << PARAM_M]; + + PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(w, vector); + PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t(syndromes, w, 2 * PARAM_DELTA); +} + + + +/** + * @brief Computes the error polynomial error from the error locator polynomial sigma + * + * See function fft for more details. + * + * @param[out] err Array of VEC_N1_SIZE_BYTES elements receiving the error polynomial + * @param[in] sigma Array of 2^PARAM_FFT elements storing the error locator polynomial + */ +static void compute_roots(uint8_t *error, const uint16_t *sigma) { + uint16_t w[1 << PARAM_M] = {0}; // w will receive the evaluation of sigma in all field elements + + PQCLEAN_HQC2563CCA2_LEAKTIME_fft(w, sigma, PARAM_DELTA + 1); + PQCLEAN_HQC2563CCA2_LEAKTIME_fft_retrieve_bch_error_poly(error, w); +} + + + +/** + * @brief Decodes the received word + * + * This function relies on four steps: + *
    + *
  1. The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes. + *
  2. The second step is the computation of the error-locator polynomial sigma. + *
  3. The third step, done by additive FFT, is finding the error-locator numbers by calculating the roots of the polynomial sigma and takings their inverses. + *
  4. The fourth step is the correction of the errors in the received polynomial. + *
+ * For a more complete picture on BCH decoding, see Shu. Lin and Daniel J. Costello in Error Control Coding: Fundamentals and Applications @cite lin1983error + * + * @param[out] message Array of size VEC_K_SIZE_BYTES receiving the decoded message + * @param[in] vector Array of size VEC_N1_SIZE_BYTES storing the received word + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector) { + uint16_t syndromes[1 << PARAM_FFT_T]; + uint16_t sigma[1 << PARAM_FFT] = {0}; + uint8_t error[(1 << PARAM_M) / 8] = {0}; + + // Calculate the 2*PARAM_DELTA syndromes + compute_syndromes(syndromes, vector); + + // Compute the error locator polynomial sigma + // Sigma's degree is at most PARAM_DELTA but the FFT requires the extra room + compute_elp(sigma, syndromes); + + // Compute the error polynomial error + compute_roots(error, sigma); + + // Add the error polynomial to the received polynomial + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(vector, vector, error, VEC_N1_SIZE_BYTES); + + // Retrieve the message from the decoded codeword + message_from_codeword(message, vector); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/bch.h b/crypto_kem/hqc-256-3-cca2/leaktime/bch.h new file mode 100644 index 00000000..4158b758 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/bch.h @@ -0,0 +1,16 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_BCH_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_BCH_H + +/** + * @file bch.h + * Header file of bch.c + */ + +#include "parameters.h" +#include +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_encode(uint8_t *codeword, const uint8_t *message); +void PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_decode(uint8_t *message, uint8_t *vector); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/fft.c b/crypto_kem/hqc-256-3-cca2/leaktime/fft.c new file mode 100644 index 00000000..c04f5bb4 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/fft.c @@ -0,0 +1,628 @@ +/** + * @file fft.c + * Implementation of the additive FFT and its transpose. + * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + */ + +#include "fft.h" +#include "gf.h" +#include "parameters.h" +#include +#include + +static void compute_fft_betas(uint16_t *betas); +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size); +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f); +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f); +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, uint8_t m, uint32_t m_f, const uint16_t *betas); + + +/** + * @brief Computes the basis of betas (omitting 1) used in the additive FFT and its transpose + * + * @param[out] betas Array of size PARAM_M-1 + */ +static void compute_fft_betas(uint16_t *betas) { + for (size_t i = 0; i < PARAM_M - 1; ++i) { + betas[i] = 1 << (PARAM_M - 1 - i); + } +} + + + +/** + * @brief Computes the subset sums of the given set + * + * The array subset_sums is such that its ith element is + * the subset sum of the set elements given by the binary form of i. + * + * @param[out] subset_sums Array of size 2^set_size receiving the subset sums + * @param[in] set Array of set_size elements + * @param[in] set_size Size of the array set + */ +static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, size_t set_size) { + subset_sums[0] = 0; + + for (size_t i = 0; i < set_size; ++i) { + for (size_t j = 0; j < (((size_t)1) << i); ++j) { + subset_sums[(((size_t)1) << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + + + +/** + * @brief Transpose of the linear radix conversion + * + * This is a direct transposition of the radix function + * implemented following the process of transposing a linear function as exposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size a power of 2 + * @param[in] f0 Array half the size of f + * @param[in] f1 Array half the size of f + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) { + switch (m_f) { + case 4: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + f[8] = f[4] ^ f0[4]; + f[9] = f[5] ^ f1[4]; + f[10] = f[6] ^ f0[5] ^ f1[4]; + f[11] = f[7] ^ f0[5] ^ f1[4] ^ f1[5]; + f[12] = f[8] ^ f0[5] ^ f0[6] ^ f1[4]; + f[13] = f[7] ^ f[9] ^ f[11] ^ f1[6]; + f[14] = f[6] ^ f0[6] ^ f0[7] ^ f1[6]; + f[15] = f[7] ^ f0[7] ^ f1[7]; + return; + + case 3: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + f[4] = f[2] ^ f0[2]; + f[5] = f[3] ^ f1[2]; + f[6] = f[4] ^ f0[3] ^ f1[2]; + f[7] = f[3] ^ f0[3] ^ f1[3]; + return; + + case 2: + f[0] = f0[0]; + f[1] = f1[0]; + f[2] = f0[1] ^ f1[0]; + f[3] = f[2] ^ f1[1]; + return; + + case 1: + f[0] = f0[0]; + f[1] = f1[0]; + return; + + default: + ; + + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t Q1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t R1[1 << (PARAM_FFT_T - 2)] = {0}; + + uint16_t Q[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + uint16_t R[1 << 2 * (PARAM_FFT_T - 2)] = {0}; + + memcpy(Q0, f0 + n, 2 * n); + memcpy(Q1, f1 + n, 2 * n); + memcpy(R0, f0, 2 * n); + memcpy(R1, f1, 2 * n); + + radix_t (Q, Q0, Q1, m_f - 1); + radix_t (R, R0, R1, m_f - 1); + + memcpy(f, R, 4 * n); + memcpy(f + 2 * n, R + n, 2 * n); + memcpy(f + 3 * n, Q + n, 2 * n); + + for (size_t i = 0; i < n; ++i) { + f[2 * n + i] ^= Q[i]; + f[3 * n + i] ^= f[2 * n + i]; + } + } +} + + + +/** + * @brief Recursively computes syndromes of family w + * + * This function is a subroutine of the function fft_t + * + * @param[out] f Array receiving the syndromes + * @param[in] w Array storing the family + * @param[in] f_coeffs Length of syndromes vector + * @param[in] m 2^m is the smallest power of 2 greater or equal to the length of family w + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the length of f + * @param[in] betas FFT constants + */ +static void fft_t_rec(uint16_t *f, const uint16_t *w, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t f0[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT_T - 2)] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + + // Step 1 + if (m_f == 1) { + for (size_t i = 0; i < (((size_t)1) << m); ++i) { + f[0] ^= w[i]; + } + + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + size_t index = (((size_t)1) << j) + ki; + betas_sums[index] = betas_sums[ki] ^ betas[j]; + f[1] ^= PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas_sums[index], w[index]); + } + } + + return; + } + + // Compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2563CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas subset sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + /* Step 6: Compute u and v from w (aka w) + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + if (f_coeffs <= 3) { // 3-coefficient polynomial f case + // Step 5: Compute f0 from u and f1 from v + f1[1] = 0; + u[0] = w[0] ^ w[k]; + f1[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + f1[0] ^= PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + } else { + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(gammas_sums[i], u[i]) ^ w[k + i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, m - 1, m_f - 1, deltas); + } + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, m_f); + + // Step 2: compute f from g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } +} + + + +/** + * @brief Computes the syndromes f of the family w + * + * Since the syndromes linear map is the transpose of multipoint evaluation, + * it uses exactly the same constants, either hardcoded or precomputed by compute_fft_lut(...).
+ * This follows directives from Bernstein, Chou and Schwabe given here: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f Array of size 2*(PARAM_FFT_T) elements receiving the syndromes + * @param[in] w Array of PARAM_GF_MUL_ORDER+1 elements + * @param[in] f_coeffs Length of syndromes vector f + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs) { + // Transposed from Gao and Mateer algorithm + uint16_t betas[PARAM_M - 1]; + uint16_t betas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + uint16_t f0[1 << (PARAM_FFT_T - 1)]; + uint16_t f1[1 << (PARAM_FFT_T - 1)]; + + compute_fft_betas(betas); + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + /* Step 6: Compute u and v from w (aka w) + * + * We had: + * w[i] = u[i] + G[i].v[i] + * w[k+i] = w[i] + v[i] = u[i] + (G[i]+1).v[i] + * Transpose: + * u[i] = w[i] + w[k+i] + * v[i] = G[i].w[i] + (G[i]+1).w[k+i] = G[i].u[i] + w[k+i] */ + u[0] = w[0] ^ w[k]; + v[0] = w[k]; + for (size_t i = 1; i < k; ++i) { + u[i] = w[i] ^ w[k + i]; + v[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas_sums[i], u[i]) ^ w[k + i]; + } + + // Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5: Compute f0 from u and f1 from v + fft_t_rec(f0, u, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + fft_t_rec(f1, v, f_coeffs / 2, PARAM_M - 1, PARAM_FFT_T - 1, deltas); + + // Step 3: Compute g from g0 and g1 + radix_t(f, f0, f1, PARAM_FFT_T); + + // Step 2: beta_m = 1 so f = g +} + + + +/** + * @brief Computes the radix conversion of a polynomial f in GF(2^m)[x] + * + * Computes f0 and f1 such that f(x) = f0(x^2-x) + x.f1(x^2-x) + * as proposed by Bernstein, Chou and Schwabe: + * https://binary.cr.yp.to/mcbits-20130616.pdf + * + * @param[out] f0 Array half the size of f + * @param[out] f1 Array half the size of f + * @param[in] f Array of size a power of 2 + * @param[in] m_f 2^{m_f} is the smallest power of 2 greater or equal to the number of coefficients of f + */ +static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) { + switch (m_f) { + case 4: + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 3: + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + return; + + case 2: + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + return; + + case 1: + f0[0] = f[0]; + f1[0] = f[1]; + return; + + default: + ; + size_t n = ((size_t)1) << (m_f - 2); + + uint16_t Q[2 * (1 << (PARAM_FFT - 2))]; + uint16_t R[2 * (1 << (PARAM_FFT - 2))]; + + uint16_t Q0[1 << (PARAM_FFT - 2)]; + uint16_t Q1[1 << (PARAM_FFT - 2)]; + uint16_t R0[1 << (PARAM_FFT - 2)]; + uint16_t R1[1 << (PARAM_FFT - 2)]; + + memcpy(Q, f + 3 * n, 2 * n); + memcpy(Q + n, f + 3 * n, 2 * n); + memcpy(R, f, 4 * n); + + for (size_t i = 0; i < n; ++i) { + Q[i] ^= f[2 * n + i]; + R[n + i] ^= Q[i]; + } + + radix(Q0, Q1, Q, m_f - 1); + radix(R0, R1, R, m_f - 1); + + memcpy(f0, R0, 2 * n); + memcpy(f0 + n, Q0, 2 * n); + memcpy(f1, R1, 2 * n); + memcpy(f1 + n, Q1, 2 * n); + } +} + + + +/** + * @brief Evaluates f at all subset sums of a given set + * + * This function is a subroutine of the function fft. + * + * @param[out] w Array + * @param[in] f Array + * @param[in] f_coeffs Number of coefficients of f + * @param[in] m Number of betas + * @param[in] m_f Number of coefficients of f (one more than its degree) + * @param[in] betas FFT constants + */ +static void fft_rec(uint16_t *w, uint16_t *f, size_t f_coeffs, + uint8_t m, uint32_t m_f, const uint16_t *betas) { + + uint16_t f0[1 << (PARAM_FFT - 2)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 2)] = {0}; + uint16_t gammas[PARAM_M - 2] = {0}; + uint16_t deltas[PARAM_M - 2] = {0}; + size_t k = ((size_t)1) << (m - 1); + uint16_t gammas_sums[1 << (PARAM_M - 2)] = {0}; + uint16_t u[1 << (PARAM_M - 2)] = {0}; + uint16_t v[1 << (PARAM_M - 2)] = {0}; + + // Step 1 + if (m_f == 1) { + uint16_t tmp[PARAM_M - (PARAM_FFT - 1)]; + for (size_t i = 0; i < m; ++i) { + tmp[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas[i], f[1]); + } + + w[0] = f[0]; + for (size_t j = 0; j < m; ++j) { + for (size_t ki = 0; ki < (((size_t)1) << j); ++ki) { + w[(((size_t)1) << j) + ki] = w[ki] ^ tmp[j]; + } + } + + return; + } + + // Step 2: compute g + if (betas[m - 1] != 1) { + uint16_t beta_m_pow = 1; + for (size_t i = 1; i < (((size_t)1) << m_f); ++i) { + beta_m_pow = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(beta_m_pow, betas[m - 1]); + f[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(beta_m_pow, f[i]); + } + } + + // Step 3 + radix(f0, f1, f, m_f); + + // Step 4: compute gammas and deltas + for (uint8_t i = 0; i < m - 1; ++i) { + gammas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas[i], PQCLEAN_HQC2563CCA2_LEAKTIME_gf_inverse(betas[m - 1])); + deltas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gammas sums + compute_subset_sums(gammas_sums, gammas, m - 1); + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, m - 1, m_f - 1, deltas); + + if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + fft_rec(v, f1, f_coeffs / 2, m - 1, m_f - 1, deltas); + + // Step 6 + memcpy(w + k, v, 2 * k); + w[0] = u[0]; + w[k] ^= u[0]; + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + + + +/** + * @brief Evaluates f on all fields elements using an additive FFT algorithm + * + * f_coeffs is the number of coefficients of f (one less than its degree).
+ * The FFT proceeds recursively to evaluate f at all subset sums of a basis B.
+ * This implementation is based on the paper from Gao and Mateer:
+ * Shuhong Gao and Todd Mateer, Additive Fast Fourier Transforms over Finite Fields, + * IEEE Transactions on Information Theory 56 (2010), 6265--6272. + * http://www.math.clemson.edu/~sgao/papers/GM10.pdf
+ * and includes improvements proposed by Bernstein, Chou and Schwabe here: + * https://binary.cr.yp.to/mcbits-20130616.pdf
+ * Constants betas, gammas and deltas involved in the algorithm are either hardcoded or precomputed + * by the subroutine compute_fft_lut(...).
+ * Note that on this first call (as opposed to the recursive calls to fft_rec), gammas are equal to betas, + * meaning the first gammas subset sums are actually the subset sums of betas (except 1).
+ * Also note that f is altered during computation (twisted at each level). + * + * @param[out] w Array + * @param[in] f Array of 2^PARAM_FFT elements + * @param[in] f_coeffs Number coefficients of f (i.e. deg(f)+1) + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs) { + uint16_t betas[PARAM_M - 1] = {0}; + uint16_t betas_sums[1 << (PARAM_M - 1)] = {0}; + uint16_t f0[1 << (PARAM_FFT - 1)] = {0}; + uint16_t f1[1 << (PARAM_FFT - 1)] = {0}; + uint16_t deltas[PARAM_M - 1]; + size_t k = 1 << (PARAM_M - 1); + uint16_t u[1 << (PARAM_M - 1)] = {0}; + uint16_t v[1 << (PARAM_M - 1)] = {0}; + + // Follows Gao and Mateer algorithm + compute_fft_betas(betas); + + // Step 1: PARAM_FFT > 1, nothing to do + + // Compute gammas sums + compute_subset_sums(betas_sums, betas, PARAM_M - 1); + + // Step 2: beta_m = 1, nothing to do + + // Step 3 + radix(f0, f1, f, PARAM_FFT); + + // Step 4: Compute deltas + for (size_t i = 0; i < PARAM_M - 1; ++i) { + deltas[i] = PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(betas[i]) ^ betas[i]; + } + + // Step 5 + fft_rec(u, f0, (f_coeffs + 1) / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + fft_rec(v, f1, f_coeffs / 2, PARAM_M - 1, PARAM_FFT - 1, deltas); + + // Step 6, 7 and error polynomial computation + memcpy(w + k, v, 2 * k); + + // Check if 0 is root + w[0] = u[0]; + + // Check if 1 is root + w[k] ^= u[0]; + + // Find other roots + for (size_t i = 1; i < k; ++i) { + w[i] = u[i] ^ PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + + + +/** + * @brief Arranges the received word vector in a form w such that applying the additive FFT transpose to w yields the BCH syndromes of the received word vector. + * + * Since the received word vector gives coefficients of the primitive element alpha, we twist accordingly.
+ * Furthermore, the additive FFT transpose needs elements indexed by their decomposition on the chosen basis, + * so we apply the adequate permutation. + * + * @param[out] w Array of size 2^PARAM_M + * @param[in] vector Array of size VEC_N1_SIZE_BYTES + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector) { + uint16_t r[1 << PARAM_M]; + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + + // Unpack the received word vector into array r + size_t i; + for (i = 0; i < VEC_N1_SIZE_BYTES - (PARAM_N1 % 8 != 0); ++i) { + for (size_t j = 0; j < 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + } + + // Last byte + for (size_t j = 0; j < PARAM_N1 % 8; ++j) { + r[8 * i + j] = (vector[i] >> j) & 1; + } + + // Complete r with zeros + memset(r + PARAM_N1, 0, 2 * ((1 << PARAM_M) - PARAM_N1)); + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + // Twist and permute r adequately to obtain w + w[0] = 0; + w[k] = -r[0] & 1; + for (i = 1; i < k; ++i) { + w[i] = -r[PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(gammas_sums[i])] & gammas_sums[i]; + w[k + i] = -r[PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1)] & (gammas_sums[i] ^ 1); + } +} + + + +/** + * @brief Retrieves the error polynomial error from the evaluations w of the ELP (Error Locator Polynomial) on all field elements. + * + * @param[out] error Array of size VEC_N1_SIZE_BYTES + * @param[in] w Array of size 2^PARAM_M + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w) { + uint16_t gammas[PARAM_M - 1]; + uint16_t gammas_sums[1 << (PARAM_M - 1)]; + size_t k = 1 << (PARAM_M - 1); + size_t index = PARAM_GF_MUL_ORDER; + + compute_fft_betas(gammas); + compute_subset_sums(gammas_sums, gammas, PARAM_M - 1); + + error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15); + uint8_t bit = 1 ^ ((uint16_t) - w[k] >> 15); + error[index / 8] ^= bit << (index % 8); + + for (size_t i = 1; i < k; ++i) { + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(gammas_sums[i]); + bit = 1 ^ ((uint16_t) - w[i] >> 15); + error[index / 8] ^= bit << (index % 8); + + index = PARAM_GF_MUL_ORDER - PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(gammas_sums[i] ^ 1); + bit = 1 ^ ((uint16_t) - w[k + i] >> 15); + error[index / 8] ^= bit << (index % 8); + } +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/fft.h b/crypto_kem/hqc-256-3-cca2/leaktime/fft.h new file mode 100644 index 00000000..bba57af7 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/fft.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_FFT_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_FFT_H + +/** + * @file fft.h + * Header file of fft.c + */ + +#include +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs); +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_t_preprocess_bch_codeword(uint16_t *w, const uint8_t *vector); + +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs); +void PQCLEAN_HQC2563CCA2_LEAKTIME_fft_retrieve_bch_error_poly(uint8_t *error, const uint16_t *w); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/gf.c b/crypto_kem/hqc-256-3-cca2/leaktime/gf.c new file mode 100644 index 00000000..2c58abb8 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/gf.c @@ -0,0 +1,99 @@ +/** + * @file gf.c + * Galois field implementation with multiplication using lookup tables + */ + +#include "gf.h" +#include "parameters.h" +#include + + +/** + * Powers of the root alpha of x^10 + x^3 + 1. + * The last two elements are needed by the gf_mul function from gf_lutmul.c + * (for example if both elements to multiply are zero). + */ +static const uint16_t exptable[1026] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 9, 18, 36, 72, 144, 288, 576, 137, 274, 548, 65, 130, 260, 520, 25, 50, 100, 200, 400, 800, 585, 155, 310, 620, 209, 418, 836, 641, 267, 534, 37, 74, 148, 296, 592, 169, 338, 676, 321, 642, 269, 538, 61, 122, 244, 488, 976, 937, 859, 703, 375, 750, 469, 938, 861, 691, 367, 734, 437, 874, 733, 435, 870, 709, 387, 774, 517, 3, 6, 12, 24, 48, 96, 192, 384, 768, 521, 27, 54, 108, 216, 432, 864, 713, 411, 822, 613, 195, 390, 780, 529, 43, 86, 172, 344, 688, 361, 722, 429, 858, 701, 371, 742, 453, 906, 797, 563, 111, 222, 444, 888, 761, 507, 1014, 997, 963, 911, 791, 551, 71, 142, 284, 568, 121, 242, 484, 968, 921, 827, 639, 247, 494, 988, 945, 875, 735, 439, 878, 725, 419, 838, 645, 259, 518, 5, 10, 20, 40, 80, 160, 320, 640, 265, 530, 45, 90, 180, 360, 720, 425, 850, 685, 339, 678, 325, 650, 285, 570, 125, 250, 500, 1000, 985, 955, 895, 759, 487, 974, 917, 803, 591, 151, 302, 604, 177, 354, 708, 385, 770, 525, 19, 38, 76, 152, 304, 608, 201, 402, 804, 577, 139, 278, 556, 81, 162, 324, 648, 281, 562, 109, 218, 436, 872, 729, 443, 886, 741, 451, 902, 773, 515, 15, 30, 60, 120, 240, 480, 960, 905, 795, 575, 119, 238, 476, 952, 889, 763, 511, 1022, 1013, 995, 975, 919, 807, 583, 135, 270, 540, 49, 98, 196, 392, 784, 553, 91, 182, 364, 728, 441, 882, 749, 467, 934, 837, 643, 271, 542, 53, 106, 212, 424, 848, 681, 347, 694, 357, 714, 413, 826, 637, 243, 486, 972, 913, 811, 607, 183, 366, 732, 433, 866, 717, 403, 806, 581, 131, 262, 524, 17, 34, 68, 136, 272, 544, 73, 146, 292, 584, 153, 306, 612, 193, 386, 772, 513, 11, 22, 44, 88, 176, 352, 704, 393, 786, 557, 83, 166, 332, 664, 313, 626, 237, 474, 948, 865, 715, 415, 830, 629, 227, 454, 908, 785, 555, 95, 190, 380, 760, 505, 1010, 1005, 979, 943, 855, 679, 327, 654, 277, 554, 93, 186, 372, 744, 473, 946, 877, 723, 431, 862, 693, 355, 710, 389, 778, 541, 51, 102, 204, 408, 816, 617, 219, 438, 876, 721, 427, 854, 677, 323, 646, 261, 522, 29, 58, 116, 232, 464, 928, 841, 667, 319, 638, 245, 490, 980, 929, 843, 671, 311, 622, 213, 426, 852, 673, 331, 662, 293, 586, 157, 314, 628, 225, 450, 900, 769, 523, 31, 62, 124, 248, 496, 992, 969, 923, 831, 631, 231, 462, 924, 817, 619, 223, 446, 892, 753, 491, 982, 933, 835, 655, 279, 558, 85, 170, 340, 680, 345, 690, 365, 730, 445, 890, 765, 499, 998, 965, 899, 783, 535, 39, 78, 156, 312, 624, 233, 466, 932, 833, 651, 287, 574, 117, 234, 468, 936, 857, 699, 383, 766, 501, 1002, 989, 947, 879, 727, 423, 846, 661, 291, 582, 133, 266, 532, 33, 66, 132, 264, 528, 41, 82, 164, 328, 656, 297, 594, 173, 346, 692, 353, 706, 397, 794, 573, 115, 230, 460, 920, 825, 635, 255, 510, 1020, 1009, 1003, 991, 951, 871, 711, 391, 782, 533, 35, 70, 140, 280, 560, 105, 210, 420, 840, 665, 315, 630, 229, 458, 916, 801, 587, 159, 318, 636, 241, 482, 964, 897, 779, 543, 55, 110, 220, 440, 880, 745, 475, 950, 869, 707, 399, 798, 565, 99, 198, 396, 792, 569, 123, 246, 492, 984, 953, 891, 767, 503, 1006, 981, 931, 847, 663, 295, 590, 149, 298, 596, 161, 322, 644, 257, 514, 13, 26, 52, 104, 208, 416, 832, 649, 283, 566, 101, 202, 404, 808, 601, 187, 374, 748, 465, 930, 845, 659, 303, 606, 181, 362, 724, 417, 834, 653, 275, 550, 69, 138, 276, 552, 89, 178, 356, 712, 409, 818, 621, 211, 422, 844, 657, 299, 598, 165, 330, 660, 289, 578, 141, 282, 564, 97, 194, 388, 776, 537, 59, 118, 236, 472, 944, 873, 731, 447, 894, 757, 483, 966, 901, 771, 527, 23, 46, 92, 184, 368, 736, 457, 914, 813, 595, 175, 350, 700, 369, 738, 461, 922, 829, 627, 239, 478, 956, 881, 747, 479, 958, 885, 739, 463, 926, 821, 611, 207, 414, 828, 625, 235, 470, 940, 849, 683, 351, 702, 373, 746, 477, 954, 893, 755, 495, 990, 949, 867, 719, 407, 814, 597, 163, 326, 652, 273, 546, 77, 154, 308, 616, 217, 434, 868, 705, 395, 790, 549, 67, 134, 268, 536, 57, 114, 228, 456, 912, 809, 603, 191, 382, 764, 497, 994, 973, 915, 815, 599, 167, 334, 668, 305, 610, 205, 410, 820, 609, 203, 406, 812, 593, 171, 342, 684, 337, 674, 333, 666, 317, 634, 253, 506, 1012, 993, 971, 927, 823, 615, 199, 398, 796, 561, 107, 214, 428, 856, 697, 379, 758, 485, 970, 925, 819, 623, 215, 430, 860, 689, 363, 726, 421, 842, 669, 307, 614, 197, 394, 788, 545, 75, 150, 300, 600, 185, 370, 740, 449, 898, 781, 531, 47, 94, 188, 376, 752, 489, 978, 941, 851, 687, 343, 686, 341, 682, 349, 698, 381, 762, 509, 1018, 1021, 1011, 1007, 983, 935, 839, 647, 263, 526, 21, 42, 84, 168, 336, 672, 329, 658, 301, 602, 189, 378, 756, 481, 962, 909, 787, 559, 87, 174, 348, 696, 377, 754, 493, 986, 957, 883, 751, 471, 942, 853, 675, 335, 670, 309, 618, 221, 442, 884, 737, 459, 918, 805, 579, 143, 286, 572, 113, 226, 452, 904, 793, 571, 127, 254, 508, 1016, 1017, 1019, 1023, 1015, 999, 967, 903, 775, 519, 7, 14, 28, 56, 112, 224, 448, 896, 777, 539, 63, 126, 252, 504, 1008, 1001, 987, 959, 887, 743, 455, 910, 789, 547, 79, 158, 316, 632, 249, 498, 996, 961, 907, 799, 567, 103, 206, 412, 824, 633, 251, 502, 1004, 977, 939, 863, 695, 359, 718, 405, 810, 605, 179, 358, 716, 401, 802, 589, 147, 294, 588, 145, 290, 580, 129, 258, 516, 1, 2, 4 +}; + + + +/** + * Logarithm of elements of GF(2^10) to the base alpha (root of x^10 + x^3 + 1). + * The logarithm of 0 is set to 1024 by convention. + */ +static const uint16_t logtable[1024] = { + 1024, 0, 1, 77, 2, 154, 78, 956, 3, 10, 155, 325, 79, 618, 957, 231, 4, 308, 11, 200, 156, 889, 326, 695, 80, 24, 619, 87, 958, 402, 232, 436, 5, 513, 309, 551, 12, 40, 201, 479, 157, 518, 890, 101, 327, 164, 696, 860, 81, 258, 25, 385, 620, 277, 88, 577, 959, 772, 403, 680, 233, 52, 437, 966, 6, 20, 514, 768, 310, 650, 552, 129, 13, 314, 41, 849, 202, 757, 480, 980, 158, 213, 519, 335, 891, 462, 102, 907, 328, 654, 165, 264, 697, 369, 861, 354, 82, 675, 259, 590, 26, 628, 386, 991, 621, 556, 278, 822, 89, 219, 578, 117, 960, 937, 773, 533, 404, 491, 681, 241, 234, 133, 53, 595, 438, 178, 967, 943, 7, 1020, 21, 305, 515, 510, 769, 255, 311, 17, 651, 210, 553, 672, 130, 934, 14, 1017, 315, 1014, 42, 610, 850, 191, 203, 318, 758, 31, 481, 428, 981, 568, 159, 613, 214, 752, 520, 667, 336, 788, 892, 45, 463, 801, 103, 525, 908, 705, 329, 194, 655, 1008, 166, 642, 265, 296, 698, 853, 370, 633, 862, 899, 355, 779, 83, 321, 676, 97, 260, 845, 591, 818, 27, 206, 629, 797, 387, 793, 992, 727, 622, 34, 557, 661, 279, 420, 823, 834, 90, 761, 220, 391, 579, 926, 118, 451, 961, 431, 938, 349, 774, 563, 534, 446, 405, 484, 492, 731, 682, 341, 242, 714, 235, 571, 134, 290, 54, 412, 596, 140, 439, 984, 179, 996, 968, 810, 944, 539, 8, 616, 1021, 152, 22, 400, 306, 887, 516, 162, 511, 38, 770, 50, 256, 275, 312, 755, 18, 648, 652, 367, 211, 460, 554, 217, 673, 626, 131, 176, 935, 489, 15, 670, 1018, 508, 316, 426, 1015, 608, 43, 523, 611, 665, 851, 897, 192, 640, 204, 791, 319, 843, 759, 924, 32, 418, 482, 339, 429, 561, 982, 808, 569, 410, 160, 48, 614, 398, 215, 174, 753, 365, 521, 895, 668, 424, 337, 806, 789, 922, 893, 804, 46, 172, 464, 872, 802, 870, 104, 466, 526, 283, 909, 874, 706, 736, 330, 528, 195, 380, 656, 285, 1009, 1003, 167, 106, 643, 838, 266, 468, 297, 66, 699, 708, 854, 111, 371, 738, 634, 60, 863, 911, 900, 827, 356, 876, 780, 497, 84, 197, 322, 74, 677, 382, 98, 548, 261, 332, 846, 765, 592, 530, 819, 587, 28, 1011, 207, 302, 630, 1005, 798, 749, 388, 658, 794, 94, 993, 287, 728, 346, 623, 645, 35, 149, 558, 840, 662, 505, 280, 169, 421, 395, 824, 108, 835, 377, 91, 299, 762, 71, 221, 68, 392, 146, 580, 268, 927, 224, 119, 470, 452, 687, 962, 856, 432, 227, 939, 113, 350, 976, 775, 701, 564, 930, 535, 710, 447, 723, 406, 636, 485, 271, 493, 62, 732, 918, 683, 373, 342, 583, 243, 740, 715, 719, 236, 902, 572, 690, 135, 829, 291, 186, 55, 865, 413, 455, 597, 913, 141, 744, 440, 782, 985, 473, 180, 499, 997, 602, 969, 358, 811, 122, 945, 878, 540, 247, 9, 324, 617, 230, 1022, 76, 153, 955, 23, 86, 401, 435, 307, 199, 888, 694, 517, 100, 163, 859, 512, 550, 39, 478, 771, 679, 51, 965, 257, 384, 276, 576, 313, 848, 756, 979, 19, 767, 649, 128, 653, 263, 368, 353, 212, 334, 461, 906, 555, 821, 218, 116, 674, 589, 627, 990, 132, 594, 177, 942, 936, 532, 490, 240, 16, 209, 671, 933, 1019, 304, 509, 254, 317, 30, 427, 567, 1016, 1013, 609, 190, 44, 800, 524, 704, 612, 751, 666, 787, 852, 632, 898, 778, 193, 1007, 641, 295, 205, 796, 792, 726, 320, 96, 844, 817, 760, 390, 925, 450, 33, 660, 419, 833, 483, 730, 340, 713, 430, 348, 562, 445, 983, 995, 809, 538, 570, 289, 411, 139, 161, 37, 49, 274, 615, 151, 399, 886, 216, 625, 175, 488, 754, 647, 366, 459, 522, 664, 896, 639, 669, 507, 425, 607, 338, 560, 807, 409, 790, 842, 923, 417, 894, 423, 805, 921, 47, 397, 173, 364, 465, 282, 873, 735, 803, 171, 871, 869, 105, 837, 467, 65, 527, 379, 284, 1002, 910, 826, 875, 496, 707, 110, 737, 59, 331, 764, 529, 586, 196, 73, 381, 547, 657, 93, 286, 345, 1010, 301, 1004, 748, 168, 394, 107, 376, 644, 148, 839, 504, 267, 223, 469, 686, 298, 70, 67, 145, 700, 929, 709, 722, 855, 226, 112, 975, 372, 582, 739, 718, 635, 270, 61, 917, 864, 454, 912, 743, 901, 689, 828, 185, 357, 121, 877, 246, 781, 472, 498, 601, 85, 434, 198, 693, 323, 229, 75, 954, 678, 964, 383, 575, 99, 858, 549, 477, 262, 352, 333, 905, 847, 978, 766, 127, 593, 941, 531, 239, 820, 115, 588, 989, 29, 566, 1012, 189, 208, 932, 303, 253, 631, 777, 1006, 294, 799, 703, 750, 786, 389, 449, 659, 832, 795, 725, 95, 816, 994, 537, 288, 138, 729, 712, 347, 444, 624, 487, 646, 458, 36, 273, 150, 885, 559, 408, 841, 416, 663, 638, 506, 606, 281, 734, 170, 868, 422, 920, 396, 363, 825, 495, 109, 58, 836, 64, 378, 1001, 92, 344, 300, 747, 763, 585, 72, 546, 222, 685, 69, 144, 393, 375, 147, 503, 581, 717, 269, 916, 928, 721, 225, 974, 120, 245, 471, 600, 453, 742, 688, 184, 963, 574, 857, 476, 433, 692, 228, 953, 940, 238, 114, 988, 351, 904, 977, 126, 776, 293, 702, 785, 565, 188, 931, 252, 536, 137, 711, 443, 448, 831, 724, 815, 407, 415, 637, 605, 486, 457, 272, 884, 494, 57, 63, 1000, 733, 867, 919, 362, 684, 143, 374, 502, 343, 746, 584, 545, 244, 599, 741, 183, 716, 915, 720, 973, 237, 987, 903, 125, 573, 475, 691, 952, 136, 442, 830, 814, 292, 784, 187, 251, 56, 999, 866, 361, 414, 604, 456, 883, 598, 182, 914, 972, 142, 501, 745, 544, 441, 813, 783, 250, 986, 124, 474, 951, 181, 971, 500, 543, 998, 360, 603, 882, 970, 542, 359, 881, 812, 249, 123, 950, 946, 947, 879, 948, 541, 880, 248, 949 +}; + + + +/** + * @brief Returns the integer i such that elt = a^i where a is the primitive element of GF(2^PARAM_M). + * + * @returns the logarithm of the given element + */ +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(uint16_t elt) { + return logtable[elt]; +} + + + +/** + * @brief Multiplies nonzero element a by element b + * + * @returns the product a*b + * @param[in] a First element of GF(2^PARAM_M) to multiply (cannot be zero) + * @param[in] b Second element of GF(2^PARAM_M) to multiply (cannot be zero) + */ +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b) { + // mask = 0xffff if neither a nor b is zero. Otherwise mask is 0. + int16_t mask = ((logtable[a] | logtable[b]) >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mod(logtable[a] + logtable[b])]; +} + + + +/** + * @brief Squares an element of GF(2^PARAM_M) + * + * @returns a^2 + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(uint16_t a) { + int16_t mask = (logtable[a] >> PARAM_M) - 1; + return mask & exptable[PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mod(2 * logtable[a])]; +} + + + +/** + * @brief Computes the inverse of an element of GF(2^PARAM_M) + * + * @returns the inverse of a + * @param[in] a Element of GF(2^PARAM_M) + */ +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_inverse(uint16_t a) { + return exptable[PARAM_GF_MUL_ORDER - logtable[a]]; +} + + + +/** + * @brief Returns i modulo 2^PARAM_M-1 + * + * i must be less than 2*(2^PARAM_M-1). + * Therefore, the return value is either i or i-2^PARAM_M+1. + * + * @returns i mod (2^PARAM_M-1) + * @param[in] i The integer whose modulo is taken + */ +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mod(uint16_t i) { + uint16_t tmp = i - PARAM_GF_MUL_ORDER; + + // mask = 0xffff if(i < PARAM_GF_MUL_ORDER) + int16_t mask = -(tmp >> 15); + + return tmp + (mask & PARAM_GF_MUL_ORDER); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/gf.h b/crypto_kem/hqc-256-3-cca2/leaktime/gf.h new file mode 100644 index 00000000..81bbae8f --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/gf.h @@ -0,0 +1,18 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_GF_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_GF_H + +/** + * @file gf.h + * Header file of gf.c + */ + +#include +#include + +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_log(uint16_t elt); +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mul(uint16_t a, uint16_t b); +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_square(uint16_t a); +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_inverse(uint16_t a); +uint16_t PQCLEAN_HQC2563CCA2_LEAKTIME_gf_mod(uint16_t i); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.c b/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.c new file mode 100644 index 00000000..25936aab --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.c @@ -0,0 +1,123 @@ +/** + * \file gf2x.c + * \brief Implementation of multiplication of two polynomials + */ + +#include "gf2x.h" +#include "parameters.h" +#include "util.h" + +#include +#include + +#define WORD_TYPE uint64_t +#define WORD_TYPE_BITS (sizeof(WORD_TYPE) * 8) +#define UTILS_VECTOR_ARRAY_SIZE CEIL_DIVIDE(PARAM_N, WORD_TYPE_BITS) + +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v); + + +/** + * @brief A subroutine used in the function sparse_dense_mul() + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + * @return 0 if precomputation is successful, -1 otherwise + */ +static int vect_mul_precompute_rows(WORD_TYPE *o, const WORD_TYPE *v) { + int8_t var; + for (size_t i = 0; i < PARAM_N; ++i) { + var = 0; + + // All the bits that we need are in the same block + if (((i % WORD_TYPE_BITS) == 0) && (i != PARAM_N - (PARAM_N % WORD_TYPE_BITS))) { + var = 1; + } + + // Cases where the bits are in before the last block, the last block and the first block + if (i > PARAM_N - WORD_TYPE_BITS) { + if (i >= PARAM_N - (PARAM_N % WORD_TYPE_BITS)) { + var = 2; + } else { + var = 3; + } + } + + switch (var) { + case 0: + // Take bits in the last block and the first one + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + break; + + case 1: + o[i] = v[i / WORD_TYPE_BITS]; + break; + + case 2: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[0] << ((PARAM_N - i) % WORD_TYPE_BITS); + break; + + case 3: + o[i] = 0; + o[i] += v[i / WORD_TYPE_BITS] >> (i % WORD_TYPE_BITS); + o[i] += v[(i / WORD_TYPE_BITS) + 1] << (WORD_TYPE_BITS - (i % WORD_TYPE_BITS)); + o[i] += v[0] << ((WORD_TYPE_BITS - i + (PARAM_N % WORD_TYPE_BITS)) % WORD_TYPE_BITS); + break; + + default: + return -1; + } + } + + return 0; +} + + + +/** + * @brief Multiplies two vectors + * + * This function multiplies two vectors: a sparse vector of Hamming weight equal to weight and a dense (random) vector. + * The vector a1 is the sparse vector and a2 is the dense vector. + * We notice that the idea is explained using vector of 32 bits elements instead of 64 (the algorithm works in booth cases). + * + * @param[out] o Pointer to a vector that is the result of the multiplication + * @param[in] a1 Pointer to the sparse vector stored by position + * @param[in] a2 Pointer to the dense vector + * @param[in] weight Integer that is the weight of the sparse vector + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight) { + WORD_TYPE v1[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE res[UTILS_VECTOR_ARRAY_SIZE] = {0}; + WORD_TYPE precomputation_array [PARAM_N] = {0}; + WORD_TYPE row [UTILS_VECTOR_ARRAY_SIZE] = {0}; + uint32_t index; + + PQCLEAN_HQC2563CCA2_LEAKTIME_load8_arr(v1, UTILS_VECTOR_ARRAY_SIZE, a2, VEC_N_SIZE_BYTES); + vect_mul_precompute_rows(precomputation_array, v1); + + for (size_t i = 0; i < weight; ++i) { + int32_t k = UTILS_VECTOR_ARRAY_SIZE; + + for (size_t j = 0; j < UTILS_VECTOR_ARRAY_SIZE - 1; ++j) { + index = WORD_TYPE_BITS * (uint32_t)j - a1[i]; + if (index > PARAM_N) { + index += PARAM_N; + } + row[j] = precomputation_array[index]; + } + + index = WORD_TYPE_BITS * (UTILS_VECTOR_ARRAY_SIZE - 1) - a1[i]; + row[UTILS_VECTOR_ARRAY_SIZE - 1] = precomputation_array[(index < PARAM_N ? index : index + PARAM_N)] & BITMASK(PARAM_N, WORD_TYPE_BITS); + + while (k--) { + res[k] ^= row[k]; + } + } + + PQCLEAN_HQC2563CCA2_LEAKTIME_store8_arr(o, VEC_N_SIZE_BYTES, res, UTILS_VECTOR_ARRAY_SIZE); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.h b/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.h new file mode 100644 index 00000000..69812708 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/gf2x.h @@ -0,0 +1,13 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_GF2X_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_GF2X_H + +/** + * @file gf2x.h + * @brief Header file for gf2x.c + */ + +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(uint8_t *o, const uint32_t *a1, const uint8_t *a2, uint16_t weight); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/hqc.c b/crypto_kem/hqc-256-3-cca2/leaktime/hqc.c new file mode 100644 index 00000000..b017b3f8 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/hqc.c @@ -0,0 +1,135 @@ +/** + * @file hqc.c + * @brief Implementation of hqc.h + */ + +#include "gf2x.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "randombytes.h" +#include "tensor.h" +#include "vector.h" +#include + + +/** + * @brief Keygen of the HQC_PKE IND_CPA scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + AES_XOF_struct pk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + uint8_t pk_seed[SEED_BYTES] = {0}; + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expanders for public key and secret key + randombytes(sk_seed, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + randombytes(pk_seed, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + // Compute secret key + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + + // Compute public key + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(s, y, h, PARAM_OMEGA); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(s, x, s, VEC_N_SIZE_BYTES); + + // Parse keys to string + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_to_string(pk, pk_seed, s); + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_to_string(sk, sk_seed, pk); +} + + + +/** + * @brief Encryption of the HQC_PKE IND_CPA scheme + * + * The cihertext is composed of vectors u and v. + * + * @param[out] u Vector u (first part of the ciphertext) + * @param[out] v Vector v (second part of the ciphertext) + * @param[in] m Vector representing the message to encrypt + * @param[in] theta Seed used to derive randomness required for encryption + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk) { + AES_XOF_struct seedexpander; + uint8_t h[VEC_N_SIZE_BYTES] = {0}; + uint8_t s[VEC_N_SIZE_BYTES] = {0}; + uint8_t r1[VEC_N_SIZE_BYTES] = {0}; + uint32_t r2[PARAM_OMEGA_R] = {0}; + uint8_t e[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Create seed_expander from theta + seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH); + + // Retrieve h and s from public key + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_from_string(h, s, pk); + + // Generate r1, r2 and e + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E); + + // Compute u = r1 + r2.h + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(u, r2, h, PARAM_OMEGA_R); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(u, r1, u, VEC_N_SIZE_BYTES); + + // Compute v = m.G by encoding the message + PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_encode(v, m); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + + // Compute v = m.G + s.r2 + e + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(tmp2, r2, s, PARAM_OMEGA_R); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(tmp2, e, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_resize(v, PARAM_N1N2, tmp2, PARAM_N); +} + + + +/** + * @brief Decryption of the HQC_PKE IND_CPA scheme + * + * @param[out] m Vector representing the decrypted message + * @param[in] u Vector u (first part of the ciphertext) + * @param[in] v Vector v (second part of the ciphertext) + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk) { + uint8_t x[VEC_N_SIZE_BYTES] = {0}; + uint32_t y[PARAM_OMEGA] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t tmp1[VEC_N_SIZE_BYTES] = {0}; + uint8_t tmp2[VEC_N_SIZE_BYTES] = {0}; + + // Retrieve x, y, pk from secret key + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_from_string(x, y, pk, sk); + + // Compute v - u.y + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_mul(tmp2, y, u, PARAM_OMEGA); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_BYTES); + + // Compute m by decoding v - u.y + PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_decode(m, tmp2); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/hqc.h b/crypto_kem/hqc-256-3-cca2/leaktime/hqc.h new file mode 100644 index 00000000..5cc99e1c --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/hqc.h @@ -0,0 +1,15 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_HQC_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_HQC_H + +/** + * @file hqc.h + * @brief Functions of the HQC_PKE IND_CPA scheme + */ + +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_keygen(uint8_t *pk, uint8_t *sk); +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_encrypt(uint8_t *u, uint8_t *v, const uint8_t *m, const uint8_t *theta, const uint8_t *pk); +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_decrypt(uint8_t *m, const uint8_t *u, const uint8_t *v, const uint8_t *sk); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/kem.c b/crypto_kem/hqc-256-3-cca2/leaktime/kem.c new file mode 100644 index 00000000..293c7107 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/kem.c @@ -0,0 +1,154 @@ +/** + * @file kem.c + * @brief Implementation of api.h + */ + +#include "api.h" +#include "hqc.h" +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "sha2.h" +#include "vector.h" +#include +#include + + +/** + * @brief Keygen of the HQC_KEM IND_CAA2 scheme + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h. + * + * The secret key is composed of the seed used to generate vectors x and y. + * As a technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] pk String containing the public key + * @param[out] sk String containing the secret key + * @returns 0 if keygen is successful + */ +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_keypair(uint8_t *pk, uint8_t *sk) { + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_keygen(pk, sk); + return 0; +} + + + +/** + * @brief Encapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ct String containing the ciphertext + * @param[out] ss String containing the shared secret + * @param[in] pk String containing the public key + * @returns 0 if encapsulation is successful + */ +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES]; + uint8_t diversifier_bytes[8] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + + // Computing m + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_from_randombytes(m); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_encrypt(u, v, m, theta, pk); + + // Computing d + sha512(d, m, VEC_K_SIZE_BYTES); + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + // Computing ciphertext + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_to_string(ct, u, v, d); + + return 0; +} + + + +/** + * @brief Decapsulation of the HQC_KEM IND_CAA2 scheme + * + * @param[out] ss String containing the shared secret + * @param[in] ct String containing the cipĥertext + * @param[in] sk String containing the secret key + * @returns 0 if decapsulation is successful, -1 otherwise + */ +int PQCLEAN_HQC2563CCA2_LEAKTIME_crypto_kem_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) { + AES_XOF_struct G_seedexpander; + uint8_t seed_G[VEC_K_SIZE_BYTES] = {0}; + uint8_t diversifier_bytes[8] = {0}; + uint8_t u[VEC_N_SIZE_BYTES] = {0}; + uint8_t v[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d[SHA512_BYTES] = {0}; + uint8_t pk[PUBLIC_KEY_BYTES] = {0}; + uint8_t m[VEC_K_SIZE_BYTES] = {0}; + uint8_t theta[SEED_BYTES] = {0}; + uint8_t u2[VEC_N_SIZE_BYTES] = {0}; + uint8_t v2[VEC_N1N2_SIZE_BYTES] = {0}; + uint8_t d2[SHA512_BYTES] = {0}; + uint8_t mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0}; + int8_t abort = 0; + + // Retrieving u, v and d from ciphertext + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_from_string(u, v, d, ct); + + // Retrieving pk from sk + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); + + // Decryting + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_decrypt(m, u, v, sk); + + // Generating G function + memcpy(seed_G, m, VEC_K_SIZE_BYTES); + seedexpander_init(&G_seedexpander, seed_G, diversifier_bytes, SEEDEXPANDER_MAX_LENGTH); + + // Computing theta + seedexpander(&G_seedexpander, theta, SEED_BYTES); + + // Encrypting m' + PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_pke_encrypt(u2, v2, m, theta, pk); + + // Checking that c = c' and abort otherwise + if (PQCLEAN_HQC2563CCA2_LEAKTIME_vect_compare(u, u2, VEC_N_SIZE_BYTES) != 0 || + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_compare(v, v2, VEC_N1N2_SIZE_BYTES) != 0) { + abort = 1; + } + + // Computing d' + sha512(d2, m, VEC_K_SIZE_BYTES); + + // Checking that d = d' and abort otherwise + if (memcmp(d, d2, SHA512_BYTES) != 0) { + abort = 1; + } + + if (abort == 1) { + memset(ss, 0, SHARED_SECRET_BYTES); + return -1; + } + + // Computing shared secret + memcpy(mc, m, VEC_K_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES, u, VEC_N_SIZE_BYTES); + memcpy(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES); + + return 0; +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/parameters.h b/crypto_kem/hqc-256-3-cca2/leaktime/parameters.h new file mode 100644 index 00000000..bc16d522 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/parameters.h @@ -0,0 +1,112 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_HQC_PARAMETERS_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_HQC_PARAMETERS_H + +/** + * @file parameters.h + * @brief Parameters of the HQC_KEM IND-CCA2 scheme + */ + +#include "api.h" + +#define CEIL_DIVIDE(a, b) (((a)/(b)) + ((a) % (b) == 0 ? 0 : 1)) /*!< Divide a by b and ceil the result*/ +#define BITMASK(a, size) ((1ULL << ((a) % (size))) - 1) /*!< Create a mask*/ + + +/* + #define PARAM_N Define the parameter n of the scheme + #define PARAM_N1 Define the parameter n1 of the scheme (length of BCH code) + #define PARAM_N2 Define the parameter n2 of the scheme (length of the repetition code) + #define PARAM_N1N2 Define the parameter n1 * n2 of the scheme (length of the tensor code) + #define PARAM_OMEGA Define the parameter omega of the scheme + #define PARAM_OMEGA_E Define the parameter omega_e of the scheme + #define PARAM_OMEGA_R Define the parameter omega_r of the scheme + #define PARAM_SECURITY Define the security level corresponding to the chosen parameters + #define PARAM_DFR_EXP Define the decryption failure rate corresponding to the chosen parameters + + #define SECRET_KEY_BYTES Define the size of the secret key in bytes + #define PUBLIC_KEY_BYTES Define the size of the public key in bytes + #define SHARED_SECRET_BYTES Define the size of the shared secret in bytes + #define CIPHERTEXT_BYTES Define the size of the ciphertext in bytes + + #define UTILS_REJECTION_THRESHOLD Define the rejection threshold used to generate given weight vectors (see vector_set_random_fixed_weight function) + #define VEC_N_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes + #define VEC_N1_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes + #define VEC_N1N2_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes + #define VEC_K_ARRAY_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes + + #define PARAM_T Define a threshold for decoding repetition code word (PARAM_T = (PARAM_N2 - 1) / 2) + + #define PARAM_DELTA Define the parameter delta of the scheme (correcting capacity of the BCH code) + #define PARAM_M Define a positive integer + #define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^m), i.e 2^m -1 + #define PARAM_K Define the size of the information bits of the BCH code + #define PARAM_G Define the size of the generator polynomial of BCH code + #define PARAM_FFT The additive FFT takes a 2^PARAM_FFT polynomial as input + We use the FFT to compute the roots of sigma, whose degree if PARAM_DELTA=60 + The smallest power of 2 greater than 60+1 is 64=2^6 + #define PARAM_FFT_T The additive FFT transpose computes a (2^PARAM_FFT_T)-sized syndrome vector + We want to compute 2*PARAM_DELTA=120 syndromes + The smallest power of 2 greater than 120 is 2^7 + #define PARAM_BCH_POLY Generator polynomial of the BCH code + + #define SHA512_BYTES Define the size of SHA512 output in bytes + #define SEED_BYTES Define the size of the seed in bytes + #define SEEDEXPANDER_MAX_LENGTH Define the seed expander max length +*/ + + +#define PARAM_N 70853 +#define PARAM_N1 796 +#define PARAM_N2 89 +#define PARAM_N1N2 70844 +#define PARAM_OMEGA 133 +#define PARAM_OMEGA_E 153 +#define PARAM_OMEGA_R 153 +#define PARAM_SECURITY 256 +#define PARAM_DFR_EXP 256 + +#define SECRET_KEY_BYTES PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_SECRETKEYBYTES +#define PUBLIC_KEY_BYTES PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_PUBLICKEYBYTES +#define SHARED_SECRET_BYTES PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_BYTES +#define CIPHERTEXT_BYTES PQCLEAN_HQC2563CCA2_LEAKTIME_CRYPTO_CIPHERTEXTBYTES + +#define UTILS_REJECTION_THRESHOLD 16721308 +#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8) +#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8) +#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8) +#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8) + +#define PARAM_T 44 + +#define PARAM_DELTA 60 +#define PARAM_M 10 +#define PARAM_GF_MUL_ORDER 1023 +#define PARAM_K 256 +#define PARAM_G 541 +#define PARAM_FFT 6 +#define PARAM_FFT_T 7 +#define PARAM_BCH_POLY { \ + 1,1,0,1,1,0,1,0,0,0,0,1,0,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,0, \ + 1,0,0,1,0,1,1,0,1,0,0,1,1,1,0,0,1,1,1,0,0,0,1,0,1,0,1,0,1,1,0,0, \ + 1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,0,1,0,1,1,0,1,0, \ + 0,1,0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,0,1,1,1,1,0,1,0,0,1,1,1,1,0,1, \ + 0,0,0,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,0,0,0,1,0,1,0,0,1,0,0,0, \ + 1,1,0,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,1,0,1,0,0,1,0,0,1,1,0,1,0, \ + 0,0,0,1,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1, \ + 1,0,1,1,0,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,1,0, \ + 0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,0,1,1, \ + 1,1,1,1,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,0,1, \ + 0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,1,0,1,1,0,0,0,1,0,1,1,0,0,0, \ + 1,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,0,1,1, \ + 0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,1,1,1, \ + 0,1,0,1,1,0,1,1,1,1,0,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,0,0,1,1,0,0, \ + 0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,0,1,1, \ + 1,1,0,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,1,0,0, \ + 1,0,1,1,0,0,0,0,0,1,1,1,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,1 \ + }; + +#define SHA512_BYTES 64 +#define SEED_BYTES 40 +#define SEEDEXPANDER_MAX_LENGTH 4294967295 + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/parsing.c b/crypto_kem/hqc-256-3-cca2/leaktime/parsing.c new file mode 100644 index 00000000..6c1fbc19 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/parsing.c @@ -0,0 +1,126 @@ +/** + * @file parsing.c + * @brief Functions to parse secret key, public key and ciphertext of the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "parsing.h" +#include "vector.h" +#include +#include + + +/** + * @brief Parse a secret key into a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] sk String containing the secret key + * @param[in] sk_seed Seed used to generate the secret key + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) { + memcpy(sk, sk_seed, SEED_BYTES); + memcpy(sk + SEED_BYTES, pk, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a secret key from a string + * + * The secret key is composed of the seed used to generate vectors x and y. + * As technicality, the public key is appended to the secret key in order to respect NIST API. + * + * @param[out] x uint8_t representation of vector x + * @param[out] y uint8_t representation of vector y + * @param[out] pk String containing the public key + * @param[in] sk String containing the secret key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk) { + AES_XOF_struct sk_seedexpander; + uint8_t sk_seed[SEED_BYTES] = {0}; + + memcpy(sk_seed, sk, SEED_BYTES); + seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA); + memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES); +} + + + +/** + * @brief Parse a public key into a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] pk String containing the public key + * @param[in] pk_seed Seed used to generate the public key + * @param[in] s uint8_t representation of vector s + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s) { + memcpy(pk, pk_seed, SEED_BYTES); + memcpy(pk + SEED_BYTES, s, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a public key from a string + * + * The public key is composed of the syndrome s as well as the seed used to generate the vector h + * + * @param[out] h uint8_t representation of vector h + * @param[out] s uint8_t representation of vector s + * @param[in] pk String containing the public key + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk) { + AES_XOF_struct pk_seedexpander; + uint8_t pk_seed[SEED_BYTES] = {0}; + + memcpy(pk_seed, pk, SEED_BYTES); + seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH); + PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random(&pk_seedexpander, h); + + memcpy(s, pk + SEED_BYTES, VEC_N_SIZE_BYTES); +} + + + +/** + * @brief Parse a ciphertext into a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] ct String containing the ciphertext + * @param[in] u uint8_t representation of vector u + * @param[in] v uint8_t representation of vector v + * @param[in] d String containing the hash d + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d) { + memcpy(ct, u, VEC_N_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES, v, VEC_N1N2_SIZE_BYTES); + memcpy(ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, d, SHA512_BYTES); +} + + + +/** + * @brief Parse a ciphertext from a string + * + * The ciphertext is composed of vectors u, v and hash d. + * + * @param[out] u uint8_t representation of vector u + * @param[out] v uint8_t representation of vector v + * @param[out] d String containing the hash d + * @param[in] ct String containing the ciphertext + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct) { + memcpy(u, ct, VEC_N_SIZE_BYTES); + memcpy(v, ct + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES); + memcpy(d, ct + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES, SHA512_BYTES); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/parsing.h b/crypto_kem/hqc-256-3-cca2/leaktime/parsing.h new file mode 100644 index 00000000..56c01d35 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/parsing.h @@ -0,0 +1,20 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_PARSING_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_PARSING_H + +/** + * @file parsing.h + * @brief Header file for parsing.c + */ + +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk); +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_secret_key_from_string(uint8_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk); + +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint8_t *s); +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_public_key_from_string(uint8_t *h, uint8_t *s, const uint8_t *pk); + +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_to_string(uint8_t *ct, const uint8_t *u, const uint8_t *v, const uint8_t *d); +void PQCLEAN_HQC2563CCA2_LEAKTIME_hqc_ciphertext_from_string(uint8_t *u, uint8_t *v, uint8_t *d, const uint8_t *ct); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/repetition.c b/crypto_kem/hqc-256-3-cca2/leaktime/repetition.c new file mode 100644 index 00000000..892dbbb0 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/repetition.c @@ -0,0 +1,100 @@ +/** + * @file repetition.c + * @brief Implementation of repetition codes + */ + +#include "parameters.h" +#include "repetition.h" +#include +#include + +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v); + + +/** + * @brief Encoding each bit in the message m using the repetition code + * + * For reasons of clarity and comprehensibility, we do the encoding by storing the encoded bits in a String (each bit in an a uint8_t), + * then we parse the obtained string to an compact array using the function array_to_rep_codeword(). + * + * @param[out] em Pointer to an array that is the code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[PARAM_N1N2] = {0}; + uint8_t bit = 0; + uint32_t index; + + for (size_t i = 0; i < (VEC_N1_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + bit = (m[i] >> j) & 0x01; + index = (8 * (uint32_t)i + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + } + + for (uint8_t j = 0; j < (PARAM_N1 % 8); ++j) { + bit = (m[VEC_N1_SIZE_BYTES - 1] >> j) & 0x01; + index = (8 * (VEC_N1_SIZE_BYTES - 1) + j) * PARAM_N2; + for (uint8_t k = 0; k < PARAM_N2; ++k) { + tmp[index + k] = bit; + } + } + + array_to_rep_codeword(em, tmp); +} + + + +/** + * @brief Decoding the code words to a message using the repetition code + * + * We use a majority decoding. In fact we have that PARAM_N2 = 2 * PARAM_T + 1, thus, + * if the Hamming weight of the vector is greater than PARAM_T, the code word is decoded + * to 1 and 0 otherwise. + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em) { + size_t t = 0; // m index + uint8_t k = PARAM_N2; // block counter + uint8_t ones = 0; // number of 1 in the current block + + for (size_t i = 0; i < VEC_N1N2_SIZE_BYTES; ++i) { + for (uint8_t j = 0; j < 8; ++j) { + ones += (em[i] >> j) & 0x01; + + if (--k) { + continue; + } + + m[t / 8] |= (ones > PARAM_T) << t % 8; + ++t; + k = PARAM_N2; + ones = 0; + } + } +} + + + +/** + * @brief Parse an array to an compact array + * + * @param[out] o Pointer to an array + * @param[in] v Pointer to an array + */ +static void array_to_rep_codeword(uint8_t *o, const uint8_t *v) { + for (size_t i = 0; i < (VEC_N1N2_SIZE_BYTES - 1); ++i) { + for (uint8_t j = 0; j < 8; ++j) { + o[i] |= v[j + 8 * i] << j; + } + } + + for (uint8_t j = 0; j < PARAM_N1N2 % 8; ++j) { + o[VEC_N1N2_SIZE_BYTES - 1] |= (v[j + 8 * (VEC_N1N2_SIZE_BYTES - 1)]) << j; + } +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/repetition.h b/crypto_kem/hqc-256-3-cca2/leaktime/repetition.h new file mode 100644 index 00000000..3a47212a --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/repetition.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_REPETITION_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_REPETITION_H + +/** + * @file repetition.h + * @brief Header file for repetition.c + */ + +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/tensor.c b/crypto_kem/hqc-256-3-cca2/leaktime/tensor.c new file mode 100644 index 00000000..4ebfb8e6 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/tensor.c @@ -0,0 +1,42 @@ +/** + * @file tensor.c + * @brief Implementation of tensor code + */ + +#include "bch.h" +#include "parameters.h" +#include "repetition.h" +#include "tensor.h" +#include + + +/** + * @brief Encoding the message m to a code word em using the tensor code + * + * First we encode the message using the BCH code, then with the repetition code to obtain + * a tensor code word. + * + * @param[out] em Pointer to an array that is the tensor code word + * @param[in] m Pointer to an array that is the message + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_encode(tmp, m); + PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_encode(em, tmp); +} + + + +/** + * @brief Decoding the code word em to a message m using the tensor code + * + * @param[out] m Pointer to an array that is the message + * @param[in] em Pointer to an array that is the code word + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em) { + uint8_t tmp[VEC_N1_SIZE_BYTES] = {0}; + + PQCLEAN_HQC2563CCA2_LEAKTIME_repetition_code_decode(tmp, em); + PQCLEAN_HQC2563CCA2_LEAKTIME_bch_code_decode(m, tmp); +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/tensor.h b/crypto_kem/hqc-256-3-cca2/leaktime/tensor.h new file mode 100644 index 00000000..9afcf9e0 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/tensor.h @@ -0,0 +1,14 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_TENSOR_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_TENSOR_H + +/** + * @file tensor.h + * @brief Header file for tensor.c + */ + +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_encode(uint8_t *em, const uint8_t *m); +void PQCLEAN_HQC2563CCA2_LEAKTIME_tensor_code_decode(uint8_t *m, const uint8_t *em); + +#endif diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/util.c b/crypto_kem/hqc-256-3-cca2/leaktime/util.c new file mode 100644 index 00000000..5471f9ab --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/util.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "stddef.h" + +#include "assert.h" + +/* These functions should help with endianness-safe conversions + * + * load8 and store8 are copied from the McEliece implementations, + * which are in the public domain. + */ + + +void PQCLEAN_HQC2563CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in) { + out[0] = (in >> 0x00) & 0xFF; + out[1] = (in >> 0x08) & 0xFF; + out[2] = (in >> 0x10) & 0xFF; + out[3] = (in >> 0x18) & 0xFF; + out[4] = (in >> 0x20) & 0xFF; + out[5] = (in >> 0x28) & 0xFF; + out[6] = (in >> 0x30) & 0xFF; + out[7] = (in >> 0x38) & 0xFF; +} + + +uint64_t PQCLEAN_HQC2563CCA2_LEAKTIME_load8(const unsigned char *in) { + uint64_t ret = in[7]; + + for (int8_t i = 6; i >= 0; i--) { + ret <<= 8; + ret |= in[i]; + } + + return ret; +} + +void PQCLEAN_HQC2563CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen) { + size_t index_in = 0; + size_t index_out = 0; + + // first copy by 8 bytes + if (inlen >= 8 && outlen >= 1) { + while (index_out < outlen && index_in + 8 <= inlen) { + out64[index_out] = PQCLEAN_HQC2563CCA2_LEAKTIME_load8(in8 + index_in); + + index_in += 8; + index_out += 1; + } + } + + // we now need to do the last 7 bytes if necessary + if (index_in >= inlen || index_out >= outlen) { + return; + } + out64[index_out] = in8[inlen - 1]; + for (int8_t i = (int8_t)(inlen - index_in) - 2; i >= 0; i--) { + out64[index_out] <<= 8; + out64[index_out] |= in8[index_in + i]; + } +} + +void PQCLEAN_HQC2563CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen) { + for (size_t index_out = 0, index_in = 0; index_out < outlen && index_in < inlen;) { + out8[index_out] = (in64[index_in] >> ((index_out % 8) * 8)) & 0xFF; + index_out++; + if (index_out % 8 == 0) { + index_in++; + } + } +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/util.h b/crypto_kem/hqc-256-3-cca2/leaktime/util.h new file mode 100644 index 00000000..daada400 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/util.h @@ -0,0 +1,9 @@ +/* These functions should help with endianness-safe conversions */ + +#include +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_store8(unsigned char *out, uint64_t in); +uint64_t PQCLEAN_HQC2563CCA2_LEAKTIME_load8(const unsigned char *in); +void PQCLEAN_HQC2563CCA2_LEAKTIME_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen); +void PQCLEAN_HQC2563CCA2_LEAKTIME_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen); diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/vector.c b/crypto_kem/hqc-256-3-cca2/leaktime/vector.c new file mode 100644 index 00000000..3c0117f7 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/vector.c @@ -0,0 +1,224 @@ +/** + * @file vector.c + * @brief Implementation of vectors sampling and some utilities for the HQC scheme + */ + +#include "nistseedexpander.h" +#include "parameters.h" +#include "randombytes.h" +#include "vector.h" +#include +#include + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. The vector + * is stored by position. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight) { + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (v[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + v[i] = random_data; + } + } +} + + + +/** + * @brief Generates a vector of a given Hamming weight + * + * This function generates uniformly at random a binary vector of a Hamming weight equal to the parameter weight. + * To generate the vector we have to sample uniformly at random values in the interval [0, PARAM_N -1]. Suppose the PARAM_N is equal to \f$ 70853 \f$, to select a position \f$ r\f$ the function works as follow: + * 1. It makes a call to the seedexpander function to obtain a random number \f$ x\f$ in \f$ [0, 2^{24}[ \f$. + * 2. Let \f$ t = \lfloor {2^{24} \over 70853} \rfloor \times 70853\f$ + * 3. If \f$ x \geq t\f$, go to 1 + * 4. It return \f$ r = x \mod 70853\f$ + * + * The parameter \f$ t \f$ is precomputed and it's denoted by UTILS_REJECTION_THRESHOLD (see the file parameters.h). + * + * @param[in] v Pointer to an array + * @param[in] weight Integer that is the Hamming weight + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight) { + + size_t random_bytes_size = 3 * weight; + uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0}; // weight is expected to be <= PARAM_OMEGA_R + uint32_t random_data = 0; + uint32_t tmp[PARAM_OMEGA_R] = {0}; + uint8_t exist = 0; + size_t j = 0; + + seedexpander(ctx, rand_bytes, random_bytes_size); + + for (uint32_t i = 0; i < weight; ++i) { + exist = 0; + do { + if (j == random_bytes_size) { + seedexpander(ctx, rand_bytes, random_bytes_size); + j = 0; + } + + random_data = ((uint32_t) rand_bytes[j++]) << 16; + random_data |= ((uint32_t) rand_bytes[j++]) << 8; + random_data |= rand_bytes[j++]; + + } while (random_data >= UTILS_REJECTION_THRESHOLD); + + random_data = random_data % PARAM_N; + + for (uint32_t k = 0; k < i; k++) { + if (tmp[k] == random_data) { + exist = 1; + } + } + + if (exist == 1) { + i--; + } else { + tmp[i] = random_data; + } + } + + for (uint16_t i = 0; i < weight; ++i) { + int32_t index = tmp[i] / 8; + int32_t pos = tmp[i] % 8; + v[index] |= 1 << pos; + } +} + + + +/** + * @brief Generates a random vector of dimension PARAM_N + * + * This function generates a random binary vector of dimension PARAM_N. It generates a random + * array of bytes using the seedexpander function, and drop the extra bits using a mask. + * + * @param[in] v Pointer to an array + * @param[in] ctx Pointer to the context of the seed expander + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v) { + uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0}; + + seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES); + + memcpy(v, rand_bytes, VEC_N_SIZE_BYTES); + v[VEC_N_SIZE_BYTES - 1] &= BITMASK(PARAM_N, 8); +} + + + +/** + * @brief Generates a random vector + * + * This function generates a random binary vector. It uses the the randombytes function. + * + * @param[in] v Pointer to an array + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v) { + uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0}; + + randombytes(rand_bytes, VEC_K_SIZE_BYTES); + memcpy(v, rand_bytes, VEC_K_SIZE_BYTES); +} + + + +/** + * @brief Adds two vectors + * + * @param[out] o Pointer to an array that is the result + * @param[in] v1 Pointer to an array that is the first vector + * @param[in] v2 Pointer to an array that is the second vector + * @param[in] size Integer that is the size of the vectors + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size) { + for (uint32_t i = 0; i < size; ++i) { + o[i] = v1[i] ^ v2[i]; + } +} + + + +/** + * @brief Compares two vectors + * + * @param[in] v1 Pointer to an array that is first vector + * @param[in] v2 Pointer to an array that is second vector + * @param[in] size Integer that is the size of the vectors + * @returns 0 if the vectors are equals and a negative/psotive value otherwise + */ +int PQCLEAN_HQC2563CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) { + return memcmp(v1, v2, size); +} + + + +/** + * @brief Resize a vector so that it contains size_o bits + * + * @param[out] o Pointer to the output vector + * @param[in] size_o Integer that is the size of the output vector in bits + * @param[in] v Pointer to the input vector + * @param[in] size_v Integer that is the size of the input vector in bits + */ +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v) { + if (size_o < size_v) { + uint8_t mask = 0x7F; + int8_t val = 8 - (size_o % 8); + + memcpy(o, v, VEC_N1N2_SIZE_BYTES); + + for (int8_t i = 0; i < val; ++i) { + o[VEC_N1N2_SIZE_BYTES - 1] &= (mask >> i); + } + } else { + memcpy(o, v, CEIL_DIVIDE(size_v, 8)); + } +} diff --git a/crypto_kem/hqc-256-3-cca2/leaktime/vector.h b/crypto_kem/hqc-256-3-cca2/leaktime/vector.h new file mode 100644 index 00000000..5034c987 --- /dev/null +++ b/crypto_kem/hqc-256-3-cca2/leaktime/vector.h @@ -0,0 +1,22 @@ +#ifndef PQCLEAN_HQC2563CCA2_LEAKTIME_VECTOR_H +#define PQCLEAN_HQC2563CCA2_LEAKTIME_VECTOR_H + +/** + * @file vector.h + * @brief Header file for vector.c + */ + +#include "nistseedexpander.h" +#include + +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight); +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint8_t *v, uint16_t weight); +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random(AES_XOF_struct *ctx, uint8_t *v); +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_set_random_from_randombytes(uint8_t *v); + +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_add(uint8_t *o, const uint8_t *v1, const uint8_t *v2, uint32_t size); +int PQCLEAN_HQC2563CCA2_LEAKTIME_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size); + +void PQCLEAN_HQC2563CCA2_LEAKTIME_vect_resize(uint8_t *o, uint32_t size_o, const uint8_t *v, uint32_t size_v); + +#endif diff --git a/test/Makefile b/test/Makefile index bc89391c..eeb09ebf 100644 --- a/test/Makefile +++ b/test/Makefile @@ -5,16 +5,19 @@ TYPE=kem SCHEME=kyber768 IMPLEMENTATION=clean -SCHEME_DIR="../crypto_$(TYPE)/$(SCHEME)/$(IMPLEMENTATION)" +SCHEME_DIR=../crypto_$(TYPE)/$(SCHEME)/$(IMPLEMENTATION) SCHEME_UPPERCASE=$(shell echo $(SCHEME) | tr a-z A-Z | sed 's/-//g') IMPLEMENTATION_UPPERCASE=$(shell echo $(IMPLEMENTATION) | tr a-z A-Z | sed 's/-//g') TEST_COMMON_DIR=../test/common COMMON_DIR=../common -COMMON_FILES=$(COMMON_DIR)/aes.c $(COMMON_DIR)/sha2.c $(COMMON_DIR)/fips202.c $(COMMON_DIR)/sp800-185.c +COMMON_FILES=$(COMMON_DIR)/aes.c $(COMMON_DIR)/sha2.c $(COMMON_DIR)/fips202.c $(COMMON_DIR)/nistseedexpander.c $(COMMON_DIR)/sp800-185.c COMMON_HEADERS=$(COMMON_DIR)/*.h DEST_DIR=../bin +SCHEME_LIBRARY=$(SCHEME_DIR)/lib$(SCHEME)_$(IMPLEMENTATION).a +SCHEME_FILES=$(wildcard $(SCHEME_DIR)/*.[chsS]) + # This -Wall was supported by the European Commission through the ERC Starting Grant 805031 (EPOQUE) CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wvla -Werror -std=c99 \ -Wundef -Wshadow -Wcast-align -Wpointer-arith -Wmissing-prototypes\ @@ -29,7 +32,9 @@ all: $(DEST_DIR)/functest_$(SCHEME)_$(IMPLEMENTATION) \ $(DEST_DIR)/printparams_$(SCHEME)_$(IMPLEMENTATION) .PHONY: build-scheme -build-scheme: +build-scheme: $(SCHEME_LIBRARY) + +$(SCHEME_LIBRARY): $(SCHEME_FILES) cd $(SCHEME_DIR) && $(MAKE) .PHONY: clean-scheme @@ -61,11 +66,15 @@ $(DEST_DIR)/test_common_sp800-185: test_common/sp800-185.c $(COMMON_FILES) mkdir -p $(DEST_DIR) $(CC) $(CFLAGS) $< $(COMMON_FILES) -o $@ -$(DEST_DIR)/functest_$(SCHEME)_$(IMPLEMENTATION): build-scheme crypto_$(TYPE)/functest.c $(COMMON_FILES) $(COMMON_DIR)/randombytes.c $(COMMON_HEADERS) +$(DEST_DIR)/test_common_nistseedexpander: test_common/sp800-185.c $(COMMON_FILES) + mkdir -p $(DEST_DIR) + $(CC) $(CFLAGS) $< $(COMMON_FILES) -o $@ + +$(DEST_DIR)/functest_$(SCHEME)_$(IMPLEMENTATION): $(SCHEME_LIBRARY) crypto_$(TYPE)/functest.c $(COMMON_FILES) $(COMMON_DIR)/randombytes.c $(COMMON_HEADERS) mkdir -p $(DEST_DIR) $(CC) $(CFLAGS) -DNTESTS=$(NTESTS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/functest.c $(COMMON_FILES) $(COMMON_DIR)/randombytes.c -o $@ -L$(SCHEME_DIR) -l$(SCHEME)_$(IMPLEMENTATION) -$(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION): build-scheme crypto_$(TYPE)/testvectors.c $(COMMON_FILES) $(TEST_COMMON_DIR)/notrandombytes.c $(COMMON_HEADERS) +$(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION): $(SCHEME_LIBRARY) crypto_$(TYPE)/testvectors.c $(COMMON_FILES) $(TEST_COMMON_DIR)/notrandombytes.c $(COMMON_HEADERS) mkdir -p $(DEST_DIR) $(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/testvectors.c $(COMMON_FILES) $(TEST_COMMON_DIR)/notrandombytes.c -o $@ -L$(SCHEME_DIR) -l$(SCHEME)_$(IMPLEMENTATION) @@ -73,7 +82,7 @@ $(DEST_DIR)/printparams_$(SCHEME)_$(IMPLEMENTATION): crypto_$(TYPE)/printparams. mkdir -p $(DEST_DIR) $(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/printparams.c -o $@ -$(DEST_DIR)/nistkat_$(SCHEME)_$(IMPLEMENTATION): build-scheme crypto_$(TYPE)/nistkat.c $(COMMON_FILES) $(TEST_COMMON_DIR)/nistkatrng.c $(COMMON_HEADERS) +$(DEST_DIR)/nistkat_$(SCHEME)_$(IMPLEMENTATION): $(SCHEME_LIBRARY) crypto_$(TYPE)/nistkat.c $(COMMON_FILES) $(TEST_COMMON_DIR)/nistkatrng.c $(COMMON_HEADERS) mkdir -p $(DEST_DIR) $(CC) $(CFLAGS) -DPQCLEAN_NAMESPACE=PQCLEAN_$(SCHEME_UPPERCASE)_$(IMPLEMENTATION_UPPERCASE) -I$(SCHEME_DIR) crypto_$(TYPE)/nistkat.c $(COMMON_FILES) $(TEST_COMMON_DIR)/nistkatrng.c -o $@ -L$(SCHEME_DIR) -l$(SCHEME)_$(IMPLEMENTATION) @@ -83,10 +92,11 @@ clean: $(RM) $(DEST_DIR)/testvectors_$(SCHEME)_$(IMPLEMENTATION) $(RM) $(DEST_DIR)/printparams_$(SCHEME)_$(IMPLEMENTATION) $(RM) $(DEST_DIR)/nistkat_$(SCHEME)_$(IMPLEMENTATION) - $(RM) $(DEST_DIR)/test_aes - $(RM) $(DEST_DIR)/test_fips202 - $(RM) $(DEST_DIR)/test_sha2 - $(RM) $(DEST_DIR)/test_sp800-185 + $(RM) $(DEST_DIR)/test_common_aes + $(RM) $(DEST_DIR)/test_common_fips202 + $(RM) $(DEST_DIR)/test_common_sha2 + $(RM) $(DEST_DIR)/test_common_sp800-185 + $(RM) $(DEST_DIR)/test_common_nistseedexpander .PHONY: distclean distclean: diff --git a/test/Makefile.Microsoft_nmake b/test/Makefile.Microsoft_nmake index a2ae2b5a..d5b1d191 100644 --- a/test/Makefile.Microsoft_nmake +++ b/test/Makefile.Microsoft_nmake @@ -13,8 +13,8 @@ SCHEME_DIR=..\crypto_$(TYPE)\$(SCHEME)\$(IMPLEMENTATION) TEST_COMMON_DIR=..\test\common COMMON_DIR=..\common -COMMON_OBJECTS=$(COMMON_DIR)\aes.obj $(COMMON_DIR)\fips202.obj $(COMMON_DIR)\sha2.obj $(COMMON_DIR)\sp800-185.obj -COMMON_OBJECTS_NOPATH=aes.obj fips202.obj sha2.obj sp800-185.obj +COMMON_OBJECTS=$(COMMON_DIR)\aes.obj $(COMMON_DIR)\fips202.obj $(COMMON_DIR)\sha2.obj $(COMMON_DIR)\nistseedexpander.obj $(COMMON_DIR)\sp800-185.obj +COMMON_OBJECTS_NOPATH=aes.obj fips202.obj sha2.obj sp800-185.obj nistseedexpander.obj DEST_DIR=..\bin diff --git a/test/common/nistkatrng.c b/test/common/nistkatrng.c index 96a3c032..744b7126 100644 --- a/test/common/nistkatrng.c +++ b/test/common/nistkatrng.c @@ -49,11 +49,11 @@ void nist_kat_init(uint8_t *entropy_input, const uint8_t *personalization_string DRBG_ctx.reseed_counter = 1; } -int randombytes(uint8_t *buf, size_t xlen) { +int randombytes(uint8_t *buf, size_t n) { uint8_t block[16]; int i = 0; - while (xlen > 0) { + while (n > 0) { //increment V for (int j = 15; j >= 0; j--) { if (DRBG_ctx.V[j] == 0xff) { @@ -64,13 +64,13 @@ int randombytes(uint8_t *buf, size_t xlen) { } } AES256_ECB(DRBG_ctx.Key, DRBG_ctx.V, block); - if (xlen > 15) { + if (n > 15) { memcpy(buf + i, block, 16); i += 16; - xlen -= 16; + n -= 16; } else { - memcpy(buf + i, block, xlen); - xlen = 0; + memcpy(buf + i, block, n); + n = 0; } } AES256_CTR_DRBG_Update(NULL, DRBG_ctx.Key, DRBG_ctx.V); diff --git a/test/common/notrandombytes.c b/test/common/notrandombytes.c index 6b2b8529..5788195a 100644 --- a/test/common/notrandombytes.c +++ b/test/common/notrandombytes.c @@ -57,8 +57,8 @@ static void surf(void) { } } -int randombytes(uint8_t *buf, size_t xlen) { - while (xlen > 0) { +int randombytes(uint8_t *buf, size_t n) { + while (n > 0) { if (!outleft) { if (!++in[0]) { if (!++in[1]) { @@ -72,7 +72,7 @@ int randombytes(uint8_t *buf, size_t xlen) { } *buf = (uint8_t) out[--outleft]; ++buf; - --xlen; + --n; } return 0; } diff --git a/test/duplicate_consistency/hqc-128-1-cca2_leaktime.yml b/test/duplicate_consistency/hqc-128-1-cca2_leaktime.yml new file mode 100644 index 00000000..c50b9bb4 --- /dev/null +++ b/test/duplicate_consistency/hqc-128-1-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-192-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/duplicate_consistency/hqc-192-1-cca2_leaktime.yml b/test/duplicate_consistency/hqc-192-1-cca2_leaktime.yml new file mode 100644 index 00000000..fcb9826d --- /dev/null +++ b/test/duplicate_consistency/hqc-192-1-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-128-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/duplicate_consistency/hqc-192-2-cca2_leaktime.yml b/test/duplicate_consistency/hqc-192-2-cca2_leaktime.yml new file mode 100644 index 00000000..fcb9826d --- /dev/null +++ b/test/duplicate_consistency/hqc-192-2-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-128-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/duplicate_consistency/hqc-256-1-cca2_leaktime.yml b/test/duplicate_consistency/hqc-256-1-cca2_leaktime.yml new file mode 100644 index 00000000..fcb9826d --- /dev/null +++ b/test/duplicate_consistency/hqc-256-1-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-128-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/duplicate_consistency/hqc-256-2-cca2_leaktime.yml b/test/duplicate_consistency/hqc-256-2-cca2_leaktime.yml new file mode 100644 index 00000000..fcb9826d --- /dev/null +++ b/test/duplicate_consistency/hqc-256-2-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-128-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/duplicate_consistency/hqc-256-3-cca2_leaktime.yml b/test/duplicate_consistency/hqc-256-3-cca2_leaktime.yml new file mode 100644 index 00000000..fcb9826d --- /dev/null +++ b/test/duplicate_consistency/hqc-256-3-cca2_leaktime.yml @@ -0,0 +1,26 @@ +consistency_checks: +- source: + scheme: hqc-128-1-cca2 + implementation: leaktime + files: + - bch.c + - bch.h + - fft.c + - fft.h + - gf.c + - gf.h + - gf2x.c + - gf2x.h + - hqc.c + - hqc.h + - kem.c + - parsing.c + - parsing.h + - repetition.c + - repetition.h + - tensor.c + - tensor.h + - vector.c + - vector.h + - util.c + - util.h diff --git a/test/test_common/nistseedexpander.c b/test/test_common/nistseedexpander.c new file mode 100644 index 00000000..78684bbd --- /dev/null +++ b/test/test_common/nistseedexpander.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +#include "nistseedexpander.h" + + +const uint8_t seed[32] = {0}; +const uint8_t expected_result[27] = { 0x55, 0x98, 0x87, 0xae, 0x24, 0x28, 0x5d, 0x7e, 0x42, 0x02, 0x74, 0x27, 0x73, 0x31, 0x03, 0xf6, 0xaf, 0x2e, 0xb6, 0xf1, 0xec, 0xfe, 0xdf, 0xfb, 0xd3, 0x50, 0x31, }; + +int test_seedexpander() { + AES_XOF_struct state; + uint8_t result[27] = {0}; + + seedexpander_init(&state, seed, seed, 28); + seedexpander(&state, result, 27); + + if (memcmp(expected_result, result, 27) != 0) { + printf("Got\tExpected\n"); + for(size_t i = 0; i < 27; i++) { + printf("0x%02x\t0x%02x\n", result[i], expected_result[i]); + } + return 1; + } + return 0; +} + + +int main(void) { + + return test_seedexpander(); +}