Skip to content

Commit

Permalink
Merge pull request #310 from whisperfish/keys-wf
Browse files Browse the repository at this point in the history
Add `Keys` sync message
  • Loading branch information
direc85 authored Oct 3, 2024
2 parents 2327c23 + aac50db commit 65b51db
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 1 deletion.
4 changes: 3 additions & 1 deletion libsignal-service/src/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -277,6 +278,7 @@ impl<Service: PushService> AccountManager<Service> {
aci_identity_store: &dyn IdentityKeyStore,
pni_identity_store: &dyn IdentityKeyStore,
credentials: ServiceCredentials,
master_key: Option<MasterKey>,
) -> Result<(), ProvisioningError> {
let query: HashMap<_, _> = url.query_pairs().collect();
let ephemeral_id =
Expand Down Expand Up @@ -328,7 +330,7 @@ impl<Service: PushService> AccountManager<Service> {
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);
Expand Down
2 changes: 2 additions & 0 deletions libsignal-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
111 changes: 111 additions & 0 deletions libsignal-service/src/master_key.rs
Original file line number Diff line number Diff line change
@@ -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: &[u8],
) -> Result<Self, std::array::TryFromSliceError> {
let inner = slice.try_into()?;
Ok(Self { inner })
}
}

impl From<MasterKey> for Vec<u8> {
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<Sha256>;
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: &[u8],
) -> Result<Self, std::array::TryFromSliceError> {
let inner = slice.try_into()?;
Ok(Self { inner })
}
}

impl From<StorageServiceKey> for Vec<u8> {
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<MasterKey>;

/// Fetch the storage service key from the store if it exists.
fn fetch_storage_service_key(&self) -> Option<StorageServiceKey>;

/// 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());
}
}
50 changes: 50 additions & 0 deletions libsignal-service/src/sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,56 @@ 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(())
}

/// 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,
Expand Down

0 comments on commit 65b51db

Please sign in to comment.