1 // Copyright 2015-2021 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]: https://eprint.iacr.org/2000/025.pdf
22 //! [`crypto.cipher.AEAD`]: https://golang.org/pkg/crypto/cipher/#AEAD
23 
24 use super::{Aad, Algorithm, BoundKey, LessSafeKey, NonceSequence, UnboundKey};
25 use crate::error;
26 use core::ops::RangeFrom;
27 
28 /// An AEAD key for authenticating and decrypting ("opening"), bound to a nonce
29 /// sequence.
30 ///
31 /// Intentionally not `Clone` or `Copy` since cloning would allow duplication
32 /// of the nonce sequence.
33 pub struct OpeningKey<N: NonceSequence> {
34     key: LessSafeKey,
35     nonce_sequence: N,
36 }
37 
38 impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
new(key: UnboundKey, nonce_sequence: N) -> Self39     fn new(key: UnboundKey, nonce_sequence: N) -> Self {
40         Self {
41             key: key.into_inner(),
42             nonce_sequence,
43         }
44     }
45 
46     #[inline]
algorithm(&self) -> &'static Algorithm47     fn algorithm(&self) -> &'static Algorithm {
48         self.key.algorithm()
49     }
50 }
51 
52 impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error>53     fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
54         self.key.fmt_debug("OpeningKey", f)
55     }
56 }
57 
58 impl<N: NonceSequence> OpeningKey<N> {
59     /// Authenticates and decrypts (“opens”) data in place.
60     ///
61     /// `aad` is the additional authenticated data (AAD), if any.
62     ///
63     /// On input, `in_out` must be the ciphertext followed by the tag. When
64     /// `open_in_place()` returns `Ok(plaintext)`, the input ciphertext
65     /// has been overwritten by the plaintext; `plaintext` will refer to the
66     /// plaintext without the tag.
67     ///
68     /// When `open_in_place()` returns `Err(..)`, `in_out` may have been
69     /// overwritten in an unspecified way.
70     #[inline]
open_in_place<'in_out, A>( &mut self, aad: Aad<A>, in_out: &'in_out mut [u8], ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>,71     pub fn open_in_place<'in_out, A>(
72         &mut self,
73         aad: Aad<A>,
74         in_out: &'in_out mut [u8],
75     ) -> Result<&'in_out mut [u8], error::Unspecified>
76     where
77         A: AsRef<[u8]>,
78     {
79         self.key
80             .open_in_place(self.nonce_sequence.advance()?, aad, in_out)
81     }
82 
83     /// Authenticates and decrypts (“opens”) data in place, with a shift.
84     ///
85     /// `aad` is the additional authenticated data (AAD), if any.
86     ///
87     /// On input, `in_out[ciphertext_and_tag]` must be the ciphertext followed
88     /// by the tag. When `open_within()` returns `Ok(plaintext)`, the plaintext
89     /// will be at `in_out[0..plaintext.len()]`. In other words, the following
90     /// two code fragments are equivalent for valid values of
91     /// `ciphertext_and_tag`, except `open_within` will often be more efficient:
92     ///
93     ///
94     /// ```skip
95     /// let plaintext = key.open_within(aad, in_out, cipertext_and_tag)?;
96     /// ```
97     ///
98     /// ```skip
99     /// let ciphertext_and_tag_len = in_out[ciphertext_and_tag].len();
100     /// in_out.copy_within(ciphertext_and_tag, 0);
101     /// let plaintext = key.open_in_place(aad, &mut in_out[..ciphertext_and_tag_len])?;
102     /// ```
103     ///
104     /// Similarly, `key.open_within(aad, in_out, 0..)` is equivalent to
105     /// `key.open_in_place(aad, in_out)`.
106     ///
107     ///  When `open_in_place()` returns `Err(..)`, `in_out` may have been
108     /// overwritten in an unspecified way.
109     ///
110     /// The shifting feature is useful in the case where multiple packets are
111     /// being reassembled in place. Consider this example where the peer has
112     /// sent the message “Split stream reassembled in place” split into
113     /// three sealed packets:
114     ///
115     /// ```ascii-art
116     ///                 Packet 1                  Packet 2                 Packet 3
117     /// Input:  [Header][Ciphertext][Tag][Header][Ciphertext][Tag][Header][Ciphertext][Tag]
118     ///                      |         +--------------+                        |
119     ///               +------+   +-----+    +----------------------------------+
120     ///               v          v          v
121     /// Output: [Plaintext][Plaintext][Plaintext]
122     ///        “Split stream reassembled in place”
123     /// ```
124     ///
125     /// This reassembly can be accomplished with three calls to `open_within()`.
126     #[inline]
open_within<'in_out, A>( &mut self, aad: Aad<A>, in_out: &'in_out mut [u8], ciphertext_and_tag: RangeFrom<usize>, ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>,127     pub fn open_within<'in_out, A>(
128         &mut self,
129         aad: Aad<A>,
130         in_out: &'in_out mut [u8],
131         ciphertext_and_tag: RangeFrom<usize>,
132     ) -> Result<&'in_out mut [u8], error::Unspecified>
133     where
134         A: AsRef<[u8]>,
135     {
136         self.key.open_within(
137             self.nonce_sequence.advance()?,
138             aad,
139             in_out,
140             ciphertext_and_tag,
141         )
142     }
143 }
144