From 8ac7178ab92c04344b8089960c31515fbf02d2c0 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 2 Jul 2023 22:29:05 +0200 Subject: [PATCH] BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, fixes #7700 --- docs/usage/general/environment.rst.inc | 16 ++++++++++++++++ src/borg/archiver.py | 2 ++ src/borg/crypto/key.py | 22 ++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/docs/usage/general/environment.rst.inc b/docs/usage/general/environment.rst.inc index bf9327d44c..87ad5148f0 100644 --- a/docs/usage/general/environment.rst.inc +++ b/docs/usage/general/environment.rst.inc @@ -100,6 +100,22 @@ General: caused EROFS. You will need this to make archives from volume shadow copies in WSL1 (Windows Subsystem for Linux 1). + authenticated_no_key + Work around a lost passphrase or key for an ``authenticated`` mode repository + (these are only authenticated, but not encrypted). + If the key is missing in the repository config, add ``key = anything`` there. + + This workaround is **only** for emergencies and **only** to extract data + from an affected repository (read-only access):: + + BORG_WORKAROUNDS=authenticated_no_key borg extract repo::archive + + After you have extracted all data you need, you MUST delete the repository:: + + BORG_WORKAROUNDS=authenticated_no_key borg delete repo + + Now you can init a fresh repo. Make sure you do not use the workaround any more. + Some automatic "answerers" (if set, they automatically answer confirmation questions): BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=no (or =yes) For "Warning: Attempting to access a previously unknown unencrypted repository" diff --git a/src/borg/archiver.py b/src/borg/archiver.py index c990c90f0d..14c57cd44f 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -4227,6 +4227,8 @@ def define_borg_mount(parser): If you do **not** want to encrypt the contents of your backups, but still want to detect malicious tampering use ``--encryption authenticated``. + To normally work with ``authenticated`` repos, you will need the passphrase, but + there is an emergency workaround, see ``BORG_WORKAROUNDS=authenticated_no_key`` docs. If ``BLAKE2b`` is faster than ``SHA-256`` on your hardware, use ``--encryption authenticated-blake2``, ``--encryption repokey-blake2`` or ``--encryption keyfile-blake2``. Note: for remote backups diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 7f309e605d..b8ed891816 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -24,6 +24,7 @@ from ..helpers import bin_to_hex from ..helpers import prepare_subprocess_env from ..helpers import msgpack +from ..helpers import workarounds from ..item import Key, EncryptedKey from ..platform import SaveFile @@ -32,6 +33,10 @@ from .low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b +# workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode +AUTHENTICATED_NO_KEY = 'authenticated_no_key' in workarounds + + class NoPassphraseFailure(Error): """can not acquire a passphrase: {}""" @@ -253,6 +258,8 @@ def unpack_and_verify_manifest(self, data, force_tam_not_required=False): offset = data.index(tam_hmac) data[offset:offset + 64] = bytes(64) tam_key = self._tam_key(tam_salt, context=b'manifest') + if AUTHENTICATED_NO_KEY: + return unpacked, True # True is a lie. calculated_hmac = hmac.digest(tam_key, data, 'sha512') if not hmac.compare_digest(calculated_hmac, tam_hmac): raise TAMInvalid() @@ -874,6 +881,19 @@ class AuthenticatedKeyBase(RepoKey): # It's only authenticated, not encrypted. logically_encrypted = False + def _load(self, key_data, passphrase): + if AUTHENTICATED_NO_KEY: + # fake _load if we have no key or passphrase + NOPE = bytes(32) # 256 bit all-zero + self.repository_id = NOPE + self.enc_key = NOPE + self.enc_hmac_key = NOPE + self.id_key = NOPE + self.chunk_seed = 0 + self.tam_required = False + return True + return super()._load(key_data, passphrase) + def load(self, target, passphrase): success = super().load(target, passphrase) self.logically_encrypted = False @@ -899,6 +919,8 @@ def decrypt(self, id, data, decompress=True): if not decompress: return payload data = self.decompress(payload) + if AUTHENTICATED_NO_KEY: + return data self.assert_id(id, data) return data