import { makeLibSignalRepository } from '../Signal/libsignal' import { SignalAuthState, SignalDataTypeMap } from '../Types' import { Curve, generateRegistrationId, generateSignalPubKey, signedKeyPair } from '../Utils' describe('Signal Tests', () => { it('should correctly encrypt/decrypt 1 message', async () => { const user1 = makeUser() const user2 = makeUser() const msg = Buffer.from('hello there!') await prepareForSendingMessage(user1, user2) const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg }) const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result }) expect(dec).toEqual(msg) }) it('should correctly override a session', async () => { const user1 = makeUser() const user2 = makeUser() const msg = Buffer.from('hello there!') for (let preKeyId = 2; preKeyId <= 3; preKeyId++) { await prepareForSendingMessage(user1, user2, preKeyId) const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg }) const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result }) expect(dec).toEqual(msg) } }) it('should correctly encrypt/decrypt multiple messages', async () => { const user1 = makeUser() const user2 = makeUser() const msg = Buffer.from('hello there!') await prepareForSendingMessage(user1, user2) for (let i = 0; i < 10; i++) { const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg }) const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result }) expect(dec).toEqual(msg) } }) it('should encrypt/decrypt messages from group', async () => { const groupId = '123456@g.us' const participants = [...Array(5)].map(makeUser) const msg = Buffer.from('hello there!') const sender = participants[0] const enc = await sender.repository.encryptGroupMessage({ group: groupId, meId: sender.jid, data: msg }) for (const participant of participants) { if (participant === sender) { continue } await participant.repository.processSenderKeyDistributionMessage({ item: { groupId, axolotlSenderKeyDistributionMessage: enc.senderKeyDistributionMessage }, authorJid: sender.jid }) const dec = await participant.repository.decryptGroupMessage({ group: groupId, authorJid: sender.jid, msg: enc.ciphertext }) expect(dec).toEqual(msg) } }) }) type User = ReturnType function makeUser() { const store = makeTestAuthState() const jid = `${Math.random().toString().replace('.', '')}@s.whatsapp.net` const repository = makeLibSignalRepository(store) return { store, jid, repository } } async function prepareForSendingMessage(sender: User, receiver: User, preKeyId = 2) { const preKey = Curve.generateKeyPair() await sender.repository.injectE2ESession({ jid: receiver.jid, session: { registrationId: receiver.store.creds.registrationId, identityKey: generateSignalPubKey(receiver.store.creds.signedIdentityKey.public), signedPreKey: { keyId: receiver.store.creds.signedPreKey.keyId, publicKey: generateSignalPubKey(receiver.store.creds.signedPreKey.keyPair.public), signature: receiver.store.creds.signedPreKey.signature }, preKey: { keyId: preKeyId, publicKey: generateSignalPubKey(preKey.public) } } }) await receiver.store.keys.set({ 'pre-key': { [preKeyId]: preKey } }) } function makeTestAuthState(): SignalAuthState { const identityKey = Curve.generateKeyPair() // eslint-disable-next-line @typescript-eslint/no-explicit-any const store: { [_: string]: any } = {} return { creds: { signedIdentityKey: identityKey, registrationId: generateRegistrationId(), signedPreKey: signedKeyPair(identityKey, 1) }, keys: { get(type, ids) { const data: { [_: string]: SignalDataTypeMap[typeof type] } = {} for (const id of ids) { const item = store[getUniqueId(type, id)] if (typeof item !== 'undefined') { data[id] = item } } return data }, set(data) { for (const type in data) { for (const id in data[type]) { store[getUniqueId(type, id)] = data[type][id] } } } } } function getUniqueId(type: string, id: string) { return `${type}.${id}` } }