rc_crypto/aead/
aes_cbc.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
22use crate::{aead, digest, error::*, hmac};
23use base64::{engine::general_purpose::STANDARD, Engine};
24use nss::aes;
25
26/// AES-256 in CBC mode with HMAC-SHA256 tags and 128 bit nonces.
27/// This is a Sync 1.5 specific encryption scheme, do not use for new
28/// applications, there are better options out there nowadays.
29/// Important note: The HMAC tag verification is done against the
30/// base64 representation of the ciphertext.
31/// More details here: https://mozilla-services.readthedocs.io/en/latest/sync/storageformat5.html#record-encryption
32pub static LEGACY_SYNC_AES_256_CBC_HMAC_SHA256: aead::Algorithm = aead::Algorithm {
33    key_len: 64, // 32 bytes for the AES key, 32 bytes for the HMAC key.
34    tag_len: 32,
35    nonce_len: 128 / 8,
36    open,
37    seal,
38};
39
40// Warning: This does not run in constant time (which is fine for our usage).
41pub(crate) fn open(
42    key: &aead::Key,
43    nonce: aead::Nonce,
44    aad: &aead::Aad<'_>,
45    ciphertext_and_tag: &[u8],
46) -> Result<Vec<u8>> {
47    let ciphertext_len = ciphertext_and_tag
48        .len()
49        .checked_sub(key.algorithm().tag_len())
50        .ok_or(ErrorKind::InternalError)?;
51    let (ciphertext, hmac_signature) = ciphertext_and_tag.split_at(ciphertext_len);
52    let (aes_key, hmac_key_bytes) = extract_keys(key);
53    // 1. Tag (HMAC signature) check.
54    let hmac_key = hmac::VerificationKey::new(&digest::SHA256, hmac_key_bytes);
55    hmac::verify(
56        &hmac_key,
57        STANDARD.encode(ciphertext).as_bytes(),
58        hmac_signature,
59    )?;
60    // 2. Decryption.
61    aes_cbc(aes_key, nonce, aad, ciphertext, aead::Direction::Opening)
62}
63
64pub(crate) fn seal(
65    key: &aead::Key,
66    nonce: aead::Nonce,
67    aad: &aead::Aad<'_>,
68    plaintext: &[u8],
69) -> Result<Vec<u8>> {
70    let (aes_key, hmac_key_bytes) = extract_keys(key);
71    // 1. Encryption.
72    let mut ciphertext = aes_cbc(aes_key, nonce, aad, plaintext, aead::Direction::Sealing)?;
73    // 2. Tag (HMAC signature) generation.
74    let hmac_key = hmac::SigningKey::new(&digest::SHA256, hmac_key_bytes);
75    let signature = hmac::sign(&hmac_key, STANDARD.encode(&ciphertext).as_bytes())?;
76    ciphertext.extend(&signature.0.value);
77    Ok(ciphertext)
78}
79
80fn extract_keys(key: &aead::Key) -> (&[u8], &[u8]) {
81    // Always split at 32 since we only do AES 256 w/ HMAC 256 tag.
82    let (aes_key, hmac_key_bytes) = key.key_value.split_at(32);
83    (aes_key, hmac_key_bytes)
84}
85
86fn aes_cbc(
87    aes_key: &[u8],
88    nonce: aead::Nonce,
89    aad: &aead::Aad<'_>,
90    data: &[u8],
91    direction: aead::Direction,
92) -> Result<Vec<u8>> {
93    if !aad.0.is_empty() {
94        // CBC mode does not support AAD.
95        return Err(ErrorKind::InternalError.into());
96    }
97    Ok(aes::aes_cbc_crypt(
98        aes_key,
99        &nonce.0,
100        data,
101        direction.to_nss_operation(),
102    )?)
103}
104
105#[cfg(test)]
106mod test {
107    use super::*;
108    use nss::ensure_initialized;
109
110    // These are the test vectors used by the sync15 crate, but concatenated
111    // together rather than split into individual pieces.
112    const IV_B64: &str = "GX8L37AAb2FZJMzIoXlX8w==";
113
114    const KEY_B64: &str = "9K/wLdXdw+nrTtXo4ZpECyHFNr4d7aYHqeg3KW9+m6Qwye0R+62At\
115                           NzwWVMtAWazz/Ew+YKV2o+Wr9BBcSPHvQ==";
116
117    const CIPHERTEXT_AND_TAG_B64: &str =
118        "NMsdnRulLwQsVcwxKW9XwaUe7ouJk5Wn80QhbD80l0HEcZGCynh45qIbeYBik0lgcHbKm\
119         lIxTJNwU+OeqipN+/j7MqhjKOGIlvbpiPQQLC6/ffF2vbzL0nzMUuSyvaQzyGGkSYM2xU\
120         Ft06aNivoQTvU2GgGmUK6MvadoY38hhW2LCMkoZcNfgCqJ26lO1O0sEO6zHsk3IVz6vsK\
121         iJ2Hq6VCo7hu123wNegmujHWQSGyf8JeudZjKzfi0OFRRvvm4QAKyBWf0MgrW1F8SFDnV\
122         fkq8amCB7NhdwhgLWbN+21NitNwWYknoEWe1m6hmGZDgDT32uxzWxCV8QqqrpH/ZggViE\
123         r9uMgoy4lYaWqP7G5WKvvechc62aqnsNEYhH26A5QgzmlNyvB+KPFvPsYzxDnSCjOoRSL\
124         x7GG86wT59QZyx5sGKww3rcCNrwNZaRvek3OO4sOAs+SGCuRTjr6XuvA==";
125
126    const CLEARTEXT_B64: &str =
127        "eyJpZCI6IjVxUnNnWFdSSlpYciIsImhpc3RVcmkiOiJmaWxlOi8vL1VzZXJzL2phc29u\
128         L0xpYnJhcnkvQXBwbGljYXRpb24lMjBTdXBwb3J0L0ZpcmVmb3gvUHJvZmlsZXMva3Nn\
129         ZDd3cGsuTG9jYWxTeW5jU2VydmVyL3dlYXZlL2xvZ3MvIiwidGl0bGUiOiJJbmRleCBv\
130         ZiBmaWxlOi8vL1VzZXJzL2phc29uL0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9G\
131         aXJlZm94L1Byb2ZpbGVzL2tzZ2Q3d3BrLkxvY2FsU3luY1NlcnZlci93ZWF2ZS9sb2dz\
132         LyIsInZpc2l0cyI6W3siZGF0ZSI6MTMxOTE0OTAxMjM3MjQyNSwidHlwZSI6MX1dfQ==";
133
134    #[test]
135    fn test_decrypt() {
136        ensure_initialized();
137        let key_bytes = STANDARD.decode(KEY_B64).unwrap();
138        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
139        let ciphertext_and_tag = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
140
141        let iv = STANDARD.decode(IV_B64).unwrap();
142        let nonce =
143            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
144                .unwrap();
145        let cleartext_bytes = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap();
146
147        let expected_cleartext_bytes = STANDARD.decode(CLEARTEXT_B64).unwrap();
148        assert_eq!(&expected_cleartext_bytes, &cleartext_bytes);
149    }
150
151    #[test]
152    fn test_encrypt() {
153        ensure_initialized();
154        let key_bytes = STANDARD.decode(KEY_B64).unwrap();
155        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
156        let cleartext = STANDARD.decode(CLEARTEXT_B64).unwrap();
157
158        let iv = STANDARD.decode(IV_B64).unwrap();
159        let nonce =
160            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
161                .unwrap();
162        let ciphertext_bytes = seal(&key, nonce, &aead::Aad::empty(), &cleartext).unwrap();
163
164        let expected_ciphertext_bytes = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
165        assert_eq!(&expected_ciphertext_bytes, &ciphertext_bytes);
166    }
167
168    #[test]
169    fn test_roundtrip() {
170        ensure_initialized();
171        let key_bytes = STANDARD.decode(KEY_B64).unwrap();
172        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
173        let cleartext = STANDARD.decode(CLEARTEXT_B64).unwrap();
174
175        let iv = STANDARD.decode(IV_B64).unwrap();
176        let nonce =
177            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
178                .unwrap();
179        let ciphertext_bytes = seal(&key, nonce, &aead::Aad::empty(), &cleartext).unwrap();
180        let nonce =
181            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
182                .unwrap();
183        let roundtriped_cleartext_bytes =
184            open(&key, nonce, &aead::Aad::empty(), &ciphertext_bytes).unwrap();
185        assert_eq!(roundtriped_cleartext_bytes, cleartext);
186    }
187
188    #[test]
189    fn test_decrypt_fails_with_wrong_aes_key() {
190        ensure_initialized();
191        let mut key_bytes = STANDARD.decode(KEY_B64).unwrap();
192        key_bytes[1] = b'X';
193
194        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
195        let ciphertext_and_tag = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
196        let iv = STANDARD.decode(IV_B64).unwrap();
197        let nonce =
198            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
199                .unwrap();
200
201        let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
202        match err.kind() {
203            ErrorKind::NSSError(_) | ErrorKind::InternalError => {}
204            _ => panic!("unexpected error kind"),
205        }
206    }
207
208    #[test]
209    fn test_decrypt_fails_with_wrong_hmac_key() {
210        ensure_initialized();
211        let mut key_bytes = STANDARD.decode(KEY_B64).unwrap();
212        key_bytes[60] = b'X';
213
214        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
215        let ciphertext_and_tag = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
216        let iv = STANDARD.decode(IV_B64).unwrap();
217        let nonce =
218            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
219                .unwrap();
220
221        let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
222        match err.kind() {
223            ErrorKind::InternalError => {}
224            _ => panic!("unexpected error kind"),
225        }
226    }
227
228    #[test]
229    fn test_decrypt_fails_with_modified_ciphertext() {
230        ensure_initialized();
231        let key_bytes = STANDARD.decode(KEY_B64).unwrap();
232        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
233        let iv = STANDARD.decode(IV_B64).unwrap();
234        let nonce =
235            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
236                .unwrap();
237
238        let mut ciphertext_and_tag = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
239        ciphertext_and_tag[4] = b'Z';
240
241        let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
242        match err.kind() {
243            ErrorKind::InternalError => {}
244            _ => panic!("unexpected error kind"),
245        }
246    }
247
248    #[test]
249    fn test_decrypt_fails_with_modified_tag() {
250        ensure_initialized();
251        let key_bytes = STANDARD.decode(KEY_B64).unwrap();
252        let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
253        let iv = STANDARD.decode(IV_B64).unwrap();
254        let nonce =
255            aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
256                .unwrap();
257
258        let mut ciphertext_and_tag = STANDARD.decode(CIPHERTEXT_AND_TAG_B64).unwrap();
259        let end = ciphertext_and_tag.len();
260        ciphertext_and_tag[end - 4] = b'Z';
261
262        let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
263        match err.kind() {
264            ErrorKind::InternalError => {}
265            _ => panic!("unexpected error kind"),
266        }
267    }
268}