fix SessionProtocol decrypt of messages with Android

also disable the old SessionRequest logic
This commit is contained in:
Audric Ackermann 2020-12-17 12:17:11 +11:00
parent b0a229bf13
commit 2af4938ff2
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
9 changed files with 75 additions and 717 deletions

View File

@ -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,
}
);
// 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);
}
// // 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);
// // }
}
}
},

View File

@ -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);

View File

@ -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<void> {
window.log.info('got end session');
@ -24,97 +16,3 @@ export async function handleEndSession(number: string): Promise<void> {
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?
}
}

View File

@ -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<void> {
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<void> {
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);
// }
}
/**

View File

@ -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(

View File

@ -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'
);
});
});

View File

@ -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'
);
});
});

View File

@ -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<void>
>;
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'
);
});
});
});

View File

@ -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);