mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 15:28:44 +00:00
Merge pull request #472 from PR0M3TH3AN/codex/add-kdf_iterations-setting-to-configmanager
Support configurable PBKDF2 iterations
This commit is contained in:
@@ -44,6 +44,7 @@ class ConfigManager:
|
|||||||
"pin_hash": "",
|
"pin_hash": "",
|
||||||
"password_hash": "",
|
"password_hash": "",
|
||||||
"inactivity_timeout": INACTIVITY_TIMEOUT,
|
"inactivity_timeout": INACTIVITY_TIMEOUT,
|
||||||
|
"kdf_iterations": 100_000,
|
||||||
"additional_backup_path": "",
|
"additional_backup_path": "",
|
||||||
"secret_mode_enabled": False,
|
"secret_mode_enabled": False,
|
||||||
"clipboard_clear_delay": 45,
|
"clipboard_clear_delay": 45,
|
||||||
@@ -57,6 +58,7 @@ class ConfigManager:
|
|||||||
data.setdefault("pin_hash", "")
|
data.setdefault("pin_hash", "")
|
||||||
data.setdefault("password_hash", "")
|
data.setdefault("password_hash", "")
|
||||||
data.setdefault("inactivity_timeout", INACTIVITY_TIMEOUT)
|
data.setdefault("inactivity_timeout", INACTIVITY_TIMEOUT)
|
||||||
|
data.setdefault("kdf_iterations", 100_000)
|
||||||
data.setdefault("additional_backup_path", "")
|
data.setdefault("additional_backup_path", "")
|
||||||
data.setdefault("secret_mode_enabled", False)
|
data.setdefault("secret_mode_enabled", False)
|
||||||
data.setdefault("clipboard_clear_delay", 45)
|
data.setdefault("clipboard_clear_delay", 45)
|
||||||
@@ -137,6 +139,19 @@ class ConfigManager:
|
|||||||
config = self.load_config(require_pin=False)
|
config = self.load_config(require_pin=False)
|
||||||
return float(config.get("inactivity_timeout", INACTIVITY_TIMEOUT))
|
return float(config.get("inactivity_timeout", INACTIVITY_TIMEOUT))
|
||||||
|
|
||||||
|
def set_kdf_iterations(self, iterations: int) -> None:
|
||||||
|
"""Persist the PBKDF2 iteration count in the config."""
|
||||||
|
if iterations <= 0:
|
||||||
|
raise ValueError("Iterations must be positive")
|
||||||
|
config = self.load_config(require_pin=False)
|
||||||
|
config["kdf_iterations"] = int(iterations)
|
||||||
|
self.save_config(config)
|
||||||
|
|
||||||
|
def get_kdf_iterations(self) -> int:
|
||||||
|
"""Retrieve the PBKDF2 iteration count."""
|
||||||
|
config = self.load_config(require_pin=False)
|
||||||
|
return int(config.get("kdf_iterations", 100_000))
|
||||||
|
|
||||||
def set_additional_backup_path(self, path: Optional[str]) -> None:
|
def set_additional_backup_path(self, path: Optional[str]) -> None:
|
||||||
"""Persist an optional additional backup path in the config."""
|
"""Persist an optional additional backup path in the config."""
|
||||||
config = self.load_config(require_pin=False)
|
config = self.load_config(require_pin=False)
|
||||||
|
@@ -381,7 +381,12 @@ class PasswordManager:
|
|||||||
if password is None:
|
if password is None:
|
||||||
password = prompt_existing_password("Enter your master password: ")
|
password = prompt_existing_password("Enter your master password: ")
|
||||||
|
|
||||||
seed_key = derive_key_from_password(password)
|
iterations = (
|
||||||
|
self.config_manager.get_kdf_iterations()
|
||||||
|
if getattr(self, "config_manager", None)
|
||||||
|
else 100_000
|
||||||
|
)
|
||||||
|
seed_key = derive_key_from_password(password, iterations=iterations)
|
||||||
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
||||||
try:
|
try:
|
||||||
self.parent_seed = seed_mgr.decrypt_parent_seed()
|
self.parent_seed = seed_mgr.decrypt_parent_seed()
|
||||||
@@ -428,7 +433,12 @@ class PasswordManager:
|
|||||||
password = prompt_existing_password("Enter your master password: ")
|
password = prompt_existing_password("Enter your master password: ")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
seed_key = derive_key_from_password(password)
|
iterations = (
|
||||||
|
self.config_manager.get_kdf_iterations()
|
||||||
|
if getattr(self, "config_manager", None)
|
||||||
|
else 100_000
|
||||||
|
)
|
||||||
|
seed_key = derive_key_from_password(password, iterations=iterations)
|
||||||
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
||||||
self.parent_seed = seed_mgr.decrypt_parent_seed()
|
self.parent_seed = seed_mgr.decrypt_parent_seed()
|
||||||
seed_bytes = Bip39SeedGenerator(self.parent_seed).Generate()
|
seed_bytes = Bip39SeedGenerator(self.parent_seed).Generate()
|
||||||
@@ -572,7 +582,12 @@ class PasswordManager:
|
|||||||
password = getpass.getpass(prompt="Enter your login password: ").strip()
|
password = getpass.getpass(prompt="Enter your login password: ").strip()
|
||||||
|
|
||||||
# Derive encryption key from password
|
# Derive encryption key from password
|
||||||
key = derive_key_from_password(password)
|
iterations = (
|
||||||
|
self.config_manager.get_kdf_iterations()
|
||||||
|
if getattr(self, "config_manager", None)
|
||||||
|
else 100_000
|
||||||
|
)
|
||||||
|
key = derive_key_from_password(password, iterations=iterations)
|
||||||
|
|
||||||
# Initialize FingerprintManager if not already initialized
|
# Initialize FingerprintManager if not already initialized
|
||||||
if not self.fingerprint_manager:
|
if not self.fingerprint_manager:
|
||||||
@@ -692,7 +707,8 @@ class PasswordManager:
|
|||||||
# Initialize EncryptionManager with key and fingerprint_dir
|
# Initialize EncryptionManager with key and fingerprint_dir
|
||||||
password = prompt_for_password()
|
password = prompt_for_password()
|
||||||
index_key = derive_index_key(parent_seed)
|
index_key = derive_index_key(parent_seed)
|
||||||
seed_key = derive_key_from_password(password)
|
iterations = self.config_manager.get_kdf_iterations()
|
||||||
|
seed_key = derive_key_from_password(password, iterations=iterations)
|
||||||
|
|
||||||
self.encryption_manager = EncryptionManager(index_key, fingerprint_dir)
|
self.encryption_manager = EncryptionManager(index_key, fingerprint_dir)
|
||||||
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
||||||
@@ -833,7 +849,12 @@ class PasswordManager:
|
|||||||
password = prompt_for_password()
|
password = prompt_for_password()
|
||||||
|
|
||||||
index_key = derive_index_key(seed)
|
index_key = derive_index_key(seed)
|
||||||
seed_key = derive_key_from_password(password)
|
iterations = (
|
||||||
|
self.config_manager.get_kdf_iterations()
|
||||||
|
if getattr(self, "config_manager", None)
|
||||||
|
else 100_000
|
||||||
|
)
|
||||||
|
seed_key = derive_key_from_password(password, iterations=iterations)
|
||||||
|
|
||||||
self.encryption_manager = EncryptionManager(index_key, fingerprint_dir)
|
self.encryption_manager = EncryptionManager(index_key, fingerprint_dir)
|
||||||
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
seed_mgr = EncryptionManager(seed_key, fingerprint_dir)
|
||||||
@@ -3357,7 +3378,8 @@ class PasswordManager:
|
|||||||
|
|
||||||
if confirm_action("Encrypt export with a password? (Y/N): "):
|
if confirm_action("Encrypt export with a password? (Y/N): "):
|
||||||
password = prompt_new_password()
|
password = prompt_new_password()
|
||||||
key = derive_key_from_password(password)
|
iterations = self.config_manager.get_kdf_iterations()
|
||||||
|
key = derive_key_from_password(password, iterations=iterations)
|
||||||
enc_mgr = EncryptionManager(key, dest.parent)
|
enc_mgr = EncryptionManager(key, dest.parent)
|
||||||
data_bytes = enc_mgr.encrypt_data(json_data.encode("utf-8"))
|
data_bytes = enc_mgr.encrypt_data(json_data.encode("utf-8"))
|
||||||
dest = dest.with_suffix(dest.suffix + ".enc")
|
dest = dest.with_suffix(dest.suffix + ".enc")
|
||||||
@@ -3569,7 +3591,8 @@ class PasswordManager:
|
|||||||
# Create a new encryption manager with the new password
|
# Create a new encryption manager with the new password
|
||||||
new_key = derive_index_key(self.parent_seed)
|
new_key = derive_index_key(self.parent_seed)
|
||||||
|
|
||||||
seed_key = derive_key_from_password(new_password)
|
iterations = self.config_manager.get_kdf_iterations()
|
||||||
|
seed_key = derive_key_from_password(new_password, iterations=iterations)
|
||||||
seed_mgr = EncryptionManager(seed_key, self.fingerprint_dir)
|
seed_mgr = EncryptionManager(seed_key, self.fingerprint_dir)
|
||||||
|
|
||||||
new_enc_mgr = EncryptionManager(new_key, self.fingerprint_dir)
|
new_enc_mgr = EncryptionManager(new_key, self.fingerprint_dir)
|
||||||
|
@@ -23,6 +23,7 @@ def test_config_defaults_and_round_trip():
|
|||||||
assert cfg["pin_hash"] == ""
|
assert cfg["pin_hash"] == ""
|
||||||
assert cfg["password_hash"] == ""
|
assert cfg["password_hash"] == ""
|
||||||
assert cfg["additional_backup_path"] == ""
|
assert cfg["additional_backup_path"] == ""
|
||||||
|
assert cfg["kdf_iterations"] == 100_000
|
||||||
|
|
||||||
cfg_mgr.set_pin("1234")
|
cfg_mgr.set_pin("1234")
|
||||||
cfg_mgr.set_relays(["wss://example.com"], require_pin=False)
|
cfg_mgr.set_relays(["wss://example.com"], require_pin=False)
|
||||||
@@ -146,3 +147,14 @@ def test_secret_mode_round_trip():
|
|||||||
cfg2 = cfg_mgr.load_config(require_pin=False)
|
cfg2 = cfg_mgr.load_config(require_pin=False)
|
||||||
assert cfg2["secret_mode_enabled"] is True
|
assert cfg2["secret_mode_enabled"] is True
|
||||||
assert cfg2["clipboard_clear_delay"] == 99
|
assert cfg2["clipboard_clear_delay"] == 99
|
||||||
|
|
||||||
|
|
||||||
|
def test_kdf_iterations_round_trip():
|
||||||
|
with TemporaryDirectory() as tmpdir:
|
||||||
|
vault, _ = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD)
|
||||||
|
cfg_mgr = ConfigManager(vault, Path(tmpdir))
|
||||||
|
|
||||||
|
assert cfg_mgr.get_kdf_iterations() == 100_000
|
||||||
|
|
||||||
|
cfg_mgr.set_kdf_iterations(200_000)
|
||||||
|
assert cfg_mgr.get_kdf_iterations() == 200_000
|
||||||
|
Reference in New Issue
Block a user