[tor-commits] [stem/master] Now validate Tor v3 address checksum in hidden service address validator
atagar at torproject.org
atagar at torproject.org
Wed Feb 10 00:15:02 UTC 2021
commit 434638a828201c286e5fef158761dc2c4c67e47d
Author: Kevin Froman <beardog at mailbox.org>
Date: Fri Feb 5 23:31:32 2021 +0000
Now validate Tor v3 address checksum in hidden service address validator
---
stem/util/tor_tools.py | 19 ++++++++++++++++
test/unit/util/tor_tools.py | 55 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+)
diff --git a/stem/util/tor_tools.py b/stem/util/tor_tools.py
index e2df4312..c9a04e33 100644
--- a/stem/util/tor_tools.py
+++ b/stem/util/tor_tools.py
@@ -20,6 +20,8 @@ Miscellaneous utility functions for working with tor.
"""
import re
+from base64 import b32decode
+from hashlib import sha3_256
import stem.util.str_tools
@@ -45,6 +47,7 @@ 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:
@@ -153,6 +156,14 @@ 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)
@@ -168,6 +179,14 @@ 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
return False
diff --git a/test/unit/util/tor_tools.py b/test/unit/util/tor_tools.py
index 5035d7b4..285b4dde 100644
--- a/test/unit/util/tor_tools.py
+++ b/test/unit/util/tor_tools.py
@@ -3,12 +3,67 @@ 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):
+ """
+ Check hidden service addresses are valid (no .onion)
+ """
+ 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))
+
+ 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))
+
+ # Test version 3 addresses
+ valid_v3_addresses = [
+ 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd',
+ 'sp3k262uwy4r2k3ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd',
+ 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jyd'
+ ]
+ invalid_v3_addresses = [
+ 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryc', # bad version
+ 'xa4r2iadxm55fbnqgwwi5mymqdcofiu3w6rpbtqn7b2dyn7mgwj64jy', # too short
+ 'sp3k262uwy4r2k4ycr5awluarykdpag6a7y33jxop4cs2lu5uz5sseqd', # checksum mismatch
+ 'pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscrybd', # too long
+ None,
+ 0,
+ 1,
+ -1,
+ os.urandom(56)
+ ]
+
+ 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))
+ 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))
+
def test_is_valid_fingerprint(self):
"""
Checks the is_valid_fingerprint function.
More information about the tor-commits
mailing list