Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove obsolete gpg version utils #504

Merged
merged 1 commit into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()