Add textsecure.AccountManager

This class should be used for account registration and for refreshing
prekeys for your account.
This commit is contained in:
lilia 2015-04-29 17:00:30 -07:00
parent a960acacc6
commit f465bdddbf
7 changed files with 327 additions and 225 deletions

View file

@ -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',
],

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
;(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

View file

@ -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');

View file

@ -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) {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
;(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;
});
}

View file

@ -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);
}
});
});
});
};

View file

@ -45,6 +45,7 @@
<script type="text/javascript" src="../storage/devices.js" data-cover></script>
<script type="text/javascript" src="../api.js"></script>
<script type="text/javascript" src="../sendmessage.js" data-cover></script>
<script type="text/javascript" src="../account_manager.js" data-></script>
<script type="text/javascript" src="fake_api.js"></script>
<script type="text/javascript" src="helpers_test.js"></script>