Ensure attachments are re-encryptable to same digest

This commit is contained in:
trevor-signal 2024-10-04 01:52:29 -04:00 committed by GitHub
parent 7d25988888
commit 6e1fd5958e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1250 additions and 295 deletions

View file

@ -2,7 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import createDebug from 'debug';
import { assert } from 'chai';
import { expect } from 'playwright/test';
import { readFileSync } from 'fs';
import { type PrimaryDevice, StorageState } from '@signalapp/mock-server';
import * as path from 'path';
import type { App } from '../playwright';
@ -15,9 +17,25 @@ import {
} from '../helpers';
import * as durations from '../../util/durations';
import { strictAssert } from '../../util/assert';
import {
encryptAttachmentV2ToDisk,
generateAttachmentKeys,
} from '../../AttachmentCrypto';
import { toBase64 } from '../../Bytes';
import type { AttachmentWithNewReencryptionInfoType } from '../../types/Attachment';
import { IMAGE_JPEG } from '../../types/MIME';
export const debug = createDebug('mock:test:attachments');
const CAT_PATH = path.join(
__dirname,
'..',
'..',
'..',
'fixtures',
'cat-screenshot.png'
);
describe('attachments', function (this: Mocha.Suite) {
this.timeout(durations.MINUTE);
@ -65,7 +83,7 @@ describe('attachments', function (this: Mocha.Suite) {
page,
pinned,
'This is my cat',
[path.join(__dirname, '..', '..', '..', 'fixtures', 'cat-screenshot.png')]
[CAT_PATH]
);
const Message = getTimelineMessageWithText(page, 'This is my cat');
@ -78,6 +96,17 @@ describe('attachments', function (this: Mocha.Suite) {
const timestamp = await Message.getAttribute('data-testid');
strictAssert(timestamp, 'timestamp must exist');
const sentMessage = (
await app.getMessagesBySentAt(parseInt(timestamp, 10))
)[0];
strictAssert(sentMessage, 'message exists in DB');
const sentAttachment = sentMessage.attachments?.[0];
assert.isTrue(sentAttachment?.isReencryptableToSameDigest);
assert.isUndefined(
(sentAttachment as unknown as AttachmentWithNewReencryptionInfoType)
.reencryptionInfo
);
// For this test, just send back the same attachment that was uploaded to test a
// round-trip
const incomingTimestamp = Date.now();
@ -95,5 +124,95 @@ describe('attachments', function (this: Mocha.Suite) {
'img.module-image__image'
)
).toBeVisible();
const incomingMessage = (
await app.getMessagesBySentAt(incomingTimestamp)
)[0];
strictAssert(incomingMessage, 'message exists in DB');
const incomingAttachment = incomingMessage.attachments?.[0];
assert.isTrue(incomingAttachment?.isReencryptableToSameDigest);
assert.isUndefined(
(incomingAttachment as unknown as AttachmentWithNewReencryptionInfoType)
.reencryptionInfo
);
assert.strictEqual(incomingAttachment?.key, sentAttachment?.key);
assert.strictEqual(incomingAttachment?.digest, sentAttachment?.digest);
});
it('receiving attachments with non-zero padding will cause new re-encryption info to be generated', async () => {
const page = await app.getWindow();
await page.getByTestId(pinned.device.aci).click();
const plaintextCat = readFileSync(CAT_PATH);
const cdnKey = 'cdnKey';
const keys = generateAttachmentKeys();
const cdnNumber = 3;
const { digest: newDigest, path: ciphertextPath } =
await encryptAttachmentV2ToDisk({
keys,
plaintext: {
// add non-zero byte to the end of the data; this will be considered padding
// when received since we will include the size of the un-appended data when
// sending
data: Buffer.concat([plaintextCat, Buffer.from([1])]),
},
getAbsoluteAttachmentPath: relativePath =>
bootstrap.getAbsoluteAttachmentPath(relativePath),
});
const ciphertextCatWithNonZeroPadding = readFileSync(
bootstrap.getAbsoluteAttachmentPath(ciphertextPath)
);
bootstrap.server.storeAttachmentOnCdn(
cdnNumber,
cdnKey,
ciphertextCatWithNonZeroPadding
);
const incomingTimestamp = Date.now();
await sendTextMessage({
from: pinned,
to: bootstrap.desktop,
desktop: bootstrap.desktop,
text: 'Wait, that is MY cat! But now with weird padding!',
attachments: [
{
size: plaintextCat.byteLength,
contentType: IMAGE_JPEG,
cdnKey,
cdnNumber,
key: keys,
digest: newDigest,
},
],
timestamp: incomingTimestamp,
});
await expect(
getMessageInTimelineByTimestamp(page, incomingTimestamp).locator(
'img.module-image__image'
)
).toBeVisible();
const incomingMessage = (
await app.getMessagesBySentAt(incomingTimestamp)
)[0];
strictAssert(incomingMessage, 'message exists in DB');
const incomingAttachment = incomingMessage.attachments?.[0];
assert.isFalse(incomingAttachment?.isReencryptableToSameDigest);
assert.exists(incomingAttachment?.reencryptionInfo);
assert.exists(incomingAttachment?.reencryptionInfo.digest);
assert.strictEqual(incomingAttachment?.key, toBase64(keys));
assert.strictEqual(incomingAttachment?.digest, toBase64(newDigest));
assert.notEqual(
incomingAttachment?.digest,
incomingAttachment.reencryptionInfo.digest
);
});
});