[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Add initial support for parsing v3 addresses.
commit f815223fc2e7d2e460c18a90d66d0b4128eb9f23
Author: George Kadianakis <desnacked@xxxxxxxxxx>
Date: Tue Sep 3 14:03:04 2019 +0300
Add initial support for parsing v3 addresses.
---
stem/descriptor/hidden_service.py | 10 ++++++-
stem/descriptor/hsv3_crypto.py | 57 +++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index 52e1b0b1..e6809306 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -526,10 +526,18 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
def create(cls, attr = None, exclude = (), validate = True, sign = False):
return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = not sign)
- def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
+ def __init__(self, raw_contents, validate = False, onion_address = None, skip_crypto_validation = False):
+ """
+ The onion_address is needed so that we can decrypt the descriptor, which is
+ impossible without the full onion address.
+ """
super(HiddenServiceDescriptorV3, self).__init__(raw_contents, lazy_load = not validate)
entries = _descriptor_components(raw_contents, validate)
+ if onion_address == None:
+ raise ValueError("The onion address MUST be provided to parse a V3 descriptor")
+ self.onion_address = onion_address
+
if validate:
for keyword in REQUIRED_V3_FIELDS:
if keyword not in entries:
diff --git a/stem/descriptor/hsv3_crypto.py b/stem/descriptor/hsv3_crypto.py
new file mode 100644
index 00000000..0feb0fe0
--- /dev/null
+++ b/stem/descriptor/hsv3_crypto.py
@@ -0,0 +1,57 @@
+import base64
+import hashlib
+
+from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.backends import default_backend
+
+
+"""
+Onion addresses
+
+ onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
+ CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
+
+ - PUBKEY is the 32 bytes ed25519 master pubkey of the hidden service.
+ - VERSION is an one byte version field (default value '\x03')
+ - ".onion checksum" is a constant string
+ - CHECKSUM is truncated to two bytes before inserting it in onion_address
+
+"""
+
+CHECKSUM_CONSTANT = b".onion checksum"
+
+def decode_address(onion_address_str):
+ """
+ Parse onion_address_str and return the pubkey.
+
+ onion_address = base32(PUBKEY | CHECKSUM | VERSION) + ".onion"
+ CHECKSUM = H(".onion checksum" | PUBKEY | VERSION)[:2]
+
+ :return: Ed25519PublicKey
+
+ :raises: ValueError
+ """
+ if (len(onion_address_str) != 56 + len(".onion")):
+ raise ValueError("Wrong address length")
+
+ # drop the '.onion'
+ onion_address = onion_address_str[:56]
+
+ # base32 decode the addr (convert to uppercase since that's what python expects)
+ onion_address = base64.b32decode(onion_address.upper())
+ assert(len(onion_address) == 35)
+
+ # extract pieces of information
+ pubkey = onion_address[:32]
+ checksum = onion_address[32:34]
+ version = onion_address[34]
+
+ # Do checksum validation
+ my_checksum_body = b"%s%s%s" % (CHECKSUM_CONSTANT, pubkey, bytes([version]))
+ my_checksum = hashlib.sha3_256(my_checksum_body).digest()
+
+ if (checksum != my_checksum[:2]):
+ raise ValueError("Bad checksum")
+
+ return Ed25519PublicKey.from_public_bytes(pubkey)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits