ring/
aead.rs

1// Copyright 2015-2016 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! Authenticated Encryption with Associated Data (AEAD).
16//!
17//! See [Authenticated encryption: relations among notions and analysis of the
18//! generic composition paradigm][AEAD] for an introduction to the concept of
19//! AEADs.
20//!
21//! [AEAD]: http://www-cse.ucsd.edu/~mihir/papers/oem.html
22//! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
23
24use self::block::{Block, BLOCK_LEN};
25use crate::{constant_time, cpu, error, hkdf, polyfill};
26use core::ops::RangeFrom;
27
28pub use self::{
29    aes_gcm::{AES_128_GCM, AES_256_GCM},
30    chacha20_poly1305::CHACHA20_POLY1305,
31    nonce::{Nonce, NONCE_LEN},
32};
33
34/// A sequences of unique nonces.
35///
36/// A given `NonceSequence` must never return the same `Nonce` twice from
37/// `advance()`.
38///
39/// A simple counter is a reasonable (but probably not ideal) `NonceSequence`.
40///
41/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
42/// of the sequence.
43pub trait NonceSequence {
44    /// Returns the next nonce in the sequence.
45    ///
46    /// This may fail if "too many" nonces have been requested, where how many
47    /// is too many is up to the implementation of `NonceSequence`. An
48    /// implementation may that enforce a maximum number of records are
49    /// sent/received under a key this way. Once `advance()` fails, it must
50    /// fail for all subsequent calls.
51    fn advance(&mut self) -> Result<Nonce, error::Unspecified>;
52}
53
54/// An AEAD key bound to a nonce sequence.
55pub trait BoundKey<N: NonceSequence>: core::fmt::Debug {
56    /// Constructs a new key from the given `UnboundKey` and `NonceSequence`.
57    fn new(key: UnboundKey, nonce_sequence: N) -> Self;
58
59    /// The key's AEAD algorithm.
60    fn algorithm(&self) -> &'static Algorithm;
61}
62
63/// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
64/// sequence.
65///
66/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
67/// of the nonce sequence.
68pub struct OpeningKey<N: NonceSequence> {
69    key: UnboundKey,
70    nonce_sequence: N,
71}
72
73impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
74    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
75        Self {
76            key,
77            nonce_sequence,
78        }
79    }
80
81    #[inline]
82    fn algorithm(&self) -> &'static Algorithm {
83        self.key.algorithm
84    }
85}
86
87impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
88    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
89        f.debug_struct("OpeningKey")
90            .field("algorithm", &self.algorithm())
91            .finish()
92    }
93}
94
95impl<N: NonceSequence> OpeningKey<N> {
96    /// Authenticates and decrypts (“opens”) data in place.
97    ///
98    /// `aad` is the additional authenticated data (AAD), if any.
99    ///
100    /// On input, `in_out` must be the ciphertext followed by the tag. When
101    /// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
102    /// has been overwritten by the plaintext; `plaintext` will refer to the
103    /// plaintext without the tag.
104    ///
105    /// When `open_in_place()` returns `Err(..)`, `in_out` may have been
106    /// overwritten in an unspecified way.
107    #[inline]
108    pub fn open_in_place<'in_out, A>(
109        &mut self,
110        aad: Aad<A>,
111        in_out: &'in_out mut [u8],
112    ) -> Result<&'in_out mut [u8], error::Unspecified>
113    where
114        A: AsRef<[u8]>,
115    {
116        self.open_within(aad, in_out, 0..)
117    }
118
119    /// Authenticates and decrypts (“opens”) data in place, with a shift.
120    ///
121    /// `aad` is the additional authenticated data (AAD), if any.
122    ///
123    /// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
124    /// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
125    /// will be at `in_out[0..plaintext.len()]`. In other words, the following
126    /// two code fragments are equivalent for valid values of
127    /// `ciphertext_and_tag`, except `open_within` will often be more efficient:
128    ///
129    ///
130    /// ```skip
131    /// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
132    /// ```
133    ///
134    /// ```skip
135    /// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
136    /// in_out.copy_within(ciphertext_and_tag, 0);
137    /// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
138    /// ```
139    ///
140    /// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
141    /// `key.open_in_place(aad, in_out)`.
142    ///
143    ///  When `open_in_place()` returns `Err(..)`, `in_out` may have been
144    /// overwritten in an unspecified way.
145    ///
146    /// The shifting feature is useful in the case where multiple packets are
147    /// being reassembled in place. Consider this example where the peer has
148    /// sent the message “Split stream reassembled in place” split into
149    /// three sealed packets:
150    ///
151    /// ```ascii-art
152    ///                 Packet 1                  Packet 2                 Packet 3
153    /// Input:  [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
154    ///                      |         +--------------+                        |
155    ///               +------+   +-----+    +----------------------------------+
156    ///               v          v          v
157    /// Output: [Plaintext][Plaintext][Plaintext]
158    ///        “Split stream reassembled in place”
159    /// ```
160    ///
161    /// This reassembly be accomplished with three calls to `open_within()`.
162    #[inline]
163    pub fn open_within<'in_out, A>(
164        &mut self,
165        aad: Aad<A>,
166        in_out: &'in_out mut [u8],
167        ciphertext_and_tag: RangeFrom<usize>,
168    ) -> Result<&'in_out mut [u8], error::Unspecified>
169    where
170        A: AsRef<[u8]>,
171    {
172        open_within_(
173            &self.key,
174            self.nonce_sequence.advance()?,
175            aad,
176            in_out,
177            ciphertext_and_tag,
178        )
179    }
180}
181
182#[inline]
183fn open_within_<'in_out, A: AsRef<[u8]>>(
184    key: &UnboundKey,
185    nonce: Nonce,
186    Aad(aad): Aad<A>,
187    in_out: &'in_out mut [u8],
188    ciphertext_and_tag: RangeFrom<usize>,
189) -> Result<&'in_out mut [u8], error::Unspecified> {
190    fn open_within<'in_out>(
191        key: &UnboundKey,
192        nonce: Nonce,
193        aad: Aad<&[u8]>,
194        in_out: &'in_out mut [u8],
195        ciphertext_and_tag: RangeFrom<usize>,
196    ) -> Result<&'in_out mut [u8], error::Unspecified> {
197        let in_prefix_len = ciphertext_and_tag.start;
198        let ciphertext_and_tag_len = in_out
199            .len()
200            .checked_sub(in_prefix_len)
201            .ok_or(error::Unspecified)?;
202        let ciphertext_len = ciphertext_and_tag_len
203            .checked_sub(TAG_LEN)
204            .ok_or(error::Unspecified)?;
205        check_per_nonce_max_bytes(key.algorithm, ciphertext_len)?;
206        let (in_out, received_tag) = in_out.split_at_mut(in_prefix_len + ciphertext_len);
207        let Tag(calculated_tag) = (key.algorithm.open)(
208            &key.inner,
209            nonce,
210            aad,
211            in_prefix_len,
212            in_out,
213            key.cpu_features,
214        );
215        if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag).is_err() {
216            // Zero out the plaintext so that it isn't accidentally leaked or used
217            // after verification fails. It would be safest if we could check the
218            // tag before decrypting, but some `open` implementations interleave
219            // authentication with decryption for performance.
220            for b in &mut in_out[..ciphertext_len] {
221                *b = 0;
222            }
223            return Err(error::Unspecified);
224        }
225        // `ciphertext_len` is also the plaintext length.
226        Ok(&mut in_out[..ciphertext_len])
227    }
228
229    open_within(
230        key,
231        nonce,
232        Aad::from(aad.as_ref()),
233        in_out,
234        ciphertext_and_tag,
235    )
236}
237
238/// An AEAD key for encrypting and signing ("sealing"), bound to a nonce
239/// sequence.
240///
241/// Intentionally not `Clone` or `Copy` since cloning would allow duplication
242/// of the nonce sequence.
243pub struct SealingKey<N: NonceSequence> {
244    key: UnboundKey,
245    nonce_sequence: N,
246}
247
248impl<N: NonceSequence> BoundKey<N> for SealingKey<N> {
249    fn new(key: UnboundKey, nonce_sequence: N) -> Self {
250        Self {
251            key,
252            nonce_sequence,
253        }
254    }
255
256    #[inline]
257    fn algorithm(&self) -> &'static Algorithm {
258        self.key.algorithm
259    }
260}
261
262impl<N: NonceSequence> core::fmt::Debug for SealingKey<N> {
263    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
264        f.debug_struct("SealingKey")
265            .field("algorithm", &self.algorithm())
266            .finish()
267    }
268}
269
270impl<N: NonceSequence> SealingKey<N> {
271    /// Deprecated. Renamed to `seal_in_place_append_tag()`.
272    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
273    #[inline]
274    pub fn seal_in_place<A, InOut>(
275        &mut self,
276        aad: Aad<A>,
277        in_out: &mut InOut,
278    ) -> Result<(), error::Unspecified>
279    where
280        A: AsRef<[u8]>,
281        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
282    {
283        self.seal_in_place_append_tag(aad, in_out)
284    }
285
286    /// Encrypts and signs (“seals”) data in place, appending the tag to the
287    /// resulting ciphertext.
288    ///
289    /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
290    ///
291    /// ```skip
292    /// key.seal_in_place_separate_tag(aad, in_out.as_mut())
293    ///     .map(|tag| in_out.extend(tag.as_ref()))
294    /// ```
295    #[inline]
296    pub fn seal_in_place_append_tag<A, InOut>(
297        &mut self,
298        aad: Aad<A>,
299        in_out: &mut InOut,
300    ) -> Result<(), error::Unspecified>
301    where
302        A: AsRef<[u8]>,
303        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
304    {
305        self.seal_in_place_separate_tag(aad, in_out.as_mut())
306            .map(|tag| in_out.extend(tag.as_ref()))
307    }
308
309    /// Encrypts and signs (“seals”) data in place.
310    ///
311    /// `aad` is the additional authenticated data (AAD), if any. This is
312    /// authenticated but not encrypted. The type `A` could be a byte slice
313    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
314    /// If there is no AAD then use `Aad::empty()`.
315    ///
316    /// The plaintext is given as the input value of `in_out`. `seal_in_place()`
317    /// will overwrite the plaintext with the ciphertext and return the tag.
318    /// For most protocols, the caller must append the tag to the ciphertext.
319    /// The tag will be `self.algorithm.tag_len()` bytes long.
320    #[inline]
321    pub fn seal_in_place_separate_tag<A>(
322        &mut self,
323        aad: Aad<A>,
324        in_out: &mut [u8],
325    ) -> Result<Tag, error::Unspecified>
326    where
327        A: AsRef<[u8]>,
328    {
329        seal_in_place_separate_tag_(
330            &self.key,
331            self.nonce_sequence.advance()?,
332            Aad::from(aad.as_ref()),
333            in_out,
334        )
335    }
336}
337
338#[inline]
339fn seal_in_place_separate_tag_(
340    key: &UnboundKey,
341    nonce: Nonce,
342    aad: Aad<&[u8]>,
343    in_out: &mut [u8],
344) -> Result<Tag, error::Unspecified> {
345    check_per_nonce_max_bytes(key.algorithm, in_out.len())?;
346    Ok((key.algorithm.seal)(
347        &key.inner,
348        nonce,
349        aad,
350        in_out,
351        key.cpu_features,
352    ))
353}
354
355/// The additionally authenticated data (AAD) for an opening or sealing
356/// operation. This data is authenticated but is **not** encrypted.
357///
358/// The type `A` could be a byte slice `&[u8]`, a byte array `[u8; N]`
359/// for some constant `N`, `Vec<u8>`, etc.
360pub struct Aad<A: AsRef<[u8]>>(A);
361
362impl<A: AsRef<[u8]>> Aad<A> {
363    /// Construct the `Aad` from the given bytes.
364    #[inline]
365    pub fn from(aad: A) -> Self {
366        Aad(aad)
367    }
368}
369
370impl<A> AsRef<[u8]> for Aad<A>
371where
372    A: AsRef<[u8]>,
373{
374    fn as_ref(&self) -> &[u8] {
375        self.0.as_ref()
376    }
377}
378
379impl Aad<[u8; 0]> {
380    /// Construct an empty `Aad`.
381    pub fn empty() -> Self {
382        Self::from([])
383    }
384}
385
386/// An AEAD key without a designated role or nonce sequence.
387pub struct UnboundKey {
388    inner: KeyInner,
389    algorithm: &'static Algorithm,
390    cpu_features: cpu::Features,
391}
392
393impl core::fmt::Debug for UnboundKey {
394    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
395        f.debug_struct("UnboundKey")
396            .field("algorithm", &self.algorithm)
397            .finish()
398    }
399}
400
401#[allow(clippy::large_enum_variant, variant_size_differences)]
402enum KeyInner {
403    AesGcm(aes_gcm::Key),
404    ChaCha20Poly1305(chacha20_poly1305::Key),
405}
406
407impl UnboundKey {
408    /// Constructs an `UnboundKey`.
409    ///
410    /// Fails if `key_bytes.len() != algorithm.key_len()`.
411    pub fn new(
412        algorithm: &'static Algorithm,
413        key_bytes: &[u8],
414    ) -> Result<Self, error::Unspecified> {
415        let cpu_features = cpu::features();
416        Ok(Self {
417            inner: (algorithm.init)(key_bytes, cpu_features)?,
418            algorithm,
419            cpu_features,
420        })
421    }
422
423    /// The key's AEAD algorithm.
424    #[inline]
425    pub fn algorithm(&self) -> &'static Algorithm {
426        self.algorithm
427    }
428}
429
430impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundKey {
431    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
432        let mut key_bytes = [0; MAX_KEY_LEN];
433        let key_bytes = &mut key_bytes[..okm.len().key_len];
434        let algorithm = *okm.len();
435        okm.fill(key_bytes).unwrap();
436        Self::new(algorithm, key_bytes).unwrap()
437    }
438}
439
440impl hkdf::KeyType for &'static Algorithm {
441    #[inline]
442    fn len(&self) -> usize {
443        self.key_len()
444    }
445}
446
447/// Immutable keys for use in situations where `OpeningKey`/`SealingKey` and
448/// `NonceSequence` cannot reasonably be used.
449///
450/// Prefer to use `OpeningKey`/`SealingKey` and `NonceSequence` when practical.
451pub struct LessSafeKey {
452    key: UnboundKey,
453}
454
455impl LessSafeKey {
456    /// Constructs a `LessSafeKey` from an `UnboundKey`.
457    pub fn new(key: UnboundKey) -> Self {
458        Self { key }
459    }
460
461    /// Like [`OpeningKey::open_in_place()`], except it accepts an arbitrary nonce.
462    ///
463    /// `nonce` must be unique for every use of the key to open data.
464    #[inline]
465    pub fn open_in_place<'in_out, A>(
466        &self,
467        nonce: Nonce,
468        aad: Aad<A>,
469        in_out: &'in_out mut [u8],
470    ) -> Result<&'in_out mut [u8], error::Unspecified>
471    where
472        A: AsRef<[u8]>,
473    {
474        self.open_within(nonce, aad, in_out, 0..)
475    }
476
477    /// Like [`OpeningKey::open_within()`], except it accepts an arbitrary nonce.
478    ///
479    /// `nonce` must be unique for every use of the key to open data.
480    #[inline]
481    pub fn open_within<'in_out, A>(
482        &self,
483        nonce: Nonce,
484        aad: Aad<A>,
485        in_out: &'in_out mut [u8],
486        ciphertext_and_tag: RangeFrom<usize>,
487    ) -> Result<&'in_out mut [u8], error::Unspecified>
488    where
489        A: AsRef<[u8]>,
490    {
491        open_within_(&self.key, nonce, aad, in_out, ciphertext_and_tag)
492    }
493
494    /// Deprecated. Renamed to `seal_in_place_append_tag()`.
495    #[deprecated(note = "Renamed to `seal_in_place_append_tag`.")]
496    #[inline]
497    pub fn seal_in_place<A, InOut>(
498        &self,
499        nonce: Nonce,
500        aad: Aad<A>,
501        in_out: &mut InOut,
502    ) -> Result<(), error::Unspecified>
503    where
504        A: AsRef<[u8]>,
505        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
506    {
507        self.seal_in_place_append_tag(nonce, aad, in_out)
508    }
509
510    /// Like [`SealingKey::seal_in_place_append_tag()`], except it accepts an
511    /// arbitrary nonce.
512    ///
513    /// `nonce` must be unique for every use of the key to seal data.
514    #[inline]
515    pub fn seal_in_place_append_tag<A, InOut>(
516        &self,
517        nonce: Nonce,
518        aad: Aad<A>,
519        in_out: &mut InOut,
520    ) -> Result<(), error::Unspecified>
521    where
522        A: AsRef<[u8]>,
523        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
524    {
525        self.seal_in_place_separate_tag(nonce, aad, in_out.as_mut())
526            .map(|tag| in_out.extend(tag.as_ref()))
527    }
528
529    /// Like `SealingKey::seal_in_place_separate_tag()`, except it accepts an
530    /// arbitrary nonce.
531    ///
532    /// `nonce` must be unique for every use of the key to seal data.
533    #[inline]
534    pub fn seal_in_place_separate_tag<A>(
535        &self,
536        nonce: Nonce,
537        aad: Aad<A>,
538        in_out: &mut [u8],
539    ) -> Result<Tag, error::Unspecified>
540    where
541        A: AsRef<[u8]>,
542    {
543        seal_in_place_separate_tag_(&self.key, nonce, Aad::from(aad.as_ref()), in_out)
544    }
545
546    /// The key's AEAD algorithm.
547    #[inline]
548    pub fn algorithm(&self) -> &'static Algorithm {
549        &self.key.algorithm
550    }
551}
552
553impl core::fmt::Debug for LessSafeKey {
554    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
555        f.debug_struct("LessSafeKey")
556            .field("algorithm", self.algorithm())
557            .finish()
558    }
559}
560
561/// An AEAD Algorithm.
562pub struct Algorithm {
563    init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
564
565    seal: fn(
566        key: &KeyInner,
567        nonce: Nonce,
568        aad: Aad<&[u8]>,
569        in_out: &mut [u8],
570        cpu_features: cpu::Features,
571    ) -> Tag,
572    open: fn(
573        key: &KeyInner,
574        nonce: Nonce,
575        aad: Aad<&[u8]>,
576        in_prefix_len: usize,
577        in_out: &mut [u8],
578        cpu_features: cpu::Features,
579    ) -> Tag,
580
581    key_len: usize,
582    id: AlgorithmID,
583
584    /// Use `max_input_len!()` to initialize this.
585    // TODO: Make this `usize`.
586    max_input_len: u64,
587}
588
589const fn max_input_len(block_len: usize, overhead_blocks_per_nonce: usize) -> u64 {
590    // Each of our AEADs use a 32-bit block counter so the maximum is the
591    // largest input that will not overflow the counter.
592    ((1u64 << 32) - polyfill::u64_from_usize(overhead_blocks_per_nonce))
593        * polyfill::u64_from_usize(block_len)
594}
595
596impl Algorithm {
597    /// The length of the key.
598    #[inline(always)]
599    pub fn key_len(&self) -> usize {
600        self.key_len
601    }
602
603    /// The length of a tag.
604    ///
605    /// See also `MAX_TAG_LEN`.
606    #[inline(always)]
607    pub fn tag_len(&self) -> usize {
608        TAG_LEN
609    }
610
611    /// The length of the nonces.
612    #[inline(always)]
613    pub fn nonce_len(&self) -> usize {
614        NONCE_LEN
615    }
616}
617
618derive_debug_via_id!(Algorithm);
619
620#[derive(Debug, Eq, PartialEq)]
621enum AlgorithmID {
622    AES_128_GCM,
623    AES_256_GCM,
624    CHACHA20_POLY1305,
625}
626
627impl PartialEq for Algorithm {
628    fn eq(&self, other: &Self) -> bool {
629        self.id == other.id
630    }
631}
632
633impl Eq for Algorithm {}
634
635/// An authentication tag.
636#[must_use]
637#[repr(C)]
638pub struct Tag([u8; TAG_LEN]);
639
640impl AsRef<[u8]> for Tag {
641    fn as_ref(&self) -> &[u8] {
642        self.0.as_ref()
643    }
644}
645
646const MAX_KEY_LEN: usize = 32;
647
648// All the AEADs we support use 128-bit tags.
649const TAG_LEN: usize = BLOCK_LEN;
650
651/// The maximum length of a tag for the algorithms in this module.
652pub const MAX_TAG_LEN: usize = TAG_LEN;
653
654fn check_per_nonce_max_bytes(alg: &Algorithm, in_out_len: usize) -> Result<(), error::Unspecified> {
655    if polyfill::u64_from_usize(in_out_len) > alg.max_input_len {
656        return Err(error::Unspecified);
657    }
658    Ok(())
659}
660
661#[derive(Clone, Copy)]
662enum Direction {
663    Opening { in_prefix_len: usize },
664    Sealing,
665}
666
667mod aes;
668mod aes_gcm;
669mod block;
670mod chacha;
671mod chacha20_poly1305;
672pub mod chacha20_poly1305_openssh;
673mod counter;
674mod gcm;
675mod iv;
676mod nonce;
677mod poly1305;
678pub mod quic;
679mod shift;