Skip to content

Commit 92a75aa

Browse files
authored
Merge pull request #521 from matrix-org/rav/async_crypto/olmlib
Make bits of `olmlib` asynchronous
2 parents d715784 + 906bf88 commit 92a75aa

File tree

5 files changed

+94
-74
lines changed

5 files changed

+94
-74
lines changed

spec/unit/crypto/algorithms/megolm.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ describe("MegolmDecryption", function() {
6060
// we stub out the olm encryption bits
6161
mockOlmLib = {};
6262
mockOlmLib.ensureOlmSessionsForDevices = expect.createSpy();
63-
mockOlmLib.encryptMessageForDevice = expect.createSpy();
63+
mockOlmLib.encryptMessageForDevice =
64+
expect.createSpy().andReturn(Promise.resolve());
6465
megolmDecryption.olmlib = mockOlmLib;
6566
});
6667

src/crypto/DeviceList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ async function _storeDeviceKeys(_olmDevice, userStore, deviceResult) {
587587
const unsigned = deviceResult.unsigned || {};
588588

589589
try {
590-
olmlib.verifySignature(_olmDevice, deviceResult, userId, deviceId, signKey);
590+
await olmlib.verifySignature(_olmDevice, deviceResult, userId, deviceId, signKey);
591591
} catch (e) {
592592
console.warn("Unable to verify signature on device " +
593593
userId + ":" + deviceId + ":" + e);

src/crypto/algorithms/megolm.js

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ MegolmEncryption.prototype._shareKeyWithDevices = function(session, devicesByUse
288288
return olmlib.ensureOlmSessionsForDevices(
289289
this._olmDevice, this._baseApis, devicesByUser,
290290
).then(function(devicemap) {
291-
let haveTargets = false;
291+
const promises = [];
292292

293293
for (const userId in devicesByUser) {
294294
if (!devicesByUser.hasOwnProperty(userId)) {
@@ -328,31 +328,35 @@ MegolmEncryption.prototype._shareKeyWithDevices = function(session, devicesByUse
328328
ciphertext: {},
329329
};
330330

331-
olmlib.encryptMessageForDevice(
332-
encryptedContent.ciphertext,
333-
self._userId,
334-
self._deviceId,
335-
self._olmDevice,
336-
userId,
337-
deviceInfo,
338-
payload,
339-
);
340-
341331
if (!contentMap[userId]) {
342332
contentMap[userId] = {};
343333
}
344334

345335
contentMap[userId][deviceId] = encryptedContent;
346-
haveTargets = true;
336+
337+
promises.push(
338+
olmlib.encryptMessageForDevice(
339+
encryptedContent.ciphertext,
340+
self._userId,
341+
self._deviceId,
342+
self._olmDevice,
343+
userId,
344+
deviceInfo,
345+
payload,
346+
),
347+
);
347348
}
348349
}
349350

350-
if (!haveTargets) {
351+
if (promises.length === 0) {
352+
// no devices to send to
351353
return Promise.resolve();
352354
}
353355

354-
// TODO: retries
355-
return self._baseApis.sendToDevice("m.room.encrypted", contentMap);
356+
return Promise.all(promises).then(() => {
357+
// TODO: retries
358+
return self._baseApis.sendToDevice("m.room.encrypted", contentMap);
359+
});
356360
}).then(function() {
357361
console.log(`Completed megolm keyshare in ${self._roomId}`);
358362

@@ -751,7 +755,7 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
751755
//
752756
// ensureOlmSessionsForUsers has already done the logging,
753757
// so just skip it.
754-
return;
758+
return null;
755759
}
756760

757761
console.log(
@@ -770,24 +774,24 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
770774
ciphertext: {},
771775
};
772776

773-
this.olmlib.encryptMessageForDevice(
777+
return this.olmlib.encryptMessageForDevice(
774778
encryptedContent.ciphertext,
775779
this._userId,
776780
this._deviceId,
777781
this._olmDevice,
778782
userId,
779783
deviceInfo,
780784
payload,
781-
);
782-
783-
const contentMap = {
784-
[userId]: {
785-
[deviceId]: encryptedContent,
786-
},
787-
};
788-
789-
// TODO: retries
790-
return this._baseApis.sendToDevice("m.room.encrypted", contentMap);
785+
).then(() => {
786+
const contentMap = {
787+
[userId]: {
788+
[deviceId]: encryptedContent,
789+
},
790+
};
791+
792+
// TODO: retries
793+
return this._baseApis.sendToDevice("m.room.encrypted", contentMap);
794+
});
791795
}).done();
792796
};
793797

src/crypto/algorithms/olm.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
107107
ciphertext: {},
108108
};
109109

110+
const promises = [];
111+
110112
for (let i = 0; i < users.length; ++i) {
111113
const userId = users[i];
112114
const devices = self._crypto.getStoredDevicesForUser(userId);
@@ -123,15 +125,17 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
123125
continue;
124126
}
125127

126-
olmlib.encryptMessageForDevice(
127-
encryptedContent.ciphertext,
128-
self._userId, self._deviceId, self._olmDevice,
129-
userId, deviceInfo, payloadFields,
128+
promises.push(
129+
olmlib.encryptMessageForDevice(
130+
encryptedContent.ciphertext,
131+
self._userId, self._deviceId, self._olmDevice,
132+
userId, deviceInfo, payloadFields,
133+
),
130134
);
131135
}
132136
}
133137

134-
return encryptedContent;
138+
return Promise.all(promises).return(encryptedContent);
135139
});
136140
};
137141

