Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mgm: decrypt only after tag verification #356

Merged
merged 4 commits into from Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.4 (2021-08-24)
### Changed
- Decrypt ciphertext only after tag verification ([#356])

[#356]: https://github.com/RustCrypto/AEADs/pull/356

## 0.4.3 (2021-07-20)
### Changed
- Pin `subtle` dependency to v2.4 ([#349])
Expand Down
2 changes: 1 addition & 1 deletion mgm/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mgm"
version = "0.4.3"
version = "0.4.4"
description = "Generic implementation of the Multilinear Galois Mode (MGM) cipher"
authors = ["RustCrypto Developers"]
edition = "2018"
Expand Down
106 changes: 57 additions & 49 deletions mgm/src/lib.rs
Expand Up @@ -39,6 +39,7 @@ use aead::{
AeadCore, AeadInPlace, Error, Key, NewAead,
};
use cipher::{BlockCipher, BlockEncrypt, NewBlockCipher};
use subtle::ConstantTimeEq;

pub use aead;

Expand Down Expand Up @@ -138,55 +139,55 @@ where
}
let final_block = C::BlockSize::lengths2block(adata.len(), buffer.len())?;

let mut enc_counter = nonce.clone();
let mut tag_counter = nonce.clone();
enc_counter[0] &= 0b0111_1111;
tag_counter[0] |= 0b1000_0000;
let mut enc_ctr = nonce.clone();
let mut tag_ctr = nonce.clone();
enc_ctr[0] &= 0b0111_1111;
tag_ctr[0] |= 0b1000_0000;

self.cipher.encrypt_block(&mut enc_counter);
self.cipher.encrypt_block(&mut tag_counter);
self.cipher.encrypt_block(&mut enc_ctr);
self.cipher.encrypt_block(&mut tag_ctr);

let mut enc_counter = C::BlockSize::block2ctr(&enc_counter);
let mut tag_counter = C::BlockSize::block2ctr(&tag_counter);
let mut enc_ctr = C::BlockSize::block2ctr(&enc_ctr);
let mut tag_ctr = C::BlockSize::block2ctr(&tag_ctr);

let mut tag = <C::BlockSize as Sealed>::Element::new();

// process adata
let mut iter = adata.chunks_exact(C::BlockSize::USIZE);
for chunk in (&mut iter).map(Block::<C>::from_slice) {
tag.mul_sum(&self.get_h(&tag_counter), chunk);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), chunk);
C::BlockSize::incr_l(&mut tag_ctr);
}
let rem = iter.remainder();
if !rem.is_empty() {
let mut chunk: Block<C> = Default::default();
chunk[..rem.len()].copy_from_slice(rem);
tag.mul_sum(&self.get_h(&tag_counter), &chunk);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), &chunk);
C::BlockSize::incr_l(&mut tag_ctr);
}

// process plaintext
let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE);
for chunk in (&mut iter).map(Block::<C>::from_mut_slice) {
xor(chunk, &self.encrypt_counter(&enc_counter));
tag.mul_sum(&self.get_h(&tag_counter), chunk);
C::BlockSize::incr_r(&mut enc_counter);
C::BlockSize::incr_l(&mut tag_counter);
xor(chunk, &self.encrypt_counter(&enc_ctr));
tag.mul_sum(&self.get_h(&tag_ctr), chunk);
C::BlockSize::incr_r(&mut enc_ctr);
C::BlockSize::incr_l(&mut tag_ctr);
}
let rem = iter.into_remainder();
if !rem.is_empty() {
let n = rem.len();
let e = self.encrypt_counter(&enc_counter);
let e = self.encrypt_counter(&enc_ctr);
xor(rem, &e[..n]);

let mut ct = Block::<C>::default();
ct[..n].copy_from_slice(rem);

tag.mul_sum(&self.get_h(&tag_counter), &ct);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), &ct);
C::BlockSize::incr_l(&mut tag_ctr);
}

