packages feed

KpqC-0.1.0: cbits/third_party/HAETAE/src/polyfix.c

// SPDX-License-Identifier: MIT

#include "polyfix.h"
#include "fips202.h"
#include "fixpoint.h"
#include "params.h"
#include "sampler.h"

#include <stdint.h>

/*************************************************
 * Name:        polyfix_add
 *
 * Description: Add double polynomial and integer polynomial.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyfix *c: pointer to output double polynomial
 *              - const polyfix *a: pointer to first summand
 *              - const poly *b: pointer to second summand
 **************************************************/
void polyfix_add(polyfix *c, const polyfix *a, const poly *b) {
  unsigned int i;

  for (i = 0; i < HAETAE_N; ++i)
    c->coeffs[i] = a->coeffs[i] + HAETAE_LN * b->coeffs[i];
}

/*************************************************
 * Name:        polyfixfix_sub
 *
 * Description: Subtract fixed polynomial and fixed polynomial.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyfix *c: pointer to output fixed polynomial
 *              - const polyfix *a: pointer to first summand
 *              - const polyfix *b: pointer to second summand
 **************************************************/
void polyfixfix_sub(polyfix *c, const polyfix *a, const polyfix *b) {
  unsigned int i;

  for (i = 0; i < HAETAE_N; ++i)
    c->coeffs[i] = a->coeffs[i] - b->coeffs[i];
}

int32_t fix_round(int32_t num) {
  return (num + HAETAE_LNHALF) >> HAETAE_LNBITS;
}

/*************************************************
 * Name:        polyfix_round
 *
 * Description: rounds a fixed polynomial to integer polynomial
 *
 * Arguments:   - poly *a: output integer polynomial
 *              - poly *b: input fixed polynomial
 **************************************************/
void polyfix_round(poly *a, const polyfix *b) {
  unsigned i;

  for (i = 0; i < HAETAE_N; ++i)
    a->coeffs[i] = fix_round(b->coeffs[i]);
}

/*************************************************
 * Name:        polyfixveck_add
 *
 * Description: Add vector to a vector of double polynomials of length HAETAE_K.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyveck *w: pointer to output vector
 *              - const polyveck *u: pointer to first summand
 *              - const polyveck *v: pointer to second summand
 **************************************************/
void polyfixveck_add(polyfixveck *w, const polyfixveck *u, const polyveck *v) {
  unsigned int i;

  for (i = 0; i < HAETAE_K; ++i)
    polyfix_add(&w->vec[i], &u->vec[i], &v->vec[i]);
}

/*************************************************
 * Name:        polyfixfixveck_sub
 *
 * Description: subtract vector to a vector of fixed polynomials of length k.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyveck *w: pointer to output vector
 *              - const polyfixveck *u: pointer to first summand
 *              - const polyfixveck *v: pointer to second summand
 **************************************************/
void polyfixfixveck_sub(polyfixveck *w, const polyfixveck *u,
                        const polyfixveck *v) {
  unsigned int i;

  for (i = 0; i < HAETAE_K; ++i)
    polyfixfix_sub(&w->vec[i], &u->vec[i], &v->vec[i]);
}

/*************************************************
 * Name:        polyfixveck_double
 *
 * Description: Double vector of polynomials of length HAETAE_K.
 *
 * Arguments:   - polyveck *b: pointer to output vector
 *              - polyveck *a: pointer to input vector
 **************************************************/
void polyfixveck_double(polyfixveck *b, const polyfixveck *a) {
  unsigned int i, j;

  for (i = 0; i < HAETAE_K; ++i)
    for (j = 0; j < HAETAE_N; ++j)
      b->vec[i].coeffs[j] = 2 * a->vec[i].coeffs[j];
}

/*************************************************
 * Name:        polyfixveck_round
 *
 * Description: rounds a fixed polynomial vector of length HAETAE_K
 *
 * Arguments:   - polyveck *a: output integer polynomial vector
 *              - polyfixveck *b: input fixed polynomial vector
 **************************************************/
