mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-10 00:09:04 +00:00
Remove password-based encryption modes
This commit is contained in:
@@ -8,7 +8,6 @@ from password_manager.encryption import EncryptionManager
|
||||
from utils.key_derivation import (
|
||||
derive_index_key,
|
||||
derive_key_from_password,
|
||||
EncryptionMode,
|
||||
)
|
||||
|
||||
TEST_SEED = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
@@ -19,14 +18,13 @@ def create_vault(
|
||||
dir_path: Path,
|
||||
seed: str = TEST_SEED,
|
||||
password: str = TEST_PASSWORD,
|
||||
mode: EncryptionMode = EncryptionMode.SEED_ONLY,
|
||||
) -> tuple[Vault, EncryptionManager]:
|
||||
"""Create a Vault initialized for tests."""
|
||||
seed_key = derive_key_from_password(password)
|
||||
seed_mgr = EncryptionManager(seed_key, dir_path)
|
||||
seed_mgr.encrypt_parent_seed(seed)
|
||||
|
||||
index_key = derive_index_key(seed, password, mode)
|
||||
index_key = derive_index_key(seed)
|
||||
enc_mgr = EncryptionManager(index_key, dir_path)
|
||||
vault = Vault(enc_mgr, dir_path)
|
||||
return vault, enc_mgr
|
||||
|
@@ -33,23 +33,3 @@ def _get_mode(monkeypatch, args=None, cfg=None):
|
||||
def test_default_mode_is_seed_only(monkeypatch):
|
||||
mode = _get_mode(monkeypatch)
|
||||
assert mode is EncryptionMode.SEED_ONLY
|
||||
|
||||
|
||||
def test_cli_flag_overrides_config(monkeypatch):
|
||||
cfg = {"encryption_mode": EncryptionMode.PW_ONLY.value}
|
||||
mode = _get_mode(monkeypatch, ["--encryption-mode", "seed+pw"], cfg)
|
||||
assert mode is EncryptionMode.SEED_PLUS_PW
|
||||
|
||||
|
||||
def test_pw_only_emits_warning(monkeypatch, capsys):
|
||||
pm = PasswordManager.__new__(PasswordManager)
|
||||
pm.encryption_mode = EncryptionMode.SEED_ONLY
|
||||
pm.fingerprint_manager = object()
|
||||
pm.setup_existing_seed = lambda: None
|
||||
pm.generate_new_seed = lambda: None
|
||||
inputs = iter(["3", "1"])
|
||||
monkeypatch.setattr("builtins.input", lambda *_: next(inputs))
|
||||
pm.handle_new_seed_setup()
|
||||
out = capsys.readouterr().out
|
||||
assert "Password-only encryption is less secure" in out
|
||||
assert pm.encryption_mode is EncryptionMode.PW_ONLY
|
||||
|
@@ -7,7 +7,6 @@ import pytest
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import main
|
||||
from password_manager.portable_backup import PortableMode
|
||||
from password_manager.manager import PasswordManager
|
||||
|
||||
|
||||
@@ -19,8 +18,8 @@ def _run(argv, monkeypatch):
|
||||
def fake_init(self, encryption_mode):
|
||||
called["init"] = True
|
||||
|
||||
def fake_export(self, mode, dest):
|
||||
called["export"] = (mode, Path(dest))
|
||||
def fake_export(self, dest):
|
||||
called["export"] = Path(dest)
|
||||
|
||||
def fake_import(self, src):
|
||||
called["import"] = Path(src)
|
||||
@@ -36,8 +35,8 @@ def _run(argv, monkeypatch):
|
||||
|
||||
|
||||
def test_export_command_invokes_handler(monkeypatch):
|
||||
called = _run(["export", "--mode", "pw-only", "--file", "out.json"], monkeypatch)
|
||||
assert called["export"] == (PortableMode.PW_ONLY, Path("out.json"))
|
||||
called = _run(["export", "--file", "out.json"], monkeypatch)
|
||||
assert called["export"] == Path("out.json")
|
||||
assert "import" not in called
|
||||
|
||||
|
||||
|
@@ -9,11 +9,7 @@ sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
from password_manager.encryption import EncryptionManager
|
||||
from password_manager.vault import Vault
|
||||
from password_manager.backup import BackupManager
|
||||
from utils.key_derivation import (
|
||||
derive_index_key,
|
||||
derive_key_from_password,
|
||||
EncryptionMode,
|
||||
)
|
||||
from utils.key_derivation import derive_index_key, derive_key_from_password
|
||||
|
||||
|
||||
def _writer(index_key: bytes, dir_path: Path, loops: int, out: Queue) -> None:
|
||||
@@ -50,7 +46,7 @@ def _backup(dir_path: Path, loops: int, out: Queue) -> None:
|
||||
@pytest.mark.parametrize("loops", [5, pytest.param(20, marks=pytest.mark.stress)])
|
||||
@pytest.mark.parametrize("_", range(3))
|
||||
def test_concurrency_stress(tmp_path: Path, loops: int, _):
|
||||
index_key = derive_index_key(TEST_SEED, TEST_PASSWORD, EncryptionMode.SEED_ONLY)
|
||||
index_key = derive_index_key(TEST_SEED)
|
||||
seed_key = derive_key_from_password(TEST_PASSWORD)
|
||||
EncryptionManager(seed_key, tmp_path).encrypt_parent_seed(TEST_SEED)
|
||||
enc = EncryptionManager(index_key, tmp_path)
|
||||
|
@@ -1,57 +0,0 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch, AsyncMock
|
||||
|
||||
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from password_manager.entry_management import EntryManager
|
||||
from password_manager.config_manager import ConfigManager
|
||||
from password_manager.vault import Vault
|
||||
from password_manager.manager import PasswordManager
|
||||
from utils.key_derivation import EncryptionMode
|
||||
|
||||
|
||||
def test_change_encryption_mode(monkeypatch):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
fp = Path(tmpdir)
|
||||
vault, enc_mgr = create_vault(
|
||||
fp, TEST_SEED, TEST_PASSWORD, EncryptionMode.SEED_ONLY
|
||||
)
|
||||
entry_mgr = EntryManager(vault, fp)
|
||||
cfg_mgr = ConfigManager(vault, fp)
|
||||
vault.save_index({"passwords": {}})
|
||||
|
||||
pm = PasswordManager.__new__(PasswordManager)
|
||||
pm.encryption_manager = enc_mgr
|
||||
pm.entry_manager = entry_mgr
|
||||
pm.config_manager = cfg_mgr
|
||||
pm.vault = vault
|
||||
pm.password_generator = SimpleNamespace(encryption_manager=enc_mgr)
|
||||
pm.fingerprint_dir = fp
|
||||
pm.current_fingerprint = "fp"
|
||||
pm.parent_seed = TEST_SEED
|
||||
pm.encryption_mode = EncryptionMode.SEED_ONLY
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.manager.prompt_existing_password",
|
||||
lambda *_: TEST_PASSWORD,
|
||||
)
|
||||
pm.verify_password = lambda pw: True
|
||||
|
||||
with patch("password_manager.manager.NostrClient") as MockClient:
|
||||
mock = MockClient.return_value
|
||||
mock.publish_snapshot = AsyncMock(return_value=None)
|
||||
pm.nostr_client = mock
|
||||
pm.change_encryption_mode(EncryptionMode.SEED_PLUS_PW)
|
||||
mock.publish_snapshot.assert_called_once()
|
||||
|
||||
assert pm.encryption_mode is EncryptionMode.SEED_PLUS_PW
|
||||
assert pm.password_generator.encryption_manager is pm.encryption_manager
|
||||
loaded = vault.load_index()
|
||||
assert loaded["passwords"] == {}
|
||||
cfg = cfg_mgr.load_config(require_pin=False)
|
||||
assert cfg["encryption_mode"] == EncryptionMode.SEED_PLUS_PW.value
|
@@ -1,92 +0,0 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from types import SimpleNamespace
|
||||
|
||||
import bcrypt
|
||||
import pytest
|
||||
|
||||
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from password_manager.entry_management import EntryManager
|
||||
from password_manager.config_manager import ConfigManager
|
||||
from password_manager.vault import Vault
|
||||
from password_manager.manager import PasswordManager
|
||||
from utils.key_derivation import EncryptionMode
|
||||
|
||||
|
||||
TRANSITIONS = [
|
||||
(EncryptionMode.SEED_ONLY, EncryptionMode.SEED_PLUS_PW),
|
||||
(EncryptionMode.SEED_ONLY, EncryptionMode.PW_ONLY),
|
||||
(EncryptionMode.SEED_PLUS_PW, EncryptionMode.SEED_ONLY),
|
||||
(EncryptionMode.SEED_PLUS_PW, EncryptionMode.PW_ONLY),
|
||||
(EncryptionMode.PW_ONLY, EncryptionMode.SEED_ONLY),
|
||||
(EncryptionMode.PW_ONLY, EncryptionMode.SEED_PLUS_PW),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("start_mode,new_mode", TRANSITIONS)
|
||||
def test_encryption_mode_migration(monkeypatch, start_mode, new_mode):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
fp = Path(tmpdir)
|
||||
vault, enc_mgr = create_vault(fp, TEST_SEED, TEST_PASSWORD, start_mode)
|
||||
entry_mgr = EntryManager(vault, fp)
|
||||
cfg_mgr = ConfigManager(vault, fp)
|
||||
|
||||
vault.save_index({"passwords": {}})
|
||||
cfg_mgr.save_config(
|
||||
{
|
||||
"relays": [],
|
||||
"pin_hash": "",
|
||||
"password_hash": bcrypt.hashpw(
|
||||
TEST_PASSWORD.encode(), bcrypt.gensalt()
|
||||
).decode(),
|
||||
"encryption_mode": start_mode.value,
|
||||
}
|
||||
)
|
||||
|
||||
pm = PasswordManager.__new__(PasswordManager)
|
||||
pm.encryption_manager = enc_mgr
|
||||
pm.entry_manager = entry_mgr
|
||||
pm.config_manager = cfg_mgr
|
||||
pm.vault = vault
|
||||
pm.password_generator = SimpleNamespace(encryption_manager=enc_mgr)
|
||||
pm.fingerprint_dir = fp
|
||||
pm.current_fingerprint = "fp"
|
||||
pm.parent_seed = TEST_SEED
|
||||
pm.encryption_mode = start_mode
|
||||
pm.nostr_client = SimpleNamespace(publish_snapshot=lambda *a, **k: None)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.manager.prompt_existing_password",
|
||||
lambda *_: TEST_PASSWORD,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"password_manager.manager.NostrClient",
|
||||
lambda *a, **kw: SimpleNamespace(publish_snapshot=lambda *a, **k: None),
|
||||
)
|
||||
|
||||
pm.change_encryption_mode(new_mode)
|
||||
|
||||
assert pm.encryption_mode is new_mode
|
||||
cfg = cfg_mgr.load_config(require_pin=False)
|
||||
assert cfg["encryption_mode"] == new_mode.value
|
||||
|
||||
pm.lock_vault()
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.manager.prompt_existing_password",
|
||||
lambda *_: TEST_PASSWORD,
|
||||
)
|
||||
monkeypatch.setattr(PasswordManager, "initialize_bip85", lambda self: None)
|
||||
monkeypatch.setattr(PasswordManager, "initialize_managers", lambda self: None)
|
||||
|
||||
pm.unlock_vault()
|
||||
|
||||
assert pm.parent_seed == TEST_SEED
|
||||
assert not pm.locked
|
||||
assert pm.encryption_mode is new_mode
|
||||
assert pm.vault.load_index()["passwords"] == {}
|
||||
assert pm.verify_password(TEST_PASSWORD)
|
@@ -9,38 +9,26 @@ sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from password_manager.encryption import EncryptionManager
|
||||
from password_manager.vault import Vault
|
||||
from utils.key_derivation import (
|
||||
derive_index_key,
|
||||
derive_key_from_password,
|
||||
EncryptionMode,
|
||||
)
|
||||
from utils.key_derivation import derive_index_key, derive_key_from_password
|
||||
|
||||
SEED = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
PASSWORD = "passw0rd"
|
||||
|
||||
|
||||
def setup_vault(tmp: Path, mode: EncryptionMode) -> Vault:
|
||||
def setup_vault(tmp: Path) -> Vault:
|
||||
seed_key = derive_key_from_password(PASSWORD)
|
||||
seed_mgr = EncryptionManager(seed_key, tmp)
|
||||
seed_mgr.encrypt_parent_seed(SEED)
|
||||
|
||||
key = derive_index_key(SEED, PASSWORD, mode)
|
||||
key = derive_index_key(SEED)
|
||||
enc_mgr = EncryptionManager(key, tmp)
|
||||
return Vault(enc_mgr, tmp)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mode",
|
||||
[
|
||||
EncryptionMode.SEED_ONLY,
|
||||
EncryptionMode.SEED_PLUS_PW,
|
||||
EncryptionMode.PW_ONLY,
|
||||
],
|
||||
)
|
||||
def test_index_export_import_round_trip(mode):
|
||||
def test_index_export_import_round_trip():
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault = setup_vault(tmp, mode)
|
||||
vault = setup_vault(tmp)
|
||||
|
||||
original = {"passwords": {"0": {"website": "example"}}}
|
||||
vault.save_index(original)
|
||||
|
@@ -3,9 +3,7 @@ import pytest
|
||||
from utils.key_derivation import (
|
||||
derive_key_from_password,
|
||||
derive_index_key_seed_only,
|
||||
derive_index_key_seed_plus_pw,
|
||||
derive_index_key,
|
||||
EncryptionMode,
|
||||
)
|
||||
|
||||
|
||||
@@ -32,23 +30,6 @@ def test_seed_only_key_deterministic():
|
||||
assert len(k1) == 44
|
||||
|
||||
|
||||
def test_seed_plus_pw_differs_from_seed_only():
|
||||
def test_derive_index_key_seed_only():
|
||||
seed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
pw = "hunter2"
|
||||
k1 = derive_index_key_seed_only(seed)
|
||||
k2 = derive_index_key_seed_plus_pw(seed, pw)
|
||||
assert k1 != k2
|
||||
|
||||
|
||||
def test_derive_index_key_modes():
|
||||
seed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
pw = "hunter2"
|
||||
assert derive_index_key(
|
||||
seed, pw, EncryptionMode.SEED_ONLY
|
||||
) == derive_index_key_seed_only(seed)
|
||||
assert derive_index_key(
|
||||
seed, pw, EncryptionMode.SEED_PLUS_PW
|
||||
) == derive_index_key_seed_plus_pw(seed, pw)
|
||||
assert derive_index_key(
|
||||
seed, pw, EncryptionMode.PW_ONLY
|
||||
) == derive_key_from_password(pw)
|
||||
assert derive_index_key(seed) == derive_index_key_seed_only(seed)
|
||||
|
@@ -29,7 +29,7 @@ def test_change_password_triggers_nostr_backup(monkeypatch):
|
||||
pm.password_generator = SimpleNamespace(encryption_manager=enc_mgr)
|
||||
pm.fingerprint_dir = fp
|
||||
pm.current_fingerprint = "fp"
|
||||
pm.parent_seed = "seed"
|
||||
pm.parent_seed = TEST_SEED
|
||||
pm.store_hashed_password = lambda pw: None
|
||||
pm.verify_password = lambda pw: True
|
||||
|
||||
|
@@ -24,7 +24,7 @@ def test_password_change_and_unlock(monkeypatch):
|
||||
new_pw = "newpw"
|
||||
|
||||
# initial encryption setup
|
||||
index_key = derive_index_key(SEED, old_pw, EncryptionMode.SEED_PLUS_PW)
|
||||
index_key = derive_index_key(SEED)
|
||||
seed_key = derive_key_from_password(old_pw)
|
||||
enc_mgr = EncryptionManager(index_key, fp)
|
||||
seed_mgr = EncryptionManager(seed_key, fp)
|
||||
@@ -45,7 +45,7 @@ def test_password_change_and_unlock(monkeypatch):
|
||||
seed_mgr.encrypt_parent_seed(SEED)
|
||||
|
||||
pm = PasswordManager.__new__(PasswordManager)
|
||||
pm.encryption_mode = EncryptionMode.SEED_PLUS_PW
|
||||
pm.encryption_mode = EncryptionMode.SEED_ONLY
|
||||
pm.encryption_manager = enc_mgr
|
||||
pm.entry_manager = entry_mgr
|
||||
pm.config_manager = cfg_mgr
|
||||
|
@@ -11,57 +11,39 @@ sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
from password_manager.encryption import EncryptionManager
|
||||
from password_manager.vault import Vault
|
||||
from password_manager.backup import BackupManager
|
||||
from password_manager.portable_backup import (
|
||||
PortableMode,
|
||||
export_backup,
|
||||
import_backup,
|
||||
)
|
||||
from utils.key_derivation import (
|
||||
derive_index_key,
|
||||
derive_key_from_password,
|
||||
EncryptionMode,
|
||||
)
|
||||
from password_manager.portable_backup import export_backup, import_backup
|
||||
from utils.key_derivation import derive_index_key, derive_key_from_password
|
||||
|
||||
|
||||
SEED = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
PASSWORD = "passw0rd"
|
||||
|
||||
|
||||
def setup_vault(tmp: Path, mode: EncryptionMode = EncryptionMode.SEED_ONLY):
|
||||
def setup_vault(tmp: Path):
|
||||
seed_key = derive_key_from_password(PASSWORD)
|
||||
seed_mgr = EncryptionManager(seed_key, tmp)
|
||||
seed_mgr.encrypt_parent_seed(SEED)
|
||||
|
||||
index_key = derive_index_key(SEED, PASSWORD, mode)
|
||||
index_key = derive_index_key(SEED)
|
||||
enc_mgr = EncryptionManager(index_key, tmp)
|
||||
vault = Vault(enc_mgr, tmp)
|
||||
backup = BackupManager(tmp)
|
||||
return vault, backup
|
||||
|
||||
|
||||
def test_round_trip_across_modes(monkeypatch):
|
||||
for pmode in [
|
||||
PortableMode.SEED_ONLY,
|
||||
PortableMode.SEED_PLUS_PW,
|
||||
PortableMode.PW_ONLY,
|
||||
]:
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault, backup = setup_vault(tmp)
|
||||
data = {"pw": 1}
|
||||
vault.save_index(data)
|
||||
def test_round_trip(monkeypatch):
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault, backup = setup_vault(tmp)
|
||||
data = {"pw": 1}
|
||||
vault.save_index(data)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
path = export_backup(vault, backup, parent_seed=SEED)
|
||||
assert path.exists()
|
||||
|
||||
path = export_backup(vault, backup, pmode, parent_seed=SEED)
|
||||
assert path.exists()
|
||||
|
||||
vault.save_index({"pw": 0})
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
assert vault.load_index()["pw"] == data["pw"]
|
||||
vault.save_index({"pw": 0})
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
assert vault.load_index()["pw"] == data["pw"]
|
||||
|
||||
|
||||
from cryptography.fernet import InvalidToken
|
||||
@@ -73,11 +55,7 @@ def test_corruption_detection(monkeypatch):
|
||||
vault, backup = setup_vault(tmp)
|
||||
vault.save_index({"a": 1})
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
path = export_backup(vault, backup, PortableMode.SEED_ONLY, parent_seed=SEED)
|
||||
path = export_backup(vault, backup, parent_seed=SEED)
|
||||
|
||||
content = json.loads(path.read_text())
|
||||
payload = base64.b64decode(content["payload"])
|
||||
@@ -89,42 +67,13 @@ def test_corruption_detection(monkeypatch):
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
|
||||
|
||||
def test_incorrect_credentials(monkeypatch):
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault, backup = setup_vault(tmp)
|
||||
vault.save_index({"a": 2})
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
path = export_backup(
|
||||
vault,
|
||||
backup,
|
||||
PortableMode.SEED_PLUS_PW,
|
||||
parent_seed=SEED,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: "wrong",
|
||||
)
|
||||
with pytest.raises(Exception):
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
|
||||
|
||||
def test_import_over_existing(monkeypatch):
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault, backup = setup_vault(tmp)
|
||||
vault.save_index({"v": 1})
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
path = export_backup(vault, backup, PortableMode.SEED_ONLY, parent_seed=SEED)
|
||||
path = export_backup(vault, backup, parent_seed=SEED)
|
||||
|
||||
vault.save_index({"v": 2})
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
@@ -138,21 +87,11 @@ def test_checksum_mismatch_detection(monkeypatch):
|
||||
vault, backup = setup_vault(tmp)
|
||||
vault.save_index({"a": 1})
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
|
||||
path = export_backup(
|
||||
vault,
|
||||
backup,
|
||||
PortableMode.SEED_ONLY,
|
||||
parent_seed=SEED,
|
||||
)
|
||||
path = export_backup(vault, backup, parent_seed=SEED)
|
||||
|
||||
wrapper = json.loads(path.read_text())
|
||||
payload = base64.b64decode(wrapper["payload"])
|
||||
key = derive_index_key(SEED, PASSWORD, EncryptionMode.SEED_ONLY)
|
||||
key = derive_index_key(SEED)
|
||||
enc_mgr = EncryptionManager(key, tmp)
|
||||
data = json.loads(enc_mgr.decrypt_data(payload).decode())
|
||||
data["a"] = 2
|
||||
@@ -165,23 +104,14 @@ def test_checksum_mismatch_detection(monkeypatch):
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pmode",
|
||||
[PortableMode.SEED_ONLY, PortableMode.SEED_PLUS_PW],
|
||||
)
|
||||
def test_export_import_seed_encrypted_with_different_key(monkeypatch, pmode):
|
||||
def test_export_import_seed_encrypted_with_different_key(monkeypatch):
|
||||
"""Ensure backup round trip works when seed is encrypted with another key."""
|
||||
with TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
vault, backup = setup_vault(tmp)
|
||||
vault.save_index({"v": 123})
|
||||
|
||||
monkeypatch.setattr(
|
||||
"password_manager.portable_backup.prompt_existing_password",
|
||||
lambda *_a, **_k: PASSWORD,
|
||||
)
|
||||
|
||||
path = export_backup(vault, backup, pmode, parent_seed=SEED)
|
||||
path = export_backup(vault, backup, parent_seed=SEED)
|
||||
vault.save_index({"v": 0})
|
||||
import_backup(vault, backup, path, parent_seed=SEED)
|
||||
assert vault.load_index()["v"] == 123
|
||||
|
Reference in New Issue
Block a user