zapo-js 0.1.0 → 0.1.2

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 (366) hide show
  1. package/README.md +12 -7
  2. package/dist/appstate/WaAppStateCrypto.js +18 -25
  3. package/dist/appstate/WaAppStateSyncClient.js +181 -114
  4. package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
  5. package/dist/appstate/constants.js +4 -3
  6. package/dist/appstate/utils.js +10 -30
  7. package/dist/auth/WaAuthClient.js +48 -55
  8. package/dist/auth/flow/WaAuthCredentialsFlow.js +21 -14
  9. package/dist/auth/index.js +1 -3
  10. package/dist/auth/pairing/WaPairingFlow.js +21 -23
  11. package/dist/auth/pairing/WaQrFlow.js +37 -24
  12. package/dist/client/WaClient.js +103 -276
  13. package/dist/client/WaClientFactory.js +227 -110
  14. package/dist/client/connection/WaConnectionManager.js +292 -0
  15. package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
  16. package/dist/client/connection/WaReceiptQueue.js +51 -0
  17. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
  18. package/dist/client/coordinators/WaGroupCoordinator.js +27 -17
  19. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
  20. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +231 -686
  21. package/dist/client/coordinators/WaRetryCoordinator.js +70 -37
  22. package/dist/client/dirty.js +35 -29
  23. package/dist/client/events/chat.js +4 -3
  24. package/dist/client/events/group.js +59 -36
  25. package/dist/client/history-sync.js +53 -63
  26. package/dist/client/incoming.js +23 -20
  27. package/dist/client/mailbox.js +8 -8
  28. package/dist/client/messages.js +4 -4
  29. package/dist/client/messaging/fanout.js +189 -0
  30. package/dist/client/messaging/key-protocol.js +130 -0
  31. package/dist/client/messaging/participants.js +191 -0
  32. package/dist/crypto/core/hkdf.js +3 -8
  33. package/dist/crypto/core/index.js +1 -4
  34. package/dist/crypto/core/keys.js +2 -3
  35. package/dist/crypto/core/primitives.js +12 -15
  36. package/dist/crypto/core/random.js +7 -26
  37. package/dist/crypto/curves/Ed25519.js +7 -8
  38. package/dist/crypto/curves/X25519.js +13 -16
  39. package/dist/crypto/index.js +0 -5
  40. package/dist/esm/appstate/WaAppStateCrypto.js +6 -13
  41. package/dist/esm/appstate/WaAppStateSyncClient.js +174 -107
  42. package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
  43. package/dist/esm/appstate/constants.js +3 -2
  44. package/dist/esm/appstate/utils.js +8 -27
  45. package/dist/esm/auth/WaAuthClient.js +48 -55
  46. package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +21 -14
  47. package/dist/esm/auth/index.js +0 -1
  48. package/dist/esm/auth/pairing/WaPairingFlow.js +14 -16
  49. package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
  50. package/dist/esm/client/WaClient.js +103 -276
  51. package/dist/esm/client/WaClientFactory.js +227 -110
  52. package/dist/esm/client/connection/WaConnectionManager.js +288 -0
  53. package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
  54. package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
  55. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
  56. package/dist/esm/client/coordinators/WaGroupCoordinator.js +20 -10
  57. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
  58. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +232 -687
  59. package/dist/esm/client/coordinators/WaRetryCoordinator.js +71 -38
  60. package/dist/esm/client/dirty.js +30 -24
  61. package/dist/esm/client/events/chat.js +4 -3
  62. package/dist/esm/client/events/group.js +50 -28
  63. package/dist/esm/client/history-sync.js +50 -60
  64. package/dist/esm/client/incoming.js +23 -20
  65. package/dist/esm/client/mailbox.js +8 -8
  66. package/dist/esm/client/messages.js +1 -1
  67. package/dist/esm/client/messaging/fanout.js +186 -0
  68. package/dist/esm/client/messaging/key-protocol.js +127 -0
  69. package/dist/esm/client/messaging/participants.js +188 -0
  70. package/dist/esm/crypto/core/hkdf.js +3 -8
  71. package/dist/esm/crypto/core/index.js +0 -1
  72. package/dist/esm/crypto/core/keys.js +2 -3
  73. package/dist/esm/crypto/core/primitives.js +12 -15
  74. package/dist/esm/crypto/core/random.js +6 -25
  75. package/dist/esm/crypto/curves/Ed25519.js +4 -5
  76. package/dist/esm/crypto/curves/X25519.js +10 -13
  77. package/dist/esm/crypto/index.js +0 -2
  78. package/dist/esm/infra/log/ConsoleLogger.js +18 -17
  79. package/dist/esm/infra/log/PinoLogger.js +15 -9
  80. package/dist/esm/infra/log/types.js +11 -1
  81. package/dist/esm/infra/perf/BoundedTaskQueue.js +13 -17
  82. package/dist/esm/media/WaMediaCrypto.js +2 -4
  83. package/dist/esm/media/WaMediaTransferClient.js +226 -58
  84. package/dist/esm/media/conn.js +10 -6
  85. package/dist/esm/media/constants.js +4 -1
  86. package/dist/esm/message/WaMessageClient.js +4 -13
  87. package/dist/esm/message/ack.js +6 -6
  88. package/dist/esm/message/addon-crypto.js +59 -0
  89. package/dist/esm/message/incoming.js +106 -111
  90. package/dist/esm/message/index.js +2 -0
  91. package/dist/esm/message/reporting-token.js +438 -0
  92. package/dist/esm/message/use-case-secret.js +49 -0
  93. package/dist/esm/protocol/appstate.js +58 -0
  94. package/dist/esm/protocol/constants.js +2 -1
  95. package/dist/esm/protocol/index.js +2 -10
  96. package/dist/esm/protocol/jid.js +63 -51
  97. package/dist/esm/protocol/media.js +3 -3
  98. package/dist/esm/protocol/nodes.js +2 -0
  99. package/dist/esm/protocol/usync.js +11 -0
  100. package/dist/esm/retry/index.js +1 -0
  101. package/dist/esm/retry/outbound.js +4 -5
  102. package/dist/esm/retry/parse.js +58 -76
  103. package/dist/esm/retry/replay.js +48 -49
  104. package/dist/esm/retry/tracker.js +56 -0
  105. package/dist/esm/signal/api/SignalDeviceSyncApi.js +249 -82
  106. package/dist/esm/signal/api/SignalDigestSyncApi.js +6 -1
  107. package/dist/esm/signal/api/SignalIdentitySyncApi.js +49 -34
  108. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +70 -62
  109. package/dist/esm/signal/api/SignalSessionSyncApi.js +23 -30
  110. package/dist/esm/signal/crypto/WaAdvSignature.js +3 -5
  111. package/dist/esm/signal/group/SenderKeyChain.js +28 -23
  112. package/dist/esm/signal/group/SenderKeyCodec.js +2 -4
  113. package/dist/esm/signal/group/SenderKeyManager.js +26 -16
  114. package/dist/esm/signal/index.js +1 -0
  115. package/dist/esm/signal/session/SignalProtocol.js +49 -14
  116. package/dist/esm/signal/session/SignalRatchet.js +24 -15
  117. package/dist/esm/signal/session/SignalSession.js +14 -9
  118. package/dist/esm/signal/session/resolver.js +186 -0
  119. package/dist/esm/signal/store/sqlite.js +16 -37
  120. package/dist/esm/store/createStore.js +16 -18
  121. package/dist/esm/store/noop.store.js +3 -6
  122. package/dist/esm/store/providers/memory/appstate.store.js +30 -6
  123. package/dist/esm/store/providers/memory/contact.store.js +5 -0
  124. package/dist/esm/store/providers/memory/device-list.store.js +3 -30
  125. package/dist/esm/store/providers/memory/message.store.js +11 -5
  126. package/dist/esm/store/providers/memory/participants.store.js +1 -8
  127. package/dist/esm/store/providers/memory/sender-key.store.js +5 -7
  128. package/dist/esm/store/providers/memory/signal.store.js +13 -1
  129. package/dist/esm/store/providers/memory/thread.store.js +5 -0
  130. package/dist/esm/store/providers/sqlite/appstate.store.js +82 -1
  131. package/dist/esm/store/providers/sqlite/connection.js +18 -13
  132. package/dist/esm/store/providers/sqlite/contact.store.js +31 -18
  133. package/dist/esm/store/providers/sqlite/device-list.store.js +7 -35
  134. package/dist/esm/store/providers/sqlite/message.store.js +45 -32
  135. package/dist/esm/store/providers/sqlite/migrations.js +1 -1
  136. package/dist/esm/store/providers/sqlite/participants.store.js +1 -9
  137. package/dist/esm/store/providers/sqlite/retry.store.js +8 -11
  138. package/dist/esm/store/providers/sqlite/sender-key.store.js +25 -30
  139. package/dist/esm/store/providers/sqlite/signal.store.js +104 -22
  140. package/dist/esm/store/providers/sqlite/table-names.js +107 -0
  141. package/dist/esm/store/providers/sqlite/thread.store.js +35 -22
  142. package/dist/esm/transport/WaComms.js +25 -23
  143. package/dist/esm/transport/WaWebSocket.js +115 -12
  144. package/dist/esm/transport/binary/decoder.js +4 -4
  145. package/dist/esm/transport/binary/encoder.js +12 -4
  146. package/dist/esm/transport/index.js +1 -0
  147. package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
  148. package/dist/esm/transport/node/WaNodeOrchestrator.js +2 -4
  149. package/dist/esm/transport/node/WaNodeTransport.js +0 -3
  150. package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
  151. package/dist/esm/transport/node/builders/index.js +2 -1
  152. package/dist/esm/transport/node/builders/message.js +9 -0
  153. package/dist/esm/transport/node/builders/pairing.js +4 -5
  154. package/dist/esm/transport/node/builders/usync.js +41 -0
  155. package/dist/esm/transport/node/helpers.js +107 -5
  156. package/dist/esm/transport/node/usync.js +35 -0
  157. package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
  158. package/dist/esm/transport/noise/WaNoiseCert.js +3 -6
  159. package/dist/esm/transport/noise/WaNoiseSession.js +17 -10
  160. package/dist/esm/transport/proxy.js +27 -0
  161. package/dist/esm/transport/stream/parse.js +13 -48
  162. package/dist/esm/util/bytes.js +50 -32
  163. package/dist/esm/util/coercion.js +6 -14
  164. package/dist/esm/util/primitives.js +39 -14
  165. package/dist/infra/log/ConsoleLogger.js +18 -17
  166. package/dist/infra/log/PinoLogger.js +15 -9
  167. package/dist/infra/log/types.js +12 -0
  168. package/dist/infra/perf/BoundedTaskQueue.js +13 -17
  169. package/dist/media/WaMediaCrypto.js +1 -3
  170. package/dist/media/WaMediaTransferClient.js +259 -58
  171. package/dist/media/conn.js +10 -6
  172. package/dist/media/constants.js +4 -1
  173. package/dist/message/WaMessageClient.js +5 -14
  174. package/dist/message/ack.js +6 -6
  175. package/dist/message/addon-crypto.js +65 -0
  176. package/dist/message/incoming.js +104 -109
  177. package/dist/message/index.js +2 -0
  178. package/dist/message/reporting-token.js +443 -0
  179. package/dist/message/use-case-secret.js +55 -0
  180. package/dist/protocol/appstate.js +59 -1
  181. package/dist/protocol/constants.js +7 -1
  182. package/dist/protocol/index.js +20 -42
  183. package/dist/protocol/jid.js +64 -51
  184. package/dist/protocol/media.js +3 -3
  185. package/dist/protocol/nodes.js +2 -0
  186. package/dist/protocol/usync.js +14 -0
  187. package/dist/retry/index.js +3 -1
  188. package/dist/retry/outbound.js +6 -7
  189. package/dist/retry/parse.js +57 -75
  190. package/dist/retry/replay.js +46 -47
  191. package/dist/retry/tracker.js +59 -0
  192. package/dist/signal/api/SignalDeviceSyncApi.js +247 -80
  193. package/dist/signal/api/SignalDigestSyncApi.js +6 -1
  194. package/dist/signal/api/SignalIdentitySyncApi.js +49 -34
  195. package/dist/signal/api/SignalMissingPreKeysSyncApi.js +67 -59
  196. package/dist/signal/api/SignalSessionSyncApi.js +23 -30
  197. package/dist/signal/crypto/WaAdvSignature.js +2 -4
  198. package/dist/signal/group/SenderKeyChain.js +27 -22
  199. package/dist/signal/group/SenderKeyCodec.js +1 -3
  200. package/dist/signal/group/SenderKeyManager.js +26 -16
  201. package/dist/signal/index.js +3 -1
  202. package/dist/signal/session/SignalProtocol.js +49 -14
  203. package/dist/signal/session/SignalRatchet.js +24 -15
  204. package/dist/signal/session/SignalSession.js +14 -9
  205. package/dist/signal/session/resolver.js +189 -0
  206. package/dist/signal/store/sqlite.js +16 -37
  207. package/dist/store/createStore.js +16 -18
  208. package/dist/store/noop.store.js +3 -6
  209. package/dist/store/providers/memory/appstate.store.js +28 -4
  210. package/dist/store/providers/memory/contact.store.js +5 -0
  211. package/dist/store/providers/memory/device-list.store.js +3 -30
  212. package/dist/store/providers/memory/message.store.js +11 -5
  213. package/dist/store/providers/memory/participants.store.js +1 -8
  214. package/dist/store/providers/memory/sender-key.store.js +8 -10
  215. package/dist/store/providers/memory/signal.store.js +21 -9
  216. package/dist/store/providers/memory/thread.store.js +5 -0
  217. package/dist/store/providers/sqlite/appstate.store.js +81 -0
  218. package/dist/store/providers/sqlite/connection.js +18 -13
  219. package/dist/store/providers/sqlite/contact.store.js +31 -18
  220. package/dist/store/providers/sqlite/device-list.store.js +7 -35
  221. package/dist/store/providers/sqlite/message.store.js +45 -32
  222. package/dist/store/providers/sqlite/migrations.js +1 -1
  223. package/dist/store/providers/sqlite/participants.store.js +1 -9
  224. package/dist/store/providers/sqlite/retry.store.js +8 -11
  225. package/dist/store/providers/sqlite/sender-key.store.js +24 -29
  226. package/dist/store/providers/sqlite/signal.store.js +105 -23
  227. package/dist/store/providers/sqlite/table-names.js +113 -0
  228. package/dist/store/providers/sqlite/thread.store.js +35 -22
  229. package/dist/transport/WaComms.js +27 -25
  230. package/dist/transport/WaWebSocket.js +148 -12
  231. package/dist/transport/binary/decoder.js +4 -4
  232. package/dist/transport/binary/encoder.js +12 -4
  233. package/dist/transport/index.js +7 -1
  234. package/dist/transport/keepalive/WaKeepAlive.js +1 -7
  235. package/dist/transport/node/WaNodeOrchestrator.js +2 -4
  236. package/dist/transport/node/WaNodeTransport.js +0 -3
  237. package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
  238. package/dist/transport/node/builders/index.js +12 -9
  239. package/dist/transport/node/builders/message.js +9 -0
  240. package/dist/transport/node/builders/pairing.js +4 -5
  241. package/dist/transport/node/builders/usync.js +45 -0
  242. package/dist/transport/node/helpers.js +112 -4
  243. package/dist/transport/node/usync.js +38 -0
  244. package/dist/transport/noise/WaFrameCodec.js +47 -32
  245. package/dist/transport/noise/WaNoiseCert.js +5 -8
  246. package/dist/transport/noise/WaNoiseSession.js +17 -10
  247. package/dist/transport/proxy.js +34 -0
  248. package/dist/transport/stream/parse.js +17 -53
  249. package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
  250. package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
  251. package/dist/types/appstate/constants.d.ts +1 -0
  252. package/dist/types/appstate/store/sqlite.d.ts +4 -18
  253. package/dist/types/appstate/utils.d.ts +0 -1
  254. package/dist/types/auth/WaAuthClient.d.ts +10 -12
  255. package/dist/types/auth/index.d.ts +0 -2
  256. package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
  257. package/dist/types/auth/types.d.ts +6 -9
  258. package/dist/types/client/WaClient.d.ts +27 -25
  259. package/dist/types/client/WaClientFactory.d.ts +22 -23
  260. package/dist/types/client/connection/WaConnectionManager.d.ts +64 -0
  261. package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
  262. package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
  263. package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
  264. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +0 -1
  265. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +18 -41
  266. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +2 -0
  267. package/dist/types/client/dirty.d.ts +1 -0
  268. package/dist/types/client/events/group.d.ts +2 -1
  269. package/dist/types/client/index.d.ts +1 -1
  270. package/dist/types/client/messaging/fanout.d.ts +14 -0
  271. package/dist/types/client/messaging/key-protocol.d.ts +18 -0
  272. package/dist/types/client/messaging/participants.d.ts +13 -0
  273. package/dist/types/client/types.d.ts +24 -1
  274. package/dist/types/crypto/core/hkdf.d.ts +0 -6
  275. package/dist/types/crypto/core/index.d.ts +0 -1
  276. package/dist/types/crypto/core/random.d.ts +1 -7
  277. package/dist/types/crypto/index.d.ts +0 -2
  278. package/dist/types/index.d.ts +1 -1
  279. package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
  280. package/dist/types/infra/log/PinoLogger.d.ts +1 -1
  281. package/dist/types/infra/log/types.d.ts +1 -0
  282. package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
  283. package/dist/types/media/WaMediaTransferClient.d.ts +13 -3
  284. package/dist/types/media/types.d.ts +5 -0
  285. package/dist/types/message/addon-crypto.d.ts +25 -0
  286. package/dist/types/message/index.d.ts +2 -0
  287. package/dist/types/message/reporting-token.d.ts +19 -0
  288. package/dist/types/message/use-case-secret.d.ts +20 -0
  289. package/dist/types/protocol/appstate.d.ts +58 -0
  290. package/dist/types/protocol/constants.d.ts +2 -1
  291. package/dist/types/protocol/index.d.ts +2 -10
  292. package/dist/types/protocol/jid.d.ts +3 -3
  293. package/dist/types/protocol/nodes.d.ts +2 -0
  294. package/dist/types/protocol/usync.d.ts +11 -0
  295. package/dist/types/retry/index.d.ts +1 -0
  296. package/dist/types/retry/replay.d.ts +0 -4
  297. package/dist/types/retry/tracker.d.ts +19 -0
  298. package/dist/types/retry/types.d.ts +4 -3
  299. package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +13 -1
  300. package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
  301. package/dist/types/signal/index.d.ts +1 -0
  302. package/dist/types/signal/session/SignalProtocol.d.ts +9 -0
  303. package/dist/types/signal/session/resolver.d.ts +17 -0
  304. package/dist/types/store/contracts/appstate.store.d.ts +3 -0
  305. package/dist/types/store/contracts/contact.store.d.ts +1 -0
  306. package/dist/types/store/contracts/device-list.store.d.ts +0 -3
  307. package/dist/types/store/contracts/message.store.d.ts +1 -0
  308. package/dist/types/store/contracts/participants.store.d.ts +0 -1
  309. package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
  310. package/dist/types/store/contracts/signal.store.d.ts +6 -0
  311. package/dist/types/store/contracts/thread.store.d.ts +1 -0
  312. package/dist/types/store/index.d.ts +1 -1
  313. package/dist/types/store/providers/memory/appstate.store.d.ts +2 -0
  314. package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
  315. package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
  316. package/dist/types/store/providers/memory/message.store.d.ts +1 -0
  317. package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
  318. package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
  319. package/dist/types/store/providers/memory/signal.store.d.ts +6 -0
  320. package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
  321. package/dist/types/store/providers/sqlite/appstate.store.d.ts +2 -0
  322. package/dist/types/store/providers/sqlite/contact.store.d.ts +2 -0
  323. package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -3
  324. package/dist/types/store/providers/sqlite/message.store.d.ts +2 -0
  325. package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -1
  326. package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -1
  327. package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -1
  328. package/dist/types/store/providers/sqlite/signal.store.d.ts +7 -0
  329. package/dist/types/store/providers/sqlite/table-names.d.ts +5 -0
  330. package/dist/types/store/providers/sqlite/thread.store.d.ts +2 -0
  331. package/dist/types/store/types.d.ts +3 -0
  332. package/dist/types/transport/WaWebSocket.d.ts +3 -0
  333. package/dist/types/transport/index.d.ts +2 -1
  334. package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
  335. package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
  336. package/dist/types/transport/node/builders/group.d.ts +4 -6
  337. package/dist/types/transport/node/builders/index.d.ts +2 -1
  338. package/dist/types/transport/node/builders/message.d.ts +14 -25
  339. package/dist/types/transport/node/builders/retry.d.ts +2 -4
  340. package/dist/types/transport/node/builders/usync.d.ts +21 -0
  341. package/dist/types/transport/node/helpers.d.ts +8 -0
  342. package/dist/types/transport/node/usync.d.ts +2 -0
  343. package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
  344. package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
  345. package/dist/types/transport/proxy.d.ts +6 -0
  346. package/dist/types/transport/stream/parse.d.ts +0 -1
  347. package/dist/types/transport/types.d.ts +18 -1
  348. package/dist/types/util/bytes.d.ts +5 -0
  349. package/dist/types/util/primitives.d.ts +3 -0
  350. package/dist/util/bytes.js +55 -33
  351. package/dist/util/coercion.js +6 -14
  352. package/dist/util/primitives.js +42 -14
  353. package/package.json +27 -9
  354. package/proto/index.d.ts +1090 -1048
  355. package/proto/index.js +1 -1
  356. package/scripts/check-node-version.cjs +0 -1
  357. package/dist/crypto/core/encoding.js +0 -29
  358. package/dist/esm/crypto/core/encoding.js +0 -25
  359. package/dist/esm/util/base64.js +0 -18
  360. package/dist/esm/util/signal-address.js +0 -5
  361. package/dist/types/crypto/core/encoding.d.ts +0 -11
  362. package/dist/types/util/base64.d.ts +0 -4
  363. package/dist/types/util/signal-address.d.ts +0 -2
  364. package/dist/util/base64.js +0 -24
  365. package/dist/util/signal-address.js +0 -8
  366. /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
@@ -1,20 +1,10 @@
1
- import { WA_DEFAULTS, WA_IQ_TYPES, WA_NODE_TAGS } from '../../protocol/constants.js';
1
+ import { WA_DEFAULTS, WA_NODE_TAGS } from '../../protocol/constants.js';
2
2
  import { splitJid } from '../../protocol/jid.js';
3
3
  import { decodeExactLength, parseUint } from '../api/codec.js';
4
4
  import { SIGNAL_KEY_DATA_LENGTH, SIGNAL_KEY_ID_LENGTH, SIGNAL_REGISTRATION_ID_LENGTH, SIGNAL_SIGNATURE_LENGTH } from '../api/constants.js';
5
5
  import { buildMissingPreKeysFetchIq } from '../api/prekeys.js';
6
- import { decodeNodeContentBase64OrBytes, findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
7
- import { parseIqError } from '../../transport/node/query.js';
8
- function parseDeviceId(value, field) {
9
- if (!value) {
10
- throw new Error(`${field} is missing`);
11
- }
12
- const parsed = Number.parseInt(value, 10);
13
- if (!Number.isSafeInteger(parsed) || parsed < 0) {
14
- throw new Error(`${field} is invalid`);
15
- }
16
- return parsed;
17
- }
6
+ import { decodeNodeContentBase64OrBytes, findNodeChildrenByTags, findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
7
+ import { assertIqResult } from '../../transport/node/query.js';
18
8
  export class SignalMissingPreKeysSyncApi {
19
9
  constructor(options) {
20
10
  this.logger = options.logger;
@@ -38,16 +28,7 @@ export class SignalMissingPreKeysSyncApi {
38
28
  return parsed;
39
29
  }
40
30
  parseFetchMissingPreKeysResponse(node, requestedTargets) {
41
- if (node.tag !== WA_NODE_TAGS.IQ) {
42
- throw new Error(`invalid missing prekeys response tag: ${node.tag}`);
43
- }
44
- if (node.attrs.type === WA_IQ_TYPES.ERROR) {
45
- const error = parseIqError(node);
46
- throw new Error(`missing prekeys iq failed (${error.code}: ${error.text})`);
47
- }
48
- if (node.attrs.type !== WA_IQ_TYPES.RESULT) {
49
- throw new Error(`invalid missing prekeys response type: ${node.attrs.type ?? 'unknown'}`);
50
- }
31
+ assertIqResult(node, 'missing prekeys');
51
32
  const listNode = findNodeChild(node, WA_NODE_TAGS.LIST);
52
33
  if (!listNode) {
53
34
  throw new Error('missing prekeys response missing list node');
@@ -75,67 +56,94 @@ export class SignalMissingPreKeysSyncApi {
75
56
  devices: this.parseUserDevices(userNode, userJid)
76
57
  });
77
58
  }
78
- return requestedTargets.map((target) => {
79
- return (parsedByJid.get(target.userJid) ?? {
59
+ const results = new Array(requestedTargets.length);
60
+ for (let index = 0; index < requestedTargets.length; index += 1) {
61
+ const target = requestedTargets[index];
62
+ results[index] = parsedByJid.get(target.userJid) ?? {
80
63
  userJid: target.userJid,
81
64
  errorText: 'missing user in key_fetch response'
82
- });
83
- });
65
+ };
66
+ }
67
+ return results;
84
68
  }
85
69
  parseUserDevices(node, userJid) {
86
70
  const { user, server } = splitJid(userJid);
87
71
  const devices = getNodeChildrenByTag(node, WA_NODE_TAGS.DEVICE);
88
- return devices.map((deviceNode, index) => {
89
- const deviceId = parseDeviceId(deviceNode.attrs.id, `missing prekeys user[${userJid}] device[${index}].id`);
90
- const registrationNode = findNodeChild(deviceNode, WA_NODE_TAGS.REGISTRATION);
91
- const identityNode = findNodeChild(deviceNode, WA_NODE_TAGS.IDENTITY);
92
- const signedKeyNode = findNodeChild(deviceNode, WA_NODE_TAGS.SKEY);
72
+ const bundles = new Array(devices.length);
73
+ for (let index = 0; index < devices.length; index += 1) {
74
+ const deviceNode = devices[index];
75
+ const deviceIdValue = deviceNode.attrs.id;
76
+ if (!deviceIdValue) {
77
+ throw new Error(`missing prekeys device[${index}].id is missing`);
78
+ }
79
+ const deviceId = Number.parseInt(deviceIdValue, 10);
80
+ if (!Number.isSafeInteger(deviceId) || deviceId < 0) {
81
+ throw new Error(`missing prekeys device[${index}].id is invalid`);
82
+ }
83
+ const [registrationNode, identityNode, signedKeyNode, oneTimeNode, deviceIdentityNode] = findNodeChildrenByTags(deviceNode, [
84
+ WA_NODE_TAGS.REGISTRATION,
85
+ WA_NODE_TAGS.IDENTITY,
86
+ WA_NODE_TAGS.SKEY,
87
+ WA_NODE_TAGS.KEY,
88
+ WA_NODE_TAGS.DEVICE_IDENTITY
89
+ ]);
93
90
  if (!registrationNode || !identityNode || !signedKeyNode) {
94
91
  throw new Error(`missing prekeys device payload is incomplete for ${userJid}`);
95
92
  }
96
- const signedKeyIdNode = findNodeChild(signedKeyNode, WA_NODE_TAGS.ID);
97
- const signedKeyValueNode = findNodeChild(signedKeyNode, WA_NODE_TAGS.VALUE);
98
- const signedKeySignatureNode = findNodeChild(signedKeyNode, WA_NODE_TAGS.SIGNATURE);
93
+ const [signedKeyIdNode, signedKeyValueNode, signedKeySignatureNode] = findNodeChildrenByTags(signedKeyNode, [
94
+ WA_NODE_TAGS.ID,
95
+ WA_NODE_TAGS.VALUE,
96
+ WA_NODE_TAGS.SIGNATURE
97
+ ]);
99
98
  if (!signedKeyIdNode || !signedKeyValueNode || !signedKeySignatureNode) {
100
99
  throw new Error(`missing prekeys signed pre-key is incomplete for ${userJid}`);
101
100
  }
102
- const oneTimeNode = findNodeChild(deviceNode, WA_NODE_TAGS.KEY);
103
- const oneTimeIdNode = oneTimeNode
104
- ? findNodeChild(oneTimeNode, WA_NODE_TAGS.ID)
105
- : undefined;
106
- const oneTimeValueNode = oneTimeNode
107
- ? findNodeChild(oneTimeNode, WA_NODE_TAGS.VALUE)
108
- : undefined;
101
+ let oneTimeIdNode;
102
+ let oneTimeValueNode;
103
+ if (oneTimeNode) {
104
+ const oneTimeNodes = findNodeChildrenByTags(oneTimeNode, [
105
+ WA_NODE_TAGS.ID,
106
+ WA_NODE_TAGS.VALUE
107
+ ]);
108
+ oneTimeIdNode = oneTimeNodes[0];
109
+ oneTimeValueNode = oneTimeNodes[1];
110
+ }
109
111
  if (oneTimeNode && (!oneTimeIdNode || !oneTimeValueNode)) {
110
112
  throw new Error(`missing prekeys one-time key is incomplete for ${userJid}`);
111
113
  }
112
- const deviceIdentityNode = findNodeChild(deviceNode, WA_NODE_TAGS.DEVICE_IDENTITY);
113
- const bundle = {
114
+ const baseBundle = {
114
115
  regId: parseUint(decodeExactLength(registrationNode.content, 'missing prekeys device registration', SIGNAL_REGISTRATION_ID_LENGTH), 'missing prekeys device registration'),
115
116
  identity: decodeExactLength(identityNode.content, 'missing prekeys device identity', SIGNAL_KEY_DATA_LENGTH),
116
117
  signedKey: {
117
118
  id: parseUint(decodeExactLength(signedKeyIdNode.content, 'missing prekeys device skey.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device skey.id'),
118
119
  publicKey: decodeExactLength(signedKeyValueNode.content, 'missing prekeys device skey.value', SIGNAL_KEY_DATA_LENGTH),
119
120
  signature: decodeExactLength(signedKeySignatureNode.content, 'missing prekeys device skey.signature', SIGNAL_SIGNATURE_LENGTH)
120
- },
121
- ...(oneTimeIdNode && oneTimeValueNode
122
- ? {
123
- oneTimeKey: {
124
- id: parseUint(decodeExactLength(oneTimeIdNode.content, 'missing prekeys device key.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device key.id'),
125
- publicKey: decodeExactLength(oneTimeValueNode.content, 'missing prekeys device key.value', SIGNAL_KEY_DATA_LENGTH)
126
- }
127
- }
128
- : {})
121
+ }
129
122
  };
130
- return {
131
- deviceJid: deviceId === 0 ? userJid : `${user}:${deviceId}@${server}`,
132
- bundle,
133
- ...(deviceIdentityNode
134
- ? {
135
- deviceIdentity: decodeNodeContentBase64OrBytes(deviceIdentityNode.content, 'missing prekeys device device-identity')
123
+ const bundle = oneTimeIdNode && oneTimeValueNode
124
+ ? {
125
+ ...baseBundle,
126
+ oneTimeKey: {
127
+ id: parseUint(decodeExactLength(oneTimeIdNode.content, 'missing prekeys device key.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device key.id'),
128
+ publicKey: decodeExactLength(oneTimeValueNode.content, 'missing prekeys device key.value', SIGNAL_KEY_DATA_LENGTH)
136
129
  }
137
- : {})
138
- };
139
- });
130
+ }
131
+ : baseBundle;
132
+ let deviceIdentity;
133
+ if (deviceIdentityNode) {
134
+ deviceIdentity = decodeNodeContentBase64OrBytes(deviceIdentityNode.content, 'missing prekeys device device-identity');
135
+ }
136
+ bundles[index] = deviceIdentity
137
+ ? {
138
+ deviceJid: deviceId === 0 ? userJid : `${user}:${deviceId}@${server}`,
139
+ bundle,
140
+ deviceIdentity
141
+ }
142
+ : {
143
+ deviceJid: deviceId === 0 ? userJid : `${user}:${deviceId}@${server}`,
144
+ bundle
145
+ };
146
+ }
147
+ return bundles;
140
148
  }
141
149
  }
@@ -2,6 +2,7 @@ import { WA_DEFAULTS, WA_IQ_TYPES, WA_NODE_TAGS, WA_XMLNS } from '../../protocol
2
2
  import { decodeExactLength, parseUint } from '../api/codec.js';
3
3
  import { SIGNAL_KEY_DATA_LENGTH, SIGNAL_KEY_ID_LENGTH, SIGNAL_REGISTRATION_ID_LENGTH, SIGNAL_SIGNATURE_LENGTH } from '../api/constants.js';
4
4
  import { findNodeChild, getNodeChildrenByTag, decodeNodeContentBase64OrBytes } from '../../transport/node/helpers.js';
5
+ import { assertIqResult } from '../../transport/node/query.js';
5
6
  export class SignalSessionSyncApi {
6
7
  constructor(options) {
7
8
  this.logger = options.logger;
@@ -40,7 +41,20 @@ export class SignalSessionSyncApi {
40
41
  reasonIdentity: (previous?.reasonIdentity ?? false) || target.reasonIdentity === true
41
42
  });
42
43
  }
43
- const mergedTargets = [...targetByJid.values()];
44
+ const mergedTargets = [];
45
+ for (const target of targetByJid.values()) {
46
+ mergedTargets.push(target);
47
+ }
48
+ const userNodes = new Array(mergedTargets.length);
49
+ for (let index = 0; index < mergedTargets.length; index += 1) {
50
+ const target = mergedTargets[index];
51
+ userNodes[index] = {
52
+ tag: WA_NODE_TAGS.USER,
53
+ attrs: target.reasonIdentity === true
54
+ ? { jid: target.jid, reason: 'identity' }
55
+ : { jid: target.jid }
56
+ };
57
+ }
44
58
  this.logger.debug('signal fetch key bundles request', {
45
59
  targets: mergedTargets.length,
46
60
  timeoutMs
@@ -56,34 +70,14 @@ export class SignalSessionSyncApi {
56
70
  {
57
71
  tag: WA_NODE_TAGS.KEY,
58
72
  attrs: {},
59
- content: mergedTargets.map((target) => ({
60
- tag: WA_NODE_TAGS.USER,
61
- attrs: {
62
- jid: target.jid,
63
- ...(target.reasonIdentity === true ? { reason: 'identity' } : {})
64
- }
65
- }))
73
+ content: userNodes
66
74
  }
67
75
  ]
68
76
  }, timeoutMs);
69
77
  return this.parseFetchKeyBundleResponse(responseNode, mergedTargets);
70
78
  }
71
79
  parseFetchKeyBundleResponse(node, requestedTargets) {
72
- if (node.tag !== WA_NODE_TAGS.IQ) {
73
- throw new Error(`invalid key bundle response tag: ${node.tag}`);
74
- }
75
- if (node.attrs.type === WA_IQ_TYPES.ERROR) {
76
- const errorNode = findNodeChild(node, WA_NODE_TAGS.ERROR);
77
- if (!errorNode) {
78
- throw new Error(`key bundle iq error for ${node.attrs.id ?? 'unknown id'}`);
79
- }
80
- const code = errorNode.attrs.code ?? 'unknown';
81
- const text = errorNode.attrs.text ?? errorNode.attrs.type ?? 'unknown';
82
- throw new Error(`key bundle iq error (${code} ${text})`);
83
- }
84
- if (node.attrs.type !== WA_IQ_TYPES.RESULT) {
85
- throw new Error(`invalid key bundle response type: ${node.attrs.type ?? 'unknown'}`);
86
- }
80
+ assertIqResult(node, 'key bundle');
87
81
  const listNode = findNodeChild(node, WA_NODE_TAGS.LIST);
88
82
  if (!listNode) {
89
83
  throw new Error('key bundle response missing list node');
@@ -115,16 +109,15 @@ export class SignalSessionSyncApi {
115
109
  ...(parsed.deviceIdentity ? { deviceIdentity: parsed.deviceIdentity } : {})
116
110
  });
117
111
  }
118
- return requestedTargets.map((target) => {
119
- const parsed = parsedByJid.get(target.jid);
120
- if (parsed) {
121
- return parsed;
122
- }
123
- return {
112
+ const output = new Array(requestedTargets.length);
113
+ for (let index = 0; index < requestedTargets.length; index += 1) {
114
+ const target = requestedTargets[index];
115
+ output[index] = parsedByJid.get(target.jid) ?? {
124
116
  jid: target.jid,
125
117
  errorText: 'missing key bundle user in response'
126
118
  };
127
- });
119
+ }
120
+ return output;
128
121
  }
129
122
  parseUserKeyBundle(node) {
130
123
  const registrationNode = findNodeChild(node, WA_NODE_TAGS.REGISTRATION);
@@ -4,10 +4,10 @@ import { encodeExtendedPoint, scalarMultBase } from '../../crypto/math/edwards.j
4
4
  import { bytesToBigIntLE, bigIntToBytesLE } from '../../crypto/math/le.js';
5
5
  import { modGroup } from '../../crypto/math/mod.js';
6
6
  import { ADV_PREFIX_ACCOUNT_SIGNATURE, ADV_PREFIX_DEVICE_SIGNATURE, ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, ADV_PREFIX_HOSTED_DEVICE_SIGNATURE, SIGNAL_PREFIX_SIGNATURE_RANDOM } from '../crypto/constants.js';
7
- import { concatBytes } from '../../util/bytes.js';
7
+ import { assertByteLength, concatBytes } from '../../util/bytes.js';
8
8
  export { ADV_PREFIX_ACCOUNT_SIGNATURE, ADV_PREFIX_DEVICE_SIGNATURE, ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, ADV_PREFIX_HOSTED_DEVICE_SIGNATURE } from '../crypto/constants.js';
9
9
  export async function verifySignalSignature(publicKey, message, signature) {
10
- if (signature.length !== 64) {
10
+ if (!assertByteLength(signature, 64, 'invalid signal signature length', false)) {
11
11
  return false;
12
12
  }
13
13
  if ((signature[63] & 0x60) !== 0) {
@@ -21,9 +21,7 @@ export async function verifySignalSignature(publicKey, message, signature) {
21
21
  return ed25519VerifyRaw(edPublic, signalSignature, message);
22
22
  }
23
23
  export async function signSignalMessage(privateKey, message) {
24
- if (privateKey.length !== 32) {
25
- throw new Error(`invalid curve25519 private key length ${privateKey.length}`);
26
- }
24
+ assertByteLength(privateKey, 32, `invalid curve25519 private key length ${privateKey.length}`);
27
25
  const clampedPrivateKey = clampCurvePrivateKeyInPlace(privateKey);
28
26
  const privateScalar = bytesToBigIntLE(clampedPrivateKey);
29
27
  const encodedPublic = encodeExtendedPoint(scalarMultBase(privateScalar));
@@ -1,6 +1,6 @@
1
1
  import { hkdf, importHmacKey, hmacSign } from '../../crypto/index.js';
2
2
  import { CHAIN_KEY_LABEL, MAX_UNUSED_KEYS, MESSAGE_KEY_LABEL, SENDER_KEY_FUTURE_MESSAGES_MAX, WHISPER_GROUP_INFO } from '../constants.js';
3
- import { removeAt } from '../../util/bytes.js';
3
+ import { assertByteLength, removeAt } from '../../util/bytes.js';
4
4
  export async function selectMessageKey(senderKey, targetIteration) {
5
5
  const delta = targetIteration - senderKey.iteration;
6
6
  if (delta > SENDER_KEY_FUTURE_MESSAGES_MAX) {
@@ -26,24 +26,33 @@ export async function selectMessageKey(senderKey, targetIteration) {
26
26
  const firstDerived = await deriveSenderKeyMsgKeyFromState(senderKey.iteration, chainState);
27
27
  chainState = firstDerived.nextState;
28
28
  let messageKey = firstDerived.messageKey;
29
- let nextUnused = currentUnused.slice();
30
- if (delta > 0) {
31
- let overflow = delta + currentUnused.length - MAX_UNUSED_KEYS;
29
+ if (delta === 0) {
30
+ return {
31
+ messageKey,
32
+ updatedRecord: {
33
+ ...senderKey,
34
+ iteration: targetIteration + 1,
35
+ chainKey: chainState.chainKey,
36
+ unusedMessageKeys: currentUnused
37
+ }
38
+ };
39
+ }
40
+ const nextUnused = currentUnused.slice();
41
+ let overflow = delta + currentUnused.length - MAX_UNUSED_KEYS;
42
+ if (overflow > 0) {
43
+ nextUnused.splice(0, overflow);
44
+ overflow -= currentUnused.length;
45
+ }
46
+ for (let iteration = senderKey.iteration + 1; iteration <= targetIteration; iteration += 1) {
32
47
  if (overflow > 0) {
33
- nextUnused = nextUnused.slice(overflow);
34
- overflow -= currentUnused.length;
48
+ overflow -= 1;
35
49
  }
36
- for (let iteration = senderKey.iteration + 1; iteration <= targetIteration; iteration += 1) {
37
- if (overflow > 0) {
38
- overflow -= 1;
39
- }
40
- else {
41
- nextUnused.push(messageKey);
42
- }
43
- const derived = await deriveSenderKeyMsgKeyFromState(iteration, chainState);
44
- chainState = derived.nextState;
45
- messageKey = derived.messageKey;
50
+ else {
51
+ nextUnused.push(messageKey);
46
52
  }
53
+ const derived = await deriveSenderKeyMsgKeyFromState(iteration, chainState);
54
+ chainState = derived.nextState;
55
+ messageKey = derived.messageKey;
47
56
  }
48
57
  return {
49
58
  messageKey,
@@ -64,20 +73,16 @@ export async function deriveSenderKeyMsgKey(iteration, chainKey) {
64
73
  };
65
74
  }
66
75
  async function createSenderChainState(chainKey) {
67
- if (chainKey.length !== 32) {
68
- throw new Error('sender key chainKey must be 32 bytes');
69
- }
76
+ assertByteLength(chainKey, 32, 'sender key chainKey must be 32 bytes');
70
77
  return {
71
78
  chainKey,
72
79
  hmacKey: await importHmacKey(chainKey)
73
80
  };
74
81
  }
75
82
  async function deriveSenderKeyMsgKeyFromState(iteration, state) {
76
- const nextChainRawPromise = hmacSign(state.hmacKey, CHAIN_KEY_LABEL);
77
- const messageInputKeyPromise = hmacSign(state.hmacKey, MESSAGE_KEY_LABEL);
78
83
  const [nextChainRaw, messageInputKey] = await Promise.all([
79
- nextChainRawPromise,
80
- messageInputKeyPromise
84
+ hmacSign(state.hmacKey, CHAIN_KEY_LABEL),
85
+ hmacSign(state.hmacKey, MESSAGE_KEY_LABEL)
81
86
  ]);
82
87
  const nextChainKey = nextChainRaw.subarray(0, 32);
83
88
  const [nextHmacKey, messageSeed] = await Promise.all([
@@ -1,7 +1,7 @@
1
1
  import { readVersionedContent, toSerializedPubKey } from '../../crypto/index.js';
2
2
  import { proto } from '../../proto.js';
3
3
  import { SIGNATURE_SIZE, SIGNAL_GROUP_VERSION } from '../constants.js';
4
- import { toBytesView } from '../../util/bytes.js';
4
+ import { assertByteLength, toBytesView } from '../../util/bytes.js';
5
5
  export function parseDistributionPayload(payload) {
6
6
  const body = readVersionedContent(payload, SIGNAL_GROUP_VERSION, 0);
7
7
  const decoded = proto.SenderKeyDistributionMessage.decode(body);
@@ -16,9 +16,7 @@ export function parseDistributionPayload(payload) {
16
16
  throw new Error('invalid sender key distribution message');
17
17
  }
18
18
  const chainKey = toBytesView(decoded.chainKey);
19
- if (chainKey.length !== 32) {
20
- throw new Error('sender key distribution chainKey must be 32 bytes');
21
- }
19
+ assertByteLength(chainKey, 32, 'sender key distribution chainKey must be 32 bytes');
22
20
  return {
23
21
  keyId: decoded.id,
24
22
  iteration: decoded.iteration,
@@ -63,12 +63,16 @@ export class SenderKeyManager {
63
63
  }
64
64
  const senderKey = await this.ensureSenderKey(groupId, sender);
65
65
  const timestampMs = Date.now();
66
- await this.store.upsertSenderKeyDistributions(participants.map((participant) => ({
67
- groupId,
68
- sender: participant,
69
- keyId: senderKey.keyId,
70
- timestampMs
71
- })));
66
+ const distributions = new Array(participants.length);
67
+ for (let index = 0; index < participants.length; index += 1) {
68
+ distributions[index] = {
69
+ groupId,
70
+ sender: participants[index],
71
+ keyId: senderKey.keyId,
72
+ timestampMs
73
+ };
74
+ }
75
+ await this.store.upsertSenderKeyDistributions(distributions);
72
76
  }
73
77
  async processSenderKeyDistributionPayload(groupId, sender, payload) {
74
78
  if (groupId.length === 0) {
@@ -84,13 +88,15 @@ export class SenderKeyManager {
84
88
  signingPublicKey: parsed.signingPublicKey,
85
89
  unusedMessageKeys: []
86
90
  };
87
- await this.store.upsertSenderKey(record);
88
- await this.store.upsertSenderKeyDistribution({
89
- groupId,
90
- sender,
91
- keyId: parsed.keyId,
92
- timestampMs: Date.now()
93
- });
91
+ await Promise.all([
92
+ this.store.upsertSenderKey(record),
93
+ this.store.upsertSenderKeyDistribution({
94
+ groupId,
95
+ sender,
96
+ keyId: parsed.keyId,
97
+ timestampMs: Date.now()
98
+ })
99
+ ]);
94
100
  return record;
95
101
  }
96
102
  async encryptGroupMessage(groupId, sender, plaintext) {
@@ -159,13 +165,17 @@ export class SenderKeyManager {
159
165
  if (existing) {
160
166
  return existing;
161
167
  }
162
- const signingKeyPair = await X25519.generateKeyPair();
168
+ const [signingKeyPair, keyId, chainKey] = await Promise.all([
169
+ X25519.generateKeyPair(),
170
+ randomIntAsync(1, 2147483647),
171
+ randomBytesAsync(32)
172
+ ]);
163
173
  const created = {
164
174
  groupId,
165
175
  sender,
166
- keyId: await randomIntAsync(1, 2147483647),
176
+ keyId,
167
177
  iteration: 0,
168
- chainKey: await randomBytesAsync(32),
178
+ chainKey,
169
179
  signingPublicKey: toSerializedPubKey(signingKeyPair.pubKey),
170
180
  signingPrivateKey: signingKeyPair.privKey,
171
181
  unusedMessageKeys: []
@@ -9,3 +9,4 @@ export { SignalSessionSyncApi } from './api/SignalSessionSyncApi.js';
9
9
  export { SenderKeyManager } from './group/SenderKeyManager.js';
10
10
  export { createAndStoreInitialKeys } from './registration/utils.js';
11
11
  export { SignalProtocol } from './session/SignalProtocol.js';
12
+ export { createSignalSessionResolver } from './session/resolver.js';
@@ -5,6 +5,9 @@ import { decryptMsg, decryptMsgFromSession, encryptMsg } from '../session/Signal
5
5
  import { deserializeMsg, deserializePkMsg, requirePreKey, requireSignedPreKey } from '../session/SignalSerializer.js';
6
6
  import { detachSession, findMatchingSession, generateSerializedKeyPair, initiateSessionIncoming, initiateSessionOutgoing, requireLocalIdentity, toSerializedKeyPair } from '../session/SignalSession.js';
7
7
  import { uint8Equal } from '../../util/bytes.js';
8
+ function signalAddressMapKey(address) {
9
+ return `${address.user}\u0001${address.server ?? ''}\u0001${address.device}`;
10
+ }
8
11
  export class SignalProtocol {
9
12
  constructor(store, logger = new ConsoleLogger('info')) {
10
13
  this.store = store;
@@ -27,23 +30,55 @@ export class SignalProtocol {
27
30
  return session;
28
31
  }
29
32
  async encryptMessage(address, plaintext, expectedIdentity) {
30
- const session = await this.store.getSession(address);
31
- if (!session) {
32
- throw new Error('signal session not found');
33
+ const [encrypted] = await this.encryptMessagesBatch([
34
+ { address, plaintext, expectedIdentity }
35
+ ]);
36
+ return encrypted;
37
+ }
38
+ async encryptMessagesBatch(requests) {
39
+ if (requests.length === 0) {
40
+ return [];
33
41
  }
34
- if (expectedIdentity &&
35
- !uint8Equal(toSerializedPubKey(expectedIdentity), session.remote.pubKey)) {
36
- throw new Error('identity mismatch');
42
+ const addresses = requests.map((request) => request.address);
43
+ const storedSessions = await this.store.getSessionsBatch(addresses);
44
+ const latestSessionByAddress = new Map();
45
+ const sessionUpdatesByAddress = new Map();
46
+ const identityUpdatesByAddress = new Map();
47
+ const results = new Array(requests.length);
48
+ for (let index = 0; index < requests.length; index += 1) {
49
+ const request = requests[index];
50
+ const address = request.address;
51
+ const addressKey = signalAddressMapKey(address);
52
+ const session = latestSessionByAddress.get(addressKey) ?? storedSessions[index];
53
+ if (!session) {
54
+ throw new Error('signal session not found');
55
+ }
56
+ if (request.expectedIdentity &&
57
+ !uint8Equal(toSerializedPubKey(request.expectedIdentity), session.remote.pubKey)) {
58
+ throw new Error('identity mismatch');
59
+ }
60
+ const [updatedSession, encrypted] = await encryptMsg(session, request.plaintext);
61
+ latestSessionByAddress.set(addressKey, updatedSession);
62
+ sessionUpdatesByAddress.set(addressKey, {
63
+ address,
64
+ session: updatedSession
65
+ });
66
+ if (!uint8Equal(updatedSession.remote.pubKey, session.remote.pubKey)) {
67
+ identityUpdatesByAddress.set(addressKey, {
68
+ address,
69
+ identityKey: updatedSession.remote.pubKey
70
+ });
71
+ }
72
+ results[index] = {
73
+ ...encrypted,
74
+ baseKey: updatedSession.aliceBaseKey
75
+ };
37
76
  }
38
- const [updatedSession, encrypted] = await encryptMsg(session, plaintext);
39
- await this.store.setSession(address, updatedSession);
40
- if (!uint8Equal(updatedSession.remote.pubKey, session.remote.pubKey)) {
41
- await this.store.setRemoteIdentity(address, updatedSession.remote.pubKey);
77
+ await this.store.setSessionsBatch([...sessionUpdatesByAddress.values()]);
78
+ if (identityUpdatesByAddress.size > 0) {
79
+ await this.store.setRemoteIdentities([...identityUpdatesByAddress.values()]);
42
80
  }
43
- return {
44
- ...encrypted,
45
- baseKey: updatedSession.aliceBaseKey
46
- };
81
+ return results;
47
82
  }
48
83
  async decryptMessage(address, envelope) {
49
84
  const currentSession = await this.store.getSession(address);
@@ -51,24 +51,33 @@ export async function selectMessageKey(chain, targetCounter) {
51
51
  const first = await deriveMsgKeyFromState(chain.nextMsgIndex, chainState);
52
52
  let currentMessageKey = first.messageKey;
53
53
  chainState = first.nextState;
54
- let nextUnused = unused.slice();
55
- if (delta > 0) {
56
- let overflow = delta + unused.length - MAX_UNUSED_KEYS;
54
+ if (delta === 0) {
55
+ return {
56
+ messageKey: currentMessageKey,
57
+ updatedChain: {
58
+ ratchetPubKey: chain.ratchetPubKey,
59
+ nextMsgIndex: targetCounter + 1,
60
+ chainKey: chainState.chainKey,
61
+ unusedMsgKeys: unused
62
+ }
63
+ };
64
+ }
65
+ const nextUnused = unused.slice();
66
+ let overflow = delta + unused.length - MAX_UNUSED_KEYS;
67
+ if (overflow > 0) {
68
+ nextUnused.splice(0, overflow);
69
+ overflow -= unused.length;
70
+ }
71
+ for (let counter = chain.nextMsgIndex + 1; counter <= targetCounter; counter += 1) {
57
72
  if (overflow > 0) {
58
- nextUnused = nextUnused.slice(overflow);
59
- overflow -= unused.length;
73
+ overflow -= 1;
60
74
  }
61
- for (let counter = chain.nextMsgIndex + 1; counter <= targetCounter; counter += 1) {
62
- if (overflow > 0) {
63
- overflow -= 1;
64
- }
65
- else {
66
- nextUnused.push(currentMessageKey);
67
- }
68
- const derived = await deriveMsgKeyFromState(counter, chainState);
69
- currentMessageKey = derived.messageKey;
70
- chainState = derived.nextState;
75
+ else {
76
+ nextUnused.push(currentMessageKey);
71
77
  }
78
+ const derived = await deriveMsgKeyFromState(counter, chainState);
79
+ currentMessageKey = derived.messageKey;
80
+ chainState = derived.nextState;
72
81
  }
73
82
  return {
74
83
  messageKey: currentMessageKey,
@@ -20,16 +20,21 @@ export function findMatchingSession(session, sessionBaseKey) {
20
20
  if (session.aliceBaseKey && uint8Equal(session.aliceBaseKey, serializedBaseKey)) {
21
21
  return session;
22
22
  }
23
- const previousSessionIndex = session.prevSessions.findIndex((prev) => !!prev.aliceBaseKey && uint8Equal(prev.aliceBaseKey, serializedBaseKey));
24
- if (previousSessionIndex !== -1) {
25
- const promoted = snapshotToRecord(session.prevSessions[previousSessionIndex]);
23
+ for (let index = 0; index < session.prevSessions.length; index += 1) {
24
+ const previousSession = session.prevSessions[index];
25
+ if (!previousSession.aliceBaseKey ||
26
+ !uint8Equal(previousSession.aliceBaseKey, serializedBaseKey)) {
27
+ continue;
28
+ }
29
+ const prevSessions = [detachSession(session)];
30
+ for (let i = 0; i < session.prevSessions.length; i += 1) {
31
+ if (i !== index) {
32
+ prevSessions.push(session.prevSessions[i]);
33
+ }
34
+ }
26
35
  return {
27
- ...promoted,
28
- prevSessions: [
29
- detachSession(session),
30
- ...session.prevSessions.slice(0, previousSessionIndex),
31
- ...session.prevSessions.slice(previousSessionIndex + 1)
32
- ]
36
+ ...previousSession,
37
+ prevSessions
33
38
  };
34
39
  }
35
40
  return null;