diff --git a/Cargo.lock b/Cargo.lock index 95cc181b..e8c0c50c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,7 +305,7 @@ dependencies = [ [[package]] name = "mgm" -version = "0.4.4" +version = "0.4.5" dependencies = [ "aead", "cipher", diff --git a/mgm/CHANGELOG.md b/mgm/CHANGELOG.md index a928bd59..c5cea171 100644 --- a/mgm/CHANGELOG.md +++ b/mgm/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.5 (2021-08-26) +### Added +- Use parallel block encryption if possible ([#358]) + +[#358]: https://github.com/RustCrypto/AEADs/pull/358 + ## 0.4.4 (2021-08-24) ### Changed - Decrypt ciphertext only after tag verification ([#356]) diff --git a/mgm/Cargo.toml b/mgm/Cargo.toml index 42658f7b..0dec40df 100644 --- a/mgm/Cargo.toml +++ b/mgm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mgm" -version = "0.4.4" +version = "0.4.5" description = "Generic implementation of the Multilinear Galois Mode (MGM) cipher" authors = ["RustCrypto Developers"] edition = "2018" diff --git a/mgm/src/lib.rs b/mgm/src/lib.rs index e33cee98..63bba526 100644 --- a/mgm/src/lib.rs +++ b/mgm/src/lib.rs @@ -38,7 +38,7 @@ use aead::{ generic_array::{typenum::Unsigned, GenericArray}, AeadCore, AeadInPlace, Error, Key, NewAead, }; -use cipher::{BlockCipher, BlockEncrypt, NewBlockCipher}; +use cipher::{BlockCipher, BlockEncrypt, NewBlockCipher, ParBlocks}; use subtle::ConstantTimeEq; pub use aead; @@ -78,13 +78,13 @@ where C: BlockEncrypt, C::BlockSize: MgmBlockSize, { - fn apply_ks_block(&self, counter: &mut Counter, buf: &mut [u8]) { - let mut block = C::BlockSize::ctr2block(counter); + fn apply_ks_block(&self, ctr: &mut Counter, buf: &mut [u8]) { + let mut block = C::BlockSize::ctr2block(ctr); self.cipher.encrypt_block(&mut block); for i in 0..core::cmp::min(block.len(), buf.len()) { buf[i] ^= block[i]; } - C::BlockSize::incr_r(counter); + C::BlockSize::incr_r(ctr); } fn update_tag(&self, tag: &mut Element, tag_ctr: &mut Counter, block: &Block) { @@ -93,6 +93,43 @@ where tag.mul_sum(&h, block); C::BlockSize::incr_l(tag_ctr); } + + fn apply_par_ks_blocks(&self, ctr: &mut Counter, par_blocks: &mut [u8]) { + let pb = C::ParBlocks::USIZE; + let bs = C::BlockSize::USIZE; + assert_eq!(par_blocks.len(), pb * bs); + + let mut par_ks = ParBlocks::::default(); + for ks in par_ks.iter_mut() { + *ks = C::BlockSize::ctr2block(ctr); + C::BlockSize::incr_r(ctr); + } + self.cipher.encrypt_par_blocks(&mut par_ks); + + let iter = par_blocks.chunks_exact_mut(bs); + for (ks, block) in par_ks.iter().zip(iter) { + for i in 0..bs { + block[i] ^= ks[i]; + } + } + } + + fn update_par_tag(&self, tag: &mut Element, tag_ctr: &mut Counter, par_blocks: &[u8]) { + let pb = C::ParBlocks::USIZE; + let bs = C::BlockSize::USIZE; + assert_eq!(par_blocks.len(), pb * bs); + + let mut par_h = ParBlocks::::default(); + for h in par_h.iter_mut() { + *h = C::BlockSize::ctr2block(tag_ctr); + C::BlockSize::incr_l(tag_ctr); + } + self.cipher.encrypt_par_blocks(&mut par_h); + let iter = par_blocks.chunks_exact(bs).map(GenericArray::from_slice); + for (h, block) in par_h.iter().zip(iter) { + tag.mul_sum(h, block); + } + } } impl From for Mgm @@ -135,14 +172,17 @@ where fn encrypt_in_place_detached( &self, nonce: &Nonce, - adata: &[u8], - buffer: &mut [u8], + mut adata: &[u8], + mut buffer: &mut [u8], ) -> Result, Error> { // first nonce bit must be equal to zero if nonce[0] >> 7 != 0 { return Err(Error); } + let adata_len = adata.len(); + let msg_len = buffer.len(); + let mut tag_ctr = nonce.clone(); tag_ctr[0] |= 0b1000_0000; self.cipher.encrypt_block(&mut tag_ctr); @@ -150,8 +190,19 @@ where let mut tag = ::Element::new(); + let pb = C::ParBlocks::USIZE; + let bs = C::BlockSize::USIZE; + // process adata - let mut iter = adata.chunks_exact(C::BlockSize::USIZE); + if pb > 1 { + let mut iter = adata.chunks_exact(pb * bs); + for chunk in &mut iter { + self.update_par_tag(&mut tag, &mut tag_ctr, chunk); + } + adata = iter.remainder(); + }; + + let mut iter = adata.chunks_exact(bs); for block in (&mut iter).map(Block::::from_slice) { self.update_tag(&mut tag, &mut tag_ctr, block); } @@ -168,7 +219,16 @@ where let mut enc_ctr = C::BlockSize::block2ctr(&enc_ctr); // process plaintext - let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE); + if pb > 1 { + let mut iter = buffer.chunks_exact_mut(pb * bs); + for chunk in &mut iter { + self.apply_par_ks_blocks(&mut enc_ctr, chunk); + self.update_par_tag(&mut tag, &mut tag_ctr, chunk); + } + buffer = iter.into_remainder(); + } + + let mut iter = buffer.chunks_exact_mut(bs); for block in (&mut iter).map(Block::::from_mut_slice) { self.apply_ks_block(&mut enc_ctr, block); self.update_tag(&mut tag, &mut tag_ctr, block); @@ -183,7 +243,7 @@ where self.update_tag(&mut tag, &mut tag_ctr, &block); } - let block = C::BlockSize::lengths2block(adata.len(), buffer.len())?; + let block = C::BlockSize::lengths2block(adata_len, msg_len)?; self.update_tag(&mut tag, &mut tag_ctr, &block); let mut tag = tag.into_bytes(); @@ -195,8 +255,8 @@ where fn decrypt_in_place_detached( &self, nonce: &Nonce, - adata: &[u8], - buffer: &mut [u8], + mut adata: &[u8], + mut buffer: &mut [u8], expected_tag: &Tag, ) -> Result<(), Error> { // first nonce bit must be equal to zero @@ -204,6 +264,9 @@ where return Err(Error); } + let adata_len = adata.len(); + let msg_len = buffer.len(); + let mut tag_ctr = nonce.clone(); tag_ctr[0] |= 0b1000_0000; self.cipher.encrypt_block(&mut tag_ctr); @@ -211,8 +274,20 @@ where let mut tag_ctr = C::BlockSize::block2ctr(&tag_ctr); let mut tag = ::Element::new(); + let pb = C::ParBlocks::USIZE; + let bs = C::BlockSize::USIZE; + + // calculate tag // process adata - let mut iter = adata.chunks_exact(C::BlockSize::USIZE); + if pb > 1 { + let mut iter = adata.chunks_exact(pb * bs); + for chunk in &mut iter { + self.update_par_tag(&mut tag, &mut tag_ctr, chunk); + } + adata = iter.remainder(); + }; + + let mut iter = adata.chunks_exact(bs); for block in (&mut iter).map(Block::::from_slice) { self.update_tag(&mut tag, &mut tag_ctr, block); } @@ -223,11 +298,22 @@ where self.update_tag(&mut tag, &mut tag_ctr, &block); } - let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE); - for block in (&mut iter).map(Block::::from_mut_slice) { + // process ciphertext + let buf = if pb > 1 { + let mut iter = buffer.chunks_exact(pb * bs); + for chunk in &mut iter { + self.update_par_tag(&mut tag, &mut tag_ctr, chunk); + } + iter.remainder() + } else { + &buffer + }; + + let mut iter = buf.chunks_exact(bs); + for block in (&mut iter).map(Block::::from_slice) { self.update_tag(&mut tag, &mut tag_ctr, block); } - let rem = iter.into_remainder(); + let rem = iter.remainder(); if !rem.is_empty() { let n = rem.len(); @@ -237,7 +323,7 @@ where self.update_tag(&mut tag, &mut tag_ctr, &block); } - let block = C::BlockSize::lengths2block(adata.len(), buffer.len())?; + let block = C::BlockSize::lengths2block(adata_len, msg_len)?; self.update_tag(&mut tag, &mut tag_ctr, &block); let mut tag = tag.into_bytes(); @@ -253,7 +339,15 @@ where self.cipher.encrypt_block(&mut dec_ctr); let mut dec_ctr = C::BlockSize::block2ctr(&dec_ctr); - let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE); + if pb > 1 { + let mut iter = buffer.chunks_exact_mut(pb * bs); + for chunk in &mut iter { + self.apply_par_ks_blocks(&mut dec_ctr, chunk); + } + buffer = iter.into_remainder(); + } + + let mut iter = buffer.chunks_exact_mut(bs); for block in (&mut iter).map(Block::::from_mut_slice) { self.apply_ks_block(&mut dec_ctr, block); }