Skip to content

Commit

Permalink
Add skeleton for sqlite store implementation (whisperfish#286)
Browse files Browse the repository at this point in the history
gferon authored Oct 24, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent b108749 commit 549f9fb
Showing 12 changed files with 640 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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]
2 changes: 1 addition & 1 deletion presage-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -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;
7 changes: 1 addition & 6 deletions presage-store-sled/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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(
13 changes: 13 additions & 0 deletions presage-store-sqlite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
217 changes: 217 additions & 0 deletions presage-store-sqlite/src/content.rs
Original file line number Diff line number Diff line change
@@ -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<Result<Contact, Self::ContentsStoreError>>;

type GroupsIter = DummyIter<Result<(GroupMasterKeyBytes, Group), Self::ContentsStoreError>>;

type MessagesIter = DummyIter<Result<Content, Self::ContentsStoreError>>;

type StickerPacksIter = DummyIter<Result<StickerPack, Self::ContentsStoreError>>;

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<bool, Self::ContentsStoreError> {
todo!()
}

async fn message(
&self,
thread: &presage::store::Thread,
timestamp: u64,
) -> Result<Option<presage::libsignal_service::prelude::Content>, Self::ContentsStoreError>
{
todo!()
}

async fn messages(
&self,
thread: &presage::store::Thread,
range: impl std::ops::RangeBounds<u64>,
) -> Result<Self::MessagesIter, Self::ContentsStoreError> {
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<Self::ContactsIter, Self::ContentsStoreError> {
todo!()
}

async fn contact_by_id(
&self,
id: &presage::libsignal_service::prelude::Uuid,
) -> Result<Option<presage::model::contacts::Contact>, 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<presage::model::groups::Group>,
) -> Result<(), Self::ContentsStoreError> {
todo!()
}

async fn groups(&self) -> Result<Self::GroupsIter, Self::ContentsStoreError> {
todo!()
}

async fn group(
&self,
master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes,
) -> Result<Option<presage::model::groups::Group>, 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<Option<presage::AvatarBytes>, Self::ContentsStoreError> {
todo!()
}

async fn upsert_profile_key(
&mut self,
uuid: &presage::libsignal_service::prelude::Uuid,
key: presage::libsignal_service::prelude::ProfileKey,
) -> Result<bool, Self::ContentsStoreError> {
todo!()
}

async fn profile_key(
&self,
uuid: &presage::libsignal_service::prelude::Uuid,
) -> Result<Option<presage::libsignal_service::prelude::ProfileKey>, 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<Option<presage::libsignal_service::Profile>, 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<Option<presage::AvatarBytes>, 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<Option<presage::store::StickerPack>, Self::ContentsStoreError> {
todo!()
}

async fn remove_sticker_pack(&mut self, id: &[u8]) -> Result<bool, Self::ContentsStoreError> {
todo!()
}

async fn sticker_packs(&self) -> Result<Self::StickerPacksIter, Self::ContentsStoreError> {
todo!()
}
}

pub struct DummyIter<T> {
_data: PhantomData<T>,
}

impl<T> Iterator for DummyIter<T> {
type Item = T;

fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
11 changes: 11 additions & 0 deletions presage-store-sqlite/src/error.rs
Original file line number Diff line number Diff line change
@@ -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 {}
101 changes: 101 additions & 0 deletions presage-store-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Path>,
trust_new_identities: OnNewIdentity,
) -> Result<Self, SqliteStoreError> {
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<Option<presage::manager::RegistrationData>, 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!()
}
}
283 changes: 283 additions & 0 deletions presage-store-sqlite/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -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<Option<SessionRecord>, 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<Vec<u32>, 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<usize, ProtocolError> {
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<PreKeyRecord, ProtocolError> {
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<u32, ProtocolError> {
todo!()
}

/// ID of the next signed pre key
async fn next_signed_pre_key_id(&self) -> Result<u32, ProtocolError> {
todo!()
}

/// ID of the next PQ pre key
async fn next_pq_pre_key_id(&self) -> Result<u32, ProtocolError> {
todo!()
}

/// number of signed pre-keys we currently have in store
async fn signed_pre_keys_count(&self) -> Result<usize, ProtocolError> {
todo!()
}

/// number of kyber pre-keys we currently have in store
async fn kyber_pre_keys_count(&self, last_resort: bool) -> Result<usize, ProtocolError> {
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<SignedPreKeyRecord, ProtocolError> {
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<KyberPreKeyRecord, ProtocolError> {
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<Vec<KyberPreKeyRecord>, 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<Utc>,
) -> Result<(), ProtocolError> {
todo!()
}

/// Analogue of deleteAllStaleOneTimeKyberPreKeys
async fn delete_all_stale_one_time_kyber_pre_keys(
&mut self,
threshold: DateTime<Utc>,
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<IdentityKeyPair, ProtocolError> {
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<u32, ProtocolError> {
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<bool, ProtocolError> {
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<bool, ProtocolError> {
todo!()
}

/// Return the public identity for the given `address`, if known.
async fn get_identity(
&self,
address: &ProtocolAddress,
) -> Result<Option<IdentityKey>, 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<Option<SenderKeyRecord>, ProtocolError> {
todo!()
}
}
3 changes: 2 additions & 1 deletion presage/src/manager/linking.rs
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@ impl<S: Store> Manager<S, Linking> {
/// 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<dyn std::error::Error>> {
4 changes: 3 additions & 1 deletion presage/src/manager/registration.rs
Original file line number Diff line number Diff line change
@@ -38,7 +38,9 @@ impl<S: Store> Manager<S, Registration> {
/// };
/// 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<dyn std::error::Error>> {
6 changes: 6 additions & 0 deletions presage/src/model/identity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// Whether to trust or reject new identities
#[derive(Debug, Clone)]
pub enum OnNewIdentity {
Reject,
Trust,
}
1 change: 1 addition & 0 deletions presage/src/model/mod.rs
Original file line number Diff line number Diff line change
@@ -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 {

0 comments on commit 549f9fb

Please sign in to comment.