Integrate libaxolotl async storage changes

* Session records are now opaque strings, so treat them that way:
  - no more cross checking identity key and session records
  - Move hasOpenSession to axolotl wrapper
  - Remote registration ids must be fetched async'ly via protocol wrapper
* Implement async AxolotlStore using textsecure.storage
* Add some db stores and move prekeys and signed keys to indexeddb
* Add storage tests
* Rename identityKey storage key from libaxolotl25519KeyidentityKey to
  simply identityKey, since it's no longer hardcoded in libaxolotl
* Rework registration and key-generation, keeping logic in libtextsecure
  and rendering in options.js.
* Remove key_worker since workers are handled at the libaxolotl level
  now
This commit is contained in:
lilia 2015-04-01 13:08:09 -07:00
parent 8304aa903a
commit 96eafc7750
20 changed files with 1014 additions and 40445 deletions

183
js/axolotl_store.js Normal file
View file

@ -0,0 +1,183 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
;(function() {
'use strict';
function isStringable(thing) {
return (thing === Object(thing) &&
(thing.__proto__ == StaticArrayBufferProto ||
thing.__proto__ == StaticUint8ArrayProto ||
thing.__proto__ == StaticByteBufferProto));
}
function convertToArrayBuffer(thing) {
if (thing === undefined)
return undefined;
if (thing === Object(thing)) {
if (thing.__proto__ == StaticArrayBufferProto)
return thing;
//TODO: Several more cases here...
}
if (thing instanceof Array) {
// Assuming Uint16Array from curve25519
//TODO: Move to convertToArrayBuffer
var res = new ArrayBuffer(thing.length * 2);
var uint = new Uint16Array(res);
for (var i = 0; i < thing.length; i++)
uint[i] = thing[i];
return res;
}
var str;
if (isStringable(thing))
str = stringObject(thing);
else if (typeof thing == "string")
str = thing;
else
throw new Error("Tried to convert a non-stringable thing of type " + typeof thing + " to an array buffer");
var res = new ArrayBuffer(str.length);
var uint = new Uint8Array(res);
for (var i = 0; i < str.length; i++)
uint[i] = str.charCodeAt(i);
return res;
}
var Model = Backbone.Model.extend({ database: Whisper.Database });
var PreKey = Model.extend({ storeName: 'preKeys' });
var SignedPreKey = Model.extend({ storeName: 'signedPreKeys' });
function AxolotlStore() {}
AxolotlStore.prototype = {
constructor: AxolotlStore,
get: function(key,defaultValue) {
return textsecure.storage.get(key, defaultValue);
},
put: function(key, value) {
textsecure.storage.put(key, value);
},
remove: function(key) {
textsecure.storage.remove(key);
},
getMyIdentityKey: function() {
var res = textsecure.storage.get('identityKey');
if (res === undefined)
return undefined;
return {
pubKey: convertToArrayBuffer(res.pubKey),
privKey: convertToArrayBuffer(res.privKey)
};
},
getMyRegistrationId: function() {
return textsecure.storage.get('registrationId');
},
getIdentityKey: function(identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get identity key for undefined/null key");
return Promise.resolve(convertToArrayBuffer(textsecure.storage.devices.getIdentityKeyForNumber(textsecure.utils.unencodeNumber(identifier)[0])));
},
putIdentityKey: function(identifier, identityKey) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to put identity key for undefined/null key");
return Promise.resolve(textsecure.storage.devices.checkSaveIdentityKeyForNumber(textsecure.utils.unencodeNumber(identifier)[0], identityKey));
},
/* Returns a prekeypair object or undefined */
getPreKey: function(keyId) {
var prekey = new PreKey({id: keyId});
return new Promise(function(resolve) {
prekey.fetch().then(function() {
resolve({
pubKey: prekey.attributes.publicKey,
privKey: prekey.attributes.privateKey
});
}).fail(resolve);
});
},
putPreKey: function(keyId, keyPair) {
var prekey = new PreKey({
id : keyId,
publicKey : keyPair.pubKey,
privateKey : keyPair.privKey
});
return new Promise(function(resolve) {
prekey.save().always(function() {
resolve();
});
});
},
removePreKey: function(keyId) {
var prekey = new PreKey({id: keyId});
return new Promise(function(resolve) {
prekey.destroy().then(function() {
resolve();
});
});
},
/* Returns a signed keypair object or undefined */
getSignedPreKey: function(keyId) {
var prekey = new SignedPreKey({id: keyId});
return new Promise(function(resolve) {
prekey.fetch().then(function() {
resolve({
pubKey: prekey.attributes.publicKey,
privKey: prekey.attributes.privateKey
});
}).fail(resolve);
});
},
putSignedPreKey: function(keyId, keyPair) {
var prekey = new SignedPreKey({
id : keyId,
publicKey : keyPair.pubKey,
privateKey : keyPair.privKey
});
return new Promise(function(resolve) {
prekey.save().always(function() {
resolve();
});
});
},
removeSignedPreKey: function(keyId) {
var prekey = new SignedPreKey({id: keyId});
return new Promise(function(resolve) {
prekey.destroy().then(function() {
resolve();
});
});
},
getSession: function(identifier) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to get session for undefined/null key");
return new Promise(function(resolve) {
resolve(textsecure.storage.sessions.getSessionsForNumber(identifier));
});
},
putSession: function(identifier, record) {
if (identifier === null || identifier === undefined)
throw new Error("Tried to put session for undefined/null key");
return new Promise(function(resolve) {
resolve(textsecure.storage.sessions.putSessionsForDevice(identifier, record));
});
}
};
window.AxolotlStore = AxolotlStore;
})();

