Store IV when encrypting or decrypting attachments

This commit is contained in:
trevor-signal 2024-05-22 00:07:39 -04:00 committed by GitHub
parent 63be4299f4
commit e9b661873b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 136 additions and 11 deletions

View file

@ -37,6 +37,7 @@ import {
CipherType,
} from '../Crypto';
import {
type HardcodedIVForEncryptionType,
KEY_SET_LENGTH,
_generateAttachmentIv,
decryptAttachmentV2,
@ -608,19 +609,24 @@ describe('Crypto', () => {
path,
data,
plaintextHash,
encryptionKeys,
dangerousIv,
}: {
path?: string;
data: Uint8Array;
plaintextHash: Uint8Array;
encryptionKeys?: Uint8Array;
dangerousIv?: HardcodedIVForEncryptionType;
}): Promise<void> {
let plaintextPath;
let ciphertextPath;
const keys = generateAttachmentKeys();
const keys = encryptionKeys ?? generateAttachmentKeys();
try {
const encryptedAttachment = await encryptAttachmentV2ToDisk({
keys,
plaintext: path ? { absolutePath: path } : { data },
dangerousIv,
});
ciphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath(
@ -639,6 +645,21 @@ describe('Crypto', () => {
);
const plaintext = readFileSync(plaintextPath);
assert.deepStrictEqual(
encryptedAttachment.iv,
decryptedAttachment.iv
);
if (dangerousIv) {
assert.deepStrictEqual(encryptedAttachment.iv, dangerousIv.iv);
if (dangerousIv.reason === 'reencrypting-for-backup') {
assert.deepStrictEqual(
encryptedAttachment.digest,
dangerousIv.digestToMatch
);
}
}
assert.isTrue(constantTimeEqual(data, plaintext));
assert.strictEqual(
encryptedAttachment.ciphertextSize,
@ -711,6 +732,52 @@ describe('Crypto', () => {
plaintextHash,
});
});
describe('dangerousIv', () => {
it('uses hardcodedIv in tests', async () => {
await testV2RoundTripData({
data: FILE_CONTENTS,
plaintextHash: FILE_HASH,
dangerousIv: {
reason: 'test',
iv: _generateAttachmentIv(),
},
});
});
it('uses hardcodedIv when re-encrypting for backup', async () => {
const keys = generateAttachmentKeys();
const previouslyEncrypted = await encryptAttachmentV2ToDisk({
keys,
plaintext: { data: FILE_CONTENTS },
});
await testV2RoundTripData({
data: FILE_CONTENTS,
plaintextHash: FILE_HASH,
encryptionKeys: keys,
dangerousIv: {
reason: 'reencrypting-for-backup',
iv: previouslyEncrypted.iv,
digestToMatch: previouslyEncrypted.digest,
},
});
// If the digest is wrong, it should throw
await assert.isRejected(
testV2RoundTripData({
data: FILE_CONTENTS,
plaintextHash: FILE_HASH,
encryptionKeys: keys,
dangerousIv: {
reason: 'reencrypting-for-backup',
iv: previouslyEncrypted.iv,
digestToMatch: getRandomBytes(32),
},
}),
'iv was hardcoded for backup re-encryption, but digest does not match'
);
});
});
});
it('v2 -> v1 (disk -> memory)', async () => {
@ -774,7 +841,7 @@ describe('Crypto', () => {
const encryptedAttachmentV2 = await encryptAttachmentV2ToDisk({
keys,
plaintext: { absolutePath: FILE_PATH },
dangerousTestOnlyIv,
dangerousIv: { iv: dangerousTestOnlyIv, reason: 'test' },
});
ciphertextPath = window.Signal.Migrations.getAbsoluteAttachmentPath(
encryptedAttachmentV2.path