diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index e2a65a4d8..f1fa495fc 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -1,5 +1,6 @@ /* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController, -clearTimeout, MessageController, libsignal, StringView, window, _, lokiFileServerAPI */ +clearTimeout, MessageController, libsignal, StringView, window, _, lokiFileServerAPI, +dcodeIO */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); @@ -770,17 +771,17 @@ class LokiPublicChannelAPI { !adnMessage.text || adnMessage.is_deleted ) { - return; // Invalid or delete message + return false; // Invalid or delete message } const messengerData = await this.getMessengerData(adnMessage); if (messengerData === false) { - return; + return false; } const { timestamp, quote } = messengerData; if (!timestamp) { - return; // Invalid message + return false; // Invalid message } // Duplicate check @@ -798,7 +799,7 @@ class LokiPublicChannelAPI { // Filter out any messages that we got previously if (this.lastMessagesCache.some(isDuplicate)) { - return; // Duplicate message + return false; // Duplicate message } // FIXME: maybe move after the de-multidev-decode @@ -858,7 +859,7 @@ class LokiPublicChannelAPI { this.conversation.setLastRetrievedMessage(this.lastGot); if (pendingMessages.length) { - this.slavePrimaryMap = {}; + const newSlavePrimaryMap = {}; // console.log('premultiDeviceResults', pubKeys) if (pubKeys.length) { @@ -878,25 +879,42 @@ class LokiPublicChannelAPI { if (Array.isArray(authorisations)) { authorisations.forEach(auth => { // console.log('auth', auth); - // FIXME: verify secondary sig - if (1) { - // add map to slavePrimaryMap + try { + // request (secondary wants to be paired with this primary) + window.libloki.crypto.verifyPairingSignature( + auth.primaryDevicePubKey, + auth.secondaryDevicePubKey, + dcodeIO.ByteBuffer.wrap( + auth.requestSignature, + 'base64' + ).toArrayBuffer(), + textsecure.protobuf.PairingAuthorisationMessage.Type + .REQUEST + ); + // if it doesn't throw, that means it's valid + // add map to newSlavePrimaryMap if ( - this.slavePrimaryMap[user.username] && - this.slavePrimaryMap[user.username] !== + newSlavePrimaryMap[user.username] && + newSlavePrimaryMap[user.username] !== auth.primaryDevicePubKey ) { log.warn( `file server user annotation primaryKey mismatch, had ${ - this.slavePrimaryMap[user.username] + newSlavePrimaryMap[user.username] } now ${auth.primaryDevicePubKey} for ${ user.username }` ); return; } - this.slavePrimaryMap[user.username] = + newSlavePrimaryMap[user.username] = auth.primaryDevicePubKey; + } catch (e) { + log.warn( + `Invalid grant signature on SecondaryPubKey ${ + auth.secondaryDevicePubKey + }` + ); } }); } @@ -905,7 +923,7 @@ class LokiPublicChannelAPI { }); } }); - log.info('updated slavePrimaryMap', this.slavePrimaryMap); + // log.info('tenative slavePrimaryMap', newSlavePrimaryMap); const primaryPubKeys = []; const slaveMessages = {}; @@ -916,8 +934,8 @@ class LokiPublicChannelAPI { log.warn('invalid pendingMessages'); return; } - if (this.slavePrimaryMap[messageData.source]) { - const primaryPubKey = this.slavePrimaryMap[messageData.source]; + if (newSlavePrimaryMap[messageData.source]) { + const primaryPubKey = newSlavePrimaryMap[messageData.source]; // add to lookup (if we don't already have it) if (primaryPubKeys.indexOf(`@${primaryPubKey}`) === -1) { primaryPubKeys.push(`@${primaryPubKey}`); @@ -944,43 +962,69 @@ class LokiPublicChannelAPI { ); // go through primaryDeviceResults primaryDeviceResults.forEach(user => { + let found = false; if (user.annotations) { user.annotations.forEach(note => { if (note.type === 'network.loki.messenger.devicemapping') { if (note.value.isPrimary) { const { authorisations } = note.value; - let found = false; if (Array.isArray(authorisations)) { authorisations.forEach(auth => { - // FIXME: verify primary sig - if ( - verifiedPrimaryPKs.indexOf( - `@${auth.primaryDevicePubKey}` - ) === -1 - ) { - verifiedPrimaryPKs.push( - `@${auth.primaryDevicePubKey}` + try { + // grant (primary approves this secondary) + window.libloki.crypto.verifyPairingSignature( + auth.primaryDevicePubKey, + auth.secondaryDevicePubKey, + dcodeIO.ByteBuffer.wrap( + auth.grantSignature, + 'base64' + ).toArrayBuffer(), + textsecure.protobuf.PairingAuthorisationMessage.Type + .GRANT + ); + if ( + verifiedPrimaryPKs.indexOf( + `@${auth.primaryDevicePubKey}` + ) === -1 + ) { + verifiedPrimaryPKs.push( + `@${auth.primaryDevicePubKey}` + ); + } + found = true; + } catch (e) { + log.warn( + `Invalid grant signature on PrimaryPubKey ${ + auth.primaryDevicePubKey + }` ); } - found = true; }); } - if (found) { - return; - } - // if not verified remove from slavePrimaryMap } - // not primary or verified - /* - Object.keys(slavePrimaryMap).forEach(slaveKey => { - if (slavePrimaryMap[slaveKey] == - }) - */ } }); } + if (found) { + return; + } + // if not verified remove this user pubkey from newSlavePrimaryMap + Object.keys(newSlavePrimaryMap).forEach(slaveKey => { + if (newSlavePrimaryMap[slaveKey] === user.username) { + log.warn( + `removing unverifible ${slaveKey} to ${user.username} mapping` + ); + delete newSlavePrimaryMap[slaveKey]; + } + }); }); + // update this.slavePrimaryMap live with updated map + this.slavePrimaryMap = newSlavePrimaryMap; + log.info( + `Updated device mappings ${JSON.stringify(this.slavePrimaryMap)}` + ); + // get final list of verified chat server profile names const verifiedDeviceResults = await this.serverAPI.getUsersAnnotations( pubKeys @@ -988,11 +1032,21 @@ class LokiPublicChannelAPI { // console.log('verifiedDeviceResults', verifiedDeviceResults) // go through verifiedDeviceResults - this.primaryUserProfileName = {}; + const newPrimaryUserProfileName = {}; verifiedDeviceResults.forEach(user => { - this.primaryUserProfileName[user.username] = user.name; + newPrimaryUserProfileName[user.username] = user.name; }); + // replace whole + this.primaryUserProfileName = newPrimaryUserProfileName; + + // process remaining messages + const ourNumber = textsecure.storage.user.getNumber(); Object.keys(slaveMessages).forEach(slaveKey => { + // prevent our own sent messages from coming back in + if (slaveKey === ourNumber) { + // we originally sent these + return; + } const primaryPubKey = this.slavePrimaryMap[slaveKey]; slaveMessages[slaveKey].forEach(messageDataP => { const messageData = messageDataP; // for linter @@ -1009,12 +1063,13 @@ class LokiPublicChannelAPI { }); }); }); - } + } // end if there are pending pubkeys to look up // console.log('pendingMessages len', pendingMessages.length); // console.log('pendingMessages', pendingMessages); // find messages for original slave key using slavePrimaryMap if (pendingMessages.length) { + const ourNumber = textsecure.storage.user.getNumber(); pendingMessages.forEach(messageDataP => { const messageData = messageDataP; // for linter // why am I getting these? @@ -1022,10 +1077,15 @@ class LokiPublicChannelAPI { log.warn(`invalid pendingMessages ${pendingMessages}`); return; } + // prevent our own sent messages from coming back in + if (messageData.source === ourNumber) { + // we originally sent this + return; + } if (this.slavePrimaryMap[messageData.source]) { // rewrite source, profile const primaryPubKey = this.slavePrimaryMap[messageData.source]; - log.info('Rewriting', messageData.source, 'to', primaryPubKey); + log.info(`Rewriting ${messageData.source} to ${primaryPubKey}`); messageData.source = primaryPubKey; messageData.message.profile.displayName = this.primaryUserProfileName[ primaryPubKey