From fab47c64c3c7529c8146689bc53001f13e741974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20F=C3=A9ron?= Date: Sat, 16 Dec 2023 23:54:10 +0100 Subject: [PATCH] Use fix linking from libsignal-service-rs Co-authored-by: Boxdot --- presage-cli/Cargo.toml | 2 +- presage-store-sled/src/lib.rs | 37 ++-- presage/src/manager/linking.rs | 280 +++++------------------------- presage/src/manager/registered.rs | 11 +- presage/src/store.rs | 22 +-- 5 files changed, 77 insertions(+), 275 deletions(-) diff --git a/presage-cli/Cargo.toml b/presage-cli/Cargo.toml index ed391437f..8f3f7c9b5 100644 --- a/presage-cli/Cargo.toml +++ b/presage-cli/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Gabriel FĂ©ron "] presage = { path = "../presage" } presage-store-sled = { path = "../presage-store-sled" } -anyhow = "1.0" +anyhow = { version = "1.0", features = ["backtrace"] } base64 = "0.12" chrono = { version = "0.4", default-features = false, features = ["serde", "clock"] } clap = { version = ">=4.2.4", features = ["derive"] } diff --git a/presage-store-sled/src/lib.rs b/presage-store-sled/src/lib.rs index abfd8b9b0..806ed8056 100644 --- a/presage-store-sled/src/lib.rs +++ b/presage-store-sled/src/lib.rs @@ -13,6 +13,7 @@ use presage::libsignal_service::{ content::Content, groups_v2::Group, models::Contact, + pre_keys::PreKeysStore, prelude::{ProfileKey, Uuid}, protocol::{ Direction, GenericSignedPreKey, IdentityKey, IdentityKeyPair, IdentityKeyStore, @@ -24,7 +25,7 @@ use presage::libsignal_service::{ session_store::SessionStoreExt, Profile, ServiceAddress, }; -use presage::store::{ContentExt, ContentsStore, PreKeyStoreExt, StateStore, Store, Thread}; +use presage::store::{ContentExt, ContentsStore, StateStore, Store, Thread}; use presage::{manager::RegistrationData, proto::verified}; use prost::Message; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -603,39 +604,43 @@ impl ContentsStore for SledStore { } } -impl PreKeyStoreExt for SledStore { - type PreKeyStoreExtError = SledStoreError; - - fn pre_keys_offset_id(&self) -> Result { +impl PreKeysStore for SledStore { + fn pre_keys_offset_id(&self) -> Result { Ok(self - .get(SLED_TREE_STATE, SLED_KEY_PRE_KEYS_OFFSET_ID)? + .get(SLED_TREE_STATE, SLED_KEY_PRE_KEYS_OFFSET_ID) + .map_err(|_| SignalProtocolError::InvalidPreKeyId)? .unwrap_or(0)) } - fn set_pre_keys_offset_id(&mut self, id: u32) -> Result<(), SledStoreError> { - self.insert(SLED_TREE_STATE, SLED_KEY_PRE_KEYS_OFFSET_ID, id)?; + fn set_pre_keys_offset_id(&mut self, id: u32) -> Result<(), SignalProtocolError> { + self.insert(SLED_TREE_STATE, SLED_KEY_PRE_KEYS_OFFSET_ID, id) + .map_err(|_| SignalProtocolError::InvalidPreKeyId)?; Ok(()) } - fn next_signed_pre_key_id(&self) -> Result { + fn next_signed_pre_key_id(&self) -> Result { Ok(self - .get(SLED_TREE_STATE, SLED_KEY_NEXT_SIGNED_PRE_KEY_ID)? + .get(SLED_TREE_STATE, SLED_KEY_NEXT_SIGNED_PRE_KEY_ID) + .map_err(|_| SignalProtocolError::InvalidSignedPreKeyId)? .unwrap_or(0)) } - fn set_next_signed_pre_key_id(&mut self, id: u32) -> Result<(), SledStoreError> { - self.insert(SLED_TREE_STATE, SLED_KEY_NEXT_SIGNED_PRE_KEY_ID, id)?; + fn set_next_signed_pre_key_id(&mut self, id: u32) -> Result<(), SignalProtocolError> { + self.insert(SLED_TREE_STATE, SLED_KEY_NEXT_SIGNED_PRE_KEY_ID, id) + .map_err(|_| SignalProtocolError::InvalidSignedPreKeyId)?; Ok(()) } - fn next_pq_pre_key_id(&self) -> Result { + fn next_pq_pre_key_id(&self) -> Result { Ok(self - .get(SLED_TREE_STATE, SLED_KEY_NEXT_PQ_PRE_KEY_ID)? + .get(SLED_TREE_STATE, SLED_KEY_NEXT_PQ_PRE_KEY_ID) + .map_err(|_| SignalProtocolError::InvalidKyberPreKeyId)? .unwrap_or(0)) } - fn set_next_pq_pre_key_id(&mut self, id: u32) -> Result<(), SledStoreError> { - self.insert(SLED_TREE_STATE, SLED_KEY_NEXT_PQ_PRE_KEY_ID, id)?; + fn set_next_pq_pre_key_id(&mut self, id: u32) -> Result<(), SignalProtocolError> { + self.insert(SLED_TREE_STATE, SLED_KEY_NEXT_PQ_PRE_KEY_ID, id) + .map_err(|_| SignalProtocolError::InvalidKyberPreKeyId)?; Ok(()) } } diff --git a/presage/src/manager/linking.rs b/presage/src/manager/linking.rs index f82113dd2..205fcab11 100644 --- a/presage/src/manager/linking.rs +++ b/presage/src/manager/linking.rs @@ -1,23 +1,12 @@ -use std::time::SystemTime; - use futures::channel::{mpsc, oneshot}; use futures::{future, StreamExt}; -use libsignal_service::configuration::{Endpoint, ServiceConfiguration, SignalServers}; -use libsignal_service::encrypt_device_name_base64; -use libsignal_service::pre_keys::{KyberPreKeyEntity, SignedPreKey}; -use libsignal_service::prelude::Uuid; -use libsignal_service::protocol::{ - kem, GenericSignedPreKey, KeyPair, KyberPreKeyRecord, ServiceIdKind, SignedPreKeyRecord, -}; +use libsignal_service::configuration::{ServiceConfiguration, SignalServers}; use libsignal_service::provisioning::{LinkingManager, SecondaryDeviceProvisioning}; -use libsignal_service::push_service::{HttpAuth, HttpAuthOverride, PushService}; -use libsignal_service::zkgroup::profiles::ProfileKey; use libsignal_service_hyper::push_service::HyperPushService; use log::info; use rand::distributions::{Alphanumeric, DistString}; use rand::rngs::StdRng; use rand::{RngCore, SeedableRng}; -use serde::{Deserialize, Serialize}; use url::Url; use crate::manager::registered::RegistrationData; @@ -91,11 +80,15 @@ impl Manager { let (tx, mut rx) = mpsc::channel(1); + // XXX: this is obviously wrong + let mut pni_store = store.clone(); + let (wait_for_qrcode_scan, registration_data) = future::join( linking_manager.provision_secondary_device( + &mut store, + &mut pni_store, &mut rng, - device_name.clone(), - signaling_key, + &device_name, tx, ), async move { @@ -107,235 +100,54 @@ impl Manager { } else { return Err(Error::LinkError); } - rx.next().await.ok_or(Error::NoProvisioningMessageReceived) + if let Some(SecondaryDeviceProvisioning::NewDeviceRegistration(data)) = + rx.next().await + { + Ok(data) + } else { + return Err(Error::NoProvisioningMessageReceived); + } }, ) .await; wait_for_qrcode_scan?; - let registration_data = registration_data?; - - let mut manager = Manager { - store, - state: Linking, - rng, - }; - - let data = match manager - .link_device( - registration_data, - push_service, - signal_servers, - signaling_key, - device_name, - password, - ) - .await - { - Ok(data) => { - info!("successfully registered device {}", &data.service_ids); - manager.store.save_registration_data(&data)?; - data + match registration_data { + Ok(d) => { + let registration_data = RegistrationData { + signal_servers, + device_name: Some(device_name), + phone_number: d.phone_number, + service_ids: d.service_ids, + password, + signaling_key, + device_id: Some(d.device_id.into()), + registration_id: d.registration_id, + pni_registration_id: Some(d.pni_registration_id), + aci_public_key: d.aci_public_key, + aci_private_key: d.aci_private_key, + pni_public_key: Some(d.pni_public_key), + pni_private_key: Some(d.pni_private_key), + profile_key: d.profile_key, + }; + + store.save_registration_data(®istration_data)?; + info!( + "successfully registered device {}", + ®istration_data.service_ids + ); + + Ok(Manager { + rng, + store, + state: Registered::with_data(registration_data), + }) } Err(e) => { - // clear the entire store on any error, there's no possible recovery here - manager.store.clear_registration()?; - return Err(e); + store.clear_registration()?; + Err(e) } - }; - - Ok(Manager { - rng: manager.rng, - store: manager.store, - state: Registered::with_data(data), - }) - } - - async fn link_device( - &mut self, - data: SecondaryDeviceProvisioning, - mut push_service: impl PushService + Clone, - signal_servers: SignalServers, - signaling_key: [u8; 52], - device_name: String, - password: String, - ) -> Result> { - let SecondaryDeviceProvisioning::NewDeviceRegistration { - phone_number, - provisioning_code, - registration_id, - pni_registration_id, - mut service_ids, - aci_private_key, - aci_public_key, - pni_private_key, - pni_public_key, - profile_key, - } = data - else { - return Err(Error::LinkError); // logic error - }; - - let aci_key_pair = KeyPair::new(aci_public_key, aci_private_key); - let pni_key_pair = KeyPair::new(pni_public_key, pni_private_key); - - let aci_pq_last_resort_pre_key = self - .generate_last_resort_kyber_key(ServiceIdKind::Aci, &aci_key_pair) - .await; - let pni_pq_last_resort_pre_key = self - .generate_last_resort_kyber_key(ServiceIdKind::Pni, &pni_key_pair) - .await; - let aci_signed_pre_key = self - .generate_signed_pre_key(ServiceIdKind::Aci, &aci_key_pair) - .await; - let pni_signed_pre_key = self - .generate_signed_pre_key(ServiceIdKind::Pni, &pni_key_pair) - .await; - - #[derive(Debug, Serialize)] - #[serde(rename_all = "camelCase")] - struct LinkRequest { - verification_code: String, - account_attributes: AccountAttributes, - aci_signed_pre_key: SignedPreKey, - pni_signed_pre_key: SignedPreKey, - aci_pq_last_resort_pre_key: KyberPreKeyEntity, - pni_pq_last_resort_pre_key: KyberPreKeyEntity, } - - #[derive(Debug, Serialize)] - #[serde(rename_all = "camelCase")] - struct AccountAttributes { - fetches_messages: bool, - name: String, - registration_id: u32, - pni_registration_id: u32, - capabilities: Capabilities, - } - - #[derive(Debug, Serialize)] - #[serde(rename_all = "camelCase")] - struct Capabilities { - pni: bool, - } - - let encrypted_device_name = - encrypt_device_name_base64(&mut self.rng, &device_name, &aci_public_key).unwrap(); - let profile_key = ProfileKey::create(profile_key.as_slice().try_into().unwrap()); - let request = LinkRequest { - verification_code: provisioning_code, - account_attributes: AccountAttributes { - registration_id, - pni_registration_id, - fetches_messages: true, - capabilities: Capabilities { pni: true }, - name: encrypted_device_name, - }, - aci_signed_pre_key: aci_signed_pre_key.try_into().unwrap(), - pni_signed_pre_key: pni_signed_pre_key.try_into().unwrap(), - aci_pq_last_resort_pre_key: aci_pq_last_resort_pre_key.try_into().unwrap(), - pni_pq_last_resort_pre_key: pni_pq_last_resort_pre_key.try_into().unwrap(), - }; - - #[derive(Debug, Deserialize)] - #[serde(rename_all = "camelCase")] - struct LinkResponse { - uuid: Uuid, - pni: Uuid, - device_id: u32, - } - - let LinkResponse { - uuid, - pni, - device_id, - } = dbg!( - push_service - .put_json( - Endpoint::Service, - "v1/devices/link", - &[], - HttpAuthOverride::Identified(HttpAuth { - username: phone_number.to_string(), - password: password.clone(), - }), - &request, - ) - .await? - ); - - service_ids.aci = uuid; - service_ids.pni = pni; - - Ok(RegistrationData { - signal_servers, - device_name: Some(device_name), - phone_number, - service_ids, - password, - signaling_key, - device_id: Some(device_id), - registration_id, - pni_registration_id: Some(pni_registration_id), - aci_private_key, - aci_public_key, - pni_private_key: Some(pni_private_key), - pni_public_key: Some(pni_public_key), - profile_key, - }) - } - - async fn generate_last_resort_kyber_key( - &mut self, - _service_id_kind: ServiceIdKind, - identity_key: &KeyPair, - ) -> KyberPreKeyRecord { - let id = self.store.next_pq_pre_key_id().unwrap(); - let id = id.max(1); // TODO: Hack, keys start with 1 - - let record = KyberPreKeyRecord::generate( - kem::KeyType::Kyber1024, - id.into(), - &identity_key.private_key, - ) - .unwrap(); - - self.store - .save_kyber_pre_key(id.into(), &record) - .await - .unwrap(); - self.store.set_next_pq_pre_key_id(id + 1).unwrap(); - - record - } - - async fn generate_signed_pre_key( - &mut self, - _service_id_kind: ServiceIdKind, - identity_key: &KeyPair, - ) -> SignedPreKeyRecord { - let id = self.store.next_signed_pre_key_id().unwrap(); - let id = id.max(1); // TODO: Hack, keys start with 1 - - let key_pair = KeyPair::generate(&mut self.rng); - let signature = identity_key - .private_key - .calculate_signature(&key_pair.public_key.serialize(), &mut self.rng) - .unwrap(); - - let unix_time = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_millis() as u64; - let record = SignedPreKeyRecord::new(id.into(), unix_time, &key_pair, &signature); - - self.store - .save_signed_pre_key(id.into(), &record) - .await - .unwrap(); - self.store.set_next_signed_pre_key_id(id + 1).unwrap(); - - record } } diff --git a/presage/src/manager/registered.rs b/presage/src/manager/registered.rs index e3d7a54d4..ca60cedac 100644 --- a/presage/src/manager/registered.rs +++ b/presage/src/manager/registered.rs @@ -213,11 +213,15 @@ impl Manager { match identified_ws.clone() { Some(ws) => Ok(ws), None => { - let keep_alive = true; let headers = &[("X-Signal-Receive-Stories", "false")]; let ws = self .identified_push_service() - .ws("/v1/websocket/", headers, self.credentials(), keep_alive) + .ws( + "/v1/websocket/", + "/v1/keepalive", + headers, + self.credentials(), + ) .await?; identified_ws.replace(ws.clone()); debug!("initialized identified websocket"); @@ -232,10 +236,9 @@ impl Manager { match unidentified_ws.clone() { Some(ws) => Ok(ws), None => { - let keep_alive = true; let ws = self .unidentified_push_service() - .ws("/v1/websocket/", &[], None, keep_alive) + .ws("/v1/websocket/", "/v1/keepalive", &[], None) .await?; unidentified_ws.replace(ws.clone()); debug!("initialized unidentified websocket"); diff --git a/presage/src/store.rs b/presage/src/store.rs index cee012d1d..d897b4bc4 100644 --- a/presage/src/store.rs +++ b/presage/src/store.rs @@ -6,6 +6,7 @@ use libsignal_service::{ content::{ContentBody, Metadata}, groups_v2::Group, models::Contact, + pre_keys::PreKeysStore, prelude::{Content, ProfileKey, Uuid, UuidError}, proto::{ sync_message::{self, Sent}, @@ -44,25 +45,6 @@ pub trait StateStore { fn clear_registration(&mut self) -> Result<(), Self::StateStoreError>; } -/// Stores the keys published ahead of time, pre-keys -/// -/// -pub trait PreKeyStoreExt { - type PreKeyStoreExtError: StoreError; - - fn pre_keys_offset_id(&self) -> Result; - - fn set_pre_keys_offset_id(&mut self, id: u32) -> Result<(), Self::PreKeyStoreExtError>; - - fn next_signed_pre_key_id(&self) -> Result; - - fn next_pq_pre_key_id(&self) -> Result; - - fn set_next_signed_pre_key_id(&mut self, id: u32) -> Result<(), Self::PreKeyStoreExtError>; - - fn set_next_pq_pre_key_id(&mut self, id: u32) -> Result<(), Self::PreKeyStoreExtError>; -} - /// Stores messages, contacts, groups and profiles pub trait ContentsStore { type ContentsStoreError: StoreError; @@ -234,7 +216,7 @@ pub trait ContentsStore { /// The manager store trait combining all other stores into a single one pub trait Store: StateStore - + PreKeyStoreExt + + PreKeysStore + ContentsStore + ProtocolStore + SenderKeyStore