Merge 'Initial pass to support per page encryption' from Avinash Sajjanshetty

This patch adds support for per page encryption. The code is of alpha
quality, was to test my hypothesis. All the encryption code is gated
behind a `encryption` flag. To play with it, you can do:
```sh
cargo run --features encryption -- database.db

turso> PRAGMA key='turso_test_encryption_key_123456';

turso> CREATE TABLE t(v);
```
Right now, most stuff is hard coded. We use AES GCM 256. This
information is not stored anywhere, but in future versions we will start
saving this info in the file. When writing to disk, we will generate a
cryptographically secure random salt, use that to encrypt the page. Then
we will store the authentication tag and the salt in the page itself. To
accommodate this encryption hardcodes reserved space of 28 bytes.
Once the key is set in the connection, we propagate that information to
pager and the WAL, to encrypt / decrypt when reading from disk.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2567
This commit is contained in:
Pekka Enberg
2025-08-20 11:11:24 +03:00
committed by GitHub
20 changed files with 640 additions and 37 deletions

View File

@@ -16,5 +16,8 @@ napi = { version = "3.1.3", default-features = false, features = ["napi6"] }
napi-derive = { version = "3.1.1", default-features = true }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
[features]
encryption = ["turso_core/encryption"]
[build-dependencies]
napi-build = "2.2.3"

View File

@@ -561,6 +561,7 @@ impl turso_core::DatabaseStorage for DatabaseFile {
fn read_page(
&self,
page_idx: usize,
_key: Option<&turso_core::EncryptionKey>,
c: turso_core::Completion,
) -> turso_core::Result<turso_core::Completion> {
let r = c.as_read();
@@ -577,6 +578,7 @@ impl turso_core::DatabaseStorage for DatabaseFile {
&self,
page_idx: usize,
buffer: Arc<turso_core::Buffer>,
_key: Option<&turso_core::EncryptionKey>,
c: turso_core::Completion,
) -> turso_core::Result<turso_core::Completion> {
let size = buffer.len();
@@ -586,12 +588,13 @@ impl turso_core::DatabaseStorage for DatabaseFile {
fn write_pages(
&self,
page_idx: usize,
first_page_idx: usize,
page_size: usize,
buffers: Vec<Arc<turso_core::Buffer>>,
_key: Option<&turso_core::EncryptionKey>,
c: turso_core::Completion,
) -> turso_core::Result<turso_core::Completion> {
let pos = page_idx.saturating_sub(1) * page_size;
let pos = first_page_idx.saturating_sub(1) * page_size;
let c = self.file.pwritev(pos, buffers, c)?;
Ok(c)
}