Skip to content

Commit

Permalink
Mark securesystemslib.gpg subpackage as internal
Browse files Browse the repository at this point in the history
The `gpg` subpackage provides a vaguely defined API (`gpg.functions`) to
create signatures, export public keys, and verify signatures. This API
and the used formats are incompatible with the securesystemslib signer
API.

For the sake of a consistent API, the `gpg` subpackage is marked as
internal (renamed to `_gpg`) and the above mentioned functionality is
exposed via the new signer API. Replacement methods are:

- `GPGSigner.import_` (replaces `export_pubkey`)
- `GPGSigner.sign`
- `GPGKey.verify_signature`

Note that public key and signature formats also change, in order to match
`Key` and `Signature` interfaces. This means:

- signature field `signature` is renamed to `sig`
- public key fields `type`, `method` and `hashes` are replaced by
  `keytype` and `scheme` fields, and
- public keys no longer include `subkeys` or key expiration infos. This
  means that the signature verification function no longer needs to
  decide, if a key is authorized or valid to verify a given signature.

See discussion for context:
secure-systems-lab#488 (comment)
secure-systems-lab#488 (comment)

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
  • Loading branch information
lukpueh committed Apr 23, 2024
1 parent 1951330 commit 771d326
Show file tree
Hide file tree
Showing 19 changed files with 89 additions and 87 deletions.
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ warn_unused_configs = True
files =
securesystemslib/signer/*.py,
securesystemslib/storage.py,
securesystemslib/gpg/constants.py
securesystemslib/_gpg/constants.py

# Supress error messages until enough modules
# are type annotated
Expand Down
File renamed without changes.
30 changes: 15 additions & 15 deletions securesystemslib/gpg/common.py → securesystemslib/_gpg/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import logging
import struct

from securesystemslib.gpg import util as gpg_util
from securesystemslib.gpg.constants import (
from securesystemslib._gpg import util as gpg_util
from securesystemslib._gpg.constants import (
FULL_KEYID_SUBPACKET,
GPG_HASH_ALGORITHM_STRING,
KEY_EXPIRATION_SUBPACKET,
Expand All @@ -45,13 +45,13 @@
SUPPORTED_PUBKEY_PACKET_VERSIONS,
SUPPORTED_SIGNATURE_PACKET_VERSIONS,
)
from securesystemslib.gpg.exceptions import (
from securesystemslib._gpg.exceptions import (
KeyNotFoundError,
PacketParsingError,
PacketVersionNotSupportedError,
SignatureAlgorithmNotSupportedError,
)
from securesystemslib.gpg.handlers import (
from securesystemslib._gpg.handlers import (
SIGNATURE_HANDLERS,
SUPPORTED_SIGNATURE_ALGORITHMS,
)
Expand All @@ -71,7 +71,7 @@ def parse_pubkey_payload(data):
(version 4) of the RFC.
NOTE: The payload can be parsed from a full key packet (header +
payload) by using securesystemslib.gpg.util.parse_packet_header.
payload) by using securesystemslib._gpg.util.parse_packet_header.
WARNING: this doesn't support armored pubkey packets, so use with
care. pubkey packets are a little bit more complicated than the
Expand All @@ -81,13 +81,13 @@ def parse_pubkey_payload(data):
ValueError
If the passed public key data is empty.
securesystemslib.gpg.exceptions.PacketVersionNotSupportedError
securesystemslib._gpg.exceptions.PacketVersionNotSupportedError
If the packet version does not match
securesystemslib.gpg.constants.SUPPORTED_PUBKEY_PACKET_VERSIONS
securesystemslib._gpg.constants.SUPPORTED_PUBKEY_PACKET_VERSIONS
securesystemslib.gpg.exceptions.SignatureAlgorithmNotSupportedError
securesystemslib._gpg.exceptions.SignatureAlgorithmNotSupportedError
If the signature algorithm does not match one of
securesystemslib.gpg.constants.SUPPORTED_SIGNATURE_ALGORITHMS
securesystemslib._gpg.constants.SUPPORTED_SIGNATURE_ALGORITHMS
<Side Effects>
None.
Expand Down Expand Up @@ -169,7 +169,7 @@ def parse_pubkey_bundle(data):
Public key data as written to stdout by gpg_export_pubkey_command.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError
securesystemslib._gpg.exceptions.PacketParsingError
If data is empty.
If data cannot be parsed.
Expand Down Expand Up @@ -585,17 +585,17 @@ def get_pubkey_bundle(data, keyid):
<Arguments>
data:
Public key data as written to stdout by
securesystemslib.gpg.constants.gpg_export_pubkey_command.
securesystemslib._gpg.constants.gpg_export_pubkey_command.
keyid:
The keyid of the master key or one of its subkeys expected to be
contained in the passed gpg data.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError
securesystemslib._gpg.exceptions.PacketParsingError
If the key data could not be parsed
securesystemslib.gpg.exceptions.KeyNotFoundError
securesystemslib._gpg.exceptions.KeyNotFoundError
If the passed data is empty.
If no master key or subkeys could be found that matches the passed
keyid.
Expand Down Expand Up @@ -676,12 +676,12 @@ def parse_signature_packet( # pylint: disable=too-many-locals,too-many-branches
section 5.2 (and 5.2.3.1).
supported_signature_types: (optional)
a set of supported signature_types, the signature packet may be
(see securesystemslib.gpg.constants for available types). If None is
(see securesystemslib._gpg.constants for available types). If None is
specified the signature packet must be of type SIGNATURE_TYPE_BINARY.
supported_hash_algorithms: (optional)
a set of supported hash algorithm ids, the signature packet
may use. Available ids are SHA1, SHA256, SHA512 (see
securesystemslib.gpg.constants). If None is specified, the signature
securesystemslib._gpg.constants). If None is specified, the signature
packet must use SHA256.
include_info: (optional)
a boolean that indicates whether an opaque dictionary should be
Expand Down
File renamed without changes.
12 changes: 6 additions & 6 deletions securesystemslib/gpg/dsa.py → securesystemslib/_gpg/dsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

# pylint: disable=wrong-import-position
from securesystemslib import exceptions
from securesystemslib.gpg import util as gpg_util
from securesystemslib.gpg.exceptions import PacketParsingError
from securesystemslib._gpg import util as gpg_util
from securesystemslib._gpg.exceptions import PacketParsingError

# pylint: enable=wrong-import-position

Expand Down Expand Up @@ -80,7 +80,7 @@ def get_pubkey_params(data):
in the fifth paragraph of section 5.5.2.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError:
securesystemslib._gpg.exceptions.PacketParsingError:
if the public key parameters are malformed
<Side Effects>
Expand Down Expand Up @@ -138,7 +138,7 @@ def get_signature_params(data):
in the fourth paragraph of section 5.2.2
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError:
securesystemslib._gpg.exceptions.PacketParsingError:
if the public key parameters are malformed
securesystemslib.exceptions.UnsupportedLibraryError:
Expand Down Expand Up @@ -189,7 +189,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
The DSA public key dict.
hash_algorithm_id:
one of SHA1, SHA256, SHA512 (see securesystemslib.gpg.constants)
one of SHA1, SHA256, SHA512 (see securesystemslib._gpg.constants)
used to verify the signature
NOTE: Overrides any hash algorithm specification in "pubkey_info"'s
"hashes" or "method" fields.
Expand All @@ -203,7 +203,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
ValueError:
if the passed hash_algorithm_id is not supported (see
securesystemslib.gpg.util.get_hashing_class)
securesystemslib._gpg.util.get_hashing_class)
<Returns>
True if signature verification passes and False otherwise
Expand Down
10 changes: 5 additions & 5 deletions securesystemslib/gpg/eddsa.py → securesystemslib/_gpg/eddsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import binascii

from securesystemslib import exceptions
from securesystemslib.gpg import util as gpg_util
from securesystemslib.gpg.exceptions import PacketParsingError
from securesystemslib._gpg import util as gpg_util
from securesystemslib._gpg.exceptions import PacketParsingError

CRYPTO = True
NO_CRYPTO_MSG = "EdDSA key support for GPG requires the cryptography library"
Expand Down Expand Up @@ -57,7 +57,7 @@ def get_pubkey_params(data):
public-key algorithm of this key.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError or IndexError:
securesystemslib._gpg.exceptions.PacketParsingError or IndexError:
if the public key data is malformed.
<Side Effects>
Expand Down Expand Up @@ -197,7 +197,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
A DSA public key dict.
hash_algorithm_id:
one of SHA1, SHA256, SHA512 (see securesystemslib.gpg.constants)
one of SHA1, SHA256, SHA512 (see securesystemslib._gpg.constants)
used to verify the signature
NOTE: Overrides any hash algorithm specification in "pubkey_info"'s
"hashes" or "method" fields.
Expand All @@ -211,7 +211,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
ValueError:
if the passed hash_algorithm_id is not supported (see
securesystemslib.gpg.util.get_hashing_class)
securesystemslib._gpg.util.get_hashing_class)
<Returns>
True if signature verification passes and False otherwise.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
import time

from securesystemslib import exceptions
from securesystemslib.gpg.common import (
from securesystemslib._gpg.common import (
get_pubkey_bundle,
parse_signature_packet,
)
from securesystemslib.gpg.constants import (
from securesystemslib._gpg.constants import (
FULLY_SUPPORTED_MIN_VERSION,
GPG_TIMEOUT,
NO_GPG_MSG,
Expand All @@ -34,9 +34,9 @@
gpg_sign_command,
have_gpg,
)
from securesystemslib.gpg.exceptions import CommandError, KeyExpirationError
from securesystemslib.gpg.handlers import SIGNATURE_HANDLERS
from securesystemslib.gpg.rsa import CRYPTO
from securesystemslib._gpg.exceptions import CommandError, KeyExpirationError
from securesystemslib._gpg.handlers import SIGNATURE_HANDLERS
from securesystemslib._gpg.rsa import CRYPTO

log = logging.getLogger(__name__)

Expand All @@ -50,10 +50,10 @@ def create_signature(content, keyid=None, homedir=None, timeout=GPG_TIMEOUT):
identified by the passed keyid from the gpg keyring at the passed homedir.
The executed base command is defined in
securesystemslib.gpg.constants.gpg_sign_command.
securesystemslib._gpg.constants.gpg_sign_command.
NOTE: On not fully supported versions of GPG, i.e. versions below
securesystemslib.gpg.constants.FULLY_SUPPORTED_MIN_VERSION the returned
securesystemslib._gpg.constants.FULLY_SUPPORTED_MIN_VERSION the returned
signature does not contain the full keyid. As a work around, we export the
public key bundle identified by the short keyid to compute the full keyid
and add it to the returned signature.
Expand Down Expand Up @@ -84,10 +84,10 @@ def create_signature(content, keyid=None, homedir=None, timeout=GPG_TIMEOUT):
If the gpg command is not available, or
the cryptography library is not installed.
securesystemslib.gpg.exceptions.CommandError:
securesystemslib._gpg.exceptions.CommandError:
If the gpg command returned a non-zero exit code
securesystemslib.gpg.exceptions.KeyNotFoundError:
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 @@ -215,7 +215,7 @@ def verify_signature(signature_object, pubkey_info, content):
The content to be verified. (bytes)
<Exceptions>
securesystemslib.gpg.exceptions.KeyExpirationError:
securesystemslib._gpg.exceptions.KeyExpirationError:
if the passed public key has expired
securesystemslib.exceptions.UnsupportedLibraryError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
the signature verification and key parsing.
"""

from securesystemslib.gpg import dsa, eddsa, rsa
from securesystemslib._gpg import dsa, eddsa, rsa

# See section 9.1. (public-key algorithms) of RFC4880 (-bis8)
SUPPORTED_SIGNATURE_ALGORITHMS = {
Expand Down
12 changes: 6 additions & 6 deletions securesystemslib/gpg/rsa.py → securesystemslib/_gpg/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

# pylint: disable=wrong-import-position
from securesystemslib import exceptions
from securesystemslib.gpg import util as gpg_util
from securesystemslib.gpg.exceptions import PacketParsingError
from securesystemslib._gpg import util as gpg_util
from securesystemslib._gpg.exceptions import PacketParsingError

# pylint: enable=wrong-import-position

Expand Down Expand Up @@ -74,7 +74,7 @@ def get_pubkey_params(data):
in the fifth paragraph of section 5.5.2.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError:
securesystemslib._gpg.exceptions.PacketParsingError:
if the public key parameters are malformed
<Side Effects>
Expand Down Expand Up @@ -115,7 +115,7 @@ def get_signature_params(data):
in the third paragraph of section 5.2.2.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError:
securesystemslib._gpg.exceptions.PacketParsingError:
if the public key parameters are malformed
<Side Effects>
Expand Down Expand Up @@ -152,7 +152,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
The signed bytes against which the signature is verified
hash_algorithm_id:
one of SHA1, SHA256, SHA512 (see securesystemslib.gpg.constants)
one of SHA1, SHA256, SHA512 (see securesystemslib._gpg.constants)
used to verify the signature
NOTE: Overrides any hash algorithm specification in "pubkey_info"'s
"hashes" or "method" fields.
Expand All @@ -163,7 +163,7 @@ def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id):
ValueError:
if the passed hash_algorithm_id is not supported (see
securesystemslib.gpg.util.get_hashing_class)
securesystemslib._gpg.util.get_hashing_class)
<Returns>
True if signature verification passes and False otherwise
Expand Down
10 changes: 5 additions & 5 deletions securesystemslib/gpg/util.py → securesystemslib/_gpg/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

# pylint: disable=wrong-import-position
from securesystemslib import exceptions
from securesystemslib.gpg import constants
from securesystemslib.gpg.exceptions import PacketParsingError
from securesystemslib._gpg import constants
from securesystemslib._gpg.exceptions import PacketParsingError

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -113,11 +113,11 @@ def parse_packet_header(
expected_type: (optional)
Used to error out if the packet does not have the expected
type. See securesystemslib.gpg.constants.PACKET_TYPE_* for
type. See securesystemslib._gpg.constants.PACKET_TYPE_* for
available types.
<Exceptions>
securesystemslib.gpg.exceptions.PacketParsingError
securesystemslib._gpg.exceptions.PacketParsingError
If the new format packet length encodes a partial body length
If the old format packet length encodes an indeterminate length
If header or body length could not be determined
Expand Down Expand Up @@ -315,7 +315,7 @@ def get_hashing_class(hash_algorithm_id):
<Arguments>
hash_algorithm_id:
one of SHA1, SHA256, SHA512 (see securesystemslib.gpg.constants)
one of SHA1, SHA256, SHA512 (see securesystemslib._gpg.constants)
<Exceptions>
ValueError
Expand Down
10 changes: 5 additions & 5 deletions securesystemslib/signer/_gpg_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from urllib import parse

from securesystemslib import exceptions
from securesystemslib.gpg import constants as gpg_constants
from securesystemslib.gpg import exceptions as gpg_exceptions
from securesystemslib.gpg import functions as gpg
from securesystemslib._gpg import constants as gpg_constants
from securesystemslib._gpg import exceptions as gpg_exceptions
from securesystemslib._gpg import functions as gpg
from securesystemslib.signer._key import Key
from securesystemslib.signer._signer import SecretsHandler, Signature, Signer

Expand Down Expand Up @@ -205,9 +205,9 @@ def sign(self, payload: bytes) -> Signature:
OSError: gpg command is not present or non-executable.
securesystemslib.exceptions.UnsupportedLibraryError: gpg command is not
available, or the cryptography library is not installed.
securesystemslib.gpg.exceptions.CommandError: gpg command returned a
securesystemslib._gpg.exceptions.CommandError: gpg command returned a
non-zero exit code.
securesystemslib.gpg.exceptions.KeyNotFoundError: gpg version is not fully
securesystemslib._gpg.exceptions.KeyNotFoundError: gpg version is not fully
supported.
Returns:
Expand Down
5 changes: 3 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

# Increase gpg subprocess timeout -- Windows CI fails frequently with default 10s.
import securesystemslib.gpg.constants
# pylint: disable=protected-access
import securesystemslib._gpg.constants

securesystemslib.gpg.constants.GPG_TIMEOUT = 120
securesystemslib._gpg.constants.GPG_TIMEOUT = 120
6 changes: 4 additions & 2 deletions tests/check_gpg_available.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@

import unittest

import securesystemslib.gpg.constants
import securesystemslib._gpg.constants


class TestGpgAvailable(unittest.TestCase):
"""Test that securesystemslib finds some GPG executable in the environment."""

def test_gpg_available(self):
"""Test that GPG is available."""
self.assertTrue(securesystemslib.gpg.constants.have_gpg())
self.assertTrue(
securesystemslib._gpg.constants.have_gpg() # pylint: disable=protected-access
)


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 771d326

Please sign in to comment.