Compare commits

..

27 Commits

Author SHA1 Message Date
RiotRobot a1baf39299 v6.0.0 2020-05-05 10:54:38 +01:00
RiotRobot 9f0f1bcc68 Prepare changelog for v6.0.0 2020-05-05 10:54:37 +01:00
J. Ryan Stinnett 246963e181 Merge pull request #1368 from matrix-org/foldleft/13167-spinner-progress-rc
Add progress callback for key backups
2020-05-04 16:34:38 +01:00
Zoe e3134ab0de satisfy linter 2, now with more self-hatred 2020-05-04 16:25:43 +01:00
Zoe b38d52da72 satisfy the linter 2020-05-04 16:25:43 +01:00
Zoe 411c4f40d9 docs 2020-05-04 16:25:43 +01:00
Zoe 694a85b652 lint 2020-05-04 16:25:43 +01:00
Zoe c84e72f53a Added a progressCallback for backup key loading 2020-05-04 16:25:43 +01:00
RiotRobot 135a76febd v6.0.0-rc.2 2020-05-01 16:18:21 +01:00
RiotRobot fae2856e7e Prepare changelog for v6.0.0-rc.2 2020-05-01 16:18:21 +01:00
David Baker 98426b6186 Merge pull request #1365 from matrix-org/dbkr/emit_on_store_trusted_master_key_rel
Emit event when a trusted self-key is stored
2020-05-01 15:37:09 +01:00
David Baker a63d9d2ccd lint 2020-05-01 15:28:15 +01:00
David Baker c567139e28 Emit event when a trusted self-key is stored
This will cause our own user trust status to change, so emit an
event to say so.
2020-05-01 15:28:09 +01:00
RiotRobot f3f6f3e39a v6.0.0-rc.1 2020-04-30 11:14:04 +01:00
RiotRobot f905586dbd Prepare changelog for v6.0.0-rc.1 2020-04-30 11:14:03 +01:00
David Baker f393cea2c2 Merge pull request #1362 from matrix-org/dbkr/sessions_there_on_login_are_old_rel
Add initialFetch param to willUpdateDevices / devicesUpdated
2020-04-29 18:14:13 +01:00
David Baker e937998b40 Add initialFetch param to willUpdateDevices / devicesUpdated
This indicates whether the device database is being populated initially
2020-04-29 17:44:25 +01:00
J. Ryan Stinnett 7ff68f3844 Merge pull request #1361 from matrix-org/bwindels/fixrequestreadyrace-rc
Fix race between sending .request and receiving .ready over to_device
2020-04-29 17:26:11 +01:00
Bruno Windels 04529bd524 add to_device verif request to txnId -> request map before sending
so if the .ready event arrives before the request finishes,
it still ends up in the correct VerificationRequest
2020-04-29 18:20:16 +02:00
J. Ryan Stinnett dca60ae4ae Merge pull request #1358 from matrix-org/bwindels/fixwaitforeventrace-rc
Handle race between sending and await next event from other party
2020-04-29 15:21:35 +01:00
Bruno Windels ff46a8fa9e handle race between sending and await next event from other party 2020-04-29 15:52:20 +02:00
David Baker a351ee9f76 Merge pull request #1356 from matrix-org/dbkr/another_round_of_toast_rel
Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
2020-04-29 12:50:00 +01:00
David Baker a5b14092cd Make getStoredDevice calls sync 2020-04-29 12:23:03 +01:00
David Baker 90f1de9c3f Add crypto.willUpdateDevices event and make getStoredDevices/getStoredDevicesForUser synchronous
Add an event fired before devices are updated, so apps can monitor
changes in devices by observing the state before and after.

BREAKING CHANGE:
Make getStoredDevices/getStoredDevicesForUser synchronous so they
can safely be called from the event listener without racing with
the data being updated. They didn't do any async work so didn't need
to be async.

Add doc for crypto.devicesUpdated
2020-04-29 12:23:00 +01:00
J. Ryan Stinnett 7dc926596f Merge pull request #1355 from matrix-org/jryans/rm-extra-key-backup-setup-rc
Remove redundant key backup setup path
2020-04-29 09:46:17 +01:00
J. Ryan Stinnett e775515c38 Remove the other copy instead 2020-04-28 21:34:17 +01:00
J. Ryan Stinnett c116f2b1bc Remove redundant key backup setup path
Bootstrap was create key backup twice due to some code duplication we missed in
previous refactorings.

