[tor-commits] [onionoo/master] Accept base64-encoded fingerprints in search parameter.

karsten at torproject.org karsten at torproject.org
Sat Nov 15 17:23:18 UTC 2014


commit fe9afb2721e87f08e895b70b72109cbbc253118b
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Fri Oct 3 14:38:30 2014 +0200

    Accept base64-encoded fingerprints in search parameter.
    
    Implements #13135.
---
 .../torproject/onionoo/docs/SummaryDocument.java   |   24 +++++++++--
 .../torproject/onionoo/server/RequestHandler.java  |    6 +++
 .../torproject/onionoo/server/ResourceServlet.java |    3 +-
 .../torproject/onionoo/server/ResponseBuilder.java |    4 +-
 .../torproject/onionoo/ResourceServletTest.java    |   45 ++++++++++++++++++++
 web/protocol.html                                  |   18 ++++----
 6 files changed, 85 insertions(+), 15 deletions(-)

diff --git a/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java b/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
index 8645950..8e325f3 100644
--- a/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
+++ b/src/main/java/org/torproject/onionoo/docs/SummaryDocument.java
@@ -11,6 +11,7 @@ import java.util.TreeSet;
 import java.util.regex.Pattern;
 
 import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.codec.digest.DigestUtils;
 
@@ -34,22 +35,37 @@ public class SummaryDocument extends Document {
       }
     }
     this.f = fingerprint;
+    this.hashedFingerprint = null;
+    this.base64Fingerprint = null;
   }
   public String getFingerprint() {
     return this.f;
   }
 
