-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CPA implementation authored by Karim M. Abdellatif
- Loading branch information
1 parent
7bb6ca7
commit 916784f
Showing
9 changed files
with
375 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// use simple_bar::ProgressBar; | ||
use indicatif::ProgressIterator; | ||
use muscat::cpa::*; | ||
use muscat::leakage::{hw, sbox}; | ||
use muscat::util::{progress_bar, read_array_2_from_npy_file, save_array}; | ||
use ndarray::*; | ||
use rayon::prelude::{ParallelBridge, ParallelIterator}; | ||
use std::time::Instant; | ||
|
||
// traces format | ||
type FormatTraces = i16; | ||
type FormatMetadata = i32; | ||
|
||
// leakage model | ||
pub fn leakage_model(value: usize, guess: usize) -> usize { | ||
hw(sbox((value ^ guess) as u8) as usize) | ||
} | ||
|
||
// multi-threading cpa | ||
fn cpa() { | ||
let size: usize = 5000; // Number of samples | ||
let guess_range = 256; // 2**(key length) | ||
let target_byte = 1; | ||
let folder = String::from("../../data"); // Directory of leakages and metadata | ||
let nfiles = 5; // Number of files in the directory. TBD: Automating this value | ||
|
||
/* Parallel operation using multi-threading on patches */ | ||
let mut cpa = (0..nfiles) | ||
.into_iter() | ||
.progress_with(progress_bar(nfiles)) | ||
.map(|n| { | ||
let dir_l = format!("{folder}/l{n}.npy"); | ||
let dir_p = format!("{folder}/p{n}.npy"); | ||
let leakages: Array2<FormatTraces> = read_array_2_from_npy_file(&dir_l); | ||
let plaintext: Array2<FormatMetadata> = read_array_2_from_npy_file(&dir_p); | ||
(leakages, plaintext) | ||
}) | ||
.into_iter() | ||
.par_bridge() | ||
.map(|patch| { | ||
let mut c: Cpa = Cpa::new(size, guess_range, target_byte, leakage_model); | ||
let len_leakage = patch.0.shape()[0]; | ||
for i in 0..len_leakage { | ||
c.update( | ||
patch.0.row(i).map(|x| *x as usize), | ||
patch.1.row(i).map(|y| *y as usize), | ||
); | ||
} | ||
c | ||
}) | ||
.reduce( | ||
|| Cpa::new(size, guess_range, target_byte, leakage_model), | ||
|a: Cpa, b| a + b, | ||
); | ||
cpa.finalize(); | ||
println!("Guessed key = {}", cpa.pass_guess()); | ||
// save corr key curves in npy | ||
save_array("../results/corr.npy", &cpa.pass_corr_array()); | ||
} | ||
|
||
fn main() { | ||
cpa(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use muscat::cpa::*; | ||
use muscat::leakage::{hw, sbox}; | ||
use muscat::util::{progress_bar, read_array_2_from_npy_file, save_array}; | ||
use ndarray::*; | ||
use rayon::prelude::{ParallelBridge, ParallelIterator}; | ||
use simple_bar::ProgressBar; | ||
use std::time::Instant; | ||
|
||
// traces format | ||
type FormatTraces = i16; | ||
type FormatMetadata = i32; | ||
|
||
// leakage model | ||
pub fn leakage_model(value: usize, guess: usize) -> usize { | ||
hw(sbox((value ^ guess) as u8) as usize) | ||
} | ||
|
||
fn rank() { | ||
let size: usize = 5000; // Number of samples | ||
let guess_range = 256; // 2**(key length) | ||
let target_byte = 1; | ||
let folder = String::from("../../data"); | ||
let nfiles = 5; | ||
// let mut bar = ProgressBar::default(nfiles as u32, 50, false); | ||
let bar = progress_bar(nfiles); | ||
let chunk = 3000; | ||
let mut rank = Cpa::new(size, guess_range, target_byte, leakage_model); | ||
for file in 0..nfiles { | ||
let dir_l = format!("{folder}/l{file}.npy"); | ||
let dir_p = format!("{folder}/p{file}.npy"); | ||
let leakages: Array2<FormatTraces> = read_array_2_from_npy_file(&dir_l); | ||
let plaintext: Array2<FormatMetadata> = read_array_2_from_npy_file(&dir_p); | ||
let len_file = leakages.shape()[0]; | ||
for sample in (0..len_file).step_by(chunk) { | ||
let l_sample: ndarray::ArrayBase< | ||
ndarray::ViewRepr<&FormatTraces>, | ||
ndarray::Dim<[usize; 2]>, | ||
> = leakages.slice(s![sample..sample + chunk, ..]); | ||
let p_sample = plaintext.slice(s![sample..sample + chunk, ..]); | ||
let x = (0..chunk) | ||
.into_iter() | ||
.par_bridge() | ||
.fold( | ||
|| Cpa::new(size, guess_range, target_byte, leakage_model), | ||
|mut r: Cpa, n| { | ||
r.update( | ||
l_sample.row(n).map(|l: &FormatTraces| *l as usize), | ||
p_sample.row(n).map(|p: &FormatMetadata| *p as usize), | ||
); | ||
r | ||
}, | ||
) | ||
.reduce( | ||
|| Cpa::new(size, guess_range, target_byte, leakage_model), | ||
|lhs, rhs| lhs + rhs, | ||
); | ||
rank = rank + x; | ||
rank.finalize(); | ||
} | ||
bar.inc(file as u64); | ||
} | ||
// save rank key curves in npy | ||
save_array("../results/rank.npy", &rank.pass_rank()); | ||
} | ||
|
||
fn main() { | ||
rank(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
use ndarray::{concatenate, s, Array1, Array2, ArrayView1, ArrayView2, Axis}; | ||
use rayon::prelude::{IntoParallelIterator, ParallelIterator}; | ||
use std::ops::Add; | ||
|
||
pub struct Cpa { | ||
sum_leakages: Array1<usize>, | ||
sig_leakages: Array1<usize>, | ||
sum_keys: Array1<usize>, | ||
sig_keys: Array1<usize>, | ||
values: Array1<usize>, | ||
a_l: Array2<usize>, | ||
target_byte: i32, | ||
len_leakages: usize, | ||
guess_range: i32, | ||
corr: Array2<f32>, | ||
max_corr: Array2<f32>, | ||
rank_slice: Array2<f32>, | ||
leakage_func: fn(usize, usize) -> usize, | ||
len_samples: usize, | ||
} | ||
|
||
/* This class implements the CPA shown in this paper: https://eprint.iacr.org/2013/794.pdf */ | ||
impl Cpa { | ||
pub fn new( | ||
size: usize, | ||
guess_range: i32, | ||
target_byte: i32, | ||
f: fn(usize, usize) -> usize, | ||
) -> Self { | ||
Self { | ||
len_samples: size, | ||
a_l: Array2::zeros((guess_range as usize, size)), | ||
target_byte: target_byte, | ||
guess_range: guess_range, | ||
sum_leakages: Array1::zeros(size), | ||
sig_leakages: Array1::zeros(size), | ||
sum_keys: Array1::zeros(guess_range as usize), | ||
sig_keys: Array1::zeros(guess_range as usize), | ||
values: Array1::zeros(guess_range as usize), | ||
corr: Array2::zeros((guess_range as usize, size)), | ||
max_corr: Array2::zeros((guess_range as usize, 1)), | ||
rank_slice: Array2::zeros((guess_range as usize, 1)), | ||
leakage_func: f, | ||
len_leakages: 0, | ||
} | ||
} | ||
|
||
pub fn update(&mut self, trace: Array1<usize>, plaintext: Array1<usize>) { | ||
/* This function updates the main arrays of the CPA, as shown in Alg. 4 | ||
in the paper.*/ | ||
self.len_leakages += 1; | ||
self.gen_values(plaintext.clone(), self.guess_range, self.target_byte); | ||
self.go(trace, plaintext.clone(), self.guess_range); | ||
} | ||
|
||
pub fn gen_values(&mut self, metadata: Array1<usize>, _guess_range: i32, _target_key: i32) { | ||
for guess in 0.._guess_range { | ||
self.values[guess as usize] = | ||
(self.leakage_func)(metadata[_target_key as usize], guess as usize) as usize; | ||
} | ||
} | ||
|
||
pub fn go(&mut self, _trace: Array1<usize>, metadata: Array1<usize>, _guess_range: i32) { | ||
for i in 0..self.len_samples { | ||
self.sum_leakages[i] += _trace[i] as usize; | ||
self.sig_leakages[i] += (_trace[i] * _trace[i]) as usize; | ||
} | ||
|
||
for guess in 0.._guess_range { | ||
self.sum_keys[guess as usize] += self.values[guess as usize] as usize; | ||
self.sig_keys[guess as usize] += | ||
(self.values[guess as usize] * self.values[guess as usize]) as usize; | ||
} | ||
let partition: usize = metadata[self.target_byte as usize] as usize; | ||
for i in 0..self.len_samples { | ||
self.a_l[[partition, i]] += _trace[i] as usize; | ||
} | ||
} | ||
|
||
pub fn finalize(&mut self) { | ||
/* This function finalizes the calculation after feeding the | ||
overall traces */ | ||
|
||
let shape_p = self.guess_range as usize; | ||
let mut p: ndarray::ArrayBase<ndarray::OwnedRepr<usize>, ndarray::Dim<[usize; 2]>> = | ||
Array2::zeros((shape_p, shape_p)); | ||
for i in 0..self.guess_range { | ||
for x in 0..self.guess_range { | ||
p[[x as usize, i as usize]] = (self.leakage_func)(x as usize, i as usize) as usize; | ||
} | ||
} | ||
for i in 0..self.guess_range { | ||
let _sigkeys = self.sig_keys[i as usize] as f32 / self.len_leakages as f32; | ||
let _sumkeys = self.sum_keys[i as usize] as f32 / self.len_leakages as f32; | ||
let lower1: f32 = _sigkeys - (_sumkeys * _sumkeys); | ||
/* Parallel operation using multi-threading */ | ||
let tmp: Vec<f32> = (0..self.len_samples) | ||
.into_par_iter() | ||
.map(|x| { | ||
let _sumleakages = | ||
self.sum_leakages[x as usize] as f32 / self.len_leakages as f32; | ||
let _sigleakages = | ||
self.sig_leakages[x as usize] as f32 / self.len_leakages as f32; | ||
let slice_a = self.a_l.slice(s![.., x]); | ||
let slice_b = p.slice(s![.., i]); | ||
let summult: i32 = self.sum_mult(slice_a, slice_b); | ||
let upper1: f32 = summult as f32 / self.len_leakages as f32; | ||
let upper: f32 = upper1 - (_sumkeys * _sumleakages); | ||
let lower2: f32 = _sigleakages - (_sumleakages * _sumleakages); | ||
let lower = f32::sqrt(lower1 * lower2); | ||
f32::abs(upper / lower) | ||
}) | ||
.collect(); | ||
|
||
for z in 0..self.len_samples { | ||
self.corr[[i as usize, z]] = tmp[z]; | ||
} | ||
} | ||
self.calculation(); | ||
} | ||
|
||
pub fn calculation(&mut self) { | ||
// let mut max_256: Array2<f32> = Array2::zeros((self._guess_range as usize, 1)); | ||
for i in 0..self.guess_range { | ||
let row = self.corr.row(i as usize); | ||
// Calculating the max value in the row | ||
let max_value = row | ||
.into_iter() | ||
.reduce(|a, b| { | ||
let mut tmp = a; | ||
if tmp < b { | ||
tmp = b; | ||
} | ||
tmp | ||
}) | ||
.unwrap(); | ||
self.max_corr[[i as usize, 0]] = *max_value; | ||
} | ||
self.rank_slice = concatenate![Axis(1), self.rank_slice, self.max_corr]; | ||
} | ||
|
||
pub fn pass_rank(&self) -> ArrayView2<f32> { | ||
self.rank_slice.slice(s![.., 1..]) | ||
} | ||
|
||
pub fn pass_corr_array(&self) -> Array2<f32> { | ||
self.corr.clone() | ||
} | ||
|
||
pub fn pass_guess(&self) -> i32 { | ||
let mut init_value: f32 = 0.0; | ||
let mut guess: i32 = 0; | ||
for i in 0..self.guess_range { | ||
if self.max_corr[[i as usize, 0]] > init_value { | ||
init_value = self.max_corr[[i as usize, 0]]; | ||
guess = i; | ||
} | ||
} | ||
|
||
guess | ||
} | ||
|
||
fn sum_mult(&self, a: ArrayView1<usize>, b: ArrayView1<usize>) -> i32 { | ||
a.dot(&b) as i32 | ||
} | ||
} | ||
|
||
impl Add for Cpa { | ||
type Output = Self; | ||
fn add(self, rhs: Self) -> Self::Output { | ||
Self { | ||
sum_leakages: self.sum_leakages + rhs.sum_leakages, | ||
sig_leakages: self.sig_leakages + rhs.sig_leakages, | ||
sum_keys: self.sum_keys + rhs.sum_keys, | ||
sig_keys: self.sig_keys + rhs.sig_keys, | ||
values: self.values + rhs.values, | ||
a_l: self.a_l + rhs.a_l, | ||
target_byte: rhs.target_byte, | ||
len_leakages: self.len_leakages + rhs.len_leakages, | ||
guess_range: rhs.guess_range, | ||
corr: self.corr + rhs.corr, | ||
max_corr: self.max_corr, | ||
rank_slice: self.rank_slice, | ||
len_samples: rhs.len_samples, | ||
leakage_func: self.leakage_func, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
pub const SBOX: [u8; 256] = [ | ||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, | ||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, | ||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, | ||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, | ||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, | ||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, | ||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, | ||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, | ||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, | ||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, | ||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, | ||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, | ||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, | ||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, | ||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, | ||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, | ||
]; | ||
|
||
pub fn sbox(index: u8) -> u8 { | ||
SBOX[index as usize] | ||
} | ||
|
||
pub fn hw(value: usize) -> usize { | ||
let mut tmp = 0; | ||
for i in 0..8 { | ||
if (value & (1 << i)) == (1 << i) { | ||
tmp += 1; | ||
} | ||
} | ||
tmp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
pub mod cpa; | ||
pub mod leakage; | ||
pub mod processors; | ||
pub mod quicklog; | ||
pub mod trace; | ||
pub mod util; | ||
pub mod util; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.