From 30514731685d2af8dee240e9e93182719a296bda 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/rcreate_cmd.py | 2 ++ src/borg/crypto/key.py | 19 +++++++++++++++++++ src/borg/repoobj.py | 11 ++++++++--- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/docs/usage/general/environment.rst.inc b/docs/usage/general/environment.rst.inc index 0ac909284..6d1ccfaac 100644 --- a/docs/usage/general/environment.rst.inc +++ b/docs/usage/general/environment.rst.inc @@ -104,6 +104,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. + Output formatting: BORG_LIST_FORMAT Giving the default value for ``borg list --format=X``. diff --git a/src/borg/archiver/rcreate_cmd.py b/src/borg/archiver/rcreate_cmd.py index 9e0504a01..a3c0ea150 100644 --- a/src/borg/archiver/rcreate_cmd.py +++ b/src/borg/archiver/rcreate_cmd.py @@ -152,6 +152,8 @@ def build_parser_rcreate(self, subparsers, common_parser, mid_common_parser): If you do **not** want to encrypt the contents of your backups, but still want to detect malicious tampering use an `authenticated` mode. It's like `repokey` minus encryption. + To normally work with ``authenticated`` repos, you will need the passphrase, but + there is an emergency workaround, see ``BORG_WORKAROUNDS=authenticated_no_key`` docs. Creating a related repository +++++++++++++++++++++++++++++ diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 9386805b0..a45c6c795 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -20,6 +20,7 @@ from ..helpers import bin_to_hex from ..helpers.passphrase import Passphrase, PasswordRetriesExceeded, PassphraseWrong from ..helpers import msgpack +from ..helpers import workarounds from ..item import Key, EncryptedKey, want_bytes from ..manifest import Manifest from ..platform import SaveFile @@ -30,6 +31,9 @@ from .low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b, AES256_OCB, CHACHA20_POLY1305 from . import low_level +# workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode +AUTHENTICATED_NO_KEY = "authenticated_no_key" in workarounds + class UnsupportedPayloadError(Error): """Unsupported payload type {}. A newer version is required to access this repository.""" @@ -267,6 +271,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() @@ -800,6 +806,19 @@ class AuthenticatedKeyBase(AESKeyBase, FlexiKey): # 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 diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py index edfa070ae..557d4a604 100644 --- a/src/borg/repoobj.py +++ b/src/borg/repoobj.py @@ -1,8 +1,11 @@ from struct import Struct -from .helpers import msgpack +from .helpers import msgpack, workarounds from .compress import Compressor, LZ4_COMPRESSOR, get_compressor +# workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode +AUTHENTICATED_NO_KEY = "authenticated_no_key" in workarounds + class RepoObj: meta_len_hdr = Struct("