Secp more refactor (#211)

* simplify some modules

* mark several modules with raises
* fix clearing of keys in auth.nim
* fix keyfile case dropping off
* fix keyfile stream storage
* uuid should be output in lowercase

* enode: simplify API
This commit is contained in:
Jacek Sieka 2020-04-06 18:24:15 +02:00 committed by GitHub
parent ac5bbe4d3d
commit 0b110f3287
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 783 additions and 856 deletions

View File

@ -7,8 +7,12 @@
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
{.push raises: [Defect].}
import nimcrypto/[bcmode, hmac, rijndael, pbkdf2, sha2, sysrand, utils, keccak],
eth/keys, json, uuid, os, strutils, streams
eth/keys, json, uuid, strutils, stew/result
export result
const
# Version 3 constants
@ -21,31 +25,26 @@ const
ScryptWorkFactor = 262_144
type
KeyFileStatus* = enum
Success, ## No Error
RandomError, ## Random generator error
UuidError, ## UUID generator error
BufferOverrun, ## Supplied buffer is too small
IncorrectDKLen, ## `dklen` parameter is 0 or more then MaxDKLen
MalformedError, ## JSON has incorrect structure
NotImplemented, ## Feature is not implemented
NotSupported, ## Feature is not supported
EmptyMac, ## `mac` parameter is zero length or not in
## hexadecimal form
EmptyCiphertext, ## `ciphertext` parameter is zero length or not in
## hexadecimal format
EmptySalt, ## `salt` parameter is zero length or not in
## hexadecimal format
EmptyIV, ## `cipherparams.iv` parameter is zero length or not in
## hexadecimal format
IncorrectIV, ## Size of IV vector is not equal to cipher block size
PrfNotSupported, ## PRF algorithm for PBKDF2 is not supported
KdfNotSupported, ## KDF algorithm is not supported
CipherNotSupported, ## `cipher` parameter is not supported
IncorrectMac, ## `mac` verification failed
IncorrectPrivateKey, ## incorrect private key
OsError, ## OS specific error
JsonError ## JSON encoder/decoder error
KeyFileError* = enum
RandomError = "kf: Random generator error"
UuidError = "kf: UUID generator error"
BufferOverrun = "kf: Supplied buffer is too small"
IncorrectDKLen = "kf: `dklen` parameter is 0 or more then MaxDKLen"
MalformedError = "kf: JSON has incorrect structure"
NotImplemented = "kf: Feature is not implemented"
NotSupported = "kf: Feature is not supported"
EmptyMac = "kf: `mac` parameter is zero length or not in hexadecimal form"
EmptyCiphertext = "kf: `ciphertext` parameter is zero length or not in hexadecimal format"
EmptySalt = "kf: `salt` parameter is zero length or not in hexadecimal format"
EmptyIV = "kf: `cipherparams.iv` parameter is zero length or not in hexadecimal format"
IncorrectIV = "kf: Size of IV vector is not equal to cipher block size"
PrfNotSupported = "kf: PRF algorithm for PBKDF2 is not supported"
KdfNotSupported = "kf: KDF algorithm is not supported"
CipherNotSupported = "kf: `cipher` parameter is not supported"
IncorrectMac = "kf: `mac` verification failed"
IncorrectPrivateKey = "kf: incorrect private key"
OsError = "kf: OS specific error"
JsonError = "kf: JSON encoder/decoder error"
KdfKind* = enum
PBKDF2, ## PBKDF2
@ -60,6 +59,11 @@ type
CipherNoSupport, ## Cipher not supported
AES128CTR ## AES-128-CTR
KfResult*[T] = Result[T, KeyFileError]
proc mapErrTo[T, E](r: Result[T, E], v: static KeyFileError): KfResult[T] =
r.mapErr(proc (e: E): KeyFileError = v)
const
SupportedHashes = [
"sha224", "sha256", "sha384", "sha512",
@ -109,103 +113,98 @@ proc deriveKey(password: string,
salt: string,
kdfkind: KdfKind,
hashkind: HashKind,
workfactor: int,
output: var openarray[byte]): KeyFileStatus =
if kdfkind == SCRYPT:
return NotImplemented
elif kdfkind == PBKDF2:
workfactor: int): KfResult[array[DKLen, byte]] =
if kdfkind == PBKDF2:
var output: array[DKLen, byte]
var c = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
case hashkind
of HashSHA2_224:
var ctx: HMAC[sha224]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA2_256:
var ctx: HMAC[sha256]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA2_384:
var ctx: HMAC[sha384]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA2_512:
var ctx: HMAC[sha512]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashKECCAK224:
var ctx: HMAC[keccak224]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashKECCAK256:
var ctx: HMAC[keccak256]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashKECCAK384:
var ctx: HMAC[keccak384]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashKECCAK512:
var ctx: HMAC[keccak512]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA3_224:
var ctx: HMAC[sha3_224]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA3_256:
var ctx: HMAC[sha3_256]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA3_384:
var ctx: HMAC[sha3_384]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
of HashSHA3_512:
var ctx: HMAC[sha3_512]
discard ctx.pbkdf2(password, salt, c, output)
result = Success
ok(output)
else:
result = PrfNotSupported
err(PrfNotSupported)
else:
err(NotImplemented)
proc encryptKey(seckey: PrivateKey,
cryptkind: CryptKind,
key: openarray[byte],
iv: openarray[byte],
crypttext: var openarray[byte]): KeyFileStatus =
if len(crypttext) != KeyLength:
return BufferOverrun
iv: openarray[byte]): KfResult[array[KeyLength, byte]] =
if cryptkind == AES128CTR:
var crypttext: array[KeyLength, byte]
var ctx: CTR[aes128]
ctx.init(toOpenArray(key, 0, 15), iv)
ctx.encrypt(seckey.toRaw(), crypttext)
ctx.clear()
result = Success
ok(crypttext)
else:
result = NotImplemented
err(NotImplemented)
proc decryptKey(ciphertext: openarray[byte],
cryptkind: CryptKind,
key: openarray[byte],
iv: openarray[byte],
plaintext: var openarray[byte]): KeyFileStatus =
if len(ciphertext) != len(plaintext):
return BufferOverrun
iv: openarray[byte]): KfResult[array[KeyLength, byte]] =
if cryptkind == AES128CTR:
if len(iv) != aes128.sizeBlock:
return IncorrectIV
return err(IncorrectIV)
var plaintext: array[KeyLength, byte]
var ctx: CTR[aes128]
ctx.init(toOpenArray(key, 0, 15), iv)
ctx.decrypt(ciphertext, plaintext)
ctx.clear()
result = Success
ok(plaintext)
else:
result = NotImplemented
err(NotImplemented)
proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int,
outjson: var JsonNode): KeyFileStatus =
proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int): KfResult[JsonNode] =
if kdfkind == SCRYPT:
var wf = if workfactor == 0: ScryptWorkFactor else: workfactor
outjson = %*
let wf = if workfactor == 0: ScryptWorkFactor else: workfactor
ok(%*
{
"dklen": DKLen,
"n": wf,
@ -213,19 +212,19 @@ proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int,
"p": ScryptP,
"salt": salt
}
result = Success
)
elif kdfkind == PBKDF2:
var wf = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
outjson = %*
let wf = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
ok(%*
{
"dklen": DKLen,
"c": wf,
"prf": "hmac-sha256",
"salt": salt
}
result = Success
)
else:
result = NotImplemented
err(NotImplemented)
proc decodeHex(m: string): seq[byte] =
if len(m) > 0:
@ -254,11 +253,10 @@ proc compareMac(m1: openarray[byte], m2: openarray[byte]): bool =
proc createKeyFileJson*(seckey: PrivateKey,
password: string,
outjson: var JsonNode,
version: int = 3,
cryptkind: CryptKind = AES128CTR,
kdfkind: KdfKind = PBKDF2,
workfactor: int = 0): KeyFileStatus =
workfactor: int = 0): KfResult[JsonNode] =
## Create JSON object with keyfile structure.
##
## ``seckey`` - private key, which will be stored
@ -270,30 +268,24 @@ proc createKeyFileJson*(seckey: PrivateKey,
## ``kdfkind`` - algorithm for key deriviation function (default is PBKDF2)
## ``workfactor`` - Key deriviation function work factor, 0 is to use
## default workfactor.
var res: KeyFileStatus
var iv: array[aes128.sizeBlock, byte]
var ciphertext: array[KeyLength, byte]
var salt: array[SaltSize, byte]
var saltstr = newString(SaltSize)
var u: UUID
if randomBytes(iv) != aes128.sizeBlock:
return RandomError
return err(RandomError)
if randomBytes(salt) != SaltSize:
return RandomError
return err(RandomError)
copyMem(addr saltstr[0], addr salt[0], SaltSize)
if uuidGenerate(u) != 1:
return UuidError
if kdfkind != PBKDF2:
return NotImplemented
var dkey = newSeq[byte](DKLen)
res = deriveKey(password, saltstr, kdfkind, HashSHA2_256,
workfactor, dkey)
if res != Success:
return res
res = encryptKey(seckey, cryptkind, dkey, iv, ciphertext)
if res != Success:
return res
let u = ? uuidGenerate().mapErrTo(UuidError)
if kdfkind != PBKDF2:
return err(NotImplemented)
let
dkey = ? deriveKey(password, saltstr, kdfkind, HashSHA2_256, workfactor)
ciphertext = ? encryptKey(seckey, cryptkind, dkey, iv)
var ctx: keccak256
ctx.init()
ctx.update(toOpenArray(dkey, 16, 31))
@ -301,14 +293,11 @@ proc createKeyFileJson*(seckey: PrivateKey,
var mac = ctx.finish()
ctx.clear()
var params: JsonNode
res = kdfParams(kdfkind, toHex(salt, true), workfactor, params)
if res != Success:
return res
let params = ? kdfParams(kdfkind, toHex(salt, true), workfactor)
outjson = %*
ok(%*
{
"address": seckey.toPublicKey().tryGet().toAddress(false),
"address": (? seckey.toPublicKey().mapErrTo(IncorrectPrivateKey)).toAddress(false),
"crypto": {
"cipher": $cryptkind,
"cipherparams": {
@ -322,34 +311,29 @@ proc createKeyFileJson*(seckey: PrivateKey,
"id": $u,
"version": version
}
result = Success
)
proc decodeKeyFileJson*(j: JsonNode,
password: string,
seckey: var PrivateKey): KeyFileStatus =
password: string): KfResult[PrivateKey] =
## Decode private key into ``seckey`` from keyfile json object ``j`` using
## password string ``password``.
var
res: KeyFileStatus
plaintext: array[KeyLength, byte]
var crypto = j.getOrDefault("crypto")
if isNil(crypto):
return MalformedError
return err(MalformedError)
var kdf = crypto.getOrDefault("kdf")
if isNil(kdf):
return MalformedError
return err(MalformedError)
var cipherparams = crypto.getOrDefault("cipherparams")
if isNil(cipherparams):
return MalformedError
return err(MalformedError)
if kdf.getStr() == "pbkdf2":
var params = crypto.getOrDefault("kdfparams")
if isNil(params):
return MalformedError
return err(MalformedError)
var salt = decodeSalt(params.getOrDefault("salt").getStr())
var ciphertext = decodeHex(crypto.getOrDefault("ciphertext").getStr())
@ -358,29 +342,26 @@ proc decodeKeyFileJson*(j: JsonNode,
var iv = decodeHex(cipherparams.getOrDefault("iv").getStr())
if len(salt) == 0:
return EmptySalt
return err(EmptySalt)
if len(ciphertext) == 0:
return EmptyCiphertext
return err(EmptyCiphertext)
if len(mactext) == 0:
return EmptyMac
return err(EmptyMac)
if cryptkind == CipherNoSupport:
return CipherNotSupported
return err(CipherNotSupported)
var dklen = params.getOrDefault("dklen").getInt()
var c = params.getOrDefault("c").getInt()
var hash = getPrfHash(params.getOrDefault("prf").getStr())
if hash == HashNoSupport:
return PrfNotSupported
return err(PrfNotSupported)
if dklen == 0 or dklen > MaxDKLen:
return IncorrectDKLen
return err(IncorrectDKLen)
if len(ciphertext) != KeyLength:
return IncorrectPrivateKey
return err(IncorrectPrivateKey)
var dkey = newSeq[byte](dklen)
res = deriveKey(password, salt, PBKDF2, hash, c, dkey)
if res != Success:
return res
let dkey = ? deriveKey(password, salt, PBKDF2, hash, c)
var ctx: keccak256
ctx.init()
@ -388,51 +369,39 @@ proc decodeKeyFileJson*(j: JsonNode,
ctx.update(ciphertext)
var mac = ctx.finish()
if not compareMac(mac.data, mactext):
return IncorrectMac
return err(IncorrectMac)
res = decryptKey(ciphertext, cryptkind, dkey, iv, plaintext)
if res != Success:
return res
try:
seckey = PrivateKey.fromRaw(plaintext).tryGet()
except CatchableError:
return IncorrectPrivateKey
result = Success
let plaintext = ? decryptKey(ciphertext, cryptkind, dkey, iv)
PrivateKey.fromRaw(plaintext).mapErrTo(IncorrectPrivateKey)
else:
return KdfNotSupported
err(KdfNotSupported)
proc loadKeyFile*(pathname: string,
password: string,
seckey: var PrivateKey): KeyFileStatus =
password: string): KfResult[PrivateKey] =
## Load and decode private key ``seckey`` from file with pathname
## ``pathname``, using password string ``password``.
var data: JsonNode
var stream = newFileStream(pathname)
if isNil(stream):
return OsError
try:
data = parseFile(pathname)
result = Success
except CatchableError:
result = JsonError
finally:
stream.close()
data = json.parseFile(pathname)
except JsonParsingError:
return err(JsonError)
except Exception: # json raises Exception
return err(OsError)
if result == Success:
result = decodeKeyFileJson(data, password, seckey)
decodeKeyFileJson(data, password)
proc saveKeyFile*(pathname: string,
jobject: JsonNode): KeyFileStatus =
jobject: JsonNode): KfResult[void] =
## Save JSON object ``jobject`` to file with pathname ``pathname``.
var
f: File
if not f.open(pathname, fmWrite):
return OsError
return err(OsError)
try:
f.write($jobject)
result = Success
ok()
except CatchableError:
result = OsError
err(OsError)
finally:
f.close()

View File

@ -16,40 +16,47 @@
## - ``FreeBSD``, ``OpenBSD``, ``NetBSD``,
## ``DragonflyBSD`` - using `uuid_create()`.
import nimcrypto/utils, stew/endians2
{.push raises: [Defect].}
import stew/[byteutils, endians2, result]
from nimcrypto import stripSpaces
export result
type
UUIDException = object of CatchableError
UUID* = object
## Represents UUID object
data*: array[16, byte]
proc raiseInvalidUuid() =
raise newException(UUIDException, "Invalid UUID!")
UuidResult*[T] = Result[T, cstring]
proc uuidFromString*(s: string): UUID =
proc uuidFromString*(s: string): UuidResult[UUID] =
## Convert string representation of UUID into UUID object.
if len(s) != 36:
raiseInvalidUuid()
return err("uuid: length must be 36 bytes")
for i in 0..<len(s):
if s[i] notin {'A'..'F', '0'..'9', 'a'..'f', '-'}:
raiseInvalidUuid()
var d = fromHex(stripSpaces(s))
var
a = uint32.fromBytesBE(d.toOpenArray(0, 3))
b = uint16.fromBytesBE(d.toOpenArray(4, 5))
c = uint16.fromBytesBE(d.toOpenArray(6, 7))
return err("uuid: invalid characters")
try:
var d = hexToSeqByte(stripSpaces(s))
var
a = uint32.fromBytesBE(d.toOpenArray(0, 3))
b = uint16.fromBytesBE(d.toOpenArray(4, 5))
c = uint16.fromBytesBE(d.toOpenArray(6, 7))
copyMem(addr result.data[0], addr a, 4)
copyMem(addr result.data[4], addr b, 2)
copyMem(addr result.data[6], addr c, 2)
copyMem(addr result.data[8], addr d[8], 8)
var ret: UUID
copyMem(addr ret.data[0], addr a, 4)
copyMem(addr ret.data[4], addr b, 2)
copyMem(addr ret.data[6], addr c, 2)
copyMem(addr ret.data[8], addr d[8], 8)
ok(ret)
except CatchableError:
err("uuid: cannot parse hex string")
proc uuidToString*(u: UUID, lowercase: bool = false): string =
proc uuidToString*(u: UUID): string =
## Convert UUID object into string representation.
## You can use ``lowercase`` flag to specify letter case
## of output string.
## UUID are lowercase, per RFC4122
result = newStringOfCap(38)
var d: array[8, byte]
var
@ -60,22 +67,22 @@ proc uuidToString*(u: UUID, lowercase: bool = false): string =
copyMem(addr d[4], addr b, 2)
copyMem(addr d[6], addr c, 2)
result.add(toHex(toOpenArray(d, 0, 3), lowercase))
result.add(toHex(toOpenArray(d, 0, 3)))
result.add("-")
result.add(toHex(toOpenArray(d, 4, 5), lowercase))
result.add(toHex(toOpenArray(d, 4, 5)))
result.add("-")
result.add(toHex(toOpenArray(d, 6, 7), lowercase))
result.add(toHex(toOpenArray(d, 6, 7)))
result.add("-")
result.add(toHex(toOpenArray(u.data, 8, 9), lowercase))
result.add(toHex(toOpenArray(u.data, 8, 9)))
result.add("-")
result.add(toHex(toOpenArray(u.data, 10, 15), lowercase))
result.add(toHex(toOpenArray(u.data, 10, 15)))
proc `$`*(u: UUID): string {.inline.} =
proc `$`*(u: UUID): string =
## Convert UUID object to lowercase string representation.
uuidToString(u, true)
uuidToString(u)
when defined(nimdoc):
proc uuidGenerate*(output: var UUID): int
proc uuidGenerate*(): UuidResult[UUID]
## Generates new unique UUID and store it to `output`.
##
## Return 1 on success, and 0 on failure
@ -85,9 +92,10 @@ when defined(posix):
proc uuidGenerateRandom(a: pointer)
{.importc: "uuid_generate_random", header: "uuid/uuid.h".}
proc uuidGenerate*(output: var UUID): int =
proc uuidGenerate*(): UuidResult[UUID] =
var output: UUID
uuidGenerateRandom(cast[pointer](addr output))
result = 1
ok(output)
elif defined(freebsd) or defined(netbsd) or defined(openbsd) or
defined(dragonflybsd):
@ -95,13 +103,14 @@ when defined(posix):
proc uuidCreate(a: pointer, s: ptr uint32)
{.importc: "uuid_create", header: "uuid.h".}
proc uuidGenerate*(output: var UUID): int =
proc uuidGenerate*(): UuidResult[UUID] =
var status: uint32 = 0
var output: UUID
uuidCreate(cast[pointer](addr output), addr status)
if status == 0:
result = 1
ok(output)
else:
result = 0
err("uuid: uuid_create failed")
elif defined(linux) or defined(android):
import posix, os, nimcrypto/sysrand
@ -124,39 +133,44 @@ when defined(posix):
break
discard posix.close(fd)
proc uuidGenerate*(output: var UUID): int =
result = 0
proc uuidGenerate*(): UuidResult[UUID] =
var buffer = newString(37)
if uuidRead(buffer, 36) == 36:
buffer.setLen(36)
output = uuidFromString(buffer)
result = 1
uuidFromString(buffer)
else:
var output: UUID
if randomBytes(output.data) == sizeof(output.data):
result = 1
ok(output)
else:
err("uuid: cannot get random bytes")
else:
import nimcrypto/sysrand
proc uuidGenerate*(output: var UUID): int =
proc uuidGenerate*(): UuidResult[UUID] =
var output: UUID
if randomBytes(output.data) == sizeof(output.data):
result = 1
ok(output)
else:
result = 0
err("uuid: cannot get random bytes")
elif defined(windows):
proc UuidCreate(p: pointer): int32
{.stdcall, dynlib: "rpcrt4", importc: "UuidCreate".}
proc uuidGenerate*(output: var UUID): int =
proc uuidGenerate*(): UuidResult[UUID] =
var output: UUID
if UuidCreate(cast[pointer](addr output)) == 0:
return 1
ok(output)
else:
return 0
err("uuid: UuidCreate failed")
elif not defined(nimdoc):
import nimcrypto/sysrand
proc uuidGenerate*(output: var UUID): int =
proc uuidGenerate*(): UuidResult[UUID] =
var output: UUID
if randomBytes(output.data) == sizeof(output.data):
result = 1
ok(output)
else:
result = 0
err("uuid: cannot get random bytes")

View File

@ -74,7 +74,7 @@ proc processIncoming(server: StreamServer,
node.peerPool.addPeer(peer)
proc listeningAddress*(node: EthereumNode): ENode =
return initENode(node.keys.pubKey, node.address)
node.toENode()
proc startListening*(node: EthereumNode) =
# TODO allow binding to specific IP / IPv6 / etc

View File

@ -10,12 +10,16 @@
## This module implements Ethereum authentication
{.push raises: [Defect].}
import eth/[keys, rlp], nimcrypto
import ecies
import stew/[byteutils, endians2]
import stew/[byteutils, endians2, result]
export result
const
SupportedRlpxVersion* = 4
SupportedRlpxVersion* = 4'u8
PlainAuthMessageV4Length* = 194
AuthMessageV4Length* = 307
PlainAuthMessageEIP8Length = 169
@ -49,18 +53,17 @@ type
Responder, ## `Handshake` owner is connection responder
Eip8 ## Flag indicates that EIP-8 handshake is used
AuthStatus* = enum
Success, ## Operation was successful
RandomError, ## Could not obtain random data
EcdhError, ## ECDH shared secret could not be calculated
BufferOverrun, ## Buffer overrun error
SignatureError, ## Signature could not be obtained
EciesError, ## ECIES encryption/decryption error
InvalidPubKey, ## Invalid public key
InvalidAuth, ## Invalid Authentication message
InvalidAck, ## Invalid Authentication ACK message
RlpError, ## Error while decoding RLP stream
IncompleteError ## Data incomplete error
AuthError* = enum
RandomError = "auth: could not obtain random data"
EcdhError = "auth: ECDH shared secret could not be calculated"
BufferOverrun = "auth: buffer overrun"
SignatureError = "auth: signature could not be obtained"
EciesError = "auth: ECIES encryption/decryption error"
InvalidPubKey = "auth: invalid public key"
InvalidAuth = "auth: invalid Authentication message"
InvalidAck = "auth: invalid Authentication ACK message"
RlpError = "auth: error while decoding RLP stream"
IncompleteError = "auth: data incomplete"
Handshake* = object
version*: uint8 ## protocol version
@ -79,80 +82,94 @@ type
egressMac*: keccak256
ingressMac*: keccak256
AuthException* = object of CatchableError
AuthResult*[T] = Result[T, AuthError]
template toa(a, b, c: untyped): untyped =
toOpenArray((a), (b), (b) + (c) - 1)
proc sxor[T](a: var openarray[T], b: openarray[T]) {.inline.} =
doAssert(len(a) == len(b))
proc `xor`[N: static int](a, b: array[N, byte]): array[N, byte] =
for i in 0 ..< len(a):
a[i] = a[i] xor b[i]
result[i] = a[i] xor b[i]
proc newHandshake*(flags: set[HandshakeFlag] = {Initiator},
version: int = SupportedRlpxVersion): Handshake =
proc mapErrTo[T, E](r: Result[T, E], v: static AuthError): AuthResult[T] =
r.mapErr(proc (e: E): AuthError = v)
proc tryInit*(
T: type Handshake, host: KeyPair, flags: set[HandshakeFlag] = {Initiator},
version: uint8 = SupportedRlpxVersion): AuthResult[T] =
## Create new `Handshake` object.
result.version = byte(version and 0xFF)
result.flags = flags
result.ephemeral = KeyPair.random().tryGet()
var
initiatorNonce: Nonce
responderNonce: Nonce
expectedLength: int
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
if Initiator in flags:
result.expectedLength = AckMessageV4Length
if randomBytes(result.initiatorNonce) != len(result.initiatorNonce):
raise newException(AuthException, "Could not obtain random data!")
expectedLength = AckMessageV4Length
if randomBytes(initiatorNonce) != len(initiatorNonce):
return err(RandomError)
else:
result.expectedLength = AuthMessageV4Length
if randomBytes(result.responderNonce) != len(result.responderNonce):
raise newException(AuthException, "Could not obtain random data!")
expectedLength = AuthMessageV4Length
if randomBytes(responderNonce) != len(responderNonce):
return err(RandomError)
return ok(T(
version: version,
flags: flags,
host: host,
ephemeral: ephemeral,
initiatorNonce: initiatorNonce,
responderNonce: responderNonce,
expectedLength: expectedLength
))
proc authMessagePreEIP8(h: var Handshake,
pubkey: PublicKey,
output: var openarray[byte],
outlen: var int,
flag: int = 0,
encrypt: bool = true): AuthStatus =
flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create plain pre-EIP8 authentication message.
var
buffer: array[PlainAuthMessageV4Length, byte]
flagb: byte
header: ptr AuthMessageV4
outlen = 0
flagb = byte(flag)
header = cast[ptr AuthMessageV4](addr buffer[0])
var secret = ecdhRaw(h.host.seckey, pubkey)
if secret.isErr:
return(EcdhError)
var xornonce = h.initiatorNonce
xornonce.sxor(secret[].data)
secret[].clear()
let sig = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
if sig.isErr:
return(SignatureError)
let header = cast[ptr AuthMessageV4](addr buffer[0])
var secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
let xornonce = secret.data xor h.initiatorNonce
secret.clear()
let signature = ? sign(
h.ephemeral.seckey, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.remoteHPubkey = pubkey
header.signature = sig[].toRaw()
header.signature = signature.toRaw()
header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data
header.pubkey = h.host.pubkey.toRaw()
header.nonce = h.initiatorNonce
header.flag = flagb
header.flag = flag
if encrypt:
if len(output) < AuthMessageV4Length:
return(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
return(EciesError)
return err(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
return err(EciesError)
outlen = AuthMessageV4Length
result = Success
else:
if len(output) < PlainAuthMessageV4Length:
return(BufferOverrun)
return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], PlainAuthMessageV4Length)
outlen = PlainAuthMessageV4Length
result = Success
ok()
proc authMessageEIP8(h: var Handshake,
pubkey: PublicKey,
output: var openarray[byte],
outlen: var int,
flag: int = 0,
encrypt: bool = true): AuthStatus =
flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create EIP8 authentication message.
var
buffer: array[PlainAuthMessageMaxEIP8, byte]
@ -160,17 +177,17 @@ proc authMessageEIP8(h: var Handshake,
doAssert(EIP8 in h.flags)
outlen = 0
var secret = ecdhRaw(h.host.seckey, pubkey)
if secret.isErr:
return(EcdhError)
var xornonce = h.initiatorNonce
xornonce.sxor(secret[].data)
secret[].clear()
var sig = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
if sig.isErr:
return(SignatureError)
var
secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
xornonce = secret.data xor h.initiatorNonce
secret.clear()
let signature = ? sign(
h.ephemeral.seckey, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.remoteHPubkey = pubkey
var payload = rlp.encodeList(sig[].toRaw(),
var payload = rlp.encodeList(signature.toRaw(),
h.host.pubkey.toRaw(),
h.initiatorNonce,
[byte(h.version)])
@ -178,65 +195,67 @@ proc authMessageEIP8(h: var Handshake,
let pencsize = eciesEncryptedLength(len(payload))
while true:
if randomBytes(addr padsize, 1) != 1:
return(RandomError)
return err(RandomError)
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
break
# It is possible to make packet size constant by uncommenting this line
# padsize = 24
var wosize = pencsize + int(padsize)
let wosize = pencsize + int(padsize)
let fullsize = wosize + 2
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
int(padsize))) != int(padsize):
return(RandomError)
return err(RandomError)
if encrypt:
copyMem(addr buffer[0], addr payload[0], len(payload))
if len(output) < fullsize:
return(BufferOverrun)
return err(BufferOverrun)
let wosizeBE = uint16(wosize).toBytesBE()
output[0..<2] = wosizeBE
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
toa(output, 2, wosize), pubkey,
toa(output, 0, 2)) != EciesStatus.Success:
return(EciesError)
toa(output, 0, 2)).isErr:
return err(EciesError)
outlen = fullsize
else:
let plainsize = len(payload) + int(padsize)
if len(output) < plainsize:
return(BufferOverrun)
return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], plainsize)
outlen = plainsize
result = Success
ok()
proc ackMessagePreEIP8(h: var Handshake,
output: var openarray[byte],
outlen: var int,
flag: int = 0,
encrypt: bool = true): AuthStatus =
flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create plain pre-EIP8 authentication ack message.
var buffer: array[PlainAckMessageV4Length, byte]
outlen = 0
var header = cast[ptr AckMessageV4](addr buffer[0])
let header = cast[ptr AckMessageV4](addr buffer[0])
header.pubkey = h.ephemeral.pubkey.toRaw()
header.nonce = h.responderNonce
header.flag = byte(flag)
header.flag = flag
if encrypt:
if len(output) < AckMessageV4Length:
return(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
return(EciesError)
return err(BufferOverrun)
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
return err(EciesError)
outlen = AckMessageV4Length
else:
if len(output) < PlainAckMessageV4Length:
return(BufferOverrun)
return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], PlainAckMessageV4Length)
outlen = PlainAckMessageV4Length
result = Success
ok()
proc ackMessageEIP8(h: var Handshake,
output: var openarray[byte],
outlen: var int,
flag: int = 0,
encrypt: bool = true): AuthStatus =
flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create EIP8 authentication ack message.
var
buffer: array[PlainAckMessageMaxEIP8, byte]
@ -250,34 +269,35 @@ proc ackMessageEIP8(h: var Handshake,
let pencsize = eciesEncryptedLength(len(payload))
while true:
if randomBytes(addr padsize, 1) != 1:
return(RandomError)
return err(RandomError)
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
break
# It is possible to make packet size constant by uncommenting this line
# padsize = 0
var wosize = pencsize + int(padsize)
let wosize = pencsize + int(padsize)
let fullsize = wosize + 2
if int(padsize) > 0:
if randomBytes(toa(buffer, PlainAckMessageEIP8Length,
int(padsize))) != int(padsize):
return(RandomError)
return err(RandomError)
copyMem(addr buffer[0], addr payload[0], len(payload))
if encrypt:
if len(output) < fullsize:
return(BufferOverrun)
return err(BufferOverrun)
output[0..<2] = uint16(wosize).toBytesBE()
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
toa(output, 2, wosize), h.remoteHPubkey,
toa(output, 0, 2)) != EciesStatus.Success:
return(EciesError)
toa(output, 0, 2)).isErr:
return err(EciesError)
outlen = fullsize
else:
let plainsize = len(payload) + int(padsize)
if len(output) < plainsize:
return(BufferOverrun)
return err(BufferOverrun)
copyMem(addr output[0], addr buffer[0], plainsize)
outlen = plainsize
result = Success
ok()
template authSize*(h: Handshake, encrypt: bool = true): int =
## Get number of bytes needed to store AuthMessage.
@ -295,55 +315,52 @@ template ackSize*(h: Handshake, encrypt: bool = true): int =
proc authMessage*(h: var Handshake, pubkey: PublicKey,
output: var openarray[byte],
outlen: var int, flag: int = 0,
encrypt: bool = true): AuthStatus {.inline.} =
outlen: var int, flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create new AuthMessage for specified `pubkey` and store it inside
## of `output`, size of generated AuthMessage will stored in `outlen`.
if EIP8 in h.flags:
result = authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
else:
result = authMessagePreEIP8(h, pubkey, output, outlen, flag, encrypt)
authMessagePreEIP8(h, pubkey, output, outlen, flag, encrypt)
proc ackMessage*(h: var Handshake, output: var openarray[byte],
outlen: var int, flag: int = 0,
encrypt: bool = true): AuthStatus =
outlen: var int, flag: byte = 0,
encrypt: bool = true): AuthResult[void] =
## Create new AckMessage and store it inside of `output`, size of generated
## AckMessage will stored in `outlen`.
if EIP8 in h.flags:
result = ackMessageEIP8(h, output, outlen, flag, encrypt)
ackMessageEIP8(h, output, outlen, flag, encrypt)
else:
result = ackMessagePreEIP8(h, output, outlen, flag, encrypt)
ackMessagePreEIP8(h, output, outlen, flag, encrypt)
proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void] =
## Decodes V4 AuthMessage.
var
buffer: array[PlainAuthMessageV4Length, byte]
doAssert(Responder in h.flags)
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
return(EciesError)
var header = cast[ptr AuthMessageV4](addr buffer[0])
let pubkey = PublicKey.fromRaw(header.pubkey)
if pubkey.isErr:
return(InvalidPubKey)
var secret = ecdhRaw(h.host.seckey, pubkey[])
if secret.isErr:
return(EcdhError)
var xornonce = header.nonce
xornonce.sxor(secret[].data)
secret[].clear()
let sig = Signature.fromRaw(header.signature)
if sig.isErr:
return(SignatureError)
let remoteEPubkey = recover(sig[], SkMessage(data: xornonce))
if remoteEPubkey.isErr:
return(SignatureError)
h.remoteEPubkey = remoteEPubkey[]
h.initiatorNonce = header.nonce
h.remoteHPubkey = pubkey[]
result = Success
if eciesDecrypt(m, buffer, h.host.seckey).isErr:
return err(EciesError)
proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
let
header = cast[ptr AuthMessageV4](addr buffer[0])
pubkey = ? PublicKey.fromRaw(header.pubkey).mapErrTo(InvalidPubKey)
signature = ? Signature.fromRaw(header.signature).mapErrTo(SignatureError)
var secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
let xornonce = secret.data xor header.nonce
secret.clear()
h.remoteEPubkey =
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.initiatorNonce = header.nonce
h.remoteHPubkey = pubkey
ok()
proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthResult[void] =
## Decodes EIP-8 AuthMessage.
var
nonce: Nonce
@ -351,155 +368,138 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
let size = uint16.fromBytesBE(m)
h.expectedLength = int(size) + 2
if h.expectedLength > len(m):
return(IncompleteError)
return err(IncompleteError)
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
toa(m, 0, 2)) != EciesStatus.Success:
return(EciesError)
toa(m, 0, 2)).isErr:
return err(EciesError)
try:
var reader = rlpFromBytes(buffer.toRange())
if not reader.isList() or reader.listLen() < 4:
return(InvalidAuth)
return err(InvalidAuth)
if reader.listElem(0).blobLen != RawSignatureSize:
return(InvalidAuth)
return err(InvalidAuth)
if reader.listElem(1).blobLen != RawPublicKeySize:
return(InvalidAuth)
return err(InvalidAuth)
if reader.listElem(2).blobLen != KeyLength:
return(InvalidAuth)
return err(InvalidAuth)
if reader.listElem(3).blobLen != 1:
return(InvalidAuth)
return err(InvalidAuth)
var signatureBr = reader.listElem(0).toBytes()
var pubkeyBr = reader.listElem(1).toBytes()
var nonceBr = reader.listElem(2).toBytes()
var versionBr = reader.listElem(3).toBytes()
let pubkey = PublicKey.fromRaw(pubkeyBr.toOpenArray())
if pubkey.isErr:
return(InvalidPubKey)
copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength)
var secret = ecdhRaw(h.host.seckey, pubkey[])
if secret.isErr:
return(EcdhError)
var xornonce = nonce
xornonce.sxor(secret[].data)
secret[].clear()
let sig = Signature.fromRaw(signatureBr.toOpenArray())
if sig.isErr:
return(SignatureError)
let remoteEPubkey = recover(sig[], SkMessage(data: xornonce))
if remoteEPubkey.isErr:
return(SignatureError)
h.remoteEPubkey = remoteEPubkey[]
h.initiatorNonce = nonce
h.remoteHPubkey = pubkey[]
h.version = cast[ptr byte](versionBr.baseAddr)[]
result = Success
except CatchableError:
result = RlpError
proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
let pubkey =
? PublicKey.fromRaw(pubkeyBr.toOpenArray()).mapErrTo(InvalidPubKey)
copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength)
var secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
let xornonce = nonce xor secret.data
secret.clear()
let signature =
? Signature.fromRaw(signatureBr.toOpenArray()).mapErrTo(SignatureError)
h.remoteEPubkey =
? recover(signature, SkMessage(data: xornonce)).mapErrTo(SignatureError)
h.initiatorNonce = nonce
h.remoteHPubkey = pubkey
h.version = cast[ptr byte](versionBr.baseAddr)[]
ok()
except CatchableError:
err(RlpError)
proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthResult[void] =
## Decodes EIP-8 AckMessage.
let size = uint16.fromBytesBE(m)
h.expectedLength = 2 + int(size)
if h.expectedLength > len(m):
return(IncompleteError)
return err(IncompleteError)
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
toa(m, 0, 2)) != EciesStatus.Success:
return(EciesError)
toa(m, 0, 2)).isErr:
return err(EciesError)
try:
var reader = rlpFromBytes(buffer.toRange())
if not reader.isList() or reader.listLen() < 3:
return(InvalidAck)
return err(InvalidAck)
if reader.listElem(0).blobLen != RawPublicKeySize:
return(InvalidAck)
return err(InvalidAck)
if reader.listElem(1).blobLen != KeyLength:
return(InvalidAck)
return err(InvalidAck)
if reader.listElem(2).blobLen != 1:
return(InvalidAck)
return err(InvalidAck)
let pubkeyBr = reader.listElem(0).toBytes()
let nonceBr = reader.listElem(1).toBytes()
let versionBr = reader.listElem(2).toBytes()
let remoteEPubkey = PublicKey.fromRaw(pubkeyBr.toOpenArray())
if remoteEPubkey.isErr:
return(InvalidPubKey)
h.remoteEPubkey = remoteEPubkey[]
h.remoteEPubkey =
? PublicKey.fromRaw(pubkeyBr.toOpenArray()).mapErrTo(InvalidPubKey)
copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength)
h.version = cast[ptr byte](versionBr.baseAddr)[]
result = Success
except CatchableError:
result = RlpError
proc decodeAckMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
ok()
except CatchableError:
err(RlpError)
proc decodeAckMessageV4(h: var Handshake, m: openarray[byte]): AuthResult[void] =
## Decodes V4 AckMessage.
var
buffer: array[PlainAckMessageV4Length, byte]
doAssert(Initiator in h.flags)
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
return(EciesError)
var header = cast[ptr AckMessageV4](addr buffer[0])
let remoteEPubkey = PublicKey.fromRaw(header.pubkey)
if remoteEPubkey.isErr:
return(InvalidPubKey)
h.remoteEPubkey = remoteEPubkey[]
h.responderNonce = header.nonce
result = Success
proc decodeAuthMessage*(h: var Handshake, input: openarray[byte]): AuthStatus =
if eciesDecrypt(m, buffer, h.host.seckey).isErr:
return err(EciesError)
var header = cast[ptr AckMessageV4](addr buffer[0])
h.remoteEPubkey = ? PublicKey.fromRaw(header.pubkey).mapErrTo(InvalidPubKey)
h.responderNonce = header.nonce
ok()
proc decodeAuthMessage*(h: var Handshake, input: openarray[byte]): AuthResult[void] =
## Decodes AuthMessage from `input`.
if len(input) < AuthMessageV4Length:
result = IncompleteError
elif len(input) == AuthMessageV4Length:
var res = h.decodeAuthMessageV4(input)
if res != Success:
res = h.decodeAuthMessageEip8(input)
if res != Success:
result = res
else:
h.flags.incl(EIP8)
result = Success
else:
result = Success
else:
result = h.decodeAuthMessageEip8(input)
if result == Success:
h.flags.incl(EIP8)
return err(IncompleteError)
proc decodeAckMessage*(h: var Handshake, input: openarray[byte]): AuthStatus =
if len(input) == AuthMessageV4Length:
let res = h.decodeAuthMessageV4(input)
if res.isOk(): return res
let res = h.decodeAuthMessageEip8(input)
if res.isOk():
h.flags.incl(EIP8)
res
proc decodeAckMessage*(h: var Handshake, input: openarray[byte]): AuthResult[void] =
## Decodes AckMessage from `input`.
if len(input) < AckMessageV4Length:
return(IncompleteError)
elif len(input) == AckMessageV4Length:
var res = h.decodeAckMessageV4(input)
if res != Success:
res = h.decodeAckMessageEip8(input)
if res != Success:
result = res
else:
h.flags.incl(EIP8)
result = Success
else:
result = Success
else:
result = h.decodeAckMessageEip8(input)
if result == Success:
h.flags.incl(EIP8)
return err(IncompleteError)
if len(input) == AckMessageV4Length:
let res = h.decodeAckMessageV4(input)
if res.isOk(): return res
proc getSecrets*(h: Handshake, authmsg: openarray[byte],
ackmsg: openarray[byte],
secret: var ConnectionSecret): AuthStatus =
let res = h.decodeAckMessageEip8(input)
if res.isOk(): h.flags.incl(EIP8)
res
proc getSecrets*(
h: Handshake, authmsg: openarray[byte],
ackmsg: openarray[byte]): AuthResult[ConnectionSecret] =
## Derive secrets from handshake `h` using encrypted AuthMessage `authmsg` and
## encrypted AckMessage `ackmsg`.
var
ctx0: keccak256
ctx1: keccak256
mac1: MDigest[256]
xornonce: Nonce
secret: ConnectionSecret
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
var shsec = ecdhRaw(h.ephemeral.seckey, h.remoteEPubkey)
if shsec.isErr:
return(EcdhError)
var shsec = ? ecdhRaw(h.ephemeral.seckey, h.remoteEPubkey).mapErrTo(EcdhError)
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
ctx0.init()
@ -508,36 +508,36 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
ctx1.update(h.initiatorNonce)
mac1 = ctx1.finish()
ctx1.clear()
ctx0.update(shsec[].data)
ctx0.update(shsec.data)
ctx0.update(mac1.data)
mac1 = ctx0.finish()
# aes-secret = keccak(ecdhe-secret || shared-secret)
ctx0.init()
ctx0.update(shsec[].data)
ctx0.update(shsec.data)
ctx0.update(mac1.data)
mac1 = ctx0.finish()
# mac-secret = keccak(ecdhe-secret || aes-secret)
ctx0.init()
ctx0.update(shsec[].data)
ctx0.update(shsec.data)
ctx0.update(mac1.data)
secret.aesKey = mac1.data
mac1 = ctx0.finish()
secret.macKey = mac1.data
shsec[].clear()
burnMem(shsec)
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)
xornonce = mac1.data
xornonce.sxor(h.responderNonce)
var xornonce = mac1.data xor h.responderNonce
ctx0.init()
ctx0.update(xornonce)
ctx0.update(authmsg)
# ingress-mac = keccak256(mac-secret ^ initiator-nonce || auth-recvd-ack)
xornonce = secret.macKey
xornonce.sxor(h.initiatorNonce)
xornonce = secret.macKey xor h.initiatorNonce
ctx1.init()
ctx1.update(xornonce)
ctx1.update(ackmsg)
@ -552,4 +552,5 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
ctx0.clear()
ctx1.clear()
result = Success
ok(secret)

