Repository: borgbackup/borg

Issue:
recover data from a repo in 'authenticated' mode without the key/passphrase?
<!--
Thank you for reporting an issue.

*IMPORTANT* -  *before* creating a new issue please look around:
 - Borgbackup documentation: http://borgbackup.readthedocs.io/en/stable/index.html
 - FAQ: https://borgbackup.readthedocs.io/en/stable/faq.html
 and
 - open issues in GitHub tracker: https://github.com/borgbackup/borg/issues
  
If you cannot find a similar problem, then create a new issue.

Please fill in as much of the template as possible.
-->

## Have you checked borgbackup docs, FAQ, and open GitHub issues?

Yes

## Is this a BUG / ISSUE report or a QUESTION?

Question

## Describe the problem you're observing.
So I have this borg archive that was created with --encryption=authenticated (or authenticated-blake2, I can't remember) and I have accessed it before, using the passphrase. But since then I've forgotten it. Now I know the data isn't actually encrypted, so how can I get to it? There's only a post on stackexchange asking this question although it did not have any answers. 



Discussion:
Well, `borg init` tells everybody to have a backup of passphrase **and** key, so it is bad that you did not follow that.

Theoretically, the data could be recovered (as it is not encrypted), but it can not be authenticated any more (and borg would want to do that, as the repo and the chunks in it are of that type) - so guess we have no code yet to actually do that.
How important is the data in the repo?

Resolving patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
--- 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
--- 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
 


Test patch: