zapo-js 0.1.0 → 0.1.2
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 +12 -7
- package/dist/appstate/WaAppStateCrypto.js +18 -25
- package/dist/appstate/WaAppStateSyncClient.js +181 -114
- package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
- package/dist/appstate/constants.js +4 -3
- package/dist/appstate/utils.js +10 -30
- package/dist/auth/WaAuthClient.js +48 -55
- package/dist/auth/flow/WaAuthCredentialsFlow.js +21 -14
- package/dist/auth/index.js +1 -3
- package/dist/auth/pairing/WaPairingFlow.js +21 -23
- package/dist/auth/pairing/WaQrFlow.js +37 -24
- package/dist/client/WaClient.js +103 -276
- package/dist/client/WaClientFactory.js +227 -110
- package/dist/client/connection/WaConnectionManager.js +292 -0
- package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
- package/dist/client/connection/WaReceiptQueue.js +51 -0
- package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
- package/dist/client/coordinators/WaGroupCoordinator.js +27 -17
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +231 -686
- package/dist/client/coordinators/WaRetryCoordinator.js +70 -37
- package/dist/client/dirty.js +35 -29
- package/dist/client/events/chat.js +4 -3
- package/dist/client/events/group.js +59 -36
- package/dist/client/history-sync.js +53 -63
- package/dist/client/incoming.js +23 -20
- package/dist/client/mailbox.js +8 -8
- package/dist/client/messages.js +4 -4
- package/dist/client/messaging/fanout.js +189 -0
- package/dist/client/messaging/key-protocol.js +130 -0
- package/dist/client/messaging/participants.js +191 -0
- package/dist/crypto/core/hkdf.js +3 -8
- package/dist/crypto/core/index.js +1 -4
- package/dist/crypto/core/keys.js +2 -3
- package/dist/crypto/core/primitives.js +12 -15
- package/dist/crypto/core/random.js +7 -26
- package/dist/crypto/curves/Ed25519.js +7 -8
- package/dist/crypto/curves/X25519.js +13 -16
- package/dist/crypto/index.js +0 -5
- package/dist/esm/appstate/WaAppStateCrypto.js +6 -13
- package/dist/esm/appstate/WaAppStateSyncClient.js +174 -107
- package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
- package/dist/esm/appstate/constants.js +3 -2
- package/dist/esm/appstate/utils.js +8 -27
- package/dist/esm/auth/WaAuthClient.js +48 -55
- package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +21 -14
- package/dist/esm/auth/index.js +0 -1
- package/dist/esm/auth/pairing/WaPairingFlow.js +14 -16
- package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
- package/dist/esm/client/WaClient.js +103 -276
- package/dist/esm/client/WaClientFactory.js +227 -110
- package/dist/esm/client/connection/WaConnectionManager.js +288 -0
- package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
- package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
- package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
- package/dist/esm/client/coordinators/WaGroupCoordinator.js +20 -10
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +232 -687
- package/dist/esm/client/coordinators/WaRetryCoordinator.js +71 -38
- package/dist/esm/client/dirty.js +30 -24
- package/dist/esm/client/events/chat.js +4 -3
- package/dist/esm/client/events/group.js +50 -28
- package/dist/esm/client/history-sync.js +50 -60
- package/dist/esm/client/incoming.js +23 -20
- package/dist/esm/client/mailbox.js +8 -8
- package/dist/esm/client/messages.js +1 -1
- package/dist/esm/client/messaging/fanout.js +186 -0
- package/dist/esm/client/messaging/key-protocol.js +127 -0
- package/dist/esm/client/messaging/participants.js +188 -0
- package/dist/esm/crypto/core/hkdf.js +3 -8
- package/dist/esm/crypto/core/index.js +0 -1
- package/dist/esm/crypto/core/keys.js +2 -3
- package/dist/esm/crypto/core/primitives.js +12 -15
- package/dist/esm/crypto/core/random.js +6 -25
- package/dist/esm/crypto/curves/Ed25519.js +4 -5
- package/dist/esm/crypto/curves/X25519.js +10 -13
- package/dist/esm/crypto/index.js +0 -2
- package/dist/esm/infra/log/ConsoleLogger.js +18 -17
- package/dist/esm/infra/log/PinoLogger.js +15 -9
- package/dist/esm/infra/log/types.js +11 -1
- package/dist/esm/infra/perf/BoundedTaskQueue.js +13 -17
- package/dist/esm/media/WaMediaCrypto.js +2 -4
- package/dist/esm/media/WaMediaTransferClient.js +226 -58
- package/dist/esm/media/conn.js +10 -6
- package/dist/esm/media/constants.js +4 -1
- package/dist/esm/message/WaMessageClient.js +4 -13
- package/dist/esm/message/ack.js +6 -6
- package/dist/esm/message/addon-crypto.js +59 -0
- package/dist/esm/message/incoming.js +106 -111
- package/dist/esm/message/index.js +2 -0
- package/dist/esm/message/reporting-token.js +438 -0
- package/dist/esm/message/use-case-secret.js +49 -0
- package/dist/esm/protocol/appstate.js +58 -0
- package/dist/esm/protocol/constants.js +2 -1
- package/dist/esm/protocol/index.js +2 -10
- package/dist/esm/protocol/jid.js +63 -51
- package/dist/esm/protocol/media.js +3 -3
- package/dist/esm/protocol/nodes.js +2 -0
- package/dist/esm/protocol/usync.js +11 -0
- package/dist/esm/retry/index.js +1 -0
- package/dist/esm/retry/outbound.js +4 -5
- package/dist/esm/retry/parse.js +58 -76
- package/dist/esm/retry/replay.js +48 -49
- package/dist/esm/retry/tracker.js +56 -0
- package/dist/esm/signal/api/SignalDeviceSyncApi.js +249 -82
- package/dist/esm/signal/api/SignalDigestSyncApi.js +6 -1
- package/dist/esm/signal/api/SignalIdentitySyncApi.js +49 -34
- package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +70 -62
- package/dist/esm/signal/api/SignalSessionSyncApi.js +23 -30
- package/dist/esm/signal/crypto/WaAdvSignature.js +3 -5
- package/dist/esm/signal/group/SenderKeyChain.js +28 -23
- package/dist/esm/signal/group/SenderKeyCodec.js +2 -4
- package/dist/esm/signal/group/SenderKeyManager.js +26 -16
- package/dist/esm/signal/index.js +1 -0
- package/dist/esm/signal/session/SignalProtocol.js +49 -14
- package/dist/esm/signal/session/SignalRatchet.js +24 -15
- package/dist/esm/signal/session/SignalSession.js +14 -9
- package/dist/esm/signal/session/resolver.js +186 -0
- package/dist/esm/signal/store/sqlite.js +16 -37
- package/dist/esm/store/createStore.js +16 -18
- package/dist/esm/store/noop.store.js +3 -6
- package/dist/esm/store/providers/memory/appstate.store.js +30 -6
- package/dist/esm/store/providers/memory/contact.store.js +5 -0
- package/dist/esm/store/providers/memory/device-list.store.js +3 -30
- package/dist/esm/store/providers/memory/message.store.js +11 -5
- package/dist/esm/store/providers/memory/participants.store.js +1 -8
- package/dist/esm/store/providers/memory/sender-key.store.js +5 -7
- package/dist/esm/store/providers/memory/signal.store.js +13 -1
- package/dist/esm/store/providers/memory/thread.store.js +5 -0
- package/dist/esm/store/providers/sqlite/appstate.store.js +82 -1
- package/dist/esm/store/providers/sqlite/connection.js +18 -13
- package/dist/esm/store/providers/sqlite/contact.store.js +31 -18
- package/dist/esm/store/providers/sqlite/device-list.store.js +7 -35
- package/dist/esm/store/providers/sqlite/message.store.js +45 -32
- package/dist/esm/store/providers/sqlite/migrations.js +1 -1
- package/dist/esm/store/providers/sqlite/participants.store.js +1 -9
- package/dist/esm/store/providers/sqlite/retry.store.js +8 -11
- package/dist/esm/store/providers/sqlite/sender-key.store.js +25 -30
- package/dist/esm/store/providers/sqlite/signal.store.js +104 -22
- package/dist/esm/store/providers/sqlite/table-names.js +107 -0
- package/dist/esm/store/providers/sqlite/thread.store.js +35 -22
- package/dist/esm/transport/WaComms.js +25 -23
- package/dist/esm/transport/WaWebSocket.js +115 -12
- package/dist/esm/transport/binary/decoder.js +4 -4
- package/dist/esm/transport/binary/encoder.js +12 -4
- package/dist/esm/transport/index.js +1 -0
- package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
- package/dist/esm/transport/node/WaNodeOrchestrator.js +2 -4
- package/dist/esm/transport/node/WaNodeTransport.js +0 -3
- package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
- package/dist/esm/transport/node/builders/index.js +2 -1
- package/dist/esm/transport/node/builders/message.js +9 -0
- package/dist/esm/transport/node/builders/pairing.js +4 -5
- package/dist/esm/transport/node/builders/usync.js +41 -0
- package/dist/esm/transport/node/helpers.js +107 -5
- package/dist/esm/transport/node/usync.js +35 -0
- package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
- package/dist/esm/transport/noise/WaNoiseCert.js +3 -6
- package/dist/esm/transport/noise/WaNoiseSession.js +17 -10
- package/dist/esm/transport/proxy.js +27 -0
- package/dist/esm/transport/stream/parse.js +13 -48
- package/dist/esm/util/bytes.js +50 -32
- package/dist/esm/util/coercion.js +6 -14
- package/dist/esm/util/primitives.js +39 -14
- package/dist/infra/log/ConsoleLogger.js +18 -17
- package/dist/infra/log/PinoLogger.js +15 -9
- package/dist/infra/log/types.js +12 -0
- package/dist/infra/perf/BoundedTaskQueue.js +13 -17
- package/dist/media/WaMediaCrypto.js +1 -3
- package/dist/media/WaMediaTransferClient.js +259 -58
- package/dist/media/conn.js +10 -6
- package/dist/media/constants.js +4 -1
- package/dist/message/WaMessageClient.js +5 -14
- package/dist/message/ack.js +6 -6
- package/dist/message/addon-crypto.js +65 -0
- package/dist/message/incoming.js +104 -109
- package/dist/message/index.js +2 -0
- package/dist/message/reporting-token.js +443 -0
- package/dist/message/use-case-secret.js +55 -0
- package/dist/protocol/appstate.js +59 -1
- package/dist/protocol/constants.js +7 -1
- package/dist/protocol/index.js +20 -42
- package/dist/protocol/jid.js +64 -51
- package/dist/protocol/media.js +3 -3
- package/dist/protocol/nodes.js +2 -0
- package/dist/protocol/usync.js +14 -0
- package/dist/retry/index.js +3 -1
- package/dist/retry/outbound.js +6 -7
- package/dist/retry/parse.js +57 -75
- package/dist/retry/replay.js +46 -47
- package/dist/retry/tracker.js +59 -0
- package/dist/signal/api/SignalDeviceSyncApi.js +247 -80
- package/dist/signal/api/SignalDigestSyncApi.js +6 -1
- package/dist/signal/api/SignalIdentitySyncApi.js +49 -34
- package/dist/signal/api/SignalMissingPreKeysSyncApi.js +67 -59
- package/dist/signal/api/SignalSessionSyncApi.js +23 -30
- package/dist/signal/crypto/WaAdvSignature.js +2 -4
- package/dist/signal/group/SenderKeyChain.js +27 -22
- package/dist/signal/group/SenderKeyCodec.js +1 -3
- package/dist/signal/group/SenderKeyManager.js +26 -16
- package/dist/signal/index.js +3 -1
- package/dist/signal/session/SignalProtocol.js +49 -14
- package/dist/signal/session/SignalRatchet.js +24 -15
- package/dist/signal/session/SignalSession.js +14 -9
- package/dist/signal/session/resolver.js +189 -0
- package/dist/signal/store/sqlite.js +16 -37
- package/dist/store/createStore.js +16 -18
- package/dist/store/noop.store.js +3 -6
- package/dist/store/providers/memory/appstate.store.js +28 -4
- package/dist/store/providers/memory/contact.store.js +5 -0
- package/dist/store/providers/memory/device-list.store.js +3 -30
- package/dist/store/providers/memory/message.store.js +11 -5
- package/dist/store/providers/memory/participants.store.js +1 -8
- package/dist/store/providers/memory/sender-key.store.js +8 -10
- package/dist/store/providers/memory/signal.store.js +21 -9
- package/dist/store/providers/memory/thread.store.js +5 -0
- package/dist/store/providers/sqlite/appstate.store.js +81 -0
- package/dist/store/providers/sqlite/connection.js +18 -13
- package/dist/store/providers/sqlite/contact.store.js +31 -18
- package/dist/store/providers/sqlite/device-list.store.js +7 -35
- package/dist/store/providers/sqlite/message.store.js +45 -32
- package/dist/store/providers/sqlite/migrations.js +1 -1
- package/dist/store/providers/sqlite/participants.store.js +1 -9
- package/dist/store/providers/sqlite/retry.store.js +8 -11
- package/dist/store/providers/sqlite/sender-key.store.js +24 -29
- package/dist/store/providers/sqlite/signal.store.js +105 -23
- package/dist/store/providers/sqlite/table-names.js +113 -0
- package/dist/store/providers/sqlite/thread.store.js +35 -22
- package/dist/transport/WaComms.js +27 -25
- package/dist/transport/WaWebSocket.js +148 -12
- package/dist/transport/binary/decoder.js +4 -4
- package/dist/transport/binary/encoder.js +12 -4
- package/dist/transport/index.js +7 -1
- package/dist/transport/keepalive/WaKeepAlive.js +1 -7
- package/dist/transport/node/WaNodeOrchestrator.js +2 -4
- package/dist/transport/node/WaNodeTransport.js +0 -3
- package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
- package/dist/transport/node/builders/index.js +12 -9
- package/dist/transport/node/builders/message.js +9 -0
- package/dist/transport/node/builders/pairing.js +4 -5
- package/dist/transport/node/builders/usync.js +45 -0
- package/dist/transport/node/helpers.js +112 -4
- package/dist/transport/node/usync.js +38 -0
- package/dist/transport/noise/WaFrameCodec.js +47 -32
- package/dist/transport/noise/WaNoiseCert.js +5 -8
- package/dist/transport/noise/WaNoiseSession.js +17 -10
- package/dist/transport/proxy.js +34 -0
- package/dist/transport/stream/parse.js +17 -53
- package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
- package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
- package/dist/types/appstate/constants.d.ts +1 -0
- package/dist/types/appstate/store/sqlite.d.ts +4 -18
- package/dist/types/appstate/utils.d.ts +0 -1
- package/dist/types/auth/WaAuthClient.d.ts +10 -12
- package/dist/types/auth/index.d.ts +0 -2
- package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
- package/dist/types/auth/types.d.ts +6 -9
- package/dist/types/client/WaClient.d.ts +27 -25
- package/dist/types/client/WaClientFactory.d.ts +22 -23
- package/dist/types/client/connection/WaConnectionManager.d.ts +64 -0
- package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
- package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
- package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
- package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +0 -1
- package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +18 -41
- package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +2 -0
- package/dist/types/client/dirty.d.ts +1 -0
- package/dist/types/client/events/group.d.ts +2 -1
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/messaging/fanout.d.ts +14 -0
- package/dist/types/client/messaging/key-protocol.d.ts +18 -0
- package/dist/types/client/messaging/participants.d.ts +13 -0
- package/dist/types/client/types.d.ts +24 -1
- package/dist/types/crypto/core/hkdf.d.ts +0 -6
- package/dist/types/crypto/core/index.d.ts +0 -1
- package/dist/types/crypto/core/random.d.ts +1 -7
- package/dist/types/crypto/index.d.ts +0 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
- package/dist/types/infra/log/PinoLogger.d.ts +1 -1
- package/dist/types/infra/log/types.d.ts +1 -0
- package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
- package/dist/types/media/WaMediaTransferClient.d.ts +13 -3
- package/dist/types/media/types.d.ts +5 -0
- package/dist/types/message/addon-crypto.d.ts +25 -0
- package/dist/types/message/index.d.ts +2 -0
- package/dist/types/message/reporting-token.d.ts +19 -0
- package/dist/types/message/use-case-secret.d.ts +20 -0
- package/dist/types/protocol/appstate.d.ts +58 -0
- package/dist/types/protocol/constants.d.ts +2 -1
- package/dist/types/protocol/index.d.ts +2 -10
- package/dist/types/protocol/jid.d.ts +3 -3
- package/dist/types/protocol/nodes.d.ts +2 -0
- package/dist/types/protocol/usync.d.ts +11 -0
- package/dist/types/retry/index.d.ts +1 -0
- package/dist/types/retry/replay.d.ts +0 -4
- package/dist/types/retry/tracker.d.ts +19 -0
- package/dist/types/retry/types.d.ts +4 -3
- package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +13 -1
- package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
- package/dist/types/signal/index.d.ts +1 -0
- package/dist/types/signal/session/SignalProtocol.d.ts +9 -0
- package/dist/types/signal/session/resolver.d.ts +17 -0
- package/dist/types/store/contracts/appstate.store.d.ts +3 -0
- package/dist/types/store/contracts/contact.store.d.ts +1 -0
- package/dist/types/store/contracts/device-list.store.d.ts +0 -3
- package/dist/types/store/contracts/message.store.d.ts +1 -0
- package/dist/types/store/contracts/participants.store.d.ts +0 -1
- package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
- package/dist/types/store/contracts/signal.store.d.ts +6 -0
- package/dist/types/store/contracts/thread.store.d.ts +1 -0
- package/dist/types/store/index.d.ts +1 -1
- package/dist/types/store/providers/memory/appstate.store.d.ts +2 -0
- package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
- package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
- package/dist/types/store/providers/memory/message.store.d.ts +1 -0
- package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
- package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
- package/dist/types/store/providers/memory/signal.store.d.ts +6 -0
- package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
- package/dist/types/store/providers/sqlite/appstate.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/contact.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -3
- package/dist/types/store/providers/sqlite/message.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/signal.store.d.ts +7 -0
- package/dist/types/store/providers/sqlite/table-names.d.ts +5 -0
- package/dist/types/store/providers/sqlite/thread.store.d.ts +2 -0
- package/dist/types/store/types.d.ts +3 -0
- package/dist/types/transport/WaWebSocket.d.ts +3 -0
- package/dist/types/transport/index.d.ts +2 -1
- package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
- package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
- package/dist/types/transport/node/builders/group.d.ts +4 -6
- package/dist/types/transport/node/builders/index.d.ts +2 -1
- package/dist/types/transport/node/builders/message.d.ts +14 -25
- package/dist/types/transport/node/builders/retry.d.ts +2 -4
- package/dist/types/transport/node/builders/usync.d.ts +21 -0
- package/dist/types/transport/node/helpers.d.ts +8 -0
- package/dist/types/transport/node/usync.d.ts +2 -0
- package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
- package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
- package/dist/types/transport/proxy.d.ts +6 -0
- package/dist/types/transport/stream/parse.d.ts +0 -1
- package/dist/types/transport/types.d.ts +18 -1
- package/dist/types/util/bytes.d.ts +5 -0
- package/dist/types/util/primitives.d.ts +3 -0
- package/dist/util/bytes.js +55 -33
- package/dist/util/coercion.js +6 -14
- package/dist/util/primitives.js +42 -14
- package/package.json +27 -9
- package/proto/index.d.ts +1090 -1048
- package/proto/index.js +1 -1
- package/scripts/check-node-version.cjs +0 -1
- package/dist/crypto/core/encoding.js +0 -29
- package/dist/esm/crypto/core/encoding.js +0 -25
- package/dist/esm/util/base64.js +0 -18
- package/dist/esm/util/signal-address.js +0 -5
- package/dist/types/crypto/core/encoding.d.ts +0 -11
- package/dist/types/util/base64.d.ts +0 -4
- package/dist/types/util/signal-address.d.ts +0 -2
- package/dist/util/base64.js +0 -24
- package/dist/util/signal-address.js +0 -8
- /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import { WA_DEFAULTS,
|
|
1
|
+
import { WA_DEFAULTS, WA_NODE_TAGS } from '../../protocol/constants.js';
|
|
2
2
|
import { splitJid } from '../../protocol/jid.js';
|
|
3
3
|
import { decodeExactLength, parseUint } from '../api/codec.js';
|
|
4
4
|
import { SIGNAL_KEY_DATA_LENGTH, SIGNAL_KEY_ID_LENGTH, SIGNAL_REGISTRATION_ID_LENGTH, SIGNAL_SIGNATURE_LENGTH } from '../api/constants.js';
|
|
5
5
|
import { buildMissingPreKeysFetchIq } from '../api/prekeys.js';
|
|
6
|
-
import { decodeNodeContentBase64OrBytes, findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
|
|
7
|
-
import {
|
|
8
|
-
function parseDeviceId(value, field) {
|
|
9
|
-
if (!value) {
|
|
10
|
-
throw new Error(`${field} is missing`);
|
|
11
|
-
}
|
|
12
|
-
const parsed = Number.parseInt(value, 10);
|
|
13
|
-
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
14
|
-
throw new Error(`${field} is invalid`);
|
|
15
|
-
}
|
|
16
|
-
return parsed;
|
|
17
|
-
}
|
|
6
|
+
import { decodeNodeContentBase64OrBytes, findNodeChildrenByTags, findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
|
|
7
|
+
import { assertIqResult } from '../../transport/node/query.js';
|
|
18
8
|
export class SignalMissingPreKeysSyncApi {
|
|
19
9
|
constructor(options) {
|
|
20
10
|
this.logger = options.logger;
|
|
@@ -38,16 +28,7 @@ export class SignalMissingPreKeysSyncApi {
|
|
|
38
28
|
return parsed;
|
|
39
29
|
}
|
|
40
30
|
parseFetchMissingPreKeysResponse(node, requestedTargets) {
|
|
41
|
-
|
|
42
|
-
throw new Error(`invalid missing prekeys response tag: ${node.tag}`);
|
|
43
|
-
}
|
|
44
|
-
if (node.attrs.type === WA_IQ_TYPES.ERROR) {
|
|
45
|
-
const error = parseIqError(node);
|
|
46
|
-
throw new Error(`missing prekeys iq failed (${error.code}: ${error.text})`);
|
|
47
|
-
}
|
|
48
|
-
if (node.attrs.type !== WA_IQ_TYPES.RESULT) {
|
|
49
|
-
throw new Error(`invalid missing prekeys response type: ${node.attrs.type ?? 'unknown'}`);
|
|
50
|
-
}
|
|
31
|
+
assertIqResult(node, 'missing prekeys');
|
|
51
32
|
const listNode = findNodeChild(node, WA_NODE_TAGS.LIST);
|
|
52
33
|
if (!listNode) {
|
|
53
34
|
throw new Error('missing prekeys response missing list node');
|
|
@@ -75,67 +56,94 @@ export class SignalMissingPreKeysSyncApi {
|
|
|
75
56
|
devices: this.parseUserDevices(userNode, userJid)
|
|
76
57
|
});
|
|
77
58
|
}
|
|
78
|
-
|
|
79
|
-
|
|
59
|
+
const results = new Array(requestedTargets.length);
|
|
60
|
+
for (let index = 0; index < requestedTargets.length; index += 1) {
|
|
61
|
+
const target = requestedTargets[index];
|
|
62
|
+
results[index] = parsedByJid.get(target.userJid) ?? {
|
|
80
63
|
userJid: target.userJid,
|
|
81
64
|
errorText: 'missing user in key_fetch response'
|
|
82
|
-
}
|
|
83
|
-
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
84
68
|
}
|
|
85
69
|
parseUserDevices(node, userJid) {
|
|
86
70
|
const { user, server } = splitJid(userJid);
|
|
87
71
|
const devices = getNodeChildrenByTag(node, WA_NODE_TAGS.DEVICE);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
72
|
+
const bundles = new Array(devices.length);
|
|
73
|
+
for (let index = 0; index < devices.length; index += 1) {
|
|
74
|
+
const deviceNode = devices[index];
|
|
75
|
+
const deviceIdValue = deviceNode.attrs.id;
|
|
76
|
+
if (!deviceIdValue) {
|
|
77
|
+
throw new Error(`missing prekeys device[${index}].id is missing`);
|
|
78
|
+
}
|
|
79
|
+
const deviceId = Number.parseInt(deviceIdValue, 10);
|
|
80
|
+
if (!Number.isSafeInteger(deviceId) || deviceId < 0) {
|
|
81
|
+
throw new Error(`missing prekeys device[${index}].id is invalid`);
|
|
82
|
+
}
|
|
83
|
+
const [registrationNode, identityNode, signedKeyNode, oneTimeNode, deviceIdentityNode] = findNodeChildrenByTags(deviceNode, [
|
|
84
|
+
WA_NODE_TAGS.REGISTRATION,
|
|
85
|
+
WA_NODE_TAGS.IDENTITY,
|
|
86
|
+
WA_NODE_TAGS.SKEY,
|
|
87
|
+
WA_NODE_TAGS.KEY,
|
|
88
|
+
WA_NODE_TAGS.DEVICE_IDENTITY
|
|
89
|
+
]);
|
|
93
90
|
if (!registrationNode || !identityNode || !signedKeyNode) {
|
|
94
91
|
throw new Error(`missing prekeys device payload is incomplete for ${userJid}`);
|
|
95
92
|
}
|
|
96
|
-
const signedKeyIdNode =
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
const [signedKeyIdNode, signedKeyValueNode, signedKeySignatureNode] = findNodeChildrenByTags(signedKeyNode, [
|
|
94
|
+
WA_NODE_TAGS.ID,
|
|
95
|
+
WA_NODE_TAGS.VALUE,
|
|
96
|
+
WA_NODE_TAGS.SIGNATURE
|
|
97
|
+
]);
|
|
99
98
|
if (!signedKeyIdNode || !signedKeyValueNode || !signedKeySignatureNode) {
|
|
100
99
|
throw new Error(`missing prekeys signed pre-key is incomplete for ${userJid}`);
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
let oneTimeIdNode;
|
|
102
|
+
let oneTimeValueNode;
|
|
103
|
+
if (oneTimeNode) {
|
|
104
|
+
const oneTimeNodes = findNodeChildrenByTags(oneTimeNode, [
|
|
105
|
+
WA_NODE_TAGS.ID,
|
|
106
|
+
WA_NODE_TAGS.VALUE
|
|
107
|
+
]);
|
|
108
|
+
oneTimeIdNode = oneTimeNodes[0];
|
|
109
|
+
oneTimeValueNode = oneTimeNodes[1];
|
|
110
|
+
}
|
|
109
111
|
if (oneTimeNode && (!oneTimeIdNode || !oneTimeValueNode)) {
|
|
110
112
|
throw new Error(`missing prekeys one-time key is incomplete for ${userJid}`);
|
|
111
113
|
}
|
|
112
|
-
const
|
|
113
|
-
const bundle = {
|
|
114
|
+
const baseBundle = {
|
|
114
115
|
regId: parseUint(decodeExactLength(registrationNode.content, 'missing prekeys device registration', SIGNAL_REGISTRATION_ID_LENGTH), 'missing prekeys device registration'),
|
|
115
116
|
identity: decodeExactLength(identityNode.content, 'missing prekeys device identity', SIGNAL_KEY_DATA_LENGTH),
|
|
116
117
|
signedKey: {
|
|
117
118
|
id: parseUint(decodeExactLength(signedKeyIdNode.content, 'missing prekeys device skey.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device skey.id'),
|
|
118
119
|
publicKey: decodeExactLength(signedKeyValueNode.content, 'missing prekeys device skey.value', SIGNAL_KEY_DATA_LENGTH),
|
|
119
120
|
signature: decodeExactLength(signedKeySignatureNode.content, 'missing prekeys device skey.signature', SIGNAL_SIGNATURE_LENGTH)
|
|
120
|
-
}
|
|
121
|
-
...(oneTimeIdNode && oneTimeValueNode
|
|
122
|
-
? {
|
|
123
|
-
oneTimeKey: {
|
|
124
|
-
id: parseUint(decodeExactLength(oneTimeIdNode.content, 'missing prekeys device key.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device key.id'),
|
|
125
|
-
publicKey: decodeExactLength(oneTimeValueNode.content, 'missing prekeys device key.value', SIGNAL_KEY_DATA_LENGTH)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
: {})
|
|
121
|
+
}
|
|
129
122
|
};
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
123
|
+
const bundle = oneTimeIdNode && oneTimeValueNode
|
|
124
|
+
? {
|
|
125
|
+
...baseBundle,
|
|
126
|
+
oneTimeKey: {
|
|
127
|
+
id: parseUint(decodeExactLength(oneTimeIdNode.content, 'missing prekeys device key.id', SIGNAL_KEY_ID_LENGTH), 'missing prekeys device key.id'),
|
|
128
|
+
publicKey: decodeExactLength(oneTimeValueNode.content, 'missing prekeys device key.value', SIGNAL_KEY_DATA_LENGTH)
|
|
136
129
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
}
|
|
131
|
+
: baseBundle;
|
|
132
|
+
let deviceIdentity;
|
|
133
|
+
if (deviceIdentityNode) {
|
|
134
|
+
deviceIdentity = decodeNodeContentBase64OrBytes(deviceIdentityNode.content, 'missing prekeys device device-identity');
|
|
135
|
+
}
|
|
136
|
+
bundles[index] = deviceIdentity
|
|
137
|
+
? {
|
|
138
|
+
deviceJid: deviceId === 0 ? userJid : `${user}:${deviceId}@${server}`,
|
|
139
|
+
bundle,
|
|
140
|
+
deviceIdentity
|
|
141
|
+
}
|
|
142
|
+
: {
|
|
143
|
+
deviceJid: deviceId === 0 ? userJid : `${user}:${deviceId}@${server}`,
|
|
144
|
+
bundle
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return bundles;
|
|
140
148
|
}
|
|
141
149
|
}
|
|
@@ -2,6 +2,7 @@ import { WA_DEFAULTS, WA_IQ_TYPES, WA_NODE_TAGS, WA_XMLNS } from '../../protocol
|
|
|
2
2
|
import { decodeExactLength, parseUint } from '../api/codec.js';
|
|
3
3
|
import { SIGNAL_KEY_DATA_LENGTH, SIGNAL_KEY_ID_LENGTH, SIGNAL_REGISTRATION_ID_LENGTH, SIGNAL_SIGNATURE_LENGTH } from '../api/constants.js';
|
|
4
4
|
import { findNodeChild, getNodeChildrenByTag, decodeNodeContentBase64OrBytes } from '../../transport/node/helpers.js';
|
|
5
|
+
import { assertIqResult } from '../../transport/node/query.js';
|
|
5
6
|
export class SignalSessionSyncApi {
|
|
6
7
|
constructor(options) {
|
|
7
8
|
this.logger = options.logger;
|
|
@@ -40,7 +41,20 @@ export class SignalSessionSyncApi {
|
|
|
40
41
|
reasonIdentity: (previous?.reasonIdentity ?? false) || target.reasonIdentity === true
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
|
-
const mergedTargets = [
|
|
44
|
+
const mergedTargets = [];
|
|
45
|
+
for (const target of targetByJid.values()) {
|
|
46
|
+
mergedTargets.push(target);
|
|
47
|
+
}
|
|
48
|
+
const userNodes = new Array(mergedTargets.length);
|
|
49
|
+
for (let index = 0; index < mergedTargets.length; index += 1) {
|
|
50
|
+
const target = mergedTargets[index];
|
|
51
|
+
userNodes[index] = {
|
|
52
|
+
tag: WA_NODE_TAGS.USER,
|
|
53
|
+
attrs: target.reasonIdentity === true
|
|
54
|
+
? { jid: target.jid, reason: 'identity' }
|
|
55
|
+
: { jid: target.jid }
|
|
56
|
+
};
|
|
57
|
+
}
|
|
44
58
|
this.logger.debug('signal fetch key bundles request', {
|
|
45
59
|
targets: mergedTargets.length,
|
|
46
60
|
timeoutMs
|
|
@@ -56,34 +70,14 @@ export class SignalSessionSyncApi {
|
|
|
56
70
|
{
|
|
57
71
|
tag: WA_NODE_TAGS.KEY,
|
|
58
72
|
attrs: {},
|
|
59
|
-
content:
|
|
60
|
-
tag: WA_NODE_TAGS.USER,
|
|
61
|
-
attrs: {
|
|
62
|
-
jid: target.jid,
|
|
63
|
-
...(target.reasonIdentity === true ? { reason: 'identity' } : {})
|
|
64
|
-
}
|
|
65
|
-
}))
|
|
73
|
+
content: userNodes
|
|
66
74
|
}
|
|
67
75
|
]
|
|
68
76
|
}, timeoutMs);
|
|
69
77
|
return this.parseFetchKeyBundleResponse(responseNode, mergedTargets);
|
|
70
78
|
}
|
|
71
79
|
parseFetchKeyBundleResponse(node, requestedTargets) {
|
|
72
|
-
|
|
73
|
-
throw new Error(`invalid key bundle response tag: ${node.tag}`);
|
|
74
|
-
}
|
|
75
|
-
if (node.attrs.type === WA_IQ_TYPES.ERROR) {
|
|
76
|
-
const errorNode = findNodeChild(node, WA_NODE_TAGS.ERROR);
|
|
77
|
-
if (!errorNode) {
|
|
78
|
-
throw new Error(`key bundle iq error for ${node.attrs.id ?? 'unknown id'}`);
|
|
79
|
-
}
|
|
80
|
-
const code = errorNode.attrs.code ?? 'unknown';
|
|
81
|
-
const text = errorNode.attrs.text ?? errorNode.attrs.type ?? 'unknown';
|
|
82
|
-
throw new Error(`key bundle iq error (${code} ${text})`);
|
|
83
|
-
}
|
|
84
|
-
if (node.attrs.type !== WA_IQ_TYPES.RESULT) {
|
|
85
|
-
throw new Error(`invalid key bundle response type: ${node.attrs.type ?? 'unknown'}`);
|
|
86
|
-
}
|
|
80
|
+
assertIqResult(node, 'key bundle');
|
|
87
81
|
const listNode = findNodeChild(node, WA_NODE_TAGS.LIST);
|
|
88
82
|
if (!listNode) {
|
|
89
83
|
throw new Error('key bundle response missing list node');
|
|
@@ -115,16 +109,15 @@ export class SignalSessionSyncApi {
|
|
|
115
109
|
...(parsed.deviceIdentity ? { deviceIdentity: parsed.deviceIdentity } : {})
|
|
116
110
|
});
|
|
117
111
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
112
|
+
const output = new Array(requestedTargets.length);
|
|
113
|
+
for (let index = 0; index < requestedTargets.length; index += 1) {
|
|
114
|
+
const target = requestedTargets[index];
|
|
115
|
+
output[index] = parsedByJid.get(target.jid) ?? {
|
|
124
116
|
jid: target.jid,
|
|
125
117
|
errorText: 'missing key bundle user in response'
|
|
126
118
|
};
|
|
127
|
-
}
|
|
119
|
+
}
|
|
120
|
+
return output;
|
|
128
121
|
}
|
|
129
122
|
parseUserKeyBundle(node) {
|
|
130
123
|
const registrationNode = findNodeChild(node, WA_NODE_TAGS.REGISTRATION);
|
|
@@ -4,10 +4,10 @@ import { encodeExtendedPoint, scalarMultBase } from '../../crypto/math/edwards.j
|
|
|
4
4
|
import { bytesToBigIntLE, bigIntToBytesLE } from '../../crypto/math/le.js';
|
|
5
5
|
import { modGroup } from '../../crypto/math/mod.js';
|
|
6
6
|
import { ADV_PREFIX_ACCOUNT_SIGNATURE, ADV_PREFIX_DEVICE_SIGNATURE, ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, ADV_PREFIX_HOSTED_DEVICE_SIGNATURE, SIGNAL_PREFIX_SIGNATURE_RANDOM } from '../crypto/constants.js';
|
|
7
|
-
import { concatBytes } from '../../util/bytes.js';
|
|
7
|
+
import { assertByteLength, concatBytes } from '../../util/bytes.js';
|
|
8
8
|
export { ADV_PREFIX_ACCOUNT_SIGNATURE, ADV_PREFIX_DEVICE_SIGNATURE, ADV_PREFIX_HOSTED_ACCOUNT_SIGNATURE, ADV_PREFIX_HOSTED_DEVICE_SIGNATURE } from '../crypto/constants.js';
|
|
9
9
|
export async function verifySignalSignature(publicKey, message, signature) {
|
|
10
|
-
if (signature
|
|
10
|
+
if (!assertByteLength(signature, 64, 'invalid signal signature length', false)) {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
13
|
if ((signature[63] & 0x60) !== 0) {
|
|
@@ -21,9 +21,7 @@ export async function verifySignalSignature(publicKey, message, signature) {
|
|
|
21
21
|
return ed25519VerifyRaw(edPublic, signalSignature, message);
|
|
22
22
|
}
|
|
23
23
|
export async function signSignalMessage(privateKey, message) {
|
|
24
|
-
|
|
25
|
-
throw new Error(`invalid curve25519 private key length ${privateKey.length}`);
|
|
26
|
-
}
|
|
24
|
+
assertByteLength(privateKey, 32, `invalid curve25519 private key length ${privateKey.length}`);
|
|
27
25
|
const clampedPrivateKey = clampCurvePrivateKeyInPlace(privateKey);
|
|
28
26
|
const privateScalar = bytesToBigIntLE(clampedPrivateKey);
|
|
29
27
|
const encodedPublic = encodeExtendedPoint(scalarMultBase(privateScalar));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { hkdf, importHmacKey, hmacSign } from '../../crypto/index.js';
|
|
2
2
|
import { CHAIN_KEY_LABEL, MAX_UNUSED_KEYS, MESSAGE_KEY_LABEL, SENDER_KEY_FUTURE_MESSAGES_MAX, WHISPER_GROUP_INFO } from '../constants.js';
|
|
3
|
-
import { removeAt } from '../../util/bytes.js';
|
|
3
|
+
import { assertByteLength, removeAt } from '../../util/bytes.js';
|
|
4
4
|
export async function selectMessageKey(senderKey, targetIteration) {
|
|
5
5
|
const delta = targetIteration - senderKey.iteration;
|
|
6
6
|
if (delta > SENDER_KEY_FUTURE_MESSAGES_MAX) {
|
|
@@ -26,24 +26,33 @@ export async function selectMessageKey(senderKey, targetIteration) {
|
|
|
26
26
|
const firstDerived = await deriveSenderKeyMsgKeyFromState(senderKey.iteration, chainState);
|
|
27
27
|
chainState = firstDerived.nextState;
|
|
28
28
|
let messageKey = firstDerived.messageKey;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (delta === 0) {
|
|
30
|
+
return {
|
|
31
|
+
messageKey,
|
|
32
|
+
updatedRecord: {
|
|
33
|
+
...senderKey,
|
|
34
|
+
iteration: targetIteration + 1,
|
|
35
|
+
chainKey: chainState.chainKey,
|
|
36
|
+
unusedMessageKeys: currentUnused
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const nextUnused = currentUnused.slice();
|
|
41
|
+
let overflow = delta + currentUnused.length - MAX_UNUSED_KEYS;
|
|
42
|
+
if (overflow > 0) {
|
|
43
|
+
nextUnused.splice(0, overflow);
|
|
44
|
+
overflow -= currentUnused.length;
|
|
45
|
+
}
|
|
46
|
+
for (let iteration = senderKey.iteration + 1; iteration <= targetIteration; iteration += 1) {
|
|
32
47
|
if (overflow > 0) {
|
|
33
|
-
|
|
34
|
-
overflow -= currentUnused.length;
|
|
48
|
+
overflow -= 1;
|
|
35
49
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
overflow -= 1;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
nextUnused.push(messageKey);
|
|
42
|
-
}
|
|
43
|
-
const derived = await deriveSenderKeyMsgKeyFromState(iteration, chainState);
|
|
44
|
-
chainState = derived.nextState;
|
|
45
|
-
messageKey = derived.messageKey;
|
|
50
|
+
else {
|
|
51
|
+
nextUnused.push(messageKey);
|
|
46
52
|
}
|
|
53
|
+
const derived = await deriveSenderKeyMsgKeyFromState(iteration, chainState);
|
|
54
|
+
chainState = derived.nextState;
|
|
55
|
+
messageKey = derived.messageKey;
|
|
47
56
|
}
|
|
48
57
|
return {
|
|
49
58
|
messageKey,
|
|
@@ -64,20 +73,16 @@ export async function deriveSenderKeyMsgKey(iteration, chainKey) {
|
|
|
64
73
|
};
|
|
65
74
|
}
|
|
66
75
|
async function createSenderChainState(chainKey) {
|
|
67
|
-
|
|
68
|
-
throw new Error('sender key chainKey must be 32 bytes');
|
|
69
|
-
}
|
|
76
|
+
assertByteLength(chainKey, 32, 'sender key chainKey must be 32 bytes');
|
|
70
77
|
return {
|
|
71
78
|
chainKey,
|
|
72
79
|
hmacKey: await importHmacKey(chainKey)
|
|
73
80
|
};
|
|
74
81
|
}
|
|
75
82
|
async function deriveSenderKeyMsgKeyFromState(iteration, state) {
|
|
76
|
-
const nextChainRawPromise = hmacSign(state.hmacKey, CHAIN_KEY_LABEL);
|
|
77
|
-
const messageInputKeyPromise = hmacSign(state.hmacKey, MESSAGE_KEY_LABEL);
|
|
78
83
|
const [nextChainRaw, messageInputKey] = await Promise.all([
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
hmacSign(state.hmacKey, CHAIN_KEY_LABEL),
|
|
85
|
+
hmacSign(state.hmacKey, MESSAGE_KEY_LABEL)
|
|
81
86
|
]);
|
|
82
87
|
const nextChainKey = nextChainRaw.subarray(0, 32);
|
|
83
88
|
const [nextHmacKey, messageSeed] = await Promise.all([
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readVersionedContent, toSerializedPubKey } from '../../crypto/index.js';
|
|
2
2
|
import { proto } from '../../proto.js';
|
|
3
3
|
import { SIGNATURE_SIZE, SIGNAL_GROUP_VERSION } from '../constants.js';
|
|
4
|
-
import { toBytesView } from '../../util/bytes.js';
|
|
4
|
+
import { assertByteLength, toBytesView } from '../../util/bytes.js';
|
|
5
5
|
export function parseDistributionPayload(payload) {
|
|
6
6
|
const body = readVersionedContent(payload, SIGNAL_GROUP_VERSION, 0);
|
|
7
7
|
const decoded = proto.SenderKeyDistributionMessage.decode(body);
|
|
@@ -16,9 +16,7 @@ export function parseDistributionPayload(payload) {
|
|
|
16
16
|
throw new Error('invalid sender key distribution message');
|
|
17
17
|
}
|
|
18
18
|
const chainKey = toBytesView(decoded.chainKey);
|
|
19
|
-
|
|
20
|
-
throw new Error('sender key distribution chainKey must be 32 bytes');
|
|
21
|
-
}
|
|
19
|
+
assertByteLength(chainKey, 32, 'sender key distribution chainKey must be 32 bytes');
|
|
22
20
|
return {
|
|
23
21
|
keyId: decoded.id,
|
|
24
22
|
iteration: decoded.iteration,
|
|
@@ -63,12 +63,16 @@ export class SenderKeyManager {
|
|
|
63
63
|
}
|
|
64
64
|
const senderKey = await this.ensureSenderKey(groupId, sender);
|
|
65
65
|
const timestampMs = Date.now();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const distributions = new Array(participants.length);
|
|
67
|
+
for (let index = 0; index < participants.length; index += 1) {
|
|
68
|
+
distributions[index] = {
|
|
69
|
+
groupId,
|
|
70
|
+
sender: participants[index],
|
|
71
|
+
keyId: senderKey.keyId,
|
|
72
|
+
timestampMs
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
await this.store.upsertSenderKeyDistributions(distributions);
|
|
72
76
|
}
|
|
73
77
|
async processSenderKeyDistributionPayload(groupId, sender, payload) {
|
|
74
78
|
if (groupId.length === 0) {
|
|
@@ -84,13 +88,15 @@ export class SenderKeyManager {
|
|
|
84
88
|
signingPublicKey: parsed.signingPublicKey,
|
|
85
89
|
unusedMessageKeys: []
|
|
86
90
|
};
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
await Promise.all([
|
|
92
|
+
this.store.upsertSenderKey(record),
|
|
93
|
+
this.store.upsertSenderKeyDistribution({
|
|
94
|
+
groupId,
|
|
95
|
+
sender,
|
|
96
|
+
keyId: parsed.keyId,
|
|
97
|
+
timestampMs: Date.now()
|
|
98
|
+
})
|
|
99
|
+
]);
|
|
94
100
|
return record;
|
|
95
101
|
}
|
|
96
102
|
async encryptGroupMessage(groupId, sender, plaintext) {
|
|
@@ -159,13 +165,17 @@ export class SenderKeyManager {
|
|
|
159
165
|
if (existing) {
|
|
160
166
|
return existing;
|
|
161
167
|
}
|
|
162
|
-
const signingKeyPair = await
|
|
168
|
+
const [signingKeyPair, keyId, chainKey] = await Promise.all([
|
|
169
|
+
X25519.generateKeyPair(),
|
|
170
|
+
randomIntAsync(1, 2147483647),
|
|
171
|
+
randomBytesAsync(32)
|
|
172
|
+
]);
|
|
163
173
|
const created = {
|
|
164
174
|
groupId,
|
|
165
175
|
sender,
|
|
166
|
-
keyId
|
|
176
|
+
keyId,
|
|
167
177
|
iteration: 0,
|
|
168
|
-
chainKey
|
|
178
|
+
chainKey,
|
|
169
179
|
signingPublicKey: toSerializedPubKey(signingKeyPair.pubKey),
|
|
170
180
|
signingPrivateKey: signingKeyPair.privKey,
|
|
171
181
|
unusedMessageKeys: []
|
package/dist/esm/signal/index.js
CHANGED
|
@@ -9,3 +9,4 @@ export { SignalSessionSyncApi } from './api/SignalSessionSyncApi.js';
|
|
|
9
9
|
export { SenderKeyManager } from './group/SenderKeyManager.js';
|
|
10
10
|
export { createAndStoreInitialKeys } from './registration/utils.js';
|
|
11
11
|
export { SignalProtocol } from './session/SignalProtocol.js';
|
|
12
|
+
export { createSignalSessionResolver } from './session/resolver.js';
|
|
@@ -5,6 +5,9 @@ import { decryptMsg, decryptMsgFromSession, encryptMsg } from '../session/Signal
|
|
|
5
5
|
import { deserializeMsg, deserializePkMsg, requirePreKey, requireSignedPreKey } from '../session/SignalSerializer.js';
|
|
6
6
|
import { detachSession, findMatchingSession, generateSerializedKeyPair, initiateSessionIncoming, initiateSessionOutgoing, requireLocalIdentity, toSerializedKeyPair } from '../session/SignalSession.js';
|
|
7
7
|
import { uint8Equal } from '../../util/bytes.js';
|
|
8
|
+
function signalAddressMapKey(address) {
|
|
9
|
+
return `${address.user}\u0001${address.server ?? ''}\u0001${address.device}`;
|
|
10
|
+
}
|
|
8
11
|
export class SignalProtocol {
|
|
9
12
|
constructor(store, logger = new ConsoleLogger('info')) {
|
|
10
13
|
this.store = store;
|
|
@@ -27,23 +30,55 @@ export class SignalProtocol {
|
|
|
27
30
|
return session;
|
|
28
31
|
}
|
|
29
32
|
async encryptMessage(address, plaintext, expectedIdentity) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const [encrypted] = await this.encryptMessagesBatch([
|
|
34
|
+
{ address, plaintext, expectedIdentity }
|
|
35
|
+
]);
|
|
36
|
+
return encrypted;
|
|
37
|
+
}
|
|
38
|
+
async encryptMessagesBatch(requests) {
|
|
39
|
+
if (requests.length === 0) {
|
|
40
|
+
return [];
|
|
33
41
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
const addresses = requests.map((request) => request.address);
|
|
43
|
+
const storedSessions = await this.store.getSessionsBatch(addresses);
|
|
44
|
+
const latestSessionByAddress = new Map();
|
|
45
|
+
const sessionUpdatesByAddress = new Map();
|
|
46
|
+
const identityUpdatesByAddress = new Map();
|
|
47
|
+
const results = new Array(requests.length);
|
|
48
|
+
for (let index = 0; index < requests.length; index += 1) {
|
|
49
|
+
const request = requests[index];
|
|
50
|
+
const address = request.address;
|
|
51
|
+
const addressKey = signalAddressMapKey(address);
|
|
52
|
+
const session = latestSessionByAddress.get(addressKey) ?? storedSessions[index];
|
|
53
|
+
if (!session) {
|
|
54
|
+
throw new Error('signal session not found');
|
|
55
|
+
}
|
|
56
|
+
if (request.expectedIdentity &&
|
|
57
|
+
!uint8Equal(toSerializedPubKey(request.expectedIdentity), session.remote.pubKey)) {
|
|
58
|
+
throw new Error('identity mismatch');
|
|
59
|
+
}
|
|
60
|
+
const [updatedSession, encrypted] = await encryptMsg(session, request.plaintext);
|
|
61
|
+
latestSessionByAddress.set(addressKey, updatedSession);
|
|
62
|
+
sessionUpdatesByAddress.set(addressKey, {
|
|
63
|
+
address,
|
|
64
|
+
session: updatedSession
|
|
65
|
+
});
|
|
66
|
+
if (!uint8Equal(updatedSession.remote.pubKey, session.remote.pubKey)) {
|
|
67
|
+
identityUpdatesByAddress.set(addressKey, {
|
|
68
|
+
address,
|
|
69
|
+
identityKey: updatedSession.remote.pubKey
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
results[index] = {
|
|
73
|
+
...encrypted,
|
|
74
|
+
baseKey: updatedSession.aliceBaseKey
|
|
75
|
+
};
|
|
37
76
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
await this.store.setRemoteIdentity(address, updatedSession.remote.pubKey);
|
|
77
|
+
await this.store.setSessionsBatch([...sessionUpdatesByAddress.values()]);
|
|
78
|
+
if (identityUpdatesByAddress.size > 0) {
|
|
79
|
+
await this.store.setRemoteIdentities([...identityUpdatesByAddress.values()]);
|
|
42
80
|
}
|
|
43
|
-
return
|
|
44
|
-
...encrypted,
|
|
45
|
-
baseKey: updatedSession.aliceBaseKey
|
|
46
|
-
};
|
|
81
|
+
return results;
|
|
47
82
|
}
|
|
48
83
|
async decryptMessage(address, envelope) {
|
|
49
84
|
const currentSession = await this.store.getSession(address);
|
|
@@ -51,24 +51,33 @@ export async function selectMessageKey(chain, targetCounter) {
|
|
|
51
51
|
const first = await deriveMsgKeyFromState(chain.nextMsgIndex, chainState);
|
|
52
52
|
let currentMessageKey = first.messageKey;
|
|
53
53
|
chainState = first.nextState;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
if (delta === 0) {
|
|
55
|
+
return {
|
|
56
|
+
messageKey: currentMessageKey,
|
|
57
|
+
updatedChain: {
|
|
58
|
+
ratchetPubKey: chain.ratchetPubKey,
|
|
59
|
+
nextMsgIndex: targetCounter + 1,
|
|
60
|
+
chainKey: chainState.chainKey,
|
|
61
|
+
unusedMsgKeys: unused
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const nextUnused = unused.slice();
|
|
66
|
+
let overflow = delta + unused.length - MAX_UNUSED_KEYS;
|
|
67
|
+
if (overflow > 0) {
|
|
68
|
+
nextUnused.splice(0, overflow);
|
|
69
|
+
overflow -= unused.length;
|
|
70
|
+
}
|
|
71
|
+
for (let counter = chain.nextMsgIndex + 1; counter <= targetCounter; counter += 1) {
|
|
57
72
|
if (overflow > 0) {
|
|
58
|
-
|
|
59
|
-
overflow -= unused.length;
|
|
73
|
+
overflow -= 1;
|
|
60
74
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
overflow -= 1;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
nextUnused.push(currentMessageKey);
|
|
67
|
-
}
|
|
68
|
-
const derived = await deriveMsgKeyFromState(counter, chainState);
|
|
69
|
-
currentMessageKey = derived.messageKey;
|
|
70
|
-
chainState = derived.nextState;
|
|
75
|
+
else {
|
|
76
|
+
nextUnused.push(currentMessageKey);
|
|
71
77
|
}
|
|
78
|
+
const derived = await deriveMsgKeyFromState(counter, chainState);
|
|
79
|
+
currentMessageKey = derived.messageKey;
|
|
80
|
+
chainState = derived.nextState;
|
|
72
81
|
}
|
|
73
82
|
return {
|
|
74
83
|
messageKey: currentMessageKey,
|
|
@@ -20,16 +20,21 @@ export function findMatchingSession(session, sessionBaseKey) {
|
|
|
20
20
|
if (session.aliceBaseKey && uint8Equal(session.aliceBaseKey, serializedBaseKey)) {
|
|
21
21
|
return session;
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
for (let index = 0; index < session.prevSessions.length; index += 1) {
|
|
24
|
+
const previousSession = session.prevSessions[index];
|
|
25
|
+
if (!previousSession.aliceBaseKey ||
|
|
26
|
+
!uint8Equal(previousSession.aliceBaseKey, serializedBaseKey)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const prevSessions = [detachSession(session)];
|
|
30
|
+
for (let i = 0; i < session.prevSessions.length; i += 1) {
|
|
31
|
+
if (i !== index) {
|
|
32
|
+
prevSessions.push(session.prevSessions[i]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
26
35
|
return {
|
|
27
|
-
...
|
|
28
|
-
prevSessions
|
|
29
|
-
detachSession(session),
|
|
30
|
-
...session.prevSessions.slice(0, previousSessionIndex),
|
|
31
|
-
...session.prevSessions.slice(previousSessionIndex + 1)
|
|
32
|
-
]
|
|
36
|
+
...previousSession,
|
|
37
|
+
prevSessions
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
return null;
|