diff --git a/double-ratchets/src/storage/db.rs b/double-ratchets/src/storage/db.rs index d69c651..b07fdd3 100644 --- a/double-ratchets/src/storage/db.rs +++ b/double-ratchets/src/storage/db.rs @@ -50,17 +50,17 @@ impl RatchetStorage { /// Opens an existing encrypted database file. pub fn new(path: &str, key: &str) -> Result { let db = SqliteDb::sqlcipher(path.to_string(), key.to_string())?; - Self::new_internal(db) + Self::run_migration(db) } /// Creates an in-memory storage (useful for testing). pub fn in_memory() -> Result { let db = SqliteDb::in_memory()?; - Self::new_internal(db) + Self::run_migration(db) } /// Creates a new ratchet storage with the given database. - fn new_internal(db: SqliteDb) -> Result { + fn run_migration(db: SqliteDb) -> Result { // Initialize schema db.execute_batch(RATCHET_SCHEMA)?; Ok(Self { db }) diff --git a/double-ratchets/src/storage/session.rs b/double-ratchets/src/storage/session.rs index cdab488..7e04487 100644 --- a/double-ratchets/src/storage/session.rs +++ b/double-ratchets/src/storage/session.rs @@ -1,7 +1,7 @@ //! Session wrapper for automatic state persistence. -use x25519_dalek::PublicKey; use storage::StorageError; +use x25519_dalek::PublicKey; use crate::{ InstallationKeyPair, @@ -83,10 +83,13 @@ impl<'a, D: HkdfInfo + Clone> RatchetSession<'a, D> { /// Initializes a new session as a sender and persists the initial state. pub fn create_sender_session( storage: &'a mut RatchetStorage, - conversation_id: impl Into, + conversation_id: &str, shared_secret: SharedSecret, remote_pub: PublicKey, ) -> Result { + if storage.exists(conversation_id)? { + return Err(StorageError::ConversationAlreadyExists); + } let state = RatchetState::::init_sender(shared_secret, remote_pub); Self::create(storage, conversation_id, state) } @@ -94,13 +97,12 @@ impl<'a, D: HkdfInfo + Clone> RatchetSession<'a, D> { /// Initializes a new session as a receiver and persists the initial state. pub fn create_receiver_session( storage: &'a mut RatchetStorage, - conversation_id: impl Into, + conversation_id: &str, shared_secret: SharedSecret, dh_self: InstallationKeyPair, ) -> Result { - let conversation_id = conversation_id.into(); - if storage.exists(&conversation_id)? { - return Self::open(storage, conversation_id); + if storage.exists(conversation_id)? { + return Err(StorageError::ConversationAlreadyExists); } let state = RatchetState::::init_receiver(shared_secret, dh_self); @@ -310,4 +312,76 @@ mod tests { assert_eq!(session.state().msg_send, 1); } } + + #[test] + fn test_create_sender_session_fails_when_conversation_exists() { + let mut storage = create_test_storage(); + + let shared_secret = [0x42; 32]; + let bob_keypair = InstallationKeyPair::generate(); + let bob_pub = bob_keypair.public().clone(); + + // First creation succeeds + { + let _session: RatchetSession = RatchetSession::create_sender_session( + &mut storage, + "conv1", + shared_secret, + bob_pub.clone(), + ) + .unwrap(); + } + + // Second creation should fail with ConversationAlreadyExists + { + let result: Result, _> = + RatchetSession::create_sender_session( + &mut storage, + "conv1", + shared_secret, + bob_pub.clone(), + ); + + assert!(matches!( + result, + Err(StorageError::ConversationAlreadyExists) + )); + } + } + + #[test] + fn test_create_receiver_session_fails_when_conversation_exists() { + let mut storage = create_test_storage(); + + let shared_secret = [0x42; 32]; + let bob_keypair = InstallationKeyPair::generate(); + + // First creation succeeds + { + let _session: RatchetSession = RatchetSession::create_receiver_session( + &mut storage, + "conv1", + shared_secret, + bob_keypair, + ) + .unwrap(); + } + + // Second creation should fail with ConversationAlreadyExists + { + let another_keypair = InstallationKeyPair::generate(); + let result: Result, _> = + RatchetSession::create_receiver_session( + &mut storage, + "conv1", + shared_secret, + another_keypair, + ); + + assert!(matches!( + result, + Err(StorageError::ConversationAlreadyExists) + )); + } + } } diff --git a/storage/src/error.rs b/storage/src/error.rs index 1410f85..e648153 100644 --- a/storage/src/error.rs +++ b/storage/src/error.rs @@ -26,6 +26,10 @@ pub enum StorageError { /// Transaction error. #[error("transaction error: {0}")] Transaction(String), + + /// Database already exists. + #[error("conversation already exists")] + ConversationAlreadyExists, } impl From for StorageError {