mirror of
https://github.com/nextcloud/server.git
synced 2026-06-12 02:00:51 -04:00
fix(security): don't propagate ValueError from Crypto::decrypt() fallback
When decrypting a v3 ciphertext with a mismatched secret, the first attempt throws an Exception (HMAC mismatch). The fallback then calls decryptWithoutSecret() with an empty string, which causes hash_hkdf() to throw a ValueError. Since ValueError extends \Error rather than \Exception, it bypassed the catch block and propagated as an unhandled error, crashing the whole request. Wrap the fallback in its own try/catch(\Throwable) and rethrow the original Exception so callers get a meaningful HMAC mismatch error. Signed-off-by: Anna Larch <anna@nextcloud.com> AI-Assisted-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0a4d4bc8ab
commit
29f43d8e7a
2 changed files with 28 additions and 1 deletions
|
|
@ -102,7 +102,13 @@ class Crypto implements ICrypto {
|
|||
} catch (Exception $e) {
|
||||
if ($password === '') {
|
||||
// Retry with empty secret as a fallback for instances where the secret might not have been set by accident
|
||||
return $this->decryptWithoutSecret($authenticatedCiphertext, '');
|
||||
try {
|
||||
return $this->decryptWithoutSecret($authenticatedCiphertext, '');
|
||||
} catch (\Throwable) {
|
||||
// Fallback failed (e.g. v3 ciphertext requires a non-empty key for hash_hkdf),
|
||||
// rethrow the original exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,27 @@ class CryptoTest extends \Test\TestCase {
|
|||
);
|
||||
}
|
||||
|
||||
public function testDecryptWithWrongSecretThrowsHmacExceptionNotValueError(): void {
|
||||
// Encrypt with 'secret-A'
|
||||
$configA = $this->createMock(IConfig::class);
|
||||
$configA->method('getSystemValue')->with('secret')->willReturn('secret-A');
|
||||
$configA->method('getSystemValueString')->with('secret')->willReturn('secret-A');
|
||||
$cryptoA = new Crypto($configA);
|
||||
$ciphertext = $cryptoA->encrypt('plaintext');
|
||||
|
||||
// Decrypt with 'secret-B': first attempt fails (HMAC mismatch), fallback to empty
|
||||
// string must not propagate a ValueError for v3 ciphertexts — it must rethrow the
|
||||
// original HMAC exception instead.
|
||||
$configB = $this->createMock(IConfig::class);
|
||||
$configB->method('getSystemValue')->with('secret')->willReturn('secret-B');
|
||||
$configB->method('getSystemValueString')->with('secret')->willReturn('secret-B');
|
||||
$cryptoB = new Crypto($configB);
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('HMAC does not match.');
|
||||
$cryptoB->decrypt($ciphertext);
|
||||
}
|
||||
|
||||
public function testVersion3CiphertextDecryptsToCorrectPlaintext(): void {
|
||||
$this->assertSame(
|
||||
'Another plaintext value that will be encrypted with version 3. It addresses the related key issue. Old ciphertexts should be decrypted properly, but only use the better version for encryption.',
|
||||
|
|
|
|||
Loading…
Reference in a new issue