Skip to content

aead: Add in-place API based on Buffer trait #59

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

Merged
merged 1 commit into from
Nov 17, 2019
Merged
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
6 changes: 5 additions & 1 deletion aead/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ license = "MIT OR Apache-2.0"
description = "Traits for Authenticated Encryption with Associated Data (AEAD) algorithms"
documentation = "https://docs.rs/aead"
repository = "https://github.com/RustCrypto/traits"
keywords = ["digest", "crypto", "encryption"]
keywords = ["crypto", "encryption"]
categories = ["cryptography", "no-std"]

[dependencies]
generic-array = { version = "0.12", default-features = false }
heapless = { version = "0.5", optional = true }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively we could define our own heapless::Vec-like type which wraps a GenericArray<u8, N> and does the minimum necessary to impl the Buffer trait, which would eliminate this dependency. But heapless seems relatively popular in the embedded space.


[features]
default = ["alloc"]
alloc = []

[package.metadata.docs.rs]
all-features = true
209 changes: 173 additions & 36 deletions aead/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,35 @@
extern crate alloc;

pub use generic_array;
#[cfg(feature = "heapless")]
pub use heapless;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};

// Define the default implementation for both `Aead::encrypt()` and
// `AeadMut::encrypt()`. Uses a macro to gloss over `&self` vs `&mut self`.
#[cfg(feature = "alloc")]
macro_rules! encrypt_to_postfix_tagged_vec {
($aead:expr, $nonce:expr, $payload:expr) => {{
let payload = $payload.into();
let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
buffer.extend_from_slice(payload.msg);

let tag = $aead.encrypt_in_place_detached($nonce, payload.aad, &mut buffer)?;
buffer.extend_from_slice(tag.as_slice());
Ok(buffer)
}};
}

