diff --git a/benches/dpa.rs b/benches/dpa.rs index 55a9e07..9aad207 100644 --- a/benches/dpa.rs +++ b/benches/dpa.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use muscat::dpa::{dpa, Dpa}; +use muscat::dpa::{dpa, Dpa, DpaProcessor}; use muscat::leakage::sbox; use ndarray::{Array1, Array2}; use ndarray_rand::rand::{rngs::StdRng, SeedableRng}; @@ -10,19 +10,17 @@ fn selection_function(metadata: Array1, guess: usize) -> usize { sbox(metadata[1] ^ guess as u8).into() } -fn dpa_sequential(leakages: &Array2, plaintexts: &Array2) -> Dpa> { - let mut dpa = Dpa::new(leakages.shape()[1], 256, selection_function); +fn dpa_sequential(leakages: &Array2, plaintexts: &Array2) -> Dpa { + let mut dpa = DpaProcessor::new(leakages.shape()[1], 256, selection_function); for i in 0..leakages.shape()[0] { dpa.update(leakages.row(i), plaintexts.row(i).to_owned()); } - dpa.finalize(); - - dpa + dpa.finalize() } -fn dpa_parallel(leakages: &Array2, plaintexts: &Array2) -> Dpa> { +fn dpa_parallel(leakages: &Array2, plaintexts: &Array2) -> Dpa { dpa( leakages.view(), plaintexts diff --git a/examples/dpa.rs b/examples/dpa.rs index d80029c..370c829 100644 --- a/examples/dpa.rs +++ b/examples/dpa.rs @@ -1,6 +1,6 @@ use anyhow::Result; use indicatif::ProgressIterator; -use muscat::dpa::Dpa; +use muscat::dpa::DpaProcessor; use muscat::leakage::sbox; use muscat::util::read_array2_from_npy_file; use ndarray::{s, Array1, Array2}; @@ -26,7 +26,7 @@ fn dpa() -> Result<()> { let leakages: Array2 = read_array2_from_npy_file::(&dir_l)?; let plaintext: Array2 = read_array2_from_npy_file::(&dir_p)?; let len_traces = 20000; //leakages.shape()[0]; - let mut dpa = Dpa::new(size, guess_range, leakage_model); + let mut dpa = DpaProcessor::new(size, guess_range, leakage_model); for i in (0..len_traces).progress() { let tmp_trace = leakages .row(i) @@ -35,7 +35,7 @@ fn dpa() -> Result<()> { let tmp_metadata = plaintext.row(i); dpa.update(tmp_trace.view(), tmp_metadata.to_owned()); } - dpa.finalize(); + let dpa = dpa.finalize(); println!("Guessed key = {:02x}", dpa.pass_guess()); // let corr = dpa.pass_corr_array(); @@ -54,7 +54,7 @@ fn dpa_success() -> Result<()> { let leakages: Array2 = read_array2_from_npy_file::(&dir_l)?; let plaintext: Array2 = read_array2_from_npy_file::(&dir_p)?; let len_traces = leakages.shape()[0]; - let mut dpa = Dpa::new(size, guess_range, leakage_model); + let mut dpa = DpaProcessor::new(size, guess_range, leakage_model); let rank_traces: usize = 100; dpa.assign_rank_traces(rank_traces); @@ -67,6 +67,7 @@ fn dpa_success() -> Result<()> { dpa.update_success(tmp_trace.view(), tmp_metadata); } + let dpa = dpa.finalize(); println!("Guessed key = {:02x}", dpa.pass_guess()); // let succss = dpa.pass_rank().to_owned(); @@ -96,7 +97,7 @@ fn dpa_parallel() -> Result<()> { let tmp_metadata = plaintext .slice(s![range_rows..range_rows + batch, ..]) .to_owned(); - let mut dpa_inner = Dpa::new(size, guess_range, leakage_model); + let mut dpa_inner = DpaProcessor::new(size, guess_range, leakage_model); for i in 0..batch { let trace = tmp_leakages.row(i); let metadata = tmp_metadata.row(i).to_owned(); @@ -104,9 +105,12 @@ fn dpa_parallel() -> Result<()> { } dpa_inner }) - .reduce(|| Dpa::new(size, guess_range, leakage_model), |x, y| x + y); + .reduce( + || DpaProcessor::new(size, guess_range, leakage_model), + |x, y| x + y, + ); - dpa.finalize(); + let dpa = dpa.finalize(); println!("{:2x}", dpa.pass_guess()); // let corr = dpa.pass_corr_array(); diff --git a/src/cpa.rs b/src/cpa.rs index b9d8a89..e3ccd50 100644 --- a/src/cpa.rs +++ b/src/cpa.rs @@ -50,7 +50,9 @@ where pub struct Cpa { /// Guess range upper excluded bound guess_range: usize, + /// Pearson correlation coefficients corr: Array2, + /// Max pearson correlation coefficients max_corr: Array1, rank_slice: Array2, } diff --git a/src/dpa.rs b/src/dpa.rs index 88da218..c2832f2 100644 --- a/src/dpa.rs +++ b/src/dpa.rs @@ -10,18 +10,18 @@ pub fn dpa( guess_range: usize, leakage_func: fn(M, usize) -> usize, chunk_size: usize, -) -> Dpa +) -> Dpa where T: Into + Copy + Sync, M: Clone + Sync, { - let mut dpa = zip( + zip( leakages.axis_chunks_iter(Axis(0), chunk_size), metadata.axis_chunks_iter(Axis(0), chunk_size), ) .par_bridge() .map(|(leakages_chunk, metadata_chunk)| { - let mut dpa = Dpa::new(leakages.shape()[1], guess_range, leakage_func); + let mut dpa = DpaProcessor::new(leakages.shape()[1], guess_range, leakage_func); for i in 0..leakages_chunk.shape()[0] { dpa.update(leakages_chunk.row(i), metadata_chunk[i].clone()); @@ -30,16 +30,40 @@ where dpa }) .reduce( - || Dpa::new(leakages.shape()[1], guess_range, leakage_func), + || DpaProcessor::new(leakages.shape()[1], guess_range, leakage_func), |a, b| a + b, - ); + ) + .finalize() +} + +pub struct Dpa { + /// Guess range upper excluded bound + guess_range: usize, + corr: Array2, + max_corr: Array1, +} - dpa.finalize(); +impl Dpa { + pub fn pass_corr_array(&self) -> ArrayView2 { + self.corr.view() + } - dpa + pub fn pass_guess(&self) -> usize { + let mut init_value = 0.0; + let mut guess = 0; + + for i in 0..self.guess_range { + if self.max_corr[i] > init_value { + init_value = self.max_corr[i]; + guess = i; + } + } + + guess + } } -pub struct Dpa { +pub struct DpaProcessor { /// Number of samples per trace len_samples: usize, /// Guess range upper excluded bound @@ -52,8 +76,6 @@ pub struct Dpa { count_0: Array1, /// Number of traces processed for which the selection function equals 1 count_1: Array1, - corr: Array2, - max_corr: Array1, rank_slice: Array2, rank_traces: usize, // Number of traces to calculate succes rate /// Selection function @@ -66,7 +88,7 @@ pub struct Dpa { https://paulkocher.com/doc/DifferentialPowerAnalysis.pdf https://web.mit.edu/6.857/OldStuff/Fall03/ref/kocher-DPATechInfo.pdf */ -impl Dpa { +impl DpaProcessor { pub fn new(size: usize, guess_range: usize, f: fn(M, usize) -> usize) -> Self { Self { len_samples: size, @@ -75,8 +97,6 @@ impl Dpa { sum_1: Array2::zeros((guess_range, size)), count_0: Array1::zeros(guess_range), count_1: Array1::zeros(guess_range), - corr: Array2::zeros((guess_range, size)), - max_corr: Array1::zeros(guess_range), rank_slice: Array2::zeros((guess_range, 1)), rank_traces: 0, leakage_func: f, @@ -125,21 +145,21 @@ impl Dpa { self.update(trace_batch, plaintext_batch); if self.len_leakages % self.rank_traces == 0 { - self.finalize(); + let dpa = self.finalize(); if self.len_leakages == self.rank_traces { - self.rank_slice = self + self.rank_slice = dpa .max_corr .clone() - .into_shape((self.max_corr.shape()[0], 1)) + .into_shape((dpa.max_corr.shape()[0], 1)) .unwrap(); } else { self.rank_slice = concatenate![ Axis(1), self.rank_slice, - self.max_corr + dpa.max_corr .clone() - .into_shape((self.max_corr.shape()[0], 1)) + .into_shape((dpa.max_corr.shape()[0], 1)) .unwrap() ]; } @@ -150,9 +170,8 @@ impl Dpa { self.rank_traces = value; } - pub fn finalize(&mut self) { - /* This function finalizes the calculation after - feeding all stored acc arrays */ + pub fn finalize(&mut self) -> Dpa { + /* This function finalizes the calculation after feeding all stored acc arrays */ let mut tmp_avg_0 = Array2::zeros((self.guess_range, self.len_samples)); let mut tmp_avg_1 = Array2::zeros((self.guess_range, self.len_samples)); @@ -162,11 +181,15 @@ impl Dpa { tmp_avg_0.row_mut(row).assign(&tmp_row_0); tmp_avg_1.row_mut(row).assign(&tmp_row_1); } - let diff = tmp_avg_0.clone() - tmp_avg_1; - self.corr = diff.map(|e| f32::abs(*e)); + let corr = (tmp_avg_0 - tmp_avg_1).map(|e| f32::abs(*e)); + let max_corr = max_per_row(corr.view()); - self.max_corr = max_per_row(self.corr.view()); + Dpa { + guess_range: self.guess_range, + corr, + max_corr, + } } pub fn success_traces(&mut self, traces_no: usize) { @@ -176,27 +199,9 @@ impl Dpa { pub fn pass_rank(&self) -> ArrayView2 { self.rank_slice.view() } - - pub fn pass_corr_array(&self) -> ArrayView2 { - self.corr.view() - } - - pub fn pass_guess(&self) -> usize { - let mut init_value = 0.0; - let mut guess = 0; - - for i in 0..self.guess_range { - if self.max_corr[i] > init_value { - init_value = self.max_corr[i]; - guess = i; - } - } - - guess - } } -impl Add for Dpa { +impl Add for DpaProcessor { type Output = Self; fn add(self, rhs: Self) -> Self::Output { @@ -211,8 +216,6 @@ impl Add for Dpa { sum_1: self.sum_1 + rhs.sum_1, count_0: self.count_0 + rhs.count_0, count_1: self.count_1 + rhs.count_1, - corr: self.corr + rhs.corr, - max_corr: self.max_corr, rank_slice: self.rank_slice, rank_traces: self.rank_traces, leakage_func: self.leakage_func,