libsignal_protocol/
crypto.rs1use std::result::Result;
7
8use aes::Aes256;
9use aes::cipher::{KeyIvInit, StreamCipher};
10use hmac::{Hmac, KeyInit as _, Mac as _};
11use sha2::Sha256;
12use subtle::ConstantTimeEq;
13
14#[derive(Debug)]
15pub(crate) enum EncryptionError {
16 BadKeyOrIv,
18}
19
20#[derive(Debug)]
21pub(crate) enum DecryptionError {
22 BadKeyOrIv,
24 BadCiphertext(&'static str),
28}
29
30fn aes_256_ctr_encrypt(ptext: &[u8], key: &[u8]) -> Result<Vec<u8>, EncryptionError> {
31 let _trace = libsignal_debug::trace_block!("aes256_ctr_encrypt");
32 let key: [u8; 32] = key.try_into().map_err(|_| EncryptionError::BadKeyOrIv)?;
33
34 let zero_nonce = [0u8; 16];
35 let mut cipher = ctr::Ctr32BE::<Aes256>::new(&key.into(), &zero_nonce.into());
36
37 let mut ctext = ptext.to_vec();
38 cipher.apply_keystream(&mut ctext);
39 Ok(ctext)
40}
41
42fn aes_256_ctr_decrypt(ctext: &[u8], key: &[u8]) -> Result<Vec<u8>, DecryptionError> {
43 aes_256_ctr_encrypt(ctext, key).map_err(|e| match e {
44 EncryptionError::BadKeyOrIv => DecryptionError::BadKeyOrIv,
45 })
46}
47
48pub(crate) fn hmac_sha256(key: &[u8], input: &[u8]) -> [u8; 32] {
49 let _trace = libsignal_debug::trace_block!("hmac_sha256");
50 let mut hmac =
51 Hmac::<Sha256>::new_from_slice(key).expect("HMAC-SHA256 should accept any size key");
52 hmac.update(input);
53 hmac.finalize().into_bytes().into()
54}
55
56pub(crate) fn aes256_ctr_hmacsha256_encrypt(
57 msg: &[u8],
58 cipher_key: &[u8],
59 mac_key: &[u8],
60) -> Result<Vec<u8>, EncryptionError> {
61 let mut ctext = aes_256_ctr_encrypt(msg, cipher_key)?;
62 let mac = hmac_sha256(mac_key, &ctext);
63 ctext.extend_from_slice(&mac[..10]);
64 Ok(ctext)
65}
66
67pub(crate) fn aes256_ctr_hmacsha256_decrypt(
68 ctext: &[u8],
69 cipher_key: &[u8],
70 mac_key: &[u8],
71) -> Result<Vec<u8>, DecryptionError> {
72 let (ctext, their_mac) = ctext
73 .split_last_chunk::<10>()
74 .ok_or(DecryptionError::BadCiphertext("truncated ciphertext"))?;
75 let our_mac = hmac_sha256(mac_key, ctext);
76 let same: bool = our_mac[..10].ct_eq(their_mac).into();
77 if !same {
78 return Err(DecryptionError::BadCiphertext("MAC verification failed"));
79 }
80 aes_256_ctr_decrypt(ctext, cipher_key)
81}
82
83#[cfg(test)]
84mod test {
85 use const_str::hex;
86
87 use super::*;
88
89 #[test]
90 fn aes_ctr_test() {
91 let key = hex!("603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4");
92 let ptext = [0u8; 35];
93
94 let ctext = aes_256_ctr_encrypt(&ptext, &key).expect("valid key");
95 assert_eq!(
96 hex::encode(ctext),
97 "e568f68194cf76d6174d4cc04310a85491151e5d0b7a1f1bc0d7acd0ae3e51e4170e23"
98 );
99 }
100}