diff --git a/benches/Cargo.toml b/benches/Cargo.toml index adf663a..aacc25b 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -12,11 +12,6 @@ criterion = "0.5" secured-enclave = { path = "../enclave/" } secured-cipher = { path = "../cipher/" } -[[bench]] -name = "enclave" -path = "src/enclave.rs" -harness = false - [[bench]] name = "chacha20" path = "src/chacha20.rs" diff --git a/benches/src/chacha20.rs b/benches/src/chacha20.rs index 529cd7b..ef4a567 100644 --- a/benches/src/chacha20.rs +++ b/benches/src/chacha20.rs @@ -1,20 +1,34 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use criterion::{ + criterion_group, criterion_main, BenchmarkId, Criterion, PlotConfiguration, Throughput, +}; use secured_cipher::chacha20::{ChaChaStream, KEY_SIZE, NONCE_SIZE}; const KB: usize = 1024; fn bench(c: &mut Criterion) { let mut group = c.benchmark_group("ChaChaStream"); - - for size in &[KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB] { + let plot_config = PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic); + group.plot_config(plot_config); + + for size in &[ + KB, + 2 * KB, + 4 * KB, + 8 * KB, + 16 * KB, + 32 * KB, + 64 * KB, + 128 * KB, + 256 * KB, + ] { let key = [0u8; KEY_SIZE]; let iv = [1u8; NONCE_SIZE]; group.throughput(Throughput::Bytes(*size as u64)); - group.bench_with_input(BenchmarkId::new("new", size), size, |b, &_size| { - b.iter(|| ChaChaStream::new(key, iv)); - }); + // group.bench_with_input(BenchmarkId::new("new", size), size, |b, &_size| { + // b.iter(|| ChaChaStream::new(key, iv)); + // }); let mut stream = ChaChaStream::new(key, iv); group.bench_with_input(BenchmarkId::new("process", size), size, |b, &_size| { diff --git a/benches/src/enclave.rs b/benches/src/enclave.rs deleted file mode 100644 index dff6ba6..0000000 --- a/benches/src/enclave.rs +++ /dev/null @@ -1,26 +0,0 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use secured_cipher::chacha20::{KEY_SIZE, NONCE_SIZE}; -use secured_enclave::Enclave; - -const KB: usize = 1024; - -fn bench(c: &mut Criterion) { - let mut group = c.benchmark_group("enclave"); - - for size in &[KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB] { - let key = [0u8; KEY_SIZE]; - - group.throughput(Throughput::Bytes(*size as u64)); - group.bench_with_input(BenchmarkId::new("encrypt", size), size, |b, &_size| { - b.iter(|| { - let buf = vec![0u8; *size]; - Enclave::from_plain_bytes("Metadata", key, buf) - }); - }); - } - - group.finish(); -} - -criterion_group!(benches, bench); -criterion_main!(benches); diff --git a/cipher/src/chacha20/core.rs b/cipher/src/chacha20/core.rs index 75a5611..d6292ca 100644 --- a/cipher/src/chacha20/core.rs +++ b/cipher/src/chacha20/core.rs @@ -86,6 +86,27 @@ pub fn seek_keystream(state: &[u32; 16], n: u64) -> [u32; 16] { keystream } +/// XORs two 512-bit state arrays. +/// This function modifies the first array in place. +/// +/// # Arguments +/// * `a` - A mutable reference to the first state array. +/// * `b` - A reference to the second state array. +/// +/// # Panics +/// Panics if the two arrays are not of equal length. +pub fn xor(left: &mut [u32], right: &[u32]) { + assert_eq!( + left.len(), + right.len(), + "State arrays must be of equal length" + ); + left + .iter_mut() + .zip(right.iter()) + .for_each(|(left, right)| *left ^= *right); +} + /// Safely increments the 2-word block counter of the ChaCha20 state. /// /// This function increments the lower 32 bits of the counter and, if there is an overflow, diff --git a/cipher/src/chacha20/stream.rs b/cipher/src/chacha20/stream.rs index 37d660b..dc4a92b 100644 --- a/cipher/src/chacha20/stream.rs +++ b/cipher/src/chacha20/stream.rs @@ -9,7 +9,7 @@ use crate::{Bytes, Slice}; use rayon::prelude::*; use super::core::{ - chacha20_rounds, safe_2words_counter_increment, seek_keystream, to_u32_slice, u32_to_u8_vec, + chacha20_rounds, safe_2words_counter_increment, seek_keystream, to_u32_slice, u32_to_u8_vec, xor, CONSTANTS, STATE_WORDS, }; @@ -52,7 +52,8 @@ impl ChaChaStream { // The block counter occupies the next two words (13th and 14th positions) in the state. // In ChaCha20, this counter is used to make each block unique. - // Here, it's initialized from the first half of the 8-byte IV (initialization vector). + // We skip those they are set to zero already. + // Here, we use the last 8-byte space of the block for the IV (initialization vector). let iv_chunks = iv.chunks_exact(4); for (val, chunk) in state[14..16].iter_mut().zip(iv_chunks) { *val = u32::from_le_bytes(chunk.try_into().unwrap()); @@ -119,20 +120,20 @@ impl ChaChaStream { // Wrap the state in an Arc to allow for parallel processing let arc_state = Arc::new(self.state); - // Process each 64-byte block in parallel - out.par_chunks_mut(64).enumerate().for_each(|(i, chunk)| { - let chunk_keystream = seek_keystream(&arc_state, i as u64); - - for (k, out_elem) in chunk.iter_mut().enumerate() { - if k >= chunk_keystream.len() { - // If the chunk is smaller than 64 bytes, the keystream will be shorter than 64 bytes. - break; - } - - // XOR the output with the keystream - *out_elem ^= chunk_keystream[k]; - } - }); + // Process each chunk of 8 blocks in parallel + out + .par_chunks_mut(128) + .enumerate() + .for_each(|(i, blocks_chunk)| { + blocks_chunk + .chunks_mut(STATE_WORDS) + .enumerate() + .for_each(|(j, block)| { + // Cipher each 64-byte block in the chunk + let chunk_keystream = seek_keystream(&arc_state, (i + j) as u64); + xor(block, &chunk_keystream); + }); + }); // Clear the keystream self.clear_stream(); @@ -164,13 +165,11 @@ mod tests { 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, ]; const CIPHERTEXT: [u8; 100] = [ - 0x92, 0x96, 0x40, 0xC6, 0x6F, 0x7A, 0xA6, 0x3E, 0x40, 0x5E, 0x1B, 0x1D, 0x94, 0xA4, 0x8D, 0x69, - 0xC7, 0x16, 0xBE, 0xDF, 0x8D, 0xD2, 0xE6, 0xDA, 0xDE, 0xF7, 0xD5, 0xE7, 0x15, 0x18, 0x51, 0x5D, - 0x40, 0x67, 0x67, 0x60, 0x8A, 0x00, 0x82, 0xE1, 0x37, 0x97, 0x41, 0x61, 0xFA, 0xAC, 0xD1, 0x14, - 0x09, 0xC5, 0x00, 0x32, 0xB1, 0xD0, 0xF1, 0xBD, 0x69, 0x7C, 0x3F, 0x93, 0x27, 0xDA, 0xDD, 0xF1, - 0x63, 0x75, 0x72, 0x61, 0x20, 0x63, 0x68, 0x65, 0x20, 0x6C, 0x61, 0x20, 0x64, 0x69, 0x72, 0x69, - 0x74, 0x74, 0x61, 0x20, 0x76, 0x69, 0x61, 0x20, 0x65, 0x72, 0x61, 0x20, 0x73, 0x6D, 0x61, 0x72, - 0x72, 0x69, 0x74, 0x61, + 146, 150, 64, 198, 111, 122, 166, 62, 64, 94, 27, 29, 148, 164, 141, 105, 199, 22, 190, 223, + 141, 210, 230, 218, 222, 247, 213, 231, 21, 24, 81, 93, 64, 103, 103, 96, 138, 0, 130, 225, 55, + 151, 65, 97, 250, 172, 209, 20, 9, 197, 0, 50, 177, 208, 241, 189, 105, 124, 63, 147, 39, 218, + 221, 241, 191, 134, 94, 135, 34, 124, 180, 33, 15, 18, 30, 88, 156, 237, 156, 97, 222, 15, 182, + 145, 219, 223, 238, 218, 213, 234, 199, 179, 20, 20, 16, 89, 91, 122, 114, 33, ]; const IV: [u8; 8] = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8];