diff --git a/package.json b/package.json index cf335e7f1..fcd86d8f7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.5.3", + "version": "1.5.4", "license": "GPL-3.0", "author": { "name": "Loki Project", diff --git a/preload.js b/preload.js index 2f250136d..24757e0d0 100644 --- a/preload.js +++ b/preload.js @@ -62,6 +62,7 @@ window.lokiFeatureFlags = { useFileOnionRequestsV2: true, // more compact encoding of files in response onionRequestHops: 3, useRequestEncryptionKeyPair: false, + padOutgoingAttachments: false, }; if ( 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..ba21e940b 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,13 @@ export class AttachmentUtils { server = this.getDefaultServer(); pointer.key = new Uint8Array(crypto.randomBytes(64)); const iv = new Uint8Array(crypto.randomBytes(16)); + + const dataToEncrypt = + !shouldPad || !window.lokiFeatureFlags.padOutgoingAttachments + ? attachment.data + : AttachmentUtils.addAttachmentPadding(attachment.data); const data = await window.textsecure.crypto.encryptAttachment( - attachment.data, + dataToEncrypt, pointer.key.buffer, iv.buffer ); @@ -126,6 +140,7 @@ export class AttachmentUtils { this.upload({ attachment, openGroup, + shouldPad: true, }) ); @@ -181,4 +196,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; + } } diff --git a/ts/window.d.ts b/ts/window.d.ts index ea0eb1920..e3a1f6e1f 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -62,6 +62,7 @@ declare global { useFileOnionRequestsV2: boolean; onionRequestHops: number; useRequestEncryptionKeyPair: boolean; + padOutgoingAttachments: boolean; }; lokiFileServerAPI: LokiFileServerInstance; lokiMessageAPI: LokiMessageInterface;