Repository: borgbackup/borg

Issue:
invalid repository
if an old borg detects a repo version > 1 it just says "invalid repository".

it could be a bit more helpful and e.g. tell "you need a new borg release to support repo version X".

Discussion:

Patch:
diff --git a/docs/usage/init.rst.inc b/docs/usage/init.rst.inc
index bfcfc40e..5bf1cb64 100644
--- a/docs/usage/init.rst.inc
+++ b/docs/usage/init.rst.inc
@@ -134,27 +134,27 @@ in the upper part of the table, in the lower part is the old and/or unsafe(r) st
 
 .. nanorst: inline-fill
 
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| Mode (K = keyfile or repokey)     | ID-Hash      | Encryption     | Authentication     | V >=    |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2-chacha20-poly1305        | BLAKE2b      | CHACHA20       | POLY1305           | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-chacha20-poly1305               | HMAC-SHA-256 | CHACHA20       | POLY1305           | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2-aes-ocb                  | BLAKE2b      | AES256-OCB     | AES256-OCB         | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-aes-ocb                         | HMAC-SHA-256 | AES256-OCB     | AES256-OCB         | 1.3     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K-blake2                          | BLAKE2b      | AES256-CTR     | BLAKE2b            | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| K                                 | HMAC-SHA-256 | AES256-CTR     | HMAC-SHA256        | any     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| authenticated-blake2              | BLAKE2b      | none           | BLAKE2b            | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| authenticated                     | HMAC-SHA-256 | none           | HMAC-SHA256        | 1.1     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
-| none                              | SHA-256      | none           | none               | any     |
-+-----------------------------------+--------------+----------------+--------------------+---------+
++---------------------------------+---------------+---------------+------------------+-------+
+|**mode (* = keyfile or repokey)**|**ID-Hash**    |**Encryption** |**Authentication**|**V>=**|
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2-chacha20-poly1305``  | BLAKE2b       | CHACHA20      | POLY1305         | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-chacha20-poly1305``         | HMAC-SHA-256  | CHACHA20      | POLY1305         | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2-aes-ocb``            | BLAKE2b       | AES256-OCB    | AES256-OCB       | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-aes-ocb``                   | HMAC-SHA-256  | AES256-OCB    | AES256-OCB       | 1.3   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*-blake2``                    | BLAKE2b       | AES256-CTR    | BLAKE2b          | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| ``*``                           | HMAC-SHA-256  | AES256-CTR    | HMAC-SHA256      | any   |
++---------------------------------+---------------+---------------+------------------+-------+
+| authenticated-blake2            | BLAKE2b       | none          | BLAKE2b          | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| authenticated                   | HMAC-SHA-256  | none          | HMAC-SHA256      | 1.1   |
++---------------------------------+---------------+---------------+------------------+-------+
+| none                            | SHA-256       | none          | none             | any   |
++---------------------------------+---------------+---------------+------------------+-------+
 
 .. nanorst: inline-replace
 
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
index 83f7b759..bf7f9d70 100644
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -632,9 +632,6 @@ def chunkit(chunker_name, *args, **kwargs):
             'lz4',
             'zstd,1',
             'zstd,3',
-            'zstd,5',
-            'zstd,10',
-            'zstd,16',
             'zstd,22',
             'zlib,0',
             'zlib,6',
@@ -4303,27 +4300,27 @@ def define_borg_mount(parser):
 
         .. nanorst: inline-fill
 
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | Mode (K = keyfile or repokey)     | ID-Hash      | Encryption     | Authentication     | V >=    |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2-chacha20-poly1305        | BLAKE2b      | CHACHA20       | POLY1305           | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-chacha20-poly1305               | HMAC-SHA-256 | CHACHA20       | POLY1305           | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2-aes-ocb                  | BLAKE2b      | AES256-OCB     | AES256-OCB         | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-aes-ocb                         | HMAC-SHA-256 | AES256-OCB     | AES256-OCB         | 1.3     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K-blake2                          | BLAKE2b      | AES256-CTR     | BLAKE2b            | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | K                                 | HMAC-SHA-256 | AES256-CTR     | HMAC-SHA256        | any     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | authenticated-blake2              | BLAKE2b      | none           | BLAKE2b            | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | authenticated                     | HMAC-SHA-256 | none           | HMAC-SHA256        | 1.1     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
-        | none                              | SHA-256      | none           | none               | any     |
-        +-----------------------------------+--------------+----------------+--------------------+---------+
+        +---------------------------------+---------------+---------------+------------------+-------+
+        |**mode (* = keyfile or repokey)**|**ID-Hash**    |**Encryption** |**Authentication**|**V>=**|
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2-chacha20-poly1305``  | BLAKE2b       | CHACHA20      | POLY1305         | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-chacha20-poly1305``         | HMAC-SHA-256  | CHACHA20      | POLY1305         | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2-aes-ocb``            | BLAKE2b       | AES256-OCB    | AES256-OCB       | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-aes-ocb``                   | HMAC-SHA-256  | AES256-OCB    | AES256-OCB       | 1.3   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*-blake2``                    | BLAKE2b       | AES256-CTR    | BLAKE2b          | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | ``*``                           | HMAC-SHA-256  | AES256-CTR    | HMAC-SHA256      | any   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | authenticated-blake2            | BLAKE2b       | none          | BLAKE2b          | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | authenticated                   | HMAC-SHA-256  | none          | HMAC-SHA256      | 1.1   |
+        +---------------------------------+---------------+---------------+------------------+-------+
+        | none                            | SHA-256       | none          | none             | any   |
+        +---------------------------------+---------------+---------------+------------------+-------+
 
         .. nanorst: inline-replace
 
