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.
- package/README.md +7 -3
- package/dist/appstate/WaAppStateCrypto.js +49 -41
- package/dist/appstate/WaAppStateSyncClient.js +79 -42
- package/dist/appstate/index.js +2 -2
- package/dist/auth/WaAuthClient.js +20 -11
- package/dist/auth/{flow/WaAuthCredentialsFlow.js → credentials-flow.js} +83 -18
- package/dist/auth/pairing/WaPairingFlow.js +26 -29
- package/dist/auth/pairing/{WaPairingCodeCrypto.js → pairing-code-crypto.js} +29 -13
- package/dist/client/WaClient.js +115 -75
- package/dist/client/WaClientFactory.js +113 -30
- package/dist/client/connection/WaConnectionManager.js +4 -1
- package/dist/client/coordinators/WaAbPropsCoordinator.js +141 -0
- package/dist/client/coordinators/WaBusinessCoordinator.js +3 -12
- package/dist/client/coordinators/WaEmailCoordinator.js +63 -0
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +33 -8
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +55 -25
- package/dist/client/coordinators/WaOfflineResumeCoordinator.js +114 -0
- package/dist/client/coordinators/WaPassiveTasksCoordinator.js +38 -20
- package/dist/client/coordinators/WaProfileCoordinator.js +3 -1
- package/dist/client/coordinators/WaRetryCoordinator.js +11 -9
- package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +22 -4
- package/dist/client/dirty.js +1 -1
- package/dist/client/events/abprops.js +43 -0
- package/dist/client/events/privacy-token.js +1 -2
- package/dist/client/events/registration.js +42 -0
- package/dist/client/incoming.js +37 -0
- package/dist/client/mailbox.js +17 -1
- package/dist/client/media.js +243 -0
- package/dist/client/messages.js +163 -86
- package/dist/crypto/core/index.js +4 -1
- package/dist/crypto/core/random.js +3 -9
- package/dist/crypto/core/xeddsa.js +57 -0
- package/dist/crypto/curves/X25519.js +18 -0
- package/dist/crypto/curves/constants.js +2 -1
- package/dist/esm/appstate/WaAppStateCrypto.js +39 -31
- package/dist/esm/appstate/WaAppStateSyncClient.js +68 -31
- package/dist/esm/appstate/index.js +1 -1
- package/dist/esm/appstate/{WaAppStateSyncResponseParser.js → response-parser.js} +1 -1
- package/dist/esm/auth/WaAuthClient.js +17 -8
- package/dist/esm/auth/{flow/WaAuthCredentialsFlow.js → credentials-flow.js} +83 -18
- package/dist/esm/auth/pairing/WaPairingFlow.js +25 -28
- package/dist/esm/auth/pairing/{WaPairingCodeCrypto.js → pairing-code-crypto.js} +20 -6
- package/dist/esm/client/WaClient.js +116 -76
- package/dist/esm/client/WaClientFactory.js +114 -31
- package/dist/esm/client/connection/WaConnectionManager.js +4 -1
- package/dist/esm/client/coordinators/WaAbPropsCoordinator.js +137 -0
- package/dist/esm/client/coordinators/WaBusinessCoordinator.js +4 -13
- package/dist/esm/client/coordinators/WaEmailCoordinator.js +60 -0
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +35 -10
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +47 -17
- package/dist/esm/client/coordinators/WaOfflineResumeCoordinator.js +110 -0
- package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +38 -20
- package/dist/esm/client/coordinators/WaProfileCoordinator.js +3 -1
- package/dist/esm/client/coordinators/WaRetryCoordinator.js +11 -9
- package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +24 -6
- package/dist/esm/client/dirty.js +1 -1
- package/dist/esm/client/events/abprops.js +40 -0
- package/dist/esm/client/events/privacy-token.js +1 -2
- package/dist/esm/client/events/registration.js +39 -0
- package/dist/esm/client/incoming.js +36 -0
- package/dist/esm/client/mailbox.js +17 -1
- package/dist/esm/client/media.js +234 -0
- package/dist/esm/client/messages.js +162 -85
- package/dist/esm/crypto/core/index.js +1 -0
- package/dist/esm/crypto/core/random.js +2 -7
- package/dist/esm/crypto/core/xeddsa.js +53 -0
- package/dist/esm/crypto/curves/X25519.js +20 -2
- package/dist/esm/crypto/curves/constants.js +1 -0
- package/dist/esm/infra/perf/StoreLock.js +7 -4
- package/dist/esm/media/WaMediaCrypto.js +257 -62
- package/dist/esm/media/WaMediaTransferClient.js +47 -190
- package/dist/esm/media/constants.js +2 -0
- package/dist/esm/media/processor.js +1 -0
- package/dist/esm/message/addon-crypto.js +130 -3
- package/dist/esm/message/content.js +12 -6
- package/dist/esm/message/icdc.js +8 -8
- package/dist/esm/message/incoming.js +14 -12
- package/dist/esm/message/phash.js +32 -12
- package/dist/esm/message/reporting-token.js +3 -3
- package/dist/esm/message/use-case-secret.js +1 -1
- package/dist/esm/protocol/abprops.js +159 -0
- package/dist/esm/protocol/browser.js +14 -0
- package/dist/esm/protocol/constants.js +3 -1
- package/dist/esm/protocol/email.js +30 -0
- package/dist/esm/protocol/jid.js +44 -10
- package/dist/esm/protocol/nodes.js +6 -2
- package/dist/esm/protocol/notification.js +7 -1
- package/dist/esm/retry/reason.js +1 -1
- package/dist/esm/signal/api/SignalDeviceSyncApi.js +5 -2
- package/dist/esm/signal/api/SignalDigestSyncApi.js +8 -6
- package/dist/esm/signal/api/SignalIdentitySyncApi.js +4 -4
- package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +1 -1
- package/dist/esm/signal/api/SignalSessionSyncApi.js +1 -1
- package/dist/esm/signal/crypto/WaAdvSignature.js +5 -51
- package/dist/esm/signal/crypto/constants.js +0 -4
- package/dist/esm/signal/encoding.js +11 -54
- package/dist/esm/signal/group/SenderKeyChain.js +3 -3
- package/dist/esm/signal/group/SenderKeyCodec.js +5 -6
- package/dist/esm/signal/group/SenderKeyManager.js +13 -10
- package/dist/esm/signal/registration/keygen.js +2 -3
- package/dist/esm/signal/registration/utils.js +2 -2
- package/dist/esm/signal/session/SignalProtocol.js +18 -17
- package/dist/esm/signal/session/SignalRatchet.js +21 -10
- package/dist/esm/signal/session/SignalSerializer.js +5 -6
- package/dist/esm/signal/session/SignalSession.js +11 -9
- package/dist/esm/signal/session/resolver.js +6 -6
- package/dist/esm/store/contracts/identity.store.js +1 -0
- package/dist/esm/store/contracts/message-secret.store.js +1 -0
- package/dist/esm/store/contracts/pre-key.store.js +1 -0
- package/dist/esm/store/contracts/session.store.js +1 -0
- package/dist/esm/store/createStore.js +48 -12
- package/dist/esm/store/index.js +4 -0
- package/dist/esm/store/locks/identity.lock.js +16 -0
- package/dist/esm/store/locks/message-secret.lock.js +17 -0
- package/dist/esm/store/locks/pre-key.lock.js +27 -0
- package/dist/esm/store/locks/session.lock.js +19 -0
- package/dist/esm/store/locks/signal.lock.js +0 -24
- package/dist/esm/store/noop.store.js +20 -0
- package/dist/esm/store/providers/memory/device-list.store.js +3 -0
- package/dist/esm/store/providers/memory/identity.store.js +31 -0
- package/dist/esm/store/providers/memory/message-secret.store.js +81 -0
- package/dist/esm/store/providers/memory/participants.store.js +3 -0
- package/dist/esm/store/providers/memory/pre-key.store.js +97 -0
- package/dist/esm/store/providers/memory/retry.store.js +25 -11
- package/dist/esm/store/providers/memory/session.store.js +45 -0
- package/dist/esm/store/providers/memory/signal.store.js +1 -164
- package/dist/esm/transport/WaComms.js +4 -3
- package/dist/esm/transport/WaWebSocket.js +9 -1
- package/dist/esm/transport/index.js +6 -0
- package/dist/esm/transport/keepalive/WaKeepAlive.js +17 -8
- package/dist/esm/transport/node/WaMobileTcpSocket.js +114 -0
- package/dist/esm/transport/node/WaNodeOrchestrator.js +17 -8
- package/dist/esm/transport/node/builders/abprops.js +20 -0
- package/dist/esm/transport/node/builders/device.js +11 -0
- package/dist/esm/transport/node/builders/email.js +65 -0
- package/dist/esm/transport/node/builders/offline.js +14 -0
- package/dist/esm/transport/node/builders/prekeys.js +37 -40
- package/dist/esm/transport/node/builders/presence.js +13 -0
- package/dist/esm/transport/node/builders/privacy-token.js +19 -23
- package/dist/esm/transport/node/builders/retry.js +1 -1
- package/dist/esm/transport/node/helpers.js +24 -0
- package/dist/esm/transport/node/mex/argo-decoder.js +152 -0
- package/dist/esm/transport/node/mex/client.js +83 -0
- package/dist/esm/transport/node/mex/persist-ids.js +10 -0
- package/dist/esm/transport/noise/WaClientPayload.js +15 -10
- package/dist/esm/transport/noise/WaFrameCodec.js +2 -2
- package/dist/esm/transport/noise/WaMobileClientPayload.js +53 -0
- package/dist/esm/transport/noise/WaNoiseCert.js +9 -27
- package/dist/esm/transport/noise/WaNoiseSession.js +12 -11
- package/dist/infra/perf/StoreLock.js +7 -4
- package/dist/media/WaMediaCrypto.js +253 -58
- package/dist/media/WaMediaTransferClient.js +50 -223
- package/dist/media/constants.js +3 -1
- package/dist/media/processor.js +2 -0
- package/dist/message/addon-crypto.js +131 -0
- package/dist/message/content.js +13 -5
- package/dist/message/icdc.js +8 -8
- package/dist/message/incoming.js +14 -12
- package/dist/message/phash.js +32 -12
- package/dist/message/reporting-token.js +2 -2
- package/dist/message/use-case-secret.js +1 -1
- package/dist/protocol/abprops.js +163 -0
- package/dist/protocol/browser.js +15 -0
- package/dist/protocol/constants.js +14 -2
- package/dist/protocol/email.js +33 -0
- package/dist/protocol/jid.js +45 -10
- package/dist/protocol/nodes.js +6 -2
- package/dist/protocol/notification.js +8 -2
- package/dist/retry/reason.js +1 -1
- package/dist/signal/api/SignalDeviceSyncApi.js +5 -2
- package/dist/signal/api/SignalDigestSyncApi.js +8 -6
- package/dist/signal/api/SignalIdentitySyncApi.js +4 -4
- package/dist/signal/crypto/WaAdvSignature.js +2 -50
- package/dist/signal/crypto/constants.js +1 -5
- package/dist/signal/encoding.js +11 -49
- package/dist/signal/group/SenderKeyChain.js +2 -2
- package/dist/signal/group/SenderKeyCodec.js +4 -5
- package/dist/signal/group/SenderKeyManager.js +12 -9
- package/dist/signal/registration/keygen.js +1 -2
- package/dist/signal/registration/utils.js +2 -2
- package/dist/signal/session/SignalProtocol.js +18 -17
- package/dist/signal/session/SignalRatchet.js +19 -8
- package/dist/signal/session/SignalSerializer.js +5 -6
- package/dist/signal/session/SignalSession.js +11 -9
- package/dist/signal/session/resolver.js +6 -6
- package/dist/store/contracts/identity.store.js +2 -0
- package/dist/store/contracts/message-secret.store.js +2 -0
- package/dist/store/contracts/pre-key.store.js +2 -0
- package/dist/store/contracts/session.store.js +2 -0
- package/dist/store/createStore.js +47 -11
- package/dist/store/index.js +9 -1
- package/dist/store/locks/identity.lock.js +19 -0
- package/dist/store/locks/message-secret.lock.js +20 -0
- package/dist/store/locks/pre-key.lock.js +30 -0
- package/dist/store/locks/session.lock.js +22 -0
- package/dist/store/locks/signal.lock.js +0 -24
- package/dist/store/noop.store.js +21 -1
- package/dist/store/providers/memory/device-list.store.js +3 -0
- package/dist/store/providers/memory/identity.store.js +35 -0
- package/dist/store/providers/memory/message-secret.store.js +85 -0
- package/dist/store/providers/memory/participants.store.js +3 -0
- package/dist/store/providers/memory/pre-key.store.js +101 -0
- package/dist/store/providers/memory/retry.store.js +24 -10
- package/dist/store/providers/memory/session.store.js +49 -0
- package/dist/store/providers/memory/signal.store.js +1 -164
- package/dist/transport/WaComms.js +4 -3
- package/dist/transport/WaWebSocket.js +9 -1
- package/dist/transport/index.js +17 -1
- package/dist/transport/keepalive/WaKeepAlive.js +17 -8
- package/dist/transport/node/WaMobileTcpSocket.js +118 -0
- package/dist/transport/node/WaNodeOrchestrator.js +16 -7
- package/dist/transport/node/builders/abprops.js +23 -0
- package/dist/transport/node/builders/device.js +14 -0
- package/dist/transport/node/builders/email.js +72 -0
- package/dist/transport/node/builders/offline.js +17 -0
- package/dist/transport/node/builders/prekeys.js +36 -39
- package/dist/transport/node/builders/presence.js +16 -0
- package/dist/transport/node/builders/privacy-token.js +18 -22
- package/dist/transport/node/builders/retry.js +1 -1
- package/dist/transport/node/helpers.js +26 -0
- package/dist/transport/node/mex/argo-decoder.js +189 -0
- package/dist/transport/node/mex/client.js +86 -0
- package/dist/transport/node/mex/persist-ids.js +13 -0
- package/dist/transport/noise/WaClientPayload.js +14 -9
- package/dist/transport/noise/WaFrameCodec.js +1 -1
- package/dist/transport/noise/WaMobileClientPayload.js +56 -0
- package/dist/transport/noise/WaNoiseCert.js +8 -26
- package/dist/transport/noise/WaNoiseSession.js +11 -10
- package/dist/types/appstate/WaAppStateCrypto.d.ts +11 -8
- package/dist/types/appstate/WaAppStateSyncClient.d.ts +6 -2
- package/dist/types/appstate/index.d.ts +1 -1
- package/dist/types/appstate/{WaAppStateSyncResponseParser.d.ts → response-parser.d.ts} +1 -1
- package/dist/types/appstate/types.d.ts +1 -1
- package/dist/types/auth/WaAuthClient.d.ts +9 -3
- package/dist/types/auth/credentials-flow.d.ts +20 -0
- package/dist/types/auth/pairing/WaPairingFlow.d.ts +3 -2
- package/dist/types/auth/pairing/{WaPairingCodeCrypto.d.ts → pairing-code-crypto.d.ts} +6 -1
- package/dist/types/auth/types.d.ts +40 -0
- package/dist/types/client/WaClient.d.ts +19 -8
- package/dist/types/client/WaClientFactory.d.ts +10 -4
- package/dist/types/client/coordinators/WaAbPropsCoordinator.d.ts +26 -0
- package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +1 -1
- package/dist/types/client/coordinators/WaEmailCoordinator.d.ts +24 -0
- package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +6 -1
- package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +15 -2
- package/dist/types/client/coordinators/WaOfflineResumeCoordinator.d.ts +31 -0
- package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +13 -2
- package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +1 -1
- package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +4 -2
- package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +6 -0
- package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +11 -1
- package/dist/types/client/dirty.d.ts +3 -1
- package/dist/types/client/events/abprops.d.ts +14 -0
- package/dist/types/client/events/registration.d.ts +17 -0
- package/dist/types/client/incoming.d.ts +6 -1
- package/dist/types/client/mailbox.d.ts +2 -0
- package/dist/types/client/media.d.ts +31 -0
- package/dist/types/client/messages.d.ts +2 -0
- package/dist/types/client/persistence/WriteBehindPersistence.d.ts +1 -1
- package/dist/types/client/types.d.ts +100 -1
- package/dist/types/crypto/core/index.d.ts +1 -0
- package/dist/types/crypto/core/primitives.d.ts +1 -1
- package/dist/types/crypto/core/random.d.ts +1 -1
- package/dist/types/crypto/core/xeddsa.d.ts +2 -0
- package/dist/types/crypto/curves/constants.d.ts +1 -0
- package/dist/types/crypto/index.d.ts +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/infra/log/ConsoleLogger.d.ts +1 -1
- package/dist/types/infra/log/PinoLogger.d.ts +1 -1
- package/dist/types/infra/perf/StoreLock.d.ts +1 -0
- package/dist/types/media/WaMediaCrypto.d.ts +15 -6
- package/dist/types/media/WaMediaTransferClient.d.ts +3 -11
- package/dist/types/media/constants.d.ts +2 -0
- package/dist/types/media/index.d.ts +1 -0
- package/dist/types/media/processor.d.ts +28 -0
- package/dist/types/media/types.d.ts +9 -3
- package/dist/types/message/addon-crypto.d.ts +34 -3
- package/dist/types/message/content.d.ts +3 -1
- package/dist/types/message/icdc.d.ts +4 -4
- package/dist/types/message/types.d.ts +16 -24
- package/dist/types/protocol/abprops.d.ts +142 -0
- package/dist/types/protocol/browser.d.ts +1 -0
- package/dist/types/protocol/constants.d.ts +5 -1
- package/dist/types/protocol/email.d.ts +32 -0
- package/dist/types/protocol/jid.d.ts +1 -0
- package/dist/types/protocol/nodes.d.ts +4 -0
- package/dist/types/protocol/notification.d.ts +6 -0
- package/dist/types/protocol/stream.d.ts +1 -0
- package/dist/types/retry/reason.d.ts +1 -1
- package/dist/types/signal/api/SignalDigestSyncApi.d.ts +3 -0
- package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +3 -3
- package/dist/types/signal/crypto/WaAdvSignature.d.ts +0 -2
- package/dist/types/signal/crypto/constants.d.ts +0 -1
- package/dist/types/signal/encoding.d.ts +7 -1
- package/dist/types/signal/group/SenderKeyChain.d.ts +1 -1
- package/dist/types/signal/group/SenderKeyManager.d.ts +7 -2
- package/dist/types/signal/registration/utils.d.ts +2 -1
- package/dist/types/signal/session/SignalProtocol.d.ts +11 -2
- package/dist/types/signal/session/SignalSerializer.d.ts +2 -1
- package/dist/types/signal/session/resolver.d.ts +4 -2
- package/dist/types/signal/types.d.ts +16 -4
- package/dist/types/store/contracts/identity.store.d.ts +11 -0
- package/dist/types/store/contracts/message-secret.store.d.ts +16 -0
- package/dist/types/store/contracts/pre-key.store.d.ts +13 -0
- package/dist/types/store/contracts/session.store.d.ts +14 -0
- package/dist/types/store/contracts/signal.store.d.ts +1 -34
- package/dist/types/store/index.d.ts +9 -1
- package/dist/types/store/locks/identity.lock.d.ts +3 -0
- package/dist/types/store/locks/message-secret.lock.d.ts +3 -0
- package/dist/types/store/locks/pre-key.lock.d.ts +3 -0
- package/dist/types/store/locks/session.lock.d.ts +3 -0
- package/dist/types/store/noop.store.d.ts +4 -0
- package/dist/types/store/providers/memory/identity.store.d.ts +18 -0
- package/dist/types/store/providers/memory/message-secret.store.d.ts +21 -0
- package/dist/types/store/providers/memory/pre-key.store.d.ts +23 -0
- package/dist/types/store/providers/memory/retry.store.d.ts +7 -1
- package/dist/types/store/providers/memory/session.store.d.ts +21 -0
- package/dist/types/store/providers/memory/signal.store.d.ts +3 -45
- package/dist/types/store/providers/memory/thread.store.d.ts +1 -1
- package/dist/types/store/types.d.ts +21 -1
- package/dist/types/transport/WaWebSocket.d.ts +1 -0
- package/dist/types/transport/index.d.ts +8 -1
- package/dist/types/transport/keepalive/WaKeepAlive.d.ts +4 -1
- package/dist/types/transport/node/WaMobileTcpSocket.d.ts +18 -0
- package/dist/types/transport/node/WaNodeOrchestrator.d.ts +6 -2
- package/dist/types/transport/node/builders/abprops.d.ts +5 -0
- package/dist/types/transport/node/builders/device.d.ts +2 -0
- package/dist/types/transport/node/builders/email.d.ts +11 -0
- package/dist/types/transport/node/builders/offline.d.ts +2 -0
- package/dist/types/transport/node/builders/prekeys.d.ts +4 -3
- package/dist/types/transport/node/builders/presence.d.ts +6 -0
- package/dist/types/transport/node/helpers.d.ts +3 -0
- package/dist/types/transport/node/mex/argo-decoder.d.ts +11 -0
- package/dist/types/transport/node/mex/client.d.ts +18 -0
- package/dist/types/transport/node/mex/persist-ids.d.ts +14 -0
- package/dist/types/transport/noise/WaMobileClientPayload.d.ts +29 -0
- package/dist/types/transport/noise/WaNoiseCert.d.ts +7 -1
- package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
- package/dist/types/transport/types.d.ts +8 -0
- package/package.json +6 -4
- package/dist/auth/pairing/constants.js +0 -5
- package/dist/client/connection/WaKeyShareCoordinator.js +0 -63
- package/dist/esm/auth/pairing/constants.js +0 -2
- package/dist/esm/client/connection/WaKeyShareCoordinator.js +0 -59
- package/dist/esm/transport/node/builders/index.js +0 -11
- package/dist/transport/node/builders/index.js +0 -51
- package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +0 -14
- package/dist/types/auth/pairing/constants.d.ts +0 -2
- package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +0 -14
- package/dist/types/transport/node/builders/index.d.ts +0 -11
- /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(
|
|
28
|
+
const tracker = current.then(StoreLock._noop, StoreLock._noop);
|
|
29
29
|
this.chains.set(key, tracker);
|
|
30
|
-
|
|
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 {
|
|
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 {
|
|
13
|
-
import { assertByteLength, concatBytes, EMPTY_BYTES,
|
|
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,
|
|
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(
|
|
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 {
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
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
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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 =
|
|
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);
|