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
@@ -25,12 +25,14 @@ export class StoreLock {
25
25
  current = Promise.reject(error);
26
26
  }
27
27
  }
28
- const tracker = current.then(() => undefined, () => undefined);
28
+ const tracker = current.then(StoreLock._noop, StoreLock._noop);
29
29
  this.chains.set(key, tracker);
30
- void tracker.finally(() => {
31
- if (this.chains.get(key) === tracker) {
30
+ tracker.then(() => {
31
+ if (this.chains.get(key) === tracker)
32
+ this.chains.delete(key);
33
+ }, () => {
34
+ if (this.chains.get(key) === tracker)
32
35
  this.chains.delete(key);
33
- }
34
36
  });
35
37
  return current;
36
38
  }
@@ -75,3 +77,4 @@ export class StoreLock {
75
77
  }
76
78
  }
77
79
  }
80
+ StoreLock._noop = () => undefined;
@@ -1,4 +1,4 @@
1
- import { createCipheriv, createDecipheriv, createHash, createHmac } from 'node:crypto';
1
+ import { createHash, createHmac } from 'node:crypto';
2
2
  import { once } from 'node:events';
3
3
  import { createWriteStream } from 'node:fs';
4
4
  import { stat, unlink } from 'node:fs/promises';
@@ -8,10 +8,91 @@ import { PassThrough } from 'node:stream';
8
8
  import { hkdf } from '../crypto/core/hkdf.js';
9
9
  import { aesCbcDecrypt, aesCbcEncrypt, hmacSign, importAesCbcKey, importHmacKey, sha256 } from '../crypto/core/primitives.js';
10
10
  import { randomBytesAsync } from '../crypto/core/random.js';
11
- import { ENC_KEY_END, ENC_KEY_START, HMAC_TRUNCATED_SIZE, IV_SIZE, MAC_KEY_END, MAC_KEY_START, MEDIA_HKDF_SIZE } from './constants.js';
12
- import { WA_APP_STATE_KEY_TYPES, getWaMediaHkdfInfo } from '../protocol/constants.js';
13
- import { assertByteLength, concatBytes, EMPTY_BYTES, toChunkBytes, toBytesView, uint8Equal, uint8TimingSafeEqual } from '../util/bytes.js';
11
+ import { ENC_KEY_END, ENC_KEY_START, HMAC_TRUNCATED_SIZE, IV_SIZE, MAC_KEY_END, MAC_KEY_START, MEDIA_HKDF_SIZE, SIDECAR_CHUNK_SIZE, SIDECAR_HMAC_SIZE } from './constants.js';
12
+ import { getWaMediaHkdfInfo, WA_APP_STATE_KEY_TYPES } from '../protocol/constants.js';
13
+ import { assertByteLength, concatBytes, EMPTY_BYTES, toBytesView, toChunkBytes, uint8Equal, uint8TimingSafeEqual } from '../util/bytes.js';
14
14
  import { toError } from '../util/primitives.js';
