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

Implement trait AeadInto for AES-GCM and ChaCha20-Poly1305 #625

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion aes-gcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@
//! [`aead::Buffer`] for `arrayvec::ArrayVec` (re-exported from the [`aead`] crate as
//! [`aead::arrayvec::ArrayVec`]).

pub use aead::{self, AeadCore, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
pub use aead::{self, AeadCore, AeadInto, AeadInPlace, Error, Key, KeyInit, KeySizeUser};
use ::cipher::inout::InOutBuf;

#[cfg(feature = "aes")]
pub use aes;
Expand Down Expand Up @@ -264,6 +265,63 @@ where
type CiphertextOverhead = U0;
}

impl<Aes, NonceSize, TagSize> AeadInto for AesGcm<Aes, NonceSize, TagSize>
where
Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
NonceSize: ArraySize,
TagSize: self::TagSize,
{
fn encrypt_into_detached(
&self,
nonce: &Nonce<NonceSize>,
plaintext: &[u8],
associated_data: &[u8],
ciphertext: &mut [u8],
) -> Result<Tag<TagSize>, Error> {
if plaintext.len() as u64 > P_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}

let buffer = InOutBuf::new(plaintext, ciphertext).map_err(|_| Error)?;
let (ctr, mask) = self.init_ctr(nonce);

// TODO(tarcieri): interleave encryption with GHASH
// See: <https://github.com/RustCrypto/AEADs/issues/74>
ctr.apply_keystream_partial(buffer);

let full_tag = self.compute_tag(mask, associated_data, ciphertext);
Ok(Tag::try_from(&full_tag[..TagSize::to_usize()]).expect("tag size mismatch"))
}

fn decrypt_into_detached(
&self,
nonce: &Nonce<NonceSize>,
ciphertext: &[u8],
associated_data: &[u8],
plaintext: &mut [u8],
tag: &Tag<TagSize>,
) -> Result<(), Error> {
if ciphertext.len() as u64 > C_MAX || associated_data.len() as u64 > A_MAX {
return Err(Error);
}

let buffer = InOutBuf::new(ciphertext, plaintext).map_err(|_| Error)?;
let (ctr, mask) = self.init_ctr(nonce);

// TODO(tarcieri): interleave encryption with GHASH
// See: <https://github.com/RustCrypto/AEADs/issues/74>
let expected_tag = self.compute_tag(mask, associated_data, ciphertext);

use subtle::ConstantTimeEq;
if expected_tag[..TagSize::to_usize()].ct_eq(tag).into() {
ctr.apply_keystream_partial(buffer);
Ok(())
} else {
Err(Error)
}
}
}

impl<Aes, NonceSize, TagSize> AeadInPlace for AesGcm<Aes, NonceSize, TagSize>
where
Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockCipherEncrypt,
Expand Down
52 changes: 52 additions & 0 deletions aes-gcm/tests/aead_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use aes_gcm::aead::{ Aead, AeadInto, Payload };
use aes_gcm::{ Aes256Gcm, KeyInit };


