commit 7bac71f5952a36ba4a2dffcc20c7a79f4fb7d7be Author: Damian Johnson atagar@torproject.org Date: Wed Mar 29 20:57:31 2017 -0700
Python3 support for ed25519
Handful of tweaks to get this working with python3. --- stem/descriptor/certificate.py | 21 +++++++++++---------- stem/descriptor/server_descriptor.py | 2 +- test/unit/descriptor/certificate.py | 15 ++++++++------- test/unit/descriptor/server_descriptor.py | 6 +++--- 4 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py index d9a36f3..cd7710c 100644 --- a/stem/descriptor/certificate.py +++ b/stem/descriptor/certificate.py @@ -57,6 +57,7 @@ used to validate the key used to sign server descriptors. """
import base64 +import binascii import collections import datetime import hashlib @@ -111,14 +112,14 @@ class Ed25519Certificate(object): """
try: - decoded = base64.b64decode(content) + decoded = base64.b64decode(stem.util.str_tools._to_bytes(content))
if not decoded: raise TypeError('empty') - except TypeError as exc: + except (TypeError, binascii.Error) as exc: raise ValueError("Ed25519 certificate wasn't propoerly base64 encoded (%s):\n%s" % (exc, content))
- version = stem.util.str_tools._to_int(decoded[0]) + version = stem.util.str_tools._to_int(decoded[0:1])
if version == 1: return Ed25519CertificateV1(version, content, decoded) @@ -145,7 +146,7 @@ class Ed25519CertificateV1(Ed25519Certificate): if len(decoded) < ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH: raise ValueError('Ed25519 certificate was %i bytes, but should be at least %i' % (len(decoded), ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH))
- cert_type = stem.util.str_tools._to_int(decoded[1]) + cert_type = stem.util.str_tools._to_int(decoded[1:2])
if cert_type in (0, 1, 2, 3): raise ValueError('Ed25519 certificate cannot have a type of %i. This is reserved to avoid conflicts with tor CERTS cells.' % cert_type) @@ -163,12 +164,12 @@ class Ed25519CertificateV1(Ed25519Certificate): # expiration time is in hours since epoch self.expiration = datetime.datetime.utcfromtimestamp(stem.util.str_tools._to_int(decoded[2:6]) * 3600)
- self.key_type = stem.util.str_tools._to_int(decoded[6]) + self.key_type = stem.util.str_tools._to_int(decoded[6:7]) self.key = decoded[7:39] self.signature = decoded[-ED25519_SIGNATURE_LENGTH:]
self.extensions = [] - extension_count = stem.util.str_tools._to_int(decoded[39]) + extension_count = stem.util.str_tools._to_int(decoded[39:40]) remaining_data = decoded[40:-ED25519_SIGNATURE_LENGTH]
for i in range(extension_count): @@ -176,8 +177,8 @@ class Ed25519CertificateV1(Ed25519Certificate): raise ValueError('Ed25519 extension is missing header field data')
extension_length = stem.util.str_tools._to_int(remaining_data[:2]) - extension_type = stem.util.str_tools._to_int(remaining_data[2]) - extension_flags = stem.util.str_tools._to_int(remaining_data[3]) + extension_type = stem.util.str_tools._to_int(remaining_data[2:3]) + extension_flags = stem.util.str_tools._to_int(remaining_data[3:4]) extension_data = remaining_data[4:4 + extension_length]
if extension_length != len(extension_data): @@ -234,7 +235,7 @@ class Ed25519CertificateV1(Ed25519Certificate): signing_key = None
if server_descriptor.ed25519_master_key: - signing_key = nacl.signing.VerifyKey(server_descriptor.ed25519_master_key + '=', encoder = nacl.encoding.Base64Encoder) + signing_key = nacl.signing.VerifyKey(stem.util.str_tools._to_bytes(server_descriptor.ed25519_master_key) + b'=', encoder = nacl.encoding.Base64Encoder) else: for extension in self.extensions: if extension.type == ExtensionType.HAS_SIGNING_KEY: @@ -245,7 +246,7 @@ class Ed25519CertificateV1(Ed25519Certificate): raise ValueError('Server descriptor missing an ed25519 signing key')
try: - signing_key.verify(base64.b64decode(self.encoded)[:-ED25519_SIGNATURE_LENGTH], self.signature) + signing_key.verify(base64.b64decode(stem.util.str_tools._to_bytes(self.encoded))[:-ED25519_SIGNATURE_LENGTH], self.signature) except BadSignatureError as exc: raise ValueError('Ed25519KeyCertificate signing key is invalid (%s)' % exc)
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py index 35b1303..1cedbe5 100644 --- a/stem/descriptor/server_descriptor.py +++ b/stem/descriptor/server_descriptor.py @@ -811,7 +811,7 @@ class RelayDescriptor(ServerDescriptor): """
signing_key_digest = hashlib.sha1(_bytes_for_block(self.signing_key)).digest() - data = signing_key_digest + base64.b64decode(self.ed25519_master_key + '=') + data = signing_key_digest + base64.b64decode(stem.util.str_tools._to_bytes(self.ed25519_master_key) + b'=') return stem.util.str_tools._to_unicode(binascii.hexlify(data).upper())
def _compare(self, other, method): diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py index a3bea04..669d9ad 100644 --- a/test/unit/descriptor/certificate.py +++ b/test/unit/descriptor/certificate.py @@ -8,6 +8,7 @@ import re import unittest
import stem.descriptor.certificate +import stem.util.str_tools import stem.prereq import test.runner
@@ -20,9 +21,9 @@ ptIr43bWPo2fIzo3uOywfoMrryprpbm4HhCkZMaO064LP+1KNuLvlc8sGG8lTjx1 g4k3ELuWYgHYWU5rAia7nl4gUfBZOEfHAfKES7l3d63dBEjEX98Ljhdp2w4= """.strip()
-EXPECTED_CERT_KEY = '\xa5\xb6\x1a\x80D\x0fR#cp:\x7f\xa1\x8d\xa8\x11%\xe4\x0f7|=\x99k\xdb\xa9\x1aG\xb9\xd4\x91\xaa' -EXPECTED_EXTENSION_DATA = 'g\xa6\xb5Q\xa6\xd2+\xe3v\xd6>\x8d\x9f#:7\xb8\xec\xb0~\x83+\xaf*k\xa5\xb9\xb8\x1e\x10\xa4d' -EXPECTED_SIGNATURE = '\xc6\x8e\xd3\xae\x0b?\xedJ6\xe2\xef\x95\xcf,\x18o%N<u\x83\x897\x10\xbb\x96b\x01\xd8YNk\x02&\xbb\x9e^ Q\xf0Y8G\xc7\x01\xf2\x84K\xb9ww\xad\xdd\x04H\xc4_\xdf\x0b\x8e\x17i\xdb\x0e' +EXPECTED_CERT_KEY = b'\xa5\xb6\x1a\x80D\x0fR#cp:\x7f\xa1\x8d\xa8\x11%\xe4\x0f7|=\x99k\xdb\xa9\x1aG\xb9\xd4\x91\xaa' +EXPECTED_EXTENSION_DATA = b'g\xa6\xb5Q\xa6\xd2+\xe3v\xd6>\x8d\x9f#:7\xb8\xec\xb0~\x83+\xaf*k\xa5\xb9\xb8\x1e\x10\xa4d' +EXPECTED_SIGNATURE = b'\xc6\x8e\xd3\xae\x0b?\xedJ6\xe2\xef\x95\xcf,\x18o%N<u\x83\x897\x10\xbb\x96b\x01\xd8YNk\x02&\xbb\x9e^ Q\xf0Y8G\xc7\x01\xf2\x84K\xb9ww\xad\xdd\x04H\xc4_\xdf\x0b\x8e\x17i\xdb\x0e'
def certificate(version = 1, cert_type = 4, extension_data = []): @@ -34,13 +35,13 @@ def certificate(version = 1, cert_type = 4, extension_data = []): :param list extension_data: extensions to embed within the certificate """
- return base64.b64encode(''.join([ - chr(version), - chr(cert_type), + return base64.b64encode(b''.join([ + stem.util.str_tools._to_bytes(chr(version)), + stem.util.str_tools._to_bytes(chr(cert_type)), b'\x00' * 4, # expiration date, leaving this as the epoch b'\x01', # key type b'\x03' * 32, # key - chr(len(extension_data)), # extension count + stem.util.str_tools._to_bytes(chr(len(extension_data))), # extension count b''.join(extension_data), b'\x01' * ED25519_SIGNATURE_LENGTH]))
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py index 38dcdce..42fa392 100644 --- a/test/unit/descriptor/server_descriptor.py +++ b/test/unit/descriptor/server_descriptor.py @@ -270,8 +270,8 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual(CertType.SIGNING, desc.certificate.type) self.assertEqual(datetime.datetime(2015, 8, 28, 17, 0, 0), desc.certificate.expiration) self.assertEqual(1, desc.certificate.key_type) - self.assertTrue(desc.certificate.key.startswith('\xa5\xb6\x1a\x80D\x0f')) - self.assertTrue(desc.certificate.signature.startswith('\xc6\x8e\xd3\xae\x0b')) + self.assertTrue(desc.certificate.key.startswith(b'\xa5\xb6\x1a\x80D\x0f')) + self.assertTrue(desc.certificate.signature.startswith(b'\xc6\x8e\xd3\xae\x0b')) self.assertEqual(1, len(desc.certificate.extensions)) self.assertTrue('bWPo2fIzo3uOywfoM' in desc.certificate.encoded)
@@ -279,7 +279,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= self.assertEqual(ExtensionType.HAS_SIGNING_KEY, extension.type) self.assertEqual([], extension.flags) self.assertEqual(0, extension.flag_int) - self.assertTrue(extension.data.startswith('g\xa6\xb5Q\xa6\xd2')) + self.assertTrue(extension.data.startswith(b'g\xa6\xb5Q\xa6\xd2'))
self.assertEqual('destiny', desc.nickname) self.assertEqual('F65E0196C94DFFF48AFBF2F5F9E3E19AAE583FD0', desc.fingerprint)