chore: remove nim-eth/keys in favour of over to libp2p/crypto

Closes: #2.

Libp2p supports multiple cryptographic curves, however we have currently only implented support for secp256k1.

This needs to be run with the compiler flag `libp2p_pki_schemes` set to `secp256k1`. If running the tests, this can be run like so: `nimble test —libp2p_pki_schemes=secp256k1` to put secp as the first supported crypto scheme.
This commit is contained in:
Eric Mastro 2022-03-25 15:00:57 +11:00 committed by Michael Bradley
parent 74b73bfda0
commit 4c65f4bd94
13 changed files with 445 additions and 356 deletions

1
config.nims Normal file
View File

@ -0,0 +1 @@
switch("define", "libp2p_pki_schemes=secp256k1")

View File

@ -1,4 +1,4 @@
import
./discv5/[spr, encoding, messages, messages_encoding, node, nodes_verification, protocol, routing_table, sessions, transport]
./discv5/[crypto, spr, encoding, messages, messages_encoding, node, nodes_verification, protocol, routing_table, sessions, transport]
export spr, encoding, messages, messages_encoding, node, nodes_verification, protocol, routing_table, sessions, transport
export crypto, spr, encoding, messages, messages_encoding, node, nodes_verification, protocol, routing_table, sessions, transport

View File

@ -0,0 +1,4 @@
import
../private/eth/p2p/discoveryv5/crypto
export crypto

View File

@ -0,0 +1,30 @@
import
std/sugar,
libp2p/crypto/[crypto, secp]
from secp256k1 import ecdhRaw, SkEcdhRawSecret, toRaw
proc fromHex*(T: type PrivateKey, data: string): Result[PrivateKey, cstring] =
let skKey = ? SkPrivateKey.init(data).mapErr(e =>
("Failed to init private key from hex string: " & $e).cstring)
ok PrivateKey.init(skKey)
proc fromHex*(T: type PublicKey, data: string): Result[PublicKey, cstring] =
let skKey = ? SkPublicKey.init(data).mapErr(e =>
("Failed to init public key from hex string: " & $e).cstring)
ok PublicKey.init(skKey)
func ecdhRaw*(seckey: SkPrivateKey, pubkey: SkPublicKey): SkEcdhRawSecret {.borrow.}
proc ecdhRaw*(
priv: PrivateKey,
pub: PublicKey): Result[SkEcdhRawSecret, cstring] =
# TODO: Do we need to support non-secp256k1 schemes?
if priv.scheme != Secp256k1 or pub.scheme != Secp256k1:
return err "Must use secp256k1 scheme".cstring
ok ecdhRaw(priv.skkey, pub.skkey)
proc toRaw*(pubkey: PublicKey): seq[byte] =
secp256k1.SkPublicKey(pubkey.skkey).toRaw()[1..^1]

View File

