chore: refactor create session

This commit is contained in:
kaichaosun 2026-01-28 17:55:58 +08:00
parent 7e1a4a6a79
commit c240450fb7
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
3 changed files with 87 additions and 9 deletions

View File

@ -50,17 +50,17 @@ impl RatchetStorage {
/// Opens an existing encrypted database file.
pub fn new(path: &str, key: &str) -> Result<Self, StorageError> {
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<Self, StorageError> {
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<Self, StorageError> {
fn run_migration(db: SqliteDb) -> Result<Self, StorageError> {
// Initialize schema
db.execute_batch(RATCHET_SCHEMA)?;
Ok(Self { db })

View File

@ -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<String>,
conversation_id: &str,
shared_secret: SharedSecret,
remote_pub: PublicKey,
) -> Result<Self, StorageError> {
if storage.exists(conversation_id)? {
return Err(StorageError::ConversationAlreadyExists);
}
let state = RatchetState::<D>::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<String>,
conversation_id: &str,
shared_secret: SharedSecret,
dh_self: InstallationKeyPair,
) -> Result<Self, StorageError> {
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::<D>::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<DefaultDomain> = RatchetSession::create_sender_session(
&mut storage,
"conv1",
shared_secret,
bob_pub.clone(),
)
.unwrap();
}
// Second creation should fail with ConversationAlreadyExists
{
let result: Result<RatchetSession<DefaultDomain>, _> =
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<DefaultDomain> = 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<DefaultDomain>, _> =
RatchetSession::create_receiver_session(
&mut storage,
"conv1",
shared_secret,
another_keypair,
);
assert!(matches!(
result,
Err(StorageError::ConversationAlreadyExists)
));
}
}
}

View File

@ -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<rusqlite::Error> for StorageError {