add the request and reply of an encryptionKeyPair if needed
parent
94a9faec9a
commit
375c5ba1a8
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue