Review fixes: the derived key is wiped also when a key is damaged, each tidy-up step after a removal on its own
CI / unit (8.2) (push) Has been cancelled
CI / unit (8.3) (push) Has been cancelled
CI / unit (8.4) (push) Has been cancelled
CI / psalm (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / integration (mariadb, 32) (push) Has been cancelled
CI / integration (mariadb, 33) (push) Has been cancelled
CI / integration (mariadb, 34) (push) Has been cancelled
CI / integration (mariadb, 35) (push) Has been cancelled
CI / integration (pgsql, 32) (push) Has been cancelled
CI / integration (pgsql, 33) (push) Has been cancelled
CI / integration (pgsql, 34) (push) Has been cancelled
CI / integration (pgsql, 35) (push) Has been cancelled
CI / integration (sqlite, 32) (push) Has been cancelled
CI / integration (sqlite, 33) (push) Has been cancelled
CI / integration (sqlite, 34) (push) Has been cancelled
CI / integration (sqlite, 35) (push) Has been cancelled
CI / integration-targets (push) Has been cancelled
CI / integration-user (32) (push) Has been cancelled
CI / integration-user (33) (push) Has been cancelled
CI / integration-user (34) (push) Has been cancelled
CI / integration-user (35) (push) Has been cancelled
CI / integration-full-restore (push) Has been cancelled

- The key derived from the passphrase is wiped on every path, also when the key or nonce turns out damaged
- After a removal the pending flag and the cached index are tidied up independently; a failing log cannot stop the removal event
- Tests: a short salt or nonce gives the damaged-key error
This commit is contained in:
2026-11-09 02:33:50 +01:00
parent fb8e8d0694
commit 2a72acb70b
3 changed files with 39 additions and 8 deletions
+10 -3
View File
@@ -108,14 +108,21 @@ final class KeyRing {
|| $wrapped['mem'] < SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE || $wrapped['mem'] > SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE) {
throw new CryptoException('Unsupported key derivation parameters (damaged or foreign key file)');
}
$kek = null;
try {
$kek = sodium_crypto_pwhash(SODIUM_CRYPTO_SECRETBOX_KEYBYTES, $passphrase, $b64($wrapped['salt']),
$salt = $b64($wrapped['salt']);
$sealed = $b64($wrapped['key']);
$nonce = $b64($wrapped['nonce']);
$kek = sodium_crypto_pwhash(SODIUM_CRYPTO_SECRETBOX_KEYBYTES, $passphrase, $salt,
$wrapped['ops'], $wrapped['mem'], SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13);
$master = sodium_crypto_secretbox_open($b64($wrapped['key']), $b64($wrapped['nonce']), $kek);
sodium_memzero($kek);
$master = sodium_crypto_secretbox_open($sealed, $nonce, $kek);
} catch (\SodiumException|\ValueError $e) {
// invalid base64 or a salt/nonce of the wrong length: damaged, so the caller shows its recovery options
throw new CryptoException('Damaged key file: ' . $e->getMessage(), 0, $e);
} finally {
if ($kek !== null) {
sodium_memzero($kek);
}
}
if ($master === false) {
throw new CryptoException('Wrong passphrase or damaged key');