Compare commits

...

3 Commits

Author SHA1 Message Date
Tanguy
a797b5e408 better test 2022-12-16 13:45:21 +01:00
Tanguy
56b284a964 fix init 2022-12-16 12:05:55 +01:00
Tanguy
1d17859eeb Implement plaintext encryption v2 2022-12-16 11:58:40 +01:00
5 changed files with 209 additions and 12 deletions

View File

@@ -26,7 +26,7 @@ import
switch, peerid, peerinfo, stream/connection, multiaddress,
crypto/crypto, transports/[transport, tcptransport],
muxers/[muxer, mplex/mplex, yamux/yamux],
protocols/[identify, secure/secure, secure/noise, rendezvous],
protocols/[identify, secure/secure, secure/noise, secure/plaintext, rendezvous],
protocols/connectivity/[autonat, relay/relay, relay/client, relay/rtransport],
connmanager, upgrademngrs/muxedupgrade,
nameresolving/nameresolver,
@@ -40,6 +40,7 @@ type
SecureProtocol* {.pure.} = enum
Noise,
PlainText,
Secio {.deprecated.}
SwitchBuilder* = ref object
@@ -134,6 +135,11 @@ proc withNoise*(b: SwitchBuilder): SwitchBuilder {.public.} =
b.secureManagers.add(SecureProtocol.Noise)
b
proc withPlainText*(b: SwitchBuilder): SwitchBuilder {.public.} =
warn "Using plain text encryption!"
b.secureManagers.add(SecureProtocol.PlainText)
b
proc withTransport*(b: SwitchBuilder, prov: TransportProvider): SwitchBuilder {.public.} =
## Use a custom transport
runnableExamples:
@@ -209,8 +215,13 @@ proc build*(b: SwitchBuilder): Switch
let
seckey = b.privKey.get(otherwise = pkRes.expect("Expected default Private Key"))
if b.secureManagers.len == 0:
b.secureManagers &= SecureProtocol.Noise
var
secureManagerInstances: seq[Secure]
if SecureProtocol.PlainText in b.secureManagers:
secureManagerInstances.add(PlainText.new(seckey))
if SecureProtocol.Noise in b.secureManagers:
secureManagerInstances.add(Noise.new(b.rng, seckey).Secure)
@@ -234,9 +245,6 @@ proc build*(b: SwitchBuilder): Switch
transports.add(tProvider(muxedUpgrade))
transports
if b.secureManagers.len == 0:
b.secureManagers &= SecureProtocol.Noise
if isNil(b.rng):
b.rng = newRng()

View File

@@ -15,20 +15,57 @@ else:
import chronos
import secure, ../../stream/connection
const PlainTextCodec* = "/plaintext/1.0.0"
const PlainTextCodec* = "/plaintext/2.0.0"
type
PlainText* = ref object of Secure
localPublicKey: PublicKey
method init(p: PlainText) {.gcsafe.} =
proc handle(conn: Connection, proto: string)
{.async, gcsafe.} = discard
## plain text doesn't do anything
PlainTextError* = object of LPError
PlainTextConnection* = ref object of SecureConn
method readMessage*(sconn: PlainTextConnection): Future[seq[byte]] {.async.} =
var buffer: array[32768, byte]
let length = await sconn.stream.readOnce(addr buffer[0], buffer.len)
return @(buffer[0 ..< length])
method write*(sconn: PlainTextConnection, message: seq[byte]): Future[void] =
sconn.stream.write(message)
method handshake*(p: PlainText, conn: Connection, initiator: bool, peerId: Opt[PeerId]): Future[SecureConn] {.async.} =
var exchange = initProtoBuffer()
exchange.write(2, p.localPublicKey)
await conn.writeLp(exchange.buffer)
let
remoteData = await conn.readLp(1024)
remotePb = initProtoBuffer(remoteData)
var remotePk: PublicKey
remotePb.getRequiredField(2, remotePk).tryGet()
let remotePeerId = PeerId.init(remotePk).valueOr:
raise newException(PlainTextError, "Invalid remote peer id: " & $error)
if peerId.isSome:
if peerId.get() != remotePeerId:
raise newException(PlainTextError, "Plain text handshake, peer id don't match! " & $remotePeerId & " != " & $peerId)
var res = PlainTextConnection.new(conn, conn.peerId, conn.observedAddr)
return res
method init*(p: PlainText) {.gcsafe.} =
procCall Secure(p).init()
p.codec = PlainTextCodec
p.handler = handle
proc new*(T: typedesc[PlainText]): T =
let plainText = T()
proc new*(
T: typedesc[PlainText],
privateKey: PrivateKey
): T =
let pk = privateKey.getPublicKey()
.expect("Expected valid Private Key")
let plainText = T(localPublicKey: pk)
plainText.init()
plainText

