@@ -1373,9 +1373,115 @@ def _verify_root_self_signed(self, signable):
13731373
13741374
13751375
1376+ def _validate_metadata_version (self , expected_version , metadata_role ,
1377+ version_downloaded ):
1378+ """
1379+ <Purpose>
1380+ Non-public method validating the metadata version number.
1381+ If the expected_version is unspecified, ensure that the version number
1382+ downloaded is equal or greater than the currently trusted version number
1383+ for 'metadata_role'.
1384+
1385+ <Arguments>
1386+ expected_version:
1387+ An integer or "None" value representing the expected and required
1388+ version number of the 'metadata_role' file downloaded.
1389+
1390+ metadata_role:
1391+ The role name of the metadata (e.g., 'root', 'targets').
1392+
1393+ version_downloaded:
1394+ The version of the newly downloaded metadata file.
1395+
1396+ <Exceptions>
1397+ tuf.exceptions.BadVersionNumberError:
1398+ In case the expected_version is not None and version_downloaded
1399+ is not equal to it.
1400+
1401+ tuf.exceptions.ReplayedMetadataError:
1402+ if expected_version is None and version_downloaded is lower than
1403+ the current version.
1404+ """
1405+
1406+ if expected_version is not None :
1407+ if version_downloaded != expected_version :
1408+ raise tuf .exceptions .BadVersionNumberError ('Downloaded'
1409+ ' version number: ' + repr (version_downloaded ) + '. Version'
1410+ ' number MUST be: ' + repr (expected_version ))
1411+
1412+ # The caller does not know which version to download.
1413+ # Verify that the downloaded version is equal or
1414+ # greater than the currently trusted version number.
1415+ else :
1416+ try :
1417+ current_version = self .metadata ['current' ][metadata_role ]['version' ]
1418+
1419+ if version_downloaded < current_version :
1420+ raise tuf .exceptions .ReplayedMetadataError (metadata_role ,
1421+ version_downloaded , current_version )
1422+
1423+ except KeyError :
1424+ logger .info (metadata_role + ' not available locally.' )
1425+
1426+
1427+
1428+ def _validate_spec_version (self , metadata_spec_version ):
1429+ """
1430+ <Purpose>
1431+ Non-public method verifying a metadata specification version.
1432+ It is assumed that "spec_version" is in (major.minor.fix) format,
1433+ (for example: "1.4.3") and that releases with the same major version
1434+ number maintain backward compatibility.
1435+ Consequently, if the major version number of new metadata equals our
1436+ expected major version number, the new metadata is safe to parse.
1437+
1438+ <Arguments>
1439+ metadata_spec_version:
1440+ A string representing the metadata spec version. It is assumed that
1441+ it is in semantic versioning (major.minor.fix) format.
1442+
1443+ <Exceptions>
1444+ tuf.exceptions.UnsupportedSpecificationError:
1445+ In case the metadata major spec version is not supported.
1446+
1447+ securesystemslib.exceptions.FormatError:
1448+ In case the metadata_spec_version string is not in the expected format.
1449+ """
1450+
1451+ try :
1452+ metadata_spec_version_split = metadata_spec_version .split ('.' )
1453+ metadata_spec_major_version = int (metadata_spec_version_split [0 ])
1454+ metadata_spec_minor_version = int (metadata_spec_version_split [1 ])
1455+
1456+ code_spec_version_split = tuf .SPECIFICATION_VERSION .split ('.' )
1457+ code_spec_major_version = int (code_spec_version_split [0 ])
1458+ code_spec_minor_version = int (code_spec_version_split [1 ])
1459+
1460+ if metadata_spec_major_version != code_spec_major_version :
1461+ raise tuf .exceptions .UnsupportedSpecificationError (
1462+ 'Downloaded metadata that specifies an unsupported spec_version. '
1463+ 'This code supports major version number: ' +
1464+ repr (code_spec_major_version ) + '; however,'
1465+ 'metadata spec version is: ' + str (metadata_spec_version ))
1466+
1467+ # report to user if minor versions do not match, continue with update
1468+ if metadata_spec_minor_version != code_spec_minor_version :
1469+ logger .info ("Downloaded metadata that specifies a different minor " +
1470+ "spec_version." )
1471+ logger .info ("This code has version " + tuf .SPECIFICATION_VERSION +
1472+ " and the metadata lists version number " +
1473+ str (metadata_spec_version ) + "." )
1474+ logger .info ("The update will continue as the major versions match." )
1475+
1476+ except (ValueError , TypeError ) as error :
1477+ six .raise_from (securesystemslib .exceptions .FormatError ('Improperly'
1478+ ' formatted spec_version, which must be in major.minor.fix format' ),
1479+ error )
1480+
1481+
13761482
13771483 def _verify_metadata_file (self , metadata_file_object ,
1378- metadata_role ):
1484+ metadata_role , expected_version ):
13791485 """
13801486 <Purpose>
13811487 Non-public method that verifies a metadata file. An exception is
@@ -1390,15 +1496,29 @@ def _verify_metadata_file(self, metadata_file_object,
13901496 The role name of the metadata (e.g., 'root', 'targets',
13911497 'unclaimed').
13921498
1499+ expected_version:
1500+ An integer or "None" value representing the expected and required
1501+ version number of the 'metadata_role' file downloaded.
1502+
13931503 <Exceptions>
13941504 securesystemslib.exceptions.FormatError:
1395- In case the metadata file is valid JSON, but not valid TUF metadata.
1505+ In case the metadata file is valid JSON, but not valid TUF metadata
1506+ or when the metadata spec version is not in the rigth format.
13961507
13971508 tuf.exceptions.InvalidMetadataJSONError:
13981509 In case the metadata file is not valid JSON.
13991510
1511+ tuf.exceptions.UnsupportedSpecificationError:
1512+ In case the metadata spec version is not supported.
1513+
14001514 tuf.exceptions.ReplayedMetadataError:
1401- In case the downloaded metadata file is older than the current one.
1515+ In case the downloaded metadata file is older than the current one or
1516+ if expected_version is None and version_downloaded is lower than the
1517+ trusted current version.
1518+
1519+ tuf.exceptions.BadVersionNumberError:
1520+ In case the expected_version is not None and version_downloaded is not
1521+ equal to it.
14021522
14031523 tuf.exceptions.RepositoryError:
14041524 In case the repository is somehow inconsistent; e.g. a parent has not
@@ -1429,6 +1549,11 @@ def _verify_metadata_file(self, metadata_file_object,
14291549 # 'securesystemslib.exceptions.FormatError' if not.
14301550 tuf .formats .check_signable_object_format (metadata_signable )
14311551
1552+ self ._validate_spec_version (metadata_signable ['signed' ]['spec_version' ])
1553+
1554+ self ._validate_metadata_version (expected_version , metadata_role ,
1555+ metadata_signable ['signed' ]['version' ])
1556+
14321557 # Is 'metadata_signable' expired?
14331558 self ._ensure_not_expired (metadata_signable ['signed' ], metadata_role )
14341559
@@ -1482,8 +1607,8 @@ def _get_metadata_file(self, metadata_role, remote_filename,
14821607 downloaded.
14831608
14841609 expected_version:
1485- The expected and required version number of the 'metadata_role' file
1486- downloaded. 'expected_version' is an integer .
1610+ An integer representing the expected and required version number
1611+ of the 'metadata_role' file downloaded .
14871612
14881613 <Exceptions>
14891614 tuf.exceptions.NoWorkingMirrorError:
@@ -1510,85 +1635,9 @@ def _get_metadata_file(self, metadata_role, remote_filename,
15101635 try :
15111636 file_object = tuf .download .unsafe_download (file_mirror ,
15121637 upperbound_filelength )
1513- file_object .seek (0 )
1514-
1515- # Verify 'file_object' according to the callable function.
1516- # 'file_object' is also verified if decompressed above (i.e., the
1517- # uncompressed version).
1518- metadata_signable = \
1519- securesystemslib .util .load_json_string (file_object .read ().decode ('utf-8' ))
1520-
1521- # Determine if the specification version number is supported. It is
1522- # assumed that "spec_version" is in (major.minor.fix) format, (for
1523- # example: "1.4.3") and that releases with the same major version
1524- # number maintain backwards compatibility. Consequently, if the major
1525- # version number of new metadata equals our expected major version
1526- # number, the new metadata is safe to parse.
1527- try :
1528- metadata_spec_version = metadata_signable ['signed' ]['spec_version' ]
1529- metadata_spec_version_split = metadata_spec_version .split ('.' )
1530- metadata_spec_major_version = int (metadata_spec_version_split [0 ])
1531- metadata_spec_minor_version = int (metadata_spec_version_split [1 ])
1532-
1533- code_spec_version_split = tuf .SPECIFICATION_VERSION .split ('.' )
1534- code_spec_major_version = int (code_spec_version_split [0 ])
1535- code_spec_minor_version = int (code_spec_version_split [1 ])
1536-
1537- if metadata_spec_major_version != code_spec_major_version :
1538- raise tuf .exceptions .UnsupportedSpecificationError (
1539- 'Downloaded metadata that specifies an unsupported '
1540- 'spec_version. This code supports major version number: ' +
1541- repr (code_spec_major_version ) + '; however, the obtained '
1542- 'metadata lists version number: ' + str (metadata_spec_version ))
1543-
1544- #report to user if minor versions do not match, continue with update
1545- if metadata_spec_minor_version != code_spec_minor_version :
1546- logger .info ("Downloaded metadata that specifies a different minor " +
1547- "spec_version. This code has version " +
1548- str (tuf .SPECIFICATION_VERSION ) +
1549- " and the metadata lists version number " +
1550- str (metadata_spec_version ) +
1551- ". The update will continue as the major versions match." )
1552-
1553- except (ValueError , TypeError ) as error :
1554- six .raise_from (securesystemslib .exceptions .FormatError ('Improperly'
1555- ' formatted spec_version, which must be in major.minor.fix format' ),
1556- error )
1557-
1558- # If the version number is unspecified, ensure that the version number
1559- # downloaded is greater than the currently trusted version number for
1560- # 'metadata_role'.
1561- version_downloaded = metadata_signable ['signed' ]['version' ]
1562-
1563- if expected_version is not None :
1564- # Verify that the downloaded version matches the version expected by
1565- # the caller.
1566- if version_downloaded != expected_version :
1567- raise tuf .exceptions .BadVersionNumberError ('Downloaded'
1568- ' version number: ' + repr (version_downloaded ) + '. Version'
1569- ' number MUST be: ' + repr (expected_version ))
1570-
1571- # The caller does not know which version to download. Verify that the
1572- # downloaded version is at least greater than the one locally
1573- # available.
1574- else :
1575- # Verify that the version number of the locally stored
1576- # 'timestamp.json', if available, is less than what was downloaded.
1577- # Otherwise, accept the new timestamp with version number
1578- # 'version_downloaded'.
1579-
1580- try :
1581- current_version = \
1582- self .metadata ['current' ][metadata_role ]['version' ]
1583-
1584- if version_downloaded < current_version :
1585- raise tuf .exceptions .ReplayedMetadataError (metadata_role ,
1586- version_downloaded , current_version )
1587-
1588- except KeyError :
1589- logger .info (metadata_role + ' not available locally.' )
15901638
1591- self ._verify_metadata_file (file_object , metadata_role )
1639+ self ._verify_metadata_file (file_object , metadata_role ,
1640+ expected_version )
15921641
15931642 except Exception as exception :
15941643 # Remember the error from this mirror, and "reset" the target file.
0 commit comments