void polyfixveck_round(polyveck *a, const polyfixveck *b) {
  unsigned i;

  for (i = 0; i < HAETAE_K; ++i)
    polyfix_round(&a->vec[i], &b->vec[i]);
}

/*************************************************
 * Name:        polyfixvecl_add
 *
 * Description: Add vector to a vector of double polynomials of length L.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyvecl *w: pointer to output vector
 *              - const polyfixvecl *u: pointer to first summand
 *              - const polyvecl *v: pointer to second summand
 **************************************************/
void polyfixvecl_add(polyfixvecl *w, const polyfixvecl *u, const polyvecl *v) {
  unsigned int i;

  for (i = 0; i < HAETAE_L; ++i)
    polyfix_add(&w->vec[i], &u->vec[i], &v->vec[i]);
}

/*************************************************
 * Name:        polyfixfixvecl_sub
 *
 * Description: subtract vector to a vector of fixed polynomials of length l.
 *              No modular reduction is performed.
 *
 * Arguments:   - polyvecl *w: pointer to output vector
 *              - const polyfixvecl *u: pointer to first summand
 *              - const polyfixvecl *v: pointer to second summand
 **************************************************/
void polyfixfixvecl_sub(polyfixvecl *w, const polyfixvecl *u,
                        const polyfixvecl *v) {
  unsigned int i;

  for (i = 0; i < HAETAE_L; ++i)
    polyfixfix_sub(&w->vec[i], &u->vec[i], &v->vec[i]);
}
/*************************************************
 * Name:        polyfixvecl_double
 *
 * Description: Double vector of polynomials of length L.
 *
 * Arguments:   - polyveck *b: pointer to output vector
 *              - polyveck *a: pointer to input vector
 **************************************************/
void polyfixvecl_double(polyfixvecl *b, const polyfixvecl *a) {
  unsigned int i, j;

  for (i = 0; i < HAETAE_L; ++i)
    for (j = 0; j < HAETAE_N; ++j)
      b->vec[i].coeffs[j] = 2 * a->vec[i].coeffs[j];
}

/*************************************************
 * Name:        polyfixvecl_round
 *
 * Description: rounds a fixed polynomial vector of length L
 *
 * Arguments:   - polyvecl *a: output integer polynomial vector
 *              - polyfixvecl *b: input fixed polynomial vector
 **************************************************/
void polyfixvecl_round(polyvecl *a, const polyfixvecl *b) {
  unsigned i;

  for (i = 0; i < HAETAE_L; ++i)
    polyfix_round(&a->vec[i], &b->vec[i]);
}

/*************************************************
 * Name:        polyfixveclk_norm2
 *
 * Description: Calculates L2 norm of a fixed point polynomial vector with
 *              length HAETAE_L + HAETAE_K The result is L2 norm * HAETAE_LN
 *               similar to the way polynomial is usually stored
 *
 * Arguments:   - polyfixvecl *a: polynomial vector with length HAETAE_L to
 *              calculate norm
 *              - polyfixveck *b: polynomial vector with length HAETAE_K to
 *              calculate norm
 **************************************************/
uint64_t polyfixveclk_sqnorm2(const polyfixvecl *a, const polyfixveck *b) {
  unsigned int i, j;
  uint64_t ret = 0;

  for (i = 0; i < HAETAE_L; ++i) {
    for (j = 0; j < HAETAE_N; ++j)
      ret += (int64_t)a->vec[i].coeffs[j] * a->vec[i].coeffs[j];
  }

  for (i = 0; i < HAETAE_K; ++i) {
    for (j = 0; j < HAETAE_N; ++j)
      ret += (int64_t)b->vec[i].coeffs[j] * b->vec[i].coeffs[j];
  }

  return ret;
}

