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
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { APP_STATE_DEFAULT_COLLECTION_VERSION, APP_STATE_DEFAULT_COLLECTIONS, APP_STATE_EMPTY_LT_HASH } from './constants.js';
|
|
2
|
+
import { parseSyncResponse } from './response-parser.js';
|
|
2
3
|
import { WaAppStateCrypto } from './WaAppStateCrypto.js';
|
|
3
|
-
import {
|
|
4
|
+
import { randomBytesAsync, randomIntAsync } from '../crypto/index.js';
|
|
4
5
|
import { proto } from '../proto.js';
|
|
5
6
|
import { WA_APP_STATE_COLLECTION_STATES, WA_DEFAULTS, WA_IQ_TYPES, WA_NODE_TAGS, WA_XMLNS } from '../protocol/constants.js';
|
|
6
7
|
import { parseSignalAddressFromJid } from '../protocol/jid.js';
|
|
7
8
|
import { assertIqResult } from '../transport/node/query.js';
|
|
8
|
-
import { decodeProtoBytes } from '../util/bytes.js';
|
|
9
|
-
import { bytesToHex, uint8Equal } from '../util/bytes.js';
|
|
9
|
+
import { bytesToHex, decodeProtoBytes, uint8Equal } from '../util/bytes.js';
|
|
10
10
|
import { longToNumber } from '../util/primitives.js';
|
|
11
11
|
export class WaAppStateMissingKeyError extends Error {
|
|
12
12
|
constructor(message, keyId, collection) {
|
|
@@ -25,7 +25,8 @@ export class WaAppStateSyncClient {
|
|
|
25
25
|
this.hostDomain = options.hostDomain ?? WA_DEFAULTS.HOST_DOMAIN;
|
|
26
26
|
this.defaultTimeoutMs = options.defaultTimeoutMs ?? WA_DEFAULTS.APP_STATE_SYNC_TIMEOUT_MS;
|
|
27
27
|
this.onMissingKeys = options.onMissingKeys;
|
|
28
|
-
this.crypto = new WaAppStateCrypto();
|
|
28
|
+
this.crypto = new WaAppStateCrypto(undefined, options.skipMacVerification === true);
|
|
29
|
+
this.mobilePrimary = options.mobilePrimary ?? false;
|
|
29
30
|
this.syncContext = null;
|
|
30
31
|
this.syncPromise = null;
|
|
31
32
|
}
|
|
@@ -33,6 +34,28 @@ export class WaAppStateSyncClient {
|
|
|
33
34
|
this.logger.trace('app-state export requested');
|
|
34
35
|
return this.store.exportData();
|
|
35
36
|
}
|
|
37
|
+
async ensureInitialSyncKey() {
|
|
38
|
+
const existing = await this.store.getActiveSyncKey();
|
|
39
|
+
if (existing) {
|
|
40
|
+
return existing;
|
|
41
|
+
}
|
|
42
|
+
const keyIdBytes = await randomBytesAsync(2);
|
|
43
|
+
const keyData = await randomBytesAsync(32);
|
|
44
|
+
const rawId = await randomIntAsync(0, 4294967295);
|
|
45
|
+
const key = {
|
|
46
|
+
keyId: keyIdBytes,
|
|
47
|
+
keyData,
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
fingerprint: { rawId, currentIndex: 0, deviceIndexes: [0] }
|
|
50
|
+
};
|
|
51
|
+
await this.store.upsertSyncKeys([key]);
|
|
52
|
+
this.crypto.clearCache();
|
|
53
|
+
this.logger.info('app-state initial sync key generated (mobile primary)', {
|
|
54
|
+
keyId: bytesToHex(keyIdBytes),
|
|
55
|
+
rawId
|
|
56
|
+
});
|
|
57
|
+
return key;
|
|
58
|
+
}
|
|
36
59
|
async importSyncKeys(keys) {
|
|
37
60
|
this.logger.debug('app-state importing sync keys', { count: keys.length });
|
|
38
61
|
const inserted = await this.store.upsertSyncKeys(keys);
|
|
@@ -283,7 +306,7 @@ export class WaAppStateSyncClient {
|
|
|
283
306
|
content: [
|
|
284
307
|
{
|
|
285
308
|
tag: WA_NODE_TAGS.SYNC,
|
|
286
|
-
attrs: {},
|
|
309
|
+
attrs: this.mobilePrimary ? { data_namespace: '3' } : {},
|
|
287
310
|
content: collectionNodes
|
|
288
311
|
}
|
|
289
312
|
]
|
|
@@ -462,11 +485,13 @@ export class WaAppStateSyncClient {
|
|
|
462
485
|
if (!snapshot.version?.version) {
|
|
463
486
|
throw new Error(`snapshot for ${collection} is missing version`);
|
|
464
487
|
}
|
|
465
|
-
if (!
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
488
|
+
if (!this.crypto.isMacVerificationSkipped) {
|
|
489
|
+
if (!snapshot.mac) {
|
|
490
|
+
throw new Error(`snapshot for ${collection} is missing mac`);
|
|
491
|
+
}
|
|
492
|
+
if (!snapshot.keyId?.id) {
|
|
493
|
+
throw new Error(`snapshot for ${collection} is missing keyId`);
|
|
494
|
+
}
|
|
470
495
|
}
|
|
471
496
|
return snapshot;
|
|
472
497
|
}
|
|
@@ -481,14 +506,16 @@ export class WaAppStateSyncClient {
|
|
|
481
506
|
if (!patch.version?.version) {
|
|
482
507
|
throw new Error(`patch for ${collection} is missing version`);
|
|
483
508
|
}
|
|
484
|
-
if (!
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
509
|
+
if (!this.crypto.isMacVerificationSkipped) {
|
|
510
|
+
if (!patch.snapshotMac) {
|
|
511
|
+
throw new Error(`patch for ${collection} is missing snapshotMac`);
|
|
512
|
+
}
|
|
513
|
+
if (!patch.patchMac) {
|
|
514
|
+
throw new Error(`patch for ${collection} is missing patchMac`);
|
|
515
|
+
}
|
|
516
|
+
if (!patch.keyId?.id) {
|
|
517
|
+
throw new Error(`patch for ${collection} is missing keyId`);
|
|
518
|
+
}
|
|
492
519
|
}
|
|
493
520
|
if (patch.mutations && patch.mutations.length > 0 && patch.externalMutations) {
|
|
494
521
|
throw new Error(`patch for ${collection} has inline and external mutations together`);
|
|
@@ -502,10 +529,13 @@ export class WaAppStateSyncClient {
|
|
|
502
529
|
}
|
|
503
530
|
async applySnapshot(collection, snapshot) {
|
|
504
531
|
const version = this.normalizeProtoLong(snapshot.version?.version, `snapshot.version.version (${collection})`);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
532
|
+
let keyData = null;
|
|
533
|
+
if (!this.crypto.isMacVerificationSkipped) {
|
|
534
|
+
const keyId = decodeProtoBytes(snapshot.keyId?.id, `snapshot.keyId.id (${collection})`);
|
|
535
|
+
keyData = await this.getKeyData(keyId);
|
|
536
|
+
if (!keyData) {
|
|
537
|
+
throw new WaAppStateMissingKeyError(`missing snapshot key ${bytesToHex(keyId)} for ${collection}`, keyId, collection);
|
|
538
|
+
}
|
|
509
539
|
}
|
|
510
540
|
const indexValueMap = new Map();
|
|
511
541
|
const mutations = [];
|
|
@@ -533,9 +563,11 @@ export class WaAppStateSyncClient {
|
|
|
533
563
|
ltHashInputIndex += 1;
|
|
534
564
|
}
|
|
535
565
|
const ltHash = await this.crypto.ltHashAdd(APP_STATE_EMPTY_LT_HASH, ltHashInput);
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
566
|
+
if (keyData !== null) {
|
|
567
|
+
const expectedSnapshotMac = await this.crypto.generateSnapshotMac(keyData, ltHash, version, collection);
|
|
568
|
+
if (!uint8Equal(expectedSnapshotMac, snapshot.mac)) {
|
|
569
|
+
throw new Error(`snapshot MAC mismatch for ${collection}`);
|
|
570
|
+
}
|
|
539
571
|
}
|
|
540
572
|
this.setCollectionState(collection, version, ltHash, indexValueMap);
|
|
541
573
|
return mutations;
|
|
@@ -546,10 +578,13 @@ export class WaAppStateSyncClient {
|
|
|
546
578
|
if (current.version !== patchVersion - 1) {
|
|
547
579
|
throw new Error(`patch version mismatch for ${collection}: local=${current.version}, incoming=${patchVersion}`);
|
|
548
580
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
581
|
+
let patchKeyData = null;
|
|
582
|
+
if (!this.crypto.isMacVerificationSkipped) {
|
|
583
|
+
const patchKeyId = decodeProtoBytes(patch.keyId?.id, `patch.keyId.id (${collection})`);
|
|
584
|
+
patchKeyData = await this.getKeyData(patchKeyId);
|
|
585
|
+
if (!patchKeyData) {
|
|
586
|
+
throw new WaAppStateMissingKeyError(`missing patch key ${bytesToHex(patchKeyId)} for ${collection}`, patchKeyId, collection);
|
|
587
|
+
}
|
|
553
588
|
}
|
|
554
589
|
const decryptedMutations = await this.decryptPatchMutations(collection, patch);
|
|
555
590
|
const macMutations = new Array(decryptedMutations.length);
|
|
@@ -564,7 +599,9 @@ export class WaAppStateSyncClient {
|
|
|
564
599
|
};
|
|
565
600
|
}
|
|
566
601
|
const nextState = await this.computeNextCollectionState(current.hash, current.indexValueMap, macMutations, collection);
|
|
567
|
-
|
|
602
|
+
if (patchKeyData !== null) {
|
|
603
|
+
await this.assertPatchMacsMatch(patch, collection, patchKeyData, patchVersion, nextState.hash, valueMacs);
|
|
604
|
+
}
|
|
568
605
|
this.setCollectionState(collection, patchVersion, nextState.hash, nextState.indexValueMap);
|
|
569
606
|
return decryptedMutations;
|
|
570
607
|
}
|
|
@@ -2,5 +2,5 @@ export * from './constants.js';
|
|
|
2
2
|
export { encodeAppStateFingerprint, decodeAppStateFingerprint, decodeAppStateCollections, decodeAppStateSyncKeys } from './encoding.js';
|
|
3
3
|
export * from './utils.js';
|
|
4
4
|
export { WaAppStateCrypto } from './WaAppStateCrypto.js';
|
|
5
|
-
export { parseSyncResponse } from './
|
|
5
|
+
export { parseSyncResponse } from './response-parser.js';
|
|
6
6
|
export { WaAppStateSyncClient } from './WaAppStateSyncClient.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseCollectionName } from './utils.js';
|
|
2
2
|
import { proto } from '../proto.js';
|
|
3
3
|
import { WA_APP_STATE_COLLECTION_STATES, WA_APP_STATE_ERROR_CODES, WA_IQ_TYPES, WA_NODE_TAGS } from '../protocol/constants.js';
|
|
4
|
-
import { decodeNodeContentBase64OrBytes,
|
|
4
|
+
import { decodeNodeContentBase64OrBytes, findNodeChild, findNodeChildrenByTags, getNodeChildrenByTag } from '../transport/node/helpers.js';
|
|
5
5
|
export function parseSyncResponse(iqNode) {
|
|
6
6
|
if (iqNode.tag !== WA_NODE_TAGS.IQ) {
|
|
7
7
|
throw new Error(`invalid sync response tag ${iqNode.tag}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCommsConfig, loadOrCreateCredentials, persistCredentials } from './flow
|
|
1
|
+
import { buildCommsConfig, loadOrCreateCredentials, persistCredentials } from './credentials-flow.js';
|
|
2
2
|
import { WaPairingFlow } from './pairing/WaPairingFlow.js';
|
|
3
3
|
import { WaQrFlow } from './pairing/WaQrFlow.js';
|
|
4
4
|
import { getWaCompanionPlatformId, WA_DEFAULTS } from '../protocol/constants.js';
|
|
@@ -24,6 +24,7 @@ export class WaAuthClient {
|
|
|
24
24
|
this.callbacks = deps.callbacks ?? {};
|
|
25
25
|
this.authStore = deps.authStore;
|
|
26
26
|
this.signalStore = deps.signalStore;
|
|
27
|
+
this.preKeyStore = deps.preKeyStore;
|
|
27
28
|
this.credentials = null;
|
|
28
29
|
this.qrFlow = new WaQrFlow({
|
|
29
30
|
logger: this.logger,
|
|
@@ -44,7 +45,8 @@ export class WaAuthClient {
|
|
|
44
45
|
emitPairingCode: (code) => this.callbacks.onPairingCode?.(code),
|
|
45
46
|
emitPairingRefresh: (forceManual) => this.callbacks.onPairingRefresh?.(forceManual),
|
|
46
47
|
emitPaired: (credentials) => this.callbacks.onPaired?.(credentials)
|
|
47
|
-
}
|
|
48
|
+
},
|
|
49
|
+
dangerous: options.dangerous
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
52
|
getState(connected = false) {
|
|
@@ -64,7 +66,9 @@ export class WaAuthClient {
|
|
|
64
66
|
this.credentials = await loadOrCreateCredentials({
|
|
65
67
|
logger: this.logger,
|
|
66
68
|
authStore: this.authStore,
|
|
67
|
-
signalStore: this.signalStore
|
|
69
|
+
signalStore: this.signalStore,
|
|
70
|
+
preKeyStore: this.preKeyStore,
|
|
71
|
+
skipSignedPreKeySignatureVerification: this.options.dangerous?.disableSignedPreKeySignatureVerification
|
|
68
72
|
});
|
|
69
73
|
this.logger.info('auth client credentials ready', {
|
|
70
74
|
registered: this.credentials?.meJid !== null && this.credentials?.meJid !== undefined
|
|
@@ -72,13 +76,17 @@ export class WaAuthClient {
|
|
|
72
76
|
return this.credentials;
|
|
73
77
|
});
|
|
74
78
|
}
|
|
75
|
-
buildCommsConfig(socketOptions) {
|
|
79
|
+
buildCommsConfig(socketOptions, overrides = {}) {
|
|
76
80
|
this.logger.trace('auth client building comms config');
|
|
77
81
|
return buildCommsConfig(this.logger, this.requireCredentials(), socketOptions, {
|
|
78
82
|
deviceBrowser: this.options.deviceBrowser,
|
|
79
83
|
deviceOsDisplayName: this.options.deviceOsDisplayName,
|
|
80
84
|
requireFullSync: this.options.requireFullSync,
|
|
81
|
-
version: this.options.version
|
|
85
|
+
version: this.options.version,
|
|
86
|
+
mobileTransport: this.options.mobileTransport,
|
|
87
|
+
noiseTrustedRootCa: overrides.noiseTrustedRootCa,
|
|
88
|
+
disableNoiseCertificateChainVerification: overrides.disableNoiseCertificateChainVerification ??
|
|
89
|
+
this.options.dangerous?.disableNoiseCertificateChainVerification
|
|
82
90
|
});
|
|
83
91
|
}
|
|
84
92
|
async clearTransientState() {
|
|
@@ -177,10 +185,10 @@ export class WaAuthClient {
|
|
|
177
185
|
}
|
|
178
186
|
});
|
|
179
187
|
}
|
|
180
|
-
async requestPairingCode(phoneNumber, shouldShowPushNotification = false) {
|
|
188
|
+
async requestPairingCode(phoneNumber, shouldShowPushNotification = false, customCode) {
|
|
181
189
|
this.requireCredentials();
|
|
182
190
|
this.logger.info('auth client requesting pairing code');
|
|
183
|
-
return this.runHandled(() => this.pairingFlow.requestPairingCode(phoneNumber, shouldShowPushNotification));
|
|
191
|
+
return this.runHandled(() => this.pairingFlow.requestPairingCode(phoneNumber, shouldShowPushNotification, customCode));
|
|
184
192
|
}
|
|
185
193
|
async fetchPairingCountryCodeIso() {
|
|
186
194
|
this.requireCredentials();
|
|
@@ -228,7 +236,8 @@ export class WaAuthClient {
|
|
|
228
236
|
await persistCredentials({
|
|
229
237
|
logger: this.logger,
|
|
230
238
|
authStore: this.authStore,
|
|
231
|
-
signalStore: this.signalStore
|
|
239
|
+
signalStore: this.signalStore,
|
|
240
|
+
preKeyStore: this.preKeyStore
|
|
232
241
|
}, credentials);
|
|
233
242
|
}
|
|
234
243
|
requireCredentials() {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { randomBytesAsync } from '
|
|
2
|
-
import { toSerializedPubKey } from '
|
|
3
|
-
import { X25519 } from '
|
|
4
|
-
import { getLoginIdentity } from '
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { randomBytesAsync, toRawPubKey, xeddsaVerify } from '../crypto/index.js';
|
|
2
|
+
import { toSerializedPubKey } from '../crypto/core/keys.js';
|
|
3
|
+
import { X25519 } from '../crypto/curves/X25519.js';
|
|
4
|
+
import { getLoginIdentity } from '../protocol/jid.js';
|
|
5
|
+
import { createAndStoreInitialKeys } from '../signal/registration/utils.js';
|
|
6
|
+
import { WaMobileTcpSocketCtor } from '../transport/node/WaMobileTcpSocket.js';
|
|
7
|
+
import { buildMobileLoginPayload } from '../transport/noise/WaMobileClientPayload.js';
|
|
8
|
+
import { toProxyAgent, toProxyDispatcher } from '../transport/proxy.js';
|
|
9
|
+
import { toError } from '../util/primitives.js';
|
|
9
10
|
export async function loadOrCreateCredentials(args) {
|
|
10
11
|
args.logger.trace('auth credentials loadOrCreate start');
|
|
11
12
|
const existing = await args.authStore.load();
|
|
@@ -18,13 +19,16 @@ export async function loadOrCreateCredentials(args) {
|
|
|
18
19
|
registered: existing.meJid !== null && existing.meJid !== undefined,
|
|
19
20
|
hasServerStaticKey: existing.serverStaticKey !== null && existing.serverStaticKey !== undefined
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
const skipSignedPreKeyCheck = args.skipSignedPreKeySignatureVerification === true;
|
|
23
|
+
if (!existing.meJid &&
|
|
24
|
+
!skipSignedPreKeyCheck &&
|
|
25
|
+
!(await hasValidSignedPreKey(args.logger, existing))) {
|
|
22
26
|
args.logger.warn('signed pre-key is invalid, regenerating credentials');
|
|
23
27
|
const fresh = await createFreshAndPersistCredentials(args);
|
|
24
28
|
args.logger.info('regenerated credentials due to invalid signed pre-key');
|
|
25
29
|
return fresh;
|
|
26
30
|
}
|
|
27
|
-
await restoreSignalStore(args.signalStore, existing);
|
|
31
|
+
await restoreSignalStore(args.signalStore, args.preKeyStore, existing);
|
|
28
32
|
args.logger.trace('auth credentials restored into signal store');
|
|
29
33
|
return existing;
|
|
30
34
|
}
|
|
@@ -34,15 +38,72 @@ export async function persistCredentials(args, credentials) {
|
|
|
34
38
|
});
|
|
35
39
|
await args.authStore.save(credentials);
|
|
36
40
|
}
|
|
41
|
+
function mobileTransportFromCredentials(credentials) {
|
|
42
|
+
if (!credentials.deviceInfo)
|
|
43
|
+
return undefined;
|
|
44
|
+
return {
|
|
45
|
+
deviceInfo: credentials.deviceInfo,
|
|
46
|
+
...(credentials.pushName !== undefined ? { pushName: credentials.pushName } : {}),
|
|
47
|
+
...(credentials.yearClass !== undefined ? { yearClass: credentials.yearClass } : {}),
|
|
48
|
+
...(credentials.memClass !== undefined ? { memClass: credentials.memClass } : {})
|
|
49
|
+
};
|
|
50
|
+
}
|
|
37
51
|
export function buildCommsConfig(logger, credentials, socketOptions, clientOptions) {
|
|
38
52
|
const meJid = credentials.meJid;
|
|
39
53
|
const registered = meJid !== null && meJid !== undefined;
|
|
40
54
|
const loginIdentity = registered ? getLoginIdentity(meJid) : null;
|
|
41
55
|
const wsProxy = socketOptions.proxy?.ws;
|
|
56
|
+
// Resolve the effective mobile transport: explicit option wins, otherwise
|
|
57
|
+
// synthesize one from persisted credentials.deviceInfo so a registered
|
|
58
|
+
// mobile session boots in mobile mode without the caller re-passing
|
|
59
|
+
// deviceInfo on every `new WaClient(...)` call.
|
|
60
|
+
const effectiveMobileTransport = clientOptions.mobileTransport ?? mobileTransportFromCredentials(credentials);
|
|
42
61
|
logger.debug('building comms config from credentials', {
|
|
43
62
|
registered,
|
|
44
|
-
hasServerStaticKey: credentials.serverStaticKey !== null && credentials.serverStaticKey !== undefined
|
|
63
|
+
hasServerStaticKey: credentials.serverStaticKey !== null && credentials.serverStaticKey !== undefined,
|
|
64
|
+
mobile: Boolean(effectiveMobileTransport),
|
|
65
|
+
mobileSource: clientOptions.mobileTransport
|
|
66
|
+
? 'option'
|
|
67
|
+
: effectiveMobileTransport
|
|
68
|
+
? 'credentials'
|
|
69
|
+
: 'none'
|
|
45
70
|
});
|
|
71
|
+
if (effectiveMobileTransport) {
|
|
72
|
+
if (wsProxy) {
|
|
73
|
+
throw new Error('mobileTransport does not support socketOptions.proxy.ws — remove the proxy option or open an issue to add TCP proxy support');
|
|
74
|
+
}
|
|
75
|
+
if (!loginIdentity) {
|
|
76
|
+
throw new Error('mobileTransport requires registered credentials (meJid) — run the mobile bridge flow first');
|
|
77
|
+
}
|
|
78
|
+
const loginPayload = buildMobileLoginPayload({
|
|
79
|
+
username: loginIdentity.username,
|
|
80
|
+
device: loginIdentity.device,
|
|
81
|
+
passive: effectiveMobileTransport.passive ?? false,
|
|
82
|
+
deviceInfo: effectiveMobileTransport.deviceInfo,
|
|
83
|
+
pushName: effectiveMobileTransport.pushName,
|
|
84
|
+
yearClass: effectiveMobileTransport.yearClass,
|
|
85
|
+
memClass: effectiveMobileTransport.memClass
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
url: effectiveMobileTransport.tcpUrl ?? 'tcp://g.whatsapp.net:443',
|
|
89
|
+
rawWebSocketConstructor: WaMobileTcpSocketCtor,
|
|
90
|
+
connectTimeoutMs: socketOptions.connectTimeoutMs,
|
|
91
|
+
reconnectIntervalMs: socketOptions.reconnectIntervalMs,
|
|
92
|
+
timeoutIntervalMs: socketOptions.timeoutIntervalMs,
|
|
93
|
+
maxReconnectAttempts: socketOptions.maxReconnectAttempts,
|
|
94
|
+
noise: {
|
|
95
|
+
clientStaticKeyPair: credentials.noiseKeyPair,
|
|
96
|
+
isRegistered: true,
|
|
97
|
+
serverStaticKey: credentials.serverStaticKey,
|
|
98
|
+
routingInfo: credentials.routingInfo,
|
|
99
|
+
trustedRootCa: clientOptions.noiseTrustedRootCa,
|
|
100
|
+
verifyCertificateChain: clientOptions.disableNoiseCertificateChainVerification
|
|
101
|
+
? false
|
|
102
|
+
: undefined,
|
|
103
|
+
loginPayload
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
46
107
|
return {
|
|
47
108
|
url: socketOptions.url,
|
|
48
109
|
urls: socketOptions.urls,
|
|
@@ -58,6 +119,10 @@ export function buildCommsConfig(logger, credentials, socketOptions, clientOptio
|
|
|
58
119
|
isRegistered: registered,
|
|
59
120
|
serverStaticKey: credentials.serverStaticKey,
|
|
60
121
|
routingInfo: credentials.routingInfo,
|
|
122
|
+
trustedRootCa: clientOptions.noiseTrustedRootCa,
|
|
123
|
+
verifyCertificateChain: clientOptions.disableNoiseCertificateChainVerification
|
|
124
|
+
? false
|
|
125
|
+
: undefined,
|
|
61
126
|
loginPayloadConfig: loginIdentity
|
|
62
127
|
? {
|
|
63
128
|
username: loginIdentity.username,
|
|
@@ -80,11 +145,11 @@ export function buildCommsConfig(logger, credentials, socketOptions, clientOptio
|
|
|
80
145
|
}
|
|
81
146
|
};
|
|
82
147
|
}
|
|
83
|
-
async function createFreshCredentials(signalStore, logger) {
|
|
148
|
+
async function createFreshCredentials(signalStore, preKeyStore, logger) {
|
|
84
149
|
logger.trace('creating fresh credentials');
|
|
85
150
|
const [noiseKeyPair, registrationBundle, advSecretKey] = await Promise.all([
|
|
86
151
|
X25519.generateKeyPair(),
|
|
87
|
-
createAndStoreInitialKeys(signalStore),
|
|
152
|
+
createAndStoreInitialKeys(signalStore, preKeyStore),
|
|
88
153
|
randomBytesAsync(32)
|
|
89
154
|
]);
|
|
90
155
|
return {
|
|
@@ -96,16 +161,16 @@ async function createFreshCredentials(signalStore, logger) {
|
|
|
96
161
|
};
|
|
97
162
|
}
|
|
98
163
|
async function createFreshAndPersistCredentials(args) {
|
|
99
|
-
const credentials = await createFreshCredentials(args.signalStore, args.logger);
|
|
164
|
+
const credentials = await createFreshCredentials(args.signalStore, args.preKeyStore, args.logger);
|
|
100
165
|
// Persist credentials first so signal restore never commits state for credentials that failed to save.
|
|
101
166
|
await args.authStore.save(credentials);
|
|
102
|
-
await restoreSignalStore(args.signalStore, credentials);
|
|
167
|
+
await restoreSignalStore(args.signalStore, args.preKeyStore, credentials);
|
|
103
168
|
return credentials;
|
|
104
169
|
}
|
|
105
170
|
async function hasValidSignedPreKey(logger, credentials) {
|
|
106
171
|
try {
|
|
107
172
|
const serializedPubKey = toSerializedPubKey(credentials.signedPreKey.keyPair.pubKey);
|
|
108
|
-
const valid = await
|
|
173
|
+
const valid = await xeddsaVerify(toRawPubKey(credentials.registrationInfo.identityKeyPair.pubKey), serializedPubKey, credentials.signedPreKey.signature);
|
|
109
174
|
logger.trace('signed pre-key validation completed', { valid });
|
|
110
175
|
return valid;
|
|
111
176
|
}
|
|
@@ -116,10 +181,10 @@ async function hasValidSignedPreKey(logger, credentials) {
|
|
|
116
181
|
return false;
|
|
117
182
|
}
|
|
118
183
|
}
|
|
119
|
-
async function restoreSignalStore(signalStore, credentials) {
|
|
184
|
+
async function restoreSignalStore(signalStore, preKeyStore, credentials) {
|
|
120
185
|
await Promise.all([
|
|
121
186
|
signalStore.setRegistrationInfo(credentials.registrationInfo),
|
|
122
187
|
signalStore.setSignedPreKey(credentials.signedPreKey),
|
|
123
|
-
|
|
188
|
+
preKeyStore.setServerHasPreKeys(credentials.serverHasPreKeys === true)
|
|
124
189
|
]);
|
|
125
190
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { completeCompanionFinish, createCompanionHello } from '../pairing/
|
|
1
|
+
import { completeCompanionFinish, createCompanionHello, normalizeCustomPairingCode } from '../pairing/pairing-code-crypto.js';
|
|
2
2
|
import { randomBytesAsync } from '../../crypto/index.js';
|
|
3
3
|
import { proto } from '../../proto.js';
|
|
4
|
+
import { getWaBrowserDisplayName } from '../../protocol/browser.js';
|
|
4
5
|
import { WA_DEFAULTS, WA_IQ_TYPES, WA_NODE_TAGS, WA_SIGNALING } from '../../protocol/constants.js';
|
|
5
6
|
import { parsePhoneJid } from '../../protocol/jid.js';
|
|
6
7
|
import { ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, computeAdvIdentityHmac, generateDeviceSignature, verifyDeviceIdentityAccountSignature } from '../../signal/crypto/WaAdvSignature.js';
|
|
7
8
|
import { buildAckNode, buildIqResultNode } from '../../transport/node/builders/global.js';
|
|
8
9
|
import { buildCompanionFinishRequestNode, buildCompanionHelloRequestNode, buildGetCountryCodeRequestNode } from '../../transport/node/builders/pairing.js';
|
|
9
|
-
import { decodeNodeContentUtf8OrBytes,
|
|
10
|
-
import { decodeProtoBytes } from '../../util/bytes.js';
|
|
11
|
-
import { concatBytes, uint8Equal } from '../../util/bytes.js';
|
|
10
|
+
import { decodeNodeContentUtf8OrBytes, findNodeChild, findNodeChildrenByTags, getFirstNodeChild, getNodeChildrenNonEmptyUtf8ByTag, hasNodeChild } from '../../transport/node/helpers.js';
|
|
11
|
+
import { concatBytes, decodeProtoBytes, uint8Equal } from '../../util/bytes.js';
|
|
12
12
|
export class WaPairingFlow {
|
|
13
13
|
constructor(options) {
|
|
14
14
|
this.opts = options;
|
|
@@ -21,26 +21,19 @@ export class WaPairingFlow {
|
|
|
21
21
|
this.opts.logger.trace('pairing flow session cleared');
|
|
22
22
|
this.pairingSession = null;
|
|
23
23
|
}
|
|
24
|
-
async requestPairingCode(phoneNumber, shouldShowPushNotification = false) {
|
|
24
|
+
async requestPairingCode(phoneNumber, shouldShowPushNotification = false, customCode) {
|
|
25
25
|
this.opts.logger.info('requesting pairing code', {
|
|
26
|
-
shouldShowPushNotification
|
|
26
|
+
shouldShowPushNotification,
|
|
27
|
+
hasCustomCode: customCode !== undefined
|
|
27
28
|
});
|
|
29
|
+
const normalizedCustomCode = customCode !== undefined ? normalizeCustomPairingCode(customCode) : undefined;
|
|
28
30
|
const credentials = this.requireCredentials();
|
|
29
31
|
const phoneJid = parsePhoneJid(phoneNumber);
|
|
30
32
|
const [companionHello, refreshedCredentials] = await Promise.all([
|
|
31
|
-
createCompanionHello(),
|
|
33
|
+
createCompanionHello({ customCode: normalizedCustomCode }),
|
|
32
34
|
this.rotateAdvSecret(credentials)
|
|
33
35
|
]);
|
|
34
|
-
const
|
|
35
|
-
const browserDisplayName = {
|
|
36
|
-
chrome: 'Chrome',
|
|
37
|
-
firefox: 'Firefox',
|
|
38
|
-
ie: 'IE',
|
|
39
|
-
opera: 'Opera',
|
|
40
|
-
safari: 'Safari',
|
|
41
|
-
edge: 'Edge',
|
|
42
|
-
chromium: 'Chromium'
|
|
43
|
-
}[browser.trim().toLowerCase()] ?? browser;
|
|
36
|
+
const browserDisplayName = getWaBrowserDisplayName(this.opts.device.browser);
|
|
44
37
|
const response = await this.opts.socket.query(buildCompanionHelloRequestNode({
|
|
45
38
|
phoneJid,
|
|
46
39
|
shouldShowPushNotification,
|
|
@@ -186,13 +179,15 @@ export class WaPairingFlow {
|
|
|
186
179
|
const wrappedHmac = decodeProtoBytes(wrappedIdentity.hmac, 'ADVSignedDeviceIdentityHMAC.hmac');
|
|
187
180
|
const accountType = wrappedIdentity.accountType ?? proto.ADVEncryptionType.E2EE;
|
|
188
181
|
const isHosted = accountType === proto.ADVEncryptionType.HOSTED;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
182
|
+
if (this.opts.dangerous?.disablePairSuccessHmacVerification !== true) {
|
|
183
|
+
const hmacInput = isHosted
|
|
184
|
+
? concatBytes([ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, wrappedDetails])
|
|
185
|
+
: wrappedDetails;
|
|
186
|
+
const expectedHmac = await computeAdvIdentityHmac(credentials.advSecretKey, hmacInput);
|
|
187
|
+
if (!uint8Equal(expectedHmac, wrappedHmac)) {
|
|
188
|
+
this.opts.logger.error('pair-success hmac mismatch');
|
|
189
|
+
throw new Error('pair-success HMAC validation failed');
|
|
190
|
+
}
|
|
196
191
|
}
|
|
197
192
|
const { signedIdentity, keyIndex, responseIdentityBytes } = await this.buildPairSuccessResponseIdentity(credentials, wrappedDetails, isHosted);
|
|
198
193
|
const nextCredentials = {
|
|
@@ -241,10 +236,12 @@ export class WaPairingFlow {
|
|
|
241
236
|
const accountSignature = decodeProtoBytes(signedIdentity.accountSignature, 'ADVSignedDeviceIdentity.accountSignature');
|
|
242
237
|
const accountSignatureKey = decodeProtoBytes(signedIdentity.accountSignatureKey, 'ADVSignedDeviceIdentity.accountSignatureKey');
|
|
243
238
|
const localIdentity = credentials.registrationInfo.identityKeyPair;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
239
|
+
if (this.opts.dangerous?.disableAdvSignatureVerification !== true) {
|
|
240
|
+
const validAccountSignature = await verifyDeviceIdentityAccountSignature(details, accountSignature, localIdentity.pubKey, accountSignatureKey, isHosted);
|
|
241
|
+
if (!validAccountSignature) {
|
|
242
|
+
this.opts.logger.error('pair-success account signature invalid');
|
|
243
|
+
throw new Error('pair-success account signature validation failed');
|
|
244
|
+
}
|
|
248
245
|
}
|
|
249
246
|
signedIdentity.deviceSignature = await generateDeviceSignature(details, localIdentity, accountSignatureKey, isHosted);
|
|
250
247
|
const advDeviceIdentity = proto.ADVDeviceIdentity.decode(details);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CROCKFORD_ALPHABET, PBKDF2_ITERATIONS } from '../pairing/constants.js';
|
|
2
1
|
import { aesCtrDecrypt, aesCtrEncrypt, aesGcmEncrypt, hkdf, importAesGcmKey, pbkdf2DeriveAesCtrKey, randomBytesAsync } from '../../crypto/index.js';
|
|
3
2
|
import { X25519 } from '../../crypto/curves/X25519.js';
|
|
4
3
|
import { WA_PAIRING_KDF_INFO } from '../../protocol/constants.js';
|
|
5
4
|
import { concatBytes, TEXT_ENCODER } from '../../util/bytes.js';
|
|
5
|
+
export const CROCKFORD_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTVWXYZ';
|
|
6
|
+
export const PBKDF2_ITERATIONS = 2 << 16;
|
|
6
7
|
function bytesToCrockford(bytes) {
|
|
7
8
|
let bitCount = 0;
|
|
8
9
|
let value = 0;
|
|
@@ -20,14 +21,27 @@ function bytesToCrockford(bytes) {
|
|
|
20
21
|
}
|
|
21
22
|
return out;
|
|
22
23
|
}
|
|
23
|
-
export
|
|
24
|
-
const
|
|
25
|
-
|
|
24
|
+
export function normalizeCustomPairingCode(input) {
|
|
25
|
+
const stripped = input.replace(/-/g, '').toUpperCase();
|
|
26
|
+
if (stripped.length !== 8) {
|
|
27
|
+
throw new Error(`custom pairing code must be 8 characters, got ${stripped.length}`);
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < stripped.length; i += 1) {
|
|
30
|
+
if (CROCKFORD_ALPHABET.indexOf(stripped[i]) < 0) {
|
|
31
|
+
throw new Error(`custom pairing code contains invalid character "${stripped[i]}" (allowed: ${CROCKFORD_ALPHABET})`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return stripped;
|
|
35
|
+
}
|
|
36
|
+
export async function createCompanionHello(options = {}) {
|
|
37
|
+
const normalizedCustomCode = options.customCode !== undefined ? normalizeCustomPairingCode(options.customCode) : null;
|
|
38
|
+
const [companionEphemeralKeyPair, salt, counter, codeBytes] = await Promise.all([
|
|
26
39
|
X25519.generateKeyPair(),
|
|
27
40
|
randomBytesAsync(32),
|
|
28
|
-
randomBytesAsync(16)
|
|
41
|
+
randomBytesAsync(16),
|
|
42
|
+
normalizedCustomCode !== null ? Promise.resolve(null) : randomBytesAsync(5)
|
|
29
43
|
]);
|
|
30
|
-
const pairingCode = bytesToCrockford(codeBytes);
|
|
44
|
+
const pairingCode = normalizedCustomCode ?? bytesToCrockford(codeBytes);
|
|
31
45
|
const cipher = await pbkdf2DeriveAesCtrKey(TEXT_ENCODER.encode(pairingCode), salt, PBKDF2_ITERATIONS);
|
|
32
46
|
const encrypted = await aesCtrEncrypt(cipher, counter, companionEphemeralKeyPair.pubKey);
|
|
33
47
|
return {
|