Skip to content

Commit 8e522f8

Browse files
tarcierinewpavlov
authored andcommitted
Add detached in-place encrypt/decrypt API; make alloc optional (#58)
This commit adds `encrypt_in_place_detached()` and `decrypt_in_place_detached()` methods to `Aead` and `AeadMut`, along with default implementations of `encrypt()` and `decrypt()` which assume a postfix authentication tag. Presently all of the AEAD implementations in `RustCrypto/AEADs` use this pattern. This PR exposes the detached interface as a public API: RustCrypto/AEADs#21 Note that this need not be the only in-place API (hence the long name with `_detached` on the end. I plan on doing a follow-up PR for adding an in-place API which does not depend on `alloc` but also handles ciphertext message assembly/parsing, which would be more useful for end users. That said, this API isn't just useful for AEAD implementers: there are genuine use cases for a detached API from an end user perspective, e.g encrypted filesystems. With the addition of this API, we can also make `alloc` an optional (but enabled-by-default) feature, allowing use of AEADs on truly `#![no_std]` targets.
1 parent 5b012e4 commit 8e522f8

File tree

2 files changed

+131
-5
lines changed

2 files changed

+131
-5
lines changed

aead/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ categories = ["cryptography", "no-std"]
1212

1313
[dependencies]
1414
generic-array = { version = "0.12", default-features = false }
15+
16+
[features]
17+
default = ["alloc"]
18+
alloc = []

aead/src/lib.rs

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,50 @@
1414
1515
#![no_std]
1616

17+
#[cfg(feature = "alloc")]
1718
extern crate alloc;
1819

1920
pub use generic_array;
2021

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

26+
// Define the default implementation for both `Aead::encrypt()` and
27+
// `AeadMut::encrypt()`. Uses a macro to gloss over `&self` vs `&mut self`.
28+
#[cfg(feature = "alloc")]
29+
macro_rules! encrypt_to_postfix_tagged_vec {
30+
($aead:expr, $nonce:expr, $payload:expr) => {{
31+
let payload = $payload.into();
32+
let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
33+
buffer.extend_from_slice(payload.msg);
34+
35+
let tag = $aead.encrypt_in_place_detached($nonce, payload.aad, &mut buffer)?;
36+
buffer.extend_from_slice(tag.as_slice());
37+
Ok(buffer)
38+
}};
39+
}
40+
41+
// Define the default implementation for both `Aead::decrypt()` and
42+
// `AeadMut::decrypt()`. Uses a macro to gloss over `&self` vs `&mut self`.
43+
#[cfg(feature = "alloc")]
44+
macro_rules! decrypt_postfix_tagged_ciphertext_to_vec {
45+
($aead:expr, $nonce:expr, $payload:expr) => {{
46+
let payload = $payload.into();
47+
48+
if payload.msg.len() < Self::TagSize::to_usize() {
49+
return Err(Error);
50+
}
51+
52+
let tag_start = payload.msg.len() - Self::TagSize::to_usize();
53+
let mut buffer = Vec::from(&payload.msg[..tag_start]);
54+
let tag = GenericArray::from_slice(&payload.msg[tag_start..]);
55+
$aead.decrypt_in_place_detached($nonce, payload.aad, &mut buffer, tag)?;
56+
57+
Ok(buffer)
58+
}};
59+
}
60+
2461
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
2562
pub struct Error;
2663

@@ -36,7 +73,7 @@ pub trait NewAead {
3673
/// Authenticated Encryption with Associated Data (AEAD) algorithm.
3774
///
3875
/// This trait is intended for use with stateless AEAD algorithms. The
39-
/// `AeadMut` trait provides a stateful interface.
76+
/// [`AeadMut`] trait provides a stateful interface.
4077
pub trait Aead {
4178
/// The length of a nonce.
4279
type NonceSize: ArrayLength<u8>;
@@ -64,11 +101,27 @@ pub trait Aead {
64101
/// let plaintext = b"Top secret message, handle with care";
65102
/// let ciphertext = cipher.encrypt(nonce, plaintext);
66103
/// ```
104+
///
105+
/// The default implementation assumes a postfix tag (ala AES-GCM,
106+
/// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not
107+
/// use a postfix tag will need to override this to correctly assemble the
108+
/// ciphertext message.
109+
#[cfg(feature = "alloc")]
67110
fn encrypt<'msg, 'aad>(
68111
&self,
69112
nonce: &GenericArray<u8, Self::NonceSize>,
70113
plaintext: impl Into<Payload<'msg, 'aad>>,
71-
) -> Result<Vec<u8>, Error>;
114+
) -> Result<Vec<u8>, Error> {
115+
encrypt_to_postfix_tagged_vec!(self, nonce, plaintext)
116+
}
117+
118+
/// Encrypt the data in-place, returning the authentication tag
119+
fn encrypt_in_place_detached(
120+
&self,
121+
nonce: &GenericArray<u8, Self::NonceSize>,
122+
associated_data: &[u8],
123+
buffer: &mut [u8],
124+
) -> Result<GenericArray<u8, Self::TagSize>, Error>;
72125

73126
/// Decrypt the given ciphertext slice, and return the resulting plaintext
74127
/// as a vector of bytes.
@@ -82,11 +135,30 @@ pub trait Aead {
82135
/// let ciphertext = b"...";
83136
/// let plaintext = cipher.decrypt(nonce, ciphertext)?;
84137
/// ```
138+
///
139+
/// The default implementation assumes a postfix tag (ala AES-GCM,
140+
/// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not
141+
/// use a postfix tag will need to override this to correctly parse the
142+
/// ciphertext message.
143+
#[cfg(feature = "alloc")]
85144
fn decrypt<'msg, 'aad>(
86145
&self,
87146
nonce: &GenericArray<u8, Self::NonceSize>,
88147
ciphertext: impl Into<Payload<'msg, 'aad>>,
89-
) -> Result<Vec<u8>, Error>;
148+
) -> Result<Vec<u8>, Error> {
149+
decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext)
150+
}
151+
152+
/// Decrypt the data in-place, returning an error in the event the provided
153+
/// authentication tag does not match the given ciphertext (i.e. ciphertext
154+
/// is modified/unauthentic)
155+
fn decrypt_in_place_detached(
156+
&self,
157+
nonce: &GenericArray<u8, Self::NonceSize>,
158+
associated_data: &[u8],
159+
buffer: &mut [u8],
160+
tag: &GenericArray<u8, Self::TagSize>,
161+
) -> Result<(), Error>;
90162
}
91163

92164
/// Stateful Authenticated Encryption with Associated Data algorithm.
@@ -104,22 +176,47 @@ pub trait AeadMut {
104176
///
105177
/// See notes on [`Aead::encrypt()`] about allowable message payloads and
106178
/// Associated Additional Data (AAD).
179+
#[cfg(feature = "alloc")]
107180
fn encrypt<'msg, 'aad>(
108181
&mut self,
109182
nonce: &GenericArray<u8, Self::NonceSize>,
110183
plaintext: impl Into<Payload<'msg, 'aad>>,
111-
) -> Result<Vec<u8>, Error>;
184+
) -> Result<Vec<u8>, Error> {
185+
encrypt_to_postfix_tagged_vec!(self, nonce, plaintext)
186+
}
187+
188+
/// Encrypt the data in-place, returning the authentication tag
189+
fn encrypt_in_place_detached(
190+
&mut self,
191+
nonce: &GenericArray<u8, Self::NonceSize>,
192+
associated_data: &[u8],
193+
buffer: &mut [u8],
194+
) -> Result<GenericArray<u8, Self::TagSize>, Error>;
112195

113196
/// Decrypt the given ciphertext slice, and return the resulting plaintext
114197
/// as a vector of bytes.
115198
///
116199
/// See notes on [`Aead::encrypt()`] and [`Aead::decrypt()`] about allowable
117200
/// message payloads and Associated Additional Data (AAD).
201+
#[cfg(feature = "alloc")]
118202
fn decrypt<'msg, 'aad>(
119203
&mut self,
120204
nonce: &GenericArray<u8, Self::NonceSize>,
121205
ciphertext: impl Into<Payload<'msg, 'aad>>,
122-
) -> Result<Vec<u8>, Error>;
206+
) -> Result<Vec<u8>, Error> {
207+
decrypt_postfix_tagged_ciphertext_to_vec!(self, nonce, ciphertext)
208+
}
209+
210+
/// Decrypt the data in-place, returning an error in the event the provided
211+
/// authentication tag does not match the given ciphertext (i.e. ciphertext
212+
/// is modified/unauthentic)
213+
fn decrypt_in_place_detached(
214+
&mut self,
215+
nonce: &GenericArray<u8, Self::NonceSize>,
216+
associated_data: &[u8],
217+
buffer: &mut [u8],
218+
tag: &GenericArray<u8, Self::TagSize>,
219+
) -> Result<(), Error>;
123220
}
124221

