From cbc5d237d138a8a9e25551f5fa8a58d0f1c34bb2 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 1 Jul 2025 22:45:55 -0400 Subject: [PATCH] test: derive keys for vault setup --- src/tests/helpers.py | 32 +++++++++++++++++++ src/tests/test_backup_restore.py | 8 ++--- src/tests/test_concurrency_stress.py | 31 +++++++++++------- src/tests/test_config_manager.py | 27 ++++------------ src/tests/test_entries_empty.py | 7 ++-- src/tests/test_entry_add.py | 7 ++-- .../test_entry_management_checksum_path.py | 13 +++----- src/tests/test_index_import_export.py | 6 ++-- src/tests/test_manager_workflow.py | 7 ++-- src/tests/test_migrations.py | 8 ++--- src/tests/test_nostr_backup.py | 7 ++-- src/tests/test_password_change.py | 6 ++-- src/tests/test_profile_management.py | 7 ++-- src/tests/test_seed_import.py | 5 +-- src/tests/test_settings_menu.py | 6 ++-- 15 files changed, 85 insertions(+), 92 deletions(-) create mode 100644 src/tests/helpers.py diff --git a/src/tests/helpers.py b/src/tests/helpers.py new file mode 100644 index 0000000..22c55cf --- /dev/null +++ b/src/tests/helpers.py @@ -0,0 +1,32 @@ +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).resolve().parents[1])) + +from password_manager.vault import Vault +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" +TEST_PASSWORD = "pw" + + +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) + enc_mgr = EncryptionManager(index_key, dir_path) + vault = Vault(enc_mgr, dir_path) + return vault, enc_mgr diff --git a/src/tests/test_backup_restore.py b/src/tests/test_backup_restore.py index 3abe76a..08e2f3e 100644 --- a/src/tests/test_backup_restore.py +++ b/src/tests/test_backup_restore.py @@ -4,21 +4,17 @@ import time from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD 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 def test_backup_restore_workflow(monkeypatch): with TemporaryDirectory() as tmpdir: fp_dir = Path(tmpdir) - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, fp_dir) - vault = Vault(enc_mgr, fp_dir) + vault, enc_mgr = create_vault(fp_dir, TEST_SEED, TEST_PASSWORD) backup_mgr = BackupManager(fp_dir) index_file = fp_dir / "seedpass_passwords_db.json.enc" diff --git a/src/tests/test_concurrency_stress.py b/src/tests/test_concurrency_stress.py index 5d07874..0e16893 100644 --- a/src/tests/test_concurrency_stress.py +++ b/src/tests/test_concurrency_stress.py @@ -1,19 +1,24 @@ import sys from pathlib import Path from multiprocessing import Process, Queue -from cryptography.fernet import Fernet import pytest +from helpers import TEST_SEED, TEST_PASSWORD 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, +) -def _writer(key: bytes, dir_path: Path, loops: int, out: Queue) -> None: +def _writer(index_key: bytes, dir_path: Path, loops: int, out: Queue) -> None: try: - enc = EncryptionManager(key, dir_path) + enc = EncryptionManager(index_key, dir_path) vault = Vault(enc, dir_path) for _ in range(loops): data = vault.load_index() @@ -23,9 +28,9 @@ def _writer(key: bytes, dir_path: Path, loops: int, out: Queue) -> None: out.put(repr(e)) -def _reader(key: bytes, dir_path: Path, loops: int, out: Queue) -> None: +def _reader(index_key: bytes, dir_path: Path, loops: int, out: Queue) -> None: try: - enc = EncryptionManager(key, dir_path) + enc = EncryptionManager(index_key, dir_path) vault = Vault(enc, dir_path) for _ in range(loops): vault.load_index() @@ -45,16 +50,18 @@ 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, _): - key = Fernet.generate_key() - enc = EncryptionManager(key, tmp_path) + index_key = derive_index_key(TEST_SEED, TEST_PASSWORD, EncryptionMode.SEED_ONLY) + seed_key = derive_key_from_password(TEST_PASSWORD) + EncryptionManager(seed_key, tmp_path).encrypt_parent_seed(TEST_SEED) + enc = EncryptionManager(index_key, tmp_path) Vault(enc, tmp_path).save_index({"counter": 0}) q: Queue = Queue() procs = [ - Process(target=_writer, args=(key, tmp_path, loops, q)), - Process(target=_writer, args=(key, tmp_path, loops, q)), - Process(target=_reader, args=(key, tmp_path, loops, q)), - Process(target=_reader, args=(key, tmp_path, loops, q)), + Process(target=_writer, args=(index_key, tmp_path, loops, q)), + Process(target=_writer, args=(index_key, tmp_path, loops, q)), + Process(target=_reader, args=(index_key, tmp_path, loops, q)), + Process(target=_reader, args=(index_key, tmp_path, loops, q)), Process(target=_backup, args=(tmp_path, loops, q)), ] @@ -69,5 +76,5 @@ def test_concurrency_stress(tmp_path: Path, loops: int, _): assert not errors - vault = Vault(EncryptionManager(key, tmp_path), tmp_path) + vault = Vault(EncryptionManager(index_key, tmp_path), tmp_path) assert isinstance(vault.load_index(), dict) diff --git a/src/tests/test_config_manager.py b/src/tests/test_config_manager.py index c64a7e7..7433dba 100644 --- a/src/tests/test_config_manager.py +++ b/src/tests/test_config_manager.py @@ -1,13 +1,12 @@ import bcrypt from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet import pytest +from helpers import create_vault, TEST_SEED, TEST_PASSWORD import sys sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.config_manager import ConfigManager from password_manager.vault import Vault from nostr.client import DEFAULT_RELAYS @@ -15,9 +14,7 @@ from nostr.client import DEFAULT_RELAYS def test_config_defaults_and_round_trip(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) cfg = cfg_mgr.load_config(require_pin=False) @@ -35,9 +32,7 @@ def test_config_defaults_and_round_trip(): def test_pin_verification_and_change(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) cfg_mgr.set_pin("1234") @@ -52,9 +47,7 @@ import json def test_config_file_encrypted_after_save(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) data = {"relays": ["wss://r"], "pin_hash": ""} @@ -72,9 +65,7 @@ def test_config_file_encrypted_after_save(): def test_set_relays_persists_changes(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) cfg_mgr.set_relays(["wss://custom"], require_pin=False) cfg = cfg_mgr.load_config(require_pin=False) @@ -83,18 +74,14 @@ def test_set_relays_persists_changes(): def test_set_relays_requires_at_least_one(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) with pytest.raises(ValueError): cfg_mgr.set_relays([], require_pin=False) def test_password_hash_migrates_from_file(tmp_path): - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, tmp_path) # save legacy config without password_hash diff --git a/src/tests/test_entries_empty.py b/src/tests/test_entries_empty.py index 0e3328d..4c466b5 100644 --- a/src/tests/test_entries_empty.py +++ b/src/tests/test_entries_empty.py @@ -1,20 +1,17 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.entry_management import EntryManager from password_manager.vault import Vault def test_list_entries_empty(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, Path(tmpdir)) entries = entry_mgr.list_entries() diff --git a/src/tests/test_entry_add.py b/src/tests/test_entry_add.py index 1f9883f..30ebab4 100644 --- a/src/tests/test_entry_add.py +++ b/src/tests/test_entry_add.py @@ -1,20 +1,17 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.entry_management import EntryManager from password_manager.vault import Vault def test_add_and_retrieve_entry(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, Path(tmpdir)) - vault = Vault(enc_mgr, Path(tmpdir)) + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, Path(tmpdir)) index = entry_mgr.add_entry("example.com", 12, "user") diff --git a/src/tests/test_entry_management_checksum_path.py b/src/tests/test_entry_management_checksum_path.py index 0a6b914..b1f6e13 100644 --- a/src/tests/test_entry_management_checksum_path.py +++ b/src/tests/test_entry_management_checksum_path.py @@ -1,21 +1,18 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager -from password_manager.vault import Vault from password_manager.entry_management import EntryManager +from password_manager.vault import Vault def test_update_checksum_writes_to_expected_path(): with TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, tmp_path) # create an empty index file @@ -29,9 +26,7 @@ def test_update_checksum_writes_to_expected_path(): def test_backup_index_file_creates_backup_in_directory(): with TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, tmp_path) vault.save_index({"passwords": {}}) diff --git a/src/tests/test_index_import_export.py b/src/tests/test_index_import_export.py index 46f170d..b9d7fbb 100644 --- a/src/tests/test_index_import_export.py +++ b/src/tests/test_index_import_export.py @@ -3,7 +3,7 @@ from tempfile import TemporaryDirectory import pytest import sys -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) @@ -56,7 +56,5 @@ def test_index_export_import_round_trip(mode): def test_get_encrypted_index_missing_file(tmp_path): - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) assert vault.get_encrypted_index() is None diff --git a/src/tests/test_manager_workflow.py b/src/tests/test_manager_workflow.py index 8694591..d651336 100644 --- a/src/tests/test_manager_workflow.py +++ b/src/tests/test_manager_workflow.py @@ -1,11 +1,10 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.entry_management import EntryManager from password_manager.vault import Vault from password_manager.backup import BackupManager @@ -29,9 +28,7 @@ class FakeNostrClient: def test_manager_workflow(monkeypatch): with TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, tmp_path) backup_mgr = BackupManager(tmp_path) diff --git a/src/tests/test_migrations.py b/src/tests/test_migrations.py index fa14ef2..c212c2a 100644 --- a/src/tests/test_migrations.py +++ b/src/tests/test_migrations.py @@ -1,19 +1,15 @@ import sys from pathlib import Path import pytest -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager -from password_manager.vault import Vault from password_manager.migrations import LATEST_VERSION def setup(tmp_path: Path): - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) return enc_mgr, vault diff --git a/src/tests/test_nostr_backup.py b/src/tests/test_nostr_backup.py index 81a2b9d..a7c966c 100644 --- a/src/tests/test_nostr_backup.py +++ b/src/tests/test_nostr_backup.py @@ -2,11 +2,10 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory from unittest.mock import patch -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.entry_management import EntryManager from password_manager.vault import Vault from nostr.client import NostrClient @@ -15,9 +14,7 @@ from nostr.client import NostrClient def test_backup_and_publish_to_nostr(): with TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, tmp_path) - vault = Vault(enc_mgr, tmp_path) + vault, enc_mgr = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, tmp_path) # create an index by adding an entry diff --git a/src/tests/test_password_change.py b/src/tests/test_password_change.py index d986b37..03d66e5 100644 --- a/src/tests/test_password_change.py +++ b/src/tests/test_password_change.py @@ -4,11 +4,10 @@ from tempfile import TemporaryDirectory from types import SimpleNamespace from unittest.mock import patch -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) -from password_manager.encryption import EncryptionManager from password_manager.entry_management import EntryManager from password_manager.config_manager import ConfigManager from password_manager.vault import Vault @@ -18,8 +17,7 @@ from password_manager.manager import PasswordManager def test_change_password_triggers_nostr_backup(monkeypatch): with TemporaryDirectory() as tmpdir: fp = Path(tmpdir) - enc_mgr = EncryptionManager(Fernet.generate_key(), fp) - vault = Vault(enc_mgr, fp) + vault, enc_mgr = create_vault(fp, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, fp) cfg_mgr = ConfigManager(vault, fp) diff --git a/src/tests/test_profile_management.py b/src/tests/test_profile_management.py index da78fcf..a413c88 100644 --- a/src/tests/test_profile_management.py +++ b/src/tests/test_profile_management.py @@ -4,7 +4,7 @@ from pathlib import Path from tempfile import TemporaryDirectory from types import SimpleNamespace -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) @@ -12,7 +12,6 @@ sys.path.append(str(Path(__file__).resolve().parents[1])) from utils.fingerprint_manager import FingerprintManager import constants import password_manager.manager as manager_module -from password_manager.encryption import EncryptionManager from password_manager.vault import Vault from password_manager.entry_management import EntryManager @@ -49,9 +48,7 @@ def test_add_and_delete_entry(monkeypatch): assert fingerprint_dir.exists() assert pm.fingerprint_manager.current_fingerprint == fingerprint - key = Fernet.generate_key() - enc_mgr = EncryptionManager(key, fingerprint_dir) - vault = Vault(enc_mgr, fingerprint_dir) + vault, enc_mgr = create_vault(fingerprint_dir, TEST_SEED, TEST_PASSWORD) entry_mgr = EntryManager(vault, fingerprint_dir) pm.encryption_manager = enc_mgr diff --git a/src/tests/test_seed_import.py b/src/tests/test_seed_import.py index b88263d..d3b3088 100644 --- a/src/tests/test_seed_import.py +++ b/src/tests/test_seed_import.py @@ -1,7 +1,8 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory -from cryptography.fernet import Fernet +from helpers import TEST_PASSWORD +from utils.key_derivation import derive_key_from_password from mnemonic import Mnemonic sys.path.append(str(Path(__file__).resolve().parents[1])) @@ -12,7 +13,7 @@ from password_manager.manager import PasswordManager def test_seed_encryption_round_trip(): with TemporaryDirectory() as tmpdir: - key = Fernet.generate_key() + key = derive_key_from_password(TEST_PASSWORD) enc_mgr = EncryptionManager(key, Path(tmpdir)) seed = Mnemonic("english").generate(strength=128) diff --git a/src/tests/test_settings_menu.py b/src/tests/test_settings_menu.py index c02f22b..7a0c0ee 100644 --- a/src/tests/test_settings_menu.py +++ b/src/tests/test_settings_menu.py @@ -5,13 +5,12 @@ from tempfile import TemporaryDirectory from types import SimpleNamespace from unittest.mock import patch -from cryptography.fernet import Fernet +from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) import main from nostr.client import DEFAULT_RELAYS -from password_manager.encryption import EncryptionManager from password_manager.config_manager import ConfigManager from password_manager.vault import Vault from utils.fingerprint_manager import FingerprintManager @@ -26,8 +25,7 @@ def setup_pm(tmp_path, monkeypatch): fp_dir = constants.APP_DIR / "fp" fp_dir.mkdir(parents=True) - enc_mgr = EncryptionManager(Fernet.generate_key(), fp_dir) - vault = Vault(enc_mgr, fp_dir) + vault, enc_mgr = create_vault(fp_dir, TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, fp_dir) fp_mgr = FingerprintManager(constants.APP_DIR)