From d1518f823343d749396ddd225f98288ecdc15a87 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 11 Jun 2020 08:55:10 +1000 Subject: [PATCH 1/4] add new handling of session request message --- libtextsecure/message_receiver.js | 47 ++++++++++++------- preload.js | 1 + protos/SignalService.proto | 2 +- .../ciphers/FallBackSessionCipherStub.ts | 2 +- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 95581ad2c..079dd30a0 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -20,6 +20,7 @@ /* global ConversationController: false */ /* global Signal: false */ /* global log: false */ +/* global session: false */ /* eslint-disable more/no-then */ /* eslint-disable no-unreachable */ @@ -1545,24 +1546,42 @@ MessageReceiver.prototype.extend({ window.log.info('Error getting conversation: ', envelope.source); } }, + async handleSessionRequestMessage(envelope, content) { + const shouldProcessSessionRequest = await session.Protocols.SessionProtocol.shouldProcessSessionRequest( + envelope.source, + envelope.timestamp + ); + + if (shouldProcessSessionRequest) { + if (content.preKeyBundleMessage) { + await this.savePreKeyBundleMessage( + envelope.source, + content.preKeyBundleMessage + ); + } + await session.Protocols.SessionProtocol.onSessionRequestProcessed( + envelope.source + ); + window.log.info('sending session established to', envelope.source); + // We don't need to await the call below because we just want to send it off + window.libloki.api.sendSessionEstablishedMessage(envelope.source); + } + }, async innerHandleContentMessage(envelope, plaintext) { const content = textsecure.protobuf.Content.decode(plaintext); + const { SESSION_REQUEST } = textsecure.protobuf.Envelope.Type; - if (content.preKeyBundleMessage) { - await this.savePreKeyBundleMessage( - envelope.source, - content.preKeyBundleMessage + if (envelope.type === SESSION_REQUEST) { + await this.handleSessionRequestMessage(envelope, content); + } else { + await session.Protocols.SessionProtocol.onSessionEstablished( + envelope.source ); + // TODO process sending queue for this device now that we have a session } this.handleFriendRequestAcceptIfNeeded(envelope, content); - if (content.lokiAddressMessage) { - return this.handleLokiAddressMessage( - envelope, - content.lokiAddressMessage - ); - } if (content.pairingAuthorisation) { return this.handlePairingAuthorisationMessage(envelope, content); } @@ -1660,14 +1679,6 @@ MessageReceiver.prototype.extend({ return this.dispatchEvent(ev); }, handleNullMessage(envelope) { - // Loki - Temp hack for new protocl backward compatibility - // This should be removed once we add the new protocol - if (envelope.type === textsecure.protobuf.Envelope.Type.FRIEND_REQUEST) { - window.log.info('sent session established to', envelope.source); - // We don't need to await the call below because we just want to send it off - window.libloki.api.sendSessionEstablishedMessage(envelope.source); - } - window.log.info('null message from', this.getEnvelopeId(envelope)); this.removeFromCache(envelope); }, diff --git a/preload.js b/preload.js index fe447f886..2d23e7278 100644 --- a/preload.js +++ b/preload.js @@ -159,6 +159,7 @@ window.setPassword = (passPhrase, oldPhrase) => }); window.passwordUtil = require('./app/password_util'); +window.session = require('./ts/session'); // We never do these in our code, so we'll prevent it everywhere window.open = () => null; diff --git a/protos/SignalService.proto b/protos/SignalService.proto index ba7375657..69be6de73 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -13,7 +13,7 @@ message Envelope { RECEIPT = 5; UNIDENTIFIED_SENDER = 6; MEDIUM_GROUP_CIPHERTEXT = 7; - FRIEND_REQUEST = 101; // contains prekeys + message and is using simple encryption + SESSION_REQUEST = 101; // contains prekeys and is using simple encryption } optional Type type = 1; diff --git a/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts b/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts index 22aba7b01..cbc6f1785 100644 --- a/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts +++ b/ts/test/test-utils/stubs/ciphers/FallBackSessionCipherStub.ts @@ -4,7 +4,7 @@ import { SignalService } from '../../../../protobuf'; export class FallBackSessionCipherStub { public async encrypt(buffer: ArrayBuffer): Promise { return { - type: SignalService.Envelope.Type.FRIEND_REQUEST, + type: SignalService.Envelope.Type.SESSION_REQUEST, body: Buffer.from(buffer).toString('binary'), }; } From d7e9d6cfb835d75bfa157f5c3a84f64b13199362 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 12 Jun 2020 08:40:35 +1000 Subject: [PATCH 2/4] move SESSION_REQUEST processing to handleSessionRequestMessage() --- libtextsecure/message_receiver.js | 91 ++++++++++++++++--------------- preload.js | 2 +- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 079dd30a0..04c18f6d7 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -20,7 +20,7 @@ /* global ConversationController: false */ /* global Signal: false */ /* global log: false */ -/* global session: false */ +/* global libsession: false */ /* eslint-disable more/no-then */ /* eslint-disable no-unreachable */ @@ -1547,24 +1547,60 @@ MessageReceiver.prototype.extend({ } }, async handleSessionRequestMessage(envelope, content) { - const shouldProcessSessionRequest = await session.Protocols.SessionProtocol.shouldProcessSessionRequest( + const shouldProcessSessionRequest = await libsession.Protocols.SessionProtocol.shouldProcessSessionRequest( envelope.source, envelope.timestamp ); if (shouldProcessSessionRequest) { - if (content.preKeyBundleMessage) { - await this.savePreKeyBundleMessage( + try { + // TODO remember to remove savePreKeyBundleMessage() from the codebase if it's actually irrelevant + // if (content.preKeyBundleMessage) { + // await this.savePreKeyBundleMessage( + // envelope.source, + // content.preKeyBundleMessage + // ); + // } + // device id are always 1 with Session + const deviceId = 1; + const address = new libsignal.SignalProtocolAddress( envelope.source, - content.preKeyBundleMessage + deviceId ); + // Instead of deleting the sessions now, + // we process the new prekeys and initiate a new session. + // The old sessions will get deleted once the correspondant + // has switch to the new session. + const { preKey, signedKey, identityKey } = content.preKeyBundleMessage; + if (preKey === undefined || signedKey === undefined) { + window.console.warn( + "Couldn't process prekey bundle without preKey or signedKey" + ); + return; + } + const device = { + identityKey, + deviceId, + preKey, + signedPreKey: signedKey, + registrationId: 0, + }; + const builder = new libsignal.SessionBuilder( + textsecure.storage.protocol, + address + ); + await builder.processPreKey(device); + + await libsession.Protocols.SessionProtocol.onSessionRequestProcessed( + envelope.source + ); + window.log.debug('sending session established to', envelope.source); + // We don't need to await the call below because we just want to send it off + window.libloki.api.sendSessionEstablishedMessage(envelope.source); + } catch (e) { + window.log.warn('Failed to process session request'); + // TODO how to handle a failed session request? } - await session.Protocols.SessionProtocol.onSessionRequestProcessed( - envelope.source - ); - window.log.info('sending session established to', envelope.source); - // We don't need to await the call below because we just want to send it off - window.libloki.api.sendSessionEstablishedMessage(envelope.source); } }, async innerHandleContentMessage(envelope, plaintext) { @@ -1574,7 +1610,7 @@ MessageReceiver.prototype.extend({ if (envelope.type === SESSION_REQUEST) { await this.handleSessionRequestMessage(envelope, content); } else { - await session.Protocols.SessionProtocol.onSessionEstablished( + await libsession.Protocols.SessionProtocol.onSessionEstablished( envelope.source ); // TODO process sending queue for this device now that we have a session @@ -1915,43 +1951,12 @@ MessageReceiver.prototype.extend({ }, async handleEndSession(number) { window.log.info('got end session'); - const deviceIds = await textsecure.storage.protocol.getDeviceIds(number); - const identityKey = StringView.hexToArrayBuffer(number); let conversation; try { conversation = window.ConversationController.get(number); } catch (e) { window.log.error('Error getting conversation: ', number); } - - await Promise.all( - deviceIds.map(async deviceId => { - const address = new libsignal.SignalProtocolAddress(number, deviceId); - // Instead of deleting the sessions now, - // we process the new prekeys and initiate a new session. - // The old sessions will get deleted once the correspondant - // has switch the the new session. - const [preKey, signedPreKey] = await Promise.all([ - textsecure.storage.protocol.loadContactPreKey(number), - textsecure.storage.protocol.loadContactSignedPreKey(number), - ]); - if (preKey === undefined || signedPreKey === undefined) { - return; - } - const device = { - identityKey, - deviceId, - preKey, - signedPreKey, - registrationId: 0, - }; - const builder = new libsignal.SessionBuilder( - textsecure.storage.protocol, - address - ); - await builder.processPreKey(device); - }) - ); await conversation.onSessionResetReceived(); }, processDecrypted(envelope, decrypted) { diff --git a/preload.js b/preload.js index 2d23e7278..8160bd2df 100644 --- a/preload.js +++ b/preload.js @@ -159,7 +159,7 @@ window.setPassword = (passPhrase, oldPhrase) => }); window.passwordUtil = require('./app/password_util'); -window.session = require('./ts/session'); +window.libsession = require('./ts/session'); // We never do these in our code, so we'll prevent it everywhere window.open = () => null; From 205e7f59e7f8d70bcca2ef1626846b2f79d948b1 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 12 Jun 2020 09:17:26 +1000 Subject: [PATCH 3/4] handle not found conversation on handleEndSession --- libtextsecure/message_receiver.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 04c18f6d7..13ad7596d 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -1567,7 +1567,6 @@ MessageReceiver.prototype.extend({ envelope.source, deviceId ); - // Instead of deleting the sessions now, // we process the new prekeys and initiate a new session. // The old sessions will get deleted once the correspondant // has switch to the new session. @@ -1954,10 +1953,14 @@ MessageReceiver.prototype.extend({ let conversation; try { conversation = window.ConversationController.get(number); + if(conversation) { + await conversation.onSessionResetReceived(); + } else { + throw new Error(); + } } catch (e) { window.log.error('Error getting conversation: ', number); } - await conversation.onSessionResetReceived(); }, processDecrypted(envelope, decrypted) { /* eslint-disable no-bitwise, no-param-reassign */ From 492cc96ad2516874d0b6a25d6312e79064b93ab7 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 12 Jun 2020 09:47:25 +1000 Subject: [PATCH 4/4] updateSessionTimestamp: return false if no write to DB needed --- libtextsecure/message_receiver.js | 2 +- ts/session/protocols/SessionProtocol.ts | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 13ad7596d..4aecb96e2 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -1953,7 +1953,7 @@ MessageReceiver.prototype.extend({ let conversation; try { conversation = window.ConversationController.get(number); - if(conversation) { + if (conversation) { await conversation.onSessionResetReceived(); } else { throw new Error(); diff --git a/ts/session/protocols/SessionProtocol.ts b/ts/session/protocols/SessionProtocol.ts index ea02e3246..79847da82 100644 --- a/ts/session/protocols/SessionProtocol.ts +++ b/ts/session/protocols/SessionProtocol.ts @@ -220,17 +220,15 @@ export class SessionProtocol { timestamp: number | undefined, map: StringToNumberMap ): Promise { - if (!timestamp) { - if (device in map) { - // tslint:disable-next-line: no-dynamic-delete - delete map[device]; - - return true; - } - + if (map[device] === timestamp) { return false; } - map[device] = timestamp; + if (!timestamp) { + // tslint:disable-next-line: no-dynamic-delete + delete map[device]; + } else { + map[device] = timestamp; + } return true; }