Compare commits
1 commit
master
...
bwindels/f
Author | SHA1 | Date | |
---|---|---|---|
|
e06bf5850f |
2 changed files with 57 additions and 10 deletions
|
@ -249,7 +249,6 @@ export class RoomEncryption {
|
||||||
/** shares the encryption key for the next message if needed */
|
/** shares the encryption key for the next message if needed */
|
||||||
async ensureNextMessageEncryptionKeyIsShared(hsApi) {
|
async ensureNextMessageEncryptionKeyIsShared(hsApi) {
|
||||||
const txn = this._storage.readWriteTxn([
|
const txn = this._storage.readWriteTxn([
|
||||||
this._storage.storeNames.operations,
|
|
||||||
this._storage.storeNames.outboundGroupSessions,
|
this._storage.storeNames.outboundGroupSessions,
|
||||||
this._storage.storeNames.inboundGroupSessions,
|
this._storage.storeNames.inboundGroupSessions,
|
||||||
]);
|
]);
|
||||||
|
@ -260,18 +259,51 @@ export class RoomEncryption {
|
||||||
txn.abort();
|
txn.abort();
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
await txn.complete();
|
||||||
// will complete the txn
|
// will complete the txn
|
||||||
if (roomKeyMessage) {
|
if (roomKeyMessage) {
|
||||||
await this._shareNewRoomKey(roomKeyMessage, hsApi, txn);
|
// TODO: track room first
|
||||||
|
await this._deviceTracker.trackRoom(this._room);
|
||||||
|
await this._shareNewRoomKey(roomKeyMessage, hsApi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async encrypt(type, content, hsApi) {
|
async encrypt(type, content, hsApi) {
|
||||||
|
let devices;
|
||||||
|
// First, in one transaction, determine whether we need to create a new session
|
||||||
|
const availableSessionId = await this._megolmEncryption.getAvailableSessionId(this._storage, this._room.id, this._encryptionParams);
|
||||||
|
if (!availableSessionId) {
|
||||||
|
// If so, track room and fetch devices (with will do network requests, interrupting any transactions)
|
||||||
await this._deviceTracker.trackRoom(this._room);
|
await this._deviceTracker.trackRoom(this._room);
|
||||||
const megolmResult = await this._megolmEncryption.encrypt(this._room.id, type, content, this._encryptionParams);
|
devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi);
|
||||||
|
}
|
||||||
|
// Now, start another transaction to write the new session and the share operation together, so nothing can get lost.
|
||||||
|
// We pass the session id we fetched earlier to make sure the same session is used if one existed already, otherwise an error is thrown
|
||||||
|
const txn = this._storage.readWriteTxn([
|
||||||
|
this._storage.storeNames.operations,
|
||||||
|
this._storage.storeNames.outboundGroupSessions,
|
||||||
|
this._storage.storeNames.inboundGroupSessions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// AARGH, we run the risk now that while we fetched the devices:
|
||||||
|
// - somebody joined the room and we won't ever send them the key
|
||||||
|
// - somebody left the room and we removed availableSessionId, throwing an error (not too bad, we can retry)
|
||||||
|
//
|
||||||
|
// DO WE HAVE THE SAME PROBLEM IF WE KEEP THE OUTBOUND SESSION IN MEMORY??
|
||||||
|
const megolmResult = await this._megolmEncryption.encrypt(
|
||||||
|
this._room.id,
|
||||||
|
this._encryptionParams,
|
||||||
|
type,
|
||||||
|
content,
|
||||||
|
availableSessionId,
|
||||||
|
txn
|
||||||
|
);
|
||||||
if (megolmResult.roomKeyMessage) {
|
if (megolmResult.roomKeyMessage) {
|
||||||
|
if (!devices) {
|
||||||
|
// a new session was not created, throw
|
||||||
|
}
|
||||||
// TODO: should we await this??
|
// TODO: should we await this??
|
||||||
this._shareNewRoomKey(megolmResult.roomKeyMessage, hsApi);
|
this._shareNewRoomKey(megolmResult.roomKeyMessage, devices, hsApi, txn);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: ENCRYPTED_TYPE,
|
type: ENCRYPTED_TYPE,
|
||||||
|
@ -288,12 +320,9 @@ export class RoomEncryption {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _shareNewRoomKey(roomKeyMessage, hsApi, txn = null) {
|
async _shareNewRoomKey(roomKeyMessage, devices, hsApi, writeOpTxn) {
|
||||||
const devices = await this._deviceTracker.devicesForTrackedRoom(this._room.id, hsApi);
|
|
||||||
const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set()));
|
const userIds = Array.from(devices.reduce((set, device) => set.add(device.userId), new Set()));
|
||||||
|
|
||||||
// store operation for room key share, in case we don't finish here
|
// store operation for room key share, in case we don't finish here
|
||||||
const writeOpTxn = txn || this._storage.readWriteTxn([this._storage.storeNames.operations]);
|
|
||||||
let operationId;
|
let operationId;
|
||||||
try {
|
try {
|
||||||
operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn);
|
operationId = this._writeRoomKeyShareOperation(roomKeyMessage, userIds, writeOpTxn);
|
||||||
|
|
|
@ -43,6 +43,24 @@ export class Encryption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAvailableOutboundSessionId(storage, roomId, encryptionParams) {
|
||||||
|
const txn = storage.readWriteTxn([
|
||||||
|
this._storage.storeNames.outboundGroupSessions,
|
||||||
|
]);
|
||||||
|
const sessionEntry = await txn.outboundGroupSessions.get(roomId);
|
||||||
|
if (sessionEntry) {
|
||||||
|
const session = new this._olm.OutboundGroupSession();
|
||||||
|
try {
|
||||||
|
session.unpickle(this._pickleKey, sessionEntry.session);
|
||||||
|
if (!this._needsToRotate(session, sessionEntry.createdAt, encryptionParams)) {
|
||||||
|
return session.session_id();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
session.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async ensureOutboundSession(roomId, encryptionParams, txn) {
|
async ensureOutboundSession(roomId, encryptionParams, txn) {
|
||||||
let session = new this._olm.OutboundGroupSession();
|
let session = new this._olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
|
@ -90,7 +108,7 @@ export class Encryption {
|
||||||
* @param {object} encryptionParams the content of the m.room.encryption event
|
* @param {object} encryptionParams the content of the m.room.encryption event
|
||||||
* @return {Promise<EncryptionResult>}
|
* @return {Promise<EncryptionResult>}
|
||||||
*/
|
*/
|
||||||
async encrypt(roomId, type, content, encryptionParams) {
|
async encrypt(roomId, encryptionParams, type, content, txn) {
|
||||||
let session = new this._olm.OutboundGroupSession();
|
let session = new this._olm.OutboundGroupSession();
|
||||||
try {
|
try {
|
||||||
const txn = this._storage.readWriteTxn([
|
const txn = this._storage.readWriteTxn([
|
||||||
|
|
Reference in a new issue