#include "gf2x_arith_mod_xPplusOne.h" #include "rng.h" #include "sort.h" #include // memcpy(...), memset(...) void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_copy(DIGIT dest[], const DIGIT in[]) { for (size_t i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { dest[i] = in[i]; } } /* returns the coefficient of the x^exponent term as the LSB of a digit */ DIGIT PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_get_coeff(const DIGIT poly[], size_t exponent) { size_t straightIdx = (NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_b - 1) - exponent; size_t digitIdx = straightIdx / DIGIT_SIZE_b; size_t inDigitIdx = straightIdx % DIGIT_SIZE_b; return (poly[digitIdx] >> (DIGIT_SIZE_b - 1 - inDigitIdx)) & ((DIGIT) 1) ; } /* sets the coefficient of the x^exponent term as the LSB of a digit */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_set_coeff(DIGIT poly[], size_t exponent, DIGIT value) { size_t straightIdx = (NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_b - 1) - exponent; size_t digitIdx = straightIdx / DIGIT_SIZE_b; size_t inDigitIdx = straightIdx % DIGIT_SIZE_b; /* clear given coefficient */ DIGIT mask = ~(((DIGIT) 1) << (DIGIT_SIZE_b - 1 - inDigitIdx)); poly[digitIdx] = poly[digitIdx] & mask; poly[digitIdx] = poly[digitIdx] | ((value & ((DIGIT) 1)) << (DIGIT_SIZE_b - 1 - inDigitIdx)); } /* toggles (flips) the coefficient of the x^exponent term as the LSB of a digit */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_toggle_coeff(DIGIT poly[], size_t exponent) { size_t straightIdx = (NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_b - 1) - exponent; size_t digitIdx = straightIdx / DIGIT_SIZE_b; size_t inDigitIdx = straightIdx % DIGIT_SIZE_b; /* clear given coefficient */ DIGIT mask = (((DIGIT) 1) << (DIGIT_SIZE_b - 1 - inDigitIdx)); poly[digitIdx] = poly[digitIdx] ^ mask; } /* population count for an unsigned 64-bit integer Source: Hacker's delight, p.66 */ static int popcount_uint64t(uint64_t x) { x -= (x >> 1) & 0x5555555555555555; x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f; return (int)((x * 0x0101010101010101) >> 56); } /* population count for a single polynomial */ int PQCLEAN_LEDAKEMLT12_LEAKTIME_population_count(const DIGIT *poly) { int ret = 0; for (int i = NUM_DIGITS_GF2X_ELEMENT - 1; i >= 0; i--) { ret += popcount_uint64t(poly[i]); } return ret; } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_add(DIGIT Res[], const DIGIT A[], const DIGIT B[]) { PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_add(Res, A, B, NUM_DIGITS_GF2X_ELEMENT); } static void gf2x_mod(DIGIT out[], const DIGIT in[]) { DIGIT aux[NUM_DIGITS_GF2X_ELEMENT + 1]; memcpy(aux, in, (NUM_DIGITS_GF2X_ELEMENT + 1)*DIGIT_SIZE_B); PQCLEAN_LEDAKEMLT12_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT + 1, aux, MSb_POSITION_IN_MSB_DIGIT_OF_MODULUS); PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_add(out, aux + 1, in + NUM_DIGITS_GF2X_ELEMENT, NUM_DIGITS_GF2X_ELEMENT); out[0] &= ((DIGIT)1 << MSb_POSITION_IN_MSB_DIGIT_OF_MODULUS) - 1; } static void right_bit_shift(size_t length, DIGIT in[]) { size_t j; for (j = length - 1; j > 0; j--) { in[j] >>= 1; in[j] |= (in[j - 1] & (DIGIT)0x01) << (DIGIT_SIZE_b - 1); } in[j] >>= 1; } /* shifts by whole digits */ static void left_DIGIT_shift_n(size_t length, DIGIT in[], size_t amount) { size_t j; for (j = 0; (j + amount) < length; j++) { in[j] = in[j + amount]; } for (; j < length; j++) { in[j] = (DIGIT)0; } } /* may shift by an arbitrary amount*/ static void left_bit_shift_wide_n(size_t length, DIGIT in[], size_t amount) { left_DIGIT_shift_n(length, in, amount / DIGIT_SIZE_b); PQCLEAN_LEDAKEMLT12_LEAKTIME_left_bit_shift_n(length, in, amount % DIGIT_SIZE_b); } /* Hackers delight, reverses a uint64_t */ static DIGIT reverse_digit(DIGIT x) { uint64_t t; x = (x << 31) | (x >> 33); t = (x ^ (x >> 20)) & 0x00000FFF800007FFLL; x = (t | (t << 20)) ^ x; t = (x ^ (x >> 8)) & 0x00F8000F80700807LL; x = (t | (t << 8)) ^ x; t = (x ^ (x >> 4)) & 0x0808708080807008LL; x = (t | (t << 4)) ^ x; t = (x ^ (x >> 2)) & 0x1111111111111111LL; x = (t | (t << 2)) ^ x; return x; } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_transpose_in_place(DIGIT A[]) { /* it keeps the lsb in the same position and * inverts the sequence of the remaining bits */ DIGIT mask = (DIGIT)0x1; DIGIT rev1, rev2, a00; int slack_bits_amount = NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_b - P; a00 = A[NUM_DIGITS_GF2X_ELEMENT - 1] & mask; right_bit_shift(NUM_DIGITS_GF2X_ELEMENT, A); for (size_t i = NUM_DIGITS_GF2X_ELEMENT - 1; i >= (NUM_DIGITS_GF2X_ELEMENT + 1) / 2; i--) { rev1 = reverse_digit(A[i]); rev2 = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT - 1 - i]); A[i] = rev2; A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1; } A[NUM_DIGITS_GF2X_ELEMENT / 2] = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT / 2]); if (slack_bits_amount) { PQCLEAN_LEDAKEMLT12_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount); } A[NUM_DIGITS_GF2X_ELEMENT - 1] = (A[NUM_DIGITS_GF2X_ELEMENT - 1] & (~mask)) | a00; } static void rotate_bit_right(DIGIT in[]) { /* x^{-1} * in(x) mod x^P+1 */ DIGIT rotated_bit = in[NUM_DIGITS_GF2X_ELEMENT - 1] & ((DIGIT)0x1); right_bit_shift(NUM_DIGITS_GF2X_ELEMENT, in); int msb_offset_in_digit = MSb_POSITION_IN_MSB_DIGIT_OF_MODULUS - 1; rotated_bit = rotated_bit << msb_offset_in_digit; in[0] |= rotated_bit; } /* cond swap: swaps digits A and B if swap_mask == -1 */ static void gf2x_cswap(DIGIT *a, DIGIT *b, int32_t swap_mask) { DIGIT t; for (size_t i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { t = swap_mask & (a[i] ^ b[i]); a[i] ^= t; b[i] ^= t; } } /* returns -1 mask if x != 0, otherwise 0 */ static inline int32_t nonzero(DIGIT x) { DIGIT t = x; t = (~t) + 1; t >>= DIGIT_SIZE_b - 1; return -((int32_t)t); } /* returns -1 mask if x < 0 else 0 */ static inline int32_t negative(int x) { uint32_t u = x; u >>= 31; return -((int32_t)u); } /* return f(0) as digit */ static inline DIGIT lsb(const DIGIT *p) { DIGIT mask = (DIGIT)1; return p[NUM_DIGITS_GF2X_ELEMENT - 1] & mask; } /* multiply poly with scalar and accumulate, expects s all-zero of all-one mask */ static void gf2x_mult_scalar_acc(DIGIT *f, const DIGIT *g, const DIGIT s) { for (size_t i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { f[i] = f[i] ^ (s & g[i]); } } /* constant-time inverse, source: gcd.cr.yp.to */ int PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_inverse(DIGIT out[], const DIGIT in[]) { int32_t swap, delta = 1; DIGIT g0_mask; DIGIT f[NUM_DIGITS_GF2X_MODULUS] = {0}; // f = x^P + 1 DIGIT g[NUM_DIGITS_GF2X_ELEMENT]; // g = in DIGIT *v = out; // v = 0, save space DIGIT r[NUM_DIGITS_GF2X_ELEMENT] = {0}; // r = 1 f[NUM_DIGITS_GF2X_MODULUS - 1] = 1; f[0] |= ((DIGIT)1 << MSb_POSITION_IN_MSB_DIGIT_OF_MODULUS); for (size_t i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { g[i] = in[i]; } for (size_t i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { v[i] = 0; } r[NUM_DIGITS_GF2X_ELEMENT - 1] = 1; for (int loop = 0; loop < 2 * P - 1; ++loop) { swap = negative(-delta) & nonzero(lsb(g)); // swap = -1 if -delta < 0 AND g(0) != 0 delta ^= swap & (delta ^ -delta); // cond swap delta with -delta if swap delta++; gf2x_cswap(f, g, swap); gf2x_cswap(v, r, swap); g0_mask = ~lsb(g) + 1; // g = (g - g0 * f) / x gf2x_mult_scalar_acc(g, f, g0_mask); right_bit_shift(NUM_DIGITS_GF2X_ELEMENT, g); // r = (r - g0 * v) / x gf2x_mult_scalar_acc(r, v, g0_mask); rotate_bit_right(r); } return nonzero(delta); // -1 if fail, 0 if success } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_mul(DIGIT Res[], const DIGIT A[], const DIGIT B[]) { DIGIT aux[2 * NUM_DIGITS_GF2X_ELEMENT]; PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mul(aux, A, B, NUM_DIGITS_GF2X_ELEMENT); gf2x_mod(Res, aux); } /*PRE: the representation of the sparse coefficients is sorted in increasing order of the coefficients themselves */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_mul_dense_to_sparse(DIGIT Res[], const DIGIT dense[], POSITION_T sparse[], size_t nPos) { DIGIT aux[2 * NUM_DIGITS_GF2X_ELEMENT] = {0x00}; DIGIT resDouble[2 * NUM_DIGITS_GF2X_ELEMENT] = {0x00}; memcpy(aux + NUM_DIGITS_GF2X_ELEMENT, dense, NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_B); memcpy(resDouble + NUM_DIGITS_GF2X_ELEMENT, dense, NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_B); if (sparse[0] != INVALID_POS_VALUE) { left_bit_shift_wide_n(2 * NUM_DIGITS_GF2X_ELEMENT, resDouble, sparse[0]); left_bit_shift_wide_n(2 * NUM_DIGITS_GF2X_ELEMENT, aux, sparse[0]); for (size_t i = 1; i < nPos; i++) { if (sparse[i] != INVALID_POS_VALUE) { left_bit_shift_wide_n(2 * NUM_DIGITS_GF2X_ELEMENT, aux, (sparse[i] - sparse[i - 1]) ); PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_add(resDouble, aux, resDouble, 2 * NUM_DIGITS_GF2X_ELEMENT); } } } gf2x_mod(Res, resDouble); } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_transpose_in_place_sparse(size_t sizeA, POSITION_T A[]) { POSITION_T t; size_t i = 0, j; if (A[i] == 0) { i = 1; } j = i; for (; i < sizeA && A[i] != INVALID_POS_VALUE; i++) { A[i] = P - A[i]; } for (i -= 1; j < i; j++, i--) { t = A[j]; A[j] = A[i]; A[i] = t; } } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_mul_sparse(size_t sizeR, POSITION_T Res[], size_t sizeA, const POSITION_T A[], size_t sizeB, const POSITION_T B[]) { POSITION_T prod; POSITION_T lastReadPos; size_t duplicateCount; size_t write_idx, read_idx; /* compute all the coefficients, filling invalid positions with P*/ size_t lastFilledPos = 0; for (size_t i = 0 ; i < sizeA ; i++) { for (size_t j = 0 ; j < sizeB ; j++) { prod = A[i] + B[j]; prod = ( (prod >= P) ? prod - P : prod); if ((A[i] != INVALID_POS_VALUE) && (B[j] != INVALID_POS_VALUE)) { Res[lastFilledPos] = prod; } else { Res[lastFilledPos] = INVALID_POS_VALUE; } lastFilledPos++; } } while (lastFilledPos < sizeR) { Res[lastFilledPos] = INVALID_POS_VALUE; lastFilledPos++; } PQCLEAN_LEDAKEMLT12_LEAKTIME_uint32_sort(Res, sizeR); /* eliminate duplicates */ write_idx = read_idx = 0; while (read_idx < sizeR && Res[read_idx] != INVALID_POS_VALUE) { lastReadPos = Res[read_idx]; read_idx++; duplicateCount = 1; while ( (Res[read_idx] == lastReadPos) && (Res[read_idx] != INVALID_POS_VALUE)) { read_idx++; duplicateCount++; } if (duplicateCount % 2) { Res[write_idx] = lastReadPos; write_idx++; } } /* fill remaining cells with INVALID_POS_VALUE */ for (; write_idx < sizeR; write_idx++) { Res[write_idx] = INVALID_POS_VALUE; } } /* the implementation is safe even in case A or B alias with the result * PRE: A and B should be sorted, disjunct arrays ending with INVALID_POS_VALUE */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_mod_add_sparse(size_t sizeR, POSITION_T Res[], size_t sizeA, const POSITION_T A[], size_t sizeB, const POSITION_T B[]) { POSITION_T tmpRes[DV * M]; size_t idxA = 0, idxB = 0, idxR = 0; while ( idxA < sizeA && idxB < sizeB && A[idxA] != INVALID_POS_VALUE && B[idxB] != INVALID_POS_VALUE ) { if (A[idxA] == B[idxB]) { idxA++; idxB++; } else { if (A[idxA] < B[idxB]) { tmpRes[idxR] = A[idxA]; idxA++; } else { tmpRes[idxR] = B[idxB]; idxB++; } idxR++; } } while (idxA < sizeA && A[idxA] != INVALID_POS_VALUE) { tmpRes[idxR] = A[idxA]; idxA++; idxR++; } while (idxB < sizeB && B[idxB] != INVALID_POS_VALUE) { tmpRes[idxR] = B[idxB]; idxB++; idxR++; } while (idxR < sizeR) { tmpRes[idxR] = INVALID_POS_VALUE; idxR++; } memcpy(Res, tmpRes, sizeof(POSITION_T)*sizeR); } /* Return a uniform random value in the range 0..n-1 inclusive, * applying a rejection sampling strategy and exploiting as a random source * the NIST seedexpander seeded with the proper key. * Assumes that the maximum value for the range n is 2^32-1 */ static uint32_t rand_range(const unsigned int n, const int logn, AES_XOF_struct *seed_expander_ctx) { unsigned long required_rnd_bytes = (logn + 7) / 8; unsigned char rnd_char_buffer[4]; uint32_t rnd_value; uint32_t mask = ( (uint32_t)1 << logn) - 1; do { PQCLEAN_LEDAKEMLT12_LEAKTIME_seedexpander(seed_expander_ctx, rnd_char_buffer, required_rnd_bytes); /* obtain an endianness independent representation of the generated random bytes into an unsigned integer */ rnd_value = ((uint32_t)rnd_char_buffer[3] << 24) + ((uint32_t)rnd_char_buffer[2] << 16) + ((uint32_t)rnd_char_buffer[1] << 8) + ((uint32_t)rnd_char_buffer[0] << 0) ; rnd_value = mask & rnd_value; } while (rnd_value >= n); return rnd_value; } /* Obtains fresh randomness and seed-expands it until all the required positions * for the '1's in the circulant block are obtained */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_rand_circulant_sparse_block(POSITION_T *pos_ones, size_t countOnes, AES_XOF_struct *seed_expander_ctx) { size_t duplicated, placedOnes = 0; POSITION_T p; while (placedOnes < countOnes) { p = rand_range(NUM_BITS_GF2X_ELEMENT, P_BITS, seed_expander_ctx); duplicated = 0; for (size_t j = 0; j < placedOnes; j++) { if (pos_ones[j] == p) { duplicated = 1; } } if (duplicated == 0) { pos_ones[placedOnes] = p; placedOnes++; } } } /* Returns random weight-t circulant block */ void PQCLEAN_LEDAKEMLT12_LEAKTIME_rand_circulant_blocks_sequence(DIGIT sequence[N0 * NUM_DIGITS_GF2X_ELEMENT], AES_XOF_struct *seed_expander_ctx) { size_t polyIndex, duplicated, counter = 0; POSITION_T p, exponent, rndPos[NUM_ERRORS_T]; memset(sequence, 0x00, N0 * NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_B); while (counter < NUM_ERRORS_T) { p = rand_range(N0 * NUM_BITS_GF2X_ELEMENT, P_BITS, seed_expander_ctx); duplicated = 0; for (size_t j = 0; j < counter; j++) { if (rndPos[j] == p) { duplicated = 1; } } if (duplicated == 0) { rndPos[counter] = p; counter++; } } for (size_t j = 0; j < counter; j++) { polyIndex = rndPos[j] / P; exponent = rndPos[j] % P; PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_set_coeff( sequence + NUM_DIGITS_GF2X_ELEMENT * polyIndex, exponent, ( (DIGIT) 1)); } } void PQCLEAN_LEDAKEMLT12_LEAKTIME_rand_error_pos(POSITION_T errorPos[NUM_ERRORS_T], AES_XOF_struct *seed_expander_ctx) { int duplicated; size_t counter = 0; while (counter < NUM_ERRORS_T) { POSITION_T p = rand_range(N0 * NUM_BITS_GF2X_ELEMENT, P_BITS, seed_expander_ctx); duplicated = 0; for (size_t j = 0; j < counter; j++) { if (errorPos[j] == p) { duplicated = 1; } } if (duplicated == 0) { errorPos[counter] = p; counter++; } } } void PQCLEAN_LEDAKEMLT12_LEAKTIME_expand_error(DIGIT sequence[N0 * NUM_DIGITS_GF2X_ELEMENT], const POSITION_T errorPos[NUM_ERRORS_T]) { size_t polyIndex; POSITION_T exponent; memset(sequence, 0x00, N0 * NUM_DIGITS_GF2X_ELEMENT * DIGIT_SIZE_B); for (int j = 0; j < NUM_ERRORS_T; j++) { polyIndex = errorPos[j] / P; exponent = errorPos[j] % P; PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_set_coeff( sequence + NUM_DIGITS_GF2X_ELEMENT * polyIndex, exponent, ( (DIGIT) 1)); } } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_tobytes(uint8_t *bytes, const DIGIT *poly) { size_t i, j; for (i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { for (j = 0; j < DIGIT_SIZE_B; j++) { bytes[i * DIGIT_SIZE_B + j] = (uint8_t) (poly[i] >> 8 * j); } } } void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_frombytes(DIGIT *poly, const uint8_t *poly_bytes) { size_t i, j; for (i = 0; i < NUM_DIGITS_GF2X_ELEMENT; i++) { poly[i] = (DIGIT) 0; for (j = 0; j < DIGIT_SIZE_B; j++) { poly[i] |= (DIGIT) poly_bytes[i * DIGIT_SIZE_B + j] << 8 * j; } } }