15
+ const AES_BLOCK_SIZE = 16;
16
+ const PKCS7_FULL_BLOCK = new Uint8Array(AES_BLOCK_SIZE).fill(AES_BLOCK_SIZE);
17
+ async function aesCbcEncryptChunk(key, iv, chunk, isFinal) {
18
+ const encrypted = await aesCbcEncrypt(key, iv, chunk);
19
+ if (isFinal) {
20
+ return {
21
+ ciphertext: encrypted,
22
+ nextIv: encrypted.subarray(encrypted.byteLength - AES_BLOCK_SIZE)
23
+ };
24
+ }
25
+ const ciphertext = encrypted.subarray(0, encrypted.byteLength - AES_BLOCK_SIZE);
26
+ return {
27
+ ciphertext,
28
+ nextIv: ciphertext.subarray(ciphertext.byteLength - AES_BLOCK_SIZE)
29
+ };
30
+ }
31
+ async function aesCbcDecryptChunk(key, iv, ciphertext, isFinal) {
32
+ const nextIv = toBytesView(ciphertext.subarray(ciphertext.byteLength - AES_BLOCK_SIZE));
33
+ if (isFinal) {
34
+ return { plaintext: await aesCbcDecrypt(key, iv, ciphertext), nextIv };
35
+ }
36
+ const padBlock = (await aesCbcEncrypt(key, nextIv, PKCS7_FULL_BLOCK)).subarray(0, AES_BLOCK_SIZE);
37
+ const withPad = concatBytes([ciphertext, padBlock]);
38
+ return { plaintext: await aesCbcDecrypt(key, iv, withPad), nextIv };
39
+ }
40
+ async function computeFirstFrameSidecar(macKey, ivCiphertext, firstFrameLength) {
41
+ const aligned = Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
42
+ const slice = ivCiphertext.subarray(0, IV_SIZE + aligned);
43
+ const key = await importHmacKey(macKey);
44
+ const digest = await hmacSign(key, slice);
45
+ return digest.subarray(0, SIDECAR_HMAC_SIZE);
46
+ }
47
+ class SidecarAccumulator {
48
+ constructor(macKey, estimatedSize = 0) {
49
+ this.resultOffset = 0;
50
+ this.totalPushed = 0;
51
+ this.windowOffset = 0;
52
+ this.nextChunkStart = 0;
53
+ this.macKey = macKey;
54
+ this.window = new Uint8Array(IV_SIZE + SIDECAR_CHUNK_SIZE);
55
+ const estimated = Math.max(Math.ceil(estimatedSize / SIDECAR_CHUNK_SIZE) + 1, 16);
56
+ this.result = new Uint8Array(estimated * SIDECAR_HMAC_SIZE);
57
+ }
58
+ push(data) {
59
+ let srcOffset = 0;
60
+ while (srcOffset < data.byteLength) {
61
+ const windowEnd = this.nextChunkStart + IV_SIZE + SIDECAR_CHUNK_SIZE;
62
+ const remaining = windowEnd - this.totalPushed;
63
+ const toCopy = Math.min(remaining, data.byteLength - srcOffset);
64
+ this.window.set(data.subarray(srcOffset, srcOffset + toCopy), this.windowOffset);
65
+ this.windowOffset += toCopy;
66
+ this.totalPushed += toCopy;
67
+ srcOffset += toCopy;
68
+ if (this.totalPushed === windowEnd) {
69
+ this.flushChunk();
70
+ }
71
+ }
72
+ }
73
+ finish() {
74
+ if (this.windowOffset > 0) {
75
+ this.flushChunk();
76
+ }
77
+ return this.result.subarray(0, this.resultOffset);
78
+ }
79
+ flushChunk() {
80
+ const digest = createHmac('sha256', this.macKey)
81
+ .update(this.window.subarray(0, this.windowOffset))
82
+ .digest();
83
+ if (this.resultOffset + SIDECAR_HMAC_SIZE > this.result.byteLength) {
84
+ const grown = new Uint8Array(this.result.byteLength * 2);
85
+ grown.set(this.result);
86
+ this.result = grown;
87
+ }
88
+ this.result.set(digest.subarray(0, SIDECAR_HMAC_SIZE), this.resultOffset);
89
+ this.resultOffset += SIDECAR_HMAC_SIZE;
90
+ this.nextChunkStart += SIDECAR_CHUNK_SIZE;
91
+ const overlapSrc = this.window.subarray(this.windowOffset - IV_SIZE, this.windowOffset);
92
+ this.window.set(overlapSrc, 0);
93
+ this.windowOffset = IV_SIZE;
94
+ }
95
+ }
15
96
  export class WaMediaCrypto {
16
97
  static async generateMediaKey() {
17
98
  return randomBytesAsync(32);
@@ -27,24 +108,41 @@ export class WaMediaCrypto {
27
108
  refKey: expanded.subarray(MAC_KEY_END, MEDIA_HKDF_SIZE)
28
109
  };
29
110
  }
30
- static async encryptBytes(mediaType, mediaKey, plaintext) {
111
+ static async encryptBytes(mediaType, mediaKey, plaintext, options) {
31
112
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
32
- const [aesKey, macKey] = await Promise.all([
113
+ const [aesKey, hmacKey] = await Promise.all([
33
114
  importAesCbcKey(keys.encKey),
34
115
  importHmacKey(keys.macKey)
35
116
  ]);
36
117
  const ciphertext = await aesCbcEncrypt(aesKey, keys.iv, plaintext);
37
118
  const ivCiphertext = concatBytes([keys.iv, ciphertext]);
38
- const mac = await hmacSign(macKey, ivCiphertext);
119
+ const mac = await hmacSign(hmacKey, ivCiphertext);
39
120
  const signature = mac.subarray(0, HMAC_TRUNCATED_SIZE);
40
121
  const ciphertextHmac = concatBytes([ciphertext, signature]);
122
+ let streamingSidecar;
123
+ if (options?.sidecar !== false) {
124
+ const acc = new SidecarAccumulator(keys.macKey);
125
+ acc.push(keys.iv);
126
+ acc.push(ciphertext);
127
+ acc.push(signature);
128
+ streamingSidecar = acc.finish();
129
+ }
130
+ const firstFrameSidecar = options?.firstFrameLength !== undefined
131
+ ? await computeFirstFrameSidecar(keys.macKey, ivCiphertext, options.firstFrameLength)
132
+ : undefined;
41
133
  const [fileSha256, fileEncSha256] = await Promise.all([
42
134
  sha256(plaintext),
43
135
  sha256(ciphertextHmac)
44
136
  ]);
45
- return { ciphertextHmac, fileSha256, fileEncSha256 };
137
+ return {
138
+ ciphertextHmac,
139
+ fileSha256,
140
+ fileEncSha256,
141
+ streamingSidecar,
142
+ firstFrameSidecar
143
+ };
46
144
  }
47
- static async decryptBytes(mediaType, mediaKey, ciphertextHmac, expectedFileSha256, expectedFileEncSha256) {
145
+ static async decryptBytes(mediaType, mediaKey, ciphertextHmac, expectedFileSha256, expectedFileEncSha256, skipMacVerification = false) {
48
146
  if (ciphertextHmac.byteLength < HMAC_TRUNCATED_SIZE) {
49
147
  throw new Error(`ciphertext too short: ${ciphertextHmac.byteLength}`);
50
148
  }
@@ -55,17 +153,19 @@ export class WaMediaCrypto {
55
153
  }
56
154
  }
57
155
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
156
+ const [aesKey, hmacKey] = await Promise.all([
157
+ importAesCbcKey(keys.encKey),
158
+ importHmacKey(keys.macKey)
159
+ ]);
58
160
  const ciphertext = ciphertextHmac.subarray(0, ciphertextHmac.byteLength - HMAC_TRUNCATED_SIZE);
59
161
  const expectedMac = ciphertextHmac.subarray(ciphertextHmac.byteLength - HMAC_TRUNCATED_SIZE);
60
162
  const ivCiphertext = concatBytes([keys.iv, ciphertext]);
61
- const [macKey, aesKey] = await Promise.all([
62
- importHmacKey(keys.macKey),
63
- importAesCbcKey(keys.encKey)
64
- ]);
65
- const mac = await hmacSign(macKey, ivCiphertext);
66
- const signature = mac.subarray(0, HMAC_TRUNCATED_SIZE);
67
- if (!uint8TimingSafeEqual(signature, expectedMac)) {
68
- throw new Error('media MAC mismatch');
163
+ if (!skipMacVerification) {
164
+ const mac = await hmacSign(hmacKey, ivCiphertext);
165
+ const signature = mac.subarray(0, HMAC_TRUNCATED_SIZE);
166
+ if (!uint8TimingSafeEqual(signature, expectedMac)) {
167
+ throw new Error('media MAC mismatch');
168
+ }
69
169
  }
70
170
  const plaintext = await aesCbcDecrypt(aesKey, keys.iv, ciphertext);
71
171
  const fileSha256 = await sha256(plaintext);
@@ -75,18 +175,18 @@ export class WaMediaCrypto {
75
175
  const fileEncSha256 = expectedFileEncSha256 ?? (await sha256(ciphertextHmac));
76
176
  return { plaintext, fileSha256, fileEncSha256 };
77
177
  }
78
- static async encryptReadable(mediaType, mediaKey, plaintext) {
178
+ static async encryptReadable(mediaType, mediaKey, plaintext, options) {
79
179
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
80
180
  const encrypted = new PassThrough();
81
- const metadata = pumpEncryption(plaintext, encrypted, keys);
181
+ const metadata = pumpEncryption(plaintext, encrypted, keys, options?.sidecar !== false, options?.firstFrameLength);
82
182
  return { encrypted, metadata };
83
183
  }
84
- static async encryptToFile(mediaType, mediaKey, plaintext) {
184
+ static async encryptToFile(mediaType, mediaKey, plaintext, options) {
85
185
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
86
186
  const filePath = join(tmpdir(), `zapo-enc-${Date.now()}-${Math.random().toString(36).slice(2)}`);
87
187
  const output = createWriteStream(filePath);
88
188
  try {
89
- const metadata = await pumpEncryptionToWritable(plaintext, output, keys);
189
+ const metadata = await pumpEncryptionToWritable(plaintext, output, keys, options?.sidecar !== false, options?.firstFrameLength);
90
190
  const fileSize = (await stat(filePath)).size;
91
191
  return { filePath, fileSize, ...metadata };
92
192
  }
@@ -112,42 +212,80 @@ export class WaMediaCrypto {
112
212
  return paddedLength + HMAC_TRUNCATED_SIZE;
113
213
  }
114
214
  }
115
- async function pumpEncryption(plaintext, encrypted, keys) {
215
+ async function pumpEncryption(plaintext, encrypted, keys, computeSidecar, firstFrameLength) {
216
+ const aesKey = await importAesCbcKey(keys.encKey);
116
217
  const plainHash = createHash('sha256');
117
218
  const encHash = createHash('sha256');
118
219
  const hmac = createHmac('sha256', keys.macKey);
119
- const cipher = createCipheriv('aes-256-cbc', keys.encKey, keys.iv);
220
+ const sidecar = computeSidecar ? new SidecarAccumulator(keys.macKey) : null;
221
+ const ffTarget = firstFrameLength !== undefined
222
+ ? IV_SIZE + Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
223
+ : 0;
224
+ let ffCollected = 0;
225
+ const ffChunks = ffTarget > 0 ? [keys.iv] : [];
226
+ if (ffTarget > 0)
227
+ ffCollected = IV_SIZE;
120
228
  let plaintextLength = 0;
229
+ let currentIv = keys.iv;
230
+ let pending = EMPTY_BYTES;
121
231
  hmac.update(keys.iv);
232
+ sidecar?.push(keys.iv);
122
233
  try {
123
234
  for await (const chunk of plaintext) {
124
235
  const plainChunk = toChunkBytes(chunk);
125
- if (plainChunk.byteLength === 0) {
236
+ if (plainChunk.byteLength === 0)
126
237
  continue;
127
- }
128
238
  plaintextLength += plainChunk.byteLength;
129
239
  plainHash.update(plainChunk);
130
- const encryptedChunk = cipher.update(plainChunk);
131
- if (encryptedChunk.byteLength > 0) {
132
- hmac.update(encryptedChunk);
133
- encHash.update(encryptedChunk);
134
- await writeChunk(encrypted, encryptedChunk);
240
+ const combined = pending.byteLength > 0
241
+ ? concatBytes([pending, plainChunk])
242
+ : toBytesView(plainChunk);
243
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
244
+ if (aligned === 0) {
245
+ pending = combined;
246
+ continue;
135
247
  }
248
+ const toEncrypt = toBytesView(combined.subarray(0, aligned));
249
+ pending = toBytesView(combined.subarray(aligned));
250
+ const { ciphertext, nextIv } = await aesCbcEncryptChunk(aesKey, currentIv, toEncrypt, false);
251
+ currentIv = nextIv;
252
+ hmac.update(ciphertext);
253
+ encHash.update(ciphertext);
254
+ sidecar?.push(ciphertext);
255
+ if (ffCollected < ffTarget) {
256
+ const need = ffTarget - ffCollected;
257
+ ffChunks.push(ciphertext.subarray(0, Math.min(need, ciphertext.byteLength)));
258
+ ffCollected += ciphertext.byteLength;
259
+ }
260
+ await writeChunk(encrypted, ciphertext);
136
261
  }
137
- const encryptedFinal = cipher.final();
138
- if (encryptedFinal.byteLength > 0) {
139
- hmac.update(encryptedFinal);
140
- encHash.update(encryptedFinal);
141
- await writeChunk(encrypted, encryptedFinal);
262
+ const { ciphertext: finalCiphertext } = await aesCbcEncryptChunk(aesKey, currentIv, pending, true);
263
+ hmac.update(finalCiphertext);
264
+ encHash.update(finalCiphertext);
265
+ sidecar?.push(finalCiphertext);
266
+ if (ffCollected < ffTarget) {
267
+ const need = ffTarget - ffCollected;
268
+ ffChunks.push(finalCiphertext.subarray(0, Math.min(need, finalCiphertext.byteLength)));
142
269
  }
143
- const signature = hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE);
270
+ await writeChunk(encrypted, finalCiphertext);
271
+ const signature = toBytesView(hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE));
144
272
  encHash.update(signature);
273
+ sidecar?.push(signature);
145
274
  await writeChunk(encrypted, signature);
146
275
  encrypted.end();
276
+ let firstFrameSidecar;
277
+ if (ffTarget > 0) {
278
+ const ivCiphertextSlice = concatBytes(ffChunks);
279
+ const ffKey = await importHmacKey(keys.macKey);
280
+ const ffDigest = await hmacSign(ffKey, ivCiphertextSlice);
281
+ firstFrameSidecar = ffDigest.subarray(0, SIDECAR_HMAC_SIZE);
282
+ }
147
283
  return {
148
284
  fileSha256: toBytesView(plainHash.digest()),
149
285
  fileEncSha256: toBytesView(encHash.digest()),
150
- plaintextLength
286
+ plaintextLength,
287
+ streamingSidecar: sidecar?.finish(),
288
+ firstFrameSidecar
151
289
  };
152
290
  }
153
291
  catch (error) {
@@ -182,42 +320,80 @@ async function endWritable(stream) {
182
320
  stream.end(() => resolve());
183
321
  });
184
322
  }
185
- async function pumpEncryptionToWritable(plaintext, output, keys) {
323
+ async function pumpEncryptionToWritable(plaintext, output, keys, computeSidecar, firstFrameLength) {
324
+ const aesKey = await importAesCbcKey(keys.encKey);
186
325
  const plainHash = createHash('sha256');
187
326
  const encHash = createHash('sha256');
188
327
  const hmac = createHmac('sha256', keys.macKey);
189
- const cipher = createCipheriv('aes-256-cbc', keys.encKey, keys.iv);
328
+ const sidecar = computeSidecar ? new SidecarAccumulator(keys.macKey) : null;
329
+ const ffTarget = firstFrameLength !== undefined
330
+ ? IV_SIZE + Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
331
+ : 0;
332
+ let ffCollected = 0;
333
+ const ffChunks = ffTarget > 0 ? [keys.iv] : [];
334
+ if (ffTarget > 0)
335
+ ffCollected = IV_SIZE;
190
336
  let plaintextLength = 0;
337
+ let currentIv = keys.iv;
338
+ let pending = EMPTY_BYTES;
191
339
  hmac.update(keys.iv);
340
+ sidecar?.push(keys.iv);
192
341
  try {
193
342
  for await (const chunk of plaintext) {
194
343
  const plainChunk = toChunkBytes(chunk);
195
- if (plainChunk.byteLength === 0) {
344
+ if (plainChunk.byteLength === 0)
196
345
  continue;
197
- }
198
346
  plaintextLength += plainChunk.byteLength;
199
347
  plainHash.update(plainChunk);
200
- const encryptedChunk = cipher.update(plainChunk);
201
- if (encryptedChunk.byteLength > 0) {
202
- hmac.update(encryptedChunk);
203
- encHash.update(encryptedChunk);
204
- await writeChunkToWritable(output, encryptedChunk);
348
+ const combined = pending.byteLength > 0
349
+ ? concatBytes([pending, plainChunk])
350
+ : toBytesView(plainChunk);
351
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
352
+ if (aligned === 0) {
353
+ pending = combined;
354
+ continue;
355
+ }
356
+ const toEncrypt = toBytesView(combined.subarray(0, aligned));
357
+ pending = toBytesView(combined.subarray(aligned));
358
+ const { ciphertext, nextIv } = await aesCbcEncryptChunk(aesKey, currentIv, toEncrypt, false);
359
+ currentIv = nextIv;
360
+ hmac.update(ciphertext);
361
+ encHash.update(ciphertext);
362
+ sidecar?.push(ciphertext);
363
+ if (ffCollected < ffTarget) {
364
+ const need = ffTarget - ffCollected;
365
+ ffChunks.push(ciphertext.subarray(0, Math.min(need, ciphertext.byteLength)));
366
+ ffCollected += ciphertext.byteLength;
205
367
  }
368
+ await writeChunkToWritable(output, ciphertext);
206
369
  }
207
- const encryptedFinal = cipher.final();
208
- if (encryptedFinal.byteLength > 0) {
209
- hmac.update(encryptedFinal);
210
- encHash.update(encryptedFinal);
211
- await writeChunkToWritable(output, encryptedFinal);
370
+ const { ciphertext: finalCiphertext } = await aesCbcEncryptChunk(aesKey, currentIv, pending, true);
371
+ hmac.update(finalCiphertext);
372
+ encHash.update(finalCiphertext);
373
+ sidecar?.push(finalCiphertext);
374
+ if (ffCollected < ffTarget) {
375
+ const need = ffTarget - ffCollected;
376
+ ffChunks.push(finalCiphertext.subarray(0, Math.min(need, finalCiphertext.byteLength)));
212
377
  }
213
- const signature = hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE);
378
+ await writeChunkToWritable(output, finalCiphertext);
379
+ const signature = toBytesView(hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE));
214
380
  encHash.update(signature);
381
+ sidecar?.push(signature);
215
382
  await writeChunkToWritable(output, signature);
216
383
  await endWritable(output);
384
+ let firstFrameSidecar;
385
+ if (ffTarget > 0) {
386
+ const ivCiphertextSlice = concatBytes(ffChunks);
387
+ const ffKey = await importHmacKey(keys.macKey);
388
+ const ffDigest = await hmacSign(ffKey, ivCiphertextSlice);
389
+ firstFrameSidecar = ffDigest.subarray(0, SIDECAR_HMAC_SIZE);
390
+ }
217
391
  return {
218
392
  fileSha256: toBytesView(plainHash.digest()),
219
393
  fileEncSha256: toBytesView(encHash.digest()),
220
- plaintextLength
394
+ plaintextLength,
395
+ streamingSidecar: sidecar?.finish(),
396
+ firstFrameSidecar
221
397
  };
222
398
  }
223
399
  catch (error) {
@@ -227,10 +403,12 @@ async function pumpEncryptionToWritable(plaintext, output, keys) {
227
403
  }
228
404
  }
229
405
  async function pumpDecryption(encrypted, plaintext, keys, options) {
406
+ const aesKey = await importAesCbcKey(keys.encKey);
230
407
  const plainHash = createHash('sha256');
231
408
  const encHash = createHash('sha256');
232
409
  const hmac = createHmac('sha256', keys.macKey);
233
- const decipher = createDecipheriv('aes-256-cbc', keys.encKey, keys.iv);
410
+ let currentIv = keys.iv;
411
+ let pending = EMPTY_BYTES;
234
412
  hmac.update(keys.iv);
235
413
  try {
236
414
  let trailing = EMPTY_BYTES;
@@ -248,20 +426,37 @@ async function pumpDecryption(encrypted, plaintext, keys, options) {
248
426
  const ciphertextChunk = merged.subarray(0, merged.byteLength - HMAC_TRUNCATED_SIZE);
249
427
  trailing = merged.subarray(merged.byteLength - HMAC_TRUNCATED_SIZE);
250
428
  hmac.update(ciphertextChunk);
251
- const plainChunk = decipher.update(ciphertextChunk);
252
- if (plainChunk.byteLength > 0) {
253
- plainHash.update(plainChunk);
254
- await writeChunk(plaintext, plainChunk);
429
+ const combined = pending.byteLength > 0
430
+ ? concatBytes([pending, ciphertextChunk])
431
+ : toBytesView(ciphertextChunk);
432
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
433
+ if (aligned > AES_BLOCK_SIZE) {
434
+ const toDecrypt = combined.subarray(0, aligned - AES_BLOCK_SIZE);
435
+ const { plaintext: plainChunk, nextIv } = await aesCbcDecryptChunk(aesKey, currentIv, toDecrypt, false);
436
+ currentIv = nextIv;
437
+ if (plainChunk.byteLength > 0) {
438
+ plainHash.update(plainChunk);
439
+ await writeChunk(plaintext, plainChunk);
440
+ }
441
+ pending = toBytesView(combined.subarray(aligned - AES_BLOCK_SIZE));
442
+ }
443
+ else {
444
+ pending = combined;
255
445
  }
256
446
  }
257
447
  if (trailing.byteLength !== HMAC_TRUNCATED_SIZE) {
258
448
  throw new Error(`ciphertext too short: ${trailing.byteLength}`);
259
449
  }
260
- const signature = hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE);
261
- if (!uint8TimingSafeEqual(signature, trailing)) {
262
- throw new Error('media MAC mismatch');
450
+ if (!options.skipMacVerification) {
451
+ const signature = hmac.digest().subarray(0, HMAC_TRUNCATED_SIZE);
452
+ if (!uint8TimingSafeEqual(signature, trailing)) {
453
+ throw new Error('media MAC mismatch');
454
+ }
455
+ }
456
+ if (pending.byteLength < AES_BLOCK_SIZE || pending.byteLength % AES_BLOCK_SIZE !== 0) {
457
+ throw new Error(`invalid ciphertext length: ${pending.byteLength}`);
263
458
  }
264
- const plainFinal = decipher.final();
459
+ const { plaintext: plainFinal } = await aesCbcDecryptChunk(aesKey, currentIv, pending, true);
265
460
  if (plainFinal.byteLength > 0) {
266
461
  plainHash.update(plainFinal);
267
462
  await writeChunk(plaintext, plainFinal);