Skip to content

Commit a701ec0

Browse files
committed
aead: rework traits API
1 parent 0969352 commit a701ec0

12 files changed

Lines changed: 627 additions & 459 deletions

.github/workflows/aead.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ jobs:
4343
- run: cargo check --all-features
4444
- run: cargo build --target ${{ matrix.target }} --release --no-default-features
4545
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features alloc
46-
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features bytes
47-
# TODO: re-enable in v0.6.1
48-
# - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features heapless
4946
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features rand_core
5047

5148
minimal-versions:

Cargo.lock

Lines changed: 2 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aead/Cargo.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,19 @@ repository = "https://github.com/RustCrypto/traits"
1010
license = "MIT OR Apache-2.0"
1111
keywords = ["crypto", "encryption"]
1212
categories = ["cryptography", "no-std"]
13-
description = """
14-
Traits for Authenticated Encryption with Associated Data (AEAD) algorithms,
15-
such as AES-GCM as ChaCha20Poly1305, which provide a high-level API
16-
"""
13+
description = "Traits for Authenticated Encryption with Associated Data (AEAD) algorithms"
1714

1815
[dependencies]
1916
common = { version = "0.2", package = "crypto-common" }
2017
inout = "0.2.2"
2118

2219
# optional dependencies
23-
arrayvec = { version = "0.7", optional = true, default-features = false }
2420
blobby = { version = "0.4", optional = true }
25-
bytes = { version = "1.11.1", optional = true, default-features = false }
2621

2722
[features]
28-
default = ["rand_core"]
2923
alloc = []
3024
dev = ["blobby", "alloc"]
31-
getrandom = ["common/getrandom"]
25+
getrandom = ["common/getrandom", "rand_core"]
3226
rand_core = ["common/rand_core"]
3327

3428
[lints]

