DC-019: fix flaky backup-manager tamper test (authTag byte corruption)
The 'rejects tampered data (auth tag mismatch)' test corrupted the encrypted blob by replacing its first base64 char with 'X'. When the random 16-byte IV's first base64 char was already 'X' (~1/64 chance), the replacement was a no-op and decryption succeeded — causing the test to flake ~1.6% of runs. Fix: parse the iv:authTag:ciphertext format, XOR the first authTag byte with 0xFF (guaranteed to change the value), reassemble. This reliably triggers the AES-256-GCM integrity failure every time. Verified: 30/30 isolated runs + 8/8 full-suite runs (1036/1036), zero failures. The production encryptBackup/decryptBackup (AES-256-GCM) code is correct and unchanged.
This commit is contained in:
@@ -184,9 +184,16 @@ describe('BackupManager — backup/restore lifecycle', () => {
|
||||
it('rejects tampered data (auth tag mismatch)', async () => {
|
||||
const data = Buffer.from('test');
|
||||
const encrypted = await backupManager.encryptBackup(data, testKey);
|
||||
// Corrupt the first character of the IV
|
||||
const str = encrypted.toString();
|
||||
const tampered = Buffer.from('X' + str.substring(1));
|
||||
// Corrupt the authTag so the GCM integrity check is guaranteed to fail.
|
||||
// The format is iv:authTag:ciphertext (all base64). We flip all bits of
|
||||
// the first authTag byte — XOR with 0xFF always changes the value, so
|
||||
// this can never be a no-op (unlike replacing a base64 char with a fixed
|
||||
// char, which collides ~1/64 of the time when that char already matches).
|
||||
const parts = encrypted.toString().split(':');
|
||||
const authTagBuf = Buffer.from(parts[1], 'base64');
|
||||
authTagBuf[0] ^= 0xFF;
|
||||
parts[1] = authTagBuf.toString('base64');
|
||||
const tampered = Buffer.from(parts.join(':'));
|
||||
await expect(backupManager.decryptBackup(tampered, testKey))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user