From b5d91a5cf8c4de89987fa1571750c25970feebaa Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Sun, 14 Jul 2024 09:59:24 +0300 Subject: [PATCH 1/4] Add `Keys` sync message --- libsignal-service/src/sender.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libsignal-service/src/sender.rs b/libsignal-service/src/sender.rs index f0c8fcfd7..86315742a 100644 --- a/libsignal-service/src/sender.rs +++ b/libsignal-service/src/sender.rs @@ -752,6 +752,25 @@ where Ok(()) } + /// Send `Keys` synchronization message + #[tracing::instrument(skip(self))] + pub async fn send_keys( + &mut self, + recipient: &ServiceAddress, + keys: sync_message::Keys, + ) -> Result<(), MessageSenderError> { + let msg = SyncMessage { + keys: Some(keys), + ..SyncMessage::with_padding() + }; + + let ts = Utc::now().timestamp_millis() as u64; + self.send_message(recipient, None, msg, ts, false, false) + .await?; + + Ok(()) + } + #[tracing::instrument(level = "trace", skip(self))] fn create_pni_signature( &mut self, From ebf83170d13d6f2fcb6b9641b26d42966f1cb7cf Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Sun, 8 Sep 2024 23:37:06 +0300 Subject: [PATCH 2/4] Add MasterKey and StorageServiceKey --- libsignal-service/src/account_manager.rs | 4 +- libsignal-service/src/lib.rs | 2 + libsignal-service/src/master_key.rs | 111 +++++++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 libsignal-service/src/master_key.rs diff --git a/libsignal-service/src/account_manager.rs b/libsignal-service/src/account_manager.rs index 0ca2abad9..c33f97408 100644 --- a/libsignal-service/src/account_manager.rs +++ b/libsignal-service/src/account_manager.rs @@ -18,6 +18,7 @@ use tracing_futures::Instrument; use zkgroup::profiles::ProfileKey; use crate::content::ContentBody; +use crate::master_key::MasterKey; use crate::pre_keys::{ KyberPreKeyEntity, PreKeyEntity, PreKeysStore, SignedPreKeyEntity, PRE_KEY_BATCH_SIZE, PRE_KEY_MINIMUM, @@ -277,6 +278,7 @@ impl AccountManager { aci_identity_store: &dyn IdentityKeyStore, pni_identity_store: &dyn IdentityKeyStore, credentials: ServiceCredentials, + master_key: Option, ) -> Result<(), ProvisioningError> { let query: HashMap<_, _> = url.query_pairs().collect(); let ephemeral_id = @@ -328,7 +330,7 @@ impl AccountManager { provisioning_code: Some(provisioning_code), read_receipts: None, user_agent: None, - master_key: None, // XXX + master_key: master_key.map(|x| x.into()), }; let cipher = ProvisioningCipher::from_public(pub_key); diff --git a/libsignal-service/src/lib.rs b/libsignal-service/src/lib.rs index a8a806e27..177325328 100644 --- a/libsignal-service/src/lib.rs +++ b/libsignal-service/src/lib.rs @@ -14,6 +14,7 @@ pub mod content; mod digeststream; pub mod envelope; pub mod groups_v2; +pub mod master_key; pub mod messagepipe; pub mod models; pub mod pre_keys; @@ -79,6 +80,7 @@ pub mod prelude { AccessControl, Group, Member, PendingMember, RequestingMember, Timer, }, + master_key::{MasterKey, MasterKeyStore, StorageServiceKey}, proto::{ attachment_pointer::AttachmentIdentifier, sync_message::Contacts, AttachmentPointer, diff --git a/libsignal-service/src/master_key.rs b/libsignal-service/src/master_key.rs new file mode 100644 index 000000000..da0ca7fd1 --- /dev/null +++ b/libsignal-service/src/master_key.rs @@ -0,0 +1,111 @@ +const MASTER_KEY_LEN: usize = 32; +const STORAGE_KEY_LEN: usize = 32; + +#[derive(Debug, PartialEq)] +pub struct MasterKey { + pub inner: [u8; MASTER_KEY_LEN], +} + +impl MasterKey { + pub fn generate() -> Self { + use rand::Rng; + + // Create random bytes + let mut rng = rand::thread_rng(); + let mut inner = [0_u8; MASTER_KEY_LEN]; + rng.fill(&mut inner); + Self { inner } + } + + pub fn from_slice( + slice: &Vec, + ) -> Result { + let inner = slice.as_slice().try_into()?; + Ok(Self { inner }) + } +} + +impl From for Vec { + fn from(val: MasterKey) -> Self { + val.inner.to_vec() + } +} + +#[derive(Debug, PartialEq)] +pub struct StorageServiceKey { + pub inner: [u8; STORAGE_KEY_LEN], +} + +impl StorageServiceKey { + pub fn from_master_key(master_key: &MasterKey) -> Self { + use hmac::{Hmac, Mac}; + use sha2::Sha256; + + type HmacSha256 = Hmac; + const KEY: &[u8] = b"Storage Service Encryption"; + + let mut mac = HmacSha256::new_from_slice(&master_key.inner).unwrap(); + mac.update(KEY); + let result = mac.finalize(); + let inner: [u8; STORAGE_KEY_LEN] = result.into_bytes().into(); + + Self { inner } + } + + pub fn from_slice( + slice: &Vec, + ) -> Result { + let inner = slice.as_slice().try_into()?; + Ok(Self { inner }) + } +} + +impl From for Vec { + fn from(val: StorageServiceKey) -> Self { + val.inner.to_vec() + } +} + +/// Storage trait for handling MasterKey and StorageKey. +pub trait MasterKeyStore { + /// Fetch the master key from the store if it exists. + fn fetch_master_key(&self) -> Option; + + /// Fetch the storage service key from the store if it exists. + fn fetch_storage_service_key(&self) -> Option; + + /// Save (or clear) the master key to the store. + fn store_master_key(&self, master_key: Option<&MasterKey>); + + /// Save (or clear) the storage service key to the store. + fn store_storage_service_key( + &self, + storage_key: Option<&StorageServiceKey>, + ); +} + +mod tests { + #[test] + fn derive_storage_key_from_master_key() { + use super::{MasterKey, StorageServiceKey}; + use base64::prelude::*; + + // This test passed with actual 'masterKey' and 'storageKey' values taken + // from Signal Desktop v7.23.0 database at 2024-09-08 after linking it with Signal Andoid. + + let master_key_bytes = BASE64_STANDARD + .decode("9hquLIIZmom8fHF7H8pbUAreawmPLEqli5ceJ94pFkU=") + .unwrap(); + let storage_key_bytes = BASE64_STANDARD + .decode("QMgZ5RGTLFTr4u/J6nypaJX6DKDlSgMw8vmxU6gxnvI=") + .unwrap(); + assert_eq!(master_key_bytes.len(), 32); + assert_eq!(storage_key_bytes.len(), 32); + + let master_key = MasterKey::from_slice(&master_key_bytes).unwrap(); + let storage_key = StorageServiceKey::from_master_key(&master_key); + + assert_eq!(master_key.inner, master_key_bytes.as_slice()); + assert_eq!(storage_key.inner, storage_key_bytes.as_slice()); + } +} From b8d776135642191bb6a25883d7456593669647b5 Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Mon, 30 Sep 2024 01:20:01 +0300 Subject: [PATCH 3/4] Add sending sync message requests --- libsignal-service/src/sender.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libsignal-service/src/sender.rs b/libsignal-service/src/sender.rs index 86315742a..2576bf785 100644 --- a/libsignal-service/src/sender.rs +++ b/libsignal-service/src/sender.rs @@ -771,6 +771,37 @@ where Ok(()) } + /// Send a `Keys` request message + #[tracing::instrument(skip(self))] + pub async fn send_sync_message_request( + &mut self, + recipient: &ServiceAddress, + request_type: sync_message::request::Type, + ) -> Result<(), MessageSenderError> { + if self.device_id == DEFAULT_DEVICE_ID.into() { + let reason = format!( + "Primary device can't send sync requests, ignoring {:?}", + request_type + ); + return Err(MessageSenderError::ServiceError( + ServiceError::SendError { reason }, + )); + } + + let msg = SyncMessage { + request: Some(sync_message::Request { + r#type: Some(request_type.into()), + }), + ..SyncMessage::with_padding() + }; + + let ts = Utc::now().timestamp_millis() as u64; + self.send_message(recipient, None, msg, ts, false, false) + .await?; + + Ok(()) + } + #[tracing::instrument(level = "trace", skip(self))] fn create_pni_signature( &mut self, From aac50db8662a9556b6223b4d4bbf0081436dfb15 Mon Sep 17 00:00:00 2001 From: Matti Viljanen Date: Thu, 3 Oct 2024 17:11:23 +0300 Subject: [PATCH 4/4] Use more correct slice type Co-authored-by: Ruben De Smet --- libsignal-service/src/master_key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsignal-service/src/master_key.rs b/libsignal-service/src/master_key.rs index da0ca7fd1..40e60a27a 100644 --- a/libsignal-service/src/master_key.rs +++ b/libsignal-service/src/master_key.rs @@ -18,9 +18,9 @@ impl MasterKey { } pub fn from_slice( - slice: &Vec, + slice: &[u8], ) -> Result { - let inner = slice.as_slice().try_into()?; + let inner = slice.try_into()?; Ok(Self { inner }) } } @@ -53,9 +53,9 @@ impl StorageServiceKey { } pub fn from_slice( - slice: &Vec, + slice: &[u8], ) -> Result { - let inner = slice.as_slice().try_into()?; + let inner = slice.try_into()?; Ok(Self { inner }) } }