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