View File

@@ -32,6 +32,7 @@ import testtcptransport,
testconnmngr,
testswitch,
testnoise,
testplaintext,
testpeerinfo,
testpeerstore,
testping,

111
tests/testplaintext.nim Normal file
View File

@@ -0,0 +1,111 @@
# Nim-LibP2P
# Copyright (c) 2022 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/plaintext,
protocols/secure/secure,
upgrademngrs/muxedupgrade,
connmanager]
import ./helpers
suite "Plain text":
teardown:
checkTrackers()
asyncTest "e2e: handle write & read":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverPlainText = PlainText.new(serverPrivKey)
let transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
await transport1.start(server)
proc acceptHandler() {.async.} =
let conn = await transport1.accept()
let sconn = await serverPlainText.secure(conn, false, Opt.none(PeerId))
try:
await sconn.writeLp("Hello 1!")
await sconn.writeLp("Hello 2!")
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)
clientPlainText = PlainText.new(clientPrivKey)
conn = await transport2.dial(transport1.addrs[0])
let sconn = await clientPlainText.secure(conn, true, Opt.some(serverInfo.peerId))
discard await sconn.readLp(100)
var msg = await sconn.readLp(100)
await sconn.close()
await conn.close()
await acceptFut
await transport1.stop()
await transport2.stop()
check string.fromBytes(msg) == "Hello 2!"
asyncTest "e2e: wrong peerid":
let
server = @[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()]
serverPrivKey = PrivateKey.random(ECDSA, rng[]).get()
serverInfo = PeerInfo.new(serverPrivKey, server)
serverPlainText = PlainText.new(serverPrivKey)
let transport1: TcpTransport = TcpTransport.new(upgrade = Upgrade())
await transport1.start(server)
proc acceptHandler() {.async.} =
let conn = await transport1.accept()
try:
discard await serverPlainText.secure(conn, false, Opt.none(PeerId))
finally:
await conn.close()
let
acceptFut = acceptHandler()
transport2: TcpTransport = TcpTransport.new(upgrade = Upgrade())
clientPrivKey = PrivateKey.random(ECDSA, rng[]).get()
clientInfo = PeerInfo.new(clientPrivKey, transport1.addrs)
clientPlainText = PlainText.new(clientPrivKey)
conn = await transport2.dial(transport1.addrs[0])
expect(CatchableError):
discard await clientPlainText.secure(conn, true, Opt.some(clientInfo.peerId))
await conn.close()
await acceptFut
await transport1.stop()
await transport2.stop()

View File

@@ -77,6 +77,46 @@ suite "Switch":
check not switch1.isConnected(switch2.peerInfo.peerId)
check not switch2.isConnected(switch1.peerInfo.peerId)
asyncTest "e2e plaintext encryption":
let done = newFuture[void]()
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =
try:
let msg = string.fromBytes(await conn.readLp(1024))
check "Hello!" == msg
await conn.writeLp("Hello!")
finally:
await conn.close()
done.complete()
let testProto = new TestProto
testProto.codec = TestCodec
testProto.handler = handle
let switch1 = newStandardSwitch(secureManagers=[PlainText])
switch1.mount(testProto)
let switch2 = newStandardSwitch(secureManagers=[PlainText])
await switch1.start()
await switch2.start()
let conn = await switch2.dial(switch1.peerInfo.peerId, switch1.peerInfo.addrs, TestCodec)
check switch1.isConnected(switch2.peerInfo.peerId)
check switch2.isConnected(switch1.peerInfo.peerId)
await conn.writeLp("Hello!")
let msg = string.fromBytes(await conn.readLp(1024))
check "Hello!" == msg
await conn.close()
await allFuturesThrowing(
done.wait(5.seconds),
switch1.stop(),
switch2.stop())
check not switch1.isConnected(switch2.peerInfo.peerId)
check not switch2.isConnected(switch1.peerInfo.peerId)
asyncTest "e2e use switch dial proto string with custom matcher":
let done = newFuture[void]()
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =