mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-08-02 21:23:16 +00:00
fix: zeroize for secret keys in db operations
This commit is contained in:
parent
0e62c44b7e
commit
c3e5f361bb
@ -64,6 +64,7 @@ pub fn create_context(name: repr_c::String) -> repr_c::Box<ContextHandle> {
|
|||||||
/// # Parameters
|
/// # Parameters
|
||||||
/// - name: Friendly name for the identity (used if creating new identity)
|
/// - name: Friendly name for the identity (used if creating new identity)
|
||||||
/// - db_path: Path to the SQLite database file
|
/// - db_path: Path to the SQLite database file
|
||||||
|
/// - db_secret: Secret key for encrypting the database
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// CResult with context handle on success, or error string on failure.
|
/// CResult with context handle on success, or error string on failure.
|
||||||
@ -73,8 +74,12 @@ pub fn create_context(name: repr_c::String) -> repr_c::Box<ContextHandle> {
|
|||||||
pub fn create_context_with_storage(
|
pub fn create_context_with_storage(
|
||||||
name: repr_c::String,
|
name: repr_c::String,
|
||||||
db_path: repr_c::String,
|
db_path: repr_c::String,
|
||||||
|
db_secret: repr_c::String,
|
||||||
) -> CResult<repr_c::Box<ContextHandle>, repr_c::String> {
|
) -> CResult<repr_c::Box<ContextHandle>, repr_c::String> {
|
||||||
let config = StorageConfig::File(db_path.to_string());
|
let config = StorageConfig::Encrypted {
|
||||||
|
path: db_path.to_string(),
|
||||||
|
key: db_secret.to_string(),
|
||||||
|
};
|
||||||
match Context::open(&*name, config) {
|
match Context::open(&*name, config) {
|
||||||
Ok(ctx) => CResult {
|
Ok(ctx) => CResult {
|
||||||
ok: Some(Box::new(ContextHandle(ctx)).into()),
|
ok: Some(Box::new(ContextHandle(ctx)).into()),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
//! Chat-specific storage implementation.
|
//! Chat-specific storage implementation.
|
||||||
|
|
||||||
use storage::{RusqliteError, SqliteDb, StorageConfig, StorageError, params};
|
use storage::{RusqliteError, SqliteDb, StorageConfig, StorageError, params};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use super::migrations;
|
use super::migrations;
|
||||||
use super::types::IdentityRecord;
|
use super::types::IdentityRecord;
|
||||||
@ -32,18 +33,24 @@ impl ChatStorage {
|
|||||||
// ==================== Identity Operations ====================
|
// ==================== Identity Operations ====================
|
||||||
|
|
||||||
/// Saves the identity (secret key).
|
/// Saves the identity (secret key).
|
||||||
|
///
|
||||||
|
/// Note: The secret key bytes are explicitly zeroized after use to minimize
|
||||||
|
/// the time sensitive data remains in stack memory.
|
||||||
pub fn save_identity(&mut self, identity: &Identity) -> Result<(), StorageError> {
|
pub fn save_identity(&mut self, identity: &Identity) -> Result<(), StorageError> {
|
||||||
self.db.connection().execute(
|
let mut secret_bytes = identity.secret().DANGER_to_bytes();
|
||||||
|
let result = self.db.connection().execute(
|
||||||
"INSERT OR REPLACE INTO identity (id, name, secret_key) VALUES (1, ?1, ?2)",
|
"INSERT OR REPLACE INTO identity (id, name, secret_key) VALUES (1, ?1, ?2)",
|
||||||
params![
|
params![identity.get_name(), secret_bytes.as_slice()],
|
||||||
identity.get_name(),
|
);
|
||||||
identity.secret().DANGER_to_bytes().as_slice()
|
secret_bytes.zeroize();
|
||||||
],
|
result?;
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the identity if it exists.
|
/// Loads the identity if it exists.
|
||||||
|
///
|
||||||
|
/// Note: Secret key bytes are zeroized after being copied into IdentityRecord,
|
||||||
|
/// which handles its own zeroization via ZeroizeOnDrop.
|
||||||
pub fn load_identity(&self) -> Result<Option<Identity>, StorageError> {
|
pub fn load_identity(&self) -> Result<Option<Identity>, StorageError> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.db
|
.db
|
||||||
@ -57,10 +64,18 @@ impl ChatStorage {
|
|||||||
});
|
});
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok((name, secret_key)) => {
|
Ok((name, mut secret_key_vec)) => {
|
||||||
let bytes: [u8; 32] = secret_key
|
let bytes: Result<[u8; 32], _> = secret_key_vec.as_slice().try_into();
|
||||||
.try_into()
|
let bytes = match bytes {
|
||||||
.map_err(|_| StorageError::InvalidData("Invalid secret key length".into()))?;
|
Ok(b) => b,
|
||||||
|
Err(_) => {
|
||||||
|
secret_key_vec.zeroize();
|
||||||
|
return Err(StorageError::InvalidData(
|
||||||
|
"Invalid secret key length".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
secret_key_vec.zeroize();
|
||||||
let record = IdentityRecord {
|
let record = IdentityRecord {
|
||||||
name,
|
name,
|
||||||
secret_key: bytes,
|
secret_key: bytes,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user