From 6ab05e28dfdb34f9ff75dcb71b80f61a78a685e2 Mon Sep 17 00:00:00 2001
From: sachaaaaa <sacha@loki.network>
Date: Fri, 23 Aug 2019 16:09:18 +1000
Subject: [PATCH] Handle incoming pairing authorisation message

---
 libtextsecure/message_receiver.js | 133 +++++++++++++++++++++++++-----
 1 file changed, 112 insertions(+), 21 deletions(-)

diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js
index ee575edc0..632f73a15 100644
--- a/libtextsecure/message_receiver.js
+++ b/libtextsecure/message_receiver.js
@@ -16,6 +16,7 @@
 /* global localServerPort: false */
 /* global lokiMessageAPI: false */
 /* global lokiP2pAPI: false */
+/* global Whisper: false */
 
 /* eslint-disable more/no-then */
 /* eslint-disable no-unreachable */
@@ -1022,47 +1023,137 @@ MessageReceiver.prototype.extend({
     }
     return this.removeFromCache(envelope);
   },
-  async handlePairingAuthorisationMessage(envelope, pairingAuthorisation) {
+  async validateAuthorisation(authorisation) {
     const {
       type,
       primaryDevicePubKey,
       secondaryDevicePubKey,
-      signature,
-    } = pairingAuthorisation;
-    const sigArrayBuffer = dcodeIO.ByteBuffer.wrap(signature).toArrayBuffer();
-    if (
-      type ===
-      textsecure.protobuf.PairingAuthorisationMessage.Type.PAIRING_REQUEST
-    ) {
-      window.log.info(
-        `Received pairing authorisation from ${primaryDevicePubKey}`
+      requestSignature,
+      grantSignature,
+    } = authorisation;
+    const alreadySecondaryDevice = !!window.storage.get('isSecondaryDevice');
+    const ourPubKey = textsecure.storage.user.getNumber();
+    const isRequest =
+      type === textsecure.protobuf.PairingAuthorisationMessage.Type.REQUEST;
+    const isGrant =
+      type === textsecure.protobuf.PairingAuthorisationMessage.Type.GRANT;
+    if (!primaryDevicePubKey || !secondaryDevicePubKey) {
+      window.log.warn(
+        'Received a pairing request with missing pubkeys. Ignored.'
+      );
+      return false;
+    } else if (!requestSignature) {
+      window.log.warn(
+        'Received a pairing request with missing request signature. Ignored.'
+      );
+      return false;
+    } else if (isRequest && alreadySecondaryDevice) {
+      window.log.warn(
+        'Received a pairing request while being a secondary device. Ignored.'
+      );
+      return false;
+    } else if (isRequest && authorisation.primaryDevicePubKey !== ourPubKey) {
+      window.log.warn(
+        'Received a pairing request addressed to another pubkey. Ignored.'
+      );
+      return false;
+    } else if (authorisation.secondaryDevicePubKey === ourPubKey) {
+      window.log.warn('Received a pairing request from ourselves. Ignored.');
+      return false;
+    }
+    try {
+      await libloki.crypto.verifyPairingAuthorisation(
+        primaryDevicePubKey,
+        secondaryDevicePubKey,
+        dcodeIO.ByteBuffer.wrap(requestSignature).toArrayBuffer(),
+        type
+      );
+    } catch (e) {
+      window.log.warn(
+        'Could not verify pairing request authorisation signature. Ignoring message.'
       );
-      let validAuthorisation = false;
+      window.log.error(e);
+      return false;
+    }
+    if (isGrant) {
       try {
         await libloki.crypto.verifyPairingAuthorisation(
           primaryDevicePubKey,
           secondaryDevicePubKey,
-          sigArrayBuffer,
+          dcodeIO.ByteBuffer.wrap(grantSignature).toArrayBuffer(),
           type
         );
-        validAuthorisation = true;
       } catch (e) {
+        window.log.warn(
+          'Could not verify pairing grant authorisation signature. Ignoring message.'
+        );
         window.log.error(e);
+        return false;
       }
-      if (validAuthorisation) {
-        await libloki.storage.savePairingAuthorisation(
-          primaryDevicePubKey,
-          secondaryDevicePubKey,
-          sigArrayBuffer
-        );
-      } else {
+    }
+    return true;
+  },
+  async handlePairingRequest(pairingRequest) {
+    if (!this.validateAuthorisation(pairingRequest)) {
+      return;
+    }
+    window.libloki.storage.savePairingAuthorisation(pairingRequest);
+    Whisper.events.trigger(
+      'devicePairingRequestReceived',
+      pairingRequest.secondaryDevicePubKey
+    );
+  },
+  async handleAuthorisationForSelf(pairingAuthorisation) {
+    if (!this.validateAuthorisation(pairingAuthorisation)) {
+      return;
+    }
+    const { type, primaryDevicePubKey } = pairingAuthorisation;
+    if (type === textsecure.protobuf.PairingAuthorisationMessage.Type.GRANT) {
+      // Authorisation received to become a secondary device
+      window.log.info(
+        `Received pairing authorisation from ${primaryDevicePubKey}`
+      );
+      const alreadySecondaryDevice = window.storage.get('isSecondaryDevice');
+      if (alreadySecondaryDevice) {
         window.log.warn(
-          'Could not verify pairing authorisation signature. Ignoring message.'
+          'Received an unexpected pairing authorisation (device is already paired as secondary device). Ignoring.'
         );
+        return;
       }
+      await libloki.storage.savePairingAuthorisation(pairingAuthorisation);
+      // Set current device as secondary.
+      // This will ensure the authorisation is sent
+      // along with each friend request.
+      window.storage.remove('secondaryDeviceStatus');
+      window.storage.put('isSecondaryDevice', true);
+      Whisper.events.trigger('secondaryDeviceRegistration');
     } else {
       window.log.warn('Unimplemented pairing authorisation message type');
     }
+  },
+  async handleAuthorisationForContact(pairingAuthorisation) {
+    if (!this.validateAuthorisation(pairingAuthorisation)) {
+      return;
+    }
+    const { primaryDevicePubKey, secondaryDevicePubKey } = pairingAuthorisation;
+    // ensure the primary device is a friend
+    const c = window.ConversationController.get(primaryDevicePubKey);
+    if (!c || !c.isFriend()) {
+      return;
+    }
+    await libloki.storage.savePairingAuthorisation(pairingAuthorisation);
+    // send friend accept?
+    window.libloki.api.sendBackgroundMessage(secondaryDevicePubKey);
+  },
+  async handlePairingAuthorisationMessage(envelope, pairingAuthorisation) {
+    const { type, secondaryDevicePubKey } = pairingAuthorisation;
+    if (type === textsecure.protobuf.PairingAuthorisationMessage.Type.REQUEST) {
+      await this.handlePairingRequest(pairingAuthorisation);
+    } else if (secondaryDevicePubKey === textsecure.storage.user.getNumber()) {
+      await this.handleAuthorisationForSelf(pairingAuthorisation);
+    } else {
+      await this.handleAuthorisationForContact(pairingAuthorisation);
+    }
     return this.removeFromCache(envelope);
   },
   handleDataMessage(envelope, msg) {