@nice-code/action 0.25.0 → 0.26.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 +52 -0
- package/build/{AcceptorHandler-BizUtq4u.d.mts → AcceptorHandler-CLbwu2Pa.d.mts} +74 -16
- package/build/{AcceptorHandler-CxPfZtIl.d.cts → AcceptorHandler-Du292dpC.d.cts} +74 -16
- package/build/{ActionDevtoolsCore-D9KBBI2V.d.cts → ActionDevtoolsCore-DGwzONZT.d.mts} +2 -2
- package/build/{ActionDevtoolsCore-xZjAtB4H.d.mts → ActionDevtoolsCore-dH4K4w3B.d.cts} +2 -2
- package/build/advanced/index.cjs +1 -1
- package/build/advanced/index.d.cts +12 -6
- package/build/advanced/index.d.mts +12 -6
- package/build/advanced/index.mjs +1 -1
- package/build/{createHibernatableWsServerAdapter-BkjESd01.mjs → createHibernatableWsServerAdapter-BD5n-Ev9.mjs} +176 -75
- package/build/createHibernatableWsServerAdapter-BD5n-Ev9.mjs.map +1 -0
- package/build/{createHibernatableWsServerAdapter-FSDWrxoF.cjs → createHibernatableWsServerAdapter-j96U9vgo.cjs} +175 -74
- package/build/{createHibernatableWsServerAdapter-BkjESd01.mjs.map → createHibernatableWsServerAdapter-j96U9vgo.cjs.map} +1 -1
- package/build/devtools/browser/index.cjs.map +1 -1
- package/build/devtools/browser/index.d.cts +1 -1
- package/build/devtools/browser/index.d.mts +1 -1
- package/build/devtools/browser/index.mjs.map +1 -1
- package/build/devtools/server/index.d.cts +1 -1
- package/build/devtools/server/index.d.mts +1 -1
- package/build/{httpAcceptorCarrier-BQYaXI9j.cjs → httpAcceptorCarrier-By0Qa__L.cjs} +2 -2
- package/build/httpAcceptorCarrier-By0Qa__L.cjs.map +1 -0
- package/build/{httpAcceptorCarrier-DWqsCz3h.mjs → httpAcceptorCarrier-moSmtBxr.mjs} +2 -2
- package/build/httpAcceptorCarrier-moSmtBxr.mjs.map +1 -0
- package/build/index.cjs +2 -2
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +1 -1
- package/build/index.d.mts +1 -1
- package/build/index.mjs +2 -2
- package/build/index.mjs.map +1 -1
- package/build/platform/cloudflare/index.cjs +1 -1
- package/build/platform/cloudflare/index.d.cts +1 -1
- package/build/platform/cloudflare/index.d.mts +1 -1
- package/build/platform/cloudflare/index.mjs +1 -1
- package/build/react-query/index.d.cts +1 -1
- package/build/react-query/index.d.mts +1 -1
- package/package.json +5 -4
- package/build/createHibernatableWsServerAdapter-FSDWrxoF.cjs.map +0 -1
- package/build/httpAcceptorCarrier-BQYaXI9j.cjs.map +0 -1
- package/build/httpAcceptorCarrier-DWqsCz3h.mjs.map +0 -1
|
@@ -1734,6 +1734,16 @@ function createInMemoryTofuVerifyKeyResolver() {
|
|
|
1734
1734
|
* Storage-backed trust-on-first-use resolver: pins survive process restarts / Durable Object eviction
|
|
1735
1735
|
* (e.g. back it with `createDurableObjectStorageAdapter`). Same policy as the in-memory variant — trust
|
|
1736
1736
|
* + pin the first verify key per client identity, reject a different one thereafter.
|
|
1737
|
+
*
|
|
1738
|
+
* Fail-closed by construction: a thrown storage read (`getJson`) or first-pin write (`updateJsonWithDef`)
|
|
1739
|
+
* propagates out of `resolve`, which makes `onProve` reject the handshake — a storage error can never be
|
|
1740
|
+
* mistaken for "first use" and silently trusted. (A genuine `undefined`/absent read is the only path to
|
|
1741
|
+
* a fresh pin; the underlying adapters never coerce a thrown read to `undefined`.) Keep it that way: do
|
|
1742
|
+
* not wrap the storage calls in a `try/catch` that swallows the error.
|
|
1743
|
+
*
|
|
1744
|
+
* On an *eventually-consistent* store a stale "absent" read re-pins the **same** verify key the client
|
|
1745
|
+
* just presented (it is signature-verified before this runs), so the worst case is a harmless re-write,
|
|
1746
|
+
* never a weakened trust decision. Cross-isolate-strong pinning still wants a strongly-consistent store.
|
|
1737
1747
|
*/
|
|
1738
1748
|
function createStorageTofuVerifyKeyResolver(storageAdapter) {
|
|
1739
1749
|
const storage = (0, _nice_code_util.createTypedStorage)({ storageAdapter });
|
|
@@ -1787,6 +1797,13 @@ function createClientHandshake(config) {
|
|
|
1787
1797
|
bindVerifyKeysIntoDerivation: true
|
|
1788
1798
|
} : {}
|
|
1789
1799
|
});
|
|
1800
|
+
const encryptionKeyMaterial = wantsEncryption && welcome.exchangePublicKey != null ? {
|
|
1801
|
+
verifyPublicKey: welcome.verifyPublicKey,
|
|
1802
|
+
exchangePublicKey: welcome.exchangePublicKey,
|
|
1803
|
+
saltString: sessionSalt(clientNonce, welcome.serverNonce),
|
|
1804
|
+
infoString: handshakeInfo(dictionaryVersion),
|
|
1805
|
+
bindVerifyKeysIntoDerivation: true
|
|
1806
|
+
} : void 0;
|
|
1790
1807
|
const challenge = buildHandshakeChallenge({
|
|
1791
1808
|
securityLevel,
|
|
1792
1809
|
dictionaryVersion,
|
|
@@ -1802,7 +1819,8 @@ function createClientHandshake(config) {
|
|
|
1802
1819
|
pending = {
|
|
1803
1820
|
linkedServerId,
|
|
1804
1821
|
server: welcome.server,
|
|
1805
|
-
challenge
|
|
1822
|
+
challenge,
|
|
1823
|
+
encryptionKeyMaterial
|
|
1806
1824
|
};
|
|
1807
1825
|
return {
|
|
1808
1826
|
t: "prove",
|
|
@@ -1821,7 +1839,8 @@ function createClientHandshake(config) {
|
|
|
1821
1839
|
return {
|
|
1822
1840
|
linkedClientId: pending.linkedServerId,
|
|
1823
1841
|
remote: pending.server,
|
|
1824
|
-
securityLevel
|
|
1842
|
+
securityLevel,
|
|
1843
|
+
encryptionKeyMaterial: pending.encryptionKeyMaterial
|
|
1825
1844
|
};
|
|
1826
1845
|
}
|
|
1827
1846
|
};
|
|
@@ -1916,6 +1935,16 @@ function createServerHandshake(config) {
|
|
|
1916
1935
|
/** The completed handshake result once `onProve` has accepted, else `undefined`. */
|
|
1917
1936
|
getResult() {
|
|
1918
1937
|
return result;
|
|
1938
|
+
},
|
|
1939
|
+
exportPending() {
|
|
1940
|
+
return pending;
|
|
1941
|
+
},
|
|
1942
|
+
async restorePending(restored) {
|
|
1943
|
+
pending = restored;
|
|
1944
|
+
await link.linkClient({
|
|
1945
|
+
linkedClientId: restored.linkedClientId,
|
|
1946
|
+
verifyPublicKey: restored.clientVerifyKey
|
|
1947
|
+
});
|
|
1919
1948
|
}
|
|
1920
1949
|
};
|
|
1921
1950
|
}
|
|
@@ -1946,14 +1975,16 @@ function parseJsonActionFrame(message) {
|
|
|
1946
1975
|
const ENCRYPTED_ENVELOPE_LENGTH = 2;
|
|
1947
1976
|
/**
|
|
1948
1977
|
* Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`
|
|
1949
|
-
* level.
|
|
1978
|
+
* level. The shared key is derived once from {@link IActionFrameCryptoConfig.keyMaterial} and captured
|
|
1979
|
+
* for this connection alone, decoupling it from the link's shared key cache.
|
|
1950
1980
|
*/
|
|
1951
|
-
function createActionFrameCrypto({ link,
|
|
1981
|
+
function createActionFrameCrypto({ link, keyMaterial }) {
|
|
1982
|
+
const keyPromise = link.deriveSharedAesGcmKey(keyMaterial);
|
|
1952
1983
|
return {
|
|
1953
1984
|
async encryptFrame(frame) {
|
|
1954
|
-
const { nonce, ciphertext } = await
|
|
1955
|
-
|
|
1956
|
-
|
|
1985
|
+
const { nonce, ciphertext } = await (0, _nice_code_util.encryptBytesWithAesGcmKey)({
|
|
1986
|
+
dataToEncrypt: frame,
|
|
1987
|
+
aesGcmKey: await keyPromise
|
|
1957
1988
|
});
|
|
1958
1989
|
return (0, msgpackr.pack)([nonce, ciphertext]);
|
|
1959
1990
|
},
|
|
@@ -1963,12 +1994,12 @@ function createActionFrameCrypto({ link, linkedClientId }) {
|
|
|
1963
1994
|
if (!Array.isArray(envelope) || envelope.length !== ENCRYPTED_ENVELOPE_LENGTH) throw new Error("[ws-crypto] malformed encrypted frame envelope");
|
|
1964
1995
|
const [nonce, ciphertext] = envelope;
|
|
1965
1996
|
if (!(nonce instanceof Uint8Array) || !(ciphertext instanceof Uint8Array)) throw new Error("[ws-crypto] malformed encrypted frame fields");
|
|
1966
|
-
return await
|
|
1967
|
-
linkedClientId,
|
|
1997
|
+
return await (0, _nice_code_util.decryptBytesWithAesGcmKey)({
|
|
1968
1998
|
dataToDecrypt: {
|
|
1969
1999
|
nonce,
|
|
1970
2000
|
ciphertext
|
|
1971
|
-
}
|
|
2001
|
+
},
|
|
2002
|
+
aesGcmKey: await keyPromise
|
|
1972
2003
|
});
|
|
1973
2004
|
}
|
|
1974
2005
|
};
|
|
@@ -2079,9 +2110,9 @@ var AcceptorSecureSession = class {
|
|
|
2079
2110
|
_complete(result) {
|
|
2080
2111
|
this._authed = true;
|
|
2081
2112
|
this._handshake = void 0;
|
|
2082
|
-
if (result.securityLevel === "encrypted") this._pipe = this._buildPipe(createActionFrameCrypto({
|
|
2113
|
+
if (result.securityLevel === "encrypted" && result.encryptionKeyMaterial != null) this._pipe = this._buildPipe(createActionFrameCrypto({
|
|
2083
2114
|
link: this.config.link,
|
|
2084
|
-
|
|
2115
|
+
keyMaterial: result.encryptionKeyMaterial
|
|
2085
2116
|
}));
|
|
2086
2117
|
this.config.onAuthenticated({
|
|
2087
2118
|
client: new RuntimeCoordinate(result.remote),
|
|
@@ -2100,17 +2131,10 @@ var AcceptorSecureSession = class {
|
|
|
2100
2131
|
this._authed = true;
|
|
2101
2132
|
if (state.securityLevel !== "encrypted" || state.keyMaterial == null) return;
|
|
2102
2133
|
const { link } = this.config;
|
|
2103
|
-
const {
|
|
2104
|
-
const cryptoReady = link.initialize().then(() =>
|
|
2105
|
-
linkedClientId,
|
|
2106
|
-
verifyPublicKey: keyMaterial.verifyPublicKey,
|
|
2107
|
-
exchangePublicKey: keyMaterial.exchangePublicKey,
|
|
2108
|
-
saltString: keyMaterial.saltString,
|
|
2109
|
-
infoString: keyMaterial.infoString,
|
|
2110
|
-
bindVerifyKeysIntoDerivation: keyMaterial.bindVerifyKeysIntoDerivation
|
|
2111
|
-
})).then(() => createActionFrameCrypto({
|
|
2134
|
+
const { keyMaterial } = state;
|
|
2135
|
+
const cryptoReady = link.initialize().then(() => createActionFrameCrypto({
|
|
2112
2136
|
link,
|
|
2113
|
-
|
|
2137
|
+
keyMaterial
|
|
2114
2138
|
}));
|
|
2115
2139
|
cryptoReady.catch((err) => console.error("[ws-server] failed to restore encrypted session", err));
|
|
2116
2140
|
this._pipe = this._buildPipe(cryptoReady);
|
|
@@ -2889,11 +2913,9 @@ async function runConnectorExchangeHandshake(carrier, secure) {
|
|
|
2889
2913
|
dictionaryVersion: secure.dictionaryVersion,
|
|
2890
2914
|
securityLevel: secure.securityLevel
|
|
2891
2915
|
});
|
|
2892
|
-
const hsid = (0, nanoid.nanoid)();
|
|
2893
2916
|
const hello = await handshake.createHello();
|
|
2894
2917
|
const welcomeReply = decodeExchangeReply(asString(await carrier.exchange(encodeExchange({
|
|
2895
2918
|
k: "hs",
|
|
2896
|
-
hsid,
|
|
2897
2919
|
m: encodeHandshakeMessage(hello)
|
|
2898
2920
|
}))));
|
|
2899
2921
|
if (welcomeReply?.k !== "hs") throw new Error("[exchange-handshake] expected a welcome reply");
|
|
@@ -2901,11 +2923,12 @@ async function runConnectorExchangeHandshake(carrier, secure) {
|
|
|
2901
2923
|
if (welcome == null) throw new Error("[exchange-handshake] malformed welcome");
|
|
2902
2924
|
if (welcome.t === "reject") throw new Error(`[exchange-handshake] rejected by peer: ${welcome.reason}`);
|
|
2903
2925
|
if (welcome.t !== "welcome") throw new Error(`[exchange-handshake] expected welcome, got ${welcome.t}`);
|
|
2926
|
+
if (welcomeReply.hsc == null) throw new Error("[exchange-handshake] welcome missing handshake continuation token");
|
|
2904
2927
|
const prove = await handshake.onWelcome(welcome);
|
|
2905
2928
|
const acceptReply = decodeExchangeReply(asString(await carrier.exchange(encodeExchange({
|
|
2906
2929
|
k: "hs",
|
|
2907
|
-
|
|
2908
|
-
|
|
2930
|
+
m: encodeHandshakeMessage(prove),
|
|
2931
|
+
hsc: welcomeReply.hsc
|
|
2909
2932
|
}))));
|
|
2910
2933
|
if (acceptReply?.k !== "hs") throw new Error("[exchange-handshake] expected an accept reply");
|
|
2911
2934
|
const accept = decodeHandshakeMessage(acceptReply.m);
|
|
@@ -2914,9 +2937,9 @@ async function runConnectorExchangeHandshake(carrier, secure) {
|
|
|
2914
2937
|
if (accept.t !== "accept") throw new Error(`[exchange-handshake] expected accept, got ${accept.t}`);
|
|
2915
2938
|
if (acceptReply.t == null) throw new Error("[exchange-handshake] accept missing session token");
|
|
2916
2939
|
const result = await handshake.onAccept(accept);
|
|
2917
|
-
const crypto = result.securityLevel === "encrypted" ? createActionFrameCrypto({
|
|
2940
|
+
const crypto = result.securityLevel === "encrypted" && result.encryptionKeyMaterial != null ? createActionFrameCrypto({
|
|
2918
2941
|
link: secure.link,
|
|
2919
|
-
|
|
2942
|
+
keyMaterial: result.encryptionKeyMaterial
|
|
2920
2943
|
}) : void 0;
|
|
2921
2944
|
return {
|
|
2922
2945
|
token: acceptReply.t,
|
|
@@ -3224,9 +3247,9 @@ async function runClientHandshake(channel, secure, nextHandshakeMessage) {
|
|
|
3224
3247
|
if (accept.t === "reject") throw new Error(`[link-handshake] rejected by peer: ${accept.reason}`);
|
|
3225
3248
|
if (accept.t !== "accept") throw new Error(`[link-handshake] expected accept, got ${accept.t}`);
|
|
3226
3249
|
const result = await handshake.onAccept(accept);
|
|
3227
|
-
return result.securityLevel === "encrypted" ? createActionFrameCrypto({
|
|
3250
|
+
return result.securityLevel === "encrypted" && result.encryptionKeyMaterial != null ? createActionFrameCrypto({
|
|
3228
3251
|
link: secure.link,
|
|
3229
|
-
|
|
3252
|
+
keyMaterial: result.encryptionKeyMaterial
|
|
3230
3253
|
}) : void 0;
|
|
3231
3254
|
}
|
|
3232
3255
|
function buildSendMethods(ctx, pipe, disconnectListeners, abortSet) {
|
|
@@ -3380,82 +3403,154 @@ var LinkTransport = class LinkTransport extends Transport {
|
|
|
3380
3403
|
}
|
|
3381
3404
|
};
|
|
3382
3405
|
//#endregion
|
|
3406
|
+
//#region src/ActionRuntime/Transport/SecureSession/exchangeTicketSeal.ts
|
|
3407
|
+
const SEALED_ENVELOPE_LENGTH = 2;
|
|
3408
|
+
function createExchangeTicketSealer(link, options = {}) {
|
|
3409
|
+
let keyPromise;
|
|
3410
|
+
const getKey = () => {
|
|
3411
|
+
if (keyPromise == null) keyPromise = link.deriveLocalSealKey({ version: options.version }).catch((err) => {
|
|
3412
|
+
keyPromise = void 0;
|
|
3413
|
+
throw err;
|
|
3414
|
+
});
|
|
3415
|
+
return keyPromise;
|
|
3416
|
+
};
|
|
3417
|
+
return {
|
|
3418
|
+
async seal(value, ttlMs) {
|
|
3419
|
+
const { nonce, ciphertext } = await (0, _nice_code_util.encryptBytesWithAesGcmKey)({
|
|
3420
|
+
dataToEncrypt: (0, msgpackr.pack)({
|
|
3421
|
+
v: value,
|
|
3422
|
+
exp: Date.now() + ttlMs
|
|
3423
|
+
}),
|
|
3424
|
+
aesGcmKey: await getKey()
|
|
3425
|
+
});
|
|
3426
|
+
return bytesToBase64((0, msgpackr.pack)([nonce, ciphertext]));
|
|
3427
|
+
},
|
|
3428
|
+
async open(blob) {
|
|
3429
|
+
try {
|
|
3430
|
+
const envelope = (0, msgpackr.unpack)(base64ToBytes(blob));
|
|
3431
|
+
if (!Array.isArray(envelope) || envelope.length !== SEALED_ENVELOPE_LENGTH) return void 0;
|
|
3432
|
+
const [nonce, ciphertext] = envelope;
|
|
3433
|
+
if (!(nonce instanceof Uint8Array) || !(ciphertext instanceof Uint8Array)) return void 0;
|
|
3434
|
+
const payload = (0, msgpackr.unpack)(await (0, _nice_code_util.decryptBytesWithAesGcmKey)({
|
|
3435
|
+
dataToDecrypt: {
|
|
3436
|
+
nonce,
|
|
3437
|
+
ciphertext
|
|
3438
|
+
},
|
|
3439
|
+
aesGcmKey: await getKey()
|
|
3440
|
+
}));
|
|
3441
|
+
if (payload == null || typeof payload.exp !== "number" || payload.exp < Date.now()) return;
|
|
3442
|
+
return payload.v;
|
|
3443
|
+
} catch {
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
//#endregion
|
|
3383
3450
|
//#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.ts
|
|
3451
|
+
/** The default session-ticket lifetime — see {@link IExchangeAcceptorConfig.sessionTtlMs}. */
|
|
3452
|
+
const DEFAULT_SESSION_TTL_MS = 720 * 60 * 1e3;
|
|
3453
|
+
/** The handshake continuation (`hsc`) only bridges the two handshake POSTs, so it expires quickly. */
|
|
3454
|
+
const HANDSHAKE_CONTINUATION_TTL_MS = 120 * 1e3;
|
|
3384
3455
|
const textEncoder = new TextEncoder();
|
|
3385
3456
|
const textDecoder = new TextDecoder();
|
|
3386
3457
|
/**
|
|
3387
3458
|
* Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to
|
|
3388
3459
|
* {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
|
|
3389
|
-
* acceptor drives the server handshake over the two `hs` POSTs
|
|
3390
|
-
*
|
|
3391
|
-
*
|
|
3392
|
-
* and returns the (encrypted) result inline as the reply.
|
|
3460
|
+
* acceptor drives the server handshake over the two `hs` POSTs, mints a session **token** on accept, and
|
|
3461
|
+
* on every later `act` POST resolves the session by token, decrypts the body (at `encrypted`), routes it
|
|
3462
|
+
* through the runtime, and returns the (encrypted) result inline as the reply.
|
|
3393
3463
|
*
|
|
3394
|
-
*
|
|
3395
|
-
*
|
|
3396
|
-
*
|
|
3464
|
+
* **Stateless.** It holds no in-memory handshakes or sessions: the in-flight handshake `pending` is
|
|
3465
|
+
* sealed into the `hsc` continuation token returned on `welcome` and echoed back on `prove`, and the live
|
|
3466
|
+
* session is sealed into the `t` token replayed on every `act`. Both are sealed under the acceptor's own
|
|
3467
|
+
* persisted identity ({@link createExchangeTicketSealer}), so any isolate that loaded the same identity
|
|
3468
|
+
* can serve any POST — no request needs to co-locate with another (no Durable Object required just to
|
|
3469
|
+
* pin a handshake to one instance). A tampered, wrong-key, or expired token opens to "no valid session".
|
|
3397
3470
|
*/
|
|
3398
3471
|
var ExchangeAcceptor = class {
|
|
3399
3472
|
_security;
|
|
3400
3473
|
_runtime;
|
|
3401
3474
|
_allowedLevels;
|
|
3402
3475
|
_noneAllowed;
|
|
3403
|
-
|
|
3404
|
-
|
|
3476
|
+
_sealer;
|
|
3477
|
+
_sessionTtlMs;
|
|
3405
3478
|
constructor(config) {
|
|
3406
3479
|
this._security = config.security;
|
|
3407
3480
|
this._runtime = config.runtime;
|
|
3408
3481
|
this._allowedLevels = Array.isArray(config.security.securityLevel) ? config.security.securityLevel : [config.security.securityLevel];
|
|
3409
3482
|
this._noneAllowed = this._allowedLevels.includes("none");
|
|
3483
|
+
this._sealer = createExchangeTicketSealer(config.security.link);
|
|
3484
|
+
this._sessionTtlMs = config.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;
|
|
3410
3485
|
}
|
|
3411
3486
|
/** Process one POST body (an exchange envelope), returning the reply body to send back. */
|
|
3412
3487
|
async handlePost(body) {
|
|
3413
3488
|
const request = decodeExchangeRequest(body);
|
|
3414
3489
|
if (request == null) return this._err("malformed exchange request");
|
|
3490
|
+
await this._security.link.initialize();
|
|
3415
3491
|
if (request.k === "hs") return encodeExchange(await this._handleHandshake(request));
|
|
3416
3492
|
return encodeExchange(await this._handleAction(request));
|
|
3417
3493
|
}
|
|
3494
|
+
_makeHandshake() {
|
|
3495
|
+
const security = this._security;
|
|
3496
|
+
return createServerHandshake({
|
|
3497
|
+
link: security.link,
|
|
3498
|
+
localCoordinate: security.localCoordinate,
|
|
3499
|
+
dictionaryVersion: security.dictionaryVersion,
|
|
3500
|
+
securityLevel: security.securityLevel,
|
|
3501
|
+
verifyKeyResolver: security.verifyKeyResolver
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3418
3504
|
async _handleHandshake(request) {
|
|
3419
3505
|
const message = decodeHandshakeMessage(request.m);
|
|
3420
3506
|
if (message == null) return {
|
|
3421
3507
|
k: "err",
|
|
3422
3508
|
message: "malformed handshake message"
|
|
3423
3509
|
};
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3510
|
+
if (message.t === "hello") {
|
|
3511
|
+
const handshake = this._makeHandshake();
|
|
3512
|
+
const reply = await handshake.onHello(message);
|
|
3513
|
+
if (reply.t === "reject") return {
|
|
3514
|
+
k: "hs",
|
|
3515
|
+
m: encodeHandshakeMessage(reply)
|
|
3516
|
+
};
|
|
3517
|
+
const pending = handshake.exportPending();
|
|
3518
|
+
if (pending == null) return {
|
|
3519
|
+
k: "err",
|
|
3520
|
+
message: "handshake produced no continuation state"
|
|
3521
|
+
};
|
|
3522
|
+
const hsc = await this._sealer.seal(pending, HANDSHAKE_CONTINUATION_TTL_MS);
|
|
3523
|
+
return {
|
|
3524
|
+
k: "hs",
|
|
3525
|
+
m: encodeHandshakeMessage(reply),
|
|
3526
|
+
hsc
|
|
3527
|
+
};
|
|
3436
3528
|
}
|
|
3437
|
-
if (message.t === "hello") return {
|
|
3438
|
-
k: "hs",
|
|
3439
|
-
m: encodeHandshakeMessage(await handshake.onHello(message))
|
|
3440
|
-
};
|
|
3441
3529
|
if (message.t === "prove") {
|
|
3530
|
+
if (request.hsc == null) return {
|
|
3531
|
+
k: "err",
|
|
3532
|
+
message: "prove missing continuation token"
|
|
3533
|
+
};
|
|
3534
|
+
const pending = await this._sealer.open(request.hsc);
|
|
3535
|
+
if (pending == null) return {
|
|
3536
|
+
k: "err",
|
|
3537
|
+
message: "invalid or expired continuation token"
|
|
3538
|
+
};
|
|
3539
|
+
const handshake = this._makeHandshake();
|
|
3540
|
+
await handshake.restorePending(pending);
|
|
3442
3541
|
const reply = await handshake.onProve(message);
|
|
3443
|
-
this._pendingHandshakes.delete(request.hsid);
|
|
3444
3542
|
const result = handshake.getResult();
|
|
3445
3543
|
if (reply.t === "accept" && result != null) {
|
|
3446
|
-
const
|
|
3447
|
-
|
|
3448
|
-
client: new RuntimeCoordinate(result.remote),
|
|
3544
|
+
const ticket = {
|
|
3545
|
+
client: result.remote,
|
|
3449
3546
|
securityLevel: result.securityLevel,
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
}) : void 0
|
|
3454
|
-
});
|
|
3547
|
+
keyMaterial: result.encryptionKeyMaterial
|
|
3548
|
+
};
|
|
3549
|
+
const t = await this._sealer.seal(ticket, this._sessionTtlMs);
|
|
3455
3550
|
return {
|
|
3456
3551
|
k: "hs",
|
|
3457
3552
|
m: encodeHandshakeMessage(reply),
|
|
3458
|
-
t
|
|
3553
|
+
t
|
|
3459
3554
|
};
|
|
3460
3555
|
}
|
|
3461
3556
|
return {
|
|
@@ -3469,20 +3564,26 @@ var ExchangeAcceptor = class {
|
|
|
3469
3564
|
};
|
|
3470
3565
|
}
|
|
3471
3566
|
async _handleAction(request) {
|
|
3472
|
-
let
|
|
3567
|
+
let client;
|
|
3568
|
+
let crypto;
|
|
3473
3569
|
let candidate;
|
|
3474
3570
|
if (request.t != null) {
|
|
3475
|
-
|
|
3476
|
-
if (
|
|
3571
|
+
const ticket = await this._sealer.open(request.t);
|
|
3572
|
+
if (ticket == null) return {
|
|
3477
3573
|
k: "err",
|
|
3478
3574
|
message: "unknown or expired session token"
|
|
3479
3575
|
};
|
|
3576
|
+
client = new RuntimeCoordinate(ticket.client);
|
|
3577
|
+
crypto = ticket.securityLevel === "encrypted" && ticket.keyMaterial != null ? createActionFrameCrypto({
|
|
3578
|
+
link: this._security.link,
|
|
3579
|
+
keyMaterial: ticket.keyMaterial
|
|
3580
|
+
}) : void 0;
|
|
3480
3581
|
if ("c" in request) {
|
|
3481
|
-
if (
|
|
3582
|
+
if (crypto == null) return {
|
|
3482
3583
|
k: "err",
|
|
3483
3584
|
message: "session is not encrypted"
|
|
3484
3585
|
};
|
|
3485
|
-
const plain = await
|
|
3586
|
+
const plain = await crypto.decryptFrame(base64ToBytes(request.c));
|
|
3486
3587
|
candidate = JSON.parse(textDecoder.decode(plain));
|
|
3487
3588
|
} else candidate = request.w;
|
|
3488
3589
|
} else {
|
|
@@ -3497,11 +3598,11 @@ var ExchangeAcceptor = class {
|
|
|
3497
3598
|
message: "malformed action wire"
|
|
3498
3599
|
};
|
|
3499
3600
|
const wire = candidate;
|
|
3500
|
-
if (
|
|
3601
|
+
if (client != null && wire.type === "request") wire.context.originClient = client.toJsonObject();
|
|
3501
3602
|
const resultWire = (await (await this._runtime.handleActionPayloadWire(wire)).waitForResultPayload()).toJsonObject();
|
|
3502
|
-
if (
|
|
3603
|
+
if (crypto != null && "c" in request) return {
|
|
3503
3604
|
k: "act",
|
|
3504
|
-
c: bytesToBase64(await
|
|
3605
|
+
c: bytesToBase64(await crypto.encryptFrame(textEncoder.encode(JSON.stringify(resultWire))))
|
|
3505
3606
|
};
|
|
3506
3607
|
return {
|
|
3507
3608
|
k: "act",
|
|
@@ -4078,4 +4179,4 @@ Object.defineProperty(exports, "runtimeLinkId", {
|
|
|
4078
4179
|
}
|
|
4079
4180
|
});
|
|
4080
4181
|
|
|
4081
|
-
//# sourceMappingURL=createHibernatableWsServerAdapter-
|
|
4182
|
+
//# sourceMappingURL=createHibernatableWsServerAdapter-j96U9vgo.cjs.map
|