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