[tor-commits] [stem/master] Implementing calculate_digest() for server descriptors
atagar at torproject.org
atagar at torproject.org
Sun Apr 15 02:50:21 UTC 2012
commit 8c1b5b6053e08bce55b9bc935daa492f727c68e8
Author: Damian Johnson <atagar at torproject.org>
Date: Fri Apr 13 22:19:33 2012 -0700
Implementing calculate_digest() for server descriptors
Implementation and test for a function to get the digest value for a relay
server descriptor. This is the same value found in the network status entry.
This was an addition suggested by Karsten.
---
stem/descriptor/server_descriptor.py | 28 ++++++++++++++
test/integ/descriptor/server_descriptor.py | 57 ++++++++++++++++++++++++++-
2 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 0a6cd29..9abe06b 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -20,6 +20,8 @@ ServerDescriptorV3 - Tor server descriptor, version 3.
"""
import re
+import base64
+import hashlib
import datetime
import stem.descriptor
@@ -542,6 +544,7 @@ class RelayDescriptorV3(ServerDescriptorV3):
self.onion_key = None
self.signing_key = None
self.signature = None
+ self._digest = None
ServerDescriptorV3.__init__(self, raw_contents, validate, annotations)
@@ -555,6 +558,31 @@ class RelayDescriptorV3(ServerDescriptorV3):
raise NotImplementedError # TODO: implement
+ def calculate_digest(self):
+ """
+ Provides the base64 encoded sha1 of our content. This value is part of the
+ server descriptor entry for this relay.
+
+ Returns:
+ str with the digest value for this server descriptor
+ """
+
+ if self._digest == None:
+ # our digest is calculated from everything except our signature
+ raw_content, ending = str(self), "\nrouter-signature\n"
+ raw_content = raw_content[:raw_content.find(ending) + len(ending)]
+
+ digest_sha1 = hashlib.sha1(raw_content).digest()
+ digest = base64.b64encode(digest_sha1)
+
+ # TODO: I'm not sure why but the base64 decodings have an anomalous '='
+ # ending which the network status entries don't have. Tad puzzled, but
+ # for now stripping it so we match.
+
+ self._digest = digest[:-1]
+
+ return self._digest
+
def _parse(self, entries, validate):
entries = dict(entries) # shallow copy since we're destructive
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index 19b2a6d..c4265d7 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -6,6 +6,7 @@ import os
import datetime
import unittest
+import stem.control
import stem.version
import stem.descriptor.server_descriptor
import test.runner
@@ -19,6 +20,18 @@ DESCRIPTOR_TEST_DATA = os.path.join(my_dir, "data")
RAN_CACHED_DESCRIPTOR_TEST = False
class TestServerDescriptor(unittest.TestCase):
+ is_descriptors_available = None
+
+ def setUp(self):
+ # If this is our first time running the integ tests and we didn't wait for
+ # a full tor initialization then the cached descriptors won't exist yet.
+ # Noting if they exist or not since some tests need them.
+
+ if self.is_descriptors_available == None:
+ test_dir = test.runner.get_runner().get_test_dir()
+ descriptor_path = os.path.join(test_dir, "cached-descriptors")
+ self.is_descriptors_available = os.path.exists(descriptor_path)
+
def test_metrics_descriptor(self):
"""
Parses and checks our results against a server descriptor from metrics.
@@ -100,9 +113,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
descriptor_path = os.path.join(test.runner.get_runner().get_test_dir(), "cached-descriptors")
- # if this is our first time running the integ tests and we didn't wait for
- # a full tor initialization then the cached descriptors won't exist yet
- if not os.path.exists(descriptor_path):
+ if not self.is_descriptors_available:
self.skipTest("(no cached descriptors)")
global RAN_CACHED_DESCRIPTOR_TEST
@@ -217,4 +228,44 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
self.assertEquals(5120, desc.observed_bandwidth)
self.assertEquals(["reject *:*"], desc.exit_policy)
self.assertEquals([], desc.get_unrecognized_lines())
+
+ def test_calculate_digest(self):
+ """
+ Checks that the digest for a descriptor matches its consensus digest value.
+ """
+
+ # TODO: Remove manual parsing with proper objects when we have them...
+ # - parsing of consensus_digest with the NetworkStatus class
+ # - low level msg() calls with Controller
+
+ if not self.is_descriptors_available:
+ self.skipTest("(no cached descriptors)")
+
+ with test.runner.get_runner().get_tor_socket() as control_socket:
+ controller = stem.control.BaseController(control_socket)
+
+ # picking one of the directory authorities (gabelmoo) since they're
+ # pretty stable and this is trivial to revise if they change
+
+ fingerprint = "F2044413DAC2E02E3D6BCF4735A19BCA1DE97281"
+ desc_entry = controller.msg("GETINFO desc/id/%s" % fingerprint)
+ ns_entry = controller.msg("GETINFO ns/id/%s" % fingerprint)
+
+ # parse the consensus digest from the ns_entry
+ consensus_digest = None
+ for line in str(ns_entry).split("\n"):
+ if line.startswith("r "):
+ consensus_digest = line.split()[3]
+ break
+
+ if not consensus_digest:
+ self.fail("Malformed network descriptor: %s" % ns_entry)
+
+ # parse the descriptor content from the desc_entry
+
+ desc_content = list(desc_entry)[0]
+ desc_content = desc_content[desc_content.find("=\n") + 2:]
+
+ desc = stem.descriptor.server_descriptor.RelayDescriptorV3(desc_content)
+ self.assertEquals(consensus_digest, desc.calculate_digest())
More information about the tor-commits
mailing list