rebuild_refcounts: keep archive ID, if possible

rebuild_refcounts verifies and recreates the TAM.
Now it re-uses the salt, so that the archive ID does not change
just because of a new salt if the archive has still the same data.
This commit is contained in:
Thomas Waldmann 2023-07-04 00:05:10 +02:00
parent 95b5604422
commit 5cd2060345
No known key found for this signature in database
GPG Key ID: 243ACFA951F78E01
3 changed files with 13 additions and 11 deletions

View File

@ -534,7 +534,7 @@ class Archive:
cdata = self.repository.get(id)
_, data = self.repo_objs.parse(id, cdata)
# we do not require TAM for archives, otherwise we can not even borg list a repo with old archives.
archive, self.tam_verified = self.key.unpack_and_verify_archive(data, force_tam_not_required=True)
archive, self.tam_verified, _ = self.key.unpack_and_verify_archive(data, force_tam_not_required=True)
metadata = ArchiveItem(internal_dict=archive)
if metadata.version not in (1, 2): # legacy: still need to read v1 archives
raise Exception("Unknown archive metadata version")
@ -1998,7 +1998,7 @@ class ArchiveChecker:
# **after** doing the low-level checks and having a strong indication that we
# are likely looking at an archive item here, also check the TAM authentication:
try:
archive, verified = self.key.unpack_and_verify_archive(data, force_tam_not_required=False)
archive, verified, _ = self.key.unpack_and_verify_archive(data, force_tam_not_required=False)
except IntegrityError:
# TAM issues - do not accept this archive!
# either somebody is trying to attack us with a fake archive data or
@ -2265,7 +2265,7 @@ class ArchiveChecker:
del self.manifest.archives[info.name]
continue
try:
archive, verified = self.key.unpack_and_verify_archive(data, force_tam_not_required=False)
archive, verified, salt = self.key.unpack_and_verify_archive(data, force_tam_not_required=False)
except IntegrityError as integrity_error:
# looks like there is a TAM issue with this archive, this might be an attack!
# when upgrading to borg 1.2.5, users are expected to TAM-authenticate all archives they
@ -2293,7 +2293,7 @@ class ArchiveChecker:
archive.item_ptrs = archive_put_items(
items_buffer.chunks, repo_objs=self.repo_objs, add_reference=add_reference
)
data = self.key.pack_and_authenticate_metadata(archive.as_dict(), context=b"archive")
data = self.key.pack_and_authenticate_metadata(archive.as_dict(), context=b"archive", salt=salt)
new_archive_id = self.key.id_hash(data)
cdata = self.repo_objs.format(new_archive_id, {}, data)
add_reference(new_archive_id, len(data), cdata)

View File

@ -755,7 +755,7 @@ class LocalCache(CacheStatsMixin):
nonlocal processed_item_metadata_chunks
csize, data = decrypted_repository.get(archive_id)
chunk_idx.add(archive_id, 1, len(data))
archive, verified = self.key.unpack_and_verify_archive(data, force_tam_not_required=True)
archive, verified, _ = self.key.unpack_and_verify_archive(data, force_tam_not_required=True)
archive = ArchiveItem(internal_dict=archive)
if archive.version not in (1, 2): # legacy
raise Exception("Unknown archive metadata version")

View File

@ -243,11 +243,13 @@ class KeyBase:
output_length=64,
)
def pack_and_authenticate_metadata(self, metadata_dict, context=b"manifest"):
def pack_and_authenticate_metadata(self, metadata_dict, context=b"manifest", salt=None):
if salt is None:
salt = os.urandom(64)
metadata_dict = StableDict(metadata_dict)
tam = metadata_dict["tam"] = StableDict({"type": "HKDF_HMAC_SHA512", "hmac": bytes(64), "salt": os.urandom(64)})
tam = metadata_dict["tam"] = StableDict({"type": "HKDF_HMAC_SHA512", "hmac": bytes(64), "salt": salt})
packed = msgpack.packb(metadata_dict)
tam_key = self._tam_key(tam["salt"], context)
tam_key = self._tam_key(salt, context)
tam["hmac"] = hmac.digest(tam_key, packed, "sha512")
return msgpack.packb(metadata_dict)
@ -317,7 +319,7 @@ class KeyBase:
raise ArchiveTAMRequiredError(archive_name)
else:
logger.debug("Archive TAM not found and not required")
return unpacked, False
return unpacked, False, None
tam = unpacked.pop("tam", None)
if not isinstance(tam, dict):
raise ArchiveTAMInvalid()
@ -329,7 +331,7 @@ class KeyBase:
logger.debug(
"Ignoring archive TAM made with unsupported suite, since TAM is not required: %r", tam_type
)
return unpacked, False
return unpacked, False, None
tam_hmac = tam.get("hmac")
tam_salt = tam.get("salt")
if not isinstance(tam_salt, (bytes, str)) or not isinstance(tam_hmac, (bytes, str)):
@ -343,7 +345,7 @@ class KeyBase:
if not hmac.compare_digest(calculated_hmac, tam_hmac):
raise ArchiveTAMInvalid()
logger.debug("TAM-verified archive")
return unpacked, True
return unpacked, True, tam_salt
class PlaintextKey(KeyBase):