From 1a2d1484823cc4170b3da85bf8d7a08f287dab3b Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 5 Mar 2021 16:55:25 +1100 Subject: [PATCH] add attachment padding --- ts/receiver/attachments.ts | 14 ++++++-- ts/session/utils/Attachments.ts | 57 +++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/ts/receiver/attachments.ts b/ts/receiver/attachments.ts index c67497594..598436dec 100644 --- a/ts/receiver/attachments.ts +++ b/ts/receiver/attachments.ts @@ -3,6 +3,7 @@ import _ from 'lodash'; import { MessageModel } from '../models/message'; import { saveMessage } from '../../ts/data/data'; import { fromBase64ToArrayBuffer } from '../session/utils/String'; +import { AttachmentUtils } from '../session/utils'; export async function downloadAttachment(attachment: any) { const serverUrl = new URL(attachment.url).origin; @@ -70,9 +71,16 @@ export async function downloadAttachment(attachment: any) { ); if (!size || size !== data.byteLength) { - throw new Error( - `downloadAttachment: Size ${size} did not match downloaded attachment size ${data.byteLength}` - ); + // we might have padding, check that all the remaining bytes are padding bytes + // otherwise we have an error. + if (AttachmentUtils.isLeftOfBufferPaddingOnly(data, size)) { + // we can safely remove the padding + data = data.slice(0, size); + } else { + throw new Error( + `downloadAttachment: Size ${size} did not match downloaded attachment size ${data.byteLength}` + ); + } } } diff --git a/ts/session/utils/Attachments.ts b/ts/session/utils/Attachments.ts index f81ec0625..97e99b4e5 100644 --- a/ts/session/utils/Attachments.ts +++ b/ts/session/utils/Attachments.ts @@ -14,6 +14,7 @@ interface UploadParams { openGroup?: OpenGroup; isAvatar?: boolean; isRaw?: boolean; + shouldPad?: boolean; } interface RawPreview { @@ -37,6 +38,8 @@ interface RawQuote { // tslint:disable-next-line: no-unnecessary-class export class AttachmentUtils { + public static readonly PADDING_BYTE = 0; + private constructor() {} public static getDefaultServer(): LokiAppDotNetServerInterface { @@ -44,7 +47,13 @@ export class AttachmentUtils { } public static async upload(params: UploadParams): Promise { - const { attachment, openGroup, isAvatar = false, isRaw = false } = params; + const { + attachment, + openGroup, + isAvatar = false, + isRaw = false, + shouldPad = false, + } = params; if (typeof attachment !== 'object' || attachment == null) { throw new Error('Invalid attachment passed.'); } @@ -83,8 +92,12 @@ export class AttachmentUtils { server = this.getDefaultServer(); pointer.key = new Uint8Array(crypto.randomBytes(64)); const iv = new Uint8Array(crypto.randomBytes(16)); + + const dataToEncrypt = !shouldPad + ? attachment.data + : AttachmentUtils.addAttachmentPadding(attachment.data); const data = await window.textsecure.crypto.encryptAttachment( - attachment.data, + dataToEncrypt, pointer.key.buffer, iv.buffer ); @@ -126,6 +139,7 @@ export class AttachmentUtils { this.upload({ attachment, openGroup, + shouldPad: true, }) ); @@ -181,4 +195,43 @@ export class AttachmentUtils { attachments, }; } + + public static isLeftOfBufferPaddingOnly( + data: ArrayBuffer, + unpaddedExpectedSize: number + ): boolean { + // to have a padding we must have a strictly longer length expected + if (data.byteLength <= unpaddedExpectedSize) { + return false; + } + const dataUint = new Uint8Array(data); + for (let i = unpaddedExpectedSize; i < data.byteLength; i++) { + if (dataUint[i] !== this.PADDING_BYTE) { + return false; + } + } + + return true; + } + + private static addAttachmentPadding(data: ArrayBuffer): ArrayBuffer { + const originalUInt = new Uint8Array(data); + + const paddedSize = Math.max( + 541, + Math.floor( + Math.pow( + 1.05, + Math.ceil(Math.log(originalUInt.length) / Math.log(1.05)) + ) + ) + ); + const paddedData = new ArrayBuffer(paddedSize); + const paddedUInt = new Uint8Array(paddedData); + + paddedUInt.fill(AttachmentUtils.PADDING_BYTE, originalUInt.length); + paddedUInt.set(originalUInt); + + return paddedUInt.buffer; + } }