diff options
-rw-r--r-- | gemato/exceptions.py | 2 | ||||
-rw-r--r-- | gemato/verify.py | 11 | ||||
-rw-r--r-- | tests/test_verify.py | 34 |
3 files changed, 43 insertions, 4 deletions
diff --git a/gemato/exceptions.py b/gemato/exceptions.py index ad9002a..71283ea 100644 --- a/gemato/exceptions.py +++ b/gemato/exceptions.py @@ -291,5 +291,7 @@ class ManifestInsecureHashes(GematoException): self.hashes = hashes def __str__(self): + if not self.hashes: + return "No hashes found when at least one secure hash is required" return ("Some of the requested hashes are considered insecure: " f"{' '.join(self.hashes)}") diff --git a/gemato/verify.py b/gemato/verify.py index 44a639f..1a951c0 100644 --- a/gemato/verify.py +++ b/gemato/verify.py @@ -129,7 +129,8 @@ def get_file_metadata(path, hashes): yield ret -def verify_path(path, e, expected_dev=None, last_mtime=None): +def verify_path(path, e, expected_dev=None, last_mtime=None, + require_secure_hash=False): """ Verify the file at system path @path against the data in entry @e. The path/filename is not matched against the entry -- the correct @@ -149,6 +150,9 @@ def verify_path(path, e, expected_dev=None, last_mtime=None): to the previous file verification. If the file is not newer than that, the checksum verification is skipped. + If @require_secure_hash is True, the file must have at least one + hash that is considered cryptographically secure. + Each name can be: - __exists__ (boolean) to indicate whether the file existed, - __type__ (string) as a human-readable description of file type, @@ -172,6 +176,11 @@ def verify_path(path, e, expected_dev=None, last_mtime=None): checksums = list(filter(is_hash_supported, e.checksums)) if not checksums and e.checksums: raise ManifestNoSupportedHashes(e) + if require_secure_hash: + # Note: even if we require secure hashes, we verify all of them + secure_hashes = list(filter(is_hash_secure, checksums)) + if not secure_hashes: + raise ManifestInsecureHashes(checksums) with contextlib.closing(get_file_metadata(path, checksums)) as g: # 1. verify whether the file existed in the first place diff --git a/tests/test_verify.py b/tests/test_verify.py index 7ee4545..43f2830 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -104,7 +104,12 @@ def get_checksums(path): '__size__': hashes['__size__'], } - +EMPTY_CHECKSUMS = { + "MD5": "d41d8cd98f00b204e9800998ecf8427e", + "SHA1": "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "SHA256": "e3b0c44298fc1c149afbf4c8996fb924" + "27ae41e4649b934ca495991b7852b855", +} TEST_PATH_SIZES = { 'empty-file': 0, 'regular-file': 43, @@ -112,8 +117,8 @@ TEST_PATH_SIZES = { '/proc/version': 0, } TEST_PATH_CHECKSUMS = { - 'empty-file': {'MD5': 'd41d8cd98f00b204e9800998ecf8427e', - 'SHA1': 'da39a3ee5e6b4b0d3255bfef95601890afd80709', + 'empty-file': {'MD5': EMPTY_CHECKSUMS["MD5"], + 'SHA1': EMPTY_CHECKSUMS["SHA1"], '__size__': TEST_PATH_SIZES['empty-file'], }, 'regular-file': {'MD5': '9e107d9d372bb6826bd81d3542a419d6', @@ -466,6 +471,29 @@ def test_insecure_hashes(test_tree, entry_hash, hashes_arg, insecure): @pytest.mark.parametrize( + "entry_hash,insecure", + [("", True), + ("MD5", True), + ("SHA1", True), + ("SHA256", False), + ("MD5 SHA1", True), + ("SHA1 SHA256", False), + ]) +def test_verify_insecure_hashes(test_tree, entry_hash, insecure): + checksums = {} + for h in entry_hash.split(): + checksums[h] = EMPTY_CHECKSUMS[h] + + ctx = (pytest.raises(ManifestInsecureHashes) if insecure + else contextlib.nullcontext()) + with ctx: + verify_path( + test_tree / "empty-file", + new_manifest_entry("DATA", "empty-file", 0, checksums), + require_secure_hash=True) + + +@pytest.mark.parametrize( 'a_cls,a_name,a_args,b_cls,b_name,b_args,expected,diff', [('DATA', 'test', [0, {'MD5': 'd41d8cd98f00b204e9800998ecf8427e'}], 'DATA', 'test', [0, {'MD5': 'd41d8cd98f00b204e9800998ecf8427e'}], |