@jeongsu816/matrix-bot-sdk 0.0.0-stage → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -2
  3. package/lib/AdminApis.d.ts +52 -0
  4. package/lib/AdminApis.js +29 -0
  5. package/lib/AdminApis.js.map +1 -0
  6. package/lib/DMs.d.ts +48 -0
  7. package/lib/DMs.js +160 -0
  8. package/lib/DMs.js.map +1 -0
  9. package/lib/IFilter.d.ts +7 -0
  10. package/lib/IFilter.js +3 -0
  11. package/lib/IFilter.js.map +1 -0
  12. package/lib/MatrixAuth.d.ts +45 -0
  13. package/lib/MatrixAuth.js +125 -0
  14. package/lib/MatrixAuth.js.map +1 -0
  15. package/lib/MatrixClient.d.ts +834 -0
  16. package/lib/MatrixClient.js +2268 -0
  17. package/lib/MatrixClient.js.map +1 -0
  18. package/lib/PantalaimonClient.d.ts +42 -0
  19. package/lib/PantalaimonClient.js +44 -0
  20. package/lib/PantalaimonClient.js.map +1 -0
  21. package/lib/SynapseAdminApis.d.ts +350 -0
  22. package/lib/SynapseAdminApis.js +244 -0
  23. package/lib/SynapseAdminApis.js.map +1 -0
  24. package/lib/SynchronousMatrixClient.d.ts +82 -0
  25. package/lib/SynchronousMatrixClient.js +130 -0
  26. package/lib/SynchronousMatrixClient.js.map +1 -0
  27. package/lib/UnstableApis.d.ts +43 -0
  28. package/lib/UnstableApis.js +74 -0
  29. package/lib/UnstableApis.js.map +1 -0
  30. package/lib/appservice/Appservice.d.ts +328 -0
  31. package/lib/appservice/Appservice.js +803 -0
  32. package/lib/appservice/Appservice.js.map +1 -0
  33. package/lib/appservice/Intent.d.ts +113 -0
  34. package/lib/appservice/Intent.js +363 -0
  35. package/lib/appservice/Intent.js.map +1 -0
  36. package/lib/appservice/MatrixBridge.d.ts +85 -0
  37. package/lib/appservice/MatrixBridge.js +111 -0
  38. package/lib/appservice/MatrixBridge.js.map +1 -0
  39. package/lib/appservice/UnstableAppserviceApis.d.ts +41 -0
  40. package/lib/appservice/UnstableAppserviceApis.js +62 -0
  41. package/lib/appservice/UnstableAppserviceApis.js.map +1 -0
  42. package/lib/appservice/http_responses.d.ts +77 -0
  43. package/lib/appservice/http_responses.js +3 -0
  44. package/lib/appservice/http_responses.js.map +1 -0
  45. package/lib/b64.d.ts +42 -0
  46. package/lib/b64.js +68 -0
  47. package/lib/b64.js.map +1 -0
  48. package/lib/e2ee/CrossSigningBootstrap.d.ts +73 -0
  49. package/lib/e2ee/CrossSigningBootstrap.js +86 -0
  50. package/lib/e2ee/CrossSigningBootstrap.js.map +1 -0
  51. package/lib/e2ee/CryptoClient.d.ts +163 -0
  52. package/lib/e2ee/CryptoClient.js +382 -0
  53. package/lib/e2ee/CryptoClient.js.map +1 -0
  54. package/lib/e2ee/ICryptoRoomInformation.d.ts +8 -0
  55. package/lib/e2ee/ICryptoRoomInformation.js +3 -0
  56. package/lib/e2ee/ICryptoRoomInformation.js.map +1 -0
  57. package/lib/e2ee/RoomTracker.d.ts +41 -0
  58. package/lib/e2ee/RoomTracker.js +96 -0
  59. package/lib/e2ee/RoomTracker.js.map +1 -0
  60. package/lib/e2ee/RustEngine.d.ts +149 -0
  61. package/lib/e2ee/RustEngine.js +467 -0
  62. package/lib/e2ee/RustEngine.js.map +1 -0
  63. package/lib/e2ee/decorators.d.ts +10 -0
  64. package/lib/e2ee/decorators.js +37 -0
  65. package/lib/e2ee/decorators.js.map +1 -0
  66. package/lib/helpers/MatrixEntity.d.ts +37 -0
  67. package/lib/helpers/MatrixEntity.js +67 -0
  68. package/lib/helpers/MatrixEntity.js.map +1 -0
  69. package/lib/helpers/MatrixGlob.d.ts +22 -0
  70. package/lib/helpers/MatrixGlob.js +35 -0
  71. package/lib/helpers/MatrixGlob.js.map +1 -0
  72. package/lib/helpers/MentionPill.d.ts +40 -0
  73. package/lib/helpers/MentionPill.js +91 -0
  74. package/lib/helpers/MentionPill.js.map +1 -0
  75. package/lib/helpers/Permalinks.d.ts +58 -0
  76. package/lib/helpers/Permalinks.js +73 -0
  77. package/lib/helpers/Permalinks.js.map +1 -0
  78. package/lib/helpers/ProfileCache.d.ts +48 -0
  79. package/lib/helpers/ProfileCache.js +114 -0
  80. package/lib/helpers/ProfileCache.js.map +1 -0
  81. package/lib/helpers/RichReply.d.ts +17 -0
  82. package/lib/helpers/RichReply.js +47 -0
  83. package/lib/helpers/RichReply.js.map +1 -0
  84. package/lib/helpers/UnpaddedBase64.d.ts +31 -0
  85. package/lib/helpers/UnpaddedBase64.js +45 -0
  86. package/lib/helpers/UnpaddedBase64.js.map +1 -0
  87. package/lib/http.d.ts +17 -0
  88. package/lib/http.js +147 -0
  89. package/lib/http.js.map +1 -0
  90. package/lib/identity/IdentityClient.d.ts +93 -0
  91. package/lib/identity/IdentityClient.js +248 -0
  92. package/lib/identity/IdentityClient.js.map +1 -0
  93. package/lib/index.d.ts +91 -0
  94. package/lib/index.js +125 -0
  95. package/lib/index.js.map +1 -0
  96. package/lib/logging/ConsoleLogger.d.ts +12 -0
  97. package/lib/logging/ConsoleLogger.js +28 -0
  98. package/lib/logging/ConsoleLogger.js.map +1 -0
  99. package/lib/logging/ILogger.d.ts +36 -0
  100. package/lib/logging/ILogger.js +3 -0
  101. package/lib/logging/ILogger.js.map +1 -0
  102. package/lib/logging/LogService.d.ts +100 -0
  103. package/lib/logging/LogService.js +169 -0
  104. package/lib/logging/LogService.js.map +1 -0
  105. package/lib/logging/RichConsoleLogger.d.ts +20 -0
  106. package/lib/logging/RichConsoleLogger.js +41 -0
  107. package/lib/logging/RichConsoleLogger.js.map +1 -0
  108. package/lib/metrics/IMetricListener.d.ts +52 -0
  109. package/lib/metrics/IMetricListener.js +3 -0
  110. package/lib/metrics/IMetricListener.js.map +1 -0
  111. package/lib/metrics/Metrics.d.ts +66 -0
  112. package/lib/metrics/Metrics.js +128 -0
  113. package/lib/metrics/Metrics.js.map +1 -0
  114. package/lib/metrics/contexts.d.ts +51 -0
  115. package/lib/metrics/contexts.js +3 -0
  116. package/lib/metrics/contexts.js.map +1 -0
  117. package/lib/metrics/decorators.d.ts +15 -0
  118. package/lib/metrics/decorators.js +92 -0
  119. package/lib/metrics/decorators.js.map +1 -0
  120. package/lib/metrics/names.d.ts +45 -0
  121. package/lib/metrics/names.js +49 -0
  122. package/lib/metrics/names.js.map +1 -0
  123. package/lib/mixins/AutojoinRoomsMixin.d.ts +10 -0
  124. package/lib/mixins/AutojoinRoomsMixin.js +25 -0
  125. package/lib/mixins/AutojoinRoomsMixin.js.map +1 -0
  126. package/lib/mixins/AutojoinUpgradedRoomsMixin.d.ts +10 -0
  127. package/lib/mixins/AutojoinUpgradedRoomsMixin.js +40 -0
  128. package/lib/mixins/AutojoinUpgradedRoomsMixin.js.map +1 -0
  129. package/lib/models/Account.d.ts +8 -0
  130. package/lib/models/Account.js +3 -0
  131. package/lib/models/Account.js.map +1 -0
  132. package/lib/models/CreateRoom.d.ts +125 -0
  133. package/lib/models/CreateRoom.js +3 -0
  134. package/lib/models/CreateRoom.js.map +1 -0
  135. package/lib/models/Crypto.d.ts +167 -0
  136. package/lib/models/Crypto.js +31 -0
  137. package/lib/models/Crypto.js.map +1 -0
  138. package/lib/models/EventContext.d.ts +19 -0
  139. package/lib/models/EventContext.js +3 -0
  140. package/lib/models/EventContext.js.map +1 -0
  141. package/lib/models/IdentityServerModels.d.ts +20 -0
  142. package/lib/models/IdentityServerModels.js +3 -0
  143. package/lib/models/IdentityServerModels.js.map +1 -0
  144. package/lib/models/MSC2176.d.ts +42 -0
  145. package/lib/models/MSC2176.js +3 -0
  146. package/lib/models/MSC2176.js.map +1 -0
  147. package/lib/models/MatrixError.d.ts +38 -0
  148. package/lib/models/MatrixError.js +30 -0
  149. package/lib/models/MatrixError.js.map +1 -0
  150. package/lib/models/MatrixProfile.d.ts +43 -0
  151. package/lib/models/MatrixProfile.js +43 -0
  152. package/lib/models/MatrixProfile.js.map +1 -0
  153. package/lib/models/OpenIDConnect.d.ts +10 -0
  154. package/lib/models/OpenIDConnect.js +3 -0
  155. package/lib/models/OpenIDConnect.js.map +1 -0
  156. package/lib/models/Policies.d.ts +25 -0
  157. package/lib/models/Policies.js +3 -0
  158. package/lib/models/Policies.js.map +1 -0
  159. package/lib/models/PowerLevelAction.d.ts +27 -0
  160. package/lib/models/PowerLevelAction.js +32 -0
  161. package/lib/models/PowerLevelAction.js.map +1 -0
  162. package/lib/models/PowerLevelBounds.d.ts +15 -0
  163. package/lib/models/PowerLevelBounds.js +3 -0
  164. package/lib/models/PowerLevelBounds.js.map +1 -0
  165. package/lib/models/Presence.d.ts +25 -0
  166. package/lib/models/Presence.js +38 -0
  167. package/lib/models/Presence.js.map +1 -0
  168. package/lib/models/ServerVersions.d.ts +8 -0
  169. package/lib/models/ServerVersions.js +3 -0
  170. package/lib/models/ServerVersions.js.map +1 -0
  171. package/lib/models/Spaces.d.ts +116 -0
  172. package/lib/models/Spaces.js +91 -0
  173. package/lib/models/Spaces.js.map +1 -0
  174. package/lib/models/Threepid.d.ts +8 -0
  175. package/lib/models/Threepid.js +3 -0
  176. package/lib/models/Threepid.js.map +1 -0
  177. package/lib/models/events/AliasesEvent.d.ts +27 -0
  178. package/lib/models/events/AliasesEvent.js +27 -0
  179. package/lib/models/events/AliasesEvent.js.map +1 -0
  180. package/lib/models/events/CanonicalAliasEvent.d.ts +23 -0
  181. package/lib/models/events/CanonicalAliasEvent.js +21 -0
  182. package/lib/models/events/CanonicalAliasEvent.js.map +1 -0
  183. package/lib/models/events/CreateEvent.d.ts +63 -0
  184. package/lib/models/events/CreateEvent.js +33 -0
  185. package/lib/models/events/CreateEvent.js.map +1 -0
  186. package/lib/models/events/EncryptedRoomEvent.d.ts +42 -0
  187. package/lib/models/events/EncryptedRoomEvent.js +28 -0
  188. package/lib/models/events/EncryptedRoomEvent.js.map +1 -0
  189. package/lib/models/events/EncryptionEvent.d.ts +47 -0
  190. package/lib/models/events/EncryptionEvent.js +42 -0
  191. package/lib/models/events/EncryptionEvent.js.map +1 -0
  192. package/lib/models/events/Event.d.ts +26 -0
  193. package/lib/models/events/Event.js +40 -0
  194. package/lib/models/events/Event.js.map +1 -0
  195. package/lib/models/events/EventKind.d.ts +15 -0
  196. package/lib/models/events/EventKind.js +20 -0
  197. package/lib/models/events/EventKind.js.map +1 -0
  198. package/lib/models/events/InvalidEventError.d.ts +14 -0
  199. package/lib/models/events/InvalidEventError.js +24 -0
  200. package/lib/models/events/InvalidEventError.js.map +1 -0
  201. package/lib/models/events/JoinRulesEvent.d.ts +29 -0
  202. package/lib/models/events/JoinRulesEvent.js +21 -0
  203. package/lib/models/events/JoinRulesEvent.js.map +1 -0
  204. package/lib/models/events/MembershipEvent.d.ts +54 -0
  205. package/lib/models/events/MembershipEvent.js +49 -0
  206. package/lib/models/events/MembershipEvent.js.map +1 -0
  207. package/lib/models/events/MessageEvent.d.ts +233 -0
  208. package/lib/models/events/MessageEvent.js +49 -0
  209. package/lib/models/events/MessageEvent.js.map +1 -0
  210. package/lib/models/events/PinnedEventsEvent.d.ts +23 -0
  211. package/lib/models/events/PinnedEventsEvent.js +21 -0
  212. package/lib/models/events/PinnedEventsEvent.js.map +1 -0
  213. package/lib/models/events/PowerLevelsEvent.d.ts +96 -0
  214. package/lib/models/events/PowerLevelsEvent.js +70 -0
  215. package/lib/models/events/PowerLevelsEvent.js.map +1 -0
  216. package/lib/models/events/PresenceEvent.d.ts +56 -0
  217. package/lib/models/events/PresenceEvent.js +21 -0
  218. package/lib/models/events/PresenceEvent.js.map +1 -0
  219. package/lib/models/events/RedactionEvent.d.ts +28 -0
  220. package/lib/models/events/RedactionEvent.js +36 -0
  221. package/lib/models/events/RedactionEvent.js.map +1 -0
  222. package/lib/models/events/RoomAvatarEvent.d.ts +28 -0
  223. package/lib/models/events/RoomAvatarEvent.js +21 -0
  224. package/lib/models/events/RoomAvatarEvent.js.map +1 -0
  225. package/lib/models/events/RoomEvent.d.ts +58 -0
  226. package/lib/models/events/RoomEvent.js +57 -0
  227. package/lib/models/events/RoomEvent.js.map +1 -0
  228. package/lib/models/events/RoomNameEvent.d.ts +20 -0
  229. package/lib/models/events/RoomNameEvent.js +21 -0
  230. package/lib/models/events/RoomNameEvent.js.map +1 -0
  231. package/lib/models/events/RoomTopicEvent.d.ts +20 -0
  232. package/lib/models/events/RoomTopicEvent.js +21 -0
  233. package/lib/models/events/RoomTopicEvent.js.map +1 -0
  234. package/lib/models/events/SpaceChildEvent.d.ts +53 -0
  235. package/lib/models/events/SpaceChildEvent.js +49 -0
  236. package/lib/models/events/SpaceChildEvent.js.map +1 -0
  237. package/lib/models/events/converter.d.ts +8 -0
  238. package/lib/models/events/converter.js +50 -0
  239. package/lib/models/events/converter.js.map +1 -0
  240. package/lib/models/unstable/MediaInfo.d.ts +16 -0
  241. package/lib/models/unstable/MediaInfo.js +3 -0
  242. package/lib/models/unstable/MediaInfo.js.map +1 -0
  243. package/lib/preprocessors/IPreprocessor.d.ts +22 -0
  244. package/lib/preprocessors/IPreprocessor.js +3 -0
  245. package/lib/preprocessors/IPreprocessor.js.map +1 -0
  246. package/lib/preprocessors/RichRepliesPreprocessor.d.ts +67 -0
  247. package/lib/preprocessors/RichRepliesPreprocessor.js +112 -0
  248. package/lib/preprocessors/RichRepliesPreprocessor.js.map +1 -0
  249. package/lib/request.d.ts +13 -0
  250. package/lib/request.js +23 -0
  251. package/lib/request.js.map +1 -0
  252. package/lib/simple-validation.d.ts +10 -0
  253. package/lib/simple-validation.js +30 -0
  254. package/lib/simple-validation.js.map +1 -0
  255. package/lib/storage/IAppserviceStorageProvider.d.ts +48 -0
  256. package/lib/storage/IAppserviceStorageProvider.js +3 -0
  257. package/lib/storage/IAppserviceStorageProvider.js.map +1 -0
  258. package/lib/storage/ICryptoStorageProvider.d.ts +32 -0
  259. package/lib/storage/ICryptoStorageProvider.js +3 -0
  260. package/lib/storage/ICryptoStorageProvider.js.map +1 -0
  261. package/lib/storage/IStorageProvider.d.ts +47 -0
  262. package/lib/storage/IStorageProvider.js +3 -0
  263. package/lib/storage/IStorageProvider.js.map +1 -0
  264. package/lib/storage/MemoryStorageProvider.d.ts +26 -0
  265. package/lib/storage/MemoryStorageProvider.js +55 -0
  266. package/lib/storage/MemoryStorageProvider.js.map +1 -0
  267. package/lib/storage/RustSdkCryptoStorageProvider.d.ts +38 -0
  268. package/lib/storage/RustSdkCryptoStorageProvider.js +69 -0
  269. package/lib/storage/RustSdkCryptoStorageProvider.js.map +1 -0
  270. package/lib/storage/SimpleFsStorageProvider.d.ts +31 -0
  271. package/lib/storage/SimpleFsStorageProvider.js +122 -0
  272. package/lib/storage/SimpleFsStorageProvider.js.map +1 -0
  273. package/lib/storage/SimplePostgresStorageProvider.d.ts +32 -0
  274. package/lib/storage/SimplePostgresStorageProvider.js +148 -0
  275. package/lib/storage/SimplePostgresStorageProvider.js.map +1 -0
  276. package/lib/strategies/AppserviceJoinRoomStrategy.d.ts +14 -0
  277. package/lib/strategies/AppserviceJoinRoomStrategy.js +54 -0
  278. package/lib/strategies/AppserviceJoinRoomStrategy.js.map +1 -0
  279. package/lib/strategies/JoinRoomStrategy.d.ts +11 -0
  280. package/lib/strategies/JoinRoomStrategy.js +44 -0
  281. package/lib/strategies/JoinRoomStrategy.js.map +1 -0
  282. package/package.json +136 -4
  283. package/src/AdminApis.ts +60 -0
  284. package/src/DMs.ts +164 -0
  285. package/src/IFilter.ts +7 -0
  286. package/src/MatrixAuth.ts +129 -0
  287. package/src/MatrixClient.ts +2046 -0
  288. package/src/PantalaimonClient.ts +62 -0
  289. package/src/SynapseAdminApis.ts +482 -0
  290. package/src/SynchronousMatrixClient.ts +129 -0
  291. package/src/UnstableApis.ts +75 -0
  292. package/src/appservice/Appservice.ts +1089 -0
  293. package/src/appservice/Intent.ts +342 -0
  294. package/src/appservice/MatrixBridge.ts +139 -0
  295. package/src/appservice/UnstableAppserviceApis.ts +63 -0
  296. package/src/appservice/http_responses.ts +77 -0
  297. package/src/b64.ts +63 -0
  298. package/src/e2ee/CrossSigningBootstrap.ts +141 -0
  299. package/src/e2ee/CryptoClient.ts +378 -0
  300. package/src/e2ee/ICryptoRoomInformation.ts +9 -0
  301. package/src/e2ee/RoomTracker.ts +99 -0
  302. package/src/e2ee/RustEngine.ts +580 -0
  303. package/src/e2ee/decorators.ts +38 -0
  304. package/src/helpers/MatrixEntity.ts +65 -0
  305. package/src/helpers/MatrixGlob.ts +38 -0
  306. package/src/helpers/MentionPill.ts +95 -0
  307. package/src/helpers/Permalinks.ts +101 -0
  308. package/src/helpers/ProfileCache.ts +119 -0
  309. package/src/helpers/RichReply.ts +46 -0
  310. package/src/helpers/UnpaddedBase64.ts +44 -0
  311. package/src/http.ts +166 -0
  312. package/src/identity/IdentityClient.ts +225 -0
  313. package/src/index.ts +125 -0
  314. package/src/logging/ConsoleLogger.ts +31 -0
  315. package/src/logging/ILogger.ts +40 -0
  316. package/src/logging/LogService.ts +167 -0
  317. package/src/logging/RichConsoleLogger.ts +70 -0
  318. package/src/metrics/IMetricListener.ts +57 -0
  319. package/src/metrics/Metrics.ts +134 -0
  320. package/src/metrics/contexts.ts +56 -0
  321. package/src/metrics/decorators.ts +94 -0
  322. package/src/metrics/names.ts +53 -0
  323. package/src/mixins/AutojoinRoomsMixin.ts +24 -0
  324. package/src/mixins/AutojoinUpgradedRoomsMixin.ts +37 -0
  325. package/src/models/Account.ts +8 -0
  326. package/src/models/CreateRoom.ts +144 -0
  327. package/src/models/Crypto.ts +188 -0
  328. package/src/models/EventContext.ts +23 -0
  329. package/src/models/IdentityServerModels.ts +18 -0
  330. package/src/models/MSC2176.ts +47 -0
  331. package/src/models/MatrixError.ts +39 -0
  332. package/src/models/MatrixProfile.ts +55 -0
  333. package/src/models/OpenIDConnect.ts +10 -0
  334. package/src/models/Policies.ts +28 -0
  335. package/src/models/PowerLevelAction.ts +31 -0
  336. package/src/models/PowerLevelBounds.ts +16 -0
  337. package/src/models/Presence.ts +38 -0
  338. package/src/models/ServerVersions.ts +8 -0
  339. package/src/models/Spaces.ts +167 -0
  340. package/src/models/Threepid.ts +8 -0
  341. package/src/models/events/AliasesEvent.ts +37 -0
  342. package/src/models/events/CanonicalAliasEvent.ts +30 -0
  343. package/src/models/events/CreateEvent.ts +82 -0
  344. package/src/models/events/EncryptedRoomEvent.ts +58 -0
  345. package/src/models/events/EncryptionEvent.ts +63 -0
  346. package/src/models/events/Event.ts +38 -0
  347. package/src/models/events/EventKind.ts +16 -0
  348. package/src/models/events/InvalidEventError.ts +19 -0
  349. package/src/models/events/JoinRulesEvent.ts +37 -0
  350. package/src/models/events/MembershipEvent.ts +77 -0
  351. package/src/models/events/MessageEvent.ts +292 -0
  352. package/src/models/events/PinnedEventsEvent.ts +30 -0
  353. package/src/models/events/PowerLevelsEvent.ts +135 -0
  354. package/src/models/events/PresenceEvent.ts +69 -0
  355. package/src/models/events/RedactionEvent.ts +44 -0
  356. package/src/models/events/RoomAvatarEvent.ts +36 -0
  357. package/src/models/events/RoomEvent.ts +83 -0
  358. package/src/models/events/RoomNameEvent.ts +27 -0
  359. package/src/models/events/RoomTopicEvent.ts +27 -0
  360. package/src/models/events/SpaceChildEvent.ts +74 -0
  361. package/src/models/events/_MissingEvents.md +35 -0
  362. package/src/models/events/converter.ts +47 -0
  363. package/src/models/unstable/MediaInfo.ts +16 -0
  364. package/src/preprocessors/IPreprocessor.ts +24 -0
  365. package/src/preprocessors/RichRepliesPreprocessor.ts +159 -0
  366. package/src/request.ts +21 -0
  367. package/src/simple-validation.ts +30 -0
  368. package/src/storage/IAppserviceStorageProvider.ts +54 -0
  369. package/src/storage/ICryptoStorageProvider.ts +36 -0
  370. package/src/storage/IStorageProvider.ts +53 -0
  371. package/src/storage/MemoryStorageProvider.ts +65 -0
  372. package/src/storage/RustSdkCryptoStorageProvider.ts +81 -0
  373. package/src/storage/SimpleFsStorageProvider.ts +142 -0
  374. package/src/storage/SimplePostgresStorageProvider.ts +168 -0
  375. package/src/strategies/AppserviceJoinRoomStrategy.ts +47 -0
  376. package/src/strategies/JoinRoomStrategy.ts +45 -0
  377. package/tsconfig.json +22 -0
