Problem
MdkSqliteStorage::new() can reuse a stale keyring entry when the database file no longer exists.
The constructor pre-creates the database file and, when the file was created by this call, currently calls get_or_create_db_key(service_id, db_key_id). If the app data directory was removed but the platform secret store kept the old key, MDK creates a new SQLCipher database using the old database key.
This can happen after app uninstall/reinstall on platforms where Keychain/Keystore-backed values or restored credential files outlive the database file. It also shows up during downgrade testing after #270: the fixed build rewrites raw 32-byte keys as a 62-byte mdk-sqlite-key-v1:<base64> payload, and an older build then rejects that payload as an invalid 32-byte key.
The downgrade failure is expected, but fresh database creation should not reuse a key from a previous database lifecycle.
Affected path
MdkSqliteStorage::new(...)
- Automatic key management through
(service_id, db_key_id)
- Host apps with per-account MLS databases, such as Whitenoise using
mdk.db.key.<pubkey>
Fix
When precreate_secure_database_file() returns Created, treat that as a fresh database lifecycle:
- delete any existing keyring entry for
(service_id, db_key_id), ignoring NoEntry
- generate and store a new 32-byte key
- open the new database with that key
Keep the existing behavior for AlreadyExisted:
- use
get_db_key(...)
- return the existing missing-key error if an encrypted database exists without a key
- do not generate a replacement key for an existing encrypted database
Tests
Add coverage for:
- stale keyring entry exists but DB file is absent:
new() creates a DB with a new key
- existing encrypted DB reopens with the existing key
- existing encrypted DB with missing key still errors
- legacy raw 32-byte key migration still works for existing databases
Problem
MdkSqliteStorage::new()can reuse a stale keyring entry when the database file no longer exists.The constructor pre-creates the database file and, when the file was created by this call, currently calls
get_or_create_db_key(service_id, db_key_id). If the app data directory was removed but the platform secret store kept the old key, MDK creates a new SQLCipher database using the old database key.This can happen after app uninstall/reinstall on platforms where Keychain/Keystore-backed values or restored credential files outlive the database file. It also shows up during downgrade testing after #270: the fixed build rewrites raw 32-byte keys as a 62-byte
mdk-sqlite-key-v1:<base64>payload, and an older build then rejects that payload as an invalid 32-byte key.The downgrade failure is expected, but fresh database creation should not reuse a key from a previous database lifecycle.
Affected path
MdkSqliteStorage::new(...)(service_id, db_key_id)mdk.db.key.<pubkey>Fix
When
precreate_secure_database_file()returnsCreated, treat that as a fresh database lifecycle:(service_id, db_key_id), ignoringNoEntryKeep the existing behavior for
AlreadyExisted:get_db_key(...)Tests
Add coverage for:
new()creates a DB with a new key