commit 63a476056017dda5ede35efc4e4f7acfcc1d7d1a Author: Damian Johnson atagar@torproject.org Date: Tue Feb 9 16:12:36 2021 -0800
Adjust extended v3 HS address validation
Mostly minor stylistic adjustments. The only functional bit is correcting a unit test failure...
====================================================================== FAIL: test_identity_key_from_address ---------------------------------------------------------------------- ValueError: '55555555555555555555555555555555555555555555555555555555.onion' isn't a valid hidden service v3 address
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "/home/atagar/Desktop/stem/test/unit/descriptor/hidden_service_v3.py", line 220, in test_identity_key_from_address self.assertRaisesWith(ValueError, 'Bad checksum (expected def7 but was 842e)', HiddenServiceDescriptorV3.identity_key_from_address, '5' * 56) File "/home/atagar/Desktop/stem/stem/util/test_tools.py", line 249, in assertRaisesWith return self.assertRaisesRegexp(exc_type, '^%s$' % re.escape(exc_msg), *args, **kwargs) AssertionError: "^Bad\ checksum\ (expected\ def7\ but\ was\ 842e)$" does not match "'55555555555555555555555555555555555555555555555555555555.onion' isn't a valid hidden service v3 address"
---------------------------------------------------------------------- --- stem/descriptor/hidden_service.py | 2 +- stem/util/tor_tools.py | 35 +++++++++++++---------------- test/unit/util/tor_tools.py | 46 ++++++++++++++++++--------------------- 3 files changed, 37 insertions(+), 46 deletions(-)
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py index 63f107e7..249c2153 100644 --- a/stem/descriptor/hidden_service.py +++ b/stem/descriptor/hidden_service.py @@ -1129,7 +1129,7 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor): if onion_address.endswith('.onion'): onion_address = onion_address[:-6]
- if not stem.util.tor_tools.is_valid_hidden_service_address(onion_address, version = 3): + if not stem.util.tor_tools.HS_V3_ADDRESS_PATTERN.match(onion_address): raise ValueError("'%s.onion' isn't a valid hidden service v3 address" % onion_address)
# onion_address = base32(PUBKEY | CHECKSUM | VERSION) + '.onion' diff --git a/stem/util/tor_tools.py b/stem/util/tor_tools.py index c9a04e33..e758b897 100644 --- a/stem/util/tor_tools.py +++ b/stem/util/tor_tools.py @@ -19,9 +19,9 @@ Miscellaneous utility functions for working with tor. is_hex_digits - checks if a string is only made up of hex digits """
+import base64 +import hashlib import re -from base64 import b32decode -from hashlib import sha3_256
import stem.util.str_tools
@@ -47,7 +47,6 @@ CIRC_ID_PATTERN = re.compile('^[a-zA-Z0-9]{1,16}$')
HS_V2_ADDRESS_PATTERN = re.compile('^[a-z2-7]{16}$') HS_V3_ADDRESS_PATTERN = re.compile('^[a-z2-7]{56}$') -HS_V3_CHECKSUM_CONSTANT = ".onion checksum"
def is_valid_fingerprint(entry: str, check_prefix: bool = False) -> bool: @@ -156,14 +155,6 @@ def is_valid_hidden_service_address(entry: str, version: Optional[Union[int, Seq otherwise """
- def _extract_v3_parts(address): - decoded = b32decode(address.upper()) - return (decoded[:32], decoded[32:34]) - - v3_pubkey = None - v3_checksum = None - v3_version = int(3).to_bytes(1, 'little') - if isinstance(entry, bytes): entry = stem.util.str_tools._to_unicode(entry)
@@ -179,15 +170,19 @@ def is_valid_hidden_service_address(entry: str, version: Optional[Union[int, Seq return True
if 3 in version and bool(HS_V3_ADDRESS_PATTERN.match(entry)): - # v3+ onions have a consistent version at end of address - if not entry.endswith('d'): - return False - # Test that the checksum (part of every v3 address) is valid - v3_pubkey, v3_checksum = _extract_v3_parts(entry) - expected_checksum = sha3_256(HS_V3_CHECKSUM_CONSTANT.encode('utf-8') + v3_pubkey + v3_version).digest()[:2] - if expected_checksum != v3_checksum: - return False - return True + # onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion" + + decoded = base64.b32decode(entry.upper()) + pubkey, checksum, addr_version = decoded[:32], decoded[32:34], decoded[34:] + + if addr_version != b'\x03': + return False # VERSION component must be three + + # CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2] + + expected_checksum = hashlib.sha3_256(b'.onion checksum' + pubkey + addr_version).digest()[:2] + + return checksum == expected_checksum
return False except TypeError: diff --git a/test/unit/util/tor_tools.py b/test/unit/util/tor_tools.py index 285b4dde..06707390 100644 --- a/test/unit/util/tor_tools.py +++ b/test/unit/util/tor_tools.py @@ -3,66 +3,62 @@ Unit tests for the stem.util.tor_tools functions. """
import unittest -import os
import stem.util.str_tools import stem.util.tor_tools
class TestTorTools(unittest.TestCase): - - def test_is_valid_hidden_service_address(self): + def test_is_valid_hidden_service_address_v2(self): """ - Check hidden service addresses are valid (no .onion) + Checks the is_valid_hidden_service_address for v2 addresses. """ + valid_v2_addresses = [ 'facebookcorewwwi', 'aaaaaaaaaaaaaaaa', ] + invalid_v2_addresses = [ 'facebookcorewww', 'facebookcorewwyi' 'facebookc0rewwwi', 'facebookcorew wi', - None, - 0, - 1, - -1, - os.urandom(8) ]
for address in valid_v2_addresses: self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address)) - self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address, version=2)) + self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address, version = 2))
for address in invalid_v2_addresses: self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address)) - self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address, version=2)) + self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address, version = 2)) + + def test_is_valid_hidden_service_address_v3(self): + """ + Checks the is_valid_hidden_service_address for v3 addresses. + """
- # Test version 3 addresses valid_v3_addresses = [ 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd', 'sp3k262uwy4r2k3ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd', - 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jyd' - ] + 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jyd', + ] + invalid_v3_addresses = [ - 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryc', # bad version - 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jy', # too short - 'sp3k262uwy4r2k4ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd', # checksum mismatch - 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscrybd', # too long - None, - 0, - 1, - -1, - os.urandom(56) + 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryc', # bad version + 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jy', # too short + 'sp3k262uwy4r2k4ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd', # checksum mismatch + 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscrybd', # too long ]
for address in valid_v3_addresses: self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address)) - self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address, version=3)) + self.assertTrue(stem.util.tor_tools.is_valid_hidden_service_address(address, version = 3)) + for address in invalid_v3_addresses: self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address)) - self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address, version=3)) + self.assertFalse(stem.util.tor_tools.is_valid_hidden_service_address(address, version = 3))
def test_is_valid_fingerprint(self): """