@@ -0,0 +1,580 @@
1
+ import {
2
+ EncryptionSettings,
3
+ KeysClaimRequest,
4
+ OlmMachine,
5
+ RequestType,
6
+ RoomId,
7
+ UserId,
8
+ EncryptionAlgorithm as RustEncryptionAlgorithm,
9
+ HistoryVisibility,
10
+ KeysUploadRequest,
11
+ KeysQueryRequest,
12
+ ToDeviceRequest,
13
+ SignatureUploadRequest,
14
+ KeysBackupRequest,
15
+ SecretStorageKey,
16
+ SecretStorageItems,
17
+ BackupDecryptionKey,
18
+ } from "@matrix-org/matrix-sdk-crypto-nodejs";
19
+
20
+ /**
21
+ * The subset of OlmMachine APIs available in @matrix-org/matrix-sdk-crypto-nodejs >= 0.6.0
22
+ * for 4S/SSSS secret storage export and import. Typed separately to provide a clear
23
+ * error when older bindings are used.
24
+ * @internal
25
+ */
26
+ interface OlmMachineWith4S extends OlmMachine {
27
+ exportSecretsForSecretStorage(key: SecretStorageKey): Promise<SecretStorageItems>;
28
+ importSecretsFromSecretStorage(key: SecretStorageKey, items: SecretStorageItems): Promise<SignatureUploadRequest>;
29
+ }
30
+ import * as AsyncLock from "async-lock";
31
+
32
+ import { MatrixClient } from "../MatrixClient";
33
+ import { ICryptoRoomInformation } from "./ICryptoRoomInformation";
34
+ import { EncryptionAlgorithm } from "../models/Crypto";
35
+ import { EncryptionEvent } from "../models/events/EncryptionEvent";
36
+
37
+ /**
38
+ * @internal
39
+ */
40
+ export const SYNC_LOCK_NAME = "sync";
41
+
42
+ /**
43
+ * Callback invoked to satisfy a User-Interactive Authentication (UIA) challenge
44
+ * when uploading cross-signing keys. Given the `flows`/`params`/`session` from a
45
+ * 401 response, it must return the `auth` dict to retry with (e.g. an
46
+ * `m.login.password` stage), or `null` to give up. May be called more than once
47
+ * if the server requires multiple stages.
48
+ *
49
+ * @category Encryption
50
+ */
51
+ export type UIACallback = (uia: {
52
+ flows: { stages: string[] }[];
53
+ params?: Record<string, unknown>;
54
+ session?: string;
55
+ }) => Promise<Record<string, unknown> | null>;
56
+
57
+ /**
58
+ * The shape of `OlmMachine.bootstrapCrossSigning()`'s return value on
59
+ * matrix-sdk-crypto-nodejs >= 0.5.0. Older bindings (<= 0.4.0) return `void`
60
+ * and provide no way to retrieve these requests, so cross-signing cannot be
61
+ * published with them — `bootstrapCrossSigning()` below detects that and reports
62
+ * it rather than silently doing nothing.
63
+ *
64
+ * @internal
65
+ */
66
+ interface CrossSigningBootstrapRequestsLike {
67
+ /** Device-keys upload; may be absent if device keys were already uploaded. */
68
+ uploadKeysReq?: KeysUploadRequest;
69
+ /**
70
+ * JSON-encoded body for `POST /keys/device_signing/upload`. No request id —
71
+ * `markRequestAsSent` is NOT called for it; uploading requires UIA.
72
+ */
73
+ uploadSigningKeysReq: string;
74
+ /** The cross-signing signatures upload (signs our own device). */
75
+ uploadSignaturesReq: SignatureUploadRequest;
76
+ }
77
+
78
+ /**
79
+ * @internal
80
+ */
81
+ export class RustEngine {
82
+ public readonly lock = new AsyncLock();
83
+
84
+ public constructor(public readonly machine: OlmMachine, private client: MatrixClient) {
85
+ }
86
+
87
+ public async run() {
88
+ await this.runOnly(); // run everything, but with syntactic sugar
89
+ }
90
+
91
+ private async runOnly(...types: RequestType[]) {
92
+ // Note: we should not be running this until it runs out, so cache the value into a variable
93
+ const requests = await this.machine.outgoingRequests();
94
+ for (const request of requests) {
95
+ if (types.length && !types.includes(request.type)) continue;
96
+ switch (request.type) {
97
+ case RequestType.KeysUpload:
98
+ await this.processKeysUploadRequest(request);
99
+ break;
100
+ case RequestType.KeysQuery:
101
+ await this.processKeysQueryRequest(request);
102
+ break;
103
+ case RequestType.KeysClaim:
104
+ await this.processKeysClaimRequest(request);
105
+ break;
106
+ case RequestType.ToDevice:
107
+ await this.processToDeviceRequest(request as ToDeviceRequest);
108
+ break;
109
+ case RequestType.RoomMessage:
110
+ throw new Error("Bindings error: Sending room messages is not supported");
111
+ case RequestType.SignatureUpload:
112
+ await this.processSignatureUploadRequest(request as SignatureUploadRequest);
113
+ break;
114
+ case RequestType.KeysBackup:
115
+ await this.processKeysBackupRequest(request as KeysBackupRequest);
116
+ break;
117
+ default:
118
+ throw new Error("Bindings error: Unrecognized request type: " + request.type);
119
+ }
120
+ }
121
+ }
122
+
123
+ public async addTrackedUsers(userIds: string[]) {
124
+ await this.lock.acquire(SYNC_LOCK_NAME, async () => {
125
+ const uids = userIds.map(u => new UserId(u));
126
+ await this.machine.updateTrackedUsers(uids);
127
+
128
+ const keysClaim = await this.machine.getMissingSessions(uids);
129
+ if (keysClaim) {
130
+ await this.processKeysClaimRequest(keysClaim);
131
+ }
132
+ });
133
+ }
134
+
135
+ public async prepareEncrypt(roomId: string, roomInfo: ICryptoRoomInformation) {
136
+ // TODO: Handle pre-shared invite keys too
137
+ const members = (await this.client.getJoinedRoomMembers(roomId)).map(u => new UserId(u));
138
+
139
+ let historyVis = HistoryVisibility.Joined;
140
+ switch (roomInfo.historyVisibility) {
141
+ case "world_readable":
142
+ historyVis = HistoryVisibility.WorldReadable;
143
+ break;
144
+ case "invited":
145
+ historyVis = HistoryVisibility.Invited;
146
+ break;
147
+ case "shared":
148
+ historyVis = HistoryVisibility.Shared;
149
+ break;
150
+ case "joined":
151
+ default:
152
+ // Default and other cases handled by assignment before switch
153
+ }
154
+
155
+ const encEv = new EncryptionEvent({
156
+ type: "m.room.encryption",
157
+ content: roomInfo,
158
+ });
159
+
160
+ const settings = new EncryptionSettings();
161
+ settings.algorithm = roomInfo.algorithm === EncryptionAlgorithm.MegolmV1AesSha2
162
+ ? RustEncryptionAlgorithm.MegolmV1AesSha2
163
+ : undefined;
164
+ settings.historyVisibility = historyVis;
165
+ settings.rotationPeriod = BigInt(encEv.rotationPeriodMs);
166
+ settings.rotationPeriodMessages = BigInt(encEv.rotationPeriodMessages);
167
+
168
+ await this.lock.acquire(SYNC_LOCK_NAME, async () => {
169
+ await this.machine.updateTrackedUsers(members); // just in case we missed some
170
+ await this.runOnly(RequestType.KeysQuery);
171
+ const keysClaim = await this.machine.getMissingSessions(members);
172
+ if (keysClaim) {
173
+ await this.processKeysClaimRequest(keysClaim);
174
+ }
175
+ });
176
+
177
+ await this.lock.acquire(roomId, async () => {
178
+ const requests = await this.machine.shareRoomKey(new RoomId(roomId), members, settings);
179
+ for (const req of requests) {
180
+ await this.actuallyProcessToDeviceRequest(req.txnId, req.eventType, JSON.parse(req.body)["messages"]);
181
+ }
182
+ });
183
+ }
184
+
185
+ private async processKeysClaimRequest(request: KeysClaimRequest) {
186
+ const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/claim", null, JSON.parse(request.body));
187
+ await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
188
+ }
189
+
190
+ private async processKeysUploadRequest(request: KeysUploadRequest) {
191
+ const body = JSON.parse(request.body);
192
+ // delete body["one_time_keys"]; // use this to test MSC3983
193
+ const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/upload", null, body);
194
+ await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
195
+ }
196
+
197
+ private async processKeysQueryRequest(request: KeysQueryRequest) {
198
+ const resp = await this.client.doRequest("POST", "/_matrix/client/v3/keys/query", null, JSON.parse(request.body));
199
+ await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
200
+ }
201
+
202
+ private async processToDeviceRequest(request: ToDeviceRequest) {
203
+ const req = JSON.parse(request.body);
204
+ await this.actuallyProcessToDeviceRequest(req.txn_id, req.event_type, req.messages);
205
+ }
206
+
207
+ private async actuallyProcessToDeviceRequest(id: string, type: string, messages: Record<string, Record<string, unknown>>) {
208
+ const resp = await this.client.sendToDevices(type, messages);
209
+ await this.machine.markRequestAsSent(id, RequestType.ToDevice, JSON.stringify(resp));
210
+ }
211
+
212
+ private async processSignatureUploadRequest(request: SignatureUploadRequest) {
213
+ // The binding serializes the body as { "signed_keys": {<user_id>: {...}} },
214
+ // but POST /keys/signatures/upload expects that inner map directly (its
215
+ // top-level keys are user IDs). Unwrap the `signed_keys` envelope —
216
+ // otherwise the server rejects "signed_keys" as a user id (M_INVALID_PARAM)
217
+ // and the failure response breaks markRequestAsSent's deserialization.
218
+ const parsed = JSON.parse(request.body);
219
+ const httpBody = parsed && parsed.signed_keys ? parsed.signed_keys : parsed;
220
+ const resp = await this.client.doRequest(
221
+ "POST", "/_matrix/client/v3/keys/signatures/upload", null, httpBody,
222
+ );
223
+ // The bootstrap-driven signature request carries no request id; only
224
+ // mark queue-driven requests as sent.
225
+ if (request.id) {
226
+ await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
227
+ }
228
+ }
229
+
230
+ private async processKeysBackupRequest(request: KeysBackupRequest) {
231
+ // Backing up room keys requires an existing server-side backup version
232
+ // to write into. The version is created out-of-band by
233
+ // `enableKeyBackup()`; here we just write the encrypted room keys to the
234
+ // active version. If no backup version exists the server returns
235
+ // M_NOT_FOUND — we surface that rather than silently dropping keys.
236
+ const version = await this.activeKeyBackupVersion();
237
+ if (!version) {
238
+ throw new Error(
239
+ "Bindings error: a KeysBackup request was produced but no server-side backup version " +
240
+ "exists. Call enableKeyBackup() to create/activate one before backing up room keys.",
241
+ );
242
+ }
243
+ const resp = await this.client.doRequest(
244
+ "PUT", "/_matrix/client/v3/room_keys/keys", { version }, JSON.parse(request.body),
245
+ );
246
+ await this.machine.markRequestAsSent(request.id, request.type, JSON.stringify(resp));
247
+ }
248
+
249
+ /** The currently-active server-side key-backup version, or null if none. */
250
+ private async activeKeyBackupVersion(): Promise<string | null> {
251
+ try {
252
+ const resp = await this.client.doRequest("GET", "/_matrix/client/v3/room_keys/version");
253
+ return resp?.version ?? null;
254
+ } catch (e) {
255
+ // M_NOT_FOUND => no backup exists yet.
256
+ return null;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Publish a cross-signing identity for this account so the device becomes
262
+ * cross-signed (self-verified). This is the piece matrix-bot-sdk historically
263
+ * could not drive: `runOnly` threw on the request types it produces, and the
264
+ * cross-signing public-key upload is not an outgoing-queue request at all —
265
+ * it is returned by `OlmMachine.bootstrapCrossSigning()`.
266
+ *
267
+ * Flow:
268
+ * 1. `machine.bootstrapCrossSigning(reset)` creates the identity locally
269
+ * and (on matrix-sdk-crypto-nodejs >= 0.5.0) returns the three upload
270
+ * requests.
271
+ * 2. Upload device keys (`uploadKeysReq`) if present.
272
+ * 3. POST the cross-signing public keys to `/keys/device_signing/upload`,
273
+ * completing the User-Interactive Auth challenge via `uiaCallback`.
274
+ * 4. POST the self-signature to `/keys/signatures/upload`.
275
+ *
276
+ * On bindings <= 0.4.0, `bootstrapCrossSigning` returns `void` and gives no
277
+ * way to retrieve the upload requests, so this method throws a clear,
278
+ * actionable error (callers are expected to treat that as non-fatal).
279
+ *
280
+ * @param uiaCallback Resolves the UIA challenge for the device-signing upload.
281
+ * @param reset Pass `false` (default) to reuse an existing local identity;
282
+ * the same request can be re-sent across UIA stages. `true`
283
+ * wipes any existing identity and resets device trust.
284
+ */
285
+ public async bootstrapCrossSigning(uiaCallback: UIACallback, reset = false): Promise<void> {
286
+ await this.lock.acquire(SYNC_LOCK_NAME, async () => {
287
+ const requests = (await this.machine.bootstrapCrossSigning(reset)) as
288
+ unknown as CrossSigningBootstrapRequestsLike | undefined;
289
+
290
+ if (!requests || typeof requests.uploadSigningKeysReq !== "string") {
291
+ throw new Error(
292
+ "Bindings error: OlmMachine.bootstrapCrossSigning() did not return the upload requests. " +
293
+ "Cross-signing cannot be published with this version of " +
294
+ "@matrix-org/matrix-sdk-crypto-nodejs (need >= 0.5.0).",
295
+ );
296
+ }
297
+
298
+ // 1. Device keys (may be absent if already uploaded).
299
+ if (requests.uploadKeysReq) {
300
+ await this.processKeysUploadRequest(requests.uploadKeysReq);
301
+ }
302
+
303
+ // 2. Cross-signing public keys -> /keys/device_signing/upload (UIA).
304
+ await this.uploadDeviceSigningKeys(JSON.parse(requests.uploadSigningKeysReq), uiaCallback);
305
+
306
+ // 3. Self-signature upload. This one IS a normal SignatureUpload
307
+ // request, so reuse the same code path (and mark it as sent).
308
+ await this.processSignatureUploadRequest(requests.uploadSignaturesReq);
309
+ });
310
+ }
311
+
312
+ /**
313
+ * POST the cross-signing public keys to `/keys/device_signing/upload`,
314
+ * driving the UIA challenge. The first attempt is sent without `auth`; the
315
+ * server replies 401 with the available flows, which `uiaCallback` resolves
316
+ * (typically an `m.login.password` stage). We then resubmit the SAME key
317
+ * body with the returned `auth` dict (including the `session` id).
318
+ */
319
+ private async uploadDeviceSigningKeys(keysBody: Record<string, unknown>, uiaCallback: UIACallback): Promise<void> {
320
+ const path = "/_matrix/client/v3/keys/device_signing/upload";
321
+ try {
322
+ await this.client.doRequest("POST", path, null, keysBody);
323
+ return; // Server accepted without UIA (rare, but allowed).
324
+ } catch (e) {
325
+ const uia = extractUIA(e);
326
+ if (!uia) throw e; // Not a UIA challenge — a real error.
327
+
328
+ // Resolve the challenge, then resubmit the same keys with `auth`.
329
+ // Loop in case the server stacks multiple stages.
330
+ let session = uia.session;
331
+ for (let attempt = 0; attempt < 10; attempt++) {
332
+ const auth = await uiaCallback(uia);
333
+ if (!auth) {
334
+ throw new Error("UIA for /keys/device_signing/upload was not satisfied (callback returned null)");
335
+ }
336
+ if (session && auth.session === undefined) auth.session = session;
337
+ try {
338
+ await this.client.doRequest("POST", path, null, { ...keysBody, auth });
339
+ return; // Success.
340
+ } catch (e2) {
341
+ const next = extractUIA(e2);
342
+ if (!next) throw e2; // Either success-shaped error or a hard failure.
343
+ // 401 again: if it carries completed stages we progress; otherwise
344
+ // the credentials were rejected. Surface the error body either way.
345
+ if (next.session) session = next.session;
346
+ if (hasFailedUIA(e2)) {
347
+ throw new Error("UIA for /keys/device_signing/upload failed: credentials rejected");
348
+ }
349
+ // Continue to next stage.
350
+ }
351
+ }
352
+ throw new Error("UIA for /keys/device_signing/upload did not complete after multiple attempts");
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Create + activate a server-side key-backup version derived from the given
358
+ * recovery/backup public key, and tell the OlmMachine to use it. After this,
359
+ * `KeysBackup` requests produced by `backupRoomKeys()` will have a version to
360
+ * write into (handled in `processKeysBackupRequest`).
361
+ *
362
+ * NOTE: key backup is secondary to cross-signing. This wires the version
363
+ * plumbing; the caller is responsible for generating the backup key and for
364
+ * calling `machine.enableBackupV1()` / `backupRoomKeys()`. It is intentionally
365
+ * conservative — see the adapter docs.
366
+ */
367
+ public async enableKeyBackup(authData: Record<string, unknown>, algorithm = "m.megolm_backup.v1.curve25519-aes-sha2"): Promise<string> {
368
+ const resp = await this.client.doRequest(
369
+ "POST", "/_matrix/client/v3/room_keys/version", null, { algorithm, auth_data: authData },
370
+ );
371
+ return resp.version;
372
+ }
373
+
374
+ /**
375
+ * Restore cross-signing private keys (and optionally the megolm backup decryption
376
+ * key) from Secure Secret Storage (4S/SSSS) into the local crypto store.
377
+ *
378
+ * This is the recovery path when the local crypto store has been lost: the
379
+ * cross-signing keys were previously uploaded encrypted to account data by
380
+ * `bootstrapSecretStorage*`, and this method fetches and decrypts them using
381
+ * the same passphrase. After a successful restore:
382
+ *
383
+ * - The OlmMachine has the master / self-signing / user-signing private keys.
384
+ * - The device re-signs itself, so it continues to appear verified without
385
+ * needing UIA or a fresh cross-signing bootstrap.
386
+ * - If the megolm backup decryption key is in account data, it is also
387
+ * restored so room-key backup requests work with the existing backup version.
388
+ *
389
+ * Requires `@matrix-org/matrix-sdk-crypto-nodejs >= 0.6.0` (for
390
+ * `importSecretsFromSecretStorage`). Throws if the passphrase does not match
391
+ * the SSSS key on the server, or if account data is missing.
392
+ *
393
+ * @param passphrase The same passphrase that was used to bootstrap 4S.
394
+ */
395
+ public async restoreSecretsFromSecretStorage(passphrase: string): Promise<void> {
396
+ const machine4s = this.machine as OlmMachineWith4S;
397
+ if (typeof machine4s.importSecretsFromSecretStorage !== "function") {
398
+ throw new Error(
399
+ "Bindings error: OlmMachine.importSecretsFromSecretStorage() is not available. " +
400
+ "Secret Storage restore requires @matrix-org/matrix-sdk-crypto-nodejs >= 0.6.0.",
401
+ );
402
+ }
403
+
404
+ // 1. Resolve the default SSSS key from account data.
405
+ const defaultKey = await this.client.getAccountData<{ key: string }>("m.secret_storage.default_key");
406
+ if (!defaultKey?.key) {
407
+ throw new Error("No default SSSS key found in account data (m.secret_storage.default_key missing or empty).");
408
+ }
409
+ const keyEventType = `m.secret_storage.key.${defaultKey.key}`;
410
+ const keyContent = await this.client.getAccountData<object>(keyEventType);
411
+ if (!keyContent) {
412
+ throw new Error(`SSSS key descriptor not found in account data (${keyEventType} missing).`);
413
+ }
414
+
415
+ // 2. Reconstruct the SecretStorageKey from the passphrase + server metadata.
416
+ // fromAccountData verifies the passphrase against the stored MAC; it throws
417
+ // if the passphrase is wrong.
418
+ const ssssKey = SecretStorageKey.fromAccountData(passphrase, keyEventType, JSON.stringify(keyContent));
419
+
420
+ // 3. Fetch the three encrypted cross-signing key blobs from account data.
421
+ const [masterRaw, selfSigningRaw, userSigningRaw] = await Promise.all([
422
+ this.client.getAccountData<object>("m.cross_signing.master"),
423
+ this.client.getAccountData<object>("m.cross_signing.self_signing"),
424
+ this.client.getAccountData<object>("m.cross_signing.user_signing"),
425
+ ]);
426
+ if (!masterRaw || !selfSigningRaw || !userSigningRaw) {
427
+ throw new Error("One or more cross-signing key blobs are missing from account data. " +
428
+ "4S may not have been bootstrapped yet — run bootstrapSecretStorage first.");
429
+ }
430
+
431
+ // 4. Import: the binding decrypts each blob using ssssKey, imports the
432
+ // private keys into the local OlmMachine, and returns a SignatureUpload
433
+ // request to re-sign the current device with the self-signing key.
434
+ const items = new SecretStorageItems({
435
+ masterKey: JSON.stringify(masterRaw),
436
+ selfSigningKey: JSON.stringify(selfSigningRaw),
437
+ userSigningKey: JSON.stringify(userSigningRaw),
438
+ });
439
+ const sigReq = await machine4s.importSecretsFromSecretStorage(ssssKey, items);
440
+ await this.processSignatureUploadRequest(sigReq);
441
+
442
+ // 5. Restore the megolm backup decryption key if present, so room-key
443
+ // backup requests are directed to the existing backup version.
444
+ try {
445
+ const backupKeyRaw = await this.client.getAccountData<object>("m.megolm_backup.v1");
446
+ if (backupKeyRaw) {
447
+ const decryptedBase64 = ssssKey.decrypt(JSON.stringify(backupKeyRaw), "m.megolm_backup.v1");
448
+ const decryptionKey = BackupDecryptionKey.fromBase64(decryptedBase64);
449
+ const backupInfo = await this.activeKeyBackupVersion();
450
+ if (backupInfo) {
451
+ await this.machine.enableBackupV1(decryptionKey.megolmV1PublicKey.publicKeyBase64, backupInfo);
452
+ await this.machine.saveBackupDecryptionKey(decryptionKey, backupInfo);
453
+ }
454
+ }
455
+ } catch (e) {
456
+ // Non-fatal: cross-signing keys are restored; only room-key backup is affected.
457
+ }
458
+ }
459
+
460
+ private async bootstrapSecretStorage(ssssKey: import("@matrix-org/matrix-sdk-crypto-nodejs").SecretStorageKey, opts: { withKeyBackup?: boolean; reset?: boolean } = {}): Promise<void> {
461
+ const machine4s = this.machine as OlmMachineWith4S;
462
+ if (typeof machine4s.exportSecretsForSecretStorage !== "function") {
463
+ throw new Error("Bindings error: OlmMachine.exportSecretsForSecretStorage() is not available. " +
464
+ "Secret Storage (4S) requires @matrix-org/matrix-sdk-crypto-nodejs >= 0.6.0.");
465
+ }
466
+
467
+ const { withKeyBackup = true, reset = false } = opts;
468
+ const userId = this.machine.userId.toString();
469
+ await this.putAccountData(userId, ssssKey.eventType(), JSON.parse(ssssKey.accountDataContent()));
470
+ await this.putAccountData(userId, "m.secret_storage.default_key", { key: ssssKey.keyId() });
471
+
472
+ const items = await machine4s.exportSecretsForSecretStorage(ssssKey);
473
+ await this.putAccountData(userId, "m.cross_signing.master", JSON.parse(items.masterKey));
474
+ await this.putAccountData(userId, "m.cross_signing.self_signing", JSON.parse(items.selfSigningKey));
475
+ await this.putAccountData(userId, "m.cross_signing.user_signing", JSON.parse(items.userSigningKey));
476
+
477
+ if (withKeyBackup) {
478
+ await this.bootstrapKeyBackupIntoSecretStorage(ssssKey, userId, reset);
479
+ }
480
+ }
481
+
482
+ public async bootstrapSecretStorageFromPassphrase(passphrase: string, opts: { withKeyBackup?: boolean; reset?: boolean } = {}): Promise<void> {
483
+ const ssssKey = SecretStorageKey.createFromPassphrase(passphrase);
484
+ await this.bootstrapSecretStorage(ssssKey, opts);
485
+ }
486
+
487
+ public async bootstrapSecretStorageFromKey(ssssKey: import("@matrix-org/matrix-sdk-crypto-nodejs").SecretStorageKey, opts: { withKeyBackup?: boolean; reset?: boolean } = {}): Promise<void> {
488
+ await this.bootstrapSecretStorage(ssssKey, opts);
489
+ }
490
+
491
+ private async bootstrapKeyBackupIntoSecretStorage(
492
+ ssssKey: import("@matrix-org/matrix-sdk-crypto-nodejs").SecretStorageKey,
493
+ userId: string,
494
+ reset: boolean,
495
+ ): Promise<void> {
496
+ const existingVersion = await this.activeKeyBackupVersion();
497
+ if (existingVersion && !reset) {
498
+ // Server has a backup. Check whether the local OlmMachine already knows about it.
499
+ const localKeys = await this.machine.getBackupKeys();
500
+ if (localKeys.backupVersion === existingVersion) return; // In sync — nothing to do.
501
+
502
+ // Local store is out of sync (e.g. crypto-store wiped and bootstrap retried with
503
+ // a new SSSS key, or interrupted before saveBackupDecryptionKey). Try to reconnect
504
+ // by decrypting the existing backup key from account data using the current SSSS key.
505
+ try {
506
+ const backupKeyRaw = await this.client.getAccountData<object>("m.megolm_backup.v1");
507
+ if (backupKeyRaw) {
508
+ const decryptedBase64 = ssssKey.decrypt(JSON.stringify(backupKeyRaw), "m.megolm_backup.v1");
509
+ const decryptionKey = BackupDecryptionKey.fromBase64(decryptedBase64);
510
+ await this.machine.enableBackupV1(decryptionKey.megolmV1PublicKey.publicKeyBase64, existingVersion);
511
+ await this.machine.saveBackupDecryptionKey(decryptionKey, existingVersion);
512
+ return;
513
+ }
514
+ } catch {
515
+ // Account data is missing, encrypted with a different SSSS key, or corrupted.
516
+ // Fall through to create a new backup version under the current SSSS key.
517
+ }
518
+ // Could not reuse the existing backup — delete it and create a fresh one.
519
+ await this.client.doRequest("DELETE", `/_matrix/client/v3/room_keys/version/${existingVersion}`);
520
+ }
521
+ if (existingVersion && reset) {
522
+ await this.client.doRequest("DELETE", `/_matrix/client/v3/room_keys/version/${existingVersion}`);
523
+ }
524
+
525
+ const decryptionKey = BackupDecryptionKey.createRandomKey();
526
+ const publicKey = decryptionKey.megolmV1PublicKey;
527
+ const authDataBase = { public_key: publicKey.publicKeyBase64 };
528
+ const canonicalBase = JSON.stringify(authDataBase, Object.keys(authDataBase).sort());
529
+ const sigs = await this.machine.sign(canonicalBase);
530
+ const authData = { ...authDataBase, signatures: JSON.parse(sigs.asJSON()) };
531
+
532
+ const version = await this.enableKeyBackup(authData, publicKey.algorithm);
533
+ await this.machine.enableBackupV1(publicKey.publicKeyBase64, version);
534
+ await this.machine.saveBackupDecryptionKey(decryptionKey, version);
535
+
536
+ const encryptedKey = ssssKey.encrypt(decryptionKey.toBase64(), "m.megolm_backup.v1");
537
+ await this.putAccountData(userId, "m.megolm_backup.v1", JSON.parse(encryptedKey));
538
+ }
539
+
540
+ private async putAccountData(userId: string, eventType: string, content: Record<string, unknown>): Promise<void> {
541
+ await this.client.doRequest(
542
+ "PUT",
543
+ `/_matrix/client/v3/user/${encodeURIComponent(userId)}/account_data/${encodeURIComponent(eventType)}`,
544
+ null, content,
545
+ );
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Extract a UIA challenge from a thrown matrix-bot-sdk request error. A 401 UIA
551
+ * body has `flows`/`session` and no `errcode`, so matrix-bot-sdk throws the raw
552
+ * response object (`throw response`) rather than a `MatrixError`. We also accept
553
+ * a `MatrixError`-like shape defensively. Returns null if it is not a UIA 401.
554
+ *
555
+ * @internal
556
+ */
557
+ export function extractUIA(err: unknown): { flows: { stages: string[] }[]; params?: Record<string, unknown>; session?: string } | null {
558
+ if (!err || typeof err !== "object") return null;
559
+ const e = err as { statusCode?: number; body?: unknown };
560
+ const status = e.statusCode;
561
+ const body = (e.body ?? err) as { flows?: unknown; session?: string; params?: Record<string, unknown> };
562
+ if (status !== undefined && status !== 401) return null;
563
+ if (!body || !Array.isArray(body.flows)) return null;
564
+ return { flows: body.flows as { stages: string[] }[], params: body.params, session: body.session };
565
+ }
566
+
567
+ /**
568
+ * A second 401 whose body reports `completed: []` (or an unchanged stage list)
569
+ * after we supplied credentials indicates the credentials were rejected, not a
570
+ * stacked second stage. We treat the presence of an `error`/`errcode` in the
571
+ * body as a hard rejection.
572
+ *
573
+ * @internal
574
+ */
575
+ export function hasFailedUIA(err: unknown): boolean {
576
+ if (!err || typeof err !== "object") return false;
577
+ const e = err as { body?: { errcode?: string; error?: string } };
578
+ const body = e.body ?? (err as { errcode?: string; error?: string });
579
+ return Boolean(body && (body as { errcode?: string }).errcode);
580
+ }
@@ -0,0 +1,38 @@
1
+ import { MatrixClient } from "../MatrixClient";
2
+ import { CryptoClient } from "./CryptoClient";
3
+
4
+ /**
5
+ * Flags a MatrixClient function as needing end-to-end encryption enabled.
6
+ * @category Encryption
7
+ */
8
+ export function requiresCrypto() {
9
+ return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
10
+ const originalMethod = descriptor.value;
11
+ descriptor.value = function(...args: any[]) {
12
+ const client: MatrixClient = this; // eslint-disable-line @typescript-eslint/no-this-alias
13
+ if (!client.crypto) {
14
+ throw new Error("End-to-end encryption is not enabled");
15
+ }
16
+
17
+ return originalMethod.apply(this, args);
18
+ };
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Flags a CryptoClient function as needing the CryptoClient to be ready.
24
+ * @category Encryption
25
+ */
26
+ export function requiresReady() {
27
+ return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
28
+ const originalMethod = descriptor.value;
29
+ descriptor.value = function(...args: any[]) {
30
+ const crypto: CryptoClient = this; // eslint-disable-line @typescript-eslint/no-this-alias
31
+ if (!crypto.isReady) {
32
+ throw new Error("End-to-end encryption has not initialized");
33
+ }
34
+
35
+ return originalMethod.apply(this, args);
36
+ };
37
+ };
38
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Represents a Matrix entity
3
+ * @category Utilities
4
+ */
5
+ export class MatrixEntity {
6
+ private entityLocalpart: string;
7
+ private entityDomain: string;
8
+
9
+ /**
10
+ * Creates a new Matrix entity
11
+ * @param {string} fullId The full ID of the entity
12
+ */
13
+ constructor(private fullId: string) {
14
+ if (!fullId) throw new Error("No entity ID provided");
15
+ if (fullId.length < 2) throw new Error("ID too short");
16
+ const parts = fullId.split(/:/g);
17
+ this.entityLocalpart = parts[0].substring(1);
18
+ this.entityDomain = parts.splice(1).join(':');
19
+ }
20
+
21
+ /**
22
+ * The localpart for the entity
23
+ */
24
+ public get localpart(): string {
25
+ return this.entityLocalpart;
26
+ }
27
+
28
+ /**
29
+ * The domain for the entity
30
+ */
31
+ public get domain(): string {
32
+ return this.entityDomain;
33
+ }
34
+
35
+ // override
36
+ public toString(): string {
37
+ return this.fullId;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Represents a Matrix user ID
43
+ * @category Utilities
44
+ */
45
+ export class UserID extends MatrixEntity {
46
+ constructor(userId: string) {
47
+ super(userId);
48
+ if (!userId.startsWith("@")) {
49
+ throw new Error("Not a valid user ID");
50
+ }
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Represents a Matrix room alias
56
+ * @category Utilities
57
+ */
58
+ export class RoomAlias extends MatrixEntity {
59
+ constructor(alias: string) {
60
+ super(alias);
61
+ if (!alias.startsWith("#")) {
62
+ throw new Error("Not a valid room alias");
63
+ }
64
+ }
65
+ }