Fixes https://github.com/vector-im/riot-web/issues/13423
2020-04-28 21:34:17 +01:00
10 changed files with 131 additions and 27 deletions
+38
View File
@@ -1,3 +1,41 @@
Changes in [6.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0) (2020-05-05)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.2...v6.0.0)
* Add progress callback for key backups
[\#1368](https://github.com/matrix-org/matrix-js-sdk/pull/1368)
Changes in [6.0.0-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.2) (2020-05-01)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.1...v6.0.0-rc.2)
* Emit event when a trusted self-key is stored
[\#1365](https://github.com/matrix-org/matrix-js-sdk/pull/1365)
Changes in [6.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.1) (2020-04-30)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.4...v6.0.0-rc.1)
BREAKING CHANGES
---
* client.getStoredDevicesForUser and client.getStoredDevices are no longer async
All Changes
---
* Add initialFetch param to willUpdateDevices / devicesUpdated
[\#1362](https://github.com/matrix-org/matrix-js-sdk/pull/1362)
* Fix race between sending .request and receiving .ready over to_device
[\#1361](https://github.com/matrix-org/matrix-js-sdk/pull/1361)
* Handle race between sending and await next event from other party
[\#1358](https://github.com/matrix-org/matrix-js-sdk/pull/1358)
* Add crypto.willUpdateDevices event and make
getStoredDevices/getStoredDevicesForUser synchronous
[\#1356](https://github.com/matrix-org/matrix-js-sdk/pull/1356)
* Remove redundant key backup setup path
[\#1355](https://github.com/matrix-org/matrix-js-sdk/pull/1355)
Changes in [5.3.1-rc.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.4) (2020-04-23)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.3...v5.3.1-rc.4)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "5.3.1-rc.4",
"version": "6.0.0",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
+36 -8
View File
@@ -737,6 +737,7 @@ MatrixClient.prototype.initCrypto = async function() {
"crypto.roomKeyRequestCancellation",
"crypto.warning",
"crypto.devicesUpdated",
"crypto.willUpdateDevices",
"deviceVerificationChanged",
"userTrustStatusChanged",
"crossSigning.keysChanged",
@@ -825,9 +826,9 @@ MatrixClient.prototype.downloadKeys = function(userIds, forceDownload) {
*
* @param {string} userId the user to list keys for.
*
* @return {Promise<module:crypto/deviceinfo[]>} list of devices
* @return {module:crypto/deviceinfo[]} list of devices
*/
MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
MatrixClient.prototype.getStoredDevicesForUser = function(userId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -840,9 +841,9 @@ MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
* @param {string} userId the user to list keys for.
* @param {string} deviceId unique identifier for the device
*
* @return {Promise<?module:crypto/deviceinfo>} device or null
* @return {module:crypto/deviceinfo} device or null
*/
MatrixClient.prototype.getStoredDevice = async function(userId, deviceId) {
MatrixClient.prototype.getStoredDevice = function(userId, deviceId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -1435,15 +1436,17 @@ MatrixClient.prototype.exportRoomKeys = function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
* @param {Object} opts
* @param {Function} opts.progressCallback called with an object that has a "stage" param
*
* @return {Promise} a promise which resolves when the keys
* have been imported
*/
MatrixClient.prototype.importRoomKeys = function(keys) {
MatrixClient.prototype.importRoomKeys = function(keys, opts) {
if (!this._crypto) {
throw new Error("End-to-end encryption disabled");
}
return this._crypto.importRoomKeys(keys);
return this._crypto.importRoomKeys(keys, opts);
};
/**
@@ -1886,7 +1889,10 @@ MatrixClient.prototype.restoreKeyBackupWithCache = async function(
MatrixClient.prototype._restoreKeyBackup = function(
privKey, targetRoomId, targetSessionId, backupInfo,
{ cacheCompleteCallback }={}, // For sequencing during tests
{
cacheCompleteCallback, // For sequencing during tests
progressCallback,
}={},
) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
@@ -1921,6 +1927,12 @@ MatrixClient.prototype._restoreKeyBackup = function(
console.warn("Error caching session backup key:", e);
}).then(cacheCompleteCallback);
if (progressCallback) {
progressCallback({
stage: "fetch",
});
}
return this._http.authedRequest(
undefined, "GET", path.path, path.queryData, undefined,
{prefix: PREFIX_UNSTABLE},
@@ -1955,7 +1967,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
}
}
return this.importRoomKeys(keys);
return this.importRoomKeys(keys, { progressCallback });
}).then(() => {
return this._crypto.setTrustedBackupPubKey(backupPubKey);
}).then(() => {
@@ -5586,6 +5598,22 @@ MatrixClient.prototype.generateClientSecret = function() {
* });
*/
/**
* Fires whenever the stored devices for a user have changed
* @event module:client~MatrixClient#"crypto.devicesUpdated"
* @param {String[]} users A list of user IDs that were updated
* @param {bool} initialFetch If true, the store was empty (apart
* from our own device) and has been seeded.
*/
/**
* Fires whenever the stored devices for a user will be updated
* @event module:client~MatrixClient#"crypto.willUpdateDevices"
* @param {String[]} users A list of user IDs that will be updated
* @param {bool} initialFetch If true, the store is empty (apart
* from our own device) and is being seeded.
*/
/**
* Fires whenever the status of e2e key backup changes, as returned by getKeyBackupEnabled()
* @event module:client~MatrixClient#"crypto.keyBackupStatus"
+7 -1
View File
@@ -109,6 +109,9 @@ export class DeviceList extends EventEmitter {
this._savePromiseTime = null;
// The timer used to delay the save
this._saveTimer = null;
// True if we have fetched data from the server or loaded a non-empty
// set of device data from the store
this._hasFetched = null;
}
/**
@@ -118,6 +121,7 @@ export class DeviceList extends EventEmitter {
await this._cryptoStore.doTxn(
'readonly', [IndexedDBCryptoStore.STORE_DEVICE_DATA], (txn) => {
this._cryptoStore.getEndToEndDeviceData(txn, (deviceData) => {
this._hasFetched = Boolean(deviceData && deviceData.devices);
this._devices = deviceData ? deviceData.devices : {},
this._crossSigningInfo = deviceData ?
deviceData.crossSigningInfo || {} : {};
@@ -652,6 +656,7 @@ export class DeviceList extends EventEmitter {
});
const finished = (success) => {
this.emit("crypto.willUpdateDevices", users, !this._hasFetched);
users.forEach((u) => {
this._dirty = true;
@@ -677,7 +682,8 @@ export class DeviceList extends EventEmitter {
}
});
this.saveIfDirty();
this.emit("crypto.devicesUpdated", users);
this.emit("crypto.devicesUpdated", users, !this._hasFetched);
this._hasFetched = true;
};
return prom;
+2 -2
View File
@@ -597,7 +597,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis,
{
[sender]: [
await this._baseApis.getStoredDevice(sender, deviceId),
this._baseApis.getStoredDevice(sender, deviceId),
],
},
);
@@ -607,7 +607,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis.deviceId,
this._baseApis._crypto._olmDevice,
sender,
this._baseApis._crypto.getStoredDevice(sender, deviceId),
this._baseApis.getStoredDevice(sender, deviceId),
payload,
);
const contentMap = {
+35 -12
View File
@@ -169,7 +169,9 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
this._deviceList.on(
'userCrossSigningUpdated', this._onDeviceListUserCrossSigningUpdated,
);
this._reEmitter.reEmit(this._deviceList, ["crypto.devicesUpdated"]);
this._reEmitter.reEmit(this._deviceList, [
"crypto.devicesUpdated", "crypto.willUpdateDevices",
]);
// the last time we did a check for the number of one-time-keys on the
// server.
@@ -596,14 +598,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
}
if (setupNewKeyBackup) {
const info = await this._baseApis.prepareKeyBackupVersion(
null /* random key */,
{ secureSecretStorage: true },
);
await this._baseApis.createKeyBackupVersion(info);
}
} else if (!inStorage && keyBackupInfo) {
// we have an existing backup, but no SSSS
@@ -2027,6 +2021,10 @@ Crypto.prototype.setDeviceVerification = async function(
if (!this._crossSigningInfo.getId() && userId === this._crossSigningInfo.userId) {
this._storeTrustedSelfKeys(xsk.keys);
// This will cause our own user trust to change, so emit the event
this.emit(
"userTrustStatusChanged", this._userId, this.checkUserTrust(userId),
);
}
// Now sign the master key with our user signing key (unless it's ourself)
@@ -2169,7 +2167,8 @@ Crypto.prototype.requestVerification = function(userId, devices) {
if (existingRequest) {
return Promise.resolve(existingRequest);
}
const channel = new ToDeviceChannel(this._baseApis, userId, devices);
const channel = new ToDeviceChannel(this._baseApis, userId, devices,
ToDeviceChannel.makeTransactionId());
return this._requestVerificationWithChannel(
userId,
channel,
@@ -2182,6 +2181,10 @@ Crypto.prototype._requestVerificationWithChannel = async function(
) {
let request = new VerificationRequest(
channel, this._verificationMethods, this._baseApis);
// if transaction id is already known, add request
if (channel.transactionId) {
requestsMap.setRequestByChannel(channel, request);
}
await request.sendRequest();
// don't replace the request created by a racing remote echo
const racingRequest = requestsMap.getRequestByChannel(channel);
@@ -2549,17 +2552,37 @@ Crypto.prototype.exportRoomKeys = async function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
* @param {Object} opts
* @param {Function} opts.progressCallback called with an object which has a stage param
* @return {Promise} a promise which resolves once the keys have been imported
*/
Crypto.prototype.importRoomKeys = function(keys) {
Crypto.prototype.importRoomKeys = function(keys, opts = {}) {
let successes = 0;
let failures = 0;
const total = keys.length;
function updateProgress() {
opts.progressCallback({
stage: "load_keys",
successes,
failures,
total,
});
}
return Promise.all(keys.map((key) => {
if (!key.room_id || !key.algorithm) {
logger.warn("ignoring room key entry with missing fields", key);
failures++;
if (opts.progressCallback) { updateProgress(); }
return null;
}
const alg = this._getRoomDecryptor(key.room_id, key.algorithm);
return alg.importRoomKey(key);
return alg.importRoomKey(key).finally((r) => {
successes++;
if (opts.progressCallback) { updateProgress(); }
});
}));
};
+6 -1
View File
@@ -122,6 +122,11 @@ export class VerificationBase extends EventEmitter {
if (this._done) {
return Promise.reject(new Error("Verification is already done"));
}
const existingEvent = this.request.getEventFromOtherParty(type);
if (existingEvent) {
return Promise.resolve(existingEvent);
}
this._expectedEvent = type;
return new Promise((resolve, reject) => {
this._resolveEvent = resolve;
@@ -370,7 +375,7 @@ export class VerificationBase extends EventEmitter {
for (const [keyId, keyInfo] of Object.entries(keys)) {
const deviceId = keyId.split(':', 2)[1];
const device = await this._baseApis.getStoredDevice(userId, deviceId);
const device = this._baseApis.getStoredDevice(userId, deviceId);
if (device) {
await verifier(keyId, device, keyInfo);
verifiedDevices.push(deviceId);
+1 -1
View File
@@ -207,7 +207,7 @@ export class QRCodeData {
const myUserId = client.getUserId();
const otherDevice = request.targetDevice;
const otherDeviceId = otherDevice ? otherDevice.deviceId : null;
const device = await client.getStoredDevice(myUserId, otherDeviceId);
const device = client.getStoredDevice(myUserId, otherDeviceId);
if (!device) {
throw new Error("could not find device " + otherDeviceId);
}
@@ -877,4 +877,8 @@ export class VerificationRequest extends EventEmitter {
this._setPhase(newTransitions[newTransitions.length - 1].phase);
}
}
getEventFromOtherParty(type) {
return this._eventsByThem.get(type);
}
}
+1 -1
View File
@@ -675,7 +675,7 @@ Room.prototype.hasUnverifiedDevices = async function() {
}
const e2eMembers = await this.getEncryptionTargetMembers();
for (const member of e2eMembers) {
const devices = await this._client.getStoredDevicesForUser(member.userId);
const devices = this._client.getStoredDevicesForUser(member.userId);
if (devices.some((device) => device.isUnverified())) {
return true;
}