zapo-js 0.2.0 → 0.3.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 (351) hide show
  1. package/README.md +7 -3
  2. package/dist/appstate/WaAppStateCrypto.js +49 -41
  3. package/dist/appstate/WaAppStateSyncClient.js +79 -42
  4. package/dist/appstate/index.js +2 -2
  5. package/dist/auth/WaAuthClient.js +20 -11
  6. package/dist/auth/{flow/WaAuthCredentialsFlow.js → credentials-flow.js} +83 -18
  7. package/dist/auth/pairing/WaPairingFlow.js +26 -29
  8. package/dist/auth/pairing/{WaPairingCodeCrypto.js → pairing-code-crypto.js} +29 -13
  9. package/dist/client/WaClient.js +115 -75
  10. package/dist/client/WaClientFactory.js +113 -30
  11. package/dist/client/connection/WaConnectionManager.js +4 -1
  12. package/dist/client/coordinators/WaAbPropsCoordinator.js +141 -0
  13. package/dist/client/coordinators/WaBusinessCoordinator.js +3 -12
  14. package/dist/client/coordinators/WaEmailCoordinator.js +63 -0
  15. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +33 -8
  16. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +55 -25
  17. package/dist/client/coordinators/WaOfflineResumeCoordinator.js +114 -0
  18. package/dist/client/coordinators/WaPassiveTasksCoordinator.js +38 -20
  19. package/dist/client/coordinators/WaProfileCoordinator.js +3 -1
  20. package/dist/client/coordinators/WaRetryCoordinator.js +11 -9
  21. package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +22 -4
  22. package/dist/client/dirty.js +1 -1
  23. package/dist/client/events/abprops.js +43 -0
  24. package/dist/client/events/privacy-token.js +1 -2
  25. package/dist/client/events/registration.js +42 -0
  26. package/dist/client/incoming.js +37 -0
  27. package/dist/client/mailbox.js +17 -1
  28. package/dist/client/media.js +243 -0
  29. package/dist/client/messages.js +163 -86
  30. package/dist/crypto/core/index.js +4 -1
  31. package/dist/crypto/core/random.js +3 -9
  32. package/dist/crypto/core/xeddsa.js +57 -0
  33. package/dist/crypto/curves/X25519.js +18 -0
  34. package/dist/crypto/curves/constants.js +2 -1
  35. package/dist/esm/appstate/WaAppStateCrypto.js +39 -31
  36. package/dist/esm/appstate/WaAppStateSyncClient.js +68 -31
  37. package/dist/esm/appstate/index.js +1 -1
  38. package/dist/esm/appstate/{WaAppStateSyncResponseParser.js → response-parser.js} +1 -1
  39. package/dist/esm/auth/WaAuthClient.js +17 -8
  40. package/dist/esm/auth/{flow/WaAuthCredentialsFlow.js → credentials-flow.js} +83 -18
  41. package/dist/esm/auth/pairing/WaPairingFlow.js +25 -28
  42. package/dist/esm/auth/pairing/{WaPairingCodeCrypto.js → pairing-code-crypto.js} +20 -6
  43. package/dist/esm/client/WaClient.js +116 -76
  44. package/dist/esm/client/WaClientFactory.js +114 -31
  45. package/dist/esm/client/connection/WaConnectionManager.js +4 -1
  46. package/dist/esm/client/coordinators/WaAbPropsCoordinator.js +137 -0
  47. package/dist/esm/client/coordinators/WaBusinessCoordinator.js +4 -13
  48. package/dist/esm/client/coordinators/WaEmailCoordinator.js +60 -0
  49. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +35 -10
  50. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +47 -17
  51. package/dist/esm/client/coordinators/WaOfflineResumeCoordinator.js +110 -0
  52. package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +38 -20
  53. package/dist/esm/client/coordinators/WaProfileCoordinator.js +3 -1
  54. package/dist/esm/client/coordinators/WaRetryCoordinator.js +11 -9
  55. package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +24 -6
  56. package/dist/esm/client/dirty.js +1 -1
  57. package/dist/esm/client/events/abprops.js +40 -0
  58. package/dist/esm/client/events/privacy-token.js +1 -2
  59. package/dist/esm/client/events/registration.js +39 -0
  60. package/dist/esm/client/incoming.js +36 -0
  61. package/dist/esm/client/mailbox.js +17 -1
  62. package/dist/esm/client/media.js +234 -0
  63. package/dist/esm/client/messages.js +162 -85
  64. package/dist/esm/crypto/core/index.js +1 -0
  65. package/dist/esm/crypto/core/random.js +2 -7
  66. package/dist/esm/crypto/core/xeddsa.js +53 -0
  67. package/dist/esm/crypto/curves/X25519.js +20 -2
  68. package/dist/esm/crypto/curves/constants.js +1 -0
  69. package/dist/esm/infra/perf/StoreLock.js +7 -4
  70. package/dist/esm/media/WaMediaCrypto.js +257 -62
  71. package/dist/esm/media/WaMediaTransferClient.js +47 -190
  72. package/dist/esm/media/constants.js +2 -0
  73. package/dist/esm/media/processor.js +1 -0
  74. package/dist/esm/message/addon-crypto.js +130 -3
  75. package/dist/esm/message/content.js +12 -6
  76. package/dist/esm/message/icdc.js +8 -8
  77. package/dist/esm/message/incoming.js +14 -12
  78. package/dist/esm/message/phash.js +32 -12
  79. package/dist/esm/message/reporting-token.js +3 -3
  80. package/dist/esm/message/use-case-secret.js +1 -1
  81. package/dist/esm/protocol/abprops.js +159 -0
  82. package/dist/esm/protocol/browser.js +14 -0
  83. package/dist/esm/protocol/constants.js +3 -1
  84. package/dist/esm/protocol/email.js +30 -0
  85. package/dist/esm/protocol/jid.js +44 -10
  86. package/dist/esm/protocol/nodes.js +6 -2
  87. package/dist/esm/protocol/notification.js +7 -1
  88. package/dist/esm/retry/reason.js +1 -1
  89. package/dist/esm/signal/api/SignalDeviceSyncApi.js +5 -2
  90. package/dist/esm/signal/api/SignalDigestSyncApi.js +8 -6
  91. package/dist/esm/signal/api/SignalIdentitySyncApi.js +4 -4
  92. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +1 -1
  93. package/dist/esm/signal/api/SignalSessionSyncApi.js +1 -1
  94. package/dist/esm/signal/crypto/WaAdvSignature.js +5 -51
  95. package/dist/esm/signal/crypto/constants.js +0 -4
  96. package/dist/esm/signal/encoding.js +11 -54
  97. package/dist/esm/signal/group/SenderKeyChain.js +3 -3
  98. package/dist/esm/signal/group/SenderKeyCodec.js +5 -6
  99. package/dist/esm/signal/group/SenderKeyManager.js +13 -10
  100. package/dist/esm/signal/registration/keygen.js +2 -3
  101. package/dist/esm/signal/registration/utils.js +2 -2
  102. package/dist/esm/signal/session/SignalProtocol.js +18 -17
  103. package/dist/esm/signal/session/SignalRatchet.js +21 -10
  104. package/dist/esm/signal/session/SignalSerializer.js +5 -6
  105. package/dist/esm/signal/session/SignalSession.js +11 -9
  106. package/dist/esm/signal/session/resolver.js +6 -6
  107. package/dist/esm/store/contracts/identity.store.js +1 -0
  108. package/dist/esm/store/contracts/message-secret.store.js +1 -0
  109. package/dist/esm/store/contracts/pre-key.store.js +1 -0
  110. package/dist/esm/store/contracts/session.store.js +1 -0
  111. package/dist/esm/store/createStore.js +48 -12
  112. package/dist/esm/store/index.js +4 -0
  113. package/dist/esm/store/locks/identity.lock.js +16 -0
  114. package/dist/esm/store/locks/message-secret.lock.js +17 -0
  115. package/dist/esm/store/locks/pre-key.lock.js +27 -0
  116. package/dist/esm/store/locks/session.lock.js +19 -0
  117. package/dist/esm/store/locks/signal.lock.js +0 -24
  118. package/dist/esm/store/noop.store.js +20 -0
  119. package/dist/esm/store/providers/memory/device-list.store.js +3 -0
  120. package/dist/esm/store/providers/memory/identity.store.js +31 -0
  121. package/dist/esm/store/providers/memory/message-secret.store.js +81 -0
  122. package/dist/esm/store/providers/memory/participants.store.js +3 -0
  123. package/dist/esm/store/providers/memory/pre-key.store.js +97 -0
  124. package/dist/esm/store/providers/memory/retry.store.js +25 -11
  125. package/dist/esm/store/providers/memory/session.store.js +45 -0
  126. package/dist/esm/store/providers/memory/signal.store.js +1 -164
  127. package/dist/esm/transport/WaComms.js +4 -3
  128. package/dist/esm/transport/WaWebSocket.js +9 -1
  129. package/dist/esm/transport/index.js +6 -0
  130. package/dist/esm/transport/keepalive/WaKeepAlive.js +17 -8
  131. package/dist/esm/transport/node/WaMobileTcpSocket.js +114 -0
  132. package/dist/esm/transport/node/WaNodeOrchestrator.js +17 -8
  133. package/dist/esm/transport/node/builders/abprops.js +20 -0
  134. package/dist/esm/transport/node/builders/device.js +11 -0
  135. package/dist/esm/transport/node/builders/email.js +65 -0
  136. package/dist/esm/transport/node/builders/offline.js +14 -0
  137. package/dist/esm/transport/node/builders/prekeys.js +37 -40
  138. package/dist/esm/transport/node/builders/presence.js +13 -0
  139. package/dist/esm/transport/node/builders/privacy-token.js +19 -23
  140. package/dist/esm/transport/node/builders/retry.js +1 -1
  141. package/dist/esm/transport/node/helpers.js +24 -0
  142. package/dist/esm/transport/node/mex/argo-decoder.js +152 -0
  143. package/dist/esm/transport/node/mex/client.js +83 -0
  144. package/dist/esm/transport/node/mex/persist-ids.js +10 -0
  145. package/dist/esm/transport/noise/WaClientPayload.js +15 -10
  146. package/dist/esm/transport/noise/WaFrameCodec.js +2 -2
  147. package/dist/esm/transport/noise/WaMobileClientPayload.js +53 -0
  148. package/dist/esm/transport/noise/WaNoiseCert.js +9 -27
  149. package/dist/esm/transport/noise/WaNoiseSession.js +12 -11
  150. package/dist/infra/perf/StoreLock.js +7 -4
  151. package/dist/media/WaMediaCrypto.js +253 -58
  152. package/dist/media/WaMediaTransferClient.js +50 -223
  153. package/dist/media/constants.js +3 -1
  154. package/dist/media/processor.js +2 -0
  155. package/dist/message/addon-crypto.js +131 -0
  156. package/dist/message/content.js +13 -5
  157. package/dist/message/icdc.js +8 -8
  158. package/dist/message/incoming.js +14 -12
  159. package/dist/message/phash.js +32 -12
  160. package/dist/message/reporting-token.js +2 -2
  161. package/dist/message/use-case-secret.js +1 -1
  162. package/dist/protocol/abprops.js +163 -0
  163. package/dist/protocol/browser.js +15 -0
  164. package/dist/protocol/constants.js +14 -2
  165. package/dist/protocol/email.js +33 -0
  166. package/dist/protocol/jid.js +45 -10
  167. package/dist/protocol/nodes.js +6 -2
  168. package/dist/protocol/notification.js +8 -2
  169. package/dist/retry/reason.js +1 -1
  170. package/dist/signal/api/SignalDeviceSyncApi.js +5 -2
  171. package/dist/signal/api/SignalDigestSyncApi.js +8 -6
  172. package/dist/signal/api/SignalIdentitySyncApi.js +4 -4
  173. package/dist/signal/crypto/WaAdvSignature.js +2 -50
  174. package/dist/signal/crypto/constants.js +1 -5
  175. package/dist/signal/encoding.js +11 -49
  176. package/dist/signal/group/SenderKeyChain.js +2 -2
  177. package/dist/signal/group/SenderKeyCodec.js +4 -5
  178. package/dist/signal/group/SenderKeyManager.js +12 -9
  179. package/dist/signal/registration/keygen.js +1 -2
  180. package/dist/signal/registration/utils.js +2 -2
  181. package/dist/signal/session/SignalProtocol.js +18 -17
  182. package/dist/signal/session/SignalRatchet.js +19 -8
  183. package/dist/signal/session/SignalSerializer.js +5 -6
  184. package/dist/signal/session/SignalSession.js +11 -9
  185. package/dist/signal/session/resolver.js +6 -6
  186. package/dist/store/contracts/identity.store.js +2 -0
  187. package/dist/store/contracts/message-secret.store.js +2 -0
  188. package/dist/store/contracts/pre-key.store.js +2 -0
  189. package/dist/store/contracts/session.store.js +2 -0
  190. package/dist/store/createStore.js +47 -11
  191. package/dist/store/index.js +9 -1
  192. package/dist/store/locks/identity.lock.js +19 -0
  193. package/dist/store/locks/message-secret.lock.js +20 -0
  194. package/dist/store/locks/pre-key.lock.js +30 -0
  195. package/dist/store/locks/session.lock.js +22 -0
  196. package/dist/store/locks/signal.lock.js +0 -24
  197. package/dist/store/noop.store.js +21 -1
  198. package/dist/store/providers/memory/device-list.store.js +3 -0
  199. package/dist/store/providers/memory/identity.store.js +35 -0
  200. package/dist/store/providers/memory/message-secret.store.js +85 -0
  201. package/dist/store/providers/memory/participants.store.js +3 -0
  202. package/dist/store/providers/memory/pre-key.store.js +101 -0
  203. package/dist/store/providers/memory/retry.store.js +24 -10
  204. package/dist/store/providers/memory/session.store.js +49 -0
  205. package/dist/store/providers/memory/signal.store.js +1 -164
  206. package/dist/transport/WaComms.js +4 -3
  207. package/dist/transport/WaWebSocket.js +9 -1
  208. package/dist/transport/index.js +17 -1
  209. package/dist/transport/keepalive/WaKeepAlive.js +17 -8
  210. package/dist/transport/node/WaMobileTcpSocket.js +118 -0
  211. package/dist/transport/node/WaNodeOrchestrator.js +16 -7
  212. package/dist/transport/node/builders/abprops.js +23 -0
  213. package/dist/transport/node/builders/device.js +14 -0
  214. package/dist/transport/node/builders/email.js +72 -0
  215. package/dist/transport/node/builders/offline.js +17 -0
  216. package/dist/transport/node/builders/prekeys.js +36 -39
  217. package/dist/transport/node/builders/presence.js +16 -0
  218. package/dist/transport/node/builders/privacy-token.js +18 -22
  219. package/dist/transport/node/builders/retry.js +1 -1
  220. package/dist/transport/node/helpers.js +26 -0
  221. package/dist/transport/node/mex/argo-decoder.js +189 -0
  222. package/dist/transport/node/mex/client.js +86 -0
  223. package/dist/transport/node/mex/persist-ids.js +13 -0
  224. package/dist/transport/noise/WaClientPayload.js +14 -9
  225. package/dist/transport/noise/WaFrameCodec.js +1 -1
  226. package/dist/transport/noise/WaMobileClientPayload.js +56 -0
  227. package/dist/transport/noise/WaNoiseCert.js +8 -26
  228. package/dist/transport/noise/WaNoiseSession.js +11 -10
  229. package/dist/types/appstate/WaAppStateCrypto.d.ts +11 -8
  230. package/dist/types/appstate/WaAppStateSyncClient.d.ts +6 -2
  231. package/dist/types/appstate/index.d.ts +1 -1
  232. package/dist/types/appstate/{WaAppStateSyncResponseParser.d.ts → response-parser.d.ts} +1 -1
  233. package/dist/types/appstate/types.d.ts +1 -1
  234. package/dist/types/auth/WaAuthClient.d.ts +9 -3
  235. package/dist/types/auth/credentials-flow.d.ts +20 -0
  236. package/dist/types/auth/pairing/WaPairingFlow.d.ts +3 -2
  237. package/dist/types/auth/pairing/{WaPairingCodeCrypto.d.ts → pairing-code-crypto.d.ts} +6 -1
  238. package/dist/types/auth/types.d.ts +40 -0
  239. package/dist/types/client/WaClient.d.ts +19 -8
  240. package/dist/types/client/WaClientFactory.d.ts +10 -4
  241. package/dist/types/client/coordinators/WaAbPropsCoordinator.d.ts +26 -0
  242. package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +1 -1
  243. package/dist/types/client/coordinators/WaEmailCoordinator.d.ts +24 -0
  244. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +6 -1
  245. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +15 -2
  246. package/dist/types/client/coordinators/WaOfflineResumeCoordinator.d.ts +31 -0
  247. package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +13 -2
  248. package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +1 -1
  249. package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +4 -2
  250. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +6 -0
  251. package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +11 -1
  252. package/dist/types/client/dirty.d.ts +3 -1
  253. package/dist/types/client/events/abprops.d.ts +14 -0
  254. package/dist/types/client/events/registration.d.ts +17 -0
  255. package/dist/types/client/incoming.d.ts +6 -1
  256. package/dist/types/client/mailbox.d.ts +2 -0
  257. package/dist/types/client/media.d.ts +31 -0
  258. package/dist/types/client/messages.d.ts +2 -0
  259. package/dist/types/client/persistence/WriteBehindPersistence.d.ts +1 -1
  260. package/dist/types/client/types.d.ts +100 -1
  261. package/dist/types/crypto/core/index.d.ts +1 -0
  262. package/dist/types/crypto/core/primitives.d.ts +1 -1
  263. package/dist/types/crypto/core/random.d.ts +1 -1
  264. package/dist/types/crypto/core/xeddsa.d.ts +2 -0
  265. package/dist/types/crypto/curves/constants.d.ts +1 -0
  266. package/dist/types/crypto/index.d.ts +1 -0
  267. package/dist/types/index.d.ts +2 -1
  268. package/dist/types/infra/log/ConsoleLogger.d.ts +1 -1
  269. package/dist/types/infra/log/PinoLogger.d.ts +1 -1
  270. package/dist/types/infra/perf/StoreLock.d.ts +1 -0
  271. package/dist/types/media/WaMediaCrypto.d.ts +15 -6
  272. package/dist/types/media/WaMediaTransferClient.d.ts +3 -11
  273. package/dist/types/media/constants.d.ts +2 -0
  274. package/dist/types/media/index.d.ts +1 -0
  275. package/dist/types/media/processor.d.ts +28 -0
  276. package/dist/types/media/types.d.ts +9 -3
  277. package/dist/types/message/addon-crypto.d.ts +34 -3
  278. package/dist/types/message/content.d.ts +3 -1
  279. package/dist/types/message/icdc.d.ts +4 -4
  280. package/dist/types/message/types.d.ts +16 -24
  281. package/dist/types/protocol/abprops.d.ts +142 -0
  282. package/dist/types/protocol/browser.d.ts +1 -0
  283. package/dist/types/protocol/constants.d.ts +5 -1
  284. package/dist/types/protocol/email.d.ts +32 -0
  285. package/dist/types/protocol/jid.d.ts +1 -0
  286. package/dist/types/protocol/nodes.d.ts +4 -0
  287. package/dist/types/protocol/notification.d.ts +6 -0
  288. package/dist/types/protocol/stream.d.ts +1 -0
  289. package/dist/types/retry/reason.d.ts +1 -1
  290. package/dist/types/signal/api/SignalDigestSyncApi.d.ts +3 -0
  291. package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +3 -3
  292. package/dist/types/signal/crypto/WaAdvSignature.d.ts +0 -2
  293. package/dist/types/signal/crypto/constants.d.ts +0 -1
  294. package/dist/types/signal/encoding.d.ts +7 -1
  295. package/dist/types/signal/group/SenderKeyChain.d.ts +1 -1
  296. package/dist/types/signal/group/SenderKeyManager.d.ts +7 -2
  297. package/dist/types/signal/registration/utils.d.ts +2 -1
  298. package/dist/types/signal/session/SignalProtocol.d.ts +11 -2
  299. package/dist/types/signal/session/SignalSerializer.d.ts +2 -1
  300. package/dist/types/signal/session/resolver.d.ts +4 -2
  301. package/dist/types/signal/types.d.ts +16 -4
  302. package/dist/types/store/contracts/identity.store.d.ts +11 -0
  303. package/dist/types/store/contracts/message-secret.store.d.ts +16 -0
  304. package/dist/types/store/contracts/pre-key.store.d.ts +13 -0
  305. package/dist/types/store/contracts/session.store.d.ts +14 -0
  306. package/dist/types/store/contracts/signal.store.d.ts +1 -34
  307. package/dist/types/store/index.d.ts +9 -1
  308. package/dist/types/store/locks/identity.lock.d.ts +3 -0
  309. package/dist/types/store/locks/message-secret.lock.d.ts +3 -0
  310. package/dist/types/store/locks/pre-key.lock.d.ts +3 -0
  311. package/dist/types/store/locks/session.lock.d.ts +3 -0
  312. package/dist/types/store/noop.store.d.ts +4 -0
  313. package/dist/types/store/providers/memory/identity.store.d.ts +18 -0
  314. package/dist/types/store/providers/memory/message-secret.store.d.ts +21 -0
  315. package/dist/types/store/providers/memory/pre-key.store.d.ts +23 -0
  316. package/dist/types/store/providers/memory/retry.store.d.ts +7 -1
  317. package/dist/types/store/providers/memory/session.store.d.ts +21 -0
  318. package/dist/types/store/providers/memory/signal.store.d.ts +3 -45
  319. package/dist/types/store/providers/memory/thread.store.d.ts +1 -1
  320. package/dist/types/store/types.d.ts +21 -1
  321. package/dist/types/transport/WaWebSocket.d.ts +1 -0
  322. package/dist/types/transport/index.d.ts +8 -1
  323. package/dist/types/transport/keepalive/WaKeepAlive.d.ts +4 -1
  324. package/dist/types/transport/node/WaMobileTcpSocket.d.ts +18 -0
  325. package/dist/types/transport/node/WaNodeOrchestrator.d.ts +6 -2
  326. package/dist/types/transport/node/builders/abprops.d.ts +5 -0
  327. package/dist/types/transport/node/builders/device.d.ts +2 -0
  328. package/dist/types/transport/node/builders/email.d.ts +11 -0
  329. package/dist/types/transport/node/builders/offline.d.ts +2 -0
  330. package/dist/types/transport/node/builders/prekeys.d.ts +4 -3
  331. package/dist/types/transport/node/builders/presence.d.ts +6 -0
  332. package/dist/types/transport/node/helpers.d.ts +3 -0
  333. package/dist/types/transport/node/mex/argo-decoder.d.ts +11 -0
  334. package/dist/types/transport/node/mex/client.d.ts +18 -0
  335. package/dist/types/transport/node/mex/persist-ids.d.ts +14 -0
  336. package/dist/types/transport/noise/WaMobileClientPayload.d.ts +29 -0
  337. package/dist/types/transport/noise/WaNoiseCert.d.ts +7 -1
  338. package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
  339. package/dist/types/transport/types.d.ts +8 -0
  340. package/package.json +6 -4
  341. package/dist/auth/pairing/constants.js +0 -5
  342. package/dist/client/connection/WaKeyShareCoordinator.js +0 -63
  343. package/dist/esm/auth/pairing/constants.js +0 -2
  344. package/dist/esm/client/connection/WaKeyShareCoordinator.js +0 -59
  345. package/dist/esm/transport/node/builders/index.js +0 -11
  346. package/dist/transport/node/builders/index.js +0 -51
  347. package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +0 -14
  348. package/dist/types/auth/pairing/constants.d.ts +0 -2
  349. package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +0 -14
  350. package/dist/types/transport/node/builders/index.d.ts +0 -11
  351. /package/dist/appstate/{WaAppStateSyncResponseParser.js → response-parser.js} +0 -0
