KpqC-0.1.0: cbits/third_party/SMAUG-T/src/hwt.c
// SPDX-License-Identifier: MIT
#include "hwt.h"
#include "fips202.h"
/*************************************************
* Name: load16_littleendian
*
* Description: load 2 bytes into a 16-bit integer
* in little-endian order
*
* Arguments: - uint16_t *out: pointer to output int16_t array
* - size_t outlen: output length
* - const uint8_t *in: pointer to input byte array
**************************************************/
#define load16_littleendian SMAUGT_NAMESPACE(load16_littleendian)
static void load16_littleendian(uint16_t *out, size_t outlen,
const uint8_t *in) {
size_t pos = 0;
for (size_t i = 0; i < outlen; ++i) {
out[i] = ((uint16_t)(in[pos])) | ((uint16_t)(in[pos + 1]) << 8);
pos += 2;
}
}
// referenced
// Décio Luiz Gazzoni Filho and Tomás S. R. Silva and Julio López
// “Efficient isochronous fixed-weight sampling with applications to {NTRU},” in
// Cryptology {ePrint} Archive, Paper 2024/548. 2024,
// url: eprint.iacr.org/2024/548.
/*************************************************
* Name: rej_sample_mod
*
* Description: Sample array of random integers such that res[i] is in the range
* [0, SMAUGT_N - i] for 0 <= i < SMAUGT_N
*
* Arguments: - uint8_t si[SMAUGT_N]: pointer to ouptput polynomial r(x),
* assumed to be already initialized
* - const uint16_t *seed: pointer to input seed (of length
* SMAUGT_HWTSEEDBYTES/2)
*
* Specification: Implements @[KS X 123456, Algorithm 4, RejSampleMod]
**************************************************/
#define rej_sample_mod SMAUGT_NAMESPACE(rej_sample_mod)
static int rej_sample_mod(int16_t si[SMAUGT_N], const uint16_t *rand) {
unsigned int i, j = SMAUGT_N;
uint32_t m;
uint16_t s, t, l;
for (i = 0; i < SMAUGT_N; i++) {
s = SMAUGT_N - i;
t = 65536 % s;
m = (uint32_t)rand[i] * s;
l = m;
while (l < t) {
if (j >= (SMAUGT_HWTSEEDBYTES / 2))
return -1; // all randomness used
m = (uint32_t)rand[j++] * s;
l = m;
}
si[i] = m >> 16;
}
return 0;
}
/*************************************************
* Name: hwt
*
* Description: Hamming weight sampling deterministically to generate sparse
* polynomial r(x) from a seed. shake256 is the Extendable-Output
* Function from the SHA-3 family.
*
* Arguments: - int16_t *res: pointer to ouptput polynomial r(x)
* (of length SMAUGT_N), assumed to be already initialized
* - const uint8_t *seed: pointer to input seed (of length
* SMAUGT_CRYPTO_BYTES + 2)
*
* Specification: Implements @[KS X 123456, Algorithm 3, HWT]
**************************************************/
int hwt(int16_t *res, const uint8_t *seed) {
unsigned int i;
int16_t si[SMAUGT_N] = {0};
uint16_t rand[SMAUGT_HWTSEEDBYTES / 2] = {0};
uint8_t sign[SMAUGT_N / 4] = {0};
uint8_t buf[SMAUGT_HWTSEEDBYTES] = {0};
keccak_state state;
shake256_init(&state);
shake256_absorb_once(&state, seed, SMAUGT_CRYPTO_BYTES + 2);
// only executed once with overwhelming probability:
shake256_squeeze(buf, SMAUGT_HWTSEEDBYTES, &state);
load16_littleendian(rand, SMAUGT_HWTSEEDBYTES / 2, buf);
if (rej_sample_mod(si, rand)) {
return -1;
}
shake256_squeeze(sign, SMAUGT_N / 4, &state);
int16_t t0;
int16_t c0 = SMAUGT_N - SMAUGT_HS;
for (i = 0; i < SMAUGT_N; i++) {
t0 = (si[i] - c0) >> 15;
c0 += t0;
res[i] = 1 + t0;
// Convert to ternary
// index of sign: (i / 16 / 8) * 16 + (i % 16)
// shift size : (i / 16) % 8
res[i] =
(-res[i]) &
((((sign[(((i >> 4) >> 3) << 4) + (i & 0x0F)] >> ((i >> 4) & 0x07))
<< 1) &
0x02) -
1);
}
return 0;
}