diff --git a/eth/p2p/discoveryv5/encoding.nim b/eth/p2p/discoveryv5/encoding.nim index 18fc300..1ee813a 100644 --- a/eth/p2p/discoveryv5/encoding.nim +++ b/eth/p2p/discoveryv5/encoding.nim @@ -19,10 +19,10 @@ const type PacketTag* = array[tagSize, byte] - AuthResponse = object - version: int - signature: array[64, byte] - record: Option[enr.Record] + AuthResponse* = object + version*: int + signature*: array[64, byte] + record*: Option[enr.Record] Codec* = object localNode*: Node diff --git a/eth/p2p/discoveryv5/protocol.nim b/eth/p2p/discoveryv5/protocol.nim index 6b2d744..4ccd009 100644 --- a/eth/p2p/discoveryv5/protocol.nim +++ b/eth/p2p/discoveryv5/protocol.nim @@ -230,7 +230,7 @@ proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] = for i in 0 .. a.high: result[i] = a[i] xor b[i] -proc whoareyouMagic(toNode: NodeId): array[magicSize, byte] = +proc whoareyouMagic*(toNode: NodeId): array[magicSize, byte] = const prefix = "WHOAREYOU" var data: array[prefix.len + sizeof(toNode), byte] data[0 .. sizeof(toNode) - 1] = toNode.toByteArrayBE() diff --git a/tests/fuzzing/discoveryv5/fuzz_receive.nim b/tests/fuzzing/discoveryv5/fuzz_receive.nim index 05b2a4f..b99a3c1 100644 --- a/tests/fuzzing/discoveryv5/fuzz_receive.nim +++ b/tests/fuzzing/discoveryv5/fuzz_receive.nim @@ -8,7 +8,8 @@ var targetNode: protocol.Protocol init: let rng = newRng() - privKey = PrivateKey.random(rng[]) + privKey = PrivateKey.fromHex( + "5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[] ip = some(ValidIpAddress.init("127.0.0.1")) port = Port(20301) dbb = DiscoveryDB.init(newMemoryDB()) diff --git a/tests/fuzzing/discoveryv5/generate_auth_response.nim b/tests/fuzzing/discoveryv5/generate_auth_response.nim new file mode 100644 index 0000000..d255b79 --- /dev/null +++ b/tests/fuzzing/discoveryv5/generate_auth_response.nim @@ -0,0 +1,32 @@ +import + std/[os, strutils], + stew/shims/net, + eth/[rlp, keys], eth/p2p/discoveryv5/[encoding, enr, types], + ../fuzzing_helpers + +template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0] +const inputsDir = sourceDir / "corpus" & DirSep + +proc generate() = + let + rng = keys.newRng() + privKey = PrivateKey.random(rng[]) + pubKey = PrivateKey.random(rng[]).toPublicKey() + var idNonce: IdNonce + brHmacDrbgGenerate(rng[], idNonce) + + let + ephKeys = KeyPair.random(rng[]) + signature = signIDNonce(privKey, idNonce, ephKeys.pubkey.toRaw) + record = enr.Record.init(1, privKey, none(ValidIpAddress), Port(9000), + Port(9000))[] + authResponse = + AuthResponse(version: 5, signature: signature.toRaw, record: some(record)) + authResponseNoRecord = + AuthResponse(version: 5, signature: signature.toRaw, record: none(enr.Record)) + + rlp.encode(authResponse).toFile(inputsDir & "auth-response") + rlp.encode(authResponseNoRecord).toFile(inputsDir & "auth-response-no-enr") + +discard existsOrCreateDir(inputsDir) +generate() diff --git a/tests/fuzzing/discoveryv5/generate_packet.nim b/tests/fuzzing/discoveryv5/generate_packet.nim new file mode 100644 index 0000000..9025a8a --- /dev/null +++ b/tests/fuzzing/discoveryv5/generate_packet.nim @@ -0,0 +1,51 @@ +import + std/[os, strutils], + stew/shims/net, + eth/[keys, rlp, trie/db], + eth/p2p/discoveryv5/[protocol, discovery_db, enr, node, types, encoding], + ../fuzzing_helpers + +template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0] +const inputsDir = sourceDir / "corpus" & DirSep + +proc generate() = + let + rng = keys.newRng() + privKey = PrivateKey.random(rng[]) + ip = some(ValidIpAddress.init("127.0.0.1")) + port = Port(20301) + dbb = DiscoveryDB.init(newMemoryDB()) + d = newProtocol(privKey, dbb, ip, port, port, rng = rng) + + # Same as the on in the fuzz test to have at least one working packet for + # the whoareyou-packet. + toPrivKey = PrivateKey.fromHex( + "5d2908f3f09ea1ff2e327c3f623159639b00af406e9009de5fd4b910fc34049d")[] + toRecord = enr.Record.init(1, toPrivKey, + some(ValidIpAddress.init("127.0.0.1")), Port(9000), Port(9000))[] + toNode = newNode(toRecord)[] + + block: # random packet + # No handshake done obviously so a new packet will be a random packet. + let + reqId = RequestId.init(d.rng[]) + message = encodeMessage(PingMessage(enrSeq: d.localNode.record.seqNum), reqId) + (data, _) = encodePacket(d.rng[], d.codec, toNode.id, toNode.address.get(), + message, challenge = nil) + + data.toFile(inputsDir & "random-packet") + + block: # whoareyou packet + var authTag: AuthTag + var idNonce: IdNonce + brHmacDrbgGenerate(d.rng[], authTag) + brHmacDrbgGenerate(d.rng[], idNonce) + + let challenge = Whoareyou(authTag: authTag, idNonce: idNonce, recordSeq: 0) + var data = @(whoareyouMagic(toNode.id)) + data.add(rlp.encode(challenge[])) + + data.toFile(inputsDir & "whoareyou-packet") + +discard existsOrCreateDir(inputsDir) +generate() diff --git a/tests/fuzzing/enr/generate.nim b/tests/fuzzing/enr/generate.nim index 6a03287..f39b4d5 100644 --- a/tests/fuzzing/enr/generate.nim +++ b/tests/fuzzing/enr/generate.nim @@ -1,17 +1,12 @@ import - streams, os, strutils, options, + std/[os, strutils, options], stew/shims/net, - eth/keys, eth/p2p/discoveryv5/enr + eth/keys, eth/p2p/discoveryv5/enr, + ../fuzzing_helpers template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0] const inputsDir = sourceDir / "corpus" -proc toFile(data: seq[byte], fn: string) = - var s = newFileStream(fn, fmWrite) - for x in data: - s.write(x) - s.close() - proc generate() = let rng = newRng() diff --git a/tests/fuzzing/fuzzing_helpers.nim b/tests/fuzzing/fuzzing_helpers.nim new file mode 100644 index 0000000..1ba2700 --- /dev/null +++ b/tests/fuzzing/fuzzing_helpers.nim @@ -0,0 +1,8 @@ +import + std/streams + +proc toFile*(data: seq[byte], fn: string) = + var s = newFileStream(fn, fmWrite) + for x in data: + s.write(x) + s.close() diff --git a/tests/fuzzing/rlp/rlp_decode.nim b/tests/fuzzing/rlp/rlp_decode.nim new file mode 100644 index 0000000..5b7d39e --- /dev/null +++ b/tests/fuzzing/rlp/rlp_decode.nim @@ -0,0 +1,32 @@ +import + testutils/fuzzing, chronicles, + eth/rlp + +type + TestEnum = enum + one = 1 + two = 2 + TestObject* = object + test1: uint32 + test2: string + +template testDecode(payload: openarray, T: type) = + try: + discard rlp.decode(payload, T) + except RlpError as e: + debug "Decode failed", err = e.msg + +test: + testDecode(payload, string) + testDecode(payload, uint) + testDecode(payload, uint8) + testDecode(payload, uint16) + testDecode(payload, uint32) + testDecode(payload, uint64) + testDecode(payload, int) + testDecode(payload, bool) + testDecode(payload, float64) + testDecode(payload, seq[byte]) + testDecode(payload, (string, int32)) + testDecode(payload, TestEnum) + testDecode(payload, TestObject)