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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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