mirror of https://github.com/status-im/nim-eth.git
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:
parent
ac5bbe4d3d
commit
0b110f3287
|
@ -7,8 +7,12 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import nimcrypto/[bcmode, hmac, rijndael, pbkdf2, sha2, sysrand, utils, keccak],
|
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
|
const
|
||||||
# Version 3 constants
|
# Version 3 constants
|
||||||
|
@ -21,31 +25,26 @@ const
|
||||||
ScryptWorkFactor = 262_144
|
ScryptWorkFactor = 262_144
|
||||||
|
|
||||||
type
|
type
|
||||||
KeyFileStatus* = enum
|
KeyFileError* = enum
|
||||||
Success, ## No Error
|
RandomError = "kf: Random generator error"
|
||||||
RandomError, ## Random generator error
|
UuidError = "kf: UUID generator error"
|
||||||
UuidError, ## UUID generator error
|
BufferOverrun = "kf: Supplied buffer is too small"
|
||||||
BufferOverrun, ## Supplied buffer is too small
|
IncorrectDKLen = "kf: `dklen` parameter is 0 or more then MaxDKLen"
|
||||||
IncorrectDKLen, ## `dklen` parameter is 0 or more then MaxDKLen
|
MalformedError = "kf: JSON has incorrect structure"
|
||||||
MalformedError, ## JSON has incorrect structure
|
NotImplemented = "kf: Feature is not implemented"
|
||||||
NotImplemented, ## Feature is not implemented
|
NotSupported = "kf: Feature is not supported"
|
||||||
NotSupported, ## Feature is not supported
|
EmptyMac = "kf: `mac` parameter is zero length or not in hexadecimal form"
|
||||||
EmptyMac, ## `mac` parameter is zero length or not in
|
EmptyCiphertext = "kf: `ciphertext` parameter is zero length or not in hexadecimal format"
|
||||||
## hexadecimal form
|
EmptySalt = "kf: `salt` parameter is zero length or not in hexadecimal format"
|
||||||
EmptyCiphertext, ## `ciphertext` parameter is zero length or not in
|
EmptyIV = "kf: `cipherparams.iv` parameter is zero length or not in hexadecimal format"
|
||||||
## hexadecimal format
|
IncorrectIV = "kf: Size of IV vector is not equal to cipher block size"
|
||||||
EmptySalt, ## `salt` parameter is zero length or not in
|
PrfNotSupported = "kf: PRF algorithm for PBKDF2 is not supported"
|
||||||
## hexadecimal format
|
KdfNotSupported = "kf: KDF algorithm is not supported"
|
||||||
EmptyIV, ## `cipherparams.iv` parameter is zero length or not in
|
CipherNotSupported = "kf: `cipher` parameter is not supported"
|
||||||
## hexadecimal format
|
IncorrectMac = "kf: `mac` verification failed"
|
||||||
IncorrectIV, ## Size of IV vector is not equal to cipher block size
|
IncorrectPrivateKey = "kf: incorrect private key"
|
||||||
PrfNotSupported, ## PRF algorithm for PBKDF2 is not supported
|
OsError = "kf: OS specific error"
|
||||||
KdfNotSupported, ## KDF algorithm is not supported
|
JsonError = "kf: JSON encoder/decoder error"
|
||||||
CipherNotSupported, ## `cipher` parameter is not supported
|
|
||||||
IncorrectMac, ## `mac` verification failed
|
|
||||||
IncorrectPrivateKey, ## incorrect private key
|
|
||||||
OsError, ## OS specific error
|
|
||||||
JsonError ## JSON encoder/decoder error
|
|
||||||
|
|
||||||
KdfKind* = enum
|
KdfKind* = enum
|
||||||
PBKDF2, ## PBKDF2
|
PBKDF2, ## PBKDF2
|
||||||
|
@ -60,6 +59,11 @@ type
|
||||||
CipherNoSupport, ## Cipher not supported
|
CipherNoSupport, ## Cipher not supported
|
||||||
AES128CTR ## AES-128-CTR
|
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
|
const
|
||||||
SupportedHashes = [
|
SupportedHashes = [
|
||||||
"sha224", "sha256", "sha384", "sha512",
|
"sha224", "sha256", "sha384", "sha512",
|
||||||
|
@ -109,103 +113,98 @@ proc deriveKey(password: string,
|
||||||
salt: string,
|
salt: string,
|
||||||
kdfkind: KdfKind,
|
kdfkind: KdfKind,
|
||||||
hashkind: HashKind,
|
hashkind: HashKind,
|
||||||
workfactor: int,
|
workfactor: int): KfResult[array[DKLen, byte]] =
|
||||||
output: var openarray[byte]): KeyFileStatus =
|
if kdfkind == PBKDF2:
|
||||||
if kdfkind == SCRYPT:
|
var output: array[DKLen, byte]
|
||||||
return NotImplemented
|
|
||||||
elif kdfkind == PBKDF2:
|
|
||||||
var c = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
|
var c = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
|
||||||
case hashkind
|
case hashkind
|
||||||
of HashSHA2_224:
|
of HashSHA2_224:
|
||||||
var ctx: HMAC[sha224]
|
var ctx: HMAC[sha224]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA2_256:
|
of HashSHA2_256:
|
||||||
var ctx: HMAC[sha256]
|
var ctx: HMAC[sha256]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA2_384:
|
of HashSHA2_384:
|
||||||
var ctx: HMAC[sha384]
|
var ctx: HMAC[sha384]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA2_512:
|
of HashSHA2_512:
|
||||||
var ctx: HMAC[sha512]
|
var ctx: HMAC[sha512]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashKECCAK224:
|
of HashKECCAK224:
|
||||||
var ctx: HMAC[keccak224]
|
var ctx: HMAC[keccak224]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashKECCAK256:
|
of HashKECCAK256:
|
||||||
var ctx: HMAC[keccak256]
|
var ctx: HMAC[keccak256]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashKECCAK384:
|
of HashKECCAK384:
|
||||||
var ctx: HMAC[keccak384]
|
var ctx: HMAC[keccak384]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashKECCAK512:
|
of HashKECCAK512:
|
||||||
var ctx: HMAC[keccak512]
|
var ctx: HMAC[keccak512]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA3_224:
|
of HashSHA3_224:
|
||||||
var ctx: HMAC[sha3_224]
|
var ctx: HMAC[sha3_224]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA3_256:
|
of HashSHA3_256:
|
||||||
var ctx: HMAC[sha3_256]
|
var ctx: HMAC[sha3_256]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA3_384:
|
of HashSHA3_384:
|
||||||
var ctx: HMAC[sha3_384]
|
var ctx: HMAC[sha3_384]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
of HashSHA3_512:
|
of HashSHA3_512:
|
||||||
var ctx: HMAC[sha3_512]
|
var ctx: HMAC[sha3_512]
|
||||||
discard ctx.pbkdf2(password, salt, c, output)
|
discard ctx.pbkdf2(password, salt, c, output)
|
||||||
result = Success
|
ok(output)
|
||||||
else:
|
else:
|
||||||
result = PrfNotSupported
|
err(PrfNotSupported)
|
||||||
|
else:
|
||||||
|
err(NotImplemented)
|
||||||
|
|
||||||
proc encryptKey(seckey: PrivateKey,
|
proc encryptKey(seckey: PrivateKey,
|
||||||
cryptkind: CryptKind,
|
cryptkind: CryptKind,
|
||||||
key: openarray[byte],
|
key: openarray[byte],
|
||||||
iv: openarray[byte],
|
iv: openarray[byte]): KfResult[array[KeyLength, byte]] =
|
||||||
crypttext: var openarray[byte]): KeyFileStatus =
|
|
||||||
if len(crypttext) != KeyLength:
|
|
||||||
return BufferOverrun
|
|
||||||
if cryptkind == AES128CTR:
|
if cryptkind == AES128CTR:
|
||||||
|
var crypttext: array[KeyLength, byte]
|
||||||
var ctx: CTR[aes128]
|
var ctx: CTR[aes128]
|
||||||
ctx.init(toOpenArray(key, 0, 15), iv)
|
ctx.init(toOpenArray(key, 0, 15), iv)
|
||||||
ctx.encrypt(seckey.toRaw(), crypttext)
|
ctx.encrypt(seckey.toRaw(), crypttext)
|
||||||
ctx.clear()
|
ctx.clear()
|
||||||
result = Success
|
ok(crypttext)
|
||||||
else:
|
else:
|
||||||
result = NotImplemented
|
err(NotImplemented)
|
||||||
|
|
||||||
proc decryptKey(ciphertext: openarray[byte],
|
proc decryptKey(ciphertext: openarray[byte],
|
||||||
cryptkind: CryptKind,
|
cryptkind: CryptKind,
|
||||||
key: openarray[byte],
|
key: openarray[byte],
|
||||||
iv: openarray[byte],
|
iv: openarray[byte]): KfResult[array[KeyLength, byte]] =
|
||||||
plaintext: var openarray[byte]): KeyFileStatus =
|
|
||||||
if len(ciphertext) != len(plaintext):
|
|
||||||
return BufferOverrun
|
|
||||||
if cryptkind == AES128CTR:
|
if cryptkind == AES128CTR:
|
||||||
if len(iv) != aes128.sizeBlock:
|
if len(iv) != aes128.sizeBlock:
|
||||||
return IncorrectIV
|
return err(IncorrectIV)
|
||||||
|
var plaintext: array[KeyLength, byte]
|
||||||
var ctx: CTR[aes128]
|
var ctx: CTR[aes128]
|
||||||
ctx.init(toOpenArray(key, 0, 15), iv)
|
ctx.init(toOpenArray(key, 0, 15), iv)
|
||||||
ctx.decrypt(ciphertext, plaintext)
|
ctx.decrypt(ciphertext, plaintext)
|
||||||
ctx.clear()
|
ctx.clear()
|
||||||
result = Success
|
ok(plaintext)
|
||||||
else:
|
else:
|
||||||
result = NotImplemented
|
err(NotImplemented)
|
||||||
|
|
||||||
proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int,
|
proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int): KfResult[JsonNode] =
|
||||||
outjson: var JsonNode): KeyFileStatus =
|
|
||||||
if kdfkind == SCRYPT:
|
if kdfkind == SCRYPT:
|
||||||
var wf = if workfactor == 0: ScryptWorkFactor else: workfactor
|
let wf = if workfactor == 0: ScryptWorkFactor else: workfactor
|
||||||
outjson = %*
|
ok(%*
|
||||||
{
|
{
|
||||||
"dklen": DKLen,
|
"dklen": DKLen,
|
||||||
"n": wf,
|
"n": wf,
|
||||||
|
@ -213,19 +212,19 @@ proc kdfParams(kdfkind: KdfKind, salt: string, workfactor: int,
|
||||||
"p": ScryptP,
|
"p": ScryptP,
|
||||||
"salt": salt
|
"salt": salt
|
||||||
}
|
}
|
||||||
result = Success
|
)
|
||||||
elif kdfkind == PBKDF2:
|
elif kdfkind == PBKDF2:
|
||||||
var wf = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
|
let wf = if workfactor == 0: Pbkdf2WorkFactor else: workfactor
|
||||||
outjson = %*
|
ok(%*
|
||||||
{
|
{
|
||||||
"dklen": DKLen,
|
"dklen": DKLen,
|
||||||
"c": wf,
|
"c": wf,
|
||||||
"prf": "hmac-sha256",
|
"prf": "hmac-sha256",
|
||||||
"salt": salt
|
"salt": salt
|
||||||
}
|
}
|
||||||
result = Success
|
)
|
||||||
else:
|
else:
|
||||||
result = NotImplemented
|
err(NotImplemented)
|
||||||
|
|
||||||
proc decodeHex(m: string): seq[byte] =
|
proc decodeHex(m: string): seq[byte] =
|
||||||
if len(m) > 0:
|
if len(m) > 0:
|
||||||
|
@ -254,11 +253,10 @@ proc compareMac(m1: openarray[byte], m2: openarray[byte]): bool =
|
||||||
|
|
||||||
proc createKeyFileJson*(seckey: PrivateKey,
|
proc createKeyFileJson*(seckey: PrivateKey,
|
||||||
password: string,
|
password: string,
|
||||||
outjson: var JsonNode,
|
|
||||||
version: int = 3,
|
version: int = 3,
|
||||||
cryptkind: CryptKind = AES128CTR,
|
cryptkind: CryptKind = AES128CTR,
|
||||||
kdfkind: KdfKind = PBKDF2,
|
kdfkind: KdfKind = PBKDF2,
|
||||||
workfactor: int = 0): KeyFileStatus =
|
workfactor: int = 0): KfResult[JsonNode] =
|
||||||
## Create JSON object with keyfile structure.
|
## Create JSON object with keyfile structure.
|
||||||
##
|
##
|
||||||
## ``seckey`` - private key, which will be stored
|
## ``seckey`` - private key, which will be stored
|
||||||
|
@ -270,30 +268,24 @@ proc createKeyFileJson*(seckey: PrivateKey,
|
||||||
## ``kdfkind`` - algorithm for key deriviation function (default is PBKDF2)
|
## ``kdfkind`` - algorithm for key deriviation function (default is PBKDF2)
|
||||||
## ``workfactor`` - Key deriviation function work factor, 0 is to use
|
## ``workfactor`` - Key deriviation function work factor, 0 is to use
|
||||||
## default workfactor.
|
## default workfactor.
|
||||||
var res: KeyFileStatus
|
|
||||||
var iv: array[aes128.sizeBlock, byte]
|
var iv: array[aes128.sizeBlock, byte]
|
||||||
var ciphertext: array[KeyLength, byte]
|
|
||||||
var salt: array[SaltSize, byte]
|
var salt: array[SaltSize, byte]
|
||||||
var saltstr = newString(SaltSize)
|
var saltstr = newString(SaltSize)
|
||||||
var u: UUID
|
|
||||||
if randomBytes(iv) != aes128.sizeBlock:
|
if randomBytes(iv) != aes128.sizeBlock:
|
||||||
return RandomError
|
return err(RandomError)
|
||||||
if randomBytes(salt) != SaltSize:
|
if randomBytes(salt) != SaltSize:
|
||||||
return RandomError
|
return err(RandomError)
|
||||||
copyMem(addr saltstr[0], addr salt[0], SaltSize)
|
copyMem(addr saltstr[0], addr salt[0], SaltSize)
|
||||||
if uuidGenerate(u) != 1:
|
|
||||||
return UuidError
|
|
||||||
if kdfkind != PBKDF2:
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
var dkey = newSeq[byte](DKLen)
|
let u = ? uuidGenerate().mapErrTo(UuidError)
|
||||||
res = deriveKey(password, saltstr, kdfkind, HashSHA2_256,
|
|
||||||
workfactor, dkey)
|
if kdfkind != PBKDF2:
|
||||||
if res != Success:
|
return err(NotImplemented)
|
||||||
return res
|
|
||||||
res = encryptKey(seckey, cryptkind, dkey, iv, ciphertext)
|
let
|
||||||
if res != Success:
|
dkey = ? deriveKey(password, saltstr, kdfkind, HashSHA2_256, workfactor)
|
||||||
return res
|
ciphertext = ? encryptKey(seckey, cryptkind, dkey, iv)
|
||||||
|
|
||||||
var ctx: keccak256
|
var ctx: keccak256
|
||||||
ctx.init()
|
ctx.init()
|
||||||
ctx.update(toOpenArray(dkey, 16, 31))
|
ctx.update(toOpenArray(dkey, 16, 31))
|
||||||
|
@ -301,14 +293,11 @@ proc createKeyFileJson*(seckey: PrivateKey,
|
||||||
var mac = ctx.finish()
|
var mac = ctx.finish()
|
||||||
ctx.clear()
|
ctx.clear()
|
||||||
|
|
||||||
var params: JsonNode
|
let params = ? kdfParams(kdfkind, toHex(salt, true), workfactor)
|
||||||
res = kdfParams(kdfkind, toHex(salt, true), workfactor, params)
|
|
||||||
if res != Success:
|
|
||||||
return res
|
|
||||||
|
|
||||||
outjson = %*
|
ok(%*
|
||||||
{
|
{
|
||||||
"address": seckey.toPublicKey().tryGet().toAddress(false),
|
"address": (? seckey.toPublicKey().mapErrTo(IncorrectPrivateKey)).toAddress(false),
|
||||||
"crypto": {
|
"crypto": {
|
||||||
"cipher": $cryptkind,
|
"cipher": $cryptkind,
|
||||||
"cipherparams": {
|
"cipherparams": {
|
||||||
|
@ -322,34 +311,29 @@ proc createKeyFileJson*(seckey: PrivateKey,
|
||||||
"id": $u,
|
"id": $u,
|
||||||
"version": version
|
"version": version
|
||||||
}
|
}
|
||||||
result = Success
|
)
|
||||||
|
|
||||||
proc decodeKeyFileJson*(j: JsonNode,
|
proc decodeKeyFileJson*(j: JsonNode,
|
||||||
password: string,
|
password: string): KfResult[PrivateKey] =
|
||||||
seckey: var PrivateKey): KeyFileStatus =
|
|
||||||
## Decode private key into ``seckey`` from keyfile json object ``j`` using
|
## Decode private key into ``seckey`` from keyfile json object ``j`` using
|
||||||
## password string ``password``.
|
## password string ``password``.
|
||||||
var
|
|
||||||
res: KeyFileStatus
|
|
||||||
plaintext: array[KeyLength, byte]
|
|
||||||
|
|
||||||
var crypto = j.getOrDefault("crypto")
|
var crypto = j.getOrDefault("crypto")
|
||||||
if isNil(crypto):
|
if isNil(crypto):
|
||||||
return MalformedError
|
return err(MalformedError)
|
||||||
|
|
||||||
var kdf = crypto.getOrDefault("kdf")
|
var kdf = crypto.getOrDefault("kdf")
|
||||||
if isNil(kdf):
|
if isNil(kdf):
|
||||||
return MalformedError
|
return err(MalformedError)
|
||||||
|
|
||||||
var cipherparams = crypto.getOrDefault("cipherparams")
|
var cipherparams = crypto.getOrDefault("cipherparams")
|
||||||
if isNil(cipherparams):
|
if isNil(cipherparams):
|
||||||
return MalformedError
|
return err(MalformedError)
|
||||||
|
|
||||||
if kdf.getStr() == "pbkdf2":
|
if kdf.getStr() == "pbkdf2":
|
||||||
var params = crypto.getOrDefault("kdfparams")
|
var params = crypto.getOrDefault("kdfparams")
|
||||||
|
|
||||||
if isNil(params):
|
if isNil(params):
|
||||||
return MalformedError
|
return err(MalformedError)
|
||||||
|
|
||||||
var salt = decodeSalt(params.getOrDefault("salt").getStr())
|
var salt = decodeSalt(params.getOrDefault("salt").getStr())
|
||||||
var ciphertext = decodeHex(crypto.getOrDefault("ciphertext").getStr())
|
var ciphertext = decodeHex(crypto.getOrDefault("ciphertext").getStr())
|
||||||
|
@ -358,29 +342,26 @@ proc decodeKeyFileJson*(j: JsonNode,
|
||||||
var iv = decodeHex(cipherparams.getOrDefault("iv").getStr())
|
var iv = decodeHex(cipherparams.getOrDefault("iv").getStr())
|
||||||
|
|
||||||
if len(salt) == 0:
|
if len(salt) == 0:
|
||||||
return EmptySalt
|
return err(EmptySalt)
|
||||||
if len(ciphertext) == 0:
|
if len(ciphertext) == 0:
|
||||||
return EmptyCiphertext
|
return err(EmptyCiphertext)
|
||||||
if len(mactext) == 0:
|
if len(mactext) == 0:
|
||||||
return EmptyMac
|
return err(EmptyMac)
|
||||||
if cryptkind == CipherNoSupport:
|
if cryptkind == CipherNoSupport:
|
||||||
return CipherNotSupported
|
return err(CipherNotSupported)
|
||||||
|
|
||||||
var dklen = params.getOrDefault("dklen").getInt()
|
var dklen = params.getOrDefault("dklen").getInt()
|
||||||
var c = params.getOrDefault("c").getInt()
|
var c = params.getOrDefault("c").getInt()
|
||||||
var hash = getPrfHash(params.getOrDefault("prf").getStr())
|
var hash = getPrfHash(params.getOrDefault("prf").getStr())
|
||||||
|
|
||||||
if hash == HashNoSupport:
|
if hash == HashNoSupport:
|
||||||
return PrfNotSupported
|
return err(PrfNotSupported)
|
||||||
if dklen == 0 or dklen > MaxDKLen:
|
if dklen == 0 or dklen > MaxDKLen:
|
||||||
return IncorrectDKLen
|
return err(IncorrectDKLen)
|
||||||
if len(ciphertext) != KeyLength:
|
if len(ciphertext) != KeyLength:
|
||||||
return IncorrectPrivateKey
|
return err(IncorrectPrivateKey)
|
||||||
|
|
||||||
var dkey = newSeq[byte](dklen)
|
let dkey = ? deriveKey(password, salt, PBKDF2, hash, c)
|
||||||
res = deriveKey(password, salt, PBKDF2, hash, c, dkey)
|
|
||||||
if res != Success:
|
|
||||||
return res
|
|
||||||
|
|
||||||
var ctx: keccak256
|
var ctx: keccak256
|
||||||
ctx.init()
|
ctx.init()
|
||||||
|
@ -388,51 +369,39 @@ proc decodeKeyFileJson*(j: JsonNode,
|
||||||
ctx.update(ciphertext)
|
ctx.update(ciphertext)
|
||||||
var mac = ctx.finish()
|
var mac = ctx.finish()
|
||||||
if not compareMac(mac.data, mactext):
|
if not compareMac(mac.data, mactext):
|
||||||
return IncorrectMac
|
return err(IncorrectMac)
|
||||||
|
|
||||||
res = decryptKey(ciphertext, cryptkind, dkey, iv, plaintext)
|
let plaintext = ? decryptKey(ciphertext, cryptkind, dkey, iv)
|
||||||
if res != Success:
|
|
||||||
return res
|
PrivateKey.fromRaw(plaintext).mapErrTo(IncorrectPrivateKey)
|
||||||
try:
|
|
||||||
seckey = PrivateKey.fromRaw(plaintext).tryGet()
|
|
||||||
except CatchableError:
|
|
||||||
return IncorrectPrivateKey
|
|
||||||
result = Success
|
|
||||||
else:
|
else:
|
||||||
return KdfNotSupported
|
err(KdfNotSupported)
|
||||||
|
|
||||||
proc loadKeyFile*(pathname: string,
|
proc loadKeyFile*(pathname: string,
|
||||||
password: string,
|
password: string): KfResult[PrivateKey] =
|
||||||
seckey: var PrivateKey): KeyFileStatus =
|
|
||||||
## Load and decode private key ``seckey`` from file with pathname
|
## Load and decode private key ``seckey`` from file with pathname
|
||||||
## ``pathname``, using password string ``password``.
|
## ``pathname``, using password string ``password``.
|
||||||
var data: JsonNode
|
var data: JsonNode
|
||||||
var stream = newFileStream(pathname)
|
|
||||||
if isNil(stream):
|
|
||||||
return OsError
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = parseFile(pathname)
|
data = json.parseFile(pathname)
|
||||||
result = Success
|
except JsonParsingError:
|
||||||
except CatchableError:
|
return err(JsonError)
|
||||||
result = JsonError
|
except Exception: # json raises Exception
|
||||||
finally:
|
return err(OsError)
|
||||||
stream.close()
|
|
||||||
|
|
||||||
if result == Success:
|
decodeKeyFileJson(data, password)
|
||||||
result = decodeKeyFileJson(data, password, seckey)
|
|
||||||
|
|
||||||
proc saveKeyFile*(pathname: string,
|
proc saveKeyFile*(pathname: string,
|
||||||
jobject: JsonNode): KeyFileStatus =
|
jobject: JsonNode): KfResult[void] =
|
||||||
## Save JSON object ``jobject`` to file with pathname ``pathname``.
|
## Save JSON object ``jobject`` to file with pathname ``pathname``.
|
||||||
var
|
var
|
||||||
f: File
|
f: File
|
||||||
if not f.open(pathname, fmWrite):
|
if not f.open(pathname, fmWrite):
|
||||||
return OsError
|
return err(OsError)
|
||||||
try:
|
try:
|
||||||
f.write($jobject)
|
f.write($jobject)
|
||||||
result = Success
|
ok()
|
||||||
except CatchableError:
|
except CatchableError:
|
||||||
result = OsError
|
err(OsError)
|
||||||
finally:
|
finally:
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
@ -16,40 +16,47 @@
|
||||||
## - ``FreeBSD``, ``OpenBSD``, ``NetBSD``,
|
## - ``FreeBSD``, ``OpenBSD``, ``NetBSD``,
|
||||||
## ``DragonflyBSD`` - using `uuid_create()`.
|
## ``DragonflyBSD`` - using `uuid_create()`.
|
||||||
|
|
||||||
import nimcrypto/utils, stew/endians2
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
import stew/[byteutils, endians2, result]
|
||||||
|
|
||||||
|
from nimcrypto import stripSpaces
|
||||||
|
|
||||||
|
export result
|
||||||
|
|
||||||
type
|
type
|
||||||
UUIDException = object of CatchableError
|
|
||||||
|
|
||||||
UUID* = object
|
UUID* = object
|
||||||
## Represents UUID object
|
## Represents UUID object
|
||||||
data*: array[16, byte]
|
data*: array[16, byte]
|
||||||
|
|
||||||
proc raiseInvalidUuid() =
|
UuidResult*[T] = Result[T, cstring]
|
||||||
raise newException(UUIDException, "Invalid UUID!")
|
|
||||||
|
|
||||||
proc uuidFromString*(s: string): UUID =
|
proc uuidFromString*(s: string): UuidResult[UUID] =
|
||||||
## Convert string representation of UUID into UUID object.
|
## Convert string representation of UUID into UUID object.
|
||||||
if len(s) != 36:
|
if len(s) != 36:
|
||||||
raiseInvalidUuid()
|
return err("uuid: length must be 36 bytes")
|
||||||
for i in 0..<len(s):
|
for i in 0..<len(s):
|
||||||
if s[i] notin {'A'..'F', '0'..'9', 'a'..'f', '-'}:
|
if s[i] notin {'A'..'F', '0'..'9', 'a'..'f', '-'}:
|
||||||
raiseInvalidUuid()
|
return err("uuid: invalid characters")
|
||||||
var d = fromHex(stripSpaces(s))
|
try:
|
||||||
var
|
var d = hexToSeqByte(stripSpaces(s))
|
||||||
a = uint32.fromBytesBE(d.toOpenArray(0, 3))
|
var
|
||||||
b = uint16.fromBytesBE(d.toOpenArray(4, 5))
|
a = uint32.fromBytesBE(d.toOpenArray(0, 3))
|
||||||
c = uint16.fromBytesBE(d.toOpenArray(6, 7))
|
b = uint16.fromBytesBE(d.toOpenArray(4, 5))
|
||||||
|
c = uint16.fromBytesBE(d.toOpenArray(6, 7))
|
||||||
|
|
||||||
copyMem(addr result.data[0], addr a, 4)
|
var ret: UUID
|
||||||
copyMem(addr result.data[4], addr b, 2)
|
copyMem(addr ret.data[0], addr a, 4)
|
||||||
copyMem(addr result.data[6], addr c, 2)
|
copyMem(addr ret.data[4], addr b, 2)
|
||||||
copyMem(addr result.data[8], addr d[8], 8)
|
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.
|
## Convert UUID object into string representation.
|
||||||
## You can use ``lowercase`` flag to specify letter case
|
## UUID are lowercase, per RFC4122
|
||||||
## of output string.
|
|
||||||
result = newStringOfCap(38)
|
result = newStringOfCap(38)
|
||||||
var d: array[8, byte]
|
var d: array[8, byte]
|
||||||
var
|
var
|
||||||
|
@ -60,22 +67,22 @@ proc uuidToString*(u: UUID, lowercase: bool = false): string =
|
||||||
copyMem(addr d[4], addr b, 2)
|
copyMem(addr d[4], addr b, 2)
|
||||||
copyMem(addr d[6], addr c, 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("-")
|
||||||
result.add(toHex(toOpenArray(d, 4, 5), lowercase))
|
result.add(toHex(toOpenArray(d, 4, 5)))
|
||||||
result.add("-")
|
result.add("-")
|
||||||
result.add(toHex(toOpenArray(d, 6, 7), lowercase))
|
result.add(toHex(toOpenArray(d, 6, 7)))
|
||||||
result.add("-")
|
result.add("-")
|
||||||
result.add(toHex(toOpenArray(u.data, 8, 9), lowercase))
|
result.add(toHex(toOpenArray(u.data, 8, 9)))
|
||||||
result.add("-")
|
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.
|
## Convert UUID object to lowercase string representation.
|
||||||
uuidToString(u, true)
|
uuidToString(u)
|
||||||
|
|
||||||
when defined(nimdoc):
|
when defined(nimdoc):
|
||||||
proc uuidGenerate*(output: var UUID): int
|
proc uuidGenerate*(): UuidResult[UUID]
|
||||||
## Generates new unique UUID and store it to `output`.
|
## Generates new unique UUID and store it to `output`.
|
||||||
##
|
##
|
||||||
## Return 1 on success, and 0 on failure
|
## Return 1 on success, and 0 on failure
|
||||||
|
@ -85,9 +92,10 @@ when defined(posix):
|
||||||
proc uuidGenerateRandom(a: pointer)
|
proc uuidGenerateRandom(a: pointer)
|
||||||
{.importc: "uuid_generate_random", header: "uuid/uuid.h".}
|
{.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))
|
uuidGenerateRandom(cast[pointer](addr output))
|
||||||
result = 1
|
ok(output)
|
||||||
|
|
||||||
elif defined(freebsd) or defined(netbsd) or defined(openbsd) or
|
elif defined(freebsd) or defined(netbsd) or defined(openbsd) or
|
||||||
defined(dragonflybsd):
|
defined(dragonflybsd):
|
||||||
|
@ -95,13 +103,14 @@ when defined(posix):
|
||||||
proc uuidCreate(a: pointer, s: ptr uint32)
|
proc uuidCreate(a: pointer, s: ptr uint32)
|
||||||
{.importc: "uuid_create", header: "uuid.h".}
|
{.importc: "uuid_create", header: "uuid.h".}
|
||||||
|
|
||||||
proc uuidGenerate*(output: var UUID): int =
|
proc uuidGenerate*(): UuidResult[UUID] =
|
||||||
var status: uint32 = 0
|
var status: uint32 = 0
|
||||||
|
var output: UUID
|
||||||
uuidCreate(cast[pointer](addr output), addr status)
|
uuidCreate(cast[pointer](addr output), addr status)
|
||||||
if status == 0:
|
if status == 0:
|
||||||
result = 1
|
ok(output)
|
||||||
else:
|
else:
|
||||||
result = 0
|
err("uuid: uuid_create failed")
|
||||||
|
|
||||||
elif defined(linux) or defined(android):
|
elif defined(linux) or defined(android):
|
||||||
import posix, os, nimcrypto/sysrand
|
import posix, os, nimcrypto/sysrand
|
||||||
|
@ -124,39 +133,44 @@ when defined(posix):
|
||||||
break
|
break
|
||||||
discard posix.close(fd)
|
discard posix.close(fd)
|
||||||
|
|
||||||
proc uuidGenerate*(output: var UUID): int =
|
proc uuidGenerate*(): UuidResult[UUID] =
|
||||||
result = 0
|
|
||||||
var buffer = newString(37)
|
var buffer = newString(37)
|
||||||
if uuidRead(buffer, 36) == 36:
|
if uuidRead(buffer, 36) == 36:
|
||||||
buffer.setLen(36)
|
buffer.setLen(36)
|
||||||
output = uuidFromString(buffer)
|
uuidFromString(buffer)
|
||||||
result = 1
|
|
||||||
else:
|
else:
|
||||||
|
var output: UUID
|
||||||
if randomBytes(output.data) == sizeof(output.data):
|
if randomBytes(output.data) == sizeof(output.data):
|
||||||
result = 1
|
ok(output)
|
||||||
|
else:
|
||||||
|
err("uuid: cannot get random bytes")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import nimcrypto/sysrand
|
import nimcrypto/sysrand
|
||||||
|
|
||||||
proc uuidGenerate*(output: var UUID): int =
|
proc uuidGenerate*(): UuidResult[UUID] =
|
||||||
|
var output: UUID
|
||||||
if randomBytes(output.data) == sizeof(output.data):
|
if randomBytes(output.data) == sizeof(output.data):
|
||||||
result = 1
|
ok(output)
|
||||||
else:
|
else:
|
||||||
result = 0
|
err("uuid: cannot get random bytes")
|
||||||
|
|
||||||
elif defined(windows):
|
elif defined(windows):
|
||||||
proc UuidCreate(p: pointer): int32
|
proc UuidCreate(p: pointer): int32
|
||||||
{.stdcall, dynlib: "rpcrt4", importc: "UuidCreate".}
|
{.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:
|
if UuidCreate(cast[pointer](addr output)) == 0:
|
||||||
return 1
|
ok(output)
|
||||||
else:
|
else:
|
||||||
return 0
|
err("uuid: UuidCreate failed")
|
||||||
elif not defined(nimdoc):
|
elif not defined(nimdoc):
|
||||||
import nimcrypto/sysrand
|
import nimcrypto/sysrand
|
||||||
|
|
||||||
proc uuidGenerate*(output: var UUID): int =
|
proc uuidGenerate*(): UuidResult[UUID] =
|
||||||
|
var output: UUID
|
||||||
if randomBytes(output.data) == sizeof(output.data):
|
if randomBytes(output.data) == sizeof(output.data):
|
||||||
result = 1
|
ok(output)
|
||||||
else:
|
else:
|
||||||
result = 0
|
err("uuid: cannot get random bytes")
|
||||||
|
|
|
@ -74,7 +74,7 @@ proc processIncoming(server: StreamServer,
|
||||||
node.peerPool.addPeer(peer)
|
node.peerPool.addPeer(peer)
|
||||||
|
|
||||||
proc listeningAddress*(node: EthereumNode): ENode =
|
proc listeningAddress*(node: EthereumNode): ENode =
|
||||||
return initENode(node.keys.pubKey, node.address)
|
node.toENode()
|
||||||
|
|
||||||
proc startListening*(node: EthereumNode) =
|
proc startListening*(node: EthereumNode) =
|
||||||
# TODO allow binding to specific IP / IPv6 / etc
|
# TODO allow binding to specific IP / IPv6 / etc
|
||||||
|
|
459
eth/p2p/auth.nim
459
eth/p2p/auth.nim
|
@ -10,12 +10,16 @@
|
||||||
|
|
||||||
## This module implements Ethereum authentication
|
## This module implements Ethereum authentication
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import eth/[keys, rlp], nimcrypto
|
import eth/[keys, rlp], nimcrypto
|
||||||
import ecies
|
import ecies
|
||||||
import stew/[byteutils, endians2]
|
import stew/[byteutils, endians2, result]
|
||||||
|
|
||||||
|
export result
|
||||||
|
|
||||||
const
|
const
|
||||||
SupportedRlpxVersion* = 4
|
SupportedRlpxVersion* = 4'u8
|
||||||
PlainAuthMessageV4Length* = 194
|
PlainAuthMessageV4Length* = 194
|
||||||
AuthMessageV4Length* = 307
|
AuthMessageV4Length* = 307
|
||||||
PlainAuthMessageEIP8Length = 169
|
PlainAuthMessageEIP8Length = 169
|
||||||
|
@ -49,18 +53,17 @@ type
|
||||||
Responder, ## `Handshake` owner is connection responder
|
Responder, ## `Handshake` owner is connection responder
|
||||||
Eip8 ## Flag indicates that EIP-8 handshake is used
|
Eip8 ## Flag indicates that EIP-8 handshake is used
|
||||||
|
|
||||||
AuthStatus* = enum
|
AuthError* = enum
|
||||||
Success, ## Operation was successful
|
RandomError = "auth: could not obtain random data"
|
||||||
RandomError, ## Could not obtain random data
|
EcdhError = "auth: ECDH shared secret could not be calculated"
|
||||||
EcdhError, ## ECDH shared secret could not be calculated
|
BufferOverrun = "auth: buffer overrun"
|
||||||
BufferOverrun, ## Buffer overrun error
|
SignatureError = "auth: signature could not be obtained"
|
||||||
SignatureError, ## Signature could not be obtained
|
EciesError = "auth: ECIES encryption/decryption error"
|
||||||
EciesError, ## ECIES encryption/decryption error
|
InvalidPubKey = "auth: invalid public key"
|
||||||
InvalidPubKey, ## Invalid public key
|
InvalidAuth = "auth: invalid Authentication message"
|
||||||
InvalidAuth, ## Invalid Authentication message
|
InvalidAck = "auth: invalid Authentication ACK message"
|
||||||
InvalidAck, ## Invalid Authentication ACK message
|
RlpError = "auth: error while decoding RLP stream"
|
||||||
RlpError, ## Error while decoding RLP stream
|
IncompleteError = "auth: data incomplete"
|
||||||
IncompleteError ## Data incomplete error
|
|
||||||
|
|
||||||
Handshake* = object
|
Handshake* = object
|
||||||
version*: uint8 ## protocol version
|
version*: uint8 ## protocol version
|
||||||
|
@ -79,80 +82,94 @@ type
|
||||||
egressMac*: keccak256
|
egressMac*: keccak256
|
||||||
ingressMac*: keccak256
|
ingressMac*: keccak256
|
||||||
|
|
||||||
AuthException* = object of CatchableError
|
AuthResult*[T] = Result[T, AuthError]
|
||||||
|
|
||||||
template toa(a, b, c: untyped): untyped =
|
template toa(a, b, c: untyped): untyped =
|
||||||
toOpenArray((a), (b), (b) + (c) - 1)
|
toOpenArray((a), (b), (b) + (c) - 1)
|
||||||
|
|
||||||
proc sxor[T](a: var openarray[T], b: openarray[T]) {.inline.} =
|
proc `xor`[N: static int](a, b: array[N, byte]): array[N, byte] =
|
||||||
doAssert(len(a) == len(b))
|
|
||||||
for i in 0 ..< len(a):
|
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},
|
proc mapErrTo[T, E](r: Result[T, E], v: static AuthError): AuthResult[T] =
|
||||||
version: int = SupportedRlpxVersion): Handshake =
|
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.
|
## Create new `Handshake` object.
|
||||||
result.version = byte(version and 0xFF)
|
|
||||||
result.flags = flags
|
var
|
||||||
result.ephemeral = KeyPair.random().tryGet()
|
initiatorNonce: Nonce
|
||||||
|
responderNonce: Nonce
|
||||||
|
expectedLength: int
|
||||||
|
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
|
||||||
|
|
||||||
if Initiator in flags:
|
if Initiator in flags:
|
||||||
result.expectedLength = AckMessageV4Length
|
expectedLength = AckMessageV4Length
|
||||||
if randomBytes(result.initiatorNonce) != len(result.initiatorNonce):
|
if randomBytes(initiatorNonce) != len(initiatorNonce):
|
||||||
raise newException(AuthException, "Could not obtain random data!")
|
return err(RandomError)
|
||||||
else:
|
else:
|
||||||
result.expectedLength = AuthMessageV4Length
|
expectedLength = AuthMessageV4Length
|
||||||
if randomBytes(result.responderNonce) != len(result.responderNonce):
|
if randomBytes(responderNonce) != len(responderNonce):
|
||||||
raise newException(AuthException, "Could not obtain random data!")
|
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,
|
proc authMessagePreEIP8(h: var Handshake,
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
outlen: var int,
|
outlen: var int,
|
||||||
flag: int = 0,
|
flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create plain pre-EIP8 authentication message.
|
## Create plain pre-EIP8 authentication message.
|
||||||
var
|
var
|
||||||
buffer: array[PlainAuthMessageV4Length, byte]
|
buffer: array[PlainAuthMessageV4Length, byte]
|
||||||
flagb: byte
|
|
||||||
header: ptr AuthMessageV4
|
|
||||||
outlen = 0
|
outlen = 0
|
||||||
flagb = byte(flag)
|
let header = cast[ptr AuthMessageV4](addr buffer[0])
|
||||||
header = cast[ptr AuthMessageV4](addr buffer[0])
|
|
||||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
var secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
|
||||||
if secret.isErr:
|
let xornonce = secret.data xor h.initiatorNonce
|
||||||
return(EcdhError)
|
|
||||||
var xornonce = h.initiatorNonce
|
secret.clear()
|
||||||
xornonce.sxor(secret[].data)
|
|
||||||
secret[].clear()
|
let signature = ? sign(
|
||||||
let sig = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
|
h.ephemeral.seckey, SkMessage(data: xornonce)).mapErrTo(SignatureError)
|
||||||
if sig.isErr:
|
|
||||||
return(SignatureError)
|
|
||||||
h.remoteHPubkey = pubkey
|
h.remoteHPubkey = pubkey
|
||||||
header.signature = sig[].toRaw()
|
header.signature = signature.toRaw()
|
||||||
header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data
|
header.keyhash = keccak256.digest(h.ephemeral.pubkey.toRaw()).data
|
||||||
header.pubkey = h.host.pubkey.toRaw()
|
header.pubkey = h.host.pubkey.toRaw()
|
||||||
header.nonce = h.initiatorNonce
|
header.nonce = h.initiatorNonce
|
||||||
header.flag = flagb
|
header.flag = flag
|
||||||
if encrypt:
|
if encrypt:
|
||||||
if len(output) < AuthMessageV4Length:
|
if len(output) < AuthMessageV4Length:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
|
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
outlen = AuthMessageV4Length
|
outlen = AuthMessageV4Length
|
||||||
result = Success
|
|
||||||
else:
|
else:
|
||||||
if len(output) < PlainAuthMessageV4Length:
|
if len(output) < PlainAuthMessageV4Length:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
copyMem(addr output[0], addr buffer[0], PlainAuthMessageV4Length)
|
copyMem(addr output[0], addr buffer[0], PlainAuthMessageV4Length)
|
||||||
outlen = PlainAuthMessageV4Length
|
outlen = PlainAuthMessageV4Length
|
||||||
result = Success
|
|
||||||
|
ok()
|
||||||
|
|
||||||
proc authMessageEIP8(h: var Handshake,
|
proc authMessageEIP8(h: var Handshake,
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
outlen: var int,
|
outlen: var int,
|
||||||
flag: int = 0,
|
flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create EIP8 authentication message.
|
## Create EIP8 authentication message.
|
||||||
var
|
var
|
||||||
buffer: array[PlainAuthMessageMaxEIP8, byte]
|
buffer: array[PlainAuthMessageMaxEIP8, byte]
|
||||||
|
@ -160,17 +177,17 @@ proc authMessageEIP8(h: var Handshake,
|
||||||
|
|
||||||
doAssert(EIP8 in h.flags)
|
doAssert(EIP8 in h.flags)
|
||||||
outlen = 0
|
outlen = 0
|
||||||
var secret = ecdhRaw(h.host.seckey, pubkey)
|
var
|
||||||
if secret.isErr:
|
secret = ? ecdhRaw(h.host.seckey, pubkey).mapErrTo(EcdhError)
|
||||||
return(EcdhError)
|
xornonce = secret.data xor h.initiatorNonce
|
||||||
var xornonce = h.initiatorNonce
|
|
||||||
xornonce.sxor(secret[].data)
|
secret.clear()
|
||||||
secret[].clear()
|
|
||||||
var sig = sign(h.ephemeral.seckey, SkMessage(data: xornonce))
|
let signature = ? sign(
|
||||||
if sig.isErr:
|
h.ephemeral.seckey, SkMessage(data: xornonce)).mapErrTo(SignatureError)
|
||||||
return(SignatureError)
|
|
||||||
h.remoteHPubkey = pubkey
|
h.remoteHPubkey = pubkey
|
||||||
var payload = rlp.encodeList(sig[].toRaw(),
|
var payload = rlp.encodeList(signature.toRaw(),
|
||||||
h.host.pubkey.toRaw(),
|
h.host.pubkey.toRaw(),
|
||||||
h.initiatorNonce,
|
h.initiatorNonce,
|
||||||
[byte(h.version)])
|
[byte(h.version)])
|
||||||
|
@ -178,65 +195,67 @@ proc authMessageEIP8(h: var Handshake,
|
||||||
let pencsize = eciesEncryptedLength(len(payload))
|
let pencsize = eciesEncryptedLength(len(payload))
|
||||||
while true:
|
while true:
|
||||||
if randomBytes(addr padsize, 1) != 1:
|
if randomBytes(addr padsize, 1) != 1:
|
||||||
return(RandomError)
|
return err(RandomError)
|
||||||
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
|
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
|
||||||
break
|
break
|
||||||
# It is possible to make packet size constant by uncommenting this line
|
# It is possible to make packet size constant by uncommenting this line
|
||||||
# padsize = 24
|
# padsize = 24
|
||||||
var wosize = pencsize + int(padsize)
|
let wosize = pencsize + int(padsize)
|
||||||
let fullsize = wosize + 2
|
let fullsize = wosize + 2
|
||||||
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
|
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
|
||||||
int(padsize))) != int(padsize):
|
int(padsize))) != int(padsize):
|
||||||
return(RandomError)
|
return err(RandomError)
|
||||||
if encrypt:
|
if encrypt:
|
||||||
copyMem(addr buffer[0], addr payload[0], len(payload))
|
copyMem(addr buffer[0], addr payload[0], len(payload))
|
||||||
if len(output) < fullsize:
|
if len(output) < fullsize:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
let wosizeBE = uint16(wosize).toBytesBE()
|
let wosizeBE = uint16(wosize).toBytesBE()
|
||||||
output[0..<2] = wosizeBE
|
output[0..<2] = wosizeBE
|
||||||
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||||
toa(output, 2, wosize), pubkey,
|
toa(output, 2, wosize), pubkey,
|
||||||
toa(output, 0, 2)) != EciesStatus.Success:
|
toa(output, 0, 2)).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
outlen = fullsize
|
outlen = fullsize
|
||||||
else:
|
else:
|
||||||
let plainsize = len(payload) + int(padsize)
|
let plainsize = len(payload) + int(padsize)
|
||||||
if len(output) < plainsize:
|
if len(output) < plainsize:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
copyMem(addr output[0], addr buffer[0], plainsize)
|
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||||
outlen = plainsize
|
outlen = plainsize
|
||||||
result = Success
|
|
||||||
|
ok()
|
||||||
|
|
||||||
proc ackMessagePreEIP8(h: var Handshake,
|
proc ackMessagePreEIP8(h: var Handshake,
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
outlen: var int,
|
outlen: var int,
|
||||||
flag: int = 0,
|
flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create plain pre-EIP8 authentication ack message.
|
## Create plain pre-EIP8 authentication ack message.
|
||||||
var buffer: array[PlainAckMessageV4Length, byte]
|
var buffer: array[PlainAckMessageV4Length, byte]
|
||||||
outlen = 0
|
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.pubkey = h.ephemeral.pubkey.toRaw()
|
||||||
header.nonce = h.responderNonce
|
header.nonce = h.responderNonce
|
||||||
header.flag = byte(flag)
|
header.flag = flag
|
||||||
if encrypt:
|
if encrypt:
|
||||||
if len(output) < AckMessageV4Length:
|
if len(output) < AckMessageV4Length:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
|
if eciesEncrypt(buffer, output, h.remoteHPubkey).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
outlen = AckMessageV4Length
|
outlen = AckMessageV4Length
|
||||||
else:
|
else:
|
||||||
if len(output) < PlainAckMessageV4Length:
|
if len(output) < PlainAckMessageV4Length:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
copyMem(addr output[0], addr buffer[0], PlainAckMessageV4Length)
|
copyMem(addr output[0], addr buffer[0], PlainAckMessageV4Length)
|
||||||
outlen = PlainAckMessageV4Length
|
outlen = PlainAckMessageV4Length
|
||||||
result = Success
|
|
||||||
|
ok()
|
||||||
|
|
||||||
proc ackMessageEIP8(h: var Handshake,
|
proc ackMessageEIP8(h: var Handshake,
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
outlen: var int,
|
outlen: var int,
|
||||||
flag: int = 0,
|
flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create EIP8 authentication ack message.
|
## Create EIP8 authentication ack message.
|
||||||
var
|
var
|
||||||
buffer: array[PlainAckMessageMaxEIP8, byte]
|
buffer: array[PlainAckMessageMaxEIP8, byte]
|
||||||
|
@ -250,34 +269,35 @@ proc ackMessageEIP8(h: var Handshake,
|
||||||
let pencsize = eciesEncryptedLength(len(payload))
|
let pencsize = eciesEncryptedLength(len(payload))
|
||||||
while true:
|
while true:
|
||||||
if randomBytes(addr padsize, 1) != 1:
|
if randomBytes(addr padsize, 1) != 1:
|
||||||
return(RandomError)
|
return err(RandomError)
|
||||||
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
|
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
|
||||||
break
|
break
|
||||||
# It is possible to make packet size constant by uncommenting this line
|
# It is possible to make packet size constant by uncommenting this line
|
||||||
# padsize = 0
|
# padsize = 0
|
||||||
var wosize = pencsize + int(padsize)
|
let wosize = pencsize + int(padsize)
|
||||||
let fullsize = wosize + 2
|
let fullsize = wosize + 2
|
||||||
if int(padsize) > 0:
|
if int(padsize) > 0:
|
||||||
if randomBytes(toa(buffer, PlainAckMessageEIP8Length,
|
if randomBytes(toa(buffer, PlainAckMessageEIP8Length,
|
||||||
int(padsize))) != int(padsize):
|
int(padsize))) != int(padsize):
|
||||||
return(RandomError)
|
return err(RandomError)
|
||||||
copyMem(addr buffer[0], addr payload[0], len(payload))
|
copyMem(addr buffer[0], addr payload[0], len(payload))
|
||||||
if encrypt:
|
if encrypt:
|
||||||
if len(output) < fullsize:
|
if len(output) < fullsize:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
output[0..<2] = uint16(wosize).toBytesBE()
|
output[0..<2] = uint16(wosize).toBytesBE()
|
||||||
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||||
toa(output, 2, wosize), h.remoteHPubkey,
|
toa(output, 2, wosize), h.remoteHPubkey,
|
||||||
toa(output, 0, 2)) != EciesStatus.Success:
|
toa(output, 0, 2)).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
outlen = fullsize
|
outlen = fullsize
|
||||||
else:
|
else:
|
||||||
let plainsize = len(payload) + int(padsize)
|
let plainsize = len(payload) + int(padsize)
|
||||||
if len(output) < plainsize:
|
if len(output) < plainsize:
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
copyMem(addr output[0], addr buffer[0], plainsize)
|
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||||
outlen = plainsize
|
outlen = plainsize
|
||||||
result = Success
|
|
||||||
|
ok()
|
||||||
|
|
||||||
template authSize*(h: Handshake, encrypt: bool = true): int =
|
template authSize*(h: Handshake, encrypt: bool = true): int =
|
||||||
## Get number of bytes needed to store AuthMessage.
|
## 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,
|
proc authMessage*(h: var Handshake, pubkey: PublicKey,
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
outlen: var int, flag: int = 0,
|
outlen: var int, flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus {.inline.} =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create new AuthMessage for specified `pubkey` and store it inside
|
## Create new AuthMessage for specified `pubkey` and store it inside
|
||||||
## of `output`, size of generated AuthMessage will stored in `outlen`.
|
## of `output`, size of generated AuthMessage will stored in `outlen`.
|
||||||
if EIP8 in h.flags:
|
if EIP8 in h.flags:
|
||||||
result = authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
|
authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
|
||||||
else:
|
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],
|
proc ackMessage*(h: var Handshake, output: var openarray[byte],
|
||||||
outlen: var int, flag: int = 0,
|
outlen: var int, flag: byte = 0,
|
||||||
encrypt: bool = true): AuthStatus =
|
encrypt: bool = true): AuthResult[void] =
|
||||||
## Create new AckMessage and store it inside of `output`, size of generated
|
## Create new AckMessage and store it inside of `output`, size of generated
|
||||||
## AckMessage will stored in `outlen`.
|
## AckMessage will stored in `outlen`.
|
||||||
if EIP8 in h.flags:
|
if EIP8 in h.flags:
|
||||||
result = ackMessageEIP8(h, output, outlen, flag, encrypt)
|
ackMessageEIP8(h, output, outlen, flag, encrypt)
|
||||||
else:
|
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.
|
## Decodes V4 AuthMessage.
|
||||||
var
|
var
|
||||||
buffer: array[PlainAuthMessageV4Length, byte]
|
buffer: array[PlainAuthMessageV4Length, byte]
|
||||||
|
|
||||||
doAssert(Responder in h.flags)
|
doAssert(Responder in h.flags)
|
||||||
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
|
if eciesDecrypt(m, buffer, h.host.seckey).isErr:
|
||||||
return(EciesError)
|
return err(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
|
|
||||||
|
|
||||||
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.
|
## Decodes EIP-8 AuthMessage.
|
||||||
var
|
var
|
||||||
nonce: Nonce
|
nonce: Nonce
|
||||||
|
@ -351,155 +368,138 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
|
||||||
let size = uint16.fromBytesBE(m)
|
let size = uint16.fromBytesBE(m)
|
||||||
h.expectedLength = int(size) + 2
|
h.expectedLength = int(size) + 2
|
||||||
if h.expectedLength > len(m):
|
if h.expectedLength > len(m):
|
||||||
return(IncompleteError)
|
return err(IncompleteError)
|
||||||
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
||||||
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
||||||
toa(m, 0, 2)) != EciesStatus.Success:
|
toa(m, 0, 2)).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
try:
|
try:
|
||||||
var reader = rlpFromBytes(buffer.toRange())
|
var reader = rlpFromBytes(buffer.toRange())
|
||||||
if not reader.isList() or reader.listLen() < 4:
|
if not reader.isList() or reader.listLen() < 4:
|
||||||
return(InvalidAuth)
|
return err(InvalidAuth)
|
||||||
if reader.listElem(0).blobLen != RawSignatureSize:
|
if reader.listElem(0).blobLen != RawSignatureSize:
|
||||||
return(InvalidAuth)
|
return err(InvalidAuth)
|
||||||
if reader.listElem(1).blobLen != RawPublicKeySize:
|
if reader.listElem(1).blobLen != RawPublicKeySize:
|
||||||
return(InvalidAuth)
|
return err(InvalidAuth)
|
||||||
if reader.listElem(2).blobLen != KeyLength:
|
if reader.listElem(2).blobLen != KeyLength:
|
||||||
return(InvalidAuth)
|
return err(InvalidAuth)
|
||||||
if reader.listElem(3).blobLen != 1:
|
if reader.listElem(3).blobLen != 1:
|
||||||
return(InvalidAuth)
|
return err(InvalidAuth)
|
||||||
var signatureBr = reader.listElem(0).toBytes()
|
var signatureBr = reader.listElem(0).toBytes()
|
||||||
var pubkeyBr = reader.listElem(1).toBytes()
|
var pubkeyBr = reader.listElem(1).toBytes()
|
||||||
var nonceBr = reader.listElem(2).toBytes()
|
var nonceBr = reader.listElem(2).toBytes()
|
||||||
var versionBr = reader.listElem(3).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.
|
## Decodes EIP-8 AckMessage.
|
||||||
let size = uint16.fromBytesBE(m)
|
let size = uint16.fromBytesBE(m)
|
||||||
|
|
||||||
h.expectedLength = 2 + int(size)
|
h.expectedLength = 2 + int(size)
|
||||||
if h.expectedLength > len(m):
|
if h.expectedLength > len(m):
|
||||||
return(IncompleteError)
|
return err(IncompleteError)
|
||||||
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
||||||
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
||||||
toa(m, 0, 2)) != EciesStatus.Success:
|
toa(m, 0, 2)).isErr:
|
||||||
return(EciesError)
|
return err(EciesError)
|
||||||
try:
|
try:
|
||||||
var reader = rlpFromBytes(buffer.toRange())
|
var reader = rlpFromBytes(buffer.toRange())
|
||||||
if not reader.isList() or reader.listLen() < 3:
|
if not reader.isList() or reader.listLen() < 3:
|
||||||
return(InvalidAck)
|
return err(InvalidAck)
|
||||||
if reader.listElem(0).blobLen != RawPublicKeySize:
|
if reader.listElem(0).blobLen != RawPublicKeySize:
|
||||||
return(InvalidAck)
|
return err(InvalidAck)
|
||||||
if reader.listElem(1).blobLen != KeyLength:
|
if reader.listElem(1).blobLen != KeyLength:
|
||||||
return(InvalidAck)
|
return err(InvalidAck)
|
||||||
if reader.listElem(2).blobLen != 1:
|
if reader.listElem(2).blobLen != 1:
|
||||||
return(InvalidAck)
|
return err(InvalidAck)
|
||||||
let pubkeyBr = reader.listElem(0).toBytes()
|
let pubkeyBr = reader.listElem(0).toBytes()
|
||||||
let nonceBr = reader.listElem(1).toBytes()
|
let nonceBr = reader.listElem(1).toBytes()
|
||||||
let versionBr = reader.listElem(2).toBytes()
|
let versionBr = reader.listElem(2).toBytes()
|
||||||
let remoteEPubkey = PublicKey.fromRaw(pubkeyBr.toOpenArray())
|
h.remoteEPubkey =
|
||||||
if remoteEPubkey.isErr:
|
? PublicKey.fromRaw(pubkeyBr.toOpenArray()).mapErrTo(InvalidPubKey)
|
||||||
return(InvalidPubKey)
|
|
||||||
h.remoteEPubkey = remoteEPubkey[]
|
|
||||||
copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength)
|
copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength)
|
||||||
h.version = cast[ptr byte](versionBr.baseAddr)[]
|
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.
|
## Decodes V4 AckMessage.
|
||||||
var
|
var
|
||||||
buffer: array[PlainAckMessageV4Length, byte]
|
buffer: array[PlainAckMessageV4Length, byte]
|
||||||
doAssert(Initiator in h.flags)
|
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`.
|
## Decodes AuthMessage from `input`.
|
||||||
if len(input) < AuthMessageV4Length:
|
if len(input) < AuthMessageV4Length:
|
||||||
result = IncompleteError
|
return err(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)
|
|
||||||
|
|
||||||
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`.
|
## Decodes AckMessage from `input`.
|
||||||
if len(input) < AckMessageV4Length:
|
if len(input) < AckMessageV4Length:
|
||||||
return(IncompleteError)
|
return err(IncompleteError)
|
||||||
elif len(input) == AckMessageV4Length:
|
if len(input) == AckMessageV4Length:
|
||||||
var res = h.decodeAckMessageV4(input)
|
let res = h.decodeAckMessageV4(input)
|
||||||
if res != Success:
|
if res.isOk(): return res
|
||||||
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)
|
|
||||||
|
|
||||||
proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
let res = h.decodeAckMessageEip8(input)
|
||||||
ackmsg: openarray[byte],
|
if res.isOk(): h.flags.incl(EIP8)
|
||||||
secret: var ConnectionSecret): AuthStatus =
|
res
|
||||||
|
|
||||||
|
proc getSecrets*(
|
||||||
|
h: Handshake, authmsg: openarray[byte],
|
||||||
|
ackmsg: openarray[byte]): AuthResult[ConnectionSecret] =
|
||||||
## Derive secrets from handshake `h` using encrypted AuthMessage `authmsg` and
|
## Derive secrets from handshake `h` using encrypted AuthMessage `authmsg` and
|
||||||
## encrypted AckMessage `ackmsg`.
|
## encrypted AckMessage `ackmsg`.
|
||||||
var
|
var
|
||||||
ctx0: keccak256
|
ctx0: keccak256
|
||||||
ctx1: keccak256
|
ctx1: keccak256
|
||||||
mac1: MDigest[256]
|
mac1: MDigest[256]
|
||||||
xornonce: Nonce
|
secret: ConnectionSecret
|
||||||
|
|
||||||
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
|
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
|
||||||
var shsec = ecdhRaw(h.ephemeral.seckey, h.remoteEPubkey)
|
var shsec = ? ecdhRaw(h.ephemeral.seckey, h.remoteEPubkey).mapErrTo(EcdhError)
|
||||||
if shsec.isErr:
|
|
||||||
return(EcdhError)
|
|
||||||
|
|
||||||
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
|
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
|
||||||
ctx0.init()
|
ctx0.init()
|
||||||
|
@ -508,36 +508,36 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
||||||
ctx1.update(h.initiatorNonce)
|
ctx1.update(h.initiatorNonce)
|
||||||
mac1 = ctx1.finish()
|
mac1 = ctx1.finish()
|
||||||
ctx1.clear()
|
ctx1.clear()
|
||||||
ctx0.update(shsec[].data)
|
ctx0.update(shsec.data)
|
||||||
ctx0.update(mac1.data)
|
ctx0.update(mac1.data)
|
||||||
mac1 = ctx0.finish()
|
mac1 = ctx0.finish()
|
||||||
|
|
||||||
# aes-secret = keccak(ecdhe-secret || shared-secret)
|
# aes-secret = keccak(ecdhe-secret || shared-secret)
|
||||||
ctx0.init()
|
ctx0.init()
|
||||||
ctx0.update(shsec[].data)
|
ctx0.update(shsec.data)
|
||||||
ctx0.update(mac1.data)
|
ctx0.update(mac1.data)
|
||||||
mac1 = ctx0.finish()
|
mac1 = ctx0.finish()
|
||||||
|
|
||||||
# mac-secret = keccak(ecdhe-secret || aes-secret)
|
# mac-secret = keccak(ecdhe-secret || aes-secret)
|
||||||
ctx0.init()
|
ctx0.init()
|
||||||
ctx0.update(shsec[].data)
|
ctx0.update(shsec.data)
|
||||||
ctx0.update(mac1.data)
|
ctx0.update(mac1.data)
|
||||||
secret.aesKey = mac1.data
|
secret.aesKey = mac1.data
|
||||||
mac1 = ctx0.finish()
|
mac1 = ctx0.finish()
|
||||||
secret.macKey = mac1.data
|
secret.macKey = mac1.data
|
||||||
|
|
||||||
shsec[].clear()
|
burnMem(shsec)
|
||||||
|
|
||||||
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)
|
# 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.init()
|
||||||
ctx0.update(xornonce)
|
ctx0.update(xornonce)
|
||||||
ctx0.update(authmsg)
|
ctx0.update(authmsg)
|
||||||
|
|
||||||
# ingress-mac = keccak256(mac-secret ^ initiator-nonce || auth-recvd-ack)
|
# ingress-mac = keccak256(mac-secret ^ initiator-nonce || auth-recvd-ack)
|
||||||
xornonce = secret.macKey
|
xornonce = secret.macKey xor h.initiatorNonce
|
||||||
xornonce.sxor(h.initiatorNonce)
|
|
||||||
ctx1.init()
|
ctx1.init()
|
||||||
ctx1.update(xornonce)
|
ctx1.update(xornonce)
|
||||||
ctx1.update(ackmsg)
|
ctx1.update(ackmsg)
|
||||||
|
@ -552,4 +552,5 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
||||||
|
|
||||||
ctx0.clear()
|
ctx0.clear()
|
||||||
ctx1.clear()
|
ctx1.clear()
|
||||||
result = Success
|
|
||||||
|
ok(secret)
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
import
|
import
|
||||||
times,
|
times,
|
||||||
chronos, stint, nimcrypto, chronicles,
|
chronos, stint, nimcrypto, chronicles,
|
||||||
eth/common/eth_types_json_serialization, eth/[keys, rlp],
|
eth/[keys, rlp],
|
||||||
kademlia, enode
|
kademlia, enode,
|
||||||
|
stew/result
|
||||||
|
|
||||||
export
|
export
|
||||||
Node
|
Node, result
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "discovery"
|
topics = "discovery"
|
||||||
|
@ -45,7 +46,8 @@ type
|
||||||
|
|
||||||
DiscProtocolError* = object of CatchableError
|
DiscProtocolError* = object of CatchableError
|
||||||
|
|
||||||
const MaxDgramSize = 1280
|
DiscResult*[T] = Result[T, cstring]
|
||||||
|
|
||||||
const MinListLen: array[CommandId, int] = [4, 3, 2, 2]
|
const MinListLen: array[CommandId, int] = [4, 3, 2, 2]
|
||||||
|
|
||||||
proc append*(w: var RlpWriter, a: IpAddress) =
|
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)
|
let msgHash = keccak256.digest(signature & encodedData)
|
||||||
result = @(msgHash.data) & 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:
|
if msg.len > HEAD_SIZE:
|
||||||
msgHash.data[0 .. ^1] = msg.toOpenArray(0, msgHash.data.high)
|
var ret: MDigest[256]
|
||||||
result = msgHash == keccak256.digest(msg.toOpenArray(MAC_SIZE, msg.high))
|
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 =
|
proc recoverMsgPublicKey(msg: openArray[byte]): DiscResult[PublicKey] =
|
||||||
if msg.len > HEAD_SIZE:
|
if msg.len <= HEAD_SIZE:
|
||||||
let sig = Signature.fromRaw(msg.toOpenArray(MAC_SIZE, HEAD_SIZE))
|
return err("disc: can't get public key")
|
||||||
if sig.isOk():
|
let sig = ? Signature.fromRaw(msg.toOpenArray(MAC_SIZE, HEAD_SIZE))
|
||||||
let pubkey = recover(sig[], msg.toOpenArray(HEAD_SIZE, msg.high))
|
recover(sig, msg.toOpenArray(HEAD_SIZE, msg.high))
|
||||||
if pubkey.isOk():
|
|
||||||
pk = pubkey[]
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc unpack(msg: Bytes): tuple[cmdId: CommandId, payload: Bytes] =
|
proc unpack(msg: Bytes): tuple[cmdId: CommandId, payload: Bytes] =
|
||||||
# Check against possible RangeError
|
# Check against possible RangeError
|
||||||
|
@ -231,17 +236,17 @@ proc expirationValid(cmdId: CommandId, rlpEncodedPayload: seq[byte]):
|
||||||
proc receive*(d: DiscoveryProtocol, a: Address, msg: Bytes) {.gcsafe.} =
|
proc receive*(d: DiscoveryProtocol, a: Address, msg: Bytes) {.gcsafe.} =
|
||||||
## Can raise `DiscProtocolError` and all of `RlpError`
|
## Can raise `DiscProtocolError` and all of `RlpError`
|
||||||
# Note: export only needed for testing
|
# Note: export only needed for testing
|
||||||
var msgHash: MDigest[256]
|
let msgHash = validateMsgHash(msg)
|
||||||
if validateMsgHash(msg, msgHash):
|
if msgHash.isOk():
|
||||||
var remotePubkey: PublicKey
|
let remotePubkey = recoverMsgPublicKey(msg)
|
||||||
if recoverMsgPublicKey(msg, remotePubkey):
|
if remotePubkey.isOk:
|
||||||
let (cmdId, payload) = unpack(msg)
|
let (cmdId, payload) = unpack(msg)
|
||||||
|
|
||||||
if expirationValid(cmdId, payload):
|
if expirationValid(cmdId, payload):
|
||||||
let node = newNode(remotePubkey, a)
|
let node = newNode(remotePubkey[], a)
|
||||||
case cmdId
|
case cmdId
|
||||||
of cmdPing:
|
of cmdPing:
|
||||||
d.recvPing(node, msgHash)
|
d.recvPing(node, msgHash[])
|
||||||
of cmdPong:
|
of cmdPong:
|
||||||
d.recvPong(node, payload)
|
d.recvPong(node, payload)
|
||||||
of cmdNeighbours:
|
of cmdNeighbours:
|
||||||
|
@ -251,14 +256,13 @@ proc receive*(d: DiscoveryProtocol, a: Address, msg: Bytes) {.gcsafe.} =
|
||||||
else:
|
else:
|
||||||
trace "Received msg already expired", cmdId, a
|
trace "Received msg already expired", cmdId, a
|
||||||
else:
|
else:
|
||||||
error "Wrong public key from ", a
|
notice "Wrong public key from ", a, err = remotePubkey.error
|
||||||
else:
|
else:
|
||||||
error "Wrong msg mac from ", a
|
notice "Wrong msg mac from ", a
|
||||||
|
|
||||||
proc processClient(transp: DatagramTransport,
|
proc processClient(transp: DatagramTransport,
|
||||||
raddr: TransportAddress): Future[void] {.async, gcsafe.} =
|
raddr: TransportAddress): Future[void] {.async, gcsafe.} =
|
||||||
var proto = getUserData[DiscoveryProtocol](transp)
|
var proto = getUserData[DiscoveryProtocol](transp)
|
||||||
var buf: seq[byte]
|
|
||||||
try:
|
try:
|
||||||
# TODO: Maybe here better to use `peekMessage()` to avoid allocation,
|
# TODO: Maybe here better to use `peekMessage()` to avoid allocation,
|
||||||
# but `Bytes` object is just a simple seq[byte], and `ByteRange` object
|
# but `Bytes` object is just a simple seq[byte], and `ByteRange` object
|
||||||
|
@ -309,16 +313,14 @@ when isMainModule:
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let m = hexToSeqByte"79664bff52ee17327b4a2d8f97d8fb32c9244d719e5038eb4f6b64da19ca6d271d659c3ad9ad7861a928ca85f8d8debfbe6b7ade26ad778f2ae2ba712567fcbd55bc09eb3e74a893d6b180370b266f6aaf3fe58a0ad95f7435bf3ddf1db940d20102f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2"
|
let m = hexToSeqByte"79664bff52ee17327b4a2d8f97d8fb32c9244d719e5038eb4f6b64da19ca6d271d659c3ad9ad7861a928ca85f8d8debfbe6b7ade26ad778f2ae2ba712567fcbd55bc09eb3e74a893d6b180370b266f6aaf3fe58a0ad95f7435bf3ddf1db940d20102f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2"
|
||||||
var msgHash: MDigest[256]
|
discard validateMsgHash(m).expect("valid hash")
|
||||||
doAssert(validateMsgHash(m, msgHash))
|
var remotePubkey = recoverMsgPublicKey(m).expect("valid key")
|
||||||
var remotePubkey: PublicKey
|
|
||||||
doAssert(recoverMsgPublicKey(m, remotePubkey))
|
|
||||||
|
|
||||||
let (cmdId, payload) = unpack(m)
|
let (cmdId, payload) = unpack(m)
|
||||||
doAssert(payload == hexToSeqByte"f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2")
|
doAssert(payload == hexToSeqByte"f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2")
|
||||||
doAssert(cmdId == cmdPong)
|
doAssert(cmdId == cmdPong)
|
||||||
doAssert(remotePubkey == PublicKey.fromHex(
|
doAssert(remotePubkey == PublicKey.fromHex(
|
||||||
"78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d"))[]
|
"78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d")[])
|
||||||
|
|
||||||
let privKey = PrivateKey.fromHex("a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
|
let privKey = PrivateKey.fromHex("a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
|
||||||
|
|
||||||
|
@ -332,7 +334,7 @@ when isMainModule:
|
||||||
|
|
||||||
var bootnodes = newSeq[ENode]()
|
var bootnodes = newSeq[ENode]()
|
||||||
for item in LOCAL_BOOTNODES:
|
for item in LOCAL_BOOTNODES:
|
||||||
bootnodes.add(initENode(item))
|
bootnodes.add(ENode.fromString(item)[])
|
||||||
|
|
||||||
let listenPort = Port(30310)
|
let listenPort = Port(30310)
|
||||||
var address = Address(udpPort: listenPort, tcpPort: listenPort)
|
var address = Address(udpPort: listenPort, tcpPort: listenPort)
|
||||||
|
|
|
@ -2,6 +2,8 @@ import
|
||||||
std/[tables, options], nimcrypto, stint, chronicles,
|
std/[tables, options], nimcrypto, stint, chronicles,
|
||||||
types, node, enr, hkdf, ../enode, eth/[rlp, keys]
|
types, node, enr, hkdf, ../enode, eth/[rlp, keys]
|
||||||
|
|
||||||
|
export keys
|
||||||
|
|
||||||
const
|
const
|
||||||
idNoncePrefix = "discovery-id-nonce"
|
idNoncePrefix = "discovery-id-nonce"
|
||||||
keyAgreementPrefix = "discovery v5 key agreement"
|
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:
|
if challenge.recordSeq < ln.record.seqNum:
|
||||||
resp.record = ln.record
|
resp.record = ln.record
|
||||||
|
|
||||||
let ephKey = PrivateKey.random().tryGet()
|
let ephKeys = KeyPair.random().tryGet()
|
||||||
let ephPubkey = ephKey.toPublicKey().tryGet().toRaw
|
|
||||||
|
|
||||||
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)
|
handshakeSecrets)
|
||||||
|
|
||||||
let respRlp = rlp.encode(resp)
|
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 respEnc = encryptGCM(handshakeSecrets.authRespKey, zeroNonce, respRLP, [])
|
||||||
|
|
||||||
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
|
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)
|
rlp.encode(header)
|
||||||
|
|
||||||
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =
|
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =
|
||||||
|
|
|
@ -6,6 +6,8 @@ import
|
||||||
nimcrypto, stew/base64,
|
nimcrypto, stew/base64,
|
||||||
eth/[rlp, keys], ../enode
|
eth/[rlp, keys], ../enode
|
||||||
|
|
||||||
|
export options
|
||||||
|
|
||||||
const
|
const
|
||||||
maxEnrSize = 300
|
maxEnrSize = 300
|
||||||
minRlpListLen = 4 # for signature, seqId, "id" key, id
|
minRlpListLen = 4 # for signature, seqId, "id" key, id
|
||||||
|
@ -163,13 +165,12 @@ proc get*(r: Record, key: string, T: type): T =
|
||||||
else:
|
else:
|
||||||
raise newException(KeyError, "Key not found in ENR: " & key)
|
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
|
var pubkeyField: Field
|
||||||
if r.getField("secp256k1", pubkeyField) and pubkeyField.kind == kBytes:
|
if r.getField("secp256k1", pubkeyField) and pubkeyField.kind == kBytes:
|
||||||
let pk = PublicKey.fromRaw(pubkeyField.bytes)
|
let pk = PublicKey.fromRaw(pubkeyField.bytes)
|
||||||
if pk.isOk:
|
if pk.isOk:
|
||||||
pubKey = pk[]
|
return some pk[]
|
||||||
return true
|
|
||||||
|
|
||||||
proc tryGet*(r: Record, key: string, T: type): Option[T] =
|
proc tryGet*(r: Record, key: string, T: type): Option[T] =
|
||||||
try:
|
try:
|
||||||
|
@ -197,12 +198,12 @@ proc toTypedRecord*(r: Record): Option[TypedRecord] =
|
||||||
return some(tr)
|
return some(tr)
|
||||||
|
|
||||||
proc verifySignatureV4(r: Record, sigData: openarray[byte], content: seq[byte]): bool =
|
proc verifySignatureV4(r: Record, sigData: openarray[byte], content: seq[byte]): bool =
|
||||||
var publicKey: PublicKey
|
let publicKey = r.get(PublicKey)
|
||||||
if r.get(publicKey):
|
if publicKey.isSome:
|
||||||
let sig = SignatureNR.fromRaw(sigData)
|
let sig = SignatureNR.fromRaw(sigData)
|
||||||
if sig.isOk:
|
if sig.isOk:
|
||||||
var h = keccak256.digest(content)
|
var h = keccak256.digest(content)
|
||||||
return verify(sig[], h, publicKey)
|
return verify(sig[], h, publicKey.get)
|
||||||
|
|
||||||
proc verifySignature(r: Record): bool =
|
proc verifySignature(r: Record): bool =
|
||||||
var rlp = rlpFromBytes(r.raw.toRange)
|
var rlp = rlpFromBytes(r.raw.toRange)
|
||||||
|
|
|
@ -21,10 +21,10 @@ proc newNode*(enode: ENode, r: Record): Node =
|
||||||
record: r)
|
record: r)
|
||||||
|
|
||||||
proc newNode*(uriString: string): Node =
|
proc newNode*(uriString: string): Node =
|
||||||
newNode initENode(uriString)
|
newNode ENode.fromString(uriString).tryGet()
|
||||||
|
|
||||||
proc newNode*(pk: PublicKey, address: Address): Node =
|
proc newNode*(pk: PublicKey, address: Address): Node =
|
||||||
newNode initENode(pk, address)
|
newNode ENode(pubkey: pk, address: address)
|
||||||
|
|
||||||
proc newNode*(r: Record): Node =
|
proc newNode*(r: Record): Node =
|
||||||
# TODO: Handle IPv6
|
# TODO: Handle IPv6
|
||||||
|
@ -48,7 +48,7 @@ proc newNode*(r: Record): Node =
|
||||||
warn "Could not recover public key", err = pk.error
|
warn "Could not recover public key", err = pk.error
|
||||||
return
|
return
|
||||||
|
|
||||||
result = newNode(initENode(pk[], a))
|
result = newNode(ENode(pubkey: pk[], address: a))
|
||||||
result.record = r
|
result.record = r
|
||||||
|
|
||||||
proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.toRaw)
|
proc hash*(n: Node): hashes.Hash = hash(n.node.pubkey.toRaw)
|
||||||
|
|
|
@ -480,7 +480,7 @@ proc newProtocol*(privKey: PrivateKey, db: Database,
|
||||||
let
|
let
|
||||||
a = Address(ip: externalIp.get(IPv4_any()),
|
a = Address(ip: externalIp.get(IPv4_any()),
|
||||||
tcpPort: tcpPort, udpPort: udpPort)
|
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)
|
enrRec = enr.Record.init(1, privKey, externalIp, tcpPort, udpPort)
|
||||||
node = newNode(enode, enrRec)
|
node = newNode(enode, enrRec)
|
||||||
|
|
||||||
|
|
|
@ -10,29 +10,37 @@
|
||||||
|
|
||||||
## This module implements ECIES method encryption/decryption.
|
## This module implements ECIES method encryption/decryption.
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sysrand, sha2, utils]
|
import eth/keys, nimcrypto/[rijndael, bcmode, hash, hmac, sysrand, sha2, utils]
|
||||||
|
import stew/result
|
||||||
|
|
||||||
|
export result
|
||||||
|
|
||||||
const
|
const
|
||||||
emptyMac* = array[0, byte]([])
|
emptyMac* = array[0, byte]([])
|
||||||
|
|
||||||
type
|
type
|
||||||
EciesException* = object of CatchableError
|
EciesError* = enum
|
||||||
EciesStatus* = enum
|
BufferOverrun = "ecies: output buffer size is too small"
|
||||||
Success, ## Operation was successful
|
RandomError = "ecies: could not obtain random data"
|
||||||
BufferOverrun, ## Output buffer size is too small
|
EcdhError = "ecies: ECDH shared secret could not be calculated"
|
||||||
RandomError, ## Could not obtain random data
|
WrongHeader = "ecies: header is incorrect"
|
||||||
EcdhError, ## ECDH shared secret could not be calculated
|
IncorrectKey = "ecies: recovered public key is invalid"
|
||||||
WrongHeader, ## ECIES header is incorrect
|
IncorrectTag = "ecies: tag verification failed"
|
||||||
IncorrectKey, ## Recovered public key is invalid
|
IncompleteError = "ecies: decryption needs more data"
|
||||||
IncorrectTag, ## ECIES tag verification failed
|
|
||||||
IncompleteError ## Decryption needs more data
|
|
||||||
|
|
||||||
EciesHeader* = object {.packed.}
|
EciesHeader* {.packed.} = object
|
||||||
version*: byte
|
version*: byte
|
||||||
pubkey*: array[RawPublicKeySize, byte]
|
pubkey*: array[RawPublicKeySize, byte]
|
||||||
iv*: array[aes128.sizeBlock, byte]
|
iv*: array[aes128.sizeBlock, byte]
|
||||||
data*: 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 =
|
template eciesOverheadLength*(): int =
|
||||||
## Return data overhead size for ECIES encrypted message
|
## Return data overhead size for ECIES encrypted message
|
||||||
1 + sizeof(PublicKey) + aes128.sizeBlock + sha256.sizeDigest
|
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],
|
proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
sharedmac: openarray[byte] = emptyMac): EciesStatus =
|
sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
|
||||||
## Encrypt data with ECIES method using given public key `pubkey`.
|
## Encrypt data with ECIES method using given public key `pubkey`.
|
||||||
## ``input`` - input data
|
## ``input`` - input data
|
||||||
## ``output`` - output data
|
## ``output`` - output data
|
||||||
|
@ -99,33 +107,31 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
|
||||||
cipher: CTR[aes128]
|
cipher: CTR[aes128]
|
||||||
ctx: HMAC[sha256]
|
ctx: HMAC[sha256]
|
||||||
iv: array[aes128.sizeBlock, byte]
|
iv: array[aes128.sizeBlock, byte]
|
||||||
material: array[KeyLength, byte]
|
|
||||||
|
|
||||||
if len(output) < eciesEncryptedLength(len(input)):
|
if len(output) < eciesEncryptedLength(len(input)):
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
if randomBytes(iv) != aes128.sizeBlock:
|
if randomBytes(iv) != aes128.sizeBlock:
|
||||||
return(RandomError)
|
return err(RandomError)
|
||||||
|
|
||||||
var ephemeral = KeyPair.random()
|
var
|
||||||
if ephemeral.isErr:
|
ephemeral = ? KeyPair.random().mapErrTo(RandomError)
|
||||||
return(RandomError)
|
secret = ? ecdhRaw(ephemeral.seckey, pubkey).mapErrTo(EcdhError)
|
||||||
|
material = kdf(secret.data)
|
||||||
|
|
||||||
var secret = ecdhRaw(ephemeral[].seckey, pubkey)
|
clear(secret)
|
||||||
if secret.isErr:
|
|
||||||
return(EcdhError)
|
|
||||||
|
|
||||||
material = kdf(secret[].data)
|
|
||||||
burnMem(secret)
|
|
||||||
|
|
||||||
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
|
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
|
||||||
|
|
||||||
var macKey = sha256.digest(material, ostart = KeyLength div 2)
|
var macKey = sha256.digest(material, ostart = KeyLength div 2)
|
||||||
burnMem(material)
|
burnMem(material)
|
||||||
|
|
||||||
var header = cast[ptr EciesHeader](addr output[0])
|
var header = cast[ptr EciesHeader](addr output[0])
|
||||||
header.version = 0x04
|
header.version = 0x04
|
||||||
header.pubkey = ephemeral[].pubkey.toRaw()
|
header.pubkey = ephemeral.pubkey.toRaw()
|
||||||
header.iv = iv
|
header.iv = iv
|
||||||
|
|
||||||
|
clear(ephemeral)
|
||||||
|
|
||||||
var so = eciesDataPos()
|
var so = eciesDataPos()
|
||||||
var eo = so + len(input)
|
var eo = so + len(input)
|
||||||
cipher.init(encKey, iv)
|
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)
|
copyMem(addr output[so], addr tag.data[0], sha256.sizeDigest)
|
||||||
ctx.clear()
|
ctx.clear()
|
||||||
|
|
||||||
result = Success
|
ok()
|
||||||
|
|
||||||
proc eciesDecrypt*(input: openarray[byte],
|
proc eciesDecrypt*(input: openarray[byte],
|
||||||
output: var openarray[byte],
|
output: var openarray[byte],
|
||||||
seckey: PrivateKey,
|
seckey: PrivateKey,
|
||||||
sharedmac: openarray[byte] = emptyMac): EciesStatus =
|
sharedmac: openarray[byte] = emptyMac): EciesResult[void] =
|
||||||
## Decrypt data with ECIES method using given private key `seckey`.
|
## Decrypt data with ECIES method using given private key `seckey`.
|
||||||
## ``input`` - input data
|
## ``input`` - input data
|
||||||
## ``output`` - output data
|
## ``output`` - output data
|
||||||
|
@ -165,24 +171,23 @@ proc eciesDecrypt*(input: openarray[byte],
|
||||||
ctx: HMAC[sha256]
|
ctx: HMAC[sha256]
|
||||||
|
|
||||||
if len(input) <= 0:
|
if len(input) <= 0:
|
||||||
return(IncompleteError)
|
return err(IncompleteError)
|
||||||
|
|
||||||
var header = cast[ptr EciesHeader](unsafeAddr input[0])
|
var header = cast[ptr EciesHeader](unsafeAddr input[0])
|
||||||
if header.version != 0x04:
|
if header.version != 0x04:
|
||||||
return(WrongHeader)
|
return err(WrongHeader)
|
||||||
if len(input) <= eciesOverheadLength():
|
if len(input) <= eciesOverheadLength():
|
||||||
return(IncompleteError)
|
return err(IncompleteError)
|
||||||
if len(input) - eciesOverheadLength() > len(output):
|
if len(input) - eciesOverheadLength() > len(output):
|
||||||
return(BufferOverrun)
|
return err(BufferOverrun)
|
||||||
let pubkey = PublicKey.fromRaw(header.pubkey)
|
|
||||||
if pubkey.isErr:
|
|
||||||
return(IncorrectKey)
|
|
||||||
var secret = ecdhRaw(seckey, pubkey[])
|
|
||||||
if secret.isErr:
|
|
||||||
return(EcdhError)
|
|
||||||
|
|
||||||
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)
|
burnMem(secret)
|
||||||
|
|
||||||
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
|
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
|
||||||
var macKey = sha256.digest(material, ostart = KeyLength div 2)
|
var macKey = sha256.digest(material, ostart = KeyLength div 2)
|
||||||
burnMem(material)
|
burnMem(material)
|
||||||
|
@ -198,7 +203,7 @@ proc eciesDecrypt*(input: openarray[byte],
|
||||||
|
|
||||||
if not equalMem(addr tag.data[0], unsafeAddr input[eciesMacPos(len(input))],
|
if not equalMem(addr tag.data[0], unsafeAddr input[eciesMacPos(len(input))],
|
||||||
sha256.sizeDigest):
|
sha256.sizeDigest):
|
||||||
return(IncorrectTag)
|
return err(IncorrectTag)
|
||||||
|
|
||||||
let datsize = eciesDecryptedLength(len(input))
|
let datsize = eciesDecryptedLength(len(input))
|
||||||
cipher.init(encKey, header.iv)
|
cipher.init(encKey, header.iv)
|
||||||
|
@ -206,4 +211,5 @@ proc eciesDecrypt*(input: openarray[byte],
|
||||||
cipher.decrypt(toOpenArray(input, eciesDataPos(),
|
cipher.decrypt(toOpenArray(input, eciesDataPos(),
|
||||||
eciesDataPos() + datsize - 1), output)
|
eciesDataPos() + datsize - 1), output)
|
||||||
cipher.clear()
|
cipher.clear()
|
||||||
result = Success
|
|
||||||
|
ok()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
# Ethereum P2P
|
# Ethereum P2P
|
||||||
# (c) Copyright 2018
|
# (c) Copyright 2018-2020
|
||||||
# Status Research & Development GmbH
|
# Status Research & Development GmbH
|
||||||
#
|
#
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
|
@ -8,22 +8,23 @@
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
#
|
#
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import uri, strutils, net
|
import uri, strutils, net
|
||||||
import eth/keys
|
import eth/keys
|
||||||
|
|
||||||
export keys
|
export keys
|
||||||
|
|
||||||
type
|
type
|
||||||
ENodeStatus* = enum
|
ENodeError* = enum
|
||||||
## ENode status codes
|
## ENode status codes
|
||||||
Success, ## Conversion operation succeed
|
IncorrectNodeId = "enode: incorrect public key"
|
||||||
IncorrectNodeId, ## Incorrect public key supplied
|
IncorrectScheme = "enode: incorrect URI scheme"
|
||||||
IncorrectScheme, ## Incorrect URI scheme supplied
|
IncorrectIP = "enode: incorrect IP address"
|
||||||
IncorrectIP, ## Incorrect IP address supplied
|
IncorrectPort = "enode: incorrect TCP port"
|
||||||
IncorrectPort, ## Incorrect TCP port supplied
|
IncorrectDiscPort = "enode: incorrect UDP discovery port"
|
||||||
IncorrectDiscPort, ## Incorrect UDP discovery port supplied
|
IncorrectUri = "enode: incorrect URI"
|
||||||
IncorrectUri, ## Incorrect URI supplied
|
IncompleteENode = "enode: incomplete ENODE object"
|
||||||
IncompleteENode ## Incomplete ENODE object
|
|
||||||
|
|
||||||
Address* = object
|
Address* = object
|
||||||
## Network address object
|
## Network address object
|
||||||
|
@ -36,25 +37,12 @@ type
|
||||||
pubkey*: PublicKey ## Node public key
|
pubkey*: PublicKey ## Node public key
|
||||||
address*: Address ## Node address
|
address*: Address ## Node address
|
||||||
|
|
||||||
ENodeException* = object of CatchableError
|
ENodeResult*[T] = Result[T, ENodeError]
|
||||||
|
|
||||||
proc raiseENodeError(status: ENodeStatus) =
|
proc mapErrTo[T, E](r: Result[T, E], v: static ENodeError): ENodeResult[T] =
|
||||||
if status == IncorrectIP:
|
r.mapErr(proc (e: E): ENodeError = v)
|
||||||
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 initENode*(e: string, node: var ENode): ENodeStatus =
|
proc fromString*(T: type ENode, e: string): ENodeResult[ENode] =
|
||||||
## Initialize ENode ``node`` from URI string ``uri``.
|
## Initialize ENode ``node`` from URI string ``uri``.
|
||||||
var
|
var
|
||||||
uport: int = 0
|
uport: int = 0
|
||||||
|
@ -62,83 +50,67 @@ proc initENode*(e: string, node: var ENode): ENodeStatus =
|
||||||
uri: Uri = initUri()
|
uri: Uri = initUri()
|
||||||
|
|
||||||
if len(e) == 0:
|
if len(e) == 0:
|
||||||
return IncorrectUri
|
return err(IncorrectUri)
|
||||||
|
|
||||||
parseUri(e, uri)
|
parseUri(e, uri)
|
||||||
|
|
||||||
if len(uri.scheme) == 0 or uri.scheme.toLowerAscii() != "enode":
|
if len(uri.scheme) == 0 or uri.scheme.toLowerAscii() != "enode":
|
||||||
return IncorrectScheme
|
return err(IncorrectScheme)
|
||||||
|
|
||||||
if len(uri.username) != 128:
|
if len(uri.username) != 128:
|
||||||
return IncorrectNodeId
|
return err(IncorrectNodeId)
|
||||||
|
|
||||||
for i in uri.username:
|
for i in uri.username:
|
||||||
if i notin {'A'..'F', 'a'..'f', '0'..'9'}:
|
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:
|
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:
|
if len(uri.hostname) == 0:
|
||||||
return IncorrectIP
|
return err(IncorrectIP)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if len(uri.port) == 0:
|
if len(uri.port) == 0:
|
||||||
return IncorrectPort
|
return err(IncorrectPort)
|
||||||
tport = parseInt(uri.port)
|
tport = parseInt(uri.port)
|
||||||
if tport <= 0 or tport > 65535:
|
if tport <= 0 or tport > 65535:
|
||||||
return IncorrectPort
|
return err(IncorrectPort)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return IncorrectPort
|
return err(IncorrectPort)
|
||||||
|
|
||||||
if len(uri.query) > 0:
|
if len(uri.query) > 0:
|
||||||
if not uri.query.toLowerAscii().startsWith("discport="):
|
if not uri.query.toLowerAscii().startsWith("discport="):
|
||||||
return IncorrectDiscPort
|
return err(IncorrectDiscPort)
|
||||||
try:
|
try:
|
||||||
uport = parseInt(uri.query[9..^1])
|
uport = parseInt(uri.query[9..^1])
|
||||||
if uport <= 0 or uport > 65535:
|
if uport <= 0 or uport > 65535:
|
||||||
return IncorrectDiscPort
|
return err(IncorrectDiscPort)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return IncorrectDiscPort
|
return err(IncorrectDiscPort)
|
||||||
else:
|
else:
|
||||||
uport = tport
|
uport = tport
|
||||||
|
|
||||||
let pk = PublicKey.fromHex(uri.username)
|
var ip: IpAddress
|
||||||
if pk.isErr:
|
|
||||||
return IncorrectNodeId
|
|
||||||
node.pubkey = pk[]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
node.address.ip = parseIpAddress(uri.hostname)
|
ip = parseIpAddress(uri.hostname)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
zeroMem(addr node.pubkey, KeyLength * 2)
|
return err(IncorrectIP)
|
||||||
return IncorrectIP
|
|
||||||
|
|
||||||
node.address.tcpPort = Port(tport)
|
let pubkey = ? PublicKey.fromHex(uri.username).mapErrTo(IncorrectNodeId)
|
||||||
node.address.udpPort = Port(uport)
|
|
||||||
result = Success
|
|
||||||
|
|
||||||
proc initENode*(uri: string): ENode {.inline.} =
|
ok(ENode(
|
||||||
## Returns ENode object from URI string ``uri``.
|
pubkey: pubkey,
|
||||||
let res = initENode(uri, result)
|
address: Address(
|
||||||
if res != Success:
|
ip: ip,
|
||||||
raiseENodeError(res)
|
tcpPort: Port(tport),
|
||||||
|
udpPort: Port(uport)
|
||||||
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
|
|
||||||
|
|
||||||
proc `$`*(n: ENode): string =
|
proc `$`*(n: ENode): string =
|
||||||
## Returns string representation of ENode.
|
## Returns string representation of ENode.
|
||||||
var ipaddr: string
|
var ipaddr: string
|
||||||
if not isCorrect(n):
|
|
||||||
raiseENodeError(IncompleteENode)
|
|
||||||
if n.address.ip.family == IpAddressFamily.IPv4:
|
if n.address.ip.family == IpAddressFamily.IPv4:
|
||||||
ipaddr = $(n.address.ip)
|
ipaddr = $(n.address.ip)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -55,12 +55,12 @@ proc toNodeId*(pk: PublicKey): NodeId =
|
||||||
|
|
||||||
proc newNode*(pk: PublicKey, address: Address): Node =
|
proc newNode*(pk: PublicKey, address: Address): Node =
|
||||||
result.new()
|
result.new()
|
||||||
result.node = initENode(pk, address)
|
result.node = ENode(pubkey: pk, address: address)
|
||||||
result.id = pk.toNodeId()
|
result.id = pk.toNodeId()
|
||||||
|
|
||||||
proc newNode*(uriString: string): Node =
|
proc newNode*(uriString: string): Node =
|
||||||
result.new()
|
result.new()
|
||||||
result.node = initENode(uriString)
|
result.node = ENode.fromString(uriString)[]
|
||||||
result.id = result.node.pubkey.toNodeId()
|
result.id = result.node.pubkey.toNodeId()
|
||||||
|
|
||||||
proc newNode*(enode: ENode): Node =
|
proc newNode*(enode: ENode): Node =
|
||||||
|
|
|
@ -212,7 +212,5 @@ proc newMockPeer*(userConfigurator: proc (m: MockConf)): EthereumNode =
|
||||||
return node
|
return node
|
||||||
|
|
||||||
proc rlpxConnect*(node, otherNode: EthereumNode): Future[Peer] =
|
proc rlpxConnect*(node, otherNode: EthereumNode): Future[Peer] =
|
||||||
let otherAsRemote = newNode(initENode(otherNode.keys.pubKey,
|
let otherAsRemote = newNode(otherNode.toENode())
|
||||||
otherNode.address))
|
|
||||||
return rlpx.rlpxConnect(node, otherAsRemote)
|
return rlpx.rlpxConnect(node, otherAsRemote)
|
||||||
|
|
||||||
|
|
|
@ -168,3 +168,5 @@ type
|
||||||
|
|
||||||
proc `$`*(peer: Peer): string = $peer.remote
|
proc `$`*(peer: Peer): string = $peer.remote
|
||||||
|
|
||||||
|
proc toENode*(v: EthereumNode): ENode =
|
||||||
|
ENode(pubkey: v.keys.pubkey, address: v.address)
|
||||||
|
|
|
@ -390,7 +390,7 @@ proc recvMsg*(peer: Peer): Future[tuple[msgId: int, msgData: Rlp]] {.async.} =
|
||||||
|
|
||||||
var msgSize: int
|
var msgSize: int
|
||||||
if decryptHeaderAndGetMsgSize(peer.secretsState,
|
if decryptHeaderAndGetMsgSize(peer.secretsState,
|
||||||
headerBytes, msgSize) != RlpxStatus.Success:
|
headerBytes, msgSize).isErr():
|
||||||
await peer.disconnectAndRaise(BreachOfProtocol,
|
await peer.disconnectAndRaise(BreachOfProtocol,
|
||||||
"Cannot decrypt RLPx frame header")
|
"Cannot decrypt RLPx frame header")
|
||||||
|
|
||||||
|
@ -414,7 +414,7 @@ proc recvMsg*(peer: Peer): Future[tuple[msgId: int, msgData: Rlp]] {.async.} =
|
||||||
decryptedBytesCount = 0
|
decryptedBytesCount = 0
|
||||||
|
|
||||||
if decryptBody(peer.secretsState, encryptedBytes, msgSize,
|
if decryptBody(peer.secretsState, encryptedBytes, msgSize,
|
||||||
decryptedBytes, decryptedBytesCount) != RlpxStatus.Success:
|
decryptedBytes, decryptedBytesCount).isErr():
|
||||||
await peer.disconnectAndRaise(BreachOfProtocol,
|
await peer.disconnectAndRaise(BreachOfProtocol,
|
||||||
"Cannot decrypt RLPx frame body")
|
"Cannot decrypt RLPx frame body")
|
||||||
|
|
||||||
|
@ -929,14 +929,9 @@ template `^`(arr): auto =
|
||||||
# variable as an open array
|
# variable as an open array
|
||||||
arr.toOpenArray(0, `arr Len` - 1)
|
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],
|
proc initSecretState(hs: var Handshake, authMsg, ackMsg: openarray[byte],
|
||||||
p: Peer) =
|
p: Peer) =
|
||||||
var secrets: ConnectionSecret
|
var secrets = hs.getSecrets(authMsg, ackMsg).tryGet()
|
||||||
check hs.getSecrets(authMsg, ackMsg, secrets)
|
|
||||||
initSecretState(secrets, p.secretsState)
|
initSecretState(secrets, p.secretsState)
|
||||||
burnMem(secrets)
|
burnMem(secrets)
|
||||||
|
|
||||||
|
@ -975,12 +970,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
|
||||||
var ok = false
|
var ok = false
|
||||||
try:
|
try:
|
||||||
result.transport = await connect(ta)
|
result.transport = await connect(ta)
|
||||||
var handshake = newHandshake({Initiator, EIP8}, int(node.baseProtocolVersion))
|
var handshake = Handshake.tryInit(
|
||||||
handshake.host = node.keys
|
node.keys, {Initiator, EIP8}, node.baseProtocolVersion).tryGet()
|
||||||
|
|
||||||
var authMsg: array[AuthMessageMaxEIP8, byte]
|
var authMsg: array[AuthMessageMaxEIP8, byte]
|
||||||
var authMsgLen = 0
|
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)
|
var res = await result.transport.write(addr authMsg[0], authMsgLen)
|
||||||
if res != authMsgLen:
|
if res != authMsgLen:
|
||||||
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
||||||
|
@ -993,12 +988,12 @@ proc rlpxConnect*(node: EthereumNode, remote: Node): Future[Peer] {.async.} =
|
||||||
await result.transport.readExactly(addr ackMsg[0], len(ackMsg))
|
await result.transport.readExactly(addr ackMsg[0], len(ackMsg))
|
||||||
|
|
||||||
var ret = handshake.decodeAckMessage(ackMsg)
|
var ret = handshake.decodeAckMessage(ackMsg)
|
||||||
if ret == AuthStatus.IncompleteError:
|
if ret.isErr and ret.error == AuthError.IncompleteError:
|
||||||
ackMsg.setLen(handshake.expectedLength)
|
ackMsg.setLen(handshake.expectedLength)
|
||||||
await result.transport.readExactly(addr ackMsg[initialSize],
|
await result.transport.readExactly(addr ackMsg[initialSize],
|
||||||
len(ackMsg) - initialSize)
|
len(ackMsg) - initialSize)
|
||||||
ret = handshake.decodeAckMessage(ackMsg)
|
ret = handshake.decodeAckMessage(ackMsg)
|
||||||
check ret
|
ret.tryGet() # for the raise!
|
||||||
|
|
||||||
node.checkSnappySupport(handshake, result)
|
node.checkSnappySupport(handshake, result)
|
||||||
initSecretState(handshake, ^authMsg, ackMsg, result)
|
initSecretState(handshake, ^authMsg, ackMsg, result)
|
||||||
|
@ -1062,8 +1057,7 @@ proc rlpxAccept*(node: EthereumNode,
|
||||||
result.transport = transport
|
result.transport = transport
|
||||||
result.network = node
|
result.network = node
|
||||||
|
|
||||||
var handshake = newHandshake({auth.Responder})
|
var handshake = HandShake.tryInit(node.keys, {auth.Responder}).tryGet
|
||||||
handshake.host = node.keys
|
|
||||||
|
|
||||||
var ok = false
|
var ok = false
|
||||||
try:
|
try:
|
||||||
|
@ -1073,19 +1067,20 @@ proc rlpxAccept*(node: EthereumNode,
|
||||||
authMsg.setLen(initialSize)
|
authMsg.setLen(initialSize)
|
||||||
await transport.readExactly(addr authMsg[0], len(authMsg))
|
await transport.readExactly(addr authMsg[0], len(authMsg))
|
||||||
var ret = handshake.decodeAuthMessage(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)
|
authMsg.setLen(handshake.expectedLength)
|
||||||
await transport.readExactly(addr authMsg[initialSize],
|
await transport.readExactly(addr authMsg[initialSize],
|
||||||
len(authMsg) - initialSize)
|
len(authMsg) - initialSize)
|
||||||
ret = handshake.decodeAuthMessage(authMsg)
|
ret = handshake.decodeAuthMessage(authMsg)
|
||||||
check ret
|
ret.tryGet() # for the raise!
|
||||||
|
|
||||||
node.checkSnappySupport(handshake, result)
|
node.checkSnappySupport(handshake, result)
|
||||||
handshake.version = uint8(result.baseProtocolVersion)
|
handshake.version = uint8(result.baseProtocolVersion)
|
||||||
|
|
||||||
var ackMsg: array[AckMessageMaxEIP8, byte]
|
var ackMsg: array[AckMessageMaxEIP8, byte]
|
||||||
var ackMsgLen: int
|
var ackMsgLen: int
|
||||||
check handshake.ackMessage(ackMsg, ackMsgLen)
|
handshake.ackMessage(ackMsg, ackMsgLen).tryGet()
|
||||||
var res = await transport.write(addr ackMsg[0], ackMsgLen)
|
var res = await transport.write(addr ackMsg[0], ackMsgLen)
|
||||||
if res != ackMsgLen:
|
if res != ackMsgLen:
|
||||||
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
raisePeerDisconnected("Unexpected disconnect while authenticating",
|
||||||
|
@ -1117,7 +1112,8 @@ proc rlpxAccept*(node: EthereumNode,
|
||||||
let remote = transport.remoteAddress()
|
let remote = transport.remoteAddress()
|
||||||
let address = Address(ip: remote.address, tcpPort: remote.port,
|
let address = Address(ip: remote.address, tcpPort: remote.port,
|
||||||
udpPort: 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,
|
trace "devp2p handshake completed", peer = result.remote,
|
||||||
clientId = response.clientId
|
clientId = response.clientId
|
||||||
|
|
|
@ -49,7 +49,7 @@ p2pProtocol Hive(version = hiveVersion,
|
||||||
debug "Hive peer connected"
|
debug "Hive peer connected"
|
||||||
|
|
||||||
proc initProtocolState*(network: BzzNetwork, node: EthereumNode) {.gcsafe.} =
|
proc initProtocolState*(network: BzzNetwork, node: EthereumNode) {.gcsafe.} =
|
||||||
network.thisENode = initENode(node.keys.pubkey, node.address)
|
network.thisENode = node.toENode()
|
||||||
|
|
||||||
p2pProtocol Bzz(version = bzzVersion,
|
p2pProtocol Bzz(version = bzzVersion,
|
||||||
rlpxName = "bzz",
|
rlpxName = "bzz",
|
||||||
|
|
|
@ -266,8 +266,8 @@ p2pProtocol les(version = lesVersion,
|
||||||
if signature.isNone:
|
if signature.isNone:
|
||||||
error "missing announce signature"
|
error "missing announce signature"
|
||||||
return
|
return
|
||||||
let sigMsg = rlp.encodeList(headHash, headNumber, headTotalDifficulty)
|
|
||||||
let sig = Signature.fromRaw(signature.get).tryGet()
|
let sig = Signature.fromRaw(signature.get).tryGet()
|
||||||
|
let sigMsg = rlp.encodeList(headHash, headNumber, headTotalDifficulty)
|
||||||
let signerKey = recover(sig, sigMsg).tryGet()
|
let signerKey = recover(sig, sigMsg).tryGet()
|
||||||
if signerKey.toNodeId != peer.remote.id:
|
if signerKey.toNodeId != peer.remote.id:
|
||||||
error "invalid announce signature"
|
error "invalid announce signature"
|
||||||
|
|
|
@ -307,8 +307,8 @@ proc encode*(self: Payload): Option[Bytes] =
|
||||||
if self.dst.isSome(): # Asymmetric key present - encryption requested
|
if self.dst.isSome(): # Asymmetric key present - encryption requested
|
||||||
var res = newSeq[byte](eciesEncryptedLength(plain.len))
|
var res = newSeq[byte](eciesEncryptedLength(plain.len))
|
||||||
let err = eciesEncrypt(plain, res, self.dst.get())
|
let err = eciesEncrypt(plain, res, self.dst.get())
|
||||||
if err != EciesStatus.Success:
|
if err.isErr:
|
||||||
notice "Encryption failed", err
|
notice "Encryption failed", err = err.error
|
||||||
return
|
return
|
||||||
return some(res)
|
return some(res)
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ proc decode*(data: openarray[byte], dst = none[PrivateKey](),
|
||||||
return
|
return
|
||||||
|
|
||||||
plain.setLen(eciesDecryptedLength(data.len))
|
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
|
debug "Couldn't decrypt using asymmetric key", len = data.len
|
||||||
return
|
return
|
||||||
elif symKey.isSome():
|
elif symKey.isSome():
|
||||||
|
|
|
@ -10,9 +10,13 @@
|
||||||
|
|
||||||
## This module implements RLPx cryptography
|
## 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
|
from auth import ConnectionSecret
|
||||||
|
|
||||||
|
export result
|
||||||
|
|
||||||
const
|
const
|
||||||
RlpHeaderLength* = 16
|
RlpHeaderLength* = 16
|
||||||
RlpMacLength* = 16
|
RlpMacLength* = 16
|
||||||
|
@ -27,15 +31,16 @@ type
|
||||||
emac*: keccak256
|
emac*: keccak256
|
||||||
imac*: keccak256
|
imac*: keccak256
|
||||||
|
|
||||||
RlpxStatus* = enum
|
RlpxError* = enum
|
||||||
Success, ## Operation was successful
|
IncorrectMac = "rlpx: MAC verification failed"
|
||||||
IncorrectMac, ## MAC verification failed
|
BufferOverrun = "rlpx: buffer overrun"
|
||||||
BufferOverrun, ## Buffer overrun error
|
IncompleteError = "rlpx: data incomplete"
|
||||||
IncompleteError, ## Data incomplete error
|
IncorrectArgs = "rlpx: incorrect arguments"
|
||||||
IncorrectArgs ## Incorrect arguments
|
|
||||||
|
|
||||||
RlpxHeader* = array[16, byte]
|
RlpxHeader* = array[16, byte]
|
||||||
|
|
||||||
|
RlpxResult*[T] = Result[T, RlpxError]
|
||||||
|
|
||||||
proc roundup16*(x: int): int {.inline.} =
|
proc roundup16*(x: int): int {.inline.} =
|
||||||
## Procedure aligns `x` to
|
## Procedure aligns `x` to
|
||||||
let rem = x and 15
|
let rem = x and 15
|
||||||
|
@ -76,7 +81,7 @@ template decryptedLength*(size: int): int =
|
||||||
|
|
||||||
proc encrypt*(c: var SecretState, header: openarray[byte],
|
proc encrypt*(c: var SecretState, header: openarray[byte],
|
||||||
frame: 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
|
## Encrypts `header` and `frame` using SecretState `c` context and store
|
||||||
## result into `output`.
|
## result into `output`.
|
||||||
##
|
##
|
||||||
|
@ -92,7 +97,7 @@ proc encrypt*(c: var SecretState, header: openarray[byte],
|
||||||
let framePos = RlpHeaderLength + RlpMacLength
|
let framePos = RlpHeaderLength + RlpMacLength
|
||||||
let frameMacPos = RlpHeaderLength * 2 + frameLength
|
let frameMacPos = RlpHeaderLength * 2 + frameLength
|
||||||
if len(header) != RlpHeaderLength or len(frame) == 0 or length != len(output):
|
if len(header) != RlpHeaderLength or len(frame) == 0 or length != len(output):
|
||||||
return IncorrectArgs
|
return err(IncorrectArgs)
|
||||||
# header_ciphertext = self.aes_enc.update(header)
|
# header_ciphertext = self.aes_enc.update(header)
|
||||||
c.aesenc.encrypt(header, toa(output, 0, RlpHeaderLength))
|
c.aesenc.encrypt(header, toa(output, 0, RlpHeaderLength))
|
||||||
# mac_secret = self.egress_mac.digest()[:HEADER_LEN]
|
# 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
|
# return header_ciphertext + header_mac + frame_ciphertext + frame_mac
|
||||||
copyMem(addr output[headerMacPos], addr headerMac.data[0], RlpHeaderLength)
|
copyMem(addr output[headerMacPos], addr headerMac.data[0], RlpHeaderLength)
|
||||||
copyMem(addr output[frameMacPos], addr frameMac.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] =
|
proc encryptMsg*(msg: openarray[byte], secrets: var SecretState): seq[byte] =
|
||||||
var header: RlpxHeader
|
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
|
# This would be safer if we use a thread-local sequ for the temporary buffer
|
||||||
result = newSeq[byte](encryptedLength(msg.len))
|
result = newSeq[byte](encryptedLength(msg.len))
|
||||||
let s = encrypt(secrets, header, msg, result)
|
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 =
|
proc getBodySize*(a: RlpxHeader): int =
|
||||||
(int(a[0]) shl 16) or (int(a[1]) shl 8) or int(a[2])
|
(int(a[0]) shl 16) or (int(a[1]) shl 8) or int(a[2])
|
||||||
|
|
||||||
proc decryptHeader*(c: var SecretState, data: openarray[byte],
|
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
|
## Decrypts header `data` using SecretState `c` context and store
|
||||||
## result into `output`.
|
## result into `output`.
|
||||||
##
|
##
|
||||||
|
@ -169,9 +174,9 @@ proc decryptHeader*(c: var SecretState, data: openarray[byte],
|
||||||
aes: array[RlpHeaderLength, byte]
|
aes: array[RlpHeaderLength, byte]
|
||||||
|
|
||||||
if len(data) != RlpHeaderLength + RlpMacLength:
|
if len(data) != RlpHeaderLength + RlpMacLength:
|
||||||
return IncompleteError
|
return err(IncompleteError)
|
||||||
if len(output) < RlpHeaderLength:
|
if len(output) < RlpHeaderLength:
|
||||||
return IncorrectArgs
|
return err(IncorrectArgs)
|
||||||
# mac_secret = self.ingress_mac.digest()[:HEADER_LEN]
|
# mac_secret = self.ingress_mac.digest()[:HEADER_LEN]
|
||||||
tmpmac = c.imac
|
tmpmac = c.imac
|
||||||
var macsec = tmpmac.finish()
|
var macsec = tmpmac.finish()
|
||||||
|
@ -188,22 +193,22 @@ proc decryptHeader*(c: var SecretState, data: openarray[byte],
|
||||||
let headerMacPos = RlpHeaderLength
|
let headerMacPos = RlpHeaderLength
|
||||||
if not equalMem(cast[pointer](unsafeAddr data[headerMacPos]),
|
if not equalMem(cast[pointer](unsafeAddr data[headerMacPos]),
|
||||||
cast[pointer](addr expectMac.data[0]), RlpMacLength):
|
cast[pointer](addr expectMac.data[0]), RlpMacLength):
|
||||||
result = IncorrectMac
|
result = err(IncorrectMac)
|
||||||
else:
|
else:
|
||||||
# return self.aes_dec.update(header_ciphertext)
|
# return self.aes_dec.update(header_ciphertext)
|
||||||
c.aesdec.decrypt(toa(data, 0, RlpHeaderLength), output)
|
c.aesdec.decrypt(toa(data, 0, RlpHeaderLength), output)
|
||||||
result = Success
|
result = ok()
|
||||||
|
|
||||||
proc decryptHeaderAndGetMsgSize*(c: var SecretState,
|
proc decryptHeaderAndGetMsgSize*(c: var SecretState,
|
||||||
encryptedHeader: openarray[byte],
|
encryptedHeader: openarray[byte],
|
||||||
outSize: var int): RlpxStatus =
|
outSize: var int): RlpxResult[void] =
|
||||||
var decryptedHeader: RlpxHeader
|
var decryptedHeader: RlpxHeader
|
||||||
result = decryptHeader(c, encryptedHeader, decryptedHeader)
|
result = decryptHeader(c, encryptedHeader, decryptedHeader)
|
||||||
if result == Success:
|
if result.isOk():
|
||||||
outSize = decryptedHeader.getBodySize
|
outSize = decryptedHeader.getBodySize
|
||||||
|
|
||||||
proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
|
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
|
## Decrypts body `data` using SecretState `c` context and store
|
||||||
## result into `output`.
|
## result into `output`.
|
||||||
##
|
##
|
||||||
|
@ -217,9 +222,9 @@ proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
|
||||||
outlen = 0
|
outlen = 0
|
||||||
let rsize = roundup16(bodysize)
|
let rsize = roundup16(bodysize)
|
||||||
if len(data) < rsize + RlpMacLength:
|
if len(data) < rsize + RlpMacLength:
|
||||||
return IncompleteError
|
return err(IncompleteError)
|
||||||
if len(output) < rsize:
|
if len(output) < rsize:
|
||||||
return IncorrectArgs
|
return err(IncorrectArgs)
|
||||||
# self.ingress_mac.update(frame_ciphertext)
|
# self.ingress_mac.update(frame_ciphertext)
|
||||||
c.imac.update(toa(data, 0, rsize))
|
c.imac.update(toa(data, 0, rsize))
|
||||||
tmpmac = c.imac
|
tmpmac = c.imac
|
||||||
|
@ -235,8 +240,8 @@ proc decryptBody*(c: var SecretState, data: openarray[byte], bodysize: int,
|
||||||
let bodyMacPos = rsize
|
let bodyMacPos = rsize
|
||||||
if not equalMem(cast[pointer](unsafeAddr data[bodyMacPos]),
|
if not equalMem(cast[pointer](unsafeAddr data[bodyMacPos]),
|
||||||
cast[pointer](addr expectMac.data[0]), RlpMacLength):
|
cast[pointer](addr expectMac.data[0]), RlpMacLength):
|
||||||
result = IncorrectMac
|
result = err(IncorrectMac)
|
||||||
else:
|
else:
|
||||||
c.aesdec.decrypt(toa(data, 0, rsize), output)
|
c.aesdec.decrypt(toa(data, 0, rsize), output)
|
||||||
outlen = bodysize
|
outlen = bodysize
|
||||||
result = Success
|
result = ok()
|
||||||
|
|
|
@ -31,4 +31,4 @@ test:
|
||||||
# These errors are also catched in `processClient` in discovery.nim
|
# 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?
|
# TODO: move them a layer down in discovery so we can do a cleaner test there?
|
||||||
except RlpError, DiscProtocolError as e:
|
except RlpError, DiscProtocolError as e:
|
||||||
debug "Receive failed", err = e.msg
|
debug "Receive failed", err = e.msg
|
||||||
|
|
|
@ -16,8 +16,7 @@ init:
|
||||||
node2 = setupTestNode(eth, Whisper)
|
node2 = setupTestNode(eth, Whisper)
|
||||||
|
|
||||||
node2.startListening()
|
node2.startListening()
|
||||||
peer = waitFor node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
|
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
aflLoop: # This appears to have unstable results with afl-clang-fast, probably
|
aflLoop: # This appears to have unstable results with afl-clang-fast, probably
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# 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
|
# Test vectors copied from
|
||||||
# https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json
|
# https://github.com/ethereum/tests/blob/develop/KeyStoreTests/basic_tests.json
|
||||||
|
@ -83,52 +83,45 @@ var TestVectors = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
var jobject: JsonNode
|
|
||||||
|
|
||||||
suite "KeyFile test suite":
|
suite "KeyFile test suite":
|
||||||
test "KeyStoreTests/basic_tests.json test1":
|
test "KeyStoreTests/basic_tests.json test1":
|
||||||
var seckey: PrivateKey
|
|
||||||
var expectkey = PrivateKey.fromHex(TestVectors[0].getOrDefault("priv").getStr())[]
|
var expectkey = PrivateKey.fromHex(TestVectors[0].getOrDefault("priv").getStr())[]
|
||||||
check:
|
let seckey =
|
||||||
decodeKeyFileJson(TestVectors[0].getOrDefault("keyfile"),
|
decodeKeyFileJson(TestVectors[0].getOrDefault("keyfile"),
|
||||||
TestVectors[0].getOrDefault("password").getStr(),
|
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())[]
|
|
||||||
check:
|
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"),
|
decodeKeyFileJson(TestVectors[1].getOrDefault("keyfile"),
|
||||||
TestVectors[1].getOrDefault("password").getStr(),
|
TestVectors[1].getOrDefault("password").getStr())[]
|
||||||
seckey) == KeyFileStatus.Success
|
check:
|
||||||
seckey.toRaw == expectkey.toRaw
|
seckey.toRaw == expectkey.toRaw
|
||||||
test "KeyStoreTests/basic_tests.json evilnonce":
|
test "KeyStoreTests/basic_tests.json evilnonce":
|
||||||
var seckey: PrivateKey
|
|
||||||
var expectkey = PrivateKey.fromHex(TestVectors[2].getOrDefault("priv").getStr())[]
|
var expectkey = PrivateKey.fromHex(TestVectors[2].getOrDefault("priv").getStr())[]
|
||||||
|
let seckey = decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
|
||||||
|
TestVectors[2].getOrDefault("password").getStr())[]
|
||||||
check:
|
check:
|
||||||
decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
|
|
||||||
TestVectors[2].getOrDefault("password").getStr(),
|
|
||||||
seckey) == KeyFileStatus.Success
|
|
||||||
seckey.toRaw == expectkey.toRaw
|
seckey.toRaw == expectkey.toRaw
|
||||||
test "KeyStoreTests/basic_tests.json evilnonce with wrong password":
|
test "KeyStoreTests/basic_tests.json evilnonce with wrong password":
|
||||||
var seckey: PrivateKey
|
let seckey =
|
||||||
check:
|
|
||||||
decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
|
decodeKeyFileJson(TestVectors[2].getOrDefault("keyfile"),
|
||||||
"wrongpassword",
|
"wrongpassword")
|
||||||
seckey) == KeyFileStatus.IncorrectMac
|
check:
|
||||||
|
seckey.error == KeyFileError.IncorrectMac
|
||||||
test "Create/Save/Load test":
|
test "Create/Save/Load test":
|
||||||
var seckey0 = PrivateKey.random()[]
|
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:
|
check:
|
||||||
createKeyFileJson(seckey0, "randompassword",
|
|
||||||
jobject) == KeyFileStatus.Success
|
|
||||||
saveKeyFile("test.keyfile", jobject) == KeyFileStatus.Success
|
|
||||||
loadKeyFile("test.keyfile", "randompassword",
|
|
||||||
seckey1) == KeyFileStatus.Success
|
|
||||||
seckey0.toRaw == seckey1.toRaw
|
seckey0.toRaw == seckey1.toRaw
|
||||||
removeFile("test.keyfile")
|
removeFile("test.keyfile")
|
||||||
test "Load non-existent pathname test":
|
test "Load non-existent pathname test":
|
||||||
var seckey: PrivateKey
|
|
||||||
check:
|
check:
|
||||||
loadKeyFile("nonexistant.keyfile", "password",
|
loadKeyFile("nonexistant.keyfile", "password").error ==
|
||||||
seckey) == KeyFileStatus.OsError
|
KeyFileError.OsError
|
||||||
|
|
|
@ -7,17 +7,15 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
import eth/keyfile/uuid, strutils, unittest
|
import eth/keyfile/uuid, unittest
|
||||||
|
|
||||||
suite "Cross-platform UUID test suite":
|
suite "Cross-platform UUID test suite":
|
||||||
test "Platform UUID check":
|
test "Platform UUID check":
|
||||||
var u: UUID
|
check uuidGenerate().isOk
|
||||||
check uuidGenerate(u) == 1
|
|
||||||
test "Conversion test":
|
test "Conversion test":
|
||||||
var u: UUID
|
let u = uuidGenerate()[]
|
||||||
check:
|
check:
|
||||||
uuidGenerate(u) == 1
|
|
||||||
len($u) == 36
|
len($u) == 36
|
||||||
$uuidFromString($u) == $u
|
$uuidFromString($u)[] == $u
|
||||||
uuidToString(u, true) == $u
|
uuidToString(u) == $u
|
||||||
uuidToString(u, false) == toUpperAscii($u)
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ import
|
||||||
|
|
||||||
var node = setupTestNode(Bzz, Hive)
|
var node = setupTestNode(Bzz, Hive)
|
||||||
|
|
||||||
var bzzENode: ENode
|
|
||||||
let nodeId = "enode://10420addaa648ffcf09c4ba9df7ce876f276f77aae015bc9346487780c9c04862dc47cec17c86be10d4fb7d93f2cae3f8e702f94cb6dea5807bfedad218a53df@127.0.0.1:30399"
|
let nodeId = "enode://10420addaa648ffcf09c4ba9df7ce876f276f77aae015bc9346487780c9c04862dc47cec17c86be10d4fb7d93f2cae3f8e702f94cb6dea5807bfedad218a53df@127.0.0.1:30399"
|
||||||
discard initENode(nodeId, bzzENode)
|
let enode = ENode.fromString(nodeId)[]
|
||||||
waitFor node.peerPool.connectToNode(newNode(bzzENode))
|
waitFor node.peerPool.connectToNode(newNode(enode))
|
||||||
|
|
||||||
doAssert node.peerPool.connectedNodes.len() == 1
|
doAssert node.peerPool.connectedNodes.len() == 1
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ proc setupBootNode*(): Future[ENode] {.async.} =
|
||||||
bootNodeKey = KeyPair.random()[]
|
bootNodeKey = KeyPair.random()[]
|
||||||
bootNodeAddr = localAddress(30301)
|
bootNodeAddr = localAddress(30301)
|
||||||
bootNode = await startDiscoveryNode(bootNodeKey.seckey, bootNodeAddr, @[])
|
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 =
|
proc setupTestNode*(capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode =
|
||||||
let keys1 = KeyPair.random()[]
|
let keys1 = KeyPair.random()[]
|
||||||
|
|
|
@ -98,20 +98,15 @@ let topic = [byte 0x12, 0, 0, 0]
|
||||||
if config.main:
|
if config.main:
|
||||||
var bootnodes: seq[ENode] = @[]
|
var bootnodes: seq[ENode] = @[]
|
||||||
for nodeId in MainnetBootnodes:
|
for nodeId in MainnetBootnodes:
|
||||||
var bootnode: ENode
|
bootnodes.add(ENode.fromString(nodeId).expect("static nodes"))
|
||||||
discard initENode(nodeId, bootnode)
|
|
||||||
bootnodes.add(bootnode)
|
|
||||||
|
|
||||||
asyncCheck node.connectToNetwork(bootnodes, true, true)
|
asyncCheck node.connectToNetwork(bootnodes, true, true)
|
||||||
# main network has mostly non SHH nodes, so we connect directly to SHH nodes
|
# main network has mostly non SHH nodes, so we connect directly to SHH nodes
|
||||||
for nodeId in WhisperNodes:
|
for nodeId in WhisperNodes:
|
||||||
var whisperENode: ENode
|
var whisperNode = newNode(ENode.fromString(nodeId).expect("static nodes"))
|
||||||
discard initENode(nodeId, whisperENode)
|
|
||||||
var whisperNode = newNode(whisperENode)
|
|
||||||
asyncCheck node.peerPool.connectToNode(whisperNode)
|
asyncCheck node.peerPool.connectToNode(whisperNode)
|
||||||
else:
|
else:
|
||||||
var bootENode: ENode
|
let bootENode = ENode.fromString(DockerBootnode).expect("static node")
|
||||||
discard initENode(DockerBootNode, bootENode)
|
|
||||||
waitFor node.connectToNetwork(@[bootENode], true, true)
|
waitFor node.connectToNetwork(@[bootENode], true, true)
|
||||||
|
|
||||||
if config.watch:
|
if config.watch:
|
||||||
|
|
|
@ -215,18 +215,20 @@ suite "Ethereum P2P handshake test suite":
|
||||||
|
|
||||||
block:
|
block:
|
||||||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||||
result = newHandshake(flags)
|
|
||||||
if Initiator in flags:
|
if Initiator in flags:
|
||||||
result.host.seckey = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
||||||
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
|
||||||
|
result = Handshake.tryInit(kp, flags)[]
|
||||||
|
|
||||||
let epki = testValue("initiator_ephemeral_private_key")
|
let epki = testValue("initiator_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
||||||
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||||
elif Responder in flags:
|
elif Responder in flags:
|
||||||
result.host.seckey = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
||||||
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
|
||||||
|
result = Handshake.tryInit(kp, flags)[]
|
||||||
let epkr = testValue("receiver_ephemeral_private_key")
|
let epkr = testValue("receiver_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
||||||
|
@ -238,9 +240,8 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = newSeq[byte](initiator.authSize(false))
|
var m0 = newSeq[byte](initiator.authSize(false))
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
check:
|
initiator.authMessage(
|
||||||
initiator.authMessage(responder.host.pubkey,
|
responder.host.pubkey, m0, k0, 0, false).expect("auth success")
|
||||||
m0, k0, 0, false) == AuthStatus.Success
|
|
||||||
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
|
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
|
||||||
var expect2 = fromHex(stripSpaces(pyevmAuth))
|
var expect2 = fromHex(stripSpaces(pyevmAuth))
|
||||||
check:
|
check:
|
||||||
|
@ -254,10 +255,11 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
||||||
let remoteHPubkey0 = initiator.host.pubkey
|
let remoteHPubkey0 = initiator.host.pubkey
|
||||||
|
|
||||||
|
initiator.authMessage(
|
||||||
|
responder.host.pubkey, m0, k0).expect("auth success")
|
||||||
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
check:
|
check:
|
||||||
initiator.authMessage(responder.host.pubkey,
|
|
||||||
m0, k0) == AuthStatus.Success
|
|
||||||
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
|
||||||
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
responder.remoteEPubkey == remoteEPubkey0
|
responder.remoteEPubkey == remoteEPubkey0
|
||||||
responder.remoteHPubkey == remoteHPubkey0
|
responder.remoteHPubkey == remoteHPubkey0
|
||||||
|
@ -270,11 +272,11 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
var k1 = 0
|
var k1 = 0
|
||||||
var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
|
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:
|
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
|
m1 == expect0
|
||||||
responder.initiatorNonce == initiator.initiatorNonce
|
responder.initiatorNonce == initiator.initiatorNonce
|
||||||
|
|
||||||
|
@ -285,12 +287,12 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var m1 = newSeq[byte](responder.ackSize())
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
var k1 = 0
|
var k1 = 0
|
||||||
check:
|
|
||||||
initiator.authMessage(responder.host.pubkey,
|
initiator.authMessage(
|
||||||
m0, k0) == AuthStatus.Success
|
responder.host.pubkey, m0, k0).expect("auth success")
|
||||||
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
responder.ackMessage(m1, k1) == AuthStatus.Success
|
responder.ackMessage(m1, k1).expect("ack success")
|
||||||
initiator.decodeAckMessage(m1) == AuthStatus.Success
|
initiator.decodeAckMessage(m1).expect("decode success")
|
||||||
let remoteEPubkey0 = responder.ephemeral.pubkey
|
let remoteEPubkey0 = responder.ephemeral.pubkey
|
||||||
let remoteHPubkey0 = responder.host.pubkey
|
let remoteHPubkey0 = responder.host.pubkey
|
||||||
check:
|
check:
|
||||||
|
@ -307,13 +309,12 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var tmac = fromHex(stripSpaces(testValue("mac_secret")))
|
var tmac = fromHex(stripSpaces(testValue("mac_secret")))
|
||||||
var temac = fromHex(stripSpaces(testValue("initial_egress_MAC")))
|
var temac = fromHex(stripSpaces(testValue("initial_egress_MAC")))
|
||||||
var timac = fromHex(stripSpaces(testValue("initial_ingress_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:
|
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.aesKey == csecResponder.aesKey
|
||||||
csecInitiator.macKey == csecResponder.macKey
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
taes[0..^1] == csecInitiator.aesKey[0..^1]
|
taes[0..^1] == csecInitiator.aesKey[0..^1]
|
||||||
|
@ -330,9 +331,11 @@ suite "Ethereum P2P handshake test suite":
|
||||||
|
|
||||||
block:
|
block:
|
||||||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||||
result = newHandshake(flags)
|
|
||||||
if Initiator in 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()[]
|
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
||||||
let esec = testE8Value("initiator_ephemeral_private_key")
|
let esec = testE8Value("initiator_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
||||||
|
@ -340,8 +343,10 @@ suite "Ethereum P2P handshake test suite":
|
||||||
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
||||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||||
elif Responder in flags:
|
elif Responder in flags:
|
||||||
result.host.seckey = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
|
let pk = PrivateKey.fromHex(testE8Value("receiver_private_key"))[]
|
||||||
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
|
||||||
|
result = Handshake.tryInit(kp, flags)[]
|
||||||
|
|
||||||
let esec = testE8Value("receiver_ephemeral_private_key")
|
let esec = testE8Value("receiver_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(esec)[]
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
||||||
|
@ -352,8 +357,8 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_v4")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_v4")))
|
||||||
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
check:
|
check:
|
||||||
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
|
||||||
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
||||||
let remoteHPubkey0 = initiator.host.pubkey
|
let remoteHPubkey0 = initiator.host.pubkey
|
||||||
|
@ -361,7 +366,7 @@ suite "Ethereum P2P handshake test suite":
|
||||||
responder.remoteEPubkey == remoteEPubkey0
|
responder.remoteEPubkey == remoteEPubkey0
|
||||||
responder.remoteHPubkey == remoteHPubkey0
|
responder.remoteHPubkey == remoteHPubkey0
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_v4")))
|
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
|
let remoteEPubkey1 = responder.ephemeral.pubkey
|
||||||
check:
|
check:
|
||||||
initiator.remoteEPubkey == remoteEPubkey1
|
initiator.remoteEPubkey == remoteEPubkey1
|
||||||
|
@ -371,28 +376,27 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8")))
|
||||||
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
check:
|
check:
|
||||||
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
|
||||||
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
||||||
check responder.remoteEPubkey == remoteEPubkey0
|
check responder.remoteEPubkey == remoteEPubkey0
|
||||||
let remoteHPubkey0 = initiator.host.pubkey
|
let remoteHPubkey0 = initiator.host.pubkey
|
||||||
check responder.remoteHPubkey == remoteHPubkey0
|
check responder.remoteHPubkey == remoteHPubkey0
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8")))
|
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
|
let remoteEPubkey1 = responder.ephemeral.pubkey
|
||||||
check:
|
check:
|
||||||
initiator.remoteEPubkey == remoteEPubkey1
|
initiator.remoteEPubkey == remoteEPubkey1
|
||||||
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
|
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
|
||||||
var taes = fromHex(stripSpaces(testE8Value("auth2ack2_aes_secret")))
|
var taes = fromHex(stripSpaces(testE8Value("auth2ack2_aes_secret")))
|
||||||
var tmac = fromHex(stripSpaces(testE8Value("auth2ack2_mac_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:
|
check:
|
||||||
int(initiator.version) == 4
|
int(initiator.version) == 4
|
||||||
int(responder.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.aesKey == csecResponder.aesKey
|
||||||
csecInitiator.macKey == csecResponder.macKey
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
taes[0..^1] == csecInitiator.aesKey[0..^1]
|
taes[0..^1] == csecInitiator.aesKey[0..^1]
|
||||||
|
@ -407,8 +411,8 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8_3f")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8_3f")))
|
||||||
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
check:
|
check:
|
||||||
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
|
||||||
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
let remoteEPubkey0 = initiator.ephemeral.pubkey
|
||||||
let remoteHPubkey0 = initiator.host.pubkey
|
let remoteHPubkey0 = initiator.host.pubkey
|
||||||
|
@ -416,7 +420,7 @@ suite "Ethereum P2P handshake test suite":
|
||||||
responder.remoteEPubkey == remoteEPubkey0
|
responder.remoteEPubkey == remoteEPubkey0
|
||||||
responder.remoteHPubkey == remoteHPubkey0
|
responder.remoteHPubkey == remoteHPubkey0
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8_3f")))
|
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
|
let remoteEPubkey1 = responder.ephemeral.pubkey
|
||||||
check:
|
check:
|
||||||
int(initiator.version) == 57
|
int(initiator.version) == 57
|
||||||
|
@ -429,22 +433,20 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var initiator = newTestHandshake({Initiator, EIP8})
|
var initiator = newTestHandshake({Initiator, EIP8})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = newSeq[byte](initiator.authSize())
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
var csecInitiator: ConnectionSecret
|
|
||||||
var csecResponder: ConnectionSecret
|
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
var k1 = 0
|
var k1 = 0
|
||||||
check initiator.authMessage(responder.host.pubkey,
|
initiator.authMessage(
|
||||||
m0, k0) == AuthStatus.Success
|
responder.host.pubkey, m0, k0).expect("auth success")
|
||||||
m0.setLen(k0)
|
m0.setLen(k0)
|
||||||
check responder.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0).expect("decode success")
|
||||||
check (EIP8 in responder.flags) == true
|
check (EIP8 in responder.flags) == true
|
||||||
var m1 = newSeq[byte](responder.ackSize())
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
check responder.ackMessage(m1, k1) == AuthStatus.Success
|
responder.ackMessage(m1, k1).expect("ack success")
|
||||||
m1.setLen(k1)
|
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:
|
check:
|
||||||
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
|
||||||
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
|
|
||||||
csecInitiator.aesKey == csecResponder.aesKey
|
csecInitiator.aesKey == csecResponder.aesKey
|
||||||
csecInitiator.macKey == csecResponder.macKey
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
|
|
||||||
|
@ -453,21 +455,19 @@ suite "Ethereum P2P handshake test suite":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = newSeq[byte](initiator.authSize())
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
var csecInitiator: ConnectionSecret
|
|
||||||
var csecResponder: ConnectionSecret
|
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
var k1 = 0
|
var k1 = 0
|
||||||
check initiator.authMessage(responder.host.pubkey,
|
initiator.authMessage(
|
||||||
m0, k0) == AuthStatus.Success
|
responder.host.pubkey, m0, k0).expect("auth success")
|
||||||
m0.setLen(k0)
|
m0.setLen(k0)
|
||||||
check responder.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0).expect("auth success")
|
||||||
var m1 = newSeq[byte](responder.ackSize())
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
check responder.ackMessage(m1, k1) == AuthStatus.Success
|
responder.ackMessage(m1, k1).expect("ack success")
|
||||||
m1.setLen(k1)
|
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:
|
check:
|
||||||
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
|
||||||
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
|
|
||||||
csecInitiator.aesKey == csecResponder.aesKey
|
csecInitiator.aesKey == csecResponder.aesKey
|
||||||
csecInitiator.macKey == csecResponder.macKey
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
|
|
|
@ -88,18 +88,19 @@ proc testValue(s: string): string =
|
||||||
|
|
||||||
suite "Ethereum RLPx encryption/decryption test suite":
|
suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||||
result = newHandshake(flags)
|
|
||||||
if Initiator in flags:
|
if Initiator in flags:
|
||||||
result.host.seckey = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
let pk = PrivateKey.fromHex(testValue("initiator_private_key"))[]
|
||||||
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
|
||||||
|
result = Handshake.tryInit(kp, flags)[]
|
||||||
let epki = testValue("initiator_ephemeral_private_key")
|
let epki = testValue("initiator_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(epki)[]
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
||||||
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||||
elif Responder in flags:
|
elif Responder in flags:
|
||||||
result.host.seckey = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
let pk = PrivateKey.fromHex(testValue("receiver_private_key"))[]
|
||||||
result.host.pubkey = result.host.seckey.toPublicKey()[]
|
let kp = KeyPair(seckey: pk, pubkey: pk.toPublicKey()[])
|
||||||
|
result = Handshake.tryInit(kp, flags)[]
|
||||||
let epkr = testValue("receiver_ephemeral_private_key")
|
let epkr = testValue("receiver_ephemeral_private_key")
|
||||||
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
result.ephemeral.seckey = PrivateKey.fromHex(epkr)[]
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
result.ephemeral.pubkey = result.ephemeral.seckey.toPublicKey()[]
|
||||||
|
@ -111,15 +112,13 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var authm = fromHex(stripSpaces(testValue("auth_ciphertext")))
|
var authm = fromHex(stripSpaces(testValue("auth_ciphertext")))
|
||||||
var ackm = fromHex(stripSpaces(testValue("authresp_ciphertext")))
|
var ackm = fromHex(stripSpaces(testValue("authresp_ciphertext")))
|
||||||
var csecInitiator: ConnectionSecret
|
|
||||||
var csecResponder: ConnectionSecret
|
|
||||||
var stateInitiator0, stateInitiator1: SecretState
|
var stateInitiator0, stateInitiator1: SecretState
|
||||||
var stateResponder0, stateResponder1: SecretState
|
var stateResponder0, stateResponder1: SecretState
|
||||||
check:
|
responder.decodeAuthMessage(authm).expect("success")
|
||||||
responder.decodeAuthMessage(authm) == AuthStatus.Success
|
initiator.decodeAckMessage(ackm).expect("success")
|
||||||
initiator.decodeAckMessage(ackm) == AuthStatus.Success
|
|
||||||
initiator.getSecrets(authm, ackm, csecInitiator) == AuthStatus.Success
|
var csecInitiator = initiator.getSecrets(authm, ackm)[]
|
||||||
responder.getSecrets(authm, ackm, csecResponder) == AuthStatus.Success
|
var csecResponder = responder.getSecrets(authm, ackm)[]
|
||||||
initSecretState(csecInitiator, stateInitiator0)
|
initSecretState(csecInitiator, stateInitiator0)
|
||||||
initSecretState(csecResponder, stateResponder0)
|
initSecretState(csecResponder, stateResponder0)
|
||||||
initSecretState(csecInitiator, stateInitiator1)
|
initSecretState(csecInitiator, stateInitiator1)
|
||||||
|
@ -132,7 +131,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
|
|
||||||
block:
|
block:
|
||||||
check stateResponder0.decryptHeader(toOpenArray(initiatorHello, 0, 31),
|
check stateResponder0.decryptHeader(toOpenArray(initiatorHello, 0, 31),
|
||||||
header) == RlpxStatus.Success
|
header).isOk()
|
||||||
let bodysize = getBodySize(header)
|
let bodysize = getBodySize(header)
|
||||||
check bodysize == 79
|
check bodysize == 79
|
||||||
# we need body size to be rounded to 16 bytes boundary to properly
|
# 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:
|
check:
|
||||||
stateResponder0.decryptBody(
|
stateResponder0.decryptBody(
|
||||||
toOpenArray(initiatorHello, 32, len(initiatorHello) - 1),
|
toOpenArray(initiatorHello, 32, len(initiatorHello) - 1),
|
||||||
getBodySize(header), body, decrsize) == RlpxStatus.Success
|
getBodySize(header), body, decrsize).isOk()
|
||||||
decrsize == 79
|
decrsize == 79
|
||||||
body.setLen(decrsize)
|
body.setLen(decrsize)
|
||||||
var hello = newSeq[byte](encryptedLength(bodysize))
|
var hello = newSeq[byte](encryptedLength(bodysize))
|
||||||
check:
|
check:
|
||||||
stateInitiator1.encrypt(header, body, hello) == RlpxStatus.Success
|
stateInitiator1.encrypt(header, body, hello).isOk()
|
||||||
hello == initiatorHello
|
hello == initiatorHello
|
||||||
block:
|
block:
|
||||||
check stateInitiator0.decryptHeader(toOpenArray(responderHello, 0, 31),
|
check stateInitiator0.decryptHeader(toOpenArray(responderHello, 0, 31),
|
||||||
header) == RlpxStatus.Success
|
header).isOk()
|
||||||
let bodysize = getBodySize(header)
|
let bodysize = getBodySize(header)
|
||||||
check bodysize == 79
|
check bodysize == 79
|
||||||
# we need body size to be rounded to 16 bytes boundary to properly
|
# 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:
|
check:
|
||||||
stateInitiator0.decryptBody(
|
stateInitiator0.decryptBody(
|
||||||
toOpenArray(responderHello, 32, len(initiatorHello) - 1),
|
toOpenArray(responderHello, 32, len(initiatorHello) - 1),
|
||||||
getBodySize(header), body, decrsize) == RlpxStatus.Success
|
getBodySize(header), body, decrsize).isOk()
|
||||||
decrsize == 79
|
decrsize == 79
|
||||||
body.setLen(decrsize)
|
body.setLen(decrsize)
|
||||||
var hello = newSeq[byte](encryptedLength(bodysize))
|
var hello = newSeq[byte](encryptedLength(bodysize))
|
||||||
check:
|
check:
|
||||||
stateResponder1.encrypt(header, body, hello) == RlpxStatus.Success
|
stateResponder1.encrypt(header, body, hello).isOk()
|
||||||
hello == responderHello
|
hello == responderHello
|
||||||
|
|
||||||
test "Continuous stream of different lengths (1000 times)":
|
test "Continuous stream of different lengths (1000 times)":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var responder = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
var m0 = newSeq[byte](initiator.authSize())
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
var csecInitiator: ConnectionSecret
|
|
||||||
var csecResponder: ConnectionSecret
|
|
||||||
var k0 = 0
|
var k0 = 0
|
||||||
var k1 = 0
|
var k1 = 0
|
||||||
check initiator.authMessage(responder.host.pubkey,
|
check initiator.authMessage(responder.host.pubkey,
|
||||||
m0, k0) == AuthStatus.Success
|
m0, k0).isOk
|
||||||
m0.setLen(k0)
|
m0.setLen(k0)
|
||||||
check responder.decodeAuthMessage(m0) == AuthStatus.Success
|
check responder.decodeAuthMessage(m0).isOk
|
||||||
var m1 = newSeq[byte](responder.ackSize())
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
check responder.ackMessage(m1, k1) == AuthStatus.Success
|
check responder.ackMessage(m1, k1).isOk
|
||||||
m1.setLen(k1)
|
m1.setLen(k1)
|
||||||
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
check initiator.decodeAckMessage(m1).isOk
|
||||||
|
|
||||||
check:
|
var csecInitiator = initiator.getSecrets(m0, m1)[]
|
||||||
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
var csecResponder = responder.getSecrets(m0, m1)[]
|
||||||
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
|
|
||||||
var stateInitiator: SecretState
|
var stateInitiator: SecretState
|
||||||
var stateResponder: SecretState
|
var stateResponder: SecretState
|
||||||
var iheader, rheader: array[16, byte]
|
var iheader, rheader: array[16, byte]
|
||||||
|
@ -207,9 +203,9 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
check:
|
check:
|
||||||
randomBytes(ibody) == len(ibody)
|
randomBytes(ibody) == len(ibody)
|
||||||
stateInitiator.encrypt(iheader, ibody,
|
stateInitiator.encrypt(iheader, ibody,
|
||||||
encrypted) == RlpxStatus.Success
|
encrypted).isOk()
|
||||||
stateResponder.decryptHeader(toOpenArray(encrypted, 0, 31),
|
stateResponder.decryptHeader(toOpenArray(encrypted, 0, 31),
|
||||||
rheader) == RlpxStatus.Success
|
rheader).isOk()
|
||||||
var length = getBodySize(rheader)
|
var length = getBodySize(rheader)
|
||||||
check length == len(ibody)
|
check length == len(ibody)
|
||||||
var rbody = newSeq[byte](decryptedLength(length))
|
var rbody = newSeq[byte](decryptedLength(length))
|
||||||
|
@ -217,7 +213,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
check:
|
check:
|
||||||
stateResponder.decryptBody(
|
stateResponder.decryptBody(
|
||||||
toOpenArray(encrypted, 32, len(encrypted) - 1),
|
toOpenArray(encrypted, 32, len(encrypted) - 1),
|
||||||
length, rbody, decrsize) == RlpxStatus.Success
|
length, rbody, decrsize).isOk()
|
||||||
decrsize == length
|
decrsize == length
|
||||||
rbody.setLen(decrsize)
|
rbody.setLen(decrsize)
|
||||||
check:
|
check:
|
||||||
|
@ -235,9 +231,9 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
check:
|
check:
|
||||||
randomBytes(ibody) == len(ibody)
|
randomBytes(ibody) == len(ibody)
|
||||||
stateResponder.encrypt(iheader, ibody,
|
stateResponder.encrypt(iheader, ibody,
|
||||||
encrypted) == RlpxStatus.Success
|
encrypted).isOk()
|
||||||
stateInitiator.decryptHeader(toOpenArray(encrypted, 0, 31),
|
stateInitiator.decryptHeader(toOpenArray(encrypted, 0, 31),
|
||||||
rheader) == RlpxStatus.Success
|
rheader).isOk()
|
||||||
var length = getBodySize(rheader)
|
var length = getBodySize(rheader)
|
||||||
check length == len(ibody)
|
check length == len(ibody)
|
||||||
var rbody = newSeq[byte](decryptedLength(length))
|
var rbody = newSeq[byte](decryptedLength(length))
|
||||||
|
@ -245,7 +241,7 @@ suite "Ethereum RLPx encryption/decryption test suite":
|
||||||
check:
|
check:
|
||||||
stateInitiator.decryptBody(
|
stateInitiator.decryptBody(
|
||||||
toOpenArray(encrypted, 32, len(encrypted) - 1),
|
toOpenArray(encrypted, 32, len(encrypted) - 1),
|
||||||
length, rbody, decrsize) == RlpxStatus.Success
|
length, rbody, decrsize).isOk()
|
||||||
decrsize == length
|
decrsize == length
|
||||||
rbody.setLen(length)
|
rbody.setLen(length)
|
||||||
check:
|
check:
|
||||||
|
|
|
@ -22,7 +22,7 @@ proc test() {.async.} =
|
||||||
bootNodeKey = PrivateKey.fromHex(
|
bootNodeKey = PrivateKey.fromHex(
|
||||||
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
|
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")[]
|
||||||
bootNodeAddr = localAddress(20301)
|
bootNodeAddr = localAddress(20301)
|
||||||
bootENode = initENode(bootNodeKey.toPublicKey()[], bootNodeAddr)
|
bootENode = ENode(pubkey: bootNodeKey.toPublicKey()[], address: bootNodeAddr)
|
||||||
bootNode = await startDiscoveryNode(bootNodeKey, bootNodeAddr, @[])
|
bootNode = await startDiscoveryNode(bootNodeKey, bootNodeAddr, @[])
|
||||||
|
|
||||||
test "Discover nodes":
|
test "Discover nodes":
|
||||||
|
|
|
@ -134,9 +134,9 @@ suite "Discovery v5 Cryptographic Primitives":
|
||||||
let
|
let
|
||||||
pub = PublicKey.fromHex(publicKey)[]
|
pub = PublicKey.fromHex(publicKey)[]
|
||||||
priv = PrivateKey.fromHex(secretKey)[]
|
priv = PrivateKey.fromHex(secretKey)[]
|
||||||
|
|
||||||
let eph = ecdhRawFull(priv, pub)
|
let eph = ecdhRawFull(priv, pub)
|
||||||
check:
|
check:
|
||||||
|
eph.isOk()
|
||||||
eph[].data == hexToSeqByte(sharedSecret)
|
eph[].data == hexToSeqByte(sharedSecret)
|
||||||
|
|
||||||
test "Key Derivation":
|
test "Key Derivation":
|
||||||
|
|
|
@ -71,14 +71,18 @@ suite "ECIES test suite":
|
||||||
var shmac = [0x13'u8, 0x13'u8]
|
var shmac = [0x13'u8, 0x13'u8]
|
||||||
var s = PrivateKey.random()[]
|
var s = PrivateKey.random()[]
|
||||||
var p = s.toPublicKey()[]
|
var p = s.toPublicKey()[]
|
||||||
|
|
||||||
|
eciesEncrypt(plain, encr, p).expect("encryption should succeed")
|
||||||
|
eciesDecrypt(encr, decr, s).expect("decryption should succeed")
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# Without additional mac data
|
# 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))
|
equalMem(addr m[0], addr decr[0], len(m))
|
||||||
# With additional mac data
|
# With additional mac data
|
||||||
eciesEncrypt(plain, encr, p, shmac) == EciesStatus.Success
|
eciesEncrypt(plain, encr, p, shmac).expect("encryption should succeed")
|
||||||
eciesDecrypt(encr, decr, s, shmac) == EciesStatus.Success
|
eciesDecrypt(encr, decr, s, shmac).expect("decryption should succeed")
|
||||||
|
|
||||||
|
check:
|
||||||
equalMem(addr m[0], addr decr[0], len(m))
|
equalMem(addr m[0], addr decr[0], len(m))
|
||||||
|
|
||||||
test "ECIES/py-evm/cpp-ethereum test_ecies.py#L43/rlpx.cpp#L187":
|
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 s = PrivateKey.fromHex(secretKeys[i])[]
|
||||||
var cipher = fromHex(stripSpaces(cipherText[i]))
|
var cipher = fromHex(stripSpaces(cipherText[i]))
|
||||||
var expect = fromHex(stripSpaces(expectText[i]))
|
var expect = fromHex(stripSpaces(expectText[i]))
|
||||||
|
|
||||||
|
eciesDecrypt(cipher, data, s).expect("decryption should succeed")
|
||||||
|
|
||||||
check:
|
check:
|
||||||
eciesDecrypt(cipher, data, s) == EciesStatus.Success
|
|
||||||
compare(data, expect) == true
|
compare(data, expect) == true
|
||||||
|
|
||||||
test "ECIES/cpp-ethereum rlpx.cpp#L432-L459":
|
test "ECIES/cpp-ethereum rlpx.cpp#L432-L459":
|
||||||
|
@ -167,5 +173,5 @@ suite "ECIES test suite":
|
||||||
var s = PrivateKey.fromHex(secretKeys[i])[]
|
var s = PrivateKey.fromHex(secretKeys[i])[]
|
||||||
var cipher = fromHex(stripSpaces(cipherData[i]))
|
var cipher = fromHex(stripSpaces(cipherData[i]))
|
||||||
check:
|
check:
|
||||||
eciesDecrypt(cipher, data, s) == EciesStatus.Success
|
eciesDecrypt(cipher, data, s).isOk()
|
||||||
compare(data, expectData[i]) == true
|
compare(data, expectData[i]) == true
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
import unittest, net
|
import unittest, net, options
|
||||||
import eth/p2p/enode
|
import eth/p2p/enode
|
||||||
|
|
||||||
suite "ENode":
|
suite "ENode":
|
||||||
|
@ -28,26 +28,27 @@ suite "ENode":
|
||||||
]
|
]
|
||||||
|
|
||||||
const results = [
|
const results = [
|
||||||
IncorrectScheme,
|
some IncorrectScheme,
|
||||||
IncorrectNodeId,
|
some IncorrectNodeId,
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
IncorrectPort,
|
some IncorrectPort,
|
||||||
IncorrectDiscPort,
|
some IncorrectDiscPort,
|
||||||
IncorrectScheme,
|
some IncorrectScheme,
|
||||||
IncorrectNodeId,
|
some IncorrectNodeId,
|
||||||
IncorrectScheme,
|
some IncorrectScheme,
|
||||||
ENodeStatus.Success,
|
none(ENodeError),
|
||||||
ENodeStatus.Success,
|
none(ENodeError),
|
||||||
ENodeStatus.Success,
|
none(ENodeError),
|
||||||
ENodeStatus.Success
|
none(ENodeError),
|
||||||
]
|
]
|
||||||
|
|
||||||
for index in 0..<len(enodes):
|
for index in 0..<len(enodes):
|
||||||
var node: ENode
|
let res = ENode.fromString(enodes[index])
|
||||||
let res = initENode(enodes[index], node)
|
check (results[index].isSome and res.error == results[index].get) or
|
||||||
check res == results[index]
|
res.isOk
|
||||||
if res == ENodeStatus.Success:
|
|
||||||
check enodes[index] == $node
|
if res.isOk:
|
||||||
|
check enodes[index] == $res[]
|
||||||
|
|
||||||
test "Custom validation tests":
|
test "Custom validation tests":
|
||||||
const enodes = [
|
const enodes = [
|
||||||
|
@ -67,35 +68,23 @@ suite "ENode":
|
||||||
]
|
]
|
||||||
|
|
||||||
const results = [
|
const results = [
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
ENodeStatus.Success,
|
none(ENodeError),
|
||||||
IncorrectUri,
|
some IncorrectUri,
|
||||||
IncorrectPort,
|
some IncorrectPort,
|
||||||
IncorrectPort,
|
some IncorrectPort,
|
||||||
IncorrectDiscPort,
|
some IncorrectDiscPort,
|
||||||
IncorrectDiscPort,
|
some IncorrectDiscPort,
|
||||||
IncorrectUri,
|
some IncorrectUri,
|
||||||
IncorrectIP,
|
some IncorrectIP,
|
||||||
IncorrectNodeId
|
some IncorrectNodeId
|
||||||
]
|
]
|
||||||
|
|
||||||
for index in 0..<len(enodes):
|
for index in 0..<len(enodes):
|
||||||
var node: ENode
|
let res = ENode.fromString(enodes[index])
|
||||||
let res = initENode(enodes[index], node)
|
|
||||||
check res == results[index]
|
|
||||||
|
|
||||||
test "isCorrect() tests":
|
check (results[index].isSome and res.error == results[index].get) or
|
||||||
var node: ENode
|
res.isOk
|
||||||
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
|
|
||||||
|
|
|
@ -58,8 +58,7 @@ suite "Testing protocol handlers":
|
||||||
var node2 = setupTestNode(abc, xyz)
|
var node2 = setupTestNode(abc, xyz)
|
||||||
|
|
||||||
node2.startListening()
|
node2.startListening()
|
||||||
let peer = await node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
|
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
check:
|
check:
|
||||||
peer.isNil == false
|
peer.isNil == false
|
||||||
|
|
||||||
|
@ -74,8 +73,7 @@ suite "Testing protocol handlers":
|
||||||
var node1 = setupTestNode(hah)
|
var node1 = setupTestNode(hah)
|
||||||
var node2 = setupTestNode(hah)
|
var node2 = setupTestNode(hah)
|
||||||
node2.startListening()
|
node2.startListening()
|
||||||
let peer = await node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
|
let peer = await node1.rlpxConnect(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
check:
|
check:
|
||||||
peer.isNil == true
|
peer.isNil == true
|
||||||
# To check if the disconnection handler did not run
|
# To check if the disconnection handler did not run
|
||||||
|
|
|
@ -13,8 +13,7 @@ node1 = setupTestNode(eth, Whisper)
|
||||||
node2 = setupTestNode(eth, Whisper)
|
node2 = setupTestNode(eth, Whisper)
|
||||||
|
|
||||||
node2.startListening()
|
node2.startListening()
|
||||||
peer = waitFor node1.rlpxConnect(newNode(initENode(node2.keys.pubKey,
|
peer = waitFor node1.rlpxConnect(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
|
|
||||||
proc testThunk(payload: openArray[byte]) =
|
proc testThunk(payload: openArray[byte]) =
|
||||||
var (msgId, msgData) = recvMsgMock(payload)
|
var (msgId, msgData) = recvMsgMock(payload)
|
||||||
|
|
|
@ -23,8 +23,7 @@ procSuite "Whisper connections":
|
||||||
var node1 = setupTestNode(Whisper)
|
var node1 = setupTestNode(Whisper)
|
||||||
var node2 = setupTestNode(Whisper)
|
var node2 = setupTestNode(Whisper)
|
||||||
node2.startListening()
|
node2.startListening()
|
||||||
waitFor node1.peerPool.connectToNode(newNode(initENode(node2.keys.pubKey,
|
waitFor node1.peerPool.connectToNode(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
asyncTest "Two peers connected":
|
asyncTest "Two peers connected":
|
||||||
check:
|
check:
|
||||||
node1.peerPool.connectedNodes.len() == 1
|
node1.peerPool.connectedNodes.len() == 1
|
||||||
|
@ -298,8 +297,7 @@ procSuite "Whisper connections":
|
||||||
var ln1 = setupTestNode(Whisper)
|
var ln1 = setupTestNode(Whisper)
|
||||||
ln1.setLightNode(true)
|
ln1.setLightNode(true)
|
||||||
|
|
||||||
await ln1.peerPool.connectToNode(newNode(initENode(node2.keys.pubKey,
|
await ln1.peerPool.connectToNode(newNode(node2.toENode()))
|
||||||
node2.address)))
|
|
||||||
|
|
||||||
let topic = [byte 0, 0, 0, 0]
|
let topic = [byte 0, 0, 0, 0]
|
||||||
|
|
||||||
|
@ -322,6 +320,5 @@ procSuite "Whisper connections":
|
||||||
ln2.setLightNode(true)
|
ln2.setLightNode(true)
|
||||||
|
|
||||||
ln2.startListening()
|
ln2.startListening()
|
||||||
let peer = await ln1.rlpxConnect(newNode(initENode(ln2.keys.pubKey,
|
let peer = await ln1.rlpxConnect(newNode(ln2.toENode()))
|
||||||
ln2.address)))
|
|
||||||
check peer.isNil == true
|
check peer.isNil == true
|
||||||
|
|
|
@ -26,8 +26,7 @@ procSuite "Waku - Whisper bridge tests":
|
||||||
nodeWaku = setupTestNode(Waku)
|
nodeWaku = setupTestNode(Waku)
|
||||||
|
|
||||||
nodeWakuWhisper.startListening()
|
nodeWakuWhisper.startListening()
|
||||||
let bridgeNode = newNode(initENode(nodeWakuWhisper.keys.pubKey,
|
let bridgeNode = newNode(nodeWakuWhisper.toENode())
|
||||||
nodeWakuWhisper.address))
|
|
||||||
nodeWakuWhisper.shareMessageQueue()
|
nodeWakuWhisper.shareMessageQueue()
|
||||||
|
|
||||||
waitFor nodeWhisper.peerPool.connectToNode(bridgeNode)
|
waitFor nodeWhisper.peerPool.connectToNode(bridgeNode)
|
||||||
|
|
|
@ -48,9 +48,9 @@ suite "Waku connections":
|
||||||
n3.startListening()
|
n3.startListening()
|
||||||
|
|
||||||
let
|
let
|
||||||
p1 = await n2.rlpxConnect(newNode(initENode(n1.keys.pubKey, n1.address)))
|
p1 = await n2.rlpxConnect(newNode(n1.toENode()))
|
||||||
p2 = await n2.rlpxConnect(newNode(initENode(n3.keys.pubKey, n3.address)))
|
p2 = await n2.rlpxConnect(newNode(n3.toENode()))
|
||||||
p3 = await n4.rlpxConnect(newNode(initENode(n3.keys.pubKey, n3.address)))
|
p3 = await n4.rlpxConnect(newNode(n3.toENode()))
|
||||||
check:
|
check:
|
||||||
p1.isNil
|
p1.isNil
|
||||||
p2.isNil == false
|
p2.isNil == false
|
||||||
|
@ -70,8 +70,7 @@ suite "Waku connections":
|
||||||
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1])
|
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1])
|
||||||
|
|
||||||
wakuNode.startListening()
|
wakuNode.startListening()
|
||||||
await wakuTopicNode.peerPool.connectToNode(newNode(
|
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
|
||||||
initENode(wakuNode.keys.pubKey, wakuNode.address)))
|
|
||||||
|
|
||||||
# Update topic interest
|
# Update topic interest
|
||||||
check:
|
check:
|
||||||
|
@ -93,8 +92,7 @@ suite "Waku connections":
|
||||||
wakuNode = setupTestNode(Waku)
|
wakuNode = setupTestNode(Waku)
|
||||||
|
|
||||||
wakuNode.startListening()
|
wakuNode.startListening()
|
||||||
await wakuPowNode.peerPool.connectToNode(newNode(
|
await wakuPowNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
|
||||||
initENode(wakuNode.keys.pubKey, wakuNode.address)))
|
|
||||||
|
|
||||||
# Update minimum pow
|
# Update minimum pow
|
||||||
await setPowRequirement(wakuPowNode, 1.0)
|
await setPowRequirement(wakuPowNode, 1.0)
|
||||||
|
@ -114,8 +112,7 @@ suite "Waku connections":
|
||||||
wakuNode = setupTestNode(Waku)
|
wakuNode = setupTestNode(Waku)
|
||||||
|
|
||||||
wakuNode.startListening()
|
wakuNode.startListening()
|
||||||
await wakuLightNode.peerPool.connectToNode(newNode(
|
await wakuLightNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
|
||||||
initENode(wakuNode.keys.pubKey, wakuNode.address)))
|
|
||||||
|
|
||||||
# Update minimum pow
|
# Update minimum pow
|
||||||
await setLightNode(wakuLightNode, true)
|
await setLightNode(wakuLightNode, true)
|
||||||
|
@ -140,8 +137,7 @@ suite "Waku connections":
|
||||||
discard await wakuBloomNode.setTopicInterest(topics)
|
discard await wakuBloomNode.setTopicInterest(topics)
|
||||||
|
|
||||||
wakuBloomNode.startListening()
|
wakuBloomNode.startListening()
|
||||||
await wakuNode.peerPool.connectToNode(newNode(
|
await wakuNode.peerPool.connectToNode(newNode(wakuBloomNode.toENode()))
|
||||||
initENode(wakuBloomNode.keys.pubKey, wakuBloomNode.address)))
|
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
check:
|
check:
|
||||||
|
@ -188,8 +184,7 @@ suite "Waku connections":
|
||||||
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1, topic2])
|
wakuTopicNode.protocolState(Waku).config.topics = some(@[topic1, topic2])
|
||||||
|
|
||||||
wakuNode.startListening()
|
wakuNode.startListening()
|
||||||
await wakuTopicNode.peerPool.connectToNode(newNode(
|
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
|
||||||
initENode(wakuNode.keys.pubKey, wakuNode.address)))
|
|
||||||
|
|
||||||
let payload = repeat(byte 0, 10)
|
let payload = repeat(byte 0, 10)
|
||||||
check:
|
check:
|
||||||
|
@ -217,8 +212,7 @@ suite "Waku connections":
|
||||||
wakuTopicNode.protocolState(Waku).config.bloom = some(toBloom([bloomTopic]))
|
wakuTopicNode.protocolState(Waku).config.bloom = some(toBloom([bloomTopic]))
|
||||||
|
|
||||||
wakuNode.startListening()
|
wakuNode.startListening()
|
||||||
await wakuTopicNode.peerPool.connectToNode(newNode(
|
await wakuTopicNode.peerPool.connectToNode(newNode(wakuNode.toENode()))
|
||||||
initENode(wakuNode.keys.pubKey, wakuNode.address)))
|
|
||||||
|
|
||||||
let payload = repeat(byte 0, 10)
|
let payload = repeat(byte 0, 10)
|
||||||
check:
|
check:
|
||||||
|
@ -235,8 +229,7 @@ suite "Waku connections":
|
||||||
await ln.setLightNode(true)
|
await ln.setLightNode(true)
|
||||||
var fn = setupTestNode(Waku)
|
var fn = setupTestNode(Waku)
|
||||||
fn.startListening()
|
fn.startListening()
|
||||||
await ln.peerPool.connectToNode(newNode(initENode(fn.keys.pubKey,
|
await ln.peerPool.connectToNode(newNode(fn.toENode()))
|
||||||
fn.address)))
|
|
||||||
|
|
||||||
let topic = [byte 0, 0, 0, 0]
|
let topic = [byte 0, 0, 0, 0]
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,8 @@ procSuite "Waku Mail Client":
|
||||||
var simpleServer = setupTestNode(Waku)
|
var simpleServer = setupTestNode(Waku)
|
||||||
|
|
||||||
simpleServer.startListening()
|
simpleServer.startListening()
|
||||||
let simpleServerNode = newNode(initENode(simpleServer.keys.pubKey,
|
let simpleServerNode = newNode(simpleServer.toENode())
|
||||||
simpleServer.address))
|
let clientNode = newNode(client.toENode())
|
||||||
let clientNode = newNode(initENode(client.keys.pubKey, client.address))
|
|
||||||
waitFor client.peerPool.connectToNode(simpleServerNode)
|
waitFor client.peerPool.connectToNode(simpleServerNode)
|
||||||
require:
|
require:
|
||||||
waitFor simpleServer.waitForConnected().withTimeout(transmissionTimeout)
|
waitFor simpleServer.waitForConnected().withTimeout(transmissionTimeout)
|
||||||
|
|
Loading…
Reference in New Issue