mirror of
https://github.com/hanzoai/sqlite.git
synced 2026-10-30 18:22:26 +00:00
The cgo EncryptionAvailable() was a compile-time `return true`, so a cgo build linking plain sqlite (libsqlcipher absent) reported "available" while Open/OpenDB handed the key to an engine that no-ops PRAGMA key and writes PLAINTEXT — a file with a "SQLite format 3\0" header and rows in the clear, believed to be encrypted by cek's per-org envelope. - EncryptionAvailable() (cgo) now runs a cached one-time runtime probe (codecEncrypts): open a throwaway keyed db, prove the on-disk bytes are ciphertext (encryptsAtRest), and reopen under the key. A build that cannot truly encrypt reports false. - The keyed-open gate moves to openDB, the single funnel Open AND OpenDB pass through, so both fail closed (ErrEncryptionUnavailable, no file). This closes the OpenDB bypass that cek/openKeyed used. - CodecLinked() shares the same cached probe on cgo; always false on pure-Go (that backend never links the C codec). Both stay exported under both tags. - encryption_proof_test drops the CodecLinked/SQLITE_REQUIRE_CODEC skip dance: the probe makes "available" mean "actually encrypts", so a mis-linked cgo build takes the same refuse-and-write-no-file branch as pure-Go. - detect.go: the pure plaintext-detection predicate, unit-tested portably. - probe_cgo_test.go: forces the probe to report a mis-linked codec and asserts Open and OpenDB both fail closed with no file (the silent-plaintext gate), plus the true-positive path.
29 lines
1.1 KiB
Go
29 lines
1.1 KiB
Go
package sqlite
|
|
|
|
import "testing"
|
|
|
|
// TestEncryptsAtRest covers the pure decision predicate behind the codec probe,
|
|
// portably (no C codec needed): plaintext is anything a SQLCipher file is not.
|
|
func TestEncryptsAtRest(t *testing.T) {
|
|
marker := []byte("SENTINEL-9f3c")
|
|
|
|
// A plaintext SQLite database (magic header) is NOT encrypted, even if the
|
|
// marker happens not to appear verbatim.
|
|
plainHeader := append([]byte("SQLite format 3\x00"), []byte("\x10\x00\x01...")...)
|
|
if encryptsAtRest(plainHeader, marker) {
|
|
t.Fatal("plaintext SQLite header accepted as encrypted")
|
|
}
|
|
|
|
// No magic header, but the sentinel is visible in the clear ⇒ not encrypted.
|
|
if encryptsAtRest([]byte("\x9anot-a-header... SENTINEL-9f3c ...tail"), marker) {
|
|
t.Fatal("visible sentinel accepted as encrypted")
|
|
}
|
|
|
|
// SQLCipher-shaped bytes: random salt where the magic would be, no sentinel in
|
|
// the clear ⇒ genuinely encrypted.
|
|
ct := []byte{0x9a, 0x3f, 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}
|
|
if !encryptsAtRest(ct, marker) {
|
|
t.Fatal("ciphertext (no header, no sentinel) rejected as plaintext")
|
|
}
|
|
}
|