def test_generate_alice_keystore(temp_dir_path): keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path) with pytest.raises(Keystore.Locked): _dec_keypair = keystore.derive_crypto_power(DecryptingPower).keypair keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) assert keystore.derive_crypto_power(DecryptingPower).keypair label = b'test' delegating_power = keystore.derive_crypto_power(DelegatingPower) delegating_pubkey = delegating_power.get_pubkey_from_label(label) bob_pubkey = SecretKey.random().public_key() signer = Signer(SecretKey.random()) delegating_pubkey_again, _kfrags = delegating_power.generate_kfrags( bob_pubkey, signer, label, threshold=2, shares=3) assert delegating_pubkey == delegating_pubkey_again another_delegating_power = keystore.derive_crypto_power(DelegatingPower) another_delegating_pubkey = another_delegating_power.get_pubkey_from_label( label) assert delegating_pubkey == another_delegating_pubkey
def test_verify(testerchain, signature_verifier): message = os.urandom(100) # Generate Umbral key umbral_privkey = SecretKey.random() umbral_pubkey = umbral_privkey.public_key() umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey) # Sign message using SHA-256 hash signer = Signer(umbral_privkey) signature = signer.sign(message) # Get recovery id (v) before using contract v = get_signature_recovery_value(message, signature, umbral_pubkey) recoverable_signature = bytes(signature) + v # Verify signature assert signature_verifier.functions.verify(message, recoverable_signature, umbral_pubkey_bytes, ALGORITHM_SHA256).call() # Verify signature using wrong key umbral_privkey = SecretKey.random() umbral_pubkey_bytes = pubkey_as_uncompressed_bytes( umbral_privkey.public_key()) assert not signature_verifier.functions.verify( message, recoverable_signature, umbral_pubkey_bytes, ALGORITHM_SHA256).call()
def make_random_bob(): """Generates a random ephemeral Bob instance.""" bob_verifying_secret = SecretKey.random() bob_verifying_key = bob_verifying_secret.public_key() decrypting_secret = SecretKey.random() decrypting_key = decrypting_secret.public_key() bob = Bob.from_public_keys(verifying_key=bob_verifying_key, encrypting_key=decrypting_key, federated_only=False) print(f'Created BOB - {bytes(bob.stamp).hex()}') return bob
def test_create_bob_card_inline(click_runner, alice_verifying_key, alice_nickname): command = ('contacts', 'create', '--type', 'b', '--verifying-key', bytes(SecretKey.random().public_key()).hex(), '--encrypting-key', bytes(SecretKey.random().public_key()).hex(), '--nickname', 'hans') assert len(list(Card.CARD_DIR.iterdir())) == 3 result = click_runner.invoke(nucypher_cli, command, catch_exceptions=False) assert result.exit_code == 0, result.output assert 'Saved new card' in result.output assert len(list(Card.CARD_DIR.iterdir())) == 4
def metadata(self): signer = Signer(SecretKey.random()) payload = NodeMetadataPayload( canonical_address=self.canonical_address, domain=':dummy:', timestamp_epoch=0, decentralized_identity_evidence=b'\x00' * LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY, verifying_key=signer.verifying_key(), encrypting_key=SecretKey.random().public_key(), certificate_bytes=b'not a certificate', host='127.0.0.1', port=1111, ) return NodeMetadata(signer=signer, payload=payload)
def test_alice_can_learn_about_a_whole_bunch_of_ursulas(highperf_mocked_alice): # During the fixture execution, Alice verified one node. # TODO: Consider changing this - #1449 assert VerificationTracker.node_verifications == 1 _teacher = highperf_mocked_alice.current_teacher_node() # Ursulas in the fleet have mocked keys, # but we need the teacher to be able to sign the MetadataResponse. signer = Signer(SecretKey.random()) _teacher._stamp = SignatureStamp(verifying_key=signer.verifying_key(), signer=signer) actual_ursula = MOCK_KNOWN_URSULAS_CACHE[_teacher.rest_interface.port] # A quick setup so that the bytes casting of Ursulas (on what in the real world will be the remote node) # doesn't take up all the time. _teacher_known_nodes_bytestring = actual_ursula.bytestring_of_known_nodes() actual_ursula.bytestring_of_known_nodes = lambda *args, **kwargs: _teacher_known_nodes_bytestring # TODO: Formalize this? #1537 with mock_cert_storage, mock_cert_loading, mock_verify_node, mock_message_verification, mock_metadata_validation: started = time.time() highperf_mocked_alice.block_until_number_of_known_nodes_is( 4000, learn_on_this_thread=True) ended = time.time() elapsed = ended - started # TODO: probably can be brought down a lot when the core is moved to Rust assert elapsed < 6 # 6 seconds is still a little long to discover 4000 out of 5000 nodes, but before starting the optimization that went with this test, this operation took about 18 minutes on jMyles' laptop. assert VerificationTracker.node_verifications == 1 # We have only verified the first Ursula. assert sum( isinstance(u, Ursula) for u in highperf_mocked_alice.known_nodes ) < 20 # We haven't instantiated many Ursulas. VerificationTracker.node_verifications = 0 # Cleanup
def test_default_character_configuration_preservation( configuration_class, testerchain, test_registry_source_manager, tmpdir): configuration_class.DEFAULT_CONFIG_ROOT = Path('/tmp') fake_address = '0xdeadbeef' network = TEMPORARY_DOMAIN expected_filename = f'{configuration_class.NAME}.{configuration_class._CONFIG_FILE_EXTENSION}' generated_filename = configuration_class.generate_filename() assert generated_filename == expected_filename expected_filepath = Path('/', 'tmp', generated_filename) if expected_filepath.exists(): expected_filepath.unlink() assert not expected_filepath.exists() if configuration_class == StakeHolderConfiguration: # special case for defaults character_config = StakeHolderConfiguration( provider_uri=testerchain.provider_uri, domain=network) elif configuration_class == UrsulaConfiguration: # special case for rest_host & dev mode # use keystore keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.signing_public_key = SecretKey.random().public_key() character_config = configuration_class(checksum_address=fake_address, domain=network, rest_host=MOCK_IP_ADDRESS, keystore=keystore) else: character_config = configuration_class(checksum_address=fake_address, domain=network) generated_filepath = character_config.generate_filepath() assert generated_filepath == expected_filepath written_filepath = character_config.to_configuration_file() assert written_filepath == expected_filepath assert written_filepath.exists() try: # Read with open(character_config.filepath, 'r') as f: contents = f.read() # Restore from JSON file restored_configuration = configuration_class.from_configuration_file() assert character_config.serialize( ) == restored_configuration.serialize() # File still exists after reading assert written_filepath.exists() finally: if expected_filepath.exists(): expected_filepath.unlink()
def test_key(): field = Key() umbral_pub_key = SecretKey.random().public_key() other_umbral_pub_key = SecretKey.random().public_key() serialized = field._serialize(value=umbral_pub_key, attr=None, obj=None) assert serialized == bytes(umbral_pub_key).hex() assert serialized != bytes(other_umbral_pub_key).hex() deserialized = field._deserialize(value=serialized, attr=None, data=None) assert deserialized == umbral_pub_key assert deserialized != other_umbral_pub_key with pytest.raises(InvalidInputData): field._deserialize(value=b"PublicKey".hex(), attr=None, data=None)
def fragments(): delegating_privkey = SecretKey.random() delegating_pubkey = delegating_privkey.public_key() signing_privkey = SecretKey.random() signer = Signer(signing_privkey) priv_key_bob = SecretKey.random() pub_key_bob = priv_key_bob.public_key() kfrags = generate_kfrags(delegating_sk=delegating_privkey, signer=signer, receiving_pk=pub_key_bob, threshold=2, shares=4, sign_delegating_key=False, sign_receiving_key=False) capsule = MessageKit(delegating_pubkey, b'unused').capsule cfrag = reencrypt(capsule, kfrags[0]) return capsule, cfrag
def test_enrico_control_starts(click_runner): policy_encrypting_key = bytes(SecretKey.random().public_key()).hex() run_args = ('enrico', 'run', '--policy-encrypting-key', policy_encrypting_key, '--dry-run') result = click_runner.invoke(nucypher_cli, run_args, catch_exceptions=False) assert result.exit_code == 0 assert policy_encrypting_key in result.output
def metadata(self): signer = Signer(SecretKey.random()) # A dummy signature with the recovery byte dummy_signature = bytes(signer.sign(b'whatever')) + b'\x00' payload = NodeMetadataPayload( staking_provider_address=self.canonical_address, domain=':dummy:', timestamp_epoch=0, operator_signature=dummy_signature, verifying_key=signer.verifying_key(), encrypting_key=SecretKey.random().public_key(), certificate_der=b'not a certificate', host=MOCK_IP_ADDRESS, port=MOCK_PORT, ) return NodeMetadata(signer=signer, payload=payload)
def test_enrico_encrypt(click_runner): policy_encrypting_key = bytes(SecretKey.random().public_key()).hex() encrypt_args = ('enrico', 'encrypt', '--message', 'to be or not to be', '--policy-encrypting-key', policy_encrypting_key) result = click_runner.invoke(nucypher_cli, encrypt_args, catch_exceptions=False) assert result.exit_code == 0 assert policy_encrypting_key in result.output assert "message_kit" in result.output
def mock_ursula(testerchain, account): ursula_privkey = SecretKey.random() ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(), signer=Signer(ursula_privkey)) signed_stamp = testerchain.client.sign_message(account=account, message=bytes(ursula_stamp)) ursula = Mock(stamp=ursula_stamp, decentralized_identity_evidence=signed_stamp) return ursula
def test_address(testerchain, signature_verifier): # Generate Umbral key and extract "address" from the public key umbral_privkey = SecretKey.random() umbral_pubkey = umbral_privkey.public_key() signer_address = pubkey_as_address(umbral_pubkey) umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey) # Check extracting address in library result_address = signature_verifier.functions.toAddress( umbral_pubkey_bytes).call() assert signer_address == to_normalized_address(result_address)
def generate_doctor_keys(): enc_privkey = SecretKey.random() sig_privkey = SecretKey.random() doctor_privkeys = { 'enc': enc_privkey.to_secret_bytes().hex(), 'sig': sig_privkey.to_secret_bytes().hex(), } with open(DOCTOR_PRIVATE_JSON, 'w') as f: json.dump(doctor_privkeys, f) enc_pubkey = enc_privkey.public_key() sig_pubkey = sig_privkey.public_key() doctor_pubkeys = { 'enc': bytes(enc_pubkey).hex(), 'sig': bytes(sig_pubkey).hex() } with open(DOCTOR_PUBLIC_JSON, 'w') as f: json.dump(doctor_pubkeys, f)
def test_retrieval_kit_field(get_random_checksum_address): field = RetrievalKit() def run_tests_on_kit(kit: RetrievalKitClass): serialized = field._serialize(value=kit, attr=None, obj=None) assert serialized == b64encode(bytes(kit)).decode() deserialized = field._deserialize(value=serialized, attr=None, data=None) assert isinstance(deserialized, RetrievalKitClass) assert deserialized.capsule == kit.capsule assert deserialized.queried_addresses == kit.queried_addresses # kit with list of ursulas encrypting_key = SecretKey.random().public_key() capsule = MessageKit(encrypting_key, b'testing retrieval kit with 2 ursulas').capsule ursulas = [get_random_checksum_address(), get_random_checksum_address()] run_tests_on_kit(kit=RetrievalKitClass( capsule, {to_canonical_address(ursula) for ursula in ursulas})) # kit with no ursulas encrypting_key = SecretKey.random().public_key() capsule = MessageKit(encrypting_key, b'testing retrieval kit with no ursulas').capsule run_tests_on_kit(kit=RetrievalKitClass(capsule, set())) with pytest.raises(InvalidInputData): field._deserialize(value=b"non_base_64_data", attr=None, data=None) with pytest.raises(InvalidInputData): field._deserialize( value=b64encode(b"invalid_retrieval_kit_bytes").decode(), attr=None, data=None)
def test_recover(testerchain, signature_verifier): message = os.urandom(100) # Prepare message hash hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend) hash_ctx.update(message) message_hash = hash_ctx.finalize() # Generate Umbral key and extract "address" from the public key umbral_privkey = SecretKey.random() umbral_pubkey = umbral_privkey.public_key() signer_address = pubkey_as_address(umbral_pubkey) # Sign message signer = Signer(umbral_privkey) signature = signer.sign(message) # Get recovery id (v) before using contract # If we don't have recovery id while signing then we should try to recover public key with different v # Only the correct v will match the correct public key v = get_signature_recovery_value(message, signature, umbral_pubkey) recoverable_signature = bytes(signature) + v # Check recovery method in the contract assert signer_address == to_normalized_address( signature_verifier.functions.recover(message_hash, recoverable_signature).call()) # Also numbers 27 and 28 can be used for v recoverable_signature = recoverable_signature[:-1] + bytes( [recoverable_signature[-1] + 27]) assert signer_address == to_normalized_address( signature_verifier.functions.recover(message_hash, recoverable_signature).call()) # Only number 0,1,27,28 are supported for v recoverable_signature = bytes(signature) + bytes([2]) with pytest.raises((TransactionFailed, ValueError)): signature_verifier.functions.recover(message_hash, recoverable_signature).call() # Signature must include r, s and v recoverable_signature = bytes(signature) with pytest.raises((TransactionFailed, ValueError)): signature_verifier.functions.recover(message_hash, recoverable_signature).call()
def test_verify_eip191(testerchain, signature_verifier): message = os.urandom(100) # Generate Umbral key umbral_privkey = SecretKey.random() umbral_pubkey = umbral_privkey.public_key() umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey) # # Check EIP191 signatures: Version E # # Produce EIP191 signature (version E) signable_message = encode_defunct(primitive=message) signature = Account.sign_message( signable_message=signable_message, private_key=umbral_privkey.to_secret_bytes()) signature = bytes(signature.signature) # Off-chain verify, just in case checksum_address = to_checksum_address( canonical_address_from_umbral_key(umbral_pubkey)) assert verify_eip_191(address=checksum_address, message=message, signature=signature) # Verify signature on-chain version_E = b'E' assert signature_verifier.functions.verifyEIP191(message, signature, umbral_pubkey_bytes, version_E).call() # Of course, it'll fail if we try using version 0 version_0 = b'\x00' assert not signature_verifier.functions.verifyEIP191( message, signature, umbral_pubkey_bytes, version_0).call() # Check that the hash-based method also works independently hash = signature_verifier.functions.hashEIP191(message, version_E).call() eip191_header = "\x19Ethereum Signed Message:\n" + str(len(message)) assert hash == keccak_digest(eip191_header.encode() + message) address = signature_verifier.functions.recover(hash, signature).call() assert address == checksum_address # # Check EIP191 signatures: Version 0 # # Produce EIP191 signature (version 0) validator = to_canonical_address(signature_verifier.address) signable_message = SignableMessage(version=HexBytes(version_0), header=HexBytes(validator), body=HexBytes(message)) signature = Account.sign_message( signable_message=signable_message, private_key=umbral_privkey.to_secret_bytes()) signature = bytes(signature.signature) # Off-chain verify, just in case checksum_address = to_checksum_address( canonical_address_from_umbral_key(umbral_pubkey)) assert checksum_address == Account.recover_message( signable_message=signable_message, signature=signature) # On chain verify signature assert signature_verifier.functions.verifyEIP191(message, signature, umbral_pubkey_bytes, version_0).call() # Of course, now it fails if we try with version E assert not signature_verifier.functions.verifyEIP191( message, signature, umbral_pubkey_bytes, version_E).call() # Check that the hash-based method also works independently hash = signature_verifier.functions.hashEIP191(message, version_0).call() eip191_header = b"\x19\x00" + validator assert hash == keccak_digest(eip191_header + message) address = signature_verifier.functions.recover(hash, signature).call() assert address == checksum_address
def bob_verifying_key(): return bytes(SecretKey.random().public_key()).hex()
def bob_encrypting_key(): return bytes(SecretKey.random().public_key()).hex()
def test_alice_get_ursulas_schema(get_random_checksum_address): # # Input i.e. load # # no args with pytest.raises(InvalidInputData): AliceGetUrsulas().load({}) quantity = 10 required_data = { 'quantity': quantity, 'duration_periods': 4, } # required args AliceGetUrsulas().load(required_data) # missing required args updated_data = {k: v for k, v in required_data.items() if k != 'quantity'} with pytest.raises(InvalidInputData): AliceGetUrsulas().load(updated_data) updated_data = { k: v for k, v in required_data.items() if k != 'duration_periods' } with pytest.raises(InvalidInputData): AliceGetUrsulas().load(updated_data) # optional components # only exclude updated_data = dict(required_data) exclude_ursulas = [] for i in range(2): exclude_ursulas.append(get_random_checksum_address()) updated_data['exclude_ursulas'] = exclude_ursulas AliceGetUrsulas().load(updated_data) # only include updated_data = dict(required_data) include_ursulas = [] for i in range(3): include_ursulas.append(get_random_checksum_address()) updated_data['include_ursulas'] = include_ursulas AliceGetUrsulas().load(updated_data) # both exclude and include updated_data = dict(required_data) updated_data['exclude_ursulas'] = exclude_ursulas updated_data['include_ursulas'] = include_ursulas AliceGetUrsulas().load(updated_data) # list input formatted as ',' separated strings updated_data = dict(required_data) updated_data['exclude_ursulas'] = ','.join(exclude_ursulas) updated_data['include_ursulas'] = ','.join(include_ursulas) data = AliceGetUrsulas().load(updated_data) assert data['exclude_ursulas'] == exclude_ursulas assert data['include_ursulas'] == include_ursulas # single value as string cast to list updated_data = dict(required_data) updated_data['exclude_ursulas'] = exclude_ursulas[0] updated_data['include_ursulas'] = include_ursulas[0] data = AliceGetUrsulas().load(updated_data) assert data['exclude_ursulas'] == [exclude_ursulas[0]] assert data['include_ursulas'] == [include_ursulas[0]] # invalid include entry updated_data = dict(required_data) updated_data['exclude_ursulas'] = exclude_ursulas updated_data['include_ursulas'] = list( include_ursulas) # make copy to modify updated_data['include_ursulas'].append("0xdeadbeef") with pytest.raises(InvalidInputData): AliceGetUrsulas().load(updated_data) # invalid exclude entry updated_data = dict(required_data) updated_data['exclude_ursulas'] = list( exclude_ursulas) # make copy to modify updated_data['exclude_ursulas'].append("0xdeadbeef") updated_data['include_ursulas'] = include_ursulas with pytest.raises(InvalidInputData): AliceGetUrsulas().load(updated_data) # too many ursulas to include updated_data = dict(required_data) too_many_ursulas_to_include = [] while len(too_many_ursulas_to_include) <= quantity: too_many_ursulas_to_include.append(get_random_checksum_address()) updated_data['include_ursulas'] = too_many_ursulas_to_include with pytest.raises(InvalidArgumentCombo): # number of ursulas to include exceeds quantity to sample AliceGetUrsulas().load(updated_data) # include and exclude addresses are not mutually exclusive - include has common entry updated_data = dict(required_data) updated_data['exclude_ursulas'] = exclude_ursulas updated_data['include_ursulas'] = list( include_ursulas) # make copy to modify updated_data['include_ursulas'].append( exclude_ursulas[0]) # one address that overlaps with pytest.raises(InvalidArgumentCombo): # 1 address in both include and exclude lists AliceGetUrsulas().load(updated_data) # include and exclude addresses are not mutually exclusive - exclude has common entry updated_data = dict(required_data) updated_data['exclude_ursulas'] = list( exclude_ursulas) # make copy to modify updated_data['exclude_ursulas'].append( include_ursulas[0]) # on address that overlaps updated_data['include_ursulas'] = include_ursulas with pytest.raises(InvalidArgumentCombo): # 1 address in both include and exclude lists AliceGetUrsulas().load(updated_data) # # Output i.e. dump # ursulas_info = [] expected_ursulas_info = [] port = 11500 for i in range(3): ursula_info = Porter.UrsulaInfo(get_random_checksum_address(), f"https://127.0.0.1:{port+i}", SecretKey.random().public_key()) ursulas_info.append(ursula_info) # use schema to determine expected output (encrypting key gets changed to hex) expected_ursulas_info.append(UrsulaInfoSchema().dump(ursula_info)) output = AliceGetUrsulas().dump(obj={'ursulas': ursulas_info}) assert output == {"ursulas": expected_ursulas_info}