1use aes::cipher::block_padding::Pkcs7;
2use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
3use hmac::{Hmac, Mac};
4use sha2::Sha256;
5
6type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
7type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
8
9#[derive(thiserror::Error, Debug, PartialEq, Eq)]
10pub enum AttachmentCipherError {
11 #[error("MAC verification error")]
12 MacError,
13 #[error("Padding verification error")]
14 PaddingError,
15}
16
17#[tracing::instrument(skip(iv, key, plaintext))]
22pub fn encrypt_in_place(iv: [u8; 16], key: [u8; 64], plaintext: &mut Vec<u8>) {
23 let aes_half = &key[..32];
24 let mac_half = &key[32..];
25
26 let plaintext_len = plaintext.len();
27 plaintext.reserve(plaintext.len() + 16 + 16);
28
29 plaintext.extend(&[0u8; 16]);
31 plaintext.copy_within(..plaintext_len, 16);
32 plaintext[0..16].copy_from_slice(&iv);
33
34 plaintext.extend(&[0u8; 16]);
36
37 let cipher = Aes256CbcEnc::new(aes_half.into(), &iv.into());
38
39 let buffer = plaintext;
40 let ciphertext_slice = cipher
41 .encrypt_padded_mut::<Pkcs7>(&mut buffer[16..], plaintext_len)
42 .expect("encrypted ciphertext");
43 let ciphertext_len = ciphertext_slice.len();
44 buffer.truncate(16 + ciphertext_len);
46
47 let mut mac = Hmac::<Sha256>::new_from_slice(mac_half)
49 .expect("fixed length key material");
50 mac.update(buffer);
51 buffer.extend(mac.finalize().into_bytes());
52}
53
54#[tracing::instrument(skip(key, ciphertext))]
58pub fn decrypt_in_place(
59 key: [u8; 64],
60 ciphertext: &mut Vec<u8>,
61) -> Result<(), AttachmentCipherError> {
62 let aes_half = &key[..32];
63 let mac_half = &key[32..];
64
65 let ciphertext_len = ciphertext.len();
66
67 let (buffer, their_mac) = ciphertext.split_at_mut(ciphertext_len - 32);
68
69 let mut mac = Hmac::<Sha256>::new_from_slice(mac_half)
71 .expect("fixed length key material");
72 mac.update(buffer);
73 mac.verify_slice(their_mac)
74 .map_err(|_| AttachmentCipherError::MacError)?;
75
76 let (iv, buffer) = buffer.split_at_mut(16);
77
78 let cipher = Aes256CbcDec::new(aes_half.into(), (&*iv).into());
79
80 let plaintext_slice = cipher
81 .decrypt_padded_mut::<Pkcs7>(buffer)
82 .map_err(|_| AttachmentCipherError::PaddingError)?;
83
84 let plaintext_len = plaintext_slice.len();
85
86 ciphertext.copy_within(16..(plaintext_len + 16), 0);
88 ciphertext.truncate(plaintext_len);
89
90 Ok(())
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 use rand::prelude::*;
98
99 #[test]
100 fn attachment_encrypt_decrypt() -> Result<(), AttachmentCipherError> {
101 let mut key = [0u8; 64];
102 let mut iv = [0u8; 16];
103 rand::thread_rng().fill_bytes(&mut key);
104 rand::thread_rng().fill_bytes(&mut iv);
105
106 let plaintext = b"Peter Parker";
107 let mut buf = Vec::from(plaintext as &[u8]);
108 encrypt_in_place(iv, key, &mut buf);
109 assert_ne!(&buf, &plaintext);
110 decrypt_in_place(key, &mut buf)?;
111 assert_eq!(&buf, &plaintext);
112 Ok(())
113 }
114
115 #[test]
116 fn attachment_encrypt_decrypt_empty() -> Result<(), AttachmentCipherError> {
117 let mut key = [0u8; 64];
118 let mut iv = [0u8; 16];
119 rand::thread_rng().fill_bytes(&mut key);
120 rand::thread_rng().fill_bytes(&mut iv);
121 let plaintext = b"";
122 let mut buf = Vec::from(plaintext as &[u8]);
123 encrypt_in_place(iv, key, &mut buf);
124 assert_ne!(&buf, &plaintext);
125 decrypt_in_place(key, &mut buf)?;
126 assert_eq!(&buf, &plaintext);
127 Ok(())
128 }
129
130 #[test]
131 fn attachment_encrypt_decrypt_bad_key() {
132 let mut key = [0u8; 64];
133 let mut iv = [0u8; 16];
134 rand::thread_rng().fill_bytes(&mut key);
135 rand::thread_rng().fill_bytes(&mut iv);
136 let plaintext = b"Peter Parker";
137 let mut buf = Vec::from(plaintext as &[u8]);
138 encrypt_in_place(iv, key, &mut buf);
139
140 rand::thread_rng().fill_bytes(&mut key);
142 assert_eq!(
143 decrypt_in_place(key, &mut buf).unwrap_err(),
144 AttachmentCipherError::MacError
145 );
146 assert_ne!(&buf, &plaintext);
147 }
148
149 #[test]
150 fn know_answer_test_attachment() -> Result<(), AttachmentCipherError> {
151 let mut ciphertext = include!("kat.bin.rs");
152 let key_material = [
153 52, 102, 97, 87, 153, 192, 64, 116, 93, 96, 57, 110, 6, 197, 208,
154 85, 49, 249, 154, 137, 116, 124, 112, 107, 8, 158, 48, 4, 8, 66,
155 173, 5, 28, 16, 199, 226, 234, 38, 69, 167, 163, 34, 107, 164, 15,
156 118, 101, 146, 34, 213, 85, 164, 110, 83, 129, 245, 62, 44, 158,
157 78, 205, 62, 153, 108,
158 ];
159
160 decrypt_in_place(key_material, &mut ciphertext)?;
161 ciphertext.truncate(32);
163 assert_eq!(ciphertext, b"test for libsignal-service-rust\n");
164 Ok(())
165 }
166}