[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Stub certificate encoding/decoding methods
commit 95f00b4fe19ac22abee90aec02498ffc6d56b76b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Oct 20 17:11:57 2019 -0700
Stub certificate encoding/decoding methods
Finally settled on to_base64() and from_base64() methods. Not implemented yet,
just stubbing the API I think we'll want.
---
stem/descriptor/certificate.py | 95 +++++++++++++++++++++++++++---------
stem/descriptor/hidden_service.py | 4 +-
stem/descriptor/server_descriptor.py | 2 +-
3 files changed, 76 insertions(+), 25 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py
index 042687e3..ba2d1d55 100644
--- a/stem/descriptor/certificate.py
+++ b/stem/descriptor/certificate.py
@@ -19,9 +19,11 @@ used to for a variety of purposes...
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
+ | |- signing_key - certificate signing key
+ | +- validate - validates a descriptor's signature
|
- +- parse - reads base64 encoded certificate data
+ |- from_base64 - decodes base64 encoded certificate data
+ +- to_base64 - encodes base64 encoded certificate data
Ed25519Extension - extension included within an Ed25519Certificate
@@ -123,12 +125,12 @@ class Ed25519Certificate(object):
:var unicode encoded: base64 encoded ed25519 certificate
"""
- def __init__(self, version, encoded):
+ def __init__(self, version):
self.version = version
- self.encoded = encoded
+ self.encoded = None # TODO: remove in stem 2.x
@staticmethod
- def parse(content):
+ def from_base64(content):
"""
Parses the given base64 encoded data as an Ed25519 certificate.
@@ -148,10 +150,23 @@ class Ed25519Certificate(object):
version = stem.util.str_tools._to_int(Ed25519Certificate._b64_decode(content)[0:1])
if version == 1:
- return Ed25519CertificateV1(content)
+ return Ed25519CertificateV1.from_base64(content)
else:
raise ValueError('Ed25519 certificate is version %i. Parser presently only supports version 1.' % version)
+ def to_base64(self, pem = False):
+ """
+ Base64 encoded certificate data.
+
+ :param bool pem: include `PEM header/footer
+ <https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail>`_, for more
+ information see `RFC 7468 <https://tools.ietf.org/html/rfc7468>`_
+
+ :returns: **bytes** for our encoded certificate representation
+ """
+
+ raise NotImplementedError('Certificate encoding has not been implemented for %s' % type(self).__name__)
+
@staticmethod
def _from_descriptor(keyword, attribute):
def _parse(descriptor, entries):
@@ -160,7 +175,7 @@ class Ed25519Certificate(object):
if not block_contents or block_type != 'ED25519 CERT':
raise ValueError("'%s' should be followed by a ED25519 CERT block, but was a %s" % (keyword, block_type))
- setattr(descriptor, attribute, Ed25519Certificate.parse(block_contents))
+ setattr(descriptor, attribute, Ed25519Certificate.from_base64(block_contents))
return _parse
@@ -177,7 +192,11 @@ class Ed25519Certificate(object):
raise ValueError("Ed25519 certificate wasn't propoerly base64 encoded (%s):\n%s" % (exc, content))
def __str__(self):
- return '-----BEGIN ED25519 CERT-----\n%s\n-----END ED25519 CERT-----' % self.encoded
+ return self.to_base64(pem = True)
+
+ @staticmethod
+ def parse(content):
+ return Ed25519Certificate.from_base64(content) # TODO: drop this alias in stem 2.x
class Ed25519CertificateV1(Ed25519Certificate):
@@ -194,33 +213,60 @@ class Ed25519CertificateV1(Ed25519Certificate):
:var bytes signature: certificate signature
"""
- def __init__(self, content):
- super(Ed25519CertificateV1, self).__init__(1, content)
+ def __init__(self, type_int, expiration, key_type, key, extensions, signature):
+ super(Ed25519CertificateV1, self).__init__(1)
+
+ self.type, self.type_int = ClientCertType.get(type_int)
+ self.expiration = expiration
+ self.key_type = key_type
+ self.key = key
+ self.extensions = extensions
+ self.signature = signature
+
+ def to_base64(self, pem = False):
+ if pem:
+ return '-----BEGIN ED25519 CERT-----\n%s\n-----END ED25519 CERT-----' % self.encoded
+ else:
+ return self.encoded
+
+ @staticmethod
+ def from_base64(content):
+ """
+ Parses the given base64 encoded data as a version 1 Ed25519 certificate.
+
+ :param str content: base64 encoded certificate
+
+ :returns: :class:`~stem.descriptor.certificate.Ed25519CertificateV1` for
+ this content
+
+ :raises: **ValueError** if certificate is malformed
+ """
+
decoded = Ed25519Certificate._b64_decode(content)
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))
- self.type, self.type_int = ClientCertType.get(stem.util.str_tools._to_int(decoded[1:2]))
+ type_enum, type_int = ClientCertType.get(stem.util.str_tools._to_int(decoded[1:2]))
- if self.type in (ClientCertType.LINK, ClientCertType.IDENTITY, ClientCertType.AUTHENTICATE):
- raise ValueError('Ed25519 certificate cannot have a type of %i. This is reserved for CERTS cells.' % self.type_int)
- elif self.type == ClientCertType.ED25519_IDENTITY:
+ if type_enum in (ClientCertType.LINK, ClientCertType.IDENTITY, ClientCertType.AUTHENTICATE):
+ raise ValueError('Ed25519 certificate cannot have a type of %i. This is reserved for CERTS cells.' % type_int)
+ elif type_enum == ClientCertType.ED25519_IDENTITY:
raise ValueError('Ed25519 certificate cannot have a type of 7. This is reserved for RSA identity cross-certification.')
- elif self.type == ClientCertType.UNKNOWN:
- raise ValueError('Ed25519 certificate type %i is unrecognized' % self.type_int)
+ elif type_enum == ClientCertType.UNKNOWN:
+ raise ValueError('Ed25519 certificate type %i is unrecognized' % type_int)
# expiration time is in hours since epoch
try:
- self.expiration = datetime.datetime.utcfromtimestamp(stem.util.str_tools._to_int(decoded[2:6]) * 3600)
+ expiration = datetime.datetime.utcfromtimestamp(stem.util.str_tools._to_int(decoded[2:6]) * 3600)
except ValueError as exc:
raise ValueError('Invalid expiration timestamp (%s): %s' % (exc, stem.util.str_tools._to_int(decoded[2:6]) * 3600))
- self.key_type = stem.util.str_tools._to_int(decoded[6:7])
- self.key = decoded[7:39]
- self.signature = decoded[-ED25519_SIGNATURE_LENGTH:]
+ key_type = stem.util.str_tools._to_int(decoded[6:7])
+ key = decoded[7:39]
+ signature = decoded[-ED25519_SIGNATURE_LENGTH:]
- self.extensions = []
+ extensions = []
extension_count = stem.util.str_tools._to_int(decoded[39:40])
remaining_data = decoded[40:-ED25519_SIGNATURE_LENGTH]
@@ -248,12 +294,17 @@ class Ed25519CertificateV1(Ed25519Certificate):
if extension_type == ExtensionType.HAS_SIGNING_KEY and len(extension_data) != 32:
raise ValueError('Ed25519 HAS_SIGNING_KEY extension must be 32 bytes, but was %i.' % len(extension_data))
- self.extensions.append(Ed25519Extension(extension_type, flags, extension_flags, extension_data))
+ extensions.append(Ed25519Extension(extension_type, flags, extension_flags, extension_data))
remaining_data = remaining_data[4 + extension_length:]
if remaining_data:
raise ValueError('Ed25519 certificate had %i bytes of unused extension data' % len(remaining_data))
+ instance = Ed25519CertificateV1(type_int, expiration, key_type, key, extensions, signature)
+ instance.encoded = content
+
+ return instance
+
def is_expired(self):
"""
Checks if this certificate is presently expired or not.
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index c2673fb3..1819dfc5 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -508,7 +508,7 @@ def _parse_v3_introduction_points(descriptor, entries):
onion_key = onion_key_line[5:] if onion_key_line.startswith('ntor ') else None
_, block_type, auth_key_cert = entry['auth-key'][0]
- auth_key_cert = Ed25519Certificate.parse(auth_key_cert)
+ auth_key_cert = Ed25519Certificate.from_base64(auth_key_cert)
if block_type != 'ED25519 CERT':
raise ValueError('Expected auth-key to have an ed25519 certificate, but was %s' % block_type)
@@ -517,7 +517,7 @@ def _parse_v3_introduction_points(descriptor, entries):
enc_key = enc_key_line[5:] if enc_key_line.startswith('ntor ') else None
_, block_type, enc_key_cert = entry['enc-key-cert'][0]
- enc_key_cert = Ed25519Certificate.parse(enc_key_cert)
+ enc_key_cert = Ed25519Certificate.from_base64(enc_key_cert)
if block_type != 'ED25519 CERT':
raise ValueError('Expected enc-key-cert to have an ed25519 certificate, but was %s' % block_type)
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index a2be8483..9c29164b 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -406,7 +406,7 @@ def _parse_identity_ed25519_line(descriptor, entries):
_parse_key_block('identity-ed25519', 'ed25519_certificate', 'ED25519 CERT')(descriptor, entries)
if descriptor.ed25519_certificate:
- descriptor.certificate = stem.descriptor.certificate.Ed25519Certificate.parse(descriptor.ed25519_certificate)
+ descriptor.certificate = stem.descriptor.certificate.Ed25519Certificate.from_base64(descriptor.ed25519_certificate)
_parse_master_key_ed25519_line = _parse_simple_line('master-key-ed25519', 'ed25519_master_key')
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits