-
-
Notifications
You must be signed in to change notification settings - Fork 594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate room encryption store to crypto store #597
Changes from 12 commits
c360dd1
88a082a
e720963
e4de333
81d54c7
ac659e8
3149958
e4ffc93
d856285
341371b
1ba0e48
fdf987f
022df1b
c0ca85f
f1194b1
b6cd826
6f50c39
cf72052
cf19526
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,7 @@ const MatrixBaseApis = require("./base-apis"); | |
const MatrixError = httpApi.MatrixError; | ||
|
||
import ReEmitter from './ReEmitter'; | ||
import RoomList from './crypto/RoomList'; | ||
|
||
const SCROLLBACK_DELAY_MS = 3000; | ||
let CRYPTO_ENABLED = false; | ||
|
@@ -181,6 +182,11 @@ function MatrixClient(opts) { | |
if (CRYPTO_ENABLED) { | ||
this.olmVersion = Crypto.getOlmVersion(); | ||
} | ||
|
||
// List of what rooms have encryption enabled: separate from crypto because | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. which rooms There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed :) |
||
// we still want to know what rooms are encrypted even if crypto is disabled: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. which There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also fixed |
||
// we don't want to start sening unencrypted events to them. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sening There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also done |
||
this._roomList = new RoomList(this._cryptoStore, this._sessionStore); | ||
} | ||
utils.inherits(MatrixClient, EventEmitter); | ||
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype); | ||
|
@@ -351,13 +357,6 @@ MatrixClient.prototype.initCrypto = async function() { | |
return; | ||
} | ||
|
||
if (!CRYPTO_ENABLED) { | ||
throw new Error( | ||
`End-to-end encryption not supported in this js-sdk build: did ` + | ||
`you remember to load the olm library?`, | ||
); | ||
} | ||
|
||
if (!this._sessionStore) { | ||
// this is temporary, the sessionstore is supposed to be going away | ||
throw new Error(`Cannot enable encryption: no sessionStore provided`); | ||
|
@@ -367,6 +366,16 @@ MatrixClient.prototype.initCrypto = async function() { | |
throw new Error(`Cannot enable encryption: no cryptoStore provided`); | ||
} | ||
|
||
// initialise the list of encrypted rooms (whether or not crypto is enabled) | ||
await this._roomList.init(); | ||
|
||
if (!CRYPTO_ENABLED) { | ||
throw new Error( | ||
`End-to-end encryption not supported in this js-sdk build: did ` + | ||
`you remember to load the olm library?`, | ||
); | ||
} | ||
|
||
const userId = this.getUserId(); | ||
if (userId === null) { | ||
throw new Error( | ||
|
@@ -387,6 +396,7 @@ MatrixClient.prototype.initCrypto = async function() { | |
userId, this.deviceId, | ||
this.store, | ||
this._cryptoStore, | ||
this._roomList, | ||
); | ||
|
||
this.reEmitter.reEmit(crypto, [ | ||
|
@@ -646,11 +656,7 @@ MatrixClient.prototype.isRoomEncrypted = function(roomId) { | |
// we don't have an m.room.encrypted event, but that might be because | ||
// the server is hiding it from us. Check the store to see if it was | ||
// previously encrypted. | ||
if (!this._sessionStore) { | ||
return false; | ||
} | ||
|
||
return Boolean(this._sessionStore.getEndToEndRoom(roomId)); | ||
return this._roomList.isRoomEncrypted(roomId); | ||
}; | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
Copyright 2018 New Vector Ltd | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
/** | ||
* @module crypto/RoomList | ||
* | ||
* Manages the list of encrypted rooms | ||
*/ | ||
|
||
import IndexedDBCryptoStore from './store/indexeddb-crypto-store'; | ||
|
||
/** | ||
* @alias module:crypto/RoomList | ||
*/ | ||
export default class RoomList { | ||
constructor(cryptoStore, sessionStore) { | ||
this._cryptoStore = cryptoStore; | ||
this._sessionStore = sessionStore; | ||
|
||
// Object of roomId -> room e2e info object | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we document the shape of said object hereabouts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have added that it's the body of the m.room.encryption object |
||
this._roomEncryption = {}; | ||
} | ||
|
||
async init() { | ||
let removeSessionStoreRooms = false; | ||
await this._cryptoStore.doTxn( | ||
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => { | ||
this._cryptoStore.getEndToEndRooms(txn, (result) => { | ||
if (result === null || Object.keys(result).length === 0) { | ||
// migrate from session store, if there's data there | ||
const sessStoreRooms = this._sessionStore.getAllEndToEndRooms(); | ||
if (sessStoreRooms !== null) { | ||
for (const roomId of Object.keys(sessStoreRooms)) { | ||
this._cryptoStore.storeEndToEndRoom( | ||
roomId, sessStoreRooms[roomId], txn, | ||
); | ||
} | ||
} | ||
this._roomEncryption = sessStoreRooms; | ||
removeSessionStoreRooms = true; | ||
} else { | ||
this._roomEncryption = result; | ||
} | ||
}); | ||
}, | ||
); | ||
if (removeSessionStoreRooms) { | ||
this._sessionStore.removeAllEndToEndRooms(); | ||
} | ||
} | ||
|
||
getRoomEncryption(roomId) { | ||
return this._roomEncryption[roomId] || null; | ||
} | ||
|
||
isRoomEncrypted(roomId) { | ||
return Boolean(this.getRoomEncryption(roomId)); | ||
} | ||
|
||
async setRoomEncryption(roomId, roomInfo) { | ||
this._roomEncryption[roomId] = roomInfo; | ||
await this._cryptoStore.doTxn( | ||
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => { | ||
this._cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn); | ||
}, | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,15 +59,18 @@ import IndexedDBCryptoStore from './store/indexeddb-crypto-store'; | |
* | ||
* @param {module:crypto/store/base~CryptoStore} cryptoStore | ||
* storage for the crypto layer. | ||
* | ||
* @param {RoomList} roomList An initialised RoomList object | ||
*/ | ||
function Crypto(baseApis, sessionStore, userId, deviceId, | ||
clientStore, cryptoStore) { | ||
clientStore, cryptoStore, roomList) { | ||
this._baseApis = baseApis; | ||
this._sessionStore = sessionStore; | ||
this._userId = userId; | ||
this._deviceId = deviceId; | ||
this._clientStore = clientStore; | ||
this._cryptoStore = cryptoStore; | ||
this._roomList = roomList; | ||
|
||
this._olmDevice = new OlmDevice(sessionStore, cryptoStore); | ||
this._deviceList = new DeviceList( | ||
|
@@ -587,6 +590,17 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) { | |
return device; | ||
}; | ||
|
||
/** | ||
* Get the current end-to-end encryption config for a room | ||
* | ||
* @param {string} roomId The room ID to query | ||
* | ||
* @return {object} The current end-to-end encyption status, or null if | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand why you've made this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok. I think the reason it's async was to match the rest of the crypto stuff which assumes it's going to be indexeddb backed. I'd also assumed that the Promise return type was implicit on an async function. |
||
* the room is not stored as using end-to-end encryption. | ||
*/ | ||
Crypto.prototype.getRoomEncryption = async function(roomId) { | ||
return this._roomList.getRoomEncryption(roomId); | ||
}; | ||
|
||
/** | ||
* Configure a room to use encryption (ie, save a flag in the sessionstore). | ||
|
@@ -601,21 +615,19 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) { | |
Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDeviceQuery) { | ||
// if we already have encryption in this room, we should ignore this event | ||
// (for now at least. maybe we should alert the user somehow?) | ||
const existingConfig = this._sessionStore.getEndToEndRoom(roomId); | ||
if (existingConfig) { | ||
if (JSON.stringify(existingConfig) != JSON.stringify(config)) { | ||
console.error("Ignoring m.room.encryption event which requests " + | ||
"a change of config in " + roomId); | ||
return; | ||
} | ||
const existingConfig = await this.getRoomEncryption(roomId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. given this seems to be the only place There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mm, point |
||
if (existingConfig && JSON.stringify(existingConfig) != JSON.stringify(config)) { | ||
console.error("Ignoring m.room.encryption event which requests " + | ||
"a change of config in " + roomId); | ||
return; | ||
} | ||
|
||
const AlgClass = algorithms.ENCRYPTION_CLASSES[config.algorithm]; | ||
if (!AlgClass) { | ||
throw new Error("Unable to encrypt with " + config.algorithm); | ||
} | ||
|
||
this._sessionStore.storeEndToEndRoom(roomId, config); | ||
await this._roomList.setRoomEncryption(roomId, config); | ||
|
||
const alg = new AlgClass({ | ||
userId: this._userId, | ||
|
@@ -693,16 +705,6 @@ Crypto.prototype.ensureOlmSessionsForUsers = function(users) { | |
); | ||
}; | ||
|
||
/** | ||
* Whether encryption is enabled for a room. | ||
* @param {string} roomId the room id to query. | ||
* @return {bool} whether encryption is enabled. | ||
*/ | ||
Crypto.prototype.isRoomEncrypted = function(roomId) { | ||
return Boolean(this._roomEncryptors[roomId]); | ||
}; | ||
|
||
|
||
/** | ||
* Get a list containing all of the room keys | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
olm was deliberately omitted here for some reason involving making it possible to do without it. the details currently escape me, but what is the reason for adding it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bah, because npm now adds any package you install to package.json even if you don't specify --save.