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
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
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
def test_password_hash():
    """
    verify_password should succeed when presented with hash_password and the same
    password that was hashed. It should fail when given a different password.
    """
    password = "******"
    password_hash = hash_password(password)
    assert verify_password(password_hash, password)
    assert not verify_password(password_hash, "wrongpassword")
示例#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"]
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
示例#8
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")