@@ -0,0 +1,234 @@
1
+ import { createReadStream, createWriteStream } from 'node:fs';
2
+ import { open, unlink } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { pipeline } from 'node:stream/promises';
6
+ import { toBytesView } from '../util/bytes.js';
7
+ import { toError } from '../util/primitives.js';
8
+ export async function readFileHead(filePath, bytes) {
9
+ const fh = await open(filePath, 'r');
10
+ try {
11
+ const buf = new Uint8Array(bytes);
12
+ const { bytesRead } = await fh.read(buf, 0, bytes, 0);
13
+ return buf.subarray(0, bytesRead);
14
+ }
15
+ finally {
16
+ await fh.close();
17
+ }
18
+ }
19
+ const RIFF_HEADER_SIZE = 12;
20
+ const CHUNK_HEADER_SIZE = 8;
21
+ const CHUNK_ID_SIZE = 4;
22
+ const ANMF_ID = [0x41, 0x4e, 0x4d, 0x46];
23
+ export function parseWebpAnimation(data) {
24
+ if (data.byteLength < RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE)
25
+ return null;
26
+ let offset = RIFF_HEADER_SIZE;
27
+ if (data[offset] !== 0x56 ||
28
+ data[offset + 1] !== 0x50 ||
29
+ data[offset + 2] !== 0x38 ||
30
+ data[offset + 3] !== 0x58) {
31
+ return null;
32
+ }
33
+ // Skip VP8X chunk: 8 byte header + 10 byte payload
34
+ offset += CHUNK_HEADER_SIZE + 10;
35
+ while (offset + CHUNK_HEADER_SIZE <= data.byteLength) {
36
+ const isAnmf = data[offset] === ANMF_ID[0] &&
37
+ data[offset + 1] === ANMF_ID[1] &&
38
+ data[offset + 2] === ANMF_ID[2] &&
39
+ data[offset + 3] === ANMF_ID[3];
40
+ const sizeOffset = offset + CHUNK_ID_SIZE;
41
+ if (sizeOffset + 4 > data.byteLength)
42
+ return null;
43
+ let chunkSize = (data[sizeOffset] |
44
+ (data[sizeOffset + 1] << 8) |
45
+ (data[sizeOffset + 2] << 16) |
46
+ (data[sizeOffset + 3] << 24)) >>>
47
+ 0;
48
+ if (chunkSize % 2 !== 0)
49
+ chunkSize += 1;
50
+ const nextOffset = offset + CHUNK_HEADER_SIZE + chunkSize;
51
+ if (nextOffset <= offset || nextOffset > data.byteLength)
52
+ return null;
53
+ if (isAnmf) {
54
+ return { isAnimated: true, firstFrameLength: nextOffset };
55
+ }
56
+ offset = nextOffset;
57
+ }
58
+ return null;
59
+ }
60
+ const IMAGE_THUMB_MAX_EDGE = 320;
61
+ const VIDEO_THUMB_MAX_EDGE = 48;
62
+ const STICKER_THUMB_MAX_EDGE = 100;
63
+ const EMPTY_PROCESSED = {};
64
+ export function isReadableStream(value) {
65
+ return (!!value &&
66
+ typeof value === 'object' &&
67
+ 'pipe' in value &&
68
+ typeof value.pipe === 'function');
69
+ }
70
+ async function streamToTempFile(source) {
71
+ const filePath = join(tmpdir(), `zapo-media-${Date.now()}-${Math.random().toString(36).slice(2)}`);
72
+ try {
73
+ await pipeline(source, createWriteStream(filePath));
74
+ }
75
+ catch (error) {
76
+ await unlink(filePath).catch(() => undefined);
77
+ throw error;
78
+ }
79
+ return filePath;
80
+ }
81
+ export async function cleanupTempFile(filePath) {
82
+ await unlink(filePath).catch(() => undefined);
83
+ }
84
+ export async function resolveMediaInputs(shouldProcess, raw) {
85
+ if (typeof raw === 'string') {
86
+ return {
87
+ processorInput: raw,
88
+ uploadMedia: createReadStream(raw)
89
+ };
90
+ }
91
+ if (isReadableStream(raw)) {
92
+ if (shouldProcess) {
93
+ const tempFilePath = await streamToTempFile(raw);
94
+ return {
95
+ processorInput: tempFilePath,
96
+ uploadMedia: createReadStream(tempFilePath),
97
+ tempFilePath
98
+ };
99
+ }
100
+ return { uploadMedia: raw };
101
+ }
102
+ const bytes = toBytesView(raw);
103
+ return { processorInput: bytes, uploadMedia: bytes };
104
+ }
105
+ function shouldGenerateThumbnail(media, content) {
106
+ if (!media?.processor || media.generateThumbnail === false) {
107
+ return false;
108
+ }
109
+ switch (content.type) {
110
+ case 'image':
111
+ return content.jpegThumbnail === undefined && !!media.processor.generateImageThumbnail;
112
+ case 'video':
113
+ case 'ptv':
114
+ return content.jpegThumbnail === undefined && !!media.processor.generateVideoThumbnail;
115
+ case 'document':
116
+ return !!media.processor.generateImageThumbnail;
117
+ default:
118
+ return false;
119
+ }
120
+ }
121
+ function shouldProbeMedia(media, content) {
122
+ if (!media?.processor?.probeMedia || media.generateProbe === false) {
123
+ return false;
124
+ }
125
+ switch (content.type) {
126
+ case 'video':
127
+ case 'ptv':
128
+ return (content.seconds === undefined ||
129
+ content.width === undefined ||
130
+ content.height === undefined);
131
+ case 'audio':
132
+ return content.seconds === undefined;
133
+ default:
134
+ return false;
135
+ }
136
+ }
137
+ function shouldGenerateWaveform(media, content) {
138
+ return (!!media?.processor?.computeWaveform &&
139
+ media.generateWaveform !== false &&
140
+ content.type === 'audio' &&
141
+ content.waveform === undefined);
142
+ }
143
+ function shouldGenerateStickerThumbnail(media, content) {
144
+ return (!!media?.processor?.generateStickerThumbnail &&
145
+ media.generateStickerThumbnail !== false &&
146
+ content.type === 'sticker' &&
147
+ content.pngThumbnail === undefined);
148
+ }
149
+ export function hasMediaProcessingTasks(media, content) {
150
+ return (shouldGenerateThumbnail(media, content) ||
151
+ shouldProbeMedia(media, content) ||
152
+ shouldGenerateWaveform(media, content) ||
153
+ shouldGenerateStickerThumbnail(media, content));
154
+ }
155
+ async function runProcessorStep(step, content, logger, fn) {
156
+ try {
157
+ return await fn();
158
+ }
159
+ catch (error) {
160
+ logger.error('media processor step failed, skipping step', {
161
+ type: content.type,
162
+ step,
163
+ message: toError(error).message
164
+ });
165
+ return null;
166
+ }
167
+ }
168
+ export async function runMediaProcessor(media, input, content, logger) {
169
+ const processor = media?.processor;
170
+ if (!processor || !hasMediaProcessingTasks(media, content) || !input)
171
+ return EMPTY_PROCESSED;
172
+ const result = {};
173
+ const isVideo = content.type === 'video' || content.type === 'ptv';
174
+ const thumbFn = isVideo ? processor.generateVideoThumbnail : processor.generateImageThumbnail;
175
+ const thumbMaxEdge = isVideo ? VIDEO_THUMB_MAX_EDGE : IMAGE_THUMB_MAX_EDGE;
176
+ const thumbTask = shouldGenerateThumbnail(media, content)
177
+ ? runProcessorStep('thumbnail', content, logger, () => thumbFn(input, thumbMaxEdge))
178
+ : null;
179
+ const probeTask = shouldProbeMedia(media, content)
180
+ ? runProcessorStep('probe', content, logger, () => processor.probeMedia(input))
181
+ : null;
182
+ const [thumb, probe] = await Promise.all([thumbTask, probeTask]);
183
+ if (thumb) {
184
+ result.jpegThumbnail = thumb.jpegThumbnail;
185
+ if (!isVideo && !probe) {
186
+ result.width = thumb.width;
187
+ result.height = thumb.height;
188
+ }
189
+ }
190
+ if (probe) {
191
+ if (probe.durationSeconds !== undefined &&
192
+ !('seconds' in content && content.seconds !== undefined)) {
193
+ result.seconds = Math.floor(probe.durationSeconds);
194
+ }
195
+ if (!('width' in content && content.width !== undefined) && probe.width !== undefined) {
196
+ result.width = probe.width;
197
+ }
198
+ if (!('height' in content && content.height !== undefined) && probe.height !== undefined) {
199
+ result.height = probe.height;
200
+ }
201
+ }
202
+ if (shouldGenerateWaveform(media, content)) {
203
+ const waveformResult = await runProcessorStep('waveform', content, logger, () => processor.computeWaveform(input));
204
+ if (waveformResult) {
205
+ result.waveform = waveformResult.waveform;
206
+ if (result.seconds === undefined &&
207
+ !('seconds' in content && content.seconds !== undefined)) {
208
+ result.seconds = Math.floor(waveformResult.durationSeconds);
209
+ }
210
+ }
211
+ }
212
+ if (content.type === 'sticker') {
213
+ if (shouldGenerateStickerThumbnail(media, content)) {
214
+ const stickerThumb = await runProcessorStep('stickerThumbnail', content, logger, () => processor.generateStickerThumbnail(input, STICKER_THUMB_MAX_EDGE));
215
+ if (stickerThumb) {
216
+ result.pngThumbnail = stickerThumb.pngThumbnail;
217
+ result.width = stickerThumb.width;
218
+ result.height = stickerThumb.height;
219
+ }
220
+ }
221
+ if (content.isAnimated === undefined) {
222
+ const header = typeof input === 'string' ? await readFileHead(input, 100) : input;
223
+ const anim = parseWebpAnimation(header);
224
+ if (anim) {
225
+ result.isAnimated = true;
226
+ result.firstFrameLength = anim.firstFrameLength;
227
+ }
228
+ else {
229
+ result.isAnimated = false;
230
+ }
231
+ }
232
+ }
233
+ return result;
234
+ }
@@ -1,11 +1,12 @@
1
1
  import { createReadStream } from 'node:fs';
