1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//! `sqlite3mc` encryption configuration.
//!
//! # Encryption flow
//!
//! The credential storage uses `sqlite3mc` (`SQLite3` Multiple Ciphers) to
//! encrypt both the vault and cache databases at rest. The encryption is
//! transparent to SQL -- once a database is opened and keyed, all reads and
//! writes are automatically encrypted/decrypted by the `SQLite` pager layer.
//!
//! The flow when opening a database is:
//!
//! 1. **Open** -- `sqlite3_open_v2` creates or opens the database file.
//! At this point the file is opaque (encrypted) and no data can be read.
//!
//! 2. **Key** -- `PRAGMA key = "x'<hex>'"` passes the 32-byte
//! `K_intermediate` (hex-encoded) to `sqlite3mc`. Internally, `sqlite3mc`
//! derives a page-level encryption key from this material using the
//! configured KDF (PBKDF2-SHA256 by default for ChaCha20-Poly1305).
//! After this point, every page read from disk is decrypted and every
//! page written to disk is encrypted.
//!
//! 3. **Verify** -- We immediately read from `sqlite_master` to confirm
//! the key is correct. If the key is wrong, `sqlite3mc` returns
//! `SQLITE_NOTADB` because the decrypted page header won't match the
//! expected `SQLite` magic bytes. We surface this as a clear error.
//!
//! 4. **Configure** -- WAL journal mode and `synchronous=FULL` are set for
//! crash consistency. Foreign keys are enabled.
//!
//! The default cipher is **ChaCha20-Poly1305** (authenticated encryption).
//! All crypto is built into the `sqlite3mc` amalgamation -- no OpenSSL or
//! other external crypto library is needed on any platform.
use Path;
use Zeroizing;
use Connection;
use ;
/// Opens a database, applies the encryption key, and configures the connection.
///
/// This is the standard open sequence used by both vault and cache databases:
/// open -> key -> verify -> configure (WAL + foreign keys).
///
/// See the [module-level documentation](self) for the full encryption flow.
///
/// # Errors
///
/// Returns `DbError` if opening, keying, or configuring the connection fails.
/// Applies the `sqlite3mc` encryption key to an open connection.
///
/// The 32-byte `k_intermediate` is hex-encoded and passed as a raw key via
/// `PRAGMA key = "x'<64-hex-chars>'"`. `sqlite3mc` interprets the `x'...'`
/// prefix as a raw key (as opposed to a passphrase that would be run through
/// a KDF first).
///
/// After keying, a lightweight read (`SELECT count(*) FROM sqlite_master`)
/// verifies the key is correct. If it's wrong, `sqlite3mc` fails with
/// `SQLITE_NOTADB` on the first page read.
/// Configures durable WAL settings, foreign keys, and secure deletion.
///
/// - `journal_mode = WAL` -- enables concurrent readers during writes.
/// - `synchronous = FULL` -- maximizes crash consistency (all WAL pages are
/// fsynced before the transaction is reported as committed).
/// - `foreign_keys = ON` -- enforces referential integrity constraints.
/// - `secure_delete = ON` -- overwrites deleted content with zeroes so
/// sensitive data does not linger in free pages.
/// Tables included in plaintext vault backups.
///
/// `vault_meta` is intentionally excluded: on restore, the destination vault
/// already has its own `vault_meta` (created by `ensure_schema` + `init_leaf_index`)
/// with the authoritative `leaf_index` from the authenticator.
///
/// **Note:** If new tables are added to the vault schema, this list must be
/// updated to include them.
pub const BACKUP_TABLES: & = &;
/// Creates a plaintext (unencrypted) copy of an already-open encrypted database.
///
/// The copy is produced by `ATTACH`-ing a new unencrypted database and copying
/// all rows via `CREATE TABLE ... AS SELECT *`. The destination file must not
/// already exist.
///
/// We use `ATTACH` + SQL instead of the `sqlite3_backup` API because
/// `sqlite3mc` requires both source and destination to share the same
/// encryption configuration. Since the destination is unencrypted, the
/// backup API cannot be used.
///
/// # Errors
///
/// Returns `DbError` if the `ATTACH`, copy, or `DETACH` fails.
/// Imports data from a plaintext (unencrypted) database into an already-open
/// encrypted database.
///
/// The source database is `ATTACH`ed with an empty key and its contents are
/// copied into the main (empty) encrypted database. This is intended for
/// restore on a fresh install where the vault tables exist but contain no data.
///
/// See [`export_plaintext_copy`] for why `ATTACH` + SQL is used instead of
/// the `sqlite3_backup` API.
///
/// **Schema migration:** The import uses `SELECT *`, so column changes are
/// handled automatically as long as both sides share the same schema. If the
/// vault schema evolves (e.g. new columns with `NOT NULL` constraints),
/// restoring an older backup into a newer schema will fail. When that happens,
/// this function will need version-aware import logic.
///
/// # Errors
///
/// Returns `DbError` if the `ATTACH`, copy, or `DETACH` fails.
/// Runs `PRAGMA integrity_check` and returns whether the database is healthy.
///
/// # Errors
///
/// Returns `DbError` if the integrity check query fails.