feat: sqlite crate

This commit is contained in:
kaichaosun 2026-03-28 07:13:03 +08:00
parent 130578b956
commit 9a90e86cb3
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
17 changed files with 71 additions and 37 deletions

14
Cargo.lock generated
View File

@ -108,6 +108,17 @@ dependencies = [
"prost",
]
[[package]]
name = "chat-sqlite"
version = "0.1.0"
dependencies = [
"crypto",
"hex",
"storage",
"tempfile",
"zeroize",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -502,6 +513,7 @@ dependencies = [
"base64",
"blake2",
"chat-proto",
"chat-sqlite",
"crypto",
"double-ratchets",
"hex",
@ -512,7 +524,6 @@ dependencies = [
"tempfile",
"thiserror",
"x25519-dalek",
"zeroize",
]
[[package]]
@ -930,6 +941,7 @@ dependencies = [
name = "storage"
version = "0.1.0"
dependencies = [
"crypto",
"rusqlite",
"thiserror",
]

View File

@ -3,6 +3,7 @@
resolver = "3"
members = [
"core/chat-sqlite",
"core/conversations",
"core/crypto",
"core/double-ratchets",

View File

@ -0,0 +1,14 @@
[package]
name = "chat-sqlite"
version = "0.1.0"
edition = "2024"
description = "SQLite storage implementation for libchat"
[dependencies]
crypto = { path = "../crypto" }
hex = "0.4.3"
storage = { path = "../storage" }
zeroize = { version = "1.8.2", features = ["derive"] }
[dev-dependencies]
tempfile = "3"

View File

@ -1,19 +1,16 @@
//! Chat-specific storage implementation.
//! Chat-specific SQLite storage implementation.
mod migrations;
mod types;
use crypto::PrivateKey;
use storage::{RusqliteError, SqliteDb, StorageConfig, StorageError, params};
use crypto::{Identity, PrivateKey};
use storage::{
ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore,
RusqliteError, SqliteDb, StorageConfig, StorageError, params,
};
use zeroize::Zeroize;
use crate::{
identity::Identity,
sqlite::types::IdentityRecord,
store::{
ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore,
},
};
use crate::types::IdentityRecord;
/// Chat-specific storage operations.
///
@ -37,7 +34,6 @@ impl ChatStorage {
migrations::apply_migrations(db.connection_mut())?;
Ok(Self { db })
}
}
impl IdentityStore for ChatStorage {
@ -241,6 +237,11 @@ impl ConversationStore for ChatStorage {
#[cfg(test)]
mod tests {
use storage::{
ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore, IdentityStore,
StorageConfig,
};
use super::*;
#[test]
@ -265,7 +266,7 @@ mod tests {
let mut storage = ChatStorage::new(StorageConfig::InMemory).unwrap();
let key1 = PrivateKey::random();
let pub1: crate::crypto::PublicKey = (&key1).into();
let pub1: crypto::PublicKey = (&key1).into();
let hex1 = hex::encode(pub1.as_bytes());
// Initially not found

View File

@ -2,8 +2,7 @@
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::crypto::PrivateKey;
use crate::identity::Identity;
use crypto::{Identity, PrivateKey};
/// Record for storing identity (secret key).
/// Implements ZeroizeOnDrop to securely clear secret key from memory.

View File

@ -8,6 +8,7 @@ crate-type = ["rlib","staticlib","dylib"]
[dependencies]
base64 = "0.22"
chat-sqlite = { path = "../chat-sqlite" }
blake2.workspace = true
chat-proto = { git = "https://github.com/logos-messaging/chat_proto" }
crypto = { path = "../crypto" }
@ -19,7 +20,6 @@ safer-ffi = "0.1.13"
thiserror = "2.0.17"
x25519-dalek = { version = "2.0.1", features = ["static_secrets", "reusable_secrets", "getrandom"] }
storage = { path = "../storage" }
zeroize = { version = "1.8.2", features = ["derive"] }
[dev-dependencies]
tempfile = "3"

View File

@ -13,12 +13,12 @@ use safer_ffi::{
prelude::{c_slice, repr_c},
};
use chat_sqlite::ChatStorage;
use storage::StorageConfig;
use crate::{
context::{Context, Introduction},
errors::ChatError,
sqlite::ChatStorage,
types::ContentData,
};

View File

@ -1,17 +1,18 @@
use std::rc::Rc;
use std::sync::Arc;
use chat_sqlite::ChatStorage;
use crypto::Identity;
use double_ratchets::{RatchetState, RatchetStorage};
use storage::StorageConfig;
use storage::{
ChatStore, ConversationKind, ConversationMeta, IdentityStore, StorageConfig,
};
use crate::{
conversation::{ConversationId, Convo, Id, PrivateV1Convo},
errors::ChatError,
identity::Identity,
inbox::Inbox,
proto::{EncryptedPayload, EnvelopeV1, Message},
sqlite::ChatStorage,
store::{ChatStore, ConversationKind, ConversationMeta, IdentityStore},
types::{AddressedEnvelope, ContentData},
};
@ -216,9 +217,9 @@ impl<T: ChatStore> Context<T> {
#[cfg(test)]
mod mock {
use crypto::PrivateKey;
use storage::StorageError;
use crate::store::{ConversationStore, EphemeralKeyStore, IdentityStore};
use storage::{
ConversationStore, EphemeralKeyStore, IdentityStore, StorageError,
};
use super::*;
use std::collections::HashMap;
@ -315,7 +316,10 @@ mod mock {
#[cfg(test)]
mod tests {
use crate::{context::mock::MockChatStore, sqlite::ChatStorage, store::ConversationStore};
use chat_sqlite::ChatStorage;
use storage::ConversationStore;
use crate::context::mock::MockChatStore;
use super::*;

View File

@ -10,7 +10,7 @@ use crypto::{PrekeyBundle, SymmetricKey32};
use crate::context::Introduction;
use crate::conversation::{ChatError, ConversationId, Convo, Id, PrivateV1Convo};
use crate::crypto::{CopyBytes, PrivateKey, PublicKey};
use crate::identity::Identity;
use crypto::Identity;
use crate::inbox::handshake::InboxHandshake;
use crate::proto;
use crate::types::{AddressedEncryptedPayload, ContentData};
@ -239,9 +239,8 @@ impl Id for Inbox {
#[cfg(test)]
mod tests {
use super::*;
use crate::sqlite::ChatStorage;
use crate::store::EphemeralKeyStore;
use storage::StorageConfig;
use chat_sqlite::ChatStorage;
use storage::{EphemeralKeyStore, StorageConfig};
#[test]
fn test_invite_privatev1_roundtrip() {

View File

@ -3,18 +3,15 @@ mod context;
mod conversation;
mod crypto;
mod errors;
mod identity;
mod inbox;
mod proto;
mod sqlite;
mod store;
mod types;
mod utils;
pub use api::*;
pub use chat_sqlite::ChatStorage;
pub use context::{Context, Introduction};
pub use errors::ChatError;
pub use sqlite::ChatStorage;
pub use storage::StorageConfig;
#[cfg(test)]

View File

@ -1,6 +1,6 @@
use std::fmt;
use crate::crypto::{PrivateKey, PublicKey};
use crate::{PrivateKey, PublicKey};
#[derive(Clone)]
pub struct Identity {

View File

@ -1,7 +1,9 @@
mod identity;
mod keys;
mod x3dh;
mod xeddsa_sign;
pub use identity::Identity;
pub use keys::{PrivateKey, PublicKey, SymmetricKey32};
pub use x3dh::{DomainSeparator, PrekeyBundle, X3Handshake};
pub use xeddsa_sign::{Ed25519Signature, SignatureError, xeddsa_sign, xeddsa_verify};

View File

@ -5,5 +5,6 @@ edition = "2024"
description = "Shared storage layer for libchat"
[dependencies]
crypto = { path = "../crypto" }
thiserror = "2"
rusqlite = { version = "0.35", features = ["bundled-sqlcipher-vendored-openssl"] }

View File

@ -7,9 +7,14 @@
mod errors;
mod sqlite;
mod store;
pub use errors::StorageError;
pub use sqlite::{SqliteDb, StorageConfig};
pub use store::{
ChatStore, ConversationKind, ConversationMeta, ConversationStore, EphemeralKeyStore,
IdentityStore,
};
// Re-export rusqlite types that domain crates will need
pub use rusqlite::{Connection, Error as RusqliteError, Transaction, params};

View File

@ -1,7 +1,6 @@
use crypto::PrivateKey;
use storage::StorageError;
use crypto::{Identity, PrivateKey};
use crate::identity::Identity;
use crate::StorageError;
/// Persistence operations for installation identity data.
pub trait IdentityStore {