You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuweni.apache.org by to...@apache.org on 2020/05/04 07:59:59 UTC
[incubator-tuweni] branch master updated: allow reading public key
from enrtree entries, and use them to check the signature
This is an automated email from the ASF dual-hosted git repository.
toulmean pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
The following commit(s) were added to refs/heads/master by this push:
new 047222f allow reading public key from enrtree entries, and use them to check the signature
047222f is described below
commit 047222f0a49746f7c5a166633ed77b48285e68a0
Author: Antoine Toulme <an...@lunar-ocean.com>
AuthorDate: Mon May 4 00:59:40 2020 -0700
allow reading public key from enrtree entries, and use them to check the signature
---
dependency-versions.gradle | 2 +-
.../org/apache/tuweni/discovery/DNSResolverTest.kt | 8 ++-
.../kotlin/org/apache/tuweni/discovery/DNSEntry.kt | 46 +++++++++------
.../org/apache/tuweni/discovery/DNSResolver.kt | 65 +++++++++-------------
4 files changed, 62 insertions(+), 59 deletions(-)
diff --git a/dependency-versions.gradle b/dependency-versions.gradle
index 29299d7..bbe202d 100644
--- a/dependency-versions.gradle
+++ b/dependency-versions.gradle
@@ -26,7 +26,7 @@ dependencyManagement {
dependency('com.jolbox:bonecp:0.8.0.RELEASE')
dependency('com.squareup.okhttp3:okhttp:3.12.0')
dependency('com.winterbe:expekt:0.5.0')
- dependency('dnsjava:dnsjava:2.1.8')
+ dependency('dnsjava:dnsjava:3.0.2')
dependency('info.picocli:picocli:4.0.0-alpha-2')
dependency('io.lettuce:lettuce-core:5.1.3.RELEASE')
dependency('io.vertx:vertx-core:3.6.2')
diff --git a/dns-discovery/src/integrationTest/kotlin/org/apache/tuweni/discovery/DNSResolverTest.kt b/dns-discovery/src/integrationTest/kotlin/org/apache/tuweni/discovery/DNSResolverTest.kt
index a5b4bf8..fde7a9e 100644
--- a/dns-discovery/src/integrationTest/kotlin/org/apache/tuweni/discovery/DNSResolverTest.kt
+++ b/dns-discovery/src/integrationTest/kotlin/org/apache/tuweni/discovery/DNSResolverTest.kt
@@ -44,8 +44,10 @@ class DNSResolverTest {
}
}
- resolver.visitTree("all.goerli.ethdisco.net", visitor)
+ resolver.visitTree("enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.goerli.ethdisco.net",
+ visitor)
assertTrue(nodes.size > 0)
+ println(nodes.size)
}
@Test
@@ -59,7 +61,9 @@ class DNSResolverTest {
}
}
- resolver.visitTree("all.mainnet.ethdisco.net", visitor)
+ resolver.visitTree("enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net",
+ visitor)
assertTrue(nodes.size > 0)
+ println(nodes.size)
}
}
diff --git a/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSEntry.kt b/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSEntry.kt
index 6a4a78a..5e3a6bf 100644
--- a/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSEntry.kt
+++ b/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSEntry.kt
@@ -22,6 +22,7 @@ import org.apache.tuweni.devp2p.EthereumNodeRecord
import org.apache.tuweni.io.Base32
import org.apache.tuweni.io.Base64URLSafe
import org.apache.tuweni.rlp.InvalidRLPEncodingException
+import java.net.URI
/**
* Intermediate format to write DNS entries
@@ -41,16 +42,16 @@ public interface DNSEntry {
if (record[0] == '"') {
record = record.substring(1, record.length - 1)
}
- if (record.startsWith("enrtree-root")) {
+ if (record.startsWith("enrtree-root:")) {
return ENRTreeRoot(readKV(record))
- } else if (record.startsWith("enrtree-branch")) {
+ } else if (record.startsWith("enrtree-branch:")) {
return ENRTree(record.substring("enrtree-branch:".length))
} else if (record.startsWith("enr:")) {
return ENRNode(readKV(record))
- } else if (record.startsWith("enrtree-link:")) {
- return ENRTreeLink(readKV(record))
+ } else if (record.startsWith("enrtree:")) {
+ return ENRTreeLink(record)
} else {
- throw InvalidEntryException("$serialized should contain enrtree-branch, enr, enrtree-root or enrtree-link")
+ throw InvalidEntryException("$serialized should contain enrtree-branch, enr, enrtree-root or enrtree")
}
}
@@ -100,9 +101,8 @@ class ENRTreeRoot(attrs: Map<String, String>) : DNSEntry {
val version: String
val seq: Int
val sig: SECP256K1.Signature
- val hash: Bytes
- val encodedHash: String
- val link: String
+ val enrRoot: String
+ val linkRoot: String
init {
if (attrs["enrtree-root"] == null || attrs["seq"] == null || attrs["sig"] == null || attrs["e"] == null ||
attrs["l"] == null) {
@@ -114,15 +114,17 @@ class ENRTreeRoot(attrs: Map<String, String>) : DNSEntry {
val sigBytes = Base64URLSafe.decode(attrs["sig"]!!)
sig = SECP256K1.Signature.fromBytes(Bytes.concatenate(sigBytes,
Bytes.wrap(ByteArray(Math.max(0, 65 - sigBytes.size())))))
- encodedHash = attrs["e"]!!
- hash = Base32.decode(encodedHash)
- link = attrs["l"]!!
+ enrRoot = attrs["e"]!!
+ linkRoot = attrs["l"]!!
}
override fun toString(): String {
- val encodedHash = Base32.encode(hash)
- return "enrtree-root:$version e=${encodedHash.subSequence(0, encodedHash.indexOf("="))} " +
- "l=$link seq=$seq sig=${Base64URLSafe.encode(sig.bytes())}"
+ return "enrtree-root:$version e=$enrRoot " +
+ "l=$linkRoot seq=$seq sig=${Base64URLSafe.encode(sig.bytes())}"
+ }
+
+ fun signedContent(): String {
+ return "enrtree-root:$version e=$enrRoot l=$linkRoot seq=$seq"
}
}
@@ -138,16 +140,24 @@ class ENRTree(entriesAsString: String) : DNSEntry {
}
}
-class ENRTreeLink(attrs: Map<String, String>) : DNSEntry {
+class ENRTreeLink(url: String) : DNSEntry {
val domainName: String
+ val pubKey: String
init {
- val attr = attrs["enrtree-link"] ?: throw InvalidEntryException("Missing attributes on enrtree-link entry")
- domainName = attr
+ val uri = URI.create(url)
+ domainName = uri.host
+ pubKey = uri.userInfo
+ }
+
+ fun publicKey(): SECP256K1.PublicKey {
+ val keyBytes = Base32.decodeBytes(pubKey)
+ val ecPoint = SECP256K1.Parameters.CURVE.getCurve().decodePoint(keyBytes)
+ return SECP256K1.PublicKey.fromBytes(Bytes.wrap(ecPoint.getEncoded(false)).slice(1))
}
override fun toString(): String {
- return "enrtree-link=$domainName"
+ return "enrtree://$pubKey@$domainName"
}
}
diff --git a/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSResolver.kt b/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSResolver.kt
index 4f6ebdd..db0d38d 100644
--- a/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSResolver.kt
+++ b/dns-discovery/src/main/kotlin/org/apache/tuweni/discovery/DNSResolver.kt
@@ -32,6 +32,7 @@ package org.apache.tuweni.discovery
* limitations under the License.
*/
+import org.apache.tuweni.crypto.Hash
import org.apache.tuweni.crypto.SECP256K1
import org.apache.tuweni.devp2p.EthereumNodeRecord
import org.slf4j.LoggerFactory
@@ -67,22 +68,18 @@ class DNSResolver(private val dnsServer: String? = null, private val signingKey:
return resolveRecordRaw(domainName)?.let { DNSEntry.readDNSEntry(it) }
}
- private fun checkSignature(entry: ENRTreeRoot, sig: SECP256K1.Signature): Boolean {
- if (signingKey == null) {
- return true
- }
- TODO("not implemented, $entry $sig")
- // val recovered = SECP256K1.PublicKey.recoverFromSignature(entry.signedContent(), sig)
- // return signingKey.equals(recovered)
+ private fun checkSignature(root: ENRTreeRoot, pubKey: SECP256K1.PublicKey, sig: SECP256K1.Signature): Boolean {
+ val hash = Hash.keccak256(root.signedContent().toByteArray())
+ return SECP256K1.verifyHashed(hash, sig, pubKey)
}
/**
* Convenience method to read all ENRs, from a top-level record.
*
- * @param domainName the DNS domain name to start with
+ * @param enrLink the ENR link to start with, of the form enrtree://PUBKEY@domain
* @return all ENRs collected
*/
- public fun collectAll(domainName: String): List<EthereumNodeRecord> {
+ public fun collectAll(enrLink: String): List<EthereumNodeRecord> {
val nodes = mutableListOf<EthereumNodeRecord>()
val visitor = object : DNSVisitor {
override fun visit(enr: EthereumNodeRecord): Boolean {
@@ -90,45 +87,37 @@ class DNSResolver(private val dnsServer: String? = null, private val signingKey:
return true
}
}
- visitTree(domainName, visitor)
+ visitTree(enrLink, visitor)
return nodes
}
/**
* Reads a complete tree of record, starting with the top-level record.
- * @param domainName the DNS domain name to start with
+ * @param enrLink the ENR link to start with
+ * @param visitor the visitor that will look at each record
+ */
+ public fun visitTree(enrLink: String, visitor: DNSVisitor) {
+ val link = ENRTreeLink(enrLink)
+ visitTree(link, visitor)
+ }
+
+ /**
+ * Reads a complete tree of record, starting with the top-level record.
+ * @param link the ENR link to start with
* @param visitor the visitor that will look at each record
*/
- public fun visitTree(domainName: String, visitor: DNSVisitor) {
- val entry = resolveRecord(domainName)
+ public fun visitTree(link: ENRTreeLink, visitor: DNSVisitor) {
+ val entry = resolveRecord(link.domainName)
if (entry !is ENRTreeRoot) {
- logger.debug("Root domain name $domainName is not an ENR tree root")
+ logger.debug("Root entry $entry is not an ENR tree root")
return
}
- val linkedStr = resolveRecordRaw("${entry.encodedHash}.$domainName")
- if (linkedStr == null) {
- logger.debug("No linked record under ${entry.encodedHash}.$domainName")
+ if (!checkSignature(entry, link.publicKey(), entry.sig)) {
+ logger.debug("ENR tree root ${link.domainName} failed signature check")
return
}
- if (!checkSignature(entry, entry.sig)) {
- logger.debug("ENR tree root $domainName failed signature check")
- return
- }
-
- val linkedEntry = DNSEntry.readDNSEntry(linkedStr)
- // TODO check hash matches
- if (linkedEntry is ENRNode) {
- visitor.visit(linkedEntry.nodeRecord)
- return
- } else if (linkedEntry is ENRTree) {
- for (e in linkedEntry.entries) {
- if (!internalVisit(e, domainName, visitor)) {
- break
- }
- }
- } else {
- logger.debug("No linked record")
- }
+ internalVisit(entry.enrRoot, link.domainName, visitor)
+ internalVisit(entry.linkRoot, link.domainName, visitor)
}
/**
@@ -148,7 +137,7 @@ class DNSResolver(private val dnsServer: String? = null, private val signingKey:
val rec = Record.newRecord(name, type, DClass.IN)
val query = Message.newQuery(rec)
val response = resolver.send(query)
- val records = response.getSectionArray(Section.ANSWER)
+ val records = response.getSection(Section.ANSWER)
if (records.isNotEmpty()) {
return records[0].rdataToString()
@@ -179,7 +168,7 @@ class DNSResolver(private val dnsServer: String? = null, private val signingKey:
}
}
} else if (entry is ENRTreeLink) {
- visitTree(entry.domainName, visitor)
+ visitTree(entry, visitor)
} else {
logger.debug("Unsupported type of node $entry")
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tuweni.apache.org
For additional commands, e-mail: commits-help@tuweni.apache.org