Browse Source

HQC: update from upstream

master
John M. Schanck 4 years ago
parent
commit
52e1b5c94b
100 changed files with 0 additions and 9768 deletions
  1. +0
    -36
      crypto_kem/hqc-128/META.yml
  2. +0
    -1
      crypto_kem/hqc-128/avx2/LICENSE
  3. +0
    -22
      crypto_kem/hqc-128/avx2/Makefile
  4. +0
    -16
      crypto_kem/hqc-128/avx2/alpha_table.h
  5. +0
    -25
      crypto_kem/hqc-128/avx2/api.h
  6. +0
    -241
      crypto_kem/hqc-128/avx2/bch.c
  7. +0
    -16
      crypto_kem/hqc-128/avx2/bch.h
  8. +0
    -105
      crypto_kem/hqc-128/avx2/code.c
  9. +0
    -18
      crypto_kem/hqc-128/avx2/code.h
  10. +0
    -359
      crypto_kem/hqc-128/avx2/fft.c
  11. +0
    -18
      crypto_kem/hqc-128/avx2/fft.h
  12. +0
    -14
      crypto_kem/hqc-128/avx2/gen_matrix.h
  13. +0
    -145
      crypto_kem/hqc-128/avx2/gf.c
  14. +0
    -30
      crypto_kem/hqc-128/avx2/gf.h
  15. +0
    -522
      crypto_kem/hqc-128/avx2/gf2x.c
  16. +0
    -15
      crypto_kem/hqc-128/avx2/gf2x.h
  17. +0
    -138
      crypto_kem/hqc-128/avx2/hqc.c
  18. +0
    -19
      crypto_kem/hqc-128/avx2/hqc.h
  19. +0
    -140
      crypto_kem/hqc-128/avx2/kem.c
  20. +0
    -128
      crypto_kem/hqc-128/avx2/parameters.h
  21. +0
    -186
      crypto_kem/hqc-128/avx2/parsing.c
  22. +0
    -36
      crypto_kem/hqc-128/avx2/parsing.h
  23. +0
    -43
      crypto_kem/hqc-128/avx2/repetition.c
  24. +0
    -15
      crypto_kem/hqc-128/avx2/repetition.h
  25. +0
    -194
      crypto_kem/hqc-128/avx2/vector.c
  26. +0
    -27
      crypto_kem/hqc-128/avx2/vector.h
  27. +0
    -1
      crypto_kem/hqc-128/clean/LICENSE
  28. +0
    -19
      crypto_kem/hqc-128/clean/Makefile
  29. +0
    -19
      crypto_kem/hqc-128/clean/Makefile.Microsoft_nmake
  30. +0
    -25
      crypto_kem/hqc-128/clean/api.h
  31. +0
    -286
      crypto_kem/hqc-128/clean/bch.c
  32. +0
    -18
      crypto_kem/hqc-128/clean/bch.h
  33. +0
    -49
      crypto_kem/hqc-128/clean/code.c
  34. +0
    -18
      crypto_kem/hqc-128/clean/code.h
  35. +0
    -673
      crypto_kem/hqc-128/clean/fft.c
  36. +0
    -23
      crypto_kem/hqc-128/clean/fft.h
  37. +0
    -63
      crypto_kem/hqc-128/clean/gf.c
  38. +0
    -38
      crypto_kem/hqc-128/clean/gf.h
  39. +0
    -154
      crypto_kem/hqc-128/clean/gf2x.c
  40. +0
    -16
      crypto_kem/hqc-128/clean/gf2x.h
  41. +0
    -143
      crypto_kem/hqc-128/clean/hqc.c
  42. +0
    -19
      crypto_kem/hqc-128/clean/hqc.h
  43. +0
    -144
      crypto_kem/hqc-128/clean/kem.c
  44. +0
    -120
      crypto_kem/hqc-128/clean/parameters.h
  45. +0
    -186
      crypto_kem/hqc-128/clean/parsing.c
  46. +0
    -36
      crypto_kem/hqc-128/clean/parsing.h
  47. +0
    -85
      crypto_kem/hqc-128/clean/repetition.c
  48. +0
    -17
      crypto_kem/hqc-128/clean/repetition.h
  49. +0
    -176
      crypto_kem/hqc-128/clean/vector.c
  50. +0
    -27
      crypto_kem/hqc-128/clean/vector.h
  51. +0
    -36
      crypto_kem/hqc-192/META.yml
  52. +0
    -1
      crypto_kem/hqc-192/avx2/LICENSE
  53. +0
    -22
      crypto_kem/hqc-192/avx2/Makefile
  54. +0
    -16
      crypto_kem/hqc-192/avx2/alpha_table.h
  55. +0
    -25
      crypto_kem/hqc-192/avx2/api.h
  56. +0
    -241
      crypto_kem/hqc-192/avx2/bch.c
  57. +0
    -16
      crypto_kem/hqc-192/avx2/bch.h
  58. +0
    -105
      crypto_kem/hqc-192/avx2/code.c
  59. +0
    -18
      crypto_kem/hqc-192/avx2/code.h
  60. +0
    -359
      crypto_kem/hqc-192/avx2/fft.c
  61. +0
    -18
      crypto_kem/hqc-192/avx2/fft.h
  62. +0
    -14
      crypto_kem/hqc-192/avx2/gen_matrix.h
  63. +0
    -145
      crypto_kem/hqc-192/avx2/gf.c
  64. +0
    -30
      crypto_kem/hqc-192/avx2/gf.h
  65. +0
    -562
      crypto_kem/hqc-192/avx2/gf2x.c
  66. +0
    -15
      crypto_kem/hqc-192/avx2/gf2x.h
  67. +0
    -138
      crypto_kem/hqc-192/avx2/hqc.c
  68. +0
    -19
      crypto_kem/hqc-192/avx2/hqc.h
  69. +0
    -140
      crypto_kem/hqc-192/avx2/kem.c
  70. +0
    -128
      crypto_kem/hqc-192/avx2/parameters.h
  71. +0
    -186
      crypto_kem/hqc-192/avx2/parsing.c
  72. +0
    -36
      crypto_kem/hqc-192/avx2/parsing.h
  73. +0
    -43
      crypto_kem/hqc-192/avx2/repetition.c
  74. +0
    -15
      crypto_kem/hqc-192/avx2/repetition.h
  75. +0
    -194
      crypto_kem/hqc-192/avx2/vector.c
  76. +0
    -27
      crypto_kem/hqc-192/avx2/vector.h
  77. +0
    -1
      crypto_kem/hqc-192/clean/LICENSE
  78. +0
    -19
      crypto_kem/hqc-192/clean/Makefile
  79. +0
    -19
      crypto_kem/hqc-192/clean/Makefile.Microsoft_nmake
  80. +0
    -25
      crypto_kem/hqc-192/clean/api.h
  81. +0
    -286
      crypto_kem/hqc-192/clean/bch.c
  82. +0
    -18
      crypto_kem/hqc-192/clean/bch.h
  83. +0
    -49
      crypto_kem/hqc-192/clean/code.c
  84. +0
    -18
      crypto_kem/hqc-192/clean/code.h
  85. +0
    -673
      crypto_kem/hqc-192/clean/fft.c
  86. +0
    -23
      crypto_kem/hqc-192/clean/fft.h
  87. +0
    -63
      crypto_kem/hqc-192/clean/gf.c
  88. +0
    -38
      crypto_kem/hqc-192/clean/gf.h
  89. +0
    -154
      crypto_kem/hqc-192/clean/gf2x.c
  90. +0
    -16
      crypto_kem/hqc-192/clean/gf2x.h
  91. +0
    -143
      crypto_kem/hqc-192/clean/hqc.c
  92. +0
    -19
      crypto_kem/hqc-192/clean/hqc.h
  93. +0
    -144
      crypto_kem/hqc-192/clean/kem.c
  94. +0
    -120
      crypto_kem/hqc-192/clean/parameters.h
  95. +0
    -186
      crypto_kem/hqc-192/clean/parsing.c
  96. +0
    -36
      crypto_kem/hqc-192/clean/parsing.h
  97. +0
    -85
      crypto_kem/hqc-192/clean/repetition.c
  98. +0
    -17
      crypto_kem/hqc-192/clean/repetition.h
  99. +0
    -176
      crypto_kem/hqc-192/clean/vector.c
  100. +0
    -27
      crypto_kem/hqc-192/clean/vector.h

+ 0
- 36
crypto_kem/hqc-128/META.yml View File

@@ -1,36 +0,0 @@
name: HQC-128
type: kem
claimed-nist-level: 1
claimed-security: IND-CCA2
length-ciphertext: 6017
length-public-key: 3024
length-secret-key: 3064
length-shared-secret: 64
nistkat-sha256: 32702949431d8a869abb530a2fda87d5c81c63c698673b135e59ad7e8b5a4f5f
principal-submitters:
- Carlos Aguilar Melchor
- Nicolas Aragon
- Slim Bettaieb
- Olivier Blazy
- Jurjen Bos
- Jean-Christophe Deneuville
- Philippe Gaborit
- Edoardo Persichetti
- Jean-Marc Robert
- Pascal Véron
- Gilles Zémor
- Loïc Bidoux
implementations:
- name: clean
version: hqc-submission_2020-05-29 via https://github.com/jschanck/package-pqclean/tree/8a81b2c9/hqc
- name: avx2
version: hqc-submission_2020-05-29 via https://github.com/jschanck/package-pqclean/tree/8a81b2c9/hqc
supported_platforms:
- architecture: x86_64
operating_systems:
- Linux
- Darwin
required_flags:
- avx2
- bmi1
- pclmulqdq

+ 0
- 1
crypto_kem/hqc-128/avx2/LICENSE View File

@@ -1 +0,0 @@
Public Domain

+ 0
- 22
crypto_kem/hqc-128/avx2/Makefile View File

@@ -1,22 +0,0 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libhqc-128_avx2.a
HEADERS=alpha_table.h api.h bch.h code.h fft.h gen_matrix.h gf2x.h gf.h hqc.h parameters.h parsing.h repetition.h vector.h
OBJECTS=bch.o code.o fft.o gf2x.o gf.o hqc.o kem.o parsing.o repetition.o vector.o

CFLAGS=-O3 -mavx2 -mbmi -mpclmul -Wall -Wextra -Wshadow -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.s $(HEADERS)
$(AS) -o $@ $<

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 0
- 16
crypto_kem/hqc-128/avx2/alpha_table.h
File diff suppressed because it is too large
View File


+ 0
- 25
crypto_kem/hqc-128/avx2/api.h View File

@@ -1,25 +0,0 @@
#ifndef PQCLEAN_HQC128_AVX2_API_H
#define PQCLEAN_HQC128_AVX2_API_H
/**
* @file api.h
* @brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme
*/

#define PQCLEAN_HQC128_AVX2_CRYPTO_ALGNAME "HQC-128"

#define PQCLEAN_HQC128_AVX2_CRYPTO_SECRETKEYBYTES 3064
#define PQCLEAN_HQC128_AVX2_CRYPTO_PUBLICKEYBYTES 3024
#define PQCLEAN_HQC128_AVX2_CRYPTO_BYTES 64
#define PQCLEAN_HQC128_AVX2_CRYPTO_CIPHERTEXTBYTES 6017

// As a technicality, the public key is appended to the secret key in order to respect the NIST API.
// Without this constraint, PQCLEAN_HQC128_AVX2_CRYPTO_SECRETKEYBYTES would be defined as 32

int PQCLEAN_HQC128_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk);

int PQCLEAN_HQC128_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);

int PQCLEAN_HQC128_AVX2_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);


#endif

+ 0
- 241
crypto_kem/hqc-128/avx2/bch.c View File

@@ -1,241 +0,0 @@
#include "alpha_table.h"
#include "bch.h"
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include "vector.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file bch.c
* Constant time implementation of BCH codes
*/


static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes);
static void message_from_codeword(uint64_t *message, const uint64_t *codeword);
static void compute_syndromes(__m256i *syndromes, const uint64_t *rcv);
static void compute_roots(uint64_t *error, const uint16_t *sigma);

/**
* @brief Computes the error locator polynomial (ELP) sigma
*
* This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding). <br>
* We use the letter p for rho which is initialized at -1/2. <br>
* The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X). <br>
* Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p. <br>
* sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated. <br>
* 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) {
uint16_t sigma_copy[PARAM_DELTA - 1] = {0};
uint16_t X_sigma_p[PARAM_DELTA + 1] = {0};
uint16_t d_p, d, dd;
uint16_t mask;
int32_t pp; // 2*rho
size_t deg_sigma, deg_sigma_p, deg_sigma_copy, deg_X_sigma_p;

d = syndromes[0];
sigma[0] = 1;
X_sigma_p[1] = 1;
deg_sigma = 0;
deg_sigma_p = 0;
d_p = 1;
pp = -1;
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;

dd = PQCLEAN_HQC128_AVX2_gf_mul(d, PQCLEAN_HQC128_AVX2_gf_inverse(d_p)); // 0 if(d == 0)
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
sigma[i] ^= PQCLEAN_HQC128_AVX2_gf_mul(dd, X_sigma_p[i]);
}

deg_X_sigma_p = 2 * mu - pp + deg_sigma_p;

// mask = 0xffff if(d != 0) and 0 otherwise
mask = -((uint16_t) - d >> 15);

// mask &= 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise
mask &= -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15);

deg_sigma ^= mask & (deg_sigma ^ deg_X_sigma_p);

if (mu == PARAM_DELTA - 1) {
break;
}

// Update pp, d_p and X_sigma_p if needed
pp ^= mask & (pp ^ (2 * mu));
d_p ^= mask & (d_p ^ d);
for (size_t i = PARAM_DELTA - 1; i; --i) {
X_sigma_p[i + 1] = X_sigma_p[i - 1];
X_sigma_p[i + 1] ^= mask & (X_sigma_p[i + 1] ^ sigma_copy[i - 1]);
}
X_sigma_p[1] = 0;
X_sigma_p[0] = 0;
deg_sigma_p ^= mask & (deg_sigma_p ^ deg_sigma_copy);

// Compute the next discrepancy
d = syndromes[2 * mu + 2];
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
d ^= PQCLEAN_HQC128_AVX2_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(uint64_t *message, const uint64_t *codeword) {
uint64_t mask1 = (uint64_t) (0xffffffffffffffff << ((PARAM_N1 - PARAM_K) % 64));
uint64_t mask2 = (uint64_t) (0xffffffffffffffff >> (64 - (PARAM_N1 - PARAM_K) % 64));
size_t index = (PARAM_N1 - PARAM_K) / 64;

for (size_t i = 0; i < VEC_K_SIZE_64 - 1; ++i) {
message[i] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
message[i] |= (codeword[++index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}

// Last byte (8-val % 8 is the number of bits given by message1)
message[VEC_K_SIZE_64 - 1] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
++index;
if (index < VEC_N1_SIZE_64) {
message[VEC_K_SIZE_64 - 1] |= (codeword[index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}
}


/**
* @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.
* These powers have been pre-computed in table_alphaPARAM_DELTA.h
* Syndromes are 16-bits long , hence we can simultaneously compute 16 syndromes
* in a 256-bit register
*
* @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes
* @param[in] rcv Array of size VEC_N1_SIZE_BYTES storing the received word
*/
void compute_syndromes(__m256i *syndromes, const uint64_t *rcv) {
const __m256i zero_256 = _mm256_set1_epi64x(0);
const __m256i mask_one = _mm256_set_epi64x(0x0303030303030303, 0x0202020202020202, 0x0101010101010101, 0x0);
const __m256i mask_two = _mm256_set1_epi64x(-0x7FBFDFEFF7FBFDFF);
const __m256i un_256 = _mm256_set1_epi64x(1);

__m256i y;
__m256i S;
__m256i L;
__m256i tmp_repeat;
uint32_t *aux;
int16_t *alpha_tmp;
size_t i, j;
uint32_t nzflag;
// static variable so that it is stored in the DATA segment
// not in the STACK segment
static uint8_t tmp_array[PARAM_N1 + 4]; // +4 to control overflow due to management of 256 bits
__m256i *z = (__m256i *) tmp_array;
// vectorized version of the separation of the coordinates of the vector v in order to put each coordinate in an unsigned char
// aux is used to consider 4 elements in v at each step of the loop
aux = (uint32_t *) rcv;
for (i = 0; i < ((VEC_N1_SIZE_BYTES >> 2) << 2); i += 4) {
// duplicate aux 8 times in y , i.e y= (aux aux aux .... aux)
y = _mm256_set1_epi32(*aux);
// shuffle the bytes of y so that if aux=(a0 a1 a2 a3)
// then y = (a0 a0 a0 a0 a0 a0 a0 a0 a1 a1 a1 a1 a1 a1 a1 a1 .... a3)
y = _mm256_shuffle_epi8(y, mask_one);
// apply a mask on each byte of y to determine if jth bit of a_k is 0 or 1
z[i >> 2] = _mm256_and_si256(y, mask_two);
aux ++;
}

// Evaluation of the polynomial corresponding to the vector v in alpha^i for i in {1, ..., 2 * PARAM_DELTA}
for (j = 0; j < SYND_SIZE_256; ++j) {
S = zero_256;
alpha_tmp = table_alpha_ij + (j << 4);

for (i = 0; i < PARAM_N1; ++i) {
nzflag = ((-(int32_t) tmp_array[i]) >> 31) & 1;
tmp_repeat = _mm256_set1_epi64x(nzflag);
L = _mm256_cmpeq_epi64(tmp_repeat, un_256);
tmp_repeat = _mm256_lddqu_si256((__m256i *)(alpha_tmp + i * (PARAM_DELTA << 1)));
L = _mm256_and_si256(L, tmp_repeat);
S = _mm256_xor_si256(L, S);
}
_mm256_storeu_si256(syndromes + j, S);
}
}


/**
* @brief Computes the error polynomial error from the error locator polynomial sigma
*
* See function PQCLEAN_HQC128_AVX2_fft for more details.
*
* @param[out] error 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(uint64_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_HQC128_AVX2_fft(w, sigma, PARAM_DELTA + 1);
PQCLEAN_HQC128_AVX2_fft_retrieve_bch_error_poly(error, w);
}



/**
* @brief Decodes the received word
*
* This function relies on four steps:
* <ol>
* <li> The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes.
* <li> The second step is the computation of the error-locator polynomial sigma.
* <li> 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.
* <li> The fourth step is the correction of the errors in the received polynomial.
* </ol>
* 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_HQC128_AVX2_bch_code_decode(uint64_t *message, uint64_t *vector) {
uint16_t sigma[1 << PARAM_FFT] = {0};
uint64_t error[(1 << PARAM_M) / 8] = {0};
static __m256i syndromes_256[SYND_SIZE_256];

// Calculate the 2*PARAM_DELTA syndromes
compute_syndromes(syndromes_256, 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, (uint16_t *)syndromes_256);

// Compute the error polynomial error
compute_roots(error, sigma);

// Add the error polynomial to the received polynomial
PQCLEAN_HQC128_AVX2_vect_add(vector, vector, error, VEC_N1_SIZE_64);

// Retrieve the message from the decoded codeword
message_from_codeword(message, vector);

}

+ 0
- 16
crypto_kem/hqc-128/avx2/bch.h View File

@@ -1,16 +0,0 @@
#ifndef BCH_H
#define BCH_H


/**
* @file bch.h
* Header file of bch.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_AVX2_bch_code_decode(uint64_t *message, uint64_t *vector);


#endif

+ 0
- 105
crypto_kem/hqc-128/avx2/code.c View File

@@ -1,105 +0,0 @@
#include "bch.h"
#include "code.h"
#include "gen_matrix.h"
#include "parameters.h"
#include "repetition.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file code.c
* @brief Implementation of tensor code
*/


static inline uint64_t mux(uint64_t a, uint64_t b, int64_t bit);

static inline uint64_t mux(uint64_t a, uint64_t b, int64_t bit) {
uint64_t ret = a ^ b;
return (ret & (-bit >> 63)) ^ a;
}



/**
*
* @brief Encoding the message m to a code word em using the tensor code
*
* We encode the message using the BCH code. For each bit obtained,
* we duplicate the bit PARAM_N2 times to apply repetition code.
* BCH encoding is done using the classical mG operation,
* columns of the matrix are stored in 256-bit registers
*
* @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_HQC128_AVX2_code_encode(uint64_t *em, const uint64_t *m) {
const uint64_t mask[2][2] = {{0x0UL, 0x0UL}, {0x7FFFFFFFUL, 0x3FFFFFFFUL}};
size_t i, j, pos_r;
uint64_t bit;
uint16_t idx_r;
uint64_t select;


__m256i *colonne, y, aux0;
__m256i msg = _mm256_lddqu_si256((const __m256i *) m);
colonne = ((__m256i *) gen_matrix);

pos_r = 0;
for (i = 0; i < PARAM_N1 - PARAM_K; i++) {
// y is the and operation between m and ith column of G
y = _mm256_and_si256(colonne[i], msg);
// aux0 = (y2 y3 y0 y1)
aux0 = _mm256_permute2x128_si256(y, y, 1);
// y = (y0^y2 y1^y3 y2^y0 y3^y1)
y = _mm256_xor_si256(y, aux0);
// aux0 = (y1^y3 y0^y2 y1^y3 y0^y2)
aux0 = _mm256_shuffle_epi32(y, 0x4e);
// y = (y0^y1^y2^y3 repeated 4 times)
y = _mm256_xor_si256(aux0, y);
bit = _mm_popcnt_u64(_mm256_extract_epi64(y, 0)) & 1;


idx_r = (pos_r & 0x3f);
select = mux(mask[0][0], mask[1][0], bit);
em[(pos_r >> 6) + 0] ^= select << idx_r;
select = mux(mask[0][1], mask[1][1], bit);
em[(pos_r >> 6) + 1] ^= select >> ((63 - idx_r));
pos_r += PARAM_N2;
}

/* now we add the message m */
/* systematic encoding */
pos_r = PARAM_N2 * (PARAM_N1 - PARAM_K);
for (i = 0; i < 4; i++) {
for (j = 0; j < 64; j++) {
bit = (m[i] >> j) & 0x1;


idx_r = (pos_r & 0x3f);
select = mux(mask[0][0], mask[1][0], bit);
em[(pos_r >> 6) + 0] ^= select << idx_r;
select = mux(mask[0][1], mask[1][1], bit);
em[(pos_r >> 6) + 1] ^= select >> ((63 - idx_r));

pos_r += PARAM_N2;
}
}

}


/**
* @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_HQC128_AVX2_code_decode(uint64_t *m, const uint64_t *em) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC128_AVX2_repetition_code_decode(tmp, em);
PQCLEAN_HQC128_AVX2_bch_code_decode(m, tmp);

}

+ 0
- 18
crypto_kem/hqc-128/avx2/code.h View File

@@ -1,18 +0,0 @@
#ifndef CODE_H
#define CODE_H


/**
* @file code.h
* Header file of code.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_AVX2_code_encode(uint64_t *em, const uint64_t *message);

void PQCLEAN_HQC128_AVX2_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 359
crypto_kem/hqc-128/avx2/fft.c View File

@@ -1,359 +0,0 @@
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/**
* @file fft.c
* Implementation of the additive FFT and its transpose.
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf
*/


