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