@unwanted/matrix-sdk-mini 34.12.0-1 → 34.12.0-3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (636) hide show
  1. package/git-revision.txt +1 -1
  2. package/lib/@types/event.d.ts +0 -19
  3. package/lib/@types/event.d.ts.map +1 -1
  4. package/lib/@types/event.js.map +1 -1
  5. package/lib/@types/global.d.js +0 -2
  6. package/lib/@types/global.d.js.map +1 -1
  7. package/lib/browser-index.d.ts.map +1 -1
  8. package/lib/browser-index.js +0 -11
  9. package/lib/browser-index.js.map +1 -1
  10. package/lib/client.d.ts +2 -1224
  11. package/lib/client.d.ts.map +1 -1
  12. package/lib/client.js +345 -2826
  13. package/lib/client.js.map +1 -1
  14. package/lib/embedded.d.ts +0 -22
  15. package/lib/embedded.d.ts.map +1 -1
  16. package/lib/embedded.js +24 -167
  17. package/lib/embedded.js.map +1 -1
  18. package/lib/event-mapper.d.ts.map +1 -1
  19. package/lib/event-mapper.js +0 -4
  20. package/lib/event-mapper.js.map +1 -1
  21. package/lib/matrix.d.ts +0 -25
  22. package/lib/matrix.d.ts.map +1 -1
  23. package/lib/matrix.js +1 -30
  24. package/lib/matrix.js.map +1 -1
  25. package/lib/models/MSC3089Branch.d.ts.map +1 -1
  26. package/lib/models/MSC3089Branch.js +0 -3
  27. package/lib/models/MSC3089Branch.js.map +1 -1
  28. package/lib/models/event.d.ts +0 -94
  29. package/lib/models/event.d.ts.map +1 -1
  30. package/lib/models/event.js +0 -274
  31. package/lib/models/event.js.map +1 -1
  32. package/lib/models/poll.d.ts.map +1 -1
  33. package/lib/models/poll.js +1 -5
  34. package/lib/models/poll.js.map +1 -1
  35. package/lib/models/relations-container.d.ts.map +1 -1
  36. package/lib/models/relations-container.js +1 -7
  37. package/lib/models/relations-container.js.map +1 -1
  38. package/lib/models/relations.d.ts +0 -1
  39. package/lib/models/relations.d.ts.map +1 -1
  40. package/lib/models/relations.js +0 -8
  41. package/lib/models/relations.js.map +1 -1
  42. package/lib/models/room-state.d.ts.map +1 -1
  43. package/lib/models/room-state.js +10 -26
  44. package/lib/models/room-state.js.map +1 -1
  45. package/lib/models/room.d.ts +0 -18
  46. package/lib/models/room.d.ts.map +1 -1
  47. package/lib/models/room.js +94 -148
  48. package/lib/models/room.js.map +1 -1
  49. package/lib/models/thread.d.ts.map +1 -1
  50. package/lib/models/thread.js +0 -1
  51. package/lib/models/thread.js.map +1 -1
  52. package/lib/sliding-sync-sdk.d.ts +2 -3
  53. package/lib/sliding-sync-sdk.d.ts.map +1 -1
  54. package/lib/sliding-sync-sdk.js +41 -90
  55. package/lib/sliding-sync-sdk.js.map +1 -1
  56. package/lib/sync.d.ts +0 -12
  57. package/lib/sync.d.ts.map +1 -1
  58. package/lib/sync.js +1 -73
  59. package/lib/sync.js.map +1 -1
  60. package/lib/testing.d.ts +0 -48
  61. package/lib/testing.d.ts.map +1 -1
  62. package/lib/testing.js +0 -105
  63. package/lib/testing.js.map +1 -1
  64. package/package.json +1 -3
  65. package/src/@types/event.ts +2 -36
  66. package/src/@types/global.d.ts +0 -3
  67. package/src/browser-index.ts +0 -11
  68. package/src/client.ts +52 -2876
  69. package/src/embedded.ts +3 -132
  70. package/src/event-mapper.ts +0 -4
  71. package/src/matrix.ts +0 -41
  72. package/src/models/MSC3089Branch.ts +0 -3
  73. package/src/models/event.ts +0 -289
  74. package/src/models/poll.ts +0 -6
  75. package/src/models/relations-container.ts +1 -8
  76. package/src/models/relations.ts +0 -8
  77. package/src/models/room-state.ts +2 -8
  78. package/src/models/room.ts +0 -62
  79. package/src/models/thread.ts +0 -1
  80. package/src/sliding-sync-sdk.ts +2 -72
  81. package/src/sync.ts +1 -98
  82. package/src/testing.ts +0 -108
  83. package/lib/@types/crypto.d.ts +0 -47
  84. package/lib/@types/crypto.d.ts.map +0 -1
  85. package/lib/@types/crypto.js +0 -1
  86. package/lib/@types/crypto.js.map +0 -1
  87. package/lib/@types/matrix-sdk-crypto-wasm.d.js +0 -1
  88. package/lib/@types/matrix-sdk-crypto-wasm.d.js.map +0 -1
  89. package/lib/common-crypto/CryptoBackend.d.ts +0 -240
  90. package/lib/common-crypto/CryptoBackend.d.ts.map +0 -1
  91. package/lib/common-crypto/CryptoBackend.js +0 -73
  92. package/lib/common-crypto/CryptoBackend.js.map +0 -1
  93. package/lib/common-crypto/key-passphrase.d.ts +0 -14
  94. package/lib/common-crypto/key-passphrase.d.ts.map +0 -1
  95. package/lib/common-crypto/key-passphrase.js +0 -33
  96. package/lib/common-crypto/key-passphrase.js.map +0 -1
  97. package/lib/crypto/CrossSigning.d.ts +0 -184
  98. package/lib/crypto/CrossSigning.d.ts.map +0 -1
  99. package/lib/crypto/CrossSigning.js +0 -718
  100. package/lib/crypto/CrossSigning.js.map +0 -1
  101. package/lib/crypto/DeviceList.d.ts +0 -216
  102. package/lib/crypto/DeviceList.d.ts.map +0 -1
  103. package/lib/crypto/DeviceList.js +0 -892
  104. package/lib/crypto/DeviceList.js.map +0 -1
  105. package/lib/crypto/EncryptionSetup.d.ts +0 -152
  106. package/lib/crypto/EncryptionSetup.d.ts.map +0 -1
  107. package/lib/crypto/EncryptionSetup.js +0 -356
  108. package/lib/crypto/EncryptionSetup.js.map +0 -1
  109. package/lib/crypto/OlmDevice.d.ts +0 -457
  110. package/lib/crypto/OlmDevice.d.ts.map +0 -1
  111. package/lib/crypto/OlmDevice.js +0 -1241
  112. package/lib/crypto/OlmDevice.js.map +0 -1
  113. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts +0 -109
  114. package/lib/crypto/OutgoingRoomKeyRequestManager.d.ts.map +0 -1
  115. package/lib/crypto/OutgoingRoomKeyRequestManager.js +0 -415
  116. package/lib/crypto/OutgoingRoomKeyRequestManager.js.map +0 -1
  117. package/lib/crypto/RoomList.d.ts +0 -26
  118. package/lib/crypto/RoomList.d.ts.map +0 -1
  119. package/lib/crypto/RoomList.js +0 -71
  120. package/lib/crypto/RoomList.js.map +0 -1
  121. package/lib/crypto/SecretSharing.d.ts +0 -24
  122. package/lib/crypto/SecretSharing.d.ts.map +0 -1
  123. package/lib/crypto/SecretSharing.js +0 -194
  124. package/lib/crypto/SecretSharing.js.map +0 -1
  125. package/lib/crypto/SecretStorage.d.ts +0 -55
  126. package/lib/crypto/SecretStorage.d.ts.map +0 -1
  127. package/lib/crypto/SecretStorage.js +0 -118
  128. package/lib/crypto/SecretStorage.js.map +0 -1
  129. package/lib/crypto/aes.d.ts +0 -6
  130. package/lib/crypto/aes.d.ts.map +0 -1
  131. package/lib/crypto/aes.js +0 -24
  132. package/lib/crypto/aes.js.map +0 -1
  133. package/lib/crypto/algorithms/base.d.ts +0 -156
  134. package/lib/crypto/algorithms/base.d.ts.map +0 -1
  135. package/lib/crypto/algorithms/base.js +0 -187
  136. package/lib/crypto/algorithms/base.js.map +0 -1
  137. package/lib/crypto/algorithms/index.d.ts +0 -4
  138. package/lib/crypto/algorithms/index.d.ts.map +0 -1
  139. package/lib/crypto/algorithms/index.js +0 -20
  140. package/lib/crypto/algorithms/index.js.map +0 -1
  141. package/lib/crypto/algorithms/megolm.d.ts +0 -385
  142. package/lib/crypto/algorithms/megolm.d.ts.map +0 -1
  143. package/lib/crypto/algorithms/megolm.js +0 -1822
  144. package/lib/crypto/algorithms/megolm.js.map +0 -1
  145. package/lib/crypto/algorithms/olm.d.ts +0 -5
  146. package/lib/crypto/algorithms/olm.d.ts.map +0 -1
  147. package/lib/crypto/algorithms/olm.js +0 -299
  148. package/lib/crypto/algorithms/olm.js.map +0 -1
  149. package/lib/crypto/api.d.ts +0 -32
  150. package/lib/crypto/api.d.ts.map +0 -1
  151. package/lib/crypto/api.js +0 -22
  152. package/lib/crypto/api.js.map +0 -1
  153. package/lib/crypto/backup.d.ts +0 -227
  154. package/lib/crypto/backup.d.ts.map +0 -1
  155. package/lib/crypto/backup.js +0 -824
  156. package/lib/crypto/backup.js.map +0 -1
  157. package/lib/crypto/crypto.d.ts +0 -3
  158. package/lib/crypto/crypto.d.ts.map +0 -1
  159. package/lib/crypto/crypto.js +0 -19
  160. package/lib/crypto/crypto.js.map +0 -1
  161. package/lib/crypto/dehydration.d.ts +0 -34
  162. package/lib/crypto/dehydration.d.ts.map +0 -1
  163. package/lib/crypto/dehydration.js +0 -252
  164. package/lib/crypto/dehydration.js.map +0 -1
  165. package/lib/crypto/device-converter.d.ts +0 -9
  166. package/lib/crypto/device-converter.d.ts.map +0 -1
  167. package/lib/crypto/device-converter.js +0 -42
  168. package/lib/crypto/device-converter.js.map +0 -1
  169. package/lib/crypto/deviceinfo.d.ts +0 -99
  170. package/lib/crypto/deviceinfo.d.ts.map +0 -1
  171. package/lib/crypto/deviceinfo.js +0 -148
  172. package/lib/crypto/deviceinfo.js.map +0 -1
  173. package/lib/crypto/index.d.ts +0 -1209
  174. package/lib/crypto/index.d.ts.map +0 -1
  175. package/lib/crypto/index.js +0 -4097
  176. package/lib/crypto/index.js.map +0 -1
  177. package/lib/crypto/key_passphrase.d.ts +0 -14
  178. package/lib/crypto/key_passphrase.d.ts.map +0 -1
  179. package/lib/crypto/key_passphrase.js +0 -44
  180. package/lib/crypto/key_passphrase.js.map +0 -1
  181. package/lib/crypto/keybackup.d.ts +0 -18
  182. package/lib/crypto/keybackup.d.ts.map +0 -1
  183. package/lib/crypto/keybackup.js +0 -1
  184. package/lib/crypto/keybackup.js.map +0 -1
  185. package/lib/crypto/olmlib.d.ts +0 -129
  186. package/lib/crypto/olmlib.d.ts.map +0 -1
  187. package/lib/crypto/olmlib.js +0 -492
  188. package/lib/crypto/olmlib.js.map +0 -1
  189. package/lib/crypto/recoverykey.d.ts +0 -2
  190. package/lib/crypto/recoverykey.d.ts.map +0 -1
  191. package/lib/crypto/recoverykey.js +0 -19
  192. package/lib/crypto/recoverykey.js.map +0 -1
  193. package/lib/crypto/store/base.d.ts +0 -252
  194. package/lib/crypto/store/base.d.ts.map +0 -1
  195. package/lib/crypto/store/base.js +0 -64
  196. package/lib/crypto/store/base.js.map +0 -1
  197. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts +0 -187
  198. package/lib/crypto/store/indexeddb-crypto-store-backend.d.ts.map +0 -1
  199. package/lib/crypto/store/indexeddb-crypto-store-backend.js +0 -1145
  200. package/lib/crypto/store/indexeddb-crypto-store-backend.js.map +0 -1
  201. package/lib/crypto/store/indexeddb-crypto-store.d.ts +0 -432
  202. package/lib/crypto/store/indexeddb-crypto-store.d.ts.map +0 -1
  203. package/lib/crypto/store/indexeddb-crypto-store.js +0 -728
  204. package/lib/crypto/store/indexeddb-crypto-store.js.map +0 -1
  205. package/lib/crypto/store/localStorage-crypto-store.d.ts +0 -119
  206. package/lib/crypto/store/localStorage-crypto-store.d.ts.map +0 -1
  207. package/lib/crypto/store/localStorage-crypto-store.js +0 -531
  208. package/lib/crypto/store/localStorage-crypto-store.js.map +0 -1
  209. package/lib/crypto/store/memory-crypto-store.d.ts +0 -215
  210. package/lib/crypto/store/memory-crypto-store.d.ts.map +0 -1
  211. package/lib/crypto/store/memory-crypto-store.js +0 -622
  212. package/lib/crypto/store/memory-crypto-store.js.map +0 -1
  213. package/lib/crypto/verification/Base.d.ts +0 -105
  214. package/lib/crypto/verification/Base.d.ts.map +0 -1
  215. package/lib/crypto/verification/Base.js +0 -372
  216. package/lib/crypto/verification/Base.js.map +0 -1
  217. package/lib/crypto/verification/Error.d.ts +0 -35
  218. package/lib/crypto/verification/Error.d.ts.map +0 -1
  219. package/lib/crypto/verification/Error.js +0 -86
  220. package/lib/crypto/verification/Error.js.map +0 -1
  221. package/lib/crypto/verification/IllegalMethod.d.ts +0 -15
  222. package/lib/crypto/verification/IllegalMethod.d.ts.map +0 -1
  223. package/lib/crypto/verification/IllegalMethod.js +0 -43
  224. package/lib/crypto/verification/IllegalMethod.js.map +0 -1
  225. package/lib/crypto/verification/QRCode.d.ts +0 -51
  226. package/lib/crypto/verification/QRCode.d.ts.map +0 -1
  227. package/lib/crypto/verification/QRCode.js +0 -277
  228. package/lib/crypto/verification/QRCode.js.map +0 -1
  229. package/lib/crypto/verification/SAS.d.ts +0 -27
  230. package/lib/crypto/verification/SAS.d.ts.map +0 -1
  231. package/lib/crypto/verification/SAS.js +0 -485
  232. package/lib/crypto/verification/SAS.js.map +0 -1
  233. package/lib/crypto/verification/SASDecimal.d.ts +0 -8
  234. package/lib/crypto/verification/SASDecimal.d.ts.map +0 -1
  235. package/lib/crypto/verification/SASDecimal.js +0 -34
  236. package/lib/crypto/verification/SASDecimal.js.map +0 -1
  237. package/lib/crypto/verification/request/Channel.d.ts +0 -18
  238. package/lib/crypto/verification/request/Channel.d.ts.map +0 -1
  239. package/lib/crypto/verification/request/Channel.js +0 -1
  240. package/lib/crypto/verification/request/Channel.js.map +0 -1
  241. package/lib/crypto/verification/request/InRoomChannel.d.ts +0 -113
  242. package/lib/crypto/verification/request/InRoomChannel.d.ts.map +0 -1
  243. package/lib/crypto/verification/request/InRoomChannel.js +0 -351
  244. package/lib/crypto/verification/request/InRoomChannel.js.map +0 -1
  245. package/lib/crypto/verification/request/ToDeviceChannel.d.ts +0 -105
  246. package/lib/crypto/verification/request/ToDeviceChannel.d.ts.map +0 -1
  247. package/lib/crypto/verification/request/ToDeviceChannel.js +0 -328
  248. package/lib/crypto/verification/request/ToDeviceChannel.js.map +0 -1
  249. package/lib/crypto/verification/request/VerificationRequest.d.ts +0 -227
  250. package/lib/crypto/verification/request/VerificationRequest.d.ts.map +0 -1
  251. package/lib/crypto/verification/request/VerificationRequest.js +0 -937
  252. package/lib/crypto/verification/request/VerificationRequest.js.map +0 -1
  253. package/lib/crypto-api/CryptoEvent.d.ts +0 -69
  254. package/lib/crypto-api/CryptoEvent.d.ts.map +0 -1
  255. package/lib/crypto-api/CryptoEvent.js +0 -33
  256. package/lib/crypto-api/CryptoEvent.js.map +0 -1
  257. package/lib/crypto-api/CryptoEventHandlerMap.d.ts +0 -16
  258. package/lib/crypto-api/CryptoEventHandlerMap.d.ts.map +0 -1
  259. package/lib/crypto-api/CryptoEventHandlerMap.js +0 -22
  260. package/lib/crypto-api/CryptoEventHandlerMap.js.map +0 -1
  261. package/lib/crypto-api/index.d.ts +0 -978
  262. package/lib/crypto-api/index.d.ts.map +0 -1
  263. package/lib/crypto-api/index.js +0 -304
  264. package/lib/crypto-api/index.js.map +0 -1
  265. package/lib/crypto-api/key-passphrase.d.ts +0 -11
  266. package/lib/crypto-api/key-passphrase.d.ts.map +0 -1
  267. package/lib/crypto-api/key-passphrase.js +0 -51
  268. package/lib/crypto-api/key-passphrase.js.map +0 -1
  269. package/lib/crypto-api/keybackup.d.ts +0 -88
  270. package/lib/crypto-api/keybackup.d.ts.map +0 -1
  271. package/lib/crypto-api/keybackup.js +0 -1
  272. package/lib/crypto-api/keybackup.js.map +0 -1
  273. package/lib/crypto-api/recovery-key.d.ts +0 -11
  274. package/lib/crypto-api/recovery-key.d.ts.map +0 -1
  275. package/lib/crypto-api/recovery-key.js +0 -65
  276. package/lib/crypto-api/recovery-key.js.map +0 -1
  277. package/lib/crypto-api/verification.d.ts +0 -344
  278. package/lib/crypto-api/verification.d.ts.map +0 -1
  279. package/lib/crypto-api/verification.js +0 -91
  280. package/lib/crypto-api/verification.js.map +0 -1
  281. package/lib/matrixrtc/CallMembership.d.ts +0 -66
  282. package/lib/matrixrtc/CallMembership.d.ts.map +0 -1
  283. package/lib/matrixrtc/CallMembership.js +0 -197
  284. package/lib/matrixrtc/CallMembership.js.map +0 -1
  285. package/lib/matrixrtc/LivekitFocus.d.ts +0 -16
  286. package/lib/matrixrtc/LivekitFocus.d.ts.map +0 -1
  287. package/lib/matrixrtc/LivekitFocus.js +0 -20
  288. package/lib/matrixrtc/LivekitFocus.js.map +0 -1
  289. package/lib/matrixrtc/MatrixRTCSession.d.ts +0 -295
  290. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +0 -1
  291. package/lib/matrixrtc/MatrixRTCSession.js +0 -1043
  292. package/lib/matrixrtc/MatrixRTCSession.js.map +0 -1
  293. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +0 -40
  294. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +0 -1
  295. package/lib/matrixrtc/MatrixRTCSessionManager.js +0 -146
  296. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +0 -1
  297. package/lib/matrixrtc/focus.d.ts +0 -10
  298. package/lib/matrixrtc/focus.d.ts.map +0 -1
  299. package/lib/matrixrtc/focus.js +0 -1
  300. package/lib/matrixrtc/focus.js.map +0 -1
  301. package/lib/matrixrtc/index.d.ts +0 -7
  302. package/lib/matrixrtc/index.d.ts.map +0 -1
  303. package/lib/matrixrtc/index.js +0 -21
  304. package/lib/matrixrtc/index.js.map +0 -1
  305. package/lib/matrixrtc/types.d.ts +0 -19
  306. package/lib/matrixrtc/types.d.ts.map +0 -1
  307. package/lib/matrixrtc/types.js +0 -1
  308. package/lib/matrixrtc/types.js.map +0 -1
  309. package/lib/rendezvous/MSC4108SignInWithQR.d.ts +0 -112
  310. package/lib/rendezvous/MSC4108SignInWithQR.d.ts.map +0 -1
  311. package/lib/rendezvous/MSC4108SignInWithQR.js +0 -392
  312. package/lib/rendezvous/MSC4108SignInWithQR.js.map +0 -1
  313. package/lib/rendezvous/RendezvousChannel.d.ts +0 -27
  314. package/lib/rendezvous/RendezvousChannel.d.ts.map +0 -1
  315. package/lib/rendezvous/RendezvousChannel.js +0 -1
  316. package/lib/rendezvous/RendezvousChannel.js.map +0 -1
  317. package/lib/rendezvous/RendezvousCode.d.ts +0 -9
  318. package/lib/rendezvous/RendezvousCode.d.ts.map +0 -1
  319. package/lib/rendezvous/RendezvousCode.js +0 -1
  320. package/lib/rendezvous/RendezvousCode.js.map +0 -1
  321. package/lib/rendezvous/RendezvousError.d.ts +0 -6
  322. package/lib/rendezvous/RendezvousError.d.ts.map +0 -1
  323. package/lib/rendezvous/RendezvousError.js +0 -23
  324. package/lib/rendezvous/RendezvousError.js.map +0 -1
  325. package/lib/rendezvous/RendezvousFailureReason.d.ts +0 -31
  326. package/lib/rendezvous/RendezvousFailureReason.d.ts.map +0 -1
  327. package/lib/rendezvous/RendezvousFailureReason.js +0 -38
  328. package/lib/rendezvous/RendezvousFailureReason.js.map +0 -1
  329. package/lib/rendezvous/RendezvousIntent.d.ts +0 -5
  330. package/lib/rendezvous/RendezvousIntent.d.ts.map +0 -1
  331. package/lib/rendezvous/RendezvousIntent.js +0 -22
  332. package/lib/rendezvous/RendezvousIntent.js.map +0 -1
  333. package/lib/rendezvous/RendezvousTransport.d.ts +0 -36
  334. package/lib/rendezvous/RendezvousTransport.d.ts.map +0 -1
  335. package/lib/rendezvous/RendezvousTransport.js +0 -1
  336. package/lib/rendezvous/RendezvousTransport.js.map +0 -1
  337. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts +0 -58
  338. package/lib/rendezvous/channels/MSC4108SecureChannel.d.ts.map +0 -1
  339. package/lib/rendezvous/channels/MSC4108SecureChannel.js +0 -246
  340. package/lib/rendezvous/channels/MSC4108SecureChannel.js.map +0 -1
  341. package/lib/rendezvous/channels/index.d.ts +0 -2
  342. package/lib/rendezvous/channels/index.d.ts.map +0 -1
  343. package/lib/rendezvous/channels/index.js +0 -18
  344. package/lib/rendezvous/channels/index.js.map +0 -1
  345. package/lib/rendezvous/index.d.ts +0 -10
  346. package/lib/rendezvous/index.d.ts.map +0 -1
  347. package/lib/rendezvous/index.js +0 -23
  348. package/lib/rendezvous/index.js.map +0 -1
  349. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts +0 -61
  350. package/lib/rendezvous/transports/MSC4108RendezvousSession.d.ts.map +0 -1
  351. package/lib/rendezvous/transports/MSC4108RendezvousSession.js +0 -253
  352. package/lib/rendezvous/transports/MSC4108RendezvousSession.js.map +0 -1
  353. package/lib/rendezvous/transports/index.d.ts +0 -2
  354. package/lib/rendezvous/transports/index.d.ts.map +0 -1
  355. package/lib/rendezvous/transports/index.js +0 -18
  356. package/lib/rendezvous/transports/index.js.map +0 -1
  357. package/lib/rust-crypto/CrossSigningIdentity.d.ts +0 -33
  358. package/lib/rust-crypto/CrossSigningIdentity.d.ts.map +0 -1
  359. package/lib/rust-crypto/CrossSigningIdentity.js +0 -157
  360. package/lib/rust-crypto/CrossSigningIdentity.js.map +0 -1
  361. package/lib/rust-crypto/DehydratedDeviceManager.d.ts +0 -98
  362. package/lib/rust-crypto/DehydratedDeviceManager.d.ts.map +0 -1
  363. package/lib/rust-crypto/DehydratedDeviceManager.js +0 -285
  364. package/lib/rust-crypto/DehydratedDeviceManager.js.map +0 -1
  365. package/lib/rust-crypto/KeyClaimManager.d.ts +0 -33
  366. package/lib/rust-crypto/KeyClaimManager.d.ts.map +0 -1
  367. package/lib/rust-crypto/KeyClaimManager.js +0 -82
  368. package/lib/rust-crypto/KeyClaimManager.js.map +0 -1
  369. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts +0 -43
  370. package/lib/rust-crypto/OutgoingRequestProcessor.d.ts.map +0 -1
  371. package/lib/rust-crypto/OutgoingRequestProcessor.js +0 -195
  372. package/lib/rust-crypto/OutgoingRequestProcessor.js.map +0 -1
  373. package/lib/rust-crypto/OutgoingRequestsManager.d.ts +0 -47
  374. package/lib/rust-crypto/OutgoingRequestsManager.d.ts.map +0 -1
  375. package/lib/rust-crypto/OutgoingRequestsManager.js +0 -148
  376. package/lib/rust-crypto/OutgoingRequestsManager.js.map +0 -1
  377. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts +0 -120
  378. package/lib/rust-crypto/PerSessionKeyBackupDownloader.d.ts.map +0 -1
  379. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js +0 -467
  380. package/lib/rust-crypto/PerSessionKeyBackupDownloader.js.map +0 -1
  381. package/lib/rust-crypto/RoomEncryptor.d.ts +0 -98
  382. package/lib/rust-crypto/RoomEncryptor.d.ts.map +0 -1
  383. package/lib/rust-crypto/RoomEncryptor.js +0 -299
  384. package/lib/rust-crypto/RoomEncryptor.js.map +0 -1
  385. package/lib/rust-crypto/backup.d.ts +0 -254
  386. package/lib/rust-crypto/backup.d.ts.map +0 -1
  387. package/lib/rust-crypto/backup.js +0 -837
  388. package/lib/rust-crypto/backup.js.map +0 -1
  389. package/lib/rust-crypto/constants.d.ts +0 -3
  390. package/lib/rust-crypto/constants.d.ts.map +0 -1
  391. package/lib/rust-crypto/constants.js +0 -19
  392. package/lib/rust-crypto/constants.js.map +0 -1
  393. package/lib/rust-crypto/device-converter.d.ts +0 -28
  394. package/lib/rust-crypto/device-converter.d.ts.map +0 -1
  395. package/lib/rust-crypto/device-converter.js +0 -123
  396. package/lib/rust-crypto/device-converter.js.map +0 -1
  397. package/lib/rust-crypto/index.d.ts +0 -61
  398. package/lib/rust-crypto/index.d.ts.map +0 -1
  399. package/lib/rust-crypto/index.js +0 -152
  400. package/lib/rust-crypto/index.js.map +0 -1
  401. package/lib/rust-crypto/libolm_migration.d.ts +0 -81
  402. package/lib/rust-crypto/libolm_migration.d.ts.map +0 -1
  403. package/lib/rust-crypto/libolm_migration.js +0 -459
  404. package/lib/rust-crypto/libolm_migration.js.map +0 -1
  405. package/lib/rust-crypto/rust-crypto.d.ts +0 -556
  406. package/lib/rust-crypto/rust-crypto.d.ts.map +0 -1
  407. package/lib/rust-crypto/rust-crypto.js +0 -2016
  408. package/lib/rust-crypto/rust-crypto.js.map +0 -1
  409. package/lib/rust-crypto/secret-storage.d.ts +0 -22
  410. package/lib/rust-crypto/secret-storage.d.ts.map +0 -1
  411. package/lib/rust-crypto/secret-storage.js +0 -63
  412. package/lib/rust-crypto/secret-storage.js.map +0 -1
  413. package/lib/rust-crypto/verification.d.ts +0 -319
  414. package/lib/rust-crypto/verification.d.ts.map +0 -1
  415. package/lib/rust-crypto/verification.js +0 -816
  416. package/lib/rust-crypto/verification.js.map +0 -1
  417. package/lib/secret-storage.d.ts +0 -370
  418. package/lib/secret-storage.d.ts.map +0 -1
  419. package/lib/secret-storage.js +0 -466
  420. package/lib/secret-storage.js.map +0 -1
  421. package/lib/webrtc/audioContext.d.ts +0 -15
  422. package/lib/webrtc/audioContext.d.ts.map +0 -1
  423. package/lib/webrtc/audioContext.js +0 -46
  424. package/lib/webrtc/audioContext.js.map +0 -1
  425. package/lib/webrtc/call.d.ts +0 -560
  426. package/lib/webrtc/call.d.ts.map +0 -1
  427. package/lib/webrtc/call.js +0 -2541
  428. package/lib/webrtc/call.js.map +0 -1
  429. package/lib/webrtc/callEventHandler.d.ts +0 -37
  430. package/lib/webrtc/callEventHandler.d.ts.map +0 -1
  431. package/lib/webrtc/callEventHandler.js +0 -344
  432. package/lib/webrtc/callEventHandler.js.map +0 -1
  433. package/lib/webrtc/callEventTypes.d.ts +0 -73
  434. package/lib/webrtc/callEventTypes.d.ts.map +0 -1
  435. package/lib/webrtc/callEventTypes.js +0 -13
  436. package/lib/webrtc/callEventTypes.js.map +0 -1
  437. package/lib/webrtc/callFeed.d.ts +0 -128
  438. package/lib/webrtc/callFeed.d.ts.map +0 -1
  439. package/lib/webrtc/callFeed.js +0 -289
  440. package/lib/webrtc/callFeed.js.map +0 -1
  441. package/lib/webrtc/groupCall.d.ts +0 -323
  442. package/lib/webrtc/groupCall.d.ts.map +0 -1
  443. package/lib/webrtc/groupCall.js +0 -1337
  444. package/lib/webrtc/groupCall.js.map +0 -1
  445. package/lib/webrtc/groupCallEventHandler.d.ts +0 -31
  446. package/lib/webrtc/groupCallEventHandler.d.ts.map +0 -1
  447. package/lib/webrtc/groupCallEventHandler.js +0 -178
  448. package/lib/webrtc/groupCallEventHandler.js.map +0 -1
  449. package/lib/webrtc/mediaHandler.d.ts +0 -89
  450. package/lib/webrtc/mediaHandler.d.ts.map +0 -1
  451. package/lib/webrtc/mediaHandler.js +0 -437
  452. package/lib/webrtc/mediaHandler.js.map +0 -1
  453. package/lib/webrtc/stats/callFeedStatsReporter.d.ts +0 -8
  454. package/lib/webrtc/stats/callFeedStatsReporter.d.ts.map +0 -1
  455. package/lib/webrtc/stats/callFeedStatsReporter.js +0 -82
  456. package/lib/webrtc/stats/callFeedStatsReporter.js.map +0 -1
  457. package/lib/webrtc/stats/callStatsReportGatherer.d.ts +0 -25
  458. package/lib/webrtc/stats/callStatsReportGatherer.d.ts.map +0 -1
  459. package/lib/webrtc/stats/callStatsReportGatherer.js +0 -199
  460. package/lib/webrtc/stats/callStatsReportGatherer.js.map +0 -1
  461. package/lib/webrtc/stats/callStatsReportSummary.d.ts +0 -17
  462. package/lib/webrtc/stats/callStatsReportSummary.d.ts.map +0 -1
  463. package/lib/webrtc/stats/callStatsReportSummary.js +0 -1
  464. package/lib/webrtc/stats/callStatsReportSummary.js.map +0 -1
  465. package/lib/webrtc/stats/connectionStats.d.ts +0 -28
  466. package/lib/webrtc/stats/connectionStats.d.ts.map +0 -1
  467. package/lib/webrtc/stats/connectionStats.js +0 -26
  468. package/lib/webrtc/stats/connectionStats.js.map +0 -1
  469. package/lib/webrtc/stats/connectionStatsBuilder.d.ts +0 -5
  470. package/lib/webrtc/stats/connectionStatsBuilder.d.ts.map +0 -1
  471. package/lib/webrtc/stats/connectionStatsBuilder.js +0 -27
  472. package/lib/webrtc/stats/connectionStatsBuilder.js.map +0 -1
  473. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts +0 -7
  474. package/lib/webrtc/stats/connectionStatsReportBuilder.d.ts.map +0 -1
  475. package/lib/webrtc/stats/connectionStatsReportBuilder.js +0 -121
  476. package/lib/webrtc/stats/connectionStatsReportBuilder.js.map +0 -1
  477. package/lib/webrtc/stats/groupCallStats.d.ts +0 -22
  478. package/lib/webrtc/stats/groupCallStats.d.ts.map +0 -1
  479. package/lib/webrtc/stats/groupCallStats.js +0 -78
  480. package/lib/webrtc/stats/groupCallStats.js.map +0 -1
  481. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts +0 -10
  482. package/lib/webrtc/stats/media/mediaSsrcHandler.d.ts.map +0 -1
  483. package/lib/webrtc/stats/media/mediaSsrcHandler.js +0 -57
  484. package/lib/webrtc/stats/media/mediaSsrcHandler.js.map +0 -1
  485. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts +0 -12
  486. package/lib/webrtc/stats/media/mediaTrackHandler.d.ts.map +0 -1
  487. package/lib/webrtc/stats/media/mediaTrackHandler.js +0 -62
  488. package/lib/webrtc/stats/media/mediaTrackHandler.js.map +0 -1
  489. package/lib/webrtc/stats/media/mediaTrackStats.d.ts +0 -86
  490. package/lib/webrtc/stats/media/mediaTrackStats.d.ts.map +0 -1
  491. package/lib/webrtc/stats/media/mediaTrackStats.js +0 -142
  492. package/lib/webrtc/stats/media/mediaTrackStats.js.map +0 -1
  493. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts +0 -22
  494. package/lib/webrtc/stats/media/mediaTrackStatsHandler.d.ts.map +0 -1
  495. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js +0 -76
  496. package/lib/webrtc/stats/media/mediaTrackStatsHandler.js.map +0 -1
  497. package/lib/webrtc/stats/statsReport.d.ts +0 -99
  498. package/lib/webrtc/stats/statsReport.d.ts.map +0 -1
  499. package/lib/webrtc/stats/statsReport.js +0 -32
  500. package/lib/webrtc/stats/statsReport.js.map +0 -1
  501. package/lib/webrtc/stats/statsReportEmitter.d.ts +0 -15
  502. package/lib/webrtc/stats/statsReportEmitter.d.ts.map +0 -1
  503. package/lib/webrtc/stats/statsReportEmitter.js +0 -33
  504. package/lib/webrtc/stats/statsReportEmitter.js.map +0 -1
  505. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts +0 -16
  506. package/lib/webrtc/stats/summaryStatsReportGatherer.d.ts.map +0 -1
  507. package/lib/webrtc/stats/summaryStatsReportGatherer.js +0 -116
  508. package/lib/webrtc/stats/summaryStatsReportGatherer.js.map +0 -1
  509. package/lib/webrtc/stats/trackStatsBuilder.d.ts +0 -19
  510. package/lib/webrtc/stats/trackStatsBuilder.d.ts.map +0 -1
  511. package/lib/webrtc/stats/trackStatsBuilder.js +0 -168
  512. package/lib/webrtc/stats/trackStatsBuilder.js.map +0 -1
  513. package/lib/webrtc/stats/transportStats.d.ts +0 -11
  514. package/lib/webrtc/stats/transportStats.d.ts.map +0 -1
  515. package/lib/webrtc/stats/transportStats.js +0 -1
  516. package/lib/webrtc/stats/transportStats.js.map +0 -1
  517. package/lib/webrtc/stats/transportStatsBuilder.d.ts +0 -5
  518. package/lib/webrtc/stats/transportStatsBuilder.d.ts.map +0 -1
  519. package/lib/webrtc/stats/transportStatsBuilder.js +0 -34
  520. package/lib/webrtc/stats/transportStatsBuilder.js.map +0 -1
  521. package/lib/webrtc/stats/valueFormatter.d.ts +0 -4
  522. package/lib/webrtc/stats/valueFormatter.d.ts.map +0 -1
  523. package/lib/webrtc/stats/valueFormatter.js +0 -25
  524. package/lib/webrtc/stats/valueFormatter.js.map +0 -1
  525. package/src/@types/crypto.ts +0 -73
  526. package/src/@types/matrix-sdk-crypto-wasm.d.ts +0 -44
  527. package/src/common-crypto/CryptoBackend.ts +0 -302
  528. package/src/common-crypto/README.md +0 -4
  529. package/src/common-crypto/key-passphrase.ts +0 -43
  530. package/src/crypto/CrossSigning.ts +0 -773
  531. package/src/crypto/DeviceList.ts +0 -989
  532. package/src/crypto/EncryptionSetup.ts +0 -351
  533. package/src/crypto/OlmDevice.ts +0 -1500
  534. package/src/crypto/OutgoingRoomKeyRequestManager.ts +0 -485
  535. package/src/crypto/RoomList.ts +0 -70
  536. package/src/crypto/SecretSharing.ts +0 -240
  537. package/src/crypto/SecretStorage.ts +0 -136
  538. package/src/crypto/aes.ts +0 -23
  539. package/src/crypto/algorithms/base.ts +0 -236
  540. package/src/crypto/algorithms/index.ts +0 -20
  541. package/src/crypto/algorithms/megolm.ts +0 -2216
  542. package/src/crypto/algorithms/olm.ts +0 -381
  543. package/src/crypto/api.ts +0 -70
  544. package/src/crypto/backup.ts +0 -922
  545. package/src/crypto/crypto.ts +0 -18
  546. package/src/crypto/dehydration.ts +0 -272
  547. package/src/crypto/device-converter.ts +0 -45
  548. package/src/crypto/deviceinfo.ts +0 -158
  549. package/src/crypto/index.ts +0 -4414
  550. package/src/crypto/key_passphrase.ts +0 -42
  551. package/src/crypto/keybackup.ts +0 -47
  552. package/src/crypto/olmlib.ts +0 -539
  553. package/src/crypto/recoverykey.ts +0 -18
  554. package/src/crypto/store/base.ts +0 -348
  555. package/src/crypto/store/indexeddb-crypto-store-backend.ts +0 -1250
  556. package/src/crypto/store/indexeddb-crypto-store.ts +0 -845
  557. package/src/crypto/store/localStorage-crypto-store.ts +0 -579
  558. package/src/crypto/store/memory-crypto-store.ts +0 -680
  559. package/src/crypto/verification/Base.ts +0 -409
  560. package/src/crypto/verification/Error.ts +0 -76
  561. package/src/crypto/verification/IllegalMethod.ts +0 -50
  562. package/src/crypto/verification/QRCode.ts +0 -310
  563. package/src/crypto/verification/SAS.ts +0 -494
  564. package/src/crypto/verification/SASDecimal.ts +0 -37
  565. package/src/crypto/verification/request/Channel.ts +0 -34
  566. package/src/crypto/verification/request/InRoomChannel.ts +0 -371
  567. package/src/crypto/verification/request/ToDeviceChannel.ts +0 -354
  568. package/src/crypto/verification/request/VerificationRequest.ts +0 -976
  569. package/src/crypto-api/CryptoEvent.ts +0 -93
  570. package/src/crypto-api/CryptoEventHandlerMap.ts +0 -32
  571. package/src/crypto-api/index.ts +0 -1175
  572. package/src/crypto-api/key-passphrase.ts +0 -58
  573. package/src/crypto-api/keybackup.ts +0 -115
  574. package/src/crypto-api/recovery-key.ts +0 -69
  575. package/src/crypto-api/verification.ts +0 -408
  576. package/src/matrixrtc/CallMembership.ts +0 -247
  577. package/src/matrixrtc/LivekitFocus.ts +0 -39
  578. package/src/matrixrtc/MatrixRTCSession.ts +0 -1319
  579. package/src/matrixrtc/MatrixRTCSessionManager.ts +0 -166
  580. package/src/matrixrtc/focus.ts +0 -25
  581. package/src/matrixrtc/index.ts +0 -22
  582. package/src/matrixrtc/types.ts +0 -36
  583. package/src/rendezvous/MSC4108SignInWithQR.ts +0 -444
  584. package/src/rendezvous/RendezvousChannel.ts +0 -48
  585. package/src/rendezvous/RendezvousCode.ts +0 -25
  586. package/src/rendezvous/RendezvousError.ts +0 -26
  587. package/src/rendezvous/RendezvousFailureReason.ts +0 -49
  588. package/src/rendezvous/RendezvousIntent.ts +0 -20
  589. package/src/rendezvous/RendezvousTransport.ts +0 -58
  590. package/src/rendezvous/channels/MSC4108SecureChannel.ts +0 -270
  591. package/src/rendezvous/channels/index.ts +0 -17
  592. package/src/rendezvous/index.ts +0 -25
  593. package/src/rendezvous/transports/MSC4108RendezvousSession.ts +0 -270
  594. package/src/rendezvous/transports/index.ts +0 -17
  595. package/src/rust-crypto/CrossSigningIdentity.ts +0 -183
  596. package/src/rust-crypto/DehydratedDeviceManager.ts +0 -306
  597. package/src/rust-crypto/KeyClaimManager.ts +0 -86
  598. package/src/rust-crypto/OutgoingRequestProcessor.ts +0 -236
  599. package/src/rust-crypto/OutgoingRequestsManager.ts +0 -143
  600. package/src/rust-crypto/PerSessionKeyBackupDownloader.ts +0 -501
  601. package/src/rust-crypto/RoomEncryptor.ts +0 -352
  602. package/src/rust-crypto/backup.ts +0 -881
  603. package/src/rust-crypto/constants.ts +0 -18
  604. package/src/rust-crypto/device-converter.ts +0 -128
  605. package/src/rust-crypto/index.ts +0 -237
  606. package/src/rust-crypto/libolm_migration.ts +0 -530
  607. package/src/rust-crypto/rust-crypto.ts +0 -2205
  608. package/src/rust-crypto/secret-storage.ts +0 -60
  609. package/src/rust-crypto/verification.ts +0 -830
  610. package/src/secret-storage.ts +0 -693
  611. package/src/webrtc/audioContext.ts +0 -44
  612. package/src/webrtc/call.ts +0 -3074
  613. package/src/webrtc/callEventHandler.ts +0 -425
  614. package/src/webrtc/callEventTypes.ts +0 -93
  615. package/src/webrtc/callFeed.ts +0 -364
  616. package/src/webrtc/groupCall.ts +0 -1735
  617. package/src/webrtc/groupCallEventHandler.ts +0 -234
  618. package/src/webrtc/mediaHandler.ts +0 -484
  619. package/src/webrtc/stats/callFeedStatsReporter.ts +0 -94
  620. package/src/webrtc/stats/callStatsReportGatherer.ts +0 -219
  621. package/src/webrtc/stats/callStatsReportSummary.ts +0 -30
  622. package/src/webrtc/stats/connectionStats.ts +0 -47
  623. package/src/webrtc/stats/connectionStatsBuilder.ts +0 -28
  624. package/src/webrtc/stats/connectionStatsReportBuilder.ts +0 -140
  625. package/src/webrtc/stats/groupCallStats.ts +0 -93
  626. package/src/webrtc/stats/media/mediaSsrcHandler.ts +0 -57
  627. package/src/webrtc/stats/media/mediaTrackHandler.ts +0 -76
  628. package/src/webrtc/stats/media/mediaTrackStats.ts +0 -176
  629. package/src/webrtc/stats/media/mediaTrackStatsHandler.ts +0 -90
  630. package/src/webrtc/stats/statsReport.ts +0 -133
  631. package/src/webrtc/stats/statsReportEmitter.ts +0 -49
  632. package/src/webrtc/stats/summaryStatsReportGatherer.ts +0 -148
  633. package/src/webrtc/stats/trackStatsBuilder.ts +0 -207
  634. package/src/webrtc/stats/transportStats.ts +0 -26
  635. package/src/webrtc/stats/transportStatsBuilder.ts +0 -48
  636. package/src/webrtc/stats/valueFormatter.ts +0 -27
@@ -1,4414 +0,0 @@
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";