tursodatabase/libsql

Embedded replicas encryption doesn't work on SQLCipher cipher type

haaawk opened this issue · 1 comments

Reproducer:

docker run -d -p 8080:8080 ghcr.io/tursodatabase/libsql-server:latest
git checkout main
rm -rf libsql-ffi/bundled/SQLite3MultipleCiphers/build
cd bindings/c
carga b -j16 --release
cd ../go
LIBSQL_PRIMARY_URL=http://localhost:8080 go test -count=1 -run=TestEncryption -v ./...

Observed problem:

?       github.com/libsql/go-libsql/examples    [no test files]
=== RUN   TestEncryption
    libsql_test.go:34: goroutine 19 [running]:
        runtime/debug.Stack()
                /usr/local/go/src/runtime/debug/stack.go:24 +0x5e
        github.com/libsql/go-libsql.T.FatalOnError({0xc0000b4210?}, {0x1005cf840, 0xc0000b0480})
                /Users/haaawk/work/libsql/bindings/go/libsql_test.go:34 +0x3d
        github.com/libsql/go-libsql.TestEncryption(0xc00009b040)
                /Users/haaawk/work/libsql/bindings/go/libsql_test.go:548 +0x8ec
        testing.tRunner(0xc00009b040, 0x100550178)
                /usr/local/go/src/testing/testing.go:1595 +0xff
        created by testing.(*T).Run in goroutine 1
                /usr/local/go/src/testing/testing.go:1648 +0x3ad
        
    libsql_test.go:35: failed to execute query SELECT * FROM test_1650501895787664715_20240304110048
        error code = 1: Error executing statement: SQLite failure: `malformed database schema (test_1650501895787664715_20240304110048) - near "T": syntax error`
--- FAIL: TestEncryption (1.10s)
FAIL
FAIL    github.com/libsql/go-libsql     1.110s
FAIL

It seems that we are not able to open encrypted embedded replica.

The problem goes away after applying the following patch and repeating the reproducer steps:

diff --git a/libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt b/libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt
index 56d375279a..c56fe9cbf7 100644
--- a/libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt
+++ b/libsql-ffi/bundled/SQLite3MultipleCiphers/CMakeLists.txt
@@ -133,7 +133,7 @@ set(SQLITE3MC_BASE_DEFINITIONS
   HAVE_CIPHER_AES_128_CBC=0
   HAVE_CIPHER_AES_256_CBC=1
   HAVE_CIPHER_CHACHA20=0   
-  HAVE_CIPHER_SQLCIPHER=1   
+  HAVE_CIPHER_SQLCIPHER=0
   HAVE_CIPHER_RC4=0   
   HAVE_CIPHER_ASCON128=0
 #  $<$<BOOL:${SQLITE_USE_TCL}>:SQLITE_USE_TCL=1>

What we noticed is that this issue happens when SQLite3 Multiple Ciphers cipher type is set to sqlcipher. It doesn't happen when aes256cbc is set.

Potential reason may be that the latter (working one) doesn't use HMAC, while former may interfere with Frame Injector at this edge. From the documentation:

aes256cbc:

The cipher does not use a Hash Message Authentication Code (HMAC), and requires therefore no reserved bytes per database page.

sqlcipher - the failing one:

he encryption key is derived from the passphrase using a random salt (stored in the first 16 bytes of the database file) and the standardized PBKDF2 algorithm with an SHA1, SHA256, or SHA512 hash function.
A random 16 bytes initial vector (nonce) for the encryption of each database page is used for the AES algorithm. Additionally, an authentication tag per database page is calculated:
SQLCipher version 1 used no tag (0 bytes).
SQLCipher version 2 to 3 used a SHA1 tag (20 bytes).
SQLCipher version 4 uses a SHA512 tag (64 bytes), allowing to optionally choose a SHA256 tag (32 bytes) instead.
Therefore this cipher requires 16, 48 or 80 reserved bytes per database page (since the number of reserved bytes is rounded to the next multiple of the AES block size of 16 bytes).