commit e7e03d2f61d6dcc7bc5e5ad4dee91c37a814ee16 Author: Damian Johnson atagar@torproject.org Date: Wed May 23 09:52:23 2012 -0700
Supporting bridge descriptor's router-digest field
Scrubbed bridge server and extra-info descriptors will soon have a 'router-digest' line which contains the server digest. This is a value that we can't calculate for ourselves because, of course, the descriptor's contents have been modified.
I'm flagging this as being a required field. This does not yet parse it in extra-info descriptors since it neither has a digest() method nor did I realize that bridge extra-info descriptors were special. --- stem/descriptor/server_descriptor.py | 52 ++++++++++++++++++-------- test/integ/descriptor/data/bridge_descriptor | 1 + test/integ/descriptor/server_descriptor.py | 1 + test/unit/descriptor/server_descriptor.py | 41 ++++++++++++++++++++- 4 files changed, 78 insertions(+), 17 deletions(-)
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py index 9a91d60..0014ff5 100644 --- a/stem/descriptor/server_descriptor.py +++ b/stem/descriptor/server_descriptor.py @@ -11,13 +11,13 @@ etc). This information is provided from a few sources... parse_file - Iterates over the server descriptors in a file. ServerDescriptor - Tor server descriptor. | |- RelayDescriptor - Server descriptor for a relay. - | | |- is_valid - checks the signature against the descriptor content - | | +- digest - calculates the digest value for our content + | | +- is_valid - checks the signature against the descriptor content | | | +- BridgeDescriptor - Scrubbed server descriptor for a bridge. | |- is_scrubbed - checks if our content has been properly scrubbed | +- get_scrubbing_issues - description of issues with our scrubbing | + |- digest - calculates the digest value for our content |- get_unrecognized_lines - lines with unrecognized content |- get_annotations - dictionary of content prior to the descriptor entry +- get_annotation_lines - lines that provided the annotations @@ -248,6 +248,21 @@ class ServerDescriptor(stem.descriptor.Descriptor): self._parse(entries, validate) if validate: self._check_constraints(entries, first_keyword, last_keyword)
+ def digest(self): + """ + Provides the base64 encoded sha1 of our content. This value is part of the + server descriptor entry for this relay. + + Note that network status entries exclude the padding, so you'll need to add + a '=' to it so they'll match... + https://en.wikipedia.org/wiki/Base64#Padding + + Returns: + str with the digest value for this server descriptor + """ + + raise NotImplementedError("Unsupported Operation: this should be implemented by the ServerDescriptor subclass") + def get_unrecognized_lines(self): return list(self._unrecognized_lines)
@@ -566,18 +581,6 @@ class RelayDescriptor(ServerDescriptor): raise NotImplementedError # TODO: implement
def digest(self): - """ - Provides the base64 encoded sha1 of our content. This value is part of the - server descriptor entry for this relay. - - Note that network status entries exclude the padding, so you'll need to add - a '=' to it so they'll match... - https://en.wikipedia.org/wiki/Base64#Padding - - Returns: - str with the digest value for this server descriptor - """ - if self._digest is None: # our digest is calculated from everything except our signature raw_content, ending = str(self), "\nrouter-signature\n" @@ -642,15 +645,28 @@ class BridgeDescriptor(ServerDescriptor):
def __init__(self, raw_contents, validate = True, annotations = None): self.address_alt = [] + self._digest = None self._scrubbing_issues = None ServerDescriptor.__init__(self, raw_contents, validate, annotations)
+ def digest(self): + return self._digest + def _parse(self, entries, validate): entries = dict(entries)
# handles fields only in bridge descriptors for keyword, values in entries.items(): - if keyword == "or-address": + value, block_contents = values[0] + line = "%s %s" % (keyword, value) + + if keyword == "router-digest": + if validate and not re.match("^[0-9a-fA-F]{40}$", value): + raise ValueError("Router digest line had an invalid sha1 digest: %s" % line) + + self._digest = value + del entries["router-digest"] + elif keyword == "or-address": or_address_entries = [value for (value, _) in values]
for entry in or_address_entries: @@ -741,7 +757,11 @@ class BridgeDescriptor(ServerDescriptor): "router-signature", )
- return filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS) + included_fields = ( + "router-digest", + ) + + return included_fields + filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS)
def _single_fields(self): return self._required_fields() + SINGLE_FIELDS diff --git a/test/integ/descriptor/data/bridge_descriptor b/test/integ/descriptor/data/bridge_descriptor index 07dffe2..b7dbd92 100644 --- a/test/integ/descriptor/data/bridge_descriptor +++ b/test/integ/descriptor/data/bridge_descriptor @@ -1,5 +1,6 @@ @type bridge-server-descriptor 1.0 router Unnamed 10.45.227.253 9001 0 0 +router-digest 006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4 or-address [fd9f:2e19:3bcf::02:9970]:9001 platform Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64 opt protocols Link 1 2 Circuit 1 diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py index afe3c93..180e82c 100644 --- a/test/integ/descriptor/server_descriptor.py +++ b/test/integ/descriptor/server_descriptor.py @@ -307,5 +307,6 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEquals(819200, desc.burst_bandwidth) self.assertEquals(5120, desc.observed_bandwidth) self.assertEquals(["reject *:*"], desc.exit_policy) + self.assertEquals("006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4", desc.digest()) self.assertEquals([], desc.get_unrecognized_lines())
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py index e381318..0eb4891 100644 --- a/test/unit/descriptor/server_descriptor.py +++ b/test/unit/descriptor/server_descriptor.py @@ -27,6 +27,7 @@ RELAY_DESCRIPTOR_ATTR = (
BRIDGE_DESCRIPTOR_ATTR = ( ("router", "Unnamed 10.45.227.253 9001 0 0"), + ("router-digest", "006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4"), ("published", "2012-03-22 17:34:38"), ("bandwidth", "409600 819200 5120"), ("reject", "*:*"), @@ -319,6 +320,7 @@ class TestServerDescriptor(unittest.TestCase): self.assertEquals("Unnamed", desc.nickname) self.assertEquals("10.45.227.253", desc.address) self.assertEquals(None, desc.fingerprint) + self.assertEquals("006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4", desc.digest())
# check that we don't have crypto fields self.assertRaises(AttributeError, getattr, desc, "onion_key") @@ -352,10 +354,47 @@ class TestServerDescriptor(unittest.TestCase): its unsanatized content. """
- desc_text = _make_descriptor() + desc_text = _make_descriptor({"router-digest": "006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4"}) desc = BridgeDescriptor(desc_text) self.assertFalse(desc.is_scrubbed())
+ def test_router_digest(self): + """ + Constructs with a router-digest line with both valid and invalid contents. + """ + + # checks with valid content + + router_digest = "068A2E28D4C934D9490303B7A645BA068DCA0504" + desc_text = _make_descriptor({"router-digest": router_digest}, is_bridge = True) + desc = BridgeDescriptor(desc_text) + self.assertEquals(router_digest, desc.digest()) + + # checks when missing + + desc_text = _make_descriptor(exclude = ["router-digest"], is_bridge = True) + self.assertRaises(ValueError, BridgeDescriptor, desc_text) + + # check that we can still construct it without validation + desc = BridgeDescriptor(desc_text, validate = False) + self.assertEquals(None, desc.digest()) + + # checks with invalid content + + test_values = ( + "", + "006FD96BA35E7785A6A3B8B75FE2E2435A13BDB44", + "006FD96BA35E7785A6A3B8B75FE2E2435A13BDB", + "006FD96BA35E7785A6A3B8B75FE2E2435A13BDBH", + ) + + for value in test_values: + desc_text = _make_descriptor({"router-digest": value}, is_bridge = True) + self.assertRaises(ValueError, BridgeDescriptor, desc_text) + + desc = BridgeDescriptor(desc_text, validate = False) + self.assertEquals(value, desc.digest()) + def test_or_address_v4(self): """ Constructs a bridge descriptor with a sanatized IPv4 or-address entry.
tor-commits@lists.torproject.org