commit ba1796674172f837e931033d8a060e435b768744 Author: Patrick O'Doherty p@trickod.com Date: Sun Feb 5 17:08:58 2017 -0800
Working onion-key-crosscert verification
Compares the digest in the signature block provided with a calculated onion-key-crosscert digest and raises a ValueError if they do not match. --- stem/descriptor/certificate.py | 6 +++++- stem/descriptor/server_descriptor.py | 38 ++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py index e26992f..63ebace 100644 --- a/stem/descriptor/certificate.py +++ b/stem/descriptor/certificate.py @@ -162,7 +162,11 @@ class Ed25519KeyCertificate(Certificate): signed_part = descriptor[:descriptor.index('router-sig-ed25519 ') + len('router-sig-ed25519 ')] descriptor_with_prefix = ED25519_ROUTER_SIGNATURE_PREFIX + signed_part descriptor_sha256_digest = hashlib.sha256(descriptor_with_prefix).digest() - verify_key.verify(descriptor_sha256_digest, signature_bytes) + + try: + verify_key.verify(descriptor_sha256_digest, signature_bytes) + except BadSignatureError: + raise ValueError('Descriptor Ed25519 certificate signature invalid')
def _verify_signature(self): if self.identity_key: diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py index c5bcaad..7d134dd 100644 --- a/stem/descriptor/server_descriptor.py +++ b/stem/descriptor/server_descriptor.py @@ -34,6 +34,7 @@ etc). This information is provided from a few sources... import functools import hashlib import re +import base64
import stem.descriptor.extrainfo_descriptor import stem.exit_policy @@ -760,23 +761,22 @@ class RelayDescriptor(ServerDescriptor): if signed_digest != self.digest(): raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, self.digest()))
+ if self.onion_key_crosscert: + onion_key_crosscert_digest = self._digest_for_signature(self.onion_key, self.onion_key_crosscert) + if onion_key_crosscert_digest != self.onion_key_crosscert_digest(): + raise ValueError('Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)' % (onion_key_crosscert_digest, self.onion_key_crosscert_digest())) + if stem.prereq.is_nacl_available() and self.ed25519_certificate: self.certificate = _parse_certificate(_bytes_for_block(self.ed25519_certificate), self.ed25519_master_key, validate)
- if self.ed25519_master_key is not None: - if self.certificate.identity_key != self.ed25519_master_key: - raise ValueError("master-key-ed25519 does not match ed25519 certificate identity key") + if self.certificate.identity_key != self.ed25519_master_key: + raise ValueError("master-key-ed25519 does not match ed25519 certificate identity key")
- self.certificate.verify_descriptor_signature(stem.util.str_tools._to_unicode(raw_contents), + self.certificate.verify_descriptor_signature(raw_contents, self.ed25519_signature)
- onion_key_bytes = _bytes_for_block(self.onion_key) - from Crypto.Util import asn1 - seq = asn1.DerSequence() - seq.decode(onion_key_bytes) - self._digest_for_signature(self.onion_key, self.onion_key_crosscert)
@lru_cache() @@ -786,11 +786,29 @@ class RelayDescriptor(ServerDescriptor):
:returns: the digest string encoded in uppercase hex
- :raises: ValueError if the digest canot be calculated + :raises: ValueError if the digest cannot be calculated """
return self._digest_for_content(b'router ', b'\nrouter-signature\n')
+ + @lru_cache() + def onion_key_crosscert_digest(self): + """ + Provides the digest of the onion-key-crosscert data consisting of the following: + + 1. SHA1 digest of the RSA identity key + 2. the ed25519 identity key + + :returns: the digest encoded in uppercase hex + + :raises: ValueError if the digest cannot be calculated + """ + signing_key_digest = hashlib.sha1(_bytes_for_block(self.signing_key)).digest() + data = signing_key_digest + base64.b64decode(self.ed25519_master_key + b'=') + return data.encode("hex").upper() + + def _compare(self, other, method): if not isinstance(other, RelayDescriptor): return False