zapo-js 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (589) hide show
  1. package/README.md +20 -4
  2. package/dist/appstate/WaAppStateCrypto.js +19 -26
  3. package/dist/appstate/WaAppStateSyncClient.js +293 -181
  4. package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
  5. package/dist/appstate/constants.js +4 -3
  6. package/dist/appstate/{store/sqlite.js → encoding.js} +13 -8
  7. package/dist/appstate/index.js +8 -6
  8. package/dist/appstate/utils.js +9 -34
  9. package/dist/auth/WaAuthClient.js +43 -61
  10. package/dist/auth/flow/WaAuthCredentialsFlow.js +22 -15
  11. package/dist/auth/index.js +1 -8
  12. package/dist/auth/pairing/WaPairingCodeCrypto.js +6 -4
  13. package/dist/auth/pairing/WaPairingFlow.js +34 -26
  14. package/dist/auth/pairing/WaQrFlow.js +37 -24
  15. package/dist/client/WaClient.js +275 -324
  16. package/dist/client/WaClientFactory.js +500 -133
  17. package/dist/client/connection/WaConnectionManager.js +301 -0
  18. package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
  19. package/dist/client/connection/WaReceiptQueue.js +51 -0
  20. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
  21. package/dist/client/coordinators/WaBusinessCoordinator.js +241 -0
  22. package/dist/client/coordinators/WaGroupCoordinator.js +30 -16
  23. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
  24. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +439 -701
  25. package/dist/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  26. package/dist/client/coordinators/WaPrivacyCoordinator.js +134 -0
  27. package/dist/client/coordinators/WaProfileCoordinator.js +212 -0
  28. package/dist/client/coordinators/WaRetryCoordinator.js +242 -57
  29. package/dist/client/coordinators/WaStreamControlCoordinator.js +18 -11
  30. package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +166 -0
  31. package/dist/client/dirty.js +74 -48
  32. package/dist/client/events/chat.js +4 -3
  33. package/dist/client/events/devices.js +72 -0
  34. package/dist/client/events/group.js +62 -47
  35. package/dist/client/events/identity.js +22 -0
  36. package/dist/client/events/privacy-token.js +39 -0
  37. package/dist/client/history-sync.js +94 -63
  38. package/dist/client/incoming.js +60 -27
  39. package/dist/client/mailbox.js +24 -23
  40. package/dist/client/messages.js +107 -31
  41. package/dist/client/messaging/fanout.js +199 -0
  42. package/dist/client/messaging/key-protocol.js +130 -0
  43. package/dist/client/messaging/participants.js +193 -0
  44. package/dist/client/persistence/WriteBehindPersistence.js +129 -0
  45. package/dist/client/tokens/cs-token.js +50 -0
  46. package/dist/client/tokens/tc-token.js +25 -0
  47. package/dist/crypto/core/hkdf.js +3 -8
  48. package/dist/crypto/core/index.js +2 -5
  49. package/dist/crypto/core/keys.js +6 -7
  50. package/dist/crypto/core/nonce.js +2 -0
  51. package/dist/crypto/core/primitives.js +12 -23
  52. package/dist/crypto/core/random.js +26 -23
  53. package/dist/crypto/curves/Ed25519.js +7 -8
  54. package/dist/crypto/curves/X25519.js +38 -22
  55. package/dist/crypto/index.js +1 -3
  56. package/dist/crypto/math/constants.js +13 -36
  57. package/dist/crypto/math/edwards.js +171 -44
  58. package/dist/crypto/math/fe.js +706 -0
  59. package/dist/crypto/math/mod.js +10 -3
  60. package/dist/esm/appstate/WaAppStateCrypto.js +7 -14
  61. package/dist/esm/appstate/WaAppStateSyncClient.js +284 -172
  62. package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
  63. package/dist/esm/appstate/constants.js +3 -2
  64. package/dist/esm/appstate/{store/sqlite.js → encoding.js} +13 -8
  65. package/dist/esm/appstate/index.js +2 -2
  66. package/dist/esm/appstate/utils.js +8 -30
  67. package/dist/esm/auth/WaAuthClient.js +43 -61
  68. package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +22 -15
  69. package/dist/esm/auth/index.js +0 -3
  70. package/dist/esm/auth/pairing/WaPairingCodeCrypto.js +6 -4
  71. package/dist/esm/auth/pairing/WaPairingFlow.js +28 -20
  72. package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
  73. package/dist/esm/client/WaClient.js +275 -324
  74. package/dist/esm/client/WaClientFactory.js +501 -134
  75. package/dist/esm/client/connection/WaConnectionManager.js +297 -0
  76. package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
  77. package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
  78. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
  79. package/dist/esm/client/coordinators/WaBusinessCoordinator.js +238 -0
  80. package/dist/esm/client/coordinators/WaGroupCoordinator.js +23 -9
  81. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
  82. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +443 -705
  83. package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  84. package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +131 -0
  85. package/dist/esm/client/coordinators/WaProfileCoordinator.js +209 -0
  86. package/dist/esm/client/coordinators/WaRetryCoordinator.js +244 -59
  87. package/dist/esm/client/coordinators/WaStreamControlCoordinator.js +19 -12
  88. package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +162 -0
  89. package/dist/esm/client/dirty.js +69 -43
  90. package/dist/esm/client/events/chat.js +4 -3
  91. package/dist/esm/client/events/devices.js +68 -0
  92. package/dist/esm/client/events/group.js +53 -39
  93. package/dist/esm/client/events/identity.js +19 -0
  94. package/dist/esm/client/events/privacy-token.js +36 -0
  95. package/dist/esm/client/history-sync.js +91 -60
  96. package/dist/esm/client/incoming.js +61 -28
  97. package/dist/esm/client/mailbox.js +24 -23
  98. package/dist/esm/client/messages.js +108 -32
  99. package/dist/esm/client/messaging/fanout.js +196 -0
  100. package/dist/esm/client/messaging/key-protocol.js +127 -0
  101. package/dist/esm/client/messaging/participants.js +190 -0
  102. package/dist/esm/client/persistence/WriteBehindPersistence.js +125 -0
  103. package/dist/esm/client/tokens/cs-token.js +46 -0
  104. package/dist/esm/client/tokens/tc-token.js +18 -0
  105. package/dist/esm/crypto/core/hkdf.js +3 -8
  106. package/dist/esm/crypto/core/index.js +2 -3
  107. package/dist/esm/crypto/core/keys.js +3 -4
  108. package/dist/esm/crypto/core/nonce.js +2 -0
  109. package/dist/esm/crypto/core/primitives.js +12 -22
  110. package/dist/esm/crypto/core/random.js +25 -23
  111. package/dist/esm/crypto/curves/Ed25519.js +4 -5
  112. package/dist/esm/crypto/curves/X25519.js +35 -19
  113. package/dist/esm/crypto/index.js +0 -1
  114. package/dist/esm/crypto/math/constants.js +12 -35
  115. package/dist/esm/crypto/math/edwards.js +174 -47
  116. package/dist/esm/crypto/math/fe.js +691 -0
  117. package/dist/esm/crypto/math/mod.js +10 -1
  118. package/dist/esm/index.js +1 -1
  119. package/dist/esm/infra/log/ConsoleLogger.js +18 -17
  120. package/dist/esm/infra/log/PinoLogger.js +15 -9
  121. package/dist/esm/infra/log/types.js +11 -1
  122. package/dist/esm/infra/perf/BackgroundQueue.js +478 -0
  123. package/dist/esm/infra/perf/BoundedTaskQueue.js +16 -18
  124. package/dist/esm/infra/perf/PromiseDedup.js +20 -0
  125. package/dist/esm/infra/perf/SharedExclusiveGate.js +109 -0
  126. package/dist/esm/infra/perf/StoreLock.js +77 -0
  127. package/dist/esm/media/WaMediaCrypto.js +96 -16
  128. package/dist/esm/media/WaMediaTransferClient.js +251 -91
  129. package/dist/esm/media/conn.js +10 -6
  130. package/dist/esm/media/constants.js +6 -2
  131. package/dist/esm/message/WaMessageClient.js +30 -32
  132. package/dist/esm/message/ack.js +6 -6
  133. package/dist/esm/message/addon-crypto.js +59 -0
  134. package/dist/esm/message/content.js +195 -9
  135. package/dist/esm/message/icdc.js +76 -0
  136. package/dist/esm/message/incoming.js +129 -122
  137. package/dist/esm/message/index.js +2 -0
  138. package/dist/esm/message/phash.js +3 -1
  139. package/dist/esm/message/reporting-token.js +425 -0
  140. package/dist/esm/message/use-case-secret.js +49 -0
  141. package/dist/esm/protocol/appstate.js +27 -0
  142. package/dist/esm/protocol/browser.js +10 -18
  143. package/dist/esm/protocol/constants.js +6 -3
  144. package/dist/esm/protocol/defaults.js +6 -0
  145. package/dist/esm/protocol/index.js +2 -11
  146. package/dist/esm/protocol/jid.js +133 -52
  147. package/dist/esm/protocol/media.js +3 -3
  148. package/dist/esm/protocol/message.js +61 -1
  149. package/dist/esm/protocol/nodes.js +4 -0
  150. package/dist/esm/protocol/notification.js +3 -1
  151. package/dist/esm/protocol/privacy-token.js +17 -0
  152. package/dist/esm/protocol/privacy.js +55 -0
  153. package/dist/esm/protocol/stream.js +26 -1
  154. package/dist/esm/protocol/usync.js +11 -0
  155. package/dist/esm/retry/codec.js +216 -0
  156. package/dist/esm/retry/constants.js +1 -1
  157. package/dist/esm/retry/index.js +3 -2
  158. package/dist/esm/retry/parse.js +88 -86
  159. package/dist/esm/retry/replay.js +54 -51
  160. package/dist/esm/retry/tracker.js +94 -0
  161. package/dist/esm/signal/api/SignalDeviceSyncApi.js +276 -92
  162. package/dist/esm/signal/api/SignalDigestSyncApi.js +17 -8
  163. package/dist/esm/signal/api/SignalIdentitySyncApi.js +67 -37
  164. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +86 -67
  165. package/dist/esm/signal/api/SignalRotateKeyApi.js +4 -2
  166. package/dist/esm/signal/api/SignalSessionSyncApi.js +36 -34
  167. package/dist/esm/signal/api/result-map.js +10 -0
  168. package/dist/esm/signal/constants.js +0 -4
  169. package/dist/esm/signal/crypto/WaAdvSignature.js +13 -9
  170. package/dist/esm/signal/{store/sqlite.js → encoding.js} +93 -60
  171. package/dist/esm/signal/group/SenderKeyChain.js +28 -23
  172. package/dist/esm/signal/group/SenderKeyCodec.js +5 -6
  173. package/dist/esm/signal/group/SenderKeyManager.js +144 -115
  174. package/dist/esm/signal/index.js +2 -0
  175. package/dist/esm/signal/registration/keygen.js +6 -2
  176. package/dist/esm/signal/registration/utils.js +1 -0
  177. package/dist/esm/signal/session/SignalProtocol.js +164 -53
  178. package/dist/esm/signal/session/SignalRatchet.js +24 -15
  179. package/dist/esm/signal/session/SignalSession.js +14 -9
  180. package/dist/esm/signal/session/resolver.js +221 -0
  181. package/dist/esm/store/contracts/privacy-token.store.js +1 -0
  182. package/dist/esm/store/createStore.js +100 -188
  183. package/dist/esm/store/index.js +1 -10
  184. package/dist/esm/store/locks/appstate.lock.js +26 -0
  185. package/dist/esm/store/locks/auth.lock.js +15 -0
  186. package/dist/esm/store/locks/contact.lock.js +20 -0
  187. package/dist/esm/store/locks/device-list.lock.js +20 -0
  188. package/dist/esm/store/locks/message.lock.js +21 -0
  189. package/dist/esm/store/locks/participants.lock.js +20 -0
  190. package/dist/esm/store/locks/privacy-token.lock.js +18 -0
  191. package/dist/esm/store/locks/retry.lock.js +29 -0
  192. package/dist/esm/store/locks/sender-key.lock.js +52 -0
  193. package/dist/esm/store/locks/signal.lock.js +63 -0
  194. package/dist/esm/store/locks/thread.lock.js +21 -0
  195. package/dist/esm/store/noop.store.js +4 -7
  196. package/dist/esm/store/providers/memory/appstate.store.js +38 -16
  197. package/dist/esm/store/providers/memory/contact.store.js +5 -0
  198. package/dist/esm/store/providers/memory/device-list.store.js +12 -34
  199. package/dist/esm/store/providers/memory/message.store.js +11 -5
  200. package/dist/esm/store/providers/memory/participants.store.js +1 -8
  201. package/dist/esm/store/providers/memory/privacy-token.store.js +43 -0
  202. package/dist/esm/store/providers/memory/retry.store.js +77 -2
  203. package/dist/esm/store/providers/memory/sender-key.store.js +11 -8
  204. package/dist/esm/store/providers/memory/signal.store.js +47 -18
  205. package/dist/esm/store/providers/memory/thread.store.js +5 -0
  206. package/dist/esm/transport/WaComms.js +28 -24
  207. package/dist/esm/transport/WaWebSocket.js +115 -18
  208. package/dist/esm/transport/binary/constants.js +0 -30
  209. package/dist/esm/transport/binary/decoder.js +8 -8
  210. package/dist/esm/transport/binary/encoder.js +10 -9
  211. package/dist/esm/transport/binary/index.js +0 -1
  212. package/dist/esm/transport/index.js +1 -0
  213. package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
  214. package/dist/esm/transport/node/WaNodeOrchestrator.js +25 -21
  215. package/dist/esm/transport/node/WaNodeTransport.js +0 -3
  216. package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
  217. package/dist/esm/transport/node/builders/business.js +129 -0
  218. package/dist/esm/transport/node/builders/global.js +370 -0
  219. package/dist/esm/transport/node/builders/index.js +7 -3
  220. package/dist/esm/transport/node/builders/message.js +63 -230
  221. package/dist/esm/transport/node/builders/pairing.js +2 -27
  222. package/dist/esm/transport/node/builders/privacy-token.js +41 -0
  223. package/dist/esm/transport/node/builders/privacy.js +48 -0
  224. package/dist/esm/transport/node/builders/profile.js +70 -0
  225. package/dist/esm/transport/node/builders/retry.js +10 -22
  226. package/dist/esm/transport/node/builders/usync.js +45 -0
  227. package/dist/esm/transport/node/helpers.js +125 -5
  228. package/dist/esm/transport/node/usync.js +5 -0
  229. package/dist/esm/transport/node/xml.js +35 -14
  230. package/dist/esm/transport/noise/WaClientPayload.js +10 -10
  231. package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
  232. package/dist/esm/transport/noise/WaNoiseCert.js +4 -7
  233. package/dist/esm/transport/noise/WaNoiseSession.js +77 -29
  234. package/dist/esm/transport/noise/WaNoiseSocket.js +8 -4
  235. package/dist/esm/transport/proxy.js +27 -0
  236. package/dist/esm/transport/stream/parse.js +17 -48
  237. package/dist/esm/util/bytes.js +67 -45
  238. package/dist/esm/util/coercion.js +6 -14
  239. package/dist/esm/util/index.js +5 -0
  240. package/dist/esm/util/primitives.js +40 -14
  241. package/dist/index.js +7 -1
  242. package/dist/infra/log/ConsoleLogger.js +18 -17
  243. package/dist/infra/log/PinoLogger.js +15 -9
  244. package/dist/infra/log/types.js +12 -0
  245. package/dist/infra/perf/BackgroundQueue.js +482 -0
  246. package/dist/infra/perf/BoundedTaskQueue.js +16 -18
  247. package/dist/infra/perf/PromiseDedup.js +24 -0
  248. package/dist/infra/perf/SharedExclusiveGate.js +113 -0
  249. package/dist/infra/perf/StoreLock.js +81 -0
  250. package/dist/media/WaMediaCrypto.js +95 -15
  251. package/dist/media/WaMediaTransferClient.js +284 -91
  252. package/dist/media/conn.js +10 -6
  253. package/dist/media/constants.js +6 -2
  254. package/dist/message/WaMessageClient.js +31 -33
  255. package/dist/message/ack.js +6 -6
  256. package/dist/message/addon-crypto.js +65 -0
  257. package/dist/message/content.js +198 -9
  258. package/dist/message/icdc.js +81 -0
  259. package/dist/message/incoming.js +127 -120
  260. package/dist/message/index.js +2 -0
  261. package/dist/message/phash.js +3 -1
  262. package/dist/message/reporting-token.js +429 -0
  263. package/dist/message/use-case-secret.js +55 -0
  264. package/dist/protocol/appstate.js +28 -1
  265. package/dist/protocol/browser.js +10 -18
  266. package/dist/protocol/constants.js +26 -1
  267. package/dist/protocol/defaults.js +6 -0
  268. package/dist/protocol/index.js +23 -42
  269. package/dist/protocol/jid.js +140 -52
  270. package/dist/protocol/media.js +3 -3
  271. package/dist/protocol/message.js +62 -2
  272. package/dist/protocol/nodes.js +4 -0
  273. package/dist/protocol/notification.js +3 -1
  274. package/dist/protocol/privacy-token.js +20 -0
  275. package/dist/protocol/privacy.js +58 -0
  276. package/dist/protocol/stream.js +27 -2
  277. package/dist/protocol/usync.js +14 -0
  278. package/dist/retry/codec.js +220 -0
  279. package/dist/retry/constants.js +1 -1
  280. package/dist/retry/index.js +7 -5
  281. package/dist/retry/parse.js +88 -85
  282. package/dist/retry/replay.js +52 -49
  283. package/dist/retry/tracker.js +97 -0
  284. package/dist/signal/api/SignalDeviceSyncApi.js +273 -89
  285. package/dist/signal/api/SignalDigestSyncApi.js +17 -8
  286. package/dist/signal/api/SignalIdentitySyncApi.js +66 -36
  287. package/dist/signal/api/SignalMissingPreKeysSyncApi.js +82 -63
  288. package/dist/signal/api/SignalRotateKeyApi.js +4 -2
  289. package/dist/signal/api/SignalSessionSyncApi.js +36 -34
  290. package/dist/signal/api/result-map.js +13 -0
  291. package/dist/signal/constants.js +1 -5
  292. package/dist/signal/crypto/WaAdvSignature.js +11 -7
  293. package/dist/signal/{store/sqlite.js → encoding.js} +94 -61
  294. package/dist/signal/group/SenderKeyChain.js +27 -22
  295. package/dist/signal/group/SenderKeyCodec.js +5 -6
  296. package/dist/signal/group/SenderKeyManager.js +144 -115
  297. package/dist/signal/index.js +15 -1
  298. package/dist/signal/registration/keygen.js +6 -2
  299. package/dist/signal/registration/utils.js +1 -0
  300. package/dist/signal/session/SignalProtocol.js +164 -53
  301. package/dist/signal/session/SignalRatchet.js +24 -15
  302. package/dist/signal/session/SignalSession.js +14 -9
  303. package/dist/signal/session/resolver.js +224 -0
  304. package/dist/store/contracts/privacy-token.store.js +2 -0
  305. package/dist/store/createStore.js +100 -188
  306. package/dist/store/index.js +15 -33
  307. package/dist/store/locks/appstate.lock.js +29 -0
  308. package/dist/store/locks/auth.lock.js +18 -0
  309. package/dist/store/locks/contact.lock.js +23 -0
  310. package/dist/store/locks/device-list.lock.js +23 -0
  311. package/dist/store/locks/message.lock.js +24 -0
  312. package/dist/store/locks/participants.lock.js +23 -0
  313. package/dist/store/locks/privacy-token.lock.js +21 -0
  314. package/dist/store/locks/retry.lock.js +32 -0
  315. package/dist/store/locks/sender-key.lock.js +55 -0
  316. package/dist/store/locks/signal.lock.js +66 -0
  317. package/dist/store/locks/thread.lock.js +24 -0
  318. package/dist/store/noop.store.js +4 -7
  319. package/dist/store/providers/memory/appstate.store.js +36 -14
  320. package/dist/store/providers/memory/contact.store.js +5 -0
  321. package/dist/store/providers/memory/device-list.store.js +12 -34
  322. package/dist/store/providers/memory/message.store.js +11 -5
  323. package/dist/store/providers/memory/participants.store.js +1 -8
  324. package/dist/store/providers/memory/privacy-token.store.js +47 -0
  325. package/dist/store/providers/memory/retry.store.js +77 -2
  326. package/dist/store/providers/memory/sender-key.store.js +14 -11
  327. package/dist/store/providers/memory/signal.store.js +54 -25
  328. package/dist/store/providers/memory/thread.store.js +5 -0
  329. package/dist/transport/WaComms.js +30 -26
  330. package/dist/transport/WaWebSocket.js +148 -18
  331. package/dist/transport/binary/constants.js +1 -31
  332. package/dist/transport/binary/decoder.js +8 -8
  333. package/dist/transport/binary/encoder.js +10 -9
  334. package/dist/transport/binary/index.js +0 -4
  335. package/dist/transport/index.js +7 -1
  336. package/dist/transport/keepalive/WaKeepAlive.js +1 -7
  337. package/dist/transport/node/WaNodeOrchestrator.js +25 -21
  338. package/dist/transport/node/WaNodeTransport.js +0 -3
  339. package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
  340. package/dist/transport/node/builders/business.js +137 -0
  341. package/dist/transport/node/builders/global.js +375 -0
  342. package/dist/transport/node/builders/index.js +29 -17
  343. package/dist/transport/node/builders/message.js +64 -236
  344. package/dist/transport/node/builders/pairing.js +2 -29
  345. package/dist/transport/node/builders/privacy-token.js +46 -0
  346. package/dist/transport/node/builders/privacy.js +55 -0
  347. package/dist/transport/node/builders/profile.js +78 -0
  348. package/dist/transport/node/builders/retry.js +9 -21
  349. package/dist/transport/node/builders/usync.js +49 -0
  350. package/dist/transport/node/helpers.js +131 -4
  351. package/dist/transport/node/usync.js +8 -0
  352. package/dist/transport/node/xml.js +35 -14
  353. package/dist/transport/noise/WaClientPayload.js +13 -13
  354. package/dist/transport/noise/WaFrameCodec.js +47 -32
  355. package/dist/transport/noise/WaNoiseCert.js +5 -8
  356. package/dist/transport/noise/WaNoiseSession.js +77 -29
  357. package/dist/transport/noise/WaNoiseSocket.js +8 -4
  358. package/dist/transport/proxy.js +34 -0
  359. package/dist/transport/stream/parse.js +20 -52
  360. package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
  361. package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
  362. package/dist/types/appstate/constants.d.ts +1 -0
  363. package/dist/types/appstate/encoding.d.ts +7 -0
  364. package/dist/types/appstate/index.d.ts +3 -3
  365. package/dist/types/appstate/utils.d.ts +0 -3
  366. package/dist/types/auth/WaAuthClient.d.ts +10 -12
  367. package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +1 -1
  368. package/dist/types/auth/index.d.ts +0 -4
  369. package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
  370. package/dist/types/auth/types.d.ts +7 -9
  371. package/dist/types/client/WaClient.d.ts +42 -25
  372. package/dist/types/client/WaClientFactory.d.ts +33 -26
  373. package/dist/types/client/connection/WaConnectionManager.d.ts +66 -0
  374. package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
  375. package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
  376. package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
  377. package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +57 -0
  378. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +3 -2
  379. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +29 -38
  380. package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +4 -0
  381. package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +26 -0
  382. package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +36 -0
  383. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +8 -0
  384. package/dist/types/client/coordinators/WaStreamControlCoordinator.d.ts +3 -2
  385. package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +45 -0
  386. package/dist/types/client/dirty.d.ts +1 -0
  387. package/dist/types/client/events/devices.d.ts +20 -0
  388. package/dist/types/client/events/group.d.ts +2 -1
  389. package/dist/types/client/events/identity.d.ts +9 -0
  390. package/dist/types/client/events/privacy-token.d.ts +7 -0
  391. package/dist/types/client/history-sync.d.ts +9 -6
  392. package/dist/types/client/incoming.d.ts +3 -1
  393. package/dist/types/client/index.d.ts +1 -1
  394. package/dist/types/client/mailbox.d.ts +3 -5
  395. package/dist/types/client/messages.d.ts +1 -2
  396. package/dist/types/client/messaging/fanout.d.ts +14 -0
  397. package/dist/types/client/messaging/key-protocol.d.ts +18 -0
  398. package/dist/types/client/messaging/participants.d.ts +13 -0
  399. package/dist/types/client/persistence/WriteBehindPersistence.d.ts +34 -0
  400. package/dist/types/client/tokens/cs-token.d.ts +10 -0
  401. package/dist/types/client/tokens/tc-token.d.ts +5 -0
  402. package/dist/types/client/types.d.ts +75 -4
  403. package/dist/types/crypto/core/hkdf.d.ts +0 -6
  404. package/dist/types/crypto/core/index.d.ts +2 -3
  405. package/dist/types/crypto/core/nonce.d.ts +2 -0
  406. package/dist/types/crypto/core/primitives.d.ts +0 -1
  407. package/dist/types/crypto/core/random.d.ts +2 -7
  408. package/dist/types/crypto/index.d.ts +0 -1
  409. package/dist/types/crypto/math/constants.d.ts +4 -2
  410. package/dist/types/crypto/math/fe.d.ts +30 -0
  411. package/dist/types/crypto/math/mod.d.ts +0 -2
  412. package/dist/types/crypto/math/types.d.ts +11 -4
  413. package/dist/types/index.d.ts +5 -3
  414. package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
  415. package/dist/types/infra/log/PinoLogger.d.ts +1 -1
  416. package/dist/types/infra/log/types.d.ts +1 -0
  417. package/dist/types/infra/perf/BackgroundQueue.d.ts +58 -0
  418. package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
  419. package/dist/types/infra/perf/PromiseDedup.d.ts +4 -0
  420. package/dist/types/infra/perf/SharedExclusiveGate.d.ts +17 -0
  421. package/dist/types/infra/perf/StoreLock.d.ts +10 -0
  422. package/dist/types/media/WaMediaCrypto.d.ts +3 -2
  423. package/dist/types/media/WaMediaTransferClient.d.ts +16 -15
  424. package/dist/types/media/constants.d.ts +1 -1
  425. package/dist/types/media/index.d.ts +1 -1
  426. package/dist/types/media/types.d.ts +15 -2
  427. package/dist/types/message/addon-crypto.d.ts +25 -0
  428. package/dist/types/message/content.d.ts +8 -0
  429. package/dist/types/message/icdc.d.ts +13 -0
  430. package/dist/types/message/index.d.ts +2 -0
  431. package/dist/types/message/reporting-token.d.ts +18 -0
  432. package/dist/types/message/types.d.ts +45 -6
  433. package/dist/types/message/use-case-secret.d.ts +20 -0
  434. package/dist/types/protocol/appstate.d.ts +47 -0
  435. package/dist/types/protocol/constants.d.ts +8 -3
  436. package/dist/types/protocol/defaults.d.ts +6 -0
  437. package/dist/types/protocol/index.d.ts +2 -11
  438. package/dist/types/protocol/jid.d.ts +22 -5
  439. package/dist/types/protocol/message.d.ts +60 -0
  440. package/dist/types/protocol/nodes.d.ts +4 -0
  441. package/dist/types/protocol/notification.d.ts +2 -0
  442. package/dist/types/protocol/privacy-token.d.ts +17 -0
  443. package/dist/types/protocol/privacy.d.ts +75 -0
  444. package/dist/types/protocol/stream.d.ts +30 -0
  445. package/dist/types/protocol/usync.d.ts +11 -0
  446. package/dist/types/retry/codec.d.ts +3 -0
  447. package/dist/types/retry/index.d.ts +4 -3
  448. package/dist/types/retry/parse.d.ts +5 -2
  449. package/dist/types/retry/replay.d.ts +0 -4
  450. package/dist/types/retry/tracker.d.ts +20 -0
  451. package/dist/types/retry/types.d.ts +10 -4
  452. package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +15 -2
  453. package/dist/types/signal/api/SignalDigestSyncApi.d.ts +6 -0
  454. package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +2 -0
  455. package/dist/types/signal/api/SignalRotateKeyApi.d.ts +4 -5
  456. package/dist/types/signal/api/SignalSessionSyncApi.d.ts +8 -6
  457. package/dist/types/signal/api/result-map.d.ts +1 -0
  458. package/dist/types/signal/constants.d.ts +0 -3
  459. package/dist/types/signal/{store/sqlite.d.ts → encoding.d.ts} +3 -3
  460. package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
  461. package/dist/types/signal/group/SenderKeyManager.d.ts +10 -5
  462. package/dist/types/signal/index.d.ts +3 -0
  463. package/dist/types/signal/session/SignalProtocol.d.ts +19 -4
  464. package/dist/types/signal/session/resolver.d.ts +22 -0
  465. package/dist/types/store/contracts/appstate.store.d.ts +4 -1
  466. package/dist/types/store/contracts/contact.store.d.ts +1 -0
  467. package/dist/types/store/contracts/device-list.store.d.ts +0 -3
  468. package/dist/types/store/contracts/message.store.d.ts +1 -0
  469. package/dist/types/store/contracts/participants.store.d.ts +0 -1
  470. package/dist/types/store/contracts/privacy-token.store.d.ts +16 -0
  471. package/dist/types/store/contracts/retry.store.d.ts +7 -0
  472. package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
  473. package/dist/types/store/contracts/signal.store.d.ts +13 -0
  474. package/dist/types/store/contracts/thread.store.d.ts +1 -0
  475. package/dist/types/store/createStore.d.ts +1 -1
  476. package/dist/types/store/index.d.ts +5 -13
  477. package/dist/types/store/locks/appstate.lock.d.ts +3 -0
  478. package/dist/types/store/locks/auth.lock.d.ts +3 -0
  479. package/dist/types/store/locks/contact.lock.d.ts +3 -0
  480. package/dist/types/store/locks/device-list.lock.d.ts +2 -0
  481. package/dist/types/store/locks/message.lock.d.ts +3 -0
  482. package/dist/types/store/locks/participants.lock.d.ts +2 -0
  483. package/dist/types/store/locks/privacy-token.lock.d.ts +2 -0
  484. package/dist/types/store/locks/retry.lock.d.ts +2 -0
  485. package/dist/types/store/locks/sender-key.lock.d.ts +3 -0
  486. package/dist/types/store/locks/signal.lock.d.ts +3 -0
  487. package/dist/types/store/locks/thread.lock.d.ts +3 -0
  488. package/dist/types/store/providers/memory/appstate.store.d.ts +3 -1
  489. package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
  490. package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
  491. package/dist/types/store/providers/memory/message.store.d.ts +1 -0
  492. package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
  493. package/dist/types/store/providers/memory/privacy-token.store.d.ts +13 -0
  494. package/dist/types/store/providers/memory/retry.store.d.ts +8 -0
  495. package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
  496. package/dist/types/store/providers/memory/signal.store.d.ts +8 -1
  497. package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
  498. package/dist/types/store/types.d.ts +49 -58
  499. package/dist/types/transport/WaWebSocket.d.ts +3 -1
  500. package/dist/types/transport/binary/constants.d.ts +0 -30
  501. package/dist/types/transport/binary/index.d.ts +0 -1
  502. package/dist/types/transport/index.d.ts +2 -1
  503. package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
  504. package/dist/types/transport/node/WaNodeOrchestrator.d.ts +3 -4
  505. package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
  506. package/dist/types/transport/node/builders/business.d.ts +29 -0
  507. package/dist/types/transport/node/builders/global.d.ts +102 -0
  508. package/dist/types/transport/node/builders/group.d.ts +4 -6
  509. package/dist/types/transport/node/builders/index.d.ts +7 -3
  510. package/dist/types/transport/node/builders/message.d.ts +20 -30
  511. package/dist/types/transport/node/builders/pairing.d.ts +0 -2
  512. package/dist/types/transport/node/builders/privacy-token.d.ts +9 -0
  513. package/dist/types/transport/node/builders/privacy.d.ts +7 -0
  514. package/dist/types/transport/node/builders/profile.d.ts +8 -0
  515. package/dist/types/transport/node/builders/retry.d.ts +2 -5
  516. package/dist/types/transport/node/builders/usync.d.ts +21 -0
  517. package/dist/types/transport/node/helpers.d.ts +13 -0
  518. package/dist/types/transport/node/usync.d.ts +2 -0
  519. package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
  520. package/dist/types/transport/noise/WaNoiseSession.d.ts +4 -2
  521. package/dist/types/transport/noise/WaNoiseSocket.d.ts +4 -2
  522. package/dist/types/transport/proxy.d.ts +6 -0
  523. package/dist/types/transport/stream/parse.d.ts +0 -1
  524. package/dist/types/transport/types.d.ts +18 -1
  525. package/dist/types/util/bytes.d.ts +5 -0
  526. package/dist/types/util/index.d.ts +5 -0
  527. package/dist/types/util/primitives.d.ts +2 -0
  528. package/dist/util/bytes.js +72 -46
  529. package/dist/util/coercion.js +6 -14
  530. package/dist/util/index.js +23 -0
  531. package/dist/util/primitives.js +42 -14
  532. package/package.json +52 -9
  533. package/proto/index.js +1 -1
  534. package/dist/crypto/core/constants.js +0 -4
  535. package/dist/crypto/core/encoding.js +0 -29
  536. package/dist/esm/crypto/core/constants.js +0 -1
  537. package/dist/esm/crypto/core/encoding.js +0 -25
  538. package/dist/esm/retry/outbound.js +0 -83
  539. package/dist/esm/store/providers/sqlite/BaseSqliteStore.js +0 -37
  540. package/dist/esm/store/providers/sqlite/appstate.store.js +0 -169
  541. package/dist/esm/store/providers/sqlite/auth.store.js +0 -176
  542. package/dist/esm/store/providers/sqlite/connection.js +0 -240
  543. package/dist/esm/store/providers/sqlite/contact.store.js +0 -61
  544. package/dist/esm/store/providers/sqlite/device-list.store.js +0 -155
  545. package/dist/esm/store/providers/sqlite/message.store.js +0 -119
  546. package/dist/esm/store/providers/sqlite/migrations.js +0 -347
  547. package/dist/esm/store/providers/sqlite/participants.store.js +0 -85
  548. package/dist/esm/store/providers/sqlite/retry.store.js +0 -144
  549. package/dist/esm/store/providers/sqlite/sender-key.store.js +0 -203
  550. package/dist/esm/store/providers/sqlite/signal.store.js +0 -353
  551. package/dist/esm/store/providers/sqlite/thread.store.js +0 -72
  552. package/dist/esm/util/base64.js +0 -18
  553. package/dist/esm/util/signal-address.js +0 -5
  554. package/dist/retry/outbound.js +0 -88
  555. package/dist/store/providers/sqlite/BaseSqliteStore.js +0 -41
  556. package/dist/store/providers/sqlite/appstate.store.js +0 -173
  557. package/dist/store/providers/sqlite/auth.store.js +0 -180
  558. package/dist/store/providers/sqlite/connection.js +0 -276
  559. package/dist/store/providers/sqlite/contact.store.js +0 -65
  560. package/dist/store/providers/sqlite/device-list.store.js +0 -159
  561. package/dist/store/providers/sqlite/message.store.js +0 -123
  562. package/dist/store/providers/sqlite/migrations.js +0 -350
  563. package/dist/store/providers/sqlite/participants.store.js +0 -89
  564. package/dist/store/providers/sqlite/retry.store.js +0 -148
  565. package/dist/store/providers/sqlite/sender-key.store.js +0 -207
  566. package/dist/store/providers/sqlite/signal.store.js +0 -357
  567. package/dist/store/providers/sqlite/thread.store.js +0 -76
  568. package/dist/types/appstate/store/sqlite.d.ts +0 -21
  569. package/dist/types/crypto/core/constants.d.ts +0 -1
  570. package/dist/types/crypto/core/encoding.d.ts +0 -11
  571. package/dist/types/retry/outbound.d.ts +0 -4
  572. package/dist/types/store/providers/sqlite/BaseSqliteStore.d.ts +0 -12
  573. package/dist/types/store/providers/sqlite/appstate.store.d.ts +0 -15
  574. package/dist/types/store/providers/sqlite/auth.store.d.ts +0 -10
  575. package/dist/types/store/providers/sqlite/connection.d.ts +0 -10
  576. package/dist/types/store/providers/sqlite/contact.store.d.ts +0 -10
  577. package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -18
  578. package/dist/types/store/providers/sqlite/message.store.d.ts +0 -11
  579. package/dist/types/store/providers/sqlite/migrations.d.ts +0 -3
  580. package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -13
  581. package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -16
  582. package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -25
  583. package/dist/types/store/providers/sqlite/signal.store.d.ts +0 -46
  584. package/dist/types/store/providers/sqlite/thread.store.d.ts +0 -11
  585. package/dist/types/util/base64.d.ts +0 -4
  586. package/dist/types/util/signal-address.d.ts +0 -2
  587. package/dist/util/base64.js +0 -24
  588. package/dist/util/signal-address.js +0 -8
  589. /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
