Skip to content

Commit

Permalink
Add an option to create TargetFile from data/file
Browse files Browse the repository at this point in the history
This is a repository tooling use case but also helpful when testing.
It could be useful when we need to update the targets object.

Signed-off-by: Velichka Atanasova <avelichka@vmware.com>
  • Loading branch information
avelichka committed Aug 27, 2021
1 parent 98cc149 commit af22a7a
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 0 deletions.
40 changes: 40 additions & 0 deletions tests/test_api.py
Expand Up @@ -50,6 +50,7 @@
import_ed25519_privatekey_from_file
)

from securesystemslib import hash as sslib_hash
from securesystemslib.signer import (
SSlibSigner,
Signature
Expand Down Expand Up @@ -623,6 +624,45 @@ def test_length_and_hash_validation(self):
file1_targetfile.verify_length_and_hashes, file1)


def test_targetfile_from_data(self):
data = b"Inline test content"

# Test with valid hash algorithm(s) specified
targetfile_from_data = TargetFile.from_data(data, ['sha256'])
targetfile_from_data.verify_length_and_hashes(data)

# Test with no algorithms specified
targetfile_from_data = TargetFile.from_data(data, [])
targetfile_from_data.verify_length_and_hashes(data)

# Test with an unsupported algorithm
self.assertRaises(
exceptions.UnsupportedAlgorithmError,
TargetFile.from_data,
data,
['123']
)


def test_targetfile_from_file(self):
# Test with an existing file
filepath = os.path.join(self.repo_dir, 'targets', 'file1.txt')
targetfile_from_file = TargetFile.from_file(
filepath, [sslib_hash.DEFAULT_HASH_ALGORITHM]
)

with open(filepath, "rb") as file:
targetfile_from_file.verify_length_and_hashes(file)

# Test with a non-existing file
filepath = os.path.join(self.repo_dir, 'targets', 'file123.txt')
self.assertRaises(
FileNotFoundError,
TargetFile.from_file,
filepath,
[sslib_hash.DEFAULT_HASH_ALGORITHM]
)

# Run unit test.
if __name__ == '__main__':
utils.configure_test_logging(sys.argv)
Expand Down
68 changes: 68 additions & 0 deletions tuf/api/metadata.py
Expand Up @@ -1123,6 +1123,74 @@ def to_dict(self) -> Dict[str, Any]:
**self.unrecognized_fields,
}

@classmethod
def from_file(
cls, filepath: str, hash_algorithms: List[str]
) -> "TargetFile":
"""Creates TargetFile object from a file.
Attributes:
filepath: The path to the file to create the TaretFile object from.
hash_algorithms: The hash algorithms to create the hashes with.
If empty the securesystemslib default hash algorithm is used.
Raises:
FileNotFoundError: The file doesn't exist.
"""
with open(filepath, "rb") as file:
return cls.from_data(file, hash_algorithms)

@classmethod
def from_data(
cls, data: Union[bytes, BinaryIO], hash_algorithms: List[str]
) -> "TargetFile":
"""Creates TargetFile object from data.
Attributes:
data: The data to create the TargetFile object from.
hash_algorithms: The hash algorithms to create the hashes with.
If empty the securesystemslib default hash algorithm is used.
Raises:
UnsupportedAlgorithmError: The hash algorithms list
contains an unsupported algorithm.
"""
# Calculate the length
is_bytes = isinstance(data, bytes)
if is_bytes:
length = len(data)
else:
# If data is not bytes, assume it is a file object.
data.seek(0, io.SEEK_END)
length = data.tell()

# Calculate the hashes for the given algorithms.
if len(hash_algorithms) == 0:
hash_algorithms.append(sslib_hash.DEFAULT_HASH_ALGORITHM)

hashes = {}
for algorithm in hash_algorithms:
try:
if is_bytes:
digest_object = sslib_hash.digest(algorithm)
digest_object.update(data)
else:
# if data is not bytes, assume it is a file object
digest_object = sslib_hash.digest_fileobject(
data, algorithm
)
except (
sslib_exceptions.UnsupportedAlgorithmError,
sslib_exceptions.FormatError,
) as e:
raise exceptions.UnsupportedAlgorithmError(
f"Unsupported algorithm '{algorithm}'"
) from e

hashes[algorithm] = digest_object.hexdigest()

return cls(length, hashes)

def verify_length_and_hashes(self, data: Union[bytes, BinaryIO]) -> None:
"""Verifies that length and hashes of "data" match expected values.
Expand Down

0 comments on commit af22a7a

Please sign in to comment.