[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [metrics-tasks/master] Add code to verify consensuses (#2768).
commit 90ff9b555c9d043d8f4392e08ee6a5e5dcdc9f68
Author: Karsten Loesing <karsten.loesing@xxxxxxx>
Date: Thu Apr 26 16:14:58 2012 +0200
Add code to verify consensuses (#2768).
---
task-2768/VerifyDescriptors.java | 306 ++++++++++++++++++++++++++++++++
task-2768/VerifyServerDescriptors.java | 145 ---------------
task-2768/run.sh | 2 +-
3 files changed, 307 insertions(+), 146 deletions(-)
diff --git a/task-2768/VerifyDescriptors.java b/task-2768/VerifyDescriptors.java
new file mode 100644
index 0000000..b784cdd
--- /dev/null
+++ b/task-2768/VerifyDescriptors.java
@@ -0,0 +1,306 @@
+/* Copyright 2012 The Tor Project
+ * See LICENSE for licensing information */
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.StringReader;
+import java.security.Security;
+import java.security.interfaces.RSAPublicKey;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.bouncycastle.asn1.ASN1OutputStream;
+import org.bouncycastle.crypto.digests.SHA1Digest;
+import org.bouncycastle.crypto.encodings.PKCS1Encoding;
+import org.bouncycastle.crypto.engines.RSAEngine;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMReader;
+import org.torproject.descriptor.Descriptor;
+import org.torproject.descriptor.DescriptorFile;
+import org.torproject.descriptor.DescriptorReader;
+import org.torproject.descriptor.DescriptorSourceFactory;
+import org.torproject.descriptor.DirectoryKeyCertificate;
+import org.torproject.descriptor.DirectorySignature;
+import org.torproject.descriptor.RelayNetworkStatusConsensus;
+import org.torproject.descriptor.ServerDescriptor;
+
+/*
+ * Verify server descriptors using the contained signing key. Verify that
+ * 1) a contained fingerprint is actually a hash of the signing key and
+ * 2) a router signature was created using the signing key.
+ *
+ * Verify consensuses using the separate certs. Verify that
+ * 1) the fingerprint in a cert is actually a hash of the identity key,
+ * 2) a cert was signed using the identity key,
+ * 3) a consensus was signed using the signing key from the cert.
+ *
+ * Usage:
+ * - Put certs in in/certs/, consensuses in in/consensuses/, and server
+ * descriptors in in/server-descriptors/.
+ * - Clone metrics-lib, run `ant jar`, and copy descriptor.jar to this
+ * directory.
+ * - Download Apache Commons Codec and Compress jar files
+ * commons-codec-1.4.jar and commons-compress-1.3.jar and put them in
+ * this directory.
+ * - Download BouncyCastle 1.47 jar files bcprov-jdk15on-147.jar and
+ * bcpkix-jdk15on-147.jar and put them in this directory.
+ * - Compile and run this class: ./run.sh.
+ */
+public class VerifyDescriptors {
+ public static void main(String[] args) throws Exception {
+ System.out.println("Verifying consensuses...");
+ if (Security.getProvider("BC") == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ verifyServerDescriptors();
+ verifyConsensuses();
+ }
+
+ private static void verifyServerDescriptors() throws Exception {
+ File serverDescriptorDirectory = new File("in/server-descriptors");
+ if (!serverDescriptorDirectory.exists()) {
+ return;
+ }
+ DescriptorReader descriptorReader = DescriptorSourceFactory
+ .createDescriptorReader();
+ descriptorReader.addDirectory(serverDescriptorDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ descriptorReader.readDescriptors();
+ int processedDescriptors = 0, verifiedDescriptors = 0;
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ if (descriptorFile.getException() != null) {
+ System.err.println("Could not read/parse descriptor file "
+ + descriptorFile.getFileName() + ": "
+ + descriptorFile.getException().getMessage());
+ continue;
+ }
+ if (descriptorFile.getDescriptors() == null) {
+ continue;
+ }
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof ServerDescriptor)) {
+ continue;
+ }
+ ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
+ boolean isVerified = true;
+
+ /* Verify that the contained fingerprint is a hash of the signing
+ * key. */
+ String signingKeyHashString = determineKeyHash(
+ serverDescriptor.getSigningKey());
+ String fingerprintString =
+ serverDescriptor.getFingerprint().toLowerCase();
+ if (!signingKeyHashString.equals(fingerprintString)) {
+ System.out.println("In " + descriptorFile.getFile()
+ + ", server descriptor, the calculated signing key hash "
+ + " does not match the contained fingerprint!");
+ isVerified = false;
+ }
+
+ /* Verify that the router signature was created using the signing
+ * key. */
+ if (!verifySignature(serverDescriptor.getServerDescriptorDigest(),
+ serverDescriptor.getRouterSignature(),
+ serverDescriptor.getSigningKey())) {
+ System.out.println("In " + descriptorFile.getFile()
+ + ", the decrypted signature does not match the descriptor "
+ + "digest!");
+ isVerified = false;
+ }
+
+ processedDescriptors++;
+ if (isVerified) {
+ verifiedDescriptors++;
+ }
+ }
+ }
+ System.out.println("Verified " + verifiedDescriptors + "/"
+ + processedDescriptors + " server descriptors.");
+ }
+
+ private static void verifyConsensuses() throws Exception {
+ File certsDirectory = new File("in/certs");
+ File consensusDirectory = new File("in/consensuses");
+ if (!certsDirectory.exists() || !consensusDirectory.exists()) {
+ return;
+ }
+ Map<String, String> signingKeys = new HashMap<String, String>();
+
+ DescriptorReader certsReader = DescriptorSourceFactory
+ .createDescriptorReader();
+ certsReader.addDirectory(certsDirectory);
+ Iterator<DescriptorFile> descriptorFiles =
+ certsReader.readDescriptors();
+ int processedCerts = 0, verifiedCerts = 0;
+ while (descriptorFiles.hasNext()) {
+ DescriptorFile descriptorFile = descriptorFiles.next();
+ if (descriptorFile.getException() != null) {
+ System.err.println("Could not read/parse descriptor file "
+ + descriptorFile.getFileName() + ": "
+ + descriptorFile.getException().getMessage());
+ continue;
+ }
+ if (descriptorFile.getDescriptors() == null) {
+ continue;
+ }
+ for (Descriptor descriptor : descriptorFile.getDescriptors()) {
+ if (!(descriptor instanceof DirectoryKeyCertificate)) {
+ continue;
+ }
+ DirectoryKeyCertificate cert =
+ (DirectoryKeyCertificate) descriptor;
+ boolean isVerified = true;
+
+ /* Verify that the contained fingerprint is a hash of the signing
+ * key. */
+ String dirIdentityKeyHashString = determineKeyHash(
+ cert.getDirIdentityKey());
+ String fingerprintString = cert.getFingerprint().toLowerCase();
+ if (!dirIdentityKeyHashString.equals(fingerprintString)) {
+ System.out.println("In " + descriptorFile.getFile()
+ + ", the calculated directory identity key hash "
+ + dirIdentityKeyHashString
+ + " does not match the contained fingerprint "
+ + fingerprintString + "!");
+ isVerified = false;
+ }
+
+ /* Verify that the router signature was created using the signing
+ * key. */
+ if (!verifySignature(cert.getCertificateDigest(),
+ cert.getDirKeyCertification(), cert.getDirIdentityKey())) {
+ System.out.println("In " + descriptorFile.getFile()
+ + ", the decrypted directory key certification does not "
+ + "match the certificate digest!");
+ isVerified = false;
+ }
+
+ /* Determine the signing key digest and remember the signing key
+ * to verify consensus signatures. */
+ String dirSigningKeyString = cert.getDirSigningKey();
+ PEMReader pemReader2 = new PEMReader(new StringReader(
+ dirSigningKeyString));
+ RSAPublicKey dirSigningKey =
+ (RSAPublicKey) pemReader2.readObject();
+ ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+ new ASN1OutputStream(baos2).writeObject(
+ new org.bouncycastle.asn1.pkcs.RSAPublicKey(
+ dirSigningKey.getModulus(),
+ dirSigningKey.getPublicExponent()).toASN1Primitive());
+ byte[] pkcs2 = baos2.toByteArray();
+ byte[] dirSigningKeyHashBytes = new byte[20];
+ SHA1Digest sha1_2 = new SHA1Digest();
+ sha1_2.update(pkcs2, 0, pkcs2.length);
+ sha1_2.doFinal(dirSigningKeyHashBytes, 0);
+ String dirSigningKeyHashString = Hex.encodeHexString(
+ dirSigningKeyHashBytes).toUpperCase();
+ signingKeys.put(dirSigningKeyHashString, cert.getDirSigningKey());
+
+ processedCerts++;
+ if (isVerified) {
+ verifiedCerts++;
+ }
+ }
+ }
+ System.out.println("Verified " + verifiedCerts + "/"
+ + processedCerts + " certs.");
+
+ DescriptorReader consensusReader = DescriptorSourceFactory
+ .createDescriptorReader();
+ consensusReader.addDirectory(consensusDirectory);
+ Iterator<DescriptorFile> consensusFiles =
+ consensusReader.readDescriptors();
+ int processedConsensuses = 0, verifiedConsensuses = 0;
+ while (consensusFiles.hasNext()) {
+ DescriptorFile consensusFile = consensusFiles.next();
+ if (consensusFile.getException() != null) {
+ System.err.println("Could not read/parse descriptor file "
+ + consensusFile.getFileName() + ": "
+ + consensusFile.getException().getMessage());
+ continue;
+ }
+ if (consensusFile.getDescriptors() == null) {
+ continue;
+ }
+ for (Descriptor descriptor : consensusFile.getDescriptors()) {
+ if (!(descriptor instanceof RelayNetworkStatusConsensus)) {
+ continue;
+ }
+ RelayNetworkStatusConsensus consensus =
+ (RelayNetworkStatusConsensus) descriptor;
+ boolean isVerified = true;
+
+ /* Verify all signatures using the corresponding certificates. */
+ if (consensus.getDirectorySignatures().isEmpty()) {
+ System.out.println(consensusFile.getFile()
+ + " does not contain any signatures.");
+ continue;
+ }
+ for (DirectorySignature signature :
+ consensus.getDirectorySignatures().values()) {
+ String signingKeyDigest = signature.getSigningKeyDigest();
+ if (!signingKeys.containsKey(signingKeyDigest)) {
+ System.out.println("Cannot find signing key with digest "
+ + signingKeyDigest + "!");
+ }
+ if (!verifySignature(consensus.getConsensusDigest(),
+ signature.getSignature(),
+ signingKeys.get(signingKeyDigest))) {
+ System.out.println("In " + consensusFile.getFile()
+ + ", the decrypted signature digest does not match the "
+ + "consensus digest!");
+ isVerified = false;
+ }
+ }
+ processedConsensuses++;
+ if (isVerified) {
+ verifiedConsensuses++;
+ }
+ }
+ }
+ System.out.println("Verified " + verifiedConsensuses + "/"
+ + processedConsensuses + " consensuses.");
+ }
+
+ private static String determineKeyHash(String key) throws Exception {
+ PEMReader pemReader = new PEMReader(new StringReader(key));
+ RSAPublicKey dirIdentityKey = (RSAPublicKey) pemReader.readObject();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new ASN1OutputStream(baos).writeObject(
+ new org.bouncycastle.asn1.pkcs.RSAPublicKey(
+ dirIdentityKey.getModulus(),
+ dirIdentityKey.getPublicExponent()).toASN1Primitive());
+ byte[] pkcs = baos.toByteArray();
+ byte[] dirIdentityKeyHashBytes = new byte[20];
+ SHA1Digest sha1 = new SHA1Digest();
+ sha1.update(pkcs, 0, pkcs.length);
+ sha1.doFinal(dirIdentityKeyHashBytes, 0);
+ String keyHash = Hex.encodeHexString(dirIdentityKeyHashBytes);
+ return keyHash;
+ }
+
+ private static boolean verifySignature(String digest, String signature,
+ String signingKey) throws Exception {
+ byte[] signatureBytes = Base64.decodeBase64(signature.substring(
+ 0 + "-----BEGIN SIGNATURE-----\n".length(),
+ signature.length() - "-----END SIGNATURE-----\n".length()).
+ replaceAll("\n", ""));
+ RSAPublicKey rsaSigningKey = (RSAPublicKey) new PEMReader(
+ new StringReader(signingKey)).readObject();
+ RSAKeyParameters rsakp = new RSAKeyParameters(false,
+ rsaSigningKey.getModulus(),
+ rsaSigningKey.getPublicExponent());
+ PKCS1Encoding pe = new PKCS1Encoding(new RSAEngine());
+ pe.init(false, rsakp);
+ byte[] decryptedSignatureDigest = pe.processBlock(
+ signatureBytes, 0, signatureBytes.length);
+ String decryptedSignatureDigestString =
+ Hex.encodeHexString(decryptedSignatureDigest);
+ return decryptedSignatureDigestString.equalsIgnoreCase(digest);
+ }
+}
+
diff --git a/task-2768/VerifyServerDescriptors.java b/task-2768/VerifyServerDescriptors.java
deleted file mode 100644
index e648d92..0000000
--- a/task-2768/VerifyServerDescriptors.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/* Copyright 2012 The Tor Project
- * See LICENSE for licensing information */
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.StringReader;
-import java.security.Security;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Iterator;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.binary.Hex;
-import org.bouncycastle.asn1.ASN1OutputStream;
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.encodings.PKCS1Encoding;
-import org.bouncycastle.crypto.engines.RSAEngine;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMReader;
-import org.torproject.descriptor.Descriptor;
-import org.torproject.descriptor.DescriptorFile;
-import org.torproject.descriptor.DescriptorReader;
-import org.torproject.descriptor.DescriptorSourceFactory;
-import org.torproject.descriptor.ServerDescriptor;
-
-/*
- * Verify server descriptors using the contained signing key. Verify that
- * 1) the contained fingerprint is actually a hash of the signing key and
- * 2) the router signature was created using the signing key.
- *
- * Usage:
- * - Extract server descriptors to in/.
- * - Clone metrics-lib, run `ant jar`, and copy descriptor.jar to this
- * directory.
- * - Download Apache Commons Codec and Compress jar files
- * commons-codec-1.4.jar and commons-compress-1.3.jar and put them in
- * this directory.
- * - Download BouncyCastle 1.47 jar files bcprov-jdk15on-147.jar and
- * bcpkix-jdk15on-147.jar and put them in this directory.
- * - Compile and run this class: ./run.sh.
- */
-public class VerifyServerDescriptors {
- public static void main(String[] args) throws Exception {
- System.out.println("Verifying descriptors...");
- if (Security.getProvider("BC") == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- File inputDirectory = new File("in/");
- DescriptorReader reader = DescriptorSourceFactory
- .createDescriptorReader();
- reader.addDirectory(inputDirectory);
- Iterator<DescriptorFile> descriptorFiles = reader.readDescriptors();
- int processedDescriptors = 0, verifiedDescriptors = 0;
- while (descriptorFiles.hasNext()) {
- DescriptorFile descriptorFile = descriptorFiles.next();
- if (descriptorFile.getException() != null) {
- System.err.println("Could not read/parse descriptor file "
- + descriptorFile.getFileName() + ": "
- + descriptorFile.getException().getMessage());
- continue;
- }
- if (descriptorFile.getDescriptors() == null) {
- continue;
- }
- for (Descriptor descriptor : descriptorFile.getDescriptors()) {
- if (!(descriptor instanceof ServerDescriptor)) {
- continue;
- }
- ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor;
- boolean isVerified = true;
-
- /* Verify that the contained fingerprint is a hash of the signing
- * key. */
- String signingKeyString = serverDescriptor.getSigningKey();
- String fingerprintString =
- serverDescriptor.getFingerprint().toLowerCase();
- PEMReader pemReader = new PEMReader(new StringReader(
- signingKeyString));
- RSAPublicKey signingKey = (RSAPublicKey) pemReader.readObject();
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new ASN1OutputStream(baos).writeObject(
- new org.bouncycastle.asn1.pkcs.RSAPublicKey(
- signingKey.getModulus(),
- signingKey.getPublicExponent()).toASN1Primitive());
- byte[] pkcs = baos.toByteArray();
- byte[] signingKeyHashBytes = new byte[20];
- SHA1Digest sha1 = new SHA1Digest();
- sha1.update(pkcs, 0, pkcs.length);
- sha1.doFinal(signingKeyHashBytes, 0);
- String signingKeyHashString = Hex.encodeHexString(
- signingKeyHashBytes);
- if (!signingKeyHashString.equals(fingerprintString)) {
- System.out.println("In " + descriptorFile.getFile()
- + ", server descriptor "
- + serverDescriptor.getServerDescriptorDigest()
- + ", the calculated signing key hash "
- + signingKeyHashString
- + " does not match the contained fingerprint "
- + fingerprintString + "!");
- isVerified = false;
- }
-
- /* Verify that the router signature was created using the signing
- * key. */
- String serverDescriptorDigestString = serverDescriptor
- .getServerDescriptorDigest().toLowerCase();
- String routerSignatureString = serverDescriptor
- .getRouterSignature();
- byte[] routerSignature = Base64
- .decodeBase64(routerSignatureString.substring(
- 0 + "-----BEGIN SIGNATURE-----\n".length(),
- routerSignatureString.length()
- - "-----END SIGNATURE-----\n".length()).replaceAll(
- "\n", ""));
- RSAKeyParameters rsakp = new RSAKeyParameters(false,
- signingKey.getModulus(), signingKey.getPublicExponent());
- PKCS1Encoding pe = new PKCS1Encoding(new RSAEngine());
- pe.init(false, rsakp);
- byte[] decryptedSignature = pe.processBlock(routerSignature, 0,
- routerSignature.length);
- String decryptedSignatureString =
- Hex.encodeHexString(decryptedSignature);
- if (!decryptedSignatureString.equals(
- serverDescriptorDigestString)) {
- System.out.println("In " + descriptorFile.getFile()
- + ", server descriptor "
- + serverDescriptor.getServerDescriptorDigest()
- + ", the decrypted signature "
- + decryptedSignatureString
- + " does not match the descriptor digest "
- + serverDescriptorDigestString + "!");
- isVerified = false;
- }
-
- processedDescriptors++;
- if (isVerified) {
- verifiedDescriptors++;
- }
- }
- }
- System.out.println("Verified " + verifiedDescriptors + "/"
- + processedDescriptors + " server descriptors.");
- }
-}
-
diff --git a/task-2768/run.sh b/task-2768/run.sh
index ed5fcc4..658ed6a 100755
--- a/task-2768/run.sh
+++ b/task-2768/run.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-javac -cp descriptor.jar:commons-codec-1.4.jar:commons-compress-1.3.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar VerifyServerDescriptors.java && java -cp descriptor.jar:commons-codec-1.4.jar:commons-compress-1.3.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar:. VerifyServerDescriptors
+javac -cp descriptor.jar:commons-codec-1.4.jar:commons-compress-1.3.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar VerifyDescriptors.java && java -cp descriptor.jar:commons-codec-1.4.jar:commons-compress-1.3.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar:. VerifyDescriptors
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits