[tor-commits] [stem/master] Drop previous ed25519 certificate implementation

atagar at torproject.org atagar at torproject.org
Thu Mar 30 04:18:03 UTC 2017


commit 000e48aa900e38994c3443773addd08017c3c91b
Author: Damian Johnson <atagar at torproject.org>
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)





More information about the tor-commits mailing list