diff --git a/src/borg/constants.py b/src/borg/constants.py
index 0b2ef16a..208991ee 100644
--- a/src/borg/constants.py
+++ b/src/borg/constants.py
@@ -112,8 +112,8 @@
 KEY_ALGORITHMS = {
     # encrypt-and-MAC, kdf: PBKDF2(HMAC−SHA256), encryption: AES256-CTR, authentication: HMAC-SHA256
     'pbkdf2': 'sha256',
-    # encrypt-then-MAC, kdf: argon2, encryption: chacha20, authentication: poly1305
-    'argon2': 'argon2 chacha20-poly1305',
+    # encrypt-then-MAC, kdf: argon2, encryption: AES256-CTR, authentication: HMAC-SHA256
+    'argon2': 'argon2 aes256-ctr hmac-sha256',
 }
 
 
diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py
index ad4cab9d..879f85c8 100644
--- a/src/borg/crypto/key.py
+++ b/src/borg/crypto/key.py
@@ -445,7 +445,7 @@ def decrypt_key_file(self, data, passphrase):
             self._encrypted_key_algorithm = encrypted_key.algorithm
             if encrypted_key.algorithm == 'sha256':
                 return self.decrypt_key_file_pbkdf2(encrypted_key, passphrase)
-            elif encrypted_key.algorithm == 'argon2 chacha20-poly1305':
+            elif encrypted_key.algorithm == 'argon2 aes256-ctr hmac-sha256':
                 return self.decrypt_key_file_argon2(encrypted_key, passphrase)
             else:
                 raise UnsupportedKeyFormatError()
@@ -497,14 +497,19 @@ def decrypt_key_file_pbkdf2(self, encrypted_key, passphrase):
     def decrypt_key_file_argon2(self, encrypted_key, passphrase):
         key = self.argon2(
             passphrase,
-            output_len_in_bytes=32,
+            output_len_in_bytes=64,
             salt=encrypted_key.salt,
             time_cost=encrypted_key.argon2_time_cost,
             memory_cost=encrypted_key.argon2_memory_cost,
             parallelism=encrypted_key.argon2_parallelism,
             type=encrypted_key.argon2_type,
         )
-        ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+        enc_key, mac_key = key[:32], key[32:]
+        ae_cipher = AES256_CTR_HMAC_SHA256(
+            iv=0, header_len=0, aad_offset=0,
+            enc_key=enc_key,
+            mac_key=mac_key,
+        )
         try:
             return ae_cipher.decrypt(encrypted_key.data)
         except low_level.IntegrityError:
@@ -513,7 +518,7 @@ def decrypt_key_file_argon2(self, encrypted_key, passphrase):
     def encrypt_key_file(self, data, passphrase, algorithm):
         if algorithm == 'sha256':
             return self.encrypt_key_file_pbkdf2(data, passphrase)
-        elif algorithm == 'argon2 chacha20-poly1305':
+        elif algorithm == 'argon2 aes256-ctr hmac-sha256':
             return self.encrypt_key_file_argon2(data, passphrase)
         else:
             raise ValueError(f'Unexpected algorithm: {algorithm}')
@@ -538,14 +543,19 @@ def encrypt_key_file_argon2(self, data, passphrase):
         salt = os.urandom(ARGON2_SALT_BYTES)
         key = self.argon2(
             passphrase,
-            output_len_in_bytes=32,
+            output_len_in_bytes=64,
             salt=salt,
             **ARGON2_ARGS,
         )
