matrix-js-sdk 41.1.0 → 41.2.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 (41) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/lib/@types/requests.d.ts +3 -1
  3. package/lib/@types/requests.d.ts.map +1 -1
  4. package/lib/@types/requests.js.map +1 -1
  5. package/lib/client.d.ts.map +1 -1
  6. package/lib/client.js +12 -7
  7. package/lib/client.js.map +1 -1
  8. package/lib/common-crypto/CryptoBackend.d.ts +6 -1
  9. package/lib/common-crypto/CryptoBackend.d.ts.map +1 -1
  10. package/lib/common-crypto/CryptoBackend.js.map +1 -1
  11. package/lib/crypto-api/index.d.ts +0 -7
  12. package/lib/crypto-api/index.d.ts.map +1 -1
  13. package/lib/crypto-api/index.js.map +1 -1
  14. package/lib/matrix.d.ts +1 -0
  15. package/lib/matrix.d.ts.map +1 -1
  16. package/lib/matrix.js +1 -0
  17. package/lib/matrix.js.map +1 -1
  18. package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -1
  19. package/lib/rendezvous/MSC4108SignInWithQR.js +5 -5
  20. package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -1
  21. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts +3 -3
  22. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts.map +1 -1
  23. package/lib/rendezvous/channels/MSC4108SecureChannel.js +2 -2
  24. package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -1
  25. package/lib/rust-crypto/index.d.ts.map +1 -1
  26. package/lib/rust-crypto/index.js +13 -1
  27. package/lib/rust-crypto/index.js.map +1 -1
  28. package/lib/rust-crypto/rust-crypto.d.ts +3 -3
  29. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
  30. package/lib/rust-crypto/rust-crypto.js +306 -281
  31. package/lib/rust-crypto/rust-crypto.js.map +1 -1
  32. package/package.json +3 -3
  33. package/src/@types/requests.ts +3 -1
  34. package/src/client.ts +10 -6
  35. package/src/common-crypto/CryptoBackend.ts +13 -1
  36. package/src/crypto-api/index.ts +0 -14
  37. package/src/matrix.ts +1 -0
  38. package/src/rendezvous/MSC4108SignInWithQR.ts +6 -6
  39. package/src/rendezvous/channels/MSC4108SecureChannel.ts +5 -5
  40. package/src/rust-crypto/index.ts +17 -1
  41. package/src/rust-crypto/rust-crypto.ts +70 -46
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matrix-js-sdk",
3
- "version": "41.1.0",
3
+ "version": "41.2.0",
4
4
  "description": "Matrix Client-Server SDK for Javascript",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -48,7 +48,7 @@
48
48
  ],
49
49
  "dependencies": {
50
50
  "@babel/runtime": "^7.12.5",
51
- "@matrix-org/matrix-sdk-crypto-wasm": "^17.1.0",
51
+ "@matrix-org/matrix-sdk-crypto-wasm": "^18.0.0",
52
52
  "another-json": "^0.2.0",
53
53
  "bs58": "^6.0.0",
54
54
  "content-type": "^1.0.4",
@@ -131,5 +131,5 @@
131
131
  "eslint": "8"
132
132
  }
133
133
  },
134
- "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc"
134
+ "packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017"
135
135
  }
@@ -58,7 +58,9 @@ export interface InviteOpts {
58
58
  /**
59
59
  * Before sending the invite, if the room is encrypted, share the keys for any messages sent while the history
60
60
  * visibility was `shared`, via the experimental
61
- * support for [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268).
61
+ * support for [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268). If the room's current
62
+ * history visibility setting is neither `shared` nor `world_readable`, history sharing will be disabled to prevent
63
+ * exposing keys for messages sent prior to the visibility restriction.
62
64
  *
63
65
  * @experimental
64
66
  */
package/src/client.ts CHANGED
@@ -2428,12 +2428,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
2428
2428
 
2429
2429
  const roomId = res.room_id;
2430
2430
  if (opts.acceptSharedHistory && inviter && this.cryptoBackend) {
2431
+ // Flag upfront that we are waiting for a key bundle, so that if we crash mid-import, we can try again.
2432
+ await this.cryptoBackend.markRoomAsPendingKeyBundle(roomId, inviter);
2431
2433
  // Try to accept the room key bundle specified in a `m.room_key_bundle` to-device message we (might have) already received.
2432
- const bundleDownloaded = await this.cryptoBackend.maybeAcceptKeyBundle(roomId, inviter);
2433
- // If this fails, i.e. we haven't received this message yet, we need to wait until the to-device message arrives.
2434
- if (!bundleDownloaded) {
2435
- this.cryptoBackend.markRoomAsPendingKeyBundle(roomId, inviter);
2436
- }
2434
+ await this.cryptoBackend.maybeAcceptKeyBundle(roomId, inviter);
2437
2435
  }
2438
2436
 
2439
2437
  // In case we were originally given an alias, check the room cache again
@@ -4088,7 +4086,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
4088
4086
  }