View File

@ -11,11 +11,12 @@
import
times,
chronos, stint, nimcrypto, chronicles,
eth/common/eth_types_json_serialization, eth/[keys, rlp],
kademlia, enode
eth/[keys, rlp],
kademlia, enode,
stew/result
export
Node
Node, result
logScope:
topics = "discovery"
@ -45,7 +46,8 @@ type
DiscProtocolError* = object of CatchableError
const MaxDgramSize = 1280
DiscResult*[T] = Result[T, cstring]
const MinListLen: array[CommandId, int] = [4, 3, 2, 2]
proc append*(w: var RlpWriter, a: IpAddress) =
@ -71,19 +73,22 @@ proc pack(cmdId: CommandId, payload: BytesRange, pk: PrivateKey): Bytes =
let msgHash = keccak256.digest(signature & encodedData)
result = @(msgHash.data) & signature & encodedData
proc validateMsgHash(msg: Bytes, msgHash: var MDigest[256]): bool =
proc validateMsgHash(msg: Bytes): DiscResult[MDigest[256]] =
if msg.len > HEAD_SIZE:
msgHash.data[0 .. ^1] = msg.toOpenArray(0, msgHash.data.high)
result = msgHash == keccak256.digest(msg.toOpenArray(MAC_SIZE, msg.high))
var ret: MDigest[256]
ret.data[0 .. ^1] = msg.toOpenArray(0, ret.data.high)
if ret == keccak256.digest(msg.toOpenArray(MAC_SIZE, msg.high)):
ok(ret)
else:
err("disc: invalid message hash")
else:
err("disc: msg missing hash")
proc recoverMsgPublicKey(msg: Bytes, pk: var PublicKey): bool =
if msg.len > HEAD_SIZE:
let sig = Signature.fromRaw(msg.toOpenArray(MAC_SIZE, HEAD_SIZE))
if sig.isOk():
let pubkey = recover(sig[], msg.toOpenArray(HEAD_SIZE, msg.high))
if pubkey.isOk():
pk = pubkey[]
return true
proc recoverMsgPublicKey(msg: openArray[byte]): DiscResult[PublicKey] =
if msg.len <= HEAD_SIZE:
return err("disc: can't get public key")
let sig = ? Signature.fromRaw(msg.toOpenArray(MAC_SIZE, HEAD_SIZE))
recover(sig, msg.toOpenArray(HEAD_SIZE, msg.high))
proc unpack(msg: Bytes): tuple[cmdId: CommandId, payload: Bytes] =
# Check against possible RangeError
@ -231,17 +236,17 @@ proc expirationValid(cmdId: CommandId, rlpEncodedPayload: seq[byte]):
proc receive*(d: DiscoveryProtocol, a: Address, msg: Bytes) {.gcsafe.} =
## Can raise `DiscProtocolError` and all of `RlpError`
# Note: export only needed for testing
var msgHash: MDigest[256]
if validateMsgHash(msg, msgHash):
var remotePubkey: PublicKey
if recoverMsgPublicKey(msg, remotePubkey):
let msgHash = validateMsgHash(msg)
if msgHash.isOk():
let remotePubkey = recoverMsgPublicKey(msg)
if remotePubkey.isOk:
let (cmdId, payload) = unpack(msg)
if expirationValid(cmdId, payload):
let node = newNode(remotePubkey, a)
let node = newNode(remotePubkey[], a)
case cmdId
of cmdPing:
d.recvPing(node, msgHash)
d.recvPing(node, msgHash[])
of cmdPong:
d.recvPong(node, payload)
of cmdNeighbours:
@ -251,14 +256,13 @@ proc receive*(d: DiscoveryProtocol, a: Address, msg: Bytes) {.gcsafe.} =
else:
trace "Received msg already expired", cmdId, a
else:
error "Wrong public key from ", a
notice "Wrong public key from ", a, err = remotePubkey.error
else:
error "Wrong msg mac from ", a
notice "Wrong msg mac from ", a
proc processClient(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async, gcsafe.} =
var proto = getUserData[DiscoveryProtocol](transp)
var buf: seq[byte]
try:
# TODO: Maybe here better to use `peekMessage()` to avoid allocation,
# but `Bytes` object is just a simple seq[byte], and `ByteRange` object
@ -309,16 +313,14 @@ when isMainModule:
block:
let m = hexToSeqByte"79664bff52ee17327b4a2d8f97d8fb32c9244d719e5038eb4f6b64da19ca6d271d659c3ad9ad7861a928ca85f8d8debfbe6b7ade26ad778f2ae2ba712567fcbd55bc09eb3e74a893d6b180370b266f6aaf3fe58a0ad95f7435bf3ddf1db940d20102f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2"
var msgHash: MDigest[256]
doAssert(validateMsgHash(m, msgHash))
var remotePubkey: PublicKey
doAssert(recoverMsgPublicKey(m, remotePubkey))
discard validateMsgHash(m).expect("valid hash")
var remotePubkey = recoverMsgPublicKey(m).expect("valid key")
let (cmdId, payload) = unpack(m)
doAssert(payload == hexToSeqByte"f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2")
doAssert(cmdId == cmdPong)
doAssert(remotePubkey == PublicKey.fromHex(
"78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d"))[]
"78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d")[])
let privKey = PrivateKey.fromHex("a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
@ -332,7 +334,7 @@ when isMainModule:
var bootnodes = newSeq[ENode]()
for item in LOCAL_BOOTNODES:
bootnodes.add(initENode(item))
bootnodes.add(ENode.fromString(item)[])
let listenPort = Port(30310)
var address = Address(udpPort: listenPort, tcpPort: listenPort)

View File

@ -2,6 +2,8 @@ import
std/[tables, options], nimcrypto, stint, chronicles,
types, node, enr, hkdf, ../enode, eth/[rlp, keys]
export keys
const
idNoncePrefix = "discovery-id-nonce"
keyAgreementPrefix = "discovery v5 key agreement"
@ -103,12 +105,11 @@ proc makeAuthHeader(c: Codec, toId: NodeID, nonce: array[gcmNonceSize, byte],
if challenge.recordSeq < ln.record.seqNum:
resp.record = ln.record
let ephKey = PrivateKey.random().tryGet()
let ephPubkey = ephKey.toPublicKey().tryGet().toRaw
let ephKeys = KeyPair.random().tryGet()
resp.signature = c.signIDNonce(challenge.idNonce, ephPubkey).toRaw
resp.signature = c.signIDNonce(challenge.idNonce, ephKeys.pubkey.toRaw).toRaw
deriveKeys(ln.id, toId, ephKey, challenge.pubKey, challenge.idNonce,
deriveKeys(ln.id, toId, ephKeys.seckey, challenge.pubKey, challenge.idNonce,
handshakeSecrets)
let respRlp = rlp.encode(resp)
@ -117,7 +118,7 @@ proc makeAuthHeader(c: Codec, toId: NodeID, nonce: array[gcmNonceSize, byte],
let respEnc = encryptGCM(handshakeSecrets.authRespKey, zeroNonce, respRLP, [])
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
scheme: authSchemeName, ephemeralKey: ephPubkey, response: respEnc)
scheme: authSchemeName, ephemeralKey: ephKeys.pubkey.toRaw, response: respEnc)
rlp.encode(header)
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =

View File

@ -6,6 +6,8 @@ import
nimcrypto, stew/base64,
eth/[rlp, keys], ../enode
export options
const
maxEnrSize = 300
minRlpListLen = 4 # for signature, seqId, "id" key, id
@ -163,13 +165,12 @@ proc get*(r: Record, key: string, T: type): T =
else:
raise newException(KeyError, "Key not found in ENR: " & key)
proc get*(r: Record, pubKey: var PublicKey): bool =
proc get*(r: Record, T: type PublicKey): Option[T] {.raises: [Defect].} =
var pubkeyField: Field
if r.getField("secp256k1", pubkeyField) and pubkeyField.kind == kBytes:
let pk = PublicKey.fromRaw(pubkeyField.bytes)
if pk.isOk:
pubKey = pk[]
return true
return some pk[]
proc tryGet*(r: Record, key: string, T: type): Option[T] =
try:
@ -197,12 +198,12 @@ proc toTypedRecord*(r: Record): Option[TypedRecord] =
return some(tr)
proc verifySignatureV4(r: Record, sigData: openarray[byte], content: seq[byte]): bool =
var publicKey: PublicKey
if r.get(publicKey):
let publicKey = r.get(PublicKey)
if publicKey.isSome:
let sig = SignatureNR.fromRaw(sigData)
if sig.isOk:
var h = keccak256.digest(content)
return verify(sig[], h, publicKey)
return verify(sig[], h, publicKey.get)
proc verifySignature(r: Record): bool =
var rlp = rlpFromBytes(r.raw.toRange)

View File

@ -21,10 +21,10 @@ proc newNode*(enode: ENode, r: Record): Node =
record: r)
proc newNode*(uriString: string): Node =
newNode initENode(uriString)
newNode ENode.fromString(uriString).tryGet()
proc newNode*(pk: PublicKey, address: Address): Node =
newNode initENode(pk, address)
newNode ENode(pubkey: pk, address: address)
proc newNode*(r: Record): Node =
# TODO: Handle IPv6
@ -48,7 +48,7 @@ proc newNode*(r: Record): Node =
warn "Could not recover public key", err = pk.error
return
result = newNode(initENode(pk[], a))
result = newNode(ENode(pubkey: pk[], address: a))
result.record = r
proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.toRaw)

