diff --git a/Gruntfile.js b/Gruntfile.js
index 91a97030bbfd..47decfd62a51 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -56,6 +56,7 @@ module.exports = function(grunt) {
'libtextsecure/helpers.js',
'libtextsecure/stringview.js',
'libtextsecure/api.js',
+ 'libtextsecure/account_manager.js',
'libtextsecure/message_receiver.js',
'libtextsecure/sendmessage.js',
],
diff --git a/js/libtextsecure.js b/js/libtextsecure.js
index 9d43f61011f1..e963e8ab2836 100644
--- a/js/libtextsecure.js
+++ b/js/libtextsecure.js
@@ -38913,116 +38913,6 @@ textsecure.processDecrypted = function(decrypted, source) {
});
};
-window.textsecure.refreshPreKeys = function() {
- return textsecure.api.getMyKeys().then(function(preKeyCount) {
- if (preKeyCount < 10) {
- generateKeys();
- }
- });
-};
-
-function createAccount(number, verificationCode, identityKeyPair, single_device) {
- textsecure.storage.put('identityKey', identityKeyPair);
-
- 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 registrationId = axolotl.util.generateRegistrationId();
- textsecure.storage.put("registrationId", registrationId);
-
- return textsecure.api.confirmCode(
- number, verificationCode, password, signalingKey, registrationId, single_device
- ).then(function(response) {
- textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1);
- textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(number));
-
- return textsecure.protocol_wrapper.generateKeys().then(textsecure.registration.done);
- });
-}
-
-function generateKeys(count, progressCallback) {
- if (count === undefined) {
- throw TypeError('generateKeys: count is undefined');
- }
- if (typeof progressCallback !== 'function') {
- progressCallback = undefined;
- }
- var store = textsecure.storage.axolotl;
- var identityKey = store.getMyIdentityKey();
- var result = { preKeys: [], identityKey: identityKey.pubKey };
- var promises = [];
-
- var startId = textsecure.storage.get('maxPreKeyId', 1);
- var signedKeyId = textsecure.storage.get('signedKeyId', 1);
-
- for (var keyId = startId; keyId < startId+count; ++keyId) {
- promises.push(
- axolotl.util.generatePreKey(keyId).then(function(res) {
- store.putPreKey(res.keyId, res.keyPair);
- result.preKeys.push({
- keyId : res.keyId,
- publicKey : res.keyPair.pubKey
- });
- if (progressCallback) { progressCallback(); }
- })
- );
- }
- promises.push(
- axolotl.util.generateSignedPreKey(identityKey, signedKeyId).then(function(res) {
- store.putSignedPreKey(res.keyId, res.keyPair);
- result.signedPreKey = {
- keyId : res.keyId,
- publicKey : res.keyPair.pubKey,
- signature : res.signature
- };
- })
- );
-
- store.removeSignedPreKey(signedKeyId - 2);
- textsecure.storage.put('maxPreKeyId', startId + count);
- textsecure.storage.put('signedKeyId', signedKeyId + 1);
-
- return Promise.all(promises).then(function() {
- return result;
- });
-};
-
-window.textsecure.registerSecondDevice = function(setProvisioningUrl, confirmNumber, progressCallback) {
- return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
- return new Promise(function(resolve) {
- new WebSocketResource(textsecure.api.getTempWebsocket(), function(request) {
- if (request.path == "/v1/address" && request.verb == "PUT") {
- var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
- setProvisioningUrl([
- 'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
- encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
- ].join(''));
- request.respond(200, 'OK');
- } else if (request.path == "/v1/message" && request.verb == "PUT") {
- var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
- request.respond(200, 'OK');
- resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
- return confirmNumber(provisionMessage.number).then(function() {
- return createAccount(
- provisionMessage.number,
- provisionMessage.provisioningCode,
- provisionMessage.identityKeyPair,
- false
- );
- });
- }));
- } else {
- console.log('Unknown websocket message', request.path);
- }
- });
- });
- });
-};
-
/* vim: ts=4:sw=4:expandtab
*
* This program is free software: you can redistribute it and/or modify
@@ -39480,6 +39370,167 @@ window.textsecure.api = function () {
return self;
}();
+/* vim: ts=4:sw=4:expandtab
+ *
+ * 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 .
+ */
+
+
+;(function () {
+ 'use strict';
+ window.textsecure = window.textsecure || {};
+
+ function AccountManager() {
+ }
+
+ AccountManager.prototype = {
+ constructor: AccountManager,
+ requestVoiceVerification: function(number) {
+ return TextSecureServer.requestVerificationVoice(number);
+ },
+ requestSMSVerification: function(number) {
+ return TextSecureServer.requestVerificationSMS(number);
+ },
+ registerSingleDevice: function(number, verificationCode) {
+ return axolotl.util.generateIdentityKeyPair().then(function(identityKeyPair) {
+ return createAccount(number, verificationCode, identityKeyPair, true).
+ then(function() { return generateKeys(100); }).
+ then(TextSecureServer.registerKeys).
+ then(textsecure.registration.done);
+ });
+ },
+ registerSecondDevice: function(setProvisioningUrl, confirmNumber, progressCallback) {
+ return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
+ return new Promise(function(resolve) {
+ new WebSocketResource(textsecure.api.getTempWebsocket(), function(request) {
+ if (request.path == "/v1/address" && request.verb == "PUT") {
+ var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
+ setProvisioningUrl([
+ 'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
+ encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
+ ].join(''));
+ request.respond(200, 'OK');
+ } else if (request.path == "/v1/message" && request.verb == "PUT") {
+ var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
+ request.respond(200, 'OK');
+ resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
+ return confirmNumber(provisionMessage.number).then(function() {
+ return createAccount(
+ provisionMessage.number,
+ provisionMessage.provisioningCode,
+ provisionMessage.identityKeyPair,
+ false
+ );
+ });
+ }));
+ } else {
+ console.log('Unknown websocket message', request.path);
+ }
+ });
+ });
+ }).then(function() {
+ return generateKeys(100, progressCallback);
+ }).then(TextSecureServer.registerKeys).then(textsecure.registration.done);
+ },
+ refreshPreKeys: function() {
+ return textsecure.api.getMyKeys().then(function(preKeyCount) {
+ if (preKeyCount < 10) {
+ return generateKeys(100);
+ }
+ });
+ }
+ };
+
+ function createAccount(number, verificationCode, identityKeyPair, single_device) {
+ textsecure.storage.put('identityKey', identityKeyPair);
+
+ 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 registrationId = axolotl.util.generateRegistrationId();
+ textsecure.storage.put("registrationId", registrationId);
+
+ return textsecure.api.confirmCode(
+ number, verificationCode, password, signalingKey, registrationId, single_device
+ ).then(function(response) {
+ textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1);
+ textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(number));
+ });
+ }
+
+ textsecure.AccountManager = AccountManager;
+
+}());
+
+function generateKeys(count, progressCallback) {
+ if (typeof progressCallback !== 'function') {
+ progressCallback = undefined;
+ }
+ var startId = textsecure.storage.get('maxPreKeyId', 1);
+ var signedKeyId = textsecure.storage.get('signedKeyId', 1);
+
+ if (typeof startId != 'number') {
+ throw new Error('Invalid maxPreKeyId');
+ }
+ if (typeof signedKeyId != 'number') {
+ throw new Error('Invalid signedKeyId');
+ }
+
+ textsecure.protocol_wrapper.startWorker();
+
+ var store = textsecure.storage.axolotl;
+ var identityKey = store.getMyIdentityKey();
+ var result = { preKeys: [], identityKey: identityKey.pubKey };
+ var promises = [];
+
+ for (var keyId = startId; keyId < startId+count; ++keyId) {
+ promises.push(
+ axolotl.util.generatePreKey(keyId).then(function(res) {
+ store.putPreKey(res.keyId, res.keyPair);
+ result.preKeys.push({
+ keyId : res.keyId,
+ publicKey : res.keyPair.pubKey
+ });
+ if (progressCallback) { progressCallback(); }
+ })
+ );
+ }
+
+ promises.push(
+ axolotl.util.generateSignedPreKey(identityKey, signedKeyId).then(function(res) {
+ store.putSignedPreKey(res.keyId, res.keyPair);
+ result.signedPreKey = {
+ keyId : res.keyId,
+ publicKey : res.keyPair.pubKey,
+ signature : res.signature
+ };
+ })
+ );
+
+ store.removeSignedPreKey(signedKeyId - 2);
+ textsecure.storage.put('maxPreKeyId', startId + count);
+ textsecure.storage.put('signedKeyId', signedKeyId + 1);
+ return Promise.all(promises).then(function() {
+ textsecure.protocol_wrapper.stopWorker();
+ return result;
+ });
+}
+
/* vim: ts=4:sw=4:expandtab
*
* This program is free software: you can redistribute it and/or modify
diff --git a/js/options.js b/js/options.js
index 15a629dde4d4..e716092c610e 100644
--- a/js/options.js
+++ b/js/options.js
@@ -52,7 +52,7 @@
e.stopPropagation();
$('.confirmation-dialog').hide();
$('.progress-dialog').show();
- $('.progress-dialog .status').text('Registering new device...');
+ $('.progress-dialog .status').text('Generating Keys');
resolve();
});
$('.modal-container').show();
@@ -78,7 +78,8 @@
$('#init-setup').show().addClass('in');
$('#status').text("Connecting...");
- bg.textsecure.registerSecondDevice(setProvisioningUrl, confirmNumber, incrementCounter).then(function() {
+ var accountManager = new bg.textsecure.AccountManager();
+ accountManager.registerSecondDevice(setProvisioningUrl, confirmNumber, incrementCounter).then(function() {
$('.modal-container').hide();
$('#init-setup').hide();
$('#setup-complete').show().addClass('in');
diff --git a/js/register.js b/js/register.js
index 306da4344a16..ee7c00cb8bd6 100644
--- a/js/register.js
+++ b/js/register.js
@@ -17,6 +17,7 @@
;(function() {
'use strict';
var bg = extension.windows.getBackground();
+ var accountManager = new bg.textsecure.AccountManager();
function log(s) {
console.log(s);
@@ -61,7 +62,7 @@
$('#error').hide();
var number = phoneView.validateNumber();
if (number) {
- bg.textsecure.api.requestVerificationVoice(number).catch(displayError);
+ accountManager.requestVoiceVerification(number).catch(displayError);
$('#step2').addClass('in').fadeIn();
} else {
$('#number-container').addClass('invalid');
@@ -72,7 +73,7 @@
$('#error').hide();
var number = phoneView.validateNumber();
if (number) {
- bg.textsecure.api.requestVerificationSMS(number).catch(displayError);
+ accountManager.requestSMSVerification(number).catch(displayError);
$('#step2').addClass('in').fadeIn();
} else {
$('#number-container').addClass('invalid');
@@ -86,7 +87,7 @@
localStorage.clear();
localStorage.setItem('first_install_ran', 1);
- bg.textsecure.registerSingleDevice(number, verificationCode).then(function() {
+ accountManager.registerSingleDevice(number, verificationCode).then(function() {
extension.navigator.tabs.create("options.html");
window.close();
}).catch(function(e) {
diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js
new file mode 100644
index 000000000000..6915791f7c9c
--- /dev/null
+++ b/libtextsecure/account_manager.js
@@ -0,0 +1,157 @@
+/* vim: ts=4:sw=4:expandtab
+ *
+ * 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 .
+ */
+
+
+;(function () {
+ 'use strict';
+ window.textsecure = window.textsecure || {};
+
+ function AccountManager() {
+ }
+
+ AccountManager.prototype = {
+ constructor: AccountManager,
+ requestVoiceVerification: function(number) {
+ return TextSecureServer.requestVerificationVoice(number);
+ },
+ requestSMSVerification: function(number) {
+ return TextSecureServer.requestVerificationSMS(number);
+ },
+ registerSingleDevice: function(number, verificationCode) {
+ return axolotl.util.generateIdentityKeyPair().then(function(identityKeyPair) {
+ return createAccount(number, verificationCode, identityKeyPair, true).
+ then(function() { return generateKeys(100); }).
+ then(TextSecureServer.registerKeys).
+ then(textsecure.registration.done);
+ });
+ },
+ registerSecondDevice: function(setProvisioningUrl, confirmNumber, progressCallback) {
+ return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
+ return new Promise(function(resolve) {
+ new WebSocketResource(textsecure.api.getTempWebsocket(), function(request) {
+ if (request.path == "/v1/address" && request.verb == "PUT") {
+ var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
+ setProvisioningUrl([
+ 'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
+ encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
+ ].join(''));
+ request.respond(200, 'OK');
+ } else if (request.path == "/v1/message" && request.verb == "PUT") {
+ var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
+ request.respond(200, 'OK');
+ resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
+ return confirmNumber(provisionMessage.number).then(function() {
+ return createAccount(
+ provisionMessage.number,
+ provisionMessage.provisioningCode,
+ provisionMessage.identityKeyPair,
+ false
+ );
+ });
+ }));
+ } else {
+ console.log('Unknown websocket message', request.path);
+ }
+ });
+ });
+ }).then(function() {
+ return generateKeys(100, progressCallback);
+ }).then(TextSecureServer.registerKeys).then(textsecure.registration.done);
+ },
+ refreshPreKeys: function() {
+ return textsecure.api.getMyKeys().then(function(preKeyCount) {
+ if (preKeyCount < 10) {
+ return generateKeys(100);
+ }
+ });
+ }
+ };
+
+ function createAccount(number, verificationCode, identityKeyPair, single_device) {
+ textsecure.storage.put('identityKey', identityKeyPair);
+
+ 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 registrationId = axolotl.util.generateRegistrationId();
+ textsecure.storage.put("registrationId", registrationId);
+
+ return textsecure.api.confirmCode(
+ number, verificationCode, password, signalingKey, registrationId, single_device
+ ).then(function(response) {
+ textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1);
+ textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(number));
+ });
+ }
+
+ textsecure.AccountManager = AccountManager;
+
+}());
+
+function generateKeys(count, progressCallback) {
+ if (typeof progressCallback !== 'function') {
+ progressCallback = undefined;
+ }
+ var startId = textsecure.storage.get('maxPreKeyId', 1);
+ var signedKeyId = textsecure.storage.get('signedKeyId', 1);
+
+ if (typeof startId != 'number') {
+ throw new Error('Invalid maxPreKeyId');
+ }
+ if (typeof signedKeyId != 'number') {
+ throw new Error('Invalid signedKeyId');
+ }
+
+ var store = textsecure.storage.axolotl;
+ var identityKey = store.getMyIdentityKey();
+ var result = { preKeys: [], identityKey: identityKey.pubKey };
+ var promises = [];
+
+ for (var keyId = startId; keyId < startId+count; ++keyId) {
+ promises.push(
+ axolotl.util.generatePreKey(keyId).then(function(res) {
+ store.putPreKey(res.keyId, res.keyPair);
+ result.preKeys.push({
+ keyId : res.keyId,
+ publicKey : res.keyPair.pubKey
+ });
+ if (progressCallback) { progressCallback(); }
+ })
+ );
+ }
+
+ promises.push(
+ axolotl.util.generateSignedPreKey(identityKey, signedKeyId).then(function(res) {
+ store.putSignedPreKey(res.keyId, res.keyPair);
+ result.signedPreKey = {
+ keyId : res.keyId,
+ publicKey : res.keyPair.pubKey,
+ signature : res.signature
+ };
+ })
+ );
+
+ store.removeSignedPreKey(signedKeyId - 2);
+ textsecure.storage.put('maxPreKeyId', startId + count);
+ textsecure.storage.put('signedKeyId', signedKeyId + 1);
+ return Promise.all(promises).then(function() {
+ return result;
+ });
+}
diff --git a/libtextsecure/helpers.js b/libtextsecure/helpers.js
index b881dc26163d..ada38b17e65b 100644
--- a/libtextsecure/helpers.js
+++ b/libtextsecure/helpers.js
@@ -247,113 +247,3 @@ textsecure.processDecrypted = function(decrypted, source) {
return decrypted;
});
};
-
-window.textsecure.refreshPreKeys = function() {
- return textsecure.api.getMyKeys().then(function(preKeyCount) {
- if (preKeyCount < 10) {
- generateKeys();
- }
- });
-};
-
-function createAccount(number, verificationCode, identityKeyPair, single_device) {
- textsecure.storage.put('identityKey', identityKeyPair);
-
- 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 registrationId = axolotl.util.generateRegistrationId();
- textsecure.storage.put("registrationId", registrationId);
-
- return textsecure.api.confirmCode(
- number, verificationCode, password, signalingKey, registrationId, single_device
- ).then(function(response) {
- textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1);
- textsecure.storage.put("regionCode", libphonenumber.util.getRegionCodeForNumber(number));
-
- return textsecure.protocol_wrapper.generateKeys().then(textsecure.registration.done);
- });
-}
-
-function generateKeys(count, progressCallback) {
- if (count === undefined) {
- throw TypeError('generateKeys: count is undefined');
- }
- if (typeof progressCallback !== 'function') {
- progressCallback = undefined;
- }
- var store = textsecure.storage.axolotl;
- var identityKey = store.getMyIdentityKey();
- var result = { preKeys: [], identityKey: identityKey.pubKey };
- var promises = [];
-
- var startId = textsecure.storage.get('maxPreKeyId', 1);
- var signedKeyId = textsecure.storage.get('signedKeyId', 1);
-
- for (var keyId = startId; keyId < startId+count; ++keyId) {
- promises.push(
- axolotl.util.generatePreKey(keyId).then(function(res) {
- store.putPreKey(res.keyId, res.keyPair);
- result.preKeys.push({
- keyId : res.keyId,
- publicKey : res.keyPair.pubKey
- });
- if (progressCallback) { progressCallback(); }
- })
- );
- }
- promises.push(
- axolotl.util.generateSignedPreKey(identityKey, signedKeyId).then(function(res) {
- store.putSignedPreKey(res.keyId, res.keyPair);
- result.signedPreKey = {
- keyId : res.keyId,
- publicKey : res.keyPair.pubKey,
- signature : res.signature
- };
- })
- );
-
- store.removeSignedPreKey(signedKeyId - 2);
- textsecure.storage.put('maxPreKeyId', startId + count);
- textsecure.storage.put('signedKeyId', signedKeyId + 1);
-
- return Promise.all(promises).then(function() {
- return result;
- });
-};
-
-window.textsecure.registerSecondDevice = function(setProvisioningUrl, confirmNumber, progressCallback) {
- return textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
- return new Promise(function(resolve) {
- new WebSocketResource(textsecure.api.getTempWebsocket(), function(request) {
- if (request.path == "/v1/address" && request.verb == "PUT") {
- var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
- setProvisioningUrl([
- 'tsdevice:/?uuid=', proto.uuid, '&pub_key=',
- encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))
- ].join(''));
- request.respond(200, 'OK');
- } else if (request.path == "/v1/message" && request.verb == "PUT") {
- var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
- request.respond(200, 'OK');
- resolve(cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
- return confirmNumber(provisionMessage.number).then(function() {
- return createAccount(
- provisionMessage.number,
- provisionMessage.provisioningCode,
- provisionMessage.identityKeyPair,
- false
- );
- });
- }));
- } else {
- console.log('Unknown websocket message', request.path);
- }
- });
- });
- });
-};
diff --git a/libtextsecure/test/index.html b/libtextsecure/test/index.html
index 5e8c26e9eb64..782583e06b2e 100644
--- a/libtextsecure/test/index.html
+++ b/libtextsecure/test/index.html
@@ -45,6 +45,7 @@
+