1""" 2Unit tests for stem.descriptor.hidden_service for version 2. 3""" 4 5import datetime 6import functools 7import unittest 8 9import stem.descriptor 10import stem.prereq 11import test.require 12 13from stem.descriptor.hidden_service import ( 14 REQUIRED_V2_FIELDS, 15 DecryptionFailure, 16 HiddenServiceDescriptorV2, 17) 18 19from test.unit.descriptor import ( 20 get_resource, 21 base_expect_invalid_attr, 22 base_expect_invalid_attr_for_text, 23) 24 25MESSAGE_BLOCK = """ 26-----BEGIN MESSAGE----- 27%s 28-----END MESSAGE-----\ 29""" 30 31EXPECTED_DDG_PERMANENT_KEY = """\ 32-----BEGIN RSA PUBLIC KEY----- 33MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE 34aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg 35I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE= 36-----END RSA PUBLIC KEY-----\ 37""" 38 39EXPECTED_DDG_INTRODUCTION_POINTS_ENCODED = """\ 40-----BEGIN MESSAGE----- 41aW50cm9kdWN0aW9uLXBvaW50IGl3a2k3N3h0YnZwNnF2ZWRmcndkem5jeHMzY2th 42eWV1CmlwLWFkZHJlc3MgMTc4LjYyLjIyMi4xMjkKb25pb24tcG9ydCA0NDMKb25p 43b24ta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpNSUdKQW9HQkFL 44OTRCRVlJSFo0S2RFa2V5UGhiTENwUlc1RVNnKzJXUFFock00eXVLWUd1cTh3Rldn 45dW1aWVI5CmsvV0EvL0ZZWE1CejBiQitja3Vacy9ZdTluSytITHpwR2FwVjBjbHN0 46NEdVTWNCSW5VQ3pDY3BqSlRRc1FEZ20KMy9ZM2NxaDBXNTVnT0NGaG9tUTQvMVdP 47WWc3WUNqazRYWUhKRTIwT2RHMkxsNXpvdEs2ZkFnTUJBQUU9Ci0tLS0tRU5EIFJT 48QSBQVUJMSUMgS0VZLS0tLS0Kc2VydmljZS1rZXkKLS0tLS1CRUdJTiBSU0EgUFVC 49TElDIEtFWS0tLS0tCk1JR0pBb0dCQUpYbUpiOGxTeWRNTXFDZ0NnZmd2bEIyRTVy 50cGQ1N2t6L0FxZzcvZDFIS2MzK2w1UW9Vdkh5dXkKWnNBbHlrYThFdTUzNGhsNDFv 51cUVLcEFLWWNNbjFUTTB2cEpFR05WT2MrMDVCSW54STloOWYwTWcwMVBEMHRZdQpH 52Y0xIWWdCemNyZkVtS3dNdE04V0VtY01KZDduMnVmZmFBdko4NDZXdWJiZVY3TVcx 53WWVoQWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBVQkxJQyBLRVktLS0tLQppbnRyb2R1 54Y3Rpb24tcG9pbnQgZW00Z2prNmVpaXVhbGhtbHlpaWZyemM3bGJ0cnNiaXAKaXAt 55YWRkcmVzcyA0Ni40LjE3NC41Mgpvbmlvbi1wb3J0IDQ0Mwpvbmlvbi1rZXkKLS0t 56LS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQUxCbWhkRjV3SHhI 57cnBMU21qQVpvdHR4MjIwKzk5NUZkTU9PdFpOalJ3MURCU3ByVVpacXR4V2EKUDhU 58S3BIS3p3R0pLQ1ZZSUlqN2xvaGJ2OVQ5dXJtbGZURTA1VVJHZW5ab2lmT0ZOejNZ 59d01KVFhTY1FFQkoxMAo5aVdOTERUc2tMekRLQ0FiR2hibi9NS3dPZllHQmhOVGxq 60ZHlUbU5ZNUVDUmJSempldjl2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBVQkxJQyBL 61RVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMgS0VZLS0t 62LS0KTUlHSkFvR0JBTXhNSG9BbXJiVU1zeGlJQ3AzaVRQWWdobjBZdWVLSHgyMTl3 63dThPL1E1MVF5Y1ZWTHBYMjdkMQpoSlhrUEIzM1hRQlhzQlM3U3hzU3NTQ1EzR0V1 64clFKN0d1QkxwWUlSL3Zxc2FrRS9sOHdjMkNKQzVXVWh5RkZrCisxVFdJVUk1dHhu 65WEx5V0NSY0tEVXJqcWRvc0RhRG9zZ0hGZzIzTW54K3hYY2FRL2ZyQi9BZ01CQUFF 66PQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlvbi1wb2lu 67dCBqcWhmbDM2NHgzdXBlNmxxbnhpem9sZXdsZnJzdzJ6eQppcC1hZGRyZXNzIDYy 68LjIxMC44Mi4xNjkKb25pb24tcG9ydCA0NDMKb25pb24ta2V5Ci0tLS0tQkVHSU4g 69UlNBIFBVQkxJQyBLRVktLS0tLQpNSUdKQW9HQkFQVWtxeGdmWWR3MFBtL2c2TWJo 70bVZzR0tsdWppZm1raGRmb0VldXpnbyt3bkVzR3Z3VWVienJ6CmZaSlJ0MGNhWEZo 71bkNHZ1FEMklnbWFyVWFVdlAyNGZYby80bVl6TGNQZUk3Z1puZXVBUUpZdm05OFl2 72OXZPSGwKTmFNL1d2RGtDc0ozR1ZOSjFIM3dMUFFSSTN2N0tiTnVjOXRDT1lsL3Iw 73OU9oVmFXa3phakFnTUJBQUU9Ci0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K 74c2VydmljZS1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pB 75b0dCQUxieDhMZXFSb1Avcjl3OWhqd0Q0MVlVbTdQbzY5N3hSdHl0RjBNY3lMQ1M3 76R1JpVVluamk3S1kKZmVwWGR2Ti9KbDVxUUtISUJiNjAya3VPVGwwcE44UStZZUZV 77U0lJRGNtUEJMcEJEaEgzUHZyUU1jR1ZhaU9XSAo4dzBITVpDeGd3QWNDQzUxdzVW 78d2l1bXhFSk5CVmNac094MG16TjFDbG95KzkwcTBsRlhMQWdNQkFBRT0KLS0tLS1F 79TkQgUlNBIFBVQkxJQyBLRVktLS0tLQoK 80-----END MESSAGE-----\ 81""" 82 83EXPECTED_DDG_INTRODUCTION_POINTS_CONTENT = b"""\ 84introduction-point iwki77xtbvp6qvedfrwdzncxs3ckayeu 85ip-address 178.62.222.129 86onion-port 443 87onion-key 88-----BEGIN RSA PUBLIC KEY----- 89MIGJAoGBAK94BEYIHZ4KdEkeyPhbLCpRW5ESg+2WPQhrM4yuKYGuq8wFWgumZYR9 90k/WA//FYXMBz0bB+ckuZs/Yu9nK+HLzpGapV0clst4GUMcBInUCzCcpjJTQsQDgm 913/Y3cqh0W55gOCFhomQ4/1WOYg7YCjk4XYHJE20OdG2Ll5zotK6fAgMBAAE= 92-----END RSA PUBLIC KEY----- 93service-key 94-----BEGIN RSA PUBLIC KEY----- 95MIGJAoGBAJXmJb8lSydMMqCgCgfgvlB2E5rpd57kz/Aqg7/d1HKc3+l5QoUvHyuy 96ZsAlyka8Eu534hl41oqEKpAKYcMn1TM0vpJEGNVOc+05BInxI9h9f0Mg01PD0tYu 97GcLHYgBzcrfEmKwMtM8WEmcMJd7n2uffaAvJ846WubbeV7MW1YehAgMBAAE= 98-----END RSA PUBLIC KEY----- 99introduction-point em4gjk6eiiualhmlyiifrzc7lbtrsbip 100ip-address 46.4.174.52 101onion-port 443 102onion-key 103-----BEGIN RSA PUBLIC KEY----- 104MIGJAoGBALBmhdF5wHxHrpLSmjAZottx220+995FdMOOtZNjRw1DBSprUZZqtxWa 105P8TKpHKzwGJKCVYIIj7lohbv9T9urmlfTE05URGenZoifOFNz3YwMJTXScQEBJ10 1069iWNLDTskLzDKCAbGhbn/MKwOfYGBhNTljdyTmNY5ECRbRzjev9vAgMBAAE= 107-----END RSA PUBLIC KEY----- 108service-key 109-----BEGIN RSA PUBLIC KEY----- 110MIGJAoGBAMxMHoAmrbUMsxiICp3iTPYghn0YueKHx219wu8O/Q51QycVVLpX27d1 111hJXkPB33XQBXsBS7SxsSsSCQ3GEurQJ7GuBLpYIR/vqsakE/l8wc2CJC5WUhyFFk 112+1TWIUI5txnXLyWCRcKDUrjqdosDaDosgHFg23Mnx+xXcaQ/frB/AgMBAAE= 113-----END RSA PUBLIC KEY----- 114introduction-point jqhfl364x3upe6lqnxizolewlfrsw2zy 115ip-address 62.210.82.169 116onion-port 443 117onion-key 118-----BEGIN RSA PUBLIC KEY----- 119MIGJAoGBAPUkqxgfYdw0Pm/g6MbhmVsGKlujifmkhdfoEeuzgo+wnEsGvwUebzrz 120fZJRt0caXFhnCGgQD2IgmarUaUvP24fXo/4mYzLcPeI7gZneuAQJYvm98Yv9vOHl 121NaM/WvDkCsJ3GVNJ1H3wLPQRI3v7KbNuc9tCOYl/r09OhVaWkzajAgMBAAE= 122-----END RSA PUBLIC KEY----- 123service-key 124-----BEGIN RSA PUBLIC KEY----- 125MIGJAoGBALbx8LeqRoP/r9w9hjwD41YUm7Po697xRtytF0McyLCS7GRiUYnji7KY 126fepXdvN/Jl5qQKHIBb602kuOTl0pN8Q+YeFUSIIDcmPBLpBDhH3PvrQMcGVaiOWH 1278w0HMZCxgwAcCC51w5VwiumxEJNBVcZsOx0mzN1Cloy+90q0lFXLAgMBAAE= 128-----END RSA PUBLIC KEY----- 129 130""" 131 132EXPECTED_DDG_SIGNATURE = """\ 133-----BEGIN SIGNATURE----- 134VKMmsDIUUFOrpqvcQroIZjDZTKxqNs88a4M9Te8cR/ZvS7H2nffv6iQs0tom5X4D 1354Dy4iZiy+pwYxdHfaOxmdpgMCRvgPb34MExWr5YemH0QuGtnlp5Wxr8GYaAQVuZX 136cZjQLW0juUYCbgIGdxVEBnlEt2rgBSM9+1oR7EAfV1U= 137-----END SIGNATURE-----\ 138""" 139 140EXPECT_POINT_1_ONION_KEY = """\ 141-----BEGIN RSA PUBLIC KEY----- 142MIGJAoGBAK94BEYIHZ4KdEkeyPhbLCpRW5ESg+2WPQhrM4yuKYGuq8wFWgumZYR9 143k/WA//FYXMBz0bB+ckuZs/Yu9nK+HLzpGapV0clst4GUMcBInUCzCcpjJTQsQDgm 1443/Y3cqh0W55gOCFhomQ4/1WOYg7YCjk4XYHJE20OdG2Ll5zotK6fAgMBAAE= 145-----END RSA PUBLIC KEY-----\ 146""" 147 148EXPECT_POINT_1_SERVICE_KEY = """\ 149-----BEGIN RSA PUBLIC KEY----- 150MIGJAoGBAJXmJb8lSydMMqCgCgfgvlB2E5rpd57kz/Aqg7/d1HKc3+l5QoUvHyuy 151ZsAlyka8Eu534hl41oqEKpAKYcMn1TM0vpJEGNVOc+05BInxI9h9f0Mg01PD0tYu 152GcLHYgBzcrfEmKwMtM8WEmcMJd7n2uffaAvJ846WubbeV7MW1YehAgMBAAE= 153-----END RSA PUBLIC KEY-----\ 154""" 155 156EXPECT_POINT_2_ONION_KEY = """\ 157-----BEGIN RSA PUBLIC KEY----- 158MIGJAoGBALBmhdF5wHxHrpLSmjAZottx220+995FdMOOtZNjRw1DBSprUZZqtxWa 159P8TKpHKzwGJKCVYIIj7lohbv9T9urmlfTE05URGenZoifOFNz3YwMJTXScQEBJ10 1609iWNLDTskLzDKCAbGhbn/MKwOfYGBhNTljdyTmNY5ECRbRzjev9vAgMBAAE= 161-----END RSA PUBLIC KEY-----\ 162""" 163 164EXPECT_POINT_2_SERVICE_KEY = """\ 165-----BEGIN RSA PUBLIC KEY----- 166MIGJAoGBAMxMHoAmrbUMsxiICp3iTPYghn0YueKHx219wu8O/Q51QycVVLpX27d1 167hJXkPB33XQBXsBS7SxsSsSCQ3GEurQJ7GuBLpYIR/vqsakE/l8wc2CJC5WUhyFFk 168+1TWIUI5txnXLyWCRcKDUrjqdosDaDosgHFg23Mnx+xXcaQ/frB/AgMBAAE= 169-----END RSA PUBLIC KEY-----\ 170""" 171 172EXPECT_POINT_3_ONION_KEY = """\ 173-----BEGIN RSA PUBLIC KEY----- 174MIGJAoGBAPUkqxgfYdw0Pm/g6MbhmVsGKlujifmkhdfoEeuzgo+wnEsGvwUebzrz 175fZJRt0caXFhnCGgQD2IgmarUaUvP24fXo/4mYzLcPeI7gZneuAQJYvm98Yv9vOHl 176NaM/WvDkCsJ3GVNJ1H3wLPQRI3v7KbNuc9tCOYl/r09OhVaWkzajAgMBAAE= 177-----END RSA PUBLIC KEY-----\ 178""" 179 180EXPECT_POINT_3_SERVICE_KEY = """\ 181-----BEGIN RSA PUBLIC KEY----- 182MIGJAoGBALbx8LeqRoP/r9w9hjwD41YUm7Po697xRtytF0McyLCS7GRiUYnji7KY 183fepXdvN/Jl5qQKHIBb602kuOTl0pN8Q+YeFUSIIDcmPBLpBDhH3PvrQMcGVaiOWH 1848w0HMZCxgwAcCC51w5VwiumxEJNBVcZsOx0mzN1Cloy+90q0lFXLAgMBAAE= 185-----END RSA PUBLIC KEY-----\ 186""" 187 188EXPECTED_BASIC_AUTH_INTRODUCTION_POINTS_ENCODED = """\ 189-----BEGIN MESSAGE----- 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 236-----END MESSAGE-----\ 237""" 238 239expect_invalid_attr = functools.partial(base_expect_invalid_attr, HiddenServiceDescriptorV2, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa') 240expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, HiddenServiceDescriptorV2, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa') 241 242 243class TestHiddenServiceDescriptorV2(unittest.TestCase): 244 def test_from_str(self): 245 sig = HiddenServiceDescriptorV2.create() 246 self.assertEqual(sig, HiddenServiceDescriptorV2.from_str(str(sig))) 247 248 def test_for_duckduckgo_with_validation(self): 249 """ 250 Parse duckduckgo's descriptor. 251 """ 252 253 with open(get_resource('hidden_service_duckduckgo'), 'rb') as descriptor_file: 254 desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True)) 255 self._assert_matches_duckduckgo(desc) 256 257 def test_for_duckduckgo_without_validation(self): 258 """ 259 Parse duckduckgo's descriptor 260 """ 261 262 with open(get_resource('hidden_service_duckduckgo'), 'rb') as descriptor_file: 263 desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = False)) 264 self._assert_matches_duckduckgo(desc) 265 266 def test_for_facebook(self): 267 """ 268 Parse facebook's descriptor. 269 """ 270 271 with open(get_resource('hidden_service_facebook'), 'rb') as descriptor_file: 272 desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True)) 273 274 self.assertEqual('utjk4arxqg6s6zzo7n6cjnq6ot34udhr', desc.descriptor_id) 275 self.assertEqual(2, desc.version) 276 self.assertEqual('6355jaerje3bqozopwq2qmpf4iviizdn', desc.secret_id_part) 277 self.assertEqual(datetime.datetime(2014, 10, 31, 23, 0, 0), desc.published) 278 self.assertEqual([2, 3], desc.protocol_versions) 279 280 @test.require.cryptography 281 def test_descriptor_signing(self): 282 self.assertRaisesWith(NotImplementedError, 'Signing of HiddenServiceDescriptorV2 not implemented', HiddenServiceDescriptorV2.create, sign = True) 283 284 @test.require.cryptography 285 def test_with_basic_auth(self): 286 """ 287 Parse a descriptor with introduction-points encrypted with basic auth. 288 """ 289 290 with open(get_resource('hidden_service_basic_auth'), 'rb') as descriptor_file: 291 desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True)) 292 293 self.assertEqual('yfmvdrkdbyquyqk5vygyeylgj2qmrvrd', desc.descriptor_id) 294 self.assertEqual(2, desc.version) 295 self.assertEqual('fluw7z3s5cghuuirq3imh5jjj5ljips6', desc.secret_id_part) 296 self.assertEqual(datetime.datetime(2015, 2, 24, 20, 0, 0), desc.published) 297 self.assertEqual([2, 3], desc.protocol_versions) 298 self.assertEqual(EXPECTED_BASIC_AUTH_INTRODUCTION_POINTS_ENCODED, desc.introduction_points_encoded) 299 self.assertEqual([], desc.introduction_points_auth) 300 301 self.assertRaises(DecryptionFailure, desc.introduction_points) 302 self.assertRaises(DecryptionFailure, desc.introduction_points, 'aCmx3qIvArbil8A0KM4KgQ==') 303 304 introduction_points = desc.introduction_points('dCmx3qIvArbil8A0KM4KgQ==') 305 self.assertEqual(3, len(introduction_points)) 306 307 point = introduction_points[0] 308 self.assertEqual('hmtvoobwglmmec26alnvl7x7mgmmr7xv', point.identifier) 309 self.assertEqual('195.154.82.88', point.address) 310 self.assertEqual(443, point.port) 311 self.assertTrue('MIGJAoGBANbPRD07T' in point.onion_key) 312 self.assertTrue('MIGJAoGBAN+LAdZP/' in point.service_key) 313 self.assertEqual([], point.intro_authentication) 314 315 point = introduction_points[1] 316 self.assertEqual('q5w6l2f4g5zw4rkr56fkyovbkkrnzcj5', point.identifier) 317 self.assertEqual('37.252.190.133', point.address) 318 self.assertEqual(9001, point.port) 319 self.assertTrue('MIGJAoGBAKmsbKrtt' in point.onion_key) 320 self.assertTrue('MIGJAoGBANwczLtzR' in point.service_key) 321 self.assertEqual([], point.intro_authentication) 322 323 point = introduction_points[2] 324 self.assertEqual('qcvprvmvnjb4dfyqjtxskugniliwlrx3', point.identifier) 325 self.assertEqual('193.11.114.45', point.address) 326 self.assertEqual(9002, point.port) 327 self.assertTrue('MIGJAoGBAM1ILL+7P' in point.onion_key) 328 self.assertTrue('MIGJAoGBAM7B/cymp' in point.service_key) 329 self.assertEqual([], point.intro_authentication) 330 331 @test.require.cryptography 332 def test_with_stealth_auth(self): 333 """ 334 Parse a descriptor with introduction-points encrypted with stealth auth. 335 """ 336 337 with open(get_resource('hidden_service_stealth_auth'), 'rb') as descriptor_file: 338 desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor 1.0', validate = True)) 339 340 self.assertEqual('ubf3xeibzlfil6s4larq6y5peup2z3oj', desc.descriptor_id) 341 self.assertEqual(2, desc.version) 342 self.assertEqual('jczvydhzetbpdiylj3d5nsnjvaigs7xm', desc.secret_id_part) 343 self.assertEqual(datetime.datetime(2015, 2, 24, 20, 0, 0), desc.published) 344 self.assertEqual([2, 3], desc.protocol_versions) 345 self.assertEqual([], desc.introduction_points_auth) 346 347 self.assertRaises(DecryptionFailure, desc.introduction_points) 348 self.assertRaises(DecryptionFailure, desc.introduction_points, 'aCmx3qIvArbil8A0KM4KgQ==') 349 350 introduction_points = desc.introduction_points('dCmx3qIvArbil8A0KM4KgQ==') 351 self.assertEqual(3, len(introduction_points)) 352 353 point = introduction_points[0] 354 self.assertEqual('6h4bkedts3yz2exl3vu4lsyiwkjrx5ff', point.identifier) 355 self.assertEqual('95.85.60.23', point.address) 356 self.assertEqual(443, point.port) 357 self.assertTrue('MIGJAoGBAMX5hO5hQ' in point.onion_key) 358 self.assertTrue('MIGJAoGBAMNSjfydv' in point.service_key) 359 self.assertEqual([], point.intro_authentication) 360 361 point = introduction_points[1] 362 self.assertEqual('4ghasjftsdfbbycafvlfx7czln3hrk53', point.identifier) 363 self.assertEqual('178.254.55.101', point.address) 364 self.assertEqual(9901, point.port) 365 self.assertTrue('MIGJAoGBAL2v/KNEY' in point.onion_key) 366 self.assertTrue('MIGJAoGBAOXiuIgBr' in point.service_key) 367 self.assertEqual([], point.intro_authentication) 368 369 point = introduction_points[2] 370 self.assertEqual('76tsxvudxqx47gedk3tl5qpesdzrh6yh', point.identifier) 371 self.assertEqual('193.11.164.243', point.address) 372 self.assertEqual(9001, point.port) 373 self.assertTrue('MIGJAoGBALca3zEoS' in point.onion_key) 374 self.assertTrue('MIGJAoGBAL3rWIAQ6' in point.service_key) 375 self.assertEqual([], point.intro_authentication) 376 377 def _assert_matches_duckduckgo(self, desc): 378 self.assertEqual('y3olqqblqw2gbh6phimfuiroechjjafa', desc.descriptor_id) 379 self.assertEqual(2, desc.version) 380 self.assertEqual(EXPECTED_DDG_PERMANENT_KEY, desc.permanent_key) 381 self.assertEqual('e24kgecavwsznj7gpbktqsiwgvngsf4e', desc.secret_id_part) 382 self.assertEqual(datetime.datetime(2015, 2, 23, 20, 0, 0), desc.published) 383 self.assertEqual([2, 3], desc.protocol_versions) 384 self.assertEqual(EXPECTED_DDG_INTRODUCTION_POINTS_ENCODED, desc.introduction_points_encoded) 385 self.assertEqual([], desc.introduction_points_auth) 386 self.assertEqual(EXPECTED_DDG_INTRODUCTION_POINTS_CONTENT, desc.introduction_points_content) 387 self.assertEqual(EXPECTED_DDG_SIGNATURE, desc.signature) 388 389 introduction_points = desc.introduction_points() 390 self.assertEqual(3, len(introduction_points)) 391 392 point = introduction_points[0] 393 self.assertEqual('iwki77xtbvp6qvedfrwdzncxs3ckayeu', point.identifier) 394 self.assertEqual('178.62.222.129', point.address) 395 self.assertEqual(443, point.port) 396 self.assertEqual(EXPECT_POINT_1_ONION_KEY, point.onion_key) 397 self.assertEqual(EXPECT_POINT_1_SERVICE_KEY, point.service_key) 398 self.assertEqual([], point.intro_authentication) 399 400 point = introduction_points[1] 401 self.assertEqual('em4gjk6eiiualhmlyiifrzc7lbtrsbip', point.identifier) 402 self.assertEqual('46.4.174.52', point.address) 403 self.assertEqual(443, point.port) 404 self.assertEqual(EXPECT_POINT_2_ONION_KEY, point.onion_key) 405 self.assertEqual(EXPECT_POINT_2_SERVICE_KEY, point.service_key) 406 self.assertEqual([], point.intro_authentication) 407 408 point = introduction_points[2] 409 self.assertEqual('jqhfl364x3upe6lqnxizolewlfrsw2zy', point.identifier) 410 self.assertEqual('62.210.82.169', point.address) 411 self.assertEqual(443, point.port) 412 self.assertEqual(EXPECT_POINT_3_ONION_KEY, point.onion_key) 413 self.assertEqual(EXPECT_POINT_3_SERVICE_KEY, point.service_key) 414 self.assertEqual([], point.intro_authentication) 415 416 def test_minimal_hidden_service_descriptor(self): 417 """ 418 Basic sanity check that we can parse a hidden service descriptor with minimal attributes. 419 """ 420 421 desc = HiddenServiceDescriptorV2.create() 422 423 self.assertEqual('y3olqqblqw2gbh6phimfuiroechjjafa', desc.descriptor_id) 424 self.assertEqual(2, desc.version) 425 self.assertEqual('e24kgecavwsznj7gpbktqsiwgvngsf4e', desc.secret_id_part) 426 self.assertEqual([2, 3], desc.protocol_versions) 427 self.assertEqual('-----BEGIN MESSAGE-----\n-----END MESSAGE-----', desc.introduction_points_encoded) 428 self.assertEqual([], desc.introduction_points_auth) 429 self.assertEqual(b'', desc.introduction_points_content) 430 self.assertEqual([], desc.introduction_points()) 431 self.assertEqual('@type hidden-service-descriptor 1.0', str(desc.type_annotation())) 432 433 def test_unrecognized_line(self): 434 """ 435 Includes unrecognized content in the descriptor. 436 """ 437 438 desc = HiddenServiceDescriptorV2.create({'pepperjack': 'is oh so tasty!'}) 439 self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines()) 440 441 def test_proceeding_line(self): 442 """ 443 Includes a line prior to the 'rendezvous-service-descriptor' entry. 444 """ 445 446 expect_invalid_attr_for_text(self, b'hibernate 1\n' + HiddenServiceDescriptorV2.content()) 447 448 def test_trailing_line(self): 449 """ 450 Includes a line after the 'router-signature' entry. 451 """ 452 453 expect_invalid_attr_for_text(self, HiddenServiceDescriptorV2.content() + b'\nhibernate 1') 454 455 def test_required_fields(self): 456 """ 457 Check that we require the mandatory fields. 458 """ 459 460 line_to_attr = { 461 'rendezvous-service-descriptor': 'descriptor_id', 462 'version': 'version', 463 'permanent-key': 'permanent_key', 464 'secret-id-part': 'secret_id_part', 465 'publication-time': 'published', 466 'introduction-points': 'introduction_points_encoded', 467 'protocol-versions': 'protocol_versions', 468 'signature': 'signature', 469 } 470 471 for line in REQUIRED_V2_FIELDS: 472 desc_text = HiddenServiceDescriptorV2.content(exclude = (line,)) 473 474 expected = [] if line == 'protocol-versions' else None 475 expect_invalid_attr_for_text(self, desc_text, line_to_attr[line], expected) 476 477 def test_invalid_version(self): 478 """ 479 Checks that our version field expects a numeric value. 480 """ 481 482 test_values = ( 483 '', 484 '-10', 485 'hello', 486 ) 487 488 for test_value in test_values: 489 expect_invalid_attr(self, {'version': test_value}, 'version') 490 491 def test_invalid_protocol_versions(self): 492 """ 493 Checks that our protocol-versions field expects comma separated numeric 494 values. 495 """ 496 497 test_values = ( 498 '', 499 '-10', 500 'hello', 501 '10,', 502 ',10', 503 '10,-10', 504 '10,hello', 505 ) 506 507 for test_value in test_values: 508 expect_invalid_attr(self, {'protocol-versions': test_value}, 'protocol_versions', []) 509 510 def test_introduction_points_when_empty(self): 511 """ 512 It's valid to advertise zero introduciton points. I'm not clear if this 513 would mean an empty protocol-versions field or that it's omitted but either 514 are valid according to the spec. 515 """ 516 517 missing_field_desc = HiddenServiceDescriptorV2.create(exclude = ('introduction-points',)) 518 519 self.assertEqual(None, missing_field_desc.introduction_points_encoded) 520 self.assertEqual([], missing_field_desc.introduction_points_auth) 521 self.assertEqual(None, missing_field_desc.introduction_points_content) 522 self.assertEqual([], missing_field_desc.introduction_points()) 523 524 empty_field_desc = HiddenServiceDescriptorV2.create({'introduction-points': MESSAGE_BLOCK % ''}) 525 526 self.assertEqual((MESSAGE_BLOCK % '').strip(), empty_field_desc.introduction_points_encoded) 527 self.assertEqual([], empty_field_desc.introduction_points_auth) 528 self.assertEqual(b'', empty_field_desc.introduction_points_content) 529 self.assertEqual([], empty_field_desc.introduction_points()) 530 531 def test_introduction_points_when_not_base64(self): 532 """ 533 Checks the introduction-points field when the content isn't base64 encoded. 534 """ 535 536 test_values = ( 537 MESSAGE_BLOCK % '12345', 538 MESSAGE_BLOCK % 'hello', 539 ) 540 541 for test_value in test_values: 542 desc = expect_invalid_attr(self, {'introduction-points': test_value}, 'introduction_points_encoded', test_value.strip()) 543 self.assertEqual([], desc.introduction_points_auth) 544 self.assertEqual(None, desc.introduction_points_content) 545 self.assertEqual([], desc.introduction_points()) 546