KpqC-0.1.0: cbits/third_party/HAETAE/src/poly.c
// SPDX-License-Identifier: MIT
#include "poly.h"
#include "decompose.h"
#include "ntt.h"
#include "params.h"
#include "reduce.h"
#include "sampler.h"
#include "symmetric.h"
#include <stdint.h>
/*************************************************
* Name: poly_add
*
* Description: Add polynomials. No modular reduction is performed.
*
* Arguments: - poly *c: pointer to output polynomial
* - const poly *a: pointer to first summand
* - const poly *b: pointer to second summand
**************************************************/
void poly_add(poly *c, const poly *a, const poly *b) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
c->coeffs[i] = a->coeffs[i] + b->coeffs[i];
}
/*************************************************
* Name: poly_sub
*
* Description: Subtract polynomials. No modular reduction is
* performed.
*
* Arguments: - poly *c: pointer to output polynomial
* - const poly *a: pointer to first input polynomial
* - const poly *b: pointer to second input polynomial to be
* subtraced from first input polynomial
**************************************************/
void poly_sub(poly *c, const poly *a, const poly *b) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
c->coeffs[i] = a->coeffs[i] - b->coeffs[i];
}
/*************************************************
* Name: poly_pointwise_montgomery
*
* Description: Pointwise multiplication of polynomials in NTT domain
* representation and multiplication of resulting polynomial
* by 2^{-32}.
*
* Arguments: - poly *c: pointer to output polynomial
* - const poly *a: pointer to first input polynomial
* - const poly *b: pointer to second input polynomial
**************************************************/
void poly_pointwise_montgomery(poly *c, const poly *a, const poly *b) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
c->coeffs[i] = montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
}
/*************************************************
* Name: poly_reduce2q
*
* Description: Inplace reduction of all coefficients of polynomial to 2q
*
* Arguments: - poly *a: pointer to input/output polynomial
**************************************************/
void poly_reduce2q(poly *a) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
a->coeffs[i] = reduce32_2q(a->coeffs[i]);
}
/*************************************************
* Name: poly_freeze2q
*
* Description: For all coefficients of in/out polynomial compute standard
* representative r = a mod^+ 2Q
*
* Arguments: - poly *a: pointer to input/output polynomial
**************************************************/
void poly_freeze2q(poly *a) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
a->coeffs[i] = freeze2q(a->coeffs[i]);
}
/*************************************************
* Name: poly_freeze
*
* Description: For all coefficients of in/out polynomial compute standard
* representative r = a mod^+ HAETAE_Q
*
* Arguments: - poly *a: pointer to input/output polynomial
**************************************************/
void poly_freeze(poly *a) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
a->coeffs[i] = freeze(a->coeffs[i]);
}
/*************************************************
* Name: poly_highbits
*
* Description: Compute HighBits of polynomial
*
* Arguments: - poly *a2: pointer to output polynomial
* - const poly *a: pointer to input polynomial
**************************************************/
void poly_highbits(poly *a2, const poly *a) {
unsigned int i;
int32_t a1tmp;
for (i = 0; i < HAETAE_N; ++i)
decompose_z1(&a2->coeffs[i], &a1tmp, a->coeffs[i]);
}
/*************************************************
* Name: poly_lowbits
*
* Description: Compute LowBits of polynomial
*
* Arguments: - poly *a1: pointer to output polynomial
* - const poly *a: pointer to input polynomial
**************************************************/
void poly_lowbits(poly *a1, const poly *a) {
unsigned int i = 0;
int32_t a2tmp = 0;
for (i = 0; i < HAETAE_N; ++i)
decompose_z1(&a2tmp, &a1->coeffs[i], a->coeffs[i]);
}
/*************************************************
* Name: poly_compose
*
* Description: Compose HighBits and LowBits to recreate the polynomial
*
* Arguments: - poly *a3: pointer to output polynomial
* - const poly *ha: pointer to HighBits polynomial
* - const poly *la: pointer to HighBits polynomial
**************************************************/
void poly_compose(poly *a, const poly *ha, const poly *la) {
unsigned int i = 0;
for (i = 0; i < HAETAE_N; ++i)
a->coeffs[i] = (ha->coeffs[i] * 256) + la->coeffs[i];
}
/*************************************************
* Name: poly_lsb
*
* Description: Compute least significant bits of polynomial
*
* Arguments: - poly *a0: pointer to output polynomial
* - const poly *a: pointer to input polynomial
**************************************************/
void poly_lsb(poly *a0, const poly *a) {
unsigned int i;
for (i = 0; i < HAETAE_N; ++i)
a0->coeffs[i] = a->coeffs[i] & 1;
}
/*************************************************
* Name: poly_uniform
*
* Description: Sample polynomial with uniformly random coefficients
* in [0,Q-1] by performing rejection sampling on the
* output stream of SHAKE128(seed|nonce)
*
* Arguments: - poly *a: pointer to output polynomial
* - const uint8_t seed[]: byte array with seed of length
* HAETAE_SEEDBYTES
* - uint16_t nonce: 2-byte nonce
*
* Specification: Implements @[Algorithm 8, PolyUniform]
**************************************************/
#define POLY_UNIFORM_NBLOCKS \
((512 + STREAM128_BLOCKBYTES - 1) / STREAM128_BLOCKBYTES)
// HAETAE_N * 2(random bytes for [0, HAETAE_Q - 1])
void poly_uniform(poly *a, const uint8_t seed[HAETAE_SEEDBYTES],
uint16_t nonce) {
unsigned int i, ctr, off;
unsigned int buflen = POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES;
uint8_t buf[POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES + 1];
stream128_state state;
stream128_init(&state, seed, nonce);
stream128_squeezeblocks(buf, POLY_UNIFORM_NBLOCKS, &state);
ctr = rej_uniform(a->coeffs, HAETAE_N, buf, buflen);
while (ctr < HAETAE_N) {
off = buflen % 2;
for (i = 0; i < off; ++i)
buf[i] = buf[buflen - off + i];
stream128_squeezeblocks(buf + off, 1, &state);
buflen = STREAM128_BLOCKBYTES + off;
ctr += rej_uniform(a->coeffs + ctr, HAETAE_N - ctr, buf, buflen);
}
}
/*************************************************
* Name: poly_uniform_eta
*
* Description: Sample polynomial with uniformly random coefficients
* in [-ETA,ETA] by performing rejection sampling on the
* output stream from SHAKE256(seed|nonce)
*
* Arguments: - poly *a: pointer to output polynomial
* - const uint8_t seed[]: byte array with seed of length
* HAETAE_CRHBYTES
* - uint16_t nonce: 2-byte nonce
*
* Specification: Implements @[Algorithm 11, PolyUniformEta]
**************************************************/
#define POLY_UNIFORM_ETA_NBLOCKS \
((136 + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES) // 1
void poly_uniform_eta(poly *a, const uint8_t seed[HAETAE_CRHBYTES],
uint16_t nonce) {
unsigned int ctr;
unsigned int buflen = POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES;
uint8_t buf[POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES];
stream256_state state;
stream256_init(&state, seed, nonce);
stream256_squeezeblocks(buf, POLY_UNIFORM_ETA_NBLOCKS, &state);
ctr = rej_eta(a->coeffs, HAETAE_N, buf, buflen);
while (ctr < HAETAE_N) {
stream256_squeezeblocks(buf, 1, &state);
ctr += rej_eta(a->coeffs + ctr, HAETAE_N - ctr, buf, STREAM256_BLOCKBYTES);
}
}
uint8_t hammingWeight_8(uint8_t x) {
x = (x & 0x55) + (x >> 1 & 0x55);
x = (x & 0x33) + (x >> 2 & 0x33);
x = (x & 0x0F) + (x >> 4 & 0x0F);
return x;
}
/*************************************************
* Name: poly_challenge
*
* Description: Implementation of challenge. Samples polynomial with HAETAE_TAU
* 1 coefficients using the output stream of SHAKE256(seed).
*
* Arguments: - poly *c: pointer to output polynomial
* - const uint8_t highbits_lsb[]: packed highbits and lsb
* - const uint8_t mu[]: hash of vk and message
*
* Specification: Implements @[Algorithm 18, SampleChallenge]
**************************************************/
void poly_challenge(
poly *c,
const uint8_t highbits_lsb[HAETAE_POLYVECK_HIGHBITS_PACKEDBYTES +
HAETAE_POLYC_PACKEDBYTES],
const uint8_t mu[HAETAE_SEEDBYTES]) {
#if (HAETAE_MODE == HAETAE_MODE2) || (HAETAE_MODE == HAETAE_MODE3)
unsigned int i, b, pos = 0;
uint8_t buf[XOF256_BLOCKBYTES];
xof256_state state;
// H(HighBits(A * y mod 2q), LSB(round(y0) * j), HAETAE_M)
xof256_absorb_twice(&state, highbits_lsb,
HAETAE_POLYVECK_HIGHBITS_PACKEDBYTES +
HAETAE_POLYC_PACKEDBYTES,
mu, HAETAE_SEEDBYTES);
xof256_squeezeblocks(buf, 1, &state);
for (i = 0; i < HAETAE_N; ++i)
c->coeffs[i] = 0;
for (i = HAETAE_N - HAETAE_TAU; i < HAETAE_N; ++i) {
do {
if (pos >= XOF256_BLOCKBYTES) {
xof256_squeezeblocks(buf, 1, &state);
pos = 0;
}
b = buf[pos++];
} while (b > i);
c->coeffs[i] = c->coeffs[b];
c->coeffs[b] = 1;
}
#elif HAETAE_MODE == HAETAE_MODE5
unsigned int i, hwt = 0, cond = 0;
uint8_t mask = 0, w0 = 0;
uint8_t buf[32] = {0};
xof256_state state;
// H(HighBits(A * y mod 2q), LSB(round(y0) * j), HAETAE_M)
xof256_absorb_twice(&state, highbits_lsb,
HAETAE_POLYVECK_HIGHBITS_PACKEDBYTES +
HAETAE_POLYC_PACKEDBYTES,
mu, HAETAE_SEEDBYTES);
xof256_squeeze(buf, 32, &state);
for (i = 0; i < 32; ++i)
hwt += hammingWeight_8(buf[i]);
cond = (128 - hwt);
mask = 0xff & (cond >> 8);
w0 = -(buf[0] & 1);
mask = w0 ^ ((-(!!cond & 1)) & (mask ^ w0)); // mask = !!cond ? mask : w0
for (i = 0; i < 32; ++i) {
buf[i] ^= mask;
c->coeffs[8 * i] = buf[i] & 1;
c->coeffs[8 * i + 1] = (buf[i] >> 1) & 1;
c->coeffs[8 * i + 2] = (buf[i] >> 2) & 1;
c->coeffs[8 * i + 3] = (buf[i] >> 3) & 1;
c->coeffs[8 * i + 4] = (buf[i] >> 4) & 1;
c->coeffs[8 * i + 5] = (buf[i] >> 5) & 1;
c->coeffs[8 * i + 6] = (buf[i] >> 6) & 1;
c->coeffs[8 * i + 7] = (buf[i] >> 7) & 1;
}
#endif
}
void poly_decomposed_pack(uint8_t *buf, const poly *a) {
unsigned int i;
for (i = 0; i < HAETAE_N; i++) {
buf[i] = a->coeffs[i];
}
}
void poly_decomposed_unpack(poly *a, const uint8_t *buf) {
unsigned int i;
for (i = 0; i < HAETAE_N; i++) {
a->coeffs[i] = (int8_t)buf[i];
}
}
/*************************************************
* Name: poly_fromcrt
*
* Description: Compute coefficient-wise inverse CRT from the "mod q"
* polynomial u and the "mod 2" polynomial v: if the LSBs of the
* two coefficients agree, keep the "mod q" coefficient, otherwise
* add q. Output coefficients lie in [0, 2q-1].
*
* Arguments: - poly *w: pointer to output polynomial
* - const poly *u: pointer to input polynomial ("mod q")
* - const poly *v: pointer to input polynomial ("mod 2")
*
* Specification: Implements @[Algorithm 4, poly_fromcrt]
**************************************************/
void poly_fromcrt(poly *w, const poly *u, const poly *v) {
unsigned int i;
int32_t xq, x2;
for (i = 0; i < HAETAE_N; i++) {
xq = u->coeffs[i];
x2 = v->coeffs[i];
w->coeffs[i] = xq + (HAETAE_Q & -((xq ^ x2) & 1));
}
}
/*************************************************
* Name: poly_fromcrt0
*
* Description: Compute coefficient-wise inverse CRT from the "mod q"
* polynomial u, assuming the "mod 2" polynomial is zero: if the
* LSB of a coefficient is zero, keep it, otherwise add q. Output
* coefficients lie in [0, 2q-1].
*
* Arguments: - poly *w: pointer to output polynomial
* - const poly *u: pointer to input polynomial ("mod q")
*
* Specification: Implements @[Algorithm 5, poly_fromcrt0]
**************************************************/
void poly_fromcrt0(poly *w, const poly *u) {
unsigned int i;
int32_t xq;
for (i = 0; i < HAETAE_N; i++) {
xq = u->coeffs[i];
w->coeffs[i] = xq + (HAETAE_Q & -(xq & 1));
}
}
void poly_ntt(poly *a) { ntt(&a->coeffs[0]); }
void poly_invntt_tomont(poly *a) { invntt_tomont(&a->coeffs[0]); }