matrix-js-sdk 41.3.0 → 41.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/lib/@types/event.d.ts +9 -0
  3. package/lib/@types/event.d.ts.map +1 -1
  4. package/lib/@types/event.js.map +1 -1
  5. package/lib/crypto-api/index.d.ts +27 -5
  6. package/lib/crypto-api/index.d.ts.map +1 -1
  7. package/lib/crypto-api/index.js +22 -3
  8. package/lib/crypto-api/index.js.map +1 -1
  9. package/lib/embedded.js +1 -1
  10. package/lib/embedded.js.map +1 -1
  11. package/lib/matrixrtc/MatrixRTCSession.js +2 -1
  12. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  13. package/lib/matrixrtc/membershipData/rtc.d.ts.map +1 -1
  14. package/lib/matrixrtc/membershipData/rtc.js +5 -4
  15. package/lib/matrixrtc/membershipData/rtc.js.map +1 -1
  16. package/lib/oidc/authorize.d.ts +8 -4
  17. package/lib/oidc/authorize.d.ts.map +1 -1
  18. package/lib/oidc/authorize.js +18 -6
  19. package/lib/oidc/authorize.js.map +1 -1
  20. package/lib/oidc/discovery.js +1 -1
  21. package/lib/oidc/discovery.js.map +1 -1
  22. package/lib/rust-crypto/backup.d.ts +6 -2
  23. package/lib/rust-crypto/backup.d.ts.map +1 -1
  24. package/lib/rust-crypto/backup.js +30 -10
  25. package/lib/rust-crypto/backup.js.map +1 -1
  26. package/lib/rust-crypto/rust-crypto.d.ts +16 -11
  27. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
  28. package/lib/rust-crypto/rust-crypto.js +38 -21
  29. package/lib/rust-crypto/rust-crypto.js.map +1 -1
  30. package/lib/store/indexeddb-local-backend.d.ts.map +1 -1
  31. package/lib/store/indexeddb-local-backend.js +4 -2
  32. package/lib/store/indexeddb-local-backend.js.map +1 -1
  33. package/package.json +14 -10
  34. package/src/@types/event.ts +14 -2
  35. package/src/crypto-api/index.ts +30 -5
  36. package/src/embedded.ts +1 -1
  37. package/src/matrixrtc/MatrixRTCSession.ts +2 -1
  38. package/src/matrixrtc/membershipData/rtc.ts +5 -4
  39. package/src/oidc/authorize.ts +25 -6
  40. package/src/oidc/discovery.ts +1 -1
  41. package/src/rust-crypto/backup.ts +31 -10
  42. package/src/rust-crypto/rust-crypto.ts +28 -13
  43. package/src/store/indexeddb-local-backend.ts +2 -1
@@ -161,7 +161,7 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
161
161
  /**
162
162
  * Handles a backup secret received event and store it if it matches the current backup version.
163
163
  *
164
- * @param secret - The secret as received from a `m.secret.send` event for secret `m.megolm_backup.v1`.
164
+ * @param secret - The secret as received from a `m.secret.send` or `io.element.msc4385.secret.push` event for secret `m.megolm_backup.v1`.
165
165
  * @returns true if the secret is valid and has been stored, false otherwise.
166
166
  */
167
167
  public async handleBackupSecretReceived(secret: string): Promise<boolean> {
@@ -180,28 +180,44 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
180
180
  // There is no server-side key backup.
181
181
  // This decryption key is useless to us.
182
182
  this.logger.warn(
183
- "handleBackupSecretReceived: Received a backup decryption key, but there is no trusted server-side key backup",
183
+ "handleBackupSecretReceived: Received a backup decryption key, but there is no server-side key backup",
184
184
  );
185
185
  return false;
186
186
  }
187
187
 
