From dadd4b97add3106c64a9ce41c84e0ea2f0e22578 Mon Sep 17 00:00:00 2001 From: Maxim Shishmarev Date: Tue, 4 Feb 2020 10:06:03 +1100 Subject: [PATCH] Remove source field from envelope --- js/background.js | 4 +- libtextsecure/message_receiver.js | 201 ++++++++++++++++-------------- libtextsecure/outgoing_message.js | 8 +- 3 files changed, 119 insertions(+), 94 deletions(-) diff --git a/js/background.js b/js/background.js index 4aa4e0500..d02be3556 100644 --- a/js/background.js +++ b/js/background.js @@ -2041,7 +2041,9 @@ // If we don't return early here, we can get into infinite error loops. So, no // delivery receipts for sealed sender errors. - if (isError || !data.unidentifiedDeliveryReceived) { + + // Note(LOKI): don't send receipt for FR as we don't have a session yet + if (isError || !data.unidentifiedDeliveryReceived || data.friendRequest) { return message; } diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 895fd81b1..e803acb36 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -654,6 +654,9 @@ MessageReceiver.prototype.extend({ }, async decrypt(envelope, ciphertext) { let promise; + + // We don't have source at this point yet (with sealed sender) + // This needs a massive cleanup! const address = new libsignal.SignalProtocolAddress( envelope.source, envelope.sourceDevice @@ -668,33 +671,19 @@ MessageReceiver.prototype.extend({ options.messageKeysLimit = false; } + // Will become obsolete const sessionCipher = new libsignal.SessionCipher( textsecure.storage.protocol, address, options ); - const secretSessionCipher = new window.Signal.Metadata.SecretSessionCipher( - textsecure.storage.protocol - ); - - const fallBackSessionCipher = new libloki.crypto.FallBackSessionCipher( - address - ); const me = { number: ourNumber, deviceId: parseInt(textsecure.storage.user.getDeviceId(), 10), }; - let conversation; - try { - conversation = await window.ConversationController.getOrCreateAndWait( - envelope.source, - 'private' - ); - } catch (e) { - window.log.info('Error getting conversation: ', envelope.source); - } + // Will become obsolete const getCurrentSessionBaseKey = async () => { const record = await sessionCipher.getRecord(address.toString()); if (!record) { @@ -707,71 +696,28 @@ MessageReceiver.prototype.extend({ const { baseKey } = openSession.indexInfo; return baseKey; }; + + // Will become obsolete const captureActiveSession = async () => { this.activeSessionBaseKey = await getCurrentSessionBaseKey(sessionCipher); }; - const restoreActiveSession = async () => { - const record = await sessionCipher.getRecord(address.toString()); - if (!record) { - return; - } - record.archiveCurrentState(); - const sessionToRestore = record.sessions[this.activeSessionBaseKey]; - record.promoteState(sessionToRestore); - record.updateSessionState(sessionToRestore); - await textsecure.storage.protocol.storeSession( - address.toString(), - record.serialize() - ); - }; - const deleteAllSessionExcept = async sessionBaseKey => { - const record = await sessionCipher.getRecord(address.toString()); - if (!record) { - return; - } - const sessionToKeep = record.sessions[sessionBaseKey]; - record.sessions = {}; - record.updateSessionState(sessionToKeep); - await textsecure.storage.protocol.storeSession( - address.toString(), - record.serialize() - ); - }; - let handleSessionReset; - if (conversation.isSessionResetOngoing()) { - handleSessionReset = async result => { - const currentSessionBaseKey = await getCurrentSessionBaseKey( - sessionCipher - ); - if ( - this.activeSessionBaseKey && - currentSessionBaseKey !== this.activeSessionBaseKey - ) { - if (conversation.isSessionResetReceived()) { - await restoreActiveSession(); - } else { - await deleteAllSessionExcept(currentSessionBaseKey); - await conversation.onNewSessionAdopted(); - } - } else if (conversation.isSessionResetReceived()) { - await deleteAllSessionExcept(this.activeSessionBaseKey); - await conversation.onNewSessionAdopted(); - } - return result; - }; - } else { - handleSessionReset = async result => result; - } + + const handleSessionReset = async result => result; + switch (envelope.type) { case textsecure.protobuf.Envelope.Type.CIPHERTEXT: window.log.info('message from', this.getEnvelopeId(envelope)); promise = captureActiveSession() .then(() => sessionCipher.decryptWhisperMessage(ciphertext)) - .then(this.unpad) - .then(handleSessionReset); + .then(this.unpad); break; case textsecure.protobuf.Envelope.Type.FRIEND_REQUEST: { window.log.info('friend-request message from ', envelope.source); + + const fallBackSessionCipher = new libloki.crypto.FallBackSessionCipher( + address + ); + promise = fallBackSessionCipher .decrypt(ciphertext.toArrayBuffer()) .then(this.unpad); @@ -779,30 +725,33 @@ MessageReceiver.prototype.extend({ } case textsecure.protobuf.Envelope.Type.PREKEY_BUNDLE: window.log.info('prekey message from', this.getEnvelopeId(envelope)); - promise = captureActiveSession(sessionCipher) - .then(async () => { - if (!this.activeSessionBaseKey) { - try { - const buffer = dcodeIO.ByteBuffer.wrap(ciphertext); - await window.libloki.storage.verifyFriendRequestAcceptPreKey( - envelope.source, - buffer - ); - } catch (e) { - await this.removeFromCache(envelope); - throw e; - } + promise = captureActiveSession(sessionCipher).then(async () => { + if (!this.activeSessionBaseKey) { + try { + const buffer = dcodeIO.ByteBuffer.wrap(ciphertext); + await window.libloki.storage.verifyFriendRequestAcceptPreKey( + envelope.source, + buffer + ); + } catch (e) { + await this.removeFromCache(envelope); + throw e; } - return this.decryptPreKeyWhisperMessage( - ciphertext, - sessionCipher, - address - ); - }) - .then(handleSessionReset); + } + return this.decryptPreKeyWhisperMessage( + ciphertext, + sessionCipher, + address + ); + }); break; case textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER: window.log.info('received unidentified sender message'); + + const secretSessionCipher = new window.Signal.Metadata.SecretSessionCipher( + textsecure.storage.protocol + ); + promise = secretSessionCipher .decrypt(ciphertext.toArrayBuffer(), me) .then( @@ -872,8 +821,7 @@ MessageReceiver.prototype.extend({ throw error; }); } - ) - .then(handleSessionReset); + ); break; default: promise = Promise.reject(new Error('Unknown message type')); @@ -887,6 +835,75 @@ MessageReceiver.prototype.extend({ return null; } + let conversation; + try { + conversation = await window.ConversationController.getOrCreateAndWait( + envelope.source, + 'private' + ); + } catch (e) { + window.log.info('Error getting conversation: ', envelope.source); + } + + /// *** BEGIN: session reset *** + + const address = new libsignal.SignalProtocolAddress( + envelope.source, + envelope.sourceDevice + ); + + const restoreActiveSession = async () => { + const record = await sessionCipher.getRecord(address.toString()); + if (!record) { + return; + } + record.archiveCurrentState(); + + // NOTE: activeSessionBaseKey will be undefined here... + const sessionToRestore = record.sessions[this.activeSessionBaseKey]; + record.promoteState(sessionToRestore); + record.updateSessionState(sessionToRestore); + await textsecure.storage.protocol.storeSession( + address.toString(), + record.serialize() + ); + }; + const deleteAllSessionExcept = async sessionBaseKey => { + const record = await sessionCipher.getRecord(address.toString()); + if (!record) { + return; + } + const sessionToKeep = record.sessions[sessionBaseKey]; + record.sessions = {}; + record.updateSessionState(sessionToKeep); + await textsecure.storage.protocol.storeSession( + address.toString(), + record.serialize() + ); + }; + + if (conversation.isSessionResetOngoing()) { + const currentSessionBaseKey = await getCurrentSessionBaseKey( + sessionCipher + ); + if ( + this.activeSessionBaseKey && + currentSessionBaseKey !== this.activeSessionBaseKey + ) { + if (conversation.isSessionResetReceived()) { + await restoreActiveSession(); + } else { + await deleteAllSessionExcept(currentSessionBaseKey); + await conversation.onNewSessionAdopted(); + } + } else if (conversation.isSessionResetReceived()) { + await deleteAllSessionExcept(this.activeSessionBaseKey); + await conversation.onNewSessionAdopted(); + } + } + + /// *** END *** + // Type here can actually be UNIDENTIFIED_SENDER even if // the underlying message is FRIEND_REQUEST if ( diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 0a0de2c29..c7fd66a99 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -266,9 +266,15 @@ OutgoingMessage.prototype = { return this.convertMessageToText(messageBuffer); }, async wrapInWebsocketMessage(outgoingObject) { + const source = + outgoingObject.type === + textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER + ? null + : outgoingObject.ourKey; + const messageEnvelope = new textsecure.protobuf.Envelope({ type: outgoingObject.type, - source: outgoingObject.ourKey, + source, sourceDevice: outgoingObject.sourceDevice, timestamp: this.timestamp, content: outgoingObject.content,