From 206d29b033a6ac28ed75fb123f67fc6d7af8230a Mon Sep 17 00:00:00 2001 From: Ryan Tharp Date: Thu, 3 Oct 2019 00:17:07 -0700 Subject: [PATCH] avoid no pubkey with guards, make sure primary and secondary authorization match --- js/modules/loki_app_dot_net_api.js | 162 +++++++++++++++++------------ 1 file changed, 94 insertions(+), 68 deletions(-) diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index f1fa495fc..82527a7ef 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -860,6 +860,7 @@ class LokiPublicChannelAPI { if (pendingMessages.length) { const newSlavePrimaryMap = {}; + const checkSigs = {}; // console.log('premultiDeviceResults', pubKeys) if (pubKeys.length) { @@ -878,6 +879,10 @@ class LokiPublicChannelAPI { const { authorisations } = note.value; if (Array.isArray(authorisations)) { authorisations.forEach(auth => { + if (auth.secondaryDevicePubKey !== user.username) { + // this is not the authorization we're looking for + return; + } // console.log('auth', auth); try { // request (secondary wants to be paired with this primary) @@ -907,6 +912,7 @@ class LokiPublicChannelAPI { ); return; } + checkSigs[user.username] = auth; newSlavePrimaryMap[user.username] = auth.primaryDevicePubKey; } catch (e) { @@ -956,68 +962,85 @@ class LokiPublicChannelAPI { pendingMessages = []; // free memory const verifiedPrimaryPKs = []; - // get a list of all of primary pubKeys to verify the secondaryDevice - const primaryDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers( - primaryPubKeys - ); - // 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; - if (Array.isArray(authorisations)) { - authorisations.forEach(auth => { - 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}` + if (primaryPubKeys.length) { + // get a list of all of primary pubKeys to verify the secondaryDevice + const primaryDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers( + primaryPubKeys + ); + // 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; + if (Array.isArray(authorisations)) { + authorisations.forEach(auth => { + 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}` + ); + } + // assuming both are ordered + // make sure our secondary and primary authorization match + if ( + JSON.stringify( + checkSigs[auth.secondaryDevicePubKey] + ) !== JSON.stringify(auth) + ) { + // should hopefully never happen + log.warn( + `Valid authorizations from ${ + auth.secondaryDevicePubKey + } does not match ${auth.primaryDevicePubKey}` + ); + return; + } + found = true; + } catch (e) { + log.warn( + `Invalid grant signature on PrimaryPubKey ${ + auth.primaryDevicePubKey + }` ); } - found = true; - } catch (e) { - log.warn( - `Invalid grant signature on PrimaryPubKey ${ - auth.primaryDevicePubKey - }` - ); - } - }); + }); + } } } + }); + } + 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]; } }); - } - 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; @@ -1025,19 +1048,22 @@ class LokiPublicChannelAPI { `Updated device mappings ${JSON.stringify(this.slavePrimaryMap)}` ); - // get final list of verified chat server profile names - const verifiedDeviceResults = await this.serverAPI.getUsersAnnotations( - pubKeys - ); - // console.log('verifiedDeviceResults', verifiedDeviceResults) - - // go through verifiedDeviceResults - const newPrimaryUserProfileName = {}; - verifiedDeviceResults.forEach(user => { - newPrimaryUserProfileName[user.username] = user.name; - }); - // replace whole - this.primaryUserProfileName = newPrimaryUserProfileName; + // if we have verified primaryKeys then update them + if (verifiedPrimaryPKs.length) { + // get final list of verified chat server profile names + const verifiedDeviceResults = await this.serverAPI.getUsersAnnotations( + verifiedPrimaryPKs + ); + // console.log('verifiedDeviceResults', verifiedDeviceResults) + + // go through verifiedDeviceResults + const newPrimaryUserProfileName = {}; + verifiedDeviceResults.forEach(user => { + newPrimaryUserProfileName[user.username] = user.name; + }); + // replace whole + this.primaryUserProfileName = newPrimaryUserProfileName; + } // process remaining messages const ourNumber = textsecure.storage.user.getNumber();