static void compute_fft_betas(uint16_t *betas);
static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, uint16_t set_size);
static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f);
static void radix_big(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) {
size_t i;
for (i = 0; i < PARAM_M - 1; ++i) {
betas[i] = (uint16_t) (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, uint16_t set_size) {
uint16_t i, j;
subset_sums[0] = 0;

for (i = 0; i < set_size; ++i) {
for (j = 0; j < (1 << i); ++j) {
subset_sums[(1 << i) + j] = set[i] ^ subset_sums[j];
}
}
}



/**
* @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];
break;

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];
break;

case 2:
f0[0] = f[0];
f0[1] = f[2] ^ f[3];
f1[0] = f[1] ^ f0[1];
f1[1] = f[3];
break;

case 1:
f0[0] = f[0];
f1[0] = f[1];
break;

default:
radix_big(f0, f1, f, m_f);
break;
}
}

static void radix_big(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) {
uint16_t Q[2 * (1 << (PARAM_FFT - 2))] = {0};
uint16_t R[2 * (1 << (PARAM_FFT - 2))] = {0};

uint16_t Q0[1 << (PARAM_FFT - 2)] = {0};
uint16_t Q1[1 << (PARAM_FFT - 2)] = {0};
uint16_t R0[1 << (PARAM_FFT - 2)] = {0};
uint16_t R1[1 << (PARAM_FFT - 2)] = {0};

size_t i, n;

n = 1;
n <<= m_f - 2;
memcpy(Q, f + 3 * n, 2 * n);
memcpy(Q + n, f + 3 * n, 2 * n);
memcpy(R, f, 4 * n);

for (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 PQCLEAN_HQC128_AVX2_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};
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};
uint16_t tmp[PARAM_M - (PARAM_FFT - 1)] = {0};

uint16_t beta_m_pow;
size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
for (i = 0; i < m; ++i) {
tmp[i] = PQCLEAN_HQC128_AVX2_gf_mul(betas[i], f[1]);
}

w[0] = f[0];
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
w[x + k] = w[k] ^ tmp[j];
}
x <<= 1;
}

return;
}

// Step 2: compute g
if (betas[m - 1] != 1) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC128_AVX2_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC128_AVX2_gf_mul(beta_m_pow, f[i]);
}
}

// Step 3
radix(f0, f1, f, m_f);

// Step 4: compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC128_AVX2_gf_mul(betas[i], PQCLEAN_HQC128_AVX2_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC128_AVX2_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);

k = 1;
k <<= ((m - 1) & 0xf); // &0xf is to let the compiler know that m-1 is small.
if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant
w[0] = u[0];
w[k] = u[0] ^ f1[0];
for (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_AVX2_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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_AVX2_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). <br>
* The FFT proceeds recursively to evaluate f at all subset sums of a basis B. <br>
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf <br>
* 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). <br>
* 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_HQC128_AVX2_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] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};

size_t i, k;

// 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 (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC128_AVX2_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);

k = 1;
k <<= PARAM_M - 1;
// 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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_AVX2_gf_mul(betas_sums[i], v[i]);
w[k + i] ^= w[i];
}
}



/**
* @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_HQC128_AVX2_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w) {
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
uint64_t bit;
uint16_t k;
size_t i, index;

compute_fft_betas(gammas);
compute_subset_sums(gammas_sums, gammas, PARAM_M - 1);

error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15);

k = 1;
k <<= PARAM_M - 1;
index = PARAM_GF_MUL_ORDER;
bit = 1 ^ ((uint16_t) - w[k] >> 15);
error[index / 8] ^= bit << (index % 64);

for (i = 1; i < k; ++i) {
index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i]];
bit = 1 ^ ((uint16_t) - w[i] >> 15);
error[index / 64] ^= bit << (index % 64);

index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i] ^ 1];
bit = 1 ^ ((uint16_t) - w[k + i] >> 15);
error[index / 64] ^= bit << (index % 64);
}
}

+ 0
- 18
crypto_kem/hqc-128/avx2/fft.h View File

@@ -1,18 +0,0 @@
#ifndef FFT_H
#define FFT_H


/**
* @file fft.h
* Header file of fft.c
*/

#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_AVX2_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs);

void PQCLEAN_HQC128_AVX2_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w);


#endif

+ 0
- 14
crypto_kem/hqc-128/avx2/gen_matrix.h
File diff suppressed because it is too large
View File


+ 0
- 145
crypto_kem/hqc-128/avx2/gf.c View File

@@ -1,145 +0,0 @@
#include "gf.h"
#include "parameters.h"
#include <emmintrin.h>
#include <immintrin.h>
#include <stdint.h>
/**
* @file gf.c
* Galois field implementation with multiplication using the pclmulqdq instruction
*/


static uint16_t gf_reduce(uint64_t x, size_t deg_x);
static uint16_t gf_quad(uint64_t a);



/**
* Reduces polynomial x modulo primitive polynomial GF_POLY.
* @returns x mod GF_POLY
* @param[in] x Polynomial of degree less than 64
* @param[in] deg_x The degree of polynomial x
*/
static uint16_t gf_reduce(uint64_t x, size_t deg_x) {
uint16_t z1, z2, rmdr, dist;
uint64_t mod;
size_t steps, i, j;

// Deduce the number of steps of reduction
steps = CEIL_DIVIDE(deg_x - (PARAM_M - 1), PARAM_GF_POLY_M2);

// Reduce
for (i = 0; i < steps; ++i) {
mod = x >> PARAM_M;
x &= (1 << PARAM_M) - 1;
x ^= mod;

z1 = 0;
rmdr = PARAM_GF_POLY ^ 1;
for (j = PARAM_GF_POLY_WT - 2; j; --j) {
z2 = __tzcnt_u16(rmdr);
dist = (uint16_t) (z2 - z1);
mod <<= dist;
x ^= mod;
rmdr ^= 1 << z2;
z1 = z2;
}
}

return x;
}



/**
* Multiplies two elements of GF(2^GF_M).
* @returns the product a*b
* @param[in] a Element of GF(2^GF_M)
* @param[in] b Element of GF(2^GF_M)
*/
uint16_t PQCLEAN_HQC128_AVX2_gf_mul(uint16_t a, uint16_t b) {
__m128i va = _mm_cvtsi32_si128(a);
__m128i vb = _mm_cvtsi32_si128(b);
__m128i vab = _mm_clmulepi64_si128(va, vb, 0);
uint32_t ab = _mm_cvtsi128_si32(vab);

return gf_reduce(ab, 2 * (PARAM_M - 1));
}



/**
* Squares an element of GF(2^GF_M).
* @returns a^2
* @param[in] a Element of GF(2^GF_M)
*/
uint16_t PQCLEAN_HQC128_AVX2_gf_square(uint16_t a) {
uint32_t b = a;
uint32_t s = b & 1;
for (size_t i = 1; i < PARAM_M; ++i) {
b <<= 1;
s ^= b & (1 << 2 * i);
}

return gf_reduce(s, 2 * (PARAM_M - 1));
}



/**
* Computes the 4th power of an element of GF(2^GF_M).
* @returns a^4
* @param[in] a Element of GF(2^GF_M)
*/
static uint16_t gf_quad(uint64_t a) {
uint64_t q = a & 1;
for (size_t i = 1; i < PARAM_M; ++i) {
a <<= 3;
q ^= a & (1ull << 4 * i);
}

return gf_reduce(q, 4 * (PARAM_M - 1));
}



/**
* Computes the inverse of an element of GF(2^10),
* using a shorter chain of squares and multiplications than fast exponentiation.
* @returns the inverse of a
* @param[in] a Element of GF(2^10)
*/
uint16_t PQCLEAN_HQC128_AVX2_gf_inverse(uint16_t a) {
uint16_t p;
uint16_t a2;

a2 = PQCLEAN_HQC128_AVX2_gf_square(a); // a^2
a = PQCLEAN_HQC128_AVX2_gf_mul(a2, a); // a^2.a
p = gf_quad(a); // a^8.a^4
a = PQCLEAN_HQC128_AVX2_gf_mul(p, a); // a^8.a^4.a^2.a
p = gf_quad(a); // a^32.a^16.a^8.a^4
p = gf_quad(p); // a^128.a^64.a^32.a^16
a = PQCLEAN_HQC128_AVX2_gf_mul(p, a); // a^128.a^64.a^32.a^16.a^8.a^4.a^2.a
p = gf_quad(a); // a^512.a^256.a^128.a^64.a^32.a^16.a^8.a^4
p = PQCLEAN_HQC128_AVX2_gf_mul(p, a2); // a^-1

return p;
}



/**
* Returns i modulo 2^GF_M-1.
* i must be less than 2*(2^GF_M-1).
* Therefore, the return value is either i or i-2^GF_M+1.
* @returns i mod (2^GF_M-1)
* @param[in] i The integer whose modulo is taken
*/
uint16_t PQCLEAN_HQC128_AVX2_gf_mod(uint16_t i) {
uint16_t tmp = (uint16_t) (i - PARAM_GF_MUL_ORDER);

// mask = 0xffff if (i < GF_MUL_ORDER)
uint16_t mask = -(tmp >> 15);

return tmp + (mask & PARAM_GF_MUL_ORDER);
}

+ 0
- 30
crypto_kem/hqc-128/avx2/gf.h
File diff suppressed because it is too large
View File


+ 0
- 522
crypto_kem/hqc-128/avx2/gf2x.c View File

@@ -1,522 +0,0 @@
#include "gf2x.h"
#include "parameters.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>

/**
* \file gf2x.c
* \brief AVX2 implementation of multiplication of two polynomials
*/


// sizes for Toom-Cook
#define T_TM3_3W_256 32
#define T_TM3_3W_64 128

#define VEC_N_SIZE_256 CEIL_DIVIDE(PARAM_N, 256) /*!< The number of needed vectors to store PARAM_N bits*/
__m256i a1_times_a2[2 * VEC_N_SIZE_256 + 1];
uint64_t bloc64[PARAM_OMEGA_R]; // Allocation with the biggest possible weight
uint64_t bit64[PARAM_OMEGA_R]; // Allocation with the biggest possible weight


static inline void reduce(uint64_t *o, const uint64_t *a);
static inline void karat_mult_1(__m128i *C, __m128i *A, __m128i *B);
static inline void karat_mult_2(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_4(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_8(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_16(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_32(__m256i *C, __m256i *A, __m256i *B);
static inline void divByXplus1(__m256i *out, __m256i *in, int size);
static void TOOM3Mult(__m256i *Out, const uint64_t *A, const uint64_t *B);



/**
* @brief Compute o(x) = a(x) mod \f$ X^n - 1\f$
*
* This function computes the modular reduction of the polynomial a(x)
*
* @param[out] o Pointer to the result
* @param[in] a Pointer to the polynomial a(x)
*/
static inline void reduce(uint64_t *o, const uint64_t *a) {
uint64_t r;
uint64_t carry;

for (uint32_t i = 0; i < VEC_N_SIZE_64; i++) {
r = a[i + VEC_N_SIZE_64 - 1] >> (PARAM_N & 63);
carry = (uint64_t) (a[i + VEC_N_SIZE_64] << (64 - (PARAM_N & 63)));
o[i] = a[i] ^ r ^ carry;
}

o[VEC_N_SIZE_64 - 1] &= RED_MASK;
}

/**
* @brief Compute C(x) = A(x)*B(x)
* A(x) and B(x) are stored in 128-bit registers
* This function computes A(x)*B(x) using Karatsuba
*
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_1(__m128i *C, __m128i *A, __m128i *B) {
__m128i D1[2];
__m128i D0[2], D2[2];
__m128i Al = _mm_loadu_si128(A);
__m128i Ah = _mm_loadu_si128(A + 1);
__m128i Bl = _mm_loadu_si128(B);
__m128i Bh = _mm_loadu_si128(B + 1);

// Compute Al.Bl=D0
__m128i DD0 = _mm_clmulepi64_si128(Al, Bl, 0);
__m128i DD2 = _mm_clmulepi64_si128(Al, Bl, 0x11);
__m128i AAlpAAh = _mm_xor_si128(Al, _mm_shuffle_epi32(Al, 0x4e));
__m128i BBlpBBh = _mm_xor_si128(Bl, _mm_shuffle_epi32(Bl, 0x4e));
__m128i DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D0[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D0[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Compute Ah.Bh=D2
DD0 = _mm_clmulepi64_si128(Ah, Bh, 0);
DD2 = _mm_clmulepi64_si128(Ah, Bh, 0x11);
AAlpAAh = _mm_xor_si128(Ah, _mm_shuffle_epi32(Ah, 0x4e));
BBlpBBh = _mm_xor_si128(Bh, _mm_shuffle_epi32(Bh, 0x4e));
DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D2[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D2[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Compute AlpAh.BlpBh=D1
// Initialisation of AlpAh and BlpBh
__m128i AlpAh = _mm_xor_si128(Al, Ah);
__m128i BlpBh = _mm_xor_si128(Bl, Bh);
DD0 = _mm_clmulepi64_si128(AlpAh, BlpBh, 0);
DD2 = _mm_clmulepi64_si128(AlpAh, BlpBh, 0x11);
AAlpAAh = _mm_xor_si128(AlpAh, _mm_shuffle_epi32(AlpAh, 0x4e));
BBlpBBh = _mm_xor_si128(BlpBh, _mm_shuffle_epi32(BlpBh, 0x4e));
DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D1[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D1[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Final comutation of C
__m128i middle = _mm_xor_si128(D0[1], D2[0]);
C[0] = D0[0];
C[1] = middle ^ D0[0] ^ D1[0];
C[2] = middle ^ D1[1] ^ D2[1];
C[3] = D2[1];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_2(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[2], D1[2], D2[2], SAA, SBB;
__m128i *A128 = (__m128i *)A, *B128 = (__m128i *)B;

karat_mult_1((__m128i *) D0, A128, B128);
karat_mult_1((__m128i *) D2, A128 + 2, B128 + 2);

SAA = A[0] ^ A[1];
SBB = B[0] ^ B[1];

karat_mult_1((__m128i *) D1, (__m128i *) &SAA, (__m128i *) &SBB);
__m256i middle = _mm256_xor_si256(D0[1], D2[0]);

C[0] = D0[0];
C[1] = middle ^ D0[0] ^ D1[0];
C[2] = middle ^ D1[1] ^ D2[1];
C[3] = D2[1];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_4(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[4], D1[4], D2[4], SAA[2], SBB[2];

karat_mult_2( D0, A, B);
karat_mult_2(D2, A + 2, B + 2);

SAA[0] = A[0] ^ A[2];
SBB[0] = B[0] ^ B[2];
SAA[1] = A[1] ^ A[3];
SBB[1] = B[1] ^ B[3];

karat_mult_2( D1, SAA, SBB);

__m256i middle0 = _mm256_xor_si256(D0[2], D2[0]);
__m256i middle1 = _mm256_xor_si256(D0[3], D2[1]);

C[0] = D0[0];
C[1] = D0[1];
C[2] = middle0 ^ D0[0] ^ D1[0];
C[3] = middle1 ^ D0[1] ^ D1[1];
C[4] = middle0 ^ D1[2] ^ D2[2];
C[5] = middle1 ^ D1[3] ^ D2[3];
C[6] = D2[2];
C[7] = D2[3];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_8(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[8], D1[8], D2[8], SAA[4], SBB[4];
int32_t i, is, is2, is3;

karat_mult_4( D0, A, B);
karat_mult_4(D2, A + 4, B + 4);

for (i = 0; i < 4; i++) {
is = i + 4;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_4(D1, SAA, SBB);

for (i = 0; i < 4; i++) {
is = i + 4;
is2 = is + 4;
is3 = is2 + 4;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_16(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[16], D1[16], D2[16], SAA[8], SBB[8];
int32_t i, is, is2, is3;

karat_mult_8( D0, A, B);
karat_mult_8(D2, A + 8, B + 8);

for (i = 0; i < 8; i++) {
is = i + 8;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_8( D1, SAA, SBB);

for (i = 0; i < 8; i++) {
is = i + 8;
is2 = is + 8;
is3 = is2 + 8;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_32(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[32], D1[32], D2[32], SAA[16], SBB[16];
int32_t i, is, is2, is3;

karat_mult_16( D0, A, B);
karat_mult_16(D2, A + 16, B + 16);

for (i = 0; i < 16; i++) {
is = i + 16;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_16( D1, SAA, SBB);

for (i = 0; i < 16; i++) {
is = i + 16;
is2 = is + 16;
is3 = is2 + 16;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}


/**
* @brief Compute B(x) = A(x)/(x+1)
*
* This function computes A(x)/(x+1) using a Quercia like algorithm
* @param[out] out Pointer to the result
* @param[in] in Pointer to the polynomial A(x)
* @param[in] size used to define the number of coeeficients of A
*/
static inline void divByXplus1(__m256i *out, __m256i *in, int size) {
uint64_t *A = (uint64_t *) in;
uint64_t *B = (uint64_t *) out;

B[0] = A[0];

for (int32_t i = 1; i < 2 * (size << 2); i++) {
B[i] = B[i - 1] ^ A[i];
}
}



/**
* @brief Compute C(x) = A(x)*B(x) using TOOM3Mult
*
* This function computes A(x)*B(x) using TOOM-COOK3 Multiplication
* last multiplication are done using Karatsuba
* @param[out] Out Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static void TOOM3Mult(__m256i *Out, const uint64_t *A, const uint64_t *B) {
static __m256i U0[T_TM3_3W_256], V0[T_TM3_3W_256], U1[T_TM3_3W_256], V1[T_TM3_3W_256], U2[T_TM3_3W_256], V2[T_TM3_3W_256];
static __m256i W0[2 * (T_TM3_3W_256)], W1[2 * (T_TM3_3W_256)], W2[2 * (T_TM3_3W_256)], W3[2 * (T_TM3_3W_256)], W4[2 * (T_TM3_3W_256)];
static __m256i tmp[2 * (T_TM3_3W_256)];
static __m256i ro256[6 * (T_TM3_3W_256)];
const __m256i zero = _mm256_setzero_si256();
int64_t *U1_64;
int64_t *U2_64;
int64_t *V1_64;
int64_t *V2_64;
int32_t T2 = T_TM3_3W_64 << 1;
int32_t i, i4, i41, i42;

for (i = 0; i < T_TM3_3W_256 - 1; i++) {
i4 = i << 2;
i42 = i4 - 2;
U0[i] = _mm256_lddqu_si256((__m256i const *)(& A[i4]));
V0[i] = _mm256_lddqu_si256((__m256i const *)(& B[i4]));
U1[i] = _mm256_lddqu_si256((__m256i const *)(& A[i42 + T_TM3_3W_64]));
V1[i] = _mm256_lddqu_si256((__m256i const *)(& B[i42 + T_TM3_3W_64]));
U2[i] = _mm256_lddqu_si256((__m256i const *)(& A[i4 + T2 - 4]));
V2[i] = _mm256_lddqu_si256((__m256i const *)(& B[i4 + T2 - 4]));
}

for (i = T_TM3_3W_256 - 1; i < T_TM3_3W_256; i++) {
i4 = i << 2;
i41 = i4 + 1;
U0[i] = _mm256_set_epi64x(0, 0, A[i41], A[i4]);
V0[i] = _mm256_set_epi64x(0, 0, B[i41], B[i4]);
U1[i] = _mm256_set_epi64x(0, 0, A[i41 + T_TM3_3W_64 - 2], A[i4 + T_TM3_3W_64 - 2]);
V1[i] = _mm256_set_epi64x(0, 0, B[i41 + T_TM3_3W_64 - 2], B[i4 + T_TM3_3W_64 - 2]);
U2[i] = _mm256_set_epi64x(0, 0, A[i4 - 3 + T2], A[i4 - 4 + T2]);
V2[i] = _mm256_set_epi64x(0, 0, B[i4 - 3 + T2], B[i4 - 4 + T2]);
}

// Evaluation phase : x= X^64
// P(X): P0=(0); P1=(1); P2=(x); P3=(1+x); P4=(\infty)
// Evaluation: 5*2 add, 2*2 shift; 5 mul (n)
//W3 = U2 + U1 + U0; W2 = V2 + V1 + V0
for (i = 0; i < T_TM3_3W_256; i++) {
W3[i] = U0[i] ^ U1[i] ^ U2[i];
W2[i] = V0[i] ^ V1[i] ^ V2[i];
}

//W1 = W2 * W3
karat_mult_32( W1, W2, W3);

//W0 =(U1 + U2*x)*x; W4 =(V1 + V2*x)*x (SIZE = T_TM3_3W_256 !)
U1_64 = ((int64_t *) U1);
U2_64 = ((int64_t *) U2);

V1_64 = ((int64_t *) V1);
V2_64 = ((int64_t *) V2);

W0[0] = _mm256_set_epi64x(U1_64[2] ^ U2_64[1], U1_64[1] ^ U2_64[0], U1_64[0], 0);
W4[0] = _mm256_set_epi64x(V1_64[2] ^ V2_64[1], V1_64[1] ^ V2_64[0], V1_64[0], 0);

for (i = 1; i < T_TM3_3W_256; i++) {
i4 = i << 2;
W0[i] = _mm256_lddqu_si256((__m256i const *)(& U1_64[i4 - 1]));
W0[i] ^= _mm256_lddqu_si256((__m256i const *)(& U2_64[i4 - 2]));

W4[i] = _mm256_lddqu_si256((__m256i const *)(& V1_64[i4 - 1]));
W4[i] ^= _mm256_lddqu_si256((__m256i const *)(& V2_64[i4 - 2]));
}

//W3 = W3 + W0 ; W2 = W2 + W4
for (i = 0; i < T_TM3_3W_256; i++) {
W3[i] ^= W0[i];
W2[i] ^= W4[i];
}

//W0 = W0 + U0 ; W4 = W4 + V0
for (i = 0; i < T_TM3_3W_256; i++) {
W0[i] ^= U0[i];
W4[i] ^= V0[i];
}

//W3 = W3 * W2 ; W2 = W0 * W4
karat_mult_32(tmp, W3, W2);

for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W3[i] = tmp[i];
}

karat_mult_32(W2, W0, W4);
//W4 = U2 * V2 ; W0 = U0 * V0
karat_mult_32(W4, U2, V2);
karat_mult_32(W0, U0, V0);

// Interpolation phase
// 9 add, 1 shift, 1 Smul, 2 Sdiv (2n)
//W3 = W3 + W2
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W3[i] ^= W2[i];
}

//W1 = W1 + W0
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W1[i] ^= W0[i];
}

//W2 =(W2 + W0)/x -> x = X^64
U1_64 = ((int64_t *) W2);
U2_64 = ((int64_t *) W0);
for (i = 0; i < (T_TM3_3W_256 << 1); i++) {
i4 = i << 2;
W2[i] = _mm256_lddqu_si256((__m256i const *)(& U1_64[i4 + 1]));
W2[i] ^= _mm256_lddqu_si256((__m256i const *)(& U2_64[i4 + 1]));
}

//W2 =(W2 + W3 + W4*(x^3+1))/(x+1)
U1_64 = ((int64_t *) W4);
__m256i *U1_256 = (__m256i *) (U1_64 + 1);
tmp[0] = W2[0] ^ W3[0] ^ W4[0] ^ _mm256_set_epi64x(U1_64[0], 0, 0, 0);

for (i = 1; i < (T_TM3_3W_256 << 1) - 1; i++) {
tmp[i] = W2[i] ^ W3[i] ^ W4[i] ^ _mm256_lddqu_si256(&U1_256[i - 1]);
}

divByXplus1(W2, tmp, T_TM3_3W_256);
W2[2 * (T_TM3_3W_256) - 1] = zero;

//W3 =(W3 + W1)/(x*(x+1))
U1_64 = (int64_t *) W3;
U1_256 = (__m256i *) (U1_64 + 1);

U2_64 = (int64_t *) W1;
__m256i *U2_256 = (__m256i *) (U2_64 + 1);

for (i = 0; i < 2 * (T_TM3_3W_256) - 1; i++) {
tmp[i] = _mm256_lddqu_si256(&U1_256[i]) ^ _mm256_lddqu_si256(&U2_256[i]);
}

divByXplus1(W3, tmp, T_TM3_3W_256);
W3[2 * (T_TM3_3W_256) - 1] = zero;

//W1 = W1 + W4 + W2
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W1[i] ^= W2[i] ^ W4[i];
}

//W2 = W2 + W3
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W2[i] ^= W3[i];
}

// Recomposition
//W = W0+ W1*x+ W2*x^2+ W3*x^3 + W4*x^4
//W0, W1, W4 of size 2*T_TM3_3W_256, W2 and W3 of size 2*(T_TM3_3W_256)
for (i = 0; i < (T_TM3_3W_256 << 1) - 1; i++) {
ro256[i] = W0[i];
ro256[i + 2 * T_TM3_3W_256 - 1] = W2[i];
ro256[i + 4 * T_TM3_3W_256 - 2] = W4[i];
}

ro256[(T_TM3_3W_256 << 1) - 1] = W0[(T_TM3_3W_256 << 1) - 1] ^ W2[0];
ro256[(T_TM3_3W_256 << 2) - 2] = W2[(T_TM3_3W_256 << 1) - 1] ^ W4[0];
ro256[(T_TM3_3W_256 * 6) - 3] = W4[(T_TM3_3W_256 << 1) - 1];

U1_64 = ((int64_t *) &ro256[T_TM3_3W_256]);
U1_256 = (__m256i *) (U1_64 - 2);

U2_64 = ((int64_t *) &ro256[3 * T_TM3_3W_256 - 1]);
U2_256 = (__m256i *) (U2_64 - 2);

for (i = 0; i < T_TM3_3W_256 << 1; i++) {
_mm256_storeu_si256(&U1_256[i], W1[i] ^ _mm256_lddqu_si256(&U1_256[i]));
_mm256_storeu_si256(&U2_256[i], W3[i] ^ _mm256_loadu_si256(&U2_256[i]));
}

for (i = 0; i < 2 * VEC_N_SIZE_256 + 1; i++) {
_mm256_storeu_si256(&Out[i], ro256[i]);
}
}


/**
* @brief Multiply two polynomials modulo \f$ X^n - 1\f$.
*
* This functions multiplies a sparse polynomial <b>a1</b> (of Hamming weight equal to <b>weight</b>)
* and a dense polynomial <b>a2</b>. The multiplication is done modulo \f$ X^n - 1\f$.
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to a polynomial
* @param[in] a2 Pointer to a polynomial
*/
void PQCLEAN_HQC128_AVX2_vect_mul(uint64_t *o, const uint64_t *a1, const uint64_t *a2) {
TOOM3Mult(a1_times_a2, a1, a2);
reduce(o, (uint64_t *)a1_times_a2);

// clear all
memset(a1_times_a2, 0, (2 * VEC_N_SIZE_256 + 1) * sizeof(__m256i));
}

+ 0
- 15
crypto_kem/hqc-128/avx2/gf2x.h View File

@@ -1,15 +0,0 @@
#ifndef GF2X_H
#define GF2X_H


/**
* @file gf2x.h
* @brief Header file for gf2x.c
*/

#include <stdint.h>

void PQCLEAN_HQC128_AVX2_vect_mul(uint64_t *o, const uint64_t *a1, const uint64_t *a2);


#endif

+ 0
- 138
crypto_kem/hqc-128/avx2/hqc.c View File

@@ -1,138 +0,0 @@
#include "code.h"
#include "gf2x.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
/**
* @file hqc.c
* @brief Implementation of hqc.h
*/



/**
* @brief Keygen of the HQC_PKE IND_CPA scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the <b>seed</b> used to generate the vector <b>h</b>.
*
* The secret key is composed of the <b>seed</b> used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_AVX2_hqc_pke_keygen(unsigned char *pk, unsigned char *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};
uint64_t x[VEC_N_256_SIZE_64] = {0};
uint64_t y[VEC_N_256_SIZE_64] = {0};
uint64_t h[VEC_N_256_SIZE_64] = {0};
uint64_t s[VEC_N_256_SIZE_64] = {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_HQC128_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, y, PARAM_OMEGA);

// Compute public key
PQCLEAN_HQC128_AVX2_vect_set_random(&pk_seedexpander, h);
PQCLEAN_HQC128_AVX2_vect_mul(s, y, h);
PQCLEAN_HQC128_AVX2_vect_add(s, x, s, VEC_N_256_SIZE_64);

// Parse keys to string
PQCLEAN_HQC128_AVX2_hqc_public_key_to_string(pk, pk_seed, s);
PQCLEAN_HQC128_AVX2_hqc_secret_key_to_string(sk, sk_seed, pk);

}



/**
* @brief Encryption of the HQC_PKE IND_CPA scheme
*
* The cihertext is composed of vectors <b>u</b> and <b>v</b>.
*
* @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_HQC128_AVX2_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk) {
AES_XOF_struct seedexpander;
uint64_t h[VEC_N_256_SIZE_64] = {0};
uint64_t s[VEC_N_256_SIZE_64] = {0};
uint64_t r1[VEC_N_256_SIZE_64] = {0};
uint64_t r2[VEC_N_256_SIZE_64] = {0};
uint64_t e[VEC_N_256_SIZE_64] = {0};
uint64_t tmp1[VEC_N_256_SIZE_64] = {0};
uint64_t tmp2[VEC_N_256_SIZE_64] = {0};

// Create seed_expander from theta
seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH);

// Retrieve h and s from public key
PQCLEAN_HQC128_AVX2_hqc_public_key_from_string(h, s, pk);

// Generate r1, r2 and e
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R);
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&seedexpander, r2, PARAM_OMEGA_R);
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E);

// Compute u = r1 + r2.h
PQCLEAN_HQC128_AVX2_vect_mul(u, r2, h);
PQCLEAN_HQC128_AVX2_vect_add(u, r1, u, VEC_N_256_SIZE_64);

// Compute v = m.G by encoding the message
PQCLEAN_HQC128_AVX2_code_encode(v, m);
PQCLEAN_HQC128_AVX2_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);

// Compute v = m.G + s.r2 + e
PQCLEAN_HQC128_AVX2_vect_mul(tmp2, r2, s);
PQCLEAN_HQC128_AVX2_vect_add(tmp2, e, tmp2, VEC_N_256_SIZE_64);
PQCLEAN_HQC128_AVX2_vect_add(tmp2, tmp1, tmp2, VEC_N_256_SIZE_64);
PQCLEAN_HQC128_AVX2_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_HQC128_AVX2_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk) {
uint64_t x[VEC_N_256_SIZE_64] = {0};
uint64_t y[VEC_N_256_SIZE_64] = {0};
uint8_t pk[PUBLIC_KEY_BYTES] = {0};
uint64_t tmp1[VEC_N_256_SIZE_64] = {0};
uint64_t tmp2[VEC_N_256_SIZE_64] = {0};

// Retrieve x, y, pk from secret key
PQCLEAN_HQC128_AVX2_hqc_secret_key_from_string(x, y, pk, sk);

// Compute v - u.y
PQCLEAN_HQC128_AVX2_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);
PQCLEAN_HQC128_AVX2_vect_mul(tmp2, y, u);
PQCLEAN_HQC128_AVX2_vect_add(tmp2, tmp1, tmp2, VEC_N_256_SIZE_64);


// Compute m by decoding v - u.y
PQCLEAN_HQC128_AVX2_code_decode(m, tmp2);
}

+ 0
- 19
crypto_kem/hqc-128/avx2/hqc.h View File

@@ -1,19 +0,0 @@
#ifndef HQC_H
#define HQC_H


/**
* @file hqc.h
* @brief Functions of the HQC_PKE IND_CPA scheme
*/

#include <stdint.h>

void PQCLEAN_HQC128_AVX2_hqc_pke_keygen(unsigned char *pk, unsigned char *sk);

void PQCLEAN_HQC128_AVX2_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk);

void PQCLEAN_HQC128_AVX2_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk);


#endif

+ 0
- 140
crypto_kem/hqc-128/avx2/kem.c View File

@@ -1,140 +0,0 @@
#include "api.h"
#include "fips202.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "sha2.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file kem.c
* @brief Implementation of api.h
*/



/**
* @brief Keygen of the HQC_KEM IND_CAA2 scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>.
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {

PQCLEAN_HQC128_AVX2_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_HQC128_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) {

uint8_t theta[SHA512_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint64_t u[VEC_N_256_SIZE_64] = {0};
uint64_t v[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Computing m
PQCLEAN_HQC128_AVX2_vect_set_random_from_randombytes(m);

// Computing theta
sha3_512(theta, (uint8_t *) m, VEC_K_SIZE_BYTES);

// Encrypting m
PQCLEAN_HQC128_AVX2_hqc_pke_encrypt(u, v, m, theta, pk);

// Computing d
sha512(d, (unsigned char *) m, VEC_K_SIZE_BYTES);

// Computing shared secret
PQCLEAN_HQC128_AVX2_store8_arr(mc, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);
PQCLEAN_HQC128_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC128_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Computing ciphertext
PQCLEAN_HQC128_AVX2_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_HQC128_AVX2_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) {

uint8_t result;
uint64_t u[VEC_N_256_SIZE_64] = {0};
uint64_t v[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char pk[PUBLIC_KEY_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint8_t theta[SHA512_BYTES] = {0};
uint64_t u2[VEC_N_256_SIZE_64] = {0};
uint64_t v2[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d2[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Retrieving u, v and d from ciphertext
PQCLEAN_HQC128_AVX2_hqc_ciphertext_from_string(u, v, d, ct);

// Retrieving pk from sk
memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES);

// Decryting
PQCLEAN_HQC128_AVX2_hqc_pke_decrypt(m, u, v, sk);

// Computing theta
sha3_512(theta, (uint8_t *) m, VEC_K_SIZE_BYTES);

// Encrypting m'
PQCLEAN_HQC128_AVX2_hqc_pke_encrypt(u2, v2, m, theta, pk);

// Computing d'
sha512(d2, (unsigned char *) m, VEC_K_SIZE_BYTES);

// Computing shared secret
PQCLEAN_HQC128_AVX2_store8_arr(mc, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);
PQCLEAN_HQC128_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC128_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Abort if c != c' or d != d'
result = PQCLEAN_HQC128_AVX2_vect_compare((uint8_t *)u, (uint8_t *)u2, VEC_N_SIZE_BYTES);
result |= PQCLEAN_HQC128_AVX2_vect_compare((uint8_t *)v, (uint8_t *)v2, VEC_N1N2_SIZE_BYTES);
result |= PQCLEAN_HQC128_AVX2_vect_compare(d, d2, SHA512_BYTES);
result = (uint8_t) (-((int16_t) result) >> 15);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
ss[i] &= ~result;
}


return -(result & 1);
}

+ 0
- 128
crypto_kem/hqc-128/avx2/parameters.h View File

@@ -1,128 +0,0 @@
#ifndef HQC_PARAMETERS_H
#define HQC_PARAMETERS_H
/**
* @file parameters.h
* @brief Parameters of the HQC_KEM IND-CCA2 scheme
*/
#include "api.h"
#include "vector.h"


#define CEIL_DIVIDE(a, b) (((a)+(b)-1)/(b)) /*!< Divide a by b and ceil the result*/

/*
#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_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes
#define VEC_K_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes
#define VEC_N1_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes
#define VEC_N1N2_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes

#define VEC_N_SIZE_64 Define the size of the array used to store a PARAM_N_MULT sized vector in 64 bits
#define VEC_K_SIZE_64 Define the size of the array used to store a PARAM_K sized vector in 64 bits
#define VEC_N1_SIZE_64 Define the size of the array used to store a PARAM_N1 sized vector in 64 bits
#define VEC_N1N2_SIZE_64 Define the size of the array used to store a PARAM_N1N2 sized vector in 64 bits

#define VEC_N_256_SIZE_64 Define the size of the array of 64 bits elements used to store an array of size PARAM_N considered as elements of 256 bits
#define VEC_N1N2_256_SIZE_64 Define the size of the array of 64 bits elements used to store an array of size PARAM_N1N2 considered as elements of 256 bits

#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_POLY Generator polynomial of galois field GF(2^PARAM_M), represented in hexadecimial form
#define PARAM_GF_POLY_WT Hamming weight of PARAM_GF_POLY
#define PARAM_GF_POLY_M2 Distance between the primitive polynomial first two set bits
#define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^PARAM_M), i.e 2^PARAM_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_BCH_POLY Generator polynomial of the BCH code

#define RED_MASK A mask fot the higher bits of a vector
#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 23869
#define PARAM_N1 766
#define PARAM_N2 31
#define PARAM_N1N2 23746
#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_HQC128_AVX2_CRYPTO_SECRETKEYBYTES
#define PUBLIC_KEY_BYTES PQCLEAN_HQC128_AVX2_CRYPTO_PUBLICKEYBYTES
#define SHARED_SECRET_BYTES PQCLEAN_HQC128_AVX2_CRYPTO_BYTES
#define CIPHERTEXT_BYTES PQCLEAN_HQC128_AVX2_CRYPTO_CIPHERTEXTBYTES

#define UTILS_REJECTION_THRESHOLD 16756038
#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8)
#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8)
#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8)
#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8)

#define VEC_N_SIZE_64 CEIL_DIVIDE(PARAM_N, 64)
#define VEC_K_SIZE_64 CEIL_DIVIDE(PARAM_K, 64)
#define VEC_N1_SIZE_64 CEIL_DIVIDE(PARAM_N1, 64)
#define VEC_N1N2_SIZE_64 CEIL_DIVIDE(PARAM_N1N2, 64)

#define PARAM_N_MULT 24192
#define VEC_N_256_SIZE_64 (CEIL_DIVIDE(PARAM_N_MULT, 256) << 2)
#define VEC_N1N2_256_SIZE_64 (CEIL_DIVIDE(PARAM_N1N2, 256) << 2)

#define PARAM_T 15

#define PARAM_DELTA 57
#define PARAM_M 10
#define PARAM_GF_POLY 0x409
#define PARAM_GF_POLY_WT 3
#define PARAM_GF_POLY_M2 7
#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 RED_MASK 0x1fffffffffffffffUL
#define SHA512_BYTES 64
#define SEED_BYTES 40
#define SEEDEXPANDER_MAX_LENGTH 4294967295

#endif

+ 0
- 186
crypto_kem/hqc-128/avx2/parsing.c View File

@@ -1,186 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file parsing.c
* @brief Functions to parse secret key, public key and ciphertext of the HQC scheme
*/


void PQCLEAN_HQC128_AVX2_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_HQC128_AVX2_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_HQC128_AVX2_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_HQC128_AVX2_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_HQC128_AVX2_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++;
}
}
}


