def test_outer_layer_creation(self): """ Outer layer creation. """ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey # minimal layer self.assertTrue(OuterLayer.content().startswith(b'desc-auth-type x25519\ndesc-auth-ephemeral-key ')) self.assertEqual('x25519', OuterLayer.create().auth_type) # specify the parameters desc = OuterLayer.create({ 'desc-auth-type': 'foo', 'desc-auth-ephemeral-key': 'bar', 'auth-client': [ 'JNil86N07AA epkaL79NtajmgME/egi8oA qosYH4rXisxda3X7p9b6fw', '1D8VBAh9hdM 6K/uO3sRqBp6URrKC7GB6Q ElwRj5+6SN9kb8bRhiiQvA', ], 'encrypted': '\n-----BEGIN MESSAGE-----\nmalformed block\n-----END MESSAGE-----', }) self.assertEqual('foo', desc.auth_type) self.assertEqual('bar', desc.ephemeral_key) self.assertEqual('-----BEGIN MESSAGE-----\nmalformed block\n-----END MESSAGE-----', desc.encrypted) self.assertEqual({ '1D8VBAh9hdM': AuthorizedClient(id = b'1D8VBAh9hdM', iv = b'6K/uO3sRqBp6URrKC7GB6Q', cookie = b'ElwRj5+6SN9kb8bRhiiQvA'), 'JNil86N07AA': AuthorizedClient(id = b'JNil86N07AA', iv = b'epkaL79NtajmgME/egi8oA', cookie = b'qosYH4rXisxda3X7p9b6fw'), }, desc.clients) self.assertEqual(EXPECTED_OUTER_LAYER, str(desc)) # create an inner layer then decrypt it revision_counter = 5 blinded_key = stem.util._pubkey_bytes(Ed25519PrivateKey.generate()) subcredential = HiddenServiceDescriptorV3._subcredential(Ed25519PrivateKey.generate(), blinded_key) outer_layer = OuterLayer.create( inner_layer = InnerLayer.create( introduction_points = [ IntroductionPointV3.create_for_address('1.1.1.1', 9001), ] ), revision_counter = revision_counter, subcredential = subcredential, blinded_key = blinded_key, ) inner_layer = InnerLayer._decrypt(outer_layer, revision_counter, subcredential, blinded_key) self.assertEqual(1, len(inner_layer.introduction_points)) self.assertEqual('1.1.1.1', inner_layer.introduction_points[0].link_specifiers[0].address)
def test_intro_point_crypto_without_prereq(self): """ Fetch cryptographic materials when the module is unavailable. """ intro_point = InnerLayer(INNER_LAYER_STR).introduction_points[0] self.assertRaisesWith(ImportError, 'cryptography module unavailable', intro_point.onion_key)
def test_inner_layer(self): """ Parse the inner layer of our test descriptor. """ desc = InnerLayer(INNER_LAYER_STR) self.assertEqual([2], desc.formats) self.assertEqual(['ed25519'], desc.intro_auth) self.assertEqual(True, desc.is_single_service) self.assertEqual(4, len(desc.introduction_points)) intro_point = desc.introduction_points[0] self.assertEqual(2, len(intro_point.link_specifiers)) link_specifier = intro_point.link_specifiers[0] self.assertEqual(stem.client.datatype.LinkByFingerprint, type(link_specifier)) self.assertEqual('CCCCCCCCCCCCCCCCCCCC', link_specifier.fingerprint) link_specifier = intro_point.link_specifiers[1] self.assertEqual(stem.client.datatype.LinkByIPv4, type(link_specifier)) self.assertEqual('1.2.3.4', link_specifier.address) self.assertEqual(9001, link_specifier.port) self.assertEqual('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', intro_point.onion_key_raw) self.assertTrue('ID2l9EFNrp' in intro_point.auth_key_cert.to_base64()) self.assertEqual('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', intro_point.enc_key_raw) self.assertTrue('ZvjPt5IfeQ', intro_point.enc_key_cert) self.assertEqual(None, intro_point.legacy_key_raw) self.assertEqual(None, intro_point.legacy_key_cert)
def test_descriptor_creation(self): """ HiddenServiceDescriptorV3 creation. """ # minimal descriptor self.assertTrue(HiddenServiceDescriptorV3.content().startswith( b'hs-descriptor 3\ndescriptor-lifetime 180\n')) self.assertEqual(180, HiddenServiceDescriptorV3.create().lifetime) # specify the parameters desc = HiddenServiceDescriptorV3.create( { 'hs-descriptor': '4', 'descriptor-lifetime': '123', 'descriptor-signing-key-cert': '\n-----BEGIN ED25519 CERT-----\nmalformed block\n-----END ED25519 CERT-----', 'revision-counter': '5', 'superencrypted': '\n-----BEGIN MESSAGE-----\nmalformed block\n-----END MESSAGE-----', 'signature': 'abcde', }, validate=False) self.assertEqual(4, desc.version) self.assertEqual(123, desc.lifetime) self.assertEqual( None, desc.signing_cert ) # malformed cert dropped because validation is disabled self.assertEqual(5, desc.revision_counter) self.assertEqual( '-----BEGIN MESSAGE-----\nmalformed block\n-----END MESSAGE-----', desc.superencrypted) self.assertEqual('abcde', desc.signature) # include introduction points from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey identity_key = Ed25519PrivateKey.generate() onion_address = HiddenServiceDescriptorV3.address_from_identity_key( identity_key) desc = HiddenServiceDescriptorV3.create( identity_key=identity_key, inner_layer=InnerLayer.create(introduction_points=[ IntroductionPointV3.create_for_address('1.1.1.1', 9001), IntroductionPointV3.create_for_address('2.2.2.2', 9001), IntroductionPointV3.create_for_address('3.3.3.3', 9001), ]), ) inner_layer = desc.decrypt(onion_address) self.assertEqual(3, len(inner_layer.introduction_points)) self.assertEqual( '1.1.1.1', inner_layer.introduction_points[0].link_specifiers[0].address)
def test_intro_point_crypto(self): """ Retrieve IntroductionPointV3 cryptographic materials. """ def base64_key(key): pubkey = stem.util._pubkey_bytes(key) pubkey_b64 = base64.b64encode(pubkey) return stem.util.str_tools._to_unicode(pubkey_b64) from cryptography.hazmat.backends.openssl.x25519 import X25519PublicKey intro_point = InnerLayer(INNER_LAYER_STR).introduction_points[0] self.assertEqual('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', intro_point.onion_key_raw) self.assertEqual('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', intro_point.enc_key_raw) self.assertTrue(isinstance(intro_point.onion_key(), X25519PublicKey)) self.assertTrue(isinstance(intro_point.enc_key(), X25519PublicKey)) self.assertEqual(intro_point.onion_key_raw, base64_key(intro_point.onion_key())) self.assertEqual(intro_point.enc_key_raw, base64_key(intro_point.enc_key())) self.assertEqual(None, intro_point.legacy_key_raw) self.assertEqual(None, intro_point.legacy_key())
def test_inner_layer_creation(self): """ Internal layer creation. """ # minimal layer self.assertEqual(b'create2-formats 2', InnerLayer.content()) self.assertEqual([2], InnerLayer.create().formats) # specify their only mandatory parameter (formats) self.assertEqual(b'create2-formats 1 2 3', InnerLayer.content({'create2-formats': '1 2 3'})) self.assertEqual([1, 2, 3], InnerLayer.create({ 'create2-formats': '1 2 3' }).formats) # include optional parameters desc = InnerLayer.create( collections.OrderedDict(( ('intro-auth-required', 'ed25519'), ('single-onion-service', ''), ))) self.assertEqual([2], desc.formats) self.assertEqual(['ed25519'], desc.intro_auth) self.assertEqual(True, desc.is_single_service) self.assertEqual([], desc.introduction_points) # include introduction points desc = InnerLayer.create(introduction_points=[ IntroductionPointV3.create_for_address('1.1.1.1', 9001), IntroductionPointV3.create_for_address('2.2.2.2', 9001), IntroductionPointV3.create_for_address('3.3.3.3', 9001), ]) self.assertEqual(3, len(desc.introduction_points)) self.assertEqual( '1.1.1.1', desc.introduction_points[0].link_specifiers[0].address) self.assertTrue( InnerLayer.content(introduction_points=[ IntroductionPointV3.create_for_address('1.1.1.1', 9001), ]).startswith( b'create2-formats 2\nintroduction-point AQAGAQEBASMp'))
def __init__(self, onion_address, identity_priv_key, blinding_param, intro_points, is_first_desc): # Timestamp of the last attempt to assemble this descriptor self.last_publish_attempt_ts = None # Timestamp we last uploaded this descriptor self.last_upload_ts = None # Set of responsible HSDirs for last time we uploaded this descriptor self.responsible_hsdirs = None # Start generating descriptor desc_signing_key = Ed25519PrivateKey.generate() # Get the intro points for this descriptor and recertify them! recertified_intro_points = [] for ip in intro_points: recertified_intro_points.append( self._recertify_intro_point(ip, desc_signing_key)) rev_counter = self._get_revision_counter(identity_priv_key, is_first_desc) v3_desc_inner_layer = InnerLayer.create( introduction_points=recertified_intro_points) v3_desc = HiddenServiceDescriptorV3.create( blinding_nonce=blinding_param, identity_key=identity_priv_key, signing_key=desc_signing_key, inner_layer=v3_desc_inner_layer, revision_counter=int(rev_counter), ) # TODO stem should probably initialize it itself so that it has balance # between descriptor creation (where this is not inted) and descriptor # parsing (where this is inited) v3_desc._inner_layer = v3_desc_inner_layer # Check max size is within range if len(str(v3_desc)) > params.MAX_DESCRIPTOR_SIZE: logger.error( "Created descriptor is too big (%d intros). Consider " "relaxing number of instances or intro points per instance " "(see N_INTROS_PER_INSTANCE)") raise BadDescriptor super().__init__(onion_address, v3_desc)