mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-09-01 07:04:07 +00:00
fix multiple bugs in the POC
This commit is contained in:
parent
484b43cd08
commit
3345a2a297
11 changed files with 133 additions and 267 deletions
10
rust/Cargo.lock
generated
10
rust/Cargo.lock
generated
|
|
@ -1801,7 +1801,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "libsignal-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/signalapp/libsignal#622d0d52471f3cc9215fe4e9abaac1970018f79c"
|
||||
source = "git+https://github.com/signalapp/libsignal?rev=44a6dd8fc9f9d6903b32842b80b3894a75678418#44a6dd8fc9f9d6903b32842b80b3894a75678418"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"derive_more",
|
||||
|
|
@ -1819,8 +1819,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libsignal-debug"
|
||||
version = "0.99.3"
|
||||
source = "git+https://github.com/signalapp/libsignal#622d0d52471f3cc9215fe4e9abaac1970018f79c"
|
||||
version = "0.97.5"
|
||||
source = "git+https://github.com/signalapp/libsignal?rev=44a6dd8fc9f9d6903b32842b80b3894a75678418#44a6dd8fc9f9d6903b32842b80b3894a75678418"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
|
@ -1828,7 +1828,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "libsignal-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/signalapp/libsignal#622d0d52471f3cc9215fe4e9abaac1970018f79c"
|
||||
source = "git+https://github.com/signalapp/libsignal?rev=44a6dd8fc9f9d6903b32842b80b3894a75678418#44a6dd8fc9f9d6903b32842b80b3894a75678418"
|
||||
dependencies = [
|
||||
"aes 0.9.2",
|
||||
"aes-gcm-siv",
|
||||
|
|
@ -2776,7 +2776,7 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|||
[[package]]
|
||||
name = "signal-crypto"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/signalapp/libsignal#622d0d52471f3cc9215fe4e9abaac1970018f79c"
|
||||
source = "git+https://github.com/signalapp/libsignal?rev=44a6dd8fc9f9d6903b32842b80b3894a75678418#44a6dd8fc9f9d6903b32842b80b3894a75678418"
|
||||
dependencies = [
|
||||
"aes 0.9.2",
|
||||
"cbc",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ sqlx = { version = "0.9.0-alpha.1", default-features = false, features = [
|
|||
libsqlite3-sys = { version = "0.35.0", features = [
|
||||
"bundled-sqlcipher-vendored-openssl",
|
||||
] }
|
||||
tokio = { version = "1.44", features = ["full"] }
|
||||
tokio = { version = "1.44", features = ["full", "macros"] }
|
||||
tracing = "0.1.44"
|
||||
prost = "0.14.1"
|
||||
blahaj = "0.6.0"
|
||||
|
|
@ -43,7 +43,7 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||
zip = { version = "2.2.2", default-features = false, features = ["deflate"] }
|
||||
scrypt = { version = "0.12", default-features = false }
|
||||
walkdir = "2.5.0"
|
||||
libsignal-protocol = { git = "https://github.com/signalapp/libsignal", version = "0.1.0" }
|
||||
libsignal-protocol = { git = "https://github.com/signalapp/libsignal", version = "0.1.0", rev = "44a6dd8fc9f9d6903b32842b80b3894a75678418" }
|
||||
rand08 = { version = "0.8.5", package = "rand" }
|
||||
rand = "0.9.4"
|
||||
async-trait = "0.1.91"
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
mod types;
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Send from the person who tries to recover their account.
|
||||
/// This can be done via a link, which will then be opened in the app of the contact.
|
||||
/// The contact then has to manually select from which user he got the request.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RecoveryRequest {
|
||||
pub temp_id: i64,
|
||||
pub public_key: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Used as envelope for TrustedFriendShare and RecoveryData
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct EncryptedEnvelope {
|
||||
pub encrypted_data: Vec<u8>,
|
||||
pub iv: Vec<u8>,
|
||||
pub mac: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct User {
|
||||
pub user_id: i64,
|
||||
pub display_name: String,
|
||||
pub avatar: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Send from the trusted friend.
|
||||
/// This is encrypted with the received public key.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TrustedFriendShare {
|
||||
/// This allows to display the user which user has send him his recovery data.
|
||||
pub trusted_friend: User,
|
||||
/// This allows to display the userdata, showing that he is recovering the correct person.
|
||||
pub share_user: User,
|
||||
/// The minimum threshold required to decrypt the shares.
|
||||
pub threshold: i32,
|
||||
/// The actual share which will become: SecretSharedData
|
||||
pub share: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SecondFactorPin {
|
||||
/// Required to try the PIN to get the share from the server.
|
||||
/// This prevents that someone else can lock the pin, as the server only
|
||||
/// allows 3 tries then after 1 day again 3 tries until the key is deleted.
|
||||
pub unlock_token: Vec<u8>,
|
||||
/// This never is send to the server but used to hash the pin before sending it to the server.
|
||||
/// This prevents that the server every knows the short 4-digit PIN.
|
||||
pub pin_seed: Vec<u8>,
|
||||
/// The recovery data in case a second factor was used
|
||||
/// The decryption key is loaded from the server either using the PIN or the MAIL
|
||||
pub recovery_data_encrypted: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SecondFactorMail {
|
||||
/// The users selected mail which will be send to the server
|
||||
/// To this mail the encryption key for the recovery_data is send
|
||||
pub mail: String,
|
||||
/// Required to try the PIN to get the share from the server.
|
||||
/// This prevents that someone else can lock the pin, as the server only
|
||||
/// allows 3 tries then after 1 day again 3 tries until the key is deleted.
|
||||
pub unlock_token: Vec<u8>,
|
||||
/// The recovery data in case a second factor was used
|
||||
/// The decryption key is loaded from the server either using the PIN or the MAIL
|
||||
pub recovery_data_encrypted: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum SecretSharedData {
|
||||
None(RecoveryData),
|
||||
Mail(SecondFactorMail),
|
||||
Pin(SecondFactorPin),
|
||||
}
|
||||
|
||||
/// The data which is recovered at the end.
|
||||
/// The backup_master_key allows to recover the actual backup uploaded in the background to the server.
|
||||
/// In case the backup is not available any more the user can use its user_id and his private_key to register as a new user.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RecoveryData {
|
||||
pub user_id: i64,
|
||||
pub master_key: Vec<u8>,
|
||||
}
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
pub(crate) mod backup_archive;
|
||||
pub(crate) mod backup_identity;
|
||||
pub(crate) mod backup_passwordless;
|
||||
|
|
|
|||
|
|
@ -12,29 +12,33 @@ impl FlutterUserDiscovery {
|
|||
public_key: Vec<u8>,
|
||||
share_promotion: bool,
|
||||
) -> Result<()> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
tracing::info!("Rust bridge: initialize_or_update started");
|
||||
let twonly = get_twonly_flutter()?;
|
||||
tracing::info!("Rust bridge: getting user_discovery lock");
|
||||
let user_discovery = twonly.user_discovery.get().await;
|
||||
tracing::info!("Rust bridge: calling initialize_or_update on protocols");
|
||||
let res = user_discovery
|
||||
.initialize_or_update(threshold, user_id, public_key, share_promotion)
|
||||
.await;
|
||||
tracing::info!("Rust bridge: initialize_or_update on protocols finished");
|
||||
Ok(res?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
tracing::info!("Rust bridge: initialize_or_update started");
|
||||
let twonly = get_twonly_flutter()?;
|
||||
tracing::info!("Rust bridge: getting user_discovery lock");
|
||||
let user_discovery = twonly.user_discovery.get().await;
|
||||
tracing::info!("Rust bridge: calling initialize_or_update on protocols");
|
||||
let res = user_discovery
|
||||
.initialize_or_update(threshold, user_id, public_key, share_promotion)
|
||||
.await;
|
||||
tracing::info!("Rust bridge: initialize_or_update on protocols finished");
|
||||
Ok(res?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_current_version(callback_id: u32) -> Result<Vec<u8>> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.get_current_version()
|
||||
.await?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.get_current_version()
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_new_messages(
|
||||
|
|
@ -42,14 +46,16 @@ impl FlutterUserDiscovery {
|
|||
contact_id: i64,
|
||||
received_version: &[u8],
|
||||
) -> Result<Vec<Vec<u8>>> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.get_new_messages(contact_id, received_version)
|
||||
.await?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.get_new_messages(contact_id, received_version)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn should_request_new_messages(
|
||||
|
|
@ -57,14 +63,16 @@ impl FlutterUserDiscovery {
|
|||
contact_id: i64,
|
||||
version: &[u8],
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.should_request_new_messages(contact_id, version)
|
||||
.await?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.should_request_new_messages(contact_id, version)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn handle_new_messages(
|
||||
|
|
@ -73,14 +81,16 @@ impl FlutterUserDiscovery {
|
|||
public_key_verified_timestamp: Option<i64>,
|
||||
messages: Vec<Vec<u8>>,
|
||||
) -> Result<()> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.handle_new_messages(contact_id, public_key_verified_timestamp, messages)
|
||||
.await?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.handle_new_messages(contact_id, public_key_verified_timestamp, messages)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_verification_state_for_user(
|
||||
|
|
@ -88,13 +98,15 @@ impl FlutterUserDiscovery {
|
|||
contact_id: i64,
|
||||
public_key_verified_timestamp: Option<i64>,
|
||||
) -> Result<()> {
|
||||
CURRENT_CALLBACK_ID.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.update_verification_state_for_user(contact_id, public_key_verified_timestamp)
|
||||
.await?)
|
||||
}).await
|
||||
CURRENT_CALLBACK_ID
|
||||
.scope(callback_id, async move {
|
||||
Ok(get_twonly_flutter()?
|
||||
.user_discovery
|
||||
.get()
|
||||
.await
|
||||
.update_verification_state_for_user(contact_id, public_key_verified_timestamp)
|
||||
.await?)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
-- Signal Identities (Stores remote IdentityKeys)
|
||||
CREATE TABLE IF NOT EXISTS signal_identities (
|
||||
name TEXT NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
identity_key BLOB NOT NULL,
|
||||
timestamp INTEGER NOT NULL,
|
||||
PRIMARY KEY (name, device_id)
|
||||
PRIMARY KEY (name)
|
||||
);
|
||||
|
||||
-- Signal PreKeys (Stores local one-time prekeys)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::signal::store::DbSignalProtocolStore;
|
|||
|
||||
pub struct RustSignalEngine {
|
||||
store: Arc<Mutex<DbSignalProtocolStore>>,
|
||||
local_name: String,
|
||||
}
|
||||
|
||||
pub struct FrbPreKeyBundle {
|
||||
|
|
@ -30,7 +31,7 @@ pub struct FrbPreKeyBundle {
|
|||
}
|
||||
|
||||
impl RustSignalEngine {
|
||||
pub async fn new() -> Result<Self> {
|
||||
pub async fn new(local_name: String) -> Result<Self> {
|
||||
let twonly = crate::bridge::get_twonly_flutter()?;
|
||||
let pool = twonly.rust_db.pool.clone();
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ impl RustSignalEngine {
|
|||
|
||||
Ok(Self {
|
||||
store: Arc::new(Mutex::new(store)),
|
||||
local_name,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +61,7 @@ impl RustSignalEngine {
|
|||
pool: sqlx::SqlitePool,
|
||||
identity_key_pair_bytes: Vec<u8>,
|
||||
local_registration_id: u32,
|
||||
local_name: String,
|
||||
) -> Result<Self> {
|
||||
let identity_key_pair = IdentityKeyPair::try_from(&identity_key_pair_bytes[..])
|
||||
.map_err(|e| TwonlyError::Signal(e.to_string()))?;
|
||||
|
|
@ -67,6 +70,7 @@ impl RustSignalEngine {
|
|||
|
||||
Ok(Self {
|
||||
store: Arc::new(Mutex::new(store)),
|
||||
local_name,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +84,7 @@ impl RustSignalEngine {
|
|||
&self,
|
||||
pre_key_id: u32,
|
||||
signed_pre_key_id: u32,
|
||||
kyber_pre_key_id: u32,
|
||||
) -> Result<FrbPreKeyBundle> {
|
||||
let mut store_guard = self.store.lock().await;
|
||||
let store = &mut *store_guard;
|
||||
|
|
@ -110,7 +115,7 @@ impl RustSignalEngine {
|
|||
let timestamp = Timestamp::from_epoch_millis(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.map_err(|e| TwonlyError::Signal(e.to_string()))?
|
||||
.as_millis() as u64,
|
||||
);
|
||||
store
|
||||
|
|
@ -127,7 +132,6 @@ impl RustSignalEngine {
|
|||
.await
|
||||
.map_err(|e| TwonlyError::Signal(e.to_string()))?;
|
||||
|
||||
let kyber_pre_key_id = 1;
|
||||
let kyber_key_pair = libsignal_protocol::kem::KeyPair::generate(
|
||||
libsignal_protocol::kem::KeyType::Kyber1024,
|
||||
&mut csprng,
|
||||
|
|
@ -143,7 +147,9 @@ impl RustSignalEngine {
|
|||
&mut csprng,
|
||||
)
|
||||
.map_err(|e| TwonlyError::Signal(e.to_string()))?;
|
||||
let kyber_sig_arr: [u8; 64] = kyber_signature[..].try_into().unwrap();
|
||||
let kyber_sig_arr: [u8; 64] = kyber_signature[..]
|
||||
.try_into()
|
||||
.map_err(|e: std::array::TryFromSliceError| TwonlyError::Signal(e.to_string()))?;
|
||||
store
|
||||
.kyber_pre_key_store
|
||||
.save_kyber_pre_key(
|
||||
|
|
@ -196,8 +202,11 @@ impl RustSignalEngine {
|
|||
let d_id = DeviceId::try_from(device_id)
|
||||
.map_err(|_| TwonlyError::Generic(format!("Invalid device id: {}", device_id)))?;
|
||||
let remote_address = ProtocolAddress::new(name, d_id);
|
||||
let local_address =
|
||||
ProtocolAddress::new("local".to_string(), DeviceId::try_from(1).unwrap());
|
||||
let local_address = ProtocolAddress::new(
|
||||
self.local_name.clone(),
|
||||
DeviceId::try_from(1)
|
||||
.map_err(|_| TwonlyError::Generic("Invalid device id 1".to_string()))?,
|
||||
);
|
||||
|
||||
let identity_key = IdentityKey::decode(&bundle.identity_key)
|
||||
.map_err(|e| TwonlyError::Signal(e.to_string()))?;
|
||||
|
|
@ -221,7 +230,9 @@ impl RustSignalEngine {
|
|||
|
||||
let pre_key_bundle = PreKeyBundle::new(
|
||||
bundle.registration_id,
|
||||
DeviceId::try_from(bundle.device_id).unwrap_or(DeviceId::try_from(1).unwrap()),
|
||||
DeviceId::try_from(bundle.device_id).map_err(|_| {
|
||||
TwonlyError::Generic(format!("Invalid device id: {}", bundle.device_id))
|
||||
})?,
|
||||
pre_key,
|
||||
SignedPreKeyId::from(bundle.signed_pre_key_id),
|
||||
signed_pre_key_public,
|
||||
|
|
@ -261,8 +272,11 @@ impl RustSignalEngine {
|
|||
let d_id = DeviceId::try_from(device_id)
|
||||
.map_err(|_| TwonlyError::Generic(format!("Invalid device id: {}", device_id)))?;
|
||||
let remote_address = ProtocolAddress::new(name, d_id);
|
||||
let local_address =
|
||||
ProtocolAddress::new("local".to_string(), DeviceId::try_from(1).unwrap());
|
||||
let local_address = ProtocolAddress::new(
|
||||
self.local_name.clone(),
|
||||
DeviceId::try_from(1)
|
||||
.map_err(|_| TwonlyError::Generic("Invalid device id 1".to_string()))?,
|
||||
);
|
||||
|
||||
let mut csprng = rand::rng();
|
||||
let now = SystemTime::now();
|
||||
|
|
@ -295,8 +309,11 @@ impl RustSignalEngine {
|
|||
let d_id = DeviceId::try_from(device_id)
|
||||
.map_err(|_| TwonlyError::Generic(format!("Invalid device id: {}", device_id)))?;
|
||||
let remote_address = ProtocolAddress::new(name, d_id);
|
||||
let local_address =
|
||||
ProtocolAddress::new("local".to_string(), DeviceId::try_from(1).unwrap());
|
||||
let local_address = ProtocolAddress::new(
|
||||
self.local_name.clone(),
|
||||
DeviceId::try_from(1)
|
||||
.map_err(|_| TwonlyError::Generic("Invalid device id 1".to_string()))?,
|
||||
);
|
||||
|
||||
let plaintext = if is_prekey_message {
|
||||
let message = PreKeySignalMessage::try_from(&ciphertext_bytes[..])
|
||||
|
|
|
|||
|
|
@ -57,21 +57,18 @@ impl IdentityKeyStore for DbIdentityKeyStore {
|
|||
identity: &IdentityKey,
|
||||
) -> Result<IdentityChange, SignalProtocolError> {
|
||||
let name = address.name();
|
||||
let device_id: u32 = address.device_id().into();
|
||||
let identity_bytes = identity.serialize();
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?
|
||||
.as_millis() as i64;
|
||||
|
||||
let existing: Option<(Vec<u8>,)> = sqlx::query_as(
|
||||
"SELECT identity_key FROM signal_identities WHERE name = ? AND device_id = ?",
|
||||
)
|
||||
.bind(name)
|
||||
.bind(device_id)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
let existing: Option<(Vec<u8>,)> =
|
||||
sqlx::query_as("SELECT identity_key FROM signal_identities WHERE name = ?")
|
||||
.bind(name)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
|
||||
let changed = if let Some(row) = existing {
|
||||
row.0 != identity_bytes.as_ref()
|
||||
|
|
@ -79,9 +76,8 @@ impl IdentityKeyStore for DbIdentityKeyStore {
|
|||
false
|
||||
};
|
||||
|
||||
sqlx::query("INSERT INTO signal_identities (name, device_id, identity_key, timestamp) VALUES (?, ?, ?, ?) ON CONFLICT(name, device_id) DO UPDATE SET identity_key = excluded.identity_key, timestamp = excluded.timestamp")
|
||||
sqlx::query("INSERT INTO signal_identities (name, identity_key, timestamp) VALUES (?, ?, ?) ON CONFLICT(name) DO UPDATE SET identity_key = excluded.identity_key, timestamp = excluded.timestamp")
|
||||
.bind(name)
|
||||
.bind(device_id)
|
||||
.bind(identity_bytes.as_ref())
|
||||
.bind(timestamp)
|
||||
.execute(&self.pool)
|
||||
|
|
@ -98,17 +94,14 @@ impl IdentityKeyStore for DbIdentityKeyStore {
|
|||
_direction: Direction,
|
||||
) -> Result<bool, SignalProtocolError> {
|
||||
let name = address.name();
|
||||
let device_id: u32 = address.device_id().into();
|
||||
let identity_bytes = identity.serialize();
|
||||
|
||||
let row: Option<(Vec<u8>,)> = sqlx::query_as(
|
||||
"SELECT identity_key FROM signal_identities WHERE name = ? AND device_id = ?",
|
||||
)
|
||||
.bind(name)
|
||||
.bind(device_id)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
let row: Option<(Vec<u8>,)> =
|
||||
sqlx::query_as("SELECT identity_key FROM signal_identities WHERE name = ?")
|
||||
.bind(name)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
|
||||
if let Some((stored_key,)) = row {
|
||||
Ok(stored_key == identity_bytes.as_ref())
|
||||
|
|
@ -122,16 +115,13 @@ impl IdentityKeyStore for DbIdentityKeyStore {
|
|||
address: &ProtocolAddress,
|
||||
) -> Result<Option<IdentityKey>, SignalProtocolError> {
|
||||
let name = address.name();
|
||||
let device_id: u32 = address.device_id().into();
|
||||
|
||||
let row: Option<(Vec<u8>,)> = sqlx::query_as(
|
||||
"SELECT identity_key FROM signal_identities WHERE name = ? AND device_id = ?",
|
||||
)
|
||||
.bind(name)
|
||||
.bind(device_id)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
let row: Option<(Vec<u8>,)> =
|
||||
sqlx::query_as("SELECT identity_key FROM signal_identities WHERE name = ?")
|
||||
.bind(name)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|_| SignalProtocolError::UntrustedIdentity(address.clone()))?;
|
||||
|
||||
if let Some((bytes,)) = row {
|
||||
let key = IdentityKey::decode(&bytes)
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
# User Discovery
|
||||
|
||||
User Discovery is a feature that allows users to discover other users in a decentralized system without any central authority. It uses Shamir's Secret Sharing to securely share and discover user information.
|
||||
|
||||
## Getting started
|
||||
|
||||
The User Discovery module is composed of the following components:
|
||||
|
||||
- **UserDiscovery** - The main struct which initializes the user discovery and provides access to the user discovery functionality.
|
||||
|
||||
- **UserDiscoveryStore** - A trait which has to be implemented. It is used to store and retrieve the user discovery data.
|
||||
- **UserDiscoveryUtils** - A trait which has to be implemented. It is used to perform signature verification and signing.
|
||||
|
||||
```rust
|
||||
use crate::user_discovery::{UserDiscovery, UserID};
|
||||
use crate::user_discovery::stores::InMemoryStore; // Replace with your persistent store
|
||||
use crate::user_discovery::traits::tests::TestingUtils; // Replace with your utils
|
||||
|
||||
const THRESHOLD: u8 = 2;
|
||||
|
||||
// Initialize user discovery for Alice
|
||||
const ALICE_ID: UserID = 1;
|
||||
let alice_ud = UserDiscovery::new(InMemoryStore::default(), TestingUtils::default()).unwrap();
|
||||
|
||||
// Set threshold, user ID, and the user's public key
|
||||
alice_ud.initialize_or_update(THRESHOLD, ALICE_ID, vec![0; 32]).unwrap();
|
||||
|
||||
// Initialize user discovery for Bob
|
||||
const BOB_ID: UserID = 2;
|
||||
let bob_ud = UserDiscovery::new(InMemoryStore::default(), TestingUtils::default()).unwrap();
|
||||
bob_ud.initialize_or_update(THRESHOLD, BOB_ID, vec![0; 32]).unwrap();
|
||||
|
||||
|
||||
|
||||
// Simulate network communication: Alice sends her current version to Bob
|
||||
let bob_received_version_from_alice = alice_ud.get_current_version().unwrap();
|
||||
|
||||
// SEND FROM ALICE TO BOB: bob_received_version_from_alice
|
||||
|
||||
// Bob checks if he should request new messages
|
||||
if bob_ud.should_request_new_messages(ALICE_ID, &bob_received_version_from_alice).unwrap() {
|
||||
|
||||
// Bob has a old version and must now request to get the new messages
|
||||
|
||||
// Bob fetches his current known version and sends it via the network to Alice
|
||||
let bob_stored_alice_version = bob_ud.get_contact_version(ALICE_ID)
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| vec![0, 0]); // Note: In practice use actual default encoded version
|
||||
|
||||
// SEND FROM BOB TO ALICE: bob_stored_alice_version
|
||||
|
||||
// Alice loads the new messages for Bob. These only conclude changes since the provided version.
|
||||
let new_messages = alice_ud.get_new_messages(BOB_ID, &bob_stored_alice_version).unwrap();
|
||||
|
||||
// SEND FROM ALICE TO BOB: new_messages
|
||||
|
||||
// Bob processes the received user discovery messages
|
||||
bob_ud.handle_user_discovery_messages(ALICE_ID, new_messages).unwrap();
|
||||
|
||||
// BOB is now able to promote ALICE to his other contacts
|
||||
}
|
||||
|
||||
// <Involve more users>
|
||||
|
||||
// 4. Retrieve all newly discovered users and relationships
|
||||
// In this example now new users where discovered, to see a more comprehensive example
|
||||
// see the test in the `mod.rs` fil.
|
||||
let discovered_users = bob_ud.get_all_announced_users().unwrap();
|
||||
for (user, connections) in discovered_users {
|
||||
println!("Discovered User: {} (Public ID: {})", user.user_id, user.public_id);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -17,13 +17,21 @@ async fn test_twonly_api_100_messages() -> Result<(), Box<dyn std::error::Error>
|
|||
let bob_identity_bytes = RustSignalEngine::generate_identity_key_pair()?;
|
||||
|
||||
// 3. Initialize engines with the DB pools
|
||||
let alice_engine =
|
||||
RustSignalEngine::new_with_pool(alice_db.pool.clone(), alice_identity_bytes, 1234)?;
|
||||
let bob_engine =
|
||||
RustSignalEngine::new_with_pool(bob_db.pool.clone(), bob_identity_bytes, 5678)?;
|
||||
let alice_engine = RustSignalEngine::new_with_pool(
|
||||
alice_db.pool.clone(),
|
||||
alice_identity_bytes,
|
||||
1234,
|
||||
"alice".to_string(),
|
||||
)?;
|
||||
let bob_engine = RustSignalEngine::new_with_pool(
|
||||
bob_db.pool.clone(),
|
||||
bob_identity_bytes,
|
||||
5678,
|
||||
"bob".to_string(),
|
||||
)?;
|
||||
|
||||
// 4. Bob generates a bundle
|
||||
let bob_bundle = bob_engine.generate_bundle(1, 1).await?;
|
||||
let bob_bundle = bob_engine.generate_bundle(1, 1, 1).await?;
|
||||
|
||||
// 5. Alice processes Bob's bundle
|
||||
alice_engine
|
||||
|
|
|
|||
Loading…
Reference in a new issue