188
+ let backupDecryptionKey: RustSdkCryptoJs.BackupDecryptionKey;
189
+ try {
190
+ backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(secret);
191
+ } catch (e) {
192
+ this.logger.warn("handleBackupSecretReceived: Invalid backup decryption key", e);
193
+ return false;
194
+ }
195
+
188
196
  try {
189
- const backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(secret);
190
197
  const privateKeyMatches = this.backupInfoMatchesBackupDecryptionKey(latestBackupInfo, backupDecryptionKey);
191
198
  if (!privateKeyMatches) {
192
199
  this.logger.warn(
193
- `handleBackupSecretReceived: Private decryption key does not match the public key of the current remote backup.`,
200
+ `handleBackupSecretReceived: Private decryption key does not match the public key of the current server-side backup version (${latestBackupInfo.version})`,
194
201
  );
195
202
  // just ignore the secret
196
203
  return false;
197
204
  }
198
205
  this.logger.info(
199
- `handleBackupSecretReceived: A valid backup decryption key has been received and stored in cache.`,
206
+ `handleBackupSecretReceived: Valid decryption key for the current server-side backup version (${latestBackupInfo.version}) received`,
200
207
  );
201
208
  await this.saveBackupDecryptionKey(backupDecryptionKey, latestBackupInfo.version);
209
+ // Check if the backup should be enabled (e.g. if it's properly
210
+ // signed), and enable it if it should
211
+ if (this.keyBackupCheckInProgress) {
212
+ await this.keyBackupCheckInProgress;
213
+ }
214
+ this.keyBackupCheckInProgress = this.doCheckKeyBackup(latestBackupInfo).finally(() => {
215
+ this.keyBackupCheckInProgress = null;
216
+ });
217
+ await this.keyBackupCheckInProgress;
202
218
  return true;
203
219
  } catch (e) {
204
- this.logger.warn("handleBackupSecretReceived: Invalid backup decryption key", e);
220
+ this.logger.warn("handleBackupSecretReceived: Unable to validate backup decryption key", e);
205
221
  }
206
222
 
207
223
  return false;
@@ -281,12 +297,17 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
281
297
 
282
298
  private keyBackupCheckInProgress: Promise<KeyBackupCheck | null> | null = null;
283
299
 