/**
* @brief Parse a secret key into a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_AVX2_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) {
memcpy(sk, sk_seed, SEED_BYTES);
sk += SEED_BYTES;
memcpy(sk, pk, PUBLIC_KEY_BYTES);
}

/**
* @brief Parse a secret key from a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* As technicality, the public key is appended to the secret key in order to respect NIST API.
*
* @param[out] x uint64_t representation of vector x
* @param[out] y uint64_t representation of vector y
* @param[out] pk String containing the public key
* @param[in] sk String containing the secret key
*/
void PQCLEAN_HQC128_AVX2_hqc_secret_key_from_string(uint64_t *x, uint64_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);
sk += SEED_BYTES;
memcpy(pk, sk, PUBLIC_KEY_BYTES);

seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, y, PARAM_OMEGA);
}

/**
* @brief Parse a public key into a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC128_AVX2_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s) {
memcpy(pk, pk_seed, SEED_BYTES);
PQCLEAN_HQC128_AVX2_store8_arr(pk + SEED_BYTES, VEC_N_SIZE_BYTES, s, VEC_N_SIZE_64);
}



/**
* @brief Parse a public key from a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC128_AVX2_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk) {
AES_XOF_struct pk_seedexpander;
uint8_t pk_seed[SEED_BYTES] = {0};

memcpy(pk_seed, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_HQC128_AVX2_load8_arr(s, VEC_N_SIZE_64, pk, VEC_N_SIZE_BYTES);

seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC128_AVX2_vect_set_random(&pk_seedexpander, h);
}


/**
* @brief Parse a ciphertext into a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC128_AVX2_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d) {
PQCLEAN_HQC128_AVX2_store8_arr(ct, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC128_AVX2_store8_arr(ct, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(ct, d, SHA512_BYTES);
}


/**
* @brief Parse a ciphertext from a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC128_AVX2_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct) {
PQCLEAN_HQC128_AVX2_load8_arr(u, VEC_N_SIZE_64, ct, VEC_N_SIZE_BYTES);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC128_AVX2_load8_arr(v, VEC_N1N2_SIZE_64, ct, VEC_N1N2_SIZE_BYTES);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(d, ct, SHA512_BYTES);
}

+ 0
- 36
crypto_kem/hqc-128/avx2/parsing.h View File

@@ -1,36 +0,0 @@
#ifndef PARSING_H
#define PARSING_H


/**
* @file parsing.h
* @brief Header file for parsing.c
*/

#include <stdint.h>

void PQCLEAN_HQC128_AVX2_store8(unsigned char *out, uint64_t in);

uint64_t PQCLEAN_HQC128_AVX2_load8(const unsigned char *in);

void PQCLEAN_HQC128_AVX2_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen);

void PQCLEAN_HQC128_AVX2_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen);


void PQCLEAN_HQC128_AVX2_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk);

void PQCLEAN_HQC128_AVX2_hqc_secret_key_from_string(uint64_t *x, uint64_t *y, uint8_t *pk, const uint8_t *sk);


void PQCLEAN_HQC128_AVX2_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s);

void PQCLEAN_HQC128_AVX2_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk);


void PQCLEAN_HQC128_AVX2_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d);

void PQCLEAN_HQC128_AVX2_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct);


#endif

+ 0
- 43
crypto_kem/hqc-128/avx2/repetition.c View File

@@ -1,43 +0,0 @@
#include "parameters.h"
#include "repetition.h"
#include <immintrin.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/**
* @file repetition.c
* @brief Implementation of repetition codes
*/


#define MASK_N2 ((((uint64_t) 1) << PARAM_N2) - 1)

/**
* @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_HQC128_AVX2_repetition_code_decode(uint64_t *m, const uint64_t *em) {
size_t t = 0;
uint32_t b, bn, bi, c, cn, ci;
uint64_t cx, ones;
uint64_t mask;

for (b = 0; b < PARAM_N1N2 - PARAM_N2 + 1; b += PARAM_N2) {
bn = b >> 6;
bi = b & 63;
c = b + PARAM_N2 - 1;
cn = c >> 6;
ci = c & 63;
cx = em[cn] << (63 - ci);
mask = (uint64_t) (-((int64_t) (cn ^ (bn + 1))) >> 63); // cn != bn+1
ones = _mm_popcnt_u64(((em[bn] >> bi) & MASK_N2) | (cx & ~mask));
m[t >> 6] |= (uint64_t) ((((PARAM_T - ones) >> 31) & 1) << (t & 63));
t++;
}
}

+ 0
- 15
crypto_kem/hqc-128/avx2/repetition.h View File

@@ -1,15 +0,0 @@
#ifndef REPETITION_H
#define REPETITION_H


/**
* @file repetition.h
* @brief Header file for repetition.c
*/

#include <stdint.h>

void PQCLEAN_HQC128_AVX2_repetition_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 194
crypto_kem/hqc-128/avx2/vector.c View File

@@ -1,194 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file vector.c
* @brief Implementation of vectors sampling and some utilities for the HQC scheme
*/



/**
* @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 <b>weight</b>.
* 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_HQC128_AVX2_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight) {
size_t random_bytes_size = 3 * weight;
uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0};
uint32_t tmp[PARAM_OMEGA_R] = {0};
__m256i bit256[PARAM_OMEGA_R];
__m256i bloc256[PARAM_OMEGA_R];
__m256i posCmp256 = _mm256_set_epi64x(3, 2, 1, 0);
__m256i pos256;
__m256i mask256;
__m256i aux;
__m256i i256;
uint64_t bloc, pos, bit64;
uint8_t inc;
size_t i, j, k;

i = 0;
j = random_bytes_size;
while (i < weight) {
do {
if (j == random_bytes_size) {
seedexpander(ctx, rand_bytes, random_bytes_size);
j = 0;
}

tmp[i] = ((uint32_t) rand_bytes[j++]) << 16;
tmp[i] |= ((uint32_t) rand_bytes[j++]) << 8;
tmp[i] |= rand_bytes[j++];

} while (tmp[i] >= UTILS_REJECTION_THRESHOLD);

tmp[i] = tmp[i] % PARAM_N;

inc = 1;
for (k = 0; k < i; k++) {
if (tmp[k] == tmp[i]) {
inc = 0;
}
}
i += inc;
}

for (i = 0; i < weight; i++) {
// we store the bloc number and bit position of each vb[i]
bloc = tmp[i] >> 6;
bloc256[i] = _mm256_set1_epi64x(bloc >> 2);
pos = (bloc & 0x3UL);
pos256 = _mm256_set1_epi64x(pos);
mask256 = _mm256_cmpeq_epi64(pos256, posCmp256);
bit64 = 1ULL << (tmp[i] & 0x3f);
bit256[i] = _mm256_set1_epi64x(bit64)&mask256;
}

for (i = 0; i < CEIL_DIVIDE(PARAM_N, 256); i++) {
aux = _mm256_loadu_si256(((__m256i *)v) + i);
i256 = _mm256_set1_epi64x(i);

for (j = 0; j < weight; j++) {
mask256 = _mm256_cmpeq_epi64(bloc256[j], i256);
aux ^= bit256[j] & mask256;
}
_mm256_storeu_si256(((__m256i *)v) + i, aux);
}

}



/**
* @brief Generates a random vector of dimension <b>PARAM_N</b>
*
* This function generates a random binary vector of dimension <b>PARAM_N</b>. 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_HQC128_AVX2_vect_set_random(AES_XOF_struct *ctx, uint64_t *v) {
uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0};

seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES);

PQCLEAN_HQC128_AVX2_load8_arr(v, VEC_N_SIZE_64, rand_bytes, VEC_N_SIZE_BYTES);
v[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @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_HQC128_AVX2_vect_set_random_from_randombytes(uint64_t *v) {
uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0};

randombytes(rand_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC128_AVX2_load8_arr(v, VEC_K_SIZE_64, 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_HQC128_AVX2_vect_add(uint64_t *o, const uint64_t *v1, const uint64_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
*/
uint8_t PQCLEAN_HQC128_AVX2_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) {
uint64_t r = 0;
for (size_t i = 0; i < size; i++) {
r |= v1[i] ^ v2[i];
}
r = (~r + 1) >> 63;
return (uint8_t) r;
}



/**
* @brief Resize a vector so that it contains <b>size_o</b> 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_HQC128_AVX2_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v) {
uint64_t mask = 0x7FFFFFFFFFFFFFFF;
int8_t val = 0;
if (size_o < size_v) {
if (size_o % 64) {
val = 64 - (size_o % 64);
}

memcpy(o, v, VEC_N1N2_SIZE_BYTES);

for (int8_t i = 0; i < val; ++i) {
o[VEC_N1N2_SIZE_64 - 1] &= (mask >> i);
}
} else {
memcpy(o, v, CEIL_DIVIDE(size_v, 8));
}
}

+ 0
- 27
crypto_kem/hqc-128/avx2/vector.h View File

@@ -1,27 +0,0 @@
#ifndef VECTOR_H
#define VECTOR_H


/**
* @file vector.h
* @brief Header file for vector.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC128_AVX2_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight);

void PQCLEAN_HQC128_AVX2_vect_set_random(AES_XOF_struct *ctx, uint64_t *v);

void PQCLEAN_HQC128_AVX2_vect_set_random_from_randombytes(uint64_t *v);


void PQCLEAN_HQC128_AVX2_vect_add(uint64_t *o, const uint64_t *v1, const uint64_t *v2, uint32_t size);

uint8_t PQCLEAN_HQC128_AVX2_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size);

void PQCLEAN_HQC128_AVX2_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v);


#endif

+ 0
- 1
crypto_kem/hqc-128/clean/LICENSE View File

@@ -1 +0,0 @@
Public Domain

+ 0
- 19
crypto_kem/hqc-128/clean/Makefile View File

@@ -1,19 +0,0 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libhqc-128_clean.a
HEADERS=api.h bch.h code.h fft.h gf2x.h gf.h hqc.h parameters.h parsing.h repetition.h vector.h
OBJECTS=bch.o code.o fft.o gf2x.o gf.o hqc.o kem.o parsing.o repetition.o vector.o

CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wshadow -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 0
- 19
crypto_kem/hqc-128/clean/Makefile.Microsoft_nmake View File

@@ -1,19 +0,0 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake

LIBRARY=libhqc-128_clean.lib
OBJECTS=bch.obj code.obj fft.obj gf2x.obj gf.obj hqc.obj kem.obj parsing.obj repetition.obj vector.obj

CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX

all: $(LIBRARY)

# Make sure objects are recompiled if headers change.
$(OBJECTS): *.h

$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**

clean:
-DEL $(OBJECTS)
-DEL $(LIBRARY)

+ 0
- 25
crypto_kem/hqc-128/clean/api.h View File

@@ -1,25 +0,0 @@
#ifndef PQCLEAN_HQC128_CLEAN_API_H
#define PQCLEAN_HQC128_CLEAN_API_H
/**
* @file api.h
* @brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme
*/

#define PQCLEAN_HQC128_CLEAN_CRYPTO_ALGNAME "HQC-128"

#define PQCLEAN_HQC128_CLEAN_CRYPTO_SECRETKEYBYTES 3064
#define PQCLEAN_HQC128_CLEAN_CRYPTO_PUBLICKEYBYTES 3024
#define PQCLEAN_HQC128_CLEAN_CRYPTO_BYTES 64
#define PQCLEAN_HQC128_CLEAN_CRYPTO_CIPHERTEXTBYTES 6017

// As a technicality, the public key is appended to the secret key in order to respect the NIST API.
// Without this constraint, PQCLEAN_HQC128_CLEAN_CRYPTO_SECRETKEYBYTES would be defined as 32

int PQCLEAN_HQC128_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk);

int PQCLEAN_HQC128_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);

int PQCLEAN_HQC128_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);


#endif

+ 0
- 286
crypto_kem/hqc-128/clean/bch.c View File

@@ -1,286 +0,0 @@
#include "bch.h"
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file bch.c
* Constant time implementation of BCH codes
*/


static void unpack_message(uint8_t *message_unpacked, const uint64_t *message);
static void lfsr_encode(uint8_t *codeword, const uint8_t *message);
static void pack_codeword(uint64_t *codeword, const uint8_t *codeword_unpacked);
static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes);
static void message_from_codeword(uint64_t *message, const uint64_t *codeword);
static void compute_syndromes(uint16_t *syndromes, const uint64_t *vector);
static void compute_roots(uint64_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 uint64_t *message) {
for (size_t i = 0; i < (VEC_K_SIZE_64 - (PARAM_K % 64 != 0)); ++i) {
for (size_t j = 0; j < 64; ++j) {
message_unpacked[j + 64 * i] = (message[i] >> j) & 0x0000000000000001;
}
}

for (int8_t j = 0; j < PARAM_K % 64; ++j) {
message_unpacked[j + 64 * (VEC_K_SIZE_64 - 1)] = (message[VEC_K_SIZE_64 - 1] >> j) & 0x0000000000000001;
}
}


/**
* @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(uint64_t *codeword, const uint8_t *codeword_unpacked) {
for (size_t i = 0; i < (VEC_N1_SIZE_64 - (PARAM_N1 % 64 != 0)); ++i) {
for (size_t j = 0; j < 64; ++j) {
codeword[i] |= ((uint64_t) codeword_unpacked[j + 64 * i]) << j;
}
}

for (size_t j = 0; j < PARAM_N1 % 64; ++j) {
codeword[VEC_N1_SIZE_64 - 1] |= ((uint64_t) codeword_unpacked[j + 64 * (VEC_N1_SIZE_64 - 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_HQC128_CLEAN_bch_code_encode(uint64_t *codeword, const uint64_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). <br>
* We use the letter p for rho which is initialized at -1/2. <br>
* The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X). <br>
* Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p. <br>
* sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated. <br>
* 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) {
uint16_t sigma_copy[PARAM_DELTA - 1] = {0};
uint16_t X_sigma_p[PARAM_DELTA + 1] = {0};
uint16_t d_p, d, dd;
uint16_t mask;
int32_t pp; // 2*rho
size_t deg_sigma, deg_sigma_p, deg_sigma_copy, deg_X_sigma_p;

d = syndromes[0];
sigma[0] = 1;
X_sigma_p[1] = 1;
deg_sigma = 0;
deg_sigma_p = 0;
d_p = 1;
pp = -1;
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;

dd = PQCLEAN_HQC128_CLEAN_gf_mul(d, PQCLEAN_HQC128_CLEAN_gf_inverse(d_p)); // 0 if(d == 0)
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
sigma[i] ^= PQCLEAN_HQC128_CLEAN_gf_mul(dd, X_sigma_p[i]);
}

deg_X_sigma_p = 2 * mu - pp + deg_sigma_p;

// mask = 0xffff if(d != 0) and 0 otherwise
mask = -((uint16_t) - d >> 15);

// mask2 &= 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise
mask &= -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15);

deg_sigma ^= mask & (deg_sigma ^ deg_X_sigma_p);

if (mu == PARAM_DELTA - 1) {
break;
}

// Update pp, d_p and X_sigma_p if needed
pp ^= mask & (pp ^ (2 * mu));
d_p ^= mask & (d_p ^ d);
for (size_t i = PARAM_DELTA - 1; i; --i) {
X_sigma_p[i + 1] = X_sigma_p[i - 1];
X_sigma_p[i + 1] ^= mask & (X_sigma_p[i + 1] ^ sigma_copy[i - 1]);
}
X_sigma_p[1] = 0;
X_sigma_p[0] = 0;
deg_sigma_p ^= mask & (deg_sigma_p ^ deg_sigma_copy);

// Compute the next discrepancy
d = syndromes[2 * mu + 2];
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
d ^= PQCLEAN_HQC128_CLEAN_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(uint64_t *message, const uint64_t *codeword) {
uint64_t mask1 = (uint64_t) (0xffffffffffffffff << ((PARAM_N1 - PARAM_K) % 64));
uint64_t mask2 = (uint64_t) (0xffffffffffffffff >> (64 - (PARAM_N1 - PARAM_K) % 64));
size_t index = (PARAM_N1 - PARAM_K) / 64;

for (size_t i = 0; i < VEC_K_SIZE_64 - 1; ++i) {
message[i] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
message[i] |= (codeword[++index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}

// Last byte (8-val % 8 is the number of bits given by message1)
message[VEC_K_SIZE_64 - 1] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
++index;
if (index < VEC_N1_SIZE_64) {
message[VEC_K_SIZE_64 - 1] |= (codeword[index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}
}


/**
* @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. <br>
* Therefore, this requires twisting and applying a permutation before feeding vector to the PQCLEAN_HQC128_CLEAN_fft transpose. <br>
* 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 uint64_t *vector) {
uint16_t w[1 << PARAM_M];

PQCLEAN_HQC128_CLEAN_fft_t_preprocess_bch_codeword(w, vector);
PQCLEAN_HQC128_CLEAN_fft_t(syndromes, w, 2 * PARAM_DELTA);
}


/**
* @brief Computes the error polynomial error from the error locator polynomial sigma
*
* See function PQCLEAN_HQC128_CLEAN_fft for more details.
*
* @param[out] error 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(uint64_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_HQC128_CLEAN_fft(w, sigma, PARAM_DELTA + 1);
PQCLEAN_HQC128_CLEAN_fft_retrieve_bch_error_poly(error, w);
}



/**
* @brief Decodes the received word
*
* This function relies on four steps:
* <ol>
* <li> The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes.
* <li> The second step is the computation of the error-locator polynomial sigma.
* <li> 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.
* <li> The fourth step is the correction of the errors in the received polynomial.
* </ol>
* 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_HQC128_CLEAN_bch_code_decode(uint64_t *message, uint64_t *vector) {
uint16_t syndromes[1 << PARAM_FFT_T] = {0};
uint16_t sigma[1 << PARAM_FFT] = {0};
uint64_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_HQC128_CLEAN_vect_add(vector, vector, error, VEC_N1_SIZE_64);

// Retrieve the message from the decoded codeword
message_from_codeword(message, vector);

}

+ 0
- 18
crypto_kem/hqc-128/clean/bch.h View File

@@ -1,18 +0,0 @@
#ifndef BCH_H
#define BCH_H


/**
* @file bch.h
* Header file of bch.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_bch_code_encode(uint64_t *codeword, const uint64_t *message);

void PQCLEAN_HQC128_CLEAN_bch_code_decode(uint64_t *message, uint64_t *vector);


#endif

+ 0
- 49
crypto_kem/hqc-128/clean/code.c View File

@@ -1,49 +0,0 @@
#include "bch.h"
#include "code.h"
#include "parameters.h"
#include "repetition.h"
#include <stdint.h>
#include <string.h>
/**
* @file code.c
* @brief Implementation of tensor code
*/



/**
*
* @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_HQC128_CLEAN_code_encode(uint64_t *em, const uint64_t *m) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC128_CLEAN_bch_code_encode(tmp, m);
PQCLEAN_HQC128_CLEAN_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_HQC128_CLEAN_code_decode(uint64_t *m, const uint64_t *em) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC128_CLEAN_repetition_code_decode(tmp, em);
PQCLEAN_HQC128_CLEAN_bch_code_decode(m, tmp);


}

+ 0
- 18
crypto_kem/hqc-128/clean/code.h View File

@@ -1,18 +0,0 @@
#ifndef CODE_H
#define CODE_H


/**
* @file code.h
* Header file of code.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_code_encode(uint64_t *em, const uint64_t *message);

void PQCLEAN_HQC128_CLEAN_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 673
crypto_kem/hqc-128/clean/fft.c View File

@@ -1,673 +0,0 @@
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/**
* @file fft.c
* Implementation of the additive FFT and its transpose.
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf
*/


static void compute_fft_betas(uint16_t *betas);
static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, uint16_t set_size);
static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f);
static void radix_t_big(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 radix_big(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) {
size_t i;
for (i = 0; i < PARAM_M - 1; ++i) {
betas[i] = (uint16_t) (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, uint16_t set_size) {
uint16_t i, j;
subset_sums[0] = 0;

for (i = 0; i < set_size; ++i) {
for (j = 0; j < (1 << i); ++j) {
subset_sums[(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];
break;

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];
break;

case 2:
f[0] = f0[0];
f[1] = f1[0];
f[2] = f0[1] ^ f1[0];
f[3] = f[2] ^ f1[1];
break;

case 1:
f[0] = f0[0];
f[1] = f1[0];
break;

default:
radix_t_big(f, f0, f1, m_f);
break;
}
}

static void radix_t_big(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) {
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};

uint16_t n;
size_t i;

n = 1;
n <<= m_f - 2;
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 (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 PQCLEAN_HQC128_CLEAN_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) {
uint16_t gammas[PARAM_M - 2] = {0};
uint16_t deltas[PARAM_M - 2] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
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};
uint16_t v[1 << (PARAM_M - 2)] = {0};
uint16_t beta_m_pow;

size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
f[0] = 0;
x = 1;
x <<= m;
for (i = 0; i < x; ++i) {
f[0] ^= w[i];
}
f[1] = 0;

betas_sums[0] = 0;
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
betas_sums[x + k] = betas_sums[k] ^ betas[j];
f[1] ^= PQCLEAN_HQC128_CLEAN_gf_mul(betas_sums[x + k], w[x + k]);
}
x <<= 1;
}

return;
}

// Compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC128_CLEAN_gf_mul(betas[i], PQCLEAN_HQC128_CLEAN_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC128_CLEAN_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] */
k = 1;
k <<= (m - 1) & 0xf; // &0xf is to let the compiler know that m-1 is small.
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 (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
f1[0] ^= PQCLEAN_HQC128_CLEAN_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 {
u[0] = w[0] ^ w[k];
v[0] = w[k];

for (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
v[i] = PQCLEAN_HQC128_CLEAN_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) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC128_CLEAN_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC128_CLEAN_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(...). <br>
* 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_HQC128_CLEAN_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] = {0};
uint16_t betas_sums[1 << (PARAM_M - 1)] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};
uint16_t deltas[PARAM_M - 1] = {0};
uint16_t f0[1 << (PARAM_FFT_T - 1)] = {0};
uint16_t f1[1 << (PARAM_FFT_T - 1)] = {0};

size_t i, k;

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] */
k = 1;
k <<= PARAM_M - 1;
u[0] = w[0] ^ w[k];
v[0] = w[k];
for (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
v[i] = PQCLEAN_HQC128_CLEAN_gf_mul(betas_sums[i], u[i]) ^ w[k + i];
}

// Compute deltas
for (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC128_CLEAN_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];
break;

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];
break;

case 2:
f0[0] = f[0];
f0[1] = f[2] ^ f[3];
f1[0] = f[1] ^ f0[1];
f1[1] = f[3];
break;

case 1:
f0[0] = f[0];
f1[0] = f[1];
break;

default:
radix_big(f0, f1, f, m_f);
break;
}
}

static void radix_big(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) {
uint16_t Q[2 * (1 << (PARAM_FFT - 2))] = {0};
uint16_t R[2 * (1 << (PARAM_FFT - 2))] = {0};

uint16_t Q0[1 << (PARAM_FFT - 2)] = {0};
uint16_t Q1[1 << (PARAM_FFT - 2)] = {0};
uint16_t R0[1 << (PARAM_FFT - 2)] = {0};
uint16_t R1[1 << (PARAM_FFT - 2)] = {0};

size_t i, n;

n = 1;
n <<= m_f - 2;
memcpy(Q, f + 3 * n, 2 * n);
memcpy(Q + n, f + 3 * n, 2 * n);
memcpy(R, f, 4 * n);

for (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 PQCLEAN_HQC128_CLEAN_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};
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};
uint16_t tmp[PARAM_M - (PARAM_FFT - 1)] = {0};

uint16_t beta_m_pow;
size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
for (i = 0; i < m; ++i) {
tmp[i] = PQCLEAN_HQC128_CLEAN_gf_mul(betas[i], f[1]);
}

w[0] = f[0];
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
w[x + k] = w[k] ^ tmp[j];
}
x <<= 1;
}

return;
}

// Step 2: compute g
if (betas[m - 1] != 1) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC128_CLEAN_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC128_CLEAN_gf_mul(beta_m_pow, f[i]);
}
}

// Step 3
radix(f0, f1, f, m_f);

// Step 4: compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC128_CLEAN_gf_mul(betas[i], PQCLEAN_HQC128_CLEAN_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC128_CLEAN_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);

k = 1;
k <<= (m - 1) & 0xf; // &0xf is to let the compiler know that m-1 is small.
if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant
w[0] = u[0];
w[k] = u[0] ^ f1[0];
for (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_CLEAN_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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_CLEAN_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). <br>
* The FFT proceeds recursively to evaluate f at all subset sums of a basis B. <br>
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf <br>
* 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). <br>
* 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_HQC128_CLEAN_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] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};

size_t i, k;

// 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 (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC128_CLEAN_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);

k = 1;
k <<= PARAM_M - 1;
// 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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC128_CLEAN_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. <br>
* 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_HQC128_CLEAN_fft_t_preprocess_bch_codeword(uint16_t *w, const uint64_t *vector) {
uint16_t r[1 << PARAM_M] = {0};
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
size_t i, j, k;

// Unpack the received word vector into array r
for (i = 0; i < VEC_N1_SIZE_64 - (PARAM_N1 % 64 != 0); ++i) {
for (j = 0; j < 64; ++j) {
r[64 * i + j] = (uint8_t) ((vector[i] >> j) & 1);
}
}

// Last byte
for (j = 0; j < PARAM_N1 % 64; ++j) {
r[64 * i + j] = (uint8_t) ((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
k = 1;
k <<= PARAM_M - 1;
w[0] = 0;
w[k] = -r[0] & 1;
for (i = 1; i < k; ++i) {
w[i] = -r[gf_log[gammas_sums[i]]] & gammas_sums[i];
w[k + i] = -r[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_HQC128_CLEAN_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w) {
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
uint64_t bit;
uint16_t k;
size_t i, index;

compute_fft_betas(gammas);
compute_subset_sums(gammas_sums, gammas, PARAM_M - 1);

error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15);

k = 1;
k <<= PARAM_M - 1;
index = PARAM_GF_MUL_ORDER;
bit = 1 ^ ((uint16_t) - w[k] >> 15);
error[index / 8] ^= bit << (index % 64);

for (i = 1; i < k; ++i) {
index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i]];
bit = 1 ^ ((uint16_t) - w[i] >> 15);
error[index / 64] ^= bit << (index % 64);

index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i] ^ 1];
bit = 1 ^ ((uint16_t) - w[k + i] >> 15);
error[index / 64] ^= bit << (index % 64);
}
}