+  private transient String hashedFingerprint = null;
   public String getHashedFingerprint() {
-    String hashedFingerprint = null;
-    if (this.f != null) {
+    if (this.hashedFingerprint == null && this.f != null) {
       try {
-        hashedFingerprint = DigestUtils.shaHex(Hex.decodeHex(
+        this.hashedFingerprint = DigestUtils.shaHex(Hex.decodeHex(
             this.f.toCharArray())).toUpperCase();
       } catch (DecoderException e) {
         /* Format tested in setFingerprint(). */
       }
     }
-    return hashedFingerprint;
+    return this.hashedFingerprint;
+  }
+
+  private transient String base64Fingerprint = null;
+  public String getBase64Fingerprint() {
+    if (this.base64Fingerprint == null && this.f != null) {
+      try {
+        this.base64Fingerprint = Base64.encodeBase64String(Hex.decodeHex(
+            this.f.toCharArray())).replaceAll("=", "");
+      } catch (DecoderException e) {
+        /* Format tested in setFingerprint(). */
+      }
+    }
+    return this.base64Fingerprint;
   }
 
   private String n;
diff --git a/src/main/java/org/torproject/onionoo/server/RequestHandler.java b/src/main/java/org/torproject/onionoo/server/RequestHandler.java
index 79987fb..28b5531 100644
--- a/src/main/java/org/torproject/onionoo/server/RequestHandler.java
+++ b/src/main/java/org/torproject/onionoo/server/RequestHandler.java
@@ -204,6 +204,8 @@ public class RequestHandler {
         filteredRelays.entrySet()) {
       String fingerprint = e.getKey();
       SummaryDocument entry = e.getValue();
+      String base64Fingerprint = entry.isRelay() ?
+          entry.getBase64Fingerprint() : null;
       boolean lineMatches = false;
       String nickname = entry.getNickname() != null ?
           entry.getNickname().toLowerCase() : "unnamed";
@@ -220,6 +222,10 @@ public class RequestHandler {
       } else if (fingerprint.startsWith(searchTerm.toUpperCase())) {
         /* Non-$-prefixed fingerprint matches. */
         lineMatches = true;
+      } else if (base64Fingerprint != null &&
+          base64Fingerprint.startsWith(searchTerm)) {
+        /* Base64-encoded fingerprint matches. */
+        lineMatches = true;
       } else {
         List<String> addresses = entry.getAddresses();
         for (String address : addresses) {
diff --git a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
index 46617c1..f6f060a 100644
--- a/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
+++ b/src/main/java/org/torproject/onionoo/server/ResourceServlet.java
@@ -363,7 +363,8 @@ public class ResourceServlet extends HttpServlet {
           + "search=([0-9a-zA-Z+/\\.: \\$\\[\\]]+)" // capture parameter
           + "(?:&.*)*"); // skip remaining parameters
   private static Pattern searchParameterPattern =
-      Pattern.compile("^\\$?[0-9a-fA-F]{1,40}$|" /* Fingerprint. */
+      Pattern.compile("^\\$?[0-9a-fA-F]{1,40}$|" /* Hex fingerprint. */
+      + "^[0-9a-zA-Z+/]{1,27}$|" /* Base64 fingerprint. */
       + "^[0-9a-zA-Z\\.]{1,19}$|" /* Nickname or IPv4 address. */
       + "^\\[[0-9a-fA-F:\\.]{1,39}\\]?$|" /* IPv6 address. */
       + "^[a-zA-Z_]+:[0-9a-zA-Z_,-]+$" /* Qualified search term. */);
diff --git a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
index 2660c26..d571505 100644
--- a/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
+++ b/src/main/java/org/torproject/onionoo/server/ResponseBuilder.java
@@ -69,9 +69,9 @@ public class ResponseBuilder {
     return this.charsWritten;
   }
 
-  private static final String PROTOCOL_VERSION = "1.2";
+  private static final String PROTOCOL_VERSION = "2.0";
 
-  private static final String NEXT_MAJOR_VERSION_SCHEDULED = "2014-11-15";
+  private static final String NEXT_MAJOR_VERSION_SCHEDULED = null;
 
   private void writeRelays(List<SummaryDocument> relays, PrintWriter pw) {
     String nextMajorVersionScheduledLine =
diff --git a/src/test/java/org/torproject/onionoo/ResourceServletTest.java b/src/test/java/org/torproject/onionoo/ResourceServletTest.java
index 0a861c3..1631661 100644
--- a/src/test/java/org/torproject/onionoo/ResourceServletTest.java
+++ b/src/test/java/org/torproject/onionoo/ResourceServletTest.java
@@ -560,6 +560,51 @@ public class ResourceServletTest {
   }
 
   @Test()
+  public void testSearchBase64FingerprintAlphaNum() {
+    this.assertSummaryDocument(
+        "/summary?search=AAxfVb1IFLkXzEdL1Tfxo7M8zio", 1,
+        new String[] { "TorkaZ" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64FingerprintSlash() {
+    this.assertSummaryDocument(
+        "/summary?search=ABwTs6Vacbl3ymXshVOdecZTo/w", 1,
+        new String[] { "Ferrari458" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64FingerprintPlus() {
+    this.assertSummaryDocument(
+        "/summary?search=ACXBNsHzqe7+KuP5GPA7+iG1Bws", 1,
+        new String[] { "TimMayTribute" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64FingerprintBridge() {
+    this.assertSummaryDocument(
+        "/summary?search=AACDGyNt/3PUCa0XtA4qcopTmU8", 0, null, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64FingerprintPartial() {
+    this.assertSummaryDocument(
+        "/summary?search=AAx", 1, new String[] { "TorkaZ" }, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64HashedFingerprintTorkaZ() {
+    this.assertSummaryDocument(
+        "/summary?search=WqFMCNYpE+AFeprVhjtFjAzpTO4", 0, null, 0, null);
+  }
+
+  @Test()
+  public void testSearchBase64Fingerprint28() {
+    this.assertErrorStatusCode(
+        "/summary?search=AAAAAAAAAAAA//AAAAAAAAAAAAAA", 400);
+  }
+
+  @Test()
   public void testSearchIp() {
     this.assertSummaryDocument(
         "/summary?search=62.216.201.221", 1, new String[] { "TorkaZ" }, 0,
diff --git a/web/protocol.html b/web/protocol.html
index 8567c90..5b79d92 100644
--- a/web/protocol.html
+++ b/web/protocol.html
@@ -166,7 +166,7 @@ field on September 16, 2014.</li>
 <li><strong>1.2</strong>: Added qualified search terms to "search"
 parameter on October 17, 2014.</li>
 <li><strong>2.0</strong>: Extended search parameter to base64-encoded
-fingerprints, scheduled for November 15, 2015.</li>
+fingerprints on November 15, 2015.</li>
 </ul>
 
 </div> <!-- box -->
@@ -272,20 +272,22 @@ Parameter values are case-insensitive.
 <p>
 Return only (1) relays with the parameter value
 matching (part of a) nickname, (possibly $-prefixed) beginning of a
-fingerprint, or beginning of an IP address, (2) bridges with (part of a)
-nickname or (possibly $-prefixed) beginning of a hashed fingerprint, and
-(3) relays and/or bridges matching a given qualified search term.
+hex-encoded fingerprint, beginning of a base64-encoded fingerprint
+without trailing equal signs, or beginning of an IP address, (2) bridges
+with (part of a) nickname or (possibly $-prefixed) beginning of a hashed
+hex-encoded fingerprint, and (3) relays and/or bridges matching a given
+qualified search term.
 Searches by relay IP address include all known addresses used for onion
 routing and for exiting to the Internet.
 Searches for beginnings of IP addresses are performed on textual
 representations of canonical IP address forms, so that searches using CIDR
 notation or non-canonical forms will return empty results.
-Searches are case-insensitive.
+Searches are case-insensitive, except for base64-encoded fingerprints.
 If multiple search terms are given, separated by spaces, the intersection
 of all relays and bridges matching all search terms will be returned.
-Full fingerprints should always be hashed using SHA-1, regardless of
-searching for a relay or a bridge, in order to not accidentally leak
-non-hashed bridge fingerprints in the URL.
+Complete hex-encoded fingerprints should always be hashed using SHA-1,
+regardless of searching for a relay or a bridge, in order to not
+accidentally leak non-hashed bridge fingerprints in the URL.
 <font color="blue">
 Qualified search terms have the form "key:value" (without double quotes)
 with "key" being one of the parameters listed here except for "search",



More information about the tor-commits mailing list