Sending and handling of special UNPAIRING_REQUEST message

This commit is contained in:
sachaaaaa 2019-11-21 11:25:03 +11:00
parent 8cd8c8d4f9
commit db0068b429
9 changed files with 113 additions and 6 deletions

View File

@ -1025,6 +1025,15 @@
pubKey
);
});
Whisper.events.on('deviceUnpairingRequested', async pubKey => {
await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
pubKey
);
await window.lokiFileServerAPI.updateOurDeviceMapping();
// TODO: we should ensure the message was sent and retry automatically if not
await libloki.api.sendUnpairingMessageToSecondary(pubKey);
});
}
window.getSyncRequest = () =>

View File

@ -224,6 +224,10 @@ class LokiFileServerAPI {
uploadPrivateAttachment(data) {
return this._server.uploadData(data);
}
clearOurDeviceMappingAnnotations() {
return this._server.setSelfAnnotation(DEVICE_MAPPING_ANNOTATION_KEY, null);
}
}
module.exports = LokiFileServerAPI;

View File

@ -223,6 +223,9 @@
dialog.on('devicePairingRequestRejected', pubKey =>
Whisper.events.trigger('devicePairingRequestRejected', pubKey)
);
dialog.on('deviceUnpairingRequested', pubKey =>
Whisper.events.trigger('deviceUnpairingRequested', pubKey)
);
dialog.once('close', () => {
Whisper.events.off('devicePairingRequestReceived');
});

View File

@ -117,10 +117,7 @@
this.pubKey = this.pubKeyRequests.pop();
},
async confirmUnpairDevice() {
await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
this.pubKeyToUnpair
);
await lokiFileServerAPI.updateOurDeviceMapping();
this.trigger('deviceUnpairingRequested', this.pubKeyToUnpair);
this.reset();
this.showView();
},

View File

@ -107,6 +107,27 @@
secondaryDevicePubKey,
});
}
function sendUnpairingMessageToSecondary(pubKey) {
const flags = textsecure.protobuf.DataMessage.Flags.UNPAIRING_REQUEST;
const dataMessage = new textsecure.protobuf.DataMessage({
flags,
});
const content = new textsecure.protobuf.Content({
dataMessage,
});
const options = { messageType: 'device-unpairing' };
const outgoingMessage = new textsecure.OutgoingMessage(
null, // server
Date.now(), // timestamp,
[pubKey], // numbers
content, // message
true, // silent
() => null, // callback
options
);
return outgoingMessage.sendToNumber(pubKey);
}
// Serialise as <Element0.length><Element0><Element1.length><Element1>...
// This is an implementation of the reciprocal of contacts_parser.js
function serialiseByteBuffers(buffers) {
@ -226,6 +247,7 @@
broadcastOnlineStatus,
sendPairingAuthorisation,
createPairingAuthorisationProtoMessage,
sendUnpairingMessageToSecondary,
createContactSyncProtoMessage,
};
})();

View File

@ -1286,7 +1286,8 @@ MessageReceiver.prototype.extend({
this.processDecrypted(envelope, msg).then(async message => {
const groupId = message.group && message.group.id;
const isBlocked = this.isGroupBlocked(groupId);
const isMe = envelope.source === textsecure.storage.user.getNumber();
const ourPubKey = textsecure.storage.user.getNumber();
const isMe = envelope.source === ourPubKey;
const conversation = window.ConversationController.get(envelope.source);
const isLeavingGroup = Boolean(
message.group &&
@ -1294,6 +1295,72 @@ MessageReceiver.prototype.extend({
);
const friendRequest =
envelope.type === textsecure.protobuf.Envelope.Type.FRIEND_REQUEST;
const { UNPAIRING_REQUEST } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise
const isUnpairingRequest = Boolean(message.flags & UNPAIRING_REQUEST);
if (isUnpairingRequest) {
// TODO: move high-level pairing logic to libloki.multidevice.xx
const unpairingRequestIsLegit = async () => {
const isSecondary = textsecure.storage.get('isSecondaryDevice');
if (!isSecondary) {
return false;
}
const primaryPubKey = window.storage.get('primaryDevicePubKey');
// TODO: allow unpairing from any paired device?
if (envelope.source !== primaryPubKey) {
return false;
}
const primaryMapping = await lokiFileServerAPI.getUserDeviceMapping(
primaryPubKey
);
if (!primaryMapping) {
return false;
}
// We expect the primary device to have updated its mapping
// before sending the unpairing request
const found = primaryMapping.authorisations.find(
authorisation => authorisation.secondaryDevicePubKey === ourPubKey
);
// our pubkey should NOT be in the primary device mapping
if (found) {
return false;
}
return true;
};
const legit = await unpairingRequestIsLegit();
this.removeFromCache(envelope);
if (legit) {
// remove our device mapping annotations from file server
await lokiFileServerAPI.clearOurDeviceMappingAnnotations();
// Delete the account and restart
try {
await window.Signal.Logs.deleteAll();
await window.Signal.Data.removeAll();
await window.Signal.Data.close();
await window.Signal.Data.removeDB();
await window.Signal.Data.removeOtherData();
// TODO generate an empty db with a flag
// to display a message about the unpairing
// after the app restarts
} catch (error) {
window.log.error(
'Something went wrong deleting all data:',
error && error.stack ? error.stack : error
);
}
window.restart();
}
}
// Check if we need to update any profile names
if (!isMe && conversation) {
@ -1787,6 +1854,8 @@ MessageReceiver.prototype.extend({
decrypted.attachments = [];
} else if (decrypted.flags & FLAGS.BACKGROUND_FRIEND_REQUEST) {
// do nothing
} else if (decrypted.flags & FLAGS.UNPAIRING_REQUEST) {
// do nothing
} else if (decrypted.flags !== 0) {
throw new Error('Unknown flags in message');
}

View File

@ -443,6 +443,8 @@ OutgoingMessage.prototype = {
switch (type) {
case 'friend-request':
return 4 * 24 * 60 * 60 * 1000; // 4 days for friend request message
case 'device-unpairing':
return 4 * 24 * 60 * 60 * 1000; // 4 days for device unpairing
case 'onlineBroadcast':
return 60 * 1000; // 1 minute for online broadcast message
case 'typing':

View File

@ -469,6 +469,6 @@ window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g;
window.SMALL_GROUP_SIZE_LIMIT = 10;
window.lokiFeatureFlags = {
multiDeviceUnpairing: false,
multiDeviceUnpairing: true,
privateGroupChats: false,
};

View File

@ -105,6 +105,7 @@ message DataMessage {
END_SESSION = 1;
EXPIRATION_TIMER_UPDATE = 2;
PROFILE_KEY_UPDATE = 4;
UNPAIRING_REQUEST = 128;
BACKGROUND_FRIEND_REQUEST = 256;
}