From d030cf9692ed81a9ae1b85deb19b4802ff05f043 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:21:56 -0400 Subject: [PATCH] Raise InvalidToken-compatible errors --- src/seedpass/core/encryption.py | 36 ++++++++++++++++++++++----------- src/seedpass/core/errors.py | 9 +++++++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/seedpass/core/encryption.py b/src/seedpass/core/encryption.py index 55f1d8b..b0b5c7c 100644 --- a/src/seedpass/core/encryption.py +++ b/src/seedpass/core/encryption.py @@ -138,13 +138,15 @@ class EncryptionManager: ciphertext = encrypted_data[15:] if len(ciphertext) < 16: logger.error("AES-GCM payload too short") - raise DecryptionError("Incorrect password or corrupt file") + raise DecryptionError( + f"Failed to decrypt{ctx}: AES-GCM payload too short" + ) return self.cipher.decrypt(nonce, ciphertext, None) except InvalidTag as e: - logger.error( - f"Failed to decrypt{ctx}: incorrect password or corrupt file" - ) - raise DecryptionError("Incorrect password or corrupt file") from e + logger.error(f"Failed to decrypt{ctx}: invalid key or corrupt file") + raise DecryptionError( + f"Failed to decrypt{ctx}: invalid key or corrupt file" + ) from e # Next try the older V2 format if encrypted_data.startswith(b"V2:"): @@ -153,7 +155,9 @@ class EncryptionManager: ciphertext = encrypted_data[15:] if len(ciphertext) < 16: logger.error("AES-GCM payload too short") - raise DecryptionError("Incorrect password or corrupt file") + raise DecryptionError( + f"Failed to decrypt{ctx}: AES-GCM payload too short" + ) return self.cipher.decrypt(nonce, ciphertext, None) except InvalidTag as e: logger.debug( @@ -167,10 +171,10 @@ class EncryptionManager: return result except InvalidToken: logger.error( - f"Failed to decrypt{ctx}: incorrect password or corrupt file" + f"Failed to decrypt{ctx}: invalid key or corrupt file" ) raise DecryptionError( - "Incorrect password or corrupt file" + f"Failed to decrypt{ctx}: invalid key or corrupt file" ) from e # If it's neither V3 nor V2, assume legacy Fernet format @@ -181,7 +185,9 @@ class EncryptionManager: logger.error( "Legacy Fernet decryption failed. Vault may be corrupt or key is incorrect." ) - raise DecryptionError("Incorrect password or corrupt file") from e + raise DecryptionError( + f"Failed to decrypt{ctx}: invalid key or corrupt file" + ) from e except DecryptionError as e: if ( @@ -193,7 +199,9 @@ class EncryptionManager: logger.debug(f"Could not decrypt data{ctx}: {e}") raise LegacyFormatRequiresMigrationError(context) from e except (InvalidToken, InvalidTag) as e: # pragma: no cover - safety net - raise DecryptionError("Incorrect password or corrupt file") from e + raise DecryptionError( + f"Failed to decrypt{ctx}: invalid key or corrupt file" + ) from e def decrypt_legacy( self, encrypted_data: bytes, password: str, context: Optional[str] = None @@ -230,7 +238,9 @@ class EncryptionManager: except Exception as e2: # pragma: no cover - try next iteration last_exc = e2 logger.error(f"Failed legacy decryption attempt: {last_exc}", exc_info=True) - raise DecryptionError(f"Incorrect password or corrupt file") from last_exc + raise DecryptionError( + f"Failed to decrypt{ctx}: invalid key or corrupt file" + ) from last_exc # --- All functions below this point now use the smart `decrypt_data` method --- @@ -420,7 +430,9 @@ class EncryptionManager: except (InvalidToken, InvalidTag) as e: # pragma: no cover - legacy safety msg = f"Failed to decrypt or parse data from {file_path}: {e}" logger.error(msg) - raise DecryptionError("Incorrect password or corrupt file") from e + raise DecryptionError( + f"Failed to decrypt {file_path}: invalid key or corrupt file" + ) from e except JSONDecodeError as e: msg = f"Failed to parse JSON data from {file_path}: {e}" logger.error(msg) diff --git a/src/seedpass/core/errors.py b/src/seedpass/core/errors.py index 6f3e033..85bc99c 100644 --- a/src/seedpass/core/errors.py +++ b/src/seedpass/core/errors.py @@ -9,6 +9,7 @@ exception, displaying a friendly message and exiting with code ``1``. """ from click import ClickException +from cryptography.fernet import InvalidToken class SeedPassError(ClickException): @@ -18,8 +19,12 @@ class SeedPassError(ClickException): super().__init__(message) -class DecryptionError(SeedPassError): - """Raised when encrypted data cannot be decrypted.""" +class DecryptionError(InvalidToken, SeedPassError): + """Raised when encrypted data cannot be decrypted. + + Subclasses :class:`cryptography.fernet.InvalidToken` so callers expecting + the cryptography exception continue to work. + """ __all__ = ["SeedPassError", "DecryptionError"]