commit 311c4d7bab40c3f3bb80c9e52a217a390e635aab Author: Patrick O'Doherty p@trickod.com Date: Tue Feb 7 20:23:49 2017 -0800
stem.descriptor.certificate unit tests
Unit tests to ensure that invalid certificate data raises the appropriately descriptive ValueError when using the _parse_certificate and _parse_extensions functions provided by stem.descriptor.certificate --- stem/descriptor/certificate.py | 6 +- test/settings.cfg | 1 + test/unit/descriptor/certificate.py | 108 ++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 3 deletions(-)
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py index 63ebace..ff78538 100644 --- a/stem/descriptor/certificate.py +++ b/stem/descriptor/certificate.py @@ -5,7 +5,7 @@ Parsing for the Tor server descriptor Ed25519 Certificates, which is used to validate the Ed25519 key used to sign the relay descriptor.
-Certificates can optionally contain CertificateExtension objects depending on their type and purpose. Currently Ed25519KeyCertificate certificates will contain one SignedWithEd25519KeyCertificateExtensio +Certificates can optionally contain CertificateExtension objects depending on their type and purpose. Currently Ed25519KeyCertificate certificates will contain one SignedWithEd25519KeyCertificateExtension
**Module Overview:** @@ -82,9 +82,9 @@ def _parse_extensions(raw_contents): extensions = [] extension_bytes = raw_contents[STANDARD_ATTRIBUTES_LENGTH:-SIGNATURE_LENGTH] while len(extension_bytes) > 0: - ext_length = _bytes_to_long(extension_bytes[0:2]) - ext_type, ext_flags = extension_bytes[2:CERTIFICATE_FLAGS_LENGTH] try: + ext_length = _bytes_to_long(extension_bytes[0:2]) + ext_type, ext_flags = extension_bytes[2:CERTIFICATE_FLAGS_LENGTH] ext_data = extension_bytes[CERTIFICATE_FLAGS_LENGTH:(CERTIFICATE_FLAGS_LENGTH + ext_length)] except: raise ValueError('Certificate contained truncated extension') diff --git a/test/settings.cfg b/test/settings.cfg index c8172f6..558487d 100644 --- a/test/settings.cfg +++ b/test/settings.cfg @@ -190,6 +190,7 @@ test.unit_tests |test.unit.descriptor.networkstatus.document_v3.TestNetworkStatusDocument |test.unit.descriptor.networkstatus.bridge_document.TestBridgeNetworkStatusDocument |test.unit.descriptor.hidden_service_descriptor.TestHiddenServiceDescriptor +|test.unit.descriptor.certificate.TestCertificate |test.unit.exit_policy.rule.TestExitPolicyRule |test.unit.exit_policy.policy.TestExitPolicy |test.unit.version.TestVersion diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py new file mode 100644 index 0000000..450685f --- /dev/null +++ b/test/unit/descriptor/certificate.py @@ -0,0 +1,108 @@ +""" +Unit tests for stem.descriptor.certificate. +""" + +import unittest + +import stem.descriptor.certificate + +import nacl.signing +import nacl.encoding + + +class TestCertificate(unittest.TestCase): + + def test_with_invalid_version(self): + cert_bytes = '\x02\x04' + self.assertRaisesRegexp( + ValueError, + 'Unknown Certificate version', + stem.descriptor.certificate._parse_certificate, + cert_bytes, + None + ) + + def test_with_invalid_type(self): + cert_bytes = '\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 = '\x00' * 39 # First 40 bytes are standard fields + cert_bytes += '\x01' # n_extensions = 1 + cert_bytes += '\x00\x08' # extension length = 8 bytes + cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * '\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 = '\x00' * 39 # First 40 bytes are standard fields + cert_bytes += '\x01' # n_extensions = 1 + cert_bytes += '\x00\x08' # extension length = 8 bytes + cert_bytes += '\x00' * 6 # pad out to 8 bytes + cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * '\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 = '\x00' * 39 # First 40 bytes are standard fields + cert_bytes += '\x02' # n_extensions = 2 + cert_bytes += '\x00\x08' # extension length = 8 bytes + cert_bytes += '\x04' # certificate type + cert_bytes += '\x00' * 5 # pad out to 8 bytes + cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * '\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 = '\x01\x04' + '\x00' * 37 # First 40 bytes are standard fields + cert_bytes += '\x00' # n_extensions = 0 + cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * '\x00' # pad empty signature block + + self.assertRaisesRegexp( + ValueError, + 'Ed25519KeyCertificate missing SignedWithEd25519KeyCertificateExtension extension', + stem.descriptor.certificate._parse_certificate, + cert_bytes, + None, + validate = True + ) + + def test_certificate_with_invalid_signature(self): + master_key = nacl.signing.SigningKey.generate() + master_key_base64 = master_key.encode(nacl.encoding.Base64Encoder) + + cert_bytes = '\x01\x04' + '\x00' * 37 # 40 byte preamble of standard fields + cert_bytes += '\x01' # n_extensions = 1 + cert_bytes += '\x00\x08' # extentsion length = 8 bytes + cert_bytes += '\x04' + '\x00' * 5 # certificate type + padding out to 8 bytes + cert_bytes += stem.descriptor.certificate.SIGNATURE_LENGTH * '\x00' # empty signature block + + self.assertRaisesRegexp( + ValueError, + 'Ed25519KeyCertificate signature invalid', + stem.descriptor.certificate._parse_certificate, + cert_bytes, + master_key_base64, + validate = True + )