nim-libp2p-experimental/tests/testnoise.nim

304 lines
9.7 KiB
Nim

# Nim-LibP2P
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.used.}
import tables
import chronos, stew/byteutils
import chronicles
import ../libp2p/[switch,
errors,
multistream,
stream/bufferstream,
protocols/identify,
stream/connection,
transports/transport,
transports/tcptransport,
multiaddress,
peerinfo,
crypto/crypto,
protocols/protocol,
muxers/muxer,
muxers/mplex/mplex,
protocols/secure/noise,
protocols/secure/secio,
protocols/secure/secure,
upgrademngrs/muxedupgrade,
connmanager]
import ./helpers
const
TestCodec = "/test/proto/1.0.0"
type
TestProto = ref object of LPProtocol
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
method init(p: TestProto) {.gcsafe.} =
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =
let msg = string.fromBytes(await conn.readLp(1024))
check "Hello!" == msg
await conn.writeLp("Hello!")
await conn.close()
p.codec = TestCodec
p.handler = handle
{.pop.}
proc createSwitch(ma: MultiAddress; outgoing: bool, secio: bool = false): (Switch, PeerInfo) =
var
privateKey = PrivateKey.random(ECDSA, rng[]).get()
peerInfo = PeerInfo.new(privateKey, @[ma])
proc createMplex(conn: Connection): Muxer =
result = Mplex.new(conn)
let
identify = Identify.new(peerInfo)
peerStore = PeerStore.new(identify)
mplexProvider = MuxerProvider.new(createMplex, MplexCodec)
muxers = @[mplexProvider]
secureManagers = if secio:
[Secure(Secio.new(rng, privateKey))]
else:
[Secure(Noise.new(rng, privateKey, outgoing = outgoing))]
connManager = ConnManager.new()
ms = MultistreamSelect.new()
muxedUpgrade = MuxedUpgrade.new(muxers, secureManagers, connManager, ms)
transports = @[Transport(TcpTransport.new(upgrade = muxedUpgrade))]
let switch = newSwitch(
peerInfo,
transports,
secureManagers,
connManager,
ms,
peerStore)
result = (switch, peerInfo)
suite "Noise":
teardown:
checkTrackers()
asyncTest "e2e: handle write + noise":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverNoise = Noise.new(rng, serverPrivKey, outgoing = false)
let transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
asyncSpawn transport1.start(server)
proc acceptHandler() {.async.} =
let conn = await transport1.accept()
let sconn = await serverNoise.secure(conn, false, Opt.none(PeerId))
try:
await sconn.write("Hello!")
finally:
await sconn.close()
await conn.close()
let
acceptFut = acceptHandler()
transport2: TcpTransport = TcpTransport.new(upgrade = Upgrade())
clientPrivKey = PrivateKey.random(ECDSA, rng[]).get()
clientInfo = PeerInfo.new(clientPrivKey, transport1.addrs)
clientNoise = Noise.new(rng, clientPrivKey, outgoing = true)
conn = await transport2.dial(transport1.addrs[0])
let sconn = await clientNoise.secure(conn, true, Opt.some(serverInfo.peerId))
var msg = newSeq[byte](6)
await sconn.readExactly(addr msg[0], 6)
await sconn.close()
await conn.close()
await acceptFut
await transport1.stop()
await transport2.stop()
check string.fromBytes(msg) == "Hello!"
asyncTest "e2e: handle write + noise (wrong prologue)":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverNoise = Noise.new(rng, serverPrivKey, outgoing = false)
let
transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
asyncSpawn transport1.start(server)
proc acceptHandler() {.async, gcsafe.} =
var conn: Connection
try:
conn = await transport1.accept()
discard await serverNoise.secure(conn, false, Opt.none(PeerId))
except CatchableError:
discard
finally:
await conn.close()
let
handlerWait = acceptHandler()
transport2: TcpTransport = TcpTransport.new(upgrade = Upgrade())
clientPrivKey = PrivateKey.random(ECDSA, rng[]).get()
clientInfo = PeerInfo.new(clientPrivKey, transport1.addrs)
clientNoise = Noise.new(rng, clientPrivKey, outgoing = true, commonPrologue = @[1'u8, 2'u8, 3'u8])
conn = await transport2.dial(transport1.addrs[0])
var sconn: Connection = nil
expect(NoiseDecryptTagError):
sconn = await clientNoise.secure(conn, true, Opt.some(conn.peerId))
await conn.close()
await handlerWait
await transport1.stop()
await transport2.stop()
asyncTest "e2e: handle read + noise":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverNoise = Noise.new(rng, serverPrivKey, outgoing = false)
readTask = newFuture[void]()
let transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
asyncSpawn transport1.start(server)
proc acceptHandler() {.async, gcsafe.} =
let conn = await transport1.accept()
let sconn = await serverNoise.secure(conn, false, Opt.none(PeerId))
defer:
await sconn.close()
await conn.close()
var msg = newSeq[byte](6)
await sconn.readExactly(addr msg[0], 6)
check string.fromBytes(msg) == "Hello!"
let
acceptFut = acceptHandler()
transport2: TcpTransport = TcpTransport.new(upgrade = Upgrade())
clientPrivKey = PrivateKey.random(ECDSA, rng[]).get()
clientInfo = PeerInfo.new(clientPrivKey, transport1.addrs)
clientNoise = Noise.new(rng, clientPrivKey, outgoing = true)
conn = await transport2.dial(transport1.addrs[0])
let sconn = await clientNoise.secure(conn, true, Opt.some(serverInfo.peerId))
await sconn.write("Hello!")
await acceptFut
await sconn.close()
await conn.close()
await transport1.stop()
await transport2.stop()
asyncTest "e2e: handle read + noise fragmented":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverNoise = Noise.new(rng, serverPrivKey, outgoing = false)
readTask = newFuture[void]()
var hugePayload = newSeq[byte](0xFFFFF)
hmacDrbgGenerate(rng[], hugePayload)
trace "Sending huge payload", size = hugePayload.len
let
transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
listenFut = transport1.start(server)
proc acceptHandler() {.async, gcsafe.} =
let conn = await transport1.accept()
let sconn = await serverNoise.secure(conn, false, Opt.none(PeerId))
defer:
await sconn.close()
let msg = await sconn.readLp(1024*1024)
check msg == hugePayload
readTask.complete()
let
acceptFut = acceptHandler()
transport2: TcpTransport = TcpTransport.new(upgrade = Upgrade())
clientPrivKey = PrivateKey.random(ECDSA, rng[]).get()
clientInfo = PeerInfo.new(clientPrivKey, transport1.addrs)
clientNoise = Noise.new(rng, clientPrivKey, outgoing = true)
conn = await transport2.dial(transport1.addrs[0])
let sconn = await clientNoise.secure(conn, true, Opt.some(serverInfo.peerId))
await sconn.writeLp(hugePayload)
await readTask
await sconn.close()
await conn.close()
await acceptFut
await transport2.stop()
await transport1.stop()
await listenFut
asyncTest "e2e use switch dial proto string":
let ma1 = MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
let ma2 = MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
var peerInfo1, peerInfo2: PeerInfo
var switch1, switch2: Switch
(switch1, peerInfo1) = createSwitch(ma1, false)
let testProto = new TestProto
testProto.init()
testProto.codec = TestCodec
switch1.mount(testProto)
(switch2, peerInfo2) = createSwitch(ma2, true)
await switch1.start()
await switch2.start()
let conn = await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
await conn.writeLp("Hello!")
let msg = string.fromBytes(await conn.readLp(1024))
check "Hello!" == msg
await conn.close()
await allFuturesThrowing(
switch1.stop(),
switch2.stop())
asyncTest "e2e test wrong secure negotiation":
let ma1 = MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
let ma2 = MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
var peerInfo1, peerInfo2: PeerInfo
var switch1, switch2: Switch
(switch1, peerInfo1) = createSwitch(ma1, false)
let testProto = new TestProto
testProto.init()
testProto.codec = TestCodec
switch1.mount(testProto)
(switch2, peerInfo2) = createSwitch(ma2, true, true) # secio, we want to fail
await switch1.start()
await switch2.start()
expect(DialFailedError):
let conn = await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
await allFuturesThrowing(
switch1.stop(),
switch2.stop())