Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade aes 0.7 -> 0.8 #247

Merged
merged 12 commits into from
Jan 3, 2024
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 All @@ -10,7 +11,7 @@
use sha2::Sha256;

pub use crate::proto::{
ProvisionEnvelope, ProvisionMessage, ProvisioningVersion,

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-hyper, Rust beta)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-hyper, Rust beta)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-actix, Rust beta)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-actix, Rust beta)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-hyper, Rust nightly)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-hyper, Rust nightly)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-actix, Rust nightly)

unused import: `ProvisioningVersion`

Check warning on line 14 in libsignal-service/src/provisioning/cipher.rs

View workflow job for this annotation

GitHub Actions / Build (libsignal-service-actix, Rust nightly)

unused import: `ProvisioningVersion`
};

use crate::{
Expand Down Expand Up @@ -101,9 +102,8 @@
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 @@
// 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
Loading