+ 0
- 23
crypto_kem/hqc-128/clean/fft.h View File

@@ -1,23 +0,0 @@
#ifndef FFT_H
#define FFT_H


/**
* @file fft.h
* Header file of fft.c
*/

#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs);

void PQCLEAN_HQC128_CLEAN_fft_t_preprocess_bch_codeword(uint16_t *w, const uint64_t *vector);


void PQCLEAN_HQC128_CLEAN_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs);

void PQCLEAN_HQC128_CLEAN_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w);


#endif

+ 0
- 63
crypto_kem/hqc-128/clean/gf.c View File

@@ -1,63 +0,0 @@
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
/**
* @file gf.c
* Galois field implementation with multiplication using lookup tables
*/


/**
* @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_HQC128_CLEAN_gf_mul(uint16_t a, uint16_t b) {
uint16_t mask;
mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
mask &= (uint16_t) (-((int32_t) b) >> 31); // b != 0
return mask & gf_exp[PQCLEAN_HQC128_CLEAN_gf_mod(gf_log[a] + gf_log[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_HQC128_CLEAN_gf_square(uint16_t a) {
int16_t mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
return mask & gf_exp[PQCLEAN_HQC128_CLEAN_gf_mod(2 * gf_log[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_HQC128_CLEAN_gf_inverse(uint16_t a) {
int16_t mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
return mask & gf_exp[PARAM_GF_MUL_ORDER - gf_log[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_HQC128_CLEAN_gf_mod(uint16_t i) {
uint16_t tmp = (uint16_t) (i - PARAM_GF_MUL_ORDER);

// mask = 0xffff if(i < PARAM_GF_MUL_ORDER)
uint16_t mask = -(tmp >> 15);

return tmp + (mask & PARAM_GF_MUL_ORDER);
}

+ 0
- 38
crypto_kem/hqc-128/clean/gf.h
File diff suppressed because it is too large
View File


+ 0
- 154
crypto_kem/hqc-128/clean/gf2x.c View File

@@ -1,154 +0,0 @@
#include "gf2x.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include <stdint.h>
/**
* \file gf2x.c
* \brief Implementation of multiplication of two polynomials
*/


static inline void swap(uint16_t *tab, uint16_t elt1, uint16_t elt2);
static void reduce(uint64_t *o, const uint64_t *a);
static void fast_convolution_mult(uint8_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx);

/**
* @brief swap two elements in a table
*
* This function exchanges tab[elt1] with tab[elt2]
*
* @param[in] tab Pointer to the table
* @param[in] elt1 Index of the first element
* @param[in] elt2 Index of the second element
*/
static inline void swap(uint16_t *tab, uint16_t elt1, uint16_t elt2) {
uint16_t tmp = tab[elt1];

tab[elt1] = tab[elt2];
tab[elt2] = tmp;
}



/**
* @brief Compute o(x) = a(x) mod \f$ X^n - 1\f$
*
* This function computes the modular reduction of the polynomial a(x)
*
* @param[in] a Pointer to the polynomial a(x)
* @param[out] o Pointer to the result
*/
static void reduce(uint64_t *o, const uint64_t *a) {
size_t i;
uint64_t r;
uint64_t carry;

for (i = 0; i < VEC_N_SIZE_64; i++) {
r = a[i + VEC_N_SIZE_64 - 1] >> (PARAM_N & 63);
carry = (uint64_t) (a[i + VEC_N_SIZE_64] << (64 - (PARAM_N & 63)));
o[i] = a[i] ^ r ^ carry;
}

o[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @brief computes product of the polynomial a1(x) with the sparse polynomial a2
*
* o(x) = a1(x)a2(x)
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to the sparse polynomial a2 (list of degrees of the monomials which appear in a2)
* @param[in] a2 Pointer to the polynomial a1(x)
* @param[in] weight Hamming wifht of the sparse polynomial a2
* @param[in] ctx Pointer to a seed expander used to randomize the multiplication process
*/
static void fast_convolution_mult(uint8_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx) {
//static uint32_t fast_convolution_mult(const uint64_t *A, const uint32_t *vB, uint64_t *C, const uint16_t w, AES_XOF_struct *ctx)
uint64_t carry;
uint32_t dec, s;
uint64_t table[16 * (VEC_N_SIZE_64 + 1)];
uint16_t permuted_table[16];
uint16_t permutation_table[16];
uint16_t permuted_sparse_vect[PARAM_OMEGA_E];
uint16_t permutation_sparse_vect[PARAM_OMEGA_E];
uint64_t tmp;
uint64_t *pt;
uint8_t *res;
size_t i, j;

for (i = 0; i < 16; i++) {
permuted_table[i] = (uint16_t) i;
}

seedexpander(ctx, (uint8_t *) permutation_table, 16 * sizeof(uint16_t));

for (i = 0; i < 15; i++) {
swap(permuted_table + i, 0, permutation_table[i] % (16 - i));
}

pt = table + (permuted_table[0] * (VEC_N_SIZE_64 + 1));
for (j = 0; j < VEC_N_SIZE_64; j++) {
pt[j] = a2[j];
}
pt[VEC_N_SIZE_64] = 0x0;

for (i = 1; i < 16; i++) {
carry = 0;
pt = table + (permuted_table[i] * (VEC_N_SIZE_64 + 1));
for (j = 0; j < VEC_N_SIZE_64; j++) {
pt[j] = (a2[j] << i) ^ carry;
carry = (a2[j] >> ((64 - i)));
}
pt[VEC_N_SIZE_64] = carry;
}

for (i = 0; i < weight; i++) {
permuted_sparse_vect[i] = (uint16_t) i;
}

seedexpander(ctx, (uint8_t *) permutation_sparse_vect, weight * sizeof(uint16_t));

for (i = 0; i + 1 < weight; i++) {
swap(permuted_sparse_vect + i, 0, (uint16_t) (permutation_sparse_vect[i] % (weight - i)));
}

for (i = 0; i < weight; i++) {
dec = a1[permuted_sparse_vect[i]] & 0xf;
s = a1[permuted_sparse_vect[i]] >> 4;
res = o + 2 * s;
pt = table + (permuted_table[dec] * (VEC_N_SIZE_64 + 1));

for (j = 0; j < VEC_N_SIZE_64 + 1; j++) {
tmp = PQCLEAN_HQC128_CLEAN_load8(res);
PQCLEAN_HQC128_CLEAN_store8(res, tmp ^ pt[j]);
res += 8;
}
}
}



/**
* @brief Multiply two polynomials modulo \f$ X^n - 1\f$.
*
* This functions multiplies a sparse polynomial <b>a1</b> (of Hamming weight equal to <b>weight</b>)
* and a dense polynomial <b>a2</b>. The multiplication is done modulo \f$ X^n - 1\f$.
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to the sparse polynomial
* @param[in] a2 Pointer to the dense polynomial
* @param[in] weight Integer that is the weigt of the sparse polynomial
* @param[in] ctx Pointer to the randomness context
*/
void PQCLEAN_HQC128_CLEAN_vect_mul(uint64_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx) {
uint64_t tmp[2 * VEC_N_SIZE_64 + 1] = {0};

fast_convolution_mult((uint8_t *) tmp, a1, a2, weight, ctx);
PQCLEAN_HQC128_CLEAN_load8_arr(tmp, 2 * VEC_N_SIZE_64 + 1, (uint8_t *) tmp, sizeof(tmp));
reduce(o, tmp);
}

+ 0
- 16
crypto_kem/hqc-128/clean/gf2x.h View File

@@ -1,16 +0,0 @@
#ifndef GF2X_H
#define GF2X_H


/**
* @file gf2x.h
* @brief Header file for gf2x.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_vect_mul(uint64_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx);


#endif

+ 0
- 143
crypto_kem/hqc-128/clean/hqc.c View File

@@ -1,143 +0,0 @@
#include "code.h"
#include "gf2x.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
/**
* @file hqc.c
* @brief Implementation of hqc.h
*/



/**
* @brief Keygen of the HQC_PKE IND_CPA scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the <b>seed</b> used to generate the vector <b>h</b>.
*
* The secret key is composed of the <b>seed</b> used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_CLEAN_hqc_pke_keygen(unsigned char *pk, unsigned char *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};
uint64_t x[VEC_N_SIZE_64] = {0};
uint32_t y[PARAM_OMEGA] = {0};
uint64_t h[VEC_N_SIZE_64] = {0};
uint64_t s[VEC_N_SIZE_64] = {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_HQC128_CLEAN_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA);

// Compute public key
PQCLEAN_HQC128_CLEAN_vect_set_random(&pk_seedexpander, h);
PQCLEAN_HQC128_CLEAN_vect_mul(s, y, h, PARAM_OMEGA, &sk_seedexpander);
PQCLEAN_HQC128_CLEAN_vect_add(s, x, s, VEC_N_SIZE_64);

// Parse keys to string
PQCLEAN_HQC128_CLEAN_hqc_public_key_to_string(pk, pk_seed, s);
PQCLEAN_HQC128_CLEAN_hqc_secret_key_to_string(sk, sk_seed, pk);

}



/**
* @brief Encryption of the HQC_PKE IND_CPA scheme
*
* The cihertext is composed of vectors <b>u</b> and <b>v</b>.
*
* @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_HQC128_CLEAN_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk) {
AES_XOF_struct seedexpander;
uint64_t h[VEC_N_SIZE_64] = {0};
uint64_t s[VEC_N_SIZE_64] = {0};
uint64_t r1[VEC_N_SIZE_64] = {0};
uint32_t r2[PARAM_OMEGA_R] = {0};
uint64_t e[VEC_N_SIZE_64] = {0};
uint64_t tmp1[VEC_N_SIZE_64] = {0};
uint64_t tmp2[VEC_N_SIZE_64] = {0};

// Create seed_expander from theta
seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH);

// Retrieve h and s from public key
PQCLEAN_HQC128_CLEAN_hqc_public_key_from_string(h, s, pk);

// Generate r1, r2 and e
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R);
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R);
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E);

// Compute u = r1 + r2.h
PQCLEAN_HQC128_CLEAN_vect_mul(u, r2, h, PARAM_OMEGA_R, &seedexpander);
PQCLEAN_HQC128_CLEAN_vect_add(u, r1, u, VEC_N_SIZE_64);

// Compute v = m.G by encoding the message
PQCLEAN_HQC128_CLEAN_code_encode(v, m);
PQCLEAN_HQC128_CLEAN_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);

// Compute v = m.G + s.r2 + e
PQCLEAN_HQC128_CLEAN_vect_mul(tmp2, r2, s, PARAM_OMEGA_R, &seedexpander);
PQCLEAN_HQC128_CLEAN_vect_add(tmp2, e, tmp2, VEC_N_SIZE_64);
PQCLEAN_HQC128_CLEAN_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_64);
PQCLEAN_HQC128_CLEAN_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_HQC128_CLEAN_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk) {
uint64_t x[VEC_N_SIZE_64] = {0};
uint32_t y[PARAM_OMEGA] = {0};
uint8_t pk[PUBLIC_KEY_BYTES] = {0};
uint64_t tmp1[VEC_N_SIZE_64] = {0};
uint64_t tmp2[VEC_N_SIZE_64] = {0};
AES_XOF_struct perm_seedexpander;
uint8_t perm_seed[SEED_BYTES] = {0};

// Retrieve x, y, pk from secret key
PQCLEAN_HQC128_CLEAN_hqc_secret_key_from_string(x, y, pk, sk);

randombytes(perm_seed, SEED_BYTES);
seedexpander_init(&perm_seedexpander, perm_seed, perm_seed + 32, SEEDEXPANDER_MAX_LENGTH);

// Compute v - u.y
PQCLEAN_HQC128_CLEAN_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);
PQCLEAN_HQC128_CLEAN_vect_mul(tmp2, y, u, PARAM_OMEGA, &perm_seedexpander);
PQCLEAN_HQC128_CLEAN_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_64);


// Compute m by decoding v - u.y
PQCLEAN_HQC128_CLEAN_code_decode(m, tmp2);
}

+ 0
- 19
crypto_kem/hqc-128/clean/hqc.h View File

@@ -1,19 +0,0 @@
#ifndef HQC_H
#define HQC_H


/**
* @file hqc.h
* @brief Functions of the HQC_PKE IND_CPA scheme
*/

#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_hqc_pke_keygen(unsigned char *pk, unsigned char *sk);

void PQCLEAN_HQC128_CLEAN_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk);

void PQCLEAN_HQC128_CLEAN_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk);


#endif

+ 0
- 144
crypto_kem/hqc-128/clean/kem.c View File

@@ -1,144 +0,0 @@
#include "api.h"
#include "fips202.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "sha2.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file kem.c
* @brief Implementation of api.h
*/



/**
* @brief Keygen of the HQC_KEM IND_CAA2 scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>.
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {

PQCLEAN_HQC128_CLEAN_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_HQC128_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) {

uint8_t theta[SHA512_BYTES] = {0};
uint8_t m_bytes[VEC_K_SIZE_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint64_t u[VEC_N_SIZE_64] = {0};
uint64_t v[VEC_N1N2_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Computing m
randombytes(m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC128_CLEAN_load8_arr(m, VEC_K_SIZE_64, m_bytes, VEC_K_SIZE_BYTES);

// Computing theta
sha3_512(theta, m_bytes, VEC_K_SIZE_BYTES);

// Encrypting m
PQCLEAN_HQC128_CLEAN_hqc_pke_encrypt(u, v, m, theta, pk);

// Computing d
sha512(d, m_bytes, VEC_K_SIZE_BYTES);

// Computing shared secret
memcpy(mc, m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC128_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC128_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Computing ciphertext
PQCLEAN_HQC128_CLEAN_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_HQC128_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) {

uint8_t result;
uint64_t u[VEC_N_SIZE_64] = {0};
uint64_t v[VEC_N1N2_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char pk[PUBLIC_KEY_BYTES] = {0};
uint8_t m_bytes[VEC_K_SIZE_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint8_t theta[SHA512_BYTES] = {0};
uint64_t u2[VEC_N_SIZE_64] = {0};
uint64_t v2[VEC_N1N2_SIZE_64] = {0};
unsigned char d2[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Retrieving u, v and d from ciphertext
PQCLEAN_HQC128_CLEAN_hqc_ciphertext_from_string(u, v, d, ct);

// Retrieving pk from sk
memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES);

// Decryting
PQCLEAN_HQC128_CLEAN_hqc_pke_decrypt(m, u, v, sk);
PQCLEAN_HQC128_CLEAN_store8_arr(m_bytes, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);

// Computing theta
sha3_512(theta, m_bytes, VEC_K_SIZE_BYTES);

// Encrypting m'
PQCLEAN_HQC128_CLEAN_hqc_pke_encrypt(u2, v2, m, theta, pk);

// Computing d'
sha512(d2, m_bytes, VEC_K_SIZE_BYTES);

// Computing shared secret
memcpy(mc, m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC128_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC128_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Abort if c != c' or d != d'
result = PQCLEAN_HQC128_CLEAN_vect_compare((uint8_t *)u, (uint8_t *)u2, VEC_N_SIZE_BYTES);
result |= PQCLEAN_HQC128_CLEAN_vect_compare((uint8_t *)v, (uint8_t *)v2, VEC_N1N2_SIZE_BYTES);
result |= PQCLEAN_HQC128_CLEAN_vect_compare(d, d2, SHA512_BYTES);
result = (uint8_t) (-((int16_t) result) >> 15);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
ss[i] &= ~result;
}


return -(result & 1);
}

+ 0
- 120
crypto_kem/hqc-128/clean/parameters.h View File

@@ -1,120 +0,0 @@
#ifndef HQC_PARAMETERS_H
#define HQC_PARAMETERS_H
/**
* @file parameters.h
* @brief Parameters of the HQC_KEM IND-CCA2 scheme
*/
#include "api.h"
#include "vector.h"


#define CEIL_DIVIDE(a, b) (((a)+(b)-1)/(b)) /*!< Divide a by b and ceil the result*/

/*
#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_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes
#define VEC_K_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes
#define VEC_N1_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes
#define VEC_N1N2_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes

#define VEC_N_SIZE_64 Define the size of the array used to store a PARAM_N sized vector in 64 bits
#define VEC_K_SIZE_64 Define the size of the array used to store a PARAM_K sized vector in 64 bits
#define VEC_N1_SIZE_64 Define the size of the array used to store a PARAM_N1 sized vector in 64 bits
#define VEC_N1N2_SIZE_64 Define the size of the array used to store a PARAM_N1N2 sized vector in 64 bits

#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_POLY Generator polynomial of galois field GF(2^PARAM_M), represented in hexadecimial form
#define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^PARAM_M), i.e 2^PARAM_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 RED_MASK A mask fot the higher bits of a vector
#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 23869
#define PARAM_N1 766
#define PARAM_N2 31
#define PARAM_N1N2 23746
#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_HQC128_CLEAN_CRYPTO_SECRETKEYBYTES
#define PUBLIC_KEY_BYTES PQCLEAN_HQC128_CLEAN_CRYPTO_PUBLICKEYBYTES
#define SHARED_SECRET_BYTES PQCLEAN_HQC128_CLEAN_CRYPTO_BYTES
#define CIPHERTEXT_BYTES PQCLEAN_HQC128_CLEAN_CRYPTO_CIPHERTEXTBYTES

#define UTILS_REJECTION_THRESHOLD 16756038
#define VEC_N_SIZE_BYTES CEIL_DIVIDE(PARAM_N, 8)
#define VEC_K_SIZE_BYTES CEIL_DIVIDE(PARAM_K, 8)
#define VEC_N1_SIZE_BYTES CEIL_DIVIDE(PARAM_N1, 8)
#define VEC_N1N2_SIZE_BYTES CEIL_DIVIDE(PARAM_N1N2, 8)

#define VEC_N_SIZE_64 CEIL_DIVIDE(PARAM_N, 64)
#define VEC_K_SIZE_64 CEIL_DIVIDE(PARAM_K, 64)
#define VEC_N1_SIZE_64 CEIL_DIVIDE(PARAM_N1, 64)
#define VEC_N1N2_SIZE_64 CEIL_DIVIDE(PARAM_N1N2, 64)

#define PARAM_T 15

#define PARAM_DELTA 57
#define PARAM_M 10
#define PARAM_GF_POLY 0x409
#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 RED_MASK 0x1fffffffffffffffUL
#define SHA512_BYTES 64
#define SEED_BYTES 40
#define SEEDEXPANDER_MAX_LENGTH 4294967295

#endif

+ 0
- 186
crypto_kem/hqc-128/clean/parsing.c View File

@@ -1,186 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file parsing.c
* @brief Functions to parse secret key, public key and ciphertext of the HQC scheme
*/


void PQCLEAN_HQC128_CLEAN_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_HQC128_CLEAN_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_HQC128_CLEAN_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_HQC128_CLEAN_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_HQC128_CLEAN_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++;
}
}
}


/**
* @brief Parse a secret key into a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC128_CLEAN_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) {
memcpy(sk, sk_seed, SEED_BYTES);
sk += SEED_BYTES;
memcpy(sk, pk, PUBLIC_KEY_BYTES);
}

/**
* @brief Parse a secret key from a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* As technicality, the public key is appended to the secret key in order to respect NIST API.
*
* @param[out] x uint64_t representation of vector x
* @param[out] y uint32_t representation of vector y
* @param[out] pk String containing the public key
* @param[in] sk String containing the secret key
*/
void PQCLEAN_HQC128_CLEAN_hqc_secret_key_from_string(uint64_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);
sk += SEED_BYTES;
memcpy(pk, sk, PUBLIC_KEY_BYTES);

seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA);
}

/**
* @brief Parse a public key into a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC128_CLEAN_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s) {
memcpy(pk, pk_seed, SEED_BYTES);
PQCLEAN_HQC128_CLEAN_store8_arr(pk + SEED_BYTES, VEC_N_SIZE_BYTES, s, VEC_N_SIZE_64);
}



/**
* @brief Parse a public key from a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC128_CLEAN_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk) {
AES_XOF_struct pk_seedexpander;
uint8_t pk_seed[SEED_BYTES] = {0};

memcpy(pk_seed, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_HQC128_CLEAN_load8_arr(s, VEC_N_SIZE_64, pk, VEC_N_SIZE_BYTES);

seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC128_CLEAN_vect_set_random(&pk_seedexpander, h);
}


/**
* @brief Parse a ciphertext into a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC128_CLEAN_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d) {
PQCLEAN_HQC128_CLEAN_store8_arr(ct, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC128_CLEAN_store8_arr(ct, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(ct, d, SHA512_BYTES);
}


/**
* @brief Parse a ciphertext from a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC128_CLEAN_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct) {
PQCLEAN_HQC128_CLEAN_load8_arr(u, VEC_N_SIZE_64, ct, VEC_N_SIZE_BYTES);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC128_CLEAN_load8_arr(v, VEC_N1N2_SIZE_64, ct, VEC_N1N2_SIZE_BYTES);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(d, ct, SHA512_BYTES);
}

+ 0
- 36
crypto_kem/hqc-128/clean/parsing.h View File

@@ -1,36 +0,0 @@
#ifndef PARSING_H
#define PARSING_H


/**
* @file parsing.h
* @brief Header file for parsing.c
*/

#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_store8(unsigned char *out, uint64_t in);

uint64_t PQCLEAN_HQC128_CLEAN_load8(const unsigned char *in);

void PQCLEAN_HQC128_CLEAN_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen);

void PQCLEAN_HQC128_CLEAN_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen);


void PQCLEAN_HQC128_CLEAN_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk);

void PQCLEAN_HQC128_CLEAN_hqc_secret_key_from_string(uint64_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk);


void PQCLEAN_HQC128_CLEAN_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s);

void PQCLEAN_HQC128_CLEAN_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk);


void PQCLEAN_HQC128_CLEAN_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d);

void PQCLEAN_HQC128_CLEAN_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct);


#endif

+ 0
- 85
crypto_kem/hqc-128/clean/repetition.c View File

@@ -1,85 +0,0 @@
#include "parameters.h"
#include "repetition.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/**
* @file repetition.c
* @brief Implementation of repetition codes
*/

#define MASK_N2 ((((uint64_t) 1) << PARAM_N2) - 1)

static inline int32_t popcount(uint64_t n);

/**
* @brief Encoding each bit in the message m using the repetition code
*
*
* @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_HQC128_CLEAN_repetition_code_encode(uint64_t *em, const uint64_t *m) {
uint64_t bit, idx_r;
size_t pos_r;

pos_r = 0;
for (size_t i = 0; i < VEC_N1_SIZE_64; i++) {
for (size_t j = 0; j < 64 && pos_r < PARAM_N1N2; j++) {
bit = (uint64_t) (-(int64_t) ((m[i] >> j) & 1));
idx_r = (pos_r & 0x3f);
em[(pos_r >> 6) + 0] ^= (bit & 0x7FFFFFFFUL) << idx_r;
em[(pos_r >> 6) + 1] ^= (bit & 0x3FFFFFFFUL) >> ((63 - idx_r));
pos_r += PARAM_N2;
}
}
}



/**
* @brief Compute the Hamming weight of the 64-bit integer n
*
* The Hamming weight is computed using a trick described in
* Henry S. Warren : "Hacker's Delight", chap 5., p. 66
* @param[out] the Hamming weight of n
* @param[in] a 64-bit integer n
*/
static inline int32_t popcount(uint64_t n) {
n -= (n >> 1) & 0x5555555555555555UL;
n = (n & 0x3333333333333333UL) + ((n >> 2) & 0x3333333333333333UL);
n = (n + (n >> 4)) & 0x0f0f0f0f0f0f0f0fUL;
return (n * 0x0101010101010101UL) >> 56;
}



/**
* @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_HQC128_CLEAN_repetition_code_decode(uint64_t *m, const uint64_t *em) {
size_t t = 0;
uint32_t b, bn, bi, c, cn, ci;
uint64_t cx, ones;
uint64_t mask;

for (b = 0; b < PARAM_N1N2 - PARAM_N2 + 1; b += PARAM_N2) {
bn = b >> 6;
bi = b & 63;
c = b + PARAM_N2 - 1;
cn = c >> 6;
ci = c & 63;
cx = em[cn] << (63 - ci);
mask = (uint64_t) (-((int64_t) (cn ^ (bn + 1))) >> 63); // cn != bn+1
ones = popcount(((em[bn] >> bi) & MASK_N2) | (cx & ~mask));
m[t >> 6] |= (uint64_t) ((((PARAM_T - ones) >> 31) & 1) << (t & 63));
t++;
}
}

+ 0
- 17
crypto_kem/hqc-128/clean/repetition.h View File

@@ -1,17 +0,0 @@
#ifndef REPETITION_H
#define REPETITION_H


/**
* @file repetition.h
* @brief Header file for repetition.c
*/

#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_repetition_code_encode(uint64_t *em, const uint64_t *m);

void PQCLEAN_HQC128_CLEAN_repetition_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 176
crypto_kem/hqc-128/clean/vector.c View File

@@ -1,176 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file vector.c
* @brief Implementation of vectors sampling and some utilities for the HQC scheme
*/


/**
* @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 <b>weight</b>. 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_HQC128_CLEAN_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
uint8_t inc;
size_t i, j;

i = 0;
j = random_bytes_size;
while (i < weight) {
do {
if (j == random_bytes_size) {
seedexpander(ctx, rand_bytes, random_bytes_size);
j = 0;
}

v[i] = ((uint32_t) rand_bytes[j++]) << 16;
v[i] |= ((uint32_t) rand_bytes[j++]) << 8;
v[i] |= rand_bytes[j++];

} while (v[i] >= UTILS_REJECTION_THRESHOLD);

v[i] = v[i] % PARAM_N;

inc = 1;
for (size_t k = 0; k < i; k++) {
if (v[k] == v[i]) {
inc = 0;
}
}
i += inc;
}
}



/**
* @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 <b>weight</b>.
* 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_HQC128_CLEAN_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight) {
uint32_t tmp[PARAM_OMEGA_R] = {0};

PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight_by_coordinates(ctx, tmp, weight);

for (size_t i = 0; i < weight; ++i) {
int32_t index = tmp[i] / 64;
int32_t pos = tmp[i] % 64;
v[index] |= ((uint64_t) 1) << pos;
}
}



/**
* @brief Generates a random vector of dimension <b>PARAM_N</b>
*
* This function generates a random binary vector of dimension <b>PARAM_N</b>. 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_HQC128_CLEAN_vect_set_random(AES_XOF_struct *ctx, uint64_t *v) {
uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0};

seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES);

PQCLEAN_HQC128_CLEAN_load8_arr(v, VEC_N_SIZE_64, rand_bytes, VEC_N_SIZE_BYTES);
v[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @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_HQC128_CLEAN_vect_add(uint64_t *o, const uint64_t *v1, const uint64_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
*/
uint8_t PQCLEAN_HQC128_CLEAN_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) {
uint64_t r = 0;
for (size_t i = 0; i < size; i++) {
r |= v1[i] ^ v2[i];
}
r = (~r + 1) >> 63;
return (uint8_t) r;
}



/**
* @brief Resize a vector so that it contains <b>size_o</b> 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_HQC128_CLEAN_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v) {
if (size_o < size_v) {
uint64_t mask = 0x7FFFFFFFFFFFFFFF;
int8_t val = 0;

if (size_o % 64) {
val = 64 - (size_o % 64);
}

memcpy(o, v, 8 * VEC_N1N2_SIZE_64);

for (int8_t i = 0; i < val; ++i) {
o[VEC_N1N2_SIZE_64 - 1] &= (mask >> i);
}
} else {
memcpy(o, v, 8 * CEIL_DIVIDE(size_v, 64));
}
}

+ 0
- 27
crypto_kem/hqc-128/clean/vector.h View File

@@ -1,27 +0,0 @@
#ifndef VECTOR_H
#define VECTOR_H


/**
* @file vector.h
* @brief Header file for vector.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight);

void PQCLEAN_HQC128_CLEAN_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight);

void PQCLEAN_HQC128_CLEAN_vect_set_random(AES_XOF_struct *ctx, uint64_t *v);


void PQCLEAN_HQC128_CLEAN_vect_add(uint64_t *o, const uint64_t *v1, const uint64_t *v2, uint32_t size);

uint8_t PQCLEAN_HQC128_CLEAN_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size);

void PQCLEAN_HQC128_CLEAN_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v);


#endif

+ 0
- 36
crypto_kem/hqc-192/META.yml View File

@@ -1,36 +0,0 @@
name: HQC-192
type: kem
claimed-nist-level: 3
claimed-security: IND-CCA2
length-ciphertext: 11364
length-public-key: 5690
length-secret-key: 5730
length-shared-secret: 64
nistkat-sha256: b49351ae5bdab016521254af85a0df2072b81841722c0c422bb44af22cec4418
principal-submitters:
- Carlos Aguilar Melchor
- Nicolas Aragon
- Slim Bettaieb
- Olivier Blazy
- Jurjen Bos
- Jean-Christophe Deneuville
- Philippe Gaborit
- Edoardo Persichetti
- Jean-Marc Robert
- Pascal Véron
- Gilles Zémor
- Loïc Bidoux
implementations:
- name: clean
version: hqc-submission_2020-05-29 via https://github.com/jschanck/package-pqclean/tree/8a81b2c9/hqc
- name: avx2
version: hqc-submission_2020-05-29 via https://github.com/jschanck/package-pqclean/tree/8a81b2c9/hqc
supported_platforms:
- architecture: x86_64
operating_systems:
- Linux
- Darwin
required_flags:
- avx2
- bmi1
- pclmulqdq

+ 0
- 1
crypto_kem/hqc-192/avx2/LICENSE View File

@@ -1 +0,0 @@
Public Domain

+ 0
- 22
crypto_kem/hqc-192/avx2/Makefile View File

@@ -1,22 +0,0 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libhqc-192_avx2.a
HEADERS=alpha_table.h api.h bch.h code.h fft.h gen_matrix.h gf2x.h gf.h hqc.h parameters.h parsing.h repetition.h vector.h
OBJECTS=bch.o code.o fft.o gf2x.o gf.o hqc.o kem.o parsing.o repetition.o vector.o

CFLAGS=-O3 -mavx2 -mbmi -mpclmul -Wall -Wextra -Wshadow -Wpedantic -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.s $(HEADERS)
$(AS) -o $@ $<

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 0
- 16
crypto_kem/hqc-192/avx2/alpha_table.h
File diff suppressed because it is too large
View File


+ 0
- 25
crypto_kem/hqc-192/avx2/api.h View File

@@ -1,25 +0,0 @@
#ifndef PQCLEAN_HQC192_AVX2_API_H
#define PQCLEAN_HQC192_AVX2_API_H
/**
* @file api.h
* @brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme
*/