// Define the default implementation for both `Aead::decrypt()` and
// `AeadMut::decrypt()`. Uses a macro to gloss over `&self` vs `&mut self`.
#[cfg(feature = "alloc")]
macro_rules! decrypt_postfix_tagged_ciphertext_to_vec {
($aead:expr, $nonce:expr, $payload:expr) => {{
let payload = $payload.into();
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Error;

if payload.msg.len() < Self::TagSize::to_usize() {
/// Implement the `decrypt_in_place` method on `Aead` and `AeadMut`.
/// Uses a macro to gloss over `&self` vs `&mut self`.
///
/// Assumes a postfix authentication tag. AEAD ciphers which do not use a
/// postfix authentication tag will need to define their own implementation.
macro_rules! impl_decrypt_in_place {
($aead:expr, $nonce:expr, $aad:expr, $buffer:expr) => {{
if $buffer.len() < Self::TagSize::to_usize() {
return Err(Error);
}

let tag_start = payload.msg.len() - Self::TagSize::to_usize();
let mut buffer = Vec::from(&payload.msg[..tag_start]);
let tag = GenericArray::from_slice(&payload.msg[tag_start..]);
$aead.decrypt_in_place_detached($nonce, payload.aad, &mut buffer, tag)?;

Ok(buffer)
let tag_pos = $buffer.len() - Self::TagSize::to_usize();
let (msg, tag) = $buffer.as_mut().split_at_mut(tag_pos);
$aead.decrypt_in_place_detached($nonce, $aad, msg, GenericArray::from_slice(tag))?;
$buffer.truncate(tag_pos);
Ok(())
}};
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Error;

/// Instantiate either a stateless [`Aead`] or stateful [`AeadMut`] algorithm.
pub trait NewAead {
/// The size of the key array required by this algorithm.
Expand Down Expand Up @@ -112,7 +98,31 @@ pub trait Aead {
nonce: &GenericArray<u8, Self::NonceSize>,
plaintext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
encrypt_to_postfix_tagged_vec!(self, nonce, plaintext)
let payload = plaintext.into();
let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
buffer.extend_from_slice(payload.msg);
self.encrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}

/// Encrypt the given buffer containing a plaintext message in-place.
///
/// The buffer must have sufficient capacity to store the ciphertext
/// message, which will always be larger than the original plaintext.
/// The exact size needed is cipher-dependent, but generally includes
/// the size of an authentication tag.
///
/// Returns an error if the buffer has insufficient capacity to store the
/// resulting ciphertext message.
fn encrypt_in_place(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
let tag = self.encrypt_in_place_detached(nonce, associated_data, buffer.as_mut())?;
buffer.extend_from_slice(tag.as_slice())?;
Ok(())
}

/// Encrypt the data in-place, returning the authentication tag
Expand Down Expand Up @@ -146,10 +156,27 @@ pub trait Aead {
nonce: &GenericArray<u8, Self::NonceSize>,
ciphertext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext)
let payload = ciphertext.into();
let mut buffer = Vec::from(payload.msg);
self.decrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}

/// Decrypt the data in-place, returning an error in the event the provided
/// Decrypt the message in-place, returning an error in the event the
/// provided authentication tag does not match the given ciphertext.
///
/// The buffer will be truncated to the length of the original plaintext
/// message upon success.
fn decrypt_in_place(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
impl_decrypt_in_place!(self, nonce, associated_data, buffer)
}

/// Decrypt the message in-place, returning an error in the event the provided
/// authentication tag does not match the given ciphertext (i.e. ciphertext
/// is modified/unauthentic)
fn decrypt_in_place_detached(
Expand Down Expand Up @@ -182,7 +209,31 @@ pub trait AeadMut {
nonce: &GenericArray<u8, Self::NonceSize>,
plaintext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
encrypt_to_postfix_tagged_vec!(self, nonce, plaintext)
let payload = plaintext.into();
let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
buffer.extend_from_slice(payload.msg);
self.encrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}

/// Encrypt the given buffer containing a plaintext message in-place.
///
/// The buffer must have sufficient capacity to store the ciphertext
/// message, which will always be larger than the original plaintext.
/// The exact size needed is cipher-dependent, but generally includes
/// the size of an authentication tag.
///
/// Returns an error if the buffer has insufficient capacity to store the
/// resulting ciphertext message.
fn encrypt_in_place(
&mut self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
let tag = self.encrypt_in_place_detached(nonce, associated_data, buffer.as_mut())?;
buffer.extend_from_slice(tag.as_slice())?;
Ok(())
}

/// Encrypt the data in-place, returning the authentication tag
Expand All @@ -204,7 +255,24 @@ pub trait AeadMut {
nonce: &GenericArray<u8, Self::NonceSize>,
ciphertext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext)
let payload = ciphertext.into();
let mut buffer = Vec::from(payload.msg);
self.decrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}

/// Decrypt the message in-place, returning an error in the event the
/// provided authentication tag does not match the given ciphertext.
///
/// The buffer will be truncated to the length of the original plaintext
/// message upon success.
fn decrypt_in_place(
&mut self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
impl_decrypt_in_place!(self, nonce, associated_data, buffer)
}

/// Decrypt the data in-place, returning an error in the event the provided
Expand Down Expand Up @@ -237,6 +305,16 @@ impl<Algo: Aead> AeadMut for Algo {
<Self as Aead>::encrypt(self, nonce, plaintext)
}

/// Encrypt the given buffer containing a plaintext message in-place.
fn encrypt_in_place(
&mut self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
<Self as Aead>::encrypt_in_place(self, nonce, associated_data, buffer)
}

/// Encrypt the data in-place, returning the authentication tag
fn encrypt_in_place_detached(
&mut self,
Expand All @@ -258,6 +336,17 @@ impl<Algo: Aead> AeadMut for Algo {
<Self as Aead>::decrypt(self, nonce, ciphertext)
}

/// Decrypt the message in-place, returning an error in the event the
/// provided authentication tag does not match the given ciphertext.
fn decrypt_in_place(
&mut self,
nonce: &GenericArray<u8, Self::NonceSize>,
associated_data: &[u8],
buffer: &mut impl Buffer,
) -> Result<(), Error> {
<Self as Aead>::decrypt_in_place(self, nonce, associated_data, buffer)
}

/// Decrypt the data in-place, returning an error in the event the provided
/// authentication tag does not match the given ciphertext (i.e. ciphertext
/// is modified/unauthentic)
Expand Down Expand Up @@ -294,3 +383,51 @@ impl<'msg, 'aad> From<&'msg [u8]> for Payload<'msg, 'aad> {
Self { msg, aad: b"" }
}
}

/// In-place encryption/decryption byte buffers.
///
/// This trait defines the set of methods needed to support in-place operations
/// on a `Vec`-like data type.
pub trait Buffer: AsRef<[u8]> + AsMut<[u8]> {
/// Get the length of the buffer
fn len(&self) -> usize {
self.as_ref().len()
}

/// Is the buffer empty?
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}

/// Extend this buffer from the given slice
fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), Error>;

/// Truncate this buffer to the given size
fn truncate(&mut self, len: usize);
}

#[cfg(feature = "alloc")]
impl Buffer for Vec<u8> {
fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), Error> {
Vec::extend_from_slice(self, other);
Ok(())
}

fn truncate(&mut self, len: usize) {
Vec::truncate(self, len);
}
}

#[cfg(feature = "heapless")]
impl<N> Buffer for heapless::Vec<u8, N>
where
N: heapless::ArrayLength<u8>,
{
fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), Error> {
heapless::Vec::extend_from_slice(self, other).map_err(|_| Error)
}

fn truncate(&mut self, len: usize) {
heapless::Vec::truncate(self, len);
}
}