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
24use super::{Aad, Algorithm, BoundKey, LessSafeKey, NonceSequence, UnboundKey};
25use crate::error;
26use 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.
33pub struct OpeningKey<N: NonceSequence> {
34 key: LessSafeKey,
35 nonce_sequence: N,
36}
37
38impl<N: NonceSequence> BoundKey<N> for OpeningKey<N> {
39 fn new(key: UnboundKey, nonce_sequence: N) -> Self {
40 Self {
41 key: key.into_inner(),
42 nonce_sequence,
43 }
44 }
45
46 #[inline]
47 fn algorithm(&self) -> &'static Algorithm {
48 self.key.algorithm()
49 }
50}
51
52impl<N: NonceSequence> core::fmt::Debug for OpeningKey<N> {
53 fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
54 self.key.fmt_debug(type_name:"OpeningKey", f)
55 }
56}
57
58impl<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]
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]
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