@@ -1,6 +1,9 @@
1
1
  import { aesCbcDecrypt, aesCbcEncrypt, importAesCbcKey, toSerializedPubKey, prependVersion, randomBytesAsync, randomIntAsync, X25519 } from '../../crypto/index.js';
2
+ import { StoreLock } from '../../infra/perf/StoreLock.js';
2
3
  import { proto } from '../../proto.js';
3
- import { SIGNAL_GROUP_VERSION, SIGNATURE_SIZE } from '../constants.js';
4
+ import { signalAddressKey } from '../../protocol/jid.js';
5
+ import { SIGNAL_SIGNATURE_LENGTH } from '../api/constants.js';
6
+ import { SIGNAL_GROUP_VERSION } from '../constants.js';
4
7
  import { signSignalMessage, verifySignalSignature } from '../crypto/WaAdvSignature.js';
5
8
  import { deriveSenderKeyMsgKey, selectMessageKey } from '../group/SenderKeyChain.js';
6
9
  import { parseDistributionPayload, parseSenderKeyMessage } from '../group/SenderKeyCodec.js';
@@ -24,148 +27,171 @@ async function aesCbcDecryptFromSeed(seed, ciphertext) {
24
27
  }
25
28
  export class SenderKeyManager {
26
29
  constructor(store) {
30
+ this.senderLock = new StoreLock();
27
31
  this.store = store;
28
32
  }
29
- async createSenderKeyDistributionMessage(groupId, sender) {
30
- const senderKey = await this.ensureSenderKey(groupId, sender);
31
- const distributionProto = proto.SenderKeyDistributionMessage.encode({
32
- id: senderKey.keyId,
33
- iteration: senderKey.iteration,
34
- chainKey: senderKey.chainKey,
35
- signingKey: senderKey.signingPublicKey
36
- }).finish();
37
- const payload = prependVersion(distributionProto, SIGNAL_GROUP_VERSION);
38
- await this.store.upsertSenderKeyDistribution({
39
- groupId,
40
- sender,
41
- keyId: senderKey.keyId,
42
- timestampMs: Date.now()
33
+ async prepareGroupEncryption(groupId, sender, plaintext) {
34
+ return this.runWithSenderLock(groupId, sender, async () => {
35
+ const senderKey = await this.ensureSenderKeyInternal(groupId, sender);
36
+ if (!senderKey.signingPrivateKey) {
37
+ throw new Error('sender private signing key is missing');
38
+ }
39
+ const derived = await deriveSenderKeyMsgKey(senderKey.iteration, senderKey.chainKey);
40
+ await this.store.upsertSenderKey({
41
+ ...senderKey,
42
+ chainKey: derived.nextChainKey,
43
+ iteration: derived.messageKey.iteration + 1
44
+ });
45
+ const distributionProto = proto.SenderKeyDistributionMessage.encode({
46
+ id: senderKey.keyId,
47
+ iteration: senderKey.iteration,
48
+ chainKey: senderKey.chainKey,
49
+ signingKey: senderKey.signingPublicKey
50
+ }).finish();
51
+ const distributionMessage = {
52
+ groupId,
53
+ axolotlSenderKeyDistributionMessage: prependVersion(distributionProto, SIGNAL_GROUP_VERSION)
54
+ };
55
+ const messagePayload = await aesCbcEncryptFromSeed(derived.messageKey.seed, plaintext);
56
+ const senderKeyMessage = proto.SenderKeyMessage.encode({
57
+ id: senderKey.keyId,
58
+ iteration: derived.messageKey.iteration,
59
+ ciphertext: messagePayload
60
+ }).finish();
61
+ const versionedContent = prependVersion(senderKeyMessage, SIGNAL_GROUP_VERSION);
62
+ const signature = await signSignalMessage(senderKey.signingPrivateKey, versionedContent);
63
+ if (signature.length !== SIGNAL_SIGNATURE_LENGTH) {
64
+ throw new Error(`invalid sender key signature length ${signature.length}`);
65
+ }
66
+ const ciphertext = {
67
+ groupId,
68
+ sender,
69
+ keyId: senderKey.keyId,
70
+ iteration: derived.messageKey.iteration,
71
+ ciphertext: concatBytes([versionedContent, signature])
72
+ };
73
+ await this.store.upsertSenderKeyDistribution({
74
+ groupId,
75
+ sender,
76
+ keyId: senderKey.keyId,
77
+ timestampMs: Date.now()
78
+ });
79
+ return {
80
+ distributionMessage,
81
+ ciphertext,
82
+ keyId: senderKey.keyId
83
+ };
43
84
  });
44
- return {
45
- groupId,
46
- axolotlSenderKeyDistributionMessage: payload
47
- };
48
85
  }
49
- async filterParticipantsNeedingDistribution(groupId, sender, participants) {
86
+ async filterParticipantsNeedingDistribution(groupId, senderKeyId, participants) {
50
87
  if (participants.length === 0) {
51
88
  return [];
52
89
  }
53
- const senderKey = await this.ensureSenderKey(groupId, sender);
54
90
  const distributed = await this.store.getDeviceSenderKeyDistributions(groupId, participants);
55
- return participants.filter((_, index) => {
91
+ const pendingParticipants = new Array(participants.length);
92
+ let pendingCount = 0;
93
+ for (let index = 0; index < participants.length; index += 1) {
56
94
  const record = distributed[index];
57
- return !record || record.keyId !== senderKey.keyId;
58
- });
95
+ if (!record || record.keyId !== senderKeyId) {
96
+ pendingParticipants[pendingCount] = participants[index];
97
+ pendingCount += 1;
98
+ }
99
+ }
100
+ pendingParticipants.length = pendingCount;
101
+ return pendingParticipants;
59
102
  }
60
- async markSenderKeyDistributed(groupId, sender, participants) {
103
+ async markSenderKeyDistributed(groupId, senderKeyId, participants) {
61
104
  if (participants.length === 0) {
62
105
  return;
63
106
  }
64
- const senderKey = await this.ensureSenderKey(groupId, sender);
65
107
  const timestampMs = Date.now();
66
- await this.store.upsertSenderKeyDistributions(participants.map((participant) => ({
67
- groupId,
68
- sender: participant,
69
- keyId: senderKey.keyId,
70
- timestampMs
71
- })));
72
- }
73
- async processSenderKeyDistributionPayload(groupId, sender, payload) {
74
- if (groupId.length === 0) {
75
- throw new Error('sender key distribution missing groupId');
108
+ const distributions = new Array(participants.length);
109
+ for (let index = 0; index < participants.length; index += 1) {
110
+ distributions[index] = {
111
+ groupId,
112
+ sender: participants[index],
113
+ keyId: senderKeyId,
114
+ timestampMs
115
+ };
76
116
  }
77
- const parsed = parseDistributionPayload(payload);
78
- const record = {
79
- groupId,
80
- sender,
81
- keyId: parsed.keyId,
82
- iteration: parsed.iteration,
83
- chainKey: parsed.chainKey,
84
- signingPublicKey: parsed.signingPublicKey,
85
- unusedMessageKeys: []
86
- };
87
- await this.store.upsertSenderKey(record);
88
- await this.store.upsertSenderKeyDistribution({
89
- groupId,
90
- sender,
91
- keyId: parsed.keyId,
92
- timestampMs: Date.now()
93
- });
94
- return record;
117
+ await this.store.upsertSenderKeyDistributions(distributions);
95
118
  }
96
- async encryptGroupMessage(groupId, sender, plaintext) {
97
- const senderKey = await this.ensureSenderKey(groupId, sender);
98
- if (!senderKey.signingPrivateKey) {
99
- throw new Error('sender private signing key is missing');
100
- }
101
- const derived = await deriveSenderKeyMsgKey(senderKey.iteration, senderKey.chainKey);
102
- const messagePayload = await aesCbcEncryptFromSeed(derived.messageKey.seed, plaintext);
103
- const senderKeyMessage = proto.SenderKeyMessage.encode({
104
- id: senderKey.keyId,
105
- iteration: derived.messageKey.iteration,
106
- ciphertext: messagePayload
107
- }).finish();
108
- const versionedContent = prependVersion(senderKeyMessage, SIGNAL_GROUP_VERSION);
109
- const signature = await signSignalMessage(senderKey.signingPrivateKey, versionedContent);
110
- if (signature.length !== SIGNATURE_SIZE) {
111
- throw new Error(`invalid sender key signature length ${signature.length}`);
112
- }
113
- const ciphertext = concatBytes([versionedContent, signature]);
114
- await this.store.upsertSenderKey({
115
- ...senderKey,
116
- chainKey: derived.nextChainKey,
117
- iteration: derived.messageKey.iteration + 1
119
+ async processSenderKeyDistributionPayload(groupId, sender, payload) {
120
+ return this.runWithSenderLock(groupId, sender, async () => {
121
+ if (groupId.length === 0) {
122
+ throw new Error('sender key distribution missing groupId');
123
+ }
124
+ const parsed = parseDistributionPayload(payload);
125
+ const record = {
126
+ groupId,
127
+ sender,
128
+ keyId: parsed.keyId,
129
+ iteration: parsed.iteration,
130
+ chainKey: parsed.chainKey,
131
+ signingPublicKey: parsed.signingPublicKey,
132
+ unusedMessageKeys: []
133
+ };
134
+ await Promise.all([
135
+ this.store.upsertSenderKey(record),
136
+ this.store.upsertSenderKeyDistribution({
137
+ groupId,
138
+ sender,
139
+ keyId: parsed.keyId,
140
+ timestampMs: Date.now()
141
+ })
142
+ ]);
143
+ return record;
118
144
  });
119
- return {
120
- groupId,
121
- sender,
122
- keyId: senderKey.keyId,
123
- iteration: derived.messageKey.iteration,
124
- ciphertext
125
- };
126
145
  }
127
146
  async decryptGroupMessage(payload) {
128
- const parsed = parseSenderKeyMessage(payload.ciphertext);
129
- const senderKey = await this.store.getDeviceSenderKey(payload.groupId, payload.sender);
130
- if (!senderKey) {
131
- throw new Error('missing sender key');
132
- }
133
- if (senderKey.keyId !== parsed.keyId) {
134
- throw new Error('sender key id mismatch');
135
- }
136
- if (payload.keyId !== undefined &&
137
- payload.keyId !== null &&
138
- parsed.keyId !== payload.keyId) {
139
- throw new Error('sender key id mismatch');
140
- }
141
- if (payload.iteration !== undefined &&
142
- payload.iteration !== null &&
143
- parsed.iteration !== payload.iteration) {
144
- throw new Error('sender key iteration mismatch');
145
- }
146
- const signedContent = parsed.versionContentMac.subarray(0, parsed.versionContentMac.length - SIGNATURE_SIZE);
147
- const signature = parsed.versionContentMac.subarray(parsed.versionContentMac.length - SIGNATURE_SIZE);
148
- const validSignature = await verifySignalSignature(senderKey.signingPublicKey, signedContent, signature);
149
- if (!validSignature) {
150
- throw new Error('invalid sender key signature');
151
- }
152
- const selected = await selectMessageKey(senderKey, parsed.iteration);
153
- const plaintext = await aesCbcDecryptFromSeed(selected.messageKey.seed, parsed.ciphertext);
154
- await this.store.upsertSenderKey(selected.updatedRecord);
155
- return plaintext;
147
+ return this.runWithSenderLock(payload.groupId, payload.sender, async () => {
148
+ const parsed = parseSenderKeyMessage(payload.ciphertext);
149
+ const senderKey = await this.store.getDeviceSenderKey(payload.groupId, payload.sender);
150
+ if (!senderKey) {
151
+ throw new Error('missing sender key');
152
+ }
153
+ if (senderKey.keyId !== parsed.keyId) {
154
+ throw new Error('sender key id mismatch');
155
+ }
156
+ if (payload.keyId !== undefined &&
157
+ payload.keyId !== null &&
158
+ parsed.keyId !== payload.keyId) {
159
+ throw new Error('sender key id mismatch');
160
+ }
161
+ if (payload.iteration !== undefined &&
162
+ payload.iteration !== null &&
163
+ parsed.iteration !== payload.iteration) {
164
+ throw new Error('sender key iteration mismatch');
165
+ }
166
+ const signedContent = parsed.versionContentMac.subarray(0, parsed.versionContentMac.length - SIGNAL_SIGNATURE_LENGTH);
167
+ const signature = parsed.versionContentMac.subarray(parsed.versionContentMac.length - SIGNAL_SIGNATURE_LENGTH);
168
+ const validSignature = await verifySignalSignature(senderKey.signingPublicKey, signedContent, signature);
169
+ if (!validSignature) {
170
+ throw new Error('invalid sender key signature');
171
+ }
172
+ const selected = await selectMessageKey(senderKey, parsed.iteration);
173
+ // Keep decrypt + persist ordered: failed decrypt must not advance sender-key state.
174
+ const plaintext = await aesCbcDecryptFromSeed(selected.messageKey.seed, parsed.ciphertext);
175
+ await this.store.upsertSenderKey(selected.updatedRecord);
176
+ return plaintext;
177
+ });
156
178
  }
157
- async ensureSenderKey(groupId, sender) {
179
+ async ensureSenderKeyInternal(groupId, sender) {
158
180
  const existing = await this.store.getDeviceSenderKey(groupId, sender);
159
181
  if (existing) {
160
182
  return existing;
161
183
  }
162
- const signingKeyPair = await X25519.generateKeyPair();
184
+ const [signingKeyPair, keyId, chainKey] = await Promise.all([
185
+ X25519.generateKeyPair(),
186
+ randomIntAsync(1, 2147483647),
187
+ randomBytesAsync(32)
188
+ ]);
163
189
  const created = {
164
190
  groupId,
165
191
  sender,
166
- keyId: await randomIntAsync(1, 2147483647),
192
+ keyId,
167
193
  iteration: 0,
168
- chainKey: await randomBytesAsync(32),
194
+ chainKey,
169
195
  signingPublicKey: toSerializedPubKey(signingKeyPair.pubKey),
170
196
  signingPrivateKey: signingKeyPair.privKey,
171
197
  unusedMessageKeys: []
@@ -173,4 +199,7 @@ export class SenderKeyManager {
173
199
  await this.store.upsertSenderKey(created);
174
200
  return created;
175
201
  }
202
+ runWithSenderLock(groupId, sender, task) {
203
+ return this.senderLock.run(`senderKey:${groupId}:${signalAddressKey(sender)}`, task);
204
+ }
176
205
  }
@@ -1,3 +1,4 @@
1
+ export { decodeSignalPreKeyRow, decodeSignalRegistrationRow, decodeSignalRemoteIdentity, decodeSignalSessionRecord, decodeSignalSignedPreKeyRow, decodeSenderKeyDistributionRow, decodeSenderKeyRecord, decodeStoreCount, encodeSenderKeyRecord, encodeSignalSessionRecord, toSignalAddressParts } from './encoding.js';
1
2
  export { generatePreKeyPair, generateRegistrationId, generateRegistrationInfo, generateSignedPreKey } from './registration/keygen.js';
2
3
  export { buildPreKeyUploadIq, parsePreKeyUploadFailure } from './api/prekeys.js';
3
4
  export { SignalDigestSyncApi } from './api/SignalDigestSyncApi.js';
@@ -9,3 +10,4 @@ export { SignalSessionSyncApi } from './api/SignalSessionSyncApi.js';
9
10
  export { SenderKeyManager } from './group/SenderKeyManager.js';
10
11
  export { createAndStoreInitialKeys } from './registration/utils.js';
11
12
  export { SignalProtocol } from './session/SignalProtocol.js';
13
+ export { createSignalSessionResolver } from './session/resolver.js';
@@ -3,9 +3,13 @@ import { toSerializedPubKey } from '../../crypto/core/keys.js';
3
3
  import { X25519 } from '../../crypto/curves/X25519.js';
4
4
  import { signSignalMessage } from '../crypto/WaAdvSignature.js';
5
5
  export async function generateRegistrationInfo() {
6
+ const [registrationId, identityKeyPair] = await Promise.all([
7
+ generateRegistrationId(),
8
+ X25519.generateKeyPair()
9
+ ]);
6
10
  return {
7
- registrationId: await generateRegistrationId(),
8
- identityKeyPair: await X25519.generateKeyPair()
11
+ registrationId,
12
+ identityKeyPair
9
13
  };
10
14
  }
11
15
  export async function generatePreKeyPair(keyId) {
@@ -5,6 +5,7 @@ export async function createAndStoreInitialKeys(store) {
5
5
  generatePreKeyPair(1)
6
6
  ]);
7
7
  const signedPreKey = await generateSignedPreKey(1, registrationInfo.identityKeyPair.privKey);
8
+ // Keep writes ordered so partial commit failures don't leave split registration bootstrap state.
8
9
  await store.setRegistrationInfo(registrationInfo);
9
10
  await store.setSignedPreKey(signedPreKey);
10
11
  await store.getOrGenSinglePreKey(async () => firstPreKey);
@@ -1,76 +1,186 @@
1
1
  import { toSerializedPubKey } from '../../crypto/index.js';
2
2
  import { ConsoleLogger } from '../../infra/log/ConsoleLogger.js';
3
+ import { StoreLock } from '../../infra/perf/StoreLock.js';
3
4
  import { MAX_PREV_SESSIONS } from '../constants.js';
4
5
  import { decryptMsg, decryptMsgFromSession, encryptMsg } from '../session/SignalRatchet.js';
5
6
  import { deserializeMsg, deserializePkMsg, requirePreKey, requireSignedPreKey } from '../session/SignalSerializer.js';
6
7
  import { detachSession, findMatchingSession, generateSerializedKeyPair, initiateSessionIncoming, initiateSessionOutgoing, requireLocalIdentity, toSerializedKeyPair } from '../session/SignalSession.js';
7
8
  import { uint8Equal } from '../../util/bytes.js';
9
+ function signalAddressMapKey(address) {
10
+ return `${address.user}\u0001${address.server ?? ''}\u0001${address.device}`;
11
+ }
12
+ function signalAddressLockKey(address) {
13
+ return `signal:${signalAddressMapKey(address)}`;
14
+ }
8
15
  export class SignalProtocol {
9
16
  constructor(store, logger = new ConsoleLogger('info')) {
10
17
  this.store = store;
11
18
  this.logger = logger;
19
+ this.sessionMutationLock = new StoreLock();
12
20
  }
13
- async hasSession(address) {
14
- return this.store.hasSession(address);
15
- }
16
- async hasSessions(addresses) {
17
- return this.store.hasSessions(addresses);
21
+ async establishOutgoingSession(address, remoteBundle, options = {}) {
22
+ return this.runWithAddressLock(address, async () => {
23
+ if (options.reuseExisting) {
24
+ const existing = await this.store.getSession(address);
25
+ if (existing) {
26
+ const remoteIdentity = toSerializedPubKey(remoteBundle.identity);
27
+ if (!uint8Equal(existing.remote.pubKey, remoteIdentity)) {
28
+ throw new Error('identity mismatch');
29
+ }
30
+ return existing;
31
+ }
32
+ }
33
+ const [local, localOneTimeBase] = await Promise.all([
34
+ requireLocalIdentity(this.store),
35
+ generateSerializedKeyPair()
36
+ ]);
37
+ const session = await initiateSessionOutgoing(local, remoteBundle, localOneTimeBase);
38
+ // Keep writes ordered: a stored session without matching remote identity causes false mismatch checks.
39
+ await this.store.setRemoteIdentity(address, session.remote.pubKey);
40
+ await this.store.setSession(address, session);
41
+ return session;
42
+ });
18
43
  }
19
- async establishOutgoingSession(address, remoteBundle) {
20
- const [local, localOneTimeBase] = await Promise.all([
21
- requireLocalIdentity(this.store),
22
- generateSerializedKeyPair()
44
+ async encryptMessage(address, plaintext, expectedIdentity) {
45
+ const [encrypted] = await this.encryptMessagesBatch([
46
+ { address, plaintext, expectedIdentity }
23
47
  ]);
24
- const session = await initiateSessionOutgoing(local, remoteBundle, localOneTimeBase);
25
- await this.store.setRemoteIdentity(address, session.remote.pubKey);
26
- await this.store.setSession(address, session);
27
- return session;
48
+ return encrypted;
28
49
  }
29
- 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
- }
34
- if (expectedIdentity &&
35
- !uint8Equal(toSerializedPubKey(expectedIdentity), session.remote.pubKey)) {
36
- throw new Error('identity mismatch');
50
+ async encryptMessagesBatch(requests, prefetchedSessions) {
51
+ if (requests.length === 0) {
52
+ return [];
37
53
  }
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);
42
- }
43
- return {
44
- ...encrypted,
45
- baseKey: updatedSession.aliceBaseKey
46
- };
54
+ const lockKeySet = new Set();
55
+ for (let i = 0; i < requests.length; i += 1)
56
+ lockKeySet.add(signalAddressLockKey(requests[i].address));
57
+ const lockKeys = [...lockKeySet];
58
+ return this.sessionMutationLock.runMany(lockKeys, async () => {
59
+ const prefetchedByAddress = new Map();
60
+ if (prefetchedSessions && prefetchedSessions.length > 0) {
61
+ for (let index = 0; index < prefetchedSessions.length; index += 1) {
62
+ const entry = prefetchedSessions[index];
63
+ prefetchedByAddress.set(signalAddressMapKey(entry.address), entry.session);
64
+ }
65
+ }
66
+ const uniqueAddressKeys = new Array(requests.length);
67
+ const uniqueAddresses = new Array(requests.length);
68
+ let uniqueAddressCount = 0;
69
+ for (let index = 0; index < requests.length; index += 1) {
70
+ const address = requests[index].address;
71
+ const addressKey = signalAddressMapKey(address);
72
+ let isDuplicate = false;
73
+ for (let dedupIndex = 0; dedupIndex < uniqueAddressCount; dedupIndex += 1) {
74
+ if (uniqueAddressKeys[dedupIndex] === addressKey) {
75
+ isDuplicate = true;
76
+ break;
77
+ }
78
+ }
79
+ if (isDuplicate) {
80
+ continue;
81
+ }
82
+ uniqueAddressKeys[uniqueAddressCount] = addressKey;
83
+ uniqueAddresses[uniqueAddressCount] = address;
84
+ uniqueAddressCount += 1;
85
+ }
86
+ uniqueAddressKeys.length = uniqueAddressCount;
87
+ uniqueAddresses.length = uniqueAddressCount;
88
+ const currentSessions = await this.store.getSessionsBatch(uniqueAddresses);
89
+ const latestSessionByAddress = new Map();
90
+ for (let index = 0; index < uniqueAddressCount; index += 1) {
91
+ const addressKey = uniqueAddressKeys[index];
92
+ const current = currentSessions[index];
93
+ if (current) {
94
+ latestSessionByAddress.set(addressKey, current);
95
+ continue;
96
+ }
97
+ const prefetched = prefetchedByAddress.get(addressKey);
98
+ if (prefetched) {
99
+ latestSessionByAddress.set(addressKey, prefetched);
100
+ }
101
+ }
102
+ const sessionUpdatesByAddress = new Map();
103
+ const identityUpdatesByAddress = new Map();
104
+ const results = new Array(requests.length);
105
+ for (let index = 0; index < requests.length; index += 1) {
106
+ const request = requests[index];
107
+ const address = request.address;
108
+ const addressKey = signalAddressMapKey(address);
109
+ const session = latestSessionByAddress.get(addressKey);
110
+ if (!session) {
111
+ throw new Error('signal session not found');
112
+ }
113
+ if (request.expectedIdentity &&
114
+ !uint8Equal(toSerializedPubKey(request.expectedIdentity), session.remote.pubKey)) {
115
+ throw new Error('identity mismatch');
116
+ }
117
+ const [updatedSession, encrypted] = await encryptMsg(session, request.plaintext);
118
+ latestSessionByAddress.set(addressKey, updatedSession);
119
+ sessionUpdatesByAddress.set(addressKey, {
120
+ address,
121
+ session: updatedSession
122
+ });
123
+ if (!uint8Equal(updatedSession.remote.pubKey, session.remote.pubKey)) {
124
+ identityUpdatesByAddress.set(addressKey, {
125
+ address,
126
+ identityKey: updatedSession.remote.pubKey
127
+ });
128
+ }
129
+ results[index] = {
130
+ ...encrypted,
131
+ baseKey: updatedSession.aliceBaseKey
132
+ };
133
+ }
134
+ // Persist remote identities first when needed so session writes never commit ahead of identity data.
135
+ if (identityUpdatesByAddress.size > 0) {
136
+ const identityUpdates = new Array(identityUpdatesByAddress.size);
137
+ let identityIndex = 0;
138
+ for (const update of identityUpdatesByAddress.values()) {
139
+ identityUpdates[identityIndex] = update;
140
+ identityIndex += 1;
141
+ }
142
+ await this.store.setRemoteIdentities(identityUpdates);
143
+ }
144
+ const sessionUpdates = new Array(sessionUpdatesByAddress.size);
145
+ let sessionIndex = 0;
146
+ for (const update of sessionUpdatesByAddress.values()) {
147
+ sessionUpdates[sessionIndex] = update;
148
+ sessionIndex += 1;
149
+ }
150
+ await this.store.setSessionsBatch(sessionUpdates);
151
+ return results;
152
+ });
47
153
  }
48
154
  async decryptMessage(address, envelope) {
49
- const currentSession = await this.store.getSession(address);
50
- let outcome;
51
- if (envelope.type === 'pkmsg') {
52
- const parsedPk = deserializePkMsg(envelope.ciphertext);
53
- outcome = await this.decryptPkMsg(currentSession, parsedPk);
54
- }
55
- else {
56
- const parsed = deserializeMsg(envelope.ciphertext);
57
- outcome = await this.decryptMsgInternal(currentSession, parsed);
58
- }
59
- const nextRemoteIdentity = outcome.newSessionInfo?.newIdentity ?? outcome.updatedSession.remote.pubKey;
60
- if (!currentSession || !uint8Equal(currentSession.remote.pubKey, nextRemoteIdentity)) {
61
- await this.store.setRemoteIdentity(address, nextRemoteIdentity);
62
- }
63
- await this.store.setSession(address, outcome.updatedSession);
64
- return outcome.plaintext;
65
- }
66
- async decryptMsgInternal(session, parsed) {
67
- return decryptMsg(session, parsed, (error, previousSessionIndex) => {
68
- this.logger.debug('signal decrypt fallback session failed', {
69
- previousSessionIndex,
70
- message: error.message
71
- });
155
+ return this.runWithAddressLock(address, async () => {
156
+ const currentSession = await this.store.getSession(address);
157
+ let outcome;
158
+ if (envelope.type === 'pkmsg') {
159
+ const parsedPk = deserializePkMsg(envelope.ciphertext);
160
+ outcome = await this.decryptPkMsg(currentSession, parsedPk);
161
+ }
162
+ else {
163
+ const parsed = deserializeMsg(envelope.ciphertext);
164
+ outcome = await decryptMsg(currentSession, parsed, (error, previousSessionIndex) => {
165
+ this.logger.debug('signal decrypt fallback session failed', {
166
+ previousSessionIndex,
167
+ message: error.message
168
+ });
169
+ });
170
+ }
171
+ const nextRemoteIdentity = outcome.newSessionInfo?.newIdentity ?? outcome.updatedSession.remote.pubKey;
172
+ const identityChanged = !currentSession || !uint8Equal(currentSession.remote.pubKey, nextRemoteIdentity);
173
+ // Keep writes ordered for consistency with resolver identity checks.
174
+ if (identityChanged) {
175
+ await this.store.setRemoteIdentity(address, nextRemoteIdentity);
176
+ }
177
+ await this.store.setSession(address, outcome.updatedSession);
178
+ return outcome.plaintext;
72
179
  });
73
180
  }
181
+ runWithAddressLock(address, task) {
182
+ return this.sessionMutationLock.run(signalAddressLockKey(address), task);
183
+ }
74
184
  async decryptPkMsg(currentSession, parsed) {
75
185
  const matchingSession = findMatchingSession(currentSession, parsed.sessionBaseKey);
76
186
  if (matchingSession) {
@@ -106,6 +216,7 @@ export class SignalProtocol {
106
216
  }
107
217
  : incoming;
108
218
  const [updatedSession, plaintext] = await decryptMsgFromSession(baseSession, parsed);
219
+ // Only consume one-time prekeys after successful decrypt/session materialization.
109
220
  if (parsed.localOneTimeKeyId !== null && parsed.localOneTimeKeyId !== undefined) {
110
221
  await this.store.consumePreKeyById(parsed.localOneTimeKeyId);
111
222
  }
@@ -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,