125222
/// A blanket implementation of the Stateful AEAD interface for Stateless
@@ -131,6 +228,7 @@ impl<Algo: Aead> AeadMut for Algo {
131228

132229
/// Encrypt the given plaintext slice, and return the resulting ciphertext
133230
/// as a vector of bytes.
231+
#[cfg(feature = "alloc")]
134232
fn encrypt<'msg, 'aad>(
135233
&mut self,
136234
nonce: &GenericArray<u8, Self::NonceSize>,
@@ -139,15 +237,39 @@ impl<Algo: Aead> AeadMut for Algo {
139237
<Self as Aead>::encrypt(self, nonce, plaintext)
140238
}
141239

240+
/// Encrypt the data in-place, returning the authentication tag
241+
fn encrypt_in_place_detached(
242+
&mut self,
243+
nonce: &GenericArray<u8, Self::NonceSize>,
244+
associated_data: &[u8],
245+
buffer: &mut [u8],
246+
) -> Result<GenericArray<u8, Self::TagSize>, Error> {
247+
<Self as Aead>::encrypt_in_place_detached(self, nonce, associated_data, buffer)
248+
}
249+
142250
/// Decrypt the given ciphertext slice, and return the resulting plaintext
143251
/// as a vector of bytes.
252+
#[cfg(feature = "alloc")]
144253
fn decrypt<'msg, 'aad>(
145254
&mut self,
146255
nonce: &GenericArray<u8, Self::NonceSize>,
147256
ciphertext: impl Into<Payload<'msg, 'aad>>,
148257
) -> Result<Vec<u8>, Error> {
149258
<Self as Aead>::decrypt(self, nonce, ciphertext)
150259
}
260+
261+
/// Decrypt the data in-place, returning an error in the event the provided
262+
/// authentication tag does not match the given ciphertext (i.e. ciphertext
263+
/// is modified/unauthentic)
264+
fn decrypt_in_place_detached(
265+
&mut self,
266+
nonce: &GenericArray<u8, Self::NonceSize>,
267+
associated_data: &[u8],
268+
buffer: &mut [u8],
269+
tag: &GenericArray<u8, Self::TagSize>,
270+
) -> Result<(), Error> {
271+
<Self as Aead>::decrypt_in_place_detached(self, nonce, associated_data, buffer, tag)
272+
}
151273
}
152274

153275
/// AEAD payloads are a combination of a message (plaintext or ciphertext)

0 commit comments

Comments
 (0)