Skip to content

Commit b07ae02

Browse files
jussisaurioavinassh
authored andcommitted
remote encryption poc
1 parent cf0fa74 commit b07ae02

File tree

6 files changed

+101
-6
lines changed

6 files changed

+101
-6
lines changed

libsql/src/database.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ enum DbType {
100100
auth_token: String,
101101
connector: crate::util::ConnectorService,
102102
_bg_abort: Option<std::sync::Arc<crate::sync::DropAbort>>,
103+
remote_encryption: Option<crate::sync::EncryptionContext>,
103104
},
104105
#[cfg(feature = "remote")]
105106
Remote {
@@ -214,7 +215,7 @@ cfg_replication! {
214215
endpoint,
215216
auth_token,
216217
https,
217-
encryption_config
218+
encryption_config,
218219
).await
219220
}
220221

@@ -524,7 +525,7 @@ cfg_remote! {
524525
url: impl Into<String>,
525526
auth_token: impl Into<String>,
526527
connector: C,
527-
version: Option<String>
528+
version: Option<String>,
528529
) -> Result<Self>
529530
where
530531
C: tower::Service<http::Uri> + Send + Clone + Sync + 'static,
@@ -677,6 +678,7 @@ impl Database {
677678
url,
678679
auth_token,
679680
connector,
681+
remote_encryption,
680682
..
681683
} => {
682684
use crate::{
@@ -708,6 +710,7 @@ impl Database {
708710
connector.clone(),
709711
None,
710712
None,
713+
remote_encryption.clone()
711714
),
712715
read_your_writes: *read_your_writes,
713716
context: db.sync_ctx.clone().unwrap(),
@@ -738,6 +741,7 @@ impl Database {
738741
connector.clone(),
739742
version.as_ref().map(|s| s.as_str()),
740743
namespace.as_ref().map(|s| s.as_str()),
744+
None,
741745
),
742746
);
743747

libsql/src/database/builder.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ cfg_core! {
55
use super::DbType;
66
use crate::{Database, Result};
77

8+
pub use crate::sync::EncryptionContext;
9+
810
/// A builder for [`Database`]. This struct can be used to build
911
/// all variants of [`Database`]. These variants include:
1012
///
@@ -50,6 +52,8 @@ impl Builder<()> {
5052
path: impl AsRef<std::path::Path>,
5153
url: String,
5254
auth_token: String,
55+
#[cfg(feature = "sync")]
56+
remote_encryption: Option<crate::sync::EncryptionContext>,
5357
) -> Builder<RemoteReplica> {
5458
Builder {
5559
inner: RemoteReplica {
@@ -68,6 +72,8 @@ impl Builder<()> {
6872
skip_safety_assert: false,
6973
#[cfg(feature = "sync")]
7074
sync_protocol: Default::default(),
75+
#[cfg(feature = "sync")]
76+
remote_encryption,
7177
},
7278
}
7379
}
@@ -92,6 +98,7 @@ impl Builder<()> {
9298
path: impl AsRef<std::path::Path>,
9399
url: String,
94100
auth_token: String,
101+
remote_encryption: Option<EncryptionContext>,
95102
) -> Builder<SyncedDatabase> {
96103
Builder {
97104
inner: SyncedDatabase {
@@ -109,6 +116,7 @@ impl Builder<()> {
109116
remote_writes: false,
110117
push_batch_size: 0,
111118
sync_interval: None,
119+
remote_encryption,
112120
},
113121
}
114122
}
@@ -229,6 +237,8 @@ cfg_replication! {
229237
skip_safety_assert: bool,
230238
#[cfg(feature = "sync")]
231239
sync_protocol: super::SyncProtocol,
240+
#[cfg(feature = "sync")]
241+
remote_encryption: Option<crate::sync::EncryptionContext>,
232242
}
233243

234244
/// Local replica configuration type in [`Builder`].
@@ -345,6 +355,8 @@ cfg_replication! {
345355
skip_safety_assert,
346356
#[cfg(feature = "sync")]
347357
sync_protocol,
358+
#[cfg(feature = "sync")]
359+
remote_encryption,
348360
} = self.inner;
349361

350362
let connector = if let Some(connector) = connector {
@@ -403,7 +415,7 @@ cfg_replication! {
403415

404416
if res.status().is_success() {
405417
tracing::trace!("Using sync protocol v2 for {}", url);
406-
let builder = Builder::new_synced_database(path, url, auth_token)
418+
let builder = Builder::new_synced_database(path, url, auth_token, remote_encryption)
407419
.connector(connector)
408420
.remote_writes(true)
409421
.read_your_writes(read_your_writes);
@@ -553,6 +565,7 @@ cfg_sync! {
553565
read_your_writes: bool,
554566
push_batch_size: u32,
555567
sync_interval: Option<std::time::Duration>,
568+
remote_encryption: Option<crate::sync::EncryptionContext>,
556569
}
557570

558571
impl Builder<SyncedDatabase> {
@@ -617,6 +630,7 @@ cfg_sync! {
617630
read_your_writes,
618631
push_batch_size,
619632
sync_interval,
633+
remote_encryption,
620634
} = self.inner;
621635

622636
let path = path.to_str().ok_or(crate::Error::InvalidUTF8Path)?.to_owned();
@@ -640,6 +654,7 @@ cfg_sync! {
640654
flags,
641655
url.clone(),
642656
auth_token.clone(),
657+
remote_encryption.clone(),
643658
)
644659
.await?;
645660

@@ -708,6 +723,8 @@ cfg_sync! {
708723
auth_token,
709724
connector,
710725
_bg_abort: bg_abort,
726+
#[cfg(feature = "sync")]
727+
remote_encryption,
711728
},
712729
max_write_replication_index: Default::default(),
713730
})

libsql/src/hrana/hyper.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,29 @@ 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>,
3032
}
3133

3234
impl HttpSender {
3335
pub fn new(
3436
connector: ConnectorService,
3537
version: Option<&str>,
3638
namespace: Option<&str>,
39+
#[cfg(feature = "sync")] remote_encryption: Option<crate::sync::EncryptionContext>,
3740
) -> Self {
3841
let ver = version.unwrap_or(env!("CARGO_PKG_VERSION"));
3942

4043
let version = HeaderValue::try_from(format!("libsql-remote-{ver}")).unwrap();
4144
let namespace = namespace.map(|v| HeaderValue::try_from(v).unwrap());
4245

4346
let inner = hyper::Client::builder().build(connector);
44-
4547
Self {
4648
inner,
4749
version,
4850
namespace,
51+
#[cfg(feature = "sync")]
52+
remote_encryption,
4953
}
5054
}
5155

@@ -63,6 +67,13 @@ impl HttpSender {
6367
req_builder = req_builder.header("x-namespace", namespace);
6468
}
6569

70+
if let Some(remote_encryption) = &self.remote_encryption {
71+
req_builder = req_builder.header(
72+
"x-turso-encryption-key",
73+
remote_encryption.key_16_bytes_base64_encoded.as_str(),
74+
);
75+
}
76+
6677
let req = req_builder
6778
.body(hyper::Body::from(body))
6879
.map_err(|err| HranaError::Http(format!("{:?}", err)))?;
@@ -126,8 +137,15 @@ impl HttpConnection<HttpSender> {
126137
connector: ConnectorService,
127138
version: Option<&str>,
128139
namespace: Option<&str>,
140+
#[cfg(feature = "sync")] remote_encryption: Option<crate::sync::EncryptionContext>,
129141
) -> Self {
130-
let inner = HttpSender::new(connector, version, namespace);
142+
let inner = HttpSender::new(
143+
connector,
144+
version,
145+
namespace,
146+
#[cfg(feature = "sync")]
147+
remote_encryption,
148+
);
131149
Self::new(url.into(), token.into(), inner)
132150
}
133151
}

libsql/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ pub mod params;
132132
cfg_sync! {
133133
mod sync;
134134
pub use database::SyncProtocol;
135+
pub use sync::EncryptionContext;
135136
}
136137

137138
cfg_replication! {

libsql/src/local/database.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ impl Database {
212212
flags: OpenFlags,
213213
endpoint: String,
214214
auth_token: String,
215+
remote_encryption: Option<crate::sync::EncryptionContext>,
215216
) -> Result<Database> {
216217
let db_path = db_path.into();
217218
let endpoint = if endpoint.starts_with("libsql:") {
@@ -222,7 +223,7 @@ impl Database {
222223
let mut db = Database::open(&db_path, flags)?;
223224

224225
let sync_ctx =
225-
SyncContext::new(connector, db_path.into(), endpoint, Some(auth_token)).await?;
226+
SyncContext::new(connector, db_path.into(), endpoint, Some(auth_token), remote_encryption).await?;
226227
db.sync_ctx = Some(Arc::new(Mutex::new(sync_ctx)));
227228

228229
Ok(db)

libsql/src/sync.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ struct PushFramesResult {
118118
baton: Option<String>,
119119
}
120120

121+
#[derive(Debug, Clone)]
122+
pub struct EncryptionContext {
123+
/// The base64-encoded key for the encryption, sent on every request.
124+
pub key_16_bytes_base64_encoded: String,
125+
/// Whether the pushed frames are already encrypted.
126+
pub push_is_encrypted: bool,
127+
/// Whether to request the server to decrypt the pulled frames.
128+
pub decrypt_pull: bool,
129+
}
130+
121131
pub struct SyncContext {
122132
db_path: String,
123133
client: hyper::Client<ConnectorService, Body>,
@@ -133,6 +143,8 @@ pub struct SyncContext {
133143
/// whenever sync is called very first time, we will call the remote server
134144
/// to get the generation information and sync the db file if needed
135145
initial_server_sync: bool,
146+
/// The encryption context for the sync.
147+
remote_encryption: Option<EncryptionContext>,
136148
}
137149

138150
impl SyncContext {
@@ -141,6 +153,7 @@ impl SyncContext {
141153
db_path: String,
142154
sync_url: String,
143155
auth_token: Option<String>,
156+
remote_encryption: Option<EncryptionContext>,
144157
) -> Result<Self> {
145158
let client = hyper::client::Client::builder().build::<_, hyper::Body>(connector);
146159

@@ -163,6 +176,7 @@ impl SyncContext {
163176
durable_generation: 0,
164177
durable_frame_num: 0,
165178
initial_server_sync: false,
179+
remote_encryption,
166180
};
167181

168182
if let Err(e) = me.read_metadata().await {
@@ -303,6 +317,16 @@ impl SyncContext {
303317
None => {}
304318
}
305319

320+
if let Some(remote_encryption) = &self.remote_encryption {
321+
if remote_encryption.decrypt_pull {
322+
req = req.header("x-turso-decrypt-response", "true");
323+
}
324+
if remote_encryption.push_is_encrypted {
325+
req = req.header("x-turso-encrypted-request", "true");
326+
}
327+
req = req.header("x-turso-encryption-key", remote_encryption.key_16_bytes_base64_encoded.as_str());
328+
}
329+
306330
let req = req.body(body.clone().into()).expect("valid body");
307331

308332
let res = self
@@ -414,6 +438,16 @@ impl SyncContext {
414438
None => {}
415439
}
416440

441+
if let Some(remote_encryption) = &self.remote_encryption {
442+
if remote_encryption.decrypt_pull {
443+
req = req.header("x-turso-decrypt-response", "true");
444+
}
445+
if remote_encryption.push_is_encrypted {
446+
req = req.header("x-turso-encrypted-request", "true");
447+
}
448+
req = req.header("x-turso-encryption-key", remote_encryption.key_16_bytes_base64_encoded.as_str());
449+
}
450+
417451
let req = req.body(Body::empty()).expect("valid request");
418452

419453
let res = self
@@ -577,6 +611,16 @@ impl SyncContext {
577611
req = req.header("Authorization", auth_token);
578612
}
579613

614+
if let Some(remote_encryption) = &self.remote_encryption {
615+
if remote_encryption.decrypt_pull {
616+
req = req.header("x-turso-decrypt-response", "true");
617+
}
618+
if remote_encryption.push_is_encrypted {
619+
req = req.header("x-turso-encrypted-request", "true");
620+
}
621+
req = req.header("x-turso-encryption-key", remote_encryption.key_16_bytes_base64_encoded.as_str());
622+
}
623+
580624
let req = req.body(Body::empty()).expect("valid request");
581625

582626
let res = self
@@ -673,6 +717,16 @@ impl SyncContext {
673717
req = req.header("Authorization", auth_token);
674718
}
675719

720+
if let Some(remote_encryption) = &self.remote_encryption {
721+
if remote_encryption.decrypt_pull {
722+
req = req.header("x-turso-decrypt-response", "true");
723+
}
724+
if remote_encryption.push_is_encrypted {
725+
req = req.header("x-turso-encrypted-request", "true");
726+
}
727+
req = req.header("x-turso-encryption-key", remote_encryption.key_16_bytes_base64_encoded.as_str());
728+
}
729+
676730
let req = req.body(Body::empty()).expect("valid request");
677731

678732
let (res, http_duration) =

0 commit comments

Comments
 (0)