2
+ import { cleanupTempFile, hasMediaProcessingTasks, isReadableStream, parseWebpAnimation, readFileHead, resolveMediaInputs, runMediaProcessor } from './media.js';
2
3
  import { parseMediaConnResponse } from '../media/conn.js';
3
4
  import { MEDIA_CONN_CACHE_GRACE_MS, MEDIA_UPLOAD_PATHS } from '../media/constants.js';
4
5
  import { WaMediaCrypto } from '../media/WaMediaCrypto.js';
5
6
  import { isSendMediaMessage } from '../message/content.js';
6
7
  import { WA_DEFAULTS } from '../protocol/constants.js';
7
8
  import { buildMediaConnIq } from '../transport/node/builders/media.js';
8
- import { bytesToBase64UrlSafe, TEXT_DECODER, toBytesView } from '../util/bytes.js';
9
+ import { bytesToBase64UrlSafe, TEXT_DECODER } from '../util/bytes.js';
9
10
  import { toError } from '../util/primitives.js';
10
11
  export async function buildMediaMessageContent(options, content) {
11
12
  if (typeof content === 'string') {
@@ -31,6 +32,9 @@ export async function getMediaConn(options, forceRefresh = false) {
31
32
  options.setMediaConnCache(mediaConn);
32
33
  return mediaConn;
33
34
  }
35
+ function needsSidecar(content) {
36
+ return content.type === 'video' || content.type === 'ptv' || content.type === 'audio';
37
+ }
34
38
  function resolveUploadType(content) {
35
39
  if (content.type === 'video' && content.gifPlayback)
36
40
  return 'gif';
@@ -38,85 +42,146 @@ function resolveUploadType(content) {
38
42
  return 'ptt';
39
43
  return content.type;
40
44
  }
41
- function isReadableStream(value) {
42
- return (!!value &&
43
- typeof value === 'object' &&
44
- 'pipe' in value &&
45
- typeof value.pipe === 'function');
45
+ function resolveMimetype(content) {
46
+ if (content.mimetype)
47
+ return content.mimetype;
48
+ if (content.type === 'sticker')
49
+ return 'image/webp';
50
+ throw new Error(`mimetype is required for ${content.type} messages`);
46
51
  }
47
52
  async function buildMediaMessage(options, content) {
48
- const uploaded = isReadableStream(content.media)
49
- ? await uploadMediaStream(options, content, content.media)
50
- : await uploadMediaBytes(options, content, toBytesView(content.media));
51
- const mediaKeyTimestamp = Math.floor(Date.now() / 1000);
52
- const common = {
53
- url: uploaded.url,
54
- mimetype: content.mimetype,
55
- fileSha256: uploaded.fileSha256,
56
- fileLength: uploaded.fileLength,
57
- mediaKey: uploaded.mediaKey,
58
- fileEncSha256: uploaded.fileEncSha256,
59
- directPath: uploaded.directPath,
60
- mediaKeyTimestamp
61
- };
62
- switch (content.type) {
63
- case 'image':
64
- return {
65
- imageMessage: {
66
- ...common,
67
- caption: content.caption,
68
- width: content.width,
69
- height: content.height
70
- }
71
- };
72
- case 'video':
73
- return {
74
- videoMessage: {
75
- ...common,
76
- caption: content.caption,
77
- gifPlayback: content.gifPlayback,
78
- seconds: content.seconds,
79
- width: content.width,
80
- height: content.height,
81
- metadataUrl: uploaded.metadataUrl
82
- }
83
- };
84
- case 'ptv':
85
- return {
86
- ptvMessage: {
87
- ...common,
88
- seconds: content.seconds,
89
- width: content.width,
90
- height: content.height
91
- }
92
- };
93
- case 'audio':
94
- return {
95
- audioMessage: {
96
- ...common,
97
- seconds: content.seconds,
98
- ptt: content.ptt
99
- }
100
- };
101
- case 'document':
102
- return {
103
- documentMessage: {
104
- ...common,
105
- caption: content.caption,
106
- fileName: content.fileName ?? 'file',
107
- title: content.fileName ?? undefined
108
- }
109
- };
110
- case 'sticker':
111
- return {
112
- stickerMessage: {
113
- ...common,
114
- width: content.width,
115
- height: content.height
53
+ const needsTempFile = hasMediaProcessingTasks(options.media, content) ||
54
+ (content.type === 'sticker' && content.firstFrameLength === undefined);
55
+ const resolved = await resolveMediaInputs(needsTempFile, content.media);
56
+ try {
57
+ let detectedFirstFrameLength;
58
+ if (content.type === 'sticker' &&
59
+ content.firstFrameLength === undefined &&
60
+ resolved.processorInput) {
61
+ const input = resolved.processorInput;
62
+ const header = typeof input === 'string' ? await readFileHead(input, 100) : input.subarray(0, 100);
63
+ detectedFirstFrameLength = parseWebpAnimation(header)?.firstFrameLength;
64
+ }
65
+ const firstFrameLength = content.type === 'sticker'
66
+ ? (content.firstFrameLength ?? detectedFirstFrameLength)
67
+ : undefined;
68
+ const uploadPromise = isReadableStream(resolved.uploadMedia)
69
+ ? uploadMediaStream(options, content, resolved.uploadMedia, firstFrameLength)
70
+ : uploadMediaBytes(options, content, resolved.uploadMedia, firstFrameLength);
71
+ const processPromise = runMediaProcessor(options.media, resolved.processorInput, content, options.logger);
72
+ const [uploadResult, processResult] = await Promise.allSettled([
73
+ uploadPromise,
74
+ processPromise
75
+ ]);
76
+ if (uploadResult.status === 'rejected')
77
+ throw uploadResult.reason;
78
+ if (processResult.status === 'rejected')
79
+ throw processResult.reason;
80
+ const uploaded = uploadResult.value;
81
+ const processed = processResult.value;
82
+ const mediaKeyTimestamp = Math.floor(Date.now() / 1000);
83
+ const uploadedFields = {
84
+ url: uploaded.url,
85
+ fileSha256: uploaded.fileSha256,
86
+ fileLength: uploaded.fileLength,
87
+ mediaKey: uploaded.mediaKey,
88
+ fileEncSha256: uploaded.fileEncSha256,
89
+ directPath: uploaded.directPath,
90
+ mediaKeyTimestamp,
91
+ mimetype: resolveMimetype(content)
92
+ };
93
+ function spread(c) {
94
+ const result = {};
95
+ for (const key in c) {
96
+ if (key !== 'type' &&
97
+ key !== 'media' &&
98
+ key !== 'fileLength' &&
99
+ key !== 'mimetype') {
100
+ result[key] = c[key];
116
101
  }
117
- };
118
- default:
119
- throw new Error(`unsupported media message type: ${String(content.type)}`);
102
+ }
103
+ return result;
104
+ }
105
+ switch (content.type) {
106
+ case 'image':
107
+ return {
108
+ imageMessage: {
109
+ ...spread(content),
110
+ ...uploadedFields,
111
+ width: content.width ?? processed.width,
112
+ height: content.height ?? processed.height,
113
+ jpegThumbnail: content.jpegThumbnail ?? processed.jpegThumbnail
114
+ }
115
+ };
116
+ case 'video':
117
+ return {
118
+ videoMessage: {
119
+ ...spread(content),
120
+ ...uploadedFields,
121
+ seconds: content.seconds ?? processed.seconds,
122
+ width: content.width ?? processed.width,
123
+ height: content.height ?? processed.height,
124
+ jpegThumbnail: content.jpegThumbnail ?? processed.jpegThumbnail,
125
+ streamingSidecar: uploaded.streamingSidecar,
126
+ metadataUrl: uploaded.metadataUrl
127
+ }
128
+ };
129
+ case 'ptv':
130
+ return {
131
+ ptvMessage: {
132
+ ...spread(content),
133
+ ...uploadedFields,
134
+ seconds: content.seconds ?? processed.seconds,
135
+ width: content.width ?? processed.width,
136
+ height: content.height ?? processed.height,
137
+ jpegThumbnail: content.jpegThumbnail ?? processed.jpegThumbnail,
138
+ streamingSidecar: uploaded.streamingSidecar
139
+ }
140
+ };
141
+ case 'audio':
142
+ return {
143
+ audioMessage: {
144
+ ...spread(content),
145
+ ...uploadedFields,
146
+ seconds: content.seconds ?? processed.seconds,
147
+ streamingSidecar: uploaded.streamingSidecar,
148
+ waveform: content.waveform ?? processed.waveform
149
+ }
150
+ };
151
+ case 'document':
152
+ return {
153
+ documentMessage: {
154
+ ...spread(content),
155
+ ...uploadedFields,
156
+ fileName: content.fileName ?? 'file',
157
+ title: content.title ?? content.fileName ?? undefined,
158
+ jpegThumbnail: content.jpegThumbnail ?? processed.jpegThumbnail
159
+ }
160
+ };
161
+ case 'sticker':
162
+ return {
163
+ stickerMessage: {
164
+ ...spread(content),
165
+ ...uploadedFields,
166
+ width: content.width ?? processed.width,
167
+ height: content.height ?? processed.height,
168
+ pngThumbnail: content.pngThumbnail ?? processed.pngThumbnail,
169
+ isAnimated: content.isAnimated ??
170
+ processed.isAnimated ??
171
+ firstFrameLength !== undefined,
172
+ firstFrameLength: content.firstFrameLength ?? uploaded.firstFrameLength,
173
+ firstFrameSidecar: content.firstFrameSidecar ?? uploaded.firstFrameSidecar,
174
+ stickerSentTs: content.stickerSentTs ?? Date.now()
175
+ }
176
+ };
177
+ default:
178
+ throw new Error(`unsupported media message type: ${String(content.type)}`);
179
+ }
180
+ }
181
+ finally {
182
+ if (resolved.tempFilePath) {
183
+ await cleanupTempFile(resolved.tempFilePath);
184
+ }
120
185
  }
121
186
  }
122
187
  function buildUploadUrl(host, uploadType, auth, fileEncSha256) {
@@ -147,11 +212,14 @@ function parseUploadResponse(body, status) {
147
212
  ...(parsed.metadata_url ? { metadataUrl: parsed.metadata_url } : {})
148
213
  };
149
214
  }
150
- async function uploadMediaBytes(options, content, mediaBytes) {
215
+ async function uploadMediaBytes(options, content, mediaBytes, firstFrameLength) {
151
216
  const uploadType = resolveUploadType(content);
152
217
  const mediaKey = await WaMediaCrypto.generateMediaKey();
153
218
  const [encrypted, mediaConn] = await Promise.all([
154
- WaMediaCrypto.encryptBytes(uploadType, mediaKey, mediaBytes),
219
+ WaMediaCrypto.encryptBytes(uploadType, mediaKey, mediaBytes, {
220
+ sidecar: needsSidecar(content),
221
+ firstFrameLength
222
+ }),
155
223
  getMediaConn(options)
156
224
  ]);
157
225
  const selectedHost = mediaConn.hosts.find((host) => !host.isFallback)?.hostname ?? mediaConn.hosts[0].hostname;
@@ -166,7 +234,7 @@ async function uploadMediaBytes(options, content, mediaBytes) {
166
234
  method: 'POST',
167
235
  body: encrypted.ciphertextHmac,
168
236
  contentLength: encrypted.ciphertextHmac.byteLength,
169
- contentType: content.mimetype
237
+ contentType: resolveMimetype(content)
170
238
  });
171
239
  const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
172
240
  const parsed = parseUploadResponse(responseBody, uploadResponse.status);
@@ -175,13 +243,19 @@ async function uploadMediaBytes(options, content, mediaBytes) {
175
243
  mediaKey,
176
244
  fileSha256: encrypted.fileSha256,
177
245
  fileEncSha256: encrypted.fileEncSha256,
178
- fileLength: mediaBytes.byteLength
246
+ fileLength: mediaBytes.byteLength,
247
+ streamingSidecar: encrypted.streamingSidecar,
248
+ firstFrameSidecar: encrypted.firstFrameSidecar,
249
+ firstFrameLength
179
250
  };
180
251
  }
181
- async function uploadMediaStream(options, content, stream) {
252
+ async function uploadMediaStream(options, content, stream, firstFrameLength) {
182
253
  const uploadType = resolveUploadType(content);
183
254
  const mediaKey = await WaMediaCrypto.generateMediaKey();
184
- const encResult = await WaMediaCrypto.encryptToFile(uploadType, mediaKey, stream);
255
+ const encResult = await WaMediaCrypto.encryptToFile(uploadType, mediaKey, stream, {
256
+ sidecar: needsSidecar(content),
257
+ firstFrameLength
258
+ });
185
259
  let readStream;
186
260
  try {
187
261
  const mediaConn = await getMediaConn(options);
@@ -200,7 +274,7 @@ async function uploadMediaStream(options, content, stream) {
200
274
  method: 'POST',
201
275
  body: readStream,
202
276
  contentLength: encResult.fileSize,
203
- contentType: content.mimetype
277
+ contentType: resolveMimetype(content)
204
278
  });
205
279
  const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
206
280
  const parsed = parseUploadResponse(responseBody, uploadResponse.status);
@@ -209,7 +283,10 @@ async function uploadMediaStream(options, content, stream) {
209
283
  mediaKey,
210
284
  fileSha256: encResult.fileSha256,
211
285
  fileEncSha256: encResult.fileEncSha256,
212
- fileLength: encResult.plaintextLength
286
+ fileLength: encResult.plaintextLength,
287
+ streamingSidecar: encResult.streamingSidecar,
288
+ firstFrameSidecar: encResult.firstFrameSidecar,
289
+ firstFrameLength
213
290
  };
214
291
  }
215
292
  finally {
@@ -8,3 +8,4 @@ export { toSerializedPubKey, toRawPubKey, prependVersion, readVersionedContent }
8
8
  export { buildNonce } from '../core/nonce.js';
9
9
  export { randomBytesAsync, randomFillAsync, randomIntAsync } from '../core/random.js';
10
10
  export { sha1, sha256, sha512, importAesGcmKey, aesGcmEncrypt, aesGcmDecrypt, importAesCbcKey, aesCbcEncrypt, aesCbcDecrypt, importHmacKey, importHmacSha512Key, hmacSign, pbkdf2DeriveAesCtrKey, aesCtrEncrypt, aesCtrDecrypt } from '../core/primitives.js';
11
+ export { xeddsaSign, xeddsaVerify } from '../core/xeddsa.js';
@@ -1,11 +1,5 @@
1
1
  import { randomBytes, randomFill, randomInt } from 'node:crypto';
2
2
  import { promisify } from 'node:util';
3
- import { toBytesView } from '../../util/bytes.js';
4
- const randomBytesAsyncImpl = promisify(randomBytes);
5
- const randomIntAsyncImpl = promisify(randomInt);
6
- export async function randomBytesAsync(size) {
7
- return toBytesView(await randomBytesAsyncImpl(size));
8
- }
9
3
  export async function randomFillAsync(target, offset, size) {
10
4
  await new Promise((resolve, reject) => {
11
5
  const onDone = (error) => {
@@ -27,4 +21,5 @@ export async function randomFillAsync(target, offset, size) {
27
21
  });
28
22
  return target;
29
23
  }
30
- export const randomIntAsync = randomIntAsyncImpl;
24
+ export const randomIntAsync = promisify(randomInt);
25
+ export const randomBytesAsync = promisify(randomBytes);
@@ -0,0 +1,53 @@
1
+ import { sha512 } from '../core/primitives.js';
2
+ import { randomBytesAsync } from '../core/random.js';
3
+ import { Ed25519 } from '../curves/Ed25519.js';
4
+ import { clampCurvePrivateKeyInPlace, montgomeryToEdwardsPublic } from '../curves/X25519.js';
5
+ import { encodeExtendedPoint, scalarMultBase } from '../math/edwards.js';
6
+ import { bigIntToBytesLE, bytesToBigIntLE } from '../math/le.js';
7
+ import { modGroup } from '../math/mod.js';
8
+ import { assertByteLength, concatBytes } from '../../util/bytes.js';
9
+ const PREFIX_SIGNATURE_RANDOM = new Uint8Array([
10
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
11
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
12
+ ]);
13
+ export async function xeddsaVerify(curvePublicKey, message, signature) {
14
+ if (signature.length !== 64) {
15
+ return false;
16
+ }
17
+ if ((signature[63] & 0x60) !== 0) {
18
+ return false;
19
+ }
20
+ const lastByteIndex = 63;
21
+ const originalLastByte = signature[lastByteIndex];
22
+ const signBit = originalLastByte & 0x80;
23
+ signature[lastByteIndex] = originalLastByte & 0x7f;
24
+ const edPublic = montgomeryToEdwardsPublic(curvePublicKey, signBit);
25
+ try {
26
+ return await Ed25519.verify(message, signature, edPublic);
27
+ }
28
+ finally {
29
+ signature[lastByteIndex] = originalLastByte;
30
+ }
31
+ }
32
+ export async function xeddsaSign(privateKey, message) {
33
+ assertByteLength(privateKey, 32, `invalid curve25519 private key length ${privateKey.length}`);
34
+ const clampedPrivateKey = clampCurvePrivateKeyInPlace(privateKey);
35
+ const privateScalar = bytesToBigIntLE(clampedPrivateKey);
36
+ const encodedPublic = encodeExtendedPoint(scalarMultBase(privateScalar));
37
+ const pubKeySignBit = encodedPublic[31] & 0x80;
38
+ const randomSuffix = await randomBytesAsync(64);
39
+ const hashInput = concatBytes([
40
+ PREFIX_SIGNATURE_RANDOM,
41
+ clampedPrivateKey,
42
+ message,
43
+ randomSuffix
44
+ ]);
45
+ const r = modGroup(bytesToBigIntLE(await sha512(hashInput)));
46
+ const encodedR = encodeExtendedPoint(scalarMultBase(r));
47
+ const hInput = concatBytes([encodedR, encodedPublic, message]);
48
+ const h = modGroup(bytesToBigIntLE(await sha512(hInput)));
49
+ const s = modGroup(r + h * privateScalar);
50
+ const encodedS = bigIntToBytesLE(s, 32);
51
+ encodedS[31] = (encodedS[31] & 0x7f) | pubKeySignBit;
52
+ return concatBytes([encodedR, encodedS]);
53
+ }
@@ -1,9 +1,11 @@
1
- import { webcrypto } from 'node:crypto';
2
- import { X25519_PKCS8_PREFIX } from '../curves/constants.js';
1
+ import { createPrivateKey, createPublicKey, diffieHellman, webcrypto } from 'node:crypto';
2
+ import { X25519_PKCS8_PREFIX, X25519_SPKI_PREFIX } from '../curves/constants.js';
3
3
  import { pkcs8FromRawPrivate } from '../curves/types.js';
4
4
  import { FE_ONE } from '../math/constants.js';
5
5
  import { fe, feAdd, feFromBytes, feInv, feMul, fePack, feSub } from '../math/fe.js';
6
6
  import { assertByteLength, decodeBase64Url, toBytesView } from '../../util/bytes.js';
7
+ import { isBunRuntime } from '../../util/runtime.js';
8
+ const IS_BUN = isBunRuntime();
7
9
  // Pre-allocated temps for montgomeryToEdwardsPublic (safe: single-threaded)
8
10
  const _mx = fe();
9
11
  const _m1 = fe();
@@ -64,6 +66,22 @@ export class X25519 {
64
66
  static async scalarMult(privKey, pubKey) {
65
67
  assertByteLength(privKey, 32, 'x25519 private key must be 32 bytes');
66
68
  assertByteLength(pubKey, 32, 'x25519 public key must be 32 bytes');
69
+ // TODO: When Bun supports deriveBits with X25519 change to Async Web Crypto API
70
+ // https://github.com/oven-sh/bun/pull/29152
71
+ if (IS_BUN) {
72
+ const spki = new Uint8Array(X25519_SPKI_PREFIX.length + 32);
73
+ spki.set(X25519_SPKI_PREFIX, 0);
74
+ spki.set(pubKey, X25519_SPKI_PREFIX.length);
75
+ const shared = diffieHellman({
76
+ privateKey: createPrivateKey({
77
+ key: pkcs8FromRawPrivate(X25519_PKCS8_PREFIX, privKey),
78
+ format: 'der',
79
+ type: 'pkcs8'
80
+ }),
81
+ publicKey: createPublicKey({ key: spki, format: 'der', type: 'spki' })
82
+ });
83
+ return toBytesView(shared);
84
+ }
67
85
  const [privateKey, publicKey] = await Promise.all([
68
86
  webcrypto.subtle.importKey('pkcs8', pkcs8FromRawPrivate(X25519_PKCS8_PREFIX, privKey), { name: 'X25519' }, false, ['deriveBits']),
69
87
  webcrypto.subtle.importKey('raw', pubKey, { name: 'X25519' }, false, [])
@@ -1,3 +1,4 @@
1
1
  import { hexToBytes } from '../../util/bytes.js';
2
2
  export const X25519_PKCS8_PREFIX = hexToBytes('302e020100300506032b656e04220420');
3
+ export const X25519_SPKI_PREFIX = hexToBytes('302a300506032b656e032100');
3
4
  export const ED25519_PKCS8_PREFIX = hexToBytes('302e020100300506032b657004220420');