diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index a83bb99f..042ffa01 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -47,10 +47,7 @@ SignedSerializer, ) -# Disable the "C0302: Too many lines in module" warning which warns for modules -# with more 1000 lines, because all of the code here is logically connected -# and currently, we are above 1000 lines by a small margin. -# pylint: disable=C0302 +# pylint: disable=too-many-lines # We aim to support SPECIFICATION_VERSION and require the input metadata # files to have the same major version (the first number) as ours. @@ -66,7 +63,6 @@ class Metadata: Attributes: signed: A subclass of Signed, which has the actual metadata payload, i.e. one of Targets, Snapshot, Timestamp or Root. - signatures: An ordered dictionary of keyids to Signature objects, each signing the canonical serialized representation of 'signed'. """ @@ -93,8 +89,8 @@ def from_dict(cls, metadata: Dict[str, Any]) -> "Metadata": Returns: A TUF Metadata object. - """ + # Dispatch to contained metadata class on metadata _type field. _type = metadata["signed"]["_type"] @@ -149,8 +145,8 @@ def from_file( Returns: A TUF Metadata object. - """ + if storage_backend is None: storage_backend = FilesystemBackend() @@ -203,20 +199,18 @@ def to_file( Arguments: filename: The path to write the file to. - serializer: A MetadataSerializer subclass instance that implements - the desired wireline format serialization. Per default a - JSONSerializer is used. - storage_backend: An object that implements - securesystemslib.storage.StorageBackendInterface. Per default - a (local) FilesystemBackend is used. + serializer: A MetadataSerializer instance that implements the + desired serialization format. Default is JSONSerializer. + storage_backend: A StorageBackendInterface implementation. Default + is FilesystemBackend (i.e. a local file). Raises: tuf.api.serialization.SerializationError: The metadata object cannot be serialized. securesystemslib.exceptions.StorageError: The file cannot be written. - """ + if serializer is None: # Use local scope import to avoid circular import errors # pylint: disable=import-outside-toplevel @@ -238,14 +232,12 @@ def sign( """Creates signature over 'signed' and assigns it to 'signatures'. Arguments: - signer: An object implementing the securesystemslib.signer.Signer - interface. + signer: A securesystemslib.signer.Signer implementation. append: A boolean indicating if the signature should be appended to the list of signatures or replace any existing signatures. The default behavior is to replace signatures. - signed_serializer: A SignedSerializer subclass instance that - implements the desired canonicalization format. Per default a - CanonicalJSONSerializer is used. + signed_serializer: A SignedSerializer that implements the desired + serialization format. Default is CanonicalJSONSerializer. Raises: tuf.api.serialization.SerializationError: @@ -256,8 +248,8 @@ def sign( Returns: A securesystemslib-style signature object. - """ + if signed_serializer is None: # Use local scope import to avoid circular import errors # pylint: disable=import-outside-toplevel @@ -408,8 +400,9 @@ def bump_version(self) -> None: class Key: """A container class representing the public portion of a Key. + Please note that "Key" instances are not semanticly validated during - initialization. We consider this as responsibility of securesystemslib. + initialization: this only happens at signature verification time. Attributes: keyid: An identifier string that must uniquely identify a key within @@ -421,7 +414,6 @@ class Key: "rsassa-pss-sha256", "ed25519", and "ecdsa-sha2-nistp256". keyval: A dictionary containing the public portion of the key. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -521,15 +513,15 @@ def verify_signature( class Role: - """A container class containing the set of keyids and threshold associated - with a particular role. + """Container that defines which keys are required to sign roles metadata. + + Role defines how many keys are required to successfully sign the roles + metadata, and which keys are accepted. Attributes: - keyids: A set of strings each of which represents a given key. - threshold: An integer representing the required number of keys for that - particular role. + keyids: A set of strings representing signing keys for this role. + threshold: Number of keys required to sign this role's metadata. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -573,29 +565,14 @@ class Root(Signed): Attributes: consistent_snapshot: An optional boolean indicating whether the repository supports consistent snapshots. - keys: A dictionary that contains a public key store used to verify - top level roles metadata signatures:: - - { - '': , - ... - }, - - roles: A dictionary that contains a list of signing keyids and - a signature threshold for each top level role:: - - { - '': , - ... - } - + keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'. + roles: Dictionary of role names to Roles. Defines which keys are + required to sign the metadata for a specific role. """ _signed_type = "root" - # TODO: determine an appropriate value for max-args and fix places where - # we violate that. This __init__ function takes 7 arguments, whereas the - # default max-args value for pylint is 5 + # TODO: determine an appropriate value for max-args # pylint: disable=too-many-arguments def __init__( self, @@ -739,18 +716,8 @@ class MetaFile(BaseFile): Attributes: version: An integer indicating the version of the metadata file. length: An optional integer indicating the length of the metadata file. - hashes: An optional dictionary mapping hash algorithms to the - hashes resulting from applying them over the metadata file - contents.:: - - 'hashes': { - '': '', - '': '', - ... - } - + hashes: An optional dictionary of hash algorithm names to hash values. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -799,10 +766,11 @@ def to_dict(self) -> Dict[str, Any]: return res_dict def verify_length_and_hashes(self, data: Union[bytes, BinaryIO]): - """Verifies that the length and hashes of "data" match expected - values. + """Verifies that the length and hashes of "data" match expected values. + Args: data: File object or its content in bytes. + Raises: LengthOrHashMismatchError: Calculated length or hashes do not match expected values or hash algorithm is not supported. @@ -817,13 +785,11 @@ def verify_length_and_hashes(self, data: Union[bytes, BinaryIO]): class Timestamp(Signed): """A container for the signed part of timestamp metadata. + Timestamp contains information about the snapshot Metadata file. + Attributes: - meta: A dictionary that contains information about snapshot metadata:: - - { - 'snapshot.json': - } - + meta: A dictionary of filenames to MetaFiles. The only valid key value + is the snapshot filename, as defined by the specification. """ _signed_type = "timestamp" @@ -865,15 +831,10 @@ def update(self, snapshot_meta: MetaFile) -> None: class Snapshot(Signed): """A container for the signed part of snapshot metadata. + Snapshot contains information about all target Metadata files. + Attributes: - meta: A dictionary that contains information about targets metadata:: - - { - 'targets.json': , - '.json': , - '.json': , - } - + meta: A dictionary of target metadata filenames to MetaFile objects. """ _signed_type = "snapshot" @@ -918,22 +879,22 @@ def update(self, rolename: str, role_info: MetaFile) -> None: class DelegatedRole(Role): - """A container with information about particular delegated role. + """A container with information about a delegated role. + + A delegation can happen in three ways: + - paths is None and path_hash_prefixes is None: delegates all targets + - paths is set: delegates targets matching any path pattern in paths + - path_hash_prefixes is set: delegates targets whose target path hash + starts with any of the prefixes in path_hash_prefixes + paths and path_hash_prefixes are mutually exclusive: both cannot be set. Attributes: name: A string giving the name of the delegated role. - keyids: A set of strings each of which represents a given key. - threshold: An integer representing the required number of keys for that - particular role. terminating: A boolean indicating whether subsequent delegations - should be considered. - paths: An optional list of strings, where each string describes - a path that the role is trusted to provide. - path_hash_prefixes: An optional list of HEX_DIGESTs used to succinctly - describe a set of target paths. Only one of the attributes "paths" - and "path_hash_prefixes" is allowed to be set. + should be considered during a target lookup. + paths: An optional list of path pattern strings. See note above. + path_hash_prefixes: An optional list of hash prefixes. See note above. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -996,12 +957,11 @@ class Delegations: """A container object storing information about all delegations. Attributes: - keys: A dictionary of keyids and key objects containing information - about the corresponding key. - roles: A list of DelegatedRole instances containing information about - all delegated roles. + keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'. + roles: List of DelegatedRoles that define which keys are required to + sign the metadata for a specific role. The roles order also + defines the order that role delegations are considered in. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -1045,17 +1005,8 @@ class TargetFile(BaseFile): Attributes: length: An integer indicating the length of the target file. - hashes: A dictionary mapping hash algorithms to the - hashes resulting from applying them over the metadata file - contents:: - - 'hashes': { - '': '', - '': '', - ... - } + hashes: A dictionary of hash algorithm names to hash values. unrecognized_fields: Dictionary of all unrecognized fields. - """ def __init__( @@ -1096,10 +1047,11 @@ def to_dict(self) -> Dict[str, Any]: } def verify_length_and_hashes(self, data: Union[bytes, BinaryIO]): - """Verifies that the length and hashes of "data" match expected - values. + """Verifies that length and hashes of "data" match expected values. + Args: data: File object or its content in bytes. + Raises: LengthOrHashMismatchError: Calculated length or hashes do not match expected values or hash algorithm is not supported. @@ -1111,25 +1063,18 @@ def verify_length_and_hashes(self, data: Union[bytes, BinaryIO]): class Targets(Signed): """A container for the signed part of targets metadata. + Targets contains verifying information about target files and also + delegates responsibility to other Targets roles. + Attributes: - targets: A dictionary that contains information about target files:: - - { - '': , - ... - } - - delegations: An optional object containing a list of delegated target - roles and public key store used to verify their metadata - signatures. - + targets: A dictionary of target filenames to TargetFiles + delegations: An optional Delegations that defines how this Targets + further delegates responsibility to other Targets Metadata files. """ _signed_type = "targets" - # TODO: determine an appropriate value for max-args and fix places where - # we violate that. This __init__ function takes 7 arguments, whereas the - # default max-args value for pylint is 5 + # TODO: determine an appropriate value for max-args # pylint: disable=too-many-arguments def __init__( self,