From 12712bae170b185d0aea201545d02eaaea4c385a Mon Sep 17 00:00:00 2001 From: Ahmed <> Date: Sun, 7 Jul 2024 22:41:15 +0200 Subject: [PATCH] bug fixes and test Signed-off-by: Ahmed <> --- ntru/Cargo.toml | 1 + ntru/src/encoded/streamlined.rs | 5 +- ntru/src/kem.rs | 6 +- ntru/src/lib.rs | 4 +- ntru/tests/nist.rs | 236 ++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 5 deletions(-) create mode 100644 ntru/tests/nist.rs diff --git a/ntru/Cargo.toml b/ntru/Cargo.toml index 5c0806b..32d6663 100644 --- a/ntru/Cargo.toml +++ b/ntru/Cargo.toml @@ -11,5 +11,6 @@ sha2 = "0.10.8" [dev-dependencies] aes="0.8.4" hex="0.4.3" +hybrid-array = { path="../../hybrid-array", features = ["extra-sizes"] } itertools = "0.13.0" rayon="1.10.0" diff --git a/ntru/src/encoded/streamlined.rs b/ntru/src/encoded/streamlined.rs index 49e0b59..358aaf5 100644 --- a/ntru/src/encoded/streamlined.rs +++ b/ntru/src/encoded/streamlined.rs @@ -47,7 +47,10 @@ fn rounded_encode( let mut M: Array = Array::default(); let mut out = Array::default(); for i in 0..Params::P::USIZE { - R[i] = ((*r.0[i] as u32 + Params::Q12 as u32).wrapping_mul(10923) >> 15) as u16; + R[i] = ((*r.0[i] as u32) + .wrapping_add(Params::Q12 as u32) + .wrapping_mul(10923) + >> 15) as u16; } for i in 0..Params::P::USIZE { M[i] = (Params::Q + 2) / 3; diff --git a/ntru/src/kem.rs b/ntru/src/kem.rs index 7996e36..df55360 100644 --- a/ntru/src/kem.rs +++ b/ntru/src/kem.rs @@ -18,13 +18,15 @@ pub struct CihpherText { } impl CihpherText { - #[must_use]pub fn to_bytes(&self) -> Vec { + #[must_use] + pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); bytes.extend_from_slice(&self.c); bytes.extend_from_slice(&self.cache); bytes } - #[must_use]pub fn from_bytes(bytes: &[u8]) -> Self { + #[must_use] + pub fn from_bytes(bytes: &[u8]) -> Self { let c = bytes[..T::CipherTextBytes::USIZE].try_into().unwrap(); let cache = bytes[T::CipherTextBytes::USIZE..].try_into().unwrap(); CihpherText { c, cache } diff --git a/ntru/src/lib.rs b/ntru/src/lib.rs index adb997c..4a36a81 100644 --- a/ntru/src/lib.rs +++ b/ntru/src/lib.rs @@ -22,7 +22,7 @@ extern crate alloc; mod algebra; mod const_time; pub mod core; -mod encoded; -mod hashes; +pub mod encoded; +pub mod hashes; pub mod kem; pub mod params; diff --git a/ntru/tests/nist.rs b/ntru/tests/nist.rs new file mode 100644 index 0000000..4f8c8fe --- /dev/null +++ b/ntru/tests/nist.rs @@ -0,0 +1,236 @@ +//! Checking that our NTRU-Prime is generating same output when compared to nist submission + +use std::{ + fs::File, + io::{BufRead, BufReader}, +}; + +use aes::{ + cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit}, + Aes256, +}; +use hybrid_array::sizes::{U1013, U1277, U16, U32, U653, U761, U857, U953}; +use itertools::{izip, Itertools}; +use ntru::{ + encoded::AsymEnc, + hashes::HashOps, + kem::{decap, encap, key_gen}, + params::{NtruCommonUtils, Streamlined}, +}; +use rand_core::{CryptoRng, RngCore, SeedableRng}; + +fn aes256_ecb( + key: &GenericArray, + crt: &GenericArray, + buffer: &mut GenericArray, +) { + let cipher = Aes256::new(key); + cipher.encrypt_block_b2b(crt, buffer); +} +struct AesDrbg { + key: GenericArray, + v: GenericArray, +} +impl AesDrbg { + fn update(&mut self, seed_material: Option<&[u8; 48]>) { + let mut tmp: [GenericArray; 3] = Default::default(); + for i in 0..3 { + for j in (1..=15).rev() { + if self.v[j] == 0xff { + self.v[j] = 0x00; + } else { + self.v[j] += 1; + break; + } + } + aes256_ecb(&self.key, &self.v, &mut tmp[i]); + } + if let Some(seed) = seed_material { + for i in 0..48 { + tmp[i / 16][i % 16] ^= seed[i]; + } + } + self.key[..16].copy_from_slice(&tmp[0]); + self.key[16..].copy_from_slice(&tmp[1]); + self.v.copy_from_slice(&tmp[2]); + } +} +impl CryptoRng for AesDrbg {} + +struct U8L48([u8; 48]); + +impl Default for U8L48 { + fn default() -> Self { + U8L48([0; 48]) + } +} + +impl AsMut<[u8]> for U8L48 { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} +impl AsRef<[u8]> for U8L48 { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl SeedableRng for AesDrbg { + type Seed = U8L48; + + fn from_seed(seed: Self::Seed) -> Self { + let entropy_input = seed.0; + let mut drbg = AesDrbg { + key: GenericArray::default(), + v: GenericArray::default(), + }; + drbg.update(Some(&entropy_input)); + drbg + } +} + +impl RngCore for AesDrbg { + fn next_u32(&mut self) -> u32 { + let mut bytes = [0u8; 4]; + self.fill_bytes(&mut bytes); + u32::from_le_bytes(bytes) + } + + fn next_u64(&mut self) -> u64 { + unimplemented!() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut block = GenericArray::::default(); + let mut i = 0; + let mut xlen = dest.len(); + while xlen > 0 { + for j in (1..=15).rev() { + if self.v[j] == 0xff { + self.v[j] = 0x00; + } else { + self.v[j] += 1; + break; + } + } + aes256_ecb(&self.key, &self.v, &mut block); + if xlen > 15 { + dest[i..i + 16].copy_from_slice(&block); + i += 16; + xlen -= 16; + } else { + dest[i..i + xlen].copy_from_slice(&block[..xlen]); + xlen = 0; + } + } + self.update(None) + } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +fn seed_builder(max: usize) -> Vec { + let mut seeds = Vec::with_capacity(100); + let mut entropy = [0u8; 48]; + for i in 0u8..48 { + entropy[i as usize] = i; + } + let mut rng = AesDrbg::from_seed(U8L48(entropy)); + for _ in 0..max { + let mut s = U8L48::default(); + rng.fill_bytes(&mut s.0); + seeds.push(s) + } + seeds +} + +struct TestEntry { + seed: Vec, + pk: Vec, + sk: Vec, + ct: Vec, + ss: Vec, +} + +impl TestEntry { + fn from_file(path: &str) -> Vec { + let file = File::open(path).unwrap(); + let mut ret = Vec::with_capacity(100); + for mut lines in &BufReader::new(file) + .lines() + .flatten() + .filter(|x| !(x.is_empty() || x.starts_with('#'))) + .chunks(6) + { + lines.next(); // we ignore the count line + let seed = hex::decode(lines.next().unwrap().split(" ").last().unwrap()).unwrap(); + let pk = hex::decode(lines.next().unwrap().split(" ").last().unwrap()).unwrap(); + let sk = hex::decode(lines.next().unwrap().split(" ").last().unwrap()).unwrap(); + let ct = hex::decode(lines.next().unwrap().split(" ").last().unwrap()).unwrap(); + let ss = hex::decode(lines.next().unwrap().split(" ").last().unwrap()).unwrap(); + ret.push(TestEntry { + seed, + pk, + sk, + ct, + ss, + }); + } + assert_eq!(ret.len(), 100); + ret + } +} + +#[test] +fn test_rng() { + let seeds = seed_builder(100); + let tests = TestEntry::from_file("test_data/ntrulpr653.rsp"); + for i in 0..100 { + assert_eq!(seeds[i].as_ref(), &tests[i].seed) + } +} + +fn test_config(config: &str) { + let seeds = seed_builder(100); + let path = format!("test_data/{config}.rsp"); + let tests = TestEntry::from_file(&path); + for (seed, test) in izip!(seeds, tests) { + let mut rng = AesDrbg::from_seed(seed); + let (sk, pk) = key_gen::(&mut rng); + assert_eq!(&pk.0 as &[u8], &test.pk); + assert_eq!(sk.to_bytes(), test.sk); + let (ct, ss) = encap(&mut rng, &pk); + assert_eq!(ct.to_bytes(), test.ct); + assert_eq!(&ss as &[u8], &test.ss); + //let ss2: [u8; 32] = decap(&ct, &sk); + //assert_eq!(&ss2 as &[u8], &test.ss); + } +} + +#[test] +fn test_sntrup1013() { + test_config::>("sntrup1013"); +} +#[test] +fn test_sntrup1277() { + test_config::>("sntrup1277"); +} +#[test] +fn test_sntrup653() { + test_config::>("sntrup653"); +} +#[test] +fn test_sntrup761() { + test_config::>("sntrup761"); +} +#[test] +fn test_sntrup857() { + test_config::>("sntrup857"); +} +#[test] +fn test_sntrup953() { + test_config::>("sntrup953"); +}