From cbcd4b9ad904c474c8c09743e69ffda8f0f3a384 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 28 Mar 2024 16:30:27 +0100 Subject: [PATCH] Repair noise session introduction --- packages/ws-relay/src/index.ts | 170 +++++++++++++++++---------------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/packages/ws-relay/src/index.ts b/packages/ws-relay/src/index.ts index 4bb7075..8779a5e 100644 --- a/packages/ws-relay/src/index.ts +++ b/packages/ws-relay/src/index.ts @@ -232,33 +232,90 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon if (!origin0 || !isEmbedded(origin0)) return; const origin = origin0; - const spec = Q.drop_lit(detailPatValue, N.toNoisePathStepDetail); - if (!spec) return; + const spec0 = Q.drop_lit(detailPatValue, N.toNoisePathStepDetail); + if (!spec0) return; + const spec = spec0; const algorithms = SaltyCrypto.Noise_25519_ChaChaPoly_BLAKE2s; const protocol = spec.protocol._variant === "present" ? spec.protocol.protocol : spec.protocol._variant === "absent" ? N.fromDefaultProtocol(N.DefaultProtocol()) as string : (() => { throw new Error("Invalid noise protocol name"); })(); - const patternName = SaltyCrypto.matchPattern(algorithms, protocol); - if (patternName === null) throw new Error("Unsupported protocol " + protocol); + + const patternName0 = SaltyCrypto.matchPattern(algorithms, protocol); + if (patternName0 === null) throw new Error("Unsupported protocol " + protocol); + const patternName = patternName0; + const preSharedKeys = spec.preSharedKeys._variant === "present" ? spec.preSharedKeys.preSharedKeys : spec.preSharedKeys._variant === "absent" ? [] : (() => { throw new Error("Invalid pre-shared keys"); })(); const prologue = underlying(canonicalEncode(spec.service)); - let H: SaltyCrypto.Handshake | null = null; - let transportState: SaltyCrypto.TransportState | null = null; - let responderSession: Ref | null = null; - let relay: Relay.Relay | null = null; - - function maybeTransition(s: SaltyCrypto.TransportState | null) { - if (transportState !== null) { - throw new Error("Unexpected double-transition to transport state"); + react { + at origin { + assert G.Resolve({ + "step": G.Step({ + "stepType": N.$noise, + "detail": fromJS(N.ServiceSelector(spec.service)), + }), + "observer": create assertionFacetObserver(e => { + const response = G.toResolved(e); + if (!response) return; + switch (response._variant) { + case "accepted": + runSession(response.responderSession); + break; + case "Rejected": + stop { + at ds { + assert G.ResolvedPathStep({ + "origin": origin, + "pathStep": G.PathStep({ + "stepType": N.$noise, + "detail": fromJS(N.NoisePathStepDetail(spec)), + }), + "resolved": response, + }); + } + } + } + }), + }); } - transportState = s; - if (transportState !== null) { + } + + function runSession(responderSession: Ref) { + const H = new SaltyCrypto.Handshake( + algorithms, + patternName, + 'initiator', + { + prologue, + remoteStaticPublicKey: underlying(spec.key), + preSharedKeys: preSharedKeys.map(underlying), + }); + let transportState: SaltyCrypto.TransportState | null = null; + let relay: Relay.Relay | null = null; + + const { packet, finished } = H.writeMessage(new Uint8Array()); + at responderSession { + assert N.Initiator(create ({ message: handlePacket })); + send message Bytes.from(packet); + } + maybeTransition(finished); + + function maybeTransition(s: SaltyCrypto.TransportState | null) { + if (transportState !== null) { + throw new Error("Unexpected double-transition to transport state"); + } + transportState = s; + if (transportState !== null) { + actuallyTransition(); + } + } + + function actuallyTransition() { const noiseSessionFacet = Turn.activeFacet; const peer = new Relay.Relay({ debug, @@ -283,74 +340,25 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon "resolved": G.Resolved.accepted(peer), }); } - } - react { - at origin { - assert G.Resolve({ - "step": G.Step({ - "stepType": N.$noise, - "detail": fromJS(N.ServiceSelector(spec.service)), - }), - "observer": create ({ - ... assertionFacetObserver(e => { - const response = G.toResolved(e); - if (!response) return; - switch (response._variant) { - case "accepted": - H = new SaltyCrypto.Handshake( - algorithms, - patternName, - 'initiator', - { - prologue, - remoteStaticPublicKey: underlying(spec.key), - preSharedKeys: preSharedKeys.map(underlying), - }); - transportState = null; - responderSession = response.responderSession; - const { packet, finished } = H.writeMessage(new Uint8Array()); - at responderSession { - send message Bytes.from(packet); - } - maybeTransition(finished); - break; - case "Rejected": - stop { - at ds { - assert G.ResolvedPathStep({ - "origin": origin, - "pathStep": G.PathStep({ - "stepType": N.$noise, - "detail": fromJS(N.NoisePathStepDetail(spec)), - }), - "resolved": response, - }); - } - } - } - }), - message(body: Assertion) { - const p = N.asPacket(body); - if (transportState) { - const packet = transportState.recv.decrypt_large( - p._variant === 'complete' - ? [underlying(p.value)] - : p.value.map(underlying)); - relay!.accept(packet); - } else { - if (p._variant !== 'complete') { - throw new Error("Unexpected fragmentation in handshake"); - } - const { message, finished } = H!.readMessage(underlying(p.value)); - if (message.byteLength !== 0) { - throw new Error("Unexpected payload during handshake"); - } - maybeTransition(finished); - } - }, - }), - }); + function handlePacket(body: Assertion) { + const p = N.asPacket(body); + if (transportState) { + const packet = transportState.recv.decrypt_large( + p._variant === 'complete' + ? [underlying(p.value)] + : p.value.map(underlying)); + relay!.accept(packet); + } else { + if (p._variant !== 'complete') { + throw new Error("Unexpected fragmentation in handshake"); + } + const { message, finished } = H!.readMessage(underlying(p.value)); + if (message.byteLength !== 0) { + throw new Error("Unexpected payload during handshake"); + } + maybeTransition(finished); + } } } }