-        ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+        enc_key, mac_key = key[:32], key[32:]
+        ae_cipher = AES256_CTR_HMAC_SHA256(
+            iv=0, header_len=0, aad_offset=0,
+            enc_key=enc_key,
+            mac_key=mac_key,
+        )
         encrypted_key = EncryptedKey(
             version=1,
-            algorithm='argon2 chacha20-poly1305',
+            algorithm='argon2 aes256-ctr hmac-sha256',
             salt=salt,
             data=ae_cipher.encrypt(data),
             **{'argon2_' + k: v for k, v in ARGON2_ARGS.items()},
diff --git a/src/borg/repository.py b/src/borg/repository.py
index cac2e2ad..9267fe0e 100644
--- a/src/borg/repository.py
+++ b/src/borg/repository.py
@@ -446,11 +446,14 @@ def open(self, path, exclusive, lock_wait=None, lock=True):
             raise self.InvalidRepository(self.path)
         if 'repository' not in self.config.sections():
             self.close()
-            raise self.InvalidRepository(path)
+            raise self.InvalidRepositoryConfig(path, 'no repository section found')
         self.version = self.config.getint('repository', 'version')
         if self.version not in (2, ):  # for now, only work on new repos
             self.close()
-            raise self.InvalidRepository(path)
+            raise self.InvalidRepositoryConfig(
+                path,
+                'repository version %d is not supported by this borg version' % self.version
+            )
         self.max_segment_size = parse_file_size(self.config.get('repository', 'max_segment_size'))
         if self.max_segment_size >= MAX_SEGMENT_SIZE_LIMIT:
             self.close()


Test patch:
diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py
index 445ebac2..76d615f6 100644
--- a/src/borg/testsuite/archiver.py
+++ b/src/borg/testsuite/archiver.py
@@ -3600,7 +3600,7 @@ def test_init_defaults_to_argon2(self):
         self.cmd('init', '--encryption=repokey', self.repository_location)
         with Repository(self.repository_path) as repository:
             key = msgpack.unpackb(a2b_base64(repository.load_key()))
-        assert key[b'algorithm'] == b'argon2 chacha20-poly1305'
+        assert key[b'algorithm'] == b'argon2 aes256-ctr hmac-sha256'
 
     def test_init_with_explicit_key_algorithm(self):
         """https://github.com/borgbackup/borg/issues/747#issuecomment-1076160401"""
@@ -3620,7 +3620,7 @@ def verify_change_passphrase_does_not_change_algorithm(self, given_algorithm, ex
             assert key[b'algorithm'] == expected_algorithm
 
     def test_change_passphrase_does_not_change_algorithm_argon2(self):
-        self.verify_change_passphrase_does_not_change_algorithm('argon2', b'argon2 chacha20-poly1305')
+        self.verify_change_passphrase_does_not_change_algorithm('argon2', b'argon2 aes256-ctr hmac-sha256')
 
     def test_change_passphrase_does_not_change_algorithm_pbkdf2(self):
         self.verify_change_passphrase_does_not_change_algorithm('pbkdf2', b'sha256')
@@ -3635,7 +3635,7 @@ def verify_change_location_does_not_change_algorithm(self, given_algorithm, expe
             assert key[b'algorithm'] == expected_algorithm
 
     def test_change_location_does_not_change_algorithm_argon2(self):
-        self.verify_change_location_does_not_change_algorithm('argon2', b'argon2 chacha20-poly1305')
+        self.verify_change_location_does_not_change_algorithm('argon2', b'argon2 aes256-ctr hmac-sha256')
 
     def test_change_location_does_not_change_algorithm_pbkdf2(self):
         self.verify_change_location_does_not_change_algorithm('pbkdf2', b'sha256')
@@ -3647,7 +3647,7 @@ def test_key_change_algorithm(self):
 
         with Repository(self.repository_path) as repository:
             _, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK)
-        assert key._encrypted_key_algorithm == 'argon2 chacha20-poly1305'
+        assert key._encrypted_key_algorithm == 'argon2 aes256-ctr hmac-sha256'
         self.cmd('info', self.repository_location)
 
 
diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py
index dac3efeb..570afbc1 100644
--- a/src/borg/testsuite/crypto.py
+++ b/src/borg/testsuite/crypto.py
@@ -247,11 +247,15 @@ def test_hkdf_hmac_sha512_5(self):
         assert okm == bytes.fromhex('1407d46013d98bc6decefcfee55f0f90b0c7f63d68eb1a80eaf07e953cfc0a3a5240a155d6e4daa965bb')
 
 
-def test_decrypt_key_file_argon2_chacha20_poly1305():
+def test_decrypt_key_file_argon2_aes256_ctr_hmac_sha256():
     plain = b'hello'
-    # echo -n "hello, pass phrase" | argon2 saltsaltsaltsalt -id -t 1 -k 8 -p 1 -l 32 -r
-    key = bytes.fromhex('a1b0cba145c154fbd8960996c5ce3428e9920cfe53c84ef08b4102a70832bcec')
-    ae_cipher = CHACHA20_POLY1305(key=key, iv=0, header_len=0, aad_offset=0)
+    # echo -n "hello, pass phrase" | argon2 saltsaltsaltsalt -id -t 1 -k 8 -p 1 -l 64 -r
+    key = bytes.fromhex('d07cc7f9cfb483303e0b9fec176b2a9c559bb70c3a9fb0d5f9c0c23527cd09570212449f09f8cd28c1a41b73fa0098e889c3f2642e87c392e51f95d70d248d9d')
+    ae_cipher = AES256_CTR_HMAC_SHA256(
+        iv=0, header_len=0, aad_offset=0,
+        enc_key=key[:32],
+        mac_key=key[32:],
+    )
 
     envelope = ae_cipher.encrypt(plain)
 
@@ -262,7 +266,7 @@ def test_decrypt_key_file_argon2_chacha20_poly1305():
         'argon2_memory_cost': 8,
         'argon2_parallelism': 1,
         'argon2_type': b'id',
-        'algorithm': 'argon2 chacha20-poly1305',
+        'algorithm': 'argon2 aes256-ctr hmac-sha256',
         'data': envelope,
     })
     key = KeyfileKey(None)