add the request and reply of an encryptionKeyPair if needed

pull/1498/head
Audric Ackermann 4 years ago
parent 94a9faec9a
commit 375c5ba1a8

@ -10,7 +10,6 @@ export interface CryptoInterface {
EncryptGCM: any; // AES-GCM
PairingType: PairingTypeEnum;
_decodeSnodeAddressToPubKey: any;
decryptForPubkey: any;
decryptToken: any;
encryptForPubkey: any;
generateEphemeralKeyPair: any;

@ -69,14 +69,6 @@
return { ciphertext, symmetricKey, ephemeralKey: ephemeral.pubKey };
}
async function decryptForPubkey(seckeyX25519, ephemKey, ciphertext) {
const symmetricKey = await deriveSymmetricKey(ephemKey, seckeyX25519);
const plaintext = await DecryptGCM(symmetricKey, ciphertext);
return plaintext;
}
async function EncryptGCM(symmetricKey, plaintext) {
const nonce = crypto.getRandomValues(new Uint8Array(NONCE_LENGTH));
@ -187,7 +179,6 @@
PairingType,
generateEphemeralKeyPair,
encryptForPubkey,
decryptForPubkey,
_decodeSnodeAddressToPubKey: decodeSnodeAddressToPubKey,
sha512,
};

@ -156,13 +156,14 @@ message DataMessage {
message ClosedGroupControlMessage {
enum Type {
NEW = 1; // publicKey, name, encryptionKeyPair, members, admins
UPDATE = 2; // name, members
ENCRYPTION_KEY_PAIR = 3; // wrappers
NAME_CHANGE = 4; // name
MEMBERS_ADDED = 5; // members
MEMBERS_REMOVED = 6; // members
MEMBER_LEFT = 7;
NEW = 1; // publicKey, name, encryptionKeyPair, members, admins
UPDATE = 2; // name, members
ENCRYPTION_KEY_PAIR = 3; // publicKey, wrappers
NAME_CHANGE = 4; // name
MEMBERS_ADDED = 5; // members
MEMBERS_REMOVED = 6; // members
MEMBER_LEFT = 7;
ENCRYPTION_KEY_PAIR_REQUEST = 8;
}

@ -4,6 +4,7 @@ import _ from 'lodash';
export async function removeFromCache(envelope: EnvelopePlus) {
const { id } = envelope;
window.log.info(`removing from cache envelope: ${id}`);
return window.textsecure.storage.unprocessed.remove(id);
}

@ -14,6 +14,8 @@ import { getMessageQueue } from '../session';
import { decryptWithSessionProtocol } from './contentMessage';
import {
addClosedGroupEncryptionKeyPair,
getAllEncryptionKeyPairsForGroup,
getLatestClosedGroupEncryptionKeyPair,
removeAllClosedGroupEncryptionKeyPairs,
} from '../../js/modules/data';
import {
@ -27,6 +29,8 @@ import { ConversationModel } from '../../js/models/conversations';
import _ from 'lodash';
import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils';
import { MessageController } from '../session/messages';
import { ClosedGroupEncryptionPairMessage } from '../session/messages/outgoing';
import { ClosedGroupEncryptionPairReplyMessage } from '../session/messages/outgoing/content/data/group';
export async function handleClosedGroupControlMessage(
envelope: EnvelopePlus,
@ -45,7 +49,13 @@ export async function handleClosedGroupControlMessage(
}
// We drop New closed group message from our other devices, as they will come as ConfigurationMessage instead
if (type === Type.ENCRYPTION_KEY_PAIR) {
await handleClosedGroupEncryptionKeyPair(envelope, groupUpdate);
const isComingFromGroupPubkey =
envelope.type === SignalService.Envelope.Type.CLOSED_GROUP_CIPHERTEXT;
await handleClosedGroupEncryptionKeyPair(
envelope,
groupUpdate,
isComingFromGroupPubkey
);
} else if (type === Type.NEW) {
await handleNewClosedGroup(envelope, groupUpdate);
} else if (
@ -53,6 +63,7 @@ export async function handleClosedGroupControlMessage(
type === Type.MEMBERS_REMOVED ||
type === Type.MEMBERS_ADDED ||
type === Type.MEMBER_LEFT ||
type === Type.ENCRYPTION_KEY_PAIR_REQUEST ||
type === Type.UPDATE
) {
await performIfValid(envelope, groupUpdate);
@ -324,7 +335,8 @@ async function handleUpdateClosedGroup(
*/
async function handleClosedGroupEncryptionKeyPair(
envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage,
isComingFromGroupPubkey: boolean
) {
if (
groupUpdate.type !==
@ -332,12 +344,18 @@ async function handleClosedGroupEncryptionKeyPair(
) {
return;
}
// groupUpdate.publicKey might be set. This is used to give an explicitGroupPublicKey for this update.
const groupPublicKey = toHex(groupUpdate.publicKey) || envelope.source;
// in the case of an encryption key pair coming as a reply to a request we made
// senderIdentity will be unset as the message is not encoded for medium groups
const sender = isComingFromGroupPubkey
? envelope.senderIdentity
: envelope.source;
window.log.info(
`Got a group update for group ${envelope.source}, type: ENCRYPTION_KEY_PAIR`
`Got a group update for group ${groupPublicKey}, type: ENCRYPTION_KEY_PAIR`
);
const ourNumber = await UserUtils.getOurNumber();
const groupPublicKey = envelope.source;
const ourKeyPair = await UserUtils.getIdentityKeyPair();
if (!ourKeyPair) {
@ -361,9 +379,9 @@ async function handleClosedGroupEncryptionKeyPair(
await removeFromCache(envelope);
return;
}
if (!groupConvo.get('groupAdmins')?.includes(envelope.senderIdentity)) {
if (!groupConvo.get('members')?.includes(sender)) {
window.log.warn(
`Ignoring closed group encryption key pair from non-admin. ${groupPublicKey}: ${envelope.senderIdentity}`
`Ignoring closed group encryption key pair from non-member. ${groupPublicKey}: ${envelope.senderIdentity}`
);
await removeFromCache(envelope);
return;
@ -426,7 +444,25 @@ async function handleClosedGroupEncryptionKeyPair(
`Received a new encryptionKeyPair for group ${groupPublicKey}`
);
// Store it
// Store it if needed
const newKeyPairInHex = keyPair.toHexKeyPair();
const keyPairsAlreadySaved = await getAllEncryptionKeyPairsForGroup(
groupPublicKey
);
const isKeyPairAlreadySaved = (keyPairsAlreadySaved || []).some(
k =>
newKeyPairInHex.publicHex === k.publicHex &&
newKeyPairInHex.privateHex === k.privateHex
);
if (isKeyPairAlreadySaved) {
window.log.info('Dropping already saved keypair for group', groupPublicKey);
await removeFromCache(envelope);
return;
}
window.log.info('Got a new encryption keypair for group', groupPublicKey);
await addClosedGroupEncryptionKeyPair(groupPublicKey, keyPair.toHexKeyPair());
await removeFromCache(envelope);
}
@ -438,6 +474,7 @@ async function performIfValid(
const { Type } = SignalService.DataMessage.ClosedGroupControlMessage;
const groupPublicKey = envelope.source;
const sender = envelope.senderIdentity;
const convo = ConversationController.getInstance().get(groupPublicKey);
if (!convo) {
@ -472,9 +509,9 @@ async function performIfValid(
// Check that the sender is a member of the group (before the update)
const oldMembers = convo.get('members') || [];
if (!oldMembers.includes(envelope.senderIdentity)) {
if (!oldMembers.includes(sender)) {
window.log.error(
`Error: closed group: ignoring closed group update message from non-member. ${envelope.senderIdentity} is not a current member.`
`Error: closed group: ignoring closed group update message from non-member. ${sender} is not a current member.`
);
await removeFromCache(envelope);
return;
@ -493,6 +530,13 @@ async function performIfValid(
await handleClosedGroupMembersRemoved(envelope, groupUpdate, convo);
} else if (groupUpdate.type === Type.MEMBER_LEFT) {
await handleClosedGroupMemberLeft(envelope, groupUpdate, convo);
} else if (groupUpdate.type === Type.ENCRYPTION_KEY_PAIR_REQUEST) {
await handleClosedGroupEncryptionKeyPairRequest(
envelope,
groupUpdate,
convo
);
// if you add a case here, remember to add it where performIfValid is called too.
}
return true;
@ -690,6 +734,60 @@ async function handleClosedGroupMemberLeft(
await removeFromCache(envelope);
}
async function handleClosedGroupEncryptionKeyPairRequest(
envelope: EnvelopePlus,
groupUpdate: SignalService.DataMessage.ClosedGroupControlMessage,
convo: ConversationModel
) {
const sender = envelope.senderIdentity;
const groupPublicKey = envelope.source;
// Guard against self-sends
if (await UserUtils.isUs(sender)) {
window.log.info(
'Dropping self send message of type ENCRYPTION_KEYPAIR_REQUEST'
);
await removeFromCache(envelope);
return;
}
// Get the latest encryption key pair
const latestKeyPair = await getLatestClosedGroupEncryptionKeyPair(
groupPublicKey
);
if (!latestKeyPair) {
window.log.info(
'We do not have the keypair ourself, so dropping this message.'
);
await removeFromCache(envelope);
return;
}
window.log.info(
`Responding to closed group encryption key pair request from: ${sender}`
);
await ConversationController.getInstance().getOrCreateAndWait(
sender,
'private'
);
const wrappers = await ClosedGroup.buildEncryptionKeyPairWrappers(
[sender],
ECKeyPair.fromHexKeyPair(latestKeyPair)
);
const expireTimer = convo.get('expireTimer') || 0;
const keypairsMessage = new ClosedGroupEncryptionPairReplyMessage({
groupId: groupPublicKey,
timestamp: Date.now(),
encryptedKeyPairs: wrappers,
expireTimer,
});
// the encryption keypair is sent using established channels
await getMessageQueue().sendToPubKey(PubKey.cast(sender), keypairsMessage);
await removeFromCache(envelope);
}
export async function createClosedGroup(
groupName: string,
members: Array<string>

@ -15,6 +15,8 @@ import { ConversationController } from '../session/conversations';
import { getAllEncryptionKeyPairsForGroup } from '../../js/modules/data';
import { ECKeyPair } from './keypairs';
import { handleNewClosedGroup } from './closedGroups';
import { KeyPairRequestManager } from './keyPairRequestManager';
import { requestEncryptionKeyPair } from '../session/group';
export async function handleContentMessage(envelope: EnvelopePlus) {
try {
@ -59,6 +61,10 @@ async function decryptForClosedGroup(
// likely be the one we want) but try older ones in case that didn't work)
let decryptedContent: ArrayBuffer | undefined;
let keyIndex = 0;
// If an error happens in here, we catch it in the inner try-catch
// When the loop is done, we check if the decryption is a success;
// If not, we trigger a new Error which will trigger in the outer try-catch
do {
try {
const hexEncryptionKeyPair = encryptionKeyPairs.pop();
@ -88,7 +94,6 @@ async function decryptForClosedGroup(
} while (encryptionKeyPairs.length > 0);
if (!decryptedContent?.byteLength) {
await removeFromCache(envelope);
throw new Error(
`Could not decrypt message for closed group with any of the ${encryptionKeyPairsCount} keypairs.`
);
@ -105,10 +110,23 @@ async function decryptForClosedGroup(
return unpad(decryptedContent);
} catch (e) {
/**
* If an error happened during the decoding,
* we trigger a request to get the latest EncryptionKeyPair for this medium group.
* Indeed, we might not have the latest one used by someone else, or not have any keypairs for this group.
*
*/
window.log.warn(
'decryptWithSessionProtocol for medium group message throw:',
e
);
const keypairRequestManager = KeyPairRequestManager.getInstance();
const groupPubKey = PubKey.cast(envelope.source);
if (keypairRequestManager.canTriggerRequestWith(groupPubKey)) {
keypairRequestManager.markRequestSendFor(groupPubKey, Date.now());
await requestEncryptionKeyPair(groupPubKey);
}
await removeFromCache(envelope);
return null;
}
@ -269,8 +287,6 @@ async function decrypt(
envelope: EnvelopePlus,
ciphertext: ArrayBuffer
): Promise<any> {
const { textsecure } = window;
try {
const plaintext = await doDecrypt(envelope, ciphertext);

@ -0,0 +1,47 @@
import { PubKey } from '../session/types';
import { SECONDS } from '../session/utils/Number';
/**
* Singleton handling the logic behing requesting EncryptionKeypair for a closed group we need.
*
* Nothing is read/written to the db, it's all on memory for now.
*/
export class KeyPairRequestManager {
public static DELAY_BETWEEN_TWO_REQUEST_MS = SECONDS * 30;
private static instance: KeyPairRequestManager | null;
private readonly requestTimestamps: Map<string, number>;
private constructor() {
this.requestTimestamps = new Map();
}
public static getInstance() {
if (KeyPairRequestManager.instance) {
return KeyPairRequestManager.instance;
}
KeyPairRequestManager.instance = new KeyPairRequestManager();
return KeyPairRequestManager.instance;
}
public reset() {
this.requestTimestamps.clear();
}
public markRequestSendFor(pubkey: PubKey, timestamp: number) {
this.requestTimestamps.set(pubkey.key, timestamp);
}
public get(pubkey: PubKey) {
return this.requestTimestamps.get(pubkey.key);
}
public canTriggerRequestWith(pubkey: PubKey) {
const record = this.requestTimestamps.get(pubkey.key);
if (!record) {
return true;
}
const now = Date.now();
return now - record >= KeyPairRequestManager.DELAY_BETWEEN_TWO_REQUEST_MS;
}
}

@ -29,6 +29,7 @@ import { UserUtils } from '../utils';
import { ClosedGroupMemberLeftMessage } from '../messages/outgoing/content/data/group/ClosedGroupMemberLeftMessage';
import {
ClosedGroupAddedMembersMessage,
ClosedGroupEncryptionPairRequestMessage,
ClosedGroupNameChangeMessage,
ClosedGroupRemovedMembersMessage,
ClosedGroupUpdateMessage,
@ -556,13 +557,55 @@ export async function generateAndSendNewEncryptionKeyPair(
);
return;
}
// Distribute it
const wrappers = await buildEncryptionKeyPairWrappers(
targetMembers,
newKeyPair
);
const expireTimer = groupConvo.get('expireTimer') || 0;
const keypairsMessage = new ClosedGroupEncryptionPairMessage({
groupId: toHex(groupId),
timestamp: Date.now(),
encryptedKeyPairs: wrappers,
expireTimer,
});
const messageSentCallback = async () => {
window.log.info(
`KeyPairMessage for ClosedGroup ${groupPublicKey} is sent. Saving the new encryptionKeyPair.`
);
await addClosedGroupEncryptionKeyPair(
toHex(groupId),
newKeyPair.toHexKeyPair()
);
};
// this is to be sent to the group pubkey adress
await getMessageQueue().sendToGroup(keypairsMessage, messageSentCallback);
}
export async function buildEncryptionKeyPairWrappers(
targetMembers: Array<string>,
encryptionKeyPair: ECKeyPair
) {
if (
!encryptionKeyPair ||
!encryptionKeyPair.publicKeyData.length ||
!encryptionKeyPair.privateKeyData.length
) {
throw new Error(
'buildEncryptionKeyPairWrappers() needs a valid encryptionKeyPair set'
);
}
const proto = new SignalService.KeyPair({
privateKey: newKeyPair?.privateKeyData,
publicKey: newKeyPair?.publicKeyData,
privateKey: encryptionKeyPair?.privateKeyData,
publicKey: encryptionKeyPair?.publicKeyData,
});
const plaintext = SignalService.KeyPair.encode(proto).finish();
// Distribute it
const wrappers = await Promise.all(
targetMembers.map(async pubkey => {
const ciphertext = await encryptUsingSessionProtocol(
@ -577,26 +620,37 @@ export async function generateAndSendNewEncryptionKeyPair(
);
})
);
return wrappers;
}
const expireTimer = groupConvo.get('expireTimer') || 0;
export async function requestEncryptionKeyPair(
groupPublicKey: string | PubKey
) {
const groupConvo = ConversationController.getInstance().get(
PubKey.cast(groupPublicKey).key
);
const keypairsMessage = new ClosedGroupEncryptionPairMessage({
groupId: toHex(groupId),
timestamp: Date.now(),
encryptedKeyPairs: wrappers,
expireTimer,
});
if (!groupConvo) {
window.log.warn(
'requestEncryptionKeyPair: Trying to request encryption key pair from unknown group'
);
return;
}
const messageSentCallback = async () => {
const ourNumber = await UserUtils.getOurNumber();
if (!groupConvo.get('members').includes(ourNumber.key)) {
window.log.info(
`KeyPairMessage for ClosedGroup ${groupPublicKey} is sent. Saving the new encryptionKeyPair.`
'requestEncryptionKeyPair: We are not a member of this group.'
);
return;
}
const expireTimer = groupConvo.get('expireTimer') || 0;
await addClosedGroupEncryptionKeyPair(
toHex(groupId),
newKeyPair.toHexKeyPair()
);
};
const ecRequestMessage = new ClosedGroupEncryptionPairRequestMessage({
expireTimer,
groupId: groupPublicKey,
timestamp: Date.now(),
});
await getMessageQueue().sendToGroup(keypairsMessage, messageSentCallback);
await getMessageQueue().sendToGroup(ecRequestMessage);
}

@ -1,11 +1,12 @@
import { Constants } from '../../../../..';
import { SignalService } from '../../../../../../protobuf';
import { fromHexToArray } from '../../../../../utils/String';
import {
ClosedGroupMessage,
ClosedGroupMessageParams,
} from './ClosedGroupMessage';
interface ClosedGroupEncryptionPairMessageParams
export interface ClosedGroupEncryptionPairMessageParams
extends ClosedGroupMessageParams {
encryptedKeyPairs: Array<
SignalService.DataMessage.ClosedGroupControlMessage.KeyPairWrapper

@ -0,0 +1,25 @@
import { SignalService } from '../../../../../../protobuf';
import { fromHexToArray } from '../../../../../utils/String';
import { ClosedGroupEncryptionPairMessage } from './ClosedGroupEncryptionPairMessage';
/**
* On Desktop, we need separate class for message being sent to a closed group or a private chat.
*
* This is because we use the class of the message to know what encryption to use.
* See toRawMessage();
*
* This class is just used to let us send the encryption key par after we receivied a ENCRYPTION_KEYPAIR_REQUEST
* from a member of a group.
* This reply must be sent to this user's pubkey, and so be encoded using sessionProtocol.
*/
export class ClosedGroupEncryptionPairReplyMessage extends ClosedGroupEncryptionPairMessage {
public dataProto(): SignalService.DataMessage {
const dataMessage = super.dataProto();
// tslint:disable: no-non-null-assertion
dataMessage.closedGroupControlMessage!.publicKey = fromHexToArray(
this.groupId.key
);
return dataMessage;
}
}

@ -0,0 +1,19 @@
import { Constants } from '../../../../..';
import { SignalService } from '../../../../../../protobuf';
import { ClosedGroupMessage } from './ClosedGroupMessage';
export class ClosedGroupEncryptionPairRequestMessage extends ClosedGroupMessage {
public dataProto(): SignalService.DataMessage {
const dataMessage = super.dataProto();
// tslint:disable: no-non-null-assertion
dataMessage.closedGroupControlMessage!.type =
SignalService.DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR_REQUEST;
return dataMessage;
}
public ttl(): number {
return Constants.TTL_DEFAULT.ENCRYPTION_PAIR_GROUP;
}
}

@ -1,5 +1,7 @@
export * from './ClosedGroupChatMessage';
export * from './ClosedGroupEncryptionPairMessage';
export * from './ClosedGroupEncryptionPairRequestMessage';
export * from './ClosedGroupEncryptionPairReplyMessage';
export * from './ClosedGroupNewMessage';
export * from './ClosedGroupAddedMembersMessage';
export * from './ClosedGroupNameChangeMessage';

@ -2,13 +2,30 @@ import { ContentMessage, OpenGroupMessage } from '../messages/outgoing';
import { RawMessage } from '../types/RawMessage';
import { TypedEventEmitter } from '../utils';
import { PubKey } from '../types';
import { ClosedGroupMessage } from '../messages/outgoing/content/data/group/ClosedGroupMessage';
import { ClosedGroupChatMessage } from '../messages/outgoing/content/data/group/ClosedGroupChatMessage';
import {
ClosedGroupAddedMembersMessage,
ClosedGroupEncryptionPairMessage,
ClosedGroupNameChangeMessage,
ClosedGroupRemovedMembersMessage,
ClosedGroupUpdateMessage,
} from '../messages/outgoing/content/data/group';
import { ClosedGroupMemberLeftMessage } from '../messages/outgoing/content/data/group/ClosedGroupMemberLeftMessage';
import { ClosedGroupEncryptionPairRequestMessage } from '../messages/outgoing/content/data/group/ClosedGroupEncryptionPairRequestMessage';
export type GroupMessageType =
| OpenGroupMessage
| ClosedGroupChatMessage
| ClosedGroupMessage;
| ClosedGroupAddedMembersMessage
| ClosedGroupRemovedMembersMessage
| ClosedGroupNameChangeMessage
| ClosedGroupMemberLeftMessage
| ClosedGroupUpdateMessage
| ClosedGroupEncryptionPairMessage
| ClosedGroupEncryptionPairRequestMessage;
// ClosedGroupEncryptionPairReplyMessage must be sent to a user pubkey. Not a group.
export interface MessageQueueInterfaceEvents {
sendSuccess: (
message: RawMessage | OpenGroupMessage,

@ -16,12 +16,16 @@ import { getLatestClosedGroupEncryptionKeyPair } from '../../../js/modules/data'
import { UserUtils } from '.';
import { ECKeyPair } from '../../receiver/keypairs';
import _ from 'lodash';
import { ClosedGroupEncryptionPairReplyMessage } from '../messages/outgoing/content/data/group/ClosedGroupEncryptionPairReplyMessage';
export function getEncryptionTypeFromMessageType(
function getEncryptionTypeFromMessageType(
message: ContentMessage
): EncryptionType {
// ClosedGroupNewMessage is sent using established channels, so using fallback
if (message instanceof ClosedGroupNewMessage) {
if (
message instanceof ClosedGroupNewMessage ||
message instanceof ClosedGroupEncryptionPairReplyMessage
) {
return EncryptionType.Fallback;
}

@ -0,0 +1,81 @@
import chai from 'chai';
// tslint:disable: no-require-imports no-var-requires no-implicit-dependencies
import _ from 'lodash';
import { describe } from 'mocha';
import { KeyPairRequestManager } from '../../../../receiver/keyPairRequestManager';
import { TestUtils } from '../../../test-utils';
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
chai.should();
const { expect } = chai;
// tslint:disable-next-line: max-func-body-length
describe('KeyPairRequestManager', () => {
let inst: KeyPairRequestManager;
beforeEach(() => {
KeyPairRequestManager.getInstance().reset();
inst = KeyPairRequestManager.getInstance();
});
it('getInstance() should return an instance', () => {
expect(inst).to.exist;
});
describe('markRequestSendFor', () => {
it('should be able to set a timestamp for a pubkey', () => {
const groupPubkey = TestUtils.generateFakePubKey();
const now = Date.now();
inst.markRequestSendFor(groupPubkey, now);
expect(inst.get(groupPubkey)).to.be.equal(now);
});
it('should be able to override a timestamp for a pubkey', () => {
const groupPubkey = TestUtils.generateFakePubKey();
const timestamp1 = Date.now();
inst.markRequestSendFor(groupPubkey, timestamp1);
expect(inst.get(groupPubkey)).to.be.equal(timestamp1);
const timestamp2 = Date.now() + 1000;
inst.markRequestSendFor(groupPubkey, timestamp2);
expect(inst.get(groupPubkey)).to.be.equal(timestamp2);
});
});
describe('canTriggerRequestWith', () => {
it('should return true if there is no timestamp set for this pubkey', () => {
const groupPubkey = TestUtils.generateFakePubKey();
const can = inst.canTriggerRequestWith(groupPubkey);
expect(can).to.be.equal(
true,
'should return true if we there is no timestamp set for this pubkey'
);
});
it('should return false if there is a timestamp set for this pubkey and it is less than DELAY_BETWEEN_TWO_REQUEST_MS', () => {
const groupPubkey = TestUtils.generateFakePubKey();
const timestamp1 = Date.now();
inst.markRequestSendFor(groupPubkey, timestamp1);
const can = inst.canTriggerRequestWith(groupPubkey);
expect(can).to.be.equal(
false,
'return false if there is a timestamp set for this pubkey and it is less than DELAY_BETWEEN_TWO_REQUEST_MS'
);
});
it('should return true if there is a timestamp set for this pubkey and it is more than DELAY_BETWEEN_TWO_REQUEST_MS', () => {
const groupPubkey = TestUtils.generateFakePubKey();
const timestamp1 =
Date.now() - KeyPairRequestManager.DELAY_BETWEEN_TWO_REQUEST_MS;
inst.markRequestSendFor(groupPubkey, timestamp1);
const can = inst.canTriggerRequestWith(groupPubkey);
expect(can).to.be.equal(
true,
'true if there is a timestamp set for this pubkey and it is more than DELAY_BETWEEN_TWO_REQUEST_MS'
);
});
});
});

@ -17,6 +17,7 @@ import {
import { ConversationModel } from '../../../../../js/models/conversations';
import { MockConversation } from '../../../test-utils/utils';
import { ConfigurationMessage } from '../../../../session/messages/outgoing/content/ConfigurationMessage';
import { ClosedGroupEncryptionPairReplyMessage } from '../../../../session/messages/outgoing/content/data/group/ClosedGroupEncryptionPairReplyMessage';
// tslint:disable-next-line: no-require-imports no-var-requires
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
@ -185,7 +186,7 @@ describe('Message Utils', () => {
expect(rawMessage.encryption).to.equal(EncryptionType.ClosedGroup);
});
it('passing ClosedGroupEncryptionPairMessage returns ClosedGroup', async () => {
it('passing ClosedGroupEncryptionKeyPairReply returns Fallback', async () => {
const device = TestUtils.generateFakePubKey();
const fakeWrappers = new Array<
@ -197,14 +198,14 @@ describe('Message Utils', () => {
encryptedKeyPair: new Uint8Array(8),
})
);
const msg = new ClosedGroupEncryptionPairMessage({
const msg = new ClosedGroupEncryptionPairReplyMessage({
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);
expect(rawMessage.encryption).to.equal(EncryptionType.Fallback);
});
it('passing a ConfigurationMessage returns Fallback', async () => {

Loading…
Cancel
Save