#define PQCLEAN_HQC192_AVX2_CRYPTO_ALGNAME "HQC-192"

#define PQCLEAN_HQC192_AVX2_CRYPTO_SECRETKEYBYTES 5730
#define PQCLEAN_HQC192_AVX2_CRYPTO_PUBLICKEYBYTES 5690
#define PQCLEAN_HQC192_AVX2_CRYPTO_BYTES 64
#define PQCLEAN_HQC192_AVX2_CRYPTO_CIPHERTEXTBYTES 11364

// As a technicality, the public key is appended to the secret key in order to respect the NIST API.
// Without this constraint, PQCLEAN_HQC192_AVX2_CRYPTO_SECRETKEYBYTES would be defined as 32

int PQCLEAN_HQC192_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk);

int PQCLEAN_HQC192_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);

int PQCLEAN_HQC192_AVX2_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);


#endif

+ 0
- 241
crypto_kem/hqc-192/avx2/bch.c View File

@@ -1,241 +0,0 @@
#include "alpha_table.h"
#include "bch.h"
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include "vector.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file bch.c
* Constant time implementation of BCH codes
*/


static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes);
static void message_from_codeword(uint64_t *message, const uint64_t *codeword);
static void compute_syndromes(__m256i *syndromes, const uint64_t *rcv);
static void compute_roots(uint64_t *error, const uint16_t *sigma);

/**
* @brief Computes the error locator polynomial (ELP) sigma
*
* This is a constant time implementation of Berlekamp's simplified algorithm (see @cite joiner1995decoding). <br>
* We use the letter p for rho which is initialized at -1/2. <br>
* The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X). <br>
* Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p. <br>
* sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated. <br>
* 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) {
uint16_t sigma_copy[PARAM_DELTA - 1] = {0};
uint16_t X_sigma_p[PARAM_DELTA + 1] = {0};
uint16_t d_p, d, dd;
uint16_t mask;
int32_t pp; // 2*rho
size_t deg_sigma, deg_sigma_p, deg_sigma_copy, deg_X_sigma_p;

d = syndromes[0];
sigma[0] = 1;
X_sigma_p[1] = 1;
deg_sigma = 0;
deg_sigma_p = 0;
d_p = 1;
pp = -1;
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;

dd = PQCLEAN_HQC192_AVX2_gf_mul(d, PQCLEAN_HQC192_AVX2_gf_inverse(d_p)); // 0 if(d == 0)
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
sigma[i] ^= PQCLEAN_HQC192_AVX2_gf_mul(dd, X_sigma_p[i]);
}

deg_X_sigma_p = 2 * mu - pp + deg_sigma_p;

// mask = 0xffff if(d != 0) and 0 otherwise
mask = -((uint16_t) - d >> 15);

// mask &= 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise
mask &= -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15);

deg_sigma ^= mask & (deg_sigma ^ deg_X_sigma_p);

if (mu == PARAM_DELTA - 1) {
break;
}

// Update pp, d_p and X_sigma_p if needed
pp ^= mask & (pp ^ (2 * mu));
d_p ^= mask & (d_p ^ d);
for (size_t i = PARAM_DELTA - 1; i; --i) {
X_sigma_p[i + 1] = X_sigma_p[i - 1];
X_sigma_p[i + 1] ^= mask & (X_sigma_p[i + 1] ^ sigma_copy[i - 1]);
}
X_sigma_p[1] = 0;
X_sigma_p[0] = 0;
deg_sigma_p ^= mask & (deg_sigma_p ^ deg_sigma_copy);

// Compute the next discrepancy
d = syndromes[2 * mu + 2];
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
d ^= PQCLEAN_HQC192_AVX2_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(uint64_t *message, const uint64_t *codeword) {
uint64_t mask1 = (uint64_t) (0xffffffffffffffff << ((PARAM_N1 - PARAM_K) % 64));
uint64_t mask2 = (uint64_t) (0xffffffffffffffff >> (64 - (PARAM_N1 - PARAM_K) % 64));
size_t index = (PARAM_N1 - PARAM_K) / 64;

for (size_t i = 0; i < VEC_K_SIZE_64 - 1; ++i) {
message[i] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
message[i] |= (codeword[++index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}

// Last byte (8-val % 8 is the number of bits given by message1)
message[VEC_K_SIZE_64 - 1] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
++index;
if (index < VEC_N1_SIZE_64) {
message[VEC_K_SIZE_64 - 1] |= (codeword[index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}
}


/**
* @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.
* These powers have been pre-computed in table_alphaPARAM_DELTA.h
* Syndromes are 16-bits long , hence we can simultaneously compute 16 syndromes
* in a 256-bit register
*
* @param[out] syndromes Array of size 2^(PARAM_FFT_T) receiving the 2*PARAM_DELTA syndromes
* @param[in] rcv Array of size VEC_N1_SIZE_BYTES storing the received word
*/
void compute_syndromes(__m256i *syndromes, const uint64_t *rcv) {
const __m256i zero_256 = _mm256_set1_epi64x(0);
const __m256i mask_one = _mm256_set_epi64x(0x0303030303030303, 0x0202020202020202, 0x0101010101010101, 0x0);
const __m256i mask_two = _mm256_set1_epi64x(-0x7FBFDFEFF7FBFDFF);
const __m256i un_256 = _mm256_set1_epi64x(1);

__m256i y;
__m256i S;
__m256i L;
__m256i tmp_repeat;
uint32_t *aux;
int16_t *alpha_tmp;
size_t i, j;
uint32_t nzflag;
// static variable so that it is stored in the DATA segment
// not in the STACK segment
static uint8_t tmp_array[PARAM_N1 + 4]; // +4 to control overflow due to management of 256 bits
__m256i *z = (__m256i *) tmp_array;
// vectorized version of the separation of the coordinates of the vector v in order to put each coordinate in an unsigned char
// aux is used to consider 4 elements in v at each step of the loop
aux = (uint32_t *) rcv;
for (i = 0; i < ((VEC_N1_SIZE_BYTES >> 2) << 2); i += 4) {
// duplicate aux 8 times in y , i.e y= (aux aux aux .... aux)
y = _mm256_set1_epi32(*aux);
// shuffle the bytes of y so that if aux=(a0 a1 a2 a3)
// then y = (a0 a0 a0 a0 a0 a0 a0 a0 a1 a1 a1 a1 a1 a1 a1 a1 .... a3)
y = _mm256_shuffle_epi8(y, mask_one);
// apply a mask on each byte of y to determine if jth bit of a_k is 0 or 1
z[i >> 2] = _mm256_and_si256(y, mask_two);
aux ++;
}

// Evaluation of the polynomial corresponding to the vector v in alpha^i for i in {1, ..., 2 * PARAM_DELTA}
for (j = 0; j < SYND_SIZE_256; ++j) {
S = zero_256;
alpha_tmp = table_alpha_ij + (j << 4);

for (i = 0; i < PARAM_N1; ++i) {
nzflag = ((-(int32_t) tmp_array[i]) >> 31) & 1;
tmp_repeat = _mm256_set1_epi64x(nzflag);
L = _mm256_cmpeq_epi64(tmp_repeat, un_256);
tmp_repeat = _mm256_lddqu_si256((__m256i *)(alpha_tmp + i * (PARAM_DELTA << 1)));
L = _mm256_and_si256(L, tmp_repeat);
S = _mm256_xor_si256(L, S);
}
_mm256_storeu_si256(syndromes + j, S);
}
}


/**
* @brief Computes the error polynomial error from the error locator polynomial sigma
*
* See function PQCLEAN_HQC192_AVX2_fft for more details.
*
* @param[out] error 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(uint64_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_HQC192_AVX2_fft(w, sigma, PARAM_DELTA + 1);
PQCLEAN_HQC192_AVX2_fft_retrieve_bch_error_poly(error, w);
}



/**
* @brief Decodes the received word
*
* This function relies on four steps:
* <ol>
* <li> The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes.
* <li> The second step is the computation of the error-locator polynomial sigma.
* <li> 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.
* <li> The fourth step is the correction of the errors in the received polynomial.
* </ol>
* 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_HQC192_AVX2_bch_code_decode(uint64_t *message, uint64_t *vector) {
uint16_t sigma[1 << PARAM_FFT] = {0};
uint64_t error[(1 << PARAM_M) / 8] = {0};
static __m256i syndromes_256[SYND_SIZE_256];

// Calculate the 2*PARAM_DELTA syndromes
compute_syndromes(syndromes_256, 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, (uint16_t *)syndromes_256);

// Compute the error polynomial error
compute_roots(error, sigma);

// Add the error polynomial to the received polynomial
PQCLEAN_HQC192_AVX2_vect_add(vector, vector, error, VEC_N1_SIZE_64);

// Retrieve the message from the decoded codeword
message_from_codeword(message, vector);

}

+ 0
- 16
crypto_kem/hqc-192/avx2/bch.h View File

@@ -1,16 +0,0 @@
#ifndef BCH_H
#define BCH_H


/**
* @file bch.h
* Header file of bch.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_AVX2_bch_code_decode(uint64_t *message, uint64_t *vector);


#endif

+ 0
- 105
crypto_kem/hqc-192/avx2/code.c View File

@@ -1,105 +0,0 @@
#include "bch.h"
#include "code.h"
#include "gen_matrix.h"
#include "parameters.h"
#include "repetition.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file code.c
* @brief Implementation of tensor code
*/


static inline uint64_t mux(uint64_t a, uint64_t b, int64_t bit);

static inline uint64_t mux(uint64_t a, uint64_t b, int64_t bit) {
uint64_t ret = a ^ b;
return (ret & (-bit >> 63)) ^ a;
}



/**
*
* @brief Encoding the message m to a code word em using the tensor code
*
* We encode the message using the BCH code. For each bit obtained,
* we duplicate the bit PARAM_N2 times to apply repetition code.
* BCH encoding is done using the classical mG operation,
* columns of the matrix are stored in 256-bit registers
*
* @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_HQC192_AVX2_code_encode(uint64_t *em, const uint64_t *m) {
const uint64_t mask[2][2] = {{0x0UL, 0x0UL}, {0x7FFFFFFFFFFFFFFUL, 0x3FFFFFFFFFFFFFFUL}};
size_t i, j, pos_r;
uint64_t bit;
uint16_t idx_r;
uint64_t select;


__m256i *colonne, y, aux0;
__m256i msg = _mm256_lddqu_si256((const __m256i *) m);
colonne = ((__m256i *) gen_matrix);

pos_r = 0;
for (i = 0; i < PARAM_N1 - PARAM_K; i++) {
// y is the and operation between m and ith column of G
y = _mm256_and_si256(colonne[i], msg);
// aux0 = (y2 y3 y0 y1)
aux0 = _mm256_permute2x128_si256(y, y, 1);
// y = (y0^y2 y1^y3 y2^y0 y3^y1)
y = _mm256_xor_si256(y, aux0);
// aux0 = (y1^y3 y0^y2 y1^y3 y0^y2)
aux0 = _mm256_shuffle_epi32(y, 0x4e);
// y = (y0^y1^y2^y3 repeated 4 times)
y = _mm256_xor_si256(aux0, y);
bit = _mm_popcnt_u64(_mm256_extract_epi64(y, 0)) & 1;


idx_r = (pos_r & 0x3f);
select = mux(mask[0][0], mask[1][0], bit);
em[(pos_r >> 6) + 0] ^= select << idx_r;
select = mux(mask[0][1], mask[1][1], bit);
em[(pos_r >> 6) + 1] ^= select >> ((63 - idx_r));
pos_r += PARAM_N2;
}

/* now we add the message m */
/* systematic encoding */
pos_r = PARAM_N2 * (PARAM_N1 - PARAM_K);
for (i = 0; i < 4; i++) {
for (j = 0; j < 64; j++) {
bit = (m[i] >> j) & 0x1;


idx_r = (pos_r & 0x3f);
select = mux(mask[0][0], mask[1][0], bit);
em[(pos_r >> 6) + 0] ^= select << idx_r;
select = mux(mask[0][1], mask[1][1], bit);
em[(pos_r >> 6) + 1] ^= select >> ((63 - idx_r));

pos_r += PARAM_N2;
}
}

}


/**
* @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_HQC192_AVX2_code_decode(uint64_t *m, const uint64_t *em) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC192_AVX2_repetition_code_decode(tmp, em);
PQCLEAN_HQC192_AVX2_bch_code_decode(m, tmp);

}

+ 0
- 18
crypto_kem/hqc-192/avx2/code.h View File

@@ -1,18 +0,0 @@
#ifndef CODE_H
#define CODE_H


/**
* @file code.h
* Header file of code.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_AVX2_code_encode(uint64_t *em, const uint64_t *message);

void PQCLEAN_HQC192_AVX2_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 359
crypto_kem/hqc-192/avx2/fft.c View File

@@ -1,359 +0,0 @@
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/**
* @file fft.c
* Implementation of the additive FFT and its transpose.
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf
*/


