Skip to content

Commit bc94d6f

Browse files
committed
Refactor _get_metadata_file() from updater.py
Currently, the `_get_metadata_file()` from `tuf/client/updater.py` is really large and does too many things and some of them partially. Right now, it validates the specification version and the metadata version in it, but doesn't validate if the metadata has expired or if the signature could be trusted. So, it's partially validating the metadata file and at the same time calling `_verify_metadata_file()` from updater.py for the rest of the work which doesn't make sense. It's logically better if we move the validation of the specification and the metadata version in separate functions which would be then called in ` _verify_metadata_file()` and that way all of the validation will be done in ` _verify_metadata_file()`. Signed-off-by: Martin Vrachev <[email protected]>
1 parent 88355bd commit bc94d6f

File tree

1 file changed

+86
-81
lines changed

1 file changed

+86
-81
lines changed

tuf/client/updater.py

Lines changed: 86 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,9 +1412,81 @@ def _verify_root_self_signed(self, signable):
14121412

14131413

14141414

1415+
def _validate_metadata_version(self, expected_version, metadata_role,
1416+
version_downloaded):
1417+
"""
1418+
Validates the metadata version number.
1419+
If the version number is unspecified, ensure that the version number
1420+
downloaded is greater than the currently trusted version number for
1421+
'metadata_role'.
1422+
"""
1423+
1424+
if expected_version is not None:
1425+
if version_downloaded != expected_version:
1426+
raise tuf.exceptions.BadVersionNumberError('Downloaded'
1427+
' version number: ' + repr(version_downloaded) + '. Version'
1428+
' number MUST be: ' + repr(expected_version))
1429+
1430+
# The caller does not know which version to download.
1431+
# Verify that the downloaded version is at least greater
1432+
# than the one locally available.
1433+
else:
1434+
try:
1435+
current_version = self.metadata['current'][metadata_role]['version']
1436+
1437+
if version_downloaded < current_version:
1438+
raise tuf.exceptions.ReplayedMetadataError(metadata_role,
1439+
version_downloaded, current_version)
1440+
1441+
except KeyError:
1442+
logger.info(metadata_role + ' not available locally.')
1443+
1444+
1445+
1446+
def _validate_spec_version(self, metadata_spec_version):
1447+
"""
1448+
Validates if the specification version number is supported.
1449+
It is assumed that "spec_version" is in (major.minor.fix) format,
1450+
(for example: "1.4.3") and that releases with the same major version
1451+
number maintain backward compatibility.
1452+
Consequently, if the major version number of new metadata equals our
1453+
expected major version number, the new metadata is safe to parse.
1454+
"""
1455+
1456+
try:
1457+
metadata_spec_version_split = metadata_spec_version.split('.')
1458+
metadata_spec_major_version = int(metadata_spec_version_split[0])
1459+
metadata_spec_minor_version = int(metadata_spec_version_split[1])
1460+
1461+
code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.')
1462+
code_spec_major_version = int(code_spec_version_split[0])
1463+
code_spec_minor_version = int(code_spec_version_split[1])
1464+
1465+
if metadata_spec_major_version != code_spec_major_version:
1466+
raise tuf.exceptions.UnsupportedSpecificationError(
1467+
'Downloaded metadata that specifies an unsupported spec_version. '
1468+
'This code supports major version number: ' +
1469+
repr(code_spec_major_version) + '; however,'
1470+
'metadata spec version is: ' + str(metadata_spec_version))
1471+
1472+
# report to user if minor versions do not match, continue with update
1473+
if metadata_spec_minor_version != code_spec_minor_version:
1474+
logger.info("Downloaded metadata that specifies a different minor " +
1475+
"spec_version.")
1476+
logger.info("This code has version " + tuf.SPECIFICATION_VERSION +
1477+
" and the metadata lists version number " +
1478+
str(metadata_spec_version) + ".")
1479+
logger.info("The update will continue as the major versions match.")
1480+
1481+
except (ValueError, TypeError) as error:
1482+
six.raise_from(securesystemslib.exceptions.FormatError('Improperly'
1483+
' formatted spec_version, which must be in major.minor.fix format'),
1484+
error)
1485+
1486+
14151487

