1use crate::{aead, digest, error::*, hmac};
23use base64::{engine::general_purpose::STANDARD, Engine};
24use nss::aes;
25
26pub static LEGACY_SYNC_AES_256_CBC_HMAC_SHA256: aead::Algorithm = aead::Algorithm {
33 key_len: 64, tag_len: 32,
35 nonce_len: 128 / 8,
36 open,
37 seal,
38};
39
40pub(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 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 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 let mut ciphertext = aes_cbc(aes_key, nonce, aad, plaintext, aead::Direction::Sealing)?;
73 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 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 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 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}