use bearssl rng throughout (#265)

* use bearssl rng throughout

* seeder can fail

* imports and exports

* modules, sigh

* one more try

* move var

* even fewer thread vars

* remove out-of-date genrated files
This commit is contained in:
Jacek Sieka 2020-07-07 10:56:26 +02:00 committed by GitHub
parent 4f533eb5e6
commit 484fbcab1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 433 additions and 1252 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ build/
*.la *.la
*.exe *.exe
*.dll *.dll
*.generated.nim

View File

@ -15,13 +15,13 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import import
secp256k1, secp256k1, bearssl,
nimcrypto/hash, nimcrypto/keccak, nimcrypto/hash, nimcrypto/keccak,
stew/[byteutils, objects, results], strformat stew/[byteutils, objects, results], strformat
from nimcrypto/utils import burnMem from nimcrypto/utils import burnMem
export secp256k1, results export secp256k1, results, bearssl
const const
KeyLength* = SkEcdhRawSecretSize - 1 KeyLength* = SkEcdhRawSecretSize - 1
@ -46,12 +46,31 @@ type
SharedSecret* = object SharedSecret* = object
data*: array[KeyLength, byte] data*: array[KeyLength, byte]
KeyPair* = object KeyPair* = distinct SkKeyPair
seckey*: PrivateKey
pubkey*: PublicKey
proc random*(T: type PrivateKey): SkResult[T] = template pubkey*(v: KeyPair): PublicKey = PublicKey(SkKeyPair(v).pubkey)
SkSecretKey.random().mapConvert(T) template seckey*(v: KeyPair): PrivateKey = PrivateKey(SkKeyPair(v).seckey)
proc newRng*(): ref BrHmacDrbgContext =
# You should only create one instance of the RNG per application / library
# Ref is used so that it can be shared between components
# TODO consider moving to bearssl
var seeder = brPrngSeederSystem(nil)
if seeder == nil:
return nil
var rng = (ref BrHmacDrbgContext)()
brHmacDrbgInit(addr rng[], addr sha256Vtable, nil, 0)
if seeder(addr rng.vtable) == 0:
return nil
rng
proc random*(T: type PrivateKey, rng: var BrHmacDrbgContext): T =
let rngPtr = unsafeAddr rng # doesn't escape
proc callRng(data: var openArray[byte]) =
brHmacDrbgGenerate(rngPtr[], data)
T(SkSecretKey.random(callRng))
func fromRaw*(T: type PrivateKey, data: openArray[byte]): SkResult[T] = func fromRaw*(T: type PrivateKey, data: openArray[byte]): SkResult[T] =
SkSecretKey.fromRaw(data).mapConvert(T) SkSecretKey.fromRaw(data).mapConvert(T)
@ -87,12 +106,16 @@ func toRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] =
func toRawCompressed*(pubkey: PublicKey): array[33, byte] {.borrow.} func toRawCompressed*(pubkey: PublicKey): array[33, byte] {.borrow.}
proc random*(T: type KeyPair): SkResult[T] = proc random*(T: type KeyPair, rng: var BrHmacDrbgContext): T =
let tmp = ? SkKeypair.random() let seckey = SkSecretKey(PrivateKey.random(rng))
ok(T(seckey: PrivateKey(tmp.seckey), pubkey: PublicKey(tmp.pubkey))) KeyPair(SkKeyPair(
seckey: seckey,
pubkey: seckey.toPublicKey()
))
func toKeyPair*(seckey: PrivateKey): KeyPair = func toKeyPair*(seckey: PrivateKey): KeyPair =
KeyPair(seckey: seckey, pubkey: seckey.toPublicKey()) KeyPair(SkKeyPair(
seckey: SkSecretKey(seckey), pubkey: SkSecretKey(seckey).toPublicKey()))
func fromRaw*(T: type Signature, data: openArray[byte]): SkResult[T] = func fromRaw*(T: type Signature, data: openArray[byte]): SkResult[T] =
SkRecoverableSignature.fromRaw(data).mapConvert(T) SkRecoverableSignature.fromRaw(data).mapConvert(T)
@ -192,28 +215,28 @@ func sign*(seckey: PrivateKey, msg: SkMessage): Signature =
func sign*(seckey: PrivateKey, msg: openArray[byte]): Signature = func sign*(seckey: PrivateKey, msg: openArray[byte]): Signature =
let hash = keccak256.digest(msg) let hash = keccak256.digest(msg)
sign(seckey, hash) sign(seckey, SkMessage(hash.data))
func signNR*(seckey: PrivateKey, msg: SkMessage): SignatureNR = func signNR*(seckey: PrivateKey, msg: SkMessage): SignatureNR =
SignatureNR(sign(SkSecretKey(seckey), msg)) SignatureNR(sign(SkSecretKey(seckey), msg))
func signNR*(seckey: PrivateKey, msg: openArray[byte]): SignatureNR = func signNR*(seckey: PrivateKey, msg: openArray[byte]): SignatureNR =
let hash = keccak256.digest(msg) let hash = keccak256.digest(msg)
signNR(seckey, hash) signNR(seckey, SkMessage(hash.data))
func recover*(sig: Signature, msg: SkMessage): SkResult[PublicKey] = func recover*(sig: Signature, msg: SkMessage): SkResult[PublicKey] =
recover(SkRecoverableSignature(sig), msg).mapConvert(PublicKey) recover(SkRecoverableSignature(sig), msg).mapConvert(PublicKey)
func recover*(sig: Signature, msg: openArray[byte]): SkResult[PublicKey] = func recover*(sig: Signature, msg: openArray[byte]): SkResult[PublicKey] =
let hash = keccak256.digest(msg) let hash = keccak256.digest(msg)
recover(sig, hash) recover(sig, SkMessage(hash.data))
func verify*(sig: SignatureNR, msg: SkMessage, key: PublicKey): bool = func verify*(sig: SignatureNR, msg: SkMessage, key: PublicKey): bool =
verify(SkSignature(sig), msg, SkPublicKey(key)) verify(SkSignature(sig), msg, SkPublicKey(key))
func verify*(sig: SignatureNR, msg: openArray[byte], key: PublicKey): bool = func verify*(sig: SignatureNR, msg: openArray[byte], key: PublicKey): bool =
let hash = keccak256.digest(msg) let hash = keccak256.digest(msg)
verify(sig, hash, key) verify(sig, SkMessage(hash.data), key)
func ecdhRaw*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret = func ecdhRaw*(seckey: PrivateKey, pubkey: PublicKey): SharedSecret =
let tmp = ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey)) let tmp = ecdhRaw(SkSecretKey(seckey), SkPublicKey(pubkey))

View File