View File

@ -480,7 +480,7 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
let
a = Address(ip: externalIp.get(IPv4_any()),
tcpPort: tcpPort, udpPort: udpPort)
enode = initENode(privKey.toPublicKey().tryGet(), a)
enode = ENode(pubkey: privKey.toPublicKey().tryGet(), address: a)
enrRec = enr.Record.init(1, privKey, externalIp, tcpPort, udpPort)
node = newNode(enode, enrRec)

View File

@ -10,29 +10,37 @@
## This module implements ECIES method encryption/decryption.
{.push raises: [Defect].}
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sysrand, sha2, utils]
import stew/result
export result
const
emptyMac* = array[0, byte]([])
type
EciesException* = object of CatchableError
EciesStatus* = enum
Success, ## Operation was successful
BufferOverrun, ## Output buffer size is too small
RandomError, ## Could not obtain random data
EcdhError, ## ECDH shared secret could not be calculated
WrongHeader, ## ECIES header is incorrect
IncorrectKey, ## Recovered public key is invalid
IncorrectTag, ## ECIES tag verification failed
IncompleteError ## Decryption needs more data
EciesError* = enum
BufferOverrun = "ecies: output buffer size is too small"
RandomError = "ecies: could not obtain random data"
EcdhError = "ecies: ECDH shared secret could not be calculated"
WrongHeader = "ecies: header is incorrect"
IncorrectKey = "ecies: recovered public key is invalid"
IncorrectTag = "ecies: tag verification failed"
IncompleteError = "ecies: decryption needs more data"
EciesHeader* = object {.packed.}
EciesHeader* {.packed.} = object
version*: byte
pubkey*: array[RawPublicKeySize, byte]
iv*: array[aes128.sizeBlock, byte]
data*: byte
EciesResult*[T] = Result[T, EciesError]
proc mapErrTo[T](r: SkResult[T], v: static EciesError): EciesResult[T] =
r.mapErr(proc (e: cstring): EciesError = v)
template eciesOverheadLength*(): int =
## Return data overhead size for ECIES encrypted message
1 + sizeof(PublicKey) + aes128.sizeBlock + sha256.sizeDigest
@ -86,7 +94,7 @@ proc kdf*(data: openarray[byte]): array[KeyLength, byte] {.noInit.} =
proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
pubkey: PublicKey,
sharedmac: openarray[byte] = emptyMac): EciesStatus =
sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
## Encrypt data with ECIES method using given public key `pubkey`.
## ``input`` - input data
## ``output`` - output data
@ -99,33 +107,31 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
cipher: CTR[aes128]
ctx: HMAC[sha256]
iv: array[aes128.sizeBlock, byte]
material: array[KeyLength, byte]
if len(output) < eciesEncryptedLength(len(input)):
return(BufferOverrun)
return err(BufferOverrun)
if randomBytes(iv) != aes128.sizeBlock:
return(RandomError)
return err(RandomError)
var ephemeral = KeyPair.random()
if ephemeral.isErr:
return(RandomError)
var
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
secret = ? ecdhRaw(ephemeral.seckey, pubkey).mapErrTo(EcdhError)
material = kdf(secret.data)
var secret = ecdhRaw(ephemeral[].seckey, pubkey)
if secret.isErr:
return(EcdhError)
material = kdf(secret[].data)
burnMem(secret)
clear(secret)
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
var macKey = sha256.digest(material, ostart = KeyLength div 2)
burnMem(material)
var header = cast[ptr EciesHeader](addr output[0])
header.version = 0x04
header.pubkey = ephemeral[].pubkey.toRaw()
header.pubkey = ephemeral.pubkey.toRaw()
header.iv = iv
clear(ephemeral)
var so = eciesDataPos()
var eo = so + len(input)
cipher.init(encKey, iv)
@ -146,12 +152,12 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
copyMem(addr output[so], addr tag.data[0], sha256.sizeDigest)
ctx.clear()
result = Success
ok()
proc eciesDecrypt*(input: openarray[byte],
output: var openarray[byte],
seckey: PrivateKey,
sharedmac: openarray[byte] = emptyMac): EciesStatus =
sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
## Decrypt data with ECIES method using given private key `seckey`.
## ``input`` - input data
## ``output`` - output data
@ -165,24 +171,23 @@ proc eciesDecrypt*(input: openarray[byte],
ctx: HMAC[sha256]
if len(input) <= 0:
return(IncompleteError)
return err(IncompleteError)
var header = cast[ptr EciesHeader](unsafeAddr input[0])
if header.version != 0x04:
return(WrongHeader)
return err(WrongHeader)
if len(input) <= eciesOverheadLength():
return(IncompleteError)
return err(IncompleteError)
if len(input) - eciesOverheadLength() > len(output):
return(BufferOverrun)
let pubkey = PublicKey.fromRaw(header.pubkey)
if pubkey.isErr:
return(IncorrectKey)
var secret = ecdhRaw(seckey, pubkey[])
if secret.isErr:
return(EcdhError)
return err(BufferOverrun)
var material = kdf(secret[].data)
var
pubkey = ? PublicKey.fromRaw(header.pubkey).mapErrTo(IncorrectKey)
secret = ? ecdhRaw(seckey, pubkey).mapErrTo(EcdhError)
var material = kdf(secret.data)
burnMem(secret)
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
var macKey = sha256.digest(material, ostart = KeyLength div 2)
burnMem(material)
@ -198,7 +203,7 @@ proc eciesDecrypt*(input: openarray[byte],
if not equalMem(addr tag.data[0], unsafeAddr input[eciesMacPos(len(input))],
sha256.sizeDigest):
return(IncorrectTag)
return err(IncorrectTag)
let datsize = eciesDecryptedLength(len(input))
cipher.init(encKey, header.iv)
@ -206,4 +211,5 @@ proc eciesDecrypt*(input: openarray[byte],
cipher.decrypt(toOpenArray(input, eciesDataPos(),
eciesDataPos() + datsize - 1), output)
cipher.clear()
result = Success
ok()

View File

@ -1,6 +1,6 @@
#
# Ethereum P2P
# (c) Copyright 2018
# (c) Copyright 2018-2020
# Status Research & Development GmbH
#
# Licensed under either of
@ -8,22 +8,23 @@
# MIT license (LICENSE-MIT)
#
{.push raises: [Defect].}
import uri, strutils, net
import eth/keys
export keys
type
ENodeStatus* = enum
ENodeError* = enum
## ENode status codes
Success, ## Conversion operation succeed
IncorrectNodeId, ## Incorrect public key supplied
IncorrectScheme, ## Incorrect URI scheme supplied
IncorrectIP, ## Incorrect IP address supplied
IncorrectPort, ## Incorrect TCP port supplied
IncorrectDiscPort, ## Incorrect UDP discovery port supplied
IncorrectUri, ## Incorrect URI supplied
IncompleteENode ## Incomplete ENODE object
IncorrectNodeId = "enode: incorrect public key"
IncorrectScheme = "enode: incorrect URI scheme"
IncorrectIP = "enode: incorrect IP address"
IncorrectPort = "enode: incorrect TCP port"
IncorrectDiscPort = "enode: incorrect UDP discovery port"
IncorrectUri = "enode: incorrect URI"
IncompleteENode = "enode: incomplete ENODE object"
Address* = object
## Network address object
@ -36,25 +37,12 @@ type
pubkey*: PublicKey ## Node public key
address*: Address ## Node address
ENodeException* = object of CatchableError
ENodeResult*[T] = Result[T, ENodeError]
proc raiseENodeError(status: ENodeStatus) =
if status == IncorrectIP:
raise newException(ENodeException, "Incorrect IP address")
elif status == IncorrectPort:
raise newException(ENodeException, "Incorrect port number")
elif status == IncorrectDiscPort:
raise newException(ENodeException, "Incorrect discovery port number")
elif status == IncorrectUri:
raise newException(ENodeException, "Incorrect URI")
elif status == IncorrectScheme:
raise newException(ENodeException, "Incorrect scheme")
elif status == IncorrectNodeId:
raise newException(ENodeException, "Incorrect node id")
elif status == IncompleteENode:
raise newException(ENodeException, "Incomplete enode")
proc mapErrTo[T, E](r: Result[T, E], v: static ENodeError): ENodeResult[T] =
r.mapErr(proc (e: E): ENodeError = v)
proc initENode*(e: string, node: var ENode): ENodeStatus =
proc fromString*(T: type ENode, e: string): ENodeResult[ENode] =
## Initialize ENode ``node`` from URI string ``uri``.
var
uport: int = 0
@ -62,83 +50,67 @@ proc initENode*(e: string, node: var ENode): ENodeStatus =
uri: Uri = initUri()
if len(e) == 0:
return IncorrectUri
return err(IncorrectUri)
parseUri(e, uri)
if len(uri.scheme) == 0 or uri.scheme.toLowerAscii() != "enode":
return IncorrectScheme
return err(IncorrectScheme)
if len(uri.username) != 128:
return IncorrectNodeId
return err(IncorrectNodeId)
for i in uri.username:
if i notin {'A'..'F', 'a'..'f', '0'..'9'}:
return IncorrectNodeId
return err(IncorrectNodeId)
if len(uri.password) != 0 or len(uri.path) != 0 or len(uri.anchor) != 0:
return IncorrectUri
return err(IncorrectUri)
if len(uri.hostname) == 0:
return IncorrectIP
return err(IncorrectIP)
try:
if len(uri.port) == 0:
return IncorrectPort
return err(IncorrectPort)
tport = parseInt(uri.port)
if tport <= 0 or tport > 65535:
return IncorrectPort
return err(IncorrectPort)
except ValueError:
return IncorrectPort
return err(IncorrectPort)
if len(uri.query) > 0:
if not uri.query.toLowerAscii().startsWith("discport="):
return IncorrectDiscPort
return err(IncorrectDiscPort)
try:
uport = parseInt(uri.query[9..^1])
if uport <= 0 or uport > 65535:
return IncorrectDiscPort
return err(IncorrectDiscPort)
except ValueError:
return IncorrectDiscPort
return err(IncorrectDiscPort)
else:
uport = tport
let pk = PublicKey.fromHex(uri.username)
if pk.isErr:
return IncorrectNodeId
node.pubkey = pk[]
var ip: IpAddress
try:
node.address.ip = parseIpAddress(uri.hostname)
ip = parseIpAddress(uri.hostname)
except ValueError:
zeroMem(addr node.pubkey, KeyLength * 2)
return IncorrectIP
return err(IncorrectIP)
node.address.tcpPort = Port(tport)
node.address.udpPort = Port(uport)
result = Success
let pubkey = ? PublicKey.fromHex(uri.username).mapErrTo(IncorrectNodeId)
proc initENode*(uri: string): ENode {.inline.} =
## Returns ENode object from URI string ``uri``.
let res = initENode(uri, result)
if res != Success:
raiseENodeError(res)
proc initENode*(pubkey: PublicKey, address: Address): ENode {.inline.} =
## Create ENode object from public key ``pubkey`` and ``address``.
result.pubkey = pubkey
result.address = address
proc isCorrect*(n: ENode): bool =
## Returns ``true`` if ENode ``n`` is properly filled.
var pk: PublicKey
n.pubkey != pk
ok(ENode(
pubkey: pubkey,
address: Address(
ip: ip,
tcpPort: Port(tport),
udpPort: Port(uport)
)
))
proc `$`*(n: ENode): string =
## Returns string representation of ENode.
var ipaddr: string
if not isCorrect(n):
raiseENodeError(IncompleteENode)
if n.address.ip.family == IpAddressFamily.IPv4:
ipaddr = $(n.address.ip)
else:

View File

@ -55,12 +55,12 @@ proc toNodeId*(pk: PublicKey): NodeId =
proc newNode*(pk: PublicKey, address: Address): Node =
result.new()
result.node = initENode(pk, address)
result.node = ENode(pubkey: pk, address: address)
result.id = pk.toNodeId()
proc newNode*(uriString: string): Node =
result.new()
result.node = initENode(uriString)
result.node = ENode.fromString(uriString)[]
result.id = result.node.pubkey.toNodeId()
proc newNode*(enode: ENode): Node =

View File

@ -212,7 +212,5 @@ proc newMockPeer*(userConfigurator: proc (m: MockConf)): EthereumNode =
return node
proc rlpxConnect*(node, otherNode: EthereumNode): Future[Peer] =
let otherAsRemote = newNode(initENode(otherNode.keys.pubKey,
otherNode.address))
let otherAsRemote = newNode(otherNode.toENode())
return rlpx.rlpxConnect(node, otherAsRemote)

View File

@ -168,3 +168,5 @@ type
proc `$`*(peer: Peer): string = $peer.remote
proc toENode*(v: EthereumNode): ENode =
ENode(pubkey: v.keys.pubkey, address: v.address)

View File

@ -390,7 +390,7 @@ proc recvMsg*(peer: Peer): Future[tuple[msgId: int, msgData: Rlp]] {.async.} =
var msgSize: int
if decryptHeaderAndGetMsgSize(peer.secretsState,
headerBytes, msgSize) != RlpxStatus.Success:
headerBytes, msgSize).isErr():
await peer.disconnectAndRaise(BreachOfProtocol,
"Cannot decrypt RLPx frame header")
@ -414,7 +414,7 @@ proc recvMsg*(peer: Peer): Future[tuple[msgId: int, msgData: Rlp]] {.async.} =
decryptedBytesCount = 0
if decryptBody(peer.secretsState, encryptedBytes, msgSize,
decryptedBytes, decryptedBytesCount) != RlpxStatus.Success:
decryptedBytes, decryptedBytesCount).isErr():
await peer.disconnectAndRaise(BreachOfProtocol,
"Cannot decrypt RLPx frame body")
@ -929,14 +929,9 @@ template `^`(arr): auto =
# variable as an open array
arr.toOpenArray(0, `arr Len` - 1)
proc check(status: AuthStatus) =
if status != AuthStatus.Success:
raise newException(CatchableError, "Error: " & $status)
proc initSecretState(hs: var Handshake, authMsg, ackMsg: openarray[byte],
p: Peer) =
var secrets: ConnectionSecret
check hs.getSecrets(authMsg, ackMsg, secrets)
var secrets = hs.getSecrets(authMsg, ackMsg).tryGet()
initSecretState(secrets, p.secretsState)
burnMem(secrets)
@ -975,12 +970,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
var ok = false
try:
result.transport = await connect(ta)
var handshake = newHandshake({Initiator, EIP8}, int(node.baseProtocolVersion))
handshake.host = node.keys
var handshake = Handshake.tryInit(
node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet()
var authMsg: array[AuthMessageMaxEIP8, byte]
var authMsgLen = 0
check authMessage(handshake, remote.node.pubkey, authMsg, authMsgLen)
authMessage(handshake, remote.node.pubkey, authMsg, authMsgLen).tryGet()
var res = await result.transport.write(addr authMsg[0], authMsgLen)
if res != authMsgLen:
raisePeerDisconnected("Unexpected disconnect while authenticating",
@ -993,12 +988,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
await result.transport.readExactly(addr ackMsg[0], len(ackMsg))
var ret = handshake.decodeAckMessage(ackMsg)
if ret == AuthStatus.IncompleteError:
if ret.isErr and ret.error == AuthError.IncompleteError:
ackMsg.setLen(handshake.expectedLength)
await result.transport.readExactly(addr ackMsg[initialSize],
len(ackMsg) - initialSize)
ret = handshake.decodeAckMessage(ackMsg)
check ret
ret.tryGet() # for the raise!
node.checkSnappySupport(handshake, result)
initSecretState(handshake, ^authMsg, ackMsg, result)
@ -1062,8 +1057,7 @@ proc rlpxAccept*(node: EthereumNode,
result.transport = transport
result.network = node
var handshake = newHandshake({auth.Responder})
handshake.host = node.keys
var handshake = HandShake.tryInit(node.keys, {auth.Responder}).tryGet
var ok = false
try:
@ -1073,19 +1067,20 @@ proc rlpxAccept*(node: EthereumNode,
authMsg.setLen(initialSize)
await transport.readExactly(addr authMsg[0], len(authMsg))
var ret = handshake.decodeAuthMessage(authMsg)
if ret == AuthStatus.IncompleteError: # Eip8 auth message is likely
if ret.isErr and ret.error == AuthError.IncompleteError:
# Eip8 auth message is likely
authMsg.setLen(handshake.expectedLength)
await transport.readExactly(addr authMsg[initialSize],
len(authMsg) - initialSize)
ret = handshake.decodeAuthMessage(authMsg)
check ret
ret.tryGet() # for the raise!
node.checkSnappySupport(handshake, result)
handshake.version = uint8(result.baseProtocolVersion)
var ackMsg: array[AckMessageMaxEIP8, byte]
var ackMsgLen: int
check handshake.ackMessage(ackMsg, ackMsgLen)
handshake.ackMessage(ackMsg, ackMsgLen).tryGet()
var res = await transport.write(addr ackMsg[0], ackMsgLen)
if res != ackMsgLen:
raisePeerDisconnected("Unexpected disconnect while authenticating",
@ -1117,7 +1112,8 @@ proc rlpxAccept*(node: EthereumNode,
let remote = transport.remoteAddress()
let address = Address(ip: remote.address, tcpPort: remote.port,
udpPort: remote.port)
result.remote = newNode(initEnode(handshake.remoteHPubkey, address))
result.remote = newNode(
ENode(pubkey: handshake.remoteHPubkey, address: address))
trace "devp2p handshake completed", peer = result.remote,
clientId = response.clientId

View File

@ -49,7 +49,7 @@ p2pProtocol Hive(version = hiveVersion,
debug "Hive peer connected"
proc initProtocolState*(network: BzzNetwork, node: EthereumNode) {.gcsafe.} =
network.thisENode = initENode(node.keys.pubkey, node.address)
network.thisENode = node.toENode()
p2pProtocol Bzz(version = bzzVersion,
rlpxName = "bzz",

View File

@ -266,8 +266,8 @@ p2pProtocol les(version = lesVersion,
if signature.isNone:
error "missing announce signature"
return
let sigMsg = rlp.encodeList(headHash, headNumber, headTotalDifficulty)
let sig = Signature.fromRaw(signature.get).tryGet()
let sigMsg = rlp.encodeList(headHash, headNumber, headTotalDifficulty)
let signerKey = recover(sig, sigMsg).tryGet()
if signerKey.toNodeId != peer.remote.id:
error "invalid announce signature"

View File

@ -307,8 +307,8 @@ proc encode*(self: Payload): Option[Bytes] =
if self.dst.isSome(): # Asymmetric key present - encryption requested
var res = newSeq[byte](eciesEncryptedLength(plain.len))
let err = eciesEncrypt(plain, res, self.dst.get())
if err != EciesStatus.Success:
notice "Encryption failed", err
if err.isErr:
notice "Encryption failed", err = err.error
return
return some(res)
@ -343,7 +343,7 @@ proc decode*(data: openarray[byte], dst = none[PrivateKey](),
return
plain.setLen(eciesDecryptedLength(data.len))
if eciesDecrypt(data, plain, dst.get()) != EciesStatus.Success:
if eciesDecrypt(data, plain, dst.get()).isErr:
debug "Couldn't decrypt using asymmetric key", len = data.len
return
elif symKey.isSome():

View File

@ -10,9 +10,13 @@
## This module implements RLPx cryptography
import stew/ranges/stackarrays, eth/rlp/types, nimcrypto
{.push raises: [Defect].}
import stew/ranges/stackarrays, eth/rlp/types, nimcrypto, stew/result
from auth import ConnectionSecret
export result
const
RlpHeaderLength* = 16
RlpMacLength* = 16
@ -27,15 +31,16 @@ type
emac*: keccak256
imac*: keccak256
RlpxStatus* = enum
Success, ## Operation was successful
IncorrectMac, ## MAC verification failed
BufferOverrun, ## Buffer overrun error
IncompleteError, ## Data incomplete error
IncorrectArgs ## Incorrect arguments
RlpxError* = enum
IncorrectMac = "rlpx: MAC verification failed"
BufferOverrun = "rlpx: buffer overrun"
IncompleteError = "rlpx: data incomplete"
IncorrectArgs = "rlpx: incorrect arguments"
RlpxHeader* = array[16, byte]
RlpxResult*[T] = Result[T, RlpxError]
proc roundup16*(x: int): int {.inline.} =
## Procedure aligns `x` to
let rem = x and 15
@ -76,7 +81,7 @@ template decryptedLength*(size: int): int =
proc encrypt*(c: var SecretState, header: openarray[byte],
frame: openarray[byte],
output: var openarray[byte]): RlpxStatus =
output: var openarray[byte]): RlpxResult[void] =
## Encrypts `header` and `frame` using SecretState `c` context and store
## result into `output`.
##
@ -92,7 +97,7 @@ proc encrypt*(c: var SecretState, header: openarray[byte],
let framePos = RlpHeaderLength + RlpMacLength
let frameMacPos = RlpHeaderLength * 2 + frameLength
if len(header) != RlpHeaderLength or len(frame) == 0 or length != len(output):
return IncorrectArgs
return err(IncorrectArgs)
# header_ciphertext = self.aes_enc.update(header)
c.aesenc.encrypt(header, toa(output, 0, RlpHeaderLength))
# mac_secret = self.egress_mac.digest()[:HEADER_LEN]
@ -128,7 +133,7 @@ proc encrypt*(c: var SecretState, header: openarray[byte],
# return header_ciphertext + header_mac + frame_ciphertext + frame_mac
copyMem(addr output[headerMacPos], addr headerMac.data[0], RlpHeaderLength)
copyMem(addr output[frameMacPos], addr frameMac.data[0], RlpHeaderLength)
result = Success
ok()
proc encryptMsg*(msg: openarray[byte], secrets: var SecretState): seq[byte] =
var header: RlpxHeader
@ -152,13 +157,13 @@ proc encryptMsg*(msg: openarray[byte], secrets: var SecretState): seq[byte] =
# This would be safer if we use a thread-local sequ for the temporary buffer
result = newSeq[byte](encryptedLength(msg.len))
let s = encrypt(secrets, header, msg, result)
doAssert s == Success
s.expect("always succeeds because we call with correct buffer")
proc getBodySize*(a: RlpxHeader): int =
(int(a[0]) shl 16) or (int(a[1]) shl 8) or int(a[2])
proc decryptHeader*(c: var SecretState, data: openarray[byte],
output: var openarray[byte]): RlpxStatus =
output: var openarray[byte]): RlpxResult[void] =
## Decrypts header `data` using SecretState `c` context and store
## result into `output`.
##
@ -169,9 +174,9 @@ proc decryptHeader*(c: var SecretState, data: openarray[byte],
aes: array[RlpHeaderLength, byte]
if len(data) != RlpHeaderLength + RlpMacLength:
return IncompleteError
return err(IncompleteError)
if len(output) < RlpHeaderLength:
return IncorrectArgs
return err(IncorrectArgs)
# mac_secret = self.ingress_mac.digest()[:HEADER_LEN]
tmpmac = c.imac
var macsec = tmpmac.finish()
@ -188,22 +193,22 @@ proc decryptHeader*(c: var SecretState, data: openarray[byte],
let headerMacPos = RlpHeaderLength
if not equalMem(cast[pointer](unsafeAddr data[headerMacPos]),
cast[pointer](addr expectMac.data[0]), RlpMacLength):
result = IncorrectMac
result = err(IncorrectMac)
else:
# return self.aes_dec.update(header_ciphertext)
c.aesdec.decrypt(toa(data, 0, RlpHeaderLength), output)
result = Success
result = ok()
proc decryptHeaderAndGetMsgSize*(c: var SecretState,
encryptedHeader: openarray[byte],
outSize: var int): RlpxStatus =
outSize: var int): RlpxResult[void] =
var decryptedHeader: RlpxHeader
result = decryptHeader(c, encryptedHeader, decryptedHeader)
if result == Success:
if result.isOk():
outSize = decryptedHeader.getBodySize
proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
output: var openarray[byte], outlen: var int): RlpxStatus =
output: var openarray[byte], outlen: var int): RlpxResult[void] =
## Decrypts body `data` using SecretState `c` context and store
## result into `output`.
##
@ -217,9 +222,9 @@ proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
outlen = 0
let rsize = roundup16(bodysize)
if len(data) < rsize + RlpMacLength:
return IncompleteError
return err(IncompleteError)
if len(output) < rsize:
return IncorrectArgs
return err(IncorrectArgs)
# self.ingress_mac.update(frame_ciphertext)
c.imac.update(toa(data, 0, rsize))
tmpmac = c.imac
@ -235,8 +240,8 @@ proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
let bodyMacPos = rsize
if not equalMem(cast[pointer](unsafeAddr data[bodyMacPos]),
cast[pointer](addr expectMac.data[0]), RlpMacLength):
result = IncorrectMac
result = err(IncorrectMac)
else:
c.aesdec.decrypt(toa(data, 0, rsize), output)
outlen = bodysize
result = Success
result = ok()

View File

@ -31,4 +31,4 @@ test:
# These errors are also catched in `processClient` in discovery.nim
# TODO: move them a layer down in discovery so we can do a cleaner test there?
except RlpError, DiscProtocolError as e:
debug "Receive failed", err = e.msg
debug "Receive failed", err = e.msg

View File

@ -16,8 +16,7 @@ init:
node2 = setupTestNode(eth, Whisper)
node2.startListening()
peer = waitFor node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
node2.address)))
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
test:
aflLoop: # This appears to have unstable results with afl-clang-fast, probably

View File

@ -7,7 +7,7 @@
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import eth/keys, eth/keyfile/[uuid, keyfile], json, strutils, os, unittest
import eth/keys, eth/keyfile/[keyfile], json, os, unittest
# Test vectors copied from
# https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json
@ -83,52 +83,45 @@ var TestVectors = [
}
]
var jobject: JsonNode
suite "KeyFile test suite":
test "KeyStoreTests/basic_tests.json test1":
var seckey: PrivateKey
var expectkey = PrivateKey.fromHex(TestVectors[0].getOrDefault("priv").getStr())[]
check:
let seckey =
decodeKeyFileJson(TestVectors[0].getOrDefault("keyfile"),
TestVectors[0].getOrDefault("password").getStr(),
seckey) == KeyFileStatus.Success
seckey.toRaw == expectkey.toRaw
test "KeyStoreTests/basic_tests.json python_generated_test_with_odd_iv":
var seckey: PrivateKey
var expectkey = PrivateKey.fromHex(TestVectors[1].getOrDefault("priv").getStr())[]
TestVectors[0].getOrDefault("password").getStr())[]
check:
seckey.toRaw() == expectkey.toRaw()
test "KeyStoreTests/basic_tests.json python_generated_test_with_odd_iv":
var expectkey = PrivateKey.fromHex(TestVectors[1].getOrDefault("priv").getStr())[]
let seckey =
decodeKeyFileJson(TestVectors[1].getOrDefault("keyfile"),
TestVectors[1].getOrDefault("password").getStr(),
seckey) == KeyFileStatus.Success
TestVectors[1].getOrDefault("password").getStr())[]
check:
seckey.toRaw == expectkey.toRaw
test "KeyStoreTests/basic_tests.json evilnonce":
var seckey: PrivateKey
var expectkey = PrivateKey.fromHex(TestVectors[2].getOrDefault("priv").getStr())[]
let seckey = decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
TestVectors[2].getOrDefault("password").getStr())[]
check:
decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
TestVectors[2].getOrDefault("password").getStr(),
seckey) == KeyFileStatus.Success
seckey.toRaw == expectkey.toRaw
test "KeyStoreTests/basic_tests.json evilnonce with wrong password":
var seckey: PrivateKey
check:
let seckey =
decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
"wrongpassword",
seckey) == KeyFileStatus.IncorrectMac
"wrongpassword")
check:
seckey.error == KeyFileError.IncorrectMac
test "Create/Save/Load test":
var seckey0 = PrivateKey.random()[]
var seckey1: PrivateKey
let jobject = createKeyFileJson(seckey0, "randompassword")[]
check:
saveKeyFile("test.keyfile", jobject).isOk()
var seckey1 = loadKeyFile("test.keyfile", "randompassword")[]
check:
createKeyFileJson(seckey0, "randompassword",
jobject) == KeyFileStatus.Success
saveKeyFile("test.keyfile", jobject) == KeyFileStatus.Success
loadKeyFile("test.keyfile", "randompassword",
seckey1) == KeyFileStatus.Success
seckey0.toRaw == seckey1.toRaw
removeFile("test.keyfile")
test "Load non-existent pathname test":
var seckey: PrivateKey
check:
loadKeyFile("nonexistant.keyfile", "password",
seckey) == KeyFileStatus.OsError
loadKeyFile("nonexistant.keyfile", "password").error ==
KeyFileError.OsError

