WiP Noise tunnel

This commit is contained in:
Emery Hemingway 2024-01-20 19:45:29 +02:00
parent 2e027afc42
commit 760ce84a8c
3 changed files with 172 additions and 1 deletions

View File

@ -1,3 +1,3 @@
include depends.tup
NIM = $(DIRENV) nim
NIM = $(DIRENV) $(NIM)
NIM_GROUPS += $(TUP_CWD)/<lock>

View File

@ -1,3 +1,4 @@
include ../preserves-nim/depends.tup
NIM_FLAGS += --path:$(TUP_CWD)/../preserves-nim/src
NIM_FLAGS += --path:$(TUP_CWD)/../noiseprotocol/src
NIM_GROUPS += $(TUP_CWD)/<protocol>

View File

@ -298,7 +298,156 @@ proc accepted(cap: Cap): Resolved =
result = Resolved(orKind: ResolvedKind.accepted)
result.accepted.responderSession = cap
import syndicate/protocols/noise
import noiseprotocol
type
NoiseTunnelEntity = ref object of Entity
handshake: HandshakeState
sendCipher, recvCipher: CipherState
peer: Cap
relay: Relay
action: ACTION
method message(tunnel: NoiseTunnelEntity; turn: var Turn; v: AssertionRef) =
doAssert(v.value.isByteString, $v.value)
stderr.writeLine "NoiseTunnelEntity: received message of ", v.value.bytes.len, " bytes"
proc protocolName(spec: NoiseSpec): string =
if spec.protocol.isNone: "Noise_NK_25519_ChaChaPoly_BLAKE2s"
else: spec.protocol.get.string
proc handshake(turn: var Turn; tunnel: NoiseTunnelEntity; spec: NoiseSpec; cont: TurnAction) =
stderr.writeLine "handshaking"
let handshake = spec.protocolName.newByName(INITIATOR)
stderr.writeLine "got handshake"
handshake.set_prologue(spec.service.encode)
stderr.writeLine "prologue set"
if spec.preSharedKeys.isSome:
stderr.writeLine "spec.preSharedKeys.isSome:"
var keys: seq[seq[byte]]
stderr.writeLine("get preSharedKeys")
if not keys.fromPreserves(get spec.preSharedKeys):
raise newException(ValueError, "invalid preSharedKeys in NoiseSpec")
for key in keys:
handshake.setPreSharedKey(key)
stderr.writeLine("get_local_keypair_dh")
let dh = handshake.get_remote_public_key_dh
if not dh.isNil:
dh.set_public_key(spec.key)
stderr.writeLine "start handshake"
start(handshake)
while true:
tunnel.action = get_action(handshake)
stderr.writeLine("tunnel.action is ", $tunnel.action)
case tunnel.action
of SPLIT:
(tunnel.sendCipher, tunnel.recvCipher) = split(handshake)
destroy(handshake)
cont(turn)
of WRITE_MESSAGE:
var buf = newSeqOfCap[byte](256)
write_message(handshake, buf, @[])
tunnel.peer.target.message(turn, AssertionRef(value: buf.toPreserves))
of READ_MESSAGE:
break
else:
raiseAssert("action is " & $tunnel.action)
#[
type
NoiseTunnel = ref object
buffer: seq[byte]
handshake: HandshakeState
sendCipher, recvCipher: CipherState
proc noiseSend(state: NoiseTunnel; buf: seq[byte]) =
stderr.writeLine("noise send ", buf.len, " bytes")
var state = NoiseTunnel(tun)
if not state.handshake.isNil:
let action = get_action(state.handshake)
case action
of SPLIT:
(state.sendCipher, state.recvCipher) = split(state.handshake)
destroy(state.handshake)
send(state, buf)
of WRITE_MESSAGE:
doAssert(buf.len < MAX_PAYLOAD_LEN)
state.buffer.setLen(buf.len)
write_message(state.handshake, state.buffer, buf)
send(state.inner, state.buffer)
else:
raiseAssert("bad noise handshake state " & $action)
else:
doAssert(buf.len < MAX_PAYLOAD_LEN)
state.buffer.setLen(buf.len)
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
encrypt(state.sendCipher, state.buffer)
send(state.inner, state.buffer)
proc noiseRecv(tun: Tunnel; buf: seq[byte]) =
stderr.writeLine("noise recv ", buf.len, " bytes")
var state = NoiseTunnel(tun)
if not state.handshake.isNil:
let action = get_action(state.handshake)
case action
of SPLIT:
(state.sendCipher, state.recvCipher) = split(state.handshake)
destroy(state.handshake)
send(state, buf)
of READ_MESSAGE:
doAssert(buf.len < MAX_PAYLOAD_LEN)
state.buffer.setLen(buf.len)
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
read_message(state.handshake, buf, state.buffer)
recv(state.outer, state.buffer)
else:
raiseAssert("bad noise handshake state " & $action)
else:
doAssert(buf.len < MAX_PAYLOAD_LEN)
state.buffer.setLen(buf.len)
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
decrypt(state.sendCipher, state.buffer)
recv(state.outer, state.buffer)
proc newNoiseTunnel(facet: Facet; spec: NoiseSpec): NoiseTunnel =
noiseprotocol.init()
let state = NoiseTunnel(
send: noiseSend,
recv: noiseRecv,
handshake: spec.protocolName.newByName(INITIATOR)
)
state.handshake.set_prologue(spec.service.encode)
if spec.preSharedKeys.isSome:
var keys: seq[seq[byte]]
if not keys.fromPreserves(get spec.preSharedKeys):
raise newException(ValueError, "invalid preSharedKeys in NoiseSpec")
for key in keys:
state.handshake.setPreSharedKey(key)
if spec.key.len > 0: # responder public key
state.handshake.get_local_keypair_dh.set_public_key(spec.key)
start(state.handshake)
]#
proc spawnNoiseRelay(turn: var Turn; ds, origin: Cap; spec: NoiseSpec) =
let tunnel = NoiseTunnelEntity(peer: origin)
proc noiseWriter(turn: var Turn; buf: seq[byte]) =
stderr.writeLine "NoiseTunnelEntity: need to send ", buf.len, " bytes"
var opts = RelayActorOptions(
packetWriter: noiseWriter,
initialCap: ds,
initialOid: 0.Oid.some,
)
spawnRelay("noise-relay", turn, opts) do (turn: var Turn; relay: Relay):
tunnel.relay = relay
#[
handshake(turn, tunnel, spec) do (turn: var Turn):
publish(turn, ds, ResolvedPathStep(
origin: origin,
pathStep: toRecord(toSymbol"noise", spec),
resolved: tunnel.relay.peer.accepted,
))
]#
when defined(posix):
@ -510,6 +659,27 @@ proc spawnRelays*(turn: var Turn; ds: Cap) =
observer: newCap(turn, during(duringCallback)),
))
spawn("noise-step", turn) do (turn: var Turn):
let
stepPat = grabRecord(toSymbol"noise", grab())
pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grab(), 1: grab()}
during(turn, ds, pat) do (origin: Literal[Cap]; detail: Literal[NoiseSpec]):
let step = toRecord(Symbol"noise", detail.value)
proc duringCallback(turn: var Turn; ass: Assertion; h: Handle): TurnAction =
# TODO: better during thing
let facet = inFacet(turn) do (turn: var Turn):
var res = ass.preservesTo Resolved
if res.isSome and res.get.orKind == ResolvedKind.accepted:
if res.get.accepted.responderSession of Cap:
spawnNoiseRelay(turn, ds, res.get.accepted.responderSession.Cap, detail.value)
proc action(turn: var Turn) =
stop(turn, facet)
result = action
publish(turn, origin.value, Resolve(
step: step,
observer: newCap(turn, during(duringCallback)),
))
type BootProc* = proc (turn: var Turn; ds: Cap) {.gcsafe.}
proc envRoute*: Route =