commit 657b8acf95c83ec3836039bf1844d9e55c607956 Author: Damian Johnson atagar@torproject.org Date: Mon Feb 17 16:42:27 2020 -0800
Drop undocumented 'encoded' attribute from certificates
When an Ed25519Certificate was created through from_base64() we appended an undocumented 'encoded' attribute with the base64 content we were created from. Our to_base64() method computes the exact same thing.
This attribute was only used in a single place for server descriptor validation.
If 'encoded' and to_base64() mismatches that is a bug. Fiddling with these I cound't come up with a scenario where that is a case, so dropping the redundant attribute.
Also merging _validate_server_desc_signing_key() into its sole caller. --- stem/descriptor/certificate.py | 44 +++++++++++++------------------ test/unit/descriptor/certificate.py | 7 +++-- test/unit/descriptor/server_descriptor.py | 2 +- 3 files changed, 23 insertions(+), 30 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py index d7c0743d..ae2d6636 100644 --- a/stem/descriptor/certificate.py +++ b/stem/descriptor/certificate.py @@ -6,8 +6,8 @@ Parsing for `Tor Ed25519 certificates https://gitweb.torproject.org/torspec.git/tree/cert-spec.txt`_, which are used to for a variety of purposes...
- * validating the key used to sign server descriptors - * validating the key used to sign hidden service v3 descriptors + * validate the signing key of server descriptors + * validate the signing key of hidden service v3 descriptors * signing and encrypting hidden service v3 indroductory points
.. versionadded:: 1.6.0 @@ -185,9 +185,7 @@ class Ed25519Certificate(object): if not decoded: raise TypeError('empty')
- instance = Ed25519Certificate.unpack(decoded) - instance.encoded = content - return instance + return Ed25519Certificate.unpack(decoded) except (TypeError, binascii.Error) as exc: raise ValueError("Ed25519 certificate wasn't propoerly base64 encoded (%s):\n%s" % (exc, content))
@@ -236,7 +234,7 @@ class Ed25519Certificate(object):
class Ed25519CertificateV1(Ed25519Certificate): """ - Version 1 Ed25519 certificate, which are used for signing tor server + Version 1 Ed25519 certificate, which sign tor server and hidden service v3 descriptors.
:var stem.client.datatype.CertType type: certificate purpose @@ -381,7 +379,21 @@ class Ed25519CertificateV1(Ed25519Certificate): signed_content = hashlib.sha256(Ed25519CertificateV1._signed_content(descriptor)).digest() signature = stem.util.str_tools._decode_b64(descriptor.ed25519_signature)
- self._validate_server_desc_signing_key(descriptor) + # verify that we're created from this descriptor's signing key + + if descriptor.ed25519_master_key: + signing_key = base64.b64decode(stem.util.str_tools._to_bytes(descriptor.ed25519_master_key) + b'=') + else: + signing_key = self.signing_key() + + if not signing_key: + raise ValueError('Server descriptor missing an ed25519 signing key') + + try: + key = Ed25519PublicKey.from_public_bytes(signing_key) + key.verify(self.signature, base64.b64decode(stem.util.str_tools._to_bytes(self.to_base64()))[:-ED25519_SIGNATURE_LENGTH]) + except InvalidSignature: + raise ValueError('Ed25519KeyCertificate signing key is invalid (signature forged or corrupt)') elif isinstance(descriptor, stem.descriptor.hidden_service.HiddenServiceDescriptorV3): signed_content = Ed25519CertificateV1._signed_content(descriptor) signature = stem.util.str_tools._decode_b64(descriptor.signature) @@ -418,21 +430,3 @@ class Ed25519CertificateV1(Ed25519Certificate): raise ValueError('Malformed descriptor missing signature line')
return prefix + match.group(1) - - def _validate_server_desc_signing_key(self, descriptor): - from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey - from cryptography.exceptions import InvalidSignature - - if descriptor.ed25519_master_key: - signing_key = base64.b64decode(stem.util.str_tools._to_bytes(descriptor.ed25519_master_key) + b'=') - else: - signing_key = self.signing_key() - - if not signing_key: - raise ValueError('Server descriptor missing an ed25519 signing key') - - try: - key = Ed25519PublicKey.from_public_bytes(signing_key) - key.verify(self.signature, base64.b64decode(stem.util.str_tools._to_bytes(self.encoded))[:-ED25519_SIGNATURE_LENGTH]) - except InvalidSignature: - raise ValueError('Ed25519KeyCertificate signing key is invalid (signature forged or corrupt)') diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py index 9e29556a..000de2de 100644 --- a/test/unit/descriptor/certificate.py +++ b/test/unit/descriptor/certificate.py @@ -58,7 +58,7 @@ class TestEd25519Certificate(unittest.TestCase):
self.assertEqual(Ed25519CertificateV1, type(cert)) self.assertEqual(1, cert.version) - self.assertEqual(stem.util.str_tools._to_unicode(cert_bytes), cert.encoded) + self.assertEqual(stem.util.str_tools._to_unicode(cert_bytes), cert.to_base64().replace('\n', '')) self.assertEqual(CertType.ED25519_SIGNING, cert.type) self.assertEqual(datetime.datetime(1970, 1, 1, 0, 0), cert.expiration) self.assertEqual(1, cert.key_type) @@ -82,7 +82,7 @@ class TestEd25519Certificate(unittest.TestCase):
self.assertEqual(Ed25519CertificateV1, type(cert)) self.assertEqual(1, cert.version) - self.assertEqual(ED25519_CERT, cert.encoded) + self.assertEqual(ED25519_CERT, cert.to_base64()) self.assertEqual(CertType.ED25519_SIGNING, cert.type) self.assertEqual(datetime.datetime(2015, 8, 28, 17, 0), cert.expiration) self.assertEqual(1, cert.key_type) @@ -109,8 +109,7 @@ class TestEd25519Certificate(unittest.TestCase): """
cert = Ed25519Certificate.from_base64(ED25519_CERT) - self.assertEqual(ED25519_CERT, cert.encoded) # read base64 encoding (getting removed in stem 2.x) - self.assertEqual(ED25519_CERT, cert.to_base64()) # computed base64 encoding + self.assertEqual(ED25519_CERT, cert.to_base64())
def test_non_base64(self): """ diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py index e1036ada..07f50013 100644 --- a/test/unit/descriptor/server_descriptor.py +++ b/test/unit/descriptor/server_descriptor.py @@ -355,7 +355,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4= 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) + self.assertTrue('bWPo2fIzo3uOywfoM' in desc.certificate.to_base64())
extension = desc.certificate.extensions[0] self.assertEqual(ExtensionType.HAS_SIGNING_KEY, extension.type)