View file

@ -33,6 +33,10 @@
conversations.createIndex("inbox", "active_at", { unique: false });
conversations.createIndex("group", "members", { unique: false, multiEntry: true });
conversations.createIndex("type", "type", { unique: false });
var preKeys = transaction.db.createObjectStore("preKeys");
var signedPreKeys = transaction.db.createObjectStore("signedPreKeys");
next();
}
}

File diff suppressed because one or more lines are too long

View file

@ -37771,47 +37771,12 @@ axolotlInternal.RecipientRecord = function() {
}();
})();
'use strict';
;(function() {
var axolotlInstance = axolotl.protocol({
getMyRegistrationId: function() {
return textsecure.storage.get("registrationId");
},
put: function(key, value) {
return textsecure.storage.put("libaxolotl" + key, value);
},
get: function(key, defaultValue) {
return textsecure.storage.get("libaxolotl" + key, defaultValue);
},
remove: function(key) {
return textsecure.storage.remove("libaxolotl" + key);
},
identityKeys: {
get: function(identifier) {
return textsecure.storage.devices.getIdentityKeyForNumber(textsecure.utils.unencodeNumber(identifier)[0]);
},
put: function(identifier, identityKey) {
return textsecure.storage.devices.checkSaveIdentityKeyForNumber(textsecure.utils.unencodeNumber(identifier)[0], identityKey);
},
},
sessions: {
get: function(identifier) {
return textsecure.storage.sessions.getSessionsForNumber(identifier);
},
put: function(identifier, record) {
return textsecure.storage.sessions.putSessionsForDevice(identifier, record);
}
}
},
function(keys) {
return textsecure.api.registerKeys(keys).catch(function(e) {
//TODO: Notify the user somehow?
console.error(e);
});
});
'use strict';
window.textsecure = window.textsecure || {};
window.textsecure.storage = window.textsecure.storage || {};
textsecure.storage.axolotl = new AxolotlStore();
var axolotlInstance = axolotl.protocol(textsecure.storage.axolotl);
var decodeMessageContents = function(res) {
var finalMessage = textsecure.protobuf.PushMessageContent.decode(res[0]);
@ -37826,7 +37791,7 @@ axolotlInternal.RecipientRecord = function() {
var handlePreKeyWhisperMessage = function(from, message) {
try {
return textsecure.protocol_wrapper.handlePreKeyWhisperMessage(from, message);
return axolotlInstance.handlePreKeyWhisperMessage(from, message);
} catch(e) {
if (e.message === 'Unknown identity key') {
// create an error that the UI will pick up and ask the
@ -37845,7 +37810,7 @@ axolotlInternal.RecipientRecord = function() {
return Promise.resolve(textsecure.protobuf.PushMessageContent.decode(proto.message));
case textsecure.protobuf.IncomingPushMessageSignal.Type.CIPHERTEXT:
var from = proto.source + "." + (proto.sourceDevice == null ? 0 : proto.sourceDevice);
return textsecure.protocol_wrapper.decryptWhisperMessage(from, getString(proto.message)).then(decodeMessageContents);
return axolotlInstance.decryptWhisperMessage(from, getString(proto.message)).then(decodeMessageContents);
case textsecure.protobuf.IncomingPushMessageSignal.Type.PREKEY_BUNDLE:
if (proto.message.readUint8() != ((3 << 4) | 3))
throw new Error("Bad version byte");
@ -37860,27 +37825,34 @@ axolotlInternal.RecipientRecord = function() {
closeOpenSessionForDevice: function(encodedNumber) {
return axolotlInstance.closeOpenSessionForDevice(encodedNumber)
},
decryptWhisperMessage: function(encodedNumber, messageBytes, session) {
return axolotlInstance.decryptWhisperMessage(encodedNumber, messageBytes, session);
},
handlePreKeyWhisperMessage: function(from, encodedMessage) {
return axolotlInstance.handlePreKeyWhisperMessage(from, encodedMessage);
},
encryptMessageFor: function(deviceObject, pushMessageContent) {
return axolotlInstance.encryptMessageFor(deviceObject, pushMessageContent);
},
generateKeys: function() {
return axolotlInstance.generateKeys();
generateKeys: function(count, progressCallback) {
if (textsecure.worker_path) {
axolotlInstance.startWorker(textsecure.worker_path);
}
return generateKeys(count, progressCallback).then(function(result) {
axolotlInstance.stopWorker();
return result;
});
},
createIdentityKeyRecvSocket: function() {
return axolotlInstance.createIdentityKeyRecvSocket();
},
hasOpenSession: function(encodedNumber) {
return axolotlInstance.hasOpenSession(encodedNumber);
},
getRegistrationId: function(encodedNumber) {
return axolotlInstance.getRegistrationId(encodedNumber);
}
};
var tryMessageAgain = function(from, encodedMessage) {
return textsecure.protocol_wrapper.handlePreKeyWhisperMessage(from, encodedMessage).then(decodeMessageContents);
return axolotlInstance.handlePreKeyWhisperMessage(from, encodedMessage).then(decodeMessageContents);
}
textsecure.replay.registerFunction(tryMessageAgain, textsecure.replay.Type.INIT_SESSION);
})();
/* vim: ts=4:sw=4:expandtab
@ -38136,46 +38108,30 @@ axolotlInternal.RecipientRecord = function() {
if (sessions[deviceId] === undefined)
return undefined;
var record = new axolotl.sessions.RecipientRecord();
record.deserialize(sessions[deviceId]);
if (getString(textsecure.storage.devices.getIdentityKeyForNumber(number)) !== getString(record.identityKey))
throw new Error("Got mismatched identity key on device object load");
return record;
return sessions[deviceId];
},
putSessionsForDevice: function(encodedNumber, record) {
var number = textsecure.utils.unencodeNumber(encodedNumber)[0];
var deviceId = textsecure.utils.unencodeNumber(encodedNumber)[1];
textsecure.storage.devices.checkSaveIdentityKeyForNumber(number, record.identityKey);
var sessions = textsecure.storage.get("sessions" + number);
if (sessions === undefined)
sessions = {};
sessions[deviceId] = record.serialize();
sessions[deviceId] = record;
textsecure.storage.put("sessions" + number, sessions);
var device = textsecure.storage.devices.getDeviceObject(encodedNumber);
if (device === undefined) {
var identityKey = textsecure.storage.devices.getIdentityKeyForNumber(number);
device = { encodedNumber: encodedNumber,
//TODO: Remove this duplication
identityKey: record.identityKey
identityKey: identityKey
};
}
if (getString(device.identityKey) !== getString(record.identityKey)) {
console.error("Got device object with key inconsistent after checkSaveIdentityKeyForNumber returned!");
throw new Error("Tried to put session for device with changed identity key");
}
return textsecure.storage.devices.saveDeviceObject(device);
},
haveOpenSessionForDevice: function(encodedNumber) {
var sessions = textsecure.storage.sessions.getSessionsForNumber(encodedNumber);
if (sessions === undefined || !sessions.haveOpenSession())
return false;
return true;
},
// Use textsecure.storage.devices.removeIdentityKeyForNumber (which calls this) instead
_removeIdentityKeyForNumber: function(number) {
textsecure.storage.remove("sessions" + number);
@ -38820,6 +38776,8 @@ window.textsecure.utils = function() {
for (var key in thing)
res[key] = ensureStringed(thing[key]);
return res;
} else if (thing === null) {
return null;
}
throw new Error("unsure of how to jsonify object of type " + typeof thing);
@ -38953,9 +38911,11 @@ textsecure.processDecrypted = function(decrypted, source) {
return Promise.all(promises).then(function() {
return decrypted;
});
}
};
function createAccount(number, verificationCode, identityKeyPair, single_device) {
textsecure.storage.put('identityKey', identityKeyPair);
window.textsecure.registerSingleDevice = function(number, verificationCode, stepDone) {
var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
textsecure.storage.put('signaling_key', signalingKey);
@ -38963,39 +38923,95 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step
password = password.substring(0, password.length - 2);
textsecure.storage.put("password", password);
var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0];
registrationId = registrationId & 0x3fff;
var registrationId = axolotl.util.generateRegistrationId();
textsecure.storage.put("registrationId", registrationId);
return textsecure.api.confirmCode(number, verificationCode, password, signalingKey, registrationId, true).then(function() {
textsecure.storage.user.setNumberAndDeviceId(number, 1);
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));
stepDone(1);
return textsecure.protocol_wrapper.generateKeys().then(function(keys) {
stepDone(2);
return textsecure.api.registerKeys(keys).then(function() {
stepDone(3);
});
});
return textsecure.protocol_wrapper.generateKeys().then(textsecure.registration.done);
});
}
window.textsecure.registerSecondDevice = function(provisionMessage) {
var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
textsecure.storage.put('signaling_key', signalingKey);
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 password = btoa(getString(textsecure.crypto.getRandomBytes(16)));
password = password.substring(0, password.length - 2);
textsecure.storage.put("password", password);
var startId = textsecure.storage.get('maxPreKeyId', 1);
var signedKeyId = textsecure.storage.get('signedKeyId', 1);
var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0];
registrationId = registrationId & 0x3fff;
textsecure.storage.put("registrationId", registrationId);
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
};
})
);
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));
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);
}
});
});
});
};
@ -39584,22 +39600,21 @@ window.textsecure.messaging = function() {
return new Promise(function() { throw new Error("Mismatched relays for number " + number); });
}
var registrationId = deviceObjectList[i].registrationId;
if (registrationId === undefined) // ie this isnt a first-send-keyful deviceObject
registrationId = textsecure.storage.sessions.getSessionsForNumber(deviceObjectList[i].encodedNumber).registrationId;
return textsecure.protocol_wrapper.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
textsecure.storage.devices.removeTempKeysFromDevice(deviceObjectList[i].encodedNumber);
return textsecure.protocol_wrapper.getRegistrationId(deviceObjectList[i].encodedNumber).then(function(registrationId) {
textsecure.storage.devices.removeTempKeysFromDevice(deviceObjectList[i].encodedNumber);
jsonData[i] = {
type: encryptedMsg.type,
destinationDeviceId: textsecure.utils.unencodeNumber(deviceObjectList[i].encodedNumber)[1],
destinationRegistrationId: registrationId,
body: encryptedMsg.body,
timestamp: timestamp
};
jsonData[i] = {
type: encryptedMsg.type,
destinationDeviceId: textsecure.utils.unencodeNumber(deviceObjectList[i].encodedNumber)[1],
destinationRegistrationId: registrationId,
body: encryptedMsg.body,
timestamp: timestamp
};
if (deviceObjectList[i].relay !== undefined)
jsonData[i].relay = deviceObjectList[i].relay;
if (deviceObjectList[i].relay !== undefined)
jsonData[i].relay = deviceObjectList[i].relay;
});
});
}
@ -39615,37 +39630,37 @@ window.textsecure.messaging = function() {
groupId = getString(groupId);
var doUpdate = false;
for (var i in devicesForNumber) {
var registrationId = deviceObjectList[i].registrationId;
if (registrationId === undefined) // ie this isnt a first-send-keyful deviceObject
registrationId = textsecure.storage.sessions.getSessionsForNumber(deviceObjectList[i].encodedNumber).registrationId;
if (textsecure.storage.groups.needUpdateByDeviceRegistrationId(groupId, number, devicesForNumber[i].encodedNumber, registrationId))
doUpdate = true;
}
if (!doUpdate)
return Promise.resolve(true);
var group = textsecure.storage.groups.getGroup(groupId);
var numberIndex = group.numbers.indexOf(number);
if (numberIndex < 0) // This is potentially a multi-message rare racing-AJAX race
return Promise.reject("Tried to refresh group to non-member");
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(group.id);
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
proto.group.members = group.numbers;
proto.group.name = group.name === undefined ? null : group.name;
if (group.avatar !== undefined) {
return makeAttachmentPointer(group.avatar).then(function(attachment) {
proto.group.avatar = attachment;
return sendMessageToDevices(Date.now(), number, devicesForNumber, proto);
Promise.all(devicesForNumber.map(function(device) {
return textsecure.protocol_wrapper.getRegistrationId(device.encodedNumber).then(function(registrationId) {
if (textsecure.storage.groups.needUpdateByDeviceRegistrationId(groupId, number, devicesForNumber[i].encodedNumber, registrationId))
doUpdate = true;
});
} else {
return sendMessageToDevices(Date.now(), number, devicesForNumber, proto);
}
})).then(function() {
if (!doUpdate)
return Promise.resolve(true);
var group = textsecure.storage.groups.getGroup(groupId);
var numberIndex = group.numbers.indexOf(number);
if (numberIndex < 0) // This is potentially a multi-message rare racing-AJAX race
return Promise.reject("Tried to refresh group to non-member");
var proto = new textsecure.protobuf.PushMessageContent();
proto.group = new textsecure.protobuf.PushMessageContent.GroupContext();
proto.group.id = toArrayBuffer(group.id);
proto.group.type = textsecure.protobuf.PushMessageContent.GroupContext.Type.UPDATE;
proto.group.members = group.numbers;
proto.group.name = group.name === undefined ? null : group.name;
if (group.avatar !== undefined) {
return makeAttachmentPointer(group.avatar).then(function(attachment) {
proto.group.avatar = attachment;
return sendMessageToDevices(Date.now(), number, devicesForNumber, proto);
});
} else {
return sendMessageToDevices(Date.now(), number, devicesForNumber, proto);
}
});
}
var tryMessageAgain = function(number, encodedMessage, timestamp) {
@ -39730,7 +39745,7 @@ window.textsecure.messaging = function() {
var promises = [];
for (var j in devicesForNumber)
if (!textsecure.storage.sessions.haveOpenSessionForDevice(devicesForNumber[j].encodedNumber))
if (!textsecure.protocol_wrapper.hasOpenSession(devicesForNumber[j].encodedNumber))
promises[promises.length] = getKeysForNumber(number, [parseInt(textsecure.utils.unencodeNumber(devicesForNumber[j].encodedNumber)[1])]);
Promise.all(promises).then(function() {

View file

@ -35,78 +35,54 @@
}
}
function setProvisioningUrl(url) {
$('#status').text('');
new QRCode($('#qr')[0]).makeCode(url);
}
function confirmNumber(number) {
return new Promise(function(resolve, reject) {
$('#qr').hide();
$('.confirmation-dialog .number').text(number);
$('.confirmation-dialog .cancel').click(function(e) {
localStorage.clear();
reject();
});
$('.confirmation-dialog .ok').click(function(e) {
e.stopPropagation();
$('.confirmation-dialog').hide();
$('.progress-dialog').show();
$('.progress-dialog .status').text('Registering new device...');
resolve();
});
$('.modal-container').show();
});
}
var counter = 0;
function incrementCounter() {
$('.progress-dialog .bar').css('width', (++counter * 100 / 100) + '%');
}
$('.modal-container .cancel').click(function() {
$('.modal-container').hide();
});
$(function() {
if (textsecure.registration.isDone()) {
$('#complete-number').text(textsecure.storage.user.getNumber());
var bg = extension.windows.getBackground();
if (bg.textsecure.registration.isDone()) {
$('#complete-number').text(bg.textsecure.storage.user.getNumber());
$('#setup-complete').show().addClass('in');
initOptions();
} else {
$('#init-setup').show().addClass('in');
$('#status').text("Connecting...");
textsecure.protocol_wrapper.createIdentityKeyRecvSocket().then(function(cryptoInfo) {
var qrCode = new QRCode(document.getElementById('qr'));
var socket = textsecure.api.getTempWebsocket();
new WebSocketResource(socket, function(request) {
if (request.path == "/v1/address" && request.verb == "PUT") {
var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
var url = [ 'tsdevice:/', '?uuid=', proto.uuid, '&pub_key=',
encodeURIComponent(btoa(String.fromCharCode.apply(null, new Uint8Array(cryptoInfo.pubKey)))) ].join('');
$('#status').text('');
qrCode.makeCode(url);
request.respond(200, 'OK');
} else if (request.path == "/v1/message" && request.verb == "PUT") {
var envelope = textsecure.protobuf.ProvisionEnvelope.decode(request.body, 'binary');
cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(provisionMessage) {
$('.confirmation-dialog .number').text(provisionMessage.number);
$('.confirmation-dialog .cancel').click(function(e) {
localStorage.clear();
});
$('.confirmation-dialog .ok').click(function(e) {
e.stopPropagation();
$('.confirmation-dialog').hide();
$('.progress-dialog').show();
$('.progress-dialog .status').text('Registering new device...');
window.textsecure.registerSecondDevice(provisionMessage).then(function() {
$('.progress-dialog .status').text('Generating keys...');
var counter = 0;
var myWorker = new Worker('/js/key_worker.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;
$('.progress-dialog .bar').css('width', (counter * 100 / 105) + '%');
break;
case 'remove':
textsecure.storage.remove(e.data.key);
break;
case 'done':
$('.progress-dialog .status').text('Uploading keys...');
textsecure.api.registerKeys(e.data.keys).then(function() {
textsecure.registration.done();
$('.modal-container').hide();
$('#init-setup').hide();
$('#setup-complete').show().addClass('in');
initOptions();
});
}
};
});
});
$('.modal-container').show();
});
} else
console.log(request.path);
});
bg.textsecure.registerSecondDevice(setProvisioningUrl, confirmNumber, incrementCounter).then(function() {
$('.modal-container').hide();
$('#init-setup').hide();
$('#setup-complete').show().addClass('in');
initOptions();
});
}
});

View file

@ -16,6 +16,7 @@
;(function() {
'use strict';
var bg = extension.windows.getBackground();
function log(s) {
console.log(s);
@ -36,7 +37,7 @@
var phoneView = new Whisper.PhoneInputView({el: $('#phone-number-input')});
phoneView.$el.find('input.number').intlTelInput();
var number = textsecure.storage.user.getNumber();
var number = bg.textsecure.storage.user.getNumber();
if (number) {
$('input.number').val(number);
}
@ -60,7 +61,7 @@
$('#error').hide();
var number = phoneView.validateNumber();
if (number) {
textsecure.api.requestVerificationVoice(number).catch(displayError);
bg.textsecure.api.requestVerificationVoice(number).catch(displayError);
$('#step2').addClass('in').fadeIn();
} else {
$('#number-container').addClass('invalid');
@ -71,7 +72,7 @@
$('#error').hide();
var number = phoneView.validateNumber();
if (number) {
textsecure.api.requestVerificationSMS(number).catch(displayError);
bg.textsecure.api.requestVerificationSMS(number).catch(displayError);
$('#step2').addClass('in').fadeIn();
} else {
$('#number-container').addClass('invalid');
@ -80,40 +81,14 @@
$('#form').submit(function(e) {
e.preventDefault();
log('registering');
var number = phoneView.validateNumber();
var verificationCode = $('#code').val().replace(/\D+/g, "");
var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
var password = btoa(String.fromCharCode.apply(null, new Uint8Array(textsecure.crypto.getRandomBytes(16))));
password = password.substring(0, password.length - 2);
var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0];
registrationId = registrationId & 0x3fff;
log('clearing data');
localStorage.clear();
localStorage.setItem('first_install_ran', 1);
textsecure.storage.put('registrationId', registrationId);
textsecure.storage.put('signaling_key', signalingKey);
textsecure.storage.put('password', password);
textsecure.storage.user.setNumberAndDeviceId(number, 1);
textsecure.storage.put('regionCode', libphonenumber.util.getRegionCodeForNumber(number));
log('verifying code');
return textsecure.api.confirmCode(
number, verificationCode, password, signalingKey, registrationId, true
).then(function() {
log('generating keys');
return textsecure.protocol_wrapper.generateKeys().then(function(keys) {
log('uploading keys');
return textsecure.api.registerKeys(keys).then(function() {
textsecure.registration.done();
log('done');
chrome.runtime.reload();
});
});
bg.textsecure.registerSingleDevice(number, verificationCode).then(function() {
extension.navigator.tabs.create("options.html");
window.close();
}).catch(function(e) {
log(e);
});