/*************************************************
 * Name:        polyfixveclk_sample_hyperball
 *
 * Description: sample hyperball
 *
 * Arguments:   - polyfixvecl *y1: polynomial vector with length HAETAE_L to
 *              calculate norm
 *              - polyfixveck *y2: polynomial vector with length HAETAE_K to
 *              calculate norm
 *              - uint8_t *b: output byte
 *              - uint8_t seed[HAETAE_CRHBYTES]: input seed bytes
 *              - const uint16_t nonce: input nonce
 *
 * Specification: Implements @[Algorithm 13, SampleHyperball]
 **************************************************/
uint16_t polyfixveclk_sample_hyperball(polyfixvecl *y1, polyfixveck *y2,
                                       uint8_t *b,
                                       const uint8_t seed[HAETAE_CRHBYTES],
                                       const uint16_t nonce) {
  uint16_t ni = nonce;
  uint64_t samples[HAETAE_N * (HAETAE_L + HAETAE_K)];
  fp96_76 sqsum, invsqrt;
  unsigned int i, j;
  uint8_t signs[HAETAE_N * (HAETAE_L + HAETAE_K) / 8];

  do {
    sqsum.limb48[0] = 0;
    sqsum.limb48[1] = 0;

    sample_gauss_N(&samples[0], &signs[0], &sqsum, seed, ni++, HAETAE_N + 1);
    sample_gauss_N(&samples[HAETAE_N], &signs[HAETAE_N / 8], &sqsum, seed, ni++,
                   HAETAE_N + 1);

    for (i = 2; i < HAETAE_L + HAETAE_K; i++)
      sample_gauss_N(&samples[HAETAE_N * i], &signs[HAETAE_N / 8 * i], &sqsum,
                     seed, ni++, HAETAE_N);

    // divide sqsum by 2 and approximate inverse square root
    sqsum.limb48[0] += 1; // rounding
    sqsum.limb48[0] >>= 1;
    sqsum.limb48[0] += (sqsum.limb48[1] & 1) << 47;
    sqsum.limb48[1] >>= 1;
    sqsum.limb48[1] += sqsum.limb48[0] >> 48;
    sqsum.limb48[0] &= (UINT64_C(1) << 48) - 1;
    fixpoint_newton_invsqrt(&invsqrt, &sqsum);
    fixpoint_mul_high(&sqsum, &invsqrt,
                      (uint64_t)(HAETAE_B0 * HAETAE_LN + HAETAE_SQNM / 2)
                          << (28 - 13));

    for (i = 0; i < HAETAE_L; i++) {
      for (j = 0; j < HAETAE_N; j++)
        y1->vec[i].coeffs[j] = fixpoint_mul_rnd13(
            samples[(i * HAETAE_N + j)], &sqsum,
            (signs[(i * HAETAE_N + j) / 8] >> ((i * HAETAE_N + j) % 8)) & 1);
    }
    for (i = HAETAE_L; i < HAETAE_K + HAETAE_L; i++) {
      for (j = 0; j < HAETAE_N; j++)
        y2->vec[i - HAETAE_L].coeffs[j] = fixpoint_mul_rnd13(
            samples[(i * HAETAE_N + j)], &sqsum,
            (signs[(i * HAETAE_N + j) / 8] >> ((i * HAETAE_N + j) % 8)) & 1);
    }
  } while (polyfixveclk_sqnorm2(y1, y2) > HAETAE_B0SQ * HAETAE_LN * HAETAE_LN);

  {
    uint8_t tmp[HAETAE_CRHBYTES + 2];
    for (i = 0; i < HAETAE_CRHBYTES; i++) {
      tmp[i] = seed[i];
    }
    tmp[HAETAE_CRHBYTES + 0] = ni >> 0;
    tmp[HAETAE_CRHBYTES + 1] = ni >> 8;
    shake256(b, 1, tmp, HAETAE_CRHBYTES + 2);
  }

  return ni;
}