use super::ntt::mod_q;
use super::params::{D, MAX_K, MAX_L, N, Params, Q};
use alloc::vec::Vec;
pub fn simple_bit_pack(w: &[i32; N], b: u32, out: &mut [u8]) {
let bits = 32 - b.leading_zeros() as usize; let mut bit_pos = 0usize;
for byte in out.iter_mut() {
*byte = 0;
}
for i in 0..N {
let val = w[i] as u32;
for bit in 0..bits {
if (val >> bit) & 1 == 1 {
out[bit_pos / 8] |= 1 << (bit_pos % 8);
}
bit_pos += 1;
}
}
}
pub fn simple_bit_unpack(data: &[u8], b: u32, w: &mut [i32; N]) {
let bits = 32 - b.leading_zeros() as usize;
let mut bit_pos = 0usize;
for i in 0..N {
let mut val = 0u32;
for bit in 0..bits {
if (data[bit_pos / 8] >> (bit_pos % 8)) & 1 == 1 {
val |= 1 << bit;
}
bit_pos += 1;
}
w[i] = val as i32;
}
}
pub fn bit_pack(w: &[i32; N], a: u32, b: u32, out: &mut [u8]) {
let range = a + b;
let bits = 32 - range.leading_zeros() as usize;
let mut bit_pos = 0usize;
for byte in out.iter_mut() {
*byte = 0;
}
for i in 0..N {
let val = (b as i32 - w[i]) as u32;
for bit in 0..bits {
if (val >> bit) & 1 == 1 {
out[bit_pos / 8] |= 1 << (bit_pos % 8);
}
bit_pos += 1;
}
}
}
pub fn bit_unpack(data: &[u8], a: u32, b: u32, w: &mut [i32; N]) {
let range = a + b;
let bits = 32 - range.leading_zeros() as usize;
let mut bit_pos = 0usize;
for i in 0..N {
let mut val = 0u32;
for bit in 0..bits {
if (data[bit_pos / 8] >> (bit_pos % 8)) & 1 == 1 {
val |= 1 << bit;
}
bit_pos += 1;
}
w[i] = b as i32 - val as i32;
}
}
pub fn hint_bit_pack<P: Params>(h: &[[i32; N]], out: &mut [u8]) {
let omega = P::OMEGA;
let k = P::K;
for byte in out.iter_mut() {
*byte = 0;
}
let mut idx = 0usize;
for i in 0..k {
for j in 0..N {
if h[i][j] != 0 {
out[idx] = j as u8;
idx += 1;
}
}
out[omega + i] = idx as u8;
}
}
pub fn hint_bit_unpack<P: Params>(data: &[u8]) -> Option<[[i32; N]; MAX_K]> {
let omega = P::OMEGA;
let k = P::K;
let mut h = [[0i32; N]; MAX_K];
let mut idx = 0usize;
for i in 0..k {
let upper = data[omega + i] as usize;
if upper < idx || upper > omega {
return None;
}
let first = idx;
while idx < upper {
if idx > first && data[idx] <= data[idx - 1] {
return None;
}
let j = data[idx] as usize;
if j >= N {
return None;
}
h[i][j] = 1;
idx += 1;
}
}
while idx < omega {
if data[idx] != 0 {
return None;
}
idx += 1;
}
Some(h)
}
pub fn pk_encode<P: Params>(rho: &[u8; 32], t1: &[[i32; N]]) -> Vec<u8> {
let k = P::K;
let coeff_bits = 10; let poly_bytes = N * coeff_bits / 8; let mut pk = vec![0u8; P::PK_LEN];
pk[..32].copy_from_slice(rho);
for i in 0..k {
let offset = 32 + i * poly_bytes;
simple_bit_pack(&t1[i], 1023, &mut pk[offset..offset + poly_bytes]);
}
pk
}
pub fn pk_decode<P: Params>(pk: &[u8]) -> ([u8; 32], [[i32; N]; MAX_K]) {
let k = P::K;
let poly_bytes = 320; let mut rho = [0u8; 32];
rho.copy_from_slice(&pk[..32]);
let mut t1 = [[0i32; N]; MAX_K];
for i in 0..k {
let offset = 32 + i * poly_bytes;
simple_bit_unpack(&pk[offset..offset + poly_bytes], 1023, &mut t1[i]);
}
(rho, t1)
}
pub fn sk_encode<P: Params>(
rho: &[u8; 32],
k_seed: &[u8; 32],
tr: &[u8; 64],
s1: &[[i32; N]],
s2: &[[i32; N]],
t0: &[[i32; N]],
) -> Vec<u8> {
let eta = P::ETA as u32;
let l = P::L;
let k = P::K;
let eta_bits = P::BITLEN_2ETA;
let poly_eta_bytes = N * eta_bits / 8;
let d = D;
let poly_t0_bytes = N * d / 8;
let mut sk = vec![0u8; P::SK_LEN];
let mut offset = 0;
sk[offset..offset + 32].copy_from_slice(rho);
offset += 32;
sk[offset..offset + 32].copy_from_slice(k_seed);
offset += 32;
sk[offset..offset + 64].copy_from_slice(tr);
offset += 64;
for i in 0..l {
bit_pack(&s1[i], eta, eta, &mut sk[offset..offset + poly_eta_bytes]);
offset += poly_eta_bytes;
}
for i in 0..k {
bit_pack(&s2[i], eta, eta, &mut sk[offset..offset + poly_eta_bytes]);
offset += poly_eta_bytes;
}
for i in 0..k {
bit_pack(&t0[i], 4095, 4096, &mut sk[offset..offset + poly_t0_bytes]);
offset += poly_t0_bytes;
}
sk
}
pub fn sk_decode<P: Params>(
sk: &[u8],
) -> (
[u8; 32],
[u8; 32],
[u8; 64],
[[i32; N]; MAX_L],
[[i32; N]; MAX_K],
[[i32; N]; MAX_K],
) {
let eta = P::ETA as u32;
let l = P::L;
let k = P::K;
let eta_bits = P::BITLEN_2ETA;
let poly_eta_bytes = N * eta_bits / 8;
let d = D;
let poly_t0_bytes = N * d / 8;
let mut offset = 0;
let mut rho = [0u8; 32];
rho.copy_from_slice(&sk[offset..offset + 32]);
offset += 32;
let mut k_seed = [0u8; 32];
k_seed.copy_from_slice(&sk[offset..offset + 32]);
offset += 32;
let mut tr = [0u8; 64];
tr.copy_from_slice(&sk[offset..offset + 64]);
offset += 64;
let mut s1 = [[0i32; N]; MAX_L];
for i in 0..l {
bit_unpack(&sk[offset..offset + poly_eta_bytes], eta, eta, &mut s1[i]);
offset += poly_eta_bytes;
}
let mut s2 = [[0i32; N]; MAX_K];
for i in 0..k {
bit_unpack(&sk[offset..offset + poly_eta_bytes], eta, eta, &mut s2[i]);
offset += poly_eta_bytes;
}
let mut t0 = [[0i32; N]; MAX_K];
for i in 0..k {
bit_unpack(&sk[offset..offset + poly_t0_bytes], 4095, 4096, &mut t0[i]);
offset += poly_t0_bytes;
}
(rho, k_seed, tr, s1, s2, t0)
}
pub fn sk_decode_seeds<P: Params>(sk: &[u8]) -> ([u8; 32], [u8; 32], [u8; 64]) {
let mut rho = [0u8; 32];
rho.copy_from_slice(&sk[..32]);
let mut k_seed = [0u8; 32];
k_seed.copy_from_slice(&sk[32..64]);
let mut tr = [0u8; 64];
tr.copy_from_slice(&sk[64..128]);
(rho, k_seed, tr)
}
pub fn sk_decode_s1<P: Params>(sk: &[u8], idx: usize, out: &mut [i32; N]) {
let eta = P::ETA as u32;
let eta_bits = P::BITLEN_2ETA;
let poly_eta_bytes = N * eta_bits / 8;
let base = 128; let offset = base + idx * poly_eta_bytes;
bit_unpack(&sk[offset..offset + poly_eta_bytes], eta, eta, out);
}
pub fn sk_decode_s2<P: Params>(sk: &[u8], idx: usize, out: &mut [i32; N]) {
let eta = P::ETA as u32;
let l = P::L;
let eta_bits = P::BITLEN_2ETA;
let poly_eta_bytes = N * eta_bits / 8;
let base = 128 + l * poly_eta_bytes;
let offset = base + idx * poly_eta_bytes;
bit_unpack(&sk[offset..offset + poly_eta_bytes], eta, eta, out);
}
pub fn sk_decode_t0<P: Params>(sk: &[u8], idx: usize, out: &mut [i32; N]) {
let l = P::L;
let k = P::K;
let eta_bits = P::BITLEN_2ETA;
let poly_eta_bytes = N * eta_bits / 8;
let d = D;
let poly_t0_bytes = N * d / 8;
let base = 128 + (l + k) * poly_eta_bytes;
let offset = base + idx * poly_t0_bytes;
bit_unpack(&sk[offset..offset + poly_t0_bytes], 4095, 4096, out);
}
pub fn sig_encode<P: Params>(c_tilde: &[u8], z: &[[i32; N]], h: &[[i32; N]]) -> Vec<u8> {
let l = P::L;
let gamma1 = P::GAMMA1 as u32;
let gamma1_bits = P::BITLEN_GAMMA1_MINUS1 + 1; let poly_z_bytes = N * gamma1_bits / 8;
let c_tilde_len = P::LAMBDA / 4;
let mut sig = vec![0u8; P::SIG_LEN];
let mut offset = 0;
sig[offset..offset + c_tilde_len].copy_from_slice(&c_tilde[..c_tilde_len]);
offset += c_tilde_len;
for i in 0..l {
bit_pack(&z[i], gamma1 - 1, gamma1, &mut sig[offset..offset + poly_z_bytes]);
offset += poly_z_bytes;
}
hint_bit_pack::<P>(h, &mut sig[offset..]);
sig
}
pub fn sig_decode<P: Params>(sig: &[u8]) -> Option<(Vec<u8>, [[i32; N]; MAX_L], [[i32; N]; MAX_K])> {
let l = P::L;
let gamma1 = P::GAMMA1 as u32;
let gamma1_bits = P::BITLEN_GAMMA1_MINUS1 + 1;
let poly_z_bytes = N * gamma1_bits / 8;
let c_tilde_len = P::LAMBDA / 4;
let mut offset = 0;
let c_tilde = sig[offset..offset + c_tilde_len].to_vec();
offset += c_tilde_len;
let mut z = [[0i32; N]; MAX_L];
for i in 0..l {
bit_unpack(&sig[offset..offset + poly_z_bytes], gamma1 - 1, gamma1, &mut z[i]);
offset += poly_z_bytes;
}
let h = hint_bit_unpack::<P>(&sig[offset..])?;
Some((c_tilde, z, h))
}
pub fn w1_encode<P: Params>(w1: &[[i32; N]]) -> Vec<u8> {
let k = P::K;
let gamma2 = P::GAMMA2;
let max_w1 = ((Q - 1) / (2 * gamma2) - 1) as u32;
let bits = 32 - max_w1.leading_zeros() as usize;
let poly_bytes = N * bits / 8;
let mut out = vec![0u8; k * poly_bytes];
for i in 0..k {
let offset = i * poly_bytes;
simple_bit_pack(&w1[i], max_w1, &mut out[offset..offset + poly_bytes]);
}
out
}
pub fn power2round(r: i32) -> (i32, i32) {
let rp = mod_q(r);
let two_d = 1i32 << D;
let half = two_d >> 1;
let mut r0v = rp % two_d; if r0v > half {
r0v -= two_d;
}
let r1 = (rp - r0v) / two_d;
(r1, r0v)
}
pub fn power2round_vec(t: &[[i32; N]], len: usize) -> ([[i32; N]; MAX_K], [[i32; N]; MAX_K]) {
let mut t1 = [[0i32; N]; MAX_K];
let mut t0 = [[0i32; N]; MAX_K];
for i in 0..len {
for j in 0..N {
let (r1, r0) = power2round(t[i][j]);
t1[i][j] = r1;
t0[i][j] = r0;
}
}
(t1, t0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_bit_pack_unpack() {
let mut w = [0i32; N];
for i in 0..N {
w[i] = (i as i32 * 3) % 1024;
}
let mut buf = [0u8; 320]; simple_bit_pack(&w, 1023, &mut buf);
let mut w2 = [0i32; N];
simple_bit_unpack(&buf, 1023, &mut w2);
assert_eq!(w, w2);
}
#[test]
fn test_bit_pack_unpack() {
let mut w = [0i32; N];
for i in 0..N {
w[i] = (i as i32 % 5) - 2; }
let mut buf = [0u8; 96];
bit_pack(&w, 2, 2, &mut buf);
let mut w2 = [0i32; N];
bit_unpack(&buf, 2, 2, &mut w2);
assert_eq!(w, w2);
}
#[test]
fn test_power2round() {
let (r1, r0) = power2round(1234567);
assert_eq!(r1 * (1 << D) + r0, 1234567);
assert!(r0.abs() <= (1 << (D - 1)));
}
}