fix expirationTimer updates closed group desktop to ios

This commit is contained in:
Audric Ackermann 2021-01-14 16:34:05 +11:00
parent 58be168227
commit f1d84177a0
9 changed files with 135 additions and 49 deletions

View file

@ -392,7 +392,7 @@ function shouldDropBlockedUserMessage(content: SignalService.Content): boolean {
if (!content?.dataMessage?.group?.id) {
return true;
}
const groupId = StringUtils.decode(content.dataMessage.group.id, 'utf8');
const groupId = toHex(content.dataMessage.group.id);
const groupConvo = ConversationController.getInstance().get(groupId);
if (!groupConvo) {

View file

@ -1,4 +1,3 @@
import { ContentMessage } from '../ContentMessage';
import { SignalService } from '../../../../../protobuf';
import { MessageParams } from '../../Message';
import { StringUtils } from '../../../../utils';

View file

@ -1,7 +1,5 @@
import { Constants } from '../../../../..';
import { SignalService } from '../../../../../../protobuf';
import { PubKey } from '../../../../../types';
import { fromHexToArray } from '../../../../../utils/String';
import {
ClosedGroupV2Message,
ClosedGroupV2MessageParams,

View file

@ -26,6 +26,13 @@ export abstract class ClosedGroupV2Message extends DataMessage {
}
}
public static areAdminsMembers(
admins: Array<string>,
members: Array<string>
) {
return admins.every(a => members.includes(a));
}
public ttl(): number {
return TTL_DEFAULT.REGULAR_MESSAGE;
}

View file

@ -38,6 +38,10 @@ export class ClosedGroupV2NewMessage extends ClosedGroupV2Message {
if (!params.members || params.members.length === 0) {
throw new Error('Members must be set');
}
// Assert that every admins is a member
if (!ClosedGroupV2Message.areAdminsMembers(params.admins, params.members)) {
throw new Error('Admins must all be members of the group');
}
if (!params.name || params.name.length === 0) {
throw new Error('Name must cannot be empty');
}

View file

@ -98,46 +98,19 @@ export class MessageQueue implements MessageQueueInterface {
}
let groupId: PubKey | undefined;
if (message instanceof TypingMessage) {
groupId = message.groupId;
} else if (message instanceof ExpirationTimerUpdateMessage) {
groupId = message.groupId;
} else if (message instanceof ClosedGroupV2Message) {
if (
message instanceof TypingMessage ||
message instanceof ExpirationTimerUpdateMessage ||
message instanceof ClosedGroupV2Message
) {
groupId = message.groupId;
}
if (!groupId) {
throw new Error('Invalid group message passed in sendToGroup.');
}
// if this is a medium group message. We just need to send to the group pubkey
if (
message instanceof ClosedGroupV2ChatMessage ||
message instanceof ClosedGroupV2Message
) {
return this.send(PubKey.cast(groupId), message, sentCb);
}
// Get devices in group
let recipients = await GroupUtils.getGroupMembers(groupId);
// Don't send to our own device as they'll likely be synced across.
const ourKey = await UserUtil.getCurrentDevicePubKey();
if (!ourKey) {
throw new Error('Cannot get current user public key');
}
const ourPrimary = await MultiDeviceProtocol.getPrimaryDevice(ourKey);
recipients = recipients.filter(member => !ourPrimary.isEqual(member));
if (recipients.length === 0) {
return;
}
// Send to all devices of members
await Promise.all(
recipients.map(async recipient =>
this.sendUsingMultiDevice(recipient, message)
)
);
// if groupId is set here, it means it's for a medium group. So send it as it
return this.send(PubKey.cast(groupId), message, sentCb);
}
public async sendSyncMessage(

View file

@ -1,9 +1,34 @@
import { RawMessage } from '../types/RawMessage';
import { ContentMessage } from '../messages/outgoing';
import {
ContentMessage,
ExpirationTimerUpdateMessage,
TypingMessage,
} from '../messages/outgoing';
import { EncryptionType, PubKey } from '../types';
import { ClosedGroupV2Message } from '../messages/outgoing/content/data/groupv2/ClosedGroupV2Message';
import { ClosedGroupV2NewMessage } from '../messages/outgoing/content/data/groupv2/ClosedGroupV2NewMessage';
export function getEncryptionTypeFromMessageType(
message: ContentMessage
): EncryptionType {
// ClosedGroupV2NewMessage is sent using established channels, so using fallback
if (message instanceof ClosedGroupV2NewMessage) {
return EncryptionType.Fallback;
}
// 1. any ClosedGroupV2Message which is not a ClosedGroupV2NewMessage must be encoded with ClosedGroup
// 2. if TypingMessage or ExpirationTimer and groupId is set => must be encoded with ClosedGroup too
if (
message instanceof ClosedGroupV2Message ||
(message instanceof ExpirationTimerUpdateMessage && message.groupId) ||
(message instanceof TypingMessage && message.groupId)
) {
return EncryptionType.ClosedGroup;
} else {
return EncryptionType.Fallback;
}
}
export async function toRawMessage(
device: PubKey,
message: ContentMessage
@ -13,17 +38,8 @@ export async function toRawMessage(
window?.log?.debug('toRawMessage proto:', message.contentProto());
const plainTextBuffer = message.plainTextBuffer();
let encryption: EncryptionType;
const encryption = getEncryptionTypeFromMessageType(message);
// ClosedGroupV2NewMessage is sent using established channels, so using fallback
if (
message instanceof ClosedGroupV2Message &&
!(message instanceof ClosedGroupV2NewMessage)
) {
encryption = EncryptionType.ClosedGroup;
} else {
encryption = EncryptionType.Fallback;
}
// tslint:disable-next-line: no-unnecessary-local-variable
const rawMessage: RawMessage = {
identifier: message.identifier,

View file

@ -5,6 +5,12 @@ import { MessageUtils } from '../../../../session/utils';
import { EncryptionType, PubKey } from '../../../../session/types';
import { SessionProtocol } from '../../../../session/protocols';
import { ClosedGroupV2ChatMessage } from '../../../../session/messages/outgoing/content/data/groupv2/ClosedGroupV2ChatMessage';
import {
ClosedGroupV2EncryptionPairMessage,
ClosedGroupV2NewMessage,
ClosedGroupV2UpdateMessage,
} from '../../../../session/messages/outgoing';
import { SignalService } from '../../../../protobuf';
// tslint:disable-next-line: no-require-imports no-var-requires
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
@ -115,5 +121,80 @@ describe('Message Utils', () => {
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback);
});
it('passing ClosedGroupV2NewMessage returns Fallback', async () => {
const device = TestUtils.generateFakePubKey();
const member = TestUtils.generateFakePubKey().key;
const msg = new ClosedGroupV2NewMessage({
timestamp: Date.now(),
name: 'df',
members: [member],
admins: [member],
groupId: TestUtils.generateFakePubKey().key,
keypair: TestUtils.generateFakeECKeyPair(),
expireTimer: 0,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback);
});
it('passing ClosedGroupV2UpdateMessage returns ClosedGroup', async () => {
const device = TestUtils.generateFakePubKey();
const msg = new ClosedGroupV2UpdateMessage({
timestamp: Date.now(),
name: 'df',
members: [TestUtils.generateFakePubKey().key],
groupId: TestUtils.generateFakePubKey().key,
expireTimer: 0,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup);
});
it('passing ClosedGroupV2EncryptionPairMessage returns ClosedGroup', async () => {
const device = TestUtils.generateFakePubKey();
const fakeWrappers = new Array<
SignalService.ClosedGroupUpdateV2.KeyPairWrapper
>();
fakeWrappers.push(
new SignalService.ClosedGroupUpdateV2.KeyPairWrapper({
publicKey: new Uint8Array(8),
encryptedKeyPair: new Uint8Array(8),
})
);
const msg = new ClosedGroupV2EncryptionPairMessage({
timestamp: Date.now(),
groupId: TestUtils.generateFakePubKey().key,
encryptedKeyPairs: fakeWrappers,
expireTimer: 0,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup);
});
it('passing ClosedGroupV2EncryptionPairMessage returns ClosedGroup', async () => {
const device = TestUtils.generateFakePubKey();
const fakeWrappers = new Array<
SignalService.ClosedGroupUpdateV2.KeyPairWrapper
>();
fakeWrappers.push(
new SignalService.ClosedGroupUpdateV2.KeyPairWrapper({
publicKey: new Uint8Array(8),
encryptedKeyPair: new Uint8Array(8),
})
);
const msg = new ClosedGroupV2EncryptionPairMessage({
timestamp: Date.now(),
groupId: TestUtils.generateFakePubKey().key,
encryptedKeyPairs: fakeWrappers,
expireTimer: 0,
});
const rawMessage = await MessageUtils.toRawMessage(device, msg);
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup);
});
});
});

View file

@ -1,5 +1,7 @@
import * as crypto from 'crypto';
import { ECKeyPair } from '../../../receiver/closedGroupsV2';
import { PubKey } from '../../../session/types';
import { fromHexToArray } from '../../../session/utils/String';
export function generateFakePubKey(): PubKey {
// Generates a mock pubkey for testing
@ -10,6 +12,12 @@ export function generateFakePubKey(): PubKey {
return new PubKey(pubkeyString);
}
export function generateFakeECKeyPair(): ECKeyPair {
const pubkey = generateFakePubKey().toArray();
const privKey = new Uint8Array(crypto.randomBytes(64));
return new ECKeyPair(pubkey, privKey);
}
export function generateFakePubKeys(amount: number): Array<PubKey> {
const numPubKeys = amount > 0 ? Math.floor(amount) : 0;