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
@@ -1,24 +1,12 @@
1
- import { Ed25519, toSerializedPubKey } from '../../crypto/index.js';
2
- import { montgomeryToEdwardsPublic } from '../../crypto/curves/X25519.js';
1
+ import { xeddsaVerify } from '../../crypto/index.js';
3
2
  import { proto } from '../../proto.js';
4
3
  import { ROOT_CA_PUBLIC_KEY_HEX, ROOT_CA_SERIAL } from '../noise/constants.js';
5
4
  import { assertByteLength, decodeProtoBytes, hexToBytes, uint8Equal } from '../../util/bytes.js';
6
5
  import { toSafeNumber } from '../../util/primitives.js';
7
- async function verifySignalVariant(serializedPublicKey, message, signatureInput) {
8
- const publicKey = toSerializedPubKey(serializedPublicKey);
9
- if (signatureInput.length !== 64) {
10
- return false;
11
- }
12
- const signature = new Uint8Array(signatureInput);
13
- const lastByte = signature[63];
14
- if ((lastByte & 0x60) !== 0) {
15
- return false;
16
- }
17
- const signBit = lastByte & 0x80;
18
- signature[63] = lastByte & 0x7f;
19
- const edwardsPublicKey = montgomeryToEdwardsPublic(publicKey.subarray(1), signBit);
20
- return Ed25519.verify(message, signature, edwardsPublicKey);
21
- }
6
+ const PRODUCTION_ROOT_CA = {
7
+ publicKey: hexToBytes(ROOT_CA_PUBLIC_KEY_HEX),
8
+ serial: ROOT_CA_SERIAL
9
+ };
22
10
  function parseNoiseCertificate(certificate) {
23
11
  if (!certificate) {
24
12
  throw new Error('missing noise certificate');
@@ -38,21 +26,16 @@ function parseNoiseCertificate(certificate) {
38
26
  signature: signatureBytes
39
27
  };
40
28
  }
41
- function rootPublicKeySerialized() {
42
- const raw = hexToBytes(ROOT_CA_PUBLIC_KEY_HEX);
43
- return toSerializedPubKey(raw);
44
- }
45
- export async function verifyNoiseCertificateChain(certificateChain, serverStatic) {
29
+ export async function verifyNoiseCertificateChain(certificateChain, serverStatic, rootCa = PRODUCTION_ROOT_CA) {
46
30
  const chain = proto.CertChain.decode(certificateChain);
47
31
  if (!chain.leaf || !chain.intermediate) {
48
32
  throw new Error('noise certificate chain is missing leaf/intermediate');
49
33
  }
50
34
  const intermediate = parseNoiseCertificate(chain.intermediate);
51
- if (intermediate.issuerSerial !== ROOT_CA_SERIAL) {
35
+ if (intermediate.issuerSerial !== rootCa.serial) {
52
36
  throw new Error('intermediate certificate issuer mismatch');
53
37
  }
54
- const rootKey = rootPublicKeySerialized();
55
- const validIntermediate = await verifySignalVariant(rootKey, intermediate.details, intermediate.signature);
38
+ const validIntermediate = await xeddsaVerify(rootCa.publicKey, intermediate.details, intermediate.signature);
56
39
  if (!validIntermediate) {
57
40
  throw new Error('intermediate certificate signature is invalid');
58
41
  }
@@ -60,8 +43,7 @@ export async function verifyNoiseCertificateChain(certificateChain, serverStatic
60
43
  if (leaf.issuerSerial !== intermediate.serial) {
61
44
  throw new Error('leaf certificate issuer mismatch');
62
45
  }
63
- const intermediatePublicSerialized = toSerializedPubKey(intermediate.key);
64
- const validLeaf = await verifySignalVariant(intermediatePublicSerialized, leaf.details, leaf.signature);
46
+ const validLeaf = await xeddsaVerify(intermediate.key, leaf.details, leaf.signature);
65
47
  if (!validLeaf) {
66
48
  throw new Error('leaf certificate signature is invalid');
67
49
  }
@@ -7,13 +7,13 @@ import { buildLoginPayload, buildRegistrationPayload } from '../noise/WaClientPa
7
7
  import { WaFrameCodec } from '../noise/WaFrameCodec.js';
8
8
  import { verifyNoiseCertificateChain } from '../noise/WaNoiseCert.js';
9
9
  import { WaNoiseHandshake } from '../noise/WaNoiseHandshake.js';
10
- import { concatBytes, toBytesView } from '../../util/bytes.js';
10
+ import { concatBytes } from '../../util/bytes.js';
11
11
  import { toError } from '../../util/primitives.js';
12
12
  function resolvePayload(payload) {
13
13
  if (payload instanceof Uint8Array) {
14
14
  return Promise.resolve(payload);
15
15
  }
16
- return Promise.resolve(payload()).then((value) => toBytesView(value));
16
+ return Promise.resolve(payload());
17
17
  }
18
18
  async function resolveHandshakePayload(config) {
19
19
  if (config.isRegistered) {
@@ -58,6 +58,7 @@ export class WaNoiseSession {
58
58
  this.closedError = null;
59
59
  this.noiseSocket = null;
60
60
  this.serverStaticKey = null;
61
+ this.trustedRootCa = undefined;
61
62
  this.handshakeFrameTimeoutMs = WA_DEFAULTS.CONNECT_TIMEOUT_MS;
62
63
  this.sendWire = sendWire;
63
64
  this.logger = logger;
@@ -68,9 +69,7 @@ export class WaNoiseSession {
68
69
  isRegistered: config.isRegistered,
69
70
  hasServerStaticKey: !!config.serverStaticKey
70
71
  });
71
- const protocolHeader = config.protocolHeader
72
- ? toBytesView(config.protocolHeader)
73
- : WA_PROTO_HEADER;
72
+ const protocolHeader = config.protocolHeader ?? WA_PROTO_HEADER;
74
73
  const introFrame = config.routingInfo && config.routingInfo.length > 0
75
74
  ? concatBytes([buildRoutingInfoPrefix(config.routingInfo), protocolHeader])
76
75
  : protocolHeader;
@@ -80,6 +79,7 @@ export class WaNoiseSession {
80
79
  resolveHandshakePayload(config)
81
80
  ]);
82
81
  const verifyCertificates = config.verifyCertificateChain !== false;
82
+ this.trustedRootCa = config.trustedRootCa;
83
83
  if (config.serverStaticKey && config.serverStaticKey.length === 32) {
84
84
  this.logger.info('noise session attempting resume handshake (IK)');
85
85
  this.noiseSocket = await this.resumeHandshake(config.serverStaticKey, config.clientStaticKeyPair, ephemeralKeyPair, payload, protocolHeader, verifyCertificates);
@@ -181,6 +181,7 @@ export class WaNoiseSession {
181
181
  this.closedError = null;
182
182
  this.noiseSocket = null;
183
183
  this.serverStaticKey = null;
184
+ this.trustedRootCa = undefined;
184
185
  this.writeChain = Promise.resolve();
185
186
  this.readChain = Promise.resolve();
186
187
  }
@@ -244,7 +245,7 @@ export class WaNoiseSession {
244
245
  if (!serverHello.payload) {
245
246
  throw new Error('noise resume handshake missing certificate payload');
246
247
  }
247
- const serverEphemeral = toBytesView(serverHello.ephemeral);
248
+ const serverEphemeral = serverHello.ephemeral;
248
249
  await handshake.authenticate(serverEphemeral);
249
250
  const [dh1, dh2] = await Promise.all([
250
251
  X25519.scalarMult(ephemeralKeyPair.privKey, serverEphemeral),
@@ -252,7 +253,7 @@ export class WaNoiseSession {
252
253
  ]);
253
254
  await handshake.mixIntoKey(dh1);
254
255
  await handshake.mixIntoKey(dh2);
255
- await handshake.decrypt(toBytesView(serverHello.payload));
256
+ await handshake.decrypt(serverHello.payload);
256
257
  this.serverStaticKey = serverStaticKey;
257
258
  this.logger.info('noise resume handshake successful without fallback');
258
259
  return { socket: await handshake.finish(), serverHelloFrame: null };
@@ -270,14 +271,14 @@ export class WaNoiseSession {
270
271
  if (!serverHello?.ephemeral || !serverHello.static || !serverHello.payload) {
271
272
  throw new Error('noise full handshake missing server hello fields');
272
273
  }
273
- const serverEphemeral = toBytesView(serverHello.ephemeral);
274
+ const serverEphemeral = serverHello.ephemeral;
274
275
  await handshake.authenticate(serverEphemeral);
275
276
  await handshake.mixIntoKey(await X25519.scalarMult(ephemeralKeyPair.privKey, serverEphemeral));
276
- const serverStatic = await handshake.decrypt(toBytesView(serverHello.static));
277
+ const serverStatic = await handshake.decrypt(serverHello.static);
277
278
  await handshake.mixIntoKey(await X25519.scalarMult(ephemeralKeyPair.privKey, serverStatic));
278
- const certificate = await handshake.decrypt(toBytesView(serverHello.payload));
279
+ const certificate = await handshake.decrypt(serverHello.payload);
279
280
  if (verifyCertificates) {
280
- await verifyNoiseCertificateChain(certificate, serverStatic);
281
+ await verifyNoiseCertificateChain(certificate, serverStatic, this.trustedRootCa);
281
282
  this.logger.trace('noise certificate chain verified');
282
283
  }
283
284
  this.serverStaticKey = serverStatic;
@@ -28,12 +28,14 @@ class StoreLock {
28
28
  current = Promise.reject(error);
29
29
  }
30
30
  }
31
- const tracker = current.then(() => undefined, () => undefined);
31
+ const tracker = current.then(StoreLock._noop, StoreLock._noop);
32
32
  this.chains.set(key, tracker);
33
- void tracker.finally(() => {
34
- if (this.chains.get(key) === tracker) {
33
+ tracker.then(() => {
34
+ if (this.chains.get(key) === tracker)
35
+ this.chains.delete(key);
36
+ }, () => {
37
+ if (this.chains.get(key) === tracker)
35
38
  this.chains.delete(key);
36
- }
37
39
  });
38
40
  return current;
39
41
  }
@@ -79,3 +81,4 @@ class StoreLock {
79
81
  }
80
82
  }
81
83
  exports.StoreLock = StoreLock;
84
+ StoreLock._noop = () => undefined;
@@ -15,6 +15,87 @@ const constants_1 = require("./constants");
15
15
  const constants_2 = require("../protocol/constants");
16
16
  const bytes_1 = require("../util/bytes");
17
17
  const primitives_2 = require("../util/primitives");
18
+ const AES_BLOCK_SIZE = 16;
19
+ const PKCS7_FULL_BLOCK = new Uint8Array(AES_BLOCK_SIZE).fill(AES_BLOCK_SIZE);
20
+ async function aesCbcEncryptChunk(key, iv, chunk, isFinal) {
21
+ const encrypted = await (0, primitives_1.aesCbcEncrypt)(key, iv, chunk);
22
+ if (isFinal) {
23
+ return {
24
+ ciphertext: encrypted,
25
+ nextIv: encrypted.subarray(encrypted.byteLength - AES_BLOCK_SIZE)
26
+ };
27
+ }
28
+ const ciphertext = encrypted.subarray(0, encrypted.byteLength - AES_BLOCK_SIZE);
29
+ return {
30
+ ciphertext,
31
+ nextIv: ciphertext.subarray(ciphertext.byteLength - AES_BLOCK_SIZE)
32
+ };
33
+ }
34
+ async function aesCbcDecryptChunk(key, iv, ciphertext, isFinal) {
35
+ const nextIv = (0, bytes_1.toBytesView)(ciphertext.subarray(ciphertext.byteLength - AES_BLOCK_SIZE));
36
+ if (isFinal) {
37
+ return { plaintext: await (0, primitives_1.aesCbcDecrypt)(key, iv, ciphertext), nextIv };
38
+ }
39
+ const padBlock = (await (0, primitives_1.aesCbcEncrypt)(key, nextIv, PKCS7_FULL_BLOCK)).subarray(0, AES_BLOCK_SIZE);
40
+ const withPad = (0, bytes_1.concatBytes)([ciphertext, padBlock]);
41
+ return { plaintext: await (0, primitives_1.aesCbcDecrypt)(key, iv, withPad), nextIv };
42
+ }
43
+ async function computeFirstFrameSidecar(macKey, ivCiphertext, firstFrameLength) {
44
+ const aligned = Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
45
+ const slice = ivCiphertext.subarray(0, constants_1.IV_SIZE + aligned);
46
+ const key = await (0, primitives_1.importHmacKey)(macKey);
47
+ const digest = await (0, primitives_1.hmacSign)(key, slice);
48
+ return digest.subarray(0, constants_1.SIDECAR_HMAC_SIZE);
49
+ }
50
+ class SidecarAccumulator {
51
+ constructor(macKey, estimatedSize = 0) {
52
+ this.resultOffset = 0;
53
+ this.totalPushed = 0;
54
+ this.windowOffset = 0;
55
+ this.nextChunkStart = 0;
56
+ this.macKey = macKey;
57
+ this.window = new Uint8Array(constants_1.IV_SIZE + constants_1.SIDECAR_CHUNK_SIZE);
58
+ const estimated = Math.max(Math.ceil(estimatedSize / constants_1.SIDECAR_CHUNK_SIZE) + 1, 16);
59
+ this.result = new Uint8Array(estimated * constants_1.SIDECAR_HMAC_SIZE);
60
+ }
61
+ push(data) {
62
+ let srcOffset = 0;
63
+ while (srcOffset < data.byteLength) {
64
+ const windowEnd = this.nextChunkStart + constants_1.IV_SIZE + constants_1.SIDECAR_CHUNK_SIZE;
65
+ const remaining = windowEnd - this.totalPushed;
66
+ const toCopy = Math.min(remaining, data.byteLength - srcOffset);
67
+ this.window.set(data.subarray(srcOffset, srcOffset + toCopy), this.windowOffset);
68
+ this.windowOffset += toCopy;
69
+ this.totalPushed += toCopy;
70
+ srcOffset += toCopy;
71
+ if (this.totalPushed === windowEnd) {
72
+ this.flushChunk();
73
+ }
74
+ }
75
+ }
76
+ finish() {
77
+ if (this.windowOffset > 0) {
78
+ this.flushChunk();
79
+ }
80
+ return this.result.subarray(0, this.resultOffset);
81
+ }
82
+ flushChunk() {
83
+ const digest = (0, node_crypto_1.createHmac)('sha256', this.macKey)
84
+ .update(this.window.subarray(0, this.windowOffset))
85
+ .digest();
86
+ if (this.resultOffset + constants_1.SIDECAR_HMAC_SIZE > this.result.byteLength) {
87
+ const grown = new Uint8Array(this.result.byteLength * 2);
88
+ grown.set(this.result);
89
+ this.result = grown;
90
+ }
91
+ this.result.set(digest.subarray(0, constants_1.SIDECAR_HMAC_SIZE), this.resultOffset);
92
+ this.resultOffset += constants_1.SIDECAR_HMAC_SIZE;
93
+ this.nextChunkStart += constants_1.SIDECAR_CHUNK_SIZE;
94
+ const overlapSrc = this.window.subarray(this.windowOffset - constants_1.IV_SIZE, this.windowOffset);
95
+ this.window.set(overlapSrc, 0);
96
+ this.windowOffset = constants_1.IV_SIZE;
97
+ }
98
+ }
18
99
  class WaMediaCrypto {
19
100
  static async generateMediaKey() {
20
101
  return (0, random_1.randomBytesAsync)(32);
@@ -30,24 +111,41 @@ class WaMediaCrypto {
30
111
  refKey: expanded.subarray(constants_1.MAC_KEY_END, constants_1.MEDIA_HKDF_SIZE)
31
112
  };
32
113
  }
33
- static async encryptBytes(mediaType, mediaKey, plaintext) {
114
+ static async encryptBytes(mediaType, mediaKey, plaintext, options) {
34
115
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
35
- const [aesKey, macKey] = await Promise.all([
116
+ const [aesKey, hmacKey] = await Promise.all([
36
117
  (0, primitives_1.importAesCbcKey)(keys.encKey),
37
118
  (0, primitives_1.importHmacKey)(keys.macKey)
38
119
  ]);
39
120
  const ciphertext = await (0, primitives_1.aesCbcEncrypt)(aesKey, keys.iv, plaintext);
40
121
  const ivCiphertext = (0, bytes_1.concatBytes)([keys.iv, ciphertext]);
41
- const mac = await (0, primitives_1.hmacSign)(macKey, ivCiphertext);
122
+ const mac = await (0, primitives_1.hmacSign)(hmacKey, ivCiphertext);
42
123
  const signature = mac.subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
43
124
  const ciphertextHmac = (0, bytes_1.concatBytes)([ciphertext, signature]);
125
+ let streamingSidecar;
126
+ if (options?.sidecar !== false) {
127
+ const acc = new SidecarAccumulator(keys.macKey);
128
+ acc.push(keys.iv);
129
+ acc.push(ciphertext);
130
+ acc.push(signature);
131
+ streamingSidecar = acc.finish();
132
+ }
133
+ const firstFrameSidecar = options?.firstFrameLength !== undefined
134
+ ? await computeFirstFrameSidecar(keys.macKey, ivCiphertext, options.firstFrameLength)
135
+ : undefined;
44
136
  const [fileSha256, fileEncSha256] = await Promise.all([
45
137
  (0, primitives_1.sha256)(plaintext),
46
138
  (0, primitives_1.sha256)(ciphertextHmac)
47
139
  ]);
48
- return { ciphertextHmac, fileSha256, fileEncSha256 };
140
+ return {
141
+ ciphertextHmac,
142
+ fileSha256,
143
+ fileEncSha256,
144
+ streamingSidecar,
145
+ firstFrameSidecar
146
+ };
49
147
  }
50
- static async decryptBytes(mediaType, mediaKey, ciphertextHmac, expectedFileSha256, expectedFileEncSha256) {
148
+ static async decryptBytes(mediaType, mediaKey, ciphertextHmac, expectedFileSha256, expectedFileEncSha256, skipMacVerification = false) {
51
149
  if (ciphertextHmac.byteLength < constants_1.HMAC_TRUNCATED_SIZE) {
52
150
  throw new Error(`ciphertext too short: ${ciphertextHmac.byteLength}`);
53
151
  }
@@ -58,17 +156,19 @@ class WaMediaCrypto {
58
156
  }
59
157
  }
60
158
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
159
+ const [aesKey, hmacKey] = await Promise.all([
160
+ (0, primitives_1.importAesCbcKey)(keys.encKey),
161
+ (0, primitives_1.importHmacKey)(keys.macKey)
162
+ ]);
61
163
  const ciphertext = ciphertextHmac.subarray(0, ciphertextHmac.byteLength - constants_1.HMAC_TRUNCATED_SIZE);
62
164
  const expectedMac = ciphertextHmac.subarray(ciphertextHmac.byteLength - constants_1.HMAC_TRUNCATED_SIZE);
63
165
  const ivCiphertext = (0, bytes_1.concatBytes)([keys.iv, ciphertext]);
64
- const [macKey, aesKey] = await Promise.all([
65
- (0, primitives_1.importHmacKey)(keys.macKey),
66
- (0, primitives_1.importAesCbcKey)(keys.encKey)
67
- ]);
68
- const mac = await (0, primitives_1.hmacSign)(macKey, ivCiphertext);
69
- const signature = mac.subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
70
- if (!(0, bytes_1.uint8TimingSafeEqual)(signature, expectedMac)) {
71
- throw new Error('media MAC mismatch');
166
+ if (!skipMacVerification) {
167
+ const mac = await (0, primitives_1.hmacSign)(hmacKey, ivCiphertext);
168
+ const signature = mac.subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
169
+ if (!(0, bytes_1.uint8TimingSafeEqual)(signature, expectedMac)) {
170
+ throw new Error('media MAC mismatch');
171
+ }
72
172
  }
73
173
  const plaintext = await (0, primitives_1.aesCbcDecrypt)(aesKey, keys.iv, ciphertext);
74
174
  const fileSha256 = await (0, primitives_1.sha256)(plaintext);
@@ -78,18 +178,18 @@ class WaMediaCrypto {
78
178
  const fileEncSha256 = expectedFileEncSha256 ?? (await (0, primitives_1.sha256)(ciphertextHmac));
79
179
  return { plaintext, fileSha256, fileEncSha256 };
80
180
  }
81
- static async encryptReadable(mediaType, mediaKey, plaintext) {
181
+ static async encryptReadable(mediaType, mediaKey, plaintext, options) {
82
182
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
83
183
  const encrypted = new node_stream_1.PassThrough();
84
- const metadata = pumpEncryption(plaintext, encrypted, keys);
184
+ const metadata = pumpEncryption(plaintext, encrypted, keys, options?.sidecar !== false, options?.firstFrameLength);
85
185
  return { encrypted, metadata };
86
186
  }
87
- static async encryptToFile(mediaType, mediaKey, plaintext) {
187
+ static async encryptToFile(mediaType, mediaKey, plaintext, options) {
88
188
  const keys = await WaMediaCrypto.deriveKeys(mediaType, mediaKey);
89
189
  const filePath = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `zapo-enc-${Date.now()}-${Math.random().toString(36).slice(2)}`);
90
190
  const output = (0, node_fs_1.createWriteStream)(filePath);
91
191
  try {
92
- const metadata = await pumpEncryptionToWritable(plaintext, output, keys);
192
+ const metadata = await pumpEncryptionToWritable(plaintext, output, keys, options?.sidecar !== false, options?.firstFrameLength);
93
193
  const fileSize = (await (0, promises_1.stat)(filePath)).size;
94
194
  return { filePath, fileSize, ...metadata };
95
195
  }
@@ -116,42 +216,80 @@ class WaMediaCrypto {
116
216
  }
117
217
  }
118
218
  exports.WaMediaCrypto = WaMediaCrypto;
119
- async function pumpEncryption(plaintext, encrypted, keys) {
219
+ async function pumpEncryption(plaintext, encrypted, keys, computeSidecar, firstFrameLength) {
220
+ const aesKey = await (0, primitives_1.importAesCbcKey)(keys.encKey);
120
221
  const plainHash = (0, node_crypto_1.createHash)('sha256');
121
222
  const encHash = (0, node_crypto_1.createHash)('sha256');
122
223
  const hmac = (0, node_crypto_1.createHmac)('sha256', keys.macKey);
123
- const cipher = (0, node_crypto_1.createCipheriv)('aes-256-cbc', keys.encKey, keys.iv);
224
+ const sidecar = computeSidecar ? new SidecarAccumulator(keys.macKey) : null;
225
+ const ffTarget = firstFrameLength !== undefined
226
+ ? constants_1.IV_SIZE + Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
227
+ : 0;
228
+ let ffCollected = 0;
229
+ const ffChunks = ffTarget > 0 ? [keys.iv] : [];
230
+ if (ffTarget > 0)
231
+ ffCollected = constants_1.IV_SIZE;
124
232
  let plaintextLength = 0;
233
+ let currentIv = keys.iv;
234
+ let pending = bytes_1.EMPTY_BYTES;
125
235
  hmac.update(keys.iv);
236
+ sidecar?.push(keys.iv);
126
237
  try {
127
238
  for await (const chunk of plaintext) {
128
239
  const plainChunk = (0, bytes_1.toChunkBytes)(chunk);
129
- if (plainChunk.byteLength === 0) {
240
+ if (plainChunk.byteLength === 0)
130
241
  continue;
131
- }
132
242
  plaintextLength += plainChunk.byteLength;
133
243
  plainHash.update(plainChunk);
134
- const encryptedChunk = cipher.update(plainChunk);
135
- if (encryptedChunk.byteLength > 0) {
136
- hmac.update(encryptedChunk);
137
- encHash.update(encryptedChunk);
138
- await writeChunk(encrypted, encryptedChunk);
244
+ const combined = pending.byteLength > 0
245
+ ? (0, bytes_1.concatBytes)([pending, plainChunk])
246
+ : (0, bytes_1.toBytesView)(plainChunk);
247
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
248
+ if (aligned === 0) {
249
+ pending = combined;
250
+ continue;
139
251
  }
252
+ const toEncrypt = (0, bytes_1.toBytesView)(combined.subarray(0, aligned));
253
+ pending = (0, bytes_1.toBytesView)(combined.subarray(aligned));
254
+ const { ciphertext, nextIv } = await aesCbcEncryptChunk(aesKey, currentIv, toEncrypt, false);
255
+ currentIv = nextIv;
256
+ hmac.update(ciphertext);
257
+ encHash.update(ciphertext);
258
+ sidecar?.push(ciphertext);
259
+ if (ffCollected < ffTarget) {
260
+ const need = ffTarget - ffCollected;
261
+ ffChunks.push(ciphertext.subarray(0, Math.min(need, ciphertext.byteLength)));
262
+ ffCollected += ciphertext.byteLength;
263
+ }
264
+ await writeChunk(encrypted, ciphertext);
140
265
  }
141
- const encryptedFinal = cipher.final();
142
- if (encryptedFinal.byteLength > 0) {
143
- hmac.update(encryptedFinal);
144
- encHash.update(encryptedFinal);
145
- await writeChunk(encrypted, encryptedFinal);
266
+ const { ciphertext: finalCiphertext } = await aesCbcEncryptChunk(aesKey, currentIv, pending, true);
267
+ hmac.update(finalCiphertext);
268
+ encHash.update(finalCiphertext);
269
+ sidecar?.push(finalCiphertext);
270
+ if (ffCollected < ffTarget) {
271
+ const need = ffTarget - ffCollected;
272
+ ffChunks.push(finalCiphertext.subarray(0, Math.min(need, finalCiphertext.byteLength)));
146
273
  }
147
- const signature = hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
274
+ await writeChunk(encrypted, finalCiphertext);
275
+ const signature = (0, bytes_1.toBytesView)(hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE));
148
276
  encHash.update(signature);
277
+ sidecar?.push(signature);
149
278
  await writeChunk(encrypted, signature);
150
279
  encrypted.end();
280
+ let firstFrameSidecar;
281
+ if (ffTarget > 0) {
282
+ const ivCiphertextSlice = (0, bytes_1.concatBytes)(ffChunks);
283
+ const ffKey = await (0, primitives_1.importHmacKey)(keys.macKey);
284
+ const ffDigest = await (0, primitives_1.hmacSign)(ffKey, ivCiphertextSlice);
285
+ firstFrameSidecar = ffDigest.subarray(0, constants_1.SIDECAR_HMAC_SIZE);
286
+ }
151
287
  return {
152
288
  fileSha256: (0, bytes_1.toBytesView)(plainHash.digest()),
153
289
  fileEncSha256: (0, bytes_1.toBytesView)(encHash.digest()),
154
- plaintextLength
290
+ plaintextLength,
291
+ streamingSidecar: sidecar?.finish(),
292
+ firstFrameSidecar
155
293
  };
156
294
  }
157
295
  catch (error) {
@@ -186,42 +324,80 @@ async function endWritable(stream) {
186
324
  stream.end(() => resolve());
187
325
  });
188
326
  }
189
- async function pumpEncryptionToWritable(plaintext, output, keys) {
327
+ async function pumpEncryptionToWritable(plaintext, output, keys, computeSidecar, firstFrameLength) {
328
+ const aesKey = await (0, primitives_1.importAesCbcKey)(keys.encKey);
190
329
  const plainHash = (0, node_crypto_1.createHash)('sha256');
191
330
  const encHash = (0, node_crypto_1.createHash)('sha256');
192
331
  const hmac = (0, node_crypto_1.createHmac)('sha256', keys.macKey);
193
- const cipher = (0, node_crypto_1.createCipheriv)('aes-256-cbc', keys.encKey, keys.iv);
332
+ const sidecar = computeSidecar ? new SidecarAccumulator(keys.macKey) : null;
333
+ const ffTarget = firstFrameLength !== undefined
334
+ ? constants_1.IV_SIZE + Math.ceil(firstFrameLength / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
335
+ : 0;
336
+ let ffCollected = 0;
337
+ const ffChunks = ffTarget > 0 ? [keys.iv] : [];
338
+ if (ffTarget > 0)
339
+ ffCollected = constants_1.IV_SIZE;
194
340
  let plaintextLength = 0;
341
+ let currentIv = keys.iv;
342
+ let pending = bytes_1.EMPTY_BYTES;
195
343
  hmac.update(keys.iv);
344
+ sidecar?.push(keys.iv);
196
345
  try {
197
346
  for await (const chunk of plaintext) {
198
347
  const plainChunk = (0, bytes_1.toChunkBytes)(chunk);
199
- if (plainChunk.byteLength === 0) {
348
+ if (plainChunk.byteLength === 0)
200
349
  continue;
201
- }
202
350
  plaintextLength += plainChunk.byteLength;
203
351
  plainHash.update(plainChunk);
204
- const encryptedChunk = cipher.update(plainChunk);
205
- if (encryptedChunk.byteLength > 0) {
206
- hmac.update(encryptedChunk);
207
- encHash.update(encryptedChunk);
208
- await writeChunkToWritable(output, encryptedChunk);
352
+ const combined = pending.byteLength > 0
353
+ ? (0, bytes_1.concatBytes)([pending, plainChunk])
354
+ : (0, bytes_1.toBytesView)(plainChunk);
355
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
356
+ if (aligned === 0) {
357
+ pending = combined;
358
+ continue;
359
+ }
360
+ const toEncrypt = (0, bytes_1.toBytesView)(combined.subarray(0, aligned));
361
+ pending = (0, bytes_1.toBytesView)(combined.subarray(aligned));
362
+ const { ciphertext, nextIv } = await aesCbcEncryptChunk(aesKey, currentIv, toEncrypt, false);
363
+ currentIv = nextIv;
364
+ hmac.update(ciphertext);
365
+ encHash.update(ciphertext);
366
+ sidecar?.push(ciphertext);
367
+ if (ffCollected < ffTarget) {
368
+ const need = ffTarget - ffCollected;
369
+ ffChunks.push(ciphertext.subarray(0, Math.min(need, ciphertext.byteLength)));
370
+ ffCollected += ciphertext.byteLength;
209
371
  }
372
+ await writeChunkToWritable(output, ciphertext);
210
373
  }
211
- const encryptedFinal = cipher.final();
212
- if (encryptedFinal.byteLength > 0) {
213
- hmac.update(encryptedFinal);
214
- encHash.update(encryptedFinal);
215
- await writeChunkToWritable(output, encryptedFinal);
374
+ const { ciphertext: finalCiphertext } = await aesCbcEncryptChunk(aesKey, currentIv, pending, true);
375
+ hmac.update(finalCiphertext);
376
+ encHash.update(finalCiphertext);
377
+ sidecar?.push(finalCiphertext);
378
+ if (ffCollected < ffTarget) {
379
+ const need = ffTarget - ffCollected;
380
+ ffChunks.push(finalCiphertext.subarray(0, Math.min(need, finalCiphertext.byteLength)));
216
381
  }
217
- const signature = hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
382
+ await writeChunkToWritable(output, finalCiphertext);
383
+ const signature = (0, bytes_1.toBytesView)(hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE));
218
384
  encHash.update(signature);
385
+ sidecar?.push(signature);
219
386
  await writeChunkToWritable(output, signature);
220
387
  await endWritable(output);
388
+ let firstFrameSidecar;
389
+ if (ffTarget > 0) {
390
+ const ivCiphertextSlice = (0, bytes_1.concatBytes)(ffChunks);
391
+ const ffKey = await (0, primitives_1.importHmacKey)(keys.macKey);
392
+ const ffDigest = await (0, primitives_1.hmacSign)(ffKey, ivCiphertextSlice);
393
+ firstFrameSidecar = ffDigest.subarray(0, constants_1.SIDECAR_HMAC_SIZE);
394
+ }
221
395
  return {
222
396
  fileSha256: (0, bytes_1.toBytesView)(plainHash.digest()),
223
397
  fileEncSha256: (0, bytes_1.toBytesView)(encHash.digest()),
224
- plaintextLength
398
+ plaintextLength,
399
+ streamingSidecar: sidecar?.finish(),
400
+ firstFrameSidecar
225
401
  };
226
402
  }
227
403
  catch (error) {
@@ -231,10 +407,12 @@ async function pumpEncryptionToWritable(plaintext, output, keys) {
231
407
  }
232
408
  }
233
409
  async function pumpDecryption(encrypted, plaintext, keys, options) {
410
+ const aesKey = await (0, primitives_1.importAesCbcKey)(keys.encKey);
234
411
  const plainHash = (0, node_crypto_1.createHash)('sha256');
235
412
  const encHash = (0, node_crypto_1.createHash)('sha256');
236
413
  const hmac = (0, node_crypto_1.createHmac)('sha256', keys.macKey);
237
- const decipher = (0, node_crypto_1.createDecipheriv)('aes-256-cbc', keys.encKey, keys.iv);
414
+ let currentIv = keys.iv;
415
+ let pending = bytes_1.EMPTY_BYTES;
238
416
  hmac.update(keys.iv);
239
417
  try {
240
418
  let trailing = bytes_1.EMPTY_BYTES;
@@ -252,20 +430,37 @@ async function pumpDecryption(encrypted, plaintext, keys, options) {
252
430
  const ciphertextChunk = merged.subarray(0, merged.byteLength - constants_1.HMAC_TRUNCATED_SIZE);
253
431
  trailing = merged.subarray(merged.byteLength - constants_1.HMAC_TRUNCATED_SIZE);
254
432
  hmac.update(ciphertextChunk);
255
- const plainChunk = decipher.update(ciphertextChunk);
256
- if (plainChunk.byteLength > 0) {
257
- plainHash.update(plainChunk);
258
- await writeChunk(plaintext, plainChunk);
433
+ const combined = pending.byteLength > 0
434
+ ? (0, bytes_1.concatBytes)([pending, ciphertextChunk])
435
+ : (0, bytes_1.toBytesView)(ciphertextChunk);
436
+ const aligned = combined.byteLength - (combined.byteLength % AES_BLOCK_SIZE);
437
+ if (aligned > AES_BLOCK_SIZE) {
438
+ const toDecrypt = combined.subarray(0, aligned - AES_BLOCK_SIZE);
439
+ const { plaintext: plainChunk, nextIv } = await aesCbcDecryptChunk(aesKey, currentIv, toDecrypt, false);
440
+ currentIv = nextIv;
441
+ if (plainChunk.byteLength > 0) {
442
+ plainHash.update(plainChunk);
443
+ await writeChunk(plaintext, plainChunk);
444
+ }
445
+ pending = (0, bytes_1.toBytesView)(combined.subarray(aligned - AES_BLOCK_SIZE));
446
+ }
447
+ else {
448
+ pending = combined;
259
449
  }
260
450
  }
261
451
  if (trailing.byteLength !== constants_1.HMAC_TRUNCATED_SIZE) {
262
452
  throw new Error(`ciphertext too short: ${trailing.byteLength}`);
263
453
  }
264
- const signature = hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
265
- if (!(0, bytes_1.uint8TimingSafeEqual)(signature, trailing)) {
266
- throw new Error('media MAC mismatch');
454
+ if (!options.skipMacVerification) {
455
+ const signature = hmac.digest().subarray(0, constants_1.HMAC_TRUNCATED_SIZE);
456
+ if (!(0, bytes_1.uint8TimingSafeEqual)(signature, trailing)) {
457
+ throw new Error('media MAC mismatch');
458
+ }
459
+ }
460
+ if (pending.byteLength < AES_BLOCK_SIZE || pending.byteLength % AES_BLOCK_SIZE !== 0) {
461
+ throw new Error(`invalid ciphertext length: ${pending.byteLength}`);
267
462
  }
268
- const plainFinal = decipher.final();
463
+ const { plaintext: plainFinal } = await aesCbcDecryptChunk(aesKey, currentIv, pending, true);
269
464
  if (plainFinal.byteLength > 0) {
270
465
  plainHash.update(plainFinal);
271
466
  await writeChunk(plaintext, plainFinal);