Improve error logging messages

As we don't need to differentate between errors with an enum
anymore thanks to refactor.
This commit is contained in:
kdeme 2020-10-08 12:08:36 +02:00 committed by zah
parent c28cba3589
commit 9b971a0e14
2 changed files with 31 additions and 52 deletions

View File

@ -9,7 +9,6 @@ export keys
const const
version: uint16 = 1 version: uint16 = 1
idNoncePrefix = "discovery-id-nonce"
idSignatureText = "discovery v5 identity proof" idSignatureText = "discovery v5 identity proof"
keyAgreementPrefix = "discovery v5 key agreement" keyAgreementPrefix = "discovery v5 key agreement"
protocolIdStr = "discv5" protocolIdStr = "discv5"
@ -70,18 +69,7 @@ type
handshakes*: Table[HandShakeKey, Challenge] handshakes*: Table[HandShakeKey, Challenge]
sessions*: Sessions sessions*: Sessions
DecodeError* = enum DecodeResult*[T] = Result[T, cstring]
HandshakeError = "discv5: handshake failed"
PacketError = "discv5: invalid packet"
DecryptError = "discv5: decryption failed"
UnsupportedMessage = "discv5: unsupported message"
DecodeResult*[T] = Result[T, DecodeError]
EncodeResult*[T] = Result[T, cstring]
proc mapErrTo[T, E](r: Result[T, E], v: static DecodeError):
DecodeResult[T] =
r.mapErr(proc (e: E): DecodeError = v)
proc idNonceHash*(challengeData, ephkey: openarray[byte], nodeId: NodeId): proc idNonceHash*(challengeData, ephkey: openarray[byte], nodeId: NodeId):
MDigest[256] = MDigest[256] =
@ -308,13 +296,13 @@ proc decodeHeader*(id: NodeId, iv, maskedHeader: openarray[byte]):
# Check fields of the static-header # Check fields of the static-header
if staticHeader.toOpenArray(0, protocolId.len - 1) != protocolId: if staticHeader.toOpenArray(0, protocolId.len - 1) != protocolId:
return err(PacketError) return err("Invalid protocol id")
if uint16.fromBytesBE(staticHeader.toOpenArray(6, 7)) != version: if uint16.fromBytesBE(staticHeader.toOpenArray(6, 7)) != version:
return err(PacketError) return err("Invalid protocol version")
if staticHeader[8] < Flag.low.byte or staticHeader[8] > Flag.high.byte: if staticHeader[8] < Flag.low.byte or staticHeader[8] > Flag.high.byte:
return err(PacketError) return err("Invalid packet flag")
let flag = cast[Flag](staticHeader[8]) let flag = cast[Flag](staticHeader[8])
var nonce: AESGCMNonce var nonce: AESGCMNonce
@ -326,7 +314,7 @@ proc decodeHeader*(id: NodeId, iv, maskedHeader: openarray[byte]):
# Input should have minimum size of staticHeader + provided authdata size # Input should have minimum size of staticHeader + provided authdata size
# Can be larger as there can come a message after. # Can be larger as there can come a message after.
if maskedHeader.len < staticHeaderSize + int(authdataSize): if maskedHeader.len < staticHeaderSize + int(authdataSize):
return err(PacketError) return err("Authdata is smaller than authdata-size indicates")
var authdata = newSeq[byte](int(authdataSize)) var authdata = newSeq[byte](int(authdataSize))
ectx.decrypt(maskedHeader.toOpenArray(staticHeaderSize, ectx.decrypt(maskedHeader.toOpenArray(staticHeaderSize,
@ -339,10 +327,10 @@ proc decodeHeader*(id: NodeId, iv, maskedHeader: openarray[byte]):
proc decodeMessage*(body: openarray[byte]): DecodeResult[Message] = proc decodeMessage*(body: openarray[byte]): DecodeResult[Message] =
## Decodes to the specific `Message` type. ## Decodes to the specific `Message` type.
if body.len < 1: if body.len < 1:
return err(PacketError) return err("No message data")
if body[0] < MessageKind.low.byte or body[0] > MessageKind.high.byte: if body[0] < MessageKind.low.byte or body[0] > MessageKind.high.byte:
return err(PacketError) return err("Invalid message type")
# This cast is covered by the above check (else we could get enum with invalid # This cast is covered by the above check (else we could get enum with invalid
# data!). However, can't we do this in a cleaner way? # data!). However, can't we do this in a cleaner way?
@ -353,7 +341,7 @@ proc decodeMessage*(body: openarray[byte]): DecodeResult[Message] =
try: try:
message.reqId = rlp.read(RequestId) message.reqId = rlp.read(RequestId)
except RlpError, ValueError: except RlpError, ValueError:
return err(PacketError) return err("Invalid request-id")
proc decode[T](rlp: var Rlp, v: var T) proc decode[T](rlp: var Rlp, v: var T)
{.inline, nimcall, raises:[RlpError, ValueError, Defect].} = {.inline, nimcall, raises:[RlpError, ValueError, Defect].} =
@ -362,7 +350,7 @@ proc decodeMessage*(body: openarray[byte]): DecodeResult[Message] =
try: try:
case kind case kind
of unused: return err(PacketError) of unused: return err("Invalid message type")
of ping: rlp.decode(message.ping) of ping: rlp.decode(message.ping)
of pong: rlp.decode(message.pong) of pong: rlp.decode(message.pong)
of findNode: rlp.decode(message.findNode) of findNode: rlp.decode(message.findNode)
@ -376,17 +364,17 @@ proc decodeMessage*(body: openarray[byte]): DecodeResult[Message] =
# semantics of this message are not final". # semantics of this message are not final".
discard discard
except RlpError, ValueError: except RlpError, ValueError:
return err(PacketError) return err("Invalid message encoding")
ok(message) ok(message)
else: else:
err(PacketError) err("Invalid message encoding: no rlp list")
proc decodeMessagePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce, proc decodeMessagePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
iv, header, ct: openArray[byte]): DecodeResult[Packet] = iv, header, ct: openArray[byte]): DecodeResult[Packet] =
# We now know the exact size that the header should be # We now know the exact size that the header should be
if header.len != staticHeaderSize + sizeof(NodeId): if header.len != staticHeaderSize + sizeof(NodeId):
return err(PacketError) return err("Invalid header length for ordinary message packet")
let srcId = NodeId.fromBytesBE(header.toOpenArray(staticHeaderSize, let srcId = NodeId.fromBytesBE(header.toOpenArray(staticHeaderSize,
header.high)) header.high))
@ -419,7 +407,7 @@ proc decodeWhoareyouPacket(c: var Codec, nonce: AESGCMNonce,
let authdata = header[staticHeaderSize..header.high()] let authdata = header[staticHeaderSize..header.high()]
# We now know the exact size that the authdata should be # We now know the exact size that the authdata should be
if authdata.len != idNonceSize + sizeof(uint64): if authdata.len != idNonceSize + sizeof(uint64):
return err(PacketError) return err("Invalid header length for whoareyou packet")
var idNonce: IdNonce var idNonce: IdNonce
copyMem(addr idNonce[0], unsafeAddr authdata[0], idNonceSize) copyMem(addr idNonce[0], unsafeAddr authdata[0], idNonceSize)
@ -434,7 +422,7 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
iv, header, ct: openArray[byte]): DecodeResult[Packet] = iv, header, ct: openArray[byte]): DecodeResult[Packet] =
# Checking if there is enough data to decode authdata-head # Checking if there is enough data to decode authdata-head
if header.len <= staticHeaderSize + authdataHeadSize: if header.len <= staticHeaderSize + authdataHeadSize:
return err(PacketError) return err("Invalid header for handshake message packet: no authdata-head")
let let
authdata = header[staticHeaderSize..header.high()] authdata = header[staticHeaderSize..header.high()]
@ -444,13 +432,12 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
# If smaller, as it can be equal and bigger (in case it holds an enr) # If smaller, as it can be equal and bigger (in case it holds an enr)
if header.len < staticHeaderSize + authdataHeadSize + int(sigSize) + int(ephKeySize): if header.len < staticHeaderSize + authdataHeadSize + int(sigSize) + int(ephKeySize):
return err(PacketError) return err("Invalid header for handshake message packet")
let key = HandShakeKey(nodeId: srcId, address: $fromAddr) let key = HandShakeKey(nodeId: srcId, address: $fromAddr)
var challenge: Challenge var challenge: Challenge
if not c.handshakes.pop(key, challenge): if not c.handshakes.pop(key, challenge):
debug "Decoding failed (no previous stored handshake challenge)" return err("No challenge found: timed out or unsolicited packet")
return err(HandshakeError)
# This should be the compressed public key. But as we use the provided # This should be the compressed public key. But as we use the provided
# ephKeySize, it should also work with full sized key. However, the idNonce # ephKeySize, it should also work with full sized key. However, the idNonce
@ -458,7 +445,7 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
let let
ephKeyPos = authdataHeadSize + int(sigSize) ephKeyPos = authdataHeadSize + int(sigSize)
ephKeyRaw = authdata[ephKeyPos..<ephKeyPos + int(ephKeySize)] ephKeyRaw = authdata[ephKeyPos..<ephKeyPos + int(ephKeySize)]
ephKey = ? PublicKey.fromRaw(ephKeyRaw).mapErrTo(HandshakeError) ephKey = ? PublicKey.fromRaw(ephKeyRaw)
var record: Option[enr.Record] var record: Option[enr.Record]
let recordPos = ephKeyPos + int(ephKeySize) let recordPos = ephKeyPos + int(ephKeySize)
@ -469,7 +456,7 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
record = some(rlp.decode(authdata.toOpenArray(recordPos, authdata.high), record = some(rlp.decode(authdata.toOpenArray(recordPos, authdata.high),
enr.Record)) enr.Record))
except RlpError, ValueError: except RlpError, ValueError:
return err(HandshakeError) return err("Invalid encoded ENR")
var pubKey: PublicKey var pubKey: PublicKey
var newNode: Option[Node] var newNode: Option[Node]
@ -477,28 +464,28 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
# need the pubkey and the nodeid # need the pubkey and the nodeid
if record.isSome(): if record.isSome():
# Node returned might not have an address or not a valid address. # Node returned might not have an address or not a valid address.
let node = ? newNode(record.get()).mapErrTo(HandshakeError) let node = ? newNode(record.get())
if node.id != srcId: if node.id != srcId:
return err(HandshakeError) return err("Invalid node id: does not match node id of ENR")
pubKey = node.pubKey pubKey = node.pubKey
newNode = some(node) newNode = some(node)
else: else:
# TODO: Hmm, should we still verify node id of the ENR of this node?
if challenge.pubkey.isSome(): if challenge.pubkey.isSome():
pubKey = challenge.pubkey.get() pubKey = challenge.pubkey.get()
else: else:
# We should have received a Record in this case. # We should have received a Record in this case.
return err(HandshakeError) return err("Missing ENR in handshake packet")
# Verify the id-nonce-sig # Verify the id-nonce-sig
let sig = ? SignatureNR.fromRaw( let sig = ? SignatureNR.fromRaw(
authdata.toOpenArray(authdataHeadSize, authdata.toOpenArray(authdataHeadSize, authdataHeadSize + int(sigSize) - 1))
authdataHeadSize + int(sigSize) - 1)).mapErrTo(HandshakeError)
let h = idNonceHash(challenge.whoareyouData.challengeData, ephKeyRaw, let h = idNonceHash(challenge.whoareyouData.challengeData, ephKeyRaw,
c.localNode.id) c.localNode.id)
if not verify(sig, SkMessage(h.data), pubkey): if not verify(sig, SkMessage(h.data), pubkey):
return err(HandshakeError) return err("Invalid id-signature")
# Do the key derivation step only after id-nonce-sig is verified! # Do the key derivation step only after id-nonce-sig is verified!
var secrets = deriveKeys(srcId, c.localNode.id, c.privKey, var secrets = deriveKeys(srcId, c.localNode.id, c.privKey,
@ -510,8 +497,11 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
if pt.isNone(): if pt.isNone():
c.sessions.del(srcId, fromAddr) c.sessions.del(srcId, fromAddr)
# Differently from an ordinary message, this is seen as an error as the # Differently from an ordinary message, this is seen as an error as the
# secrets just got negotiated in the handshake. # secrets just got negotiated in the handshake and thus decryption should
return err(DecryptError) # always work. We do not send a new Whoareyou on these as it probably means
# there is a compatiblity issue and we might loop forever in failed
# handshakes with this peer.
return err("Decryption of message failed in handshake packet")
let message = ? decodeMessage(pt.get()) let message = ? decodeMessage(pt.get())
@ -528,7 +518,7 @@ proc decodePacket*(c: var Codec, fromAddr: Address, input: openArray[byte]):
## WHOAREYOU packet. In case of the latter a `newNode` might be provided. ## WHOAREYOU packet. In case of the latter a `newNode` might be provided.
# Smallest packet is Whoareyou packet so that is the minimum size # Smallest packet is Whoareyou packet so that is the minimum size
if input.len() < whoareyouSize: if input.len() < whoareyouSize:
return err(PacketError) return err("Packet size too short")
# TODO: Just pass in the full input? Makes more sense perhaps.. # TODO: Just pass in the full input? Makes more sense perhaps..
let (staticHeader, header) = ? decodeHeader(c.localNode.id, let (staticHeader, header) = ? decodeHeader(c.localNode.id,

View File

@ -384,19 +384,8 @@ proc receive*(d: Protocol, a: Address, packet: openArray[byte]) {.gcsafe,
debug "Adding new node to routing table", node = $node, debug "Adding new node to routing table", node = $node,
localNode = $d.localNode localNode = $d.localNode
discard d.addNode(node) discard d.addNode(node)
else:
elif decoded.error == DecodeError.UnsupportedMessage:
# TODO: Probably should still complete handshake in these cases.
trace "Packet contained unsupported message"
elif decoded.error == DecodeError.PacketError:
debug "Packet decoding error", error = decoded.error debug "Packet decoding error", error = decoded.error
elif decoded.error == DecodeError.HandshakeError:
debug "Packet handshake error", error = decoded.error
elif decoded.error == DecodeError.DecryptError:
# This is a specific decryption error on a handshake. We do not send a
# new Whoareyou on these as it probably means there is a compatiblity
# issue and we might loop forever in failed handshakes with this peer.
debug "Packet decrypting error", error = decoded.error
# TODO: Not sure why but need to pop the raises here as it is apparently not # TODO: Not sure why but need to pop the raises here as it is apparently not
# enough to put it in the raises pragma of `processClient` and other async procs. # enough to put it in the raises pragma of `processClient` and other async procs.