Final version for review.
This commit is contained in:
parent
b89d42b633
commit
59d65df3f4
686
ethp2p/auth.nim
686
ethp2p/auth.nim
|
@ -13,27 +13,31 @@ import endians
|
||||||
import ecc, ecies, rlp
|
import ecc, ecies, rlp
|
||||||
import nimcrypto/sysrand, nimcrypto/hash, nimcrypto/utils, nimcrypto/hmac
|
import nimcrypto/sysrand, nimcrypto/hash, nimcrypto/utils, nimcrypto/hmac
|
||||||
import nimcrypto/rijndael, nimcrypto/keccak, nimcrypto/sha2
|
import nimcrypto/rijndael, nimcrypto/keccak, nimcrypto/sha2
|
||||||
import hexdump
|
|
||||||
|
|
||||||
const
|
const
|
||||||
SupportedRlpxVersion* = 4
|
SupportedRlpxVersion* = 4
|
||||||
# REVIEW: If these messages have fixed lenghts, they will be
|
PlainAuthMessageV4Length* = 194
|
||||||
# better described by an object type (see my similar comments
|
AuthMessageV4Length* = 307
|
||||||
# in the ecies module.
|
PlainAuthMessageEIP8Length = 169
|
||||||
PlainAuthMessageLength* = 194
|
PlainAuthMessageMaxEIP8 = PlainAuthMessageEIP8Length + 255
|
||||||
PlainAuthAckMessageLength* = 97
|
AuthMessageEIP8Length* = 282 + 2
|
||||||
AuthMessageLength* = 307
|
AuthMessageMaxEIP8* = AuthMessageEIP8Length + 255
|
||||||
AuthAckMessageLength* = 210
|
PlainAckMessageV4Length* = 97
|
||||||
|
AckMessageV4Length* = 210
|
||||||
|
PlainAckMessageEIP8Length = 102
|
||||||
|
PlainAckMessageMaxEIP8 = PlainAckMessageEIP8Length + 255
|
||||||
|
AckMessageEIP8Length = 215 + 2
|
||||||
|
AckMessageMaxEIP8 = AckMessageEIP8Length + 255
|
||||||
|
|
||||||
type
|
type
|
||||||
PlainAuthMessage* = object {.packed.}
|
AuthMessageV4* = object {.packed.}
|
||||||
signature: RawSignature
|
signature: RawSignature
|
||||||
keyhash: array[keccak256.sizeDigest, byte]
|
keyhash: array[keccak256.sizeDigest, byte]
|
||||||
pubkey: PublicKey
|
pubkey: PublicKey
|
||||||
nonce: array[keccak256.sizeDigest, byte]
|
nonce: array[keccak256.sizeDigest, byte]
|
||||||
flag: byte
|
flag: byte
|
||||||
|
|
||||||
PlainAuthAckMessage* = object {.packed.}
|
AckMessageV4* = object {.packed.}
|
||||||
pubkey: PublicKey
|
pubkey: PublicKey
|
||||||
nonce: array[keccak256.sizeDigest, byte]
|
nonce: array[keccak256.sizeDigest, byte]
|
||||||
flag: byte
|
flag: byte
|
||||||
|
@ -47,6 +51,7 @@ type
|
||||||
Success, ## Operation was successful
|
Success, ## Operation was successful
|
||||||
RandomError, ## Could not obtain random data
|
RandomError, ## Could not obtain random data
|
||||||
EcdhError, ## ECDH shared secret could not be calculated
|
EcdhError, ## ECDH shared secret could not be calculated
|
||||||
|
BufferOverrun, ## Buffer overrun error
|
||||||
SignatureError, ## Signature could not be obtained
|
SignatureError, ## Signature could not be obtained
|
||||||
EciesError, ## ECIES encryption/decryption error
|
EciesError, ## ECIES encryption/decryption error
|
||||||
InvalidPubKey, ## Invalid public key
|
InvalidPubKey, ## Invalid public key
|
||||||
|
@ -56,56 +61,37 @@ type
|
||||||
IncompleteError ## Data incomplete error
|
IncompleteError ## Data incomplete error
|
||||||
|
|
||||||
Handshake* = object
|
Handshake* = object
|
||||||
version: uint8
|
version*: uint8 ## protocol version
|
||||||
flags: set[HandshakeFlag]
|
flags*: set[HandshakeFlag] ## handshake flags
|
||||||
host*: KeyPair
|
host*: KeyPair ## host keypair
|
||||||
ephemeral*: KeyPair
|
ephemeral*: KeyPair ## ephemeral host keypair
|
||||||
remoteHPubkey*: PublicKey
|
remoteHPubkey*: PublicKey ## remote host public key
|
||||||
remoteEPubkey*: PublicKey
|
remoteEPubkey*: PublicKey ## remote host ephemeral public key
|
||||||
initiatorNonce*: Nonce
|
initiatorNonce*: Nonce ## initiator nonce
|
||||||
responderNonce*: Nonce
|
responderNonce*: Nonce ## responder nonce
|
||||||
|
|
||||||
ConnectionSecret* = object
|
ConnectionSecret* = object
|
||||||
# REVIEW: it would be nice if Nimcrypto defines distinct or
|
|
||||||
# alias types such as `aes256.key` instead of having to spell
|
|
||||||
# out the full array type everywhere.
|
|
||||||
aesKey*: array[aes256.sizeKey, byte]
|
aesKey*: array[aes256.sizeKey, byte]
|
||||||
macKey*: array[KeyLength, byte]
|
macKey*: array[KeyLength, byte]
|
||||||
egressMac*: array[keccak256.sizeDigest, byte]
|
egressMac*: array[keccak256.sizeDigest, byte]
|
||||||
ingressMac*: array[keccak256.sizeDigest, byte]
|
ingressMac*: array[keccak256.sizeDigest, byte]
|
||||||
|
|
||||||
# PlainAuthMessage* = array[PlainAuthMessageLength, byte]
|
|
||||||
# PlainAuthAckMessage* = array[PlainAuthAckMessageLength, byte]
|
|
||||||
AuthMessage* = array[AuthMessageLength, byte]
|
|
||||||
AuthAckMessage* = array[AuthAckMessageLength, byte]
|
|
||||||
|
|
||||||
AuthException* = object of Exception
|
AuthException* = object of Exception
|
||||||
|
|
||||||
|
template toa(a, b, c: untyped): untyped =
|
||||||
|
toOpenArray((a), (b), (b) + (c) - 1)
|
||||||
|
|
||||||
proc sxor[T](a: var openarray[T], b: openarray[T]) =
|
proc sxor[T](a: var openarray[T], b: openarray[T]) =
|
||||||
assert(len(a) == len(b))
|
assert(len(a) == len(b))
|
||||||
for i in 0 ..< len(a):
|
for i in 0 ..< len(a):
|
||||||
a[i] = a[i] xor b[i]
|
a[i] = a[i] xor b[i]
|
||||||
|
|
||||||
proc empty[T](v: openarray[T]): bool =
|
proc newHandshake*(flags: set[HandshakeFlag] = {Initiator},
|
||||||
var r: T
|
version: int = SupportedRlpxVersion): Handshake =
|
||||||
for item in v:
|
## Create new `Handshake` object.
|
||||||
r = r + item
|
result.version = byte(version and 0xFF)
|
||||||
result = (r == T(0))
|
|
||||||
|
|
||||||
proc move[T](dst: var openarray[T], src: openarray[T],
|
|
||||||
dstx: int = 0, dsty: int = -1, srcx: int = 0, srcy: int = -1) =
|
|
||||||
let sx = if srcx < 0: (len(src) + srcx) else: srcx
|
|
||||||
let sy = if srcy < 0: (len(src) + srcy) else: srcy
|
|
||||||
let dx = if dstx < 0: (len(dst) + dstx) else: dstx
|
|
||||||
let dy = if dsty < 0: (len(dst) + dsty) else: dsty
|
|
||||||
assert(sy - sx == dy - dx)
|
|
||||||
moveMem(addr dst[dstx], unsafeAddr src[srcx], (dy - dx + 1) * sizeof(T))
|
|
||||||
|
|
||||||
proc newHandshake*(flags: set[HandshakeFlag] = {Initiator}): Handshake =
|
|
||||||
var p: ptr byte
|
|
||||||
result.flags = flags
|
result.flags = flags
|
||||||
result.ephemeral = newKeyPair()
|
result.ephemeral = newKeyPair()
|
||||||
|
|
||||||
if Initiator in flags:
|
if Initiator in flags:
|
||||||
if randomBytes(result.initiatorNonce) != len(result.initiatorNonce):
|
if randomBytes(result.initiatorNonce) != len(result.initiatorNonce):
|
||||||
raise newException(AuthException, "Could not obtain random data!")
|
raise newException(AuthException, "Could not obtain random data!")
|
||||||
|
@ -113,240 +99,252 @@ proc newHandshake*(flags: set[HandshakeFlag] = {Initiator}): Handshake =
|
||||||
if randomBytes(result.responderNonce) != len(result.responderNonce):
|
if randomBytes(result.responderNonce) != len(result.responderNonce):
|
||||||
raise newException(AuthException, "Could not obtain random data!")
|
raise newException(AuthException, "Could not obtain random data!")
|
||||||
|
|
||||||
proc authMessagePreEIP8*(h: var Handshake,
|
proc authMessagePreEIP8(h: var Handshake,
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
output: var PlainAuthMessage,
|
output: var openarray[byte],
|
||||||
flag: int = 0): AuthStatus =
|
outlen: var int,
|
||||||
## Create plain preEIP8 authentication message.
|
flag: int = 0,
|
||||||
var secret: SharedSecret
|
encrypt: bool = true): AuthStatus =
|
||||||
var signature: Signature
|
## Create plain pre-EIP8 authentication message.
|
||||||
var flagb = byte(flag)
|
var
|
||||||
|
secret: SharedSecret
|
||||||
|
signature: Signature
|
||||||
|
buffer: array[PlainAuthMessageV4Length, byte]
|
||||||
|
flagb: byte
|
||||||
|
header: ptr AuthMessageV4
|
||||||
|
outlen = 0
|
||||||
|
flagb = byte(flag)
|
||||||
|
header = cast[ptr AuthMessageV4](addr buffer[0])
|
||||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||||
return(EcdhError)
|
return(EcdhError)
|
||||||
|
|
||||||
var xornonce = h.initiatorNonce
|
var xornonce = h.initiatorNonce
|
||||||
xornonce.sxor(secret)
|
xornonce.sxor(secret)
|
||||||
|
|
||||||
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
|
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
|
||||||
return(SignatureError)
|
return(SignatureError)
|
||||||
|
|
||||||
h.remoteHPubkey = pubkey
|
h.remoteHPubkey = pubkey
|
||||||
|
header.signature = signature.getRaw()
|
||||||
output.signature = signature.getRaw()
|
header.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw().data).data
|
||||||
output.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw().data).data
|
header.pubkey = cast[PublicKey](h.host.pubkey.getRaw().data)
|
||||||
output.pubkey = cast[PublicKey](h.host.pubkey.getRaw().data)
|
header.nonce = h.initiatorNonce
|
||||||
output.nonce = h.initiatorNonce
|
header.flag = flagb
|
||||||
output.flag = flagb
|
if encrypt:
|
||||||
|
if len(output) < AuthMessageV4Length:
|
||||||
|
return(BufferOverrun)
|
||||||
|
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
|
||||||
|
return(EciesError)
|
||||||
|
outlen = AuthMessageV4Length
|
||||||
|
result = Success
|
||||||
|
else:
|
||||||
|
if len(output) < PlainAuthMessageV4Length:
|
||||||
|
return(BufferOverrun)
|
||||||
|
copyMem(addr output[0], addr buffer[0], PlainAuthMessageV4Length)
|
||||||
|
outlen = PlainAuthMessageV4Length
|
||||||
|
result = Success
|
||||||
|
|
||||||
proc authAckMessagePreEIP8*(h: var Handshake,
|
proc authMessageEIP8(h: var Handshake,
|
||||||
output: var PlainAuthAckMessage,
|
pubkey: PublicKey,
|
||||||
flag: int = 0): AuthStatus =
|
output: var openarray[byte],
|
||||||
output.pubkey = cast[PublicKey](h.ephemeral.pubkey.getRaw().data)
|
outlen: var int,
|
||||||
output.nonce = h.responderNonce
|
flag: int = 0,
|
||||||
output.flag = byte(flag)
|
encrypt: bool = true): AuthStatus =
|
||||||
|
## Create EIP8 authentication message.
|
||||||
proc encryptAuthMessage*(input: ptr byte, inputlen: int,
|
var
|
||||||
output: ptr byte, outputlen: int,
|
secret: SharedSecret
|
||||||
pubkey: PublicKey, shmac: ptr byte = nil,
|
signature: Signature
|
||||||
shlen: int = 0): AuthStatus =
|
buffer: array[PlainAuthMessageMaxEIP8, byte]
|
||||||
result = Success
|
padsize: byte
|
||||||
if eciesEncrypt(input, output, inputlen, outputlen,
|
|
||||||
pubkey, shmac, shlen) != EciesStatus.Success:
|
|
||||||
result = EciesError
|
|
||||||
|
|
||||||
proc encryptAuthMessage*(input: PlainAuthMessage,
|
|
||||||
output: var AuthMessage,
|
|
||||||
pubkey: PublicKey): AuthStatus =
|
|
||||||
result = Success
|
|
||||||
result = encryptAuthMessage(unsafeAddr input[0], PlainAuthMessageLength,
|
|
||||||
addr output[0], AuthMessageLength, pubkey)
|
|
||||||
|
|
||||||
proc decryptAuthMessage*(input: ptr byte, inputlen: int,
|
|
||||||
output: ptr byte, outputlen: int,
|
|
||||||
seckey: PrivateKey, shmac: ptr byte = nil,
|
|
||||||
shlen: int = 0): AuthStatus =
|
|
||||||
result = Success
|
|
||||||
if eciesDecrypt(input, output, inputlen, outputlen,
|
|
||||||
seckey, shmac, shlen) != EciesStatus.Success:
|
|
||||||
result = EciesError
|
|
||||||
|
|
||||||
proc decryptAuthMessage*(input: AuthMessage, output: var PlainAuthMessage,
|
|
||||||
seckey: PrivateKey): AuthStatus =
|
|
||||||
result = decryptAuthMessage(unsafeAddr input[0], AuthMessageLength,
|
|
||||||
addr output[0], PlainAuthMessageLength,
|
|
||||||
seckey)
|
|
||||||
|
|
||||||
proc encryptAuthAckMessage*(input: ptr byte, inputlen: int,
|
|
||||||
output: ptr byte, outputlen: int,
|
|
||||||
pubkey: PublicKey, shmac: ptr byte = nil,
|
|
||||||
shlen: int = 0): AuthStatus =
|
|
||||||
result = Success
|
|
||||||
if eciesEncrypt(input, output, inputlen, outputlen,
|
|
||||||
pubkey, shmac, shlen) != EciesStatus.Success:
|
|
||||||
result = EciesError
|
|
||||||
|
|
||||||
proc encryptAuthAckMessage*(input: PlainAuthAckMessage,
|
|
||||||
output: var AuthAckMessage,
|
|
||||||
pubkey: PublicKey): AuthStatus =
|
|
||||||
result = encryptAuthAckMessage(unsafeAddr input[0], PlainAuthAckMessageLength,
|
|
||||||
addr output[0], AuthAckMessageLength,
|
|
||||||
pubkey)
|
|
||||||
|
|
||||||
proc decryptAuthAckMessage*(input: ptr byte, inputlen: int,
|
|
||||||
output: ptr byte, outputlen: int,
|
|
||||||
seckey: PrivateKey, shmac: ptr byte = nil,
|
|
||||||
shlen: int = 0): AuthStatus =
|
|
||||||
result = Success
|
|
||||||
if eciesDecrypt(input, output, inputlen, outputlen,
|
|
||||||
seckey, shmac, shlen) != EciesStatus.Success:
|
|
||||||
result = EciesError
|
|
||||||
|
|
||||||
proc decryptAuthAckMessage*(input: AuthAckMessage,
|
|
||||||
output: var PlainAuthAckMessage,
|
|
||||||
seckey: PrivateKey): AuthStatus =
|
|
||||||
result = decryptAuthAckMessage(unsafeAddr input[0], AuthAckMessageLength,
|
|
||||||
addr output[0], PlainAuthAckMessageLength,
|
|
||||||
seckey)
|
|
||||||
|
|
||||||
proc decodePlainAuthMessage(h: var Handshake, m: PlainAuthMessage): AuthStatus =
|
|
||||||
var secret: SharedSecret
|
|
||||||
var nonce: array[32, byte]
|
|
||||||
var pubkey: PublicKey
|
|
||||||
|
|
||||||
copyMem(addr nonce[0], unsafeAddr m[161], KeyLength)
|
|
||||||
if recoverPublicKey(unsafeAddr m[97], sizeof(PublicKey),
|
|
||||||
pubkey) != EccStatus.Success:
|
|
||||||
return(InvalidPubKey)
|
|
||||||
|
|
||||||
|
assert(EIP8 in h.flags)
|
||||||
|
outlen = 0
|
||||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||||
return(EcdhError)
|
return(EcdhError)
|
||||||
|
var xornonce = h.initiatorNonce
|
||||||
var xornonce = nonce
|
|
||||||
xornonce.sxor(secret)
|
xornonce.sxor(secret)
|
||||||
|
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
|
||||||
|
return(SignatureError)
|
||||||
|
h.remoteHPubkey = pubkey
|
||||||
|
var payload = rlp.encodeList(signature.getRaw().data,
|
||||||
|
h.host.pubkey.getRaw().data,
|
||||||
|
h.initiatorNonce,
|
||||||
|
[byte(h.version)])
|
||||||
|
assert(len(payload) == PlainAuthMessageEIP8Length)
|
||||||
|
let pencsize = eciesEncryptedLength(len(payload))
|
||||||
|
while true:
|
||||||
|
if randomBytes(addr padsize, 1) != 1:
|
||||||
|
return(RandomError)
|
||||||
|
if int(padsize) > (AuthMessageV4Length - (pencsize + 2)):
|
||||||
|
break
|
||||||
|
# It is possible to make packet size constant by uncommenting this line
|
||||||
|
# padsize = 24
|
||||||
|
var wosize = pencsize + int(padsize)
|
||||||
|
let fullsize = wosize + 2
|
||||||
|
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
|
||||||
|
int(padsize))) != int(padsize):
|
||||||
|
return(RandomError)
|
||||||
|
if encrypt:
|
||||||
|
copyMem(addr buffer[0], payload.baseAddr, len(payload))
|
||||||
|
if len(output) < fullsize:
|
||||||
|
return(BufferOverrun)
|
||||||
|
bigEndian16(addr output, addr wosize)
|
||||||
|
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||||
|
toa(output, 2, wosize), pubkey,
|
||||||
|
toa(output, 0, 2)) != EciesStatus.Success:
|
||||||
|
return(EciesError)
|
||||||
|
outlen = fullsize
|
||||||
|
else:
|
||||||
|
let plainsize = len(payload) + int(padsize)
|
||||||
|
if len(output) < plainsize:
|
||||||
|
return(BufferOverrun)
|
||||||
|
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||||
|
outlen = plainsize
|
||||||
|
result = Success
|
||||||
|
|
||||||
if recoverSignatureKey(unsafeAddr m[0], SignatureLength, addr xornonce[0],
|
proc ackMessagePreEIP8(h: var Handshake,
|
||||||
|
output: var openarray[byte],
|
||||||
|
outlen: var int,
|
||||||
|
flag: int = 0,
|
||||||
|
encrypt: bool = true): AuthStatus =
|
||||||
|
## Create plain pre-EIP8 authentication ack message.
|
||||||
|
var buffer: array[PlainAckMessageV4Length, byte]
|
||||||
|
outlen = 0
|
||||||
|
var header = cast[ptr AckMessageV4](addr buffer[0])
|
||||||
|
header.pubkey = cast[PublicKey](h.ephemeral.pubkey.getRaw().data)
|
||||||
|
header.nonce = h.responderNonce
|
||||||
|
header.flag = byte(flag)
|
||||||
|
if encrypt:
|
||||||
|
if len(output) < AckMessageV4Length:
|
||||||
|
return(BufferOverrun)
|
||||||
|
if eciesEncrypt(buffer, output, h.remoteHPubkey) != EciesStatus.Success:
|
||||||
|
return(EciesError)
|
||||||
|
outlen = AckMessageV4Length
|
||||||
|
else:
|
||||||
|
if len(output) < PlainAckMessageV4Length:
|
||||||
|
return(BufferOverrun)
|
||||||
|
copyMem(addr output[0], addr buffer[0], PlainAckMessageV4Length)
|
||||||
|
outlen = PlainAckMessageV4Length
|
||||||
|
result = Success
|
||||||
|
|
||||||
|
proc ackMessageEIP8(h: var Handshake,
|
||||||
|
output: var openarray[byte],
|
||||||
|
outlen: var int,
|
||||||
|
flag: int = 0,
|
||||||
|
encrypt: bool = true): AuthStatus =
|
||||||
|
## Create EIP8 authentication ack message.
|
||||||
|
var
|
||||||
|
buffer: array[PlainAckMessageMaxEIP8, byte]
|
||||||
|
padsize: byte
|
||||||
|
assert(EIP8 in h.flags)
|
||||||
|
var payload = rlp.encodeList(h.ephemeral.pubkey.getRaw().data,
|
||||||
|
h.responderNonce,
|
||||||
|
[byte(h.version)])
|
||||||
|
assert(len(payload) == PlainAckMessageEIP8Length)
|
||||||
|
outlen = 0
|
||||||
|
let pencsize = eciesEncryptedLength(len(payload))
|
||||||
|
while true:
|
||||||
|
if randomBytes(addr padsize, 1) != 1:
|
||||||
|
return(RandomError)
|
||||||
|
if int(padsize) > (AckMessageV4Length - (pencsize + 2)):
|
||||||
|
break
|
||||||
|
# It is possible to make packet size constant by uncommenting this line
|
||||||
|
# padsize = 0
|
||||||
|
var wosize = pencsize + int(padsize)
|
||||||
|
let fullsize = wosize + 2
|
||||||
|
if int(padsize) > 0:
|
||||||
|
if randomBytes(toa(buffer, PlainAuthMessageEIP8Length,
|
||||||
|
int(padsize))) != int(padsize):
|
||||||
|
return(RandomError)
|
||||||
|
copyMem(addr buffer[0], payload.baseAddr, len(payload))
|
||||||
|
if encrypt:
|
||||||
|
if len(output) < fullsize:
|
||||||
|
return(BufferOverrun)
|
||||||
|
bigEndian16(addr output, addr wosize)
|
||||||
|
if eciesEncrypt(toa(buffer, 0, len(payload) + int(padsize)),
|
||||||
|
toa(output, 2, wosize), h.remoteHPubkey,
|
||||||
|
toa(output, 0, 2)) != EciesStatus.Success:
|
||||||
|
return(EciesError)
|
||||||
|
outlen = fullsize
|
||||||
|
else:
|
||||||
|
let plainsize = len(payload) + int(padsize)
|
||||||
|
if len(output) < plainsize:
|
||||||
|
return(BufferOverrun)
|
||||||
|
copyMem(addr output[0], addr buffer[0], plainsize)
|
||||||
|
outlen = plainsize
|
||||||
|
result = Success
|
||||||
|
|
||||||
|
template authSize*(h: Handshake, encrypt: bool = true): int =
|
||||||
|
## Get number of bytes needed to store AuthMessage.
|
||||||
|
if EIP8 in h.flags:
|
||||||
|
if encrypt: (AuthMessageMaxEIP8) else: (PlainAuthMessageMaxEIP8)
|
||||||
|
else:
|
||||||
|
if encrypt: (AuthMessageV4Length) else: (PlainAuthMessageV4Length)
|
||||||
|
|
||||||
|
template ackSize*(h: Handshake, encrypt: bool = true): int =
|
||||||
|
## Get number of bytes needed to store AckMessage.
|
||||||
|
if EIP8 in h.flags:
|
||||||
|
if encrypt: (AckMessageMaxEIP8) else: (PlainAckMessageMaxEIP8)
|
||||||
|
else:
|
||||||
|
if encrypt: (AckMessageV4Length) else: (PlainAckMessageV4Length)
|
||||||
|
|
||||||
|
proc authMessage*(h: var Handshake, pubkey: PublicKey,
|
||||||
|
output: var openarray[byte],
|
||||||
|
outlen: var int, flag: int = 0,
|
||||||
|
encrypt: bool = true): AuthStatus {.inline.} =
|
||||||
|
## Create new AuthMessage for specified `pubkey` and store it inside
|
||||||
|
## of `output`, size of generated AuthMessage will stored in `outlen`.
|
||||||
|
if EIP8 in h.flags:
|
||||||
|
result = authMessageEIP8(h, pubkey, output, outlen, flag, encrypt)
|
||||||
|
else:
|
||||||
|
result = authMessagePreEIP8(h, pubkey, output, outlen, flag, encrypt)
|
||||||
|
|
||||||
|
proc ackMessage*(h: var Handshake, output: var openarray[byte],
|
||||||
|
outlen: var int, flag: int = 0,
|
||||||
|
encrypt: bool = true): AuthStatus =
|
||||||
|
## Create new AckMessage and store it inside of `output`, size of generated
|
||||||
|
## AckMessage will stored in `outlen`.
|
||||||
|
if EIP8 in h.flags:
|
||||||
|
result = ackMessageEIP8(h, output, outlen, flag, encrypt)
|
||||||
|
else:
|
||||||
|
result = ackMessagePreEIP8(h, output, outlen, flag, encrypt)
|
||||||
|
|
||||||
|
proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
|
||||||
|
## Decodes V4 AuthMessage.
|
||||||
|
var
|
||||||
|
secret: SharedSecret
|
||||||
|
buffer: array[PlainAuthMessageV4Length, byte]
|
||||||
|
pubkey: PublicKey
|
||||||
|
assert(Responder in h.flags)
|
||||||
|
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
|
||||||
|
return(EciesError)
|
||||||
|
var header = cast[ptr AuthMessageV4](addr buffer[0])
|
||||||
|
if recoverPublicKey(header.pubkey.data, pubkey) != EccStatus.Success:
|
||||||
|
return(InvalidPubKey)
|
||||||
|
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||||
|
return(EcdhError)
|
||||||
|
var xornonce = header.nonce
|
||||||
|
xornonce.sxor(secret)
|
||||||
|
if recoverSignatureKey(header.signature.data, xornonce,
|
||||||
h.remoteEPubkey) != EccStatus.Success:
|
h.remoteEPubkey) != EccStatus.Success:
|
||||||
return(SignatureError)
|
return(SignatureError)
|
||||||
|
h.initiatorNonce = header.nonce
|
||||||
h.initiatorNonce = nonce
|
|
||||||
h.remoteHPubkey = pubkey
|
h.remoteHPubkey = pubkey
|
||||||
result = Success
|
result = Success
|
||||||
|
|
||||||
proc decodePlainAuthAckMessage*(h: var Handshake,
|
proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
|
||||||
m: PlainAuthAckMessage): AuthStatus =
|
## Decodes EIP-8 AuthMessage.
|
||||||
if recoverPublicKey(m, h.remoteEPubkey, 0, 63) != EccStatus.Success:
|
|
||||||
return(InvalidPubKey)
|
|
||||||
|
|
||||||
h.responderNonce[0..31] = m[64..95]
|
|
||||||
result = Success
|
|
||||||
|
|
||||||
proc getSecrets*(h: var Handshake,
|
|
||||||
msg: ptr byte, msglen: int,
|
|
||||||
ack: ptr byte, acklen: int,
|
|
||||||
secret: var ConnectionSecret): AuthStatus =
|
|
||||||
|
|
||||||
var
|
|
||||||
shsec: SharedSecret
|
|
||||||
ctx0: keccak256
|
|
||||||
ctx1: keccak256
|
|
||||||
digest: array[keccak256.sizeDigest, byte]
|
|
||||||
mac1: array[keccak256.sizeDigest, byte]
|
|
||||||
mac2: array[keccak256.sizeDigest, byte]
|
|
||||||
xornonce: Nonce
|
|
||||||
|
|
||||||
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
|
|
||||||
if ecdhAgree(h.ephemeral.seckey, h.remoteEPubkey, shsec) != EccStatus.Success:
|
|
||||||
return(EcdhError)
|
|
||||||
|
|
||||||
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
|
|
||||||
ctx0.init()
|
|
||||||
ctx1.init()
|
|
||||||
ctx1.update(addr h.responderNonce[0], uint(len(h.responderNonce)))
|
|
||||||
ctx1.update(addr h.initiatorNonce[0], uint(len(h.initiatorNonce)))
|
|
||||||
digest = ctx1.finish().data
|
|
||||||
|
|
||||||
ctx1.init() # clean keccak256 context
|
|
||||||
ctx0.update(addr shsec[0], uint(sizeof(SharedSecret)))
|
|
||||||
ctx0.update(addr digest[0], uint(keccak256.sizeDigest))
|
|
||||||
digest = ctx0.finish().data
|
|
||||||
|
|
||||||
# aes-secret = keccak(ecdhe-secret || shared-secret)
|
|
||||||
ctx0.init()
|
|
||||||
ctx0.update(addr shsec[0], uint(sizeof(SharedSecret)))
|
|
||||||
ctx0.update(addr digest[0], uint(keccak256.sizeDigest))
|
|
||||||
secret.aesKey = ctx0.finish().data
|
|
||||||
|
|
||||||
# mac-secret = keccak(ecdhe-secret || aes-secret)
|
|
||||||
ctx0.init()
|
|
||||||
ctx0.update(addr shsec[0], uint(sizeof(SharedSecret)))
|
|
||||||
ctx0.update(addr secret.aesKey[0], uint(keccak256.sizeDigest))
|
|
||||||
secret.macKey = ctx0.finish().data
|
|
||||||
|
|
||||||
zeroMem(addr shsec[0], sizeof(SharedSecret)) # clean ecdhe-secret
|
|
||||||
|
|
||||||
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)
|
|
||||||
xornonce = secret.macKey
|
|
||||||
xornonce.sxor(h.responderNonce)
|
|
||||||
ctx0.init()
|
|
||||||
ctx0.update(addr xornonce[0], uint(sizeof(Nonce)))
|
|
||||||
ctx0.update(msg, uint(msglen))
|
|
||||||
mac1 = ctx0.finish().data
|
|
||||||
|
|
||||||
# ingress-mac = keccak256(mac-secret ^ initiator-nonce || auth-recvd-ack)
|
|
||||||
xornonce = secret.macKey
|
|
||||||
xornonce.sxor(h.initiatorNonce)
|
|
||||||
ctx0.init()
|
|
||||||
ctx0.update(addr xornonce[0], uint(sizeof(Nonce)))
|
|
||||||
ctx0.update(ack, uint(acklen))
|
|
||||||
mac2 = ctx0.finish().data
|
|
||||||
|
|
||||||
ctx0.init() # clean keccak256 context
|
|
||||||
zeroMem(addr xornonce[0], sizeof(Nonce)) # clean xornonce
|
|
||||||
|
|
||||||
if Initiator in h.flags:
|
|
||||||
secret.egressMac = mac1
|
|
||||||
secret.ingressMac = mac2
|
|
||||||
else:
|
|
||||||
secret.ingressMac = mac1
|
|
||||||
secret.egressMac = mac2
|
|
||||||
|
|
||||||
zeroMem(addr mac1[0], keccak256.sizeDigest) # clean temporary mac1
|
|
||||||
zeroMem(addr mac2[0], keccak256.sizeDigest) # clean temporary mac2
|
|
||||||
|
|
||||||
result = Success
|
|
||||||
|
|
||||||
proc getSecrets*(h: var Handshake, msg: AuthMessage, ack: AuthAckMessage,
|
|
||||||
secret: var ConnectionSecret): AuthStatus =
|
|
||||||
result = getSecrets(h, unsafeAddr msg[0], AuthMessageLength,
|
|
||||||
unsafeAddr ack[0], AuthAckMessageLength,
|
|
||||||
secret)
|
|
||||||
|
|
||||||
proc decodeAuthEip8Message*(h: var Handshake, msg: ptr byte,
|
|
||||||
msglen: int): AuthStatus =
|
|
||||||
var
|
var
|
||||||
pubkey: PublicKey
|
pubkey: PublicKey
|
||||||
nonce: Nonce
|
nonce: Nonce
|
||||||
size: uint16
|
size: uint16
|
||||||
secret: SharedSecret
|
secret: SharedSecret
|
||||||
if msglen < 2:
|
bigEndian16(addr size, unsafeAddr m[0])
|
||||||
return(InvalidAuth)
|
if 2 + int(size) > len(m):
|
||||||
bigEndian16(addr size, msg)
|
return(IncompleteError)
|
||||||
|
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
||||||
if (2 + int(size)) > msglen:
|
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
||||||
return(InvalidAuth)
|
toa(m, 0, 2)) != EciesStatus.Success:
|
||||||
|
|
||||||
# Maximum `size` value is 65535 bytes
|
|
||||||
var outlen = eciesDecryptedLength(int(size))
|
|
||||||
var output = newSeq[byte](outlen)
|
|
||||||
var input = cast[ptr UncheckedArray[byte]](msg)
|
|
||||||
if decryptAuthMessage(addr input[2], int(size), addr output[0],
|
|
||||||
outlen, h.host.seckey,
|
|
||||||
addr input[0], 2) != Success:
|
|
||||||
return(EciesError)
|
return(EciesError)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
var reader = rlpFromBytes(output.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(InvalidAuth)
|
||||||
if reader.listElem(0).blobLen != SignatureLength:
|
if reader.listElem(0).blobLen != SignatureLength:
|
||||||
|
@ -357,56 +355,42 @@ proc decodeAuthEip8Message*(h: var Handshake, msg: ptr byte,
|
||||||
return(InvalidAuth)
|
return(InvalidAuth)
|
||||||
if reader.listElem(3).blobLen != 1:
|
if reader.listElem(3).blobLen != 1:
|
||||||
return(InvalidAuth)
|
return(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()
|
||||||
|
|
||||||
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
||||||
pubkey) != EccStatus.Success:
|
pubkey) != EccStatus.Success:
|
||||||
return(InvalidPubKey)
|
return(InvalidPubKey)
|
||||||
copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength)
|
copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength)
|
||||||
|
|
||||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||||
return(EcdhError)
|
return(EcdhError)
|
||||||
|
|
||||||
var xornonce = nonce
|
var xornonce = nonce
|
||||||
xornonce.sxor(secret)
|
xornonce.sxor(secret)
|
||||||
|
|
||||||
if recoverSignatureKey(signatureBr.baseAddr, SignatureLength,
|
if recoverSignatureKey(signatureBr.baseAddr, SignatureLength,
|
||||||
addr xornonce[0],
|
addr xornonce[0],
|
||||||
h.remoteEPubkey) != EccStatus.Success:
|
h.remoteEPubkey) != EccStatus.Success:
|
||||||
return(SignatureError)
|
return(SignatureError)
|
||||||
|
|
||||||
h.initiatorNonce = nonce
|
h.initiatorNonce = nonce
|
||||||
h.remoteHPubkey = pubkey
|
h.remoteHPubkey = pubkey
|
||||||
h.version = cast[ptr byte](versionBr.baseAddr)[]
|
h.version = cast[ptr byte](versionBr.baseAddr)[]
|
||||||
result = Success
|
result = Success
|
||||||
except:
|
except:
|
||||||
return(RlpError)
|
result = RlpError
|
||||||
|
|
||||||
proc decodeAuthAckEip8Message(h: var Handshake, msg: ptr byte,
|
proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
|
||||||
msglen: int): AuthStatus =
|
## Decodes EIP-8 AckMessage.
|
||||||
var size: uint16
|
var size: uint16
|
||||||
if msglen < 2:
|
assert(len(m) > 2)
|
||||||
|
bigEndian16(addr size, unsafeAddr m[0])
|
||||||
|
if 2 + int(size) > len(m):
|
||||||
return(IncompleteError)
|
return(IncompleteError)
|
||||||
bigEndian16(addr size, msg)
|
var buffer = newSeq[byte](eciesDecryptedLength(int(size)))
|
||||||
|
if eciesDecrypt(toa(m, 2, int(size)), buffer, h.host.seckey,
|
||||||
if (2 + int(size)) > msglen:
|
toa(m, 0, 2)) != EciesStatus.Success:
|
||||||
return(IncompleteError)
|
|
||||||
|
|
||||||
# Maximum `size` value is 65535 bytes
|
|
||||||
var outlen = eciesDecryptedLength(int(size))
|
|
||||||
var output = newSeq[byte](outlen)
|
|
||||||
var input = cast[ptr UncheckedArray[byte]](msg)
|
|
||||||
if decryptAuthMessage(addr input[2], int(size), addr output[0],
|
|
||||||
outlen, h.host.seckey,
|
|
||||||
addr input[0], 2) != Success:
|
|
||||||
return(EciesError)
|
return(EciesError)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
var reader = rlpFromBytes(output.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(InvalidAck)
|
||||||
if reader.listElem(0).blobLen != PublicKeyLength:
|
if reader.listElem(0).blobLen != PublicKeyLength:
|
||||||
|
@ -418,7 +402,6 @@ proc decodeAuthAckEip8Message(h: var Handshake, msg: ptr byte,
|
||||||
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()
|
||||||
|
|
||||||
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
||||||
h.remoteEPubkey) != EccStatus.Success:
|
h.remoteEPubkey) != EccStatus.Success:
|
||||||
return(InvalidPubKey)
|
return(InvalidPubKey)
|
||||||
|
@ -426,43 +409,128 @@ proc decodeAuthAckEip8Message(h: var Handshake, msg: ptr byte,
|
||||||
h.version = cast[ptr byte](versionBr.baseAddr)[]
|
h.version = cast[ptr byte](versionBr.baseAddr)[]
|
||||||
result = Success
|
result = Success
|
||||||
except:
|
except:
|
||||||
return(RlpError)
|
result = RlpError
|
||||||
|
|
||||||
proc decodeAuthMessage*(h: var Handshake, msg: ptr byte,
|
proc decodeAckMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
|
||||||
msglen: int): AuthStatus =
|
## Decodes V4 AckMessage.
|
||||||
if msglen < AuthMessageLength:
|
var
|
||||||
return(IncompleteError)
|
buffer: array[PlainAckMessageV4Length, byte]
|
||||||
elif msglen == AuthMessageLength:
|
assert(Initiator in h.flags)
|
||||||
# Decoding plain authentication message
|
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
|
||||||
var plain: PlainAuthMessage
|
return(EciesError)
|
||||||
result = decryptAuthMessage(msg, msglen, addr plain[0],
|
var header = cast[ptr AckMessageV4](addr buffer[0])
|
||||||
sizeof(PlainAuthMessage), h.host.seckey)
|
if recoverPublicKey(header.pubkey.data, h.remoteEPubkey) != EccStatus.Success:
|
||||||
if result == Success:
|
return(InvalidPubKey)
|
||||||
result = decodePlainAuthMessage(h, plain)
|
h.responderNonce = header.nonce
|
||||||
|
result = Success
|
||||||
|
|
||||||
|
proc decodeAuthMessage*(h: var Handshake, input: openarray[byte]): AuthStatus =
|
||||||
|
## Decodes AuthMessage from `input`.
|
||||||
|
if len(input) < AuthMessageV4Length:
|
||||||
|
result = IncompleteError
|
||||||
|
elif len(input) == AuthMessageV4Length:
|
||||||
|
let res = h.decodeAuthMessageV4(input)
|
||||||
|
if res != Success:
|
||||||
|
if h.decodeAuthMessageEip8(input) != Success:
|
||||||
|
result = res
|
||||||
|
else:
|
||||||
|
h.flags.incl(EIP8)
|
||||||
|
result = Success
|
||||||
|
else:
|
||||||
|
result = Success
|
||||||
else:
|
else:
|
||||||
# Decoding EIP-8 authentication message
|
result = h.decodeAuthMessageEip8(input)
|
||||||
result = decodeAuthEip8Message(h, msg, msglen)
|
|
||||||
if result == Success:
|
if result == Success:
|
||||||
h.flags.incl(EIP8)
|
h.flags.incl(EIP8)
|
||||||
|
|
||||||
proc decodeAckMessage*(h: var Handshake, msg: ptr byte,
|
proc decodeAckMessage*(h: var Handshake, input: openarray[byte]): AuthStatus =
|
||||||
msglen: int): AuthStatus =
|
## Decodes AckMessage from `input`.
|
||||||
if msglen < AuthAckMessageLength:
|
if len(input) < AckMessageV4Length:
|
||||||
return(IncompleteError)
|
return(IncompleteError)
|
||||||
elif msglen == AuthAckMessageLength:
|
elif len(input) == AckMessageV4Length:
|
||||||
# Decoding plain authentication ACK message
|
let res = h.decodeAckMessageV4(input)
|
||||||
var plain: PlainAuthAckMessage
|
if res != Success:
|
||||||
result = decryptAuthAckMessage(msg, msglen, addr plain[0],
|
if h.decodeAckMessageEip8(input) != Success:
|
||||||
PlainAuthAckMessageLength,
|
result = res
|
||||||
h.host.seckey)
|
else:
|
||||||
if result == Success:
|
h.flags.incl(EIP8)
|
||||||
result = decodePlainAuthAckMessage(h, plain)
|
result = Success
|
||||||
|
else:
|
||||||
|
result = Success
|
||||||
else:
|
else:
|
||||||
# Decoding EIP-8 ACK authentication message
|
result = h.decodeAckMessageEip8(input)
|
||||||
result = decodeAuthAckEip8Message(h, msg, msglen)
|
if result == Success:
|
||||||
|
h.flags.incl(EIP8)
|
||||||
|
|
||||||
proc decodeAuthMessage*(h: var Handshake, msg: openarray[byte]): AuthStatus =
|
proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
||||||
result = decodeAuthMessage(h, unsafeAddr msg[0], len(msg))
|
ackmsg: openarray[byte],
|
||||||
|
secret: var ConnectionSecret): AuthStatus =
|
||||||
|
## Derive secrets from handshake `h` using encrypted AuthMessage `authmsg` and
|
||||||
|
## encrypted AckMessage `ackmsg`.
|
||||||
|
var
|
||||||
|
shsec: SharedSecret
|
||||||
|
ctx0: keccak256
|
||||||
|
ctx1: keccak256
|
||||||
|
mac1: MDigest[256]
|
||||||
|
mac2: MDigest[256]
|
||||||
|
xornonce: Nonce
|
||||||
|
|
||||||
proc decodeAckMessage*(h: var Handshake, msg: openarray[byte]): AuthStatus =
|
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
|
||||||
result = decodeAckMessage(h, unsafeAddr msg[0], len(msg))
|
if ecdhAgree(h.ephemeral.seckey, h.remoteEPubkey, shsec) != EccStatus.Success:
|
||||||
|
return(EcdhError)
|
||||||
|
|
||||||
|
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
|
||||||
|
ctx0.init()
|
||||||
|
ctx1.init()
|
||||||
|
ctx1.update(h.responderNonce)
|
||||||
|
ctx1.update(h.initiatorNonce)
|
||||||
|
mac1 = ctx1.finish()
|
||||||
|
ctx1.clear()
|
||||||
|
ctx0.update(shsec)
|
||||||
|
ctx0.update(mac1.data)
|
||||||
|
mac1 = ctx0.finish()
|
||||||
|
|
||||||
|
# aes-secret = keccak(ecdhe-secret || shared-secret)
|
||||||
|
ctx0.init()
|
||||||
|
ctx0.update(shsec)
|
||||||
|
ctx0.update(mac1.data)
|
||||||
|
mac1 = ctx0.finish()
|
||||||
|
|
||||||
|
# mac-secret = keccak(ecdhe-secret || aes-secret)
|
||||||
|
ctx0.init()
|
||||||
|
ctx0.update(shsec)
|
||||||
|
ctx0.update(mac1.data)
|
||||||
|
secret.aesKey = mac1.data
|
||||||
|
mac1 = ctx0.finish()
|
||||||
|
secret.macKey = mac1.data
|
||||||
|
|
||||||
|
burnMem(shsec)
|
||||||
|
# egress-mac = keccak256(mac-secret ^ recipient-nonce || auth-sent-init)
|
||||||
|
xornonce = mac1.data
|
||||||
|
xornonce.sxor(h.responderNonce)
|
||||||
|
ctx0.init()
|
||||||
|
ctx0.update(xornonce)
|
||||||
|
ctx0.update(authmsg)
|
||||||
|
mac1 = ctx0.finish()
|
||||||
|
|
||||||
|
# ingress-mac = keccak256(mac-secret ^ initiator-nonce || auth-recvd-ack)
|
||||||
|
xornonce = secret.macKey
|
||||||
|
xornonce.sxor(h.initiatorNonce)
|
||||||
|
ctx0.init()
|
||||||
|
ctx0.update(xornonce)
|
||||||
|
ctx0.update(ackmsg)
|
||||||
|
mac2 = ctx0.finish()
|
||||||
|
|
||||||
|
ctx0.init() # clean keccak256 context
|
||||||
|
zeroMem(addr xornonce[0], sizeof(Nonce)) # clean xornonce
|
||||||
|
|
||||||
|
if Initiator in h.flags:
|
||||||
|
secret.egressMac = mac1.data
|
||||||
|
secret.ingressMac = mac2.data
|
||||||
|
else:
|
||||||
|
secret.ingressMac = mac1.data
|
||||||
|
secret.egressMac = mac2.data
|
||||||
|
|
||||||
|
burnMem(mac1)
|
||||||
|
burnMem(mac2)
|
||||||
|
result = Success
|
||||||
|
|
|
@ -213,96 +213,106 @@ proc testE8Value(s: string): string =
|
||||||
suite "Ethereum P2P handshake test suite":
|
suite "Ethereum P2P handshake test suite":
|
||||||
|
|
||||||
block:
|
block:
|
||||||
var initiator: Handshake
|
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||||
var receiver: Handshake
|
result = newHandshake(flags)
|
||||||
var m0, dm0: PlainAuthMessage
|
if Initiator in flags:
|
||||||
var em0: AuthMessage
|
result.host.seckey = getPrivateKey(testValue("initiator_private_key"))
|
||||||
|
result.host.pubkey = result.host.seckey.getPublicKey()
|
||||||
initiator = newHandshake({Initiator})
|
let epki = testValue("initiator_ephemeral_private_key")
|
||||||
receiver = newHandshake({Responder})
|
result.ephemeral.seckey = getPrivateKey(epki)
|
||||||
initiator.host.seckey = getPrivateKey(testValue("initiator_private_key"))
|
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||||
initiator.host.pubkey = initiator.host.seckey.getPublicKey()
|
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||||
var epki = testValue("initiator_ephemeral_private_key")
|
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||||
initiator.ephemeral.seckey = getPrivateKey(epki)
|
elif Responder in flags:
|
||||||
initiator.ephemeral.pubkey = initiator.ephemeral.seckey.getPublicKey()
|
result.host.seckey = getPrivateKey(testValue("receiver_private_key"))
|
||||||
receiver.host.seckey = getPrivateKey(testValue("receiver_private_key"))
|
result.host.pubkey = result.host.seckey.getPublicKey()
|
||||||
receiver.host.pubkey = receiver.host.seckey.getPublicKey()
|
let epkr = testValue("receiver_ephemeral_private_key")
|
||||||
var epkr = testValue("receiver_ephemeral_private_key")
|
result.ephemeral.seckey = getPrivateKey(epkr)
|
||||||
receiver.ephemeral.seckey = getPrivateKey(epkr)
|
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||||
receiver.ephemeral.pubkey = receiver.ephemeral.seckey.getPublicKey()
|
let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
|
||||||
var n0 = fromHex(stripSpaces(testValue("initiator_nonce")))
|
result.responderNonce[0..^1] = nonce[0..^1]
|
||||||
initiator.initiatorNonce[0..^1] = n0[0..^1]
|
|
||||||
var n1 = fromHex(stripSpaces(testValue("receiver_nonce")))
|
|
||||||
receiver.responderNonce[0..^1] = n1[0..^1]
|
|
||||||
|
|
||||||
test "Create plain auth message":
|
test "Create plain auth message":
|
||||||
check authMessage(initiator, receiver.host.pubkey,
|
var initiator = newTestHandshake({Initiator})
|
||||||
m0) == AuthStatus.Success
|
var responder = newTestHandshake({Responder})
|
||||||
var m1 = fromHex(stripSpaces(testValue("auth_plaintext")))
|
var m0 = newSeq[byte](initiator.authSize(false))
|
||||||
var m2 = fromHex(stripSpaces(pyevmAuth))
|
var k0 = 0
|
||||||
check:
|
check:
|
||||||
m0[65..^1] == m1[65..^1]
|
initiator.authMessage(responder.host.pubkey,
|
||||||
m0[0..^1] == m2[0..^1]
|
m0, k0, 0, false) == AuthStatus.Success
|
||||||
|
var expect1 = fromHex(stripSpaces(testValue("auth_plaintext")))
|
||||||
test "Auth message encrypt/decrypt":
|
var expect2 = fromHex(stripSpaces(pyevmAuth))
|
||||||
# Check that encrypting and decrypting the auth_init gets us the orig msg.
|
|
||||||
check:
|
check:
|
||||||
encryptAuthMessage(m0, em0, receiver.host.pubkey) == AuthStatus.Success
|
m0[65..^1] == expect1[65..^1]
|
||||||
decryptAuthMessage(em0, dm0, receiver.host.seckey) == AuthStatus.Success
|
m0[0..^1] == expect2[0..^1]
|
||||||
m0[0..^1] == dm0[0..^1]
|
|
||||||
|
|
||||||
test "Auth message decode":
|
test "Auth message decoding":
|
||||||
# Check that the responder correctly decodes the auth msg.
|
var initiator = newTestHandshake({Initiator})
|
||||||
check receiver.decodeAuthMessage(em0) == AuthStatus.Success
|
var responder = newTestHandshake({Responder})
|
||||||
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
|
var k0 = 0
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
||||||
let remoteHPubkey0 = initiator.host.pubkey.data
|
let remoteHPubkey0 = initiator.host.pubkey.data
|
||||||
check:
|
check:
|
||||||
receiver.initiatorNonce[0..^1] == n0[0..^1]
|
initiator.authMessage(responder.host.pubkey,
|
||||||
receiver.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
m0, k0) == AuthStatus.Success
|
||||||
receiver.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
|
responder.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
||||||
|
responder.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
||||||
|
|
||||||
var k0: PlainAuthAckMessage
|
test "ACK message expectation":
|
||||||
var ek0: AuthAckMessage
|
var initiator = newTestHandshake({Initiator})
|
||||||
|
var responder = newTestHandshake({Responder})
|
||||||
test "Auth ACK expectation":
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
# Check that the auth_ack msg generated by the responder is what we
|
var m1 = newSeq[byte](responder.ackSize(false))
|
||||||
# expect.
|
var k0 = 0
|
||||||
check receiver.authAckMessage(k0) == AuthStatus.Success
|
var k1 = 0
|
||||||
var ac0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
|
var expect0 = fromHex(stripSpaces(testValue("authresp_plaintext")))
|
||||||
check:
|
check:
|
||||||
k0[0..^1] == ac0[0..^1]
|
initiator.authMessage(responder.host.pubkey,
|
||||||
receiver.initiatorNonce[0..^1] == n0[0..^1]
|
m0, k0) == AuthStatus.Success
|
||||||
encryptAuthAckMessage(k0, ek0,
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
receiver.remoteHPubkey) == AuthStatus.Success
|
responder.ackMessage(m1, k1, 0, false) == AuthStatus.Success
|
||||||
|
m1 == expect0
|
||||||
|
responder.initiatorNonce == initiator.initiatorNonce
|
||||||
|
|
||||||
test "Initiator decode Auth ACK message":
|
test "ACK message decoding":
|
||||||
# Check if initiator correctly decodes the auth ack msg.
|
var initiator = newTestHandshake({Initiator})
|
||||||
check initiator.decodeAckMessage(ek0) == AuthStatus.Success
|
var responder = newTestHandshake({Responder})
|
||||||
let remoteEPubkey1 = receiver.ephemeral.pubkey.data
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
let remoteHPubkey1 = receiver.host.pubkey.data
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
|
var k0 = 0
|
||||||
|
var k1 = 0
|
||||||
check:
|
check:
|
||||||
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
initiator.authMessage(responder.host.pubkey,
|
||||||
initiator.remoteHPubkey.data[0..^1] == remoteHPubkey1[0..^1]
|
m0, k0) == AuthStatus.Success
|
||||||
initiator.responderNonce[0..^1] == n1[0..^1]
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
|
responder.ackMessage(m1, k1) == AuthStatus.Success
|
||||||
|
initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
let remoteEPubkey0 = responder.ephemeral.pubkey.data
|
||||||
|
let remoteHPubkey0 = responder.host.pubkey.data
|
||||||
|
check:
|
||||||
|
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
||||||
|
initiator.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
||||||
|
initiator.responderNonce == responder.responderNonce
|
||||||
|
|
||||||
test "Check derived secrets":
|
test "Check derived secrets":
|
||||||
# Check that the secrets derived from ephemeral key agreements match
|
var initiator = newTestHandshake({Initiator})
|
||||||
# the expected values.
|
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 taes = fromHex(stripSpaces(testValue("aes_secret")))
|
var taes = fromHex(stripSpaces(testValue("aes_secret")))
|
||||||
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 csecInitiator: ConnectionSecret
|
||||||
var csecResponder: ConnectionSecret
|
var csecResponder: ConnectionSecret
|
||||||
|
|
||||||
check:
|
check:
|
||||||
initiator.getSecrets(addr authm[0], len(authm), addr ackm[0],
|
responder.decodeAuthMessage(authm) == AuthStatus.Success
|
||||||
len(ackm), csecInitiator) == AuthStatus.Success
|
initiator.decodeAckMessage(ackm) == AuthStatus.Success
|
||||||
receiver.getSecrets(addr authm[0], len(authm), addr ackm[0],
|
initiator.getSecrets(authm, ackm, csecInitiator) == AuthStatus.Success
|
||||||
len(ackm), csecResponder) == 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]
|
||||||
|
@ -322,7 +332,7 @@ suite "Ethereum P2P handshake test suite":
|
||||||
result.ephemeral.seckey = getPrivateKey(esec)
|
result.ephemeral.seckey = getPrivateKey(esec)
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||||
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
||||||
result.initiatorNonce[0..(KeyLength - 1)] = nonce[0..(KeyLength - 1)]
|
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||||
elif Responder in flags:
|
elif Responder in flags:
|
||||||
result.host.seckey = getPrivateKey(testE8Value("receiver_private_key"))
|
result.host.seckey = getPrivateKey(testE8Value("receiver_private_key"))
|
||||||
result.host.pubkey = result.host.seckey.getPublicKey()
|
result.host.pubkey = result.host.seckey.getPublicKey()
|
||||||
|
@ -330,67 +340,53 @@ suite "Ethereum P2P handshake test suite":
|
||||||
result.ephemeral.seckey = getPrivateKey(esec)
|
result.ephemeral.seckey = getPrivateKey(esec)
|
||||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||||
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
|
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
|
||||||
result.responderNonce[0..(KeyLength - 1)] = nonce[0..(KeyLength - 1)]
|
result.responderNonce[0..^1] = nonce[0..^1]
|
||||||
|
|
||||||
test "AUTH/ACK v4 test vectors": # auth/ack v4
|
test "AUTH/ACK v4 test vectors": # auth/ack v4
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var receiver = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
|
|
||||||
# Check that the responder correctly decodes the auth msg.
|
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_v4")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_v4")))
|
||||||
check:
|
check:
|
||||||
receiver.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
receiver.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
||||||
check receiver.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
|
||||||
let remoteHPubkey0 = initiator.host.pubkey.data
|
let remoteHPubkey0 = initiator.host.pubkey.data
|
||||||
check receiver.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
check:
|
||||||
|
responder.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
||||||
# Check that the initiator correctly decodes the auth ack msg.
|
responder.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_v4")))
|
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_v4")))
|
||||||
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
let remoteEPubkey1 = responder.ephemeral.pubkey.data
|
||||||
let remoteEPubkey1 = receiver.ephemeral.pubkey.data
|
|
||||||
check:
|
check:
|
||||||
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
||||||
initiator.responderNonce[0..^1] == receiver.responderNonce[0..^1]
|
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
|
||||||
|
|
||||||
test "AUTH/ACK EIP-8 test vectors":
|
test "AUTH/ACK EIP-8 test vectors":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var receiver = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
|
|
||||||
# Check that the responder correctly decodes the auth msg.
|
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8")))
|
||||||
check:
|
check:
|
||||||
receiver.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
receiver.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
||||||
check receiver.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
check responder.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
||||||
let remoteHPubkey0 = initiator.host.pubkey.data
|
let remoteHPubkey0 = initiator.host.pubkey.data
|
||||||
check receiver.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
check responder.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
||||||
|
|
||||||
# Check that the initiator correctly decodes the auth ack msg.
|
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8")))
|
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8")))
|
||||||
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
let remoteEPubkey1 = responder.ephemeral.pubkey.data
|
||||||
let remoteEPubkey1 = receiver.ephemeral.pubkey.data
|
|
||||||
check:
|
check:
|
||||||
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
||||||
initiator.responderNonce[0..^1] == receiver.responderNonce[0..^1]
|
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
|
||||||
|
|
||||||
# Check that the secrets derived from ephemeral key agreements match
|
|
||||||
# the expected values.
|
|
||||||
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 csecInitiator: ConnectionSecret
|
||||||
var csecResponder: ConnectionSecret
|
var csecResponder: ConnectionSecret
|
||||||
|
|
||||||
check:
|
check:
|
||||||
initiator.getSecrets(addr m0[0], len(m0), addr m1[0],
|
int(initiator.version) == 4
|
||||||
len(m1), csecInitiator) == AuthStatus.Success
|
int(responder.version) == 4
|
||||||
receiver.getSecrets(addr m0[0], len(m0), addr m1[0],
|
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
||||||
len(m1), csecResponder) == 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]
|
||||||
|
@ -398,23 +394,69 @@ suite "Ethereum P2P handshake test suite":
|
||||||
|
|
||||||
test "AUTH/ACK EIP-8 with additional fields test vectors":
|
test "AUTH/ACK EIP-8 with additional fields test vectors":
|
||||||
var initiator = newTestHandshake({Initiator})
|
var initiator = newTestHandshake({Initiator})
|
||||||
var receiver = newTestHandshake({Responder})
|
var responder = newTestHandshake({Responder})
|
||||||
|
|
||||||
# Check that the responder correctly decodes the auth msg.
|
|
||||||
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8_3f")))
|
var m0 = fromHex(stripSpaces(testE8Value("auth_ciphertext_eip8_3f")))
|
||||||
check:
|
check:
|
||||||
receiver.decodeAuthMessage(m0) == AuthStatus.Success
|
responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
receiver.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
responder.initiatorNonce[0..^1] == initiator.initiatorNonce[0..^1]
|
||||||
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
let remoteEPubkey0 = initiator.ephemeral.pubkey.data
|
||||||
check receiver.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
|
||||||
let remoteHPubkey0 = initiator.host.pubkey.data
|
let remoteHPubkey0 = initiator.host.pubkey.data
|
||||||
check receiver.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
check:
|
||||||
|
responder.remoteEPubkey.data[0..^1] == remoteEPubkey0[0..^1]
|
||||||
# Check that the initiator correctly decodes the auth ack msg.
|
responder.remoteHPubkey.data[0..^1] == remoteHPubkey0[0..^1]
|
||||||
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8_3f")))
|
var m1 = fromHex(stripSpaces(testE8Value("authack_ciphertext_eip8_3f")))
|
||||||
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
let remoteEPubkey1 = responder.ephemeral.pubkey.data
|
||||||
let remoteEPubkey1 = receiver.ephemeral.pubkey.data
|
|
||||||
check:
|
check:
|
||||||
|
int(initiator.version) == 57
|
||||||
|
int(responder.version) == 56
|
||||||
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
initiator.remoteEPubkey.data[0..^1] == remoteEPubkey1[0..^1]
|
||||||
initiator.responderNonce[0..^1] == receiver.responderNonce[0..^1]
|
initiator.responderNonce[0..^1] == responder.responderNonce[0..^1]
|
||||||
|
|
||||||
|
test "100 AUTH/ACK EIP-8 handshakes":
|
||||||
|
for i in 1..100:
|
||||||
|
var initiator = newTestHandshake({Initiator, EIP8})
|
||||||
|
var responder = newTestHandshake({Responder})
|
||||||
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
|
var csecInitiator: ConnectionSecret
|
||||||
|
var csecResponder: ConnectionSecret
|
||||||
|
var k0 = 0
|
||||||
|
var k1 = 0
|
||||||
|
check initiator.authMessage(responder.host.pubkey,
|
||||||
|
m0, k0) == AuthStatus.Success
|
||||||
|
m0.setLen(k0)
|
||||||
|
check responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
|
check (EIP8 in responder.flags) == true
|
||||||
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
|
check responder.ackMessage(m1, k1) == AuthStatus.Success
|
||||||
|
m1.setLen(k1)
|
||||||
|
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
check:
|
||||||
|
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
||||||
|
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
|
||||||
|
csecInitiator.aesKey == csecResponder.aesKey
|
||||||
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
|
|
||||||
|
test "100 AUTH/ACK V4 handshakes":
|
||||||
|
for i in 1..100:
|
||||||
|
var initiator = newTestHandshake({Initiator})
|
||||||
|
var responder = newTestHandshake({Responder})
|
||||||
|
var m0 = newSeq[byte](initiator.authSize())
|
||||||
|
var csecInitiator: ConnectionSecret
|
||||||
|
var csecResponder: ConnectionSecret
|
||||||
|
var k0 = 0
|
||||||
|
var k1 = 0
|
||||||
|
check initiator.authMessage(responder.host.pubkey,
|
||||||
|
m0, k0) == AuthStatus.Success
|
||||||
|
m0.setLen(k0)
|
||||||
|
check responder.decodeAuthMessage(m0) == AuthStatus.Success
|
||||||
|
var m1 = newSeq[byte](responder.ackSize())
|
||||||
|
check responder.ackMessage(m1, k1) == AuthStatus.Success
|
||||||
|
m1.setLen(k1)
|
||||||
|
check initiator.decodeAckMessage(m1) == AuthStatus.Success
|
||||||
|
|
||||||
|
check:
|
||||||
|
initiator.getSecrets(m0, m1, csecInitiator) == AuthStatus.Success
|
||||||
|
responder.getSecrets(m0, m1, csecResponder) == AuthStatus.Success
|
||||||
|
csecInitiator.aesKey == csecResponder.aesKey
|
||||||
|
csecInitiator.macKey == csecResponder.macKey
|
||||||
|
|
Loading…
Reference in New Issue