From bfe23d86aa9a8b1b5348beecc7e94e87f6cc338a Mon Sep 17 00:00:00 2001 From: lilia Date: Wed, 25 Mar 2015 17:08:05 -0700 Subject: [PATCH] Run key generation in a worker Ground work for a smoother registration flow. Overall UX still needs some polish but at least now we can have a progress gif or animation or whatever. Also adds the phonenumber-confirmation step as a simple alert box, which will be replaced with a nice dialogue in a later commit. --- js/generate_keys.js | 76 ++++++++++++++++++++++++++++++++++++++ js/libtextsecure.js | 39 +++++++------------ js/options.js | 51 +++++++++++++++++-------- libtextsecure/helpers.js | 37 ++++++------------- libtextsecure/protobufs.js | 2 +- 5 files changed, 137 insertions(+), 68 deletions(-) create mode 100644 js/generate_keys.js diff --git a/js/generate_keys.js b/js/generate_keys.js new file mode 100644 index 000000000..cb3103ce2 --- /dev/null +++ b/js/generate_keys.js @@ -0,0 +1,76 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +'use strict'; + +/* +* Load this script in a Web Worker to generate new prekeys without +* tying up the main thread. +* https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API +* +* Because workers don't have access to the window or localStorage, we +* create our own version that proxies back to the caller for actual +* storage. +* +* Example usage: +* + var myWorker = new Worker('/js/generate_keys.js'); + myWorker.onmessage = function(e) { + switch(e.data.method) { + case 'set': + localStorage.setItem(e.data.key, e.data.value); + break; + case 'remove': + localStorage.removeItem(e.data.key); + break; + case 'done': + console.log(e.data.keys); + } + }; +*/ +var store = {}; +var window = this; +importScripts('libtextsecure.js'); +window.textsecure.storage.impl = { + /***************************** + *** Override Storage Routines *** + *****************************/ + put: function(key, value) { + if (value === undefined) + throw new Error("Tried to store undefined"); + store[key] = value; + postMessage({method: 'set', key: key, value: value}); + }, + + get: function(key, defaultValue) { + if (key in store) { + return store[key]; + } else { + return defaultValue; + } + }, + + remove: function(key) { + delete store[key]; + postMessage({method: 'remove', key: key}); + }, +}; +onmessage = function(e) { + store = e.data; + textsecure.protocol_wrapper.generateKeys().then(function(keys) { + postMessage({method: 'done', keys: keys}); + }); +} diff --git a/js/libtextsecure.js b/js/libtextsecure.js index 015dae12e..8e3bcc97d 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -38388,7 +38388,7 @@ window.axolotl.sessions = { ;(function() { function loadProtoBufs(filename) { - return dcodeIO.ProtoBuf.loadProtoFile({root: 'protos', file: filename}).build('textsecure'); + return dcodeIO.ProtoBuf.loadProtoFile({root: '/protos', file: filename}).build('textsecure'); }; var pushMessages = loadProtoBufs('IncomingPushMessageSignal.proto'); @@ -38926,34 +38926,21 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step }); } -window.textsecure.registerSecondDevice = function(encodedProvisionEnvelope, cryptoInfo, stepDone) { - var envelope = textsecure.protobuf.ProvisionEnvelope.decode(encodedProvisionEnvelope, 'binary'); - return cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(identityKey) { - stepDone(1); +window.textsecure.registerSecondDevice = function(provisionMessage) { + var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); + textsecure.storage.put('signaling_key', signalingKey); - var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); - textsecure.storage.put('signaling_key', signalingKey); + var password = btoa(getString(textsecure.crypto.getRandomBytes(16))); + password = password.substring(0, password.length - 2); + textsecure.storage.put("password", password); - var password = btoa(getString(textsecure.crypto.getRandomBytes(16))); - password = password.substring(0, password.length - 2); - textsecure.storage.put("password", password); + var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0]; + registrationId = registrationId & 0x3fff; + textsecure.storage.put("registrationId", registrationId); - var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0]; - registrationId = registrationId & 0x3fff; - textsecure.storage.put("registrationId", registrationId); - - return textsecure.api.confirmCode(identityKey.number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { - textsecure.storage.user.setNumberAndDeviceId(identityKey.number, result.deviceId); - textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(identityKey.number)); - stepDone(2); - - return textsecure.protocol_wrapper.generateKeys().then(function(keys) { - stepDone(3); - return textsecure.api.registerKeys(keys).then(function() { - stepDone(4); - }); - }); - }); + return textsecure.api.confirmCode(provisionMessage.number, provisionMessage.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { + textsecure.storage.user.setNumberAndDeviceId(provisionMessage.number, result.deviceId); + textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(provisionMessage.number)); }); }; diff --git a/js/options.js b/js/options.js index 6474a32dc..180b49c27 100644 --- a/js/options.js +++ b/js/options.js @@ -56,23 +56,42 @@ request.respond(200, 'OK'); } else if (request.path == "/v1/message" && request.verb == "PUT") { $('#qr').hide(); - textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) { - switch(step) { - case 1: + + var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary'); + cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) { + if (confirm(provisionMessage.number)) { $('#status').text('Registering new device...'); - break; - case 2: - $('#status').text('Generating keys...'); - break; - case 3: - $('#status').text('Uploading keys...'); - break; - case 4: - $('#status').text('All done!'); - textsecure.registration.done(); - $('#init-setup').hide(); - $('#setup-complete').show().addClass('in'); - initOptions(); + window.textsecure.registerSecondDevice(provisionMessage).then(function() { + $('#status').text('Generating keys...'); + var counter = 0; + var myWorker = new Worker('/js/generate_keys.js'); + myWorker.postMessage({ + maxPreKeyId: textsecure.storage.get("maxPreKeyId", 0), + signedKeyId: textsecure.storage.get("signedKeyId", 0), + libaxolotl25519KeyidentityKey: textsecure.storage.get("libaxolotl25519KeyidentityKey"), + }); + myWorker.onmessage = function(e) { + switch(e.data.method) { + case 'set': + textsecure.storage.put(e.data.key, e.data.value); + counter = counter + 1; + $('#status').text('Generating keys...' + counter); + break; + case 'remove': + textsecure.storage.remove(e.data.key); + break; + case 'done': + $('#status').text('Uploading keys...'); + textsecure.api.registerKeys(e.data.keys).then(function() { + $('#status').text('All done!'); + textsecure.registration.done(); + $('#init-setup').hide(); + $('#setup-complete').show().addClass('in'); + initOptions(); + }); + } + }; + }); } }); } else diff --git a/libtextsecure/helpers.js b/libtextsecure/helpers.js index 0a5765096..f112fc93a 100644 --- a/libtextsecure/helpers.js +++ b/libtextsecure/helpers.js @@ -279,33 +279,20 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step }); } -window.textsecure.registerSecondDevice = function(encodedProvisionEnvelope, cryptoInfo, stepDone) { - var envelope = textsecure.protobuf.ProvisionEnvelope.decode(encodedProvisionEnvelope, 'binary'); - return cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(identityKey) { - stepDone(1); +window.textsecure.registerSecondDevice = function(provisionMessage) { + var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); + textsecure.storage.put('signaling_key', signalingKey); - var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); - textsecure.storage.put('signaling_key', signalingKey); + var password = btoa(getString(textsecure.crypto.getRandomBytes(16))); + password = password.substring(0, password.length - 2); + textsecure.storage.put("password", password); - var password = btoa(getString(textsecure.crypto.getRandomBytes(16))); - password = password.substring(0, password.length - 2); - textsecure.storage.put("password", password); + var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0]; + registrationId = registrationId & 0x3fff; + textsecure.storage.put("registrationId", registrationId); - var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0]; - registrationId = registrationId & 0x3fff; - textsecure.storage.put("registrationId", registrationId); - - return textsecure.api.confirmCode(identityKey.number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { - textsecure.storage.user.setNumberAndDeviceId(identityKey.number, result.deviceId); - textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(identityKey.number)); - stepDone(2); - - return textsecure.protocol_wrapper.generateKeys().then(function(keys) { - stepDone(3); - return textsecure.api.registerKeys(keys).then(function() { - stepDone(4); - }); - }); - }); + return textsecure.api.confirmCode(provisionMessage.number, provisionMessage.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { + textsecure.storage.user.setNumberAndDeviceId(provisionMessage.number, result.deviceId); + textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(provisionMessage.number)); }); }; diff --git a/libtextsecure/protobufs.js b/libtextsecure/protobufs.js index 364b14d92..ac52443c8 100644 --- a/libtextsecure/protobufs.js +++ b/libtextsecure/protobufs.js @@ -1,7 +1,7 @@ ;(function() { function loadProtoBufs(filename) { - return dcodeIO.ProtoBuf.loadProtoFile({root: 'protos', file: filename}).build('textsecure'); + return dcodeIO.ProtoBuf.loadProtoFile({root: '/protos', file: filename}).build('textsecure'); }; var pushMessages = loadProtoBufs('IncomingPushMessageSignal.proto');