summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Waldmann <tw@waldmann-edv.de>2023-07-28 15:58:03 +0200
committerThomas Waldmann <tw@waldmann-edv.de>2023-08-30 02:28:57 +0200
commit56da3987111eb80b4ca38ac3e6aaa7953c61d2e3 (patch)
treefc2d2a81b2daa2fc456e2362bad36d71bf4127a3 /src
parent5cd2060345f38f2e0324ab178f847c2f45598b12 (diff)
add tests for archive TAMs
Diffstat (limited to 'src')
-rw-r--r--src/borg/testsuite/archiver/checks.py60
-rw-r--r--src/borg/testsuite/key.py73
2 files changed, 119 insertions, 14 deletions
diff --git a/src/borg/testsuite/archiver/checks.py b/src/borg/testsuite/archiver/checks.py
index 7f3c977ad..766ac8602 100644
--- a/src/borg/testsuite/archiver/checks.py
+++ b/src/borg/testsuite/archiver/checks.py
@@ -8,7 +8,7 @@ import pytest
from ...cache import Cache, LocalCache
from ...constants import * # NOQA
from ...crypto.key import TAMRequiredError
-from ...helpers import Location, get_security_dir, bin_to_hex
+from ...helpers import Location, get_security_dir, bin_to_hex, archive_ts_now
from ...helpers import EXIT_ERROR
from ...helpers import msgpack
from ...manifest import Manifest, MandatoryFeatureUnsupported
@@ -322,7 +322,7 @@ def test_check_cache(archivers, request):
check_cache(archiver)
-# Begin manifest tests
+# Begin manifest TAM tests
def spoof_manifest(repository):
with repository:
manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK)
@@ -380,6 +380,62 @@ def test_not_required(archiver):
cmd(archiver, "rlist")
+# Begin archive TAM tests
+def write_archive_without_tam(repository, archive_name):
+ manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK)
+ archive_data = msgpack.packb(
+ {
+ "version": 2,
+ "name": archive_name,
+ "item_ptrs": [],
+ "command_line": "",
+ "hostname": "",
+ "username": "",
+ "time": archive_ts_now().isoformat(timespec="microseconds"),
+ "size": 0,
+ "nfiles": 0,
+ }
+ )
+ archive_id = manifest.repo_objs.id_hash(archive_data)
+ cdata = manifest.repo_objs.format(archive_id, {}, archive_data)
+ repository.put(archive_id, cdata)
+ manifest.archives[archive_name] = (archive_id, datetime.now())
+ manifest.write()
+ repository.commit(compact=False)
+
+
+def test_check_rebuild_manifest(archiver):
+ cmd(archiver, "rcreate", RK_ENCRYPTION)
+ create_src_archive(archiver, "archive_tam")
+ repository = Repository(archiver.repository_path, exclusive=True)
+ with repository:
+ write_archive_without_tam(repository, "archive_no_tam")
+ repository.delete(Manifest.MANIFEST_ID) # kill manifest, so check has to rebuild it
+ repository.commit(compact=False)
+ cmd(archiver, "check", "--repair")
+ output = cmd(archiver, "rlist", "--format='{name} tam:{tam}{NL}'")
+ assert "archive_tam tam:verified" in output # TAM-verified archive is in rebuilt manifest
+ assert "archive_no_tam" not in output # check got rid of untrusted not TAM-verified archive
+
+
+def test_check_rebuild_refcounts(archiver):
+ cmd(archiver, "rcreate", RK_ENCRYPTION)
+ create_src_archive(archiver, "archive_tam")
+ archive_id_pre_check = cmd(archiver, "rlist", "--format='{name} {id}{NL}'")
+ repository = Repository(archiver.repository_path, exclusive=True)
+ with repository:
+ write_archive_without_tam(repository, "archive_no_tam")
+ output = cmd(archiver, "rlist", "--format='{name} tam:{tam}{NL}'")
+ assert "archive_tam tam:verified" in output # good
+ assert "archive_no_tam tam:none" in output # could be borg < 1.0.9 archive or fake
+ cmd(archiver, "check", "--repair")
+ output = cmd(archiver, "rlist", "--format='{name} tam:{tam}{NL}'")
+ assert "archive_tam tam:verified" in output # TAM-verified archive still there
+ assert "archive_no_tam" not in output # check got rid of untrusted not TAM-verified archive
+ archive_id_post_check = cmd(archiver, "rlist", "--format='{name} {id}{NL}'")
+ assert archive_id_post_check == archive_id_pre_check # rebuild_refcounts didn't change archive_tam archive id
+
+
# Begin Remote Tests
def test_remote_repo_restrict_to_path(remote_archiver):
original_location, repo_path = remote_archiver.repository_location, remote_archiver.repository_path
diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py
index 94a3765c1..73d9df35d 100644
--- a/src/borg/testsuite/key.py
+++ b/src/borg/testsuite/key.py
@@ -11,13 +11,8 @@ from ..crypto.key import AEADKeyBase
from ..crypto.key import AESOCBRepoKey, AESOCBKeyfileKey, CHPORepoKey, CHPOKeyfileKey
from ..crypto.key import Blake2AESOCBRepoKey, Blake2AESOCBKeyfileKey, Blake2CHPORepoKey, Blake2CHPOKeyfileKey
from ..crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256
-from ..crypto.key import (
- TAMRequiredError,
- TAMInvalid,
- TAMUnsupportedSuiteError,
- UnsupportedManifestError,
- UnsupportedKeyFormatError,
-)
+from ..crypto.key import TAMRequiredError, TAMInvalid, TAMUnsupportedSuiteError, ArchiveTAMInvalid
+from ..crypto.key import UnsupportedManifestError, UnsupportedKeyFormatError
from ..crypto.key import identify_key
from ..crypto.low_level import IntegrityError as IntegrityErrorBase
from ..helpers import IntegrityError
@@ -281,6 +276,8 @@ class TestTAM:
blob = msgpack.packb({})
with pytest.raises(TAMRequiredError):
key.unpack_and_verify_manifest(blob)
+ with pytest.raises(TAMRequiredError):
+ key.unpack_and_verify_archive(blob)
def test_missing(self, key):
blob = msgpack.packb({})
@@ -288,11 +285,16 @@ class TestTAM:
unpacked, verified = key.unpack_and_verify_manifest(blob)
assert unpacked == {}
assert not verified
+ unpacked, verified, _ = key.unpack_and_verify_archive(blob)
+ assert unpacked == {}
+ assert not verified
def test_unknown_type_when_required(self, key):
blob = msgpack.packb({"tam": {"type": "HMAC_VOLLBIT"}})
with pytest.raises(TAMUnsupportedSuiteError):
key.unpack_and_verify_manifest(blob)
+ with pytest.raises(TAMUnsupportedSuiteError):
+ key.unpack_and_verify_archive(blob)
def test_unknown_type(self, key):
blob = msgpack.packb({"tam": {"type": "HMAC_VOLLBIT"}})
@@ -300,6 +302,9 @@ class TestTAM:
unpacked, verified = key.unpack_and_verify_manifest(blob)
assert unpacked == {}
assert not verified
+ unpacked, verified, _ = key.unpack_and_verify_archive(blob)
+ assert unpacked == {}
+ assert not verified
@pytest.mark.parametrize(
"tam, exc",
@@ -310,12 +315,26 @@ class TestTAM:
(1234, TAMInvalid),
),
)
- def test_invalid(self, key, tam, exc):
+ def test_invalid_manifest(self, key, tam, exc):
blob = msgpack.packb({"tam": tam})
with pytest.raises(exc):
key.unpack_and_verify_manifest(blob)
@pytest.mark.parametrize(
+ "tam, exc",
+ (
+ ({}, TAMUnsupportedSuiteError),
+ ({"type": b"\xff"}, TAMUnsupportedSuiteError),
+ (None, ArchiveTAMInvalid),
+ (1234, ArchiveTAMInvalid),
+ ),
+ )
+ def test_invalid_archive(self, key, tam, exc):
+ blob = msgpack.packb({"tam": tam})
+ with pytest.raises(exc):
+ key.unpack_and_verify_archive(blob)
+
+ @pytest.mark.parametrize(
"hmac, salt",
(({}, bytes(64)), (bytes(64), {}), (None, bytes(64)), (bytes(64), None)),
ids=["ed-b64", "b64-ed", "n-b64", "b64-n"],
@@ -330,10 +349,12 @@ class TestTAM:
blob = msgpack.packb(data)
with pytest.raises(TAMInvalid):
key.unpack_and_verify_manifest(blob)
+ with pytest.raises(ArchiveTAMInvalid):
+ key.unpack_and_verify_archive(blob)
- def test_round_trip(self, key):
+ def test_round_trip_manifest(self, key):
data = {"foo": "bar"}
- blob = key.pack_and_authenticate_metadata(data)
+ blob = key.pack_and_authenticate_metadata(data, context=b"manifest")
assert blob.startswith(b"\x82")
unpacked = msgpack.unpackb(blob)
@@ -344,10 +365,23 @@ class TestTAM:
assert unpacked["foo"] == "bar"
assert "tam" not in unpacked
+ def test_round_trip_archive(self, key):
+ data = {"foo": "bar"}
+ blob = key.pack_and_authenticate_metadata(data, context=b"archive")
+ assert blob.startswith(b"\x82")
+
+ unpacked = msgpack.unpackb(blob)
+ assert unpacked["tam"]["type"] == "HKDF_HMAC_SHA512"
+
+ unpacked, verified, _ = key.unpack_and_verify_archive(blob)
+ assert verified
+ assert unpacked["foo"] == "bar"
+ assert "tam" not in unpacked
+
@pytest.mark.parametrize("which", ("hmac", "salt"))
- def test_tampered(self, key, which):
+ def test_tampered_manifest(self, key, which):
data = {"foo": "bar"}
- blob = key.pack_and_authenticate_metadata(data)
+ blob = key.pack_and_authenticate_metadata(data, context=b"manifest")
assert blob.startswith(b"\x82")
unpacked = msgpack.unpackb(blob, object_hook=StableDict)
@@ -359,6 +393,21 @@ class TestTAM:
with pytest.raises(TAMInvalid):
key.unpack_and_verify_manifest(blob)
+ @pytest.mark.parametrize("which", ("hmac", "salt"))
+ def test_tampered_archive(self, key, which):
+ data = {"foo": "bar"}
+ blob = key.pack_and_authenticate_metadata(data, context=b"archive")
+ assert blob.startswith(b"\x82")
+
+ unpacked = msgpack.unpackb(blob, object_hook=StableDict)
+ assert len(unpacked["tam"][which]) == 64
+ unpacked["tam"][which] = unpacked["tam"][which][0:32] + bytes(32)
+ assert len(unpacked["tam"][which]) == 64
+ blob = msgpack.packb(unpacked)
+
+ with pytest.raises(ArchiveTAMInvalid):
+ key.unpack_and_verify_archive(blob)
+
def test_decrypt_key_file_unsupported_algorithm():
"""We will add more algorithms in the future. We should raise a helpful error."""