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 | use crate::{ |
16 | constant_time, cpu, |
17 | error::{self, InputTooLongError}, |
18 | hkdf, |
19 | }; |
20 | use core::ops::RangeFrom; |
21 | |
22 | use super::{ |
23 | aes, aes_gcm, chacha20_poly1305, |
24 | nonce::{Nonce, NONCE_LEN}, |
25 | overlapping::{IndexError, Overlapping}, |
26 | Aad, KeyInner, Tag, TAG_LEN, |
27 | }; |
28 | |
29 | impl hkdf::KeyType for &'static Algorithm { |
30 | #[inline ] |
31 | fn len(&self) -> usize { |
32 | self.key_len() |
33 | } |
34 | } |
35 | |
36 | /// An AEAD Algorithm. |
37 | pub 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 | |
60 | impl 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 | |
133 | derive_debug_via_id!(Algorithm); |
134 | |
135 | #[derive (Debug, Eq, PartialEq)] |
136 | pub(super) enum AlgorithmID { |
137 | AES_128_GCM, |
138 | AES_256_GCM, |
139 | CHACHA20_POLY1305, |
140 | } |
141 | |
142 | impl PartialEq for Algorithm { |
143 | fn eq(&self, other: &Self) -> bool { |
144 | self.id == other.id |
145 | } |
146 | } |
147 | |
148 | impl Eq for Algorithm {} |
149 | |
150 | /// AES-128 in GCM mode with 128-bit tags and 96 bit nonces. |
151 | pub 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. |
160 | pub 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 | |
168 | fn 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 | |
179 | fn 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 | |
190 | fn 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 | |
204 | pub(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 |
224 | pub 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|. |
233 | fn 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 | |
241 | fn 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 | |
256 | fn 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 | |