Skip to main content

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}