14161488
def _verify_metadata_file(self, metadata_file_object,
1417-
metadata_role):
1489+
metadata_role, expected_version):
14181490
"""
14191491
<Purpose>
14201492
Non-public method that verifies a metadata file. An exception is
@@ -1429,6 +1501,10 @@ def _verify_metadata_file(self, metadata_file_object,
14291501
The role name of the metadata (e.g., 'root', 'targets',
14301502
'unclaimed').
14311503
1504+
expected_version:
1505+
An integer representing the expected and required version number
1506+
of the 'metadata_role' file downloaded.
1507+
14321508
<Exceptions>
14331509
securesystemslib.exceptions.FormatError:
14341510
In case the metadata file is valid JSON, but not valid TUF metadata.
@@ -1468,6 +1544,11 @@ def _verify_metadata_file(self, metadata_file_object,
14681544
# 'securesystemslib.exceptions.FormatError' if not.
14691545
tuf.formats.check_signable_object_format(metadata_signable)
14701546

1547+
self._validate_spec_version(metadata_signable['signed']['spec_version'])
1548+
1549+
self._validate_metadata_version(expected_version, metadata_role,
1550+
metadata_signable['signed']['version'])
1551+
14711552
# Is 'metadata_signable' expired?
14721553
self._ensure_not_expired(metadata_signable['signed'], metadata_role)
14731554

@@ -1521,8 +1602,8 @@ def _get_metadata_file(self, metadata_role, remote_filename,
15211602
downloaded.
15221603
15231604
expected_version:
1524-
The expected and required version number of the 'metadata_role' file
1525-
downloaded. 'expected_version' is an integer.
1605+
An integer representing the expected and required version number
1606+
of the 'metadata_role' file downloaded.
15261607
15271608
<Exceptions>
15281609
tuf.exceptions.NoWorkingMirrorError:
@@ -1549,85 +1630,9 @@ def _get_metadata_file(self, metadata_role, remote_filename,
15491630
try:
15501631
file_object = tuf.download.unsafe_download(file_mirror,
15511632
upperbound_filelength)
1552-
file_object.seek(0)
1553-
1554-
# Verify 'file_object' according to the callable function.
1555-
# 'file_object' is also verified if decompressed above (i.e., the
1556-
# uncompressed version).
1557-
metadata_signable = \
1558-
securesystemslib.util.load_json_string(file_object.read().decode('utf-8'))
1559-
1560-
# Determine if the specification version number is supported. It is
1561-
# assumed that "spec_version" is in (major.minor.fix) format, (for
1562-
# example: "1.4.3") and that releases with the same major version
1563-
# number maintain backwards compatibility. Consequently, if the major
1564-
# version number of new metadata equals our expected major version
1565-
# number, the new metadata is safe to parse.
1566-
try:
1567-
metadata_spec_version = metadata_signable['signed']['spec_version']
1568-
metadata_spec_version_split = metadata_spec_version.split('.')
1569-
metadata_spec_major_version = int(metadata_spec_version_split[0])
1570-
metadata_spec_minor_version = int(metadata_spec_version_split[1])
1571-
1572-
code_spec_version_split = tuf.SPECIFICATION_VERSION.split('.')
1573-
code_spec_major_version = int(code_spec_version_split[0])
1574-
code_spec_minor_version = int(code_spec_version_split[1])
1575-
1576-
if metadata_spec_major_version != code_spec_major_version:
1577-
raise tuf.exceptions.UnsupportedSpecificationError(
1578-
'Downloaded metadata that specifies an unsupported '
1579-
'spec_version. This code supports major version number: ' +
1580-
repr(code_spec_major_version) + '; however, the obtained '
1581-
'metadata lists version number: ' + str(metadata_spec_version))
1582-
1583-
#report to user if minor versions do not match, continue with update
1584-
if metadata_spec_minor_version != code_spec_minor_version:
1585-
logger.info("Downloaded metadata that specifies a different minor " +
1586-
"spec_version. This code has version " +
1587-
str(tuf.SPECIFICATION_VERSION) +
1588-
" and the metadata lists version number " +
1589-
str(metadata_spec_version) +
1590-
". The update will continue as the major versions match.")
1591-
1592-
except (ValueError, TypeError) as error:
1593-
six.raise_from(securesystemslib.exceptions.FormatError('Improperly'
1594-
' formatted spec_version, which must be in major.minor.fix format'),
1595-
error)
1596-
1597-
# If the version number is unspecified, ensure that the version number
1598-
# downloaded is greater than the currently trusted version number for
1599-
# 'metadata_role'.
1600-
version_downloaded = metadata_signable['signed']['version']
1601-
1602-
if expected_version is not None:
1603-
# Verify that the downloaded version matches the version expected by
1604-
# the caller.
1605-
if version_downloaded != expected_version:
1606-
raise tuf.exceptions.BadVersionNumberError('Downloaded'
1607-
' version number: ' + repr(version_downloaded) + '. Version'
1608-
' number MUST be: ' + repr(expected_version))
1609-
1610-
# The caller does not know which version to download. Verify that the
1611-
# downloaded version is at least greater than the one locally
1612-
# available.
1613-
else:
1614-
# Verify that the version number of the locally stored
1615-
# 'timestamp.json', if available, is less than what was downloaded.
1616-
# Otherwise, accept the new timestamp with version number
1617-
# 'version_downloaded'.
1618-
1619-
try:
1620-
current_version = \
1621-
self.metadata['current'][metadata_role]['version']
1622-
1623-
if version_downloaded < current_version:
1624-
raise tuf.exceptions.ReplayedMetadataError(metadata_role,
1625-
version_downloaded, current_version)
1626-
1627-
except KeyError:
1628-
logger.info(metadata_role + ' not available locally.')
16291633

1630-
self._verify_metadata_file(file_object, metadata_role)
1634+
self._verify_metadata_file(file_object, metadata_role,
1635+
expected_version)
16311636

16321637
except Exception as exception:
16331638
# Remember the error from this mirror, and "reset" the target file.

0 commit comments

Comments
 (0)