View File

@ -7,17 +7,15 @@
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import eth/keyfile/uuid, strutils, unittest
import eth/keyfile/uuid, unittest
suite "Cross-platform UUID test suite":
test "Platform UUID check":
var u: UUID
check uuidGenerate(u) == 1
check uuidGenerate().isOk
test "Conversion test":
var u: UUID
let u = uuidGenerate()[]
check:
uuidGenerate(u) == 1
len($u) == 36
$uuidFromString($u) == $u
uuidToString(u, true) == $u
uuidToString(u, false) == toUpperAscii($u)
$uuidFromString($u)[] == $u
uuidToString(u) == $u

View File

@ -7,10 +7,9 @@ import
var node = setupTestNode(Bzz, Hive)
var bzzENode: ENode
let nodeId = "enode://10420addaa648ffcf09c4ba9df7ce876f276f77aae015bc9346487780c9c04862dc47cec17c86be10d4fb7d93f2cae3f8e702f94cb6dea5807bfedad218a53df@127.0.0.1:30399"
discard initENode(nodeId, bzzENode)
waitFor node.peerPool.connectToNode(newNode(bzzENode))
let enode = ENode.fromString(nodeId)[]
waitFor node.peerPool.connectToNode(newNode(enode))
doAssert node.peerPool.connectedNodes.len() == 1

View File

@ -20,7 +20,7 @@ proc setupBootNode*(): Future[ENode] {.async.} =
bootNodeKey = KeyPair.random()[]
bootNodeAddr = localAddress(30301)
bootNode = await startDiscoveryNode(bootNodeKey.seckey, bootNodeAddr, @[])
result = initENode(bootNodeKey.pubkey, bootNodeAddr)
result = ENode(pubkey: bootNodeKey.pubkey, address: bootNodeAddr)
proc setupTestNode*(capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode =
let keys1 = KeyPair.random()[]

View File

@ -98,20 +98,15 @@ let topic = [byte 0x12, 0, 0, 0]
if config.main:
var bootnodes: seq[ENode] = @[]
for nodeId in MainnetBootnodes:
var bootnode: ENode
discard initENode(nodeId, bootnode)
bootnodes.add(bootnode)
bootnodes.add(ENode.fromString(nodeId).expect("static nodes"))
asyncCheck node.connectToNetwork(bootnodes, true, true)
# main network has mostly non SHH nodes, so we connect directly to SHH nodes
for nodeId in WhisperNodes:
var whisperENode: ENode
discard initENode(nodeId, whisperENode)
var whisperNode = newNode(whisperENode)
var whisperNode = newNode(ENode.fromString(nodeId).expect("static nodes"))
asyncCheck node.peerPool.connectToNode(whisperNode)
else:
var bootENode: ENode
discard initENode(DockerBootNode, bootENode)
let bootENode = ENode.fromString(DockerBootnode).expect("static node")
waitFor node.connectToNetwork(@[bootENode], true, true)
if config.watch:

View File

@ -215,18 +215,20 @@ suite "Ethereum P2P handshake test suite":
block:
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
result = newHandshake(flags)
if Initiator in flags:
result.host.seckey = PrivateKey.fromHex(testValue("initiator_private_key"))[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
result = Handshake.tryInit(kp, flags)[]
let epki = testValue("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags:
result.host.seckey = PrivateKey.fromHex(testValue("receiver_private_key"))[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
result = Handshake.tryInit(kp, flags)[]
let epkr = testValue("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
@ -238,9 +240,8 @@ suite "Ethereum P2P handshake test suite":
var responder = newTestHandshake({Responder})
var m0 = newSeq[byte](initiator.authSize(false))
var k0 = 0
check:
initiator.authMessage(responder.host.pubkey,
m0, k0, 0, false) == AuthStatus.Success
initiator.authMessage(
responder.host.pubkey, m0, k0, 0, false).expect("auth success")
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
var expect2 = fromHex(stripSpaces(pyevmAuth))
check:
@ -254,10 +255,11 @@ suite "Ethereum P2P handshake test suite":
var k0 = 0
let remoteEPubkey0 = initiator.ephemeral.pubkey
let remoteHPubkey0 = initiator.host.pubkey
initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success")
check:
initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
responder.remoteEPubkey == remoteEPubkey0
responder.remoteHPubkey == remoteHPubkey0
@ -270,11 +272,11 @@ suite "Ethereum P2P handshake test suite":
var k0 = 0
var k1 = 0
var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success")
responder.ackMessage(m1, k1, 0, false).expect("ack success")
check:
initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.ackMessage(m1, k1, 0, false) == AuthStatus.Success
m1 == expect0
responder.initiatorNonce == initiator.initiatorNonce
@ -285,12 +287,12 @@ suite "Ethereum P2P handshake test suite":
var m1 = newSeq[byte](responder.ackSize())
var k0 = 0
var k1 = 0
check:
initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.ackMessage(m1, k1) == AuthStatus.Success
initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success")
responder.decodeAuthMessage(m0).expect("decode success")
responder.ackMessage(m1, k1).expect("ack success")
initiator.decodeAckMessage(m1).expect("decode success")
let remoteEPubkey0 = responder.ephemeral.pubkey
let remoteHPubkey0 = responder.host.pubkey
check:
@ -307,13 +309,12 @@ suite "Ethereum P2P handshake test suite":
var tmac = fromHex(stripSpaces(testValue("mac_secret")))
var temac = fromHex(stripSpaces(testValue("initial_egress_MAC")))
var timac = fromHex(stripSpaces(testValue("initial_ingress_MAC")))
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
responder.decodeAuthMessage(authm).expect("decode success")
initiator.decodeAckMessage(ackm).expect("ack success")
var csecInitiator = initiator.getSecrets(authm, ackm).expect("secrets success")
var csecResponder = responder.getSecrets(authm, ackm).expect("secrets success")
check:
responder.decodeAuthMessage(authm) == AuthStatus.Success
initiator.decodeAckMessage(ackm) == AuthStatus.Success
initiator.getSecrets(authm, ackm, csecInitiator) == AuthStatus.Success
responder.getSecrets(authm, ackm, csecResponder) == AuthStatus.Success
csecInitiator.aesKey == csecResponder.aesKey
csecInitiator.macKey == csecResponder.macKey
taes[0..^1] == csecInitiator.aesKey[0..^1]
@ -330,9 +331,11 @@ suite "Ethereum P2P handshake test suite":
block:
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
result = newHandshake(flags)
if Initiator in flags:
result.host.seckey = 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(kp, flags)[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let esec = testE8Value("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
@ -340,8 +343,10 @@ suite "Ethereum P2P handshake test suite":
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags:
result.host.seckey = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let pk = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
result = Handshake.tryInit(kp, flags)[]
let esec = testE8Value("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
@ -352,8 +357,8 @@ suite "Ethereum P2P handshake test suite":
var initiator = newTestHandshake({Initiator})
var responder = newTestHandshake({Responder})
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_v4")))
responder.decodeAuthMessage(m0).expect("decode success")
check:
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
let remoteEPubkey0 = initiator.ephemeral.pubkey
let remoteHPubkey0 = initiator.host.pubkey
@ -361,7 +366,7 @@ suite "Ethereum P2P handshake test suite":
responder.remoteEPubkey == remoteEPubkey0
responder.remoteHPubkey == remoteHPubkey0
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_v4")))
check initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.decodeAckMessage(m1).expect("decode success")
let remoteEPubkey1 = responder.ephemeral.pubkey
check:
initiator.remoteEPubkey == remoteEPubkey1
@ -371,28 +376,27 @@ suite "Ethereum P2P handshake test suite":
var initiator = newTestHandshake({Initiator})
var responder = newTestHandshake({Responder})
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8")))
responder.decodeAuthMessage(m0).expect("decode success")
check:
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
let remoteEPubkey0 = initiator.ephemeral.pubkey
check responder.remoteEPubkey == remoteEPubkey0
let remoteHPubkey0 = initiator.host.pubkey
check responder.remoteHPubkey == remoteHPubkey0
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8")))
check initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.decodeAckMessage(m1).expect("decode success")
let remoteEPubkey1 = responder.ephemeral.pubkey
check:
initiator.remoteEPubkey == remoteEPubkey1
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
var taes = fromHex(stripSpaces(testE8Value("auth2ack2_aes_secret")))
var tmac = fromHex(stripSpaces(testE8Value("auth2ack2_mac_secret")))
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets")
var csecResponder = responder.getSecrets(m0, m1).expect("secrets")
check:
int(initiator.version) == 4
int(responder.version) == 4
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
csecInitiator.aesKey == csecResponder.aesKey
csecInitiator.macKey == csecResponder.macKey
taes[0..^1] == csecInitiator.aesKey[0..^1]
@ -407,8 +411,8 @@ suite "Ethereum P2P handshake test suite":
var initiator = newTestHandshake({Initiator})
var responder = newTestHandshake({Responder})
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8_3f")))
responder.decodeAuthMessage(m0).expect("decode success")
check:
responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
let remoteEPubkey0 = initiator.ephemeral.pubkey
let remoteHPubkey0 = initiator.host.pubkey
@ -416,7 +420,7 @@ suite "Ethereum P2P handshake test suite":
responder.remoteEPubkey == remoteEPubkey0
responder.remoteHPubkey == remoteHPubkey0
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8_3f")))
check initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.decodeAckMessage(m1).expect("decode success")
let remoteEPubkey1 = responder.ephemeral.pubkey
check:
int(initiator.version) == 57
@ -429,22 +433,20 @@ suite "Ethereum P2P handshake test suite":
var initiator = newTestHandshake({Initiator, EIP8})
var responder = newTestHandshake({Responder})
var m0 = newSeq[byte](initiator.authSize())
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
var k0 = 0
var k1 = 0
check initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success")
m0.setLen(k0)
check responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.decodeAuthMessage(m0).expect("decode success")
check (EIP8 in responder.flags) == true
var m1 = newSeq[byte](responder.ackSize())
check responder.ackMessage(m1, k1) == AuthStatus.Success
responder.ackMessage(m1, k1).expect("ack success")
m1.setLen(k1)
check initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.decodeAckMessage(m1).expect("decode success")
var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets")
var csecResponder = responder.getSecrets(m0, m1).expect("secrets")
check:
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
csecInitiator.aesKey == csecResponder.aesKey
csecInitiator.macKey == csecResponder.macKey
@ -453,21 +455,19 @@ suite "Ethereum P2P handshake test suite":
var initiator = newTestHandshake({Initiator})
var responder = newTestHandshake({Responder})
var m0 = newSeq[byte](initiator.authSize())
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
var k0 = 0
var k1 = 0
check initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
initiator.authMessage(
responder.host.pubkey, m0, k0).expect("auth success")
m0.setLen(k0)
check responder.decodeAuthMessage(m0) == AuthStatus.Success
responder.decodeAuthMessage(m0).expect("auth success")
var m1 = newSeq[byte](responder.ackSize())
check responder.ackMessage(m1, k1) == AuthStatus.Success
responder.ackMessage(m1, k1).expect("ack success")
m1.setLen(k1)
check initiator.decodeAckMessage(m1) == AuthStatus.Success
initiator.decodeAckMessage(m1).expect("ack success")
var csecInitiator = initiator.getSecrets(m0, m1).expect("secrets")
var csecResponder = responder.getSecrets(m0, m1).expect("secrets")
check:
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
csecInitiator.aesKey == csecResponder.aesKey
csecInitiator.macKey == csecResponder.macKey

View File

@ -88,18 +88,19 @@ proc testValue(s: string): string =
suite "Ethereum RLPx encryption/decryption test suite":
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
result = newHandshake(flags)
if Initiator in flags:
result.host.seckey = PrivateKey.fromHex(testValue("initiator_private_key"))[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
result = Handshake.tryInit(kp, flags)[]
let epki = testValue("initiator_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags:
result.host.seckey = PrivateKey.fromHex(testValue("receiver_private_key"))[]
result.host.pubkey = result.host.seckey.toPublicKey()[]
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
result = Handshake.tryInit(kp, flags)[]
let epkr = testValue("receiver_ephemeral_private_key")
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
@ -111,15 +112,13 @@ suite "Ethereum RLPx encryption/decryption test suite":
var responder = newTestHandshake({Responder})
var authm = fromHex(stripSpaces(testValue("auth_ciphertext")))
var ackm = fromHex(stripSpaces(testValue("authresp_ciphertext")))
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
var stateInitiator0, stateInitiator1: SecretState
var stateResponder0, stateResponder1: SecretState
check:
responder.decodeAuthMessage(authm) == AuthStatus.Success
initiator.decodeAckMessage(ackm) == AuthStatus.Success
initiator.getSecrets(authm, ackm, csecInitiator) == AuthStatus.Success
responder.getSecrets(authm, ackm, csecResponder) == AuthStatus.Success
responder.decodeAuthMessage(authm).expect("success")
initiator.decodeAckMessage(ackm).expect("success")
var csecInitiator = initiator.getSecrets(authm, ackm)[]
var csecResponder = responder.getSecrets(authm, ackm)[]
initSecretState(csecInitiator, stateInitiator0)
initSecretState(csecResponder, stateResponder0)
initSecretState(csecInitiator, stateInitiator1)
@ -132,7 +131,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
block:
check stateResponder0.decryptHeader(toOpenArray(initiatorHello, 0, 31),
header) == RlpxStatus.Success
header).isOk()
let bodysize = getBodySize(header)
check bodysize == 79
# we need body size to be rounded to 16 bytes boundary to properly
@ -142,16 +141,16 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
stateResponder0.decryptBody(
toOpenArray(initiatorHello, 32, len(initiatorHello) - 1),
getBodySize(header), body, decrsize) == RlpxStatus.Success
getBodySize(header), body, decrsize).isOk()
decrsize == 79
body.setLen(decrsize)
var hello = newSeq[byte](encryptedLength(bodysize))
check:
stateInitiator1.encrypt(header, body, hello) == RlpxStatus.Success
stateInitiator1.encrypt(header, body, hello).isOk()
hello == initiatorHello
block:
check stateInitiator0.decryptHeader(toOpenArray(responderHello, 0, 31),
header) == RlpxStatus.Success
header).isOk()
let bodysize = getBodySize(header)
check bodysize == 79
# we need body size to be rounded to 16 bytes boundary to properly
@ -161,34 +160,31 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
stateInitiator0.decryptBody(
toOpenArray(responderHello, 32, len(initiatorHello) - 1),
getBodySize(header), body, decrsize) == RlpxStatus.Success
getBodySize(header), body, decrsize).isOk()
decrsize == 79
body.setLen(decrsize)
var hello = newSeq[byte](encryptedLength(bodysize))
check:
stateResponder1.encrypt(header, body, hello) == RlpxStatus.Success
stateResponder1.encrypt(header, body, hello).isOk()
hello == responderHello
test "Continuous stream of different lengths (1000 times)":
var initiator = newTestHandshake({Initiator})
var responder = newTestHandshake({Responder})
var m0 = newSeq[byte](initiator.authSize())
var csecInitiator: ConnectionSecret
var csecResponder: ConnectionSecret
var k0 = 0
var k1 = 0
check initiator.authMessage(responder.host.pubkey,
m0, k0) == AuthStatus.Success
m0, k0).isOk
m0.setLen(k0)
check responder.decodeAuthMessage(m0) == AuthStatus.Success
check responder.decodeAuthMessage(m0).isOk
var m1 = newSeq[byte](responder.ackSize())
check responder.ackMessage(m1, k1) == AuthStatus.Success
check responder.ackMessage(m1, k1).isOk
m1.setLen(k1)
check initiator.decodeAckMessage(m1) == AuthStatus.Success
check initiator.decodeAckMessage(m1).isOk
check:
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
var csecInitiator = initiator.getSecrets(m0, m1)[]
var csecResponder = responder.getSecrets(m0, m1)[]
var stateInitiator: SecretState
var stateResponder: SecretState
var iheader, rheader: array[16, byte]
@ -207,9 +203,9 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
randomBytes(ibody) == len(ibody)
stateInitiator.encrypt(iheader, ibody,
encrypted) == RlpxStatus.Success
encrypted).isOk()
stateResponder.decryptHeader(toOpenArray(encrypted, 0, 31),
rheader) == RlpxStatus.Success
rheader).isOk()
var length = getBodySize(rheader)
check length == len(ibody)
var rbody = newSeq[byte](decryptedLength(length))
@ -217,7 +213,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
stateResponder.decryptBody(
toOpenArray(encrypted, 32, len(encrypted) - 1),
length, rbody, decrsize) == RlpxStatus.Success
length, rbody, decrsize).isOk()
decrsize == length
rbody.setLen(decrsize)
check:
@ -235,9 +231,9 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
randomBytes(ibody) == len(ibody)
stateResponder.encrypt(iheader, ibody,
encrypted) == RlpxStatus.Success
encrypted).isOk()
stateInitiator.decryptHeader(toOpenArray(encrypted, 0, 31),
rheader) == RlpxStatus.Success
rheader).isOk()
var length = getBodySize(rheader)
check length == len(ibody)
var rbody = newSeq[byte](decryptedLength(length))
@ -245,7 +241,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
check:
stateInitiator.decryptBody(
toOpenArray(encrypted, 32, len(encrypted) - 1),
length, rbody, decrsize) == RlpxStatus.Success
length, rbody, decrsize).isOk()
decrsize == length
rbody.setLen(length)
check:

View File

@ -22,7 +22,7 @@ proc test() {.async.} =
bootNodeKey = PrivateKey.fromHex(
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
bootNodeAddr = localAddress(20301)
bootENode = initENode(bootNodeKey.toPublicKey()[], bootNodeAddr)
bootENode = ENode(pubkey: bootNodeKey.toPublicKey()[], address: bootNodeAddr)
bootNode = await startDiscoveryNode(bootNodeKey, bootNodeAddr, @[])
test "Discover nodes":

View File

@ -134,9 +134,9 @@ suite "Discovery v5 Cryptographic Primitives":
let
pub = PublicKey.fromHex(publicKey)[]
priv = PrivateKey.fromHex(secretKey)[]
let eph = ecdhRawFull(priv, pub)
check:
eph.isOk()
eph[].data == hexToSeqByte(sharedSecret)
test "Key Derivation":

View File

@ -71,14 +71,18 @@ suite "ECIES test suite":
var shmac = [0x13'u8, 0x13'u8]
var s = PrivateKey.random()[]
var p = s.toPublicKey()[]
eciesEncrypt(plain, encr, p).expect("encryption should succeed")
eciesDecrypt(encr, decr, s).expect("decryption should succeed")
check:
# Without additional mac data
eciesEncrypt(plain, encr, p) == EciesStatus.Success
eciesDecrypt(encr, decr, s) == EciesStatus.Success
equalMem(addr m[0], addr decr[0], len(m))
# With additional mac data
eciesEncrypt(plain, encr, p, shmac) == EciesStatus.Success
eciesDecrypt(encr, decr, s, shmac) == EciesStatus.Success
# With additional mac data
eciesEncrypt(plain, encr, p, shmac).expect("encryption should succeed")
eciesDecrypt(encr, decr, s, shmac).expect("decryption should succeed")
check:
equalMem(addr m[0], addr decr[0], len(m))
test "ECIES/py-evm/cpp-ethereum test_ecies.py#L43/rlpx.cpp#L187":
@ -126,8 +130,10 @@ suite "ECIES test suite":
var s = PrivateKey.fromHex(secretKeys[i])[]
var cipher = fromHex(stripSpaces(cipherText[i]))
var expect = fromHex(stripSpaces(expectText[i]))
eciesDecrypt(cipher, data, s).expect("decryption should succeed")
check:
eciesDecrypt(cipher, data, s) == EciesStatus.Success
compare(data, expect) == true
test "ECIES/cpp-ethereum rlpx.cpp#L432-L459":
@ -167,5 +173,5 @@ suite "ECIES test suite":
var s = PrivateKey.fromHex(secretKeys[i])[]
var cipher = fromHex(stripSpaces(cipherData[i]))
check:
eciesDecrypt(cipher, data, s) == EciesStatus.Success
eciesDecrypt(cipher, data, s).isOk()
compare(data, expectData[i]) == true

View File

@ -7,7 +7,7 @@
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest, net
import unittest, net, options
import eth/p2p/enode
suite "ENode":
@ -28,26 +28,27 @@ suite "ENode":
]
const results = [
IncorrectScheme,
IncorrectNodeId,
IncorrectIP,
IncorrectPort,
IncorrectDiscPort,
IncorrectScheme,
IncorrectNodeId,
IncorrectScheme,
ENodeStatus.Success,
ENodeStatus.Success,
ENodeStatus.Success,
ENodeStatus.Success
some IncorrectScheme,
some IncorrectNodeId,
some IncorrectIP,
some IncorrectPort,
some IncorrectDiscPort,
some IncorrectScheme,
some IncorrectNodeId,
some IncorrectScheme,
none(ENodeError),
none(ENodeError),
none(ENodeError),
none(ENodeError),
]
for index in 0..<len(enodes):
var node: ENode
let res = initENode(enodes[index], node)
check res == results[index]
if res == ENodeStatus.Success:
check enodes[index] == $node
let res = ENode.fromString(enodes[index])
check (results[index].isSome and res.error == results[index].get) or
res.isOk
if res.isOk:
check enodes[index] == $res[]
test "Custom validation tests":
const enodes = [
@ -67,35 +68,23 @@ suite "ENode":
]
const results = [
IncorrectIP,
IncorrectIP,
IncorrectIP,
IncorrectIP,
ENodeStatus.Success,
IncorrectUri,
IncorrectPort,
IncorrectPort,
IncorrectDiscPort,
IncorrectDiscPort,
IncorrectUri,
IncorrectIP,
IncorrectNodeId
some IncorrectIP,
some IncorrectIP,
some IncorrectIP,
some IncorrectIP,
none(ENodeError),
some IncorrectUri,
some IncorrectPort,
some IncorrectPort,
some IncorrectDiscPort,
some IncorrectDiscPort,
some IncorrectUri,
some IncorrectIP,
some IncorrectNodeId
]
for index in 0..<len(enodes):
var node: ENode
let res = initENode(enodes[index], node)
check res == results[index]
let res = ENode.fromString(enodes[index])
test "isCorrect() tests":
var node: ENode
check isCorrect(node) == false
let ip = IpAddress(family:IpAddressFamily.IPv4)
node = Enode(address: Address(ip: ip))
check isCorrect(node) == false
node.address.tcpPort = Port(25)
check isCorrect(node) == false
node.address.udpPort = Port(25)
check isCorrect(node) == false
node.pubkey = PrivateKey.random()[].toPublicKey()[]
check isCorrect(node) == true
check (results[index].isSome and res.error == results[index].get) or
res.isOk

View File

@ -58,8 +58,7 @@ suite "Testing protocol handlers":
var node2 = setupTestNode(abc, xyz)
node2.startListening()
let peer = await node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
node2.address)))
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
check:
peer.isNil == false
@ -74,8 +73,7 @@ suite "Testing protocol handlers":
var node1 = setupTestNode(hah)
var node2 = setupTestNode(hah)
node2.startListening()
let peer = await node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
node2.address)))
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
check:
peer.isNil == true
# To check if the disconnection handler did not run

View File

@ -13,8 +13,7 @@ node1 = setupTestNode(eth, Whisper)
node2 = setupTestNode(eth, Whisper)
node2.startListening()
peer = waitFor node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
node2.address)))
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
proc testThunk(payload: openArray[byte]) =
var (msgId, msgData) = recvMsgMock(payload)

View File

@ -23,8 +23,7 @@ procSuite "Whisper connections":
var node1 = setupTestNode(Whisper)
var node2 = setupTestNode(Whisper)
node2.startListening()
waitFor node1.peerPool.connectToNode(newNode(initENode(node2.keys.pubKey,
node2.address)))
waitFor node1.peerPool.connectToNode(newNode(node2.toENode()))
asyncTest "Two peers connected":
check:
node1.peerPool.connectedNodes.len() == 1
@ -298,8 +297,7 @@ procSuite "Whisper connections":
var ln1 = setupTestNode(Whisper)
ln1.setLightNode(true)
await ln1.peerPool.connectToNode(newNode(initENode(node2.keys.pubKey,
node2.address)))
await ln1.peerPool.connectToNode(newNode(node2.toENode()))
let topic = [byte 0, 0, 0, 0]
@ -322,6 +320,5 @@ procSuite "Whisper connections":
ln2.setLightNode(true)
ln2.startListening()
let peer = await ln1.rlpxConnect(newNode(initENode(ln2.keys.pubKey,
ln2.address)))
let peer = await ln1.rlpxConnect(newNode(ln2.toENode()))
check peer.isNil == true

View File

@ -26,8 +26,7 @@ procSuite "Waku - Whisper bridge tests":
nodeWaku = setupTestNode(Waku)
nodeWakuWhisper.startListening()
let bridgeNode = newNode(initENode(nodeWakuWhisper.keys.pubKey,
nodeWakuWhisper.address))
let bridgeNode = newNode(nodeWakuWhisper.toENode())
nodeWakuWhisper.shareMessageQueue()
waitFor nodeWhisper.peerPool.connectToNode(bridgeNode)

View File

@ -48,9 +48,9 @@ suite "Waku connections":
n3.startListening()
let
p1 = await n2.rlpxConnect(newNode(initENode(n1.keys.pubKey, n1.address)))
p2 = await n2.rlpxConnect(newNode(initENode(n3.keys.pubKey, n3.address)))
p3 = await n4.rlpxConnect(newNode(initENode(n3.keys.pubKey, n3.address)))
p1 = await n2.rlpxConnect(newNode(n1.toENode()))
p2 = await n2.rlpxConnect(newNode(n3.toENode()))
p3 = await n4.rlpxConnect(newNode(n3.toENode()))
check:
p1.isNil
p2.isNil == false
@ -70,8 +70,7 @@ suite "Waku connections":
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1])
wakuNode.startListening()
await wakuTopicNode.peerPool.connectToNode(newNode(
initENode(wakuNode.keys.pubKey, wakuNode.address)))
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
# Update topic interest
check:
@ -93,8 +92,7 @@ suite "Waku connections":
wakuNode = setupTestNode(Waku)
wakuNode.startListening()
await wakuPowNode.peerPool.connectToNode(newNode(
initENode(wakuNode.keys.pubKey, wakuNode.address)))
await wakuPowNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
# Update minimum pow
await setPowRequirement(wakuPowNode, 1.0)
@ -114,8 +112,7 @@ suite "Waku connections":
wakuNode = setupTestNode(Waku)
wakuNode.startListening()
await wakuLightNode.peerPool.connectToNode(newNode(
initENode(wakuNode.keys.pubKey, wakuNode.address)))
await wakuLightNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
# Update minimum pow
await setLightNode(wakuLightNode, true)
@ -140,8 +137,7 @@ suite "Waku connections":
discard await wakuBloomNode.setTopicInterest(topics)
wakuBloomNode.startListening()
await wakuNode.peerPool.connectToNode(newNode(
initENode(wakuBloomNode.keys.pubKey, wakuBloomNode.address)))
await wakuNode.peerPool.connectToNode(newNode(wakuBloomNode.toENode()))
# Sanity check
check:
@ -188,8 +184,7 @@ suite "Waku connections":
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1, topic2])
wakuNode.startListening()
await wakuTopicNode.peerPool.connectToNode(newNode(
initENode(wakuNode.keys.pubKey, wakuNode.address)))
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
let payload = repeat(byte 0, 10)
check:
@ -217,8 +212,7 @@ suite "Waku connections":
wakuTopicNode.protocolState(Waku).config.bloom = some(toBloom([bloomTopic]))
wakuNode.startListening()
await wakuTopicNode.peerPool.connectToNode(newNode(
initENode(wakuNode.keys.pubKey, wakuNode.address)))
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
let payload = repeat(byte 0, 10)
check:
@ -235,8 +229,7 @@ suite "Waku connections":
await ln.setLightNode(true)
var fn = setupTestNode(Waku)
fn.startListening()
await ln.peerPool.connectToNode(newNode(initENode(fn.keys.pubKey,
fn.address)))
await ln.peerPool.connectToNode(newNode(fn.toENode()))
let topic = [byte 0, 0, 0, 0]

View File

@ -16,9 +16,8 @@ procSuite "Waku Mail Client":
var simpleServer = setupTestNode(Waku)
simpleServer.startListening()
let simpleServerNode = newNode(initENode(simpleServer.keys.pubKey,
simpleServer.address))
let clientNode = newNode(initENode(client.keys.pubKey, client.address))
let simpleServerNode = newNode(simpleServer.toENode())
let clientNode = newNode(client.toENode())
waitFor client.peerPool.connectToNode(simpleServerNode)
require:
waitFor simpleServer.waitForConnected().withTimeout(transmissionTimeout)