@@ -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