rc_crypto/
aead.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// This file contains code that was copied from the ring crate which is under
6// the ISC license, reproduced below:
7
8// Copyright 2015-2017 Brian Smith.
9
10// Permission to use, copy, modify, and/or distribute this software for any
11// purpose with or without fee is hereby granted, provided that the above
12// copyright notice and this permission notice appear in all copies.
13
14// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
15// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
17// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
22mod aes_cbc;
23mod aes_gcm;
24
25use crate::error::*;
26pub use aes_cbc::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256;
27pub use aes_gcm::{AES_128_GCM, AES_256_GCM};
28use nss::aes;
29
30pub fn open(
31    key: &OpeningKey,
32    nonce: Nonce,
33    aad: Aad<'_>,
34    ciphertext_and_tag: &[u8],
35) -> Result<Vec<u8>> {
36    (key.algorithm().open)(&key.key, nonce, &aad, ciphertext_and_tag)
37}
38
39pub fn seal(key: &SealingKey, nonce: Nonce, aad: Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>> {
40    (key.algorithm().seal)(&key.key, nonce, &aad, plaintext)
41}
42
43/// The additional authenticated data (AAD) for an opening or sealing
44/// operation. This data is authenticated but is **not** encrypted.
45/// This is a type-safe wrapper around the raw bytes designed to encourage
46/// correct use of the API.
47#[repr(transparent)]
48pub struct Aad<'a>(&'a [u8]);
49
50impl<'a> Aad<'a> {
51    /// Construct the `Aad` by borrowing a contiguous sequence of bytes.
52    #[inline]
53    pub fn from(aad: &'a [u8]) -> Self {
54        Aad(aad)
55    }
56}
57
58impl Aad<'static> {
59    /// Construct an empty `Aad`.
60    pub fn empty() -> Self {
61        Self::from(&[])
62    }
63}
64
65/// The nonce for an opening or sealing operation.
66/// This is a type-safe wrapper around the raw bytes designed to encourage
67/// correct use of the API.
68pub struct Nonce(Vec<u8>);
69
70impl Nonce {
71    #[inline]
72    pub fn try_assume_unique_for_key(algorithm: &'static Algorithm, value: &[u8]) -> Result<Self> {
73        if value.len() != algorithm.nonce_len() {
74            return Err(ErrorKind::InternalError.into());
75        }
76        Ok(Self(value.to_vec()))
77    }
78}
79
80pub struct OpeningKey {
81    key: Key,
82}
83
84impl OpeningKey {
85    /// Create a new opening key.
86    ///
87    /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
88    #[inline]
89    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
90        Ok(Self {
91            key: Key::new(algorithm, key_bytes)?,
92        })
93    }
94
95    /// The key's AEAD algorithm.
96    #[inline]
97    pub fn algorithm(&self) -> &'static Algorithm {
98        self.key.algorithm()
99    }
100}
101
102pub struct SealingKey {
103    key: Key,
104}
105
106impl SealingKey {
107    /// Create a new sealing key.
108    ///
109    /// `key_bytes` must be exactly `algorithm.key_len` bytes long.
110    #[inline]
111    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
112        Ok(Self {
113            key: Key::new(algorithm, key_bytes)?,
114        })
115    }
116
117    /// The key's AEAD algorithm.
118    #[inline]
119    pub fn algorithm(&self) -> &'static Algorithm {
120        self.key.algorithm()
121    }
122}
123
124/// `OpeningKey` and `SealingKey` are type-safety wrappers around `Key`.
125pub(crate) struct Key {
126    key_value: Vec<u8>,
127    algorithm: &'static Algorithm,
128}
129
130impl Key {
131    fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
132        if key_bytes.len() != algorithm.key_len() {
133            return Err(ErrorKind::InternalError.into());
134        }
135        Ok(Key {
136            key_value: key_bytes.to_vec(),
137            algorithm,
138        })
139    }
140
141    #[inline]
142    pub fn algorithm(&self) -> &'static Algorithm {
143        self.algorithm
144    }
145}
146
147// An AEAD algorithm.
148#[allow(clippy::type_complexity)]
149pub struct Algorithm {
150    tag_len: usize,
151    key_len: usize,
152    nonce_len: usize,
153    open: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, ciphertext_and_tag: &[u8]) -> Result<Vec<u8>>,
154    seal: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>>,
155}
156
157impl Algorithm {
158    /// The length of the key.
159    #[inline]
160    pub const fn key_len(&self) -> usize {
161        self.key_len
162    }
163
164    /// The length of a tag.
165    #[inline]
166    pub const fn tag_len(&self) -> usize {
167        self.tag_len
168    }
169
170    /// The length of the nonces.
171    #[inline]
172    pub const fn nonce_len(&self) -> usize {
173        self.nonce_len
174    }
175}
176
177pub(crate) enum Direction {
178    Opening,
179    Sealing,
180}
181
182impl Direction {
183    fn to_nss_operation(&self) -> aes::Operation {
184        match self {
185            Direction::Opening => aes::Operation::Decrypt,
186            Direction::Sealing => aes::Operation::Encrypt,
187        }
188    }
189}
190
191#[cfg(test)]
192mod test {
193    use super::*;
194    use nss::ensure_initialized;
195
196    static ALL_ALGORITHMS: &[&Algorithm] = &[
197        &LEGACY_SYNC_AES_256_CBC_HMAC_SHA256,
198        &AES_128_GCM,
199        &AES_256_GCM,
200    ];
201    static ALL_ALGORITHMS_THAT_SUPPORT_AAD: &[&Algorithm] = &[&AES_128_GCM, &AES_256_GCM];
202
203    #[test]
204    fn test_roundtrip() {
205        ensure_initialized();
206        for algorithm in ALL_ALGORITHMS {
207            let mut cleartext_bytes = vec![0u8; 127];
208            crate::rand::fill(&mut cleartext_bytes).unwrap();
209
210            let mut key_bytes = vec![0u8; algorithm.key_len()];
211            crate::rand::fill(&mut key_bytes).unwrap();
212
213            let nonce_bytes = vec![0u8; algorithm.nonce_len()];
214
215            let key = SealingKey::new(algorithm, &key_bytes).unwrap();
216            let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
217            let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &cleartext_bytes).unwrap();
218
219            let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
220            let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
221            let roundtriped_cleartext_bytes =
222                open(&key, nonce, Aad::empty(), &ciphertext_bytes).unwrap();
223            assert_eq!(roundtriped_cleartext_bytes, cleartext_bytes);
224        }
225    }
226
227    #[test]
228    fn test_cant_open_with_mismatched_key() {
229        ensure_initialized();
230        let mut key_bytes_1 = vec![0u8; AES_256_GCM.key_len()];
231        crate::rand::fill(&mut key_bytes_1).unwrap();
232
233        let mut key_bytes_2 = vec![0u8; AES_128_GCM.key_len()];
234        crate::rand::fill(&mut key_bytes_2).unwrap();
235
236        let nonce_bytes = vec![0u8; AES_256_GCM.nonce_len()];
237
238        let key = SealingKey::new(&AES_256_GCM, &key_bytes_1).unwrap();
239        let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
240        let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
241
242        let key = OpeningKey::new(&AES_128_GCM, &key_bytes_2).unwrap();
243        let nonce = Nonce::try_assume_unique_for_key(&AES_128_GCM, &nonce_bytes).unwrap();
244        let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
245        assert!(result.is_err());
246    }
247
248    #[test]
249    fn test_cant_open_modified_ciphertext() {
250        ensure_initialized();
251        for algorithm in ALL_ALGORITHMS {
252            let mut key_bytes = vec![0u8; algorithm.key_len()];
253            crate::rand::fill(&mut key_bytes).unwrap();
254
255            let nonce_bytes = vec![0u8; algorithm.nonce_len()];
256
257            let key = SealingKey::new(algorithm, &key_bytes).unwrap();
258            let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
259            let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
260
261            for i in 0..ciphertext_bytes.len() {
262                let mut modified_ciphertext = ciphertext_bytes.clone();
263                modified_ciphertext[i] = modified_ciphertext[i].wrapping_add(1);
264
265                let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
266                let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
267                let result = open(&key, nonce, Aad::empty(), &modified_ciphertext);
268                assert!(result.is_err());
269            }
270        }
271    }
272
273    #[test]
274    fn test_cant_open_with_incorrect_associated_data() {
275        ensure_initialized();
276        for algorithm in ALL_ALGORITHMS_THAT_SUPPORT_AAD {
277            let mut key_bytes = vec![0u8; algorithm.key_len()];
278            crate::rand::fill(&mut key_bytes).unwrap();
279
280            let nonce_bytes = vec![0u8; algorithm.nonce_len()];
281
282            let key = SealingKey::new(algorithm, &key_bytes).unwrap();
283            let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
284            let ciphertext_bytes = seal(&key, nonce, Aad::from(&[1, 2, 3]), &[0u8; 0]).unwrap();
285
286            let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
287            let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
288            let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
289            assert!(result.is_err());
290
291            let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
292            let result = open(&key, nonce, Aad::from(&[2, 3, 4]), &ciphertext_bytes);
293            assert!(result.is_err());
294        }
295    }
296
297    #[test]
298    fn test_cant_use_incorrectly_sized_key() {
299        ensure_initialized();
300        for algorithm in ALL_ALGORITHMS {
301            let key_bytes = vec![0u8; algorithm.key_len() - 1];
302            let result = Key::new(algorithm, &key_bytes);
303            assert!(result.is_err());
304
305            let key_bytes = vec![0u8; algorithm.key_len() + 1];
306            let result = Key::new(algorithm, &key_bytes);
307            assert!(result.is_err());
308        }
309    }
310
311    #[test]
312    fn test_cant_use_incorrectly_sized_nonce() {
313        ensure_initialized();
314        for algorithm in ALL_ALGORITHMS {
315            let nonce_bytes = vec![0u8; algorithm.nonce_len() - 1];
316            let result = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes);
317            assert!(result.is_err());
318
319            let nonce_bytes = vec![0u8; algorithm.nonce_len() + 1];
320            let result = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes);
321            assert!(result.is_err());
322        }
323    }
324}