[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] Pack/unpack methods on Ed25519CertificateV1
commit 1e515a27d3e4f038876891251b49fe6048b26998
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Wed Oct 23 16:45:19 2019 -0700
Pack/unpack methods on Ed25519CertificateV1
On reflection working with bytes rather than base64 within our child class
makes us more consistent with other parsers. Our parent class can provide
base64 counterparts.
---
stem/descriptor/certificate.py | 122 +++++++++++++++++++-----------------
test/unit/descriptor/certificate.py | 6 +-
2 files changed, 68 insertions(+), 60 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py
index 855f1262..84610328 100644
--- a/stem/descriptor/certificate.py
+++ b/stem/descriptor/certificate.py
@@ -22,8 +22,11 @@ used to for a variety of purposes...
| |- signing_key - certificate signing key
| +- validate - validates a descriptor's signature
|
- |- from_base64 - decodes base64 encoded certificate data
- +- to_base64 - encodes base64 encoded certificate data
+ |- from_base64 - decodes a base64 encoded certificate
+ |- to_base64 - base64 encoding of this certificate
+ |
+ |- unpack - decodes a byte encoded certificate
+ +- pack - byte encoding of this certificate
Ed25519Extension - extension included within an Ed25519Certificate
@@ -173,9 +176,29 @@ class Ed25519Certificate(object):
self.encoded = None # TODO: remove in stem 2.x
@staticmethod
+ def unpack(content):
+ """
+ Parses a byte encoded ED25519 certificate.
+
+ :param bytes content: encoded certificate
+
+ :returns: :class:`~stem.descriptor.certificate.Ed25519Certificate` subclsss
+ for the given certificate
+
+ :raises: **ValueError** if certificate is malformed
+ """
+
+ version = Size.CHAR.pop(content)[0]
+
+ if version == 1:
+ return Ed25519CertificateV1.unpack(content)
+ else:
+ raise ValueError('Ed25519 certificate is version %i. Parser presently only supports version 1.' % version)
+
+ @staticmethod
def from_base64(content):
"""
- Parses the given base64 encoded data as an Ed25519 certificate.
+ Parses a base64 encoded ED25519 certificate.
:param str content: base64 encoded certificate
@@ -190,12 +213,26 @@ class Ed25519Certificate(object):
if content.startswith('-----BEGIN ED25519 CERT-----\n') and content.endswith('\n-----END ED25519 CERT-----'):
content = content[29:-27]
- version = stem.util.str_tools._to_int(Ed25519Certificate._b64_decode(content)[0:1])
+ try:
+ decoded = base64.b64decode(content)
- if version == 1:
- return Ed25519CertificateV1.from_base64(content)
- else:
- raise ValueError('Ed25519 certificate is version %i. Parser presently only supports version 1.' % version)
+ if not decoded:
+ raise TypeError('empty')
+
+ instance = Ed25519Certificate.unpack(decoded)
+ instance.encoded = content
+ return instance
+ except (TypeError, binascii.Error) as exc:
+ raise ValueError("Ed25519 certificate wasn't propoerly base64 encoded (%s):\n%s" % (exc, content))
+
+ def pack(self):
+ """
+ Encoded byte representation of our certificate.
+
+ :returns: **bytes** for our encoded certificate representation
+ """
+
+ raise NotImplementedError('Certificate encoding has not been implemented for %s' % type(self).__name__)
def to_base64(self, pem = False):
"""
@@ -208,7 +245,12 @@ class Ed25519Certificate(object):
:returns: **bytes** for our encoded certificate representation
"""
- raise NotImplementedError('Certificate encoding has not been implemented for %s' % type(self).__name__)
+ encoded = '\n'.join(stem.util.str_tools._split_by_length(base64.b64encode(self.pack()), 64))
+
+ if pem:
+ return '-----BEGIN ED25519 CERT-----\n%s\n-----END ED25519 CERT-----' % encoded
+ else:
+ return encoded
@staticmethod
def _from_descriptor(keyword, attribute):
@@ -222,18 +264,6 @@ class Ed25519Certificate(object):
return _parse
- @staticmethod
- def _b64_decode(content):
- try:
- decoded = base64.b64decode(content)
-
- if not decoded:
- raise TypeError('empty')
-
- return decoded
- except (TypeError, binascii.Error) as exc:
- raise ValueError("Ed25519 certificate wasn't propoerly base64 encoded (%s):\n%s" % (exc, content))
-
def __str__(self):
return self.to_base64(pem = True)
@@ -273,45 +303,26 @@ class Ed25519CertificateV1(Ed25519Certificate):
elif self.type == ClientCertType.UNKNOWN:
raise ValueError('Ed25519 certificate type %i is unrecognized' % self.type_int)
- def to_base64(self, pem = False):
- if self.encoded is None:
- encoded = bytearray()
- encoded += Size.CHAR.pack(self.version)
- encoded += Size.CHAR.pack(self.type_int)
- encoded += Size.LONG.pack(stem.util.datetime_to_unix(self.expiration) / 3600)
- encoded += Size.CHAR.pack(self.key_type)
- encoded += self.key
- encoded += Size.CHAR.pack(len(self.extensions))
-
- for extension in self.extensions:
- encoded += extension.pack()
+ def pack(self):
+ encoded = bytearray()
+ encoded += Size.CHAR.pack(self.version)
+ encoded += Size.CHAR.pack(self.type_int)
+ encoded += Size.LONG.pack(stem.util.datetime_to_unix(self.expiration) / 3600)
+ encoded += Size.CHAR.pack(self.key_type)
+ encoded += self.key
+ encoded += Size.CHAR.pack(len(self.extensions))
- self.encoded = '\n'.join(stem.util.str_tools._split_by_length(base64.b64encode(bytes(encoded + self.signature)), 64))
+ for extension in self.extensions:
+ encoded += extension.pack()
- if pem:
- return '-----BEGIN ED25519 CERT-----\n%s\n-----END ED25519 CERT-----' % self.encoded
- else:
- return self.encoded
+ return bytes(encoded + self.signature)
@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))
+ def unpack(content):
+ if len(content) < ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH:
+ raise ValueError('Ed25519 certificate was %i bytes, but should be at least %i' % (len(content), ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH))
- header, signature = split(decoded, len(decoded) - ED25519_SIGNATURE_LENGTH)
+ header, signature = split(content, len(content) - ED25519_SIGNATURE_LENGTH)
version, header = Size.CHAR.pop(header)
cert_type, header = Size.CHAR.pop(header)
@@ -333,7 +344,6 @@ class Ed25519CertificateV1(Ed25519Certificate):
raise ValueError('Ed25519 certificate had %i bytes of unused extension data' % len(extension_data))
instance = Ed25519CertificateV1(cert_type, datetime.datetime.utcfromtimestamp(expiration_hours * 3600), key_type, key, extensions, signature)
- instance.encoded = content
return instance
diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py
index a5f7837a..d2b13f72 100644
--- a/test/unit/descriptor/certificate.py
+++ b/test/unit/descriptor/certificate.py
@@ -112,10 +112,8 @@ class TestEd25519Certificate(unittest.TestCase):
"""
cert = Ed25519Certificate.from_base64(ED25519_CERT)
- self.assertEqual(ED25519_CERT, cert.encoded)
-
- cert.encoded = None # clear cached encoding
- self.assertEqual(ED25519_CERT, cert.to_base64())
+ 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
def test_non_base64(self):
"""
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits