From 18a0140f44679b5a792e1d6e778743daf384d37f Mon Sep 17 00:00:00 2001 From: Kris Kwiatkowski Date: Wed, 9 Mar 2022 14:09:28 +0000 Subject: [PATCH] [cavptool] update --- .github/workflows/main.yml | 4 +- test/bench/kyber.cc | 2 +- test/bench/sphincs.cc | 2 +- test/katrunner/src/main.rs | 152 ++++++++++++++++++++++++++++++++----- 4 files changed, 135 insertions(+), 25 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e97ddf93..f351fda7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,9 +83,9 @@ jobs: - name: Run KAT tests run: | cd test/katrunner && - curl http://amongbytes.com/~flowher/permalinks/kat.zip --output kat.zip + curl http://nice.pqsh.net/files/kat.zip --output kat.zip unzip kat.zip - cargo run --release -- --katdir KAT + cargo run --release -- --katdir kat MEMSAN: name: Memory Sanitizer build runs-on: [ubuntu-20.04] diff --git a/test/bench/kyber.cc b/test/bench/kyber.cc index 1663677f..ca748a4e 100644 --- a/test/bench/kyber.cc +++ b/test/bench/kyber.cc @@ -14,7 +14,7 @@ extern "C" { #include "kem/kyber/kyber512/avx2/ntt.h" } -auto cpucycle = [](benchmark::State &st, int64_t cycles) { +static auto cpucycle = [](benchmark::State &st, int64_t cycles) { st.counters["CPU cycles: mean"] = benchmark::Counter( cycles, benchmark::Counter::kAvgIterations | benchmark::Counter::kResultNoFormat); }; diff --git a/test/bench/sphincs.cc b/test/bench/sphincs.cc index 75f7afd9..8d6664f5 100644 --- a/test/bench/sphincs.cc +++ b/test/bench/sphincs.cc @@ -11,7 +11,7 @@ #define ARRAY_LEN(X) sizeof(X)/sizeof(X[0]) -auto cpucycle = [](benchmark::State &st, int64_t cycles) { +static auto cpucycle = [](benchmark::State &st, int64_t cycles) { st.counters["CPU cycles: mean"] = benchmark::Counter( cycles, benchmark::Counter::kAvgIterations | benchmark::Counter::kResultNoFormat); }; diff --git a/test/katrunner/src/main.rs b/test/katrunner/src/main.rs index 79e7d569..dc1a107d 100644 --- a/test/katrunner/src/main.rs +++ b/test/katrunner/src/main.rs @@ -5,12 +5,20 @@ use std::env; use std::path::Path; use threadpool::ThreadPool; use std::convert::TryInto; -use aes_ctr_drbg::DrbgCtx; +use aes_ctr_drbg::DrbgCtx as AesCtrDrbgCtx; use std::collections::HashMap; use std::thread; use std::sync::Mutex; use lazy_static::lazy_static; +#[derive(PartialEq)] +enum TestStatus { + Processed, + // For now this variant is not used + #[allow(dead_code)] + Skipped, +} + // Used for signature algorithm registration macro_rules! REG_SIGN { ($ID:expr,$F:expr) => { @@ -33,13 +41,88 @@ macro_rules! REG_KEM { execfn: test_kem_vector} } } +// Needed to implement get_rng. +trait Rng { + fn init(&mut self, entropy: &[u8], diversifier: Vec); + fn get_random(&mut self, data: &mut [u8]); +} + +// DummyDrbg just returns value of self.data. Useful +// in testing functions that are non-deterministic. +struct DummyDrbgContext{ + data: Vec, +} + +impl Rng for DummyDrbgContext { + fn init(&mut self, entropy: &[u8], _diversifier: Vec) { + self.data = entropy.to_vec(); + } + fn get_random(&mut self, data: &mut [u8]) { + for i in 0..std::cmp::min(data.len(), self.data.len()) { + data[i] = self.data[i]; + } + } +} + +// DrbgCtx uses AES-CTR to generate random byte-string. +impl Rng for AesCtrDrbgCtx { + fn init(&mut self, entropy: &[u8], diversifier: Vec) { + self.init(entropy, diversifier); + } + fn get_random(&mut self, data: &mut [u8]) { + self.get_random(data); + } +} + +// Allows to use some custom Drbg that implements +// Rng trait. In Current implementation if `use_aes` +// is set then AesCtrDrbgCtx is used (default), otherwise +// DummyDrbg is used. +struct CustomizableDrbgCtx{ + aes_drbg: AesCtrDrbgCtx, + dummy: DummyDrbgContext, + use_aes: bool, +} + +impl CustomizableDrbgCtx{ + pub const fn new() -> Self { + Self { + use_aes: true, + aes_drbg: AesCtrDrbgCtx::new(), + dummy: DummyDrbgContext{ + data: Vec::new(), + }, + } + } + + // TODO: possibily to be done with Box<> or something + fn get_rng(&mut self) -> &mut dyn Rng { + if self.use_aes { + return &mut self.aes_drbg; + } else { + return &mut self.dummy; + } + } + + pub fn init(&mut self, entropy: &[u8], diversifier: Vec, is_fixed: bool) { + if is_fixed { + self.use_aes = false; + } + self.get_rng().init(entropy, diversifier); + } + + pub fn get_random(&mut self, data: &mut [u8]) { + self.get_rng().get_random(data); + } +} // Stores one DRBG context per execution thread. DRBG // is inserted in this map, just after thread starts // and removed after thread is finished. Operation // is synchronized. lazy_static! { - static ref DRBGV: Mutex> = Mutex::new(HashMap::new()); + static ref DRBGV: Mutex< + HashMap> = Mutex::new(HashMap::new()); } // We have to provide the implementation for randombytes @@ -53,22 +136,21 @@ unsafe extern "C" fn randombytes( if let Some(drbg) = DRBGV.lock().unwrap().get_mut(&thread::current().id()) { drbg.get_random(&mut slice); } - } -type ExecFn = fn(&TestVector); +type ExecFn = fn(&TestVector) -> TestStatus; struct Register { kat: katwalk::reader::Kat, execfn: ExecFn, } -fn test_sign_vector(el: &TestVector) { +fn test_sign_vector(el: &TestVector) -> TestStatus { let mut pk = Vec::new(); let mut sk = Vec::new(); let mut sm = Vec::new(); if let Some(drbg) = DRBGV.lock().unwrap().get_mut(&thread::current().id()) { - drbg.init(el.sig.seed.as_slice(), Vec::new()); + drbg.init(el.sig.seed.as_slice(), Vec::new(), false); } unsafe { @@ -114,17 +196,18 @@ fn test_sign_vector(el: &TestVector) { el.sig.msg.as_ptr(), el.sig.msg.len() as u64, el.sig.pk.as_ptr()), true); + return TestStatus::Processed; } } -fn test_kem_vector(el: &TestVector) { +fn test_kem_vector(el: &TestVector) -> TestStatus { let mut pk = Vec::new(); let mut sk = Vec::new(); let mut ct = Vec::new(); let mut ss = Vec::new(); if let Some(drbg) = DRBGV.lock().unwrap().get_mut(&thread::current().id()) { - drbg.init(el.kem.seed.as_slice(), Vec::new()); + drbg.init(el.kem.seed.as_slice(), Vec::new(), false); } unsafe { @@ -161,6 +244,7 @@ fn test_kem_vector(el: &TestVector) { true); assert_eq!(ss, el.kem.ss); } + return TestStatus::Processed; } // KAT test register @@ -221,30 +305,50 @@ const KATS: &'static[Register] = &[ REG_KEM!(PQC_ALG_KEM_SIKE434, "round3/sike/PQCkemKAT_374.rsp"), ]; -fn execute(kat_dir: String, thc: usize, file_filter: &str) { +// Main loop +fn execute(kat_dir: String, thc: usize, file_filter: &str) -> u8 { // Can't do multi-threads as DRBG context is global let pool = ThreadPool::new(thc); + let path = Path::new(&kat_dir); + let mut code: u8 = 0; + for k in KATS.iter() { - let tmp = kat_dir.clone(); if !file_filter.is_empty() && !k.kat.kat_file.contains(file_filter) { continue; } + + // Check if file exists + let filepath = path.join(k.kat.kat_file); + let fhandle = match File::open(filepath) { + Ok(fhandle) => fhandle, + Err(_) => { + eprintln!("File {:?} doesn't exist", k.kat.kat_file); + code |= 1; + continue; + } + }; + let buf = BufReader::new(fhandle); + pool.execute(move || { DRBGV.lock().unwrap() - .insert(thread::current().id(), DrbgCtx::new()); - let f = Path::new(&tmp.to_string()).join(k.kat.kat_file); - let file = File::open(format!("{}", f.to_str().unwrap())); - println!("Processing file: {}", Path::new(k.kat.kat_file).to_str().unwrap()); - let b = BufReader::new(file.unwrap()); - - for el in KatReader::new(b, k.kat.scheme_type, k.kat.scheme_id) { - (k.execfn)(&el); + .insert(thread::current().id(), CustomizableDrbgCtx::new()); + let proc = KatReader::new(buf, k.kat.scheme_type, k.kat.scheme_id); + let iter = proc.into_iter(); + let mut processed = 0; + let mut skipped = 0; + for el in iter { + let status = (k.execfn)(&el); + match status { + TestStatus::Processed => processed += 1, + TestStatus::Skipped => skipped += 1, + } } - DRBGV.lock().unwrap() - .remove(&thread::current().id()); + println!("{:60} KAT# : {:10}{:10}", k.kat.kat_file, processed,skipped); + DRBGV.lock().unwrap().remove(&thread::current().id()); }); } pool.join(); + return code; } fn main() { @@ -259,6 +363,7 @@ fn main() { argmap.insert(&args[i], &args[i+1]); } + // Number of threads to be used let thread_number: usize = match argmap.get(&"--threads".to_string()) { Some(n) => n.to_string().parse::().unwrap(), None => 4 /* by default 4 threads */, @@ -270,8 +375,13 @@ fn main() { None => "" }; + // Header for the results + println!("Test file{:60}Processed Skipped", ""); + println!("------------------------------------------------------------------------------------------"); match argmap.get(&"--katdir".to_string()) { - Some(kat_dir) => execute(kat_dir.to_string(), thread_number, file_filter), + Some(kat_dir) => + std::process::exit( + execute(kat_dir.to_string(), thread_number, file_filter).into()), None => panic!("--katdir required") };