diff --git a/Cargo.toml b/Cargo.toml index 9beea6058..383b6c5d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["presage", "presage-cli", "presage-store-sled", "presage-store-cipher"] +members = ["presage", "presage-cli", "presage-store-sled", "presage-store-cipher", "presage-store-sqlite"] resolver = "2" [patch.crates-io] diff --git a/presage-cli/src/main.rs b/presage-cli/src/main.rs index e98f12155..9f4e70409 100644 --- a/presage-cli/src/main.rs +++ b/presage-cli/src/main.rs @@ -27,6 +27,7 @@ use presage::libsignal_service::ServiceAddress; use presage::manager::ReceivingMode; use presage::model::contacts::Contact; use presage::model::groups::Group; +use presage::model::identity::OnNewIdentity; use presage::proto::receipt_message; use presage::proto::EditMessage; use presage::proto::ReceiptMessage; @@ -39,7 +40,6 @@ use presage::{ Manager, }; use presage_store_sled::MigrationConflictStrategy; -use presage_store_sled::OnNewIdentity; use presage_store_sled::SledStore; use tempfile::Builder; use tokio::task; diff --git a/presage-store-sled/src/lib.rs b/presage-store-sled/src/lib.rs index 40187bf15..1c788b0b5 100644 --- a/presage-store-sled/src/lib.rs +++ b/presage-store-sled/src/lib.rs @@ -16,6 +16,7 @@ use presage::{ }, }, manager::RegistrationData, + model::identity::OnNewIdentity, store::{ContentsStore, StateStore, Store}, }; use protocol::{AciSledStore, PniSledStore, SledProtocolStore, SledTrees}; @@ -101,12 +102,6 @@ impl SchemaVersion { } } -#[derive(Debug, Clone)] -pub enum OnNewIdentity { - Reject, - Trust, -} - impl SledStore { #[allow(unused_variables)] fn new( diff --git a/presage-store-sqlite/Cargo.toml b/presage-store-sqlite/Cargo.toml new file mode 100644 index 000000000..4f3d6057b --- /dev/null +++ b/presage-store-sqlite/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "presage-store-sqlite" +version = "0.1.0" +edition = "2021" + +[dependencies] +async-trait = "0.1.83" +chrono = "0.4.38" +presage = { path = "../presage" } +presage-store-cipher = { path = "../presage-store-cipher", optional = true } + +sqlx = { version = "0.8.2", features = ["sqlite"] } +thiserror = "1.0.65" diff --git a/presage-store-sqlite/src/content.rs b/presage-store-sqlite/src/content.rs new file mode 100644 index 000000000..b06932ca4 --- /dev/null +++ b/presage-store-sqlite/src/content.rs @@ -0,0 +1,217 @@ +use std::marker::PhantomData; + +use presage::{ + libsignal_service::{prelude::Content, zkgroup::GroupMasterKeyBytes}, + model::{contacts::Contact, groups::Group}, + store::{ContentsStore, StickerPack}, +}; + +use crate::{SqliteStore, SqliteStoreError}; + +impl ContentsStore for SqliteStore { + type ContentsStoreError = SqliteStoreError; + + type ContactsIter = DummyIter>; + + type GroupsIter = DummyIter>; + + type MessagesIter = DummyIter>; + + type StickerPacksIter = DummyIter>; + + async fn clear_profiles(&mut self) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn clear_contents(&mut self) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn clear_messages(&mut self) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn clear_thread( + &mut self, + thread: &presage::store::Thread, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn save_message( + &self, + thread: &presage::store::Thread, + message: presage::libsignal_service::prelude::Content, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn delete_message( + &mut self, + thread: &presage::store::Thread, + timestamp: u64, + ) -> Result { + todo!() + } + + async fn message( + &self, + thread: &presage::store::Thread, + timestamp: u64, + ) -> Result, Self::ContentsStoreError> + { + todo!() + } + + async fn messages( + &self, + thread: &presage::store::Thread, + range: impl std::ops::RangeBounds, + ) -> Result { + todo!() + } + + async fn clear_contacts(&mut self) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn save_contact( + &mut self, + contacts: &presage::model::contacts::Contact, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn contacts(&self) -> Result { + todo!() + } + + async fn contact_by_id( + &self, + id: &presage::libsignal_service::prelude::Uuid, + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn clear_groups(&mut self) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn save_group( + &self, + master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, + group: impl Into, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn groups(&self) -> Result { + todo!() + } + + async fn group( + &self, + master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn save_group_avatar( + &self, + master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, + avatar: &presage::AvatarBytes, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn group_avatar( + &self, + master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn upsert_profile_key( + &mut self, + uuid: &presage::libsignal_service::prelude::Uuid, + key: presage::libsignal_service::prelude::ProfileKey, + ) -> Result { + todo!() + } + + async fn profile_key( + &self, + uuid: &presage::libsignal_service::prelude::Uuid, + ) -> Result, Self::ContentsStoreError> + { + todo!() + } + + async fn save_profile( + &mut self, + uuid: presage::libsignal_service::prelude::Uuid, + key: presage::libsignal_service::prelude::ProfileKey, + profile: presage::libsignal_service::Profile, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn profile( + &self, + uuid: presage::libsignal_service::prelude::Uuid, + key: presage::libsignal_service::prelude::ProfileKey, + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn save_profile_avatar( + &mut self, + uuid: presage::libsignal_service::prelude::Uuid, + key: presage::libsignal_service::prelude::ProfileKey, + profile: &presage::AvatarBytes, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn profile_avatar( + &self, + uuid: presage::libsignal_service::prelude::Uuid, + key: presage::libsignal_service::prelude::ProfileKey, + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn add_sticker_pack( + &mut self, + pack: &presage::store::StickerPack, + ) -> Result<(), Self::ContentsStoreError> { + todo!() + } + + async fn sticker_pack( + &self, + id: &[u8], + ) -> Result, Self::ContentsStoreError> { + todo!() + } + + async fn remove_sticker_pack(&mut self, id: &[u8]) -> Result { + todo!() + } + + async fn sticker_packs(&self) -> Result { + todo!() + } +} + +pub struct DummyIter { + _data: PhantomData, +} + +impl Iterator for DummyIter { + type Item = T; + + fn next(&mut self) -> Option { + todo!() + } +} diff --git a/presage-store-sqlite/src/error.rs b/presage-store-sqlite/src/error.rs new file mode 100644 index 000000000..fa7764462 --- /dev/null +++ b/presage-store-sqlite/src/error.rs @@ -0,0 +1,11 @@ +use presage::store::StoreError; + +#[derive(Debug, thiserror::Error)] +pub enum SqliteStoreError { + #[error("database migration is not supported")] + MigrationConflict, + #[error("data store error: {0}")] + Db(#[from] sqlx::Error), +} + +impl StoreError for SqliteStoreError {} diff --git a/presage-store-sqlite/src/lib.rs b/presage-store-sqlite/src/lib.rs new file mode 100644 index 000000000..90e58977b --- /dev/null +++ b/presage-store-sqlite/src/lib.rs @@ -0,0 +1,101 @@ +#![allow(warnings)] + +use std::path::Path; + +use presage::{ + model::identity::OnNewIdentity, + store::{StateStore, Store}, +}; +use protocol::SqliteProtocolStore; +use sqlx::{sqlite::SqliteConnectOptions, SqlitePool}; + +mod content; +mod error; +mod protocol; + +pub use error::SqliteStoreError; + +#[derive(Debug, Clone)] +pub struct SqliteStore { + db: SqlitePool, + /// Whether to trust new identities automatically (for instance, when a somebody's phone has changed) + trust_new_identities: OnNewIdentity, +} + +impl SqliteStore { + pub async fn open( + db_path: impl AsRef, + trust_new_identities: OnNewIdentity, + ) -> Result { + let connect_options = SqliteConnectOptions::new().filename(db_path); + let pool = SqlitePool::connect_with(connect_options).await?; + + Ok(Self { + db: pool, + trust_new_identities, + }) + } +} + +impl Store for SqliteStore { + type Error = SqliteStoreError; + + type AciStore = SqliteProtocolStore; + + type PniStore = SqliteProtocolStore; + + async fn clear(&mut self) -> Result<(), SqliteStoreError> { + todo!() + } + + fn aci_protocol_store(&self) -> Self::AciStore { + SqliteProtocolStore { + store: self.clone(), + } + } + + fn pni_protocol_store(&self) -> Self::PniStore { + SqliteProtocolStore { + store: self.clone(), + } + } +} + +impl StateStore for SqliteStore { + type StateStoreError = SqliteStoreError; + + async fn load_registration_data( + &self, + ) -> Result, Self::StateStoreError> { + todo!() + } + + async fn set_aci_identity_key_pair( + &self, + key_pair: presage::libsignal_service::protocol::IdentityKeyPair, + ) -> Result<(), Self::StateStoreError> { + todo!() + } + + async fn set_pni_identity_key_pair( + &self, + key_pair: presage::libsignal_service::protocol::IdentityKeyPair, + ) -> Result<(), Self::StateStoreError> { + todo!() + } + + async fn save_registration_data( + &mut self, + state: &presage::manager::RegistrationData, + ) -> Result<(), Self::StateStoreError> { + todo!() + } + + async fn is_registered(&self) -> bool { + todo!() + } + + async fn clear_registration(&mut self) -> Result<(), Self::StateStoreError> { + todo!() + } +} diff --git a/presage-store-sqlite/src/protocol.rs b/presage-store-sqlite/src/protocol.rs new file mode 100644 index 000000000..196229f26 --- /dev/null +++ b/presage-store-sqlite/src/protocol.rs @@ -0,0 +1,283 @@ +use async_trait::async_trait; +use chrono::{DateTime, Utc}; +use presage::libsignal_service::{ + pre_keys::{KyberPreKeyStoreExt, PreKeysStore}, + prelude::{IdentityKeyStore, SessionStoreExt, Uuid}, + protocol::{ + Direction, IdentityKey, IdentityKeyPair, KyberPreKeyId, KyberPreKeyRecord, + KyberPreKeyStore, PreKeyId, PreKeyRecord, PreKeyStore, ProtocolAddress, ProtocolStore, + SenderKeyRecord, SenderKeyStore, SessionRecord, SessionStore, + SignalProtocolError as ProtocolError, SignedPreKeyId, SignedPreKeyRecord, + SignedPreKeyStore, + }, + ServiceAddress, +}; + +use crate::SqliteStore; + +#[derive(Clone)] +pub struct SqliteProtocolStore { + pub(crate) store: SqliteStore, +} + +impl ProtocolStore for SqliteProtocolStore {} + +#[async_trait(?Send)] +impl SessionStore for SqliteProtocolStore { + /// Look up the session corresponding to `address`. + async fn load_session( + &self, + address: &ProtocolAddress, + ) -> Result, ProtocolError> { + todo!() + } + + /// Set the entry for `address` to the value of `record`. + async fn store_session( + &mut self, + address: &ProtocolAddress, + record: &SessionRecord, + ) -> Result<(), ProtocolError> { + todo!(); + } +} + +#[async_trait(?Send)] +impl SessionStoreExt for SqliteProtocolStore { + /// Get the IDs of all known sub devices with active sessions for a recipient. + /// + /// This should return every device except for the main device [DEFAULT_DEVICE_ID]. + async fn get_sub_device_sessions( + &self, + name: &ServiceAddress, + ) -> Result, ProtocolError> { + todo!() + } + + /// Remove a session record for a recipient ID + device ID tuple. + async fn delete_session(&self, address: &ProtocolAddress) -> Result<(), ProtocolError> { + todo!() + } + + /// Remove the session records corresponding to all devices of a recipient + /// ID. + /// + /// Returns the number of deleted sessions. + async fn delete_all_sessions(&self, address: &ServiceAddress) -> Result { + todo!() + } +} + +#[async_trait(?Send)] +impl PreKeyStore for SqliteProtocolStore { + /// Look up the pre-key corresponding to `prekey_id`. + async fn get_pre_key(&self, prekey_id: PreKeyId) -> Result { + todo!() + } + + /// Set the entry for `prekey_id` to the value of `record`. + async fn save_pre_key( + &mut self, + prekey_id: PreKeyId, + record: &PreKeyRecord, + ) -> Result<(), ProtocolError> { + todo!() + } + + /// Remove the entry for `prekey_id`. + async fn remove_pre_key(&mut self, prekey_id: PreKeyId) -> Result<(), ProtocolError> { + todo!() + } +} + +#[async_trait(?Send)] +impl PreKeysStore for SqliteProtocolStore { + /// ID of the next pre key + async fn next_pre_key_id(&self) -> Result { + todo!() + } + + /// ID of the next signed pre key + async fn next_signed_pre_key_id(&self) -> Result { + todo!() + } + + /// ID of the next PQ pre key + async fn next_pq_pre_key_id(&self) -> Result { + todo!() + } + + /// number of signed pre-keys we currently have in store + async fn signed_pre_keys_count(&self) -> Result { + todo!() + } + + /// number of kyber pre-keys we currently have in store + async fn kyber_pre_keys_count(&self, last_resort: bool) -> Result { + todo!() + } +} + +#[async_trait(?Send)] +impl SignedPreKeyStore for SqliteProtocolStore { + /// Look up the signed pre-key corresponding to `signed_prekey_id`. + async fn get_signed_pre_key( + &self, + signed_prekey_id: SignedPreKeyId, + ) -> Result { + todo!() + } + + /// Set the entry for `signed_prekey_id` to the value of `record`. + async fn save_signed_pre_key( + &mut self, + signed_prekey_id: SignedPreKeyId, + record: &SignedPreKeyRecord, + ) -> Result<(), ProtocolError> { + todo!() + } +} + +#[async_trait(?Send)] +impl KyberPreKeyStore for SqliteProtocolStore { + /// Look up the signed kyber pre-key corresponding to `kyber_prekey_id`. + async fn get_kyber_pre_key( + &self, + kyber_prekey_id: KyberPreKeyId, + ) -> Result { + todo!() + } + + /// Set the entry for `kyber_prekey_id` to the value of `record`. + async fn save_kyber_pre_key( + &mut self, + kyber_prekey_id: KyberPreKeyId, + record: &KyberPreKeyRecord, + ) -> Result<(), ProtocolError> { + todo!() + } + + /// Mark the entry for `kyber_prekey_id` as "used". + /// This would mean different things for one-time and last-resort Kyber keys. + async fn mark_kyber_pre_key_used( + &mut self, + kyber_prekey_id: KyberPreKeyId, + ) -> Result<(), ProtocolError> { + todo!() + } +} + +#[async_trait(?Send)] +impl KyberPreKeyStoreExt for SqliteProtocolStore { + async fn store_last_resort_kyber_pre_key( + &mut self, + kyber_prekey_id: KyberPreKeyId, + record: &KyberPreKeyRecord, + ) -> Result<(), ProtocolError> { + todo!() + } + + async fn load_last_resort_kyber_pre_keys( + &self, + ) -> Result, ProtocolError> { + todo!() + } + + async fn remove_kyber_pre_key( + &mut self, + kyber_prekey_id: KyberPreKeyId, + ) -> Result<(), ProtocolError> { + todo!() + } + + /// Analogous to markAllOneTimeKyberPreKeysStaleIfNecessary + async fn mark_all_one_time_kyber_pre_keys_stale_if_necessary( + &mut self, + stale_time: DateTime, + ) -> Result<(), ProtocolError> { + todo!() + } + + /// Analogue of deleteAllStaleOneTimeKyberPreKeys + async fn delete_all_stale_one_time_kyber_pre_keys( + &mut self, + threshold: DateTime, + min_count: usize, + ) -> Result<(), ProtocolError> { + todo!() + } +} + +#[async_trait(?Send)] +impl IdentityKeyStore for SqliteProtocolStore { + /// Return the single specific identity the store is assumed to represent, with private key. + async fn get_identity_key_pair(&self) -> Result { + todo!() + } + + /// Return a [u32] specific to this store instance. + /// + /// This local registration id is separate from the per-device identifier used in + /// [ProtocolAddress] and should not change run over run. + /// + /// If the same *device* is unregistered, then registers again, the [ProtocolAddress::device_id] + /// may be the same, but the store registration id returned by this method should + /// be regenerated. + async fn get_local_registration_id(&self) -> Result { + todo!() + } + + // TODO: make this into an enum instead of a bool! + /// Record an identity into the store. The identity is then considered "trusted". + /// + /// The return value represents whether an existing identity was replaced (`Ok(true)`). If it is + /// new or hasn't changed, the return value should be `Ok(false)`. + async fn save_identity( + &mut self, + address: &ProtocolAddress, + identity: &IdentityKey, + ) -> Result { + todo!() + } + + /// Return whether an identity is trusted for the role specified by `direction`. + async fn is_trusted_identity( + &self, + address: &ProtocolAddress, + identity: &IdentityKey, + direction: Direction, + ) -> Result { + todo!() + } + + /// Return the public identity for the given `address`, if known. + async fn get_identity( + &self, + address: &ProtocolAddress, + ) -> Result, ProtocolError> { + todo!() + } +} + +#[async_trait(?Send)] +impl SenderKeyStore for SqliteProtocolStore { + /// Assign `record` to the entry for `(sender, distribution_id)`. + async fn store_sender_key( + &mut self, + sender: &ProtocolAddress, + distribution_id: Uuid, + // TODO: pass this by value! + record: &SenderKeyRecord, + ) -> Result<(), ProtocolError> { + todo!() + } + + /// Look up the entry corresponding to `(sender, distribution_id)`. + async fn load_sender_key( + &mut self, + sender: &ProtocolAddress, + distribution_id: Uuid, + ) -> Result, ProtocolError> { + todo!() + } +} diff --git a/presage/src/manager/linking.rs b/presage/src/manager/linking.rs index ec8455004..a954a6b6f 100644 --- a/presage/src/manager/linking.rs +++ b/presage/src/manager/linking.rs @@ -29,7 +29,8 @@ impl Manager { /// use futures::{channel::oneshot, future, StreamExt}; /// use presage::libsignal_service::configuration::SignalServers; /// use presage::Manager; - /// use presage_store_sled::{MigrationConflictStrategy, OnNewIdentity, SledStore}; + /// use presage::model::identity::OnNewIdentity; + /// use presage_store_sled::{MigrationConflictStrategy, SledStore}; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { diff --git a/presage/src/manager/registration.rs b/presage/src/manager/registration.rs index 4fd87b222..c6bf11d27 100644 --- a/presage/src/manager/registration.rs +++ b/presage/src/manager/registration.rs @@ -38,7 +38,9 @@ impl Manager { /// }; /// use presage::manager::RegistrationOptions; /// use presage::Manager; - /// use presage_store_sled::{MigrationConflictStrategy, OnNewIdentity, SledStore}; + /// use presage::model::identity::OnNewIdentity; + /// + /// use presage_store_sled::{MigrationConflictStrategy, SledStore}; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { diff --git a/presage/src/model/identity.rs b/presage/src/model/identity.rs new file mode 100644 index 000000000..5243ef28d --- /dev/null +++ b/presage/src/model/identity.rs @@ -0,0 +1,6 @@ +/// Whether to trust or reject new identities +#[derive(Debug, Clone)] +pub enum OnNewIdentity { + Reject, + Trust, +} diff --git a/presage/src/model/mod.rs b/presage/src/model/mod.rs index 7920317b5..90139bf9f 100644 --- a/presage/src/model/mod.rs +++ b/presage/src/model/mod.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; pub mod contacts; pub mod groups; +pub mod identity; #[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize)] pub enum ServiceIdType {