/// Confirm that the [AeadInto] implementation produces exactly the same output as the [Aead]
/// implementation.
#[test]
fn test_aeadinto() {
let key = [0x60, 0xaf, 0x5d, 0xe1, 0x8b, 0x63, 0xf8, 0xe3, 0xe9, 0xbc, 0xff, 0x93, 0xa1, 0xab, 0x69, 0x1c, 0x8c, 0x2a, 0x87, 0xb5, 0x35, 0xac, 0x2a, 0xa1, 0x4e, 0xba, 0xd1, 0xf1, 0x7d, 0x02, 0xff, 0x92];
let aad = [0xbc, 0xf7, 0x7c, 0x42, 0x96, 0xf4, 0x96, 0x63, 0x16, 0x70, 0x02, 0x4e, 0xb2, 0x70, 0xd5, 0x0d, 0x6d, 0xef, 0xa2, 0x82, 0x59, 0xf7, 0x74, 0x60, 0xfc, 0x15, 0x05, 0xa1, 0x4c, 0x97, 0x4d, 0x4c];
let data = [0xf6, 0xef, 0x21, 0x88, 0x8c, 0xfa, 0x75, 0x82, 0xda, 0x73, 0x7c, 0xad, 0x6a, 0x08, 0x64, 0x9b, 0xa3, 0x11, 0xfa, 0x27, 0x90, 0xdb, 0x74, 0x6f, 0xf0, 0x70, 0x57, 0xca, 0x15, 0xf8, 0xc8, 0x0a];

let mut trait_aeadinto_encrypted_output = [0u8; 32 + 16];
Aes256Gcm::new((&key).try_into().unwrap())
.encrypt_into(
(&[0u8; 12]).try_into().unwrap(),
&data,
&aad,
&mut trait_aeadinto_encrypted_output
).unwrap();

let trait_aead_encrypted_output = Aes256Gcm::new((&key).try_into().unwrap())
.encrypt(
(&[0u8; 12]).try_into().unwrap(),
Payload {
msg: &data,
aad: &aad
}
).unwrap();

assert_eq!(trait_aead_encrypted_output, trait_aeadinto_encrypted_output);

let mut trait_aeadinto_decrypted_output = [0u8; 32];
Aes256Gcm::new((&key).try_into().unwrap())
.decrypt_into(
(&[0u8; 12]).try_into().unwrap(),
&trait_aeadinto_encrypted_output,
&aad,
&mut trait_aeadinto_decrypted_output
).unwrap();

let trait_aead_decrypted_output = Aes256Gcm::new((&key).try_into().unwrap())
.decrypt(
(&[0u8; 12]).try_into().unwrap(),
Payload {
msg: &trait_aeadinto_encrypted_output,
aad: &aad
}
).unwrap();

assert_eq!(trait_aead_decrypted_output, trait_aeadinto_decrypted_output);
}
46 changes: 46 additions & 0 deletions chacha20poly1305/src/cipher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Core AEAD cipher implementation for (X)ChaCha20Poly1305.

use ::cipher::{StreamCipher, StreamCipherSeek};
use ::cipher::inout::InOutBuf;
use aead::array::Array;
use aead::Error;
use poly1305::{
Expand Down Expand Up @@ -46,6 +47,26 @@ where
Self { cipher, mac }
}

pub(crate) fn encrypt_inout_detached(
mut self,
associated_data: &[u8],
mut buffer: InOutBuf<'_, '_, u8>,
) -> Result<Tag, Error> {
if buffer.len() / BLOCK_SIZE >= MAX_BLOCKS {
return Err(Error);
}

self.mac.update_padded(associated_data);

// TODO(tarcieri): interleave encryption with Poly1305
// See: <https://github.com/RustCrypto/AEADs/issues/74>
self.cipher.apply_keystream_inout(buffer.reborrow());
self.mac.update_padded(buffer.get_out());

self.authenticate_lengths(associated_data, buffer.get_out())?;
Ok(self.mac.finalize())
}

/// Encrypt the given message in-place, returning the authentication tag
pub(crate) fn encrypt_in_place_detached(
mut self,
Expand All @@ -67,6 +88,31 @@ where
Ok(self.mac.finalize())
}

pub(crate) fn decrypt_inout_detached(
mut self,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
tag: &Tag,
) -> Result<(), Error> {
if buffer.len() / BLOCK_SIZE >= MAX_BLOCKS {
return Err(Error);
}

self.mac.update_padded(associated_data);
self.mac.update_padded(buffer.get_in());
self.authenticate_lengths(associated_data, buffer.get_in())?;

// This performs a constant-time comparison using the `subtle` crate
if self.mac.verify(tag).is_ok() {
// TODO(tarcieri): interleave decryption with Poly1305
// See: <https://github.com/RustCrypto/AEADs/issues/74>
self.cipher.apply_keystream_inout(buffer);
Ok(())
} else {
Err(Error)
}
}

