use core::fmt;
use core::marker::PhantomData;
use crate::{Error, low};
use crate::{
low::{ct_copy, ct_equal, ct_select_i16},
mid::{
rng::{RandomSource, SystemRandom},
sha3,
},
};
pub struct DecapKey {
ek_pke: EncapKey,
dk_pke: [u8; K * 384],
h_ek: [u8; 32],
z: [u8; 32],
}
impl DecapKey {
pub fn generate() -> Result<Self, Error> {
let _entry = low::Entry::new_secret();
let mut seed = [0u8; 64];
SystemRandom.fill(&mut seed)?;
Ok(Self::keygen_internal(&seed))
}
pub fn decaps(self, c: &Ciphertext) -> SharedSecret {
let _entry = low::Entry::new_secret();
self.decaps_internal(c)
}
pub fn encapsulation_key(&self) -> EncapKey {
let _entry = low::Entry::new_public();
self.ek_pke.clone()
}
#[doc(hidden)]
pub fn as_bytes(&self) -> [u8; K * 768 + 96] {
let _entry = low::Entry::new_secret();
let mut out = [0u8; K * 768 + 96];
out[..K * 384].copy_from_slice(&self.dk_pke);
out[K * 384..K * 768].copy_from_slice(&self.ek_pke.t_hat);
out[K * 768..K * 768 + 32].copy_from_slice(&self.ek_pke.rho);
out[K * 768 + 32..K * 768 + 64].copy_from_slice(&self.h_ek);
out[K * 768 + 64..].copy_from_slice(&self.z);
out
}
#[doc(hidden)]
pub fn keygen_internal(seed: &[u8; 64]) -> Self {
let (d, z) = seed.split_at(32);
let d = d.try_into().unwrap();
let z = z.try_into().unwrap();
let (ek_pke, dk_pke) = Self::kpke_keygen(&d);
let mut h = sha3::Sha3_256Context::new();
h.update(&ek_pke.t_hat);
h.update(&ek_pke.rho);
let h_ek = h.finish();
Self {
dk_pke,
ek_pke,
h_ek,
z,
}
}
#[doc(hidden)]
pub fn decaps_internal(&self, c: &Ciphertext) -> SharedSecret {
let m_prime = self.kpke_decrypt(c);
let mut g = sha3::Sha3_512Context::new();
g.update(&m_prime.0);
g.update(&self.h_ek);
let kr_prime = g.finish();
let (k_prime, r_prime) = kr_prime.split_at(32);
let r_prime = KpkeRandomness(r_prime.try_into().unwrap());
let mut k_bar = SharedSecret([0u8; 32]);
sha3::Shake256::new(&[&self.z, &c.0]).read(&mut k_bar.0);
let c_prime = self.ek_pke.kpke_encrypt(m_prime, r_prime);
let mut k_prime = k_prime.try_into().unwrap();
ct_copy(ct_equal(&c.0, &c_prime.0), &mut k_prime, &k_bar.0);
SharedSecret(k_prime)
}
fn kpke_keygen(d: &[u8; 32]) -> (EncapKey, [u8; K * 384]) {
let mut g = sha3::Sha3_512Context::new();
g.update(d);
g.update(&[K_BYTE]);
let g = g.finish();
let (rho, sigma) = g.split_at(32);
let rho: [u8; 32] = rho.try_into().unwrap();
let sigma = sigma.try_into().unwrap();
let a_hat = Coeffs::sample_poly(&rho);
let (s, e) = Coeffs::sample_poly_cbd_dual(sigma);
let mut s_hat = s.ntt();
let e_hat = e.ntt();
let t_hat = a_hat.mul_add(&s_hat, &e_hat);
let encap = EncapKey {
t_hat: t_hat.to_bytes(),
rho,
transpose_a_hat: a_hat.transpose(),
};
s_hat.reduce_in_place();
let decap = s_hat.to_bytes();
(encap, decap)
}
fn kpke_decrypt(&self, c: &Ciphertext) -> Message {
let (c_1, c_2) = c.0.split_at(32 * DU * K);
let u_prime = Coeffs::decompress_from_bytes_du_10(c_1.try_into().unwrap());
let v_prime = Coeffs::decompress_from_bytes_dv_4(c_2.try_into().unwrap());
let s_hat = Coeffs::from_bytes(&self.dk_pke);
let w = v_prime.sub(&s_hat.mul_and_inverse_ntt(&u_prime.ntt()));
w.to_message_bits()
}
}
impl Drop for DecapKey {
fn drop(&mut self) {
low::zeroise(&mut self.dk_pke);
low::zeroise(&mut self.z);
}
}
#[derive(Clone)]
pub struct EncapKey {
t_hat: [u8; K * 384],
rho: [u8; 32],
transpose_a_hat: Coeffs<{ K * K * N }, Ntt>,
}
impl EncapKey {
pub fn from_bytes(input: &[u8; K * 384 + 32]) -> Result<Self, Error> {
let _entry = low::Entry::new_public();
let (t_hat, rho) = input.split_at(K * 384);
let t_hat = t_hat.try_into().unwrap();
let rho = rho.try_into().unwrap();
if t_hat != Coeffs::from_bytes(&t_hat).to_bytes() {
return Err(Error::OutOfRange);
}
let transpose_a_hat = Coeffs::sample_poly_transposed(&rho);
Ok(Self {
t_hat,
rho,
transpose_a_hat,
})
}
pub fn encaps(self) -> Result<(SharedSecret, Ciphertext), Error> {
let _entry = low::Entry::new_secret();
let mut m = Message([0; 32]);
SystemRandom.fill(&mut m.0)?;
Ok(self.encaps_internal(m))
}
pub fn as_bytes(&self) -> [u8; K * 384 + 32] {
let _entry = low::Entry::new_public();
let mut out = [0u8; K * 384 + 32];
out[..K * 384].copy_from_slice(&self.t_hat);
out[K * 384..].copy_from_slice(&self.rho);
out
}
#[doc(hidden)]
pub fn encaps_internal(self, m: Message) -> (SharedSecret, Ciphertext) {
let mut h = sha3::Sha3_256Context::new();
h.update(&self.t_hat);
h.update(&self.rho);
let h_ek = h.finish();
let mut g = sha3::Sha3_512Context::new();
g.update(&m.0);
g.update(&h_ek);
let kr = g.finish();
let (k, r) = kr.split_at(32);
let k = k.try_into().unwrap();
let r = KpkeRandomness(r.try_into().unwrap());
let c = self.kpke_encrypt(m, r);
(SharedSecret(k), c)
}
fn kpke_encrypt(&self, m: Message, r: KpkeRandomness) -> Ciphertext {
let t_hat = Coeffs::from_bytes(&self.t_hat);
let (y, e_1) = Coeffs::sample_poly_cbd_dual(&r.0);
let mut buf = [0u8; 128];
sha3::Shake256::new(&[&r.0, &[K_BYTE * 2]]).read(&mut buf);
let mut e_2 = Coeffs::zero();
sample_cbd2(&buf, &mut e_2.0);
let y_hat = y.ntt();
let u = self.transpose_a_hat.mul_and_inverse_ntt(&y_hat).add(&e_1);
let mu = Coeffs::from_message_bits(m);
let v = t_hat.mul_and_inverse_ntt(&y_hat).add(&e_2).add(&mu);
let mut c = Ciphertext([0; _]);
let (c_1, c_2) = c.0.split_at_mut(32 * DU * K);
u.compress_into_bytes_du_10(c_1.try_into().unwrap());
v.compress_into_bytes_dv_4(c_2.try_into().unwrap());
c
}
}
#[derive(Debug, Clone)]
pub struct Ciphertext([u8; 32 * (DU * K + DV)]);
impl From<[u8; 1088]> for Ciphertext {
fn from(value: [u8; 1088]) -> Self {
Self(value)
}
}
impl AsRef<[u8; 1088]> for Ciphertext {
fn as_ref(&self) -> &[u8; 1088] {
&self.0
}
}
#[doc(hidden)]
pub struct Message(pub [u8; 32]);
impl Drop for Message {
fn drop(&mut self) {
low::zeroise(&mut self.0);
}
}
struct KpkeRandomness([u8; 32]);
pub struct SharedSecret([u8; 32]);
impl AsRef<[u8; 32]> for SharedSecret {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Debug for SharedSecret {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("SharedSecret").finish_non_exhaustive()
}
}
impl Drop for SharedSecret {
fn drop(&mut self) {
low::zeroise(&mut self.0);
}
}
trait Domain: Clone {}
#[derive(Clone)]
struct Normal;
impl Domain for Normal {}
#[derive(Clone)]
struct Ntt;
impl Domain for Ntt {}
#[derive(Clone)]
struct MulCache;
impl Domain for MulCache {}
#[derive(Clone, Debug)]
#[repr(align(32))]
struct Coeffs<const C: usize, D: Domain>([i16; C], PhantomData<D>);
impl<const C: usize, D: Domain> Coeffs<C, D> {
const fn zero() -> Self {
Self([0; C], PhantomData)
}
fn reduce_in_place(&mut self) {
for k in self.0.as_chunks_mut().0 {
low::mlkem_reduce(k);
}
}
}
impl Coeffs<{ K * K * N }, Ntt> {
fn sample_poly(rho: &[u8; 32]) -> Self {
Self::_sample_poly_ntt::<false>(rho)
}
fn sample_poly_transposed(rho: &[u8; 32]) -> Self {
Self::_sample_poly_ntt::<true>(rho)
}
fn _sample_poly_ntt<const TRANSPOSED: bool>(rho: &[u8; 32]) -> Self {
let mut r = Coeffs::zero();
let mut work_iter = SAMPLE_POLY_WORK.chunks_exact(4);
for c4 in work_iter.by_ref() {
let inputs = match TRANSPOSED {
false => &[
&[c4[0].1, c4[0].0],
&[c4[1].1, c4[1].0],
&[c4[2].1, c4[2].0],
&[c4[3].1, c4[3].0],
],
true => &[
&[c4[0].0, c4[0].1],
&[c4[1].0, c4[1].1],
&[c4[2].0, c4[2].1],
&[c4[3].0, c4[3].1],
],
};
Self::_sample_poly_ntt_quad(
rho,
inputs,
(&mut r.0[c4[0].2..c4[3].2 + N]).try_into().unwrap(),
);
}
for (i, j, offs) in work_iter.remainder() {
let input = match TRANSPOSED {
false => &[*j, *i],
true => &[*i, *j],
};
Shake128ForMlKem::new(&[rho, input])
.sample_into((&mut r.0[*offs..*offs + N]).try_into().unwrap());
}
r.reorder();
r
}
fn _sample_poly_ntt_quad(rho: &[u8; 32], inputs: &[&[u8; 2]; 4], outputs: &mut [i16; N * 4]) {
let mut buf = [0; 40];
buf[..32].copy_from_slice(rho);
buf[34] = sha3::SHAKE_PAD_BYTE;
let mut buf0 = buf;
buf0[32..34].clone_from_slice(inputs[0]);
let mut buf1 = buf;
buf1[32..34].clone_from_slice(inputs[1]);
let mut buf2 = buf;
buf2[32..34].clone_from_slice(inputs[2]);
let mut buf3 = buf;
buf3[32..34].clone_from_slice(inputs[3]);
let sponge_4x = sha3::SqueezingSponge4xShake128::new(&[&buf0, &buf1, &buf2, &buf3]);
let (output0, outputs) = outputs.split_at_mut(N);
let (output1, outputs) = outputs.split_at_mut(N);
let (output2, output3) = outputs.split_at_mut(N);
let mut samples = [[0; sha3::SHAKE_128_R_BYTES * 3]; 4];
let [tsponge0, tsponge1, tsponge2, tsponge3] = sponge_4x.squeeze(&mut samples);
let tail0 = Shake128ForMlKem::sample(&samples[0], output0.try_into().unwrap());
let tail1 = Shake128ForMlKem::sample(&samples[1], output1.try_into().unwrap());
let tail2 = Shake128ForMlKem::sample(&samples[2], output2.try_into().unwrap());
let tail3 = Shake128ForMlKem::sample(&samples[3], output3.try_into().unwrap());
if !tail0.is_empty() {
Shake128ForMlKem {
sponge: tsponge0.restitute(),
}
.tail_case(tail0);
}
if !tail1.is_empty() {
Shake128ForMlKem {
sponge: tsponge1.restitute(),
}
.tail_case(tail1);
}
if !tail2.is_empty() {
Shake128ForMlKem {
sponge: tsponge2.restitute(),
}
.tail_case(tail2);
}
if !tail3.is_empty() {
Shake128ForMlKem {
sponge: tsponge3.restitute(),
}
.tail_case(tail3);
}
}
fn mul_add(
&self,
s: &Coeffs<{ K * N }, Ntt>,
e: &Coeffs<{ K * N }, Ntt>,
) -> Coeffs<{ K * N }, Ntt> {
let mut r = e.clone();
let s_precomp = s.mul_cache();
let mut term = Coeffs::<_, Ntt>::zero();
for (aa, rr) in self
.0
.as_chunks()
.0
.iter()
.zip(r.0.as_chunks_mut::<N>().0.iter_mut())
{
low::mlkem_basemul_k3(&mut term.0, aa, &s.0, &s_precomp.0);
low::mlkem_tomont(&mut term.0);
for (r, t) in rr.iter_mut().zip(term.0) {
*r += t;
}
}
r.reduce_in_place();
r
}
fn reorder(&mut self) {
for ch in self.0.as_chunks_mut().0 {
low::mlkem_unpack(ch);
}
}
fn transpose(mut self) -> Self {
let (chunks, _) = self.0.as_chunks_mut::<N>();
for i in 0..K {
for j in (i + 1)..K {
chunks.swap(i * K + j, j * K + i);
}
}
self
}
fn mul_and_inverse_ntt(&self, y: &Coeffs<{ K * N }, Ntt>) -> Coeffs<{ K * N }, Normal> {
let mut r = Coeffs::<_, Ntt>::zero();
let y_precomp = y.mul_cache();
let mut term = Coeffs::<_, Ntt>::zero();
for (aa, rr) in self
.0
.as_chunks()
.0
.iter()
.zip(r.0.as_chunks_mut::<N>().0.iter_mut())
{
low::mlkem_basemul_k3(&mut term.0, aa, &y.0, &y_precomp.0);
for (r, t) in rr.iter_mut().zip(term.0) {
*r += t;
}
}
r.reduce_in_place();
r.inverse_ntt()
}
}
const SAMPLE_POLY_WORK: &[(u8, u8, usize)] = &[
(0, 0, 0), (0, 1, N),
(0, 2, N * 2),
(1, 0, K * N),
(1, 1, N + K * N),
(1, 2, N * 2 + K * N),
(2, 0, K * N * 2),
(2, 1, N + K * N * 2),
(2, 2, N * 2 + K * N * 2),
];
impl Coeffs<{ K * N }, Ntt> {
fn from_bytes(bytes: &[u8; 1152]) -> Self {
let mut r = Coeffs::zero();
let (r_chunks, _) = r.0.as_chunks_mut();
let (b_chunks, _) = bytes.as_chunks();
for (rr, bb) in r_chunks.iter_mut().zip(b_chunks.iter()) {
low::mlkem_frombytes(rr, bb);
low::mlkem_reduce(rr);
}
r
}
fn to_bytes(&self) -> [u8; 1152] {
let mut r = [0u8; 1152];
let (c_chunks, _) = self.0.as_chunks();
let (r_chunks, _) = r.as_chunks_mut();
for (rr, cc) in r_chunks.iter_mut().zip(c_chunks.iter()) {
low::mlkem_tobytes(rr, cc);
}
r
}
fn mul_and_inverse_ntt(&self, s: &Coeffs<{ K * N }, Ntt>) -> Coeffs<{ N }, Normal> {
let mut r = Coeffs::zero();
let s_precomp = s.mul_cache();
low::mlkem_basemul_k3(&mut r.0, &self.0, &s.0, &s_precomp.0);
r.inverse_ntt()
}
fn mul_cache(&self) -> Coeffs<{ K * 128 }, MulCache> {
let mut r = Coeffs::zero();
let (m, _) = self.0.as_chunks();
let (rr, _) = r.0.as_chunks_mut();
low::mlkem_mulcache_compute(&mut rr[0], &m[0]);
low::mlkem_mulcache_compute(&mut rr[1], &m[1]);
low::mlkem_mulcache_compute(&mut rr[2], &m[2]);
r
}
}
impl<const C: usize> Coeffs<C, Ntt> {
fn inverse_ntt(mut self) -> Coeffs<C, Normal> {
for ch in self.0.as_chunks_mut().0 {
low::mlkem_intt(ch);
}
Coeffs(self.0, PhantomData)
}
}
fn compress_4(x: i16) -> u8 {
debug_assert!((0..Q).contains(&x));
let c = x as u64;
let c = c << 4;
let c = c + Q_HALF as u64;
let c = c * 80635;
let c = c >> 28;
let c = c & 0xf;
c as u8
}
fn decompress_4(y: u16) -> i16 {
debug_assert!(y < 16);
let d = y as u32;
let d = d * (Q as u32);
let d = d + 8;
let d = d >> 4;
d as i16
}
fn compress_10(x: i16) -> u16 {
debug_assert!((0..Q).contains(&x));
let c = x as u64;
let c = c << 10;
let c = c + Q_HALF as u64;
let c = c * 1290167;
let c = c >> 32;
let c = c & 0x3ff;
c as u16
}
fn decompress_10(y: u16) -> i16 {
debug_assert!(y < 1024);
let d = y as u32;
let d = d * (Q as u32);
let d = d + 512;
let d = d >> 10;
d as i16
}
fn compress_1_x8(coeffs: &[i16; 8]) -> u8 {
let mut r = 0;
for (i, x) in coeffs.iter().enumerate() {
debug_assert!((0..Q).contains(x));
let x = (*x as u64) << 1;
let x = x + Q_HALF as u64;
let x = x * 80635;
let x = x >> 28;
r |= ((x & 1) as u8) << i;
}
r
}
impl Coeffs<{ K * N }, Normal> {
fn sample_poly_cbd_dual(sigma: &[u8; 32]) -> (Self, Self) {
let mut samples = [[0; 128]; 6];
let mut buf = [0; 40];
buf[..32].copy_from_slice(sigma);
buf[33] = sha3::SHAKE_PAD_BYTE;
let mut buf0 = buf;
let mut buf1 = buf;
let mut buf2 = buf;
let mut buf3 = buf;
let mut buf4 = buf;
let mut buf5 = buf;
buf0[32] = 0;
buf1[32] = 1;
buf2[32] = 2;
buf3[32] = 3;
buf4[32] = 4;
buf5[32] = 5;
sha3::Shake256::one_shot_sextet(&[&buf0, &buf1, &buf2, &buf3, &buf4, &buf5], &mut samples);
let mut a = Coeffs::zero();
let mut b = Coeffs::zero();
for (sample, poly) in samples.iter().zip(
a.0.as_chunks_mut()
.0
.iter_mut()
.chain(b.0.as_chunks_mut().0.iter_mut()),
) {
sample_cbd2(sample, poly);
}
(a, b)
}
fn ntt(mut self) -> Coeffs<{ K * N }, Ntt> {
for ch in self.0.as_chunks_mut().0 {
low::mlkem_ntt(ch);
}
Coeffs(self.0, PhantomData)
}
fn compress_into_bytes_du_10(&self, c_1: &mut [u8; 960]) {
let (quads, _) = self.0.as_chunks::<4>();
let (out_chunks, _) = c_1.as_chunks_mut::<5>();
for (quad, out) in quads.iter().zip(out_chunks.iter_mut()) {
let t0 = compress_10(quad[0]);
let t1 = compress_10(quad[1]);
let t2 = compress_10(quad[2]);
let t3 = compress_10(quad[3]);
out[0] = t0 as u8;
out[1] = ((t0 >> 8) | (t1 << 2)) as u8;
out[2] = ((t1 >> 6) | (t2 << 4)) as u8;
out[3] = ((t2 >> 4) | (t3 << 6)) as u8;
out[4] = (t3 >> 2) as u8;
}
}
fn decompress_from_bytes_du_10(c_1: &[u8; 960]) -> Self {
let mut r = Self::zero();
let (in_chunks, _) = c_1.as_chunks::<5>();
let (quads, _) = r.0.as_chunks_mut::<4>();
for (inp, quad) in in_chunks.iter().zip(quads.iter_mut()) {
let t0 = (inp[0] as u16) | ((inp[1] as u16 & 0x3) << 8);
let t1 = ((inp[1] as u16) >> 2) | ((inp[2] as u16 & 0xf) << 6);
let t2 = ((inp[2] as u16) >> 4) | ((inp[3] as u16 & 0x3f) << 4);
let t3 = ((inp[3] as u16) >> 6) | ((inp[4] as u16) << 2);
quad[0] = decompress_10(t0);
quad[1] = decompress_10(t1);
quad[2] = decompress_10(t2);
quad[3] = decompress_10(t3);
}
r
}
}
impl Coeffs<N, Normal> {
fn from_message_bits(m: Message) -> Self {
let mut r = Self::zero();
for (i, byte) in m.0.iter().enumerate() {
for bit in 0..8 {
r.0[i * 8 + bit] = ct_select_i16(byte >> bit & 1, Q_HALF, 0);
}
}
r
}
fn to_message_bits(&self) -> Message {
let mut r = Message([0; 32]);
for (i, r) in r.0.iter_mut().enumerate() {
*r = compress_1_x8(&self.0[i * 8..(i + 1) * 8].try_into().unwrap());
}
r
}
fn compress_into_bytes_dv_4(&self, c_2: &mut [u8; 128]) {
let (pairs, _) = self.0.as_chunks::<2>();
for (pair, byte) in pairs.iter().zip(c_2.iter_mut()) {
*byte = compress_4(pair[0]) | (compress_4(pair[1]) << 4);
}
}
fn decompress_from_bytes_dv_4(c_2: &[u8; 128]) -> Self {
let mut r = Self::zero();
let (pairs, _) = r.0.as_chunks_mut::<2>();
for (byte, pair) in c_2.iter().zip(pairs.iter_mut()) {
pair[0] = decompress_4((byte & 0xf) as u16);
pair[1] = decompress_4((byte >> 4) as u16);
}
r
}
}
impl<const C: usize> Coeffs<C, Normal> {
fn add(mut self, term: &Self) -> Self {
for (ss, tt) in self.0.iter_mut().zip(term.0.iter()) {
*ss += *tt;
}
self.reduce_in_place();
self
}
fn sub(mut self, term: &Self) -> Self {
for (ss, tt) in self.0.iter_mut().zip(term.0.iter()) {
*ss -= *tt;
}
self.reduce_in_place();
self
}
}
fn sample_cbd2(buf: &[u8; 128], out: &mut [i16; 256]) {
for (in_bytes, out_coeffs) in buf.chunks_exact(4).zip(out.chunks_exact_mut(8)) {
let t = u32::from_le_bytes(in_bytes.try_into().unwrap());
let d = (t & 0x5555_5555) + ((t >> 1) & 0x5555_5555);
for (j, coeff) in out_coeffs.iter_mut().enumerate() {
let a = ((d >> (4 * j)) & 0x3) as i16;
let b = ((d >> (4 * j + 2)) & 0x3) as i16;
*coeff = a - b;
}
}
}
struct Shake128ForMlKem {
sponge: sha3::Shake128SqueezingSponge,
}
impl Shake128ForMlKem {
fn new(message: &[&[u8]]) -> Self {
Self {
sponge: sha3::Shake128Sponge::new_for_message(message),
}
}
fn sample_into(mut self, output: &mut [i16; 256]) {
let mut initial_bytes = [0; sha3::SHAKE_128_R_BYTES * 3];
self.sponge.squeeze(&mut initial_bytes);
let tail = Self::sample(&initial_bytes, output);
if !tail.is_empty() {
self.tail_case(tail);
}
}
#[must_use]
fn sample<'a>(
samples: &'_ [u8; sha3::SHAKE_128_R_BYTES * 3],
output: &'a mut [i16; 256],
) -> &'a mut [i16] {
let used = low::mlkem_rej_uniform_vartime(output, samples) as usize;
output.split_at_mut(used).1
}
fn tail_case(self, output: &mut [i16]) {
let tail_iterator = Shake128TwelveBitIterator::new(self.sponge).filter(|f| *f < Q);
for (out, coeff) in output.iter_mut().zip(tail_iterator) {
*out = coeff;
}
}
}
struct Shake128TwelveBitIterator {
sponge: sha3::Shake128SqueezingSponge,
samples: [i16; Self::SAMPLE_COUNT],
used: usize,
}
impl Shake128TwelveBitIterator {
fn new(sponge: sha3::Shake128SqueezingSponge) -> Self {
Self {
sponge,
samples: [0; Self::SAMPLE_COUNT],
used: Self::SAMPLE_COUNT,
}
}
const SAMPLE_COUNT: usize = sha3::SHAKE_128_R_BYTES / 3 * 2;
}
impl Iterator for Shake128TwelveBitIterator {
type Item = i16;
#[cold]
fn next(&mut self) -> Option<Self::Item> {
if self.used == self.samples.len() {
let mut bytes = [0u8; sha3::SHAKE_128_R_BYTES];
self.sponge.squeeze(&mut bytes);
for (buf, d) in bytes.chunks_exact(3).zip(self.samples.chunks_exact_mut(2)) {
d[0] = (u16::from_le_bytes([buf[0], buf[1]]) & 0xfff) as i16;
d[1] = (u16::from_le_bytes([buf[1], buf[2]]) >> 4) as i16;
}
self.used = 0;
}
let item = self.samples[self.used];
self.used += 1;
Some(item)
}
}
const K: usize = 3;
const K_BYTE: u8 = K as u8;
const DU: usize = 10;
const DV: usize = 4;
const Q: i16 = 3329;
const Q_HALF: i16 = (Q + 1) / 2;
const N: usize = 256;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shared_secret_debug() {
assert_eq!(format!("{:?}", SharedSecret([0u8; 32])), "SharedSecret(..)");
}
#[test]
fn pairwise() {
let d = DecapKey::generate().unwrap();
let (ess, ct) = d.encapsulation_key().encaps().unwrap();
let dss = d.decaps(&ct);
assert_eq!(ess.0, dss.0);
}
#[test]
fn encaps_modulus_test() {
assert_eq!(
EncapKey::from_bytes(&[0xffu8; 1184]).err(),
Some(Error::OutOfRange)
);
}
}