@peers-app/peers-sdk 0.27.3 → 0.27.5
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/dist/contracts/contract-proxy.d.ts +7 -0
- package/dist/contracts/contract-proxy.js +19 -2
- package/dist/contracts/contract-transport.d.ts +2 -1
- package/dist/contracts/contract-transport.js +24 -2
- package/dist/contracts/create-local-consumer.js +16 -1
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.js +2 -0
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +2 -0
- package/dist/contracts/system/invites-contract.js +2 -1
- package/dist/contracts/system/sync-check-contract.d.ts +97 -0
- package/dist/contracts/system/sync-check-contract.js +115 -0
- package/dist/contracts/tool-arguments.d.ts +15 -0
- package/dist/contracts/tool-arguments.js +41 -0
- package/dist/contracts/tool-catalog-service.js +2 -12
- package/dist/contracts/tool-catalog.js +6 -2
- package/dist/data/change-tracking.d.ts +40 -0
- package/dist/data/change-tracking.js +89 -0
- package/dist/data/orm/types.d.ts +24 -7
- package/dist/data/orm/types.js +97 -24
- package/dist/define/manifest.js +6 -34
- package/dist/device/binary-peer-connection-v2.d.ts +7 -15
- package/dist/device/binary-peer-connection-v2.js +14 -30
- package/dist/device/connection.d.ts +34 -43
- package/dist/device/connection.js +188 -186
- package/dist/device/device.d.ts +1 -8
- package/dist/device/device.js +1 -30
- package/dist/device/socket.type.d.ts +6 -12
- package/dist/package-loader/package-loader.js +27 -22
- package/dist/system-ids.d.ts +5 -0
- package/dist/system-ids.js +6 -1
- package/package.json +4 -2
- package/dist/context/user-context-identity.test.d.ts +0 -1
- package/dist/context/user-context-identity.test.js +0 -220
- package/dist/contracts/__tests__/action-dispatch.test.d.ts +0 -1
- package/dist/contracts/__tests__/action-dispatch.test.js +0 -319
- package/dist/contracts/__tests__/also-implements.test.d.ts +0 -1
- package/dist/contracts/__tests__/also-implements.test.js +0 -110
- package/dist/contracts/__tests__/builder.test.d.ts +0 -1
- package/dist/contracts/__tests__/builder.test.js +0 -669
- package/dist/contracts/__tests__/contract-events.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-events.test.js +0 -199
- package/dist/contracts/__tests__/contract-observables.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-observables.test.js +0 -290
- package/dist/contracts/__tests__/contract-provider-router.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-provider-router.test.js +0 -315
- package/dist/contracts/__tests__/contract-proxy.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-proxy.test.js +0 -657
- package/dist/contracts/__tests__/contract-resolvers.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-resolvers.test.js +0 -758
- package/dist/contracts/__tests__/contract-subscription-lifecycle.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-subscription-lifecycle.test.js +0 -353
- package/dist/contracts/__tests__/contract-table-events.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-table-events.test.js +0 -209
- package/dist/contracts/__tests__/contract-transport.test.d.ts +0 -1
- package/dist/contracts/__tests__/contract-transport.test.js +0 -163
- package/dist/contracts/__tests__/extract.test.d.ts +0 -1
- package/dist/contracts/__tests__/extract.test.js +0 -145
- package/dist/contracts/__tests__/frozen-contracts.test.d.ts +0 -1
- package/dist/contracts/__tests__/frozen-contracts.test.js +0 -191
- package/dist/contracts/__tests__/generic-tool-dispatch.test.d.ts +0 -1
- package/dist/contracts/__tests__/generic-tool-dispatch.test.js +0 -130
- package/dist/contracts/__tests__/host-contract-client.test.d.ts +0 -1
- package/dist/contracts/__tests__/host-contract-client.test.js +0 -793
- package/dist/contracts/__tests__/host-contract-consumer.test.d.ts +0 -1
- package/dist/contracts/__tests__/host-contract-consumer.test.js +0 -163
- package/dist/contracts/__tests__/host-contract-transport.test.d.ts +0 -1
- package/dist/contracts/__tests__/host-contract-transport.test.js +0 -66
- package/dist/contracts/__tests__/integration.test.d.ts +0 -1
- package/dist/contracts/__tests__/integration.test.js +0 -348
- package/dist/contracts/__tests__/legacy-removal.test.d.ts +0 -1
- package/dist/contracts/__tests__/legacy-removal.test.js +0 -128
- package/dist/contracts/__tests__/persistent-registry.test.d.ts +0 -1
- package/dist/contracts/__tests__/persistent-registry.test.js +0 -109
- package/dist/contracts/__tests__/playground.test.d.ts +0 -1
- package/dist/contracts/__tests__/playground.test.js +0 -216
- package/dist/contracts/__tests__/registry.test.d.ts +0 -1
- package/dist/contracts/__tests__/registry.test.js +0 -238
- package/dist/contracts/__tests__/remote-contract-transport.test.d.ts +0 -1
- package/dist/contracts/__tests__/remote-contract-transport.test.js +0 -119
- package/dist/contracts/__tests__/system-contracts.test.d.ts +0 -1
- package/dist/contracts/__tests__/system-contracts.test.js +0 -247
- package/dist/contracts/__tests__/target-dispatch.d.ts +0 -11
- package/dist/contracts/__tests__/target-dispatch.js +0 -24
- package/dist/contracts/__tests__/tool-catalog-inventory.test.d.ts +0 -1
- package/dist/contracts/__tests__/tool-catalog-inventory.test.js +0 -44
- package/dist/contracts/__tests__/tool-catalog-service.test.d.ts +0 -1
- package/dist/contracts/__tests__/tool-catalog-service.test.js +0 -482
- package/dist/contracts/__tests__/tool-catalog.test.d.ts +0 -1
- package/dist/contracts/__tests__/tool-catalog.test.js +0 -1199
- package/dist/contracts/__tests__/validate.test.d.ts +0 -1
- package/dist/contracts/__tests__/validate.test.js +0 -991
- package/dist/contracts/__tests__/workflow-context.test.d.ts +0 -1
- package/dist/contracts/__tests__/workflow-context.test.js +0 -50
- package/dist/data/access-level.test.d.ts +0 -1
- package/dist/data/access-level.test.js +0 -53
- package/dist/data/change-tracking.test.d.ts +0 -1
- package/dist/data/change-tracking.test.js +0 -62
- package/dist/data/files/files.test.d.ts +0 -1
- package/dist/data/files/files.test.js +0 -770
- package/dist/data/group-members.test.d.ts +0 -1
- package/dist/data/group-members.test.js +0 -336
- package/dist/data/group-share.test.d.ts +0 -1
- package/dist/data/group-share.test.js +0 -110
- package/dist/data/groups.test.d.ts +0 -1
- package/dist/data/groups.test.js +0 -153
- package/dist/data/orm/cursor.test.d.ts +0 -1
- package/dist/data/orm/cursor.test.js +0 -314
- package/dist/data/orm/data-query.mongo.test.d.ts +0 -1
- package/dist/data/orm/data-query.mongo.test.js +0 -367
- package/dist/data/orm/data-query.sqlite.test.d.ts +0 -1
- package/dist/data/orm/data-query.sqlite.test.js +0 -365
- package/dist/data/orm/data-query.test.d.ts +0 -1
- package/dist/data/orm/data-query.test.js +0 -545
- package/dist/data/orm/dependency-injection.test.d.ts +0 -1
- package/dist/data/orm/dependency-injection.test.js +0 -171
- package/dist/data/orm/event-registry.test.d.ts +0 -1
- package/dist/data/orm/event-registry.test.js +0 -44
- package/dist/data/orm/multi-cursors.test.d.ts +0 -1
- package/dist/data/orm/multi-cursors.test.js +0 -463
- package/dist/data/orm/sql.data-source.test.d.ts +0 -1
- package/dist/data/orm/sql.data-source.test.js +0 -731
- package/dist/data/orm/table-container-events.test.d.ts +0 -1
- package/dist/data/orm/table-container-events.test.js +0 -101
- package/dist/data/orm/table-container.test.d.ts +0 -1
- package/dist/data/orm/table-container.test.js +0 -273
- package/dist/data/orm/table.event-source.test.d.ts +0 -1
- package/dist/data/orm/table.event-source.test.js +0 -331
- package/dist/data/orm/types.test.d.ts +0 -1
- package/dist/data/orm/types.test.js +0 -409
- package/dist/data/package-table-identity.test.d.ts +0 -1
- package/dist/data/package-table-identity.test.js +0 -46
- package/dist/data/package-version-permissions.test.d.ts +0 -1
- package/dist/data/package-version-permissions.test.js +0 -107
- package/dist/data/package-version-resolver.test.d.ts +0 -4
- package/dist/data/package-version-resolver.test.js +0 -543
- package/dist/data/package-versions.test.d.ts +0 -1
- package/dist/data/package-versions.test.js +0 -227
- package/dist/data/persistent-vars.conflict.test.d.ts +0 -1
- package/dist/data/persistent-vars.conflict.test.js +0 -126
- package/dist/data/persistent-vars.migration.test.d.ts +0 -1
- package/dist/data/persistent-vars.migration.test.js +0 -133
- package/dist/data/persistent-vars.package-scope.test.d.ts +0 -1
- package/dist/data/persistent-vars.package-scope.test.js +0 -27
- package/dist/data/sync-recovery-queue.test.d.ts +0 -1
- package/dist/data/sync-recovery-queue.test.js +0 -215
- package/dist/data/table-signing.test.d.ts +0 -1
- package/dist/data/table-signing.test.js +0 -118
- package/dist/data/tool-access.test.d.ts +0 -1
- package/dist/data/tool-access.test.js +0 -166
- package/dist/data/user-permissions.test.d.ts +0 -1
- package/dist/data/user-permissions.test.js +0 -305
- package/dist/data/user-trust-levels.test.d.ts +0 -1
- package/dist/data/user-trust-levels.test.js +0 -37
- package/dist/define/also-implements.test.d.ts +0 -1
- package/dist/define/also-implements.test.js +0 -123
- package/dist/define/declare-contract.test.d.ts +0 -1
- package/dist/define/declare-contract.test.js +0 -573
- package/dist/define/define.test.d.ts +0 -1
- package/dist/define/define.test.js +0 -1010
- package/dist/define/empty-fields.test.d.ts +0 -1
- package/dist/define/empty-fields.test.js +0 -55
- package/dist/define/events-observables.test.d.ts +0 -1
- package/dist/define/events-observables.test.js +0 -1013
- package/dist/define/serve-members.test.d.ts +0 -1
- package/dist/define/serve-members.test.js +0 -630
- package/dist/define/vars.test.d.ts +0 -1
- package/dist/define/vars.test.js +0 -39
- package/dist/device/binary-peer-connection-v2.test.d.ts +0 -1
- package/dist/device/binary-peer-connection-v2.test.js +0 -730
- package/dist/device/binary-peer-connection.d.ts +0 -58
- package/dist/device/binary-peer-connection.js +0 -516
- package/dist/device/binary-peer-connection.test.d.ts +0 -1
- package/dist/device/binary-peer-connection.test.js +0 -184
- package/dist/device/connection.test.d.ts +0 -1
- package/dist/device/connection.test.js +0 -613
- package/dist/device/device-election.test.d.ts +0 -1
- package/dist/device/device-election.test.js +0 -516
- package/dist/device/device.test.d.ts +0 -1
- package/dist/device/device.test.js +0 -243
- package/dist/device/frame-cipher.test.d.ts +0 -1
- package/dist/device/frame-cipher.test.js +0 -219
- package/dist/device/get-trust-level-fn.test.d.ts +0 -1
- package/dist/device/get-trust-level-fn.test.js +0 -685
- package/dist/device/streamed-socket.d.ts +0 -23
- package/dist/device/streamed-socket.js +0 -255
- package/dist/device/streamed-socket.test.d.ts +0 -1
- package/dist/device/streamed-socket.test.js +0 -216
- package/dist/device/tx-encoding.test.d.ts +0 -1
- package/dist/device/tx-encoding.test.js +0 -295
- package/dist/device-pairing/cpace.test.d.ts +0 -1
- package/dist/device-pairing/cpace.test.js +0 -116
- package/dist/device-pairing/device-pairing.test.d.ts +0 -1
- package/dist/device-pairing/device-pairing.test.js +0 -211
- package/dist/hlc.test.d.ts +0 -1
- package/dist/hlc.test.js +0 -45
- package/dist/host-credentials.test.d.ts +0 -1
- package/dist/host-credentials.test.js +0 -88
- package/dist/identity/account-email.test.d.ts +0 -1
- package/dist/identity/account-email.test.js +0 -59
- package/dist/identity/anchor-document.test.d.ts +0 -1
- package/dist/identity/anchor-document.test.js +0 -16
- package/dist/identity/anchor-resolution.test.d.ts +0 -1
- package/dist/identity/anchor-resolution.test.js +0 -130
- package/dist/identity/key-change.test.d.ts +0 -1
- package/dist/identity/key-change.test.js +0 -811
- package/dist/invites/group-approval.test.d.ts +0 -1
- package/dist/invites/group-approval.test.js +0 -223
- package/dist/invites/invite-token.test.d.ts +0 -1
- package/dist/invites/invite-token.test.js +0 -123
- package/dist/keys.test.d.ts +0 -1
- package/dist/keys.test.js +0 -336
- package/dist/logging/console-logger.test.d.ts +0 -1
- package/dist/logging/console-logger.test.js +0 -300
- package/dist/logging/console-logs.table.test.d.ts +0 -1
- package/dist/logging/console-logs.table.test.js +0 -156
- package/dist/observable.test.d.ts +0 -1
- package/dist/observable.test.js +0 -309
- package/dist/package-build/build-package.test.d.ts +0 -1
- package/dist/package-build/build-package.test.js +0 -204
- package/dist/package-build/contract-add.test.d.ts +0 -1
- package/dist/package-build/contract-add.test.js +0 -467
- package/dist/package-build/freeze-contracts.test.d.ts +0 -1
- package/dist/package-build/freeze-contracts.test.js +0 -355
- package/dist/package-installer/package-author-signing.test.d.ts +0 -1
- package/dist/package-installer/package-author-signing.test.js +0 -200
- package/dist/package-installer/package-cloner.test.d.ts +0 -1
- package/dist/package-installer/package-cloner.test.js +0 -278
- package/dist/package-installer/package-creator.test.d.ts +0 -1
- package/dist/package-installer/package-creator.test.js +0 -908
- package/dist/package-installer/package-dev-tools.test.d.ts +0 -1
- package/dist/package-installer/package-dev-tools.test.js +0 -64
- package/dist/package-installer/package-installer.test.d.ts +0 -1
- package/dist/package-installer/package-installer.test.js +0 -882
- package/dist/package-installer/package-propagation.test.d.ts +0 -1
- package/dist/package-installer/package-propagation.test.js +0 -1269
- package/dist/package-installer/package-publisher.test.d.ts +0 -1
- package/dist/package-installer/package-publisher.test.js +0 -153
- package/dist/package-installer/package-remote-checker.test.d.ts +0 -1
- package/dist/package-installer/package-remote-checker.test.js +0 -334
- package/dist/package-installer/package-seed-installer.test.d.ts +0 -1
- package/dist/package-installer/package-seed-installer.test.js +0 -123
- package/dist/package-installer/package-tarball.test.d.ts +0 -1
- package/dist/package-installer/package-tarball.test.js +0 -75
- package/dist/package-installer/peers-core-installer.test.d.ts +0 -1
- package/dist/package-installer/peers-core-installer.test.js +0 -49
- package/dist/package-installer/peers-core-trust-anchor.test.d.ts +0 -1
- package/dist/package-installer/peers-core-trust-anchor.test.js +0 -155
- package/dist/package-loader/contract-package-loader.test.d.ts +0 -1
- package/dist/package-loader/contract-package-loader.test.js +0 -274
- package/dist/package-loader/host-authority-loading.test.d.ts +0 -1
- package/dist/package-loader/host-authority-loading.test.js +0 -104
- package/dist/package-loader/host-dispatch-policy.test.d.ts +0 -1
- package/dist/package-loader/host-dispatch-policy.test.js +0 -56
- package/dist/package-loader/isolated-artifact.test.d.ts +0 -1
- package/dist/package-loader/isolated-artifact.test.js +0 -688
- package/dist/package-loader/isolated-persistent-var-gateway.test.d.ts +0 -1
- package/dist/package-loader/isolated-persistent-var-gateway.test.js +0 -56
- package/dist/package-loader/isolated-table-gateway.test.d.ts +0 -1
- package/dist/package-loader/isolated-table-gateway.test.js +0 -633
- package/dist/package-loader/package-loader.test.d.ts +0 -1
- package/dist/package-loader/package-loader.test.js +0 -1764
- package/dist/package-loader/resolve-consumed-contracts.test.d.ts +0 -1
- package/dist/package-loader/resolve-consumed-contracts.test.js +0 -166
- package/dist/serial-json.compat.test.d.ts +0 -1
- package/dist/serial-json.compat.test.js +0 -369
- package/dist/serial-json.test.d.ts +0 -1
- package/dist/serial-json.test.js +0 -401
- package/dist/testing/mock-host-contract.test.d.ts +0 -1
- package/dist/testing/mock-host-contract.test.js +0 -782
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -232
|
@@ -14,12 +14,10 @@ const socket_type_1 = require("./socket.type");
|
|
|
14
14
|
* Authenticated RPC connection between two Peers devices.
|
|
15
15
|
*
|
|
16
16
|
* The handshake (`doHandshake` on the client, `completeHandshake` on the
|
|
17
|
-
* server) authenticates both identities with signed, boxed messages
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Against a peer that does not advertise `frameCrypto` the connection falls
|
|
22
|
-
* back to the legacy behaviour and logs a warning.
|
|
17
|
+
* server) authenticates both identities with signed, boxed messages and agrees
|
|
18
|
+
* on per-connection frame-cipher keys (`frame-cipher.ts`), then arms the
|
|
19
|
+
* socket: every frame after the handshake is sealed. A peer that does not
|
|
20
|
+
* offer frame encryption (a build before 0.25.7) is refused.
|
|
23
21
|
*/
|
|
24
22
|
class Connection {
|
|
25
23
|
localDevice;
|
|
@@ -32,23 +30,9 @@ class Connection {
|
|
|
32
30
|
publicKey: "",
|
|
33
31
|
publicBoxKey: "",
|
|
34
32
|
};
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated Legacy transport classification, only consulted when the frame
|
|
37
|
-
* cipher is not armed. Removed once legacy peers are no longer supported.
|
|
38
|
-
*/
|
|
39
|
-
secureLocal = false;
|
|
40
|
-
/** @deprecated See {@link Connection.secureLocal}. */
|
|
41
|
-
secureRemote = false;
|
|
42
|
-
/**
|
|
43
|
-
* Legacy per-message encryption override. Ignored once the frame cipher is
|
|
44
|
-
* armed. `"dont-encrypt-traffic-after-handshake"` is honoured only under
|
|
45
|
-
* `NODE_ENV=test`.
|
|
46
|
-
* @deprecated Removed with the legacy path.
|
|
47
|
-
*/
|
|
48
|
-
overrideEncryption;
|
|
49
33
|
/** Trust assigned to the remote identity once the handshake completes. */
|
|
50
34
|
trustLevel = socket_type_1.TrustLevel.Unknown;
|
|
51
|
-
/** Server-side hook fired shortly after
|
|
35
|
+
/** Server-side hook fired shortly after the successful handshake reply has been sent. */
|
|
52
36
|
onHandshakeComplete;
|
|
53
37
|
/** RPC timeout for {@link Connection.emit}. */
|
|
54
38
|
timeoutMs = 60_000; // 60 seconds
|
|
@@ -66,6 +50,8 @@ class Connection {
|
|
|
66
50
|
pendingClientHandshake;
|
|
67
51
|
/** Server side: keys to arm for sending right after the handshake response frame. */
|
|
68
52
|
pendingSendArm;
|
|
53
|
+
/** Bumped by every {@link Connection.reset}, so a handshake can tell it was cut short. */
|
|
54
|
+
handshakeGeneration = 0;
|
|
69
55
|
constructor(socket, localDevice, localDeviceServerAddresses, getTrustLevel = () => Promise.resolve(socket_type_1.TrustLevel.Unknown)) {
|
|
70
56
|
this.localDevice = localDevice;
|
|
71
57
|
this.localDeviceServerAddresses = localDeviceServerAddresses;
|
|
@@ -76,7 +62,7 @@ class Connection {
|
|
|
76
62
|
if (!socket.id) {
|
|
77
63
|
socket.id = (0, utils_1.newid)();
|
|
78
64
|
}
|
|
79
|
-
//
|
|
65
|
+
// Stream frames bypass RPC verification; only sealed ones may reach a Connection.
|
|
80
66
|
socket.setPlaintextStreamsAllowed?.(false);
|
|
81
67
|
// Only the server side of a connection should expose these methods
|
|
82
68
|
this.exposeRPC("close", () => {
|
|
@@ -100,10 +86,6 @@ class Connection {
|
|
|
100
86
|
// carries the server's ephemeral key), and the reply callback needs the
|
|
101
87
|
// transport hint that arms the send cipher after this exact frame.
|
|
102
88
|
this.socket.on("completeHandshake", this.handleCompleteHandshakeRPC);
|
|
103
|
-
this.exposeRPC("requestSecure", () => {
|
|
104
|
-
this.secureRemote = true;
|
|
105
|
-
return !this.encryptTraffic;
|
|
106
|
-
});
|
|
107
89
|
}
|
|
108
90
|
}
|
|
109
91
|
/** True once the remote identity has been authenticated by the handshake. */
|
|
@@ -114,29 +96,6 @@ class Connection {
|
|
|
114
96
|
get frameCipherArmed() {
|
|
115
97
|
return this.socket.frameCipherArmed === true;
|
|
116
98
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Whether to apply the legacy per-message envelope (sign + box) to RPC payloads.
|
|
119
|
-
*
|
|
120
|
-
* Always false once the frame cipher is armed: the frame layer already gives
|
|
121
|
-
* confidentiality, integrity, and sender binding for every frame. Otherwise
|
|
122
|
-
* (legacy peer) the transport classification below applies:
|
|
123
|
-
* - skipped when both sides agreed the transport is secure (`secureLocal && secureRemote`)
|
|
124
|
-
* - applied on plain `ws` / `http`
|
|
125
|
-
*/
|
|
126
|
-
get encryptTraffic() {
|
|
127
|
-
if (this.socket.frameCipherArmed) {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
if (this.overrideEncryption === "dont-encrypt-traffic-after-handshake" &&
|
|
131
|
-
process.env.NODE_ENV === "test") {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
if (this.overrideEncryption === "encrypt-traffic-regardless-of-protocol") {
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
// Encrypt only if EITHER side is using an insecure protocol
|
|
138
|
-
return !(this.secureLocal && this.secureRemote);
|
|
139
|
-
}
|
|
140
99
|
/** Copy of the authenticated remote device identity (empty before the handshake). */
|
|
141
100
|
get remoteDeviceInfo() {
|
|
142
101
|
return { ...this._remoteDeviceInfo };
|
|
@@ -145,20 +104,33 @@ class Connection {
|
|
|
145
104
|
get connectionId() {
|
|
146
105
|
return this.socket.id || "";
|
|
147
106
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
107
|
+
/** The socket's frame-cipher arming; throws when the transport can't encrypt frames. */
|
|
108
|
+
requireFrameCipher() {
|
|
109
|
+
const arm = this.socket.armFrameCipher;
|
|
110
|
+
if (typeof arm !== "function") {
|
|
111
|
+
throw new Error("This transport can't encrypt frames, so it can't carry a connection");
|
|
112
|
+
}
|
|
113
|
+
return arm.bind(this.socket);
|
|
153
114
|
}
|
|
154
115
|
handleCompleteHandshakeRPC = async (args, callback) => {
|
|
155
116
|
try {
|
|
117
|
+
const generation = this.handshakeGeneration;
|
|
156
118
|
const [handshakeBox] = args;
|
|
157
119
|
const response = await this.completeHandshake(handshakeBox);
|
|
120
|
+
this.assertHandshakeCurrent(generation);
|
|
158
121
|
const boxed = this.localDevice.signAndBoxDataForDevice(response, this._remoteDeviceInfo);
|
|
159
122
|
const armAfter = this.pendingSendArm;
|
|
160
123
|
this.pendingSendArm = undefined;
|
|
161
|
-
callback(null, boxed, armAfter ? { armFrameCipherAfter: armAfter } : undefined);
|
|
124
|
+
await callback(null, boxed, armAfter ? { armFrameCipherAfter: armAfter } : undefined);
|
|
125
|
+
// The reply may wait on backpressure or span several frames. Start application RPCs
|
|
126
|
+
// only once the final frame has gone out and the send cipher has armed.
|
|
127
|
+
if (this.onHandshakeComplete) {
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
if (generation === this.handshakeGeneration && this.socket.connected !== false) {
|
|
130
|
+
this.onHandshakeComplete?.(this._remoteDeviceInfo);
|
|
131
|
+
}
|
|
132
|
+
}, 10);
|
|
133
|
+
}
|
|
162
134
|
}
|
|
163
135
|
catch (e) {
|
|
164
136
|
console.error("Error handling completeHandshake", e);
|
|
@@ -239,13 +211,16 @@ class Connection {
|
|
|
239
211
|
}
|
|
240
212
|
/** Calls a remote RPC handler and resolves with its result (rejects on error or timeout). */
|
|
241
213
|
emit(eventName, ...args) {
|
|
214
|
+
return this.request(eventName, args);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Sends one RPC. `onReply`, when given, runs synchronously while the reply frame is
|
|
218
|
+
* processed, before any later frame from the peer; its return value (or throw) settles
|
|
219
|
+
* the call.
|
|
220
|
+
*/
|
|
221
|
+
request(eventName, args, onReply) {
|
|
242
222
|
return new Promise((resolve, reject) => {
|
|
243
223
|
try {
|
|
244
|
-
// Legacy per-message envelope; skipped when the frame cipher is armed
|
|
245
|
-
// or both sides agreed the transport is secure.
|
|
246
|
-
if (this._verified && this.encryptTraffic) {
|
|
247
|
-
args = this.localDevice.signAndBoxDataForDevice(args, this._remoteDeviceInfo);
|
|
248
|
-
}
|
|
249
224
|
const timeoutMs = this.timeoutMs;
|
|
250
225
|
const timeoutPid = setTimeout(() => {
|
|
251
226
|
reject(`${eventName} timed out after ${timeoutMs}ms`);
|
|
@@ -254,16 +229,17 @@ class Connection {
|
|
|
254
229
|
try {
|
|
255
230
|
clearTimeout(timeoutPid);
|
|
256
231
|
if (err) {
|
|
257
|
-
err = this.localDevice.unwrapResponse(err);
|
|
258
232
|
return reject(err);
|
|
259
233
|
}
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
234
|
+
if (onReply) {
|
|
235
|
+
let value;
|
|
236
|
+
try {
|
|
237
|
+
value = onReply(result);
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
return reject(e);
|
|
241
|
+
}
|
|
242
|
+
return resolve(value);
|
|
267
243
|
}
|
|
268
244
|
resolve(result);
|
|
269
245
|
}
|
|
@@ -284,8 +260,7 @@ class Connection {
|
|
|
284
260
|
}
|
|
285
261
|
/**
|
|
286
262
|
* Registers an RPC handler. Events other than the handshake set are rejected
|
|
287
|
-
* until the connection is verified
|
|
288
|
-
* when {@link Connection.encryptTraffic} is true.
|
|
263
|
+
* until the connection is verified.
|
|
289
264
|
*/
|
|
290
265
|
on(eventName, handler) {
|
|
291
266
|
const safeEvents = [
|
|
@@ -300,31 +275,12 @@ class Connection {
|
|
|
300
275
|
console.error(`Ignoring event from unverified connection: ${eventName}`);
|
|
301
276
|
return callback(`Connection not verified. Handshake must be completed successfully before calling ${eventName}`);
|
|
302
277
|
}
|
|
303
|
-
let result;
|
|
304
278
|
try {
|
|
305
|
-
|
|
306
|
-
// or both sides agreed the transport is secure.
|
|
307
|
-
if (this._verified && this.encryptTraffic) {
|
|
308
|
-
args = this.localDevice.openBoxedAndSignedData(args);
|
|
309
|
-
}
|
|
310
|
-
result = await handler(...args);
|
|
311
|
-
if (this._verified && this.encryptTraffic) {
|
|
312
|
-
result = this.localDevice.signAndBoxDataForDevice(result, this._remoteDeviceInfo);
|
|
313
|
-
}
|
|
314
|
-
callback(null, result);
|
|
279
|
+
callback(null, await handler(...args));
|
|
315
280
|
}
|
|
316
281
|
catch (e) {
|
|
317
282
|
console.error(`Error handling ${eventName}(${JSON.stringify(args)})`, e);
|
|
318
|
-
|
|
319
|
-
if (this._verified && this.encryptTraffic) {
|
|
320
|
-
try {
|
|
321
|
-
rpcError = this.localDevice.signAndBoxDataForDevice(rpcError, this._remoteDeviceInfo);
|
|
322
|
-
}
|
|
323
|
-
catch (e) {
|
|
324
|
-
console.error("Error signing and boxing RPC error", e);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
callback(rpcError);
|
|
283
|
+
callback({ error: e.message, errorType: "RPC_ERROR" });
|
|
328
284
|
}
|
|
329
285
|
});
|
|
330
286
|
}
|
|
@@ -346,50 +302,54 @@ class Connection {
|
|
|
346
302
|
publicBoxKey: "",
|
|
347
303
|
};
|
|
348
304
|
this._verified = false;
|
|
349
|
-
this.secureLocal = false;
|
|
350
|
-
this.secureRemote = false;
|
|
351
305
|
this.trustLevel = socket_type_1.TrustLevel.Unknown;
|
|
352
306
|
this.pendingClientHandshake?.ephemeralSecretKey.fill(0);
|
|
353
307
|
this.pendingClientHandshake = undefined;
|
|
354
308
|
this.pendingSendArm = undefined;
|
|
355
|
-
this.
|
|
309
|
+
this.handshakeGeneration++;
|
|
356
310
|
}
|
|
357
311
|
/**
|
|
358
|
-
* Client side: builds the signed handshake (
|
|
359
|
-
*
|
|
360
|
-
* remote device.
|
|
312
|
+
* Client side: builds the signed handshake (offering frame encryption with a
|
|
313
|
+
* fresh ephemeral key) and boxes it to the remote device.
|
|
361
314
|
*/
|
|
362
315
|
initiateHandshake(serverAddress, remoteDeviceInfo) {
|
|
363
316
|
if (remoteDeviceInfo) {
|
|
364
317
|
this._remoteDeviceInfo = remoteDeviceInfo;
|
|
365
318
|
}
|
|
366
|
-
let frameCrypto;
|
|
367
319
|
this.pendingClientHandshake?.ephemeralSecretKey.fill(0);
|
|
368
320
|
this.pendingClientHandshake = undefined;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
321
|
+
this.requireFrameCipher();
|
|
322
|
+
const ephemeral = (0, keys_1.newEphemeralBoxKeyPair)();
|
|
323
|
+
const ephemeralSecretKey = ephemeral.secretKey;
|
|
324
|
+
const frameCrypto = {
|
|
325
|
+
version: 1,
|
|
326
|
+
ephemeralBoxKey: (0, keys_1.encodeBase64)(ephemeral.publicKey),
|
|
327
|
+
};
|
|
328
|
+
try {
|
|
329
|
+
const handshake = this.localDevice.getHandshake(this.connectionId, serverAddress, frameCrypto, (0, key_change_1.peekLocalKeyContest)());
|
|
377
330
|
this.pendingClientHandshake = {
|
|
378
331
|
ephemeralSecretKey,
|
|
379
332
|
timestamp: handshake.contents.timestamp,
|
|
380
333
|
};
|
|
334
|
+
return this.localDevice.boxDataForDevice(handshake, this._remoteDeviceInfo);
|
|
335
|
+
}
|
|
336
|
+
catch (e) {
|
|
337
|
+
ephemeralSecretKey.fill(0);
|
|
338
|
+
this.pendingClientHandshake = undefined;
|
|
339
|
+
throw e;
|
|
381
340
|
}
|
|
382
|
-
return this.localDevice.boxDataForDevice(handshake, this._remoteDeviceInfo);
|
|
383
341
|
}
|
|
384
342
|
/**
|
|
385
343
|
* Server side: verifies the client's handshake, resolves trust, and returns
|
|
386
|
-
* the (unsigned) response for the caller to sign and box.
|
|
387
|
-
* offered `frameCrypto` and this socket can arm a cipher, derives the frame
|
|
344
|
+
* the (unsigned) response for the caller to sign and box. Derives the frame
|
|
388
345
|
* keys, arms the receive direction immediately, and schedules the send
|
|
389
346
|
* direction to arm right after the response frame (see
|
|
390
|
-
* {@link Connection.handleCompleteHandshakeRPC}).
|
|
347
|
+
* {@link Connection.handleCompleteHandshakeRPC}). A client that does not
|
|
348
|
+
* offer frame encryption is refused.
|
|
391
349
|
*/
|
|
392
350
|
async completeHandshake(boxedHandshake) {
|
|
351
|
+
const generation = this.handshakeGeneration;
|
|
352
|
+
this.assertHandshakeCurrent(generation);
|
|
393
353
|
const signedHandshake = this.localDevice.openBoxWithSecretKey(boxedHandshake);
|
|
394
354
|
const _handshake = data_1.deviceHandshakeSchema.parse((0, keys_1.openSignedObject)(signedHandshake));
|
|
395
355
|
const timeDiff = Math.abs(Date.now() - _handshake.timestamp);
|
|
@@ -400,15 +360,17 @@ class Connection {
|
|
|
400
360
|
const thisServerAddress = this.localDeviceServerAddresses?.find((a) => a === clientServerAddress) ||
|
|
401
361
|
this.localDeviceServerAddresses?.[0] ||
|
|
402
362
|
"unknown";
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
363
|
+
if (!_handshake.frameCrypto) {
|
|
364
|
+
throw legacyPeerError(_handshake.deviceId);
|
|
365
|
+
}
|
|
366
|
+
const armFrameCipher = this.requireFrameCipher();
|
|
367
|
+
const ephemeral = (0, keys_1.newEphemeralBoxKeyPair)();
|
|
368
|
+
const frameCrypto = {
|
|
369
|
+
version: 1,
|
|
370
|
+
ephemeralBoxKey: (0, keys_1.encodeBase64)(ephemeral.publicKey),
|
|
371
|
+
};
|
|
408
372
|
let frameKeys;
|
|
409
|
-
|
|
410
|
-
const ephemeral = (0, keys_1.newEphemeralBoxKeyPair)();
|
|
411
|
-
frameCrypto = { version: 1, ephemeralBoxKey: (0, keys_1.encodeBase64)(ephemeral.publicKey) };
|
|
373
|
+
try {
|
|
412
374
|
const derived = this.localDevice.deriveFrameKeys({
|
|
413
375
|
remotePublicBoxKey: _handshake.publicBoxKey,
|
|
414
376
|
ephemeralSecretKey: ephemeral.secretKey,
|
|
@@ -420,8 +382,26 @@ class Connection {
|
|
|
420
382
|
});
|
|
421
383
|
frameKeys = (0, frame_cipher_1.frameKeysForRole)(derived, "server");
|
|
422
384
|
}
|
|
385
|
+
finally {
|
|
386
|
+
// Invalid peer keys can throw before Device.deriveFrameKeys reaches its cleanup.
|
|
387
|
+
ephemeral.secretKey.fill(0);
|
|
388
|
+
}
|
|
423
389
|
const handshakeResponse = this.localDevice.handshakeResponse(signedHandshake, this.connectionId, thisServerAddress, frameCrypto, (0, key_change_1.peekLocalKeyContest)());
|
|
424
390
|
await noteHandshakeContest(_handshake.keyContest);
|
|
391
|
+
this.assertHandshakeCurrent(generation);
|
|
392
|
+
const trustLevel = await this.getTrustLevel(_handshake, true);
|
|
393
|
+
this.assertHandshakeCurrent(generation);
|
|
394
|
+
if (trustLevel < socket_type_1.TrustLevel.Unknown) {
|
|
395
|
+
this.resetRemote();
|
|
396
|
+
throw new Error("Untrusted connection");
|
|
397
|
+
}
|
|
398
|
+
// The client seals everything after it reads our response, so receive
|
|
399
|
+
// can arm now; send arms after the response frame goes out.
|
|
400
|
+
armFrameCipher(frameKeys, "receive");
|
|
401
|
+
this.pendingSendArm = frameKeys;
|
|
402
|
+
// Publish the identity only after arming succeeds; a failed cipher must not
|
|
403
|
+
// leave application RPC handlers accepting an unsealed connection.
|
|
404
|
+
this._connectionAddress = thisServerAddress;
|
|
425
405
|
this._remoteDeviceInfo = {
|
|
426
406
|
userId: _handshake.userId,
|
|
427
407
|
deviceId: _handshake.deviceId,
|
|
@@ -430,57 +410,76 @@ class Connection {
|
|
|
430
410
|
userName: _handshake.userName,
|
|
431
411
|
deviceName: _handshake.deviceName,
|
|
432
412
|
};
|
|
433
|
-
this.trustLevel =
|
|
434
|
-
if (this.trustLevel < socket_type_1.TrustLevel.Unknown) {
|
|
435
|
-
this.resetRemote();
|
|
436
|
-
throw new Error("Untrusted connection");
|
|
437
|
-
}
|
|
413
|
+
this.trustLevel = trustLevel;
|
|
438
414
|
this._verified = true;
|
|
439
|
-
if (frameKeys && this.socket.armFrameCipher) {
|
|
440
|
-
// The client seals everything after it reads our response, so receive
|
|
441
|
-
// can arm now; send arms after the response frame goes out.
|
|
442
|
-
this.socket.armFrameCipher(frameKeys, "receive");
|
|
443
|
-
this.pendingSendArm = frameKeys;
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
this.warnLegacyPeer(_handshake.deviceId);
|
|
447
|
-
this.socket.setPlaintextStreamsAllowed?.(true);
|
|
448
|
-
}
|
|
449
|
-
if (this.onHandshakeComplete) {
|
|
450
|
-
setTimeout(() => {
|
|
451
|
-
this.onHandshakeComplete?.(this._remoteDeviceInfo);
|
|
452
|
-
}, 10);
|
|
453
|
-
}
|
|
454
415
|
return handshakeResponse;
|
|
455
416
|
}
|
|
456
417
|
/**
|
|
457
418
|
* Client side: runs the full handshake against `remoteAddress`. On success
|
|
458
|
-
* the remote identity is verified and
|
|
459
|
-
*
|
|
419
|
+
* the remote identity is verified and the frame cipher is armed in both
|
|
420
|
+
* directions before this resolves.
|
|
460
421
|
*/
|
|
461
422
|
async doHandshake(remoteAddress) {
|
|
462
423
|
remoteAddress = normalizeAddress(remoteAddress);
|
|
463
424
|
this.reset();
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
425
|
+
const generation = this.handshakeGeneration;
|
|
426
|
+
try {
|
|
427
|
+
this.assertHandshakeCurrent(generation);
|
|
428
|
+
await this.emit("reset");
|
|
429
|
+
this.assertHandshakeCurrent(generation);
|
|
430
|
+
const remoteDeviceInfoSigned = await this.emit("requestDeviceInfo");
|
|
431
|
+
this.assertHandshakeCurrent(generation);
|
|
432
|
+
const remoteDeviceInfo = data_1.deviceInfoSchema.parse((0, keys_1.openSignedObject)(remoteDeviceInfoSigned));
|
|
433
|
+
if (remoteDeviceInfoSigned.publicKey !== remoteDeviceInfo.publicKey) {
|
|
434
|
+
throw new Error("Device info signing key does not match claimed identity key");
|
|
435
|
+
}
|
|
436
|
+
if (remoteDeviceInfo.userId === this.localDevice.userId &&
|
|
437
|
+
remoteDeviceInfo.publicKey !== this.localDevice.publicKey) {
|
|
438
|
+
// The server will refuse this handshake. Decide here, while we still have the signed
|
|
439
|
+
// identity, whether the anchors say this device's key was rotated away.
|
|
440
|
+
await (0, key_change_1.detectIdentityRotatedAway)(this.localDevice, remoteDeviceInfo);
|
|
441
|
+
this.assertHandshakeCurrent(generation);
|
|
442
|
+
}
|
|
443
|
+
const handshake = this.initiateHandshake(remoteAddress, remoteDeviceInfo);
|
|
444
|
+
// The server seals every frame after its reply, and a busy client can read the next one
|
|
445
|
+
// in the same batch as the reply. So the reply is checked and the cipher armed while the
|
|
446
|
+
// reply frame is processed, not after an await.
|
|
447
|
+
const handshakeResponse = await this.request("completeHandshake", [handshake], (box) => this.acceptHandshakeReply(box, remoteAddress, remoteDeviceInfo, generation));
|
|
448
|
+
this.assertHandshakeCurrent(generation);
|
|
449
|
+
const currentRemoteDeviceInfo = this.remoteDeviceInfo;
|
|
450
|
+
await noteHandshakeContest(handshakeResponse.keyContest);
|
|
451
|
+
this.assertHandshakeCurrent(generation);
|
|
452
|
+
const trustLevel = await this.getTrustLevel(currentRemoteDeviceInfo, true);
|
|
453
|
+
this.assertHandshakeCurrent(generation);
|
|
454
|
+
this.trustLevel = trustLevel;
|
|
455
|
+
if (this.trustLevel < socket_type_1.TrustLevel.Unknown) {
|
|
456
|
+
this.resetRemote();
|
|
457
|
+
throw new Error("Untrusted connection");
|
|
458
|
+
}
|
|
459
|
+
return handshakeResponse;
|
|
469
460
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
|
|
461
|
+
catch (e) {
|
|
462
|
+
// A failed or timed-out attempt must neither retain its ephemeral secret nor let a
|
|
463
|
+
// late reply authenticate it. Do not clear state belonging to a newer handshake.
|
|
464
|
+
if (generation === this.handshakeGeneration)
|
|
465
|
+
this.reset();
|
|
466
|
+
throw e;
|
|
475
467
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Client side: checks the server's handshake reply, arms the frame cipher, and records the
|
|
471
|
+
* verified identity. Runs synchronously while the reply frame is processed, so a sealed
|
|
472
|
+
* frame read right after it finds the cipher armed and the connection verified.
|
|
473
|
+
*/
|
|
474
|
+
acceptHandshakeReply(handshakeResponseBox, remoteAddress, remoteDeviceInfo, generation) {
|
|
475
|
+
this.assertHandshakeCurrent(generation);
|
|
476
|
+
const signedResponse = this.localDevice.openBoxWithSecretKey(handshakeResponseBox);
|
|
477
|
+
const handshakeResponse = data_1.deviceHandshakeSchema.parse((0, keys_1.openSignedObject)(signedResponse));
|
|
480
478
|
if (handshakeResponse.connectionId !== this.connectionId) {
|
|
481
479
|
throw new Error(`Invalid connectionId ${handshakeResponse.connectionId}, expected ${this.connectionId}`);
|
|
482
480
|
}
|
|
483
|
-
if (
|
|
481
|
+
if (signedResponse.publicKey !== remoteDeviceInfo.publicKey ||
|
|
482
|
+
remoteDeviceInfo.publicKey !== handshakeResponse.publicKey ||
|
|
484
483
|
remoteDeviceInfo.publicBoxKey !== handshakeResponse.publicBoxKey) {
|
|
485
484
|
throw new Error("Inconsistent public keys");
|
|
486
485
|
}
|
|
@@ -488,21 +487,15 @@ class Connection {
|
|
|
488
487
|
remoteDeviceInfo.deviceId !== handshakeResponse.deviceId) {
|
|
489
488
|
throw new Error("Inconsistent device info");
|
|
490
489
|
}
|
|
491
|
-
const currentRemoteDeviceInfo = {
|
|
492
|
-
...remoteDeviceInfo,
|
|
493
|
-
userName: handshakeResponse.userName !== undefined
|
|
494
|
-
? handshakeResponse.userName
|
|
495
|
-
: remoteDeviceInfo.userName,
|
|
496
|
-
deviceName: handshakeResponse.deviceName !== undefined
|
|
497
|
-
? handshakeResponse.deviceName
|
|
498
|
-
: remoteDeviceInfo.deviceName,
|
|
499
|
-
};
|
|
500
|
-
this._remoteDeviceInfo = currentRemoteDeviceInfo;
|
|
501
|
-
this._verified = true;
|
|
502
|
-
this._connectionAddress = remoteAddress;
|
|
503
490
|
const pending = this.pendingClientHandshake;
|
|
504
491
|
this.pendingClientHandshake = undefined;
|
|
505
|
-
|
|
492
|
+
try {
|
|
493
|
+
if (!handshakeResponse.frameCrypto) {
|
|
494
|
+
throw legacyPeerError(handshakeResponse.deviceId);
|
|
495
|
+
}
|
|
496
|
+
if (!pending) {
|
|
497
|
+
throw new Error("No handshake is pending on this connection");
|
|
498
|
+
}
|
|
506
499
|
const derived = this.localDevice.deriveFrameKeys({
|
|
507
500
|
remotePublicBoxKey: handshakeResponse.publicBoxKey,
|
|
508
501
|
ephemeralSecretKey: pending.ephemeralSecretKey,
|
|
@@ -513,29 +506,34 @@ class Connection {
|
|
|
513
506
|
timestamp: pending.timestamp,
|
|
514
507
|
});
|
|
515
508
|
// The server armed receive before replying and send right after; from
|
|
516
|
-
// here on both directions are sealed.
|
|
517
|
-
this.
|
|
509
|
+
// here on both directions are sealed. Do not mark verified if deriving or arming fails.
|
|
510
|
+
this.requireFrameCipher()((0, frame_cipher_1.frameKeysForRole)(derived, "client"));
|
|
518
511
|
}
|
|
519
|
-
|
|
512
|
+
finally {
|
|
520
513
|
pending?.ephemeralSecretKey.fill(0);
|
|
521
|
-
this.warnLegacyPeer(handshakeResponse.deviceId);
|
|
522
|
-
this.socket.setPlaintextStreamsAllowed?.(true);
|
|
523
|
-
}
|
|
524
|
-
// Legacy classification, only consulted when the frame cipher is not armed.
|
|
525
|
-
const secureProtocols = ["https", "wss", "wrtc"];
|
|
526
|
-
this.secureLocal =
|
|
527
|
-
secureProtocols.some((s) => remoteAddress.startsWith(s)) &&
|
|
528
|
-
handshakeResponse.serverAddress === remoteAddress;
|
|
529
|
-
if (this.secureLocal && !this.socket.frameCipherArmed) {
|
|
530
|
-
await this.emit("requestSecure");
|
|
531
|
-
}
|
|
532
|
-
this.trustLevel = await this.getTrustLevel(currentRemoteDeviceInfo, true);
|
|
533
|
-
if (this.trustLevel < socket_type_1.TrustLevel.Unknown) {
|
|
534
|
-
this.resetRemote();
|
|
535
|
-
throw new Error("Untrusted connection");
|
|
536
514
|
}
|
|
515
|
+
this._remoteDeviceInfo = {
|
|
516
|
+
...remoteDeviceInfo,
|
|
517
|
+
userName: handshakeResponse.userName !== undefined
|
|
518
|
+
? handshakeResponse.userName
|
|
519
|
+
: remoteDeviceInfo.userName,
|
|
520
|
+
deviceName: handshakeResponse.deviceName !== undefined
|
|
521
|
+
? handshakeResponse.deviceName
|
|
522
|
+
: remoteDeviceInfo.deviceName,
|
|
523
|
+
};
|
|
524
|
+
this._verified = true;
|
|
525
|
+
this._connectionAddress = remoteAddress;
|
|
537
526
|
return handshakeResponse;
|
|
538
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Throws when the connection was reset or closed after the handshake started: a handshake
|
|
530
|
+
* cut short must fail, not finish on a dead or reused connection.
|
|
531
|
+
*/
|
|
532
|
+
assertHandshakeCurrent(generation) {
|
|
533
|
+
if (generation !== this.handshakeGeneration || this.socket.connected === false) {
|
|
534
|
+
throw new Error("Connection was closed or reset during the handshake");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
539
537
|
/**
|
|
540
538
|
* Resets this side and asks the remote to do the same. The remote call is a courtesy: a
|
|
541
539
|
* peer that is not a server has no `reset` handler, and a peer we just refused may already
|
|
@@ -578,6 +576,10 @@ class Connection {
|
|
|
578
576
|
}
|
|
579
577
|
}
|
|
580
578
|
exports.Connection = Connection;
|
|
579
|
+
/** Refusal for a peer whose build predates frame encryption (0.25.7). */
|
|
580
|
+
function legacyPeerError(deviceId) {
|
|
581
|
+
return new Error(`Device ${deviceId} does not support frame encryption; update Peers on that device to connect`);
|
|
582
|
+
}
|
|
581
583
|
async function noteHandshakeContest(contest) {
|
|
582
584
|
if (!contest)
|
|
583
585
|
return;
|
package/dist/device/device.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export declare class Device implements IDeviceInfo {
|
|
|
49
49
|
boxDataForDevice(data: any, device: IDeviceInfo): IDataBox;
|
|
50
50
|
/** Opens a box addressed to this device. @throws on authentication failure */
|
|
51
51
|
openBoxWithSecretKey(data: IDataBox): any;
|
|
52
|
-
/** Signs then boxes `data` for a device
|
|
52
|
+
/** Signs then boxes `data` for a device, including handshake replies and pairing credentials. */
|
|
53
53
|
signAndBoxDataForDevice<T>(data: T, device: IDeviceInfo): IDataBox;
|
|
54
54
|
/** Signs then boxes `data` to a raw X25519 public key (base64url). */
|
|
55
55
|
signAndBoxDataForKey<T>(data: T, toPublicBoxKey: string): IDataBox;
|
|
@@ -60,13 +60,6 @@ export declare class Device implements IDeviceInfo {
|
|
|
60
60
|
* zeroizes both secrets and the supplied ephemeral secret key.
|
|
61
61
|
*/
|
|
62
62
|
deriveFrameKeys(params: DeviceFrameKeyParams): DerivedFrameKeys;
|
|
63
|
-
/**
|
|
64
|
-
* Unwraps a possibly boxed and/or signed RPC value down to its contents.
|
|
65
|
-
* Plain values pass through unchanged.
|
|
66
|
-
*/
|
|
67
|
-
unwrapResponse<T>(response: T | ISignedObject<T> | IDataBox): T;
|
|
68
|
-
/** Opens a box addressed to this device and verifies the signed object inside. */
|
|
69
|
-
openBoxedAndSignedData<T>(data: IDataBox): T;
|
|
70
63
|
/** Updates bounded presentation metadata included in future signed identity messages. */
|
|
71
64
|
setDisplayInfo({ userName, deviceName }: IDeviceDisplayInfo): void;
|
|
72
65
|
/**
|
package/dist/device/device.js
CHANGED
|
@@ -54,7 +54,7 @@ class Device {
|
|
|
54
54
|
openBoxWithSecretKey(data) {
|
|
55
55
|
return (0, keys_1.openBoxWithSecretKey)(data, this.keys.secretKey);
|
|
56
56
|
}
|
|
57
|
-
/** Signs then boxes `data` for a device
|
|
57
|
+
/** Signs then boxes `data` for a device, including handshake replies and pairing credentials. */
|
|
58
58
|
signAndBoxDataForDevice(data, device) {
|
|
59
59
|
const signedData = this.signObjectWithSecretKey(data);
|
|
60
60
|
return this.boxDataForDevice(signedData, device);
|
|
@@ -89,35 +89,6 @@ class Device {
|
|
|
89
89
|
params.ephemeralSecretKey.fill(0);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Unwraps a possibly boxed and/or signed RPC value down to its contents.
|
|
94
|
-
* Plain values pass through unchanged.
|
|
95
|
-
*/
|
|
96
|
-
unwrapResponse(response) {
|
|
97
|
-
if (!response) {
|
|
98
|
-
return response;
|
|
99
|
-
}
|
|
100
|
-
if (typeof response === "object" &&
|
|
101
|
-
"contents" in response &&
|
|
102
|
-
"nonce" in response &&
|
|
103
|
-
"fromPublicKey" in response) {
|
|
104
|
-
const signedResponse = this.openBoxWithSecretKey(response);
|
|
105
|
-
return this.unwrapResponse(signedResponse);
|
|
106
|
-
}
|
|
107
|
-
if (typeof response === "object" &&
|
|
108
|
-
"contents" in response &&
|
|
109
|
-
"signature" in response &&
|
|
110
|
-
"publicKey" in response) {
|
|
111
|
-
const signedResponse = response;
|
|
112
|
-
return (0, keys_1.openSignedObject)(signedResponse);
|
|
113
|
-
}
|
|
114
|
-
return response;
|
|
115
|
-
}
|
|
116
|
-
/** Opens a box addressed to this device and verifies the signed object inside. */
|
|
117
|
-
openBoxedAndSignedData(data) {
|
|
118
|
-
const signedData = this.openBoxWithSecretKey(data);
|
|
119
|
-
return (0, keys_1.openSignedObject)(signedData);
|
|
120
|
-
}
|
|
121
92
|
/** Updates bounded presentation metadata included in future signed identity messages. */
|
|
122
93
|
setDisplayInfo({ userName, deviceName }) {
|
|
123
94
|
userName = userName ? userName.slice(0, data_1.DISPLAY_NAME_MAX_LENGTH) : undefined;
|