Skip to content

Commit 6075769

Browse files
authored
Merge branch 'main' into add-to-mockserver
2 parents 97a2c5f + 7eed898 commit 6075769

File tree

11 files changed

+176
-17
lines changed

11 files changed

+176
-17
lines changed

.github/workflows/rust.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ jobs:
130130

131131
- name: Run tests
132132
run: cargo nextest run
133+
134+
- name: Run libsql encryption tests
135+
run: cargo test --features encryption --color=always --test encryption test_encryption
133136
# test-custom-pager:
134137
# runs-on: ubuntu-latest
135138
# name: Run Tests

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ exclude = [
2525
]
2626

2727
[workspace.package]
28-
version = "0.9.21"
28+
version = "0.9.23"
2929
authors = ["the libSQL authors"]
3030
edition = "2021"
3131
license = "MIT"
3232
repository = "https://github.com/tursodatabase/libsql"
3333

3434
[workspace.dependencies]
35-
libsql-ffi = { path = "libsql-ffi", version = "0.9.21" }
36-
libsql-sys = { path = "libsql-sys", version = "0.9.21", default-features = false }
37-
libsql-hrana = { path = "libsql-hrana", version = "0.9.21" }
38-
libsql_replication = { path = "libsql-replication", version = "0.9.21" }
39-
rusqlite = { package = "libsql-rusqlite", path = "vendored/rusqlite", version = "0.9.21", default-features = false, features = [
35+
libsql-ffi = { path = "libsql-ffi", version = "0.9.23" }
36+
libsql-sys = { path = "libsql-sys", version = "0.9.23", default-features = false }
37+
libsql-hrana = { path = "libsql-hrana", version = "0.9.23" }
38+
libsql_replication = { path = "libsql-replication", version = "0.9.23" }
39+
rusqlite = { package = "libsql-rusqlite", path = "vendored/rusqlite", version = "0.9.23", default-features = false, features = [
4040
"libsql-experimental",
4141
"column_decltype",
4242
"load_extension",

libsql-ffi/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ fn build_multiple_ciphers(out_path: &Path) -> PathBuf {
494494
if cfg!(feature = "wasmtime-bindings") {
495495
config.define("LIBSQL_ENABLE_WASM_RUNTIME", "1");
496496
}
497+
config.define("LIBSQL_ENCRYPTION", "1");
497498

498499
if cfg!(feature = "session") {
499500
config

libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ set(SQLITE3MC_BASE_DEFINITIONS
124124
$<$<BOOL:${LIBSQL_ENABLE_WASM_RUNTIME}>:LIBSQL_ENABLE_WASM_RUNTIME=1>
125125
LIBSQL_EXTRA_PRAGMAS=1
126126
LIBSQL_CUSTOM_PAGER_CODEC=1
127+
LIBSQL_ENCRYPTION=1
127128

128129
SQLITE_ENABLE_DBSTAT_VTAB=1
129130
SQLITE_ENABLE_DBPAGE_VTAB=1

libsql-ffi/bundled/src/sqlite3.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121872,6 +121872,10 @@ SQLITE_PRIVATE int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
121872121872
int libsql_handle_extra_attach_params(sqlite3* db, const char* zName, const char* zPath, sqlite3_value* pKey, char** zErrDyn);
121873121873
#endif
121874121874

121875+
#ifdef LIBSQL_ENCRYPTION
121876+
SQLITE_PRIVATE int sqlite3mcHandleAttachKey(sqlite3*, const char*, const char*, sqlite3_value*, char**);
121877+
#endif
121878+
121875121879
/*
121876121880
** An SQL user-function registered to do the work of an ATTACH statement. The
121877121881
** three arguments to the function come directly from an attach statement:
@@ -122031,6 +122035,16 @@ static void attachFunc(
122031122035
rc = libsql_handle_extra_attach_params(db, zName, zPath, argv, &zErrDyn);
122032122036
}
122033122037
#endif
122038+
122039+
#ifdef LIBSQL_ENCRYPTION
122040+
/* If the ATTACH statement came with key parameter, then lets handle it here. */
122041+
if( rc==SQLITE_OK ){
122042+
if( argv != NULL && argv[0] != NULL && argv[1] != NULL && argv[2] != NULL ){
122043+
rc = sqlite3mcHandleAttachKey(db, zName, zPath, argv[2], &zErrDyn);
122044+
}
122045+
}
122046+
#endif
122047+
122034122048
sqlite3_free_filename( zPath );
122035122049

122036122050
/* If the file was opened successfully, read the schema for the new database.

libsql-sqlite3/src/attach.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
6161
int libsql_handle_extra_attach_params(sqlite3* db, const char* zName, const char* zPath, sqlite3_value* pKey, char** zErrDyn);
6262
#endif
6363

64+
#ifdef LIBSQL_ENCRYPTION
65+
SQLITE_PRIVATE int sqlite3mcHandleAttachKey(sqlite3*, const char*, const char*, sqlite3_value*, char**);
66+
#endif
67+
6468
/*
6569
** An SQL user-function registered to do the work of an ATTACH statement. The
6670
** three arguments to the function come directly from an attach statement:
@@ -220,6 +224,16 @@ static void attachFunc(
220224
rc = libsql_handle_extra_attach_params(db, zName, zPath, argv, &zErrDyn);
221225
}
222226
#endif
227+
228+
#ifdef LIBSQL_ENCRYPTION
229+
/* If the ATTACH statement came with key parameter, then lets handle it here. */
230+
if( rc==SQLITE_OK ){
231+
if( argv != NULL && argv[0] != NULL && argv[1] != NULL && argv[2] != NULL ){
232+
rc = sqlite3mcHandleAttachKey(db, zName, zPath, argv[2], &zErrDyn);
233+
}
234+
}
235+
#endif
236+
223237
sqlite3_free_filename( zPath );
224238

225239
/* If the file was opened successfully, read the schema for the new database.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Example of showing using an encrypted local database with libsql. It also shows how to
2+
// attach another encrypted database. The example expects a local `world.db` encrypted database
3+
// to be present in the same directory.
4+
5+
use libsql::{params, Builder};
6+
use libsql::{Cipher, EncryptionConfig};
7+
8+
#[tokio::main]
9+
async fn main() {
10+
tracing_subscriber::fmt::init();
11+
12+
// The local database path where the data will be stored.
13+
let db_path = std::env::var("LIBSQL_DB_PATH").unwrap();
14+
// The encryption key for the database.
15+
let encryption_key = std::env::var("LIBSQL_ENCRYPTION_KEY").unwrap_or("s3cR3t".to_string());
16+
17+
let mut db_builder = Builder::new_local(db_path);
18+
19+
db_builder = db_builder.encryption_config(EncryptionConfig {
20+
cipher: Cipher::Aes256Cbc,
21+
encryption_key: encryption_key.into(),
22+
});
23+
24+
let db = db_builder.build().await.unwrap();
25+
let conn = db.connect().unwrap();
26+
conn.execute(
27+
"CREATE TABLE IF NOT EXISTS guest_book_entries (text TEXT)",
28+
(),
29+
)
30+
.await
31+
.unwrap();
32+
33+
// let's attach another encrypted database and print its contents
34+
conn.execute("ATTACH DATABASE 'world.db' AS world KEY s3cR3t", ())
35+
.await
36+
.unwrap();
37+
38+
let mut attached_results = conn
39+
.query("SELECT * FROM world.guest_book_entries", ())
40+
.await
41+
.unwrap();
42+
43+
println!("attached database guest book entries:");
44+
while let Some(row) = attached_results.next().await.unwrap() {
45+
let text: String = row.get(0).unwrap();
46+
println!(" {}", text);
47+
}
48+
49+
let mut input = String::new();
50+
println!("Please write your entry to the guestbook:");
51+
match std::io::stdin().read_line(&mut input) {
52+
Ok(_) => {
53+
println!("You entered: {}", input);
54+
let params = params![input.as_str()];
55+
conn.execute("INSERT INTO guest_book_entries (text) VALUES (?)", params)
56+
.await
57+
.unwrap();
58+
}
59+
Err(error) => {
60+
eprintln!("Error reading input: {}", error);
61+
}
62+
}
63+
let mut results = conn
64+
.query("SELECT * FROM guest_book_entries", ())
65+
.await
66+
.unwrap();
67+
println!("Guest book entries:");
68+
while let Some(row) = results.next().await.unwrap() {
69+
let text: String = row.get(0).unwrap();
70+
println!(" {}", text);
71+
}
72+
}

libsql/src/sync.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,6 @@ impl SyncContext {
172172
remote_encryption,
173173
};
174174
me.read_metadata().await?;
175-
if me.durable_generation == 0 {
176-
return Err(SyncError::InvalidLocalState("generation is 0".to_string()).into());
177-
}
178175
Ok(me)
179176
}
180177

@@ -575,6 +572,10 @@ impl SyncContext {
575572
metadata
576573
);
577574

575+
if metadata.generation == 0 {
576+
return Err(SyncError::InvalidLocalState("generation is 0".to_string()).into());
577+
}
578+
578579
self.durable_generation = metadata.generation;
579580
self.durable_frame_num = metadata.durable_frame_num;
580581

@@ -991,7 +992,8 @@ pub async fn try_pull(
991992
if !insert_handle.in_session() {
992993
tracing::debug!(
993994
"pull_frames: generation={}, frame={}, start wal transaction session",
994-
generation, next_frame_no
995+
generation,
996+
next_frame_no
995997
);
996998
insert_handle.begin()?;
997999
}

libsql/tests/encryption.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use libsql::{params, Builder};
2+
use libsql_sys::{Cipher, EncryptionConfig};
3+
4+
#[tokio::test]
5+
#[cfg(feature = "encryption")]
6+
async fn test_encryption() {
7+
let tempdir = std::env::temp_dir();
8+
let encrypted_path = tempdir.join("encrypted.db");
9+
let base_path = tempdir.join("base.db");
10+
11+
// lets create an encrypted database
12+
{
13+
let mut db_builder = Builder::new_local(&encrypted_path);
14+
db_builder = db_builder.encryption_config(EncryptionConfig {
15+
cipher: Cipher::Aes256Cbc,
16+
encryption_key: "s3cR3t".into(),
17+
});
18+
let db = db_builder.build().await.unwrap();
19+
20+
let conn = db.connect().unwrap();
21+
conn.execute("CREATE TABLE IF NOT EXISTS messages (text TEXT)", ())
22+
.await
23+
.unwrap();
24+
let params = params!["the only winning move is not to play"];
25+
conn.execute("INSERT INTO messages (text) VALUES (?)", params)
26+
.await
27+
.unwrap();
28+
}
29+
30+
// lets test encryption with ATTACH
31+
{
32+
let db = Builder::new_local(&base_path).build().await.unwrap();
33+
let conn = db.connect().unwrap();
34+
let attach_stmt = format!(
35+
"ATTACH DATABASE '{}' AS encrypted KEY 's3cR3t'",
36+
tempdir.join("encrypted.db").display()
37+
);
38+
conn.execute(&attach_stmt, ()).await.unwrap();
39+
let mut attached_results = conn
40+
.query("SELECT * FROM encrypted.messages", ())
41+
.await
42+
.unwrap();
43+
let row = attached_results.next().await.unwrap().unwrap();
44+
let text: String = row.get(0).unwrap();
45+
assert_eq!(text, "the only winning move is not to play");
46+
}
47+
48+
{
49+
let _ = std::fs::remove_file(&encrypted_path);
50+
let _ = std::fs::remove_file(&base_path);
51+
}
52+
}

0 commit comments

Comments
 (0)