Mostly done device-bringup stuff

This commit is contained in:
Matt Corallo 2015-01-23 11:19:29 -10:00 committed by lilia
parent c0a8c00884
commit 02d0c58e5e
8 changed files with 64 additions and 73 deletions

View file

@ -5694,8 +5694,8 @@ CryptoJS.lib.Cipher || (function (undefined) {
WhisperMessage : protocolMessages.WhisperMessage, WhisperMessage : protocolMessages.WhisperMessage,
PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage, PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage,
ProvisioningUuid : deviceMessages.ProvisioningUuid, ProvisioningUuid : deviceMessages.ProvisioningUuid,
DeviceInit : deviceMessages.DeviceInit, ProvisionEnvelope : deviceMessages.ProvisionEnvelope,
IdentityKey : deviceMessages.IdentityKey, ProvisionMessage : deviceMessages.ProvisionMessage,
DeviceControl : deviceMessages.DeviceControl, DeviceControl : deviceMessages.DeviceControl,
WebSocketResponseMessage : subProtocolMessages.WebSocketResponseMessage, WebSocketResponseMessage : subProtocolMessages.WebSocketResponseMessage,
WebSocketRequestMessage : subProtocolMessages.WebSocketRequestMessage, WebSocketRequestMessage : subProtocolMessages.WebSocketRequestMessage,
@ -6190,13 +6190,9 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step
}); });
} }
window.textsecure.registerSecondDevice = function(encodedDeviceInit, cryptoInfo, stepDone) { window.textsecure.registerSecondDevice = function(encodedProvisionEnvelope, cryptoInfo, stepDone) {
var deviceInit = textsecure.protobuf.DeviceInit.decode(encodedDeviceInit, 'binary'); var envelope = textsecure.protobuf.ProvisionEnvelope.decode(encodedProvisionEnvelope, 'binary');
return cryptoInfo.decryptAndHandleDeviceInit(deviceInit).then(function(identityKey) { return cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(identityKey) {
if (identityKey.server != textsecure.api.relay)
throw new Error("Unknown relay used by master");
var number = identityKey.phoneNumber;
stepDone(1); stepDone(1);
var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
@ -6210,10 +6206,10 @@ window.textsecure.registerSecondDevice = function(encodedDeviceInit, cryptoInfo,
registrationId = registrationId & 0x3fff; registrationId = registrationId & 0x3fff;
textsecure.storage.putUnencrypted("registrationId", registrationId); textsecure.storage.putUnencrypted("registrationId", registrationId);
return textsecure.api.confirmCode(number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { return textsecure.api.confirmCode(identityKey.number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) {
var numberId = number + "." + result; var numberId = identityKey.number + "." + result.deviceId;
textsecure.storage.putUnencrypted("number_id", numberId); textsecure.storage.putUnencrypted("number_id", numberId);
textsecure.storage.putUnencrypted("regionCode", libphonenumber.util.getRegion(number)); textsecure.storage.putUnencrypted("regionCode", libphonenumber.util.getRegionCodeForNumber(identityKey.number));
stepDone(2); stepDone(2);
return textsecure.protocol.generateKeys().then(function(keys) { return textsecure.protocol.generateKeys().then(function(keys) {
@ -6758,7 +6754,7 @@ window.textsecure.api = function () {
URL_CALLS.devices = "/v1/devices"; URL_CALLS.devices = "/v1/devices";
URL_CALLS.keys = "/v2/keys"; URL_CALLS.keys = "/v2/keys";
URL_CALLS.push = "/v1/websocket"; URL_CALLS.push = "/v1/websocket";
URL_CALLS.temp_push = "/v1/provisioning"; URL_CALLS.temp_push = "/v1/websocket/provisioning";
URL_CALLS.messages = "/v1/messages"; URL_CALLS.messages = "/v1/messages";
URL_CALLS.attachment = "/v1/attachments"; URL_CALLS.attachment = "/v1/attachments";
@ -7950,26 +7946,26 @@ window.textsecure.protocol = function() {
var keyPair; var keyPair;
socketInfo.decryptAndHandleDeviceInit = function(deviceInit) { socketInfo.decryptAndHandleDeviceInit = function(deviceInit) {
var masterEphemeral = toArrayBuffer(deviceInit.masterEphemeralPubKey); var masterEphemeral = toArrayBuffer(deviceInit.publicKey);
var message = toArrayBuffer(deviceInit.identityKeyMessage); var message = toArrayBuffer(deviceInit.body);
return textsecure.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) { return textsecure.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) {
return HKDF(ecRes, masterEphemeral, "WhisperDeviceInit").then(function(keys) { return HKDF(ecRes, '', "TextSecure Provisioning Message").then(function(keys) {
if (new Uint8Array(message)[0] != (3 << 4) | 3) if (new Uint8Array(message)[0] != 1)
throw new Error("Bad version number on IdentityKeyMessage"); throw new Error("Bad version number on ProvisioningMessage");
var iv = message.slice(1, 16 + 1); var iv = message.slice(1, 16 + 1);
var mac = message.slice(message.length - 32, message.length); var mac = message.slice(message.byteLength - 32, message.byteLength);
var ivAndCiphertext = message.slice(0, message.length - 32); var ivAndCiphertext = message.slice(0, message.byteLength - 32);
var ciphertext = message.slice(16 + 1, message.length - 32); var ciphertext = message.slice(16 + 1, message.byteLength - 32);
return verifyMAC(ivAndCiphertext, ecRes[1], mac).then(function() { return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
window.textsecure.crypto.decrypt(ecRes[0], ciphertext, iv).then(function(plaintext) { return window.textsecure.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
var identityKeyMsg = textsecure.protobuf.IdentityKey.decode(plaintext); var identityKeyMsg = textsecure.protobuf.ProvisionMessage.decode(plaintext);
textsecure.crypto.createKeyPair(toArrayBuffer(identityKeyMsg.identityKey)).then(function(identityKeyPair) { return textsecure.crypto.createKeyPair(toArrayBuffer(identityKeyMsg.identityKeyPrivate)).then(function(identityKeyPair) {
crypto_storage.putKeyPair("identityKey", identityKeyPair); crypto_storage.putKeyPair("identityKey", identityKeyPair);
identityKeyMsg.identityKey = null; identityKeyMsg.identityKeyPrivate = null;
return identityKeyMsg; return identityKeyMsg;
}); });

View file

@ -142,14 +142,13 @@
new WebSocketResource(socket, function(request) { new WebSocketResource(socket, function(request) {
if (request.path == "/v1/address" && request.verb == "PUT") { if (request.path == "/v1/address" && request.verb == "PUT") {
var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body); var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body);
qrCode.makeCode('textsecure-device-init:/' + qrCode.makeCode('tsdevice:/' +
'?channel_uuid=' + proto.uuid + '?uuid=' + proto.uuid +
'&channel_server=' + textsecure.api.relay + '&pub_key=' + btoa(getString(cryptoInfo.pubKey)));
'&publicKey=' + btoa(getString(cryptoInfo.publicKey)));
$('img').removeAttr('style'); $('img').removeAttr('style');
$('#multi-device .status').text("Use your phone to scan the QR code.") $('#multi-device .status').text("Use your phone to scan the QR code.")
request.respond(200, 'OK'); request.respond(200, 'OK');
} else { } else if (request.path == "/v1/message" && request.verb == "PUT") {
$('#init-setup').hide(); $('#init-setup').hide();
$('#verify1done').text(''); $('#verify1done').text('');
$('#verify2done').text(''); $('#verify2done').text('');
@ -158,8 +157,7 @@
$('#verify5done').text(''); $('#verify5done').text('');
$('#verify').show().addClass('in'); $('#verify').show().addClass('in');
textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) {
textsecure.registerSecondDevice(message.body, cryptoInfo, function(step) {
switch(step) { switch(step) {
case 1: case 1:
$('#verify1done').text('done'); $('#verify1done').text('done');
@ -171,16 +169,19 @@
$('#verify3done').text('done'); $('#verify3done').text('done');
break; break;
case 4: case 4:
//TODO: User needs to verify number before we continue //XXX: User needs to verify number before we continue
$('#complete-number').text(parsedNumber);
$('#verify4done').text('done'); $('#verify4done').text('done');
//$('#complete-number').text(parsedNumber);
textsecure.registration.done();
case 5: case 5:
//TODO: Do sync to get 5!
$('#verify').hide(); $('#verify').hide();
$('#setup-complete').show().addClass('in'); $('#setup-complete').show().addClass('in');
textsecure.registration.done(); textsecure.registration.done();
} }
}); });
} } else
console.log(request.path);
}); });
}); });
}); });

View file

@ -37,7 +37,7 @@ window.textsecure.api = function () {
URL_CALLS.devices = "/v1/devices"; URL_CALLS.devices = "/v1/devices";
URL_CALLS.keys = "/v2/keys"; URL_CALLS.keys = "/v2/keys";
URL_CALLS.push = "/v1/websocket"; URL_CALLS.push = "/v1/websocket";
URL_CALLS.temp_push = "/v1/provisioning"; URL_CALLS.temp_push = "/v1/websocket/provisioning";
URL_CALLS.messages = "/v1/messages"; URL_CALLS.messages = "/v1/messages";
URL_CALLS.attachment = "/v1/attachments"; URL_CALLS.attachment = "/v1/attachments";

View file

@ -268,13 +268,9 @@ window.textsecure.registerSingleDevice = function(number, verificationCode, step
}); });
} }
window.textsecure.registerSecondDevice = function(encodedDeviceInit, cryptoInfo, stepDone) { window.textsecure.registerSecondDevice = function(encodedProvisionEnvelope, cryptoInfo, stepDone) {
var deviceInit = textsecure.protobuf.DeviceInit.decode(encodedDeviceInit, 'binary'); var envelope = textsecure.protobuf.ProvisionEnvelope.decode(encodedProvisionEnvelope, 'binary');
return cryptoInfo.decryptAndHandleDeviceInit(deviceInit).then(function(identityKey) { return cryptoInfo.decryptAndHandleDeviceInit(envelope).then(function(identityKey) {
if (identityKey.server != textsecure.api.relay)
throw new Error("Unknown relay used by master");
var number = identityKey.phoneNumber;
stepDone(1); stepDone(1);
var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); var signalingKey = textsecure.crypto.getRandomBytes(32 + 20);
@ -288,17 +284,16 @@ window.textsecure.registerSecondDevice = function(encodedDeviceInit, cryptoInfo,
registrationId = registrationId & 0x3fff; registrationId = registrationId & 0x3fff;
textsecure.storage.putUnencrypted("registrationId", registrationId); textsecure.storage.putUnencrypted("registrationId", registrationId);
return textsecure.api.confirmCode(number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) { return textsecure.api.confirmCode(identityKey.number, identityKey.provisioningCode, password, signalingKey, registrationId, false).then(function(result) {
var numberId = number + "." + result; var numberId = identityKey.number + "." + result.deviceId;
textsecure.storage.putUnencrypted("number_id", numberId); textsecure.storage.putUnencrypted("number_id", numberId);
textsecure.storage.putUnencrypted("regionCode", libphonenumber.util.getRegion(number)); textsecure.storage.putUnencrypted("regionCode", libphonenumber.util.getRegionCodeForNumber(identityKey.number));
stepDone(2); stepDone(2);
return textsecure.protocol.generateKeys().then(function(keys) { return textsecure.protocol.generateKeys().then(function(keys) {
stepDone(3); stepDone(3);
return textsecure.api.registerKeys(keys).then(function() { return textsecure.api.registerKeys(keys).then(function() {
stepDone(4); stepDone(4);
//TODO: Send DeviceControl.NEW_DEVICE_REGISTERED to all other devices
}); });
}); });
}); });

View file

@ -16,8 +16,8 @@
WhisperMessage : protocolMessages.WhisperMessage, WhisperMessage : protocolMessages.WhisperMessage,
PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage, PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage,
ProvisioningUuid : deviceMessages.ProvisioningUuid, ProvisioningUuid : deviceMessages.ProvisioningUuid,
DeviceInit : deviceMessages.DeviceInit, ProvisionEnvelope : deviceMessages.ProvisionEnvelope,
IdentityKey : deviceMessages.IdentityKey, ProvisionMessage : deviceMessages.ProvisionMessage,
DeviceControl : deviceMessages.DeviceControl, DeviceControl : deviceMessages.DeviceControl,
WebSocketResponseMessage : subProtocolMessages.WebSocketResponseMessage, WebSocketResponseMessage : subProtocolMessages.WebSocketResponseMessage,
WebSocketRequestMessage : subProtocolMessages.WebSocketRequestMessage, WebSocketRequestMessage : subProtocolMessages.WebSocketRequestMessage,

View file

@ -762,26 +762,26 @@ window.textsecure.protocol = function() {
var keyPair; var keyPair;
socketInfo.decryptAndHandleDeviceInit = function(deviceInit) { socketInfo.decryptAndHandleDeviceInit = function(deviceInit) {
var masterEphemeral = toArrayBuffer(deviceInit.masterEphemeralPubKey); var masterEphemeral = toArrayBuffer(deviceInit.publicKey);
var message = toArrayBuffer(deviceInit.identityKeyMessage); var message = toArrayBuffer(deviceInit.body);
return textsecure.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) { return textsecure.crypto.ECDHE(masterEphemeral, keyPair.privKey).then(function(ecRes) {
return HKDF(ecRes, masterEphemeral, "WhisperDeviceInit").then(function(keys) { return HKDF(ecRes, '', "TextSecure Provisioning Message").then(function(keys) {
if (new Uint8Array(message)[0] != (3 << 4) | 3) if (new Uint8Array(message)[0] != 1)
throw new Error("Bad version number on IdentityKeyMessage"); throw new Error("Bad version number on ProvisioningMessage");
var iv = message.slice(1, 16 + 1); var iv = message.slice(1, 16 + 1);
var mac = message.slice(message.length - 32, message.length); var mac = message.slice(message.byteLength - 32, message.byteLength);
var ivAndCiphertext = message.slice(0, message.length - 32); var ivAndCiphertext = message.slice(0, message.byteLength - 32);
var ciphertext = message.slice(16 + 1, message.length - 32); var ciphertext = message.slice(16 + 1, message.byteLength - 32);
return verifyMAC(ivAndCiphertext, ecRes[1], mac).then(function() { return verifyMAC(ivAndCiphertext, keys[1], mac).then(function() {
window.textsecure.crypto.decrypt(ecRes[0], ciphertext, iv).then(function(plaintext) { return window.textsecure.crypto.decrypt(keys[0], ciphertext, iv).then(function(plaintext) {
var identityKeyMsg = textsecure.protobuf.IdentityKey.decode(plaintext); var identityKeyMsg = textsecure.protobuf.ProvisionMessage.decode(plaintext);
textsecure.crypto.createKeyPair(toArrayBuffer(identityKeyMsg.identityKey)).then(function(identityKeyPair) { return textsecure.crypto.createKeyPair(toArrayBuffer(identityKeyMsg.identityKeyPrivate)).then(function(identityKeyPair) {
crypto_storage.putKeyPair("identityKey", identityKeyPair); crypto_storage.putKeyPair("identityKey", identityKeyPair);
identityKeyMsg.identityKey = null; identityKeyMsg.identityKeyPrivate = null;
return identityKeyMsg; return identityKeyMsg;
}); });

View file

@ -73,7 +73,7 @@
<div class='row'> <div class='row'>
<div class='col-xs-offset-1 col-md-6'> <div class='col-xs-offset-1 col-md-6'>
<div id="verify" class="collapse narrow"> <div id="verify" class="collapse narrow">
<div id="verify1">Receiving identity key...<span class="done"></span></div> <div id="verify1">Receiving identity key...<span class="verify1done"></span></div>
<div id="verify2">Verifying number and setup code...<span class="done" id="verify2done"></span></div> <div id="verify2">Verifying number and setup code...<span class="done" id="verify2done"></span></div>
<div id="verify3">Generating keys...<span class="done" id="verify3done"></span></div> <div id="verify3">Generating keys...<span class="done" id="verify3done"></span></div>
<div id="verify4">Registering...<span class="done" id="verify4done"></span></div> <div id="verify4">Registering...<span class="done" id="verify4done"></span></div>

View file

@ -4,17 +4,16 @@ message ProvisioningUuid {
optional string uuid = 1; optional string uuid = 1;
} }
message DeviceInit {
required bytes masterEphemeralPubKey = 1; message ProvisionEnvelope {
required bytes identityKeyMessage = 2; // contains an IdentityKey optional bytes publicKey = 1;
optional bytes body = 2; // Encrypted ProvisionMessage
} }
message IdentityKey { message ProvisionMessage {
required bytes identityKey = 1; optional bytes identityKeyPrivate = 2;
required string phoneNumber = 2; optional string number = 3;
required string server = 3; optional string provisioningCode = 4;
required bool masterSupportsSms = 4;
required uint32 provisioningCode = 5;
} }
message DeviceControl { message DeviceControl {