Skip to content

Commit 3c42428

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

File tree

9 files changed

+113
-51
lines changed

9 files changed

+113
-51
lines changed

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: 43 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,8 @@ impl Builder<()> {
6463
connector: None,
6564
version: None,
6665
namespace: None,
66+
#[cfg(any(feature = "remote", feature = "sync"))]
67+
remote_encryption: None,
6768
},
6869
encryption_config: None,
6970
read_your_writes: true,
@@ -73,7 +74,7 @@ impl Builder<()> {
7374
#[cfg(feature = "sync")]
7475
sync_protocol: Default::default(),
7576
#[cfg(feature = "sync")]
76-
remote_encryption,
77+
remote_encryption: None
7778
},
7879
}
7980
}
@@ -98,7 +99,6 @@ impl Builder<()> {
9899
path: impl AsRef<std::path::Path>,
99100
url: String,
100101
auth_token: String,
101-
remote_encryption: Option<EncryptionContext>,
102102
) -> Builder<SyncedDatabase> {
103103
Builder {
104104
inner: SyncedDatabase {
@@ -110,13 +110,14 @@ impl Builder<()> {
110110
connector: None,
111111
version: None,
112112
namespace: None,
113+
remote_encryption: None,
113114
},
114115
connector: None,
115116
read_your_writes: true,
116117
remote_writes: false,
117118
push_batch_size: 0,
118119
sync_interval: None,
119-
remote_encryption,
120+
remote_encryption: None,
120121
},
121122
}
122123
}
@@ -132,6 +133,7 @@ impl Builder<()> {
132133
connector: None,
133134
version: None,
134135
namespace: None,
136+
remote_encryption: None,
135137
},
136138
}
137139
}
@@ -146,6 +148,8 @@ cfg_replication_or_remote_or_sync! {
146148
connector: Option<crate::util::ConnectorService>,
147149
version: Option<String>,
148150
namespace: Option<String>,
151+
#[cfg(any(feature = "remote", feature = "sync"))]
152+
remote_encryption: Option<EncryptionContext>,
149153
}
150154
}
151155

@@ -238,7 +242,7 @@ cfg_replication! {
238242
#[cfg(feature = "sync")]
239243
sync_protocol: super::SyncProtocol,
240244
#[cfg(feature = "sync")]
241-
remote_encryption: Option<crate::sync::EncryptionContext>,
245+
remote_encryption: Option<EncryptionContext>,
242246
}
243247

244248
/// Local replica configuration type in [`Builder`].
@@ -300,6 +304,13 @@ cfg_replication! {
300304
self
301305
}
302306

307+
/// Set the encryption context if the database is encrypted in remote server.
308+
#[cfg(feature = "sync")]
309+
pub fn remote_encryption(mut self, encryption_context: Option<EncryptionContext>) -> Builder<RemoteReplica> {
310+
self.inner.remote_encryption = encryption_context;
311+
self
312+
}
313+
303314
pub fn http_request_callback<F>(mut self, f: F) -> Builder<RemoteReplica>
304315
where
305316
F: Fn(&mut http::Request<()>) + Send + Sync + 'static
@@ -347,6 +358,7 @@ cfg_replication! {
347358
connector,
348359
version,
349360
namespace,
361+
..
350362
},
351363
encryption_config,
352364
read_your_writes,
@@ -415,10 +427,11 @@ cfg_replication! {
415427

416428
if res.status().is_success() {
417429
tracing::trace!("Using sync protocol v2 for {}", url);
418-
let builder = Builder::new_synced_database(path, url, auth_token, remote_encryption)
430+
let builder = Builder::new_synced_database(path, url, auth_token)
419431
.connector(connector)
420432
.remote_writes(true)
421-
.read_your_writes(read_your_writes);
433+
.read_your_writes(read_your_writes)
434+
.remote_encryption(remote_encryption);
422435

423436
let builder = if let Some(sync_interval) = sync_interval {
424437
builder.sync_interval(sync_interval)
@@ -475,7 +488,10 @@ cfg_replication! {
475488

476489

477490
Ok(Database {
478-
db_type: DbType::Sync { db, encryption_config },
491+
db_type: DbType::Sync {
492+
db,
493+
encryption_config,
494+
},
479495
max_write_replication_index: Default::default(),
480496
})
481497
}
@@ -515,6 +531,7 @@ cfg_replication! {
515531
connector,
516532
version,
517533
namespace,
534+
..
518535
}) = remote
519536
{
520537
let connector = if let Some(connector) = connector {
@@ -565,7 +582,7 @@ cfg_sync! {
565582
read_your_writes: bool,
566583
push_batch_size: u32,
567584
sync_interval: Option<std::time::Duration>,
568-
remote_encryption: Option<crate::sync::EncryptionContext>,
585+
remote_encryption: Option<EncryptionContext>,
569586
}
570587

571588
impl Builder<SyncedDatabase> {
@@ -598,6 +615,12 @@ cfg_sync! {
598615
self
599616
}
600617

618+
/// Set the encryption context if the database is encrypted in remote server.
619+
pub fn remote_encryption(mut self, encryption_context: Option<EncryptionContext>) -> Builder<SyncedDatabase> {
620+
self.inner.remote_encryption = encryption_context;
621+
self
622+
}
623+
601624
/// Provide a custom http connector that will be used to create http connections.
602625
pub fn connector<C>(mut self, connector: C) -> Builder<SyncedDatabase>
603626
where
@@ -624,6 +647,7 @@ cfg_sync! {
624647
connector: _,
625648
version: _,
626649
namespace: _,
650+
..
627651
},
628652
connector,
629653
remote_writes,
@@ -759,6 +783,12 @@ cfg_remote! {
759783
self
760784
}
761785

786+
/// Set the encryption context if the database is encrypted in remote server.
787+
pub fn remote_encryption(mut self, encryption_context: Option<EncryptionContext>) -> Builder<Remote> {
788+
self.inner.remote_encryption = encryption_context;
789+
self
790+
}
791+
762792
/// Build the remote database client.
763793
pub async fn build(self) -> Result<Database> {
764794
let Remote {
@@ -767,6 +797,7 @@ cfg_remote! {
767797
connector,
768798
version,
769799
namespace,
800+
remote_encryption,
770801
} = self.inner;
771802

772803
let connector = if let Some(connector) = connector {
@@ -789,6 +820,7 @@ cfg_remote! {
789820
connector,
790821
version,
791822
namespace,
823+
remote_encryption
792824
},
793825
max_write_replication_index: Default::default(),
794826
})

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)