mirror of https://github.com/status-im/nim-eth.git
Attempt to further add the discv5 wire test vectors
This commit is contained in:
parent
5ca1a21ecd
commit
3dd26e8526
|
@ -49,11 +49,12 @@ proc idNonceHash(nonce, ephkey: openarray[byte]): array[32, byte] =
|
|||
ctx.update(ephkey)
|
||||
ctx.finish().data
|
||||
|
||||
proc signIDNonce(c: Codec, idNonce, ephKey: openarray[byte]): SignatureNR =
|
||||
proc signIDNonce*(c: Codec, idNonce, ephKey: openarray[byte]): SignatureNR =
|
||||
if signRawMessage(idNonceHash(idNonce, ephKey), c.privKey, result) != EthKeysStatus.Success:
|
||||
raise newException(EthKeysException, "Could not sign idNonce")
|
||||
|
||||
proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey, challenge: Whoareyou, result: var HandshakeSecrets) =
|
||||
proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey,
|
||||
idNonce: openarray[byte], result: var HandshakeSecrets) =
|
||||
var eph: SharedSecretFull
|
||||
if ecdhAgree(priv, pub, eph) != EthKeysStatus.Success:
|
||||
raise newException(EthKeysException, "ecdhAgree failed")
|
||||
|
@ -68,9 +69,9 @@ proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey, challenge: Who
|
|||
|
||||
static: assert(sizeof(result) == 16 * 3)
|
||||
var res = cast[ptr UncheckedArray[byte]](addr result)
|
||||
hkdf(sha256, eph.data, challenge.idNonce, info, toOpenArray(res, 0, sizeof(result) - 1))
|
||||
hkdf(sha256, eph.data, idNonce, info, toOpenArray(res, 0, sizeof(result) - 1))
|
||||
|
||||
proc encryptGCM(key, nonce, pt, authData: openarray[byte]): seq[byte] =
|
||||
proc encryptGCM*(key, nonce, pt, authData: openarray[byte]): seq[byte] =
|
||||
var ectx: GCM[aes128]
|
||||
ectx.init(key, nonce, authData)
|
||||
result = newSeq[byte](pt.len + gcmTagSize)
|
||||
|
@ -79,7 +80,8 @@ proc encryptGCM(key, nonce, pt, authData: openarray[byte]): seq[byte] =
|
|||
ectx.clear()
|
||||
|
||||
proc makeAuthHeader(c: Codec, toNode: Node, nonce: array[gcmNonceSize, byte],
|
||||
handhsakeSecrets: var HandshakeSecrets, challenge: Whoareyou): seq[byte] =
|
||||
handshakeSecrets: var HandshakeSecrets,
|
||||
challenge: Whoareyou): seq[byte] =
|
||||
var resp = AuthResponse(version: 5)
|
||||
let ln = c.localNode
|
||||
|
||||
|
@ -91,15 +93,16 @@ proc makeAuthHeader(c: Codec, toNode: Node, nonce: array[gcmNonceSize, byte],
|
|||
|
||||
resp.signature = c.signIDNonce(challenge.idNonce, ephPubkey).getRaw
|
||||
|
||||
deriveKeys(ln.id, toNode.id, ephKey, toNode.node.pubKey, challenge, handhsakeSecrets)
|
||||
deriveKeys(ln.id, toNode.id, ephKey, toNode.node.pubKey, challenge.idNonce,
|
||||
handshakeSecrets)
|
||||
|
||||
let respRlp = rlp.encode(resp)
|
||||
|
||||
var zeroNonce: array[gcmNonceSize, byte]
|
||||
let respEnc = encryptGCM(handhsakeSecrets.authRespKey, zeroNonce, respRLP, [])
|
||||
let respEnc = encryptGCM(handshakeSecrets.authRespKey, zeroNonce, respRLP, [])
|
||||
|
||||
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce, scheme: authSchemeName,
|
||||
ephemeralKey: ephPubkey, response: respEnc)
|
||||
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
|
||||
scheme: authSchemeName, ephemeralKey: ephPubkey, response: respEnc)
|
||||
rlp.encode(header)
|
||||
|
||||
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] =
|
||||
|
@ -181,7 +184,8 @@ proc decodePacketBody(typ: byte, body: openarray[byte], res: var Packet): bool =
|
|||
|
||||
return true
|
||||
|
||||
proc decodeAuthResp(c: Codec, fromId: NodeId, head: AuthHeader, challenge: Whoareyou, secrets: var HandshakeSecrets, newNode: var Node): bool =
|
||||
proc decodeAuthResp(c: Codec, fromId: NodeId, head: AuthHeader,
|
||||
challenge: Whoareyou, secrets: var HandshakeSecrets, newNode: var Node): bool =
|
||||
if head.scheme != authSchemeName:
|
||||
warn "Unknown auth scheme"
|
||||
return false
|
||||
|
@ -190,7 +194,8 @@ proc decodeAuthResp(c: Codec, fromId: NodeId, head: AuthHeader, challenge: Whoar
|
|||
if recoverPublicKey(head.ephemeralKey, ephKey) != EthKeysStatus.Success:
|
||||
return false
|
||||
|
||||
deriveKeys(fromId, c.localNode.id, c.privKey, ephKey, challenge, secrets)
|
||||
deriveKeys(fromId, c.localNode.id, c.privKey, ephKey, challenge.idNonce,
|
||||
secrets)
|
||||
|
||||
var zeroNonce: array[gcmNonceSize, byte]
|
||||
let respData = decryptGCM(secrets.authRespKey, zeroNonce, head.response, [])
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import unittest
|
||||
import eth/p2p/discoveryv5/[types, encoding, enr]
|
||||
import eth/rlp, stew/byteutils
|
||||
import
|
||||
unittest, stew/byteutils, stint,
|
||||
eth/[rlp, keys] , eth/p2p/discoveryv5/[types, encoding, enr]
|
||||
|
||||
# According to test vectors:
|
||||
# https://github.com/ethereum/devp2p/blob/master/discv5/discv5-wire-test-vectors.md
|
||||
|
@ -121,3 +121,76 @@ suite "Discovery v5 Protocol Message Encodings":
|
|||
p.enrs = @[e1, e2]
|
||||
var reqId: RequestId = 1
|
||||
check encodePacket(p, reqId).toHex == "04f8f20101f8eef875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
|
||||
|
||||
suite "Discovery v5 Cryptographic Primitives":
|
||||
test "ECDH":
|
||||
const
|
||||
# input
|
||||
publicKey = "0x9961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231503061ac4aaee666073d7e5bc2c80c3f5c5b500c1cb5fd0a76abbb6b675ad157"
|
||||
secretKey = "0xfb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
|
||||
# expected output
|
||||
sharedSecret = "0x033b11a2a1f214567e1537ce5e509ffd9b21373247f2a3ff6841f4976f53165e7e"
|
||||
|
||||
let
|
||||
pub = initPublicKey(publicKey)
|
||||
priv = initPrivateKey(secretKey)
|
||||
var eph: SharedSecretFull
|
||||
|
||||
check:
|
||||
ecdhAgree(priv, pub, eph) == EthKeysStatus.Success
|
||||
eph.data == hexToSeqByte(sharedSecret)
|
||||
|
||||
test "Key Derivation":
|
||||
const
|
||||
# input
|
||||
secretKey = "0x02a77e3aa0c144ae7c0a3af73692b7d6e5b7a2fdc0eda16e8d5e6cb0d08e88dd04"
|
||||
nodeIdA = "0xa448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
|
||||
nodeIdB = "0x885bba8dfeddd49855459df852ad5b63d13a3fae593f3f9fa7e317fd43651409"
|
||||
idNonce = "0x0101010101010101010101010101010101010101010101010101010101010101"
|
||||
# expected output
|
||||
initiatorKey = "0x238d8b50e4363cf603a48c6cc3542967"
|
||||
recipientKey = "0xbebc0183484f7e7ca2ac32e3d72c8891"
|
||||
authRespKey = "0xe987ad9e414d5b4f9bfe4ff1e52f2fae"
|
||||
|
||||
# Code doesn't allow to start from shared `secretKey`, but only from the
|
||||
# public and private key. Would require pulling `ecdhAgree` out of
|
||||
# `deriveKeys`
|
||||
skip()
|
||||
|
||||
test "Nonce Signing":
|
||||
const
|
||||
# input
|
||||
idNonce = "0xa77e3aa0c144ae7c0a3af73692b7d6e5b7a2fdc0eda16e8d5e6cb0d08e88dd04"
|
||||
ephemeralKey = "0x9961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231503061ac4aaee666073d7e5bc2c80c3f5c5b500c1cb5fd0a76abbb6b675ad157"
|
||||
localSecretKey = "0xfb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
|
||||
# expected output
|
||||
idNonceSig = "0xc5036e702a79902ad8aa147dabfe3958b523fd6fa36cc78e2889b912d682d8d35fdea142e141f690736d86f50b39746ba2d2fc510b46f82ee08f08fd55d133a4"
|
||||
|
||||
let
|
||||
c = Codec(privKey: initPrivateKey(localSecretKey))
|
||||
signature = signIDNonce(c, hexToByteArray[32](idNonce),
|
||||
hexToByteArray[64](ephemeralKey))
|
||||
check signature.getRaw() == hexToByteArray[64](idNonceSig)
|
||||
|
||||
test "Encryption/Decryption":
|
||||
const
|
||||
# input
|
||||
encryptionKey = "0x9f2d77db7004bf8a1a85107ac686990b"
|
||||
nonce = "0x27b5af763c446acd2749fe8e"
|
||||
pt = "0x01c20101"
|
||||
ad = "0x93a7400fa0d6a694ebc24d5cf570f65d04215b6ac00757875e3f3a5f42107903"
|
||||
# expected output
|
||||
messageCiphertext = "0xa5d12a2d94b8ccb3ba55558229867dc13bfa3648"
|
||||
|
||||
let encrypted = encryptGCM(hexToByteArray[16](encryptionKey),
|
||||
hexToByteArray[12](nonce),
|
||||
hexToSeqByte(pt),
|
||||
hexToByteArray[32](ad))
|
||||
check encrypted == hexToSeqByte(messageCiphertext)
|
||||
|
||||
test "Authentication Header and Encrypted Message Generation":
|
||||
# Can't work directly with the provided shared secret as keys are derived
|
||||
# inside makeAuthHeader, and passed on one call up.
|
||||
# The encryption of the auth-resp-pt uses one of these keys, as does the
|
||||
# encryption of the message itself. So the whole test depends on this.
|
||||
skip()
|
||||
|
|
Loading…
Reference in New Issue