[tor-commits] [metrics-lib/master] Parse certs and everything we need to verify consensuses.

karsten at torproject.org karsten at torproject.org
Thu Apr 26 14:15:27 UTC 2012


commit 47e771f2b772f21563d38a98bb7c7fe284b474d8
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date:   Thu Apr 26 16:07:55 2012 +0200

    Parse certs and everything we need to verify consensuses.
---
 .../descriptor/DirectoryKeyCertificate.java        |   45 ++++
 .../torproject/descriptor/DirectorySignature.java  |   16 ++
 .../descriptor/RelayNetworkStatusConsensus.java    |    6 +-
 .../descriptor/RelayNetworkStatusVote.java         |    2 +-
 .../torproject/descriptor/impl/DescriptorImpl.java |    4 +-
 .../impl/DirectoryKeyCertificateImpl.java          |  270 ++++++++++++++++++++
 .../descriptor/impl/DirectorySignatureImpl.java    |   91 +++++++
 .../descriptor/impl/NetworkStatusImpl.java         |   50 +---
 .../impl/RelayNetworkStatusConsensusImpl.java      |   33 +++
 9 files changed, 479 insertions(+), 38 deletions(-)

diff --git a/src/org/torproject/descriptor/DirectoryKeyCertificate.java b/src/org/torproject/descriptor/DirectoryKeyCertificate.java
new file mode 100644
index 0000000..9a4aeae
--- /dev/null
+++ b/src/org/torproject/descriptor/DirectoryKeyCertificate.java
@@ -0,0 +1,45 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor;
+
+public interface DirectoryKeyCertificate extends Descriptor {
+
+  /* Return the directory key certificate version. */
+  public int getDirKeyCertificateVersion();
+
+  /* Return the IP address, or null if the certificate does not contain an
+   * address. */
+  public String getAddress();
+
+  /* Return the directory port, or -1 if the certificate does not contain
+   * one. */
+  public int getPort();
+
+  /* Return the directory identity fingerprint. */
+  public String getFingerprint();
+
+  /* Return the directory identity key. */
+  public String getDirIdentityKey();
+
+  /* Return the directory key certificate publication timestamp. */
+  public long getDirKeyPublishedMillis();
+
+  /* Return the directory key certificate expiration timestamp. */
+  public long getDirKeyExpiresMillis();
+
+  /* Return the directory signing key digest. */
+  public String getDirSigningKey();
+
+  /* Return the signature of the directory identity key made using the
+   * directory signing key, or null if the certificate does not contain
+   * this signature. */
+  public String getDirKeyCrosscert();
+
+  /* Return the certificate signature made using the directory identity
+   * key. */
+  public String getDirKeyCertification();
+
+  /* Return the calculated certificate digest. */
+  public String getCertificateDigest();
+}
+
diff --git a/src/org/torproject/descriptor/DirectorySignature.java b/src/org/torproject/descriptor/DirectorySignature.java
new file mode 100644
index 0000000..29eb055
--- /dev/null
+++ b/src/org/torproject/descriptor/DirectorySignature.java
@@ -0,0 +1,16 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor;
+
+public interface DirectorySignature {
+
+  /* Return the directory identity fingerprint. */
+  public String getIdentity();
+
+  /* Return the directory signing key digest. */
+  public String getSigningKeyDigest();
+
+  /* Return the directory signature made using the signing key. */
+  public String getSignature();
+}
+
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
index 2c0ff4f..4a1634f 100644
--- a/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusConsensus.java
@@ -60,10 +60,14 @@ public interface RelayNetworkStatusConsensus extends Descriptor {
   public NetworkStatusEntry getStatusEntry(String fingerprint);
 
   /* Return directory signatures. */
-  public SortedMap<String, String> getDirectorySignatures();
+  public SortedMap<String, DirectorySignature> getDirectorySignatures();
 
   /* Return bandwidth weights or null if the consensus doesn't contain
    * bandwidth weights. */
   public SortedMap<String, Integer> getBandwidthWeights();
+
+  /* Return the consensus digest that directory authorities use to sign
+   * the consensus. */
+  public String getConsensusDigest();
 }
 
