From eb189b0d6d25b01af742384bc41072bb532cbee8 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Fri, 16 Feb 2024 09:27:37 +0200 Subject: [PATCH] libsql: Make encryption cipher configurable Introduce a `EncryptionConfig` struct to configure both encrytion cipher and key. Needed to support multiple ciphers. Fixes #951 --- Cargo.lock | 1 + bottomless/src/replicator.rs | 25 ++++-- libsql-ffi/Cargo.toml | 1 + libsql-ffi/src/lib.rs | 46 ++++++++++ libsql-replication/src/injector/mod.rs | 4 +- libsql-replication/src/lib.rs | 11 ++- libsql-replication/src/replicator.rs | 4 +- libsql-server/src/config.rs | 5 +- libsql-server/src/connection/libsql.rs | 19 ++-- libsql-server/src/connection/write_proxy.rs | 11 +-- libsql-server/src/lib.rs | 4 +- libsql-server/src/main.rs | 13 ++- libsql-server/src/namespace/meta_store.rs | 2 +- libsql-server/src/namespace/mod.rs | 35 ++++---- libsql-server/src/query_result_builder.rs | 3 +- .../src/replication/primary/logger.rs | 17 ++-- libsql-server/src/test/bottomless.rs | 2 +- libsql-sys/src/connection.rs | 45 ++++++++-- libsql-sys/src/lib.rs | 4 + libsql/examples/replica.rs | 8 +- libsql/src/database.rs | 86 +++++++++++-------- libsql/src/database/builder.rs | 44 +++++----- libsql/src/lib.rs | 2 +- libsql/src/local/database.rs | 18 ++-- libsql/src/replication/mod.rs | 9 +- 25 files changed, 272 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb12571684..0e36166648 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2737,6 +2737,7 @@ name = "libsql-ffi" version = "0.1.0" dependencies = [ "bindgen", + "bytes", "cc", "libsql-wasmtime-bindings", ] diff --git a/bottomless/src/replicator.rs b/bottomless/src/replicator.rs index ebbc22c032..1701024b7e 100644 --- a/bottomless/src/replicator.rs +++ b/bottomless/src/replicator.rs @@ -15,8 +15,10 @@ use aws_sdk_s3::primitives::ByteStream; use aws_sdk_s3::{Client, Config}; use bytes::{Buf, Bytes}; use chrono::{NaiveDateTime, TimeZone, Utc}; +use libsql_sys::{Cipher, EncryptionConfig}; use std::ops::Deref; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use tokio::fs::{File, OpenOptions}; @@ -59,7 +61,7 @@ pub struct Replicator { pub db_name: String, use_compression: CompressionKind, - encryption_key: Option, + encryption_config: Option, max_frames_per_batch: usize, s3_upload_max_parallelism: usize, join_set: JoinSet<()>, @@ -85,7 +87,7 @@ pub struct Options { pub verify_crc: bool, /// Kind of compression algorithm used on the WAL frames to be sent to S3. pub use_compression: CompressionKind, - pub encryption_key: Option, + pub encryption_config: Option, pub aws_endpoint: Option, pub access_key_id: Option, pub secret_access_key: Option, @@ -181,6 +183,7 @@ impl Options { let use_compression = CompressionKind::parse(&env_var_or("LIBSQL_BOTTOMLESS_COMPRESSION", "zstd")) .map_err(|e| anyhow!("unknown compression kind: {}", e))?; + let encryption_cipher = env_var("LIBSQL_BOTTOMLESS_ENCRYPTION_CIPHER").ok(); let encryption_key = env_var("LIBSQL_BOTTOMLESS_ENCRYPTION_KEY") .map(Bytes::from) .ok(); @@ -196,12 +199,20 @@ impl Options { ), }; let s3_max_retries = env_var_or("LIBSQL_BOTTOMLESS_S3_MAX_RETRIES", 10).parse::()?; + let cipher = match encryption_cipher { + Some(cipher) => Cipher::from_str(&cipher)?, + None => Cipher::default(), + }; + let encryption_config = match encryption_key { + Some(key) => Some(EncryptionConfig::new(cipher, key)), + None => None, + }; Ok(Options { db_id, create_bucket_if_not_exists: true, verify_crc, use_compression, - encryption_key, + encryption_config, max_batch_interval, max_frames_per_batch, s3_upload_max_parallelism, @@ -358,7 +369,7 @@ impl Replicator { snapshot_waiter, snapshot_notifier: Arc::new(snapshot_notifier), use_compression: options.use_compression, - encryption_key: options.encryption_key, + encryption_config: options.encryption_config, max_frames_per_batch: options.max_frames_per_batch, s3_upload_max_parallelism: options.s3_upload_max_parallelism, join_set, @@ -697,7 +708,7 @@ impl Replicator { flags, Sqlite3WalManager::new(), libsql_sys::connection::NO_AUTOCHECKPOINT, // no checkpointing - self.encryption_key.clone(), + self.encryption_config.clone(), )?; Ok(conn) } @@ -1323,12 +1334,12 @@ impl Replicator { utc_time: Option, db_path: &Path, ) -> Result { - let encryption_key = self.encryption_key.clone(); + let encryption_config = self.encryption_config.clone(); let mut injector = libsql_replication::injector::Injector::new( db_path, 4096, libsql_sys::connection::NO_AUTOCHECKPOINT, - encryption_key, + encryption_config, )?; let prefix = format!("{}-{}/", self.db_name, generation); let mut page_buf = { diff --git a/libsql-ffi/Cargo.toml b/libsql-ffi/Cargo.toml index 9698d215e9..06e222e0eb 100644 --- a/libsql-ffi/Cargo.toml +++ b/libsql-ffi/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/tursodatabase/libsql" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bytes = "1.5.0" libsql-wasmtime-bindings = { version = "0.2.1", optional = true } [build-dependencies] diff --git a/libsql-ffi/src/lib.rs b/libsql-ffi/src/lib.rs index f83a715e33..e1d8e334bd 100644 --- a/libsql-ffi/src/lib.rs +++ b/libsql-ffi/src/lib.rs @@ -7,6 +7,7 @@ use std::fmt; use std::marker::PhantomData; use std::mem; use std::os::raw::c_int; +use std::str::FromStr; #[cfg(feature = "wasmtime-bindings")] pub use libsql_wasm::{ @@ -15,6 +16,51 @@ pub use libsql_wasm::{ include!(concat!(env!("OUT_DIR"), "/bindgen.rs")); +#[derive(Clone, Debug)] +pub enum Cipher { + // AES 256 Bit CBC - No HMAC (wxSQLite3) + AES_256_CBC, +} + +impl Default for Cipher { + fn default() -> Self { + Cipher::AES_256_CBC + } +} + +impl FromStr for Cipher { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "aes256cbc" => Ok(Cipher::AES_256_CBC), + _ => Err(Error::new(SQLITE_MISUSE)), + } + } +} + +#[derive(Clone, Debug)] +pub struct EncryptionConfig { + pub cipher: Cipher, + pub encryption_key: bytes::Bytes, +} + +impl EncryptionConfig { + pub fn new(cipher: Cipher, encryption_key: bytes::Bytes) -> Self { + Self { + cipher, + encryption_key, + } + } + + pub fn cipher_id(&self) -> c_int { + match self.cipher { + Cipher::AES_256_CBC => 2, // CODEC_TYPE_AES256 + Cipher::SQLCIPHER => 4, // CODEC_TYPE_SQLCIPHER + } + } +} + #[must_use] pub fn SQLITE_STATIC() -> sqlite3_destructor_type { None diff --git a/libsql-replication/src/injector/mod.rs b/libsql-replication/src/injector/mod.rs index cc61c0fec0..0c44b5700b 100644 --- a/libsql-replication/src/injector/mod.rs +++ b/libsql-replication/src/injector/mod.rs @@ -44,7 +44,7 @@ impl Injector { path: impl AsRef, capacity: usize, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, ) -> Result { let buffer = FrameBuffer::default(); let wal_manager = InjectorWalManager::new(buffer.clone()); @@ -56,7 +56,7 @@ impl Injector { | OpenFlags::SQLITE_OPEN_NO_MUTEX, wal_manager, auto_checkpoint, - encryption_key, + encryption_config, )?; Ok(Self { diff --git a/libsql-replication/src/lib.rs b/libsql-replication/src/lib.rs index 3ec77e65b1..f2f0e520de 100644 --- a/libsql-replication/src/lib.rs +++ b/libsql-replication/src/lib.rs @@ -7,6 +7,8 @@ pub mod snapshot; mod error; +use libsql_sys::Cipher; + pub const LIBSQL_PAGE_SIZE: usize = 4096; #[derive(Debug, Clone)] @@ -16,14 +18,17 @@ pub struct FrameEncryptor { } impl FrameEncryptor { - pub fn new(key: bytes::Bytes) -> Self { + pub fn new(encryption_config: libsql_sys::EncryptionConfig) -> Self { #[cfg(feature = "encryption")] const SEED: u32 = 911; #[cfg(not(feature = "encryption"))] - let _ = key; + let _ = encryption_config; use aes::cipher::KeyIvInit; + // TODO: make cipher configurable + assert!(matches!(encryption_config.cipher, Cipher::AES_256_CBC)); + #[allow(unused_mut)] let mut iv: [u8; 16] = [0; 16]; #[allow(unused_mut)] @@ -31,7 +36,7 @@ impl FrameEncryptor { #[cfg(feature = "encryption")] libsql_sys::connection::generate_initial_vector(SEED, &mut iv); #[cfg(feature = "encryption")] - libsql_sys::connection::generate_aes256_key(&key, &mut digest); + libsql_sys::connection::generate_aes256_key(&cfg.key, &mut digest); let enc = cbc::Encryptor::new((&digest).into(), (&iv).into()); let dec = cbc::Decryptor::new((&digest).into(), (&iv).into()); diff --git a/libsql-replication/src/replicator.rs b/libsql-replication/src/replicator.rs index 9bf6a28a28..374b6de67a 100644 --- a/libsql-replication/src/replicator.rs +++ b/libsql-replication/src/replicator.rs @@ -157,7 +157,7 @@ impl Replicator { client: C, db_path: PathBuf, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, ) -> Result { let injector = { let db_path = db_path.clone(); @@ -166,7 +166,7 @@ impl Replicator { db_path, INJECTOR_BUFFER_CAPACITY, auto_checkpoint, - encryption_key, + encryption_config, ) }) .await?? diff --git a/libsql-server/src/config.rs b/libsql-server/src/config.rs index 1c72d59ec3..47dd141918 100644 --- a/libsql-server/src/config.rs +++ b/libsql-server/src/config.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use anyhow::Context; use hyper::client::HttpConnector; use hyper_rustls::HttpsConnector; +use libsql_sys::EncryptionConfig; use sha256::try_digest; use tokio::time::Duration; use tonic::transport::Channel; @@ -125,7 +126,7 @@ pub struct DbConfig { pub snapshot_exec: Option, pub checkpoint_interval: Option, pub snapshot_at_shutdown: bool, - pub encryption_key: Option, + pub encryption_config: Option, pub max_concurrent_requests: u64, } @@ -143,7 +144,7 @@ impl Default for DbConfig { snapshot_exec: None, checkpoint_interval: None, snapshot_at_shutdown: false, - encryption_key: None, + encryption_config: None, max_concurrent_requests: 128, } } diff --git a/libsql-server/src/connection/libsql.rs b/libsql-server/src/connection/libsql.rs index b498c71f33..497599e63c 100644 --- a/libsql-server/src/connection/libsql.rs +++ b/libsql-server/src/connection/libsql.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use libsql_sys::wal::wrapper::{WrapWal, WrappedWal}; use libsql_sys::wal::{BusyHandler, CheckpointCallback, Wal, WalManager}; +use libsql_sys::EncryptionConfig; use metrics::{histogram, increment_counter}; use once_cell::sync::Lazy; use parking_lot::{Mutex, RwLock}; @@ -44,7 +45,7 @@ pub struct MakeLibSqlConn { /// In wal mode, closing the last database takes time, and causes other databases creation to /// return sqlite busy. To mitigate that, we hold on to one connection _db: Option>, - encryption_key: Option, + encryption_config: Option, } impl MakeLibSqlConn @@ -63,7 +64,7 @@ where max_total_response_size: u64, auto_checkpoint: u32, current_frame_no_receiver: watch::Receiver>, - encryption_key: Option, + encryption_config: Option, ) -> Result { let mut this = Self { db_path, @@ -77,7 +78,7 @@ where _db: None, state: Default::default(), wal_manager, - encryption_key, + encryption_config, }; let db = this.try_create_db().await?; @@ -126,7 +127,7 @@ where max_size: Some(self.max_response_size), max_total_size: Some(self.max_total_response_size), auto_checkpoint: self.auto_checkpoint, - encryption_key: self.encryption_key.clone(), + encryption_config: self.encryption_config.clone(), }, self.current_frame_no_receiver.clone(), self.state.clone(), @@ -235,7 +236,7 @@ pub fn open_conn( path: &Path, wal_manager: T, flags: Option, - encryption_key: Option, + encryption_config: Option, ) -> Result>, rusqlite::Error> where T: WalManager, @@ -252,7 +253,7 @@ where flags, wal_manager.wrap(InhibitCheckpointWalWrapper::new(false)), u32::MAX, - encryption_key, + encryption_config, ) } @@ -262,7 +263,7 @@ pub fn open_conn_active_checkpoint( wal_manager: T, flags: Option, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, ) -> Result, rusqlite::Error> where T: WalManager, @@ -279,7 +280,7 @@ where flags, wal_manager, auto_checkpoint, - encryption_key, + encryption_config, ) } @@ -579,7 +580,7 @@ impl Connection { wal_manager, None, builder_config.auto_checkpoint, - builder_config.encryption_key.clone(), + builder_config.encryption_config.clone(), )?; // register the lock-stealing busy handler diff --git a/libsql-server/src/connection/write_proxy.rs b/libsql-server/src/connection/write_proxy.rs index c3de191c60..cec74d6764 100644 --- a/libsql-server/src/connection/write_proxy.rs +++ b/libsql-server/src/connection/write_proxy.rs @@ -9,6 +9,7 @@ use libsql_replication::rpc::proxy::{ }; use libsql_replication::rpc::replication::NAMESPACE_METADATA_KEY; use libsql_sys::wal::{Sqlite3Wal, Sqlite3WalManager}; +use libsql_sys::EncryptionConfig; use parking_lot::Mutex as PMutex; use tokio::sync::{mpsc, watch, Mutex}; use tokio_stream::StreamExt; @@ -44,7 +45,7 @@ pub struct MakeWriteProxyConn { namespace: NamespaceName, primary_replication_index: Option, make_read_only_conn: MakeLibSqlConn, - encryption_key: Option, + encryption_config: Option, } impl MakeWriteProxyConn { @@ -61,7 +62,7 @@ impl MakeWriteProxyConn { max_total_response_size: u64, namespace: NamespaceName, primary_replication_index: Option, - encryption_key: Option, + encryption_config: Option, ) -> crate::Result { let client = ProxyClient::with_origin(channel, uri); let make_read_only_conn = MakeLibSqlConn::new( @@ -74,7 +75,7 @@ impl MakeWriteProxyConn { max_total_response_size, DEFAULT_AUTO_CHECKPOINT, applied_frame_no_receiver.clone(), - encryption_key.clone(), + encryption_config.clone(), ) .await?; @@ -87,7 +88,7 @@ impl MakeWriteProxyConn { namespace, make_read_only_conn, primary_replication_index, - encryption_key, + encryption_config, }) } } @@ -104,7 +105,7 @@ impl MakeConnection for MakeWriteProxyConn { max_size: Some(self.max_response_size), max_total_size: Some(self.max_total_response_size), auto_checkpoint: DEFAULT_AUTO_CHECKPOINT, - encryption_key: self.encryption_key.clone(), + encryption_config: self.encryption_config.clone(), }, self.namespace.clone(), self.primary_replication_index, diff --git a/libsql-server/src/lib.rs b/libsql-server/src/lib.rs index 9d125ed748..93b70f0fe5 100644 --- a/libsql-server/src/lib.rs +++ b/libsql-server/src/lib.rs @@ -542,7 +542,7 @@ where max_response_size: self.db_config.max_response_size, max_total_response_size: self.db_config.max_total_response_size, checkpoint_interval: self.db_config.checkpoint_interval, - encryption_key: self.db_config.encryption_key.clone(), + encryption_config: self.db_config.encryption_config.clone(), max_concurrent_connections: Arc::new(Semaphore::new(self.max_concurrent_connections)), scripted_backup, max_concurrent_requests: self.db_config.max_concurrent_requests, @@ -657,7 +657,7 @@ impl Replica { base_path: self.base_path.clone(), max_response_size: self.db_config.max_response_size, max_total_response_size: self.db_config.max_total_response_size, - encryption_key: self.db_config.encryption_key.clone(), + encryption_config: self.db_config.encryption_config.clone(), max_concurrent_connections: Arc::new(Semaphore::new(self.max_concurrent_connections)), max_concurrent_requests: self.db_config.max_concurrent_requests, }; diff --git a/libsql-server/src/main.rs b/libsql-server/src/main.rs index 71930d4645..2ab3546989 100644 --- a/libsql-server/src/main.rs +++ b/libsql-server/src/main.rs @@ -23,6 +23,7 @@ use libsql_server::config::{ use libsql_server::net::AddrIncoming; use libsql_server::Server; use libsql_server::{connection::dump::exporter::export_dump, version::Version}; +use libsql_sys::{Cipher, EncryptionConfig}; // Use system allocator for now, seems like we are getting too much fragmentation. // #[global_allocator] @@ -288,7 +289,7 @@ impl Cli { eprintln!("\t- listening for HTTP requests on: {}", self.http_listen_addr); eprintln!("\t- grpc_tls: {}", if self.grpc_tls { "yes" } else { "no" }); #[cfg(feature = "encryption")] - eprintln!("\t- encryption at rest: {}", if self.encryption_key.is_some() { "enabled" } else { "disabled" }); + eprintln!("\t- encryption at rest: {}", if self.encryption_config.is_some() { "enabled" } else { "disabled" }); } } @@ -335,14 +336,18 @@ fn enable_libsql_logging() { } fn make_db_config(config: &Cli) -> anyhow::Result { + let encryption_config = config.encryption_key.as_ref().map(|key| EncryptionConfig { + cipher: Cipher::AES_256_CBC, + encryption_key: key.clone(), + }); let mut bottomless_replication = config .enable_bottomless_replication .then(bottomless::replicator::Options::from_env) .transpose()?; // Inherit encryption key for bottomless from the db config, if not specified. if let Some(ref mut bottomless_replication) = bottomless_replication { - if bottomless_replication.encryption_key.is_none() { - bottomless_replication.encryption_key = config.encryption_key.clone(); + if bottomless_replication.encryption_config.is_none() { + bottomless_replication.encryption_config = encryption_config.clone(); } } Ok(DbConfig { @@ -357,7 +362,7 @@ fn make_db_config(config: &Cli) -> anyhow::Result { snapshot_exec: config.snapshot_exec.clone(), checkpoint_interval: config.checkpoint_interval_s.map(Duration::from_secs), snapshot_at_shutdown: config.snapshot_at_shutdown, - encryption_key: config.encryption_key.clone(), + encryption_config: encryption_config.clone(), max_concurrent_requests: config.max_concurrent_requests, }) } diff --git a/libsql-server/src/namespace/meta_store.rs b/libsql-server/src/namespace/meta_store.rs index d966a5296a..79f7b805fa 100644 --- a/libsql-server/src/namespace/meta_store.rs +++ b/libsql-server/src/namespace/meta_store.rs @@ -170,7 +170,7 @@ impl MetaStore { create_bucket_if_not_exists: true, verify_crc: true, use_compression: CompressionKind::None, - encryption_key: None, + encryption_config: None, aws_endpoint: Some(config.bucket_endpoint), access_key_id: Some(config.access_key_id), secret_access_key: Some(config.secret_access_key), diff --git a/libsql-server/src/namespace/mod.rs b/libsql-server/src/namespace/mod.rs index a27550e17f..1238e0863f 100644 --- a/libsql-server/src/namespace/mod.rs +++ b/libsql-server/src/namespace/mod.rs @@ -18,6 +18,7 @@ use futures_core::{Future, Stream}; use hyper::Uri; use libsql_replication::rpc::replication::replication_log_client::ReplicationLogClient; use libsql_sys::wal::{Sqlite3WalManager, WalManager}; +use libsql_sys::EncryptionConfig; use moka::future::Cache; use parking_lot::Mutex; use rusqlite::ErrorCode; @@ -848,7 +849,7 @@ pub struct ReplicaNamespaceConfig { pub extensions: Arc<[PathBuf]>, /// Stats monitor pub stats_sender: StatsSender, - pub encryption_key: Option, + pub encryption_config: Option, pub max_concurrent_connections: Arc, pub max_concurrent_requests: u64, } @@ -878,7 +879,7 @@ impl Namespace { client, db_path.join("data"), DEFAULT_AUTO_CHECKPOINT, - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await?; @@ -954,7 +955,7 @@ impl Namespace { config.stats_sender.clone(), name.clone(), applied_frame_no_receiver.clone(), - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await?; @@ -970,7 +971,7 @@ impl Namespace { config.max_total_response_size, name.clone(), primary_current_replicatio_index, - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await? .throttled( @@ -1003,7 +1004,7 @@ pub struct PrimaryNamespaceConfig { pub(crate) max_response_size: u64, pub(crate) max_total_response_size: u64, pub(crate) checkpoint_interval: Option, - pub(crate) encryption_key: Option, + pub(crate) encryption_config: Option, pub(crate) max_concurrent_connections: Arc, pub(crate) scripted_backup: Option, pub(crate) max_concurrent_requests: u64, @@ -1125,7 +1126,7 @@ impl Namespace { auto_checkpoint, config.scripted_backup.clone(), name.clone(), - config.encryption_key.clone(), + config.encryption_config.clone(), )?); let stats = make_stats( @@ -1134,7 +1135,7 @@ impl Namespace { config.stats_sender.clone(), name.clone(), logger.new_frame_notifier.subscribe(), - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await?; @@ -1149,7 +1150,7 @@ impl Namespace { config.max_total_response_size, auto_checkpoint, logger.new_frame_notifier.subscribe(), - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await? .throttled( @@ -1172,7 +1173,7 @@ impl Namespace { &db_path, dump, wal_manager.clone(), - config.encryption_key.clone(), + config.encryption_config.clone(), ) .await?; } @@ -1207,7 +1208,7 @@ async fn make_stats( stats_sender: StatsSender, name: NamespaceName, mut current_frame_no: watch::Receiver>, - encryption_key: Option, + encryption_config: Option, ) -> anyhow::Result> { let stats = Stats::new(name.clone(), db_path, join_set).await?; @@ -1235,7 +1236,7 @@ async fn make_stats( join_set.spawn(run_storage_monitor( db_path.into(), Arc::downgrade(&stats), - encryption_key, + encryption_config, )); Ok(stats) @@ -1262,7 +1263,7 @@ async fn load_dump( db_path: &Path, dump: S, wal_manager: C, - encryption_key: Option, + encryption_config: Option, ) -> crate::Result<(), LoadDumpError> where S: Stream> + Unpin, @@ -1275,9 +1276,9 @@ where let db_path = db_path.to_path_buf(); let wal_manager = wal_manager.clone(); - let encryption_key = encryption_key.clone(); + let encryption_config = encryption_config.clone(); match tokio::task::spawn_blocking(move || { - open_conn(&db_path, wal_manager, None, encryption_key) + open_conn(&db_path, wal_manager, None, encryption_config) }) .await? { @@ -1427,7 +1428,7 @@ fn check_fresh_db(path: &Path) -> crate::Result { async fn run_storage_monitor( db_path: PathBuf, stats: Weak, - encryption_key: Option, + encryption_config: Option, ) -> anyhow::Result<()> { // on initialization, the database file doesn't exist yet, so we wait a bit for it to be // created @@ -1441,12 +1442,12 @@ async fn run_storage_monitor( return Ok(()); }; - let encryption_key = encryption_key.clone(); + let encryption_config = encryption_config.clone(); let _ = tokio::task::spawn_blocking(move || { // because closing the last connection interferes with opening a new one, we lazily // initialize a connection here, and keep it alive for the entirety of the program. If we // fail to open it, we wait for `duration` and try again later. - match open_conn(&db_path, Sqlite3WalManager::new(), Some(rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY), encryption_key) { + match open_conn(&db_path, Sqlite3WalManager::new(), Some(rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY), encryption_config) { Ok(conn) => { if let Ok(storage_bytes_used) = conn.query_row("select sum(pgsize) from dbstat;", [], |row| { diff --git a/libsql-server/src/query_result_builder.rs b/libsql-server/src/query_result_builder.rs index a580b2e79c..5effa3a8b2 100644 --- a/libsql-server/src/query_result_builder.rs +++ b/libsql-server/src/query_result_builder.rs @@ -3,6 +3,7 @@ use std::io::{self, ErrorKind}; use std::ops::{Deref, DerefMut}; use bytesize::ByteSize; +use libsql_sys::EncryptionConfig; use rusqlite::types::ValueRef; use serde::Serialize; use serde_json::ser::Formatter; @@ -87,7 +88,7 @@ pub struct QueryBuilderConfig { pub max_size: Option, pub max_total_size: Option, pub auto_checkpoint: u32, - pub encryption_key: Option, + pub encryption_config: Option, } pub trait QueryResultBuilder: Send + 'static { diff --git a/libsql-server/src/replication/primary/logger.rs b/libsql-server/src/replication/primary/logger.rs index aaa03e61c0..afc8baf39a 100644 --- a/libsql-server/src/replication/primary/logger.rs +++ b/libsql-server/src/replication/primary/logger.rs @@ -11,6 +11,7 @@ use bytes::{Bytes, BytesMut}; use chrono::{DateTime, Utc}; use libsql_replication::frame::{Frame, FrameBorrowed, FrameHeader, FrameMut}; use libsql_replication::snapshot::SnapshotFile; +use libsql_sys::EncryptionConfig; use once_cell::sync::Lazy; use parking_lot::{Mutex, RwLock}; use rusqlite::ffi::SQLITE_CHECKPOINT_TRUNCATE; @@ -553,7 +554,7 @@ impl ReplicationLogger { auto_checkpoint: u32, scripted_backup: Option, namespace: NamespaceName, - encryption_key: Option, + encryption_config: Option, ) -> anyhow::Result { let log_path = db_path.join("wallog"); let data_path = db_path.join("data"); @@ -567,7 +568,7 @@ impl ReplicationLogger { .open(log_path)?; let max_log_frame_count = max_log_size * 1_000_000 / LogFile::FRAME_SIZE as u64; - let encryption = encryption_key.clone().map(FrameEncryptor::new); + let encryption = encryption_config.clone().map(FrameEncryptor::new); let log_file = LogFile::new(file, max_log_frame_count, max_log_duration, encryption)?; let header = log_file.header(); @@ -596,7 +597,7 @@ impl ReplicationLogger { auto_checkpoint, scripted_backup, namespace, - encryption_key, + encryption_config, ) } else { Self::from_log_file( @@ -605,7 +606,7 @@ impl ReplicationLogger { auto_checkpoint, scripted_backup, namespace, - encryption_key, + encryption_config, ) } } @@ -616,7 +617,7 @@ impl ReplicationLogger { auto_checkpoint: u32, scripted_backup: Option, namespace: NamespaceName, - encryption_key: Option, + encryption_config: Option, ) -> anyhow::Result { let header = log_file.header(); let generation_start_frame_no = header.last_frame_no(); @@ -646,7 +647,7 @@ impl ReplicationLogger { let (closed_signal, _) = watch::channel(false); - let encryptor = encryption_key.map(FrameEncryptor::new); + let encryptor = encryption_config.map(FrameEncryptor::new); Ok(Self { generation: Generation::new(generation_start_frame_no.unwrap_or(0)), compactor: LogCompactor::new( @@ -672,7 +673,7 @@ impl ReplicationLogger { auto_checkpoint: u32, scripted_backup: Option, namespace: NamespaceName, - encryption_key: Option, + encryption_config: Option, ) -> anyhow::Result { // It is necessary to checkpoint before we restore the replication log, since the WAL may // contain pages that are not in the database file. @@ -719,7 +720,7 @@ impl ReplicationLogger { auto_checkpoint, scripted_backup, namespace, - encryption_key, + encryption_config, ) } diff --git a/libsql-server/src/test/bottomless.rs b/libsql-server/src/test/bottomless.rs index 6386161cec..e18b9c1526 100644 --- a/libsql-server/src/test/bottomless.rs +++ b/libsql-server/src/test/bottomless.rs @@ -92,7 +92,7 @@ async fn configure_server( snapshot_exec: None, checkpoint_interval: Some(Duration::from_secs(3)), snapshot_at_shutdown: false, - encryption_key: None, + encryption_config: None, max_concurrent_requests: 128, }, admin_api_config: None, diff --git a/libsql-sys/src/connection.rs b/libsql-sys/src/connection.rs index f5c3ab3155..47a40c176a 100644 --- a/libsql-sys/src/connection.rs +++ b/libsql-sys/src/connection.rs @@ -3,6 +3,10 @@ use std::path::Path; use crate::wal::{ffi::make_wal_manager, Wal, WalManager}; +pub type Cipher = libsql_ffi::Cipher; + +pub type EncryptionConfig = libsql_ffi::EncryptionConfig; + #[cfg(not(feature = "rusqlite"))] type RawConnection = *mut crate::ffi::sqlite3; #[cfg(feature = "rusqlite")] @@ -54,6 +58,11 @@ impl Connection { #[cfg(feature = "encryption")] extern "C" { + fn sqlite3mc_config( + db: *mut libsql_ffi::sqlite3, + cipher: *const std::ffi::c_void, + nKey: std::ffi::c_int, + ) -> std::ffi::c_int; fn sqlite3_key( db: *mut libsql_ffi::sqlite3, pKey: *const std::ffi::c_void, @@ -70,6 +79,13 @@ extern "C" { fn libsql_generate_aes256_key(user_password: *const u8, password_length: u32, digest: *mut u8); } +#[cfg(feature = "encryption")] +/// # Safety +/// db must point to a vaid sqlite database +pub unsafe fn set_encryption_cipher(db: *mut libsql_ffi::sqlite3, cipher_id: i32) -> i32 { + unsafe { sqlite3mc_config(db, "cipher".as_ptr() as _, cipher_id) as i32 } +} + #[cfg(feature = "encryption")] /// # Safety /// db must point to a vaid sqlite database @@ -114,7 +130,7 @@ impl Connection { flags: OpenFlags, wal_manager: T, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, ) -> Result where T: WalManager, @@ -141,7 +157,7 @@ impl Connection { ) }?; - if !cfg!(feature = "encryption") && encryption_key.is_some() { + if !cfg!(feature = "encryption") && encryption_config.is_some() { return Err(Error::SqliteFailure( rusqlite::ffi::Error::new(21), Some("encryption feature is not enabled, the database will not be encrypted on disk" @@ -149,8 +165,17 @@ impl Connection { )); } #[cfg(feature = "encryption")] - if let Some(encryption_key) = encryption_key { - if unsafe { set_encryption_key(conn.handle(), &encryption_key) } + if let Some(cfg) = encryption_config { + let cipher_id = cfg.cipher_id(); + if unsafe { set_encryption_cipher(conn.handle(), cipher_id) } + != rusqlite::ffi::SQLITE_OK + { + return Err(Error::SqliteFailure( + rusqlite::ffi::Error::new(21), + Some("failed to set encryption cipher".into()), + )); + }; + if unsafe { set_encryption_key(conn.handle(), &cfg.encryption_key) } != rusqlite::ffi::SQLITE_OK { return Err(Error::SqliteFailure( @@ -199,16 +224,20 @@ impl Connection { make_wal_manager(wal_manager), ); - if !cfg!(feature = "encryption") && encryption_key.is_some() { + if !cfg!(feature = "encryption") && encryption_config.is_some() { return Err(Error::Bug( "encryption feature is not enabled, the database will not be encrypted on disk", )); } #[cfg(feature = "encryption")] - if let Some(encryption_key) = encryption_key { - if set_encryption_key(conn, &encryption_key) != libsql_ffi::SQLITE_OK { + if let Some(cfg) = encryption_config { + let cipher_id = cfg.cipher_id(); + if set_encryption_cipher(conn, cipher_id) != libsql_ffi::SQLITE_OK { + return Err(Error::Bug("failed to set encryption cipher")); + } + if set_encryption_key(conn, &cfg.encryption_key) != libsql_ffi::SQLITE_OK { return Err(Error::Bug("failed to set encryption key")); - }; + } } if rc == 0 { diff --git a/libsql-sys/src/lib.rs b/libsql-sys/src/lib.rs index a5bde825d2..839fa5aded 100644 --- a/libsql-sys/src/lib.rs +++ b/libsql-sys/src/lib.rs @@ -74,9 +74,13 @@ pub mod value; #[cfg(feature = "wal")] pub mod wal; +#[cfg(feature = "api")] +pub use connection::Cipher; #[cfg(feature = "api")] pub use connection::Connection; #[cfg(feature = "api")] +pub use connection::EncryptionConfig; +#[cfg(feature = "api")] pub use error::{Error, Result}; #[cfg(feature = "api")] pub use statement::{prepare_stmt, Statement}; diff --git a/libsql/examples/replica.rs b/libsql/examples/replica.rs index 8a616ad8b5..1627ffbef0 100644 --- a/libsql/examples/replica.rs +++ b/libsql/examples/replica.rs @@ -1,4 +1,4 @@ -use libsql::{Builder, Value}; +use libsql::{Builder, Cipher, EncryptionConfig, Value}; use std::time::Duration; #[tokio::main] @@ -20,8 +20,12 @@ async fn main() { }) .replace("libsql", "https"); + let encryption_config = EncryptionConfig { + cipher: Cipher::AES_256_CBC, + encryption_key: "s3cr3t".into(), + }; let db = Builder::new_remote_replica(db_file.path(), url, auth_token) - .encryption_key("s3cr3t") + .encryption_config(encryption_config) .build() .await .unwrap(); diff --git a/libsql/src/database.rs b/libsql/src/database.rs index fffc74fe9f..5bf4fed82c 100644 --- a/libsql/src/database.rs +++ b/libsql/src/database.rs @@ -4,6 +4,8 @@ mod builder; pub use builder::Builder; +pub use libsql_sys::{Cipher, EncryptionConfig}; + use std::fmt; use crate::{Connection, Result}; @@ -36,12 +38,12 @@ enum DbType { File { path: String, flags: OpenFlags, - encryption_key: Option, + encryption_config: Option, }, #[cfg(feature = "replication")] Sync { db: crate::local::Database, - encryption_key: Option, + encryption_config: Option, }, #[cfg(feature = "remote")] Remote { @@ -98,7 +100,7 @@ cfg_core! { db_type: DbType::File { path: db_path.into(), flags, - encryption_key: None, + encryption_config: None, }, }) } @@ -115,16 +117,16 @@ cfg_replication! { #[deprecated = "Use the new `Builder` to construct `Database`"] pub async fn open_with_local_sync( db_path: impl Into, - encryption_key: Option + encryption_config: Option ) -> Result { let db = crate::local::Database::open_local_sync( db_path, OpenFlags::default(), - encryption_key.clone() + encryption_config.clone() ).await?; Ok(Database { - db_type: DbType::Sync { db, encryption_key }, + db_type: DbType::Sync { db, encryption_config }, }) } @@ -136,7 +138,7 @@ cfg_replication! { db_path: impl Into, endpoint: String, auth_token: String, - encryption_key: Option, + encryption_config: Option, ) -> Result { let https = connector(); @@ -145,7 +147,7 @@ cfg_replication! { endpoint, auth_token, https, - encryption_key + encryption_config ).await } @@ -157,7 +159,7 @@ cfg_replication! { endpoint: String, auth_token: String, connector: C, - encryption_key: Option, + encryption_config: Option, ) -> Result where C: tower::Service + Send + Clone + Sync + 'static, @@ -180,11 +182,11 @@ cfg_replication! { auth_token, None, OpenFlags::default(), - encryption_key.clone() + encryption_config.clone() ).await?; Ok(Database { - db_type: DbType::Sync { db, encryption_key }, + db_type: DbType::Sync { db, encryption_config }, }) } @@ -194,11 +196,11 @@ cfg_replication! { db_path: impl Into, url: impl Into, token: impl Into, - encryption_key: Option, + encryption_config: Option, ) -> Result { let https = connector(); - Self::open_with_remote_sync_connector(db_path, url, token, https, false, encryption_key).await + Self::open_with_remote_sync_connector(db_path, url, token, https, false, encryption_config).await } /// Open a local database file with the ability to sync from a remote database @@ -211,11 +213,11 @@ cfg_replication! { db_path: impl Into, url: impl Into, token: impl Into, - encryption_key: Option, + encryption_config: Option, ) -> Result { let https = connector(); - Self::open_with_remote_sync_connector(db_path, url, token, https, true, encryption_key).await + Self::open_with_remote_sync_connector(db_path, url, token, https, true, encryption_config).await } /// Connect an embedded replica to a remote primary with a custom @@ -227,7 +229,7 @@ cfg_replication! { token: impl Into, connector: C, read_your_writes: bool, - encryption_key: Option, + encryption_config: Option, ) -> Result where C: tower::Service + Send + Clone + Sync + 'static, @@ -242,7 +244,7 @@ cfg_replication! { connector, None, read_your_writes, - encryption_key, + encryption_config, None ).await } @@ -254,7 +256,7 @@ cfg_replication! { token: impl Into, version: Option, read_your_writes: bool, - encryption_key: Option, + encryption_config: Option, periodic_sync: Option, ) -> Result { let https = connector(); @@ -266,7 +268,7 @@ cfg_replication! { https, version, read_your_writes, - encryption_key, + encryption_config, periodic_sync ).await } @@ -279,7 +281,7 @@ cfg_replication! { connector: C, version: Option, read_your_writes: bool, - encryption_key: Option, + encryption_config: Option, periodic_sync: Option, ) -> Result where @@ -303,12 +305,12 @@ cfg_replication! { token.into(), version, read_your_writes, - encryption_key.clone(), + encryption_config.clone(), periodic_sync ).await?; Ok(Database { - db_type: DbType::Sync { db, encryption_key }, + db_type: DbType::Sync { db, encryption_config }, }) } @@ -316,7 +318,7 @@ cfg_replication! { /// Sync database from remote, and returns the committed frame_no after syncing, if /// applicable. pub async fn sync(&self) -> Result> { - if let DbType::Sync { db, encryption_key: _ } = &self.db_type { + if let DbType::Sync { db, encryption_config: _ } = &self.db_type { db.sync().await } else { Err(Error::SyncNotSupported(format!("{:?}", self.db_type))) @@ -326,7 +328,7 @@ cfg_replication! { /// Apply a set of frames to the database and returns the committed frame_no after syncing, if /// applicable. pub async fn sync_frames(&self, frames: crate::replication::Frames) -> Result> { - if let DbType::Sync { db, encryption_key: _ } = &self.db_type { + if let DbType::Sync { db, encryption_config: _ } = &self.db_type { db.sync_frames(frames).await } else { Err(Error::SyncNotSupported(format!("{:?}", self.db_type))) @@ -336,7 +338,7 @@ cfg_replication! { /// Force buffered replication frames to be applied, and return the current commit frame_no /// if applicable. pub async fn flush_replicator(&self) -> Result> { - if let DbType::Sync { db, encryption_key: _ } = &self.db_type { + if let DbType::Sync { db, encryption_config: _ } = &self.db_type { db.flush_replicator().await } else { Err(Error::SyncNotSupported(format!("{:?}", self.db_type))) @@ -345,7 +347,7 @@ cfg_replication! { /// Returns the database currently committed replication index pub async fn replication_index(&self) -> Result> { - if let DbType::Sync { db, encryption_key: _ } = &self.db_type { + if let DbType::Sync { db, encryption_config: _ } = &self.db_type { db.replication_index().await } else { Err(Error::SyncNotSupported(format!("{:?}", self.db_type))) @@ -364,7 +366,7 @@ cfg_replication! { DbType::Sync { db, .. } => { let path = db.path().to_string(); Ok(Database { - db_type: DbType::File { path, flags: OpenFlags::default(), encryption_key: None} + db_type: DbType::File { path, flags: OpenFlags::default(), encryption_config: None} }) } t => Err(Error::FreezeNotSupported(format!("{:?}", t))) @@ -471,24 +473,31 @@ impl Database { DbType::File { path, flags, - encryption_key, + encryption_config, } => { use crate::local::impls::LibsqlConnection; let db = crate::local::Database::open(path, *flags)?; let conn = db.connect()?; - if !cfg!(feature = "encryption") && encryption_key.is_some() { + if !cfg!(feature = "encryption") && encryption_config.is_some() { return Err(crate::Error::Misuse( "Encryption is not enabled: enable the `encryption` feature in order to enable encryption-at-rest".to_string(), )); } #[cfg(feature = "encryption")] - if let Some(encryption_key) = encryption_key { - if unsafe { - libsql_sys::connection::set_encryption_key(conn.raw, encryption_key) - } != crate::ffi::SQLITE_OK + if let Some(cfg) = encryption_config { + let cipher_id = cfg.cipher_id(); + if unsafe { libsql_sys::connection::set_encryption_cipher(conn.raw, cipher_id) } + != crate::ffi::SQLITE_OK + { + return Err(crate::Error::Misuse( + "failed to set encryption cipher".to_string(), + )); + } + if unsafe { libsql_sys::connection::set_encryption_key(conn.raw, cfg) } + != crate::ffi::SQLITE_OK { return Err(crate::Error::Misuse( "failed to set encryption key".to_string(), @@ -502,20 +511,23 @@ impl Database { } #[cfg(feature = "replication")] - DbType::Sync { db, encryption_key } => { + DbType::Sync { + db, + encryption_config, + } => { use crate::local::impls::LibsqlConnection; let conn = db.connect()?; - if !cfg!(feature = "encryption") && encryption_key.is_some() { + if !cfg!(feature = "encryption") && encryption_config.is_some() { return Err(crate::Error::Misuse( "Encryption is not enabled: enable the `encryption` feature in order to enable encryption-at-rest".to_string(), )); } #[cfg(feature = "encryption")] - if let Some(encryption_key) = encryption_key { + if let Some(encryption_config) = encryption_config { if unsafe { - libsql_sys::connection::set_encryption_key(conn.raw, encryption_key) + libsql_sys::connection::set_encryption_key(conn.raw, encryption_config) } != crate::ffi::SQLITE_OK { return Err(crate::Error::Misuse( diff --git a/libsql/src/database/builder.rs b/libsql/src/database/builder.rs index ed24fbc1d2..b45ce28976 100644 --- a/libsql/src/database/builder.rs +++ b/libsql/src/database/builder.rs @@ -1,4 +1,4 @@ -use crate::{Database, Result}; +use crate::{Database, EncryptionConfig, Result}; use super::DbType; @@ -26,7 +26,7 @@ impl Builder<()> { inner: Local { path: path.as_ref().to_path_buf(), flags: crate::OpenFlags::default(), - encryption_key: None, + encryption_config: None, }, } } @@ -48,7 +48,7 @@ impl Builder<()> { connector: None, version: None, }, - encryption_key: None, + encryption_config: None, read_your_writes: false, periodic_sync: None }, @@ -62,7 +62,7 @@ impl Builder<()> { path: path.as_ref().to_path_buf(), flags: crate::OpenFlags::default(), remote: None, - encryption_key: None, + encryption_config: None, }, } } @@ -98,7 +98,7 @@ cfg_core! { pub struct Local { path: std::path::PathBuf, flags: crate::OpenFlags, - encryption_key: Option, + encryption_config: Option, } impl Builder { @@ -108,12 +108,12 @@ cfg_core! { self } - /// Set an encryption key that will encrypt the local database. - pub fn encryption_key( + /// Set an encryption config that will encrypt the local database. + pub fn encryption_config( mut self, - encryption_key: impl Into, + encryption_config: EncryptionConfig, ) -> Builder { - self.inner.encryption_key = Some(encryption_key.into()); + self.inner.encryption_config = Some(encryption_config); self } @@ -135,7 +135,7 @@ cfg_core! { db_type: DbType::File { path, flags: self.inner.flags, - encryption_key: self.inner.encryption_key, + encryption_config: self.inner.encryption_config, }, } }; @@ -150,7 +150,7 @@ cfg_replication! { pub struct RemoteReplica { path: std::path::PathBuf, remote: Remote, - encryption_key: Option, + encryption_config: Option, read_your_writes: bool, periodic_sync: Option, } @@ -160,7 +160,7 @@ cfg_replication! { path: std::path::PathBuf, flags: crate::OpenFlags, remote: Option, - encryption_key: Option, + encryption_config: Option, } impl Builder { @@ -177,11 +177,11 @@ cfg_replication! { } /// Set an encryption key that will encrypt the local database. - pub fn encryption_key( + pub fn encryption_config( mut self, - encryption_key: impl Into, + encryption_config: EncryptionConfig, ) -> Builder { - self.inner.encryption_key = Some(encryption_key.into()); + self.inner.encryption_config = Some(encryption_config.into()); self } @@ -217,7 +217,7 @@ cfg_replication! { connector, version, }, - encryption_key, + encryption_config, read_your_writes, periodic_sync } = self.inner; @@ -244,13 +244,13 @@ cfg_replication! { auth_token, version, read_your_writes, - encryption_key.clone(), + encryption_config.clone(), periodic_sync ) .await?; Ok(Database { - db_type: DbType::Sync { db, encryption_key }, + db_type: DbType::Sync { db, encryption_config }, }) } } @@ -268,7 +268,7 @@ cfg_replication! { path, flags, remote, - encryption_key, + encryption_config, } = self.inner; let path = path.to_str().ok_or(crate::Error::InvalidUTF8Path)?.to_owned(); @@ -300,15 +300,15 @@ cfg_replication! { auth_token, version, flags, - encryption_key.clone(), + encryption_config.clone(), ) .await? } else { - crate::local::Database::open_local_sync(path, flags, encryption_key.clone()).await? + crate::local::Database::open_local_sync(path, flags, encryption_config.clone()).await? }; Ok(Database { - db_type: DbType::Sync { db, encryption_key }, + db_type: DbType::Sync { db, encryption_config }, }) } } diff --git a/libsql/src/lib.rs b/libsql/src/lib.rs index fae9dce5a7..2eaee96387 100644 --- a/libsql/src/lib.rs +++ b/libsql/src/lib.rs @@ -145,7 +145,7 @@ cfg_hrana! { pub use self::{ connection::Connection, - database::{Builder, Database}, + database::{Builder, Cipher, Database, EncryptionConfig}, rows::{Column, Row, Rows}, statement::Statement, transaction::{Transaction, TransactionBehavior}, diff --git a/libsql/src/local/database.rs b/libsql/src/local/database.rs index 1f3f002ee3..125af1b738 100644 --- a/libsql/src/local/database.rs +++ b/libsql/src/local/database.rs @@ -16,7 +16,7 @@ cfg_replication!( } ); -use crate::{database::OpenFlags, local::connection::Connection}; +use crate::{database::EncryptionConfig, database::OpenFlags, local::connection::Connection}; use crate::{Error::ConnectionFailed, Result}; use libsql_sys::ffi; @@ -51,7 +51,7 @@ impl Database { db_path: String, endpoint: String, auth_token: String, - encryption_key: Option, + encryption_config: Option, periodic_sync: Option, ) -> Result { Self::open_http_sync_internal( @@ -61,7 +61,7 @@ impl Database { auth_token, None, false, - encryption_key, + encryption_config, periodic_sync, ) .await @@ -76,7 +76,7 @@ impl Database { auth_token: String, version: Option, read_your_writes: bool, - encryption_key: Option, + encryption_config: Option, periodic_sync: Option, ) -> Result { use std::path::PathBuf; @@ -99,7 +99,7 @@ impl Database { .map_err(|e| crate::errors::Error::ConnectionFailed(e.to_string()))?; let replicator = - EmbeddedReplicator::with_remote(client, path, 1000, encryption_key, periodic_sync) + EmbeddedReplicator::with_remote(client, path, 1000, encryption_config, periodic_sync) .await; db.replication_ctx = Some(ReplicationContext { @@ -115,7 +115,7 @@ impl Database { pub async fn open_local_sync( db_path: impl Into, flags: OpenFlags, - encryption_key: Option, + encryption_config: Option, ) -> Result { use std::path::PathBuf; @@ -125,7 +125,7 @@ impl Database { let path = PathBuf::from(db_path); let client = LocalClient::new(&path).await.unwrap(); - let replicator = EmbeddedReplicator::with_local(client, path, 1000, encryption_key).await; + let replicator = EmbeddedReplicator::with_local(client, path, 1000, encryption_config).await; db.replication_ctx = Some(ReplicationContext { replicator, @@ -144,7 +144,7 @@ impl Database { auth_token: String, version: Option, flags: OpenFlags, - encryption_key: Option, + encryption_config: Option, ) -> Result { use std::path::PathBuf; @@ -165,7 +165,7 @@ impl Database { let path = PathBuf::from(db_path); let client = LocalClient::new(&path).await.unwrap(); - let replicator = EmbeddedReplicator::with_local(client, path, 1000, encryption_key).await; + let replicator = EmbeddedReplicator::with_local(client, path, 1000, encryption_config).await; db.replication_ctx = Some(ReplicationContext { replicator, diff --git a/libsql/src/replication/mod.rs b/libsql/src/replication/mod.rs index 4d4916b692..14aeeba4d8 100644 --- a/libsql/src/replication/mod.rs +++ b/libsql/src/replication/mod.rs @@ -16,6 +16,7 @@ use tokio::sync::Mutex; use tokio::task::AbortHandle; use tracing::Instrument; +use crate::database::EncryptionConfig; use crate::parser::Statement; use crate::Result; @@ -106,7 +107,7 @@ impl EmbeddedReplicator { client: RemoteClient, db_path: PathBuf, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, perodic_sync: Option, ) -> Self { let replicator = Arc::new(Mutex::new( @@ -114,7 +115,7 @@ impl EmbeddedReplicator { Either::Left(client), db_path, auto_checkpoint, - encryption_key, + encryption_config, ) .await .unwrap(), @@ -151,14 +152,14 @@ impl EmbeddedReplicator { client: LocalClient, db_path: PathBuf, auto_checkpoint: u32, - encryption_key: Option, + encryption_config: Option, ) -> Self { let replicator = Arc::new(Mutex::new( Replicator::new( Either::Right(client), db_path, auto_checkpoint, - encryption_key, + encryption_config, ) .await .unwrap(),