@ -14,15 +14,24 @@
{.push raises: [Defect].}
import
std/[tables, options, hashes, net],
nimcrypto, stint, chronicles, bearssl, stew/[results, byteutils], metrics,
eth/[rlp, keys],
std/[hashes, net, options, sugar, tables],
eth/rlp,
bearssl,
chronicles,
stew/[results, byteutils],
stint,
libp2p/crypto/crypto as libp2p_crypto,
libp2p/crypto/secp,
libp2p/signed_envelope,
"."/[messages, messages_encoding, node, spr, hkdf, sessions]
metrics,
nimcrypto,
"."/[messages, messages_encoding, node, spr, hkdf, sessions],
"."/crypto
from nimcrypto/utils import toHex
from stew/objects import checkedEnumAssign
export keys
export crypto
declareCounter discovery_session_lru_cache_hits, "Session LRU cache hits"
declareCounter discovery_session_lru_cache_misses, "Session LRU cache misses"
@ -57,7 +66,7 @@ type
Challenge* = object
whoareyouData*: WhoareyouData
pubkey*: Option[keys.PublicKey]
pubkey*: Option[PublicKey]
StaticHeader* = object
flag: Flag
@ -93,12 +102,16 @@ type
Codec* = object
localNode*: Node
privKey*: keys.PrivateKey
privKey*: PrivateKey
handshakes*: Table[HandshakeKey, Challenge]
sessions*: Sessions
EncodeResult*[T] = Result[T, cstring]
DecodeResult*[T] = Result[T, cstring]
CryptoResult*[T] = Result[T, CryptoError]
func `==`*(a, b: HandshakeKey): bool =
(a.nodeId == b.nodeId) and (a.address == b.address)
@ -117,18 +130,23 @@ proc idHash(challengeData, ephkey: openArray[byte], nodeId: NodeId):
result = ctx.finish()
ctx.clear()
proc createIdSignature*(privKey: keys.PrivateKey, challengeData,
ephKey: openArray[byte], nodeId: NodeId): SignatureNR =
signNR(privKey, SkMessage(idHash(challengeData, ephKey, nodeId).data))
proc createIdSignature*(privKey: PrivateKey, challengeData,
ephKey: openArray[byte], nodeId: NodeId): EncodeResult[Signature] =
proc verifyIdSignature*(sig: SignatureNR, challengeData, ephKey: openArray[byte],
nodeId: NodeId, pubkey: keys.PublicKey): bool =
let h = idHash(challengeData, ephKey, nodeId)
verify(sig, SkMessage(h.data), pubkey)
sign(privKey, h.data).mapErr(e =>
("Failed to sign challegene data: " & $e).cstring)
proc deriveKeys*(n1, n2: NodeId, priv: keys.PrivateKey, pub: keys.PublicKey,
challengeData: openArray[byte]): HandshakeSecrets =
let eph = ecdhRawFull(priv, pub)
proc verifyIdSignature*(sig: Signature, challengeData, ephKey: openArray[byte],
nodeId: NodeId, pubkey: PublicKey): bool =
let h = idHash(challengeData, ephKey, nodeId)
verify(sig, h.data, pubkey)
proc deriveKeys*(n1, n2: NodeId, priv: PrivateKey, pub: PublicKey,
challengeData: openArray[byte]): EncodeResult[HandshakeSecrets] =
let eph = ? ecdhRaw(priv, pub)
var info = newSeqOfCap[byte](keyAgreementPrefix.len + 32 * 2)
for i, c in keyAgreementPrefix: info.add(byte(c))
@ -141,7 +159,7 @@ proc deriveKeys*(n1, n2: NodeId, priv: keys.PrivateKey, pub: keys.PublicKey,
hkdf(sha256, eph.data, challengeData, info,
toOpenArray(res, 0, sizeof(secrets) - 1))
secrets
ok secrets
proc encryptGCM*(key: AesKey, nonce, pt, authData: openArray[byte]): seq[byte] =
var ectx: GCM[aes128]
@ -236,7 +254,7 @@ proc encodeMessagePacket*(rng: var BrHmacDrbgContext, c: var Codec,
proc encodeWhoareyouPacket*(rng: var BrHmacDrbgContext, c: var Codec,
toId: NodeId, toAddr: Address, requestNonce: AESGCMNonce, recordSeq: uint64,
pubkey: Option[keys.PublicKey]): seq[byte] =
pubkey: Option[PublicKey]): seq[byte] =
var idNonce: IdNonce
brHmacDrbgGenerate(rng, idNonce)
@ -278,7 +296,7 @@ proc encodeWhoareyouPacket*(rng: var BrHmacDrbgContext, c: var Codec,
proc encodeHandshakePacket*(rng: var BrHmacDrbgContext, c: var Codec,
toId: NodeId, toAddr: Address, message: openArray[byte],
whoareyouData: WhoareyouData, pubkey: keys.PublicKey): seq[byte] =
whoareyouData: WhoareyouData, pubkey: PublicKey): EncodeResult[seq[byte]] =
var header: seq[byte]
var nonce: AESGCMNonce
brHmacDrbgGenerate(rng, nonce)
@ -289,37 +307,52 @@ proc encodeHandshakePacket*(rng: var BrHmacDrbgContext, c: var Codec,
var authdataHead: seq[byte]
authdataHead.add(c.localNode.id.toByteArrayBE())
authdataHead.add(64'u8) # sig-size: 64
authdataHead.add(33'u8) # eph-key-size: 33
let ephKeys = ? KeyPair.random(rng)
.mapErr((e: CryptoError) =>
("Failed to create random key pair: " & $e).cstring)
# TODO: Do we need to support non-secp256k1 schemes?
if ephKeys.pubkey.scheme != Secp256k1:
return err "Crypto scheme must be secp256k1".cstring
let
pubKeyRaw = ? ephKeys.pubkey.getBytes().mapErr((e: CryptoError) =>
("Failed to serialize public key: " & $e).cstring)
signature = ? createIdSignature(
c.privKey,
whoareyouData.challengeData,
pubKeyRaw,
toId)
let sigBytes = signature.getBytes()
authdataHead.add(sigBytes.len.uint8) # DER variable, less than 72 bytes
authdataHead.add(pubKeyRaw.len.uint8) # eph-key-size: 33
authdata.add(authdataHead)
authdata.add(sigBytes)
let ephKeys = keys.KeyPair.random(rng)
let signature = createIdSignature(c.privKey, whoareyouData.challengeData,
ephKeys.pubkey.toRawCompressed(), toId)
authdata.add(signature.toRaw())
# compressed pub key format (33 bytes)
authdata.add(ephKeys.pubkey.toRawCompressed())
authdata.add(pubKeyRaw)
# Add SPR of sequence number is newer
if whoareyouData.recordSeq < c.localNode.record.seqNum:
let encoded = c.localNode.record.encode
if encoded.isOk:
trace "Encoded local node's SignedPeerRecord", bytes = encoded.get
authdata.add(encoded.get)
else:
error "Failed to encode local node's SignedPeerRecord", error = encoded.error
authdata.add(@[])
let encoded = ? c.localNode.record.encode.mapErr((e: CryptoError) =>
("Failed to encode local node's SignedPeerRecord: " &
$e).cstring)
authdata.add(encoded)
let secrets = deriveKeys(c.localNode.id, toId, ephKeys.seckey, pubkey,
whoareyouData.challengeData)
let secrets = ? deriveKeys(
c.localNode.id,
toId,
ephKeys.seckey,
pubkey,
whoareyouData.challengeData)
# Header
let staticHeader = encodeStaticHeader(Flag.HandshakeMessage, nonce,
authdata.len())
header.add(staticHeader)
trace "Handshake packet's authdata", authdata
header.add(authdata)
c.sessions.store(toId, toAddr, secrets.recipientKey, secrets.initiatorKey)
@ -333,7 +366,7 @@ proc encodeHandshakePacket*(rng: var BrHmacDrbgContext, c: var Codec,
packet.add(maskedHeader)
packet.add(messageEncrypted)
return packet
return ok packet
proc decodeHeader*(id: NodeId, iv, maskedHeader: openArray[byte]):
DecodeResult[(StaticHeader, seq[byte])] =
@ -437,6 +470,7 @@ proc decodeWhoareyouPacket(c: var Codec, nonce: AESGCMNonce,
proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
iv, header, ct: openArray[byte]): DecodeResult[Packet] =
# Checking if there is enough data to decode authdata-head
if header.len <= staticHeaderSize + authdataHeadSize:
return err("Invalid header for handshake message packet: no authdata-head")
@ -469,24 +503,26 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
let
ephKeyPos = authdataHeadSize + int(sigSize)
ephKeyRaw = authdata[ephKeyPos..<ephKeyPos + int(ephKeySize)]
ephKey = ? keys.PublicKey.fromRaw(ephKeyRaw)
ephKey = ? PublicKey.init(ephKeyRaw).mapErr(e =>
("Failed to deserialize PublicKey: " & $e).cstring)
var record: Option[SignedPeerRecord]
let recordPos = ephKeyPos + int(ephKeySize)
if authdata.len() > recordPos:
# There is possibly an SPR still
try:
trace "Decoding handshake packet's authdata", authdata, recordPos, decodeBytes = authdata.toOpenArray(recordPos, authdata.high)
# Signature check of record happens in decode.
let
prBytes = @(authdata.toOpenArray(recordPos, authdata.high))
decoded = SignedPeerRecord.decode(prBytes)
.expect("Should be valid bytes for SignedPeerRecord")
decoded = ? SignedPeerRecord.decode(prBytes).mapErr(
(e: EnvelopeError) =>
("Invalid bytes for SignedPeerRecord: " & $e).cstring
)
record = some(decoded)
except RlpError, ValueError:
return err("Invalid encoded SPR")
var pubkey: keys.PublicKey
var pubkey: PublicKey
var newNode: Option[Node]
# TODO: Shall we return Node or SignedPeerRecord? SignedPeerRecord makes
# more sense, but we do need the pubkey and the nodeid
@ -509,16 +545,27 @@ proc decodeHandshakePacket(c: var Codec, fromAddr: Address, nonce: AESGCMNonce,
return err("Missing SPR in handshake packet")
# Verify the id-signature
let sig = ? SignatureNR.fromRaw(
authdata.toOpenArray(authdataHeadSize, authdataHeadSize + int(sigSize) - 1))
let
sigBytes = @(authdata.toOpenArray(
authdataHeadSize,
authdataHeadSize + int(sigSize) - 1
))
sig = ? Signature.init(sigBytes).mapErr((e: CryptoError) =>
("Failed to deserialize signature from bytes: " & $e).cstring)
if not verifyIdSignature(sig, challenge.whoareyouData.challengeData,
ephKeyRaw, c.localNode.id, pubkey):
return err("Invalid id-signature")
# Do the key derivation step only after id-signature is verified as this is
# costly.
var secrets = deriveKeys(srcId, c.localNode.id, c.privKey,
ephKey, challenge.whoareyouData.challengeData)
var secrets = ? deriveKeys(
srcId,
c.localNode.id,
c.privKey,
ephKey,
challenge.whoareyouData.challengeData)
swap(secrets.recipientKey, secrets.initiatorKey)

View File

@ -9,8 +9,14 @@
import
std/hashes,
nimcrypto, stint, chronos, stew/shims/net, chronicles,
eth/keys, eth/net/utils,
bearssl,
chronicles,
chronos,
nimcrypto,
stew/shims/net,
stint,
eth/net/utils,
./crypto,
./spr
export stint
@ -24,24 +30,28 @@ type
Node* = ref object
id*: NodeId
pubkey*: keys.PublicKey
pubkey*: PublicKey
address*: Option[Address]
record*: SignedPeerRecord
seen*: bool ## Indicates if there was at least one successful
## request-response with this node, or if the nde was verified
## through the underlying transport mechanisms.
func toNodeId*(pk: keys.PublicKey): NodeId =
func toNodeId*(pid: PeerId): NodeId =
## Convert public key to a node identifier.
# Keccak256 hash is used as defined in SPR spec for scheme v4:
# https://github.com/ethereum/devp2p/blob/master/enr.md#v4-identity-scheme
readUintBE[256](keccak256.digest(pk.toRaw()).data)
readUintBE[256](keccak256.digest(pid.data).data)
proc toNodeId*(pk: PublicKey): Result[NodeId, cstring] =
let pid = ? PeerId.init(pk)
ok pid.toNodeId
func newNode*(r: SignedPeerRecord): Result[Node, cstring] =
## Create a new `Node` from a `SignedPeerRecord`.
# TODO: Handle IPv6
let pk = r.get(keys.PublicKey)
let pk = r.get(PublicKey)
# This check is redundant for a properly created record as the deserialization
# of a record will fail at `verifySignature` if there is no public key.
if pk.isNone():
@ -49,17 +59,20 @@ func newNode*(r: SignedPeerRecord): Result[Node, cstring] =
# Also this can not fail for a properly created record as id is checked upon
# deserialization.
let tr = ? r.toTypedRecord()
let
tr = ? r.toTypedRecord()
nodeId = ? pk.get().toNodeId()
if tr.ip.isSome() and tr.udp.isSome():
let a = Address(ip: ipv4(tr.ip.get()), port: Port(tr.udp.get()))
ok(Node(id: pk.get().toNodeId(), pubkey: pk.get() , record: r,
ok(Node(id: nodeId, pubkey: pk.get() , record: r,
address: some(a)))
else:
ok(Node(id: pk.get().toNodeId(), pubkey: pk.get(), record: r,
ok(Node(id: nodeId, pubkey: pk.get(), record: r,
address: none(Address)))
proc update*(n: Node, pk: keys.PrivateKey, ip: Option[ValidIpAddress],
proc update*(n: Node, pk: PrivateKey, ip: Option[ValidIpAddress],
tcpPort, udpPort: Option[Port] = none[Port]()): Result[void, cstring] =
? n.record.update(pk, ip, tcpPort, udpPort)
@ -77,7 +90,8 @@ proc update*(n: Node, pk: keys.PrivateKey, ip: Option[ValidIpAddress],
ok()
func hash*(n: Node): hashes.Hash = hash(n.pubkey.toRaw)
func hash*(n: Node): hashes.Hash =
hash(n.pubkey.getRawBytes.expect("Public key has correct structure"))
func `==`*(a, b: Node): bool =
(a.isNil and b.isNil) or

View File

@ -77,7 +77,8 @@ import
std/[tables, sets, options, math, sequtils, algorithm],
stew/shims/net as stewNet, json_serialization/std/net,
stew/[base64, endians2, results], chronicles, chronos, chronos/timer, stint, bearssl,
metrics, eth/[rlp, keys, async_utils], libp2p/routing_record,
metrics, eth/[rlp, async_utils],
libp2p/[crypto/crypto, routing_record],
"."/[transport, messages, messages_encoding, node, routing_table, spr, random2, ip_vote, nodes_verification]
import nimcrypto except toHex
@ -120,7 +121,7 @@ type
Protocol* = ref object
localNode*: Node
privateKey: keys.PrivateKey
privateKey: PrivateKey
transport*: Transport[Protocol] # exported for tests
routingTable*: RoutingTable
awaitedMessages: Table[(NodeId, RequestId), Future[Option[Message]]]
@ -214,7 +215,7 @@ proc neighboursAtDistances*(d: Protocol, distances: seq[uint16],
proc nodesDiscovered*(d: Protocol): int = d.routingTable.len
func privKey*(d: Protocol): lent keys.PrivateKey =
func privKey*(d: Protocol): lent PrivateKey =
d.privateKey
func getRecord*(d: Protocol): SignedPeerRecord =
@ -948,7 +949,7 @@ func init*(
)
proc newProtocol*(
privKey: keys.PrivateKey,
privKey: PrivateKey,
enrIp: Option[ValidIpAddress],
enrTcpPort, enrUdpPort: Option[Port],
localEnrFields: openArray[(string, seq[byte])] = [],
@ -958,7 +959,7 @@ proc newProtocol*(
bindIp = IPv4_any(),
enrAutoUpdate = false,
config = defaultDiscoveryConfig,
rng = keys.newRng()):
rng = newRng()):
Protocol =
# TODO: Tried adding bindPort = udpPort as parameter but that gave
# "Error: internal error: environment misses: udpPort" in nim-beacon-chain.

View File

@ -12,7 +12,6 @@ import
stew/shims/net,
stew/base64,
eth/rlp,
eth/keys,
libp2p/crypto/crypto,
libp2p/crypto/secp,
libp2p/routing_record,
@ -36,13 +35,11 @@ proc seqNum*(r: SignedPeerRecord): uint64 =
proc append*(rlpWriter: var RlpWriter, value: SignedPeerRecord) =
# echo "encoding to:" & $value.signedPeerRecord.encode.get
var encoded = value.encode
trace "Encoding SignedPeerRecord for RLP", bytes = encoded.get(@[])
if encoded.isErr:
error "Error encoding SignedPeerRecord for RLP", error = encoded.error
rlpWriter.append encoded.get(@[])
proc fromBytes(r: var SignedPeerRecord, s: openArray[byte]): bool =
trace "Decoding SignedPeerRecord for RLP", bytes = s
let decoded = SignedPeerRecord.decode(@s)
if decoded.isErr:
@ -56,46 +53,24 @@ proc read*(rlp: var Rlp, T: typedesc[SignedPeerRecord]):
T {.raises: [RlpError, ValueError, Defect].} =
# echo "read:" & $rlp.rawData
## code directly borrowed from spr.nim
trace "Reading RLP SignedPeerRecord", rawData = rlp.rawData, toBytes = rlp.toBytes
if not rlp.hasData() or not result.fromBytes(rlp.toBytes):
# TODO: This could also just be an invalid signature, would be cleaner to
# split of RLP deserialisation errors from this.
raise newException(ValueError, "Could not deserialize")
rlp.skipElem()
proc get*(r: SignedPeerRecord, T: type crypto.PublicKey): Option[T] =
## Get the `PublicKey` from provided `Record`. Return `none` when there is
## no `PublicKey` in the record.
some(r.envelope.publicKey)
func pkToPk(pk: crypto.PublicKey) : Option[keys.PublicKey] =
some((keys.PublicKey)(pk.skkey))
func pkToPk(pk: keys.PublicKey) : Option[crypto.PublicKey] =
some(crypto.PublicKey.init((secp.SkPublicKey)(pk)))
func pkToPk(pk: crypto.PrivateKey) : Option[keys.PrivateKey] =
some((keys.PrivateKey)(pk.skkey))
func pkToPk(pk: keys.PrivateKey) : Option[crypto.PrivateKey] =
some(crypto.PrivateKey.init((secp.SkPrivateKey)(pk)))
proc get*(r: SignedPeerRecord, T: type keys.PublicKey): Option[T] =
proc get*(r: SignedPeerRecord, T: type PublicKey): Option[T] =
## Get the `PublicKey` from provided `Record`. Return `none` when there is
## no `PublicKey` in the record.
## PublicKey* = distinct SkPublicKey
let
pk = r.envelope.publicKey
pkToPk(pk)
r.envelope.publicKey.some
proc incSeqNo*(
r: var SignedPeerRecord,
pk: keys.PrivateKey): RecordResult[void] =
let cryptoPk = pk.pkToPk.get() # TODO: remove when eth/keys removed
pk: PrivateKey): RecordResult[void] =
r.data.seqNo.inc()
r = ? SignedPeerRecord.init(cryptoPk, r.data).mapErr(
r = ? SignedPeerRecord.init(pk, r.data).mapErr(
(e: CryptoError) =>
("Error initialising SignedPeerRecord with incremented seqNo: " &
$e).cstring
@ -123,10 +98,11 @@ proc update*(r: var SignedPeerRecord, pk: crypto.PrivateKey,
# ip/tcpPort/udpPort/extraFields
let
pubkey = r.get(crypto.PublicKey)
keysPubKey = pubkey.get.pkToPk.get # remove when move away from eth/keys
keysPrivKey = pk.pkToPk.get
if pubkey.isNone() or keysPubKey != keysPrivKey.toPublicKey:
sprPubKey = r.get(PublicKey)
pubKey = pk.getPublicKey
# keysPubKey = pubkey.get.pkToPk.get # remove when move away from eth/keys
# keysPrivKey = pk.pkToPk.get
if sprPubKey.isNone or pubKey.isErr or sprPubKey.get != pubKey.get:
return err("Public key does not correspond with given private key")
var
@ -208,13 +184,6 @@ proc update*(r: var SignedPeerRecord, pk: crypto.PrivateKey,
return ok()
proc update*(r: var SignedPeerRecord, pk: keys.PrivateKey,
ip: Option[ValidIpAddress],
tcpPort, udpPort: Option[Port] = none[Port]()):
RecordResult[void] =
let cPk = pkToPk(pk).get
r.update(cPk, ip, tcpPort, udpPort)
proc toTypedRecord*(r: SignedPeerRecord) : RecordResult[SignedPeerRecord] = ok(r)
proc ip*(r: SignedPeerRecord): Option[array[4, byte]] =
@ -275,7 +244,7 @@ proc toBase64*(r: SignedPeerRecord): string =
proc toURI*(r: SignedPeerRecord): string = "spr:" & r.toBase64
proc init*(T: type SignedPeerRecord, seqNum: uint64,
pk: crypto.PrivateKey,
pk: PrivateKey,
ip: Option[ValidIpAddress],
tcpPort, udpPort: Option[Port]):
RecordResult[T] =
@ -317,35 +286,10 @@ proc init*(T: type SignedPeerRecord, seqNum: uint64,
let ma = MultiAddress.init(ipAddr, proto, protoPort)
# if ip.isSome:
# let
# ipAddr = ip.get
# proto = ipAddr.family
# address = if proto == IPv4: ipAddr.address_v4
# else: ipAddr.address_v6
# u and udpPort.isSome
# # let ta = initTAddress(ip.get, udpPort.get)
# # echo ta
# # ma = MultiAddress.init(ta).get
# #let ma1 = MultiAddress.init("/ip4/127.0.0.1").get() #TODO
# #let ma2 = MultiAddress.init(multiCodec("udp"), udpPort.get.int).get
# #ma = ma1 & ma2
# ma = MultiAddress.init("/ip4/127.0.0.1/udp/" & $udpPort.get.int).get #TODO
# else:
# ma = MultiAddress.init()
# # echo "not implemented"
let pr = PeerRecord.init(peerId, @[ma], seqNum)
SignedPeerRecord.init(pk, pr).mapErr((e: CryptoError) => ("Failed to init SignedPeerRecord: " & $e).cstring)
proc init*(T: type SignedPeerRecord, seqNum: uint64,
pk: keys.PrivateKey,
ip: Option[ValidIpAddress],
tcpPort, udpPort: Option[Port]):
RecordResult[T] =
let kPk = pkToPk(pk).get
SignedPeerRecord.init(seqNum, kPk, ip, tcpPort, udpPort)
proc contains*(r: SignedPeerRecord, fp: (string, seq[byte])): bool =
# TODO: use FieldPair for this, but that is a bit cumbersome. Perhaps the
# `get` call can be improved to make this easier.

View File

@ -7,8 +7,10 @@
# Everything below the handling of ordinary messages
import
std/[tables, options],
bearssl,
chronos,
chronicles,
libp2p/crypto/crypto,
stew/shims/net,
"."/[node, encoding, sessions]
@ -124,8 +126,15 @@ proc receive*(t: Transport, a: Address, packet: openArray[byte]) =
# This is a node we previously contacted and thus must have an address.
doAssert(toNode.address.isSome())
let address = toNode.address.get()
let data = encodeHandshakePacket(t.rng[], t.codec, toNode.id,
address, pr.message, packet.whoareyou, toNode.pubkey)
let data = encodeHandshakePacket(
t.rng[],
t.codec,
toNode.id,
address,
pr.message,
packet.whoareyou,
toNode.pubkey
).expect("Valid handshake packet to encode")
trace "Send handshake message packet", dstId = toNode.id, address
t.send(toNode, data)

View File

@ -1,19 +1,30 @@
import
stew/shims/net, bearssl, chronos,
eth/keys,
libp2pdht/discv5/[spr, node, routing_table],
bearssl,
chronos,
libp2p/crypto/[crypto, secp],
libp2p/multiaddress,
libp2pdht/discv5/[node, routing_table, spr],
libp2pdht/discv5/crypto as dhtcrypto,
libp2pdht/discv5/protocol as discv5_protocol,
libp2p/crypto/crypto,
libp2p/multiaddress
stew/shims/net
export net
proc localAddress*(port: int): Address =
Address(ip: ValidIpAddress.init("127.0.0.1"), port: Port(port))
proc example*(T: type PrivateKey, rng: ref HmacDrbgContext): PrivateKey =
PrivateKey.random(rng[]).expect("Valid rng for private key")
proc example*(T: type NodeId, rng: ref HmacDrbgContext): NodeId =
let
privKey = PrivateKey.example(rng)
pubKey = privKey.getPublicKey.expect("Valid private key for public key")
pubKey.toNodeId().expect("Public key valid for node id")
proc initDiscoveryNode*(
rng: ref BrHmacDrbgContext,
privKey: keys.PrivateKey,
privKey: PrivateKey,
address: Address,
bootstrapRecords: openArray[SignedPeerRecord] = [],
localEnrFields: openArray[(string, seq[byte])] = [],
@ -41,7 +52,7 @@ proc nodeIdInNodes*(id: NodeId, nodes: openArray[Node]): bool =
for n in nodes:
if id == n.id: return true
proc generateNode*(privKey: keys.PrivateKey, port: int = 20302,
proc generateNode*(privKey: PrivateKey, port: int = 20302,
ip: ValidIpAddress = ValidIpAddress.init("127.0.0.1")): Node =
let
port = Port(port)
@ -52,17 +63,20 @@ proc generateNode*(privKey: keys.PrivateKey, port: int = 20302,
proc generateNRandomNodes*(rng: ref BrHmacDrbgContext, n: int): seq[Node] =
var res = newSeq[Node]()
for i in 1..n:
let node = generateNode(keys.PrivateKey.random(rng[]))
let
privKey = PrivateKey.example(rng)
node = privKey.generateNode()
res.add(node)
res
proc nodeAndPrivKeyAtDistance*(n: Node, rng: var BrHmacDrbgContext, d: uint32,
ip: ValidIpAddress = ValidIpAddress.init("127.0.0.1")): (Node, keys.PrivateKey) =
ip: ValidIpAddress = ValidIpAddress.init("127.0.0.1")): (Node, PrivateKey) =
while true:
let pk = keys.PrivateKey.random(rng)
let node = generateNode(pk, ip = ip)
let
privKey = PrivateKey.random(rng).expect("Valid rng for private key")
node = privKey.generateNode(ip = ip)
if logDistance(n.id, node.id) == d:
return (node, pk)
return (node, privKey)
proc nodeAtDistance*(n: Node, rng: var BrHmacDrbgContext, d: uint32,
ip: ValidIpAddress = ValidIpAddress.init("127.0.0.1")): Node =
@ -98,7 +112,7 @@ func udpExamples*(_: type MultiAddress, count: int): seq[MultiAddress] =
res.add Multiaddress.init("/ip4/0.0.0.0/udp/" & $i).get
return res
proc toSignedPeerRecord*(privKey: crypto.PrivateKey) : SignedPeerRecord =
proc toSignedPeerRecord*(privKey: PrivateKey) : SignedPeerRecord =
## handle conversion between the two worlds
let pr = PeerRecord.init(
@ -109,7 +123,7 @@ proc toSignedPeerRecord*(privKey: crypto.PrivateKey) : SignedPeerRecord =
proc example*(T: type SignedPeerRecord): T =
let
rng = crypto.newRng()
privKey = crypto.PrivateKey.random(rng[]).expect("Valid rng")
rng = newRng()
privKey = PrivateKey.example(rng)
privKey.toSignedPeerRecord

View File

@ -10,32 +10,30 @@
{.used.}
import
std/options,
std/sequtils,
chronos, stew/byteutils, nimcrypto, asynctest,
eth/keys,
libp2pdht/dht,
std/[options, sequtils],
asynctest,
bearssl,
chronicles,
chronos,
nimcrypto,
libp2p/crypto/[crypto, secp],
libp2p/[multiaddress, multicodec, multihash, routing_record, signed_envelope],
libp2pdht/dht,
libp2pdht/discv5/crypto as dhtcrypto,
libp2pdht/discv5/protocol as discv5_protocol,
test_helper,
libp2p/crypto/crypto,
libp2p/crypto/secp,
libp2p/routing_record,
libp2p/multiaddress,
libp2p/multihash,
libp2p/multicodec,
libp2p/signed_envelope
stew/byteutils,
test_helper
proc bootstrapNodes(
nodecount: int,
bootnodes: seq[SignedPeerRecord],
rng = keys.newRng(),
rng = newRng(),
delay: int = 0
) : Future[seq[(discv5_protocol.Protocol, keys.PrivateKey)]] {.async.} =
) : Future[seq[(discv5_protocol.Protocol, PrivateKey)]] {.async.} =
debug "---- STARTING BOOSTRAPS ---"
for i in 0..<nodecount:
let privKey = keys.PrivateKey.random(rng[])
let privKey = PrivateKey.example(rng)
let node = initDiscoveryNode(rng, privKey, localAddress(20302 + i), bootnodes)
node.start()
result.add((node, privKey))
@ -47,13 +45,14 @@ proc bootstrapNodes(
proc bootstrapNetwork(
nodecount: int,
rng = keys.newRng(),
rng = newRng(),
delay: int = 0
) : Future[seq[(discv5_protocol.Protocol, keys.PrivateKey)]] {.async.} =
) : Future[seq[(discv5_protocol.Protocol, PrivateKey)]] {.async.} =
let
bootNodeKey = keys.PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
bootNodeKey = PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")
.expect("Valid private key hex")
bootNodeAddr = localAddress(20301)
bootNode = initDiscoveryNode(rng, bootNodeKey, bootNodeAddr, @[]) # just a shortcut for new and open
@ -66,30 +65,23 @@ proc bootstrapNetwork(
res.insert((bootNode, bootNodeKey), 0)
return res
# TODO: Remove this once we have removed all traces of nim-eth/keys
func pkToPk(pk: keys.PrivateKey) : Option[crypto.PrivateKey] =
let res = some(crypto.PrivateKey.init((secp.SkPrivateKey)(pk)))
return res
# suite "Providers Tests":
suite "Providers Tests: node alone":
var
rng: ref HmacDrbgContext
nodes: seq[(discv5_protocol.Protocol, keys.PrivateKey)]
nodes: seq[(discv5_protocol.Protocol, PrivateKey)]
targetId: NodeId
node0: discv5_protocol.Protocol
privKey_keys0: keys.PrivateKey
privKey0: crypto.PrivateKey
privKey0: PrivateKey
signedPeerRec0: SignedPeerRecord
peerRec0: PeerRecord
setupAll:
rng = keys.newRng()
rng = newRng()
nodes = await bootstrapNetwork(nodecount=1)
targetId = toNodeId(keys.PrivateKey.random(rng[]).toPublicKey)
(node0, privKey_keys0) = nodes[0]
privKey0 = privKey_keys0.pkToPk.get
targetId = NodeId.example(rng)
(node0, privKey0) = nodes[0]
signedPeerRec0 = privKey0.toSignedPeerRecord
peerRec0 = signedPeerRec0.data
@ -122,7 +114,7 @@ suite "Providers Tests: node alone":
test "Should not retrieve bogus":
let bogusId = toNodeId(keys.PrivateKey.random(rng[]).toPublicKey)
let bogusId = NodeId.example(rng)
debug "---- STARTING PROVIDERS LOOKUP ---"
let providersRes = await node0.getProviders(bogusId)
@ -138,20 +130,18 @@ suite "Providers Tests: two nodes":
var
rng: ref HmacDrbgContext
nodes: seq[(discv5_protocol.Protocol, keys.PrivateKey)]
nodes: seq[(discv5_protocol.Protocol, PrivateKey)]
targetId: NodeId
node0: discv5_protocol.Protocol
privKey_keys0: keys.PrivateKey
privKey0: crypto.PrivateKey
privKey0: PrivateKey
signedPeerRec0: SignedPeerRecord
peerRec0: PeerRecord
setupAll:
rng = keys.newRng()
rng = newRng()
nodes = await bootstrapNetwork(nodecount=3)
targetId = toNodeId(keys.PrivateKey.random(rng[]).toPublicKey)
(node0, privKey_keys0) = nodes[0]
privKey0 = privKey_keys0.pkToPk.get
targetId = NodeId.example(rng)
(node0, privKey0) = nodes[0]
signedPeerRec0 = privKey0.toSignedPeerRecord
peerRec0 = signedPeerRec0.data
@ -185,26 +175,22 @@ suite "Providers Tests: two nodes":
debug "Providers:", providers
check (providers.len == 1 and providers[0].data.peerId == peerRec0.peerId)
suite "Providers Tests: 20 nodes":
var
rng: ref HmacDrbgContext
nodes: seq[(discv5_protocol.Protocol, keys.PrivateKey)]
nodes: seq[(discv5_protocol.Protocol, PrivateKey)]
targetId: NodeId
node0: discv5_protocol.Protocol
privKey_keys0: keys.PrivateKey
privKey0: crypto.PrivateKey
privKey0: PrivateKey
signedPeerRec0: SignedPeerRecord
peerRec0: PeerRecord
setupAll:
rng = keys.newRng()
rng = newRng()
nodes = await bootstrapNetwork(nodecount=20)
targetId = toNodeId(keys.PrivateKey.random(rng[]).toPublicKey)
(node0, privKey_keys0) = nodes[0]
privKey0 = privKey_keys0.pkToPk.get
targetId = NodeId.example(rng)
(node0, privKey0) = nodes[0]
signedPeerRec0 = privKey0.toSignedPeerRecord
peerRec0 = signedPeerRec0.data

View File

@ -4,8 +4,9 @@ import
std/tables,
chronos, chronicles, stint, asynctest, stew/shims/net,
stew/byteutils, bearssl,
eth/keys,
libp2p/crypto/crypto,
libp2pdht/discv5/[transport, spr, node, routing_table, encoding, sessions, messages, nodes_verification],
libp2pdht/discv5/crypto as dhtcrypto,
libp2pdht/discv5/protocol as discv5_protocol,
../dht/test_helper
@ -13,18 +14,21 @@ suite "Discovery v5 Tests":
var rng: ref HmacDrbgContext
setup:
rng = keys.newRng()
rng = newRng()
test "GetNode":
# TODO: This could be tested in just a routing table only context
let
node = initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20302))
targetNode = generateNode(keys.PrivateKey.random(rng[]))
pk = PrivateKey.example(rng)
targetPk = PrivateKey.example(rng)
node = initDiscoveryNode(rng, pk, localAddress(20302))
targetNode = targetPk.generateNode()
check node.addNode(targetNode)
for i in 0..<1000:
discard node.addNode(generateNode(keys.PrivateKey.random(rng[])))
let pk = PrivateKey.example(rng)
discard node.addNode(pk.generateNode())
let n = node.getNode(targetNode.id)
check n.isSome()
@ -34,13 +38,14 @@ suite "Discovery v5 Tests":
test "Node deletion":
let
pkBootnode = PrivateKey.example(rng)
bootnode = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20301))
rng, PrivateKey.example(rng), localAddress(20301))
node1 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302),
rng, PrivateKey.example(rng), localAddress(20302),
@[bootnode.localNode.record])
node2 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20303),
rng, PrivateKey.example(rng), localAddress(20303),
@[bootnode.localNode.record])
pong1 = await discv5_protocol.ping(node1, bootnode.localNode)
pong2 = await discv5_protocol.ping(node1, node2.localNode)
@ -88,58 +93,64 @@ suite "Discovery v5 Tests":
# Values for this test are taken from
# https://github.com/ethereum/go-ethereum/blob/d8ff53dfb8a516f47db37dbc7fd7ad18a1e8a125/p2p/discover/v4_lookup_test.go#L176
const
targetKey = "5d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125"
targetKey = "045d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125"
testValues = [
("29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169", 251'u16),
("511b1686e4e58a917f7f848e9bf5539d206a68f5ad6b54b552c2399fe7d174ae", 251'u16),
("d09e5eaeec0fd596236faed210e55ef45112409a5aa7f3276d26646080dcfaeb", 251'u16),
("c1e20dbbf0d530e50573bd0a260b32ec15eb9190032b4633d44834afc8afe578", 251'u16),
("ed5f38f5702d92d306143e5d9154fb21819777da39af325ea359f453d179e80b", 251'u16),
("14a98db9b46a831d67eff29f3b85b1b485bb12ae9796aea98d91be3dc78d8a91", 248'u16),
("1c9b1cafbec00848d2c174b858219914b42a7d5c9359b1ca03fd650e8239ae94", 252'u16),
("e0e1e8db4a6f13c1ffdd3e96b72fa7012293ced187c9dcdcb9ba2af37a46fa10", 252'u16),
("3d53823e0a0295cb09f3e11d16c1b44d07dd37cec6f739b8df3a590189fe9fb9", 252'u16),
("29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169", 252'u16),
("dec742079ec00ff4ec1284d7905bc3de2366f67a0769431fd16f80fd68c58a7c", 252'u16),
("2d0511ae9bf590166597eeab86b6f27b1ab761761eaea8965487b162f8703847", 253'u16),
("6cfbd7b8503073fc3dbdb746a7c672571648d3bd15197ccf7f7fef3d904f53a2", 253'u16),
("a30599b12827b69120633f15b98a7f6bc9fc2e9a0fd6ae2ebb767c0e64d743ab", 253'u16),
("14a98db9b46a831d67eff29f3b85b1b485bb12ae9796aea98d91be3dc78d8a91", 253'u16),
("2369ff1fc1ff8ca7d20b17e2673adc3365c3674377f21c5d9dafaff21fe12e24", 253'u16),
("9ae91101d6b5048607f41ec0f690ef5d09507928aded2410aabd9237aa2727d7", 253'u16),
("05e3c59090a3fd1ae697c09c574a36fcf9bedd0afa8fe3946f21117319ca4973", 253'u16),
("06f31c5ea632658f718a91a1b1b9ae4b7549d7b3bc61cbc2be5f4a439039f3ad", 253'u16),
("ce1435a956a98ffec484cd11489c4f165cf1606819ab6b521cee440f0c677e9e", 253'u16),
("120260dce739b6f71f171da6f65bc361b5fad51db74cf02d3e973347819a6518", 253'u16),
("dec742079ec00ff4ec1284d7905bc3de2366f67a0769431fd16f80fd68c58a7c", 254'u16),
("ff02c8861fa12fbd129d2a95ea663492ef9c1e51de19dcfbbfe1c59894a28d2b", 254'u16),
("4dded9e4eefcbce4262be4fd9e8a773670ab0b5f448f286ec97dfc8cf681444a", 254'u16),
("750d931e2a8baa2c9268cb46b7cd851f4198018bed22f4dceb09dd334a2395f6", 254'u16),
("ce1435a956a98ffec484cd11489c4f165cf1606819ab6b521cee440f0c677e9e", 254'u16),
("a30599b12827b69120633f15b98a7f6bc9fc2e9a0fd6ae2ebb767c0e64d743ab", 254'u16),
("8c5b422155d33ea8e9d46f71d1ad3e7b24cb40051413ffa1a81cff613d243ba9", 254'u16),
("996e7f8d1638be92d7328b4770f47e5420fc4bafecb4324fd33b1f5d9f403a75", 254'u16),
("46bd1eddcf6431bea66fc19ebc45df191c1c7d6ed552dcdc7392885009c322f0", 254'u16),
("da8645f90826e57228d9ea72aff84500060ad111a5d62e4af831ed8e4b5acfb8", 255'u16),
("3c944c5d9af51d4c1d43f5d0f3a1a7ef65d5e82744d669b58b5fed242941a566", 255'u16),
("5ebcde76f1d579eebf6e43b0ffe9157e65ffaa391175d5b9aa988f47df3e33da", 255'u16),
("d09e5eaeec0fd596236faed210e55ef45112409a5aa7f3276d26646080dcfaeb", 255'u16),
("6cfbd7b8503073fc3dbdb746a7c672571648d3bd15197ccf7f7fef3d904f53a2", 255'u16),
("9ae91101d6b5048607f41ec0f690ef5d09507928aded2410aabd9237aa2727d7", 255'u16),
("06f31c5ea632658f718a91a1b1b9ae4b7549d7b3bc61cbc2be5f4a439039f3ad", 255'u16),
("97f78253a7d1d796e4eaabce721febcc4550dd68fb11cc818378ba807a2cb7de", 255'u16),
("a38cd7dc9b4079d1c0406afd0fdb1165c285f2c44f946eca96fc67772c988c7d", 255'u16),
("d64cbb3ffdf712c372b7a22a176308ef8f91861398d5dbaf326fd89c6eaeef1c", 255'u16),
("d269609743ef29d6446e3355ec647e38d919c82a4eb5837e442efd7f4218944f", 255'u16),
("d8f7bcc4a530efde1d143717007179e0d9ace405ddaaf151c4d863753b7fd64c", 255'u16),
("1fa56cf25d4b46c2bf94e82355aa631717b63190785ac6bae545a88aadc304a9", 255'u16),
("3c38c503c0376f9b4adcbe935d5f4b890391741c764f61b03cd4d0d42deae002", 255'u16),
("3a54af3e9fa162bc8623cdf3e5d9b70bf30ade1d54cc3abea8659aba6cff471f", 255'u16),
("8c5b422155d33ea8e9d46f71d1ad3e7b24cb40051413ffa1a81cff613d243ba9", 256'u16),
("511b1686e4e58a917f7f848e9bf5539d206a68f5ad6b54b552c2399fe7d174ae", 256'u16),
("c1e20dbbf0d530e50573bd0a260b32ec15eb9190032b4633d44834afc8afe578", 256'u16),
("ed5f38f5702d92d306143e5d9154fb21819777da39af325ea359f453d179e80b", 256'u16),
("1c9b1cafbec00848d2c174b858219914b42a7d5c9359b1ca03fd650e8239ae94", 256'u16),
("e0e1e8db4a6f13c1ffdd3e96b72fa7012293ced187c9dcdcb9ba2af37a46fa10", 256'u16),
("3d53823e0a0295cb09f3e11d16c1b44d07dd37cec6f739b8df3a590189fe9fb9", 256'u16),
("2d0511ae9bf590166597eeab86b6f27b1ab761761eaea8965487b162f8703847", 256'u16),
("2369ff1fc1ff8ca7d20b17e2673adc3365c3674377f21c5d9dafaff21fe12e24", 256'u16),
("05e3c59090a3fd1ae697c09c574a36fcf9bedd0afa8fe3946f21117319ca4973", 256'u16),
("ff02c8861fa12fbd129d2a95ea663492ef9c1e51de19dcfbbfe1c59894a28d2b", 256'u16),
("4dded9e4eefcbce4262be4fd9e8a773670ab0b5f448f286ec97dfc8cf681444a", 256'u16),
("750d931e2a8baa2c9268cb46b7cd851f4198018bed22f4dceb09dd334a2395f6", 256'u16),
("46bd1eddcf6431bea66fc19ebc45df191c1c7d6ed552dcdc7392885009c322f0", 256'u16),
("da8645f90826e57228d9ea72aff84500060ad111a5d62e4af831ed8e4b5acfb8", 256'u16),
("3c944c5d9af51d4c1d43f5d0f3a1a7ef65d5e82744d669b58b5fed242941a566", 256'u16),
("5ebcde76f1d579eebf6e43b0ffe9157e65ffaa391175d5b9aa988f47df3e33da", 256'u16),
("a38cd7dc9b4079d1c0406afd0fdb1165c285f2c44f946eca96fc67772c988c7d", 256'u16),
("d64cbb3ffdf712c372b7a22a176308ef8f91861398d5dbaf326fd89c6eaeef1c", 255'u16),
("d269609743ef29d6446e3355ec647e38d919c82a4eb5837e442efd7f4218944f", 256'u16),
("937b1af801def4e8f5a3a8bd225a8bcff1db764e41d3e177f2e9376e8dd87233", 256'u16),
("120260dce739b6f71f171da6f65bc361b5fad51db74cf02d3e973347819a6518", 256'u16),
("1fa56cf25d4b46c2bf94e82355aa631717b63190785ac6bae545a88aadc304a9", 256'u16),
("3c38c503c0376f9b4adcbe935d5f4b890391741c764f61b03cd4d0d42deae002", 256'u16),
("3a54af3e9fa162bc8623cdf3e5d9b70bf30ade1d54cc3abea8659aba6cff471f", 256'u16),
("6799a02ea1999aefdcbcc4d3ff9544478be7365a328d0d0f37c26bd95ade0cda", 256'u16),
("e24a7bc9051058f918646b0f6e3d16884b2a55a15553b89bab910d55ebc36116", 256'u16)
]
let targetId = toNodeId(keys.PublicKey.fromHex(targetKey)[])
let
targetPubKey = PublicKey.fromHex(targetKey).expect("Valid public key hex")
targetId = targetPubKey.toNodeId().expect("Public key valid for node id")
for (key, d) in testValues:
let id = toNodeId(keys.PrivateKey.fromHex(key)[].toPublicKey())
let
privKey = PrivateKey.fromHex(key).expect("Valid private key hex")
pubKey = privKey.getPublicKey.expect("Valid private key for public key")
id = pubKey.toNodeId.expect("Public key valid for node id")
debugEcho ">>> key: ", key
check logDistance(targetId, id) == d
test "Distance to id check":
@ -160,17 +171,19 @@ suite "Discovery v5 Tests":
test "Distance to id check with keys":
const
targetKey = "5d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125"
targetKey = "045d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125"
testValues = [ # possible id in that distance range
("9e5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 251'u16),
("925b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 252'u16),
("8a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 253'u16),
("ba5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 254'u16),
("da5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 255'u16),
("1a5b34809116e3790b2258a45e7ef03b11af786503fb1a6d4b4a8ca021ad653c", 256'u16)
("cd2c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 251'u16),
("c12c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 252'u16),
("d92c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 253'u16),
("e92c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 254'u16),
("892c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 255'u16),
("492c707bdcbdf5109c66de68e8f26adec1527d075e0f93df0ad21c72e98b7a4d", 256'u16)
]
let targetId = toNodeId(keys.PublicKey.fromHex(targetKey)[])
let
targetPubKey = PublicKey.fromHex(targetKey).expect("Valid public key hex")
targetId = targetPubKey.toNodeId().expect("Public key valid for node id")
for (id, d) in testValues:
check idAtDistance(targetId, d) == parse(id, UInt256, 16)
@ -178,10 +191,12 @@ suite "Discovery v5 Tests":
test "FindNode Test":
const dist = 253'u16
let
mainNodeKey = keys.PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
testNodeKey = keys.PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a618")[]
mainNodeKey = PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")
.expect("Valid private key hex")
testNodeKey = PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a618")
.expect("Valid private key hex")
mainNode = initDiscoveryNode(rng, mainNodeKey, localAddress(20301))
testNode = initDiscoveryNode(rng, testNodeKey, localAddress(20302))
# logarithmic distance between mainNode and testNode is 256
@ -246,11 +261,11 @@ suite "Discovery v5 Tests":
test "FindNode with test table":
let mainNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
# Generate 1000 random nodes and add to our main node's routing table
for i in 0..<1000:
discard mainNode.addSeenNode(generateNode(keys.PrivateKey.random(rng[]))) # for testing only!
discard mainNode.addSeenNode(generateNode(PrivateKey.example(rng))) # for testing only!
let
neighbours = mainNode.neighbours(mainNode.localNode.id)
@ -261,7 +276,7 @@ suite "Discovery v5 Tests":
let
testNode = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302),
rng, PrivateKey.example(rng), localAddress(20302),
@[mainNode.localNode.record])
discovered = await findNode(testNode, mainNode.localNode,
@[closestDistance])
@ -277,13 +292,13 @@ suite "Discovery v5 Tests":
nodeCount = 17
let bootNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
bootNode.start()
var nodes = newSeqOfCap[discv5_protocol.Protocol](nodeCount)
nodes.add(bootNode)
for i in 1 ..< nodeCount:
nodes.add(initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301 + i),
nodes.add(initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301 + i),
@[bootNode.localNode.record]))
# Make sure all nodes have "seen" each other by forcing pings
@ -317,10 +332,10 @@ suite "Discovery v5 Tests":
test "Resolve target":
let
mainNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
lookupNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20302))
targetKey = keys.PrivateKey.random(rng[])
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20302))
targetKey = PrivateKey.example(rng)
targetAddress = localAddress(20303)
targetNode = initDiscoveryNode(rng, targetKey, targetAddress)
targetId = targetNode.localNode.id
@ -400,10 +415,10 @@ suite "Discovery v5 Tests":
# We no longer support field filtering
# test "Random nodes with spr field filter":
# let
# lookupNode = initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
# targetNode = generateNode(keys.PrivateKey.random(rng[]))
# otherNode = generateNode(keys.PrivateKey.random(rng[]))
# anotherNode = generateNode(keys.PrivateKey.random(rng[]))
# lookupNode = initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
# targetNode = generateNode(PrivateKey.example(rng))
# otherNode = generateNode(PrivateKey.example(rng))
# anotherNode = generateNode(PrivateKey.example(rng))
# check:
# lookupNode.addNode(targetNode)
@ -420,7 +435,7 @@ suite "Discovery v5 Tests":
test "New protocol with spr":
let
privKey = keys.PrivateKey.random(rng[])
privKey = PrivateKey.example(rng)
ip = some(ValidIpAddress.init("127.0.0.1"))
port = Port(20301)
node = newProtocol(privKey, ip, some(port), some(port), bindPort = port,
@ -441,16 +456,16 @@ suite "Discovery v5 Tests":
# Defect (for now?) on incorrect key use
expect ResultDefect:
let incorrectKeyUpdates = newProtocol(keys.PrivateKey.random(rng[]),
let incorrectKeyUpdates = newProtocol(PrivateKey.example(rng),
ip, some(port), some(port), bindPort = port, rng = rng,
previousRecord = some(updatesNode.getRecord()))
test "Update node record with revalidate":
let
mainNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
testNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20302))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20302))
testNodeId = testNode.localNode.id
check:
@ -482,9 +497,9 @@ suite "Discovery v5 Tests":
test "Update node record with handshake":
let
mainNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20301))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20301))
testNode =
initDiscoveryNode(rng, keys.PrivateKey.random(rng[]), localAddress(20302))
initDiscoveryNode(rng, PrivateKey.example(rng), localAddress(20302))
testNodeId = testNode.localNode.id
# Add the node (from the record, so new node!) so no handshake is done yet.
@ -515,18 +530,20 @@ suite "Discovery v5 Tests":
test "Verify records of nodes message":
let
port = Port(9000)
fromNoderecord = SignedPeerRecord.init(1, keys.PrivateKey.random(rng[]),
fromNoderecord = SignedPeerRecord.init(1, PrivateKey.example(rng),
some(ValidIpAddress.init("11.12.13.14")),
some(port), some(port))[]
fromNode = newNode(fromNoderecord)[]
pk = keys.PrivateKey.random(rng[])
targetDistance = @[logDistance(fromNode.id, pk.toPublicKey().toNodeId())]
privKey = PrivateKey.example(rng)
pubKey = privKey.getPublicKey.expect("Valid private key for public key")
nodeId = pubKey.toNodeId().expect("Public key valid for node id")
targetDistance = @[logDistance(fromNode.id, nodeId)]
limit = 16
block: # Duplicates
let
record = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("12.13.14.15")),
1, privKey, some(ValidIpAddress.init("12.13.14.15")),
some(port), some(port))[]
# Exact duplicates
@ -536,7 +553,7 @@ suite "Discovery v5 Tests":
# Node id duplicates
let recordSameId = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("212.13.14.15")),
1, privKey, some(ValidIpAddress.init("212.13.14.15")),
some(port), some(port))[]
records.add(recordSameId)
nodes = verifyNodesRecords(records, fromNode, limit, targetDistance)
@ -545,7 +562,7 @@ suite "Discovery v5 Tests":
block: # No address
let
recordNoAddress = SignedPeerRecord.init(
1, pk, none(ValidIpAddress), some(port), some(port))[]
1, privKey, none(ValidIpAddress), some(port), some(port))[]
records = [recordNoAddress]
test = verifyNodesRecords(records, fromNode, limit, targetDistance)
check test.len == 0
@ -553,7 +570,7 @@ suite "Discovery v5 Tests":
block: # Invalid address - site local
let
recordInvalidAddress = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("10.1.2.3")),
1, privKey, some(ValidIpAddress.init("10.1.2.3")),
some(port), some(port))[]
records = [recordInvalidAddress]
test = verifyNodesRecords(records, fromNode, limit, targetDistance)
@ -562,7 +579,7 @@ suite "Discovery v5 Tests":
block: # Invalid address - loopback
let
recordInvalidAddress = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("127.0.0.1")),
1, privKey, some(ValidIpAddress.init("127.0.0.1")),
some(port), some(port))[]
records = [recordInvalidAddress]
test = verifyNodesRecords(records, fromNode, limit, targetDistance)
@ -571,7 +588,7 @@ suite "Discovery v5 Tests":
block: # Invalid distance
let
recordInvalidDistance = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("12.13.14.15")),
1, privKey, some(ValidIpAddress.init("12.13.14.15")),
some(port), some(port))[]
records = [recordInvalidDistance]
test = verifyNodesRecords(records, fromNode, limit, @[0'u16])
@ -580,7 +597,7 @@ suite "Discovery v5 Tests":
block: # Invalid distance but distance validation is disabled
let
recordInvalidDistance = SignedPeerRecord.init(
1, pk, some(ValidIpAddress.init("12.13.14.15")),
1, privKey, some(ValidIpAddress.init("12.13.14.15")),
some(port), some(port))[]
records = [recordInvalidDistance]
test = verifyNodesRecords(records, fromNode, limit)
@ -598,14 +615,14 @@ suite "Discovery v5 Tests":
test "Handshake cleanup: different ids":
# Node to test the handshakes on.
let receiveNode = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
# Create random packets with same ip but different node ids
# and "receive" them on receiveNode
let a = localAddress(20303)
for i in 0 ..< 5:
let
privKey = keys.PrivateKey.random(rng[])
privKey = PrivateKey.example(rng)
enrRec = SignedPeerRecord.init(1, privKey,
some(ValidIpAddress.init("127.0.0.1")), some(Port(9000)),
some(Port(9000))).expect("Properly intialized private key")
@ -629,12 +646,12 @@ suite "Discovery v5 Tests":
test "Handshake cleanup: different ips":
# Node to test the handshakes on.
let receiveNode = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
# Create random packets with same node ids but different ips
# and "receive" them on receiveNode
let
privKey = keys.PrivateKey.random(rng[])
privKey = PrivateKey.example(rng)
enrRec = SignedPeerRecord.init(1, privKey,
some(ValidIpAddress.init("127.0.0.1")), some(Port(9000)),
some(Port(9000))).expect("Properly intialized private key")
@ -659,13 +676,13 @@ suite "Discovery v5 Tests":
test "Handshake duplicates":
# Node to test the handshakes on.
let receiveNode = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
# Create random packets with same node ids and same ips
# and "receive" them on receiveNode
let
a = localAddress(20303)
privKey = keys.PrivateKey.random(rng[])
privKey = PrivateKey.example(rng)
enrRec = SignedPeerRecord.init(1, privKey,
some(ValidIpAddress.init("127.0.0.1")), some(Port(9000)),
some(Port(9000))).expect("Properly intialized private key")
@ -692,9 +709,9 @@ suite "Discovery v5 Tests":
test "Talkreq no protocol":
let
node1 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
node2 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20303))
rng, PrivateKey.example(rng), localAddress(20303))
talkresp = await discv5_protocol.talkReq(node1, node2.localNode,
@[byte 0x01], @[])
@ -708,9 +725,9 @@ suite "Discovery v5 Tests":
test "Talkreq echo protocol":
let
node1 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
node2 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20303))
rng, PrivateKey.example(rng), localAddress(20303))
talkProtocol = "echo".toBytes()
proc handler(protocol: TalkProtocol, request: seq[byte], fromId: NodeId, fromUdpAddress: Address): seq[byte]
@ -733,9 +750,9 @@ suite "Discovery v5 Tests":
test "Talkreq register protocols":
let
node1 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20302))
rng, PrivateKey.example(rng), localAddress(20302))
node2 = initDiscoveryNode(
rng, keys.PrivateKey.random(rng[]), localAddress(20303))
rng, PrivateKey.example(rng), localAddress(20303))
talkProtocol = "echo".toBytes()
proc handler(protocol: TalkProtocol, request: seq[byte], fromId: NodeId, fromUdpAddress: Address): seq[byte]

View File

@ -2,13 +2,20 @@
import
std/[options, sequtils, tables],
chronos,
asynctest/unittest2,
stint, stew/byteutils, stew/shims/net,
eth/[keys,rlp],
bearssl,
chronos,
eth/rlp,
libp2p/crypto/secp,
libp2pdht/discv5/[messages, messages_encoding, encoding, spr, node, sessions],
libp2pdht/discv5/crypto,
stew/byteutils,
stew/shims/net,
stint,
../dht/test_helper
from secp256k1 import toRaw
suite "Discovery v5.1 Protocol Message Encodings":
test "Ping Request":
let
@ -89,15 +96,15 @@ suite "Discovery v5.1 Protocol Message Encodings":
test "Nodes Response (multiple)":
var s1, s2: SignedPeerRecord
check s1.fromURI("spr:CiQIARIgWu2YZ5TQVW1gWEfvQijVHqSBtjCbwDt9VppJvYpHX9wSAgMBGlUKJgAkCAESIFrtmGeU0FVtYFhH70Io1R6kgbYwm8A7fVaaSb2KR1_cEKz1xZEGGgsKCQQAAAAAkQIAARoLCgkEAAAAAJECAAIaCwoJBAAAAACRAgADKkAjkK9DeWc82uzd1AEjRr-ksQyRiQ7vYGV4Af3FAEi0JgHvMC8RCQdqn2wBYxvBcyO8o1XMEEKCG01AUZrJlCkD")
check s2.fromURI("spr:CiQIARIguW3cNKnlvRsJVmV0ddgFMmvfAQLi0zf4tlt_6WGA03YSAgMBGlUKJgAkCAESILlt3DSp5b0bCVZldHXYBTJr3wEC4tM3-LZbf-lhgNN2EKz1xZEGGgsKCQQAAAAAkQIAARoLCgkEAAAAAJECAAIaCwoJBAAAAACRAgADKkC4Y9NkDHf-71LOvZon0NjmyzQnkm4IlAJGMDPS0cbSgIF3-2cECC5mRiXHjcHWlI5hPpxUURxFyIgSp7XX1jIL")
check s1.fromURI("spr:CiUIAhIhAjOdSH7SNzktg3kZUNyJHwY23mmMH6BR6gGuP6WL14WAEgIDARpWCicAJQgCEiECM51IftI3OS2DeRlQ3IkfBjbeaYwfoFHqAa4_pYvXhYAQnP2JkgYaCwoJBAAAAACRAgABGgsKCQQAAAAAkQIAAhoLCgkEAAAAAJECAAMqRzBFAiEAjMd_0mXjPJVRdLn0ligEiy1ypjlayzDwup2QU2-hpdUCIH-o5bq46N3umISo4kSwmQIo41RrWptoSGMqvZJHluV2")
check s2.fromURI("spr:CiUIAhIhAmvtpc_d8c2JEw57W7YJK6wj20oES_hHMoqgMQ3RI6RFEgIDARpWCicAJQgCEiECa-2lz93xzYkTDntbtgkrrCPbSgRL-EcyiqAxDdEjpEUQnP2JkgYaCwoJBAAAAACRAgABGgsKCQQAAAAAkQIAAhoLCgkEAAAAAJECAAMqRjBEAiA9QbGnjF5tmMm08_yyE9wWrk3lChyHFaspxRav5kiLTgIgWEHQnpKz0vGtcse8Bm5WHatXMgiG8_u_Jy0s8XMsolk")
let
total = 0x1'u32
n = NodesMessage(total: total, sprs: @[s1, s2])
reqId = RequestId(id: @[1.byte])
let encoded = encodeMessage(n, reqId)
check byteutils.toHex(encoded) == "04f9018f0101f9018ab8c30a24080112205aed986794d0556d605847ef4228d51ea481b6309bc03b7d569a49bd8a475fdc120203011a550a260024080112205aed986794d0556d605847ef4228d51ea481b6309bc03b7d569a49bd8a475fdc10acf5c591061a0b0a090400000000910200011a0b0a090400000000910200021a0b0a090400000000910200032a402390af4379673cdaecddd4012346bfa4b10c91890eef60657801fdc50048b42601ef302f1109076a9f6c01631bc17323bca355cc1042821b4d40519ac9942903b8c30a2408011220b96ddc34a9e5bd1b0956657475d805326bdf0102e2d337f8b65b7fe96180d376120203011a550a26002408011220b96ddc34a9e5bd1b0956657475d805326bdf0102e2d337f8b65b7fe96180d37610acf5c591061a0b0a090400000000910200011a0b0a090400000000910200021a0b0a090400000000910200032a40b863d3640c77feef52cebd9a27d0d8e6cb3427926e089402463033d2d1c6d2808177fb6704082e664625c78dc1d6948e613e9c54511c45c88812a7b5d7d6320b"
check byteutils.toHex(encoded) == "04f901a00101f9019bb8cc0a250802122102339d487ed237392d83791950dc891f0636de698c1fa051ea01ae3fa58bd78580120203011a560a2700250802122102339d487ed237392d83791950dc891f0636de698c1fa051ea01ae3fa58bd78580109cfd8992061a0b0a090400000000910200011a0b0a090400000000910200021a0b0a090400000000910200032a4730450221008cc77fd265e33c955174b9f49628048b2d72a6395acb30f0ba9d90536fa1a5d502207fa8e5bab8e8ddee9884a8e244b0990228e3546b5a9b6848632abd924796e576b8cb0a2508021221026beda5cfddf1cd89130e7b5bb6092bac23db4a044bf847328aa0310dd123a445120203011a560a27002508021221026beda5cfddf1cd89130e7b5bb6092bac23db4a044bf847328aa0310dd123a445109cfd8992061a0b0a090400000000910200011a0b0a090400000000910200021a0b0a090400000000910200032a46304402203d41b1a78c5e6d98c9b4f3fcb213dc16ae4de50a1c8715ab29c516afe6488b4e02205841d09e92b3d2f1ad72c7bc066e561dab57320886f3fbbf272d2cf1732ca259"
let decoded = decodeMessage(encoded)
check decoded.isOk()
@ -177,9 +184,9 @@ suite "Discovery v5.1 Cryptographic Primitives Test Vectors":
sharedSecret = "0x033b11a2a1f214567e1537ce5e509ffd9b21373247f2a3ff6841f4976f53165e7e"
let
pub = keys.PublicKey.fromHex(publicKey)[]
priv = keys.PrivateKey.fromHex(secretKey)[]
eph = ecdhRawFull(priv, pub)
pub = PublicKey.fromHex(publicKey).expect("Valid public key hex")
priv = PrivateKey.fromHex(secretKey).expect("Valid private key hex")
eph = ecdhRaw(priv, pub).expect("Valid public and private keys")
check:
eph.data == hexToSeqByte(sharedSecret)
@ -198,9 +205,10 @@ suite "Discovery v5.1 Cryptographic Primitives Test Vectors":
let secrets = deriveKeys(
NodeId.fromHex(nodeIdA),
NodeId.fromHex(nodeIdB),
keys.PrivateKey.fromHex(ephemeralKey)[],
keys.PublicKey.fromHex(destPubkey)[],
hexToSeqByte(challengeData))
PrivateKey.fromHex(ephemeralKey).expect("Valid private key hex"),
PublicKey.fromHex(destPubkey).expect("Valid public key hex"),
hexToSeqByte(challengeData)
).expect("Valid key structure")
check:
secrets.initiatorKey == hexToByteArray[aesKeySize](initiatorKey)
@ -214,20 +222,23 @@ suite "Discovery v5.1 Cryptographic Primitives Test Vectors":
ephemeralPubkey = "0x039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231"
nodeIdB = "0xbbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9"
# expected output
idSignature = "0x94852a1e2318c4e5e9d422c98eaf19d1d90d876b29cd06ca7cb7546d0fff7b484fe86c09a064fe72bdbef73ba8e9c34df0cd2b53e9d65528c2c7f336d5dfc6e6"
idSignature = "0xdb0ae930a460fd767cb26a519221e6be5edc3501865406d8af6d215f87ebf35b07563d891082d97147d9499f49bb86ee399f57367af1b866674f9e54760e3a21"
let
privKey = keys.PrivateKey.fromHex(staticKey)[]
privKey = PrivateKey.fromHex(staticKey).expect("Valid private key hex")
signature = createIdSignature(
privKey,
hexToSeqByte(challengeData),
hexToSeqByte(ephemeralPubkey),
NodeId.fromHex(nodeIdB))
NodeId.fromHex(nodeIdB)
).expect("Valid signature data")
libp2pSig = SkSignature.init(signature.data).expect("Valid sig data")
skSig = secp256k1.SkSignature(libp2pSig)
check:
signature.toRaw() == hexToByteArray[64](idSignature)
skSig.toRaw() == hexToByteArray[64](idSignature)
verifyIdSignature(signature, hexToSeqByte(challengeData),
hexToSeqByte(ephemeralPubkey), NodeId.fromHex(nodeIdB),
privKey.toPublicKey())
privKey.getPublicKey.expect("Valid private key for public key"))
test "Encryption/Decryption":
const
@ -249,17 +260,19 @@ suite "Discovery v5.1 Cryptographic Primitives Test Vectors":
# https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md#packet-encodings
suite "Discovery v5.1 Packet Encodings Test Vectors":
const
nodeAKey = "0xeef77acb6c6a6eebc5b363a475ac583ec7eccdb42b6481424c60f59aa326547f"
nodeBKey = "0x66fb62bfbd66b9177a138c1e5cddbe4f7c30c343e94e68df8769459cb1cde628"
nodeAKey = "0xfe5f08c842aa946659b266ce68faa5d2fd982634594dccdf7f916e3fcf0541a3"
nodeBKey = "0x00064765abe9a4e63b068b5af99c26c61c8ade9bfdae6494873b137ec8152578"
var
codecA, codecB: Codec
nodeA, nodeB: Node
privKeyA, privKeyB: keys.PrivateKey
privKeyA, privKeyB: PrivateKey
setup:
privKeyA = keys.PrivateKey.fromHex(nodeAKey)[] # sender -> encode
privKeyB = keys.PrivateKey.fromHex(nodeBKey)[] # receive -> decode
# sender -> encode
privKeyA = PrivateKey.fromHex(nodeAKey).expect("Valid private key hex")
# receive -> decode
privKeyB = PrivateKey.fromHex(nodeBKey).expect("Valid private key hex")
let
enrRecA = SignedPeerRecord.init(1, privKeyA,
@ -284,9 +297,9 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
pingSprSeq = 2'u64
encodedPacket =
"00000000000000000000000000000000088b3d4342774649325f313964a39e55" &
"ea96c005ad52be8c7560413a7008f16c9e6d2f43bbea8814a546b7409ce783d3" &
"4c4f53245d08dab84102ed931f66d1492acb308fa1c6715b9d139b81acbdcc"
"000000000000000000000000000000003788c1e1079e89374c4beac74d76364d" &
"bd9e8cd1847adc2f49fbacc6862425583586c023b19b6fdd1d836777ee39fee8" &
"7afd279a5fe4ffdded6d1a6d388217da82d38761b60b0c6e9dd94a8713bc5d"
let dummyKey = "0x00000000000000000000000000000001" # of no importance
codecA.sessions.store(nodeB.id, nodeB.address.get(),
@ -311,8 +324,8 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
whoareyouSprSeq = 0
encodedPacket =
"00000000000000000000000000000000088b3d434277464933a1ccc59f5967ad" &
"1d6035f15e528627dde75cd68292f9e6c27d6b66c8100a873fcbaed4e16b8d"
"000000000000000000000000000000003788c1e1079e89374d4beac74d76364d" &
"bd9e8cd1847ae48d2f96e595c7a904454033dd25eaefc076a4537f17e8a43a"
let decoded = codecB.decodePacket(nodeA.address.get(),
hexToSeqByte(encodedPacket))
@ -341,13 +354,13 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
whoareyouSprSeq = 1'u64
encodedPacket =
"00000000000000000000000000000000088b3d4342774649305f313964a39e55" &
"ea96c005ad521d8c7560413a7008f16c9e6d2f43bbea8814a546b7409ce783d3" &
"4c4f53245d08da4bb252012b2cba3f4f374a90a75cff91f142fa9be3e0a5f3ef" &
"268ccb9065aeecfd67a999e7fdc137e062b2ec4a0eb92947f0d9a74bfbf44dfb" &
"a776b21301f8b65efd5796706adff216ab862a9186875f9494150c4ae06fa4d1" &
"f0396c93f215fa4ef524f1eadf5f0f4126b79336671cbcf7a885b1f8bd2a5d83" &
"9cf8"
"000000000000000000000000000000003788c1e1079e89374e4beac74d76364d" &
"bd9e8cd1847a712f49fbacc6862425583586c023b19b6fdd1d836777ee39fee8" &
"7afd279a5fe4ff441af3b17ec968350f37edbda9e0ba8ac0fd2617ef67a1e362" &
"5ea8eb284a3ca85f7ef976ccf2e87932ffeada775849d7aca378033b7a75dbe8" &
"7cc1767123bb7d7e5d96b5d6ad7c26cb55f6160b250d042ef1b9e6000191ce4e" &
"a93234ca3de051518684902e70e6a47eb8f0c2efeca8e42d2ea7f5bd1f27c12d" &
"ae3c579ddcef630659089c99"
let
whoareyouData = WhoareyouData(
@ -355,7 +368,9 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
idNonce: hexToByteArray[idNonceSize](whoareyouIdNonce),
recordSeq: whoareyouSprSeq,
challengeData: hexToSeqByte(whoareyouChallengeData))
pubkey = some(privKeyA.toPublicKey())
pubkey = privKeyA.getPublicKey
.expect("Valid private key for public key")
.some
challenge = Challenge(whoareyouData: whoareyouData, pubkey: pubkey)
key = HandshakeKey(nodeId: nodeA.id, address: nodeA.address.get())
@ -387,18 +402,18 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
whoareyouSprSeq = 0'u64
encodedPacket =
"2746cce362989b5d7e2496490b25f952e9198c524b06c7e9e069c5f7c8d2c84b" &
"943322ac741826023cb35086eee94baaf98f81217c3dbcb022afb1464555b144" &
"69b49cb19fe1f3459b4bbb03a52fc588bcc69d7ff50842ee6c3fc3ffd58d425f" &
"e8c7bec9777fcb15d9c9e37c4aa3b226274f6631526d6d2127f39e1daff277fd" &
"e867a8222ae509922d9e94456f7cbde14c1788894708713789b28b307ac983c8" &
"31ebc00113ded4011af2bfa06078c8f0a3401e8c034b3ae5506fb002a0355bf1" &
"48b19022bae8b088a0c0bdc22dc3d5ce4a6c5ad700a3f8a82be214c2bef98afe" &
"2dbf4ffaaf816602d470dcfe8184b1db8d873d8813984f86b6350ff5d00d466c" &
"06de59f1797ad01a68bb9c07b9cb56e6989ab0e94d32c60e435a48aa7c89d602" &
"3863bd1605a33f895903657fe72f79ded24b366486a1c02a893702ec7d299ea8" &
"7afe0bb771fad244b8d4d0bd7bf4dc833a17c4db2f926eb7614788308a6f98af" &
"9a0e20bd75af75175645058702122b15"
"000000000000000000000000000000003788c1e1079e89374e4beac74d76364d" &
"bd9e8cd1847bc02f49fbacc6862425583586c023b19b6fdd1d836777ee39fee8" &
"7afd279a5fe4ff451af3b07ec8407cedec19c57a8460e08d3d8a908f78261170" &
"68196e7df56279e7493fbb2076025b395dde6ffeecc45daa59def06c9be97b1f" &
"95636fb8f16887cf13b4a8cca0bcaf805fe62529ad86c59204e73917cf183d19" &
"847617448722cc8c0eea80b68653e858eff5d250abbd55315db21fac1485db8f" &
"deaadba582d43c88f0b25512a5fd8395bd2f9519362d29cceb29028de04e0076" &
"4f6aece318e26e2d123888e484cb1c0ce37ecfee42ced9a811966bae40f40e9d" &
"4b46e27c388330304409a405b6455547661361d2129aa7bed4ff26f68d53532d" &
"cb6bae00506a7c5161b0652afcbf2416e97116bdcf9a7a548d6d8b5b0ab2ed0e" &
"b7a737afc0dbf65f32fd22c27cb17ebfe3c0d43e9bf45cfd24170c9fea348b10" &
"1207010ad51e28040b46770c1e96e22e7c552a6f1a62b4e29f8c99"
let
whoareyouData = WhoareyouData(
@ -406,7 +421,7 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
idNonce: hexToByteArray[idNonceSize](whoareyouIdNonce),
recordSeq: whoareyouSprSeq,
challengeData: hexToSeqByte(whoareyouChallengeData))
pubkey = none(keys.PublicKey)
pubkey = none(PublicKey)
challenge = Challenge(whoareyouData: whoareyouData, pubkey: pubkey)
key = HandshakeKey(nodeId: nodeA.id, address: nodeA.address.get())
@ -426,7 +441,7 @@ suite "Discovery v5.1 Packet Encodings Test Vectors":
hexToSeqByte(encodedPacket & "00")).isErr()
suite "Discovery v5.1 Additional Encode/Decode":
var rng = keys.newRng()
var rng = newRng()
test "Encryption/Decryption":
let
@ -467,8 +482,7 @@ suite "Discovery v5.1 Additional Encode/Decode":
var nonce: AESGCMNonce
brHmacDrbgGenerate(rng[], nonce)
let
privKey = keys.PrivateKey.random(rng[])
nodeId = privKey.toPublicKey().toNodeId()
nodeId = NodeId.example(rng)
authdata = newSeq[byte](32)
staticHeader = encodeStaticHeader(Flag.OrdinaryMessage, nonce,
authdata.len())
@ -486,11 +500,11 @@ suite "Discovery v5.1 Additional Encode/Decode":
var
codecA, codecB: Codec
nodeA, nodeB: Node
privKeyA, privKeyB: keys.PrivateKey
privKeyA, privKeyB: PrivateKey
setup:
privKeyA = keys.PrivateKey.random(rng[]) # sender -> encode
privKeyB = keys.PrivateKey.random(rng[]) # receiver -> decode
privKeyA = PrivateKey.example(rng) # sender -> encode
privKeyB = PrivateKey.example(rng) # receiver -> decode
let
enrRecA = SignedPeerRecord.init(1, privKeyA,
@ -528,7 +542,7 @@ suite "Discovery v5.1 Additional Encode/Decode":
let recordSeq = 0'u64
let data = encodeWhoareyouPacket(rng[], codecA, nodeB.id,
nodeB.address.get(), requestNonce, recordSeq, none(keys.PublicKey))
nodeB.address.get(), requestNonce, recordSeq, none(PublicKey))
let decoded = codecB.decodePacket(nodeA.address.get(), data)
@ -551,7 +565,9 @@ suite "Discovery v5.1 Additional Encode/Decode":
m = PingMessage(sprSeq: 0)
reqId = RequestId.init(rng[])
message = encodeMessage(m, reqId)
pubkey = some(privKeyA.toPublicKey())
pubkey = privKeyA.getPublicKey
.expect("Valid private key for public key")
.some
# Encode/decode whoareyou packet to get the handshake stored and the
# whoareyou data returned. It's either that or construct the header for the
@ -561,9 +577,12 @@ suite "Discovery v5.1 Additional Encode/Decode":
nodeA.address.get(), requestNonce, recordSeq, pubkey)
decodedDummy = codecA.decodePacket(nodeB.address.get(), encodedDummy)
let data = encodeHandshakePacket(rng[], codecA, nodeB.id,
nodeB.address.get(), message, decodedDummy[].whoareyou,
privKeyB.toPublicKey())
let
pubKeyB = privKeyB.getPublicKey.expect("Valid private key for public key")
data = encodeHandshakePacket(rng[], codecA, nodeB.id,
nodeB.address.get(), message, decodedDummy[].whoareyou,
pubKeyB
).expect("Valid handshake packet data")
let decoded = codecB.decodePacket(nodeA.address.get(), data)
@ -582,7 +601,7 @@ suite "Discovery v5.1 Additional Encode/Decode":
m = PingMessage(sprSeq: 0)
reqId = RequestId.init(rng[])
message = encodeMessage(m, reqId)
pubkey = none(keys.PublicKey)
pubkey = none(PublicKey)
# Encode/decode whoareyou packet to get the handshake stored and the
# whoareyou data returned. It's either that or construct the header for the
@ -592,9 +611,12 @@ suite "Discovery v5.1 Additional Encode/Decode":
nodeA.address.get(), requestNonce, recordSeq, pubkey)
decodedDummy = codecA.decodePacket(nodeB.address.get(), encodedDummy)
let encoded = encodeHandshakePacket(rng[], codecA, nodeB.id,
nodeB.address.get(), message, decodedDummy[].whoareyou,
privKeyB.toPublicKey())
let
pubKeyB = privKeyB.getPublicKey.expect("Valid private key for public key")
encoded = encodeHandshakePacket(rng[], codecA, nodeB.id,
nodeB.address.get(), message, decodedDummy[].whoareyou,
pubKeyB
).expect("Valid handshake packet data")
let decoded = codecB.decodePacket(nodeA.address.get(), encoded)