4089
4087
 
4090
4088
  if (opts.shareEncryptedHistory) {
4091
- await this.cryptoBackend?.shareRoomHistoryWithUser(roomId, userId);
4089
+ const historyVisibility = this.getRoom(roomId)?.getHistoryVisibility() ?? HistoryVisibility.Shared;
4090
+ // We should only share room history if the *current* visibility allows it.
4091
+ if ([HistoryVisibility.Invited, HistoryVisibility.Joined].includes(historyVisibility)) {
4092
+ this.logger.debug("Not sharing message history as the room history visibility is currently unshared");
4093
+ } else {
4094
+ await this.cryptoBackend?.shareRoomHistoryWithUser(roomId, userId);
4095
+ }
4092
4096
  }
4093
4097
 
4094
4098
  return await this.membershipChange(roomId, userId, KnownMembership.Invite, opts.reason);
@@ -80,6 +80,18 @@ export interface CryptoBackend extends SyncCryptoCallbacks, CryptoApi {
80
80
  */
81
81
  importBackedUpRoomKeys(keys: IMegolmSessionData[], backupVersion: string, opts?: ImportRoomKeysOpts): Promise<void>;
82
82
 
83
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
84
+ //
85
+ // Room key history sharing (MSC4268)
86
+ //
87
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
88
+
89
+ /**
90
+ * Share any shareable E2EE history in the given room with the given recipient,
91
+ * as per [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268)
92
+ */
93
+ shareRoomHistoryWithUser(roomId: string, userId: string): Promise<void>;
94
+
83
95
  /**
84
96
  * Having accepted an invite for the given room from the given user, attempt to
85
97
  * find information about a room key bundle and, if found, download the
@@ -103,7 +115,7 @@ export interface CryptoBackend extends SyncCryptoCallbacks, CryptoApi {
103
115
  * @param roomId - The room we were invited to, for which we did not receive a key bundle before accepting the invite.
104
116
  * @param inviterId - The user who invited us to the room and is expected to send the room key bundle.
105
117
  */
106
- markRoomAsPendingKeyBundle(roomId: string, inviterId: string): void;
118
+ markRoomAsPendingKeyBundle(roomId: string, inviterId: string): Promise<void>;
107
119
  }
108
120
 
109
121
  /** The methods which crypto implementations should expose to the Sync api
@@ -720,20 +720,6 @@ export interface CryptoApi {
720
720
  * @param secrets - The secrets bundle received from the other device
721
721
  */
722
722
  importSecretsBundle?(secrets: Awaited<ReturnType<SecretsBundle["to_json"]>>): Promise<void>;
723
-
724
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
725
- //
726
- // Room key history sharing (MSC4268)
727
- //
728
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
729
-
730
- /**
731
- * Share any shareable E2EE history in the given room with the given recipient,
732
- * as per [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268)
733
- *
734
- * @experimental
735
- */
736
- shareRoomHistoryWithUser(roomId: string, userId: string): Promise<void>;
737
723
  }
738
724
 
739
725
  /** A reason code for a failure to decrypt an event. */
package/src/matrix.ts CHANGED
@@ -82,6 +82,7 @@ export * from "./models/room-summary.ts";
82
82
  export * from "./models/event-status.ts";
83
83
  export * from "./models/profile-keys.ts";
84
84
  export * from "./models/related-relations.ts";
85
+ export { type StickyMatrixEvent, RoomStickyEventsEvent } from "./models/room-sticky-events.ts";
85
86
  export type { RoomSummary } from "./client.ts";
