Support arbitrary stream write sizes in noise

This commit is contained in:
Giovanni Petrantoni 2020-03-25 16:10:11 +09:00
parent 4199508f17
commit 1550bea1cc
2 changed files with 62 additions and 11 deletions

View File

@ -35,6 +35,8 @@ const
# Empty is a special value which indicates k has not yet been initialized. # Empty is a special value which indicates k has not yet been initialized.
EmptyKey: ChaChaPolyKey = [0.byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] EmptyKey: ChaChaPolyKey = [0.byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
NonceMax = uint64.high - 1 # max is reserved NonceMax = uint64.high - 1 # max is reserved
NoiseSize = 32
MaxPlainSize = int(uint16.high - NoiseSize - ChaChaPolyTag.len)
type type
KeyPair = object KeyPair = object
@ -282,7 +284,7 @@ proc sendHSMessage(sconn: Connection; buf: seq[byte]) {.async.} =
proc packNoisePayload(payload: openarray[byte]): seq[byte] = proc packNoisePayload(payload: openarray[byte]): seq[byte] =
let let
noiselen = rand(2..31) noiselen = rand(2..<NoiseSize)
plen = payload.len.uint16 plen = payload.len.uint16
var var
@ -427,17 +429,24 @@ method readMessage(sconn: NoiseConnection): Future[seq[byte]] {.async.} =
method writeMessage(sconn: NoiseConnection, message: seq[byte]): Future[void] {.async.} = method writeMessage(sconn: NoiseConnection, message: seq[byte]): Future[void] {.async.} =
try: try:
let
packed = packNoisePayload(message)
cipher = sconn.writeCs.encryptWithAd([], packed)
var var
lesize = cipher.len.uint16 left = message.len
besize = lesize.toBytesBE offset = 0
outbuf = newSeqOfCap[byte](cipher.len + 2) while left > 0:
trace "sendEncryptedMessage", size = lesize, peer = $sconn.peerInfo let
outbuf &= besize chunkSize = if left > MaxPlainSize: MaxPlainSize else: left
outbuf &= cipher packed = packNoisePayload(message.toOpenArray(offset, offset + chunkSize - 1))
await sconn.write(outbuf) cipher = sconn.writeCs.encryptWithAd([], packed)
left = left - chunkSize
offset = offset + chunkSize
var
lesize = cipher.len.uint16
besize = lesize.toBytesBE
outbuf = newSeqOfCap[byte](cipher.len + 2)
trace "sendEncryptedMessage", size = lesize, peer = $sconn.peerInfo, left, offset
outbuf &= besize
outbuf &= cipher
await sconn.write(outbuf)
except AsyncStreamWriteError: except AsyncStreamWriteError:
trace "Could not write to connection" trace "Could not write to connection"

View File

@ -10,6 +10,7 @@
import unittest, tables import unittest, tables
import chronos import chronos
import chronicles import chronicles
import nimcrypto/sysrand
import ../libp2p/crypto/crypto import ../libp2p/crypto/crypto
import ../libp2p/[switch, import ../libp2p/[switch,
multistream, multistream,
@ -134,6 +135,47 @@ suite "Noise":
check: check:
waitFor(testListenerDialer()) == true waitFor(testListenerDialer()) == true
test "e2e: handle read + noise fragmented":
proc testListenerDialer(): Future[bool] {.async.} =
let
server: MultiAddress = Multiaddress.init("/ip4/0.0.0.0/tcp/0")
serverInfo = PeerInfo.init(PrivateKey.random(RSA), [server])
serverNoise = newNoise(serverInfo.privateKey, outgoing = false)
readTask = newFuture[void]()
var hugePayload = newSeq[byte](0xFFFFF)
check randomBytes(hugePayload) == hugePayload.len
trace "Sending huge payload", size = hugePayload.len
proc connHandler(conn: Connection) {.async, gcsafe.} =
let sconn = await serverNoise.secure(conn)
defer:
await sconn.close()
let msg = await sconn.readLp()
check msg == hugePayload
readTask.complete()
let
transport1: TcpTransport = newTransport(TcpTransport)
asyncCheck await transport1.listen(server, connHandler)
let
transport2: TcpTransport = newTransport(TcpTransport)
clientInfo = PeerInfo.init(PrivateKey.random(RSA), [transport1.ma])
clientNoise = newNoise(clientInfo.privateKey, outgoing = true)
conn = await transport2.dial(transport1.ma)
sconn = await clientNoise.secure(conn)
await sconn.writeLp(hugePayload)
await readTask
await sconn.close()
await transport1.close()
result = true
check:
waitFor(testListenerDialer()) == true
test "e2e use switch dial proto string": test "e2e use switch dial proto string":
proc testSwitch(): Future[bool] {.async, gcsafe.} = proc testSwitch(): Future[bool] {.async, gcsafe.} =
let ma1: MultiAddress = Multiaddress.init("/ip4/0.0.0.0/tcp/0") let ma1: MultiAddress = Multiaddress.init("/ip4/0.0.0.0/tcp/0")