@unwanted/matrix-sdk-mini 34.12.0-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (1203) hide show
  1. package/CHANGELOG.md +5910 -0
  2. package/LICENSE +177 -0
  3. package/README.md +459 -0
  4. package/git-revision.txt +1 -0
  5. package/lib/@types/AESEncryptedSecretStoragePayload.d.ts +14 -0
  6. package/lib/@types/AESEncryptedSecretStoragePayload.d.ts.map +1 -0
  7. package/lib/@types/AESEncryptedSecretStoragePayload.js +1 -0
  8. package/lib/@types/AESEncryptedSecretStoragePayload.js.map +1 -0
  9. package/lib/@types/IIdentityServerProvider.d.ts +9 -0
  10. package/lib/@types/IIdentityServerProvider.d.ts.map +1 -0
  11. package/lib/@types/IIdentityServerProvider.js +1 -0
  12. package/lib/@types/IIdentityServerProvider.js.map +1 -0
  13. package/lib/@types/PushRules.d.ts +140 -0
  14. package/lib/@types/PushRules.d.ts.map +1 -0
  15. package/lib/@types/PushRules.js +94 -0
  16. package/lib/@types/PushRules.js.map +1 -0
  17. package/lib/@types/another-json.d.js +0 -0
  18. package/lib/@types/another-json.d.js.map +1 -0
  19. package/lib/@types/auth.d.ts +208 -0
  20. package/lib/@types/auth.d.ts.map +1 -0
  21. package/lib/@types/auth.js +99 -0
  22. package/lib/@types/auth.js.map +1 -0
  23. package/lib/@types/beacon.d.ts +106 -0
  24. package/lib/@types/beacon.d.ts.map +1 -0
  25. package/lib/@types/beacon.js +119 -0
  26. package/lib/@types/beacon.js.map +1 -0
  27. package/lib/@types/common.d.ts +9 -0
  28. package/lib/@types/common.d.ts.map +1 -0
  29. package/lib/@types/common.js +1 -0
  30. package/lib/@types/common.js.map +1 -0
  31. package/lib/@types/crypto.d.ts +47 -0
  32. package/lib/@types/crypto.d.ts.map +1 -0
  33. package/lib/@types/crypto.js +1 -0
  34. package/lib/@types/crypto.js.map +1 -0
  35. package/lib/@types/event.d.ts +258 -0
  36. package/lib/@types/event.d.ts.map +1 -0
  37. package/lib/@types/event.js +239 -0
  38. package/lib/@types/event.js.map +1 -0
  39. package/lib/@types/events.d.ts +92 -0
  40. package/lib/@types/events.d.ts.map +1 -0
  41. package/lib/@types/events.js +1 -0
  42. package/lib/@types/events.js.map +1 -0
  43. package/lib/@types/extensible_events.d.ts +98 -0
  44. package/lib/@types/extensible_events.d.ts.map +1 -0
  45. package/lib/@types/extensible_events.js +116 -0
  46. package/lib/@types/extensible_events.js.map +1 -0
  47. package/lib/@types/global.d.js +20 -0
  48. package/lib/@types/global.d.js.map +1 -0
  49. package/lib/@types/local_notifications.d.ts +4 -0
  50. package/lib/@types/local_notifications.d.ts.map +1 -0
  51. package/lib/@types/local_notifications.js +1 -0
  52. package/lib/@types/local_notifications.js.map +1 -0
  53. package/lib/@types/location.d.ts +60 -0
  54. package/lib/@types/location.d.ts.map +1 -0
  55. package/lib/@types/location.js +67 -0
  56. package/lib/@types/location.js.map +1 -0
  57. package/lib/@types/matrix-sdk-crypto-wasm.d.js +1 -0
  58. package/lib/@types/matrix-sdk-crypto-wasm.d.js.map +1 -0
  59. package/lib/@types/media.d.ts +220 -0
  60. package/lib/@types/media.d.ts.map +1 -0
  61. package/lib/@types/media.js +1 -0
  62. package/lib/@types/media.js.map +1 -0
  63. package/lib/@types/membership.d.ts +41 -0
  64. package/lib/@types/membership.d.ts.map +1 -0
  65. package/lib/@types/membership.js +37 -0
  66. package/lib/@types/membership.js.map +1 -0
  67. package/lib/@types/oidc-client-ts.d.js +18 -0
  68. package/lib/@types/oidc-client-ts.d.js.map +1 -0
  69. package/lib/@types/partials.d.ts +39 -0
  70. package/lib/@types/partials.d.ts.map +1 -0
  71. package/lib/@types/partials.js +53 -0
  72. package/lib/@types/partials.js.map +1 -0
  73. package/lib/@types/polls.d.ts +88 -0
  74. package/lib/@types/polls.d.ts.map +1 -0
  75. package/lib/@types/polls.js +86 -0
  76. package/lib/@types/polls.js.map +1 -0
  77. package/lib/@types/read_receipts.d.ts +36 -0
  78. package/lib/@types/read_receipts.d.ts.map +1 -0
  79. package/lib/@types/read_receipts.js +27 -0
  80. package/lib/@types/read_receipts.js.map +1 -0
  81. package/lib/@types/registration.d.ts +85 -0
  82. package/lib/@types/registration.d.ts.map +1 -0
  83. package/lib/@types/registration.js +1 -0
  84. package/lib/@types/registration.js.map +1 -0
  85. package/lib/@types/requests.d.ts +241 -0
  86. package/lib/@types/requests.d.ts.map +1 -0
  87. package/lib/@types/requests.js +28 -0
  88. package/lib/@types/requests.js.map +1 -0
  89. package/lib/@types/search.d.ts +90 -0
  90. package/lib/@types/search.d.ts.map +1 -0
  91. package/lib/@types/search.js +30 -0
  92. package/lib/@types/search.js.map +1 -0
  93. package/lib/@types/signed.d.ts +9 -0
  94. package/lib/@types/signed.d.ts.map +1 -0
  95. package/lib/@types/signed.js +1 -0
  96. package/lib/@types/signed.js.map +1 -0
  97. package/lib/@types/spaces.d.ts +16 -0
  98. package/lib/@types/spaces.d.ts.map +1 -0
  99. package/lib/@types/spaces.js +1 -0
  100. package/lib/@types/spaces.js.map +1 -0
  101. package/lib/@types/state_events.d.ts +116 -0
  102. package/lib/@types/state_events.d.ts.map +1 -0
  103. package/lib/@types/state_events.js +1 -0
  104. package/lib/@types/state_events.js.map +1 -0
  105. package/lib/@types/synapse.d.ts +19 -0
  106. package/lib/@types/synapse.d.ts.map +1 -0
  107. package/lib/@types/synapse.js +1 -0
  108. package/lib/@types/synapse.js.map +1 -0
  109. package/lib/@types/sync.d.ts +8 -0
  110. package/lib/@types/sync.d.ts.map +1 -0
  111. package/lib/@types/sync.js +25 -0
  112. package/lib/@types/sync.js.map +1 -0
  113. package/lib/@types/threepids.d.ts +12 -0
  114. package/lib/@types/threepids.d.ts.map +1 -0
  115. package/lib/@types/threepids.js +24 -0
  116. package/lib/@types/threepids.js.map +1 -0
  117. package/lib/@types/topic.d.ts +48 -0
  118. package/lib/@types/topic.d.ts.map +1 -0
  119. package/lib/@types/topic.js +57 -0
  120. package/lib/@types/topic.js.map +1 -0
  121. package/lib/@types/uia.d.ts +12 -0
  122. package/lib/@types/uia.d.ts.map +1 -0
  123. package/lib/@types/uia.js +1 -0
  124. package/lib/@types/uia.js.map +1 -0
  125. package/lib/NamespacedValue.d.ts +33 -0
  126. package/lib/NamespacedValue.d.ts.map +1 -0
  127. package/lib/NamespacedValue.js +113 -0
  128. package/lib/NamespacedValue.js.map +1 -0
  129. package/lib/ReEmitter.d.ts +15 -0
  130. package/lib/ReEmitter.d.ts.map +1 -0
  131. package/lib/ReEmitter.js +87 -0
  132. package/lib/ReEmitter.js.map +1 -0
  133. package/lib/ToDeviceMessageQueue.d.ts +28 -0
  134. package/lib/ToDeviceMessageQueue.d.ts.map +1 -0
  135. package/lib/ToDeviceMessageQueue.js +135 -0
  136. package/lib/ToDeviceMessageQueue.js.map +1 -0
  137. package/lib/autodiscovery.d.ts +136 -0
  138. package/lib/autodiscovery.d.ts.map +1 -0
  139. package/lib/autodiscovery.js +464 -0
  140. package/lib/autodiscovery.js.map +1 -0
  141. package/lib/base64.d.ts +28 -0
  142. package/lib/base64.d.ts.map +1 -0
  143. package/lib/base64.js +88 -0
  144. package/lib/base64.js.map +1 -0
  145. package/lib/browser-index.d.ts +8 -0
  146. package/lib/browser-index.d.ts.map +1 -0
  147. package/lib/browser-index.js +35 -0
  148. package/lib/browser-index.js.map +1 -0
  149. package/lib/client.d.ts +4232 -0
  150. package/lib/client.d.ts.map +1 -0
  151. package/lib/client.js +8622 -0
  152. package/lib/client.js.map +1 -0
  153. package/lib/common-crypto/CryptoBackend.d.ts +240 -0
  154. package/lib/common-crypto/CryptoBackend.d.ts.map +1 -0
  155. package/lib/common-crypto/CryptoBackend.js +73 -0
  156. package/lib/common-crypto/CryptoBackend.js.map +1 -0
  157. package/lib/common-crypto/key-passphrase.d.ts +14 -0
  158. package/lib/common-crypto/key-passphrase.d.ts.map +1 -0
  159. package/lib/common-crypto/key-passphrase.js +33 -0
  160. package/lib/common-crypto/key-passphrase.js.map +1 -0
  161. package/lib/content-helpers.d.ts +90 -0
  162. package/lib/content-helpers.d.ts.map +1 -0
  163. package/lib/content-helpers.js +250 -0
  164. package/lib/content-helpers.js.map +1 -0
  165. package/lib/content-repo.d.ts +24 -0
  166. package/lib/content-repo.d.ts.map +1 -0
  167. package/lib/content-repo.js +104 -0
  168. package/lib/content-repo.js.map +1 -0
  169. package/lib/crypto/CrossSigning.d.ts +184 -0
  170. package/lib/crypto/CrossSigning.d.ts.map +1 -0
  171. package/lib/crypto/CrossSigning.js +718 -0
  172. package/lib/crypto/CrossSigning.js.map +1 -0
  173. package/lib/crypto/DeviceList.d.ts +216 -0
  174. package/lib/crypto/DeviceList.d.ts.map +1 -0
  175. package/lib/crypto/DeviceList.js +892 -0
  176. package/lib/crypto/DeviceList.js.map +1 -0
  177. package/lib/crypto/EncryptionSetup.d.ts +152 -0
  178. package/lib/crypto/EncryptionSetup.d.ts.map +1 -0
  179. package/lib/crypto/EncryptionSetup.js +356 -0
  180. package/lib/crypto/EncryptionSetup.js.map +1 -0
  181. package/lib/crypto/OlmDevice.d.ts +457 -0
  182. package/lib/crypto/OlmDevice.d.ts.map +1 -0
  183. package/lib/crypto/OlmDevice.js +1241 -0
  184. package/lib/crypto/OlmDevice.js.map +1 -0
  185. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts +109 -0
  186. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts.map +1 -0
  187. package/lib/crypto/OutgoingRoomKeyRequestManager.js +415 -0
  188. package/lib/crypto/OutgoingRoomKeyRequestManager.js.map +1 -0
  189. package/lib/crypto/RoomList.d.ts +26 -0
  190. package/lib/crypto/RoomList.d.ts.map +1 -0
  191. package/lib/crypto/RoomList.js +71 -0
  192. package/lib/crypto/RoomList.js.map +1 -0
  193. package/lib/crypto/SecretSharing.d.ts +24 -0
  194. package/lib/crypto/SecretSharing.d.ts.map +1 -0
  195. package/lib/crypto/SecretSharing.js +194 -0
  196. package/lib/crypto/SecretSharing.js.map +1 -0
  197. package/lib/crypto/SecretStorage.d.ts +55 -0
  198. package/lib/crypto/SecretStorage.d.ts.map +1 -0
  199. package/lib/crypto/SecretStorage.js +118 -0
  200. package/lib/crypto/SecretStorage.js.map +1 -0
  201. package/lib/crypto/aes.d.ts +6 -0
  202. package/lib/crypto/aes.d.ts.map +1 -0
  203. package/lib/crypto/aes.js +24 -0
  204. package/lib/crypto/aes.js.map +1 -0
  205. package/lib/crypto/algorithms/base.d.ts +156 -0
  206. package/lib/crypto/algorithms/base.d.ts.map +1 -0
  207. package/lib/crypto/algorithms/base.js +187 -0
  208. package/lib/crypto/algorithms/base.js.map +1 -0
  209. package/lib/crypto/algorithms/index.d.ts +4 -0
  210. package/lib/crypto/algorithms/index.d.ts.map +1 -0
  211. package/lib/crypto/algorithms/index.js +20 -0
  212. package/lib/crypto/algorithms/index.js.map +1 -0
  213. package/lib/crypto/algorithms/megolm.d.ts +385 -0
  214. package/lib/crypto/algorithms/megolm.d.ts.map +1 -0
  215. package/lib/crypto/algorithms/megolm.js +1822 -0
  216. package/lib/crypto/algorithms/megolm.js.map +1 -0
  217. package/lib/crypto/algorithms/olm.d.ts +5 -0
  218. package/lib/crypto/algorithms/olm.d.ts.map +1 -0
  219. package/lib/crypto/algorithms/olm.js +299 -0
  220. package/lib/crypto/algorithms/olm.js.map +1 -0
  221. package/lib/crypto/api.d.ts +32 -0
  222. package/lib/crypto/api.d.ts.map +1 -0
  223. package/lib/crypto/api.js +22 -0
  224. package/lib/crypto/api.js.map +1 -0
  225. package/lib/crypto/backup.d.ts +227 -0
  226. package/lib/crypto/backup.d.ts.map +1 -0
  227. package/lib/crypto/backup.js +824 -0
  228. package/lib/crypto/backup.js.map +1 -0
  229. package/lib/crypto/crypto.d.ts +3 -0
  230. package/lib/crypto/crypto.d.ts.map +1 -0
  231. package/lib/crypto/crypto.js +19 -0
  232. package/lib/crypto/crypto.js.map +1 -0
  233. package/lib/crypto/dehydration.d.ts +34 -0
  234. package/lib/crypto/dehydration.d.ts.map +1 -0
  235. package/lib/crypto/dehydration.js +252 -0
  236. package/lib/crypto/dehydration.js.map +1 -0
  237. package/lib/crypto/device-converter.d.ts +9 -0
  238. package/lib/crypto/device-converter.d.ts.map +1 -0
  239. package/lib/crypto/device-converter.js +42 -0
  240. package/lib/crypto/device-converter.js.map +1 -0
  241. package/lib/crypto/deviceinfo.d.ts +99 -0
  242. package/lib/crypto/deviceinfo.d.ts.map +1 -0
  243. package/lib/crypto/deviceinfo.js +148 -0
  244. package/lib/crypto/deviceinfo.js.map +1 -0
  245. package/lib/crypto/index.d.ts +1209 -0
  246. package/lib/crypto/index.d.ts.map +1 -0
  247. package/lib/crypto/index.js +4097 -0
  248. package/lib/crypto/index.js.map +1 -0
  249. package/lib/crypto/key_passphrase.d.ts +14 -0
  250. package/lib/crypto/key_passphrase.d.ts.map +1 -0
  251. package/lib/crypto/key_passphrase.js +44 -0
  252. package/lib/crypto/key_passphrase.js.map +1 -0
  253. package/lib/crypto/keybackup.d.ts +18 -0
  254. package/lib/crypto/keybackup.d.ts.map +1 -0
  255. package/lib/crypto/keybackup.js +1 -0
  256. package/lib/crypto/keybackup.js.map +1 -0
  257. package/lib/crypto/olmlib.d.ts +129 -0
  258. package/lib/crypto/olmlib.d.ts.map +1 -0
  259. package/lib/crypto/olmlib.js +492 -0
  260. package/lib/crypto/olmlib.js.map +1 -0
  261. package/lib/crypto/recoverykey.d.ts +2 -0
  262. package/lib/crypto/recoverykey.d.ts.map +1 -0
  263. package/lib/crypto/recoverykey.js +19 -0
  264. package/lib/crypto/recoverykey.js.map +1 -0
  265. package/lib/crypto/store/base.d.ts +252 -0
  266. package/lib/crypto/store/base.d.ts.map +1 -0
  267. package/lib/crypto/store/base.js +64 -0
  268. package/lib/crypto/store/base.js.map +1 -0
  269. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts +187 -0
  270. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts.map +1 -0
  271. package/lib/crypto/store/indexeddb-crypto-store-backend.js +1145 -0
  272. package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +1 -0
  273. package/lib/crypto/store/indexeddb-crypto-store.d.ts +432 -0
  274. package/lib/crypto/store/indexeddb-crypto-store.d.ts.map +1 -0
  275. package/lib/crypto/store/indexeddb-crypto-store.js +728 -0
  276. package/lib/crypto/store/indexeddb-crypto-store.js.map +1 -0
  277. package/lib/crypto/store/localStorage-crypto-store.d.ts +119 -0
  278. package/lib/crypto/store/localStorage-crypto-store.d.ts.map +1 -0
  279. package/lib/crypto/store/localStorage-crypto-store.js +531 -0
  280. package/lib/crypto/store/localStorage-crypto-store.js.map +1 -0
  281. package/lib/crypto/store/memory-crypto-store.d.ts +215 -0
  282. package/lib/crypto/store/memory-crypto-store.d.ts.map +1 -0
  283. package/lib/crypto/store/memory-crypto-store.js +622 -0
  284. package/lib/crypto/store/memory-crypto-store.js.map +1 -0
  285. package/lib/crypto/verification/Base.d.ts +105 -0
  286. package/lib/crypto/verification/Base.d.ts.map +1 -0
  287. package/lib/crypto/verification/Base.js +372 -0
  288. package/lib/crypto/verification/Base.js.map +1 -0
  289. package/lib/crypto/verification/Error.d.ts +35 -0
  290. package/lib/crypto/verification/Error.d.ts.map +1 -0
  291. package/lib/crypto/verification/Error.js +86 -0
  292. package/lib/crypto/verification/Error.js.map +1 -0
  293. package/lib/crypto/verification/IllegalMethod.d.ts +15 -0
  294. package/lib/crypto/verification/IllegalMethod.d.ts.map +1 -0
  295. package/lib/crypto/verification/IllegalMethod.js +43 -0
  296. package/lib/crypto/verification/IllegalMethod.js.map +1 -0
  297. package/lib/crypto/verification/QRCode.d.ts +51 -0
  298. package/lib/crypto/verification/QRCode.d.ts.map +1 -0
  299. package/lib/crypto/verification/QRCode.js +277 -0
  300. package/lib/crypto/verification/QRCode.js.map +1 -0
  301. package/lib/crypto/verification/SAS.d.ts +27 -0
  302. package/lib/crypto/verification/SAS.d.ts.map +1 -0
  303. package/lib/crypto/verification/SAS.js +485 -0
  304. package/lib/crypto/verification/SAS.js.map +1 -0
  305. package/lib/crypto/verification/SASDecimal.d.ts +8 -0
  306. package/lib/crypto/verification/SASDecimal.d.ts.map +1 -0
  307. package/lib/crypto/verification/SASDecimal.js +34 -0
  308. package/lib/crypto/verification/SASDecimal.js.map +1 -0
  309. package/lib/crypto/verification/request/Channel.d.ts +18 -0
  310. package/lib/crypto/verification/request/Channel.d.ts.map +1 -0
  311. package/lib/crypto/verification/request/Channel.js +1 -0
  312. package/lib/crypto/verification/request/Channel.js.map +1 -0
  313. package/lib/crypto/verification/request/InRoomChannel.d.ts +113 -0
  314. package/lib/crypto/verification/request/InRoomChannel.d.ts.map +1 -0
  315. package/lib/crypto/verification/request/InRoomChannel.js +351 -0
  316. package/lib/crypto/verification/request/InRoomChannel.js.map +1 -0
  317. package/lib/crypto/verification/request/ToDeviceChannel.d.ts +105 -0
  318. package/lib/crypto/verification/request/ToDeviceChannel.d.ts.map +1 -0
  319. package/lib/crypto/verification/request/ToDeviceChannel.js +328 -0
  320. package/lib/crypto/verification/request/ToDeviceChannel.js.map +1 -0
  321. package/lib/crypto/verification/request/VerificationRequest.d.ts +227 -0
  322. package/lib/crypto/verification/request/VerificationRequest.d.ts.map +1 -0
  323. package/lib/crypto/verification/request/VerificationRequest.js +937 -0
  324. package/lib/crypto/verification/request/VerificationRequest.js.map +1 -0
  325. package/lib/crypto-api/CryptoEvent.d.ts +69 -0
  326. package/lib/crypto-api/CryptoEvent.d.ts.map +1 -0
  327. package/lib/crypto-api/CryptoEvent.js +33 -0
  328. package/lib/crypto-api/CryptoEvent.js.map +1 -0
  329. package/lib/crypto-api/CryptoEventHandlerMap.d.ts +16 -0
  330. package/lib/crypto-api/CryptoEventHandlerMap.d.ts.map +1 -0
  331. package/lib/crypto-api/CryptoEventHandlerMap.js +22 -0
  332. package/lib/crypto-api/CryptoEventHandlerMap.js.map +1 -0
  333. package/lib/crypto-api/index.d.ts +978 -0
  334. package/lib/crypto-api/index.d.ts.map +1 -0
  335. package/lib/crypto-api/index.js +304 -0
  336. package/lib/crypto-api/index.js.map +1 -0
  337. package/lib/crypto-api/key-passphrase.d.ts +11 -0
  338. package/lib/crypto-api/key-passphrase.d.ts.map +1 -0
  339. package/lib/crypto-api/key-passphrase.js +51 -0
  340. package/lib/crypto-api/key-passphrase.js.map +1 -0
  341. package/lib/crypto-api/keybackup.d.ts +88 -0
  342. package/lib/crypto-api/keybackup.d.ts.map +1 -0
  343. package/lib/crypto-api/keybackup.js +1 -0
  344. package/lib/crypto-api/keybackup.js.map +1 -0
  345. package/lib/crypto-api/recovery-key.d.ts +11 -0
  346. package/lib/crypto-api/recovery-key.d.ts.map +1 -0
  347. package/lib/crypto-api/recovery-key.js +65 -0
  348. package/lib/crypto-api/recovery-key.js.map +1 -0
  349. package/lib/crypto-api/verification.d.ts +344 -0
  350. package/lib/crypto-api/verification.d.ts.map +1 -0
  351. package/lib/crypto-api/verification.js +91 -0
  352. package/lib/crypto-api/verification.js.map +1 -0
  353. package/lib/digest.d.ts +10 -0
  354. package/lib/digest.d.ts.map +1 -0
  355. package/lib/digest.js +40 -0
  356. package/lib/digest.js.map +1 -0
  357. package/lib/embedded.d.ts +143 -0
  358. package/lib/embedded.d.ts.map +1 -0
  359. package/lib/embedded.js +567 -0
  360. package/lib/embedded.js.map +1 -0
  361. package/lib/errors.d.ts +24 -0
  362. package/lib/errors.d.ts.map +1 -0
  363. package/lib/errors.js +51 -0
  364. package/lib/errors.js.map +1 -0
  365. package/lib/event-mapper.d.ts +10 -0
  366. package/lib/event-mapper.d.ts.map +1 -0
  367. package/lib/event-mapper.js +81 -0
  368. package/lib/event-mapper.js.map +1 -0
  369. package/lib/extensible_events_v1/ExtensibleEvent.d.ts +38 -0
  370. package/lib/extensible_events_v1/ExtensibleEvent.d.ts.map +1 -0
  371. package/lib/extensible_events_v1/ExtensibleEvent.js +57 -0
  372. package/lib/extensible_events_v1/ExtensibleEvent.js.map +1 -0
  373. package/lib/extensible_events_v1/InvalidEventError.d.ts +7 -0
  374. package/lib/extensible_events_v1/InvalidEventError.d.ts.map +1 -0
  375. package/lib/extensible_events_v1/InvalidEventError.js +25 -0
  376. package/lib/extensible_events_v1/InvalidEventError.js.map +1 -0
  377. package/lib/extensible_events_v1/MessageEvent.d.ts +45 -0
  378. package/lib/extensible_events_v1/MessageEvent.d.ts.map +1 -0
  379. package/lib/extensible_events_v1/MessageEvent.js +134 -0
  380. package/lib/extensible_events_v1/MessageEvent.js.map +1 -0
  381. package/lib/extensible_events_v1/PollEndEvent.d.ts +33 -0
  382. package/lib/extensible_events_v1/PollEndEvent.d.ts.map +1 -0
  383. package/lib/extensible_events_v1/PollEndEvent.js +88 -0
  384. package/lib/extensible_events_v1/PollEndEvent.js.map +1 -0
  385. package/lib/extensible_events_v1/PollResponseEvent.d.ts +49 -0
  386. package/lib/extensible_events_v1/PollResponseEvent.d.ts.map +1 -0
  387. package/lib/extensible_events_v1/PollResponseEvent.js +135 -0
  388. package/lib/extensible_events_v1/PollResponseEvent.js.map +1 -0
  389. package/lib/extensible_events_v1/PollStartEvent.d.ts +71 -0
  390. package/lib/extensible_events_v1/PollStartEvent.d.ts.map +1 -0
  391. package/lib/extensible_events_v1/PollStartEvent.js +185 -0
  392. package/lib/extensible_events_v1/PollStartEvent.js.map +1 -0
  393. package/lib/extensible_events_v1/utilities.d.ts +14 -0
  394. package/lib/extensible_events_v1/utilities.d.ts.map +1 -0
  395. package/lib/extensible_events_v1/utilities.js +34 -0
  396. package/lib/extensible_events_v1/utilities.js.map +1 -0
  397. package/lib/feature.d.ts +20 -0
  398. package/lib/feature.d.ts.map +1 -0
  399. package/lib/feature.js +85 -0
  400. package/lib/feature.js.map +1 -0
  401. package/lib/filter-component.d.ts +64 -0
  402. package/lib/filter-component.d.ts.map +1 -0
  403. package/lib/filter-component.js +167 -0
  404. package/lib/filter-component.js.map +1 -0
  405. package/lib/filter.d.ts +97 -0
  406. package/lib/filter.d.ts.map +1 -0
  407. package/lib/filter.js +207 -0
  408. package/lib/filter.js.map +1 -0
  409. package/lib/http-api/errors.d.ts +80 -0
  410. package/lib/http-api/errors.d.ts.map +1 -0
  411. package/lib/http-api/errors.js +185 -0
  412. package/lib/http-api/errors.js.map +1 -0
  413. package/lib/http-api/fetch.d.ts +114 -0
  414. package/lib/http-api/fetch.d.ts.map +1 -0
  415. package/lib/http-api/fetch.js +346 -0
  416. package/lib/http-api/fetch.js.map +1 -0
  417. package/lib/http-api/index.d.ts +33 -0
  418. package/lib/http-api/index.d.ts.map +1 -0
  419. package/lib/http-api/index.js +180 -0
  420. package/lib/http-api/index.js.map +1 -0
  421. package/lib/http-api/interface.d.ts +142 -0
  422. package/lib/http-api/interface.d.ts.map +1 -0
  423. package/lib/http-api/interface.js +35 -0
  424. package/lib/http-api/interface.js.map +1 -0
  425. package/lib/http-api/method.d.ts +10 -0
  426. package/lib/http-api/method.d.ts.map +1 -0
  427. package/lib/http-api/method.js +27 -0
  428. package/lib/http-api/method.js.map +1 -0
  429. package/lib/http-api/prefix.d.ts +31 -0
  430. package/lib/http-api/prefix.d.ts.map +1 -0
  431. package/lib/http-api/prefix.js +32 -0
  432. package/lib/http-api/prefix.js.map +1 -0
  433. package/lib/http-api/utils.d.ts +37 -0
  434. package/lib/http-api/utils.d.ts.map +1 -0
  435. package/lib/http-api/utils.js +178 -0
  436. package/lib/http-api/utils.js.map +1 -0
  437. package/lib/index.d.ts +4 -0
  438. package/lib/index.d.ts.map +1 -0
  439. package/lib/index.js +24 -0
  440. package/lib/index.js.map +1 -0
  441. package/lib/indexeddb-helpers.d.ts +10 -0
  442. package/lib/indexeddb-helpers.d.ts.map +1 -0
  443. package/lib/indexeddb-helpers.js +51 -0
  444. package/lib/indexeddb-helpers.js.map +1 -0
  445. package/lib/indexeddb-worker.d.ts +7 -0
  446. package/lib/indexeddb-worker.d.ts.map +1 -0
  447. package/lib/indexeddb-worker.js +25 -0
  448. package/lib/indexeddb-worker.js.map +1 -0
  449. package/lib/interactive-auth.d.ts +337 -0
  450. package/lib/interactive-auth.d.ts.map +1 -0
  451. package/lib/interactive-auth.js +557 -0
  452. package/lib/interactive-auth.js.map +1 -0
  453. package/lib/logger.d.ts +81 -0
  454. package/lib/logger.d.ts.map +1 -0
  455. package/lib/logger.js +139 -0
  456. package/lib/logger.js.map +1 -0
  457. package/lib/matrix.d.ts +118 -0
  458. package/lib/matrix.d.ts.map +1 -0
  459. package/lib/matrix.js +146 -0
  460. package/lib/matrix.js.map +1 -0
  461. package/lib/matrixrtc/CallMembership.d.ts +66 -0
  462. package/lib/matrixrtc/CallMembership.d.ts.map +1 -0
  463. package/lib/matrixrtc/CallMembership.js +197 -0
  464. package/lib/matrixrtc/CallMembership.js.map +1 -0
  465. package/lib/matrixrtc/LivekitFocus.d.ts +16 -0
  466. package/lib/matrixrtc/LivekitFocus.d.ts.map +1 -0
  467. package/lib/matrixrtc/LivekitFocus.js +20 -0
  468. package/lib/matrixrtc/LivekitFocus.js.map +1 -0
  469. package/lib/matrixrtc/MatrixRTCSession.d.ts +295 -0
  470. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -0
  471. package/lib/matrixrtc/MatrixRTCSession.js +1043 -0
  472. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -0
  473. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +40 -0
  474. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +1 -0
  475. package/lib/matrixrtc/MatrixRTCSessionManager.js +146 -0
  476. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -0
  477. package/lib/matrixrtc/focus.d.ts +10 -0
  478. package/lib/matrixrtc/focus.d.ts.map +1 -0
  479. package/lib/matrixrtc/focus.js +1 -0
  480. package/lib/matrixrtc/focus.js.map +1 -0
  481. package/lib/matrixrtc/index.d.ts +7 -0
  482. package/lib/matrixrtc/index.d.ts.map +1 -0
  483. package/lib/matrixrtc/index.js +21 -0
  484. package/lib/matrixrtc/index.js.map +1 -0
  485. package/lib/matrixrtc/types.d.ts +19 -0
  486. package/lib/matrixrtc/types.d.ts.map +1 -0
  487. package/lib/matrixrtc/types.js +1 -0
  488. package/lib/matrixrtc/types.js.map +1 -0
  489. package/lib/models/MSC3089Branch.d.ts +98 -0
  490. package/lib/models/MSC3089Branch.d.ts.map +1 -0
  491. package/lib/models/MSC3089Branch.js +240 -0
  492. package/lib/models/MSC3089Branch.js.map +1 -0
  493. package/lib/models/MSC3089TreeSpace.d.ts +165 -0
  494. package/lib/models/MSC3089TreeSpace.d.ts.map +1 -0
  495. package/lib/models/MSC3089TreeSpace.js +520 -0
  496. package/lib/models/MSC3089TreeSpace.js.map +1 -0
  497. package/lib/models/ToDeviceMessage.d.ts +17 -0
  498. package/lib/models/ToDeviceMessage.d.ts.map +1 -0
  499. package/lib/models/ToDeviceMessage.js +1 -0
  500. package/lib/models/ToDeviceMessage.js.map +1 -0
  501. package/lib/models/beacon.d.ts +53 -0
  502. package/lib/models/beacon.d.ts.map +1 -0
  503. package/lib/models/beacon.js +174 -0
  504. package/lib/models/beacon.js.map +1 -0
  505. package/lib/models/compare-event-ordering.d.ts +24 -0
  506. package/lib/models/compare-event-ordering.d.ts.map +1 -0
  507. package/lib/models/compare-event-ordering.js +120 -0
  508. package/lib/models/compare-event-ordering.js.map +1 -0
  509. package/lib/models/device.d.ts +45 -0
  510. package/lib/models/device.d.ts.map +1 -0
  511. package/lib/models/device.js +77 -0
  512. package/lib/models/device.js.map +1 -0
  513. package/lib/models/event-context.d.ts +62 -0
  514. package/lib/models/event-context.d.ts.map +1 -0
  515. package/lib/models/event-context.js +113 -0
  516. package/lib/models/event-context.js.map +1 -0
  517. package/lib/models/event-status.d.ts +19 -0
  518. package/lib/models/event-status.d.ts.map +1 -0
  519. package/lib/models/event-status.js +30 -0
  520. package/lib/models/event-status.js.map +1 -0
  521. package/lib/models/event-timeline-set.d.ts +312 -0
  522. package/lib/models/event-timeline-set.d.ts.map +1 -0
  523. package/lib/models/event-timeline-set.js +813 -0
  524. package/lib/models/event-timeline-set.js.map +1 -0
  525. package/lib/models/event-timeline.d.ts +219 -0
  526. package/lib/models/event-timeline.d.ts.map +1 -0
  527. package/lib/models/event-timeline.js +455 -0
  528. package/lib/models/event-timeline.js.map +1 -0
  529. package/lib/models/event.d.ts +811 -0
  530. package/lib/models/event.d.ts.map +1 -0
  531. package/lib/models/event.js +1520 -0
  532. package/lib/models/event.js.map +1 -0
  533. package/lib/models/invites-ignorer.d.ts +136 -0
  534. package/lib/models/invites-ignorer.d.ts.map +1 -0
  535. package/lib/models/invites-ignorer.js +382 -0
  536. package/lib/models/invites-ignorer.js.map +1 -0
  537. package/lib/models/poll.d.ts +67 -0
  538. package/lib/models/poll.d.ts.map +1 -0
  539. package/lib/models/poll.js +241 -0
  540. package/lib/models/poll.js.map +1 -0
  541. package/lib/models/profile-keys.d.ts +8 -0
  542. package/lib/models/profile-keys.d.ts.map +1 -0
  543. package/lib/models/profile-keys.js +8 -0
  544. package/lib/models/profile-keys.js.map +1 -0
  545. package/lib/models/read-receipt.d.ts +115 -0
  546. package/lib/models/read-receipt.d.ts.map +1 -0
  547. package/lib/models/read-receipt.js +366 -0
  548. package/lib/models/read-receipt.js.map +1 -0
  549. package/lib/models/related-relations.d.ts +11 -0
  550. package/lib/models/related-relations.d.ts.map +1 -0
  551. package/lib/models/related-relations.js +33 -0
  552. package/lib/models/related-relations.js.map +1 -0
  553. package/lib/models/relations-container.d.ts +44 -0
  554. package/lib/models/relations-container.d.ts.map +1 -0
  555. package/lib/models/relations-container.js +132 -0
  556. package/lib/models/relations-container.js.map +1 -0
  557. package/lib/models/relations.d.ts +114 -0
  558. package/lib/models/relations.d.ts.map +1 -0
  559. package/lib/models/relations.js +354 -0
  560. package/lib/models/relations.js.map +1 -0
  561. package/lib/models/room-member.d.ts +204 -0
  562. package/lib/models/room-member.d.ts.map +1 -0
  563. package/lib/models/room-member.js +360 -0
  564. package/lib/models/room-member.js.map +1 -0
  565. package/lib/models/room-receipts.d.ts +39 -0
  566. package/lib/models/room-receipts.d.ts.map +1 -0
  567. package/lib/models/room-receipts.js +392 -0
  568. package/lib/models/room-receipts.js.map +1 -0
  569. package/lib/models/room-state.d.ts +468 -0
  570. package/lib/models/room-state.d.ts.map +1 -0
  571. package/lib/models/room-state.js +984 -0
  572. package/lib/models/room-state.js.map +1 -0
  573. package/lib/models/room-summary.d.ts +29 -0
  574. package/lib/models/room-summary.d.ts.map +1 -0
  575. package/lib/models/room-summary.js +28 -0
  576. package/lib/models/room-summary.js.map +1 -0
  577. package/lib/models/room.d.ts +1203 -0
  578. package/lib/models/room.d.ts.map +1 -0
  579. package/lib/models/room.js +3336 -0
  580. package/lib/models/room.js.map +1 -0
  581. package/lib/models/search-result.d.ts +20 -0
  582. package/lib/models/search-result.d.ts.map +1 -0
  583. package/lib/models/search-result.js +52 -0
  584. package/lib/models/search-result.js.map +1 -0
  585. package/lib/models/thread.d.ts +246 -0
  586. package/lib/models/thread.d.ts.map +1 -0
  587. package/lib/models/thread.js +861 -0
  588. package/lib/models/thread.js.map +1 -0
  589. package/lib/models/typed-event-emitter.d.ts +157 -0
  590. package/lib/models/typed-event-emitter.d.ts.map +1 -0
  591. package/lib/models/typed-event-emitter.js +227 -0
  592. package/lib/models/typed-event-emitter.js.map +1 -0
  593. package/lib/models/user.d.ts +195 -0
  594. package/lib/models/user.d.ts.map +1 -0
  595. package/lib/models/user.js +218 -0
  596. package/lib/models/user.js.map +1 -0
  597. package/lib/oidc/authorize.d.ts +90 -0
  598. package/lib/oidc/authorize.d.ts.map +1 -0
  599. package/lib/oidc/authorize.js +278 -0
  600. package/lib/oidc/authorize.js.map +1 -0
  601. package/lib/oidc/discovery.d.ts +14 -0
  602. package/lib/oidc/discovery.d.ts.map +1 -0
  603. package/lib/oidc/discovery.js +66 -0
  604. package/lib/oidc/discovery.js.map +1 -0
  605. package/lib/oidc/error.d.ts +18 -0
  606. package/lib/oidc/error.d.ts.map +1 -0
  607. package/lib/oidc/error.js +35 -0
  608. package/lib/oidc/error.js.map +1 -0
  609. package/lib/oidc/index.d.ts +17 -0
  610. package/lib/oidc/index.d.ts.map +1 -0
  611. package/lib/oidc/index.js +29 -0
  612. package/lib/oidc/index.js.map +1 -0
  613. package/lib/oidc/register.d.ts +43 -0
  614. package/lib/oidc/register.d.ts.map +1 -0
  615. package/lib/oidc/register.js +96 -0
  616. package/lib/oidc/register.js.map +1 -0
  617. package/lib/oidc/tokenRefresher.d.ts +69 -0
  618. package/lib/oidc/tokenRefresher.d.ts.map +1 -0
  619. package/lib/oidc/tokenRefresher.js +148 -0
  620. package/lib/oidc/tokenRefresher.js.map +1 -0
  621. package/lib/oidc/validate.d.ts +90 -0
  622. package/lib/oidc/validate.d.ts.map +1 -0
  623. package/lib/oidc/validate.js +194 -0
  624. package/lib/oidc/validate.js.map +1 -0
  625. package/lib/pushprocessor.d.ts +128 -0
  626. package/lib/pushprocessor.d.ts.map +1 -0
  627. package/lib/pushprocessor.js +685 -0
  628. package/lib/pushprocessor.js.map +1 -0
  629. package/lib/randomstring.d.ts +5 -0
  630. package/lib/randomstring.d.ts.map +1 -0
  631. package/lib/randomstring.js +43 -0
  632. package/lib/randomstring.js.map +1 -0
  633. package/lib/realtime-callbacks.d.ts +18 -0
  634. package/lib/realtime-callbacks.d.ts.map +1 -0
  635. package/lib/realtime-callbacks.js +177 -0
  636. package/lib/realtime-callbacks.js.map +1 -0
  637. package/lib/receipt-accumulator.d.ts +51 -0
  638. package/lib/receipt-accumulator.d.ts.map +1 -0
  639. package/lib/receipt-accumulator.js +164 -0
  640. package/lib/receipt-accumulator.js.map +1 -0
  641. package/lib/rendezvous/MSC4108SignInWithQR.d.ts +112 -0
  642. package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +1 -0
  643. package/lib/rendezvous/MSC4108SignInWithQR.js +392 -0
  644. package/lib/rendezvous/MSC4108SignInWithQR.js.map +1 -0
  645. package/lib/rendezvous/RendezvousChannel.d.ts +27 -0
  646. package/lib/rendezvous/RendezvousChannel.d.ts.map +1 -0
  647. package/lib/rendezvous/RendezvousChannel.js +1 -0
  648. package/lib/rendezvous/RendezvousChannel.js.map +1 -0
  649. package/lib/rendezvous/RendezvousCode.d.ts +9 -0
  650. package/lib/rendezvous/RendezvousCode.d.ts.map +1 -0
  651. package/lib/rendezvous/RendezvousCode.js +1 -0
  652. package/lib/rendezvous/RendezvousCode.js.map +1 -0
  653. package/lib/rendezvous/RendezvousError.d.ts +6 -0
  654. package/lib/rendezvous/RendezvousError.d.ts.map +1 -0
  655. package/lib/rendezvous/RendezvousError.js +23 -0
  656. package/lib/rendezvous/RendezvousError.js.map +1 -0
  657. package/lib/rendezvous/RendezvousFailureReason.d.ts +31 -0
  658. package/lib/rendezvous/RendezvousFailureReason.d.ts.map +1 -0
  659. package/lib/rendezvous/RendezvousFailureReason.js +38 -0
  660. package/lib/rendezvous/RendezvousFailureReason.js.map +1 -0
  661. package/lib/rendezvous/RendezvousIntent.d.ts +5 -0
  662. package/lib/rendezvous/RendezvousIntent.d.ts.map +1 -0
  663. package/lib/rendezvous/RendezvousIntent.js +22 -0
  664. package/lib/rendezvous/RendezvousIntent.js.map +1 -0
  665. package/lib/rendezvous/RendezvousTransport.d.ts +36 -0
  666. package/lib/rendezvous/RendezvousTransport.d.ts.map +1 -0
  667. package/lib/rendezvous/RendezvousTransport.js +1 -0
  668. package/lib/rendezvous/RendezvousTransport.js.map +1 -0
  669. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts +58 -0
  670. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts.map +1 -0
  671. package/lib/rendezvous/channels/MSC4108SecureChannel.js +246 -0
  672. package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +1 -0
  673. package/lib/rendezvous/channels/index.d.ts +2 -0
  674. package/lib/rendezvous/channels/index.d.ts.map +1 -0
  675. package/lib/rendezvous/channels/index.js +18 -0
  676. package/lib/rendezvous/channels/index.js.map +1 -0
  677. package/lib/rendezvous/index.d.ts +10 -0
  678. package/lib/rendezvous/index.d.ts.map +1 -0
  679. package/lib/rendezvous/index.js +23 -0
  680. package/lib/rendezvous/index.js.map +1 -0
  681. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts +61 -0
  682. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts.map +1 -0
  683. package/lib/rendezvous/transports/MSC4108RendezvousSession.js +253 -0
  684. package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +1 -0
  685. package/lib/rendezvous/transports/index.d.ts +2 -0
  686. package/lib/rendezvous/transports/index.d.ts.map +1 -0
  687. package/lib/rendezvous/transports/index.js +18 -0
  688. package/lib/rendezvous/transports/index.js.map +1 -0
  689. package/lib/room-hierarchy.d.ts +35 -0
  690. package/lib/room-hierarchy.d.ts.map +1 -0
  691. package/lib/room-hierarchy.js +136 -0
  692. package/lib/room-hierarchy.js.map +1 -0
  693. package/lib/rust-crypto/CrossSigningIdentity.d.ts +33 -0
  694. package/lib/rust-crypto/CrossSigningIdentity.d.ts.map +1 -0
  695. package/lib/rust-crypto/CrossSigningIdentity.js +157 -0
  696. package/lib/rust-crypto/CrossSigningIdentity.js.map +1 -0
  697. package/lib/rust-crypto/DehydratedDeviceManager.d.ts +98 -0
  698. package/lib/rust-crypto/DehydratedDeviceManager.d.ts.map +1 -0
  699. package/lib/rust-crypto/DehydratedDeviceManager.js +285 -0
  700. package/lib/rust-crypto/DehydratedDeviceManager.js.map +1 -0
  701. package/lib/rust-crypto/KeyClaimManager.d.ts +33 -0
  702. package/lib/rust-crypto/KeyClaimManager.d.ts.map +1 -0
  703. package/lib/rust-crypto/KeyClaimManager.js +82 -0
  704. package/lib/rust-crypto/KeyClaimManager.js.map +1 -0
  705. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts +43 -0
  706. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map +1 -0
  707. package/lib/rust-crypto/OutgoingRequestProcessor.js +195 -0
  708. package/lib/rust-crypto/OutgoingRequestProcessor.js.map +1 -0
  709. package/lib/rust-crypto/OutgoingRequestsManager.d.ts +47 -0
  710. package/lib/rust-crypto/OutgoingRequestsManager.d.ts.map +1 -0
  711. package/lib/rust-crypto/OutgoingRequestsManager.js +148 -0
  712. package/lib/rust-crypto/OutgoingRequestsManager.js.map +1 -0
  713. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts +120 -0
  714. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts.map +1 -0
  715. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js +467 -0
  716. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map +1 -0
  717. package/lib/rust-crypto/RoomEncryptor.d.ts +98 -0
  718. package/lib/rust-crypto/RoomEncryptor.d.ts.map +1 -0
  719. package/lib/rust-crypto/RoomEncryptor.js +299 -0
  720. package/lib/rust-crypto/RoomEncryptor.js.map +1 -0
  721. package/lib/rust-crypto/backup.d.ts +254 -0
  722. package/lib/rust-crypto/backup.d.ts.map +1 -0
  723. package/lib/rust-crypto/backup.js +837 -0
  724. package/lib/rust-crypto/backup.js.map +1 -0
  725. package/lib/rust-crypto/constants.d.ts +3 -0
  726. package/lib/rust-crypto/constants.d.ts.map +1 -0
  727. package/lib/rust-crypto/constants.js +19 -0
  728. package/lib/rust-crypto/constants.js.map +1 -0
  729. package/lib/rust-crypto/device-converter.d.ts +28 -0
  730. package/lib/rust-crypto/device-converter.d.ts.map +1 -0
  731. package/lib/rust-crypto/device-converter.js +123 -0
  732. package/lib/rust-crypto/device-converter.js.map +1 -0
  733. package/lib/rust-crypto/index.d.ts +61 -0
  734. package/lib/rust-crypto/index.d.ts.map +1 -0
  735. package/lib/rust-crypto/index.js +152 -0
  736. package/lib/rust-crypto/index.js.map +1 -0
  737. package/lib/rust-crypto/libolm_migration.d.ts +81 -0
  738. package/lib/rust-crypto/libolm_migration.d.ts.map +1 -0
  739. package/lib/rust-crypto/libolm_migration.js +459 -0
  740. package/lib/rust-crypto/libolm_migration.js.map +1 -0
  741. package/lib/rust-crypto/rust-crypto.d.ts +556 -0
  742. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -0
  743. package/lib/rust-crypto/rust-crypto.js +2016 -0
  744. package/lib/rust-crypto/rust-crypto.js.map +1 -0
  745. package/lib/rust-crypto/secret-storage.d.ts +22 -0
  746. package/lib/rust-crypto/secret-storage.d.ts.map +1 -0
  747. package/lib/rust-crypto/secret-storage.js +63 -0
  748. package/lib/rust-crypto/secret-storage.js.map +1 -0
  749. package/lib/rust-crypto/verification.d.ts +319 -0
  750. package/lib/rust-crypto/verification.d.ts.map +1 -0
  751. package/lib/rust-crypto/verification.js +816 -0
  752. package/lib/rust-crypto/verification.js.map +1 -0
  753. package/lib/scheduler.d.ts +132 -0
  754. package/lib/scheduler.d.ts.map +1 -0
  755. package/lib/scheduler.js +259 -0
  756. package/lib/scheduler.js.map +1 -0
  757. package/lib/secret-storage.d.ts +370 -0
  758. package/lib/secret-storage.d.ts.map +1 -0
  759. package/lib/secret-storage.js +466 -0
  760. package/lib/secret-storage.js.map +1 -0
  761. package/lib/serverCapabilities.d.ts +72 -0
  762. package/lib/serverCapabilities.d.ts.map +1 -0
  763. package/lib/serverCapabilities.js +105 -0
  764. package/lib/serverCapabilities.js.map +1 -0
  765. package/lib/service-types.d.ts +5 -0
  766. package/lib/service-types.d.ts.map +1 -0
  767. package/lib/service-types.js +22 -0
  768. package/lib/service-types.js.map +1 -0
  769. package/lib/sliding-sync-sdk.d.ts +107 -0
  770. package/lib/sliding-sync-sdk.d.ts.map +1 -0
  771. package/lib/sliding-sync-sdk.js +903 -0
  772. package/lib/sliding-sync-sdk.js.map +1 -0
  773. package/lib/sliding-sync.d.ts +343 -0
  774. package/lib/sliding-sync.d.ts.map +1 -0
  775. package/lib/sliding-sync.js +817 -0
  776. package/lib/sliding-sync.js.map +1 -0
  777. package/lib/store/index.d.ts +201 -0
  778. package/lib/store/index.d.ts.map +1 -0
  779. package/lib/store/index.js +1 -0
  780. package/lib/store/index.js.map +1 -0
  781. package/lib/store/indexeddb-backend.d.ts +24 -0
  782. package/lib/store/indexeddb-backend.d.ts.map +1 -0
  783. package/lib/store/indexeddb-backend.js +1 -0
  784. package/lib/store/indexeddb-backend.js.map +1 -0
  785. package/lib/store/indexeddb-local-backend.d.ts +129 -0
  786. package/lib/store/indexeddb-local-backend.d.ts.map +1 -0
  787. package/lib/store/indexeddb-local-backend.js +597 -0
  788. package/lib/store/indexeddb-local-backend.js.map +1 -0
  789. package/lib/store/indexeddb-remote-backend.d.ts +79 -0
  790. package/lib/store/indexeddb-remote-backend.d.ts.map +1 -0
  791. package/lib/store/indexeddb-remote-backend.js +210 -0
  792. package/lib/store/indexeddb-remote-backend.js.map +1 -0
  793. package/lib/store/indexeddb-store-worker.d.ts +35 -0
  794. package/lib/store/indexeddb-store-worker.d.ts.map +1 -0
  795. package/lib/store/indexeddb-store-worker.js +146 -0
  796. package/lib/store/indexeddb-store-worker.js.map +1 -0
  797. package/lib/store/indexeddb.d.ts +142 -0
  798. package/lib/store/indexeddb.d.ts.map +1 -0
  799. package/lib/store/indexeddb.js +347 -0
  800. package/lib/store/indexeddb.js.map +1 -0
  801. package/lib/store/local-storage-events-emitter.d.ts +30 -0
  802. package/lib/store/local-storage-events-emitter.d.ts.map +1 -0
  803. package/lib/store/local-storage-events-emitter.js +37 -0
  804. package/lib/store/local-storage-events-emitter.js.map +1 -0
  805. package/lib/store/memory.d.ts +209 -0
  806. package/lib/store/memory.d.ts.map +1 -0
  807. package/lib/store/memory.js +432 -0
  808. package/lib/store/memory.js.map +1 -0
  809. package/lib/store/stub.d.ts +161 -0
  810. package/lib/store/stub.d.ts.map +1 -0
  811. package/lib/store/stub.js +268 -0
  812. package/lib/store/stub.js.map +1 -0
  813. package/lib/sync-accumulator.d.ts +172 -0
  814. package/lib/sync-accumulator.d.ts.map +1 -0
  815. package/lib/sync-accumulator.js +532 -0
  816. package/lib/sync-accumulator.js.map +1 -0
  817. package/lib/sync.d.ts +260 -0
  818. package/lib/sync.d.ts.map +1 -0
  819. package/lib/sync.js +1686 -0
  820. package/lib/sync.js.map +1 -0
  821. package/lib/testing.d.ts +81 -0
  822. package/lib/testing.d.ts.map +1 -0
  823. package/lib/testing.js +162 -0
  824. package/lib/testing.js.map +1 -0
  825. package/lib/thread-utils.d.ts +10 -0
  826. package/lib/thread-utils.d.ts.map +1 -0
  827. package/lib/thread-utils.js +31 -0
  828. package/lib/thread-utils.js.map +1 -0
  829. package/lib/timeline-window.d.ts +168 -0
  830. package/lib/timeline-window.d.ts.map +1 -0
  831. package/lib/timeline-window.js +494 -0
  832. package/lib/timeline-window.js.map +1 -0
  833. package/lib/types.d.ts +33 -0
  834. package/lib/types.d.ts.map +1 -0
  835. package/lib/types.js +33 -0
  836. package/lib/types.js.map +1 -0
  837. package/lib/utils/decryptAESSecretStorageItem.d.ts +12 -0
  838. package/lib/utils/decryptAESSecretStorageItem.d.ts.map +1 -0
  839. package/lib/utils/decryptAESSecretStorageItem.js +50 -0
  840. package/lib/utils/decryptAESSecretStorageItem.js.map +1 -0
  841. package/lib/utils/encryptAESSecretStorageItem.d.ts +16 -0
  842. package/lib/utils/encryptAESSecretStorageItem.d.ts.map +1 -0
  843. package/lib/utils/encryptAESSecretStorageItem.js +68 -0
  844. package/lib/utils/encryptAESSecretStorageItem.js.map +1 -0
  845. package/lib/utils/internal/deriveKeys.d.ts +10 -0
  846. package/lib/utils/internal/deriveKeys.d.ts.map +1 -0
  847. package/lib/utils/internal/deriveKeys.js +60 -0
  848. package/lib/utils/internal/deriveKeys.js.map +1 -0
  849. package/lib/utils.d.ts +267 -0
  850. package/lib/utils.d.ts.map +1 -0
  851. package/lib/utils.js +749 -0
  852. package/lib/utils.js.map +1 -0
  853. package/lib/version-support.d.ts +19 -0
  854. package/lib/version-support.d.ts.map +1 -0
  855. package/lib/version-support.js +37 -0
  856. package/lib/version-support.js.map +1 -0
  857. package/lib/webrtc/audioContext.d.ts +15 -0
  858. package/lib/webrtc/audioContext.d.ts.map +1 -0
  859. package/lib/webrtc/audioContext.js +46 -0
  860. package/lib/webrtc/audioContext.js.map +1 -0
  861. package/lib/webrtc/call.d.ts +560 -0
  862. package/lib/webrtc/call.d.ts.map +1 -0
  863. package/lib/webrtc/call.js +2541 -0
  864. package/lib/webrtc/call.js.map +1 -0
  865. package/lib/webrtc/callEventHandler.d.ts +37 -0
  866. package/lib/webrtc/callEventHandler.d.ts.map +1 -0
  867. package/lib/webrtc/callEventHandler.js +344 -0
  868. package/lib/webrtc/callEventHandler.js.map +1 -0
  869. package/lib/webrtc/callEventTypes.d.ts +73 -0
  870. package/lib/webrtc/callEventTypes.d.ts.map +1 -0
  871. package/lib/webrtc/callEventTypes.js +13 -0
  872. package/lib/webrtc/callEventTypes.js.map +1 -0
  873. package/lib/webrtc/callFeed.d.ts +128 -0
  874. package/lib/webrtc/callFeed.d.ts.map +1 -0
  875. package/lib/webrtc/callFeed.js +289 -0
  876. package/lib/webrtc/callFeed.js.map +1 -0
  877. package/lib/webrtc/groupCall.d.ts +323 -0
  878. package/lib/webrtc/groupCall.d.ts.map +1 -0
  879. package/lib/webrtc/groupCall.js +1337 -0
  880. package/lib/webrtc/groupCall.js.map +1 -0
  881. package/lib/webrtc/groupCallEventHandler.d.ts +31 -0
  882. package/lib/webrtc/groupCallEventHandler.d.ts.map +1 -0
  883. package/lib/webrtc/groupCallEventHandler.js +178 -0
  884. package/lib/webrtc/groupCallEventHandler.js.map +1 -0
  885. package/lib/webrtc/mediaHandler.d.ts +89 -0
  886. package/lib/webrtc/mediaHandler.d.ts.map +1 -0
  887. package/lib/webrtc/mediaHandler.js +437 -0
  888. package/lib/webrtc/mediaHandler.js.map +1 -0
  889. package/lib/webrtc/stats/callFeedStatsReporter.d.ts +8 -0
  890. package/lib/webrtc/stats/callFeedStatsReporter.d.ts.map +1 -0
  891. package/lib/webrtc/stats/callFeedStatsReporter.js +82 -0
  892. package/lib/webrtc/stats/callFeedStatsReporter.js.map +1 -0
  893. package/lib/webrtc/stats/callStatsReportGatherer.d.ts +25 -0
  894. package/lib/webrtc/stats/callStatsReportGatherer.d.ts.map +1 -0
  895. package/lib/webrtc/stats/callStatsReportGatherer.js +199 -0
  896. package/lib/webrtc/stats/callStatsReportGatherer.js.map +1 -0
  897. package/lib/webrtc/stats/callStatsReportSummary.d.ts +17 -0
  898. package/lib/webrtc/stats/callStatsReportSummary.d.ts.map +1 -0
  899. package/lib/webrtc/stats/callStatsReportSummary.js +1 -0
  900. package/lib/webrtc/stats/callStatsReportSummary.js.map +1 -0
  901. package/lib/webrtc/stats/connectionStats.d.ts +28 -0
  902. package/lib/webrtc/stats/connectionStats.d.ts.map +1 -0
  903. package/lib/webrtc/stats/connectionStats.js +26 -0
  904. package/lib/webrtc/stats/connectionStats.js.map +1 -0
  905. package/lib/webrtc/stats/connectionStatsBuilder.d.ts +5 -0
  906. package/lib/webrtc/stats/connectionStatsBuilder.d.ts.map +1 -0
  907. package/lib/webrtc/stats/connectionStatsBuilder.js +27 -0
  908. package/lib/webrtc/stats/connectionStatsBuilder.js.map +1 -0
  909. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts +7 -0
  910. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts.map +1 -0
  911. package/lib/webrtc/stats/connectionStatsReportBuilder.js +121 -0
  912. package/lib/webrtc/stats/connectionStatsReportBuilder.js.map +1 -0
  913. package/lib/webrtc/stats/groupCallStats.d.ts +22 -0
  914. package/lib/webrtc/stats/groupCallStats.d.ts.map +1 -0
  915. package/lib/webrtc/stats/groupCallStats.js +78 -0
  916. package/lib/webrtc/stats/groupCallStats.js.map +1 -0
  917. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts +10 -0
  918. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts.map +1 -0
  919. package/lib/webrtc/stats/media/mediaSsrcHandler.js +57 -0
  920. package/lib/webrtc/stats/media/mediaSsrcHandler.js.map +1 -0
  921. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts +12 -0
  922. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts.map +1 -0
  923. package/lib/webrtc/stats/media/mediaTrackHandler.js +62 -0
  924. package/lib/webrtc/stats/media/mediaTrackHandler.js.map +1 -0
  925. package/lib/webrtc/stats/media/mediaTrackStats.d.ts +86 -0
  926. package/lib/webrtc/stats/media/mediaTrackStats.d.ts.map +1 -0
  927. package/lib/webrtc/stats/media/mediaTrackStats.js +142 -0
  928. package/lib/webrtc/stats/media/mediaTrackStats.js.map +1 -0
  929. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts +22 -0
  930. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts.map +1 -0
  931. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js +76 -0
  932. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js.map +1 -0
  933. package/lib/webrtc/stats/statsReport.d.ts +99 -0
  934. package/lib/webrtc/stats/statsReport.d.ts.map +1 -0
  935. package/lib/webrtc/stats/statsReport.js +32 -0
  936. package/lib/webrtc/stats/statsReport.js.map +1 -0
  937. package/lib/webrtc/stats/statsReportEmitter.d.ts +15 -0
  938. package/lib/webrtc/stats/statsReportEmitter.d.ts.map +1 -0
  939. package/lib/webrtc/stats/statsReportEmitter.js +33 -0
  940. package/lib/webrtc/stats/statsReportEmitter.js.map +1 -0
  941. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts +16 -0
  942. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts.map +1 -0
  943. package/lib/webrtc/stats/summaryStatsReportGatherer.js +116 -0
  944. package/lib/webrtc/stats/summaryStatsReportGatherer.js.map +1 -0
  945. package/lib/webrtc/stats/trackStatsBuilder.d.ts +19 -0
  946. package/lib/webrtc/stats/trackStatsBuilder.d.ts.map +1 -0
  947. package/lib/webrtc/stats/trackStatsBuilder.js +168 -0
  948. package/lib/webrtc/stats/trackStatsBuilder.js.map +1 -0
  949. package/lib/webrtc/stats/transportStats.d.ts +11 -0
  950. package/lib/webrtc/stats/transportStats.d.ts.map +1 -0
  951. package/lib/webrtc/stats/transportStats.js +1 -0
  952. package/lib/webrtc/stats/transportStats.js.map +1 -0
  953. package/lib/webrtc/stats/transportStatsBuilder.d.ts +5 -0
  954. package/lib/webrtc/stats/transportStatsBuilder.d.ts.map +1 -0
  955. package/lib/webrtc/stats/transportStatsBuilder.js +34 -0
  956. package/lib/webrtc/stats/transportStatsBuilder.js.map +1 -0
  957. package/lib/webrtc/stats/valueFormatter.d.ts +4 -0
  958. package/lib/webrtc/stats/valueFormatter.d.ts.map +1 -0
  959. package/lib/webrtc/stats/valueFormatter.js +25 -0
  960. package/lib/webrtc/stats/valueFormatter.js.map +1 -0
  961. package/package.json +134 -0
  962. package/src/@types/AESEncryptedSecretStoragePayload.ts +29 -0
  963. package/src/@types/IIdentityServerProvider.ts +24 -0
  964. package/src/@types/PushRules.ts +209 -0
  965. package/src/@types/another-json.d.ts +19 -0
  966. package/src/@types/auth.ts +252 -0
  967. package/src/@types/beacon.ts +140 -0
  968. package/src/@types/common.ts +22 -0
  969. package/src/@types/crypto.ts +73 -0
  970. package/src/@types/event.ts +370 -0
  971. package/src/@types/events.ts +119 -0
  972. package/src/@types/extensible_events.ts +150 -0
  973. package/src/@types/global.d.ts +70 -0
  974. package/src/@types/local_notifications.ts +19 -0
  975. package/src/@types/location.ts +92 -0
  976. package/src/@types/matrix-sdk-crypto-wasm.d.ts +44 -0
  977. package/src/@types/media.ts +245 -0
  978. package/src/@types/membership.ts +57 -0
  979. package/src/@types/oidc-client-ts.d.ts +24 -0
  980. package/src/@types/partials.ts +67 -0
  981. package/src/@types/polls.ts +119 -0
  982. package/src/@types/read_receipts.ts +61 -0
  983. package/src/@types/registration.ts +102 -0
  984. package/src/@types/requests.ts +314 -0
  985. package/src/@types/search.ts +119 -0
  986. package/src/@types/signed.ts +25 -0
  987. package/src/@types/spaces.ts +37 -0
  988. package/src/@types/state_events.ts +147 -0
  989. package/src/@types/synapse.ts +40 -0
  990. package/src/@types/sync.ts +27 -0
  991. package/src/@types/threepids.ts +29 -0
  992. package/src/@types/topic.ts +63 -0
  993. package/src/@types/uia.ts +29 -0
  994. package/src/NamespacedValue.ts +123 -0
  995. package/src/ReEmitter.ts +93 -0
  996. package/src/ToDeviceMessageQueue.ts +153 -0
  997. package/src/autodiscovery.ts +505 -0
  998. package/src/base64.ts +88 -0
  999. package/src/browser-index.ts +44 -0
  1000. package/src/client.ts +10474 -0
  1001. package/src/common-crypto/CryptoBackend.ts +302 -0
  1002. package/src/common-crypto/README.md +4 -0
  1003. package/src/common-crypto/key-passphrase.ts +43 -0
  1004. package/src/content-helpers.ts +288 -0
  1005. package/src/content-repo.ts +117 -0
  1006. package/src/crypto/CrossSigning.ts +773 -0
  1007. package/src/crypto/DeviceList.ts +989 -0
  1008. package/src/crypto/EncryptionSetup.ts +351 -0
  1009. package/src/crypto/OlmDevice.ts +1500 -0
  1010. package/src/crypto/OutgoingRoomKeyRequestManager.ts +485 -0
  1011. package/src/crypto/RoomList.ts +70 -0
  1012. package/src/crypto/SecretSharing.ts +240 -0
  1013. package/src/crypto/SecretStorage.ts +136 -0
  1014. package/src/crypto/aes.ts +23 -0
  1015. package/src/crypto/algorithms/base.ts +236 -0
  1016. package/src/crypto/algorithms/index.ts +20 -0
  1017. package/src/crypto/algorithms/megolm.ts +2216 -0
  1018. package/src/crypto/algorithms/olm.ts +381 -0
  1019. package/src/crypto/api.ts +70 -0
  1020. package/src/crypto/backup.ts +922 -0
  1021. package/src/crypto/crypto.ts +18 -0
  1022. package/src/crypto/dehydration.ts +272 -0
  1023. package/src/crypto/device-converter.ts +45 -0
  1024. package/src/crypto/deviceinfo.ts +158 -0
  1025. package/src/crypto/index.ts +4414 -0
  1026. package/src/crypto/key_passphrase.ts +42 -0
  1027. package/src/crypto/keybackup.ts +47 -0
  1028. package/src/crypto/olmlib.ts +539 -0
  1029. package/src/crypto/recoverykey.ts +18 -0
  1030. package/src/crypto/store/base.ts +348 -0
  1031. package/src/crypto/store/indexeddb-crypto-store-backend.ts +1250 -0
  1032. package/src/crypto/store/indexeddb-crypto-store.ts +845 -0
  1033. package/src/crypto/store/localStorage-crypto-store.ts +579 -0
  1034. package/src/crypto/store/memory-crypto-store.ts +680 -0
  1035. package/src/crypto/verification/Base.ts +409 -0
  1036. package/src/crypto/verification/Error.ts +76 -0
  1037. package/src/crypto/verification/IllegalMethod.ts +50 -0
  1038. package/src/crypto/verification/QRCode.ts +310 -0
  1039. package/src/crypto/verification/SAS.ts +494 -0
  1040. package/src/crypto/verification/SASDecimal.ts +37 -0
  1041. package/src/crypto/verification/request/Channel.ts +34 -0
  1042. package/src/crypto/verification/request/InRoomChannel.ts +371 -0
  1043. package/src/crypto/verification/request/ToDeviceChannel.ts +354 -0
  1044. package/src/crypto/verification/request/VerificationRequest.ts +976 -0
  1045. package/src/crypto-api/CryptoEvent.ts +93 -0
  1046. package/src/crypto-api/CryptoEventHandlerMap.ts +32 -0
  1047. package/src/crypto-api/index.ts +1175 -0
  1048. package/src/crypto-api/key-passphrase.ts +58 -0
  1049. package/src/crypto-api/keybackup.ts +115 -0
  1050. package/src/crypto-api/recovery-key.ts +69 -0
  1051. package/src/crypto-api/verification.ts +408 -0
  1052. package/src/digest.ts +34 -0
  1053. package/src/embedded.ts +631 -0
  1054. package/src/errors.ts +54 -0
  1055. package/src/event-mapper.ts +97 -0
  1056. package/src/extensible_events_v1/ExtensibleEvent.ts +58 -0
  1057. package/src/extensible_events_v1/InvalidEventError.ts +24 -0
  1058. package/src/extensible_events_v1/MessageEvent.ts +145 -0
  1059. package/src/extensible_events_v1/PollEndEvent.ts +97 -0
  1060. package/src/extensible_events_v1/PollResponseEvent.ts +148 -0
  1061. package/src/extensible_events_v1/PollStartEvent.ts +207 -0
  1062. package/src/extensible_events_v1/utilities.ts +35 -0
  1063. package/src/feature.ts +87 -0
  1064. package/src/filter-component.ts +207 -0
  1065. package/src/filter.ts +245 -0
  1066. package/src/http-api/errors.ts +199 -0
  1067. package/src/http-api/fetch.ts +383 -0
  1068. package/src/http-api/index.ts +191 -0
  1069. package/src/http-api/interface.ts +178 -0
  1070. package/src/http-api/method.ts +25 -0
  1071. package/src/http-api/prefix.ts +48 -0
  1072. package/src/http-api/utils.ts +200 -0
  1073. package/src/index.ts +25 -0
  1074. package/src/indexeddb-helpers.ts +50 -0
  1075. package/src/indexeddb-worker.ts +24 -0
  1076. package/src/interactive-auth.ts +694 -0
  1077. package/src/logger.ts +185 -0
  1078. package/src/matrix.ts +177 -0
  1079. package/src/matrixrtc/CallMembership.ts +247 -0
  1080. package/src/matrixrtc/LivekitFocus.ts +39 -0
  1081. package/src/matrixrtc/MatrixRTCSession.ts +1319 -0
  1082. package/src/matrixrtc/MatrixRTCSessionManager.ts +166 -0
  1083. package/src/matrixrtc/focus.ts +25 -0
  1084. package/src/matrixrtc/index.ts +22 -0
  1085. package/src/matrixrtc/types.ts +36 -0
  1086. package/src/models/MSC3089Branch.ts +272 -0
  1087. package/src/models/MSC3089TreeSpace.ts +565 -0
  1088. package/src/models/ToDeviceMessage.ts +38 -0
  1089. package/src/models/beacon.ts +214 -0
  1090. package/src/models/compare-event-ordering.ts +139 -0
  1091. package/src/models/device.ts +85 -0
  1092. package/src/models/event-context.ts +110 -0
  1093. package/src/models/event-status.ts +39 -0
  1094. package/src/models/event-timeline-set.ts +979 -0
  1095. package/src/models/event-timeline.ts +476 -0
  1096. package/src/models/event.ts +1751 -0
  1097. package/src/models/invites-ignorer.ts +376 -0
  1098. package/src/models/poll.ts +285 -0
  1099. package/src/models/profile-keys.ts +7 -0
  1100. package/src/models/read-receipt.ts +422 -0
  1101. package/src/models/related-relations.ts +39 -0
  1102. package/src/models/relations-container.ts +149 -0
  1103. package/src/models/relations.ts +368 -0
  1104. package/src/models/room-member.ts +457 -0
  1105. package/src/models/room-receipts.ts +439 -0
  1106. package/src/models/room-state.ts +1130 -0
  1107. package/src/models/room-summary.ts +47 -0
  1108. package/src/models/room.ts +3822 -0
  1109. package/src/models/search-result.ts +57 -0
  1110. package/src/models/thread.ts +923 -0
  1111. package/src/models/typed-event-emitter.ts +246 -0
  1112. package/src/models/user.ts +302 -0
  1113. package/src/oidc/authorize.ts +274 -0
  1114. package/src/oidc/discovery.ts +60 -0
  1115. package/src/oidc/error.ts +33 -0
  1116. package/src/oidc/index.ts +34 -0
  1117. package/src/oidc/register.ts +123 -0
  1118. package/src/oidc/tokenRefresher.ts +149 -0
  1119. package/src/oidc/validate.ts +282 -0
  1120. package/src/pushprocessor.ts +837 -0
  1121. package/src/randomstring.ts +51 -0
  1122. package/src/realtime-callbacks.ts +191 -0
  1123. package/src/receipt-accumulator.ts +189 -0
  1124. package/src/rendezvous/MSC4108SignInWithQR.ts +444 -0
  1125. package/src/rendezvous/RendezvousChannel.ts +48 -0
  1126. package/src/rendezvous/RendezvousCode.ts +25 -0
  1127. package/src/rendezvous/RendezvousError.ts +26 -0
  1128. package/src/rendezvous/RendezvousFailureReason.ts +49 -0
  1129. package/src/rendezvous/RendezvousIntent.ts +20 -0
  1130. package/src/rendezvous/RendezvousTransport.ts +58 -0
  1131. package/src/rendezvous/channels/MSC4108SecureChannel.ts +270 -0
  1132. package/src/rendezvous/channels/index.ts +17 -0
  1133. package/src/rendezvous/index.ts +25 -0
  1134. package/src/rendezvous/transports/MSC4108RendezvousSession.ts +270 -0
  1135. package/src/rendezvous/transports/index.ts +17 -0
  1136. package/src/room-hierarchy.ts +152 -0
  1137. package/src/rust-crypto/CrossSigningIdentity.ts +183 -0
  1138. package/src/rust-crypto/DehydratedDeviceManager.ts +306 -0
  1139. package/src/rust-crypto/KeyClaimManager.ts +86 -0
  1140. package/src/rust-crypto/OutgoingRequestProcessor.ts +236 -0
  1141. package/src/rust-crypto/OutgoingRequestsManager.ts +143 -0
  1142. package/src/rust-crypto/PerSessionKeyBackupDownloader.ts +501 -0
  1143. package/src/rust-crypto/RoomEncryptor.ts +352 -0
  1144. package/src/rust-crypto/backup.ts +881 -0
  1145. package/src/rust-crypto/constants.ts +18 -0
  1146. package/src/rust-crypto/device-converter.ts +128 -0
  1147. package/src/rust-crypto/index.ts +237 -0
  1148. package/src/rust-crypto/libolm_migration.ts +530 -0
  1149. package/src/rust-crypto/rust-crypto.ts +2205 -0
  1150. package/src/rust-crypto/secret-storage.ts +60 -0
  1151. package/src/rust-crypto/verification.ts +830 -0
  1152. package/src/scheduler.ts +309 -0
  1153. package/src/secret-storage.ts +693 -0
  1154. package/src/serverCapabilities.ts +139 -0
  1155. package/src/service-types.ts +20 -0
  1156. package/src/sliding-sync-sdk.ts +1026 -0
  1157. package/src/sliding-sync.ts +965 -0
  1158. package/src/store/index.ts +261 -0
  1159. package/src/store/indexeddb-backend.ts +41 -0
  1160. package/src/store/indexeddb-local-backend.ts +610 -0
  1161. package/src/store/indexeddb-remote-backend.ts +213 -0
  1162. package/src/store/indexeddb-store-worker.ts +157 -0
  1163. package/src/store/indexeddb.ts +397 -0
  1164. package/src/store/local-storage-events-emitter.ts +46 -0
  1165. package/src/store/memory.ts +448 -0
  1166. package/src/store/stub.ts +280 -0
  1167. package/src/sync-accumulator.ts +689 -0
  1168. package/src/sync.ts +1920 -0
  1169. package/src/testing.ts +191 -0
  1170. package/src/thread-utils.ts +31 -0
  1171. package/src/timeline-window.ts +536 -0
  1172. package/src/types.ts +59 -0
  1173. package/src/utils/decryptAESSecretStorageItem.ts +54 -0
  1174. package/src/utils/encryptAESSecretStorageItem.ts +73 -0
  1175. package/src/utils/internal/deriveKeys.ts +63 -0
  1176. package/src/utils.ts +763 -0
  1177. package/src/version-support.ts +36 -0
  1178. package/src/webrtc/audioContext.ts +44 -0
  1179. package/src/webrtc/call.ts +3074 -0
  1180. package/src/webrtc/callEventHandler.ts +425 -0
  1181. package/src/webrtc/callEventTypes.ts +93 -0
  1182. package/src/webrtc/callFeed.ts +364 -0
  1183. package/src/webrtc/groupCall.ts +1735 -0
  1184. package/src/webrtc/groupCallEventHandler.ts +234 -0
  1185. package/src/webrtc/mediaHandler.ts +484 -0
  1186. package/src/webrtc/stats/callFeedStatsReporter.ts +94 -0
  1187. package/src/webrtc/stats/callStatsReportGatherer.ts +219 -0
  1188. package/src/webrtc/stats/callStatsReportSummary.ts +30 -0
  1189. package/src/webrtc/stats/connectionStats.ts +47 -0
  1190. package/src/webrtc/stats/connectionStatsBuilder.ts +28 -0
  1191. package/src/webrtc/stats/connectionStatsReportBuilder.ts +140 -0
  1192. package/src/webrtc/stats/groupCallStats.ts +93 -0
  1193. package/src/webrtc/stats/media/mediaSsrcHandler.ts +57 -0
  1194. package/src/webrtc/stats/media/mediaTrackHandler.ts +76 -0
  1195. package/src/webrtc/stats/media/mediaTrackStats.ts +176 -0
  1196. package/src/webrtc/stats/media/mediaTrackStatsHandler.ts +90 -0
  1197. package/src/webrtc/stats/statsReport.ts +133 -0
  1198. package/src/webrtc/stats/statsReportEmitter.ts +49 -0
  1199. package/src/webrtc/stats/summaryStatsReportGatherer.ts +148 -0
  1200. package/src/webrtc/stats/trackStatsBuilder.ts +207 -0
  1201. package/src/webrtc/stats/transportStats.ts +26 -0
  1202. package/src/webrtc/stats/transportStatsBuilder.ts +48 -0
  1203. package/src/webrtc/stats/valueFormatter.ts +27 -0