tag.mul_sum(&self.get_h(&tag_counter), &final_block);
tag.mul_sum(&self.get_h(&tag_ctr), &final_block);

let mut tag = tag.into_bytes();
self.cipher.encrypt_block(&mut tag);
Expand All @@ -205,67 +206,74 @@ where
if nonce[0] >> 7 != 0 {
return Err(Error);
}
let final_block = C::BlockSize::lengths2block(adata.len(), buffer.len())?;

let mut enc_counter = nonce.clone();
let mut tag_counter = nonce.clone();
enc_counter[0] &= 0b0111_1111;
tag_counter[0] |= 0b1000_0000;

self.cipher.encrypt_block(&mut enc_counter);
self.cipher.encrypt_block(&mut tag_counter);
let mut enc_ctr = nonce.clone();
let mut tag_ctr = nonce.clone();
enc_ctr[0] &= 0b0111_1111;
tag_ctr[0] |= 0b1000_0000;

let mut dec_counter = C::BlockSize::block2ctr(&enc_counter);
let mut tag_counter = C::BlockSize::block2ctr(&tag_counter);
self.cipher.encrypt_block(&mut enc_ctr);
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();

// process adata
let mut iter = adata.chunks_exact(C::BlockSize::USIZE);
for chunk in (&mut iter).map(Block::<C>::from_slice) {
tag.mul_sum(&self.get_h(&tag_counter), chunk);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), chunk);
C::BlockSize::incr_l(&mut tag_ctr);
}
let rem = iter.remainder();
if !rem.is_empty() {
let mut chunk: Block<C> = Default::default();
chunk[..rem.len()].copy_from_slice(rem);
tag.mul_sum(&self.get_h(&tag_counter), &chunk);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), &chunk);
C::BlockSize::incr_l(&mut tag_ctr);
}

// process ciphertext
let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE);
for chunk in (&mut iter).map(Block::<C>::from_mut_slice) {
tag.mul_sum(&self.get_h(&tag_counter), chunk);
xor(chunk, &self.encrypt_counter(&dec_counter));
C::BlockSize::incr_r(&mut dec_counter);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), chunk);
C::BlockSize::incr_l(&mut tag_ctr);
}
let rem = iter.into_remainder();
if !rem.is_empty() {
let n = rem.len();
let e = self.encrypt_counter(&dec_counter);

let mut ct = Block::<C>::default();
ct[..n].copy_from_slice(rem);

tag.mul_sum(&self.get_h(&tag_counter), &ct);
xor(rem, &e[..n]);
C::BlockSize::incr_l(&mut tag_counter);
tag.mul_sum(&self.get_h(&tag_ctr), &ct);
C::BlockSize::incr_l(&mut tag_ctr);
}

tag.mul_sum(&self.get_h(&tag_counter), &final_block);
let final_block = C::BlockSize::lengths2block(adata.len(), buffer.len())?;
tag.mul_sum(&self.get_h(&tag_ctr), &final_block);

let mut tag = tag.into_bytes();
self.cipher.encrypt_block(&mut tag);

use subtle::ConstantTimeEq;
if expected_tag.ct_eq(&tag).unwrap_u8() == 1 {
Ok(())
} else {
Err(Error)
if expected_tag.ct_eq(&tag).unwrap_u8() == 0 {
return Err(Error);
}

// decrypt ciphertext
let mut dec_counter = C::BlockSize::block2ctr(&enc_ctr);
let mut iter = buffer.chunks_exact_mut(C::BlockSize::USIZE);
for chunk in (&mut iter).map(Block::<C>::from_mut_slice) {
xor(chunk, &self.encrypt_counter(&dec_counter));
C::BlockSize::incr_r(&mut dec_counter);
C::BlockSize::incr_l(&mut tag_ctr);
}
let rem = iter.into_remainder();
if !rem.is_empty() {
let n = rem.len();
let e = self.encrypt_counter(&dec_counter);
xor(rem, &e[..n]);
}

Ok(())
}
}

Expand Down