aead/src/aead.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use crate::{Result, VariableAead};
2+
use alloc::vec::Vec;
3+
use common::typenum::Unsigned;
4+
5+
/// High-level functionality of Authenticated Encryption with Associated Data (AEAD) algorithms.
6+
pub trait Aead {
7+
/// Encrypt the given plaintext payload, and return the resulting
8+
/// ciphertext as a vector of bytes.
9+
///
10+
/// # Errors
11+
/// AEAD algorithm implementations may return an error if the plaintext or AAD are too long.
12+
fn encrypt_into_vec(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>>;
13+
14+
/// Decrypt the given ciphertext slice, and return the resulting plaintext
15+
/// as a vector of bytes.
16+
///
17+
/// # Errors
18+
/// - if the `ciphertext` is inauthentic (i.e. tag verification failure)
19+
/// - if the `ciphertext` is too long
20+
/// - if the `aad` is too long
21+
fn decrypt_into_vec(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>>;
22+
23+
/// Encrypt plaintext in `buf` extending it as necessary.
24+
///
25+
/// On success, `buf` will contain the resulting ciphertext,
26+
/// while on error it will be left intact.
27+
///
28+
/// # Errors
29+
/// AEAD algorithm implementations may return an error if the plaintext or AAD are too long.
30+
#[inline]
31+
fn encrypt_within_vec(&self, nonce: &[u8], aad: &[u8], buf: &mut Vec<u8>) -> Result<()> {
32+
let res = self.encrypt_into_vec(nonce, aad, buf)?;
33+
*buf = res;
34+
Ok(())
35+
}
36+
37+
/// Decrypt ciphertext in `buf` truncating it as necessary.
38+
///
39+
/// On success, `buf` will contain the resulting plaintext,
40+
/// while on error it will be zeroized.
41+
///
42+
/// # Errors
43+
/// - if the `ciphertext` is inauthentic (i.e. tag verification failure)
44+
/// - if the `ciphertext` is too long
45+
/// - if the `aad` is too long
46+
#[inline]
47+
fn decrypt_within_vec(&self, nonce: &[u8], aad: &[u8], buf: &mut Vec<u8>) -> Result<()> {
48+
let res = self.decrypt_into_vec(nonce, aad, buf);
49+
match res {
50+
Ok(pt) => {
51+
*buf = pt;
52+
Ok(())
53+
}
54+
Err(err) => {
55+
buf.fill(0);
56+
Err(err)
57+
}
58+
}
59+
}
60+
}
61+
62+
impl<T: VariableAead> Aead for T {
63+
#[inline]
64+
fn encrypt_into_vec(&self, nonce: &[u8], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
65+
self.variable_encrypt_into(nonce, aad, plaintext, T::TagSize::USIZE, alloc_vec)
66+
}
67+
68+
#[inline]
69+
fn decrypt_into_vec(&self, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
70+
self.variable_decrypt_into(nonce, aad, ciphertext, T::TagSize::USIZE, alloc_vec)
71+
}
72+
73+
#[inline]
74+
fn encrypt_within_vec(&self, nonce: &[u8], aad: &[u8], buf: &mut Vec<u8>) -> Result<()> {
75+
self.variable_encrypt_within(nonce, aad, buf, T::TagSize::USIZE, extend_vec)
76+
}
77+
78+
#[inline]
79+
fn decrypt_within_vec(&self, nonce: &[u8], aad: &[u8], buf: &mut Vec<u8>) -> Result<()> {
80+
self.variable_decrypt_within(nonce, aad, buf, T::TagSize::USIZE, Vec::truncate)
81+
}
82+
}
83+
84+
fn alloc_vec(len: usize) -> Vec<u8> {
85+
alloc::vec![0u8; len]
86+
}
87+
88+
fn extend_vec(buf: &mut Vec<u8>, len: usize) {
89+
buf.resize(len, 0);
90+
}

aead/src/dev.rs

Lines changed: 23 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
//! Development-related functionality
22
3-
#![allow(clippy::missing_errors_doc, reason = "dev module")]
4-
#![allow(clippy::missing_panics_doc, reason = "dev module")]
5-
#![allow(clippy::unwrap_in_result, reason = "dev module")]
6-
7-
use crate::{
8-
Aead, AeadInOut, Payload, Tag, TagPosition, array::typenum::Unsigned, inout::InOutBuf,
9-
};
103
pub use blobby;
4+
5+
use crate::Aead;
116
use common::KeyInit;
127

138
/// AEAD test vector
@@ -26,8 +21,11 @@ pub struct TestVector {
2621
}
2722

2823
/// Run AEAD test for the provided passing test vector
29-
#[allow(clippy::cast_possible_truncation)]
30-
pub fn pass_test<C: AeadInOut + KeyInit>(
24+
///
25+
/// # Errors
26+
/// - If the cipher has failed initialization with the provided key.
27+
/// - If the AEAD mode has failed to pass the test vector.
28+
pub fn pass_test<C: Aead + KeyInit>(
3129
&TestVector {
3230
key,
3331
nonce,
@@ -36,76 +34,31 @@ pub fn pass_test<C: AeadInOut + KeyInit>(
3634
ciphertext,
3735
}: &TestVector,
3836
) -> Result<(), &'static str> {
39-
let nonce = nonce.try_into().expect("wrong nonce size");
40-
let cipher = <C as KeyInit>::new_from_slice(key).expect("failed to initialize the cipher");
37+
let cipher: C = KeyInit::new_from_slice(key).map_err(|_| "failed to initialize the cipher")?;
4138

4239
let res = cipher
43-
.encrypt(
44-
nonce,
45-
Payload {
46-
aad,
47-
msg: plaintext,
48-
},
49-
)
40+
.encrypt_into_vec(nonce, aad, plaintext)
5041
.map_err(|_| "encryption failure")?;
5142
if res != ciphertext {
5243
return Err("encrypted data is different from target ciphertext");
5344
}
5445

5546
let res = cipher
56-
.decrypt(
57-
nonce,
58-
Payload {
59-
aad,
60-
msg: ciphertext,
61-
},
62-
)
47+
.decrypt_into_vec(nonce, aad, ciphertext)
6348
.map_err(|_| "decryption failure")?;
6449
if res != plaintext {
6550
return Err("decrypted data is different from target plaintext");
6651
}
6752

68-
let (ct, tag) = match C::TAG_POSITION {
69-
TagPosition::Prefix => {
70-
let (tag, ct) = ciphertext.split_at(C::TagSize::USIZE);
71-
(ct, tag)
72-
}
73-
TagPosition::Postfix => ciphertext.split_at(plaintext.len()),
74-
};
75-
let tag: &Tag<C> = tag.try_into().expect("tag has correct length");
76-
77-
// Fill output buffer with "garbage" to test that its data does not get read during encryption
78-
let mut buf: alloc::vec::Vec<u8> = (0..plaintext.len()).map(|i| i as u8).collect();
79-
let inout_buf = InOutBuf::new(plaintext, &mut buf).expect("pt and buf have the same length");
80-
81-
let calc_tag = cipher
82-
.encrypt_inout_detached(nonce, aad, inout_buf)
83-
.map_err(|_| "encrypt_inout_detached: encryption failure")?;
84-
if tag != &calc_tag {
85-
return Err("encrypt_inout_detached: tag mismatch");
86-
}
87-
if ct != buf {
88-
return Err("encrypt_inout_detached: ciphertext mismatch");
89-
}
90-
91-
// Fill output buffer with "garbage"
92-
buf.iter_mut()
93-
.enumerate()
94-
.for_each(|(i, v): (usize, &mut u8)| *v = i as u8);
95-
96-
let inout_buf = InOutBuf::new(ct, &mut buf).expect("ct and buf have the same length");
97-
cipher
98-
.decrypt_inout_detached(nonce, aad, inout_buf, tag)
99-
.map_err(|_| "decrypt_inout_detached: decryption failure")?;
100-
if plaintext != buf {
101-
return Err("decrypt_inout_detached: plaintext mismatch");
102-
}
103-
10453
Ok(())
10554
}
10655

10756
/// Run AEAD test for the provided failing test vector
108-
pub fn fail_test<C: AeadInOut + KeyInit>(
57+
///
58+
/// # Errors
59+
/// - If the cipher has failed initialization with the provided key.
60+
/// - If the cipher has passed the test vector.
61+
pub fn fail_test<C: Aead + KeyInit>(
10962
&TestVector {
11063
key,
11164
nonce,
@@ -114,16 +67,9 @@ pub fn fail_test<C: AeadInOut + KeyInit>(
11467
..
11568
}: &TestVector,
11669
) -> Result<(), &'static str> {
117-
let nonce = nonce.try_into().expect("wrong nonce size");
118-
let cipher = <C as KeyInit>::new_from_slice(key).expect("failed to initialize the cipher");
70+
let cipher: C = KeyInit::new_from_slice(key).map_err(|_| "failed to initialize the cipher")?;
11971

120-
let res = cipher.decrypt(
121-
nonce,
122-
Payload {
123-
aad,
124-
msg: ciphertext,
125-
},
126-
);
72+
let res = cipher.decrypt_into_vec(nonce, aad, ciphertext);
12773
if res.is_ok() {
12874
Err("decryption must return error")
12975
} else {
@@ -134,6 +80,9 @@ pub fn fail_test<C: AeadInOut + KeyInit>(
13480
/// Define AEAD test for passing test vectors
13581
#[macro_export]
13682
macro_rules! new_pass_test {
83+
($name:ident, $cipher:ty $(,)?) => {
84+
$crate::new_pass_test!($name, stringify!($name), $cipher);
85+
};
13786
($name:ident, $test_name:expr, $cipher:ty $(,)?) => {
13887
#[test]
13988
fn $name() {
@@ -165,6 +114,9 @@ macro_rules! new_pass_test {
165114
/// Define AEAD test for failing test vectors
166115
#[macro_export]
167116
macro_rules! new_fail_test {
117+
($name:ident, $cipher:ty $(,)?) => {
118+
$crate::new_fail_test!($name, stringify!($name), $cipher);
119+
};
168120
($name:ident, $test_name:expr, $cipher:ty $(,)?) => {
169121
#[test]
170122
fn $name() {

0 commit comments

Comments
 (0)