Skip to content

Commit

Permalink
bug fixes and test
Browse files Browse the repository at this point in the history
Signed-off-by: Ahmed <>
  • Loading branch information
Ahmed committed Jul 7, 2024
1 parent d94d7f4 commit 12712ba
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 5 deletions.
1 change: 1 addition & 0 deletions ntru/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
5 changes: 4 additions & 1 deletion ntru/src/encoded/streamlined.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ fn rounded_encode<Params: NtruCommonUtils + StreamlinedNtru>(
let mut M: Array<u16, Params::P> = 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;
Expand Down
6 changes: 4 additions & 2 deletions ntru/src/kem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ pub struct CihpherText<Params: NtruCommonUtils> {
}

impl<T: NtruCommonUtils> CihpherText<T> {
#[must_use]pub fn to_bytes(&self) -> Vec<u8> {
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
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 }
Expand Down
4 changes: 2 additions & 2 deletions ntru/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
236 changes: 236 additions & 0 deletions ntru/tests/nist.rs
Original file line number Diff line number Diff line change
@@ -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<u8, U32>,
crt: &GenericArray<u8, U16>,
buffer: &mut GenericArray<u8, U16>,
) {
let cipher = Aes256::new(key);
cipher.encrypt_block_b2b(crt, buffer);
}
struct AesDrbg {
key: GenericArray<u8, U32>,
v: GenericArray<u8, U16>,
}
impl AesDrbg {
fn update(&mut self, seed_material: Option<&[u8; 48]>) {
let mut tmp: [GenericArray<u8, U16>; 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::<u8, U16>::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<U8L48> {
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<u8>,
pk: Vec<u8>,
sk: Vec<u8>,
ct: Vec<u8>,
ss: Vec<u8>,
}

impl TestEntry {
fn from_file(path: &str) -> Vec<TestEntry> {
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<T: NtruCommonUtils + AsymEnc + HashOps>(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::<T>(&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::<Streamlined<U1013>>("sntrup1013");
}
#[test]
fn test_sntrup1277() {
test_config::<Streamlined<U1277>>("sntrup1277");
}
#[test]
fn test_sntrup653() {
test_config::<Streamlined<U653>>("sntrup653");
}
#[test]
fn test_sntrup761() {
test_config::<Streamlined<U761>>("sntrup761");
}
#[test]
fn test_sntrup857() {
test_config::<Streamlined<U857>>("sntrup857");
}
#[test]
fn test_sntrup953() {
test_config::<Streamlined<U953>>("sntrup953");
}

0 comments on commit 12712ba

Please sign in to comment.