284
- /** Helper for `checkKeyBackup` */
285
- private async doCheckKeyBackup(): Promise<KeyBackupCheck | null> {
300
+ /** Helper to check the key backup status, and enable/disable it as appropriate
301
+ *
302
+ * A KeyBackupInfo can be passed if it was fetched recently, to avoid trying to
303
+ * re-fetch it from the server.
304
+ */
305
+ private async doCheckKeyBackup(backupInfo?: KeyBackupInfo | null | undefined): Promise<KeyBackupCheck | null> {
286
306
  this.logger.debug("Checking key backup status...");
287
- let backupInfo: KeyBackupInfo | null | undefined;
288
307
  try {
289
- backupInfo = await this.requestKeyBackupVersion();
308
+ if (!backupInfo) {
309
+ backupInfo = await this.requestKeyBackupVersion();
310
+ }
290
311
  } catch (e) {
291
312
  this.logger.warn("Error checking for active key backup", e);
292
313
  this.serverBackupInfo = undefined;
@@ -736,7 +736,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
736
736
  ? userIdentity.identityNeedsUserApproval()
737
737
  : false;
738
738
  userIdentity.free();
739
- return new UserVerificationStatus(verified, wasVerified, false, needsUserApproval);
739
+ return new UserVerificationStatus(verified, wasVerified, true, needsUserApproval);
740
740
  }
741
741
 
742
742
  /**
@@ -1376,6 +1376,8 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1376
1376
  public async resetKeyBackup(): Promise<void> {
1377
1377
  const backupInfo = await this.backupManager.setupKeyBackup((o) => this.signObject(o));
1378
1378
 
1379
+ await this.pushSecretToVerifiedDevices("m.megolm_backup.v1");
1380
+
1379
1381
  // we want to store the private key in 4S
1380
1382
  // need to check if 4S is set up?
1381
1383
  if (await this.secretStorageHasAESKey()) {
@@ -1971,14 +1973,15 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1971
1973
  * the event.
1972
1974
  *
1973
1975
  * To counter this, we proactively discard any active outgoing Megolm
1974
- * session when we see a `leave` event. Note that we have to do this in
1975
- * `onRoomStateEvent` rather than `onRoomMembership`, because
1976
- * `onRoomMembership` is only called when we see a *change* in membership.
1977
- * In the case of a gappy sync, we might miss Charlie's invite and join,
1978
- * and only see the final `leave` event (sohis membership goes from `leave`
1979
- * to `leave`).
1980
- */
1981
- public onRoomStateEvent(event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent | null): void {
1976
+ * session when we see an event indicating the user left.
1977
+ *
1978
+ * Note that we have to do this in `onRoomStateEvent` rather than
1979
+ * `onRoomMembership`, because `onRoomMembership` is only called when we see
1980
+ * a *change* in membership. In the case of a gappy sync, we might miss
1981
+ * Charlie's invite and join, and only see the final `leave` event (so his
1982
+ * membership goes from `leave` to `leave`).
1983
+ */
1984
+ public onRoomStateEvent(event: MatrixEvent, _state: RoomState, _prevEvent: MatrixEvent | null): void {
1982
1985
  if (event.getType() != EventType.RoomMember) {
1983
1986
  // Ignore all events that aren't member updates.
1984
1987
  return;
@@ -1986,7 +1989,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1986
1989
 
1987
1990
  if (
1988
1991
  event.getStateKey()! !== this.olmMachine.userId.toString() &&
1989
- event.getContent().membership === KnownMembership.Leave
1992
+ event.getContent().membership !== KnownMembership.Join
1990
1993
  ) {
1991
1994
  this.logger.info(`Rotating session for room ${event.getRoomId()} due to member leaving the room`);
1992
1995
  this.forceDiscardSession(event.getRoomId()!);
@@ -2103,9 +2106,9 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
2103
2106
  /**
2104
2107
  * Handles secret received from the rust secret inbox.
2105
2108
  *
2106
- * The gossipped secrets are received using the `m.secret.send` event type
2107
- * and are guaranteed to have been received over a 1-to-1 Olm
2108
- * Session from a verified device.
2109
+ * The gossipped secrets are received using the `m.secret.send` or
2110
+ * `io.element.msc4385.secret.push` event types and are guaranteed to have
2111
+ * been received over a 1-to-1 Olm Session from a verified device.
2109
2112
  *
2110
2113
  * The only secret currently handled in this way is `m.megolm_backup.v1`.
2111
2114
  *
@@ -2253,6 +2256,18 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
2253
2256
  | undefined;
2254
2257
  return identity;
2255
2258
  }
2259
+
2260
+ /**
2261
+ * Push a secret to all of the current user's verified devices.
2262
+ */
2263
+ public async pushSecretToVerifiedDevices(name: string): Promise<void> {
2264
+ const logger = new LogSpan(this.logger, "pushSecretToVerifiedDevices");
2265
+ await this.keyClaimManager.ensureSessionsForUsers(logger, [new RustSdkCryptoJs.UserId(this.userId)]);
2266
+ await this.olmMachine.pushSecretToVerifiedDevices(name);
2267
+ this.outgoingRequestsManager.doProcessOutgoingRequests().catch((e) => {
2268
+ logger.warn("pushSecretToVerifiedDevices: Error processing outgoing requests", e);
2269
+ });
2270
+ }
2256
2271
  }
2257
2272
 
2258
2273
  class EventDecryptor {
@@ -61,6 +61,7 @@ const VERSION = DB_MIGRATIONS.length;
61
61
  * Return the data you want to keep.
62
62
  * @returns Promise which resolves to an array of whatever you returned from
63
63
  * resultMapper.
64
+ * @throws If there was an error completing the query.
64
65
  */
65
66
  function selectQuery<T>(
66
67
  store: IDBObjectStore,
@@ -71,7 +72,7 @@ function selectQuery<T>(
71
72
  return new Promise((resolve, reject) => {
72
73
  const results: T[] = [];
73
74
  query.onerror = (): void => {
74
- reject(new Error("Query failed: " + query.error?.name));
75
+ reject(new Error(`selectQuery failed for ${store.name}`, { cause: query.error }));
75
76
  };
76
77
  // collect results
77
78
  query.onsuccess = (): void => {