@@ -0,0 +1,4414 @@
1
+ /*
2
+ Copyright 2016 OpenMarket Ltd
3
+ Copyright 2017 Vector Creations Ltd
4
+ Copyright 2018-2019 New Vector Ltd
5
+ Copyright 2019-2021 The Matrix.org Foundation C.I.C.
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ */
19
+
20
+ import anotherjson from "another-json";
21
+ import { v4 as uuidv4 } from "uuid";
22
+
23
+ import type { IDeviceKeys, IEventDecryptionResult, IMegolmSessionData, IOneTimeKey } from "../@types/crypto.ts";
24
+ import type { PkDecryption, PkSigning } from "@matrix-org/olm";
25
+ import { EventType, ToDeviceMessageId } from "../@types/event.ts";
26
+ import { TypedReEmitter } from "../ReEmitter.ts";
27
+ import { logger } from "../logger.ts";
28
+ import { IExportedDevice, OlmDevice } from "./OlmDevice.ts";
29
+ import { IOlmDevice } from "./algorithms/megolm.ts";
30
+ import * as olmlib from "./olmlib.ts";
31
+ import { DeviceInfoMap, DeviceList } from "./DeviceList.ts";
32
+ import { DeviceInfo, IDevice } from "./deviceinfo.ts";
33
+ import type { DecryptionAlgorithm, EncryptionAlgorithm } from "./algorithms/index.ts";
34
+ import * as algorithms from "./algorithms/index.ts";
35
+ import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning.ts";
36
+ import { EncryptionSetupBuilder } from "./EncryptionSetup.ts";
37
+ import { SecretStorage as LegacySecretStorage } from "./SecretStorage.ts";
38
+ import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api.ts";
39
+ import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager.ts";
40
+ import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store.ts";
41
+ import { VerificationBase } from "./verification/Base.ts";
42
+ import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from "./verification/QRCode.ts";
43
+ import { SAS as SASVerification } from "./verification/SAS.ts";
44
+ import { keyFromPassphrase } from "./key_passphrase.ts";
45
+ import { VerificationRequest } from "./verification/request/VerificationRequest.ts";
46
+ import { InRoomChannel, InRoomRequests } from "./verification/request/InRoomChannel.ts";
47
+ import { Request, ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDeviceChannel.ts";
48
+ import { IllegalMethod } from "./verification/IllegalMethod.ts";
49
+ import { KeySignatureUploadError } from "../errors.ts";
50
+ import { DehydrationManager } from "./dehydration.ts";
51
+ import { BackupManager, LibOlmBackupDecryptor, backupTrustInfoFromLegacyTrustInfo } from "./backup.ts";
52
+ import { IStore } from "../store/index.ts";
53
+ import { Room, RoomEvent } from "../models/room.ts";
54
+ import { RoomMember, RoomMemberEvent } from "../models/room-member.ts";
55
+ import { EventStatus, IContent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event.ts";
56
+ import { ToDeviceBatch, ToDevicePayload } from "../models/ToDeviceMessage.ts";
57
+ import { ClientEvent, IKeysUploadResponse, ISignedKey, IUploadKeySignaturesResponse, MatrixClient } from "../client.ts";
58
+ import { IRoomEncryption, RoomList } from "./RoomList.ts";
59
+ import { IKeyBackupInfo } from "./keybackup.ts";
60
+ import { ISyncStateData } from "../sync.ts";
61
+ import { CryptoStore } from "./store/base.ts";
62
+ import { IVerificationChannel } from "./verification/request/Channel.ts";
63
+ import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
64
+ import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator.ts";
65
+ import { ISignatures } from "../@types/signed.ts";
66
+ import { IMessage } from "./algorithms/olm.ts";
67
+ import {
68
+ BackupDecryptor,
69
+ CryptoBackend,
70
+ DecryptionError,
71
+ OnSyncCompletedData,
72
+ } from "../common-crypto/CryptoBackend.ts";
73
+ import { RoomState, RoomStateEvent } from "../models/room-state.ts";
74
+ import { MapWithDefault, recursiveMapToObject } from "../utils.ts";
75
+ import {
76
+ AccountDataClient,
77
+ AddSecretStorageKeyOpts,
78
+ calculateKeyCheck,
79
+ SECRET_STORAGE_ALGORITHM_V1_AES,
80
+ SecretStorageKeyDescription,
81
+ SecretStorageKeyObject,
82
+ SecretStorageKeyTuple,
83
+ ServerSideSecretStorageImpl,
84
+ } from "../secret-storage.ts";
85
+ import { ISecretRequest } from "./SecretSharing.ts";
86
+ import {
87
+ BackupTrustInfo,
88
+ BootstrapCrossSigningOpts,
89
+ CrossSigningKeyInfo,
90
+ CrossSigningStatus,
91
+ decodeRecoveryKey,
92
+ DecryptionFailureCode,
93
+ DeviceIsolationMode,
94
+ DeviceVerificationStatus,
95
+ encodeRecoveryKey,
96
+ EventEncryptionInfo,
97
+ EventShieldColour,
98
+ EventShieldReason,
99
+ ImportRoomKeysOpts,
100
+ KeyBackupCheck,
101
+ KeyBackupInfo,
102
+ OwnDeviceKeys,
103
+ CryptoEvent as CryptoApiCryptoEvent,
104
+ CryptoEventHandlerMap as CryptoApiCryptoEventHandlerMap,
105
+ KeyBackupRestoreResult,
106
+ KeyBackupRestoreOpts,
107
+ } from "../crypto-api/index.ts";
108
+ import { Device, DeviceMap } from "../models/device.ts";
109
+ import { deviceInfoToDevice } from "./device-converter.ts";
110
+ import { ClientPrefix, MatrixError, Method } from "../http-api/index.ts";
111
+ import { decodeBase64, encodeBase64 } from "../base64.ts";
112
+ import { KnownMembership } from "../@types/membership.ts";
113
+ import decryptAESSecretStorageItem from "../utils/decryptAESSecretStorageItem.ts";
114
+ import encryptAESSecretStorageItem from "../utils/encryptAESSecretStorageItem.ts";
115
+ import { AESEncryptedSecretStoragePayload } from "../@types/AESEncryptedSecretStoragePayload.ts";
116
+
117
+ /* re-exports for backwards compatibility */
118
+ export type {
119
+ BootstrapCrossSigningOpts as IBootstrapCrossSigningOpts,
120
+ CryptoCallbacks as ICryptoCallbacks,
121
+ } from "../crypto-api/index.ts";
122
+
123
+ const DeviceVerification = DeviceInfo.DeviceVerification;
124
+
125
+ const defaultVerificationMethods = {
126
+ [ReciprocateQRCode.NAME]: ReciprocateQRCode,
127
+ [SASVerification.NAME]: SASVerification,
128
+
129
+ // These two can't be used for actual verification, but we do
130
+ // need to be able to define them here for the verification flows
131
+ // to start.
132
+ [SHOW_QR_CODE_METHOD]: IllegalMethod,
133
+ [SCAN_QR_CODE_METHOD]: IllegalMethod,
134
+ } as const;
135
+
136
+ /**
137
+ * verification method names
138
+ */
139
+ // legacy export identifier
140
+ export const verificationMethods = {
141
+ RECIPROCATE_QR_CODE: ReciprocateQRCode.NAME,
142
+ SAS: SASVerification.NAME,
143
+ } as const;
144
+
145
+ export type VerificationMethod = keyof typeof verificationMethods | string;
146
+
147
+ // minimum time between attempting to unwedge an Olm session, if we succeeded
148
+ // in creating a new session
149
+ const MIN_FORCE_SESSION_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
150
+ // minimum time between attempting to unwedge an Olm session, if we failed
151
+ // to create a new session
152
+ const FORCE_SESSION_RETRY_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
153
+
154
+ interface IInitOpts {
155
+ exportedOlmDevice?: IExportedDevice;
156
+ pickleKey?: string;
157
+ }
158
+
159
+ /* eslint-disable camelcase */
160
+ interface IRoomKey {
161
+ room_id: string;
162
+ algorithm: string;
163
+ }
164
+
165
+ /**
166
+ * The parameters of a room key request. The details of the request may
167
+ * vary with the crypto algorithm, but the management and storage layers for
168
+ * outgoing requests expect it to have 'room_id' and 'session_id' properties.
169
+ */
170
+ export interface IRoomKeyRequestBody extends IRoomKey {
171
+ session_id: string;
172
+ sender_key: string;
173
+ }
174
+
175
+ /* eslint-enable camelcase */
176
+
177
+ interface IDeviceVerificationUpgrade {
178
+ devices: DeviceInfo[];
179
+ crossSigningInfo: CrossSigningInfo;
180
+ }
181
+
182
+ export interface ICheckOwnCrossSigningTrustOpts {
183
+ allowPrivateKeyRequests?: boolean;
184
+ }
185
+
186
+ interface IUserOlmSession {
187
+ deviceIdKey: string;
188
+ sessions: {
189
+ sessionId: string;
190
+ hasReceivedMessage: boolean;
191
+ }[];
192
+ }
193
+
194
+ export interface IRoomKeyRequestRecipient {
195
+ userId: string;
196
+ deviceId: string;
197
+ }
198
+
199
+ interface ISignableObject {
200
+ signatures?: ISignatures;
201
+ unsigned?: object;
202
+ }
203
+
204
+ export interface IRequestsMap {
205
+ getRequest(event: MatrixEvent): VerificationRequest | undefined;
206
+ getRequestByChannel(channel: IVerificationChannel): VerificationRequest | undefined;
207
+ setRequest(event: MatrixEvent, request: VerificationRequest): void;
208
+ setRequestByChannel(channel: IVerificationChannel, request: VerificationRequest): void;
209
+ }
210
+
211
+ /* eslint-disable camelcase */
212
+ export interface IOlmEncryptedContent {
213
+ algorithm: typeof olmlib.OLM_ALGORITHM;
214
+ sender_key: string;
215
+ ciphertext: Record<string, IMessage>;
216
+ [ToDeviceMessageId]?: string;
217
+ }
218
+
219
+ export interface IMegolmEncryptedContent {
220
+ algorithm: typeof olmlib.MEGOLM_ALGORITHM;
221
+ sender_key: string;
222
+ session_id: string;
223
+ device_id: string;
224
+ ciphertext: string;
225
+ [ToDeviceMessageId]?: string;
226
+ }
227
+ /* eslint-enable camelcase */
228
+
229
+ export type IEncryptedContent = IOlmEncryptedContent | IMegolmEncryptedContent;
230
+
231
+ export enum CryptoEvent {
232
+ /** @deprecated Event not fired by the rust crypto */
233
+ DeviceVerificationChanged = "deviceVerificationChanged",
234
+ UserTrustStatusChanged = CryptoApiCryptoEvent.UserTrustStatusChanged,
235
+ /** @deprecated Event not fired by the rust crypto */
236
+ UserCrossSigningUpdated = "userCrossSigningUpdated",
237
+ /** @deprecated Event not fired by the rust crypto */
238
+ RoomKeyRequest = "crypto.roomKeyRequest",
239
+ /** @deprecated Event not fired by the rust crypto */
240
+ RoomKeyRequestCancellation = "crypto.roomKeyRequestCancellation",
241
+ KeyBackupStatus = CryptoApiCryptoEvent.KeyBackupStatus,
242
+ KeyBackupFailed = CryptoApiCryptoEvent.KeyBackupFailed,
243
+ KeyBackupSessionsRemaining = CryptoApiCryptoEvent.KeyBackupSessionsRemaining,
244
+
245
+ /**
246
+ * Fires when a new valid backup decryption key is in cache.
247
+ * This will happen when a secret is received from another session, from secret storage,
248
+ * or when a new backup is created from this session.
249
+ *
250
+ * The payload is the version of the backup for which we have the key for.
251
+ *
252
+ * This event is only fired by the rust crypto backend.
253
+ */
254
+ KeyBackupDecryptionKeyCached = CryptoApiCryptoEvent.KeyBackupDecryptionKeyCached,
255
+
256
+ /** @deprecated Event not fired by the rust crypto */
257
+ KeySignatureUploadFailure = "crypto.keySignatureUploadFailure",
258
+ /** @deprecated Use `VerificationRequestReceived`. */
259
+ VerificationRequest = "crypto.verification.request",
260
+
261
+ /**
262
+ * Fires when a key verification request is received.
263
+ *
264
+ * The payload is a {@link Crypto.VerificationRequest}.
265
+ */
266
+ VerificationRequestReceived = CryptoApiCryptoEvent.VerificationRequestReceived,
267
+
268
+ /** @deprecated Event not fired by the rust crypto */
269
+ Warning = "crypto.warning",
270
+ /** @deprecated Use {@link DevicesUpdated} instead when using rust crypto */
271
+ WillUpdateDevices = CryptoApiCryptoEvent.WillUpdateDevices,
272
+ DevicesUpdated = CryptoApiCryptoEvent.DevicesUpdated,
273
+ KeysChanged = CryptoApiCryptoEvent.KeysChanged,
274
+
275
+ /**
276
+ * Fires when data is being migrated from legacy crypto to rust crypto.
277
+ *
278
+ * The payload is a pair `(progress, total)`, where `progress` is the number of steps completed so far, and
279
+ * `total` is the total number of steps. When migration is complete, a final instance of the event is emitted, with
280
+ * `progress === total === -1`.
281
+ */
282
+ LegacyCryptoStoreMigrationProgress = CryptoApiCryptoEvent.LegacyCryptoStoreMigrationProgress,
283
+ }
284
+
285
+ export type CryptoEventHandlerMap = CryptoApiCryptoEventHandlerMap & {
286
+ /**
287
+ * Fires when a device is marked as verified/unverified/blocked/unblocked by
288
+ * {@link MatrixClient#setDeviceVerified | MatrixClient.setDeviceVerified} or
289
+ * {@link MatrixClient#setDeviceBlocked | MatrixClient.setDeviceBlocked}.
290
+ *
291
+ * @param userId - the owner of the verified device
292
+ * @param deviceId - the id of the verified device
293
+ * @param deviceInfo - updated device information
294
+ */
295
+ [CryptoEvent.DeviceVerificationChanged]: (userId: string, deviceId: string, deviceInfo: DeviceInfo) => void;
296
+ /**
297
+ * Fires when we receive a room key request
298
+ *
299
+ * @param request - request details
300
+ */
301
+ [CryptoEvent.RoomKeyRequest]: (request: IncomingRoomKeyRequest) => void;
302
+ /**
303
+ * Fires when we receive a room key request cancellation
304
+ */
305
+ [CryptoEvent.RoomKeyRequestCancellation]: (request: IncomingRoomKeyRequestCancellation) => void;
306
+ [CryptoEvent.KeySignatureUploadFailure]: (
307
+ failures: IUploadKeySignaturesResponse["failures"],
308
+ source: "checkOwnCrossSigningTrust" | "afterCrossSigningLocalKeyChange" | "setDeviceVerification",
309
+ upload: (opts: { shouldEmit: boolean }) => Promise<void>,
310
+ ) => void;
311
+ /**
312
+ * Fires when a key verification is requested.
313
+ *
314
+ * Deprecated: use `CryptoEvent.VerificationRequestReceived`.
315
+ */
316
+ [CryptoEvent.VerificationRequest]: (request: VerificationRequest<any>) => void;
317
+ /**
318
+ * Fires when the app may wish to warn the user about something related
319
+ * the end-to-end crypto.
320
+ *
321
+ * @param type - One of the strings listed above
322
+ */
323
+ [CryptoEvent.Warning]: (type: string) => void;
324
+ [CryptoEvent.UserCrossSigningUpdated]: (userId: string) => void;
325
+
326
+ [CryptoEvent.LegacyCryptoStoreMigrationProgress]: (progress: number, total: number) => void;
327
+ };
328
+
329
+ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap> implements CryptoBackend {
330
+ /**
331
+ * @returns The version of Olm.
332
+ */
333
+ public static getOlmVersion(): [number, number, number] {
334
+ return OlmDevice.getOlmVersion();
335
+ }
336
+
337
+ public readonly backupManager: BackupManager;
338
+ public readonly crossSigningInfo: CrossSigningInfo;
339
+ public readonly olmDevice: OlmDevice;
340
+ public readonly deviceList: DeviceList;
341
+ public readonly dehydrationManager: DehydrationManager;
342
+ public readonly secretStorage: LegacySecretStorage;
343
+
344
+ private readonly roomList: RoomList;
345
+ private readonly reEmitter: TypedReEmitter<CryptoEvent, CryptoEventHandlerMap>;
346
+ private readonly verificationMethods: Map<VerificationMethod, typeof VerificationBase>;
347
+ public readonly supportedAlgorithms: string[];
348
+ private readonly outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager;
349
+ private readonly toDeviceVerificationRequests: ToDeviceRequests;
350
+ public readonly inRoomVerificationRequests: InRoomRequests;
351
+
352
+ private trustCrossSignedDevices = true;
353
+ // the last time we did a check for the number of one-time-keys on the server.
354
+ private lastOneTimeKeyCheck: number | null = null;
355
+ private oneTimeKeyCheckInProgress = false;
356
+
357
+ // EncryptionAlgorithm instance for each room
358
+ private roomEncryptors = new Map<string, EncryptionAlgorithm>();
359
+ // map from algorithm to DecryptionAlgorithm instance, for each room
360
+ private roomDecryptors = new Map<string, Map<string, DecryptionAlgorithm>>();
361
+
362
+ private deviceKeys: Record<string, string> = {}; // type: key
363
+
364
+ public globalBlacklistUnverifiedDevices = false;
365
+ public globalErrorOnUnknownDevices = true;
366
+
367
+ // list of IncomingRoomKeyRequests/IncomingRoomKeyRequestCancellations
368
+ // we received in the current sync.
369
+ private receivedRoomKeyRequests: IncomingRoomKeyRequest[] = [];
370
+ private receivedRoomKeyRequestCancellations: IncomingRoomKeyRequestCancellation[] = [];
371
+ // true if we are currently processing received room key requests
372
+ private processingRoomKeyRequests = false;
373
+ // controls whether device tracking is delayed
374
+ // until calling encryptEvent or trackRoomDevices,
375
+ // or done immediately upon enabling room encryption.
376
+ private lazyLoadMembers = false;
377
+ // in case lazyLoadMembers is true,
378
+ // track if an initial tracking of all the room members
379
+ // has happened for a given room. This is delayed
380
+ // to avoid loading room members as long as possible.
381
+ private roomDeviceTrackingState: { [roomId: string]: Promise<void> } = {};
382
+
383
+ // The timestamp of the minimum time at which we will retry forcing establishment
384
+ // of a new session for each device, in milliseconds.
385
+ // {
386
+ // userId: {
387
+ // deviceId: 1234567890000,
388
+ // },
389
+ // }
390
+ // Map: user Id → device Id → timestamp
391
+ private forceNewSessionRetryTime: MapWithDefault<string, MapWithDefault<string, number>> = new MapWithDefault(
392
+ () => new MapWithDefault(() => 0),
393
+ );
394
+
395
+ // This flag will be unset whilst the client processes a sync response
396
+ // so that we don't start requesting keys until we've actually finished
397
+ // processing the response.
398
+ private sendKeyRequestsImmediately = false;
399
+
400
+ private oneTimeKeyCount?: number;
401
+ private needsNewFallback?: boolean;
402
+ private fallbackCleanup?: ReturnType<typeof setTimeout>;
403
+
404
+ /**
405
+ * Cryptography bits
406
+ *
407
+ * This module is internal to the js-sdk; the public API is via MatrixClient.
408
+ *
409
+ * @internal
410
+ *
411
+ * @param baseApis - base matrix api interface
412
+ *
413
+ * @param userId - The user ID for the local user
414
+ *
415
+ * @param deviceId - The identifier for this device.
416
+ *
417
+ * @param clientStore - the MatrixClient data store.
418
+ *
419
+ * @param cryptoStore - storage for the crypto layer.
420
+ *
421
+ * @param verificationMethods - Array of verification methods to use.
422
+ * Each element can either be a string from MatrixClient.verificationMethods
423
+ * or a class that implements a verification method.
424
+ */
425
+ public constructor(
426
+ public readonly baseApis: MatrixClient,
427
+ public readonly userId: string,
428
+ private readonly deviceId: string,
429
+ private readonly clientStore: IStore,
430
+ public readonly cryptoStore: CryptoStore,
431
+ verificationMethods: Array<VerificationMethod | (typeof VerificationBase & { NAME: string })>,
432
+ ) {
433
+ super();
434
+
435
+ logger.debug("Crypto: initialising roomlist...");
436
+ this.roomList = new RoomList(cryptoStore);
437
+
438
+ this.reEmitter = new TypedReEmitter(this);
439
+
440
+ if (verificationMethods) {
441
+ this.verificationMethods = new Map();
442
+ for (const method of verificationMethods) {
443
+ if (typeof method === "string") {
444
+ if (defaultVerificationMethods[method]) {
445
+ this.verificationMethods.set(
446
+ method,
447
+ <typeof VerificationBase>defaultVerificationMethods[method],
448
+ );
449
+ }
450
+ } else if (method["NAME"]) {
451
+ this.verificationMethods.set(method["NAME"], method as typeof VerificationBase);
452
+ } else {
453
+ logger.warn(`Excluding unknown verification method ${method}`);
454
+ }
455
+ }
456
+ } else {
457
+ this.verificationMethods = new Map(Object.entries(defaultVerificationMethods)) as Map<
458
+ VerificationMethod,
459
+ typeof VerificationBase
460
+ >;
461
+ }
462
+
463
+ this.backupManager = new BackupManager(baseApis, async () => {
464
+ // try to get key from cache
465
+ const cachedKey = await this.getSessionBackupPrivateKey();
466
+ if (cachedKey) {
467
+ return cachedKey;
468
+ }
469
+
470
+ // try to get key from secret storage
471
+ const storedKey = await this.secretStorage.get("m.megolm_backup.v1");
472
+
473
+ if (storedKey) {
474
+ // ensure that the key is in the right format. If not, fix the key and
475
+ // store the fixed version
476
+ const fixedKey = fixBackupKey(storedKey);
477
+ if (fixedKey) {
478
+ const keys = await this.secretStorage.getKey();
479
+ await this.secretStorage.store("m.megolm_backup.v1", fixedKey, [keys![0]]);
480
+ }
481
+
482
+ return decodeBase64(fixedKey || storedKey);
483
+ }
484
+
485
+ // try to get key from app
486
+ if (this.baseApis.cryptoCallbacks && this.baseApis.cryptoCallbacks.getBackupKey) {
487
+ return this.baseApis.cryptoCallbacks.getBackupKey();
488
+ }
489
+
490
+ throw new Error("Unable to get private key");
491
+ });
492
+
493
+ this.olmDevice = new OlmDevice(cryptoStore);
494
+ this.deviceList = new DeviceList(baseApis, cryptoStore, this.olmDevice);
495
+
496
+ // XXX: This isn't removed at any point, but then none of the event listeners
497
+ // this class sets seem to be removed at any point... :/
498
+ this.deviceList.on(CryptoEvent.UserCrossSigningUpdated, this.onDeviceListUserCrossSigningUpdated);
499
+ this.reEmitter.reEmit(this.deviceList, [CryptoEvent.DevicesUpdated, CryptoEvent.WillUpdateDevices]);
500
+
501
+ this.supportedAlgorithms = Array.from(algorithms.DECRYPTION_CLASSES.keys());
502
+
503
+ this.outgoingRoomKeyRequestManager = new OutgoingRoomKeyRequestManager(
504
+ baseApis,
505
+ this.deviceId,
506
+ this.cryptoStore,
507
+ );
508
+
509
+ this.toDeviceVerificationRequests = new ToDeviceRequests();
510
+ this.inRoomVerificationRequests = new InRoomRequests();
511
+
512
+ const cryptoCallbacks = this.baseApis.cryptoCallbacks || {};
513
+ const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore, this.olmDevice);
514
+
515
+ this.crossSigningInfo = new CrossSigningInfo(userId, cryptoCallbacks, cacheCallbacks);
516
+ // Yes, we pass the client twice here: see SecretStorage
517
+ this.secretStorage = new LegacySecretStorage(baseApis as AccountDataClient, cryptoCallbacks, baseApis);
518
+ this.dehydrationManager = new DehydrationManager(this);
519
+
520
+ // Assuming no app-supplied callback, default to getting from SSSS.
521
+ if (!cryptoCallbacks.getCrossSigningKey && cryptoCallbacks.getSecretStorageKey) {
522
+ cryptoCallbacks.getCrossSigningKey = async (type): Promise<Uint8Array | null> => {
523
+ return CrossSigningInfo.getFromSecretStorage(type, this.secretStorage);
524
+ };
525
+ }
526
+ }
527
+
528
+ /**
529
+ * Initialise the crypto module so that it is ready for use
530
+ *
531
+ * Returns a promise which resolves once the crypto module is ready for use.
532
+ *
533
+ * @param exportedOlmDevice - (Optional) data from exported device
534
+ * that must be re-created.
535
+ */
536
+ public async init({ exportedOlmDevice, pickleKey }: IInitOpts = {}): Promise<void> {
537
+ logger.log("Crypto: initialising Olm...");
538
+ await globalThis.Olm.init();
539
+ logger.log(
540
+ exportedOlmDevice
541
+ ? "Crypto: initialising Olm device from exported device..."
542
+ : "Crypto: initialising Olm device...",
543
+ );
544
+ await this.olmDevice.init({ fromExportedDevice: exportedOlmDevice, pickleKey });
545
+ logger.log("Crypto: loading device list...");
546
+ await this.deviceList.load();
547
+
548
+ // build our device keys: these will later be uploaded
549
+ this.deviceKeys["ed25519:" + this.deviceId] = this.olmDevice.deviceEd25519Key!;
550
+ this.deviceKeys["curve25519:" + this.deviceId] = this.olmDevice.deviceCurve25519Key!;
551
+
552
+ logger.log("Crypto: fetching own devices...");
553
+ let myDevices = this.deviceList.getRawStoredDevicesForUser(this.userId);
554
+
555
+ if (!myDevices) {
556
+ myDevices = {};
557
+ }
558
+
559
+ if (!myDevices[this.deviceId]) {
560
+ // add our own deviceinfo to the cryptoStore
561
+ logger.log("Crypto: adding this device to the store...");
562
+ const deviceInfo = {
563
+ keys: this.deviceKeys,
564
+ algorithms: this.supportedAlgorithms,
565
+ verified: DeviceVerification.VERIFIED,
566
+ known: true,
567
+ };
568
+
569
+ myDevices[this.deviceId] = deviceInfo;
570
+ this.deviceList.storeDevicesForUser(this.userId, myDevices);
571
+ this.deviceList.saveIfDirty();
572
+ }
573
+
574
+ await this.cryptoStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
575
+ this.cryptoStore.getCrossSigningKeys(txn, (keys) => {
576
+ // can be an empty object after resetting cross-signing keys, see storeTrustedSelfKeys
577
+ if (keys && Object.keys(keys).length !== 0) {
578
+ logger.log("Loaded cross-signing public keys from crypto store");
579
+ this.crossSigningInfo.setKeys(keys);
580
+ }
581
+ });
582
+ });
583
+ // make sure we are keeping track of our own devices
584
+ // (this is important for key backups & things)
585
+ this.deviceList.startTrackingDeviceList(this.userId);
586
+
587
+ logger.debug("Crypto: initialising roomlist...");
588
+ await this.roomList.init();
589
+
590
+ logger.log("Crypto: checking for key backup...");
591
+ this.backupManager.checkAndStart();
592
+ }
593
+
594
+ /**
595
+ * Implementation of {@link Crypto.CryptoApi#setDeviceIsolationMode}.
596
+ */
597
+ public setDeviceIsolationMode(isolationMode: DeviceIsolationMode): void {
598
+ throw new Error("Not supported");
599
+ }
600
+ /**
601
+ * Implementation of {@link Crypto.CryptoApi#getVersion}.
602
+ */
603
+ public getVersion(): string {
604
+ const olmVersionTuple = Crypto.getOlmVersion();
605
+ return `Olm ${olmVersionTuple[0]}.${olmVersionTuple[1]}.${olmVersionTuple[2]}`;
606
+ }
607
+
608
+ /**
609
+ * Whether to trust a others users signatures of their devices.
610
+ * If false, devices will only be considered 'verified' if we have
611
+ * verified that device individually (effectively disabling cross-signing).
612
+ *
613
+ * Default: true
614
+ *
615
+ * @returns True if trusting cross-signed devices
616
+ */
617
+ public getTrustCrossSignedDevices(): boolean {
618
+ return this.trustCrossSignedDevices;
619
+ }
620
+
621
+ /**
622
+ * @deprecated Use {@link Crypto.CryptoApi#getTrustCrossSignedDevices}.
623
+ */
624
+ public getCryptoTrustCrossSignedDevices(): boolean {
625
+ return this.trustCrossSignedDevices;
626
+ }
627
+
628
+ /**
629
+ * See getCryptoTrustCrossSignedDevices
630
+ *
631
+ * @param val - True to trust cross-signed devices
632
+ */
633
+ public setTrustCrossSignedDevices(val: boolean): void {
634
+ this.trustCrossSignedDevices = val;
635
+
636
+ for (const userId of this.deviceList.getKnownUserIds()) {
637
+ const devices = this.deviceList.getRawStoredDevicesForUser(userId);
638
+ for (const deviceId of Object.keys(devices)) {
639
+ const deviceTrust = this.checkDeviceTrust(userId, deviceId);
640
+ // If the device is locally verified then isVerified() is always true,
641
+ // so this will only have caused the value to change if the device is
642
+ // cross-signing verified but not locally verified
643
+ if (!deviceTrust.isLocallyVerified() && deviceTrust.isCrossSigningVerified()) {
644
+ const deviceObj = this.deviceList.getStoredDevice(userId, deviceId)!;
645
+ this.emit(CryptoEvent.DeviceVerificationChanged, userId, deviceId, deviceObj);
646
+ }
647
+ }
648
+ }
649
+ }
650
+
651
+ /**
652
+ * @deprecated Use {@link Crypto.CryptoApi#setTrustCrossSignedDevices}.
653
+ */
654
+ public setCryptoTrustCrossSignedDevices(val: boolean): void {
655
+ this.setTrustCrossSignedDevices(val);
656
+ }
657
+
658
+ /**
659
+ * Create a recovery key from a user-supplied passphrase.
660
+ *
661
+ * @param password - Passphrase string that can be entered by the user
662
+ * when restoring the backup as an alternative to entering the recovery key.
663
+ * Optional.
664
+ * @returns Object with public key metadata, encoded private
665
+ * recovery key which should be disposed of after displaying to the user,
666
+ * and raw private key to avoid round tripping if needed.
667
+ */
668
+ public async createRecoveryKeyFromPassphrase(password?: string): Promise<IRecoveryKey> {
669
+ const decryption = new globalThis.Olm.PkDecryption();
670
+ try {
671
+ if (password) {
672
+ const derivation = await keyFromPassphrase(password);
673
+
674
+ decryption.init_with_private_key(derivation.key);
675
+ const privateKey = decryption.get_private_key();
676
+ return {
677
+ keyInfo: {
678
+ passphrase: {
679
+ algorithm: "m.pbkdf2",
680
+ iterations: derivation.iterations,
681
+ salt: derivation.salt,
682
+ },
683
+ },
684
+ privateKey: privateKey,
685
+ encodedPrivateKey: encodeRecoveryKey(privateKey),
686
+ };
687
+ } else {
688
+ decryption.generate_key();
689
+ const privateKey = decryption.get_private_key();
690
+ return {
691
+ privateKey: privateKey,
692
+ encodedPrivateKey: encodeRecoveryKey(privateKey),
693
+ };
694
+ }
695
+ } finally {
696
+ decryption?.free();
697
+ }
698
+ }
699
+
700
+ /**
701
+ * Checks if the user has previously published cross-signing keys
702
+ *
703
+ * This means downloading the devicelist for the user and checking if the list includes
704
+ * the cross-signing pseudo-device.
705
+ *
706
+ * @internal
707
+ */
708
+ public async userHasCrossSigningKeys(userId = this.userId): Promise<boolean> {
709
+ await this.downloadKeys([userId]);
710
+ return this.deviceList.getStoredCrossSigningForUser(userId) !== null;
711
+ }
712
+
713
+ /**
714
+ * Checks whether cross signing:
715
+ * - is enabled on this account and trusted by this device
716
+ * - has private keys either cached locally or stored in secret storage
717
+ *
718
+ * If this function returns false, bootstrapCrossSigning() can be used
719
+ * to fix things such that it returns true. That is to say, after
720
+ * bootstrapCrossSigning() completes successfully, this function should
721
+ * return true.
722
+ *
723
+ * The cross-signing API is currently UNSTABLE and may change without notice.
724
+ *
725
+ * @returns True if cross-signing is ready to be used on this device
726
+ */
727
+ public async isCrossSigningReady(): Promise<boolean> {
728
+ const publicKeysOnDevice = this.crossSigningInfo.getId();
729
+ const privateKeysExistSomewhere =
730
+ (await this.crossSigningInfo.isStoredInKeyCache()) ||
731
+ (await this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage));
732
+
733
+ return !!(publicKeysOnDevice && privateKeysExistSomewhere);
734
+ }
735
+
736
+ /**
737
+ * Checks whether secret storage:
738
+ * - is enabled on this account
739
+ * - is storing cross-signing private keys
740
+ * - is storing session backup key (if enabled)
741
+ *
742
+ * If this function returns false, bootstrapSecretStorage() can be used
743
+ * to fix things such that it returns true. That is to say, after
744
+ * bootstrapSecretStorage() completes successfully, this function should
745
+ * return true.
746
+ *
747
+ * The Secure Secret Storage API is currently UNSTABLE and may change without notice.
748
+ *
749
+ * @returns True if secret storage is ready to be used on this device
750
+ */
751
+ public async isSecretStorageReady(): Promise<boolean> {
752
+ const secretStorageKeyInAccount = await this.secretStorage.hasKey();
753
+ const privateKeysInStorage = await this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage);
754
+ const sessionBackupInStorage =
755
+ !this.backupManager.getKeyBackupEnabled() || (await this.baseApis.isKeyBackupKeyStored());
756
+
757
+ return !!(secretStorageKeyInAccount && privateKeysInStorage && sessionBackupInStorage);
758
+ }
759
+
760
+ /**
761
+ * Implementation of {@link Crypto.CryptoApi#getCrossSigningStatus}
762
+ */
763
+ public async getCrossSigningStatus(): Promise<CrossSigningStatus> {
764
+ const publicKeysOnDevice = Boolean(this.crossSigningInfo.getId());
765
+ const privateKeysInSecretStorage = Boolean(
766
+ await this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage),
767
+ );
768
+ const cacheCallbacks = this.crossSigningInfo.getCacheCallbacks();
769
+ const masterKey = Boolean(await cacheCallbacks.getCrossSigningKeyCache?.("master"));
770
+ const selfSigningKey = Boolean(await cacheCallbacks.getCrossSigningKeyCache?.("self_signing"));
771
+ const userSigningKey = Boolean(await cacheCallbacks.getCrossSigningKeyCache?.("user_signing"));
772
+
773
+ return {
774
+ publicKeysOnDevice,
775
+ privateKeysInSecretStorage,
776
+ privateKeysCachedLocally: {
777
+ masterKey,
778
+ selfSigningKey,
779
+ userSigningKey,
780
+ },
781
+ };
782
+ }
783
+
784
+ /**
785
+ * Bootstrap cross-signing by creating keys if needed. If everything is already
786
+ * set up, then no changes are made, so this is safe to run to ensure
787
+ * cross-signing is ready for use.
788
+ *
789
+ * This function:
790
+ * - creates new cross-signing keys if they are not found locally cached nor in
791
+ * secret storage (if it has been setup)
792
+ *
793
+ * The cross-signing API is currently UNSTABLE and may change without notice.
794
+ */
795
+ public async bootstrapCrossSigning({
796
+ authUploadDeviceSigningKeys,
797
+ setupNewCrossSigning,
798
+ }: BootstrapCrossSigningOpts = {}): Promise<void> {
799
+ logger.log("Bootstrapping cross-signing");
800
+
801
+ const delegateCryptoCallbacks = this.baseApis.cryptoCallbacks;
802
+ const builder = new EncryptionSetupBuilder(this.baseApis.store.accountData, delegateCryptoCallbacks);
803
+ const crossSigningInfo = new CrossSigningInfo(
804
+ this.userId,
805
+ builder.crossSigningCallbacks,
806
+ builder.crossSigningCallbacks,
807
+ );
808
+
809
+ // Reset the cross-signing keys
810
+ const resetCrossSigning = async (): Promise<void> => {
811
+ crossSigningInfo.resetKeys();
812
+ // Sign master key with device key
813
+ await this.signObject(crossSigningInfo.keys.master);
814
+
815
+ // Store auth flow helper function, as we need to call it when uploading
816
+ // to ensure we handle auth errors properly.
817
+ builder.addCrossSigningKeys(authUploadDeviceSigningKeys, crossSigningInfo.keys);
818
+
819
+ // Cross-sign own device
820
+ const device = this.deviceList.getStoredDevice(this.userId, this.deviceId)!;
821
+ const deviceSignature = await crossSigningInfo.signDevice(this.userId, device);
822
+ builder.addKeySignature(this.userId, this.deviceId, deviceSignature!);
823
+
824
+ // Sign message key backup with cross-signing master key
825
+ if (this.backupManager.backupInfo) {
826
+ await crossSigningInfo.signObject(this.backupManager.backupInfo.auth_data, "master");
827
+ builder.addSessionBackup(this.backupManager.backupInfo);
828
+ }
829
+ };
830
+
831
+ const publicKeysOnDevice = this.crossSigningInfo.getId();
832
+ const privateKeysInCache = await this.crossSigningInfo.isStoredInKeyCache();
833
+ const privateKeysInStorage = await this.crossSigningInfo.isStoredInSecretStorage(this.secretStorage);
834
+ const privateKeysExistSomewhere = privateKeysInCache || privateKeysInStorage;
835
+
836
+ // Log all relevant state for easier parsing of debug logs.
837
+ logger.log({
838
+ setupNewCrossSigning,
839
+ publicKeysOnDevice,
840
+ privateKeysInCache,
841
+ privateKeysInStorage,
842
+ privateKeysExistSomewhere,
843
+ });
844
+
845
+ if (!privateKeysExistSomewhere || setupNewCrossSigning) {
846
+ logger.log("Cross-signing private keys not found locally or in secret storage, " + "creating new keys");
847
+ // If a user has multiple devices, it important to only call bootstrap
848
+ // as part of some UI flow (and not silently during startup), as they
849
+ // may have setup cross-signing on a platform which has not saved keys
850
+ // to secret storage, and this would reset them. In such a case, you
851
+ // should prompt the user to verify any existing devices first (and
852
+ // request private keys from those devices) before calling bootstrap.
853
+ await resetCrossSigning();
854
+ } else if (publicKeysOnDevice && privateKeysInCache) {
855
+ logger.log("Cross-signing public keys trusted and private keys found locally");
856
+ } else if (privateKeysInStorage) {
857
+ logger.log(
858
+ "Cross-signing private keys not found locally, but they are available " +
859
+ "in secret storage, reading storage and caching locally",
860
+ );
861
+ await this.checkOwnCrossSigningTrust({
862
+ allowPrivateKeyRequests: true,
863
+ });
864
+ }
865
+
866
+ // Assuming no app-supplied callback, default to storing new private keys in
867
+ // secret storage if it exists. If it does not, it is assumed this will be
868
+ // done as part of setting up secret storage later.
869
+ const crossSigningPrivateKeys = builder.crossSigningCallbacks.privateKeys;
870
+ if (crossSigningPrivateKeys.size && !this.baseApis.cryptoCallbacks.saveCrossSigningKeys) {
871
+ const secretStorage = new ServerSideSecretStorageImpl(
872
+ builder.accountDataClientAdapter,
873
+ builder.ssssCryptoCallbacks,
874
+ );
875
+ if (await secretStorage.hasKey()) {
876
+ logger.log("Storing new cross-signing private keys in secret storage");
877
+ // This is writing to in-memory account data in
878
+ // builder.accountDataClientAdapter so won't fail
879
+ await CrossSigningInfo.storeInSecretStorage(crossSigningPrivateKeys, secretStorage);
880
+ }
881
+ }
882
+
883
+ const operation = builder.buildOperation();
884
+ await operation.apply(this);
885
+ // This persists private keys and public keys as trusted,
886
+ // only do this if apply succeeded for now as retry isn't in place yet
887
+ await builder.persist(this);
888
+
889
+ logger.log("Cross-signing ready");
890
+ }
891
+
892
+ /**
893
+ * Bootstrap Secure Secret Storage if needed by creating a default key. If everything is
894
+ * already set up, then no changes are made, so this is safe to run to ensure secret
895
+ * storage is ready for use.
896
+ *
897
+ * This function
898
+ * - creates a new Secure Secret Storage key if no default key exists
899
+ * - if a key backup exists, it is migrated to store the key in the Secret
900
+ * Storage
901
+ * - creates a backup if none exists, and one is requested
902
+ * - migrates Secure Secret Storage to use the latest algorithm, if an outdated
903
+ * algorithm is found
904
+ *
905
+ * The Secure Secret Storage API is currently UNSTABLE and may change without notice.
906
+ *
907
+ * Returns:
908
+ * A promise which resolves to key creation data for
909
+ * SecretStorage#addKey: an object with `passphrase` etc fields.
910
+ */
911
+ // TODO this does not resolve with what it says it does
912
+ public async bootstrapSecretStorage({
913
+ createSecretStorageKey = async (): Promise<IRecoveryKey> => ({}) as IRecoveryKey,
914
+ keyBackupInfo,
915
+ setupNewKeyBackup,
916
+ setupNewSecretStorage,
917
+ getKeyBackupPassphrase,
918
+ }: ICreateSecretStorageOpts = {}): Promise<void> {
919
+ logger.log("Bootstrapping Secure Secret Storage");
920
+ const delegateCryptoCallbacks = this.baseApis.cryptoCallbacks;
921
+ const builder = new EncryptionSetupBuilder(this.baseApis.store.accountData, delegateCryptoCallbacks);
922
+ const secretStorage = new ServerSideSecretStorageImpl(
923
+ builder.accountDataClientAdapter,
924
+ builder.ssssCryptoCallbacks,
925
+ );
926
+
927
+ // the ID of the new SSSS key, if we create one
928
+ let newKeyId: string | null = null;
929
+
930
+ // create a new SSSS key and set it as default
931
+ const createSSSS = async (opts: AddSecretStorageKeyOpts): Promise<string> => {
932
+ const { keyId, keyInfo } = await secretStorage.addKey(SECRET_STORAGE_ALGORITHM_V1_AES, opts);
933
+
934
+ // make the private key available to encrypt 4S secrets
935
+ builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, opts.key);
936
+
937
+ await secretStorage.setDefaultKeyId(keyId);
938
+ return keyId;
939
+ };
940
+
941
+ const ensureCanCheckPassphrase = async (keyId: string, keyInfo: SecretStorageKeyDescription): Promise<void> => {
942
+ if (!keyInfo.mac) {
943
+ const key = await this.baseApis.cryptoCallbacks.getSecretStorageKey?.(
944
+ { keys: { [keyId]: keyInfo } },
945
+ "",
946
+ );
947
+ if (key) {
948
+ const privateKey = key[1];
949
+ builder.ssssCryptoCallbacks.addPrivateKey(keyId, keyInfo, privateKey);
950
+ const { iv, mac } = await calculateKeyCheck(privateKey);
951
+ keyInfo.iv = iv;
952
+ keyInfo.mac = mac;
953
+
954
+ await builder.setAccountData(`m.secret_storage.key.${keyId}`, keyInfo);
955
+ }
956
+ }
957
+ };
958
+
959
+ const signKeyBackupWithCrossSigning = async (keyBackupAuthData: IKeyBackupInfo["auth_data"]): Promise<void> => {
960
+ if (this.crossSigningInfo.getId() && (await this.crossSigningInfo.isStoredInKeyCache("master"))) {
961
+ try {
962
+ logger.log("Adding cross-signing signature to key backup");
963
+ await this.crossSigningInfo.signObject(keyBackupAuthData, "master");
964
+ } catch (e) {
965
+ // This step is not critical (just helpful), so we catch here
966
+ // and continue if it fails.
967
+ logger.error("Signing key backup with cross-signing keys failed", e);
968
+ }
969
+ } else {
970
+ logger.warn("Cross-signing keys not available, skipping signature on key backup");
971
+ }
972
+ };
973
+
974
+ const oldSSSSKey = await this.secretStorage.getKey();
975
+ const [oldKeyId, oldKeyInfo] = oldSSSSKey || [null, null];
976
+ const storageExists =
977
+ !setupNewSecretStorage && oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES;
978
+
979
+ // Log all relevant state for easier parsing of debug logs.
980
+ logger.log({
981
+ keyBackupInfo,
982
+ setupNewKeyBackup,
983
+ setupNewSecretStorage,
984
+ storageExists,
985
+ oldKeyInfo,
986
+ });
987
+
988
+ if (!storageExists && !keyBackupInfo) {
989
+ // either we don't have anything, or we've been asked to restart
990
+ // from scratch
991
+ logger.log("Secret storage does not exist, creating new storage key");
992
+
993
+ // if we already have a usable default SSSS key and aren't resetting
994
+ // SSSS just use it. otherwise, create a new one
995
+ // Note: we leave the old SSSS key in place: there could be other
996
+ // secrets using it, in theory. We could move them to the new key but a)
997
+ // that would mean we'd need to prompt for the old passphrase, and b)
998
+ // it's not clear that would be the right thing to do anyway.
999
+ const { keyInfo, privateKey } = await createSecretStorageKey();
1000
+ newKeyId = await createSSSS({ passphrase: keyInfo?.passphrase, key: privateKey, name: keyInfo?.name });
1001
+ } else if (!storageExists && keyBackupInfo) {
1002
+ // we have an existing backup, but no SSSS
1003
+ logger.log("Secret storage does not exist, using key backup key");
1004
+
1005
+ // if we have the backup key already cached, use it; otherwise use the
1006
+ // callback to prompt for the key
1007
+ const backupKey = (await this.getSessionBackupPrivateKey()) || (await getKeyBackupPassphrase?.());
1008
+
1009
+ // create a new SSSS key and use the backup key as the new SSSS key
1010
+ const opts = { key: backupKey } as AddSecretStorageKeyOpts;
1011
+
1012
+ if (keyBackupInfo.auth_data.private_key_salt && keyBackupInfo.auth_data.private_key_iterations) {
1013
+ // FIXME: ???
1014
+ opts.passphrase = {
1015
+ algorithm: "m.pbkdf2",
1016
+ iterations: keyBackupInfo.auth_data.private_key_iterations,
1017
+ salt: keyBackupInfo.auth_data.private_key_salt,
1018
+ bits: 256,
1019
+ };
1020
+ }
1021
+
1022
+ newKeyId = await createSSSS(opts);
1023
+
1024
+ // store the backup key in secret storage
1025
+ await secretStorage.store("m.megolm_backup.v1", encodeBase64(backupKey!), [newKeyId]);
1026
+
1027
+ // The backup is trusted because the user provided the private key.
1028
+ // Sign the backup with the cross-signing key so the key backup can
1029
+ // be trusted via cross-signing.
1030
+ await signKeyBackupWithCrossSigning(keyBackupInfo.auth_data);
1031
+
1032
+ builder.addSessionBackup(keyBackupInfo);
1033
+ } else {
1034
+ // 4S is already set up
1035
+ logger.log("Secret storage exists");
1036
+
1037
+ if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
1038
+ // make sure that the default key has the information needed to
1039
+ // check the passphrase
1040
+ await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
1041
+ }
1042
+ }
1043
+
1044
+ // If we have cross-signing private keys cached, store them in secret
1045
+ // storage if they are not there already.
1046
+ if (
1047
+ !this.baseApis.cryptoCallbacks.saveCrossSigningKeys &&
1048
+ (await this.isCrossSigningReady()) &&
1049
+ (newKeyId || !(await this.crossSigningInfo.isStoredInSecretStorage(secretStorage)))
1050
+ ) {
1051
+ logger.log("Copying cross-signing private keys from cache to secret storage");
1052
+ const crossSigningPrivateKeys = await this.crossSigningInfo.getCrossSigningKeysFromCache();
1053
+ // This is writing to in-memory account data in
1054
+ // builder.accountDataClientAdapter so won't fail
1055
+ await CrossSigningInfo.storeInSecretStorage(crossSigningPrivateKeys, secretStorage);
1056
+ }
1057
+
1058
+ if (setupNewKeyBackup && !keyBackupInfo) {
1059
+ logger.log("Creating new message key backup version");
1060
+ const info = await this.baseApis.prepareKeyBackupVersion(
1061
+ null /* random key */,
1062
+ // don't write to secret storage, as it will write to this.secretStorage.
1063
+ // Here, we want to capture all the side-effects of bootstrapping,
1064
+ // and want to write to the local secretStorage object
1065
+ { secureSecretStorage: false },
1066
+ );
1067
+ // write the key to 4S
1068
+ const privateKey = decodeRecoveryKey(info.recovery_key);
1069
+ await secretStorage.store("m.megolm_backup.v1", encodeBase64(privateKey));
1070
+
1071
+ // create keyBackupInfo object to add to builder
1072
+ const data: IKeyBackupInfo = {
1073
+ algorithm: info.algorithm,
1074
+ auth_data: info.auth_data,
1075
+ };
1076
+
1077
+ // Sign with cross-signing master key
1078
+ await signKeyBackupWithCrossSigning(data.auth_data);
1079
+
1080
+ // sign with the device fingerprint
1081
+ await this.signObject(data.auth_data);
1082
+
1083
+ builder.addSessionBackup(data);
1084
+ }
1085
+
1086
+ // Cache the session backup key
1087
+ const sessionBackupKey = await secretStorage.get("m.megolm_backup.v1");
1088
+ if (sessionBackupKey) {
1089
+ logger.info("Got session backup key from secret storage: caching");
1090
+ // fix up the backup key if it's in the wrong format, and replace
1091
+ // in secret storage
1092
+ const fixedBackupKey = fixBackupKey(sessionBackupKey);
1093
+ if (fixedBackupKey) {
1094
+ const keyId = newKeyId || oldKeyId;
1095
+ await secretStorage.store("m.megolm_backup.v1", fixedBackupKey, keyId ? [keyId] : null);
1096
+ }
1097
+ const decodedBackupKey = new Uint8Array(decodeBase64(fixedBackupKey || sessionBackupKey));
1098
+ builder.addSessionBackupPrivateKeyToCache(decodedBackupKey);
1099
+ } else if (this.backupManager.getKeyBackupEnabled()) {
1100
+ // key backup is enabled but we don't have a session backup key in SSSS: see if we have one in
1101
+ // the cache or the user can provide one, and if so, write it to SSSS
1102
+ const backupKey = (await this.getSessionBackupPrivateKey()) || (await getKeyBackupPassphrase?.());
1103
+ if (!backupKey) {
1104
+ // This will require user intervention to recover from since we don't have the key
1105
+ // backup key anywhere. The user should probably just set up a new key backup and
1106
+ // the key for the new backup will be stored. If we hit this scenario in the wild
1107
+ // with any frequency, we should do more than just log an error.
1108
+ logger.error("Key backup is enabled but couldn't get key backup key!");
1109
+ return;
1110
+ }
1111
+ logger.info("Got session backup key from cache/user that wasn't in SSSS: saving to SSSS");
1112
+ await secretStorage.store("m.megolm_backup.v1", encodeBase64(backupKey));
1113
+ }
1114
+
1115
+ const operation = builder.buildOperation();
1116
+ await operation.apply(this);
1117
+ // this persists private keys and public keys as trusted,
1118
+ // only do this if apply succeeded for now as retry isn't in place yet
1119
+ await builder.persist(this);
1120
+
1121
+ logger.log("Secure Secret Storage ready");
1122
+ }
1123
+
1124
+ /**
1125
+ * Implementation of {@link Crypto.CryptoApi#resetKeyBackup}.
1126
+ */
1127
+ public async resetKeyBackup(): Promise<void> {
1128
+ // Delete existing ones
1129
+ // There is no use case for having several key backup version live server side.
1130
+ // Even if not deleted it would be lost as the key to restore is lost.
1131
+ // There should be only one backup at a time.
1132
+ await this.backupManager.deleteAllKeyBackupVersions();
1133
+
1134
+ const info = await this.backupManager.prepareKeyBackupVersion();
1135
+
1136
+ await this.signObject(info.auth_data);
1137
+
1138
+ // add new key backup
1139
+ const { version } = await this.baseApis.http.authedRequest<{ version: string }>(
1140
+ Method.Post,
1141
+ "/room_keys/version",
1142
+ undefined,
1143
+ info,
1144
+ {
1145
+ prefix: ClientPrefix.V3,
1146
+ },
1147
+ );
1148
+
1149
+ logger.log(`Created backup version ${version}`);
1150
+
1151
+ // write the key to 4S
1152
+ const privateKey = info.privateKey;
1153
+ await this.secretStorage.store("m.megolm_backup.v1", encodeBase64(privateKey));
1154
+ await this.storeSessionBackupPrivateKey(privateKey);
1155
+
1156
+ await this.backupManager.checkAndStart();
1157
+ await this.backupManager.scheduleAllGroupSessionsForBackup();
1158
+ }
1159
+
1160
+ /**
1161
+ * Implementation of {@link Crypto.CryptoApi#deleteKeyBackupVersion}.
1162
+ */
1163
+ public async deleteKeyBackupVersion(version: string): Promise<void> {
1164
+ await this.backupManager.deleteKeyBackupVersion(version);
1165
+ }
1166
+
1167
+ /**
1168
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#addKey}.
1169
+ */
1170
+ public addSecretStorageKey(
1171
+ algorithm: string,
1172
+ opts: AddSecretStorageKeyOpts,
1173
+ keyID?: string,
1174
+ ): Promise<SecretStorageKeyObject> {
1175
+ return this.secretStorage.addKey(algorithm, opts, keyID);
1176
+ }
1177
+
1178
+ /**
1179
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#hasKey}.
1180
+ */
1181
+ public hasSecretStorageKey(keyID?: string): Promise<boolean> {
1182
+ return this.secretStorage.hasKey(keyID);
1183
+ }
1184
+
1185
+ /**
1186
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#getKey}.
1187
+ */
1188
+ public getSecretStorageKey(keyID?: string): Promise<SecretStorageKeyTuple | null> {
1189
+ return this.secretStorage.getKey(keyID);
1190
+ }
1191
+
1192
+ /**
1193
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#store}.
1194
+ */
1195
+ public storeSecret(name: string, secret: string, keys?: string[]): Promise<void> {
1196
+ return this.secretStorage.store(name, secret, keys);
1197
+ }
1198
+
1199
+ /**
1200
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#get}.
1201
+ */
1202
+ public getSecret(name: string): Promise<string | undefined> {
1203
+ return this.secretStorage.get(name);
1204
+ }
1205
+
1206
+ /**
1207
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#isStored}.
1208
+ */
1209
+ public isSecretStored(name: string): Promise<Record<string, SecretStorageKeyDescription> | null> {
1210
+ return this.secretStorage.isStored(name);
1211
+ }
1212
+
1213
+ public requestSecret(name: string, devices: string[]): ISecretRequest {
1214
+ if (!devices) {
1215
+ devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(this.userId));
1216
+ }
1217
+ return this.secretStorage.request(name, devices);
1218
+ }
1219
+
1220
+ /**
1221
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#getDefaultKeyId}.
1222
+ */
1223
+ public getDefaultSecretStorageKeyId(): Promise<string | null> {
1224
+ return this.secretStorage.getDefaultKeyId();
1225
+ }
1226
+
1227
+ /**
1228
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#setDefaultKeyId}.
1229
+ */
1230
+ public setDefaultSecretStorageKeyId(k: string): Promise<void> {
1231
+ return this.secretStorage.setDefaultKeyId(k);
1232
+ }
1233
+
1234
+ /**
1235
+ * @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#checkKey}.
1236
+ */
1237
+ public checkSecretStorageKey(key: Uint8Array, info: SecretStorageKeyDescription): Promise<boolean> {
1238
+ return this.secretStorage.checkKey(key, info);
1239
+ }
1240
+
1241
+ /**
1242
+ * Checks that a given secret storage private key matches a given public key.
1243
+ * This can be used by the getSecretStorageKey callback to verify that the
1244
+ * private key it is about to supply is the one that was requested.
1245
+ *
1246
+ * @param privateKey - The private key
1247
+ * @param expectedPublicKey - The public key
1248
+ * @returns true if the key matches, otherwise false
1249
+ */
1250
+ public checkSecretStoragePrivateKey(privateKey: Uint8Array, expectedPublicKey: string): boolean {
1251
+ let decryption: PkDecryption | null = null;
1252
+ try {
1253
+ decryption = new globalThis.Olm.PkDecryption();
1254
+ const gotPubkey = decryption.init_with_private_key(privateKey);
1255
+ // make sure it agrees with the given pubkey
1256
+ return gotPubkey === expectedPublicKey;
1257
+ } finally {
1258
+ decryption?.free();
1259
+ }
1260
+ }
1261
+
1262
+ /**
1263
+ * Fetches the backup private key, if cached
1264
+ * @returns the key, if any, or null
1265
+ */
1266
+ public async getSessionBackupPrivateKey(): Promise<Uint8Array | null> {
1267
+ const encodedKey = await new Promise<Uint8Array | AESEncryptedSecretStoragePayload | string | null>(
1268
+ (resolve) => {
1269
+ this.cryptoStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
1270
+ this.cryptoStore.getSecretStorePrivateKey(txn, resolve, "m.megolm_backup.v1");
1271
+ });
1272
+ },
1273
+ );
1274
+
1275
+ let key: Uint8Array | null = null;
1276
+
1277
+ // make sure we have a Uint8Array, rather than a string
1278
+ if (typeof encodedKey === "string") {
1279
+ key = new Uint8Array(decodeBase64(fixBackupKey(encodedKey) || encodedKey));
1280
+ await this.storeSessionBackupPrivateKey(key);
1281
+ }
1282
+ if (encodedKey && typeof encodedKey === "object" && "ciphertext" in encodedKey) {
1283
+ const pickleKey = Buffer.from(this.olmDevice.pickleKey);
1284
+ const decrypted = await decryptAESSecretStorageItem(encodedKey, pickleKey, "m.megolm_backup.v1");
1285
+ key = decodeBase64(decrypted);
1286
+ }
1287
+ return key;
1288
+ }
1289
+
1290
+ /**
1291
+ * Stores the session backup key to the cache
1292
+ * @param key - the private key
1293
+ * @returns a promise so you can catch failures
1294
+ */
1295
+ public async storeSessionBackupPrivateKey(key: ArrayLike<number>, version?: string): Promise<void> {
1296
+ if (!(key instanceof Uint8Array)) {
1297
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
1298
+ throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`);
1299
+ }
1300
+ const pickleKey = Buffer.from(this.olmDevice.pickleKey);
1301
+ const encryptedKey = await encryptAESSecretStorageItem(encodeBase64(key), pickleKey, "m.megolm_backup.v1");
1302
+ return this.cryptoStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
1303
+ this.cryptoStore.storeSecretStorePrivateKey(txn, "m.megolm_backup.v1", encryptedKey);
1304
+ });
1305
+ }
1306
+
1307
+ /**
1308
+ * Implementation of {@link Crypto.loadSessionBackupPrivateKeyFromSecretStorage}.
1309
+ */
1310
+ public loadSessionBackupPrivateKeyFromSecretStorage(): Promise<void> {
1311
+ throw new Error("Not implmeented");
1312
+ }
1313
+
1314
+ /**
1315
+ * Get the current status of key backup.
1316
+ *
1317
+ * Implementation of {@link Crypto.CryptoApi.getActiveSessionBackupVersion}.
1318
+ */
1319
+ public async getActiveSessionBackupVersion(): Promise<string | null> {
1320
+ if (this.backupManager.getKeyBackupEnabled()) {
1321
+ return this.backupManager.version ?? null;
1322
+ }
1323
+ return null;
1324
+ }
1325
+
1326
+ /**
1327
+ * Implementation of {@link Crypto.CryptoApi#getKeyBackupInfo}.
1328
+ */
1329
+ public async getKeyBackupInfo(): Promise<KeyBackupInfo | null> {
1330
+ throw new Error("Not implemented");
1331
+ }
1332
+
1333
+ /**
1334
+ * Determine if a key backup can be trusted.
1335
+ *
1336
+ * Implementation of {@link Crypto.CryptoApi.isKeyBackupTrusted}.
1337
+ */
1338
+ public async isKeyBackupTrusted(info: KeyBackupInfo): Promise<BackupTrustInfo> {
1339
+ const trustInfo = await this.backupManager.isKeyBackupTrusted(info);
1340
+ return backupTrustInfoFromLegacyTrustInfo(trustInfo);
1341
+ }
1342
+
1343
+ /**
1344
+ * Force a re-check of the key backup and enable/disable it as appropriate.
1345
+ *
1346
+ * Implementation of {@link Crypto.CryptoApi.checkKeyBackupAndEnable}.
1347
+ */
1348
+ public async checkKeyBackupAndEnable(): Promise<KeyBackupCheck | null> {
1349
+ const checkResult = await this.backupManager.checkKeyBackup();
1350
+ if (!checkResult || !checkResult.backupInfo) return null;
1351
+ return {
1352
+ backupInfo: checkResult.backupInfo,
1353
+ trustInfo: backupTrustInfoFromLegacyTrustInfo(checkResult.trustInfo),
1354
+ };
1355
+ }
1356
+
1357
+ /**
1358
+ * Checks that a given cross-signing private key matches a given public key.
1359
+ * This can be used by the getCrossSigningKey callback to verify that the
1360
+ * private key it is about to supply is the one that was requested.
1361
+ *
1362
+ * @param privateKey - The private key
1363
+ * @param expectedPublicKey - The public key
1364
+ * @returns true if the key matches, otherwise false
1365
+ */
1366
+ public checkCrossSigningPrivateKey(privateKey: Uint8Array, expectedPublicKey: string): boolean {
1367
+ let signing: PkSigning | null = null;
1368
+ try {
1369
+ signing = new globalThis.Olm.PkSigning();
1370
+ const gotPubkey = signing.init_with_seed(privateKey);
1371
+ // make sure it agrees with the given pubkey
1372
+ return gotPubkey === expectedPublicKey;
1373
+ } finally {
1374
+ signing?.free();
1375
+ }
1376
+ }
1377
+
1378
+ /**
1379
+ * Run various follow-up actions after cross-signing keys have changed locally
1380
+ * (either by resetting the keys for the account or by getting them from secret
1381
+ * storage), such as signing the current device, upgrading device
1382
+ * verifications, etc.
1383
+ */
1384
+ private async afterCrossSigningLocalKeyChange(): Promise<void> {
1385
+ logger.info("Starting cross-signing key change post-processing");
1386
+
1387
+ // sign the current device with the new key, and upload to the server
1388
+ const device = this.deviceList.getStoredDevice(this.userId, this.deviceId)!;
1389
+ const signedDevice = await this.crossSigningInfo.signDevice(this.userId, device);
1390
+ logger.info(`Starting background key sig upload for ${this.deviceId}`);
1391
+
1392
+ const upload = ({ shouldEmit = false }): Promise<void> => {
1393
+ return this.baseApis
1394
+ .uploadKeySignatures({
1395
+ [this.userId]: {
1396
+ [this.deviceId]: signedDevice!,
1397
+ },
1398
+ })
1399
+ .then((response) => {
1400
+ const { failures } = response || {};
1401
+ if (Object.keys(failures || []).length > 0) {
1402
+ if (shouldEmit) {
1403
+ this.baseApis.emit(
1404
+ CryptoEvent.KeySignatureUploadFailure,
1405
+ failures,
1406
+ "afterCrossSigningLocalKeyChange",
1407
+ upload, // continuation
1408
+ );
1409
+ }
1410
+ throw new KeySignatureUploadError("Key upload failed", { failures });
1411
+ }
1412
+ logger.info(`Finished background key sig upload for ${this.deviceId}`);
1413
+ })
1414
+ .catch((e) => {
1415
+ logger.error(`Error during background key sig upload for ${this.deviceId}`, e);
1416
+ });
1417
+ };
1418
+ upload({ shouldEmit: true });
1419
+
1420
+ const shouldUpgradeCb = this.baseApis.cryptoCallbacks.shouldUpgradeDeviceVerifications;
1421
+ if (shouldUpgradeCb) {
1422
+ logger.info("Starting device verification upgrade");
1423
+
1424
+ // Check all users for signatures if upgrade callback present
1425
+ // FIXME: do this in batches
1426
+ const users: Record<string, IDeviceVerificationUpgrade> = {};
1427
+ for (const [userId, crossSigningInfo] of Object.entries(this.deviceList.crossSigningInfo)) {
1428
+ const upgradeInfo = await this.checkForDeviceVerificationUpgrade(
1429
+ userId,
1430
+ CrossSigningInfo.fromStorage(crossSigningInfo, userId),
1431
+ );
1432
+ if (upgradeInfo) {
1433
+ users[userId] = upgradeInfo;
1434
+ }
1435
+ }
1436
+
1437
+ if (Object.keys(users).length > 0) {
1438
+ logger.info(`Found ${Object.keys(users).length} verif users to upgrade`);
1439
+ try {
1440
+ const usersToUpgrade = await shouldUpgradeCb({ users: users });
1441
+ if (usersToUpgrade) {
1442
+ for (const userId of usersToUpgrade) {
1443
+ if (userId in users) {
1444
+ await this.baseApis.setDeviceVerified(userId, users[userId].crossSigningInfo.getId()!);
1445
+ }
1446
+ }
1447
+ }
1448
+ } catch (e) {
1449
+ logger.log("shouldUpgradeDeviceVerifications threw an error: not upgrading", e);
1450
+ }
1451
+ }
1452
+
1453
+ logger.info("Finished device verification upgrade");
1454
+ }
1455
+
1456
+ logger.info("Finished cross-signing key change post-processing");
1457
+ }
1458
+
1459
+ /**
1460
+ * Check if a user's cross-signing key is a candidate for upgrading from device
1461
+ * verification.
1462
+ *
1463
+ * @param userId - the user whose cross-signing information is to be checked
1464
+ * @param crossSigningInfo - the cross-signing information to check
1465
+ */
1466
+ private async checkForDeviceVerificationUpgrade(
1467
+ userId: string,
1468
+ crossSigningInfo: CrossSigningInfo,
1469
+ ): Promise<IDeviceVerificationUpgrade | undefined> {
1470
+ // only upgrade if this is the first cross-signing key that we've seen for
1471
+ // them, and if their cross-signing key isn't already verified
1472
+ const trustLevel = this.crossSigningInfo.checkUserTrust(crossSigningInfo);
1473
+ if (crossSigningInfo.firstUse && !trustLevel.isVerified()) {
1474
+ const devices = this.deviceList.getRawStoredDevicesForUser(userId);
1475
+ const deviceIds = await this.checkForValidDeviceSignature(userId, crossSigningInfo.keys.master, devices);
1476
+ if (deviceIds.length) {
1477
+ return {
1478
+ devices: deviceIds.map((deviceId) => DeviceInfo.fromStorage(devices[deviceId], deviceId)),
1479
+ crossSigningInfo,
1480
+ };
1481
+ }
1482
+ }
1483
+ }
1484
+
1485
+ /**
1486
+ * Check if the cross-signing key is signed by a verified device.
1487
+ *
1488
+ * @param userId - the user ID whose key is being checked
1489
+ * @param key - the key that is being checked
1490
+ * @param devices - the user's devices. Should be a map from device ID
1491
+ * to device info
1492
+ */
1493
+ private async checkForValidDeviceSignature(
1494
+ userId: string,
1495
+ key: CrossSigningKeyInfo,
1496
+ devices: Record<string, IDevice>,
1497
+ ): Promise<string[]> {
1498
+ const deviceIds: string[] = [];
1499
+ if (devices && key.signatures && key.signatures[userId]) {
1500
+ for (const signame of Object.keys(key.signatures[userId])) {
1501
+ const [, deviceId] = signame.split(":", 2);
1502
+ if (deviceId in devices && devices[deviceId].verified === DeviceVerification.VERIFIED) {
1503
+ try {
1504
+ await olmlib.verifySignature(
1505
+ this.olmDevice,
1506
+ key,
1507
+ userId,
1508
+ deviceId,
1509
+ devices[deviceId].keys[signame],
1510
+ );
1511
+ deviceIds.push(deviceId);
1512
+ } catch {}
1513
+ }
1514
+ }
1515
+ }
1516
+ return deviceIds;
1517
+ }
1518
+
1519
+ /**
1520
+ * Get the user's cross-signing key ID.
1521
+ *
1522
+ * @param type - The type of key to get the ID of. One of
1523
+ * "master", "self_signing", or "user_signing". Defaults to "master".
1524
+ *
1525
+ * @returns the key ID
1526
+ */
1527
+ public getCrossSigningKeyId(type: CrossSigningKey = CrossSigningKey.Master): Promise<string | null> {
1528
+ return Promise.resolve(this.getCrossSigningId(type));
1529
+ }
1530
+
1531
+ // old name, for backwards compatibility
1532
+ public getCrossSigningId(type: string): string | null {
1533
+ return this.crossSigningInfo.getId(type);
1534
+ }
1535
+
1536
+ /**
1537
+ * Get the cross signing information for a given user.
1538
+ *
1539
+ * @param userId - the user ID to get the cross-signing info for.
1540
+ *
1541
+ * @returns the cross signing information for the user.
1542
+ */
1543
+ public getStoredCrossSigningForUser(userId: string): CrossSigningInfo | null {
1544
+ return this.deviceList.getStoredCrossSigningForUser(userId);
1545
+ }
1546
+
1547
+ /**
1548
+ * Check whether a given user is trusted.
1549
+ *
1550
+ * @param userId - The ID of the user to check.
1551
+ *
1552
+ * @returns
1553
+ */
1554
+ public checkUserTrust(userId: string): UserTrustLevel {
1555
+ const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId);
1556
+ if (!userCrossSigning) {
1557
+ return new UserTrustLevel(false, false, false);
1558
+ }
1559
+ return this.crossSigningInfo.checkUserTrust(userCrossSigning);
1560
+ }
1561
+
1562
+ /**
1563
+ * Implementation of {@link Crypto.CryptoApi.getUserVerificationStatus}.
1564
+ */
1565
+ public async getUserVerificationStatus(userId: string): Promise<UserTrustLevel> {
1566
+ return this.checkUserTrust(userId);
1567
+ }
1568
+
1569
+ /**
1570
+ * Implementation of {@link Crypto.CryptoApi.pinCurrentUserIdentity}.
1571
+ */
1572
+ public async pinCurrentUserIdentity(userId: string): Promise<void> {
1573
+ throw new Error("not implemented");
1574
+ }
1575
+
1576
+ /**
1577
+ * Check whether a given device is trusted.
1578
+ *
1579
+ * @param userId - The ID of the user whose device is to be checked.
1580
+ * @param deviceId - The ID of the device to check
1581
+ */
1582
+ public async getDeviceVerificationStatus(
1583
+ userId: string,
1584
+ deviceId: string,
1585
+ ): Promise<DeviceVerificationStatus | null> {
1586
+ const device = this.deviceList.getStoredDevice(userId, deviceId);
1587
+ if (!device) {
1588
+ return null;
1589
+ }
1590
+ return this.checkDeviceInfoTrust(userId, device);
1591
+ }
1592
+
1593
+ /**
1594
+ * @deprecated Use {@link Crypto.CryptoApi.getDeviceVerificationStatus}.
1595
+ */
1596
+ public checkDeviceTrust(userId: string, deviceId: string): DeviceTrustLevel {
1597
+ const device = this.deviceList.getStoredDevice(userId, deviceId);
1598
+ return this.checkDeviceInfoTrust(userId, device);
1599
+ }
1600
+
1601
+ /**
1602
+ * Check whether a given deviceinfo is trusted.
1603
+ *
1604
+ * @param userId - The ID of the user whose devices is to be checked.
1605
+ * @param device - The device info object to check
1606
+ *
1607
+ * @deprecated Use {@link Crypto.CryptoApi.getDeviceVerificationStatus}.
1608
+ */
1609
+ public checkDeviceInfoTrust(userId: string, device?: DeviceInfo): DeviceTrustLevel {
1610
+ const trustedLocally = !!device?.isVerified();
1611
+
1612
+ const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId);
1613
+ if (device && userCrossSigning) {
1614
+ // The trustCrossSignedDevices only affects trust of other people's cross-signing
1615
+ // signatures
1616
+ const trustCrossSig = this.trustCrossSignedDevices || userId === this.userId;
1617
+ return this.crossSigningInfo.checkDeviceTrust(userCrossSigning, device, trustedLocally, trustCrossSig);
1618
+ } else {
1619
+ return new DeviceTrustLevel(false, false, trustedLocally, false);
1620
+ }
1621
+ }
1622
+
1623
+ /**
1624
+ * Check whether one of our own devices is cross-signed by our
1625
+ * user's stored keys, regardless of whether we trust those keys yet.
1626
+ *
1627
+ * @param deviceId - The ID of the device to check
1628
+ *
1629
+ * @returns true if the device is cross-signed
1630
+ */
1631
+ public checkIfOwnDeviceCrossSigned(deviceId: string): boolean {
1632
+ const device = this.deviceList.getStoredDevice(this.userId, deviceId);
1633
+ if (!device) return false;
1634
+ const userCrossSigning = this.deviceList.getStoredCrossSigningForUser(this.userId);
1635
+ return (
1636
+ userCrossSigning?.checkDeviceTrust(userCrossSigning, device, false, true).isCrossSigningVerified() ?? false
1637
+ );
1638
+ }
1639
+
1640
+ /*
1641
+ * Event handler for DeviceList's userNewDevices event
1642
+ */
1643
+ private onDeviceListUserCrossSigningUpdated = async (userId: string): Promise<void> => {
1644
+ if (userId === this.userId) {
1645
+ // An update to our own cross-signing key.
1646
+ // Get the new key first:
1647
+ const newCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId);
1648
+ const seenPubkey = newCrossSigning ? newCrossSigning.getId() : null;
1649
+ const currentPubkey = this.crossSigningInfo.getId();
1650
+ const changed = currentPubkey !== seenPubkey;
1651
+
1652
+ if (currentPubkey && seenPubkey && !changed) {
1653
+ // If it's not changed, just make sure everything is up to date
1654
+ await this.checkOwnCrossSigningTrust();
1655
+ } else {
1656
+ // We'll now be in a state where cross-signing on the account is not trusted
1657
+ // because our locally stored cross-signing keys will not match the ones
1658
+ // on the server for our account. So we clear our own stored cross-signing keys,
1659
+ // effectively disabling cross-signing until the user gets verified by the device
1660
+ // that reset the keys
1661
+ this.storeTrustedSelfKeys(null);
1662
+ // emit cross-signing has been disabled
1663
+ this.emit(CryptoEvent.KeysChanged, {});
1664
+ // as the trust for our own user has changed,
1665
+ // also emit an event for this
1666
+ this.emit(CryptoEvent.UserTrustStatusChanged, this.userId, this.checkUserTrust(userId));
1667
+ }
1668
+ } else {
1669
+ await this.checkDeviceVerifications(userId);
1670
+
1671
+ // Update verified before latch using the current state and save the new
1672
+ // latch value in the device list store.
1673
+ const crossSigning = this.deviceList.getStoredCrossSigningForUser(userId);
1674
+ if (crossSigning) {
1675
+ crossSigning.updateCrossSigningVerifiedBefore(this.checkUserTrust(userId).isCrossSigningVerified());
1676
+ this.deviceList.setRawStoredCrossSigningForUser(userId, crossSigning.toStorage());
1677
+ }
1678
+
1679
+ this.emit(CryptoEvent.UserTrustStatusChanged, userId, this.checkUserTrust(userId));
1680
+ }
1681
+ };
1682
+
1683
+ /**
1684
+ * Check the copy of our cross-signing key that we have in the device list and
1685
+ * see if we can get the private key. If so, mark it as trusted.
1686
+ */
1687
+ public async checkOwnCrossSigningTrust({
1688
+ allowPrivateKeyRequests = false,
1689
+ }: ICheckOwnCrossSigningTrustOpts = {}): Promise<void> {
1690
+ const userId = this.userId;
1691
+
1692
+ // Before proceeding, ensure our cross-signing public keys have been
1693
+ // downloaded via the device list.
1694
+ await this.downloadKeys([this.userId]);
1695
+
1696
+ // Also check which private keys are locally cached.
1697
+ const crossSigningPrivateKeys = await this.crossSigningInfo.getCrossSigningKeysFromCache();
1698
+
1699
+ // If we see an update to our own master key, check it against the master
1700
+ // key we have and, if it matches, mark it as verified
1701
+
1702
+ // First, get the new cross-signing info
1703
+ const newCrossSigning = this.deviceList.getStoredCrossSigningForUser(userId);
1704
+ if (!newCrossSigning) {
1705
+ logger.error(
1706
+ "Got cross-signing update event for user " + userId + " but no new cross-signing information found!",
1707
+ );
1708
+ return;
1709
+ }
1710
+
1711
+ const seenPubkey = newCrossSigning.getId()!;
1712
+ const masterChanged = this.crossSigningInfo.getId() !== seenPubkey;
1713
+ const masterExistsNotLocallyCached = newCrossSigning.getId() && !crossSigningPrivateKeys.has("master");
1714
+ if (masterChanged) {
1715
+ logger.info("Got new master public key", seenPubkey);
1716
+ }
1717
+ if (allowPrivateKeyRequests && (masterChanged || masterExistsNotLocallyCached)) {
1718
+ logger.info("Attempting to retrieve cross-signing master private key");
1719
+ let signing: PkSigning | null = null;
1720
+ // It's important for control flow that we leave any errors alone for
1721
+ // higher levels to handle so that e.g. cancelling access properly
1722
+ // aborts any larger operation as well.
1723
+ try {
1724
+ const ret = await this.crossSigningInfo.getCrossSigningKey("master", seenPubkey);
1725
+ signing = ret[1];
1726
+ logger.info("Got cross-signing master private key");
1727
+ } finally {
1728
+ signing?.free();
1729
+ }
1730
+ }
1731
+
1732
+ const oldSelfSigningId = this.crossSigningInfo.getId("self_signing");
1733
+ const oldUserSigningId = this.crossSigningInfo.getId("user_signing");
1734
+
1735
+ // Update the version of our keys in our cross-signing object and the local store
1736
+ this.storeTrustedSelfKeys(newCrossSigning.keys);
1737
+
1738
+ const selfSigningChanged = oldSelfSigningId !== newCrossSigning.getId("self_signing");
1739
+ const userSigningChanged = oldUserSigningId !== newCrossSigning.getId("user_signing");
1740
+
1741
+ const selfSigningExistsNotLocallyCached =
1742
+ newCrossSigning.getId("self_signing") && !crossSigningPrivateKeys.has("self_signing");
1743
+ const userSigningExistsNotLocallyCached =
1744
+ newCrossSigning.getId("user_signing") && !crossSigningPrivateKeys.has("user_signing");
1745
+
1746
+ const keySignatures: Record<string, ISignedKey> = {};
1747
+
1748
+ if (selfSigningChanged) {
1749
+ logger.info("Got new self-signing key", newCrossSigning.getId("self_signing"));
1750
+ }
1751
+ if (allowPrivateKeyRequests && (selfSigningChanged || selfSigningExistsNotLocallyCached)) {
1752
+ logger.info("Attempting to retrieve cross-signing self-signing private key");
1753
+ let signing: PkSigning | null = null;
1754
+ try {
1755
+ const ret = await this.crossSigningInfo.getCrossSigningKey(
1756
+ "self_signing",
1757
+ newCrossSigning.getId("self_signing")!,
1758
+ );
1759
+ signing = ret[1];
1760
+ logger.info("Got cross-signing self-signing private key");
1761
+ } finally {
1762
+ signing?.free();
1763
+ }
1764
+
1765
+ const device = this.deviceList.getStoredDevice(this.userId, this.deviceId)!;
1766
+ const signedDevice = await this.crossSigningInfo.signDevice(this.userId, device);
1767
+ keySignatures[this.deviceId] = signedDevice!;
1768
+ }
1769
+ if (userSigningChanged) {
1770
+ logger.info("Got new user-signing key", newCrossSigning.getId("user_signing"));
1771
+ }
1772
+ if (allowPrivateKeyRequests && (userSigningChanged || userSigningExistsNotLocallyCached)) {
1773
+ logger.info("Attempting to retrieve cross-signing user-signing private key");
1774
+ let signing: PkSigning | null = null;
1775
+ try {
1776
+ const ret = await this.crossSigningInfo.getCrossSigningKey(
1777
+ "user_signing",
1778
+ newCrossSigning.getId("user_signing")!,
1779
+ );
1780
+ signing = ret[1];
1781
+ logger.info("Got cross-signing user-signing private key");
1782
+ } finally {
1783
+ signing?.free();
1784
+ }
1785
+ }
1786
+
1787
+ if (masterChanged) {
1788
+ const masterKey = this.crossSigningInfo.keys.master;
1789
+ await this.signObject(masterKey);
1790
+ const deviceSig = masterKey.signatures![this.userId]["ed25519:" + this.deviceId];
1791
+ // Include only the _new_ device signature in the upload.
1792
+ // We may have existing signatures from deleted devices, which will cause
1793
+ // the entire upload to fail.
1794
+ keySignatures[this.crossSigningInfo.getId()!] = Object.assign({} as ISignedKey, masterKey, {
1795
+ signatures: {
1796
+ [this.userId]: {
1797
+ ["ed25519:" + this.deviceId]: deviceSig,
1798
+ },
1799
+ },
1800
+ });
1801
+ }
1802
+
1803
+ const keysToUpload = Object.keys(keySignatures);
1804
+ if (keysToUpload.length) {
1805
+ const upload = ({ shouldEmit = false }): Promise<void> => {
1806
+ logger.info(`Starting background key sig upload for ${keysToUpload}`);
1807
+ return this.baseApis
1808
+ .uploadKeySignatures({ [this.userId]: keySignatures })
1809
+ .then((response) => {
1810
+ const { failures } = response || {};
1811
+ logger.info(`Finished background key sig upload for ${keysToUpload}`);
1812
+ if (Object.keys(failures || []).length > 0) {
1813
+ if (shouldEmit) {
1814
+ this.baseApis.emit(
1815
+ CryptoEvent.KeySignatureUploadFailure,
1816
+ failures,
1817
+ "checkOwnCrossSigningTrust",
1818
+ upload,
1819
+ );
1820
+ }
1821
+ throw new KeySignatureUploadError("Key upload failed", { failures });
1822
+ }
1823
+ })
1824
+ .catch((e) => {
1825
+ logger.error(`Error during background key sig upload for ${keysToUpload}`, e);
1826
+ });
1827
+ };
1828
+ upload({ shouldEmit: true });
1829
+ }
1830
+
1831
+ this.emit(CryptoEvent.UserTrustStatusChanged, userId, this.checkUserTrust(userId));
1832
+
1833
+ if (masterChanged) {
1834
+ this.emit(CryptoEvent.KeysChanged, {});
1835
+ await this.afterCrossSigningLocalKeyChange();
1836
+ }
1837
+
1838
+ // Now we may be able to trust our key backup
1839
+ await this.backupManager.checkKeyBackup();
1840
+ // FIXME: if we previously trusted the backup, should we automatically sign
1841
+ // the backup with the new key (if not already signed)?
1842
+ }
1843
+
1844
+ /**
1845
+ * Implementation of {@link CryptoBackend#getBackupDecryptor}.
1846
+ */
1847
+ public async getBackupDecryptor(backupInfo: KeyBackupInfo, privKey: ArrayLike<number>): Promise<BackupDecryptor> {
1848
+ if (!(privKey instanceof Uint8Array)) {
1849
+ throw new Error(`getBackupDecryptor expects Uint8Array`);
1850
+ }
1851
+
1852
+ const algorithm = await BackupManager.makeAlgorithm(backupInfo, async () => {
1853
+ return privKey;
1854
+ });
1855
+
1856
+ // If the pubkey computed from the private data we've been given
1857
+ // doesn't match the one in the auth_data, the user has entered
1858
+ // a different recovery key / the wrong passphrase.
1859
+ if (!(await algorithm.keyMatches(privKey))) {
1860
+ return Promise.reject(new MatrixError({ errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY }));
1861
+ }
1862
+
1863
+ return new LibOlmBackupDecryptor(algorithm);
1864
+ }
1865
+
1866
+ /**
1867
+ * Implementation of {@link CryptoBackend#importBackedUpRoomKeys}.
1868
+ */
1869
+ public importBackedUpRoomKeys(
1870
+ keys: IMegolmSessionData[],
1871
+ backupVersion: string,
1872
+ opts: ImportRoomKeysOpts = {},
1873
+ ): Promise<void> {
1874
+ opts.source = "backup";
1875
+ return this.importRoomKeys(keys, opts);
1876
+ }
1877
+
1878
+ /**
1879
+ * Store a set of keys as our own, trusted, cross-signing keys.
1880
+ *
1881
+ * @param keys - The new trusted set of keys
1882
+ */
1883
+ private async storeTrustedSelfKeys(keys: Record<string, CrossSigningKeyInfo> | null): Promise<void> {
1884
+ if (keys) {
1885
+ this.crossSigningInfo.setKeys(keys);
1886
+ } else {
1887
+ this.crossSigningInfo.clearKeys();
1888
+ }
1889
+ await this.cryptoStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
1890
+ this.cryptoStore.storeCrossSigningKeys(txn, this.crossSigningInfo.keys);
1891
+ });
1892
+ }
1893
+
1894
+ /**
1895
+ * Check if the master key is signed by a verified device, and if so, prompt
1896
+ * the application to mark it as verified.
1897
+ *
1898
+ * @param userId - the user ID whose key should be checked
1899
+ */
1900
+ private async checkDeviceVerifications(userId: string): Promise<void> {
1901
+ const shouldUpgradeCb = this.baseApis.cryptoCallbacks.shouldUpgradeDeviceVerifications;
1902
+ if (!shouldUpgradeCb) {
1903
+ // Upgrading skipped when callback is not present.
1904
+ return;
1905
+ }
1906
+ logger.info(`Starting device verification upgrade for ${userId}`);
1907
+ if (this.crossSigningInfo.keys.user_signing) {
1908
+ const crossSigningInfo = this.deviceList.getStoredCrossSigningForUser(userId);
1909
+ if (crossSigningInfo) {
1910
+ const upgradeInfo = await this.checkForDeviceVerificationUpgrade(userId, crossSigningInfo);
1911
+ if (upgradeInfo) {
1912
+ const usersToUpgrade = await shouldUpgradeCb({
1913
+ users: {
1914
+ [userId]: upgradeInfo,
1915
+ },
1916
+ });
1917
+ if (usersToUpgrade.includes(userId)) {
1918
+ await this.baseApis.setDeviceVerified(userId, crossSigningInfo.getId()!);
1919
+ }
1920
+ }
1921
+ }
1922
+ }
1923
+ logger.info(`Finished device verification upgrade for ${userId}`);
1924
+ }
1925
+
1926
+ /**
1927
+ */
1928
+ public enableLazyLoading(): void {
1929
+ this.lazyLoadMembers = true;
1930
+ }
1931
+
1932
+ /**
1933
+ * Tell the crypto module to register for MatrixClient events which it needs to
1934
+ * listen for
1935
+ *
1936
+ * @param eventEmitter - event source where we can register
1937
+ * for event notifications
1938
+ */
1939
+ public registerEventHandlers(
1940
+ eventEmitter: TypedEventEmitter<
1941
+ RoomMemberEvent.Membership | ClientEvent.ToDeviceEvent | RoomEvent.Timeline | MatrixEventEvent.Decrypted,
1942
+ any
1943
+ >,
1944
+ ): void {
1945
+ eventEmitter.on(RoomMemberEvent.Membership, this.onMembership);
1946
+ eventEmitter.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
1947
+ eventEmitter.on(RoomEvent.Timeline, this.onTimelineEvent);
1948
+ eventEmitter.on(MatrixEventEvent.Decrypted, this.onTimelineEvent);
1949
+ }
1950
+
1951
+ /**
1952
+ * @deprecated this does nothing and will be removed in a future version
1953
+ */
1954
+ public start(): void {
1955
+ logger.warn("MatrixClient.crypto.start() is deprecated");
1956
+ }
1957
+
1958
+ /** Stop background processes related to crypto */
1959
+ public stop(): void {
1960
+ this.outgoingRoomKeyRequestManager.stop();
1961
+ this.deviceList.stop();
1962
+ this.dehydrationManager.stop();
1963
+ this.backupManager.stop();
1964
+ }
1965
+
1966
+ /**
1967
+ * Get the Ed25519 key for this device
1968
+ *
1969
+ * @returns base64-encoded ed25519 key.
1970
+ *
1971
+ * @deprecated Use {@link Crypto.CryptoApi#getOwnDeviceKeys}.
1972
+ */
1973
+ public getDeviceEd25519Key(): string | null {
1974
+ return this.olmDevice.deviceEd25519Key;
1975
+ }
1976
+
1977
+ /**
1978
+ * Get the Curve25519 key for this device
1979
+ *
1980
+ * @returns base64-encoded curve25519 key.
1981
+ *
1982
+ * @deprecated Use {@link Crypto.CryptoApi#getOwnDeviceKeys}
1983
+ */
1984
+ public getDeviceCurve25519Key(): string | null {
1985
+ return this.olmDevice.deviceCurve25519Key;
1986
+ }
1987
+
1988
+ /**
1989
+ * Implementation of {@link Crypto.CryptoApi#getOwnDeviceKeys}.
1990
+ */
1991
+ public async getOwnDeviceKeys(): Promise<OwnDeviceKeys> {
1992
+ if (!this.olmDevice.deviceCurve25519Key) {
1993
+ throw new Error("Curve25519 key not yet created");
1994
+ }
1995
+ if (!this.olmDevice.deviceEd25519Key) {
1996
+ throw new Error("Ed25519 key not yet created");
1997
+ }
1998
+ return {
1999
+ ed25519: this.olmDevice.deviceEd25519Key,
2000
+ curve25519: this.olmDevice.deviceCurve25519Key,
2001
+ };
2002
+ }
2003
+
2004
+ /**
2005
+ * Set the global override for whether the client should ever send encrypted
2006
+ * messages to unverified devices. This provides the default for rooms which
2007
+ * do not specify a value.
2008
+ *
2009
+ * @param value - whether to blacklist all unverified devices by default
2010
+ *
2011
+ * @deprecated Set {@link Crypto.CryptoApi#globalBlacklistUnverifiedDevices | CryptoApi.globalBlacklistUnverifiedDevices} directly.
2012
+ */
2013
+ public setGlobalBlacklistUnverifiedDevices(value: boolean): void {
2014
+ this.globalBlacklistUnverifiedDevices = value;
2015
+ }
2016
+
2017
+ /**
2018
+ * @returns whether to blacklist all unverified devices by default
2019
+ *
2020
+ * @deprecated Reference {@link Crypto.CryptoApi#globalBlacklistUnverifiedDevices | CryptoApi.globalBlacklistUnverifiedDevices} directly.
2021
+ */
2022
+ public getGlobalBlacklistUnverifiedDevices(): boolean {
2023
+ return this.globalBlacklistUnverifiedDevices;
2024
+ }
2025
+
2026
+ /**
2027
+ * Upload the device keys to the homeserver.
2028
+ * @returns A promise that will resolve when the keys are uploaded.
2029
+ */
2030
+ public uploadDeviceKeys(): Promise<IKeysUploadResponse> {
2031
+ const deviceKeys = {
2032
+ algorithms: this.supportedAlgorithms,
2033
+ device_id: this.deviceId,
2034
+ keys: this.deviceKeys,
2035
+ user_id: this.userId,
2036
+ };
2037
+
2038
+ return this.signObject(deviceKeys).then(() => {
2039
+ return this.baseApis.uploadKeysRequest({
2040
+ device_keys: deviceKeys as Required<IDeviceKeys>,
2041
+ });
2042
+ });
2043
+ }
2044
+
2045
+ public getNeedsNewFallback(): boolean {
2046
+ return !!this.needsNewFallback;
2047
+ }
2048
+
2049
+ // check if it's time to upload one-time keys, and do so if so.
2050
+ private maybeUploadOneTimeKeys(): void {
2051
+ // frequency with which to check & upload one-time keys
2052
+ const uploadPeriod = 1000 * 60; // one minute
2053
+
2054
+ // max number of keys to upload at once
2055
+ // Creating keys can be an expensive operation so we limit the
2056
+ // number we generate in one go to avoid blocking the application
2057
+ // for too long.
2058
+ const maxKeysPerCycle = 5;
2059
+
2060
+ if (this.oneTimeKeyCheckInProgress) {
2061
+ return;
2062
+ }
2063
+
2064
+ const now = Date.now();
2065
+ if (this.lastOneTimeKeyCheck !== null && now - this.lastOneTimeKeyCheck < uploadPeriod) {
2066
+ // we've done a key upload recently.
2067
+ return;
2068
+ }
2069
+
2070
+ this.lastOneTimeKeyCheck = now;
2071
+
2072
+ // We need to keep a pool of one time public keys on the server so that
2073
+ // other devices can start conversations with us. But we can only store
2074
+ // a finite number of private keys in the olm Account object.
2075
+ // To complicate things further then can be a delay between a device
2076
+ // claiming a public one time key from the server and it sending us a
2077
+ // message. We need to keep the corresponding private key locally until
2078
+ // we receive the message.
2079
+ // But that message might never arrive leaving us stuck with duff
2080
+ // private keys clogging up our local storage.
2081
+ // So we need some kind of engineering compromise to balance all of
2082
+ // these factors.
2083
+
2084
+ // Check how many keys we can store in the Account object.
2085
+ const maxOneTimeKeys = this.olmDevice.maxNumberOfOneTimeKeys();
2086
+ // Try to keep at most half that number on the server. This leaves the
2087
+ // rest of the slots free to hold keys that have been claimed from the
2088
+ // server but we haven't received a message for.
2089
+ // If we run out of slots when generating new keys then olm will
2090
+ // discard the oldest private keys first. This will eventually clean
2091
+ // out stale private keys that won't receive a message.
2092
+ const keyLimit = Math.floor(maxOneTimeKeys / 2);
2093
+
2094
+ const uploadLoop = async (keyCount: number): Promise<void> => {
2095
+ while (keyLimit > keyCount || this.getNeedsNewFallback()) {
2096
+ // Ask olm to generate new one time keys, then upload them to synapse.
2097
+ if (keyLimit > keyCount) {
2098
+ logger.info("generating oneTimeKeys");
2099
+ const keysThisLoop = Math.min(keyLimit - keyCount, maxKeysPerCycle);
2100
+ await this.olmDevice.generateOneTimeKeys(keysThisLoop);
2101
+ }
2102
+
2103
+ if (this.getNeedsNewFallback()) {
2104
+ const fallbackKeys = await this.olmDevice.getFallbackKey();
2105
+ // if fallbackKeys is non-empty, we've already generated a
2106
+ // fallback key, but it hasn't been published yet, so we
2107
+ // can use that instead of generating a new one
2108
+ if (!fallbackKeys.curve25519 || Object.keys(fallbackKeys.curve25519).length == 0) {
2109
+ logger.info("generating fallback key");
2110
+ if (this.fallbackCleanup) {
2111
+ // cancel any pending fallback cleanup because generating
2112
+ // a new fallback key will already drop the old fallback
2113
+ // that would have been dropped, and we don't want to kill
2114
+ // the current key
2115
+ clearTimeout(this.fallbackCleanup);
2116
+ delete this.fallbackCleanup;
2117
+ }
2118
+ await this.olmDevice.generateFallbackKey();
2119
+ }
2120
+ }
2121
+
2122
+ logger.info("calling uploadOneTimeKeys");
2123
+ const res = await this.uploadOneTimeKeys();
2124
+ if (res.one_time_key_counts && res.one_time_key_counts.signed_curve25519) {
2125
+ // if the response contains a more up to date value use this
2126
+ // for the next loop
2127
+ keyCount = res.one_time_key_counts.signed_curve25519;
2128
+ } else {
2129
+ throw new Error(
2130
+ "response for uploading keys does not contain " + "one_time_key_counts.signed_curve25519",
2131
+ );
2132
+ }
2133
+ }
2134
+ };
2135
+
2136
+ this.oneTimeKeyCheckInProgress = true;
2137
+ Promise.resolve()
2138
+ .then(() => {
2139
+ if (this.oneTimeKeyCount !== undefined) {
2140
+ // We already have the current one_time_key count from a /sync response.
2141
+ // Use this value instead of asking the server for the current key count.
2142
+ return Promise.resolve(this.oneTimeKeyCount);
2143
+ }
2144
+ // ask the server how many keys we have
2145
+ return this.baseApis.uploadKeysRequest({}).then((res) => {
2146
+ return res.one_time_key_counts.signed_curve25519 || 0;
2147
+ });
2148
+ })
2149
+ .then((keyCount) => {
2150
+ // Start the uploadLoop with the current keyCount. The function checks if
2151
+ // we need to upload new keys or not.
2152
+ // If there are too many keys on the server then we don't need to
2153
+ // create any more keys.
2154
+ return uploadLoop(keyCount);
2155
+ })
2156
+ .catch((e) => {
2157
+ logger.error("Error uploading one-time keys", e.stack || e);
2158
+ })
2159
+ .finally(() => {
2160
+ // reset oneTimeKeyCount to prevent start uploading based on old data.
2161
+ // it will be set again on the next /sync-response
2162
+ this.oneTimeKeyCount = undefined;
2163
+ this.oneTimeKeyCheckInProgress = false;
2164
+ });
2165
+ }
2166
+
2167
+ // returns a promise which resolves to the response
2168
+ private async uploadOneTimeKeys(): Promise<IKeysUploadResponse> {
2169
+ const promises: Promise<unknown>[] = [];
2170
+
2171
+ let fallbackJson: Record<string, IOneTimeKey> | undefined;
2172
+ if (this.getNeedsNewFallback()) {
2173
+ fallbackJson = {};
2174
+ const fallbackKeys = await this.olmDevice.getFallbackKey();
2175
+ for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) {
2176
+ const k = { key, fallback: true };
2177
+ fallbackJson["signed_curve25519:" + keyId] = k;
2178
+ promises.push(this.signObject(k));
2179
+ }
2180
+ this.needsNewFallback = false;
2181
+ }
2182
+
2183
+ const oneTimeKeys = await this.olmDevice.getOneTimeKeys();
2184
+ const oneTimeJson: Record<string, { key: string }> = {};
2185
+
2186
+ for (const keyId in oneTimeKeys.curve25519) {
2187
+ if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) {
2188
+ const k = {
2189
+ key: oneTimeKeys.curve25519[keyId],
2190
+ };
2191
+ oneTimeJson["signed_curve25519:" + keyId] = k;
2192
+ promises.push(this.signObject(k));
2193
+ }
2194
+ }
2195
+
2196
+ await Promise.all(promises);
2197
+
2198
+ const requestBody: Record<string, any> = {
2199
+ one_time_keys: oneTimeJson,
2200
+ };
2201
+
2202
+ if (fallbackJson) {
2203
+ requestBody["org.matrix.msc2732.fallback_keys"] = fallbackJson;
2204
+ requestBody["fallback_keys"] = fallbackJson;
2205
+ }
2206
+
2207
+ const res = await this.baseApis.uploadKeysRequest(requestBody);
2208
+
2209
+ if (fallbackJson) {
2210
+ this.fallbackCleanup = setTimeout(
2211
+ () => {
2212
+ delete this.fallbackCleanup;
2213
+ this.olmDevice.forgetOldFallbackKey();
2214
+ },
2215
+ 60 * 60 * 1000,
2216
+ );
2217
+ }
2218
+
2219
+ await this.olmDevice.markKeysAsPublished();
2220
+ return res;
2221
+ }
2222
+
2223
+ /**
2224
+ * Download the keys for a list of users and stores the keys in the session
2225
+ * store.
2226
+ * @param userIds - The users to fetch.
2227
+ * @param forceDownload - Always download the keys even if cached.
2228
+ *
2229
+ * @returns A promise which resolves to a map `userId->deviceId->{@link DeviceInfo}`.
2230
+ */
2231
+ public downloadKeys(userIds: string[], forceDownload?: boolean): Promise<DeviceInfoMap> {
2232
+ return this.deviceList.downloadKeys(userIds, !!forceDownload);
2233
+ }
2234
+
2235
+ /**
2236
+ * Get the stored device keys for a user id
2237
+ *
2238
+ * @param userId - the user to list keys for.
2239
+ *
2240
+ * @returns list of devices, or null if we haven't
2241
+ * managed to get a list of devices for this user yet.
2242
+ */
2243
+ public getStoredDevicesForUser(userId: string): Array<DeviceInfo> | null {
2244
+ return this.deviceList.getStoredDevicesForUser(userId);
2245
+ }
2246
+
2247
+ /**
2248
+ * Get the device information for the given list of users.
2249
+ *
2250
+ * @param userIds - The users to fetch.
2251
+ * @param downloadUncached - If true, download the device list for users whose device list we are not
2252
+ * currently tracking. Defaults to false, in which case such users will not appear at all in the result map.
2253
+ *
2254
+ * @returns A map `{@link DeviceMap}`.
2255
+ */
2256
+ public async getUserDeviceInfo(userIds: string[], downloadUncached = false): Promise<DeviceMap> {
2257
+ const deviceMapByUserId = new Map<string, Map<string, Device>>();
2258
+ // Keep the users without device to download theirs keys
2259
+ const usersWithoutDeviceInfo: string[] = [];
2260
+
2261
+ for (const userId of userIds) {
2262
+ const deviceInfos = await this.getStoredDevicesForUser(userId);
2263
+ // If there are device infos for a userId, we transform it into a map
2264
+ // Else, the keys will be downloaded after
2265
+ if (deviceInfos) {
2266
+ const deviceMap = new Map(
2267
+ // Convert DeviceInfo to Device
2268
+ deviceInfos.map((deviceInfo) => [deviceInfo.deviceId, deviceInfoToDevice(deviceInfo, userId)]),
2269
+ );
2270
+ deviceMapByUserId.set(userId, deviceMap);
2271
+ } else {
2272
+ usersWithoutDeviceInfo.push(userId);
2273
+ }
2274
+ }
2275
+
2276
+ // Download device info for users without device infos
2277
+ if (downloadUncached && usersWithoutDeviceInfo.length > 0) {
2278
+ const newDeviceInfoMap = await this.downloadKeys(usersWithoutDeviceInfo);
2279
+
2280
+ newDeviceInfoMap.forEach((deviceInfoMap, userId) => {
2281
+ const deviceMap = new Map<string, Device>();
2282
+ // Convert DeviceInfo to Device
2283
+ deviceInfoMap.forEach((deviceInfo, deviceId) =>
2284
+ deviceMap.set(deviceId, deviceInfoToDevice(deviceInfo, userId)),
2285
+ );
2286
+
2287
+ // Put the new device infos into the returned map
2288
+ deviceMapByUserId.set(userId, deviceMap);
2289
+ });
2290
+ }
2291
+
2292
+ return deviceMapByUserId;
2293
+ }
2294
+
2295
+ /**
2296
+ * Get the stored keys for a single device
2297
+ *
2298
+ *
2299
+ * @returns device, or undefined
2300
+ * if we don't know about this device
2301
+ */
2302
+ public getStoredDevice(userId: string, deviceId: string): DeviceInfo | undefined {
2303
+ return this.deviceList.getStoredDevice(userId, deviceId);
2304
+ }
2305
+
2306
+ /**
2307
+ * Save the device list, if necessary
2308
+ *
2309
+ * @param delay - Time in ms before which the save actually happens.
2310
+ * By default, the save is delayed for a short period in order to batch
2311
+ * multiple writes, but this behaviour can be disabled by passing 0.
2312
+ *
2313
+ * @returns true if the data was saved, false if
2314
+ * it was not (eg. because no changes were pending). The promise
2315
+ * will only resolve once the data is saved, so may take some time
2316
+ * to resolve.
2317
+ */
2318
+ public saveDeviceList(delay: number): Promise<boolean> {
2319
+ return this.deviceList.saveIfDirty(delay);
2320
+ }
2321
+
2322
+ /**
2323
+ * Mark the given device as locally verified.
2324
+ *
2325
+ * Implementation of {@link Crypto.CryptoApi#setDeviceVerified}.
2326
+ */
2327
+ public async setDeviceVerified(userId: string, deviceId: string, verified = true): Promise<void> {
2328
+ await this.setDeviceVerification(userId, deviceId, verified);
2329
+ }
2330
+
2331
+ /**
2332
+ * Blindly cross-sign one of our other devices.
2333
+ *
2334
+ * Implementation of {@link Crypto.CryptoApi#crossSignDevice}.
2335
+ */
2336
+ public async crossSignDevice(deviceId: string): Promise<void> {
2337
+ await this.setDeviceVerified(this.userId, deviceId, true);
2338
+ }
2339
+
2340
+ /**
2341
+ * Update the blocked/verified state of the given device
2342
+ *
2343
+ * @param userId - owner of the device
2344
+ * @param deviceId - unique identifier for the device or user's
2345
+ * cross-signing public key ID.
2346
+ *
2347
+ * @param verified - whether to mark the device as verified. Null to
2348
+ * leave unchanged.
2349
+ *
2350
+ * @param blocked - whether to mark the device as blocked. Null to
2351
+ * leave unchanged.
2352
+ *
2353
+ * @param known - whether to mark that the user has been made aware of
2354
+ * the existence of this device. Null to leave unchanged
2355
+ *
2356
+ * @param keys - The list of keys that was present
2357
+ * during the device verification. This will be double checked with the list
2358
+ * of keys the given device has currently.
2359
+ *
2360
+ * @returns updated DeviceInfo
2361
+ */
2362
+ public async setDeviceVerification(
2363
+ userId: string,
2364
+ deviceId: string,
2365
+ verified: boolean | null = null,
2366
+ blocked: boolean | null = null,
2367
+ known: boolean | null = null,
2368
+ keys?: Record<string, string>,
2369
+ ): Promise<DeviceInfo | CrossSigningInfo | CrossSigningKeyInfo | undefined> {
2370
+ // Check if the 'device' is actually a cross signing key
2371
+ // The js-sdk's verification treats cross-signing keys as devices
2372
+ // and so uses this method to mark them verified.
2373
+ const xsk = this.deviceList.getStoredCrossSigningForUser(userId);
2374
+ if (xsk?.getId() === deviceId) {
2375
+ if (blocked !== null || known !== null) {
2376
+ throw new Error("Cannot set blocked or known for a cross-signing key");
2377
+ }
2378
+ if (!verified) {
2379
+ throw new Error("Cannot set a cross-signing key as unverified");
2380
+ }
2381
+ const gotKeyId = keys ? Object.values(keys)[0] : null;
2382
+ if (keys && (Object.values(keys).length !== 1 || gotKeyId !== xsk.getId())) {
2383
+ throw new Error(`Key did not match expected value: expected ${xsk.getId()}, got ${gotKeyId}`);
2384
+ }
2385
+
2386
+ if (!this.crossSigningInfo.getId() && userId === this.crossSigningInfo.userId) {
2387
+ this.storeTrustedSelfKeys(xsk.keys);
2388
+ // This will cause our own user trust to change, so emit the event
2389
+ this.emit(CryptoEvent.UserTrustStatusChanged, this.userId, this.checkUserTrust(userId));
2390
+ }
2391
+
2392
+ // Now sign the master key with our user signing key (unless it's ourself)
2393
+ if (userId !== this.userId) {
2394
+ logger.info("Master key " + xsk.getId() + " for " + userId + " marked verified. Signing...");
2395
+ const device = await this.crossSigningInfo.signUser(xsk);
2396
+ if (device) {
2397
+ const upload = async ({ shouldEmit = false }): Promise<void> => {
2398
+ logger.info("Uploading signature for " + userId + "...");
2399
+ const response = await this.baseApis.uploadKeySignatures({
2400
+ [userId]: {
2401
+ [deviceId]: device,
2402
+ },
2403
+ });
2404
+ const { failures } = response || {};
2405
+ if (Object.keys(failures || []).length > 0) {
2406
+ if (shouldEmit) {
2407
+ this.baseApis.emit(
2408
+ CryptoEvent.KeySignatureUploadFailure,
2409
+ failures,
2410
+ "setDeviceVerification",
2411
+ upload,
2412
+ );
2413
+ }
2414
+ /* Throwing here causes the process to be cancelled and the other
2415
+ * user to be notified */
2416
+ throw new KeySignatureUploadError("Key upload failed", { failures });
2417
+ }
2418
+ };
2419
+ await upload({ shouldEmit: true });
2420
+
2421
+ // This will emit events when it comes back down the sync
2422
+ // (we could do local echo to speed things up)
2423
+ }
2424
+ return device!;
2425
+ } else {
2426
+ return xsk;
2427
+ }
2428
+ }
2429
+
2430
+ const devices = this.deviceList.getRawStoredDevicesForUser(userId);
2431
+ if (!devices || !devices[deviceId]) {
2432
+ throw new Error("Unknown device " + userId + ":" + deviceId);
2433
+ }
2434
+
2435
+ const dev = devices[deviceId];
2436
+ let verificationStatus = dev.verified;
2437
+
2438
+ if (verified) {
2439
+ if (keys) {
2440
+ for (const [keyId, key] of Object.entries(keys)) {
2441
+ if (dev.keys[keyId] !== key) {
2442
+ throw new Error(`Key did not match expected value: expected ${key}, got ${dev.keys[keyId]}`);
2443
+ }
2444
+ }
2445
+ }
2446
+ verificationStatus = DeviceVerification.VERIFIED;
2447
+ } else if (verified !== null && verificationStatus == DeviceVerification.VERIFIED) {
2448
+ verificationStatus = DeviceVerification.UNVERIFIED;
2449
+ }
2450
+
2451
+ if (blocked) {
2452
+ verificationStatus = DeviceVerification.BLOCKED;
2453
+ } else if (blocked !== null && verificationStatus == DeviceVerification.BLOCKED) {
2454
+ verificationStatus = DeviceVerification.UNVERIFIED;
2455
+ }
2456
+
2457
+ let knownStatus = dev.known;
2458
+ if (known !== null) {
2459
+ knownStatus = known;
2460
+ }
2461
+
2462
+ if (dev.verified !== verificationStatus || dev.known !== knownStatus) {
2463
+ dev.verified = verificationStatus;
2464
+ dev.known = knownStatus;
2465
+ this.deviceList.storeDevicesForUser(userId, devices);
2466
+ this.deviceList.saveIfDirty();
2467
+ }
2468
+
2469
+ // do cross-signing
2470
+ if (verified && userId === this.userId) {
2471
+ logger.info("Own device " + deviceId + " marked verified: signing");
2472
+
2473
+ // Signing only needed if other device not already signed
2474
+ let device: ISignedKey | undefined;
2475
+ const deviceTrust = this.checkDeviceTrust(userId, deviceId);
2476
+ if (deviceTrust.isCrossSigningVerified()) {
2477
+ logger.log(`Own device ${deviceId} already cross-signing verified`);
2478
+ } else {
2479
+ device = (await this.crossSigningInfo.signDevice(userId, DeviceInfo.fromStorage(dev, deviceId)))!;
2480
+ }
2481
+
2482
+ if (device) {
2483
+ const upload = async ({ shouldEmit = false }): Promise<void> => {
2484
+ logger.info("Uploading signature for " + deviceId);
2485
+ const response = await this.baseApis.uploadKeySignatures({
2486
+ [userId]: {
2487
+ [deviceId]: device!,
2488
+ },
2489
+ });
2490
+ const { failures } = response || {};
2491
+ if (Object.keys(failures || []).length > 0) {
2492
+ if (shouldEmit) {
2493
+ this.baseApis.emit(
2494
+ CryptoEvent.KeySignatureUploadFailure,
2495
+ failures,
2496
+ "setDeviceVerification",
2497
+ upload, // continuation
2498
+ );
2499
+ }
2500
+ throw new KeySignatureUploadError("Key upload failed", { failures });
2501
+ }
2502
+ };
2503
+ await upload({ shouldEmit: true });
2504
+ // XXX: we'll need to wait for the device list to be updated
2505
+ }
2506
+ }
2507
+
2508
+ const deviceObj = DeviceInfo.fromStorage(dev, deviceId);
2509
+ this.emit(CryptoEvent.DeviceVerificationChanged, userId, deviceId, deviceObj);
2510
+ return deviceObj;
2511
+ }
2512
+
2513
+ public findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined {
2514
+ return this.inRoomVerificationRequests.findRequestInProgress(roomId, userId);
2515
+ }
2516
+
2517
+ public getVerificationRequestsToDeviceInProgress(userId: string): VerificationRequest[] {
2518
+ return this.toDeviceVerificationRequests.getRequestsInProgress(userId);
2519
+ }
2520
+
2521
+ public requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest> {
2522
+ const existingRequest = this.inRoomVerificationRequests.findRequestInProgress(roomId);
2523
+ if (existingRequest) {
2524
+ return Promise.resolve(existingRequest);
2525
+ }
2526
+ const channel = new InRoomChannel(this.baseApis, roomId, userId);
2527
+ return this.requestVerificationWithChannel(userId, channel, this.inRoomVerificationRequests);
2528
+ }
2529
+
2530
+ /** @deprecated Use `requestOwnUserVerificationToDevice` or `requestDeviceVerification` */
2531
+ public requestVerification(userId: string, devices?: string[]): Promise<VerificationRequest> {
2532
+ if (!devices) {
2533
+ devices = Object.keys(this.deviceList.getRawStoredDevicesForUser(userId));
2534
+ }
2535
+ const existingRequest = this.toDeviceVerificationRequests.findRequestInProgress(userId, devices);
2536
+ if (existingRequest) {
2537
+ return Promise.resolve(existingRequest);
2538
+ }
2539
+ const channel = new ToDeviceChannel(this.baseApis, userId, devices, ToDeviceChannel.makeTransactionId());
2540
+ return this.requestVerificationWithChannel(userId, channel, this.toDeviceVerificationRequests);
2541
+ }
2542
+
2543
+ public requestOwnUserVerification(): Promise<VerificationRequest> {
2544
+ return this.requestVerification(this.userId);
2545
+ }
2546
+
2547
+ public requestDeviceVerification(userId: string, deviceId: string): Promise<VerificationRequest> {
2548
+ return this.requestVerification(userId, [deviceId]);
2549
+ }
2550
+
2551
+ private async requestVerificationWithChannel(
2552
+ userId: string,
2553
+ channel: IVerificationChannel,
2554
+ requestsMap: IRequestsMap,
2555
+ ): Promise<VerificationRequest> {
2556
+ let request = new VerificationRequest(channel, this.verificationMethods, this.baseApis);
2557
+ // if transaction id is already known, add request
2558
+ if (channel.transactionId) {
2559
+ requestsMap.setRequestByChannel(channel, request);
2560
+ }
2561
+ await request.sendRequest();
2562
+ // don't replace the request created by a racing remote echo
2563
+ const racingRequest = requestsMap.getRequestByChannel(channel);
2564
+ if (racingRequest) {
2565
+ request = racingRequest;
2566
+ } else {
2567
+ logger.log(
2568
+ `Crypto: adding new request to ` + `requestsByTxnId with id ${channel.transactionId} ${channel.roomId}`,
2569
+ );
2570
+ requestsMap.setRequestByChannel(channel, request);
2571
+ }
2572
+ return request;
2573
+ }
2574
+
2575
+ public beginKeyVerification(
2576
+ method: string,
2577
+ userId: string,
2578
+ deviceId: string,
2579
+ transactionId: string | null = null,
2580
+ ): VerificationBase<any, any> {
2581
+ let request: Request | undefined;
2582
+ if (transactionId) {
2583
+ request = this.toDeviceVerificationRequests.getRequestBySenderAndTxnId(userId, transactionId);
2584
+ if (!request) {
2585
+ throw new Error(`No request found for user ${userId} with ` + `transactionId ${transactionId}`);
2586
+ }
2587
+ } else {
2588
+ transactionId = ToDeviceChannel.makeTransactionId();
2589
+ const channel = new ToDeviceChannel(this.baseApis, userId, [deviceId], transactionId, deviceId);
2590
+ request = new VerificationRequest(channel, this.verificationMethods, this.baseApis);
2591
+ this.toDeviceVerificationRequests.setRequestBySenderAndTxnId(userId, transactionId, request);
2592
+ }
2593
+ return request.beginKeyVerification(method, { userId, deviceId });
2594
+ }
2595
+
2596
+ public async legacyDeviceVerification(
2597
+ userId: string,
2598
+ deviceId: string,
2599
+ method: VerificationMethod,
2600
+ ): Promise<VerificationRequest> {
2601
+ const transactionId = ToDeviceChannel.makeTransactionId();
2602
+ const channel = new ToDeviceChannel(this.baseApis, userId, [deviceId], transactionId, deviceId);
2603
+ const request = new VerificationRequest(channel, this.verificationMethods, this.baseApis);
2604
+ this.toDeviceVerificationRequests.setRequestBySenderAndTxnId(userId, transactionId, request);
2605
+ const verifier = request.beginKeyVerification(method, { userId, deviceId });
2606
+ // either reject by an error from verify() while sending .start
2607
+ // or resolve when the request receives the
2608
+ // local (fake remote) echo for sending the .start event
2609
+ await Promise.race([verifier.verify(), request.waitFor((r) => r.started)]);
2610
+ return request;
2611
+ }
2612
+
2613
+ /**
2614
+ * Get information on the active olm sessions with a user
2615
+ * <p>
2616
+ * Returns a map from device id to an object with keys 'deviceIdKey' (the
2617
+ * device's curve25519 identity key) and 'sessions' (an array of objects in the
2618
+ * same format as that returned by
2619
+ * {@link OlmDevice#getSessionInfoForDevice}).
2620
+ * <p>
2621
+ * This method is provided for debugging purposes.
2622
+ *
2623
+ * @param userId - id of user to inspect
2624
+ */
2625
+ public async getOlmSessionsForUser(userId: string): Promise<Record<string, IUserOlmSession>> {
2626
+ const devices = this.getStoredDevicesForUser(userId) || [];
2627
+ const result: { [deviceId: string]: IUserOlmSession } = {};
2628
+ for (const device of devices) {
2629
+ const deviceKey = device.getIdentityKey();
2630
+ const sessions = await this.olmDevice.getSessionInfoForDevice(deviceKey);
2631
+
2632
+ result[device.deviceId] = {
2633
+ deviceIdKey: deviceKey,
2634
+ sessions: sessions,
2635
+ };
2636
+ }
2637
+ return result;
2638
+ }
2639
+
2640
+ /**
2641
+ * Get the device which sent an event
2642
+ *
2643
+ * @param event - event to be checked
2644
+ */
2645
+ public getEventSenderDeviceInfo(event: MatrixEvent): DeviceInfo | null {
2646
+ const senderKey = event.getSenderKey();
2647
+ const algorithm = event.getWireContent().algorithm;
2648
+
2649
+ if (!senderKey || !algorithm) {
2650
+ return null;
2651
+ }
2652
+
2653
+ if (event.isKeySourceUntrusted()) {
2654
+ // we got the key for this event from a source that we consider untrusted
2655
+ return null;
2656
+ }
2657
+
2658
+ // senderKey is the Curve25519 identity key of the device which the event
2659
+ // was sent from. In the case of Megolm, it's actually the Curve25519
2660
+ // identity key of the device which set up the Megolm session.
2661
+
2662
+ const device = this.deviceList.getDeviceByIdentityKey(algorithm, senderKey);
2663
+
2664
+ if (device === null) {
2665
+ // we haven't downloaded the details of this device yet.
2666
+ return null;
2667
+ }
2668
+
2669
+ // so far so good, but now we need to check that the sender of this event
2670
+ // hadn't advertised someone else's Curve25519 key as their own. We do that
2671
+ // by checking the Ed25519 claimed by the event (or, in the case of megolm,
2672
+ // the event which set up the megolm session), to check that it matches the
2673
+ // fingerprint of the purported sending device.
2674
+ //
2675
+ // (see https://github.com/vector-im/vector-web/issues/2215)
2676
+
2677
+ const claimedKey = event.getClaimedEd25519Key();
2678
+ if (!claimedKey) {
2679
+ logger.warn("Event " + event.getId() + " claims no ed25519 key: " + "cannot verify sending device");
2680
+ return null;
2681
+ }
2682
+
2683
+ if (claimedKey !== device.getFingerprint()) {
2684
+ logger.warn(
2685
+ "Event " +
2686
+ event.getId() +
2687
+ " claims ed25519 key " +
2688
+ claimedKey +
2689
+ " but sender device has key " +
2690
+ device.getFingerprint(),
2691
+ );
2692
+ return null;
2693
+ }
2694
+
2695
+ return device;
2696
+ }
2697
+
2698
+ /**
2699
+ * Get information about the encryption of an event
2700
+ *
2701
+ * @param event - event to be checked
2702
+ *
2703
+ * @returns An object with the fields:
2704
+ * - encrypted: whether the event is encrypted (if not encrypted, some of the
2705
+ * other properties may not be set)
2706
+ * - senderKey: the sender's key
2707
+ * - algorithm: the algorithm used to encrypt the event
2708
+ * - authenticated: whether we can be sure that the owner of the senderKey
2709
+ * sent the event
2710
+ * - sender: the sender's device information, if available
2711
+ * - mismatchedSender: if the event's ed25519 and curve25519 keys don't match
2712
+ * (only meaningful if `sender` is set)
2713
+ */
2714
+ public getEventEncryptionInfo(event: MatrixEvent): IEncryptedEventInfo {
2715
+ const ret: Partial<IEncryptedEventInfo> = {};
2716
+
2717
+ ret.senderKey = event.getSenderKey() ?? undefined;
2718
+ ret.algorithm = event.getWireContent().algorithm;
2719
+
2720
+ if (!ret.senderKey || !ret.algorithm) {
2721
+ ret.encrypted = false;
2722
+ return ret as IEncryptedEventInfo;
2723
+ }
2724
+ ret.encrypted = true;
2725
+
2726
+ if (event.isKeySourceUntrusted()) {
2727
+ // we got the key this event from somewhere else
2728
+ // TODO: check if we can trust the forwarders.
2729
+ ret.authenticated = false;
2730
+ } else {
2731
+ ret.authenticated = true;
2732
+ }
2733
+
2734
+ // senderKey is the Curve25519 identity key of the device which the event
2735
+ // was sent from. In the case of Megolm, it's actually the Curve25519
2736
+ // identity key of the device which set up the Megolm session.
2737
+
2738
+ ret.sender = this.deviceList.getDeviceByIdentityKey(ret.algorithm, ret.senderKey) ?? undefined;
2739
+
2740
+ // so far so good, but now we need to check that the sender of this event
2741
+ // hadn't advertised someone else's Curve25519 key as their own. We do that
2742
+ // by checking the Ed25519 claimed by the event (or, in the case of megolm,
2743
+ // the event which set up the megolm session), to check that it matches the
2744
+ // fingerprint of the purported sending device.
2745
+ //
2746
+ // (see https://github.com/vector-im/vector-web/issues/2215)
2747
+
2748
+ const claimedKey = event.getClaimedEd25519Key();
2749
+ if (!claimedKey) {
2750
+ logger.warn("Event " + event.getId() + " claims no ed25519 key: " + "cannot verify sending device");
2751
+ ret.mismatchedSender = true;
2752
+ }
2753
+
2754
+ if (ret.sender && claimedKey !== ret.sender.getFingerprint()) {
2755
+ logger.warn(
2756
+ "Event " +
2757
+ event.getId() +
2758
+ " claims ed25519 key " +
2759
+ claimedKey +
2760
+ "but sender device has key " +
2761
+ ret.sender.getFingerprint(),
2762
+ );
2763
+ ret.mismatchedSender = true;
2764
+ }
2765
+
2766
+ return ret as IEncryptedEventInfo;
2767
+ }
2768
+
2769
+ /**
2770
+ * Implementation of {@link Crypto.CryptoApi.getEncryptionInfoForEvent}.
2771
+ */
2772
+ public async getEncryptionInfoForEvent(event: MatrixEvent): Promise<EventEncryptionInfo | null> {
2773
+ const encryptionInfo = this.getEventEncryptionInfo(event);
2774
+ if (!encryptionInfo.encrypted) {
2775
+ return null;
2776
+ }
2777
+
2778
+ const senderId = event.getSender();
2779
+ if (!senderId || encryptionInfo.mismatchedSender) {
2780
+ // something definitely wrong is going on here
2781
+
2782
+ // previously: E2EState.Warning -> E2ePadlockUnverified -> Red/"Encrypted by an unverified session"
2783
+ return {
2784
+ shieldColour: EventShieldColour.RED,
2785
+ shieldReason: EventShieldReason.MISMATCHED_SENDER_KEY,
2786
+ };
2787
+ }
2788
+
2789
+ const userTrust = this.checkUserTrust(senderId);
2790
+ if (!userTrust.isCrossSigningVerified()) {
2791
+ // If the message is unauthenticated, then display a grey
2792
+ // shield, otherwise if the user isn't cross-signed then
2793
+ // nothing's needed
2794
+ if (!encryptionInfo.authenticated) {
2795
+ // previously: E2EState.Unauthenticated -> E2ePadlockUnauthenticated -> Grey/"The authenticity of this encrypted message can't be guaranteed on this device."
2796
+ return {
2797
+ shieldColour: EventShieldColour.GREY,
2798
+ shieldReason: EventShieldReason.AUTHENTICITY_NOT_GUARANTEED,
2799
+ };
2800
+ } else {
2801
+ // previously: E2EState.Normal -> no icon
2802
+ return { shieldColour: EventShieldColour.NONE, shieldReason: null };
2803
+ }
2804
+ }
2805
+
2806
+ const eventSenderTrust =
2807
+ senderId &&
2808
+ encryptionInfo.sender &&
2809
+ (await this.getDeviceVerificationStatus(senderId, encryptionInfo.sender.deviceId));
2810
+
2811
+ if (!eventSenderTrust) {
2812
+ // previously: E2EState.Unknown -> E2ePadlockUnknown -> Grey/"Encrypted by a deleted session"
2813
+ return {
2814
+ shieldColour: EventShieldColour.GREY,
2815
+ shieldReason: EventShieldReason.UNKNOWN_DEVICE,
2816
+ };
2817
+ }
2818
+
2819
+ if (!eventSenderTrust.isVerified()) {
2820
+ // previously: E2EState.Warning -> E2ePadlockUnverified -> Red/"Encrypted by an unverified session"
2821
+ return {
2822
+ shieldColour: EventShieldColour.RED,
2823
+ shieldReason: EventShieldReason.UNSIGNED_DEVICE,
2824
+ };
2825
+ }
2826
+
2827
+ if (!encryptionInfo.authenticated) {
2828
+ // previously: E2EState.Unauthenticated -> E2ePadlockUnauthenticated -> Grey/"The authenticity of this encrypted message can't be guaranteed on this device."
2829
+ return {
2830
+ shieldColour: EventShieldColour.GREY,
2831
+ shieldReason: EventShieldReason.AUTHENTICITY_NOT_GUARANTEED,
2832
+ };
2833
+ }
2834
+
2835
+ // previously: E2EState.Verified -> no icon
2836
+ return { shieldColour: EventShieldColour.NONE, shieldReason: null };
2837
+ }
2838
+
2839
+ /**
2840
+ * Forces the current outbound group session to be discarded such
2841
+ * that another one will be created next time an event is sent.
2842
+ *
2843
+ * @param roomId - The ID of the room to discard the session for
2844
+ *
2845
+ * This should not normally be necessary.
2846
+ */
2847
+ public forceDiscardSession(roomId: string): Promise<void> {
2848
+ const alg = this.roomEncryptors.get(roomId);
2849
+ if (alg === undefined) throw new Error("Room not encrypted");
2850
+ if (alg.forceDiscardSession === undefined) {
2851
+ throw new Error("Room encryption algorithm doesn't support session discarding");
2852
+ }
2853
+ alg.forceDiscardSession();
2854
+ return Promise.resolve();
2855
+ }
2856
+
2857
+ /**
2858
+ * Configure a room to use encryption (ie, save a flag in the cryptoStore).
2859
+ *
2860
+ * @param roomId - The room ID to enable encryption in.
2861
+ *
2862
+ * @param config - The encryption config for the room.
2863
+ *
2864
+ * @param inhibitDeviceQuery - true to suppress device list query for
2865
+ * users in the room (for now). In case lazy loading is enabled,
2866
+ * the device query is always inhibited as the members are not tracked.
2867
+ *
2868
+ * @deprecated It is normally incorrect to call this method directly. Encryption
2869
+ * is enabled by receiving an `m.room.encryption` event (which we may have sent
2870
+ * previously).
2871
+ */
2872
+ public async setRoomEncryption(
2873
+ roomId: string,
2874
+ config: IRoomEncryption,
2875
+ inhibitDeviceQuery?: boolean,
2876
+ ): Promise<void> {
2877
+ const room = this.clientStore.getRoom(roomId);
2878
+ if (!room) {
2879
+ throw new Error(`Unable to enable encryption tracking devices in unknown room ${roomId}`);
2880
+ }
2881
+ await this.setRoomEncryptionImpl(room, config);
2882
+ if (!this.lazyLoadMembers && !inhibitDeviceQuery) {
2883
+ this.deviceList.refreshOutdatedDeviceLists();
2884
+ }
2885
+ }
2886
+
2887
+ /**
2888
+ * Set up encryption for a room.
2889
+ *
2890
+ * This is called when an <tt>m.room.encryption</tt> event is received. It saves a flag
2891
+ * for the room in the cryptoStore (if it wasn't already set), sets up an "encryptor" for
2892
+ * the room, and enables device-list tracking for the room.
2893
+ *
2894
+ * It does <em>not</em> initiate a device list query for the room. That is normally
2895
+ * done once we finish processing the sync, in onSyncCompleted.
2896
+ *
2897
+ * @param room - The room to enable encryption in.
2898
+ * @param config - The encryption config for the room.
2899
+ */
2900
+ private async setRoomEncryptionImpl(room: Room, config: IRoomEncryption): Promise<void> {
2901
+ const roomId = room.roomId;
2902
+
2903
+ // ignore crypto events with no algorithm defined
2904
+ // This will happen if a crypto event is redacted before we fetch the room state
2905
+ // It would otherwise just throw later as an unknown algorithm would, but we may
2906
+ // as well catch this here
2907
+ if (!config.algorithm) {
2908
+ logger.log("Ignoring setRoomEncryption with no algorithm");
2909
+ return;
2910
+ }
2911
+
2912
+ // if state is being replayed from storage, we might already have a configuration
2913
+ // for this room as they are persisted as well.
2914
+ // We just need to make sure the algorithm is initialized in this case.
2915
+ // However, if the new config is different,
2916
+ // we should bail out as room encryption can't be changed once set.
2917
+ const existingConfig = this.roomList.getRoomEncryption(roomId);
2918
+ if (existingConfig) {
2919
+ if (JSON.stringify(existingConfig) != JSON.stringify(config)) {
2920
+ logger.error("Ignoring m.room.encryption event which requests " + "a change of config in " + roomId);
2921
+ return;
2922
+ }
2923
+ }
2924
+ // if we already have encryption in this room, we should ignore this event,
2925
+ // as it would reset the encryption algorithm.
2926
+ // This is at least expected to be called twice, as sync calls onCryptoEvent
2927
+ // for both the timeline and state sections in the /sync response,
2928
+ // the encryption event would appear in both.
2929
+ // If it's called more than twice though,
2930
+ // it signals a bug on client or server.
2931
+ const existingAlg = this.roomEncryptors.get(roomId);
2932
+ if (existingAlg) {
2933
+ return;
2934
+ }
2935
+
2936
+ // _roomList.getRoomEncryption will not race with _roomList.setRoomEncryption
2937
+ // because it first stores in memory. We should await the promise only
2938
+ // after all the in-memory state (roomEncryptors and _roomList) has been updated
2939
+ // to avoid races when calling this method multiple times. Hence keep a hold of the promise.
2940
+ let storeConfigPromise: Promise<void> | null = null;
2941
+ if (!existingConfig) {
2942
+ storeConfigPromise = this.roomList.setRoomEncryption(roomId, config);
2943
+ }
2944
+
2945
+ const AlgClass = algorithms.ENCRYPTION_CLASSES.get(config.algorithm);
2946
+ if (!AlgClass) {
2947
+ throw new Error("Unable to encrypt with " + config.algorithm);
2948
+ }
2949
+
2950
+ const alg = new AlgClass({
2951
+ userId: this.userId,
2952
+ deviceId: this.deviceId,
2953
+ crypto: this,
2954
+ olmDevice: this.olmDevice,
2955
+ baseApis: this.baseApis,
2956
+ roomId,
2957
+ config,
2958
+ });
2959
+ this.roomEncryptors.set(roomId, alg);
2960
+
2961
+ if (storeConfigPromise) {
2962
+ await storeConfigPromise;
2963
+ }
2964
+
2965
+ logger.log(`Enabling encryption in ${roomId}`);
2966
+
2967
+ // we don't want to force a download of the full membership list of this room, but as soon as we have that
2968
+ // list we can start tracking the device list.
2969
+ if (room.membersLoaded()) {
2970
+ await this.trackRoomDevicesImpl(room);
2971
+ } else {
2972
+ // wait for the membership list to be loaded
2973
+ const onState = (_state: RoomState): void => {
2974
+ room.off(RoomStateEvent.Update, onState);
2975
+ if (room.membersLoaded()) {
2976
+ this.trackRoomDevicesImpl(room).catch((e) => {
2977
+ logger.error(`Error enabling device tracking in ${roomId}`, e);
2978
+ });
2979
+ }
2980
+ };
2981
+ room.on(RoomStateEvent.Update, onState);
2982
+ }
2983
+ }
2984
+
2985
+ /**
2986
+ * Make sure we are tracking the device lists for all users in this room.
2987
+ *
2988
+ * @param roomId - The room ID to start tracking devices in.
2989
+ * @returns when all devices for the room have been fetched and marked to track
2990
+ * @deprecated there's normally no need to call this function: device list tracking
2991
+ * will be enabled as soon as we have the full membership list.
2992
+ */
2993
+ public trackRoomDevices(roomId: string): Promise<void> {
2994
+ const room = this.clientStore.getRoom(roomId);
2995
+ if (!room) {
2996
+ throw new Error(`Unable to start tracking devices in unknown room ${roomId}`);
2997
+ }
2998
+ return this.trackRoomDevicesImpl(room);
2999
+ }
3000
+
3001
+ /**
3002
+ * Make sure we are tracking the device lists for all users in this room.
3003
+ *
3004
+ * This is normally called when we are about to send an encrypted event, to make sure
3005
+ * we have all the devices in the room; but it is also called when processing an
3006
+ * m.room.encryption state event (if lazy-loading is disabled), or when members are
3007
+ * loaded (if lazy-loading is enabled), to prepare the device list.
3008
+ *
3009
+ * @param room - Room to enable device-list tracking in
3010
+ */
3011
+ private trackRoomDevicesImpl(room: Room): Promise<void> {
3012
+ const roomId = room.roomId;
3013
+ const trackMembers = async (): Promise<void> => {
3014
+ // not an encrypted room
3015
+ if (!this.roomEncryptors.has(roomId)) {
3016
+ return;
3017
+ }
3018
+ logger.log(`Starting to track devices for room ${roomId} ...`);
3019
+ const members = await room.getEncryptionTargetMembers();
3020
+ members.forEach((m) => {
3021
+ this.deviceList.startTrackingDeviceList(m.userId);
3022
+ });
3023
+ };
3024
+
3025
+ let promise = this.roomDeviceTrackingState[roomId];
3026
+ if (!promise) {
3027
+ promise = trackMembers();
3028
+ this.roomDeviceTrackingState[roomId] = promise.catch((err) => {
3029
+ delete this.roomDeviceTrackingState[roomId];
3030
+ throw err;
3031
+ });
3032
+ }
3033
+ return promise;
3034
+ }
3035
+
3036
+ /**
3037
+ * Try to make sure we have established olm sessions for all known devices for
3038
+ * the given users.
3039
+ *
3040
+ * @param users - list of user ids
3041
+ * @param force - If true, force a new Olm session to be created. Default false.
3042
+ *
3043
+ * @returns resolves once the sessions are complete, to
3044
+ * an Object mapping from userId to deviceId to
3045
+ * `IOlmSessionResult`
3046
+ */
3047
+ public ensureOlmSessionsForUsers(
3048
+ users: string[],
3049
+ force?: boolean,
3050
+ ): Promise<Map<string, Map<string, olmlib.IOlmSessionResult>>> {
3051
+ // map user Id → DeviceInfo[]
3052
+ const devicesByUser: Map<string, DeviceInfo[]> = new Map();
3053
+
3054
+ for (const userId of users) {
3055
+ const userDevices: DeviceInfo[] = [];
3056
+ devicesByUser.set(userId, userDevices);
3057
+
3058
+ const devices = this.getStoredDevicesForUser(userId) || [];
3059
+ for (const deviceInfo of devices) {
3060
+ const key = deviceInfo.getIdentityKey();
3061
+ if (key == this.olmDevice.deviceCurve25519Key) {
3062
+ // don't bother setting up session to ourself
3063
+ continue;
3064
+ }
3065
+ if (deviceInfo.verified == DeviceVerification.BLOCKED) {
3066
+ // don't bother setting up sessions with blocked users
3067
+ continue;
3068
+ }
3069
+
3070
+ userDevices.push(deviceInfo);
3071
+ }
3072
+ }
3073
+
3074
+ return olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, force);
3075
+ }
3076
+
3077
+ /**
3078
+ * Get a list containing all of the room keys
3079
+ *
3080
+ * @returns a list of session export objects
3081
+ */
3082
+ public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
3083
+ const exportedSessions: IMegolmSessionData[] = [];
3084
+ await this.cryptoStore.doTxn("readonly", [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
3085
+ this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => {
3086
+ if (s === null) return;
3087
+
3088
+ const sess = this.olmDevice.exportInboundGroupSession(s.senderKey, s.sessionId, s.sessionData!);
3089
+ delete sess.first_known_index;
3090
+ sess.algorithm = olmlib.MEGOLM_ALGORITHM;
3091
+ exportedSessions.push(sess);
3092
+ });
3093
+ });
3094
+
3095
+ return exportedSessions;
3096
+ }
3097
+
3098
+ /**
3099
+ * Get a JSON list containing all of the room keys
3100
+ *
3101
+ * @returns a JSON string encoding a list of session
3102
+ * export objects, each of which is an IMegolmSessionData
3103
+ */
3104
+ public async exportRoomKeysAsJson(): Promise<string> {
3105
+ return JSON.stringify(await this.exportRoomKeys());
3106
+ }
3107
+
3108
+ /**
3109
+ * Import a list of room keys previously exported by exportRoomKeys
3110
+ *
3111
+ * @param keys - a list of session export objects
3112
+ * @returns a promise which resolves once the keys have been imported
3113
+ */
3114
+ public importRoomKeys(keys: IMegolmSessionData[], opts: ImportRoomKeysOpts = {}): Promise<void> {
3115
+ let successes = 0;
3116
+ let failures = 0;
3117
+ const total = keys.length;
3118
+
3119
+ function updateProgress(): void {
3120
+ opts.progressCallback?.({
3121
+ stage: "load_keys",
3122
+ successes,
3123
+ failures,
3124
+ total,
3125
+ });
3126
+ }
3127
+
3128
+ return Promise.all(
3129
+ keys.map((key) => {
3130
+ if (!key.room_id || !key.algorithm) {
3131
+ logger.warn("ignoring room key entry with missing fields", key);
3132
+ failures++;
3133
+ if (opts.progressCallback) {
3134
+ updateProgress();
3135
+ }
3136
+ return null;
3137
+ }
3138
+
3139
+ const alg = this.getRoomDecryptor(key.room_id, key.algorithm);
3140
+ return alg.importRoomKey(key, opts).finally(() => {
3141
+ successes++;
3142
+ if (opts.progressCallback) {
3143
+ updateProgress();
3144
+ }
3145
+ });
3146
+ }),
3147
+ ).then();
3148
+ }
3149
+
3150
+ /**
3151
+ * Import a JSON string encoding a list of room keys previously
3152
+ * exported by exportRoomKeysAsJson
3153
+ *
3154
+ * @param keys - a JSON string encoding a list of session export
3155
+ * objects, each of which is an IMegolmSessionData
3156
+ * @param opts - options object
3157
+ * @returns a promise which resolves once the keys have been imported
3158
+ */
3159
+ public async importRoomKeysAsJson(keys: string, opts?: ImportRoomKeysOpts): Promise<void> {
3160
+ return await this.importRoomKeys(JSON.parse(keys));
3161
+ }
3162
+
3163
+ /**
3164
+ * Counts the number of end to end session keys that are waiting to be backed up
3165
+ * @returns Promise which resolves to the number of sessions requiring backup
3166
+ */
3167
+ public countSessionsNeedingBackup(): Promise<number> {
3168
+ return this.backupManager.countSessionsNeedingBackup();
3169
+ }
3170
+
3171
+ /**
3172
+ * Perform any background tasks that can be done before a message is ready to
3173
+ * send, in order to speed up sending of the message.
3174
+ *
3175
+ * @param room - the room the event is in
3176
+ */
3177
+ public prepareToEncrypt(room: Room): void {
3178
+ const alg = this.roomEncryptors.get(room.roomId);
3179
+ if (alg) {
3180
+ alg.prepareToEncrypt(room);
3181
+ }
3182
+ }
3183
+
3184
+ /**
3185
+ * Encrypt an event according to the configuration of the room.
3186
+ *
3187
+ * @param event - event to be sent
3188
+ *
3189
+ * @param room - destination room.
3190
+ *
3191
+ * @returns Promise which resolves when the event has been
3192
+ * encrypted, or null if nothing was needed
3193
+ */
3194
+ public async encryptEvent(event: MatrixEvent, room: Room): Promise<void> {
3195
+ const roomId = event.getRoomId()!;
3196
+
3197
+ const alg = this.roomEncryptors.get(roomId);
3198
+ if (!alg) {
3199
+ // MatrixClient has already checked that this room should be encrypted,
3200
+ // so this is an unexpected situation.
3201
+ throw new Error(
3202
+ "Room " +
3203
+ roomId +
3204
+ " was previously configured to use encryption, but is " +
3205
+ "no longer. Perhaps the homeserver is hiding the " +
3206
+ "configuration event.",
3207
+ );
3208
+ }
3209
+
3210
+ // wait for all the room devices to be loaded
3211
+ await this.trackRoomDevicesImpl(room);
3212
+
3213
+ let content = event.getContent();
3214
+ // If event has an m.relates_to then we need
3215
+ // to put this on the wrapping event instead
3216
+ const mRelatesTo = content["m.relates_to"];
3217
+ if (mRelatesTo) {
3218
+ // Clone content here so we don't remove `m.relates_to` from the local-echo
3219
+ content = Object.assign({}, content);
3220
+ delete content["m.relates_to"];
3221
+ }
3222
+
3223
+ // Treat element's performance metrics the same as `m.relates_to` (when present)
3224
+ const elementPerfMetrics = content["io.element.performance_metrics"];
3225
+ if (elementPerfMetrics) {
3226
+ content = Object.assign({}, content);
3227
+ delete content["io.element.performance_metrics"];
3228
+ }
3229
+
3230
+ const encryptedContent = (await alg.encryptMessage(room, event.getType(), content)) as IContent;
3231
+
3232
+ if (mRelatesTo) {
3233
+ encryptedContent["m.relates_to"] = mRelatesTo;
3234
+ }
3235
+ if (elementPerfMetrics) {
3236
+ encryptedContent["io.element.performance_metrics"] = elementPerfMetrics;
3237
+ }
3238
+
3239
+ event.makeEncrypted(
3240
+ "m.room.encrypted",
3241
+ encryptedContent,
3242
+ this.olmDevice.deviceCurve25519Key!,
3243
+ this.olmDevice.deviceEd25519Key!,
3244
+ );
3245
+ }
3246
+
3247
+ /**
3248
+ * Decrypt a received event
3249
+ *
3250
+ *
3251
+ * @returns resolves once we have
3252
+ * finished decrypting. Rejects with an `algorithms.DecryptionError` if there
3253
+ * is a problem decrypting the event.
3254
+ */
3255
+ public async decryptEvent(event: MatrixEvent): Promise<IEventDecryptionResult> {
3256
+ if (event.isRedacted()) {
3257
+ // Try to decrypt the redaction event, to support encrypted
3258
+ // redaction reasons. If we can't decrypt, just fall back to using
3259
+ // the original redacted_because.
3260
+ const redactionEvent = new MatrixEvent({
3261
+ room_id: event.getRoomId(),
3262
+ ...event.getUnsigned().redacted_because,
3263
+ });
3264
+ let redactedBecause: IEvent = event.getUnsigned().redacted_because!;
3265
+ if (redactionEvent.isEncrypted()) {
3266
+ try {
3267
+ const decryptedEvent = await this.decryptEvent(redactionEvent);
3268
+ redactedBecause = decryptedEvent.clearEvent as IEvent;
3269
+ } catch (e) {
3270
+ logger.warn("Decryption of redaction failed. Falling back to unencrypted event.", e);
3271
+ }
3272
+ }
3273
+
3274
+ return {
3275
+ clearEvent: {
3276
+ room_id: event.getRoomId(),
3277
+ type: "m.room.message",
3278
+ content: {},
3279
+ unsigned: {
3280
+ redacted_because: redactedBecause,
3281
+ },
3282
+ },
3283
+ };
3284
+ } else {
3285
+ const content = event.getWireContent();
3286
+ const alg = this.getRoomDecryptor(event.getRoomId()!, content.algorithm);
3287
+ return alg.decryptEvent(event);
3288
+ }
3289
+ }
3290
+
3291
+ /**
3292
+ * Handle the notification from /sync that device lists have
3293
+ * been changed.
3294
+ *
3295
+ * @param deviceLists - device_lists field from /sync
3296
+ */
3297
+ public async processDeviceLists(deviceLists: IDeviceLists): Promise<void> {
3298
+ // Here, we're relying on the fact that we only ever save the sync data after
3299
+ // sucessfully saving the device list data, so we're guaranteed that the device
3300
+ // list store is at least as fresh as the sync token from the sync store, ie.
3301
+ // any device changes received in sync tokens prior to the 'next' token here
3302
+ // have been processed and are reflected in the current device list.
3303
+ // If we didn't make this assumption, we'd have to use the /keys/changes API
3304
+ // to get key changes between the sync token in the device list and the 'old'
3305
+ // sync token used here to make sure we didn't miss any.
3306
+ await this.evalDeviceListChanges(deviceLists);
3307
+ }
3308
+
3309
+ /**
3310
+ * Send a request for some room keys, if we have not already done so
3311
+ *
3312
+ * @param resend - whether to resend the key request if there is
3313
+ * already one
3314
+ *
3315
+ * @returns a promise that resolves when the key request is queued
3316
+ */
3317
+ public requestRoomKey(
3318
+ requestBody: IRoomKeyRequestBody,
3319
+ recipients: IRoomKeyRequestRecipient[],
3320
+ resend = false,
3321
+ ): Promise<void> {
3322
+ return this.outgoingRoomKeyRequestManager
3323
+ .queueRoomKeyRequest(requestBody, recipients, resend)
3324
+ .then(() => {
3325
+ if (this.sendKeyRequestsImmediately) {
3326
+ this.outgoingRoomKeyRequestManager.sendQueuedRequests();
3327
+ }
3328
+ })
3329
+ .catch((e) => {
3330
+ // this normally means we couldn't talk to the store
3331
+ logger.error("Error requesting key for event", e);
3332
+ });
3333
+ }
3334
+
3335
+ /**
3336
+ * Cancel any earlier room key request
3337
+ *
3338
+ * @param requestBody - parameters to match for cancellation
3339
+ */
3340
+ public cancelRoomKeyRequest(requestBody: IRoomKeyRequestBody): void {
3341
+ this.outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody).catch((e) => {
3342
+ logger.warn("Error clearing pending room key requests", e);
3343
+ });
3344
+ }
3345
+
3346
+ /**
3347
+ * Re-send any outgoing key requests, eg after verification
3348
+ * @returns
3349
+ */
3350
+ public async cancelAndResendAllOutgoingKeyRequests(): Promise<void> {
3351
+ await this.outgoingRoomKeyRequestManager.cancelAndResendAllOutgoingRequests();
3352
+ }
3353
+
3354
+ /**
3355
+ * handle an m.room.encryption event
3356
+ *
3357
+ * @param room - in which the event was received
3358
+ * @param event - encryption event to be processed
3359
+ */
3360
+ public async onCryptoEvent(room: Room, event: MatrixEvent): Promise<void> {
3361
+ const content = event.getContent<IRoomEncryption>();
3362
+ await this.setRoomEncryptionImpl(room, content);
3363
+ }
3364
+
3365
+ /**
3366
+ * Called before the result of a sync is processed
3367
+ *
3368
+ * @param syncData - the data from the 'MatrixClient.sync' event
3369
+ */
3370
+ public async onSyncWillProcess(syncData: ISyncStateData): Promise<void> {
3371
+ if (!syncData.oldSyncToken) {
3372
+ // If there is no old sync token, we start all our tracking from
3373
+ // scratch, so mark everything as untracked. onCryptoEvent will
3374
+ // be called for all e2e rooms during the processing of the sync,
3375
+ // at which point we'll start tracking all the users of that room.
3376
+ logger.log("Initial sync performed - resetting device tracking state");
3377
+ this.deviceList.stopTrackingAllDeviceLists();
3378
+ // we always track our own device list (for key backups etc)
3379
+ this.deviceList.startTrackingDeviceList(this.userId);
3380
+ this.roomDeviceTrackingState = {};
3381
+ }
3382
+
3383
+ this.sendKeyRequestsImmediately = false;
3384
+ }
3385
+
3386
+ /**
3387
+ * handle the completion of a /sync
3388
+ *
3389
+ * This is called after the processing of each successful /sync response.
3390
+ * It is an opportunity to do a batch process on the information received.
3391
+ *
3392
+ * @param syncData - the data from the 'MatrixClient.sync' event
3393
+ */
3394
+ public async onSyncCompleted(syncData: OnSyncCompletedData): Promise<void> {
3395
+ this.deviceList.setSyncToken(syncData.nextSyncToken ?? null);
3396
+ this.deviceList.saveIfDirty();
3397
+
3398
+ // we always track our own device list (for key backups etc)
3399
+ this.deviceList.startTrackingDeviceList(this.userId);
3400
+
3401
+ this.deviceList.refreshOutdatedDeviceLists();
3402
+
3403
+ // we don't start uploading one-time keys until we've caught up with
3404
+ // to-device messages, to help us avoid throwing away one-time-keys that we
3405
+ // are about to receive messages for
3406
+ // (https://github.com/vector-im/element-web/issues/2782).
3407
+ if (!syncData.catchingUp) {
3408
+ this.maybeUploadOneTimeKeys();
3409
+ this.processReceivedRoomKeyRequests();
3410
+
3411
+ // likewise don't start requesting keys until we've caught up
3412
+ // on to_device messages, otherwise we'll request keys that we're
3413
+ // just about to get.
3414
+ this.outgoingRoomKeyRequestManager.sendQueuedRequests();
3415
+
3416
+ // Sync has finished so send key requests straight away.
3417
+ this.sendKeyRequestsImmediately = true;
3418
+ }
3419
+ }
3420
+
3421
+ /**
3422
+ * Trigger the appropriate invalidations and removes for a given
3423
+ * device list
3424
+ *
3425
+ * @param deviceLists - device_lists field from /sync, or response from
3426
+ * /keys/changes
3427
+ */
3428
+ private async evalDeviceListChanges(deviceLists: Required<ISyncResponse>["device_lists"]): Promise<void> {
3429
+ if (Array.isArray(deviceLists?.changed)) {
3430
+ deviceLists.changed.forEach((u) => {
3431
+ this.deviceList.invalidateUserDeviceList(u);
3432
+ });
3433
+ }
3434
+
3435
+ if (Array.isArray(deviceLists?.left) && deviceLists.left.length) {
3436
+ // Check we really don't share any rooms with these users
3437
+ // any more: the server isn't required to give us the
3438
+ // exact correct set.
3439
+ const e2eUserIds = new Set(await this.getTrackedE2eUsers());
3440
+
3441
+ deviceLists.left.forEach((u) => {
3442
+ if (!e2eUserIds.has(u)) {
3443
+ this.deviceList.stopTrackingDeviceList(u);
3444
+ }
3445
+ });
3446
+ }
3447
+ }
3448
+
3449
+ /**
3450
+ * Get a list of all the IDs of users we share an e2e room with
3451
+ * for which we are tracking devices already
3452
+ *
3453
+ * @returns List of user IDs
3454
+ */
3455
+ private async getTrackedE2eUsers(): Promise<string[]> {
3456
+ const e2eUserIds: string[] = [];
3457
+ for (const room of this.getTrackedE2eRooms()) {
3458
+ const members = await room.getEncryptionTargetMembers();
3459
+ for (const member of members) {
3460
+ e2eUserIds.push(member.userId);
3461
+ }
3462
+ }
3463
+ return e2eUserIds;
3464
+ }
3465
+
3466
+ /**
3467
+ * Get a list of the e2e-enabled rooms we are members of,
3468
+ * and for which we are already tracking the devices
3469
+ *
3470
+ * @returns
3471
+ */
3472
+ private getTrackedE2eRooms(): Room[] {
3473
+ return this.clientStore.getRooms().filter((room) => {
3474
+ // check for rooms with encryption enabled
3475
+ const alg = this.roomEncryptors.get(room.roomId);
3476
+ if (!alg) {
3477
+ return false;
3478
+ }
3479
+ if (!this.roomDeviceTrackingState[room.roomId]) {
3480
+ return false;
3481
+ }
3482
+
3483
+ // ignore any rooms which we have left
3484
+ const myMembership = room.getMyMembership();
3485
+ return myMembership === KnownMembership.Join || myMembership === KnownMembership.Invite;
3486
+ });
3487
+ }
3488
+
3489
+ /**
3490
+ * Encrypts and sends a given object via Olm to-device messages to a given
3491
+ * set of devices.
3492
+ * @param userDeviceInfoArr - the devices to send to
3493
+ * @param payload - fields to include in the encrypted payload
3494
+ * @returns Promise which
3495
+ * resolves once the message has been encrypted and sent to the given
3496
+ * userDeviceMap, and returns the `{ contentMap, deviceInfoByDeviceId }`
3497
+ * of the successfully sent messages.
3498
+ *
3499
+ * @deprecated Instead use {@link encryptToDeviceMessages} followed by {@link MatrixClient.queueToDevice}.
3500
+ */
3501
+ public async encryptAndSendToDevices(userDeviceInfoArr: IOlmDevice<DeviceInfo>[], payload: object): Promise<void> {
3502
+ try {
3503
+ const toDeviceBatch = await this.prepareToDeviceBatch(userDeviceInfoArr, payload);
3504
+
3505
+ try {
3506
+ await this.baseApis.queueToDevice(toDeviceBatch);
3507
+ } catch (e) {
3508
+ logger.error("sendToDevice failed", e);
3509
+ throw e;
3510
+ }
3511
+ } catch (e) {
3512
+ logger.error("encryptAndSendToDevices promises failed", e);
3513
+ throw e;
3514
+ }
3515
+ }
3516
+
3517
+ private async prepareToDeviceBatch(
3518
+ userDeviceInfoArr: IOlmDevice<DeviceInfo>[],
3519
+ payload: object,
3520
+ ): Promise<ToDeviceBatch> {
3521
+ const toDeviceBatch: ToDeviceBatch = {
3522
+ eventType: EventType.RoomMessageEncrypted,
3523
+ batch: [],
3524
+ };
3525
+
3526
+ await Promise.all(
3527
+ userDeviceInfoArr.map(async ({ userId, deviceInfo }) => {
3528
+ const deviceId = deviceInfo.deviceId;
3529
+ const encryptedContent: IEncryptedContent = {
3530
+ algorithm: olmlib.OLM_ALGORITHM,
3531
+ sender_key: this.olmDevice.deviceCurve25519Key!,
3532
+ ciphertext: {},
3533
+ [ToDeviceMessageId]: uuidv4(),
3534
+ };
3535
+
3536
+ toDeviceBatch.batch.push({
3537
+ userId,
3538
+ deviceId,
3539
+ payload: encryptedContent,
3540
+ });
3541
+
3542
+ await olmlib.ensureOlmSessionsForDevices(
3543
+ this.olmDevice,
3544
+ this.baseApis,
3545
+ new Map([[userId, [deviceInfo]]]),
3546
+ );
3547
+ await olmlib.encryptMessageForDevice(
3548
+ encryptedContent.ciphertext,
3549
+ this.userId,
3550
+ this.deviceId,
3551
+ this.olmDevice,
3552
+ userId,
3553
+ deviceInfo,
3554
+ payload,
3555
+ );
3556
+ }),
3557
+ );
3558
+
3559
+ // prune out any devices that encryptMessageForDevice could not encrypt for,
3560
+ // in which case it will have just not added anything to the ciphertext object.
3561
+ // There's no point sending messages to devices if we couldn't encrypt to them,
3562
+ // since that's effectively a blank message.
3563
+ toDeviceBatch.batch = toDeviceBatch.batch.filter((msg) => {
3564
+ if (Object.keys(msg.payload.ciphertext).length > 0) {
3565
+ return true;
3566
+ } else {
3567
+ logger.log(`No ciphertext for device ${msg.userId}:${msg.deviceId}: pruning`);
3568
+ return false;
3569
+ }
3570
+ });
3571
+
3572
+ return toDeviceBatch;
3573
+ }
3574
+
3575
+ /**
3576
+ * Implementation of {@link Crypto.CryptoApi#encryptToDeviceMessages}.
3577
+ */
3578
+ public async encryptToDeviceMessages(
3579
+ eventType: string,
3580
+ devices: { userId: string; deviceId: string }[],
3581
+ payload: ToDevicePayload,
3582
+ ): Promise<ToDeviceBatch> {
3583
+ const userIds = new Set(devices.map(({ userId }) => userId));
3584
+ const deviceInfoMap = await this.downloadKeys(Array.from(userIds), false);
3585
+
3586
+ const userDeviceInfoArr: IOlmDevice<DeviceInfo>[] = [];
3587
+
3588
+ devices.forEach(({ userId, deviceId }) => {
3589
+ const devices = deviceInfoMap.get(userId);
3590
+ if (!devices) {
3591
+ logger.warn(`No devices found for user ${userId}`);
3592
+ return;
3593
+ }
3594
+
3595
+ if (devices.has(deviceId)) {
3596
+ // Send the message to a specific device
3597
+ userDeviceInfoArr.push({ userId, deviceInfo: devices.get(deviceId)! });
3598
+ } else {
3599
+ logger.warn(`No device found for user ${userId} with id ${deviceId}`);
3600
+ }
3601
+ });
3602
+
3603
+ return this.prepareToDeviceBatch(userDeviceInfoArr, payload);
3604
+ }
3605
+
3606
+ private onMembership = (event: MatrixEvent, member: RoomMember, oldMembership?: string): void => {
3607
+ try {
3608
+ this.onRoomMembership(event, member, oldMembership);
3609
+ } catch (e) {
3610
+ logger.error("Error handling membership change:", e);
3611
+ }
3612
+ };
3613
+
3614
+ public async preprocessToDeviceMessages(events: IToDeviceEvent[]): Promise<IToDeviceEvent[]> {
3615
+ // all we do here is filter out encrypted to-device messages with the wrong algorithm. Decryption
3616
+ // happens later in decryptEvent, via the EventMapper
3617
+ return events.filter((toDevice) => {
3618
+ if (
3619
+ toDevice.type === EventType.RoomMessageEncrypted &&
3620
+ !["m.olm.v1.curve25519-aes-sha2"].includes(toDevice.content?.algorithm)
3621
+ ) {
3622
+ logger.log("Ignoring invalid encrypted to-device event from " + toDevice.sender);
3623
+ return false;
3624
+ }
3625
+ return true;
3626
+ });
3627
+ }
3628
+
3629
+ /**
3630
+ * Stores the current one_time_key count which will be handled later (in a call of
3631
+ * onSyncCompleted).
3632
+ *
3633
+ * @param currentCount - The current count of one_time_keys to be stored
3634
+ */
3635
+ private updateOneTimeKeyCount(currentCount: number): void {
3636
+ if (isFinite(currentCount)) {
3637
+ this.oneTimeKeyCount = currentCount;
3638
+ } else {
3639
+ throw new TypeError("Parameter for updateOneTimeKeyCount has to be a number");
3640
+ }
3641
+ }
3642
+
3643
+ public processKeyCounts(oneTimeKeysCounts?: Record<string, number>, unusedFallbackKeys?: string[]): Promise<void> {
3644
+ if (oneTimeKeysCounts !== undefined) {
3645
+ this.updateOneTimeKeyCount(oneTimeKeysCounts["signed_curve25519"] || 0);
3646
+ }
3647
+
3648
+ if (unusedFallbackKeys !== undefined) {
3649
+ // If `unusedFallbackKeys` is defined, that means `device_unused_fallback_key_types`
3650
+ // is present in the sync response, which indicates that the server supports fallback keys.
3651
+ //
3652
+ // If there's no unused signed_curve25519 fallback key, we need a new one.
3653
+ this.needsNewFallback = !unusedFallbackKeys.includes("signed_curve25519");
3654
+ }
3655
+
3656
+ return Promise.resolve();
3657
+ }
3658
+
3659
+ private onToDeviceEvent = (event: MatrixEvent): void => {
3660
+ try {
3661
+ logger.log(
3662
+ `received to-device ${event.getType()} from: ` +
3663
+ `${event.getSender()} id: ${event.getContent()[ToDeviceMessageId]}`,
3664
+ );
3665
+
3666
+ if (event.getType() == "m.room_key" || event.getType() == "m.forwarded_room_key") {
3667
+ this.onRoomKeyEvent(event);
3668
+ } else if (event.getType() == "m.room_key_request") {
3669
+ this.onRoomKeyRequestEvent(event);
3670
+ } else if (event.getType() === "m.secret.request") {
3671
+ this.secretStorage.onRequestReceived(event);
3672
+ } else if (event.getType() === "m.secret.send") {
3673
+ this.secretStorage.onSecretReceived(event);
3674
+ } else if (event.getType() === "m.room_key.withheld") {
3675
+ this.onRoomKeyWithheldEvent(event);
3676
+ } else if (event.getContent().transaction_id) {
3677
+ this.onKeyVerificationMessage(event);
3678
+ } else if (event.getContent().msgtype === "m.bad.encrypted") {
3679
+ this.onToDeviceBadEncrypted(event);
3680
+ } else if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
3681
+ if (!event.isBeingDecrypted()) {
3682
+ event.attemptDecryption(this);
3683
+ }
3684
+ // once the event has been decrypted, try again
3685
+ event.once(MatrixEventEvent.Decrypted, (ev) => {
3686
+ this.onToDeviceEvent(ev);
3687
+ });
3688
+ }
3689
+ } catch (e) {
3690
+ logger.error("Error handling toDeviceEvent:", e);
3691
+ }
3692
+ };
3693
+
3694
+ /**
3695
+ * Handle a key event
3696
+ *
3697
+ * @internal
3698
+ * @param event - key event
3699
+ */
3700
+ private onRoomKeyEvent(event: MatrixEvent): void {
3701
+ const content = event.getContent();
3702
+
3703
+ if (!content.room_id || !content.algorithm) {
3704
+ logger.error("key event is missing fields");
3705
+ return;
3706
+ }
3707
+
3708
+ if (!this.backupManager.checkedForBackup) {
3709
+ // don't bother awaiting on this - the important thing is that we retry if we
3710
+ // haven't managed to check before
3711
+ this.backupManager.checkAndStart();
3712
+ }
3713
+
3714
+ const alg = this.getRoomDecryptor(content.room_id, content.algorithm);
3715
+ alg.onRoomKeyEvent(event);
3716
+ }
3717
+
3718
+ /**
3719
+ * Handle a key withheld event
3720
+ *
3721
+ * @internal
3722
+ * @param event - key withheld event
3723
+ */
3724
+ private onRoomKeyWithheldEvent(event: MatrixEvent): void {
3725
+ const content = event.getContent();
3726
+
3727
+ if (
3728
+ (content.code !== "m.no_olm" && (!content.room_id || !content.session_id)) ||
3729
+ !content.algorithm ||
3730
+ !content.sender_key
3731
+ ) {
3732
+ logger.error("key withheld event is missing fields");
3733
+ return;
3734
+ }
3735
+
3736
+ logger.info(
3737
+ `Got room key withheld event from ${event.getSender()} ` +
3738
+ `for ${content.algorithm} session ${content.sender_key}|${content.session_id} ` +
3739
+ `in room ${content.room_id} with code ${content.code} (${content.reason})`,
3740
+ );
3741
+
3742
+ const alg = this.getRoomDecryptor(content.room_id, content.algorithm);
3743
+ if (alg.onRoomKeyWithheldEvent) {
3744
+ alg.onRoomKeyWithheldEvent(event);
3745
+ }
3746
+ if (!content.room_id) {
3747
+ // retry decryption for all events sent by the sender_key. This will
3748
+ // update the events to show a message indicating that the olm session was
3749
+ // wedged.
3750
+ const roomDecryptors = this.getRoomDecryptors(content.algorithm);
3751
+ for (const decryptor of roomDecryptors) {
3752
+ decryptor.retryDecryptionFromSender(content.sender_key);
3753
+ }
3754
+ }
3755
+ }
3756
+
3757
+ /**
3758
+ * Handle a general key verification event.
3759
+ *
3760
+ * @internal
3761
+ * @param event - verification start event
3762
+ */
3763
+ private onKeyVerificationMessage(event: MatrixEvent): void {
3764
+ if (!ToDeviceChannel.validateEvent(event, this.baseApis)) {
3765
+ return;
3766
+ }
3767
+ const createRequest = (event: MatrixEvent): VerificationRequest | undefined => {
3768
+ if (!ToDeviceChannel.canCreateRequest(ToDeviceChannel.getEventType(event))) {
3769
+ return;
3770
+ }
3771
+ const content = event.getContent();
3772
+ const deviceId = content && content.from_device;
3773
+ if (!deviceId) {
3774
+ return;
3775
+ }
3776
+ const userId = event.getSender()!;
3777
+ const channel = new ToDeviceChannel(this.baseApis, userId, [deviceId]);
3778
+ return new VerificationRequest(channel, this.verificationMethods, this.baseApis);
3779
+ };
3780
+ this.handleVerificationEvent(event, this.toDeviceVerificationRequests, createRequest);
3781
+ }
3782
+
3783
+ /**
3784
+ * Handle key verification requests sent as timeline events
3785
+ *
3786
+ * @internal
3787
+ * @param event - the timeline event
3788
+ * @param room - not used
3789
+ * @param atStart - not used
3790
+ * @param removed - not used
3791
+ * @param whether - this is a live event
3792
+ */
3793
+ private onTimelineEvent = (
3794
+ event: MatrixEvent,
3795
+ room: Room,
3796
+ atStart: boolean,
3797
+ removed: boolean,
3798
+ { liveEvent = true } = {},
3799
+ ): void => {
3800
+ if (!InRoomChannel.validateEvent(event, this.baseApis)) {
3801
+ return;
3802
+ }
3803
+ const createRequest = (event: MatrixEvent): VerificationRequest => {
3804
+ const channel = new InRoomChannel(this.baseApis, event.getRoomId()!);
3805
+ return new VerificationRequest(channel, this.verificationMethods, this.baseApis);
3806
+ };
3807
+ this.handleVerificationEvent(event, this.inRoomVerificationRequests, createRequest, liveEvent);
3808
+ };
3809
+
3810
+ private async handleVerificationEvent(
3811
+ event: MatrixEvent,
3812
+ requestsMap: IRequestsMap,
3813
+ createRequest: (event: MatrixEvent) => VerificationRequest | undefined,
3814
+ isLiveEvent = true,
3815
+ ): Promise<void> {
3816
+ // Wait for event to get its final ID with pendingEventOrdering: "chronological", since DM channels depend on it.
3817
+ if (event.isSending() && event.status != EventStatus.SENT) {
3818
+ let eventIdListener: () => void;
3819
+ let statusListener: () => void;
3820
+ try {
3821
+ await new Promise<void>((resolve, reject) => {
3822
+ eventIdListener = resolve;
3823
+ statusListener = (): void => {
3824
+ if (event.status == EventStatus.CANCELLED) {
3825
+ reject(new Error("Event status set to CANCELLED."));
3826
+ }
3827
+ };
3828
+ event.once(MatrixEventEvent.LocalEventIdReplaced, eventIdListener);
3829
+ event.on(MatrixEventEvent.Status, statusListener);
3830
+ });
3831
+ } catch (err) {
3832
+ logger.error("error while waiting for the verification event to be sent: ", err);
3833
+ return;
3834
+ } finally {
3835
+ event.removeListener(MatrixEventEvent.LocalEventIdReplaced, eventIdListener!);
3836
+ event.removeListener(MatrixEventEvent.Status, statusListener!);
3837
+ }
3838
+ }
3839
+ let request: VerificationRequest | undefined = requestsMap.getRequest(event);
3840
+ let isNewRequest = false;
3841
+ if (!request) {
3842
+ request = createRequest(event);
3843
+ // a request could not be made from this event, so ignore event
3844
+ if (!request) {
3845
+ logger.log(
3846
+ `Crypto: could not find VerificationRequest for ` +
3847
+ `${event.getType()}, and could not create one, so ignoring.`,
3848
+ );
3849
+ return;
3850
+ }
3851
+ isNewRequest = true;
3852
+ requestsMap.setRequest(event, request);
3853
+ }
3854
+ event.setVerificationRequest(request);
3855
+ try {
3856
+ await request.channel.handleEvent(event, request, isLiveEvent);
3857
+ } catch (err) {
3858
+ logger.error("error while handling verification event", err);
3859
+ }
3860
+ const shouldEmit =
3861
+ isNewRequest &&
3862
+ !request.initiatedByMe &&
3863
+ !request.invalid && // check it has enough events to pass the UNSENT stage
3864
+ !request.observeOnly;
3865
+ if (shouldEmit) {
3866
+ this.baseApis.emit(CryptoEvent.VerificationRequest, request);
3867
+ this.baseApis.emit(CryptoEvent.VerificationRequestReceived, request);
3868
+ }
3869
+ }
3870
+
3871
+ /**
3872
+ * Handle a toDevice event that couldn't be decrypted
3873
+ *
3874
+ * @internal
3875
+ * @param event - undecryptable event
3876
+ */
3877
+ private async onToDeviceBadEncrypted(event: MatrixEvent): Promise<void> {
3878
+ const content = event.getWireContent();
3879
+ const sender = event.getSender();
3880
+ const algorithm = content.algorithm;
3881
+ const deviceKey = content.sender_key;
3882
+
3883
+ this.baseApis.emit(ClientEvent.UndecryptableToDeviceEvent, event);
3884
+
3885
+ // retry decryption for all events sent by the sender_key. This will
3886
+ // update the events to show a message indicating that the olm session was
3887
+ // wedged.
3888
+ const retryDecryption = (): void => {
3889
+ const roomDecryptors = this.getRoomDecryptors(olmlib.MEGOLM_ALGORITHM);
3890
+ for (const decryptor of roomDecryptors) {
3891
+ decryptor.retryDecryptionFromSender(deviceKey);
3892
+ }
3893
+ };
3894
+
3895
+ if (sender === undefined || deviceKey === undefined || deviceKey === undefined) {
3896
+ return;
3897
+ }
3898
+
3899
+ // check when we can force a new session with this device: if we've already done so
3900
+ // recently, don't do it again.
3901
+ const forceNewSessionRetryTimeDevices = this.forceNewSessionRetryTime.getOrCreate(sender);
3902
+ const forceNewSessionRetryTime = forceNewSessionRetryTimeDevices.getOrCreate(deviceKey);
3903
+ if (forceNewSessionRetryTime > Date.now()) {
3904
+ logger.debug(
3905
+ `New session already forced with device ${sender}:${deviceKey}: ` +
3906
+ `not forcing another until at least ${new Date(forceNewSessionRetryTime).toUTCString()}`,
3907
+ );
3908
+ await this.olmDevice.recordSessionProblem(deviceKey, "wedged", true);
3909
+ retryDecryption();
3910
+ return;
3911
+ }
3912
+
3913
+ // make sure we don't retry to unwedge too soon even if we fail to create a new session
3914
+ forceNewSessionRetryTimeDevices.set(deviceKey, Date.now() + FORCE_SESSION_RETRY_INTERVAL_MS);
3915
+
3916
+ // establish a new olm session with this device since we're failing to decrypt messages
3917
+ // on a current session.
3918
+ // Note that an undecryptable message from another device could easily be spoofed -
3919
+ // is there anything we can do to mitigate this?
3920
+ let device = this.deviceList.getDeviceByIdentityKey(algorithm, deviceKey);
3921
+ if (!device) {
3922
+ // if we don't know about the device, fetch the user's devices again
3923
+ // and retry before giving up
3924
+ await this.downloadKeys([sender], false);
3925
+ device = this.deviceList.getDeviceByIdentityKey(algorithm, deviceKey);
3926
+ if (!device) {
3927
+ logger.info("Couldn't find device for identity key " + deviceKey + ": not re-establishing session");
3928
+ await this.olmDevice.recordSessionProblem(deviceKey, "wedged", false);
3929
+ retryDecryption();
3930
+ return;
3931
+ }
3932
+ }
3933
+ const devicesByUser = new Map([[sender, [device]]]);
3934
+ await olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, true);
3935
+
3936
+ forceNewSessionRetryTimeDevices.set(deviceKey, Date.now() + MIN_FORCE_SESSION_INTERVAL_MS);
3937
+
3938
+ // Now send a blank message on that session so the other side knows about it.
3939
+ // (The keyshare request is sent in the clear so that won't do)
3940
+ // We send this first such that, as long as the toDevice messages arrive in the
3941
+ // same order we sent them, the other end will get this first, set up the new session,
3942
+ // then get the keyshare request and send the key over this new session (because it
3943
+ // is the session it has most recently received a message on).
3944
+ const encryptedContent: IEncryptedContent = {
3945
+ algorithm: olmlib.OLM_ALGORITHM,
3946
+ sender_key: this.olmDevice.deviceCurve25519Key!,
3947
+ ciphertext: {},
3948
+ [ToDeviceMessageId]: uuidv4(),
3949
+ };
3950
+ await olmlib.encryptMessageForDevice(
3951
+ encryptedContent.ciphertext,
3952
+ this.userId,
3953
+ this.deviceId,
3954
+ this.olmDevice,
3955
+ sender,
3956
+ device,
3957
+ { type: "m.dummy" },
3958
+ );
3959
+
3960
+ await this.olmDevice.recordSessionProblem(deviceKey, "wedged", true);
3961
+ retryDecryption();
3962
+
3963
+ await this.baseApis.sendToDevice(
3964
+ "m.room.encrypted",
3965
+ new Map([[sender, new Map([[device.deviceId, encryptedContent]])]]),
3966
+ );
3967
+
3968
+ // Most of the time this probably won't be necessary since we'll have queued up a key request when
3969
+ // we failed to decrypt the message and will be waiting a bit for the key to arrive before sending
3970
+ // it. This won't always be the case though so we need to re-send any that have already been sent
3971
+ // to avoid races.
3972
+ const requestsToResend = await this.outgoingRoomKeyRequestManager.getOutgoingSentRoomKeyRequest(
3973
+ sender,
3974
+ device.deviceId,
3975
+ );
3976
+ for (const keyReq of requestsToResend) {
3977
+ this.requestRoomKey(keyReq.requestBody, keyReq.recipients, true);
3978
+ }
3979
+ }
3980
+
3981
+ /**
3982
+ * Handle a change in the membership state of a member of a room
3983
+ *
3984
+ * @internal
3985
+ * @param event - event causing the change
3986
+ * @param member - user whose membership changed
3987
+ * @param oldMembership - previous membership
3988
+ */
3989
+ private onRoomMembership(event: MatrixEvent, member: RoomMember, oldMembership?: string): void {
3990
+ // this event handler is registered on the *client* (as opposed to the room
3991
+ // member itself), which means it is only called on changes to the *live*
3992
+ // membership state (ie, it is not called when we back-paginate, nor when
3993
+ // we load the state in the initialsync).
3994
+ //
3995
+ // Further, it is automatically registered and called when new members
3996
+ // arrive in the room.
3997
+
3998
+ const roomId = member.roomId;
3999
+
4000
+ const alg = this.roomEncryptors.get(roomId);
4001
+ if (!alg) {
4002
+ // not encrypting in this room
4003
+ return;
4004
+ }
4005
+ // only mark users in this room as tracked if we already started tracking in this room
4006
+ // this way we don't start device queries after sync on behalf of this room which we won't use
4007
+ // the result of anyway, as we'll need to do a query again once all the members are fetched
4008
+ // by calling _trackRoomDevices
4009
+ if (roomId in this.roomDeviceTrackingState) {
4010
+ if (member.membership == KnownMembership.Join) {
4011
+ logger.log("Join event for " + member.userId + " in " + roomId);
4012
+ // make sure we are tracking the deviceList for this user
4013
+ this.deviceList.startTrackingDeviceList(member.userId);
4014
+ } else if (
4015
+ member.membership == KnownMembership.Invite &&
4016
+ this.clientStore.getRoom(roomId)?.shouldEncryptForInvitedMembers()
4017
+ ) {
4018
+ logger.log("Invite event for " + member.userId + " in " + roomId);
4019
+ this.deviceList.startTrackingDeviceList(member.userId);
4020
+ }
4021
+ }
4022
+
4023
+ alg.onRoomMembership(event, member, oldMembership);
4024
+ }
4025
+
4026
+ /**
4027
+ * Called when we get an m.room_key_request event.
4028
+ *
4029
+ * @internal
4030
+ * @param event - key request event
4031
+ */
4032
+ private onRoomKeyRequestEvent(event: MatrixEvent): void {
4033
+ const content = event.getContent();
4034
+ if (content.action === "request") {
4035
+ // Queue it up for now, because they tend to arrive before the room state
4036
+ // events at initial sync, and we want to see if we know anything about the
4037
+ // room before passing them on to the app.
4038
+ const req = new IncomingRoomKeyRequest(event);
4039
+ this.receivedRoomKeyRequests.push(req);
4040
+ } else if (content.action === "request_cancellation") {
4041
+ const req = new IncomingRoomKeyRequestCancellation(event);
4042
+ this.receivedRoomKeyRequestCancellations.push(req);
4043
+ }
4044
+ }
4045
+
4046
+ /**
4047
+ * Process any m.room_key_request events which were queued up during the
4048
+ * current sync.
4049
+ *
4050
+ * @internal
4051
+ */
4052
+ private async processReceivedRoomKeyRequests(): Promise<void> {
4053
+ if (this.processingRoomKeyRequests) {
4054
+ // we're still processing last time's requests; keep queuing new ones
4055
+ // up for now.
4056
+ return;
4057
+ }
4058
+ this.processingRoomKeyRequests = true;
4059
+
4060
+ try {
4061
+ // we need to grab and clear the queues in the synchronous bit of this method,
4062
+ // so that we don't end up racing with the next /sync.
4063
+ const requests = this.receivedRoomKeyRequests;
4064
+ this.receivedRoomKeyRequests = [];
4065
+ const cancellations = this.receivedRoomKeyRequestCancellations;
4066
+ this.receivedRoomKeyRequestCancellations = [];
4067
+
4068
+ // Process all of the requests, *then* all of the cancellations.
4069
+ //
4070
+ // This makes sure that if we get a request and its cancellation in the
4071
+ // same /sync result, then we process the request before the
4072
+ // cancellation (and end up with a cancelled request), rather than the
4073
+ // cancellation before the request (and end up with an outstanding
4074
+ // request which should have been cancelled.)
4075
+ await Promise.all(requests.map((req) => this.processReceivedRoomKeyRequest(req)));
4076
+ await Promise.all(
4077
+ cancellations.map((cancellation) => this.processReceivedRoomKeyRequestCancellation(cancellation)),
4078
+ );
4079
+ } catch (e) {
4080
+ logger.error(`Error processing room key requsts: ${e}`);
4081
+ } finally {
4082
+ this.processingRoomKeyRequests = false;
4083
+ }
4084
+ }
4085
+
4086
+ /**
4087
+ * Helper for processReceivedRoomKeyRequests
4088
+ *
4089
+ */
4090
+ private async processReceivedRoomKeyRequest(req: IncomingRoomKeyRequest): Promise<void> {
4091
+ const userId = req.userId;
4092
+ const deviceId = req.deviceId;
4093
+
4094
+ const body = req.requestBody;
4095
+ const roomId = body.room_id;
4096
+ const alg = body.algorithm;
4097
+
4098
+ logger.log(
4099
+ `m.room_key_request from ${userId}:${deviceId}` +
4100
+ ` for ${roomId} / ${body.session_id} (id ${req.requestId})`,
4101
+ );
4102
+
4103
+ if (userId !== this.userId) {
4104
+ if (!this.roomEncryptors.get(roomId)) {
4105
+ logger.debug(`room key request for unencrypted room ${roomId}`);
4106
+ return;
4107
+ }
4108
+ const encryptor = this.roomEncryptors.get(roomId)!;
4109
+ const device = this.deviceList.getStoredDevice(userId, deviceId);
4110
+ if (!device) {
4111
+ logger.debug(`Ignoring keyshare for unknown device ${userId}:${deviceId}`);
4112
+ return;
4113
+ }
4114
+
4115
+ try {
4116
+ await encryptor.reshareKeyWithDevice!(body.sender_key, body.session_id, userId, device);
4117
+ } catch (e) {
4118
+ logger.warn(
4119
+ "Failed to re-share keys for session " +
4120
+ body.session_id +
4121
+ " with device " +
4122
+ userId +
4123
+ ":" +
4124
+ device.deviceId,
4125
+ e,
4126
+ );
4127
+ }
4128
+ return;
4129
+ }
4130
+
4131
+ if (deviceId === this.deviceId) {
4132
+ // We'll always get these because we send room key requests to
4133
+ // '*' (ie. 'all devices') which includes the sending device,
4134
+ // so ignore requests from ourself because apart from it being
4135
+ // very silly, it won't work because an Olm session cannot send
4136
+ // messages to itself.
4137
+ // The log here is probably superfluous since we know this will
4138
+ // always happen, but let's log anyway for now just in case it
4139
+ // causes issues.
4140
+ logger.log("Ignoring room key request from ourselves");
4141
+ return;
4142
+ }
4143
+
4144
+ // todo: should we queue up requests we don't yet have keys for,
4145
+ // in case they turn up later?
4146
+
4147
+ // if we don't have a decryptor for this room/alg, we don't have
4148
+ // the keys for the requested events, and can drop the requests.
4149
+ if (!this.roomDecryptors.has(roomId)) {
4150
+ logger.log(`room key request for unencrypted room ${roomId}`);
4151
+ return;
4152
+ }
4153
+
4154
+ const decryptor = this.roomDecryptors.get(roomId)!.get(alg);
4155
+ if (!decryptor) {
4156
+ logger.log(`room key request for unknown alg ${alg} in room ${roomId}`);
4157
+ return;
4158
+ }
4159
+
4160
+ if (!(await decryptor.hasKeysForKeyRequest(req))) {
4161
+ logger.log(`room key request for unknown session ${roomId} / ` + body.session_id);
4162
+ return;
4163
+ }
4164
+
4165
+ req.share = (): void => {
4166
+ decryptor.shareKeysWithDevice(req);
4167
+ };
4168
+
4169
+ // if the device is verified already, share the keys
4170
+ if (this.checkDeviceTrust(userId, deviceId).isVerified()) {
4171
+ logger.log("device is already verified: sharing keys");
4172
+ req.share();
4173
+ return;
4174
+ }
4175
+
4176
+ this.emit(CryptoEvent.RoomKeyRequest, req);
4177
+ }
4178
+
4179
+ /**
4180
+ * Helper for processReceivedRoomKeyRequests
4181
+ *
4182
+ */
4183
+ private async processReceivedRoomKeyRequestCancellation(
4184
+ cancellation: IncomingRoomKeyRequestCancellation,
4185
+ ): Promise<void> {
4186
+ logger.log(
4187
+ `m.room_key_request cancellation for ${cancellation.userId}:` +
4188
+ `${cancellation.deviceId} (id ${cancellation.requestId})`,
4189
+ );
4190
+
4191
+ // we should probably only notify the app of cancellations we told it
4192
+ // about, but we don't currently have a record of that, so we just pass
4193
+ // everything through.
4194
+ this.emit(CryptoEvent.RoomKeyRequestCancellation, cancellation);
4195
+ }
4196
+
4197
+ /**
4198
+ * Get a decryptor for a given room and algorithm.
4199
+ *
4200
+ * If we already have a decryptor for the given room and algorithm, return
4201
+ * it. Otherwise try to instantiate it.
4202
+ *
4203
+ * @internal
4204
+ *
4205
+ * @param roomId - room id for decryptor. If undefined, a temporary
4206
+ * decryptor is instantiated.
4207
+ *
4208
+ * @param algorithm - crypto algorithm
4209
+ *
4210
+ * @throws `DecryptionError` if the algorithm is unknown
4211
+ */
4212
+ public getRoomDecryptor(roomId: string | null, algorithm: string): DecryptionAlgorithm {
4213
+ let decryptors: Map<string, DecryptionAlgorithm> | undefined;
4214
+ let alg: DecryptionAlgorithm | undefined;
4215
+
4216
+ if (roomId) {
4217
+ decryptors = this.roomDecryptors.get(roomId);
4218
+ if (!decryptors) {
4219
+ decryptors = new Map<string, DecryptionAlgorithm>();
4220
+ this.roomDecryptors.set(roomId, decryptors);
4221
+ }
4222
+
4223
+ alg = decryptors.get(algorithm);
4224
+ if (alg) {
4225
+ return alg;
4226
+ }
4227
+ }
4228
+
4229
+ const AlgClass = algorithms.DECRYPTION_CLASSES.get(algorithm);
4230
+ if (!AlgClass) {
4231
+ throw new DecryptionError(
4232
+ DecryptionFailureCode.UNKNOWN_ENCRYPTION_ALGORITHM,
4233
+ 'Unknown encryption algorithm "' + algorithm + '".',
4234
+ );
4235
+ }
4236
+ alg = new AlgClass({
4237
+ userId: this.userId,
4238
+ crypto: this,
4239
+ olmDevice: this.olmDevice,
4240
+ baseApis: this.baseApis,
4241
+ roomId: roomId ?? undefined,
4242
+ });
4243
+
4244
+ if (decryptors) {
4245
+ decryptors.set(algorithm, alg);
4246
+ }
4247
+ return alg;
4248
+ }
4249
+
4250
+ /**
4251
+ * Get all the room decryptors for a given encryption algorithm.
4252
+ *
4253
+ * @param algorithm - The encryption algorithm
4254
+ *
4255
+ * @returns An array of room decryptors
4256
+ */
4257
+ private getRoomDecryptors(algorithm: string): DecryptionAlgorithm[] {
4258
+ const decryptors: DecryptionAlgorithm[] = [];
4259
+ for (const d of this.roomDecryptors.values()) {
4260
+ if (d.has(algorithm)) {
4261
+ decryptors.push(d.get(algorithm)!);
4262
+ }
4263
+ }
4264
+ return decryptors;
4265
+ }
4266
+
4267
+ /**
4268
+ * sign the given object with our ed25519 key
4269
+ *
4270
+ * @param obj - Object to which we will add a 'signatures' property
4271
+ */
4272
+ public async signObject<T extends ISignableObject & object>(obj: T): Promise<void> {
4273
+ const sigs = new Map(Object.entries(obj.signatures || {}));
4274
+ const unsigned = obj.unsigned;
4275
+
4276
+ delete obj.signatures;
4277
+ delete obj.unsigned;
4278
+
4279
+ const userSignatures = sigs.get(this.userId) || {};
4280
+ sigs.set(this.userId, userSignatures);
4281
+ userSignatures["ed25519:" + this.deviceId] = await this.olmDevice.sign(anotherjson.stringify(obj));
4282
+ obj.signatures = recursiveMapToObject(sigs);
4283
+ if (unsigned !== undefined) obj.unsigned = unsigned;
4284
+ }
4285
+
4286
+ /**
4287
+ * @returns true if the room with the supplied ID is encrypted. False if the
4288
+ * room is not encrypted, or is unknown to us.
4289
+ */
4290
+ public isRoomEncrypted(roomId: string): boolean {
4291
+ return this.roomList.isRoomEncrypted(roomId);
4292
+ }
4293
+
4294
+ /**
4295
+ * Implementation of {@link Crypto.CryptoApi#isEncryptionEnabledInRoom}.
4296
+ */
4297
+ public async isEncryptionEnabledInRoom(roomId: string): Promise<boolean> {
4298
+ return this.isRoomEncrypted(roomId);
4299
+ }
4300
+
4301
+ /**
4302
+ * @returns information about the encryption on the room with the supplied
4303
+ * ID, or null if the room is not encrypted or unknown to us.
4304
+ */
4305
+ public getRoomEncryption(roomId: string): IRoomEncryption | null {
4306
+ return this.roomList.getRoomEncryption(roomId);
4307
+ }
4308
+
4309
+ /**
4310
+ * Returns whether dehydrated devices are supported by the crypto backend
4311
+ * and by the server.
4312
+ */
4313
+ public async isDehydrationSupported(): Promise<boolean> {
4314
+ return false;
4315
+ }
4316
+
4317
+ /**
4318
+ * Stub function -- dehydration is not implemented here, so throw error
4319
+ */
4320
+ public async startDehydration(createNewKey?: boolean): Promise<void> {
4321
+ throw new Error("Not implemented");
4322
+ }
4323
+
4324
+ /**
4325
+ * Stub function -- restoreKeyBackup is not implemented here, so throw error
4326
+ */
4327
+ public restoreKeyBackup(opts: KeyBackupRestoreOpts): Promise<KeyBackupRestoreResult> {
4328
+ throw new Error("Not implemented");
4329
+ }
4330
+
4331
+ /**
4332
+ * Stub function -- restoreKeyBackupWithPassphrase is not implemented here, so throw error
4333
+ */
4334
+ public restoreKeyBackupWithPassphrase(
4335
+ passphrase: string,
4336
+ opts: KeyBackupRestoreOpts,
4337
+ ): Promise<KeyBackupRestoreResult> {
4338
+ throw new Error("Not implemented");
4339
+ }
4340
+ }
4341
+
4342
+ /**
4343
+ * Fix up the backup key, that may be in the wrong format due to a bug in a
4344
+ * migration step. Some backup keys were stored as a comma-separated list of
4345
+ * integers, rather than a base64-encoded byte array. If this function is
4346
+ * passed a string that looks like a list of integers rather than a base64
4347
+ * string, it will attempt to convert it to the right format.
4348
+ *
4349
+ * @param key - the key to check
4350
+ * @returns If the key is in the wrong format, then the fixed
4351
+ * key will be returned. Otherwise null will be returned.
4352
+ *
4353
+ */
4354
+ export function fixBackupKey(key?: string): string | null {
4355
+ if (typeof key !== "string" || key.indexOf(",") < 0) {
4356
+ return null;
4357
+ }
4358
+ const fixedKey = Uint8Array.from(key.split(","), (x) => parseInt(x));
4359
+ return encodeBase64(fixedKey);
4360
+ }
4361
+
4362
+ /**
4363
+ * Represents a received m.room_key_request event
4364
+ */
4365
+ export class IncomingRoomKeyRequest {
4366
+ /** user requesting the key */
4367
+ public readonly userId: string;
4368
+ /** device requesting the key */
4369
+ public readonly deviceId: string;
4370
+ /** unique id for the request */
4371
+ public readonly requestId: string;
4372
+ public readonly requestBody: IRoomKeyRequestBody;
4373
+ /**
4374
+ * callback which, when called, will ask
4375
+ * the relevant crypto algorithm implementation to share the keys for
4376
+ * this request.
4377
+ */
4378
+ public share: () => void;
4379
+
4380
+ public constructor(event: MatrixEvent) {
4381
+ const content = event.getContent();
4382
+
4383
+ this.userId = event.getSender()!;
4384
+ this.deviceId = content.requesting_device_id;
4385
+ this.requestId = content.request_id;
4386
+ this.requestBody = content.body || {};
4387
+ this.share = (): void => {
4388
+ throw new Error("don't know how to share keys for this request yet");
4389
+ };
4390
+ }
4391
+ }
4392
+
4393
+ /**
4394
+ * Represents a received m.room_key_request cancellation
4395
+ */
4396
+ class IncomingRoomKeyRequestCancellation {
4397
+ /** user requesting the cancellation */
4398
+ public readonly userId: string;
4399
+ /** device requesting the cancellation */
4400
+ public readonly deviceId: string;
4401
+ /** unique id for the request to be cancelled */
4402
+ public readonly requestId: string;
4403
+
4404
+ public constructor(event: MatrixEvent) {
4405
+ const content = event.getContent();
4406
+
4407
+ this.userId = event.getSender()!;
4408
+ this.deviceId = content.requesting_device_id;
4409
+ this.requestId = content.request_id;
4410
+ }
4411
+ }
4412
+
4413
+ // a number of types are re-exported for backwards compatibility, in case any applications are referencing it.
4414
+ export type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto.ts";