Skip to content

Commit b91dbed

Browse files
committed
enable remote encryption support for remote and synced databases
1 parent 5da95cc commit b91dbed

9 files changed

Lines changed: 111 additions & 51 deletions

File tree

libsql/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ sync = [
102102
"stream",
103103
"remote",
104104
"replication",
105+
"dep:base64",
105106
"dep:tower",
106107
"dep:hyper",
107108
"dep:http",
@@ -131,6 +132,7 @@ hrana = [
131132
serde = ["dep:serde"]
132133
remote = [
133134
"hrana",
135+
"dep:base64",
134136
"dep:tower",
135137
"dep:hyper",
136138
"dep:hyper",

libsql/examples/encryption_sync.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Example of using offline writes with encryption
22

3-
use libsql::{params, Builder, EncryptionContext, EncryptionKey};
3+
use libsql::{params, Builder};
4+
use libsql::{EncryptionContext, EncryptionKey};
45

56
#[tokio::main]
67
async fn main() {
@@ -24,7 +25,8 @@ async fn main() {
2425
None
2526
};
2627

27-
let db_builder = Builder::new_synced_database(db_path, sync_url, auth_token, encryption);
28+
let db_builder =
29+
Builder::new_synced_database(db_path, sync_url, auth_token).remote_encryption(encryption);
2830

2931
let db = match db_builder.build().await {
3032
Ok(db) => db,

libsql/src/database.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub use builder::Builder;
88
pub use libsql_sys::{Cipher, EncryptionConfig};
99

1010
use crate::{Connection, Result};
11+
#[cfg(any(feature = "remote", feature = "sync"))]
12+
use base64::{engine::general_purpose, Engine};
1113
use std::fmt;
1214
use std::sync::atomic::AtomicU64;
1315

@@ -100,7 +102,7 @@ enum DbType {
100102
auth_token: String,
101103
connector: crate::util::ConnectorService,
102104
_bg_abort: Option<std::sync::Arc<crate::sync::DropAbort>>,
103-
remote_encryption: Option<crate::sync::EncryptionContext>,
105+
remote_encryption: Option<EncryptionContext>,
104106
},
105107
#[cfg(feature = "remote")]
106108
Remote {
@@ -109,6 +111,7 @@ enum DbType {
109111
connector: crate::util::ConnectorService,
110112
version: Option<String>,
111113
namespace: Option<String>,
114+
remote_encryption: Option<EncryptionContext>,
112115
},
113116
}
114117

@@ -545,6 +548,7 @@ cfg_remote! {
545548
connector: crate::util::ConnectorService::new(svc),
546549
version,
547550
namespace: None,
551+
remote_encryption: None
548552
},
549553
max_write_replication_index: Default::default(),
550554
})
@@ -733,6 +737,7 @@ impl Database {
733737
connector,
734738
version,
735739
namespace,
740+
remote_encryption,
736741
} => {
737742
let conn = std::sync::Arc::new(
738743
crate::hrana::connection::HttpConnection::new_with_connector(
@@ -741,7 +746,7 @@ impl Database {
741746
connector.clone(),
742747
version.as_ref().map(|s| s.as_str()),
743748
namespace.as_ref().map(|s| s.as_str()),
744-
None,
749+
remote_encryption.clone(),
745750
),
746751
);
747752

@@ -785,3 +790,29 @@ impl std::fmt::Debug for Database {
785790
f.debug_struct("Database").finish()
786791
}
787792
}
793+
794+
#[cfg(any(feature = "remote", feature = "sync"))]
795+
#[derive(Debug, Clone)]
796+
pub enum EncryptionKey {
797+
/// The key is a base64-encoded string.
798+
Base64Encoded(String),
799+
/// The key is a byte array.
800+
Bytes(Vec<u8>),
801+
}
802+
803+
#[cfg(any(feature = "remote", feature = "sync"))]
804+
impl EncryptionKey {
805+
pub fn as_string(&self) -> String {
806+
match self {
807+
EncryptionKey::Base64Encoded(s) => s.clone(),
808+
EncryptionKey::Bytes(b) => general_purpose::STANDARD.encode(b),
809+
}
810+
}
811+
}
812+
813+
#[cfg(any(feature = "remote", feature = "sync"))]
814+
#[derive(Debug, Clone)]
815+
pub struct EncryptionContext {
816+
/// The base64-encoded key for the encryption, sent on every request.
817+
pub key: EncryptionKey,
818+
}

libsql/src/database/builder.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ cfg_core! {
55
use super::DbType;
66
use crate::{Database, Result};
77

8-
pub use crate::sync::EncryptionContext;
8+
#[cfg(any(feature = "remote", feature = "sync"))]
9+
pub use crate::database::EncryptionContext;
910

1011
/// A builder for [`Database`]. This struct can be used to build
1112
/// all variants of [`Database`]. These variants include:
@@ -52,8 +53,6 @@ impl Builder<()> {
5253
path: impl AsRef<std::path::Path>,
5354
url: String,
5455
auth_token: String,
55-
#[cfg(feature = "sync")]
56-
remote_encryption: Option<crate::sync::EncryptionContext>,
5756
) -> Builder<RemoteReplica> {
5857
Builder {
5958
inner: RemoteReplica {
@@ -64,6 +63,7 @@ impl Builder<()> {
6463
connector: None,
6564
version: None,
6665
namespace: None,
66+
remote_encryption: None
6767
},
6868
encryption_config: None,
6969
read_your_writes: true,
@@ -73,7 +73,7 @@ impl Builder<()> {
7373
#[cfg(feature = "sync")]
7474
sync_protocol: Default::default(),
7575
#[cfg(feature = "sync")]
76-
remote_encryption,
76+
remote_encryption: None
7777
},
7878
}
7979
}
@@ -98,7 +98,6 @@ impl Builder<()> {
9898
path: impl AsRef<std::path::Path>,
9999
url: String,
100100
auth_token: String,
101-
remote_encryption: Option<EncryptionContext>,
102101
) -> Builder<SyncedDatabase> {
103102
Builder {
104103
inner: SyncedDatabase {
@@ -110,13 +109,14 @@ impl Builder<()> {
110109
connector: None,
111110
version: None,
112111
namespace: None,
112+
remote_encryption: None,
113113
},
114114
connector: None,
115115
read_your_writes: true,
116116
remote_writes: false,
117117
push_batch_size: 0,
118118
sync_interval: None,
119-
remote_encryption,
119+
remote_encryption: None,
120120
},
121121
}
122122
}
@@ -132,6 +132,7 @@ impl Builder<()> {
132132
connector: None,
133133
version: None,
134134
namespace: None,
135+
remote_encryption: None,
135136
},
136137
}
137138
}
@@ -146,6 +147,7 @@ cfg_replication_or_remote_or_sync! {
146147
connector: Option<crate::util::ConnectorService>,
147148
version: Option<String>,
148149
namespace: Option<String>,
150+
remote_encryption: Option<EncryptionContext>,
149151
}
150152
}
151153

@@ -238,7 +240,7 @@ cfg_replication! {
238240
#[cfg(feature = "sync")]
239241
sync_protocol: super::SyncProtocol,
240242
#[cfg(feature = "sync")]
241-
remote_encryption: Option<crate::sync::EncryptionContext>,
243+
remote_encryption: Option<EncryptionContext>,
242244
}
243245

244246
/// Local replica configuration type in [`Builder`].
@@ -300,6 +302,13 @@ cfg_replication! {
300302
self
301303
}
302304

305+
/// Set the encryption context if the database is encrypted in remote server.
306+
#[cfg(feature = "sync")]
307+
pub fn remote_encryption(mut self, encryption_context: EncryptionContext) -> Builder<RemoteReplica> {
308+
self.inner.remote_encryption = Some(encryption_context);
309+
self
310+
}
311+
303312
pub fn http_request_callback<F>(mut self, f: F) -> Builder<RemoteReplica>
304313
where
305314
F: Fn(&mut http::Request<()>) + Send + Sync + 'static
@@ -347,6 +356,7 @@ cfg_replication! {
347356
connector,
348357
version,
349358
namespace,
359+
..
350360
},
351361
encryption_config,
352362
read_your_writes,
@@ -415,10 +425,11 @@ cfg_replication! {
415425

416426
if res.status().is_success() {
417427
tracing::trace!("Using sync protocol v2 for {}", url);
418-
let builder = Builder::new_synced_database(path, url, auth_token, remote_encryption)
428+
let builder = Builder::new_synced_database(path, url, auth_token)
419429
.connector(connector)
420430
.remote_writes(true)
421-
.read_your_writes(read_your_writes);
431+
.read_your_writes(read_your_writes)
432+
.remote_encryption(remote_encryption);
422433

423434
let builder = if let Some(sync_interval) = sync_interval {
424435
builder.sync_interval(sync_interval)
@@ -475,7 +486,10 @@ cfg_replication! {
475486

476487

477488
Ok(Database {
478-
db_type: DbType::Sync { db, encryption_config },
489+
db_type: DbType::Sync {
490+
db,
491+
encryption_config,
492+
},
479493
max_write_replication_index: Default::default(),
480494
})
481495
}
@@ -515,6 +529,7 @@ cfg_replication! {
515529
connector,
516530
version,
517531
namespace,
532+
..
518533
}) = remote
519534
{
520535
let connector = if let Some(connector) = connector {
@@ -565,7 +580,7 @@ cfg_sync! {
565580
read_your_writes: bool,
566581
push_batch_size: u32,
567582
sync_interval: Option<std::time::Duration>,
568-
remote_encryption: Option<crate::sync::EncryptionContext>,
583+
remote_encryption: Option<EncryptionContext>,
569584
}
570585

571586
impl Builder<SyncedDatabase> {
@@ -598,6 +613,12 @@ cfg_sync! {
598613
self
599614
}
600615

616+
/// Set the encryption context if the database is encrypted in remote server.
617+
pub fn remote_encryption(mut self, encryption_context: Option<EncryptionContext>) -> Builder<SyncedDatabase> {
618+
self.inner.remote_encryption = encryption_context;
619+
self
620+
}
621+
601622
/// Provide a custom http connector that will be used to create http connections.
602623
pub fn connector<C>(mut self, connector: C) -> Builder<SyncedDatabase>
603624
where
@@ -624,6 +645,7 @@ cfg_sync! {
624645
connector: _,
625646
version: _,
626647
namespace: _,
648+
..
627649
},
628650
connector,
629651
remote_writes,
@@ -759,6 +781,12 @@ cfg_remote! {
759781
self
760782
}
761783

784+
/// Set the encryption context if the database is encrypted in remote server.
785+
pub fn remote_encryption(mut self, encryption_context: EncryptionContext) -> Builder<Remote> {
786+
self.inner.remote_encryption = Some(encryption_context);
787+
self
788+
}
789+
762790
/// Build the remote database client.
763791
pub async fn build(self) -> Result<Database> {
764792
let Remote {
@@ -767,6 +795,7 @@ cfg_remote! {
767795
connector,
768796
version,
769797
namespace,
798+
remote_encryption,
770799
} = self.inner;
771800

772801
let connector = if let Some(connector) = connector {
@@ -789,6 +818,7 @@ cfg_remote! {
789818
connector,
790819
version,
791820
namespace,
821+
remote_encryption
792822
},
793823
max_write_replication_index: Default::default(),
794824
})

libsql/src/hrana/hyper.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ pub struct HttpSender {
2727
inner: hyper::Client<ConnectorService, hyper::Body>,
2828
version: HeaderValue,
2929
namespace: Option<HeaderValue>,
30-
#[cfg(feature = "sync")]
31-
remote_encryption: Option<crate::sync::EncryptionContext>,
30+
#[cfg(any(feature = "remote", feature = "sync"))]
31+
remote_encryption: Option<crate::database::EncryptionContext>,
3232
}
3333

3434
impl HttpSender {
3535
pub fn new(
3636
connector: ConnectorService,
3737
version: Option<&str>,
3838
namespace: Option<&str>,
39-
#[cfg(feature = "sync")] remote_encryption: Option<crate::sync::EncryptionContext>,
39+
#[cfg(any(feature = "remote", feature = "sync"))] remote_encryption: Option<
40+
crate::database::EncryptionContext,
41+
>,
4042
) -> Self {
4143
let ver = version.unwrap_or(env!("CARGO_PKG_VERSION"));
4244

@@ -48,7 +50,7 @@ impl HttpSender {
4850
inner,
4951
version,
5052
namespace,
51-
#[cfg(feature = "sync")]
53+
#[cfg(any(feature = "remote", feature = "sync"))]
5254
remote_encryption,
5355
}
5456
}
@@ -67,6 +69,7 @@ impl HttpSender {
6769
req_builder = req_builder.header("x-namespace", namespace);
6870
}
6971

72+
#[cfg(any(feature = "remote", feature = "sync"))]
7073
if let Some(remote_encryption) = &self.remote_encryption {
7174
req_builder =
7275
req_builder.header("x-turso-encryption-key", remote_encryption.key.as_string());
@@ -135,13 +138,15 @@ impl HttpConnection<HttpSender> {
135138
connector: ConnectorService,
136139
version: Option<&str>,
137140
namespace: Option<&str>,
138-
#[cfg(feature = "sync")] remote_encryption: Option<crate::sync::EncryptionContext>,
141+
#[cfg(any(feature = "remote", feature = "sync"))] remote_encryption: Option<
142+
crate::database::EncryptionContext,
143+
>,
139144
) -> Self {
140145
let inner = HttpSender::new(
141146
connector,
142147
version,
143148
namespace,
144-
#[cfg(feature = "sync")]
149+
#[cfg(any(feature = "remote", feature = "sync"))]
145150
remote_encryption,
146151
);
147152
Self::new(url.into(), token.into(), inner)

libsql/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ pub mod params;
132132
cfg_sync! {
133133
mod sync;
134134
pub use database::SyncProtocol;
135-
pub use sync::EncryptionContext;
136-
pub use sync::EncryptionKey;
135+
pub use database::EncryptionContext;
136+
pub use database::EncryptionKey;
137137
}
138138

139139
cfg_replication! {

0 commit comments

Comments
 (0)