@ -9,7 +9,7 @@
# #
import import
tables, algorithm, random, tables, algorithm, random, bearssl,
chronos, chronos/timer, chronicles, chronos, chronos/timer, chronicles,
eth/keys, eth/common/eth_types, eth/keys, eth/common/eth_types,
eth/p2p/[kademlia, discovery, enode, peer_pool, rlpx], eth/p2p/[kademlia, discovery, enode, peer_pool, rlpx],
@ -38,7 +38,12 @@ proc newEthereumNode*(keys: KeyPair,
clientId = "nim-eth-p2p/0.2.0", # TODO: read this value from nimble somehow clientId = "nim-eth-p2p/0.2.0", # TODO: read this value from nimble somehow
addAllCapabilities = true, addAllCapabilities = true,
useCompression: bool = false, useCompression: bool = false,
minPeers = 10): EthereumNode = minPeers = 10,
rng = newRng()): EthereumNode =
if rng == nil: # newRng could fail
raise (ref CatchableError)(msg: "Cannot initialize RNG")
new result new result
result.keys = keys result.keys = keys
result.networkId = networkId result.networkId = networkId
@ -47,6 +52,7 @@ proc newEthereumNode*(keys: KeyPair,
result.capabilities.newSeq 0 result.capabilities.newSeq 0
result.address = address result.address = address
result.connectionState = ConnectionState.None result.connectionState = ConnectionState.None
result.rng = rng
when useSnappy: when useSnappy:
result.protocolVersion = if useCompression: devp2pSnappyVersion result.protocolVersion = if useCompression: devp2pSnappyVersion

View File

@ -12,7 +12,7 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import eth/[keys, rlp], nimcrypto import eth/[keys, rlp], nimcrypto/[rijndael, keccak, utils], bearssl
import ecies import ecies
import stew/[byteutils, endians2, objects, results] import stew/[byteutils, endians2, objects, results]
@ -54,7 +54,6 @@ type
Eip8 ## Flag indicates that EIP-8 handshake is used Eip8 ## Flag indicates that EIP-8 handshake is used
AuthError* = enum AuthError* = enum
RandomError = "auth: could not obtain random data"
EcdhError = "auth: ECDH shared secret could not be calculated" EcdhError = "auth: ECDH shared secret could not be calculated"
BufferOverrun = "auth: buffer overrun" BufferOverrun = "auth: buffer overrun"
SignatureError = "auth: signature could not be obtained" SignatureError = "auth: signature could not be obtained"
@ -95,7 +94,8 @@ proc mapErrTo[T, E](r: Result[T, E], v: static AuthError): AuthResult[T] =
r.mapErr(proc (e: E): AuthError = v) r.mapErr(proc (e: E): AuthError = v)
proc tryInit*( proc tryInit*(
T: type Handshake, host: KeyPair, flags: set[HandshakeFlag] = {Initiator}, T: type Handshake, rng: var BrHmacDrbgContext, host: KeyPair,
flags: set[HandshakeFlag] = {Initiator},
version: uint8 = SupportedRlpxVersion): AuthResult[T] = version: uint8 = SupportedRlpxVersion): AuthResult[T] =
## Create new `Handshake` object. ## Create new `Handshake` object.
@ -103,16 +103,14 @@ proc tryInit*(
initiatorNonce: Nonce initiatorNonce: Nonce
responderNonce: Nonce responderNonce: Nonce
expectedLength: int expectedLength: int
ephemeral = ? KeyPair.random().mapErrTo(RandomError) ephemeral = KeyPair.random(rng)
if Initiator in flags: if Initiator in flags:
expectedLength = AckMessageV4Length expectedLength = AckMessageV4Length
if randomBytes(initiatorNonce) != len(initiatorNonce): brHmacDrbgGenerate(rng, initiatorNonce)
return err(RandomError)
else: else:
expectedLength = AuthMessageV4Length expectedLength = AuthMessageV4Length
if randomBytes(responderNonce) != len(responderNonce): brHmacDrbgGenerate(rng, responderNonce)
return err(RandomError)
return ok(T( return ok(T(
version: version, version: version,
@ -125,6 +123,7 @@ proc tryInit*(
)) ))
proc authMessagePreEIP8(h: var Handshake, proc authMessagePreEIP8(h: var Handshake,
rng: var BrHmacDrbgContext,
pubkey: PublicKey, pubkey: PublicKey,
output: var openarray[byte], output: var openarray[byte],
outlen: var int, outlen: var int,
@ -137,12 +136,11 @@ proc authMessagePreEIP8(h: var Handshake,
let header = cast[ptr AuthMessageV4](addr buffer[0]) let header = cast[ptr AuthMessageV4](addr buffer[0])
var secret = ecdhRaw(h.host.seckey, pubkey) var secret = ecdhRaw(h.host.seckey, pubkey)
let xornonce = secret.data xor h.initiatorNonce secret.data = secret.data xor h.initiatorNonce
let signature = sign(h.ephemeral.seckey, SkMessage(secret.data))
secret.clear() secret.clear()
let signature = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
header.signature = signature.toRaw() header.signature = signature.toRaw()
header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data
@ -152,7 +150,7 @@ proc authMessagePreEIP8(h: var Handshake,
if encrypt: if encrypt:
if len(output) < AuthMessageV4Length: if len(output) < AuthMessageV4Length:
return err(BufferOverrun) return err(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr: if eciesEncrypt(rng, buffer, output, h.remoteHPubkey).isErr:
return err(EciesError) return err(EciesError)
outlen = AuthMessageV4Length outlen = AuthMessageV4Length
else: else:
@ -164,6 +162,7 @@ proc authMessagePreEIP8(h: var Handshake,
ok() ok()
proc authMessageEIP8(h: var Handshake, proc authMessageEIP8(h: var Handshake,
rng: var BrHmacDrbgContext,
pubkey: PublicKey, pubkey: PublicKey,
output: var openarray[byte], output: var openarray[byte],
outlen: var int, outlen: var int,
@ -172,50 +171,49 @@ proc authMessageEIP8(h: var Handshake,
## Create EIP8 authentication message. ## Create EIP8 authentication message.
var var
buffer: array[PlainAuthMessageMaxEIP8, byte] buffer: array[PlainAuthMessageMaxEIP8, byte]
padsize: byte padsize: array[1, byte]
doAssert(EIP8 in h.flags) doAssert(EIP8 in h.flags)
outlen = 0 outlen = 0
var
secret = ecdhRaw(h.host.seckey, pubkey)
xornonce = secret.data xor h.initiatorNonce
var secret = ecdhRaw(h.host.seckey, pubkey)
secret.data = secret.data xor h.initiatorNonce
let signature = sign(h.ephemeral.seckey, SkMessage(secret.data))
secret.clear() secret.clear()
let signature = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
var payload = rlp.encodeList(signature.toRaw(), var payload = rlp.encodeList(signature.toRaw(),
h.host.pubkey.toRaw(), h.host.pubkey.toRaw(),
h.initiatorNonce, h.initiatorNonce,
[byte(h.version)]) [byte(h.version)])
doAssert(len(payload) == PlainAuthMessageEIP8Length) doAssert(len(payload) == PlainAuthMessageEIP8Length)
let pencsize = eciesEncryptedLength(len(payload)) let
pencsize = eciesEncryptedLength(len(payload))
while true: while true:
if randomBytes(addr padsize, 1) != 1: brHmacDrbgGenerate(rng, padsize)
return err(RandomError) if int(padsize[0]) > (AuthMessageV4Length - (pencsize + 2)):
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
break break
# It is possible to make packet size constant by uncommenting this line # It is possible to make packet size constant by uncommenting this line
# padsize = 24 # padsize = 24
let wosize = pencsize + int(padsize) let wosize = pencsize + int(padsize[0])
let fullsize = wosize + 2 let fullsize = wosize + 2
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length, brHmacDrbgGenerate(
int(padsize))) != int(padsize): rng, toa(buffer, PlainAuthMessageEIP8Length, int(padsize[0])))
return err(RandomError)
if encrypt: if encrypt:
copyMem(addr buffer[0], addr payload[0], len(payload)) copyMem(addr buffer[0], addr payload[0], len(payload))
if len(output) < fullsize: if len(output) < fullsize:
return err(BufferOverrun) return err(BufferOverrun)
let wosizeBE = uint16(wosize).toBytesBE() let wosizeBE = uint16(wosize).toBytesBE()
output[0..<2] = wosizeBE output[0..<2] = wosizeBE
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)), if eciesEncrypt(rng, toa(buffer, 0, len(payload) + int(padsize[0])),
toa(output, 2, wosize), pubkey, toa(output, 2, wosize), pubkey,
toa(output, 0, 2)).isErr: toa(output, 0, 2)).isErr:
return err(EciesError) return err(EciesError)
outlen = fullsize outlen = fullsize
else: else:
let plainsize = len(payload) + int(padsize) let plainsize = len(payload) + int(padsize[0])
if len(output) < plainsize: if len(output) < plainsize:
return err(BufferOverrun) return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], plainsize) copyMem(addr output[0], addr buffer[0], plainsize)
@ -224,6 +222,7 @@ proc authMessageEIP8(h: var Handshake,
ok() ok()
proc ackMessagePreEIP8(h: var Handshake, proc ackMessagePreEIP8(h: var Handshake,
rng: var BrHmacDrbgContext,
output: var openarray[byte], output: var openarray[byte],
outlen: var int, outlen: var int,
flag: byte = 0, flag: byte = 0,
@ -238,7 +237,7 @@ proc ackMessagePreEIP8(h: var Handshake,
if encrypt: if encrypt:
if len(output) < AckMessageV4Length: if len(output) < AckMessageV4Length:
return err(BufferOverrun) return err(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr: if eciesEncrypt(rng, buffer, output, h.remoteHPubkey).isErr:
return err(EciesError) return err(EciesError)
outlen = AckMessageV4Length outlen = AckMessageV4Length
else: else:
@ -250,6 +249,7 @@ proc ackMessagePreEIP8(h: var Handshake,
ok() ok()
proc ackMessageEIP8(h: var Handshake, proc ackMessageEIP8(h: var Handshake,
rng: var BrHmacDrbgContext,
output: var openarray[byte], output: var openarray[byte],
outlen: var int, outlen: var int,
flag: byte = 0, flag: byte = 0,
@ -257,7 +257,7 @@ proc ackMessageEIP8(h: var Handshake,
## Create EIP8 authentication ack message. ## Create EIP8 authentication ack message.
var var
buffer: array[PlainAckMessageMaxEIP8, byte] buffer: array[PlainAckMessageMaxEIP8, byte]
padsize: byte padsize: array[1, byte]
doAssert(EIP8 in h.flags) doAssert(EIP8 in h.flags)
var payload = rlp.encodeList(h.ephemeral.pubkey.toRaw(), var payload = rlp.encodeList(h.ephemeral.pubkey.toRaw(),
h.responderNonce, h.responderNonce,
@ -266,30 +266,29 @@ proc ackMessageEIP8(h: var Handshake,
outlen = 0 outlen = 0
let pencsize = eciesEncryptedLength(len(payload)) let pencsize = eciesEncryptedLength(len(payload))
while true: while true:
if randomBytes(addr padsize, 1) != 1: brHmacDrbgGenerate(rng, padsize)
return err(RandomError) if int(padsize[0]) > (AckMessageV4Length - (pencsize + 2)):
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
break break
# It is possible to make packet size constant by uncommenting this line # It is possible to make packet size constant by uncommenting this line
# padsize = 0 # padsize = 0
let wosize = pencsize + int(padsize) let wosize = pencsize + int(padsize[0])
let fullsize = wosize + 2 let fullsize = wosize + 2
if int(padsize) > 0: if int(padsize[0]) > 0:
if randomBytes(toa(buffer, PlainAckMessageEIP8Length, brHmacDrbgGenerate(
int(padsize))) != int(padsize): rng, toa(buffer, PlainAckMessageEIP8Length, int(padsize[0])))
return err(RandomError)
copyMem(addr buffer[0], addr payload[0], len(payload)) copyMem(addr buffer[0], addr payload[0], len(payload))
if encrypt: if encrypt:
if len(output) < fullsize: if len(output) < fullsize:
return err(BufferOverrun) return err(BufferOverrun)
output[0..<2] = uint16(wosize).toBytesBE() output[0..<2] = uint16(wosize).toBytesBE()
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)), if eciesEncrypt(rng, toa(buffer, 0, len(payload) + int(padsize[0])),
toa(output, 2, wosize), h.remoteHPubkey, toa(output, 2, wosize), h.remoteHPubkey,
toa(output, 0, 2)).isErr: toa(output, 0, 2)).isErr:
return err(EciesError) return err(EciesError)
outlen = fullsize outlen = fullsize
else: else:
let plainsize = len(payload) + int(padsize) let plainsize = len(payload) + int(padsize[0])
if len(output) < plainsize: if len(output) < plainsize:
return err(BufferOverrun) return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], plainsize) copyMem(addr output[0], addr buffer[0], plainsize)
@ -311,26 +310,28 @@ template ackSize*(h: Handshake, encrypt: bool = true): int =
else: else:
if encrypt: (AckMessageV4Length) else: (PlainAckMessageV4Length) if encrypt: (AckMessageV4Length) else: (PlainAckMessageV4Length)
proc authMessage*(h: var Handshake, pubkey: PublicKey, proc authMessage*(h: var Handshake, rng: var BrHmacDrbgContext,
pubkey: PublicKey,
output: var openarray[byte], output: var openarray[byte],
outlen: var int, flag: byte = 0, outlen: var int, flag: byte = 0,
encrypt: bool = true): AuthResult[void] = encrypt: bool = true): AuthResult[void] =
## Create new AuthMessage for specified `pubkey` and store it inside ## Create new AuthMessage for specified `pubkey` and store it inside
## of `output`, size of generated AuthMessage will stored in `outlen`. ## of `output`, size of generated AuthMessage will stored in `outlen`.
if EIP8 in h.flags: if EIP8 in h.flags:
authMessageEIP8(h, pubkey, output, outlen, flag, encrypt) authMessageEIP8(h, rng, pubkey, output, outlen, flag, encrypt)
else: else:
authMessagePreEIP8(h, pubkey, output, outlen, flag, encrypt) authMessagePreEIP8(h, rng, pubkey, output, outlen, flag, encrypt)
proc ackMessage*(h: var Handshake, output: var openarray[byte], proc ackMessage*(h: var Handshake, rng: var BrHmacDrbgContext,
output: var openarray[byte],
outlen: var int, flag: byte = 0, outlen: var int, flag: byte = 0,
encrypt: bool = true): AuthResult[void] = encrypt: bool = true): AuthResult[void] =
## Create new AckMessage and store it inside of `output`, size of generated ## Create new AckMessage and store it inside of `output`, size of generated
## AckMessage will stored in `outlen`. ## AckMessage will stored in `outlen`.
if EIP8 in h.flags: if EIP8 in h.flags:
ackMessageEIP8(h, output, outlen, flag, encrypt) ackMessageEIP8(h, rng, output, outlen, flag, encrypt)
else: else:
ackMessagePreEIP8(h, output, outlen, flag, encrypt) ackMessagePreEIP8(h, rng, output, outlen, flag, encrypt)
proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void] = proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void] =
## Decodes V4 AuthMessage. ## Decodes V4 AuthMessage.
@ -347,12 +348,12 @@ proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void]
signature = ? Signature.fromRaw(header.signature).mapErrTo(SignatureError) signature = ? Signature.fromRaw(header.signature).mapErrTo(SignatureError)
var secret = ecdhRaw(h.host.seckey, pubkey) var secret = ecdhRaw(h.host.seckey, pubkey)
let xornonce = secret.data xor header.nonce secret.data = secret.data xor header.nonce
var recovered = recover(signature, SkMessage(secret.data))
secret.clear() secret.clear()
h.remoteEPubkey = h.remoteEPubkey = ? recovered.mapErrTo(SignatureError)
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.initiatorNonce = header.nonce h.initiatorNonce = header.nonce
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
@ -392,13 +393,12 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthResult[voi
nonce = toArray(KeyLength, nonceBr) nonce = toArray(KeyLength, nonceBr)
var secret = ecdhRaw(h.host.seckey, pubkey) var secret = ecdhRaw(h.host.seckey, pubkey)
secret.data = secret.data xor nonce
let xornonce = nonce xor secret.data let recovered = recover(signature, SkMessage(secret.data))
secret.clear() secret.clear()
h.remoteEPubkey = h.remoteEPubkey = ? recovered.mapErrTo(SignatureError)
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.initiatorNonce = nonce h.initiatorNonce = nonce
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
h.version = versionBr[0] h.version = versionBr[0]
@ -520,7 +520,7 @@ proc getSecrets*(
mac1 = ctx0.finish() mac1 = ctx0.finish()
secret.macKey = mac1.data secret.macKey = mac1.data
burnMem(shsec) clear(shsec)
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init) # egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)

View File

@ -10,7 +10,7 @@
import import
times, times,
chronos, stint, nimcrypto, chronicles, chronos, stint, nimcrypto/keccak, chronicles, bearssl,
eth/[keys, rlp], eth/[keys, rlp],
kademlia, enode, kademlia, enode,
stew/[objects, results] stew/[objects, results]
@ -155,7 +155,7 @@ proc sendNeighbours*(d: DiscoveryProtocol, node: Node, neighbours: seq[Node]) =
if nodes.len != 0: flush() if nodes.len != 0: flush()
proc newDiscoveryProtocol*(privKey: PrivateKey, address: Address, proc newDiscoveryProtocol*(privKey: PrivateKey, address: Address,
bootstrapNodes: openarray[ENode] bootstrapNodes: openarray[ENode], rng = newRng()
): DiscoveryProtocol = ): DiscoveryProtocol =
result.new() result.new()
result.privKey = privKey result.privKey = privKey
@ -163,7 +163,7 @@ proc newDiscoveryProtocol*(privKey: PrivateKey, address: Address,
result.bootstrapNodes = newSeqOfCap[Node](bootstrapNodes.len) result.bootstrapNodes = newSeqOfCap[Node](bootstrapNodes.len)
for n in bootstrapNodes: result.bootstrapNodes.add(newNode(n)) for n in bootstrapNodes: result.bootstrapNodes.add(newNode(n))
result.thisNode = newNode(privKey.toPublicKey(), address) result.thisNode = newNode(privKey.toPublicKey(), address)
result.kademlia = newKademliaProtocol(result.thisNode, result) result.kademlia = newKademliaProtocol(result.thisNode, result, rng = rng)
proc recvPing(d: DiscoveryProtocol, node: Node, proc recvPing(d: DiscoveryProtocol, node: Node,
msgHash: MDigest[256]) {.inline.} = msgHash: MDigest[256]) {.inline.} =

View File

@ -28,7 +28,7 @@ proc makeKey(id: NodeId, address: Address): array[keySize, byte] =
of IpAddressFamily.IpV4: of IpAddressFamily.IpV4:
result[pos ..< pos+sizeof(address.ip.address_v4)] = address.ip.address_v4 result[pos ..< pos+sizeof(address.ip.address_v4)] = address.ip.address_v4
of IpAddressFamily.IpV6: of IpAddressFamily.IpV6:
result[pos..< pos+sizeof(address.ip.address_v6)] = address.ip.address_v6 result[pos ..< pos+sizeof(address.ip.address_v6)] = address.ip.address_v6
pos.inc(sizeof(address.ip.address_v6)) pos.inc(sizeof(address.ip.address_v6))
result[pos ..< pos+sizeof(address.port)] = toBytes(address.port.uint16) result[pos ..< pos+sizeof(address.port)] = toBytes(address.port.uint16)

View File

@ -1,6 +1,6 @@
import import
std/[tables, options], nimcrypto, stint, chronicles, stew/results, std/[tables, options], nimcrypto, stint, chronicles, stew/results,
types, node, enr, hkdf, eth/[rlp, keys] types, node, enr, hkdf, eth/[rlp, keys], bearssl
export keys export keys
@ -64,7 +64,7 @@ proc idNonceHash(nonce, ephkey: openarray[byte]): MDigest[256] =
proc signIDNonce*(privKey: PrivateKey, idNonce, ephKey: openarray[byte]): proc signIDNonce*(privKey: PrivateKey, idNonce, ephKey: openarray[byte]):
SignatureNR = SignatureNR =
signNR(privKey, idNonceHash(idNonce, ephKey)) signNR(privKey, SkMessage(idNonceHash(idNonce, ephKey).data))
proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey, proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey,
idNonce: openarray[byte]): HandshakeSecrets = idNonce: openarray[byte]): HandshakeSecrets =
@ -89,11 +89,12 @@ proc encryptGCM*(key, nonce, pt, authData: openarray[byte]): seq[byte] =
ectx.getTag(result.toOpenArray(pt.len, result.high)) ectx.getTag(result.toOpenArray(pt.len, result.high))
ectx.clear() ectx.clear()
proc encodeAuthHeader*(c: Codec, proc encodeAuthHeader*(rng: var BrHmacDrbgContext,
c: Codec,
toId: NodeID, toId: NodeID,
nonce: array[gcmNonceSize, byte], nonce: array[gcmNonceSize, byte],
challenge: Whoareyou): challenge: Whoareyou):
EncodeResult[(seq[byte], HandshakeSecrets)] = (seq[byte], HandshakeSecrets) =
var resp = AuthResponse(version: 5) var resp = AuthResponse(version: 5)
let ln = c.localNode let ln = c.localNode
@ -101,7 +102,7 @@ proc encodeAuthHeader*(c: Codec,
if challenge.recordSeq < ln.record.seqNum: if challenge.recordSeq < ln.record.seqNum:
resp.record = ln.record resp.record = ln.record
let ephKeys = ? KeyPair.random() let ephKeys = KeyPair.random(rng)
let signature = signIDNonce(c.privKey, challenge.idNonce, let signature = signIDNonce(c.privKey, challenge.idNonce,
ephKeys.pubkey.toRaw) ephKeys.pubkey.toRaw)
resp.signature = signature.toRaw resp.signature = signature.toRaw
@ -117,7 +118,7 @@ proc encodeAuthHeader*(c: Codec,
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce, let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
scheme: authSchemeName, ephemeralKey: ephKeys.pubkey.toRaw, scheme: authSchemeName, ephemeralKey: ephKeys.pubkey.toRaw,
response: respEnc) response: respEnc)
ok((rlp.encode(header), secrets)) (rlp.encode(header), secrets)
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] = proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =
for i in 0 .. a.high: for i in 0 .. a.high:
@ -130,15 +131,16 @@ proc packetTag(destNode, srcNode: NodeID): PacketTag =
destidHash = sha256.digest(destId) destidHash = sha256.digest(destId)
result = srcId xor destidHash.data result = srcId xor destidHash.data
proc encodePacket*(c: Codec, proc encodePacket*(
toId: NodeID, rng: var BrHmacDrbgContext,
toAddr: Address, c: Codec,
message: openarray[byte], toId: NodeID,
challenge: Whoareyou): toAddr: Address,
EncodeResult[(seq[byte], array[gcmNonceSize, byte])] = message: openarray[byte],
challenge: Whoareyou):
(seq[byte], array[gcmNonceSize, byte]) =
var nonce: array[gcmNonceSize, byte] var nonce: array[gcmNonceSize, byte]
if randomBytes(nonce) != nonce.len: brHmacDrbgGenerate(rng, nonce)
return err("Could not randomize bytes")
var headEnc: seq[byte] var headEnc: seq[byte]
@ -153,7 +155,7 @@ proc encodePacket*(c: Codec,
discard c.db.loadKeys(toId, toAddr, readKey, writeKey) discard c.db.loadKeys(toId, toAddr, readKey, writeKey)
else: else:
var secrets: HandshakeSecrets var secrets: HandshakeSecrets
(headEnc, secrets) = ? c.encodeAuthHeader(toId, nonce, challenge) (headEnc, secrets) = encodeAuthHeader(rng, c, toId, nonce, challenge)
writeKey = secrets.writeKey writeKey = secrets.writeKey
# TODO: is it safe to ignore the error here? # TODO: is it safe to ignore the error here?
@ -165,7 +167,7 @@ proc encodePacket*(c: Codec,
packet.add(tag) packet.add(tag)
packet.add(headEnc) packet.add(headEnc)
packet.add(encryptGCM(writeKey, nonce, message, tag)) packet.add(encryptGCM(writeKey, nonce, message, tag))
ok((packet, nonce)) (packet, nonce)
proc decryptGCM*(key: AesKey, nonce, ct, authData: openarray[byte]): proc decryptGCM*(key: AesKey, nonce, ct, authData: openarray[byte]):
Option[seq[byte]] = Option[seq[byte]] =
@ -260,7 +262,7 @@ proc decodeAuthResp*(c: Codec, fromId: NodeId, head: AuthHeader,
# Verify the id-nonce-sig # Verify the id-nonce-sig
let sig = ? SignatureNR.fromRaw(authResp.signature).mapErrTo(HandshakeError) let sig = ? SignatureNR.fromRaw(authResp.signature).mapErrTo(HandshakeError)
let h = idNonceHash(head.idNonce, head.ephemeralKey) let h = idNonceHash(head.idNonce, head.ephemeralKey)
if verify(sig, h, newNode.pubkey): if verify(sig, SkMessage(h.data), newNode.pubkey):
ok(secrets) ok(secrets)
else: else:
err(HandshakeError) err(HandshakeError)
@ -332,12 +334,12 @@ proc decodePacket*(c: var Codec,
decodeMessage(message.get()) decodeMessage(message.get())
proc newRequestId*(): Result[RequestId, cstring] = proc init*(T: type RequestId, rng: var BrHmacDrbgContext): T =
var id: RequestId var buf: array[sizeof(T), byte]
if randomBytes(addr id, sizeof(id)) != sizeof(id): brHmacDrbgGenerate(rng, buf)
err("Could not randomize bytes") var id: T
else: copyMem(addr id, addr buf[0], sizeof(id))
ok(id) id
proc numFields(T: typedesc): int = proc numFields(T: typedesc): int =
for k, v in fieldPairs(default(T)): inc result for k, v in fieldPairs(default(T)): inc result

View File

@ -232,7 +232,7 @@ proc verifySignatureV4(r: Record, sigData: openarray[byte], content: seq[byte]):
let sig = SignatureNR.fromRaw(sigData) let sig = SignatureNR.fromRaw(sigData)
if sig.isOk: if sig.isOk:
var h = keccak256.digest(content) var h = keccak256.digest(content)
return verify(sig[], h, publicKey.get) return verify(sig[], SkMessage(h.data), publicKey.get)
proc verifySignature(r: Record): bool {.raises: [RlpError, Defect].} = proc verifySignature(r: Record): bool {.raises: [RlpError, Defect].} =
var rlp = rlpFromBytes(r.raw) var rlp = rlpFromBytes(r.raw)

View File

@ -73,7 +73,7 @@
## This might be a concern for mobile devices. ## This might be a concern for mobile devices.
import import
std/[tables, sets, options, math, random], std/[tables, sets, options, math, random], bearssl,
stew/shims/net as stewNet, json_serialization/std/net, stew/shims/net as stewNet, json_serialization/std/net,
stew/[byteutils, endians2], chronicles, chronos, stint, stew/[byteutils, endians2], chronicles, chronos, stint,
eth/[rlp, keys, async_utils], types, encoding, node, routing_table, enr eth/[rlp, keys, async_utils], types, encoding, node, routing_table, enr
@ -120,6 +120,7 @@ type
lookupLoop: Future[void] lookupLoop: Future[void]
revalidateLoop: Future[void] revalidateLoop: Future[void]
bootstrapRecords*: seq[Record] bootstrapRecords*: seq[Record]
rng*: ref BrHmacDrbgContext
PendingRequest = object PendingRequest = object
node: Node node: Node
@ -222,9 +223,7 @@ proc sendWhoareyou(d: Protocol, address: Address, toNode: NodeId,
authTag: AuthTag): DiscResult[void] {.raises: [Exception, Defect].} = authTag: AuthTag): DiscResult[void] {.raises: [Exception, Defect].} =
trace "sending who are you", to = $toNode, toAddress = $address trace "sending who are you", to = $toNode, toAddress = $address
let challenge = Whoareyou(authTag: authTag, recordSeq: 0) let challenge = Whoareyou(authTag: authTag, recordSeq: 0)
brHmacDrbgGenerate(d.rng[], challenge.idNonce)
if randomBytes(challenge.idNonce) != challenge.idNonce.len:
return err("Could not randomize bytes")
# If there is already a handshake going on for this nodeid then we drop this # If there is already a handshake going on for this nodeid then we drop this
# new one. Handshake will get cleaned up after `handshakeTimeout`. # new one. Handshake will get cleaned up after `handshakeTimeout`.
@ -250,17 +249,18 @@ proc sendWhoareyou(d: Protocol, address: Address, toNode: NodeId,
err("NodeId already has ongoing handshake") err("NodeId already has ongoing handshake")
proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, reqId: RequestId, proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, reqId: RequestId,
nodes: openarray[Node]): DiscResult[void] = nodes: openarray[Node]) =
proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address,
message: NodesMessage, reqId: RequestId): DiscResult[void] {.nimcall.} = message: NodesMessage, reqId: RequestId) {.nimcall.} =
let (data, _) = ? d.codec.encodePacket(toId, toAddr, let (data, _) = encodePacket(
d.rng[], d.codec, toId, toAddr,
encodeMessage(message, reqId), challenge = nil) encodeMessage(message, reqId), challenge = nil)
d.send(toAddr, data) d.send(toAddr, data)
ok()
if nodes.len == 0: if nodes.len == 0:
# In case of 0 nodes, a reply is still needed # In case of 0 nodes, a reply is still needed
return d.sendNodes(toId, toAddr, NodesMessage(total: 1, enrs: @[]), reqId) d.sendNodes(toId, toAddr, NodesMessage(total: 1, enrs: @[]), reqId)
return
var message: NodesMessage var message: NodesMessage
# TODO: Do the total calculation based on the max UDP packet size we want to # TODO: Do the total calculation based on the max UDP packet size we want to
@ -271,19 +271,14 @@ proc sendNodes(d: Protocol, toId: NodeId, toAddr: Address, reqId: RequestId,
for i in 0 ..< nodes.len: for i in 0 ..< nodes.len:
message.enrs.add(nodes[i].record) message.enrs.add(nodes[i].record)
if message.enrs.len == maxNodesPerMessage: if message.enrs.len == maxNodesPerMessage:
let res = d.sendNodes(toId, toAddr, message, reqId) d.sendNodes(toId, toAddr, message, reqId)
if res.isErr: # TODO: is there something nicer for this?
return res
message.enrs.setLen(0) message.enrs.setLen(0)
if message.enrs.len != 0: if message.enrs.len != 0:
let res = d.sendNodes(toId, toAddr, message, reqId) d.sendNodes(toId, toAddr, message, reqId)
if res.isErr: # TODO: is there something nicer for this?
return res
ok()
proc handlePing(d: Protocol, fromId: NodeId, fromAddr: Address, proc handlePing(d: Protocol, fromId: NodeId, fromAddr: Address,
ping: PingMessage, reqId: RequestId): DiscResult[void] = ping: PingMessage, reqId: RequestId) =
let a = fromAddr let a = fromAddr
var pong: PongMessage var pong: PongMessage
pong.enrSeq = ping.enrSeq pong.enrSeq = ping.enrSeq
@ -292,14 +287,13 @@ proc handlePing(d: Protocol, fromId: NodeId, fromAddr: Address,
of IpAddressFamily.IPv6: @(a.ip.address_v6) of IpAddressFamily.IPv6: @(a.ip.address_v6)
pong.port = a.port.uint16 pong.port = a.port.uint16
let (data, _) = ? d.codec.encodePacket(fromId, fromAddr, let (data, _) = encodePacket(d.rng[], d.codec, fromId, fromAddr,
encodeMessage(pong, reqId), challenge = nil) encodeMessage(pong, reqId), challenge = nil)
d.send(fromAddr, data) d.send(fromAddr, data)
ok()
proc handleFindNode(d: Protocol, fromId: NodeId, fromAddr: Address, proc handleFindNode(d: Protocol, fromId: NodeId, fromAddr: Address,
fn: FindNodeMessage, reqId: RequestId): DiscResult[void] = fn: FindNodeMessage, reqId: RequestId) =
if fn.distance == 0: if fn.distance == 0:
d.sendNodes(fromId, fromAddr, reqId, [d.localNode]) d.sendNodes(fromId, fromAddr, reqId, [d.localNode])
else: else:
@ -333,14 +327,8 @@ proc receive*(d: Protocol, a: Address, packet: openArray[byte]) {.gcsafe,
let toNode = pr.node let toNode = pr.node
whoareyou.pubKey = toNode.pubkey # TODO: Yeah, rather ugly this. whoareyou.pubKey = toNode.pubkey # TODO: Yeah, rather ugly this.
doAssert(toNode.address.isSome()) doAssert(toNode.address.isSome())
let encoded = d.codec.encodePacket(toNode.id, toNode.address.get(), let (data, _) = encodePacket(d.rng[], d.codec, toNode.id, toNode.address.get(),
pr.message, challenge = whoareyou) pr.message, challenge = whoareyou)
# TODO: Perhaps just expect here? Or raise Defect in `encodePacket`?
# if this occurs there is an issue with the system anyhow?
if encoded.isErr:
warn "Not enough randomness to encode packet"
return
let (data, _) = encoded[]
d.send(toNode, data) d.send(toNode, data)
else: else:
debug "Timed out or unrequested WhoAreYou packet" debug "Timed out or unrequested WhoAreYou packet"
@ -366,11 +354,9 @@ proc receive*(d: Protocol, a: Address, packet: openArray[byte]) {.gcsafe,
case message.kind case message.kind
of ping: of ping:
if d.handlePing(sender, a, message.ping, message.reqId).isErr: d.handlePing(sender, a, message.ping, message.reqId)
debug "Sending Pong message failed"
of findNode: of findNode:
if d.handleFindNode(sender, a, message.findNode, message.reqId).isErr: d.handleFindNode(sender, a, message.findNode, message.reqId)
debug "Sending Nodes message failed"
else: else:
var waiter: Future[Option[Message]] var waiter: Future[Option[Message]]
if d.awaitedMessages.take((sender, message.reqId), waiter): if d.awaitedMessages.take((sender, message.reqId), waiter):
@ -518,24 +504,22 @@ proc waitNodes(d: Protocol, fromNode: Node, reqId: RequestId):
return err("Nodes message not received in time") return err("Nodes message not received in time")
proc sendMessage*[T: SomeMessage](d: Protocol, toNode: Node, m: T): proc sendMessage*[T: SomeMessage](d: Protocol, toNode: Node, m: T):
DiscResult[RequestId] {.raises: [Exception, Defect].} = RequestId {.raises: [Exception, Defect].} =
doAssert(toNode.address.isSome()) doAssert(toNode.address.isSome())
let let
reqId = ? newRequestId() reqId = RequestId.init(d.rng[])
message = encodeMessage(m, reqId) message = encodeMessage(m, reqId)
(data, nonce) = ? d.codec.encodePacket(toNode.id, toNode.address.get(), (data, nonce) = encodePacket(d.rng[], d.codec, toNode.id, toNode.address.get(),
message, challenge = nil) message, challenge = nil)
d.registerRequest(toNode, message, nonce) d.registerRequest(toNode, message, nonce)
d.send(toNode, data) d.send(toNode, data)
return ok(reqId) return reqId
proc ping*(d: Protocol, toNode: Node): proc ping*(d: Protocol, toNode: Node):
Future[DiscResult[PongMessage]] {.async, raises: [Exception, Defect].} = Future[DiscResult[PongMessage]] {.async, raises: [Exception, Defect].} =
let reqId = d.sendMessage(toNode, let reqId = d.sendMessage(toNode,
PingMessage(enrSeq: d.localNode.record.seqNum)) PingMessage(enrSeq: d.localNode.record.seqNum))
if reqId.isErr: let resp = await d.waitMessage(toNode, reqId)
return err(reqId.error)
let resp = await d.waitMessage(toNode, reqId[])
if resp.isSome() and resp.get().kind == pong: if resp.isSome() and resp.get().kind == pong:
d.routingTable.setJustSeen(toNode) d.routingTable.setJustSeen(toNode)
@ -547,9 +531,7 @@ proc ping*(d: Protocol, toNode: Node):
proc findNode*(d: Protocol, toNode: Node, distance: uint32): proc findNode*(d: Protocol, toNode: Node, distance: uint32):
Future[DiscResult[seq[Node]]] {.async, raises: [Exception, Defect].} = Future[DiscResult[seq[Node]]] {.async, raises: [Exception, Defect].} =
let reqId = d.sendMessage(toNode, FindNodeMessage(distance: distance)) let reqId = d.sendMessage(toNode, FindNodeMessage(distance: distance))
if reqId.isErr: let nodes = await d.waitNodes(toNode, reqId)
return err(reqId.error)
let nodes = await d.waitNodes(toNode, reqId[])
if nodes.isOk: if nodes.isOk:
var res = newSeq[Node]() var res = newSeq[Node]()
@ -632,15 +614,16 @@ proc lookup*(d: Protocol, target: NodeId): Future[seq[Node]]
if result.len < BUCKET_SIZE: if result.len < BUCKET_SIZE:
result.add(n) result.add(n)
proc lookupRandom*(d: Protocol): Future[DiscResult[seq[Node]]] proc lookupRandom*(d: Protocol): Future[seq[Node]]
{.async, raises:[Exception, Defect].} = {.async, raises:[Exception, Defect].} =
## Perform a lookup for a random target, return the closest n nodes to the ## Perform a lookup for a random target, return the closest n nodes to the
## target. Maximum value for n is `BUCKET_SIZE`. ## target. Maximum value for n is `BUCKET_SIZE`.
var id: NodeId var id: NodeId
if randomBytes(addr id, sizeof(id)) != sizeof(id): var buf: array[sizeof(id), byte]
return err("Could not randomize bytes") brHmacDrbgGenerate(d.rng[], buf)
copyMem(addr id, addr buf[0], sizeof(id))
return ok(await d.lookup(id)) return await d.lookup(id)
proc resolve*(d: Protocol, id: NodeId): Future[Option[Node]] proc resolve*(d: Protocol, id: NodeId): Future[Option[Node]]
{.async, raises: [Exception, Defect].} = {.async, raises: [Exception, Defect].} =
@ -700,11 +683,8 @@ proc lookupLoop(d: Protocol) {.async, raises: [Exception, Defect].} =
trace "Discovered nodes in self lookup", nodes = $selfLookup trace "Discovered nodes in self lookup", nodes = $selfLookup
while true: while true:
let randomLookup = await d.lookupRandom() let randomLookup = await d.lookupRandom()
if randomLookup.isOK: trace "Discovered nodes in random lookup", nodes = $randomLookup
trace "Discovered nodes in random lookup", nodes = $randomLookup[] trace "Total nodes in routing table", total = d.routingTable.len()
trace "Total nodes in routing table", total = d.routingTable.len()
else:
trace "random lookup failed", err = randomLookup.error
await sleepAsync(lookupInterval) await sleepAsync(lookupInterval)
except CancelledError: except CancelledError:
trace "lookupLoop canceled" trace "lookupLoop canceled"
@ -713,7 +693,7 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
externalIp: Option[ValidIpAddress], tcpPort, udpPort: Port, externalIp: Option[ValidIpAddress], tcpPort, udpPort: Port,
localEnrFields: openarray[FieldPair] = [], localEnrFields: openarray[FieldPair] = [],
bootstrapRecords: openarray[Record] = [], bootstrapRecords: openarray[Record] = [],
bindIp = IPv4_any()): bindIp = IPv4_any(), rng = newRng()):
Protocol {.raises: [Defect].} = Protocol {.raises: [Defect].} =
# TODO: Tried adding bindPort = udpPort as parameter but that gave # TODO: Tried adding bindPort = udpPort as parameter but that gave
# "Error: internal error: environment misses: udpPort" in nim-beacon-chain. # "Error: internal error: environment misses: udpPort" in nim-beacon-chain.
@ -725,6 +705,9 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
localEnrFields).expect("Properly intialized private key") localEnrFields).expect("Properly intialized private key")
node = newNode(enrRec).expect("Properly initialized node") node = newNode(enrRec).expect("Properly initialized node")
# TODO Consider whether this should be a Defect
doAssert rng != nil, "RNG initialization failed"
result = Protocol( result = Protocol(
privateKey: privKey, privateKey: privKey,
db: db, db: db,
@ -733,7 +716,8 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
whoareyouMagic: whoareyouMagic(node.id), whoareyouMagic: whoareyouMagic(node.id),
idHash: sha256.digest(node.id.toByteArrayBE).data, idHash: sha256.digest(node.id.toByteArrayBE).data,
codec: Codec(localNode: node, privKey: privKey, db: db), codec: Codec(localNode: node, privKey: privKey, db: db),
bootstrapRecords: @bootstrapRecords) bootstrapRecords: @bootstrapRecords,
rng: rng)
result.routingTable.init(node, 5) result.routingTable.init(node, 5)

View File

@ -12,7 +12,8 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sysrand, sha2, utils] import bearssl
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sha2, utils]
import stew/results import stew/results
export results export results
@ -23,7 +24,6 @@ const
type type
EciesError* = enum EciesError* = enum
BufferOverrun = "ecies: output buffer size is too small" BufferOverrun = "ecies: output buffer size is too small"
RandomError = "ecies: could not obtain random data"
EcdhError = "ecies: ECDH shared secret could not be calculated" EcdhError = "ecies: ECDH shared secret could not be calculated"
WrongHeader = "ecies: header is incorrect" WrongHeader = "ecies: header is incorrect"
IncorrectKey = "ecies: recovered public key is invalid" IncorrectKey = "ecies: recovered public key is invalid"
@ -92,8 +92,8 @@ proc kdf*(data: openarray[byte]): array[KeyLength, byte] {.noInit.} =
ctx.clear() # clean ctx ctx.clear() # clean ctx
copyMem(addr result[0], addr storage[0], KeyLength) copyMem(addr result[0], addr storage[0], KeyLength)
proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte], proc eciesEncrypt*(rng: var BrHmacDrbgContext, input: openarray[byte],
pubkey: PublicKey, output: var openarray[byte], pubkey: PublicKey,
sharedmac: openarray[byte] = emptyMac): EciesResult[void] = sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
## Encrypt data with ECIES method using given public key `pubkey`. ## Encrypt data with ECIES method using given public key `pubkey`.
## ``input`` - input data ## ``input`` - input data
@ -110,11 +110,11 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
if len(output) < eciesEncryptedLength(len(input)): if len(output) < eciesEncryptedLength(len(input)):
return err(BufferOverrun) return err(BufferOverrun)
if randomBytes(iv) != aes128.sizeBlock:
return err(RandomError) brHmacDrbgGenerate(rng, iv)
var var
ephemeral = ? KeyPair.random().mapErrTo(RandomError) ephemeral = KeyPair.random(rng)
secret = ecdhRaw(ephemeral.seckey, pubkey) secret = ecdhRaw(ephemeral.seckey, pubkey)
material = kdf(secret.data) material = kdf(secret.data)

View File

@ -9,8 +9,8 @@
# #
import import
tables, hashes, times, algorithm, sets, sequtils, random, tables, hashes, times, algorithm, sets, sequtils, bearssl, random,
chronos, eth/keys, chronicles, stint, nimcrypto, chronos, eth/keys, chronicles, stint, nimcrypto/keccak,
enode enode
export sets # TODO: This should not be needed, but compilation fails otherwise export sets # TODO: This should not be needed, but compilation fails otherwise
@ -26,6 +26,7 @@ type
pongFutures: Table[seq[byte], Future[bool]] pongFutures: Table[seq[byte], Future[bool]]
pingFutures: Table[Node, Future[bool]] pingFutures: Table[Node, Future[bool]]
neighboursCallbacks: Table[Node, proc(n: seq[Node]) {.gcsafe.}] neighboursCallbacks: Table[Node, proc(n: seq[Node]) {.gcsafe.}]
rng: ref BrHmacDrbgContext
NodeId* = UInt256 NodeId* = UInt256
@ -231,12 +232,15 @@ proc neighbours(r: RoutingTable, id: NodeId, k: int = BUCKET_SIZE): seq[Node] =
proc len(r: RoutingTable): int = proc len(r: RoutingTable): int =
for b in r.buckets: result += b.len for b in r.buckets: result += b.len
proc newKademliaProtocol*[Wire](thisNode: Node, proc newKademliaProtocol*[Wire](
wire: Wire): KademliaProtocol[Wire] = thisNode: Node, wire: Wire, rng = newRng()): KademliaProtocol[Wire] =
if rng == nil: raiseAssert "Need an RNG" # doAssert gives compile error on mac
result.new() result.new()
result.thisNode = thisNode result.thisNode = thisNode
result.wire = wire result.wire = wire
result.routing.init(thisNode) result.routing.init(thisNode)
result.rng = rng
proc bond(k: KademliaProtocol, n: Node): Future[bool] {.async, gcsafe.} proc bond(k: KademliaProtocol, n: Node): Future[bool] {.async, gcsafe.}
@ -408,7 +412,10 @@ proc lookup*(k: KademliaProtocol, nodeId: NodeId): Future[seq[Node]] {.async.} =
proc lookupRandom*(k: KademliaProtocol): Future[seq[Node]] = proc lookupRandom*(k: KademliaProtocol): Future[seq[Node]] =
var id: NodeId var id: NodeId
discard randomBytes(addr id, id.sizeof) var buf: array[sizeof(id), byte]
brHmacDrbgGenerate(k.rng[], buf)
copyMem(addr id, addr buf[0], sizeof(id))
k.lookup(id) k.lookup(id)
proc resolve*(k: KademliaProtocol, id: NodeId): Future[Node] {.async.} = proc resolve*(k: KademliaProtocol, id: NodeId): Future[Node] {.async.} =

View File

@ -1,5 +1,5 @@
import import
deques, tables, deques, tables, bearssl,
eth/[rlp, keys], chronos, eth/common/eth_types, eth/[rlp, keys], chronos, eth/common/eth_types,
../enode, ../kademlia, ../discovery, ../rlpxcrypt ../enode, ../kademlia, ../discovery, ../rlpxcrypt
@ -24,6 +24,7 @@ type
discovery*: DiscoveryProtocol discovery*: DiscoveryProtocol
when useSnappy: when useSnappy:
protocolVersion*: uint protocolVersion*: uint
rng*: ref BrHmacDrbgContext
Peer* = ref object Peer* = ref object
remote*: Node remote*: Node

View File

@ -461,7 +461,7 @@ proc waitSingleMsg(peer: Peer, MsgType: type): Future[MsgType] {.async.} =
result = checkedRlpRead(peer, nextMsgData, MsgType) result = checkedRlpRead(peer, nextMsgData, MsgType)
logReceivedMsg(peer, result) logReceivedMsg(peer, result)
return return
except RlpError: except rlp.RlpError:
await peer.disconnectAndRaise(BreachOfProtocol, await peer.disconnectAndRaise(BreachOfProtocol,
"Invalid RLPx message body") "Invalid RLPx message body")
@ -969,11 +969,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
try: try:
result.transport = await connect(ta) result.transport = await connect(ta)
var handshake = Handshake.tryInit( var handshake = Handshake.tryInit(
node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet() node.rng[], node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet()
var authMsg: array[AuthMessageMaxEIP8, byte] var authMsg: array[AuthMessageMaxEIP8, byte]
var authMsgLen = 0 var authMsgLen = 0
authMessage(handshake, remote.node.pubkey, authMsg, authMsgLen).tryGet() authMessage(
handshake, node.rng[], remote.node.pubkey, authMsg, authMsgLen).tryGet()
var res = await result.transport.write(addr authMsg[0], authMsgLen) var res = await result.transport.write(addr authMsg[0], authMsgLen)
if res != authMsgLen: if res != authMsgLen:
raisePeerDisconnected("Unexpected disconnect while authenticating", raisePeerDisconnected("Unexpected disconnect while authenticating",
@ -1055,7 +1056,8 @@ proc rlpxAccept*(node: EthereumNode,
result.transport = transport result.transport = transport
result.network = node result.network = node
var handshake = HandShake.tryInit(node.keys, {auth.Responder}).tryGet var handshake =
HandShake.tryInit(node.rng[], node.keys, {auth.Responder}).tryGet
var ok = false var ok = false
try: try:
@ -1078,7 +1080,7 @@ proc rlpxAccept*(node: EthereumNode,
var ackMsg: array[AckMessageMaxEIP8, byte] var ackMsg: array[AckMessageMaxEIP8, byte]
var ackMsgLen: int var ackMsgLen: int
handshake.ackMessage(ackMsg, ackMsgLen).tryGet() handshake.ackMessage(node.rng[], ackMsg, ackMsgLen).tryGet()
var res = await transport.write(addr ackMsg[0], ackMsgLen) var res = await transport.write(addr ackMsg[0], ackMsgLen)
if res != ackMsgLen: if res != ackMsgLen:
raisePeerDisconnected("Unexpected disconnect while authenticating", raisePeerDisconnected("Unexpected disconnect while authenticating",

View File

@ -1,196 +0,0 @@
## Generated at line 781
type
DevP2P* = object
type
helloObj* = object
version*: uint
clientId*: string
capabilities*: seq[Capability]
listenPort*: uint
nodeId*: array[RawPublicKeySize, byte]
template hello*(PROTO: type DevP2P): type =
helloObj
template msgProtocol*(MSG: type helloObj): type =
DevP2P
template RecType*(MSG: type helloObj): untyped =
helloObj
template msgId*(MSG: type helloObj): int =
0
type
sendDisconnectMsgObj* = object
reason*: DisconnectionReasonList
template sendDisconnectMsg*(PROTO: type DevP2P): type =
sendDisconnectMsgObj
template msgProtocol*(MSG: type sendDisconnectMsgObj): type =
DevP2P
template RecType*(MSG: type sendDisconnectMsgObj): untyped =
sendDisconnectMsgObj
template msgId*(MSG: type sendDisconnectMsgObj): int =
1
type
pingObj* = object
emptyList*: EmptyList
template ping*(PROTO: type DevP2P): type =
pingObj
template msgProtocol*(MSG: type pingObj): type =
DevP2P
template RecType*(MSG: type pingObj): untyped =
pingObj
template msgId*(MSG: type pingObj): int =
2
type
pongObj* = object
emptyList*: EmptyList
template pong*(PROTO: type DevP2P): type =
pongObj
template msgProtocol*(MSG: type pongObj): type =
DevP2P
template RecType*(MSG: type pongObj): untyped =
pongObj
template msgId*(MSG: type pongObj): int =
3
var DevP2PProtocolObj = initProtocol("p2p", 5, nil, nil)
var DevP2PProtocol = addr DevP2PProtocolObj
template protocolInfo*(P`gensym75730262: type DevP2P): auto =
DevP2PProtocol
proc hello*(peerOrResponder: Peer; version: uint; clientId: string;
capabilities: seq[Capability]; listenPort: uint;
nodeId: array[RawPublicKeySize, byte]): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 0
let perPeerMsgId = 0
append(writer, perPeerMsgId)
startList(writer, 5)
append(writer, version)
append(writer, clientId)
append(writer, capabilities)
append(writer, listenPort)
append(writer, nodeId)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc sendDisconnectMsg*(peerOrResponder: Peer; reason: DisconnectionReasonList): Future[
void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 1
let perPeerMsgId = 1
append(writer, perPeerMsgId)
append(writer, reason)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc ping*(peerOrResponder: Peer; emptyList: EmptyList): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 2
let perPeerMsgId = 2
append(writer, perPeerMsgId)
append(writer, emptyList)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc pong*(peerOrResponder: Peer; emptyList: EmptyList): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 3
let perPeerMsgId = 3
append(writer, perPeerMsgId)
append(writer, emptyList)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc sendDisconnectMsgUserHandler(peer: Peer; reason: DisconnectionReasonList) {.
gcsafe, async.} =
type
CurrentProtocol = DevP2P
const
perProtocolMsgId = 1
trace "disconnect message received", reason = reason.value, peer
await peer.disconnect(reason.value, false)
proc pingUserHandler(peer: Peer; emptyList: EmptyList) {.gcsafe, async.} =
type
CurrentProtocol = DevP2P
const
perProtocolMsgId = 2
discard peer.pong(EmptyList())
proc pongUserHandler(peer: Peer; emptyList: EmptyList) {.gcsafe, async.} =
type
CurrentProtocol = DevP2P
const
perProtocolMsgId = 3
discard
proc helloThunk(peer: Peer; _`gensym75730215: int; data`gensym75730216: Rlp) {.async,
gcsafe.} =
var rlp = data`gensym75730216
var msg {.noinit.}: helloObj
tryEnterList(rlp)
msg.version = checkedRlpRead(peer, rlp, uint)
msg.clientId = checkedRlpRead(peer, rlp, string)
msg.capabilities = checkedRlpRead(peer, rlp, seq[Capability])
msg.listenPort = checkedRlpRead(peer, rlp, uint)
msg.nodeId = checkedRlpRead(peer, rlp, array[RawPublicKeySize, byte])
proc sendDisconnectMsgThunk(peer: Peer; _`gensym75730250: int;
data`gensym75730251: Rlp) {.async, gcsafe.} =
var rlp = data`gensym75730251
var msg {.noinit.}: sendDisconnectMsgObj
msg.reason = checkedRlpRead(peer, rlp, DisconnectionReasonList)
await(sendDisconnectMsgUserHandler(peer, msg.reason))
proc pingThunk(peer: Peer; _`gensym75730252: int; data`gensym75730253: Rlp) {.async,
gcsafe.} =
var rlp = data`gensym75730253
var msg {.noinit.}: pingObj
msg.emptyList = checkedRlpRead(peer, rlp, EmptyList)
await(pingUserHandler(peer, msg.emptyList))
proc pongThunk(peer: Peer; _`gensym75730254: int; data`gensym75730255: Rlp) {.async,
gcsafe.} =
var rlp = data`gensym75730255
var msg {.noinit.}: pongObj
msg.emptyList = checkedRlpRead(peer, rlp, EmptyList)
await(pongUserHandler(peer, msg.emptyList))
registerMsg(DevP2PProtocol, 0, "hello", helloThunk, messagePrinter[helloObj],
requestResolver[helloObj], nextMsgResolver[helloObj])
registerMsg(DevP2PProtocol, 1, "sendDisconnectMsg", sendDisconnectMsgThunk,
messagePrinter[sendDisconnectMsgObj],
requestResolver[sendDisconnectMsgObj],
nextMsgResolver[sendDisconnectMsgObj])
registerMsg(DevP2PProtocol, 2, "ping", pingThunk, messagePrinter[pingObj],
requestResolver[pingObj], nextMsgResolver[pingObj])
registerMsg(DevP2PProtocol, 3, "pong", pongThunk, messagePrinter[pongObj],
requestResolver[pongObj], nextMsgResolver[pongObj])
setEventHandlers(DevP2PProtocol, nil, nil)
registerProtocol(DevP2PProtocol)

View File

@ -9,9 +9,9 @@
# #
import import
algorithm, bitops, math, options, tables, times, chronicles, hashes, strutils, algorithm, bitops, math, options, tables, times, chronicles, hashes,
stew/[byteutils, endians2], metrics, stew/[byteutils, endians2], metrics, bearssl,
nimcrypto/[bcmode, hash, keccak, rijndael, sysrand], nimcrypto/[bcmode, hash, keccak, rijndael],
eth/[keys, rlp, p2p], eth/p2p/ecies eth/[keys, rlp, p2p], eth/p2p/ecies
logScope: logScope:
@ -158,12 +158,10 @@ proc topicBloom*(topic: Topic): Bloom =
doAssert idx <= 511 doAssert idx <= 511
result[idx div 8] = result[idx div 8] or byte(1 shl (idx and 7'u16)) result[idx div 8] = result[idx div 8] or byte(1 shl (idx and 7'u16))
proc generateRandomID*(): string = proc generateRandomID*(rng: var BrHmacDrbgContext): string =
var bytes: array[256 div 8, byte] var bytes: array[256 div 8, byte]
while true: # XXX: error instead of looping? brHmacDrbgGenerate(rng, bytes)
if randomBytes(bytes) == 256 div 8: toHex(bytes)
result = toHex(bytes)
break
proc `or`(a, b: Bloom): Bloom = proc `or`(a, b: Bloom): Bloom =
for i in 0..<a.len: for i in 0..<a.len:
@ -231,7 +229,7 @@ proc decryptAesGcm(cipher: openarray[byte], key: SymKey): Option[seq[byte]] =
# simply because that makes it closer to EIP 627 - see also: # simply because that makes it closer to EIP 627 - see also:
# https://github.com/paritytech/parity-ethereum/issues/9652 # https://github.com/paritytech/parity-ethereum/issues/9652
proc encode*(self: Payload): Option[seq[byte]] = proc encode*(rng: var BrHmacDrbgContext, self: Payload): Option[seq[byte]] =
## Encode a payload according so as to make it suitable to put in an Envelope ## Encode a payload according so as to make it suitable to put in an Envelope
## The format follows EIP 627 - https://eips.ethereum.org/EIPS/eip-627 ## The format follows EIP 627 - https://eips.ethereum.org/EIPS/eip-627
@ -284,9 +282,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
plain.add self.padding.get() plain.add self.padding.get()
else: else:
var padding = newSeq[byte](padLen) var padding = newSeq[byte](padLen)
if randomBytes(padding) != padLen: brHmacDrbgGenerate(rng, padding)
notice "Generation of random padding failed"
return
plain.add padding plain.add padding
@ -297,7 +293,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
if self.dst.isSome(): # Asymmetric key present - encryption requested if self.dst.isSome(): # Asymmetric key present - encryption requested
var res = newSeq[byte](eciesEncryptedLength(plain.len)) var res = newSeq[byte](eciesEncryptedLength(plain.len))
let err = eciesEncrypt(plain, res, self.dst.get()) let err = eciesEncrypt(rng, plain, res, self.dst.get())
if err.isErr: if err.isErr:
notice "Encryption failed", err = err.error notice "Encryption failed", err = err.error
return return
@ -305,9 +301,7 @@ proc encode*(self: Payload): Option[seq[byte]] =
if self.symKey.isSome(): # Symmetric key present - encryption requested if self.symKey.isSome(): # Symmetric key present - encryption requested
var iv: array[gcmIVLen, byte] var iv: array[gcmIVLen, byte]
if randomBytes(iv) != gcmIVLen: brHmacDrbgGenerate(rng, iv)
notice "Generation of random IV failed"
return
return some(encryptAesGcm(plain, self.symKey.get(), iv)) return some(encryptAesGcm(plain, self.symKey.get(), iv))
@ -582,11 +576,12 @@ proc initFilter*(src = none[PublicKey](), privateKey = none[PrivateKey](),
Filter(src: src, privateKey: privateKey, symKey: symKey, topics: topics, Filter(src: src, privateKey: privateKey, symKey: symKey, topics: topics,
powReq: powReq, allowP2P: allowP2P, bloom: toBloom(topics)) powReq: powReq, allowP2P: allowP2P, bloom: toBloom(topics))
proc subscribeFilter*(filters: var Filters, filter: Filter, proc subscribeFilter*(
handler:FilterMsgHandler = nil): string = rng: var BrHmacDrbgContext, filters: var Filters, filter: Filter,
handler: FilterMsgHandler = nil): string =
# NOTE: Should we allow a filter without a key? Encryption is mandatory in v6? # NOTE: Should we allow a filter without a key? Encryption is mandatory in v6?
# Check if asymmetric _and_ symmetric key? Now asymmetric just has precedence. # Check if asymmetric _and_ symmetric key? Now asymmetric just has precedence.
let id = generateRandomID() let id = generateRandomID(rng)
var filter = filter var filter = filter
if handler.isNil(): if handler.isNil():
filter.queue = newSeqOfCap[ReceivedMessage](defaultFilterQueueCapacity) filter.queue = newSeqOfCap[ReceivedMessage](defaultFilterQueueCapacity)

View File

@ -343,8 +343,8 @@ proc postMessage*(node: EthereumNode, pubKey = none[PublicKey](),
## ##
## NOTE: This call allows a post without encryption. If encryption is ## NOTE: This call allows a post without encryption. If encryption is
## mandatory it should be enforced a layer up ## mandatory it should be enforced a layer up
let payload = encode(Payload(payload: payload, src: src, dst: pubKey, let payload = encode(node.rng[], Payload(
symKey: symKey, padding: padding)) payload: payload, src: src, dst: pubKey, symKey: symKey, padding: padding))
if payload.isSome(): if payload.isSome():
var env = Envelope(expiry:epochTime().uint32 + ttl, var env = Envelope(expiry:epochTime().uint32 + ttl,
ttl: ttl, topic: topic, data: payload.get(), nonce: 0) ttl: ttl, topic: topic, data: payload.get(), nonce: 0)
@ -387,7 +387,8 @@ proc subscribeFilter*(node: EthereumNode, filter: Filter,
## ##
## NOTE: This call allows for a filter without decryption. If encryption is ## NOTE: This call allows for a filter without decryption. If encryption is
## mandatory it should be enforced a layer up. ## mandatory it should be enforced a layer up.
return node.protocolState(Whisper).filters.subscribeFilter(filter, handler) return subscribeFilter(
node.rng[], node.protocolState(Whisper).filters, filter, handler)
proc unsubscribeFilter*(node: EthereumNode, filterId: string): bool = proc unsubscribeFilter*(node: EthereumNode, filterId: string): bool =
## Remove a previously subscribed filter. ## Remove a previously subscribed filter.

View File

@ -1,662 +0,0 @@
## Generated at line 119
type
Whisper* = object
template State*(PROTO: type Whisper): type =
ref[WhisperPeer:ObjectType]
template NetworkState*(PROTO: type Whisper): type =
ref[WhisperNetwork:ObjectType]
type
statusObj* = object
protocolVersion*: uint
powConverted*: uint64
bloom*: seq[byte]
isLightNode*: bool
template status*(PROTO: type Whisper): type =
statusObj
template msgProtocol*(MSG: type statusObj): type =
Whisper
template RecType*(MSG: type statusObj): untyped =
statusObj
template msgId*(MSG: type statusObj): int =
0
type
messagesObj* = object
envelopes*: seq[Envelope]
template messages*(PROTO: type Whisper): type =
messagesObj
template msgProtocol*(MSG: type messagesObj): type =
Whisper
template RecType*(MSG: type messagesObj): untyped =
messagesObj
template msgId*(MSG: type messagesObj): int =
1
type
powRequirementObj* = object
value*: uint64
template powRequirement*(PROTO: type Whisper): type =
powRequirementObj
template msgProtocol*(MSG: type powRequirementObj): type =
Whisper
template RecType*(MSG: type powRequirementObj): untyped =
powRequirementObj
template msgId*(MSG: type powRequirementObj): int =
2
type
bloomFilterExchangeObj* = object
bloom*: seq[byte]
template bloomFilterExchange*(PROTO: type Whisper): type =
bloomFilterExchangeObj
template msgProtocol*(MSG: type bloomFilterExchangeObj): type =
Whisper
template RecType*(MSG: type bloomFilterExchangeObj): untyped =
bloomFilterExchangeObj
template msgId*(MSG: type bloomFilterExchangeObj): int =
3
type
p2pRequestObj* = object
envelope*: Envelope
template p2pRequest*(PROTO: type Whisper): type =
p2pRequestObj
template msgProtocol*(MSG: type p2pRequestObj): type =
Whisper
template RecType*(MSG: type p2pRequestObj): untyped =
p2pRequestObj
template msgId*(MSG: type p2pRequestObj): int =
126
type
p2pMessageObj* = object
envelope*: Envelope
template p2pMessage*(PROTO: type Whisper): type =
p2pMessageObj
template msgProtocol*(MSG: type p2pMessageObj): type =
Whisper
template RecType*(MSG: type p2pMessageObj): untyped =
p2pMessageObj
template msgId*(MSG: type p2pMessageObj): int =
127
type
batchAcknowledgedObj* = object
template batchAcknowledged*(PROTO: type Whisper): type =
batchAcknowledgedObj
template msgProtocol*(MSG: type batchAcknowledgedObj): type =
Whisper
template RecType*(MSG: type batchAcknowledgedObj): untyped =
batchAcknowledgedObj
template msgId*(MSG: type batchAcknowledgedObj): int =
11
type
messageResponseObj* = object
template messageResponse*(PROTO: type Whisper): type =
messageResponseObj
template msgProtocol*(MSG: type messageResponseObj): type =
Whisper
template RecType*(MSG: type messageResponseObj): untyped =
messageResponseObj
template msgId*(MSG: type messageResponseObj): int =
12
type
p2pSyncResponseObj* = object
template p2pSyncResponse*(PROTO: type Whisper): type =
p2pSyncResponseObj
template msgProtocol*(MSG: type p2pSyncResponseObj): type =
Whisper
template RecType*(MSG: type p2pSyncResponseObj): untyped =
p2pSyncResponseObj
template msgId*(MSG: type p2pSyncResponseObj): int =
124
type
p2pSyncRequestObj* = object
template p2pSyncRequest*(PROTO: type Whisper): type =
p2pSyncRequestObj
template msgProtocol*(MSG: type p2pSyncRequestObj): type =
Whisper
template RecType*(MSG: type p2pSyncRequestObj): untyped =
p2pSyncRequestObj
template msgId*(MSG: type p2pSyncRequestObj): int =
123
type
p2pRequestCompleteObj* = object
template p2pRequestComplete*(PROTO: type Whisper): type =
p2pRequestCompleteObj
template msgProtocol*(MSG: type p2pRequestCompleteObj): type =
Whisper
template RecType*(MSG: type p2pRequestCompleteObj): untyped =
p2pRequestCompleteObj
template msgId*(MSG: type p2pRequestCompleteObj): int =
125
var WhisperProtocolObj = initProtocol("shh", 6, createPeerState[Peer,
ref[WhisperPeer:ObjectType]], createNetworkState[EthereumNode,
ref[WhisperNetwork:ObjectType]])
var WhisperProtocol = addr WhisperProtocolObj
template protocolInfo*(P`gensym85175079: type Whisper): auto =
WhisperProtocol
proc statusRawSender(peerOrResponder: Peer; protocolVersion: uint;
powConverted: uint64; bloom: seq[byte]; isLightNode: bool;
timeout: Duration = milliseconds(10000'i64)): Future[void] {.
gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 0
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 0)
append(writer, perPeerMsgId)
startList(writer, 4)
append(writer, protocolVersion)
append(writer, powConverted)
append(writer, bloom)
append(writer, isLightNode)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
template status*(peerOrResponder: Peer; protocolVersion: uint; powConverted: uint64;
bloom: seq[byte]; isLightNode: bool;
timeout: Duration = milliseconds(10000'i64)): Future[statusObj] =
let peer = peerOrResponder
let sendingFuture`gensym85175056 = statusRawSender(peer, protocolVersion,
powConverted, bloom, isLightNode)
handshakeImpl(peer, sendingFuture`gensym85175056, nextMsg(peer, statusObj),
timeout)
proc messages*(peerOrResponder: Peer; envelopes: openarray[Envelope]): Future[void] {.
gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 1
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 1)
append(writer, perPeerMsgId)
append(writer, envelopes)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc powRequirement*(peerOrResponder: Peer; value: uint64): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 2
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 2)
append(writer, perPeerMsgId)
append(writer, value)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc bloomFilterExchange*(peerOrResponder: Peer; bloom: openArray[byte]): Future[void] {.
gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 3
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 3)
append(writer, perPeerMsgId)
append(writer, bloom)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc p2pRequest*(peerOrResponder: Peer; envelope: Envelope): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 126
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 126)
append(writer, perPeerMsgId)
append(writer, envelope)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc p2pMessage*(peerOrResponder: Peer; envelope: Envelope): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 127
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 127)
append(writer, perPeerMsgId)
append(writer, envelope)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc batchAcknowledged*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 11
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 11)
append(writer, perPeerMsgId)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc messageResponse*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 12
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 12)
append(writer, perPeerMsgId)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc p2pSyncResponse*(peerOrResponder: ResponderWithId[p2pSyncResponseObj]): Future[
void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 124
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 124)
append(writer, perPeerMsgId)
append(writer, peerOrResponder.reqId)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
template send*(r`gensym85175073: ResponderWithId[p2pSyncResponseObj];
args`gensym85175074: varargs[untyped]): auto =
p2pSyncResponse(r`gensym85175073, args`gensym85175074)
proc p2pSyncRequest*(peerOrResponder: Peer;
timeout: Duration = milliseconds(10000'i64)): Future[
Option[p2pSyncResponseObj]] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 123
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 123)
append(writer, perPeerMsgId)
initFuture result
let reqId = registerRequest(peer, timeout, result, perPeerMsgId + 1)
append(writer, reqId)
let msgBytes = finish(writer)
linkSendFailureToReqFuture(sendMsg(peer, msgBytes), result)
proc p2pRequestComplete*(peerOrResponder: Peer): Future[void] {.gcsafe.} =
let peer = getPeer(peerOrResponder)
var writer = initRlpWriter()
const
perProtocolMsgId = 125
let perPeerMsgId = perPeerMsgIdImpl(peer, WhisperProtocol, 125)
append(writer, perPeerMsgId)
let msgBytes = finish(writer)
return sendMsg(peer, msgBytes)
proc messagesUserHandler(peer: Peer; envelopes: seq[Envelope]) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 1
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
if not peer.state.initialized:
warn "Handshake not completed yet, discarding messages"
return
for envelope in envelopes:
if not envelope.valid():
warn "Expired or future timed envelope", peer
continue
let msg = initMessage(envelope)
if not msg.allowed(peer.networkState.config):
continue
if peer.state.received.containsOrIncl(msg.hash):
dropped_duplicate_envelopes.inc()
trace "Peer sending duplicate messages", peer, hash = $msg.hash
continue
if peer.networkState.queue[].add(msg):
peer.networkState.filters.notify(msg)
proc powRequirementUserHandler(peer: Peer; value: uint64) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 2
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
if not peer.state.initialized:
warn "Handshake not completed yet, discarding powRequirement"
return
peer.state.powRequirement = cast[float64](value)
proc bloomFilterExchangeUserHandler(peer: Peer; bloom: seq[byte]) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 3
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
if not peer.state.initialized:
warn "Handshake not completed yet, discarding bloomFilterExchange"
return
if bloom.len == bloomSize:
peer.state.bloom.bytesCopy(bloom)
proc p2pRequestUserHandler(peer: Peer; envelope: Envelope) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 126
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
discard
proc p2pMessageUserHandler(peer: Peer; envelope: Envelope) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 127
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
if peer.state.trusted:
let msg = Message(env: envelope, isP2P: true)
peer.networkState.filters.notify(msg)
proc batchAcknowledgedUserHandler(peer: Peer) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 11
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
discard
proc messageResponseUserHandler(peer: Peer) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 12
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
discard
proc p2pSyncResponseUserHandler(peer: Peer; reqId: int) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 124
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
discard
proc p2pSyncRequestUserHandler(peer: Peer; reqId: int) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 123
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
var response = init(ResponderWithId[p2pSyncResponseObj], peer, reqId)
discard
proc p2pRequestCompleteUserHandler(peer: Peer) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
const
perProtocolMsgId = 125
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
discard
proc statusThunk(peer: Peer; _`gensym85175033: int; data`gensym85175034: Rlp) {.async,
gcsafe.} =
var rlp = data`gensym85175034
var msg {.noinit.}: statusObj
tryEnterList(rlp)
msg.protocolVersion = checkedRlpRead(peer, rlp, uint)
msg.powConverted = checkedRlpRead(peer, rlp, uint64)
msg.bloom = checkedRlpRead(peer, rlp, seq[byte])
msg.isLightNode = checkedRlpRead(peer, rlp, bool)
proc messagesThunk(peer: Peer; _`gensym85175057: int; data`gensym85175058: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175058
var msg {.noinit.}: messagesObj
msg.envelopes = checkedRlpRead(peer, rlp, openarray[Envelope])
await(messagesUserHandler(peer, msg.envelopes))
proc powRequirementThunk(peer: Peer; _`gensym85175059: int; data`gensym85175060: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175060
var msg {.noinit.}: powRequirementObj
msg.value = checkedRlpRead(peer, rlp, uint64)
await(powRequirementUserHandler(peer, msg.value))
proc bloomFilterExchangeThunk(peer: Peer; _`gensym85175061: int;
data`gensym85175062: Rlp) {.async, gcsafe.} =
var rlp = data`gensym85175062
var msg {.noinit.}: bloomFilterExchangeObj
msg.bloom = checkedRlpRead(peer, rlp, openArray[byte])
await(bloomFilterExchangeUserHandler(peer, msg.bloom))
proc p2pRequestThunk(peer: Peer; _`gensym85175063: int; data`gensym85175064: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175064
var msg {.noinit.}: p2pRequestObj
msg.envelope = checkedRlpRead(peer, rlp, Envelope)
await(p2pRequestUserHandler(peer, msg.envelope))
proc p2pMessageThunk(peer: Peer; _`gensym85175065: int; data`gensym85175066: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175066
var msg {.noinit.}: p2pMessageObj
msg.envelope = checkedRlpRead(peer, rlp, Envelope)
await(p2pMessageUserHandler(peer, msg.envelope))
proc batchAcknowledgedThunk(peer: Peer; _`gensym85175067: int;
data`gensym85175068: Rlp) {.async, gcsafe.} =
var rlp = data`gensym85175068
var msg {.noinit.}: batchAcknowledgedObj
await(batchAcknowledgedUserHandler(peer))
proc messageResponseThunk(peer: Peer; _`gensym85175069: int; data`gensym85175070: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175070
var msg {.noinit.}: messageResponseObj
await(messageResponseUserHandler(peer))
proc p2pSyncResponseThunk(peer: Peer; _`gensym85175071: int; data`gensym85175072: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175072
var msg {.noinit.}: p2pSyncResponseObj
let reqId = read(rlp, int)
await(p2pSyncResponseUserHandler(peer, reqId))
resolveResponseFuture(peer, perPeerMsgId(peer, p2pSyncResponseObj), addr(msg),
reqId)
proc p2pSyncRequestThunk(peer: Peer; _`gensym85175075: int; data`gensym85175076: Rlp) {.
async, gcsafe.} =
var rlp = data`gensym85175076
var msg {.noinit.}: p2pSyncRequestObj
let reqId = read(rlp, int)
await(p2pSyncRequestUserHandler(peer, reqId))
proc p2pRequestCompleteThunk(peer: Peer; _`gensym85175077: int;
data`gensym85175078: Rlp) {.async, gcsafe.} =
var rlp = data`gensym85175078
var msg {.noinit.}: p2pRequestCompleteObj
await(p2pRequestCompleteUserHandler(peer))
registerMsg(WhisperProtocol, 0, "status", statusThunk, messagePrinter[statusObj],
requestResolver[statusObj], nextMsgResolver[statusObj])
registerMsg(WhisperProtocol, 1, "messages", messagesThunk,
messagePrinter[messagesObj], requestResolver[messagesObj],
nextMsgResolver[messagesObj])
registerMsg(WhisperProtocol, 2, "powRequirement", powRequirementThunk,
messagePrinter[powRequirementObj],
requestResolver[powRequirementObj],
nextMsgResolver[powRequirementObj])
registerMsg(WhisperProtocol, 3, "bloomFilterExchange", bloomFilterExchangeThunk,
messagePrinter[bloomFilterExchangeObj],
requestResolver[bloomFilterExchangeObj],
nextMsgResolver[bloomFilterExchangeObj])
registerMsg(WhisperProtocol, 126, "p2pRequest", p2pRequestThunk,
messagePrinter[p2pRequestObj], requestResolver[p2pRequestObj],
nextMsgResolver[p2pRequestObj])
registerMsg(WhisperProtocol, 127, "p2pMessage", p2pMessageThunk,
messagePrinter[p2pMessageObj], requestResolver[p2pMessageObj],
nextMsgResolver[p2pMessageObj])
registerMsg(WhisperProtocol, 11, "batchAcknowledged", batchAcknowledgedThunk,
messagePrinter[batchAcknowledgedObj],
requestResolver[batchAcknowledgedObj],
nextMsgResolver[batchAcknowledgedObj])
registerMsg(WhisperProtocol, 12, "messageResponse", messageResponseThunk,
messagePrinter[messageResponseObj],
requestResolver[messageResponseObj],
nextMsgResolver[messageResponseObj])
registerMsg(WhisperProtocol, 124, "p2pSyncResponse", p2pSyncResponseThunk,
messagePrinter[p2pSyncResponseObj],
requestResolver[p2pSyncResponseObj],
nextMsgResolver[p2pSyncResponseObj])
registerMsg(WhisperProtocol, 123, "p2pSyncRequest", p2pSyncRequestThunk,
messagePrinter[p2pSyncRequestObj],
requestResolver[p2pSyncRequestObj],
nextMsgResolver[p2pSyncRequestObj])
registerMsg(WhisperProtocol, 125, "p2pRequestComplete", p2pRequestCompleteThunk,
messagePrinter[p2pRequestCompleteObj],
requestResolver[p2pRequestCompleteObj],
nextMsgResolver[p2pRequestCompleteObj])
proc WhisperPeerConnected(peer: Peer) {.gcsafe, async.} =
type
CurrentProtocol = Whisper
template state(peer: Peer): ref[WhisperPeer:ObjectType] =
cast[ref[WhisperPeer:ObjectType]](getState(peer, WhisperProtocol))
template networkState(peer: Peer): ref[WhisperNetwork:ObjectType] =
cast[ref[WhisperNetwork:ObjectType]](getNetworkState(peer.network,
WhisperProtocol))
trace "onPeerConnected Whisper"
let
whisperNet = peer.networkState
whisperPeer = peer.state
let m = await peer.status(whisperVersion,
cast[uint64](whisperNet.config.powRequirement),
@(whisperNet.config.bloom),
whisperNet.config.isLightNode,
timeout = chronos.milliseconds(5000))
if m.protocolVersion == whisperVersion:
debug "Whisper peer", peer, whisperVersion
else:
raise newException(UselessPeerError, "Incompatible Whisper version")
whisperPeer.powRequirement = cast[float64](m.powConverted)
if m.bloom.len > 0:
if m.bloom.len != bloomSize:
raise newException(UselessPeerError, "Bloomfilter size mismatch")
else:
whisperPeer.bloom.bytesCopy(m.bloom)
else:
whisperPeer.bloom = fullBloom()
whisperPeer.isLightNode = m.isLightNode
if whisperPeer.isLightNode and whisperNet.config.isLightNode:
raise newException(UselessPeerError, "Two light nodes connected")
whisperPeer.received.init()
whisperPeer.trusted = false
whisperPeer.initialized = true
if not whisperNet.config.isLightNode:
traceAsyncErrors peer.run()
debug "Whisper peer initialized", peer
setEventHandlers(WhisperProtocol, WhisperPeerConnected, nil)
registerProtocol(WhisperProtocol)

View File

@ -9,12 +9,13 @@ var
node2: EthereumNode node2: EthereumNode
peer: Peer peer: Peer
let rng = newRng()
# This is not a good example of a fuzzing test and it would be much better # This is not a good example of a fuzzing test and it would be much better
# to mock more to get rid of anything sockets, async, etc. # to mock more to get rid of anything sockets, async, etc.
# However, it can and has provided reasonably quick results anyhow. # However, it can and has provided reasonably quick results anyhow.
init: init:
node1 = setupTestNode(eth, Whisper) node1 = setupTestNode(rng, eth, Whisper)
node2 = setupTestNode(eth, Whisper) node2 = setupTestNode(rng, eth, Whisper)
node2.startListening() node2.startListening()
peer = waitFor node1.rlpxConnect(newNode(node2.toENode())) peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))

View File

@ -14,6 +14,8 @@ import eth/keys, eth/keyfile/[keyfile], json, os, unittest
# Test vectors copied from # Test vectors copied from
# https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json # https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json
let rng = newRng()
var TestVectors = [ var TestVectors = [
%*{ %*{
"keyfile": { "keyfile": {
@ -114,7 +116,7 @@ suite "KeyFile test suite":
check: check:
seckey.error == KeyFileError.IncorrectMac seckey.error == KeyFileError.IncorrectMac
test "Create/Save/Load test": test "Create/Save/Load test":
var seckey0 = PrivateKey.random()[] var seckey0 = PrivateKey.random(rng[])
let jobject = createKeyFileJson(seckey0, "randompassword")[] let jobject = createKeyFileJson(seckey0, "randompassword")[]
check: check:

View File

@ -10,7 +10,7 @@
{.used.} {.used.}
import unittest import unittest
import eth/keys import eth/keys, bearssl
import nimcrypto/hash, nimcrypto/keccak, nimcrypto/utils import nimcrypto/hash, nimcrypto/keccak, nimcrypto/utils
from strutils import toLowerAscii from strutils import toLowerAscii
@ -25,6 +25,7 @@ proc compare(x: openarray[byte], y: openarray[byte]): bool =
break break
let message = "message".toBytes() let message = "message".toBytes()
let rng = newRng()
const const
pkbytes = "58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d" pkbytes = "58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d"
@ -80,7 +81,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
var s = PrivateKey.fromHex(pkbytes)[] var s = PrivateKey.fromHex(pkbytes)[]
var mhash = keccak256.digest(message) var mhash = keccak256.digest(message)
var signature = s.sign(message) var signature = s.sign(message)
var p = recover(signature, mhash)[] var p = recover(signature, SkMessage(mhash.data))[]
check: check:
s.toPublicKey() == p s.toPublicKey() == p
@ -130,7 +131,7 @@ suite "ECC/ECDSA/ECDHE tests suite":
test "EIP-55 100 addresses": test "EIP-55 100 addresses":
for i in 1..100: for i in 1..100:
var kp = KeyPair.random()[] var kp = KeyPair.random(rng[])
var chaddress = kp.pubkey.toChecksumAddress() var chaddress = kp.pubkey.toChecksumAddress()
var noaddress = kp.pubkey.toAddress() var noaddress = kp.pubkey.toAddress()
if noaddress != chaddress: if noaddress != chaddress:
@ -206,9 +207,9 @@ suite "ECC/ECDSA/ECDHE tests suite":
var s = PrivateKey.fromRaw(keccak256.digest("sec").data)[] var s = PrivateKey.fromRaw(keccak256.digest("sec").data)[]
var m = keccak256.digest("msg") var m = keccak256.digest("msg")
var sig = sign(s, m) var sig = sign(s, SkMessage(m.data))
var sersig = sig.toRaw() var sersig = sig.toRaw()
var key = recover(sig, m)[] var key = recover(sig, SkMessage(m.data))[]
var serkey = key.toRaw() var serkey = key.toRaw()
check: check:
compare(sersig, check1) == true compare(sersig, check1) == true
@ -217,18 +218,19 @@ suite "ECC/ECDSA/ECDHE tests suite":
test "ECDSA/100 signatures": test "ECDSA/100 signatures":
# signature test # signature test
for i in 1..100: for i in 1..100:
var m = PrivateKey.random()[].toRaw var m: array[32, byte]
var s = PrivateKey.random()[] brHmacDrbgGenerate(rng[], m)
var s = PrivateKey.random(rng[])
var key = s.toPublicKey() var key = s.toPublicKey()
let sig = sign(s, m) let sig = sign(s, SkMessage(m))
let rkey = recover(sig, m)[] let rkey = recover(sig, SkMessage(m))[]
check: check:
key == rkey key == rkey
test "KEYS/100 create/recovery keys": test "KEYS/100 create/recovery keys":
# key create/recovery test # key create/recovery test
for i in 1..100: for i in 1..100:
var s = PrivateKey.random()[] var s = PrivateKey.random(rng[])
var key = s.toPublicKey() var key = s.toPublicKey()
let rkey = PublicKey.fromRaw(key.toRaw())[] let rkey = PublicKey.fromRaw(key.toRaw())[]
check: check:
@ -237,9 +239,9 @@ suite "ECC/ECDSA/ECDHE tests suite":
test "ECDHE/100 shared secrets": test "ECDHE/100 shared secrets":
# ECDHE shared secret test # ECDHE shared secret test
for i in 1..100: for i in 1..100:
var aliceSecret = PrivateKey.random()[] var aliceSecret = PrivateKey.random(rng[])
var alicePublic = aliceSecret.toPublicKey() var alicePublic = aliceSecret.toPublicKey()
var bobSecret = PrivateKey.random()[] var bobSecret = PrivateKey.random(rng[])
var bobPublic = bobSecret.toPublicKey() var bobPublic = bobSecret.toPublicKey()
var secret1 = ecdhRaw(aliceSecret, bobPublic) var secret1 = ecdhRaw(aliceSecret, bobPublic)
var secret2 = ecdhRaw(bobSecret, alicePublic) var secret2 = ecdhRaw(bobSecret, alicePublic)

View File

@ -1,14 +1,13 @@
import import
testutils/unittests, stew/shims/net, nimcrypto, testutils/unittests, stew/shims/net, bearssl,
eth/[keys, rlp, trie/db], eth/[keys, rlp, trie/db],
eth/p2p/discoveryv5/[discovery_db, enr, node, types, routing_table, encoding], eth/p2p/discoveryv5/[discovery_db, enr, node, types, routing_table, encoding],
eth/p2p/discoveryv5/protocol as discv5_protocol eth/p2p/discoveryv5/protocol as discv5_protocol
proc localAddress*(port: int): Address = proc localAddress*(port: int): Address =
Address(ip: ValidIpAddress.init("127.0.0.1"), port: Port(port)) Address(ip: ValidIpAddress.init("127.0.0.1"), port: Port(port))
proc initDiscoveryNode*(privKey: PrivateKey, address: Address, proc initDiscoveryNode*(rng: ref BrHmacDrbgContext, privKey: PrivateKey, address: Address,
bootstrapRecords: openarray[Record] = [], bootstrapRecords: openarray[Record] = [],
localEnrFields: openarray[FieldPair] = []): localEnrFields: openarray[FieldPair] = []):
discv5_protocol.Protocol = discv5_protocol.Protocol =
@ -17,7 +16,7 @@ proc initDiscoveryNode*(privKey: PrivateKey, address: Address,
some(address.ip), some(address.ip),
address.port, address.port, address.port, address.port,
bootstrapRecords = bootstrapRecords, bootstrapRecords = bootstrapRecords,
localEnrFields = localEnrFields) localEnrFields = localEnrFields, rng = rng)
result.open() result.open()
@ -26,33 +25,34 @@ proc nodeIdInNodes*(id: NodeId, nodes: openarray[Node]): bool =
if id == n.id: return true if id == n.id: return true
# Creating a random packet with specific nodeid each time # Creating a random packet with specific nodeid each time
proc randomPacket*(tag: PacketTag): seq[byte] = proc randomPacket*(rng: var BrHmacDrbgContext, tag: PacketTag): seq[byte] =
var var
authTag: AuthTag authTag: AuthTag
msg: array[44, byte] msg: array[44, byte]
check randomBytes(authTag) == authTag.len brHmacDrbgGenerate(rng, authTag)
check randomBytes(msg) == msg.len brHmacDrbgGenerate(rng, msg)
result.add(tag) result.add(tag)
result.add(rlp.encode(authTag)) result.add(rlp.encode(authTag))
result.add(msg) result.add(msg)
proc generateNode*(privKey = PrivateKey.random()[], port: int = 20302, proc generateNode*(privKey: PrivateKey, port: int = 20302,
localEnrFields: openarray[FieldPair] = []): Node = localEnrFields: openarray[FieldPair] = []): Node =
let port = Port(port) let port = Port(port)
let enr = enr.Record.init(1, privKey, some(ValidIpAddress.init("127.0.0.1")), let enr = enr.Record.init(1, privKey, some(ValidIpAddress.init("127.0.0.1")),
port, port, localEnrFields).expect("Properly intialized private key") port, port, localEnrFields).expect("Properly intialized private key")
result = newNode(enr).expect("Properly initialized node") result = newNode(enr).expect("Properly initialized node")
proc nodeAtDistance*(n: Node, d: uint32): Node = proc nodeAtDistance*(n: Node, rng: var BrHmacDrbgContext, d: uint32): Node =
while true: while true:
let node = generateNode() let node = generateNode(PrivateKey.random(rng))
if logDist(n.id, node.id) == d: if logDist(n.id, node.id) == d:
return node return node
proc nodesAtDistance*(n: Node, d: uint32, amount: int): seq[Node] = proc nodesAtDistance*(
n: Node, rng: var BrHmacDrbgContext, d: uint32, amount: int): seq[Node] =
for i in 0..<amount: for i in 0..<amount:
result.add(nodeAtDistance(n, d)) result.add(nodeAtDistance(n, rng, d))
proc addSeenNode*(d: discv5_protocol.Protocol, n: Node): bool = proc addSeenNode*(d: discv5_protocol.Protocol, n: Node): bool =
# Add it as a seen node, warning: for testing convenience only! # Add it as a seen node, warning: for testing convenience only!

View File

@ -1,5 +1,5 @@
import import
unittest, chronos, nimcrypto, strutils, unittest, chronos, nimcrypto, strutils, bearssl,
eth/[keys, p2p], eth/p2p/[discovery, enode] eth/[keys, p2p], eth/p2p/[discovery, enode]
var nextPort = 30303 var nextPort = 30303
@ -15,17 +15,13 @@ proc startDiscoveryNode*(privKey: PrivateKey, address: Address,
result.open() result.open()
await result.bootstrap() await result.bootstrap()
proc setupBootNode*(): Future[ENode] {.async.} = proc setupTestNode*(
let rng: ref BrHmacDrbgContext,
bootNodeKey = KeyPair.random()[] capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode {.gcsafe.} =
bootNodeAddr = localAddress(30301) # Don't create new RNG every time in production code!
bootNode = await startDiscoveryNode(bootNodeKey.seckey, bootNodeAddr, @[]) let keys1 = KeyPair.random(rng[])
result = ENode(pubkey: bootNodeKey.pubkey, address: bootNodeAddr)
proc setupTestNode*(capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode =
let keys1 = KeyPair.random()[]
result = newEthereumNode(keys1, localAddress(nextPort), 1, nil, result = newEthereumNode(keys1, localAddress(nextPort), 1, nil,
addAllCapabilities = false) addAllCapabilities = false, rng = rng)
nextPort.inc nextPort.inc
for capability in capabilities: for capability in capabilities:
result.addCapability capability result.addCapability capability

View File

@ -199,6 +199,8 @@ const eip8data = [
"0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5") "0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5")
] ]
let rng = newRng()
proc testValue(s: string): string = proc testValue(s: string): string =
for item in data: for item in data:
if item[0] == s: if item[0] == s:
@ -212,26 +214,21 @@ proc testE8Value(s: string): string =
break break
suite "Ethereum P2P handshake test suite": suite "Ethereum P2P handshake test suite":
block: block:
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake = proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
if Initiator in flags: if Initiator in flags:
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[] let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
let epki = testValue("initiator_ephemeral_private_key") let epki = testValue("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epki)[] result.ephemeral = PrivateKey.fromHex(epki)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testValue("initiator_nonce"))) let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1] result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags: elif Responder in flags:
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[] let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
let epkr = testValue("receiver_ephemeral_private_key") let epkr = testValue("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[] result.ephemeral = PrivateKey.fromHex(epkr)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testValue("receiver_nonce"))) let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
result.responderNonce[0..^1] = nonce[0..^1] result.responderNonce[0..^1] = nonce[0..^1]
@ -241,7 +238,7 @@ suite "Ethereum P2P handshake test suite":
var m0 = newSeq[byte](initiator.authSize(false)) var m0 = newSeq[byte](initiator.authSize(false))
var k0 = 0 var k0 = 0
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0, 0, false).expect("auth success") rng[], responder.host.pubkey, m0, k0, 0, false).expect("auth success")
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext"))) var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
var expect2 = fromHex(stripSpaces(pyevmAuth)) var expect2 = fromHex(stripSpaces(pyevmAuth))
check: check:
@ -257,7 +254,7 @@ suite "Ethereum P2P handshake test suite":
let remoteHPubkey0 = initiator.host.pubkey let remoteHPubkey0 = initiator.host.pubkey
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success") rng[], responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success") responder.decodeAuthMessage(m0).expect("decode success")
check: check:
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1] responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
@ -273,9 +270,9 @@ suite "Ethereum P2P handshake test suite":
var k1 = 0 var k1 = 0
var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext"))) var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success") rng[], responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success") responder.decodeAuthMessage(m0).expect("decode success")
responder.ackMessage(m1, k1, 0, false).expect("ack success") responder.ackMessage(rng[], m1, k1, 0, false).expect("ack success")
check: check:
m1 == expect0 m1 == expect0
responder.initiatorNonce == initiator.initiatorNonce responder.initiatorNonce == initiator.initiatorNonce
@ -289,9 +286,9 @@ suite "Ethereum P2P handshake test suite":
var k1 = 0 var k1 = 0
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success") rng[], responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success") responder.decodeAuthMessage(m0).expect("decode success")
responder.ackMessage(m1, k1).expect("ack success") responder.ackMessage(rng[], m1, k1).expect("ack success")
initiator.decodeAckMessage(m1).expect("decode success") initiator.decodeAckMessage(m1).expect("decode success")
let remoteEPubkey0 = responder.ephemeral.pubkey let remoteEPubkey0 = responder.ephemeral.pubkey
let remoteHPubkey0 = responder.host.pubkey let remoteHPubkey0 = responder.host.pubkey
@ -333,23 +330,18 @@ suite "Ethereum P2P handshake test suite":
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake = proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
if Initiator in flags: if Initiator in flags:
let pk = PrivateKey.fromHex(testE8Value("initiator_private_key"))[] let pk = PrivateKey.fromHex(testE8Value("initiator_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
result.host.pubkey = result.host.seckey.toPublicKey()
let esec = testE8Value("initiator_ephemeral_private_key") let esec = testE8Value("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(esec)[] result.ephemeral = PrivateKey.fromHex(esec)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce"))) let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1] result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags: elif Responder in flags:
let pk = PrivateKey.fromHex(testE8Value("receiver_private_key"))[] let pk = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
let esec = testE8Value("receiver_ephemeral_private_key") let esec = testE8Value("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(esec)[] result.ephemeral = PrivateKey.fromHex(esec)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce"))) let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
result.responderNonce[0..^1] = nonce[0..^1] result.responderNonce[0..^1] = nonce[0..^1]
@ -436,12 +428,12 @@ suite "Ethereum P2P handshake test suite":
var k0 = 0 var k0 = 0
var k1 = 0 var k1 = 0
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success") rng[], responder.host.pubkey, m0, k0).expect("auth success")
m0.setLen(k0) m0.setLen(k0)
responder.decodeAuthMessage(m0).expect("decode success") responder.decodeAuthMessage(m0).expect("decode success")
check (EIP8 in responder.flags) == true check (EIP8 in responder.flags) == true
var m1 = newSeq[byte](responder.ackSize()) var m1 = newSeq[byte](responder.ackSize())
responder.ackMessage(m1, k1).expect("ack success") responder.ackMessage(rng[], m1, k1).expect("ack success")
m1.setLen(k1) m1.setLen(k1)
initiator.decodeAckMessage(m1).expect("decode success") initiator.decodeAckMessage(m1).expect("decode success")
var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets") var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets")
@ -458,11 +450,11 @@ suite "Ethereum P2P handshake test suite":
var k0 = 0 var k0 = 0
var k1 = 0 var k1 = 0
initiator.authMessage( initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success") rng[], responder.host.pubkey, m0, k0).expect("auth success")
m0.setLen(k0) m0.setLen(k0)
responder.decodeAuthMessage(m0).expect("auth success") responder.decodeAuthMessage(m0).expect("auth success")
var m1 = newSeq[byte](responder.ackSize()) var m1 = newSeq[byte](responder.ackSize())
responder.ackMessage(m1, k1).expect("ack success") responder.ackMessage(rng[], m1, k1).expect("ack success")
m1.setLen(k1) m1.setLen(k1)
initiator.decodeAckMessage(m1).expect("ack success") initiator.decodeAckMessage(m1).expect("ack success")

View File

@ -80,6 +80,8 @@ const data = [
e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8""") e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8""")
] ]
let rng = newRng()
proc testValue(s: string): string = proc testValue(s: string): string =
for item in data: for item in data:
if item[0] == s: if item[0] == s:
@ -90,20 +92,16 @@ suite "Ethereum RLPx encryption/decryption test suite":
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake = proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
if Initiator in flags: if Initiator in flags:
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[] let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
let epki = testValue("initiator_ephemeral_private_key") let epki = testValue("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epki)[] result.ephemeral = PrivateKey.fromHex(epki)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testValue("initiator_nonce"))) let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1] result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags: elif Responder in flags:
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[] let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()) result = Handshake.tryInit(rng[], pk.toKeyPair(), flags)[]
result = Handshake.tryInit(kp, flags)[]
let epkr = testValue("receiver_ephemeral_private_key") let epkr = testValue("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[] result.ephemeral = PrivateKey.fromHex(epkr)[].toKeyPair()
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()
let nonce = fromHex(stripSpaces(testValue("receiver_nonce"))) let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
result.responderNonce[0..^1] = nonce[0..^1] result.responderNonce[0..^1] = nonce[0..^1]
@ -174,12 +172,12 @@ suite "Ethereum RLPx encryption/decryption test suite":
var m0 = newSeq[byte](initiator.authSize()) var m0 = newSeq[byte](initiator.authSize())
var k0 = 0 var k0 = 0
var k1 = 0 var k1 = 0
check initiator.authMessage(responder.host.pubkey, check initiator.authMessage(rng[], responder.host.pubkey,
m0, k0).isOk m0, k0).isOk
m0.setLen(k0) m0.setLen(k0)
check responder.decodeAuthMessage(m0).isOk check responder.decodeAuthMessage(m0).isOk
var m1 = newSeq[byte](responder.ackSize()) var m1 = newSeq[byte](responder.ackSize())
check responder.ackMessage(m1, k1).isOk check responder.ackMessage(rng[], m1, k1).isOk
m1.setLen(k1) m1.setLen(k1)
check initiator.decodeAckMessage(m1).isOk check initiator.decodeAckMessage(m1).isOk

View File

@ -1,21 +1,23 @@
import import
chronos, chronicles, tables, stint, nimcrypto, testutils/unittests, chronos, chronicles, tables, stint, testutils/unittests,
stew/shims/net, eth/keys, stew/shims/net, eth/keys, bearssl,
eth/p2p/discoveryv5/[enr, node, types, routing_table, encoding], eth/p2p/discoveryv5/[enr, node, types, routing_table, encoding],
eth/p2p/discoveryv5/protocol as discv5_protocol, eth/p2p/discoveryv5/protocol as discv5_protocol,
./discv5_test_helper ./discv5_test_helper
procSuite "Discovery v5 Tests": procSuite "Discovery v5 Tests":
let rng = newRng()
asyncTest "GetNode": asyncTest "GetNode":
# TODO: This could be tested in just a routing table only context # TODO: This could be tested in just a routing table only context
let let
node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302)) node = initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20302))
targetNode = generateNode() targetNode = generateNode(PrivateKey.random(rng[]))
check node.addNode(targetNode) check node.addNode(targetNode)
for i in 0..<1000: for i in 0..<1000:
discard node.addNode(generateNode()) discard node.addNode(generateNode(PrivateKey.random(rng[])))
let n = node.getNode(targetNode.id) let n = node.getNode(targetNode.id)
check n.isSome() check n.isSome()
@ -25,10 +27,13 @@ procSuite "Discovery v5 Tests":
asyncTest "Node deletion": asyncTest "Node deletion":
let let
bootnode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301)) bootnode = initDiscoveryNode(
node1 = initDiscoveryNode(PrivateKey.random()[], localAddress(20302), rng, PrivateKey.random(rng[]), localAddress(20301))
node1 = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20302),
@[bootnode.localNode.record]) @[bootnode.localNode.record])
node2 = initDiscoveryNode(PrivateKey.random()[], localAddress(20303), node2 = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20303),
@[bootnode.localNode.record]) @[bootnode.localNode.record])
pong1 = await discv5_protocol.ping(node1, bootnode.localNode) pong1 = await discv5_protocol.ping(node1, bootnode.localNode)
pong2 = await discv5_protocol.ping(node1, node2.localNode) pong2 = await discv5_protocol.ping(node1, node2.localNode)
@ -51,13 +56,14 @@ procSuite "Discovery v5 Tests":
asyncTest "Handshake cleanup": asyncTest "Handshake cleanup":
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302)) let node = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20302))
var tag: PacketTag var tag: PacketTag
let a = localAddress(20303) let a = localAddress(20303)
for i in 0 ..< 5: for i in 0 ..< 5:
check randomBytes(tag) == tag.len brHmacDrbgGenerate(rng[], tag)
node.receive(a, randomPacket(tag)) node.receive(a, randomPacket(rng[], tag))
# Checking different nodeIds but same address # Checking different nodeIds but same address
check node.codec.handshakes.len == 5 check node.codec.handshakes.len == 5
@ -70,24 +76,26 @@ procSuite "Discovery v5 Tests":
await node.closeWait() await node.closeWait()
asyncTest "Handshake different address": asyncTest "Handshake different address":
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302)) let node = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20302))
var tag: PacketTag var tag: PacketTag
for i in 0 ..< 5: for i in 0 ..< 5:
let a = localAddress(20303 + i) let a = localAddress(20303 + i)
node.receive(a, randomPacket(tag)) node.receive(a, randomPacket(rng[], tag))
check node.codec.handshakes.len == 5 check node.codec.handshakes.len == 5
await node.closeWait() await node.closeWait()
asyncTest "Handshake duplicates": asyncTest "Handshake duplicates":
let node = initDiscoveryNode(PrivateKey.random()[], localAddress(20302)) let node = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20302))
var tag: PacketTag var tag: PacketTag
let a = localAddress(20303) let a = localAddress(20303)
for i in 0 ..< 5: for i in 0 ..< 5:
node.receive(a, randomPacket(tag)) node.receive(a, randomPacket(rng[], tag))
# Checking handshake duplicates # Checking handshake duplicates
check node.codec.handshakes.len == 1 check node.codec.handshakes.len == 1
@ -177,11 +185,11 @@ procSuite "Discovery v5 Tests":
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[] "a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
testNodeKey = PrivateKey.fromHex( testNodeKey = PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a618")[] "a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a618")[]
mainNode = initDiscoveryNode(mainNodeKey, localAddress(20301)) mainNode = initDiscoveryNode(rng, mainNodeKey, localAddress(20301))
testNode = initDiscoveryNode(testNodeKey, localAddress(20302)) testNode = initDiscoveryNode(rng, testNodeKey, localAddress(20302))
# logarithmic distance between mainNode and testNode is 256 # logarithmic distance between mainNode and testNode is 256
let nodes = nodesAtDistance(mainNode.localNode, dist, 10) let nodes = nodesAtDistance(mainNode.localNode, rng[], dist, 10)
for n in nodes: for n in nodes:
discard mainNode.addSeenNode(n) # for testing only! discard mainNode.addSeenNode(n) # for testing only!
@ -218,7 +226,7 @@ procSuite "Discovery v5 Tests":
check discovered.isOk check discovered.isOk
check discovered[].len == 0 check discovered[].len == 0
let moreNodes = nodesAtDistance(mainNode.localNode, dist, 10) let moreNodes = nodesAtDistance(mainNode.localNode, rng[], dist, 10)
for n in moreNodes: for n in moreNodes:
discard mainNode.addSeenNode(n) # for testing only! discard mainNode.addSeenNode(n) # for testing only!
@ -233,11 +241,12 @@ procSuite "Discovery v5 Tests":
asyncTest "FindNode with test table": asyncTest "FindNode with test table":
let mainNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301)) let mainNode =
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
# Generate 1000 random nodes and add to our main node's routing table # Generate 1000 random nodes and add to our main node's routing table
for i in 0..<1000: for i in 0..<1000:
discard mainNode.addSeenNode(generateNode()) # for testing only! discard mainNode.addSeenNode(generateNode(PrivateKey.random(rng[]))) # for testing only!
let let
neighbours = mainNode.neighbours(mainNode.localNode.id) neighbours = mainNode.neighbours(mainNode.localNode.id)
@ -247,7 +256,8 @@ procSuite "Discovery v5 Tests":
debug "Closest neighbour", closestDistance, id=closest.id.toHex() debug "Closest neighbour", closestDistance, id=closest.id.toHex()
let let
testNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20302), testNode = initDiscoveryNode(
rng, PrivateKey.random(rng[]), localAddress(20302),
@[mainNode.localNode.record]) @[mainNode.localNode.record])
discovered = await discv5_protocol.findNode(testNode, mainNode.localNode, discovered = await discv5_protocol.findNode(testNode, mainNode.localNode,
closestDistance) closestDistance)
@ -262,13 +272,14 @@ procSuite "Discovery v5 Tests":
const const
nodeCount = 17 nodeCount = 17
let bootNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301)) let bootNode =
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
bootNode.start() bootNode.start()
var nodes = newSeqOfCap[discv5_protocol.Protocol](nodeCount) var nodes = newSeqOfCap[discv5_protocol.Protocol](nodeCount)
nodes.add(bootNode) nodes.add(bootNode)
for i in 1 ..< nodeCount: for i in 1 ..< nodeCount:
nodes.add(initDiscoveryNode(PrivateKey.random()[], localAddress(20301 + i), nodes.add(initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301 + i),
@[bootNode.localNode.record])) @[bootNode.localNode.record]))
# Make sure all nodes have "seen" each other by forcing pings # Make sure all nodes have "seen" each other by forcing pings
@ -292,11 +303,13 @@ procSuite "Discovery v5 Tests":
asyncTest "Resolve target": asyncTest "Resolve target":
let let
mainNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301)) mainNode =
lookupNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20302)) initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
targetKey = PrivateKey.random()[] lookupNode =
initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20302))
targetKey = PrivateKey.random(rng[])
targetAddress = localAddress(20303) targetAddress = localAddress(20303)
targetNode = initDiscoveryNode(targetKey, targetAddress) targetNode = initDiscoveryNode(rng, targetKey, targetAddress)
targetId = targetNode.localNode.id targetId = targetNode.localNode.id
var targetSeqNum = targetNode.localNode.record.seqNum var targetSeqNum = targetNode.localNode.record.seqNum
@ -362,12 +375,12 @@ procSuite "Discovery v5 Tests":
asyncTest "Random nodes with enr field filter": asyncTest "Random nodes with enr field filter":
let let
lookupNode = initDiscoveryNode(PrivateKey.random()[], localAddress(20301)) lookupNode = initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(20301))
targetFieldPair = toFieldPair("test", @[byte 1,2,3,4]) targetFieldPair = toFieldPair("test", @[byte 1,2,3,4])
targetNode = generateNode(localEnrFields = [targetFieldPair]) targetNode = generateNode(PrivateKey.random(rng[]), localEnrFields = [targetFieldPair])
otherFieldPair = toFieldPair("test", @[byte 1,2,3,4,5]) otherFieldPair = toFieldPair("test", @[byte 1,2,3,4,5])
otherNode = generateNode(localEnrFields = [otherFieldPair]) otherNode = generateNode(PrivateKey.random(rng[]), localEnrFields = [otherFieldPair])
anotherNode = generateNode() anotherNode = generateNode(PrivateKey.random(rng[]))
check: check:
lookupNode.addNode(targetNode) lookupNode.addNode(targetNode)

View File

@ -5,6 +5,8 @@ import
# According to test vectors: # According to test vectors:
# https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md # https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md
let rng = newRng()
suite "Discovery v5 Packet Encodings": suite "Discovery v5 Packet Encodings":
# TODO: These tests are currently not completely representative for the code # TODO: These tests are currently not completely representative for the code
# and thus will not necessarily notice failures. Refactor/restructure code # and thus will not necessarily notice failures. Refactor/restructure code
@ -230,19 +232,19 @@ suite "Discovery v5 Additional":
test "AuthHeader encode/decode": test "AuthHeader encode/decode":
let let
privKey = PrivateKey.random()[] privKey = PrivateKey.random(rng[])
enrRec = enr.Record.init(1, privKey, none(ValidIpAddress), Port(9000), enrRec = enr.Record.init(1, privKey, none(ValidIpAddress), Port(9000),
Port(9000)).expect("Properly intialized private key") Port(9000)).expect("Properly intialized private key")
node = newNode(enrRec).expect("Properly initialized record") node = newNode(enrRec).expect("Properly initialized record")
nonce = hexToByteArray[authTagSize]("0x27b5af763c446acd2749fe8e") nonce = hexToByteArray[authTagSize]("0x27b5af763c446acd2749fe8e")
pubKey = PrivateKey.random()[].toPublicKey() pubKey = PrivateKey.random(rng[]).toPublicKey()
nodeId = pubKey.toNodeId() nodeId = pubKey.toNodeId()
idNonce = hexToByteArray[idNonceSize]( idNonce = hexToByteArray[idNonceSize](
"0xa77e3aa0c144ae7c0a3af73692b7d6e5b7a2fdc0eda16e8d5e6cb0d08e88dd04") "0xa77e3aa0c144ae7c0a3af73692b7d6e5b7a2fdc0eda16e8d5e6cb0d08e88dd04")
whoareyou = Whoareyou(idNonce: idNonce, recordSeq: 0, pubKey: pubKey) whoareyou = Whoareyou(idNonce: idNonce, recordSeq: 0, pubKey: pubKey)
c = Codec(localNode: node, privKey: privKey) c = Codec(localNode: node, privKey: privKey)
let (auth, _) = c.encodeAuthHeader(nodeId, nonce, whoareyou)[] let (auth, _) = encodeAuthHeader(rng[], c, nodeId, nonce, whoareyou)
var rlp = rlpFromBytes(auth) var rlp = rlpFromBytes(auth)
let authHeader = rlp.read(AuthHeader) let authHeader = rlp.read(AuthHeader)
var newNode: Node var newNode: Node

View File

@ -23,6 +23,8 @@ proc compare[A, B](x: openarray[A], y: openarray[B], s: int = 0): bool =
template offsetOf(a, b): int = template offsetOf(a, b): int =
cast[int](cast[uint](unsafeAddr b) - cast[uint](unsafeAddr a)) cast[int](cast[uint](unsafeAddr b) - cast[uint](unsafeAddr a))
let rng = newRng()
suite "ECIES test suite": suite "ECIES test suite":
test "ECIES structures alignment": test "ECIES structures alignment":
var header: EciesHeader var header: EciesHeader
@ -69,17 +71,17 @@ suite "ECIES test suite":
var encr = newSeq[byte](eciesEncryptedLength(len(m))) var encr = newSeq[byte](eciesEncryptedLength(len(m)))
var decr = newSeq[byte](len(m)) var decr = newSeq[byte](len(m))
var shmac = [0x13'u8, 0x13'u8] var shmac = [0x13'u8, 0x13'u8]
var s = PrivateKey.random()[] var s = PrivateKey.random(rng[])
var p = s.toPublicKey() var p = s.toPublicKey()
eciesEncrypt(plain, encr, p).expect("encryption should succeed") eciesEncrypt(rng[], plain, encr, p).expect("encryption should succeed")
eciesDecrypt(encr, decr, s).expect("decryption should succeed") eciesDecrypt(encr, decr, s).expect("decryption should succeed")
check: check:
# Without additional mac data # Without additional mac data
equalMem(addr m[0], addr decr[0], len(m)) equalMem(addr m[0], addr decr[0], len(m))
# With additional mac data # With additional mac data
eciesEncrypt(plain, encr, p, shmac).expect("encryption should succeed") eciesEncrypt(rng[], plain, encr, p, shmac).expect("encryption should succeed")
eciesDecrypt(encr, decr, s, shmac).expect("decryption should succeed") eciesDecrypt(encr, decr, s, shmac).expect("decryption should succeed")
check: check:

View File

@ -3,6 +3,8 @@ import
nimcrypto/utils, stew/shims/net, nimcrypto/utils, stew/shims/net,
eth/p2p/enode, eth/p2p/discoveryv5/enr, eth/keys eth/p2p/enode, eth/p2p/discoveryv5/enr, eth/keys
let rng = newRng()
suite "ENR": suite "ENR":
test "Serialization": test "Serialization":
var pk = PrivateKey.fromHex( var pk = PrivateKey.fromHex(
@ -33,7 +35,7 @@ suite "ENR":
test "Create from ENode address": test "Create from ENode address":
let let
keys = KeyPair.random()[] keys = KeyPair.random(rng[])
ip = ValidIpAddress.init("10.20.30.40") ip = ValidIpAddress.init("10.20.30.40")
enr = Record.init(100, keys.seckey, some(ip), Port(9000), Port(9000), @[])[] enr = Record.init(100, keys.seckey, some(ip), Port(9000), Port(9000), @[])[]
typedEnr = get enr.toTypedRecord() typedEnr = get enr.toTypedRecord()
@ -53,7 +55,7 @@ suite "ENR":
test "ENR without address": test "ENR without address":
let let
keys = KeyPair.random()[] keys = KeyPair.random(rng[])
enr = Record.init(100, keys.seckey, none(ValidIpAddress), Port(9000), Port(9000))[] enr = Record.init(100, keys.seckey, none(ValidIpAddress), Port(9000), Port(9000))[]
typedEnr = get enr.toTypedRecord() typedEnr = get enr.toTypedRecord()

View File

@ -52,11 +52,13 @@ p2pProtocol hah(version = 1,
onPeerDisconnected do (peer: Peer, reason: DisconnectionReason) {.gcsafe.}: onPeerDisconnected do (peer: Peer, reason: DisconnectionReason) {.gcsafe.}:
peer.networkState.count -= 1 peer.networkState.count -= 1
suite "Testing protocol handlers": suite "Testing protocol handlers":
asyncTest "Failing disconnection handler": asyncTest "Failing disconnection handler":
let bootENode = await setupBootNode() let rng = newRng()
var node1 = setupTestNode(abc, xyz)
var node2 = setupTestNode(abc, xyz) var node1 = setupTestNode(rng, abc, xyz)
var node2 = setupTestNode(rng, abc, xyz)
node2.startListening() node2.startListening()
let peer = await node1.rlpxConnect(newNode(node2.toENode())) let peer = await node1.rlpxConnect(newNode(node2.toENode()))
@ -71,8 +73,10 @@ suite "Testing protocol handlers":
node1.protocolState(xyz).count == 0 node1.protocolState(xyz).count == 0
asyncTest "Failing connection handler": asyncTest "Failing connection handler":
var node1 = setupTestNode(hah) let rng = newRng()
var node2 = setupTestNode(hah)
var node1 = setupTestNode(rng, hah)
var node2 = setupTestNode(rng, hah)
node2.startListening() node2.startListening()
let peer = await node1.rlpxConnect(newNode(node2.toENode())) let peer = await node1.rlpxConnect(newNode(node2.toENode()))
check: check:

View File

@ -3,17 +3,14 @@ import
eth/p2p, eth/p2p/rlpx_protocols/[whisper_protocol, eth_protocol], eth/p2p, eth/p2p/rlpx_protocols/[whisper_protocol, eth_protocol],
../p2p/p2p_test_helper ../p2p/p2p_test_helper
let rng = newRng()
var var
node1: EthereumNode node1 = setupTestNode(rng, eth, Whisper)
node2: EthereumNode node2 = setupTestNode(rng, eth, Whisper)
peer: Peer
node1 = setupTestNode(eth, Whisper)
node2 = setupTestNode(eth, Whisper)
node2.startListening() node2.startListening()
peer = waitFor node1.rlpxConnect(newNode(node2.toENode())) var peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
proc testThunk(payload: openArray[byte]) = proc testThunk(payload: openArray[byte]) =
var (msgId, msgData) = recvMsgMock(payload) var (msgId, msgData) = recvMsgMock(payload)

View File

@ -1,11 +1,14 @@
import import
unittest, unittest, bearssl,
eth/keys,
eth/p2p/discoveryv5/[routing_table, node], eth/p2p/discoveryv5/[routing_table, node],
./discv5_test_helper ./discv5_test_helper
suite "Routing Table Tests": suite "Routing Table Tests":
let rng = newRng()
test "Bucket splitting in range branch b=1": test "Bucket splitting in range branch b=1":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
@ -13,11 +16,11 @@ suite "Routing Table Tests":
for j in 0..5'u32: for j in 0..5'u32:
for i in 0..<BUCKET_SIZE: for i in 0..<BUCKET_SIZE:
check table.addNode(node.nodeAtDistance(256-j)) == nil check table.addNode(node.nodeAtDistance(rng[], 256-j)) == nil
check table.addNode(node.nodeAtDistance(256-j)) != nil check table.addNode(node.nodeAtDistance(rng[], 256-j)) != nil
test "Bucket splitting off range branch b=1": test "Bucket splitting off range branch b=1":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
@ -25,19 +28,19 @@ suite "Routing Table Tests":
# Add 16 nodes, distance 256 # Add 16 nodes, distance 256
for i in 0..<BUCKET_SIZE: for i in 0..<BUCKET_SIZE:
check table.addNode(node.nodeAtDistance(256)) == nil check table.addNode(node.nodeAtDistance(rng[], 256)) == nil
# This should split the bucket in the distance 256 branch, and the distance # This should split the bucket in the distance 256 branch, and the distance
# <=255 branch. But not add the node, as distance 256 bucket is already full # <=255 branch. But not add the node, as distance 256 bucket is already full
# and b=1 will not allow it to spit any further # and b=1 will not allow it to spit any further
check table.addNode(node.nodeAtDistance(256)) != nil check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
# This add should be allowed as it is on the branch where the own node's id # This add should be allowed as it is on the branch where the own node's id
# id belongs to. # id belongs to.
check table.addNode(node.nodeAtDistance(255)) == nil check table.addNode(node.nodeAtDistance(rng[], 255)) == nil
test "Bucket splitting off range branch b=2": test "Bucket splitting off range branch b=2":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 2, allow not in range branch to split once (2 buckets). # bitsPerHop = 2, allow not in range branch to split once (2 buckets).
@ -45,46 +48,46 @@ suite "Routing Table Tests":
# Add 16 nodes, distance 256 from `node`, but all with 2 bits shared prefix # Add 16 nodes, distance 256 from `node`, but all with 2 bits shared prefix
# among themselves. # among themselves.
let firstNode = node.nodeAtDistance(256) let firstNode = node.nodeAtDistance(rng[], 256)
check table.addNode(firstNode) == nil check table.addNode(firstNode) == nil
for n in 1..<BUCKET_SIZE: for n in 1..<BUCKET_SIZE:
check table.addNode(firstNode.nodeAtDistance(254)) == nil check table.addNode(firstNode.nodeAtDistance(rng[], 254)) == nil
# Add 16 more nodes with only 1 bit shared prefix with previous 16. This # Add 16 more nodes with only 1 bit shared prefix with previous 16. This
# should cause the initial bucket to split and and fill the second bucket # should cause the initial bucket to split and and fill the second bucket
# with the 16 new entries. # with the 16 new entries.
for n in 0..<BUCKET_SIZE: for n in 0..<BUCKET_SIZE:
check table.addNode(firstNode.nodeAtDistance(255)) == nil check table.addNode(firstNode.nodeAtDistance(rng[], 255)) == nil
# Adding another should fail as both buckets will be full and not be # Adding another should fail as both buckets will be full and not be
# allowed to split another time. # allowed to split another time.
check table.addNode(node.nodeAtDistance(256)) != nil check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
# And also when targetting one of the two specific buckets. # And also when targetting one of the two specific buckets.
check table.addNode(firstNode.nodeAtDistance(255)) != nil check table.addNode(firstNode.nodeAtDistance(rng[], 255)) != nil
check table.addNode(firstNode.nodeAtDistance(254)) != nil check table.addNode(firstNode.nodeAtDistance(rng[], 254)) != nil
# This add should be allowed as it is on the branch where the own node's id # This add should be allowed as it is on the branch where the own node's id
# id belongs to. # id belongs to.
check table.addNode(node.nodeAtDistance(255)) == nil check table.addNode(node.nodeAtDistance(rng[], 255)) == nil
test "Replacement cache": test "Replacement cache":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
# create a full bucket # create a full bucket
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE) let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
for n in bucketNodes: for n in bucketNodes:
check table.addNode(n) == nil check table.addNode(n) == nil
# create a full replacement cache # create a full replacement cache
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE) let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
for n in replacementNodes: for n in replacementNodes:
check table.addNode(n) != nil check table.addNode(n) != nil
# Add one more node to replacement (would drop first one) # Add one more node to replacement (would drop first one)
let lastNode = node.nodeAtDistance(256) let lastNode = node.nodeAtDistance(rng[], 256)
check table.addNode(lastNode) != nil check table.addNode(lastNode) != nil
# This should replace the last node in the bucket, with the last one of # This should replace the last node in the bucket, with the last one of
@ -101,7 +104,7 @@ suite "Routing Table Tests":
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone() check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
test "Empty bucket": test "Empty bucket":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
@ -110,29 +113,29 @@ suite "Routing Table Tests":
check table.nodeToRevalidate().isNil() check table.nodeToRevalidate().isNil()
# try to replace not existing node # try to replace not existing node
table.replaceNode(generateNode()) table.replaceNode(generateNode(PrivateKey.random(rng[])))
check table.len == 0 check table.len == 0
let addedNode = generateNode() let addedNode = generateNode(PrivateKey.random(rng[]))
check table.addNode(addedNode) == nil check table.addNode(addedNode) == nil
check table.len == 1 check table.len == 1
# try to replace not existing node # try to replace not existing node
table.replaceNode(generateNode()) table.replaceNode(generateNode(PrivateKey.random(rng[])))
check table.len == 1 check table.len == 1
table.replaceNode(addedNode) table.replaceNode(addedNode)
check table.len == 0 check table.len == 0
test "Empty replacement cache": test "Empty replacement cache":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
# create a full bucket TODO: no need to store bucketNodes # create a full bucket TODO: no need to store bucketNodes
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE) let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
for n in bucketNodes: for n in bucketNodes:
check table.addNode(n) == nil check table.addNode(n) == nil
@ -141,21 +144,21 @@ suite "Routing Table Tests":
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone() check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
test "Double add": test "Double add":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
let doubleNode = node.nodeAtDistance(256) let doubleNode = node.nodeAtDistance(rng[], 256)
# Try to add the node twice # Try to add the node twice
check table.addNode(doubleNode) == nil check table.addNode(doubleNode) == nil
check table.addNode(doubleNode) == nil check table.addNode(doubleNode) == nil
for n in 0..<BUCKET_SIZE-1: for n in 0..<BUCKET_SIZE-1:
check table.addNode(node.nodeAtDistance(256)) == nil check table.addNode(node.nodeAtDistance(rng[], 256)) == nil
check table.addNode(node.nodeAtDistance(256)) != nil check table.addNode(node.nodeAtDistance(rng[], 256)) != nil
# Check when adding again once the bucket is full # Check when adding again once the bucket is full
check table.addNode(doubleNode) == nil check table.addNode(doubleNode) == nil
@ -171,19 +174,19 @@ suite "Routing Table Tests":
table.len == 1 table.len == 1
test "Double replacement add": test "Double replacement add":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
# create a full bucket # create a full bucket
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE) let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
for n in bucketNodes: for n in bucketNodes:
check table.addNode(n) == nil check table.addNode(n) == nil
# create a full replacement cache # create a full replacement cache
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE) let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
for n in replacementNodes: for n in replacementNodes:
check table.addNode(n) != nil check table.addNode(n) != nil
@ -201,14 +204,14 @@ suite "Routing Table Tests":
check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone() check (table.getNode(bucketNodes[bucketNodes.high].id)).isNone()
test "Just seen": test "Just seen":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
# create a full bucket # create a full bucket
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE) let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
for n in bucketNodes: for n in bucketNodes:
check table.addNode(n) == nil check table.addNode(n) == nil
@ -221,19 +224,19 @@ suite "Routing Table Tests":
check (table.getNode(n.id)).isNone() check (table.getNode(n.id)).isNone()
test "Just seen replacement": test "Just seen replacement":
let node = generateNode() let node = generateNode(PrivateKey.random(rng[]))
var table: RoutingTable var table: RoutingTable
# bitsPerHop = 1 -> Split only the branch in range of own id # bitsPerHop = 1 -> Split only the branch in range of own id
table.init(node, 1) table.init(node, 1)
# create a full bucket # create a full bucket
let bucketNodes = node.nodesAtDistance(256, BUCKET_SIZE) let bucketNodes = node.nodesAtDistance(rng[], 256, BUCKET_SIZE)
for n in bucketNodes: for n in bucketNodes:
check table.addNode(n) == nil check table.addNode(n) == nil
# create a full replacement cache # create a full replacement cache
let replacementNodes = node.nodesAtDistance(256, REPLACEMENT_CACHE_SIZE) let replacementNodes = node.nodesAtDistance(rng[], 256, REPLACEMENT_CACHE_SIZE)
for n in replacementNodes: for n in replacementNodes:
check table.addNode(n) != nil check table.addNode(n) != nil

View File

@ -11,10 +11,12 @@ import
sequtils, options, unittest, tables, nimcrypto/hash, sequtils, options, unittest, tables, nimcrypto/hash,
eth/[keys, rlp], eth/p2p/rlpx_protocols/whisper/whisper_types as whisper eth/[keys, rlp], eth/p2p/rlpx_protocols/whisper/whisper_types as whisper
let rng = newRng()
suite "Whisper payload": suite "Whisper payload":
test "should roundtrip without keys": test "should roundtrip without keys":
let payload = Payload(payload: @[byte 0, 1, 2]) let payload = Payload(payload: @[byte 0, 1, 2])
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -26,7 +28,7 @@ suite "Whisper payload":
test "should roundtrip with symmetric encryption": test "should roundtrip with symmetric encryption":
var symKey: SymKey var symKey: SymKey
let payload = Payload(symKey: some(symKey), payload: @[byte 0, 1, 2]) let payload = Payload(symKey: some(symKey), payload: @[byte 0, 1, 2])
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get(), symKey = some(symKey)) let decoded = whisper.decode(encoded.get(), symKey = some(symKey))
check: check:
@ -36,10 +38,10 @@ suite "Whisper payload":
decoded.get().padding.get().len == 251 # 256 -1 -1 -3 decoded.get().padding.get().len == 251 # 256 -1 -1 -3
test "should roundtrip with signature": test "should roundtrip with signature":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(src: some(privKey), payload: @[byte 0, 1, 2]) let payload = Payload(src: some(privKey), payload: @[byte 0, 1, 2])
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -49,11 +51,11 @@ suite "Whisper payload":
decoded.get().padding.get().len == 186 # 256 -1 -1 -3 -65 decoded.get().padding.get().len == 186 # 256 -1 -1 -3 -65
test "should roundtrip with asymmetric encryption": test "should roundtrip with asymmetric encryption":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(dst: some(privKey.toPublicKey()), let payload = Payload(dst: some(privKey.toPublicKey()),
payload: @[byte 0, 1, 2]) payload: @[byte 0, 1, 2])
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get(), dst = some(privKey)) let decoded = whisper.decode(encoded.get(), dst = some(privKey))
check: check:
@ -74,7 +76,7 @@ suite "Whisper payload":
suite "Whisper payload padding": suite "Whisper payload padding":
test "should do max padding": test "should do max padding":
let payload = Payload(payload: repeat(byte 1, 254)) let payload = Payload(payload: repeat(byte 1, 254))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -84,10 +86,10 @@ suite "Whisper payload padding":
decoded.get().padding.get().len == 256 # as dataLen == 256 decoded.get().padding.get().len == 256 # as dataLen == 256
test "should do max padding with signature": test "should do max padding with signature":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 189)) let payload = Payload(src: some(privKey), payload: repeat(byte 1, 189))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -99,7 +101,7 @@ suite "Whisper payload padding":
test "should do min padding": test "should do min padding":
let payload = Payload(payload: repeat(byte 1, 253)) let payload = Payload(payload: repeat(byte 1, 253))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -109,10 +111,10 @@ suite "Whisper payload padding":
decoded.get().padding.get().len == 1 # as dataLen == 255 decoded.get().padding.get().len == 1 # as dataLen == 255
test "should do min padding with signature": test "should do min padding with signature":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 188)) let payload = Payload(src: some(privKey), payload: repeat(byte 1, 188))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -125,7 +127,7 @@ suite "Whisper payload padding":
test "should roundtrip custom padding": test "should roundtrip custom padding":
let payload = Payload(payload: repeat(byte 1, 10), let payload = Payload(payload: repeat(byte 1, 10),
padding: some(repeat(byte 2, 100))) padding: some(repeat(byte 2, 100)))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -138,7 +140,7 @@ suite "Whisper payload padding":
let padding: seq[byte] = @[] let padding: seq[byte] = @[]
let payload = Payload(payload: repeat(byte 1, 10), let payload = Payload(payload: repeat(byte 1, 10),
padding: some(padding)) padding: some(padding))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -147,10 +149,10 @@ suite "Whisper payload padding":
decoded.get().padding.isNone() decoded.get().padding.isNone()
test "should roundtrip custom padding with signature": test "should roundtrip custom padding with signature":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10), let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10),
padding: some(repeat(byte 2, 100))) padding: some(repeat(byte 2, 100)))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -162,10 +164,10 @@ suite "Whisper payload padding":
test "should roundtrip custom 0 padding with signature": test "should roundtrip custom 0 padding with signature":
let padding: seq[byte] = @[] let padding: seq[byte] = @[]
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10), let payload = Payload(src: some(privKey), payload: repeat(byte 1, 10),
padding: some(padding)) padding: some(padding))
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let decoded = whisper.decode(encoded.get()) let decoded = whisper.decode(encoded.get())
check: check:
@ -276,7 +278,7 @@ proc prepFilterTestMsg(pubKey = none[PublicKey](), symKey = none[SymKey](),
padding = none[seq[byte]]()): Message = padding = none[seq[byte]]()): Message =
let payload = Payload(dst: pubKey, symKey: symKey, src: src, let payload = Payload(dst: pubKey, symKey: symKey, src: src,
payload: @[byte 0, 1, 2], padding: padding) payload: @[byte 0, 1, 2], padding: padding)
let encoded = whisper.encode(payload) let encoded = whisper.encode(rng[], payload)
let env = Envelope(expiry: 1, ttl: 1, topic: topic, data: encoded.get(), let env = Envelope(expiry: 1, ttl: 1, topic: topic, data: encoded.get(),
nonce: 0) nonce: 0)
result = initMessage(env) result = initMessage(env)
@ -289,7 +291,7 @@ suite "Whisper filter":
var filters = initTable[string, Filter]() var filters = initTable[string, Filter]()
let filter = initFilter(symKey = some(symKey), topics = @[topic]) let filter = initFilter(symKey = some(symKey), topics = @[topic])
let filterId = filters.subscribeFilter(filter) let filterId = subscribeFilter(rng[], filters, filter)
notify(filters, msg) notify(filters, msg)
@ -300,14 +302,14 @@ suite "Whisper filter":
messages[0].dst.isNone() messages[0].dst.isNone()
test "should notify filter on message with asymmetric encryption": test "should notify filter on message with asymmetric encryption":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let topic = [byte 0, 0, 0, 0] let topic = [byte 0, 0, 0, 0]
let msg = prepFilterTestMsg(pubKey = some(privKey.toPublicKey()), let msg = prepFilterTestMsg(pubKey = some(privKey.toPublicKey()),
topic = topic) topic = topic)
var filters = initTable[string, Filter]() var filters = initTable[string, Filter]()
let filter = initFilter(privateKey = some(privKey), topics = @[topic]) let filter = initFilter(privateKey = some(privKey), topics = @[topic])
let filterId = filters.subscribeFilter(filter) let filterId = subscribeFilter(rng[], filters, filter)
notify(filters, msg) notify(filters, msg)
@ -318,14 +320,14 @@ suite "Whisper filter":
messages[0].dst.isSome() messages[0].dst.isSome()
test "should notify filter on message with signature": test "should notify filter on message with signature":
let privKey = PrivateKey.random()[] let privKey = PrivateKey.random(rng[])
let topic = [byte 0, 0, 0, 0] let topic = [byte 0, 0, 0, 0]
let msg = prepFilterTestMsg(src = some(privKey), topic = topic) let msg = prepFilterTestMsg(src = some(privKey), topic = topic)
var filters = initTable[string, Filter]() var filters = initTable[string, Filter]()
let filter = initFilter(src = some(privKey.toPublicKey()), let filter = initFilter(src = some(privKey.toPublicKey()),
topics = @[topic]) topics = @[topic])
let filterId = filters.subscribeFilter(filter) let filterId = subscribeFilter(rng[], filters, filter)
notify(filters, msg) notify(filters, msg)
@ -346,9 +348,9 @@ suite "Whisper filter":
var filters = initTable[string, Filter]() var filters = initTable[string, Filter]()
let let
filterId1 = filters.subscribeFilter( filterId1 = subscribeFilter(rng[], filters,
initFilter(topics = @[topic], powReq = 0.014492753623188406)) initFilter(topics = @[topic], powReq = 0.014492753623188406))
filterId2 = filters.subscribeFilter( filterId2 = subscribeFilter(rng[], filters,
initFilter(topics = @[topic], powReq = 0.014492753623188407)) initFilter(topics = @[topic], powReq = 0.014492753623188407))
notify(filters, msg) notify(filters, msg)
@ -366,8 +368,8 @@ suite "Whisper filter":
var filters = initTable[string, Filter]() var filters = initTable[string, Filter]()
let let
filterId1 = filters.subscribeFilter(initFilter(topics = @[topic1])) filterId1 = subscribeFilter(rng[], filters, initFilter(topics = @[topic1]))
filterId2 = filters.subscribeFilter(initFilter(topics = @[topic2])) filterId2 = subscribeFilter(rng[], filters, initFilter(topics = @[topic2]))
notify(filters, msg) notify(filters, msg)

View File

@ -8,7 +8,7 @@
# MIT license (LICENSE-MIT) # MIT license (LICENSE-MIT)
import import
sequtils, options, tables, chronos, testutils/unittests, sequtils, options, tables, chronos, testutils/unittests, bearssl,
eth/[keys, p2p], eth/p2p/rlpx_protocols/whisper_protocol, eth/p2p/peer_pool, eth/[keys, p2p], eth/p2p/rlpx_protocols/whisper_protocol, eth/p2p/peer_pool,
./p2p_test_helper ./p2p_test_helper
@ -20,8 +20,9 @@ let safeTTL = 5'u32
let waitInterval = messageInterval + 150.milliseconds let waitInterval = messageInterval + 150.milliseconds
procSuite "Whisper connections": procSuite "Whisper connections":
var node1 = setupTestNode(Whisper) let rng = newRng()
var node2 = setupTestNode(Whisper) var node1 = setupTestNode(rng, Whisper)
var node2 = setupTestNode(rng, Whisper)
node2.startListening() node2.startListening()
waitFor node1.peerPool.connectToNode(newNode(node2.toENode())) waitFor node1.peerPool.connectToNode(newNode(node2.toENode()))
asyncTest "Two peers connected": asyncTest "Two peers connected":
@ -29,8 +30,8 @@ procSuite "Whisper connections":
node1.peerPool.connectedNodes.len() == 1 node1.peerPool.connectedNodes.len() == 1
asyncTest "Filters with encryption and signing": asyncTest "Filters with encryption and signing":
let encryptKeyPair = KeyPair.random()[] let encryptKeyPair = KeyPair.random(rng[])
let signKeyPair = KeyPair.random()[] let signKeyPair = KeyPair.random(rng[])
var symKey: SymKey var symKey: SymKey
let topic = [byte 0x12, 0, 0, 0] let topic = [byte 0x12, 0, 0, 0]
var filters: seq[string] = @[] var filters: seq[string] = @[]
@ -294,7 +295,7 @@ procSuite "Whisper connections":
node1.unsubscribeFilter(filter) == true node1.unsubscribeFilter(filter) == true
asyncTest "Light node posting": asyncTest "Light node posting":
var ln1 = setupTestNode(Whisper) var ln1 = setupTestNode(rng, Whisper)
ln1.setLightNode(true) ln1.setLightNode(true)
await ln1.peerPool.connectToNode(newNode(node2.toENode())) await ln1.peerPool.connectToNode(newNode(node2.toENode()))
@ -313,8 +314,8 @@ procSuite "Whisper connections":
ln1.protocolState(Whisper).queue.items.len == 0 ln1.protocolState(Whisper).queue.items.len == 0
asyncTest "Connect two light nodes": asyncTest "Connect two light nodes":
var ln1 = setupTestNode(Whisper) var ln1 = setupTestNode(rng, Whisper)
var ln2 = setupTestNode(Whisper) var ln2 = setupTestNode(rng, Whisper)
ln1.setLightNode(true) ln1.setLightNode(true)
ln2.setLightNode(true) ln2.setLightNode(true)