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 mark_conversation_read_up_to(
1221            bridge: &mut SignalBridge,
1222            rdx_fingerprint: &str,
1223            up_to_timestamp: u64,
1224        ) -> Result<()>;
1225
1226        fn delete_message(bridge: &mut SignalBridge, message_id: i64) -> Result<()>;
1227
1228        fn delete_conversation(bridge: &mut SignalBridge, rdx_fingerprint: &str) -> Result<()>;
1229
1230        fn get_unread_count(bridge: &mut SignalBridge, rdx_fingerprint: &str) -> Result<u32>;
1231    }
1232}
1233
1234/// Creates a new Signal Protocol bridge with SQLite storage
1235///
1236/// # Arguments
1237/// * `db_path` - Path to SQLite database file
1238pub fn new_signal_bridge(db_path: &str) -> Result<Box<SignalBridge>, Box<dyn std::error::Error>> {
1239    let rt = tokio::runtime::Runtime::new()
1240        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1241    let bridge = rt
1242        .block_on(SignalBridge::new(db_path))
1243        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1244    Ok(Box::new(bridge))
1245}
1246
1247/// Encrypts a message for a peer using Signal Protocol
1248///
1249/// # Arguments
1250/// * `bridge` - Signal bridge instance
1251/// * `peer` - Recipient's RDX fingerprint or Nostr pubkey
1252/// * `plaintext` - Message bytes to encrypt
1253pub fn encrypt_message(
1254    bridge: &mut SignalBridge,
1255    peer: &str,
1256    plaintext: &[u8],
1257) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
1258    let rt = tokio::runtime::Runtime::new()
1259        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1260    rt.block_on(bridge.encrypt_message(peer, plaintext))
1261        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1262}
1263
1264/// Decrypts an incoming Signal Protocol message
1265///
1266/// # Arguments
1267/// * `bridge` - Signal bridge instance
1268/// * `peer` - Sender's RDX fingerprint or Nostr pubkey (peer hint)
1269/// * `ciphertext` - Encrypted message bytes
1270pub fn decrypt_message(
1271    bridge: &mut SignalBridge,
1272    peer: &str,
1273    ciphertext: &[u8],
1274) -> Result<ffi::DecryptionResult, Box<dyn std::error::Error>> {
1275    let rt = tokio::runtime::Runtime::new()
1276        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1277    let result = rt
1278        .block_on(bridge.decrypt_message(peer, ciphertext))
1279        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1280    Ok(ffi::DecryptionResult {
1281        plaintext: result.plaintext,
1282        should_republish_bundle: result.should_republish_bundle,
1283    })
1284}
1285
1286/// Establishes a Signal Protocol session from a prekey bundle
1287///
1288/// # Arguments
1289/// * `bridge` - Signal bridge instance
1290/// * `peer` - Peer's RDX fingerprint or Nostr pubkey
1291/// * `bundle` - Serialized prekey bundle bytes
1292pub fn establish_session(
1293    bridge: &mut SignalBridge,
1294    peer: &str,
1295    bundle: &[u8],
1296) -> Result<(), Box<dyn std::error::Error>> {
1297    let rt = tokio::runtime::Runtime::new()
1298        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1299    rt.block_on(bridge.establish_session(peer, bundle))
1300        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1301}
1302
1303/// Generates a new prekey bundle with metadata for publishing
1304///
1305/// # Arguments
1306/// * `bridge` - Signal bridge instance
1307///
1308/// # Returns
1309/// PreKeyBundleWithMetadata containing bundle bytes and key IDs
1310pub fn generate_pre_key_bundle(
1311    bridge: &mut SignalBridge,
1312) -> Result<ffi::PreKeyBundleWithMetadata, Box<dyn std::error::Error>> {
1313    let rt = tokio::runtime::Runtime::new()
1314        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1315    let (bundle_bytes, pre_key_id, signed_pre_key_id, kyber_pre_key_id) = rt
1316        .block_on(bridge.generate_pre_key_bundle())
1317        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1318    Ok(ffi::PreKeyBundleWithMetadata {
1319        bundle_bytes,
1320        pre_key_id,
1321        signed_pre_key_id,
1322        kyber_pre_key_id,
1323    })
1324}
1325
1326/// Clears the Signal Protocol session with a specific peer
1327///
1328/// # Arguments
1329/// * `bridge` - Signal bridge instance
1330/// * `peer` - Peer's RDX fingerprint or Nostr pubkey
1331pub fn clear_peer_session(
1332    bridge: &mut SignalBridge,
1333    peer: &str,
1334) -> Result<(), Box<dyn std::error::Error>> {
1335    let rt = tokio::runtime::Runtime::new()
1336        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1337    rt.block_on(bridge.clear_peer_session(peer))
1338        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1339}
1340
1341/// Clears all Signal Protocol sessions
1342///
1343/// # Arguments
1344/// * `bridge` - Signal bridge instance
1345pub fn clear_all_sessions(bridge: &mut SignalBridge) -> Result<(), Box<dyn std::error::Error>> {
1346    let rt = tokio::runtime::Runtime::new()
1347        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1348    rt.block_on(bridge.clear_all_sessions())
1349        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1350}
1351
1352/// Resets the node's Signal Protocol identity (generates new identity key)
1353///
1354/// # Arguments
1355/// * `bridge` - Signal bridge instance
1356pub fn reset_identity(bridge: &mut SignalBridge) -> Result<(), Box<dyn std::error::Error>> {
1357    let rt = tokio::runtime::Runtime::new()
1358        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1359    rt.block_on(bridge.reset_identity())
1360        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1361}
1362
1363/// Performs periodic key rotation and cleanup
1364///
1365/// # Arguments
1366/// * `bridge` - Signal bridge instance
1367///
1368/// # Returns
1369/// KeyMaintenanceResult indicating which keys were rotated
1370pub fn perform_key_maintenance(
1371    bridge: &mut SignalBridge,
1372) -> Result<ffi::KeyMaintenanceResult, Box<dyn std::error::Error>> {
1373    let rt = tokio::runtime::Runtime::new()
1374        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1375    let result = rt
1376        .block_on(bridge.perform_key_maintenance())
1377        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1378    Ok(ffi::KeyMaintenanceResult {
1379        signed_pre_key_rotated: result.signed_pre_key_rotated,
1380        kyber_pre_key_rotated: result.kyber_pre_key_rotated,
1381        pre_keys_replenished: result.pre_keys_replenished,
1382    })
1383}
1384
1385/// Records a published bundle to track used keys
1386///
1387/// # Arguments
1388/// * `bridge` - Signal bridge instance
1389/// * `pre_key_id` - One-time prekey ID
1390/// * `signed_pre_key_id` - Signed prekey ID
1391/// * `kyber_pre_key_id` - Kyber prekey ID
1392pub fn record_published_bundle(
1393    bridge: &mut SignalBridge,
1394    pre_key_id: u32,
1395    signed_pre_key_id: u32,
1396    kyber_pre_key_id: u32,
1397) -> Result<(), Box<dyn std::error::Error>> {
1398    bridge
1399        .storage
1400        .record_published_bundle(pre_key_id, signed_pre_key_id, kyber_pre_key_id)?;
1401    Ok(())
1402}
1403
1404/// Retrieves all conversations ordered by recent activity
1405///
1406/// # Arguments
1407/// * `bridge` - Signal bridge instance
1408/// * `include_archived` - Whether to include archived conversations
1409pub fn get_conversations(
1410    bridge: &mut SignalBridge,
1411    include_archived: bool,
1412) -> Result<Vec<ffi::Conversation>, Box<dyn std::error::Error>> {
1413    let conversations = bridge
1414        .storage
1415        .message_history()
1416        .get_conversations(include_archived)?;
1417
1418    Ok(conversations
1419        .into_iter()
1420        .map(|c| ffi::Conversation {
1421            id: c.id,
1422            rdx_fingerprint: c.rdx_fingerprint,
1423            last_message_timestamp: c.last_message_timestamp,
1424            unread_count: c.unread_count,
1425            archived: c.archived,
1426        })
1427        .collect())
1428}
1429
1430/// Retrieves messages for a conversation with pagination
1431///
1432/// # Arguments
1433/// * `bridge` - Signal bridge instance
1434/// * `rdx_fingerprint` - Contact's RDX fingerprint
1435/// * `limit` - Maximum number of messages to return
1436/// * `offset` - Number of messages to skip
1437pub fn get_conversation_messages(
1438    bridge: &mut SignalBridge,
1439    rdx_fingerprint: &str,
1440    limit: u32,
1441    offset: u32,
1442) -> Result<Vec<ffi::StoredMessage>, Box<dyn std::error::Error>> {
1443    let messages = bridge.storage.message_history().get_conversation_messages(
1444        rdx_fingerprint,
1445        limit,
1446        offset,
1447    )?;
1448
1449    Ok(messages
1450        .into_iter()
1451        .map(|m| ffi::StoredMessage {
1452            id: m.id,
1453            conversation_id: m.conversation_id,
1454            direction: match m.direction {
1455                MessageDirection::Incoming => ffi::MessageDirection::Incoming,
1456                MessageDirection::Outgoing => ffi::MessageDirection::Outgoing,
1457            },
1458            timestamp: m.timestamp,
1459            message_type: match m.message_type {
1460                MessageType::Text => ffi::MessageType::Text,
1461                MessageType::BundleAnnouncement => ffi::MessageType::BundleAnnouncement,
1462                MessageType::System => ffi::MessageType::System,
1463            },
1464            content: m.content,
1465            delivery_status: match m.delivery_status {
1466                DeliveryStatus::Pending => ffi::DeliveryStatus::Pending,
1467                DeliveryStatus::Sent => ffi::DeliveryStatus::Sent,
1468                DeliveryStatus::Delivered => ffi::DeliveryStatus::Delivered,
1469                DeliveryStatus::Failed => ffi::DeliveryStatus::Failed,
1470            },
1471            was_prekey_message: m.was_prekey_message,
1472            session_established: m.session_established,
1473        })
1474        .collect())
1475}
1476
1477/// Marks a conversation as read (clears unread count)
1478///
1479/// # Arguments
1480/// * `bridge` - Signal bridge instance
1481/// * `rdx_fingerprint` - Contact's RDX fingerprint
1482pub fn mark_conversation_read(
1483    bridge: &mut SignalBridge,
1484    rdx_fingerprint: &str,
1485) -> Result<(), Box<dyn std::error::Error>> {
1486    bridge
1487        .storage
1488        .message_history()
1489        .mark_conversation_read(rdx_fingerprint)?;
1490    Ok(())
1491}
1492
1493/// Marks a conversation as read up to a specific timestamp
1494///
1495/// This prevents race conditions where new messages arrive after loading history.
1496/// Only marks messages with timestamp <= up_to_timestamp as read.
1497///
1498/// # Arguments
1499/// * `bridge` - Signal bridge instance
1500/// * `rdx_fingerprint` - Contact's RDX fingerprint
1501/// * `up_to_timestamp` - Messages up to and including this timestamp are marked read
1502pub fn mark_conversation_read_up_to(
1503    bridge: &mut SignalBridge,
1504    rdx_fingerprint: &str,
1505    up_to_timestamp: u64,
1506) -> Result<(), Box<dyn std::error::Error>> {
1507    bridge
1508        .storage
1509        .message_history()
1510        .mark_conversation_read_up_to(rdx_fingerprint, up_to_timestamp)?;
1511    Ok(())
1512}
1513
1514/// Deletes a message by ID
1515///
1516/// # Arguments
1517/// * `bridge` - Signal bridge instance
1518/// * `message_id` - Database ID of message to delete
1519pub fn delete_message(
1520    bridge: &mut SignalBridge,
1521    message_id: i64,
1522) -> Result<(), Box<dyn std::error::Error>> {
1523    bridge
1524        .storage
1525        .message_history()
1526        .delete_message(message_id)?;
1527    Ok(())
1528}
1529
1530/// Deletes an entire conversation and all its messages
1531///
1532/// # Arguments
1533/// * `bridge` - Signal bridge instance
1534/// * `rdx_fingerprint` - Contact's RDX fingerprint
1535pub fn delete_conversation(
1536    bridge: &mut SignalBridge,
1537    rdx_fingerprint: &str,
1538) -> Result<(), Box<dyn std::error::Error>> {
1539    bridge
1540        .storage
1541        .message_history()
1542        .delete_conversation(rdx_fingerprint)?;
1543    Ok(())
1544}
1545
1546/// Gets the unread message count for a conversation
1547///
1548/// # Arguments
1549/// * `bridge` - Signal bridge instance
1550/// * `rdx_fingerprint` - Contact's RDX fingerprint
1551pub fn get_unread_count(
1552    bridge: &mut SignalBridge,
1553    rdx_fingerprint: &str,
1554) -> Result<u32, Box<dyn std::error::Error>> {
1555    let count = bridge
1556        .storage
1557        .message_history()
1558        .get_unread_count(rdx_fingerprint)?;
1559    Ok(count)
1560}
1561
1562/// Returns this node's RDX fingerprint
1563///
1564/// # Arguments
1565/// * `bridge` - Signal bridge instance
1566///
1567/// # Returns
1568/// Hex-encoded SHA-256 hash of node's identity public key
1569pub fn generate_node_fingerprint(
1570    bridge: &mut SignalBridge,
1571) -> Result<String, Box<dyn std::error::Error>> {
1572    let rt = tokio::runtime::Runtime::new()
1573        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1574    rt.block_on(bridge.generate_node_fingerprint())
1575        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1576}
1577
1578/// Signs a Nostr event with node's private key
1579///
1580/// # Arguments
1581/// * `bridge` - Signal bridge instance
1582/// * `event_json` - Unsigned event JSON
1583///
1584/// # Returns
1585/// Signed event JSON with id and sig fields
1586pub fn sign_nostr_event(
1587    bridge: &mut SignalBridge,
1588    event_json: &str,
1589) -> Result<String, Box<dyn std::error::Error>> {
1590    let rt = tokio::runtime::Runtime::new()
1591        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1592
1593    let mut event: serde_json::Value = serde_json::from_str(event_json)
1594        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1595
1596    let created_at = event["created_at"].as_u64().ok_or("Missing created_at")?;
1597    let kind = event["kind"].as_u64().ok_or("Missing kind")? as u32;
1598    let content = event["content"].as_str().ok_or("Missing content")?;
1599
1600    let tags_array = event["tags"].as_array().ok_or("Missing tags")?;
1601    let tags: Vec<Vec<String>> = tags_array
1602        .iter()
1603        .map(|tag| {
1604            tag.as_array()
1605                .unwrap_or(&vec![])
1606                .iter()
1607                .map(|v| v.as_str().unwrap_or("").to_string())
1608                .collect()
1609        })
1610        .collect();
1611
1612    let (pubkey, event_id, signature) = rt
1613        .block_on(bridge.sign_nostr_event(created_at, kind, tags, content))
1614        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1615
1616    event["pubkey"] = serde_json::Value::String(pubkey);
1617    event["id"] = serde_json::Value::String(event_id);
1618    event["sig"] = serde_json::Value::String(signature);
1619
1620    serde_json::to_string(&event).map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1621}
1622
1623/// Creates and signs a Nostr encrypted message event
1624///
1625/// # Arguments
1626/// * `bridge` - Signal bridge instance
1627/// * `session_id` - Recipient's session identifier
1628/// * `encrypted_content` - Hex-encoded encrypted content
1629/// * `timestamp` - Unix timestamp
1630/// * `project_version` - Protocol version string
1631///
1632/// # Returns
1633/// Signed Nostr event JSON
1634pub fn create_and_sign_encrypted_message(
1635    bridge: &mut SignalBridge,
1636    session_id: &str,
1637    encrypted_content: &str,
1638    timestamp: u64,
1639    project_version: &str,
1640) -> Result<String, Box<dyn std::error::Error>> {
1641    let rt = tokio::runtime::Runtime::new()
1642        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1643
1644    // Derive recipient's Nostr pubkey from session
1645    let recipient_pubkey_bytes = rt
1646        .block_on(bridge.derive_peer_nostr_key(session_id))
1647        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1648    let recipient_pubkey = hex::encode(&recipient_pubkey_bytes);
1649
1650    // Create the Nostr event JSON
1651    let event_json = serde_json::json!({
1652        "id": "",
1653        "pubkey": "",
1654        "created_at": timestamp,
1655        "kind": 40001,
1656        "tags": [
1657            ["p", recipient_pubkey],
1658            ["radix_version", project_version]
1659        ],
1660        "content": encrypted_content,
1661        "sig": ""
1662    });
1663
1664    // Sign the event
1665    let event_json_str = serde_json::to_string(&event_json)
1666        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1667
1668    // Use the existing signing function
1669    sign_nostr_event(bridge, &event_json_str)
1670}
1671
1672/// Creates a Nostr subscription filter for messages to this node
1673///
1674/// # Arguments
1675/// * `bridge` - Signal bridge instance
1676/// * `subscription_id` - Subscription identifier
1677/// * `since_timestamp` - Optional timestamp to filter messages since
1678///
1679/// # Returns
1680/// REQ message JSON
1681pub fn create_subscription_for_self(
1682    bridge: &mut SignalBridge,
1683    subscription_id: &str,
1684    since_timestamp: u64,
1685) -> Result<String, Box<dyn std::error::Error>> {
1686    let rt = tokio::runtime::Runtime::new()
1687        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1688    rt.block_on(bridge.create_subscription_for_self(subscription_id, since_timestamp))
1689        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1690}
1691
1692/// Updates the timestamp of the last received message
1693///
1694/// # Arguments
1695/// * `bridge` - Signal bridge instance
1696/// * `timestamp` - Unix timestamp
1697pub fn update_last_message_timestamp(
1698    bridge: &mut SignalBridge,
1699    timestamp: u64,
1700) -> Result<(), Box<dyn std::error::Error>> {
1701    let rt = tokio::runtime::Runtime::new()
1702        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })?;
1703    rt.block_on(bridge.update_last_message_timestamp(timestamp))
1704        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1705}
1706
1707/// Looks up a contact by RDX fingerprint or alias
1708///
1709/// # Arguments
1710/// * `bridge` - Signal bridge instance
1711/// * `identifier` - Contact identifier (RDX fingerprint or user alias)
1712///
1713/// # Returns
1714/// Contact information
1715pub fn lookup_contact(
1716    bridge: &mut SignalBridge,
1717    identifier: &str,
1718) -> Result<ffi::ContactInfo, Box<dyn std::error::Error>> {
1719    let rt = tokio::runtime::Runtime::new()?;
1720    let contact = rt.block_on(bridge.lookup_contact(identifier))?;
1721    Ok(ffi::ContactInfo {
1722        rdx_fingerprint: contact.rdx_fingerprint,
1723        nostr_pubkey: contact.nostr_pubkey,
1724        user_alias: contact.user_alias.unwrap_or_default(),
1725        has_active_session: contact.has_active_session,
1726    })
1727}
1728
1729/// Assigns an alias to a contact
1730///
1731/// # Arguments
1732/// * `bridge` - Signal bridge instance
1733/// * `identifier` - Contact's RDX fingerprint
1734/// * `new_alias` - User-friendly alias
1735pub fn assign_contact_alias(
1736    bridge: &mut SignalBridge,
1737    identifier: &str,
1738    new_alias: &str,
1739) -> Result<(), Box<dyn std::error::Error>> {
1740    let rt = tokio::runtime::Runtime::new()?;
1741    rt.block_on(bridge.assign_contact_alias(identifier, new_alias))
1742        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1743}
1744
1745/// Lists all known contacts
1746///
1747/// # Arguments
1748/// * `bridge` - Signal bridge instance
1749///
1750/// # Returns
1751/// Vector of contact information structs
1752pub fn list_contacts(
1753    bridge: &mut SignalBridge,
1754) -> Result<Vec<ffi::ContactInfo>, Box<dyn std::error::Error>> {
1755    let rt = tokio::runtime::Runtime::new()?;
1756    let contacts = rt.block_on(bridge.list_contacts())?;
1757    Ok(contacts
1758        .into_iter()
1759        .map(|c| ffi::ContactInfo {
1760            rdx_fingerprint: c.rdx_fingerprint,
1761            nostr_pubkey: c.nostr_pubkey,
1762            user_alias: c.user_alias.unwrap_or_default(),
1763            has_active_session: c.has_active_session,
1764        })
1765        .collect())
1766}
1767
1768/// Generates a signed prekey bundle announcement
1769///
1770/// # Arguments
1771/// * `bridge` - Signal bridge instance
1772/// * `project_version` - Protocol version string
1773///
1774/// # Returns
1775/// Bundle information with announcement JSON and prekey IDs
1776pub fn generate_prekey_bundle_announcement(
1777    bridge: &mut SignalBridge,
1778    project_version: &str,
1779) -> Result<ffi::BundleInfo, Box<dyn std::error::Error>> {
1780    let rt = tokio::runtime::Runtime::new()?;
1781    rt.block_on(bridge.generate_prekey_bundle_announcement(project_version))
1782        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1783}
1784
1785/// Generates an empty bundle announcement for unpublishing
1786///
1787/// # Arguments
1788/// * `bridge` - Signal bridge instance
1789/// * `project_version` - Protocol version string
1790///
1791/// # Returns
1792/// Signed empty bundle announcement JSON
1793pub fn generate_empty_bundle_announcement(
1794    bridge: &mut SignalBridge,
1795    project_version: &str,
1796) -> Result<String, Box<dyn std::error::Error>> {
1797    let rt = tokio::runtime::Runtime::new()?;
1798    rt.block_on(bridge.generate_empty_bundle_announcement(project_version))
1799        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1800}
1801
1802/// Adds a contact from a bundle and establishes a session
1803///
1804/// # Arguments
1805/// * `bridge` - Signal bridge instance
1806/// * `bundle_bytes` - Serialized prekey bundle
1807/// * `user_alias` - Optional user-assigned alias
1808///
1809/// # Returns
1810/// RDX fingerprint of the added contact
1811pub fn add_contact_and_establish_session(
1812    bridge: &mut SignalBridge,
1813    bundle_bytes: &[u8],
1814    user_alias: &str,
1815) -> Result<String, Box<dyn std::error::Error>> {
1816    let rt = tokio::runtime::Runtime::new()?;
1817    let alias = if user_alias.is_empty() {
1818        None
1819    } else {
1820        Some(user_alias)
1821    };
1822    rt.block_on(bridge.add_contact_and_establish_session(bundle_bytes, alias))
1823        .map_err(|e| -> Box<dyn std::error::Error> { Box::new(e) })
1824}
1825
1826/// Adds a contact from a base64-encoded bundle and establishes a session
1827///
1828/// # Arguments
1829/// * `bridge` - Signal bridge instance
1830/// * `bundle_base64` - Base64-encoded prekey bundle
1831/// * `user_alias` - Optional user-assigned alias
1832///
1833/// # Returns
1834/// RDX fingerprint of the added contact
1835pub fn add_contact_and_establish_session_from_base64(
1836    bridge: &mut SignalBridge,
1837    bundle_base64: &str,
1838    user_alias: &str,
1839) -> Result<String, Box<dyn std::error::Error>> {
1840    use base64::Engine;
1841    let bundle_bytes = base64::engine::general_purpose::STANDARD
1842        .decode(bundle_base64)
1843        .map_err(|e| -> Box<dyn std::error::Error> {
1844            Box::new(SignalBridgeError::Protocol(format!(
1845                "Base64 decode error: {}",
1846                e
1847            )))
1848        })?;
1849
1850    add_contact_and_establish_session(bridge, &bundle_bytes, user_alias)
1851}
1852
1853/// Extracts RDX fingerprint from a prekey bundle without adding contact
1854///
1855/// # Arguments
1856/// * `_bridge` - Signal bridge instance (unused)
1857/// * `bundle_bytes` - Serialized prekey bundle
1858///
1859/// # Returns
1860/// RDX fingerprint
1861pub fn extract_rdx_from_bundle(
1862    _bridge: &mut SignalBridge,
1863    bundle_bytes: &[u8],
1864) -> Result<String, Box<dyn std::error::Error>> {
1865    use libsignal_protocol::IdentityKey;
1866
1867    let bundle: SerializablePreKeyBundle =
1868        bincode::deserialize(bundle_bytes).map_err(|e| -> Box<dyn std::error::Error> {
1869            Box::new(SignalBridgeError::InvalidInput(format!(
1870                "Failed to deserialize bundle: {}",
1871                e
1872            )))
1873        })?;
1874
1875    let identity_key =
1876        IdentityKey::decode(&bundle.identity_key).map_err(|e| -> Box<dyn std::error::Error> {
1877            Box::new(SignalBridgeError::Protocol(e.to_string()))
1878        })?;
1879
1880    Ok(ContactManager::generate_identity_fingerprint_from_key(
1881        &identity_key,
1882    ))
1883}
1884
1885/// Extracts RDX fingerprint from a base64-encoded prekey bundle
1886///
1887/// # Arguments
1888/// * `bridge` - Signal bridge instance
1889/// * `bundle_base64` - Base64-encoded prekey bundle
1890///
1891/// # Returns
1892/// RDX fingerprint
1893pub fn extract_rdx_from_bundle_base64(
1894    bridge: &mut SignalBridge,
1895    bundle_base64: &str,
1896) -> Result<String, Box<dyn std::error::Error>> {
1897    use base64::Engine;
1898    let bundle_bytes = base64::engine::general_purpose::STANDARD
1899        .decode(bundle_base64)
1900        .map_err(|e| -> Box<dyn std::error::Error> {
1901            Box::new(SignalBridgeError::Protocol(format!(
1902                "Base64 decode error: {}",
1903                e
1904            )))
1905        })?;
1906
1907    extract_rdx_from_bundle(bridge, &bundle_bytes)
1908}
1909
1910#[cfg(test)]
1911mod tests {
1912    use super::*;
1913    use std::time::{SystemTime, UNIX_EPOCH};
1914
1915    #[test]
1916    fn test_libsignal_basic_types() {
1917        let device_id = DeviceId::new(1).expect("Valid device ID");
1918        let protocol_address = ProtocolAddress::new("test_device".to_string(), device_id);
1919        assert_eq!(protocol_address.name(), "test_device");
1920        assert_eq!(protocol_address.device_id(), device_id);
1921    }
1922
1923    #[tokio::test]
1924    async fn test_nostr_key_derivation_deterministic() {
1925        let temp_dir = std::env::temp_dir();
1926        let db_path = temp_dir.join("test_nostr_derivation.db");
1927        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
1928
1929        let nostr_keypair1 = bridge.derive_nostr_keypair().await.unwrap();
1930        let nostr_keypair2 = bridge.derive_nostr_keypair().await.unwrap();
1931
1932        assert_eq!(nostr_keypair1.secret_key(), nostr_keypair2.secret_key());
1933        assert_eq!(nostr_keypair1.public_key(), nostr_keypair2.public_key());
1934
1935        let _ = std::fs::remove_file(&db_path);
1936    }
1937
1938    #[tokio::test]
1939    async fn test_peer_nostr_key_derivation() {
1940        let temp_dir = std::env::temp_dir();
1941        let alice_db = temp_dir.join("test_alice_nostr.db");
1942        let bob_db = temp_dir.join("test_bob_nostr.db");
1943
1944        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
1945        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
1946
1947        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
1948        let bob_rdx = alice
1949            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
1950            .await
1951            .unwrap();
1952
1953        let bob_nostr_key = alice.derive_peer_nostr_key(&bob_rdx).await.unwrap();
1954
1955        let bob_actual_nostr = bob.derive_nostr_keypair().await.unwrap();
1956        assert_eq!(
1957            bob_nostr_key,
1958            bob_actual_nostr.public_key().to_bytes().to_vec()
1959        );
1960
1961        let _ = std::fs::remove_file(&alice_db);
1962        let _ = std::fs::remove_file(&bob_db);
1963    }
1964
1965    #[test]
1966    fn test_error_types_creation() {
1967        let storage_error = SignalBridgeError::Storage("Database connection failed".to_string());
1968        assert_eq!(
1969            storage_error.to_string(),
1970            "Storage error: Database connection failed"
1971        );
1972
1973        let protocol_error = SignalBridgeError::Protocol("Invalid device ID".to_string());
1974        assert_eq!(
1975            protocol_error.to_string(),
1976            "Signal Protocol error: Invalid device ID"
1977        );
1978
1979        let serialization_error =
1980            SignalBridgeError::Serialization("Invalid data format".to_string());
1981        assert_eq!(
1982            serialization_error.to_string(),
1983            "Serialization error: Invalid data format"
1984        );
1985
1986        let invalid_input_error = SignalBridgeError::InvalidInput("Empty peer name".to_string());
1987        assert_eq!(
1988            invalid_input_error.to_string(),
1989            "Invalid input: Empty peer name"
1990        );
1991
1992        let session_not_found_error = SignalBridgeError::SessionNotFound(
1993            "Establish a session with alice before sending messages".to_string(),
1994        );
1995        assert_eq!(
1996            session_not_found_error.to_string(),
1997            "Establish a session with alice before sending messages"
1998        );
1999
2000        let schema_error = SignalBridgeError::SchemaVersionTooOld;
2001        assert_eq!(
2002            schema_error.to_string(),
2003            "Update your database to a newer schema version"
2004        );
2005    }
2006
2007    #[test]
2008    fn test_error_from_conversions() {
2009        let device_result = DeviceId::new(0);
2010        if let Err(protocol_err) = device_result {
2011            let bridge_error = SignalBridgeError::Protocol(protocol_err.to_string());
2012            assert!(matches!(bridge_error, SignalBridgeError::Protocol(_)));
2013        }
2014
2015        let invalid_data = vec![0xFF, 0xFF, 0xFF];
2016        let bincode_result: Result<String, bincode::Error> = bincode::deserialize(&invalid_data);
2017        if let Err(bincode_err) = bincode_result {
2018            let bridge_error: SignalBridgeError = bincode_err.into();
2019            assert!(matches!(bridge_error, SignalBridgeError::Serialization(_)));
2020        }
2021
2022        use std::time::{Duration, SystemTime};
2023        let bad_time = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
2024        let time_result = bad_time.duration_since(SystemTime::UNIX_EPOCH);
2025        if let Err(time_err) = time_result {
2026            let bridge_error: SignalBridgeError = time_err.into();
2027            assert!(matches!(bridge_error, SignalBridgeError::Protocol(_)));
2028        }
2029    }
2030
2031    #[tokio::test]
2032    async fn test_crypto_handler_exists() -> Result<(), Box<dyn std::error::Error>> {
2033        use crate::SignalBridge;
2034        use std::env;
2035        use std::fs;
2036
2037        let temp_dir = env::temp_dir();
2038        let db_path = temp_dir.join("test_crypto_handler.db");
2039        let db_path_str = db_path.to_str().unwrap();
2040
2041        let _ = fs::remove_file(&db_path);
2042        let key_path = format!("{}.key", db_path_str);
2043        let _ = fs::remove_file(&key_path);
2044
2045        let _handler = SignalBridge::new(db_path_str).await?;
2046        Ok(())
2047    }
2048
2049    #[tokio::test]
2050    async fn test_crypto_handler_alice_bob_integration() -> Result<(), Box<dyn std::error::Error>> {
2051        use crate::SignalBridge;
2052        use std::env;
2053
2054        let temp_dir = env::temp_dir();
2055        let process_id = std::process::id();
2056        let timestamp = std::time::SystemTime::now()
2057            .duration_since(std::time::UNIX_EPOCH)
2058            .unwrap()
2059            .as_millis();
2060
2061        let alice_db_path = temp_dir.join(format!("test_alice_{}_{}.db", process_id, timestamp));
2062        let alice_db_str = alice_db_path.to_str().unwrap();
2063        let mut alice = SignalBridge::new(alice_db_str).await?;
2064
2065        let bob_db_path = temp_dir.join(format!("test_bob_{}_{}.db", process_id, timestamp));
2066        let bob_db_str = bob_db_path.to_str().unwrap();
2067        let mut bob = SignalBridge::new(bob_db_str).await?;
2068
2069        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2070        let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2071
2072        let bob_rdx = alice
2073            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2074            .await?;
2075        let alice_rdx = bob
2076            .add_contact_and_establish_session(&alice_bundle, Some("alice"))
2077            .await?;
2078
2079        let plaintext = b"Hello Bob! This is Alice using SignalBridge.";
2080        let ciphertext = alice.encrypt_message(&bob_rdx, plaintext).await?;
2081        let result = bob.decrypt_message(&alice_rdx, &ciphertext).await?;
2082
2083        assert_eq!(plaintext.as_slice(), result.plaintext.as_slice());
2084
2085        Ok(())
2086    }
2087
2088    #[tokio::test]
2089    async fn test_signalbridge_persistence_across_instantiations(
2090    ) -> Result<(), Box<dyn std::error::Error>> {
2091        use crate::SignalBridge;
2092        use std::env;
2093
2094        let temp_dir = env::temp_dir();
2095        let process_id = std::process::id();
2096        let timestamp = std::time::SystemTime::now()
2097            .duration_since(std::time::UNIX_EPOCH)
2098            .unwrap()
2099            .as_millis();
2100
2101        let alice_db_path = temp_dir.join(format!(
2102            "test_persistence_alice_{}_{}.db",
2103            process_id, timestamp
2104        ));
2105        let alice_db_str = alice_db_path.to_str().unwrap();
2106        let bob_db_path = temp_dir.join(format!(
2107            "test_persistence_bob_{}_{}.db",
2108            process_id, timestamp
2109        ));
2110        let bob_db_str = bob_db_path.to_str().unwrap();
2111
2112        let original_plaintext = b"Hello Bob! This is Alice testing persistence.";
2113        let (ciphertext, bob_rdx, alice_rdx) = {
2114            let mut alice = SignalBridge::new(alice_db_str).await?;
2115            let mut bob = SignalBridge::new(bob_db_str).await?;
2116
2117            let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2118            let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2119
2120            let bob_rdx = alice
2121                .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2122                .await?;
2123            let alice_rdx = bob
2124                .add_contact_and_establish_session(&alice_bundle, Some("alice"))
2125                .await?;
2126
2127            let ciphertext = alice.encrypt_message(&bob_rdx, original_plaintext).await?;
2128            (ciphertext, bob_rdx, alice_rdx)
2129        };
2130
2131        {
2132            let mut alice_reopened = SignalBridge::new(alice_db_str).await?;
2133
2134            let second_message = b"Second message after restart";
2135            let second_ciphertext = alice_reopened
2136                .encrypt_message(&bob_rdx, second_message)
2137                .await?;
2138
2139            assert!(!second_ciphertext.is_empty());
2140            assert_ne!(ciphertext, second_ciphertext);
2141        }
2142
2143        {
2144            let mut bob_reopened = SignalBridge::new(bob_db_str).await?;
2145
2146            let result1 = bob_reopened
2147                .decrypt_message(&alice_rdx, &ciphertext)
2148                .await?;
2149            assert_eq!(result1.plaintext, original_plaintext);
2150        }
2151
2152        Ok(())
2153    }
2154
2155    #[tokio::test]
2156    async fn test_clear_peer_session() -> Result<(), Box<dyn std::error::Error>> {
2157        use crate::SignalBridge;
2158        use std::env;
2159
2160        let temp_dir = env::temp_dir();
2161        let process_id = std::process::id();
2162        let timestamp = std::time::SystemTime::now()
2163            .duration_since(std::time::UNIX_EPOCH)
2164            .unwrap()
2165            .as_millis();
2166
2167        let alice_db_path = temp_dir.join(format!(
2168            "test_clear_peer_alice_{}_{}.db",
2169            process_id, timestamp
2170        ));
2171        let alice_db_str = alice_db_path.to_str().unwrap();
2172        let bob_db_path = temp_dir.join(format!(
2173            "test_clear_peer_bob_{}_{}.db",
2174            process_id, timestamp
2175        ));
2176        let bob_db_str = bob_db_path.to_str().unwrap();
2177
2178        let mut alice = SignalBridge::new(alice_db_str).await?;
2179        let mut bob = SignalBridge::new(bob_db_str).await?;
2180
2181        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2182        let bob_rdx = alice
2183            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2184            .await?;
2185
2186        let message = b"Test message";
2187        let _ciphertext = alice.encrypt_message(&bob_rdx, message).await?;
2188
2189        alice.clear_peer_session(&bob_rdx).await?;
2190
2191        let result = alice.encrypt_message(&bob_rdx, message).await;
2192        assert!(
2193            result.is_err(),
2194            "Encryption should fail after clearing peer session"
2195        );
2196
2197        Ok(())
2198    }
2199
2200    #[tokio::test]
2201    async fn test_clear_all_sessions() -> Result<(), Box<dyn std::error::Error>> {
2202        use crate::SignalBridge;
2203        use std::env;
2204
2205        let temp_dir = env::temp_dir();
2206        let process_id = std::process::id();
2207        let timestamp = std::time::SystemTime::now()
2208            .duration_since(std::time::UNIX_EPOCH)
2209            .unwrap()
2210            .as_millis();
2211
2212        let alice_db_path = temp_dir.join(format!(
2213            "test_clear_all_alice_{}_{}.db",
2214            process_id, timestamp
2215        ));
2216        let alice_db_str = alice_db_path.to_str().unwrap();
2217        let bob_db_path = temp_dir.join(format!(
2218            "test_clear_all_bob_{}_{}.db",
2219            process_id, timestamp
2220        ));
2221        let bob_db_str = bob_db_path.to_str().unwrap();
2222        let charlie_db_path = temp_dir.join(format!(
2223            "test_clear_all_charlie_{}_{}.db",
2224            process_id, timestamp
2225        ));
2226        let charlie_db_str = charlie_db_path.to_str().unwrap();
2227
2228        let mut alice = SignalBridge::new(alice_db_str).await?;
2229        let mut bob = SignalBridge::new(bob_db_str).await?;
2230        let mut charlie = SignalBridge::new(charlie_db_str).await?;
2231
2232        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2233        let (charlie_bundle, _, _, _) = charlie.generate_pre_key_bundle().await?;
2234
2235        let bob_rdx = alice
2236            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2237            .await?;
2238        let charlie_rdx = alice
2239            .add_contact_and_establish_session(&charlie_bundle, Some("charlie"))
2240            .await?;
2241
2242        let message = b"Test message";
2243        let _ciphertext1 = alice.encrypt_message(&bob_rdx, message).await?;
2244        let _ciphertext2 = alice.encrypt_message(&charlie_rdx, message).await?;
2245
2246        alice.clear_all_sessions().await?;
2247
2248        let result1 = alice.encrypt_message(&bob_rdx, message).await;
2249        let result2 = alice.encrypt_message(&charlie_rdx, message).await;
2250        assert!(
2251            result1.is_err(),
2252            "Encryption to Bob should fail after clearing all sessions"
2253        );
2254        assert!(
2255            result2.is_err(),
2256            "Encryption to Charlie should fail after clearing all sessions"
2257        );
2258
2259        Ok(())
2260    }
2261
2262    #[tokio::test]
2263    async fn test_reset_identity() -> Result<(), Box<dyn std::error::Error>> {
2264        use crate::SignalBridge;
2265        use std::env;
2266
2267        let temp_dir = env::temp_dir();
2268        let process_id = std::process::id();
2269        let timestamp = std::time::SystemTime::now()
2270            .duration_since(std::time::UNIX_EPOCH)
2271            .unwrap()
2272            .as_millis();
2273
2274        let alice_db_path = temp_dir.join(format!(
2275            "test_reset_identity_alice_{}_{}.db",
2276            process_id, timestamp
2277        ));
2278        let alice_db_str = alice_db_path.to_str().unwrap();
2279        let bob_db_path = temp_dir.join(format!(
2280            "test_reset_identity_bob_{}_{}.db",
2281            process_id, timestamp
2282        ));
2283        let bob_db_str = bob_db_path.to_str().unwrap();
2284
2285        let (original_alice_bundle, bob_rdx) = {
2286            let mut alice = SignalBridge::new(alice_db_str).await?;
2287            let mut bob = SignalBridge::new(bob_db_str).await?;
2288
2289            let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await?;
2290            let bob_rdx = alice
2291                .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2292                .await?;
2293
2294            let (alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2295            (alice_bundle, bob_rdx)
2296        };
2297
2298        {
2299            let mut alice = SignalBridge::new(alice_db_str).await?;
2300            alice.reset_identity().await?;
2301
2302            let (new_alice_bundle, _, _, _) = alice.generate_pre_key_bundle().await?;
2303            assert_ne!(
2304                original_alice_bundle, new_alice_bundle,
2305                "Alice's identity should be different after reset"
2306            );
2307
2308            let message = b"Test message";
2309            let result = alice.encrypt_message(&bob_rdx, message).await;
2310            assert!(
2311                result.is_err(),
2312                "Encryption should fail after identity reset"
2313            );
2314        }
2315
2316        Ok(())
2317    }
2318
2319    #[tokio::test]
2320    async fn test_encrypt_message_empty_peer_name() {
2321        use std::env;
2322
2323        let temp_dir = env::temp_dir();
2324        let process_id = std::process::id();
2325        let timestamp = std::time::SystemTime::now()
2326            .duration_since(std::time::UNIX_EPOCH)
2327            .unwrap()
2328            .as_millis();
2329
2330        let db_path = temp_dir.join(format!("test_empty_peer_{}_{}.db", process_id, timestamp));
2331        let db_path_str = db_path.to_str().unwrap();
2332        let mut bridge = SignalBridge::new(db_path_str)
2333            .await
2334            .expect("Failed to create bridge");
2335
2336        let result = bridge.encrypt_message("", b"test message").await;
2337        assert!(result.is_err());
2338        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2339            assert_eq!(msg, "Specify a peer name");
2340        } else {
2341            panic!("Expected InvalidInput error for empty peer name");
2342        }
2343    }
2344
2345    #[tokio::test]
2346    async fn test_decrypt_message_empty_ciphertext() {
2347        use std::env;
2348
2349        let temp_dir = env::temp_dir();
2350        let process_id = std::process::id();
2351        let timestamp = std::time::SystemTime::now()
2352            .duration_since(std::time::UNIX_EPOCH)
2353            .unwrap()
2354            .as_millis();
2355
2356        let db_path = temp_dir.join(format!(
2357            "test_empty_ciphertext_{}_{}.db",
2358            process_id, timestamp
2359        ));
2360        let db_path_str = db_path.to_str().unwrap();
2361        let mut bridge = SignalBridge::new(db_path_str)
2362            .await
2363            .expect("Failed to create bridge");
2364
2365        let result = bridge.decrypt_message("alice", &[]).await;
2366        assert!(result.is_err());
2367        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2368            assert_eq!(msg, "Provide a message to decrypt");
2369        } else {
2370            panic!("Expected InvalidInput error for empty ciphertext");
2371        }
2372    }
2373
2374    #[tokio::test]
2375    async fn test_establish_session_empty_peer_name() {
2376        use std::env;
2377
2378        let temp_dir = env::temp_dir();
2379        let process_id = std::process::id();
2380        let timestamp = std::time::SystemTime::now()
2381            .duration_since(std::time::UNIX_EPOCH)
2382            .unwrap()
2383            .as_millis();
2384
2385        let db_path = temp_dir.join(format!(
2386            "test_empty_peer_session_{}_{}.db",
2387            process_id, timestamp
2388        ));
2389        let db_path_str = db_path.to_str().unwrap();
2390        let mut bridge = SignalBridge::new(db_path_str)
2391            .await
2392            .expect("Failed to create bridge");
2393
2394        let result = bridge.establish_session("", b"fake_bundle").await;
2395        assert!(result.is_err());
2396        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2397            assert_eq!(msg, "Specify a peer name");
2398        } else {
2399            panic!("Expected InvalidInput error for empty peer name");
2400        }
2401    }
2402
2403    #[tokio::test]
2404    async fn test_establish_session_empty_bundle() {
2405        use std::env;
2406
2407        let temp_dir = env::temp_dir();
2408        let process_id = std::process::id();
2409        let timestamp = std::time::SystemTime::now()
2410            .duration_since(std::time::UNIX_EPOCH)
2411            .unwrap()
2412            .as_millis();
2413
2414        let db_path = temp_dir.join(format!("test_empty_bundle_{}_{}.db", process_id, timestamp));
2415        let db_path_str = db_path.to_str().unwrap();
2416        let mut bridge = SignalBridge::new(db_path_str)
2417            .await
2418            .expect("Failed to create bridge");
2419
2420        let result = bridge.establish_session("alice", &[]).await;
2421        assert!(result.is_err());
2422        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2423            assert_eq!(msg, "Provide a pre-key bundle from the peer");
2424        } else {
2425            panic!("Expected InvalidInput error for empty pre-key bundle");
2426        }
2427    }
2428
2429    #[tokio::test]
2430    async fn test_clear_peer_session_empty_peer_name() {
2431        use std::env;
2432
2433        let temp_dir = env::temp_dir();
2434        let process_id = std::process::id();
2435        let timestamp = std::time::SystemTime::now()
2436            .duration_since(std::time::UNIX_EPOCH)
2437            .unwrap()
2438            .as_millis();
2439
2440        let db_path = temp_dir.join(format!(
2441            "test_clear_empty_peer_{}_{}.db",
2442            process_id, timestamp
2443        ));
2444        let db_path_str = db_path.to_str().unwrap();
2445        let mut bridge = SignalBridge::new(db_path_str)
2446            .await
2447            .expect("Failed to create bridge");
2448
2449        let result = bridge.clear_peer_session("").await;
2450        assert!(result.is_err());
2451        if let Err(SignalBridgeError::InvalidInput(msg)) = result {
2452            assert_eq!(msg, "Specify a peer name");
2453        } else {
2454            panic!("Expected InvalidInput error for empty peer name");
2455        }
2456    }
2457
2458    #[tokio::test]
2459    async fn test_encrypt_message_session_not_found() {
2460        use std::env;
2461
2462        let temp_dir = env::temp_dir();
2463        let process_id = std::process::id();
2464        let timestamp = std::time::SystemTime::now()
2465            .duration_since(std::time::UNIX_EPOCH)
2466            .unwrap()
2467            .as_millis();
2468
2469        let db_path = temp_dir.join(format!(
2470            "test_session_not_found_{}_{}.db",
2471            process_id, timestamp
2472        ));
2473        let db_path_str = db_path.to_str().unwrap();
2474        let mut bridge = SignalBridge::new(db_path_str)
2475            .await
2476            .expect("Failed to create bridge");
2477
2478        let result = bridge
2479            .encrypt_message("unknown_peer", b"test message")
2480            .await;
2481        assert!(result.is_err());
2482        if let Err(SignalBridgeError::SessionNotFound(msg)) = result {
2483            assert_eq!(
2484                msg,
2485                "Establish a session with unknown_peer before sending messages"
2486            );
2487        } else {
2488            panic!("Expected SessionNotFound error for unknown peer");
2489        }
2490    }
2491
2492    #[tokio::test]
2493    async fn test_decrypt_message_malformed_ciphertext() {
2494        use std::env;
2495
2496        let temp_dir = env::temp_dir();
2497        let process_id = std::process::id();
2498        let timestamp = std::time::SystemTime::now()
2499            .duration_since(std::time::UNIX_EPOCH)
2500            .unwrap()
2501            .as_millis();
2502
2503        let db_path = temp_dir.join(format!(
2504            "test_malformed_ciphertext_{}_{}.db",
2505            process_id, timestamp
2506        ));
2507        let db_path_str = db_path.to_str().unwrap();
2508        let mut bridge = SignalBridge::new(db_path_str)
2509            .await
2510            .expect("Failed to create bridge");
2511
2512        let malformed_data = vec![0x01, 0x02, 0x03, 0x04];
2513        let result = bridge.decrypt_message("alice", &malformed_data).await;
2514        assert!(result.is_err());
2515        if let Err(SignalBridgeError::Serialization(msg)) = result {
2516            assert_eq!(msg, "Provide a valid Signal Protocol message");
2517        } else {
2518            panic!("Expected Serialization error for malformed ciphertext");
2519        }
2520    }
2521
2522    #[tokio::test]
2523    async fn test_establish_session_malformed_bundle() {
2524        use std::env;
2525
2526        let temp_dir = env::temp_dir();
2527        let process_id = std::process::id();
2528        let timestamp = std::time::SystemTime::now()
2529            .duration_since(std::time::UNIX_EPOCH)
2530            .unwrap()
2531            .as_millis();
2532
2533        let db_path = temp_dir.join(format!(
2534            "test_malformed_bundle_{}_{}.db",
2535            process_id, timestamp
2536        ));
2537        let db_path_str = db_path.to_str().unwrap();
2538        let mut bridge = SignalBridge::new(db_path_str)
2539            .await
2540            .expect("Failed to create bridge");
2541
2542        let malformed_bundle = vec![0xFF, 0xFE, 0xFD, 0xFC];
2543        let result = bridge.establish_session("alice", &malformed_bundle).await;
2544        assert!(result.is_err());
2545        if let Err(SignalBridgeError::Serialization(msg)) = result {
2546            assert_eq!(msg, "io error: unexpected end of file");
2547        } else {
2548            panic!("Expected Serialization error for malformed bundle");
2549        }
2550    }
2551
2552    #[tokio::test]
2553    async fn test_sign_nostr_event() -> Result<(), Box<dyn std::error::Error>> {
2554        use std::env;
2555
2556        let temp_dir = env::temp_dir();
2557        let process_id = std::process::id();
2558        let timestamp = std::time::SystemTime::now()
2559            .duration_since(std::time::UNIX_EPOCH)
2560            .unwrap()
2561            .as_millis();
2562
2563        let db_path = temp_dir.join(format!("test_sign_nostr_{}_{}.db", process_id, timestamp));
2564        let db_path_str = db_path.to_str().unwrap();
2565        let mut bridge = SignalBridge::new(db_path_str).await?;
2566
2567        let _event_json = r#"{
2568            "id": "",
2569            "pubkey": "",
2570            "created_at": 1234567890,
2571            "kind": 40001,
2572            "tags": [
2573                ["p", "recipient_pubkey"]
2574            ],
2575            "content": "encrypted_content_here",
2576            "sig": ""
2577        }"#;
2578
2579        let result = bridge
2580            .sign_nostr_event(
2581                1234567890,
2582                40001,
2583                vec![vec!["p".to_string(), "recipient_pubkey".to_string()]],
2584                "encrypted_content_here",
2585            )
2586            .await;
2587        assert!(result.is_ok());
2588
2589        let (pubkey, event_id, signature) = result.unwrap();
2590        assert!(!pubkey.is_empty());
2591        assert!(!event_id.is_empty());
2592        assert!(!signature.is_empty());
2593        assert_eq!(event_id.len(), 64); // SHA256 hex should be 64 chars
2594        assert_eq!(pubkey.len(), 64); // Nostr pubkey hex should be 64 chars
2595
2596        let keys = bridge.derive_nostr_keypair().await?;
2597        assert_eq!(pubkey, keys.public_key().to_hex());
2598
2599        Ok(())
2600    }
2601
2602    #[tokio::test]
2603    async fn test_error_message_formatting() {
2604        let storage_error = SignalBridgeError::Storage("Database locked".to_string());
2605        assert_eq!(storage_error.to_string(), "Storage error: Database locked");
2606
2607        let protocol_error = SignalBridgeError::Protocol("Invalid signature".to_string());
2608        assert_eq!(
2609            protocol_error.to_string(),
2610            "Signal Protocol error: Invalid signature"
2611        );
2612
2613        let serialization_error = SignalBridgeError::Serialization("Invalid format".to_string());
2614        assert_eq!(
2615            serialization_error.to_string(),
2616            "Serialization error: Invalid format"
2617        );
2618
2619        let invalid_input_error = SignalBridgeError::InvalidInput("Name too long".to_string());
2620        assert_eq!(
2621            invalid_input_error.to_string(),
2622            "Invalid input: Name too long"
2623        );
2624
2625        let session_not_found_error = SignalBridgeError::SessionNotFound(
2626            "Establish a session with bob before sending messages".to_string(),
2627        );
2628        assert_eq!(
2629            session_not_found_error.to_string(),
2630            "Establish a session with bob before sending messages"
2631        );
2632
2633        let schema_error = SignalBridgeError::SchemaVersionTooOld;
2634        assert_eq!(
2635            schema_error.to_string(),
2636            "Update your database to a newer schema version"
2637        );
2638    }
2639
2640    #[tokio::test]
2641    async fn test_add_contact_and_establish_session_returns_rdx() {
2642        let temp_dir = std::env::temp_dir();
2643        let timestamp = SystemTime::now()
2644            .duration_since(UNIX_EPOCH)
2645            .unwrap()
2646            .as_millis();
2647
2648        let alice_db = temp_dir.join(format!("test_contact_alice_{}.db", timestamp));
2649        let bob_db = temp_dir.join(format!("test_contact_bob_{}.db", timestamp));
2650
2651        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2652        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2653
2654        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2655
2656        let bob_rdx = alice
2657            .add_contact_and_establish_session(&bob_bundle, None)
2658            .await
2659            .unwrap();
2660        assert!(bob_rdx.starts_with("RDX:"));
2661
2662        let _ = std::fs::remove_file(&alice_db);
2663        let _ = std::fs::remove_file(&bob_db);
2664    }
2665
2666    #[tokio::test]
2667    async fn test_lookup_contact_by_rdx() {
2668        let temp_dir = std::env::temp_dir();
2669        let timestamp = SystemTime::now()
2670            .duration_since(UNIX_EPOCH)
2671            .unwrap()
2672            .as_millis();
2673
2674        let alice_db = temp_dir.join(format!("test_lookup_alice_{}.db", timestamp));
2675        let bob_db = temp_dir.join(format!("test_lookup_bob_{}.db", timestamp));
2676
2677        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2678        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2679
2680        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2681        let bob_rdx = alice
2682            .add_contact_and_establish_session(&bob_bundle, None)
2683            .await
2684            .unwrap();
2685
2686        let contact = alice.lookup_contact(&bob_rdx).await.unwrap();
2687        assert_eq!(contact.rdx_fingerprint, bob_rdx);
2688        assert!(contact.user_alias.is_none());
2689        assert!(contact.has_active_session);
2690
2691        let _ = std::fs::remove_file(&alice_db);
2692        let _ = std::fs::remove_file(&bob_db);
2693    }
2694
2695    #[tokio::test]
2696    async fn test_assign_and_lookup_contact_by_alias() {
2697        let temp_dir = std::env::temp_dir();
2698        let timestamp = SystemTime::now()
2699            .duration_since(UNIX_EPOCH)
2700            .unwrap()
2701            .as_millis();
2702
2703        let alice_db = temp_dir.join(format!("test_alias_alice_{}.db", timestamp));
2704        let bob_db = temp_dir.join(format!("test_alias_bob_{}.db", timestamp));
2705
2706        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2707        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2708
2709        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2710        let bob_rdx = alice
2711            .add_contact_and_establish_session(&bob_bundle, None)
2712            .await
2713            .unwrap();
2714
2715        let contact_before = alice.lookup_contact(&bob_rdx).await.unwrap();
2716        assert!(contact_before.user_alias.is_none());
2717
2718        alice.assign_contact_alias(&bob_rdx, "bob").await.unwrap();
2719
2720        let contact_by_alias = alice.lookup_contact("bob").await.unwrap();
2721        assert_eq!(contact_by_alias.rdx_fingerprint, bob_rdx);
2722        assert_eq!(contact_by_alias.user_alias, Some("bob".to_string()));
2723
2724        let _ = std::fs::remove_file(&alice_db);
2725        let _ = std::fs::remove_file(&bob_db);
2726    }
2727
2728    #[tokio::test]
2729    async fn test_list_contacts() {
2730        let temp_dir = std::env::temp_dir();
2731        let timestamp = SystemTime::now()
2732            .duration_since(UNIX_EPOCH)
2733            .unwrap()
2734            .as_millis();
2735
2736        let alice_db = temp_dir.join(format!("test_list_alice_{}.db", timestamp));
2737        let bob_db = temp_dir.join(format!("test_list_bob_{}.db", timestamp));
2738        let charlie_db = temp_dir.join(format!("test_list_charlie_{}.db", timestamp));
2739
2740        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2741        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2742        let mut charlie = SignalBridge::new(charlie_db.to_str().unwrap())
2743            .await
2744            .unwrap();
2745
2746        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2747        let (charlie_bundle, _, _, _) = charlie.generate_pre_key_bundle().await.unwrap();
2748
2749        let bob_rdx = alice
2750            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2751            .await
2752            .unwrap();
2753        let charlie_rdx = alice
2754            .add_contact_and_establish_session(&charlie_bundle, None)
2755            .await
2756            .unwrap();
2757
2758        let contacts = alice.list_contacts().await.unwrap();
2759        assert_eq!(contacts.len(), 2);
2760
2761        let bob_contact = contacts
2762            .iter()
2763            .find(|c| c.rdx_fingerprint == bob_rdx)
2764            .unwrap();
2765        assert_eq!(bob_contact.user_alias, Some("bob".to_string()));
2766
2767        let charlie_contact = contacts
2768            .iter()
2769            .find(|c| c.rdx_fingerprint == charlie_rdx)
2770            .unwrap();
2771        assert!(charlie_contact.user_alias.is_none());
2772
2773        let _ = std::fs::remove_file(&alice_db);
2774        let _ = std::fs::remove_file(&bob_db);
2775        let _ = std::fs::remove_file(&charlie_db);
2776    }
2777
2778    #[tokio::test]
2779    async fn test_alias_with_multiple_sessions() {
2780        let temp_dir = std::env::temp_dir();
2781        let timestamp = SystemTime::now()
2782            .duration_since(UNIX_EPOCH)
2783            .unwrap()
2784            .as_millis();
2785
2786        let alice_db = temp_dir.join(format!("test_multi_alias_alice_{}.db", timestamp));
2787        let bob1_db = temp_dir.join(format!("test_multi_alias_bob1_{}.db", timestamp));
2788        let bob2_db = temp_dir.join(format!("test_multi_alias_bob2_{}.db", timestamp));
2789        let bob3_db = temp_dir.join(format!("test_multi_alias_bob3_{}.db", timestamp));
2790
2791        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2792        let mut bob1 = SignalBridge::new(bob1_db.to_str().unwrap()).await.unwrap();
2793        let mut bob2 = SignalBridge::new(bob2_db.to_str().unwrap()).await.unwrap();
2794        let mut bob3 = SignalBridge::new(bob3_db.to_str().unwrap()).await.unwrap();
2795
2796        let (bob1_bundle, _, _, _) = bob1.generate_pre_key_bundle().await.unwrap();
2797        let (bob2_bundle, _, _, _) = bob2.generate_pre_key_bundle().await.unwrap();
2798        let (bob3_bundle, _, _, _) = bob3.generate_pre_key_bundle().await.unwrap();
2799
2800        let bob1_rdx = alice
2801            .add_contact_and_establish_session(&bob1_bundle, None)
2802            .await
2803            .unwrap();
2804        let bob2_rdx = alice
2805            .add_contact_and_establish_session(&bob2_bundle, None)
2806            .await
2807            .unwrap();
2808        let bob3_rdx = alice
2809            .add_contact_and_establish_session(&bob3_bundle, None)
2810            .await
2811            .unwrap();
2812
2813        assert_ne!(bob1_rdx, bob2_rdx);
2814        assert_ne!(bob2_rdx, bob3_rdx);
2815        assert_ne!(bob1_rdx, bob3_rdx);
2816
2817        alice.assign_contact_alias(&bob3_rdx, "bob").await.unwrap();
2818
2819        let contact_by_alias = alice.lookup_contact("bob").await.unwrap();
2820        assert_eq!(contact_by_alias.rdx_fingerprint, bob3_rdx);
2821        assert_eq!(contact_by_alias.user_alias, Some("bob".to_string()));
2822
2823        let contact1 = alice.lookup_contact(&bob1_rdx).await.unwrap();
2824        assert_eq!(contact1.rdx_fingerprint, bob1_rdx);
2825        assert!(contact1.user_alias.is_none());
2826
2827        let contact2 = alice.lookup_contact(&bob2_rdx).await.unwrap();
2828        assert_eq!(contact2.rdx_fingerprint, bob2_rdx);
2829        assert!(contact2.user_alias.is_none());
2830
2831        let _ = std::fs::remove_file(&alice_db);
2832        let _ = std::fs::remove_file(&bob1_db);
2833        let _ = std::fs::remove_file(&bob2_db);
2834        let _ = std::fs::remove_file(&bob3_db);
2835    }
2836
2837    #[tokio::test]
2838    async fn test_reassigning_same_alias_to_different_contact() {
2839        let temp_dir = std::env::temp_dir();
2840        let timestamp = SystemTime::now()
2841            .duration_since(UNIX_EPOCH)
2842            .unwrap()
2843            .as_millis();
2844
2845        let alice_db = temp_dir.join(format!("test_reassign_alias_alice_{}.db", timestamp));
2846        let bob1_db = temp_dir.join(format!("test_reassign_alias_bob1_{}.db", timestamp));
2847        let bob2_db = temp_dir.join(format!("test_reassign_alias_bob2_{}.db", timestamp));
2848
2849        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2850        let mut bob1 = SignalBridge::new(bob1_db.to_str().unwrap()).await.unwrap();
2851        let mut bob2 = SignalBridge::new(bob2_db.to_str().unwrap()).await.unwrap();
2852
2853        let (bob1_bundle, _, _, _) = bob1.generate_pre_key_bundle().await.unwrap();
2854        let (bob2_bundle, _, _, _) = bob2.generate_pre_key_bundle().await.unwrap();
2855
2856        let bob1_rdx = alice
2857            .add_contact_and_establish_session(&bob1_bundle, None)
2858            .await
2859            .unwrap();
2860        let bob2_rdx = alice
2861            .add_contact_and_establish_session(&bob2_bundle, None)
2862            .await
2863            .unwrap();
2864
2865        alice.assign_contact_alias(&bob1_rdx, "bob").await.unwrap();
2866
2867        let contact = alice.lookup_contact("bob").await.unwrap();
2868        assert_eq!(contact.rdx_fingerprint, bob1_rdx);
2869
2870        let result = alice.assign_contact_alias(&bob2_rdx, "bob").await;
2871        assert!(result.is_err(), "Should fail when alias is already taken");
2872        assert!(
2873            result.unwrap_err().to_string().contains("already assigned"),
2874            "Error message should indicate alias is already assigned"
2875        );
2876
2877        let contact_still = alice.lookup_contact("bob").await.unwrap();
2878        assert_eq!(
2879            contact_still.rdx_fingerprint, bob1_rdx,
2880            "Alias 'bob' should still point to bob1"
2881        );
2882
2883        let _ = std::fs::remove_file(&alice_db);
2884        let _ = std::fs::remove_file(&bob1_db);
2885        let _ = std::fs::remove_file(&bob2_db);
2886    }
2887
2888    #[tokio::test]
2889    async fn test_generate_bundle_announcement_includes_rdx_tag() {
2890        let temp_dir = std::env::temp_dir();
2891        let timestamp = SystemTime::now()
2892            .duration_since(UNIX_EPOCH)
2893            .unwrap()
2894            .as_millis();
2895        let db_path = temp_dir.join(format!("test_generate_announcement_{}.db", timestamp));
2896
2897        let mut alice = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
2898
2899        let bundle_info = alice
2900            .generate_prekey_bundle_announcement("1.0.0-test")
2901            .await
2902            .unwrap();
2903        let event: serde_json::Value =
2904            serde_json::from_str(&bundle_info.announcement_json).unwrap();
2905
2906        assert_eq!(event["kind"], 30078);
2907        assert!(event["content"].is_string());
2908
2909        let tags = event["tags"].as_array().unwrap();
2910        let rdx_tag = tags
2911            .iter()
2912            .find(|t| t[0] == "rdx")
2913            .expect("RDX tag should be present");
2914        let rdx_value = rdx_tag[1].as_str().unwrap();
2915        assert!(rdx_value.starts_with("RDX:"));
2916
2917        let version_tag = tags
2918            .iter()
2919            .find(|t| t[0] == "radix_version")
2920            .expect("Version tag should be present");
2921        assert_eq!(version_tag[1], "1.0.0-test");
2922
2923        let _ = std::fs::remove_file(&db_path);
2924    }
2925
2926    #[tokio::test]
2927    async fn test_add_contact_and_establish_session() {
2928        let temp_dir = std::env::temp_dir();
2929        let timestamp = SystemTime::now()
2930            .duration_since(UNIX_EPOCH)
2931            .unwrap()
2932            .as_millis();
2933        let alice_db = temp_dir.join(format!("test_contact_session_alice_{}.db", timestamp));
2934        let bob_db = temp_dir.join(format!("test_contact_session_bob_{}.db", timestamp));
2935
2936        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2937        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2938
2939        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2940
2941        let bob_rdx = alice
2942            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2943            .await
2944            .unwrap();
2945
2946        assert!(bob_rdx.starts_with("RDX:"));
2947
2948        let contact = alice.lookup_contact(&bob_rdx).await.unwrap();
2949        assert_eq!(contact.rdx_fingerprint, bob_rdx);
2950        assert_eq!(contact.user_alias, Some("bob".to_string()));
2951        assert!(contact.has_active_session);
2952
2953        let plaintext = b"Hello Bob!";
2954        let ciphertext = alice.encrypt_message(&bob_rdx, plaintext).await.unwrap();
2955        assert!(!ciphertext.is_empty());
2956
2957        let _ = std::fs::remove_file(&alice_db);
2958        let _ = std::fs::remove_file(&bob_db);
2959    }
2960
2961    #[tokio::test]
2962    async fn test_extract_rdx_from_bundle_without_establishing_session() {
2963        let temp_dir = std::env::temp_dir();
2964        let timestamp = SystemTime::now()
2965            .duration_since(UNIX_EPOCH)
2966            .unwrap()
2967            .as_millis();
2968
2969        let alice_db = temp_dir.join(format!("test_extract_rdx_alice_{}.db", timestamp));
2970        let bob_db = temp_dir.join(format!("test_extract_rdx_bob_{}.db", timestamp));
2971
2972        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
2973        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
2974
2975        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
2976
2977        let extracted_rdx = extract_rdx_from_bundle(&mut alice, &bob_bundle).unwrap();
2978
2979        assert!(extracted_rdx.starts_with("RDX:"));
2980
2981        let result = alice.encrypt_message(&extracted_rdx, b"test").await;
2982        assert!(
2983            result.is_err(),
2984            "Should not be able to encrypt without establishing session"
2985        );
2986
2987        let bob_rdx = alice
2988            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
2989            .await
2990            .unwrap();
2991
2992        assert_eq!(
2993            extracted_rdx, bob_rdx,
2994            "RDX extracted before session should match RDX after session establishment"
2995        );
2996
2997        let _ = std::fs::remove_file(&alice_db);
2998        let _ = std::fs::remove_file(&bob_db);
2999    }
3000
3001    #[tokio::test]
3002    async fn test_extract_rdx_from_bundle_base64() {
3003        let temp_dir = std::env::temp_dir();
3004        let timestamp = SystemTime::now()
3005            .duration_since(UNIX_EPOCH)
3006            .unwrap()
3007            .as_millis();
3008
3009        let alice_db = temp_dir.join(format!("test_extract_rdx_base64_alice_{}.db", timestamp));
3010        let bob_db = temp_dir.join(format!("test_extract_rdx_base64_bob_{}.db", timestamp));
3011
3012        let mut alice = SignalBridge::new(alice_db.to_str().unwrap()).await.unwrap();
3013        let mut bob = SignalBridge::new(bob_db.to_str().unwrap()).await.unwrap();
3014
3015        let (bob_bundle, _, _, _) = bob.generate_pre_key_bundle().await.unwrap();
3016        let bob_bundle_base64 = base64::engine::general_purpose::STANDARD.encode(&bob_bundle);
3017
3018        let extracted_rdx = extract_rdx_from_bundle_base64(&mut alice, &bob_bundle_base64).unwrap();
3019
3020        assert!(extracted_rdx.starts_with("RDX:"));
3021
3022        let bob_rdx = alice
3023            .add_contact_and_establish_session(&bob_bundle, Some("bob"))
3024            .await
3025            .unwrap();
3026
3027        assert_eq!(
3028            extracted_rdx, bob_rdx,
3029            "RDX extracted from base64 should match RDX after session establishment"
3030        );
3031
3032        let _ = std::fs::remove_file(&alice_db);
3033        let _ = std::fs::remove_file(&bob_db);
3034    }
3035
3036    #[tokio::test]
3037    async fn test_generate_empty_bundle_announcement() {
3038        let temp_dir = std::env::temp_dir();
3039        let timestamp = SystemTime::now()
3040            .duration_since(UNIX_EPOCH)
3041            .unwrap()
3042            .as_millis();
3043
3044        let db_path = temp_dir.join(format!("test_empty_bundle_{}.db", timestamp));
3045        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await.unwrap();
3046
3047        let announcement_json = bridge
3048            .generate_empty_bundle_announcement("0.4.0")
3049            .await
3050            .unwrap();
3051
3052        let event: serde_json::Value = serde_json::from_str(&announcement_json).unwrap();
3053
3054        assert_eq!(event["kind"].as_u64().unwrap(), 30078);
3055        assert_eq!(event["content"].as_str().unwrap(), "");
3056
3057        let tags = event["tags"].as_array().unwrap();
3058        let d_tag = tags.iter().find(|t| t[0] == "d").unwrap();
3059        assert_eq!(d_tag[1].as_str().unwrap(), "radix_prekey_bundle_v1");
3060
3061        let version_tag = tags.iter().find(|t| t[0] == "radix_version").unwrap();
3062        assert_eq!(version_tag[1].as_str().unwrap(), "0.4.0");
3063
3064        assert!(
3065            !tags.iter().any(|t| t[0] == "rdx"),
3066            "Empty bundle should not contain rdx tag"
3067        );
3068
3069        let _ = std::fs::remove_file(&db_path);
3070    }
3071
3072    #[tokio::test]
3073    async fn test_perform_key_maintenance_replenishes_pre_keys(
3074    ) -> Result<(), Box<dyn std::error::Error>> {
3075        use crate::key_rotation::MIN_PRE_KEY_COUNT;
3076        use crate::SignalBridge;
3077
3078        let temp_dir = std::env::temp_dir();
3079        let timestamp = SystemTime::now()
3080            .duration_since(UNIX_EPOCH)
3081            .unwrap()
3082            .as_millis();
3083        let db_path = temp_dir.join(format!("test_maintenance_prekeys_{}.db", timestamp));
3084        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3085
3086        // Get initial pre-key count
3087        let initial_count = bridge.storage.pre_key_store().pre_key_count().await;
3088
3089        // Perform maintenance - should not change count if above threshold
3090        bridge.perform_key_maintenance().await?;
3091
3092        let after_maintenance_count = bridge.storage.pre_key_store().pre_key_count().await;
3093        assert!(
3094            after_maintenance_count >= initial_count,
3095            "Pre-key count should not decrease after maintenance"
3096        );
3097
3098        // Clear pre-keys to below threshold and verify replenishment
3099        bridge.storage.pre_key_store().clear_all_pre_keys().await?;
3100        let empty_count = bridge.storage.pre_key_store().pre_key_count().await;
3101        assert_eq!(empty_count, 0, "Pre-keys should be cleared");
3102
3103        // Perform maintenance - should replenish
3104        bridge.perform_key_maintenance().await?;
3105
3106        let replenished_count = bridge.storage.pre_key_store().pre_key_count().await;
3107        assert!(
3108            replenished_count >= MIN_PRE_KEY_COUNT,
3109            "Pre-keys should be replenished to at least MIN_PRE_KEY_COUNT ({}), got {}",
3110            MIN_PRE_KEY_COUNT,
3111            replenished_count
3112        );
3113
3114        let _ = std::fs::remove_file(&db_path);
3115        Ok(())
3116    }
3117
3118    #[tokio::test]
3119    async fn test_perform_key_maintenance_no_rotation_for_fresh_keys(
3120    ) -> Result<(), Box<dyn std::error::Error>> {
3121        use crate::SignalBridge;
3122
3123        let temp_dir = std::env::temp_dir();
3124        let timestamp = SystemTime::now()
3125            .duration_since(UNIX_EPOCH)
3126            .unwrap()
3127            .as_millis();
3128        let db_path = temp_dir.join(format!("test_maintenance_fresh_{}.db", timestamp));
3129        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3130
3131        // Get initial signed pre-key count
3132        let initial_signed_count = bridge
3133            .storage
3134            .signed_pre_key_store()
3135            .signed_pre_key_count()
3136            .await;
3137        let initial_kyber_count = bridge
3138            .storage
3139            .kyber_pre_key_store()
3140            .kyber_pre_key_count()
3141            .await;
3142
3143        // Perform maintenance - fresh keys should not be rotated
3144        bridge.perform_key_maintenance().await?;
3145
3146        let after_signed_count = bridge
3147            .storage
3148            .signed_pre_key_store()
3149            .signed_pre_key_count()
3150            .await;
3151        let after_kyber_count = bridge
3152            .storage
3153            .kyber_pre_key_store()
3154            .kyber_pre_key_count()
3155            .await;
3156
3157        // Fresh keys should not be rotated
3158        assert_eq!(
3159            initial_signed_count, after_signed_count,
3160            "Fresh signed pre-keys should not be rotated"
3161        );
3162        assert_eq!(
3163            initial_kyber_count, after_kyber_count,
3164            "Fresh Kyber pre-keys should not be rotated"
3165        );
3166
3167        let _ = std::fs::remove_file(&db_path);
3168        Ok(())
3169    }
3170
3171    #[tokio::test]
3172    async fn test_perform_key_maintenance_idempotent() -> Result<(), Box<dyn std::error::Error>> {
3173        use crate::SignalBridge;
3174
3175        let temp_dir = std::env::temp_dir();
3176        let timestamp = SystemTime::now()
3177            .duration_since(UNIX_EPOCH)
3178            .unwrap()
3179            .as_millis();
3180        let db_path = temp_dir.join(format!("test_maintenance_idempotent_{}.db", timestamp));
3181        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3182
3183        // Perform maintenance multiple times
3184        bridge.perform_key_maintenance().await?;
3185
3186        let first_pre_key_count = bridge.storage.pre_key_store().pre_key_count().await;
3187        let first_signed_count = bridge
3188            .storage
3189            .signed_pre_key_store()
3190            .signed_pre_key_count()
3191            .await;
3192
3193        bridge.perform_key_maintenance().await?;
3194
3195        let second_pre_key_count = bridge.storage.pre_key_store().pre_key_count().await;
3196        let second_signed_count = bridge
3197            .storage
3198            .signed_pre_key_store()
3199            .signed_pre_key_count()
3200            .await;
3201
3202        // Consecutive maintenance calls should not change counts (idempotent)
3203        assert_eq!(
3204            first_pre_key_count, second_pre_key_count,
3205            "Pre-key count should be stable between maintenance calls"
3206        );
3207        assert_eq!(
3208            first_signed_count, second_signed_count,
3209            "Signed pre-key count should be stable between maintenance calls"
3210        );
3211
3212        let _ = std::fs::remove_file(&db_path);
3213        Ok(())
3214    }
3215
3216    #[tokio::test]
3217    async fn test_perform_key_maintenance_rotates_kyber_keys(
3218    ) -> Result<(), Box<dyn std::error::Error>> {
3219        use crate::key_rotation::ROTATION_INTERVAL_SECS;
3220        use crate::SignalBridge;
3221        use libsignal_protocol::{
3222            kem, GenericSignedPreKey, KyberPreKeyId, KyberPreKeyRecord, KyberPreKeyStore, Timestamp,
3223        };
3224
3225        let temp_dir = std::env::temp_dir();
3226        let timestamp = SystemTime::now()
3227            .duration_since(UNIX_EPOCH)
3228            .unwrap()
3229            .as_millis();
3230        let db_path = temp_dir.join(format!("test_maintenance_kyber_{}.db", timestamp));
3231        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3232
3233        // Get initial Kyber key count
3234        let initial_kyber_count = bridge
3235            .storage
3236            .kyber_pre_key_store()
3237            .kyber_pre_key_count()
3238            .await;
3239        assert!(initial_kyber_count >= 1, "Should have initial Kyber key");
3240
3241        // Create an old Kyber key to simulate needing rotation
3242        let identity_key_pair = bridge
3243            .storage
3244            .identity_store()
3245            .get_identity_key_pair()
3246            .await?;
3247
3248        let old_timestamp =
3249            SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() - (ROTATION_INTERVAL_SECS + 1);
3250
3251        let mut rng = rand::rng();
3252        let kyber_keypair = kem::KeyPair::generate(kem::KeyType::Kyber1024, &mut rng);
3253        let kyber_signature = identity_key_pair
3254            .private_key()
3255            .calculate_signature(&kyber_keypair.public_key.serialize(), &mut rng)?;
3256        let old_kyber_key = KyberPreKeyRecord::new(
3257            KyberPreKeyId::from(999u32),
3258            Timestamp::from_epoch_millis(old_timestamp * 1000),
3259            &kyber_keypair,
3260            &kyber_signature,
3261        );
3262
3263        // Save the old key as the "current" one by giving it a high ID
3264        bridge
3265            .storage
3266            .kyber_pre_key_store()
3267            .save_kyber_pre_key(KyberPreKeyId::from(999u32), &old_kyber_key)
3268            .await?;
3269
3270        // Get the max ID before maintenance
3271        let max_id_before = bridge
3272            .storage
3273            .kyber_pre_key_store()
3274            .get_max_kyber_pre_key_id()
3275            .await?
3276            .unwrap();
3277
3278        // Perform maintenance - should rotate Kyber key
3279        bridge.perform_key_maintenance().await?;
3280
3281        // Get the max ID after maintenance
3282        let max_id_after = bridge
3283            .storage
3284            .kyber_pre_key_store()
3285            .get_max_kyber_pre_key_id()
3286            .await?
3287            .unwrap();
3288
3289        // The max ID should have increased (new key was generated)
3290        assert!(
3291            max_id_after > max_id_before,
3292            "Max Kyber key ID should increase after rotation (before: {}, after: {})",
3293            max_id_before,
3294            max_id_after
3295        );
3296
3297        let _ = std::fs::remove_file(&db_path);
3298        Ok(())
3299    }
3300
3301    #[tokio::test]
3302    async fn test_perform_key_maintenance_cleans_up_expired_kyber_keys(
3303    ) -> Result<(), Box<dyn std::error::Error>> {
3304        use crate::key_rotation::GRACE_PERIOD_SECS;
3305        use crate::SignalBridge;
3306        use libsignal_protocol::{
3307            kem, GenericSignedPreKey, KyberPreKeyId, KyberPreKeyRecord, KyberPreKeyStore, Timestamp,
3308        };
3309
3310        let temp_dir = std::env::temp_dir();
3311        let timestamp = SystemTime::now()
3312            .duration_since(UNIX_EPOCH)
3313            .unwrap()
3314            .as_millis();
3315        let db_path = temp_dir.join(format!("test_maintenance_kyber_cleanup_{}.db", timestamp));
3316        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3317
3318        let identity_key_pair = bridge
3319            .storage
3320            .identity_store()
3321            .get_identity_key_pair()
3322            .await?;
3323
3324        // Create an expired Kyber key (past grace period) with low ID so it won't trigger rotation check
3325        let expired_timestamp =
3326            SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() - (GRACE_PERIOD_SECS + 1);
3327
3328        let mut rng = rand::rng();
3329        let kyber_keypair = kem::KeyPair::generate(kem::KeyType::Kyber1024, &mut rng);
3330        let kyber_signature = identity_key_pair
3331            .private_key()
3332            .calculate_signature(&kyber_keypair.public_key.serialize(), &mut rng)?;
3333        let expired_kyber_key = KyberPreKeyRecord::new(
3334            KyberPreKeyId::from(0u32), // Use ID 0 so it's not the max and won't trigger rotation
3335            Timestamp::from_epoch_millis(expired_timestamp * 1000),
3336            &kyber_keypair,
3337            &kyber_signature,
3338        );
3339
3340        bridge
3341            .storage
3342            .kyber_pre_key_store()
3343            .save_kyber_pre_key(KyberPreKeyId::from(0u32), &expired_kyber_key)
3344            .await?;
3345
3346        let before_count = bridge
3347            .storage
3348            .kyber_pre_key_store()
3349            .kyber_pre_key_count()
3350            .await;
3351        assert!(
3352            before_count >= 2,
3353            "Should have at least 2 Kyber keys (fresh + expired)"
3354        );
3355
3356        // Perform maintenance - should clean up expired Kyber key
3357        bridge.perform_key_maintenance().await?;
3358
3359        let after_count = bridge
3360            .storage
3361            .kyber_pre_key_store()
3362            .kyber_pre_key_count()
3363            .await;
3364
3365        // Should have fewer Kyber keys after cleanup (expired one removed)
3366        assert!(
3367            after_count < before_count,
3368            "Kyber key count should decrease after cleanup (before: {}, after: {})",
3369            before_count,
3370            after_count
3371        );
3372
3373        let _ = std::fs::remove_file(&db_path);
3374        Ok(())
3375    }
3376
3377    #[tokio::test]
3378    async fn test_bundle_uses_latest_available_keys() -> Result<(), Box<dyn std::error::Error>> {
3379        use crate::key_rotation::{rotate_kyber_pre_key, rotate_signed_pre_key};
3380        use crate::SignalBridge;
3381
3382        let temp_dir = std::env::temp_dir();
3383        let timestamp = SystemTime::now()
3384            .duration_since(UNIX_EPOCH)
3385            .unwrap()
3386            .as_millis();
3387        let db_path = temp_dir.join(format!("test_bundle_latest_keys_{}.db", timestamp));
3388        let mut bridge = SignalBridge::new(db_path.to_str().unwrap()).await?;
3389
3390        // Generate initial bundle
3391        let (initial_bundle_bytes, _, _, _) = bridge.generate_pre_key_bundle().await?;
3392        let initial_bundle: SerializablePreKeyBundle = bincode::deserialize(&initial_bundle_bytes)?;
3393
3394        // Initial bundle should use latest (maximum) keys
3395        // We initialize with 100 pre-keys (IDs 1-100), 1 signed (ID 1), 1 kyber (ID 1)
3396        assert_eq!(initial_bundle.pre_key_id, Some(100));
3397        assert_eq!(initial_bundle.signed_pre_key_id, 1);
3398        assert_eq!(initial_bundle.kyber_pre_key_id, 1);
3399
3400        // Force key rotation by calling rotation functions directly
3401        let identity_key_pair = bridge
3402            .storage
3403            .identity_store()
3404            .get_identity_key_pair()
3405            .await?;
3406        rotate_signed_pre_key(&mut bridge.storage, &identity_key_pair).await?;
3407        rotate_kyber_pre_key(&mut bridge.storage, &identity_key_pair).await?;
3408
3409        // Generate new bundle after rotation
3410        let (rotated_bundle_bytes, _, _, _) = bridge.generate_pre_key_bundle().await?;
3411        let rotated_bundle: SerializablePreKeyBundle = bincode::deserialize(&rotated_bundle_bytes)?;
3412
3413        // Bundle should now use the NEW (higher ID) keys, not still use #1
3414        // After rotation, we expect:
3415        // - SignedPreKey #2 (rotated from #1)
3416        // - KyberPreKey #2 (rotated from #1)
3417        // - PreKey should still be from available pool (may still be #1 if not consumed)
3418        assert_eq!(
3419            rotated_bundle.signed_pre_key_id, 2,
3420            "Bundle should use latest signed pre-key after rotation"
3421        );
3422        assert_eq!(
3423            rotated_bundle.kyber_pre_key_id, 2,
3424            "Bundle should use latest kyber pre-key after rotation"
3425        );
3426
3427        let _ = std::fs::remove_file(&db_path);
3428        Ok(())
3429    }
3430
3431    #[tokio::test]
3432    async fn test_extract_identity_from_prekey_message() -> Result<(), Box<dyn std::error::Error>> {
3433        use libsignal_protocol::PreKeySignalMessage;
3434
3435        let temp_dir = std::env::temp_dir();
3436        let timestamp = SystemTime::now()
3437            .duration_since(UNIX_EPOCH)
3438            .unwrap()
3439            .as_millis();
3440
3441        let alice_db_path = temp_dir.join(format!("test_extract_alice_{}.db", timestamp));
3442        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3443
3444        let bob_db_path = temp_dir.join(format!("test_extract_bob_{}.db", timestamp));
3445        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3446
3447        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3448        bob_bridge
3449            .establish_session("alice", &alice_bundle_bytes)
3450            .await?;
3451
3452        let plaintext = b"Hello Alice!";
3453        let ciphertext = bob_bridge.encrypt_message("alice", plaintext).await?;
3454
3455        let prekey_msg = PreKeySignalMessage::try_from(ciphertext.as_ref())?;
3456        let extracted_identity_key = prekey_msg.identity_key();
3457
3458        let bob_identity_key_pair = bob_bridge
3459            .storage
3460            .identity_store()
3461            .get_identity_key_pair()
3462            .await?;
3463        let bob_identity_key = bob_identity_key_pair.identity_key();
3464
3465        assert_eq!(
3466            extracted_identity_key.serialize(),
3467            bob_identity_key.serialize(),
3468            "Alice should be able to extract Bob's identity key from the PreKeySignalMessage"
3469        );
3470
3471        let _ = std::fs::remove_file(&alice_db_path);
3472        let _ = std::fs::remove_file(&bob_db_path);
3473        Ok(())
3474    }
3475
3476    #[tokio::test]
3477    async fn test_first_message_is_prekey_signal_message() -> Result<(), Box<dyn std::error::Error>>
3478    {
3479        use libsignal_protocol::PreKeySignalMessage;
3480
3481        let temp_dir = std::env::temp_dir();
3482        let timestamp = SystemTime::now()
3483            .duration_since(UNIX_EPOCH)
3484            .unwrap()
3485            .as_millis();
3486
3487        let alice_db_path = temp_dir.join(format!("test_prekey_type_alice_{}.db", timestamp));
3488        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3489
3490        let bob_db_path = temp_dir.join(format!("test_prekey_type_bob_{}.db", timestamp));
3491        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3492
3493        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3494        bob_bridge
3495            .establish_session("alice", &alice_bundle_bytes)
3496            .await?;
3497
3498        let plaintext = b"Hello Alice!";
3499        let ciphertext = bob_bridge.encrypt_message("alice", plaintext).await?;
3500
3501        assert!(
3502            PreKeySignalMessage::try_from(ciphertext.as_ref()).is_ok(),
3503            "First message to a new recipient must be a PreKeySignalMessage containing sender identity"
3504        );
3505
3506        let _ = std::fs::remove_file(&alice_db_path);
3507        let _ = std::fs::remove_file(&bob_db_path);
3508        Ok(())
3509    }
3510
3511    #[tokio::test]
3512    async fn test_decrypt_initial_message_from_unknown_sender(
3513    ) -> Result<(), Box<dyn std::error::Error>> {
3514        let temp_dir = std::env::temp_dir();
3515        let timestamp = SystemTime::now()
3516            .duration_since(UNIX_EPOCH)
3517            .unwrap()
3518            .as_millis();
3519
3520        let alice_db_path = temp_dir.join(format!("test_unknown_alice_{}.db", timestamp));
3521        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3522
3523        let bob_db_path = temp_dir.join(format!("test_unknown_bob_{}.db", timestamp));
3524        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3525
3526        let bob_nostr_keypair = bob_bridge.derive_nostr_keypair().await?;
3527        let bob_nostr_pubkey = bob_nostr_keypair.public_key().to_string();
3528
3529        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3530        let alice_rdx = bob_bridge
3531            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3532            .await?;
3533
3534        let plaintext = b"Hello Alice!";
3535        let ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3536
3537        let result = alice_bridge
3538            .decrypt_message(&bob_nostr_pubkey, &ciphertext)
3539            .await?;
3540
3541        assert_eq!(result.plaintext, plaintext);
3542        assert!(result.should_republish_bundle);
3543
3544        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3545        assert!(bob_contact.rdx_fingerprint.starts_with("RDX:"));
3546        assert_eq!(bob_contact.nostr_pubkey, bob_nostr_pubkey);
3547        assert!(bob_contact
3548            .user_alias
3549            .as_ref()
3550            .unwrap()
3551            .starts_with("Unknown-"));
3552
3553        let response = b"Hi Bob!";
3554        let response_ciphertext = alice_bridge
3555            .encrypt_message(&bob_contact.rdx_fingerprint, response)
3556            .await?;
3557
3558        let decrypted_response = bob_bridge
3559            .decrypt_message(
3560                &alice_bridge
3561                    .derive_nostr_keypair()
3562                    .await?
3563                    .public_key()
3564                    .to_string(),
3565                &response_ciphertext,
3566            )
3567            .await?;
3568
3569        assert_eq!(decrypted_response.plaintext, response);
3570
3571        let _ = std::fs::remove_file(&alice_db_path);
3572        let _ = std::fs::remove_file(&bob_db_path);
3573        Ok(())
3574    }
3575
3576    #[tokio::test]
3577    async fn test_bidirectional_session_uses_signal_message(
3578    ) -> Result<(), Box<dyn std::error::Error>> {
3579        use libsignal_protocol::{PreKeySignalMessage, SignalMessage};
3580
3581        let temp_dir = std::env::temp_dir();
3582        let timestamp = SystemTime::now()
3583            .duration_since(UNIX_EPOCH)
3584            .unwrap()
3585            .as_millis();
3586
3587        let alice_db_path = temp_dir.join(format!("test_bidi_alice_{}.db", timestamp));
3588        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3589
3590        let bob_db_path = temp_dir.join(format!("test_bidi_bob_{}.db", timestamp));
3591        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3592
3593        let alice_nostr_pubkey = alice_bridge
3594            .derive_nostr_keypair()
3595            .await?
3596            .public_key()
3597            .to_string();
3598        let bob_nostr_pubkey = bob_bridge
3599            .derive_nostr_keypair()
3600            .await?
3601            .public_key()
3602            .to_string();
3603
3604        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3605        let alice_rdx = bob_bridge
3606            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3607            .await?;
3608
3609        let first_msg = b"Hello Alice!";
3610        let first_ciphertext = bob_bridge.encrypt_message(&alice_rdx, first_msg).await?;
3611
3612        assert!(
3613            PreKeySignalMessage::try_from(first_ciphertext.as_ref()).is_ok(),
3614            "First message from Bob should be PreKeySignalMessage"
3615        );
3616
3617        let result = alice_bridge
3618            .decrypt_message(&bob_nostr_pubkey, &first_ciphertext)
3619            .await?;
3620        assert_eq!(result.plaintext, first_msg);
3621
3622        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3623
3624        let alice_response = b"Hi Bob!";
3625        let alice_response_ciphertext = alice_bridge
3626            .encrypt_message(&bob_contact.rdx_fingerprint, alice_response)
3627            .await?;
3628
3629        assert!(
3630            SignalMessage::try_from(alice_response_ciphertext.as_ref()).is_ok(),
3631            "Alice's response uses SignalMessage (session established on her side during decrypt)"
3632        );
3633
3634        let bob_result = bob_bridge
3635            .decrypt_message(&alice_nostr_pubkey, &alice_response_ciphertext)
3636            .await?;
3637        assert_eq!(bob_result.plaintext, alice_response);
3638
3639        let second_msg = b"How are you?";
3640        let second_ciphertext = bob_bridge.encrypt_message(&alice_rdx, second_msg).await?;
3641
3642        assert!(
3643            SignalMessage::try_from(second_ciphertext.as_ref()).is_ok(),
3644            "After bidirectional session established, should use SignalMessage"
3645        );
3646
3647        let second_result = alice_bridge
3648            .decrypt_message(&bob_nostr_pubkey, &second_ciphertext)
3649            .await?;
3650        assert_eq!(second_result.plaintext, second_msg);
3651        assert!(
3652            !second_result.should_republish_bundle,
3653            "No pre-key consumed in SignalMessage"
3654        );
3655
3656        let _ = std::fs::remove_file(&alice_db_path);
3657        let _ = std::fs::remove_file(&bob_db_path);
3658        Ok(())
3659    }
3660
3661    #[tokio::test]
3662    async fn test_encrypt_message_stores_in_history() -> Result<(), Box<dyn std::error::Error>> {
3663        let temp_dir = std::env::temp_dir();
3664        let timestamp = SystemTime::now()
3665            .duration_since(UNIX_EPOCH)
3666            .unwrap()
3667            .as_millis();
3668
3669        let alice_db_path = temp_dir.join(format!("test_encrypt_history_alice_{}.db", timestamp));
3670        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3671
3672        let bob_db_path = temp_dir.join(format!("test_encrypt_history_bob_{}.db", timestamp));
3673        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3674
3675        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3676        let alice_rdx = bob_bridge
3677            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3678            .await?;
3679
3680        let plaintext = b"Test outgoing message";
3681        let _ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3682
3683        let messages = bob_bridge
3684            .storage
3685            .message_history()
3686            .get_conversation_messages(&alice_rdx, 10, 0)?;
3687
3688        assert_eq!(messages.len(), 1, "Should have stored 1 outgoing message");
3689        assert_eq!(
3690            messages[0].direction,
3691            crate::message_history::MessageDirection::Outgoing
3692        );
3693        assert_eq!(
3694            messages[0].content,
3695            String::from_utf8(plaintext.to_vec()).unwrap()
3696        );
3697
3698        let _ = std::fs::remove_file(&alice_db_path);
3699        let _ = std::fs::remove_file(&bob_db_path);
3700        Ok(())
3701    }
3702
3703    #[tokio::test]
3704    async fn test_decrypt_message_stores_in_history() -> Result<(), Box<dyn std::error::Error>> {
3705        let temp_dir = std::env::temp_dir();
3706        let timestamp = SystemTime::now()
3707            .duration_since(UNIX_EPOCH)
3708            .unwrap()
3709            .as_millis();
3710
3711        let alice_db_path = temp_dir.join(format!("test_decrypt_history_alice_{}.db", timestamp));
3712        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3713
3714        let bob_db_path = temp_dir.join(format!("test_decrypt_history_bob_{}.db", timestamp));
3715        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3716
3717        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3718
3719        let alice_rdx = bob_bridge
3720            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3721            .await?;
3722
3723        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3724        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3725
3726        let plaintext = b"Test incoming message";
3727        let ciphertext = bob_bridge.encrypt_message(&alice_rdx, plaintext).await?;
3728
3729        let _result = alice_bridge.decrypt_message("", &ciphertext).await?;
3730
3731        let bob_contact = alice_bridge.lookup_contact(&bob_nostr_pubkey).await?;
3732
3733        let messages = alice_bridge
3734            .storage
3735            .message_history()
3736            .get_conversation_messages(&bob_contact.rdx_fingerprint, 10, 0)?;
3737
3738        assert_eq!(messages.len(), 1, "Should have stored 1 incoming message");
3739        assert_eq!(
3740            messages[0].direction,
3741            crate::message_history::MessageDirection::Incoming
3742        );
3743        assert_eq!(
3744            messages[0].content,
3745            String::from_utf8(plaintext.to_vec()).unwrap()
3746        );
3747        assert!(
3748            messages[0].was_prekey_message,
3749            "First message should be prekey"
3750        );
3751        assert!(
3752            messages[0].session_established,
3753            "Session should be established"
3754        );
3755
3756        let _ = std::fs::remove_file(&alice_db_path);
3757        let _ = std::fs::remove_file(&bob_db_path);
3758        Ok(())
3759    }
3760
3761    #[tokio::test]
3762    async fn test_bidirectional_message_history() -> Result<(), Box<dyn std::error::Error>> {
3763        let temp_dir = std::env::temp_dir();
3764        let timestamp = SystemTime::now()
3765            .duration_since(UNIX_EPOCH)
3766            .unwrap()
3767            .as_millis();
3768
3769        let alice_db_path =
3770            temp_dir.join(format!("test_bidirectional_history_alice_{}.db", timestamp));
3771        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3772
3773        let bob_db_path = temp_dir.join(format!("test_bidirectional_history_bob_{}.db", timestamp));
3774        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3775
3776        let (alice_bundle_bytes, _, _, _) = alice_bridge.generate_pre_key_bundle().await?;
3777        let alice_keys = alice_bridge.derive_nostr_keypair().await?;
3778        let alice_nostr_pubkey = alice_keys.public_key().to_hex();
3779
3780        let (bob_bundle_bytes, _, _, _) = bob_bridge.generate_pre_key_bundle().await?;
3781        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3782        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3783
3784        let bob_rdx = alice_bridge
3785            .add_contact_and_establish_session(&bob_bundle_bytes, Some("Bob"))
3786            .await?;
3787        let alice_rdx = bob_bridge
3788            .add_contact_and_establish_session(&alice_bundle_bytes, Some("Alice"))
3789            .await?;
3790
3791        let msg1 = b"Hello Bob!";
3792        let cipher1 = alice_bridge.encrypt_message(&bob_rdx, msg1).await?;
3793        let _result1 = bob_bridge
3794            .decrypt_message(&alice_nostr_pubkey, &cipher1)
3795            .await?;
3796
3797        let msg2 = b"Hi Alice!";
3798        let cipher2 = bob_bridge.encrypt_message(&alice_rdx, msg2).await?;
3799        let _result2 = alice_bridge
3800            .decrypt_message(&bob_nostr_pubkey, &cipher2)
3801            .await?;
3802
3803        let msg3 = b"How are you?";
3804        let cipher3 = alice_bridge.encrypt_message(&bob_rdx, msg3).await?;
3805        let _result3 = bob_bridge
3806            .decrypt_message(&alice_nostr_pubkey, &cipher3)
3807            .await?;
3808
3809        let alice_messages = alice_bridge
3810            .storage
3811            .message_history()
3812            .get_conversation_messages(&bob_rdx, 10, 0)?;
3813
3814        assert_eq!(
3815            alice_messages.len(),
3816            3,
3817            "Alice should have 3 messages (2 sent, 1 received)"
3818        );
3819
3820        let outgoing = alice_messages
3821            .iter()
3822            .filter(|m| m.direction == crate::message_history::MessageDirection::Outgoing)
3823            .count();
3824        let incoming = alice_messages
3825            .iter()
3826            .filter(|m| m.direction == crate::message_history::MessageDirection::Incoming)
3827            .count();
3828
3829        assert_eq!(outgoing, 2, "Alice sent 2 messages");
3830        assert_eq!(incoming, 1, "Alice received 1 message");
3831
3832        let bob_messages = bob_bridge
3833            .storage
3834            .message_history()
3835            .get_conversation_messages(&alice_rdx, 10, 0)?;
3836
3837        assert_eq!(
3838            bob_messages.len(),
3839            3,
3840            "Bob should have 3 messages (1 sent, 2 received)"
3841        );
3842
3843        let _ = std::fs::remove_file(&alice_db_path);
3844        let _ = std::fs::remove_file(&bob_db_path);
3845        Ok(())
3846    }
3847
3848    #[tokio::test]
3849    async fn test_conversation_list_ordering() -> Result<(), Box<dyn std::error::Error>> {
3850        let temp_dir = std::env::temp_dir();
3851        let timestamp = SystemTime::now()
3852            .duration_since(UNIX_EPOCH)
3853            .unwrap()
3854            .as_millis();
3855
3856        let alice_db_path =
3857            temp_dir.join(format!("test_conversation_ordering_alice_{}.db", timestamp));
3858        let mut alice_bridge = SignalBridge::new(alice_db_path.to_str().unwrap()).await?;
3859
3860        let bob_db_path = temp_dir.join(format!("test_conversation_ordering_bob_{}.db", timestamp));
3861        let mut bob_bridge = SignalBridge::new(bob_db_path.to_str().unwrap()).await?;
3862
3863        let charlie_db_path = temp_dir.join(format!(
3864            "test_conversation_ordering_charlie_{}.db",
3865            timestamp
3866        ));
3867        let mut charlie_bridge = SignalBridge::new(charlie_db_path.to_str().unwrap()).await?;
3868
3869        let (bob_bundle_bytes, _, _, _) = bob_bridge.generate_pre_key_bundle().await?;
3870        let bob_rdx = alice_bridge
3871            .add_contact_and_establish_session(&bob_bundle_bytes, Some("Bob"))
3872            .await?;
3873
3874        let (charlie_bundle_bytes, _, _, _) = charlie_bridge.generate_pre_key_bundle().await?;
3875        let charlie_rdx = alice_bridge
3876            .add_contact_and_establish_session(&charlie_bundle_bytes, Some("Charlie"))
3877            .await?;
3878
3879        let bob_msg = b"From Bob";
3880        let bob_cipher = alice_bridge.encrypt_message(&bob_rdx, bob_msg).await?;
3881
3882        let bob_keys = bob_bridge.derive_nostr_keypair().await?;
3883        let bob_nostr_pubkey = bob_keys.public_key().to_hex();
3884
3885        let _bob_result = bob_bridge
3886            .decrypt_message(&bob_nostr_pubkey, &bob_cipher)
3887            .await?;
3888
3889        tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
3890
3891        let charlie_msg = b"From Charlie";
3892        let charlie_cipher = alice_bridge
3893            .encrypt_message(&charlie_rdx, charlie_msg)
3894            .await?;
3895
3896        let charlie_keys = charlie_bridge.derive_nostr_keypair().await?;
3897        let charlie_nostr_pubkey = charlie_keys.public_key().to_hex();
3898
3899        let _charlie_result = charlie_bridge
3900            .decrypt_message(&charlie_nostr_pubkey, &charlie_cipher)
3901            .await?;
3902
3903        let conversations = alice_bridge
3904            .storage
3905            .message_history()
3906            .get_conversations(false)?;
3907
3908        assert_eq!(conversations.len(), 2, "Alice should have 2 conversations");
3909        assert!(
3910            conversations[0].last_message_timestamp > conversations[1].last_message_timestamp,
3911            "Conversations should be ordered by most recent message"
3912        );
3913
3914        let _ = std::fs::remove_file(&alice_db_path);
3915        let _ = std::fs::remove_file(&bob_db_path);
3916        let _ = std::fs::remove_file(&charlie_db_path);
3917        Ok(())
3918    }
3919}