mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-10-27 16:02:05 +00:00
Introduced a random salt to key derivation for parent seed encryption to prevent rainbow table attacks. The salt is now stored in the `parent_seed.enc` file header (V3 format). * Modified `src/utils/key_derivation.py` to accept optional `salt` argument. * Modified `src/seedpass/core/encryption.py` to read/write salt in `parent_seed.enc` using V3 format (V3:salt:nonce:ciphertext). * Modified `src/seedpass/core/manager.py` to generate salt for new seeds and read salt for existing ones. * Added auto-upgrade logic for legacy (no salt) seeds upon access. * Updated tests to verify salt usage and backward compatibility. Co-authored-by: PR0M3TH3AN <[email protected]>
78 lines
2.8 KiB
Python
78 lines
2.8 KiB
Python
import bcrypt
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
from types import SimpleNamespace
|
|
|
|
from utils.key_derivation import (
|
|
derive_key_from_password,
|
|
derive_key_from_password_argon2,
|
|
derive_index_key,
|
|
)
|
|
from seedpass.core.encryption import EncryptionManager
|
|
from seedpass.core.vault import Vault
|
|
from seedpass.core.config_manager import ConfigManager
|
|
from seedpass.core.manager import PasswordManager, EncryptionMode
|
|
|
|
TEST_SEED = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
TEST_PASSWORD = "pw"
|
|
|
|
|
|
def _setup_profile(tmp: Path, mode: str):
|
|
argon_kwargs = dict(time_cost=1, memory_cost=8, parallelism=1)
|
|
if mode == "argon2":
|
|
seed_key = derive_key_from_password_argon2(TEST_PASSWORD, **argon_kwargs)
|
|
else:
|
|
seed_key = derive_key_from_password(TEST_PASSWORD, iterations=1)
|
|
EncryptionManager(seed_key, tmp).encrypt_parent_seed(TEST_SEED)
|
|
|
|
index_key = derive_index_key(TEST_SEED)
|
|
enc_mgr = EncryptionManager(index_key, tmp)
|
|
vault = Vault(enc_mgr, tmp)
|
|
cfg_mgr = ConfigManager(vault, tmp)
|
|
cfg = cfg_mgr.load_config(require_pin=False)
|
|
cfg["password_hash"] = bcrypt.hashpw(
|
|
TEST_PASSWORD.encode(), bcrypt.gensalt()
|
|
).decode()
|
|
cfg["kdf_mode"] = mode
|
|
cfg["kdf_iterations"] = 1
|
|
cfg_mgr.save_config(cfg)
|
|
return cfg_mgr
|
|
|
|
|
|
def _make_pm(tmp: Path, cfg: ConfigManager):
|
|
pm = PasswordManager.__new__(PasswordManager)
|
|
pm.encryption_mode = EncryptionMode.SEED_ONLY
|
|
pm.config_manager = cfg
|
|
pm.fingerprint_dir = tmp
|
|
pm.current_fingerprint = "fp"
|
|
pm.verify_password = lambda pw: True
|
|
return pm
|
|
|
|
|
|
def test_setup_encryption_manager_kdf_modes(monkeypatch):
|
|
with TemporaryDirectory() as td:
|
|
tmp = Path(td)
|
|
argon_kwargs = dict(time_cost=1, memory_cost=8, parallelism=1)
|
|
for mode in ("pbkdf2", "argon2"):
|
|
path = tmp / mode
|
|
path.mkdir()
|
|
cfg = _setup_profile(path, mode)
|
|
pm = _make_pm(path, cfg)
|
|
monkeypatch.setattr(
|
|
"seedpass.core.manager.prompt_existing_password",
|
|
lambda *_: TEST_PASSWORD,
|
|
)
|
|
if mode == "argon2":
|
|
monkeypatch.setattr(
|
|
"seedpass.core.manager.derive_key_from_password_argon2",
|
|
lambda pw, salt=None: derive_key_from_password_argon2(
|
|
pw, salt=salt if salt is not None else b"\x00" * 16, **argon_kwargs
|
|
),
|
|
)
|
|
monkeypatch.setattr(PasswordManager, "initialize_bip85", lambda self: None)
|
|
monkeypatch.setattr(
|
|
PasswordManager, "initialize_managers", lambda self: None
|
|
)
|
|
assert pm.setup_encryption_manager(path, exit_on_fail=False)
|
|
assert pm.parent_seed == TEST_SEED
|