86
87
  export * as ContentHelpers from "./content-helpers.ts";
87
88
  export * as SecretStorage from "./secret-storage.ts";
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { QrCodeMode } from "@matrix-org/matrix-sdk-crypto-wasm";
17
+ import { QrCodeIntent } from "@matrix-org/matrix-sdk-crypto-wasm";
18
18
 
19
19
  import {
20
20
  ClientRendezvousFailureReason,
@@ -108,7 +108,7 @@ interface SecretsPayload extends MSC4108Payload, Awaited<ReturnType<NonNullable<
108
108
  * @experimental Note that this is UNSTABLE and may have breaking changes without notice.
109
109
  */
110
110
  export class MSC4108SignInWithQR {
111
- private readonly ourIntent: QrCodeMode;
111
+ private readonly ourIntent: QrCodeIntent;
112
112
  private _code?: Uint8Array;
113
113
  private expectingNewDeviceId?: string;
114
114
 
@@ -131,7 +131,7 @@ export class MSC4108SignInWithQR {
131
131
  private readonly client?: MatrixClient,
132
132
  public onFailure?: RendezvousFailureListener,
133
133
  ) {
134
- this.ourIntent = client ? QrCodeMode.Reciprocate : QrCodeMode.Login;
134
+ this.ourIntent = client ? QrCodeIntent.Reciprocate : QrCodeIntent.Login;
135
135
  }
136
136
 
137
137
  /**
@@ -149,9 +149,9 @@ export class MSC4108SignInWithQR {
149
149
  return;
150
150
  }
151
151
 
152
- if (this.ourIntent === QrCodeMode.Reciprocate && this.client) {
152
+ if (this.ourIntent === QrCodeIntent.Reciprocate && this.client) {
153
153
  this._code = await this.channel.generateCode(this.ourIntent, this.client.getDomain()!);
154
- } else if (this.ourIntent === QrCodeMode.Login) {
154
+ } else if (this.ourIntent === QrCodeIntent.Login) {
155
155
  this._code = await this.channel.generateCode(this.ourIntent);
156
156
  }
157
157
  }
@@ -160,7 +160,7 @@ export class MSC4108SignInWithQR {
160
160
  * Returns true if the device is the already logged in device reciprocating a new login on the other side of the channel.
161
161
  */
162
162
  public get isExistingDevice(): boolean {
163
- return this.ourIntent === QrCodeMode.Reciprocate;
163
+ return this.ourIntent === QrCodeIntent.Reciprocate;
164
164
  }
165
165
 
166
166
  /**
@@ -19,7 +19,7 @@ import {
19
19
  Ecies,
20
20
  type EstablishedEcies,
21
21
  QrCodeData,
22
- QrCodeMode,
22
+ QrCodeIntent,
23
23
  } from "@matrix-org/matrix-sdk-crypto-wasm";
24
24
 
25
25
  import {
@@ -56,9 +56,9 @@ export class MSC4108SecureChannel {
56
56
  * @param mode the mode to generate the QR code in, either `Login` or `Reciprocate`.
57
57
  * @param serverName the name of the homeserver to connect to, as defined by server discovery in the spec, required for `Reciprocate` mode.
58
58
  */
59
- public async generateCode(mode: QrCodeMode.Login): Promise<Uint8Array>;
60
- public async generateCode(mode: QrCodeMode.Reciprocate, serverName: string): Promise<Uint8Array>;
61
- public async generateCode(mode: QrCodeMode, serverName?: string): Promise<Uint8Array> {
59
+ public async generateCode(mode: QrCodeIntent.Login): Promise<Uint8Array>;
60
+ public async generateCode(mode: QrCodeIntent.Reciprocate, serverName: string): Promise<Uint8Array>;
61
+ public async generateCode(mode: QrCodeIntent, serverName?: string): Promise<Uint8Array> {
62
62
  const { url } = this.rendezvousSession;
63
63
 
64
64
  if (!url) {
@@ -68,7 +68,7 @@ export class MSC4108SecureChannel {
68
68
  return new QrCodeData(
69
69
  this.secureChannel.public_key(),
70
70
  url,
71
- mode === QrCodeMode.Reciprocate ? serverName : undefined,
71
+ mode === QrCodeIntent.Reciprocate ? serverName : undefined,
72
72
  ).toBytes();
73
73
  }
74
74
 
@@ -17,7 +17,7 @@ limitations under the License.
17
17
  import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
18
18
  import { StoreHandle } from "@matrix-org/matrix-sdk-crypto-wasm";
19
19
 
20
- import { RustCrypto } from "./rust-crypto.ts";
20
+ import { MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE, RustCrypto } from "./rust-crypto.ts";
21
21
  import { type IHttpOpts, type MatrixHttpApi } from "../http-api/index.ts";
22
22
  import { type ServerSideSecretStorage } from "../secret-storage.ts";
23
23
  import { type Logger } from "../logger.ts";
@@ -247,5 +247,21 @@ async function initOlmMachine(
247
247
  }
248
248
  }
249
249
 
250
+ // If we have any recently-joined rooms, see if we have a pending key bundle for them.
251
+ for (const pendingDetails of await olmMachine.getAllRoomsPendingKeyBundles()) {
252
+ const roomId = pendingDetails.roomId.toString();
253
+ if (Date.now() - pendingDetails.inviteAcceptedAtMillis <= MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {
254
+ logger.info(
255
+ `Checking for pending key bundle for recently-joined room ${roomId} (joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,
256
+ );
257
+ await rustCrypto.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString());
258
+ } else {
259
+ logger.info(
260
+ `Clearing pending-key-bundle flag for room ${roomId} (too old: joined ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()})`,
261
+ );
262
+ await olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
263
+ }
264
+ }
265
+
250
266
  return rustCrypto;
251
267
  }
@@ -111,6 +111,9 @@ interface ISignableObject {
111
111
  unsigned?: object;
112
112
  }
113
113
 
114
+ /** The maximum time, in milliseconds, since we accepted an invite, that we should accept a key bundle. */
115
+ export const MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE = 24 * 60 * 60 * 1000; // 24 hours
116
+
114
117
  /**
115
118
  * An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
116
119
  *
@@ -131,9 +134,6 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
131
134
  /** mapping of roomId → encryptor class */
132
135
  private roomEncryptors: Record<string, RoomEncryptor> = {};
133
136
 
134
- /** mapping of room ID -> inviter ID for rooms pending MSC4268 key bundles */
135
- private readonly roomsPendingKeyBundles: Map<string, string> = new Map();
136
-
137
137
  private eventDecryptor: EventDecryptor;
138
138
  private keyClaimManager: KeyClaimManager;
139
139
  private outgoingRequestProcessor: OutgoingRequestProcessor;
@@ -370,10 +370,10 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
370
370
  /* allowRedirects */ true,
371
371
  /* useAuthentication */ true,
372
372
  );
373
- let encryptedBundle: Blob;
373
+ let encryptedBundle: Uint8Array;
374
374
  try {
375
375
  const bundleUrl = new URL(url);
376
- encryptedBundle = await this.http.authedRequest<Blob>(
376
+ const encryptedBundleBlob = await this.http.authedRequest<Blob>(
377
377
  Method.Get,
378
378
  bundleUrl.pathname + bundleUrl.search,
379
379
  {},
@@ -383,17 +383,24 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
383
383
  prefix: "",
384
384
  },
385
385
  );
386
+ logger.info(`Received blob of length ${encryptedBundleBlob.size}`);
387
+ encryptedBundle = new Uint8Array(await encryptedBundleBlob.arrayBuffer());
386
388
  } catch (err) {
387
389
  logger.warn(`Error downloading encrypted bundle from ${url}:`, err);
388
390
  throw err;
389
391
  }
390
392
 
391
- logger.info(`Received blob of length ${encryptedBundle.size}`);
392
393
  try {
393
- await this.olmMachine.receiveRoomKeyBundle(bundleData, new Uint8Array(await encryptedBundle.arrayBuffer()));
394
+ await this.olmMachine.receiveRoomKeyBundle(bundleData, encryptedBundle);
394
395
  } catch (err) {
395
396
  logger.warn(`Error receiving encrypted bundle:`, err);
397
+
396
398
  throw err;
399
+ } finally {
400
+ // Even if we were unable to import the bundle, we still clear the flag that indicates that we
401
+ // are waiting for the bundle to be received. The only reason this can happen is that the bundle was
402
+ // malformed somehow, so we don't want to keep retrying it.
403
+ await this.olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
397
404
  }
398
405
 
399
406
  return true;
@@ -402,8 +409,11 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
402
409
  /**
403
410
  * Implementation of {@link CryptoBackend.markRoomAsPendingKeyBundle}.
404
411
  */
405
- public markRoomAsPendingKeyBundle(roomId: string, inviter: string): void {
406
- this.roomsPendingKeyBundles.set(roomId, inviter);
412
+ public async markRoomAsPendingKeyBundle(roomId: string, inviter: string): Promise<void> {
413
+ await this.olmMachine.storeRoomPendingKeyBundle(
414
+ new RustSdkCryptoJs.RoomId(roomId),
415
+ new RustSdkCryptoJs.UserId(inviter),
416
+ );
407
417
  }
408
418
 
409
419
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -781,9 +791,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
781
791
  * Implementation of {@link CryptoApi#getCrossSigningKeyId}
782
792
  */
783
793
  public async getCrossSigningKeyId(type: CrossSigningKey = CrossSigningKey.Master): Promise<string | null> {
784
- const userIdentity: RustSdkCryptoJs.OwnUserIdentity | undefined = await this.olmMachine.getIdentity(
785
- new RustSdkCryptoJs.UserId(this.userId),
786
- );
794
+ const userIdentity = await this.getOwnIdentity();
787
795
  if (!userIdentity) {
788
796
  // The public keys are not available on this device
789
797
  return null;
@@ -1012,9 +1020,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1012
1020
  * Implementation of {@link CryptoApi#getCrossSigningStatus}
1013
1021
  */
1014
1022
  public async getCrossSigningStatus(): Promise<CrossSigningStatus> {
1015
- const userIdentity: RustSdkCryptoJs.OwnUserIdentity | null = await this.getOlmMachineOrThrow().getIdentity(
1016
- new RustSdkCryptoJs.UserId(this.userId),
1017
- );
1023
+ const userIdentity = await this.getOwnIdentity();
1018
1024
 
1019
1025
  const publicKeysOnDevice =
1020
1026
  Boolean(userIdentity?.masterKey) &&
@@ -1128,9 +1134,9 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1128
1134
  * Implementation of {@link CryptoApi#requestVerificationDM}
1129
1135
  */
1130
1136
  public async requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest> {
1131
- const userIdentity: RustSdkCryptoJs.OtherUserIdentity | undefined = await this.olmMachine.getIdentity(
1132
- new RustSdkCryptoJs.UserId(userId),
1133
- );
1137
+ const userIdentity = (await this.olmMachine.getIdentity(new RustSdkCryptoJs.UserId(userId))) as
1138
+ | RustSdkCryptoJs.OtherUserIdentity
1139
+ | undefined;
1134
1140
 
1135
1141
  if (!userIdentity) throw new Error(`unknown userId ${userId}`);
1136
1142
 
@@ -1214,9 +1220,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1214
1220
  * @returns a VerificationRequest when the request has been sent to the other party.
1215
1221
  */
1216
1222
  public async requestOwnUserVerification(): Promise<VerificationRequest> {
1217
- const userIdentity: RustSdkCryptoJs.OwnUserIdentity | undefined = await this.olmMachine.getIdentity(
1218
- new RustSdkCryptoJs.UserId(this.userId),
1219
- );
1223
+ const userIdentity = await this.getOwnIdentity();
1220
1224
  if (userIdentity === undefined) {
1221
1225
  throw new Error("cannot request verification for this device when there is no existing cross-signing key");
1222
1226
  }
@@ -1726,34 +1730,37 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1726
1730
  },
1727
1731
  });
1728
1732
 
1729
- // If we have received a room key bundle message, and have previously marked the room
1730
- // IDs it references as pending key bundles, tell the Rust SDK to try and accept it,
1731
- // just in case it was received after invite.
1733
+ // If we have received a room key bundle message, and have recently joined the room in question,
1734
+ // tell the Rust SDK to try and accept the key bundle.
1732
1735
  //
1733
1736
  // We don't actually need to validate the contents of the bundle message, or do
1734
1737
  // anything with its contents at all. We simply want to inform the Rust SDK we have
1735
1738
  // received a new room key bundle that we might be able to download.
1736
- if (
1737
- isRoomKeyBundleMessage(parsedMessage) &&
1738
- this.roomsPendingKeyBundles.has(parsedMessage.content.room_id)
1739
- ) {
1740
- // No `await`-ing here, as this is called from inside the `/sync` loop.
1741
- this.maybeAcceptKeyBundle(
1742
- parsedMessage.content.room_id,
1743
- this.roomsPendingKeyBundles.get(parsedMessage.content.room_id)!,
1744
- ).then(
1745
- (success) => {
1746
- if (success) {
1747
- this.roomsPendingKeyBundles.delete(parsedMessage.content.room_id);
1748
- }
1749
- },
1750
- (err) => {
1751
- this.logger.error(
1752
- `Error attempting to download key bundle for room ${parsedMessage.content.room_id}`,
1753
- );
1754
- this.logger.error(err);
1755
- },
1739
+ if (isRoomKeyBundleMessage(parsedMessage)) {
1740
+ const roomId = parsedMessage.content.room_id;
1741
+ const pendingDetails = await this.olmMachine.getPendingKeyBundleDetailsForRoom(
1742
+ new RustSdkCryptoJs.RoomId(roomId),
1756
1743
  );
1744
+ // Only accept the key bundle if we joined the room less than 24 hours ago.
1745
+ if (!pendingDetails) {
1746
+ this.logger.debug(
1747
+ `Not yet accepting key bundle for room where we are not awaiting a bundle: ${roomId}`,
1748
+ );
1749
+ } else if (
1750
+ Date.now() - pendingDetails.inviteAcceptedAtMillis >
1751
+ MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE
1752
+ ) {
1753
+ this.logger.info(
1754
+ `Ignoring key bundle for room we joined too long ago: ${roomId}, joining time: ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()}`,
1755
+ );
1756
+ } else {
1757
+ this.logger.info(`Considering key bundle for recently-joined room ${roomId}`);
1758
+ // Don't block for the import to happen, here, as this is called from inside the `/sync` loop.
1759
+ this.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString()).catch((err) => {
1760
+ this.logger.error(`Error attempting to download key bundle for room ${roomId}`);
1761
+ this.logger.error(err);
1762
+ });
1763
+ }
1757
1764
  }
1758
1765
 
1759
1766
  break;
@@ -1926,7 +1933,21 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
1926
1933
  * @param oldMembership - The previous membership state. Null if it's a new member.
1927
1934
  */
1928
1935
  public onRoomMembership(event: MatrixEvent, member: RoomMember, oldMembership?: string): void {
1929
- const enc = this.roomEncryptors[event.getRoomId()!];
1936
+ const roomId = event.getRoomId()!;
1937
+
1938
+ // If it's our own membership, and we are no longer joined, clear any indication that we are waiting for a key
1939
+ // bundle.
1940
+ if (
1941
+ oldMembership === KnownMembership.Join &&
1942
+ member.membership !== KnownMembership.Join &&
1943
+ member.userId === this.olmMachine.userId.toString()
1944
+ ) {
1945
+ this.olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId)).catch((e) => {
1946
+ this.logger.error(`Error clearing room pending key bundle indicator for ${roomId}: ${e}`);
1947
+ });
1948
+ }
1949
+
1950
+ const enc = this.roomEncryptors[roomId];
1930
1951
  if (!enc) {
1931
1952
  // not encrypting in this room
1932
1953
  return;
@@ -2189,7 +2210,10 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
2189
2210
  * Used during migration from legacy js-crypto to update local trust if needed.
2190
2211
  */
2191
2212
  public async getOwnIdentity(): Promise<RustSdkCryptoJs.OwnUserIdentity | undefined> {
2192
- return await this.olmMachine.getIdentity(new RustSdkCryptoJs.UserId(this.userId));
2213
+ const identity = (await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(this.userId))) as
2214
+ | RustSdkCryptoJs.OwnUserIdentity
2215
+ | undefined;
2216
+ return identity;
2193
2217
  }
2194
2218
  }
2195
2219