Fix: queue messages when there is no encryption key (#83)
* encodeMessagePacket: expose haskey encodeMessagePacket checks for session and behaves differently based on that. Exposing this difference in behavior. Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> * improve tracing of message exchange run e.g. as ``` nim c -r -d:debug -d:chronicles_enabled=on -d:chronicles_log_level=TRACE -d:chronicles_sinks=textlines[nocolors,stdout] tests/dht/test_providers.nim >err ``` Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> * add debug on Handshake timeour Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> * queue messages during handshake and send later If a handshake was already in progress, messages were dropped. Instead of this, it is better to queue these and send as soon as the handshake is finished and thus the encryption key is known. Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> * rename handshakeInProgress to keyexchangeInProgress Handshake is also a name of a message, which makes previous name less clear. Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> * keyexchangeInProgress: do not remove on handshake received This is the wrong direction, not needed Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com> --------- Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
parent
60dc4e764c
commit
66116b9bf6
|
@ -209,8 +209,9 @@ proc encodeStaticHeader*(flag: Flag, nonce: AESGCMNonce, authSize: int):
|
||||||
|
|
||||||
proc encodeMessagePacket*(rng: var HmacDrbgContext, c: var Codec,
|
proc encodeMessagePacket*(rng: var HmacDrbgContext, c: var Codec,
|
||||||
toId: NodeId, toAddr: Address, message: openArray[byte]):
|
toId: NodeId, toAddr: Address, message: openArray[byte]):
|
||||||
(seq[byte], AESGCMNonce) =
|
(seq[byte], AESGCMNonce, bool) =
|
||||||
var nonce: AESGCMNonce
|
var nonce: AESGCMNonce
|
||||||
|
var haskey: bool
|
||||||
hmacDrbgGenerate(rng, nonce) # Random AESGCM nonce
|
hmacDrbgGenerate(rng, nonce) # Random AESGCM nonce
|
||||||
var iv: array[ivSize, byte]
|
var iv: array[ivSize, byte]
|
||||||
hmacDrbgGenerate(rng, iv) # Random IV
|
hmacDrbgGenerate(rng, iv) # Random IV
|
||||||
|
@ -228,6 +229,7 @@ proc encodeMessagePacket*(rng: var HmacDrbgContext, c: var Codec,
|
||||||
var messageEncrypted: seq[byte]
|
var messageEncrypted: seq[byte]
|
||||||
var initiatorKey, recipientKey: AesKey
|
var initiatorKey, recipientKey: AesKey
|
||||||
if c.sessions.load(toId, toAddr, recipientKey, initiatorKey):
|
if c.sessions.load(toId, toAddr, recipientKey, initiatorKey):
|
||||||
|
haskey = true
|
||||||
messageEncrypted = encryptGCM(initiatorKey, nonce, message, @iv & header)
|
messageEncrypted = encryptGCM(initiatorKey, nonce, message, @iv & header)
|
||||||
discovery_session_lru_cache_hits.inc()
|
discovery_session_lru_cache_hits.inc()
|
||||||
else:
|
else:
|
||||||
|
@ -238,6 +240,7 @@ proc encodeMessagePacket*(rng: var HmacDrbgContext, c: var Codec,
|
||||||
# message. 16 bytes for the gcm tag and 4 bytes for ping with requestId of
|
# message. 16 bytes for the gcm tag and 4 bytes for ping with requestId of
|
||||||
# 1 byte (e.g "01c20101"). Could increase to 27 for 8 bytes requestId in
|
# 1 byte (e.g "01c20101"). Could increase to 27 for 8 bytes requestId in
|
||||||
# case this must not look like a random packet.
|
# case this must not look like a random packet.
|
||||||
|
haskey = false
|
||||||
var randomData: array[gcmTagSize + 4, byte]
|
var randomData: array[gcmTagSize + 4, byte]
|
||||||
hmacDrbgGenerate(rng, randomData)
|
hmacDrbgGenerate(rng, randomData)
|
||||||
messageEncrypted.add(randomData)
|
messageEncrypted.add(randomData)
|
||||||
|
@ -250,7 +253,7 @@ proc encodeMessagePacket*(rng: var HmacDrbgContext, c: var Codec,
|
||||||
packet.add(maskedHeader)
|
packet.add(maskedHeader)
|
||||||
packet.add(messageEncrypted)
|
packet.add(messageEncrypted)
|
||||||
|
|
||||||
return (packet, nonce)
|
return (packet, nonce, haskey)
|
||||||
|
|
||||||
proc encodeWhoareyouPacket*(rng: var HmacDrbgContext, c: var Codec,
|
proc encodeWhoareyouPacket*(rng: var HmacDrbgContext, c: var Codec,
|
||||||
toId: NodeId, toAddr: Address, requestNonce: AESGCMNonce, recordSeq: uint64,
|
toId: NodeId, toAddr: Address, requestNonce: AESGCMNonce, recordSeq: uint64,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
# Everything below the handling of ordinary messages
|
# Everything below the handling of ordinary messages
|
||||||
import
|
import
|
||||||
std/[tables, options],
|
std/[tables, options, sets],
|
||||||
bearssl/rand,
|
bearssl/rand,
|
||||||
chronos,
|
chronos,
|
||||||
chronicles,
|
chronicles,
|
||||||
|
@ -26,6 +26,8 @@ type
|
||||||
bindAddress: Address ## UDP binding address
|
bindAddress: Address ## UDP binding address
|
||||||
transp: DatagramTransport
|
transp: DatagramTransport
|
||||||
pendingRequests: Table[AESGCMNonce, PendingRequest]
|
pendingRequests: Table[AESGCMNonce, PendingRequest]
|
||||||
|
keyexchangeInProgress: HashSet[NodeId]
|
||||||
|
pendingRequestsByNode: Table[NodeId, seq[seq[byte]]]
|
||||||
codec*: Codec
|
codec*: Codec
|
||||||
rng: ref HmacDrbgContext
|
rng: ref HmacDrbgContext
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ type
|
||||||
message: seq[byte]
|
message: seq[byte]
|
||||||
|
|
||||||
proc sendToA(t: Transport, a: Address, data: seq[byte]) =
|
proc sendToA(t: Transport, a: Address, data: seq[byte]) =
|
||||||
|
trace "Send packet", myport = t.bindAddress.port, address = a
|
||||||
let ta = initTAddress(a.ip, a.port)
|
let ta = initTAddress(a.ip, a.port)
|
||||||
let f = t.transp.sendTo(ta, data)
|
let f = t.transp.sendTo(ta, data)
|
||||||
f.callback = proc(data: pointer) {.gcsafe.} =
|
f.callback = proc(data: pointer) {.gcsafe.} =
|
||||||
|
@ -55,7 +58,7 @@ proc send(t: Transport, n: Node, data: seq[byte]) =
|
||||||
t.sendToA(n.address.get(), data)
|
t.sendToA(n.address.get(), data)
|
||||||
|
|
||||||
proc sendMessage*(t: Transport, toId: NodeId, toAddr: Address, message: seq[byte]) =
|
proc sendMessage*(t: Transport, toId: NodeId, toAddr: Address, message: seq[byte]) =
|
||||||
let (data, _) = encodeMessagePacket(t.rng[], t.codec, toId, toAddr,
|
let (data, _, _) = encodeMessagePacket(t.rng[], t.codec, toId, toAddr,
|
||||||
message)
|
message)
|
||||||
t.sendToA(toAddr, data)
|
t.sendToA(toAddr, data)
|
||||||
|
|
||||||
|
@ -73,11 +76,30 @@ proc registerRequest(t: Transport, n: Node, message: seq[byte],
|
||||||
proc sendMessage*(t: Transport, toNode: Node, message: seq[byte]) =
|
proc sendMessage*(t: Transport, toNode: Node, message: seq[byte]) =
|
||||||
doAssert(toNode.address.isSome())
|
doAssert(toNode.address.isSome())
|
||||||
let address = toNode.address.get()
|
let address = toNode.address.get()
|
||||||
let (data, nonce) = encodeMessagePacket(t.rng[], t.codec,
|
let (data, nonce, haskey) = encodeMessagePacket(t.rng[], t.codec,
|
||||||
toNode.id, address, message)
|
toNode.id, address, message)
|
||||||
|
|
||||||
t.registerRequest(toNode, message, nonce)
|
if haskey:
|
||||||
t.send(toNode, data)
|
trace "Send message: has key", myport = t.bindAddress.port , dstId = toNode
|
||||||
|
t.registerRequest(toNode, message, nonce)
|
||||||
|
t.send(toNode, data)
|
||||||
|
else:
|
||||||
|
# we don't have an encryption key for this target, so we should initiate keyexchange
|
||||||
|
if not (toNode.id in t.keyexchangeInProgress):
|
||||||
|
trace "Send message: send random to trigger Whoareyou", myport = t.bindAddress.port , dstId = toNode
|
||||||
|
t.registerRequest(toNode, message, nonce)
|
||||||
|
t.send(toNode, data)
|
||||||
|
t.keyexchangeInProgress.incl(toNode.id)
|
||||||
|
trace "keyexchangeInProgress added", myport = t.bindAddress.port , dstId = toNode
|
||||||
|
sleepAsync(responseTimeout).addCallback() do(data: pointer):
|
||||||
|
t.keyexchangeInProgress.excl(toNode.id)
|
||||||
|
trace "keyexchangeInProgress removed (timeout)", myport = t.bindAddress.port , dstId = toNode
|
||||||
|
else:
|
||||||
|
# delay sending this message until whoareyou is received and handshake is sent
|
||||||
|
# have to reencode once keys are clear
|
||||||
|
t.pendingRequestsByNode.mgetOrPut(toNode.id, newSeq[seq[byte]]()).add(message)
|
||||||
|
trace "Send message: Node with this id already has ongoing keyexchage, delaying packet",
|
||||||
|
myport = t.bindAddress.port , dstId = toNode, qlen=t.pendingRequestsByNode[toNode.id].len
|
||||||
|
|
||||||
proc sendWhoareyou(t: Transport, toId: NodeId, a: Address,
|
proc sendWhoareyou(t: Transport, toId: NodeId, a: Address,
|
||||||
requestNonce: AESGCMNonce, node: Option[Node]) =
|
requestNonce: AESGCMNonce, node: Option[Node]) =
|
||||||
|
@ -94,13 +116,28 @@ proc sendWhoareyou(t: Transport, toId: NodeId, a: Address,
|
||||||
sleepAsync(handshakeTimeout).addCallback() do(data: pointer):
|
sleepAsync(handshakeTimeout).addCallback() do(data: pointer):
|
||||||
# TODO: should we still provide cancellation in case handshake completes
|
# TODO: should we still provide cancellation in case handshake completes
|
||||||
# correctly?
|
# correctly?
|
||||||
t.codec.handshakes.del(key)
|
if t.codec.hasHandshake(key):
|
||||||
|
debug "Handshake timeout", myport = t.bindAddress.port , dstId = toId, address = a
|
||||||
|
t.codec.handshakes.del(key)
|
||||||
|
|
||||||
trace "Send whoareyou", dstId = toId, address = a
|
trace "Send whoareyou", dstId = toId, address = a
|
||||||
t.sendToA(a, data)
|
t.sendToA(a, data)
|
||||||
else:
|
else:
|
||||||
debug "Node with this id already has ongoing handshake, ignoring packet"
|
# TODO: is this reasonable to drop it? Should we allow a mini-queue here?
|
||||||
|
# Queue should be on sender side, as this is random encoded!
|
||||||
|
debug "Node with this id already has ongoing handshake, queuing packet", myport = t.bindAddress.port , dstId = toId, address = a
|
||||||
|
|
||||||
|
proc sendPending(t:Transport, toNode: Node):
|
||||||
|
Future[void] {.async.} =
|
||||||
|
if t.pendingRequestsByNode.hasKey(toNode.id):
|
||||||
|
trace "Found pending request", myport = t.bindAddress.port, src = toNode, len = t.pendingRequestsByNode[toNode.id].len
|
||||||
|
for message in t.pendingRequestsByNode[toNode.id]:
|
||||||
|
trace "Sending pending packet", myport = t.bindAddress.port, dstId = toNode.id
|
||||||
|
let address = toNode.address.get()
|
||||||
|
let (data, nonce, haskey) = encodeMessagePacket(t.rng[], t.codec, toNode.id, address, message)
|
||||||
|
t.registerRequest(toNode, message, nonce)
|
||||||
|
t.send(toNode, data)
|
||||||
|
t.pendingRequestsByNode.del(toNode.id)
|
||||||
proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
|
proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
|
||||||
let decoded = t.codec.decodePacket(a, packet)
|
let decoded = t.codec.decodePacket(a, packet)
|
||||||
if decoded.isOk:
|
if decoded.isOk:
|
||||||
|
@ -109,17 +146,23 @@ proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
|
||||||
of OrdinaryMessage:
|
of OrdinaryMessage:
|
||||||
if packet.messageOpt.isSome():
|
if packet.messageOpt.isSome():
|
||||||
let message = packet.messageOpt.get()
|
let message = packet.messageOpt.get()
|
||||||
trace "Received message packet", srcId = packet.srcId, address = a,
|
trace "Received message packet", myport = t.bindAddress.port, srcId = packet.srcId, address = a,
|
||||||
kind = message.kind, p = $packet
|
kind = message.kind, p = $packet
|
||||||
t.client.handleMessage(packet.srcId, a, message)
|
t.client.handleMessage(packet.srcId, a, message)
|
||||||
else:
|
else:
|
||||||
trace "Not decryptable message packet received",
|
trace "Not decryptable message packet received", myport = t.bindAddress.port,
|
||||||
srcId = packet.srcId, address = a
|
srcId = packet.srcId, address = a
|
||||||
|
# If we already have a keyexchange in progress, we have a case of simultaneous cross-connect.
|
||||||
|
# We could try to decide here which should go on, but since we are on top of UDP, a more robust
|
||||||
|
# choice is to answer here and resolve conflicts in the next stage (reception of Whoareyou), or
|
||||||
|
# even later (reception of Handshake).
|
||||||
|
if packet.srcId in t.keyexchangeInProgress:
|
||||||
|
trace "cross-connect detected, still sending Whoareyou"
|
||||||
t.sendWhoareyou(packet.srcId, a, packet.requestNonce,
|
t.sendWhoareyou(packet.srcId, a, packet.requestNonce,
|
||||||
t.client.getNode(packet.srcId))
|
t.client.getNode(packet.srcId))
|
||||||
|
|
||||||
of Flag.Whoareyou:
|
of Flag.Whoareyou:
|
||||||
trace "Received whoareyou packet", address = a
|
trace "Received whoareyou packet", myport = t.bindAddress.port, address = a
|
||||||
var pr: PendingRequest
|
var pr: PendingRequest
|
||||||
if t.pendingRequests.take(packet.whoareyou.requestNonce, pr):
|
if t.pendingRequests.take(packet.whoareyou.requestNonce, pr):
|
||||||
let toNode = pr.node
|
let toNode = pr.node
|
||||||
|
@ -136,12 +179,17 @@ proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
|
||||||
toNode.pubkey
|
toNode.pubkey
|
||||||
).expect("Valid handshake packet to encode")
|
).expect("Valid handshake packet to encode")
|
||||||
|
|
||||||
trace "Send handshake message packet", dstId = toNode.id, address
|
trace "Send handshake message packet", myport = t.bindAddress.port, dstId = toNode.id, address
|
||||||
t.send(toNode, data)
|
t.send(toNode, data)
|
||||||
|
# keyexchange ready, we can send queued packets
|
||||||
|
t.keyexchangeInProgress.excl(toNode.id)
|
||||||
|
trace "keyexchangeInProgress removed (finished)", myport = t.bindAddress.port, dstId = toNode.id, address
|
||||||
|
discard t.sendPending(toNode)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
debug "Timed out or unrequested whoareyou packet", address = a
|
debug "Timed out or unrequested whoareyou packet", address = a
|
||||||
of HandshakeMessage:
|
of HandshakeMessage:
|
||||||
trace "Received handshake message packet", srcId = packet.srcIdHs,
|
trace "Received handshake message packet", myport = t.bindAddress.port, srcId = packet.srcIdHs,
|
||||||
address = a, kind = packet.message.kind
|
address = a, kind = packet.message.kind
|
||||||
t.client.handleMessage(packet.srcIdHs, a, packet.message)
|
t.client.handleMessage(packet.srcIdHs, a, packet.message)
|
||||||
# For a handshake message it is possible that we received an newer SPR.
|
# For a handshake message it is possible that we received an newer SPR.
|
||||||
|
@ -157,9 +205,10 @@ proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
|
||||||
# sending the 'whoareyou' message to. In that case, we can set 'seen'
|
# sending the 'whoareyou' message to. In that case, we can set 'seen'
|
||||||
node.seen = true
|
node.seen = true
|
||||||
if t.client.addNode(node):
|
if t.client.addNode(node):
|
||||||
trace "Added new node to routing table after handshake", node
|
trace "Added new node to routing table after handshake", node, tablesize=t.client.nodesDiscovered()
|
||||||
|
discard t.sendPending(node)
|
||||||
else:
|
else:
|
||||||
trace "Packet decoding error", error = decoded.error, address = a
|
trace "Packet decoding error", myport = t.bindAddress.port, error = decoded.error, address = a
|
||||||
|
|
||||||
proc processClient[T](transp: DatagramTransport, raddr: TransportAddress):
|
proc processClient[T](transp: DatagramTransport, raddr: TransportAddress):
|
||||||
Future[void] {.async.} =
|
Future[void] {.async.} =
|
||||||
|
|
|
@ -629,7 +629,7 @@ suite "Discovery v5 Tests":
|
||||||
sendNode = newNode(enrRec).expect("Properly initialized record")
|
sendNode = newNode(enrRec).expect("Properly initialized record")
|
||||||
var codec = Codec(localNode: sendNode, privKey: privKey, sessions: Sessions.init(5))
|
var codec = Codec(localNode: sendNode, privKey: privKey, sessions: Sessions.init(5))
|
||||||
|
|
||||||
let (packet, _) = encodeMessagePacket(rng[], codec,
|
let (packet, _, _) = encodeMessagePacket(rng[], codec,
|
||||||
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
||||||
receiveNode.transport.receive(a, packet)
|
receiveNode.transport.receive(a, packet)
|
||||||
|
|
||||||
|
@ -659,7 +659,7 @@ suite "Discovery v5 Tests":
|
||||||
var codec = Codec(localNode: sendNode, privKey: privKey, sessions: Sessions.init(5))
|
var codec = Codec(localNode: sendNode, privKey: privKey, sessions: Sessions.init(5))
|
||||||
for i in 0 ..< 5:
|
for i in 0 ..< 5:
|
||||||
let a = localAddress(20303 + i)
|
let a = localAddress(20303 + i)
|
||||||
let (packet, _) = encodeMessagePacket(rng[], codec,
|
let (packet, _, _) = encodeMessagePacket(rng[], codec,
|
||||||
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
||||||
receiveNode.transport.receive(a, packet)
|
receiveNode.transport.receive(a, packet)
|
||||||
|
|
||||||
|
@ -691,7 +691,7 @@ suite "Discovery v5 Tests":
|
||||||
|
|
||||||
var firstRequestNonce: AESGCMNonce
|
var firstRequestNonce: AESGCMNonce
|
||||||
for i in 0 ..< 5:
|
for i in 0 ..< 5:
|
||||||
let (packet, requestNonce) = encodeMessagePacket(rng[], codec,
|
let (packet, requestNonce, _) = encodeMessagePacket(rng[], codec,
|
||||||
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
receiveNode.localNode.id, receiveNode.localNode.address.get(), @[])
|
||||||
receiveNode.transport.receive(a, packet)
|
receiveNode.transport.receive(a, packet)
|
||||||
if i == 0:
|
if i == 0:
|
||||||
|
|
|
@ -526,7 +526,7 @@ suite "Discovery v5.1 Additional Encode/Decode":
|
||||||
reqId = RequestId.init(rng[])
|
reqId = RequestId.init(rng[])
|
||||||
message = encodeMessage(m, reqId)
|
message = encodeMessage(m, reqId)
|
||||||
|
|
||||||
let (data, nonce) = encodeMessagePacket(rng[], codecA, nodeB.id,
|
let (data, nonce, _) = encodeMessagePacket(rng[], codecA, nodeB.id,
|
||||||
nodeB.address.get(), message)
|
nodeB.address.get(), message)
|
||||||
|
|
||||||
let decoded = codecB.decodePacket(nodeA.address.get(), data)
|
let decoded = codecB.decodePacket(nodeA.address.get(), data)
|
||||||
|
@ -642,7 +642,7 @@ suite "Discovery v5.1 Additional Encode/Decode":
|
||||||
codecB.sessions.store(nodeA.id, nodeA.address.get(), secrets.initiatorKey,
|
codecB.sessions.store(nodeA.id, nodeA.address.get(), secrets.initiatorKey,
|
||||||
secrets.recipientKey)
|
secrets.recipientKey)
|
||||||
|
|
||||||
let (data, nonce) = encodeMessagePacket(rng[], codecA, nodeB.id,
|
let (data, nonce, _) = encodeMessagePacket(rng[], codecA, nodeB.id,
|
||||||
nodeB.address.get(), message)
|
nodeB.address.get(), message)
|
||||||
|
|
||||||
let decoded = codecB.decodePacket(nodeA.address.get(), data)
|
let decoded = codecB.decodePacket(nodeA.address.get(), data)
|
||||||
|
|
Loading…
Reference in New Issue