diff --git a/src/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
index f9635e2..eda0cef 100644
--- a/src/org/torproject/descriptor/RelayNetworkStatusVote.java
+++ b/src/org/torproject/descriptor/RelayNetworkStatusVote.java
@@ -92,6 +92,6 @@ public interface RelayNetworkStatusVote extends Descriptor {
   public NetworkStatusEntry getStatusEntry(String fingerprint);
 
   /* Return directory signatures. */
-  public SortedMap<String, String> getDirectorySignatures();
+  public SortedMap<String, DirectorySignature> getDirectorySignatures();
 }
 
diff --git a/src/org/torproject/descriptor/impl/DescriptorImpl.java b/src/org/torproject/descriptor/impl/DescriptorImpl.java
index 33b94e9..6b1b167 100644
--- a/src/org/torproject/descriptor/impl/DescriptorImpl.java
+++ b/src/org/torproject/descriptor/impl/DescriptorImpl.java
@@ -60,7 +60,9 @@ public abstract class DescriptorImpl implements Descriptor {
           parseDescriptors(rawDescriptorBytes,
           failUnrecognizedDescriptorLines));
     } else if (firstLines.startsWith("dir-key-certificate-version ")) {
-      /* TODO Implement parsing of directory certificates. */
+      parsedDescriptors.addAll(DirectoryKeyCertificateImpl.
+          parseDescriptors(rawDescriptorBytes,
+          failUnrecognizedDescriptorLines));
     } else if (firstLines.startsWith("ExitNode ")) {
       parsedDescriptors.add(new ExitListImpl(rawDescriptorBytes, fileName,
           failUnrecognizedDescriptorLines));
diff --git a/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java b/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
new file mode 100644
index 0000000..2483aa1
--- /dev/null
+++ b/src/org/torproject/descriptor/impl/DirectoryKeyCertificateImpl.java
@@ -0,0 +1,270 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.torproject.descriptor.DirectoryKeyCertificate;
+
+/* TODO Add test class. */
+
+public class DirectoryKeyCertificateImpl extends DescriptorImpl
+    implements DirectoryKeyCertificate {
+
+  protected static List<DirectoryKeyCertificate> parseDescriptors(
+      byte[] descriptorsBytes, boolean failUnrecognizedDescriptorLines)
+      throws DescriptorParseException {
+    List<DirectoryKeyCertificate> parsedDescriptors =
+        new ArrayList<DirectoryKeyCertificate>();
+    List<byte[]> splitDescriptorsBytes =
+        DirectoryKeyCertificateImpl.splitRawDescriptorBytes(
+            descriptorsBytes, "dir-key-certificate-version ");
+    for (byte[] descriptorBytes : splitDescriptorsBytes) {
+      DirectoryKeyCertificate parsedDescriptor =
+          new DirectoryKeyCertificateImpl(descriptorBytes,
+          failUnrecognizedDescriptorLines);
+      parsedDescriptors.add(parsedDescriptor);
+    }
+    return parsedDescriptors;
+  }
+
+  protected DirectoryKeyCertificateImpl(byte[] rawDescriptorBytes,
+      boolean failUnrecognizedDescriptorLines)
+      throws DescriptorParseException {
+    super(rawDescriptorBytes, failUnrecognizedDescriptorLines);
+    this.parseDescriptorBytes();
+    this.calculateDigest();
+    Set<String> exactlyOnceKeywords = new HashSet<String>(Arrays.asList((
+        "dir-key-certificate-version,fingerprint,dir-identity-key,"
+        + "dir-key-published,dir-key-expires,dir-signing-key,"
+        + "dir-key-certification").split(",")));
+    this.checkExactlyOnceKeywords(exactlyOnceKeywords);
+    Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
+        "dir-address,dir-key-crosscert").split(",")));
+    this.checkAtMostOnceKeywords(atMostOnceKeywords);
+    this.checkFirstKeyword("dir-key-certificate-version");
+    this.checkLastKeyword("dir-key-certification");
+  }
+
+  private void parseDescriptorBytes() throws DescriptorParseException {
+    Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
+        useDelimiter("\n");
+    String nextCrypto = null;
+    StringBuilder crypto = null;
+    while (s.hasNext()) {
+      String line = s.next();
+      String[] parts = line.split(" ");
+      String keyword = parts[0];
+      if (keyword.equals("dir-key-certificate-version")) {
+        this.parseDirKeyCertificateVersionLine(line, parts);
+      } else if (keyword.equals("dir-address")) {
+        this.parseDirAddressLine(line, parts);
+      } else if (keyword.equals("fingerprint")) {
+        this.parseFingerprintLine(line, parts);
+      } else if (keyword.equals("dir-identity-key")) {
+        this.parseDirIdentityKeyLine(line, parts);
+        nextCrypto = "dir-identity-key";
+      } else if (keyword.equals("dir-key-published")) {
+        this.parseDirKeyPublishedLine(line, parts);
+      } else if (keyword.equals("dir-key-expires")) {
+        this.parseDirKeyExpiresLine(line, parts);
+      } else if (keyword.equals("dir-signing-key")) {
+        this.parseDirSigningKeyLine(line, parts);
+        nextCrypto = "dir-signing-key";
+      } else if (keyword.equals("dir-key-crosscert")) {
+        this.parseDirKeyCrosscertLine(line, parts);
+        nextCrypto = "dir-key-crosscert";
+      } else if (keyword.equals("dir-key-certification")) {
+        this.parseDirKeyCertificationLine(line, parts);
+        nextCrypto = "dir-key-certification";
+      } else if (line.startsWith("-----BEGIN")) {
+        crypto = new StringBuilder();
+        crypto.append(line + "\n");
+      } else if (line.startsWith("-----END")) {
+        crypto.append(line + "\n");
+        String cryptoString = crypto.toString();
+        crypto = null;
+        if (nextCrypto.equals("dir-identity-key")) {
+          this.dirIdentityKey = cryptoString;
+        } else if (nextCrypto.equals("dir-signing-key")) {
+          this.dirSigningKey = cryptoString;
+        } else if (nextCrypto.equals("dir-key-crosscert")) {
+          this.dirKeyCrosscert = cryptoString;
+        } else if (nextCrypto.equals("dir-key-certification")) {
+          this.dirKeyCertification = cryptoString;
+        } else {
+          throw new DescriptorParseException("Unrecognized crypto "
+              + "block in directory key certificate.");
+        }
+        nextCrypto = null;
+      } else if (crypto != null) {
+        crypto.append(line + "\n");
+      } else {
+        if (this.failUnrecognizedDescriptorLines) {
+          throw new DescriptorParseException("Unrecognized line '"
+              + line + "' in directory key certificate.");
+        } else {
+          if (this.unrecognizedLines == null) {
+            this.unrecognizedLines = new ArrayList<String>();
+          }
+          this.unrecognizedLines.add(line);
+        }
+      }
+    }
+  }
+
+  private void parseDirKeyCertificateVersionLine(String line,
+      String[] parts) throws DescriptorParseException {
+    if (!line.equals("dir-key-certificate-version 3")) {
+      throw new DescriptorParseException("Illegal directory key "
+          + "certificate version number in line '" + line + "'.");
+    }
+    this.dirKeyCertificateVersion = 3;
+  }
+
+  private void parseDirAddressLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (parts.length != 2 || parts[1].split(":").length != 2) {
+      throw new DescriptorParseException("Illegal line '" + line
+          + "' in directory key certificate.");
+    }
+    this.address = ParseHelper.parseIpv4Address(line,
+        parts[1].split(":")[0]);
+    this.port = ParseHelper.parsePort(line, parts[1].split(":")[1]);
+  }
+
+  private void parseFingerprintLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (parts.length != 2) {
+      throw new DescriptorParseException("Illegal line '" + line
+          + "' in directory key certificate.");
+    }
+    this.fingerprint = ParseHelper.parseTwentyByteHexString(line,
+        parts[1]);
+  }
+
+  private void parseDirIdentityKeyLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (!line.equals("dir-identity-key")) {
+      throw new DescriptorParseException("Illegal line '" + line + "'.");
+    }
+  }
+
+  private void parseDirKeyPublishedLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.dirKeyPublishedMillis = ParseHelper.parseTimestampAtIndex(line,
+        parts, 1, 2);
+  }
+
+  private void parseDirKeyExpiresLine(String line, String[] parts)
+      throws DescriptorParseException {
+    this.dirKeyExpiresMillis = ParseHelper.parseTimestampAtIndex(line,
+        parts, 1, 2);
+  }
+
+  private void parseDirSigningKeyLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (!line.equals("dir-signing-key")) {
+      throw new DescriptorParseException("Illegal line '" + line + "'.");
+    }
+  }
+
+  private void parseDirKeyCrosscertLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (!line.equals("dir-key-crosscert")) {
+      throw new DescriptorParseException("Illegal line '" + line + "'.");
+    }
+  }
+
+  private void parseDirKeyCertificationLine(String line, String[] parts)
+      throws DescriptorParseException {
+    if (!line.equals("dir-key-certification")) {
+      throw new DescriptorParseException("Illegal line '" + line + "'.");
+    }
+  }
+
+  private void calculateDigest() throws DescriptorParseException {
+    try {
+      String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+      String startToken = "dir-key-certificate-version ";
+      String sigToken = "\ndir-key-certification\n";
+      int start = ascii.indexOf(startToken);
+      int sig = ascii.indexOf(sigToken) + sigToken.length();
+      if (start >= 0 && sig >= 0 && sig > start) {
+        byte[] forDigest = new byte[sig - start];
+        System.arraycopy(this.getRawDescriptorBytes(), start,
+            forDigest, 0, sig - start);
+        this.certificateDigest = DigestUtils.shaHex(forDigest);
+      }
+    } catch (UnsupportedEncodingException e) {
+      /* Handle below. */
+    }
+    if (this.certificateDigest == null) {
+      throw new DescriptorParseException("Could not calculate "
+          + "certificate digest.");
+    }
+  }
+
+  private int dirKeyCertificateVersion;
+  public int getDirKeyCertificateVersion() {
+    return this.dirKeyCertificateVersion;
+  }
+
+  private String address;
+  public String getAddress() {
+    return this.address;
+  }
+
+  private int port = -1;
+  public int getPort() {
+    return this.port;
+  }
+
+  private String fingerprint;
+  public String getFingerprint() {
+    return this.fingerprint;
+  }
+
+  private String dirIdentityKey;
+  public String getDirIdentityKey() {
+    return this.dirIdentityKey;
+  }
+
+  private long dirKeyPublishedMillis;
+  public long getDirKeyPublishedMillis() {
+    return this.dirKeyPublishedMillis;
+  }
+
+  private long dirKeyExpiresMillis;
+  public long getDirKeyExpiresMillis() {
+    return this.dirKeyExpiresMillis;
+  }
+
+  private String dirSigningKey;
+  public String getDirSigningKey() {
+    return this.dirSigningKey;
+  }
+
+  private String dirKeyCrosscert;
+  public String getDirKeyCrosscert() {
+    return this.dirKeyCrosscert;
+  }
+
+  private String dirKeyCertification;
+  public String getDirKeyCertification() {
+    return this.dirKeyCertification;
+  }
+
+  private String certificateDigest;
+  public String getCertificateDigest() {
+    return this.certificateDigest;
+  }
+}
+
diff --git a/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
new file mode 100644
index 0000000..d205345
--- /dev/null
+++ b/src/org/torproject/descriptor/impl/DirectorySignatureImpl.java
@@ -0,0 +1,91 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+package org.torproject.descriptor.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import org.torproject.descriptor.DirectorySignature;
+
+public class DirectorySignatureImpl implements DirectorySignature {
+
+  private byte[] directorySignatureBytes;
+  public byte[] getDirectorySignatureBytes() {
+    return this.directorySignatureBytes;
+  }
+
+  private boolean failUnrecognizedDescriptorLines;
+  private List<String> unrecognizedLines;
+  protected List<String> getAndClearUnrecognizedLines() {
+    List<String> lines = this.unrecognizedLines;
+    this.unrecognizedLines = null;
+    return lines;
+  }
+
+  protected DirectorySignatureImpl(byte[] directorySignatureBytes,
+      boolean failUnrecognizedDescriptorLines)
+      throws DescriptorParseException {
+    this.directorySignatureBytes = directorySignatureBytes;
+    this.failUnrecognizedDescriptorLines =
+        failUnrecognizedDescriptorLines;
+    this.parseDirectorySignatureBytes();
+  }
+
+  private void parseDirectorySignatureBytes()
+      throws DescriptorParseException {
+    Scanner s = new Scanner(new String(this.directorySignatureBytes)).
+        useDelimiter("\n");
+    StringBuilder crypto = null;
+    while (s.hasNext()) {
+      String line = s.next();
+      if (line.startsWith("directory-signature ")) {
+        String[] parts = line.split(" ", -1);
+        if (parts.length != 3) {
+          throw new DescriptorParseException("Illegal line '" + line
+              + "'.");
+        }
+        this.identity = ParseHelper.parseTwentyByteHexString(line,
+            parts[1]);
+        this.signingKeyDigest = ParseHelper.parseTwentyByteHexString(
+            line, parts[2]);
+      } else if (line.startsWith("-----BEGIN")) {
+        crypto = new StringBuilder();
+        crypto.append(line + "\n");
+      } else if (line.startsWith("-----END")) {
+        crypto.append(line + "\n");
+        String cryptoString = crypto.toString();
+        crypto = null;
+        this.signature = cryptoString;
+      } else if (crypto != null) {
+        crypto.append(line + "\n");
+      } else {
+        if (this.failUnrecognizedDescriptorLines) {
+          throw new DescriptorParseException("Unrecognized line '"
+              + line + "' in dir-source entry.");
+        } else {
+          if (this.unrecognizedLines == null) {
+            this.unrecognizedLines = new ArrayList<String>();
+          }
+          this.unrecognizedLines.add(line);
+        }
+      }
+    }
+  }
+
+  private String identity;
+  public String getIdentity() {
+    return this.identity;
+  }
+
+  private String signingKeyDigest;
+  public String getSigningKeyDigest() {
+    return this.signingKeyDigest;
+  }
+
+  private String signature;
+  public String getSignature() {
+    return this.signature;
+  }
+}
+
diff --git a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
index f080171..d27e651 100644
--- a/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
+++ b/src/org/torproject/descriptor/impl/NetworkStatusImpl.java
@@ -4,11 +4,11 @@ package org.torproject.descriptor.impl;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Scanner;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
 import org.torproject.descriptor.DirSourceEntry;
+import org.torproject.descriptor.DirectorySignature;
 import org.torproject.descriptor.NetworkStatusEntry;
 
 /* Parse the common parts of v3 consensuses, v3 votes, v3 microdesc
@@ -195,39 +195,19 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
   protected void parseDirectorySignature(byte[] directorySignatureBytes)
       throws DescriptorParseException {
     if (this.directorySignatures == null) {
-      this.directorySignatures = new TreeMap<String, String>();
+      this.directorySignatures = new TreeMap<String,
+          DirectorySignature>();
     }
-    Scanner s = new Scanner(new String(directorySignatureBytes)).
-        useDelimiter("\n");
-    boolean skipCrypto = false;
-    while (s.hasNext()) {
-      String line = s.next();
-      if (line.startsWith("directory-signature ")) {
-        String[] parts = line.split(" ", -1);
-        if (parts.length != 3) {
-          throw new DescriptorParseException("Illegal line '" + line
-              + "'.");
-        }
-        String identity = ParseHelper.parseTwentyByteHexString(line,
-            parts[1]);
-        String signingKeyDigest = ParseHelper.parseTwentyByteHexString(
-            line, parts[2]);
-        this.directorySignatures.put(identity, signingKeyDigest);
-      } else if (line.startsWith("-----BEGIN")) {
-        skipCrypto = true;
-      } else if (line.startsWith("-----END")) {
-        skipCrypto = false;
-      } else if (!skipCrypto) {
-        if (this.failUnrecognizedDescriptorLines) {
-          throw new DescriptorParseException("Unrecognized line '"
-              + line + "' in dir-source entry.");
-        } else {
-          if (this.unrecognizedLines == null) {
-            this.unrecognizedLines = new ArrayList<String>();
-          }
-          this.unrecognizedLines.add(line);
-        }
+    DirectorySignatureImpl signature = new DirectorySignatureImpl(
+        directorySignatureBytes, failUnrecognizedDescriptorLines);
+    this.directorySignatures.put(signature.getIdentity(), signature);
+    List<String> unrecognizedStatusEntryLines = signature.
+        getAndClearUnrecognizedLines();
+    if (unrecognizedStatusEntryLines != null) {
+      if (this.unrecognizedLines == null) {
+        this.unrecognizedLines = new ArrayList<String>();
       }
+      this.unrecognizedLines.addAll(unrecognizedStatusEntryLines);
     }
   }
 
@@ -249,10 +229,10 @@ public abstract class NetworkStatusImpl extends DescriptorImpl {
     return this.statusEntries.get(fingerprint);
   }
 
-  private SortedMap<String, String> directorySignatures;
-  public SortedMap<String, String> getDirectorySignatures() {
+  private SortedMap<String, DirectorySignature> directorySignatures;
+  public SortedMap<String, DirectorySignature> getDirectorySignatures() {
     return this.directorySignatures == null ? null :
-        new TreeMap<String, String>(this.directorySignatures);
+        new TreeMap<String, DirectorySignature>(this.directorySignatures);
   }
 }
 
diff --git a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
index 564beb2..3d2af37 100644
--- a/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
+++ b/src/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java
@@ -2,6 +2,7 @@
  * See LICENSE for licensing information */
 package org.torproject.descriptor.impl;
 
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -13,6 +14,7 @@ import java.util.SortedSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
+import org.apache.commons.codec.digest.DigestUtils;
 import org.torproject.descriptor.RelayNetworkStatusConsensus;
 
 /* Contains a network status consensus. */