static void compute_fft_betas(uint16_t *betas);
static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, uint16_t set_size);
static void radix(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f);
static void radix_big(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) {
size_t i;
for (i = 0; i < PARAM_M - 1; ++i) {
betas[i] = (uint16_t) (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, uint16_t set_size) {
uint16_t i, j;
subset_sums[0] = 0;

for (i = 0; i < set_size; ++i) {
for (j = 0; j < (1 << i); ++j) {
subset_sums[(1 << i) + j] = set[i] ^ subset_sums[j];
}
}
}



/**
* @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];
break;

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];
break;

case 2:
f0[0] = f[0];
f0[1] = f[2] ^ f[3];
f1[0] = f[1] ^ f0[1];
f1[1] = f[3];
break;

case 1:
f0[0] = f[0];
f1[0] = f[1];
break;

default:
radix_big(f0, f1, f, m_f);
break;
}
}

static void radix_big(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) {
uint16_t Q[2 * (1 << (PARAM_FFT - 2))] = {0};
uint16_t R[2 * (1 << (PARAM_FFT - 2))] = {0};

uint16_t Q0[1 << (PARAM_FFT - 2)] = {0};
uint16_t Q1[1 << (PARAM_FFT - 2)] = {0};
uint16_t R0[1 << (PARAM_FFT - 2)] = {0};
uint16_t R1[1 << (PARAM_FFT - 2)] = {0};

size_t i, n;

n = 1;
n <<= m_f - 2;
memcpy(Q, f + 3 * n, 2 * n);
memcpy(Q + n, f + 3 * n, 2 * n);
memcpy(R, f, 4 * n);

for (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 PQCLEAN_HQC192_AVX2_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};
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};
uint16_t tmp[PARAM_M - (PARAM_FFT - 1)] = {0};

uint16_t beta_m_pow;
size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
for (i = 0; i < m; ++i) {
tmp[i] = PQCLEAN_HQC192_AVX2_gf_mul(betas[i], f[1]);
}

w[0] = f[0];
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
w[x + k] = w[k] ^ tmp[j];
}
x <<= 1;
}

return;
}

// Step 2: compute g
if (betas[m - 1] != 1) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC192_AVX2_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC192_AVX2_gf_mul(beta_m_pow, f[i]);
}
}

// Step 3
radix(f0, f1, f, m_f);

// Step 4: compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC192_AVX2_gf_mul(betas[i], PQCLEAN_HQC192_AVX2_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC192_AVX2_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);

k = 1;
k <<= ((m - 1) & 0xf); // &0xf is to let the compiler know that m-1 is small.
if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant
w[0] = u[0];
w[k] = u[0] ^ f1[0];
for (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_AVX2_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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_AVX2_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). <br>
* The FFT proceeds recursively to evaluate f at all subset sums of a basis B. <br>
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf <br>
* 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). <br>
* 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_HQC192_AVX2_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] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};

size_t i, k;

// 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 (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC192_AVX2_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);

k = 1;
k <<= PARAM_M - 1;
// 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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_AVX2_gf_mul(betas_sums[i], v[i]);
w[k + i] ^= w[i];
}
}



/**
* @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_HQC192_AVX2_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w) {
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
uint64_t bit;
uint16_t k;
size_t i, index;

compute_fft_betas(gammas);
compute_subset_sums(gammas_sums, gammas, PARAM_M - 1);

error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15);

k = 1;
k <<= PARAM_M - 1;
index = PARAM_GF_MUL_ORDER;
bit = 1 ^ ((uint16_t) - w[k] >> 15);
error[index / 8] ^= bit << (index % 64);

for (i = 1; i < k; ++i) {
index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i]];
bit = 1 ^ ((uint16_t) - w[i] >> 15);
error[index / 64] ^= bit << (index % 64);

index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i] ^ 1];
bit = 1 ^ ((uint16_t) - w[k + i] >> 15);
error[index / 64] ^= bit << (index % 64);
}
}

+ 0
- 18
crypto_kem/hqc-192/avx2/fft.h View File

@@ -1,18 +0,0 @@
#ifndef FFT_H
#define FFT_H


/**
* @file fft.h
* Header file of fft.c
*/

#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_AVX2_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs);

void PQCLEAN_HQC192_AVX2_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w);


#endif

+ 0
- 14
crypto_kem/hqc-192/avx2/gen_matrix.h
File diff suppressed because it is too large
View File


+ 0
- 145
crypto_kem/hqc-192/avx2/gf.c View File

@@ -1,145 +0,0 @@
#include "gf.h"
#include "parameters.h"
#include <emmintrin.h>
#include <immintrin.h>
#include <stdint.h>
/**
* @file gf.c
* Galois field implementation with multiplication using the pclmulqdq instruction
*/


static uint16_t gf_reduce(uint64_t x, size_t deg_x);
static uint16_t gf_quad(uint64_t a);



/**
* Reduces polynomial x modulo primitive polynomial GF_POLY.
* @returns x mod GF_POLY
* @param[in] x Polynomial of degree less than 64
* @param[in] deg_x The degree of polynomial x
*/
static uint16_t gf_reduce(uint64_t x, size_t deg_x) {
uint16_t z1, z2, rmdr, dist;
uint64_t mod;
size_t steps, i, j;

// Deduce the number of steps of reduction
steps = CEIL_DIVIDE(deg_x - (PARAM_M - 1), PARAM_GF_POLY_M2);

// Reduce
for (i = 0; i < steps; ++i) {
mod = x >> PARAM_M;
x &= (1 << PARAM_M) - 1;
x ^= mod;

z1 = 0;
rmdr = PARAM_GF_POLY ^ 1;
for (j = PARAM_GF_POLY_WT - 2; j; --j) {
z2 = __tzcnt_u16(rmdr);
dist = (uint16_t) (z2 - z1);
mod <<= dist;
x ^= mod;
rmdr ^= 1 << z2;
z1 = z2;
}
}

return x;
}



/**
* Multiplies two elements of GF(2^GF_M).
* @returns the product a*b
* @param[in] a Element of GF(2^GF_M)
* @param[in] b Element of GF(2^GF_M)
*/
uint16_t PQCLEAN_HQC192_AVX2_gf_mul(uint16_t a, uint16_t b) {
__m128i va = _mm_cvtsi32_si128(a);
__m128i vb = _mm_cvtsi32_si128(b);
__m128i vab = _mm_clmulepi64_si128(va, vb, 0);
uint32_t ab = _mm_cvtsi128_si32(vab);

return gf_reduce(ab, 2 * (PARAM_M - 1));
}



/**
* Squares an element of GF(2^GF_M).
* @returns a^2
* @param[in] a Element of GF(2^GF_M)
*/
uint16_t PQCLEAN_HQC192_AVX2_gf_square(uint16_t a) {
uint32_t b = a;
uint32_t s = b & 1;
for (size_t i = 1; i < PARAM_M; ++i) {
b <<= 1;
s ^= b & (1 << 2 * i);
}

return gf_reduce(s, 2 * (PARAM_M - 1));
}



/**
* Computes the 4th power of an element of GF(2^GF_M).
* @returns a^4
* @param[in] a Element of GF(2^GF_M)
*/
static uint16_t gf_quad(uint64_t a) {
uint64_t q = a & 1;
for (size_t i = 1; i < PARAM_M; ++i) {
a <<= 3;
q ^= a & (1ull << 4 * i);
}

return gf_reduce(q, 4 * (PARAM_M - 1));
}



/**
* Computes the inverse of an element of GF(2^10),
* using a shorter chain of squares and multiplications than fast exponentiation.
* @returns the inverse of a
* @param[in] a Element of GF(2^10)
*/
uint16_t PQCLEAN_HQC192_AVX2_gf_inverse(uint16_t a) {
uint16_t p;
uint16_t a2;

a2 = PQCLEAN_HQC192_AVX2_gf_square(a); // a^2
a = PQCLEAN_HQC192_AVX2_gf_mul(a2, a); // a^2.a
p = gf_quad(a); // a^8.a^4
a = PQCLEAN_HQC192_AVX2_gf_mul(p, a); // a^8.a^4.a^2.a
p = gf_quad(a); // a^32.a^16.a^8.a^4
p = gf_quad(p); // a^128.a^64.a^32.a^16
a = PQCLEAN_HQC192_AVX2_gf_mul(p, a); // a^128.a^64.a^32.a^16.a^8.a^4.a^2.a
p = gf_quad(a); // a^512.a^256.a^128.a^64.a^32.a^16.a^8.a^4
p = PQCLEAN_HQC192_AVX2_gf_mul(p, a2); // a^-1

return p;
}



/**
* Returns i modulo 2^GF_M-1.
* i must be less than 2*(2^GF_M-1).
* Therefore, the return value is either i or i-2^GF_M+1.
* @returns i mod (2^GF_M-1)
* @param[in] i The integer whose modulo is taken
*/
uint16_t PQCLEAN_HQC192_AVX2_gf_mod(uint16_t i) {
uint16_t tmp = (uint16_t) (i - PARAM_GF_MUL_ORDER);

// mask = 0xffff if (i < GF_MUL_ORDER)
uint16_t mask = -(tmp >> 15);

return tmp + (mask & PARAM_GF_MUL_ORDER);
}

+ 0
- 30
crypto_kem/hqc-192/avx2/gf.h
File diff suppressed because it is too large
View File


+ 0
- 562
crypto_kem/hqc-192/avx2/gf2x.c View File

@@ -1,562 +0,0 @@
#include "gf2x.h"
#include "parameters.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>

/**
* \file gf2x.c
* \brief AVX2 implementation of multiplication of two polynomials
*/


// sizes for Toom-Cook
#define T_TM3_3W_256 64
#define T_TM3_3W_64 256

#define VEC_N_SIZE_256 CEIL_DIVIDE(PARAM_N, 256) /*!< The number of needed vectors to store PARAM_N bits*/
__m256i a1_times_a2[2 * VEC_N_SIZE_256 + 1];
uint64_t bloc64[PARAM_OMEGA_R]; // Allocation with the biggest possible weight
uint64_t bit64[PARAM_OMEGA_R]; // Allocation with the biggest possible weight


static inline void reduce(uint64_t *o, const uint64_t *a);
static inline void karat_mult_1(__m128i *C, __m128i *A, __m128i *B);
static inline void karat_mult_2(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_4(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_8(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_16(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_32(__m256i *C, __m256i *A, __m256i *B);
static inline void karat_mult_64(__m256i *C, __m256i *A, __m256i *B);
static inline void divByXplus1(__m256i *out, __m256i *in, int size);
static void TOOM3Mult(__m256i *Out, const uint64_t *A, const uint64_t *B);



/**
* @brief Compute o(x) = a(x) mod \f$ X^n - 1\f$
*
* This function computes the modular reduction of the polynomial a(x)
*
* @param[out] o Pointer to the result
* @param[in] a Pointer to the polynomial a(x)
*/
static inline void reduce(uint64_t *o, const uint64_t *a) {
uint64_t r;
uint64_t carry;

for (uint32_t i = 0; i < VEC_N_SIZE_64; i++) {
r = a[i + VEC_N_SIZE_64 - 1] >> (PARAM_N & 63);
carry = (uint64_t) (a[i + VEC_N_SIZE_64] << (64 - (PARAM_N & 63)));
o[i] = a[i] ^ r ^ carry;
}

o[VEC_N_SIZE_64 - 1] &= RED_MASK;
}

/**
* @brief Compute C(x) = A(x)*B(x)
* A(x) and B(x) are stored in 128-bit registers
* This function computes A(x)*B(x) using Karatsuba
*
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_1(__m128i *C, __m128i *A, __m128i *B) {
__m128i D1[2];
__m128i D0[2], D2[2];
__m128i Al = _mm_loadu_si128(A);
__m128i Ah = _mm_loadu_si128(A + 1);
__m128i Bl = _mm_loadu_si128(B);
__m128i Bh = _mm_loadu_si128(B + 1);

// Compute Al.Bl=D0
__m128i DD0 = _mm_clmulepi64_si128(Al, Bl, 0);
__m128i DD2 = _mm_clmulepi64_si128(Al, Bl, 0x11);
__m128i AAlpAAh = _mm_xor_si128(Al, _mm_shuffle_epi32(Al, 0x4e));
__m128i BBlpBBh = _mm_xor_si128(Bl, _mm_shuffle_epi32(Bl, 0x4e));
__m128i DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D0[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D0[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Compute Ah.Bh=D2
DD0 = _mm_clmulepi64_si128(Ah, Bh, 0);
DD2 = _mm_clmulepi64_si128(Ah, Bh, 0x11);
AAlpAAh = _mm_xor_si128(Ah, _mm_shuffle_epi32(Ah, 0x4e));
BBlpBBh = _mm_xor_si128(Bh, _mm_shuffle_epi32(Bh, 0x4e));
DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D2[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D2[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Compute AlpAh.BlpBh=D1
// Initialisation of AlpAh and BlpBh
__m128i AlpAh = _mm_xor_si128(Al, Ah);
__m128i BlpBh = _mm_xor_si128(Bl, Bh);
DD0 = _mm_clmulepi64_si128(AlpAh, BlpBh, 0);
DD2 = _mm_clmulepi64_si128(AlpAh, BlpBh, 0x11);
AAlpAAh = _mm_xor_si128(AlpAh, _mm_shuffle_epi32(AlpAh, 0x4e));
BBlpBBh = _mm_xor_si128(BlpBh, _mm_shuffle_epi32(BlpBh, 0x4e));
DD1 = _mm_xor_si128(_mm_xor_si128(DD0, DD2), _mm_clmulepi64_si128(AAlpAAh, BBlpBBh, 0));
D1[0] = _mm_xor_si128(DD0, _mm_unpacklo_epi64(_mm_setzero_si128(), DD1));
D1[1] = _mm_xor_si128(DD2, _mm_unpackhi_epi64(DD1, _mm_setzero_si128()));

// Final comutation of C
__m128i middle = _mm_xor_si128(D0[1], D2[0]);
C[0] = D0[0];
C[1] = middle ^ D0[0] ^ D1[0];
C[2] = middle ^ D1[1] ^ D2[1];
C[3] = D2[1];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_2(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[2], D1[2], D2[2], SAA, SBB;
__m128i *A128 = (__m128i *)A, *B128 = (__m128i *)B;

karat_mult_1((__m128i *) D0, A128, B128);
karat_mult_1((__m128i *) D2, A128 + 2, B128 + 2);

SAA = A[0] ^ A[1];
SBB = B[0] ^ B[1];

karat_mult_1((__m128i *) D1, (__m128i *) &SAA, (__m128i *) &SBB);
__m256i middle = _mm256_xor_si256(D0[1], D2[0]);

C[0] = D0[0];
C[1] = middle ^ D0[0] ^ D1[0];
C[2] = middle ^ D1[1] ^ D2[1];
C[3] = D2[1];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_4(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[4], D1[4], D2[4], SAA[2], SBB[2];

karat_mult_2( D0, A, B);
karat_mult_2(D2, A + 2, B + 2);

SAA[0] = A[0] ^ A[2];
SBB[0] = B[0] ^ B[2];
SAA[1] = A[1] ^ A[3];
SBB[1] = B[1] ^ B[3];

karat_mult_2( D1, SAA, SBB);

__m256i middle0 = _mm256_xor_si256(D0[2], D2[0]);
__m256i middle1 = _mm256_xor_si256(D0[3], D2[1]);

C[0] = D0[0];
C[1] = D0[1];
C[2] = middle0 ^ D0[0] ^ D1[0];
C[3] = middle1 ^ D0[1] ^ D1[1];
C[4] = middle0 ^ D1[2] ^ D2[2];
C[5] = middle1 ^ D1[3] ^ D2[3];
C[6] = D2[2];
C[7] = D2[3];
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_8(__m256i *C, __m256i *A, __m256i *B) {
int32_t i, is, is2, is3;
__m256i D0[8], D1[8], D2[8], SAA[4], SBB[4];

karat_mult_4( D0, A, B);
karat_mult_4(D2, A + 4, B + 4);

for (i = 0; i < 4; i++) {
is = i + 4;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_4(D1, SAA, SBB);

for (i = 0; i < 4; i++) {
is = i + 4;
is2 = is + 4;
is3 = is2 + 4;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_16(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[16], D1[16], D2[16], SAA[8], SBB[8];
int32_t i, is, is2, is3;

karat_mult_8( D0, A, B);
karat_mult_8(D2, A + 8, B + 8);

for (i = 0; i < 8; i++) {
is = i + 8;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_8( D1, SAA, SBB);

for (i = 0; i < 8; i++) {
is = i + 8;
is2 = is + 8;
is3 = is2 + 8;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_32(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[32], D1[32], D2[32], SAA[16], SBB[16];
int32_t i, is, is2, is3;

karat_mult_16( D0, A, B);
karat_mult_16(D2, A + 16, B + 16);

for (i = 0; i < 16; i++) {
is = i + 16;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_16( D1, SAA, SBB);

for (i = 0; i < 16; i++) {
is = i + 16;
is2 = is + 16;
is3 = is2 + 16;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute C(x) = A(x)*B(x)
*
* This function computes A(x)*B(x) using Karatsuba
* A(x) and B(x) are stored in 256-bit registers
* @param[out] C Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static inline void karat_mult_64(__m256i *C, __m256i *A, __m256i *B) {
__m256i D0[64], D1[64], D2[64], SAA[32], SBB[32];
int32_t i, is, is2, is3;

karat_mult_32( D0, A, B);
karat_mult_32(D2, A + 32, B + 32);
for (i = 0; i < 32; i++) {
is = i + 32;
SAA[i] = A[i] ^ A[is];
SBB[i] = B[i] ^ B[is];
}

karat_mult_32( D1, SAA, SBB);

for (i = 0; i < 32; i++) {
is = i + 32;
is2 = is + 32;
is3 = is2 + 32;

__m256i middle = _mm256_xor_si256(D0[is], D2[i]);

C[i] = D0[i];
C[is] = middle ^ D0[i] ^ D1[i];
C[is2] = middle ^ D1[is] ^ D2[is];
C[is3] = D2[is];
}
}



/**
* @brief Compute B(x) = A(x)/(x+1)
*
* This function computes A(x)/(x+1) using a Quercia like algorithm
* @param[out] out Pointer to the result
* @param[in] in Pointer to the polynomial A(x)
* @param[in] size used to define the number of coeeficients of A
*/
static inline void divByXplus1(__m256i *out, __m256i *in, int size) {
uint64_t *A = (uint64_t *) in;
uint64_t *B = (uint64_t *) out;

B[0] = A[0];

for (int32_t i = 1; i < 2 * (size << 2); i++) {
B[i] = B[i - 1] ^ A[i];
}
}



/**
* @brief Compute C(x) = A(x)*B(x) using TOOM3Mult
*
* This function computes A(x)*B(x) using TOOM-COOK3 Multiplication
* last multiplication are done using Karatsuba
* @param[out] Out Pointer to the result
* @param[in] A Pointer to the polynomial A(x)
* @param[in] B Pointer to the polynomial B(x)
*/
static void TOOM3Mult(__m256i *Out, const uint64_t *A, const uint64_t *B) {
static __m256i U0[T_TM3_3W_256], V0[T_TM3_3W_256], U1[T_TM3_3W_256], V1[T_TM3_3W_256], U2[T_TM3_3W_256], V2[T_TM3_3W_256];
static __m256i W0[2 * (T_TM3_3W_256)], W1[2 * (T_TM3_3W_256)], W2[2 * (T_TM3_3W_256)], W3[2 * (T_TM3_3W_256)], W4[2 * (T_TM3_3W_256)];
static __m256i tmp[2 * (T_TM3_3W_256)];
static __m256i ro256[6 * (T_TM3_3W_256)];
const __m256i zero = _mm256_setzero_si256();
int64_t *U1_64;
int64_t *U2_64;
int64_t *V1_64;
int64_t *V2_64;
int32_t T2 = T_TM3_3W_64 << 1;
int32_t i, i4, i41, i42;

for (i = 0; i < T_TM3_3W_256 - 1; i++) {
i4 = i << 2;
i42 = i4 - 2;
U0[i] = _mm256_lddqu_si256((__m256i const *)(& A[i4]));
V0[i] = _mm256_lddqu_si256((__m256i const *)(& B[i4]));
U1[i] = _mm256_lddqu_si256((__m256i const *)(& A[i42 + T_TM3_3W_64]));
V1[i] = _mm256_lddqu_si256((__m256i const *)(& B[i42 + T_TM3_3W_64]));
U2[i] = _mm256_lddqu_si256((__m256i const *)(& A[i4 + T2 - 4]));
V2[i] = _mm256_lddqu_si256((__m256i const *)(& B[i4 + T2 - 4]));
}

for (i = T_TM3_3W_256 - 1; i < T_TM3_3W_256; i++) {
i4 = i << 2;
i41 = i4 + 1;
U0[i] = _mm256_set_epi64x(0, 0, A[i41], A[i4]);
V0[i] = _mm256_set_epi64x(0, 0, B[i41], B[i4]);
U1[i] = _mm256_set_epi64x(0, 0, A[i41 + T_TM3_3W_64 - 2], A[i4 + T_TM3_3W_64 - 2]);
V1[i] = _mm256_set_epi64x(0, 0, B[i41 + T_TM3_3W_64 - 2], B[i4 + T_TM3_3W_64 - 2]);
U2[i] = _mm256_set_epi64x(0, 0, A[i4 - 3 + T2], A[i4 - 4 + T2]);
V2[i] = _mm256_set_epi64x(0, 0, B[i4 - 3 + T2], B[i4 - 4 + T2]);
}

// Evaluation phase : x= X^64
// P(X): P0=(0); P1=(1); P2=(x); P3=(1+x); P4=(\infty)
// Evaluation: 5*2 add, 2*2 shift; 5 mul (n)
//W3 = U2 + U1 + U0; W2 = V2 + V1 + V0
for (i = 0; i < T_TM3_3W_256; i++) {
W3[i] = U0[i] ^ U1[i] ^ U2[i];
W2[i] = V0[i] ^ V1[i] ^ V2[i];
}

//W1 = W2 * W3
karat_mult_64( W1, W2, W3);

//W0 =(U1 + U2*x)*x; W4 =(V1 + V2*x)*x (SIZE = T_TM3_3W_256 !)
U1_64 = ((int64_t *) U1);
U2_64 = ((int64_t *) U2);

V1_64 = ((int64_t *) V1);
V2_64 = ((int64_t *) V2);

W0[0] = _mm256_set_epi64x(U1_64[2] ^ U2_64[1], U1_64[1] ^ U2_64[0], U1_64[0], 0);
W4[0] = _mm256_set_epi64x(V1_64[2] ^ V2_64[1], V1_64[1] ^ V2_64[0], V1_64[0], 0);

for (i = 1; i < T_TM3_3W_256; i++) {
i4 = i << 2;
W0[i] = _mm256_lddqu_si256((__m256i const *)(& U1_64[i4 - 1]));
W0[i] ^= _mm256_lddqu_si256((__m256i const *)(& U2_64[i4 - 2]));

W4[i] = _mm256_lddqu_si256((__m256i const *)(& V1_64[i4 - 1]));
W4[i] ^= _mm256_lddqu_si256((__m256i const *)(& V2_64[i4 - 2]));
}

//W3 = W3 + W0 ; W2 = W2 + W4
for (i = 0; i < T_TM3_3W_256; i++) {
W3[i] ^= W0[i];
W2[i] ^= W4[i];
}

//W0 = W0 + U0 ; W4 = W4 + V0
for (i = 0; i < T_TM3_3W_256; i++) {
W0[i] ^= U0[i];
W4[i] ^= V0[i];
}

karat_mult_64(tmp, W3, W2);

for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W3[i] = tmp[i];
}

karat_mult_64( W2, W0, W4);
//W4 = U2 * V2 ; W0 = U0 * V0
karat_mult_64(W4, U2, V2);
karat_mult_64(W0, U0, V0);

// Interpolation phase
// 9 add, 1 shift, 1 Smul, 2 Sdiv (2n)
//W3 = W3 + W2
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W3[i] ^= W2[i];
}

//W1 = W1 + W0
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W1[i] ^= W0[i];
}

//W2 =(W2 + W0)/x -> x = X^64
U1_64 = ((int64_t *) W2);
U2_64 = ((int64_t *) W0);
for (i = 0; i < (T_TM3_3W_256 << 1); i++) {
i4 = i << 2;
W2[i] = _mm256_lddqu_si256((__m256i const *)(& U1_64[i4 + 1]));
W2[i] ^= _mm256_lddqu_si256((__m256i const *)(& U2_64[i4 + 1]));
}

//W2 =(W2 + W3 + W4*(x^3+1))/(x+1)
U1_64 = ((int64_t *) W4);
__m256i *U1_256 = (__m256i *) (U1_64 + 1);
tmp[0] = W2[0] ^ W3[0] ^ W4[0] ^ _mm256_set_epi64x(U1_64[0], 0, 0, 0);

for (i = 1; i < (T_TM3_3W_256 << 1) - 1; i++) {
tmp[i] = W2[i] ^ W3[i] ^ W4[i] ^ _mm256_lddqu_si256(&U1_256[i - 1]);
}

divByXplus1(W2, tmp, T_TM3_3W_256);
W2[2 * (T_TM3_3W_256) - 1] = zero;

//W3 =(W3 + W1)/(x*(x+1))
U1_64 = (int64_t *) W3;
U1_256 = (__m256i *) (U1_64 + 1);

U2_64 = (int64_t *) W1;
__m256i *U2_256 = (__m256i *) (U2_64 + 1);

for (i = 0; i < 2 * (T_TM3_3W_256) - 1; i++) {
tmp[i] = _mm256_lddqu_si256(&U1_256[i]) ^ _mm256_lddqu_si256(&U2_256[i]);
}

divByXplus1(W3, tmp, T_TM3_3W_256);
W3[2 * (T_TM3_3W_256) - 1] = zero;

//W1 = W1 + W4 + W2
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W1[i] ^= W2[i] ^ W4[i];
}

//W2 = W2 + W3
for (i = 0; i < 2 * (T_TM3_3W_256); i++) {
W2[i] ^= W3[i];
}

// Recomposition
//W = W0+ W1*x+ W2*x^2+ W3*x^3 + W4*x^4
//W0, W1, W4 of size 2*T_TM3_3W_256, W2 and W3 of size 2*(T_TM3_3W_256)
for (i = 0; i < (T_TM3_3W_256 << 1) - 1; i++) {
ro256[i] = W0[i];
ro256[i + 2 * T_TM3_3W_256 - 1] = W2[i];
ro256[i + 4 * T_TM3_3W_256 - 2] = W4[i];
}

ro256[(T_TM3_3W_256 << 1) - 1] = W0[(T_TM3_3W_256 << 1) - 1] ^ W2[0];
ro256[(T_TM3_3W_256 << 2) - 2] = W2[(T_TM3_3W_256 << 1) - 1] ^ W4[0];
ro256[(T_TM3_3W_256 * 6) - 3] = W4[(T_TM3_3W_256 << 1) - 1];

U1_64 = ((int64_t *) &ro256[T_TM3_3W_256]);
U1_256 = (__m256i *) (U1_64 - 2);

U2_64 = ((int64_t *) &ro256[3 * T_TM3_3W_256 - 1]);
U2_256 = (__m256i *) (U2_64 - 2);

for (i = 0; i < T_TM3_3W_256 << 1; i++) {
_mm256_storeu_si256(&U1_256[i], W1[i] ^ _mm256_lddqu_si256(&U1_256[i]));
_mm256_storeu_si256(&U2_256[i], W3[i] ^ _mm256_loadu_si256(&U2_256[i]));
}

for (i = 0; i < 2 * VEC_N_SIZE_256 + 1; i++) {
_mm256_storeu_si256(&Out[i], ro256[i]);
}
}


/**
* @brief Multiply two polynomials modulo \f$ X^n - 1\f$.
*
* This functions multiplies a sparse polynomial <b>a1</b> (of Hamming weight equal to <b>weight</b>)
* and a dense polynomial <b>a2</b>. The multiplication is done modulo \f$ X^n - 1\f$.
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to a polynomial
* @param[in] a2 Pointer to a polynomial
*/
void PQCLEAN_HQC192_AVX2_vect_mul(uint64_t *o, const uint64_t *a1, const uint64_t *a2) {
TOOM3Mult(a1_times_a2, a1, a2);
reduce(o, (uint64_t *)a1_times_a2);

// clear all
memset(a1_times_a2, 0, (2 * VEC_N_SIZE_256 + 1) * sizeof(__m256i));
}

+ 0
- 15
crypto_kem/hqc-192/avx2/gf2x.h View File

@@ -1,15 +0,0 @@
#ifndef GF2X_H
#define GF2X_H


/**
* @file gf2x.h
* @brief Header file for gf2x.c
*/

#include <stdint.h>

void PQCLEAN_HQC192_AVX2_vect_mul(uint64_t *o, const uint64_t *a1, const uint64_t *a2);


#endif

+ 0
- 138
crypto_kem/hqc-192/avx2/hqc.c View File

@@ -1,138 +0,0 @@
#include "code.h"
#include "gf2x.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
/**
* @file hqc.c
* @brief Implementation of hqc.h
*/



/**
* @brief Keygen of the HQC_PKE IND_CPA scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the <b>seed</b> used to generate the vector <b>h</b>.
*
* The secret key is composed of the <b>seed</b> used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_AVX2_hqc_pke_keygen(unsigned char *pk, unsigned char *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};
uint64_t x[VEC_N_256_SIZE_64] = {0};
uint64_t y[VEC_N_256_SIZE_64] = {0};
uint64_t h[VEC_N_256_SIZE_64] = {0};
uint64_t s[VEC_N_256_SIZE_64] = {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_HQC192_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, y, PARAM_OMEGA);

// Compute public key
PQCLEAN_HQC192_AVX2_vect_set_random(&pk_seedexpander, h);
PQCLEAN_HQC192_AVX2_vect_mul(s, y, h);
PQCLEAN_HQC192_AVX2_vect_add(s, x, s, VEC_N_256_SIZE_64);

// Parse keys to string
PQCLEAN_HQC192_AVX2_hqc_public_key_to_string(pk, pk_seed, s);
PQCLEAN_HQC192_AVX2_hqc_secret_key_to_string(sk, sk_seed, pk);

}



/**
* @brief Encryption of the HQC_PKE IND_CPA scheme
*
* The cihertext is composed of vectors <b>u</b> and <b>v</b>.
*
* @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_HQC192_AVX2_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk) {
AES_XOF_struct seedexpander;
uint64_t h[VEC_N_256_SIZE_64] = {0};
uint64_t s[VEC_N_256_SIZE_64] = {0};
uint64_t r1[VEC_N_256_SIZE_64] = {0};
uint64_t r2[VEC_N_256_SIZE_64] = {0};
uint64_t e[VEC_N_256_SIZE_64] = {0};
uint64_t tmp1[VEC_N_256_SIZE_64] = {0};
uint64_t tmp2[VEC_N_256_SIZE_64] = {0};

// Create seed_expander from theta
seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH);

// Retrieve h and s from public key
PQCLEAN_HQC192_AVX2_hqc_public_key_from_string(h, s, pk);

// Generate r1, r2 and e
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R);
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&seedexpander, r2, PARAM_OMEGA_R);
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E);

// Compute u = r1 + r2.h
PQCLEAN_HQC192_AVX2_vect_mul(u, r2, h);
PQCLEAN_HQC192_AVX2_vect_add(u, r1, u, VEC_N_256_SIZE_64);

// Compute v = m.G by encoding the message
PQCLEAN_HQC192_AVX2_code_encode(v, m);
PQCLEAN_HQC192_AVX2_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);

// Compute v = m.G + s.r2 + e
PQCLEAN_HQC192_AVX2_vect_mul(tmp2, r2, s);
PQCLEAN_HQC192_AVX2_vect_add(tmp2, e, tmp2, VEC_N_256_SIZE_64);
PQCLEAN_HQC192_AVX2_vect_add(tmp2, tmp1, tmp2, VEC_N_256_SIZE_64);
PQCLEAN_HQC192_AVX2_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_HQC192_AVX2_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk) {
uint64_t x[VEC_N_256_SIZE_64] = {0};
uint64_t y[VEC_N_256_SIZE_64] = {0};
uint8_t pk[PUBLIC_KEY_BYTES] = {0};
uint64_t tmp1[VEC_N_256_SIZE_64] = {0};
uint64_t tmp2[VEC_N_256_SIZE_64] = {0};

// Retrieve x, y, pk from secret key
PQCLEAN_HQC192_AVX2_hqc_secret_key_from_string(x, y, pk, sk);

// Compute v - u.y
PQCLEAN_HQC192_AVX2_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);
PQCLEAN_HQC192_AVX2_vect_mul(tmp2, y, u);
PQCLEAN_HQC192_AVX2_vect_add(tmp2, tmp1, tmp2, VEC_N_256_SIZE_64);


// Compute m by decoding v - u.y
PQCLEAN_HQC192_AVX2_code_decode(m, tmp2);
}

+ 0
- 19
crypto_kem/hqc-192/avx2/hqc.h View File

@@ -1,19 +0,0 @@
#ifndef HQC_H
#define HQC_H


/**
* @file hqc.h
* @brief Functions of the HQC_PKE IND_CPA scheme
*/

#include <stdint.h>

void PQCLEAN_HQC192_AVX2_hqc_pke_keygen(unsigned char *pk, unsigned char *sk);

void PQCLEAN_HQC192_AVX2_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk);

void PQCLEAN_HQC192_AVX2_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk);


#endif

+ 0
- 140
crypto_kem/hqc-192/avx2/kem.c View File

@@ -1,140 +0,0 @@
#include "api.h"
#include "fips202.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "sha2.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file kem.c
* @brief Implementation of api.h
*/



/**
* @brief Keygen of the HQC_KEM IND_CAA2 scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>.
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_AVX2_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {

PQCLEAN_HQC192_AVX2_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_HQC192_AVX2_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) {

uint8_t theta[SHA512_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint64_t u[VEC_N_256_SIZE_64] = {0};
uint64_t v[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Computing m
PQCLEAN_HQC192_AVX2_vect_set_random_from_randombytes(m);

// Computing theta
sha3_512(theta, (uint8_t *) m, VEC_K_SIZE_BYTES);

// Encrypting m
PQCLEAN_HQC192_AVX2_hqc_pke_encrypt(u, v, m, theta, pk);

// Computing d
sha512(d, (unsigned char *) m, VEC_K_SIZE_BYTES);

// Computing shared secret
PQCLEAN_HQC192_AVX2_store8_arr(mc, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);
PQCLEAN_HQC192_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC192_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Computing ciphertext
PQCLEAN_HQC192_AVX2_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_HQC192_AVX2_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) {

uint8_t result;
uint64_t u[VEC_N_256_SIZE_64] = {0};
uint64_t v[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char pk[PUBLIC_KEY_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint8_t theta[SHA512_BYTES] = {0};
uint64_t u2[VEC_N_256_SIZE_64] = {0};
uint64_t v2[VEC_N1N2_256_SIZE_64] = {0};
unsigned char d2[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Retrieving u, v and d from ciphertext
PQCLEAN_HQC192_AVX2_hqc_ciphertext_from_string(u, v, d, ct);

// Retrieving pk from sk
memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES);

// Decryting
PQCLEAN_HQC192_AVX2_hqc_pke_decrypt(m, u, v, sk);

// Computing theta
sha3_512(theta, (uint8_t *) m, VEC_K_SIZE_BYTES);

// Encrypting m'
PQCLEAN_HQC192_AVX2_hqc_pke_encrypt(u2, v2, m, theta, pk);

// Computing d'
sha512(d2, (unsigned char *) m, VEC_K_SIZE_BYTES);

// Computing shared secret
PQCLEAN_HQC192_AVX2_store8_arr(mc, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);
PQCLEAN_HQC192_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC192_AVX2_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Abort if c != c' or d != d'
result = PQCLEAN_HQC192_AVX2_vect_compare((uint8_t *)u, (uint8_t *)u2, VEC_N_SIZE_BYTES);
result |= PQCLEAN_HQC192_AVX2_vect_compare((uint8_t *)v, (uint8_t *)v2, VEC_N1N2_SIZE_BYTES);
result |= PQCLEAN_HQC192_AVX2_vect_compare(d, d2, SHA512_BYTES);
result = (uint8_t) (-((int16_t) result) >> 15);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
ss[i] &= ~result;
}


return -(result & 1);
}

+ 0
- 128
crypto_kem/hqc-192/avx2/parameters.h View File

@@ -1,128 +0,0 @@
#ifndef HQC_PARAMETERS_H
#define HQC_PARAMETERS_H
/**
* @file parameters.h
* @brief Parameters of the HQC_KEM IND-CCA2 scheme
*/
#include "api.h"
#include "vector.h"


#define CEIL_DIVIDE(a, b) (((a)+(b)-1)/(b)) /*!< Divide a by b and ceil the result*/

/*
#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_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes
#define VEC_K_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes
#define VEC_N1_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes
#define VEC_N1N2_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes

#define VEC_N_SIZE_64 Define the size of the array used to store a PARAM_N sized vector in 64 bits
#define VEC_K_SIZE_64 Define the size of the array used to store a PARAM_K sized vector in 64 bits
#define VEC_N1_SIZE_64 Define the size of the array used to store a PARAM_N1 sized vector in 64 bits
#define VEC_N1N2_SIZE_64 Define the size of the array used to store a PARAM_N1N2 sized vector in 64 bits

#define VEC_N_256_SIZE_64 Define the size of the array of 64 bits elements used to store an array of size PARAM_N considered as elements of 256 bits
#define VEC_N1N2_256_SIZE_64 Define the size of the array of 64 bits elements used to store an array of size PARAM_N1N2 considered as elements of 256 bits

#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_POLY Generator polynomial of galois field GF(2^PARAM_M), represented in hexadecimial form
#define PARAM_GF_POLY_WT Hamming weight of PARAM_GF_POLY
#define PARAM_GF_POLY_M2 Distance between the primitive polynomial first two set bits
#define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^PARAM_M), i.e 2^PARAM_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_BCH_POLY Generator polynomial of the BCH code

#define RED_MASK A mask fot the higher bits of a vector
#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 45197
#define PARAM_N1 766
#define PARAM_N2 59
#define PARAM_N1N2 45194
#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_HQC192_AVX2_CRYPTO_SECRETKEYBYTES
#define PUBLIC_KEY_BYTES PQCLEAN_HQC192_AVX2_CRYPTO_PUBLICKEYBYTES
#define SHARED_SECRET_BYTES PQCLEAN_HQC192_AVX2_CRYPTO_BYTES
#define CIPHERTEXT_BYTES PQCLEAN_HQC192_AVX2_CRYPTO_CIPHERTEXTBYTES

#define UTILS_REJECTION_THRESHOLD 16768087
#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 VEC_N_SIZE_64 CEIL_DIVIDE(PARAM_N, 64)
#define VEC_K_SIZE_64 CEIL_DIVIDE(PARAM_K, 64)
#define VEC_N1_SIZE_64 CEIL_DIVIDE(PARAM_N1, 64)
#define VEC_N1N2_SIZE_64 CEIL_DIVIDE(PARAM_N1N2, 64)

#define PARAM_N_MULT 48768
#define VEC_N_256_SIZE_64 (CEIL_DIVIDE(PARAM_N_MULT, 256) << 2)
#define VEC_N1N2_256_SIZE_64 (CEIL_DIVIDE(PARAM_N1N2, 256) << 2)

#define PARAM_T 29

#define PARAM_DELTA 57
#define PARAM_M 10
#define PARAM_GF_POLY 0x409
#define PARAM_GF_POLY_WT 3
#define PARAM_GF_POLY_M2 7
#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 RED_MASK 0x0000000000001fffUL
#define SHA512_BYTES 64
#define SEED_BYTES 40
#define SEEDEXPANDER_MAX_LENGTH 4294967295

#endif

+ 0
- 186
crypto_kem/hqc-192/avx2/parsing.c View File

@@ -1,186 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file parsing.c
* @brief Functions to parse secret key, public key and ciphertext of the HQC scheme
*/


void PQCLEAN_HQC192_AVX2_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_HQC192_AVX2_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_HQC192_AVX2_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_HQC192_AVX2_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_HQC192_AVX2_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++;
}
}
}


/**
* @brief Parse a secret key into a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_AVX2_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) {
memcpy(sk, sk_seed, SEED_BYTES);
sk += SEED_BYTES;
memcpy(sk, pk, PUBLIC_KEY_BYTES);
}

/**
* @brief Parse a secret key from a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* As technicality, the public key is appended to the secret key in order to respect NIST API.
*
* @param[out] x uint64_t representation of vector x
* @param[out] y uint64_t representation of vector y
* @param[out] pk String containing the public key
* @param[in] sk String containing the secret key
*/
void PQCLEAN_HQC192_AVX2_hqc_secret_key_from_string(uint64_t *x, uint64_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);
sk += SEED_BYTES;
memcpy(pk, sk, PUBLIC_KEY_BYTES);

seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(&sk_seedexpander, y, PARAM_OMEGA);
}

/**
* @brief Parse a public key into a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC192_AVX2_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s) {
memcpy(pk, pk_seed, SEED_BYTES);
PQCLEAN_HQC192_AVX2_store8_arr(pk + SEED_BYTES, VEC_N_SIZE_BYTES, s, VEC_N_SIZE_64);
}



/**
* @brief Parse a public key from a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC192_AVX2_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk) {
AES_XOF_struct pk_seedexpander;
uint8_t pk_seed[SEED_BYTES] = {0};

memcpy(pk_seed, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_HQC192_AVX2_load8_arr(s, VEC_N_SIZE_64, pk, VEC_N_SIZE_BYTES);

seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC192_AVX2_vect_set_random(&pk_seedexpander, h);
}


/**
* @brief Parse a ciphertext into a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC192_AVX2_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d) {
PQCLEAN_HQC192_AVX2_store8_arr(ct, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC192_AVX2_store8_arr(ct, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(ct, d, SHA512_BYTES);
}


/**
* @brief Parse a ciphertext from a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC192_AVX2_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct) {
PQCLEAN_HQC192_AVX2_load8_arr(u, VEC_N_SIZE_64, ct, VEC_N_SIZE_BYTES);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC192_AVX2_load8_arr(v, VEC_N1N2_SIZE_64, ct, VEC_N1N2_SIZE_BYTES);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(d, ct, SHA512_BYTES);
}

+ 0
- 36
crypto_kem/hqc-192/avx2/parsing.h View File

@@ -1,36 +0,0 @@
#ifndef PARSING_H
#define PARSING_H


/**
* @file parsing.h
* @brief Header file for parsing.c
*/

#include <stdint.h>

void PQCLEAN_HQC192_AVX2_store8(unsigned char *out, uint64_t in);

uint64_t PQCLEAN_HQC192_AVX2_load8(const unsigned char *in);

void PQCLEAN_HQC192_AVX2_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen);

void PQCLEAN_HQC192_AVX2_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen);


void PQCLEAN_HQC192_AVX2_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk);

void PQCLEAN_HQC192_AVX2_hqc_secret_key_from_string(uint64_t *x, uint64_t *y, uint8_t *pk, const uint8_t *sk);


void PQCLEAN_HQC192_AVX2_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s);

void PQCLEAN_HQC192_AVX2_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk);


void PQCLEAN_HQC192_AVX2_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d);

void PQCLEAN_HQC192_AVX2_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct);


#endif

+ 0
- 43
crypto_kem/hqc-192/avx2/repetition.c View File

@@ -1,43 +0,0 @@
#include "parameters.h"
#include "repetition.h"
#include <immintrin.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/**
* @file repetition.c
* @brief Implementation of repetition codes
*/


#define MASK_N2 ((((uint64_t) 1) << PARAM_N2) - 1)

/**
* @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_HQC192_AVX2_repetition_code_decode(uint64_t *m, const uint64_t *em) {
size_t t = 0;
uint32_t b, bn, bi, c, cn, ci;
uint64_t cx, ones;
uint64_t mask;

for (b = 0; b < PARAM_N1N2 - PARAM_N2 + 1; b += PARAM_N2) {
bn = b >> 6;
bi = b & 63;
c = b + PARAM_N2 - 1;
cn = c >> 6;
ci = c & 63;
cx = em[cn] << (63 - ci);
mask = (uint64_t) (-((int64_t) (cn ^ (bn + 1))) >> 63); // cn != bn+1
ones = _mm_popcnt_u64(((em[bn] >> bi) & MASK_N2) | (cx & ~mask));
m[t >> 6] |= (uint64_t) ((((PARAM_T - ones) >> 31) & 1) << (t & 63));
t++;
}
}

+ 0
- 15
crypto_kem/hqc-192/avx2/repetition.h View File

@@ -1,15 +0,0 @@
#ifndef REPETITION_H
#define REPETITION_H


/**
* @file repetition.h
* @brief Header file for repetition.c
*/

