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

aead: rework traits #1713

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions aead/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rust-version = "1.81"

[dependencies]
crypto-common = "0.2.0-rc.0"
inout = "0.2.0-rc.1"

# optional dependencies
arrayvec = { version = "0.7", optional = true, default-features = false }
Expand Down
38 changes: 36 additions & 2 deletions aead/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,43 @@ able to execute [chosen-ciphertext attacks]. The resulting security property,
[ciphertext indistinguishability], is considered a basic requirement for
modern cryptographic implementations.

See [RustCrypto/AEADs] for cipher implementations which use this trait.
See [RustCrypto/AEADs] for cipher implementations which implement traits from
this crate.

[Documentation][docs-link]
## Nonces: ⚠️ Security Warning ⚠️

AEAD algorithms accept a parameter to encryption/decryption called
a "nonce" which must be unique every time encryption is performed and
never repeated for the same key. The nonce is often prepended to the
ciphertext. The nonce used to produce a given ciphertext must be passed
to the decryption function in order for it to decrypt correctly.

AEAD algorithms often fail catastrophically if nonces are ever repeated
for the same key (with SIV modes being a "misuse-resistent" exception).

Nonces don't necessarily have to be random, but it is one strategy
which is often used in practice.

Using random nonces runs the risk of repeating them unless the nonce
size is particularly large, e.g. 192-bit extended nonces used by the
`XChaCha20Poly1305` and `XSalsa20Poly1305` constructions.

[NIST SP 800-38D] recommends the following for 128-bit nonces:

> The total number of invocations of the authenticated encryption
> function shall not exceed 2^32, including all IV lengths and all
> instances of the authenticated encryption function with the given key.

Following this guideline, only 4,294,967,296 messages with random
nonces can be encrypted under a given key. While this bound is high,
it's possible to encounter in practice, and systems which might
reach it should consider alternatives to purely random nonces, like
a counter or a combination of a random nonce + counter.

See the [`aead-stream`] crate for a ready-made implementation of the latter.

[NIST SP 800-38D]: https://csrc.nist.gov/publications/detail/sp/800-38d/final
[`aead-stream`]: https://docs.rs/aead-stream

## Minimum Supported Rust Version

Expand Down
201 changes: 201 additions & 0 deletions aead/src/dyn_aead.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
use inout::{InOutBuf, InOutBufReserved};

use crate::{Aead, Buffer, Error, Result};

#[cfg(feature = "alloc")]
use alloc::vec::Vec;

mod sealed {
pub trait Sealed {}
}

/// Object-safe variant of the [`Aead`] trait.
///
/// This trait is implemented automaticlly for all types which implement the [`Aead`] trait.
pub trait DynAead: sealed::Sealed {
newpavlov marked this conversation as resolved.
Show resolved Hide resolved
fn postfix_encrypt_inout<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: InOutBufReserved<'_, 'out, u8>,
) -> Result<&'out mut [u8]>;

fn postfix_decrypt_inout<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: InOutBuf<'_, 'out, u8>,
) -> Result<&'out mut [u8]>;

fn postfix_encrypt_inplace<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: &'out mut [u8],
plaintext_len: usize,
) -> Result<&'out mut [u8]>;

fn postfix_decrypt_inplace<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]>;

fn postfix_encrypt_to_buf<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
plaintext: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]>;

fn postfix_decrypt_to_buf<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
ciphertext: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]>;

fn encrypt_to_buffer2(
&self,
nonce: &[u8],
associated_data: &[u8],
plaintext: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()>;

fn decrypt_to_buffer2(
newpavlov marked this conversation as resolved.
Show resolved Hide resolved
&self,
nonce: &[u8],
associated_data: &[u8],
ciphertext: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()>;

#[cfg(feature = "alloc")]
fn encrypt_to_vec(
&self,
nonce: &[u8],
associated_data: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>>;

#[cfg(feature = "alloc")]
fn decrypt_to_vec(
&self,
nonce: &[u8],
associated_data: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>>;
}

impl<T: Aead> sealed::Sealed for T {}

impl<T: Aead> DynAead for T {
fn postfix_encrypt_inout<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: InOutBufReserved<'_, 'out, u8>,
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_encrypt_inout(self, nonce, associated_data, buffer)
}

fn postfix_decrypt_inout<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: InOutBuf<'_, 'out, u8>,
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_decrypt_inout(self, nonce, associated_data, buffer)
}

fn postfix_encrypt_inplace<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: &'out mut [u8],
plaintext_len: usize,
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_encrypt_inplace(self, nonce, associated_data, buffer, plaintext_len)
}

fn postfix_decrypt_inplace<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_decrypt_inplace(self, nonce, associated_data, buffer)
}

fn postfix_encrypt_to_buf<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
plaintext: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_encrypt_to_buf(self, nonce, associated_data, plaintext, buffer)
}

fn postfix_decrypt_to_buf<'out>(
&self,
nonce: &[u8],
associated_data: &[u8],
ciphertext: &[u8],
buffer: &'out mut [u8],
) -> Result<&'out mut [u8]> {
let nonce = nonce.try_into().map_err(|_| Error)?;
Aead::postfix_decrypt_to_buf(self, nonce, associated_data, ciphertext, buffer)
}

fn encrypt_to_buffer2(
&self,
nonce: &[u8],
aad: &[u8],
msg: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = nonce.try_into().map_err(|_| Error)?;
let payload = crate::Payload { aad, msg };
Aead::encrypt_to_buffer(self, nonce, payload, buffer)
}

fn decrypt_to_buffer2(
&self,
nonce: &[u8],
aad: &[u8],
msg: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let nonce = nonce.try_into().map_err(|_| Error)?;
let payload = crate::Payload { aad, msg };
Aead::decrypt_to_buffer(self, nonce, payload, buffer)
}

#[cfg(feature = "alloc")]
fn encrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result<Vec<u8>> {
let nonce = nonce.try_into().map_err(|_| Error)?;
let payload = crate::Payload { aad, msg };
Aead::encrypt_to_vec(self, nonce, payload)
}

#[cfg(feature = "alloc")]
fn decrypt_to_vec(&self, nonce: &[u8], aad: &[u8], msg: &[u8]) -> Result<Vec<u8>> {
let nonce = nonce.try_into().map_err(|_| Error)?;
let payload = crate::Payload { aad, msg };
Aead::decrypt_to_vec(self, nonce, payload)
}
}

// Ensure that `DynAead` is an object-safe trait
#[allow(dead_code)]
fn foo(_: &dyn DynAead) {}
Loading