@@ -49,6 +51,32 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         + "bandwidth-weights").split(",")));
     this.checkAtMostOnceKeywords(atMostOnceKeywords);
     this.checkFirstKeyword("network-status-version");
+    this.calculateDigest();
+  }
+
+  private void calculateDigest() throws DescriptorParseException {
+    try {
+      String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
+      String startToken = "network-status-version ";
+      String sigToken = "\ndirectory-signature ";
+      if (!ascii.contains(sigToken)) {
+        return;
+      }
+      int start = ascii.indexOf(startToken);
+      int sig = ascii.indexOf(sigToken) + sigToken.length();
+      if (start >= 0 && sig >= 0 && sig > start) {
+        byte[] forDigest = new byte[sig - start];
+        System.arraycopy(this.getRawDescriptorBytes(), start,
+            forDigest, 0, sig - start);
+        this.consensusDigest = DigestUtils.shaHex(forDigest);
+      }
+    } catch (UnsupportedEncodingException e) {
+      /* Handle below. */
+    }
+    if (this.consensusDigest == null) {
+      throw new DescriptorParseException("Could not calculate consensus "
+          + "digest.");
+    }
   }
 
   protected void parseHeader(byte[] headerBytes)
@@ -238,6 +266,11 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl
         1);
   }
 
+  private String consensusDigest;
+  public String getConsensusDigest() {
+    return this.consensusDigest;
+  }
+
   private int networkStatusVersion;
   public int getNetworkStatusVersion() {
     return this.networkStatusVersion;



More information about the tor-commits mailing list