src/crypto/olmlib.js

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,11 @@ module.exports.MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
4848
* @param {string} recipientUserId
4949
* @param {module:crypto/deviceinfo} recipientDevice
5050
* @param {object} payloadFields fields to include in the encrypted payload
51+
*
52+
* Returns a promise which resolves (to undefined) when the payload
53+
* has been encrypted into `resultsObject`
5154
*/
52-
module.exports.encryptMessageForDevice = function(
55+
module.exports.encryptMessageForDevice = async function(
5356
resultsObject,
5457
ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice,
5558
payloadFields,
@@ -118,7 +121,7 @@ module.exports.encryptMessageForDevice = function(
118121
* an Object mapping from userId to deviceId to
119122
* {@link module:crypto~OlmSessionResult}
120123
*/
121-
module.exports.ensureOlmSessionsForDevices = function(
124+
module.exports.ensureOlmSessionsForDevices = async function(
122125
olmDevice, baseApis, devicesByUser,
123126
) {
124127
const devicesWithoutSession = [
@@ -148,7 +151,7 @@ module.exports.ensureOlmSessionsForDevices = function(
148151
}
149152

150153
if (devicesWithoutSession.length === 0) {
151-
return Promise.resolve(result);
154+
return result;
152155
}
153156

154157
// TODO: this has a race condition - if we try to send another message
@@ -158,55 +161,60 @@ module.exports.ensureOlmSessionsForDevices = function(
158161
// That should eventually resolve itself, but it's poor form.
159162

160163
const oneTimeKeyAlgorithm = "signed_curve25519";
161-
return baseApis.claimOneTimeKeys(
164+
const res = await baseApis.claimOneTimeKeys(
162165
devicesWithoutSession, oneTimeKeyAlgorithm,
163-
).then(function(res) {
164-
const otk_res = res.one_time_keys || {};
165-
for (const userId in devicesByUser) {
166-
if (!devicesByUser.hasOwnProperty(userId)) {
166+
);
167+
168+
const otk_res = res.one_time_keys || {};
169+
const promises = [];
170+
for (const userId in devicesByUser) {
171+
if (!devicesByUser.hasOwnProperty(userId)) {
172+
continue;
173+
}
174+
const userRes = otk_res[userId] || {};
175+
const devices = devicesByUser[userId];
176+
for (let j = 0; j < devices.length; j++) {
177+
const deviceInfo = devices[j];
178+
const deviceId = deviceInfo.deviceId;
179+
if (result[userId][deviceId].sessionId) {
180+
// we already have a result for this device
167181
continue;
168182
}
169-
const userRes = otk_res[userId] || {};
170-
const devices = devicesByUser[userId];
171-
for (let j = 0; j < devices.length; j++) {
172-
const deviceInfo = devices[j];
173-
const deviceId = deviceInfo.deviceId;
174-
if (result[userId][deviceId].sessionId) {
175-
// we already have a result for this device
176-
continue;
177-
}
178183

179-
const deviceRes = userRes[deviceId] || {};
180-
let oneTimeKey = null;
181-
for (const keyId in deviceRes) {
182-
if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) {
183-
oneTimeKey = deviceRes[keyId];
184-
}
185-
}
186-
187-
if (!oneTimeKey) {
188-
console.warn(
189-
"No one-time keys (alg=" + oneTimeKeyAlgorithm +
190-
") for device " + userId + ":" + deviceId,
191-
);
192-
continue;
184+
const deviceRes = userRes[deviceId] || {};
185+
let oneTimeKey = null;
186+
for (const keyId in deviceRes) {
187+
if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) {
188+
oneTimeKey = deviceRes[keyId];
193189
}
190+
}
194191

195-
const sid = _verifyKeyAndStartSession(
196-
olmDevice, oneTimeKey, userId, deviceInfo,
192+
if (!oneTimeKey) {
193+
console.warn(
194+
"No one-time keys (alg=" + oneTimeKeyAlgorithm +
195+
") for device " + userId + ":" + deviceId,
197196
);
198-
result[userId][deviceId].sessionId = sid;
197+
continue;
199198
}
199+
200+
promises.push(
201+
_verifyKeyAndStartSession(
202+
olmDevice, oneTimeKey, userId, deviceInfo,
203+
).then((sid) => {
204+
result[userId][deviceId].sessionId = sid;
205+
}),
206+
);
200207
}
201-
return result;
202-
});
203-
};
208+
}
204209

210+
await Promise.all(promises);
211+
return result;
212+
};
205213

206-
function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
214+
async function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
207215
const deviceId = deviceInfo.deviceId;
208216
try {
209-
_verifySignature(
217+
await _verifySignature(
210218
olmDevice, oneTimeKey, userId, deviceId,
211219
deviceInfo.getFingerprint(),
212220
);
@@ -249,8 +257,11 @@ function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
249257
* @param {string} signingDeviceId ID of the device whose signature should be checked
250258
*
251259
* @param {string} signingKey base64-ed ed25519 public key
260+
*
261+
* Returns a promise which resolves (to undefined) if the the signature is good,
262+
* or rejects with an Error if it is bad.
252263
*/
253-
const _verifySignature = module.exports.verifySignature = function(
264+
const _verifySignature = module.exports.verifySignature = async function(
254265
olmDevice, obj, signingUserId, signingDeviceId, signingKey,
255266
) {
256267
const signKeyId = "ed25519:" + signingDeviceId;

0 commit comments

Comments
 (0)