#include <stdint.h>

void PQCLEAN_HQC192_AVX2_repetition_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 194
crypto_kem/hqc-192/avx2/vector.c View File

@@ -1,194 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
/**
* @file vector.c
* @brief Implementation of vectors sampling and some utilities for the HQC scheme
*/



/**
* @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 <b>weight</b>.
* 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_HQC192_AVX2_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight) {
size_t random_bytes_size = 3 * weight;
uint8_t rand_bytes[3 * PARAM_OMEGA_R] = {0};
uint32_t tmp[PARAM_OMEGA_R] = {0};
__m256i bit256[PARAM_OMEGA_R];
__m256i bloc256[PARAM_OMEGA_R];
__m256i posCmp256 = _mm256_set_epi64x(3, 2, 1, 0);
__m256i pos256;
__m256i mask256;
__m256i aux;
__m256i i256;
uint64_t bloc, pos, bit64;
uint8_t inc;
size_t i, j, k;

i = 0;
j = random_bytes_size;
while (i < weight) {
do {
if (j == random_bytes_size) {
seedexpander(ctx, rand_bytes, random_bytes_size);
j = 0;
}

tmp[i] = ((uint32_t) rand_bytes[j++]) << 16;
tmp[i] |= ((uint32_t) rand_bytes[j++]) << 8;
tmp[i] |= rand_bytes[j++];

} while (tmp[i] >= UTILS_REJECTION_THRESHOLD);

tmp[i] = tmp[i] % PARAM_N;

inc = 1;
for (k = 0; k < i; k++) {
if (tmp[k] == tmp[i]) {
inc = 0;
}
}
i += inc;
}

for (i = 0; i < weight; i++) {
// we store the bloc number and bit position of each vb[i]
bloc = tmp[i] >> 6;
bloc256[i] = _mm256_set1_epi64x(bloc >> 2);
pos = (bloc & 0x3UL);
pos256 = _mm256_set1_epi64x(pos);
mask256 = _mm256_cmpeq_epi64(pos256, posCmp256);
bit64 = 1ULL << (tmp[i] & 0x3f);
bit256[i] = _mm256_set1_epi64x(bit64)&mask256;
}

for (i = 0; i < CEIL_DIVIDE(PARAM_N, 256); i++) {
aux = _mm256_loadu_si256(((__m256i *)v) + i);
i256 = _mm256_set1_epi64x(i);

for (j = 0; j < weight; j++) {
mask256 = _mm256_cmpeq_epi64(bloc256[j], i256);
aux ^= bit256[j] & mask256;
}
_mm256_storeu_si256(((__m256i *)v) + i, aux);
}

}



/**
* @brief Generates a random vector of dimension <b>PARAM_N</b>
*
* This function generates a random binary vector of dimension <b>PARAM_N</b>. 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_HQC192_AVX2_vect_set_random(AES_XOF_struct *ctx, uint64_t *v) {
uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0};

seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES);

PQCLEAN_HQC192_AVX2_load8_arr(v, VEC_N_SIZE_64, rand_bytes, VEC_N_SIZE_BYTES);
v[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @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_HQC192_AVX2_vect_set_random_from_randombytes(uint64_t *v) {
uint8_t rand_bytes [VEC_K_SIZE_BYTES] = {0};

randombytes(rand_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC192_AVX2_load8_arr(v, VEC_K_SIZE_64, 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_HQC192_AVX2_vect_add(uint64_t *o, const uint64_t *v1, const uint64_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
*/
uint8_t PQCLEAN_HQC192_AVX2_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) {
uint64_t r = 0;
for (size_t i = 0; i < size; i++) {
r |= v1[i] ^ v2[i];
}
r = (~r + 1) >> 63;
return (uint8_t) r;
}



/**
* @brief Resize a vector so that it contains <b>size_o</b> 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_HQC192_AVX2_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v) {
uint64_t mask = 0x7FFFFFFFFFFFFFFF;
int8_t val = 0;
if (size_o < size_v) {
if (size_o % 64) {
val = 64 - (size_o % 64);
}

memcpy(o, v, VEC_N1N2_SIZE_BYTES);

for (int8_t i = 0; i < val; ++i) {
o[VEC_N1N2_SIZE_64 - 1] &= (mask >> i);
}
} else {
memcpy(o, v, CEIL_DIVIDE(size_v, 8));
}
}

+ 0
- 27
crypto_kem/hqc-192/avx2/vector.h View File

@@ -1,27 +0,0 @@
#ifndef VECTOR_H
#define VECTOR_H


/**
* @file vector.h
* @brief Header file for vector.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC192_AVX2_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight);

void PQCLEAN_HQC192_AVX2_vect_set_random(AES_XOF_struct *ctx, uint64_t *v);

void PQCLEAN_HQC192_AVX2_vect_set_random_from_randombytes(uint64_t *v);


void PQCLEAN_HQC192_AVX2_vect_add(uint64_t *o, const uint64_t *v1, const uint64_t *v2, uint32_t size);

uint8_t PQCLEAN_HQC192_AVX2_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size);

void PQCLEAN_HQC192_AVX2_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v);


#endif

+ 0
- 1
crypto_kem/hqc-192/clean/LICENSE View File

@@ -1 +0,0 @@
Public Domain

+ 0
- 19
crypto_kem/hqc-192/clean/Makefile View File

@@ -1,19 +0,0 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libhqc-192_clean.a
HEADERS=api.h bch.h code.h fft.h gf2x.h gf.h hqc.h parameters.h parsing.h repetition.h vector.h
OBJECTS=bch.o code.o fft.o gf2x.o gf.o hqc.o kem.o parsing.o repetition.o vector.o

CFLAGS=-O3 -Wall -Wextra -Wpedantic -Wshadow -Wvla -Werror -Wredundant-decls -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 0
- 19
crypto_kem/hqc-192/clean/Makefile.Microsoft_nmake View File

@@ -1,19 +0,0 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake

LIBRARY=libhqc-192_clean.lib
OBJECTS=bch.obj code.obj fft.obj gf2x.obj gf.obj hqc.obj kem.obj parsing.obj repetition.obj vector.obj

CFLAGS=/nologo /O2 /I ..\..\..\common /W4 /WX

all: $(LIBRARY)

# Make sure objects are recompiled if headers change.
$(OBJECTS): *.h

$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**

clean:
-DEL $(OBJECTS)
-DEL $(LIBRARY)

+ 0
- 25
crypto_kem/hqc-192/clean/api.h View File

@@ -1,25 +0,0 @@
#ifndef PQCLEAN_HQC192_CLEAN_API_H
#define PQCLEAN_HQC192_CLEAN_API_H
/**
* @file api.h
* @brief NIST KEM API used by the HQC_KEM IND-CCA2 scheme
*/

#define PQCLEAN_HQC192_CLEAN_CRYPTO_ALGNAME "HQC-192"

#define PQCLEAN_HQC192_CLEAN_CRYPTO_SECRETKEYBYTES 5730
#define PQCLEAN_HQC192_CLEAN_CRYPTO_PUBLICKEYBYTES 5690
#define PQCLEAN_HQC192_CLEAN_CRYPTO_BYTES 64
#define PQCLEAN_HQC192_CLEAN_CRYPTO_CIPHERTEXTBYTES 11364

// As a technicality, the public key is appended to the secret key in order to respect the NIST API.
// Without this constraint, PQCLEAN_HQC192_CLEAN_CRYPTO_SECRETKEYBYTES would be defined as 32

int PQCLEAN_HQC192_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk);

int PQCLEAN_HQC192_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk);

int PQCLEAN_HQC192_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk);


#endif

+ 0
- 286
crypto_kem/hqc-192/clean/bch.c View File

@@ -1,286 +0,0 @@
#include "bch.h"
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file bch.c
* Constant time implementation of BCH codes
*/


static void unpack_message(uint8_t *message_unpacked, const uint64_t *message);
static void lfsr_encode(uint8_t *codeword, const uint8_t *message);
static void pack_codeword(uint64_t *codeword, const uint8_t *codeword_unpacked);
static size_t compute_elp(uint16_t *sigma, const uint16_t *syndromes);
static void message_from_codeword(uint64_t *message, const uint64_t *codeword);
static void compute_syndromes(uint16_t *syndromes, const uint64_t *vector);
static void compute_roots(uint64_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 uint64_t *message) {
for (size_t i = 0; i < (VEC_K_SIZE_64 - (PARAM_K % 64 != 0)); ++i) {
for (size_t j = 0; j < 64; ++j) {
message_unpacked[j + 64 * i] = (message[i] >> j) & 0x0000000000000001;
}
}

for (int8_t j = 0; j < PARAM_K % 64; ++j) {
message_unpacked[j + 64 * (VEC_K_SIZE_64 - 1)] = (message[VEC_K_SIZE_64 - 1] >> j) & 0x0000000000000001;
}
}


/**
* @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(uint64_t *codeword, const uint8_t *codeword_unpacked) {
for (size_t i = 0; i < (VEC_N1_SIZE_64 - (PARAM_N1 % 64 != 0)); ++i) {
for (size_t j = 0; j < 64; ++j) {
codeword[i] |= ((uint64_t) codeword_unpacked[j + 64 * i]) << j;
}
}

for (size_t j = 0; j < PARAM_N1 % 64; ++j) {
codeword[VEC_N1_SIZE_64 - 1] |= ((uint64_t) codeword_unpacked[j + 64 * (VEC_N1_SIZE_64 - 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_HQC192_CLEAN_bch_code_encode(uint64_t *codeword, const uint64_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). <br>
* We use the letter p for rho which is initialized at -1/2. <br>
* The array X_sigma_p represents the polynomial X^(2(mu-rho))*sigma_p(X). <br>
* Instead of maintaining a list of sigmas, we update in place both sigma and X_sigma_p. <br>
* sigma_copy serves as a temporary save of sigma in case X_sigma_p needs to be updated. <br>
* 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) {
uint16_t sigma_copy[PARAM_DELTA - 1] = {0};
uint16_t X_sigma_p[PARAM_DELTA + 1] = {0};
uint16_t d_p, d, dd;
uint16_t mask;
int32_t pp; // 2*rho
size_t deg_sigma, deg_sigma_p, deg_sigma_copy, deg_X_sigma_p;

d = syndromes[0];
sigma[0] = 1;
X_sigma_p[1] = 1;
deg_sigma = 0;
deg_sigma_p = 0;
d_p = 1;
pp = -1;
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;

dd = PQCLEAN_HQC192_CLEAN_gf_mul(d, PQCLEAN_HQC192_CLEAN_gf_inverse(d_p)); // 0 if(d == 0)
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
sigma[i] ^= PQCLEAN_HQC192_CLEAN_gf_mul(dd, X_sigma_p[i]);
}

deg_X_sigma_p = 2 * mu - pp + deg_sigma_p;

// mask = 0xffff if(d != 0) and 0 otherwise
mask = -((uint16_t) - d >> 15);

// mask2 &= 0xffff if(deg_X_sigma_p > deg_sigma) and 0 otherwise
mask &= -((uint16_t) (deg_sigma - deg_X_sigma_p) >> 15);

deg_sigma ^= mask & (deg_sigma ^ deg_X_sigma_p);

if (mu == PARAM_DELTA - 1) {
break;
}

// Update pp, d_p and X_sigma_p if needed
pp ^= mask & (pp ^ (2 * mu));
d_p ^= mask & (d_p ^ d);
for (size_t i = PARAM_DELTA - 1; i; --i) {
X_sigma_p[i + 1] = X_sigma_p[i - 1];
X_sigma_p[i + 1] ^= mask & (X_sigma_p[i + 1] ^ sigma_copy[i - 1]);
}
X_sigma_p[1] = 0;
X_sigma_p[0] = 0;
deg_sigma_p ^= mask & (deg_sigma_p ^ deg_sigma_copy);

// Compute the next discrepancy
d = syndromes[2 * mu + 2];
for (size_t i = 1; (i <= 2 * mu + 1) && (i <= PARAM_DELTA); ++i) {
d ^= PQCLEAN_HQC192_CLEAN_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(uint64_t *message, const uint64_t *codeword) {
uint64_t mask1 = (uint64_t) (0xffffffffffffffff << ((PARAM_N1 - PARAM_K) % 64));
uint64_t mask2 = (uint64_t) (0xffffffffffffffff >> (64 - (PARAM_N1 - PARAM_K) % 64));
size_t index = (PARAM_N1 - PARAM_K) / 64;

for (size_t i = 0; i < VEC_K_SIZE_64 - 1; ++i) {
message[i] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
message[i] |= (codeword[++index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}

// Last byte (8-val % 8 is the number of bits given by message1)
message[VEC_K_SIZE_64 - 1] = (codeword[index] & mask1) >> ((PARAM_N1 - PARAM_K) % 64);
++index;
if (index < VEC_N1_SIZE_64) {
message[VEC_K_SIZE_64 - 1] |= (codeword[index] & mask2) << (64 - (PARAM_N1 - PARAM_K) % 64);
}
}


/**
* @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. <br>
* Therefore, this requires twisting and applying a permutation before feeding vector to the PQCLEAN_HQC192_CLEAN_fft transpose. <br>
* 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 uint64_t *vector) {
uint16_t w[1 << PARAM_M];

PQCLEAN_HQC192_CLEAN_fft_t_preprocess_bch_codeword(w, vector);
PQCLEAN_HQC192_CLEAN_fft_t(syndromes, w, 2 * PARAM_DELTA);
}


/**
* @brief Computes the error polynomial error from the error locator polynomial sigma
*
* See function PQCLEAN_HQC192_CLEAN_fft for more details.
*
* @param[out] error 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(uint64_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_HQC192_CLEAN_fft(w, sigma, PARAM_DELTA + 1);
PQCLEAN_HQC192_CLEAN_fft_retrieve_bch_error_poly(error, w);
}



/**
* @brief Decodes the received word
*
* This function relies on four steps:
* <ol>
* <li> The first step, done by additive FFT transpose, is the computation of the 2*PARAM_DELTA syndromes.
* <li> The second step is the computation of the error-locator polynomial sigma.
* <li> 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.
* <li> The fourth step is the correction of the errors in the received polynomial.
* </ol>
* 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_HQC192_CLEAN_bch_code_decode(uint64_t *message, uint64_t *vector) {
uint16_t syndromes[1 << PARAM_FFT_T] = {0};
uint16_t sigma[1 << PARAM_FFT] = {0};
uint64_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_HQC192_CLEAN_vect_add(vector, vector, error, VEC_N1_SIZE_64);

// Retrieve the message from the decoded codeword
message_from_codeword(message, vector);

}

+ 0
- 18
crypto_kem/hqc-192/clean/bch.h View File

@@ -1,18 +0,0 @@
#ifndef BCH_H
#define BCH_H


/**
* @file bch.h
* Header file of bch.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_bch_code_encode(uint64_t *codeword, const uint64_t *message);

void PQCLEAN_HQC192_CLEAN_bch_code_decode(uint64_t *message, uint64_t *vector);


#endif

+ 0
- 49
crypto_kem/hqc-192/clean/code.c View File

@@ -1,49 +0,0 @@
#include "bch.h"
#include "code.h"
#include "parameters.h"
#include "repetition.h"
#include <stdint.h>
#include <string.h>
/**
* @file code.c
* @brief Implementation of tensor code
*/



/**
*
* @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_HQC192_CLEAN_code_encode(uint64_t *em, const uint64_t *m) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC192_CLEAN_bch_code_encode(tmp, m);
PQCLEAN_HQC192_CLEAN_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_HQC192_CLEAN_code_decode(uint64_t *m, const uint64_t *em) {

uint64_t tmp[VEC_N1_SIZE_64] = {0};

PQCLEAN_HQC192_CLEAN_repetition_code_decode(tmp, em);
PQCLEAN_HQC192_CLEAN_bch_code_decode(m, tmp);


}

+ 0
- 18
crypto_kem/hqc-192/clean/code.h View File

@@ -1,18 +0,0 @@
#ifndef CODE_H
#define CODE_H


/**
* @file code.h
* Header file of code.c
*/
#include "parameters.h"
#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_code_encode(uint64_t *em, const uint64_t *message);

void PQCLEAN_HQC192_CLEAN_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 673
crypto_kem/hqc-192/clean/fft.c View File

@@ -1,673 +0,0 @@
#include "fft.h"
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/**
* @file fft.c
* Implementation of the additive FFT and its transpose.
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf
*/


static void compute_fft_betas(uint16_t *betas);
static void compute_subset_sums(uint16_t *subset_sums, const uint16_t *set, uint16_t set_size);
static void radix_t(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f);
static void radix_t_big(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 radix_big(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) {
size_t i;
for (i = 0; i < PARAM_M - 1; ++i) {
betas[i] = (uint16_t) (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, uint16_t set_size) {
uint16_t i, j;
subset_sums[0] = 0;

for (i = 0; i < set_size; ++i) {
for (j = 0; j < (1 << i); ++j) {
subset_sums[(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];
break;

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];
break;

case 2:
f[0] = f0[0];
f[1] = f1[0];
f[2] = f0[1] ^ f1[0];
f[3] = f[2] ^ f1[1];
break;

case 1:
f[0] = f0[0];
f[1] = f1[0];
break;

default:
radix_t_big(f, f0, f1, m_f);
break;
}
}

static void radix_t_big(uint16_t *f, const uint16_t *f0, const uint16_t *f1, uint32_t m_f) {
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};

uint16_t n;
size_t i;

n = 1;
n <<= m_f - 2;
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 (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 PQCLEAN_HQC192_CLEAN_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) {
uint16_t gammas[PARAM_M - 2] = {0};
uint16_t deltas[PARAM_M - 2] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
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};
uint16_t v[1 << (PARAM_M - 2)] = {0};
uint16_t beta_m_pow;

size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
f[0] = 0;
x = 1;
x <<= m;
for (i = 0; i < x; ++i) {
f[0] ^= w[i];
}
f[1] = 0;

betas_sums[0] = 0;
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
betas_sums[x + k] = betas_sums[k] ^ betas[j];
f[1] ^= PQCLEAN_HQC192_CLEAN_gf_mul(betas_sums[x + k], w[x + k]);
}
x <<= 1;
}

return;
}

// Compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC192_CLEAN_gf_mul(betas[i], PQCLEAN_HQC192_CLEAN_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC192_CLEAN_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] */
k = 1;
k <<= (m - 1) & 0xf; // &0xf is to let the compiler know that m-1 is small.
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 (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
f1[0] ^= PQCLEAN_HQC192_CLEAN_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 {
u[0] = w[0] ^ w[k];
v[0] = w[k];

for (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
v[i] = PQCLEAN_HQC192_CLEAN_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) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC192_CLEAN_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC192_CLEAN_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(...). <br>
* 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_HQC192_CLEAN_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] = {0};
uint16_t betas_sums[1 << (PARAM_M - 1)] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};
uint16_t deltas[PARAM_M - 1] = {0};
uint16_t f0[1 << (PARAM_FFT_T - 1)] = {0};
uint16_t f1[1 << (PARAM_FFT_T - 1)] = {0};

size_t i, k;

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] */
k = 1;
k <<= PARAM_M - 1;
u[0] = w[0] ^ w[k];
v[0] = w[k];
for (i = 1; i < k; ++i) {
u[i] = w[i] ^ w[k + i];
v[i] = PQCLEAN_HQC192_CLEAN_gf_mul(betas_sums[i], u[i]) ^ w[k + i];
}

// Compute deltas
for (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC192_CLEAN_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];
break;

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];
break;

case 2:
f0[0] = f[0];
f0[1] = f[2] ^ f[3];
f1[0] = f[1] ^ f0[1];
f1[1] = f[3];
break;

case 1:
f0[0] = f[0];
f1[0] = f[1];
break;

default:
radix_big(f0, f1, f, m_f);
break;
}
}

static void radix_big(uint16_t *f0, uint16_t *f1, const uint16_t *f, uint32_t m_f) {
uint16_t Q[2 * (1 << (PARAM_FFT - 2))] = {0};
uint16_t R[2 * (1 << (PARAM_FFT - 2))] = {0};

uint16_t Q0[1 << (PARAM_FFT - 2)] = {0};
uint16_t Q1[1 << (PARAM_FFT - 2)] = {0};
uint16_t R0[1 << (PARAM_FFT - 2)] = {0};
uint16_t R1[1 << (PARAM_FFT - 2)] = {0};

size_t i, n;

n = 1;
n <<= m_f - 2;
memcpy(Q, f + 3 * n, 2 * n);
memcpy(Q + n, f + 3 * n, 2 * n);
memcpy(R, f, 4 * n);

for (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 PQCLEAN_HQC192_CLEAN_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};
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};
uint16_t tmp[PARAM_M - (PARAM_FFT - 1)] = {0};

uint16_t beta_m_pow;
size_t i, j, k;
size_t x;

// Step 1
if (m_f == 1) {
for (i = 0; i < m; ++i) {
tmp[i] = PQCLEAN_HQC192_CLEAN_gf_mul(betas[i], f[1]);
}

w[0] = f[0];
x = 1;
for (j = 0; j < m; ++j) {
for (k = 0; k < x; ++k) {
w[x + k] = w[k] ^ tmp[j];
}
x <<= 1;
}

return;
}

// Step 2: compute g
if (betas[m - 1] != 1) {
beta_m_pow = 1;
x = 1;
x <<= m_f;
for (i = 1; i < x; ++i) {
beta_m_pow = PQCLEAN_HQC192_CLEAN_gf_mul(beta_m_pow, betas[m - 1]);
f[i] = PQCLEAN_HQC192_CLEAN_gf_mul(beta_m_pow, f[i]);
}
}

// Step 3
radix(f0, f1, f, m_f);

// Step 4: compute gammas and deltas
for (i = 0; i + 1 < m; ++i) {
gammas[i] = PQCLEAN_HQC192_CLEAN_gf_mul(betas[i], PQCLEAN_HQC192_CLEAN_gf_inverse(betas[m - 1]));
deltas[i] = PQCLEAN_HQC192_CLEAN_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);

k = 1;
k <<= (m - 1) & 0xf; // &0xf is to let the compiler know that m-1 is small.
if (f_coeffs <= 3) { // 3-coefficient polynomial f case: f1 is constant
w[0] = u[0];
w[k] = u[0] ^ f1[0];
for (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_CLEAN_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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_CLEAN_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). <br>
* The FFT proceeds recursively to evaluate f at all subset sums of a basis B. <br>
* This implementation is based on the paper from Gao and Mateer: <br>
* 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 <br>
* and includes improvements proposed by Bernstein, Chou and Schwabe here:
* https://binary.cr.yp.to/mcbits-20130616.pdf <br>
* 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). <br>
* 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_HQC192_CLEAN_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] = {0};
uint16_t u[1 << (PARAM_M - 1)] = {0};
uint16_t v[1 << (PARAM_M - 1)] = {0};

size_t i, k;

// 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 (i = 0; i < PARAM_M - 1; ++i) {
deltas[i] = PQCLEAN_HQC192_CLEAN_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);

