From 33fd9c5dd43dd24a698fba2d24bb04dbbd948445 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Wed, 17 Jan 2018 15:28:32 -0800 Subject: [PATCH] Account setup: confirm first signed prekey, clear prekeys (#1979) --- js/signal_protocol_store.js | 18 ++++- libtextsecure/account_manager.js | 133 +++++++++++++++++++------------ 2 files changed, 100 insertions(+), 51 deletions(-) diff --git a/js/signal_protocol_store.js b/js/signal_protocol_store.js index 0400b42f7417..f3eb489a5a66 100644 --- a/js/signal_protocol_store.js +++ b/js/signal_protocol_store.js @@ -89,6 +89,11 @@ var Model = Backbone.Model.extend({ database: Whisper.Database }); var PreKey = Model.extend({ storeName: 'preKeys' }); + var PreKeyCollection = Backbone.Collection.extend({ + storeName: 'preKeys', + database: Whisper.Database, + model: PreKey + }); var SignedPreKey = Model.extend({ storeName: 'signedPreKeys' }); var SignedPreKeyCollection = Backbone.Collection.extend({ storeName: 'signedPreKeys', @@ -229,6 +234,12 @@ }); }); }, + clearPreKeyStore: function() { + return new Promise(function(resolve) { + var preKeys = new PreKeyCollection(); + preKeys.sync('delete', preKeys, {}).always(resolve); + }); + }, /* Returns a signed keypair object or undefined */ loadSignedPreKey: function(keyId) { @@ -293,6 +304,12 @@ deferred.then(resolve, reject); }); }, + clearSignedPreKeysStore: function() { + return new Promise(function(resolve) { + var signedPreKeys = new SignedPreKeyCollection(); + signedPreKeys.sync('delete', signedPreKeys, {}).always(resolve); + }); + }, loadSession: function(encodedNumber) { if (encodedNumber === null || encodedNumber === undefined) { @@ -392,7 +409,6 @@ var sessions = new SessionCollection(); sessions.sync('delete', sessions, {}).always(resolve); }); - }, isTrustedIdentity: function(identifier, publicKey, direction) { if (identifier === null || identifier === undefined) { diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 69f7c2c8a5c9..1fd6e6eb0888 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -26,21 +26,30 @@ registerSingleDevice: function(number, verificationCode) { var registerKeys = this.server.registerKeys.bind(this.server); var createAccount = this.createAccount.bind(this); + var clearSessionsAndPreKeys = this.clearSessionsAndPreKeys.bind(this); var generateKeys = this.generateKeys.bind(this, 100); + var confirmKeys = this.confirmKeys.bind(this); var registrationDone = this.registrationDone.bind(this); return this.queueTask(function() { return libsignal.KeyHelper.generateIdentityKeyPair().then(function(identityKeyPair) { var profileKey = textsecure.crypto.getRandomBytes(32); - return createAccount(number, verificationCode, identityKeyPair, profileKey). - then(generateKeys). - then(registerKeys). - then(registrationDone); + return createAccount(number, verificationCode, identityKeyPair, profileKey) + .then(clearSessionsAndPreKeys) + .then(generateKeys) + .then(function(keys) { + return registerKeys(keys).then(function() { + return confirmKeys(keys); + }); + }) + .then(registrationDone); }); }); }, registerSecondDevice: function(setProvisioningUrl, confirmNumber, progressCallback) { var createAccount = this.createAccount.bind(this); + var clearSessionsAndPreKeys = this.clearSessionsAndPreKeys.bind(this); var generateKeys = this.generateKeys.bind(this, 100, progressCallback); + var confirmKeys = this.confirmKeys.bind(this); var registrationDone = this.registrationDone.bind(this); var registerKeys = this.server.registerKeys.bind(this.server); var getSocket = this.server.getProvisioningSocket.bind(this.server); @@ -88,9 +97,15 @@ deviceName, provisionMessage.userAgent, provisionMessage.readReceipts - ).then(generateKeys). - then(registerKeys). - then(registrationDone); + ) + .then(clearSessionsAndPreKeys) + .then(generateKeys) + .then(function(keys) { + return registerKeys(keys).then(function() { + return confirmKeys(keys); + }); + }) + .then(registrationDone); }); }); })); @@ -249,52 +264,68 @@ return this.server.confirmCode( number, verificationCode, password, signalingKey, registrationId, deviceName ).then(function(response) { - return textsecure.storage.protocol.clearSessionStore().then(function() { - textsecure.storage.remove('identityKey'); - textsecure.storage.remove('signaling_key'); - textsecure.storage.remove('password'); - textsecure.storage.remove('registrationId'); - textsecure.storage.remove('number_id'); - textsecure.storage.remove('device_name'); - textsecure.storage.remove('regionCode'); - textsecure.storage.remove('userAgent'); - textsecure.storage.remove('profileKey'); - textsecure.storage.remove('read-receipts-setting'); + textsecure.storage.remove('identityKey'); + textsecure.storage.remove('signaling_key'); + textsecure.storage.remove('password'); + textsecure.storage.remove('registrationId'); + textsecure.storage.remove('number_id'); + textsecure.storage.remove('device_name'); + textsecure.storage.remove('regionCode'); + textsecure.storage.remove('userAgent'); + textsecure.storage.remove('profileKey'); + textsecure.storage.remove('read-receipts-setting'); - // update our own identity key, which may have changed - // if we're relinking after a reinstall on the master device - textsecure.storage.protocol.saveIdentityWithAttributes(number, { - id : number, - publicKey : identityKeyPair.pubKey, - firstUse : true, - timestamp : Date.now(), - verified : textsecure.storage.protocol.VerifiedStatus.VERIFIED, - nonblockingApproval : true - }); + // update our own identity key, which may have changed + // if we're relinking after a reinstall on the master device + textsecure.storage.protocol.saveIdentityWithAttributes(number, { + id : number, + publicKey : identityKeyPair.pubKey, + firstUse : true, + timestamp : Date.now(), + verified : textsecure.storage.protocol.VerifiedStatus.VERIFIED, + nonblockingApproval : true + }); - textsecure.storage.put('identityKey', identityKeyPair); - textsecure.storage.put('signaling_key', signalingKey); - textsecure.storage.put('password', password); - textsecure.storage.put('registrationId', registrationId); - if (profileKey) { - textsecure.storage.put('profileKey', profileKey); - } - if (userAgent) { - textsecure.storage.put('userAgent', userAgent); - } - if (readReceipts) { - textsecure.storage.put('read-receipt-setting', true); - } else { - textsecure.storage.put('read-receipt-setting', false); - } + textsecure.storage.put('identityKey', identityKeyPair); + textsecure.storage.put('signaling_key', signalingKey); + textsecure.storage.put('password', password); + textsecure.storage.put('registrationId', registrationId); + if (profileKey) { + textsecure.storage.put('profileKey', profileKey); + } + if (userAgent) { + textsecure.storage.put('userAgent', userAgent); + } + if (readReceipts) { + textsecure.storage.put('read-receipt-setting', true); + } else { + textsecure.storage.put('read-receipt-setting', false); + } - - textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1, deviceName); - textsecure.storage.put('regionCode', libphonenumber.util.getRegionCodeForNumber(number)); - this.server.username = textsecure.storage.get('number_id'); - }.bind(this)); + textsecure.storage.user.setNumberAndDeviceId(number, response.deviceId || 1, deviceName); + textsecure.storage.put('regionCode', libphonenumber.util.getRegionCodeForNumber(number)); + this.server.username = textsecure.storage.get('number_id'); }.bind(this)); }, + clearSessionsAndPreKeys: function() { + var store = textsecure.storage.protocol; + + console.log('clearing all sessions, prekeys, and signed prekeys'); + return Promise.all([ + store.clearPreKeyStore(), + store.clearSignedPreKeysStore(), + store.clearSessionStore(), + ]); + }, + // Takes the same object returned by generateKeys + confirmKeys: function(keys) { + var store = textsecure.storage.protocol; + var key = keys.signedPreKey; + var confirmed = true; + + console.log('confirmKeys: confirming key', key.keyId); + return store.storeSignedPreKey(key.keyId, key.keyPair, confirmed); + }, generateKeys: function (count, progressCallback) { if (typeof progressCallback !== 'function') { progressCallback = undefined; @@ -309,7 +340,6 @@ throw new Error('Invalid signedKeyId'); } - var store = textsecure.storage.protocol; return store.getIdentityKeyPair().then(function(identityKey) { var result = { preKeys: [], identityKey: identityKey.pubKey }; @@ -334,7 +364,9 @@ result.signedPreKey = { keyId : res.keyId, publicKey : res.keyPair.pubKey, - signature : res.signature + signature : res.signature, + // server.registerKeys doesn't use keyPair, confirmKeys does + keyPair : res.keyPair, }; }) ); @@ -342,6 +374,7 @@ textsecure.storage.put('maxPreKeyId', startId + count); textsecure.storage.put('signedKeyId', signedKeyId + 1); return Promise.all(promises).then(function() { + // This is primarily for the signed prekey summary it logs out return this.cleanSignedPreKeys().then(function() { return result; });