@zapo-js/fake-server 0.3.0 → 1.0.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 +3 -3
- package/dist/api/AppStateSyncManager.d.ts +0 -1
- package/dist/api/AppStateSyncManager.js +0 -1
- package/dist/api/FakePairingDriver.d.ts +0 -1
- package/dist/api/FakePairingDriver.js +0 -1
- package/dist/api/FakePeer.d.ts +77 -4
- package/dist/api/FakePeer.js +201 -1
- package/dist/api/FakeWaServer.d.ts +11 -1
- package/dist/api/FakeWaServer.js +34 -2
- package/dist/api/PreKeyDispenser.d.ts +0 -1
- package/dist/api/PreKeyDispenser.js +0 -1
- package/dist/api/Scenario.d.ts +0 -1
- package/dist/api/Scenario.js +0 -1
- package/dist/api/ServerRegistries.d.ts +0 -1
- package/dist/api/ServerRegistries.js +0 -1
- package/dist/api/iq-handlers.d.ts +0 -1
- package/dist/api/iq-handlers.js +0 -1
- package/dist/{cli.d.ts → bin.d.ts} +0 -1
- package/dist/{cli.js → bin.js} +1 -2
- package/dist/esm/api/AppStateSyncManager.js +146 -0
- package/dist/esm/api/FakePairingDriver.js +31 -0
- package/dist/esm/api/FakePeer.js +604 -0
- package/dist/esm/api/FakeWaServer.js +680 -0
- package/dist/esm/api/PreKeyDispenser.js +93 -0
- package/dist/esm/api/Scenario.js +32 -0
- package/dist/esm/api/ServerRegistries.js +261 -0
- package/dist/esm/api/iq-handlers.js +492 -0
- package/dist/esm/bin.js +246 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/infra/WaFakeConnection.js +49 -0
- package/dist/esm/infra/WaFakeConnectionPipeline.js +223 -0
- package/dist/esm/infra/WaFakeFrameSocket.js +117 -0
- package/dist/esm/infra/WaFakeMediaHttpsServer.js +126 -0
- package/dist/esm/infra/WaFakeNoiseHandshake.js +67 -0
- package/dist/esm/infra/WaFakeTransport.js +31 -0
- package/dist/esm/infra/WaFakeWsServer.js +96 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/protocol/auth/cert-chain.js +39 -0
- package/dist/esm/protocol/auth/client-payload-validate.js +55 -0
- package/dist/esm/protocol/auth/fake-primary-device.js +47 -0
- package/dist/esm/protocol/auth/pair-device.js +87 -0
- package/dist/esm/protocol/auth/success-node.js +26 -0
- package/dist/esm/protocol/iq/abprops.js +30 -0
- package/dist/esm/protocol/iq/appstate-sync.js +146 -0
- package/dist/esm/protocol/iq/business.js +59 -0
- package/dist/esm/protocol/iq/dirty-bits.js +19 -0
- package/dist/esm/protocol/iq/group-ops.js +154 -0
- package/dist/esm/protocol/iq/newsletter.js +14 -0
- package/dist/esm/protocol/iq/prekey-fetch.js +88 -0
- package/dist/esm/protocol/iq/privacy-token.js +26 -0
- package/dist/esm/protocol/iq/privacy.js +123 -0
- package/dist/esm/protocol/iq/profile.js +72 -0
- package/dist/esm/protocol/iq/router.js +111 -0
- package/dist/esm/protocol/iq/usync.js +64 -0
- package/dist/esm/protocol/push/app-state-key-share.js +26 -0
- package/dist/esm/protocol/push/call-failure.js +27 -0
- package/dist/esm/protocol/push/chatstate.js +23 -0
- package/dist/esm/protocol/push/error-stanza.js +13 -0
- package/dist/esm/protocol/push/history-sync.js +111 -0
- package/dist/esm/protocol/push/message.js +38 -0
- package/dist/esm/protocol/push/notification.js +29 -0
- package/dist/esm/protocol/push/presence.js +14 -0
- package/dist/esm/protocol/push/receipt.js +21 -0
- package/dist/esm/protocol/signal/fake-app-state-crypto.js +196 -0
- package/dist/esm/protocol/signal/fake-peer-double-ratchet.js +398 -0
- package/dist/esm/protocol/signal/fake-peer-group-recv-session.js +120 -0
- package/dist/esm/protocol/signal/fake-peer-identity.js +8 -0
- package/dist/esm/protocol/signal/fake-peer-key-bundle.js +25 -0
- package/dist/esm/protocol/signal/fake-sender-key.js +83 -0
- package/dist/esm/protocol/signal/prekey-upload.js +107 -0
- package/dist/esm/protocol/stream/stream-error.js +56 -0
- package/dist/esm/state/fake-app-state-collection.js +134 -0
- package/dist/esm/state/fake-media-store.js +53 -0
- package/dist/esm/transport/codec.js +8 -0
- package/dist/esm/transport/crypto.js +8 -0
- package/dist/esm/transport/protos.js +7 -0
- package/dist/esm/transport/util.js +8 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/WaFakeConnection.d.ts +0 -1
- package/dist/infra/WaFakeConnection.js +0 -1
- package/dist/infra/WaFakeConnectionPipeline.d.ts +0 -1
- package/dist/infra/WaFakeConnectionPipeline.js +27 -24
- package/dist/infra/WaFakeFrameSocket.d.ts +0 -1
- package/dist/infra/WaFakeFrameSocket.js +0 -1
- package/dist/infra/WaFakeMediaHttpsServer.d.ts +0 -1
- package/dist/infra/WaFakeMediaHttpsServer.js +0 -1
- package/dist/infra/WaFakeNoiseHandshake.d.ts +8 -10
- package/dist/infra/WaFakeNoiseHandshake.js +19 -24
- package/dist/infra/WaFakeTransport.d.ts +2 -4
- package/dist/infra/WaFakeTransport.js +2 -1
- package/dist/infra/WaFakeWsServer.d.ts +0 -1
- package/dist/infra/WaFakeWsServer.js +0 -1
- package/dist/protocol/auth/cert-chain.d.ts +0 -1
- package/dist/protocol/auth/cert-chain.js +0 -1
- package/dist/protocol/auth/client-payload-validate.d.ts +0 -1
- package/dist/protocol/auth/client-payload-validate.js +0 -1
- package/dist/protocol/auth/fake-primary-device.d.ts +0 -1
- package/dist/protocol/auth/fake-primary-device.js +1 -3
- package/dist/protocol/auth/pair-device.d.ts +0 -1
- package/dist/protocol/auth/pair-device.js +4 -16
- package/dist/protocol/auth/success-node.d.ts +0 -1
- package/dist/protocol/auth/success-node.js +0 -1
- package/dist/protocol/iq/abprops.d.ts +0 -1
- package/dist/protocol/iq/abprops.js +0 -1
- package/dist/protocol/iq/appstate-sync.d.ts +0 -1
- package/dist/protocol/iq/appstate-sync.js +0 -1
- package/dist/protocol/iq/business.d.ts +0 -1
- package/dist/protocol/iq/business.js +0 -1
- package/dist/protocol/iq/dirty-bits.d.ts +0 -1
- package/dist/protocol/iq/dirty-bits.js +0 -1
- package/dist/protocol/iq/group-ops.d.ts +0 -1
- package/dist/protocol/iq/group-ops.js +0 -1
- package/dist/protocol/iq/newsletter.d.ts +0 -1
- package/dist/protocol/iq/newsletter.js +0 -1
- package/dist/protocol/iq/prekey-fetch.d.ts +0 -1
- package/dist/protocol/iq/prekey-fetch.js +0 -1
- package/dist/protocol/iq/privacy-token.d.ts +0 -1
- package/dist/protocol/iq/privacy-token.js +0 -1
- package/dist/protocol/iq/privacy.d.ts +0 -1
- package/dist/protocol/iq/privacy.js +15 -16
- package/dist/protocol/iq/profile.d.ts +0 -1
- package/dist/protocol/iq/profile.js +0 -1
- package/dist/protocol/iq/router.d.ts +0 -1
- package/dist/protocol/iq/router.js +0 -1
- package/dist/protocol/iq/usync.d.ts +0 -1
- package/dist/protocol/iq/usync.js +0 -1
- package/dist/protocol/push/app-state-key-share.d.ts +0 -1
- package/dist/protocol/push/app-state-key-share.js +0 -1
- package/dist/protocol/push/call-failure.d.ts +0 -1
- package/dist/protocol/push/call-failure.js +0 -1
- package/dist/protocol/push/chatstate.d.ts +0 -1
- package/dist/protocol/push/chatstate.js +0 -1
- package/dist/protocol/push/error-stanza.d.ts +0 -1
- package/dist/protocol/push/error-stanza.js +0 -1
- package/dist/protocol/push/history-sync.d.ts +0 -1
- package/dist/protocol/push/history-sync.js +0 -1
- package/dist/protocol/push/message.d.ts +0 -1
- package/dist/protocol/push/message.js +0 -1
- package/dist/protocol/push/notification.d.ts +0 -1
- package/dist/protocol/push/notification.js +0 -1
- package/dist/protocol/push/presence.d.ts +0 -1
- package/dist/protocol/push/presence.js +0 -1
- package/dist/protocol/push/receipt.d.ts +0 -1
- package/dist/protocol/push/receipt.js +0 -1
- package/dist/protocol/signal/fake-app-state-crypto.d.ts +7 -9
- package/dist/protocol/signal/fake-app-state-crypto.js +31 -33
- package/dist/protocol/signal/fake-peer-double-ratchet.d.ts +0 -1
- package/dist/protocol/signal/fake-peer-double-ratchet.js +23 -28
- package/dist/protocol/signal/fake-peer-group-recv-session.d.ts +0 -1
- package/dist/protocol/signal/fake-peer-group-recv-session.js +6 -10
- package/dist/protocol/signal/fake-peer-identity.d.ts +0 -1
- package/dist/protocol/signal/fake-peer-identity.js +0 -1
- package/dist/protocol/signal/fake-peer-key-bundle.d.ts +0 -1
- package/dist/protocol/signal/fake-peer-key-bundle.js +0 -1
- package/dist/protocol/signal/fake-sender-key.d.ts +0 -1
- package/dist/protocol/signal/fake-sender-key.js +5 -9
- package/dist/protocol/signal/prekey-upload.d.ts +0 -1
- package/dist/protocol/signal/prekey-upload.js +0 -1
- package/dist/protocol/stream/stream-error.d.ts +0 -1
- package/dist/protocol/stream/stream-error.js +0 -1
- package/dist/state/fake-app-state-collection.d.ts +0 -1
- package/dist/state/fake-app-state-collection.js +0 -1
- package/dist/state/fake-media-store.d.ts +0 -1
- package/dist/state/fake-media-store.js +2 -2
- package/dist/transport/codec.d.ts +2 -3
- package/dist/transport/codec.js +2 -3
- package/dist/transport/crypto.d.ts +5 -7
- package/dist/transport/crypto.js +6 -11
- package/dist/transport/protos.d.ts +2 -4
- package/dist/transport/protos.js +2 -4
- package/dist/transport/util.d.ts +8 -0
- package/dist/transport/util.js +15 -0
- package/package.json +21 -6
- package/dist/api/AppStateSyncManager.d.ts.map +0 -1
- package/dist/api/AppStateSyncManager.js.map +0 -1
- package/dist/api/FakePairingDriver.d.ts.map +0 -1
- package/dist/api/FakePairingDriver.js.map +0 -1
- package/dist/api/FakePeer.d.ts.map +0 -1
- package/dist/api/FakePeer.js.map +0 -1
- package/dist/api/FakeWaServer.d.ts.map +0 -1
- package/dist/api/FakeWaServer.js.map +0 -1
- package/dist/api/PreKeyDispenser.d.ts.map +0 -1
- package/dist/api/PreKeyDispenser.js.map +0 -1
- package/dist/api/Scenario.d.ts.map +0 -1
- package/dist/api/Scenario.js.map +0 -1
- package/dist/api/ServerRegistries.d.ts.map +0 -1
- package/dist/api/ServerRegistries.js.map +0 -1
- package/dist/api/iq-handlers.d.ts.map +0 -1
- package/dist/api/iq-handlers.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/infra/WaFakeConnection.d.ts.map +0 -1
- package/dist/infra/WaFakeConnection.js.map +0 -1
- package/dist/infra/WaFakeConnectionPipeline.d.ts.map +0 -1
- package/dist/infra/WaFakeConnectionPipeline.js.map +0 -1
- package/dist/infra/WaFakeFrameSocket.d.ts.map +0 -1
- package/dist/infra/WaFakeFrameSocket.js.map +0 -1
- package/dist/infra/WaFakeMediaHttpsServer.d.ts.map +0 -1
- package/dist/infra/WaFakeMediaHttpsServer.js.map +0 -1
- package/dist/infra/WaFakeNoiseHandshake.d.ts.map +0 -1
- package/dist/infra/WaFakeNoiseHandshake.js.map +0 -1
- package/dist/infra/WaFakeTransport.d.ts.map +0 -1
- package/dist/infra/WaFakeTransport.js.map +0 -1
- package/dist/infra/WaFakeWsServer.d.ts.map +0 -1
- package/dist/infra/WaFakeWsServer.js.map +0 -1
- package/dist/protocol/auth/cert-chain.d.ts.map +0 -1
- package/dist/protocol/auth/cert-chain.js.map +0 -1
- package/dist/protocol/auth/client-payload-validate.d.ts.map +0 -1
- package/dist/protocol/auth/client-payload-validate.js.map +0 -1
- package/dist/protocol/auth/fake-primary-device.d.ts.map +0 -1
- package/dist/protocol/auth/fake-primary-device.js.map +0 -1
- package/dist/protocol/auth/pair-device.d.ts.map +0 -1
- package/dist/protocol/auth/pair-device.js.map +0 -1
- package/dist/protocol/auth/success-node.d.ts.map +0 -1
- package/dist/protocol/auth/success-node.js.map +0 -1
- package/dist/protocol/iq/abprops.d.ts.map +0 -1
- package/dist/protocol/iq/abprops.js.map +0 -1
- package/dist/protocol/iq/appstate-sync.d.ts.map +0 -1
- package/dist/protocol/iq/appstate-sync.js.map +0 -1
- package/dist/protocol/iq/business.d.ts.map +0 -1
- package/dist/protocol/iq/business.js.map +0 -1
- package/dist/protocol/iq/dirty-bits.d.ts.map +0 -1
- package/dist/protocol/iq/dirty-bits.js.map +0 -1
- package/dist/protocol/iq/group-ops.d.ts.map +0 -1
- package/dist/protocol/iq/group-ops.js.map +0 -1
- package/dist/protocol/iq/newsletter.d.ts.map +0 -1
- package/dist/protocol/iq/newsletter.js.map +0 -1
- package/dist/protocol/iq/prekey-fetch.d.ts.map +0 -1
- package/dist/protocol/iq/prekey-fetch.js.map +0 -1
- package/dist/protocol/iq/privacy-token.d.ts.map +0 -1
- package/dist/protocol/iq/privacy-token.js.map +0 -1
- package/dist/protocol/iq/privacy.d.ts.map +0 -1
- package/dist/protocol/iq/privacy.js.map +0 -1
- package/dist/protocol/iq/profile.d.ts.map +0 -1
- package/dist/protocol/iq/profile.js.map +0 -1
- package/dist/protocol/iq/router.d.ts.map +0 -1
- package/dist/protocol/iq/router.js.map +0 -1
- package/dist/protocol/iq/usync.d.ts.map +0 -1
- package/dist/protocol/iq/usync.js.map +0 -1
- package/dist/protocol/push/app-state-key-share.d.ts.map +0 -1
- package/dist/protocol/push/app-state-key-share.js.map +0 -1
- package/dist/protocol/push/call-failure.d.ts.map +0 -1
- package/dist/protocol/push/call-failure.js.map +0 -1
- package/dist/protocol/push/chatstate.d.ts.map +0 -1
- package/dist/protocol/push/chatstate.js.map +0 -1
- package/dist/protocol/push/error-stanza.d.ts.map +0 -1
- package/dist/protocol/push/error-stanza.js.map +0 -1
- package/dist/protocol/push/history-sync.d.ts.map +0 -1
- package/dist/protocol/push/history-sync.js.map +0 -1
- package/dist/protocol/push/message.d.ts.map +0 -1
- package/dist/protocol/push/message.js.map +0 -1
- package/dist/protocol/push/notification.d.ts.map +0 -1
- package/dist/protocol/push/notification.js.map +0 -1
- package/dist/protocol/push/presence.d.ts.map +0 -1
- package/dist/protocol/push/presence.js.map +0 -1
- package/dist/protocol/push/receipt.d.ts.map +0 -1
- package/dist/protocol/push/receipt.js.map +0 -1
- package/dist/protocol/signal/fake-app-state-crypto.d.ts.map +0 -1
- package/dist/protocol/signal/fake-app-state-crypto.js.map +0 -1
- package/dist/protocol/signal/fake-peer-double-ratchet.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-double-ratchet.js.map +0 -1
- package/dist/protocol/signal/fake-peer-group-recv-session.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-group-recv-session.js.map +0 -1
- package/dist/protocol/signal/fake-peer-identity.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-identity.js.map +0 -1
- package/dist/protocol/signal/fake-peer-key-bundle.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-key-bundle.js.map +0 -1
- package/dist/protocol/signal/fake-peer-recv-session.d.ts +0 -60
- package/dist/protocol/signal/fake-peer-recv-session.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-recv-session.js +0 -284
- package/dist/protocol/signal/fake-peer-recv-session.js.map +0 -1
- package/dist/protocol/signal/fake-peer-session.d.ts +0 -87
- package/dist/protocol/signal/fake-peer-session.d.ts.map +0 -1
- package/dist/protocol/signal/fake-peer-session.js +0 -207
- package/dist/protocol/signal/fake-peer-session.js.map +0 -1
- package/dist/protocol/signal/fake-sender-key.d.ts.map +0 -1
- package/dist/protocol/signal/fake-sender-key.js.map +0 -1
- package/dist/protocol/signal/prekey-upload.d.ts.map +0 -1
- package/dist/protocol/signal/prekey-upload.js.map +0 -1
- package/dist/protocol/stream/stream-error.d.ts.map +0 -1
- package/dist/protocol/stream/stream-error.js.map +0 -1
- package/dist/state/fake-app-state-collection.d.ts.map +0 -1
- package/dist/state/fake-app-state-collection.js.map +0 -1
- package/dist/state/fake-media-store.d.ts.map +0 -1
- package/dist/state/fake-media-store.js.map +0 -1
- package/dist/transport/codec.d.ts.map +0 -1
- package/dist/transport/codec.js.map +0 -1
- package/dist/transport/crypto.d.ts.map +0 -1
- package/dist/transport/crypto.js.map +0 -1
- package/dist/transport/index.d.ts +0 -4
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js +0 -20
- package/dist/transport/index.js.map +0 -1
- package/dist/transport/protos.d.ts.map +0 -1
- package/dist/transport/protos.js.map +0 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** HTTPS listener used by fake media upload/download flows. */
|
|
2
|
+
import { createServer as createHttpsServer } from 'node:https';
|
|
3
|
+
const FAKE_MEDIA_TLS_KEY_PEM = `-----BEGIN PRIVATE KEY-----
|
|
4
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3xTRqB/wZbz/w
|
|
5
|
+
pQgcOfCfizS0MrtVR0ulBa1vK4OlU1FXJNC0dtTKLs/jMWNqwYWXxSoGEHHfhUIS
|
|
6
|
+
PgZ08nauCJs83ZFAQC1Es1sN1R32YokPmKOCTHHh8f5/uX3h5oUD/xr3gwOVs4c2
|
|
7
|
+
ISmk1OTmp77KolxZXHOnlnHweO6DK0CqfT+53RzKj5rz4d7inyMk5ctk+JjHZd8e
|
|
8
|
+
q46e4MH4ZWPM+ehwRwgWMQ2E1bFjVQsBMvtAkUxOEx+B2/Cys6jEUFo/ABkQxXt+
|
|
9
|
+
InqWnYKHbPOToKmpPxDjT54OpVZs4IRtbkTrgEMMz3/c5g4XamGUHLqkI0GBESK2
|
|
10
|
+
16STZettAgMBAAECggEAAr6CDGOfMMzRkdyeWyKosPKMIGbY+wNiSSbsabim4BO3
|
|
11
|
+
LkLQgSK1d3fF3+KrDj6/DUJUti2/oZEKGoKwUn6T14BmV+7qvjX1WN8kqNsGYLXP
|
|
12
|
+
XFB0jinpkf6XhPKUn7olCGdwglgR28XknLBXsmG6eMK5ODgiZi0Zqng9WPCSP3Aa
|
|
13
|
+
5q7iSeJF0FsbQ1OdQT3t98WhnSN9t0TzLyNUqXWDBDIrrtT3O7B7theS+3LRhodE
|
|
14
|
+
VFROogp4hfo6pBNweki08uFD250OP6iGpYsE0xY6NIfyebGS2iBmbDKQmMkWLdf0
|
|
15
|
+
mSlvYoksnDan4tJMiXAbb39wLdJEa+x98Qzxq0iJyQKBgQDeBQSgVd7j99W0bdzo
|
|
16
|
+
fnDgnjJS606UrWID/RyTEIMfq5Jf2sM2vTJ2/w4X1JoUxxQCJbFmoJs9RXn52XUa
|
|
17
|
+
3/GaUbr3akUVjMb93JlbWTQVoKipMM6HGCee4rj69qdsmA8iJrnloGFpymLThcfm
|
|
18
|
+
xZB535vbG3DrYRM8MH/crDQH2QKBgQDT5YlXskOxx/iVxpko4uacIBqHnPGbCPnK
|
|
19
|
+
LXMaa5vrjGgWNK0ajj+0MFdpEl+HXC7ZDISmSRKExrMDaWkjoXzXhxz1BmHVdN3O
|
|
20
|
+
lOZY6gY+WeQUPn2sfqROBAV7B66LmRjbLdK9kwqrD3nhED6XWR+ZuXaH0dMQ2UMe
|
|
21
|
+
T9E1ixr3tQKBgQChrYvR6taWsosYioy9bh5rJCjHg33E/YIMH41odzTVok7EqP1R
|
|
22
|
+
5nNSfqhXqEXQbazfZ22Jq9mAxBBwZvBFcuFxHKWHuQa4C715buTqxcoNGeLY2qb7
|
|
23
|
+
cBaiOL62W1pO1Wjn2MW1N5bYwD45hQmuvx5X8gOAirovXpDwWu0x2OFG+QKBgAxE
|
|
24
|
+
U4kiJs0Z+IgjMSauzA7pxN9o9Iu3H07XBrzW0fX28OeoMQVCiumRit3oVGNvsL6b
|
|
25
|
+
/OnrfQj2v9JIve90H9gSWFjO/8bttxGIiTVIhwgBCDUr0Pa1dimWDgMrVK18NXYq
|
|
26
|
+
1vJmms2AXdvrptP4Mt4hYH1IZmGpUjtk/4WMAgmpAoGAKJXzDPY5ll9Ebjz28u3W
|
|
27
|
+
sPZdIz90rpRIBnyDC/AwtEzliU2PkuGpqe1ZJaP6cmPTxf/Bv8hfUMXknURpOShE
|
|
28
|
+
Uusyt3/4dGRMY+XXKuTPMZA8sbIEatA2z2e6oBoyj9avzEKAwDsReuPyyX6BubTx
|
|
29
|
+
rLSyj/PpCngrQw7YXsQc6Wc=
|
|
30
|
+
-----END PRIVATE KEY-----
|
|
31
|
+
`;
|
|
32
|
+
const FAKE_MEDIA_TLS_CERT_PEM = `-----BEGIN CERTIFICATE-----
|
|
33
|
+
MIIDGTCCAgGgAwIBAgIUCb8Qbq/uzK9W14G057lmny5p4kIwDQYJKoZIhvcNAQEL
|
|
34
|
+
BQAwGzEZMBcGA1UEAwwQZmFrZS1tZWRpYS5sb2NhbDAgFw0yNjA0MDgyMjAzMDRa
|
|
35
|
+
GA8yMTI2MDMxNTIyMDMwNFowGzEZMBcGA1UEAwwQZmFrZS1tZWRpYS5sb2NhbDCC
|
|
36
|
+
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALfFNGoH/BlvP/ClCBw58J+L
|
|
37
|
+
NLQyu1VHS6UFrW8rg6VTUVck0LR21Mouz+MxY2rBhZfFKgYQcd+FQhI+BnTydq4I
|
|
38
|
+
mzzdkUBALUSzWw3VHfZiiQ+Yo4JMceHx/n+5feHmhQP/GveDA5WzhzYhKaTU5Oan
|
|
39
|
+
vsqiXFlcc6eWcfB47oMrQKp9P7ndHMqPmvPh3uKfIyTly2T4mMdl3x6rjp7gwfhl
|
|
40
|
+
Y8z56HBHCBYxDYTVsWNVCwEy+0CRTE4TH4Hb8LKzqMRQWj8AGRDFe34iepadgods
|
|
41
|
+
85Ogqak/EONPng6lVmzghG1uROuAQwzPf9zmDhdqYZQcuqQjQYERIrbXpJNl620C
|
|
42
|
+
AwEAAaNTMFEwHQYDVR0OBBYEFG3DX82mLaatjTwwspHIj2pOD8CnMB8GA1UdIwQY
|
|
43
|
+
MBaAFG3DX82mLaatjTwwspHIj2pOD8CnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
|
44
|
+
hvcNAQELBQADggEBAEqIu6mXgvSrGyQxRp6cJVg5OhBpwxH/V0IFe2H7xPBxreDS
|
|
45
|
+
e9yb/ESLRFxpfeWhLXOBEsOGbrF8HvZ0zNTHW+MtIkJB/hhYVzDJyhEpf9jWvpaM
|
|
46
|
+
H6CyzWIZgrXMmyeh0WkmVWoYyvlQrz9yJP63VfzCTCO6YBo02S+kKAyJiM9l8ZW5
|
|
47
|
+
8vALhsyrGSMDnxQSixdc4L/Migam1LHxqwApzDvTdfXNiqjWEmfbw7sQKjd8EHad
|
|
48
|
+
KxNzgzzCRBSkeLe9YoXxLkKRvYeBM/xiv0AVp/ABedHj0xj3ybvvdjowui1uCBip
|
|
49
|
+
BoMF3OiuOeaTubj+tDzpZcauS4L+OyUPjsKqFoM=
|
|
50
|
+
-----END CERTIFICATE-----
|
|
51
|
+
`;
|
|
52
|
+
export class WaFakeMediaHttpsServer {
|
|
53
|
+
constructor() {
|
|
54
|
+
this.server = null;
|
|
55
|
+
this.listenInfo = null;
|
|
56
|
+
this.requestHandler = null;
|
|
57
|
+
}
|
|
58
|
+
setRequestHandler(handler) {
|
|
59
|
+
this.requestHandler = handler;
|
|
60
|
+
}
|
|
61
|
+
async listen(host = '127.0.0.1', port = 0) {
|
|
62
|
+
if (this.server) {
|
|
63
|
+
throw new Error('fake media https server is already listening');
|
|
64
|
+
}
|
|
65
|
+
const server = createHttpsServer({
|
|
66
|
+
key: FAKE_MEDIA_TLS_KEY_PEM,
|
|
67
|
+
cert: FAKE_MEDIA_TLS_CERT_PEM
|
|
68
|
+
}, (req, res) => {
|
|
69
|
+
const handler = this.requestHandler;
|
|
70
|
+
if (!handler) {
|
|
71
|
+
res.statusCode = 404;
|
|
72
|
+
res.end();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const result = handler(req, res);
|
|
77
|
+
if (result instanceof Promise) {
|
|
78
|
+
result.catch((error) => {
|
|
79
|
+
if (!res.headersSent) {
|
|
80
|
+
res.statusCode = 500;
|
|
81
|
+
}
|
|
82
|
+
res.end(error instanceof Error ? error.message : String(error));
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!res.headersSent) {
|
|
88
|
+
res.statusCode = 500;
|
|
89
|
+
}
|
|
90
|
+
res.end(error instanceof Error ? error.message : String(error));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
await new Promise((resolve, reject) => {
|
|
94
|
+
const onError = (error) => {
|
|
95
|
+
server.off('listening', onListening);
|
|
96
|
+
reject(error);
|
|
97
|
+
};
|
|
98
|
+
const onListening = () => {
|
|
99
|
+
server.off('error', onError);
|
|
100
|
+
resolve();
|
|
101
|
+
};
|
|
102
|
+
server.once('error', onError);
|
|
103
|
+
server.once('listening', onListening);
|
|
104
|
+
server.listen(port, host);
|
|
105
|
+
});
|
|
106
|
+
const address = server.address();
|
|
107
|
+
this.server = server;
|
|
108
|
+
this.listenInfo = { host: address.address, port: address.port };
|
|
109
|
+
return this.listenInfo;
|
|
110
|
+
}
|
|
111
|
+
async close() {
|
|
112
|
+
const server = this.server;
|
|
113
|
+
if (!server)
|
|
114
|
+
return;
|
|
115
|
+
await new Promise((resolve, reject) => {
|
|
116
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
117
|
+
});
|
|
118
|
+
// Force any keep-alive sockets to close so the test process can exit.
|
|
119
|
+
server.closeAllConnections?.();
|
|
120
|
+
this.server = null;
|
|
121
|
+
this.listenInfo = null;
|
|
122
|
+
}
|
|
123
|
+
get info() {
|
|
124
|
+
return this.listenInfo;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Responder-side Noise handshake primitives for fake server pipelines. */
|
|
2
|
+
import { aesGcmDecrypt, aesGcmEncrypt, hkdfSplit, sha256 } from '../transport/crypto.js';
|
|
3
|
+
const EMPTY = new Uint8Array(0);
|
|
4
|
+
export class WaFakeNoiseHandshake {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.hash = EMPTY;
|
|
7
|
+
this.chainingKey = EMPTY;
|
|
8
|
+
this.cipherKey = null;
|
|
9
|
+
this.nonceCounter = 0;
|
|
10
|
+
}
|
|
11
|
+
start(name, prologue) {
|
|
12
|
+
const initialHash = name.byteLength === 32 ? name : sha256(name);
|
|
13
|
+
this.hash = initialHash;
|
|
14
|
+
this.chainingKey = initialHash;
|
|
15
|
+
this.cipherKey = initialHash;
|
|
16
|
+
this.nonceCounter = 0;
|
|
17
|
+
this.authenticate(prologue);
|
|
18
|
+
}
|
|
19
|
+
authenticate(data) {
|
|
20
|
+
const concatenated = new Uint8Array(this.hash.byteLength + data.byteLength);
|
|
21
|
+
concatenated.set(this.hash, 0);
|
|
22
|
+
concatenated.set(data, this.hash.byteLength);
|
|
23
|
+
this.hash = sha256(concatenated);
|
|
24
|
+
}
|
|
25
|
+
mixIntoKey(input) {
|
|
26
|
+
const [nextChainingKey, nextCipherMaterial] = hkdfSplit(input, this.chainingKey, EMPTY);
|
|
27
|
+
this.chainingKey = nextChainingKey;
|
|
28
|
+
this.cipherKey = nextCipherMaterial;
|
|
29
|
+
this.nonceCounter = 0;
|
|
30
|
+
}
|
|
31
|
+
encrypt(plaintext) {
|
|
32
|
+
if (!this.cipherKey) {
|
|
33
|
+
throw new Error('noise handshake encrypt called before key was derived');
|
|
34
|
+
}
|
|
35
|
+
const iv = this.buildNonceIv();
|
|
36
|
+
this.nonceCounter += 1;
|
|
37
|
+
const ciphertext = aesGcmEncrypt(this.cipherKey, iv, plaintext, this.hash);
|
|
38
|
+
this.authenticate(ciphertext);
|
|
39
|
+
return ciphertext;
|
|
40
|
+
}
|
|
41
|
+
decrypt(ciphertext) {
|
|
42
|
+
if (!this.cipherKey) {
|
|
43
|
+
throw new Error('noise handshake decrypt called before key was derived');
|
|
44
|
+
}
|
|
45
|
+
const iv = this.buildNonceIv();
|
|
46
|
+
this.nonceCounter += 1;
|
|
47
|
+
const plaintext = aesGcmDecrypt(this.cipherKey, iv, ciphertext, this.hash);
|
|
48
|
+
this.authenticate(ciphertext);
|
|
49
|
+
return plaintext;
|
|
50
|
+
}
|
|
51
|
+
/** Derives transport keys (`first -> recv`, `second -> send` for responder role). */
|
|
52
|
+
finish() {
|
|
53
|
+
const [first, second] = hkdfSplit(EMPTY, this.chainingKey, EMPTY);
|
|
54
|
+
return {
|
|
55
|
+
recvKey: first,
|
|
56
|
+
sendKey: second,
|
|
57
|
+
recvKeyBytes: first,
|
|
58
|
+
sendKeyBytes: second
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
buildNonceIv() {
|
|
62
|
+
const iv = new Uint8Array(12);
|
|
63
|
+
const view = new DataView(iv.buffer);
|
|
64
|
+
view.setUint32(8, this.nonceCounter, false);
|
|
65
|
+
return iv;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Post-handshake Noise transport (AES-GCM with direction-specific counters). */
|
|
2
|
+
import { aesGcmDecrypt, aesGcmEncrypt } from '../transport/crypto.js';
|
|
3
|
+
export class WaFakeTransport {
|
|
4
|
+
constructor(keys) {
|
|
5
|
+
this.sendCounter = 0;
|
|
6
|
+
this.recvCounter = 0;
|
|
7
|
+
this.recvKey = keys.recvKey;
|
|
8
|
+
this.sendKey = keys.sendKey;
|
|
9
|
+
}
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
11
|
+
async encryptFrame(plaintext) {
|
|
12
|
+
const nonce = buildAesGcmNonce(this.sendCounter);
|
|
13
|
+
this.sendCounter += 1;
|
|
14
|
+
return aesGcmEncrypt(this.sendKey, nonce, plaintext);
|
|
15
|
+
}
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
17
|
+
async decryptFrame(ciphertext) {
|
|
18
|
+
const nonce = buildAesGcmNonce(this.recvCounter);
|
|
19
|
+
this.recvCounter += 1;
|
|
20
|
+
return aesGcmDecrypt(this.recvKey, nonce, ciphertext);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function buildAesGcmNonce(counter) {
|
|
24
|
+
if (counter > 0xffffffff) {
|
|
25
|
+
throw new Error('noise transport nonce counter overflow');
|
|
26
|
+
}
|
|
27
|
+
const nonce = new Uint8Array(12);
|
|
28
|
+
const view = new DataView(nonce.buffer);
|
|
29
|
+
view.setUint32(8, counter, false);
|
|
30
|
+
return nonce;
|
|
31
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import { WaFakeConnection } from './WaFakeConnection.js';
|
|
4
|
+
export class WaFakeWsServer {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.httpServer = null;
|
|
7
|
+
this.wsServer = null;
|
|
8
|
+
this.connectionListener = null;
|
|
9
|
+
this.httpRequestHandler = null;
|
|
10
|
+
this.nextConnectionId = 0;
|
|
11
|
+
this.options = {
|
|
12
|
+
host: options.host ?? '127.0.0.1',
|
|
13
|
+
port: options.port ?? 0,
|
|
14
|
+
path: options.path ?? '/ws/chat'
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
onConnection(listener) {
|
|
18
|
+
this.connectionListener = listener;
|
|
19
|
+
}
|
|
20
|
+
setHttpRequestHandler(handler) {
|
|
21
|
+
this.httpRequestHandler = handler;
|
|
22
|
+
}
|
|
23
|
+
async listen() {
|
|
24
|
+
if (this.httpServer) {
|
|
25
|
+
throw new Error('fake ws server is already listening');
|
|
26
|
+
}
|
|
27
|
+
const httpServer = createServer(async (req, res) => {
|
|
28
|
+
const handler = this.httpRequestHandler;
|
|
29
|
+
if (!handler) {
|
|
30
|
+
res.statusCode = 404;
|
|
31
|
+
res.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
await Promise.resolve(handler(req, res)).catch((error) => {
|
|
36
|
+
if (!res.headersSent) {
|
|
37
|
+
res.statusCode = 500;
|
|
38
|
+
}
|
|
39
|
+
res.end(error instanceof Error ? error.message : String(error));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (!res.headersSent) {
|
|
44
|
+
res.statusCode = 500;
|
|
45
|
+
}
|
|
46
|
+
res.end(error instanceof Error ? error.message : String(error));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const wsServer = new WebSocketServer({ server: httpServer, path: this.options.path });
|
|
50
|
+
wsServer.on('connection', (socket) => {
|
|
51
|
+
const id = `c${this.nextConnectionId++}`;
|
|
52
|
+
const connection = new WaFakeConnection(id, socket);
|
|
53
|
+
this.connectionListener?.(connection);
|
|
54
|
+
});
|
|
55
|
+
await new Promise((resolve, reject) => {
|
|
56
|
+
const onError = (error) => {
|
|
57
|
+
httpServer.off('listening', onListening);
|
|
58
|
+
reject(error);
|
|
59
|
+
};
|
|
60
|
+
const onListening = () => {
|
|
61
|
+
httpServer.off('error', onError);
|
|
62
|
+
resolve();
|
|
63
|
+
};
|
|
64
|
+
httpServer.once('error', onError);
|
|
65
|
+
httpServer.once('listening', onListening);
|
|
66
|
+
httpServer.listen(this.options.port, this.options.host);
|
|
67
|
+
});
|
|
68
|
+
this.httpServer = httpServer;
|
|
69
|
+
this.wsServer = wsServer;
|
|
70
|
+
const address = httpServer.address();
|
|
71
|
+
return {
|
|
72
|
+
host: address.address,
|
|
73
|
+
port: address.port,
|
|
74
|
+
path: this.options.path,
|
|
75
|
+
url: `ws://${address.address}:${address.port}${this.options.path}`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async close() {
|
|
79
|
+
const wsServer = this.wsServer;
|
|
80
|
+
const httpServer = this.httpServer;
|
|
81
|
+
if (!wsServer || !httpServer) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for (const client of wsServer.clients) {
|
|
85
|
+
client.terminate();
|
|
86
|
+
}
|
|
87
|
+
await new Promise((resolve, reject) => {
|
|
88
|
+
wsServer.close((error) => (error ? reject(error) : resolve()));
|
|
89
|
+
});
|
|
90
|
+
await new Promise((resolve, reject) => {
|
|
91
|
+
httpServer.close((error) => (error ? reject(error) : resolve()));
|
|
92
|
+
});
|
|
93
|
+
this.wsServer = null;
|
|
94
|
+
this.httpServer = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Fake noise certificate chain generator for handshake tests. */
|
|
2
|
+
import { X25519, xeddsaSign } from '../../transport/crypto.js';
|
|
3
|
+
import { proto } from '../../transport/protos.js';
|
|
4
|
+
export async function generateFakeNoiseRootCa() {
|
|
5
|
+
const keyPair = await X25519.generateKeyPair();
|
|
6
|
+
return {
|
|
7
|
+
publicKey: keyPair.pubKey,
|
|
8
|
+
privateKey: keyPair.privKey,
|
|
9
|
+
serial: 0
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export async function buildFakeCertChain(input) {
|
|
13
|
+
const intermediateSerial = input.intermediateSerial ?? 1;
|
|
14
|
+
const leafSerial = input.leafSerial ?? 2;
|
|
15
|
+
const intermediateKeyPair = await X25519.generateKeyPair();
|
|
16
|
+
const intermediateDetails = proto.CertChain.NoiseCertificate.Details.encode({
|
|
17
|
+
serial: intermediateSerial,
|
|
18
|
+
issuerSerial: input.root.serial,
|
|
19
|
+
key: intermediateKeyPair.pubKey
|
|
20
|
+
}).finish();
|
|
21
|
+
const intermediateSignature = await xeddsaSign(input.root.privateKey, intermediateDetails);
|
|
22
|
+
const leafDetails = proto.CertChain.NoiseCertificate.Details.encode({
|
|
23
|
+
serial: leafSerial,
|
|
24
|
+
issuerSerial: intermediateSerial,
|
|
25
|
+
key: input.leafKey
|
|
26
|
+
}).finish();
|
|
27
|
+
const leafSignature = await xeddsaSign(intermediateKeyPair.privKey, leafDetails);
|
|
28
|
+
const encoded = proto.CertChain.encode({
|
|
29
|
+
intermediate: {
|
|
30
|
+
details: intermediateDetails,
|
|
31
|
+
signature: intermediateSignature
|
|
32
|
+
},
|
|
33
|
+
leaf: {
|
|
34
|
+
details: leafDetails,
|
|
35
|
+
signature: leafSignature
|
|
36
|
+
}
|
|
37
|
+
}).finish();
|
|
38
|
+
return { encoded };
|
|
39
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** ClientPayload parser/validator (registration vs login flavors). */
|
|
2
|
+
import { proto } from '../../transport/protos.js';
|
|
3
|
+
export class ClientPayloadValidationError extends Error {
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'ClientPayloadValidationError';
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function parseClientPayload(bytes) {
|
|
11
|
+
let raw;
|
|
12
|
+
try {
|
|
13
|
+
raw = proto.ClientPayload.decode(bytes);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw new ClientPayloadValidationError('invalid_proto', `failed to decode ClientPayload: ${error.message}`);
|
|
17
|
+
}
|
|
18
|
+
const devicePairingData = raw.devicePairingData;
|
|
19
|
+
if (devicePairingData) {
|
|
20
|
+
validateRegistrationFields(devicePairingData);
|
|
21
|
+
return {
|
|
22
|
+
kind: 'registration',
|
|
23
|
+
raw,
|
|
24
|
+
devicePairingData
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (raw.username === undefined || raw.username === null) {
|
|
28
|
+
throw new ClientPayloadValidationError('missing_username', 'login ClientPayload is missing the username field');
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
kind: 'login',
|
|
32
|
+
raw,
|
|
33
|
+
username: String(raw.username),
|
|
34
|
+
device: typeof raw.device === 'number' ? raw.device : 0,
|
|
35
|
+
loginCounter: typeof raw.lc === 'number' ? raw.lc : 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function validateRegistrationFields(devicePairingData) {
|
|
39
|
+
const required = [
|
|
40
|
+
['eIdent', 'identity public key'],
|
|
41
|
+
['eRegid', 'registration id'],
|
|
42
|
+
['eKeytype', 'key type marker'],
|
|
43
|
+
['eSkeyId', 'signed pre-key id'],
|
|
44
|
+
['eSkeyVal', 'signed pre-key public'],
|
|
45
|
+
['eSkeySig', 'signed pre-key signature'],
|
|
46
|
+
['buildHash', 'client build hash'],
|
|
47
|
+
['deviceProps', 'device properties']
|
|
48
|
+
];
|
|
49
|
+
for (const [field, label] of required) {
|
|
50
|
+
const value = devicePairingData[field];
|
|
51
|
+
if (value === undefined || value === null) {
|
|
52
|
+
throw new ClientPayloadValidationError('missing_field', `registration ClientPayload missing ${label} (devicePairingData.${String(field)})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Fake primary-device identity/signature helpers for pairing. */
|
|
2
|
+
import { hmacSha256Sign, X25519, xeddsaSign } from '../../transport/crypto.js';
|
|
3
|
+
import { proto } from '../../transport/protos.js';
|
|
4
|
+
const ADV_PREFIX_ACCOUNT_SIGNATURE = new Uint8Array([0x06, 0x00]);
|
|
5
|
+
export async function generateFakePrimaryDevice() {
|
|
6
|
+
return {
|
|
7
|
+
identityKeyPair: await X25519.generateKeyPair()
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export async function buildAdvSignedDeviceIdentity(input) {
|
|
11
|
+
const advDeviceIdentity = proto.ADVDeviceIdentity.encode({
|
|
12
|
+
rawId: input.companionDeviceId,
|
|
13
|
+
timestamp: input.timestampSeconds ?? Math.floor(Date.now() / 1000),
|
|
14
|
+
keyIndex: input.keyIndex ?? 0
|
|
15
|
+
}).finish();
|
|
16
|
+
// Sign exactly: prefix || details || companionIdentityPublicKey.
|
|
17
|
+
const accountSignatureKey = input.primary.identityKeyPair.pubKey;
|
|
18
|
+
const messageToSign = concatBytes([
|
|
19
|
+
ADV_PREFIX_ACCOUNT_SIGNATURE,
|
|
20
|
+
advDeviceIdentity,
|
|
21
|
+
input.companionIdentityPublicKey
|
|
22
|
+
]);
|
|
23
|
+
const accountSignature = await xeddsaSign(input.primary.identityKeyPair.privKey, messageToSign);
|
|
24
|
+
const signedIdentity = proto.ADVSignedDeviceIdentity.encode({
|
|
25
|
+
details: advDeviceIdentity,
|
|
26
|
+
accountSignatureKey,
|
|
27
|
+
accountSignature
|
|
28
|
+
}).finish();
|
|
29
|
+
const hmac = hmacSha256Sign(input.advSecretKey, signedIdentity);
|
|
30
|
+
const wrapped = proto.ADVSignedDeviceIdentityHMAC.encode({
|
|
31
|
+
details: signedIdentity,
|
|
32
|
+
hmac
|
|
33
|
+
}).finish();
|
|
34
|
+
return { deviceIdentityBytes: wrapped };
|
|
35
|
+
}
|
|
36
|
+
function concatBytes(parts) {
|
|
37
|
+
let total = 0;
|
|
38
|
+
for (const p of parts)
|
|
39
|
+
total += p.byteLength;
|
|
40
|
+
const out = new Uint8Array(total);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
for (const p of parts) {
|
|
43
|
+
out.set(p, offset);
|
|
44
|
+
offset += p.byteLength;
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Pairing IQ builders/parsers (source: `WAWebHandlePairDevice*`, `/wa-web`). */
|
|
2
|
+
import { decodeBase64Url } from '../../transport/util.js';
|
|
3
|
+
export function buildPairDeviceIq(input) {
|
|
4
|
+
if (input.refs.length !== 6) {
|
|
5
|
+
throw new Error(`pair-device requires exactly 6 refs, got ${input.refs.length}`);
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
tag: 'iq',
|
|
9
|
+
attrs: {
|
|
10
|
+
id: input.id ?? `pair-device-${Math.random().toString(36).slice(2, 10)}`,
|
|
11
|
+
type: 'set',
|
|
12
|
+
xmlns: 'md',
|
|
13
|
+
from: 's.whatsapp.net'
|
|
14
|
+
},
|
|
15
|
+
content: [
|
|
16
|
+
{
|
|
17
|
+
tag: 'pair-device',
|
|
18
|
+
attrs: {},
|
|
19
|
+
content: input.refs.map((ref) => ({
|
|
20
|
+
tag: 'ref',
|
|
21
|
+
attrs: {},
|
|
22
|
+
content: ref
|
|
23
|
+
}))
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function buildPairSuccessIq(input) {
|
|
29
|
+
const children = [
|
|
30
|
+
{
|
|
31
|
+
tag: 'device',
|
|
32
|
+
attrs: {
|
|
33
|
+
jid: input.deviceJid,
|
|
34
|
+
...(input.deviceLid !== undefined ? { lid: input.deviceLid } : {})
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
tag: 'platform',
|
|
39
|
+
attrs: { name: input.platform }
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
tag: 'device-identity',
|
|
43
|
+
attrs: {},
|
|
44
|
+
content: input.deviceIdentityBytes
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
if (input.bizName !== undefined) {
|
|
48
|
+
children.push({
|
|
49
|
+
tag: 'biz',
|
|
50
|
+
attrs: { name: input.bizName }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
tag: 'iq',
|
|
55
|
+
attrs: {
|
|
56
|
+
id: input.id ?? `pair-success-${Math.random().toString(36).slice(2, 10)}`,
|
|
57
|
+
type: 'set',
|
|
58
|
+
xmlns: 'md',
|
|
59
|
+
from: 's.whatsapp.net'
|
|
60
|
+
},
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
tag: 'pair-success',
|
|
64
|
+
attrs: {},
|
|
65
|
+
content: children
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function parsePairingQrString(qr) {
|
|
71
|
+
const parts = qr.split(',');
|
|
72
|
+
if (parts.length < 5) {
|
|
73
|
+
throw new Error(`pairing qr must have 5 comma-separated parts, got ${parts.length}`);
|
|
74
|
+
}
|
|
75
|
+
const platform = parts[parts.length - 1];
|
|
76
|
+
const advSecretB64 = parts[parts.length - 2];
|
|
77
|
+
const identityPubB64 = parts[parts.length - 3];
|
|
78
|
+
const noisePubB64 = parts[parts.length - 4];
|
|
79
|
+
const ref = parts.slice(0, parts.length - 4).join(',');
|
|
80
|
+
return {
|
|
81
|
+
ref,
|
|
82
|
+
noisePublicKey: decodeBase64Url(noisePubB64, 'qr.noisePublicKey'),
|
|
83
|
+
identityPublicKey: decodeBase64Url(identityPubB64, 'qr.identityPublicKey'),
|
|
84
|
+
advSecretKey: decodeBase64Url(advSecretB64, 'qr.advSecretKey'),
|
|
85
|
+
platform
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Builder for the post-handshake `<success/>` stanza. */
|
|
2
|
+
export function buildSuccessNode(input = {}) {
|
|
3
|
+
const t = input.t ?? Math.floor(Date.now() / 1000);
|
|
4
|
+
const props = input.props ?? 0;
|
|
5
|
+
const attrs = {
|
|
6
|
+
t: String(t),
|
|
7
|
+
props: String(props),
|
|
8
|
+
companion_enc_static: input.companionEncStatic ?? 'NULL'
|
|
9
|
+
};
|
|
10
|
+
if (input.lid !== undefined)
|
|
11
|
+
attrs.lid = input.lid;
|
|
12
|
+
if (input.displayName !== undefined)
|
|
13
|
+
attrs.display_name = input.displayName;
|
|
14
|
+
if (input.abprops !== undefined)
|
|
15
|
+
attrs.abprops = String(input.abprops);
|
|
16
|
+
if (input.groupAbprops !== undefined)
|
|
17
|
+
attrs.group_abprops = String(input.groupAbprops);
|
|
18
|
+
if (input.location !== undefined)
|
|
19
|
+
attrs.location = input.location;
|
|
20
|
+
if (input.creation !== undefined)
|
|
21
|
+
attrs.creation = String(input.creation);
|
|
22
|
+
return {
|
|
23
|
+
tag: 'success',
|
|
24
|
+
attrs
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Builds the `abt/props` IQ result consumed by the client ABProps parser. */
|
|
2
|
+
import { buildIqResult } from './router.js';
|
|
3
|
+
export function buildAbPropsResult(iq, input = {}) {
|
|
4
|
+
const result = buildIqResult(iq);
|
|
5
|
+
const propsAttrs = {
|
|
6
|
+
protocol: '1',
|
|
7
|
+
hash: input.hash ?? 'fake-abprops-hash',
|
|
8
|
+
refresh: String(input.refreshSeconds ?? 86400),
|
|
9
|
+
refresh_id: String(input.refreshId ?? 1),
|
|
10
|
+
delta_update: 'false'
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
...result,
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
tag: 'props',
|
|
17
|
+
attrs: propsAttrs,
|
|
18
|
+
content: (input.props ?? []).map((prop) => ({
|
|
19
|
+
tag: 'prop',
|
|
20
|
+
attrs: {
|
|
21
|
+
config_code: String(prop.configCode),
|
|
22
|
+
...(prop.configValue !== undefined
|
|
23
|
+
? { config_value: prop.configValue }
|
|
24
|
+
: {})
|
|
25
|
+
}
|
|
26
|
+
}))
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|