1use crate::error::{warn, Error, Result};
6use base64::{
7 engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD},
8 Engine,
9};
10use rc_crypto::{
11 aead::{self, OpeningKey, SealingKey},
12 rand,
13};
14
15#[derive(Clone, PartialEq, Eq, Hash)]
16pub struct KeyBundle {
17 enc_key: Vec<u8>,
18 mac_key: Vec<u8>,
19}
20
21impl std::fmt::Debug for KeyBundle {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("KeyBundle").finish()
24 }
25}
26
27impl KeyBundle {
28 pub fn new(enc: Vec<u8>, mac: Vec<u8>) -> Result<KeyBundle> {
31 if enc.len() != 32 {
32 error_support::report_error!(
33 "sync15-key-bundle",
34 "Bad key length (enc_key): {} != 32",
35 enc.len()
36 );
37 return Err(Error::BadKeyLength("enc_key", enc.len(), 32));
38 }
39 if mac.len() != 32 {
40 error_support::report_error!(
41 "sync15-key-bundle",
42 "Bad key length (mac_key): {} != 32",
43 mac.len()
44 );
45 return Err(Error::BadKeyLength("mac_key", mac.len(), 32));
46 }
47 Ok(KeyBundle {
48 enc_key: enc,
49 mac_key: mac,
50 })
51 }
52
53 pub fn new_random() -> Result<KeyBundle> {
54 let mut buffer = [0u8; 64];
55 rand::fill(&mut buffer)?;
56 KeyBundle::from_ksync_bytes(&buffer)
57 }
58
59 pub fn from_ksync_bytes(ksync: &[u8]) -> Result<KeyBundle> {
60 if ksync.len() != 64 {
61 error_support::report_error!(
62 "sync15-key-bundle",
63 "Bad key length (kSync): {} != 64",
64 ksync.len()
65 );
66 return Err(Error::BadKeyLength("kSync", ksync.len(), 64));
67 }
68 Ok(KeyBundle {
69 enc_key: ksync[0..32].into(),
70 mac_key: ksync[32..64].into(),
71 })
72 }
73
74 pub fn from_ksync_base64(ksync: &str) -> Result<KeyBundle> {
75 let bytes = URL_SAFE_NO_PAD.decode(ksync)?;
76 KeyBundle::from_ksync_bytes(&bytes)
77 }
78
79 pub fn from_base64(enc: &str, mac: &str) -> Result<KeyBundle> {
80 let enc_bytes = STANDARD.decode(enc)?;
81 let mac_bytes = STANDARD.decode(mac)?;
82 KeyBundle::new(enc_bytes, mac_bytes)
83 }
84
85 #[inline]
86 pub fn encryption_key(&self) -> &[u8] {
87 &self.enc_key
88 }
89
90 #[inline]
91 pub fn hmac_key(&self) -> &[u8] {
92 &self.mac_key
93 }
94
95 #[inline]
96 pub fn to_b64_array(&self) -> [String; 2] {
97 [
98 STANDARD.encode(&self.enc_key),
99 STANDARD.encode(&self.mac_key),
100 ]
101 }
102
103 pub fn decrypt(&self, enc_base64: &str, iv_base64: &str, hmac_base16: &str) -> Result<String> {
106 let mut decoded_hmac = vec![0u8; 32];
110 if base16::decode_slice(hmac_base16, &mut decoded_hmac).is_err() {
111 warn!("Garbage HMAC verification string: contained non base16 characters");
112 return Err(Error::HmacMismatch);
113 }
114 let iv = STANDARD.decode(iv_base64)?;
115 let ciphertext_bytes = STANDARD.decode(enc_base64)?;
116 let key_bytes = [self.encryption_key(), self.hmac_key()].concat();
117 let key = OpeningKey::new(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes)?;
118 let nonce = aead::Nonce::try_assume_unique_for_key(
119 &aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256,
120 &iv,
121 )?;
122 let ciphertext_and_hmac = [ciphertext_bytes, decoded_hmac].concat();
123 let cleartext_bytes = aead::open(&key, nonce, aead::Aad::empty(), &ciphertext_and_hmac)?;
124 let cleartext = String::from_utf8(cleartext_bytes)?;
125 Ok(cleartext)
126 }
127
128 pub fn encrypt_bytes_with_iv(
130 &self,
131 cleartext_bytes: &[u8],
132 iv: &[u8],
133 ) -> Result<(String, String)> {
134 let key_bytes = [self.encryption_key(), self.hmac_key()].concat();
135 let key = SealingKey::new(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes)?;
136 let nonce =
137 aead::Nonce::try_assume_unique_for_key(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, iv)?;
138 let ciphertext_and_hmac = aead::seal(&key, nonce, aead::Aad::empty(), cleartext_bytes)?;
139 let ciphertext_len = ciphertext_and_hmac.len() - key.algorithm().tag_len();
140 let (ciphertext, hmac_signature) = ciphertext_and_hmac.split_at(ciphertext_len);
142 let enc_base64 = STANDARD.encode(ciphertext);
143 let hmac_base16 = base16::encode_lower(&hmac_signature);
144 Ok((enc_base64, hmac_base16))
145 }
146
147 pub fn encrypt_bytes_rand_iv(
150 &self,
151 cleartext_bytes: &[u8],
152 ) -> Result<(String, String, String)> {
153 let mut iv = [0u8; 16];
154 rand::fill(&mut iv)?;
155 let (enc_base64, hmac_base16) = self.encrypt_bytes_with_iv(cleartext_bytes, &iv)?;
156 let iv_base64 = STANDARD.encode(iv);
157 Ok((enc_base64, iv_base64, hmac_base16))
158 }
159
160 pub fn encrypt_with_iv(&self, cleartext: &str, iv: &[u8]) -> Result<(String, String)> {
161 self.encrypt_bytes_with_iv(cleartext.as_bytes(), iv)
162 }
163
164 pub fn encrypt_rand_iv(&self, cleartext: &str) -> Result<(String, String, String)> {
165 self.encrypt_bytes_rand_iv(cleartext.as_bytes())
166 }
167}
168
169#[cfg(test)]
170mod test {
171 use super::*;
172
173 const HMAC_B16: &str = "b1e6c18ac30deb70236bc0d65a46f7a4dce3b8b0e02cf92182b914e3afa5eebc";
174 const IV_B64: &str = "GX8L37AAb2FZJMzIoXlX8w==";
175 const HMAC_KEY_B64: &str = "MMntEfutgLTc8FlTLQFms8/xMPmCldqPlq/QQXEjx70=";
176 const ENC_KEY_B64: &str = "9K/wLdXdw+nrTtXo4ZpECyHFNr4d7aYHqeg3KW9+m6Q=";
177
178 const CIPHERTEXT_B64_PIECES: &[&str] = &[
179 "NMsdnRulLwQsVcwxKW9XwaUe7ouJk5Wn80QhbD80l0HEcZGCynh45qIbeYBik0lgcHbK",
180 "mlIxTJNwU+OeqipN+/j7MqhjKOGIlvbpiPQQLC6/ffF2vbzL0nzMUuSyvaQzyGGkSYM2",
181 "xUFt06aNivoQTvU2GgGmUK6MvadoY38hhW2LCMkoZcNfgCqJ26lO1O0sEO6zHsk3IVz6",
182 "vsKiJ2Hq6VCo7hu123wNegmujHWQSGyf8JeudZjKzfi0OFRRvvm4QAKyBWf0MgrW1F8S",
183 "FDnVfkq8amCB7NhdwhgLWbN+21NitNwWYknoEWe1m6hmGZDgDT32uxzWxCV8QqqrpH/Z",
184 "ggViEr9uMgoy4lYaWqP7G5WKvvechc62aqnsNEYhH26A5QgzmlNyvB+KPFvPsYzxDnSC",
185 "jOoRSLx7GG86wT59QZw=",
186 ];
187
188 const CLEARTEXT_B64_PIECES: &[&str] = &[
189 "eyJpZCI6IjVxUnNnWFdSSlpYciIsImhpc3RVcmkiOiJmaWxlOi8vL1VzZXJzL2phc29u",
190 "L0xpYnJhcnkvQXBwbGljYXRpb24lMjBTdXBwb3J0L0ZpcmVmb3gvUHJvZmlsZXMva3Nn",
191 "ZDd3cGsuTG9jYWxTeW5jU2VydmVyL3dlYXZlL2xvZ3MvIiwidGl0bGUiOiJJbmRleCBv",
192 "ZiBmaWxlOi8vL1VzZXJzL2phc29uL0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9G",
193 "aXJlZm94L1Byb2ZpbGVzL2tzZ2Q3d3BrLkxvY2FsU3luY1NlcnZlci93ZWF2ZS9sb2dz",
194 "LyIsInZpc2l0cyI6W3siZGF0ZSI6MTMxOTE0OTAxMjM3MjQyNSwidHlwZSI6MX1dfQ==",
195 ];
196
197 #[test]
198 fn test_decrypt() {
199 let key_bundle = KeyBundle::from_base64(ENC_KEY_B64, HMAC_KEY_B64).unwrap();
200 let ciphertext = CIPHERTEXT_B64_PIECES.join("");
201 let s = key_bundle.decrypt(&ciphertext, IV_B64, HMAC_B16).unwrap();
202
203 let cleartext =
204 String::from_utf8(STANDARD.decode(CLEARTEXT_B64_PIECES.join("")).unwrap()).unwrap();
205 assert_eq!(&cleartext, &s);
206 }
207
208 #[test]
209 fn test_encrypt() {
210 let key_bundle = KeyBundle::from_base64(ENC_KEY_B64, HMAC_KEY_B64).unwrap();
211 let iv = STANDARD.decode(IV_B64).unwrap();
212
213 let cleartext_bytes = STANDARD.decode(CLEARTEXT_B64_PIECES.join("")).unwrap();
214 let (enc_base64, _hmac_base16) = key_bundle
215 .encrypt_bytes_with_iv(&cleartext_bytes, &iv)
216 .unwrap();
217
218 let expect_ciphertext = CIPHERTEXT_B64_PIECES.join("");
219
220 assert_eq!(&enc_base64, &expect_ciphertext);
221
222 let (enc_base64_2, iv_base64_2, hmac_base16_2) =
223 key_bundle.encrypt_bytes_rand_iv(&cleartext_bytes).unwrap();
224 assert_ne!(&enc_base64_2, &expect_ciphertext);
225
226 let s = key_bundle
227 .decrypt(&enc_base64_2, &iv_base64_2, &hmac_base16_2)
228 .unwrap();
229 assert_eq!(&cleartext_bytes, &s.as_bytes());
230 }
231}