diff --git a/src/seedpass/core/encryption.py b/src/seedpass/core/encryption.py index 1a71ced..af17586 100644 --- a/src/seedpass/core/encryption.py +++ b/src/seedpass/core/encryption.py @@ -24,6 +24,7 @@ from cryptography.exceptions import InvalidTag from cryptography.fernet import Fernet, InvalidToken from termcolor import colored from utils.file_lock import exclusive_lock +from mnemonic import Mnemonic # Instantiate the logger logger = logging.getLogger(__name__) @@ -314,25 +315,21 @@ class EncryptionManager: ) raise - # ... validate_seed and derive_seed_from_mnemonic can remain the same ... - def validate_seed(self, seed_phrase: str) -> bool: + def validate_seed(self, seed_phrase: str) -> tuple[bool, Optional[str]]: + """Validate a BIP-39 mnemonic. + + Returns a tuple of ``(is_valid, error_message)`` where ``error_message`` + is ``None`` when the mnemonic is valid. + """ try: - words = seed_phrase.split() - if len(words) != 12: - logger.error("Seed phrase does not contain exactly 12 words.") - print( - colored( - "Error: Seed phrase must contain exactly 12 words.", - "red", - ) - ) - return False - logger.debug("Seed phrase validated successfully.") - return True + if Mnemonic("english").check(seed_phrase): + logger.debug("Seed phrase validated successfully.") + return True, None + logger.error("Seed phrase failed BIP-39 validation.") + return False, "Invalid seed phrase." except Exception as e: - logging.error(f"Error validating seed phrase: {e}", exc_info=True) - print(colored(f"Error: Failed to validate seed phrase: {e}", "red")) - return False + logger.error(f"Error validating seed phrase: {e}", exc_info=True) + return False, f"Failed to validate seed phrase: {e}" def derive_seed_from_mnemonic(self, mnemonic: str, passphrase: str = "") -> bytes: try: diff --git a/src/tests/test_encryption_validate_seed.py b/src/tests/test_encryption_validate_seed.py new file mode 100644 index 0000000..5806d76 --- /dev/null +++ b/src/tests/test_encryption_validate_seed.py @@ -0,0 +1,30 @@ +import sys +from pathlib import Path + +import pytest +from cryptography.fernet import Fernet + +sys.path.append(str(Path(__file__).resolve().parents[1])) + +from seedpass.core.encryption import EncryptionManager + + +def make_manager(tmp_path): + key = Fernet.generate_key() + return EncryptionManager(key, tmp_path) + + +def test_validate_seed_valid_mnemonic(tmp_path): + manager = make_manager(tmp_path) + valid = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" + is_valid, error = manager.validate_seed(valid) + assert is_valid is True + assert error is None + + +def test_validate_seed_invalid_mnemonic(tmp_path): + manager = make_manager(tmp_path) + invalid = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon" + is_valid, error = manager.validate_seed(invalid) + assert is_valid is False + assert error == "Invalid seed phrase."