mirror of
https://github.com/luxfi/crypto.git
synced 2026-11-03 07:20:43 +00:00
Replaces the stub backend (ErrBackendNotWired) with a working byte-
for-byte NIST-compatible HQC implementation. Build tag hqc_pqclean
activates the cgo path; default builds remain stub for portability.
Vendored:
- PQClean HQC-128/192/256 clean reference C (2023-04-30 NIST
submission), public domain
- PQClean shared fips202 (SHA-3 / SHAKE)
- Per-mode/per-source shim TUs (36 total) to keep PQClean's
static helpers isolated and avoid duplicate-symbol collisions
- randombytes_shim.c wiring PQClean's randombytes() to a Go-side
io.Reader callback, mutex-protected for concurrent dispatch
Tests:
- TestKAT_HQC128/192/256 — all three NIST KAT vectors verified
byte-for-byte against META.yml nistkat-sha256
- TestRoundTrip_AllModes — keygen → encap → decap roundtrip,
shared secrets byte-equal in all 3 modes
- TestDeterminism_AllModes — same seed produces identical ct/ss
(load-bearing for the precompile's on-chain determinism)
- -race clean
Correction: ciphertext sizes updated from 4481/9026/14469 (older
HQC round-3 listing) to PQClean's 4433/8978/14421 (2023-04-30 NIST
submission); the prior numbers would have failed round-trip with
a 48-byte mismatch.
199 lines
4.9 KiB
C
199 lines
4.9 KiB
C
#include "gf2x.h"
|
|
#include "parameters.h"
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
/**
|
|
* @file gf2x.c
|
|
* @brief Implementation of multiplication of two polynomials
|
|
*/
|
|
|
|
|
|
/**
|
|
* @brief Caryless multiplication of two words of 64 bits
|
|
*
|
|
* Implemntation of the algorithm mul1 in https://hal.inria.fr/inria-00188261v4/document.
|
|
* With w = 64 and s = 4
|
|
*
|
|
* @param[out] c The result c = a * b
|
|
* @param[in] a The first value a
|
|
* @param[in] b The second value b
|
|
*/
|
|
static void base_mul(uint64_t *c, uint64_t a, uint64_t b) {
|
|
uint64_t h = 0;
|
|
uint64_t l = 0;
|
|
uint64_t g;
|
|
uint64_t u[16] = {0};
|
|
uint64_t mask_tab[4] = {0};
|
|
uint64_t tmp1, tmp2;
|
|
|
|
// Step 1
|
|
u[0] = 0;
|
|
u[1] = b & (((uint64_t)1 << (64 - 4)) - 1);
|
|
u[2] = u[1] << 1;
|
|
u[3] = u[2] ^ u[1];
|
|
u[4] = u[2] << 1;
|
|
u[5] = u[4] ^ u[1];
|
|
u[6] = u[3] << 1;
|
|
u[7] = u[6] ^ u[1];
|
|
u[8] = u[4] << 1;
|
|
u[9] = u[8] ^ u[1];
|
|
u[10] = u[5] << 1;
|
|
u[11] = u[10] ^ u[1];
|
|
u[12] = u[6] << 1;
|
|
u[13] = u[12] ^ u[1];
|
|
u[14] = u[7] << 1;
|
|
u[15] = u[14] ^ u[1];
|
|
|
|
g = 0;
|
|
tmp1 = a & 0x0f;
|
|
|
|
for (size_t i = 0; i < 16; ++i) {
|
|
tmp2 = tmp1 - i;
|
|
g ^= (u[i] & (uint64_t)(0 - (1 - ((uint64_t)(tmp2 | (0 - tmp2)) >> 63))));
|
|
}
|
|
|
|
l = g;
|
|
h = 0;
|
|
|
|
// Step 2
|
|
for (size_t i = 4; i < 64; i += 4) {
|
|
g = 0;
|
|
tmp1 = (a >> i) & 0x0f;
|
|
for (size_t j = 0; j < 16; ++j) {
|
|
tmp2 = tmp1 - j;
|
|
g ^= (u[j] & (uint64_t)(0 - (1 - ((uint64_t)(tmp2 | (0 - tmp2)) >> 63))));
|
|
}
|
|
|
|
l ^= g << i;
|
|
h ^= g >> (64 - i);
|
|
}
|
|
|
|
// Step 3
|
|
mask_tab [0] = 0 - ((b >> 60) & 1);
|
|
mask_tab [1] = 0 - ((b >> 61) & 1);
|
|
mask_tab [2] = 0 - ((b >> 62) & 1);
|
|
mask_tab [3] = 0 - ((b >> 63) & 1);
|
|
|
|
l ^= ((a << 60) & mask_tab[0]);
|
|
h ^= ((a >> 4) & mask_tab[0]);
|
|
|
|
l ^= ((a << 61) & mask_tab[1]);
|
|
h ^= ((a >> 3) & mask_tab[1]);
|
|
|
|
l ^= ((a << 62) & mask_tab[2]);
|
|
h ^= ((a >> 2) & mask_tab[2]);
|
|
|
|
l ^= ((a << 63) & mask_tab[3]);
|
|
h ^= ((a >> 1) & mask_tab[3]);
|
|
|
|
c[0] = l;
|
|
c[1] = h;
|
|
}
|
|
|
|
static void karatsuba_add1(uint64_t *alh, uint64_t *blh, const uint64_t *a, const uint64_t *b, size_t size_l, size_t size_h) {
|
|
for (size_t i = 0; i < size_h; ++i) {
|
|
alh[i] = a[i] ^ a[i + size_l];
|
|
blh[i] = b[i] ^ b[i + size_l];
|
|
}
|
|
|
|
if (size_h < size_l) {
|
|
alh[size_h] = a[size_h];
|
|
blh[size_h] = b[size_h];
|
|
}
|
|
}
|
|
|
|
static void karatsuba_add2(uint64_t *o, uint64_t *tmp1, const uint64_t *tmp2, size_t size_l, size_t size_h) {
|
|
for (size_t i = 0; i < (2 * size_l); ++i) {
|
|
tmp1[i] = tmp1[i] ^ o[i];
|
|
}
|
|
|
|
for (size_t i = 0; i < ( 2 * size_h); ++i) {
|
|
tmp1[i] = tmp1[i] ^ tmp2[i];
|
|
}
|
|
|
|
for (size_t i = 0; i < (2 * size_l); ++i) {
|
|
o[i + size_l] = o[i + size_l] ^ tmp1[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Karatsuba multiplication of a and b, Implementation inspired from the NTL library.
|
|
*
|
|
* @param[out] o Polynomial
|
|
* @param[in] a Polynomial
|
|
* @param[in] b Polynomial
|
|
* @param[in] size Length of polynomial
|
|
* @param[in] stack Length of polynomial
|
|
*/
|
|
static void karatsuba(uint64_t *o, const uint64_t *a, const uint64_t *b, size_t size, uint64_t *stack) {
|
|
size_t size_l, size_h;
|
|
const uint64_t *ah, *bh;
|
|
|
|
if (size == 1) {
|
|
base_mul(o, a[0], b[0]);
|
|
return;
|
|
}
|
|
|
|
size_h = size / 2;
|
|
size_l = (size + 1) / 2;
|
|
|
|
uint64_t *alh = stack;
|
|
uint64_t *blh = alh + size_l;
|
|
uint64_t *tmp1 = blh + size_l;
|
|
uint64_t *tmp2 = o + 2 * size_l;
|
|
|
|
stack += 4 * size_l;
|
|
|
|
ah = a + size_l;
|
|
bh = b + size_l;
|
|
|
|
karatsuba(o, a, b, size_l, stack);
|
|
|
|
karatsuba(tmp2, ah, bh, size_h, stack);
|
|
|
|
karatsuba_add1(alh, blh, a, b, size_l, size_h);
|
|
|
|
karatsuba(tmp1, alh, blh, size_l, stack);
|
|
|
|
karatsuba_add2(o, tmp1, tmp2, size_l, size_h);
|
|
}
|
|
|
|
/**
|
|
* @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) {
|
|
uint64_t r;
|
|
uint64_t carry;
|
|
|
|
for (size_t i = 0; i < VEC_N_SIZE_64; ++i) {
|
|
r = a[i + VEC_N_SIZE_64 - 1] >> (PARAM_N & 0x3F);
|
|
carry = a[i + VEC_N_SIZE_64] << (64 - (PARAM_N & 0x3F));
|
|
o[i] = a[i] ^ r ^ carry;
|
|
}
|
|
|
|
o[VEC_N_SIZE_64 - 1] &= RED_MASK;
|
|
}
|
|
|
|
/**
|
|
* @brief Multiply two polynomials modulo \f$ X^n - 1\f$.
|
|
*
|
|
* This functions multiplies polynomials <b>v1</b> and <b>v2</b>.
|
|
* The multiplication is done modulo \f$ X^n - 1\f$.
|
|
*
|
|
* @param[out] o Product of <b>v1</b> and <b>v2</b>
|
|
* @param[in] v1 Pointer to the first polynomial
|
|
* @param[in] v2 Pointer to the second polynomial
|
|
*/
|
|
void PQCLEAN_HQC192_CLEAN_vect_mul(uint64_t *o, const uint64_t *v1, const uint64_t *v2) {
|
|
uint64_t stack[VEC_N_SIZE_64 << 3] = {0};
|
|
uint64_t o_karat[VEC_N_SIZE_64 << 1] = {0};
|
|
|
|
karatsuba(o_karat, v1, v2, VEC_N_SIZE_64, stack);
|
|
reduce(o, o_karat);
|
|
}
|