libsignal_rust/
crypto.rs

1use cbc::{cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit}};
2use hmac::{Hmac, Mac};
3use sha2::{Sha256, Sha512, Digest};
4use subtle::ConstantTimeEq;
5
6type Aes256Cbc = cbc::Encryptor<aes::Aes256>;
7type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
8type HmacSha256 = Hmac<Sha256>;
9
10/// AES-256-CBC encryption
11pub fn encrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
12    if key.len() != 32 {
13        return Err("Key must be 32 bytes".into());
14    }
15    if iv.len() != 16 {
16        return Err("IV must be 16 bytes".into());
17    }
18
19    let cipher = Aes256Cbc::new(key.into(), iv.into());
20    Ok(cipher.encrypt_padded_vec_mut::<cbc::cipher::block_padding::Pkcs7>(data))
21}
22
23/// AES-256-CBC decryption
24pub fn decrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
25    if key.len() != 32 {
26        return Err("Key must be 32 bytes".into());
27    }
28    if iv.len() != 16 {
29        return Err("IV must be 16 bytes".into());
30    }
31
32    let cipher = Aes256CbcDec::new(key.into(), iv.into());
33    cipher.decrypt_padded_vec_mut::<cbc::cipher::block_padding::Pkcs7>(data)
34        .map_err(|e| format!("Decryption error: {:?}", e).into())
35}
36
37/// HMAC-SHA256 calculation
38pub fn calculate_mac(key: &[u8], data: &[u8]) -> Vec<u8> {
39    let mut mac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
40    mac.update(data);
41    mac.finalize().into_bytes().to_vec()
42}
43
44/// SHA-512 hashing
45pub fn hash(data: &[u8]) -> Vec<u8> {
46    let mut hasher = Sha512::new();
47    hasher.update(data);
48    hasher.finalize().to_vec()
49}
50
51/// Calculate SHA-512 hash (alias for hash function)
52pub fn calculate_sha512(data: &[u8]) -> Vec<u8> {
53    hash(data)
54}
55
56/// HKDF implementation (RFC 5869) - specific implementation that returns the first 3 32-byte chunks
57pub fn derive_secrets(input: &[u8], salt: &[u8], info: &[u8], chunks: Option<usize>) -> Result<Vec<Vec<u8>>, Box<dyn std::error::Error + Send + Sync>> {
58    if salt.len() != 32 {
59        return Err("Got salt of incorrect length".into());
60    }
61
62    let chunks = chunks.unwrap_or(3);
63    if chunks < 1 || chunks > 3 {
64        return Err("Chunks must be between 1 and 3".into());
65    }
66
67    let prk = calculate_mac(salt, input);
68
69    let mut results = Vec::new();
70    let mut info_array = vec![0u8; info.len() + 1 + 32];
71    info_array[32..32 + info.len()].copy_from_slice(info);
72    let len = info_array.len();
73    info_array[len - 1] = 1;
74
75    let first = calculate_mac(&prk, &info_array[32..]);
76    results.push(first.clone());
77
78    if chunks > 1 {
79        info_array[..32].copy_from_slice(&first);
80        let len = info_array.len();
81        info_array[len - 1] = 2;
82        let second = calculate_mac(&prk, &info_array[..]);
83        results.push(second.clone());
84
85        if chunks > 2 {
86            info_array[..32].copy_from_slice(&second);
87            let len = info_array.len();
88            info_array[len - 1] = 3;
89            let third = calculate_mac(&prk, &info_array[..]);
90            results.push(third);
91        }
92    }
93
94    Ok(results)
95}
96
97/// Verify HMAC
98pub fn verify_mac(data: &[u8], key: &[u8], mac: &[u8], length: usize) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
99    let calculated_mac = calculate_mac(key, data);
100    let calculated_mac = &calculated_mac[..length];
101    
102    if mac.len() != length || calculated_mac.len() != length {
103        return Err("Bad MAC length".into());
104    }
105    
106    if calculated_mac.ct_eq(mac).into() {
107        Ok(())
108    } else {
109        Err("Bad MAC".into())
110    }
111}