padd Message buffer for all outgoing messages (even opengroupv2)

pull/1604/head
Audric Ackermann 4 years ago
parent 63e66b2ac0
commit 7b96aba1bf
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -9,7 +9,13 @@ let tray = null;
function createTrayIcon(getMainWindow, messages) { function createTrayIcon(getMainWindow, messages) {
// A smaller icon is needed on macOS // A smaller icon is needed on macOS
const iconSize = process.platform === 'darwin' ? '16' : '256'; const iconSize = process.platform === 'darwin' ? '16' : '256';
const iconNoNewMessages = path.join(__dirname, '..', 'images', 'session', `session_icon_${iconSize}.png`); const iconNoNewMessages = path.join(
__dirname,
'..',
'images',
'session',
`session_icon_${iconSize}.png`
);
tray = new Tray(iconNoNewMessages); tray = new Tray(iconNoNewMessages);

@ -208,9 +208,7 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
return; return;
} }
const isValidWithStoredInDB = Boolean( const isValidWithStoredInDB = Boolean(await this.validatePasswordHash(oldPassword));
await this.validatePasswordHash(oldPassword)
);
if (!isValidWithStoredInDB) { if (!isValidWithStoredInDB) {
this.setState({ this.setState({
error: window.i18n('changePasswordInvalid'), error: window.i18n('changePasswordInvalid'),

@ -17,6 +17,7 @@ import { KeyPairRequestManager } from './keyPairRequestManager';
import { requestEncryptionKeyPair } from '../session/group'; import { requestEncryptionKeyPair } from '../session/group';
import { handleConfigurationMessage } from './configMessage'; import { handleConfigurationMessage } from './configMessage';
import { ConversationTypeEnum } from '../models/conversation'; import { ConversationTypeEnum } from '../models/conversation';
import { removeMessagePadding } from '../session/crypto/MessagePadding';
export async function handleContentMessage(envelope: EnvelopePlus) { export async function handleContentMessage(envelope: EnvelopePlus) {
try { try {
@ -94,7 +95,7 @@ async function decryptForClosedGroup(envelope: EnvelopePlus, ciphertext: ArrayBu
} }
window.log.info('ClosedGroup Message decrypted successfully with keyIndex:', keyIndex); window.log.info('ClosedGroup Message decrypted successfully with keyIndex:', keyIndex);
return unpad(decryptedContent); return removeMessagePadding(decryptedContent);
} catch (e) { } catch (e) {
/** /**
* If an error happened during the decoding, * If an error happened during the decoding,
@ -130,7 +131,7 @@ async function decryptForClosedGroup(envelope: EnvelopePlus, ciphertext: ArrayBu
* or a message sent to a closed group. * or a message sent to a closed group.
* *
* We do not unpad the result here, as in the case of the keypair wrapper, there is not padding. * We do not unpad the result here, as in the case of the keypair wrapper, there is not padding.
* Instead, it is the called who needs to unpad() the content. * Instead, it is the called who needs to removeMessagePadding() the content.
*/ */
export async function decryptWithSessionProtocol( export async function decryptWithSessionProtocol(
envelope: EnvelopePlus, envelope: EnvelopePlus,
@ -191,22 +192,6 @@ export async function decryptWithSessionProtocol(
return plaintext; return plaintext;
} }
export function unpad(paddedData: ArrayBuffer): ArrayBuffer {
const paddedPlaintext = new Uint8Array(paddedData);
for (let i = paddedPlaintext.length - 1; i >= 0; i -= 1) {
if (paddedPlaintext[i] === 0x80) {
const plaintext = new Uint8Array(i);
plaintext.set(paddedPlaintext.subarray(0, i));
return plaintext.buffer;
} else if (paddedPlaintext[i] !== 0x00) {
throw new Error('Invalid padding');
}
}
throw new Error('Invalid padding');
}
export async function isBlocked(number: string) { export async function isBlocked(number: string) {
return BlockedNumberController.isBlockedAsync(number); return BlockedNumberController.isBlockedAsync(number);
} }
@ -227,7 +212,7 @@ async function decryptUnidentifiedSender(
); );
// keep the await so the try catch works as expected // keep the await so the try catch works as expected
const retSessionProtocol = await decryptWithSessionProtocol(envelope, ciphertext, ecKeyPair); const retSessionProtocol = await decryptWithSessionProtocol(envelope, ciphertext, ecKeyPair);
return unpad(retSessionProtocol); return removeMessagePadding(retSessionProtocol);
} catch (e) { } catch (e) {
window.log.warn('decryptWithSessionProtocol for unidentified message throw:', e); window.log.warn('decryptWithSessionProtocol for unidentified message throw:', e);
return null; return null;

@ -9,7 +9,7 @@ import { processMessage } from '../session/snode_api/swarmPolling';
import { onError } from './errors'; import { onError } from './errors';
// innerHandleContentMessage is only needed because of code duplication in handleDecryptedEnvelope... // innerHandleContentMessage is only needed because of code duplication in handleDecryptedEnvelope...
import { handleContentMessage, innerHandleContentMessage, unpad } from './contentMessage'; import { handleContentMessage, innerHandleContentMessage } from './contentMessage';
import _, { noop } from 'lodash'; import _, { noop } from 'lodash';
export { processMessage }; export { processMessage };
@ -36,6 +36,7 @@ import { OpenGroupMessageV2 } from '../opengroup/opengroupV2/OpenGroupMessageV2'
import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil'; import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
import { handleMessageJob } from './queuedJob'; import { handleMessageJob } from './queuedJob';
import { fromBase64ToArray } from '../session/utils/String'; import { fromBase64ToArray } from '../session/utils/String';
import { removeMessagePadding } from '../session/crypto/MessagePadding';
// TODO: check if some of these exports no longer needed // TODO: check if some of these exports no longer needed
@ -315,7 +316,8 @@ export async function handleOpenGroupV2Message(
return; return;
} }
const dataUint = new Uint8Array(unpad(fromBase64ToArray(base64EncodedData))); // Note: opengroup messages are not padded
const dataUint = new Uint8Array(removeMessagePadding(fromBase64ToArray(base64EncodedData)));
const decoded = SignalService.Content.decode(dataUint); const decoded = SignalService.Content.decode(dataUint);

@ -6,29 +6,7 @@ import { fromHexToArray } from '../utils/String';
export { concatUInt8Array, getSodium }; export { concatUInt8Array, getSodium };
import { getLatestClosedGroupEncryptionKeyPair } from '../../../ts/data/data'; import { getLatestClosedGroupEncryptionKeyPair } from '../../../ts/data/data';
import { UserUtils } from '../utils'; import { UserUtils } from '../utils';
import { addMessagePadding } from './MessagePadding';
/**
* Add padding to a message buffer
* @param messageBuffer The buffer to add padding to.
*/
export function padPlainTextBuffer(messageBuffer: Uint8Array): Uint8Array {
const plaintext = new Uint8Array(getPaddedMessageLength(messageBuffer.byteLength + 1) - 1);
plaintext.set(new Uint8Array(messageBuffer));
plaintext[messageBuffer.byteLength] = 0x80;
return plaintext;
}
function getPaddedMessageLength(originalLength: number): number {
const messageLengthWithTerminator = originalLength + 1;
let messagePartCount = Math.floor(messageLengthWithTerminator / 160);
if (messageLengthWithTerminator % 160 !== 0) {
messagePartCount += 1;
}
return messagePartCount * 160;
}
type EncryptResult = { type EncryptResult = {
envelopeType: SignalService.Envelope.Type; envelopeType: SignalService.Envelope.Type;
@ -53,7 +31,7 @@ export async function encrypt(
throw new Error(`Invalid encryption type:${encryptionType}`); throw new Error(`Invalid encryption type:${encryptionType}`);
} }
const encryptForClosedGroup = encryptionType === EncryptionType.ClosedGroup; const encryptForClosedGroup = encryptionType === EncryptionType.ClosedGroup;
const plainText = padPlainTextBuffer(plainTextBuffer); const plainText = addMessagePadding(plainTextBuffer);
if (encryptForClosedGroup) { if (encryptForClosedGroup) {
window?.log?.info( window?.log?.info(

@ -0,0 +1,45 @@
/**
* Unpad the buffer from its padding.
* An error is thrown if there is no padding.
* A padded buffer is
* * whatever at start
* * ends with 0x80 and any number of 0x00 until the end
*/
export function removeMessagePadding(paddedData: ArrayBuffer): ArrayBuffer {
const paddedPlaintext = new Uint8Array(paddedData);
for (let i = paddedPlaintext.length - 1; i >= 0; i -= 1) {
if (paddedPlaintext[i] === 0x80) {
const plaintext = new Uint8Array(i);
plaintext.set(paddedPlaintext.subarray(0, i));
return plaintext.buffer;
} else if (paddedPlaintext[i] !== 0x00) {
throw new Error('Invalid padding');
}
}
throw new Error('Invalid padding');
}
/**
* Add padding to a message buffer
* @param messageBuffer The buffer to add padding to.
*/
export function addMessagePadding(messageBuffer: Uint8Array): Uint8Array {
const plaintext = new Uint8Array(getPaddedMessageLength(messageBuffer.byteLength + 1) - 1);
plaintext.set(new Uint8Array(messageBuffer));
plaintext[messageBuffer.byteLength] = 0x80;
return plaintext;
}
function getPaddedMessageLength(originalLength: number): number {
const messageLengthWithTerminator = originalLength + 1;
let messagePartCount = Math.floor(messageLengthWithTerminator / 160);
if (messageLengthWithTerminator % 160 !== 0) {
messagePartCount += 1;
}
return messagePartCount * 160;
}

@ -10,10 +10,10 @@ import { UserUtils } from '../utils';
import { OpenGroupRequestCommonType } from '../../opengroup/opengroupV2/ApiUtil'; import { OpenGroupRequestCommonType } from '../../opengroup/opengroupV2/ApiUtil';
import { postMessage } from '../../opengroup/opengroupV2/OpenGroupAPIV2'; import { postMessage } from '../../opengroup/opengroupV2/OpenGroupAPIV2';
import { OpenGroupMessageV2 } from '../../opengroup/opengroupV2/OpenGroupMessageV2'; import { OpenGroupMessageV2 } from '../../opengroup/opengroupV2/OpenGroupMessageV2';
import { padPlainTextBuffer } from '../crypto/MessageEncrypter';
import { fromUInt8ArrayToBase64 } from '../utils/String'; import { fromUInt8ArrayToBase64 } from '../utils/String';
import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
import * as LokiMessageApi from './LokiMessageApi'; import * as LokiMessageApi from './LokiMessageApi';
import { addMessagePadding } from '../crypto/MessagePadding';
// ================ Regular ================ // ================ Regular ================
@ -136,7 +136,8 @@ export async function sendToOpenGroupV2(
rawMessage: OpenGroupVisibleMessage, rawMessage: OpenGroupVisibleMessage,
roomInfos: OpenGroupRequestCommonType roomInfos: OpenGroupRequestCommonType
): Promise<OpenGroupMessageV2> { ): Promise<OpenGroupMessageV2> {
const paddedBody = padPlainTextBuffer(rawMessage.plainTextBuffer()); // we agreed to pad message for opengroupv2
const paddedBody = addMessagePadding(rawMessage.plainTextBuffer());
const v2Message = new OpenGroupMessageV2({ const v2Message = new OpenGroupMessageV2({
sentTimestamp: Date.now(), sentTimestamp: Date.now(),
sender: UserUtils.getOurPubKeyStrFromCache(), sender: UserUtils.getOurPubKeyStrFromCache(),

@ -74,6 +74,7 @@ export class AttachmentUtils {
let attachmentData: ArrayBuffer; let attachmentData: ArrayBuffer;
// We don't pad attachments for opengroup as they are unencrypted
if (isRaw || openGroup) { if (isRaw || openGroup) {
attachmentData = attachment.data; attachmentData = attachment.data;
} else { } else {

@ -11,6 +11,7 @@ import { StringUtils, UserUtils } from '../../../../session/utils';
import chaiBytes from 'chai-bytes'; import chaiBytes from 'chai-bytes';
import { PubKey } from '../../../../session/types'; import { PubKey } from '../../../../session/types';
import { fromHex, toHex } from '../../../../session/utils/String'; import { fromHex, toHex } from '../../../../session/utils/String';
import { addMessagePadding } from '../../../../session/crypto/MessagePadding';
chai.use(chaiBytes); chai.use(chaiBytes);
@ -187,7 +188,7 @@ describe('MessageEncrypter', () => {
const spy = sinon.spy(MessageEncrypter, 'encryptUsingSessionProtocol'); const spy = sinon.spy(MessageEncrypter, 'encryptUsingSessionProtocol');
await MessageEncrypter.encrypt(TestUtils.generateFakePubKey(), data, EncryptionType.Fallback); await MessageEncrypter.encrypt(TestUtils.generateFakePubKey(), data, EncryptionType.Fallback);
chai.expect(spy.callCount).to.be.equal(1); chai.expect(spy.callCount).to.be.equal(1);
const paddedData = MessageEncrypter.padPlainTextBuffer(data); const paddedData = addMessagePadding(data);
const firstArgument = new Uint8Array(spy.args[0][1]); const firstArgument = new Uint8Array(spy.args[0][1]);
chai.expect(firstArgument).to.deep.equal(paddedData); chai.expect(firstArgument).to.deep.equal(paddedData);
spy.restore(); spy.restore();

Loading…
Cancel
Save