signal_bridge/
lib.rs

1//! Signal Protocol integration for Radix Relay
2//!
3//! This crate provides a bridge between Radix Relay's C++ transport layer
4//! and the official Signal Protocol Rust implementation for end-to-end encryption.
5
6mod contact_manager;
7mod db_encryption;
8mod encryption_trait;
9pub mod key_rotation;
10mod keys;
11pub mod memory_storage;
12pub mod message_history;
13mod nostr_identity;
14mod session_trait;
15pub mod sqlite_storage;
16pub mod storage_trait;
17
18#[cfg(test)]
19mod session;
20
21#[cfg(test)]
22mod encryption;
23
24#[cfg(test)]
25mod sqlcipher_tests;
26
27#[cfg(test)]
28mod message_history_tests;
29
30pub use contact_manager::{ContactInfo, ContactManager};
31pub use key_rotation::{
32    cleanup_expired_kyber_pre_keys, cleanup_expired_signed_pre_keys, consume_pre_key,
33    kyber_pre_key_needs_rotation, replenish_pre_keys, rotate_kyber_pre_key, rotate_signed_pre_key,
34    signed_pre_key_needs_rotation, GRACE_PERIOD_SECS, MIN_PRE_KEY_COUNT, REPLENISH_COUNT,
35    ROTATION_INTERVAL_SECS,
36};
37pub use message_history::{
38    Conversation, DeliveryStatus, MessageDirection, MessageHistory, MessageType, StoredMessage,
39};
40
41/// Result of key maintenance operations indicating which keys were rotated/replenished
42#[derive(Debug, Clone, Default)]
43pub struct KeyMaintenanceResult {
44    /// True if a new signed pre-key was generated
45    pub signed_pre_key_rotated: bool,
46    /// True if a new Kyber pre-key was generated
47    pub kyber_pre_key_rotated: bool,
48    /// True if one-time pre-keys were replenished
49    pub pre_keys_replenished: bool,
50}
51pub use memory_storage::MemoryStorage;
52pub use nostr_identity::NostrIdentity;
53pub use sqlite_storage::SqliteStorage;
54pub use storage_trait::{
55    ExtendedIdentityStore, ExtendedKyberPreKeyStore, ExtendedPreKeyStore, ExtendedSessionStore,
56    ExtendedSignedPreKeyStore, ExtendedStorageOps, SignalStorageContainer,
57};
58
59use base64::Engine;
60use libsignal_protocol::{
61    CiphertextMessage, DeviceId, IdentityKeyPair, IdentityKeyStore, ProtocolAddress, SessionStore,
62};
63use nostr::{EventBuilder, Keys, Kind, Tag};
64use serde::{Deserialize, Serialize};
65use std::time::{SystemTime, UNIX_EPOCH};
66
67#[derive(Serialize, Deserialize, Clone)]
68struct SerializablePreKeyBundle {
69    pub registration_id: u32,
70    pub device_id: u32,
71    pub pre_key_id: Option<u32>,
72    pub pre_key_public: Option<Vec<u8>>,
73    pub signed_pre_key_id: u32,
74    pub signed_pre_key_public: Vec<u8>,
75    pub signed_pre_key_signature: Vec<u8>,
76    pub identity_key: Vec<u8>,
77    pub kyber_pre_key_id: u32,
78    pub kyber_pre_key_public: Vec<u8>,
79    pub kyber_pre_key_signature: Vec<u8>,
80}
81
82/// Result of decrypting an incoming message
83pub struct DecryptionResult {
84    /// Decrypted plaintext bytes
85    pub plaintext: Vec<u8>,
86    /// Whether the sender needs this node to republish its bundle
87    pub should_republish_bundle: bool,
88}
89
90/// Main bridge between C++ and Rust Signal Protocol implementation
91pub struct SignalBridge {
92    /// SQLite-backed Signal Protocol storage
93    pub(crate) storage: SqliteStorage,
94    /// Contact/peer management
95    contact_manager: ContactManager,
96}
97
98impl SignalBridge {
99    async fn ensure_keys_exist(
100        storage: &mut SqliteStorage,
101    ) -> Result<IdentityKeyPair, SignalBridgeError> {
102        use crate::keys::{generate_identity_key_pair, generate_pre_keys, generate_signed_pre_key};
103        use crate::storage_trait::{
104            ExtendedIdentityStore, ExtendedKyberPreKeyStore, ExtendedPreKeyStore,
105            ExtendedSignedPreKeyStore,
106        };
107        use libsignal_protocol::*;
108
109        let identity_key_pair = match storage.identity_store().get_identity_key_pair().await {
110            Ok(existing_identity) => existing_identity,
111            Err(_) => {
112                let new_identity = generate_identity_key_pair().await?;
113                let new_registration_id = rand::random::<u32>();
114
115                storage
116                    .identity_store()
117                    .set_local_identity_key_pair(&new_identity)
118                    .await?;
119                storage
120                    .identity_store()
121                    .set_local_registration_id(new_registration_id)
122                    .await?;
123
124                new_identity
125            }
126        };
127
128        let current_pre_key_count = storage.pre_key_store().pre_key_count().await;
129        if current_pre_key_count < 5 {
130            // Generate 100 pre-keys (2x MIN_PRE_KEY_COUNT) to provide a comfortable buffer
131            let pre_keys = generate_pre_keys(1, 100).await?;
132            for (key_id, key_pair) in &pre_keys {
133                let record = PreKeyRecord::new((*key_id).into(), key_pair);
134                storage
135                    .pre_key_store()
136                    .save_pre_key((*key_id).into(), &record)
137                    .await?;
138            }
139        }
140
141        let current_signed_pre_key_count =
142            storage.signed_pre_key_store().signed_pre_key_count().await;
143        if current_signed_pre_key_count == 0 {
144            let signed_pre_key = generate_signed_pre_key(&identity_key_pair, 1).await?;
145            storage
146                .signed_pre_key_store()
147                .save_signed_pre_key(signed_pre_key.id()?, &signed_pre_key)
148                .await?;
149        }
150
151        let current_kyber_pre_key_count = storage.kyber_pre_key_store().kyber_pre_key_count().await;
152        if current_kyber_pre_key_count == 0 {
153            let mut rng = rand::rng();
154            let kyber_keypair = kem::KeyPair::generate(kem::KeyType::Kyber1024, &mut rng);
155            let kyber_signature = identity_key_pair
156                .private_key()
157                .calculate_signature(&kyber_keypair.public_key.serialize(), &mut rng)
158                .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?;
159
160            let now = std::time::SystemTime::now();
161            let kyber_record = KyberPreKeyRecord::new(
162                KyberPreKeyId::from(1u32),
163                Timestamp::from_epoch_millis(
164                    now.duration_since(std::time::UNIX_EPOCH)?.as_millis() as u64,
165                ),
166                &kyber_keypair,
167                &kyber_signature,
168            );
169            storage
170                .kyber_pre_key_store()
171                .save_kyber_pre_key(KyberPreKeyId::from(1u32), &kyber_record)
172                .await?;
173        }
174
175        Ok(identity_key_pair)
176    }
177
178    pub async fn new(db_path: &str) -> Result<Self, SignalBridgeError> {
179        if let Some(parent) = std::path::Path::new(db_path).parent() {
180            std::fs::create_dir_all(parent)?;
181        }
182
183        let mut storage = SqliteStorage::new(db_path).await?;
184        storage.initialize()?;
185
186        let schema_version = storage.get_schema_version()?;
187        if schema_version < 1 {
188            return Err(SignalBridgeError::SchemaVersionTooOld);
189        }
190
191        Self::ensure_keys_exist(&mut storage).await?;
192
193        println!("SignalBridge initialized with {} storage, {} existing sessions, {} peer identities, {} pre-keys, {} signed pre-keys, {} kyber pre-keys",
194                 storage.storage_type(),
195                 storage.session_store().session_count().await,
196                 storage.identity_store().identity_count().await,
197                 storage.pre_key_store().pre_key_count().await,
198                 storage.signed_pre_key_store().signed_pre_key_count().await,
199                 storage.kyber_pre_key_store().kyber_pre_key_count().await);
200
201        let contact_manager = ContactManager::new(storage.connection());
202
203        Ok(Self {
204            storage,
205            contact_manager,
206        })
207    }
208
209    pub async fn encrypt_message(
210        &mut self,
211        peer: &str,
212        plaintext: &[u8],
213    ) -> Result<Vec<u8>, SignalBridgeError> {
214        if peer.is_empty() {
215            return Err(SignalBridgeError::InvalidInput(
216                "Specify a peer name".to_string(),
217            ));
218        }
219
220        let session_address = match self
221            .contact_manager
222            .lookup_contact(peer, self.storage.session_store())
223            .await
224        {
225            Ok(contact) => contact.rdx_fingerprint,
226            Err(_) => peer.to_string(),
227        };
228
229        let address = ProtocolAddress::new(
230            session_address.clone(),
231            DeviceId::new(1).map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
232        );
233
234        let session = self.storage.session_store().load_session(&address).await?;
235        if session.is_none() {
236            return Err(SignalBridgeError::SessionNotFound(format!(
237                "Establish a session with {} before sending messages",
238                peer
239            )));
240        }
241
242        let ciphertext = self.storage.encrypt_message(&address, plaintext).await?;
243
244        let timestamp = std::time::SystemTime::now()
245            .duration_since(std::time::UNIX_EPOCH)
246            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?
247            .as_millis() as u64;
248
249        if let Err(e) = self.storage.message_history().store_outgoing_message(
250            &session_address,
251            timestamp,
252            plaintext,
253        ) {
254            eprintln!("Warning: Failed to store outgoing message: {}", e);
255        }
256
257        Ok(ciphertext.serialize().to_vec())
258    }
259
260    pub async fn decrypt_message(
261        &mut self,
262        peer_hint: &str,
263        ciphertext_bytes: &[u8],
264    ) -> Result<DecryptionResult, SignalBridgeError> {
265        use crate::contact_manager::ContactManager;
266        use libsignal_protocol::{PreKeySignalMessage, SignalMessage};
267
268        if ciphertext_bytes.is_empty() {
269            return Err(SignalBridgeError::InvalidInput(
270                "Provide a message to decrypt".to_string(),
271            ));
272        }
273
274        let (session_address, pre_key_consumed, ciphertext) =
275            if let Ok(prekey_msg) = PreKeySignalMessage::try_from(ciphertext_bytes) {
276                let sender_identity = prekey_msg.identity_key();
277                let rdx_fingerprint =
278                    ContactManager::generate_identity_fingerprint_from_key(sender_identity);
279
280                let contact_exists = self
281                    .contact_manager
282                    .lookup_contact(&rdx_fingerprint, self.storage.session_store())
283                    .await
284                    .is_ok();
285
286                if !contact_exists {
287                    self.contact_manager
288                        .add_contact_from_identity_key(sender_identity)
289                        .await?;
290                }
291
292                let pre_key_consumed = prekey_msg.pre_key_id().is_some();
293                (
294                    rdx_fingerprint,
295                    pre_key_consumed,
296                    CiphertextMessage::PreKeySignalMessage(prekey_msg),
297                )
298            } else if let Ok(signal_msg) = SignalMessage::try_from(ciphertext_bytes) {
299                if peer_hint.is_empty() {
300                    return Err(SignalBridgeError::InvalidInput(
301                        "SignalMessage requires peer_hint (Nostr pubkey from event wrapper)"
302                            .to_string(),
303                    ));
304                }
305
306                let contact = self
307                    .contact_manager
308                    .lookup_contact(peer_hint, self.storage.session_store())
309                    .await?;
310
311                (
312                    contact.rdx_fingerprint,
313                    false,
314                    CiphertextMessage::SignalMessage(signal_msg),
315                )
316            } else {
317                return Err(SignalBridgeError::Serialization(
318                    "Provide a valid Signal Protocol message".to_string(),
319                ));
320            };
321
322        let address = ProtocolAddress::new(
323            session_address,
324            DeviceId::new(1).map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
325        );
326
327        let plaintext = self.storage.decrypt_message(&address, &ciphertext).await?;
328
329        let timestamp = std::time::SystemTime::now()
330            .duration_since(std::time::UNIX_EPOCH)
331            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?
332            .as_millis() as u64;
333
334        let session_established = pre_key_consumed;
335
336        if let Err(e) = self.storage.message_history().store_incoming_message(
337            address.name(),
338            timestamp,
339            &plaintext,
340            pre_key_consumed,
341            session_established,
342        ) {
343            eprintln!("Warning: Failed to store incoming message: {}", e);
344        }
345
346        Ok(DecryptionResult {
347            plaintext,
348            should_republish_bundle: pre_key_consumed,
349        })
350    }
351
352    pub async fn establish_session(
353        &mut self,
354        peer: &str,
355        pre_key_bundle_bytes: &[u8],
356    ) -> Result<(), SignalBridgeError> {
357        if peer.is_empty() {
358            return Err(SignalBridgeError::InvalidInput(
359                "Specify a peer name".to_string(),
360            ));
361        }
362
363        if pre_key_bundle_bytes.is_empty() {
364            return Err(SignalBridgeError::InvalidInput(
365                "Provide a pre-key bundle from the peer".to_string(),
366            ));
367        }
368        use crate::storage_trait::ExtendedStorageOps;
369        use libsignal_protocol::*;
370
371        let serializable: SerializablePreKeyBundle = bincode::deserialize(pre_key_bundle_bytes)?;
372
373        let bundle = PreKeyBundle::new(
374            serializable.registration_id,
375            DeviceId::new(serializable.device_id.try_into().map_err(|e| {
376                SignalBridgeError::InvalidInput(format!("Invalid device ID: {}", e))
377            })?)
378            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
379            serializable
380                .pre_key_id
381                .map(|id| {
382                    let public_key =
383                        PublicKey::deserialize(serializable.pre_key_public.as_ref().unwrap())
384                            .map_err(|e| SignalBridgeError::Serialization(e.to_string()))?;
385                    Ok::<_, SignalBridgeError>((PreKeyId::from(id), public_key))
386                })
387                .transpose()?,
388            SignedPreKeyId::from(serializable.signed_pre_key_id),
389            PublicKey::deserialize(&serializable.signed_pre_key_public)
390                .map_err(|e| SignalBridgeError::Serialization(e.to_string()))?,
391            serializable.signed_pre_key_signature,
392            KyberPreKeyId::from(serializable.kyber_pre_key_id),
393            kem::PublicKey::deserialize(&serializable.kyber_pre_key_public)
394                .map_err(|e| SignalBridgeError::Serialization(e.to_string()))?,
395            serializable.kyber_pre_key_signature,
396            IdentityKey::decode(&serializable.identity_key)
397                .map_err(|e| SignalBridgeError::Serialization(e.to_string()))?,
398        )?;
399
400        let address = ProtocolAddress::new(
401            peer.to_string(),
402            DeviceId::new(1).map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
403        );
404
405        self.storage
406            .establish_session_from_bundle(&address, &bundle)
407            .await?;
408
409        Ok(())
410    }
411
412    pub async fn generate_pre_key_bundle(
413        &mut self,
414    ) -> Result<(Vec<u8>, u32, u32, u32), SignalBridgeError> {
415        use crate::storage_trait::{
416            ExtendedKyberPreKeyStore, ExtendedPreKeyStore, ExtendedSignedPreKeyStore,
417        };
418        use libsignal_protocol::*;
419
420        let identity_key = *self
421            .storage
422            .identity_store()
423            .get_identity_key_pair()
424            .await?
425            .identity_key();
426        let registration_id = self
427            .storage
428            .identity_store()
429            .get_local_registration_id()
430            .await?;
431
432        // Use latest available keys (highest IDs) for bundle generation
433        // This ensures bundles reflect the most recent key rotation
434        let pre_key_id = self
435            .storage
436            .pre_key_store()
437            .get_max_pre_key_id()
438            .await?
439            .ok_or_else(|| SignalBridgeError::Protocol("No pre-keys available".to_string()))?;
440        let signed_pre_key_id = self
441            .storage
442            .signed_pre_key_store()
443            .get_max_signed_pre_key_id()
444            .await?
445            .ok_or_else(|| {
446                SignalBridgeError::Protocol("No signed pre-keys available".to_string())
447            })?;
448        let kyber_pre_key_id = self
449            .storage
450            .kyber_pre_key_store()
451            .get_max_kyber_pre_key_id()
452            .await?
453            .ok_or_else(|| {
454                SignalBridgeError::Protocol("No kyber pre-keys available".to_string())
455            })?;
456
457        let pre_key_record = self
458            .storage
459            .pre_key_store()
460            .get_pre_key(PreKeyId::from(pre_key_id))
461            .await?;
462        let signed_pre_key_record = self
463            .storage
464            .signed_pre_key_store()
465            .get_signed_pre_key(SignedPreKeyId::from(signed_pre_key_id))
466            .await?;
467        let kyber_pre_key_record = self
468            .storage
469            .kyber_pre_key_store()
470            .get_kyber_pre_key(KyberPreKeyId::from(kyber_pre_key_id))
471            .await?;
472
473        let bundle = PreKeyBundle::new(
474            registration_id,
475            DeviceId::new(1).map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
476            Some((
477                PreKeyId::from(pre_key_id),
478                pre_key_record.key_pair()?.public_key,
479            )),
480            signed_pre_key_record.id()?,
481            signed_pre_key_record.public_key()?,
482            signed_pre_key_record.signature()?.to_vec(),
483            KyberPreKeyId::from(kyber_pre_key_id),
484            kyber_pre_key_record.key_pair()?.public_key,
485            kyber_pre_key_record.signature()?.to_vec(),
486            identity_key,
487        )?;
488
489        let serializable = SerializablePreKeyBundle {
490            registration_id: bundle.registration_id()?,
491            device_id: bundle.device_id()?.into(),
492            pre_key_id: bundle.pre_key_id()?.map(|id| id.into()),
493            pre_key_public: bundle.pre_key_public()?.map(|key| key.serialize().to_vec()),
494            signed_pre_key_id: bundle.signed_pre_key_id()?.into(),
495            signed_pre_key_public: bundle.signed_pre_key_public()?.serialize().to_vec(),
496            signed_pre_key_signature: bundle.signed_pre_key_signature()?.to_vec(),
497            identity_key: bundle.identity_key()?.serialize().to_vec(),
498            kyber_pre_key_id: bundle.kyber_pre_key_id()?.into(),
499            kyber_pre_key_public: bundle.kyber_pre_key_public()?.serialize().to_vec(),
500            kyber_pre_key_signature: bundle.kyber_pre_key_signature()?.to_vec(),
501        };
502
503        let bundle_bytes = bincode::serialize(&serializable)?;
504        Ok((
505            bundle_bytes,
506            pre_key_id,
507            signed_pre_key_id,
508            kyber_pre_key_id,
509        ))
510    }
511
512    pub async fn cleanup_all_sessions(&mut self) -> Result<(), SignalBridgeError> {
513        let session_count = self.storage.session_store().session_count().await;
514        if session_count > 0 {
515            println!("Cleaning up {} sessions", session_count);
516            self.storage.session_store().clear_all_sessions().await?;
517        }
518        Ok(())
519    }
520
521    pub async fn clear_peer_session(&mut self, peer: &str) -> Result<(), SignalBridgeError> {
522        if peer.is_empty() {
523            return Err(SignalBridgeError::InvalidInput(
524                "Specify a peer name".to_string(),
525            ));
526        }
527        let address = ProtocolAddress::new(
528            peer.to_string(),
529            DeviceId::new(1).map_err(|e| SignalBridgeError::Protocol(e.to_string()))?,
530        );
531
532        self.storage
533            .session_store()
534            .delete_session(&address)
535            .await?;
536
537        if (self
538            .storage
539            .identity_store()
540            .get_peer_identity(&address)
541            .await)
542            .is_ok()
543        {
544            self.storage
545                .identity_store()
546                .delete_identity(&address)
547                .await?;
548        }
549
550        println!("Cleared session and identity for peer: {}", peer);
551        Ok(())
552    }
553
554    pub async fn clear_all_sessions(&mut self) -> Result<(), SignalBridgeError> {
555        let session_count = self.storage.session_store().session_count().await;
556        let identity_count = self.storage.identity_store().identity_count().await;
557
558        if session_count > 0 || identity_count > 0 {
559            self.storage.session_store().clear_all_sessions().await?;
560            self.storage.identity_store().clear_all_identities().await?;
561
562            println!(
563                "Cleared all {} sessions and {} peer identities",
564                session_count, identity_count
565            );
566        }
567        Ok(())
568    }
569
570    pub async fn reset_identity(&mut self) -> Result<(), SignalBridgeError> {
571        use crate::storage_trait::{
572            ExtendedIdentityStore, ExtendedKyberPreKeyStore, ExtendedPreKeyStore,
573            ExtendedSignedPreKeyStore,
574        };
575
576        println!("WARNING: Resetting identity - all existing sessions will be invalidated");
577
578        self.clear_all_sessions().await?;
579
580        self.storage.pre_key_store().clear_all_pre_keys().await?;
581        self.storage
582            .signed_pre_key_store()
583            .clear_all_signed_pre_keys()
584            .await?;
585        self.storage
586            .kyber_pre_key_store()
587            .clear_all_kyber_pre_keys()
588            .await?;
589        self.storage.identity_store().clear_local_identity().await?;
590
591        Self::ensure_keys_exist(&mut self.storage).await?;
592
593        println!("Identity reset complete - new identity generated with fresh keys");
594        Ok(())
595    }
596
597    pub async fn derive_nostr_keypair(&mut self) -> Result<Keys, SignalBridgeError> {
598        let identity_key_pair = self
599            .storage
600            .identity_store()
601            .get_identity_key_pair()
602            .await?;
603        NostrIdentity::derive_from_signal_identity(&identity_key_pair)
604    }
605
606    pub async fn derive_peer_nostr_key(
607        &mut self,
608        peer: &str,
609    ) -> Result<Vec<u8>, SignalBridgeError> {
610        let session_address = match self
611            .contact_manager
612            .lookup_contact(peer, self.storage.session_store())
613            .await
614        {
615            Ok(contact) => contact.rdx_fingerprint,
616            Err(_) => peer.to_string(),
617        };
618
619        let peer_identity = self
620            .storage
621            .identity_store()
622            .get_identity(&ProtocolAddress::new(
623                session_address,
624                DeviceId::new(1).unwrap(),
625            ))
626            .await?
627            .ok_or_else(|| {
628                SignalBridgeError::SessionNotFound(format!("No identity found for peer: {}", peer))
629            })?;
630
631        let peer_nostr_public_key =
632            NostrIdentity::derive_public_key_from_peer_identity(&peer_identity)?;
633        Ok(peer_nostr_public_key.to_bytes().to_vec())
634    }
635
636    pub async fn create_subscription_for_self(
637        &mut self,
638        subscription_id: &str,
639        since_timestamp: u64,
640    ) -> Result<String, SignalBridgeError> {
641        let keys = self.derive_nostr_keypair().await?;
642        let our_pubkey = keys.public_key().to_hex();
643
644        let since = if since_timestamp > 0 {
645            since_timestamp
646        } else {
647            self.storage.get_last_message_timestamp().map_err(|e| {
648                SignalBridgeError::Protocol(format!("Failed to get last message timestamp: {}", e))
649            })?
650        };
651
652        let mut filter = serde_json::json!({
653            "kinds": [40001],
654            "#p": [our_pubkey]
655        });
656
657        if since > 0 {
658            filter
659                .as_object_mut()
660                .unwrap()
661                .insert("since".to_string(), serde_json::json!(since));
662        }
663
664        let subscription = serde_json::json!(["REQ", subscription_id, filter]);
665
666        Ok(subscription.to_string())
667    }
668
669    pub async fn update_last_message_timestamp(
670        &mut self,
671        timestamp: u64,
672    ) -> Result<(), SignalBridgeError> {
673        self.storage
674            .set_last_message_timestamp(timestamp)
675            .map_err(|e| {
676                SignalBridgeError::Protocol(format!(
677                    "Failed to update last message timestamp: {}",
678                    e
679                ))
680            })
681    }
682
683    pub async fn generate_prekey_bundle_announcement(
684        &mut self,
685        project_version: &str,
686    ) -> Result<ffi::BundleInfo, SignalBridgeError> {
687        let (bundle_bytes, pre_key_id, signed_pre_key_id, kyber_pre_key_id) =
688            self.generate_pre_key_bundle().await?;
689
690        let rdx_fingerprint = self
691            .add_contact_from_bundle(&bundle_bytes, Some("self"))
692            .await?;
693
694        let bundle_base64 = base64::engine::general_purpose::STANDARD.encode(&bundle_bytes);
695        let timestamp = SystemTime::now()
696            .duration_since(UNIX_EPOCH)
697            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?
698            .as_secs();
699
700        let tags = vec![
701            vec!["d".to_string(), "radix_prekey_bundle_v1".to_string()],
702            vec!["rdx".to_string(), rdx_fingerprint.clone()],
703            vec!["radix_version".to_string(), project_version.to_string()],
704            vec!["bundle_timestamp".to_string(), timestamp.to_string()],
705        ];
706
707        let (pubkey, event_id, signature) = self
708            .sign_nostr_event(timestamp, 30078, tags.clone(), &bundle_base64)
709            .await?;
710
711        let event = serde_json::json!({
712            "id": event_id,
713            "pubkey": pubkey,
714            "created_at": timestamp,
715            "kind": 30078,
716            "tags": [
717                ["d", "radix_prekey_bundle_v1"],
718                ["rdx", rdx_fingerprint],
719                ["radix_version", project_version],
720                ["bundle_timestamp", timestamp.to_string()],
721            ],
722            "content": bundle_base64,
723            "sig": signature,
724        });
725
726        let announcement_json = serde_json::to_string(&event)
727            .map_err(|e| SignalBridgeError::Serialization(e.to_string()))?;
728
729        Ok(ffi::BundleInfo {
730            announcement_json,
731            pre_key_id,
732            signed_pre_key_id,
733            kyber_pre_key_id,
734        })
735    }
736
737    pub async fn generate_empty_bundle_announcement(
738        &mut self,
739        project_version: &str,
740    ) -> Result<String, SignalBridgeError> {
741        let timestamp = SystemTime::now()
742            .duration_since(UNIX_EPOCH)
743            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?
744            .as_secs();
745
746        let tags = vec![
747            vec!["d".to_string(), "radix_prekey_bundle_v1".to_string()],
748            vec!["radix_version".to_string(), project_version.to_string()],
749        ];
750
751        let (pubkey, event_id, signature) = self
752            .sign_nostr_event(timestamp, 30078, tags.clone(), "")
753            .await?;
754
755        let event = serde_json::json!({
756            "id": event_id,
757            "pubkey": pubkey,
758            "created_at": timestamp,
759            "kind": 30078,
760            "tags": [
761                ["d", "radix_prekey_bundle_v1"],
762                ["radix_version", project_version],
763            ],
764            "content": "",
765            "sig": signature,
766        });
767
768        serde_json::to_string(&event).map_err(|e| SignalBridgeError::Serialization(e.to_string()))
769    }
770
771    pub async fn add_contact_and_establish_session(
772        &mut self,
773        bundle_bytes: &[u8],
774        user_alias: Option<&str>,
775    ) -> Result<String, SignalBridgeError> {
776        use crate::SerializablePreKeyBundle;
777        use libsignal_protocol::IdentityKey;
778
779        let bundle: SerializablePreKeyBundle = bincode::deserialize(bundle_bytes).map_err(|e| {
780            SignalBridgeError::InvalidInput(format!("Failed to deserialize bundle: {}", e))
781        })?;
782
783        let bundle_identity_key = IdentityKey::decode(&bundle.identity_key)
784            .map_err(|e| SignalBridgeError::Protocol(e.to_string()))?;
785
786        let our_identity_pair = self
787            .storage
788            .identity_store()
789            .get_identity_key_pair()
790            .await
791            .map_err(|e| SignalBridgeError::Storage(e.to_string()))?;
792        let our_identity_key = our_identity_pair.identity_key();
793
794        if bundle_identity_key.public_key() == our_identity_key.public_key() {
795            return Err(SignalBridgeError::InvalidInput(
796                "Ignoring bundle from self".to_string(),
797            ));
798        }
799
800        let rdx = self
801            .add_contact_from_bundle(bundle_bytes, user_alias)
802            .await?;
803
804        self.establish_session(&rdx, bundle_bytes).await?;
805
806        Ok(rdx)
807    }
808
809    pub async fn sign_nostr_event(
810        &mut self,
811        created_at: u64,
812        kind: u32,
813        tags: Vec<Vec<String>>,
814        content: &str,
815    ) -> Result<(String, String, String), SignalBridgeError> {
816        let keys = self.derive_nostr_keypair().await?;
817
818        let nostr_tags: Vec<Tag> = tags
819            .into_iter()
820            .map(|tag_vec| {
821                if tag_vec.is_empty() {
822                    Tag::parse(&[""]).unwrap_or_else(|_| Tag::parse(&["unknown"]).unwrap())
823                } else {
824                    Tag::parse(&tag_vec).unwrap_or_else(|_| Tag::parse(&["unknown"]).unwrap())
825                }
826            })
827            .collect();
828
829        let event = EventBuilder::new(Kind::Custom(kind as u16), content, nostr_tags)
830            .custom_created_at(nostr::Timestamp::from(created_at))
831            .to_event(&keys)
832            .map_err(|e| SignalBridgeError::Protocol(format!("Failed to create event: {}", e)))?;
833
834        Ok((
835            keys.public_key().to_hex(),
836            event.id.to_hex(),
837            event.sig.to_string(),
838        ))
839    }
840
841    pub async fn generate_node_fingerprint(&mut self) -> Result<String, SignalBridgeError> {
842        let identity_key_pair = self
843            .storage
844            .identity_store()
845            .get_identity_key_pair()
846            .await?;
847
848        Ok(ContactManager::generate_identity_fingerprint_from_key(
849            identity_key_pair.identity_key(),
850        ))
851    }
852
853    pub async fn add_contact_from_bundle(
854        &mut self,
855        bundle_bytes: &[u8],
856        user_alias: Option<&str>,
857    ) -> Result<String, SignalBridgeError> {
858        self.contact_manager
859            .add_contact_from_bundle(bundle_bytes, user_alias, self.storage.session_store())
860            .await
861    }
862
863    pub async fn lookup_contact(
864        &mut self,
865        identifier: &str,
866    ) -> Result<ContactInfo, SignalBridgeError> {
867        self.contact_manager
868            .lookup_contact(identifier, self.storage.session_store())
869            .await
870    }
871
872    pub async fn assign_contact_alias(
873        &mut self,
874        identifier: &str,
875        new_alias: &str,
876    ) -> Result<(), SignalBridgeError> {
877        self.contact_manager
878            .assign_contact_alias(identifier, new_alias, self.storage.session_store())
879            .await
880    }
881
882    pub async fn list_contacts(&mut self) -> Result<Vec<ContactInfo>, SignalBridgeError> {
883        self.contact_manager
884            .list_contacts(self.storage.session_store())
885            .await
886    }
887
888    /// Perform periodic key maintenance including rotation, cleanup, and replenishment.
889    ///
890    /// This should be called periodically (e.g., hourly) to maintain proper key hygiene.
891    /// It will:
892    /// - Rotate signed pre-keys if they are older than the rotation interval
893    /// - Rotate Kyber pre-keys if they are older than the rotation interval
894    /// - Clean up expired signed pre-keys past the grace period
895    /// - Replenish one-time pre-keys if the count drops below the threshold
896    ///
897    /// Returns a `KeyMaintenanceResult` indicating which keys were rotated/replenished.
898    pub async fn perform_key_maintenance(
899        &mut self,
900    ) -> Result<KeyMaintenanceResult, SignalBridgeError> {
901        use crate::key_rotation::{
902            cleanup_expired_kyber_pre_keys, cleanup_expired_signed_pre_keys,
903            kyber_pre_key_needs_rotation, replenish_pre_keys, rotate_kyber_pre_key,
904            rotate_signed_pre_key, signed_pre_key_needs_rotation, MIN_PRE_KEY_COUNT,
905        };
906
907        let mut result = KeyMaintenanceResult::default();
908
909        let identity_key_pair = self
910            .storage
911            .identity_store()
912            .get_identity_key_pair()
913            .await?;
914
915        // Check and rotate signed pre-key if needed
916        if signed_pre_key_needs_rotation(&mut self.storage).await? {
917            rotate_signed_pre_key(&mut self.storage, &identity_key_pair).await?;
918            result.signed_pre_key_rotated = true;
919        }
920
921        // Check and rotate Kyber pre-key if needed (independently)
922        if kyber_pre_key_needs_rotation(&mut self.storage).await? {
923            rotate_kyber_pre_key(&mut self.storage, &identity_key_pair).await?;
924            result.kyber_pre_key_rotated = true;
925        }
926
927        // Clean up expired keys past grace period
928        cleanup_expired_signed_pre_keys(&mut self.storage).await?;
929        cleanup_expired_kyber_pre_keys(&mut self.storage).await?;
930
931        // Replenish one-time pre-keys if count is low
932        let pre_key_count = self.storage.pre_key_store().pre_key_count().await;
933        if pre_key_count < MIN_PRE_KEY_COUNT {
934            replenish_pre_keys(&mut self.storage).await?;
935            result.pre_keys_replenished = true;
936        }
937
938        Ok(result)
939    }
940}
941
942impl Drop for SignalBridge {
943    fn drop(&mut self) {
944        if !self.storage.is_closed() {
945            if let Err(e) = self.storage.close() {
946                eprintln!("Warning: Failed to close storage properly: {}", e);
947            }
948        }
949    }
950}
951
952/// Errors that can occur in the Signal Protocol bridge
953#[derive(Debug, thiserror::Error)]
954pub enum SignalBridgeError {
955    /// Storage-related error
956    #[error("Storage error: {0}")]
957    Storage(String),
958    /// Signal Protocol operation error
959    #[error("Signal Protocol error: {0}")]
960    Protocol(String),
961    /// Serialization/deserialization error
962    #[error("Serialization error: {0}")]
963    Serialization(String),
964    /// Invalid input provided to function
965    #[error("Invalid input: {0}")]
966    InvalidInput(String),
967    /// Session not found for peer
968    #[error("{0}")]
969    SessionNotFound(String),
970    /// Key derivation failure
971    #[error("Key derivation error: {0}")]
972    KeyDerivation(String),
973    /// Database schema version too old
974    #[error("Update your database to a newer schema version")]
975    SchemaVersionTooOld,
976}
977
978impl From<libsignal_protocol::SignalProtocolError> for SignalBridgeError {
979    fn from(err: libsignal_protocol::SignalProtocolError) -> Self {
980        SignalBridgeError::Protocol(err.to_string())
981    }
982}
983
984impl From<bincode::Error> for SignalBridgeError {
985    fn from(err: bincode::Error) -> Self {
986        SignalBridgeError::Serialization(err.to_string())
987    }
988}
989
990impl From<std::time::SystemTimeError> for SignalBridgeError {
991    fn from(err: std::time::SystemTimeError) -> Self {
992        SignalBridgeError::Protocol(err.to_string())
993    }
994}
995
996impl From<rusqlite::Error> for SignalBridgeError {
997    fn from(err: rusqlite::Error) -> Self {
998        SignalBridgeError::Storage(err.to_string())
999    }
1000}
1001
1002impl From<Box<dyn std::error::Error>> for SignalBridgeError {
1003    fn from(err: Box<dyn std::error::Error>) -> Self {
1004        SignalBridgeError::Storage(err.to_string())
1005    }
1006}
1007
1008impl From<std::num::TryFromIntError> for SignalBridgeError {
1009    fn from(err: std::num::TryFromIntError) -> Self {
1010        SignalBridgeError::InvalidInput(format!("Integer conversion error: {}", err))
1011    }
1012}
1013
1014impl From<std::io::Error> for SignalBridgeError {
1015    fn from(err: std::io::Error) -> Self {
1016        SignalBridgeError::Storage(format!("Filesystem error: {}", err))
1017    }
1018}
1019
1020#[cxx::bridge(namespace = "radix_relay")]
1021mod ffi {
1022    /// Message direction (incoming or outgoing)
1023    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1024    #[repr(u8)]
1025    pub enum MessageDirection {
1026        Incoming = 0,
1027        Outgoing = 1,
1028    }
1029
1030    /// Message type (text, bundle announcement, or system)
1031    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1032    #[repr(u8)]
1033    pub enum MessageType {
1034        Text = 0,
1035        BundleAnnouncement = 1,
1036        System = 2,
1037    }
1038
1039    /// Delivery status for outgoing messages
1040    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1041    #[repr(u8)]
1042    pub enum DeliveryStatus {
1043        Pending = 0,
1044        Sent = 1,
1045        Delivered = 2,
1046        Failed = 3,
1047    }
1048
1049    #[derive(Clone, Debug)]
1050    pub struct ContactInfo {
1051        pub rdx_fingerprint: String,
1052        pub nostr_pubkey: String,
1053        pub user_alias: String,
1054        pub has_active_session: bool,
1055    }
1056
1057    #[derive(Clone, Debug, Default)]
1058    pub struct KeyMaintenanceResult {
1059        pub signed_pre_key_rotated: bool,
1060        pub kyber_pre_key_rotated: bool,
1061        pub pre_keys_replenished: bool,
1062    }
1063
1064    #[derive(Clone, Debug)]
1065    pub struct DecryptionResult {
1066        pub plaintext: Vec<u8>,
1067        pub should_republish_bundle: bool,
1068    }
1069
1070    #[derive(Clone, Debug)]
1071    pub struct BundleInfo {
1072        pub announcement_json: String,
1073        pub pre_key_id: u32,
1074        pub signed_pre_key_id: u32,
1075        pub kyber_pre_key_id: u32,
1076    }
1077
1078    #[derive(Clone, Debug)]
1079    pub struct PreKeyBundleWithMetadata {
1080        pub bundle_bytes: Vec<u8>,
1081        pub pre_key_id: u32,
1082        pub signed_pre_key_id: u32,
1083        pub kyber_pre_key_id: u32,
1084    }
1085
1086    #[derive(Clone, Debug)]
1087    pub struct StoredMessage {
1088        pub id: i64,
1089        pub conversation_id: i64,
1090        pub direction: MessageDirection,
1091        pub timestamp: u64,
1092        pub message_type: MessageType,
1093        pub content: String,
1094        pub delivery_status: DeliveryStatus,
1095        pub was_prekey_message: bool,
1096        pub session_established: bool,
1097    }
1098
1099    #[derive(Clone, Debug)]
1100    pub struct Conversation {
1101        pub id: i64,
1102        pub rdx_fingerprint: String,
1103        pub last_message_timestamp: u64,
1104        pub unread_count: u32,
1105        pub archived: bool,
1106    }
1107
1108    extern "Rust" {
1109        type SignalBridge;
1110
1111        fn new_signal_bridge(db_path: &str) -> Result<Box<SignalBridge>>;
1112
1113        fn encrypt_message(
1114            bridge: &mut SignalBridge,
1115            peer: &str,
1116            plaintext: &[u8],
1117        ) -> Result<Vec<u8>>;
1118
1119        fn decrypt_message(
1120            bridge: &mut SignalBridge,
1121            peer: &str,
1122            ciphertext: &[u8],
1123        ) -> Result<DecryptionResult>;
1124
1125        fn establish_session(bridge: &mut SignalBridge, peer: &str, bundle: &[u8]) -> Result<()>;
1126
1127        fn generate_pre_key_bundle(bridge: &mut SignalBridge) -> Result<PreKeyBundleWithMetadata>;
1128
1129        fn clear_peer_session(bridge: &mut SignalBridge, peer: &str) -> Result<()>;
1130
1131        fn clear_all_sessions(bridge: &mut SignalBridge) -> Result<()>;
1132
1133        fn reset_identity(bridge: &mut SignalBridge) -> Result<()>;
1134
1135        fn generate_node_fingerprint(bridge: &mut SignalBridge) -> Result<String>;
1136
1137        fn sign_nostr_event(bridge: &mut SignalBridge, event_json: &str) -> Result<String>;
1138
1139        fn create_and_sign_encrypted_message(
1140            bridge: &mut SignalBridge,
1141            session_id: &str,
1142            encrypted_content: &str,
1143            timestamp: u64,
1144            project_version: &str,
1145        ) -> Result<String>;
1146
1147        fn create_subscription_for_self(
1148            bridge: &mut SignalBridge,
1149            subscription_id: &str,
1150            since_timestamp: u64,
1151        ) -> Result<String>;
1152
1153        fn update_last_message_timestamp(bridge: &mut SignalBridge, timestamp: u64) -> Result<()>;
1154
1155        fn lookup_contact(bridge: &mut SignalBridge, identifier: &str) -> Result<ContactInfo>;
1156
1157        fn assign_contact_alias(
1158            bridge: &mut SignalBridge,
1159            identifier: &str,
1160            new_alias: &str,
1161        ) -> Result<()>;
1162
1163        fn list_contacts(bridge: &mut SignalBridge) -> Result<Vec<ContactInfo>>;
1164
1165        fn generate_prekey_bundle_announcement(
1166            bridge: &mut SignalBridge,
1167            project_version: &str,
1168        ) -> Result<BundleInfo>;
1169
1170        fn generate_empty_bundle_announcement(
1171            bridge: &mut SignalBridge,
1172            project_version: &str,
1173        ) -> Result<String>;
1174
1175        fn add_contact_and_establish_session(
1176            bridge: &mut SignalBridge,
1177            bundle_bytes: &[u8],
1178            user_alias: &str,
1179        ) -> Result<String>;
1180
1181        fn add_contact_and_establish_session_from_base64(
1182            bridge: &mut SignalBridge,
1183            bundle_base64: &str,
1184            user_alias: &str,
1185        ) -> Result<String>;
1186
1187        fn extract_rdx_from_bundle(
1188            bridge: &mut SignalBridge,
1189            bundle_bytes: &[u8],
1190        ) -> Result<String>;
1191
1192        fn extract_rdx_from_bundle_base64(
1193            bridge: &mut SignalBridge,
1194            bundle_base64: &str,
1195        ) -> Result<String>;
1196
1197        fn perform_key_maintenance(bridge: &mut SignalBridge) -> Result<KeyMaintenanceResult>;
1198
1199        fn record_published_bundle(
1200            bridge: &mut SignalBridge,
1201            pre_key_id: u32,
1202            signed_pre_key_id: u32,
1203            kyber_pre_key_id: u32,
1204        ) -> Result<()>;
1205
1206        fn get_conversations(
1207            bridge: &mut SignalBridge,
1208            include_archived: bool,
1209        ) -> Result<Vec<Conversation>>;
1210
1211        fn get_conversation_messages(
1212            bridge: &mut SignalBridge,
1213            rdx_fingerprint: &str,
1214            limit: u32,
1215            offset: u32,
1216        ) -> Result<Vec<StoredMessage>>;
1217
1218        fn mark_conversation_read(bridge: &mut SignalBridge, rdx_fingerprint: &str) -> Result<()>;
1219
1220        fn delete_message(bridge: &mut SignalBridge, message_id: i64) -> Result<()>;
1221
1222        fn delete_conversation(bridge: &mut SignalBridge, rdx_fingerprint: &str) -> Result<()>;
1223
1224        fn get_unread_count(bridge: &mut SignalBridge, rdx_fingerprint: &str) -> Result<u32>;
1225    }
1226}
1227
1228/// Creates a new Signal Protocol bridge with SQLite storage
1229///
1230/// # Arguments
1231/// * `db_path` - Path to SQLite database file
1232pub fn new_signal_bridge(db_path: &str) -> Result<Box<SignalBridge>, Box<dyn std::error::Error>> {
1233    let rt = tokio::runtime::Runtime::new()
1234        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1235    let bridge = rt
1236        .block_on(SignalBridge::new(db_path))
1237        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1238    Ok(Box::new(bridge))
1239}
1240
1241/// Encrypts a message for a peer using Signal Protocol
1242///
1243/// # Arguments
1244/// * `bridge` - Signal bridge instance
1245/// * `peer` - Recipient's RDX fingerprint or Nostr pubkey
1246/// * `plaintext` - Message bytes to encrypt
1247pub fn encrypt_message(
1248    bridge: &mut SignalBridge,
1249    peer: &str,
1250    plaintext: &[u8],
1251) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1252    let rt = tokio::runtime::Runtime::new()
1253        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1254    rt.block_on(bridge.encrypt_message(peer, plaintext))
1255        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1256}
1257
1258/// Decrypts an incoming Signal Protocol message
1259///
1260/// # Arguments
1261/// * `bridge` - Signal bridge instance
1262/// * `peer` - Sender's RDX fingerprint or Nostr pubkey (peer hint)
1263/// * `ciphertext` - Encrypted message bytes
1264pub fn decrypt_message(
1265    bridge: &mut SignalBridge,
1266    peer: &str,
1267    ciphertext: &[u8],
1268) -> Result<ffi::DecryptionResult, Box<dyn std::error::Error>> {
1269    let rt = tokio::runtime::Runtime::new()
1270        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1271    let result = rt
1272        .block_on(bridge.decrypt_message(peer, ciphertext))
1273        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1274    Ok(ffi::DecryptionResult {
1275        plaintext: result.plaintext,
1276        should_republish_bundle: result.should_republish_bundle,
1277    })
1278}
1279
1280/// Establishes a Signal Protocol session from a prekey bundle
1281///
1282/// # Arguments
1283/// * `bridge` - Signal bridge instance
1284/// * `peer` - Peer's RDX fingerprint or Nostr pubkey
1285/// * `bundle` - Serialized prekey bundle bytes
1286pub fn establish_session(
1287    bridge: &mut SignalBridge,
1288    peer: &str,
1289    bundle: &[u8],
1290) -> Result<(), Box<dyn std::error::Error>> {
1291    let rt = tokio::runtime::Runtime::new()
1292        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1293    rt.block_on(bridge.establish_session(peer, bundle))
1294        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1295}
1296
1297/// Generates a new prekey bundle with metadata for publishing
1298///
1299/// # Arguments
1300/// * `bridge` - Signal bridge instance
1301///
1302/// # Returns
1303/// PreKeyBundleWithMetadata containing bundle bytes and key IDs
1304pub fn generate_pre_key_bundle(
1305    bridge: &mut SignalBridge,
1306) -> Result<ffi::PreKeyBundleWithMetadata, Box<dyn std::error::Error>> {
1307    let rt = tokio::runtime::Runtime::new()
1308        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1309    let (bundle_bytes, pre_key_id, signed_pre_key_id, kyber_pre_key_id) = rt
1310        .block_on(bridge.generate_pre_key_bundle())
1311        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1312    Ok(ffi::PreKeyBundleWithMetadata {
1313        bundle_bytes,
1314        pre_key_id,
1315        signed_pre_key_id,
1316        kyber_pre_key_id,
1317    })
1318}
1319
1320/// Clears the Signal Protocol session with a specific peer
1321///
1322/// # Arguments
1323/// * `bridge` - Signal bridge instance
1324/// * `peer` - Peer's RDX fingerprint or Nostr pubkey
1325pub fn clear_peer_session(
1326    bridge: &mut SignalBridge,
1327    peer: &str,
1328) -> Result<(), Box<dyn std::error::Error>> {
1329    let rt = tokio::runtime::Runtime::new()
1330        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1331    rt.block_on(bridge.clear_peer_session(peer))
1332        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1333}
1334
1335/// Clears all Signal Protocol sessions
1336///
1337/// # Arguments
1338/// * `bridge` - Signal bridge instance
1339pub fn clear_all_sessions(bridge: &mut SignalBridge) -> Result<(), Box<dyn std::error::Error>> {
1340    let rt = tokio::runtime::Runtime::new()
1341        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1342    rt.block_on(bridge.clear_all_sessions())
1343        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1344}
1345
1346/// Resets the node's Signal Protocol identity (generates new identity key)
1347///
1348/// # Arguments
1349/// * `bridge` - Signal bridge instance
1350pub fn reset_identity(bridge: &mut SignalBridge) -> Result<(), Box<dyn std::error::Error>> {
1351    let rt = tokio::runtime::Runtime::new()
1352        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1353    rt.block_on(bridge.reset_identity())
1354        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1355}
1356
1357/// Performs periodic key rotation and cleanup
1358///
1359/// # Arguments
1360/// * `bridge` - Signal bridge instance
1361///
1362/// # Returns
1363/// KeyMaintenanceResult indicating which keys were rotated
1364pub fn perform_key_maintenance(
1365    bridge: &mut SignalBridge,
1366) -> Result<ffi::KeyMaintenanceResult, Box<dyn std::error::Error>> {
1367    let rt = tokio::runtime::Runtime::new()
1368        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1369    let result = rt
1370        .block_on(bridge.perform_key_maintenance())
1371        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1372    Ok(ffi::KeyMaintenanceResult {
1373        signed_pre_key_rotated: result.signed_pre_key_rotated,
1374        kyber_pre_key_rotated: result.kyber_pre_key_rotated,
1375        pre_keys_replenished: result.pre_keys_replenished,
1376    })
1377}
1378
1379/// Records a published bundle to track used keys
1380///
1381/// # Arguments
1382/// * `bridge` - Signal bridge instance
1383/// * `pre_key_id` - One-time prekey ID
1384/// * `signed_pre_key_id` - Signed prekey ID
1385/// * `kyber_pre_key_id` - Kyber prekey ID
1386pub fn record_published_bundle(
1387    bridge: &mut SignalBridge,
1388    pre_key_id: u32,
1389    signed_pre_key_id: u32,
1390    kyber_pre_key_id: u32,
1391) -> Result<(), Box<dyn std::error::Error>> {
1392    bridge
1393        .storage
1394        .record_published_bundle(pre_key_id, signed_pre_key_id, kyber_pre_key_id)?;
1395    Ok(())
1396}
1397
1398/// Retrieves all conversations ordered by recent activity
1399///
1400/// # Arguments
1401/// * `bridge` - Signal bridge instance
1402/// * `include_archived` - Whether to include archived conversations
1403pub fn get_conversations(
1404    bridge: &mut SignalBridge,
1405    include_archived: bool,
1406) -> Result<Vec<ffi::Conversation>, Box<dyn std::error::Error>> {
1407    let conversations = bridge
1408        .storage
1409        .message_history()
1410        .get_conversations(include_archived)?;
1411
1412    Ok(conversations
1413        .into_iter()
1414        .map(|c| ffi::Conversation {
1415            id: c.id,
1416            rdx_fingerprint: c.rdx_fingerprint,
1417            last_message_timestamp: c.last_message_timestamp,
1418            unread_count: c.unread_count,
1419            archived: c.archived,
1420        })
1421        .collect())
1422}
1423
1424/// Retrieves messages for a conversation with pagination
1425///
1426/// # Arguments
1427/// * `bridge` - Signal bridge instance
1428/// * `rdx_fingerprint` - Contact's RDX fingerprint
1429/// * `limit` - Maximum number of messages to return
1430/// * `offset` - Number of messages to skip
1431pub fn get_conversation_messages(
1432    bridge: &mut SignalBridge,
1433    rdx_fingerprint: &str,
1434    limit: u32,
1435    offset: u32,
1436) -> Result<Vec<ffi::StoredMessage>, Box<dyn std::error::Error>> {
1437    let messages = bridge.storage.message_history().get_conversation_messages(
1438        rdx_fingerprint,
1439        limit,
1440        offset,
1441    )?;
1442
1443    Ok(messages
1444        .into_iter()
1445        .map(|m| ffi::StoredMessage {
1446            id: m.id,
1447            conversation_id: m.conversation_id,
1448            direction: match m.direction {
1449                MessageDirection::Incoming => ffi::MessageDirection::Incoming,
1450                MessageDirection::Outgoing => ffi::MessageDirection::Outgoing,
1451            },
1452            timestamp: m.timestamp,
1453            message_type: match m.message_type {
1454                MessageType::Text => ffi::MessageType::Text,
1455                MessageType::BundleAnnouncement => ffi::MessageType::BundleAnnouncement,
1456                MessageType::System => ffi::MessageType::System,
1457            },
1458            content: m.content,
1459            delivery_status: match m.delivery_status {
1460                DeliveryStatus::Pending => ffi::DeliveryStatus::Pending,
1461                DeliveryStatus::Sent => ffi::DeliveryStatus::Sent,
1462                DeliveryStatus::Delivered => ffi::DeliveryStatus::Delivered,
1463                DeliveryStatus::Failed => ffi::DeliveryStatus::Failed,
1464            },
1465            was_prekey_message: m.was_prekey_message,
1466            session_established: m.session_established,
1467        })
1468        .collect())
1469}
1470
1471/// Marks a conversation as read (clears unread count)
1472///
1473/// # Arguments
1474/// * `bridge` - Signal bridge instance
1475/// * `rdx_fingerprint` - Contact's RDX fingerprint
1476pub fn mark_conversation_read(
1477    bridge: &mut SignalBridge,
1478    rdx_fingerprint: &str,
1479) -> Result<(), Box<dyn std::error::Error>> {
1480    bridge
1481        .storage
1482        .message_history()
1483        .mark_conversation_read(rdx_fingerprint)?;
1484    Ok(())
1485}
1486
1487/// Deletes a message by ID
1488///
1489/// # Arguments
1490/// * `bridge` - Signal bridge instance
1491/// * `message_id` - Database ID of message to delete
1492pub fn delete_message(
1493    bridge: &mut SignalBridge,
1494    message_id: i64,
1495) -> Result<(), Box<dyn std::error::Error>> {
1496    bridge
1497        .storage
1498        .message_history()
1499        .delete_message(message_id)?;
1500    Ok(())
1501}
1502
1503/// Deletes an entire conversation and all its messages
1504///
1505/// # Arguments
1506/// * `bridge` - Signal bridge instance
1507/// * `rdx_fingerprint` - Contact's RDX fingerprint
1508pub fn delete_conversation(
1509    bridge: &mut SignalBridge,
1510    rdx_fingerprint: &str,
1511) -> Result<(), Box<dyn std::error::Error>> {
1512    bridge
1513        .storage
1514        .message_history()
1515        .delete_conversation(rdx_fingerprint)?;
1516    Ok(())
1517}
1518
1519/// Gets the unread message count for a conversation
1520///
1521/// # Arguments
1522/// * `bridge` - Signal bridge instance
1523/// * `rdx_fingerprint` - Contact's RDX fingerprint
1524pub fn get_unread_count(
1525    bridge: &mut SignalBridge,
1526    rdx_fingerprint: &str,
1527) -> Result<u32, Box<dyn std::error::Error>> {
1528    let count = bridge
1529        .storage
1530        .message_history()
1531        .get_unread_count(rdx_fingerprint)?;
1532    Ok(count)
1533}
1534
1535/// Returns this node's RDX fingerprint
1536///
1537/// # Arguments
1538/// * `bridge` - Signal bridge instance
1539///
1540/// # Returns
1541/// Hex-encoded SHA-256 hash of node's identity public key
1542pub fn generate_node_fingerprint(
1543    bridge: &mut SignalBridge,
1544) -> Result<String, Box<dyn std::error::Error>> {
1545    let rt = tokio::runtime::Runtime::new()
1546        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1547    rt.block_on(bridge.generate_node_fingerprint())
1548        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1549}
1550
1551/// Signs a Nostr event with node's private key
1552///
1553/// # Arguments
1554/// * `bridge` - Signal bridge instance
1555/// * `event_json` - Unsigned event JSON
1556///
1557/// # Returns
1558/// Signed event JSON with id and sig fields
1559pub fn sign_nostr_event(
1560    bridge: &mut SignalBridge,
1561    event_json: &str,
1562) -> Result<String, Box<dyn std::error::Error>> {
1563    let rt = tokio::runtime::Runtime::new()
1564        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1565
1566    let mut event: serde_json::Value = serde_json::from_str(event_json)
1567        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1568
1569    let created_at = event["created_at"].as_u64().ok_or("Missing created_at")?;
1570    let kind = event["kind"].as_u64().ok_or("Missing kind")? as u32;
1571    let content = event["content"].as_str().ok_or("Missing content")?;
1572
1573    let tags_array = event["tags"].as_array().ok_or("Missing tags")?;
1574    let tags: Vec<Vec<String>> = tags_array
1575        .iter()
1576        .map(|tag| {
1577            tag.as_array()
1578                .unwrap_or(&vec![])
1579                .iter()
1580                .map(|v| v.as_str().unwrap_or("").to_string())
1581                .collect()
1582        })
1583        .collect();
1584
1585    let (pubkey, event_id, signature) = rt
1586        .block_on(bridge.sign_nostr_event(created_at, kind, tags, content))
1587        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1588
1589    event["pubkey"] = serde_json::Value::String(pubkey);
1590    event["id"] = serde_json::Value::String(event_id);
1591    event["sig"] = serde_json::Value::String(signature);
1592
1593    serde_json::to_string(&event).map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1594}
1595
1596/// Creates and signs a Nostr encrypted message event
1597///
1598/// # Arguments
1599/// * `bridge` - Signal bridge instance
1600/// * `session_id` - Recipient's session identifier
1601/// * `encrypted_content` - Hex-encoded encrypted content
1602/// * `timestamp` - Unix timestamp
1603/// * `project_version` - Protocol version string
1604///
1605/// # Returns
1606/// Signed Nostr event JSON
1607pub fn create_and_sign_encrypted_message(
1608    bridge: &mut SignalBridge,
1609    session_id: &str,
1610    encrypted_content: &str,
1611    timestamp: u64,
1612    project_version: &str,
1613) -> Result<String, Box<dyn std::error::Error>> {
1614    let rt = tokio::runtime::Runtime::new()
1615        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1616
1617    // Derive recipient's Nostr pubkey from session
1618    let recipient_pubkey_bytes = rt
1619        .block_on(bridge.derive_peer_nostr_key(session_id))
1620        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1621    let recipient_pubkey = hex::encode(&recipient_pubkey_bytes);
1622
1623    // Create the Nostr event JSON
1624    let event_json = serde_json::json!({
1625        "id": "",
1626        "pubkey": "",
1627        "created_at": timestamp,
1628        "kind": 40001,
1629        "tags": [
1630            ["p", recipient_pubkey],
1631            ["radix_version", project_version]
1632        ],
1633        "content": encrypted_content,
1634        "sig": ""
1635    });
1636
1637    // Sign the event
1638    let event_json_str = serde_json::to_string(&event_json)
1639        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1640
1641    // Use the existing signing function
1642    sign_nostr_event(bridge, &event_json_str)
1643}
1644
1645/// Creates a Nostr subscription filter for messages to this node
1646///
1647/// # Arguments
1648/// * `bridge` - Signal bridge instance
1649/// * `subscription_id` - Subscription identifier
1650/// * `since_timestamp` - Optional timestamp to filter messages since
1651///
1652/// # Returns
1653/// REQ message JSON
1654pub fn create_subscription_for_self(
1655    bridge: &mut SignalBridge,
1656    subscription_id: &str,
1657    since_timestamp: u64,
1658) -> Result<String, Box<dyn std::error::Error>> {
1659    let rt = tokio::runtime::Runtime::new()
1660        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1661    rt.block_on(bridge.create_subscription_for_self(subscription_id, since_timestamp))
1662        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1663}
1664
1665/// Updates the timestamp of the last received message
1666///
1667/// # Arguments
1668/// * `bridge` - Signal bridge instance
1669/// * `timestamp` - Unix timestamp
1670pub fn update_last_message_timestamp(
1671    bridge: &mut SignalBridge,
1672    timestamp: u64,
1673) -> Result<(), Box<dyn std::error::Error>> {
1674    let rt = tokio::runtime::Runtime::new()
1675        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1676    rt.block_on(bridge.update_last_message_timestamp(timestamp))
1677        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1678}
1679
1680/// Looks up a contact by RDX fingerprint or alias
1681///
1682/// # Arguments
1683/// * `bridge` - Signal bridge instance
1684/// * `identifier` - Contact identifier (RDX fingerprint or user alias)
1685///
1686/// # Returns
1687/// Contact information
1688pub fn lookup_contact(
1689    bridge: &mut SignalBridge,
1690    identifier: &str,
1691) -> Result<ffi::ContactInfo, Box<dyn std::error::Error>> {
1692    let rt = tokio::runtime::Runtime::new()?;
1693    let contact = rt.block_on(bridge.lookup_contact(identifier))?;
1694    Ok(ffi::ContactInfo {
1695        rdx_fingerprint: contact.rdx_fingerprint,
1696        nostr_pubkey: contact.nostr_pubkey,
1697        user_alias: contact.user_alias.unwrap_or_default(),
1698        has_active_session: contact.has_active_session,
1699    })
1700}
1701
1702/// Assigns an alias to a contact
1703///
1704/// # Arguments
1705/// * `bridge` - Signal bridge instance
1706/// * `identifier` - Contact's RDX fingerprint
1707/// * `new_alias` - User-friendly alias
1708pub fn assign_contact_alias(
1709    bridge: &mut SignalBridge,
1710    identifier: &str,
1711    new_alias: &str,
1712) -> Result<(), Box<dyn std::error::Error>> {
1713    let rt = tokio::runtime::Runtime::new()?;
1714    rt.block_on(bridge.assign_contact_alias(identifier, new_alias))
1715        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1716}
1717
1718/// Lists all known contacts
1719///
1720/// # Arguments
1721/// * `bridge` - Signal bridge instance
1722///
1723/// # Returns
1724/// Vector of contact information structs
1725pub fn list_contacts(
1726    bridge: &mut SignalBridge,
1727) -> Result<Vec<ffi::ContactInfo>, Box<dyn std::error::Error>> {
1728    let rt = tokio::runtime::Runtime::new()?;
1729    let contacts = rt.block_on(bridge.list_contacts())?;
1730    Ok(contacts
1731        .into_iter()
1732        .map(|c| ffi::ContactInfo {
1733            rdx_fingerprint: c.rdx_fingerprint,
1734            nostr_pubkey: c.nostr_pubkey,
1735            user_alias: c.user_alias.unwrap_or_default(),
1736            has_active_session: c.has_active_session,
1737        })
1738        .collect())
1739}
1740
1741/// Generates a signed prekey bundle announcement
1742///
1743/// # Arguments
1744/// * `bridge` - Signal bridge instance
1745/// * `project_version` - Protocol version string
1746///
1747/// # Returns
1748/// Bundle information with announcement JSON and prekey IDs
1749pub fn generate_prekey_bundle_announcement(
1750    bridge: &mut SignalBridge,
1751    project_version: &str,
1752) -> Result<ffi::BundleInfo, Box<dyn std::error::Error>> {
1753    let rt = tokio::runtime::Runtime::new()?;
1754    rt.block_on(bridge.generate_prekey_bundle_announcement(project_version))
1755        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1756}
1757
1758/// Generates an empty bundle announcement for unpublishing
1759///
1760/// # Arguments
1761/// * `bridge` - Signal bridge instance
1762/// * `project_version` - Protocol version string
1763///
1764/// # Returns
1765/// Signed empty bundle announcement JSON
1766pub fn generate_empty_bundle_announcement(
1767    bridge: &mut SignalBridge,
1768    project_version: &str,
1769) -> Result<String, Box<dyn std::error::Error>> {
1770    let rt = tokio::runtime::Runtime::new()?;
1771    rt.block_on(bridge.generate_empty_bundle_announcement(project_version))
1772        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1773}
1774
1775/// Adds a contact from a bundle and establishes a session
1776///
1777/// # Arguments
1778/// * `bridge` - Signal bridge instance
1779/// * `bundle_bytes` - Serialized prekey bundle
1780/// * `user_alias` - Optional user-assigned alias
1781///
1782/// # Returns
1783/// RDX fingerprint of the added contact
1784pub fn add_contact_and_establish_session(
1785    bridge: &mut SignalBridge,
1786    bundle_bytes: &[u8],
1787    user_alias: &str,
1788) -> Result<String, Box<dyn std::error::Error>> {
1789    let rt = tokio::runtime::Runtime::new()?;
1790    let alias = if user_alias.is_empty() {
1791        None
1792    } else {
1793        Some(user_alias)
1794    };
1795    rt.block_on(bridge.add_contact_and_establish_session(bundle_bytes, alias))
1796        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1797}
1798
1799/// Adds a contact from a base64-encoded bundle and establishes a session
1800///
1801/// # Arguments
1802/// * `bridge` - Signal bridge instance
1803/// * `bundle_base64` - Base64-encoded prekey bundle
1804/// * `user_alias` - Optional user-assigned alias
1805///
1806/// # Returns
1807/// RDX fingerprint of the added contact
1808pub fn add_contact_and_establish_session_from_base64(
1809    bridge: &mut SignalBridge,
1810    bundle_base64: &str,
1811    user_alias: &str,
1812) -> Result<String, Box<dyn std::error::Error>> {
1813    use base64::Engine;
1814    let bundle_bytes = base64::engine::general_purpose::STANDARD
1815        .decode(bundle_base64)
1816        .map_err(|e| -> Box<dyn std::error::Error> {
1817            Box::new(SignalBridgeError::Protocol(format!(
1818                "Base64 decode error: {}",
1819                e
1820            )))
1821        })?;
1822
1823    add_contact_and_establish_session(bridge, &bundle_bytes, user_alias)
1824}
1825
1826/// Extracts RDX fingerprint from a prekey bundle without adding contact
1827///
1828/// # Arguments
1829/// * `_bridge` - Signal bridge instance (unused)
1830/// * `bundle_bytes` - Serialized prekey bundle
1831///
1832/// # Returns
1833/// RDX fingerprint
1834pub fn extract_rdx_from_bundle(
1835    _bridge: &mut SignalBridge,
1836    bundle_bytes: &[u8],
1837) -> Result<String, Box<dyn std::error::Error>> {
1838    use libsignal_protocol::IdentityKey;
1839
1840    let bundle: SerializablePreKeyBundle =
1841        bincode::deserialize(bundle_bytes).map_err(|e| -> Box<dyn std::error::Error> {
1842            Box::new(SignalBridgeError::InvalidInput(format!(
1843                "Failed to deserialize bundle: {}",
1844                e
1845            )))
1846        })?;
1847
1848    let identity_key =
1849        IdentityKey::decode(&bundle.identity_key).map_err(|e| -> Box<dyn std::error::Error> {
1850            Box::new(SignalBridgeError::Protocol(e.to_string()))
1851        })?;
1852
1853    Ok(ContactManager::generate_identity_fingerprint_from_key(
1854        &identity_key,
1855    ))
1856}
1857
1858/// Extracts RDX fingerprint from a base64-encoded prekey bundle
1859///
1860/// # Arguments
1861/// * `bridge` - Signal bridge instance
1862/// * `bundle_base64` - Base64-encoded prekey bundle
1863///
1864/// # Returns
1865/// RDX fingerprint
1866pub fn extract_rdx_from_bundle_base64(
1867    bridge: &mut SignalBridge,
1868    bundle_base64: &str,
1869) -> Result<String, Box<dyn std::error::Error>> {
1870    use base64::Engine;
1871    let bundle_bytes = base64::engine::general_purpose::STANDARD
1872        .decode(bundle_base64)
1873        .map_err(|e| -> Box<dyn std::error::Error> {
1874            Box::new(SignalBridgeError::Protocol(format!(
1875                "Base64 decode error: {}",
1876                e
1877            )))
1878        })?;
1879
1880    extract_rdx_from_bundle(bridge, &bundle_bytes)
1881}
1882
1883#[cfg(test)]
1884mod tests {
1885    use super::*;
1886    use std::time::{SystemTime, UNIX_EPOCH};
1887
1888    #[test]
1889    fn test_libsignal_basic_types() {
1890        let device_id = DeviceId::new(1).expect("Valid device ID");
1891        let protocol_address = ProtocolAddress::new("test_device".to_string(), device_id);
1892        assert_eq!(protocol_address.name(), "test_device");
1893        assert_eq!(protocol_address.device_id(), device_id);
1894    }
1895
1896    #[tokio::test]
1897    async fn test_nostr_key_derivation_deterministic() {
1898        let temp_dir = std::env::temp_dir();
1899        let db_path = temp_dir.join("test_nostr_derivation.db");
1900        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
1901
1902        let nostr_keypair1 = bridge.derive_nostr_keypair().await.unwrap();
1903        let nostr_keypair2 = bridge.derive_nostr_keypair().await.unwrap();
1904
1905        assert_eq!(nostr_keypair1.secret_key(), nostr_keypair2.secret_key());
1906        assert_eq!(nostr_keypair1.public_key(), nostr_keypair2.public_key());
1907
1908        let _ = std::fs::remove_file(&db_path);
1909    }
1910
1911    #[tokio::test]
1912    async fn test_peer_nostr_key_derivation() {
1913        let temp_dir = std::env::temp_dir();
1914        let alice_db = temp_dir.join("test_alice_nostr.db");
1915        let bob_db = temp_dir.join("test_bob_nostr.db");
1916
1917        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
1918        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
1919
1920        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
1921        let bob_rdx = alice
1922            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
1923            .await
1924            .unwrap();
1925
1926        let bob_nostr_key = alice.derive_peer_nostr_key(&bob_rdx).await.unwrap();
1927
1928        let bob_actual_nostr = bob.derive_nostr_keypair().await.unwrap();
1929        assert_eq!(
1930            bob_nostr_key,
1931            bob_actual_nostr.public_key().to_bytes().to_vec()
1932        );
1933
1934        let _ = std::fs::remove_file(&alice_db);
1935        let _ = std::fs::remove_file(&bob_db);
1936    }
1937
1938    #[test]
1939    fn test_error_types_creation() {
1940        let storage_error = SignalBridgeError::Storage("Database connection failed".to_string());
1941        assert_eq!(
1942            storage_error.to_string(),
1943            "Storage error: Database connection failed"
1944        );
1945
1946        let protocol_error = SignalBridgeError::Protocol("Invalid device ID".to_string());
1947        assert_eq!(
1948            protocol_error.to_string(),
1949            "Signal Protocol error: Invalid device ID"
1950        );
1951
1952        let serialization_error =
1953            SignalBridgeError::Serialization("Invalid data format".to_string());
1954        assert_eq!(
1955            serialization_error.to_string(),
1956            "Serialization error: Invalid data format"
1957        );
1958
1959        let invalid_input_error = SignalBridgeError::InvalidInput("Empty peer name".to_string());
1960        assert_eq!(
1961            invalid_input_error.to_string(),
1962            "Invalid input: Empty peer name"
1963        );
1964
1965        let session_not_found_error = SignalBridgeError::SessionNotFound(
1966            "Establish a session with alice before sending messages".to_string(),
1967        );
1968        assert_eq!(
1969            session_not_found_error.to_string(),
1970            "Establish a session with alice before sending messages"
1971        );
1972
1973        let schema_error = SignalBridgeError::SchemaVersionTooOld;
1974        assert_eq!(
1975            schema_error.to_string(),
1976            "Update your database to a newer schema version"
1977        );
1978    }
1979
1980    #[test]
1981    fn test_error_from_conversions() {
1982        let device_result = DeviceId::new(0);
1983        if let Err(protocol_err) = device_result {
1984            let bridge_error = SignalBridgeError::Protocol(protocol_err.to_string());
1985            assert!(matches!(bridge_error, SignalBridgeError::Protocol(_)));
1986        }
1987
1988        let invalid_data = vec![0xFF, 0xFF, 0xFF];
1989        let bincode_result: Result<String, bincode::Error> = bincode::deserialize(&invalid_data);
1990        if let Err(bincode_err) = bincode_result {
1991            let bridge_error: SignalBridgeError = bincode_err.into();
1992            assert!(matches!(bridge_error, SignalBridgeError::Serialization(_)));
1993        }
1994
1995        use std::time::{Duration, SystemTime};
1996        let bad_time = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
1997        let time_result = bad_time.duration_since(SystemTime::UNIX_EPOCH);
1998        if let Err(time_err) = time_result {
1999            let bridge_error: SignalBridgeError = time_err.into();
2000            assert!(matches!(bridge_error, SignalBridgeError::Protocol(_)));
2001        }
2002    }
2003
2004    #[tokio::test]
2005    async fn test_crypto_handler_exists() -> Result<(), Box<dyn std::error::Error>> {
2006        use crate::SignalBridge;
2007        use std::env;
2008        use std::fs;
2009
2010        let temp_dir = env::temp_dir();
2011        let db_path = temp_dir.join("test_crypto_handler.db");
2012        let db_path_str = db_path.to_str().unwrap();
2013
2014        let _ = fs::remove_file(&db_path);
2015        let key_path = format!("{}.key", db_path_str);
2016        let _ = fs::remove_file(&key_path);
2017
2018        let _handler = SignalBridge::new(db_path_str).await?;
2019        Ok(())
2020    }
2021
2022    #[tokio::test]
2023    async fn test_crypto_handler_alice_bob_integration() -> Result<(), Box<dyn std::error::Error>> {
2024        use crate::SignalBridge;
2025        use std::env;
2026
2027        let temp_dir = env::temp_dir();
2028        let process_id = std::process::id();
2029        let timestamp = std::time::SystemTime::now()
2030            .duration_since(std::time::UNIX_EPOCH)
2031            .unwrap()
2032            .as_millis();
2033
2034        let alice_db_path = temp_dir.join(format!("test_alice_{}_{}.db", process_id, timestamp));
2035        let alice_db_str = alice_db_path.to_str().unwrap();
2036        let mut alice = SignalBridge::new(alice_db_str).await?;
2037
2038        let bob_db_path = temp_dir.join(format!("test_bob_{}_{}.db", process_id, timestamp));
2039        let bob_db_str = bob_db_path.to_str().unwrap();
2040        let mut bob = SignalBridge::new(bob_db_str).await?;
2041
2042        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2043        let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2044
2045        let bob_rdx = alice
2046            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2047            .await?;
2048        let alice_rdx = bob
2049            .add_contact_and_establish_session(&alice_bundle, Some("alice"))
2050            .await?;
2051
2052        let plaintext = b"Hello Bob! This is Alice using SignalBridge.";
2053        let ciphertext = alice.encrypt_message(&bob_rdx, plaintext).await?;
2054        let result = bob.decrypt_message(&alice_rdx, &ciphertext).await?;
2055
2056        assert_eq!(plaintext.as_slice(), result.plaintext.as_slice());
2057
2058        Ok(())
2059    }
2060
2061    #[tokio::test]
2062    async fn test_signalbridge_persistence_across_instantiations(
2063    ) -> Result<(), Box<dyn std::error::Error>> {
2064        use crate::SignalBridge;
2065        use std::env;
2066
2067        let temp_dir = env::temp_dir();
2068        let process_id = std::process::id();
2069        let timestamp = std::time::SystemTime::now()
2070            .duration_since(std::time::UNIX_EPOCH)
2071            .unwrap()
2072            .as_millis();
2073
2074        let alice_db_path = temp_dir.join(format!(
2075            "test_persistence_alice_{}_{}.db",
2076            process_id, timestamp
2077        ));
2078        let alice_db_str = alice_db_path.to_str().unwrap();
2079        let bob_db_path = temp_dir.join(format!(
2080            "test_persistence_bob_{}_{}.db",
2081            process_id, timestamp
2082        ));
2083        let bob_db_str = bob_db_path.to_str().unwrap();
2084
2085        let original_plaintext = b"Hello Bob! This is Alice testing persistence.";
2086        let (ciphertext, bob_rdx, alice_rdx) = {
2087            let mut alice = SignalBridge::new(alice_db_str).await?;
2088            let mut bob = SignalBridge::new(bob_db_str).await?;
2089
2090            let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2091            let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2092
2093            let bob_rdx = alice
2094                .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2095                .await?;
2096            let alice_rdx = bob
2097                .add_contact_and_establish_session(&alice_bundle, Some("alice"))
2098                .await?;
2099
2100            let ciphertext = alice.encrypt_message(&bob_rdx, original_plaintext).await?;
2101            (ciphertext, bob_rdx, alice_rdx)
2102        };
2103
2104        {
2105            let mut alice_reopened = SignalBridge::new(alice_db_str).await?;
2106
2107            let second_message = b"Second message after restart";
2108            let second_ciphertext = alice_reopened
2109                .encrypt_message(&bob_rdx, second_message)
2110                .await?;
2111
2112            assert!(!second_ciphertext.is_empty());
2113            assert_ne!(ciphertext, second_ciphertext);
2114        }
2115
2116        {
2117            let mut bob_reopened = SignalBridge::new(bob_db_str).await?;
2118
2119            let result1 = bob_reopened
2120                .decrypt_message(&alice_rdx, &ciphertext)
2121                .await?;
2122            assert_eq!(result1.plaintext, original_plaintext);
2123        }
2124
2125        Ok(())
2126    }
2127
2128    #[tokio::test]
2129    async fn test_clear_peer_session() -> Result<(), Box<dyn std::error::Error>> {
2130        use crate::SignalBridge;
2131        use std::env;
2132
2133        let temp_dir = env::temp_dir();
2134        let process_id = std::process::id();
2135        let timestamp = std::time::SystemTime::now()
2136            .duration_since(std::time::UNIX_EPOCH)
2137            .unwrap()
2138            .as_millis();
2139
2140        let alice_db_path = temp_dir.join(format!(
2141            "test_clear_peer_alice_{}_{}.db",
2142            process_id, timestamp
2143        ));
2144        let alice_db_str = alice_db_path.to_str().unwrap();
2145        let bob_db_path = temp_dir.join(format!(
2146            "test_clear_peer_bob_{}_{}.db",
2147            process_id, timestamp
2148        ));
2149        let bob_db_str = bob_db_path.to_str().unwrap();
2150
2151        let mut alice = SignalBridge::new(alice_db_str).await?;
2152        let mut bob = SignalBridge::new(bob_db_str).await?;
2153
2154        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2155        let bob_rdx = alice
2156            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2157            .await?;
2158
2159        let message = b"Test message";
2160        let _ciphertext = alice.encrypt_message(&bob_rdx, message).await?;
2161
2162        alice.clear_peer_session(&bob_rdx).await?;
2163
2164        let result = alice.encrypt_message(&bob_rdx, message).await;
2165        assert!(
2166            result.is_err(),
2167            "Encryption should fail after clearing peer session"
2168        );
2169
2170        Ok(())
2171    }
2172
2173    #[tokio::test]
2174    async fn test_clear_all_sessions() -> Result<(), Box<dyn std::error::Error>> {
2175        use crate::SignalBridge;
2176        use std::env;
2177
2178        let temp_dir = env::temp_dir();
2179        let process_id = std::process::id();
2180        let timestamp = std::time::SystemTime::now()
2181            .duration_since(std::time::UNIX_EPOCH)
2182            .unwrap()
2183            .as_millis();
2184
2185        let alice_db_path = temp_dir.join(format!(
2186            "test_clear_all_alice_{}_{}.db",
2187            process_id, timestamp
2188        ));
2189        let alice_db_str = alice_db_path.to_str().unwrap();
2190        let bob_db_path = temp_dir.join(format!(
2191            "test_clear_all_bob_{}_{}.db",
2192            process_id, timestamp
2193        ));
2194        let bob_db_str = bob_db_path.to_str().unwrap();
2195        let charlie_db_path = temp_dir.join(format!(
2196            "test_clear_all_charlie_{}_{}.db",
2197            process_id, timestamp
2198        ));
2199        let charlie_db_str = charlie_db_path.to_str().unwrap();
2200
2201        let mut alice = SignalBridge::new(alice_db_str).await?;
2202        let mut bob = SignalBridge::new(bob_db_str).await?;
2203        let mut charlie = SignalBridge::new(charlie_db_str).await?;
2204
2205        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2206        let (charlie_bundle, _, _, _) = charlie.generate_pre_key_bundle().await?;
2207
2208        let bob_rdx = alice
2209            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2210            .await?;
2211        let charlie_rdx = alice
2212            .add_contact_and_establish_session(&charlie_bundle, Some("charlie"))
2213            .await?;
2214
2215        let message = b"Test message";
2216        let _ciphertext1 = alice.encrypt_message(&bob_rdx, message).await?;
2217        let _ciphertext2 = alice.encrypt_message(&charlie_rdx, message).await?;
2218
2219        alice.clear_all_sessions().await?;
2220
2221        let result1 = alice.encrypt_message(&bob_rdx, message).await;
2222        let result2 = alice.encrypt_message(&charlie_rdx, message).await;
2223        assert!(
2224            result1.is_err(),
2225            "Encryption to Bob should fail after clearing all sessions"
2226        );
2227        assert!(
2228            result2.is_err(),
2229            "Encryption to Charlie should fail after clearing all sessions"
2230        );
2231
2232        Ok(())
2233    }
2234
2235    #[tokio::test]
2236    async fn test_reset_identity() -> Result<(), Box<dyn std::error::Error>> {
2237        use crate::SignalBridge;
2238        use std::env;
2239
2240        let temp_dir = env::temp_dir();
2241        let process_id = std::process::id();
2242        let timestamp = std::time::SystemTime::now()
2243            .duration_since(std::time::UNIX_EPOCH)
2244            .unwrap()
2245            .as_millis();
2246
2247        let alice_db_path = temp_dir.join(format!(
2248            "test_reset_identity_alice_{}_{}.db",
2249            process_id, timestamp
2250        ));
2251        let alice_db_str = alice_db_path.to_str().unwrap();
2252        let bob_db_path = temp_dir.join(format!(
2253            "test_reset_identity_bob_{}_{}.db",
2254            process_id, timestamp
2255        ));
2256        let bob_db_str = bob_db_path.to_str().unwrap();
2257
2258        let (original_alice_bundle, bob_rdx) = {
2259            let mut alice = SignalBridge::new(alice_db_str).await?;
2260            let mut bob = SignalBridge::new(bob_db_str).await?;
2261
2262            let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2263            let bob_rdx = alice
2264                .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2265                .await?;
2266
2267            let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2268            (alice_bundle, bob_rdx)
2269        };
2270
2271        {
2272            let mut alice = SignalBridge::new(alice_db_str).await?;
2273            alice.reset_identity().await?;
2274
2275            let (new_alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2276            assert_ne!(
2277                original_alice_bundle, new_alice_bundle,
2278                "Alice's identity should be different after reset"
2279            );
2280
2281            let message = b"Test message";
2282            let result = alice.encrypt_message(&bob_rdx, message).await;
2283            assert!(
2284                result.is_err(),
2285                "Encryption should fail after identity reset"
2286            );
2287        }
2288
2289        Ok(())
2290    }
2291
2292    #[tokio::test]
2293    async fn test_encrypt_message_empty_peer_name() {
2294        use std::env;
2295
2296        let temp_dir = env::temp_dir();
2297        let process_id = std::process::id();
2298        let timestamp = std::time::SystemTime::now()
2299            .duration_since(std::time::UNIX_EPOCH)
2300            .unwrap()
2301            .as_millis();
2302
2303        let db_path = temp_dir.join(format!("test_empty_peer_{}_{}.db", process_id, timestamp));
2304        let db_path_str = db_path.to_str().unwrap();
2305        let mut bridge = SignalBridge::new(db_path_str)
2306            .await
2307            .expect("Failed to create bridge");
2308
2309        let result = bridge.encrypt_message("", b"test message").await;
2310        assert!(result.is_err());
2311        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2312            assert_eq!(msg, "Specify a peer name");
2313        } else {
2314            panic!("Expected InvalidInput error for empty peer name");
2315        }
2316    }
2317
2318    #[tokio::test]
2319    async fn test_decrypt_message_empty_ciphertext() {
2320        use std::env;
2321
2322        let temp_dir = env::temp_dir();
2323        let process_id = std::process::id();
2324        let timestamp = std::time::SystemTime::now()
2325            .duration_since(std::time::UNIX_EPOCH)
2326            .unwrap()
2327            .as_millis();
2328
2329        let db_path = temp_dir.join(format!(
2330            "test_empty_ciphertext_{}_{}.db",
2331            process_id, timestamp
2332        ));
2333        let db_path_str = db_path.to_str().unwrap();
2334        let mut bridge = SignalBridge::new(db_path_str)
2335            .await
2336            .expect("Failed to create bridge");
2337
2338        let result = bridge.decrypt_message("alice", &[]).await;
2339        assert!(result.is_err());
2340        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2341            assert_eq!(msg, "Provide a message to decrypt");
2342        } else {
2343            panic!("Expected InvalidInput error for empty ciphertext");
2344        }
2345    }
2346
2347    #[tokio::test]
2348    async fn test_establish_session_empty_peer_name() {
2349        use std::env;
2350
2351        let temp_dir = env::temp_dir();
2352        let process_id = std::process::id();
2353        let timestamp = std::time::SystemTime::now()
2354            .duration_since(std::time::UNIX_EPOCH)
2355            .unwrap()
2356            .as_millis();
2357
2358        let db_path = temp_dir.join(format!(
2359            "test_empty_peer_session_{}_{}.db",
2360            process_id, timestamp
2361        ));
2362        let db_path_str = db_path.to_str().unwrap();
2363        let mut bridge = SignalBridge::new(db_path_str)
2364            .await
2365            .expect("Failed to create bridge");
2366
2367        let result = bridge.establish_session("", b"fake_bundle").await;
2368        assert!(result.is_err());
2369        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2370            assert_eq!(msg, "Specify a peer name");
2371        } else {
2372            panic!("Expected InvalidInput error for empty peer name");
2373        }
2374    }
2375
2376    #[tokio::test]
2377    async fn test_establish_session_empty_bundle() {
2378        use std::env;
2379
2380        let temp_dir = env::temp_dir();
2381        let process_id = std::process::id();
2382        let timestamp = std::time::SystemTime::now()
2383            .duration_since(std::time::UNIX_EPOCH)
2384            .unwrap()
2385            .as_millis();
2386
2387        let db_path = temp_dir.join(format!("test_empty_bundle_{}_{}.db", process_id, timestamp));
2388        let db_path_str = db_path.to_str().unwrap();
2389        let mut bridge = SignalBridge::new(db_path_str)
2390            .await
2391            .expect("Failed to create bridge");
2392
2393        let result = bridge.establish_session("alice", &[]).await;
2394        assert!(result.is_err());
2395        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2396            assert_eq!(msg, "Provide a pre-key bundle from the peer");
2397        } else {
2398            panic!("Expected InvalidInput error for empty pre-key bundle");
2399        }
2400    }
2401
2402    #[tokio::test]
2403    async fn test_clear_peer_session_empty_peer_name() {
2404        use std::env;
2405
2406        let temp_dir = env::temp_dir();
2407        let process_id = std::process::id();
2408        let timestamp = std::time::SystemTime::now()
2409            .duration_since(std::time::UNIX_EPOCH)
2410            .unwrap()
2411            .as_millis();
2412
2413        let db_path = temp_dir.join(format!(
2414            "test_clear_empty_peer_{}_{}.db",
2415            process_id, timestamp
2416        ));
2417        let db_path_str = db_path.to_str().unwrap();
2418        let mut bridge = SignalBridge::new(db_path_str)
2419            .await
2420            .expect("Failed to create bridge");
2421
2422        let result = bridge.clear_peer_session("").await;
2423        assert!(result.is_err());
2424        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2425            assert_eq!(msg, "Specify a peer name");
2426        } else {
2427            panic!("Expected InvalidInput error for empty peer name");
2428        }
2429    }
2430
2431    #[tokio::test]
2432    async fn test_encrypt_message_session_not_found() {
2433        use std::env;
2434
2435        let temp_dir = env::temp_dir();
2436        let process_id = std::process::id();
2437        let timestamp = std::time::SystemTime::now()
2438            .duration_since(std::time::UNIX_EPOCH)
2439            .unwrap()
2440            .as_millis();
2441
2442        let db_path = temp_dir.join(format!(
2443            "test_session_not_found_{}_{}.db",
2444            process_id, timestamp
2445        ));
2446        let db_path_str = db_path.to_str().unwrap();
2447        let mut bridge = SignalBridge::new(db_path_str)
2448            .await
2449            .expect("Failed to create bridge");
2450
2451        let result = bridge
2452            .encrypt_message("unknown_peer", b"test message")
2453            .await;
2454        assert!(result.is_err());
2455        if let Err(SignalBridgeError::SessionNotFound(msg)) = result {
2456            assert_eq!(
2457                msg,
2458                "Establish a session with unknown_peer before sending messages"
2459            );
2460        } else {
2461            panic!("Expected SessionNotFound error for unknown peer");
2462        }
2463    }
2464
2465    #[tokio::test]
2466    async fn test_decrypt_message_malformed_ciphertext() {
2467        use std::env;
2468
2469        let temp_dir = env::temp_dir();
2470        let process_id = std::process::id();
2471        let timestamp = std::time::SystemTime::now()
2472            .duration_since(std::time::UNIX_EPOCH)
2473            .unwrap()
2474            .as_millis();
2475
2476        let db_path = temp_dir.join(format!(
2477            "test_malformed_ciphertext_{}_{}.db",
2478            process_id, timestamp
2479        ));
2480        let db_path_str = db_path.to_str().unwrap();
2481        let mut bridge = SignalBridge::new(db_path_str)
2482            .await
2483            .expect("Failed to create bridge");
2484
2485        let malformed_data = vec![0x01, 0x02, 0x03, 0x04];
2486        let result = bridge.decrypt_message("alice", &malformed_data).await;
2487        assert!(result.is_err());
2488        if let Err(SignalBridgeError::Serialization(msg)) = result {
2489            assert_eq!(msg, "Provide a valid Signal Protocol message");
2490        } else {
2491            panic!("Expected Serialization error for malformed ciphertext");
2492        }
2493    }
2494
2495    #[tokio::test]
2496    async fn test_establish_session_malformed_bundle() {
2497        use std::env;
2498
2499        let temp_dir = env::temp_dir();
2500        let process_id = std::process::id();
2501        let timestamp = std::time::SystemTime::now()
2502            .duration_since(std::time::UNIX_EPOCH)
2503            .unwrap()
2504            .as_millis();
2505
2506        let db_path = temp_dir.join(format!(
2507            "test_malformed_bundle_{}_{}.db",
2508            process_id, timestamp
2509        ));
2510        let db_path_str = db_path.to_str().unwrap();
2511        let mut bridge = SignalBridge::new(db_path_str)
2512            .await
2513            .expect("Failed to create bridge");
2514
2515        let malformed_bundle = vec![0xFF, 0xFE, 0xFD, 0xFC];
2516        let result = bridge.establish_session("alice", &malformed_bundle).await;
2517        assert!(result.is_err());
2518        if let Err(SignalBridgeError::Serialization(msg)) = result {
2519            assert_eq!(msg, "io error: unexpected end of file");
2520        } else {
2521            panic!("Expected Serialization error for malformed bundle");
2522        }
2523    }
2524
2525    #[tokio::test]
2526    async fn test_sign_nostr_event() -> Result<(), Box<dyn std::error::Error>> {
2527        use std::env;
2528
2529        let temp_dir = env::temp_dir();
2530        let process_id = std::process::id();
2531        let timestamp = std::time::SystemTime::now()
2532            .duration_since(std::time::UNIX_EPOCH)
2533            .unwrap()
2534            .as_millis();
2535
2536        let db_path = temp_dir.join(format!("test_sign_nostr_{}_{}.db", process_id, timestamp));
2537        let db_path_str = db_path.to_str().unwrap();
2538        let mut bridge = SignalBridge::new(db_path_str).await?;
2539
2540        let _event_json = r#"{
2541            "id": "",
2542            "pubkey": "",
2543            "created_at": 1234567890,
2544            "kind": 40001,
2545            "tags": [
2546                ["p", "recipient_pubkey"]
2547            ],
2548            "content": "encrypted_content_here",
2549            "sig": ""
2550        }"#;
2551
2552        let result = bridge
2553            .sign_nostr_event(
2554                1234567890,
2555                40001,
2556                vec![vec!["p".to_string(), "recipient_pubkey".to_string()]],
2557                "encrypted_content_here",
2558            )
2559            .await;
2560        assert!(result.is_ok());
2561
2562        let (pubkey, event_id, signature) = result.unwrap();
2563        assert!(!pubkey.is_empty());
2564        assert!(!event_id.is_empty());
2565        assert!(!signature.is_empty());
2566        assert_eq!(event_id.len(), 64); // SHA256 hex should be 64 chars
2567        assert_eq!(pubkey.len(), 64); // Nostr pubkey hex should be 64 chars
2568
2569        let keys = bridge.derive_nostr_keypair().await?;
2570        assert_eq!(pubkey, keys.public_key().to_hex());
2571
2572        Ok(())
2573    }
2574
2575    #[tokio::test]
2576    async fn test_error_message_formatting() {
2577        let storage_error = SignalBridgeError::Storage("Database locked".to_string());
2578        assert_eq!(storage_error.to_string(), "Storage error: Database locked");
2579
2580        let protocol_error = SignalBridgeError::Protocol("Invalid signature".to_string());
2581        assert_eq!(
2582            protocol_error.to_string(),
2583            "Signal Protocol error: Invalid signature"
2584        );
2585
2586        let serialization_error = SignalBridgeError::Serialization("Invalid format".to_string());
2587        assert_eq!(
2588            serialization_error.to_string(),
2589            "Serialization error: Invalid format"
2590        );
2591
2592        let invalid_input_error = SignalBridgeError::InvalidInput("Name too long".to_string());
2593        assert_eq!(
2594            invalid_input_error.to_string(),
2595            "Invalid input: Name too long"
2596        );
2597
2598        let session_not_found_error = SignalBridgeError::SessionNotFound(
2599            "Establish a session with bob before sending messages".to_string(),
2600        );
2601        assert_eq!(
2602            session_not_found_error.to_string(),
2603            "Establish a session with bob before sending messages"
2604        );
2605
2606        let schema_error = SignalBridgeError::SchemaVersionTooOld;
2607        assert_eq!(
2608            schema_error.to_string(),
2609            "Update your database to a newer schema version"
2610        );
2611    }
2612
2613    #[tokio::test]
2614    async fn test_add_contact_and_establish_session_returns_rdx() {
2615        let temp_dir = std::env::temp_dir();
2616        let timestamp = SystemTime::now()
2617            .duration_since(UNIX_EPOCH)
2618            .unwrap()
2619            .as_millis();
2620
2621        let alice_db = temp_dir.join(format!("test_contact_alice_{}.db", timestamp));
2622        let bob_db = temp_dir.join(format!("test_contact_bob_{}.db", timestamp));
2623
2624        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2625        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2626
2627        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2628
2629        let bob_rdx = alice
2630            .add_contact_and_establish_session(&bob_bundle, None)
2631            .await
2632            .unwrap();
2633        assert!(bob_rdx.starts_with("RDX:"));
2634
2635        let _ = std::fs::remove_file(&alice_db);
2636        let _ = std::fs::remove_file(&bob_db);
2637    }
2638
2639    #[tokio::test]
2640    async fn test_lookup_contact_by_rdx() {
2641        let temp_dir = std::env::temp_dir();
2642        let timestamp = SystemTime::now()
2643            .duration_since(UNIX_EPOCH)
2644            .unwrap()
2645            .as_millis();
2646
2647        let alice_db = temp_dir.join(format!("test_lookup_alice_{}.db", timestamp));
2648        let bob_db = temp_dir.join(format!("test_lookup_bob_{}.db", timestamp));
2649
2650        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2651        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2652
2653        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2654        let bob_rdx = alice
2655            .add_contact_and_establish_session(&bob_bundle, None)
2656            .await
2657            .unwrap();
2658
2659        let contact = alice.lookup_contact(&bob_rdx).await.unwrap();
2660        assert_eq!(contact.rdx_fingerprint, bob_rdx);
2661        assert!(contact.user_alias.is_none());
2662        assert!(contact.has_active_session);
2663
2664        let _ = std::fs::remove_file(&alice_db);
2665        let _ = std::fs::remove_file(&bob_db);
2666    }
2667
2668    #[tokio::test]
2669    async fn test_assign_and_lookup_contact_by_alias() {
2670        let temp_dir = std::env::temp_dir();
2671        let timestamp = SystemTime::now()
2672            .duration_since(UNIX_EPOCH)
2673            .unwrap()
2674            .as_millis();
2675
2676        let alice_db = temp_dir.join(format!("test_alias_alice_{}.db", timestamp));
2677        let bob_db = temp_dir.join(format!("test_alias_bob_{}.db", timestamp));
2678
2679        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2680        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2681
2682        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2683        let bob_rdx = alice
2684            .add_contact_and_establish_session(&bob_bundle, None)
2685            .await
2686            .unwrap();
2687
2688        let contact_before = alice.lookup_contact(&bob_rdx).await.unwrap();
2689        assert!(contact_before.user_alias.is_none());
2690
2691        alice.assign_contact_alias(&bob_rdx, "bob").await.unwrap();
2692
2693        let contact_by_alias = alice.lookup_contact("bob").await.unwrap();
2694        assert_eq!(contact_by_alias.rdx_fingerprint, bob_rdx);
2695        assert_eq!(contact_by_alias.user_alias, Some("bob".to_string()));
2696
2697        let _ = std::fs::remove_file(&alice_db);
2698        let _ = std::fs::remove_file(&bob_db);
2699    }
2700
2701    #[tokio::test]
2702    async fn test_list_contacts() {
2703        let temp_dir = std::env::temp_dir();
2704        let timestamp = SystemTime::now()
2705            .duration_since(UNIX_EPOCH)
2706            .unwrap()
2707            .as_millis();
2708
2709        let alice_db = temp_dir.join(format!("test_list_alice_{}.db", timestamp));
2710        let bob_db = temp_dir.join(format!("test_list_bob_{}.db", timestamp));
2711        let charlie_db = temp_dir.join(format!("test_list_charlie_{}.db", timestamp));
2712
2713        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2714        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2715        let mut charlie = SignalBridge::new(charlie_db.to_str().unwrap())
2716            .await
2717            .unwrap();
2718
2719        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2720        let (charlie_bundle, _, _, _) = charlie.generate_pre_key_bundle().await.unwrap();
2721
2722        let bob_rdx = alice
2723            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2724            .await
2725            .unwrap();
2726        let charlie_rdx = alice
2727            .add_contact_and_establish_session(&charlie_bundle, None)
2728            .await
2729            .unwrap();
2730
2731        let contacts = alice.list_contacts().await.unwrap();
2732        assert_eq!(contacts.len(), 2);
2733
2734        let bob_contact = contacts
2735            .iter()
2736            .find(|c| c.rdx_fingerprint == bob_rdx)
2737            .unwrap();
2738        assert_eq!(bob_contact.user_alias, Some("bob".to_string()));
2739
2740        let charlie_contact = contacts
2741            .iter()
2742            .find(|c| c.rdx_fingerprint == charlie_rdx)
2743            .unwrap();
2744        assert!(charlie_contact.user_alias.is_none());
2745
2746        let _ = std::fs::remove_file(&alice_db);
2747        let _ = std::fs::remove_file(&bob_db);
2748        let _ = std::fs::remove_file(&charlie_db);
2749    }
2750
2751    #[tokio::test]
2752    async fn test_alias_with_multiple_sessions() {
2753        let temp_dir = std::env::temp_dir();
2754        let timestamp = SystemTime::now()
2755            .duration_since(UNIX_EPOCH)
2756            .unwrap()
2757            .as_millis();
2758
2759        let alice_db = temp_dir.join(format!("test_multi_alias_alice_{}.db", timestamp));
2760        let bob1_db = temp_dir.join(format!("test_multi_alias_bob1_{}.db", timestamp));
2761        let bob2_db = temp_dir.join(format!("test_multi_alias_bob2_{}.db", timestamp));
2762        let bob3_db = temp_dir.join(format!("test_multi_alias_bob3_{}.db", timestamp));
2763
2764        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2765        let mut bob1 = SignalBridge::new(bob1_db.to_str().unwrap()).await.unwrap();
2766        let mut bob2 = SignalBridge::new(bob2_db.to_str().unwrap()).await.unwrap();
2767        let mut bob3 = SignalBridge::new(bob3_db.to_str().unwrap()).await.unwrap();
2768
2769        let (bob1_bundle, _, _, _) = bob1.generate_pre_key_bundle().await.unwrap();
2770        let (bob2_bundle, _, _, _) = bob2.generate_pre_key_bundle().await.unwrap();
2771        let (bob3_bundle, _, _, _) = bob3.generate_pre_key_bundle().await.unwrap();
2772
2773        let bob1_rdx = alice
2774            .add_contact_and_establish_session(&bob1_bundle, None)
2775            .await
2776            .unwrap();
2777        let bob2_rdx = alice
2778            .add_contact_and_establish_session(&bob2_bundle, None)
2779            .await
2780            .unwrap();
2781        let bob3_rdx = alice
2782            .add_contact_and_establish_session(&bob3_bundle, None)
2783            .await
2784            .unwrap();
2785
2786        assert_ne!(bob1_rdx, bob2_rdx);
2787        assert_ne!(bob2_rdx, bob3_rdx);
2788        assert_ne!(bob1_rdx, bob3_rdx);
2789
2790        alice.assign_contact_alias(&bob3_rdx, "bob").await.unwrap();
2791
2792        let contact_by_alias = alice.lookup_contact("bob").await.unwrap();
2793        assert_eq!(contact_by_alias.rdx_fingerprint, bob3_rdx);
2794        assert_eq!(contact_by_alias.user_alias, Some("bob".to_string()));
2795
2796        let contact1 = alice.lookup_contact(&bob1_rdx).await.unwrap();
2797        assert_eq!(contact1.rdx_fingerprint, bob1_rdx);
2798        assert!(contact1.user_alias.is_none());
2799
2800        let contact2 = alice.lookup_contact(&bob2_rdx).await.unwrap();
2801        assert_eq!(contact2.rdx_fingerprint, bob2_rdx);
2802        assert!(contact2.user_alias.is_none());
2803
2804        let _ = std::fs::remove_file(&alice_db);
2805        let _ = std::fs::remove_file(&bob1_db);
2806        let _ = std::fs::remove_file(&bob2_db);
2807        let _ = std::fs::remove_file(&bob3_db);
2808    }
2809
2810    #[tokio::test]
2811    async fn test_reassigning_same_alias_to_different_contact() {
2812        let temp_dir = std::env::temp_dir();
2813        let timestamp = SystemTime::now()
2814            .duration_since(UNIX_EPOCH)
2815            .unwrap()
2816            .as_millis();
2817
2818        let alice_db = temp_dir.join(format!("test_reassign_alias_alice_{}.db", timestamp));
2819        let bob1_db = temp_dir.join(format!("test_reassign_alias_bob1_{}.db", timestamp));
2820        let bob2_db = temp_dir.join(format!("test_reassign_alias_bob2_{}.db", timestamp));
2821
2822        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2823        let mut bob1 = SignalBridge::new(bob1_db.to_str().unwrap()).await.unwrap();
2824        let mut bob2 = SignalBridge::new(bob2_db.to_str().unwrap()).await.unwrap();
2825
2826        let (bob1_bundle, _, _, _) = bob1.generate_pre_key_bundle().await.unwrap();
2827        let (bob2_bundle, _, _, _) = bob2.generate_pre_key_bundle().await.unwrap();
2828
2829        let bob1_rdx = alice
2830            .add_contact_and_establish_session(&bob1_bundle, None)
2831            .await
2832            .unwrap();
2833        let bob2_rdx = alice
2834            .add_contact_and_establish_session(&bob2_bundle, None)
2835            .await
2836            .unwrap();
2837
2838        alice.assign_contact_alias(&bob1_rdx, "bob").await.unwrap();
2839
2840        let contact = alice.lookup_contact("bob").await.unwrap();
2841        assert_eq!(contact.rdx_fingerprint, bob1_rdx);
2842
2843        let result = alice.assign_contact_alias(&bob2_rdx, "bob").await;
2844        assert!(result.is_err(), "Should fail when alias is already taken");
2845        assert!(
2846            result.unwrap_err().to_string().contains("already assigned"),
2847            "Error message should indicate alias is already assigned"
2848        );
2849
2850        let contact_still = alice.lookup_contact("bob").await.unwrap();
2851        assert_eq!(
2852            contact_still.rdx_fingerprint, bob1_rdx,
2853            "Alias 'bob' should still point to bob1"
2854        );
2855
2856        let _ = std::fs::remove_file(&alice_db);
2857        let _ = std::fs::remove_file(&bob1_db);
2858        let _ = std::fs::remove_file(&bob2_db);
2859    }
2860
2861    #[tokio::test]
2862    async fn test_generate_bundle_announcement_includes_rdx_tag() {
2863        let temp_dir = std::env::temp_dir();
2864        let timestamp = SystemTime::now()
2865            .duration_since(UNIX_EPOCH)
2866            .unwrap()
2867            .as_millis();
2868        let db_path = temp_dir.join(format!("test_generate_announcement_{}.db", timestamp));
2869
2870        let mut alice = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
2871
2872        let bundle_info = alice
2873            .generate_prekey_bundle_announcement("1.0.0-test")
2874            .await
2875            .unwrap();
2876        let event: serde_json::Value =
2877            serde_json::from_str(&bundle_info.announcement_json).unwrap();
2878
2879        assert_eq!(event["kind"], 30078);
2880        assert!(event["content"].is_string());
2881
2882        let tags = event["tags"].as_array().unwrap();
2883        let rdx_tag = tags
2884            .iter()
2885            .find(|t| t[0] == "rdx")
2886            .expect("RDX tag should be present");
2887        let rdx_value = rdx_tag[1].as_str().unwrap();
2888        assert!(rdx_value.starts_with("RDX:"));
2889
2890        let version_tag = tags
2891            .iter()
2892            .find(|t| t[0] == "radix_version")
2893            .expect("Version tag should be present");
2894        assert_eq!(version_tag[1], "1.0.0-test");
2895
2896        let _ = std::fs::remove_file(&db_path);
2897    }
2898
2899    #[tokio::test]
2900    async fn test_add_contact_and_establish_session() {
2901        let temp_dir = std::env::temp_dir();
2902        let timestamp = SystemTime::now()
2903            .duration_since(UNIX_EPOCH)
2904            .unwrap()
2905            .as_millis();
2906        let alice_db = temp_dir.join(format!("test_contact_session_alice_{}.db", timestamp));
2907        let bob_db = temp_dir.join(format!("test_contact_session_bob_{}.db", timestamp));
2908
2909        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2910        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2911
2912        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2913
2914        let bob_rdx = alice
2915            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2916            .await
2917            .unwrap();
2918
2919        assert!(bob_rdx.starts_with("RDX:"));
2920
2921        let contact = alice.lookup_contact(&bob_rdx).await.unwrap();
2922        assert_eq!(contact.rdx_fingerprint, bob_rdx);
2923        assert_eq!(contact.user_alias, Some("bob".to_string()));
2924        assert!(contact.has_active_session);
2925
2926        let plaintext = b"Hello Bob!";
2927        let ciphertext = alice.encrypt_message(&bob_rdx, plaintext).await.unwrap();
2928        assert!(!ciphertext.is_empty());
2929
2930        let _ = std::fs::remove_file(&alice_db);
2931        let _ = std::fs::remove_file(&bob_db);
2932    }
2933
2934    #[tokio::test]
2935    async fn test_extract_rdx_from_bundle_without_establishing_session() {
2936        let temp_dir = std::env::temp_dir();
2937        let timestamp = SystemTime::now()
2938            .duration_since(UNIX_EPOCH)
2939            .unwrap()
2940            .as_millis();
2941
2942        let alice_db = temp_dir.join(format!("test_extract_rdx_alice_{}.db", timestamp));
2943        let bob_db = temp_dir.join(format!("test_extract_rdx_bob_{}.db", timestamp));
2944
2945        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2946        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2947
2948        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2949
2950        let extracted_rdx = extract_rdx_from_bundle(&mut alice, &bob_bundle).unwrap();
2951
2952        assert!(extracted_rdx.starts_with("RDX:"));
2953
2954        let result = alice.encrypt_message(&extracted_rdx, b"test").await;
2955        assert!(
2956            result.is_err(),
2957            "Should not be able to encrypt without establishing session"
2958        );
2959
2960        let bob_rdx = alice
2961            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2962            .await
2963            .unwrap();
2964
2965        assert_eq!(
2966            extracted_rdx, bob_rdx,
2967            "RDX extracted before session should match RDX after session establishment"
2968        );
2969
2970        let _ = std::fs::remove_file(&alice_db);
2971        let _ = std::fs::remove_file(&bob_db);
2972    }
2973
2974    #[tokio::test]
2975    async fn test_extract_rdx_from_bundle_base64() {
2976        let temp_dir = std::env::temp_dir();
2977        let timestamp = SystemTime::now()
2978            .duration_since(UNIX_EPOCH)
2979            .unwrap()
2980            .as_millis();
2981
2982        let alice_db = temp_dir.join(format!("test_extract_rdx_base64_alice_{}.db", timestamp));
2983        let bob_db = temp_dir.join(format!("test_extract_rdx_base64_bob_{}.db", timestamp));
2984
2985        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2986        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2987
2988        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2989        let bob_bundle_base64 = base64::engine::general_purpose::STANDARD.encode(&bob_bundle);
2990
2991        let extracted_rdx = extract_rdx_from_bundle_base64(&mut alice, &bob_bundle_base64).unwrap();
2992
2993        assert!(extracted_rdx.starts_with("RDX:"));
2994
2995        let bob_rdx = alice
2996            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2997            .await
2998            .unwrap();
2999
3000        assert_eq!(
3001            extracted_rdx, bob_rdx,
3002            "RDX extracted from base64 should match RDX after session establishment"
3003        );
3004
3005        let _ = std::fs::remove_file(&alice_db);
3006        let _ = std::fs::remove_file(&bob_db);
3007    }
3008
3009    #[tokio::test]
3010    async fn test_generate_empty_bundle_announcement() {
3011        let temp_dir = std::env::temp_dir();
3012        let timestamp = SystemTime::now()
3013            .duration_since(UNIX_EPOCH)
3014            .unwrap()
3015            .as_millis();
3016
3017        let db_path = temp_dir.join(format!("test_empty_bundle_{}.db", timestamp));
3018        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
3019
3020        let announcement_json = bridge
3021            .generate_empty_bundle_announcement("0.4.0")
3022            .await
3023            .unwrap();
3024
3025        let event: serde_json::Value = serde_json::from_str(&announcement_json).unwrap();
3026
3027        assert_eq!(event["kind"].as_u64().unwrap(), 30078);
3028        assert_eq!(event["content"].as_str().unwrap(), "");
3029
3030        let tags = event["tags"].as_array().unwrap();
3031        let d_tag = tags.iter().find(|t| t[0] == "d").unwrap();
3032        assert_eq!(d_tag[1].as_str().unwrap(), "radix_prekey_bundle_v1");
3033
3034        let version_tag = tags.iter().find(|t| t[0] == "radix_version").unwrap();
3035        assert_eq!(version_tag[1].as_str().unwrap(), "0.4.0");
3036
3037        assert!(
3038            !tags.iter().any(|t| t[0] == "rdx"),
3039            "Empty bundle should not contain rdx tag"
3040        );
3041
3042        let _ = std::fs::remove_file(&db_path);
3043    }
3044
3045    #[tokio::test]
3046    async fn test_perform_key_maintenance_replenishes_pre_keys(
3047    ) -> Result<(), Box<dyn std::error::Error>> {
3048        use crate::key_rotation::MIN_PRE_KEY_COUNT;
3049        use crate::SignalBridge;
3050
3051        let temp_dir = std::env::temp_dir();
3052        let timestamp = SystemTime::now()
3053            .duration_since(UNIX_EPOCH)
3054            .unwrap()
3055            .as_millis();
3056        let db_path = temp_dir.join(format!("test_maintenance_prekeys_{}.db", timestamp));
3057        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3058
3059        // Get initial pre-key count
3060        let initial_count = bridge.storage.pre_key_store().pre_key_count().await;
3061
3062        // Perform maintenance - should not change count if above threshold
3063        bridge.perform_key_maintenance().await?;
3064
3065        let after_maintenance_count = bridge.storage.pre_key_store().pre_key_count().await;
3066        assert!(
3067            after_maintenance_count >= initial_count,
3068            "Pre-key count should not decrease after maintenance"
3069        );
3070
3071        // Clear pre-keys to below threshold and verify replenishment
3072        bridge.storage.pre_key_store().clear_all_pre_keys().await?;
3073        let empty_count = bridge.storage.pre_key_store().pre_key_count().await;
3074        assert_eq!(empty_count, 0, "Pre-keys should be cleared");
3075
3076        // Perform maintenance - should replenish
3077        bridge.perform_key_maintenance().await?;
3078
3079        let replenished_count = bridge.storage.pre_key_store().pre_key_count().await;
3080        assert!(
3081            replenished_count >= MIN_PRE_KEY_COUNT,
3082            "Pre-keys should be replenished to at least MIN_PRE_KEY_COUNT ({}), got {}",
3083            MIN_PRE_KEY_COUNT,
3084            replenished_count
3085        );
3086
3087        let _ = std::fs::remove_file(&db_path);
3088        Ok(())
3089    }
3090
3091    #[tokio::test]
3092    async fn test_perform_key_maintenance_no_rotation_for_fresh_keys(
3093    ) -> Result<(), Box<dyn std::error::Error>> {
3094        use crate::SignalBridge;
3095
3096        let temp_dir = std::env::temp_dir();
3097        let timestamp = SystemTime::now()
3098            .duration_since(UNIX_EPOCH)
3099            .unwrap()
3100            .as_millis();
3101        let db_path = temp_dir.join(format!("test_maintenance_fresh_{}.db", timestamp));
3102        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3103
3104        // Get initial signed pre-key count
3105        let initial_signed_count = bridge
3106            .storage
3107            .signed_pre_key_store()
3108            .signed_pre_key_count()
3109            .await;
3110        let initial_kyber_count = bridge
3111            .storage
3112            .kyber_pre_key_store()
3113            .kyber_pre_key_count()
3114            .await;
3115
3116        // Perform maintenance - fresh keys should not be rotated
3117        bridge.perform_key_maintenance().await?;
3118
3119        let after_signed_count = bridge
3120            .storage
3121            .signed_pre_key_store()
3122            .signed_pre_key_count()
3123            .await;
3124        let after_kyber_count = bridge
3125            .storage
3126            .kyber_pre_key_store()
3127            .kyber_pre_key_count()
3128            .await;
3129
3130        // Fresh keys should not be rotated
3131        assert_eq!(
3132            initial_signed_count, after_signed_count,
3133            "Fresh signed pre-keys should not be rotated"
3134        );
3135        assert_eq!(
3136            initial_kyber_count, after_kyber_count,
3137            "Fresh Kyber pre-keys should not be rotated"
3138        );
3139
3140        let _ = std::fs::remove_file(&db_path);
3141        Ok(())
3142    }
3143
3144    #[tokio::test]
3145    async fn test_perform_key_maintenance_idempotent() -> Result<(), Box<dyn std::error::Error>> {
3146        use crate::SignalBridge;
3147
3148        let temp_dir = std::env::temp_dir();
3149        let timestamp = SystemTime::now()
3150            .duration_since(UNIX_EPOCH)
3151            .unwrap()
3152            .as_millis();
3153        let db_path = temp_dir.join(format!("test_maintenance_idempotent_{}.db", timestamp));
3154        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3155
3156        // Perform maintenance multiple times
3157        bridge.perform_key_maintenance().await?;
3158
3159        let first_pre_key_count = bridge.storage.pre_key_store().pre_key_count().await;
3160        let first_signed_count = bridge
3161            .storage
3162            .signed_pre_key_store()
3163            .signed_pre_key_count()
3164            .await;
3165
3166        bridge.perform_key_maintenance().await?;
3167
3168        let second_pre_key_count = bridge.storage.pre_key_store().pre_key_count().await;
3169        let second_signed_count = bridge
3170            .storage
3171            .signed_pre_key_store()
3172            .signed_pre_key_count()
3173            .await;
3174
3175        // Consecutive maintenance calls should not change counts (idempotent)
3176        assert_eq!(
3177            first_pre_key_count, second_pre_key_count,
3178            "Pre-key count should be stable between maintenance calls"
3179        );
3180        assert_eq!(
3181            first_signed_count, second_signed_count,
3182            "Signed pre-key count should be stable between maintenance calls"
3183        );
3184
3185        let _ = std::fs::remove_file(&db_path);
3186        Ok(())
3187    }
3188
3189    #[tokio::test]
3190    async fn test_perform_key_maintenance_rotates_kyber_keys(
3191    ) -> Result<(), Box<dyn std::error::Error>> {
3192        use crate::key_rotation::ROTATION_INTERVAL_SECS;
3193        use crate::SignalBridge;
3194        use libsignal_protocol::{
3195            kem, GenericSignedPreKey, KyberPreKeyId, KyberPreKeyRecord, KyberPreKeyStore, Timestamp,
3196        };
3197
3198        let temp_dir = std::env::temp_dir();
3199        let timestamp = SystemTime::now()
3200            .duration_since(UNIX_EPOCH)
3201            .unwrap()
3202            .as_millis();
3203        let db_path = temp_dir.join(format!("test_maintenance_kyber_{}.db", timestamp));
3204        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3205
3206        // Get initial Kyber key count
3207        let initial_kyber_count = bridge
3208            .storage
3209            .kyber_pre_key_store()
3210            .kyber_pre_key_count()
3211            .await;
3212        assert!(initial_kyber_count >= 1, "Should have initial Kyber key");
3213
3214        // Create an old Kyber key to simulate needing rotation
3215        let identity_key_pair = bridge
3216            .storage
3217            .identity_store()
3218            .get_identity_key_pair()
3219            .await?;
3220
3221        let old_timestamp =
3222            SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() - (ROTATION_INTERVAL_SECS + 1);
3223
3224        let mut rng = rand::rng();
3225        let kyber_keypair = kem::KeyPair::generate(kem::KeyType::Kyber1024, &mut rng);
3226        let kyber_signature = identity_key_pair
3227            .private_key()
3228            .calculate_signature(&kyber_keypair.public_key.serialize(), &mut rng)?;
3229        let old_kyber_key = KyberPreKeyRecord::new(
3230            KyberPreKeyId::from(999u32),
3231            Timestamp::from_epoch_millis(old_timestamp * 1000),
3232            &kyber_keypair,
3233            &kyber_signature,
3234        );
3235
3236        // Save the old key as the "current" one by giving it a high ID
3237        bridge
3238            .storage
3239            .kyber_pre_key_store()
3240            .save_kyber_pre_key(KyberPreKeyId::from(999u32), &old_kyber_key)
3241            .await?;
3242
3243        // Get the max ID before maintenance
3244        let max_id_before = bridge
3245            .storage
3246            .kyber_pre_key_store()
3247            .get_max_kyber_pre_key_id()
3248            .await?
3249            .unwrap();
3250
3251        // Perform maintenance - should rotate Kyber key
3252        bridge.perform_key_maintenance().await?;
3253
3254        // Get the max ID after maintenance
3255        let max_id_after = bridge
3256            .storage
3257            .kyber_pre_key_store()
3258            .get_max_kyber_pre_key_id()
3259            .await?
3260            .unwrap();
3261
3262        // The max ID should have increased (new key was generated)
3263        assert!(
3264            max_id_after > max_id_before,
3265            "Max Kyber key ID should increase after rotation (before: {}, after: {})",
3266            max_id_before,
3267            max_id_after
3268        );
3269
3270        let _ = std::fs::remove_file(&db_path);
3271        Ok(())
3272    }
3273
3274    #[tokio::test]
3275    async fn test_perform_key_maintenance_cleans_up_expired_kyber_keys(
3276    ) -> Result<(), Box<dyn std::error::Error>> {
3277        use crate::key_rotation::GRACE_PERIOD_SECS;
3278        use crate::SignalBridge;
3279        use libsignal_protocol::{
3280            kem, GenericSignedPreKey, KyberPreKeyId, KyberPreKeyRecord, KyberPreKeyStore, Timestamp,
3281        };
3282
3283        let temp_dir = std::env::temp_dir();
3284        let timestamp = SystemTime::now()
3285            .duration_since(UNIX_EPOCH)
3286            .unwrap()
3287            .as_millis();
3288        let db_path = temp_dir.join(format!("test_maintenance_kyber_cleanup_{}.db", timestamp));
3289        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3290
3291        let identity_key_pair = bridge
3292            .storage
3293            .identity_store()
3294            .get_identity_key_pair()
3295            .await?;
3296
3297        // Create an expired Kyber key (past grace period) with low ID so it won't trigger rotation check
3298        let expired_timestamp =
3299            SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() - (GRACE_PERIOD_SECS + 1);
3300
3301        let mut rng = rand::rng();
3302        let kyber_keypair = kem::KeyPair::generate(kem::KeyType::Kyber1024, &mut rng);
3303        let kyber_signature = identity_key_pair
3304            .private_key()
3305            .calculate_signature(&kyber_keypair.public_key.serialize(), &mut rng)?;
3306        let expired_kyber_key = KyberPreKeyRecord::new(
3307            KyberPreKeyId::from(0u32), // Use ID 0 so it's not the max and won't trigger rotation
3308            Timestamp::from_epoch_millis(expired_timestamp * 1000),
3309            &kyber_keypair,
3310            &kyber_signature,
3311        );
3312
3313        bridge
3314            .storage
3315            .kyber_pre_key_store()
3316            .save_kyber_pre_key(KyberPreKeyId::from(0u32), &expired_kyber_key)
3317            .await?;
3318
3319        let before_count = bridge
3320            .storage
3321            .kyber_pre_key_store()
3322            .kyber_pre_key_count()
3323            .await;
3324        assert!(
3325            before_count >= 2,
3326            "Should have at least 2 Kyber keys (fresh + expired)"
3327        );
3328
3329        // Perform maintenance - should clean up expired Kyber key
3330        bridge.perform_key_maintenance().await?;
3331
3332        let after_count = bridge
3333            .storage
3334            .kyber_pre_key_store()
3335            .kyber_pre_key_count()
3336            .await;
3337
3338        // Should have fewer Kyber keys after cleanup (expired one removed)
3339        assert!(
3340            after_count < before_count,
3341            "Kyber key count should decrease after cleanup (before: {}, after: {})",
3342            before_count,
3343            after_count
3344        );
3345
3346        let _ = std::fs::remove_file(&db_path);
3347        Ok(())
3348    }
3349
3350    #[tokio::test]
3351    async fn test_bundle_uses_latest_available_keys() -> Result<(), Box<dyn std::error::Error>> {
3352        use crate::key_rotation::{rotate_kyber_pre_key, rotate_signed_pre_key};
3353        use crate::SignalBridge;
3354
3355        let temp_dir = std::env::temp_dir();
3356        let timestamp = SystemTime::now()
3357            .duration_since(UNIX_EPOCH)
3358            .unwrap()
3359            .as_millis();
3360        let db_path = temp_dir.join(format!("test_bundle_latest_keys_{}.db", timestamp));
3361        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3362
3363        // Generate initial bundle
3364        let (initial_bundle_bytes, _, _, _) = bridge.generate_pre_key_bundle().await?;
3365        let initial_bundle: SerializablePreKeyBundle = bincode::deserialize(&initial_bundle_bytes)?;
3366
3367        // Initial bundle should use latest (maximum) keys
3368        // We initialize with 100 pre-keys (IDs 1-100), 1 signed (ID 1), 1 kyber (ID 1)
3369        assert_eq!(initial_bundle.pre_key_id, Some(100));
3370        assert_eq!(initial_bundle.signed_pre_key_id, 1);
3371        assert_eq!(initial_bundle.kyber_pre_key_id, 1);
3372
3373        // Force key rotation by calling rotation functions directly
3374        let identity_key_pair = bridge
3375            .storage
3376            .identity_store()
3377            .get_identity_key_pair()
3378            .await?;
3379        rotate_signed_pre_key(&mut bridge.storage, &identity_key_pair).await?;
3380        rotate_kyber_pre_key(&mut bridge.storage, &identity_key_pair).await?;
3381
3382        // Generate new bundle after rotation
3383        let (rotated_bundle_bytes, _, _, _) = bridge.generate_pre_key_bundle().await?;
3384        let rotated_bundle: SerializablePreKeyBundle = bincode::deserialize(&rotated_bundle_bytes)?;
3385
3386        // Bundle should now use the NEW (higher ID) keys, not still use #1
3387        // After rotation, we expect:
3388        // - SignedPreKey #2 (rotated from #1)
3389        // - KyberPreKey #2 (rotated from #1)
3390        // - PreKey should still be from available pool (may still be #1 if not consumed)
3391        assert_eq!(
3392            rotated_bundle.signed_pre_key_id, 2,
3393            "Bundle should use latest signed pre-key after rotation"
3394        );
3395        assert_eq!(
3396            rotated_bundle.kyber_pre_key_id, 2,
3397            "Bundle should use latest kyber pre-key after rotation"
3398        );
3399
3400        let _ = std::fs::remove_file(&db_path);
3401        Ok(())
3402    }
3403
3404    #[tokio::test]
3405    async fn test_extract_identity_from_prekey_message() -> Result<(), Box<dyn std::error::Error>> {
3406        use libsignal_protocol::PreKeySignalMessage;
3407
3408        let temp_dir = std::env::temp_dir();
3409        let timestamp = SystemTime::now()
3410            .duration_since(UNIX_EPOCH)
3411            .unwrap()
3412            .as_millis();
3413
3414        let alice_db_path = temp_dir.join(format!("test_extract_alice_{}.db", timestamp));
3415        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3416
3417        let bob_db_path = temp_dir.join(format!("test_extract_bob_{}.db", timestamp));
3418        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3419
3420        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3421        bob_bridge
3422            .establish_session("alice", &alice_bundle_bytes)
3423            .await?;
3424
3425        let plaintext = b"Hello Alice!";
3426        let ciphertext = bob_bridge.encrypt_message("alice", plaintext).await?;
3427
3428        let prekey_msg = PreKeySignalMessage::try_from(ciphertext.as_ref())?;
3429        let extracted_identity_key = prekey_msg.identity_key();
3430
3431        let bob_identity_key_pair = bob_bridge
3432            .storage
3433            .identity_store()
3434            .get_identity_key_pair()
3435            .await?;
3436        let bob_identity_key = bob_identity_key_pair.identity_key();
3437
3438        assert_eq!(
3439            extracted_identity_key.serialize(),
3440            bob_identity_key.serialize(),
3441            "Alice should be able to extract Bob's identity key from the PreKeySignalMessage"
3442        );
3443
3444        let _ = std::fs::remove_file(&alice_db_path);
3445        let _ = std::fs::remove_file(&bob_db_path);
3446        Ok(())
3447    }
3448
3449    #[tokio::test]
3450    async fn test_first_message_is_prekey_signal_message() -> Result<(), Box<dyn std::error::Error>>
3451    {
3452        use libsignal_protocol::PreKeySignalMessage;
3453
3454        let temp_dir = std::env::temp_dir();
3455        let timestamp = SystemTime::now()
3456            .duration_since(UNIX_EPOCH)
3457            .unwrap()
3458            .as_millis();
3459
3460        let alice_db_path = temp_dir.join(format!("test_prekey_type_alice_{}.db", timestamp));
3461        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3462
3463        let bob_db_path = temp_dir.join(format!("test_prekey_type_bob_{}.db", timestamp));
3464        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3465
3466        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3467        bob_bridge
3468            .establish_session("alice", &alice_bundle_bytes)
3469            .await?;
3470
3471        let plaintext = b"Hello Alice!";
3472        let ciphertext = bob_bridge.encrypt_message("alice", plaintext).await?;
3473
3474        assert!(
3475            PreKeySignalMessage::try_from(ciphertext.as_ref()).is_ok(),
3476            "First message to a new recipient must be a PreKeySignalMessage containing sender identity"
3477        );
3478
3479        let _ = std::fs::remove_file(&alice_db_path);
3480        let _ = std::fs::remove_file(&bob_db_path);
3481        Ok(())
3482    }
3483
3484    #[tokio::test]
3485    async fn test_decrypt_initial_message_from_unknown_sender(
3486    ) -> Result<(), Box<dyn std::error::Error>> {
3487        let temp_dir = std::env::temp_dir();
3488        let timestamp = SystemTime::now()
3489            .duration_since(UNIX_EPOCH)
3490            .unwrap()
3491            .as_millis();
3492
3493        let alice_db_path = temp_dir.join(format!("test_unknown_alice_{}.db", timestamp));
3494        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3495
3496        let bob_db_path = temp_dir.join(format!("test_unknown_bob_{}.db", timestamp));
3497        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3498
3499        let bob_nostr_keypair = bob_bridge.derive_nostr_keypair().await?;
3500        let bob_nostr_pubkey = bob_nostr_keypair.public_key().to_string();
3501
3502        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3503        let alice_rdx = bob_bridge
3504            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3505            .await?;
3506
3507        let plaintext = b"Hello Alice!";
3508        let ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3509
3510        let result = alice_bridge
3511            .decrypt_message(&bob_nostr_pubkey, &ciphertext)
3512            .await?;
3513
3514        assert_eq!(result.plaintext, plaintext);
3515        assert!(result.should_republish_bundle);
3516
3517        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3518        assert!(bob_contact.rdx_fingerprint.starts_with("RDX:"));
3519        assert_eq!(bob_contact.nostr_pubkey, bob_nostr_pubkey);
3520        assert!(bob_contact
3521            .user_alias
3522            .as_ref()
3523            .unwrap()
3524            .starts_with("Unknown-"));
3525
3526        let response = b"Hi Bob!";
3527        let response_ciphertext = alice_bridge
3528            .encrypt_message(&bob_contact.rdx_fingerprint, response)
3529            .await?;
3530
3531        let decrypted_response = bob_bridge
3532            .decrypt_message(
3533                &alice_bridge
3534                    .derive_nostr_keypair()
3535                    .await?
3536                    .public_key()
3537                    .to_string(),
3538                &response_ciphertext,
3539            )
3540            .await?;
3541
3542        assert_eq!(decrypted_response.plaintext, response);
3543
3544        let _ = std::fs::remove_file(&alice_db_path);
3545        let _ = std::fs::remove_file(&bob_db_path);
3546        Ok(())
3547    }
3548
3549    #[tokio::test]
3550    async fn test_bidirectional_session_uses_signal_message(
3551    ) -> Result<(), Box<dyn std::error::Error>> {
3552        use libsignal_protocol::{PreKeySignalMessage, SignalMessage};
3553
3554        let temp_dir = std::env::temp_dir();
3555        let timestamp = SystemTime::now()
3556            .duration_since(UNIX_EPOCH)
3557            .unwrap()
3558            .as_millis();
3559
3560        let alice_db_path = temp_dir.join(format!("test_bidi_alice_{}.db", timestamp));
3561        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3562
3563        let bob_db_path = temp_dir.join(format!("test_bidi_bob_{}.db", timestamp));
3564        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3565
3566        let alice_nostr_pubkey = alice_bridge
3567            .derive_nostr_keypair()
3568            .await?
3569            .public_key()
3570            .to_string();
3571        let bob_nostr_pubkey = bob_bridge
3572            .derive_nostr_keypair()
3573            .await?
3574            .public_key()
3575            .to_string();
3576
3577        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3578        let alice_rdx = bob_bridge
3579            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3580            .await?;
3581
3582        let first_msg = b"Hello Alice!";
3583        let first_ciphertext = bob_bridge.encrypt_message(&alice_rdx, first_msg).await?;
3584
3585        assert!(
3586            PreKeySignalMessage::try_from(first_ciphertext.as_ref()).is_ok(),
3587            "First message from Bob should be PreKeySignalMessage"
3588        );
3589
3590        let result = alice_bridge
3591            .decrypt_message(&bob_nostr_pubkey, &first_ciphertext)
3592            .await?;
3593        assert_eq!(result.plaintext, first_msg);
3594
3595        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3596
3597        let alice_response = b"Hi Bob!";
3598        let alice_response_ciphertext = alice_bridge
3599            .encrypt_message(&bob_contact.rdx_fingerprint, alice_response)
3600            .await?;
3601
3602        assert!(
3603            SignalMessage::try_from(alice_response_ciphertext.as_ref()).is_ok(),
3604            "Alice's response uses SignalMessage (session established on her side during decrypt)"
3605        );
3606
3607        let bob_result = bob_bridge
3608            .decrypt_message(&alice_nostr_pubkey, &alice_response_ciphertext)
3609            .await?;
3610        assert_eq!(bob_result.plaintext, alice_response);
3611
3612        let second_msg = b"How are you?";
3613        let second_ciphertext = bob_bridge.encrypt_message(&alice_rdx, second_msg).await?;
3614
3615        assert!(
3616            SignalMessage::try_from(second_ciphertext.as_ref()).is_ok(),
3617            "After bidirectional session established, should use SignalMessage"
3618        );
3619
3620        let second_result = alice_bridge
3621            .decrypt_message(&bob_nostr_pubkey, &second_ciphertext)
3622            .await?;
3623        assert_eq!(second_result.plaintext, second_msg);
3624        assert!(
3625            !second_result.should_republish_bundle,
3626            "No pre-key consumed in SignalMessage"
3627        );
3628
3629        let _ = std::fs::remove_file(&alice_db_path);
3630        let _ = std::fs::remove_file(&bob_db_path);
3631        Ok(())
3632    }
3633
3634    #[tokio::test]
3635    async fn test_encrypt_message_stores_in_history() -> Result<(), Box<dyn std::error::Error>> {
3636        let temp_dir = std::env::temp_dir();
3637        let timestamp = SystemTime::now()
3638            .duration_since(UNIX_EPOCH)
3639            .unwrap()
3640            .as_millis();
3641
3642        let alice_db_path = temp_dir.join(format!("test_encrypt_history_alice_{}.db", timestamp));
3643        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3644
3645        let bob_db_path = temp_dir.join(format!("test_encrypt_history_bob_{}.db", timestamp));
3646        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3647
3648        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3649        let alice_rdx = bob_bridge
3650            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3651            .await?;
3652
3653        let plaintext = b"Test outgoing message";
3654        let _ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3655
3656        let messages = bob_bridge
3657            .storage
3658            .message_history()
3659            .get_conversation_messages(&alice_rdx, 10, 0)?;
3660
3661        assert_eq!(messages.len(), 1, "Should have stored 1 outgoing message");
3662        assert_eq!(
3663            messages[0].direction,
3664            crate::message_history::MessageDirection::Outgoing
3665        );
3666        assert_eq!(
3667            messages[0].content,
3668            String::from_utf8(plaintext.to_vec()).unwrap()
3669        );
3670
3671        let _ = std::fs::remove_file(&alice_db_path);
3672        let _ = std::fs::remove_file(&bob_db_path);
3673        Ok(())
3674    }
3675
3676    #[tokio::test]
3677    async fn test_decrypt_message_stores_in_history() -> Result<(), Box<dyn std::error::Error>> {
3678        let temp_dir = std::env::temp_dir();
3679        let timestamp = SystemTime::now()
3680            .duration_since(UNIX_EPOCH)
3681            .unwrap()
3682            .as_millis();
3683
3684        let alice_db_path = temp_dir.join(format!("test_decrypt_history_alice_{}.db", timestamp));
3685        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3686
3687        let bob_db_path = temp_dir.join(format!("test_decrypt_history_bob_{}.db", timestamp));
3688        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3689
3690        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3691
3692        let alice_rdx = bob_bridge
3693            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3694            .await?;
3695
3696        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3697        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3698
3699        let plaintext = b"Test incoming message";
3700        let ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3701
3702        let _result = alice_bridge.decrypt_message("", &ciphertext).await?;
3703
3704        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3705
3706        let messages = alice_bridge
3707            .storage
3708            .message_history()
3709            .get_conversation_messages(&bob_contact.rdx_fingerprint, 10, 0)?;
3710
3711        assert_eq!(messages.len(), 1, "Should have stored 1 incoming message");
3712        assert_eq!(
3713            messages[0].direction,
3714            crate::message_history::MessageDirection::Incoming
3715        );
3716        assert_eq!(
3717            messages[0].content,
3718            String::from_utf8(plaintext.to_vec()).unwrap()
3719        );
3720        assert!(
3721            messages[0].was_prekey_message,
3722            "First message should be prekey"
3723        );
3724        assert!(
3725            messages[0].session_established,
3726            "Session should be established"
3727        );
3728
3729        let _ = std::fs::remove_file(&alice_db_path);
3730        let _ = std::fs::remove_file(&bob_db_path);
3731        Ok(())
3732    }
3733
3734    #[tokio::test]
3735    async fn test_bidirectional_message_history() -> Result<(), Box<dyn std::error::Error>> {
3736        let temp_dir = std::env::temp_dir();
3737        let timestamp = SystemTime::now()
3738            .duration_since(UNIX_EPOCH)
3739            .unwrap()
3740            .as_millis();
3741
3742        let alice_db_path =
3743            temp_dir.join(format!("test_bidirectional_history_alice_{}.db", timestamp));
3744        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3745
3746        let bob_db_path = temp_dir.join(format!("test_bidirectional_history_bob_{}.db", timestamp));
3747        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3748
3749        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3750        let alice_keys = alice_bridge.derive_nostr_keypair().await?;
3751        let alice_nostr_pubkey = alice_keys.public_key().to_hex();
3752
3753        let (bob_bundle_bytes, _, _, _) = bob_bridge.generate_pre_key_bundle().await?;
3754        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3755        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3756
3757        let bob_rdx = alice_bridge
3758            .add_contact_and_establish_session(&bob_bundle_bytes, Some("Bob"))
3759            .await?;
3760        let alice_rdx = bob_bridge
3761            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3762            .await?;
3763
3764        let msg1 = b"Hello Bob!";
3765        let cipher1 = alice_bridge.encrypt_message(&bob_rdx, msg1).await?;
3766        let _result1 = bob_bridge
3767            .decrypt_message(&alice_nostr_pubkey, &cipher1)
3768            .await?;
3769
3770        let msg2 = b"Hi Alice!";
3771        let cipher2 = bob_bridge.encrypt_message(&alice_rdx, msg2).await?;
3772        let _result2 = alice_bridge
3773            .decrypt_message(&bob_nostr_pubkey, &cipher2)
3774            .await?;
3775
3776        let msg3 = b"How are you?";
3777        let cipher3 = alice_bridge.encrypt_message(&bob_rdx, msg3).await?;
3778        let _result3 = bob_bridge
3779            .decrypt_message(&alice_nostr_pubkey, &cipher3)
3780            .await?;
3781
3782        let alice_messages = alice_bridge
3783            .storage
3784            .message_history()
3785            .get_conversation_messages(&bob_rdx, 10, 0)?;
3786
3787        assert_eq!(
3788            alice_messages.len(),
3789            3,
3790            "Alice should have 3 messages (2 sent, 1 received)"
3791        );
3792
3793        let outgoing = alice_messages
3794            .iter()
3795            .filter(|m| m.direction == crate::message_history::MessageDirection::Outgoing)
3796            .count();
3797        let incoming = alice_messages
3798            .iter()
3799            .filter(|m| m.direction == crate::message_history::MessageDirection::Incoming)
3800            .count();
3801
3802        assert_eq!(outgoing, 2, "Alice sent 2 messages");
3803        assert_eq!(incoming, 1, "Alice received 1 message");
3804
3805        let bob_messages = bob_bridge
3806            .storage
3807            .message_history()
3808            .get_conversation_messages(&alice_rdx, 10, 0)?;
3809
3810        assert_eq!(
3811            bob_messages.len(),
3812            3,
3813            "Bob should have 3 messages (1 sent, 2 received)"
3814        );
3815
3816        let _ = std::fs::remove_file(&alice_db_path);
3817        let _ = std::fs::remove_file(&bob_db_path);
3818        Ok(())
3819    }
3820
3821    #[tokio::test]
3822    async fn test_conversation_list_ordering() -> Result<(), Box<dyn std::error::Error>> {
3823        let temp_dir = std::env::temp_dir();
3824        let timestamp = SystemTime::now()
3825            .duration_since(UNIX_EPOCH)
3826            .unwrap()
3827            .as_millis();
3828
3829        let alice_db_path =
3830            temp_dir.join(format!("test_conversation_ordering_alice_{}.db", timestamp));
3831        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3832
3833        let bob_db_path = temp_dir.join(format!("test_conversation_ordering_bob_{}.db", timestamp));
3834        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3835
3836        let charlie_db_path = temp_dir.join(format!(
3837            "test_conversation_ordering_charlie_{}.db",
3838            timestamp
3839        ));
3840        let mut charlie_bridge = SignalBridge::new(charlie_db_path.to_str().unwrap()).await?;
3841
3842        let (bob_bundle_bytes, _, _, _) = bob_bridge.generate_pre_key_bundle().await?;
3843        let bob_rdx = alice_bridge
3844            .add_contact_and_establish_session(&bob_bundle_bytes, Some("Bob"))
3845            .await?;
3846
3847        let (charlie_bundle_bytes, _, _, _) = charlie_bridge.generate_pre_key_bundle().await?;
3848        let charlie_rdx = alice_bridge
3849            .add_contact_and_establish_session(&charlie_bundle_bytes, Some("Charlie"))
3850            .await?;
3851
3852        let bob_msg = b"From Bob";
3853        let bob_cipher = alice_bridge.encrypt_message(&bob_rdx, bob_msg).await?;
3854
3855        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3856        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3857
3858        let _bob_result = bob_bridge
3859            .decrypt_message(&bob_nostr_pubkey, &bob_cipher)
3860            .await?;
3861
3862        tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
3863
3864        let charlie_msg = b"From Charlie";
3865        let charlie_cipher = alice_bridge
3866            .encrypt_message(&charlie_rdx, charlie_msg)
3867            .await?;
3868
3869        let charlie_keys = charlie_bridge.derive_nostr_keypair().await?;
3870        let charlie_nostr_pubkey = charlie_keys.public_key().to_hex();
3871
3872        let _charlie_result = charlie_bridge
3873            .decrypt_message(&charlie_nostr_pubkey, &charlie_cipher)
3874            .await?;
3875
3876        let conversations = alice_bridge
3877            .storage
3878            .message_history()
3879            .get_conversations(false)?;
3880
3881        assert_eq!(conversations.len(), 2, "Alice should have 2 conversations");
3882        assert!(
3883            conversations[0].last_message_timestamp > conversations[1].last_message_timestamp,
3884            "Conversations should be ordered by most recent message"
3885        );
3886
3887        let _ = std::fs::remove_file(&alice_db_path);
3888        let _ = std::fs::remove_file(&bob_db_path);
3889        let _ = std::fs::remove_file(&charlie_db_path);
3890        Ok(())
3891    }
3892}