Skip to content
Snippets Groups Projects
Commit 373d56f8 authored by Damian Johnson's avatar Damian Johnson
Browse files

Support multiple 'id' lines in microdescriptors

With the ed25519 additions the 'id' lines of microdescriptors can now appear
multiple times...

  https://gitweb.torproject.org/torspec.git/commit/?id=09ff9e2
parent 66e39330
No related branches found
No related tags found
No related merge requests found
......@@ -56,6 +56,7 @@ The following are only available within Stem's `git repository
* Support for ed25519 descriptor fields (:spec:`5a79d67`)
* Server descriptor validation fails with 'extra-info-digest line had an invalid value' from additions in proposal 228 (:trac:`16227`)
* :class:`~stem.descriptor.server_descriptor.BridgeDescriptor` now has 'ntor_onion_key' like its unsanitized counterparts
* Replaced the :class:`~stem.descriptor.microdescriptor.Microdescriptor` identifier and identifier_type attributes with an identifiers hash since it can now appear multiple times (:spec:`09ff9e2`)
* **Website**
......
......@@ -73,6 +73,7 @@ from stem.descriptor import (
_get_descriptor_components,
_read_until_keywords,
_value,
_values,
_parse_simple_line,
_parse_key_block,
)
......@@ -159,14 +160,24 @@ def _parse_file(descriptor_file, validate = False, **kwargs):
def _parse_id_line(descriptor, entries):
value = _value('id', entries)
value_comp = value.split()
identities = {}
if len(value_comp) >= 2:
descriptor.identifier_type = value_comp[0]
descriptor.identifier = value_comp[1]
for entry in _values('id', entries):
entry_comp = entry.split()
if len(entry_comp) >= 2:
key_type, key_value = entry_comp[0], entry_comp[1]
if key_type in identities:
raise ValueError("There can only be one 'id' line per a key type, but '%s' appeared multiple times" % key_type)
descriptor.identifier_type = key_type
descriptor.identifier = key_value
identities[key_type] = key_value
else:
raise ValueError("'id' lines should contain both the key type and digest: id %s" % value)
raise ValueError("'id' lines should contain both the key type and digest: id %s" % entry)
descriptor.identifiers = identities
_parse_digest = lambda descriptor, entries: setattr(descriptor, 'digest', hashlib.sha256(descriptor.get_bytes()).hexdigest().upper())
......@@ -192,13 +203,23 @@ class Microdescriptor(Descriptor):
:var list family: **\*** nicknames or fingerprints of declared family
:var stem.exit_policy.MicroExitPolicy exit_policy: **\*** relay's exit policy
:var stem.exit_policy.MicroExitPolicy exit_policy_v6: **\*** exit policy for IPv6
:var str identifier_type: identity digest key type
:var str identifier: base64 encoded identity digest, this is only used for collision prevention (:trac:`11743`)
:var hash identifiers: mapping of key types (like rsa1024 or ed25519) to
their base64 encoded identity, this is only used for collision prevention
(:trac:`11743`)
:var str identifier: base64 encoded identity digest (**deprecated**, use
identifiers instead)
:var str identifier_type: identity digest key type (**deprecated**, use
identifiers instead)
**\*** attribute is required when we're parsed with validation
.. versionchanged:: 1.1.0
Added the identifier and identifier_type attributes.
.. versionchanged:: 1.5.0
Added the identifiers attribute, and deprecated identifier and
identifier_type since the field can now appear multiple times.
"""
ATTRIBUTES = {
......@@ -208,8 +229,9 @@ class Microdescriptor(Descriptor):
'family': ([], _parse_family_line),
'exit_policy': (stem.exit_policy.MicroExitPolicy('reject 1-65535'), _parse_p_line),
'exit_policy_v6': (None, _parse_p6_line),
'identifier_type': (None, _parse_id_line),
'identifier': (None, _parse_id_line),
'identifier_type': (None, _parse_id_line), # deprecated in favor of identifiers
'identifier': (None, _parse_id_line), # deprecated in favor of identifiers
'identifiers': ({}, _parse_id_line),
'digest': (None, _parse_digest),
}
......
......@@ -420,7 +420,7 @@ class ServerDescriptor(Descriptor):
:var str address: **\*** IPv4 address of the relay
:var int or_port: **\*** port used for relaying
:var int socks_port: **\*** port used as client (deprecated, always **None**)
:var int socks_port: **\*** port used as client (**deprecated**, always **None**)
:var int dir_port: **\*** port used for descriptor mirroring
:var bytes platform: line with operating system and tor version
......@@ -442,13 +442,13 @@ class ServerDescriptor(Descriptor):
:var bool allow_single_hop_exits: **\*** flag if single hop exiting is allowed
:var bool extra_info_cache: **\*** flag if a mirror for extra-info documents
:var str extra_info_digest: upper-case hex encoded digest of our extra-info document
:var bool eventdns: flag for evdns backend (deprecated, always unset)
:var bool eventdns: flag for evdns backend (**deprecated**, always unset)
:var str ntor_onion_key: base64 key used to encrypt EXTEND in the ntor protocol
:var list or_addresses: **\*** alternative for our address/or_port
attributes, each entry is a tuple of the form (address (**str**), port
(**int**), is_ipv6 (**bool**))
Deprecated, moved to extra-info descriptor...
**Deprecated**, moved to extra-info descriptor...
:var datetime read_history_end: end of the sampling interval
:var int read_history_interval: seconds per interval
......
......@@ -95,6 +95,7 @@ class TestMicrodescriptor(unittest.TestCase):
self.assertEqual([], desc.family)
self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), desc.exit_policy)
self.assertEqual(None, desc.exit_policy_v6)
self.assertEqual({}, desc.identifiers)
self.assertEqual(None, desc.identifier_type)
self.assertEqual(None, desc.identifier)
self.assertEqual([], desc.get_unrecognized_lines())
......@@ -170,5 +171,32 @@ class TestMicrodescriptor(unittest.TestCase):
"""
desc = get_microdescriptor({'id': 'rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4'})
self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4'}, desc.identifiers)
self.assertEqual('rsa1024', desc.identifier_type)
self.assertEqual('Cd47okjCHD83YGzThGBDptXs9Z4', desc.identifier)
# check when there's multiple key types
desc_text = b'\n'.join((get_microdescriptor(content = True),
b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
b'id ed25519 50f6ddbecdc848dcc6b818b14d1'))
desc = Microdescriptor(desc_text, validate = True)
self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4', 'ed25519': '50f6ddbecdc848dcc6b818b14d1'}, desc.identifiers)
self.assertEqual('ed25519', desc.identifier_type)
self.assertEqual('50f6ddbecdc848dcc6b818b14d1', desc.identifier)
# check when there's conflicting keys
desc_text = b'\n'.join((get_microdescriptor(content = True),
b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
b'id rsa1024 50f6ddbecdc848dcc6b818b14d1'))
desc = Microdescriptor(desc_text)
self.assertEqual({}, desc.identifiers)
try:
Microdescriptor(desc_text, validate = True)
self.fail('constructor validation should fail')
except ValueError as exc:
self.assertEqual("There can only be one 'id' line per a key type, but 'rsa1024' appeared multiple times", str(exc))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment