matrix-js-sdk 41.8.0 → 41.9.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.
- package/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/lib/@types/AESEncryptedSecretStoragePayload.js +3 -1
- package/lib/@types/IIdentityServerProvider.js +3 -1
- package/lib/@types/PushRules.js +7 -7
- package/lib/@types/PushRules.js.map +1 -1
- package/lib/@types/auth.js +4 -4
- package/lib/@types/auth.js.map +1 -1
- package/lib/@types/beacon.js +2 -2
- package/lib/@types/beacon.js.map +1 -1
- package/lib/@types/common.js +3 -1
- package/lib/@types/crypto.js +3 -1
- package/lib/@types/event.js +20 -20
- package/lib/@types/event.js.map +1 -1
- package/lib/@types/events.js +3 -1
- package/lib/@types/extensible_events.js +6 -6
- package/lib/@types/extensible_events.js.map +1 -1
- package/lib/@types/global.d.js.map +1 -1
- package/lib/@types/json.js +3 -1
- package/lib/@types/local_notifications.js +3 -1
- package/lib/@types/location.js +4 -4
- package/lib/@types/location.js.map +1 -1
- package/lib/@types/matrix-sdk-crypto-wasm.d.js +3 -1
- package/lib/@types/media.js +3 -1
- package/lib/@types/membership.js +1 -1
- package/lib/@types/membership.js.map +1 -1
- package/lib/@types/partials.js +6 -6
- package/lib/@types/partials.js.map +1 -1
- package/lib/@types/polls.js +5 -5
- package/lib/@types/polls.js.map +1 -1
- package/lib/@types/read_receipts.js +2 -2
- package/lib/@types/read_receipts.js.map +1 -1
- package/lib/@types/registration.js +3 -1
- package/lib/@types/requests.js +1 -1
- package/lib/@types/requests.js.map +1 -1
- package/lib/@types/retention.js +1 -1
- package/lib/@types/retention.js.map +1 -1
- package/lib/@types/search.js +1 -1
- package/lib/@types/search.js.map +1 -1
- package/lib/@types/signed.js +3 -1
- package/lib/@types/spaces.js +3 -1
- package/lib/@types/state_events.js +3 -1
- package/lib/@types/synapse.js +3 -1
- package/lib/@types/sync.js +1 -1
- package/lib/@types/sync.js.map +1 -1
- package/lib/@types/threepids.js +1 -1
- package/lib/@types/threepids.js.map +1 -1
- package/lib/@types/topic.js +1 -1
- package/lib/@types/topic.js.map +1 -1
- package/lib/@types/uia.js +3 -1
- package/lib/NamespacedValue.js +8 -8
- package/lib/NamespacedValue.js.map +1 -1
- package/lib/ReEmitter.js +9 -16
- package/lib/ReEmitter.js.map +1 -1
- package/lib/ToDeviceMessageQueue.js +49 -57
- package/lib/ToDeviceMessageQueue.js.map +1 -1
- package/lib/autodiscovery.js +232 -247
- package/lib/autodiscovery.js.map +1 -1
- package/lib/base64.js +1 -1
- package/lib/base64.js.map +1 -1
- package/lib/browser-index.d.ts +2 -2
- package/lib/browser-index.d.ts.map +1 -1
- package/lib/browser-index.js +5 -5
- package/lib/browser-index.js.map +1 -1
- package/lib/capabilityPoller.js +19 -22
- package/lib/capabilityPoller.js.map +1 -1
- package/lib/client.d.ts +2 -8
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +2043 -2487
- package/lib/client.js.map +1 -1
- package/lib/common-crypto/CryptoBackend.js +2 -2
- package/lib/common-crypto/CryptoBackend.js.map +1 -1
- package/lib/common-crypto/key-passphrase.js.map +1 -1
- package/lib/content-helpers.js +43 -48
- package/lib/content-helpers.js.map +1 -1
- package/lib/content-repo.js +14 -24
- package/lib/content-repo.js.map +1 -1
- package/lib/crypto/store/base.js +6 -6
- package/lib/crypto/store/base.js.map +1 -1
- package/lib/crypto/store/indexeddb-crypto-store-backend.js +190 -238
- package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +1 -1
- package/lib/crypto/store/indexeddb-crypto-store.js +25 -30
- package/lib/crypto/store/indexeddb-crypto-store.js.map +1 -1
- package/lib/crypto/store/localStorage-crypto-store.js +113 -145
- package/lib/crypto/store/localStorage-crypto-store.js.map +1 -1
- package/lib/crypto/store/memory-crypto-store.js +74 -105
- package/lib/crypto/store/memory-crypto-store.js.map +1 -1
- package/lib/crypto-api/CryptoEvent.js +1 -1
- package/lib/crypto-api/CryptoEvent.js.map +1 -1
- package/lib/crypto-api/CryptoEventHandlerMap.js +3 -1
- package/lib/crypto-api/index.d.ts +15 -6
- package/lib/crypto-api/index.d.ts.map +1 -1
- package/lib/crypto-api/index.js +22 -22
- package/lib/crypto-api/index.js.map +1 -1
- package/lib/crypto-api/key-passphrase.js +15 -23
- package/lib/crypto-api/key-passphrase.js.map +1 -1
- package/lib/crypto-api/keybackup.js +3 -1
- package/lib/crypto-api/recovery-key.js +11 -12
- package/lib/crypto-api/recovery-key.js.map +1 -1
- package/lib/crypto-api/verification.js +3 -3
- package/lib/crypto-api/verification.js.map +1 -1
- package/lib/digest.js +7 -14
- package/lib/digest.js.map +1 -1
- package/lib/embedded.d.ts.map +1 -1
- package/lib/embedded.js +380 -505
- package/lib/embedded.js.map +1 -1
- package/lib/errors.js +2 -2
- package/lib/errors.js.map +1 -1
- package/lib/event-mapper.js +10 -12
- package/lib/event-mapper.js.map +1 -1
- package/lib/extensible_events_v1/ExtensibleEvent.js.map +1 -1
- package/lib/extensible_events_v1/InvalidEventError.js.map +1 -1
- package/lib/extensible_events_v1/MessageEvent.js +11 -13
- package/lib/extensible_events_v1/MessageEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollEndEvent.js +3 -4
- package/lib/extensible_events_v1/PollEndEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollResponseEvent.js +5 -6
- package/lib/extensible_events_v1/PollResponseEvent.js.map +1 -1
- package/lib/extensible_events_v1/PollStartEvent.js +8 -10
- package/lib/extensible_events_v1/PollStartEvent.js.map +1 -1
- package/lib/extensible_events_v1/utilities.js.map +1 -1
- package/lib/feature.js +18 -31
- package/lib/feature.js.map +1 -1
- package/lib/filter-component.d.ts.map +1 -1
- package/lib/filter-component.js +20 -26
- package/lib/filter-component.js.map +1 -1
- package/lib/filter.js +14 -17
- package/lib/filter.js.map +1 -1
- package/lib/http-api/errors.js +28 -43
- package/lib/http-api/errors.js.map +1 -1
- package/lib/http-api/fetch.js +141 -159
- package/lib/http-api/fetch.js.map +1 -1
- package/lib/http-api/index.js +17 -20
- package/lib/http-api/index.js.map +1 -1
- package/lib/http-api/interface.js +1 -1
- package/lib/http-api/interface.js.map +1 -1
- package/lib/http-api/method.js +1 -1
- package/lib/http-api/method.js.map +1 -1
- package/lib/http-api/prefix.js +3 -3
- package/lib/http-api/prefix.js.map +1 -1
- package/lib/http-api/refresh.js +74 -94
- package/lib/http-api/refresh.js.map +1 -1
- package/lib/http-api/utils.d.ts +1 -1
- package/lib/http-api/utils.d.ts.map +1 -1
- package/lib/http-api/utils.js +34 -42
- package/lib/http-api/utils.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/indexeddb-helpers.js +3 -3
- package/lib/indexeddb-helpers.js.map +1 -1
- package/lib/indexeddb-worker.js.map +1 -1
- package/lib/interactive-auth.d.ts +5 -5
- package/lib/interactive-auth.d.ts.map +1 -1
- package/lib/interactive-auth.js +172 -204
- package/lib/interactive-auth.js.map +1 -1
- package/lib/logger.js +21 -57
- package/lib/logger.js.map +1 -1
- package/lib/matrix.js +7 -11
- package/lib/matrix.js.map +1 -1
- package/lib/matrixrtc/CallMembership.js +80 -74
- package/lib/matrixrtc/CallMembership.js.map +1 -1
- package/lib/matrixrtc/EncryptionManager.js +1 -1
- package/lib/matrixrtc/EncryptionManager.js.map +1 -1
- package/lib/matrixrtc/IKeyTransport.js +1 -1
- package/lib/matrixrtc/IKeyTransport.js.map +1 -1
- package/lib/matrixrtc/IMembershipManager.js +1 -1
- package/lib/matrixrtc/IMembershipManager.js.map +1 -1
- package/lib/matrixrtc/LivekitTransport.d.ts +1 -1
- package/lib/matrixrtc/LivekitTransport.js +4 -4
- package/lib/matrixrtc/LivekitTransport.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.js +137 -187
- package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.js +36 -41
- package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManager.js +332 -378
- package/lib/matrixrtc/MembershipManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManagerActionScheduler.js +54 -63
- package/lib/matrixrtc/MembershipManagerActionScheduler.js.map +1 -1
- package/lib/matrixrtc/RTCEncryptionManager.js +119 -143
- package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
- package/lib/matrixrtc/ToDeviceKeyTransport.js +53 -58
- package/lib/matrixrtc/ToDeviceKeyTransport.js.map +1 -1
- package/lib/matrixrtc/index.js.map +1 -1
- package/lib/matrixrtc/membershipData/common.js +1 -1
- package/lib/matrixrtc/membershipData/common.js.map +1 -1
- package/lib/matrixrtc/membershipData/index.js.map +1 -1
- package/lib/matrixrtc/membershipData/rtc.js +15 -23
- package/lib/matrixrtc/membershipData/rtc.js.map +1 -1
- package/lib/matrixrtc/membershipData/session.js +4 -5
- package/lib/matrixrtc/membershipData/session.js.map +1 -1
- package/lib/matrixrtc/types.js +4 -6
- package/lib/matrixrtc/types.js.map +1 -1
- package/lib/matrixrtc/utils.js +4 -11
- package/lib/matrixrtc/utils.js.map +1 -1
- package/lib/models/MSC3089Branch.js +91 -116
- package/lib/models/MSC3089Branch.js.map +1 -1
- package/lib/models/MSC3089TreeSpace.js +209 -245
- package/lib/models/MSC3089TreeSpace.js.map +1 -1
- package/lib/models/ToDeviceMessage.js +3 -1
- package/lib/models/beacon.js +14 -13
- package/lib/models/beacon.js.map +1 -1
- package/lib/models/compare-event-ordering.js +13 -13
- package/lib/models/compare-event-ordering.js.map +1 -1
- package/lib/models/device.js +3 -3
- package/lib/models/device.js.map +1 -1
- package/lib/models/event-context.js +5 -8
- package/lib/models/event-context.js.map +1 -1
- package/lib/models/event-status.js +1 -1
- package/lib/models/event-status.js.map +1 -1
- package/lib/models/event-timeline-set.js +88 -94
- package/lib/models/event-timeline-set.js.map +1 -1
- package/lib/models/event-timeline.js +28 -30
- package/lib/models/event-timeline.js.map +1 -1
- package/lib/models/event.js +182 -226
- package/lib/models/event.js.map +1 -1
- package/lib/models/invites-ignorer-types.js +4 -4
- package/lib/models/invites-ignorer-types.js.map +1 -1
- package/lib/models/invites-ignorer.js +159 -179
- package/lib/models/invites-ignorer.js.map +1 -1
- package/lib/models/poll.js +71 -81
- package/lib/models/poll.js.map +1 -1
- package/lib/models/profile-keys.js +2 -2
- package/lib/models/profile-keys.js.map +1 -1
- package/lib/models/read-receipt.js +40 -56
- package/lib/models/read-receipt.js.map +1 -1
- package/lib/models/related-relations.js.map +1 -1
- package/lib/models/relations-container.js +22 -23
- package/lib/models/relations-container.js.map +1 -1
- package/lib/models/relations.js +133 -159
- package/lib/models/relations.js.map +1 -1
- package/lib/models/room-member.js +24 -29
- package/lib/models/room-member.js.map +1 -1
- package/lib/models/room-receipts.js +29 -43
- package/lib/models/room-receipts.js.map +1 -1
- package/lib/models/room-retention.js +74 -83
- package/lib/models/room-retention.js.map +1 -1
- package/lib/models/room-state.js +143 -172
- package/lib/models/room-state.js.map +1 -1
- package/lib/models/room-sticky-events.d.ts +1 -0
- package/lib/models/room-sticky-events.d.ts.map +1 -1
- package/lib/models/room-sticky-events.js +45 -65
- package/lib/models/room-sticky-events.js.map +1 -1
- package/lib/models/room-summary.js.map +1 -1
- package/lib/models/room.d.ts.map +1 -1
- package/lib/models/room.js +765 -909
- package/lib/models/room.js.map +1 -1
- package/lib/models/search-result.js +5 -5
- package/lib/models/search-result.js.map +1 -1
- package/lib/models/thread.js +225 -285
- package/lib/models/thread.js.map +1 -1
- package/lib/models/typed-event-emitter.js +7 -18
- package/lib/models/typed-event-emitter.js.map +1 -1
- package/lib/models/user.js +8 -8
- package/lib/models/user.js.map +1 -1
- package/lib/oidc/authorize.js +208 -241
- package/lib/oidc/authorize.js.map +1 -1
- package/lib/oidc/discovery.js +23 -36
- package/lib/oidc/discovery.js.map +1 -1
- package/lib/oidc/error.js +1 -1
- package/lib/oidc/error.js.map +1 -1
- package/lib/oidc/index.js +1 -0
- package/lib/oidc/index.js.map +1 -1
- package/lib/oidc/register.js +60 -66
- package/lib/oidc/register.js.map +1 -1
- package/lib/oidc/tokenRefresher.js +77 -91
- package/lib/oidc/tokenRefresher.js.map +1 -1
- package/lib/oidc/validate.js +18 -18
- package/lib/oidc/validate.js.map +1 -1
- package/lib/pushprocessor.js +71 -82
- package/lib/pushprocessor.js.map +1 -1
- package/lib/randomstring.js +9 -9
- package/lib/randomstring.js.map +1 -1
- package/lib/realtime-callbacks.js +25 -28
- package/lib/realtime-callbacks.js.map +1 -1
- package/lib/receipt-accumulator.d.ts +2 -0
- package/lib/receipt-accumulator.d.ts.map +1 -1
- package/lib/receipt-accumulator.js +14 -22
- package/lib/receipt-accumulator.js.map +1 -1
- package/lib/rendezvous/MSC4108SignInWithQR.js +271 -302
- package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -1
- package/lib/rendezvous/RendezvousChannel.js +3 -1
- package/lib/rendezvous/RendezvousCode.js +3 -1
- package/lib/rendezvous/RendezvousError.js.map +1 -1
- package/lib/rendezvous/RendezvousFailureReason.js +2 -2
- package/lib/rendezvous/RendezvousFailureReason.js.map +1 -1
- package/lib/rendezvous/RendezvousIntent.js +1 -1
- package/lib/rendezvous/RendezvousIntent.js.map +1 -1
- package/lib/rendezvous/RendezvousTransport.js +3 -1
- package/lib/rendezvous/channels/MSC4108SecureChannel.js +123 -147
- package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -1
- package/lib/rendezvous/index.js +44 -69
- package/lib/rendezvous/index.js.map +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts +1 -1
- package/lib/rendezvous/transports/MSC4108RendezvousSession.js +134 -152
- package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +1 -1
- package/lib/retentionPolicy.js +6 -9
- package/lib/retentionPolicy.js.map +1 -1
- package/lib/room-hierarchy.js +52 -60
- package/lib/room-hierarchy.js.map +1 -1
- package/lib/rust-crypto/CrossSigningIdentity.js +94 -104
- package/lib/rust-crypto/CrossSigningIdentity.js.map +1 -1
- package/lib/rust-crypto/DehydratedDeviceManager.js +157 -190
- package/lib/rust-crypto/DehydratedDeviceManager.js.map +1 -1
- package/lib/rust-crypto/KeyClaimManager.js +18 -22
- package/lib/rust-crypto/KeyClaimManager.js.map +1 -1
- package/lib/rust-crypto/OutgoingRequestProcessor.js +109 -139
- package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -1
- package/lib/rust-crypto/OutgoingRequestsManager.js +66 -80
- package/lib/rust-crypto/OutgoingRequestsManager.js.map +1 -1
- package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts +1 -1
- package/lib/rust-crypto/PerSessionKeyBackupDownloader.js +196 -224
- package/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map +1 -1
- package/lib/rust-crypto/RoomEncryptor.js +127 -143
- package/lib/rust-crypto/RoomEncryptor.js.map +1 -1
- package/lib/rust-crypto/backup.js +453 -549
- package/lib/rust-crypto/backup.js.map +1 -1
- package/lib/rust-crypto/constants.js +1 -1
- package/lib/rust-crypto/constants.js.map +1 -1
- package/lib/rust-crypto/device-converter.js +15 -28
- package/lib/rust-crypto/device-converter.js.map +1 -1
- package/lib/rust-crypto/index.js +103 -116
- package/lib/rust-crypto/index.js.map +1 -1
- package/lib/rust-crypto/libolm_migration.js +303 -365
- package/lib/rust-crypto/libolm_migration.js.map +1 -1
- package/lib/rust-crypto/rust-crypto.d.ts +5 -1
- package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
- package/lib/rust-crypto/rust-crypto.js +1067 -1332
- package/lib/rust-crypto/rust-crypto.js.map +1 -1
- package/lib/rust-crypto/secret-storage.js +12 -25
- package/lib/rust-crypto/secret-storage.js.map +1 -1
- package/lib/rust-crypto/verification.js +137 -189
- package/lib/rust-crypto/verification.js.map +1 -1
- package/lib/scheduler.js +44 -19
- package/lib/scheduler.js.map +1 -1
- package/lib/secret-storage.js +179 -213
- package/lib/secret-storage.js.map +1 -1
- package/lib/serverCapabilities.js +6 -9
- package/lib/serverCapabilities.js.map +1 -1
- package/lib/service-types.js +1 -1
- package/lib/service-types.js.map +1 -1
- package/lib/sliding-sync-sdk.d.ts.map +1 -1
- package/lib/sliding-sync-sdk.js +368 -424
- package/lib/sliding-sync-sdk.js.map +1 -1
- package/lib/sliding-sync.js +135 -171
- package/lib/sliding-sync.js.map +1 -1
- package/lib/store/index.js +3 -1
- package/lib/store/indexeddb-backend.js +3 -1
- package/lib/store/indexeddb-local-backend.js +194 -253
- package/lib/store/indexeddb-local-backend.js.map +1 -1
- package/lib/store/indexeddb-remote-backend.js +33 -63
- package/lib/store/indexeddb-remote-backend.js.map +1 -1
- package/lib/store/indexeddb-store-worker.js +22 -23
- package/lib/store/indexeddb-store-worker.js.map +1 -1
- package/lib/store/indexeddb.js +44 -71
- package/lib/store/indexeddb.js.map +1 -1
- package/lib/store/local-storage-events-emitter.js +2 -2
- package/lib/store/local-storage-events-emitter.js.map +1 -1
- package/lib/store/memory.js +34 -57
- package/lib/store/memory.js.map +1 -1
- package/lib/store/stub.js +22 -35
- package/lib/store/stub.js.map +1 -1
- package/lib/sync-accumulator.js +54 -66
- package/lib/sync-accumulator.js.map +1 -1
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +905 -998
- package/lib/sync.js.map +1 -1
- package/lib/testing.js +63 -105
- package/lib/testing.js.map +1 -1
- package/lib/thread-utils.js +1 -4
- package/lib/thread-utils.js.map +1 -1
- package/lib/timeline-window.js +89 -102
- package/lib/timeline-window.js.map +1 -1
- package/lib/types.js +1 -1
- package/lib/types.js.map +1 -1
- package/lib/utils/decryptAESSecretStorageItem.js +14 -25
- package/lib/utils/decryptAESSecretStorageItem.js.map +1 -1
- package/lib/utils/encryptAESSecretStorageItem.js +27 -38
- package/lib/utils/encryptAESSecretStorageItem.js.map +1 -1
- package/lib/utils/internal/deriveKeys.js +25 -32
- package/lib/utils/internal/deriveKeys.js.map +1 -1
- package/lib/utils/roomVersion.js +1 -1
- package/lib/utils/roomVersion.js.map +1 -1
- package/lib/utils.js +83 -135
- package/lib/utils.js.map +1 -1
- package/lib/version-support.js +3 -3
- package/lib/version-support.js.map +1 -1
- package/lib/webrtc/audioContext.js +5 -6
- package/lib/webrtc/audioContext.js.map +1 -1
- package/lib/webrtc/call.js +1081 -1250
- package/lib/webrtc/call.js.map +1 -1
- package/lib/webrtc/callEventHandler.js +202 -214
- package/lib/webrtc/callEventHandler.js.map +1 -1
- package/lib/webrtc/callEventTypes.js +2 -2
- package/lib/webrtc/callEventTypes.js.map +1 -1
- package/lib/webrtc/callFeed.js +18 -20
- package/lib/webrtc/callFeed.js.map +1 -1
- package/lib/webrtc/groupCall.js +513 -602
- package/lib/webrtc/groupCall.js.map +1 -1
- package/lib/webrtc/groupCallEventHandler.js +59 -62
- package/lib/webrtc/groupCallEventHandler.js.map +1 -1
- package/lib/webrtc/mediaHandler.js +198 -232
- package/lib/webrtc/mediaHandler.js.map +1 -1
- package/lib/webrtc/stats/callFeedStatsReporter.js +16 -20
- package/lib/webrtc/stats/callFeedStatsReporter.js.map +1 -1
- package/lib/webrtc/stats/callStatsReportGatherer.js +67 -75
- package/lib/webrtc/stats/callStatsReportGatherer.js.map +1 -1
- package/lib/webrtc/stats/callStatsReportSummary.js +3 -1
- package/lib/webrtc/stats/connectionStats.js.map +1 -1
- package/lib/webrtc/stats/connectionStatsBuilder.js +2 -2
- package/lib/webrtc/stats/connectionStatsBuilder.js.map +1 -1
- package/lib/webrtc/stats/connectionStatsReportBuilder.js +20 -24
- package/lib/webrtc/stats/connectionStatsReportBuilder.js.map +1 -1
- package/lib/webrtc/stats/groupCallStats.js +6 -8
- package/lib/webrtc/stats/groupCallStats.js.map +1 -1
- package/lib/webrtc/stats/media/mediaSsrcHandler.js +7 -8
- package/lib/webrtc/stats/media/mediaSsrcHandler.js.map +1 -1
- package/lib/webrtc/stats/media/mediaTrackHandler.js +7 -9
- package/lib/webrtc/stats/media/mediaTrackHandler.js.map +1 -1
- package/lib/webrtc/stats/media/mediaTrackStats.js +3 -3
- package/lib/webrtc/stats/media/mediaTrackStats.js.map +1 -1
- package/lib/webrtc/stats/media/mediaTrackStatsHandler.js +7 -7
- package/lib/webrtc/stats/media/mediaTrackStatsHandler.js.map +1 -1
- package/lib/webrtc/stats/statsReport.js +1 -1
- package/lib/webrtc/stats/statsReport.js.map +1 -1
- package/lib/webrtc/stats/statsReportEmitter.js.map +1 -1
- package/lib/webrtc/stats/summaryStatsReportGatherer.js +14 -14
- package/lib/webrtc/stats/summaryStatsReportGatherer.js.map +1 -1
- package/lib/webrtc/stats/trackStatsBuilder.js +32 -34
- package/lib/webrtc/stats/trackStatsBuilder.js.map +1 -1
- package/lib/webrtc/stats/transportStats.js +3 -1
- package/lib/webrtc/stats/transportStatsBuilder.js +9 -9
- package/lib/webrtc/stats/transportStatsBuilder.js.map +1 -1
- package/lib/webrtc/stats/valueFormatter.js +1 -1
- package/lib/webrtc/stats/valueFormatter.js.map +1 -1
- package/package.json +18 -33
- package/src/@types/global.d.ts +2 -2
- package/src/ToDeviceMessageQueue.ts +2 -2
- package/src/browser-index.ts +4 -4
- package/src/client.ts +15 -20
- package/src/crypto-api/index.ts +17 -6
- package/src/embedded.ts +4 -6
- package/src/filter-component.ts +1 -0
- package/src/http-api/utils.ts +1 -1
- package/src/interactive-auth.ts +11 -5
- package/src/matrixrtc/LivekitTransport.ts +1 -1
- package/src/matrixrtc/MatrixRTCSession.ts +1 -1
- package/src/models/MSC3089Branch.ts +1 -1
- package/src/models/room-sticky-events.ts +1 -0
- package/src/models/room.ts +9 -11
- package/src/receipt-accumulator.ts +2 -0
- package/src/rendezvous/index.ts +1 -1
- package/src/rendezvous/transports/MSC4108RendezvousSession.ts +2 -2
- package/src/rust-crypto/PerSessionKeyBackupDownloader.ts +1 -1
- package/src/rust-crypto/index.ts +6 -6
- package/src/rust-crypto/rust-crypto.ts +31 -7
- package/src/rust-crypto/verification.ts +1 -1
- package/src/sliding-sync-sdk.ts +5 -7
- package/src/store/indexeddb-local-backend.ts +3 -3
- package/src/sync.ts +6 -8
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
|
3
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
3
|
/*
|
|
7
4
|
Copyright 2022-2023 The Matrix.org Foundation C.I.C.
|
|
8
5
|
|
|
@@ -49,9 +46,9 @@ import { DehydratedDeviceManager } from "./DehydratedDeviceManager.js";
|
|
|
49
46
|
import { VerificationMethod } from "../types.js";
|
|
50
47
|
import { keyFromAuthData } from "../common-crypto/key-passphrase.js";
|
|
51
48
|
import { getHttpUriForMxc } from "../content-repo.js";
|
|
52
|
-
|
|
49
|
+
const ALL_VERIFICATION_METHODS = [VerificationMethod.Sas, VerificationMethod.ScanQrCode, VerificationMethod.ShowQrCode, VerificationMethod.Reciprocate];
|
|
53
50
|
/** The maximum time, in milliseconds, since we accepted an invite, that we should accept a key bundle. */
|
|
54
|
-
export
|
|
51
|
+
export const MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE = 24 * 60 * 60 * 1000; // 24 hours
|
|
55
52
|
|
|
56
53
|
/**
|
|
57
54
|
* An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
|
|
@@ -70,16 +67,9 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
70
67
|
userId, /** The local user's Device ID. */
|
|
71
68
|
_deviceId, /** Interface to server-side secret storage */
|
|
72
69
|
secretStorage, /** Crypto callbacks provided by the application */
|
|
73
|
-
cryptoCallbacks
|
|
74
|
-
|
|
70
|
+
cryptoCallbacks, /** Enable support for encrypted state events under MSC4362. */
|
|
71
|
+
enableEncryptedStateEvents = false) {
|
|
75
72
|
super();
|
|
76
|
-
this.logger = logger;
|
|
77
|
-
this.olmMachine = olmMachine;
|
|
78
|
-
this.http = http;
|
|
79
|
-
this.userId = userId;
|
|
80
|
-
this.secretStorage = secretStorage;
|
|
81
|
-
this.cryptoCallbacks = cryptoCallbacks;
|
|
82
|
-
this.enableEncryptedStateEvents = enableEncryptedStateEvents;
|
|
83
73
|
/**
|
|
84
74
|
* The number of iterations to use when deriving a recovery key from a passphrase.
|
|
85
75
|
*/
|
|
@@ -109,6 +99,13 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
109
99
|
* The verification methods we offer to the other side during an interactive verification.
|
|
110
100
|
*/
|
|
111
101
|
_defineProperty(this, "_supportedVerificationMethods", ALL_VERIFICATION_METHODS);
|
|
102
|
+
this.logger = logger;
|
|
103
|
+
this.olmMachine = olmMachine;
|
|
104
|
+
this.http = http;
|
|
105
|
+
this.userId = userId;
|
|
106
|
+
this.secretStorage = secretStorage;
|
|
107
|
+
this.cryptoCallbacks = cryptoCallbacks;
|
|
108
|
+
this.enableEncryptedStateEvents = enableEncryptedStateEvents;
|
|
112
109
|
this.outgoingRequestProcessor = new OutgoingRequestProcessor(logger, olmMachine, http);
|
|
113
110
|
this.outgoingRequestsManager = new OutgoingRequestsManager(this.logger, olmMachine, this.outgoingRequestProcessor);
|
|
114
111
|
this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
|
|
@@ -171,133 +168,115 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
171
168
|
// can then be deleted.
|
|
172
169
|
this.olmMachine.close();
|
|
173
170
|
}
|
|
174
|
-
encryptEvent(event, _room) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// once we drop support for the libolm crypto implementation, we can stop passing to-device messages
|
|
194
|
-
// through decryptEvent and hence get rid of this case.
|
|
195
|
-
throw new Error("to-device event was not decrypted in preprocessToDeviceMessages");
|
|
196
|
-
}
|
|
197
|
-
return yield _this2.eventDecryptor.attemptEventDecryption(event, _this2.deviceIsolationMode);
|
|
198
|
-
})();
|
|
171
|
+
async encryptEvent(event, _room) {
|
|
172
|
+
const roomId = event.getRoomId();
|
|
173
|
+
const encryptor = this.roomEncryptors[roomId];
|
|
174
|
+
if (!encryptor) {
|
|
175
|
+
throw new Error(`Cannot encrypt event in unconfigured room ${roomId}`);
|
|
176
|
+
}
|
|
177
|
+
await encryptor.encryptEvent(event, this.globalBlacklistUnverifiedDevices, this.deviceIsolationMode);
|
|
178
|
+
}
|
|
179
|
+
async decryptEvent(event) {
|
|
180
|
+
const roomId = event.getRoomId();
|
|
181
|
+
if (!roomId) {
|
|
182
|
+
// presumably, a to-device message. These are normally decrypted in preprocessToDeviceMessages
|
|
183
|
+
// so the fact it has come back here suggests that decryption failed.
|
|
184
|
+
//
|
|
185
|
+
// once we drop support for the libolm crypto implementation, we can stop passing to-device messages
|
|
186
|
+
// through decryptEvent and hence get rid of this case.
|
|
187
|
+
throw new Error("to-device event was not decrypted in preprocessToDeviceMessages");
|
|
188
|
+
}
|
|
189
|
+
return await this.eventDecryptor.attemptEventDecryption(event, this.deviceIsolationMode);
|
|
199
190
|
}
|
|
200
191
|
|
|
201
192
|
/**
|
|
202
193
|
* Implementation of {@link CryptoBackend#getBackupDecryptor}.
|
|
203
194
|
*/
|
|
204
|
-
getBackupDecryptor(backupInfo, privKey) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
return _this3.backupManager.createBackupDecryptor(backupDecryptionKey);
|
|
218
|
-
})();
|
|
195
|
+
async getBackupDecryptor(backupInfo, privKey) {
|
|
196
|
+
if (!(privKey instanceof Uint8Array)) {
|
|
197
|
+
throw new Error(`getBackupDecryptor: expects Uint8Array`);
|
|
198
|
+
}
|
|
199
|
+
if (backupInfo.algorithm != "m.megolm_backup.v1.curve25519-aes-sha2") {
|
|
200
|
+
throw new Error(`getBackupDecryptor: Unsupported algorithm ${backupInfo.algorithm}`);
|
|
201
|
+
}
|
|
202
|
+
const backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(encodeBase64(privKey));
|
|
203
|
+
if (!decryptionKeyMatchesKeyBackupInfo(backupDecryptionKey, backupInfo)) {
|
|
204
|
+
throw new Error(`getBackupDecryptor: key backup on server does not match the decryption key`);
|
|
205
|
+
}
|
|
206
|
+
return this.backupManager.createBackupDecryptor(backupDecryptionKey);
|
|
219
207
|
}
|
|
220
208
|
|
|
221
209
|
/**
|
|
222
210
|
* Implementation of {@link CryptoBackend#importBackedUpRoomKeys}.
|
|
223
211
|
*/
|
|
224
|
-
importBackedUpRoomKeys(keys, backupVersion, opts) {
|
|
225
|
-
|
|
226
|
-
return _asyncToGenerator(function* () {
|
|
227
|
-
return yield _this4.backupManager.importBackedUpRoomKeys(keys, backupVersion, opts);
|
|
228
|
-
})();
|
|
212
|
+
async importBackedUpRoomKeys(keys, backupVersion, opts) {
|
|
213
|
+
return await this.backupManager.importBackedUpRoomKeys(keys, backupVersion, opts);
|
|
229
214
|
}
|
|
230
215
|
|
|
231
216
|
/**
|
|
232
217
|
* Implementation of {@link CryptoBackend.maybeAcceptKeyBundle}.
|
|
233
218
|
*/
|
|
234
|
-
maybeAcceptKeyBundle(roomId, inviter) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// TODO: retry this if it gets interrupted or it fails. (https://github.com/matrix-org/matrix-rust-sdk/issues/5112)
|
|
238
|
-
// TODO: do this in the background.
|
|
219
|
+
async maybeAcceptKeyBundle(roomId, inviter) {
|
|
220
|
+
// TODO: retry this if it gets interrupted or it fails. (https://github.com/matrix-org/matrix-rust-sdk/issues/5112)
|
|
221
|
+
// TODO: do this in the background.
|
|
239
222
|
|
|
240
|
-
|
|
223
|
+
const logger = new LogSpan(this.logger, `maybeAcceptKeyBundle(${roomId}, ${inviter})`);
|
|
241
224
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
})();
|
|
225
|
+
// Make sure we have an up-to-date idea of the inviter's cross-signing keys, so that we can check if the
|
|
226
|
+
// device that sent us the bundle data was correctly cross-signed.
|
|
227
|
+
//
|
|
228
|
+
// TODO: it would be nice to skip this step if we have an up-to-date copy of the inviter's cross-signing keys,
|
|
229
|
+
// but we don't have an easy way to check that. Possibly the rust side could trigger a key request and then
|
|
230
|
+
// block until it happens.
|
|
231
|
+
logger.info(`Checking inviter cross-signing keys`);
|
|
232
|
+
const request = this.olmMachine.queryKeysForUsers([new RustSdkCryptoJs.UserId(inviter)]);
|
|
233
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
|
234
|
+
const bundleData = await this.olmMachine.getReceivedRoomKeyBundleData(new RustSdkCryptoJs.RoomId(roomId), new RustSdkCryptoJs.UserId(inviter));
|
|
235
|
+
if (!bundleData) {
|
|
236
|
+
logger.info("No key bundle found for user");
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
logger.info(`Fetching key bundle ${bundleData.url}`);
|
|
240
|
+
const url = getHttpUriForMxc(this.http.opts.baseUrl, bundleData.url, undefined, undefined, undefined, /* allowDirectLinks */false, /* allowRedirects */true, /* useAuthentication */true);
|
|
241
|
+
let encryptedBundle;
|
|
242
|
+
try {
|
|
243
|
+
const bundleUrl = new URL(url);
|
|
244
|
+
const encryptedBundleBlob = await this.http.authedRequest(Method.Get, bundleUrl.pathname + bundleUrl.search, {}, undefined, {
|
|
245
|
+
rawResponseBody: true,
|
|
246
|
+
prefix: ""
|
|
247
|
+
});
|
|
248
|
+
logger.info(`Received blob of length ${encryptedBundleBlob.size}`);
|
|
249
|
+
encryptedBundle = new Uint8Array(await encryptedBundleBlob.arrayBuffer());
|
|
250
|
+
} catch (err) {
|
|
251
|
+
logger.warn(`Error downloading encrypted bundle from ${url}:`, err);
|
|
252
|
+
throw err;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
await this.olmMachine.receiveRoomKeyBundle(bundleData, encryptedBundle);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
logger.warn(`Error receiving encrypted bundle:`, err);
|
|
258
|
+
throw err;
|
|
259
|
+
} finally {
|
|
260
|
+
// Even if we were unable to import the bundle, we still clear the flag that indicates that we
|
|
261
|
+
// are waiting for the bundle to be received. The only reason this can happen is that the bundle was
|
|
262
|
+
// malformed somehow, so we don't want to keep retrying it.
|
|
263
|
+
await this.olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
284
266
|
}
|
|
285
267
|
|
|
286
268
|
/**
|
|
287
269
|
* Implementation of {@link CryptoBackend.markRoomAsPendingKeyBundle}.
|
|
288
270
|
*/
|
|
289
|
-
markRoomAsPendingKeyBundle(roomId, inviter) {
|
|
290
|
-
|
|
291
|
-
return _asyncToGenerator(function* () {
|
|
292
|
-
yield _this6.olmMachine.storeRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId), new RustSdkCryptoJs.UserId(inviter));
|
|
293
|
-
})();
|
|
271
|
+
async markRoomAsPendingKeyBundle(roomId, inviter) {
|
|
272
|
+
await this.olmMachine.storeRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId), new RustSdkCryptoJs.UserId(inviter));
|
|
294
273
|
}
|
|
295
274
|
/**
|
|
296
275
|
* Implementation of {@link CryptoApi#getVersion}.
|
|
297
276
|
*/
|
|
298
277
|
getVersion() {
|
|
299
|
-
|
|
300
|
-
return
|
|
278
|
+
const versions = RustSdkCryptoJs.getVersions();
|
|
279
|
+
return `Rust SDK ${versions.matrix_sdk_crypto} (${versions.git_sha}), Vodozemac ${versions.vodozemac}`;
|
|
301
280
|
}
|
|
302
281
|
|
|
303
282
|
/**
|
|
@@ -310,122 +289,93 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
310
289
|
/**
|
|
311
290
|
* Implementation of {@link CryptoApi#isEncryptionEnabledInRoom}.
|
|
312
291
|
*/
|
|
313
|
-
isEncryptionEnabledInRoom(roomId) {
|
|
314
|
-
|
|
315
|
-
return
|
|
316
|
-
var roomSettings = yield _this7.olmMachine.getRoomSettings(new RustSdkCryptoJs.RoomId(roomId));
|
|
317
|
-
return Boolean(roomSettings === null || roomSettings === void 0 ? void 0 : roomSettings.algorithm);
|
|
318
|
-
})();
|
|
292
|
+
async isEncryptionEnabledInRoom(roomId) {
|
|
293
|
+
const roomSettings = await this.olmMachine.getRoomSettings(new RustSdkCryptoJs.RoomId(roomId));
|
|
294
|
+
return Boolean(roomSettings?.algorithm);
|
|
319
295
|
}
|
|
320
296
|
|
|
321
297
|
/**
|
|
322
298
|
* Implementation of {@link CryptoApi#isStateEncryptionEnabledInRoom}.
|
|
323
299
|
*/
|
|
324
|
-
isStateEncryptionEnabledInRoom(roomId) {
|
|
325
|
-
|
|
326
|
-
return
|
|
327
|
-
var roomSettings = yield _this8.olmMachine.getRoomSettings(new RustSdkCryptoJs.RoomId(roomId));
|
|
328
|
-
return Boolean(roomSettings === null || roomSettings === void 0 ? void 0 : roomSettings.encryptStateEvents);
|
|
329
|
-
})();
|
|
300
|
+
async isStateEncryptionEnabledInRoom(roomId) {
|
|
301
|
+
const roomSettings = await this.olmMachine.getRoomSettings(new RustSdkCryptoJs.RoomId(roomId));
|
|
302
|
+
return Boolean(roomSettings?.encryptStateEvents);
|
|
330
303
|
}
|
|
331
304
|
|
|
332
305
|
/**
|
|
333
306
|
* Implementation of {@link CryptoApi#getOwnDeviceKeys}.
|
|
334
307
|
*/
|
|
335
|
-
getOwnDeviceKeys() {
|
|
336
|
-
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
curve25519: keys.curve25519.toBase64()
|
|
342
|
-
};
|
|
343
|
-
})();
|
|
308
|
+
async getOwnDeviceKeys() {
|
|
309
|
+
const keys = this.olmMachine.identityKeys;
|
|
310
|
+
return {
|
|
311
|
+
ed25519: keys.ed25519.toBase64(),
|
|
312
|
+
curve25519: keys.curve25519.toBase64()
|
|
313
|
+
};
|
|
344
314
|
}
|
|
345
315
|
prepareToEncrypt(room) {
|
|
346
|
-
|
|
316
|
+
const encryptor = this.roomEncryptors[room.roomId];
|
|
347
317
|
if (encryptor) {
|
|
348
318
|
encryptor.prepareForEncryption(this.globalBlacklistUnverifiedDevices, this.deviceIsolationMode);
|
|
349
319
|
}
|
|
350
320
|
}
|
|
351
321
|
forceDiscardSession(roomId) {
|
|
352
|
-
|
|
353
|
-
return (_this$roomEncryptors$ = this.roomEncryptors[roomId]) === null || _this$roomEncryptors$ === void 0 ? void 0 : _this$roomEncryptors$.forceDiscardSession();
|
|
322
|
+
return this.roomEncryptors[roomId]?.forceDiscardSession();
|
|
354
323
|
}
|
|
355
|
-
exportRoomKeys() {
|
|
356
|
-
|
|
357
|
-
return
|
|
358
|
-
var raw = yield _this0.olmMachine.exportRoomKeys(() => true);
|
|
359
|
-
return JSON.parse(raw);
|
|
360
|
-
})();
|
|
324
|
+
async exportRoomKeys() {
|
|
325
|
+
const raw = await this.olmMachine.exportRoomKeys(() => true);
|
|
326
|
+
return JSON.parse(raw);
|
|
361
327
|
}
|
|
362
|
-
exportRoomKeysAsJson() {
|
|
363
|
-
|
|
364
|
-
return _asyncToGenerator(function* () {
|
|
365
|
-
return yield _this1.olmMachine.exportRoomKeys(() => true);
|
|
366
|
-
})();
|
|
328
|
+
async exportRoomKeysAsJson() {
|
|
329
|
+
return await this.olmMachine.exportRoomKeys(() => true);
|
|
367
330
|
}
|
|
368
|
-
importRoomKeys(keys, opts) {
|
|
369
|
-
|
|
370
|
-
return _asyncToGenerator(function* () {
|
|
371
|
-
return yield _this10.backupManager.importRoomKeys(keys, opts);
|
|
372
|
-
})();
|
|
331
|
+
async importRoomKeys(keys, opts) {
|
|
332
|
+
return await this.backupManager.importRoomKeys(keys, opts);
|
|
373
333
|
}
|
|
374
|
-
importRoomKeysAsJson(keys, opts) {
|
|
375
|
-
|
|
376
|
-
return _asyncToGenerator(function* () {
|
|
377
|
-
return yield _this11.backupManager.importRoomKeysAsJson(keys, opts);
|
|
378
|
-
})();
|
|
334
|
+
async importRoomKeysAsJson(keys, opts) {
|
|
335
|
+
return await this.backupManager.importRoomKeysAsJson(keys, opts);
|
|
379
336
|
}
|
|
380
337
|
|
|
381
338
|
/**
|
|
382
339
|
* Implementation of {@link CryptoApi.userHasCrossSigningKeys}.
|
|
383
340
|
*/
|
|
384
|
-
userHasCrossSigningKeys() {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
var rustTrackedUser;
|
|
393
|
-
for (var u of rustTrackedUsers) {
|
|
394
|
-
if (userId === u.toString()) {
|
|
395
|
-
rustTrackedUser = u;
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
341
|
+
async userHasCrossSigningKeys(userId = this.userId, downloadUncached = false) {
|
|
342
|
+
// TODO: could probably do with a more efficient way of doing this than returning the whole set and searching
|
|
343
|
+
const rustTrackedUsers = await this.olmMachine.trackedUsers();
|
|
344
|
+
let rustTrackedUser;
|
|
345
|
+
for (const u of rustTrackedUsers) {
|
|
346
|
+
if (userId === u.toString()) {
|
|
347
|
+
rustTrackedUser = u;
|
|
348
|
+
break;
|
|
398
349
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
})();
|
|
350
|
+
}
|
|
351
|
+
if (rustTrackedUser !== undefined) {
|
|
352
|
+
if (userId === this.userId) {
|
|
353
|
+
/* make sure we have an *up-to-date* idea of the user's cross-signing keys. This is important, because if we
|
|
354
|
+
* return "false" here, we will end up generating new cross-signing keys and replacing the existing ones.
|
|
355
|
+
*/
|
|
356
|
+
const request = this.olmMachine.queryKeysForUsers(
|
|
357
|
+
// clone as rust layer will take ownership and it's reused later
|
|
358
|
+
[rustTrackedUser.clone()]);
|
|
359
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
|
|
360
|
+
}
|
|
361
|
+
const userIdentity = await this.olmMachine.getIdentity(rustTrackedUser);
|
|
362
|
+
userIdentity?.free();
|
|
363
|
+
return userIdentity !== undefined;
|
|
364
|
+
} else if (downloadUncached) {
|
|
365
|
+
// Download the cross signing keys and check if the master key is available
|
|
366
|
+
const keyResult = await this.downloadDeviceList(new Set([userId]));
|
|
367
|
+
const keys = keyResult.master_keys?.[userId];
|
|
368
|
+
|
|
369
|
+
// No master key
|
|
370
|
+
if (!keys) return false;
|
|
371
|
+
|
|
372
|
+
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
|
373
|
+
// We assume only a single key, and we want the bare form without type
|
|
374
|
+
// prefix, so we select the values.
|
|
375
|
+
return Boolean(Object.values(keys.keys)[0]);
|
|
376
|
+
} else {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
429
379
|
}
|
|
430
380
|
|
|
431
381
|
/**
|
|
@@ -437,98 +387,82 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
437
387
|
*
|
|
438
388
|
* @returns A map `{@link DeviceMap}`.
|
|
439
389
|
*/
|
|
440
|
-
getUserDeviceInfo(userIds) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (trackedUsers.has(_userId)) {
|
|
459
|
-
deviceMapByUserId.set(_userId, yield _this13.getUserDevices(_userId));
|
|
460
|
-
} else {
|
|
461
|
-
untrackedUsers.add(_userId);
|
|
462
|
-
}
|
|
390
|
+
async getUserDeviceInfo(userIds, downloadUncached = false) {
|
|
391
|
+
const deviceMapByUserId = new Map();
|
|
392
|
+
const rustTrackedUsers = await this.getOlmMachineOrThrow().trackedUsers();
|
|
393
|
+
|
|
394
|
+
// Convert RustSdkCryptoJs.UserId to a `Set<string>`
|
|
395
|
+
const trackedUsers = new Set();
|
|
396
|
+
rustTrackedUsers.forEach(rustUserId => trackedUsers.add(rustUserId.toString()));
|
|
397
|
+
|
|
398
|
+
// Keep untracked user to download their keys after
|
|
399
|
+
const untrackedUsers = new Set();
|
|
400
|
+
for (const userId of userIds) {
|
|
401
|
+
// if this is a tracked user, we can just fetch the device list from the rust-sdk
|
|
402
|
+
// (NB: this is probably ok even if we race with a leave event such that we stop tracking the user's
|
|
403
|
+
// devices: the rust-sdk will return the last-known device list, which will be good enough.)
|
|
404
|
+
if (trackedUsers.has(userId)) {
|
|
405
|
+
deviceMapByUserId.set(userId, await this.getUserDevices(userId));
|
|
406
|
+
} else {
|
|
407
|
+
untrackedUsers.add(userId);
|
|
463
408
|
}
|
|
409
|
+
}
|
|
464
410
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
deviceKeys = _ref2[1];
|
|
473
|
-
return deviceMapByUserId.set(userId, deviceKeysToDeviceMap(deviceKeys));
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
return deviceMapByUserId;
|
|
477
|
-
})();
|
|
411
|
+
// for any users whose device lists we are not tracking, fall back to downloading the device list
|
|
412
|
+
// over HTTP.
|
|
413
|
+
if (downloadUncached && untrackedUsers.size >= 1) {
|
|
414
|
+
const queryResult = await this.downloadDeviceList(untrackedUsers);
|
|
415
|
+
Object.entries(queryResult.device_keys).forEach(([userId, deviceKeys]) => deviceMapByUserId.set(userId, deviceKeysToDeviceMap(deviceKeys)));
|
|
416
|
+
}
|
|
417
|
+
return deviceMapByUserId;
|
|
478
418
|
}
|
|
479
419
|
|
|
480
420
|
/**
|
|
481
421
|
* Get the device list for the given user from the olm machine
|
|
482
422
|
* @param userId - Rust SDK UserId
|
|
483
423
|
*/
|
|
484
|
-
getUserDevices(userId) {
|
|
485
|
-
|
|
486
|
-
return _asyncToGenerator(function* () {
|
|
487
|
-
var rustUserId = new RustSdkCryptoJs.UserId(userId);
|
|
488
|
-
|
|
489
|
-
// For reasons I don't really understand, the Javascript FinalizationRegistry doesn't seem to run the
|
|
490
|
-
// registered callbacks when `userDevices` goes out of scope, nor when the individual devices in the array
|
|
491
|
-
// returned by `userDevices.devices` do so.
|
|
492
|
-
//
|
|
493
|
-
// This is particularly problematic, because each of those structures holds a reference to the
|
|
494
|
-
// VerificationMachine, which in turn holds a reference to the IndexeddbCryptoStore. Hence, we end up leaking
|
|
495
|
-
// open connections to the crypto store, which means the store can't be deleted on logout.
|
|
496
|
-
//
|
|
497
|
-
// To fix this, we explicitly call `.free` on each of the objects, which tells the rust code to drop the
|
|
498
|
-
// allocated memory and decrement the refcounts for the crypto store.
|
|
424
|
+
async getUserDevices(userId) {
|
|
425
|
+
const rustUserId = new RustSdkCryptoJs.UserId(userId);
|
|
499
426
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
427
|
+
// For reasons I don't really understand, the Javascript FinalizationRegistry doesn't seem to run the
|
|
428
|
+
// registered callbacks when `userDevices` goes out of scope, nor when the individual devices in the array
|
|
429
|
+
// returned by `userDevices.devices` do so.
|
|
430
|
+
//
|
|
431
|
+
// This is particularly problematic, because each of those structures holds a reference to the
|
|
432
|
+
// VerificationMachine, which in turn holds a reference to the IndexeddbCryptoStore. Hence, we end up leaking
|
|
433
|
+
// open connections to the crypto store, which means the store can't be deleted on logout.
|
|
434
|
+
//
|
|
435
|
+
// To fix this, we explicitly call `.free` on each of the objects, which tells the rust code to drop the
|
|
436
|
+
// allocated memory and decrement the refcounts for the crypto store.
|
|
437
|
+
|
|
438
|
+
// Wait for up to a second for any in-flight device list requests to complete.
|
|
439
|
+
// The reason for this isn't so much to avoid races (some level of raciness is
|
|
440
|
+
// inevitable for this method) but to make testing easier.
|
|
441
|
+
const userDevices = await this.olmMachine.getUserDevices(rustUserId, 1);
|
|
442
|
+
try {
|
|
443
|
+
const deviceArray = userDevices.devices();
|
|
504
444
|
try {
|
|
505
|
-
|
|
506
|
-
try {
|
|
507
|
-
return new Map(deviceArray.map(device => [device.deviceId.toString(), rustDeviceToJsDevice(device, rustUserId)]));
|
|
508
|
-
} finally {
|
|
509
|
-
deviceArray.forEach(d => d.free());
|
|
510
|
-
}
|
|
445
|
+
return new Map(deviceArray.map(device => [device.deviceId.toString(), rustDeviceToJsDevice(device, rustUserId)]));
|
|
511
446
|
} finally {
|
|
512
|
-
|
|
447
|
+
deviceArray.forEach(d => d.free());
|
|
513
448
|
}
|
|
514
|
-
}
|
|
449
|
+
} finally {
|
|
450
|
+
userDevices.free();
|
|
451
|
+
}
|
|
515
452
|
}
|
|
516
453
|
|
|
517
454
|
/**
|
|
518
455
|
* Download the given user keys by calling `/keys/query` request
|
|
519
456
|
* @param untrackedUsers - download keys of these users
|
|
520
457
|
*/
|
|
521
|
-
downloadDeviceList(untrackedUsers) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
prefix: ""
|
|
530
|
-
});
|
|
531
|
-
})();
|
|
458
|
+
async downloadDeviceList(untrackedUsers) {
|
|
459
|
+
const queryBody = {
|
|
460
|
+
device_keys: {}
|
|
461
|
+
};
|
|
462
|
+
untrackedUsers.forEach(user => queryBody.device_keys[user] = []);
|
|
463
|
+
return await this.http.authedRequest(Method.Post, "/_matrix/client/v3/keys/query", undefined, queryBody, {
|
|
464
|
+
prefix: ""
|
|
465
|
+
});
|
|
532
466
|
}
|
|
533
467
|
|
|
534
468
|
/**
|
|
@@ -552,21 +486,16 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
552
486
|
*
|
|
553
487
|
* Implementation of {@link CryptoApi#setDeviceVerified}.
|
|
554
488
|
*/
|
|
555
|
-
setDeviceVerified(userId, deviceId) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
yield device.setLocalTrust(verified ? RustSdkCryptoJs.LocalTrust.Verified : RustSdkCryptoJs.LocalTrust.Unset);
|
|
566
|
-
} finally {
|
|
567
|
-
device.free();
|
|
568
|
-
}
|
|
569
|
-
})();
|
|
489
|
+
async setDeviceVerified(userId, deviceId, verified = true) {
|
|
490
|
+
const device = await this.olmMachine.getDevice(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId));
|
|
491
|
+
if (!device) {
|
|
492
|
+
throw new Error(`Unknown device ${userId}|${deviceId}`);
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
await device.setLocalTrust(verified ? RustSdkCryptoJs.LocalTrust.Verified : RustSdkCryptoJs.LocalTrust.Unset);
|
|
496
|
+
} finally {
|
|
497
|
+
device.free();
|
|
498
|
+
}
|
|
570
499
|
}
|
|
571
500
|
|
|
572
501
|
/**
|
|
@@ -574,287 +503,269 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
574
503
|
*
|
|
575
504
|
* Implementation of {@link CryptoApi#crossSignDevice}.
|
|
576
505
|
*/
|
|
577
|
-
crossSignDevice(deviceId) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
device.free();
|
|
589
|
-
}
|
|
590
|
-
})();
|
|
506
|
+
async crossSignDevice(deviceId) {
|
|
507
|
+
const device = await this.olmMachine.getDevice(new RustSdkCryptoJs.UserId(this.userId), new RustSdkCryptoJs.DeviceId(deviceId));
|
|
508
|
+
if (!device) {
|
|
509
|
+
throw new Error(`Unknown device ${deviceId}`);
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
const outgoingRequest = await device.verify();
|
|
513
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
|
514
|
+
} finally {
|
|
515
|
+
device.free();
|
|
516
|
+
}
|
|
591
517
|
}
|
|
592
518
|
|
|
593
519
|
/**
|
|
594
520
|
* Implementation of {@link CryptoApi#getDeviceVerificationStatus}.
|
|
595
521
|
*/
|
|
596
|
-
getDeviceVerificationStatus(userId, deviceId) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
device.free();
|
|
610
|
-
}
|
|
611
|
-
})();
|
|
522
|
+
async getDeviceVerificationStatus(userId, deviceId) {
|
|
523
|
+
const device = await this.olmMachine.getDevice(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId));
|
|
524
|
+
if (!device) return null;
|
|
525
|
+
try {
|
|
526
|
+
return new DeviceVerificationStatus({
|
|
527
|
+
signedByOwner: device.isCrossSignedByOwner(),
|
|
528
|
+
crossSigningVerified: device.isCrossSigningTrusted(),
|
|
529
|
+
localVerified: device.isLocallyTrusted(),
|
|
530
|
+
trustCrossSignedDevices: this._trustCrossSignedDevices
|
|
531
|
+
});
|
|
532
|
+
} finally {
|
|
533
|
+
device.free();
|
|
534
|
+
}
|
|
612
535
|
}
|
|
613
536
|
|
|
614
537
|
/**
|
|
615
538
|
* Implementation of {@link CryptoApi#getUserVerificationStatus}.
|
|
616
539
|
*/
|
|
617
|
-
getUserVerificationStatus(userId) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
userIdentity.free();
|
|
628
|
-
return new UserVerificationStatus(verified, wasVerified, true, needsUserApproval);
|
|
629
|
-
})();
|
|
540
|
+
async getUserVerificationStatus(userId) {
|
|
541
|
+
const userIdentity = await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(userId));
|
|
542
|
+
if (userIdentity === undefined) {
|
|
543
|
+
return new UserVerificationStatus(false, false, false);
|
|
544
|
+
}
|
|
545
|
+
const verified = userIdentity.isVerified();
|
|
546
|
+
const wasVerified = userIdentity.wasPreviouslyVerified();
|
|
547
|
+
const needsUserApproval = userIdentity instanceof RustSdkCryptoJs.OtherUserIdentity ? userIdentity.identityNeedsUserApproval() : false;
|
|
548
|
+
userIdentity.free();
|
|
549
|
+
return new UserVerificationStatus(verified, wasVerified, true, needsUserApproval);
|
|
630
550
|
}
|
|
631
551
|
|
|
632
552
|
/**
|
|
633
553
|
* Implementation of {@link CryptoApi#pinCurrentUserIdentity}.
|
|
634
554
|
*/
|
|
635
|
-
pinCurrentUserIdentity(userId) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
yield userIdentity.pinCurrentMasterKey();
|
|
646
|
-
})();
|
|
555
|
+
async pinCurrentUserIdentity(userId) {
|
|
556
|
+
const userIdentity = await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(userId));
|
|
557
|
+
if (userIdentity === undefined) {
|
|
558
|
+
throw new Error("Cannot pin identity of unknown user");
|
|
559
|
+
}
|
|
560
|
+
if (userIdentity instanceof RustSdkCryptoJs.OwnUserIdentity) {
|
|
561
|
+
throw new Error("Cannot pin identity of own user");
|
|
562
|
+
}
|
|
563
|
+
await userIdentity.pinCurrentMasterKey();
|
|
647
564
|
}
|
|
648
565
|
|
|
649
566
|
/**
|
|
650
567
|
* Implementation of {@link CryptoApi#withdrawVerificationRequirement}.
|
|
651
568
|
*/
|
|
652
|
-
withdrawVerificationRequirement(userId) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
569
|
+
async withdrawVerificationRequirement(userId) {
|
|
570
|
+
const userIdentity = await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(userId));
|
|
571
|
+
if (userIdentity === undefined) {
|
|
572
|
+
throw new Error("Cannot withdraw verification of unknown user");
|
|
573
|
+
}
|
|
574
|
+
await userIdentity.withdrawVerification();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Implementation of {@link CryptoApi#getUserCrossSigningKeys}.
|
|
579
|
+
*/
|
|
580
|
+
async getUserCrossSigningKeys(userId) {
|
|
581
|
+
const userIdentity = await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(userId));
|
|
582
|
+
if (!userIdentity) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const result = {
|
|
586
|
+
master_key: JSON.parse(userIdentity.masterKey),
|
|
587
|
+
self_signing_key: JSON.parse(userIdentity.selfSigningKey)
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// The USK is only visible for our own identity
|
|
591
|
+
if ("userSigningKey" in userIdentity) {
|
|
592
|
+
result.user_signing_key = JSON.parse(userIdentity.userSigningKey);
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
661
595
|
}
|
|
662
596
|
|
|
663
597
|
/**
|
|
664
598
|
* Implementation of {@link CryptoApi#isCrossSigningReady}
|
|
665
599
|
*/
|
|
666
|
-
isCrossSigningReady() {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
var identity = yield _this22.getOwnIdentity();
|
|
600
|
+
async isCrossSigningReady() {
|
|
601
|
+
const {
|
|
602
|
+
privateKeysInSecretStorage,
|
|
603
|
+
privateKeysCachedLocally
|
|
604
|
+
} = await this.getCrossSigningStatus();
|
|
605
|
+
const hasKeysInCache = Boolean(privateKeysCachedLocally.masterKey) && Boolean(privateKeysCachedLocally.selfSigningKey) && Boolean(privateKeysCachedLocally.userSigningKey);
|
|
606
|
+
const identity = await this.getOwnIdentity();
|
|
674
607
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
})();
|
|
608
|
+
// Cross-signing is ready if the public identity is trusted, and the private keys
|
|
609
|
+
// are either cached, or accessible via secret-storage.
|
|
610
|
+
return !!identity?.isVerified() && (hasKeysInCache || privateKeysInSecretStorage);
|
|
679
611
|
}
|
|
680
612
|
|
|
681
613
|
/**
|
|
682
614
|
* Implementation of {@link CryptoApi#getCrossSigningKeyId}
|
|
683
615
|
*/
|
|
684
|
-
getCrossSigningKeyId() {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
616
|
+
async getCrossSigningKeyId(type = CrossSigningKey.Master) {
|
|
617
|
+
const userIdentity = await this.getOwnIdentity();
|
|
618
|
+
if (!userIdentity) {
|
|
619
|
+
// The public keys are not available on this device
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
const crossSigningStatus = await this.olmMachine.crossSigningStatus();
|
|
624
|
+
const privateKeysOnDevice = crossSigningStatus.hasMaster && crossSigningStatus.hasUserSigning && crossSigningStatus.hasSelfSigning;
|
|
625
|
+
if (!privateKeysOnDevice) {
|
|
626
|
+
// The private keys are not available on this device
|
|
692
627
|
return null;
|
|
693
628
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
629
|
+
if (!userIdentity.isVerified()) {
|
|
630
|
+
// We have both public and private keys, but they don't match!
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
let key;
|
|
634
|
+
switch (type) {
|
|
635
|
+
case CrossSigningKey.Master:
|
|
636
|
+
key = userIdentity.masterKey;
|
|
637
|
+
break;
|
|
638
|
+
case CrossSigningKey.SelfSigning:
|
|
639
|
+
key = userIdentity.selfSigningKey;
|
|
640
|
+
break;
|
|
641
|
+
case CrossSigningKey.UserSigning:
|
|
642
|
+
key = userIdentity.userSigningKey;
|
|
643
|
+
break;
|
|
644
|
+
default:
|
|
645
|
+
// Unknown type
|
|
703
646
|
return null;
|
|
704
|
-
}
|
|
705
|
-
var key;
|
|
706
|
-
switch (type) {
|
|
707
|
-
case CrossSigningKey.Master:
|
|
708
|
-
key = userIdentity.masterKey;
|
|
709
|
-
break;
|
|
710
|
-
case CrossSigningKey.SelfSigning:
|
|
711
|
-
key = userIdentity.selfSigningKey;
|
|
712
|
-
break;
|
|
713
|
-
case CrossSigningKey.UserSigning:
|
|
714
|
-
key = userIdentity.userSigningKey;
|
|
715
|
-
break;
|
|
716
|
-
default:
|
|
717
|
-
// Unknown type
|
|
718
|
-
return null;
|
|
719
|
-
}
|
|
720
|
-
var parsedKey = JSON.parse(key);
|
|
721
|
-
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
|
722
|
-
// We assume only a single key, and we want the bare form without type
|
|
723
|
-
// prefix, so we select the values.
|
|
724
|
-
return Object.values(parsedKey.keys)[0];
|
|
725
|
-
} finally {
|
|
726
|
-
userIdentity.free();
|
|
727
647
|
}
|
|
728
|
-
|
|
648
|
+
const parsedKey = JSON.parse(key);
|
|
649
|
+
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
|
|
650
|
+
// We assume only a single key, and we want the bare form without type
|
|
651
|
+
// prefix, so we select the values.
|
|
652
|
+
return Object.values(parsedKey.keys)[0];
|
|
653
|
+
} finally {
|
|
654
|
+
userIdentity.free();
|
|
655
|
+
}
|
|
729
656
|
}
|
|
730
657
|
|
|
731
658
|
/**
|
|
732
659
|
* Implementation of {@link CryptoApi#bootstrapCrossSigning}
|
|
733
660
|
*/
|
|
734
|
-
bootstrapCrossSigning(opts) {
|
|
735
|
-
|
|
736
|
-
return _asyncToGenerator(function* () {
|
|
737
|
-
yield _this24.crossSigningIdentity.bootstrapCrossSigning(opts);
|
|
738
|
-
})();
|
|
661
|
+
async bootstrapCrossSigning(opts) {
|
|
662
|
+
await this.crossSigningIdentity.bootstrapCrossSigning(opts);
|
|
739
663
|
}
|
|
740
664
|
|
|
741
665
|
/**
|
|
742
666
|
* Implementation of {@link CryptoApi#isSecretStorageReady}
|
|
743
667
|
*/
|
|
744
|
-
isSecretStorageReady() {
|
|
745
|
-
|
|
746
|
-
return _asyncToGenerator(function* () {
|
|
747
|
-
return (yield _this25.getSecretStorageStatus()).ready;
|
|
748
|
-
})();
|
|
668
|
+
async isSecretStorageReady() {
|
|
669
|
+
return (await this.getSecretStorageStatus()).ready;
|
|
749
670
|
}
|
|
750
671
|
|
|
751
672
|
/**
|
|
752
673
|
* Implementation of {@link CryptoApi#getSecretStorageStatus}
|
|
753
674
|
*/
|
|
754
|
-
getSecretStorageStatus() {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
// make sure that the cross-signing keys are stored
|
|
758
|
-
var secretsToCheck = ["m.cross_signing.master", "m.cross_signing.user_signing", "m.cross_signing.self_signing"];
|
|
675
|
+
async getSecretStorageStatus() {
|
|
676
|
+
// make sure that the cross-signing keys are stored
|
|
677
|
+
const secretsToCheck = ["m.cross_signing.master", "m.cross_signing.user_signing", "m.cross_signing.self_signing"];
|
|
759
678
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
})();
|
|
679
|
+
// If key backup is active, we also need to check that the backup decryption key is stored
|
|
680
|
+
const keyBackupEnabled = (await this.backupManager.getActiveBackupVersion()) != null;
|
|
681
|
+
if (keyBackupEnabled) {
|
|
682
|
+
secretsToCheck.push("m.megolm_backup.v1");
|
|
683
|
+
}
|
|
684
|
+
const defaultKeyId = await this.secretStorage.getDefaultKeyId();
|
|
685
|
+
const result = {
|
|
686
|
+
// Assume we have all secrets until proven otherwise
|
|
687
|
+
ready: true,
|
|
688
|
+
defaultKeyId,
|
|
689
|
+
secretStorageKeyValidityMap: {}
|
|
690
|
+
};
|
|
691
|
+
for (const secretName of secretsToCheck) {
|
|
692
|
+
// Check which keys this particular secret is encrypted with
|
|
693
|
+
const record = (await this.secretStorage.isStored(secretName)) || {};
|
|
694
|
+
|
|
695
|
+
// If it's encrypted with the right key, it is valid
|
|
696
|
+
const secretStored = !!defaultKeyId && defaultKeyId in record;
|
|
697
|
+
result.secretStorageKeyValidityMap[secretName] = secretStored;
|
|
698
|
+
result.ready = result.ready && secretStored;
|
|
699
|
+
}
|
|
700
|
+
return result;
|
|
783
701
|
}
|
|
784
702
|
|
|
785
703
|
/**
|
|
786
704
|
* Implementation of {@link CryptoApi#bootstrapSecretStorage}
|
|
787
705
|
*/
|
|
788
|
-
bootstrapSecretStorage(
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
yield _this27.secretStorage.store("m.cross_signing.user_signing", crossSigningPrivateKeys.userSigningKey);
|
|
821
|
-
yield _this27.secretStorage.store("m.cross_signing.self_signing", crossSigningPrivateKeys.self_signing_key);
|
|
822
|
-
}
|
|
706
|
+
async bootstrapSecretStorage({
|
|
707
|
+
createSecretStorageKey,
|
|
708
|
+
setupNewSecretStorage,
|
|
709
|
+
setupNewKeyBackup
|
|
710
|
+
} = {}) {
|
|
711
|
+
// If an AES Key is already stored in the secret storage and setupNewSecretStorage is not set
|
|
712
|
+
// we don't want to create a new key
|
|
713
|
+
const isNewSecretStorageKeyNeeded = setupNewSecretStorage || !(await this.secretStorageHasAESKey());
|
|
714
|
+
if (isNewSecretStorageKeyNeeded) {
|
|
715
|
+
if (!createSecretStorageKey) {
|
|
716
|
+
throw new Error("unable to create a new secret storage key, createSecretStorageKey is not set");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Create a new storage key and add it to secret storage
|
|
720
|
+
this.logger.info("bootstrapSecretStorage: creating new secret storage key");
|
|
721
|
+
const recoveryKey = await createSecretStorageKey();
|
|
722
|
+
if (!recoveryKey) {
|
|
723
|
+
throw new Error("createSecretStorageKey() callback did not return a secret storage key");
|
|
724
|
+
}
|
|
725
|
+
await this.addSecretStorageKeyToSecretStorage(recoveryKey);
|
|
726
|
+
}
|
|
727
|
+
const crossSigningPrivateKeys = await this.olmMachine.exportCrossSigningKeys();
|
|
728
|
+
const hasPrivateKeys = crossSigningPrivateKeys && crossSigningPrivateKeys.masterKey !== undefined && crossSigningPrivateKeys.self_signing_key !== undefined && crossSigningPrivateKeys.userSigningKey !== undefined;
|
|
729
|
+
|
|
730
|
+
// If we have cross-signing private keys cached, store them in secret
|
|
731
|
+
// storage if they are not there already.
|
|
732
|
+
if (hasPrivateKeys && (isNewSecretStorageKeyNeeded || !(await secretStorageContainsCrossSigningKeys(this.secretStorage)))) {
|
|
733
|
+
this.logger.info("bootstrapSecretStorage: cross-signing keys not yet exported; doing so now.");
|
|
734
|
+
await this.secretStorage.store("m.cross_signing.master", crossSigningPrivateKeys.masterKey);
|
|
735
|
+
await this.secretStorage.store("m.cross_signing.user_signing", crossSigningPrivateKeys.userSigningKey);
|
|
736
|
+
await this.secretStorage.store("m.cross_signing.self_signing", crossSigningPrivateKeys.self_signing_key);
|
|
737
|
+
}
|
|
823
738
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
})();
|
|
739
|
+
// likewise with the key backup key: if we have one, store it in secret storage (if it's not already there)
|
|
740
|
+
// also don't bother storing it if we're about to set up a new backup
|
|
741
|
+
if (!setupNewKeyBackup) {
|
|
742
|
+
await this.saveBackupKeyToStorage();
|
|
743
|
+
} else {
|
|
744
|
+
await this.resetKeyBackup();
|
|
745
|
+
}
|
|
832
746
|
}
|
|
833
747
|
|
|
834
748
|
/**
|
|
835
749
|
* If we have a backup key for the current, trusted backup in cache,
|
|
836
750
|
* save it to secret storage.
|
|
837
751
|
*/
|
|
838
|
-
saveBackupKeyToStorage() {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
var backupKeyBase64 = backupKeys.decryptionKey.toBase64();
|
|
856
|
-
yield _this28.secretStorage.store("m.megolm_backup.v1", backupKeyBase64);
|
|
857
|
-
})();
|
|
752
|
+
async saveBackupKeyToStorage() {
|
|
753
|
+
const keyBackupInfo = await this.backupManager.getServerBackupInfo();
|
|
754
|
+
if (!keyBackupInfo || !keyBackupInfo.version) {
|
|
755
|
+
this.logger.info("Not saving backup key to secret storage: no backup info");
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
const backupKeys = await this.olmMachine.getBackupKeys();
|
|
759
|
+
if (!backupKeys.decryptionKey) {
|
|
760
|
+
this.logger.info("Not saving backup key to secret storage: no backup key");
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (!decryptionKeyMatchesKeyBackupInfo(backupKeys.decryptionKey, keyBackupInfo)) {
|
|
764
|
+
this.logger.info("Not saving backup key to secret storage: decryption key does not match backup info");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const backupKeyBase64 = backupKeys.decryptionKey.toBase64();
|
|
768
|
+
await this.secretStorage.store("m.megolm_backup.v1", backupKeyBase64);
|
|
858
769
|
}
|
|
859
770
|
|
|
860
771
|
/**
|
|
@@ -865,18 +776,14 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
865
776
|
*
|
|
866
777
|
* @param secretStorageKey - The secret storage key to add in the secret storage.
|
|
867
778
|
*/
|
|
868
|
-
addSecretStorageKeyToSecretStorage(secretStorageKey) {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
});
|
|
877
|
-
yield _this29.secretStorage.setDefaultKeyId(secretStorageKeyObject.keyId);
|
|
878
|
-
(_this29$cryptoCallbac = (_this29$cryptoCallbac2 = _this29.cryptoCallbacks).cacheSecretStorageKey) === null || _this29$cryptoCallbac === void 0 || _this29$cryptoCallbac.call(_this29$cryptoCallbac2, secretStorageKeyObject.keyId, secretStorageKeyObject.keyInfo, secretStorageKey.privateKey);
|
|
879
|
-
})();
|
|
779
|
+
async addSecretStorageKeyToSecretStorage(secretStorageKey) {
|
|
780
|
+
const secretStorageKeyObject = await this.secretStorage.addKey(SECRET_STORAGE_ALGORITHM_V1_AES, {
|
|
781
|
+
passphrase: secretStorageKey.keyInfo?.passphrase,
|
|
782
|
+
name: secretStorageKey.keyInfo?.name,
|
|
783
|
+
key: secretStorageKey.privateKey
|
|
784
|
+
});
|
|
785
|
+
await this.secretStorage.setDefaultKeyId(secretStorageKeyObject.keyId);
|
|
786
|
+
this.cryptoCallbacks.cacheSecretStorageKey?.(secretStorageKeyObject.keyId, secretStorageKeyObject.keyInfo, secretStorageKey.privateKey);
|
|
880
787
|
}
|
|
881
788
|
|
|
882
789
|
/**
|
|
@@ -884,86 +791,73 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
884
791
|
*
|
|
885
792
|
* @returns True if an AES key is in the secret storage
|
|
886
793
|
*/
|
|
887
|
-
secretStorageHasAESKey() {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (!secretStorageKeyTuple) return false;
|
|
893
|
-
var _secretStorageKeyTupl = _slicedToArray(secretStorageKeyTuple, 2),
|
|
894
|
-
keyInfo = _secretStorageKeyTupl[1];
|
|
794
|
+
async secretStorageHasAESKey() {
|
|
795
|
+
// See if we already have an AES secret-storage key.
|
|
796
|
+
const secretStorageKeyTuple = await this.secretStorage.getKey();
|
|
797
|
+
if (!secretStorageKeyTuple) return false;
|
|
798
|
+
const [, keyInfo] = secretStorageKeyTuple;
|
|
895
799
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
})();
|
|
800
|
+
// Check if the key is an AES key
|
|
801
|
+
return keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES;
|
|
899
802
|
}
|
|
900
803
|
|
|
901
804
|
/**
|
|
902
805
|
* Implementation of {@link CryptoApi#getCrossSigningStatus}
|
|
903
806
|
*/
|
|
904
|
-
getCrossSigningStatus() {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
})();
|
|
807
|
+
async getCrossSigningStatus() {
|
|
808
|
+
const userIdentity = await this.getOwnIdentity();
|
|
809
|
+
const publicKeysOnDevice = Boolean(userIdentity?.masterKey) && Boolean(userIdentity?.selfSigningKey) && Boolean(userIdentity?.userSigningKey);
|
|
810
|
+
userIdentity?.free();
|
|
811
|
+
const privateKeysInSecretStorage = await secretStorageContainsCrossSigningKeys(this.secretStorage);
|
|
812
|
+
const crossSigningStatus = await this.getOlmMachineOrThrow().crossSigningStatus();
|
|
813
|
+
return {
|
|
814
|
+
publicKeysOnDevice,
|
|
815
|
+
privateKeysInSecretStorage,
|
|
816
|
+
privateKeysCachedLocally: {
|
|
817
|
+
masterKey: Boolean(crossSigningStatus?.hasMaster),
|
|
818
|
+
userSigningKey: Boolean(crossSigningStatus?.hasUserSigning),
|
|
819
|
+
selfSigningKey: Boolean(crossSigningStatus?.hasSelfSigning)
|
|
820
|
+
}
|
|
821
|
+
};
|
|
922
822
|
}
|
|
923
823
|
|
|
924
824
|
/**
|
|
925
825
|
* Implementation of {@link CryptoApi#createRecoveryKeyFromPassphrase}
|
|
926
826
|
*/
|
|
927
|
-
createRecoveryKeyFromPassphrase(password) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
})();
|
|
827
|
+
async createRecoveryKeyFromPassphrase(password) {
|
|
828
|
+
if (password) {
|
|
829
|
+
// Generate the key from the passphrase
|
|
830
|
+
// first we generate a random salt
|
|
831
|
+
const salt = secureRandomString(32);
|
|
832
|
+
// then we derive the key from the passphrase
|
|
833
|
+
const recoveryKey = await deriveRecoveryKeyFromPassphrase(password, salt, this.RECOVERY_KEY_DERIVATION_ITERATIONS);
|
|
834
|
+
return {
|
|
835
|
+
keyInfo: {
|
|
836
|
+
passphrase: {
|
|
837
|
+
algorithm: "m.pbkdf2",
|
|
838
|
+
iterations: this.RECOVERY_KEY_DERIVATION_ITERATIONS,
|
|
839
|
+
salt
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
privateKey: recoveryKey,
|
|
843
|
+
encodedPrivateKey: encodeRecoveryKey(recoveryKey)
|
|
844
|
+
};
|
|
845
|
+
} else {
|
|
846
|
+
// Using the navigator crypto API to generate the private key
|
|
847
|
+
const key = new Uint8Array(32);
|
|
848
|
+
globalThis.crypto.getRandomValues(key);
|
|
849
|
+
return {
|
|
850
|
+
privateKey: key,
|
|
851
|
+
encodedPrivateKey: encodeRecoveryKey(key)
|
|
852
|
+
};
|
|
853
|
+
}
|
|
957
854
|
}
|
|
958
855
|
|
|
959
856
|
/**
|
|
960
857
|
* Implementation of {@link CryptoApi#getEncryptionInfoForEvent}.
|
|
961
858
|
*/
|
|
962
|
-
getEncryptionInfoForEvent(event) {
|
|
963
|
-
|
|
964
|
-
return _asyncToGenerator(function* () {
|
|
965
|
-
return _this33.eventDecryptor.getEncryptionInfoForEvent(event);
|
|
966
|
-
})();
|
|
859
|
+
async getEncryptionInfoForEvent(event) {
|
|
860
|
+
return this.eventDecryptor.getEncryptionInfoForEvent(event);
|
|
967
861
|
}
|
|
968
862
|
|
|
969
863
|
/**
|
|
@@ -976,7 +870,7 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
976
870
|
* @returns the VerificationRequests that are in progress
|
|
977
871
|
*/
|
|
978
872
|
getVerificationRequestsToDeviceInProgress(userId) {
|
|
979
|
-
|
|
873
|
+
const requests = this.olmMachine.getVerificationRequests(new RustSdkCryptoJs.UserId(userId));
|
|
980
874
|
return requests.filter(request => request.roomId === undefined && !request.isCancelled()).map(request => this.makeVerificationRequest(request));
|
|
981
875
|
}
|
|
982
876
|
|
|
@@ -993,13 +887,10 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
993
887
|
*/
|
|
994
888
|
findVerificationRequestDMInProgress(roomId, userId) {
|
|
995
889
|
if (!userId) throw new Error("missing userId");
|
|
996
|
-
|
|
890
|
+
const requests = this.olmMachine.getVerificationRequests(new RustSdkCryptoJs.UserId(userId));
|
|
997
891
|
|
|
998
892
|
// Search for the verification request for the given room id
|
|
999
|
-
|
|
1000
|
-
var _request$roomId;
|
|
1001
|
-
return ((_request$roomId = request.roomId) === null || _request$roomId === void 0 ? void 0 : _request$roomId.toString()) === roomId && !request.isCancelled();
|
|
1002
|
-
});
|
|
893
|
+
const request = requests.find(request => request.roomId?.toString() === roomId && !request.isCancelled());
|
|
1003
894
|
if (request) {
|
|
1004
895
|
return this.makeVerificationRequest(request);
|
|
1005
896
|
}
|
|
@@ -1008,32 +899,29 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1008
899
|
/**
|
|
1009
900
|
* Implementation of {@link CryptoApi#requestVerificationDM}
|
|
1010
901
|
*/
|
|
1011
|
-
requestVerificationDM(userId, roomId) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
userIdentity.free();
|
|
1035
|
-
}
|
|
1036
|
-
})();
|
|
902
|
+
async requestVerificationDM(userId, roomId) {
|
|
903
|
+
const userIdentity = await this.olmMachine.getIdentity(new RustSdkCryptoJs.UserId(userId));
|
|
904
|
+
if (!userIdentity) throw new Error(`unknown userId ${userId}`);
|
|
905
|
+
try {
|
|
906
|
+
// Transform the verification methods into rust objects
|
|
907
|
+
const methods = this._supportedVerificationMethods.map(method => verificationMethodIdentifierToMethod(method));
|
|
908
|
+
// Get the request content to send to the DM room
|
|
909
|
+
const verCont = userIdentity.verificationRequestContent(methods);
|
|
910
|
+
|
|
911
|
+
// TODO: due to https://github.com/matrix-org/matrix-rust-sdk/issues/5643, we need to fix up the verification request content to include `msgtype`.
|
|
912
|
+
const verContObj = JSON.parse(verCont);
|
|
913
|
+
verContObj["msgtype"] = "m.key.verification.request";
|
|
914
|
+
const verificationEventContent = JSON.stringify(verContObj);
|
|
915
|
+
|
|
916
|
+
// Send the request content to send to the DM room
|
|
917
|
+
const eventId = await this.sendVerificationRequestContent(roomId, verificationEventContent);
|
|
918
|
+
|
|
919
|
+
// Get a verification request
|
|
920
|
+
const request = userIdentity.requestVerification(new RustSdkCryptoJs.RoomId(roomId), new RustSdkCryptoJs.EventId(eventId), methods);
|
|
921
|
+
return this.makeVerificationRequest(request);
|
|
922
|
+
} finally {
|
|
923
|
+
userIdentity.free();
|
|
924
|
+
}
|
|
1037
925
|
}
|
|
1038
926
|
|
|
1039
927
|
/**
|
|
@@ -1047,17 +935,15 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1047
935
|
*
|
|
1048
936
|
* @returns the event id
|
|
1049
937
|
*/
|
|
1050
|
-
sendVerificationRequestContent(roomId, verificationEventContent) {
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
return eventId;
|
|
1060
|
-
})();
|
|
938
|
+
async sendVerificationRequestContent(roomId, verificationEventContent) {
|
|
939
|
+
const txId = secureRandomString(32);
|
|
940
|
+
// Send the verification request content to the DM room
|
|
941
|
+
const {
|
|
942
|
+
event_id: eventId
|
|
943
|
+
} = await this.http.authedRequest(Method.Put, `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${encodeURIComponent(txId)}`, undefined, verificationEventContent, {
|
|
944
|
+
prefix: ""
|
|
945
|
+
});
|
|
946
|
+
return eventId;
|
|
1061
947
|
}
|
|
1062
948
|
/**
|
|
1063
949
|
* Set the verification methods we offer to the other side during an interactive verification.
|
|
@@ -1066,7 +952,7 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1066
952
|
*/
|
|
1067
953
|
setSupportedVerificationMethods(methods) {
|
|
1068
954
|
// by default, the Rust SDK does not offer `m.qr_code.scan.v1`, but we do want to offer that.
|
|
1069
|
-
this._supportedVerificationMethods = methods
|
|
955
|
+
this._supportedVerificationMethods = methods ?? ALL_VERIFICATION_METHODS;
|
|
1070
956
|
}
|
|
1071
957
|
|
|
1072
958
|
/**
|
|
@@ -1078,24 +964,18 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1078
964
|
*
|
|
1079
965
|
* @returns a VerificationRequest when the request has been sent to the other party.
|
|
1080
966
|
*/
|
|
1081
|
-
requestOwnUserVerification() {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
yield _this36.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
|
1094
|
-
return _this36.makeVerificationRequest(request);
|
|
1095
|
-
} finally {
|
|
1096
|
-
userIdentity.free();
|
|
1097
|
-
}
|
|
1098
|
-
})();
|
|
967
|
+
async requestOwnUserVerification() {
|
|
968
|
+
const userIdentity = await this.getOwnIdentity();
|
|
969
|
+
if (userIdentity === undefined) {
|
|
970
|
+
throw new Error("cannot request verification for this device when there is no existing cross-signing key");
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
973
|
+
const [request, outgoingRequest] = await userIdentity.requestVerification(this._supportedVerificationMethods.map(verificationMethodIdentifierToMethod));
|
|
974
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
|
975
|
+
return this.makeVerificationRequest(request);
|
|
976
|
+
} finally {
|
|
977
|
+
userIdentity.free();
|
|
978
|
+
}
|
|
1099
979
|
}
|
|
1100
980
|
|
|
1101
981
|
/**
|
|
@@ -1110,24 +990,18 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1110
990
|
*
|
|
1111
991
|
* @returns a VerificationRequest when the request has been sent to the other party.
|
|
1112
992
|
*/
|
|
1113
|
-
requestDeviceVerification(userId, deviceId) {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
yield _this37.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
|
1126
|
-
return _this37.makeVerificationRequest(request);
|
|
1127
|
-
} finally {
|
|
1128
|
-
device.free();
|
|
1129
|
-
}
|
|
1130
|
-
})();
|
|
993
|
+
async requestDeviceVerification(userId, deviceId) {
|
|
994
|
+
const device = await this.olmMachine.getDevice(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId));
|
|
995
|
+
if (!device) {
|
|
996
|
+
throw new Error("Not a known device");
|
|
997
|
+
}
|
|
998
|
+
try {
|
|
999
|
+
const [request, outgoingRequest] = device.requestVerification(this._supportedVerificationMethods.map(verificationMethodIdentifierToMethod));
|
|
1000
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(outgoingRequest);
|
|
1001
|
+
return this.makeVerificationRequest(request);
|
|
1002
|
+
} finally {
|
|
1003
|
+
device.free();
|
|
1004
|
+
}
|
|
1131
1005
|
}
|
|
1132
1006
|
|
|
1133
1007
|
/**
|
|
@@ -1137,13 +1011,10 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1137
1011
|
*
|
|
1138
1012
|
* @returns the key, if any, or null
|
|
1139
1013
|
*/
|
|
1140
|
-
getSessionBackupPrivateKey() {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
if (!backupKeys.decryptionKey) return null;
|
|
1145
|
-
return decodeBase64(backupKeys.decryptionKey.toBase64());
|
|
1146
|
-
})();
|
|
1014
|
+
async getSessionBackupPrivateKey() {
|
|
1015
|
+
const backupKeys = await this.olmMachine.getBackupKeys();
|
|
1016
|
+
if (!backupKeys.decryptionKey) return null;
|
|
1017
|
+
return decodeBase64(backupKeys.decryptionKey.toBase64());
|
|
1147
1018
|
}
|
|
1148
1019
|
|
|
1149
1020
|
/**
|
|
@@ -1154,37 +1025,31 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1154
1025
|
* @param key - the backup decryption key
|
|
1155
1026
|
* @param version - the backup version for this key.
|
|
1156
1027
|
*/
|
|
1157
|
-
storeSessionBackupPrivateKey(key, version) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1164
|
-
yield _this39.backupManager.saveBackupDecryptionKey(RustSdkCryptoJs.BackupDecryptionKey.fromBase64(base64Key), version);
|
|
1165
|
-
})();
|
|
1028
|
+
async storeSessionBackupPrivateKey(key, version) {
|
|
1029
|
+
const base64Key = encodeBase64(key);
|
|
1030
|
+
if (!version) {
|
|
1031
|
+
throw new Error("storeSessionBackupPrivateKey: version is required");
|
|
1032
|
+
}
|
|
1033
|
+
await this.backupManager.saveBackupDecryptionKey(RustSdkCryptoJs.BackupDecryptionKey.fromBase64(base64Key), version);
|
|
1166
1034
|
}
|
|
1167
1035
|
|
|
1168
1036
|
/**
|
|
1169
1037
|
* Implementation of {@link CryptoApi#loadSessionBackupPrivateKeyFromSecretStorage}.
|
|
1170
1038
|
*/
|
|
1171
|
-
loadSessionBackupPrivateKeyFromSecretStorage() {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1186
|
-
yield _this40.backupManager.saveBackupDecryptionKey(backupDecryptionKey, keyBackupInfo.version);
|
|
1187
|
-
})();
|
|
1039
|
+
async loadSessionBackupPrivateKeyFromSecretStorage() {
|
|
1040
|
+
const backupKey = await this.secretStorage.get("m.megolm_backup.v1");
|
|
1041
|
+
if (!backupKey) {
|
|
1042
|
+
throw new Error("loadSessionBackupPrivateKeyFromSecretStorage: missing decryption key in secret storage");
|
|
1043
|
+
}
|
|
1044
|
+
const keyBackupInfo = await this.backupManager.getServerBackupInfo();
|
|
1045
|
+
if (!keyBackupInfo || !keyBackupInfo.version) {
|
|
1046
|
+
throw new Error("loadSessionBackupPrivateKeyFromSecretStorage: unable to get backup version");
|
|
1047
|
+
}
|
|
1048
|
+
const backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(backupKey);
|
|
1049
|
+
if (!decryptionKeyMatchesKeyBackupInfo(backupDecryptionKey, keyBackupInfo)) {
|
|
1050
|
+
throw new DecryptionKeyDoesNotMatchError("loadSessionBackupPrivateKeyFromSecretStorage: decryption key does not match backup info");
|
|
1051
|
+
}
|
|
1052
|
+
await this.backupManager.saveBackupDecryptionKey(backupDecryptionKey, keyBackupInfo.version);
|
|
1188
1053
|
}
|
|
1189
1054
|
|
|
1190
1055
|
/**
|
|
@@ -1192,21 +1057,15 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1192
1057
|
*
|
|
1193
1058
|
* Implementation of {@link CryptoApi#getActiveSessionBackupVersion}.
|
|
1194
1059
|
*/
|
|
1195
|
-
getActiveSessionBackupVersion() {
|
|
1196
|
-
|
|
1197
|
-
return _asyncToGenerator(function* () {
|
|
1198
|
-
return yield _this41.backupManager.getActiveBackupVersion();
|
|
1199
|
-
})();
|
|
1060
|
+
async getActiveSessionBackupVersion() {
|
|
1061
|
+
return await this.backupManager.getActiveBackupVersion();
|
|
1200
1062
|
}
|
|
1201
1063
|
|
|
1202
1064
|
/**
|
|
1203
1065
|
* Implementation of {@link CryptoApi#getKeyBackupInfo}.
|
|
1204
1066
|
*/
|
|
1205
|
-
getKeyBackupInfo() {
|
|
1206
|
-
|
|
1207
|
-
return _asyncToGenerator(function* () {
|
|
1208
|
-
return (yield _this42.backupManager.getServerBackupInfo()) || null;
|
|
1209
|
-
})();
|
|
1067
|
+
async getKeyBackupInfo() {
|
|
1068
|
+
return (await this.backupManager.getServerBackupInfo()) || null;
|
|
1210
1069
|
}
|
|
1211
1070
|
|
|
1212
1071
|
/**
|
|
@@ -1214,11 +1073,8 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1214
1073
|
*
|
|
1215
1074
|
* Implementation of {@link CryptoApi#isKeyBackupTrusted}.
|
|
1216
1075
|
*/
|
|
1217
|
-
isKeyBackupTrusted(info) {
|
|
1218
|
-
|
|
1219
|
-
return _asyncToGenerator(function* () {
|
|
1220
|
-
return yield _this43.backupManager.isKeyBackupTrusted(info);
|
|
1221
|
-
})();
|
|
1076
|
+
async isKeyBackupTrusted(info) {
|
|
1077
|
+
return await this.backupManager.isKeyBackupTrusted(info);
|
|
1222
1078
|
}
|
|
1223
1079
|
|
|
1224
1080
|
/**
|
|
@@ -1226,61 +1082,49 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1226
1082
|
*
|
|
1227
1083
|
* Implementation of {@link CryptoApi#checkKeyBackupAndEnable}.
|
|
1228
1084
|
*/
|
|
1229
|
-
checkKeyBackupAndEnable() {
|
|
1230
|
-
|
|
1231
|
-
return _asyncToGenerator(function* () {
|
|
1232
|
-
return yield _this44.backupManager.checkKeyBackupAndEnable(true);
|
|
1233
|
-
})();
|
|
1085
|
+
async checkKeyBackupAndEnable() {
|
|
1086
|
+
return await this.backupManager.checkKeyBackupAndEnable(true);
|
|
1234
1087
|
}
|
|
1235
1088
|
|
|
1236
1089
|
/**
|
|
1237
1090
|
* Implementation of {@link CryptoApi#deleteKeyBackupVersion}.
|
|
1238
1091
|
*/
|
|
1239
|
-
deleteKeyBackupVersion(version) {
|
|
1240
|
-
|
|
1241
|
-
return _asyncToGenerator(function* () {
|
|
1242
|
-
yield _this45.backupManager.deleteKeyBackupVersion(version);
|
|
1243
|
-
})();
|
|
1092
|
+
async deleteKeyBackupVersion(version) {
|
|
1093
|
+
await this.backupManager.deleteKeyBackupVersion(version);
|
|
1244
1094
|
}
|
|
1245
1095
|
|
|
1246
1096
|
/**
|
|
1247
1097
|
* Implementation of {@link CryptoApi#resetKeyBackup}.
|
|
1248
1098
|
*/
|
|
1249
|
-
resetKeyBackup() {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
var backupInfo = yield _this46.backupManager.setupKeyBackup(o => _this46.signObject(o));
|
|
1253
|
-
yield _this46.pushSecretToVerifiedDevices("m.megolm_backup.v1");
|
|
1099
|
+
async resetKeyBackup() {
|
|
1100
|
+
const backupInfo = await this.backupManager.setupKeyBackup(o => this.signObject(o));
|
|
1101
|
+
await this.pushSecretToVerifiedDevices("m.megolm_backup.v1");
|
|
1254
1102
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1103
|
+
// we want to store the private key in 4S
|
|
1104
|
+
// need to check if 4S is set up?
|
|
1105
|
+
if (await this.secretStorageHasAESKey()) {
|
|
1106
|
+
await this.secretStorage.store("m.megolm_backup.v1", backupInfo.decryptionKey.toBase64());
|
|
1107
|
+
}
|
|
1260
1108
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
})();
|
|
1109
|
+
// we can check and start async
|
|
1110
|
+
void this.checkKeyBackupAndEnable();
|
|
1264
1111
|
}
|
|
1265
1112
|
|
|
1266
1113
|
/**
|
|
1267
1114
|
* Implementation of {@link CryptoApi#disableKeyStorage}.
|
|
1268
1115
|
*/
|
|
1269
|
-
disableKeyStorage() {
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
_this47.logger.error("Can't delete key backup version: no version available");
|
|
1278
|
-
}
|
|
1116
|
+
async disableKeyStorage() {
|
|
1117
|
+
// Get the key backup version we're using
|
|
1118
|
+
const info = await this.getKeyBackupInfo();
|
|
1119
|
+
if (info?.version) {
|
|
1120
|
+
await this.deleteKeyBackupVersion(info.version);
|
|
1121
|
+
} else {
|
|
1122
|
+
this.logger.error("Can't delete key backup version: no version available");
|
|
1123
|
+
}
|
|
1279
1124
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
})();
|
|
1125
|
+
// also turn off 4S, since this is also storing keys on the server.
|
|
1126
|
+
await this.deleteSecretStorage();
|
|
1127
|
+
await this.dehydratedDeviceManager.delete();
|
|
1284
1128
|
}
|
|
1285
1129
|
|
|
1286
1130
|
/**
|
|
@@ -1291,257 +1135,218 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1291
1135
|
*
|
|
1292
1136
|
* @param obj - The object to sign
|
|
1293
1137
|
*/
|
|
1294
|
-
signObject(obj) {
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
if (unsigned !== undefined) obj.unsigned = unsigned;
|
|
1307
|
-
obj.signatures = Object.fromEntries(sigs.entries());
|
|
1308
|
-
})();
|
|
1138
|
+
async signObject(obj) {
|
|
1139
|
+
const sigs = new Map(Object.entries(obj.signatures || {}));
|
|
1140
|
+
const unsigned = obj.unsigned;
|
|
1141
|
+
delete obj.signatures;
|
|
1142
|
+
delete obj.unsigned;
|
|
1143
|
+
const userSignatures = sigs.get(this.userId) || {};
|
|
1144
|
+
const canonalizedJson = anotherjson.stringify(obj);
|
|
1145
|
+
const signatures = await this.olmMachine.sign(canonalizedJson);
|
|
1146
|
+
const map = JSON.parse(signatures.asJSON());
|
|
1147
|
+
sigs.set(this.userId, _objectSpread(_objectSpread({}, userSignatures), map[this.userId]));
|
|
1148
|
+
if (unsigned !== undefined) obj.unsigned = unsigned;
|
|
1149
|
+
obj.signatures = Object.fromEntries(sigs.entries());
|
|
1309
1150
|
}
|
|
1310
1151
|
|
|
1311
1152
|
/**
|
|
1312
1153
|
* Implementation of {@link CryptoApi#restoreKeyBackupWithPassphrase}.
|
|
1313
1154
|
*/
|
|
1314
|
-
restoreKeyBackupWithPassphrase(passphrase, opts) {
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
}
|
|
1321
|
-
var privateKey = yield keyFromAuthData(backupInfo.auth_data, passphrase);
|
|
1155
|
+
async restoreKeyBackupWithPassphrase(passphrase, opts) {
|
|
1156
|
+
const backupInfo = await this.backupManager.getServerBackupInfo();
|
|
1157
|
+
if (!backupInfo?.version) {
|
|
1158
|
+
throw new Error("No backup info available");
|
|
1159
|
+
}
|
|
1160
|
+
const privateKey = await keyFromAuthData(backupInfo.auth_data, passphrase);
|
|
1322
1161
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
})();
|
|
1162
|
+
// Cache the key
|
|
1163
|
+
await this.storeSessionBackupPrivateKey(privateKey, backupInfo.version);
|
|
1164
|
+
return this.restoreKeyBackup(opts);
|
|
1327
1165
|
}
|
|
1328
1166
|
|
|
1329
1167
|
/**
|
|
1330
1168
|
* Implementation of {@link CryptoApi#restoreKeyBackup}.
|
|
1331
1169
|
*/
|
|
1332
|
-
restoreKeyBackup(opts) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
})();
|
|
1170
|
+
async restoreKeyBackup(opts) {
|
|
1171
|
+
// Get the decryption key from the crypto store
|
|
1172
|
+
const backupKeys = await this.olmMachine.getBackupKeys();
|
|
1173
|
+
const {
|
|
1174
|
+
decryptionKey,
|
|
1175
|
+
backupVersion
|
|
1176
|
+
} = backupKeys;
|
|
1177
|
+
if (!decryptionKey || !backupVersion) throw new Error("No decryption key found in crypto store");
|
|
1178
|
+
const decodedDecryptionKey = decodeBase64(decryptionKey.toBase64());
|
|
1179
|
+
const backupInfo = await this.backupManager.requestKeyBackupVersion(backupVersion);
|
|
1180
|
+
if (!backupInfo) throw new Error(`Backup version to restore ${backupVersion} not found on server`);
|
|
1181
|
+
const backupDecryptor = await this.getBackupDecryptor(backupInfo, decodedDecryptionKey);
|
|
1182
|
+
try {
|
|
1183
|
+
opts?.progressCallback?.({
|
|
1184
|
+
stage: ImportRoomKeyStage.Fetch
|
|
1185
|
+
});
|
|
1186
|
+
return await this.backupManager.restoreKeyBackup(backupVersion, backupDecryptor, opts);
|
|
1187
|
+
} finally {
|
|
1188
|
+
// Free to avoid to keep in memory the decryption key stored in it. To avoid to exposing it to an attacker.
|
|
1189
|
+
backupDecryptor.free();
|
|
1190
|
+
}
|
|
1355
1191
|
}
|
|
1356
1192
|
|
|
1357
1193
|
/**
|
|
1358
1194
|
* Implementation of {@link CryptoApi#isDehydrationSupported}.
|
|
1359
1195
|
*/
|
|
1360
|
-
isDehydrationSupported() {
|
|
1361
|
-
|
|
1362
|
-
return _asyncToGenerator(function* () {
|
|
1363
|
-
return yield _this51.dehydratedDeviceManager.isSupported();
|
|
1364
|
-
})();
|
|
1196
|
+
async isDehydrationSupported() {
|
|
1197
|
+
return await this.dehydratedDeviceManager.isSupported();
|
|
1365
1198
|
}
|
|
1366
1199
|
|
|
1367
1200
|
/**
|
|
1368
1201
|
* Implementation of {@link CryptoApi#startDehydration}.
|
|
1369
1202
|
*/
|
|
1370
|
-
startDehydration() {
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
if (!(yield _this52.isCrossSigningReady()) || !(yield _this52.isSecretStorageReady())) {
|
|
1376
|
-
throw new Error("Device dehydration requires cross-signing and secret storage to be set up");
|
|
1377
|
-
}
|
|
1378
|
-
return yield _this52.dehydratedDeviceManager.start(opts || {});
|
|
1379
|
-
})();
|
|
1203
|
+
async startDehydration(opts = {}) {
|
|
1204
|
+
if (!(await this.isCrossSigningReady()) || !(await this.isSecretStorageReady())) {
|
|
1205
|
+
throw new Error("Device dehydration requires cross-signing and secret storage to be set up");
|
|
1206
|
+
}
|
|
1207
|
+
return await this.dehydratedDeviceManager.start(opts || {});
|
|
1380
1208
|
}
|
|
1381
1209
|
|
|
1382
1210
|
/**
|
|
1383
1211
|
* Implementation of {@link CryptoApi#importSecretsBundle}.
|
|
1384
1212
|
*/
|
|
1385
|
-
importSecretsBundle(secrets) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
var secretsBundle = RustSdkCryptoJs.SecretsBundle.from_json(secrets);
|
|
1389
|
-
yield _this53.getOlmMachineOrThrow().importSecretsBundle(secretsBundle); // this method frees the SecretsBundle
|
|
1390
|
-
})();
|
|
1213
|
+
async importSecretsBundle(secrets) {
|
|
1214
|
+
const secretsBundle = RustSdkCryptoJs.SecretsBundle.from_json(secrets);
|
|
1215
|
+
await this.getOlmMachineOrThrow().importSecretsBundle(secretsBundle); // this method frees the SecretsBundle
|
|
1391
1216
|
}
|
|
1392
1217
|
|
|
1393
1218
|
/**
|
|
1394
1219
|
* Implementation of {@link CryptoApi#exportSecretsBundle}.
|
|
1395
1220
|
*/
|
|
1396
|
-
exportSecretsBundle() {
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
secretsBundle.free();
|
|
1402
|
-
return secrets;
|
|
1403
|
-
})();
|
|
1221
|
+
async exportSecretsBundle() {
|
|
1222
|
+
const secretsBundle = await this.getOlmMachineOrThrow().exportSecretsBundle();
|
|
1223
|
+
const secrets = secretsBundle.to_json();
|
|
1224
|
+
secretsBundle.free();
|
|
1225
|
+
return secrets;
|
|
1404
1226
|
}
|
|
1405
1227
|
|
|
1406
1228
|
/**
|
|
1407
1229
|
* Implementation of {@link CryptoApi#encryptToDeviceMessages}.
|
|
1408
1230
|
*/
|
|
1409
|
-
encryptToDeviceMessages(eventType, devices, payload) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
deviceId,
|
|
1435
|
-
userId,
|
|
1436
|
-
payload: encryptedPayload
|
|
1437
|
-
});
|
|
1438
|
-
} else {
|
|
1439
|
-
_this55.logger.warn("encryptToDeviceMessages: unknown device ".concat(userId, ":").concat(deviceId));
|
|
1440
|
-
}
|
|
1231
|
+
async encryptToDeviceMessages(eventType, devices, payload) {
|
|
1232
|
+
const logger = new LogSpan(this.logger, "encryptToDeviceMessages");
|
|
1233
|
+
const uniqueUsers = new Set(devices.map(({
|
|
1234
|
+
userId
|
|
1235
|
+
}) => userId));
|
|
1236
|
+
|
|
1237
|
+
// This will ensure we have Olm sessions for all of the users' devices.
|
|
1238
|
+
// However, we only care about some of the devices.
|
|
1239
|
+
// So, perhaps we can optimise this later on.
|
|
1240
|
+
await this.keyClaimManager.ensureSessionsForUsers(logger, Array.from(uniqueUsers).map(userId => new RustSdkCryptoJs.UserId(userId)));
|
|
1241
|
+
const batch = {
|
|
1242
|
+
batch: [],
|
|
1243
|
+
eventType: EventType.RoomMessageEncrypted
|
|
1244
|
+
};
|
|
1245
|
+
await Promise.all(devices.map(async ({
|
|
1246
|
+
userId,
|
|
1247
|
+
deviceId
|
|
1248
|
+
}) => {
|
|
1249
|
+
const device = await this.olmMachine.getDevice(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.DeviceId(deviceId));
|
|
1250
|
+
if (device) {
|
|
1251
|
+
const encryptedPayload = JSON.parse(await device.encryptToDeviceEvent(eventType, payload));
|
|
1252
|
+
batch.batch.push({
|
|
1253
|
+
deviceId,
|
|
1254
|
+
userId,
|
|
1255
|
+
payload: encryptedPayload
|
|
1441
1256
|
});
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
})();
|
|
1257
|
+
} else {
|
|
1258
|
+
this.logger.warn(`encryptToDeviceMessages: unknown device ${userId}:${deviceId}`);
|
|
1259
|
+
}
|
|
1260
|
+
}));
|
|
1261
|
+
return batch;
|
|
1448
1262
|
}
|
|
1449
1263
|
|
|
1450
1264
|
/**
|
|
1451
1265
|
* Implementation of {@link CryptoApi#resetEncryption}.
|
|
1452
1266
|
*/
|
|
1453
|
-
resetEncryption(authUploadDeviceSigningKeys) {
|
|
1454
|
-
|
|
1455
|
-
return _asyncToGenerator(function* () {
|
|
1456
|
-
_this56.logger.debug("resetEncryption: resetting encryption");
|
|
1267
|
+
async resetEncryption(authUploadDeviceSigningKeys) {
|
|
1268
|
+
this.logger.debug("resetEncryption: resetting encryption");
|
|
1457
1269
|
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1270
|
+
// Delete the dehydrated device, since any existing one will be signed
|
|
1271
|
+
// by the wrong cross-signing key
|
|
1272
|
+
void this.dehydratedDeviceManager.delete();
|
|
1461
1273
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1274
|
+
// Disable backup, and delete all the backups from the server
|
|
1275
|
+
await this.backupManager.deleteAllKeyBackupVersions();
|
|
1276
|
+
await this.deleteSecretStorage();
|
|
1465
1277
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1278
|
+
// Reset the cross-signing keys
|
|
1279
|
+
await this.crossSigningIdentity.bootstrapCrossSigning({
|
|
1280
|
+
setupNewCrossSigning: true,
|
|
1281
|
+
authUploadDeviceSigningKeys
|
|
1282
|
+
});
|
|
1471
1283
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
})();
|
|
1284
|
+
// Create a new key backup
|
|
1285
|
+
await this.resetKeyBackup();
|
|
1286
|
+
this.logger.debug("resetEncryption: ended");
|
|
1476
1287
|
}
|
|
1477
1288
|
|
|
1478
1289
|
/**
|
|
1479
1290
|
* Removes the secret storage key, default key pointer and all (known) secret storage data
|
|
1480
1291
|
* from the user's account data
|
|
1481
1292
|
*/
|
|
1482
|
-
deleteSecretStorage() {
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
yield _this57.secretStorage.store("m.cross_signing.user_signing", null);
|
|
1489
|
-
yield _this57.secretStorage.store("m.megolm_backup.v1", null);
|
|
1293
|
+
async deleteSecretStorage() {
|
|
1294
|
+
// Remove the stored secrets in the secret storage
|
|
1295
|
+
await this.secretStorage.store("m.cross_signing.master", null);
|
|
1296
|
+
await this.secretStorage.store("m.cross_signing.self_signing", null);
|
|
1297
|
+
await this.secretStorage.store("m.cross_signing.user_signing", null);
|
|
1298
|
+
await this.secretStorage.store("m.megolm_backup.v1", null);
|
|
1490
1299
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
})();
|
|
1300
|
+
// Remove the recovery key
|
|
1301
|
+
const defaultKeyId = await this.secretStorage.getDefaultKeyId();
|
|
1302
|
+
if (defaultKeyId) await this.secretStorage.store(`m.secret_storage.key.${defaultKeyId}`, null);
|
|
1303
|
+
// Disable the recovery key and the secret storage
|
|
1304
|
+
await this.secretStorage.setDefaultKeyId(null);
|
|
1497
1305
|
}
|
|
1498
1306
|
|
|
1499
1307
|
/**
|
|
1500
1308
|
* Implementation of {@link CryptoApi#shareRoomHistoryWithUser}.
|
|
1501
1309
|
*/
|
|
1502
|
-
shareRoomHistoryWithUser(roomId, userId) {
|
|
1503
|
-
|
|
1504
|
-
return _asyncToGenerator(function* () {
|
|
1505
|
-
var logger = new LogSpan(_this58.logger, "shareRoomHistoryWithUser(".concat(roomId, ", ").concat(userId, ")"));
|
|
1310
|
+
async shareRoomHistoryWithUser(roomId, userId) {
|
|
1311
|
+
const logger = new LogSpan(this.logger, `shareRoomHistoryWithUser(${roomId}, ${userId})`);
|
|
1506
1312
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1313
|
+
// 0. We can only share room history if our user has set up cross-signing.
|
|
1314
|
+
const identity = await this.getOwnIdentity();
|
|
1315
|
+
if (!identity?.isVerified()) {
|
|
1316
|
+
logger.warn("Not sharing message history as the current device is not verified by our cross-signing identity");
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
logger.info("Sharing message history");
|
|
1514
1320
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1321
|
+
// 1. Download keys from backup.
|
|
1322
|
+
if (!(await this.getOlmMachineOrThrow().hasDownloadedAllRoomKeys(new RustSdkCryptoJs.RoomId(roomId)))) {
|
|
1323
|
+
await this.backupManager.downloadLatestRoomKeyBackup(roomId);
|
|
1324
|
+
await this.getOlmMachineOrThrow().setHasDownloadedAllRoomKeys(new RustSdkCryptoJs.RoomId(roomId));
|
|
1325
|
+
}
|
|
1520
1326
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1327
|
+
// 2. Construct the key bundle
|
|
1328
|
+
const bundle = await this.getOlmMachineOrThrow().buildRoomKeyBundle(new RustSdkCryptoJs.RoomId(roomId));
|
|
1329
|
+
if (!bundle) {
|
|
1330
|
+
logger.info("No keys to share");
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1527
1333
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1334
|
+
// 3. Upload the encrypted bundle to the server
|
|
1335
|
+
const uploadResponse = await this.http.uploadContent(bundle.encryptedData);
|
|
1336
|
+
logger.info(`Uploaded encrypted key blob: ${JSON.stringify(uploadResponse)}`);
|
|
1531
1337
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1338
|
+
// 4. We may not share a room with the user, so get a fresh list of devices for the invited user.
|
|
1339
|
+
const req = this.getOlmMachineOrThrow().queryKeysForUsers([new RustSdkCryptoJs.UserId(userId)]);
|
|
1340
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
1535
1341
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1342
|
+
// 5. Establish Olm sessions with all of the recipient's devices.
|
|
1343
|
+
await this.keyClaimManager.ensureSessionsForUsers(logger, [new RustSdkCryptoJs.UserId(userId)]);
|
|
1538
1344
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
})();
|
|
1345
|
+
// 6. Send to-device messages to the recipient to share the keys.
|
|
1346
|
+
const requests = await this.getOlmMachineOrThrow().shareRoomKeyBundleData(new RustSdkCryptoJs.UserId(userId), new RustSdkCryptoJs.RoomId(roomId), uploadResponse.content_uri, bundle.mediaEncryptionInfo, RustSdkCryptoJs.CollectStrategy.identityBasedStrategy());
|
|
1347
|
+
for (const req of requests) {
|
|
1348
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
1349
|
+
}
|
|
1545
1350
|
}
|
|
1546
1351
|
|
|
1547
1352
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1558,17 +1363,13 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1558
1363
|
* @param devices - the received device list updates
|
|
1559
1364
|
* @returns A list of processed to-device messages.
|
|
1560
1365
|
*/
|
|
1561
|
-
receiveSyncChanges(
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
_ref7$devices = _ref7.devices,
|
|
1569
|
-
devices = _ref7$devices === void 0 ? new RustSdkCryptoJs.DeviceLists() : _ref7$devices;
|
|
1570
|
-
return yield _this59.olmMachine.receiveSyncChanges(events ? JSON.stringify(events) : "[]", devices, oneTimeKeysCounts, unusedFallbackKeys);
|
|
1571
|
-
})();
|
|
1366
|
+
async receiveSyncChanges({
|
|
1367
|
+
events,
|
|
1368
|
+
oneTimeKeysCounts = new Map(),
|
|
1369
|
+
unusedFallbackKeys,
|
|
1370
|
+
devices = new RustSdkCryptoJs.DeviceLists()
|
|
1371
|
+
}) {
|
|
1372
|
+
return await this.olmMachine.receiveSyncChanges(events ? JSON.stringify(events) : "[]", devices, oneTimeKeysCounts, unusedFallbackKeys);
|
|
1572
1373
|
}
|
|
1573
1374
|
|
|
1574
1375
|
/** called by the sync loop to preprocess incoming to-device messages
|
|
@@ -1576,87 +1377,80 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1576
1377
|
* @param events - the received to-device messages
|
|
1577
1378
|
* @returns A list of preprocessed to-device messages.
|
|
1578
1379
|
*/
|
|
1579
|
-
preprocessToDeviceMessages(events) {
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
if (transactionId && sender) {
|
|
1596
|
-
_this60.onIncomingKeyVerificationRequest(sender, transactionId);
|
|
1597
|
-
}
|
|
1380
|
+
async preprocessToDeviceMessages(events) {
|
|
1381
|
+
// send the received to-device messages into receiveSyncChanges. We have no info on device-list changes,
|
|
1382
|
+
// one-time-keys, or fallback keys, so just pass empty data.
|
|
1383
|
+
const processed = await this.receiveSyncChanges({
|
|
1384
|
+
events
|
|
1385
|
+
});
|
|
1386
|
+
const received = [];
|
|
1387
|
+
for (const message of processed) {
|
|
1388
|
+
const parsedMessage = JSON.parse(message.rawEvent);
|
|
1389
|
+
|
|
1390
|
+
// look for interesting to-device messages
|
|
1391
|
+
if (parsedMessage.type === EventType.KeyVerificationRequest) {
|
|
1392
|
+
const sender = parsedMessage.sender;
|
|
1393
|
+
const transactionId = parsedMessage.content.transaction_id;
|
|
1394
|
+
if (transactionId && sender) {
|
|
1395
|
+
this.onIncomingKeyVerificationRequest(sender, transactionId);
|
|
1598
1396
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1397
|
+
}
|
|
1398
|
+
switch (message.type) {
|
|
1399
|
+
case RustSdkCryptoJs.ProcessedToDeviceEventType.Decrypted:
|
|
1400
|
+
{
|
|
1401
|
+
const encryptionInfo = message.encryptionInfo;
|
|
1402
|
+
received.push({
|
|
1403
|
+
message: parsedMessage,
|
|
1404
|
+
encryptionInfo: {
|
|
1405
|
+
sender: encryptionInfo.sender.toString(),
|
|
1406
|
+
senderDevice: encryptionInfo.senderDevice?.toString(),
|
|
1407
|
+
senderCurve25519KeyBase64: encryptionInfo.senderCurve25519Key,
|
|
1408
|
+
senderVerified: encryptionInfo.isSenderVerified()
|
|
1409
|
+
}
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
// If we have received a room key bundle message, and have recently joined the room in question,
|
|
1413
|
+
// tell the Rust SDK to try and accept the key bundle.
|
|
1414
|
+
//
|
|
1415
|
+
// We don't actually need to validate the contents of the bundle message, or do
|
|
1416
|
+
// anything with its contents at all. We simply want to inform the Rust SDK we have
|
|
1417
|
+
// received a new room key bundle that we might be able to download.
|
|
1418
|
+
if (isRoomKeyBundleMessage(parsedMessage)) {
|
|
1419
|
+
const roomId = parsedMessage.content.room_id;
|
|
1420
|
+
const pendingDetails = await this.olmMachine.getPendingKeyBundleDetailsForRoom(new RustSdkCryptoJs.RoomId(roomId));
|
|
1421
|
+
// Only accept the key bundle if we joined the room less than 24 hours ago.
|
|
1422
|
+
if (!pendingDetails) {
|
|
1423
|
+
this.logger.debug(`Not yet accepting key bundle for room where we are not awaiting a bundle: ${roomId}`);
|
|
1424
|
+
} else if (Date.now() - pendingDetails.inviteAcceptedAtMillis > MAX_INVITE_ACCEPTANCE_MS_FOR_KEY_BUNDLE) {
|
|
1425
|
+
this.logger.info(`Ignoring key bundle for room we joined too long ago: ${roomId}, joining time: ${new Date(pendingDetails.inviteAcceptedAtMillis).toISOString()}`);
|
|
1426
|
+
} else {
|
|
1427
|
+
this.logger.info(`Considering key bundle for recently-joined room ${roomId}`);
|
|
1428
|
+
// Don't block for the import to happen, here, as this is called from inside the `/sync` loop.
|
|
1429
|
+
this.maybeAcceptKeyBundle(roomId, pendingDetails.inviterId.toString()).catch(err => {
|
|
1430
|
+
this.logger.error(`Error attempting to download key bundle for room ${roomId}`);
|
|
1431
|
+
this.logger.error(err);
|
|
1432
|
+
});
|
|
1636
1433
|
}
|
|
1637
|
-
break;
|
|
1638
|
-
}
|
|
1639
|
-
case RustSdkCryptoJs.ProcessedToDeviceEventType.PlainText:
|
|
1640
|
-
{
|
|
1641
|
-
received.push({
|
|
1642
|
-
message: parsedMessage,
|
|
1643
|
-
encryptionInfo: null
|
|
1644
|
-
});
|
|
1645
|
-
break;
|
|
1646
1434
|
}
|
|
1647
|
-
case RustSdkCryptoJs.ProcessedToDeviceEventType.UnableToDecrypt:
|
|
1648
|
-
// ignore messages we cannot decrypt
|
|
1649
1435
|
break;
|
|
1650
|
-
|
|
1651
|
-
|
|
1436
|
+
}
|
|
1437
|
+
case RustSdkCryptoJs.ProcessedToDeviceEventType.PlainText:
|
|
1438
|
+
{
|
|
1439
|
+
received.push({
|
|
1440
|
+
message: parsedMessage,
|
|
1441
|
+
encryptionInfo: null
|
|
1442
|
+
});
|
|
1652
1443
|
break;
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1444
|
+
}
|
|
1445
|
+
case RustSdkCryptoJs.ProcessedToDeviceEventType.UnableToDecrypt:
|
|
1446
|
+
// ignore messages we cannot decrypt
|
|
1447
|
+
break;
|
|
1448
|
+
case RustSdkCryptoJs.ProcessedToDeviceEventType.Invalid:
|
|
1449
|
+
// ignore invalid messages
|
|
1450
|
+
break;
|
|
1657
1451
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1452
|
+
}
|
|
1453
|
+
return received;
|
|
1660
1454
|
}
|
|
1661
1455
|
|
|
1662
1456
|
/** called by the sync loop to process one time key counts and unused fallback keys
|
|
@@ -1664,18 +1458,15 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1664
1458
|
* @param oneTimeKeysCounts - the received one time key counts
|
|
1665
1459
|
* @param unusedFallbackKeys - the received unused fallback keys
|
|
1666
1460
|
*/
|
|
1667
|
-
processKeyCounts(oneTimeKeysCounts, unusedFallbackKeys) {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
})();
|
|
1461
|
+
async processKeyCounts(oneTimeKeysCounts, unusedFallbackKeys) {
|
|
1462
|
+
const mapOneTimeKeysCount = oneTimeKeysCounts && new Map(Object.entries(oneTimeKeysCounts));
|
|
1463
|
+
const setUnusedFallbackKeys = unusedFallbackKeys && new Set(unusedFallbackKeys);
|
|
1464
|
+
if (mapOneTimeKeysCount !== undefined || setUnusedFallbackKeys !== undefined) {
|
|
1465
|
+
await this.receiveSyncChanges({
|
|
1466
|
+
oneTimeKeysCounts: mapOneTimeKeysCount,
|
|
1467
|
+
unusedFallbackKeys: setUnusedFallbackKeys
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1679
1470
|
}
|
|
1680
1471
|
|
|
1681
1472
|
/** called by the sync loop to process the notification that device lists have
|
|
@@ -1683,15 +1474,11 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1683
1474
|
*
|
|
1684
1475
|
* @param deviceLists - device_lists field from /sync
|
|
1685
1476
|
*/
|
|
1686
|
-
processDeviceLists(deviceLists) {
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
yield _this62.receiveSyncChanges({
|
|
1692
|
-
devices
|
|
1693
|
-
});
|
|
1694
|
-
})();
|
|
1477
|
+
async processDeviceLists(deviceLists) {
|
|
1478
|
+
const devices = new RustSdkCryptoJs.DeviceLists(deviceLists.changed?.map(userId => new RustSdkCryptoJs.UserId(userId)), deviceLists.left?.map(userId => new RustSdkCryptoJs.UserId(userId)));
|
|
1479
|
+
await this.receiveSyncChanges({
|
|
1480
|
+
devices
|
|
1481
|
+
});
|
|
1695
1482
|
}
|
|
1696
1483
|
|
|
1697
1484
|
/** called by the sync loop on m.room.encryption events
|
|
@@ -1699,40 +1486,37 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1699
1486
|
* @param room - in which the event was received
|
|
1700
1487
|
* @param event - encryption event to be processed
|
|
1701
1488
|
*/
|
|
1702
|
-
onCryptoEvent(room, event) {
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1489
|
+
async onCryptoEvent(room, event) {
|
|
1490
|
+
const config = event.getContent();
|
|
1491
|
+
const settings = new RustSdkCryptoJs.RoomSettings();
|
|
1492
|
+
if (config.algorithm === "m.megolm.v1.aes-sha2") {
|
|
1493
|
+
settings.algorithm = RustSdkCryptoJs.EncryptionAlgorithm.MegolmV1AesSha2;
|
|
1494
|
+
} else {
|
|
1495
|
+
// Among other situations, this happens if the crypto state event is redacted.
|
|
1496
|
+
this.logger.warn(`Room ${room.roomId}: ignoring crypto event with invalid algorithm ${config.algorithm}`);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
if (config["io.element.msc4362.encrypt_state_events"] && this.enableEncryptedStateEvents) {
|
|
1500
|
+
this.logger.info("crypto Enabling state event encryption...");
|
|
1501
|
+
settings.encryptStateEvents = true;
|
|
1502
|
+
}
|
|
1503
|
+
try {
|
|
1504
|
+
settings.sessionRotationPeriodMs = config.rotation_period_ms;
|
|
1505
|
+
settings.sessionRotationPeriodMessages = config.rotation_period_msgs;
|
|
1506
|
+
await this.olmMachine.setRoomSettings(new RustSdkCryptoJs.RoomId(room.roomId), settings);
|
|
1507
|
+
} catch (e) {
|
|
1508
|
+
this.logger.warn(`Room ${room.roomId}: ignoring crypto event which caused error: ${e}`);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1726
1511
|
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
})();
|
|
1512
|
+
// If we got this far, the SDK found the event acceptable.
|
|
1513
|
+
// We need to either create or update the active RoomEncryptor.
|
|
1514
|
+
const existingEncryptor = this.roomEncryptors[room.roomId];
|
|
1515
|
+
if (existingEncryptor) {
|
|
1516
|
+
existingEncryptor.onCryptoEvent(config);
|
|
1517
|
+
} else {
|
|
1518
|
+
this.roomEncryptors[room.roomId] = new RoomEncryptor(this.logger.getChild(`[${room.roomId} encryption]`), this.olmMachine, this.keyClaimManager, this.outgoingRequestsManager, room, config);
|
|
1519
|
+
}
|
|
1736
1520
|
}
|
|
1737
1521
|
|
|
1738
1522
|
/** called by the sync loop after processing each sync.
|
|
@@ -1751,11 +1535,8 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1751
1535
|
/**
|
|
1752
1536
|
* Implementation of {@link CryptoApi#markAllTrackedUsersAsDirty}.
|
|
1753
1537
|
*/
|
|
1754
|
-
markAllTrackedUsersAsDirty() {
|
|
1755
|
-
|
|
1756
|
-
return _asyncToGenerator(function* () {
|
|
1757
|
-
yield _this64.olmMachine.markAllTrackedUsersAsDirty();
|
|
1758
|
-
})();
|
|
1538
|
+
async markAllTrackedUsersAsDirty() {
|
|
1539
|
+
await this.olmMachine.markAllTrackedUsersAsDirty();
|
|
1759
1540
|
}
|
|
1760
1541
|
|
|
1761
1542
|
/**
|
|
@@ -1766,13 +1547,13 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1766
1547
|
* content of the message; for in-room messages it is the event ID.
|
|
1767
1548
|
*/
|
|
1768
1549
|
onIncomingKeyVerificationRequest(sender, transactionId) {
|
|
1769
|
-
|
|
1550
|
+
const request = this.olmMachine.getVerificationRequest(new RustSdkCryptoJs.UserId(sender), transactionId);
|
|
1770
1551
|
if (request) {
|
|
1771
1552
|
this.emit(CryptoEvent.VerificationRequestReceived, this.makeVerificationRequest(request));
|
|
1772
1553
|
} else {
|
|
1773
1554
|
// There are multiple reasons this can happen; probably the most likely is that the event is an
|
|
1774
1555
|
// in-room event which is too old.
|
|
1775
|
-
this.logger.info(
|
|
1556
|
+
this.logger.info(`Ignoring just-received verification request ${transactionId} which did not start a rust-side verification`);
|
|
1776
1557
|
}
|
|
1777
1558
|
}
|
|
1778
1559
|
|
|
@@ -1794,16 +1575,16 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1794
1575
|
* @param oldMembership - The previous membership state. Null if it's a new member.
|
|
1795
1576
|
*/
|
|
1796
1577
|
onRoomMembership(event, member, oldMembership) {
|
|
1797
|
-
|
|
1578
|
+
const roomId = event.getRoomId();
|
|
1798
1579
|
|
|
1799
1580
|
// If it's our own membership, and we are no longer joined, clear any indication that we are waiting for a key
|
|
1800
1581
|
// bundle.
|
|
1801
1582
|
if (oldMembership === KnownMembership.Join && member.membership !== KnownMembership.Join && member.userId === this.olmMachine.userId.toString()) {
|
|
1802
1583
|
this.olmMachine.clearRoomPendingKeyBundle(new RustSdkCryptoJs.RoomId(roomId)).catch(e => {
|
|
1803
|
-
this.logger.error(
|
|
1584
|
+
this.logger.error(`Error clearing room pending key bundle indicator for ${roomId}: ${e}`);
|
|
1804
1585
|
});
|
|
1805
1586
|
}
|
|
1806
|
-
|
|
1587
|
+
const enc = this.roomEncryptors[roomId];
|
|
1807
1588
|
if (!enc) {
|
|
1808
1589
|
// not encrypting in this room
|
|
1809
1590
|
return;
|
|
@@ -1840,8 +1621,8 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1840
1621
|
return;
|
|
1841
1622
|
}
|
|
1842
1623
|
if (event.getStateKey() !== this.olmMachine.userId.toString() && event.getContent().membership !== KnownMembership.Join) {
|
|
1843
|
-
this.logger.info(
|
|
1844
|
-
this.forceDiscardSession(event.getRoomId());
|
|
1624
|
+
this.logger.info(`Rotating session for room ${event.getRoomId()} due to member leaving the room`);
|
|
1625
|
+
void this.forceDiscardSession(event.getRoomId());
|
|
1845
1626
|
}
|
|
1846
1627
|
}
|
|
1847
1628
|
|
|
@@ -1853,22 +1634,18 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1853
1634
|
*
|
|
1854
1635
|
* @param keys - details of the updated keys
|
|
1855
1636
|
*/
|
|
1856
|
-
onRoomKeysUpdated(keys) {
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
_this65.backupManager.maybeUploadKey();
|
|
1863
|
-
})();
|
|
1637
|
+
async onRoomKeysUpdated(keys) {
|
|
1638
|
+
for (const key of keys) {
|
|
1639
|
+
this.onRoomKeyUpdated(key);
|
|
1640
|
+
}
|
|
1641
|
+
void this.backupManager.maybeUploadKey();
|
|
1864
1642
|
}
|
|
1865
1643
|
onRoomKeyUpdated(key) {
|
|
1866
|
-
var _this66 = this;
|
|
1867
1644
|
if (this.stopped) return;
|
|
1868
|
-
this.logger.debug(
|
|
1869
|
-
|
|
1645
|
+
this.logger.debug(`Got update for session ${key.sessionId} from sender ${key.senderKey.toBase64()} in ${key.roomId.toString()}`);
|
|
1646
|
+
const pendingList = this.eventDecryptor.getEventsPendingRoomKey(key.roomId.toString(), key.sessionId);
|
|
1870
1647
|
if (pendingList.length === 0) return;
|
|
1871
|
-
this.logger.debug("Retrying decryption on events:", pendingList.map(e =>
|
|
1648
|
+
this.logger.debug("Retrying decryption on events:", pendingList.map(e => `${e.getId()}`));
|
|
1872
1649
|
|
|
1873
1650
|
// Have another go at decrypting events with this key.
|
|
1874
1651
|
//
|
|
@@ -1876,15 +1653,12 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1876
1653
|
// so we don't wait for the decryption to complete. In any case, there is no need to wait:
|
|
1877
1654
|
// MatrixEvent.attemptDecryption ensures that there is only one decryption attempt happening at once,
|
|
1878
1655
|
// and deduplicates repeated attempts for the same event.
|
|
1879
|
-
|
|
1880
|
-
ev.attemptDecryption(
|
|
1656
|
+
for (const ev of pendingList) {
|
|
1657
|
+
ev.attemptDecryption(this, {
|
|
1881
1658
|
isRetry: true
|
|
1882
1659
|
}).catch(_e => {
|
|
1883
|
-
|
|
1660
|
+
this.logger.info(`Still unable to decrypt event ${ev.getId()} after receiving key`);
|
|
1884
1661
|
});
|
|
1885
|
-
};
|
|
1886
|
-
for (var ev of pendingList) {
|
|
1887
|
-
_loop2(ev);
|
|
1888
1662
|
}
|
|
1889
1663
|
}
|
|
1890
1664
|
|
|
@@ -1896,25 +1670,22 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1896
1670
|
*
|
|
1897
1671
|
* @param withheld - Details of the withheld sessions.
|
|
1898
1672
|
*/
|
|
1899
|
-
onRoomKeysWithheld(withheld) {
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
// It's somewhat expected that we still can't decrypt here.
|
|
1914
|
-
});
|
|
1915
|
-
}
|
|
1673
|
+
async onRoomKeysWithheld(withheld) {
|
|
1674
|
+
for (const session of withheld) {
|
|
1675
|
+
this.logger.debug(`Got withheld message for session ${session.sessionId} in ${session.roomId.toString()}`);
|
|
1676
|
+
const pendingList = this.eventDecryptor.getEventsPendingRoomKey(session.roomId.toString(), session.sessionId);
|
|
1677
|
+
if (pendingList.length === 0) return;
|
|
1678
|
+
|
|
1679
|
+
// The easiest way to update the status of the event is to have another go at decrypting it.
|
|
1680
|
+
this.logger.debug("Retrying decryption on events:", pendingList.map(e => `${e.getId()}`));
|
|
1681
|
+
for (const ev of pendingList) {
|
|
1682
|
+
ev.attemptDecryption(this, {
|
|
1683
|
+
isRetry: true
|
|
1684
|
+
}).catch(_e => {
|
|
1685
|
+
// It's somewhat expected that we still can't decrypt here.
|
|
1686
|
+
});
|
|
1916
1687
|
}
|
|
1917
|
-
}
|
|
1688
|
+
}
|
|
1918
1689
|
}
|
|
1919
1690
|
|
|
1920
1691
|
/**
|
|
@@ -1925,19 +1696,16 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1925
1696
|
*
|
|
1926
1697
|
* @param userId - the user with the updated identity
|
|
1927
1698
|
*/
|
|
1928
|
-
onUserIdentityUpdated(userId) {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
yield _this68.checkKeyBackupAndEnable();
|
|
1939
|
-
}
|
|
1940
|
-
})();
|
|
1699
|
+
async onUserIdentityUpdated(userId) {
|
|
1700
|
+
const newVerification = await this.getUserVerificationStatus(userId.toString());
|
|
1701
|
+
this.emit(CryptoEvent.UserTrustStatusChanged, userId.toString(), newVerification);
|
|
1702
|
+
|
|
1703
|
+
// If our own user identity has changed, we may now trust the key backup where we did not before.
|
|
1704
|
+
// So, re-check the key backup status and enable it if available.
|
|
1705
|
+
if (userId.toString() === this.userId) {
|
|
1706
|
+
this.emit(CryptoEvent.KeysChanged, {});
|
|
1707
|
+
await this.checkKeyBackupAndEnable();
|
|
1708
|
+
}
|
|
1941
1709
|
}
|
|
1942
1710
|
|
|
1943
1711
|
/**
|
|
@@ -1950,12 +1718,9 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1950
1718
|
*
|
|
1951
1719
|
* @param userIds - an array of user IDs of users whose devices have updated.
|
|
1952
1720
|
*/
|
|
1953
|
-
onDevicesUpdated(userIds) {
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
_this69.emit(CryptoEvent.WillUpdateDevices, userIds, false);
|
|
1957
|
-
_this69.emit(CryptoEvent.DevicesUpdated, userIds, false);
|
|
1958
|
-
})();
|
|
1721
|
+
async onDevicesUpdated(userIds) {
|
|
1722
|
+
this.emit(CryptoEvent.WillUpdateDevices, userIds, false);
|
|
1723
|
+
this.emit(CryptoEvent.DevicesUpdated, userIds, false);
|
|
1959
1724
|
}
|
|
1960
1725
|
|
|
1961
1726
|
/**
|
|
@@ -1970,19 +1735,16 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1970
1735
|
* @param name - the secret name
|
|
1971
1736
|
* @param value - the secret value
|
|
1972
1737
|
*/
|
|
1973
|
-
handleSecretReceived(name, value) {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
}
|
|
1984
|
-
return false;
|
|
1985
|
-
})();
|
|
1738
|
+
async handleSecretReceived(name, value) {
|
|
1739
|
+
this.logger.debug(`onReceiveSecret: Received secret ${name}`);
|
|
1740
|
+
if (name === "m.megolm_backup.v1") {
|
|
1741
|
+
return await this.backupManager.handleBackupSecretReceived(value);
|
|
1742
|
+
// XXX at this point we should probably try to download the backup and import the keys,
|
|
1743
|
+
// or at least retry for the current decryption failures?
|
|
1744
|
+
// Maybe add some signaling when a new secret is received, and let clients handle it?
|
|
1745
|
+
// as it's where the restore from backup APIs are exposed.
|
|
1746
|
+
}
|
|
1747
|
+
return false;
|
|
1986
1748
|
}
|
|
1987
1749
|
|
|
1988
1750
|
/**
|
|
@@ -1992,21 +1754,18 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
1992
1754
|
*
|
|
1993
1755
|
* @param name - The name of the secret received.
|
|
1994
1756
|
*/
|
|
1995
|
-
checkSecrets(name) {
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
// It's probably the same secret shared by another device.
|
|
2003
|
-
break;
|
|
2004
|
-
}
|
|
1757
|
+
async checkSecrets(name) {
|
|
1758
|
+
const pendingValues = await this.olmMachine.getSecretsFromInbox(name);
|
|
1759
|
+
for (const value of pendingValues) {
|
|
1760
|
+
if (await this.handleSecretReceived(name, value)) {
|
|
1761
|
+
// If we have a valid secret for that name there is no point of processing the other secrets values.
|
|
1762
|
+
// It's probably the same secret shared by another device.
|
|
1763
|
+
break;
|
|
2005
1764
|
}
|
|
1765
|
+
}
|
|
2006
1766
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
})();
|
|
1767
|
+
// Important to call this after handling the secrets as good hygiene.
|
|
1768
|
+
await this.olmMachine.deleteSecretsFromInbox(name);
|
|
2010
1769
|
}
|
|
2011
1770
|
|
|
2012
1771
|
/**
|
|
@@ -2015,42 +1774,34 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
2015
1774
|
*
|
|
2016
1775
|
* @param event - live event
|
|
2017
1776
|
*/
|
|
2018
|
-
onLiveEventFromSync(event) {
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
// Process only verification event
|
|
2027
|
-
if (isVerificationEvent(event)) {
|
|
2028
|
-
yield _this72.onKeyVerificationEvent(evt);
|
|
2029
|
-
}
|
|
2030
|
-
});
|
|
2031
|
-
return function processEvent(_x2) {
|
|
2032
|
-
return _ref8.apply(this, arguments);
|
|
2033
|
-
};
|
|
2034
|
-
}();
|
|
2035
|
-
|
|
2036
|
-
// If the event is encrypted of in failure, we wait for decryption
|
|
2037
|
-
if (event.isDecryptionFailure() || event.isEncrypted()) {
|
|
2038
|
-
// 5 mins
|
|
2039
|
-
var TIMEOUT_DELAY = 5 * 60 * 1000;
|
|
2040
|
-
|
|
2041
|
-
// After 5mins, we are not expecting the event to be decrypted
|
|
2042
|
-
var timeoutId = setTimeout(() => event.off(MatrixEventEvent.Decrypted, onDecrypted), TIMEOUT_DELAY);
|
|
2043
|
-
var onDecrypted = (decryptedEvent, error) => {
|
|
2044
|
-
if (error) return;
|
|
2045
|
-
clearTimeout(timeoutId);
|
|
2046
|
-
event.off(MatrixEventEvent.Decrypted, onDecrypted);
|
|
2047
|
-
processEvent(decryptedEvent);
|
|
2048
|
-
};
|
|
2049
|
-
event.on(MatrixEventEvent.Decrypted, onDecrypted);
|
|
2050
|
-
} else {
|
|
2051
|
-
yield processEvent(event);
|
|
1777
|
+
async onLiveEventFromSync(event) {
|
|
1778
|
+
// Ignore state event or remote echo
|
|
1779
|
+
// transaction_id is provided in case of remote echo {@link https://spec.matrix.org/v1.7/client-server-api/#local-echo}
|
|
1780
|
+
if (event.isState() || !!event.getUnsigned().transaction_id) return;
|
|
1781
|
+
const processEvent = async evt => {
|
|
1782
|
+
// Process only verification event
|
|
1783
|
+
if (isVerificationEvent(event)) {
|
|
1784
|
+
await this.onKeyVerificationEvent(evt);
|
|
2052
1785
|
}
|
|
2053
|
-
}
|
|
1786
|
+
};
|
|
1787
|
+
|
|
1788
|
+
// If the event is encrypted of in failure, we wait for decryption
|
|
1789
|
+
if (event.isDecryptionFailure() || event.isEncrypted()) {
|
|
1790
|
+
// 5 mins
|
|
1791
|
+
const TIMEOUT_DELAY = 5 * 60 * 1000;
|
|
1792
|
+
|
|
1793
|
+
// After 5mins, we are not expecting the event to be decrypted
|
|
1794
|
+
const timeoutId = setTimeout(() => event.off(MatrixEventEvent.Decrypted, onDecrypted), TIMEOUT_DELAY);
|
|
1795
|
+
const onDecrypted = (decryptedEvent, error) => {
|
|
1796
|
+
if (error) return;
|
|
1797
|
+
clearTimeout(timeoutId);
|
|
1798
|
+
event.off(MatrixEventEvent.Decrypted, onDecrypted);
|
|
1799
|
+
void processEvent(decryptedEvent);
|
|
1800
|
+
};
|
|
1801
|
+
event.on(MatrixEventEvent.Decrypted, onDecrypted);
|
|
1802
|
+
} else {
|
|
1803
|
+
await processEvent(event);
|
|
1804
|
+
}
|
|
2054
1805
|
}
|
|
2055
1806
|
|
|
2056
1807
|
/**
|
|
@@ -2058,45 +1809,42 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
2058
1809
|
*
|
|
2059
1810
|
* @param event - a key validation request event.
|
|
2060
1811
|
*/
|
|
2061
|
-
onKeyVerificationEvent(event) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
_this73.onIncomingKeyVerificationRequest(senderId, event.getId());
|
|
2093
|
-
}
|
|
1812
|
+
async onKeyVerificationEvent(event) {
|
|
1813
|
+
const roomId = event.getRoomId();
|
|
1814
|
+
const senderId = event.getSender();
|
|
1815
|
+
if (!roomId) {
|
|
1816
|
+
throw new Error("missing roomId in the event");
|
|
1817
|
+
}
|
|
1818
|
+
if (!senderId) {
|
|
1819
|
+
throw new Error("missing sender in the event");
|
|
1820
|
+
}
|
|
1821
|
+
this.logger.debug(`Incoming verification event ${event.getId()} type ${event.getType()} from ${event.getSender()}`);
|
|
1822
|
+
const isRoomVerificationRequest = event.getType() === EventType.RoomMessage && event.getContent().msgtype === MsgType.KeyVerificationRequest;
|
|
1823
|
+
if (isRoomVerificationRequest) {
|
|
1824
|
+
// Before processing an in-room verification request, we need to
|
|
1825
|
+
// make sure we have the sender's device information - otherwise we
|
|
1826
|
+
// will immediately abort verification. So we explicitly fetch it
|
|
1827
|
+
// from /keys/query and wait for that request to complete before we
|
|
1828
|
+
// call receiveVerificationEvent.
|
|
1829
|
+
const req = this.getOlmMachineOrThrow().queryKeysForUsers([new RustSdkCryptoJs.UserId(senderId)]);
|
|
1830
|
+
await this.outgoingRequestProcessor.makeOutgoingRequest(req);
|
|
1831
|
+
}
|
|
1832
|
+
await this.getOlmMachineOrThrow().receiveVerificationEvent(JSON.stringify({
|
|
1833
|
+
event_id: event.getId(),
|
|
1834
|
+
type: event.getType(),
|
|
1835
|
+
sender: senderId,
|
|
1836
|
+
state_key: event.getStateKey(),
|
|
1837
|
+
content: event.getContent(),
|
|
1838
|
+
origin_server_ts: event.getTs()
|
|
1839
|
+
}), new RustSdkCryptoJs.RoomId(roomId));
|
|
1840
|
+
if (isRoomVerificationRequest) {
|
|
1841
|
+
this.onIncomingKeyVerificationRequest(senderId, event.getId());
|
|
1842
|
+
}
|
|
2094
1843
|
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
})();
|
|
1844
|
+
// that may have caused us to queue up outgoing requests, so make sure we send them.
|
|
1845
|
+
this.outgoingRequestsManager.doProcessOutgoingRequests().catch(e => {
|
|
1846
|
+
this.logger.warn("onKeyVerificationRequest: Error processing outgoing requests", e);
|
|
1847
|
+
});
|
|
2100
1848
|
}
|
|
2101
1849
|
|
|
2102
1850
|
/**
|
|
@@ -2105,79 +1853,69 @@ export class RustCrypto extends TypedEventEmitter {
|
|
|
2105
1853
|
* Not part of the public crypto-api interface.
|
|
2106
1854
|
* Used during migration from legacy js-crypto to update local trust if needed.
|
|
2107
1855
|
*/
|
|
2108
|
-
getOwnIdentity() {
|
|
2109
|
-
|
|
2110
|
-
return
|
|
2111
|
-
var identity = yield _this74.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(_this74.userId));
|
|
2112
|
-
return identity;
|
|
2113
|
-
})();
|
|
1856
|
+
async getOwnIdentity() {
|
|
1857
|
+
const identity = await this.getOlmMachineOrThrow().getIdentity(new RustSdkCryptoJs.UserId(this.userId));
|
|
1858
|
+
return identity;
|
|
2114
1859
|
}
|
|
2115
1860
|
|
|
2116
1861
|
/**
|
|
2117
1862
|
* Push a secret to all of the current user's verified devices.
|
|
2118
1863
|
*/
|
|
2119
|
-
pushSecretToVerifiedDevices(name) {
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
logger.warn("pushSecretToVerifiedDevices: Error processing outgoing requests", e);
|
|
2127
|
-
});
|
|
2128
|
-
})();
|
|
1864
|
+
async pushSecretToVerifiedDevices(name) {
|
|
1865
|
+
const logger = new LogSpan(this.logger, "pushSecretToVerifiedDevices");
|
|
1866
|
+
await this.keyClaimManager.ensureSessionsForUsers(logger, [new RustSdkCryptoJs.UserId(this.userId)]);
|
|
1867
|
+
await this.olmMachine.pushSecretToVerifiedDevices(name);
|
|
1868
|
+
this.outgoingRequestsManager.doProcessOutgoingRequests().catch(e => {
|
|
1869
|
+
logger.warn("pushSecretToVerifiedDevices: Error processing outgoing requests", e);
|
|
1870
|
+
});
|
|
2129
1871
|
}
|
|
2130
1872
|
}
|
|
2131
1873
|
class EventDecryptor {
|
|
2132
1874
|
constructor(logger, olmMachine, perSessionBackupDownloader) {
|
|
2133
|
-
this.logger = logger;
|
|
2134
|
-
this.olmMachine = olmMachine;
|
|
2135
|
-
this.perSessionBackupDownloader = perSessionBackupDownloader;
|
|
2136
1875
|
/**
|
|
2137
1876
|
* Events which we couldn't decrypt due to unknown sessions / indexes.
|
|
2138
1877
|
*
|
|
2139
1878
|
* Map from roomId to sessionId to Set of MatrixEvents
|
|
2140
1879
|
*/
|
|
2141
1880
|
_defineProperty(this, "eventsPendingKey", new MapWithDefault(() => new MapWithDefault(() => new Set())));
|
|
1881
|
+
this.logger = logger;
|
|
1882
|
+
this.olmMachine = olmMachine;
|
|
1883
|
+
this.perSessionBackupDownloader = perSessionBackupDownloader;
|
|
2142
1884
|
}
|
|
2143
|
-
attemptEventDecryption(event, isolationMode) {
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
|
-
_this76.onMegolmDecryptionError(event, err, yield _this76.perSessionBackupDownloader.getServerBackupInfo());
|
|
2176
|
-
} else {
|
|
2177
|
-
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, "Unknown error");
|
|
2178
|
-
}
|
|
1885
|
+
async attemptEventDecryption(event, isolationMode) {
|
|
1886
|
+
// add the event to the pending list *before* attempting to decrypt.
|
|
1887
|
+
// then, if the key turns up while decryption is in progress (and
|
|
1888
|
+
// decryption fails), we will schedule a retry.
|
|
1889
|
+
// (fixes https://github.com/vector-im/element-web/issues/5001)
|
|
1890
|
+
this.addEventToPendingList(event);
|
|
1891
|
+
let trustRequirement;
|
|
1892
|
+
switch (isolationMode.kind) {
|
|
1893
|
+
case DeviceIsolationModeKind.AllDevicesIsolationMode:
|
|
1894
|
+
trustRequirement = RustSdkCryptoJs.TrustRequirement.Untrusted;
|
|
1895
|
+
break;
|
|
1896
|
+
case DeviceIsolationModeKind.OnlySignedDevicesIsolationMode:
|
|
1897
|
+
trustRequirement = RustSdkCryptoJs.TrustRequirement.CrossSignedOrLegacy;
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1900
|
+
try {
|
|
1901
|
+
const res = await this.olmMachine.decryptRoomEvent(stringifyEvent(event), new RustSdkCryptoJs.RoomId(event.getRoomId()), new RustSdkCryptoJs.DecryptionSettings(trustRequirement));
|
|
1902
|
+
|
|
1903
|
+
// Success. We can remove the event from the pending list, if
|
|
1904
|
+
// that hasn't already happened.
|
|
1905
|
+
this.removeEventFromPendingList(event);
|
|
1906
|
+
return {
|
|
1907
|
+
clearEvent: JSON.parse(res.event),
|
|
1908
|
+
claimedEd25519Key: res.senderClaimedEd25519Key,
|
|
1909
|
+
senderCurve25519Key: res.senderCurve25519Key,
|
|
1910
|
+
keyForwardedBy: res.forwarder?.toString()
|
|
1911
|
+
};
|
|
1912
|
+
} catch (err) {
|
|
1913
|
+
if (err instanceof RustSdkCryptoJs.MegolmDecryptionError) {
|
|
1914
|
+
this.onMegolmDecryptionError(event, err, await this.perSessionBackupDownloader.getServerBackupInfo());
|
|
1915
|
+
} else {
|
|
1916
|
+
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, "Unknown error");
|
|
2179
1917
|
}
|
|
2180
|
-
}
|
|
1918
|
+
}
|
|
2181
1919
|
}
|
|
2182
1920
|
|
|
2183
1921
|
/**
|
|
@@ -2191,8 +1929,8 @@ class EventDecryptor {
|
|
|
2191
1929
|
* `undefined` if our attempt to check failed.
|
|
2192
1930
|
*/
|
|
2193
1931
|
onMegolmDecryptionError(event, err, serverBackupInfo) {
|
|
2194
|
-
|
|
2195
|
-
|
|
1932
|
+
const content = event.getWireContent();
|
|
1933
|
+
const errorDetails = {
|
|
2196
1934
|
sender_key: content.sender_key,
|
|
2197
1935
|
session_id: content.session_id
|
|
2198
1936
|
};
|
|
@@ -2203,7 +1941,7 @@ class EventDecryptor {
|
|
|
2203
1941
|
|
|
2204
1942
|
// If the server is telling us our membership at the time the event
|
|
2205
1943
|
// was sent, and it isn't "join", we use a different error code.
|
|
2206
|
-
|
|
1944
|
+
const membership = event.getMembershipAtEvent();
|
|
2207
1945
|
if (membership && membership !== KnownMembership.Join && membership !== KnownMembership.Invite) {
|
|
2208
1946
|
throw new DecryptionError(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED, "This message was sent when we were not a member of the room.", errorDetails);
|
|
2209
1947
|
}
|
|
@@ -2224,7 +1962,7 @@ class EventDecryptor {
|
|
|
2224
1962
|
if (err.maybe_withheld) {
|
|
2225
1963
|
// Unfortunately the Rust SDK API doesn't let us distinguish between different withheld cases, other than
|
|
2226
1964
|
// by string-matching.
|
|
2227
|
-
|
|
1965
|
+
const failureCode = err.maybe_withheld === "The sender has disabled encrypting to unverified devices." ? DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE : DecryptionFailureCode.MEGOLM_KEY_WITHHELD;
|
|
2228
1966
|
throw new DecryptionError(failureCode, err.maybe_withheld, errorDetails);
|
|
2229
1967
|
}
|
|
2230
1968
|
switch (err.code) {
|
|
@@ -2257,24 +1995,21 @@ class EventDecryptor {
|
|
|
2257
1995
|
throw new DecryptionError(DecryptionFailureCode.UNKNOWN_ERROR, err.description, errorDetails);
|
|
2258
1996
|
}
|
|
2259
1997
|
}
|
|
2260
|
-
getEncryptionInfoForEvent(event) {
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
return null;
|
|
2266
|
-
}
|
|
1998
|
+
async getEncryptionInfoForEvent(event) {
|
|
1999
|
+
if (!event.getClearContent() || event.isDecryptionFailure()) {
|
|
2000
|
+
// not successfully decrypted
|
|
2001
|
+
return null;
|
|
2002
|
+
}
|
|
2267
2003
|
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
})();
|
|
2004
|
+
// special-case outgoing events, which the rust crypto-sdk will barf on
|
|
2005
|
+
if (event.status !== null) {
|
|
2006
|
+
return {
|
|
2007
|
+
shieldColour: EventShieldColour.NONE,
|
|
2008
|
+
shieldReason: null
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
const encryptionInfo = await this.olmMachine.getRoomEventEncryptionInfo(stringifyEvent(event), new RustSdkCryptoJs.RoomId(event.getRoomId()));
|
|
2012
|
+
return rustEncryptionInfoToJsEncryptionInfo(this.logger, encryptionInfo);
|
|
2278
2013
|
}
|
|
2279
2014
|
|
|
2280
2015
|
/**
|
|
@@ -2283,9 +2018,9 @@ class EventDecryptor {
|
|
|
2283
2018
|
* Returns a list of events which were encrypted by `session` and could not be decrypted
|
|
2284
2019
|
*/
|
|
2285
2020
|
getEventsPendingRoomKey(roomId, sessionId) {
|
|
2286
|
-
|
|
2021
|
+
const roomPendingEvents = this.eventsPendingKey.get(roomId);
|
|
2287
2022
|
if (!roomPendingEvents) return [];
|
|
2288
|
-
|
|
2023
|
+
const sessionPendingEvents = roomPendingEvents.get(sessionId);
|
|
2289
2024
|
if (!sessionPendingEvents) return [];
|
|
2290
2025
|
return [...sessionPendingEvents];
|
|
2291
2026
|
}
|
|
@@ -2294,11 +2029,11 @@ class EventDecryptor {
|
|
|
2294
2029
|
* Add an event to the list of those awaiting their session keys.
|
|
2295
2030
|
*/
|
|
2296
2031
|
addEventToPendingList(event) {
|
|
2297
|
-
|
|
2032
|
+
const roomId = event.getRoomId();
|
|
2298
2033
|
// We shouldn't have events without a room id here.
|
|
2299
2034
|
if (!roomId) return;
|
|
2300
|
-
|
|
2301
|
-
|
|
2035
|
+
const roomPendingEvents = this.eventsPendingKey.getOrCreate(roomId);
|
|
2036
|
+
const sessionPendingEvents = roomPendingEvents.getOrCreate(event.getWireContent().session_id);
|
|
2302
2037
|
sessionPendingEvents.add(event);
|
|
2303
2038
|
}
|
|
2304
2039
|
|
|
@@ -2306,11 +2041,11 @@ class EventDecryptor {
|
|
|
2306
2041
|
* Remove an event from the list of those awaiting their session keys.
|
|
2307
2042
|
*/
|
|
2308
2043
|
removeEventFromPendingList(event) {
|
|
2309
|
-
|
|
2044
|
+
const roomId = event.getRoomId();
|
|
2310
2045
|
if (!roomId) return;
|
|
2311
|
-
|
|
2046
|
+
const roomPendingEvents = this.eventsPendingKey.getOrCreate(roomId);
|
|
2312
2047
|
if (!roomPendingEvents) return;
|
|
2313
|
-
|
|
2048
|
+
const sessionPendingEvents = roomPendingEvents.get(event.getWireContent().session_id);
|
|
2314
2049
|
if (!sessionPendingEvents) return;
|
|
2315
2050
|
sessionPendingEvents.delete(event);
|
|
2316
2051
|
|
|
@@ -2340,8 +2075,8 @@ function rustEncryptionInfoToJsEncryptionInfo(logger, encryptionInfo) {
|
|
|
2340
2075
|
}
|
|
2341
2076
|
|
|
2342
2077
|
// TODO: use strict shield semantics.
|
|
2343
|
-
|
|
2344
|
-
|
|
2078
|
+
const shieldState = encryptionInfo.shieldState(false);
|
|
2079
|
+
let shieldColour;
|
|
2345
2080
|
switch (shieldState.color) {
|
|
2346
2081
|
case RustSdkCryptoJs.ShieldColor.Grey:
|
|
2347
2082
|
shieldColour = EventShieldColour.GREY;
|
|
@@ -2352,7 +2087,7 @@ function rustEncryptionInfoToJsEncryptionInfo(logger, encryptionInfo) {
|
|
|
2352
2087
|
default:
|
|
2353
2088
|
shieldColour = EventShieldColour.RED;
|
|
2354
2089
|
}
|
|
2355
|
-
|
|
2090
|
+
let shieldReason;
|
|
2356
2091
|
switch (shieldState.code) {
|
|
2357
2092
|
case undefined:
|
|
2358
2093
|
case null:
|