From 2af4938ff24020967d6b7494a553f8e4bc8658c9 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 17 Dec 2020 12:17:11 +1100 Subject: [PATCH] fix SessionProtocol decrypt of messages with Android also disable the old SessionRequest logic --- js/models/conversations.js | 42 +- ts/receiver/contentMessage.ts | 29 +- ts/receiver/sessionHandling.ts | 102 ----- ts/session/protocols/SessionProtocol.ts | 70 ++- ts/session/utils/Messages.ts | 7 +- .../SessionEstablishedMessage_test.ts | 36 -- .../unit/messages/SessionResetMessage_test.ts | 79 ---- .../unit/protocols/SessionProtocol_test.ts | 403 ------------------ ts/test/session/unit/utils/Messages_test.ts | 26 -- 9 files changed, 76 insertions(+), 718 deletions(-) delete mode 100644 ts/test/session/unit/messages/SessionEstablishedMessage_test.ts delete mode 100644 ts/test/session/unit/messages/SessionResetMessage_test.ts delete mode 100644 ts/test/session/unit/protocols/SessionProtocol_test.ts diff --git a/js/models/conversations.js b/js/models/conversations.js index a617d3401..d3c343abd 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -1616,27 +1616,27 @@ this.get('sessionResetStatus') !== SessionResetEnum.request_received ) { await this.onSessionResetInitiated(); - const message = await this.createAndStoreEndSessionMessage({ - type: 'outgoing', - endSessionType: 'ongoing', - }); - window.log.info('resetting secure session'); - const device = new libsession.Types.PubKey(this.id); - const preKeyBundle = await window.libloki.storage.getPreKeyBundleForContact( - device.key - ); - const endSessionMessage = new libsession.Messages.Outgoing.EndSessionMessage( - { - timestamp: message.get('sent_at'), - preKeyBundle, - } - ); - - await libsession.getMessageQueue().send(device, endSessionMessage); - // TODO handle errors to reset session reset status with the new pipeline - if (message.hasErrors()) { - await this.setSessionResetStatus(SessionResetEnum.none); - } + // const message = await this.createAndStoreEndSessionMessage({ + // type: 'outgoing', + // endSessionType: 'ongoing', + // }); + // window.log.info('resetting secure session'); + // const device = new libsession.Types.PubKey(this.id); + // const preKeyBundle = await window.libloki.storage.getPreKeyBundleForContact( + // device.key + // ); + // // const endSessionMessage = new libsession.Messages.Outgoing.EndSessionMessage( + // // { + // // timestamp: message.get('sent_at'), + // // preKeyBundle, + // // } + // // ); + + // // await libsession.getMessageQueue().send(device, endSessionMessage); + // // // TODO handle errors to reset session reset status with the new pipeline + // // if (message.hasErrors()) { + // // await this.setSessionResetStatus(SessionResetEnum.none); + // // } } } }, diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index 52ac750b7..9d0a908c1 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -6,7 +6,6 @@ import { removeFromCache, updateCache } from './cache'; import { SignalService } from '../protobuf'; import * as Lodash from 'lodash'; import * as libsession from '../session'; -import { handleSessionRequestMessage } from './sessionHandling'; import { handlePairingAuthorisationMessage } from './multidevice'; import { MediumGroupRequestKeysMessage } from '../session/messages/outgoing'; import { MultiDeviceProtocol, SessionProtocol } from '../session/protocols'; @@ -106,7 +105,11 @@ async function decryptForMediumGroup( ciphertext ); return retSessionProtocol; - } catch { + } catch (e) { + window.log.warn( + 'decryptWithSessionProtocol for medium group message throw:', + e + ); const retSSK = await decryptWithSharedSenderKeys(envelope, ciphertext); return retSSK; } @@ -155,8 +158,11 @@ async function decryptWithSessionProtocol( recipientX25519PrivateKey = await libsession.MediumGroup.getGroupSecretKey( hexEncodedGroupPublicKey ); // throws if not found + const groupPubKeyHexWithoutPrefix = PubKey.remove05PrefixIfNeeded( + hexEncodedGroupPublicKey + ); recipientX25519PublicKey = new Uint8Array( - fromHex(hexEncodedGroupPublicKey) + fromHex(groupPubKeyHexWithoutPrefix) ); break; @@ -175,7 +181,10 @@ async function decryptWithSessionProtocol( recipientX25519PublicKey, new Uint8Array(recipientX25519PrivateKey) ); - if (plaintextWithMetadata.byteLength > signatureSize + ed25519PublicKeySize) { + if ( + plaintextWithMetadata.byteLength <= + signatureSize + ed25519PublicKeySize + ) { throw new Error('Decryption failed.'); // throw Error.decryptionFailed; } @@ -219,6 +228,8 @@ async function decryptWithSessionProtocol( // set the sender identity on the envelope itself. if (isMediumGroup) { envelope.senderIdentity = `05${toHex(senderX25519PublicKey)}`; + } else { + envelope.source = `05${toHex(senderX25519PublicKey)}`; } return unpad(plaintext); } @@ -363,7 +374,11 @@ async function decryptUnidentifiedSender( ciphertext ); return retSessionProtocol; - } catch { + } catch (e) { + window.log.warn( + 'decryptWithSessionProtocol for unidentified message throw:', + e + ); const retSignalProtocol = await decryptWithSignalProtocol( envelope, ciphertext @@ -585,9 +600,7 @@ export async function innerHandleContentMessage( await ConversationController.getOrCreateAndWait(envelope.source, 'private'); - if (content.preKeyBundleMessage) { - await handleSessionRequestMessage(envelope, content.preKeyBundleMessage); - } else if (envelope.type !== FALLBACK_MESSAGE) { + if (envelope.type !== FALLBACK_MESSAGE) { const device = new PubKey(envelope.source); await SessionProtocol.onSessionEstablished(device); diff --git a/ts/receiver/sessionHandling.ts b/ts/receiver/sessionHandling.ts index 910c7675b..c849b2941 100644 --- a/ts/receiver/sessionHandling.ts +++ b/ts/receiver/sessionHandling.ts @@ -1,11 +1,3 @@ -import { EnvelopePlus } from './types'; -import { SignalService } from '../protobuf'; -import * as libsession from './../session'; -import { toNumber } from 'lodash'; -import { PubKey } from '../session/types'; -import { SessionEstablishedMessage } from '../session/messages/outgoing'; -import { SessionProtocol } from '../session/protocols'; - export async function handleEndSession(number: string): Promise { window.log.info('got end session'); @@ -24,97 +16,3 @@ export async function handleEndSession(number: string): Promise { window.log.error('Error getting conversation: ', number); } } - -export async function handleSessionRequestMessage( - envelope: EnvelopePlus, - preKeyBundleMessage: SignalService.IPreKeyBundleMessage -) { - const { libsignal, StringView, textsecure, dcodeIO, log } = window; - - window.log.info(`Received SESSION_REQUEST from source: ${envelope.source}`); - - if (!preKeyBundleMessage) { - log.warn('No pre-key bundle found in a session request'); - return; - } - - const shouldProcessSessionRequest = await SessionProtocol.shouldProcessSessionRequest( - new PubKey(envelope.source), - toNumber(envelope.timestamp) - ); - - if (!shouldProcessSessionRequest) { - log.debug('Ignoring a session request message'); - return; - } - try { - // device id are always 1 with Session - const deviceId = 1; - const pubkey = envelope.source; - const address = new libsignal.SignalProtocolAddress( - envelope.source, - deviceId - ); - // we process the new prekeys and initiate a new session. - // The old sessions will get deleted once the correspondant - // has switch to the new session. - const [identityKey, preKey, signedKey, signature] = [ - preKeyBundleMessage.identityKey, - preKeyBundleMessage.preKey, - preKeyBundleMessage.signedKey, - preKeyBundleMessage.signature, - ].map(k => dcodeIO.ByteBuffer.wrap(k).toArrayBuffer()); - const { preKeyId, signedKeyId } = preKeyBundleMessage; - - if (pubkey !== StringView.arrayBufferToHex(identityKey)) { - throw new Error( - 'Error in savePreKeyBundleMessage: envelope pubkey does not match pubkey in prekey bundle' - ); - } - if (preKey === undefined || signedKey === undefined) { - window.log.warn( - "Couldn't process prekey bundle without preKey or signedKey" - ); - return; - } - const signedPreKey = { - keyId: signedKeyId, - publicKey: signedKey, - signature, - }; - - const preKeyObject = { - publicKey: preKey, - keyId: preKeyId, - }; - - const device = { - identityKey, - deviceId, - preKey: preKeyObject, - signedPreKey, - registrationId: 0, - }; - const builder = new libsignal.SessionBuilder( - textsecure.storage.protocol, - address - ); - await builder.processPreKey(device); - - await SessionProtocol.onSessionRequestProcessed( - new PubKey(envelope.source) - ); - log.debug('sending session established to', envelope.source); - // We don't need to await the call below because we just want to send it off - - const user = new PubKey(envelope.source); - - const sessionEstablished = new SessionEstablishedMessage({ - timestamp: Date.now(), - }); - await libsession.getMessageQueue().send(user, sessionEstablished); - } catch (e) { - log.warn('Failed to process session request', e); - // TODO how to handle a failed session request? - } -} diff --git a/ts/session/protocols/SessionProtocol.ts b/ts/session/protocols/SessionProtocol.ts index df98e4fbd..27e279019 100644 --- a/ts/session/protocols/SessionProtocol.ts +++ b/ts/session/protocols/SessionProtocol.ts @@ -97,6 +97,7 @@ export class SessionProtocol { } /** + * This is disabled until we remove it completely once we removed * Triggers a SessionRequestMessage to be sent if: * - we do not already have a session and * - we did not sent a session request already to that device and @@ -105,27 +106,24 @@ export class SessionProtocol { public static async sendSessionRequestIfNeeded( pubkey: PubKey ): Promise { - if ( - (await SessionProtocol.hasSession(pubkey)) || - (await SessionProtocol.hasSentSessionRequest(pubkey)) - ) { - return; - } - - const preKeyBundle = await window.libloki.storage.getPreKeyBundleForContact( - pubkey.key - ); - - const sessionReset = new SessionRequestMessage({ - preKeyBundle, - timestamp: Date.now(), - }); - - try { - await SessionProtocol.sendSessionRequest(sessionReset, pubkey); - } catch (error) { - console.warn('Failed to send session request to:', pubkey.key, error); - } + // if ( + // (await SessionProtocol.hasSession(pubkey)) || + // (await SessionProtocol.hasSentSessionRequest(pubkey)) + // ) { + // return; + // } + // const preKeyBundle = await window.libloki.storage.getPreKeyBundleForContact( + // pubkey.key + // ); + // const sessionReset = new SessionRequestMessage({ + // preKeyBundle, + // timestamp: Date.now(), + // }); + // try { + // await SessionProtocol.sendSessionRequest(sessionReset, pubkey); + // } catch (error) { + // console.warn('Failed to send session request to:', pubkey.key, error); + // } } /** @@ -136,22 +134,20 @@ export class SessionProtocol { message: SessionRequestMessage, pubkey: PubKey ): Promise { - const timestamp = Date.now(); - - // mark the session as being pending send with current timestamp - // so we know we already triggered a new session with that device - // so sendSessionRequestIfNeeded does not sent another session request - SessionProtocol.pendingSendSessionsTimestamp.add(pubkey.key); - - try { - const rawMessage = await MessageUtils.toRawMessage(pubkey, message); - await MessageSender.send(rawMessage); - await SessionProtocol.updateSentSessionTimestamp(pubkey.key, timestamp); - } catch (e) { - throw e; - } finally { - SessionProtocol.pendingSendSessionsTimestamp.delete(pubkey.key); - } + // const timestamp = Date.now(); + // // mark the session as being pending send with current timestamp + // // so we know we already triggered a new session with that device + // // so sendSessionRequestIfNeeded does not sent another session request + // SessionProtocol.pendingSendSessionsTimestamp.add(pubkey.key); + // try { + // const rawMessage = await MessageUtils.toRawMessage(pubkey, message); + // await MessageSender.send(rawMessage); + // await SessionProtocol.updateSentSessionTimestamp(pubkey.key, timestamp); + // } catch (e) { + // throw e; + // } finally { + // SessionProtocol.pendingSendSessionsTimestamp.delete(pubkey.key); + // } } /** diff --git a/ts/session/utils/Messages.ts b/ts/session/utils/Messages.ts index 2e41e6cdd..654c6f09f 100644 --- a/ts/session/utils/Messages.ts +++ b/ts/session/utils/Messages.ts @@ -1,11 +1,6 @@ import { RawMessage } from '../types/RawMessage'; -import { - ContentMessage, - MediumGroupChatMessage, - SessionRequestMessage, -} from '../messages/outgoing'; +import { ContentMessage, MediumGroupChatMessage } from '../messages/outgoing'; import { EncryptionType, PubKey } from '../types'; -import { SessionProtocol } from '../protocols'; import { MediumGroupUpdateMessage } from '../messages/outgoing/content/data/mediumgroup/MediumGroupUpdateMessage'; export async function toRawMessage( diff --git a/ts/test/session/unit/messages/SessionEstablishedMessage_test.ts b/ts/test/session/unit/messages/SessionEstablishedMessage_test.ts deleted file mode 100644 index e84693204..000000000 --- a/ts/test/session/unit/messages/SessionEstablishedMessage_test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect } from 'chai'; -import { beforeEach } from 'mocha'; - -import { SessionEstablishedMessage } from '../../../../session/messages/outgoing'; -import { SignalService } from '../../../../protobuf'; -import { Constants } from '../../../../session'; - -describe('SessionEstablishedMessage', () => { - let message: SessionEstablishedMessage; - beforeEach(() => { - const timestamp = Date.now(); - message = new SessionEstablishedMessage({ timestamp }); - }); - - it('has a nullMessage not null', () => { - const plainText = message.plainTextBuffer(); - const decoded = SignalService.Content.decode(plainText); - - expect(decoded.nullMessage).to.be.not.equal( - null, - 'decoded.dataMessage.nullMessage should not be null' - ); - }); - - it('correct ttl', () => { - expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.SESSION_ESTABLISHED); - }); - - it('has an identifier', () => { - expect(message.identifier).to.not.equal(null, 'identifier cannot be null'); - expect(message.identifier).to.not.equal( - undefined, - 'identifier cannot be undefined' - ); - }); -}); diff --git a/ts/test/session/unit/messages/SessionResetMessage_test.ts b/ts/test/session/unit/messages/SessionResetMessage_test.ts deleted file mode 100644 index 9857be5db..000000000 --- a/ts/test/session/unit/messages/SessionResetMessage_test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { expect } from 'chai'; -import { beforeEach } from 'mocha'; - -import { SessionRequestMessage } from '../../../../session/messages/outgoing'; -import { SignalService } from '../../../../protobuf'; -import { TextDecoder, TextEncoder } from 'util'; -import { Constants } from '../../../../session'; - -describe('SessionRequestMessage', () => { - let message: SessionRequestMessage; - const preKeyBundle = { - deviceId: 123456, - preKeyId: 654321, - signedKeyId: 111111, - preKey: new TextEncoder().encode('preKey'), - signature: new TextEncoder().encode('signature'), - signedKey: new TextEncoder().encode('signedKey'), - identityKey: new TextEncoder().encode('identityKey'), - }; - - beforeEach(() => { - const timestamp = Date.now(); - message = new SessionRequestMessage({ timestamp, preKeyBundle }); - }); - - it('has a preKeyBundle', () => { - const plainText = message.plainTextBuffer(); - const decoded = SignalService.Content.decode(plainText); - - expect(decoded.preKeyBundleMessage).to.have.property( - 'deviceId', - preKeyBundle.deviceId - ); - expect(decoded.preKeyBundleMessage).to.have.property( - 'preKeyId', - preKeyBundle.preKeyId - ); - expect(decoded.preKeyBundleMessage).to.have.property( - 'signedKeyId', - preKeyBundle.signedKeyId - ); - - const signature = new TextDecoder().decode( - decoded.preKeyBundleMessage?.signature - ); - const signedKey = new TextDecoder().decode( - decoded.preKeyBundleMessage?.signedKey - ); - const identityKey = new TextDecoder().decode( - decoded.preKeyBundleMessage?.identityKey - ); - - expect(signature).to.be.deep.equal('signature'); - expect(signedKey).to.be.deep.equal('signedKey'); - expect(identityKey).to.be.deep.equal('identityKey'); - }); - - it('has a nullMessage not null', () => { - const plainText = message.plainTextBuffer(); - const decoded = SignalService.Content.decode(plainText); - - expect(decoded.nullMessage).to.be.not.equal( - null, - 'decoded.dataMessage.nullMessage should not be null' - ); - }); - - it('correct ttl', () => { - expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.SESSION_REQUEST); - }); - - it('has an identifier', () => { - expect(message.identifier).to.not.equal(null, 'identifier cannot be null'); - expect(message.identifier).to.not.equal( - undefined, - 'identifier cannot be undefined' - ); - }); -}); diff --git a/ts/test/session/unit/protocols/SessionProtocol_test.ts b/ts/test/session/unit/protocols/SessionProtocol_test.ts deleted file mode 100644 index 851f2866c..000000000 --- a/ts/test/session/unit/protocols/SessionProtocol_test.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { expect } from 'chai'; -import * as sinon from 'sinon'; -import { SessionProtocol } from '../../../../session/protocols'; -import { Stubs, TestUtils } from '../../../test-utils'; -import { UserUtil } from '../../../../util'; -import { SessionRequestMessage } from '../../../../session/messages/outgoing'; -import { TextEncoder } from 'util'; -import { MessageSender } from '../../../../session/sending'; -import { PubKey } from '../../../../session/types'; -import { Constants } from '../../../../session'; - -// tslint:disable-next-line: max-func-body-length -describe('SessionProtocol', () => { - const sandbox = sinon.createSandbox(); - const ourNumber = TestUtils.generateFakePubKey(); - const pubkey = TestUtils.generateFakePubKey(); - let getItemById: sinon.SinonStub; - let send: sinon.SinonStub; - - const resetMessage: SessionRequestMessage = new SessionRequestMessage({ - timestamp: Date.now(), - preKeyBundle: { - identityKey: new TextEncoder().encode('identityKey'), - deviceId: 1, - preKeyId: 2, - signedKeyId: 3, - preKey: new TextEncoder().encode('preKey'), - signedKey: new TextEncoder().encode('signedKey'), - signature: new TextEncoder().encode('signature'), - }, - }); - - beforeEach(() => { - TestUtils.stubWindow('libsignal', { - SignalProtocolAddress: sandbox.stub(), - SessionCipher: Stubs.SessionCipherStub, - } as any); - - TestUtils.stubWindow('libloki', { - storage: { - getPreKeyBundleForContact: sandbox.stub(), - }, - }); - - TestUtils.stubWindow('textsecure', { - storage: { - protocol: sandbox.stub(), - }, - }); - - TestUtils.stubData('createOrUpdateItem'); - - getItemById = TestUtils.stubData('getItemById').resolves({ value: {} }); - - sandbox.stub(UserUtil, 'getCurrentDevicePubKey').resolves(ourNumber.key); - send = sandbox.stub(MessageSender, 'send' as any); - SessionProtocol.reset(); - }); - - afterEach(() => { - sandbox.restore(); - TestUtils.restoreStubs(); - }); - - describe('db fetch', () => { - it('protocol: should fetch from DB `sentSessionsTimestamp` and `processedSessionsTimestamp`', async () => { - await SessionProtocol.hasSentSessionRequest(pubkey); - expect(getItemById.calledWith('sentSessionsTimestamp')); - expect(getItemById.calledWith('processedSessionsTimestamp')); - expect(getItemById.callCount).to.equal(2); - }); - - it('protocol: should fetch only once', async () => { - await SessionProtocol.hasSentSessionRequest(pubkey); - await SessionProtocol.hasSentSessionRequest(pubkey); - await SessionProtocol.hasSentSessionRequest(pubkey); - await SessionProtocol.hasSentSessionRequest(pubkey); - expect(getItemById.calledWith('sentSessionsTimestamp')); - expect(getItemById.calledWith('processedSessionsTimestamp')); - expect(getItemById.callCount).to.equal(2); - }); - }); - - describe('sendSessionRequest', () => { - beforeEach(async () => { - // trigger a sessionReset - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); - }); - - it('protocol: sendSessionRequest should add the deviceID to the sentMap', async () => { - expect(SessionProtocol.getSentSessionsTimestamp()) - .to.have.property(pubkey.key) - .to.be.approximately(Date.now(), 100); - }); - - it('protocol: sendSessionRequest should not have pendingSend set after', async () => { - expect( - SessionProtocol.getPendingSendSessionTimestamp() - ).to.not.have.property(pubkey.key); - }); - }); - - describe('checkSessionRequestExpiry', () => { - let clock: sinon.SinonFakeTimers; - let now: number; - let sendSessionRequestStub: sinon.SinonStub< - [SessionRequestMessage, PubKey], - Promise - >; - beforeEach(() => { - now = Date.now(); - clock = sandbox.useFakeTimers(now); - - sendSessionRequestStub = sandbox - .stub(SessionProtocol, 'sendSessionRequest') - .resolves(); - }); - - it('should not send a session request if none have expired', async () => { - getItemById.withArgs('sentSessionsTimestamp').resolves({ - id: 'sentSessionsTimestamp', - value: { - [pubkey.key]: now, - }, - }); - - // Set the time just before expiry - clock.tick(Constants.TTL_DEFAULT.SESSION_REQUEST - 100); - - await SessionProtocol.checkSessionRequestExpiry(); - expect(getItemById.calledWith('sentSessionsTimestamp')); - expect(sendSessionRequestStub.callCount).to.equal(0); - }); - - it('should send a session request if expired', async () => { - getItemById.withArgs('sentSessionsTimestamp').resolves({ - id: 'sentSessionsTimestamp', - value: { - [pubkey.key]: now, - }, - }); - - // Expire the request - clock.tick(Constants.TTL_DEFAULT.SESSION_REQUEST + 100); - - await SessionProtocol.checkSessionRequestExpiry(); - expect(getItemById.calledWith('sentSessionsTimestamp')); - expect(sendSessionRequestStub.callCount).to.equal(1); - }); - - it('should remove the old sent timestamp when expired', async () => { - getItemById.withArgs('sentSessionsTimestamp').resolves({ - id: 'sentSessionsTimestamp', - value: { - [pubkey.key]: now, - }, - }); - - // Remove this call from the equation - sandbox.stub(SessionProtocol, 'sendSessionRequestIfNeeded').resolves(); - - // Expire the request - clock.tick(Constants.TTL_DEFAULT.SESSION_REQUEST + 100); - - await SessionProtocol.checkSessionRequestExpiry(); - expect(getItemById.calledWith('sentSessionsTimestamp')); - expect(await SessionProtocol.hasSentSessionRequest(pubkey)).to.equal( - false, - 'hasSentSessionRequest should return false.' - ); - expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property( - pubkey.key - ); - }); - }); - - describe('onSessionEstablished', () => { - beforeEach(async () => { - // add an existing entry in the sentMap - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); - }); - - it('protocol: onSessionEstablished should remove the device in sentTimestamps', async () => { - expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property( - pubkey.key - ); - await SessionProtocol.onSessionEstablished(pubkey); - expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property( - pubkey.key - ); - }); - - it('protocol: onSessionEstablished should remove the device in sentTimestamps and ONLY that one', async () => { - // add a second item to the map - const anotherPubKey = TestUtils.generateFakePubKey(); - await SessionProtocol.sendSessionRequest(resetMessage, anotherPubKey); - - expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property( - pubkey.key - ); - expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property( - anotherPubKey.key - ); - - await SessionProtocol.onSessionEstablished(pubkey); - expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property( - pubkey.key - ); - expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property( - anotherPubKey.key - ); - }); - }); - - describe('hasSentSessionRequest', () => { - it('protocol: hasSentSessionRequest returns false if a message was not sent to that device', async () => { - const hasSent = await SessionProtocol.hasSentSessionRequest(pubkey); - expect(hasSent).to.be.equal( - false, - `hasSent should be false for ${pubkey.key}` - ); - }); - - it('protocol: hasSentSessionRequest returns true if a message is already sent for that device', async () => { - // add an existing entry in the sentMap - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); - const hasSent = await SessionProtocol.hasSentSessionRequest(pubkey); - expect(hasSent).to.be.equal( - true, - `hasSent should be true for ${pubkey.key}` - ); - }); - - // TODO add a test to validate that pending is filled when message is triggered and not yet sent - }); - - describe('sendSessionRequestIfNeeded', () => { - it('protocol: sendSessionRequestIfNeeded should send a new sessionMessage ', async () => { - // not called before, so the message reset sending should be triggered - await SessionProtocol.sendSessionRequestIfNeeded(pubkey); - expect(send.callCount).to.be.equal( - 1, - 'MessageSender.send() should have been called' - ); - - // check that the map is updated with that ID - const hasSent = await SessionProtocol.hasSentSessionRequest(pubkey); - expect(hasSent).to.be.equal( - true, - `hasSent should be true for ${pubkey.key}` - ); - }); - - it('protocol: sendSessionRequestIfNeeded should NOT send a new sessionMessage on second try ', async () => { - await SessionProtocol.sendSessionRequestIfNeeded(pubkey); - expect(send.callCount).to.be.equal( - 1, - 'MessageSender.send() should have been called' - ); - - // check that the map is updated with that ID - const hasSent = await SessionProtocol.hasSentSessionRequest(pubkey); - expect(hasSent).to.be.equal( - true, - `hasSent should be true for ${pubkey.key}` - ); - send.resetHistory(); - - // trigger a second call, Message.send().calledCount should still be 1 - await SessionProtocol.sendSessionRequestIfNeeded(pubkey); - expect(send.callCount).to.be.equal( - 0, - 'MessageSender.send() should NOT have been called a second time' - ); - }); - }); - - describe('onSessionRequestProcessed', () => { - it('protocol: onSessionRequestProcessed should insert a new item in the processedMap ', async () => { - // trigger the requestProcessed and check the map is updated - await SessionProtocol.onSessionRequestProcessed(pubkey); - expect(SessionProtocol.getProcessedSessionsTimestamp()) - .to.have.property(pubkey.key) - .to.be.approximately(Date.now(), 5); - }); - - it('protocol: onSessionRequestProcessed should update an existing item in the processedMap ', async () => { - // trigger the requestProcessed and check the map is updated - // then trigger it a second time, and expect a change in the processed timestamp - - await SessionProtocol.onSessionRequestProcessed(pubkey); - expect(SessionProtocol.getProcessedSessionsTimestamp()) - .to.have.property(pubkey.key) - .to.be.approximately(Date.now(), 5); - await TestUtils.timeout(5); - const oldTimestamp = SessionProtocol.getProcessedSessionsTimestamp()[ - pubkey.key - ]; - await SessionProtocol.onSessionRequestProcessed(pubkey); - expect(SessionProtocol.getProcessedSessionsTimestamp()) - .to.have.property(pubkey.key) - .to.be.approximately(Date.now(), 5) - .to.not.be.equal(oldTimestamp); - }); - }); - - describe('shouldProcessSessionRequest', () => { - it('protocol: shouldProcessSessionRequest returns true if timestamp is more recent than processed timestamp', async () => { - await SessionProtocol.onSessionRequestProcessed(pubkey); // adds a Date.now() entry - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, Date.now() + 1000) - ).to.be.eventually.equal( - true, - 'shouldProcessSessionRequest should return true when existingProcessed is less recent' - ); - }); - - it('protocol: shouldProcessSessionRequest returns true if there is no processed timestamp yet for this device', async () => { - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, 100) - ).to.be.eventually.equal( - true, - 'shouldProcessSessionRequest should return false when existingProcessed is empty for this device' - ); - }); - - it('protocol: shouldProcessSessionRequest returns false if timestamp is less recent than current processed timestamp', async () => { - await SessionProtocol.onSessionRequestProcessed(pubkey); // adds a Date.now() entry - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, 100) - ).to.be.eventually.equal( - false, - 'shouldProcessSessionRequest should return false when existingProcessed is more recent' - ); - }); - - it('protocol: shouldProcessSessionRequest returns false if timestamp is less recent than current sent timestamp', async () => { - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); // adds a Date.now() entry - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, 100) - ).to.be.eventually.equal( - false, - 'shouldProcessSessionRequest should return false when existingSent is more recent' - ); - }); - - it('protocol: shouldProcessSessionRequest returns true if timestamp is more recent than current sent timestamp', async () => { - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); // adds a Date.now() entry - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, Date.now() + 1000) - ).to.be.eventually.equal( - true, - 'shouldProcessSessionRequest should return true when existingSent is less recent' - ); - }); - - it('protocol: shouldProcessSessionRequest returns true if there is no sent timestamp', async () => { - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, 100) - ).to.be.eventually.equal( - true, - 'shouldProcessSessionRequest should return true as there is no sent timestamp' - ); - }); - - it('protocol: shouldProcessSessionRequest returns false if there is a more recent sent but a less recent processed', async () => { - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); // adds a Date.now() entry - await TestUtils.timeout(100); - await SessionProtocol.onSessionRequestProcessed(pubkey); // adds a Date.now() entry 100ms after - - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, Date.now() - 50) - ).to.be.eventually.equal( - false, - 'shouldProcessSessionRequest should return false if there is a more recent sent but a less recent processed' - ); - }); - - it('protocol: shouldProcessSessionRequest returns false if there is a more recent processed but a less recent sent', async () => { - await SessionProtocol.onSessionRequestProcessed(pubkey); // adds a Date.now() entry - await TestUtils.timeout(100); - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); // adds a Date.now() entry 100ms after - - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, Date.now() - 50) - ).to.be.eventually.equal( - false, - 'shouldProcessSessionRequest should return false if there is a more recent processed but a less recent sent' - ); - }); - - it('protocol: shouldProcessSessionRequest returns true if both sent and processed timestamp are older', async () => { - await SessionProtocol.onSessionRequestProcessed(pubkey); // adds a Date.now() entry - await SessionProtocol.sendSessionRequest(resetMessage, pubkey); // adds a Date.now() entry - expect( - SessionProtocol.shouldProcessSessionRequest(pubkey, Date.now() + 1000) - ).to.be.eventually.equal( - true, - 'shouldProcessSessionRequest should return true if there if both processed and sent are set but are older' - ); - }); - }); -}); diff --git a/ts/test/session/unit/utils/Messages_test.ts b/ts/test/session/unit/utils/Messages_test.ts index 32c805702..b908acf0a 100644 --- a/ts/test/session/unit/utils/Messages_test.ts +++ b/ts/test/session/unit/utils/Messages_test.ts @@ -101,32 +101,6 @@ describe('Message Utils', () => { expect(rawMessage.encryption).to.equal(EncryptionType.MediumGroup); }); - it('should set encryption to Fallback if a SessionRequestMessage is passed in', async () => { - hasSessionStub.resolves(true); - - const device = TestUtils.generateFakePubKey(); - const preKeyBundle = { - deviceId: 123456, - preKeyId: 654321, - signedKeyId: 111111, - preKey: crypto.randomBytes(16), - signature: crypto.randomBytes(16), - signedKey: crypto.randomBytes(16), - identityKey: crypto.randomBytes(16), - }; - const sessionRequest = new SessionRequestMessage({ - timestamp: Date.now(), - preKeyBundle, - }); - - const rawMessage = await MessageUtils.toRawMessage( - device, - sessionRequest - ); - - expect(rawMessage.encryption).to.equal(EncryptionType.Fallback); - }); - it('should set encryption to Fallback on other messages if we do not have a session', async () => { hasSessionStub.resolves(false);