Skip to content

Commit

Permalink
Remove obsolete gpg version utils
Browse files Browse the repository at this point in the history
The gpg utility functions `is_version_fully_supported` and
`get_version` were used in in-toto tests to case-handle different
gpg versions. With in-toto/in-toto#190 this is no longer needed.
See in-toto/in-toto@28ba993, in-toto/in-toto@c3a40b8.

To reduce the API surface and maintenance burden this commit
removes those functions.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
  • Loading branch information
lukpueh committed Feb 9, 2023
1 parent e809bf9 commit b0e9579
Show file tree
Hide file tree
Showing 3 changed files with 2 additions and 156 deletions.
108 changes: 1 addition & 107 deletions securesystemslib/gpg/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
general-purpose utilities for binary data handling and pgp data parsing
"""
import binascii
import dataclasses
import logging
import re
import struct

CRYPTO = True
Expand All @@ -29,7 +27,7 @@
CRYPTO = False

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

Expand Down Expand Up @@ -308,110 +306,6 @@ def parse_subpackets(data):
return parsed_subpackets


@dataclasses.dataclass(order=True)
class Version:
"""A version of GPG."""

major: int
minor: int
patch: int

VERSION_RE = re.compile(r"(\d)\.(\d)\.(\d+)")
EXAMPLE = "1.3.22"

@classmethod
def from_string(cls, value: str) -> "Version":
"""
<Purpose>
Parses `value` as a `Version`.
Expects a version in the format `major.minor.patch`. `major` and `minor`
must be one-digit numbers; `patch` can be any integer.
<Arguments>
value:
The version string to parse.
<Exceptions>
ValueError:
If the version string is invalid.
<Returns>
Version
"""
match = cls.VERSION_RE.fullmatch(value)
if not match:
raise ValueError(
f"Invalid version number '{value}'; "
f"expected MAJOR.MINOR.PATCH (e.g., '{cls.EXAMPLE}')"
)
major, minor, patch = map(int, match.groups())
return cls(major, minor, patch)

def __str__(self):
return f"{self.major}.{self.minor}.{self.patch}"


def get_version() -> Version:
"""
<Purpose>
Uses `gpg2 --version` to get the version info of the installed gpg2
and extracts and returns the version number.
The executed base command is defined in constants.gpg_version_command.
<Exceptions>
securesystemslib.exceptions.UnsupportedLibraryError:
If the gpg command is not available
<Side Effects>
Executes a command: constants.gpg_version_command.
<Returns>
Version of GPG.
"""
if not constants.have_gpg(): # pragma: no cover
raise exceptions.UnsupportedLibraryError(constants.NO_GPG_MSG)

command = constants.gpg_version_command()
gpg_process = process.run(
command,
stdout=process.PIPE,
stderr=process.PIPE,
universal_newlines=True,
)

full_version_info = gpg_process.stdout
try:
match = Version.VERSION_RE.search(full_version_info)
if not match:
raise ValueError(
f"Couldn't find version number (ex. '{Version.EXAMPLE}') "
f"in the output of `{command}`:\n" + full_version_info
)
version = Version.from_string(match.group(0))
except ValueError as err:
raise exceptions.UnsupportedLibraryError(constants.NO_GPG_MSG) from err

return version


def is_version_fully_supported():
"""
<Purpose>
Compares the version of installed gpg2 with the minimal fully supported
gpg2 version (2.1.0).
<Returns>
True if the version returned by `get_version` is greater-equal
constants.FULLY_SUPPORTED_MIN_VERSION, False otherwise.
"""
min_version = constants.FULLY_SUPPORTED_MIN_VERSION
return get_version() >= Version.from_string(min_version)


def get_hashing_class(hash_algorithm_id):
"""
<Purpose>
Expand Down
5 changes: 0 additions & 5 deletions tests/check_public_interfaces_gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
export_pubkeys,
verify_signature,
)
from securesystemslib.gpg.util import get_version


class TestPublicInterfacesGPG(
Expand All @@ -60,10 +59,6 @@ def test_gpg_functions(self):
export_pubkeys(["f00"])
self.assertEqual(NO_GPG_MSG, str(ctx.exception))

with self.assertRaises(UnsupportedLibraryError) as ctx:
get_version()
self.assertEqual(NO_GPG_MSG, str(ctx.exception))

def test_gpg_verify(self):
"""Signature verification does not require gpg to be installed on the host.
To prove it, we run basic verification tests for rsa, dsa and eddsa with
Expand Down
45 changes: 1 addition & 44 deletions tests/test_gpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from cryptography.hazmat import backends
from cryptography.hazmat.primitives import serialization

from securesystemslib import exceptions, process
from securesystemslib import process
from securesystemslib.formats import ANY_PUBKEY_DICT_SCHEMA, GPG_PUBKEY_SCHEMA
from securesystemslib.gpg.common import (
_assign_certified_key_info,
Expand Down Expand Up @@ -72,10 +72,7 @@
)
from securesystemslib.gpg.rsa import create_pubkey as rsa_create_pubkey
from securesystemslib.gpg.util import (
Version,
get_hashing_class,
get_version,
is_version_fully_supported,
parse_packet_header,
parse_subpacket_header,
)
Expand All @@ -100,19 +97,6 @@ def ignore_not_found_error(
class TestUtil(unittest.TestCase):
"""Test util functions."""

def test_version_utils_return_types(self):
"""Run dummy tests for coverage."""
self.assertTrue(isinstance(get_version(), Version))
self.assertTrue(isinstance(is_version_fully_supported(), bool))

def test_version_utils_error(self):
"""Run dummy tests for coverage."""
with patch(
"securesystemslib.gpg.constants.have_gpg", return_value=False
):
with self.assertRaises(exceptions.UnsupportedLibraryError):
get_version()

def test_get_hashing_class(self):
# Assert return expected hashing class
expected_hashing_class = [hashing.SHA1, hashing.SHA256, hashing.SHA512]
Expand Down Expand Up @@ -947,32 +931,5 @@ def test_verify_short_signature(self):
self.assertTrue(verify_signature(signature, key, test_data))


class TestVersion(unittest.TestCase):
"""Tests for the Version utility class."""

def test_version_roundtrip_string(self):
"""Version parses and formats strings correctly."""
for value, expected in [
("1.3.0", Version(1, 3, 0)),
("1.3.1", Version(1, 3, 1)),
("1.3.22", Version(1, 3, 22)),
]:
self.assertEqual(Version.from_string(value), expected)
self.assertEqual(str(expected), value)

def test_version_from_string_invalid(self):
"""Version.from_string rejects invalid inputs."""
for value in [
"1.3",
"1.33.0",
"1.3.-1",
"1.3.1a",
]:
with self.assertRaises(
ValueError, msg=f"expected error for input '{value}'"
):
Version.from_string(value)


if __name__ == "__main__":
unittest.main()

0 comments on commit b0e9579

Please sign in to comment.