Merge pull request #1166 from Bilb/message-sending-refactor

pull/1173/head
Audric Ackermann 5 years ago committed by GitHub
commit 4a09de9aae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -50,7 +50,7 @@ export async function encrypt(
}
let innerCipherText: CipherTextObject;
if (encryptionType === EncryptionType.SessionReset) {
if (encryptionType === EncryptionType.SessionRequest) {
const cipher = new libloki.crypto.FallBackSessionCipher(address);
innerCipherText = await cipher.encrypt(plainText.buffer);
} else {

@ -1,7 +1,7 @@
import { SessionResetMessage } from './SessionResetMessage';
import { SessionRequestMessage } from './SessionRequestMessage';
import { SignalService } from '../../../../protobuf';
export class EndSessionMessage extends SessionResetMessage {
export class EndSessionMessage extends SessionRequestMessage {
public ttl(): number {
return 4 * 24 * 60 * 60 * 1000; // 4 days
}

@ -12,14 +12,14 @@ export interface PreKeyBundleType {
signature: Uint8Array;
}
interface SessionResetParams extends MessageParams {
interface SessionRequestParams extends MessageParams {
preKeyBundle: PreKeyBundleType;
}
export class SessionResetMessage extends ContentMessage {
export class SessionRequestMessage extends ContentMessage {
private readonly preKeyBundle: PreKeyBundleType;
constructor(params: SessionResetParams) {
constructor(params: SessionRequestParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
this.preKeyBundle = params.preKeyBundle;
}

@ -1,7 +1,7 @@
export * from './ContentMessage';
export * from './EndSessionMessage';
export * from './SessionEstablishedMessage';
export * from './SessionResetMessage';
export * from './SessionRequestMessage';
export * from './TypingMessage';
export * from './data';
export * from './link';

@ -1,56 +1,315 @@
// TODO: Need to flesh out these functions
// Structure of this can be changed for example sticking this all in a class
// The reason i haven't done it is to avoid having instances of the protocol, rather you should be able to call the functions directly
import { SessionRequestMessage } from '../messages/outgoing';
// import { MessageSender } from '../sending';
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { libloki, libsignal, textsecure } from '../../window';
import { MessageSender } from '../sending';
import * as MessageUtils from '../utils';
import { PubKey } from '../types';
import { SessionResetMessage } from '../messages/outgoing';
export function hasSession(device: string): boolean {
return false; // TODO: Implement
interface StringToNumberMap {
[key: string]: number;
}
// tslint:disable: no-unnecessary-class
export class SessionProtocol {
private static dbLoaded: Boolean = false;
/**
* This map olds the sent session timestamps, i.e. session requests message effectively sent to the recipient.
* It is backed by a database entry so it's loaded from db on startup.
* This map should not be used directly, but instead through
* `updateSendSessionTimestamp()`, or `hasSendSessionRequest()`
*/
private static sentSessionsTimestamp: StringToNumberMap;
export function hasSentSessionRequest(device: string): boolean {
// TODO: need a way to keep track of if we've sent a session request
// My idea was to use the timestamp of when it was sent but there might be another better approach
return false;
}
/**
* This map olds the processed session timestamps, i.e. when we received a session request and handled it.
* It is backed by a database entry so it's loaded from db on startup.
* This map should not be used directly, but instead through
* `updateProcessedSessionTimestamp()`, `getProcessedSessionRequest()` or `hasProcessedSessionRequest()`
*/
private static processedSessionsTimestamp: StringToNumberMap;
export async function sendSessionRequestIfNeeded(
device: string
): Promise<void> {
if (hasSession(device) || hasSentSessionRequest(device)) {
return Promise.resolve();
/**
* This map olds the timestamp on which a sent session reset is triggered for a specific device.
* Once the message is sent or failed to sent, this device is removed from here.
* This is a memory only map. Which means that on app restart it's starts empty.
*/
private static readonly pendingSendSessionsTimestamp: Set<string> = new Set();
public static getSentSessionsTimestamp(): Readonly<StringToNumberMap> {
return SessionProtocol.sentSessionsTimestamp;
}
// TODO: Call sendSessionRequest with SessionReset
return Promise.reject(new Error('Need to implement this function'));
}
public static getProcessedSessionsTimestamp(): Readonly<StringToNumberMap> {
return SessionProtocol.processedSessionsTimestamp;
}
export async function sendSessionRequest(
message: SessionResetMessage
): Promise<void> {
// TODO: Optimistically store timestamp of when session request was sent
// TODO: Send out the request via MessageSender
// TODO: On failure, unset the timestamp
return Promise.resolve();
}
public static getPendingSendSessionTimestamp(): Readonly<Set<string>> {
return SessionProtocol.pendingSendSessionsTimestamp;
}
export function sessionEstablished(device: string) {
// TODO: this is called when we receive an encrypted message from the other user
// Maybe it should be renamed to something else
// TODO: This should make `hasSentSessionRequest` return `false`
}
/** Returns true if we already have a session with that device */
public static async hasSession(pubkey: PubKey): Promise<boolean> {
// Session does not use the concept of a deviceId, thus it's always 1
const address = new libsignal.SignalProtocolAddress(pubkey.key, 1);
const sessionCipher = new libsignal.SessionCipher(
textsecure.storage.protocol,
address
);
export function shouldProcessSessionRequest(
device: string,
messageTimestamp: number
): boolean {
// TODO: Need to do the following here
// messageTimestamp > session request sent timestamp && messageTimestamp > session request processed timestamp
return false;
}
return sessionCipher.hasOpenSession();
}
/**
* Returns true if we sent a session request to that device already OR
* if a session request to that device is right now being sent.
*/
public static async hasSentSessionRequest(pubkey: PubKey): Promise<boolean> {
const pendingSend = SessionProtocol.pendingSendSessionsTimestamp.has(
pubkey.key
);
const hasSent = await SessionProtocol.hasAlreadySentSessionRequest(
pubkey.key
);
return pendingSend || hasSent;
}
/**
* 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
* - we do not have a session request currently being sent to that device
*/
public static async sendSessionRequestIfNeeded(
pubkey: PubKey
): Promise<void> {
if (
(await SessionProtocol.hasSession(pubkey)) ||
(await SessionProtocol.hasSentSessionRequest(pubkey))
) {
return;
}
const preKeyBundle = await libloki.storage.getPreKeyBundleForContact(
pubkey.key
);
const sessionReset = new SessionRequestMessage({
preKeyBundle,
timestamp: Date.now(),
});
try {
await SessionProtocol.sendSessionRequest(sessionReset, pubkey);
} catch (error) {
window.console.warn(
'Failed to send session request to:',
pubkey.key,
error
);
}
}
/**
* Sends a session request message to that pubkey.
* We store the sent timestamp only if the message is effectively sent.
*/
public static async sendSessionRequest(
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 = MessageUtils.toRawMessage(pubkey, message);
await MessageSender.send(rawMessage);
await SessionProtocol.updateSentSessionTimestamp(pubkey.key, timestamp);
} catch (e) {
throw e;
} finally {
SessionProtocol.pendingSendSessionsTimestamp.delete(pubkey.key);
}
}
/**
* Called when a session is establish so we store on database this info.
*/
public static async onSessionEstablished(pubkey: PubKey) {
// remove our existing sent timestamp for that device
return SessionProtocol.updateSentSessionTimestamp(pubkey.key, undefined);
}
public static async shouldProcessSessionRequest(
pubkey: PubKey,
messageTimestamp: number
): Promise<boolean> {
const existingSentTimestamp =
(await SessionProtocol.getSentSessionRequest(pubkey.key)) || 0;
const existingProcessedTimestamp =
(await SessionProtocol.getProcessedSessionRequest(pubkey.key)) || 0;
return (
messageTimestamp > existingSentTimestamp &&
messageTimestamp > existingProcessedTimestamp
);
}
public static async onSessionRequestProcessed(pubkey: PubKey) {
return SessionProtocol.updateProcessedSessionTimestamp(
pubkey.key,
Date.now()
);
}
export function sessionRequestProcessed(device: string) {
// TODO: this is called when we process the session request
// This should store the processed timestamp
// Again naming is crap so maybe some other name is better
public static reset() {
SessionProtocol.dbLoaded = false;
SessionProtocol.sentSessionsTimestamp = {};
SessionProtocol.processedSessionsTimestamp = {};
}
/**
* We only need to fetch once from the database, because we are the only one writing to it
*/
private static async fetchFromDBIfNeeded(): Promise<void> {
if (!SessionProtocol.dbLoaded) {
const sentItem = await getItemById('sentSessionsTimestamp');
if (sentItem) {
SessionProtocol.sentSessionsTimestamp = sentItem.value;
} else {
SessionProtocol.sentSessionsTimestamp = {};
}
const processedItem = await getItemById('processedSessionsTimestamp');
if (processedItem) {
SessionProtocol.processedSessionsTimestamp = processedItem.value;
} else {
SessionProtocol.processedSessionsTimestamp = {};
}
SessionProtocol.dbLoaded = true;
}
}
private static async writeToDBSentSessions(): Promise<void> {
const data = {
id: 'sentSessionsTimestamp',
value: JSON.stringify(SessionProtocol.sentSessionsTimestamp),
};
await createOrUpdateItem(data);
}
private static async writeToDBProcessedSessions(): Promise<void> {
const data = {
id: 'processedSessionsTimestamp',
value: JSON.stringify(SessionProtocol.processedSessionsTimestamp),
};
await createOrUpdateItem(data);
}
/**
* This is a utility function to avoid duplicated code of updateSentSessionTimestamp and updateProcessedSessionTimestamp
*/
private static async updateSessionTimestamp(
device: string,
timestamp: number | undefined,
map: StringToNumberMap
): Promise<boolean> {
if (!timestamp) {
if (device in map) {
// tslint:disable-next-line: no-dynamic-delete
delete map[device];
return true;
}
return false;
}
map[device] = timestamp;
return true;
}
/**
*
* @param device the device id
* @param timestamp undefined to remove the key/value pair, otherwise updates the sent timestamp and write to DB
*/
private static async updateSentSessionTimestamp(
device: string,
timestamp: number | undefined
): Promise<void> {
await SessionProtocol.fetchFromDBIfNeeded();
if (
SessionProtocol.updateSessionTimestamp(
device,
timestamp,
SessionProtocol.sentSessionsTimestamp
)
) {
await SessionProtocol.writeToDBSentSessions();
}
}
/**
* timestamp undefined to remove the key/value pair, otherwise updates the processed timestamp and writes to DB
*/
private static async updateProcessedSessionTimestamp(
device: string,
timestamp: number | undefined
): Promise<void> {
await SessionProtocol.fetchFromDBIfNeeded();
if (
SessionProtocol.updateSessionTimestamp(
device,
timestamp,
SessionProtocol.processedSessionsTimestamp
)
) {
await SessionProtocol.writeToDBProcessedSessions();
}
}
/**
* This is a utility function to avoid duplicate code between `getProcessedSessionRequest()` and `getSentSessionRequest()`
*/
private static async getSessionRequest(
device: string,
map: StringToNumberMap
): Promise<number | undefined> {
await SessionProtocol.fetchFromDBIfNeeded();
return map[device];
}
private static async getSentSessionRequest(
device: string
): Promise<number | undefined> {
return SessionProtocol.getSessionRequest(
device,
SessionProtocol.sentSessionsTimestamp
);
}
private static async getProcessedSessionRequest(
device: string
): Promise<number | undefined> {
return SessionProtocol.getSessionRequest(
device,
SessionProtocol.processedSessionsTimestamp
);
}
private static async hasAlreadySentSessionRequest(
device: string
): Promise<boolean> {
await SessionProtocol.fetchFromDBIfNeeded();
return !!SessionProtocol.sentSessionsTimestamp[device];
}
}

@ -1,4 +1,4 @@
import * as SessionProtocol from './SessionProtocol';
import { SessionProtocol } from './SessionProtocol';
import * as MultiDeviceProtocol from './MultiDeviceProtocol';
export { SessionProtocol, MultiDeviceProtocol };

@ -1,5 +1,5 @@
export enum EncryptionType {
Signal,
SessionReset,
SessionRequest,
MediumGroup,
}

@ -7,6 +7,7 @@ import { Stubs, TestUtils } from '../../test-utils';
import { UserUtil } from '../../../util';
import { SignalService } from '../../../protobuf';
// tslint:disable-next-line: max-func-body-length
describe('MessageEncrypter', () => {
const sandbox = sinon.createSandbox();
const ourNumber = 'ourNumber';
@ -58,14 +59,18 @@ describe('MessageEncrypter', () => {
});
});
describe('SessionReset', () => {
describe('SessionRequest', () => {
it('should call FallbackSessionCipher encrypt', async () => {
const data = crypto.randomBytes(10);
const spy = sandbox.spy(
Stubs.FallBackSessionCipherStub.prototype,
'encrypt'
);
await MessageEncrypter.encrypt('1', data, EncryptionType.SessionReset);
await MessageEncrypter.encrypt(
'1',
data,
EncryptionType.SessionRequest
);
expect(spy.called).to.equal(
true,
'FallbackSessionCipher.encrypt should be called.'
@ -78,7 +83,11 @@ describe('MessageEncrypter', () => {
Stubs.FallBackSessionCipherStub.prototype,
'encrypt'
);
await MessageEncrypter.encrypt('1', data, EncryptionType.SessionReset);
await MessageEncrypter.encrypt(
'1',
data,
EncryptionType.SessionRequest
);
const paddedData = MessageEncrypter.padPlainTextBuffer(data);
const firstArgument = new Uint8Array(spy.args[0][0]);
@ -90,7 +99,7 @@ describe('MessageEncrypter', () => {
const result = await MessageEncrypter.encrypt(
'1',
data,
EncryptionType.SessionReset
EncryptionType.SessionRequest
);
expect(result.envelopeType).to.deep.equal(
SignalService.Envelope.Type.UNIDENTIFIED_SENDER
@ -135,7 +144,7 @@ describe('MessageEncrypter', () => {
describe('Sealed Sender', () => {
it('should pass the correct values to SecretSessionCipher encrypt', async () => {
const types = [EncryptionType.SessionReset, EncryptionType.Signal];
const types = [EncryptionType.SessionRequest, EncryptionType.Signal];
for (const type of types) {
const spy = sandbox.spy(
Stubs.SecretSessionCipherStub.prototype,

@ -1,12 +1,12 @@
import { expect } from 'chai';
import { beforeEach } from 'mocha';
import { SessionResetMessage } from '../../../session/messages/outgoing';
import { SessionRequestMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { TextDecoder, TextEncoder } from 'util';
describe('SessionResetMessage', () => {
let message: SessionResetMessage;
describe('SessionRequestMessage', () => {
let message: SessionRequestMessage;
const preKeyBundle = {
deviceId: 123456,
preKeyId: 654321,
@ -19,7 +19,7 @@ describe('SessionResetMessage', () => {
beforeEach(() => {
const timestamp = Date.now();
message = new SessionResetMessage({ timestamp, preKeyBundle });
message = new SessionRequestMessage({ timestamp, preKeyBundle });
});
it('has a preKeyBundle', () => {

@ -0,0 +1,329 @@
import { expect } from 'chai';
import { SessionProtocol } from '../../../session/protocols';
import * as sinon from 'sinon';
import { Stubs, TestUtils, timeout } 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';
// tslint:disable-next-line: max-func-body-length
describe('SessionProtocol', () => {
const sandbox = sinon.createSandbox();
const ourNumber = 'ourNumber';
const pubkey = new PubKey('deviceid');
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);
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('deviceid')
.to.be.approximately(Date.now(), 100);
});
it('protocol: sendSessionRequest should not have pendingSend set after', async () => {
expect(
SessionProtocol.getPendingSendSessionTimestamp()
).to.not.have.property('deviceid');
});
});
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(
'deviceid'
);
await SessionProtocol.onSessionEstablished(pubkey);
expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property(
'deviceid'
);
});
it('protocol: onSessionEstablished should remove the device in sentTimestamps and ONLY that one', async () => {
// add a second item to the map
await SessionProtocol.sendSessionRequest(
resetMessage,
new PubKey('deviceid2')
);
expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property(
'deviceid'
);
expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property(
'deviceid2'
);
await SessionProtocol.onSessionEstablished(pubkey);
expect(SessionProtocol.getSentSessionsTimestamp()).to.not.have.property(
'deviceid'
);
expect(SessionProtocol.getSentSessionsTimestamp()).to.have.property(
'deviceid2'
);
});
});
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 `deviceid`'
);
});
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 `deviceid`'
);
});
// 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 `deviceid`'
);
});
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 `deviceid`'
);
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('deviceid')
.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('deviceid')
.to.be.approximately(Date.now(), 5);
await timeout(5);
const oldTimestamp = SessionProtocol.getProcessedSessionsTimestamp()
.deviceid;
await SessionProtocol.onSessionRequestProcessed(pubkey);
expect(SessionProtocol.getProcessedSessionsTimestamp())
.to.have.property('deviceid')
.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 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 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'
);
});
});
});

@ -42,7 +42,7 @@ export class SessionCipherStub implements SessionCipher {
}
public async hasOpenSession(): Promise<boolean> {
throw new Error('Method not implemented.');
return false;
}
public async closeOpenSessionForDevice(): Promise<void> {

Loading…
Cancel
Save