Skip to content

Commit

Permalink
mgm: use parallel block encryption if possible (#358)
Browse files Browse the repository at this point in the history
  • Loading branch information
newpavlov committed Aug 26, 2021
1 parent e470a51 commit 59ae86b
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions mgm/CHANGELOG.md
Expand Up @@ -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])
Expand Down
2 changes: 1 addition & 1 deletion 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"
Expand Down
128 changes: 111 additions & 17 deletions mgm/src/lib.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -78,13 +78,13 @@ where
C: BlockEncrypt,
C::BlockSize: MgmBlockSize,
{
fn apply_ks_block(&self, counter: &mut Counter<C>, buf: &mut [u8]) {
let mut block = C::BlockSize::ctr2block(counter);
fn apply_ks_block(&self, ctr: &mut Counter<C>, 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<C>, tag_ctr: &mut Counter<C>, block: &Block<C>) {
Expand All @@ -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<C>, 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::<C>::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<C>, tag_ctr: &mut Counter<C>, 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::<C>::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<C> From<C> for Mgm<C>
Expand Down Expand Up @@ -135,23 +172,37 @@ where
fn encrypt_in_place_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
adata: &[u8],
buffer: &mut [u8],
mut adata: &[u8],
mut buffer: &mut [u8],
) -> Result<Tag<Self::TagSize>, 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);
let mut tag_ctr = C::BlockSize::block2ctr(&tag_ctr);

let mut tag = <C::BlockSize as Sealed>::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::<C>::from_slice) {
self.update_tag(&mut tag, &mut tag_ctr, block);
}
Expand All @@ -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::<C>::from_mut_slice) {
self.apply_ks_block(&mut enc_ctr, block);
self.update_tag(&mut tag, &mut tag_ctr, block);
Expand All @@ -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();
Expand All @@ -195,24 +255,39 @@ where
fn decrypt_in_place_detached(
&self,
nonce: &Nonce<Self::NonceSize>,
adata: &[u8],
buffer: &mut [u8],
mut adata: &[u8],
mut buffer: &mut [u8],
expected_tag: &Tag<Self::TagSize>,
) -> 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);

let mut tag_ctr = C::BlockSize::block2ctr(&tag_ctr);
let mut tag = <C::BlockSize as Sealed>::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::<C>::from_slice) {
self.update_tag(&mut tag, &mut tag_ctr, block);
}
Expand All @@ -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::<C>::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::<C>::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();

Expand All @@ -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();
Expand All @@ -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::<C>::from_mut_slice) {
self.apply_ks_block(&mut dec_ctr, block);
}
Expand Down

0 comments on commit 59ae86b

Please sign in to comment.