Skip to content

Commit 07241d0

Browse files
authored
Adiantum encrypting VFS improvements. (#80)
Encrypt temporary files.
1 parent 2c30bc9 commit 07241d0

File tree

17 files changed

+112
-59
lines changed

17 files changed

+112
-59
lines changed

embed/exports.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ sqlite3_create_collation_go
5151
sqlite3_create_function_go
5252
sqlite3_create_module_go
5353
sqlite3_create_window_function_go
54+
sqlite3_database_file_object
5455
sqlite3_db_config
5556
sqlite3_db_name
5657
sqlite3_db_readonly
@@ -61,9 +62,6 @@ sqlite3_errmsg
6162
sqlite3_error_offset
6263
sqlite3_errstr
6364
sqlite3_exec
64-
sqlite3_filename_database
65-
sqlite3_filename_journal
66-
sqlite3_filename_wal
6765
sqlite3_finalize
6866
sqlite3_get_autocommit
6967
sqlite3_get_auxdata

embed/sqlite3.wasm

-490 Bytes
Binary file not shown.

vfs/adiantum/README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,24 @@ In general, any HBSH construction can be used to wrap any VFS.
1616

1717
The default Adiantum construction uses XChaCha12 for its stream cipher,
1818
AES for its block cipher, and NH and Poly1305 for hashing.
19-
It uses Argon2id to derive 256-bit keys from plain text.
19+
Additionally, we use Argon2id to derive 256-bit keys from plain text.
2020

21-
The VFS encrypts database files, rollback and statement journals, and WAL files.
21+
The VFS encrypts all files _except_
22+
[super journals](https://sqlite.org/tempfiles.html#super_journal_files):
23+
these _never_ contain database data, only filenames,
24+
and padding them to the block size is problematic.
25+
26+
Temporary files _are_ encrypted with **random** keys,
27+
as they _may_ contain database data.
28+
To avoid the overhead of encrypting temporary files,
29+
keep them in memory:
30+
31+
PRAGMA temp_store = memory;
2232

2333
> [!IMPORTANT]
2434
> Adiantum is typically used for disk encryption.
2535
> The standard threat model for disk encryption considers an adversary
2636
> that can read multiple snapshots of a disk.
27-
> The security property that disk encryption provides is that
28-
> the only information such an adversary can determine is
29-
> whether the data in a sector has or has not changed over time.
37+
> The only security property that disk encryption (and this package)
38+
> provides is that the only information such an adversary can determine
39+
> is whether the data in a sector has or has not changed over time.

vfs/adiantum/adiantum.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package adiantum
22

33
import (
4+
"crypto/rand"
45
"sync"
56

67
"golang.org/x/crypto/argon2"
@@ -20,6 +21,12 @@ func (adiantumCreator) HBSH(key []byte) *hbsh.HBSH {
2021
}
2122

2223
func (adiantumCreator) KDF(text string) []byte {
24+
if text == "" {
25+
key := make([]byte, 32)
26+
n, _ := rand.Read(key)
27+
return key[:n]
28+
}
29+
2330
if key := keyCacheGet(text); key != nil {
2431
return key[:]
2532
}

vfs/adiantum/api.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,20 @@ func Register(name string, base vfs.VFS, cipher HBSHCreator) {
3333
if cipher == nil {
3434
cipher = adiantumCreator{}
3535
}
36-
vfs.Register("adiantum", &hbshVFS{
36+
vfs.Register(name, &hbshVFS{
3737
VFS: base,
3838
hbsh: cipher,
3939
})
4040
}
4141

42-
// HBSHCreator creates an [hbsh.HBSH] cipher,
42+
// HBSHCreator creates an [hbsh.HBSH] cipher
4343
// given key material.
4444
type HBSHCreator interface {
45-
// KDF maps a secret (text) to a key of the appropriate size.
46-
KDF(text string) (key []byte)
45+
// KDF derives an HBSH key from a secret.
46+
// If no secret is given, a random key is generated.
47+
KDF(secret string) (key []byte)
4748

48-
// HBSH creates an HBSH cipher given an appropriate key.
49+
// HBSH creates an HBSH cipher given a key.
50+
// If key is not appropriate, nil is returned.
4951
HBSH(key []byte) *hbsh.HBSH
5052
}

vfs/adiantum/hbsh.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,23 @@ type hbshVFS struct {
1818
}
1919

2020
func (h *hbshVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
21-
return h.OpenParams(name, flags, url.Values{})
21+
return h.OpenParams(name, flags, nil)
2222
}
2323

2424
func (h *hbshVFS) OpenParams(name string, flags vfs.OpenFlag, params url.Values) (file vfs.File, _ vfs.OpenFlag, err error) {
25-
encrypt := flags&(0|
26-
vfs.OPEN_MAIN_DB|
27-
vfs.OPEN_MAIN_JOURNAL|
28-
vfs.OPEN_SUBJOURNAL|
29-
vfs.OPEN_WAL) != 0
30-
3125
var hbsh *hbsh.HBSH
32-
if encrypt {
26+
27+
// Encrypt everything except super journals.
28+
if flags&vfs.OPEN_SUPER_JOURNAL == 0 {
3329
var key []byte
3430
if t, ok := params["key"]; ok {
3531
key = []byte(t[0])
3632
} else if t, ok := params["hexkey"]; ok {
3733
key, _ = hex.DecodeString(t[0])
3834
} else if t, ok := params["textkey"]; ok {
3935
key = h.hbsh.KDF(t[0])
36+
} else if name == "" {
37+
key = h.hbsh.KDF("")
4038
}
4139

4240
if hbsh = h.hbsh.HBSH(key); hbsh == nil {
@@ -45,15 +43,14 @@ func (h *hbshVFS) OpenParams(name string, flags vfs.OpenFlag, params url.Values)
4543
}
4644

4745
if h, ok := h.VFS.(vfs.VFSParams); ok {
48-
delete(params, "vfs")
4946
delete(params, "key")
5047
delete(params, "hexkey")
5148
delete(params, "textkey")
5249
file, flags, err = h.OpenParams(name, flags, params)
5350
} else {
5451
file, flags, err = h.Open(name, flags)
5552
}
56-
if err != nil || hbsh == nil {
53+
if err != nil || hbsh == nil || flags&vfs.OPEN_MEMORY != 0 {
5754
return file, flags, err
5855
}
5956
return &hbshFile{File: file, hbsh: hbsh}, flags, err

vfs/api.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ type VFSParams interface {
2424
OpenParams(name string, flags OpenFlag, params url.Values) (File, OpenFlag, error)
2525
}
2626

27+
// VFSJournal extends VFS with the ability to open journals
28+
// that need a reference to their corresponding database files.
29+
//
30+
// https://sqlite.org/c3ref/database_file_object.html
31+
type VFSJournal interface {
32+
VFS
33+
OpenJournal(name string, flags OpenFlag, db File) (File, OpenFlag, error)
34+
}
35+
2736
// A File represents an open file in the OS interface layer.
2837
//
2938
// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.

vfs/memdb/memdb.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@ import (
1111
"github.com/ncruces/go-sqlite3/vfs"
1212
)
1313

14+
// Must be a multiple of 64K (the largest page size).
15+
const sectorSize = 65536
16+
1417
type memVFS struct{}
1518

1619
func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
17-
// Allowed file types:
20+
// For simplicity, we do not support reading or writing data
21+
// across "sector" boundaries.
22+
//
23+
// This is not a problem for most SQLite file types:
1824
// - databases, which only do page aligned reads/writes;
19-
// - temp journals, used by the sorter, which does the same.
25+
// - temp journals, as used by the sorter, which does the same:
26+
// https://sqlite.org/src/artifact/237840?ln=409-412
27+
//
28+
// We refuse to open all other file types,
29+
// but returning OPEN_MEMORY means SQLite won't ask us to.
2030
const types = vfs.OPEN_MAIN_DB |
2131
vfs.OPEN_TRANSIENT_DB |
2232
vfs.OPEN_TEMP_DB |
@@ -61,9 +71,6 @@ func (memVFS) FullPathname(name string) (string, error) {
6171
return name, nil
6272
}
6373

64-
// Must be a multiple of 64K (the largest page size).
65-
const sectorSize = 65536
66-
6774
type memDB struct {
6875
// +checklocks:lockMtx
6976
pending *memFile

vfs/registry.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func Find(name string) VFS {
2323
}
2424

2525
// Register registers a VFS.
26+
// Empty and "os" are reserved names.
2627
//
2728
// https://sqlite.org/c3ref/vfs_find.html
2829
func Register(name string, vfs VFS) {

vfs/tests/mptest/testdata/build.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ WASI_SDK="$ROOT/tools/wasi-sdk-22.0/bin"
1616
-fno-stack-protector -fno-stack-clash-protection \
1717
-Wl,--stack-first \
1818
-Wl,--import-undefined \
19-
-D_HAVE_SQLITE_CONFIG_H -DHAVE_USLEEP \
19+
-D_HAVE_SQLITE_CONFIG_H -DSQLITE_USE_URI \
2020
-DSQLITE_DEFAULT_SYNCHRONOUS=0 \
2121
-DSQLITE_DEFAULT_LOCKING_MODE=0 \
2222
-DSQLITE_NO_SYNC -DSQLITE_THREADSAFE=0 \
23-
-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_USE_URI \
23+
-DSQLITE_OMIT_LOAD_EXTENSION -DHAVE_USLEEP \
2424
-D_WASI_EMULATED_GETPID -lwasi-emulated-getpid \
2525
$(awk '{print "-Wl,--export="$0}' exports.txt)
2626

0 commit comments

Comments
 (0)