88
99import abc
1010import fnmatch
11+ import hashlib
1112import io
1213import logging
14+ import sys
1315from dataclasses import dataclass
1416from datetime import datetime , timezone
1517from typing import (
2123)
2224
2325from securesystemslib import exceptions as sslib_exceptions
24- from securesystemslib import hash as sslib_hash
2526from securesystemslib .signer import Key , Signature
2627
2728from tuf .api .exceptions import LengthOrHashMismatchError , UnsignedMetadataError
3435_TARGETS = "targets"
3536_TIMESTAMP = "timestamp"
3637
38+ _DEFAULT_HASH_ALGORITHM = "sha256"
39+ _BLAKE_HASH_ALGORITHM = "blake2b-256"
40+
3741# We aim to support SPECIFICATION_VERSION and require the input metadata
3842# files to have the same major version (the first number) as ours.
3943SPECIFICATION_VERSION = ["1" , "0" , "31" ]
4549T = TypeVar ("T" , "Root" , "Timestamp" , "Snapshot" , "Targets" )
4650
4751
52+ def _get_digest (algo : str ) -> Any : # noqa: ANN401
53+ """New digest helper to support custom "blake2b-256" algo name."""
54+ if algo == _BLAKE_HASH_ALGORITHM :
55+ return hashlib .blake2b (digest_size = 32 )
56+
57+ return hashlib .new (algo )
58+
59+
60+ def _hash_bytes (data : bytes , algo : str ) -> str :
61+ """Returns hexdigest for data using algo."""
62+ digest = _get_digest (algo )
63+ digest .update (data )
64+
65+ return digest .hexdigest ()
66+
67+
68+ def _hash_file (f : IO [bytes ], algo : str ) -> str :
69+ """Returns hexdigest for file using algo."""
70+ f .seek (0 )
71+ if sys .version_info >= (3 , 11 ):
72+ digest = hashlib .file_digest (f , lambda : _get_digest (algo )) # type: ignore[arg-type]
73+
74+ else :
75+ # Fallback for older Pythons. Chunk size is taken from the previously
76+ # used and now deprecated `securesystemslib.hash.digest_fileobject`.
77+ digest = _get_digest (algo )
78+ for chunk in iter (lambda : f .read (4096 ), b"" ):
79+ digest .update (chunk )
80+
81+ return digest .hexdigest ()
82+
83+
4884class Signed (metaclass = abc .ABCMeta ):
4985 """A base class for the signed part of TUF metadata.
5086
@@ -664,24 +700,18 @@ def _verify_hashes(
664700 data : bytes | IO [bytes ], expected_hashes : dict [str , str ]
665701 ) -> None :
666702 """Verify that the hash of ``data`` matches ``expected_hashes``."""
667- is_bytes = isinstance (data , bytes )
668703 for algo , exp_hash in expected_hashes .items ():
669704 try :
670- if is_bytes :
671- digest_object = sslib_hash .digest (algo )
672- digest_object .update (data )
705+ if isinstance (data , bytes ):
706+ observed_hash = _hash_bytes (data , algo )
673707 else :
674708 # if data is not bytes, assume it is a file object
675- digest_object = sslib_hash .digest_fileobject (data , algo )
676- except (
677- sslib_exceptions .UnsupportedAlgorithmError ,
678- sslib_exceptions .FormatError ,
679- ) as e :
709+ observed_hash = _hash_file (data , algo )
710+ except (ValueError , TypeError ) as e :
680711 raise LengthOrHashMismatchError (
681712 f"Unsupported algorithm '{ algo } '"
682713 ) from e
683714
684- observed_hash = digest_object .hexdigest ()
685715 if observed_hash != exp_hash :
686716 raise LengthOrHashMismatchError (
687717 f"Observed hash { observed_hash } does not match "
@@ -731,25 +761,17 @@ def _get_length_and_hashes(
731761 hashes = {}
732762
733763 if hash_algorithms is None :
734- hash_algorithms = [sslib_hash . DEFAULT_HASH_ALGORITHM ]
764+ hash_algorithms = [_DEFAULT_HASH_ALGORITHM ]
735765
736766 for algorithm in hash_algorithms :
737767 try :
738768 if isinstance (data , bytes ):
739- digest_object = sslib_hash .digest (algorithm )
740- digest_object .update (data )
769+ hashes [algorithm ] = _hash_bytes (data , algorithm )
741770 else :
742- digest_object = sslib_hash .digest_fileobject (
743- data , algorithm
744- )
745- except (
746- sslib_exceptions .UnsupportedAlgorithmError ,
747- sslib_exceptions .FormatError ,
748- ) as e :
771+ hashes [algorithm ] = _hash_file (data , algorithm )
772+ except (ValueError , TypeError ) as e :
749773 raise ValueError (f"Unsupported algorithm '{ algorithm } '" ) from e
750774
751- hashes [algorithm ] = digest_object .hexdigest ()
752-
753775 return (length , hashes )
754776
755777
@@ -832,7 +854,7 @@ def from_data(
832854 version: Version of the metadata file.
833855 data: Metadata bytes that the metafile represents.
834856 hash_algorithms: Hash algorithms to create the hashes with. If not
835- specified, the securesystemslib default hash algorithm is used.
857+ specified, "sha256" is used.
836858
837859 Raises:
838860 ValueError: The hash algorithms list contains an unsupported
@@ -1150,7 +1172,7 @@ def is_delegated_path(self, target_filepath: str) -> bool:
11501172 if self .path_hash_prefixes is not None :
11511173 # Calculate the hash of the filepath
11521174 # to determine in which bin to find the target.
1153- digest_object = sslib_hash . digest ( algorithm = "sha256" )
1175+ digest_object = hashlib . new ( name = "sha256" )
11541176 digest_object .update (target_filepath .encode ("utf-8" ))
11551177 target_filepath_hash = digest_object .hexdigest ()
11561178
@@ -1269,7 +1291,7 @@ def get_role_for_target(self, target_filepath: str) -> str:
12691291 target_filepath: URL path to a target file, relative to a base
12701292 targets URL.
12711293 """
1272- hasher = sslib_hash . digest ( algorithm = "sha256" )
1294+ hasher = hashlib . new ( name = "sha256" )
12731295 hasher .update (target_filepath .encode ("utf-8" ))
12741296
12751297 # We can't ever need more than 4 bytes (32 bits).
@@ -1542,7 +1564,7 @@ def from_file(
15421564 targets URL.
15431565 local_path: Local path to target file content.
15441566 hash_algorithms: Hash algorithms to calculate hashes with. If not
1545- specified the securesystemslib default hash algorithm is used.
1567+ specified, "sha256" is used.
15461568
15471569 Raises:
15481570 FileNotFoundError: The file doesn't exist.
@@ -1566,7 +1588,7 @@ def from_data(
15661588 targets URL.
15671589 data: Target file content.
15681590 hash_algorithms: Hash algorithms to create the hashes with. If not
1569- specified the securesystemslib default hash algorithm is used.
1591+ specified, "sha256" is used.
15701592
15711593 Raises:
15721594 ValueError: The hash algorithms list contains an unsupported
0 commit comments