@floegence/flowersec-core 2.5.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -18
- package/dist/browser/connectSessionV3.d.ts +13 -0
- package/dist/browser/connectSessionV3.js +75 -0
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/v2.d.ts +3 -0
- package/dist/browser/v2.js +2 -0
- package/dist/facade.d.ts +15 -8
- package/dist/facade.js +5 -4
- package/dist/framing/jsonframe.d.ts +1 -1
- package/dist/framing/jsonframe.js +3 -1
- package/dist/interop/proxyServerPeer.js +1 -1
- package/dist/interop/serverParityPeer.js +8 -1
- package/dist/node/acceptor.d.ts +10 -0
- package/dist/node/acceptor.js +53 -7
- package/dist/node/acceptorV3.d.ts +57 -0
- package/dist/node/acceptorV3.js +227 -0
- package/dist/node/connectSessionV3.d.ts +21 -0
- package/dist/node/connectSessionV3.js +73 -0
- package/dist/node/index.d.ts +15 -10
- package/dist/node/index.js +6 -5
- package/dist/node/nativeTransportAddon.d.ts +39 -1
- package/dist/node/nativeTransportAddon.js +29 -5
- package/dist/node/rawQuicAdapterV3.d.ts +10 -0
- package/dist/node/rawQuicAdapterV3.js +76 -0
- package/dist/node/rawQuicServerV3.d.ts +24 -0
- package/dist/node/rawQuicServerV3.js +34 -0
- package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
- package/dist/node/runtimeAuthorizationV3.js +126 -0
- package/dist/node/tunnelRuntimeV3.d.ts +57 -0
- package/dist/node/tunnelRuntimeV3.js +857 -0
- package/dist/node/v2.d.ts +11 -0
- package/dist/node/v2.js +6 -0
- package/dist/node/webSocketServerV3.d.ts +27 -0
- package/dist/node/webSocketServerV3.js +219 -0
- package/dist/proxy/runtime.js +39 -6
- package/dist/proxy/serviceWorker.d.ts +2 -0
- package/dist/proxy/serviceWorker.js +63 -7
- package/dist/proxy/windowBridge.d.ts +1 -0
- package/dist/proxy/windowBridge.js +93 -33
- package/dist/proxy/wsPatch.d.ts +1 -0
- package/dist/proxy/wsPatch.js +39 -6
- package/dist/public/streamHandlers.d.ts +3 -0
- package/dist/public/streamHandlers.js +6 -1
- package/dist/rpc/client.js +9 -7
- package/dist/rpc/server.js +44 -20
- package/dist/rpc/validate.d.ts +1 -0
- package/dist/rpc/validate.js +18 -6
- package/dist/v2/index.d.ts +14 -0
- package/dist/v2/index.js +7 -0
- package/dist/v2/protocol.js +23 -32
- package/dist/v2/publicSession.js +4 -0
- package/dist/v2/session.d.ts +5 -0
- package/dist/v2/session.js +94 -35
- package/dist/v3/admissionError.d.ts +4 -0
- package/dist/v3/admissionError.js +8 -0
- package/dist/v3/artifact.d.ts +145 -0
- package/dist/v3/artifact.js +1162 -0
- package/dist/v3/artifactLease.d.ts +17 -0
- package/dist/v3/artifactLease.js +109 -0
- package/dist/v3/browserRuntime.d.ts +37 -0
- package/dist/v3/browserRuntime.js +197 -0
- package/dist/v3/browserSessionRuntime.d.ts +2 -0
- package/dist/v3/browserSessionRuntime.js +21 -0
- package/dist/v3/capability.d.ts +32 -0
- package/dist/v3/capability.js +345 -0
- package/dist/v3/carrier.d.ts +84 -0
- package/dist/v3/carrier.js +411 -0
- package/dist/v3/connectionController.d.ts +87 -0
- package/dist/v3/connectionController.js +805 -0
- package/dist/v3/contract.d.ts +65 -0
- package/dist/v3/contract.js +3 -0
- package/dist/v3/controller.d.ts +43 -0
- package/dist/v3/controller.js +303 -0
- package/dist/v3/handshake.d.ts +81 -0
- package/dist/v3/handshake.js +471 -0
- package/dist/v3/jcs.d.ts +6 -0
- package/dist/v3/jcs.js +69 -0
- package/dist/v3/jsonPreflight.d.ts +1 -0
- package/dist/v3/jsonPreflight.js +251 -0
- package/dist/v3/nodeRuntime.d.ts +40 -0
- package/dist/v3/nodeRuntime.js +315 -0
- package/dist/v3/nodeSessionRuntime.d.ts +2 -0
- package/dist/v3/nodeSessionRuntime.js +13 -0
- package/dist/v3/protocol.d.ts +99 -0
- package/dist/v3/protocol.js +773 -0
- package/dist/v3/publicApi.d.ts +12 -0
- package/dist/v3/publicApi.js +46 -0
- package/dist/v3/publicSession.d.ts +1 -0
- package/dist/v3/publicSession.js +281 -0
- package/dist/v3/retryDisposition.d.ts +16 -0
- package/dist/v3/retryDisposition.js +30 -0
- package/dist/v3/runtimeAdapters.d.ts +9 -0
- package/dist/v3/runtimeAdapters.js +109 -0
- package/dist/v3/security.d.ts +34 -0
- package/dist/v3/security.js +100 -0
- package/dist/v3/serverAdmission.d.ts +38 -0
- package/dist/v3/serverAdmission.js +223 -0
- package/dist/v3/session.d.ts +318 -0
- package/dist/v3/session.js +2289 -0
- package/dist/v3/sessionConnector.d.ts +37 -0
- package/dist/v3/sessionConnector.js +440 -0
- package/dist/v3/streamLifetimeLedger.d.ts +31 -0
- package/dist/v3/streamLifetimeLedger.js +131 -0
- package/dist/v3/streamMetadata.d.ts +1 -0
- package/dist/v3/streamMetadata.js +7 -0
- package/dist/v3/transportConstants.d.ts +56 -0
- package/dist/v3/transportConstants.js +59 -0
- package/dist/v3/unreliableMessage.d.ts +44 -0
- package/dist/v3/unreliableMessage.js +284 -0
- package/dist/v3/webSocketCarrier.d.ts +31 -0
- package/dist/v3/webSocketCarrier.js +370 -0
- package/dist/v3/webTransportCarrier.d.ts +27 -0
- package/dist/v3/webTransportCarrier.js +534 -0
- package/dist/yamux/stream.d.ts +3 -0
- package/dist/yamux/stream.js +17 -4
- package/package.json +4 -4
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AdmissionStatusV3, type ArtifactV3, type DecodedFSB3RequestV3 } from "./artifact.js";
|
|
2
|
+
import type { CarrierSessionV3, CarrierStreamV3 } from "./carrier.js";
|
|
3
|
+
import { type SessionProtocolRuntimeV3, type SessionV3 } from "./session.js";
|
|
4
|
+
import type { RpcRouter } from "../rpc/server.js";
|
|
5
|
+
export type AdmissionDecisionV3 = Readonly<{
|
|
6
|
+
accepted: true;
|
|
7
|
+
artifact: ArtifactV3;
|
|
8
|
+
}> | Readonly<{
|
|
9
|
+
accepted: false;
|
|
10
|
+
status: AdmissionStatusV3.Reject | AdmissionStatusV3.Retryable;
|
|
11
|
+
reason: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type AdmissionAuthorizerV3 = (request: DecodedFSB3RequestV3, signal?: AbortSignal) => Promise<AdmissionDecisionV3>;
|
|
14
|
+
export declare function createAdmissionReasonRegistryV3(builtInReasons: readonly string[], deploymentReasons?: readonly string[]): ReadonlySet<string>;
|
|
15
|
+
export type ReceivedSessionAdmissionV3 = Readonly<{
|
|
16
|
+
carrier: CarrierSessionV3;
|
|
17
|
+
stream: CarrierStreamV3;
|
|
18
|
+
rawFSB3: Uint8Array;
|
|
19
|
+
decoded: DecodedFSB3RequestV3;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function receiveSessionAdmissionV3(carrier: CarrierSessionV3, signal?: AbortSignal): Promise<ReceivedSessionAdmissionV3>;
|
|
22
|
+
export declare function rejectSessionAdmissionV3(received: ReceivedSessionAdmissionV3, decision: Extract<AdmissionDecisionV3, Readonly<{
|
|
23
|
+
accepted: false;
|
|
24
|
+
}>>, admissionReasons: ReadonlySet<string>, signal?: AbortSignal): Promise<never>;
|
|
25
|
+
export declare function acceptReceivedSessionV3(received: ReceivedSessionAdmissionV3, artifact: ArtifactV3, options: Readonly<{
|
|
26
|
+
runtime: SessionProtocolRuntimeV3;
|
|
27
|
+
admissionReasons: ReadonlySet<string>;
|
|
28
|
+
resolveRPCRouter?(request: DecodedFSB3RequestV3, signal?: AbortSignal): Promise<RpcRouter> | RpcRouter;
|
|
29
|
+
nowUnixSeconds?: () => number;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
}>): Promise<SessionV3>;
|
|
32
|
+
export declare function acceptCarrierSessionV3(carrier: CarrierSessionV3, authorize: AdmissionAuthorizerV3, options: Readonly<{
|
|
33
|
+
runtime: SessionProtocolRuntimeV3;
|
|
34
|
+
admissionReasons: ReadonlySet<string>;
|
|
35
|
+
resolveRPCRouter?(request: DecodedFSB3RequestV3, signal?: AbortSignal): Promise<RpcRouter> | RpcRouter;
|
|
36
|
+
nowUnixSeconds?: () => number;
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
}>): Promise<SessionV3>;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { base64urlDecode } from "../utils/base64url.js";
|
|
2
|
+
import { AdmissionStatusV3, buildFSB3RequestV3, decodeFSB3RequestV3, encodeFSA3ResponseV3, encodeFSB3RequestV3, validateArtifactV3, } from "./artifact.js";
|
|
3
|
+
import { AdmissionSessionV3Error } from "./admissionError.js";
|
|
4
|
+
import { configureServerWebSocketCarrierRoleV3 } from "./webSocketCarrier.js";
|
|
5
|
+
import { establishSessionV3 } from "./session.js";
|
|
6
|
+
export function createAdmissionReasonRegistryV3(builtInReasons, deploymentReasons = []) {
|
|
7
|
+
const registry = new Set();
|
|
8
|
+
for (const reason of [...builtInReasons, ...deploymentReasons]) {
|
|
9
|
+
if (registry.has(reason))
|
|
10
|
+
throw new TypeError(`duplicate Flowersec v3 admission reason: ${reason}`);
|
|
11
|
+
registry.add(reason);
|
|
12
|
+
encodeFSA3ResponseV3({
|
|
13
|
+
status: reason === "expired_artifact" ? AdmissionStatusV3.Retryable : AdmissionStatusV3.Reject,
|
|
14
|
+
reason,
|
|
15
|
+
}, registry);
|
|
16
|
+
}
|
|
17
|
+
return registry;
|
|
18
|
+
}
|
|
19
|
+
export async function receiveSessionAdmissionV3(carrier, signal) {
|
|
20
|
+
const stream = await carrier.acceptStream(signalOptions(signal));
|
|
21
|
+
try {
|
|
22
|
+
const rawFSB3 = await readFSB3(stream, signal);
|
|
23
|
+
return Object.freeze({ carrier, stream, rawFSB3, decoded: decodeFSB3RequestV3(rawFSB3) });
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
stream.abort(asError(error));
|
|
27
|
+
carrier.abort({ code: 6, reason: "admission failed" });
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function rejectSessionAdmissionV3(received, decision, admissionReasons, signal) {
|
|
32
|
+
try {
|
|
33
|
+
await respondAdmission(received.stream, { status: decision.status, reason: decision.reason }, admissionReasons, signal);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
received.carrier.abort({ code: 6, reason: "admission rejected" });
|
|
37
|
+
}
|
|
38
|
+
throw new AdmissionSessionV3Error(decision.reason, `Flowersec v3 admission rejected: ${decision.reason}`);
|
|
39
|
+
}
|
|
40
|
+
export async function acceptReceivedSessionV3(received, artifact, options) {
|
|
41
|
+
return await acceptCarrierBoundSessionV3(received, artifact, options);
|
|
42
|
+
}
|
|
43
|
+
async function acceptCarrierBoundSessionV3(received, artifact, options) {
|
|
44
|
+
try {
|
|
45
|
+
assertAdmissionCarrierBindingV3(received);
|
|
46
|
+
validateArtifactV3(artifact);
|
|
47
|
+
const expected = encodeFSB3RequestV3(buildFSB3RequestV3(artifact, received.decoded.request.chosen_candidate_id));
|
|
48
|
+
if (!equalBytes(expected, received.rawFSB3))
|
|
49
|
+
throw new Error("authorized artifact does not match admission");
|
|
50
|
+
if ((options.nowUnixSeconds?.() ?? Math.floor(Date.now() / 1_000)) >= artifact.session.init_expire_at_unix_s) {
|
|
51
|
+
return await rejectSessionAdmissionV3(received, {
|
|
52
|
+
accepted: false,
|
|
53
|
+
status: AdmissionStatusV3.Retryable,
|
|
54
|
+
reason: "expired_artifact",
|
|
55
|
+
}, options.admissionReasons, options.signal);
|
|
56
|
+
}
|
|
57
|
+
const rpcRouter = options.resolveRPCRouter === undefined
|
|
58
|
+
? undefined
|
|
59
|
+
: await raceAbort(Promise.resolve().then(async () => await options.resolveRPCRouter(received.decoded, options.signal)), options.signal);
|
|
60
|
+
if (received.carrier.kind === "websocket") {
|
|
61
|
+
configureServerWebSocketCarrierRoleV3(received.carrier, false);
|
|
62
|
+
}
|
|
63
|
+
await respondAdmission(received.stream, { status: AdmissionStatusV3.Success, reason: "" }, options.admissionReasons, options.signal);
|
|
64
|
+
const binding = received.decoded.localAdmissionBinding;
|
|
65
|
+
return await establishSessionV3(received.carrier, {
|
|
66
|
+
role: "server",
|
|
67
|
+
path: artifact.path.kind,
|
|
68
|
+
channelID: artifact.session.channel_id,
|
|
69
|
+
sessionContractHash: base64urlDecode(artifact.session.contract_hash_b64u),
|
|
70
|
+
suite: artifact.session.default_suite,
|
|
71
|
+
psk: base64urlDecode(artifact.session.e2ee_psk_b64u),
|
|
72
|
+
maxInboundStreams: artifact.session.max_inbound_streams,
|
|
73
|
+
sessionContract: artifact.session,
|
|
74
|
+
idleTimeoutMs: artifact.session.idle_timeout_seconds * 1_000,
|
|
75
|
+
closeTimeoutMs: 5_000,
|
|
76
|
+
runtime: options.runtime,
|
|
77
|
+
...(rpcRouter === undefined ? {} : { rpcRouter }),
|
|
78
|
+
localAdmissionBinding: binding,
|
|
79
|
+
peerAdmissionBinding: binding,
|
|
80
|
+
localEndpointInstanceID: "",
|
|
81
|
+
expectedPeerEndpointInstanceID: "",
|
|
82
|
+
deadlines: {
|
|
83
|
+
establishTimeoutMs: artifact.session.establish_timeout_seconds * 1_000,
|
|
84
|
+
rekeyPrepareTimeoutMs: artifact.session.rekey_prepare_timeout_seconds * 1_000,
|
|
85
|
+
rekeyCompletionTimeoutMs: artifact.session.rekey_completion_timeout_seconds * 1_000,
|
|
86
|
+
},
|
|
87
|
+
}, signalOptions(options.signal));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
received.stream.abort(asError(error));
|
|
91
|
+
received.carrier.abort({ code: 6, reason: "admission failed" });
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export async function acceptCarrierSessionV3(carrier, authorize, options) {
|
|
96
|
+
const received = await receiveSessionAdmissionV3(carrier, options.signal);
|
|
97
|
+
try {
|
|
98
|
+
assertAdmissionCarrierBindingV3(received);
|
|
99
|
+
const decision = await raceAbort(Promise.resolve().then(async () => await authorize(received.decoded, options.signal)), options.signal);
|
|
100
|
+
if (!decision.accepted) {
|
|
101
|
+
return await rejectSessionAdmissionV3(received, decision, options.admissionReasons, options.signal);
|
|
102
|
+
}
|
|
103
|
+
return await acceptCarrierBoundSessionV3(received, decision.artifact, options);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
received.stream.abort(asError(error));
|
|
107
|
+
carrier.abort({ code: 6, reason: "admission failed" });
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function assertAdmissionCarrierBindingV3(received) {
|
|
112
|
+
const chosen = received.decoded.request.candidates.find(({ id }) => id === received.decoded.request.chosen_candidate_id);
|
|
113
|
+
if (chosen?.carrier !== received.carrier.kind || received.decoded.request.pathKind !== received.carrier.path) {
|
|
114
|
+
throw new Error("admission carrier binding mismatch");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function respondAdmission(stream, response, admissionReasons, signal) {
|
|
118
|
+
await writeAll(stream, encodeFSA3ResponseV3(response, admissionReasons), signal);
|
|
119
|
+
await raceAbort(stream.closeWrite(), signal);
|
|
120
|
+
}
|
|
121
|
+
async function readFSB3(stream, signal) {
|
|
122
|
+
const reader = new ExactReader(stream);
|
|
123
|
+
const header = await reader.readExactly(12, signal);
|
|
124
|
+
if (header[0] !== 0x46 ||
|
|
125
|
+
header[1] !== 0x53 ||
|
|
126
|
+
header[2] !== 0x42 ||
|
|
127
|
+
header[3] !== 0x33 ||
|
|
128
|
+
header[4] !== 3 ||
|
|
129
|
+
(header[5] !== 1 && header[5] !== 2) ||
|
|
130
|
+
header[6] !== 0 ||
|
|
131
|
+
header[7] !== 0) {
|
|
132
|
+
throw new Error("invalid FSB3 header");
|
|
133
|
+
}
|
|
134
|
+
const length = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint32(8, false);
|
|
135
|
+
if (length < 1 || length > 32_768)
|
|
136
|
+
throw new Error("invalid FSB3 payload length");
|
|
137
|
+
const payload = await reader.readExactly(length, signal);
|
|
138
|
+
await reader.expectEOF(signal);
|
|
139
|
+
const raw = new Uint8Array(header.length + payload.length);
|
|
140
|
+
raw.set(header);
|
|
141
|
+
raw.set(payload, header.length);
|
|
142
|
+
return raw;
|
|
143
|
+
}
|
|
144
|
+
class ExactReader {
|
|
145
|
+
stream;
|
|
146
|
+
chunks = [];
|
|
147
|
+
offset = 0;
|
|
148
|
+
available = 0;
|
|
149
|
+
constructor(stream) {
|
|
150
|
+
this.stream = stream;
|
|
151
|
+
}
|
|
152
|
+
async readExactly(length, signal) {
|
|
153
|
+
while (this.available < length) {
|
|
154
|
+
const chunk = await raceAbort(this.stream.read(signalOptions(signal)), signal);
|
|
155
|
+
if (chunk === null)
|
|
156
|
+
throw new Error("truncated admission stream");
|
|
157
|
+
if (chunk.length === 0)
|
|
158
|
+
continue;
|
|
159
|
+
this.chunks.push(chunk);
|
|
160
|
+
this.available += chunk.length;
|
|
161
|
+
}
|
|
162
|
+
const result = new Uint8Array(length);
|
|
163
|
+
let written = 0;
|
|
164
|
+
while (written < length) {
|
|
165
|
+
const chunk = this.chunks[0];
|
|
166
|
+
const take = Math.min(length - written, chunk.length - this.offset);
|
|
167
|
+
result.set(chunk.subarray(this.offset, this.offset + take), written);
|
|
168
|
+
this.offset += take;
|
|
169
|
+
this.available -= take;
|
|
170
|
+
written += take;
|
|
171
|
+
if (this.offset === chunk.length) {
|
|
172
|
+
this.chunks.shift();
|
|
173
|
+
this.offset = 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
async expectEOF(signal) {
|
|
179
|
+
if (this.available !== 0)
|
|
180
|
+
throw new Error("trailing bytes after FSB3");
|
|
181
|
+
while (true) {
|
|
182
|
+
const chunk = await raceAbort(this.stream.read(signalOptions(signal)), signal);
|
|
183
|
+
if (chunk === null)
|
|
184
|
+
return;
|
|
185
|
+
if (chunk.length !== 0)
|
|
186
|
+
throw new Error("trailing bytes after FSB3");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function writeAll(stream, value, signal) {
|
|
191
|
+
let offset = 0;
|
|
192
|
+
while (offset < value.length) {
|
|
193
|
+
const written = await raceAbort(stream.write(value.subarray(offset), signalOptions(signal)), signal);
|
|
194
|
+
if (written < 1 || written > value.length - offset)
|
|
195
|
+
throw new Error("short admission write");
|
|
196
|
+
offset += written;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function signalOptions(signal) {
|
|
200
|
+
return signal === undefined ? {} : { signal };
|
|
201
|
+
}
|
|
202
|
+
async function raceAbort(promise, signal) {
|
|
203
|
+
if (signal === undefined)
|
|
204
|
+
return await promise;
|
|
205
|
+
if (signal.aborted)
|
|
206
|
+
throw signal.reason;
|
|
207
|
+
return await new Promise((resolve, reject) => {
|
|
208
|
+
const abort = () => reject(signal.reason);
|
|
209
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
210
|
+
void promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function equalBytes(left, right) {
|
|
214
|
+
if (left.length !== right.length)
|
|
215
|
+
return false;
|
|
216
|
+
let difference = 0;
|
|
217
|
+
for (let index = 0; index < left.length; index += 1)
|
|
218
|
+
difference |= left[index] ^ right[index];
|
|
219
|
+
return difference === 0;
|
|
220
|
+
}
|
|
221
|
+
function asError(error) {
|
|
222
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
223
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { RpcClient } from "../rpc/client.js";
|
|
2
|
+
import { RpcRouter, type RpcServerOptions } from "../rpc/server.js";
|
|
3
|
+
import type { InternalByteStreamV3 as ByteStreamV3, InternalIncomingStreamV3 as IncomingStreamV3, InternalRpcPeerV3, InternalSessionV3 as SessionV3Contract, OperationOptionsV3, PathKind, InternalStreamOpenOptionsV3, UnreliableMessageChannelV3 } from "./contract.js";
|
|
4
|
+
import { type CarrierSessionV3, type CarrierStreamV3 } from "./carrier.js";
|
|
5
|
+
import type { SessionContractV3 } from "./artifact.js";
|
|
6
|
+
import { DirectionV3, InnerTypeV3, type EpochRootsV3, type RecordMaterialV3, type RecordHeaderV3, type CipherSuiteV3 } from "./protocol.js";
|
|
7
|
+
type SessionTerminationV3 = Readonly<{
|
|
8
|
+
error: Error;
|
|
9
|
+
}>;
|
|
10
|
+
export type SessionRoleV3 = "client" | "server";
|
|
11
|
+
export type SessionDeadlinePhaseV3 = "establish" | "rekey_prepare" | "rekey_completion";
|
|
12
|
+
export type SessionDeadlineHandleV3 = Readonly<{
|
|
13
|
+
signal: AbortSignal;
|
|
14
|
+
cancel(): void;
|
|
15
|
+
}>;
|
|
16
|
+
export type SessionDeadlineFactoryV3 = (timeoutMs: number, phase: SessionDeadlinePhaseV3) => SessionDeadlineHandleV3;
|
|
17
|
+
export type SessionDeadlinesV3 = Readonly<{
|
|
18
|
+
establishTimeoutMs: number;
|
|
19
|
+
rekeyPrepareTimeoutMs: number;
|
|
20
|
+
rekeyCompletionTimeoutMs: number;
|
|
21
|
+
factory?: SessionDeadlineFactoryV3;
|
|
22
|
+
}>;
|
|
23
|
+
export type SessionConfigV3 = Readonly<{
|
|
24
|
+
role: SessionRoleV3;
|
|
25
|
+
path: PathKind;
|
|
26
|
+
channelID: string;
|
|
27
|
+
sessionContractHash: Uint8Array;
|
|
28
|
+
suite: CipherSuiteV3;
|
|
29
|
+
psk: Uint8Array;
|
|
30
|
+
maxInboundStreams: number;
|
|
31
|
+
localAdmissionBinding: Uint8Array;
|
|
32
|
+
peerAdmissionBinding: Uint8Array;
|
|
33
|
+
/** Required by admitted APIs to bind runtime limits and keys to the signed contract. */
|
|
34
|
+
sessionContract?: SessionContractV3;
|
|
35
|
+
localEndpointInstanceID: string;
|
|
36
|
+
expectedPeerEndpointInstanceID: string;
|
|
37
|
+
rpcRouter?: RpcRouter;
|
|
38
|
+
rpcServerOptions?: RpcServerOptions;
|
|
39
|
+
deadlines?: SessionDeadlinesV3;
|
|
40
|
+
idleTimeoutMs?: number;
|
|
41
|
+
closeTimeoutMs?: number;
|
|
42
|
+
runtime: SessionProtocolRuntimeV3;
|
|
43
|
+
}>;
|
|
44
|
+
export type SessionProtocolRuntimeV3 = Readonly<{
|
|
45
|
+
entropy(length: number): Uint8Array;
|
|
46
|
+
monotonicMilliseconds(): number;
|
|
47
|
+
}>;
|
|
48
|
+
export declare class SessionV3Error extends Error {
|
|
49
|
+
readonly code: "aborted" | "closed" | "going_away" | "handshake" | "open_rejected" | "protocol" | "rekey_failed" | "resource_exhausted" | "stream_reset" | "timeout";
|
|
50
|
+
constructor(code: "aborted" | "closed" | "going_away" | "handshake" | "open_rejected" | "protocol" | "rekey_failed" | "resource_exhausted" | "stream_reset" | "timeout", message: string);
|
|
51
|
+
}
|
|
52
|
+
declare class SessionRpcPeerV3 implements InternalRpcPeerV3 {
|
|
53
|
+
private readonly outbound;
|
|
54
|
+
private readonly inbound;
|
|
55
|
+
constructor(outbound: RpcClient, inbound: RpcRouter);
|
|
56
|
+
call(typeId: number, payload: unknown, signal?: AbortSignal): Promise<{
|
|
57
|
+
payload: unknown;
|
|
58
|
+
error?: import("../rpc/wire.js").RpcError;
|
|
59
|
+
}>;
|
|
60
|
+
notify(typeId: number, payload: unknown): Promise<void>;
|
|
61
|
+
onNotify(typeId: number, handler: (payload: unknown) => void): () => void;
|
|
62
|
+
close(): void;
|
|
63
|
+
}
|
|
64
|
+
type HandshakeMaterial = Readonly<{
|
|
65
|
+
h3: Uint8Array;
|
|
66
|
+
sessionPRK: Uint8Array;
|
|
67
|
+
selectedFeatures: number;
|
|
68
|
+
}>;
|
|
69
|
+
export declare function establishSessionV3(carrier: CarrierSessionV3, config: SessionConfigV3, options?: OperationOptionsV3): Promise<SessionV3>;
|
|
70
|
+
export declare class SessionV3 implements SessionV3Contract {
|
|
71
|
+
private readonly carrier;
|
|
72
|
+
private readonly control;
|
|
73
|
+
private readonly controlReader;
|
|
74
|
+
private readonly config;
|
|
75
|
+
readonly path: PathKind;
|
|
76
|
+
readonly endpointInstanceId: string | undefined;
|
|
77
|
+
readonly rpc: SessionRpcPeerV3;
|
|
78
|
+
readonly termination: Promise<SessionTerminationV3>;
|
|
79
|
+
readonly unreliableMessages: UnreliableMessageChannelV3 | undefined;
|
|
80
|
+
terminalError: Error | undefined;
|
|
81
|
+
private readonly role;
|
|
82
|
+
private readonly sendDirection;
|
|
83
|
+
private readonly receiveDirection;
|
|
84
|
+
private readonly h3;
|
|
85
|
+
private readonly sendRoots;
|
|
86
|
+
private readonly receiveRoots;
|
|
87
|
+
private sendEpoch;
|
|
88
|
+
private receiveEpoch;
|
|
89
|
+
private controlSendEpoch;
|
|
90
|
+
private controlSendSequence;
|
|
91
|
+
private controlReceiveEpoch;
|
|
92
|
+
private controlReceiveSequence;
|
|
93
|
+
private controlWriteTail;
|
|
94
|
+
private controlTerminalSealed;
|
|
95
|
+
private nextLogicalID;
|
|
96
|
+
private receivedGoAway;
|
|
97
|
+
private receivedGoAwayLastAccepted;
|
|
98
|
+
private receivedGoAwayReason;
|
|
99
|
+
private sentGoAway;
|
|
100
|
+
private sentGoAwayLastAccepted;
|
|
101
|
+
private sentGoAwayReason;
|
|
102
|
+
private closePromise;
|
|
103
|
+
private lifecycle;
|
|
104
|
+
private sentSessionClose;
|
|
105
|
+
private sessionCloseCommitted;
|
|
106
|
+
private readonly peerSessionClose;
|
|
107
|
+
private readonly streams;
|
|
108
|
+
private readonly releasedStreams;
|
|
109
|
+
private readonly releasedStreamCleanup;
|
|
110
|
+
private readonly peerLedger;
|
|
111
|
+
private readonly outboundLedger;
|
|
112
|
+
private readonly incoming;
|
|
113
|
+
private readonly outboundPermits;
|
|
114
|
+
private readonly inboundPermits;
|
|
115
|
+
private readonly pings;
|
|
116
|
+
private readonly backgroundTasks;
|
|
117
|
+
private nextPing;
|
|
118
|
+
private readonly rpcActivation;
|
|
119
|
+
private rpcStreamPromise;
|
|
120
|
+
private rpcServing;
|
|
121
|
+
private rekeyTail;
|
|
122
|
+
private nextTransition;
|
|
123
|
+
private receiveTransition;
|
|
124
|
+
private pendingSessionRekey;
|
|
125
|
+
private lastSessionRekeyACK;
|
|
126
|
+
private preparingSendEpoch;
|
|
127
|
+
private pendingReceiveEpoch;
|
|
128
|
+
private openFrozen;
|
|
129
|
+
private openGate;
|
|
130
|
+
private outboundFrontierChanged;
|
|
131
|
+
private activeInboundResponders;
|
|
132
|
+
private localResponderFrozen;
|
|
133
|
+
private peerResponderFrozen;
|
|
134
|
+
private responderChanged;
|
|
135
|
+
private idleWatchdogStarted;
|
|
136
|
+
private idleDeadlineMs;
|
|
137
|
+
private idleTimerCancel;
|
|
138
|
+
private readonly terminationState;
|
|
139
|
+
private readonly rpcRouter;
|
|
140
|
+
constructor(carrier: CarrierSessionV3, control: CarrierStreamV3, controlReader: ExactReader, config: SessionConfigV3, material: HandshakeMaterial);
|
|
141
|
+
openStream(kind: string, options?: InternalStreamOpenOptionsV3): Promise<ByteStreamV3>;
|
|
142
|
+
acceptStream(options?: OperationOptionsV3): Promise<IncomingStreamV3>;
|
|
143
|
+
probeLiveness(options?: OperationOptionsV3): Promise<number>;
|
|
144
|
+
rekey(options?: OperationOptionsV3): Promise<void>;
|
|
145
|
+
close(): Promise<void>;
|
|
146
|
+
start(): void;
|
|
147
|
+
rootForSend(epoch: number): EpochRootsV3;
|
|
148
|
+
rootForReceive(epoch: number): EpochRootsV3;
|
|
149
|
+
hasReceiveRoots(epoch: number): boolean;
|
|
150
|
+
installReceiveRoots(epoch: number, roots: EpochRootsV3): void;
|
|
151
|
+
transcriptHash(): Uint8Array;
|
|
152
|
+
receiveDirectionValue(): DirectionV3;
|
|
153
|
+
sendStreamRecord(stream: EncryptedStreamV3, type: InnerTypeV3, payload: Uint8Array, signal?: AbortSignal): Promise<void>;
|
|
154
|
+
readStreamRecord(stream: EncryptedStreamV3): Promise<Readonly<{
|
|
155
|
+
type: InnerTypeV3;
|
|
156
|
+
payload: Uint8Array;
|
|
157
|
+
header: RecordHeaderV3;
|
|
158
|
+
}>>;
|
|
159
|
+
localReset(stream: EncryptedStreamV3, error: Error): Promise<void>;
|
|
160
|
+
releaseStream(stream: EncryptedStreamV3): void;
|
|
161
|
+
private confirmControlDelivery;
|
|
162
|
+
private openLogicalStream;
|
|
163
|
+
private ensureRPCStream;
|
|
164
|
+
private acceptCarrierLoop;
|
|
165
|
+
private acceptCarrierStream;
|
|
166
|
+
private acceptCarrierStreamAfterFSS3;
|
|
167
|
+
private resetInboundBeforeDelivery;
|
|
168
|
+
private sendControl;
|
|
169
|
+
private sendControlResponse;
|
|
170
|
+
private sendControlCleanup;
|
|
171
|
+
private sendTerminalControl;
|
|
172
|
+
private writeControl;
|
|
173
|
+
private readControl;
|
|
174
|
+
private controlLoop;
|
|
175
|
+
private closeOnce;
|
|
176
|
+
waitTermination(): Promise<SessionTerminationV3>;
|
|
177
|
+
private rekeyOnce;
|
|
178
|
+
private rejectExhaustedRekey;
|
|
179
|
+
private receiveSessionRekey;
|
|
180
|
+
private enterInboundResponder;
|
|
181
|
+
private leaveInboundResponder;
|
|
182
|
+
private freezeInboundResponders;
|
|
183
|
+
private unfreezeInboundResponders;
|
|
184
|
+
private notifyResponderChanged;
|
|
185
|
+
private receiveSessionRekeyBeforeDeadline;
|
|
186
|
+
private validateSessionRekeySequence;
|
|
187
|
+
private receiveSessionRekeyACK;
|
|
188
|
+
private waitOpenGate;
|
|
189
|
+
resolveOutboundOpen(id: bigint): void;
|
|
190
|
+
private waitOutboundFrontier;
|
|
191
|
+
private commitOutboundReset;
|
|
192
|
+
private commitLocalReset;
|
|
193
|
+
private notifyOutboundFrontierChanged;
|
|
194
|
+
private isLocalLogicalID;
|
|
195
|
+
private localOpenHighWatermark;
|
|
196
|
+
private validGoAwayBoundary;
|
|
197
|
+
private sendGoAway;
|
|
198
|
+
private receiveGoAway;
|
|
199
|
+
private trackBackground;
|
|
200
|
+
private localOpeningAllowedAfterGoAway;
|
|
201
|
+
private acceptsPeerStreamAfterGoAway;
|
|
202
|
+
private cleanupEpochRoots;
|
|
203
|
+
streamEpochStateChanged(): void;
|
|
204
|
+
private cleanupRootMap;
|
|
205
|
+
private wipeAllRoots;
|
|
206
|
+
private startIdleWatchdog;
|
|
207
|
+
private markAuthenticatedActivity;
|
|
208
|
+
private scheduleIdleWatchdog;
|
|
209
|
+
private fail;
|
|
210
|
+
private assertOpen;
|
|
211
|
+
private beginClosing;
|
|
212
|
+
}
|
|
213
|
+
declare class EncryptedStreamV3 implements ByteStreamV3 {
|
|
214
|
+
readonly session: SessionV3;
|
|
215
|
+
readonly carrier: CarrierStreamV3;
|
|
216
|
+
readonly id: bigint;
|
|
217
|
+
readonly kind: string;
|
|
218
|
+
sendEpoch: number;
|
|
219
|
+
receiveEpoch: number;
|
|
220
|
+
private readonly permitRelease;
|
|
221
|
+
readonly reader: ExactReader;
|
|
222
|
+
readonly opened: Readonly<{
|
|
223
|
+
promise: Promise<void>;
|
|
224
|
+
resolve: (value: void | PromiseLike<void>) => void;
|
|
225
|
+
reject: (reason?: unknown) => void;
|
|
226
|
+
readonly settled: boolean;
|
|
227
|
+
}>;
|
|
228
|
+
readonly data: ByteQueue;
|
|
229
|
+
terminalError: Error | undefined;
|
|
230
|
+
sendSequence: bigint;
|
|
231
|
+
receiveSequence: bigint;
|
|
232
|
+
priorACK: Readonly<{
|
|
233
|
+
epoch: number;
|
|
234
|
+
sequence: bigint;
|
|
235
|
+
}> | undefined;
|
|
236
|
+
private sendTail;
|
|
237
|
+
private localFIN;
|
|
238
|
+
private remoteFIN;
|
|
239
|
+
private pumpStarted;
|
|
240
|
+
private permitReleased;
|
|
241
|
+
private closeWriteTask;
|
|
242
|
+
private pendingSendRekey;
|
|
243
|
+
private lastSendRekeyACK;
|
|
244
|
+
private receiveRekey;
|
|
245
|
+
private readonly sendMaterials;
|
|
246
|
+
private readonly receiveMaterials;
|
|
247
|
+
constructor(session: SessionV3, carrier: CarrierStreamV3, id: bigint, kind: string, sendEpoch: number, receiveEpoch: number, permitRelease: () => void, reader?: ExactReader);
|
|
248
|
+
read(options?: OperationOptionsV3): Promise<Uint8Array | null>;
|
|
249
|
+
write(payload: Uint8Array, options?: OperationOptionsV3): Promise<number>;
|
|
250
|
+
closeWrite(): Promise<void>;
|
|
251
|
+
reset(): Promise<void>;
|
|
252
|
+
close(): Promise<void>;
|
|
253
|
+
send(type: InnerTypeV3, payload: Uint8Array, signal?: AbortSignal): Promise<void>;
|
|
254
|
+
canRekeySend(): boolean;
|
|
255
|
+
hasRemoteFIN(): boolean;
|
|
256
|
+
startSendRekey(transition: bigint, epoch: number): Readonly<{
|
|
257
|
+
armed: Deferred<void>;
|
|
258
|
+
done: Deferred<void>;
|
|
259
|
+
}>;
|
|
260
|
+
waitReceiveRekey(transition: bigint, epoch: number, signal?: AbortSignal): Promise<void>;
|
|
261
|
+
publishReceiveRekey(transition: bigint, epoch: number): void;
|
|
262
|
+
startPump(): void;
|
|
263
|
+
markOpen(): void;
|
|
264
|
+
markTerminal(error: Error): boolean;
|
|
265
|
+
peerReset(error: Error): void;
|
|
266
|
+
carrierReset(): void;
|
|
267
|
+
abort(error?: Error): void;
|
|
268
|
+
releasePermit(): void;
|
|
269
|
+
sendMaterial(roots: EpochRootsV3, h3: Uint8Array, direction: DirectionV3): RecordMaterialV3;
|
|
270
|
+
receiveMaterial(roots: EpochRootsV3, h3: Uint8Array, direction: DirectionV3, epoch: number): RecordMaterialV3;
|
|
271
|
+
pruneReceiveMaterials(): void;
|
|
272
|
+
wipeRecordMaterials(): void;
|
|
273
|
+
transferReceiveMaterialsTo(destination: EncryptedStreamV3): void;
|
|
274
|
+
private pump;
|
|
275
|
+
private openedHash;
|
|
276
|
+
setOpenHash(value: Uint8Array): void;
|
|
277
|
+
private enqueueSend;
|
|
278
|
+
private waitSendRekey;
|
|
279
|
+
private receiveStreamKeyUpdate;
|
|
280
|
+
private receiveStreamKeyUpdateACK;
|
|
281
|
+
sendEpochsInUse(): readonly number[];
|
|
282
|
+
receiveEpochsInUse(): readonly number[];
|
|
283
|
+
private releaseIfClean;
|
|
284
|
+
}
|
|
285
|
+
declare class ExactReader {
|
|
286
|
+
private readonly stream;
|
|
287
|
+
private chunks;
|
|
288
|
+
private offset;
|
|
289
|
+
private bytes;
|
|
290
|
+
constructor(stream: Pick<CarrierStreamV3, "read">);
|
|
291
|
+
readExactly(length: number, signal?: AbortSignal): Promise<Uint8Array>;
|
|
292
|
+
readEOF(signal?: AbortSignal): Promise<void>;
|
|
293
|
+
}
|
|
294
|
+
declare class ByteQueue {
|
|
295
|
+
private readonly limit;
|
|
296
|
+
private readonly chunks;
|
|
297
|
+
private head;
|
|
298
|
+
private bytes;
|
|
299
|
+
private closed;
|
|
300
|
+
private error;
|
|
301
|
+
private readonly waiters;
|
|
302
|
+
private readonly capacityWaiters;
|
|
303
|
+
constructor(limit: number);
|
|
304
|
+
push(chunk: Uint8Array): void;
|
|
305
|
+
close(): void;
|
|
306
|
+
fail(error: Error): void;
|
|
307
|
+
waitForCapacity(required: number): Promise<void>;
|
|
308
|
+
read(signal?: AbortSignal): Promise<Uint8Array | null>;
|
|
309
|
+
private wake;
|
|
310
|
+
private wakeCapacity;
|
|
311
|
+
}
|
|
312
|
+
type Deferred<T> = Readonly<{
|
|
313
|
+
promise: Promise<T>;
|
|
314
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
315
|
+
reject: (reason?: unknown) => void;
|
|
316
|
+
readonly settled: boolean;
|
|
317
|
+
}>;
|
|
318
|
+
export {};
|