浏览代码

BORG_WORKAROUNDS=authenticated_no_key to extract from authenticated repos without key, fixes #7700

Thomas Waldmann 1 年之前
父节点
当前提交
8ac7178ab9
共有 3 个文件被更改,包括 40 次插入0 次删除
  1. 16 0
      docs/usage/general/environment.rst.inc
  2. 2 0
      src/borg/archiver.py
  3. 22 0
      src/borg/crypto/key.py

+ 16 - 0
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"

+ 2 - 0
src/borg/archiver.py

@@ -4227,6 +4227,8 @@ class Archiver:
 
         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

+ 22 - 0
src/borg/crypto/key.py

@@ -24,6 +24,7 @@ from ..helpers import get_limited_unpacker
 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 AES, bytes_to_long, long_to_bytes, bytes_to_int, num_ciph
 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 @@ class KeyBase:
         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 @@ class AuthenticatedKeyBase(RepoKey):
         if not decompress:
             return payload
         data = self.decompress(payload)
+        if AUTHENTICATED_NO_KEY:
+            return data
         self.assert_id(id, data)
         return data