From 0dabce9e28b2efd266d13cf4720313c88a42a633 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Fri, 23 Nov 2018 11:09:47 +1100 Subject: [PATCH 1/2] Remove preKeyBundle from message. Always save preKeyBundles if we receive them. Always override preKeys when saving. --- js/background.js | 1 - js/modules/data.js | 7 ++-- libloki/libloki-protocol.js | 59 ++++++++++--------------------- libtextsecure/message_receiver.js | 27 +++++--------- 4 files changed, 30 insertions(+), 64 deletions(-) diff --git a/js/background.js b/js/background.js index 0e228228a..948bc1283 100644 --- a/js/background.js +++ b/js/background.js @@ -1264,7 +1264,6 @@ unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived, type: 'incoming', unread: 1, - preKeyBundle: data.preKeyBundle || null, }; if (data.type === 'friend-request') { diff --git a/js/modules/data.js b/js/modules/data.js index a31ed1c74..5e61d5a43 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -727,7 +727,6 @@ async function searchConversations(query, { ConversationCollection }) { } // Message -const MESSAGE_PRE_KEYS = ['identityKey', 'preKey', 'signature', 'signedKey'].map(k => `preKeyBundle.${k}`); async function getMessageCount() { return channels.getMessageCount(); } @@ -745,8 +744,7 @@ async function saveSeenMessageHash(data) { } async function saveMessage(data, { forceSave, Message } = {}) { - const updated = keysFromArrayBuffer(MESSAGE_PRE_KEYS, data); - const id = await channels.saveMessage(_cleanData(updated), { forceSave }); + const id = await channels.saveMessage(_cleanData(data), { forceSave }); Message.refreshExpirationTimer(); return id; } @@ -789,8 +787,7 @@ async function saveLegacyMessage(data) { } async function saveMessages(arrayOfMessages, { forceSave } = {}) { - const updated = arrayOfMessages.map(m => keysFromArrayBuffer(MESSAGE_PRE_KEYS, m)); - await channels.saveMessages(_cleanData(updated), { forceSave }); + await channels.saveMessages(_cleanData(arrayOfMessages), { forceSave }); } async function removeMessage(id, { Message }) { diff --git a/libloki/libloki-protocol.js b/libloki/libloki-protocol.js index 204a36c9c..1dafb1f78 100644 --- a/libloki/libloki-protocol.js +++ b/libloki/libloki-protocol.js @@ -98,47 +98,26 @@ signedKey, signature, }) { - const signedKeyPromise = new Promise(async resolve => { - const existingSignedKeys = await textsecure.storage.protocol.loadContactSignedPreKeys( - { identityKeyString: pubKey, keyId: signedKeyId } - ); - if ( - !existingSignedKeys || - (existingSignedKeys instanceof Array && existingSignedKeys.length === 0) - ) { - const signedPreKey = { - keyId: signedKeyId, - publicKey: signedKey, - signature, - }; - await textsecure.storage.protocol.storeContactSignedPreKey( - pubKey, - signedPreKey - ); - } - resolve(); - }); + const signedPreKey = { + keyId: signedKeyId, + publicKey: signedKey, + signature, + }; - const preKeyPromise = new Promise(async resolve => { - const existingPreKeys = await textsecure.storage.protocol.loadContactPreKeys({ - identityKeyString: pubKey, - keyId: preKeyId, - }); - if ( - !existingPreKeys || - (existingPreKeys instanceof Array && existingPreKeys.length === 0) - ) { - const preKeyObject = { - publicKey: preKey, - keyId: preKeyId, - }; - await textsecure.storage.protocol.storeContactPreKey( - pubKey, - preKeyObject - ); - } - resolve(); - }); + const signedKeyPromise = textsecure.storage.protocol.storeContactSignedPreKey( + pubKey, + signedPreKey + ); + + const preKeyObject = { + publicKey: preKey, + keyId: preKeyId, + }; + + const preKeyPromise = textsecure.storage.protocol.storeContactPreKey( + pubKey, + preKeyObject + ); await Promise.all([signedKeyPromise, preKeyPromise]); } diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 28b3ad11c..fbb7ab54f 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -721,15 +721,11 @@ MessageReceiver.prototype.extend({ // eslint-disable-next-line no-param-reassign envelope.preKeyBundleMessage = decodedBundle; - // Save the preKey bundle if this is not a friend request. - // We don't automatically save on a friend request because - // we only want to save the preKeys when we click the accept button. - if (envelope.type !== textsecure.protobuf.Envelope.Type.FRIEND_REQUEST) { - await this.handlePreKeyBundleMessage( - envelope.source, - envelope.preKeyBundleMessage - ); - } + // Save the preKeyBundle + await this.handlePreKeyBundleMessage( + envelope.source, + envelope.preKeyBundleMessage + ); } const me = { @@ -970,7 +966,6 @@ MessageReceiver.prototype.extend({ receivedAt: envelope.receivedAt, unidentifiedDeliveryReceived: envelope.unidentifiedDeliveryReceived, message, - preKeyBundle: envelope.preKeyBundleMessage || null, }; return this.dispatchAndWait(ev); }) @@ -1010,15 +1005,8 @@ MessageReceiver.prototype.extend({ await conversation.updateTextInputState(); } - // If we accepted an incoming friend request then save the preKeyBundle + // If we accepted an incoming friend request then update our state if (message.direction === 'incoming' && message.friendStatus === 'accepted') { - // Register the preKeys used for communication - if (message.preKeyBundle) { - await this.handlePreKeyBundleMessage( - pubKey, - message.preKeyBundle - ); - } // Accept the friend request if (conversation) { @@ -1028,6 +1016,9 @@ MessageReceiver.prototype.extend({ // Send a reply back libloki.sendFriendRequestAccepted(pubKey); } + + // TODO: If we decline a friend request then delete preKeys from our db + window.log.info(`Friend request for ${pubKey} was ${message.friendStatus}`, message); }, async innerHandleContentMessage(envelope, plaintext) { From 9fd822a7e30aaeffa42eb6f5aeace2e7befc5dab Mon Sep 17 00:00:00 2001 From: Mikunj Date: Fri, 23 Nov 2018 11:28:02 +1100 Subject: [PATCH 2/2] Remove preKeys on friend request decline. --- app/sql.js | 16 +++++++----- js/modules/data.js | 42 +++++++++++-------------------- js/signal_protocol_store.js | 18 ++++++++++++- libloki/libloki-protocol.js | 8 ++++++ libtextsecure/account_manager.js | 4 ++- libtextsecure/message_receiver.js | 23 +++++++++-------- 6 files changed, 66 insertions(+), 45 deletions(-) diff --git a/app/sql.js b/app/sql.js index 23da7f554..5b882a325 100644 --- a/app/sql.js +++ b/app/sql.js @@ -49,7 +49,7 @@ module.exports = { getContactPreKeys, getAllContactPreKeys, bulkAddContactPreKeys, - removeContactPreKeyById, + removeContactPreKeyByIdentityKey, removeAllContactPreKeys, createOrUpdateContactSignedPreKey, @@ -57,7 +57,7 @@ module.exports = { getContactSignedPreKeyByIdentityKey, getContactSignedPreKeys, bulkAddContactSignedPreKeys, - removeContactSignedPreKeyById, + removeContactSignedPreKeyByIdentityKey, removeAllContactSignedPreKeys, createOrUpdateItem, @@ -712,8 +712,10 @@ async function getContactPreKeys(keyId, identityKeyString) { async function bulkAddContactPreKeys(array) { return bulkAdd(CONTACT_PRE_KEYS_TABLE, array); } -async function removeContactPreKeyById(id) { - return removeById(CONTACT_PRE_KEYS_TABLE, id); +async function removeContactPreKeyByIdentityKey(key) { + await db.run(`DELETE FROM ${CONTACT_PRE_KEYS_TABLE} WHERE identityKeyString = $identityKeyString;`, { + $identityKeyString: key, + }); } async function removeAllContactPreKeys() { return removeAllFromTable(CONTACT_PRE_KEYS_TABLE); @@ -765,8 +767,10 @@ async function getContactSignedPreKeys(keyId, identityKeyString) { async function bulkAddContactSignedPreKeys(array) { return bulkAdd(CONTACT_SIGNED_PRE_KEYS_TABLE, array); } -async function removeContactSignedPreKeyById(id) { - return removeById(CONTACT_SIGNED_PRE_KEYS_TABLE, id); +async function removeContactSignedPreKeyByIdentityKey(key) { + await db.run(`DELETE FROM ${CONTACT_SIGNED_PRE_KEYS_TABLE} WHERE identityKeyString = $identityKeyString;`, { + $identityKeyString: key, + }); } async function removeAllContactSignedPreKeys() { return removeAllFromTable(CONTACT_SIGNED_PRE_KEYS_TABLE); diff --git a/js/modules/data.js b/js/modules/data.js index 5e61d5a43..f61ccfdd5 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -80,7 +80,7 @@ module.exports = { getContactPreKeys, getAllContactPreKeys, bulkAddContactPreKeys, - removeContactPreKeyById, + removeContactPreKeyByIdentityKey, removeAllContactPreKeys, createOrUpdateContactSignedPreKey, @@ -88,7 +88,7 @@ module.exports = { getContactSignedPreKeyByIdentityKey, getContactSignedPreKeys, bulkAddContactSignedPreKeys, - removeContactSignedPreKeyById, + removeContactSignedPreKeyByIdentityKey, removeAllContactSignedPreKeys, createOrUpdateItem, @@ -528,8 +528,8 @@ async function bulkAddContactPreKeys(array) { const updated = map(array, data => keysFromArrayBuffer(PRE_KEY_KEYS, data)); await channels.bulkAddContactPreKeys(updated); } -async function removeContactPreKeyById(id) { - await channels.removePreContactKeyById(id); +async function removeContactPreKeyByIdentityKey(id) { + await channels.removeContactPreKeyByIdentityKey(id); } async function removeAllContactPreKeys() { await channels.removeAllContactPreKeys(); @@ -556,8 +556,8 @@ async function bulkAddContactSignedPreKeys(array) { const updated = map(array, data => keysFromArrayBuffer(PRE_KEY_KEYS, data)); await channels.bulkAddContactSignedPreKeys(updated); } -async function removeContactSignedPreKeyById(id) { - await channels.removePreContactSignedKeyById(id); +async function removeContactSignedPreKeyByIdentityKey(id) { + await channels.removeContactSignedPreKeyByIdentityKey(id); } async function removeAllContactSignedPreKeys() { await channels.removeAllContactSignedPreKeys(); @@ -812,22 +812,18 @@ async function getMessageById(id, { Message }) { return null; } - const encoded = keysToArrayBuffer(MESSAGE_PRE_KEYS, message); - - return new Message(encoded); + return new Message(message); } // For testing only async function getAllMessages({ MessageCollection }) { const messages = await channels.getAllMessages(); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getAllUnsentMessages({ MessageCollection }) { const messages = await channels.getAllUnsentMessages(); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getAllMessageIds() { @@ -849,9 +845,7 @@ async function getMessageBySender( return null; } - const encoded = keysToArrayBuffer(MESSAGE_PRE_KEYS, messages[0]); - - return new Message(encoded); + return new Message(messages[0]); } async function getUnreadByConversation(conversationId, { MessageCollection }) { @@ -869,9 +863,7 @@ async function getMessagesByConversation( type, }); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getSeenMessagesByHashList( @@ -913,26 +905,22 @@ async function removeAllMessagesInConversation( async function getMessagesBySentAt(sentAt, { MessageCollection }) { const messages = await channels.getMessagesBySentAt(sentAt); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getExpiredMessages({ MessageCollection }) { const messages = await channels.getExpiredMessages(); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getOutgoingWithoutExpiresAt({ MessageCollection }) { const messages = await channels.getOutgoingWithoutExpiresAt(); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } async function getNextExpiringMessage({ MessageCollection }) { const messages = await channels.getNextExpiringMessage(); - const encoded = messages.map(m => keysToArrayBuffer(MESSAGE_PRE_KEYS, m)); - return new MessageCollection(encoded); + return new MessageCollection(messages); } // Unprocessed diff --git a/js/signal_protocol_store.js b/js/signal_protocol_store.js index 80185d238..6e7bc52c3 100644 --- a/js/signal_protocol_store.js +++ b/js/signal_protocol_store.js @@ -162,7 +162,7 @@ }, async getLocalRegistrationId() { return 1; - + // const item = await window.Signal.Data.getItemById('registrationId'); // if (item) { // return item.value; @@ -197,6 +197,8 @@ recipient: key.recipient, }; } + + return undefined; }, async loadContactPreKey(pubKey) { const preKey = await window.Signal.Data.getContactPreKeyByIdentityKey(pubKey); @@ -266,6 +268,13 @@ await window.Signal.Data.removeAllPreKeys(); }, + async removeContactPreKey(pubKey) { + await window.Signal.Data.removeContactPreKeyByIdentityKey(pubKey); + }, + async clearContactPreKeysStore() { + await window.Signal.Data.removeAllContactPreKeys(); + }, + /* Returns a signed keypair object or undefined */ async loadSignedPreKey(keyId) { const key = await window.Signal.Data.getSignedPreKeyById(keyId); @@ -366,6 +375,13 @@ await window.Signal.Data.removeAllSignedPreKeys(); }, + async removeContactSignedPreKey(pubKey) { + await window.Signal.Data.removeContactSignedPreKeyByIdentityKey(pubKey); + }, + async clearContactSignedPreKeysStore() { + await window.Signal.Data.removeAllContactSignedPreKeys(); + }, + async loadSession(encodedNumber) { if (encodedNumber === null || encodedNumber === undefined) { throw new Error('Tried to get session for undefined/null number'); diff --git a/libloki/libloki-protocol.js b/libloki/libloki-protocol.js index 1dafb1f78..0a78f0ca0 100644 --- a/libloki/libloki-protocol.js +++ b/libloki/libloki-protocol.js @@ -122,6 +122,13 @@ await Promise.all([signedKeyPromise, preKeyPromise]); } + async function removePreKeyBundleForNumber(pubKey) { + await Promise.all([ + textsecure.storage.protocol.removeContactPreKey(pubKey), + textsecure.storage.protocol.removeContactSignedPreKey(pubKey), + ]); + } + async function sendFriendRequestAccepted(pubKey) { // empty content message const content = new textsecure.protobuf.Content(); @@ -154,5 +161,6 @@ window.libloki.getPreKeyBundleForNumber = getPreKeyBundleForNumber; window.libloki.FallBackDecryptionError = FallBackDecryptionError; window.libloki.savePreKeyBundleForNumber = savePreKeyBundleForNumber; + window.libloki.removePreKeyBundleForNumber = removePreKeyBundleForNumber; window.libloki.sendFriendRequestAccepted = sendFriendRequestAccepted; })(); diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 52ea87b62..8c91f44af 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -328,7 +328,7 @@ textsecure.storage.remove('userAgent'), 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 await textsecure.storage.protocol.saveIdentityWithAttributes(pubKeyString, { @@ -362,7 +362,9 @@ window.log.info('clearing all sessions, prekeys, and signed prekeys'); return Promise.all([ store.clearPreKeyStore(), + store.clearContactPreKeysStore(), store.clearSignedPreKeysStore(), + store.clearContactSignedPreKeysStore(), store.clearSessionStore(), ]); }, diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index fbb7ab54f..be8cfdb06 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -1005,20 +1005,23 @@ MessageReceiver.prototype.extend({ await conversation.updateTextInputState(); } - // If we accepted an incoming friend request then update our state - if (message.direction === 'incoming' && message.friendStatus === 'accepted') { + // Check if we changed the state of the incoming friend request + if (message.direction === 'incoming') { + // If we accepted an incoming friend request then update our state + if (message.friendStatus === 'accepted') { + // Accept the friend request + if (conversation) { + await conversation.onFriendRequestAccepted(); + } - // Accept the friend request - if (conversation) { - await conversation.onFriendRequestAccepted(); + // Send a reply back + libloki.sendFriendRequestAccepted(pubKey); + } else if (message.friendStatus === 'declined') { + // Delete the preKeys + await libloki.removePreKeyBundleForNumber(pubKey); } - - // Send a reply back - libloki.sendFriendRequestAccepted(pubKey); } - // TODO: If we decline a friend request then delete preKeys from our db - window.log.info(`Friend request for ${pubKey} was ${message.friendStatus}`, message); }, async innerHandleContentMessage(envelope, plaintext) {