Skip to content

Commit

Permalink
Upgrade aes 0.7 -> 0.8 (#247)
Browse files Browse the repository at this point in the history
* Port attachment cipher to aes 8

* Port envelope decryption to aes 8

* Port provisioning cipher to aes 8

* Use Iso7816 padding from aes8

* Port account management cipher to aes 8

* Port profile cipher to aes gcm 0.10

* Undo renaming of crates

* Replace try_into by GenericArray::from_slice

* replace GenericArray::from_slice by just into()

* remove alloc from hmac calc
  • Loading branch information
boxdot committed Jan 3, 2024
1 parent 0a7987e commit 1858813
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 75 deletions.
7 changes: 4 additions & 3 deletions libsignal-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ readme = "../README.md"
libsignal-protocol = { git = "https://github.com/signalapp/libsignal", tag = "v0.32.0" }
zkgroup = { git = "https://github.com/signalapp/libsignal", tag = "v0.32.0" }

aes = { version = "0.7", features = ["ctr"] }
aes-gcm = "0.9"
aes = "0.8"
aes-gcm = "0.10"
cbc = "0.1"
ctr = "0.9"
async-trait = "0.1"
base64 = "0.13"
bincode = "1.3"
block-modes = "0.8"
bytes = "1"
chrono = { version = "0.4", features = ["serde", "clock"], default-features = false }
derivative = "2.2"
Expand Down
43 changes: 23 additions & 20 deletions libsignal-service/src/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::time::SystemTime;

use aes::cipher::generic_array::GenericArray;
use aes::cipher::{NewCipher, StreamCipher};
use aes::Aes256Ctr;
use aes::cipher::{KeyIvInit, StreamCipher as _};
use hmac::digest::Output;
use hmac::{Hmac, Mac};
use libsignal_protocol::{
kem, GenericSignedPreKey, IdentityKeyStore, KeyPair, KyberPreKeyRecord,
Expand Down Expand Up @@ -33,6 +32,8 @@ use crate::{
utils::serde_base64,
};

type Aes256Ctr128BE = ctr::Ctr128BE<aes::Aes256>;

pub struct AccountManager<Service> {
service: Service,
profile_key: Option<ProfileKey>,
Expand Down Expand Up @@ -524,11 +525,11 @@ impl<Service: PushService> AccountManager<Service> {
fn calculate_hmac256(
mac_key: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, ServiceError> {
) -> Result<Output<Hmac<Sha256>>, ServiceError> {
let mut mac = Hmac::<Sha256>::new_from_slice(mac_key)
.map_err(|_| ServiceError::MacError)?;
mac.update(ciphertext);
Ok(mac.finalize().into_bytes().to_vec())
Ok(mac.finalize().into_bytes())
}

pub fn encrypt_device_name<R: rand::Rng + rand::CryptoRng>(
Expand All @@ -544,17 +545,16 @@ pub fn encrypt_device_name<R: rand::Rng + rand::CryptoRng>(
.calculate_agreement(identity_public)?;

let key1 = calculate_hmac256(&master_secret, b"auth")?;
let mut synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
synthetic_iv.truncate(16);
let synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
let synthetic_iv = &synthetic_iv[..16];

let key2 = calculate_hmac256(&master_secret, b"cipher")?;
let cipher_key = calculate_hmac256(&key2, &synthetic_iv)?;
let cipher_key = calculate_hmac256(&key2, synthetic_iv)?;

let mut ciphertext = plaintext;
let mut cipher = Aes256Ctr::new(
GenericArray::from_slice(&cipher_key),
GenericArray::from_slice(&[0u8; 16]),
);

const IV: [u8; 16] = [0; 16];
let mut cipher = Aes256Ctr128BE::new(&cipher_key, &IV.into());
cipher.apply_keystream(&mut ciphertext);

let device_name = DeviceName {
Expand All @@ -581,24 +581,27 @@ pub fn decrypt_device_name(
return Err(ServiceError::InvalidDeviceName);
};

let synthetic_iv: [u8; 16] = synthetic_iv[..synthetic_iv.len().min(16)]
.try_into()
.map_err(|_| ServiceError::MacError)?;

let ephemeral_public = PublicKey::deserialize(ephemeral_public)?;

let master_secret = private_key.calculate_agreement(&ephemeral_public)?;
let key2 = calculate_hmac256(&master_secret, b"cipher")?;
let cipher_key = calculate_hmac256(&key2, synthetic_iv)?;
let cipher_key = calculate_hmac256(&key2, &synthetic_iv)?;

let mut plaintext = ciphertext.to_vec();
let mut cipher = Aes256Ctr::new(
GenericArray::from_slice(&cipher_key),
GenericArray::from_slice(&[0u8; 16]),
);
const IV: [u8; 16] = [0; 16];
let mut cipher =
Aes256Ctr128BE::new(cipher_key.as_slice().into(), &IV.into());
cipher.apply_keystream(&mut plaintext);

let key1 = calculate_hmac256(&master_secret, b"auth")?;
let mut our_synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
our_synthetic_iv.truncate(16);
let our_synthetic_iv = calculate_hmac256(&key1, &plaintext)?;
let our_synthetic_iv = &our_synthetic_iv[..16];

if synthetic_iv != &our_synthetic_iv {
if synthetic_iv != our_synthetic_iv {
Err(ServiceError::MacError)
} else {
Ok(String::from_utf8_lossy(&plaintext).to_string())
Expand Down
17 changes: 9 additions & 8 deletions libsignal-service/src/attachment_cipher.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use aes::Aes256;
use block_modes::{block_padding::Pkcs7, BlockMode, Cbc};
use aes::cipher::block_padding::Pkcs7;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use hmac::{Hmac, Mac};
use sha2::Sha256;

type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;

#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum AttachmentCipherError {
#[error("MAC verification error")]
Expand Down Expand Up @@ -30,12 +33,11 @@ pub fn encrypt_in_place(iv: [u8; 16], key: [u8; 64], plaintext: &mut Vec<u8>) {
// Pad with zeroes for padding
plaintext.extend(&[0u8; 16]);

let cipher = Cbc::<Aes256, Pkcs7>::new_from_slices(aes_half, &iv)
.expect("fixed length key material");
let cipher = Aes256CbcEnc::new(aes_half.into(), &iv.into());

let buffer = plaintext;
let ciphertext_slice = cipher
.encrypt(&mut buffer[16..], plaintext_len)
.encrypt_padded_mut::<Pkcs7>(&mut buffer[16..], plaintext_len)
.expect("encrypted ciphertext");
let ciphertext_len = ciphertext_slice.len();
// Correct length for padding
Expand Down Expand Up @@ -71,11 +73,10 @@ pub fn decrypt_in_place(

let (iv, buffer) = buffer.split_at_mut(16);

let cipher = Cbc::<Aes256, Pkcs7>::new_from_slices(aes_half, iv)
.expect("fixed length key material");
let cipher = Aes256CbcDec::new(aes_half.into(), (&*iv).into());

let plaintext_slice = cipher
.decrypt(buffer)
.decrypt_padded_mut::<Pkcs7>(buffer)
.map_err(|_| AttachmentCipherError::PaddingError)?;

let plaintext_len = plaintext_slice.len();
Expand Down
10 changes: 3 additions & 7 deletions libsignal-service/src/cipher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{convert::TryFrom, time::SystemTime};

use block_modes::block_padding::{Iso7816, Padding};
use aes::cipher::block_padding::{Iso7816, RawPadding};
use libsignal_protocol::{
group_decrypt, message_decrypt_prekey, message_decrypt_signal,
message_encrypt, process_sender_key_distribution_message,
Expand Down Expand Up @@ -358,11 +358,7 @@ fn add_padding(version: u32, contents: &[u8]) -> Result<Vec<u8>, ServiceError> {

let mut buffer = vec![0u8; message_length_with_padding];
buffer[..message_length].copy_from_slice(contents);
Iso7816::pad_block(&mut buffer, message_length).map_err(|e| {
ServiceError::InvalidFrameError {
reason: format!("Invalid message padding: {:?}", e),
}
})?;
Iso7816::raw_pad(&mut buffer, message_length);
Ok(buffer)
}
}
Expand All @@ -386,7 +382,7 @@ fn strip_padding_version(

#[allow(clippy::comparison_chain)]
fn strip_padding(contents: &mut Vec<u8>) -> Result<(), ServiceError> {
let new_length = Iso7816::unpad(contents)
let new_length = Iso7816::raw_unpad(contents)
.map_err(|e| ServiceError::InvalidFrameError {
reason: format!("Invalid message padding: {:?}", e),
})?
Expand Down
37 changes: 31 additions & 6 deletions libsignal-service/src/envelope.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::convert::{TryFrom, TryInto};

use aes::cipher::block_padding::Pkcs7;
use aes::cipher::{BlockDecryptMut, KeyIvInit};
use prost::Message;
use uuid::Uuid;

Expand Down Expand Up @@ -65,15 +67,15 @@ impl Envelope {
return Err(ServiceError::MacError);
}

use aes::Aes256;
// libsignal-service-java uses Pkcs5,
// but that should not matter.
// https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding
use block_modes::{block_padding::Pkcs7, BlockMode, Cbc};
let cipher = Cbc::<Aes256, Pkcs7>::new_from_slices(aes_key, iv)
.expect("initalization of CBC/AES/PKCS7");
let cipher =
cbc::Decryptor::<aes::Aes256>::new(aes_key.into(), iv.into());
let input = &input[CIPHERTEXT_OFFSET..(input.len() - MAC_SIZE)];
let input = cipher.decrypt_vec(input).expect("decryption");
let input = cipher
.decrypt_padded_vec_mut::<Pkcs7>(input)
.expect("decryption");

log::trace!("Envelope::decrypt: decrypted, decoding");

Expand Down Expand Up @@ -213,6 +215,29 @@ mod tests {
];

let signaling_key = [0u8; 52];
let _ = Envelope::decrypt(&body, &signaling_key, true).unwrap();
let envelope = Envelope::decrypt(&body, &signaling_key, true).unwrap();
assert_eq!(envelope.server_timestamp(), 1594373582421);
assert_eq!(envelope.timestamp(), 1594373580977);
assert_eq!(
envelope.content(),
[
51, 10, 33, 5, 239, 254, 183, 191, 204, 223, 85, 150, 43, 192,
240, 57, 46, 189, 153, 7, 48, 17, 9, 166, 185, 157, 205, 181,
66, 235, 99, 221, 114, 58, 187, 117, 16, 76, 24, 0, 34, 160, 1,
85, 61, 73, 83, 99, 213, 160, 109, 122, 125, 204, 137, 178,
237, 146, 87, 183, 107, 33, 213, 234, 64, 152, 132, 122, 173,
25, 33, 4, 65, 20, 134, 117, 62, 116, 80, 151, 18, 132, 187,
101, 235, 208, 74, 78, 214, 66, 59, 71, 171, 124, 167, 217,
157, 36, 194, 156, 12, 50, 239, 185, 230, 253, 38, 107, 106,
149, 194, 39, 214, 35, 245, 58, 216, 250, 225, 150, 170, 26,
241, 153, 133, 173, 197, 194, 27, 127, 56, 77, 119, 242, 26,
252, 168, 61, 221, 44, 76, 128, 69, 27, 203, 6, 173, 193, 179,
69, 27, 243, 36, 185, 181, 157, 41, 23, 72, 113, 40, 209, 46,
189, 63, 167, 156, 148, 118, 76, 153, 91, 40, 179, 180, 245,
193, 123, 180, 47, 115, 220, 191, 148, 245, 116, 32, 194, 232,
55, 13, 0, 217, 52, 116, 21, 48, 244, 17, 222, 26, 240, 31,
236, 199, 237, 94, 255, 93, 137, 192,
]
);
}
}
32 changes: 12 additions & 20 deletions libsignal-service/src/profile_cipher.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use aes_gcm::{
aead::{
generic_array::typenum::U32, generic_array::GenericArray, Aead,
AeadInPlace,
},
Aes256Gcm, NewAead,
};
use rand::Rng;
use std::convert::TryInto;

use aes_gcm::{aead::Aead, AeadCore, AeadInPlace, Aes256Gcm, KeyInit};
use zkgroup::profiles::ProfileKey;

use crate::profile_name::ProfileName;
Expand Down Expand Up @@ -87,27 +82,22 @@ impl ProfileCipher {
self.profile_key
}

fn get_key(&self) -> GenericArray<u8, U32> {
GenericArray::from(self.profile_key.get_bytes())
}

fn pad_and_encrypt(
&self,
mut bytes: Vec<u8>,
padding_brackets: &[usize],
) -> Result<Vec<u8>, ProfileCipherError> {
let _len = pad_plaintext(&mut bytes, padding_brackets)?;

let cipher = Aes256Gcm::new(&self.get_key());
let nonce: [u8; 12] = rand::thread_rng().gen();
let nonce = GenericArray::from_slice(&nonce);
let cipher = Aes256Gcm::new(&self.profile_key.get_bytes().into());
let nonce = Aes256Gcm::generate_nonce(rand::thread_rng());

cipher
.encrypt_in_place(nonce, b"", &mut bytes)
.encrypt_in_place(&nonce, b"", &mut bytes)
.map_err(|_| ProfileCipherError::EncryptionError)?;

let mut concat = Vec::with_capacity(nonce.len() + bytes.len());
concat.extend_from_slice(nonce);
concat.extend_from_slice(&nonce);
concat.extend_from_slice(&bytes);
Ok(concat)
}
Expand All @@ -117,11 +107,13 @@ impl ProfileCipher {
bytes: impl AsRef<[u8]>,
) -> Result<Vec<u8>, ProfileCipherError> {
let bytes = bytes.as_ref();
let nonce = GenericArray::from_slice(&bytes[0..12]);
let cipher = Aes256Gcm::new(&self.get_key());
let nonce: [u8; 12] = bytes[0..12]
.try_into()
.expect("fixed length nonce material");
let cipher = Aes256Gcm::new(&self.profile_key.get_bytes().into());

let mut plaintext = cipher
.decrypt(nonce, &bytes[12..])
.decrypt(&nonce.into(), &bytes[12..])
.map_err(|_| ProfileCipherError::EncryptionError)?;

// Unpad
Expand Down
19 changes: 9 additions & 10 deletions libsignal-service/src/provisioning/cipher.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::fmt::{self, Debug};

use aes::cipher::block_padding::Pkcs7;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use aes::Aes256;
use block_modes::{block_padding::Pkcs7, BlockMode, Cbc};
use bytes::Bytes;
use hmac::{Hmac, Mac};
use libsignal_protocol::{KeyPair, PublicKey};
Expand Down Expand Up @@ -101,9 +102,8 @@ impl ProvisioningCipher {
let mac_key = &shared_secrets[32..];
let iv: [u8; IV_LENGTH] = rng.gen();

let cipher = Cbc::<Aes256, Pkcs7>::new_from_slices(aes_key, &iv)
.expect("initalization of CBC/AES/PKCS7");
let ciphertext = cipher.encrypt_vec(&msg);
let cipher = cbc::Encryptor::<Aes256>::new(aes_key.into(), &iv.into());
let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(&msg);
let mut mac = Hmac::<Sha256>::new_from_slice(mac_key)
.expect("HMAC can take any size key");
mac.update(&[VERSION]);
Expand Down Expand Up @@ -176,13 +176,12 @@ impl ProvisioningCipher {
// libsignal-service-java uses Pkcs5,
// but that should not matter.
// https://crypto.stackexchange.com/questions/9043/what-is-the-difference-between-pkcs5-padding-and-pkcs7-padding
let cipher = Cbc::<Aes256, Pkcs7>::new_from_slices(parts1, iv)
.expect("initalization of CBC/AES/PKCS7");
let input = cipher.decrypt_vec(cipher_text).map_err(|e| {
ProvisioningError::InvalidData {
let cipher = cbc::Decryptor::<Aes256>::new(parts1.into(), iv.into());
let input = cipher
.decrypt_padded_vec_mut::<Pkcs7>(cipher_text)
.map_err(|e| ProvisioningError::InvalidData {
reason: format!("CBC/Padding error: {:?}", e).into(),
}
})?;
})?;

Ok(prost::Message::decode(Bytes::from(input))?)
}
Expand Down
2 changes: 1 addition & 1 deletion libsignal-service/src/push_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ pub trait PushService: MaybeSend {
"/v1/devices/link",
&[],
HttpAuthOverride::Identified(http_auth),
&link_request,
link_request,
)
.await
}
Expand Down

0 comments on commit 1858813

Please sign in to comment.