k = 1;
k <<= PARAM_M - 1;
// 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 (i = 1; i < k; ++i) {
w[i] = u[i] ^ PQCLEAN_HQC192_CLEAN_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. <br>
* 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_HQC192_CLEAN_fft_t_preprocess_bch_codeword(uint16_t *w, const uint64_t *vector) {
uint16_t r[1 << PARAM_M] = {0};
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
size_t i, j, k;

// Unpack the received word vector into array r
for (i = 0; i < VEC_N1_SIZE_64 - (PARAM_N1 % 64 != 0); ++i) {
for (j = 0; j < 64; ++j) {
r[64 * i + j] = (uint8_t) ((vector[i] >> j) & 1);
}
}

// Last byte
for (j = 0; j < PARAM_N1 % 64; ++j) {
r[64 * i + j] = (uint8_t) ((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
k = 1;
k <<= PARAM_M - 1;
w[0] = 0;
w[k] = -r[0] & 1;
for (i = 1; i < k; ++i) {
w[i] = -r[gf_log[gammas_sums[i]]] & gammas_sums[i];
w[k + i] = -r[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_HQC192_CLEAN_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w) {
uint16_t gammas[PARAM_M - 1] = {0};
uint16_t gammas_sums[1 << (PARAM_M - 1)] = {0};
uint64_t bit;
uint16_t k;
size_t i, index;

compute_fft_betas(gammas);
compute_subset_sums(gammas_sums, gammas, PARAM_M - 1);

error[0] ^= 1 ^ ((uint16_t) - w[0] >> 15);

k = 1;
k <<= PARAM_M - 1;
index = PARAM_GF_MUL_ORDER;
bit = 1 ^ ((uint16_t) - w[k] >> 15);
error[index / 8] ^= bit << (index % 64);

for (i = 1; i < k; ++i) {
index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i]];
bit = 1 ^ ((uint16_t) - w[i] >> 15);
error[index / 64] ^= bit << (index % 64);

index = PARAM_GF_MUL_ORDER - gf_log[gammas_sums[i] ^ 1];
bit = 1 ^ ((uint16_t) - w[k + i] >> 15);
error[index / 64] ^= bit << (index % 64);
}
}

+ 0
- 23
crypto_kem/hqc-192/clean/fft.h View File

@@ -1,23 +0,0 @@
#ifndef FFT_H
#define FFT_H


/**
* @file fft.h
* Header file of fft.c
*/

#include <stddef.h>
#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_fft_t(uint16_t *f, const uint16_t *w, size_t f_coeffs);

void PQCLEAN_HQC192_CLEAN_fft_t_preprocess_bch_codeword(uint16_t *w, const uint64_t *vector);


void PQCLEAN_HQC192_CLEAN_fft(uint16_t *w, const uint16_t *f, size_t f_coeffs);

void PQCLEAN_HQC192_CLEAN_fft_retrieve_bch_error_poly(uint64_t *error, const uint16_t *w);


#endif

+ 0
- 63
crypto_kem/hqc-192/clean/gf.c View File

@@ -1,63 +0,0 @@
#include "gf.h"
#include "parameters.h"
#include <stdint.h>
/**
* @file gf.c
* Galois field implementation with multiplication using lookup tables
*/


/**
* @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_HQC192_CLEAN_gf_mul(uint16_t a, uint16_t b) {
uint16_t mask;
mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
mask &= (uint16_t) (-((int32_t) b) >> 31); // b != 0
return mask & gf_exp[PQCLEAN_HQC192_CLEAN_gf_mod(gf_log[a] + gf_log[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_HQC192_CLEAN_gf_square(uint16_t a) {
int16_t mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
return mask & gf_exp[PQCLEAN_HQC192_CLEAN_gf_mod(2 * gf_log[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_HQC192_CLEAN_gf_inverse(uint16_t a) {
int16_t mask = (uint16_t) (-((int32_t) a) >> 31); // a != 0
return mask & gf_exp[PARAM_GF_MUL_ORDER - gf_log[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_HQC192_CLEAN_gf_mod(uint16_t i) {
uint16_t tmp = (uint16_t) (i - PARAM_GF_MUL_ORDER);

// mask = 0xffff if(i < PARAM_GF_MUL_ORDER)
uint16_t mask = -(tmp >> 15);

return tmp + (mask & PARAM_GF_MUL_ORDER);
}

+ 0
- 38
crypto_kem/hqc-192/clean/gf.h
File diff suppressed because it is too large
View File


+ 0
- 154
crypto_kem/hqc-192/clean/gf2x.c View File

@@ -1,154 +0,0 @@
#include "gf2x.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include <stdint.h>
/**
* \file gf2x.c
* \brief Implementation of multiplication of two polynomials
*/


static inline void swap(uint16_t *tab, uint16_t elt1, uint16_t elt2);
static void reduce(uint64_t *o, const uint64_t *a);
static void fast_convolution_mult(uint8_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx);

/**
* @brief swap two elements in a table
*
* This function exchanges tab[elt1] with tab[elt2]
*
* @param[in] tab Pointer to the table
* @param[in] elt1 Index of the first element
* @param[in] elt2 Index of the second element
*/
static inline void swap(uint16_t *tab, uint16_t elt1, uint16_t elt2) {
uint16_t tmp = tab[elt1];

tab[elt1] = tab[elt2];
tab[elt2] = tmp;
}



/**
* @brief Compute o(x) = a(x) mod \f$ X^n - 1\f$
*
* This function computes the modular reduction of the polynomial a(x)
*
* @param[in] a Pointer to the polynomial a(x)
* @param[out] o Pointer to the result
*/
static void reduce(uint64_t *o, const uint64_t *a) {
size_t i;
uint64_t r;
uint64_t carry;

for (i = 0; i < VEC_N_SIZE_64; i++) {
r = a[i + VEC_N_SIZE_64 - 1] >> (PARAM_N & 63);
carry = (uint64_t) (a[i + VEC_N_SIZE_64] << (64 - (PARAM_N & 63)));
o[i] = a[i] ^ r ^ carry;
}

o[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @brief computes product of the polynomial a1(x) with the sparse polynomial a2
*
* o(x) = a1(x)a2(x)
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to the sparse polynomial a2 (list of degrees of the monomials which appear in a2)
* @param[in] a2 Pointer to the polynomial a1(x)
* @param[in] weight Hamming wifht of the sparse polynomial a2
* @param[in] ctx Pointer to a seed expander used to randomize the multiplication process
*/
static void fast_convolution_mult(uint8_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx) {
//static uint32_t fast_convolution_mult(const uint64_t *A, const uint32_t *vB, uint64_t *C, const uint16_t w, AES_XOF_struct *ctx)
uint64_t carry;
uint32_t dec, s;
uint64_t table[16 * (VEC_N_SIZE_64 + 1)];
uint16_t permuted_table[16];
uint16_t permutation_table[16];
uint16_t permuted_sparse_vect[PARAM_OMEGA_E];
uint16_t permutation_sparse_vect[PARAM_OMEGA_E];
uint64_t tmp;
uint64_t *pt;
uint8_t *res;
size_t i, j;

for (i = 0; i < 16; i++) {
permuted_table[i] = (uint16_t) i;
}

seedexpander(ctx, (uint8_t *) permutation_table, 16 * sizeof(uint16_t));

for (i = 0; i < 15; i++) {
swap(permuted_table + i, 0, permutation_table[i] % (16 - i));
}

pt = table + (permuted_table[0] * (VEC_N_SIZE_64 + 1));
for (j = 0; j < VEC_N_SIZE_64; j++) {
pt[j] = a2[j];
}
pt[VEC_N_SIZE_64] = 0x0;

for (i = 1; i < 16; i++) {
carry = 0;
pt = table + (permuted_table[i] * (VEC_N_SIZE_64 + 1));
for (j = 0; j < VEC_N_SIZE_64; j++) {
pt[j] = (a2[j] << i) ^ carry;
carry = (a2[j] >> ((64 - i)));
}
pt[VEC_N_SIZE_64] = carry;
}

for (i = 0; i < weight; i++) {
permuted_sparse_vect[i] = (uint16_t) i;
}

seedexpander(ctx, (uint8_t *) permutation_sparse_vect, weight * sizeof(uint16_t));

for (i = 0; i + 1 < weight; i++) {
swap(permuted_sparse_vect + i, 0, (uint16_t) (permutation_sparse_vect[i] % (weight - i)));
}

for (i = 0; i < weight; i++) {
dec = a1[permuted_sparse_vect[i]] & 0xf;
s = a1[permuted_sparse_vect[i]] >> 4;
res = o + 2 * s;
pt = table + (permuted_table[dec] * (VEC_N_SIZE_64 + 1));

for (j = 0; j < VEC_N_SIZE_64 + 1; j++) {
tmp = PQCLEAN_HQC192_CLEAN_load8(res);
PQCLEAN_HQC192_CLEAN_store8(res, tmp ^ pt[j]);
res += 8;
}
}
}



/**
* @brief Multiply two polynomials modulo \f$ X^n - 1\f$.
*
* This functions multiplies a sparse polynomial <b>a1</b> (of Hamming weight equal to <b>weight</b>)
* and a dense polynomial <b>a2</b>. The multiplication is done modulo \f$ X^n - 1\f$.
*
* @param[out] o Pointer to the result
* @param[in] a1 Pointer to the sparse polynomial
* @param[in] a2 Pointer to the dense polynomial
* @param[in] weight Integer that is the weigt of the sparse polynomial
* @param[in] ctx Pointer to the randomness context
*/
void PQCLEAN_HQC192_CLEAN_vect_mul(uint64_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx) {
uint64_t tmp[2 * VEC_N_SIZE_64 + 1] = {0};

fast_convolution_mult((uint8_t *) tmp, a1, a2, weight, ctx);
PQCLEAN_HQC192_CLEAN_load8_arr(tmp, 2 * VEC_N_SIZE_64 + 1, (uint8_t *) tmp, sizeof(tmp));
reduce(o, tmp);
}

+ 0
- 16
crypto_kem/hqc-192/clean/gf2x.h View File

@@ -1,16 +0,0 @@
#ifndef GF2X_H
#define GF2X_H


/**
* @file gf2x.h
* @brief Header file for gf2x.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_vect_mul(uint64_t *o, const uint32_t *a1, const uint64_t *a2, uint16_t weight, AES_XOF_struct *ctx);


#endif

+ 0
- 143
crypto_kem/hqc-192/clean/hqc.c View File

@@ -1,143 +0,0 @@
#include "code.h"
#include "gf2x.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
/**
* @file hqc.c
* @brief Implementation of hqc.h
*/



/**
* @brief Keygen of the HQC_PKE IND_CPA scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the <b>seed</b> used to generate the vector <b>h</b>.
*
* The secret key is composed of the <b>seed</b> used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_CLEAN_hqc_pke_keygen(unsigned char *pk, unsigned char *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};
uint64_t x[VEC_N_SIZE_64] = {0};
uint32_t y[PARAM_OMEGA] = {0};
uint64_t h[VEC_N_SIZE_64] = {0};
uint64_t s[VEC_N_SIZE_64] = {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_HQC192_CLEAN_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA);

// Compute public key
PQCLEAN_HQC192_CLEAN_vect_set_random(&pk_seedexpander, h);
PQCLEAN_HQC192_CLEAN_vect_mul(s, y, h, PARAM_OMEGA, &sk_seedexpander);
PQCLEAN_HQC192_CLEAN_vect_add(s, x, s, VEC_N_SIZE_64);

// Parse keys to string
PQCLEAN_HQC192_CLEAN_hqc_public_key_to_string(pk, pk_seed, s);
PQCLEAN_HQC192_CLEAN_hqc_secret_key_to_string(sk, sk_seed, pk);

}



/**
* @brief Encryption of the HQC_PKE IND_CPA scheme
*
* The cihertext is composed of vectors <b>u</b> and <b>v</b>.
*
* @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_HQC192_CLEAN_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk) {
AES_XOF_struct seedexpander;
uint64_t h[VEC_N_SIZE_64] = {0};
uint64_t s[VEC_N_SIZE_64] = {0};
uint64_t r1[VEC_N_SIZE_64] = {0};
uint32_t r2[PARAM_OMEGA_R] = {0};
uint64_t e[VEC_N_SIZE_64] = {0};
uint64_t tmp1[VEC_N_SIZE_64] = {0};
uint64_t tmp2[VEC_N_SIZE_64] = {0};

// Create seed_expander from theta
seedexpander_init(&seedexpander, theta, theta + 32, SEEDEXPANDER_MAX_LENGTH);

// Retrieve h and s from public key
PQCLEAN_HQC192_CLEAN_hqc_public_key_from_string(h, s, pk);

// Generate r1, r2 and e
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight(&seedexpander, r1, PARAM_OMEGA_R);
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight_by_coordinates(&seedexpander, r2, PARAM_OMEGA_R);
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight(&seedexpander, e, PARAM_OMEGA_E);

// Compute u = r1 + r2.h
PQCLEAN_HQC192_CLEAN_vect_mul(u, r2, h, PARAM_OMEGA_R, &seedexpander);
PQCLEAN_HQC192_CLEAN_vect_add(u, r1, u, VEC_N_SIZE_64);

// Compute v = m.G by encoding the message
PQCLEAN_HQC192_CLEAN_code_encode(v, m);
PQCLEAN_HQC192_CLEAN_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);

// Compute v = m.G + s.r2 + e
PQCLEAN_HQC192_CLEAN_vect_mul(tmp2, r2, s, PARAM_OMEGA_R, &seedexpander);
PQCLEAN_HQC192_CLEAN_vect_add(tmp2, e, tmp2, VEC_N_SIZE_64);
PQCLEAN_HQC192_CLEAN_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_64);
PQCLEAN_HQC192_CLEAN_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_HQC192_CLEAN_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk) {
uint64_t x[VEC_N_SIZE_64] = {0};
uint32_t y[PARAM_OMEGA] = {0};
uint8_t pk[PUBLIC_KEY_BYTES] = {0};
uint64_t tmp1[VEC_N_SIZE_64] = {0};
uint64_t tmp2[VEC_N_SIZE_64] = {0};
AES_XOF_struct perm_seedexpander;
uint8_t perm_seed[SEED_BYTES] = {0};

// Retrieve x, y, pk from secret key
PQCLEAN_HQC192_CLEAN_hqc_secret_key_from_string(x, y, pk, sk);

randombytes(perm_seed, SEED_BYTES);
seedexpander_init(&perm_seedexpander, perm_seed, perm_seed + 32, SEEDEXPANDER_MAX_LENGTH);

// Compute v - u.y
PQCLEAN_HQC192_CLEAN_vect_resize(tmp1, PARAM_N, v, PARAM_N1N2);
PQCLEAN_HQC192_CLEAN_vect_mul(tmp2, y, u, PARAM_OMEGA, &perm_seedexpander);
PQCLEAN_HQC192_CLEAN_vect_add(tmp2, tmp1, tmp2, VEC_N_SIZE_64);


// Compute m by decoding v - u.y
PQCLEAN_HQC192_CLEAN_code_decode(m, tmp2);
}

+ 0
- 19
crypto_kem/hqc-192/clean/hqc.h View File

@@ -1,19 +0,0 @@
#ifndef HQC_H
#define HQC_H


/**
* @file hqc.h
* @brief Functions of the HQC_PKE IND_CPA scheme
*/

#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_hqc_pke_keygen(unsigned char *pk, unsigned char *sk);

void PQCLEAN_HQC192_CLEAN_hqc_pke_encrypt(uint64_t *u, uint64_t *v, uint64_t *m, unsigned char *theta, const unsigned char *pk);

void PQCLEAN_HQC192_CLEAN_hqc_pke_decrypt(uint64_t *m, const uint64_t *u, const uint64_t *v, const unsigned char *sk);


#endif

+ 0
- 144
crypto_kem/hqc-192/clean/kem.c View File

@@ -1,144 +0,0 @@
#include "api.h"
#include "fips202.h"
#include "hqc.h"
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "sha2.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file kem.c
* @brief Implementation of api.h
*/



/**
* @brief Keygen of the HQC_KEM IND_CAA2 scheme
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>.
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_CLEAN_crypto_kem_keypair(unsigned char *pk, unsigned char *sk) {

PQCLEAN_HQC192_CLEAN_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_HQC192_CLEAN_crypto_kem_enc(unsigned char *ct, unsigned char *ss, const unsigned char *pk) {

uint8_t theta[SHA512_BYTES] = {0};
uint8_t m_bytes[VEC_K_SIZE_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint64_t u[VEC_N_SIZE_64] = {0};
uint64_t v[VEC_N1N2_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Computing m
randombytes(m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC192_CLEAN_load8_arr(m, VEC_K_SIZE_64, m_bytes, VEC_K_SIZE_BYTES);

// Computing theta
sha3_512(theta, m_bytes, VEC_K_SIZE_BYTES);

// Encrypting m
PQCLEAN_HQC192_CLEAN_hqc_pke_encrypt(u, v, m, theta, pk);

// Computing d
sha512(d, m_bytes, VEC_K_SIZE_BYTES);

// Computing shared secret
memcpy(mc, m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC192_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC192_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Computing ciphertext
PQCLEAN_HQC192_CLEAN_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_HQC192_CLEAN_crypto_kem_dec(unsigned char *ss, const unsigned char *ct, const unsigned char *sk) {

uint8_t result;
uint64_t u[VEC_N_SIZE_64] = {0};
uint64_t v[VEC_N1N2_SIZE_64] = {0};
unsigned char d[SHA512_BYTES] = {0};
unsigned char pk[PUBLIC_KEY_BYTES] = {0};
uint8_t m_bytes[VEC_K_SIZE_BYTES] = {0};
uint64_t m[VEC_K_SIZE_64] = {0};
uint8_t theta[SHA512_BYTES] = {0};
uint64_t u2[VEC_N_SIZE_64] = {0};
uint64_t v2[VEC_N1N2_SIZE_64] = {0};
unsigned char d2[SHA512_BYTES] = {0};
unsigned char mc[VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES] = {0};

// Retrieving u, v and d from ciphertext
PQCLEAN_HQC192_CLEAN_hqc_ciphertext_from_string(u, v, d, ct);

// Retrieving pk from sk
memcpy(pk, sk + SEED_BYTES, PUBLIC_KEY_BYTES);

// Decryting
PQCLEAN_HQC192_CLEAN_hqc_pke_decrypt(m, u, v, sk);
PQCLEAN_HQC192_CLEAN_store8_arr(m_bytes, VEC_K_SIZE_BYTES, m, VEC_K_SIZE_64);

// Computing theta
sha3_512(theta, m_bytes, VEC_K_SIZE_BYTES);

// Encrypting m'
PQCLEAN_HQC192_CLEAN_hqc_pke_encrypt(u2, v2, m, theta, pk);

// Computing d'
sha512(d2, m_bytes, VEC_K_SIZE_BYTES);

// Computing shared secret
memcpy(mc, m_bytes, VEC_K_SIZE_BYTES);
PQCLEAN_HQC192_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
PQCLEAN_HQC192_CLEAN_store8_arr(mc + VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
sha512(ss, mc, VEC_K_SIZE_BYTES + VEC_N_SIZE_BYTES + VEC_N1N2_SIZE_BYTES);

// Abort if c != c' or d != d'
result = PQCLEAN_HQC192_CLEAN_vect_compare((uint8_t *)u, (uint8_t *)u2, VEC_N_SIZE_BYTES);
result |= PQCLEAN_HQC192_CLEAN_vect_compare((uint8_t *)v, (uint8_t *)v2, VEC_N1N2_SIZE_BYTES);
result |= PQCLEAN_HQC192_CLEAN_vect_compare(d, d2, SHA512_BYTES);
result = (uint8_t) (-((int16_t) result) >> 15);
for (size_t i = 0; i < SHARED_SECRET_BYTES; i++) {
ss[i] &= ~result;
}


return -(result & 1);
}

+ 0
- 120
crypto_kem/hqc-192/clean/parameters.h View File

@@ -1,120 +0,0 @@
#ifndef HQC_PARAMETERS_H
#define HQC_PARAMETERS_H
/**
* @file parameters.h
* @brief Parameters of the HQC_KEM IND-CCA2 scheme
*/
#include "api.h"
#include "vector.h"


#define CEIL_DIVIDE(a, b) (((a)+(b)-1)/(b)) /*!< Divide a by b and ceil the result*/

/*
#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_SIZE_BYTES Define the size of the array used to store a PARAM_N sized vector in bytes
#define VEC_K_SIZE_BYTES Define the size of the array used to store a PARAM_K sized vector in bytes
#define VEC_N1_SIZE_BYTES Define the size of the array used to store a PARAM_N1 sized vector in bytes
#define VEC_N1N2_SIZE_BYTES Define the size of the array used to store a PARAM_N1N2 sized vector in bytes

#define VEC_N_SIZE_64 Define the size of the array used to store a PARAM_N sized vector in 64 bits
#define VEC_K_SIZE_64 Define the size of the array used to store a PARAM_K sized vector in 64 bits
#define VEC_N1_SIZE_64 Define the size of the array used to store a PARAM_N1 sized vector in 64 bits
#define VEC_N1N2_SIZE_64 Define the size of the array used to store a PARAM_N1N2 sized vector in 64 bits

#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_POLY Generator polynomial of galois field GF(2^PARAM_M), represented in hexadecimial form
#define PARAM_GF_MUL_ORDER Define the size of the multiplicative group of GF(2^PARAM_M), i.e 2^PARAM_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 RED_MASK A mask fot the higher bits of a vector
#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 45197
#define PARAM_N1 766
#define PARAM_N2 59
#define PARAM_N1N2 45194
#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_HQC192_CLEAN_CRYPTO_SECRETKEYBYTES
#define PUBLIC_KEY_BYTES PQCLEAN_HQC192_CLEAN_CRYPTO_PUBLICKEYBYTES
#define SHARED_SECRET_BYTES PQCLEAN_HQC192_CLEAN_CRYPTO_BYTES
#define CIPHERTEXT_BYTES PQCLEAN_HQC192_CLEAN_CRYPTO_CIPHERTEXTBYTES

#define UTILS_REJECTION_THRESHOLD 16768087
#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 VEC_N_SIZE_64 CEIL_DIVIDE(PARAM_N, 64)
#define VEC_K_SIZE_64 CEIL_DIVIDE(PARAM_K, 64)
#define VEC_N1_SIZE_64 CEIL_DIVIDE(PARAM_N1, 64)
#define VEC_N1N2_SIZE_64 CEIL_DIVIDE(PARAM_N1N2, 64)

#define PARAM_T 29

#define PARAM_DELTA 57
#define PARAM_M 10
#define PARAM_GF_POLY 0x409
#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 RED_MASK 0x0000000000001fffUL
#define SHA512_BYTES 64
#define SEED_BYTES 40
#define SEEDEXPANDER_MAX_LENGTH 4294967295

#endif

+ 0
- 186
crypto_kem/hqc-192/clean/parsing.c View File

@@ -1,186 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file parsing.c
* @brief Functions to parse secret key, public key and ciphertext of the HQC scheme
*/


void PQCLEAN_HQC192_CLEAN_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_HQC192_CLEAN_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_HQC192_CLEAN_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_HQC192_CLEAN_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_HQC192_CLEAN_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++;
}
}
}


/**
* @brief Parse a secret key into a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* 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_HQC192_CLEAN_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk) {
memcpy(sk, sk_seed, SEED_BYTES);
sk += SEED_BYTES;
memcpy(sk, pk, PUBLIC_KEY_BYTES);
}

/**
* @brief Parse a secret key from a string
*
* The secret key is composed of the seed used to generate vectors <b>x</b> and <b>y</b>.
* As technicality, the public key is appended to the secret key in order to respect NIST API.
*
* @param[out] x uint64_t representation of vector x
* @param[out] y uint32_t representation of vector y
* @param[out] pk String containing the public key
* @param[in] sk String containing the secret key
*/
void PQCLEAN_HQC192_CLEAN_hqc_secret_key_from_string(uint64_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);
sk += SEED_BYTES;
memcpy(pk, sk, PUBLIC_KEY_BYTES);

seedexpander_init(&sk_seedexpander, sk_seed, sk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight(&sk_seedexpander, x, PARAM_OMEGA);
PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight_by_coordinates(&sk_seedexpander, y, PARAM_OMEGA);
}

/**
* @brief Parse a public key into a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC192_CLEAN_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s) {
memcpy(pk, pk_seed, SEED_BYTES);
PQCLEAN_HQC192_CLEAN_store8_arr(pk + SEED_BYTES, VEC_N_SIZE_BYTES, s, VEC_N_SIZE_64);
}



/**
* @brief Parse a public key from a string
*
* The public key is composed of the syndrome <b>s</b> as well as the seed used to generate the vector <b>h</b>
*
* @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_HQC192_CLEAN_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk) {
AES_XOF_struct pk_seedexpander;
uint8_t pk_seed[SEED_BYTES] = {0};

memcpy(pk_seed, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_HQC192_CLEAN_load8_arr(s, VEC_N_SIZE_64, pk, VEC_N_SIZE_BYTES);

seedexpander_init(&pk_seedexpander, pk_seed, pk_seed + 32, SEEDEXPANDER_MAX_LENGTH);
PQCLEAN_HQC192_CLEAN_vect_set_random(&pk_seedexpander, h);
}


/**
* @brief Parse a ciphertext into a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC192_CLEAN_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d) {
PQCLEAN_HQC192_CLEAN_store8_arr(ct, VEC_N_SIZE_BYTES, u, VEC_N_SIZE_64);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC192_CLEAN_store8_arr(ct, VEC_N1N2_SIZE_BYTES, v, VEC_N1N2_SIZE_64);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(ct, d, SHA512_BYTES);
}


/**
* @brief Parse a ciphertext from a string
*
* The ciphertext is composed of vectors <b>u</b>, <b>v</b> and hash <b>d</b>.
*
* @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_HQC192_CLEAN_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct) {
PQCLEAN_HQC192_CLEAN_load8_arr(u, VEC_N_SIZE_64, ct, VEC_N_SIZE_BYTES);
ct += VEC_N_SIZE_BYTES;
PQCLEAN_HQC192_CLEAN_load8_arr(v, VEC_N1N2_SIZE_64, ct, VEC_N1N2_SIZE_BYTES);
ct += VEC_N1N2_SIZE_BYTES;
memcpy(d, ct, SHA512_BYTES);
}

+ 0
- 36
crypto_kem/hqc-192/clean/parsing.h View File

@@ -1,36 +0,0 @@
#ifndef PARSING_H
#define PARSING_H


/**
* @file parsing.h
* @brief Header file for parsing.c
*/

#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_store8(unsigned char *out, uint64_t in);

uint64_t PQCLEAN_HQC192_CLEAN_load8(const unsigned char *in);

void PQCLEAN_HQC192_CLEAN_load8_arr(uint64_t *out64, size_t outlen, const uint8_t *in8, size_t inlen);

void PQCLEAN_HQC192_CLEAN_store8_arr(uint8_t *out8, size_t outlen, const uint64_t *in64, size_t inlen);


void PQCLEAN_HQC192_CLEAN_hqc_secret_key_to_string(uint8_t *sk, const uint8_t *sk_seed, const uint8_t *pk);

void PQCLEAN_HQC192_CLEAN_hqc_secret_key_from_string(uint64_t *x, uint32_t *y, uint8_t *pk, const uint8_t *sk);


void PQCLEAN_HQC192_CLEAN_hqc_public_key_to_string(uint8_t *pk, const uint8_t *pk_seed, const uint64_t *s);

void PQCLEAN_HQC192_CLEAN_hqc_public_key_from_string(uint64_t *h, uint64_t *s, const uint8_t *pk);


void PQCLEAN_HQC192_CLEAN_hqc_ciphertext_to_string(uint8_t *ct, const uint64_t *u, const uint64_t *v, const uint8_t *d);

void PQCLEAN_HQC192_CLEAN_hqc_ciphertext_from_string(uint64_t *u, uint64_t *v, uint8_t *d, const uint8_t *ct);


#endif

+ 0
- 85
crypto_kem/hqc-192/clean/repetition.c View File

@@ -1,85 +0,0 @@
#include "parameters.h"
#include "repetition.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/**
* @file repetition.c
* @brief Implementation of repetition codes
*/

#define MASK_N2 ((((uint64_t) 1) << PARAM_N2) - 1)

static inline int32_t popcount(uint64_t n);

/**
* @brief Encoding each bit in the message m using the repetition code
*
*
* @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_HQC192_CLEAN_repetition_code_encode(uint64_t *em, const uint64_t *m) {
uint64_t bit, idx_r;
size_t pos_r;

pos_r = 0;
for (size_t i = 0; i < VEC_N1_SIZE_64; i++) {
for (size_t j = 0; j < 64 && pos_r < PARAM_N1N2; j++) {
bit = (uint64_t) (-(int64_t) ((m[i] >> j) & 1));
idx_r = (pos_r & 0x3f);
em[(pos_r >> 6) + 0] ^= (bit & 0x7FFFFFFFFFFFFFFUL) << idx_r;
em[(pos_r >> 6) + 1] ^= (bit & 0x3FFFFFFFFFFFFFFUL) >> ((63 - idx_r));
pos_r += PARAM_N2;
}
}
}



/**
* @brief Compute the Hamming weight of the 64-bit integer n
*
* The Hamming weight is computed using a trick described in
* Henry S. Warren : "Hacker's Delight", chap 5., p. 66
* @param[out] the Hamming weight of n
* @param[in] a 64-bit integer n
*/
static inline int32_t popcount(uint64_t n) {
n -= (n >> 1) & 0x5555555555555555UL;
n = (n & 0x3333333333333333UL) + ((n >> 2) & 0x3333333333333333UL);
n = (n + (n >> 4)) & 0x0f0f0f0f0f0f0f0fUL;
return (n * 0x0101010101010101UL) >> 56;
}



/**
* @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_HQC192_CLEAN_repetition_code_decode(uint64_t *m, const uint64_t *em) {
size_t t = 0;
uint32_t b, bn, bi, c, cn, ci;
uint64_t cx, ones;
uint64_t mask;

for (b = 0; b < PARAM_N1N2 - PARAM_N2 + 1; b += PARAM_N2) {
bn = b >> 6;
bi = b & 63;
c = b + PARAM_N2 - 1;
cn = c >> 6;
ci = c & 63;
cx = em[cn] << (63 - ci);
mask = (uint64_t) (-((int64_t) (cn ^ (bn + 1))) >> 63); // cn != bn+1
ones = popcount(((em[bn] >> bi) & MASK_N2) | (cx & ~mask));
m[t >> 6] |= (uint64_t) ((((PARAM_T - ones) >> 31) & 1) << (t & 63));
t++;
}
}

+ 0
- 17
crypto_kem/hqc-192/clean/repetition.h View File

@@ -1,17 +0,0 @@
#ifndef REPETITION_H
#define REPETITION_H


/**
* @file repetition.h
* @brief Header file for repetition.c
*/

#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_repetition_code_encode(uint64_t *em, const uint64_t *m);

void PQCLEAN_HQC192_CLEAN_repetition_code_decode(uint64_t *m, const uint64_t *em);


#endif

+ 0
- 176
crypto_kem/hqc-192/clean/vector.c View File

@@ -1,176 +0,0 @@
#include "nistseedexpander.h"
#include "parameters.h"
#include "parsing.h"
#include "randombytes.h"
#include "vector.h"
#include <stdint.h>
#include <string.h>
/**
* @file vector.c
* @brief Implementation of vectors sampling and some utilities for the HQC scheme
*/


/**
* @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 <b>weight</b>. 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_HQC192_CLEAN_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
uint8_t inc;
size_t i, j;

i = 0;
j = random_bytes_size;
while (i < weight) {
do {
if (j == random_bytes_size) {
seedexpander(ctx, rand_bytes, random_bytes_size);
j = 0;
}

v[i] = ((uint32_t) rand_bytes[j++]) << 16;
v[i] |= ((uint32_t) rand_bytes[j++]) << 8;
v[i] |= rand_bytes[j++];

} while (v[i] >= UTILS_REJECTION_THRESHOLD);

v[i] = v[i] % PARAM_N;

inc = 1;
for (size_t k = 0; k < i; k++) {
if (v[k] == v[i]) {
inc = 0;
}
}
i += inc;
}
}



/**
* @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 <b>weight</b>.
* 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_HQC192_CLEAN_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight) {
uint32_t tmp[PARAM_OMEGA_R] = {0};

PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight_by_coordinates(ctx, tmp, weight);

for (size_t i = 0; i < weight; ++i) {
int32_t index = tmp[i] / 64;
int32_t pos = tmp[i] % 64;
v[index] |= ((uint64_t) 1) << pos;
}
}



/**
* @brief Generates a random vector of dimension <b>PARAM_N</b>
*
* This function generates a random binary vector of dimension <b>PARAM_N</b>. 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_HQC192_CLEAN_vect_set_random(AES_XOF_struct *ctx, uint64_t *v) {
uint8_t rand_bytes[VEC_N_SIZE_BYTES] = {0};

seedexpander(ctx, rand_bytes, VEC_N_SIZE_BYTES);

PQCLEAN_HQC192_CLEAN_load8_arr(v, VEC_N_SIZE_64, rand_bytes, VEC_N_SIZE_BYTES);
v[VEC_N_SIZE_64 - 1] &= RED_MASK;
}



/**
* @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_HQC192_CLEAN_vect_add(uint64_t *o, const uint64_t *v1, const uint64_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
*/
uint8_t PQCLEAN_HQC192_CLEAN_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size) {
uint64_t r = 0;
for (size_t i = 0; i < size; i++) {
r |= v1[i] ^ v2[i];
}
r = (~r + 1) >> 63;
return (uint8_t) r;
}



/**
* @brief Resize a vector so that it contains <b>size_o</b> 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_HQC192_CLEAN_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v) {
if (size_o < size_v) {
uint64_t mask = 0x7FFFFFFFFFFFFFFF;
int8_t val = 0;

if (size_o % 64) {
val = 64 - (size_o % 64);
}

memcpy(o, v, 8 * VEC_N1N2_SIZE_64);

for (int8_t i = 0; i < val; ++i) {
o[VEC_N1N2_SIZE_64 - 1] &= (mask >> i);
}
} else {
memcpy(o, v, 8 * CEIL_DIVIDE(size_v, 64));
}
}

+ 0
- 27
crypto_kem/hqc-192/clean/vector.h View File

@@ -1,27 +0,0 @@
#ifndef VECTOR_H
#define VECTOR_H


/**
* @file vector.h
* @brief Header file for vector.c
*/
#include "nistseedexpander.h"
#include "randombytes.h"
#include <stdint.h>

void PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight_by_coordinates(AES_XOF_struct *ctx, uint32_t *v, uint16_t weight);

void PQCLEAN_HQC192_CLEAN_vect_set_random_fixed_weight(AES_XOF_struct *ctx, uint64_t *v, uint16_t weight);

void PQCLEAN_HQC192_CLEAN_vect_set_random(AES_XOF_struct *ctx, uint64_t *v);


void PQCLEAN_HQC192_CLEAN_vect_add(uint64_t *o, const uint64_t *v1, const uint64_t *v2, uint32_t size);

uint8_t PQCLEAN_HQC192_CLEAN_vect_compare(const uint8_t *v1, const uint8_t *v2, uint32_t size);

void PQCLEAN_HQC192_CLEAN_vect_resize(uint64_t *o, uint32_t size_o, const uint64_t *v, uint32_t size_v);


#endif

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save