[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Drop previous ed25519 certificate implementation
commit 000e48aa900e38994c3443773addd08017c3c91b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Thu Mar 30 04:18:34 2017 +0200
Drop previous ed25519 certificate implementation
Now that we support everything it did time to drop the prior implementation.
---
stem/descriptor/certificate.py | 256 ++++--------------------------------
test/unit/descriptor/certificate.py | 65 ---------
2 files changed, 24 insertions(+), 297 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py
index d49df50..4a279a3 100644
--- a/stem/descriptor/certificate.py
+++ b/stem/descriptor/certificate.py
@@ -13,8 +13,14 @@ used to validate the key used to sign server descriptors.
::
Ed25519Certificate - Ed25519 signing key certificate
+ | +- Ed25519CertificateV1 - version 1 Ed25519 certificate
+ | |- is_expired - checks if certificate is presently expired
+ | +- validate - validates signature of a server descriptor
+ |
+- parse - reads base64 encoded certificate data
+ Ed25519Extension - extension included within an Ed25519Certificate
+
.. data:: CertType (enum)
Purpose of Ed25519 certificate. As new certificate versions are added this
@@ -53,16 +59,30 @@ used to validate the key used to sign server descriptors.
import base64
import collections
import datetime
+import hashlib
-from stem.util import enum
+import stem.prereq
+import stem.util.enum
+import stem.util.str_tools
ED25519_HEADER_LENGTH = 40
ED25519_SIGNATURE_LENGTH = 64
ED25519_ROUTER_SIGNATURE_PREFIX = b'Tor router descriptor signature v1'
-CertType = enum.UppercaseEnum('SIGNING', 'LINK_CERT', 'AUTH')
-ExtensionType = enum.Enum(('HAS_SIGNING_KEY', 4),)
-ExtensionFlag = enum.UppercaseEnum('AFFECTS_VALIDATION', 'UNKNOWN')
+CertType = stem.util.enum.UppercaseEnum('SIGNING', 'LINK_CERT', 'AUTH')
+ExtensionType = stem.util.enum.Enum(('HAS_SIGNING_KEY', 4),)
+ExtensionFlag = stem.util.enum.UppercaseEnum('AFFECTS_VALIDATION', 'UNKNOWN')
+
+
+class Ed25519Extension(collections.namedtuple('Ed25519Extension', ['type', 'flags', 'flag_int', 'data'])):
+ """
+ Extension within an Ed25519 certificate.
+
+ :var int type: extension type
+ :var list flags: extension attribute flags
+ :var int flag_int: integer encoding of the extension attribute flags
+ :var bytes data: data the extension concerns
+ """
class Ed25519Certificate(object):
@@ -245,231 +265,3 @@ class Ed25519CertificateV1(Ed25519Certificate):
verify_key.verify(descriptor_sha256_digest, signature_bytes)
except BadSignatureError as exc:
raise ValueError('Descriptor Ed25519 certificate signature invalid (%s)' % exc)
-
-
-class Ed25519Extension(collections.namedtuple('Ed25519Extension', ['type', 'flags', 'flag_int', 'data'])):
- """
- Extension within an Ed25519 certificate.
-
- :var int type: extension type
- :var list flags: extension attribute flags
- :var int flag_int: integer encoding of the extension attribute flags
- :var bytes data: data the extension concerns
- """
-
-
-
-
-
-
-
-"""
-Certificates can optionally contain CertificateExtension objects depending on
-their type and purpose. Currently Ed25519KeyCertificate certificates will
-contain one SignedWithEd25519KeyCertificateExtension.
-
- Certificate - Tor Certificate
- +- Ed25519KeyCertificate - Certificate for Ed25519 signing key
- +- verify_descriptor_signature - verify a relay descriptor against a signature
-
- CertificateExtension - Certificate extension
- +- SignedWithEd25519KeyCertificateExtension - Ed25519 signing key extension
-"""
-
-import binascii
-import hashlib
-import time
-
-import stem.prereq
-import stem.util.str_tools
-
-try:
- # added in python 2.7
- from collections import OrderedDict
-except ImportError:
- from stem.util.ordereddict import OrderedDict
-
-SIGNATURE_LENGTH = 64
-STANDARD_ATTRIBUTES_LENGTH = 40
-CERTIFICATE_FLAGS_LENGTH = 4
-ED25519_ROUTER_SIGNATURE_PREFIX = b'Tor router descriptor signature v1'
-
-
-def _parse_long_offset(offset, length):
- def _parse(raw_contents):
- return stem.util.str_tools._to_int(raw_contents[offset:(offset + length)])
-
- return _parse
-
-
-def _parse_offset(offset, length):
- def _parse(raw_contents):
- return raw_contents[offset:(offset + length)]
-
- return _parse
-
-
-def _parse_certificate(raw_contents, master_key_bytes, validate = False):
- version = raw_contents[0:1]
- cert_type = raw_contents[1:2]
-
- if version == b'\x01':
- if cert_type == b'\x04':
- return Ed25519KeyCertificate(raw_contents, master_key_bytes, validate = validate)
- elif cert_type == b'\x05':
- # TLS link certificated signed with ed25519 signing key
- pass
- elif cert_type == b'\x06':
- # Ed25519 authentication signed with ed25519 signing key
- pass
- else:
- raise ValueError('Unknown Certificate type %s' % binascii.hexlify(cert_type))
- else:
- raise ValueError('Unknown Certificate version %s' % binascii.hexlify(version))
-
-
-def _parse_extensions(raw_contents):
- n_extensions = stem.util.str_tools._to_int(raw_contents[39:40])
-
- if n_extensions == 0:
- return []
-
- extensions = []
- extension_bytes = raw_contents[STANDARD_ATTRIBUTES_LENGTH:-SIGNATURE_LENGTH]
-
- while len(extension_bytes) > 0:
- ext_length = stem.util.str_tools._to_int(extension_bytes[0:2])
- ext_type = extension_bytes[2:3]
- ext_flags = extension_bytes[3:CERTIFICATE_FLAGS_LENGTH]
- ext_data = extension_bytes[CERTIFICATE_FLAGS_LENGTH:(CERTIFICATE_FLAGS_LENGTH + ext_length)]
- if len(ext_type) == 0 or len(ext_flags) == 0 or len(ext_data) == 0:
- raise ValueError('Certificate contained truncated extension')
-
- if ext_type == SignedWithEd25519KeyCertificateExtension.TYPE:
- extension = SignedWithEd25519KeyCertificateExtension(ext_type, ext_flags, ext_data)
- else:
- raise ValueError('Invalid certificate extension type: %s' % binascii.hexlify(ext_type))
-
- extensions.append(extension)
- extension_bytes = extension_bytes[CERTIFICATE_FLAGS_LENGTH + ext_length:]
-
- if len(extensions) != n_extensions:
- raise ValueError('n_extensions was %d but parsed %d' % (n_extensions, len(extensions)))
-
- return extensions
-
-
-def _parse_signature(cert):
- return cert[-SIGNATURE_LENGTH:]
-
-
-class Certificate(object):
- """
- See proposal #220 <https://gitweb.torproject.org/torspec.git/tree/proposals/220-ecc-id-keys.txt>
- """
-
- ATTRIBUTES = {
- 'version': _parse_offset(0, 1),
- 'cert_type': _parse_offset(1, 1),
- 'expiration_date': _parse_long_offset(2, 4),
- 'cert_key_type': _parse_offset(6, 1),
- 'certified_key': _parse_offset(7, 32),
- 'n_extensions': _parse_long_offset(39, 1),
- 'extensions': _parse_extensions,
- 'signature': _parse_signature
- }
-
- def __init__(self, raw_contents, identity_key, validate = False):
- self.certificate_bytes = raw_contents
-
- if type(identity_key) == bytes:
- self.identity_key = stem.util.str_tools._to_unicode(identity_key)
- else:
- self.identity_key = identity_key
-
- self.__set_certificate_entries(raw_contents)
-
- def __set_certificate_entries(self, raw_contents):
- entries = OrderedDict()
- for key, func in Certificate.ATTRIBUTES.items():
- try:
- entries[key] = func(raw_contents)
- except IndexError:
- raise ValueError('Unable to get bytes for %s from certificate' % key)
-
- for key, value in entries.items():
- setattr(self, key, value)
-
-
-class Ed25519KeyCertificate(Certificate):
- def __init__(self, raw_contents, identity_key, validate = False):
- super(Ed25519KeyCertificate, self).__init__(raw_contents, identity_key, validate = False)
-
- if validate:
- if len(self.extensions) == 0:
- raise ValueError('Ed25519KeyCertificate missing SignedWithEd25519KeyCertificateExtension extension')
-
- self._verify_signature()
-
- if (self.expiration_date * 3600) < int(time.time()):
- raise ValueError('Expired Ed25519KeyCertificate')
-
- def verify_descriptor_signature(self, descriptor, signature):
- if not stem.prereq._is_pynacl_available():
- raise ValueError('Certificate validation requires the pynacl module')
-
- import nacl.signing
- from nacl.exceptions import BadSignatureError
-
- missing_padding = len(signature) % 4
- signature_bytes = base64.b64decode(stem.util.str_tools._to_bytes(signature) + b'=' * missing_padding)
- verify_key = nacl.signing.VerifyKey(self.certified_key)
-
- signed_part = descriptor[:descriptor.index(b'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()
-
- try:
- verify_key.verify(descriptor_sha256_digest, signature_bytes)
- except BadSignatureError:
- raise ValueError('Descriptor Ed25519 certificate signature invalid')
-
- def _verify_signature(self):
- if not stem.prereq._is_pynacl_available():
- raise ValueError('Certificate validation requires the pynacl module')
-
- import nacl.signing
- import nacl.encoding
- from nacl.exceptions import BadSignatureError
-
- if self.identity_key:
- verify_key = nacl.signing.VerifyKey(self.identity_key + '=', encoder=nacl.encoding.Base64Encoder)
- else:
- verify_key = nacl.singing.VerifyKey(self.extensions[0].ext_data)
-
- try:
- verify_key.verify(self.certificate_bytes[:-SIGNATURE_LENGTH], self.signature)
- except BadSignatureError:
- raise ValueError('Ed25519KeyCertificate signature invalid')
-
-
-class CertificateExtension(object):
- KNOWN_TYPES = [b'\x04']
-
- def __init__(self, ext_type, ext_flags, ext_data):
- self.ext_type = ext_type
- self.ext_flags = ext_flags
- self.ext_data = ext_data
-
- def is_known_type(self):
- return self.ext_type in CertificateExtension.KNOWN_TYPES
-
- def affects_validation(self):
- return self.ext_flags == b'\x01'
-
-
-class SignedWithEd25519KeyCertificateExtension(CertificateExtension):
- TYPE = b'\x04'
-
- def __init__(self, ext_type, ext_flags, ext_data):
- super(SignedWithEd25519KeyCertificateExtension, self).__init__(ext_type, ext_flags, ext_data)
diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py
index 9226f30..6cbc3b2 100644
--- a/test/unit/descriptor/certificate.py
+++ b/test/unit/descriptor/certificate.py
@@ -191,68 +191,3 @@ class TestEd25519Certificate(unittest.TestCase):
cert = Ed25519Certificate.parse(certificate())
self.assertRaisesRegexp(ValueError, re.escape('Ed25519KeyCertificate signing key is invalid (Signature was forged or corrupt)'), cert.validate, desc)
-
-
-class TestCertificate(unittest.TestCase):
- def test_with_invalid_version(self):
- cert_bytes = b'\x02\x04'
- self.assertRaisesRegexp(ValueError, 'Unknown Certificate version', stem.descriptor.certificate._parse_certificate, cert_bytes, None)
-
- def test_with_invalid_type(self):
- cert_bytes = b'\x01\x07'
- self.assertRaisesRegexp(ValueError, 'Unknown Certificate type', stem.descriptor.certificate._parse_certificate, cert_bytes, None)
-
- def test_parse_extensions_truncated_extension(self):
- cert_bytes = b'\x00' * 39 # First 40 bytes are standard fields
- cert_bytes += b'\x01' # n_extensions = 1
- cert_bytes += b'\x00\x08' # extension length = 8 bytes
- cert_bytes += b'\x04' # ext_type = 0x04
- cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * b'\x00' # pad empty signature block
-
- self.assertRaisesRegexp(ValueError, 'Certificate contained truncated extension', stem.descriptor.certificate._parse_extensions, cert_bytes)
-
- def test_parse_extensions_invalid_certificate_extension_type(self):
- cert_bytes = b'\x00' * 39 # First 40 bytes are standard fields
- cert_bytes += b'\x01' # n_extensions = 1
- cert_bytes += b'\x00\x08' # extension length = 8 bytes
- cert_bytes += b'\x00' * 6 # pad out to 8 bytes
- cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * b'\x00' # pad empty signature block
-
- self.assertRaisesRegexp(ValueError, 'Invalid certificate extension type:', stem.descriptor.certificate._parse_extensions, cert_bytes)
-
- def test_parse_extensions_invalid_n_extensions_count(self):
- cert_bytes = b'\x00' * 39 # First 40 bytes are standard fields
- cert_bytes += b'\x02' # n_extensions = 2
- cert_bytes += b'\x00\x08' # extension length = 8 bytes
- cert_bytes += b'\x04' # certificate type
- cert_bytes += b'\x00' * 5 # pad out to 8 bytes
- cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * b'\x00' # pad empty signature block
-
- self.assertRaisesRegexp(ValueError, 'n_extensions was 2 but parsed 1', stem.descriptor.certificate._parse_extensions, cert_bytes)
-
- def test_ed25519_key_certificate_without_extensions(self):
- cert_bytes = b'\x01\x04' + b'\x00' * 37 # First 40 bytes are standard fields
- cert_bytes += b'\x00' # n_extensions = 0
- cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * b'\x00' # pad empty signature block
-
- exc_msg = 'Ed25519KeyCertificate missing SignedWithEd25519KeyCertificateExtension extension'
- self.assertRaisesRegexp(ValueError, exc_msg, stem.descriptor.certificate._parse_certificate, cert_bytes, None, validate = True)
-
- def test_certificate_with_invalid_signature(self):
- if not stem.prereq._is_pynacl_available():
- test.runner.skip(self, '(requires pynacl module)')
- return
-
- import nacl.signing
- import nacl.encoding
-
- master_key = nacl.signing.SigningKey.generate()
- master_key_base64 = master_key.encode(nacl.encoding.Base64Encoder)
-
- cert_bytes = b'\x01\x04' + b'\x00' * 37 # 40 byte preamble of standard fields
- cert_bytes += b'\x01' # n_extensions = 1
- cert_bytes += b'\x00\x08' # extentsion length = 8 bytes
- cert_bytes += b'\x04' + b'\x00' * 5 # certificate type + padding out to 8 bytes
- cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * b'\x00' # empty signature block
-
- self.assertRaisesRegexp(ValueError, 'Ed25519KeyCertificate signature invalid', stem.descriptor.certificate._parse_certificate, cert_bytes, master_key_base64, validate = True)
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits