def test_legacy_dir_key(self): """ Includes a 'legacy-dir-key' line with both valid and invalid content. """ test_value = '65968CCB6BECB5AA88459C5A072624C6995B6B72' authority = get_directory_authority({'legacy-dir-key': test_value}, is_vote = True) self.assertEqual(test_value, authority.legacy_dir_key) # check that we'll fail if legacy-dir-key appears in a consensus content = get_directory_authority({'legacy-dir-key': test_value}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) test_values = ( '', 'zzzzz', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', ) for value in test_values: content = get_directory_authority({'legacy-dir-key': value}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual(None, authority.legacy_dir_key)
def test_with_directory_authorities(self): """ Includes a couple directory authorities in the document. """ for is_document_vote in (False, True): for is_authorities_vote in (False, True): authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = is_authorities_vote) authority2 = get_directory_authority({'contact': 'mister hyde'}, is_vote = is_authorities_vote) vote_status = 'vote' if is_document_vote else 'consensus' content = get_network_status_document_v3({'vote-status': vote_status}, authorities = (authority1, authority2), content = True) if is_document_vote == is_authorities_vote: if is_document_vote: # votes can only have a single authority self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, validate = False) else: document = NetworkStatusDocumentV3(content) self.assertEqual((authority1, authority2), document.directory_authorities) else: # authority votes in a consensus or consensus authorities in a vote self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, validate = False) self.assertEqual((authority1, authority2), document.directory_authorities)
def test_with_legacy_directory_authorities(self): """ Includes both normal authorities and those following the '-legacy' format. """ legacy_content = "dir-source gabelmoo-legacy 81349FC1F2DBA2C2C11B45CB9706637D480AB913 212.112.245.170 212.112.245.170 80 443" authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = False) authority2 = DirectoryAuthority(legacy_content, validate = True, is_vote = False) authority3 = get_directory_authority({'contact': 'mister hyde'}, is_vote = False) document = get_network_status_document_v3({"vote-status": "consensus"}, authorities = (authority1, authority2, authority3)) self.assertEquals((authority1, authority2, authority3), document.directory_authorities)
def test_blank_lines(self): """ Includes blank lines, which should be ignored. """ authority = get_directory_authority({'dir-source': AUTHORITY_HEADER[0][1] + '\n\n\n'}) self.assertEqual('Mike Perry <email>', authority.contact)
def test_malformed_port(self): """ Includes a malformed orport or dirport on the 'dir-source' line. """ test_values = ( '', '-1', '399482', 'blarg', ) for value in test_values: for include_or_port in (False, True): for include_dir_port in (False, True): if not include_or_port and not include_dir_port: continue dir_source = AUTHORITY_HEADER[0][1] if include_or_port: dir_source = dir_source.replace('9090', value) if include_dir_port: dir_source = dir_source.replace('9030', value) content = get_directory_authority({'dir-source': dir_source}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) actual_value = authority.or_port if include_or_port else authority.dir_port self.assertEqual(None, actual_value)
def test_footer_consensus_method_requirement(self): """ Check that validation will notice if a footer appears before it was introduced. """ content = get_network_status_document_v3({'consensus-method': '8'}, content = True) self.assertRaises(ValueError, NetworkStatusDocumentV3, content, True) document = NetworkStatusDocumentV3(content, False) self.assertEqual([DOC_SIG], document.signatures) self.assertEqual([], document.get_unrecognized_lines()) # excludes a footer from a version that shouldn't have it document = get_network_status_document_v3({'consensus-method': '8'}, ('directory-footer', 'directory-signature')) self.assertEqual([], document.signatures) self.assertEqual([], document.get_unrecognized_lines()) # Prior to conensus method 9 votes can still have a signature in their # footer... # # https://trac.torproject.org/7932 document = get_network_status_document_v3( { 'vote-status': 'vote', 'consensus-methods': '1 8', }, exclude = ('directory-footer',), authorities = (get_directory_authority(is_vote = True),) ) self.assertEqual([DOC_SIG], document.signatures) self.assertEqual([], document.get_unrecognized_lines())
def test_unrecognized_line(self): """ Includes unrecognized content in the descriptor. """ authority = get_directory_authority({"pepperjack": "is oh so tasty!"}) self.assertEquals(["pepperjack is oh so tasty!"], authority.get_unrecognized_lines())
def test_malformed_port(self): """ Includes a malformed orport or dirport on the 'dir-source' line. """ test_values = ( "", "-1", "399482", "blarg", ) for value in test_values: for include_or_port in (False, True): for include_dir_port in (False, True): if not include_or_port and not include_dir_port: continue dir_source = AUTHORITY_HEADER[0][1] if include_or_port: dir_source = dir_source.replace('9090', value) if include_dir_port: dir_source = dir_source.replace('9030', value) content = get_directory_authority({"dir-source": dir_source}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) expected_value = 399482 if value == "399482" else None actual_value = authority.or_port if include_or_port else authority.dir_port self.assertEqual(expected_value, actual_value)
def test_unrecognized_line(self): """ Includes unrecognized content in the descriptor. """ authority = get_directory_authority({'pepperjack': 'is oh so tasty!'}) self.assertEqual(['pepperjack is oh so tasty!'], authority.get_unrecognized_lines())
def test_key_certificate(self): """ Includes or exclude a key certificate from the directory entry. """ key_cert = get_key_certificate(content = True) # include a key cert with a consensus content = get_directory_authority(content = True) + b'\n' + key_cert self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname) # exclude key cert from a vote content = get_directory_authority(content = True, is_vote = True).replace(b'\n' + key_cert, b'') self.assertRaises(ValueError, DirectoryAuthority, content, True, True) authority = DirectoryAuthority(content, False, True) self.assertEqual('turtles', authority.nickname)
def test_first_line(self): """ Includes a non-mandatory field before the 'dir-source' line. """ content = b'ho-hum 567\n' + get_directory_authority(content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname) self.assertEqual(['ho-hum 567'], authority.get_unrecognized_lines())
def test_first_line(self): """ Includes a non-mandatory field before the 'dir-source' line. """ content = b'ho-hum 567\n' + get_directory_authority(content=True) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname) self.assertEqual(['ho-hum 567'], authority.get_unrecognized_lines())
def test_key_certificate(self): """ Includes or exclude a key certificate from the directory entry. """ key_cert = get_key_certificate() # include a key cert with a consensus content = get_directory_authority(content=True) + "\n" + str(key_cert) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname) # exclude key cert from a vote content = get_directory_authority(content=True, is_vote=True).replace( "\n" + str(key_cert), '') self.assertRaises(ValueError, DirectoryAuthority, content, True, True) authority = DirectoryAuthority(content, False, True) self.assertEqual('turtles', authority.nickname)
def test_with_directory_authorities(self): """ Includes a couple directory authorities in the document. """ for is_document_vote in (False, True): for is_authorities_vote in (False, True): authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote = is_authorities_vote) authority2 = get_directory_authority({'contact': 'mister hyde'}, is_vote = is_authorities_vote) vote_status = "vote" if is_document_vote else "consensus" content = get_network_status_document_v3({"vote-status": vote_status}, authorities = (authority1, authority2), content = True) if is_document_vote == is_authorities_vote: document = NetworkStatusDocumentV3(content) self.assertEquals((authority1, authority2), document.directory_authorities) else: # authority votes in a consensus or consensus authorities in a vote self.assertRaises(ValueError, NetworkStatusDocumentV3, content) document = NetworkStatusDocumentV3(content, validate = False) self.assertEquals((authority1, authority2), document.directory_authorities)
def test_duplicate_lines(self): """ Duplicates linesin the entry. """ lines = get_directory_authority(content = True).split("\n") for i in xrange(len(lines)): content = "\n".join(lines[:i] + [lines[i]] + lines[i:]) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) self.assertEqual("turtles", authority.nickname)
def test_duplicate_lines(self): """ Duplicates linesin the entry. """ lines = get_directory_authority(content = True).split(b'\n') for index, duplicate_line in enumerate(lines): content = b'\n'.join(lines[:index] + [duplicate_line] + lines[index:]) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual('turtles', authority.nickname)
def test_with_legacy_directory_authorities(self): """ Includes both normal authorities and those following the '-legacy' format. """ legacy_content = "dir-source gabelmoo-legacy 81349FC1F2DBA2C2C11B45CB9706637D480AB913 212.112.245.170 212.112.245.170 80 443" authority1 = get_directory_authority({'contact': 'doctor jekyll'}, is_vote=False) authority2 = DirectoryAuthority(legacy_content, validate=True, is_vote=False) authority3 = get_directory_authority({'contact': 'mister hyde'}, is_vote=False) document = get_network_status_document_v3({"vote-status": "consensus"}, authorities=(authority1, authority2, authority3)) self.assertEquals((authority1, authority2, authority3), document.directory_authorities)
def test_malformed_address(self): """ Includes a malformed ip address on the 'dir-source' line. """ test_values = ("", "71.35.150.", "71.35..29", "71.35.150", "71.35.150.256", "[fd9f:2e19:3bcf::02:9970]") for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace("76.73.17.194", value) content = get_directory_authority({"dir-source": dir_source}, content=True) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) self.assertEqual(value, authority.address)
def test_malformed_fingerprint(self): """ Includes a malformed fingerprint on the 'dir-source' line. """ test_values = ("", "zzzzz", "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz") for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace("27B6B5996C426270A5C95488AA5BCEB6BCC86956", value) content = get_directory_authority({"dir-source": dir_source}, content=True) self.assertRaises(ValueError, DirectoryAuthority, content) authority = DirectoryAuthority(content, False) self.assertEqual(value, authority.fingerprint)
def test_missing_fields(self): """ Parse an authority where a mandatory field is missing. """ for excluded_field in ('dir-source', 'contact'): content = get_directory_authority(exclude = (excluded_field,), content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) if excluded_field == 'dir-source': self.assertEqual('Mike Perry <email>', authority.contact) else: self.assertEqual('turtles', authority.nickname)
def test_authority_validation_flag_propagation(self): """ Includes invalid certificate content in an authority entry. This is testing that the 'validate' flag propagages from the document to authority, and authority to certificate classes. """ # make the dir-key-published field of the certiciate be malformed authority_content = get_directory_authority(is_vote = True, content = True) authority_content = authority_content.replace("dir-key-published 2011", "dir-key-published 2011a") content = get_network_status_document_v3({"vote-status": "vote"}, authorities = (authority_content,), content = True) self.assertRaises(ValueError, NetworkStatusDocumentV3, content) document = NetworkStatusDocumentV3(content, validate = False) self.assertEquals((DirectoryAuthority(authority_content, False, True),), document.directory_authorities)
def test_authority_validation_flag_propagation(self): """ Includes invalid certificate content in an authority entry. This is testing that the 'validate' flag propagages from the document to authority, and authority to certificate classes. """ # make the dir-key-published field of the certiciate be malformed authority_content = get_directory_authority(is_vote = True, content = True) authority_content = authority_content.replace(b"dir-key-published 2011", b"dir-key-published 2011a") authority = DirectoryAuthority(authority_content, False, True) content = get_network_status_document_v3({"vote-status": "vote"}, authorities = (authority,), content = True) self.assertRaises(ValueError, NetworkStatusDocumentV3, content) document = NetworkStatusDocumentV3(content, validate = False) self.assertEquals((authority,), document.directory_authorities)
def test_minimal_consensus_authority(self): """ Parses a minimal directory authority for a consensus. """ authority = get_directory_authority() self.assertEqual("turtles", authority.nickname) self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", authority.fingerprint) self.assertEqual("no.place.com", authority.hostname) self.assertEqual("76.73.17.194", authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) self.assertEqual("Mike Perry <email>", authority.contact) self.assertEqual("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) self.assertEqual(None, authority.key_certificate) self.assertEqual([], authority.get_unrecognized_lines())
def test_minimal_vote_authority(self): """ Parses a minimal directory authority for a vote. """ authority = get_directory_authority(is_vote = True) self.assertEqual("turtles", authority.nickname) self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", authority.fingerprint) self.assertEqual("no.place.com", authority.hostname) self.assertEqual("76.73.17.194", authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) self.assertEqual("Mike Perry <email>", authority.contact) self.assertEqual(None, authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) self.assertEqual(get_key_certificate(), authority.key_certificate) self.assertEqual([], authority.get_unrecognized_lines())
def test_malformed_fingerprint(self): """ Includes a malformed fingerprint on the 'dir-source' line. """ test_values = ( '', 'zzzzz', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', ) for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace('27B6B5996C426270A5C95488AA5BCEB6BCC86956', value) content = get_directory_authority({'dir-source': dir_source}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual(None, authority.fingerprint)
def test_missing_dir_source_field(self): """ Excludes fields from the 'dir-source' line. """ for missing_value in AUTHORITY_HEADER[0][1].split(' '): dir_source = AUTHORITY_HEADER[0][1].replace(missing_value, '').replace(' ', ' ') content = get_directory_authority({'dir-source': dir_source}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual(None, authority.nickname) self.assertEqual(None, authority.fingerprint) self.assertEqual(None, authority.hostname) self.assertEqual(None, authority.address) self.assertEqual(None, authority.dir_port) self.assertEqual(None, authority.or_port)
def test_minimal_vote_authority(self): """ Parses a minimal directory authority for a vote. """ authority = get_directory_authority(is_vote = True) self.assertEqual('turtles', authority.nickname) self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint) self.assertEqual('no.place.com', authority.hostname) self.assertEqual('76.73.17.194', authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) self.assertEqual(False, authority.is_legacy) self.assertEqual('Mike Perry <email>', authority.contact) self.assertEqual(None, authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) self.assertEqual(get_key_certificate(), authority.key_certificate) self.assertEqual([], authority.get_unrecognized_lines())
def test_minimal_consensus_authority(self): """ Parses a minimal directory authority for a consensus. """ authority = get_directory_authority() self.assertEqual("turtles", authority.nickname) self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", authority.fingerprint) self.assertEqual("no.place.com", authority.hostname) self.assertEqual("76.73.17.194", authority.address) self.assertEqual(9030, authority.dir_port) self.assertEqual(9090, authority.or_port) self.assertEqual(False, authority.is_legacy) self.assertEqual("Mike Perry <email>", authority.contact) self.assertEqual("0B6D1E9A300B895AA2D0B427F92917B6995C3C1C", authority.vote_digest) self.assertEqual(None, authority.legacy_dir_key) self.assertEqual(None, authority.key_certificate) self.assertEqual([], authority.get_unrecognized_lines())
def test_malformed_address(self): """ Includes a malformed ip address on the 'dir-source' line. """ test_values = ( '', '71.35.150.', '71.35..29', '71.35.150', '71.35.150.256', '[fd9f:2e19:3bcf::02:9970]', ) for value in test_values: dir_source = AUTHORITY_HEADER[0][1].replace('76.73.17.194', value) content = get_directory_authority({'dir-source': dir_source}, content = True) self.assertRaises(ValueError, DirectoryAuthority, content, True) authority = DirectoryAuthority(content, False) self.assertEqual(None, authority.address)
def test_footer_consensus_method_requirement(self): """ Check that validation will notice if a footer appears before it was introduced. """ content = get_network_status_document_v3({"consensus-method": "8"}, content=True) self.assertRaises(ValueError, NetworkStatusDocumentV3, content) document = NetworkStatusDocumentV3(content, False) self.assertEqual([DOC_SIG], document.signatures) self.assertEqual([], document.get_unrecognized_lines()) # excludes a footer from a version that shouldn't have it document = get_network_status_document_v3( {"consensus-method": "8"}, ("directory-footer", "directory-signature")) self.assertEqual([], document.signatures) self.assertEqual([], document.get_unrecognized_lines()) # Prior to conensus method 9 votes can still have a signature in their # footer... # # https://trac.torproject.org/7932 document = get_network_status_document_v3( { "vote-status": "vote", "consensus-methods": "1 8", }, exclude=("directory-footer", ), authorities=(get_directory_authority(is_vote=True), )) self.assertEqual([DOC_SIG], document.signatures) self.assertEqual([], document.get_unrecognized_lines())