Skip to content

Commit

Permalink
Do not raise non-public errors in GPGSigner
Browse files Browse the repository at this point in the history
* KeyNotFoundError, which is raised internally in multiple places,
is caught and re-raised as ValueError in GPGSigner

* CommandError, which was raised only in one place and close to the
user boundary, is replaced directly with OSError.

Using these builtin Error classes seems semantically correct, and
consistent with other errors expected in GPGSigner.

Signed-off-by: Lukas Puehringer <[email protected]>
  • Loading branch information
lukpueh committed Apr 23, 2024
1 parent 771d326 commit 439ddd8
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 28 deletions.
4 changes: 0 additions & 4 deletions securesystemslib/_gpg/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ class SignatureAlgorithmNotSupportedError(Exception):
pass


class CommandError(Exception):
pass


class KeyExpirationError(Exception): # pylint: disable=missing-class-docstring
def __init__(self, key):
super( # pylint: disable=super-with-arguments
Expand Down
10 changes: 4 additions & 6 deletions securesystemslib/_gpg/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
gpg_sign_command,
have_gpg,
)
from securesystemslib._gpg.exceptions import CommandError, KeyExpirationError
from securesystemslib._gpg.exceptions import KeyExpirationError
from securesystemslib._gpg.handlers import SIGNATURE_HANDLERS
from securesystemslib._gpg.rsa import CRYPTO

Expand Down Expand Up @@ -78,15 +78,13 @@ def create_signature(content, keyid=None, homedir=None, timeout=GPG_TIMEOUT):
If the gpg command failed to create a valid signature.
OSError:
If the gpg command is not present or non-executable.
If the gpg command is not present, or non-executable,
or returned a non-zero exit code
securesystemslib.exceptions.UnsupportedLibraryError:
If the gpg command is not available, or
the cryptography library is not installed.
securesystemslib._gpg.exceptions.CommandError:
If the gpg command returned a non-zero exit code
securesystemslib._gpg.exceptions.KeyNotFoundError:
If the used gpg version is not fully supported
and no public key can be found for short keyid.
Expand Down Expand Up @@ -134,7 +132,7 @@ def create_signature(content, keyid=None, homedir=None, timeout=GPG_TIMEOUT):
# reporting, as there is no clear distinction between the return codes
# https://lists.gnupg.org/pipermail/gnupg-devel/2005-December/022559.html
if gpg_process.returncode != 0:
raise CommandError(
raise OSError(
"Command '{}' returned " # pylint: disable=consider-using-f-string
"non-zero exit status '{}', stderr was:\n{}.".format(
gpg_process.args,
Expand Down
31 changes: 19 additions & 12 deletions securesystemslib/signer/_gpg_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,20 @@ def import_(
Raises:
UnsupportedLibraryError: The gpg command or pyca/cryptography are
not available.
KeyNotFoundError: No key was found for the passed keyid.
ValueError: No key was found for the passed keyid.
Returns:
Tuple of private key uri and the public key.
"""
uri = f"{cls.SCHEME}:{homedir or ''}"

raw_key = gpg.export_pubkey(keyid, homedir)
try:
raw_key = gpg.export_pubkey(keyid, homedir)

except gpg_exceptions.KeyNotFoundError as e:
raise ValueError(e) from e

raw_keys = [raw_key] + list(raw_key.pop("subkeys", {}).values())
keyids = []

Expand All @@ -187,7 +192,7 @@ def import_(
keyids.append(key["keyid"])

else:
raise gpg_exceptions.KeyNotFoundError(
raise ValueError(
f"No exact match found for passed keyid"
f" {keyid}, found: {keyids}."
)
Expand All @@ -201,22 +206,24 @@ def sign(self, payload: bytes) -> Signature:
payload: bytes to be signed.
Raises:
ValueError: gpg command failed to create a valid signature.
OSError: gpg command is not present or non-executable.
ValueError: gpg command failed to create a valid signature, e.g.
because its keyid does not match the public key keyid.
OSError: gpg command is not present, or non-executable, or returned
a non-zero exit code.
securesystemslib.exceptions.UnsupportedLibraryError: gpg command is not
available, or the cryptography library is not installed.
securesystemslib._gpg.exceptions.CommandError: gpg command returned a
non-zero exit code.
securesystemslib._gpg.exceptions.KeyNotFoundError: gpg version is not fully
supported.
Returns:
Signature.
"""
raw_sig = gpg.create_signature(
payload, self.public_key.keyid, self.homedir
)
try:
raw_sig = gpg.create_signature(
payload, self.public_key.keyid, self.homedir
)
except gpg_exceptions.KeyNotFoundError as e:
raise ValueError(e) from e

if raw_sig["keyid"] != self.public_key.keyid:
raise ValueError(
f"The signing key {raw_sig['keyid']} does not"
Expand Down
5 changes: 2 additions & 3 deletions tests/test_gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
from securesystemslib._gpg.dsa import create_pubkey as dsa_create_pubkey
from securesystemslib._gpg.eddsa import ED25519_SIG_LENGTH
from securesystemslib._gpg.exceptions import (
CommandError,
KeyExpirationError,
KeyNotFoundError,
PacketParsingError,
Expand Down Expand Up @@ -702,8 +701,8 @@ def test_gpg_sign_and_verify_object_default_keyring(self):
del os.environ["GNUPGHOME"]

def test_create_signature_with_expired_key(self):
"""Test signing with expired key raises gpg CommandError."""
with self.assertRaises(CommandError) as ctx:
"""Test signing with expired key raises OSError."""
with self.assertRaises(OSError) as ctx:
create_signature(
b"livestock",
keyid=self.expired_key_keyid,
Expand Down
5 changes: 2 additions & 3 deletions tests/test_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
)

from securesystemslib._gpg.constants import have_gpg
from securesystemslib._gpg.exceptions import CommandError, KeyNotFoundError
from securesystemslib.exceptions import FormatError, UnverifiedSignatureError
from securesystemslib.signer import (
KEY_FOR_TYPE_AND_SCHEME,
Expand Down Expand Up @@ -452,7 +451,7 @@ def test_gpg_fail_import_keyid_match(self):

# gpg exports the right key, but we require an exact keyid match
non_matching_keyid = self.default_keyid.upper()
with self.assertRaises(KeyNotFoundError):
with self.assertRaises(ValueError):
GPGSigner.import_(non_matching_keyid, self.gnupg_home)

def test_gpg_fail_sign_expired_key(self):
Expand All @@ -461,7 +460,7 @@ def test_gpg_fail_sign_expired_key(self):

uri, public_key = GPGSigner.import_(expired_key, self.gnupg_home)
signer = Signer.from_priv_key_uri(uri, public_key)
with self.assertRaises(CommandError):
with self.assertRaises(OSError):
signer.sign(self.test_data)

def test_gpg_signer_load_with_bad_scheme(self):
Expand Down

0 comments on commit 439ddd8

Please sign in to comment.