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
15use crate::{
16 constant_time, cpu,
17 error::{self, InputTooLongError},
18 hkdf,
19};
20use core::ops::RangeFrom;
21
22use super::{
23 aes, aes_gcm, chacha20_poly1305,
24 nonce::{Nonce, NONCE_LEN},
25 overlapping::{IndexError, Overlapping},
26 Aad, KeyInner, Tag, TAG_LEN,
27};
28
29impl hkdf::KeyType for &'static Algorithm {
30 #[inline]
31 fn len(&self) -> usize {
32 self.key_len()
33 }
34}
35
36/// An AEAD Algorithm.
37pub struct Algorithm {
38 init: fn(key: &[u8], cpu_features: cpu::Features) -> Result<KeyInner, error::Unspecified>,
39
40 seal: fn(
41 key: &KeyInner,
42 nonce: Nonce,
43 aad: Aad<&[u8]>,
44 in_out: &mut [u8],
45 cpu_features: cpu::Features,
46 ) -> Result<Tag, error::Unspecified>,
47 open: fn(
48 key: &KeyInner,
49 nonce: Nonce,
50 aad: Aad<&[u8]>,
51 in_out: &mut [u8],
52 src: RangeFrom<usize>,
53 cpu_features: cpu::Features,
54 ) -> Result<Tag, error::Unspecified>,
55
56 key_len: usize,
57 id: AlgorithmID,
58}
59
60impl Algorithm {
61 /// The length of the key.
62 #[inline(always)]
63 pub fn key_len(&self) -> usize {
64 self.key_len
65 }
66
67 /// The length of a tag.
68 ///
69 /// See also `MAX_TAG_LEN`.
70 #[inline(always)]
71 pub fn tag_len(&self) -> usize {
72 TAG_LEN
73 }
74
75 /// The length of the nonces.
76 #[inline(always)]
77 pub fn nonce_len(&self) -> usize {
78 NONCE_LEN
79 }
80
81 pub(super) fn new_key(
82 &self,
83 key_bytes: &[u8],
84 cpu_features: cpu::Features,
85 ) -> Result<KeyInner, error::Unspecified> {
86 (self.init)(key_bytes, cpu_features)
87 }
88
89 pub(super) fn open_within<'io>(
90 &self,
91 key: &KeyInner,
92 nonce: Nonce,
93 aad: Aad<&[u8]>,
94 received_tag: Tag,
95 in_out: &'io mut [u8],
96 src: RangeFrom<usize>,
97 cpu_features: cpu::Features,
98 ) -> Result<&'io mut [u8], error::Unspecified> {
99 let ciphertext_len = in_out.get(src.clone()).ok_or(error::Unspecified)?.len();
100
101 let Tag(calculated_tag) = (self.open)(key, nonce, aad, in_out, src, cpu_features)?;
102
103 if constant_time::verify_slices_are_equal(calculated_tag.as_ref(), received_tag.as_ref())
104 .is_err()
105 {
106 // Zero out the plaintext so that it isn't accidentally leaked or used
107 // after verification fails. It would be safest if we could check the
108 // tag before decrypting, but some `open` implementations interleave
109 // authentication with decryption for performance.
110 for b in &mut in_out[..ciphertext_len] {
111 *b = 0;
112 }
113 return Err(error::Unspecified);
114 }
115
116 // `ciphertext_len` is also the plaintext length.
117 Ok(&mut in_out[..ciphertext_len])
118 }
119
120 #[inline]
121 pub(super) fn seal(
122 &self,
123 key: &KeyInner,
124 nonce: Nonce,
125 aad: Aad<&[u8]>,
126 in_out: &mut [u8],
127 cpu_features: cpu::Features,
128 ) -> Result<Tag, error::Unspecified> {
129 (self.seal)(key, nonce, aad, in_out, cpu_features)
130 }
131}
132
133derive_debug_via_id!(Algorithm);
134
135#[derive(Debug, Eq, PartialEq)]
136pub(super) enum AlgorithmID {
137 AES_128_GCM,
138 AES_256_GCM,
139 CHACHA20_POLY1305,
140}
141
142impl PartialEq for Algorithm {
143 fn eq(&self, other: &Self) -> bool {
144 self.id == other.id
145 }
146}
147
148impl Eq for Algorithm {}
149
150/// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
151pub static AES_128_GCM: Algorithm = Algorithm {
152 key_len: aes::AES_128_KEY_LEN,
153 init: aes_gcm_init_128,
154 seal: aes_gcm_seal,
155 open: aes_gcm_open,
156 id: AlgorithmID::AES_128_GCM,
157};
158
159/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
160pub static AES_256_GCM: Algorithm = Algorithm {
161 key_len: aes::AES_256_KEY_LEN,
162 init: aes_gcm_init_256,
163 seal: aes_gcm_seal,
164 open: aes_gcm_open,
165 id: AlgorithmID::AES_256_GCM,
166};
167
168fn aes_gcm_init_128(
169 key: &[u8],
170 cpu_features: cpu::Features,
171) -> Result<KeyInner, error::Unspecified> {
172 let key: &[u8; 16] = key.try_into().map_err(|_| error::Unspecified)?;
173 Ok(KeyInner::AesGcm(aes_gcm::Key::new(
174 key:aes::KeyBytes::AES_128(key),
175 cpu_features,
176 )?))
177}
178
179fn aes_gcm_init_256(
180 key: &[u8],
181 cpu_features: cpu::Features,
182) -> Result<KeyInner, error::Unspecified> {
183 let key: &[u8; 32] = key.try_into().map_err(|_| error::Unspecified)?;
184 Ok(KeyInner::AesGcm(aes_gcm::Key::new(
185 key:aes::KeyBytes::AES_256(key),
186 cpu_features,
187 )?))
188}
189
190fn aes_gcm_seal(
191 key: &KeyInner,
192 nonce: Nonce,
193 aad: Aad<&[u8]>,
194 in_out: &mut [u8],
195 _cpu_features: cpu::Features,
196) -> Result<Tag, error::Unspecified> {
197 let key: &Key = match key {
198 KeyInner::AesGcm(key: &Key) => key,
199 _ => unreachable!(),
200 };
201 aes_gcm::seal(key, nonce, aad, in_out)
202}
203
204pub(super) fn aes_gcm_open(
205 key: &KeyInner,
206 nonce: Nonce,
207 aad: Aad<&[u8]>,
208 in_out: &mut [u8],
209 src: RangeFrom<usize>,
210 _cpu_features: cpu::Features,
211) -> Result<Tag, error::Unspecified> {
212 let key: &Key = match key {
213 KeyInner::AesGcm(key: &Key) => key,
214 _ => unreachable!(),
215 };
216 aes_gcm::open(key, nonce, aad, in_out, src)
217}
218
219/// ChaCha20-Poly1305 as described in [RFC 8439].
220///
221/// The keys are 256 bits long and the nonces are 96 bits long.
222///
223/// [RFC 8439]: https://tools.ietf.org/html/rfc8439
224pub static CHACHA20_POLY1305: Algorithm = Algorithm {
225 key_len: chacha20_poly1305::KEY_LEN,
226 init: chacha20_poly1305_init,
227 seal: chacha20_poly1305_seal,
228 open: chacha20_poly1305_open,
229 id: AlgorithmID::CHACHA20_POLY1305,
230};
231
232/// Copies |key| into |ctx_buf|.
233fn chacha20_poly1305_init(
234 key: &[u8],
235 _cpu_features: cpu::Features,
236) -> Result<KeyInner, error::Unspecified> {
237 let key: [u8; chacha20_poly1305::KEY_LEN] = key.try_into()?;
238 Ok(KeyInner::ChaCha20Poly1305(chacha20_poly1305::Key::new(key)))
239}
240
241fn chacha20_poly1305_seal(
242 key: &KeyInner,
243 nonce: Nonce,
244 aad: Aad<&[u8]>,
245 in_out: &mut [u8],
246 cpu_features: cpu::Features,
247) -> Result<Tag, error::Unspecified> {
248 let key: &Key = match key {
249 KeyInner::ChaCha20Poly1305(key: &Key) => key,
250 _ => unreachable!(),
251 };
252 chacha20_poly1305::seal(key, nonce, aad, in_out, cpu_features)
253 .map_err(op:error::erase::<InputTooLongError>)
254}
255
256fn chacha20_poly1305_open(
257 key: &KeyInner,
258 nonce: Nonce,
259 aad: Aad<&[u8]>,
260 in_out: &mut [u8],
261 src: RangeFrom<usize>,
262 cpu_features: cpu::Features,
263) -> Result<Tag, error::Unspecified> {
264 let key: &Key = match key {
265 KeyInner::ChaCha20Poly1305(key: &Key) => key,
266 _ => unreachable!(),
267 };
268 let in_out: Overlapping<'_, u8> = Overlapping::new(in_out, src).map_err(op:error::erase::<IndexError>)?;
269 chacha20_poly1305::open(key, nonce, aad, in_out, cpu_features)
270 .map_err(op:error::erase::<InputTooLongError>)
271}
272