def test_restore_keystore_from_mnemonic(tmpdir, mocker): # Setup spy = mocker.spy(Mnemonic, 'generate') # Decrypt post-generation keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) mnemonic = Mnemonic(_MNEMONIC_LANGUAGE) words = spy.spy_return secret = bytes(mnemonic.to_entropy(words)) keystore_path = keystore.keystore_path # remove local and disk references, simulating a # lost keystore or forgotten password. del keystore os.unlink(keystore_path) # prove the keystore is lost or missing assert not keystore_path.exists() with pytest.raises(Keystore.NotFound): _keystore = Keystore(keystore_path=keystore_path) # Restore with user-supplied words and a new password keystore = Keystore.restore(words=words, password='******') keystore.unlock(password='******') assert keystore._Keystore__secret == secret
def test_invalid_keystore_file_type(tmp_path, tmp_path_factory): # Not a file invalid_path = Path() with pytest.raises(ValueError, match="Keystore path must be a file."): _keystore = Keystore(invalid_path) invalid_path = Path(tmp_path) with pytest.raises(ValueError, match="Keystore path must be a file."): _keystore = Keystore(invalid_path) # Not an existing file invalid_path = Path('does-not-exist') with pytest.raises(Keystore.NotFound, match=f"Keystore '{invalid_path.absolute()}' does not exist."): _keystore = Keystore(invalid_path)
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_unlock_nucypher_keystore_dev_mode(mocker, test_emitter, capsys, alice_blockchain_test_config, tmpdir): # Setup unlock_spy = mocker.spy(Keystore, 'unlock') mocker.patch.object(CharacterConfiguration, 'dev_mode', return_value=True, new_callable=mocker.PropertyMock) keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) alice_blockchain_test_config.attach_keystore(keystore) result = unlock_nucypher_keystore( emitter=test_emitter, password=INSECURE_DEVELOPMENT_PASSWORD, character_configuration=alice_blockchain_test_config) assert result output = capsys.readouterr().out message = DECRYPTING_CHARACTER_KEYSTORE.format( name=alice_blockchain_test_config.NAME.capitalize()) assert message in output unlock_spy.assert_not_called()
def test_unlock_nucypher_keystore_invalid_password( mocker, test_emitter, alice_blockchain_test_config, capsys, tmpdir, test_registry_source_manager): # Setup mocker.patch.object(passwords, 'secret_box_decrypt', side_effect=SecretBoxAuthenticationError) mocker.patch.object(CharacterConfiguration, 'dev_mode', return_value=False, new_callable=mocker.PropertyMock) keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) alice_blockchain_test_config.attach_keystore(keystore) # Test with pytest.raises(Keystore.AuthenticationFailed): unlock_nucypher_keystore( emitter=test_emitter, password=INSECURE_DEVELOPMENT_PASSWORD + 'typo', character_configuration=alice_blockchain_test_config) captured = capsys.readouterr() assert DECRYPTING_CHARACTER_KEYSTORE.format( name=alice_blockchain_test_config.NAME.capitalize()) in captured.out
def test_unlock_nucypher_keystore(mocker, test_emitter, capsys, alice_blockchain_test_config, patch_keystore, tmpdir): # Setup # Do not test "real" unlocking here, just the plumbing unlock_spy = mocker.patch.object(Keystore, 'unlock', return_value=True) mocker.patch.object(CharacterConfiguration, 'dev_mode', return_value=False, new_callable=mocker.PropertyMock) mocker.patch.object(Mnemonic, 'detect_language', return_value='english') keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) alice_blockchain_test_config.attach_keystore(keystore) result = unlock_nucypher_keystore( emitter=test_emitter, password=INSECURE_DEVELOPMENT_PASSWORD, character_configuration=alice_blockchain_test_config) assert result captured = capsys.readouterr() message = DECRYPTING_CHARACTER_KEYSTORE.format( name=alice_blockchain_test_config.NAME.capitalize()) assert message in captured.out unlock_spy.assert_called_once_with(password=INSECURE_DEVELOPMENT_PASSWORD)
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_keystore_persistence(tmpdir): """Regression test for keystore file persistence""" keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) path = keystore.keystore_path del keystore assert path.exists()
def test_keystore_lock_unlock(tmpdir): keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) # locked by default assert not keystore.is_unlocked assert keystore._Keystore__secret is KEYSTORE_LOCKED # incorrect password with pytest.raises(Keystore.AuthenticationFailed): keystore.unlock('opensaysme') # unlock keystore.unlock(INSECURE_DEVELOPMENT_PASSWORD) assert keystore.is_unlocked assert keystore._Keystore__secret != KEYSTORE_LOCKED assert isinstance(keystore._Keystore__secret, bytes) # unlock when already unlocked keystore.unlock(INSECURE_DEVELOPMENT_PASSWORD) assert keystore.is_unlocked # incorrect password when already unlocked with pytest.raises(Keystore.AuthenticationFailed): keystore.unlock('opensaysme') # lock keystore.lock() assert not keystore.is_unlocked # lock when already locked keystore.lock() assert not keystore.is_unlocked
def test_initialize_ursula_defaults(click_runner, mocker, tmpdir): # Mock out filesystem writes mocker.patch.object(UrsulaConfiguration, 'initialize', autospec=True) mocker.patch.object(UrsulaConfiguration, 'to_configuration_file', autospec=True) # Mock Keystore init keystore = Keystore.generate(keystore_dir=tmpdir, password=INSECURE_DEVELOPMENT_PASSWORD) mocker.patch.object(CharacterConfiguration, 'keystore', return_value=keystore, new_callable=PropertyMock) # Use default ursula init args init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--federated-only') user_input = YES_ENTER + FAKE_PASSWORD_CONFIRMED result = click_runner.invoke(nucypher_cli, init_args, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # REST Host assert "Is this the public-facing address of Ursula? " in result.output # Auth assert COLLECT_NUCYPHER_PASSWORD in result.output, 'WARNING: User was not prompted for password' assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
def test_derive_hosting_power(tmpdir): keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) hosting_power = keystore.derive_crypto_power(power_class=TLSHostingPower, host=LOOPBACK_ADDRESS) assert hosting_power.public_key().public_numbers() assert hosting_power.keypair.certificate.public_bytes(encoding=Encoding.PEM) rederived_hosting_power = keystore.derive_crypto_power(power_class=TLSHostingPower, host=LOOPBACK_ADDRESS) assert hosting_power.public_key().public_numbers() == rederived_hosting_power.public_key().public_numbers()
def test_derive_delegating_power(tmpdir): keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) delegating_power = keystore.derive_crypto_power(power_class=DelegatingPower) parent_skf = SecretKeyFactory.from_secure_randomness(keystore._Keystore__secret) child_skf = parent_skf.make_factory(_DELEGATING_INFO) assert delegating_power._DelegatingPower__secret_key_factory.to_secret_bytes() == child_skf.to_secret_bytes() assert delegating_power._get_privkey_from_label(label=b'some-label')
def test_import_custom_keystore(tmpdir): # Too short - 32 bytes is required custom_secret = b'tooshort' with pytest.raises(ValueError, match=f'Entropy bytes bust be exactly {SecretKey.serialized_size()}.'): _keystore = Keystore.import_secure(key_material=custom_secret, password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) # Too short - 32 bytes is required custom_secret = b'thisisabunchofbytesthatisabittoolong' with pytest.raises(ValueError, match=f'Entropy bytes bust be exactly {SecretKey.serialized_size()}.'): _keystore = Keystore.import_secure(key_material=custom_secret, password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) # Import private key custom_secret = os.urandom(SecretKey.serialized_size()) # insecure but works keystore = Keystore.import_secure(key_material=custom_secret, password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) assert keystore._Keystore__secret == custom_secret keystore.lock() path = keystore.keystore_path del keystore # Restore custom secret from encrypted keystore file keystore = Keystore(keystore_path=path) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) assert keystore._Keystore__secret == custom_secret
def test_keystore_generation_defaults(tmp_path_factory): # Setup parent = Path(tmp_path_factory.mktemp('test-keystore-')) parent.touch(exist_ok=True) # Test keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=parent) assert not keystore.is_unlocked # defaults to locked assert keystore._Keystore__secret is KEYSTORE_LOCKED assert parent in keystore.keystore_path.parents # created in the correct directory
def test_decrypt_keystore(tmpdir, mocker): # Setup spy = mocker.spy(Mnemonic, 'generate') # Decrypt post-generation keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) mnemonic = Mnemonic(_MNEMONIC_LANGUAGE) words = spy.spy_return secret = bytes(mnemonic.to_entropy(words)) assert keystore._Keystore__secret == secret # Decrypt from keystore file keystore_path = keystore.keystore_path del words del keystore keystore = Keystore(keystore_path=keystore_path) keystore.unlock(INSECURE_DEVELOPMENT_PASSWORD) assert keystore._Keystore__secret == secret
def recover_keystore(emitter) -> None: emitter.message('This procedure will recover your nucypher keystore from mnemonic seed words. ' 'You will need to provide the entire mnemonic (space seperated) in the correct ' 'order and choose a new password.', color='cyan') click.confirm('Do you want to continue', abort=True) __words = click.prompt("Enter nucypher keystore seed words") word_count = len(__words.split()) if word_count != _WORD_COUNT: emitter.message(f'Invalid mnemonic - Number of words must be {str(_WORD_COUNT)}, but only got {word_count}') __password = get_nucypher_password(emitter=emitter, confirm=True) keystore = Keystore.restore(words=__words, password=__password) emitter.message(f'Recovered nucypher keystore {keystore.id} to \n {keystore.keystore_path}', color='green')
def write_keystore(self, password: str, key_material: Optional[bytes] = None, interactive: bool = True) -> Keystore: if key_material: self.__keystore = Keystore.import_secure( key_material=key_material, password=password, keystore_dir=self.keystore_dir) else: if interactive: self.__keystore = Keystore.generate( password=password, keystore_dir=self.keystore_dir, interactive=interactive) else: self.__keystore, _ = Keystore.generate( password=password, keystore_dir=self.keystore_dir, interactive=interactive) return self.keystore
def test_keystore_instantiation_defaults(tmp_path_factory): # Setup parent = Path(tmp_path_factory.mktemp('test-keystore-')) parent.touch(exist_ok=True) keystore_id = ''.join(random.choice(string.hexdigits.lower()) for _ in range(Keystore._ID_SIZE)) path = parent / f'123-{keystore_id}.priv' path.touch() # Test keystore = Keystore(path) assert keystore.keystore_path == path # retains the correct keystore path assert keystore.id == keystore_id # accurately parses filename for ID assert not keystore.is_unlocked # defaults to locked assert keystore._Keystore__secret is KEYSTORE_LOCKED assert parent in keystore.keystore_path.parents # created in the correct directory
def test_tls_hosting_certificate_remains_the_same(temp_dir_path, mocker): keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) rest_port = 12345 db_filepath = tempfile.mkdtemp() ursula = Ursula(federated_only=True, start_learning_now=False, keystore=keystore, rest_host=LOOPBACK_ADDRESS, rest_port=rest_port, db_filepath=db_filepath, domain=TEMPORARY_DOMAIN) assert ursula.keystore is keystore assert ursula.certificate == ursula._crypto_power.power_ups( TLSHostingPower).keypair.certificate original_certificate_bytes = ursula.certificate.public_bytes( encoding=Encoding.DER) ursula.disenchant() del ursula spy_rest_server_init = mocker.spy(ProxyRESTServer, '__init__') recreated_ursula = Ursula(federated_only=True, start_learning_now=False, keystore=keystore, rest_host=LOOPBACK_ADDRESS, rest_port=rest_port, db_filepath=db_filepath, domain=TEMPORARY_DOMAIN) assert recreated_ursula.keystore is keystore assert recreated_ursula.certificate.public_bytes( encoding=Encoding.DER) == original_certificate_bytes tls_hosting_power = recreated_ursula._crypto_power.power_ups( TLSHostingPower) spy_rest_server_init.assert_called_once_with( ANY, # self rest_host=LOOPBACK_ADDRESS, rest_port=rest_port, rest_app=IsType(Flask), datastore=IsType(Datastore), hosting_power=tls_hosting_power) recreated_ursula.disenchant()
def test_characters_use_keystore(temp_dir_path): keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) alice = Alice(federated_only=True, start_learning_now=False, keystore=keystore) Bob(federated_only=True, start_learning_now=False, keystore=keystore) Ursula(federated_only=True, start_learning_now=False, keystore=keystore, rest_host=LOOPBACK_ADDRESS, rest_port=12345, db_filepath=tempfile.mkdtemp(), domain=TEMPORARY_DOMAIN) alice.disenchant( ) # To stop Alice's publication threadpool. TODO: Maybe only start it at first enactment?
def test_initialize_alice_defaults(click_runner, mocker, custom_filepath, monkeypatch, blockchain_ursulas, tmpdir): monkeypatch.delenv(NUCYPHER_ENVVAR_KEYSTORE_PASSWORD, raising=False) # Mock out filesystem writes mocker.patch.object(AliceConfiguration, 'initialize', autospec=True) mocker.patch.object(AliceConfiguration, 'to_configuration_file', autospec=True) mocker.patch.object(LocalFileBasedNodeStorage, 'all', return_value=blockchain_ursulas) # Mock Keystore init keystore = Keystore.generate(keystore_dir=tmpdir, password=INSECURE_DEVELOPMENT_PASSWORD) mocker.patch.object(CharacterConfiguration, 'keystore', return_value=keystore, new_callable=PropertyMock) # Use default alice init args init_args = ('alice', 'init', '--network', TEMPORARY_DOMAIN, '--config-root', str(custom_filepath.absolute()), '--federated-only') result = click_runner.invoke(nucypher_cli, init_args, input=FAKE_PASSWORD_CONFIRMED, catch_exceptions=False) assert result.exit_code == 0 # REST Host assert "nucypher alice run" in result.output # Auth assert COLLECT_NUCYPHER_PASSWORD in result.output, 'WARNING: User was not prompted for password' assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
def test_keystore_generate_report_interactive_false(tmpdir): _keystore, words = Keystore.generate( INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir, interactive=False) assert len(words.split(" ")) == 24
def __init__( self, # Base emitter=None, config_root: Optional[Path] = None, filepath: Optional[Path] = None, # Mode dev_mode: bool = False, federated_only: bool = False, # Identity checksum_address: str = None, crypto_power: CryptoPower = None, # Keystore keystore: Keystore = None, keystore_path: Optional[Path] = None, # Learner learn_on_same_thread: bool = False, abort_on_learning_error: bool = False, start_learning_now: bool = True, # Network controller_port: int = None, domain: str = DEFAULT_DOMAIN, network_middleware: RestMiddleware = None, lonely: bool = False, # Node Storage known_nodes: set = None, node_storage: NodeStorage = None, reload_metadata: bool = True, save_metadata: bool = True, # Blockchain poa: bool = None, light: bool = False, provider_uri: str = None, gas_strategy: Union[Callable, str] = DEFAULT_GAS_STRATEGY, max_gas_price: Optional[int] = None, signer_uri: str = None, # Registry registry: BaseContractRegistry = None, registry_filepath: Optional[Path] = None, # Deployed Workers worker_data: dict = None): self.log = Logger(self.__class__.__name__) # This constant is used to signal that a path can be generated if one is not provided. UNINITIALIZED_CONFIGURATION.bool_value(False) # Identity # NOTE: NodeConfigurations can only be used with Self-Characters self.is_me = True self.checksum_address = checksum_address # Keystore self.crypto_power = crypto_power if keystore_path and not keystore: keystore = Keystore(keystore_path=keystore_path) self.__keystore = self.__keystore = keystore or NO_KEYSTORE_ATTACHED.bool_value( False) self.keystore_dir = Path( keystore.keystore_path ).parent if keystore else UNINITIALIZED_CONFIGURATION # Contract Registry if registry and registry_filepath: if registry.filepath != registry_filepath: error = f"Inconsistent registry filepaths for '{registry.filepath.absolute()}'" \ f" and '{registry_filepath.absolute()}'." raise ValueError(error) else: self.log.warn( f"Registry and registry filepath were both passed.") self.registry = registry or NO_BLOCKCHAIN_CONNECTION.bool_value(False) self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION # Blockchain self.poa = poa self.is_light = light self.provider_uri = provider_uri or NO_BLOCKCHAIN_CONNECTION self.signer_uri = signer_uri or None # Learner self.federated_only = federated_only self.domain = domain self.learn_on_same_thread = learn_on_same_thread self.abort_on_learning_error = abort_on_learning_error self.start_learning_now = start_learning_now self.save_metadata = save_metadata self.reload_metadata = reload_metadata self.known_nodes = known_nodes or set() # handpicked self.lonely = lonely # Configuration self.__dev_mode = dev_mode self.config_file_location = filepath or UNINITIALIZED_CONFIGURATION self.config_root = UNINITIALIZED_CONFIGURATION # Deployed Workers self.worker_data = worker_data # # Federated vs. Blockchain arguments consistency # # # Federated # if self.federated_only: # Check for incompatible values blockchain_args = { 'filepath': registry_filepath, 'poa': poa, 'provider_uri': provider_uri, 'gas_strategy': gas_strategy, 'max_gas_price': max_gas_price } if any(blockchain_args.values()): bad_args = ", ".join(f"{arg}={val}" for arg, val in blockchain_args.items() if val) self.log.warn( f"Arguments {bad_args} are incompatible with federated_only. " f"Overridden with a sane default.") # Clear decentralized attributes to ensure consistency with a # federated configuration. self.poa = False self.is_light = False self.provider_uri = None self.registry_filepath = None self.gas_strategy = None self.max_gas_price = None # # Decentralized # else: self.gas_strategy = gas_strategy self.max_gas_price = max_gas_price # gwei is_initialized = BlockchainInterfaceFactory.is_interface_initialized( provider_uri=self.provider_uri) if not is_initialized and provider_uri: BlockchainInterfaceFactory.initialize_interface( provider_uri=self.provider_uri, poa=self.poa, light=self.is_light, emitter=emitter, gas_strategy=self.gas_strategy, max_gas_price=self.max_gas_price) else: self.log.warn( f"Using existing blockchain interface connection ({self.provider_uri})." ) if not self.registry: # TODO: These two code blocks are untested. if not self.registry_filepath: # TODO: Registry URI (goerli://speedynet.json) :-) self.log.info(f"Fetching latest registry from source.") self.registry = InMemoryContractRegistry.from_latest_publication( network=self.domain) else: self.registry = LocalContractRegistry( filepath=self.registry_filepath) self.log.info(f"Using local registry ({self.registry}).") self.testnet = self.domain != NetworksInventory.MAINNET self.signer = Signer.from_signer_uri(self.signer_uri, testnet=self.testnet) if dev_mode: self.__temp_dir = UNINITIALIZED_CONFIGURATION self._setup_node_storage() self.initialize(password=DEVELOPMENT_CONFIGURATION) else: self.__temp_dir = LIVE_CONFIGURATION self.config_root = config_root or self.DEFAULT_CONFIG_ROOT self._cache_runtime_filepaths() self._setup_node_storage(node_storage=node_storage) # Network self.controller_port = controller_port or self.DEFAULT_CONTROLLER_PORT self.network_middleware = network_middleware or self.DEFAULT_NETWORK_MIDDLEWARE( registry=self.registry) super().__init__(filepath=self.config_file_location, config_root=self.config_root)
def test_keystore_invalid_password(tmpdir): with pytest.raises(InvalidPassword): _keystore = Keystore.generate('short', keystore_dir=tmpdir)
def test_derive_decrypting_power(tmpdir): keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) decrypting_power = keystore.derive_crypto_power(power_class=DecryptingPower) assert bytes(decrypting_power.public_key()).hex() assert decrypting_power.keypair.fingerprint()
def test_keystore_derive_crypto_power_without_unlock(tmpdir): keystore = Keystore.generate(INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=tmpdir) with pytest.raises(Keystore.Locked): keystore.derive_crypto_power(power_class=DecryptingPower)
def __init__(self, domain: str = None, known_node_class: object = None, is_me: bool = True, federated_only: bool = False, checksum_address: str = None, network_middleware: RestMiddleware = None, keystore: Keystore = None, crypto_power: CryptoPower = None, crypto_power_ups: List[CryptoPowerUp] = None, eth_provider_uri: str = None, signer: Signer = None, registry: BaseContractRegistry = None, include_self_in_the_state: bool = False, *args, **kwargs) -> None: """ A participant in the cryptological drama (a screenplay, if you like) of NuCypher. Characters can represent users, nodes, wallets, offline devices, or other objects of varying levels of abstraction. The Named Characters use this class as a Base, and achieve their individuality from additional methods and PowerUps. PowerUps ======== :param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower. :param crypto_power_ups: If crypto_power is not provided, a new one will be made to consume all CryptoPowerUps. If neither crypto_power nor crypto_power_ups are provided, we give this Character all CryptoPowerUps listed in their _default_crypto_powerups attribute. :param is_me: Set this to True when you want this Character to represent the owner of the configuration under which the program is being run. A Character who is_me can do things that other Characters can't, like run servers, sign messages, and decrypt messages which are encrypted for them. Typically this will be True for exactly one Character, but there are scenarios in which its imaginable to be represented by zero Characters or by more than one Character. """ # # Prologue of the federation # # FIXME: excuse me... can I speak to the manager? if is_me: # If this is a federated-is_me-character, assume everyone else is too. self._set_known_node_class(known_node_class, federated_only) else: # What an awful hack. The last convulsions of #466. # TODO: Anything else. with suppress(AttributeError): federated_only = known_node_class._federated_only_instances if federated_only: if registry or eth_provider_uri: raise ValueError( f"Cannot init federated-only character with {registry or eth_provider_uri}." ) self.federated_only: bool = federated_only ########################################## # # Keys & Powers # if keystore: crypto_power_ups = list() for power_up in self._default_crypto_powerups: power = keystore.derive_crypto_power(power_class=power_up) crypto_power_ups.append(power) self.keystore = keystore if crypto_power and crypto_power_ups: raise ValueError( "Pass crypto_power or crypto_power_ups (or neither), but not both." ) crypto_power_ups = crypto_power_ups or list() # type: list if crypto_power: self._crypto_power = crypto_power # type: CryptoPower elif crypto_power_ups: self._crypto_power = CryptoPower(power_ups=crypto_power_ups) else: self._crypto_power = CryptoPower( power_ups=self._default_crypto_powerups) # # Self # if is_me: # Signing Power self.signer = signer try: signing_power = self._crypto_power.power_ups( SigningPower) # type: SigningPower self._stamp = signing_power.get_signature_stamp( ) # type: SignatureStamp except NoSigningPower: self._stamp = NO_SIGNING_POWER # Blockchainy if not self.federated_only: self.eth_provider_uri = eth_provider_uri self.registry = registry or InMemoryContractRegistry.from_latest_publication( network=domain) # See #1580 else: self.registry = NO_BLOCKCHAIN_CONNECTION.bool_value(False) # REST self.network_middleware = network_middleware or RestMiddleware( registry=self.registry, eth_provider_uri=eth_provider_uri) # Learner Learner.__init__( self, domain=domain, network_middleware=self.network_middleware, node_class=known_node_class, include_self_in_the_state=include_self_in_the_state, *args, **kwargs) if self.federated_only: try: derived_federated_address = self.derive_federated_address() except NoSigningPower: # TODO: Why allow such a character (without signing power) to be created at all? derived_federated_address = NO_SIGNING_POWER.bool_value( False) if checksum_address and (checksum_address != derived_federated_address): raise ValueError( f"Provided checksum address {checksum_address} " f"does not match federated character's verifying key {derived_federated_address}" ) checksum_address = derived_federated_address self.checksum_address = checksum_address # # Stranger # else: if network_middleware is not None: raise TypeError( "Network middleware cannot be attached to a Stranger-Character." ) if registry is not None: raise TypeError( "Registry cannot be attached to stranger-Characters.") verifying_key = self.public_keys(SigningPower) self._stamp = StrangerStamp(verifying_key) self.keystore_dir = STRANGER self.network_middleware = STRANGER self.checksum_address = checksum_address self.__setup_nickname(is_me=is_me) # Character Control # TODO: have argument about meaning of 'lawful' and whether maybe only Lawful characters have an interface if hasattr(self, '_interface_class'): # Controller Interface self.interface = self._interface_class(character=self) self.controller = NO_CONTROL_PROTOCOL