From 0b4ef8dcbbc3c2290a6b3292cfe10445b7612187 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 5 Dec 2017 21:47:22 +0000 Subject: [PATCH 01/24] Migrate inbound group sessions to crypto store --- src/crypto/OlmDevice.js | 402 ++++++++---------- src/crypto/algorithms/megolm.js | 16 +- .../store/indexeddb-crypto-store-backend.js | 61 ++- src/crypto/store/indexeddb-crypto-store.js | 46 ++ 4 files changed, 287 insertions(+), 238 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 2c29b4e3936..8b245533a3f 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -765,27 +765,12 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) { */ /** - * store an InboundGroupSession in the session store - * - * @param {string} senderCurve25519Key - * @param {string} sessionId - * @param {InboundGroupSessionData} sessionData - * @private - */ -OlmDevice.prototype._saveInboundGroupSession = function( - senderCurve25519Key, sessionId, sessionData, -) { - this._sessionStore.storeEndToEndInboundGroupSession( - senderCurve25519Key, sessionId, JSON.stringify(sessionData), - ); -}; - -/** - * extract an InboundGroupSession from the session store and call the given function + * extract an InboundGroupSession from the crypto store and call the given function * * @param {string} roomId * @param {string} senderKey * @param {string} sessionId + * @param {*} txn Opaque transaction object from cryptoStore.doTxn() * @param {function(Olm.InboundGroupSession, InboundGroupSessionData): T} func * function to call. * @@ -797,34 +782,31 @@ OlmDevice.prototype._saveInboundGroupSession = function( * @template {T} */ OlmDevice.prototype._getInboundGroupSession = function( - roomId, senderKey, sessionId, func, + roomId, senderKey, sessionId, txn, func, ) { - let r = this._sessionStore.getEndToEndInboundGroupSession( - senderKey, sessionId, - ); - - if (r === null) { - return null; - } - - r = JSON.parse(r); + this._cryptoStore.getEndToEndInboundGroupSession(senderKey, sessionId, txn, (sessionData) => { + if (sessionData === null) { + func(null); + return; + } - // check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId !== r.room_id) { - throw new Error( - "Mismatched room_id for inbound group session (expected " + r.room_id + - ", was " + roomId + ")", - ); - } + // check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId !== sessionData.room_id) { + throw new Error( + "Mismatched room_id for inbound group session (expected " + sessionData.room_id + + ", was " + roomId + ")", + ); + } - const session = new Olm.InboundGroupSession(); - try { - session.unpickle(this._pickleKey, r.session); - return func(session, r); - } finally { - session.free(); - } + const session = new Olm.InboundGroupSession(); + try { + session.unpickle(this._pickleKey, sessionData.session); + return func(session, sessionData); + } finally { + session.free(); + } + }); }; /** @@ -845,100 +827,46 @@ OlmDevice.prototype.addInboundGroupSession = async function( sessionId, sessionKey, keysClaimed, exportFormat, ) { - const self = this; - - /* if we already have this session, consider updating it */ - function updateSession(session, sessionData) { - console.log("Update for megolm session " + senderKey + "/" + sessionId); - // for now we just ignore updates. TODO: implement something here - - return true; - } - - const r = this._getInboundGroupSession( - roomId, senderKey, sessionId, updateSession, - ); - - if (r !== null) { - return; - } - - // new session. - const session = new Olm.InboundGroupSession(); - try { - if (exportFormat) { - session.import_session(sessionKey); - } else { - session.create(sessionKey); - } - if (sessionId != session.session_id()) { - throw new Error( - "Mismatched group session ID from senderKey: " + senderKey, - ); - } - - const sessionData = { - room_id: roomId, - session: session.pickle(this._pickleKey), - keysClaimed: keysClaimed, - forwardingCurve25519KeyChain: forwardingCurve25519KeyChain, - }; - - self._saveInboundGroupSession( - senderKey, sessionId, sessionData, - ); - } finally { - session.free(); - } -}; - - -/** - * Add a previously-exported inbound group session to the session store - * - * @param {module:crypto/OlmDevice.MegolmSessionData} data session data - */ -OlmDevice.prototype.importInboundGroupSession = async function(data) { - /* if we already have this session, consider updating it */ - function updateSession(session, sessionData) { - console.log("Update for megolm session " + data.sender_key + "|" + - data.session_id); - // for now we just ignore updates. TODO: implement something here - - return true; - } - - const r = this._getInboundGroupSession( - data.room_id, data.sender_key, data.session_id, updateSession, - ); - - if (r !== null) { - return; - } + await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + /* if we already have this session, consider updating it */ + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (existingSession, existingSessionData) => { + if (existingSession) { + console.log("Update for megolm session " + senderKey + "/" + sessionId); + // for now we just ignore updates. TODO: implement something here + return; + } - // new session. - const session = new Olm.InboundGroupSession(); - try { - session.import_session(data.session_key); - if (data.session_id != session.session_id()) { - throw new Error( - "Mismatched group session ID from senderKey: " + data.sender_key, - ); - } + // new session. + const session = new Olm.InboundGroupSession(); + try { + if (exportFormat) { + session.import_session(sessionKey); + } else { + session.create(sessionKey); + } + if (sessionId != session.session_id()) { + throw new Error( + "Mismatched group session ID from senderKey: " + senderKey, + ); + } - const sessionData = { - room_id: data.room_id, - session: session.pickle(this._pickleKey), - keysClaimed: data.sender_claimed_keys, - forwardingCurve25519KeyChain: data.forwarding_curve25519_key_chain, - }; + const sessionData = { + room_id: roomId, + session: session.pickle(this._pickleKey), + keysClaimed: keysClaimed, + forwardingCurve25519KeyChain: forwardingCurve25519KeyChain, + }; - this._saveInboundGroupSession( - data.sender_key, data.session_id, sessionData, + this._cryptoStore.addEndToEndInboundGroupSession( + senderKey, sessionId, sessionData, txn, + ); + } finally { + session.free(); + } + }, ); - } finally { - session.free(); - } + }); }; /** @@ -960,51 +888,55 @@ OlmDevice.prototype.importInboundGroupSession = async function(data) { OlmDevice.prototype.decryptGroupMessage = async function( roomId, senderKey, sessionId, body, eventId, timestamp, ) { - const self = this; + let result; - function decrypt(session, sessionData) { - const res = session.decrypt(body); - - let plaintext = res.plaintext; - if (plaintext === undefined) { - // Compatibility for older olm versions. - plaintext = res; - } else { - // Check if we have seen this message index before to detect replay attacks. - // If the event ID and timestamp are specified, and the match the event ID - // and timestamp from the last time we used this message index, then we - // don't consider it a replay attack. - const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index; - if (messageIndexKey in self._inboundGroupSessionMessageIndexes) { - const msgInfo = self._inboundGroupSessionMessageIndexes[messageIndexKey]; - if (msgInfo.id !== eventId || msgInfo.timestamp !== timestamp) { - throw new Error( - "Duplicate message index, possible replay attack: " + - messageIndexKey, - ); + await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession(roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + result = null; + return; + } + const res = session.decrypt(body); + + let plaintext = res.plaintext; + if (plaintext === undefined) { + // Compatibility for older olm versions. + plaintext = res; + } else { + // Check if we have seen this message index before to detect replay attacks. + // If the event ID and timestamp are specified, and the match the event ID + // and timestamp from the last time we used this message index, then we + // don't consider it a replay attack. + const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index; + if (messageIndexKey in this._inboundGroupSessionMessageIndexes) { + const msgInfo = this._inboundGroupSessionMessageIndexes[messageIndexKey]; + if (msgInfo.id !== eventId || msgInfo.timestamp !== timestamp) { + throw new Error( + "Duplicate message index, possible replay attack: " + + messageIndexKey, + ); + } } + this._inboundGroupSessionMessageIndexes[messageIndexKey] = { + id: eventId, + timestamp: timestamp, + }; } - self._inboundGroupSessionMessageIndexes[messageIndexKey] = { - id: eventId, - timestamp: timestamp, - }; - } - sessionData.session = session.pickle(self._pickleKey); - self._saveInboundGroupSession( - senderKey, sessionId, sessionData, - ); - return { - result: plaintext, - keysClaimed: sessionData.keysClaimed || {}, - senderKey: senderKey, - forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [], - }; - } + sessionData.session = session.pickle(this._pickleKey); + this._cryptoStore.storeEndToEndInboundGroupSession( + senderKey, sessionId, sessionData, txn, + ); + result = { + result: plaintext, + keysClaimed: sessionData.keysClaimed || {}, + senderKey: senderKey, + forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [], + }; + }); + }); - return this._getInboundGroupSession( - roomId, senderKey, sessionId, decrypt, - ); + return result; }; /** @@ -1017,25 +949,30 @@ OlmDevice.prototype.decryptGroupMessage = async function( * @returns {Promise} true if we have the keys to this session */ OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) { - const s = this._sessionStore.getEndToEndInboundGroupSession( - senderKey, sessionId, - ); - - if (s === null) { - return false; - } + let result; + await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._cryptoStore.getEndToEndInboundGroupSession( + senderKey, sessionId, txn, (sessionData) => { + if (sessionData === null) { + result = false; + return; + } - const r = JSON.parse(s); - if (roomId !== r.room_id) { - console.warn( - `requested keys for inbound group session ${senderKey}|` + - `${sessionId}, with incorrect room_id (expected ${r.room_id}, ` + - `was ${roomId})`, + if (roomId !== sessionData.room_id) { + console.warn( + `requested keys for inbound group session ${senderKey}|` + + `${sessionId}, with incorrect room_id (expected ${sessionData.room_id}, ` + + `was ${roomId})`, + ); + result = false; + } else { + result = true; + } + }, ); - return false; - } + }); - return true; + return result; }; /** @@ -1055,24 +992,31 @@ OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, se OlmDevice.prototype.getInboundGroupSessionKey = async function( roomId, senderKey, sessionId, ) { - function getKey(session, sessionData) { - const messageIndex = session.first_known_index(); - - const claimedKeys = sessionData.keysClaimed || {}; - const senderEd25519Key = claimedKeys.ed25519 || null; - - return { - "chain_index": messageIndex, - "key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - sessionData.forwardingCurve25519KeyChain || [], - "sender_claimed_ed25519_key": senderEd25519Key, - }; - } + let result; + await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + result = null; + return; + } + const messageIndex = session.first_known_index(); + + const claimedKeys = sessionData.keysClaimed || {}; + const senderEd25519Key = claimedKeys.ed25519 || null; + + result = { + "chain_index": messageIndex, + "key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": + sessionData.forwardingCurve25519KeyChain || [], + "sender_claimed_ed25519_key": senderEd25519Key, + }; + }, + ); + }); - return this._getInboundGroupSession( - roomId, senderKey, sessionId, getKey, - ); + return result; }; /** @@ -1083,34 +1027,32 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function( * @return {Promise} exported session data */ OlmDevice.prototype.exportInboundGroupSession = async function(senderKey, sessionId) { - const s = this._sessionStore.getEndToEndInboundGroupSession( - senderKey, sessionId, - ); + let result; + await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + throw new Error( + "Unknown inbound group session [" + senderKey + "," + sessionId + "]" + ); - if (s === null) { - throw new Error("Unknown inbound group session [" + senderKey + "," + - sessionId + "]"); - } - const r = JSON.parse(s); + const messageIndex = session.first_known_index(); - const session = new Olm.InboundGroupSession(); - try { - session.unpickle(this._pickleKey, r.session); - - const messageIndex = session.first_known_index(); + result = { + "sender_key": senderKey, + "sender_claimed_keys": r.keysClaimed, + "room_id": r.room_id, + "session_id": sessionId, + "session_key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": + session.forwardingCurve25519KeyChain || [], + }; + } + }, + ); + }); - return { - "sender_key": senderKey, - "sender_claimed_keys": r.keysClaimed, - "room_id": r.room_id, - "session_id": sessionId, - "session_key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - session.forwardingCurve25519KeyChain || [], - }; - } finally { - session.free(); - } + return result; }; // Utilities diff --git a/src/crypto/algorithms/megolm.js b/src/crypto/algorithms/megolm.js index 372caaf1746..74726c867a5 100644 --- a/src/crypto/algorithms/megolm.js +++ b/src/crypto/algorithms/megolm.js @@ -927,10 +927,18 @@ MegolmDecryption.prototype._buildKeyForwardingMessage = async function( * @param {module:crypto/OlmDevice.MegolmSessionData} session */ MegolmDecryption.prototype.importRoomKey = function(session) { - this._olmDevice.importInboundGroupSession(session); - - // have another go at decrypting events sent with this session. - this._retryDecryption(session.sender_key, session.session_id); + return this._olmDevice.addInboundGroupSession({ + session.room_id, + session.sender_key, + session.forwarding_curve25519_key_chain, + session.session_id, + session.session_key, + session.sender_claimed_keys, + true, + }).then(() => { + // have another go at decrypting events sent with this session. + this._retryDecryption(session.sender_key, session.session_id); + }); }; /** diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index bd812a979e0..004ee3f27cd 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -1,7 +1,7 @@ import Promise from 'bluebird'; import utils from '../../utils'; -export const VERSION = 3; +export const VERSION = 4; /** * Implementation of a CryptoStore which is backed by an existing @@ -316,6 +316,39 @@ export class Backend { objectStore.put({deviceKey, sessionId, session}); } + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { + const objectStore = txn.objectStore("inbound_group_sessions"); + const getReq = objectStore.get([senderCurve25519Key, sessionId]); + getReq.onsuccess = function() { + try { + if (getReq.result) { + func(getReq.result.session); + } else { + func(null); + } + } catch (e) { + abortWithException(txn, e); + } + }; + } + + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + const objectStore = txn.objectStore("inbound_group_sessions"); + const addReq = objectStore.add({ + senderCurve25519Key, sessionId, session: sessionData, + }); + addReq.onerror = () => { + abortWithException(txn, new Error("Inbound Session already exists")); + }; + } + + storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + const objectStore = txn.objectStore("inbound_group_sessions"); + const addReq = objectStore.put({ + senderCurve25519Key, sessionId, session: sessionData, + }); + } + doTxn(mode, stores, func) { const txn = this._db.transaction(stores, mode); const promise = promiseifyTxn(txn); @@ -343,6 +376,11 @@ export function upgradeDatabase(db, oldVersion) { }); sessionsStore.createIndex("deviceKey", "deviceKey"); } + if (oldVersion < 4) { + db.createObjectStore("inbound_group_sessions", { + keyPath: ["senderCurve25519Key", "sessionId"], + }); + } // Expand as needed. } @@ -368,13 +406,28 @@ function abortWithException(txn, e) { // We could alternatively make the thing we pass back to the app // an object containing the transaction and exception. txn._mx_abortexception = e; - txn.abort(); + try { + txn.abort(); + } catch (e) { + // sometimes we won't be able to abort the transaction + // (ie. if it's aborted or completed) + } } function promiseifyTxn(txn) { return new Promise((resolve, reject) => { - txn.oncomplete = resolve; - txn.onerror = reject; + txn.oncomplete = () => { + if (txn._mx_abortexception !== undefined) { + reject(txn._mx_abortexception); + } + resolve(); + }; + txn.onerror = () => { + if (txn._mx_abortexception !== undefined) { + reject(txn._mx_abortexception); + } + reject(); + }; txn.onabort = () => reject(txn._mx_abortexception); }); } diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index d5ad0ceaa41..12b5559cd2e 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -287,6 +287,51 @@ export default class IndexedDBCryptoStore { ); } + /** + * Retrieve the end-to-end inbound group session for a given + * server key and session ID + * @param {string} senderCurve25519Key The sender's curve 25519 key + * @param {string} sessionId The ID of the session + * @param {*} txn An active transaction. See doTxn(). + * @param {function(object)} func Called with A map from sessionId + * to Base64 end-to-end session. + */ + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { + this._backendPromise.value().getEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, txn, func, + ); + } + + /** + * Adds an end-to-end inbound group session to the store. + * If there already exists an inbound group session with the same + * senderCurve25519Key and sessionID, the session will not be added. + * @param {string} senderCurve25519Key The sender's curve 25519 key + * @param {string} sessionId The ID of the session + * @param {object} sessionData The session data structure + * @param {*} txn An active transaction. See doTxn(). + */ + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + this._backendPromise.value().addEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, sessionData, txn, + ); + } + + /** + * Writes an end-to-end inbound group session to the store. + * If there already exists an inbound group session with the same + * senderCurve25519Key and sessionID, it will be overwritten. + * @param {string} senderCurve25519Key The sender's curve 25519 key + * @param {string} sessionId The ID of the session + * @param {object} sessionData The session data structure + * @param {*} txn An active transaction. See doTxn(). + */ + storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + this._backendPromise.value().storeEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, sessionData, txn, + ); + } + /** * Perform a transaction on the crypto store. Any store methods * that require a transaction (txn) object to be passed in may @@ -317,3 +362,4 @@ export default class IndexedDBCryptoStore { IndexedDBCryptoStore.STORE_ACCOUNT = 'account'; IndexedDBCryptoStore.STORE_SESSIONS = 'sessions'; +IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS = 'inbound_group_sessions'; From 30e00d5fa7cf57a4b2c1b5988d358463fa4b7384 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 12:02:05 +0000 Subject: [PATCH 02/24] Add impl to localstorage & memory store --- .../store/indexeddb-crypto-store-backend.js | 6 ++++ src/crypto/store/indexeddb-crypto-store.js | 6 ++++ src/crypto/store/localStorage-crypto-store.js | 34 +++++++++++++++++++ src/crypto/store/memory-crypto-store.js | 24 +++++++++++++ 4 files changed, 70 insertions(+) diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 004ee3f27cd..da29aa60f38 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -258,6 +258,8 @@ export class Backend { return promiseifyTxn(txn); } + // Olm Account + getAccount(txn, func) { const objectStore = txn.objectStore("account"); const getReq = objectStore.get("-"); @@ -275,6 +277,8 @@ export class Backend { objectStore.put(newData, "-"); } + // Olm Sessions + getEndToEndSessions(deviceKey, txn, func) { const objectStore = txn.objectStore("sessions"); const idx = objectStore.index("deviceKey"); @@ -316,6 +320,8 @@ export class Backend { objectStore.put({deviceKey, sessionId, session}); } + // Inbound group sessions + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { const objectStore = txn.objectStore("inbound_group_sessions"); const getReq = objectStore.get([senderCurve25519Key, sessionId]); diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 12b5559cd2e..dd7fa59554d 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -227,6 +227,8 @@ export default class IndexedDBCryptoStore { }); } + // Olm Account + /* * Get the account pickle from the store. * This requires an active transaction. See doTxn(). @@ -249,6 +251,8 @@ export default class IndexedDBCryptoStore { this._backendPromise.value().storeAccount(txn, newData); } + // Olm sessions + /** * Retrieve a specific end-to-end session between the logged-in user * and another device. @@ -287,6 +291,8 @@ export default class IndexedDBCryptoStore { ); } + // Inbound group saessions + /** * Retrieve the end-to-end inbound group session for a given * server key and session ID diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index ec48d99c04a..eed8015eab9 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -34,6 +34,10 @@ function keyEndToEndSessions(deviceKey) { return E2E_PREFIX + "sessions/" + deviceKey; } +function keyEndToEndInboundGroupSession(senderKey, sessionId) { + return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId; +} + /** * @implements {module:crypto/store/base~CryptoStore} */ @@ -43,6 +47,8 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { this.store = global.localStorage; } + // Olm Sessions + _getEndToEndSessions(deviceKey, txn, func) { return getJsonItem(this.store, keyEndToEndSessions(deviceKey)); } @@ -64,6 +70,32 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { ); } + // Inbound Group Sessions + + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { + func(getJsonItem( + this.store, + keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), + )); + } + + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + const existing = getJsonItem( + this.store, + keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), + ); + if (!existing) { + this.storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn); + } + } + + storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + this.store.setItem( + keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), + sessionData, + ); + } + /** * Delete all data from this store. * @@ -74,6 +106,8 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { return Promise.resolve(); } + // Olm account + getAccount(txn, func) { const account = this.store.getItem(KEY_END_TO_END_ACCOUNT); func(account); diff --git a/src/crypto/store/memory-crypto-store.js b/src/crypto/store/memory-crypto-store.js index 8d47270016f..80735c8debe 100644 --- a/src/crypto/store/memory-crypto-store.js +++ b/src/crypto/store/memory-crypto-store.js @@ -34,6 +34,8 @@ export default class MemoryCryptoStore { // Map of {devicekey -> {sessionId -> session pickle}} this._sessions = {}; + // Map of {senderCurve25519Key+'/'+sessionId -> session data object} + this._inboundGroupSessions = {}; } /** @@ -201,6 +203,8 @@ export default class MemoryCryptoStore { return Promise.resolve(null); } + // Olm Account + getAccount(txn, func) { func(this._account); } @@ -209,6 +213,8 @@ export default class MemoryCryptoStore { this._account = newData; } + // Olm Sessions + getEndToEndSession(deviceKey, sessionId, txn, func) { const deviceSessions = this._sessions[deviceKey] || {}; func(deviceSessions[sessionId] || null); @@ -227,6 +233,24 @@ export default class MemoryCryptoStore { deviceSessions[sessionId] = session; } + // Inbound Group Sessions + + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { + func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null); + } + + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + const k = senderCurve25519Key+'/'+sessionId; + if (this._inboundGroupSessions[k] === undefined) { + this._inboundGroupSessions[k] = sessionData; + } + } + + storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { + this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] = sessionData; + } + + doTxn(mode, stores, func) { return Promise.resolve(func(null)); } From b6330c3a4f0ea6f8a01d53a230a33ff05408d1a4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 19:41:44 +0000 Subject: [PATCH 03/24] er, this isn't an object --- src/crypto/algorithms/megolm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crypto/algorithms/megolm.js b/src/crypto/algorithms/megolm.js index 74726c867a5..7534b3ed182 100644 --- a/src/crypto/algorithms/megolm.js +++ b/src/crypto/algorithms/megolm.js @@ -927,7 +927,7 @@ MegolmDecryption.prototype._buildKeyForwardingMessage = async function( * @param {module:crypto/OlmDevice.MegolmSessionData} session */ MegolmDecryption.prototype.importRoomKey = function(session) { - return this._olmDevice.addInboundGroupSession({ + return this._olmDevice.addInboundGroupSession( session.room_id, session.sender_key, session.forwarding_curve25519_key_chain, @@ -935,7 +935,7 @@ MegolmDecryption.prototype.importRoomKey = function(session) { session.session_key, session.sender_claimed_keys, true, - }).then(() => { + ).then(() => { // have another go at decrypting events sent with this session. this._retryDecryption(session.sender_key, session.session_id); }); From e6dd573e8a5885997a6a954f0b78586d67787458 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 19:42:01 +0000 Subject: [PATCH 04/24] Fix test (needs a cryptostore now) --- spec/unit/crypto/algorithms/megolm.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/unit/crypto/algorithms/megolm.spec.js b/spec/unit/crypto/algorithms/megolm.spec.js index b81e866898e..cf8e58f2e0b 100644 --- a/spec/unit/crypto/algorithms/megolm.spec.js +++ b/spec/unit/crypto/algorithms/megolm.spec.js @@ -10,6 +10,7 @@ import Promise from 'bluebird'; import sdk from '../../../..'; import algorithms from '../../../../lib/crypto/algorithms'; import WebStorageSessionStore from '../../../../lib/store/session/webstorage'; +import MemoryCryptoStore from '../../../../lib/crypto/store/memory-crypto-store.js'; import MockStorageApi from '../../../MockStorageApi'; import testUtils from '../../../test-utils'; @@ -45,8 +46,9 @@ describe("MegolmDecryption", function() { const mockStorage = new MockStorageApi(); const sessionStore = new WebStorageSessionStore(mockStorage); + const cryptoStore = new MemoryCryptoStore(mockStorage); - const olmDevice = new OlmDevice(sessionStore); + const olmDevice = new OlmDevice(sessionStore, cryptoStore); megolmDecryption = new MegolmDecryption({ userId: '@user:id', From d92a77f69580ffba714e03fe1c6086ce48e0634a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 22:24:19 +0000 Subject: [PATCH 05/24] lint --- src/crypto/store/localStorage-crypto-store.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index eed8015eab9..0eab4793455 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -85,7 +85,9 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), ); if (!existing) { - this.storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn); + this.storeEndToEndInboundGroupSession( + senderCurve25519Key, sessionId, sessionData, txn, + ); } } From 0362e61f36c0b9affec43751aabbca89d02f5b2d Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 22:24:57 +0000 Subject: [PATCH 06/24] Unused var --- src/crypto/store/indexeddb-crypto-store-backend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index da29aa60f38..378929df8fc 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -350,7 +350,7 @@ export class Backend { storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { const objectStore = txn.objectStore("inbound_group_sessions"); - const addReq = objectStore.put({ + objectStore.put({ senderCurve25519Key, sessionId, session: sessionData, }); } From 0dd8ffa3a0d81710da9b02a5e99af86ba2213f1c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 22:36:24 +0000 Subject: [PATCH 07/24] Much tedious linting --- src/crypto/OlmDevice.js | 357 +++++++++++++++++++++------------------- 1 file changed, 191 insertions(+), 166 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 8b245533a3f..0b662538bb1 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -774,39 +774,37 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) { * @param {function(Olm.InboundGroupSession, InboundGroupSessionData): T} func * function to call. * - * @return {null} the sessionId is unknown - * - * @return {T} result of func - * * @private * @template {T} */ OlmDevice.prototype._getInboundGroupSession = function( roomId, senderKey, sessionId, txn, func, ) { - this._cryptoStore.getEndToEndInboundGroupSession(senderKey, sessionId, txn, (sessionData) => { - if (sessionData === null) { - func(null); - return; - } + this._cryptoStore.getEndToEndInboundGroupSession( + senderKey, sessionId, txn, (sessionData) => { + if (sessionData === null) { + func(null); + return; + } - // check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId !== sessionData.room_id) { - throw new Error( - "Mismatched room_id for inbound group session (expected " + sessionData.room_id + - ", was " + roomId + ")", - ); - } + // check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId !== sessionData.room_id) { + throw new Error( + "Mismatched room_id for inbound group session (expected " + + sessionData.room_id + ", was " + roomId + ")", + ); + } - const session = new Olm.InboundGroupSession(); - try { - session.unpickle(this._pickleKey, sessionData.session); - return func(session, sessionData); - } finally { - session.free(); - } - }); + const session = new Olm.InboundGroupSession(); + try { + session.unpickle(this._pickleKey, sessionData.session); + return func(session, sessionData); + } finally { + session.free(); + } + }, + ); }; /** @@ -827,46 +825,52 @@ OlmDevice.prototype.addInboundGroupSession = async function( sessionId, sessionKey, keysClaimed, exportFormat, ) { - await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - /* if we already have this session, consider updating it */ - this._getInboundGroupSession( - roomId, senderKey, sessionId, txn, (existingSession, existingSessionData) => { - if (existingSession) { - console.log("Update for megolm session " + senderKey + "/" + sessionId); - // for now we just ignore updates. TODO: implement something here - return; - } - - // new session. - const session = new Olm.InboundGroupSession(); - try { - if (exportFormat) { - session.import_session(sessionKey); - } else { - session.create(sessionKey); - } - if (sessionId != session.session_id()) { - throw new Error( - "Mismatched group session ID from senderKey: " + senderKey, + await this._cryptoStore.doTxn( + 'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + /* if we already have this session, consider updating it */ + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, + (existingSession, existingSessionData) => { + if (existingSession) { + console.log( + "Update for megolm session " + senderKey + "/" + sessionId, ); + // for now we just ignore updates. TODO: implement something here + return; } - const sessionData = { - room_id: roomId, - session: session.pickle(this._pickleKey), - keysClaimed: keysClaimed, - forwardingCurve25519KeyChain: forwardingCurve25519KeyChain, - }; - - this._cryptoStore.addEndToEndInboundGroupSession( - senderKey, sessionId, sessionData, txn, - ); - } finally { - session.free(); - } - }, - ); - }); + // new session. + const session = new Olm.InboundGroupSession(); + try { + if (exportFormat) { + session.import_session(sessionKey); + } else { + session.create(sessionKey); + } + if (sessionId != session.session_id()) { + throw new Error( + "Mismatched group session ID from senderKey: " + + senderKey, + ); + } + + const sessionData = { + room_id: roomId, + session: session.pickle(this._pickleKey), + keysClaimed: keysClaimed, + forwardingCurve25519KeyChain: forwardingCurve25519KeyChain, + }; + + this._cryptoStore.addEndToEndInboundGroupSession( + senderKey, sessionId, sessionData, txn, + ); + } finally { + session.free(); + } + }, + ); + }, + ); }; /** @@ -890,51 +894,64 @@ OlmDevice.prototype.decryptGroupMessage = async function( ) { let result; - await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._getInboundGroupSession(roomId, senderKey, sessionId, txn, (session, sessionData) => { - if (session === null) { - result = null; - return; - } - const res = session.decrypt(body); + await this._cryptoStore.doTxn( + 'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + result = null; + return; + } + const res = session.decrypt(body); - let plaintext = res.plaintext; - if (plaintext === undefined) { - // Compatibility for older olm versions. - plaintext = res; - } else { - // Check if we have seen this message index before to detect replay attacks. - // If the event ID and timestamp are specified, and the match the event ID - // and timestamp from the last time we used this message index, then we - // don't consider it a replay attack. - const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index; - if (messageIndexKey in this._inboundGroupSessionMessageIndexes) { - const msgInfo = this._inboundGroupSessionMessageIndexes[messageIndexKey]; - if (msgInfo.id !== eventId || msgInfo.timestamp !== timestamp) { - throw new Error( - "Duplicate message index, possible replay attack: " + - messageIndexKey, + let plaintext = res.plaintext; + if (plaintext === undefined) { + // Compatibility for older olm versions. + plaintext = res; + } else { + // Check if we have seen this message index before to detect replay attacks. + // If the event ID and timestamp are specified, and the match the event ID + // and timestamp from the last time we used this message index, then we + // don't consider it a replay attack. + const messageIndexKey = ( + senderKey + "|" + sessionId + "|" + res.message_index ); + if (messageIndexKey in this._inboundGroupSessionMessageIndexes) { + const msgInfo = ( + this._inboundGroupSessionMessageIndexes[messageIndexKey] + ); + if ( + msgInfo.id !== eventId || + msgInfo.timestamp !== timestamp + ) { + throw new Error( + "Duplicate message index, possible replay attack: " + + messageIndexKey, + ); + } + } + this._inboundGroupSessionMessageIndexes[messageIndexKey] = { + id: eventId, + timestamp: timestamp, + }; } - } - this._inboundGroupSessionMessageIndexes[messageIndexKey] = { - id: eventId, - timestamp: timestamp, - }; - } - sessionData.session = session.pickle(this._pickleKey); - this._cryptoStore.storeEndToEndInboundGroupSession( - senderKey, sessionId, sessionData, txn, + sessionData.session = session.pickle(this._pickleKey); + this._cryptoStore.storeEndToEndInboundGroupSession( + senderKey, sessionId, sessionData, txn, + ); + result = { + result: plaintext, + keysClaimed: sessionData.keysClaimed || {}, + senderKey: senderKey, + forwardingCurve25519KeyChain: ( + sessionData.forwardingCurve25519KeyChain || [] + ), + }; + }, ); - result = { - result: plaintext, - keysClaimed: sessionData.keysClaimed || {}, - senderKey: senderKey, - forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [], - }; - }); - }); + }, + ); return result; }; @@ -950,27 +967,30 @@ OlmDevice.prototype.decryptGroupMessage = async function( */ OlmDevice.prototype.hasInboundSessionKeys = async function(roomId, senderKey, sessionId) { let result; - await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._cryptoStore.getEndToEndInboundGroupSession( - senderKey, sessionId, txn, (sessionData) => { - if (sessionData === null) { - result = false; - return; - } + await this._cryptoStore.doTxn( + 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._cryptoStore.getEndToEndInboundGroupSession( + senderKey, sessionId, txn, (sessionData) => { + if (sessionData === null) { + result = false; + return; + } - if (roomId !== sessionData.room_id) { - console.warn( - `requested keys for inbound group session ${senderKey}|` + - `${sessionId}, with incorrect room_id (expected ${sessionData.room_id}, ` + - `was ${roomId})`, - ); - result = false; - } else { - result = true; - } - }, - ); - }); + if (roomId !== sessionData.room_id) { + console.warn( + `requested keys for inbound group session ${senderKey}|` + + `${sessionId}, with incorrect room_id ` + + `(expected ${sessionData.room_id}, ` + + `was ${roomId})`, + ); + result = false; + } else { + result = true; + } + }, + ); + }, + ); return result; }; @@ -993,28 +1013,30 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function( roomId, senderKey, sessionId, ) { let result; - await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._getInboundGroupSession( - roomId, senderKey, sessionId, txn, (session, sessionData) => { - if (session === null) { - result = null; - return; - } - const messageIndex = session.first_known_index(); - - const claimedKeys = sessionData.keysClaimed || {}; - const senderEd25519Key = claimedKeys.ed25519 || null; - - result = { - "chain_index": messageIndex, - "key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - sessionData.forwardingCurve25519KeyChain || [], - "sender_claimed_ed25519_key": senderEd25519Key, - }; - }, - ); - }); + await this._cryptoStore.doTxn( + 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + result = null; + return; + } + const messageIndex = session.first_known_index(); + + const claimedKeys = sessionData.keysClaimed || {}; + const senderEd25519Key = claimedKeys.ed25519 || null; + + result = { + "chain_index": messageIndex, + "key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": + sessionData.forwardingCurve25519KeyChain || [], + "sender_claimed_ed25519_key": senderEd25519Key, + }; + }, + ); + }, + ); return result; }; @@ -1028,29 +1050,32 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function( */ OlmDevice.prototype.exportInboundGroupSession = async function(senderKey, sessionId) { let result; - await this._cryptoStore.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._getInboundGroupSession( - roomId, senderKey, sessionId, txn, (session, sessionData) => { - if (session === null) { - throw new Error( - "Unknown inbound group session [" + senderKey + "," + sessionId + "]" - ); - - const messageIndex = session.first_known_index(); + await this._cryptoStore.doTxn( + 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._getInboundGroupSession( + roomId, senderKey, sessionId, txn, (session, sessionData) => { + if (session === null) { + throw new Error( + "Unknown inbound group session [" + + senderKey + "," + sessionId + "]", + ); - result = { - "sender_key": senderKey, - "sender_claimed_keys": r.keysClaimed, - "room_id": r.room_id, - "session_id": sessionId, - "session_key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - session.forwardingCurve25519KeyChain || [], - }; - } - }, - ); - }); + const messageIndex = session.first_known_index(); + + result = { + "sender_key": senderKey, + "sender_claimed_keys": r.keysClaimed, + "room_id": r.room_id, + "session_id": sessionId, + "session_key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": + session.forwardingCurve25519KeyChain || [], + }; + } + }, + ); + }, + ); return result; }; From 8da48211d9be666e85a3de8419abae82988be499 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 6 Dec 2017 22:42:19 +0000 Subject: [PATCH 08/24] Fix exportInboundGroupSession --- src/crypto/OlmDevice.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 0b662538bb1..04f69020b46 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -787,9 +787,9 @@ OlmDevice.prototype._getInboundGroupSession = function( return; } - // check that the room id matches the original one for the session. This stops + // if we were given a room ID, check that the it matches the original one for the session. This stops // the HS pretending a message was targeting a different room. - if (roomId !== sessionData.room_id) { + if (roomId !== null && roomId !== sessionData.room_id) { throw new Error( "Mismatched room_id for inbound group session (expected " + sessionData.room_id + ", was " + roomId + ")", @@ -1053,25 +1053,25 @@ OlmDevice.prototype.exportInboundGroupSession = async function(senderKey, sessio await this._cryptoStore.doTxn( 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { this._getInboundGroupSession( - roomId, senderKey, sessionId, txn, (session, sessionData) => { + null, senderKey, sessionId, txn, (session, sessionData) => { if (session === null) { throw new Error( "Unknown inbound group session [" + senderKey + "," + sessionId + "]", ); + } - const messageIndex = session.first_known_index(); + const messageIndex = session.first_known_index(); - result = { - "sender_key": senderKey, - "sender_claimed_keys": r.keysClaimed, - "room_id": r.room_id, - "session_id": sessionId, - "session_key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - session.forwardingCurve25519KeyChain || [], - }; - } + result = { + "sender_key": senderKey, + "sender_claimed_keys": sessionData.keysClaimed, + "room_id": sessionData.room_id, + "session_id": sessionId, + "session_key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": + session.forwardingCurve25519KeyChain || [], + }; }, ); }, From 0fffd64a7fac37cff07403f6f072562b4f4d232f Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 14:23:36 +0000 Subject: [PATCH 09/24] Fix key export --- src/crypto/OlmDevice.js | 64 ++++++++----------- src/crypto/index.js | 25 +++++--- .../store/indexeddb-crypto-store-backend.js | 26 ++++++++ src/crypto/store/indexeddb-crypto-store.js | 11 ++++ src/crypto/store/localStorage-crypto-store.js | 22 ++++++- src/crypto/store/memory-crypto-store.js | 16 +++++ 6 files changed, 116 insertions(+), 48 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 04f69020b46..695abd0c911 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -764,6 +764,16 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) { * this session to us (normally empty). */ +OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) { + const session = new Olm.InboundGroupSession(); + try { + session.unpickle(this._pickleKey, sessionData.session); + return func(session); + } finally { + session.free(); + } +}; + /** * extract an InboundGroupSession from the crypto store and call the given function * @@ -796,13 +806,9 @@ OlmDevice.prototype._getInboundGroupSession = function( ); } - const session = new Olm.InboundGroupSession(); - try { - session.unpickle(this._pickleKey, sessionData.session); - return func(session, sessionData); - } finally { - session.free(); - } + this._unpickleInboundGroupSession(sessionData, (session) => { + func(session, sessionData); + }); }, ); }; @@ -1046,38 +1052,22 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function( * * @param {string} senderKey base64-encoded curve25519 key of the sender * @param {string} sessionId session identifier - * @return {Promise} exported session data + * @param {string} sessiondata The session object from the store + * @return {module:crypto/OlmDevice.MegolmSessionData} exported session data */ -OlmDevice.prototype.exportInboundGroupSession = async function(senderKey, sessionId) { - let result; - await this._cryptoStore.doTxn( - 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._getInboundGroupSession( - null, senderKey, sessionId, txn, (session, sessionData) => { - if (session === null) { - throw new Error( - "Unknown inbound group session [" + - senderKey + "," + sessionId + "]", - ); - } - - const messageIndex = session.first_known_index(); +OlmDevice.prototype.exportInboundGroupSession = function(senderKey, sessionId, sessionData) { + return this._unpickleInboundGroupSession(sessionData, (session) => { + const messageIndex = session.first_known_index(); - result = { - "sender_key": senderKey, - "sender_claimed_keys": sessionData.keysClaimed, - "room_id": sessionData.room_id, - "session_id": sessionId, - "session_key": session.export_session(messageIndex), - "forwarding_curve25519_key_chain": - session.forwardingCurve25519KeyChain || [], - }; - }, - ); - }, - ); - - return result; + return { + "sender_key": senderKey, + "sender_claimed_keys": sessionData.keysClaimed, + "room_id": sessionData.room_id, + "session_id": sessionId, + "session_key": session.export_session(messageIndex), + "forwarding_curve25519_key_chain": session.forwardingCurve25519KeyChain || [], + }; + }); }; // Utilities diff --git a/src/crypto/index.js b/src/crypto/index.js index da14c90c5d6..27629295c75 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -33,6 +33,7 @@ const DeviceVerification = DeviceInfo.DeviceVerification; const DeviceList = require('./DeviceList').default; import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager'; +import IndexedDBCryptoStore from './store/indexeddb-crypto-store'; /** * Cryptography bits @@ -681,21 +682,25 @@ Crypto.prototype.isRoomEncrypted = function(roomId) { /** * Get a list containing all of the room keys * - * @return {module:client.Promise} a promise which resolves to a list of - * session export objects + * @return {module:crypto/OlmDevice.MegolmSessionData[]} a list of session export objects */ -Crypto.prototype.exportRoomKeys = function() { - return Promise.map( - this._sessionStore.getAllEndToEndInboundGroupSessionKeys(), - (s) => { - return this._olmDevice.exportInboundGroupSession( - s.senderKey, s.sessionId, - ).then((sess) => { +Crypto.prototype.exportRoomKeys = async function() { + const exportedSessions = []; + await this._cryptoStore.doTxn( + 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => { + if (s === null) return; + + const sess = this._olmDevice.exportInboundGroupSession( + s.senderKey, s.sessionId, s.sessionData, + ); sess.algorithm = olmlib.MEGOLM_ALGORITHM; - return sess; + exportedSessions.push(sess); }); }, ); + + return exportedSessions; }; /** diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 378929df8fc..db5a7b78dcd 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -338,6 +338,32 @@ export class Backend { }; } + getAllEndToEndInboundGroupSessions(txn, func) { + const objectStore = txn.objectStore("inbound_group_sessions"); + const getReq = objectStore.openCursor(); + getReq.onsuccess = function() { + const cursor = getReq.result; + if (cursor) { + try { + func({ + senderKey: cursor.value.senderCurve25519Key, + sessionId: cursor.value.sessionId, + sessionData: cursor.value.session, + }); + } catch (e) { + abortWithException(txn, e); + } + cursor.continue(); + } else { + try { + func(null); + } catch (e) { + abortWithException(txn, e); + } + } + }; + } + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { const objectStore = txn.objectStore("inbound_group_sessions"); const addReq = objectStore.add({ diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index dd7fa59554d..9ea078c0886 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -308,6 +308,17 @@ export default class IndexedDBCryptoStore { ); } + /** + * Fetches all inbound group sessions in the store + * @param {*} txn An active transaction. See doTxn(). + * @param {function(object|null)} func Called once for each group session + * in the store with an object having keys {senderKey, sessionId, + * sessionData}, then once with null to indicate the end of the list. + */ + getAllEndToEndInboundGroupSessions(txn, func) { + this._backendPromise.value().getAllEndToEndInboundGroupSessions(txn, func); + } + /** * Adds an end-to-end inbound group session to the store. * If there already exists an inbound group session with the same diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index 0eab4793455..d0ce9fe31b8 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -29,13 +29,14 @@ import MemoryCryptoStore from './memory-crypto-store.js'; const E2E_PREFIX = "crypto."; const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account"; +const KEY_INBOUND_SESSION_PREFIX = E2E_PREFIX + "inboundgroupsessions/"; function keyEndToEndSessions(deviceKey) { return E2E_PREFIX + "sessions/" + deviceKey; } function keyEndToEndInboundGroupSession(senderKey, sessionId) { - return E2E_PREFIX + "inboundgroupsessions/" + senderKey + "/" + sessionId; + return KEY_INBOUND_SESSION_PREFIX + senderKey + "/" + sessionId; } /** @@ -79,6 +80,25 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { )); } + getAllEndToEndInboundGroupSessions(txn, func) { + for (let i = 0; i < this.store.length; ++i) { + const key = this.store.key(i); + if (key.startsWith(KEY_INBOUND_SESSION_PREFIX)) { + // we can't use split, as the components we are trying to split out + // might themselves contain '/' characters. We rely on the + // senderKey being a (32-byte) curve25519 key, base64-encoded + // (hence 43 characters long). + + func({ + senderKey: key.substr(KEY_INBOUND_SESSION_PREFIX.length, 43), + sessionId: key.substr(KEY_INBOUND_SESSION_PREFIX.length + 44), + sessionData: getJsonItem(this.store, key), + }); + } + } + func(null); + } + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { const existing = getJsonItem( this.store, diff --git a/src/crypto/store/memory-crypto-store.js b/src/crypto/store/memory-crypto-store.js index 80735c8debe..9c0560de1d3 100644 --- a/src/crypto/store/memory-crypto-store.js +++ b/src/crypto/store/memory-crypto-store.js @@ -239,6 +239,22 @@ export default class MemoryCryptoStore { func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null); } + getAllEndToEndInboundGroupSessions(txn, func) { + for (const key of Object.keys(this._inboundGroupSessions)) { + // we can't use split, as the components we are trying to split out + // might themselves contain '/' characters. We rely on the + // senderKey being a (32-byte) curve25519 key, base64-encoded + // (hence 43 characters long). + + func({ + senderKey: key.substr(0, 43), + sessionId: key.substr(44), + sessionData: this._inboundGroupSessions[key], + }); + } + func(null); + } + addEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { const k = senderCurve25519Key+'/'+sessionId; if (this._inboundGroupSessions[k] === undefined) { From f80626a0fa5a70eed4826f431614da2cfe09f112 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 14:32:23 +0000 Subject: [PATCH 10/24] lint --- src/crypto/OlmDevice.js | 6 ++++-- src/crypto/store/indexeddb-crypto-store.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 695abd0c911..b21b0f60106 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -1052,10 +1052,12 @@ OlmDevice.prototype.getInboundGroupSessionKey = async function( * * @param {string} senderKey base64-encoded curve25519 key of the sender * @param {string} sessionId session identifier - * @param {string} sessiondata The session object from the store + * @param {string} sessionData The session object from the store * @return {module:crypto/OlmDevice.MegolmSessionData} exported session data */ -OlmDevice.prototype.exportInboundGroupSession = function(senderKey, sessionId, sessionData) { +OlmDevice.prototype.exportInboundGroupSession = function( + senderKey, sessionId, sessionData, +) { return this._unpickleInboundGroupSession(sessionData, (session) => { const messageIndex = session.first_known_index(); diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 9ea078c0886..3d0e85155a7 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -311,7 +311,7 @@ export default class IndexedDBCryptoStore { /** * Fetches all inbound group sessions in the store * @param {*} txn An active transaction. See doTxn(). - * @param {function(object|null)} func Called once for each group session + * @param {function(object)} func Called once for each group session * in the store with an object having keys {senderKey, sessionId, * sessionData}, then once with null to indicate the end of the list. */ From 0748c864cd115e37b009906980bafc6eabbd27b6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 14:57:18 +0000 Subject: [PATCH 11/24] Migrate inbound sessions from session store --- src/crypto/OlmDevice.js | 33 +++++++++++++++++++ .../store/indexeddb-crypto-store-backend.js | 8 +++++ src/crypto/store/indexeddb-crypto-store.js | 9 +++++ src/crypto/store/localStorage-crypto-store.js | 12 +++++++ src/crypto/store/memory-crypto-store.js | 4 +++ src/store/session/webstorage.js | 5 ++- 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index b21b0f60106..94b71ebfbca 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -208,6 +208,39 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { this._sessionStore.removeAllEndToEndSessions(); } + + // inbound group sessions + const ibGroupSessions = this._sessionStore.getAllEndToEndInboundGroupSessionKeys(); + if (Object.keys(ibGroupSessions).length > 0) { + let numIbSessions = 0; + await this._cryptoStore.doTxn( + 'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + this._cryptoStore.countEndToEndInboundGroupSessions(txn, (count) => { + if (count) { + console.log( + "Cryto store already has inbound group sessions: "+ + "not migrating", + ); + return; + } + for (const s of ibGroupSessions) { + this._cryptoStore.addEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + this._sessionStore.getEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + ), + ); + ++numIbSessions; + } + console.log( + "Migrating " + numIbSessions + + " inbound group sessions from session store", + ); + }); + }, + ); + this._sessionStore.removeAllEndToEndInboundGroupSessions(); + } }; /** diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index db5a7b78dcd..7d663bb5d4b 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -322,6 +322,14 @@ export class Backend { // Inbound group sessions + countEndToEndInboundGroupSessions(txn, func) { + const objectStore = txn.objectStore("inbound_group_sessions"); + const countReq = objectStore.count(); + countReq.onsuccess = function() { + func(countReq.result); + }; + } + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { const objectStore = txn.objectStore("inbound_group_sessions"); const getReq = objectStore.get([senderCurve25519Key, sessionId]); diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 3d0e85155a7..16ef82c7586 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -293,6 +293,15 @@ export default class IndexedDBCryptoStore { // Inbound group saessions + /** + * Gets the number of inbound group sessions in the store + * @param {*} txn An active transaction. See doTxn(). + * @param {function(object)} func Called witht he count of inbound group sessions + */ + countEndToEndInboundGroupSessions(txn, func) { + this._backendPromise.value().countEndToEndInboundGroupSessions(txn, func); + } + /** * Retrieve the end-to-end inbound group session for a given * server key and session ID diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index d0ce9fe31b8..48de2ba6d18 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -73,6 +73,18 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { // Inbound Group Sessions + countEndToEndInboundGroupSessions(txn, func) { + let count = 0; + for (let i = 0; i < this.store.length; ++i) { + const key = this.store.key(i); + if (key.startsWith(KEY_INBOUND_SESSION_PREFIX)) { + ++count; + } + } + + func(count); + } + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { func(getJsonItem( this.store, diff --git a/src/crypto/store/memory-crypto-store.js b/src/crypto/store/memory-crypto-store.js index 9c0560de1d3..c3babdb498d 100644 --- a/src/crypto/store/memory-crypto-store.js +++ b/src/crypto/store/memory-crypto-store.js @@ -235,6 +235,10 @@ export default class MemoryCryptoStore { // Inbound Group Sessions + countEndToEndInboundGroupSessions(txn, func) { + func(this._inboundGroupSessions.length); + } + getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null); } diff --git a/src/store/session/webstorage.js b/src/store/session/webstorage.js index 675de187a4f..0fa813a2784 100644 --- a/src/store/session/webstorage.js +++ b/src/store/session/webstorage.js @@ -178,9 +178,8 @@ WebStorageSessionStore.prototype = { return this.store.getItem(key); }, - storeEndToEndInboundGroupSession: function(senderKey, sessionId, pickledSession) { - const key = keyEndToEndInboundGroupSession(senderKey, sessionId); - return this.store.setItem(key, pickledSession); + removeAllEndToEndInboundGroupSessions: function() { + removeByPrefix(this.store, E2E_PREFIX + 'inboundgroupsessions/'); }, /** From 1414c24caf514bb68554cb2eaa87188806f299d6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 15:03:02 +0000 Subject: [PATCH 12/24] Missed txn --- src/crypto/OlmDevice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 94b71ebfbca..fab8165784e 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -228,7 +228,7 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { s.senderKey, s.sessionId, this._sessionStore.getEndToEndInboundGroupSession( s.senderKey, s.sessionId, - ), + ), txn, ); ++numIbSessions; } From a1ddeea00ebfb7ae10f9a8681f44cf0e3c130605 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 16:50:18 +0000 Subject: [PATCH 13/24] Fix inbound group session migration Apparently they are parsed at a different layer --- src/crypto/OlmDevice.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index fab8165784e..47380c84168 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -224,12 +224,19 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { return; } for (const s of ibGroupSessions) { - this._cryptoStore.addEndToEndInboundGroupSession( - s.senderKey, s.sessionId, - this._sessionStore.getEndToEndInboundGroupSession( + try { + this._cryptoStore.addEndToEndInboundGroupSession( s.senderKey, s.sessionId, - ), txn, - ); + JSON.parse(this._sessionStore.getEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + )), txn, + ); + } catch (e) { + console.warn( + "Failed to import session " + s.senderKey + "/" + + s.sessionId + ": " + e.stack || e + ); + } ++numIbSessions; } console.log( From b0365f8b0eb2859c4fc70f129233ddc6d9ade403 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 17:07:06 +0000 Subject: [PATCH 14/24] json encode before saving to localstorage --- src/crypto/store/localStorage-crypto-store.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index 48de2ba6d18..4ace51625e4 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -124,7 +124,8 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { } storeEndToEndInboundGroupSession(senderCurve25519Key, sessionId, sessionData, txn) { - this.store.setItem( + setJsonItem( + this.store, keyEndToEndInboundGroupSession(senderCurve25519Key, sessionId), sessionData, ); From 7188f17f9a8388084687173615b3e92774b8ab83 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 7 Dec 2017 17:14:11 +0000 Subject: [PATCH 15/24] more linting --- src/crypto/OlmDevice.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 47380c84168..72c5de8d3ce 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -227,14 +227,16 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { try { this._cryptoStore.addEndToEndInboundGroupSession( s.senderKey, s.sessionId, - JSON.parse(this._sessionStore.getEndToEndInboundGroupSession( - s.senderKey, s.sessionId, - )), txn, + JSON.parse( + this._sessionStore.getEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + ), + ), txn, ); } catch (e) { console.warn( "Failed to import session " + s.senderKey + "/" + - s.sessionId + ": " + e.stack || e + s.sessionId + ": " + e.stack || e, ); } ++numIbSessions; From c43ccb860ba603bd8f79a2c41a93c3b80e5e60d7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 13:31:57 +0000 Subject: [PATCH 16/24] Always migrate inbound group sessions --- src/crypto/OlmDevice.js | 51 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 3d064074071..634d9614451 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -223,37 +223,30 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { let numIbSessions = 0; await this._cryptoStore.doTxn( 'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - this._cryptoStore.countEndToEndInboundGroupSessions(txn, (count) => { - if (count) { - console.log( - "Cryto store already has inbound group sessions: "+ - "not migrating", + // We always migrate inbound group sessions, even if we already have some + // in the new store. They should be be safe to migrate. + for (const s of ibGroupSessions) { + try { + this._cryptoStore.addEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + JSON.parse( + this._sessionStore.getEndToEndInboundGroupSession( + s.senderKey, s.sessionId, + ), + ), txn, + ); + } catch (e) { + console.warn( + "Failed to import session " + s.senderKey + "/" + + s.sessionId + ": " + e.stack || e, ); - return; - } - for (const s of ibGroupSessions) { - try { - this._cryptoStore.addEndToEndInboundGroupSession( - s.senderKey, s.sessionId, - JSON.parse( - this._sessionStore.getEndToEndInboundGroupSession( - s.senderKey, s.sessionId, - ), - ), txn, - ); - } catch (e) { - console.warn( - "Failed to import session " + s.senderKey + "/" + - s.sessionId + ": " + e.stack || e, - ); - } - ++numIbSessions; } - console.log( - "Migrating " + numIbSessions + - " inbound group sessions from session store", - ); - }); + ++numIbSessions; + } + console.log( + "Migrating " + numIbSessions + + " inbound group sessions from session store", + ); }, ); this._sessionStore.removeAllEndToEndInboundGroupSessions(); From e26ade0e620eb7fabe5e574f97ea11a0265d8cd7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 13:32:51 +0000 Subject: [PATCH 17/24] Wording fix --- src/crypto/OlmDevice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 634d9614451..66684b7f6df 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -237,7 +237,7 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { ); } catch (e) { console.warn( - "Failed to import session " + s.senderKey + "/" + + "Failed to migrate session " + s.senderKey + "/" + s.sessionId + ": " + e.stack || e, ); } From fee90bab6673f937ec165e728eec3595ef50f4b4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 13:35:52 +0000 Subject: [PATCH 18/24] Wording fix --- src/crypto/OlmDevice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 66684b7f6df..a82962eb2c1 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -244,7 +244,7 @@ OlmDevice.prototype._migrateFromSessionStore = async function() { ++numIbSessions; } console.log( - "Migrating " + numIbSessions + + "Migrated " + numIbSessions + " inbound group sessions from session store", ); }, From 124ab30f98b9c4ace91f854fe1bb106d26ca197b Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 13:42:16 +0000 Subject: [PATCH 19/24] jsdoc --- src/crypto/OlmDevice.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index a82962eb2c1..1d62122c821 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -807,6 +807,13 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) { * this session to us (normally empty). */ +/** + * Unpickle a session from a sessionData object and invoke the given function. + * The session is valid only until func returns. + * + * @param {Object} sessionData Object describing the session. + * @param {function(Olm.InboundGroupSession)} func Invoked with the unpickled session + */ OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) { const session = new Olm.InboundGroupSession(); try { From 6f3d279165250122ca0c659328b700458955a6cd Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 13:43:43 +0000 Subject: [PATCH 20/24] Fix doc --- src/crypto/OlmDevice.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 1d62122c821..a1c8784bc17 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -831,11 +831,10 @@ OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) { * @param {string} senderKey * @param {string} sessionId * @param {*} txn Opaque transaction object from cryptoStore.doTxn() - * @param {function(Olm.InboundGroupSession, InboundGroupSessionData): T} func + * @param {function(Olm.InboundGroupSession, InboundGroupSessionData)} func * function to call. * * @private - * @template {T} */ OlmDevice.prototype._getInboundGroupSession = function( roomId, senderKey, sessionId, txn, func, From b290ce795fc0bf36423e94c3c8002eec9e3728f8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 16:21:12 +0000 Subject: [PATCH 21/24] Update comment --- src/crypto/OlmDevice.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index a1c8784bc17..2db3773319e 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -827,7 +827,8 @@ OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) { /** * extract an InboundGroupSession from the crypto store and call the given function * - * @param {string} roomId + * @param {string} roomId The room ID to extract the session for, or null to fetch + * sessions for any room. * @param {string} senderKey * @param {string} sessionId * @param {*} txn Opaque transaction object from cryptoStore.doTxn() From c856eb931f41e66b1c8635b2de58daa81f5345f4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 16:32:33 +0000 Subject: [PATCH 22/24] Make error message more truthful --- src/crypto/store/indexeddb-crypto-store-backend.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 04adb3b38f3..193672f3e99 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -386,7 +386,9 @@ export class Backend { senderCurve25519Key, sessionId, session: sessionData, }); addReq.onerror = () => { - abortWithException(txn, new Error("Inbound Session already exists")); + abortWithException(txn, new Error( + "Failed to add inbound group session - session may already exist", + )); }; } From 7a069c40182d8b9d394e039a751c82e31fb2b60f Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 2 Jan 2018 17:03:57 +0000 Subject: [PATCH 23/24] doc return --- src/crypto/OlmDevice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js index 2db3773319e..cda14779c2b 100644 --- a/src/crypto/OlmDevice.js +++ b/src/crypto/OlmDevice.js @@ -813,6 +813,7 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) { * * @param {Object} sessionData Object describing the session. * @param {function(Olm.InboundGroupSession)} func Invoked with the unpickled session + * @return {*} result of func */ OlmDevice.prototype._unpickleInboundGroupSession = function(sessionData, func) { const session = new Olm.InboundGroupSession(); From 145c76095ad4b5fa343d2715751c787f60abf95a Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Jan 2018 10:37:44 +0000 Subject: [PATCH 24/24] Remove unused countEndToEndInboundGroupSessions --- src/crypto/store/indexeddb-crypto-store-backend.js | 8 -------- src/crypto/store/indexeddb-crypto-store.js | 9 --------- src/crypto/store/localStorage-crypto-store.js | 12 ------------ src/crypto/store/memory-crypto-store.js | 4 ---- 4 files changed, 33 deletions(-) diff --git a/src/crypto/store/indexeddb-crypto-store-backend.js b/src/crypto/store/indexeddb-crypto-store-backend.js index 193672f3e99..1a5442c13fe 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.js +++ b/src/crypto/store/indexeddb-crypto-store-backend.js @@ -330,14 +330,6 @@ export class Backend { // Inbound group sessions - countEndToEndInboundGroupSessions(txn, func) { - const objectStore = txn.objectStore("inbound_group_sessions"); - const countReq = objectStore.count(); - countReq.onsuccess = function() { - func(countReq.result); - }; - } - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { const objectStore = txn.objectStore("inbound_group_sessions"); const getReq = objectStore.get([senderCurve25519Key, sessionId]); diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 0a1dcf88ed7..6758e5da9ff 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -302,15 +302,6 @@ export default class IndexedDBCryptoStore { // Inbound group saessions - /** - * Gets the number of inbound group sessions in the store - * @param {*} txn An active transaction. See doTxn(). - * @param {function(object)} func Called witht he count of inbound group sessions - */ - countEndToEndInboundGroupSessions(txn, func) { - this._backendPromise.value().countEndToEndInboundGroupSessions(txn, func); - } - /** * Retrieve the end-to-end inbound group session for a given * server key and session ID diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js index 657ecaf02ba..df1bb087a0f 100644 --- a/src/crypto/store/localStorage-crypto-store.js +++ b/src/crypto/store/localStorage-crypto-store.js @@ -81,18 +81,6 @@ export default class LocalStorageCryptoStore extends MemoryCryptoStore { // Inbound Group Sessions - countEndToEndInboundGroupSessions(txn, func) { - let count = 0; - for (let i = 0; i < this.store.length; ++i) { - const key = this.store.key(i); - if (key.startsWith(KEY_INBOUND_SESSION_PREFIX)) { - ++count; - } - } - - func(count); - } - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { func(getJsonItem( this.store, diff --git a/src/crypto/store/memory-crypto-store.js b/src/crypto/store/memory-crypto-store.js index 53cbf391397..cefb80ee882 100644 --- a/src/crypto/store/memory-crypto-store.js +++ b/src/crypto/store/memory-crypto-store.js @@ -239,10 +239,6 @@ export default class MemoryCryptoStore { // Inbound Group Sessions - countEndToEndInboundGroupSessions(txn, func) { - func(this._inboundGroupSessions.length); - } - getEndToEndInboundGroupSession(senderCurve25519Key, sessionId, txn, func) { func(this._inboundGroupSessions[senderCurve25519Key+'/'+sessionId] || null); }