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_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_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_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_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(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_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_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_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_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_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 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_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_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 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_invalid_password(tmpdir): with pytest.raises(InvalidPassword): _keystore = Keystore.generate('short', keystore_dir=tmpdir)
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 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 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()