/// Decrypt the given message, first authenticating ciphertext integrity
/// and returning an error if it's been tampered with.
pub(crate) fn decrypt_in_place_detached(
Expand Down
34 changes: 33 additions & 1 deletion chacha20poly1305/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,11 @@

mod cipher;

pub use aead::{self, consts, AeadCore, AeadInPlace, Error, KeyInit, KeySizeUser};
pub use aead::{self, consts, AeadCore, AeadInto, AeadInPlace, Error, KeyInit, KeySizeUser};

use self::cipher::Cipher;
use ::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
use ::cipher::inout::InOutBuf;
use aead::{
array::{Array, ArraySize},
consts::{U0, U12, U16, U24, U32},
Expand Down Expand Up @@ -252,6 +253,37 @@ where
type CiphertextOverhead = U0;
}

impl<C, N> AeadInto for ChaChaPoly1305<C, N>
where
C: KeyIvInit<KeySize = U32, IvSize = N> + StreamCipher + StreamCipherSeek,
N: ArraySize,
{
fn encrypt_into_detached(
&self,
nonce: &aead::Nonce<Self>,
plaintext: &[u8],
associated_data: &[u8],
ciphertext: &mut [u8],
) -> Result<Tag, Error> {
Cipher::new(C::new(&self.key, nonce)).encrypt_inout_detached(associated_data, InOutBuf::new(plaintext, ciphertext).map_err(|_| Error)?)
}

fn decrypt_into_detached(
&self,
nonce: &aead::Nonce<Self>,
ciphertext: &[u8],
associated_data: &[u8],
plaintext: &mut [u8],
tag: &Tag,
) -> Result<(), Error> {
Cipher::new(C::new(&self.key, nonce)).decrypt_inout_detached(
associated_data,
InOutBuf::new(ciphertext, plaintext).map_err(|_| Error)?,
tag,
)
}
}

impl<C, N> AeadInPlace for ChaChaPoly1305<C, N>
where
C: KeyIvInit<KeySize = U32, IvSize = N> + StreamCipher + StreamCipherSeek,
Expand Down
52 changes: 52 additions & 0 deletions chacha20poly1305/tests/aead_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use chacha20poly1305::aead::{ Aead, AeadInto, Payload };
use chacha20poly1305::{ ChaCha20Poly1305, KeyInit };


/// Confirm that the [AeadInto] implementation produces exactly the same output as the [Aead]
/// implementation.
#[test]
fn test_aeadinto() {
let key = [0xfe, 0x14, 0xbc, 0x65, 0x16, 0xd6, 0x4a, 0x80, 0x8d, 0x58, 0xa3, 0x09, 0x8b, 0x0d, 0xa1, 0xc3, 0x2d, 0xf4, 0x62, 0xc0, 0x4d, 0x9e, 0x85, 0x55, 0x70, 0xc6, 0x83, 0xc2, 0x0f, 0xd7, 0xf4, 0x88];
let aad = [0xfe, 0xa6, 0xa3, 0xc3, 0xae, 0x1a, 0xab, 0x0f, 0x80, 0xe7, 0xa9, 0xcb, 0x9a, 0x9e, 0x5f, 0x06, 0x16, 0x4d, 0x85, 0x46, 0x43, 0xbf, 0x74, 0xd4, 0x38, 0x19, 0xf6, 0xd3, 0x38, 0xa0, 0x5e, 0xaf];
let data = [0xae, 0xfb, 0x8c, 0x8c, 0x38, 0xc9, 0x12, 0x04, 0x0f, 0x1d, 0xc0, 0x10, 0xf1, 0x94, 0xb0, 0x31, 0xcc, 0x00, 0xd8, 0xe4, 0xae, 0x5d, 0x04, 0x70, 0xb3, 0x6b, 0xfa, 0xb8, 0xe1, 0x23, 0x0c, 0x8c];

let mut trait_aeadinto_encrypted_output = [0u8; 32 + 16];
ChaCha20Poly1305::new((&key).try_into().unwrap())
.encrypt_into(
(&[0u8; 12]).try_into().unwrap(),
&data,
&aad,
&mut trait_aeadinto_encrypted_output
).unwrap();

let trait_aead_encrypted_output = ChaCha20Poly1305::new((&key).try_into().unwrap())
.encrypt(
(&[0u8; 12]).try_into().unwrap(),
Payload {
msg: &data,
aad: &aad
}
).unwrap();

assert_eq!(trait_aead_encrypted_output, trait_aeadinto_encrypted_output);

let mut trait_aeadinto_decrypted_output = [0u8; 32];
ChaCha20Poly1305::new((&key).try_into().unwrap())
.decrypt_into(
(&[0u8; 12]).try_into().unwrap(),
&trait_aeadinto_encrypted_output,
&aad,
&mut trait_aeadinto_decrypted_output
).unwrap();

let trait_aead_decrypted_output = ChaCha20Poly1305::new((&key).try_into().unwrap())
.decrypt(
(&[0u8; 12]).try_into().unwrap(),
Payload {
msg: &trait_aeadinto_encrypted_output,
aad: &aad
}
).unwrap();

assert_eq!(trait_aead_decrypted_output, trait_aeadinto_decrypted_output);
}
2 changes: 2 additions & 0 deletions chacha20poly1305/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#![cfg(feature = "alloc")]

pub mod aead_into;

use chacha20poly1305::ChaCha20Poly1305;
use chacha20poly1305::XChaCha20Poly1305;

Expand Down