コード例 #1
0
def test_reencrypt_user_secret_on_set_password(empty_data_folder):
    """
    Should re-encrypt the user_secret when the user changes their password.
    """
    specter = Specter(data_folder=empty_data_folder)

    password = "******"
    user = User.from_json(
        user_dict={
            "id": "someuser",
            "username": "******",
            "password": hash_password(password),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter,
    )

    # Force generation of a new user_secret
    user.decrypt_user_secret(password)
    assert user.encrypted_user_secret is not None
    assert user.plaintext_user_secret is not None

    first_encrypted_user_secret = user.encrypted_user_secret
    first_plaintext_user_secret = user.plaintext_user_secret

    new_password = "******"
    user.set_password(new_password)

    # The new encrypted_user_secret will be different...
    assert first_encrypted_user_secret != user.encrypted_user_secret

    # ...but the plaintext_user_secret remains unchanged
    assert first_plaintext_user_secret == user.plaintext_user_secret
コード例 #2
0
    def remove_all_services_from_user(self, user: User):
        """
        Clears User.services and `user_secret`; wipes the User's
        ServiceEncryptedStorage.
        """
        # Don't show any Services on the sidebar for the admin user
        user.services.clear()

        # Reset as if we never had any encrypted storage
        user.delete_user_secret(autosave=False)
        user.save_info()

        if self.user_has_encrypted_storage(user=user):
            # Encrypted Service data is now orphaned since there is no
            # password. So wipe it from the disk.
            ServiceEncryptedStorageManager.get_instance().delete_all_service_data(user)
コード例 #3
0
def test_generate_user_secret_on_set_password(empty_data_folder):
    """
    Should generate a user_secret if one does not yet exist when the User's password
    is changed via set_password.
    """
    specter = Specter(data_folder=empty_data_folder)

    password = "******"
    user = User.from_json(
        user_dict={
            "id": "someuser",
            "username": "******",
            "password": hash_password(password),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter,
    )

    assert user.encrypted_user_secret is None
    assert user.plaintext_user_secret is None

    new_password = "******"
    user.set_password(new_password)

    assert user.encrypted_user_secret is not None
    assert user.plaintext_user_secret is not None

    # Reset the plaintext user_secret and test decryption
    user.plaintext_user_secret = None
    user.decrypt_user_secret(new_password)

    assert user.plaintext_user_secret is not None
コード例 #4
0
def test_generate_user_secret_on_decrypt_user_secret(empty_data_folder):
    """
    Should generate a user_secret if one does not yet exist when decrypt_user_secret
    is called (happens during the login flow).
    """
    specter = Specter(data_folder=empty_data_folder)

    password = "******"
    user = User.from_json(
        user_dict={
            "id": "someuser",
            "username": "******",
            "password": hash_password(password),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter,
    )

    assert user.encrypted_user_secret is None
    assert user.plaintext_user_secret is None

    # Even though there's no user_secret yet, the flow calls decrypt anyway...
    user.decrypt_user_secret(password)

    # ...and a new user_secret is created and stored encrypted and plaintext
    assert user.encrypted_user_secret is not None
    assert user.plaintext_user_secret is not None
コード例 #5
0
def test_ServiceEncryptedStorage(empty_data_folder):
    specter_mock = Mock()
    specter_mock.config = {"uid": ""}
    specter_mock.user_manager = Mock()
    specter_mock.user_manager.users = [""]

    user1 = User.from_json(
        user_dict={
            "id": "user1",
            "username": "******",
            "password": hash_password("somepassword"),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter_mock,
    )
    user2 = User.from_json(
        user_dict={
            "id": "user2",
            "username": "******",
            "password": hash_password("somepassword"),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter_mock,
    )
    user1._generate_user_secret("muh")

    # Can set and get service storage fields
    service_storage = ServiceEncryptedStorage(empty_data_folder, user1)
    service_storage.set_service_data("a_service_id", {"somekey": "green"})
    assert service_storage.get_service_data("a_service_id") == {
        "somekey": "green"
    }
    assert service_storage.get_service_data("another_service_id") == {}

    # We expect a call for a user that isn't logged in to fail
    with pytest.raises(ServiceEncryptedStorageError) as execinfo:
        ServiceEncryptedStorage(empty_data_folder, user2)
    assert "must be authenticated with password" in str(execinfo.value)
コード例 #6
0
def test_storage_field_encrypt_decrypt(empty_data_folder):
    """
    Storage class should be able to use the associated User's decrypted user_secret
    to encrypt and decrypt the specified encrypted_fields to and from on-disk
    json. When loaded into memory, all fields -- whether encrypted or not -- should
    be plaintext readable.
    """
    specter = Specter(data_folder=empty_data_folder)

    password = "******"
    user = User.from_json(
        user_dict={
            "id": "someuser",
            "username": "******",
            "password": hash_password("somepassword"),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter,
    )

    # User must provide their password in order to decrypt their user_secret which is
    #   then used to decrypt/encrypt their service storage
    user.decrypt_user_secret(password)

    storage = ExampleStorage(
        data_folder=specter.data_folder, encryption_key=user.plaintext_user_secret
    )
    storage.data["testfield1"] = "This data is not encrypted"
    storage.data["testfield2"] = "This data WILL BE encrypted"
    storage._save()

    # Read the resulting storage file
    with open(storage.data_file, "r") as storage_json_file:
        data_on_disk = json.load(storage_json_file)
    print(data_on_disk)

    # Plaintext fields are readable...
    assert data_on_disk["testfield1"] == storage.data["testfield1"]

    # ...while encrypted fields are not
    assert data_on_disk["testfield2"] != storage.data["testfield2"]

    # Re-instantiate the storage so it has to load from the saved file...
    storage_2 = ExampleStorage(
        data_folder=specter.data_folder, encryption_key=user.plaintext_user_secret
    )

    # ...and verify the field decryption
    assert storage_2.data["testfield2"] == storage.data["testfield2"]
    assert data_on_disk["testfield2"] != storage_2.data["testfield2"]
コード例 #7
0
def test_WalletImporter_integration(specter_regtest_configured,
                                    bitcoin_regtest):
    """
    WalletImporter can load a wallet from a backup json with unknown devices and
    initialize a watch-only wallet that can receive funds and update its balance.
    """
    specter = specter_regtest_configured
    someuser: User = specter.user_manager.add_user(
        User.from_json(
            {
                "id": "someuser",
                "username": "******",
                "password": "******",
                "config": {},
                "is_admin": False,
            },
            specter,
        ))
    specter.user_manager.save()
    specter.check()

    # Create a Wallet
    wallet_json = '{"label": "another_simple_wallet", "blockheight": 0, "descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/0/*)#xp8lv5nr", "devices": [{"type": "trezor", "label": "trezor"}]} '
    wallet_importer = WalletImporter(wallet_json,
                                     specter,
                                     device_manager=someuser.device_manager)
    wallet_importer.create_nonexisting_signers(
        someuser.device_manager,
        {
            "unknown_cosigner_0_name": "trezor",
            "unknown_cosigner_0_type": "trezor"
        },
    )
    dm: DeviceManager = someuser.device_manager
    wallet = wallet_importer.create_wallet(someuser.wallet_manager)
    # fund it with some coins
    bitcoin_regtest.testcoin_faucet(address=wallet.getnewaddress(),
                                    confirm_payment=False)

    # There can be a delay in the node generating the faucet deposit tx so keep
    #   rechecking until it's done (or we timeout).
    for i in range(0, 15):
        wallet.update()
        if wallet.update_balance()["untrusted_pending"] != 0:
            break
        else:
            time.sleep(2)

    wallet = someuser.wallet_manager.get_by_alias("another_simple_wallet")
    assert wallet.update_balance()["untrusted_pending"] == 20
コード例 #8
0
    def remove_swan_integration(cls, user: User):
        # Unreserve unused addresses in all wallets
        for wallet_name, wallet in user.wallet_manager.wallets.items():
            SwanService.unreserve_addresses(wallet=wallet)

        # If an autowithdrawal setup is active, remove pending addrs from Swan
        try:
            service_data = SwanService.get_current_user_service_data()
            if service_data.get(cls.SPECTER_WALLET_ALIAS) and service_data.get(
                    cls.SWAN_WALLET_ID):
                # Import here to prevent circular dependency
                from . import client as swan_client

                cls.client().delete_autowithdrawal_addresses(
                    service_data[cls.SWAN_WALLET_ID])
        except Exception as e:
            # Note the exception but proceed with clearing local data
            logger.exception(e)

        # Wipe the on-disk encrypted service data (refresh_token, etc)
        SwanService.set_current_user_service_data({})

        # Remove Swan from User's list of active Services
        user.remove_service(SwanService.id)
コード例 #9
0
def test_reencrypt_user_secret_on_iterations_increase(empty_data_folder):
    """
    Should re-encrypt the user_secret when the User.encryption_iterations is increased
    """
    specter = Specter(data_folder=empty_data_folder)

    password = "******"
    user = User.from_json(
        user_dict={
            "id": "someuser",
            "username": "******",
            "password": hash_password(password),
            "config": {},
            "is_admin": False,
            "services": None,
        },
        specter=specter,
    )

    # Override current default iterations setting
    original_encryption_iterations = user.encryption_iterations
    user.encryption_iterations -= 10000

    # Force generation of a new user_secret
    user.decrypt_user_secret(password)
    assert user.encrypted_user_secret is not None
    assert user.plaintext_user_secret is not None
    assert user.encrypted_user_secret["iterations"] < original_encryption_iterations

    first_encrypted_user_secret = user.encrypted_user_secret
    first_plaintext_user_secret = user.plaintext_user_secret

    # Reset iterations to default
    user.encryption_iterations = original_encryption_iterations

    # On decrypt, should automatically re-encrypt the `user_secret`
    user.decrypt_user_secret(password)
    assert user.encrypted_user_secret["iterations"] == original_encryption_iterations

    # The new encrypted_user_secret will be different...
    assert first_encrypted_user_secret != user.encrypted_user_secret

    # ...but the plaintext_user_secret remains unchanged
    assert first_plaintext_user_secret == user.plaintext_user_secret
コード例 #10
0
def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
    assert bitcoin_regtest.get_rpc().test_connection()
    config = {
        "rpc": {
            "autodetect": False,
            "datadir": "",
            "user": bitcoin_regtest.rpcconn.rpcuser,
            "password": bitcoin_regtest.rpcconn.rpcpassword,
            "port": bitcoin_regtest.rpcconn.rpcport,
            "host": bitcoin_regtest.rpcconn.ipaddress,
            "protocol": "http",
        },
        "auth": {
            "method": "rpcpasswordaspin",
        },
    }
    specter = Specter(data_folder=devices_filled_data_folder, config=config)
    assert specter.chain == "regtest"
    # Create a User
    someuser = specter.user_manager.add_user(
        User.from_json(
            user_dict={
                "id": "someuser",
                "username": "******",
                "password": hash_password("somepassword"),
                "config": {},
                "is_admin": False,
                "services": None,
            },
            specter=specter,
        ))
    specter.user_manager.save()
    specter.check()

    assert not someuser.wallet_manager.working_folder is None

    # Create a Wallet
    wallet_json = '{"label": "a_simple_wallet", "blockheight": 0, "descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/0/*)#xp8lv5nr", "devices": [{"type": "trezor", "label": "trezor"}]} '
    wallet_importer = WalletImporter(wallet_json,
                                     specter,
                                     device_manager=someuser.device_manager)
    wallet_importer.create_nonexisting_signers(
        someuser.device_manager,
        {
            "unknown_cosigner_0_name": "trezor",
            "unknown_cosigner_0_type": "trezor"
        },
    )
    dm: DeviceManager = someuser.device_manager
    wallet = wallet_importer.create_wallet(someuser.wallet_manager)
    try:
        # fund it with some coins
        bitcoin_regtest.testcoin_faucet(address=wallet.getnewaddress())
        # make sure it's confirmed
        bitcoin_regtest.mine()
        # Realize that the wallet has funds:
        wallet.update()
    except SpecterError as se:
        if str(se).startswith("Timeout"):
            pytest.fail(
                "We got a Bitcoin-RPC timeout while setting up the test, minting some coins. Test Error! Check cpu/mem utilastion and btc/elem logs!"
            )
            return
        else:
            raise se

    assert wallet.fullbalance >= 20
    assert not specter.wallet_manager.working_folder is None
    try:
        yield specter
    finally:
        # Deleting all Wallets (this will also purge them on core)
        for user in specter.user_manager.users:
            for wallet in list(user.wallet_manager.wallets.values()):
                user.wallet_manager.delete_wallet(
                    wallet,
                    bitcoin_datadir=bitcoin_regtest.datadir,
                    chain="regtest")