From 53830ecae9fe306eac29222fd5ed837549906ab3 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 29 Jul 2022 09:08:03 +0200 Subject: [PATCH] check: try harder to create the key, fixes #5719 the old code did just 1 attempt to detect the repo decryption key. if the first chunkid we got from the chunks hashtable iterator was accidentally the id of the chunk we intentionally corrupted in test_delete_double_force, setup of the key failed and that made the test crash. in practice, this could of course also happen if chunks are corrupted, thus we now do many retries with other chunks before giving up. error handling was improved: do not return None (instead of a key), it just leads to weird crashes elsewhere, but fail early with IntegrityError and a reasonable error msg. rename method to make_key to avoid confusion with borg.crypto.key.identify_key. --- src/borg/archive.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 7c6b832b0..481017f54 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -21,7 +21,7 @@ logger = create_logger() from . import xattr from .chunker import get_chunker, Chunk from .cache import ChunkListEntry -from .crypto.key import key_factory, AEADKeyBase +from .crypto.key import key_factory, UnsupportedPayloadError, AEADKeyBase from .compress import Compressor, CompressionSpec from .constants import * # NOQA from .crypto.low_level import IntegrityError as IntegrityErrorBase @@ -1630,7 +1630,7 @@ class ArchiveChecker: if not self.chunks: logger.error("Repository contains no apparent data at all, cannot continue check/repair.") return False - self.key = self.identify_key(repository) + self.key = self.make_key(repository) if verify_data: self.verify_data() if Manifest.MANIFEST_ID not in self.chunks: @@ -1670,14 +1670,24 @@ class ArchiveChecker: for id_ in result: self.chunks[id_] = init_entry - def identify_key(self, repository): - try: - some_chunkid, _ = next(self.chunks.iteritems()) - except StopIteration: - # repo is completely empty, no chunks - return None - cdata = repository.get(some_chunkid) - return key_factory(repository, cdata) + def make_key(self, repository): + attempt = 0 + for chunkid, _ in self.chunks.iteritems(): + attempt += 1 + if attempt > 999: + # we did a lot of attempts, but could not create the key via key_factory, give up. + break + cdata = repository.get(chunkid) + try: + return key_factory(repository, cdata) + except UnsupportedPayloadError: + # we get here, if the cdata we got has a corrupted key type byte + pass # ignore it, just try the next chunk + if attempt == 0: + msg = "make_key: repository has no chunks at all!" + else: + msg = "make_key: failed to create the key (tried %d chunks)" % attempt + raise IntegrityError(msg) def verify_data(self): logger.info("Starting cryptographic data integrity verification...")