From 21e2469b751a09bf1565d1d85d3c9e035d3841a9 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 2 Jun 2020 14:05:16 +1000 Subject: [PATCH] Add more tests --- ts/session/crypto/MessageEncrypter.ts | 2 +- .../session/crypto/MessageEncrypter_test.ts | 116 +++++++++++++++--- .../stubs/SessionCipherBasicStub.ts | 16 --- .../ciphers/FallBackSessionCipherStub.ts | 11 ++ .../stubs/ciphers/SecretSessionCipherStub.ts | 26 ++++ .../stubs/ciphers/SessionCipherStub.ts | 20 +++ ts/test/test-utils/stubs/ciphers/index.ts | 3 + ts/test/test-utils/stubs/index.ts | 2 +- ts/window/types/libsignal-protocol.ts | 2 +- 9 files changed, 165 insertions(+), 33 deletions(-) delete mode 100644 ts/test/test-utils/stubs/SessionCipherBasicStub.ts create mode 100644 ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts create mode 100644 ts/test/test-utils/stubs/ciphers/SecretSessionCipherStub.ts create mode 100644 ts/test/test-utils/stubs/ciphers/SessionCipherStub.ts create mode 100644 ts/test/test-utils/stubs/ciphers/index.ts diff --git a/ts/session/crypto/MessageEncrypter.ts b/ts/session/crypto/MessageEncrypter.ts index 89e5bd35a..8efe6512b 100644 --- a/ts/session/crypto/MessageEncrypter.ts +++ b/ts/session/crypto/MessageEncrypter.ts @@ -25,7 +25,7 @@ function getPaddedMessageLength(originalLength: number): number { return messagePartCount * 160; } -export type Base64String = String; +export type Base64String = string; /** * Encrypt `plainTextBuffer` with given `encryptionType` for `device`. diff --git a/ts/test/session/crypto/MessageEncrypter_test.ts b/ts/test/session/crypto/MessageEncrypter_test.ts index dad0400dd..fb1956e8e 100644 --- a/ts/test/session/crypto/MessageEncrypter_test.ts +++ b/ts/test/session/crypto/MessageEncrypter_test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { ImportMock, MockManager } from 'ts-mock-imports'; +import { ImportMock } from 'ts-mock-imports'; import * as crypto from 'crypto'; import * as sinon from 'sinon'; import * as window from '../../../window'; @@ -7,16 +7,16 @@ import { MessageEncrypter } from '../../../session/crypto'; import { EncryptionType } from '../../../session/types/EncryptionType'; import { Stubs } from '../../test-utils'; import { UserUtil } from '../../../util'; +import { SignalService } from '../../../protobuf'; describe('MessageEncrypter', () => { const sandbox = sinon.createSandbox(); + const ourNumber = 'ourNumber'; - let sessionCipherStub: MockManager; beforeEach(() => { - sessionCipherStub = ImportMock.mockClass(Stubs, 'SessionCipherBasicStub'); ImportMock.mockOther(window, 'libsignal', { SignalProtocolAddress: sandbox.stub(), - SessionCipher: Stubs.SessionCipherBasicStub, + SessionCipher: Stubs.SessionCipherStub, } as any); ImportMock.mockOther(window, 'textsecure', { @@ -25,7 +25,19 @@ describe('MessageEncrypter', () => { }, }); - ImportMock.mockFunction(UserUtil, 'getCurrentDevicePubKey', '1'); + ImportMock.mockOther(window, 'Signal', { + Metadata: { + SecretSessionCipher: Stubs.SecretSessionCipherStub, + }, + }); + + ImportMock.mockOther(window, 'libloki', { + crypto: { + FallBackSessionCipher: Stubs.FallBackSessionCipherStub, + }, + }); + + ImportMock.mockFunction(UserUtil, 'getCurrentDevicePubKey', ourNumber); }); afterEach(() => { @@ -48,30 +60,106 @@ describe('MessageEncrypter', () => { }); }); - /* describe('SessionReset', () => { - it('should call FallbackSessionCipher', async () => { + 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); + expect(spy.called).to.equal( + true, + 'FallbackSessionCipher.encrypt should be called.' + ); }); it('should pass the padded message body to encrypt', async () => { + const data = crypto.randomBytes(10); + const spy = sandbox.spy( + Stubs.FallBackSessionCipherStub.prototype, + 'encrypt' + ); + await MessageEncrypter.encrypt('1', data, EncryptionType.SessionReset); + + const paddedData = MessageEncrypter.padPlainTextBuffer(data); + const firstArgument = new Uint8Array(spy.args[0][0]); + expect(firstArgument).to.deep.equal(paddedData); + }); + + it('should return an UNIDENTIFIED SENDER envelope type', async () => { + const data = crypto.randomBytes(10); + const result = await MessageEncrypter.encrypt( + '1', + data, + EncryptionType.SessionReset + ); + expect(result.envelopeType).to.deep.equal( + SignalService.Envelope.Type.UNIDENTIFIED_SENDER + ); }); }); - */ + describe('Signal', () => { it('should call SessionCipher encrypt', async () => { const data = crypto.randomBytes(10); - const stub = sessionCipherStub.mock('encrypt').resolves({ - type: 1, - body: 'body', - }); + const spy = sandbox.spy(Stubs.SessionCipherStub.prototype, 'encrypt'); await MessageEncrypter.encrypt('1', data, EncryptionType.Signal); - expect(stub.called).to.equal( + expect(spy.called).to.equal( true, 'SessionCipher.encrypt should be called.' ); }); - it('should pass the padded message body to encrypt', async () => {}); + it('should pass the padded message body to encrypt', async () => { + const data = crypto.randomBytes(10); + const spy = sandbox.spy(Stubs.SessionCipherStub.prototype, 'encrypt'); + await MessageEncrypter.encrypt('1', data, EncryptionType.Signal); + + const paddedData = MessageEncrypter.padPlainTextBuffer(data); + const firstArgument = new Uint8Array(spy.args[0][0]); + expect(firstArgument).to.deep.equal(paddedData); + }); + + it('should return an UNIDENTIFIED SENDER envelope type', async () => { + const data = crypto.randomBytes(10); + const result = await MessageEncrypter.encrypt( + '1', + data, + EncryptionType.Signal + ); + expect(result.envelopeType).to.deep.equal( + SignalService.Envelope.Type.UNIDENTIFIED_SENDER + ); + }); + }); + }); + + describe('Sealed Sender', () => { + it('should pass the correct values to SecretSessionCipher encrypt', async () => { + const types = [EncryptionType.SessionReset, EncryptionType.Signal]; + for (const type of types) { + const spy = sandbox.spy( + Stubs.SecretSessionCipherStub.prototype, + 'encrypt' + ); + await MessageEncrypter.encrypt('user', crypto.randomBytes(10), type); + + const args = spy.args[0]; + const [device, certificate] = args; + + const expectedCertificate = SignalService.SenderCertificate.create({ + sender: ourNumber, + senderDevice: 1, + }); + + expect(device).to.equal('user'); + expect(certificate.toJSON()).to.deep.equal( + expectedCertificate.toJSON() + ); + + spy.restore(); + } }); }); }); diff --git a/ts/test/test-utils/stubs/SessionCipherBasicStub.ts b/ts/test/test-utils/stubs/SessionCipherBasicStub.ts deleted file mode 100644 index 20586cc29..000000000 --- a/ts/test/test-utils/stubs/SessionCipherBasicStub.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CipherTextObject } from '../../../window/types/libsignal-protocol'; - -export class SessionCipherBasicStub { - public storage: any; - public address: any; - constructor(storage: any, address: any) { - this.storage = storage; - this.address = address; - } - - public async encrypt( - buffer: ArrayBuffer | Uint8Array - ): Promise { - throw new Error('Should stub this out'); - } -} diff --git a/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts b/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts new file mode 100644 index 000000000..ea30f41ad --- /dev/null +++ b/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts @@ -0,0 +1,11 @@ +import { CipherTextObject } from '../../../../window/types/libsignal-protocol'; +import { SignalService } from '../../../../protobuf'; + +export class FallBackSessionCipherStub { + public async encrypt(buffer: ArrayBuffer): Promise { + return { + type: SignalService.Envelope.Type.FRIEND_REQUEST, + body: Buffer.from(buffer).toString('binary'), + }; + } +} diff --git a/ts/test/test-utils/stubs/ciphers/SecretSessionCipherStub.ts b/ts/test/test-utils/stubs/ciphers/SecretSessionCipherStub.ts new file mode 100644 index 000000000..826ab7308 --- /dev/null +++ b/ts/test/test-utils/stubs/ciphers/SecretSessionCipherStub.ts @@ -0,0 +1,26 @@ +import { SignalService } from '../../../../protobuf'; +import { CipherTextObject } from '../../../../window/types/libsignal-protocol'; + +export class SecretSessionCipherStub { + public async encrypt( + _destinationPubkey: string, + _senderCertificate: SignalService.SenderCertificate, + innerEncryptedMessage: CipherTextObject + ): Promise { + const { body } = innerEncryptedMessage; + + return Buffer.from(body, 'binary').buffer; + } + + public async decrypt( + _cipherText: ArrayBuffer, + _me: { number: string; deviceId: number } + ): Promise<{ + isMe?: boolean; + sender: string; + content: ArrayBuffer; + type: SignalService.Envelope.Type; + }> { + throw new Error('Not implemented'); + } +} diff --git a/ts/test/test-utils/stubs/ciphers/SessionCipherStub.ts b/ts/test/test-utils/stubs/ciphers/SessionCipherStub.ts new file mode 100644 index 000000000..3d08bebfe --- /dev/null +++ b/ts/test/test-utils/stubs/ciphers/SessionCipherStub.ts @@ -0,0 +1,20 @@ +import { CipherTextObject } from '../../../../window/types/libsignal-protocol'; +import { SignalService } from '../../../../protobuf'; + +export class SessionCipherStub { + public storage: any; + public address: any; + constructor(storage: any, address: any) { + this.storage = storage; + this.address = address; + } + + public async encrypt( + buffer: ArrayBuffer | Uint8Array + ): Promise { + return { + type: SignalService.Envelope.Type.CIPHERTEXT, + body: Buffer.from(buffer).toString('binary'), + }; + } +} diff --git a/ts/test/test-utils/stubs/ciphers/index.ts b/ts/test/test-utils/stubs/ciphers/index.ts new file mode 100644 index 000000000..290785930 --- /dev/null +++ b/ts/test/test-utils/stubs/ciphers/index.ts @@ -0,0 +1,3 @@ +export * from './SessionCipherStub'; +export * from './SecretSessionCipherStub'; +export * from './FallBackSessionCipherStub'; diff --git a/ts/test/test-utils/stubs/index.ts b/ts/test/test-utils/stubs/index.ts index 8ec99023e..10ad19f0e 100644 --- a/ts/test/test-utils/stubs/index.ts +++ b/ts/test/test-utils/stubs/index.ts @@ -1 +1 @@ -export * from './SessionCipherBasicStub'; +export * from './ciphers'; diff --git a/ts/window/types/libsignal-protocol.ts b/ts/window/types/libsignal-protocol.ts index 52f46f06e..9b0e18146 100644 --- a/ts/window/types/libsignal-protocol.ts +++ b/ts/window/types/libsignal-protocol.ts @@ -1,6 +1,6 @@ import { SignalService } from '../../protobuf'; -export type BinaryString = String; +export type BinaryString = string; export type CipherTextObject = { type: SignalService.Envelope.Type;