[tor-commits] [metrics-lib/master] Parse recently added lines.
karsten at torproject.org
karsten at torproject.org
Mon Mar 2 15:19:16 UTC 2020
commit 81570c4dbc097089f367c104c7ef5a77bee29763
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Wed Feb 26 21:31:08 2020 +0100
Parse recently added lines.
- Compute bandwidth file digests.
- Parse bandwidth file header and bandwidth file digest in votes.
- Parse bridge distribution requests in bridge server descriptors.
- Parse authority fingerprint in bridge network statuses.
Implements #33206.
---
CHANGELOG.md | 8 ++++-
.../org/torproject/descriptor/BandwidthFile.java | 9 +++++
.../torproject/descriptor/BridgeNetworkStatus.java | 8 +++++
.../descriptor/RelayNetworkStatusVote.java | 20 +++++++++++
.../torproject/descriptor/ServerDescriptor.java | 8 +++++
.../descriptor/impl/BandwidthFileImpl.java | 6 ++++
.../descriptor/impl/BridgeNetworkStatusImpl.java | 26 ++++++++++++++
.../torproject/descriptor/impl/DescriptorImpl.java | 9 ++++-
.../java/org/torproject/descriptor/impl/Key.java | 3 ++
.../impl/RelayNetworkStatusVoteImpl.java | 41 +++++++++++++++++++++-
.../descriptor/impl/ServerDescriptorImpl.java | 20 ++++++++++-
.../descriptor/impl/BridgeNetworkStatusTest.java | 31 ++++++++++++++++
.../descriptor/impl/ServerDescriptorImplTest.java | 30 ++++++++++++++++
13 files changed, 215 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d58e92b..068c0b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,10 @@
-# Changes in version 2.1?.? - 2020-0?-??
+# Changes in version 2.11.0 - 2020-0?-??
+
+ * Medium changes
+ - Compute bandwidth file digests.
+ - Parse bandwidth file header and bandwidth file digest in votes.
+ - Parse bridge distribution requests in bridge server descriptors.
+ - Parse authority fingerprint in bridge network statuses.
* Minor changes
- Avoid invoking overridable methods from constructors.
diff --git a/src/main/java/org/torproject/descriptor/BandwidthFile.java b/src/main/java/org/torproject/descriptor/BandwidthFile.java
index bbc4056..25d738e 100644
--- a/src/main/java/org/torproject/descriptor/BandwidthFile.java
+++ b/src/main/java/org/torproject/descriptor/BandwidthFile.java
@@ -18,6 +18,15 @@ import java.util.Optional;
public interface BandwidthFile extends Descriptor {
/**
+ * Return the SHA-256 bandwidth file digest, encoded as 43 base64 characters
+ * without padding characters, that is used to reference this bandwidth file
+ * from a vote.
+ *
+ * @since 2.11.0
+ */
+ String digestSha256Base64();
+
+ /**
* Time of the most recent generator bandwidth result.
*
* @since 2.6.0
diff --git a/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
index 9c78b77..6ae4751 100644
--- a/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
+++ b/src/main/java/org/torproject/descriptor/BridgeNetworkStatus.java
@@ -117,6 +117,14 @@ public interface BridgeNetworkStatus extends Descriptor {
int getIgnoringAdvertisedBws();
/**
+ * Return a SHA-1 digest of the bridge authority's identity key, encoded as 40
+ * upper-case hexadecimal characters.
+ *
+ * @since 2.11.0
+ */
+ String getFingerprint();
+
+ /**
* Return status entries for each contained bridge, with map keys being
* SHA-1 digests of SHA-1 digest of the bridges' public identity keys,
* encoded as 40 upper-case hexadecimal characters.
diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
index 5ed31b3..a447a7a 100644
--- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -361,6 +361,26 @@ public interface RelayNetworkStatusVote extends Descriptor {
String getSharedRandCurrentValue();
/**
+ * Return the headers from the bandwidth file used to generate this vote, or
+ * null if the authority producing this vote is not configured with a
+ * bandwidth file or does not include the headers of the configured bandwidth
+ * file in its vote.
+ *
+ * @since 2.11.0
+ */
+ SortedMap<String, String> getBandwidthFileHeaders();
+
+ /**
+ * Return the SHA256 digest of the bandwidth file, encoded as 43 base64
+ * characters without padding characters, or null if the authority producing
+ * this vote is not configured with a bandwidth file or does not include the
+ * SHA256 digest of the configured bandwidth file in its vote.
+ *
+ * @since 2.11.0
+ */
+ String getBandwidthFileDigestSha256Base64();
+
+ /**
* Return the version of the directory key certificate used by this
* authority, which must be 3 or higher.
*
diff --git a/src/main/java/org/torproject/descriptor/ServerDescriptor.java b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
index 2e0ecbd..ed585b0 100644
--- a/src/main/java/org/torproject/descriptor/ServerDescriptor.java
+++ b/src/main/java/org/torproject/descriptor/ServerDescriptor.java
@@ -245,6 +245,14 @@ public interface ServerDescriptor extends Descriptor {
String getContact();
/**
+ * Return the method how a bridge requests to be distributed by BridgeDB, or
+ * {@code null} if no such request is contained in the descriptor.
+ *
+ * @since 2.11.0
+ */
+ String getBridgeDistributionRequest();
+
+ /**
* Return nicknames, $-prefixed identity fingerprints, or tuples of the
* format {@code $fingerprint=nickname} or {@code $fingerprint~nickname}
* of servers contained in this server's family, or null if the
diff --git a/src/main/java/org/torproject/descriptor/impl/BandwidthFileImpl.java b/src/main/java/org/torproject/descriptor/impl/BandwidthFileImpl.java
index f9198a4..94df839 100644
--- a/src/main/java/org/torproject/descriptor/impl/BandwidthFileImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BandwidthFileImpl.java
@@ -66,6 +66,7 @@ public class BandwidthFileImpl extends DescriptorImpl implements BandwidthFile {
this.parseRelayLine(line);
}
}
+ this.calculateDigestSha256Base64();
}
private void parseTimestampLine(String line) throws DescriptorParseException {
@@ -257,6 +258,11 @@ public class BandwidthFileImpl extends DescriptorImpl implements BandwidthFile {
additionalKeyValues.isEmpty() ? null : additionalKeyValues));
}
+ @Override
+ public String digestSha256Base64() {
+ return this.getDigestSha256Base64();
+ }
+
private LocalDateTime timestamp;
@Override
diff --git a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
index b9241a8..68b954f 100644
--- a/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/BridgeNetworkStatusImpl.java
@@ -10,8 +10,10 @@ import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.EnumSet;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TimeZone;
@@ -24,6 +26,10 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
throws DescriptorParseException {
super(rawDescriptorBytes, offsetAndLength, descriptorFile, false);
this.splitAndParseParts(false);
+ Set<Key> atMostOnceKeys = EnumSet.of(Key.PUBLISHED, Key.FLAG_THRESHOLDS,
+ Key.FINGERPRINT);
+ this.checkAtMostOnceKeys(atMostOnceKeys);
+ this.clearParsedKeys();
this.setPublishedMillisFromFileName(fileName);
}
@@ -85,6 +91,9 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
case FLAG_THRESHOLDS:
this.parseFlagThresholdsLine(line, parts);
break;
+ case FINGERPRINT:
+ this.parseFingerprintLine(line, parts);
+ break;
default:
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<>();
@@ -151,6 +160,16 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
}
}
+ private void parseFingerprintLine(String line,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line
+ + "' in bridge network status.");
+ }
+ this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+ partsNoOpt[1]);
+ }
+
protected void parseDirSource(int offset, int length)
throws DescriptorParseException {
throw new DescriptorParseException("No directory source expected in "
@@ -238,5 +257,12 @@ public class BridgeNetworkStatusImpl extends NetworkStatusImpl
public int getIgnoringAdvertisedBws() {
return this.ignoringAdvertisedBws;
}
+
+ private String fingerprint;
+
+ @Override
+ public String getFingerprint() {
+ return this.fingerprint;
+ }
}
diff --git a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
index 4b380bc..f963a53 100644
--- a/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -408,7 +408,10 @@ public abstract class DescriptorImpl implements Descriptor {
if (null == this.digestSha256Base64) {
String ascii = new String(this.rawDescriptorBytes, this.offset,
this.length, StandardCharsets.US_ASCII);
- int start = ascii.indexOf(startToken);
+ int start = 0;
+ if (null != startToken) {
+ start = ascii.indexOf(startToken);
+ }
int end = -1;
if (null == endToken) {
end = ascii.length();
@@ -431,6 +434,10 @@ public abstract class DescriptorImpl implements Descriptor {
this.calculateDigestSha256Base64(startToken, null);
}
+ protected void calculateDigestSha256Base64() throws DescriptorParseException {
+ this.calculateDigestSha256Base64(null, null);
+ }
+
public String getDigestSha256Base64() {
return this.digestSha256Base64;
}
diff --git a/src/main/java/org/torproject/descriptor/impl/Key.java b/src/main/java/org/torproject/descriptor/impl/Key.java
index 10839dd..a6202e4 100644
--- a/src/main/java/org/torproject/descriptor/impl/Key.java
+++ b/src/main/java/org/torproject/descriptor/impl/Key.java
@@ -17,10 +17,13 @@ public enum Key {
ACCEPT("accept"),
ALLOW_SINGLE_HOP_EXITS("allow-single-hop-exits"),
BANDWIDTH("bandwidth"),
+ BANDWIDTH_FILE_DIGEST("bandwidth-file-digest"),
+ BANDWIDTH_FILE_HEADERS("bandwidth-file-headers"),
BANDWIDTH_WEIGHTS("bandwidth-weights"),
BRIDGEDB_METRICS_END("bridgedb-metrics-end"),
BRIDGEDB_METRICS_VERSION("bridgedb-metrics-version"),
BRIDGEDB_METRIC_COUNT("bridgedb-metric-count"),
+ BRIDGE_DISTRIBUTION_REQUEST("bridge-distribution-request"),
BRIDGE_IPS("bridge-ips"),
BRIDGE_IP_TRANSPORTS("bridge-ip-transports"),
BRIDGE_IP_VERSIONS("bridge-ip-versions"),
diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
index 7e8d816..e3fab9e 100644
--- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java
@@ -41,7 +41,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
Key.REQUIRED_CLIENT_PROTOCOLS, Key.REQUIRED_RELAY_PROTOCOLS,
Key.FLAG_THRESHOLDS, Key.PARAMS, Key.CONTACT,
Key.SHARED_RAND_PARTICIPATE, Key.SHARED_RAND_PREVIOUS_VALUE,
- Key.SHARED_RAND_CURRENT_VALUE, Key.LEGACY_KEY, Key.DIR_KEY_CROSSCERT,
+ Key.SHARED_RAND_CURRENT_VALUE, Key.BANDWIDTH_FILE_HEADERS,
+ Key.BANDWIDTH_FILE_DIGEST, Key.LEGACY_KEY, Key.DIR_KEY_CROSSCERT,
Key.DIR_ADDRESS, Key.DIRECTORY_FOOTER);
this.checkAtMostOnceKeys(atMostOnceKeys);
this.checkAtLeastOnceKeys(EnumSet.of(Key.DIRECTORY_SIGNATURE));
@@ -133,6 +134,12 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
case SHARED_RAND_CURRENT_VALUE:
this.parseSharedRandCurrentValueLine(line, parts);
break;
+ case BANDWIDTH_FILE_HEADERS:
+ this.parseBandwidthFileHeaders(line, parts);
+ break;
+ case BANDWIDTH_FILE_DIGEST:
+ this.parseBandwidthFileDigest(line, parts);
+ break;
case DIR_KEY_CERTIFICATE_VERSION:
this.parseDirKeyCertificateVersionLine(line, parts);
break;
@@ -479,6 +486,24 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
this.sharedRandCurrentValue = parts[2];
}
+ protected void parseBandwidthFileHeaders(String line, String[] parts)
+ throws DescriptorParseException {
+ this.bandwidthFileHeaders
+ = ParseHelper.parseKeyValueStringPairs(line, parts, 1);
+ }
+
+ protected void parseBandwidthFileDigest(String line, String[] parts)
+ throws DescriptorParseException {
+ for (int i = 1; i < parts.length; i++) {
+ String part = parts[i];
+ if (part.startsWith("sha256=")) {
+ /* 7 == "sha256=".length() */
+ ParseHelper.verifyThirtyTwoByteBase64String(line, part.substring(7));
+ this.bandwidthFileDigestSha256Base64 = part.substring(7);
+ }
+ }
+ }
+
private void parseDirKeyCertificateVersionLine(String line,
String[] parts) throws DescriptorParseException {
if (parts.length != 2) {
@@ -662,6 +687,20 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl
return this.sharedRandCurrentValue;
}
+ private SortedMap<String, String> bandwidthFileHeaders;
+
+ @Override
+ public SortedMap<String, String> getBandwidthFileHeaders() {
+ return this.bandwidthFileHeaders;
+ }
+
+ private String bandwidthFileDigestSha256Base64;
+
+ @Override
+ public String getBandwidthFileDigestSha256Base64() {
+ return this.bandwidthFileDigestSha256Base64;
+ }
+
private int dirKeyCertificateVersion;
@Override
diff --git a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
index aac4bcc..261c897 100644
--- a/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
+++ b/src/main/java/org/torproject/descriptor/impl/ServerDescriptorImpl.java
@@ -31,7 +31,7 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
Key.IPV6_POLICY, Key.NTOR_ONION_KEY, Key.ONION_KEY_CROSSCERT,
Key.NTOR_ONION_KEY_CROSSCERT, Key.TUNNELLED_DIR_SERVER,
Key.ROUTER_SIG_ED25519, Key.ROUTER_SIGNATURE, Key.ROUTER_DIGEST_SHA256,
- Key.ROUTER_DIGEST);
+ Key.ROUTER_DIGEST, Key.BRIDGE_DISTRIBUTION_REQUEST);
private static final Set<Key> exactlyOnce = EnumSet.of(
Key.ROUTER, Key.BANDWIDTH, Key.PUBLISHED);
@@ -113,6 +113,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
case CONTACT:
this.parseContactLine(lineNoOpt);
break;
+ case BRIDGE_DISTRIBUTION_REQUEST:
+ this.parseBridgeDistributionRequestLine(line, partsNoOpt);
+ break;
case FAMILY:
this.parseFamilyLine(line, partsNoOpt);
break;
@@ -394,6 +397,14 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
}
}
+ private void parseBridgeDistributionRequestLine(String line,
+ String[] partsNoOpt) throws DescriptorParseException {
+ if (partsNoOpt.length < 2) {
+ throw new DescriptorParseException("Illegal line '" + line + "'.");
+ }
+ this.bridgeDistributionRequest = partsNoOpt[1];
+ }
+
private void parseFamilyLine(String line,
String[] partsNoOpt) throws DescriptorParseException {
String[] familyEntries = new String[partsNoOpt.length - 1];
@@ -790,6 +801,13 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
return this.contact;
}
+ private String bridgeDistributionRequest = null;
+
+ @Override
+ public String getBridgeDistributionRequest() {
+ return this.bridgeDistributionRequest;
+ }
+
private String[] familyEntries;
@Override
diff --git a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
index e586650..eea15ed 100644
--- a/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/BridgeNetworkStatusTest.java
@@ -9,7 +9,9 @@ import static org.junit.Assert.assertTrue;
import org.torproject.descriptor.BridgeNetworkStatus;
import org.torproject.descriptor.DescriptorParseException;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import java.util.ArrayList;
import java.util.List;
@@ -18,6 +20,9 @@ import java.util.List;
* already tested in the consensus/vote-parsing tests. */
public class BridgeNetworkStatusTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
/* Helper class to build a bridge network status based on default data
* and modifications requested by test methods. */
private static class StatusBuilder {
@@ -57,6 +62,16 @@ public class BridgeNetworkStatusTest {
return sb.buildStatus();
}
+ private String fingerprintLine
+ = "fingerprint BA44A889E64B93FAA2B114E02C2A279A8555C533";
+
+ private static BridgeNetworkStatus createWithFingerprintLine(String line)
+ throws DescriptorParseException {
+ StatusBuilder sb = new StatusBuilder();
+ sb.fingerprintLine = line;
+ return sb.buildStatus();
+ }
+
private List<String> statusEntries = new ArrayList<>();
private String unrecognizedHeaderLine = null;
@@ -106,6 +121,9 @@ public class BridgeNetworkStatusTest {
if (this.flagThresholdsLine != null) {
sb.append(this.flagThresholdsLine).append("\n");
}
+ if (this.fingerprintLine != null) {
+ sb.append(this.fingerprintLine).append("\n");
+ }
if (this.unrecognizedHeaderLine != null) {
sb.append(this.unrecognizedHeaderLine).append("\n");
}
@@ -135,6 +153,8 @@ public class BridgeNetworkStatusTest {
assertEquals(339000L, status.getGuardBandwidthExcludingExits());
assertEquals(1, status.getEnoughMtbfInfo());
assertEquals(0, status.getIgnoringAdvertisedBws());
+ assertEquals("BA44A889E64B93FAA2B114E02C2A279A8555C533",
+ status.getFingerprint());
assertEquals(264, status.getStatusEntries().get(
"001934C20E23E812C27592A5795B6636B7F32462").getBandwidth());
assertTrue(status.getUnrecognizedLines().isEmpty());
@@ -161,5 +181,16 @@ public class BridgeNetworkStatusTest {
assertEquals(-1, status.getEnoughMtbfInfo());
assertEquals(-1, status.getIgnoringAdvertisedBws());
}
+
+ @Test
+ public void testBridgeDistributionRequestGivenTwice()
+ throws DescriptorParseException {
+ this.thrown.expect(DescriptorParseException.class);
+ this.thrown.expectMessage("Keyword 'fingerprint' is contained 2 times, but "
+ + "must be contained at most once.");
+ StatusBuilder.createWithFingerprintLine(
+ "fingerprint BA44A889E64B93FAA2B114E02C2A279A8555C533\n"
+ + "fingerprint BA44A889E64B93FAA2B114E02C2A279A8555C533");
+ }
}
diff --git a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
index 97456d6..f5fa52d 100644
--- a/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
+++ b/src/test/java/org/torproject/descriptor/impl/ServerDescriptorImplTest.java
@@ -338,6 +338,15 @@ public class ServerDescriptorImplTest {
return db.buildDescriptor();
}
+ private String bridgeDistributionRequestLine = null;
+
+ private static ServerDescriptor createWithBridgeDistributionRequestLine(
+ String line) throws DescriptorParseException {
+ DescriptorBuilder db = new DescriptorBuilder();
+ db.bridgeDistributionRequestLine = line;
+ return db.buildDescriptor();
+ }
+
private byte[] buildDescriptorBytes() {
StringBuilder sb = new StringBuilder();
if (this.routerLine != null) {
@@ -385,6 +394,9 @@ public class ServerDescriptorImplTest {
if (this.contactLine != null) {
sb.append(this.contactLine).append("\n");
}
+ if (this.bridgeDistributionRequestLine != null) {
+ sb.append(this.bridgeDistributionRequestLine).append("\n");
+ }
if (this.familyLine != null) {
sb.append(this.familyLine).append("\n");
}
@@ -1973,5 +1985,23 @@ public class ServerDescriptorImplTest {
assertNull(descriptor.getDigestSha1Hex());
assertNull(descriptor.getDigestSha256Base64());
}
+
+ @Test
+ public void testBridgeDistributionRequestMoat()
+ throws DescriptorParseException {
+ ServerDescriptor descriptor =
+ DescriptorBuilder.createWithBridgeDistributionRequestLine(
+ "bridge-distribution-request moat");
+ assertEquals("moat", descriptor.getBridgeDistributionRequest());
+ }
+
+ @Test
+ public void testBridgeDistributionRequestEmptySpace()
+ throws DescriptorParseException {
+ this.thrown.expect(DescriptorParseException.class);
+ this.thrown.expectMessage("Illegal line 'bridge-distribution-request '.");
+ DescriptorBuilder.createWithBridgeDistributionRequestLine(
+ "bridge-distribution-request ");
+ }
}
More information about the tor-commits
mailing list