aead/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8#![forbid(unsafe_code)]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13#[cfg(feature = "dev")]
14pub mod dev;
15
16pub use common::{
17 self, Key, KeyInit, KeySizeUser,
18 array::{self, typenum::consts},
19};
20
21#[cfg(feature = "arrayvec")]
22pub use arrayvec;
23#[cfg(feature = "bytes")]
24pub use bytes;
25#[cfg(feature = "rand_core")]
26pub use common::{Generate, rand_core};
27pub use inout;
28
29use common::array::{Array, ArraySize, typenum::Unsigned};
30use core::fmt;
31use inout::InOutBuf;
32
33#[cfg(feature = "alloc")]
34use alloc::vec::Vec;
35#[cfg(feature = "bytes")]
36use bytes::BytesMut;
37
38/// Error type.
39///
40/// This type is deliberately opaque as to avoid potential side-channel
41/// leakage (e.g. padding oracle).
42#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
43pub struct Error;
44
45/// Result type alias with [`Error`].
46pub type Result<T> = core::result::Result<T, Error>;
47
48impl fmt::Display for Error {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str("aead::Error")
51 }
52}
53
54impl core::error::Error for Error {}
55
56/// Nonce: single-use value for ensuring ciphertexts are unique.
57///
58/// AEAD algorithms accept a parameter to encryption/decryption called
59/// a "nonce" which must be unique every time encryption is performed and
60/// never repeated for the same key. The nonce is often prepended to the
61/// ciphertext, a.k.a. an explicit nonce, but may also be an implicit counter.
62///
63/// AEAD decryption takes the nonce which was originally used to produce a
64/// given ciphertext as a parameter along with the ciphertext itself.
65///
66/// # Generating random nonces
67///
68/// Nonces don't necessarily have to be random, but it is a simple strategy
69/// which can be implemented as follows using the [`Generate`] trait
70/// (requires `getrandom` feature):
71///
72/// ```text
73/// use aead::{Nonce, Generate};
74///
75/// let nonce = Nonce::<AeadAlg>::generate();
76/// ```
77///
78/// <div class="warning">
79/// AEAD algorithms often fail catastrophically if nonces are ever repeated
80/// (with SIV modes being an exception).
81///
82/// Using random nonces runs the risk of repeating them unless the nonce
83/// size is particularly large, e.g. 192-bit extended nonces used by the
84/// `XChaCha20Poly1305` and `XSalsa20Poly1305` constructions.
85///
86/// [NIST SP 800-38D] recommends the following:
87///
88/// > The total number of invocations of the authenticated encryption
89/// > function shall not exceed 2<sup>32</sup>, including all IV lengths and all
90/// > instances of the authenticated encryption function with the given key.
91///
92/// Following this guideline, only 4,294,967,296 messages with random
93/// nonces can be encrypted under a given key. While this bound is high,
94/// it's possible to encounter in practice, and systems which might
95/// reach it should consider alternatives to purely random nonces, like
96/// a counter or a combination of a random nonce + counter.
97///
98/// See the [`aead-stream`] crate for a ready-made implementation of the latter.
99/// </div>
100///
101/// [NIST SP 800-38D]: https://csrc.nist.gov/publications/detail/sp/800-38d/final
102/// [`aead-stream`]: https://docs.rs/aead-stream
103pub type Nonce<A> = Array<u8, <A as AeadCore>::NonceSize>;
104
105/// Tag: authentication code which ensures ciphertexts are authentic
106pub type Tag<A> = Array<u8, <A as AeadCore>::TagSize>;
107
108/// Enum which specifies tag position used by an AEAD algorithm.
109#[derive(Debug, Clone, Copy, Eq, PartialEq)]
110pub enum TagPosition {
111 /// Postfix tag
112 Postfix,
113 /// Prefix tag
114 Prefix,
115}
116
117/// Authenticated Encryption with Associated Data (AEAD) algorithm.
118pub trait AeadCore {
119 /// The length of a nonce.
120 type NonceSize: ArraySize;
121
122 /// The maximum length of the tag.
123 type TagSize: ArraySize;
124
125 /// The AEAD tag position.
126 const TAG_POSITION: TagPosition;
127}
128
129/// Authenticated Encryption with Associated Data (AEAD) algorithm.
130#[cfg(feature = "alloc")]
131pub trait Aead: AeadCore {
132 /// Encrypt the given plaintext payload, and return the resulting
133 /// ciphertext as a vector of bytes.
134 ///
135 /// The [`Payload`] type can be used to provide Additional Associated Data
136 /// (AAD) along with the message: this is an optional bytestring which is
137 /// not encrypted, but *is* authenticated along with the message. Failure
138 /// to pass the same AAD that was used during encryption will cause
139 /// decryption to fail, which is useful if you would like to "bind" the
140 /// ciphertext to some other identifier, like a digital signature key
141 /// or other identifier.
142 ///
143 /// If you don't care about AAD and just want to encrypt a plaintext
144 /// message, `&[u8]` will automatically be coerced into a `Payload`:
145 ///
146 /// ```nobuild
147 /// let plaintext = b"Top secret message, handle with care";
148 /// let ciphertext = cipher.encrypt(nonce, plaintext);
149 /// ```
150 ///
151 /// The default implementation assumes a postfix tag (ala AES-GCM,
152 /// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not
153 /// use a postfix tag will need to override this to correctly assemble the
154 /// ciphertext message.
155 ///
156 /// # Errors
157 /// AEAD algorithm implementations may return an error if the plaintext or AAD are too long.
158 fn encrypt<'msg, 'aad>(
159 &self,
160 nonce: &Nonce<Self>,
161 plaintext: impl Into<Payload<'msg, 'aad>>,
162 ) -> Result<Vec<u8>>;
163
164 /// Decrypt the given ciphertext slice, and return the resulting plaintext
165 /// as a vector of bytes.
166 ///
167 /// See notes on [`Aead::encrypt()`] about allowable message payloads and
168 /// Associated Additional Data (AAD).
169 ///
170 /// If you have no AAD, you can call this as follows:
171 ///
172 /// ```nobuild
173 /// let ciphertext = b"...";
174 /// let plaintext = cipher.decrypt(nonce, ciphertext)?;
175 /// ```
176 ///
177 /// The default implementation assumes a postfix tag (ala AES-GCM,
178 /// AES-GCM-SIV, ChaCha20Poly1305). [`Aead`] implementations which do not
179 /// use a postfix tag will need to override this to correctly parse the
180 /// ciphertext message.
181 ///
182 /// # Errors
183 /// - if the `ciphertext` is inauthentic (i.e. tag verification failure)
184 /// - if the `ciphertext` is too long
185 /// - if the `aad` is too long
186 fn decrypt<'msg, 'aad>(
187 &self,
188 nonce: &Nonce<Self>,
189 ciphertext: impl Into<Payload<'msg, 'aad>>,
190 ) -> Result<Vec<u8>>;
191}
192
193#[cfg(feature = "alloc")]
194impl<T: AeadInOut> Aead for T {
195 fn encrypt<'msg, 'aad>(
196 &self,
197 nonce: &Nonce<Self>,
198 plaintext: impl Into<Payload<'msg, 'aad>>,
199 ) -> Result<Vec<u8>> {
200 let payload = plaintext.into();
201 let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
202 buffer.extend_from_slice(payload.msg);
203 self.encrypt_in_place(nonce, payload.aad, &mut buffer)?;
204 Ok(buffer)
205 }
206
207 fn decrypt<'msg, 'aad>(
208 &self,
209 nonce: &Nonce<Self>,
210 ciphertext: impl Into<Payload<'msg, 'aad>>,
211 ) -> Result<Vec<u8>> {
212 let payload = ciphertext.into();
213 let mut buffer = Vec::from(payload.msg);
214 self.decrypt_in_place(nonce, payload.aad, &mut buffer)?;
215 Ok(buffer)
216 }
217}
218
219/// In-place and inout AEAD trait which handles the authentication tag as a return value/separate parameter.
220pub trait AeadInOut: AeadCore {
221 /// Encrypt the data in the provided [`InOutBuf`], returning the authentication tag.
222 ///
223 /// # Errors
224 /// AEAD algorithm implementations may return an error if the plaintext or AAD are too long.
225 fn encrypt_inout_detached(
226 &self,
227 nonce: &Nonce<Self>,
228 associated_data: &[u8],
229 buffer: InOutBuf<'_, '_, u8>,
230 ) -> Result<Tag<Self>>;
231
232 /// Decrypt the data in the provided [`InOutBuf`], returning an error in the event the
233 /// provided authentication tag is invalid for the given ciphertext (i.e. ciphertext
234 /// is modified/unauthentic).
235 ///
236 /// # Errors
237 /// - if the `ciphertext` is inauthentic (i.e. tag verification failure)
238 /// - if the `ciphertext` is too long
239 /// - if the `aad` is too long
240 fn decrypt_inout_detached(
241 &self,
242 nonce: &Nonce<Self>,
243 associated_data: &[u8],
244 buffer: InOutBuf<'_, '_, u8>,
245 tag: &Tag<Self>,
246 ) -> Result<()>;
247
248 /// Encrypt the given buffer containing a plaintext message in-place.
249 ///
250 /// The buffer must have sufficient capacity to store the ciphertext
251 /// message, which will always be larger than the original plaintext.
252 /// The exact size needed is cipher-dependent, but generally includes
253 /// the size of an authentication tag.
254 ///
255 /// # Errors
256 /// Returns an error if the buffer has insufficient capacity to store the
257 /// resulting ciphertext message.
258 fn encrypt_in_place(
259 &self,
260 nonce: &Nonce<Self>,
261 associated_data: &[u8],
262 buffer: &mut dyn Buffer,
263 ) -> Result<()> {
264 match Self::TAG_POSITION {
265 TagPosition::Prefix => {
266 let msg_len = buffer.len();
267 buffer.extend_from_slice(&Tag::<Self>::default())?;
268 let buffer = buffer.as_mut();
269 let tag_size = Self::TagSize::USIZE;
270 buffer.copy_within(..msg_len, tag_size);
271 let (tag_dst, msg) = buffer.split_at_mut(tag_size);
272 let tag = self.encrypt_inout_detached(nonce, associated_data, msg.into())?;
273 tag_dst.copy_from_slice(&tag);
274 }
275 TagPosition::Postfix => {
276 let tag =
277 self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?;
278 buffer.extend_from_slice(tag.as_slice())?;
279 }
280 }
281 Ok(())
282 }
283
284 /// Decrypt the message in-place, returning an error in the event the
285 /// provided authentication tag does not match the given ciphertext.
286 ///
287 /// The buffer will be truncated to the length of the original plaintext
288 /// message upon success.
289 ///
290 /// # Errors
291 /// - if the `ciphertext` is inauthentic (i.e. tag verification failure)
292 fn decrypt_in_place(
293 &self,
294 nonce: &Nonce<Self>,
295 associated_data: &[u8],
296 buffer: &mut dyn Buffer,
297 ) -> Result<()> {
298 let tag_size = Self::TagSize::USIZE;
299 let tagless_len = buffer.len().checked_sub(tag_size).ok_or(Error)?;
300
301 match Self::TAG_POSITION {
302 TagPosition::Prefix => {
303 let (tag, msg) = buffer.as_mut().split_at_mut(tag_size);
304 let tag = Tag::<Self>::try_from(&*tag).expect("tag length mismatch");
305 self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?;
306 buffer.as_mut().copy_within(tag_size.., 0);
307 }
308 TagPosition::Postfix => {
309 let (msg, tag) = buffer.as_mut().split_at_mut(tagless_len);
310 let tag = Tag::<Self>::try_from(&*tag).expect("tag length mismatch");
311 self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?;
312 }
313 }
314 buffer.truncate(tagless_len);
315 Ok(())
316 }
317}
318
319/// Legacy in-place stateless AEAD trait.
320///
321/// NOTE: deprecated! Please migrate to [`AeadInOut`].
322#[deprecated(since = "0.6.0", note = "use `AeadInOut` instead")]
323#[allow(clippy::missing_errors_doc)]
324pub trait AeadInPlace: AeadCore {
325 /// Encrypt the given buffer containing a plaintext message in-place.
326 #[deprecated(since = "0.6.0", note = "use `AeadInOut::encrypt_in_place` instead")]
327 fn encrypt_in_place(
328 &self,
329 nonce: &Nonce<Self>,
330 associated_data: &[u8],
331 buffer: &mut dyn Buffer,
332 ) -> Result<()>;
333
334 /// Encrypt the data in-place, returning the authentication tag
335 #[deprecated(
336 since = "0.6.0",
337 note = "use `AeadInOut::encrypt_inout_detached` instead"
338 )]
339 fn encrypt_in_place_detached(
340 &self,
341 nonce: &Nonce<Self>,
342 associated_data: &[u8],
343 buffer: &mut [u8],
344 ) -> Result<Tag<Self>>;
345
346 /// Decrypt the message in-place, returning an error in the event the
347 /// provided authentication tag does not match the given ciphertext.
348 #[deprecated(since = "0.6.0", note = "use `AeadInOut::decrypt_in_place` instead")]
349 fn decrypt_in_place(
350 &self,
351 nonce: &Nonce<Self>,
352 associated_data: &[u8],
353 buffer: &mut dyn Buffer,
354 ) -> Result<()>;
355
356 /// Decrypt the message in-place, returning an error in the event the provided
357 /// authentication tag does not match the given ciphertext (i.e. ciphertext
358 /// is modified/unauthentic)
359 #[deprecated(
360 since = "0.6.0",
361 note = "use `AeadInOut::decrypt_inout_detached` instead"
362 )]
363 fn decrypt_in_place_detached(
364 &self,
365 nonce: &Nonce<Self>,
366 associated_data: &[u8],
367 buffer: &mut [u8],
368 tag: &Tag<Self>,
369 ) -> Result<()>;
370}
371
372#[allow(deprecated)]
373impl<T: AeadInOut> AeadInPlace for T {
374 fn encrypt_in_place(
375 &self,
376 nonce: &Nonce<Self>,
377 associated_data: &[u8],
378 buffer: &mut dyn Buffer,
379 ) -> Result<()> {
380 <Self as AeadInOut>::encrypt_in_place(self, nonce, associated_data, buffer)
381 }
382
383 fn encrypt_in_place_detached(
384 &self,
385 nonce: &Nonce<Self>,
386 associated_data: &[u8],
387 buffer: &mut [u8],
388 ) -> Result<Tag<Self>> {
389 self.encrypt_inout_detached(nonce, associated_data, buffer.into())
390 }
391
392 fn decrypt_in_place(
393 &self,
394 nonce: &Nonce<Self>,
395 associated_data: &[u8],
396 buffer: &mut dyn Buffer,
397 ) -> Result<()> {
398 <Self as AeadInOut>::decrypt_in_place(self, nonce, associated_data, buffer)
399 }
400
401 fn decrypt_in_place_detached(
402 &self,
403 nonce: &Nonce<Self>,
404 associated_data: &[u8],
405 buffer: &mut [u8],
406 tag: &Tag<Self>,
407 ) -> Result<()> {
408 self.decrypt_inout_detached(nonce, associated_data, buffer.into(), tag)
409 }
410}
411
412/// AEAD payloads (message + AAD).
413///
414/// Combination of a message (plaintext or ciphertext) and
415/// "additional associated data" (AAD) to be authenticated (in cleartext)
416/// along with the message.
417///
418/// If you don't care about AAD, you can pass a `&[u8]` as the payload to
419/// `encrypt`/`decrypt` and it will automatically be coerced to this type.
420#[derive(Debug)]
421pub struct Payload<'msg, 'aad> {
422 /// Message to be encrypted/decrypted
423 pub msg: &'msg [u8],
424
425 /// Optional "additional associated data" to authenticate along with
426 /// this message. If AAD is provided at the time the message is encrypted,
427 /// the same AAD *MUST* be provided at the time the message is decrypted,
428 /// or decryption will fail.
429 pub aad: &'aad [u8],
430}
431
432impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> {
433 fn from(msg: &'msg [u8]) -> Self {
434 Self { msg, aad: b"" }
435 }
436}
437
438/// In-place encryption/decryption byte buffers.
439///
440/// This trait defines the set of methods needed to support in-place operations
441/// on a `Vec`-like data type.
442pub trait Buffer: AsRef<[u8]> + AsMut<[u8]> {
443 /// Get the length of the buffer
444 fn len(&self) -> usize {
445 self.as_ref().len()
446 }
447
448 /// Is the buffer empty?
449 fn is_empty(&self) -> bool {
450 self.as_ref().is_empty()
451 }
452
453 /// Extend this buffer from the given slice.
454 ///
455 /// # Errors
456 /// If the buffer has insufficient capacity.
457 fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>;
458
459 /// Truncate this buffer to the given size.
460 fn truncate(&mut self, len: usize);
461}
462
463#[cfg(feature = "alloc")]
464impl Buffer for Vec<u8> {
465 fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
466 Vec::extend_from_slice(self, other);
467 Ok(())
468 }
469
470 fn truncate(&mut self, len: usize) {
471 Vec::truncate(self, len);
472 }
473}
474
475#[cfg(feature = "bytes")]
476impl Buffer for BytesMut {
477 fn len(&self) -> usize {
478 BytesMut::len(self)
479 }
480
481 fn is_empty(&self) -> bool {
482 BytesMut::is_empty(self)
483 }
484
485 fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
486 BytesMut::extend_from_slice(self, other);
487 Ok(())
488 }
489
490 fn truncate(&mut self, len: usize) {
491 BytesMut::truncate(self, len);
492 }
493}
494
495#[cfg(feature = "arrayvec")]
496impl<const N: usize> Buffer for arrayvec::ArrayVec<u8, N> {
497 fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
498 arrayvec::ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error)
499 }
500
501 fn truncate(&mut self, len: usize) {
502 arrayvec::ArrayVec::truncate(self, len);
503 }
504}