From 43e5d16020f7b2d1b9f7675d73eb4f2c9d07a9d3 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Fri, 11 Jan 2019 08:53:35 -0800 Subject: [PATCH] Support for web socket communication with no signaling key --- js/background.js | 14 +++++++++++- js/modules/web_api.js | 11 +++++++-- libtextsecure/account_manager.js | 10 ++++---- libtextsecure/message_receiver.js | 14 ++++++++++-- libtextsecure/storage/user.js | 4 ++++ libtextsecure/websocket-resources.js | 4 ++++ protos/SubProtocol.proto | 34 +++++++++++++++------------- 7 files changed, 66 insertions(+), 25 deletions(-) diff --git a/js/background.js b/js/background.js index 3482e65154..cadf621db2 100644 --- a/js/background.js +++ b/js/background.js @@ -681,7 +681,19 @@ textsecure.storage.user.getDeviceId() != '1' ) { window.getSyncRequest(); - window.getAccountManager().maybeUpdateDeviceName(); + + try { + const manager = window.getAccountManager(); + await Promise.all([ + manager.maybeUpdateDeviceName(), + manager.maybeDeleteSignalingKey(), + ]); + } catch (e) { + window.log.error( + 'Problem with account manager updates after starting new version: ', + e && e.stack ? e.stack : e + ); + } } const udSupportKey = 'hasRegisterSupportForUnauthenticatedDelivery'; diff --git a/js/modules/web_api.js b/js/modules/web_api.js index fb5ca5308e..a77cbe7b32 100644 --- a/js/modules/web_api.js +++ b/js/modules/web_api.js @@ -326,6 +326,7 @@ function HTTPError(message, providedCode, response, stack) { const URL_CALLS = { accounts: 'v1/accounts', updateDeviceName: 'v1/accounts/name', + removeSignalingKey: 'v1/accounts/signaling_key', attachment: 'v1/attachments', deliveryCert: 'v1/certificate/delivery', supportUnauthenticatedDelivery: 'v1/devices/unauthenticated_delivery', @@ -388,6 +389,7 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) { sendMessagesUnauth, setSignedPreKey, updateDeviceName, + removeSignalingKey, }; function _ajax(param) { @@ -518,14 +520,12 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) { number, code, newPassword, - signalingKey, registrationId, deviceName, options = {} ) { const { accessKey } = options; const jsonData = { - signalingKey: _btoa(_getString(signalingKey)), supportsSms: false, fetchesMessages: true, registrationId, @@ -580,6 +580,13 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) { }); } + function removeSignalingKey() { + return _ajax({ + call: 'removeSignalingKey', + httpType: 'DELETE', + }); + } + function getDevices() { return _ajax({ call: 'devices', diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 443c82e66f..38915de28b 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -99,6 +99,12 @@ async deviceNameIsEncrypted() { await textsecure.storage.user.setDeviceNameEncrypted(); }, + async maybeDeleteSignalingKey() { + const key = await textsecure.storage.user.getSignalingKey(); + if (key) { + await this.server.removeSignalingKey(); + } + }, registerSingleDevice(number, verificationCode) { const registerKeys = this.server.registerKeys.bind(this.server); const createAccount = this.createAccount.bind(this); @@ -400,7 +406,6 @@ options = {} ) { const { accessKey } = options; - const signalingKey = libsignal.crypto.getRandomBytes(32 + 20); let password = btoa(getString(libsignal.crypto.getRandomBytes(16))); password = password.substring(0, password.length - 2); const registrationId = libsignal.KeyHelper.generateRegistrationId(); @@ -417,7 +422,6 @@ number, verificationCode, password, - signalingKey, registrationId, encryptedDeviceName, { accessKey } @@ -441,7 +445,6 @@ await Promise.all([ textsecure.storage.remove('identityKey'), - textsecure.storage.remove('signaling_key'), textsecure.storage.remove('password'), textsecure.storage.remove('registrationId'), textsecure.storage.remove('number_id'), @@ -464,7 +467,6 @@ }); await textsecure.storage.put('identityKey', identityKeyPair); - await textsecure.storage.put('signaling_key', signalingKey); await textsecure.storage.put('password', password); await textsecure.storage.put('registrationId', registrationId); if (profileKey) { diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index bf96f99a7b..567edf76b8 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -274,8 +274,18 @@ MessageReceiver.prototype.extend({ return; } - const promise = textsecure.crypto - .decryptWebsocketMessage(request.body, this.signalingKey) + let promise; + const headers = request.headers || []; + if (headers.includes('X-Signal-Key: true')) { + promise = textsecure.crypto.decryptWebsocketMessage( + request.body, + this.signalingKey + ); + } else { + promise = Promise.resolve(request.body.toArrayBuffer()); + } + + promise = promise .then(plaintext => { const envelope = textsecure.protobuf.Envelope.decode(plaintext); // After this point, decoding errors are not the server's diff --git a/libtextsecure/storage/user.js b/libtextsecure/storage/user.js index e8b7e0fd55..b6330acf87 100644 --- a/libtextsecure/storage/user.js +++ b/libtextsecure/storage/user.js @@ -39,5 +39,9 @@ getDeviceNameEncrypted() { return textsecure.storage.get('deviceNameEncrypted'); }, + + getSignalingKey() { + return textsecure.storage.get('signaling_key'); + }, }; })(); diff --git a/libtextsecure/websocket-resources.js b/libtextsecure/websocket-resources.js index aa4f869275..5d807c4161 100644 --- a/libtextsecure/websocket-resources.js +++ b/libtextsecure/websocket-resources.js @@ -27,6 +27,7 @@ const Request = function Request(options) { this.verb = options.verb || options.type; this.path = options.path || options.url; + this.headers = options.headers; this.body = options.body || options.data; this.success = options.success; this.error = options.error; @@ -50,6 +51,7 @@ this.verb = request.verb; this.path = request.path; this.body = request.body; + this.headers = request.headers; this.respond = (status, message) => { socket.send( @@ -77,6 +79,7 @@ verb: request.verb, path: request.path, body: request.body, + headers: request.headers, id: request.id, }, }) @@ -105,6 +108,7 @@ verb: message.request.verb, path: message.request.path, body: message.request.body, + headers: message.request.headers, id: message.request.id, socket, }) diff --git a/protos/SubProtocol.proto b/protos/SubProtocol.proto index 52dbe37b7f..c266e53f24 100644 --- a/protos/SubProtocol.proto +++ b/protos/SubProtocol.proto @@ -19,27 +19,29 @@ package signalservice; option java_package = "org.whispersystems.websocket.messages.protobuf"; message WebSocketRequestMessage { - optional string verb = 1; - optional string path = 2; - optional bytes body = 3; - optional uint64 id = 4; + optional string verb = 1; + optional string path = 2; + optional bytes body = 3; + repeated string headers = 5; + optional uint64 id = 4; } message WebSocketResponseMessage { - optional uint64 id = 1; - optional uint32 status = 2; - optional string message = 3; - optional bytes body = 4; + optional uint64 id = 1; + optional uint32 status = 2; + optional string message = 3; + repeated string headers = 5; + optional bytes body = 4; } message WebSocketMessage { - enum Type { - UNKNOWN = 0; - REQUEST = 1; - RESPONSE = 2; - } + enum Type { + UNKNOWN = 0; + REQUEST = 1; + RESPONSE = 2; + } - optional Type type = 1; - optional WebSocketRequestMessage request = 2; - optional WebSocketResponseMessage response = 3; + optional Type type = 1; + optional WebSocketRequestMessage request = 2; + optional WebSocketResponseMessage response = 3; }