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 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
7 changes: 4 additions & 3 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.3"

# 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
97 changes: 97 additions & 0 deletions aead/src/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use crate::Result;

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

#[cfg(feature = "bytes")]
use bytes::BytesMut;

#[cfg(feature = "arrayvec")]
use arrayvec::ArrayVec;

/// 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: AsMut<[u8]> {
/// Resizes buffer to the requested length.
///
/// If buffer is smaller than `len`, fills it with zeros. Otherwise, truncates it to `len`.
fn resize(&mut self, len: usize) -> Result<()>;

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

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

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

fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
Vec::extend_from_slice(self, other);
Ok(())
}

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

#[cfg(feature = "bytes")]
impl Buffer for BytesMut {
fn resize(&mut self, len: usize) -> Result<()> {
BytesMut::resize(self, len, 0);
Ok(())
}

fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
BytesMut::extend_from_slice(self, other);
Ok(())
}

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

#[cfg(feature = "arrayvec")]
impl<const N: usize> Buffer for ArrayVec<u8, N> {
fn resize(&mut self, len: usize) -> Result<()> {
if let Some(ext_len) = len.checked_sub(self.len()) {
let buf = &[0u8; N][..ext_len];
self.try_extend_from_slice(buf).map_err(|_| crate::Error)
} else {
self.truncate(len);
Ok(())
}
}

fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
ArrayVec::try_extend_from_slice(self, other).map_err(|_| crate::Error)
}

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

#[cfg(feature = "heapless")]
impl<const N: usize> Buffer for heapless::Vec<u8, N> {
fn resize(&mut self, len: usize) -> Result<()> {
heapless::Vec::resize(self, len, 0).map_err(|_| crate::Error)
}

fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
heapless::Vec::extend_from_slice(self, other).map_err(|_| crate::Error)
}

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