Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ jobs:

- name: Run tests
run: cargo nextest run

- name: Run libsql encryption tests
run: cargo test --features encryption --color=always --test encryption test_encryption
# test-custom-pager:
# runs-on: ubuntu-latest
# name: Run Tests
Expand Down
1 change: 1 addition & 0 deletions libsql-ffi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ fn build_multiple_ciphers(out_path: &Path) -> PathBuf {
if cfg!(feature = "wasmtime-bindings") {
config.define("LIBSQL_ENABLE_WASM_RUNTIME", "1");
}
config.define("LIBSQL_ENCRYPTION", "1");

if cfg!(feature = "session") {
config
Expand Down
1 change: 1 addition & 0 deletions libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ set(SQLITE3MC_BASE_DEFINITIONS
$<$<BOOL:${LIBSQL_ENABLE_WASM_RUNTIME}>:LIBSQL_ENABLE_WASM_RUNTIME=1>
LIBSQL_EXTRA_PRAGMAS=1
LIBSQL_CUSTOM_PAGER_CODEC=1
LIBSQL_ENCRYPTION=1

SQLITE_ENABLE_DBSTAT_VTAB=1
SQLITE_ENABLE_DBPAGE_VTAB=1
Expand Down
14 changes: 14 additions & 0 deletions libsql-ffi/bundled/src/sqlite3.c
Original file line number Diff line number Diff line change
Expand Up @@ -121872,6 +121872,10 @@ SQLITE_PRIVATE int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
int libsql_handle_extra_attach_params(sqlite3* db, const char* zName, const char* zPath, sqlite3_value* pKey, char** zErrDyn);
#endif

#ifdef LIBSQL_ENCRYPTION
SQLITE_PRIVATE int sqlite3mcHandleAttachKey(sqlite3*, const char*, const char*, sqlite3_value*, char**);
#endif

/*
** An SQL user-function registered to do the work of an ATTACH statement. The
** three arguments to the function come directly from an attach statement:
Expand Down Expand Up @@ -122031,6 +122035,16 @@ static void attachFunc(
rc = libsql_handle_extra_attach_params(db, zName, zPath, argv, &zErrDyn);
}
#endif

#ifdef LIBSQL_ENCRYPTION
/* If the ATTACH statement came with key parameter, then lets handle it here. */
if( rc==SQLITE_OK ){
if( argv != NULL && argv[0] != NULL && argv[1] != NULL && argv[2] != NULL ){
rc = sqlite3mcHandleAttachKey(db, zName, zPath, argv[2], &zErrDyn);
}
}
#endif

sqlite3_free_filename( zPath );

/* If the file was opened successfully, read the schema for the new database.
Expand Down
14 changes: 14 additions & 0 deletions libsql-sqlite3/src/attach.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
int libsql_handle_extra_attach_params(sqlite3* db, const char* zName, const char* zPath, sqlite3_value* pKey, char** zErrDyn);
#endif

#ifdef LIBSQL_ENCRYPTION
SQLITE_PRIVATE int sqlite3mcHandleAttachKey(sqlite3*, const char*, const char*, sqlite3_value*, char**);
#endif

/*
** An SQL user-function registered to do the work of an ATTACH statement. The
** three arguments to the function come directly from an attach statement:
Expand Down Expand Up @@ -220,6 +224,16 @@ static void attachFunc(
rc = libsql_handle_extra_attach_params(db, zName, zPath, argv, &zErrDyn);
}
#endif

#ifdef LIBSQL_ENCRYPTION
/* If the ATTACH statement came with key parameter, then lets handle it here. */
if( rc==SQLITE_OK ){
if( argv != NULL && argv[0] != NULL && argv[1] != NULL && argv[2] != NULL ){
rc = sqlite3mcHandleAttachKey(db, zName, zPath, argv[2], &zErrDyn);
}
}
#endif

sqlite3_free_filename( zPath );

/* If the file was opened successfully, read the schema for the new database.
Expand Down
72 changes: 72 additions & 0 deletions libsql/examples/encryption_local.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Example of showing using an encrypted local database with libsql. It also shows how to
// attach another encrypted database. The example expects a local `world.db` encrypted database
// to be present in the same directory.

use libsql::{params, Builder};
use libsql::{Cipher, EncryptionConfig};

#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();

// The local database path where the data will be stored.
let db_path = std::env::var("LIBSQL_DB_PATH").unwrap();
// The encryption key for the database.
let encryption_key = std::env::var("LIBSQL_ENCRYPTION_KEY").unwrap_or("s3cR3t".to_string());

let mut db_builder = Builder::new_local(db_path);

db_builder = db_builder.encryption_config(EncryptionConfig {
cipher: Cipher::Aes256Cbc,
encryption_key: encryption_key.into(),
});

let db = db_builder.build().await.unwrap();
let conn = db.connect().unwrap();
conn.execute(
"CREATE TABLE IF NOT EXISTS guest_book_entries (text TEXT)",
(),
)
.await
.unwrap();

// let's attach another encrypted database and print its contents
conn.execute("ATTACH DATABASE 'world.db' AS world KEY s3cR3t", ())
.await
.unwrap();

let mut attached_results = conn
.query("SELECT * FROM world.guest_book_entries", ())
.await
.unwrap();

println!("attached database guest book entries:");
while let Some(row) = attached_results.next().await.unwrap() {
let text: String = row.get(0).unwrap();
println!(" {}", text);
}

let mut input = String::new();
println!("Please write your entry to the guestbook:");
match std::io::stdin().read_line(&mut input) {
Ok(_) => {
println!("You entered: {}", input);
let params = params![input.as_str()];
conn.execute("INSERT INTO guest_book_entries (text) VALUES (?)", params)
.await
.unwrap();
}
Err(error) => {
eprintln!("Error reading input: {}", error);
}
}
let mut results = conn
.query("SELECT * FROM guest_book_entries", ())
.await
.unwrap();
println!("Guest book entries:");
while let Some(row) = results.next().await.unwrap() {
let text: String = row.get(0).unwrap();
println!(" {}", text);
}
}
52 changes: 52 additions & 0 deletions libsql/tests/encryption.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use libsql::{params, Builder};
use libsql_sys::{Cipher, EncryptionConfig};

#[tokio::test]
#[cfg(feature = "encryption")]
async fn test_encryption() {
let tempdir = std::env::temp_dir();
let encrypted_path = tempdir.join("encrypted.db");
let base_path = tempdir.join("base.db");

// lets create an encrypted database
{
let mut db_builder = Builder::new_local(&encrypted_path);
db_builder = db_builder.encryption_config(EncryptionConfig {
cipher: Cipher::Aes256Cbc,
encryption_key: "s3cR3t".into(),
});
let db = db_builder.build().await.unwrap();

let conn = db.connect().unwrap();
conn.execute("CREATE TABLE IF NOT EXISTS messages (text TEXT)", ())
.await
.unwrap();
let params = params!["the only winning move is not to play"];
conn.execute("INSERT INTO messages (text) VALUES (?)", params)
.await
.unwrap();
}

// lets test encryption with ATTACH
{
let db = Builder::new_local(&base_path).build().await.unwrap();
let conn = db.connect().unwrap();
let attach_stmt = format!(
"ATTACH DATABASE '{}' AS encrypted KEY 's3cR3t'",
tempdir.join("encrypted.db").display()
);
conn.execute(&attach_stmt, ()).await.unwrap();
let mut attached_results = conn
.query("SELECT * FROM encrypted.messages", ())
.await
.unwrap();
let row = attached_results.next().await.unwrap().unwrap();
let text: String = row.get(0).unwrap();
assert_eq!(text, "the only winning move is not to play");
}

{
let _ = std::fs::remove_file(&encrypted_path);
let _ = std::fs::remove_file(&base_path);
}
}
Loading