libsignal_protocol/
crypto.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use std::result::Result;
7
8use aes::Aes256;
9use aes::cipher::{KeyIvInit, StreamCipher};
10use hmac::{Hmac, Mac};
11use sha2::Sha256;
12use subtle::ConstantTimeEq;
13
14#[derive(Debug)]
15pub(crate) enum EncryptionError {
16    /// The key or IV is the wrong length.
17    BadKeyOrIv,
18}
19
20#[derive(Debug)]
21pub(crate) enum DecryptionError {
22    /// The key or IV is the wrong length.
23    BadKeyOrIv,
24    /// Either the input is malformed, or the MAC doesn't match on decryption.
25    ///
26    /// These cases should not be distinguished; message corruption can cause either problem.
27    BadCiphertext(&'static str),
28}
29
30fn aes_256_ctr_encrypt(ptext: &[u8], key: &[u8]) -> Result<Vec<u8>, EncryptionError> {
31    let key: [u8; 32] = key.try_into().map_err(|_| EncryptionError::BadKeyOrIv)?;
32
33    let zero_nonce = [0u8; 16];
34    let mut cipher = ctr::Ctr32BE::<Aes256>::new(key[..].into(), zero_nonce[..].into());
35
36    let mut ctext = ptext.to_vec();
37    cipher.apply_keystream(&mut ctext);
38    Ok(ctext)
39}
40
41fn aes_256_ctr_decrypt(ctext: &[u8], key: &[u8]) -> Result<Vec<u8>, DecryptionError> {
42    aes_256_ctr_encrypt(ctext, key).map_err(|e| match e {
43        EncryptionError::BadKeyOrIv => DecryptionError::BadKeyOrIv,
44    })
45}
46
47pub(crate) fn hmac_sha256(key: &[u8], input: &[u8]) -> [u8; 32] {
48    let mut hmac =
49        Hmac::<Sha256>::new_from_slice(key).expect("HMAC-SHA256 should accept any size key");
50    hmac.update(input);
51    hmac.finalize().into_bytes().into()
52}
53
54pub(crate) fn aes256_ctr_hmacsha256_encrypt(
55    msg: &[u8],
56    cipher_key: &[u8],
57    mac_key: &[u8],
58) -> Result<Vec<u8>, EncryptionError> {
59    let mut ctext = aes_256_ctr_encrypt(msg, cipher_key)?;
60    let mac = hmac_sha256(mac_key, &ctext);
61    ctext.extend_from_slice(&mac[..10]);
62    Ok(ctext)
63}
64
65pub(crate) fn aes256_ctr_hmacsha256_decrypt(
66    ctext: &[u8],
67    cipher_key: &[u8],
68    mac_key: &[u8],
69) -> Result<Vec<u8>, DecryptionError> {
70    if ctext.len() < 10 {
71        return Err(DecryptionError::BadCiphertext("truncated ciphertext"));
72    }
73    let ptext_len = ctext.len() - 10;
74    let our_mac = hmac_sha256(mac_key, &ctext[..ptext_len]);
75    let same: bool = our_mac[..10].ct_eq(&ctext[ptext_len..]).into();
76    if !same {
77        return Err(DecryptionError::BadCiphertext("MAC verification failed"));
78    }
79    aes_256_ctr_decrypt(&ctext[..ptext_len], cipher_key)
80}
81
82#[cfg(test)]
83mod test {
84    use const_str::hex;
85
86    use super::*;
87
88    #[test]
89    fn aes_ctr_test() {
90        let key = hex!("603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4");
91        let ptext = [0u8; 35];
92
93        let ctext = aes_256_ctr_encrypt(&ptext, &key).expect("valid key");
94        assert_eq!(
95            hex::encode(ctext),
96            "e568f68194cf76d6174d4cc04310a85491151e5d0b7a1f1bc0d7acd0ae3e51e4170e23"
97        );
98    }
99}