From 37961656d1598d9b8e07385b74ddfef5fcf63cd9 Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Mon, 6 Jan 2025 18:04:59 +0200 Subject: [PATCH] Remove signaling key Quick and dirty rebase of https://github.com/whisperfish/libsignal-service-rs/pull/277 --- src/configuration.rs | 13 ++-- src/envelope.rs | 134 ------------------------------------- src/lib.rs | 4 +- src/messagepipe.rs | 21 +++--- src/profile_cipher.rs | 5 +- src/provisioning/cipher.rs | 12 ++-- src/receiver.rs | 2 +- src/utils.rs | 37 ---------- 8 files changed, 27 insertions(+), 201 deletions(-) diff --git a/src/configuration.rs b/src/configuration.rs index 19a1b9423..4304b205c 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,18 +1,16 @@ use core::fmt; use std::{borrow::Cow, collections::HashMap, str::FromStr}; -use crate::utils::BASE64_RELAXED; +use crate::{ + push_service::{HttpAuth, DEFAULT_DEVICE_ID}, + utils::BASE64_RELAXED, +}; use base64::prelude::*; use libsignal_protocol::PublicKey; use serde::{Deserialize, Serialize}; use url::Url; use zkgroup::ServerPublicParams; -use crate::{ - envelope::{CIPHER_KEY_SIZE, MAC_KEY_SIZE}, - push_service::{HttpAuth, DEFAULT_DEVICE_ID}, -}; - #[derive(Clone)] pub struct ServiceConfiguration { service_url: Url, @@ -24,15 +22,12 @@ pub struct ServiceConfiguration { pub zkgroup_server_public_params: ServerPublicParams, } -pub type SignalingKey = [u8; CIPHER_KEY_SIZE + MAC_KEY_SIZE]; - #[derive(Clone)] pub struct ServiceCredentials { pub aci: Option, pub pni: Option, pub phonenumber: phonenumber::PhoneNumber, pub password: Option, - pub signaling_key: Option, pub device_id: Option, } diff --git a/src/envelope.rs b/src/envelope.rs index ae19e0a1d..5b7c26f3d 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -1,72 +1,8 @@ -use aes::cipher::block_padding::Pkcs7; -use aes::cipher::{BlockDecryptMut, KeyIvInit}; use libsignal_protocol::ServiceId; -use prost::Message; - -use crate::{configuration::SignalingKey, push_service::ServiceError}; pub use crate::proto::Envelope; impl Envelope { - #[tracing::instrument(skip(input, signaling_key), fields(signaling_key_present = signaling_key.is_some(), input_size = input.len()))] - pub fn decrypt( - input: &[u8], - signaling_key: Option<&SignalingKey>, - is_signaling_key_encrypted: bool, - ) -> Result { - if !is_signaling_key_encrypted { - tracing::trace!("Envelope::decrypt: not encrypted"); - Ok(Envelope::decode(input)?) - } else { - let signaling_key = signaling_key - .expect("signaling_key required to decrypt envelopes"); - tracing::trace!("Envelope::decrypt: decrypting"); - if input.len() < VERSION_LENGTH - || input[VERSION_OFFSET] != SUPPORTED_VERSION - { - return Err(ServiceError::InvalidFrame { - reason: "unsupported signaling cryptogram version", - }); - } - - let aes_key = &signaling_key[..CIPHER_KEY_SIZE]; - let mac_key = &signaling_key[CIPHER_KEY_SIZE..]; - let mac = &input[(input.len() - MAC_SIZE)..]; - let input_for_mac = &input[..(input.len() - MAC_SIZE)]; - let iv = &input[IV_OFFSET..(IV_OFFSET + IV_LENGTH)]; - debug_assert_eq!(mac_key.len(), MAC_KEY_SIZE); - debug_assert_eq!(aes_key.len(), CIPHER_KEY_SIZE); - debug_assert_eq!(iv.len(), IV_LENGTH); - - // Verify MAC - use hmac::{Hmac, Mac}; - use sha2::Sha256; - let mut verifier = Hmac::::new_from_slice(mac_key) - .expect("Hmac can take any size key"); - verifier.update(input_for_mac); - // XXX: possible timing attack, but we need the bytes for a - // truncated view... - let our_mac = verifier.finalize().into_bytes(); - if &our_mac[..MAC_SIZE] != mac { - return Err(ServiceError::MacError); - } - - // 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::Decryptor::::new(aes_key.into(), iv.into()); - let input = &input[CIPHERTEXT_OFFSET..(input.len() - MAC_SIZE)]; - let input = cipher - .decrypt_padded_vec_mut::(input) - .expect("decryption"); - - tracing::trace!("Envelope::decrypt: decrypted, decoding"); - - Ok(Envelope::decode(&input as &[u8])?) - } - } - pub fn is_unidentified_sender(&self) -> bool { self.r#type() == crate::proto::envelope::Type::UnidentifiedSender } @@ -112,73 +48,3 @@ impl Envelope { } } } - -pub(crate) const SUPPORTED_VERSION: u8 = 1; -pub(crate) const CIPHER_KEY_SIZE: usize = 32; -pub(crate) const MAC_KEY_SIZE: usize = 20; -pub(crate) const MAC_SIZE: usize = 10; - -pub(crate) const VERSION_OFFSET: usize = 0; -pub(crate) const VERSION_LENGTH: usize = 1; -pub(crate) const IV_OFFSET: usize = VERSION_OFFSET + VERSION_LENGTH; -pub(crate) const IV_LENGTH: usize = 16; -pub(crate) const CIPHERTEXT_OFFSET: usize = IV_OFFSET + IV_LENGTH; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn decrypt_envelope() { - // This is a real message, reencrypted with the zero-key. - let body = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 32, 12, 100, - 26, 157, 130, 210, 254, 174, 87, 45, 238, 126, 68, 39, 188, 171, - 156, 16, 10, 138, 233, 73, 202, 52, 125, 102, 121, 182, 71, 148, 8, - 3, 134, 149, 154, 67, 116, 40, 146, 253, 242, 196, 139, 203, 14, - 174, 254, 78, 27, 47, 108, 60, 202, 60, 42, 210, 242, 58, 13, 185, - 67, 147, 166, 191, 71, 164, 128, 81, 177, 199, 147, 252, 162, 229, - 143, 98, 141, 222, 46, 83, 109, 82, 196, 109, 161, 40, 108, 207, - 82, 53, 162, 205, 171, 33, 140, 5, 74, 76, 150, 22, 122, 176, 189, - 228, 176, 234, 176, 13, 118, 181, 134, 35, 133, 164, 160, 205, 176, - 32, 188, 185, 166, 73, 24, 164, 20, 187, 2, 226, 186, 238, 98, 57, - 51, 76, 156, 83, 113, 72, 184, 50, 220, 49, 138, 46, 36, 4, 49, - 215, 66, 173, 58, 139, 187, 6, 252, 97, 191, 69, 246, 82, 48, 177, - 11, 149, 168, 93, 15, 170, 125, 131, 101, 103, 253, 177, 165, 71, - 85, 219, 207, 106, 12, 58, 47, 159, 33, 243, 107, 6, 117, 141, 209, - 115, 207, 19, 236, 137, 195, 230, 167, 225, 172, 99, 204, 113, 125, - 69, 125, 97, 252, 90, 248, 198, 175, 240, 187, 246, 164, 220, 102, - 7, 224, 124, 28, 170, 6, 4, 137, 155, 233, 85, 125, 93, 119, 97, - 183, 114, 193, 10, 184, 191, 202, 109, 97, 116, 194, 152, 40, 46, - 202, 49, 195, 138, 14, 2, 255, 44, 107, 160, 45, 150, 6, 78, 145, - 99, - ]; - - let signaling_key = [0u8; 52]; - let envelope = - Envelope::decrypt(&body, Some(&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, - ] - ); - } -} diff --git a/src/lib.rs b/src/lib.rs index 1cc48816f..138d387b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,9 +50,7 @@ pub const GROUP_LEAVE_FLAG: u32 = 2; pub mod prelude { pub use crate::{ cipher::ServiceCipher, - configuration::{ - ServiceConfiguration, ServiceCredentials, SignalingKey, - }, + configuration::{ServiceConfiguration, ServiceCredentials}, content::Content, envelope::Envelope, groups_v2::{ diff --git a/src/messagepipe.rs b/src/messagepipe.rs index 9d4e89efa..238363f17 100644 --- a/src/messagepipe.rs +++ b/src/messagepipe.rs @@ -5,6 +5,7 @@ use futures::{ }, prelude::*, }; +use prost::Message; pub use crate::{ configuration::ServiceCredentials, @@ -24,15 +25,11 @@ pub enum Incoming { pub struct MessagePipe { ws: SignalWebSocket, - credentials: ServiceCredentials, } impl MessagePipe { - pub fn from_socket( - ws: SignalWebSocket, - credentials: ServiceCredentials, - ) -> Self { - MessagePipe { ws, credentials } + pub fn from_socket(ws: SignalWebSocket) -> Self { + MessagePipe { ws } } /// Return a SignalWebSocket for sending messages and other purposes beyond receiving messages. @@ -83,11 +80,13 @@ impl MessagePipe { reason: "request without body.", }); }; - Some(Incoming::Envelope(Envelope::decrypt( - body, - self.credentials.signaling_key.as_ref(), - request.is_signal_key_encrypted(), - )?)) + if request.is_signal_key_encrypted() { + return Err(ServiceError::InvalidFrame { + reason: "Signal key encrypted envelope received, but not supported anymore.", + }); + } else { + Some(Incoming::Envelope(Envelope::decode(body as &[u8])?)) + } } else if request.is_queue_empty() { Some(Incoming::QueueEmpty) } else { diff --git a/src/profile_cipher.rs b/src/profile_cipher.rs index f4c552181..0664170a1 100644 --- a/src/profile_cipher.rs +++ b/src/profile_cipher.rs @@ -19,12 +19,13 @@ use crate::{ /// # let mut rng = rand::thread_rng(); /// # let some_randomness = rng.gen(); /// let profile_key = ProfileKey::generate(some_randomness); +/// let profile_cipher = ProfileCipher::new(profile_key); /// let name = ProfileName::<&str> { /// given_name: "Bill", /// family_name: None, /// }; -/// let cipher = ProfileCipher::from(profile_key); -/// let encrypted = cipher.encrypt_name(&name).unwrap(); +/// let cipher = ProfileCipher::from(profile_cipher); +/// let encrypted = cipher.encrypt_name(&name, &mut rng).unwrap(); /// let decrypted = cipher.decrypt_name(&encrypted).unwrap().unwrap(); /// assert_eq!(decrypted.as_ref(), name); /// ``` diff --git a/src/provisioning/cipher.rs b/src/provisioning/cipher.rs index b0ec8ab61..8fb754716 100644 --- a/src/provisioning/cipher.rs +++ b/src/provisioning/cipher.rs @@ -12,10 +12,14 @@ use sha2::Sha256; pub use crate::proto::{ProvisionEnvelope, ProvisionMessage}; -use crate::{ - envelope::{CIPHER_KEY_SIZE, IV_LENGTH, IV_OFFSET}, - provisioning::ProvisioningError, -}; +use crate::provisioning::ProvisioningError; + +pub(crate) const CIPHER_KEY_SIZE: usize = 32; + +pub(crate) const VERSION_OFFSET: usize = 0; +pub(crate) const VERSION_LENGTH: usize = 1; +pub(crate) const IV_OFFSET: usize = VERSION_OFFSET + VERSION_LENGTH; +pub(crate) const IV_LENGTH: usize = 16; enum CipherMode { DecryptAndEncrypt(KeyPair), diff --git a/src/receiver.rs b/src/receiver.rs index 57016dce7..0794eb8f1 100644 --- a/src/receiver.rs +++ b/src/receiver.rs @@ -39,7 +39,7 @@ impl MessageReceiver { Some(credentials.clone()), ) .await?; - Ok(MessagePipe::from_socket(ws, credentials)) + Ok(MessagePipe::from_socket(ws)) } pub async fn retrieve_contacts( diff --git a/src/utils.rs b/src/utils.rs index 4d42ef4f7..fe86f6e1f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -231,43 +231,6 @@ pub mod serde_optional_private_key { } } -pub mod serde_signaling_key { - use std::convert::TryInto; - - use super::BASE64_RELAXED; - use crate::configuration::SignalingKey; - use base64::prelude::*; - use serde::{Deserialize, Deserializer, Serializer}; - - pub fn serialize( - signaling_key: &SignalingKey, - serializer: S, - ) -> Result - where - S: Serializer, - { - serializer.serialize_str(&BASE64_RELAXED.encode(signaling_key)) - } - - pub fn deserialize<'de, D>( - deserializer: D, - ) -> Result - where - D: Deserializer<'de>, - { - BASE64_RELAXED - .decode(String::deserialize(deserializer)?) - .map_err(serde::de::Error::custom)? - .try_into() - .map_err(|buf: Vec| { - serde::de::Error::invalid_length( - buf.len(), - &"invalid signaling key length", - ) - }) - } -} - pub mod serde_phone_number { use phonenumber::PhoneNumber; use serde::{Deserialize, Deserializer, Serializer};