From d78ed697ae80736f69fb6c9c0a332fa233e2d2d9 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 4 Jul 2023 00:05:10 +0200 Subject: [PATCH] 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. --- src/borg/archive.py | 8 ++++---- src/borg/archiver.py | 2 +- src/borg/cache.py | 2 +- src/borg/crypto/key.py | 14 ++++++++------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 9e84b2b27..4155d2341 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -490,7 +490,7 @@ class Archive: def _load_meta(self, id): data = self.key.decrypt(id, self.repository.get(id)) # 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 != 1: raise Exception('Unknown archive metadata version') @@ -1819,7 +1819,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 @@ -2065,7 +2065,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 @@ -2088,7 +2088,7 @@ class ArchiveChecker: for previous_item_id in archive.items: mark_as_possibly_superseded(previous_item_id) archive.items = items_buffer.chunks - 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.key.encrypt(data) add_reference(new_archive_id, len(data), len(cdata), cdata) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 20766de80..abf4fbb30 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -1630,7 +1630,7 @@ class Archiver: archive_formatted = format_archive(info) cdata = repository.get(archive_id) data = key.decrypt(archive_id, cdata) - archive, verified = key.unpack_and_verify_archive(data, force_tam_not_required=True) + archive, verified, _ = key.unpack_and_verify_archive(data, force_tam_not_required=True) if not verified: # we do not have an archive TAM yet -> add TAM now! archive = ArchiveItem(internal_dict=archive) archive.cmdline = [safe_decode(arg) for arg in archive.cmdline] diff --git a/src/borg/cache.py b/src/borg/cache.py index b650528c7..523aedf93 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -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), csize) - 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 != 1: raise Exception('Unknown archive metadata version') diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 48789878c..c1ff76b1d 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -226,15 +226,17 @@ 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), + '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) @@ -300,7 +302,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(b'tam', None) if not isinstance(tam, dict): raise ArchiveTAMInvalid() @@ -310,7 +312,7 @@ class KeyBase: raise TAMUnsupportedSuiteError(repr(tam_type)) else: 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(b'hmac') tam_salt = tam.get(b'salt') if not isinstance(tam_salt, bytes) or not isinstance(tam_hmac, bytes): @@ -322,7 +324,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):